diff --git a/.github/actions/prepare-build/action.yml b/.github/actions/prepare-build/action.yml
index ab13d7528c5..6a86d914c9e 100644
--- a/.github/actions/prepare-build/action.yml
+++ b/.github/actions/prepare-build/action.yml
@@ -29,7 +29,7 @@ runs:
- if: ${{ steps.restore-ref-artifact.outputs.cache-hit != 'true' }}
name: Build ${{ inputs.name }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
ref: ${{ inputs.ref }}
fetch-depth: 1
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index bf0b8c9c177..075d445f1dc 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -63,7 +63,7 @@ inputs:
runs:
using: composite
steps:
- - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
+ - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/setup-node@v4
with:
registry-url: 'https://registry.npmjs.org'
diff --git a/.github/renovate.json b/.github/renovate.json
index 3676d30e1f4..0d72e8ebd16 100644
--- a/.github/renovate.json
+++ b/.github/renovate.json
@@ -95,5 +95,5 @@
"assignees": ["@runspired"],
"enabled": true
},
- "ignorePaths": ["node_modules/**", "**/node_modules/**", "tests/smoke-tests/**"]
+ "ignorePaths": ["node_modules/**", "**/node_modules/**"]
}
diff --git a/.github/workflows/asset-size-check.yml b/.github/workflows/asset-size-check.yml
index 81c6ae9bd68..026c1c53d43 100644
--- a/.github/workflows/asset-size-check.yml
+++ b/.github/workflows/asset-size-check.yml
@@ -24,11 +24,11 @@ jobs:
if: contains(github.event.pull_request.labels.*.name, 'ci-assetsize')
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-depth: 3
- run: git fetch origin main --depth=1
- - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
+ - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/setup-node@v4
with:
node-version: 19.x
diff --git a/.github/workflows/compat-tests.yml b/.github/workflows/compat-tests.yml
index 19620590cd8..4109f4bb655 100644
--- a/.github/workflows/compat-tests.yml
+++ b/.github/workflows/compat-tests.yml
@@ -20,7 +20,7 @@ jobs:
timeout-minutes: 7
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
restore-broccoli-cache: true
@@ -32,7 +32,7 @@ jobs:
timeout-minutes: 7
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
restore-broccoli-cache: true
@@ -46,7 +46,7 @@ jobs:
timeout-minutes: 7
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
restore-broccoli-cache: true
@@ -58,7 +58,7 @@ jobs:
timeout-minutes: 9
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
@@ -74,7 +74,7 @@ jobs:
matrix:
node-version: [16.x, 18.x]
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
node-version: ${{ matrix.node-version }}
@@ -83,34 +83,3 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Basic Tests
run: pnpm test
-
- smoke-tests:
- name: Smoke ${{ matrix.scenario.name }} w/ ${{ matrix.packageManager }}
- timeout-minutes: 10
- runs-on: ubuntu-latest
- # TODO:
- # needs: [embroider, vite]
-
- strategy:
- matrix:
- packageManager:
- - npm
- # - yarn # yarn@1 has not been reliable, if yarn@4 were easy to setup, we could test against that
- - pnpm
- scenario:
- - { dir: "dt-types", name: "DT Types" }
- - { dir: "native-types", name: "Native Types" }
-
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- - uses: ./.github/actions/setup
- with:
- restore-broccoli-cache: true
- install: true
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- - name: "Run a basic smoke test with ${{ matrix.packageManager }} and ${{ matrix.kind }} tagging"
- run: |
- bun ./tests/smoke-tests/run.ts \
- "${{ matrix.scenario.dir }}" "${{ matrix.packageManager }}"
-
-
diff --git a/.github/workflows/deprecations-check.yml b/.github/workflows/deprecations-check.yml
index 67777beb910..1f5d674ecb5 100644
--- a/.github/workflows/deprecations-check.yml
+++ b/.github/workflows/deprecations-check.yml
@@ -12,8 +12,8 @@ jobs:
test-all-deprecations:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
+ - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/setup-node@v4
with:
node-version: 19.x
@@ -33,8 +33,8 @@ jobs:
scenario: [ember-beta, ember-canary]
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
+ - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/setup-node@v4
with:
node-version: 19.x
diff --git a/.github/workflows/docs-and-blueprint-tests.yml b/.github/workflows/docs-and-blueprint-tests.yml
index 7f004d6fb08..cccb7c87741 100644
--- a/.github/workflows/docs-and-blueprint-tests.yml
+++ b/.github/workflows/docs-and-blueprint-tests.yml
@@ -20,7 +20,7 @@ jobs:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
install: true
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 8a66fd1cdca..d1f746be123 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -27,7 +27,7 @@ jobs:
timeout-minutes: 8
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
restore-lint-caches: ${{ secrets.ACTIONS_RUNNER_DEBUG != 'true' }}
@@ -52,7 +52,7 @@ jobs:
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
restore-broccoli-cache: true
@@ -93,7 +93,7 @@ jobs:
runs-on: ubuntu-latest
name: Test ${{matrix.launcher}}
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
github-token: ${{ secrets.GH_PACKAGES_ACCESS_TOKEN }}
@@ -163,7 +163,7 @@ jobs:
scenario: [ember-lts-4.12, ember-lts-4.8, ember-lts-4.4, ember-lts-3.28]
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
restore-broccoli-cache: true
@@ -191,7 +191,7 @@ jobs:
release: [ember-canary, ember-beta]
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: ./.github/actions/setup
with:
restore-broccoli-cache: true
diff --git a/.github/workflows/perf-check.yml b/.github/workflows/perf-check.yml
index 72d7cd3dd25..005c7e05ece 100644
--- a/.github/workflows/perf-check.yml
+++ b/.github/workflows/perf-check.yml
@@ -25,7 +25,7 @@ jobs:
name: 'Performance Checks'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-depth: 3
- run: git fetch origin main --depth=1
@@ -39,21 +39,12 @@ jobs:
originSha=$(git rev-parse HEAD^2)
echo $originSha > tmp/sha-for-commit.txt
git show --format=short --no-patch $originSha
- - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
+ - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/setup-node@v4
with:
- registry-url: 'https://registry.npmjs.org'
- node-version-file: 'package.json'
+ node-version: 19.x
cache: 'pnpm'
- - uses: oven-sh/setup-bun@v1
- with:
- bun-version: latest
- - name: Get Browser Flags
- id: browser-flags
- run: |
- BROWSER_FLAGS=$(node ./scripts/perf-tracking/browser-flags.mjs)
- echo "BROWSER_FLAGS=$BROWSER_FLAGS" >> $GITHUB_OUTPUT
- - uses: tracerbench/tracerbench-compare-action@35f3ab44b512fd2caffbe81adf875ab47272b5b5
+ - uses: tracerbench/tracerbench-compare-action@master
with:
experiment-build-command: pnpm install && pnpm --filter performance-test-app exec ember build -e production --output-path dist-experiment --suppress-sizes
experiment-serve-command: pnpm --filter performance-test-app exec ember s --path dist-experiment --port 4201
@@ -62,7 +53,6 @@ jobs:
control-sha: origin/main
sample-timeout: 60
use-pnpm: true
- browser-args: ${{ steps.browser-flags.outputs.BROWSER_FLAGS }}
scenarios: |
{
"basic-record-materialization": {
diff --git a/.github/workflows/perf-over-release.yml b/.github/workflows/perf-over-release.yml
index 10883e21622..9dffa251153 100644
--- a/.github/workflows/perf-over-release.yml
+++ b/.github/workflows/perf-over-release.yml
@@ -25,7 +25,7 @@ jobs:
name: 'Performance Check Against Release'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-depth: 3
- run: git fetch origin release --depth=1
@@ -39,21 +39,12 @@ jobs:
originSha=$(git rev-parse HEAD^2)
echo $originSha > tmp/sha-for-commit.txt
git show --format=short --no-patch $originSha
- - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
+ - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/setup-node@v4
with:
- registry-url: 'https://registry.npmjs.org'
- node-version-file: 'package.json'
+ node-version: 19.x
cache: 'pnpm'
- - uses: oven-sh/setup-bun@v1
- with:
- bun-version: latest
- - name: Get Browser Flags
- id: browser-flags
- run: |
- BROWSER_FLAGS=$(node ./scripts/perf-tracking/browser-flags.mjs)
- echo "BROWSER_FLAGS=$BROWSER_FLAGS" >> $GITHUB_OUTPUT
- - uses: tracerbench/tracerbench-compare-action@35f3ab44b512fd2caffbe81adf875ab47272b5b5
+ - uses: tracerbench/tracerbench-compare-action@master
with:
experiment-build-command: pnpm install && pnpm --filter performance-test-app exec ember build -e production --output-path dist-experiment --suppress-sizes
experiment-serve-command: pnpm --filter performance-test-app exec ember s --path dist-experiment --port 4201
@@ -61,7 +52,6 @@ jobs:
control-serve-command: pnpm --filter performance-test-app exec ember s --path dist-control
sample-timeout: 60
use-pnpm: true
- browser-args: ${{ steps.browser-flags.outputs.BROWSER_FLAGS }}
scenarios: |
{
"basic-record-materialization": {
diff --git a/.github/workflows/release_promote-lts.yml b/.github/workflows/release_promote-lts.yml
index f1f84329835..6cb8aa6f8ae 100644
--- a/.github/workflows/release_promote-lts.yml
+++ b/.github/workflows/release_promote-lts.yml
@@ -42,7 +42,7 @@ jobs:
run: |
echo "Releases may only be performed from the main branch."
exit 1
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-depth: 1
fetch-tags: true
diff --git a/.github/workflows/release_publish-beta.yml b/.github/workflows/release_publish-beta.yml
index f8e1e7debf3..6d47c59c56e 100644
--- a/.github/workflows/release_publish-beta.yml
+++ b/.github/workflows/release_publish-beta.yml
@@ -75,7 +75,7 @@ jobs:
else
echo "DESIRED_BRANCH=beta" >> "$GITHUB_OUTPUT"
fi
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-tags: true
show-progress: false
diff --git a/.github/workflows/release_publish-canary.yml b/.github/workflows/release_publish-canary.yml
index a0a47bdd42a..5ff92de05b3 100644
--- a/.github/workflows/release_publish-canary.yml
+++ b/.github/workflows/release_publish-canary.yml
@@ -64,7 +64,7 @@ jobs:
else
echo "DESIRED_BRANCH=main" >> "$GITHUB_OUTPUT"
fi
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-depth: 1
fetch-tags: true
diff --git a/.github/workflows/release_publish-lts.yml b/.github/workflows/release_publish-lts.yml
index dd6471b56e9..07f79cd53ed 100644
--- a/.github/workflows/release_publish-lts.yml
+++ b/.github/workflows/release_publish-lts.yml
@@ -44,7 +44,7 @@ jobs:
run: |
echo "Releases may only be performed from the main branch."
exit 1
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-tags: true
show-progress: false
diff --git a/.github/workflows/release_publish-stable.yml b/.github/workflows/release_publish-stable.yml
index ee6b9c06d8d..33ae0807183 100644
--- a/.github/workflows/release_publish-stable.yml
+++ b/.github/workflows/release_publish-stable.yml
@@ -87,7 +87,7 @@ jobs:
else
echo "DESIRED_BRANCH=release" >> "$GITHUB_OUTPUT"
fi
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with:
fetch-tags: true
show-progress: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 608ffc4feea..f612bd9e544 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,443 +1,4 @@
-# EmberData Changelog
-
-## v5.3.4 (2024-06-15)
-
-#### :evergreen_tree: New Deprecation
-
-* [#9479](https://github.com/emberjs/data/pull/9479) feat: support migration path for ember-inflector usage ([@runspired](https://github.com/runspired))
-* [#9403](https://github.com/emberjs/data/pull/9403) feat: deprecate store extending EmberObject ([@runspired](https://github.com/runspired))
-
-#### :memo: Documentation
-
-* [#9394](https://github.com/emberjs/data/pull/9394) Add cookbook page about model names convention ([@Baltazore](https://github.com/Baltazore))
-* [#9393](https://github.com/emberjs/data/pull/9393) Update types on typescript guide part 4 ([@Baltazore](https://github.com/Baltazore))
-* [#9390](https://github.com/emberjs/data/pull/9390) docs: fix readme links in @warp-drive/ember ([@runspired](https://github.com/runspired))
-* [#9379](https://github.com/emberjs/data/pull/9379) fix: Automate uninstall process ([@MichalBryxi](https://github.com/MichalBryxi))
-* [#9378](https://github.com/emberjs/data/pull/9378) Update some docs to string ids ([@wagenet](https://github.com/wagenet))
-* [#9300](https://github.com/emberjs/data/pull/9300) doc: remove reference to unexisting ESA auth handler ([@sly7-7](https://github.com/sly7-7))
-* [#9332](https://github.com/emberjs/data/pull/9332) docs: add typescript guide ([@runspired](https://github.com/runspired))
-* [#9328](https://github.com/emberjs/data/pull/9328) chore: update READMEs with status and dist tag info ([@runspired](https://github.com/runspired))
-* [#9329](https://github.com/emberjs/data/pull/9329) chore: update compat chart in README ([@runspired](https://github.com/runspired))
-* [#9299](https://github.com/emberjs/data/pull/9299) doc: use store for save-record docs ([@Yelinz](https://github.com/Yelinz))
-* [#9298](https://github.com/emberjs/data/pull/9298) docs(request): remove duplicate line in readme ([@Yelinz](https://github.com/Yelinz))
-* [#9063](https://github.com/emberjs/data/pull/9063) docs: add requests guide ([@runspired](https://github.com/runspired))
-* [#9215](https://github.com/emberjs/data/pull/9215) Docs: Add guide for incremental adoption ([@Baltazore](https://github.com/Baltazore))
-* [#9275](https://github.com/emberjs/data/pull/9275) doc: don't mention unexisting ESA auth handler ([@sly7-7](https://github.com/sly7-7))
-
-#### :rocket: Enhancement
-
-* [#9474](https://github.com/emberjs/data/pull/9474) Improve query types for legacy-compat/builders ([@gitKrystan](https://github.com/gitKrystan))
-* [#9473](https://github.com/emberjs/data/pull/9473) npx: warp-drive retrofit types@canary 🪄 ([@runspired](https://github.com/runspired))
-* [#9471](https://github.com/emberjs/data/pull/9471) feat: npx warp-drive ([@runspired](https://github.com/runspired))
-* [#9467](https://github.com/emberjs/data/pull/9467) feat: implement schema-object for schema-record ([@richgt](https://github.com/richgt))
-* [#9468](https://github.com/emberjs/data/pull/9468) feat: string utils 🌌 ([@runspired](https://github.com/runspired))
-* [#9466](https://github.com/emberjs/data/pull/9466) feat: make @id editable and reactive ([@runspired](https://github.com/runspired))
-* [#9465](https://github.com/emberjs/data/pull/9465) feat: implement edit & create cases for legacy relationships ([@runspired](https://github.com/runspired))
-* [#9464](https://github.com/emberjs/data/pull/9464) feat: implement support for legacy hasMany and belongsTo relationship reads ([@runspired](https://github.com/runspired))
-* [#9407](https://github.com/emberjs/data/pull/9407) feat: v2 addons ([@runspired](https://github.com/runspired))
-* [#9453](https://github.com/emberjs/data/pull/9453) feat: update SchemaService to reflect RFC updates ([@runspired](https://github.com/runspired))
-* [#9448](https://github.com/emberjs/data/pull/9448) feat: impl SchemaService RFC ([@runspired](https://github.com/runspired))
-* [#9450](https://github.com/emberjs/data/pull/9450) feat: improve typing around Model and createRecord ([@runspired](https://github.com/runspired))
-* [#9444](https://github.com/emberjs/data/pull/9444) feat: rename LifetimesService => CachePolicy for clarity ([@runspired](https://github.com/runspired))
-* [#9443](https://github.com/emberjs/data/pull/9443) feat: universal consts ([@runspired](https://github.com/runspired))
-* [#9396](https://github.com/emberjs/data/pull/9396) fix: Resolve promise types for props passed to `store.createRecord()` ([@seanCodes](https://github.com/seanCodes))
-* [#9401](https://github.com/emberjs/data/pull/9401) feat: preserve lids returned by the API in legacy normalization ([@runspired](https://github.com/runspired))
-* [#9400](https://github.com/emberjs/data/pull/9400) feat: add expectId util ([@runspired](https://github.com/runspired))
-* [#9343](https://github.com/emberjs/data/pull/9343) @ember-data/codemods package ([@gitKrystan](https://github.com/gitKrystan))
-* [#9387](https://github.com/emberjs/data/pull/9387) feat: better types for legacy store methods ([@runspired](https://github.com/runspired))
-* [#8957](https://github.com/emberjs/data/pull/8957) feat(private): schema CLI ([@runspired](https://github.com/runspired))
-* [#9366](https://github.com/emberjs/data/pull/9366) feat: typed Model ([@runspired](https://github.com/runspired))
-* [#9363](https://github.com/emberjs/data/pull/9363) feat: autoRefresh ([@runspired](https://github.com/runspired))
-* [#9359](https://github.com/emberjs/data/pull/9359) feat: type checked builders and inferred request types from builders ([@runspired](https://github.com/runspired))
-* [#9353](https://github.com/emberjs/data/pull/9353) feat: utilies for migrating to stricter type and id usage ([@runspired](https://github.com/runspired))
-* [#9352](https://github.com/emberjs/data/pull/9352) feat: make setKeyInfoForResource public ([@runspired](https://github.com/runspired))
-* [#9277](https://github.com/emberjs/data/pull/9277) feat: implement managed object for schemaRecord ([@richgt](https://github.com/richgt))
-* [#9319](https://github.com/emberjs/data/pull/9319) Add @ember-data/legacy-compat/builders ([@gitKrystan](https://github.com/gitKrystan))
-* [#9314](https://github.com/emberjs/data/pull/9314) feat: improve lifetime handling of ad-hoc createRecord requests ([@runspired](https://github.com/runspired))
-* [#9317](https://github.com/emberjs/data/pull/9317) feat: ensure data utils work well with legacy relationship proxies ([@runspired](https://github.com/runspired))
-* [#9260](https://github.com/emberjs/data/pull/9260) feat: ember specific data utils ([@runspired](https://github.com/runspired))
-* [#9240](https://github.com/emberjs/data/pull/9240) feat: implement managed array for schemaRecord ([@richgt](https://github.com/richgt))
-* [#9256](https://github.com/emberjs/data/pull/9256) feat: improve alpha types support ([@runspired](https://github.com/runspired))
-* [#9250](https://github.com/emberjs/data/pull/9250) feat: fix types for legacy decorator syntax ([@runspired](https://github.com/runspired))
-* [#9249](https://github.com/emberjs/data/pull/9249) chore: handle declare statements in module rewriting ([@runspired](https://github.com/runspired))
-* [#9248](https://github.com/emberjs/data/pull/9248) feat: publish types as module defs ([@runspired](https://github.com/runspired))
-* [#9245](https://github.com/emberjs/data/pull/9245) feat: add consumer types for Model APIs ([@runspired](https://github.com/runspired))
-* [#9246](https://github.com/emberjs/data/pull/9246) normalization in json-api serializer preserves lid #7956 ([@sly7-7](https://github.com/sly7-7))
-* [#9244](https://github.com/emberjs/data/pull/9244) feat: improves consumer-facing store types ([@runspired](https://github.com/runspired))
-
-#### :bug: Bug Fix
-
-* [#9475](https://github.com/emberjs/data/pull/9475) fix: dont install optional peers if not already present ([@runspired](https://github.com/runspired))
-* [#9469](https://github.com/emberjs/data/pull/9469) Fix exports for 'ember-data' ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
-* [#9459](https://github.com/emberjs/data/pull/9459) fix: ensure cachehandler responses are cast to documents ([@runspired](https://github.com/runspired))
-* [#9456](https://github.com/emberjs/data/pull/9456) fix: visibilitychange => hidden should update unavailableStart ([@runspired](https://github.com/runspired))
-* [#9454](https://github.com/emberjs/data/pull/9454) Allow RequestState.abort to be used with on modifier ([@gitKrystan](https://github.com/gitKrystan))
-* [#9455](https://github.com/emberjs/data/pull/9455) fix: config version lookup needs to be project location aware ([@runspired](https://github.com/runspired))
-* [#9355](https://github.com/emberjs/data/pull/9355) Fix: @attr defaultValue() results should persist after initialization ([@christophersansone](https://github.com/christophersansone))
-* [#9391](https://github.com/emberjs/data/pull/9391) fix: dont fall-through after shouldAttempt on refresh ([@runspired](https://github.com/runspired))
-* [#9383](https://github.com/emberjs/data/pull/9383) fix: ensure cache-handler clones full errors ([@runspired](https://github.com/runspired))
-* [#9369](https://github.com/emberjs/data/pull/9369) fix: @warp-drive-ember, dont leak empty slot ([@runspired](https://github.com/runspired))
-* [#9364](https://github.com/emberjs/data/pull/9364) fix: restore old behavior in deprecation ([@enspandi](https://github.com/enspandi))
-* [#9360](https://github.com/emberjs/data/pull/9360) fix: Make IS_MAYBE_MIRAGE work in Firefox ([@MichalBryxi](https://github.com/MichalBryxi))
-* [#9318](https://github.com/emberjs/data/pull/9318) fix: be more specific in files in case .npmignore is ignored ([@runspired](https://github.com/runspired))
-* [#9307](https://github.com/emberjs/data/pull/9307) fix: mirage does not support anything ([@runspired](https://github.com/runspired))
-* [#9265](https://github.com/emberjs/data/pull/9265) feat: Improve config handling for polyfillUUID ([@MehulKChaudhari](https://github.com/MehulKChaudhari))
-* [#9263](https://github.com/emberjs/data/pull/9263) fix: set localState to latest identifier in belongsTo when merging identifiers ([@runspired](https://github.com/runspired))
-* [#9254](https://github.com/emberjs/data/pull/9254) Update IS_MAYBE_MIRAGE function to check for Mirage in development mode ([@Baltazore](https://github.com/Baltazore))
-* [#9257](https://github.com/emberjs/data/pull/9257) fix: use npm pack instead of pnpm pack to respect .npmignore rules ([@runspired](https://github.com/runspired))
-* [#9252](https://github.com/emberjs/data/pull/9252) fix: update line when removing declare statements ([@runspired](https://github.com/runspired))
-* [#9251](https://github.com/emberjs/data/pull/9251) fix: notify during replace if existing localState never previously calculated ([@runspired](https://github.com/runspired))
-
-#### :house: Internal
-
-* [#9477](https://github.com/emberjs/data/pull/9477) fix: add deprecation and avoid breaking configs ([@runspired](https://github.com/runspired))
-* [#9476](https://github.com/emberjs/data/pull/9476) chore: cleanup symbol usage ([@runspired](https://github.com/runspired))
-* [#9463](https://github.com/emberjs/data/pull/9463) types: ManyArray => HasMany ([@runspired](https://github.com/runspired))
-* [#9457](https://github.com/emberjs/data/pull/9457) feat: the big list of versions ([@runspired](https://github.com/runspired))
-* [#9292](https://github.com/emberjs/data/pull/9292) feat: add new build-config package ([@runspired](https://github.com/runspired))
-* [#9399](https://github.com/emberjs/data/pull/9399) types: limit traversal depth on include path generation ([@runspired](https://github.com/runspired))
-* [#9398](https://github.com/emberjs/data/pull/9398) chore: dont --compile during prepack ([@runspired](https://github.com/runspired))
-* [#9397](https://github.com/emberjs/data/pull/9397) chore: fixup publish for @ember-data/codemods ([@runspired](https://github.com/runspired))
-* [#9395](https://github.com/emberjs/data/pull/9395) Update strategy.json to mirror publish @warp-drive/schema-record ([@runspired](https://github.com/runspired))
-* [#9385](https://github.com/emberjs/data/pull/9385) fix: Make IS_MAYBE_MIRAGE simplified ([@MichalBryxi](https://github.com/MichalBryxi))
-* [#9392](https://github.com/emberjs/data/pull/9392) Fix some typos after reading code ([@Baltazore](https://github.com/Baltazore))
-* [#9370](https://github.com/emberjs/data/pull/9370) chore: rename macros ([@runspired](https://github.com/runspired))
-* [#9368](https://github.com/emberjs/data/pull/9368) docs: Update ISSUE_TEMPLATE.md to follow latest pnpm ([@MichalBryxi](https://github.com/MichalBryxi))
-* [#9365](https://github.com/emberjs/data/pull/9365) chore: remove unneeded infra tests ([@runspired](https://github.com/runspired))
-* [#9349](https://github.com/emberjs/data/pull/9349) chore: fix CI installs ([@runspired](https://github.com/runspired))
-* [#9330](https://github.com/emberjs/data/pull/9330) chore: ensure latest tag is canary/beta tag for early stage packages ([@runspired](https://github.com/runspired))
-* [#9303](https://github.com/emberjs/data/pull/9303) infra: setup mirror and types publishing ([@runspired](https://github.com/runspired))
-* [#9291](https://github.com/emberjs/data/pull/9291) chore: remove unused scripts ([@runspired](https://github.com/runspired))
-* [#9289](https://github.com/emberjs/data/pull/9289) chore: bump timeout for floating dep check in CI ([@runspired](https://github.com/runspired))
-* [#9287](https://github.com/emberjs/data/pull/9287) chore: bump deps for example-api app ([@runspired](https://github.com/runspired))
-* [#9279](https://github.com/emberjs/data/pull/9279) types: branded transforms and improve types needed for serializers ([@runspired](https://github.com/runspired))
-* [#9280](https://github.com/emberjs/data/pull/9280) chore: handle dynamic imports with relative paths ([@runspired](https://github.com/runspired))
-* [#9259](https://github.com/emberjs/data/pull/9259) Update setting-up-the-project.md ([@MehulKChaudhari](https://github.com/MehulKChaudhari))
-* [#9258](https://github.com/emberjs/data/pull/9258) fix: remove unused turbo key ([@runspired](https://github.com/runspired))
-
-#### Committers: (13)
-
-Chris Thoburn ([@runspired](https://github.com/runspired))
-Kirill Shaplyko ([@Baltazore](https://github.com/Baltazore))
-Michal Bryxí ([@MichalBryxi](https://github.com/MichalBryxi))
-Peter Wagenet ([@wagenet](https://github.com/wagenet))
-Sylvain Mina ([@sly7-7](https://github.com/sly7-7))
-Yelin Zhang ([@Yelinz](https://github.com/Yelinz))
-Krystan HuffMenne ([@gitKrystan](https://github.com/gitKrystan))
-Rich Glazerman ([@richgt](https://github.com/richgt))
-Sean Juarez ([@seanCodes](https://github.com/seanCodes))
-[@NullVoxPopuli](https://github.com/NullVoxPopuli)
-Christopher Sansone ([@christophersansone](https://github.com/christophersansone))
-Andreas Minnich ([@enspandi](https://github.com/enspandi))
-Mehul Kiran Chaudhari ([@MehulKChaudhari](https://github.com/MehulKChaudhari))
-
-## v5.3.3 (2024-03-02)
-
-#### :bug: Bug Fix
-
-* [#9243](https://github.com/emberjs/data/pull/9243) fix: keep core-type peer-deps ([@runspired](https://github.com/runspired))
-
-#### Committers: (1)
-
-Chris Thoburn ([@runspired](https://github.com/runspired))
-
-## v5.3.2 (2024-02-29)
-
-#### :house: Internal
-
-* [#9241](https://github.com/emberjs/data/pull/9241) chore: manually run prepack ([@runspired](https://github.com/runspired))
-* [#9238](https://github.com/emberjs/data/pull/9238) chore: better "from" version default value population ([@runspired](https://github.com/runspired))
-* [#9237](https://github.com/emberjs/data/pull/9237) chore: fix publishing of core-types when strategy is private ([@runspired](https://github.com/runspired))
-
-#### Committers: (1)
-
-Chris Thoburn ([@runspired](https://github.com/runspired))
-
-## v5.3.1 (2024-02-24)
-
-#### :evergreen_tree: New Deprecation
-
-* [#9189](https://github.com/emberjs/data/pull/9189) fix: mutating ManyArray should handle duplicates gracefully (with deprecation) ([@gitKrystan](https://github.com/gitKrystan))
-
-#### :memo: Documentation
-
-* [#9132](https://github.com/emberjs/data/pull/9132) Add auth handler guides ([@Baltazore](https://github.com/Baltazore))
-* [#9071](https://github.com/emberjs/data/pull/9071) chore: refactor relationships guide ([@runspired](https://github.com/runspired))
-* [#9059](https://github.com/emberjs/data/pull/9059) docs: The comprehensive guide to relationships ([@runspired](https://github.com/runspired))
-* [#9018](https://github.com/emberjs/data/pull/9018) doc(README): remove typo ([@omimakhare](https://github.com/omimakhare))
-* [#8966](https://github.com/emberjs/data/pull/8966) feat: Add links to the CODE_OF_CONDUCT.md ([@Agnik7](https://github.com/Agnik7))
-* [#8963](https://github.com/emberjs/data/pull/8963) chore: scaffold additional contributing materials ([@runspired](https://github.com/runspired))
-* [#9162](https://github.com/emberjs/data/pull/9162) feat: improve store.request documentation ([@runspired](https://github.com/runspired))
-* [#9161](https://github.com/emberjs/data/pull/9161) docs: fix return signature of peekRequest ([@runspired](https://github.com/runspired))
-* [#9159](https://github.com/emberjs/data/pull/9159) fix: support full range of json:api for references, update docs ([@runspired](https://github.com/runspired))
-* [#9160](https://github.com/emberjs/data/pull/9160) docs: update links ([@runspired](https://github.com/runspired))
-* [#8954](https://github.com/emberjs/data/pull/8954) docs: typo in hasChangedRelationships description ([@BoussonKarel](https://github.com/BoussonKarel))
-* [#9072](https://github.com/emberjs/data/pull/9072) feat: advanced JSON:API queries & basic request example ([@runspired](https://github.com/runspired))
-* [#9070](https://github.com/emberjs/data/pull/9070) docs: fix note notation to make use of github formatting ([@runspired](https://github.com/runspired))
-* [#9068](https://github.com/emberjs/data/pull/9068) docs: unroll details sections ([@runspired](https://github.com/runspired))
-
-#### :rocket: Enhancement
-
-* [#9220](https://github.com/emberjs/data/pull/9220) feat: request infra improvements ([@runspired](https://github.com/runspired))
-* [#9163](https://github.com/emberjs/data/pull/9163) feat: improved lifetimes-service capabilities ([@runspired](https://github.com/runspired))
-* [#9159](https://github.com/emberjs/data/pull/9159) fix: support full range of json:api for references, update docs ([@runspired](https://github.com/runspired))
-* [#9094](https://github.com/emberjs/data/pull/9094) feat: support legacy attribute behaviors in SchemaRecord ([@gitKrystan](https://github.com/gitKrystan))
-* [#9095](https://github.com/emberjs/data/pull/9095) feat (internal): support legacy model behaviors in SchemaRecord legacy mode ([@runspired](https://github.com/runspired))
-* [#9072](https://github.com/emberjs/data/pull/9072) feat: advanced JSON:API queries & basic request example ([@runspired](https://github.com/runspired))
-* [#9069](https://github.com/emberjs/data/pull/9069) feat: Improve extensibility ([@runspired](https://github.com/runspired))
-* [#8955](https://github.com/emberjs/data/pull/8955) feat(private): scaffold packages for schema parser ([@runspired](https://github.com/runspired))
-* [#8949](https://github.com/emberjs/data/pull/8949) feat:prepare for universal reactivity ([@runspired](https://github.com/runspired))
-* [#8948](https://github.com/emberjs/data/pull/8948) feat(private): reactive simple fields ([@runspired](https://github.com/runspired))
-* [#8946](https://github.com/emberjs/data/pull/8946) feat (private): implement resource relationships for SchemaRecord ([@runspired](https://github.com/runspired))
-* [#8939](https://github.com/emberjs/data/pull/8939) feat (private): implement support for derivations in schema-record ([@runspired](https://github.com/runspired))
-* [#8935](https://github.com/emberjs/data/pull/8935) feat: (private) implement basic field support for schema-record ([@runspired](https://github.com/runspired))
-* [#8925](https://github.com/emberjs/data/pull/8925) feat: implement postQuery builder ([@runspired](https://github.com/runspired))
-* [#8921](https://github.com/emberjs/data/pull/8921) feat: Improved Fetch Errors ([@runspired](https://github.com/runspired))
-
-#### :bug: Bug Fix
-
-* [#9221](https://github.com/emberjs/data/pull/9221) fix: prevent rollbackRelationships from setting remoteState and localState to the same array reference ([@runspired](https://github.com/runspired))
-* [#9203](https://github.com/emberjs/data/pull/9203) fix: Fetch handler hacks for Mirage (canary) ([@gitKrystan](https://github.com/gitKrystan))
-* [#9189](https://github.com/emberjs/data/pull/9189) fix: mutating ManyArray should handle duplicates gracefully (with deprecation) ([@gitKrystan](https://github.com/gitKrystan))
-* [#9183](https://github.com/emberjs/data/pull/9183) fix: keep a backreference for previously merged identifiers ([@runspired](https://github.com/runspired))
-* [#8927](https://github.com/emberjs/data/pull/8927) fix: live-array delete sync should not clear the set on length match ([@runspired](https://github.com/runspired))
-* [#9164](https://github.com/emberjs/data/pull/9164) fix: url configuration should respect / for host and error more meaningfully when invalid ([@runspired](https://github.com/runspired))
-* [#9159](https://github.com/emberjs/data/pull/9159) fix: support full range of json:api for references, update docs ([@runspired](https://github.com/runspired))
-* [#9097](https://github.com/emberjs/data/pull/9097) fix: allow decorator syntax in code comments during yui doc processing ([@jaredgalanis](https://github.com/jaredgalanis))
-* [#9014](https://github.com/emberjs/data/pull/9014) fix: make willCommit slightly safer when race conditions occur ([@runspired](https://github.com/runspired))
-* [#8934](https://github.com/emberjs/data/pull/8934) fix: JSONAPISerializer should not reify empty records ([@runspired](https://github.com/runspired))
-* [#8892](https://github.com/emberjs/data/pull/8892) doc: Fix paths in transform deprecations ([@HeroicEric](https://github.com/HeroicEric))
-
-#### :house: Internal
-
-* [#9125](https://github.com/emberjs/data/pull/9125) Configure ESLint for test packages ([@gitKrystan](https://github.com/gitKrystan))
-* [#8994](https://github.com/emberjs/data/pull/8994) chore: fix recursive pnpm on node 18.18 ([@runspired](https://github.com/runspired))
-* [#9110](https://github.com/emberjs/data/pull/9110) Stricter typescript-eslint config ([@gitKrystan](https://github.com/gitKrystan))
-* [#9101](https://github.com/emberjs/data/pull/9101) chore: Type check test files ([@gitKrystan](https://github.com/gitKrystan))
-* [#9093](https://github.com/emberjs/data/pull/9093) feat(internal): implement legacy mode toggle ([@runspired](https://github.com/runspired))
-* [#9085](https://github.com/emberjs/data/pull/9085) Add type-checking to tests/warp-drive__schema-record ([@gitKrystan](https://github.com/gitKrystan))
-* [#9089](https://github.com/emberjs/data/pull/9089) Add type-checking for packages/unpublished-test-infra ([@gitKrystan](https://github.com/gitKrystan))
-* [#9009](https://github.com/emberjs/data/pull/9009) chore(internal) add @warp-drive/diagnostic/ember ([@runspired](https://github.com/runspired))
-* [#9007](https://github.com/emberjs/data/pull/9007) chore(internal): convert model and adapter tests to use diagnostic ([@runspired](https://github.com/runspired))
-* [#8967](https://github.com/emberjs/data/pull/8967) chore(private): implements a QUnit alternative ([@runspired](https://github.com/runspired))
-* [#9086](https://github.com/emberjs/data/pull/9086) Add ESLint config for tests/warp-drive__schema-record ([@gitKrystan](https://github.com/gitKrystan))
-* [#9078](https://github.com/emberjs/data/pull/9078) docs: add compatibility table to readme ([@runspired](https://github.com/runspired))
-* [#9054](https://github.com/emberjs/data/pull/9054) Initial lint config for tests/blueprints ([@gitKrystan](https://github.com/gitKrystan))
-* [#9061](https://github.com/emberjs/data/pull/9061) Git-ignore .prettier-cache ([@gitKrystan](https://github.com/gitKrystan))
-* [#8993](https://github.com/emberjs/data/pull/8993) chore: fix development test command ([@runspired](https://github.com/runspired))
-* [#8986](https://github.com/emberjs/data/pull/8986) chore: rename schema tests to warp-drive__* variants ([@runspired](https://github.com/runspired))
-* [#8984](https://github.com/emberjs/data/pull/8984) chore: remove unneeded debug-encapsulation tests ([@runspired](https://github.com/runspired))
-* [#8983](https://github.com/emberjs/data/pull/8983) chore: rename request-test-app to ember-data__request ([@runspired](https://github.com/runspired))
-* [#8982](https://github.com/emberjs/data/pull/8982) chore: rename json-api-test-app to ember-data__json-api ([@runspired](https://github.com/runspired))
-* [#8981](https://github.com/emberjs/data/pull/8981) chore: rename adapter-encapsulation-test-app to ember-data__adapter ([@runspired](https://github.com/runspired))
-* [#8980](https://github.com/emberjs/data/pull/8980) chore: rename graph-test-app to ember-data__graph ([@runspired](https://github.com/runspired))
-* [#8979](https://github.com/emberjs/data/pull/8979) chore: rename serializer-encapsulation tests, remove smoke-test ([@runspired](https://github.com/runspired))
-* [#8978](https://github.com/emberjs/data/pull/8978) chore: rename model-encapsulation tests, remove smoke-test ([@runspired](https://github.com/runspired))
-* [#8974](https://github.com/emberjs/data/pull/8974) chore: remove uneeded json-api-encapsulation test app ([@runspired](https://github.com/runspired))
-* [#8960](https://github.com/emberjs/data/pull/8960) internal: fix test settledness ([@runspired](https://github.com/runspired))
-* [#9084](https://github.com/emberjs/data/pull/9084) Add import types ([@gitKrystan](https://github.com/gitKrystan))
-* [#8989](https://github.com/emberjs/data/pull/8989) chore(private): concurrent mode ([@runspired](https://github.com/runspired))
-* [#9082](https://github.com/emberjs/data/pull/9082) Remove remaining @types/ember* packages ([@gitKrystan](https://github.com/gitKrystan))
-* [#8961](https://github.com/emberjs/data/pull/8961) chore: run tests nicely ([@runspired](https://github.com/runspired))
-* [#9062](https://github.com/emberjs/data/pull/9062) Extract qunit ESLint config ([@gitKrystan](https://github.com/gitKrystan))
-* [#9058](https://github.com/emberjs/data/pull/9058) Switch from eslint-plugin-prettier to running prettier directly ([@gitKrystan](https://github.com/gitKrystan))
-* [#9057](https://github.com/emberjs/data/pull/9057) Add eslint-plugin-n to eslint config for node files ([@gitKrystan](https://github.com/gitKrystan))
-* [#9055](https://github.com/emberjs/data/pull/9055) Fix ESLint for VSCode ([@gitKrystan](https://github.com/gitKrystan))
-* [#9051](https://github.com/emberjs/data/pull/9051) chore: use references for tsc, add checks to schema-record, bun to run scripts ([@runspired](https://github.com/runspired))
-* [#9032](https://github.com/emberjs/data/pull/9032) chore(types): split out lint and type commands to be per-package ([@runspired](https://github.com/runspired))
-* [#9050](https://github.com/emberjs/data/pull/9050) chore: use composite mode for tsc ([@runspired](https://github.com/runspired))
-* [#9049](https://github.com/emberjs/data/pull/9049) chore: incremental tsc builds ([@runspired](https://github.com/runspired))
-* [#9046](https://github.com/emberjs/data/pull/9046) chore: reduce number of things turbo builds for build ([@runspired](https://github.com/runspired))
-* [#9027](https://github.com/emberjs/data/pull/9027) chore: improve types for store package ([@runspired](https://github.com/runspired))
-* [#9029](https://github.com/emberjs/data/pull/9029) chore: add @warp-drive/core as home for shared code ([@runspired](https://github.com/runspired))
-* [#9028](https://github.com/emberjs/data/pull/9028) chore: more isolated types ([@runspired](https://github.com/runspired))
-* [#9025](https://github.com/emberjs/data/pull/9025) chore: reconfigure request package type location ([@runspired](https://github.com/runspired))
-* [#9024](https://github.com/emberjs/data/pull/9024) chore: cleanup more types ([@runspired](https://github.com/runspired))
-* [#9021](https://github.com/emberjs/data/pull/9021) chore: cleanup ember-data/-private types ([@runspired](https://github.com/runspired))
-* [#9019](https://github.com/emberjs/data/pull/9019) chore: make model types strict ([@runspired](https://github.com/runspired))
-* [#9017](https://github.com/emberjs/data/pull/9017) chore: make json-api cache strict ([@runspired](https://github.com/runspired))
-* [#9016](https://github.com/emberjs/data/pull/9016) chore: make type-only files strict ([@runspired](https://github.com/runspired))
-* [#9008](https://github.com/emberjs/data/pull/9008) chore: update eslint plugin name ([@runspired](https://github.com/runspired))
-* [#9006](https://github.com/emberjs/data/pull/9006) chore (internal): convert builder and request tests to use diagnostic+runner ([@runspired](https://github.com/runspired))
-* [#9000](https://github.com/emberjs/data/pull/9000) feat(private): native test runner ([@runspired](https://github.com/runspired))
-* [#8995](https://github.com/emberjs/data/pull/8995) chore: add @warp-drive/diagnostic docs ([@runspired](https://github.com/runspired))
-* [#8987](https://github.com/emberjs/data/pull/8987) chore: test-harness improvements ([@runspired](https://github.com/runspired))
-* [#8972](https://github.com/emberjs/data/pull/8972) chore: use new test runner for request tests ([@runspired](https://github.com/runspired))
-* [#8931](https://github.com/emberjs/data/pull/8931) chore: package infra for schema-record ([@runspired](https://github.com/runspired))
-* [#8930](https://github.com/emberjs/data/pull/8930) chore: get last request for any record on instantiation ([@runspired](https://github.com/runspired))
-* [#8923](https://github.com/emberjs/data/pull/8923) chore: prepare files for new eslint plugin ([@runspired](https://github.com/runspired))
-* [#8911](https://github.com/emberjs/data/pull/8911) chore: remove unneeded type cast ([@runspired](https://github.com/runspired))
-* [#8912](https://github.com/emberjs/data/pull/8912) chore: docs for holodeck ([@runspired](https://github.com/runspired))
-* [#8906](https://github.com/emberjs/data/pull/8906) feat: expand mock-server capabilities, add to main tests ([@runspired](https://github.com/runspired))
-
-#### Committers: (8)
-
-Krystan HuffMenne ([@gitKrystan](https://github.com/gitKrystan))
-Kirill Shaplyko ([@Baltazore](https://github.com/Baltazore))
-Chris Thoburn ([@runspired](https://github.com/runspired))
-OMKAR MAKHARE ([@omimakhare](https://github.com/omimakhare))
-Agnik Bakshi ([@Agnik7](https://github.com/Agnik7))
-[@BoussonKarel](https://github.com/BoussonKarel)
-Jared Galanis ([@jaredgalanis](https://github.com/jaredgalanis))
-Eric Kelly ([@HeroicEric](https://github.com/HeroicEric))
-
-## v5.3.0 (2023-09-18)
-
-#### :rocket: Enhancement
- * [#8849](https://github.com/emberjs/data/pull/8849) feat: docs, tests and fixes for create/update/deleteRecord builders ([@Baltazore](https://github.com/Baltazore))
- * [#8824](https://github.com/emberjs/data/pull/8824) feat: relationshipRollback, serializePatch ([@runspired](https://github.com/runspired))
- * [#8798](https://github.com/emberjs/data/pull/8798) feat: implement a simple LifetimeService utility, improve document reconstruction ([@runspired](https://github.com/runspired))
- * [#8741](https://github.com/emberjs/data/pull/8741) feat: JSON:API serialization utils ([@runspired](https://github.com/runspired))
- * [#8740](https://github.com/emberjs/data/pull/8740) feat: saveRecord builders ([@runspired](https://github.com/runspired))
- * [#8744](https://github.com/emberjs/data/pull/8744) add sortQueryParams, update roadmap with link to checklist ([@runspired](https://github.com/runspired))
- * [#8716](https://github.com/emberjs/data/pull/8716) feat: filterEmpty for query params ([@runspired](https://github.com/runspired))
- * [#8687](https://github.com/emberjs/data/pull/8687) feat: findRecord and query request builders ([@runspired](https://github.com/runspired))
- * [#8673](https://github.com/emberjs/data/pull/8673) DX: Nicer backtracking errors ([@runspired](https://github.com/runspired))
- * [#8736](https://github.com/emberjs/data/pull/8736) chore: refactor IdentityCache to make resource more opaque ([@runspired](https://github.com/runspired))
-
-#### :bug: Bug Fix
- * [#8876](https://github.com/emberjs/data/pull/8876) fix: Fetch handler should account for empty body ([@runspired](https://github.com/runspired))
- * [#8842](https://github.com/emberjs/data/pull/8842) fix: handle Immutable Response objects ([@runspired](https://github.com/runspired))
- * [#8828](https://github.com/emberjs/data/pull/8828) fix: set headers after setResponse in Fetch handler ([@runspired](https://github.com/runspired))
- * [#8850](https://github.com/emberjs/data/pull/8850) Overwrite addMixin ([@patricklx](https://github.com/patricklx))
- * [#8831](https://github.com/emberjs/data/pull/8831) fix: cleanup build deps and add @ember/string to REST/ActiveRecord builder peer-deps ([@runspired](https://github.com/runspired))
- * [#8826](https://github.com/emberjs/data/pull/8826) fix createRecord error when no adapter is present ([@runspired](https://github.com/runspired))
- * [#8791](https://github.com/emberjs/data/pull/8791) fix: clear relationships properly when unloading new records ([@Windvis](https://github.com/Windvis))
- * [#8794](https://github.com/emberjs/data/pull/8794) Fix check for new records in JSONAPISerializer.serializeHasMany ([@dagroe](https://github.com/dagroe))
- * [#8751](https://github.com/emberjs/data/pull/8751) Forward fixes from 3.12.x into main ([@jrjohnson](https://github.com/jrjohnson))
- * [#8684](https://github.com/emberjs/data/pull/8684) fix: unloadAll(void) should not destroy the notification manager ([@runspired](https://github.com/runspired))
-
-#### :evergreen_tree: New Deprecation
- * [#8747](https://github.com/emberjs/data/pull/8747) feat: implement legacy imports deprecation ([@runspired](https://github.com/runspired))
- * [#8734](https://github.com/emberjs/data/pull/8734) feat: Implement Strict Types and Id Deprecations ([@runspired](https://github.com/runspired))
-
-#### :shower: Deprecation Removal
-* `adapter`, `model`, `private-build-infra`, `serializer`
- * [#8797](https://github.com/emberjs/data/pull/8797) Drop support for `ember-cli-mocha` and `ember-mocha` when generating test blueprints ([@bertdeblock](https://github.com/bertdeblock))
-
-#### :memo: Documentation
- * [#8848](https://github.com/emberjs/data/pull/8848) feat: add request options documentation parts to find-record builder ([@Baltazore](https://github.com/Baltazore))
- * [#8825](https://github.com/emberjs/data/pull/8825) feat: more docs for builders ([@runspired](https://github.com/runspired))
- * [#8819](https://github.com/emberjs/data/pull/8819) fix: `JSONAPISerializer.shouldSerializeHasMany` relation param type ([@samridhivig](https://github.com/samridhivig))
- * [#8746](https://github.com/emberjs/data/pull/8746) docs: more documentation for builders ([@runspired](https://github.com/runspired))
- * [#8745](https://github.com/emberjs/data/pull/8745) chore: readme overviews for builders ([@runspired](https://github.com/runspired))
- * [#8724](https://github.com/emberjs/data/pull/8724) chore: rename CacheStoreWrapper => CacheCapabilitiesManager to reflect its role ([@runspired](https://github.com/runspired))
- * [#8671](https://github.com/emberjs/data/pull/8671) Typo correction in ROADMAP.md ([@wagenet](https://github.com/wagenet))
-
-#### :goal_net: Test
- * [#8878](https://github.com/emberjs/data/pull/8878) test: add basic test for Fetch handler ([@runspired](https://github.com/runspired))
- * [#8849](https://github.com/emberjs/data/pull/8849) feat: docs, tests and fixes for create/update/deleteRecord builders ([@Baltazore](https://github.com/Baltazore))
- * [#8868](https://github.com/emberjs/data/pull/8868) Add tests for filter-empty request util ([@Baltazore](https://github.com/Baltazore))
- * [#8866](https://github.com/emberjs/data/pull/8866) Add tests for parse-cache-control ([@Baltazore](https://github.com/Baltazore))
- * [#8864](https://github.com/emberjs/data/pull/8864) test: confirm records unload properly for #8863 ([@runspired](https://github.com/runspired))
- * [#8780](https://github.com/emberjs/data/pull/8780) chore: add test to demonstrate create props work as expected ([@runspired](https://github.com/runspired))
-
-#### :house: Internal
- * [#8758](https://github.com/emberjs/data/pull/8758) chore: refactor implicit edge to match resource and collection pattern ([@runspired](https://github.com/runspired))
- * [#8755](https://github.com/emberjs/data/pull/8755) chore: simplify file structure in graph package ([@runspired](https://github.com/runspired))
- * [#8749](https://github.com/emberjs/data/pull/8749) fix: ensure we are not allowing embroider to do anything ([@runspired](https://github.com/runspired))
- * [#8672](https://github.com/emberjs/data/pull/8672) chore: update roadmap for 5.3 ([@runspired](https://github.com/runspired))
- * [#8670](https://github.com/emberjs/data/pull/8670) chore: add ROADMAP and update CONTRIBUTING ([@runspired](https://github.com/runspired))
- * [#8739](https://github.com/emberjs/data/pull/8739) chore: migrate store/graph to strict types config ([@runspired](https://github.com/runspired))
- * [#8733](https://github.com/emberjs/data/pull/8733) chore: improve types and lint ([@runspired](https://github.com/runspired))
- * [#8727](https://github.com/emberjs/data/pull/8727) chore: fix peers and get perf-test-app running again ([@runspired](https://github.com/runspired))
- * [#8717](https://github.com/emberjs/data/pull/8717) Switch from local and @types/ember types to ember-source types ([@BradBarnich](https://github.com/BradBarnich))
- * [#8499](https://github.com/emberjs/data/pull/8499) chore: refactor model hook support to live in the model package ([@runspired](https://github.com/runspired))
- * [#8862](https://github.com/emberjs/data/pull/8862) chore: remove more runloop usage | completely remove rsvp ([@runspired](https://github.com/runspired))
- * [#8861](https://github.com/emberjs/data/pull/8861) chore: remove runloop usage ([@runspired](https://github.com/runspired))
- * [#8859](https://github.com/emberjs/data/pull/8859) chore: update target labels ([@runspired](https://github.com/runspired))
- * [#8858](https://github.com/emberjs/data/pull/8858) chore: update required labels ([@runspired](https://github.com/runspired))
- * [#8830](https://github.com/emberjs/data/pull/8830) chore: cleanup actions/setup usage ([@runspired](https://github.com/runspired))
- * [#8812](https://github.com/emberjs/data/pull/8812) fix typo ([@samridhivig](https://github.com/samridhivig))
- * [#8802](https://github.com/emberjs/data/pull/8802) chore: fix fastboot-testing deprecation ([@runspired](https://github.com/runspired))
- * [#8801](https://github.com/emberjs/data/pull/8801) chore: resolve deprecation in fastboot app ([@runspired](https://github.com/runspired))
- * [#8860](https://github.com/emberjs/data/pull/8860) chore: burn down runloop and RSVP usage ([@runspired](https://github.com/runspired))
- * [#8832](https://github.com/emberjs/data/pull/8832) chore: add recommended JSON:API setup test app ([@runspired](https://github.com/runspired))
- * [#8829](https://github.com/emberjs/data/pull/8829) chore: eliminate dead build code ([@runspired](https://github.com/runspired))
- * [#8823](https://github.com/emberjs/data/pull/8823) fix: graph instantiation should not be required ([@runspired](https://github.com/runspired))
-
-#### Committers: 11
-- Bert De Block ([@bertdeblock](https://github.com/bertdeblock))
-- Chris Thoburn ([@runspired](https://github.com/runspired))
-- Daniel Gröger ([@dagroe](https://github.com/dagroe))
-- Kirill Shaplyko ([@Baltazore](https://github.com/Baltazore))
-- Patrick Pircher ([@patricklx](https://github.com/patricklx))
-- Sam Van Campenhout ([@Windvis](https://github.com/Windvis))
-- Samridhi Vig ([@samridhivig](https://github.com/samridhivig))
-- Brad Barnich ([@BradBarnich](https://github.com/BradBarnich))
-- Jon Johnson ([@jrjohnson](https://github.com/jrjohnson))
-- Michal Bryxí ([@MichalBryxi](https://github.com/MichalBryxi))
-- Peter Wagenet ([@wagenet](https://github.com/wagenet))
-
-## 5.2.0 (2023-08-17)
-
-* Re-release of 5.1.2 to keep lockstep pace. This release contains no new work.
-## 5.1.2 (2023-08-17)
-#### :bug: Bug Fix
-
-* [#8750](https://github.com/emberjs/data/pull/8750) Backport into release ([@jrjohnson](https://github.com/jrjohnson))
- * fix: @ember-data/debug should declare its peer-dependency on @ember-data/store #8703
- * fix: de-dupe coalescing when includes or adapterOptions is present but still use findRecord #8704
- * fix: make implicit relationship teardown following delete of related record safe #8705
- * fix: catch errors during didCommit in DEBUG #8708
-
-## 5.1.1 (2023-07-07)
-
-#### :bug: Bug Fix
- * [#8685](https://github.com/emberjs/data/pull/8685) fix: unloadAll(void) should not destroy the notification manager (backports #8684) ([@runspired](https://github.com/runspired))
-
-#### Committers: 1
-- Chris Thoburn ([@runspired](https://github.com/runspired))
-
-## 5.1.0 (2023-06-29)
-
-#### :bug: Bug Fix
- * [#8657](https://github.com/emberjs/data/pull/8657) fix: ensure deprecation configs are threaded to each package ([@runspired](https://github.com/runspired))
- * [#8649](https://github.com/emberjs/data/pull/8649) fix: NotificationManager should only invoke resource/document callbacks owned by the originating store ([@runspired](https://github.com/runspired))
-
-#### Committers: 1
-- Chris Thoburn ([@runspired](https://github.com/runspired))
-
-## 5.0.1 (2023-06-29)
-
-#### :bug: Bug Fix
- * [#8649](https://github.com/emberjs/data/pull/8649) fix: NotificationManager should only invoke resource/document callbacks owned by the originating store ([@runspired](https://github.com/runspired))
-
-#### Committers: 1
-- Chris Thoburn ([@runspired](https://github.com/runspired))
-
-## 5.0.0 (2023-06-10)
-
-#### :bug: Bug Fix
-* `adapter`
- * [#8621](https://github.com/emberjs/data/pull/8621) fix: normalizeErrorResponse should be resilient to non-string details ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
-* Other
- * [#8598](https://github.com/emberjs/data/pull/8598) fix: docs generation should maintain a stable relative path ([@runspired](https://github.com/runspired))
-* `json-api`, `legacy-compat`, `store`
- * [#8566](https://github.com/emberjs/data/pull/8566) Avoid unnecessary identity notification when record is saved ([@robbytx](https://github.com/robbytx))
-* `model`
- * [#8597](https://github.com/emberjs/data/pull/8597) fix: dont share promise cache for all fields ([@runspired](https://github.com/runspired))
-* `store`
- * [#8594](https://github.com/emberjs/data/pull/8594) fix: restore Store extends EmberObject :( ([@runspired](https://github.com/runspired))
- * [#8570](https://github.com/emberjs/data/pull/8570) Fix: don't clear RecordArray if remaining record does not match the removed record ([@esbanarango](https://github.com/esbanarango))
-* `graph`, `model`, `private-build-infra`
- * [#8555](https://github.com/emberjs/data/pull/8555) fix: fix polymorphic assertions when deprecated code is removed, improve polymorphic dx ([@runspired](https://github.com/runspired))
-
-#### :shower: Deprecation Removal
-* `-ember-data`, `adapter`, `debug`, `graph`, `json-api`, `legacy-compat`, `model`, `private-build-infra`, `store`, `unpublished-test-infra`
- * [#8550](https://github.com/emberjs/data/pull/8550) chore: remove 4.x deprecations ([@runspired](https://github.com/runspired))
-
-#### :memo: Documentation
-* `store`
- * [#8601](https://github.com/emberjs/data/pull/8601) docs: fix forgotten references to FetchManager ([@runspired](https://github.com/runspired))
-* Other
- * [#8598](https://github.com/emberjs/data/pull/8598) fix: docs generation should maintain a stable relative path ([@runspired](https://github.com/runspired))
-
-#### Committers: 4
-- Chris Thoburn ([@runspired](https://github.com/runspired))
-- Esteban ([@esbanarango](https://github.com/esbanarango))
-- Robby Morgan ([@robbytx](https://github.com/robbytx))
-- [@NullVoxPopuli](https://github.com/NullVoxPopuli)
+# Ember Data Changelog
## v4.12.8 (2024-05-08)
@@ -510,7 +71,7 @@ Eric Kelly ([@HeroicEric](https://github.com/HeroicEric))
#### Committers: 1
- Chris Thoburn ([@runspired](https://github.com/runspired))
-## LTS 4.12.2 (2023-07-07)
+## 4.12.2 (2023-07-07)
#### :rocket: Enhancement
* [#8660](https://github.com/emberjs/data/pull/8660) DX: Nicer backtracking errors ([@runspired](https://github.com/runspired))
@@ -521,7 +82,7 @@ Eric Kelly ([@HeroicEric](https://github.com/HeroicEric))
#### Committers: 1
- Chris Thoburn ([@runspired](https://github.com/runspired))
-## LTS 4.12.1 (2023-06-29)
+## 4.12.1 (2023-06-29)
#### :bug: Bug Fix
* [#8656](https://github.com/emberjs/data/pull/8656) fix: NotificationManager should only invoke resource/document callbacks owned by the originating store (#8649) ([@runspired](https://github.com/runspired))
@@ -533,6 +94,7 @@ Eric Kelly ([@HeroicEric](https://github.com/HeroicEric))
- Chris Thoburn ([@runspired](https://github.com/runspired))
- Esteban ([@esbanarango](https://github.com/esbanarango))
+
## 4.12.0 (2023-04-06)
#### :rocket: Enhancement
@@ -884,46 +446,6 @@ This is a re-release of 4.10.0
- [@law-rence](https://github.com/law-rence)
- Eugen Ciur ([@ciur](https://github.com/ciur))
-## v4.6.5 (2024-05-08)
-
-#### :bug: Bug Fix
-* [#9316](https://github.com/emberjs/data/pull/9316) Notify on length when notifying that many-array has changed
-
-#### Committers: 1
--
-Ross Grayton ([@grayt0r](https://github.com/grayt0r))
-
-
-## v4.6.4 (2022-10-02)
-
-#### :bug: Bug Fix
-* `private-build-infra`
- * [#8199](https://github.com/emberjs/data/pull/8199) [backport release-prev] fix: thread polyfillUUID config through nested deps ([@runspired](https://github.com/runspired))
-
-#### Committers: 1
-- Chris Thoburn ([@runspired](https://github.com/runspired))
-
-## v4.6.3 (2022-09-15)
-
-#### :bug: Bug Fix
-* `store`
- * fix: allow ManyArray being passed to createRecord
-
-## v4.6.2 (2022-09-15)
-
-#### :bug: Bug Fix
-* `store`
- * [#8169](https://github.com/emberjs/data/pull/8169) fix: uuid polyfill logic ([@jrjohnson](https://github.com/jrjohnson))
-* `-ember-data`, `model`
- * [#8148](https://github.com/emberjs/data/pull/8148) Clear subscriptions once unsubscribed, don't unnecessarily churn on subscriptions ([@jrjohnson](https://github.com/jrjohnson))
-* `private-build-infra`
- * [#8145](https://github.com/emberjs/data/pull/8145) fix earlier versions of node-14 (#8108) ([@jrjohnson](https://github.com/jrjohnson))
-* `private-build-infra`, `store`
- * [#8144](https://github.com/emberjs/data/pull/8144) Backport add optional polyfill (#8109) ([@jrjohnson](https://github.com/jrjohnson))
-
-#### Committers: 1
-- Jon Johnson ([@jrjohnson](https://github.com/jrjohnson))
-
## v4.6.1 (2022-07-28)
#### :bug: Bug Fix
@@ -1034,15 +556,6 @@ Ross Grayton ([@grayt0r](https://github.com/grayt0r))
- Cameron Dubas ([@camerondubas](https://github.com/camerondubas))
- Jen Weber ([@jenweber](https://github.com/jenweber))
-## v4.4.2 (2023-08-01)
-
-#### :bug: Bug Fix
-* `model`
- * [#8713](https://github.com/emberjs/data/pull/8713) Notify on length when notifying that many-array has changed ([@richgt](https://github.com/richgt))
-
-#### Committers: 1
-- Rich Glazerman ([@richgt](https://github.com/richgt))
-
## v4.1.0 (2021-12-30)
#### :house: Internal
@@ -3418,7 +2931,7 @@ The full API reference of `DS.Snapshot` can be found [here](https://api.emberjs.
- fetch() -> fetchById() in docs
- Run findHasMany inside an ED runloop
- Cleanup debug adapter test: Watching Records
-- Fixed didDelete event/callback not fired in uncommitted state
+- Fixed didDelete event/callback not fired in uncommited state
- Add main entry point for package.json.
- register the store as a service
- Warn when expected coalesced records are not found in the response
diff --git a/config/package.json b/config/package.json
index df035eda2a8..a9888f9e860 100644
--- a/config/package.json
+++ b/config/package.json
@@ -1,7 +1,7 @@
{
"name": "@warp-drive/internal-config",
"private": true,
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"type": "module",
"dependencies": {
"@babel/cli": "^7.24.5",
@@ -11,10 +11,9 @@
"@typescript-eslint/eslint-plugin": "^8.10.0",
"@typescript-eslint/parser": "^8.10.0",
"typescript-eslint": "^8.10.0",
- "@embroider/addon-dev": "^7.1.1",
+ "@embroider/addon-dev": "^4.3.1",
"@eslint/js": "^9.13.0",
"globals": "^15.11.0",
- "glob": "^11.0.1",
"ember-eslint-parser": "^0.5.2",
"eslint": "^9.12.0",
"eslint-config-prettier": "^9.1.0",
diff --git a/config/rollup/external.js b/config/rollup/external.js
index f8ff9f47922..f65507dd201 100644
--- a/config/rollup/external.js
+++ b/config/rollup/external.js
@@ -1,6 +1,6 @@
import path from 'path';
import fs from 'fs';
-import { globSync } from '../utils/glob.js';
+import { globSync } from 'fs';
function loadConfig() {
const configPath = path.join(process.cwd(), './package.json');
diff --git a/config/vite/fix-module-output-plugin.js b/config/vite/fix-module-output-plugin.js
index faea8857a19..9077cd47287 100644
--- a/config/vite/fix-module-output-plugin.js
+++ b/config/vite/fix-module-output-plugin.js
@@ -1,6 +1,5 @@
import child_process from 'child_process';
-import { readFileSync, writeFileSync } from 'fs';
-import { globSync } from '../utils/glob.js';
+import { globSync, readFileSync, writeFileSync } from 'fs';
import path from 'path';
const DEBUG = process.env.DEBUG === '*';
diff --git a/config/vite/keep-assets.js b/config/vite/keep-assets.js
index c35b196a571..c002c6c169c 100644
--- a/config/vite/keep-assets.js
+++ b/config/vite/keep-assets.js
@@ -1,6 +1,5 @@
import { join } from 'path';
-import { copyFileSync, mkdirSync } from 'fs';
-import { globSync } from '../utils/glob.js';
+import { copyFileSync, globSync, mkdirSync } from 'fs';
export function keepAssets({ from, include, dist }) {
return {
diff --git a/guides/community-resources.md b/guides/community-resources.md
index 656f39f74fe..42708761c8a 100644
--- a/guides/community-resources.md
+++ b/guides/community-resources.md
@@ -10,4 +10,3 @@
- [Not Your Parent's EmberData](https://runspired.com/2024/01/31/modern-ember-data.html) | *2024-01-31* by [Chris Thoburn](https://github.com/runspired)
- [Adventures in WarpDrive | Cascade On Delete](https://runspired.com/2024/11/29/cascade-on-delete.html) | *2024-11-29* by [Chris Thoburn](https://github.com/runspired)
-- [Exploring Transformed and Derived Values in @warp-drive/schema-record](https://runspired.com/2025/02/06/exploring-transformed-and-derivied-values-in-schema-record.html) | *2025-02-06* by [Chris Thoburn](https://github.com/runspired)
diff --git a/guides/index.md b/guides/index.md
index a7683f0f262..1a1a1b67863 100644
--- a/guides/index.md
+++ b/guides/index.md
@@ -10,7 +10,6 @@ Read [The Manual](./manual/0-index.md)
- [Typescript](./typescript/index.md)
- [Terminology](./terminology.md)
- [Cookbook](./cookbook/index.md)
-- [The Two Store Migration Approach](./migrating/two-store-migration.md)
## Community Resources
diff --git a/guides/requests/examples/0-basic-usage.md b/guides/requests/examples/0-basic-usage.md
index d79631a0b25..412d9a15833 100644
--- a/guides/requests/examples/0-basic-usage.md
+++ b/guides/requests/examples/0-basic-usage.md
@@ -68,7 +68,8 @@ Lets see how we'd approach this request.
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
-const fetch = new RequestManager().use([Fetch]);
+const fetch = new RequestManager();
+manager.use([Fetch]);
export default fetch;
```
diff --git a/guides/requests/index.md b/guides/requests/index.md
index 16bfeef7687..4d74ac42a04 100644
--- a/guides/requests/index.md
+++ b/guides/requests/index.md
@@ -323,9 +323,13 @@ import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
class extends Store {
- requestManager = new RequestManager()
- .use([Fetch])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
@@ -341,16 +345,19 @@ Additional handlers or a service injection like the above would need to be done
consuming application in order to make broader use of `RequestManager`.
```ts
-import Store from 'ember-data/store';
-import { CacheHandler } from '@ember-data/store';
+import Store, { CacheHandler } from 'ember-data/store';
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
export default class extends Store {
- requestManager = new RequestManager()
- .use([LegacyNetworkHandler, Fetch])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([LegacyNetworkHandler, Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
diff --git a/guides/typescript/0-installation.md b/guides/typescript/0-installation.md
index 725695231d4..b8e64d8b240 100644
--- a/guides/typescript/0-installation.md
+++ b/guides/typescript/0-installation.md
@@ -19,9 +19,7 @@ simply running the command again.
For additional documentation or to manuall install and configure, continue reading the
below guide.
-
----
-
+=========================================================
> [!CAUTION]
> EmberData does not maintain the DefinitelyTyped types for
@@ -31,50 +29,43 @@ below guide.
> [!IMPORTANT]
> EmberData's Native Types require the use of Ember's
> Native Types.
->
+
+> [!IMPORTANT]
> Type definitions need to be installed top-level, this means
> you have to install every EmberData package `ember-data`
> depends on.
> [!TIP]
-> When installing packages, use an NPM dist tag to get the latest
-> version for a given channel. E.g. `pnpm install ember-data@latest`
-> valid channels with types are `latest`, `canary`, `v4-latest` and `v4-canary`
+> When installing packages, use the `@canary` dist tag to get the latest
+> version. E.g. `pnpm install ember-data@canary`
There are currently two ways to gain access to EmberData's native types.
-1) [Use A Version That Has Types](#using-native-types)
+1) [Use Canary](#using-canary)
2) [Use Official Types Packages](#using-types-packages)
with releases `>= 4.12.*`
---
-### Using Versions That Supply Types
-
-The following versions supply their own type definitions. These type definitions will still need to be configured for use in tsconfig.
-
-- Versions of 4.x >= 4.13.0-alpha.0
-- Versions of 5.x >= 5.3.8
-
-In order to use the types for these versions, the dependencies of `ember-data` (and their peer-dependencies) must also be added to `package.json`.
-
-Generally that means the following packages are needed, though you may need fewer (or more!) depending on if you have migrated away from Adapter/Serializer and replaced Model with SchemaRecord:
-
-| Name | Latest | Canary | V4 |
-| ---- | -------| ------ | -- |
-| [ember-data](https://github.com/emberjs/data/blob/main/packages/-ember-data/README.md) |  |  |  |
-| [@ember-data/adapter](https://github.com/emberjs/data/blob/main/packages/adapter/README.md) |  |  |  |
-| [@ember-data/graph](https://github.com/emberjs/data/blob/main/packages/graph/README.md) |  |  |  |
-| [@ember-data/json-api](https://github.com/emberjs/data/blob/main/packages/json-api/README.md) |  |  |  |
-| [@ember-data/legacy-compat](https://github.com/emberjs/data/blob/main/packages/legacy-compat/README.md) |  |  |  |
-| [@ember-data/model](https://github.com/emberjs/data/blob/main/packages/model/README.md) |  |  |  |
-| [@ember-data/request](https://github.com/emberjs/data/blob/main/packages/request/README.md) |  |  |  |
-| [@ember-data/request-utils](https://github.com/emberjs/data/blob/main/packages/request-utils/README.md) |  |  |  |
-| [@ember-data/serializer](https://github.com/emberjs/data/blob/main/packages/serializer/README.md) |  |  |  |
-| [@ember-data/store](https://github.com/emberjs/data/blob/main/packages/store/README.md) |  |  |  |
-| [@ember-data/tracking](https://github.com/emberjs/data/blob/main/packages/tracking/README.md) |  |  |  |
-| [@warp-drive/core-types](https://github.com/emberjs/data/blob/main/packages/core-types/README.md) |  |  |  |
+### Using Canary
+
+Required Packages for Canary Types
+
+| Name | Version |
+| ---- | ------- |
+| [ember-data](https://github.com/emberjs/data/blob/main/packages/-ember-data/README.md) |  |
+| [@ember-data/adapter](https://github.com/emberjs/data/blob/main/packages/adapter/README.md) |  |
+| [@ember-data/graph](https://github.com/emberjs/data/blob/main/packages/graph/README.md) |  |
+| [@ember-data/json-api](https://github.com/emberjs/data/blob/main/packages/json-api/README.md) |  |
+| [@ember-data/legacy-compat](https://github.com/emberjs/data/blob/main/packages/legacy-compat/README.md) |  |
+| [@ember-data/model](https://github.com/emberjs/data/blob/main/packages/model/README.md) |  |
+| [@ember-data/request](https://github.com/emberjs/data/blob/main/packages/request/README.md) |  |
+| [@ember-data/request-utils](https://github.com/emberjs/data/blob/main/packages/request-utils/README.md) |  |
+| [@ember-data/serializer](https://github.com/emberjs/data/blob/main/packages/serializer/README.md) |  |
+| [@ember-data/store](https://github.com/emberjs/data/blob/main/packages/store/README.md) |  |
+| [@ember-data/tracking](https://github.com/emberjs/data/blob/main/packages/tracking/README.md) |  |
+| [@warp-drive/core-types](https://github.com/emberjs/data/blob/main/packages/core-types/README.md) |  |
Here's a single install command for pnpm. Swap pnpm for yarn or npm as needed.
@@ -83,7 +74,7 @@ PACKAGES=("@types/ember" "@types/ember-data" "@types/ember-data__adapter" "@type
for pkg in "${PACKAGES[@]}"; do pnpm remove "$pkg"; done
-pnpm install ember-data@latest @ember-data/adapter@latest @ember-data/graph@latest @ember-data/json-api@latest @ember-data/legacy-compat@latest @ember-data/model@latest @ember-data/request@latest @ember-data/request-utils@latest @ember-data/serializer@latest @ember-data/store@latest @ember-data/tracking@latest @warp-drive/core-types@latest
+pnpm install ember-data@canary @ember-data/adapter@canary @ember-data/graph@canary @ember-data/json-api@canary @ember-data/legacy-compat@canary @ember-data/model@canary @ember-data/request@canary @ember-data/request-utils@canary @ember-data/serializer@canary @ember-data/store@canary @ember-data/tracking@canary @warp-drive/core-types@canary
```
Here's an example change to package.json which drops all use of types from `@types/` for both Ember and EmberData and adds the appropriate canary packages.
diff --git a/guides/typescript/1-configuration.md b/guides/typescript/1-configuration.md
index 1e98871a73b..15fe1586ad0 100644
--- a/guides/typescript/1-configuration.md
+++ b/guides/typescript/1-configuration.md
@@ -4,7 +4,7 @@ There are currently two ways to gain access to EmberData's native types.
Follow the configuration guide below for the [installation](./0-installation.md)
option you chose.
-1) [Use A Version That Has Types](#using-native-types)
+1) [Use Canary](#using-canary)
2) [Use Official Types Packages](#using-types-packages)
with releases `>= 4.12.*`
@@ -14,7 +14,7 @@ with releases `>= 4.12.*`
> Native Types, the configuration below will also setup
> Your application to consume Ember's Native Types.
-### Using Native Types
+### Using Canary
To consume `alpha` stage types, you must import the types in your project's `tsconfig.json`.
@@ -26,18 +26,18 @@ potential volatility.
"compilerOptions": {
+ "types": [
+ "ember-source/types",
-+ "ember-data/unstable-preview-types",
-+ "@ember-data/store/unstable-preview-types",
-+ "@ember-data/adapter/unstable-preview-types",
-+ "@ember-data/graph/unstable-preview-types",
-+ "@ember-data/json-api/unstable-preview-types",
-+ "@ember-data/legacy-compat/unstable-preview-types",
-+ "@ember-data/request/unstable-preview-types",
-+ "@ember-data/request-utils/unstable-preview-types",
-+ "@ember-data/model/unstable-preview-types",
-+ "@ember-data/serializer/unstable-preview-types",
-+ "@ember-data/tracking/unstable-preview-types",
-+ "@warp-drive/core-types/unstable-preview-types"
++ "./node_modules/ember-data/unstable-preview-types",
++ "./node_modules/@ember-data/store/unstable-preview-types",
++ "./node_modules/@ember-data/adapter/unstable-preview-types",
++ "./node_modules/@ember-data/graph/unstable-preview-types",
++ "./node_modules/@ember-data/json-api/unstable-preview-types",
++ "./node_modules/@ember-data/legacy-compat/unstable-preview-types",
++ "./node_modules/@ember-data/request/unstable-preview-types",
++ "./node_modules/@ember-data/request-utils/unstable-preview-types",
++ "./node_modules/@ember-data/model/unstable-preview-types",
++ "./node_modules/@ember-data/serializer/unstable-preview-types",
++ "./node_modules/@ember-data/tracking/unstable-preview-types",
++ "./node_modules/@warp-drive/core-types/unstable-preview-types"
+ ]
}
}
diff --git a/guides/typescript/index.md b/guides/typescript/index.md
index f38d656d78f..f9787c47ede 100644
--- a/guides/typescript/index.md
+++ b/guides/typescript/index.md
@@ -10,10 +10,10 @@ the following two sections
---
- Installation
- - [Using Versions That Supply Types](./0-installation.md#using-versions-that-supply-types)
+ - [Using Canary](./0-installation.md#using-canary)
- [Using Types Packages](./0-installation.md#using-types-packages)
- Configuration
- - [Using Native Types](./1-configuration.md#using-native-types)
+ - [Using Canary](./1-configuration.md#using-canary)
- [Using Types Packages](./1-configuration.md#using-types-packages)
- Usage
- [Why Brands](./2-why-brands.md)
@@ -43,7 +43,7 @@ Each package in the project can choose its own stage for types.
## Contributing Type Fixes
-Even though EmberData/WarpDrive is typed, what makes for good types for a project doesn't necessarily make for good types for that project's consumers (your application).
+Even though EmberData is typed, what makes for good types for a project doesn't necessarily make for good types for that project's consumers (your application).
Currently, TypeScript support is `alpha` largely because we expect to need to improve **a lot** of type signatures to make them more useful and correct for your app.
diff --git a/package.json b/package.json
index ea7696d47ba..d407a4d57c9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "root",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"license": "MIT",
"repository": {
@@ -35,6 +35,7 @@
},
"devDependencies": {
"@babel/core": "^7.24.5",
+ "@ember/test-helpers": "3.3.0",
"@glimmer/component": "^1.1.2",
"@glint/core": "1.5.0",
"@glint/environment-ember-loose": "1.5.0",
@@ -42,7 +43,7 @@
"@glint/template": "1.5.0",
"@types/semver": "^7.5.8",
"badge-maker": "4.1.0",
- "bun-types": "^1.2.2",
+ "bun-types": "^1.1.30",
"chalk": "^4.1.2",
"co": "^4.6.0",
"command-line-args": "^5.2.1",
@@ -56,7 +57,7 @@
"lerna-changelog": "^2.2.0",
"prettier": "^3.3.2",
"prettier-plugin-ember-template-tag": "^2.0.2",
- "rimraf": "^5.0.10",
+ "rimraf": "^5.0.6",
"semver": "^7.6.3",
"silent-error": "^1.1.1",
"typescript": "^5.7.2",
@@ -168,13 +169,11 @@
"typescript"
]
},
- "allowNonAppliedPatches": true,
"patchedDependencies": {
"qunit@2.19.4": "patches/qunit@2.19.4.patch",
"testem@3.11.0": "patches/testem@3.11.0.patch",
"@ember/test-helpers@3.3.0": "patches/@ember__test-helpers@3.3.0.patch",
- "@ember/test-helpers@4.0.4": "patches/@ember__test-helpers@4.0.4.patch",
- "@ember/test-helpers@5.1.0": "patches/@ember__test-helpers@5.1.0.patch"
+ "@ember/test-helpers@4.0.4": "patches/@ember__test-helpers@4.0.4.patch"
}
}
}
diff --git a/packages/-ember-data/app/adapters/-json-api.js b/packages/-ember-data/app/adapters/-json-api.js
new file mode 100644
index 00000000000..2cbb7cd7057
--- /dev/null
+++ b/packages/-ember-data/app/adapters/-json-api.js
@@ -0,0 +1 @@
+export { default } from '@ember-data/adapter/json-api';
diff --git a/packages/-ember-data/app/initializers/ember-data.js b/packages/-ember-data/app/initializers/ember-data.js
index 4c8e1d14e7b..a3dd9a3a6f5 100644
--- a/packages/-ember-data/app/initializers/ember-data.js
+++ b/packages/-ember-data/app/initializers/ember-data.js
@@ -1,12 +1,11 @@
-import '@ember-data/request-utils/deprecation-support';
+import 'ember-data';
+
+import setupContainer from 'ember-data/setup-container';
/*
This code initializes EmberData in an Ember application.
*/
export default {
name: 'ember-data',
- initialize(application) {
- application.registerOptionsForType('serializer', { singleton: false });
- application.registerOptionsForType('adapter', { singleton: false });
- },
+ initialize: setupContainer,
};
diff --git a/packages/-ember-data/app/instance-initializers/ember-data.js b/packages/-ember-data/app/instance-initializers/ember-data.js
new file mode 100644
index 00000000000..b48556a316c
--- /dev/null
+++ b/packages/-ember-data/app/instance-initializers/ember-data.js
@@ -0,0 +1,5 @@
+/* exists only for things that historically used "after" or "before" */
+export default {
+ name: 'ember-data',
+ initialize() {},
+};
diff --git a/packages/-ember-data/app/serializers/-default.js b/packages/-ember-data/app/serializers/-default.js
new file mode 100644
index 00000000000..d617bfb1824
--- /dev/null
+++ b/packages/-ember-data/app/serializers/-default.js
@@ -0,0 +1 @@
+export { default } from '@ember-data/serializer/json';
diff --git a/packages/-ember-data/app/serializers/-json-api.js b/packages/-ember-data/app/serializers/-json-api.js
new file mode 100644
index 00000000000..59723c5ab2a
--- /dev/null
+++ b/packages/-ember-data/app/serializers/-json-api.js
@@ -0,0 +1 @@
+export { default } from '@ember-data/serializer/json-api';
diff --git a/packages/-ember-data/app/serializers/-rest.js b/packages/-ember-data/app/serializers/-rest.js
new file mode 100644
index 00000000000..d6878ba3c3e
--- /dev/null
+++ b/packages/-ember-data/app/serializers/-rest.js
@@ -0,0 +1 @@
+export { default } from '@ember-data/serializer/rest';
diff --git a/packages/-ember-data/app/services/store.js b/packages/-ember-data/app/services/store.js
index 94f7019f584..043aebdc25a 100644
--- a/packages/-ember-data/app/services/store.js
+++ b/packages/-ember-data/app/services/store.js
@@ -1,10 +1,11 @@
import { deprecate } from '@ember/debug';
export { default } from 'ember-data/store';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
deprecate(
"You are relying on ember-data auto-magically installing the store service. Use `export { default } from 'ember-data/store';` in app/services/store.js instead",
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/app/transforms/boolean.js b/packages/-ember-data/app/transforms/boolean.js
index be707e054f2..764286175bc 100644
--- a/packages/-ember-data/app/transforms/boolean.js
+++ b/packages/-ember-data/app/transforms/boolean.js
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { BooleanTransform as default } from '@ember-data/serializer/transform';
deprecate(
"You are relying on ember-data auto-magically installing the BooleanTransform. Use `export { BooleanTransform as default } from '@ember-data/serializer/transform';` in app/transforms/boolean.js instead",
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/app/transforms/date.js b/packages/-ember-data/app/transforms/date.js
index 1ba53723825..187c01d2e40 100644
--- a/packages/-ember-data/app/transforms/date.js
+++ b/packages/-ember-data/app/transforms/date.js
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { DateTransform as default } from '@ember-data/serializer/transform';
deprecate(
"You are relying on ember-data auto-magically installing the DateTransform. Use `export { DateTransform as default } from '@ember-data/serializer/transform';` in app/transforms/date.js instead",
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/app/transforms/number.js b/packages/-ember-data/app/transforms/number.js
index e33eac7b1a4..c79ef0d21dd 100644
--- a/packages/-ember-data/app/transforms/number.js
+++ b/packages/-ember-data/app/transforms/number.js
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { NumberTransform as default } from '@ember-data/serializer/transform';
deprecate(
"You are relying on ember-data auto-magically installing the NumberTransform. Use `export { NumberTransform as default } from '@ember-data/serializer/transform';` in app/transforms/number.js instead",
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/app/transforms/string.js b/packages/-ember-data/app/transforms/string.js
index 95d95453178..929095af070 100644
--- a/packages/-ember-data/app/transforms/string.js
+++ b/packages/-ember-data/app/transforms/string.js
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { StringTransform as default } from '@ember-data/serializer/transform';
deprecate(
"You are relying on ember-data auto-magically installing the StringTransform. Use `export { StringTransform as default } from '@ember-data/serializer/transform';` in app/transforms/string.js instead",
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/package.json b/packages/-ember-data/package.json
index 5952688a1be..39848b6e609 100644
--- a/packages/-ember-data/package.json
+++ b/packages/-ember-data/package.json
@@ -1,6 +1,6 @@
{
"name": "ember-data",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "The lightweight reactive data library for JavaScript applications",
"keywords": [
"ember-addon"
@@ -120,13 +120,13 @@
"@ember-data/store": "workspace:*",
"@ember-data/tracking": "workspace:*",
"@ember/edition-utils": "^1.2.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/build-config": "workspace:*"
},
"peerDependencies": {
"ember-source": "3.28.12 || ^4.0.4 || ^5.0.0 || ^6.0.0",
- "@ember/test-helpers": "^3.3.0 || ^4.0.4 || ^5.1.0",
+ "@ember/test-helpers": "^3.3.0 || ^4.0.4",
"@ember/test-waiters": "^3.1.0 || ^4.0.0",
"qunit": "^2.18.0"
},
@@ -150,7 +150,7 @@
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@types/qunit": "2.19.10",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@warp-drive/internal-config": "workspace:*",
"ember-source": "~5.12.0",
"eslint": "^9.12.0",
diff --git a/packages/-ember-data/src/-private/index.ts b/packages/-ember-data/src/-private/index.ts
index d677821855a..ef3ed492d25 100644
--- a/packages/-ember-data/src/-private/index.ts
+++ b/packages/-ember-data/src/-private/index.ts
@@ -4,15 +4,21 @@ import { deprecate } from '@ember/debug';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
import ObjectProxy from '@ember/object/proxy';
-deprecate('Importing from `ember-data/-private` is deprecated without replacement.', false, {
- id: 'ember-data:deprecate-legacy-imports',
- for: 'ember-data',
- until: '6.0',
- since: {
- enabled: '5.2',
- available: '4.13',
- },
-});
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
+deprecate(
+ 'Importing from `ember-data/-private` is deprecated without replacement.',
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:deprecate-legacy-imports',
+ for: 'ember-data',
+ until: '6.0',
+ since: {
+ enabled: '5.2',
+ available: '4.13',
+ },
+ }
+);
export { default as Store } from '../store';
diff --git a/packages/-ember-data/src/adapter.ts b/packages/-ember-data/src/adapter.ts
index 86e9e39f16c..7f870c613c2 100644
--- a/packages/-ember-data/src/adapter.ts
+++ b/packages/-ember-data/src/adapter.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/adapter';
deprecate(
'Importing from `ember-data/adapter` is deprecated. Please import from `@ember-data/adapter` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/adapters/errors.ts b/packages/-ember-data/src/adapters/errors.ts
index ef0abcbe475..ffa8d144a6a 100644
--- a/packages/-ember-data/src/adapters/errors.ts
+++ b/packages/-ember-data/src/adapters/errors.ts
@@ -1,5 +1,7 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export {
AbortError,
default as AdapterError,
@@ -10,11 +12,13 @@ export {
ServerError,
TimeoutError,
UnauthorizedError,
+ errorsArrayToHash,
+ errorsHashToArray,
} from '@ember-data/adapter/error';
deprecate(
'Importing from `ember-data/adapters/errors` is deprecated. Please import from `@ember-data/adapter` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/adapters/json-api.ts b/packages/-ember-data/src/adapters/json-api.ts
index 5b02150cc2b..d056eea91f9 100644
--- a/packages/-ember-data/src/adapters/json-api.ts
+++ b/packages/-ember-data/src/adapters/json-api.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/adapter/json-api';
deprecate(
'Importing from `ember-data/adapters/json-api` is deprecated. Please import from `@ember-data/adapter/json-api` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/adapters/rest.ts b/packages/-ember-data/src/adapters/rest.ts
index 3e8d5887c90..7bea4ec2261 100644
--- a/packages/-ember-data/src/adapters/rest.ts
+++ b/packages/-ember-data/src/adapters/rest.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/adapter/rest';
deprecate(
'Importing from `ember-data/adapters/rest` is deprecated. Please import from `@ember-data/adapter/rest` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/attr.ts b/packages/-ember-data/src/attr.ts
index d4d29c0318a..93b5cd32c1e 100644
--- a/packages/-ember-data/src/attr.ts
+++ b/packages/-ember-data/src/attr.ts
@@ -1,13 +1,19 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { attr as default } from '@ember-data/model';
-deprecate('Importing from `ember-data/attr` is deprecated. Please import from `@ember-data/model` instead.', false, {
- id: 'ember-data:deprecate-legacy-imports',
- for: 'ember-data',
- until: '6.0',
- since: {
- enabled: '5.2',
- available: '4.13',
- },
-});
+deprecate(
+ 'Importing from `ember-data/attr` is deprecated. Please import from `@ember-data/model` instead.',
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:deprecate-legacy-imports',
+ for: 'ember-data',
+ until: '6.0',
+ since: {
+ enabled: '5.2',
+ available: '4.13',
+ },
+ }
+);
diff --git a/packages/-ember-data/src/index.ts b/packages/-ember-data/src/index.ts
index 6db2ee494c9..87d64f7b851 100644
--- a/packages/-ember-data/src/index.ts
+++ b/packages/-ember-data/src/index.ts
@@ -185,6 +185,7 @@ import Transform, {
NumberTransform,
StringTransform,
} from '@ember-data/serializer/transform';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
import {
DS,
@@ -201,7 +202,7 @@ import setupContainer from './setup-container';
deprecate(
'Importing from `ember-data` is deprecated. Please import from the appropriate `@ember-data/*` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/model.ts b/packages/-ember-data/src/model.ts
index f27c9832bca..4164b3a5dd1 100644
--- a/packages/-ember-data/src/model.ts
+++ b/packages/-ember-data/src/model.ts
@@ -1,13 +1,19 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/model';
-deprecate('Importing from `ember-data/model` is deprecated. Please import from `@ember-data/model` instead.', false, {
- id: 'ember-data:deprecate-legacy-imports',
- for: 'ember-data',
- until: '6.0',
- since: {
- enabled: '5.2',
- available: '4.13',
- },
-});
+deprecate(
+ 'Importing from `ember-data/model` is deprecated. Please import from `@ember-data/model` instead.',
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:deprecate-legacy-imports',
+ for: 'ember-data',
+ until: '6.0',
+ since: {
+ enabled: '5.2',
+ available: '4.13',
+ },
+ }
+);
diff --git a/packages/-ember-data/src/relationships.ts b/packages/-ember-data/src/relationships.ts
index 24578d4944c..5c5cabfbf14 100644
--- a/packages/-ember-data/src/relationships.ts
+++ b/packages/-ember-data/src/relationships.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { belongsTo, hasMany } from '@ember-data/model';
deprecate(
'Importing from `ember-data/relationships` is deprecated. Please import from `@ember-data/model` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/serializer.ts b/packages/-ember-data/src/serializer.ts
index 33c1b590691..6c878eaeccb 100644
--- a/packages/-ember-data/src/serializer.ts
+++ b/packages/-ember-data/src/serializer.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/serializer';
deprecate(
'Importing from `ember-data/serializer` is deprecated. Please import from `@ember-data/serializer` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/serializers/embedded-records-mixin.ts b/packages/-ember-data/src/serializers/embedded-records-mixin.ts
index 8cc22d64b6f..6e5ad9a040f 100644
--- a/packages/-ember-data/src/serializers/embedded-records-mixin.ts
+++ b/packages/-ember-data/src/serializers/embedded-records-mixin.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { EmbeddedRecordsMixin as default } from '@ember-data/serializer/rest';
deprecate(
'Importing from `ember-data/serializers/embedded-records-mixin` is deprecated. Please import from `@ember-data/serializer/rest` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/serializers/json-api.ts b/packages/-ember-data/src/serializers/json-api.ts
index 272e7f8deb5..cd8bb715d8f 100644
--- a/packages/-ember-data/src/serializers/json-api.ts
+++ b/packages/-ember-data/src/serializers/json-api.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/serializer/json-api';
deprecate(
'Importing from `ember-data/serializers/json-api` is deprecated. Please import from `@ember-data/serializer/json-api` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/serializers/json.ts b/packages/-ember-data/src/serializers/json.ts
index 3148ab0ff37..46fccf1e318 100644
--- a/packages/-ember-data/src/serializers/json.ts
+++ b/packages/-ember-data/src/serializers/json.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/serializer/json';
deprecate(
'Importing from `ember-data/serializers/json` is deprecated. Please import from `@ember-data/serializer/json` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/serializers/rest.ts b/packages/-ember-data/src/serializers/rest.ts
index 41743413946..4c65661a043 100644
--- a/packages/-ember-data/src/serializers/rest.ts
+++ b/packages/-ember-data/src/serializers/rest.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/serializer/rest';
deprecate(
'Importing from `ember-data/serializers/rest` is deprecated. Please import from `@ember-data/serializer/rest` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/-ember-data/src/setup-container.ts b/packages/-ember-data/src/setup-container.ts
index f6c010d3548..00a519ce955 100644
--- a/packages/-ember-data/src/setup-container.ts
+++ b/packages/-ember-data/src/setup-container.ts
@@ -1,6 +1,8 @@
import type Application from '@ember/application';
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
function initializeStore(application: Application) {
application.registerOptionsForType('serializer', { singleton: false });
application.registerOptionsForType('adapter', { singleton: false });
@@ -10,12 +12,16 @@ export default function setupContainer(application: Application) {
initializeStore(application);
}
-deprecate('Importing from `ember-data/setup-container` is deprecated without replacement', false, {
- id: 'ember-data:deprecate-legacy-imports',
- for: 'ember-data',
- until: '6.0',
- since: {
- enabled: '5.2',
- available: '4.13',
- },
-});
+deprecate(
+ 'Importing from `ember-data/setup-container` is deprecated without replacement',
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:deprecate-legacy-imports',
+ for: 'ember-data',
+ until: '6.0',
+ since: {
+ enabled: '5.2',
+ available: '4.13',
+ },
+ }
+);
diff --git a/packages/-ember-data/src/store.ts b/packages/-ember-data/src/store.ts
index 7482209de23..d750281c67e 100644
--- a/packages/-ember-data/src/store.ts
+++ b/packages/-ember-data/src/store.ts
@@ -24,6 +24,12 @@ function hasRequestManager(store: BaseStore): boolean {
return 'requestManager' in store;
}
+// FIXME @ember-data/store
+// may also need to do all of this configuration
+// because in 4.12 we had not yet caused it to be
+// required to use `ember-data/store` to get the configured
+// store except in the case of RequestManager.
+// so for instance in tests new Store would mostly just work (tm)
export default class Store extends BaseStore {
declare _fetchManager: FetchManager;
diff --git a/packages/-ember-data/src/transform.ts b/packages/-ember-data/src/transform.ts
index ca0fb52b174..c9124e01d93 100644
--- a/packages/-ember-data/src/transform.ts
+++ b/packages/-ember-data/src/transform.ts
@@ -1,10 +1,12 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
+
export { default } from '@ember-data/serializer/transform';
deprecate(
'Importing from `ember-data/transform` is deprecated. Please import from `@ember-data/serializer/transform` instead.',
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-legacy-imports',
for: 'ember-data',
diff --git a/packages/active-record/package.json b/packages/active-record/package.json
index d46b1922f6c..998f940fd86 100644
--- a/packages/active-record/package.json
+++ b/packages/active-record/package.json
@@ -1,7 +1,7 @@
{
"name": "@ember-data/active-record",
"description": "ActiveRecord Format Support for EmberData",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": false,
"license": "MIT",
"author": "Chris Thoburn ",
@@ -45,7 +45,7 @@
"version": 2
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"peerDependencies": {
diff --git a/packages/adapter/README.md b/packages/adapter/README.md
index d153408105b..90d98d724f4 100644
--- a/packages/adapter/README.md
+++ b/packages/adapter/README.md
@@ -60,9 +60,13 @@ import RequestManager from '@ember-data/request';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
export default class extends Store {
- requestManager = new RequestManager()
- .use([LegacyNetworkHandler])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([LegacyNetworkHandler]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
diff --git a/packages/adapter/package.json b/packages/adapter/package.json
index 3c95fd24b91..20c57b23e6e 100644
--- a/packages/adapter/package.json
+++ b/packages/adapter/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/adapter",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Provides Legacy JSON:API and REST Implementations of the Adapter Interface for use with @ember-data/store",
"keywords": [
"ember-addon"
@@ -84,7 +84,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"ember-cli-test-info": "^1.0.0",
"ember-cli-string-utils": "^1.1.0",
"ember-cli-path-utils": "^1.0.0",
@@ -104,7 +104,7 @@
"@ember-data/tracking": "workspace:*",
"@ember/test-waiters": "^3.1.0",
"@glimmer/component": "^1.1.2",
- "decorator-transforms": "^2.3.0",
+ "decorator-transforms": "^2.2.2",
"@types/jquery": "^3.5.30",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
diff --git a/packages/adapter/src/error.js b/packages/adapter/src/error.js
index f19e7149b9d..d21456c1334 100644
--- a/packages/adapter/src/error.js
+++ b/packages/adapter/src/error.js
@@ -1,6 +1,9 @@
/**
@module @ember-data/adapter/error
*/
+import { deprecate } from '@ember/debug';
+
+import { DEPRECATE_HELPERS } from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
import { getOrSetGlobal } from '@warp-drive/core-types/-private';
@@ -353,3 +356,161 @@ export const ServerError = getOrSetGlobal(
extend(AdapterError, 'The adapter operation failed due to a server error')
);
ServerError.prototype.code = 'ServerError';
+
+function makeArray(value) {
+ return Array.isArray(value) ? value : [value];
+}
+
+const SOURCE_POINTER_REGEXP = /^\/?data\/(attributes|relationships)\/(.*)/;
+const SOURCE_POINTER_PRIMARY_REGEXP = /^\/?data/;
+const PRIMARY_ATTRIBUTE_KEY = 'base';
+/**
+ Convert an hash of errors into an array with errors in JSON-API format.
+ ```javascript
+ import { errorsHashToArray } from '@ember-data/adapter/error';
+
+ let errors = {
+ base: 'Invalid attributes on saving this record',
+ name: 'Must be present',
+ age: ['Must be present', 'Must be a number']
+ };
+ let errorsArray = errorsHashToArray(errors);
+ // [
+ // {
+ // title: "Invalid Document",
+ // detail: "Invalid attributes on saving this record",
+ // source: { pointer: "/data" }
+ // },
+ // {
+ // title: "Invalid Attribute",
+ // detail: "Must be present",
+ // source: { pointer: "/data/attributes/name" }
+ // },
+ // {
+ // title: "Invalid Attribute",
+ // detail: "Must be present",
+ // source: { pointer: "/data/attributes/age" }
+ // },
+ // {
+ // title: "Invalid Attribute",
+ // detail: "Must be a number",
+ // source: { pointer: "/data/attributes/age" }
+ // }
+ // ]
+ ```
+ @method errorsHashToArray
+ @for @ember-data/adapter/error
+ @static
+ @deprecated
+ @public
+ @param {Object} errors hash with errors as properties
+ @return {Array} array of errors in JSON-API format
+*/
+export function errorsHashToArray(errors) {
+ if (DEPRECATE_HELPERS) {
+ deprecate(`errorsHashToArray helper has been deprecated.`, false, {
+ id: 'ember-data:deprecate-errors-hash-to-array-helper',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ });
+ const out = [];
+
+ if (errors) {
+ Object.keys(errors).forEach((key) => {
+ const messages = makeArray(errors[key]);
+ for (let i = 0; i < messages.length; i++) {
+ let title = 'Invalid Attribute';
+ let pointer = `/data/attributes/${key}`;
+ if (key === PRIMARY_ATTRIBUTE_KEY) {
+ title = 'Invalid Document';
+ pointer = `/data`;
+ }
+ out.push({
+ title: title,
+ detail: messages[i],
+ source: {
+ pointer: pointer,
+ },
+ });
+ }
+ });
+ }
+
+ return out;
+ }
+ assert(`errorsHashToArray helper has been removed`);
+}
+
+/**
+ Convert an array of errors in JSON-API format into an object.
+
+ ```javascript
+ import { errorsArrayToHash } from '@ember-data/adapter/error';
+
+ let errorsArray = [
+ {
+ title: 'Invalid Attribute',
+ detail: 'Must be present',
+ source: { pointer: '/data/attributes/name' }
+ },
+ {
+ title: 'Invalid Attribute',
+ detail: 'Must be present',
+ source: { pointer: '/data/attributes/age' }
+ },
+ {
+ title: 'Invalid Attribute',
+ detail: 'Must be a number',
+ source: { pointer: '/data/attributes/age' }
+ }
+ ];
+
+ let errors = errorsArrayToHash(errorsArray);
+ // {
+ // "name": ["Must be present"],
+ // "age": ["Must be present", "must be a number"]
+ // }
+ ```
+
+ @method errorsArrayToHash
+ @static
+ @for @ember-data/adapter/error
+ @deprecated
+ @public
+ @param {Array} errors array of errors in JSON-API format
+ @return {Object}
+*/
+export function errorsArrayToHash(errors) {
+ if (DEPRECATE_HELPERS) {
+ deprecate(`errorsArrayToHash helper has been deprecated.`, false, {
+ id: 'ember-data:deprecate-errors-array-to-hash-helper',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ });
+ const out = {};
+
+ if (errors) {
+ errors.forEach((error) => {
+ if (error.source && error.source.pointer) {
+ let key = error.source.pointer.match(SOURCE_POINTER_REGEXP);
+
+ if (key) {
+ key = key[2];
+ } else if (error.source.pointer.search(SOURCE_POINTER_PRIMARY_REGEXP) !== -1) {
+ key = PRIMARY_ATTRIBUTE_KEY;
+ }
+
+ if (key) {
+ out[key] = out[key] || [];
+ out[key].push(error.detail || error.title);
+ }
+ }
+ });
+ }
+
+ return out;
+ }
+ assert(`errorsArrayToHash helper has been removed`);
+}
diff --git a/packages/build-config/package.json b/packages/build-config/package.json
index ddc6bafad23..4b51db38a69 100644
--- a/packages/build-config/package.json
+++ b/packages/build-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@warp-drive/build-config",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Provides Build Configuration for projects using WarpDrive or EmberData",
"keywords": [
"ember-data",
@@ -41,8 +41,8 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
- "@embroider/addon-shim": "^1.9.0",
+ "@embroider/macros": "^1.16.6",
+ "@embroider/addon-shim": "^1.8.9",
"babel-import-util": "^2.1.1",
"broccoli-funnel": "^3.0.8",
"semver": "^7.6.3"
@@ -56,7 +56,7 @@
"@babel/core": "^7.24.5",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
"typescript": "^5.7.2",
- "bun-types": "^1.2.2",
+ "bun-types": "^1.1.30",
"vite": "^5.2.11"
},
"engines": {
diff --git a/packages/build-config/src/-private/utils/deprecations.ts b/packages/build-config/src/-private/utils/deprecations.ts
index 1a8862e389b..621b68e0c83 100644
--- a/packages/build-config/src/-private/utils/deprecations.ts
+++ b/packages/build-config/src/-private/utils/deprecations.ts
@@ -8,7 +8,7 @@ function deprecationIsResolved(deprecatedSince: MajorMinor, compatVersion: Major
return semver.lte(semver.minVersion(deprecatedSince)!, semver.minVersion(compatVersion)!);
}
-const NextMajorVersion = '6.';
+const NextMajorVersion = '5.';
function deprecationIsNextMajorCycle(deprecatedSince: MajorMinor) {
return deprecatedSince.startsWith(NextMajorVersion);
@@ -20,17 +20,17 @@ export function getDeprecations(
): { [key in DeprecationFlag]: boolean } {
const flags = {} as Record;
const keys = Object.keys(CURRENT_DEPRECATIONS) as DeprecationFlag[];
- const DISABLE_7X_DEPRECATIONS = deprecations?.DISABLE_7X_DEPRECATIONS ?? true;
+ const DISABLE_6X_DEPRECATIONS = deprecations?.DISABLE_6X_DEPRECATIONS ?? true;
keys.forEach((flag) => {
const deprecatedSince = CURRENT_DEPRECATIONS[flag];
- const isDeactivatedDeprecationNotice = DISABLE_7X_DEPRECATIONS && deprecationIsNextMajorCycle(deprecatedSince);
+ const isDeactivatedDeprecationNotice = DISABLE_6X_DEPRECATIONS && deprecationIsNextMajorCycle(deprecatedSince);
let flagState = true; // default to no code-stripping
if (!isDeactivatedDeprecationNotice) {
// if we have a specific flag setting, use it
if (typeof deprecations?.[flag] === 'boolean') {
- flagState = deprecations?.[flag]!;
+ flagState = deprecations?.[flag];
} else if (compatVersion) {
// if we are told we are compatible with a version
// we check if we can strip this flag
diff --git a/packages/build-config/src/debugging.ts b/packages/build-config/src/debugging.ts
index ce19966afe2..60d6a2880c5 100644
--- a/packages/build-config/src/debugging.ts
+++ b/packages/build-config/src/debugging.ts
@@ -103,11 +103,3 @@ export const LOG_GRAPH: boolean = false;
* @public
*/
export const LOG_INSTANCE_CACHE: boolean = false;
-/**
- * Log key count metrics, useful for performance
- * debugging.
- *
- * @property {boolean} LOG_METRIC_COUNTS
- * @public
- */
-export const LOG_METRIC_COUNTS: boolean = false;
diff --git a/packages/build-config/src/deprecation-versions.ts b/packages/build-config/src/deprecation-versions.ts
index 4f9389e1b09..80d5148b4d3 100644
--- a/packages/build-config/src/deprecation-versions.ts
+++ b/packages/build-config/src/deprecation-versions.ts
@@ -88,6 +88,621 @@
* @public
*/
export const DEPRECATE_CATCH_ALL = '99.0';
+
+/**
+ * **id: ember-data:rsvp-unresolved-async**
+ *
+ * Deprecates when a request promise did not resolve prior to the store tearing down.
+ *
+ * Note: in most cases even with the promise guard that is now being deprecated
+ * a test crash would still be encountered.
+ *
+ * To resolve: Tests or Fastboot instances which crash need to find triggers requests
+ * and properly await them before tearing down.
+ *
+ * @property DEPRECATE_RSVP_PROMISE
+ * @since 4.4
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_RSVP_PROMISE = '4.4';
+
+/**
+ * **id: ember-data:model-save-promise**
+ *
+ * Affects
+ * - model.save / store.saveRecord
+ * - model.reload
+ *
+ * Deprecates the promise-proxy returned by these methods in favor of
+ * a Promise return value.
+ *
+ * To resolve this deprecation, `await` or `.then` the return value
+ * before doing work with the result instead of accessing values via
+ * the proxy.
+ *
+ * To continue utilizing flags such as `isPending` in your templates
+ * consider using [ember-promise-helpers](https://github.com/fivetanley/ember-promise-helpers)
+ *
+ * @property DEPRECATE_SAVE_PROMISE_ACCESS
+ * @since 4.4
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_SAVE_PROMISE_ACCESS = '4.4';
+
+/**
+ * **id: ember-data:deprecate-snapshot-model-class-access**
+ *
+ * Deprecates accessing the factory class for a given resource type
+ * via properties on various classes.
+ *
+ * Guards
+ *
+ * - SnapshotRecordArray.type
+ * - Snapshot.type
+ * - RecordArray.type
+ *
+ * Use `store.modelFor()` instead.
+ *
+ * @property DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS
+ * @since 4.5
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS = '4.5';
+
+/**
+ * **id: ember-data:deprecate-store-find**
+ *
+ * Deprecates using `store.find` instead of `store.findRecord`. Typically
+ * `store.find` is a mistaken call that occurs when using implicit route behaviors
+ * in Ember which attempt to derive how to load data via parsing the route params
+ * for a route which does not implement a `model` hook.
+ *
+ * To resolve, use `store.findRecord`. This may require implementing an associated
+ * route's `model() {}` hook.
+ *
+ * @property DEPRECATE_STORE_FIND
+ * @since 4.5
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_STORE_FIND = '4.5';
+
+/**
+ * **id: ember-data:deprecate-has-record-for-id**
+ *
+ * Deprecates `store.hasRecordForId(type, id)` in favor of `store.peekRecord({ type, id }) !== null`.
+ *
+ * Broadly speaking, while the ability to query for presence is important, a key distinction exists
+ * between these methods that make relying on `hasRecordForId` unsafe, as it may report `true` for a
+ * record which is not-yet loaded and un-peekable. `peekRecord` offers a safe mechanism by which to check
+ * for whether a record is present in a usable manner.
+ *
+ * @property DEPRECATE_HAS_RECORD
+ * @since 4.5
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_HAS_RECORD = '4.5';
+
+/**
+ * **id: ember-data:deprecate-string-arg-schemas**
+ *
+ * Deprecates `schema.attributesDefinitionFor(type)` and
+ * `schema.relationshipsDefinitionFor(type)` in favor of
+ * a consistent object signature (`identifier | { type }`).
+ *
+ * To resolve change
+ *
+ * ```diff
+ * - store.getSchemaDefinitionService().attributesDefinitionFor('user')
+ * + store.getSchemaDefinitionService().attributesDefinitionFor({ type: 'user' })
+ * ```
+ *
+ * @property DEPRECATE_STRING_ARG_SCHEMAS
+ * @since 4.5
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_STRING_ARG_SCHEMAS = '4.5';
+
+/**
+ * **id: ember-data:deprecate-secret-adapter-fallback**
+ *
+ * Deprecates the secret `-json-api` fallback adapter in favor
+ * or an explicit "catch all" application adapter. In addition
+ * to this deprecation ensuring the user has explicitly chosen an
+ * adapter, this ensures that the user may choose to use no adapter
+ * at all.
+ *
+ * Simplest fix:
+ *
+ * */app/adapters/application.js*
+ * ```js
+ * export { default } from '@ember-data/adapter/json-api';
+ * ```
+ *
+ * @property DEPRECATE_JSON_API_FALLBACK
+ * @since 4.5
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_JSON_API_FALLBACK = '4.5';
+
+/**
+ * **id: ember-data:deprecate-model-reopen**
+ *
+ * ----
+ *
+ * For properties known ahead of time, instead of
+ *
+ * ```ts
+ * class User extends Model { @attr firstName; }
+ *
+ * User.reopen({ lastName: attr() });
+ * ```
+ *
+ * Extend `User` again or include it in the initial definition.
+ *
+ * ```ts
+ * class User extends Model { @attr firstName; @attr lastName }
+ * ```
+ *
+ * For properties generated dynamically, consider registering
+ * a `SchemaDefinitionService` with the store , as such services
+ * are capable of dynamically adjusting their schemas, and utilize
+ * the `instantiateRecord` hook to create a Proxy based class that
+ * can react to the changes in the schema.
+ *
+ *
+ * Use Foo extends Model to extend your class instead
+ *
+ *
+ *
+ *
+ * **id: ember-data:deprecate-model-reopenclass**
+ *
+ * ----
+ *
+ * Instead of reopenClass, define `static` properties with native class syntax
+ * or add them to the final object.
+ *
+ * ```ts
+ * // instead of
+ * User.reopenClass({ aStaticMethod() {} });
+ *
+ * // do this
+ * class User {
+ * static aStaticMethod() {}
+ * }
+ *
+ * // or do this
+ * User.aStaticMethod = function() {}
+ * ```
+ *
+ *
+ * @property DEPRECATE_MODEL_REOPEN
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_MODEL_REOPEN = '4.7';
+
+/**
+ * **id: ember-data:deprecate-early-static**
+ *
+ * This deprecation triggers if static computed properties
+ * or methods are triggered without looking up the record
+ * via the store service's `modelFor` hook. Accessing this
+ * static information without looking up the model via the
+ * store most commonly occurs when
+ *
+ * - using ember-cli-mirage (to fix, refactor to not use its auto-discovery of ember-data models)
+ * - importing a model class and accessing its static information via the import
+ *
+ * Instead of
+ *
+ * ```js
+ * import User from 'my-app/models/user';
+ *
+ * const relationships = User.relationshipsByName;
+ * ```
+ *
+ * Do *at least* this
+ *
+ * ```js
+ * const relationships = store.modelFor('user').relationshipsByName;
+ * ```
+ *
+ * However, the much more future proof refactor is to not use `modelFor` at all but instead
+ * to utilize the schema service for this static information.
+ *
+ * ```js
+ * const relationships = store.getSchemaDefinitionService().relationshipsDefinitionFor({ type: 'user' });
+ * ```
+ *
+ *
+ * @property DEPRECATE_EARLY_STATIC
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_EARLY_STATIC = '4.7';
+
+/**
+ * **id: ember-data:deprecate-errors-hash-to-array-helper**
+ * **id: ember-data:deprecate-errors-array-to-hash-helper**
+ * **id: ember-data:deprecate-normalize-modelname-helper**
+ *
+ * Deprecates `errorsHashToArray` `errorsArrayToHash` and `normalizeModelName`
+ *
+ * Users making use of these (already private) utilities can trivially copy them
+ * into their own codebase to continue using them, though we recommend refactoring
+ * to a more direct conversion into the expected errors format for the errors helpers.
+ *
+ * For refactoring normalizeModelName we also recommend following the guidance in
+ * [RFC#740 Deprecate Non-Strict Types](https://github.com/emberjs/rfcs/pull/740).
+ *
+ *
+ * @property DEPRECATE_HELPERS
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_HELPERS = '4.7';
+
+/**
+ * **id: ember-data:deprecate-promise-many-array-behavior**
+ *
+ * [RFC Documentation](https://rfcs.emberjs.com/id/0745-ember-data-deprecate-methods-on-promise-many-array)
+ *
+ * This deprecation deprecates accessing values on the asynchronous proxy
+ * in favor of first "resolving" or "awaiting" the promise to retrieve a
+ * synchronous value.
+ *
+ * Template iteration of the asynchronous value will still work and not trigger
+ * the deprecation, but all JS access should be avoided and HBS access for anything
+ * but `{{#each}}` should also be refactored.
+ *
+ * Recommended approaches include using the addon `ember-promise-helpers`, using
+ * Ember's `resource` pattern (including potentially the addon `ember-data-resources`),
+ * resolving the value in routes/provider components, or using the references API.
+ *
+ * An example of using the [hasMany](https://api.emberjs.com/ember-data/4.11/classes/Model/methods/hasMany?anchor=hasMany) [reference API](https://api.emberjs.com/ember-data/release/classes/HasManyReference):
+ *
+ * ```ts
+ * // get the synchronous "ManyArray" value for the asynchronous "friends" relationship.
+ * // note, this will return `null` if the relationship has not been loaded yet
+ * const value = person.hasMany('friends').value();
+ *
+ * // to get just the list of related IDs
+ * const ids = person.hasMany('friends').ids();
+ * ```
+ *
+ * References participate in autotracking and getters/cached getters etc. which consume them
+ * will recompute if the value changes.
+ *
+ * @property DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS = '4.7';
+
+/**
+ * **id: ember-data:deprecate-non-strict-relationships**
+ *
+ * Deprecates when belongsTo and hasMany relationships are defined
+ * without specifying the inverse record's type.
+ *
+ * Instead of
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany() employees;
+ * }
+ * class Employee extends Model {
+ * @belongsTo() company;
+ * }
+ * ```
+ *
+ * Use
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany('employee', { async: true, inverse: 'company' }) employees;
+ * }
+ *
+ * class Employee extends Model {
+ * @belongsTo('company', { async: true, inverse: 'employees' }) company;
+ * }
+ * ```
+ *
+ * @property DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE = '4.7';
+
+/**
+ * **id: ember-data:deprecate-non-strict-relationships**
+ *
+ * Deprecates when belongsTo and hasMany relationships are defined
+ * without specifying whether the relationship is asynchronous.
+ *
+ * The current behavior is that relationships which do not define
+ * this setting are aschronous (`{ async: true }`).
+ *
+ * Instead of
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany('employee') employees;
+ * }
+ * class Employee extends Model {
+ * @belongsTo('company') company;
+ * }
+ * ```
+ *
+ * Use
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany('employee', { async: true, inverse: 'company' }) employees;
+ * }
+ *
+ * class Employee extends Model {
+ * @belongsTo('company', { async: true, inverse: 'employees' }) company;
+ * }
+ * ```
+ *
+ * @property DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC = '4.7';
+
+/**
+ * **id: ember-data:deprecate-non-strict-relationships**
+ *
+ * Deprecates when belongsTo and hasMany relationships are defined
+ * without specifying the inverse field on the related type.
+ *
+ * The current behavior is that relationships which do not define
+ * this setting have their inverse determined at runtime, which is
+ * potentially non-deterministic when mixins and polymorphism are involved.
+ *
+ * If an inverse relationship exists and you wish changes on one side to
+ * reflect onto the other side, use the inverse key. If you wish to not have
+ * changes reflected or no inverse relationship exists, specify `inverse: null`.
+ *
+ * Instead of
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany('employee') employees;
+ * }
+ * class Employee extends Model {
+ * @belongsTo('company') company;
+ * }
+ * ```
+ *
+ * Use
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany('employee', { async: true, inverse: 'company' }) employees;
+ * }
+ *
+ * class Employee extends Model {
+ * @belongsTo('company', { async: true, inverse: 'employees' }) company;
+ * }
+ * ```
+ *
+ * Instead of
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany('employee') employees;
+ * }
+ * class Employee extends Model {
+ * @attr name;
+ * }
+ * ```
+ *
+ * Use
+ *
+ * ```ts
+ * class Company extends Model {
+ * @hasMany('employee', { async: true, inverse: null }) employees;
+ * }
+ *
+ * class Employee extends Model {
+ * @attr name;
+ * }
+ * ```
+ *
+ * @property DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE = '4.7';
+
+/**
+ * **id: ember-data:no-a-with-array-like**
+ *
+ * Deprecates when calling `A()` on an EmberData ArrayLike class
+ * is detected. This deprecation may not always trigger due to complexities
+ * in ember-source versions and the use (or disabling) of prototype extensions.
+ *
+ * To fix, just use the native array methods instead of the EmberArray methods
+ * and refrain from wrapping the array in `A()`.
+ *
+ * Note that some computed property macros may themselves utilize `A()`, in which
+ * scenario the computed properties need to be upgraded to octane syntax.
+ *
+ * For instance, instead of:
+ *
+ * ```ts
+ * class extends Component {
+ * @filterBy('items', 'isComplete') completedItems;
+ * }
+ * ```
+ *
+ * Use the following:
+ *
+ * ```ts
+ * class extends Component {
+ * get completedItems() {
+ * return this.items.filter(item => item.isComplete);
+ * }
+ * }
+ * ```
+ *
+ * @property DEPRECATE_A_USAGE
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_A_USAGE = '4.7';
+
+/**
+ * **id: ember-data:deprecate-promise-proxies**
+ *
+ * Additional Reading: [RFC#846 Deprecate Proxies](https://rfcs.emberjs.com/id/0846-ember-data-deprecate-proxies)
+ *
+ * Deprecates using the proxy object/proxy array capabilities of values returned from
+ *
+ * - `store.findRecord`
+ * - `store.findAll`
+ * - `store.query`
+ * - `store.queryRecord`
+ * - `record.save`
+ * - `recordArray.save`
+ * - `recordArray.update`
+ *
+ * These methods will now return a native Promise that resolves with the value.
+ *
+ * Note that this does not deprecate the proxy behaviors of `PromiseBelongsTo`. See RFC for reasoning.
+ * The opportunity should still be taken if available to stop using these proxy behaviors; however, this class
+ * will remain until `import Model from '@ember-data/model';` is deprecated more broadly.
+ *
+ * @property DEPRECATE_PROMISE_PROXIES
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_PROMISE_PROXIES = '4.7';
+
+/**
+ * **id: ember-data:deprecate-array-like**
+ *
+ * Deprecates Ember "Array-like" methods on RecordArray and ManyArray.
+ *
+ * These are the arrays returned respectively by `store.peekAll()`, `store.findAll()`and
+ * hasMany relationships on instance of Model or `record.hasMany('relationshipName').value()`.
+ *
+ * The appropriate refactor is to treat these arrays as native arrays and to use native array methods.
+ *
+ * For instance, instead of:
+ *
+ * ```ts
+ * users.firstObject;
+ * ```
+ *
+ * Use:
+ *
+ * ```ts
+ * users[0];
+ * // or
+ * users.at(0);
+ * ```
+ *
+ * @property DEPRECATE_ARRAY_LIKE
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_ARRAY_LIKE = '4.7';
+
+/**
+ * **id: **
+ *
+ * This is a planned deprecation which will trigger when observer or computed
+ * chains are used to watch for changes on any EmberData RecordArray, ManyArray
+ * or PromiseManyArray.
+ *
+ * Support for these chains is currently guarded by the inactive deprecation flag
+ * listed here.
+ *
+ * @property DEPRECATE_COMPUTED_CHAINS
+ * @since 5.0
+ * @until 6.0
+ * @public
+ */
+export const DEPRECATE_COMPUTED_CHAINS = '5.0';
+
+/**
+ * **id: ember-data:non-explicit-relationships**
+ *
+ * Deprecates when polymorphic relationships are detected via inheritance or mixins
+ * and no polymorphic relationship configuration has been setup.
+ *
+ * For further reading please review [RFC#793](https://rfcs.emberjs.com/id/0793-polymporphic-relations-without-inheritance)
+ * which introduced support for explicit relationship polymorphism without
+ * mixins or inheritance.
+ *
+ * You may still use mixins and inheritance to setup your polymorphism; however, the class
+ * structure is no longer what drives the design. Instead polymorphism is "traits" based or "structural":
+ * so long as each model which can satisfy the polymorphic relationship defines the inverse in the same
+ * way they work.
+ *
+ * Notably: `inverse: null` relationships can receive any type as a record with no additional configuration
+ * at all.
+ *
+ * Example Polymorphic Relationship Configuration
+ *
+ * ```ts
+ * // polymorphic relationship
+ * class Tag extends Model {
+ * @hasMany("taggable", { async: false, polymorphic: true, inverse: "tags" }) tagged;
+ * }
+ *
+ * // an inverse concrete relationship (e.g. satisfies "taggable")
+ * class Post extends Model {
+ * @hasMany("tag", { async: false, inverse: "tagged", as: "taggable" }) tags;
+ * }
+ * ```
+ *
+ * @property DEPRECATE_NON_EXPLICIT_POLYMORPHISM
+ * @since 4.7
+ * @until 5.0
+ * @public
+ */
+export const DEPRECATE_NON_EXPLICIT_POLYMORPHISM = '4.7';
+
+/**
+ * **id: ember-data:deprecate-many-array-duplicates**
+ *
+ * When the flag is `true` (default), adding duplicate records to a `ManyArray`
+ * is deprecated in non-production environments. In production environments,
+ * duplicate records added to a `ManyArray` will be deduped and no error will
+ * be thrown.
+ *
+ * When the flag is `false`, an error will be thrown when duplicates are added.
+ *
+ * @property DEPRECATE_MANY_ARRAY_DUPLICATES
+ * @since 5.3
+ * @until 6.0
+ * @public
+ */
+export const DEPRECATE_MANY_ARRAY_DUPLICATES = '4.12'; // '5.3';
+
/**
* **id: ember-data:deprecate-non-strict-types**
*
@@ -146,42 +761,6 @@ export const DEPRECATE_NON_STRICT_TYPES = '5.3';
*/
export const DEPRECATE_NON_STRICT_ID = '5.3';
-/**
- * **id: **
- *
- * This is a planned deprecation which will trigger when observer or computed
- * chains are used to watch for changes on any EmberData LiveArray, CollectionRecordArray,
- * ManyArray or PromiseManyArray.
- *
- * Support for these chains is currently guarded by the deprecation flag
- * listed here, enabling removal of the behavior if desired.
- *
- * @property DEPRECATE_COMPUTED_CHAINS
- * @since 5.0
- * @until 6.0
- * @public
- */
-export const DEPRECATE_COMPUTED_CHAINS = '5.0';
-
-/**
- * **id: ember-data:deprecate-legacy-imports**
- *
- * Deprecates when importing from `ember-data/*` instead of `@ember-data/*`
- * in order to prepare for the eventual removal of the legacy `ember-data/*`
- *
- * All imports from `ember-data/*` should be updated to `@ember-data/*`
- * except for `ember-data/store`. When you are using `ember-data` (as opposed to
- * installing the indivudal packages) you should import from `ember-data/store`
- * instead of `@ember-data/store` in order to receive the appropriate configuration
- * of defaults.
- *
- * @property DEPRECATE_LEGACY_IMPORTS
- * @since 5.3
- * @until 6.0
- * @public
- */
-export const DEPRECATE_LEGACY_IMPORTS = '5.3';
-
/**
* **id: ember-data:deprecate-non-unique-collection-payloads**
*
@@ -372,23 +951,6 @@ export const DEPRECATE_NON_UNIQUE_PAYLOADS = '5.3';
*/
export const DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE = '5.3';
-/**
- * **id: ember-data:deprecate-many-array-duplicates**
- *
- * When the flag is `true` (default), adding duplicate records to a `ManyArray`
- * is deprecated in non-production environments. In production environments,
- * duplicate records added to a `ManyArray` will be deduped and no error will
- * be thrown.
- *
- * When the flag is `false`, an error will be thrown when duplicates are added.
- *
- * @property DEPRECATE_MANY_ARRAY_DUPLICATES
- * @since 5.3
- * @until 6.0
- * @public
- */
-export const DEPRECATE_MANY_ARRAY_DUPLICATES = '5.3';
-
/**
* **id: ember-data:deprecate-store-extends-ember-object**
*
@@ -461,18 +1023,18 @@ export const ENABLE_LEGACY_SCHEMA_SERVICE = '5.4';
export const DEPRECATE_EMBER_INFLECTOR = '5.3';
/**
- * This is a special flag that can be used to opt-in early to receiving deprecations introduced in 6.x
- * which have had their infra backported to 5.x versions of EmberData.
+ * This is a special flag that can be used to opt-in early to receiving deprecations introduced in 5.x
+ * which have had their infra backported to 4.x versions of EmberData.
*
- * When this flag is not present or set to `true`, the deprecations from the 6.x branch
+ * When this flag is not present or set to `true`, the deprecations from the 5.x branch
* will not print their messages and the deprecation cannot be resolved.
*
- * When this flag is present and set to `false`, the deprecations from the 6.x branch will
+ * When this flag is present and set to `false`, the deprecations from the 5.x branch will
* print and can be resolved.
*
- * @property DISABLE_7X_DEPRECATIONS
- * @since 5.3
- * @until 7.0
+ * @property DISABLE_6X_DEPRECATIONS
+ * @since 4.13
+ * @until 5.0
* @public
*/
-export const DISABLE_7X_DEPRECATIONS = '7.0';
+export const DISABLE_6X_DEPRECATIONS = '6.0';
diff --git a/packages/build-config/src/deprecations.ts b/packages/build-config/src/deprecations.ts
index 03413ccf20d..344315da32f 100644
--- a/packages/build-config/src/deprecations.ts
+++ b/packages/build-config/src/deprecations.ts
@@ -1,13 +1,31 @@
// deprecations
export const DEPRECATE_CATCH_ALL: boolean = true;
+export const DEPRECATE_SAVE_PROMISE_ACCESS: boolean = true;
+export const DEPRECATE_RSVP_PROMISE: boolean = true;
+export const DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS: boolean = true;
+export const DEPRECATE_STORE_FIND: boolean = true;
+export const DEPRECATE_HAS_RECORD: boolean = true;
+// FIXME we can potentially drop this since the cache implementations we care about turn out to all be stuck 4.6 or earlier
+export const DEPRECATE_STRING_ARG_SCHEMAS: boolean = true;
+export const DEPRECATE_JSON_API_FALLBACK: boolean = true;
+export const DEPRECATE_MODEL_REOPEN: boolean = true;
+export const DEPRECATE_EARLY_STATIC: boolean = true;
+export const DEPRECATE_HELPERS: boolean = true;
+export const DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS: boolean = true;
+export const DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE: boolean = true;
+export const DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC: boolean = true;
+export const DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE: boolean = true;
+export const DEPRECATE_A_USAGE: boolean = true;
+export const DEPRECATE_PROMISE_PROXIES: boolean = true;
+export const DEPRECATE_ARRAY_LIKE: boolean = true;
export const DEPRECATE_COMPUTED_CHAINS: boolean = true;
+export const DEPRECATE_NON_EXPLICIT_POLYMORPHISM: boolean = true;
+export const DEPRECATE_MANY_ARRAY_DUPLICATES: boolean = true;
export const DEPRECATE_NON_STRICT_TYPES: boolean = true;
export const DEPRECATE_NON_STRICT_ID: boolean = true;
-export const DEPRECATE_LEGACY_IMPORTS: boolean = true;
export const DEPRECATE_NON_UNIQUE_PAYLOADS: boolean = true;
export const DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE: boolean = true;
-export const DEPRECATE_MANY_ARRAY_DUPLICATES: boolean = true;
export const DEPRECATE_STORE_EXTENDS_EMBER_OBJECT: boolean = true;
export const ENABLE_LEGACY_SCHEMA_SERVICE: boolean = true;
export const DEPRECATE_EMBER_INFLECTOR: boolean = true;
-export const DISABLE_7X_DEPRECATIONS: boolean = true;
+export const DISABLE_6X_DEPRECATIONS: boolean = true;
diff --git a/packages/core-types/package.json b/packages/core-types/package.json
index 62d3b2775d8..f2301ab135a 100644
--- a/packages/core-types/package.json
+++ b/packages/core-types/package.json
@@ -1,6 +1,6 @@
{
"name": "@warp-drive/core-types",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Provides core logic, utils and types for WarpDrive and EmberData",
"keywords": [
"ember-addon"
@@ -38,7 +38,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"devDependencies": {
diff --git a/packages/core-types/src/identifier.ts b/packages/core-types/src/identifier.ts
index d740cb5dda2..605c5ef1d07 100644
--- a/packages/core-types/src/identifier.ts
+++ b/packages/core-types/src/identifier.ts
@@ -2,19 +2,13 @@
@module @ember-data/store
*/
-import { DEBUG } from '@warp-drive/build-config/env';
-
// provided for additional debuggability
export const DEBUG_CLIENT_ORIGINATED: unique symbol = Symbol('record-originated-on-client');
export const DEBUG_IDENTIFIER_BUCKET: unique symbol = Symbol('identifier-bucket');
export const DEBUG_STALE_CACHE_OWNER: unique symbol = Symbol('warpDriveStaleCache');
-function ProdSymbol(str: T): T {
- return DEBUG ? (Symbol(str) as unknown as T) : str;
-}
-
// also present in production
-export const CACHE_OWNER = ProdSymbol('__$co');
+export const CACHE_OWNER: unique symbol = Symbol('warpDriveCache');
export type IdentifierBucket = 'record' | 'document';
diff --git a/packages/core-types/src/request.ts b/packages/core-types/src/request.ts
index 115fa4fed29..559e4e5ffa1 100644
--- a/packages/core-types/src/request.ts
+++ b/packages/core-types/src/request.ts
@@ -302,8 +302,6 @@ export type RequestInfo = Request & {
options?: Record;
[RequestSignature]?: RT;
-
- [EnableHydration]?: boolean;
};
/**
diff --git a/packages/core-types/src/schema/fields.ts b/packages/core-types/src/schema/fields.ts
index 34056b03715..22b485841f2 100644
--- a/packages/core-types/src/schema/fields.ts
+++ b/packages/core-types/src/schema/fields.ts
@@ -729,40 +729,6 @@ export type LegacyBelongsToField = {
*/
polymorphic?: boolean;
- /**
- * Whether this field should ever make use of the legacy support infra
- * from @ember-data/model and the LegacyNetworkMiddleware for adapters and serializers.
- *
- * When true, none of the legacy support will be utilized. Sync relationships
- * will be expected to already have all their data. When reloading a sync relationship
- * you would be expected to have a `related link` available from a prior relationship
- * payload e.g.
- *
- * ```ts
- * {
- * data: {
- * type: 'user',
- * id: '2',
- * attributes: { name: 'Chris' },
- * relationships: {
- * bestFriend: {
- * links: { related: "/users/1/bestFriend" },
- * data: { type: 'user', id: '1' },
- * }
- * }
- * },
- * included: [
- * { type: 'user', id: '1', attributes: { name: 'Krystan' } }
- * ]
- * }
- * ```
- *
- * Async relationships will be loaded via their link if needed.
- *
- * @typedoc
- */
- linksMode?: true;
-
/**
* When omitted, the cache data for this field will
* clear local state of all changes except for the
@@ -853,40 +819,6 @@ export type LegacyHasManyField = {
*/
polymorphic?: boolean;
- /**
- * Whether this field should ever make use of the legacy support infra
- * from @ember-data/model and the LegacyNetworkMiddleware for adapters and serializers.
- *
- * When true, none of the legacy support will be utilized. Sync relationships
- * will be expected to already have all their data. When reloading a sync relationship
- * you would be expected to have a `related link` available from a prior relationship
- * payload e.g.
- *
- * ```ts
- * {
- * data: {
- * type: 'user',
- * id: '2',
- * attributes: { name: 'Chris' },
- * relationships: {
- * bestFriends: {
- * links: { related: "/users/1/bestFriends" },
- * data: [ { type: 'user', id: '1' } ],
- * }
- * }
- * },
- * included: [
- * { type: 'user', id: '1', attributes: { name: 'Krystan' } }
- * ]
- * }
- * ```
- *
- * Async relationships will be loaded via their link if needed.
- *
- * @typedoc
- */
- linksMode?: true;
-
/**
* When omitted, the cache data for this field will
* clear local state of all changes except for the
diff --git a/packages/core-types/src/spec/document.ts b/packages/core-types/src/spec/document.ts
index 93ab42c4e67..0ee5fa01399 100644
--- a/packages/core-types/src/spec/document.ts
+++ b/packages/core-types/src/spec/document.ts
@@ -9,13 +9,13 @@ export interface ResourceMetaDocument {
links?: Links | PaginationLinks;
}
-export interface SingleResourceDataDocument {
+export interface SingleResourceDataDocument {
// the url or cache-key associated with the structured document
lid?: string;
links?: Links | PaginationLinks;
meta?: Meta;
data: T | null;
- included?: R[];
+ included?: T[];
}
export interface CollectionResourceDataDocument {
diff --git a/packages/debug/package.json b/packages/debug/package.json
index 9703351ec4e..c46ab76f1d5 100644
--- a/packages/debug/package.json
+++ b/packages/debug/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/debug",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Provides support for the ember-inspector for apps built with Ember and EmberData",
"keywords": [
"ember-addon"
@@ -74,7 +74,7 @@
},
"dependencies": {
"@ember/edition-utils": "^1.2.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"devDependencies": {
@@ -93,7 +93,7 @@
"@warp-drive/core-types": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
"ember-source": "~5.12.0",
- "decorator-transforms": "^2.3.0",
+ "decorator-transforms": "^2.2.2",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
"typescript": "^5.7.2",
"vite": "^5.2.11"
diff --git a/packages/diagnostic/README.md b/packages/diagnostic/README.md
index b79dd33e53b..ae7773a9fc0 100644
--- a/packages/diagnostic/README.md
+++ b/packages/diagnostic/README.md
@@ -452,7 +452,7 @@ module('My Module', function(hooks) {
1. Add the following peer-deps to your app:
```diff
-+ "@ember/test-helpers": "^3.3.0 || ^4.0.4 || ^5.1.0",
++ "@ember/test-helpers": "^3.3.0 || ^4.0.4",
+ "ember-cli-test-loader": ">= 3.1.0",
+ "@embroider/addon-shim": ">= 1.8.6"
```
diff --git a/packages/diagnostic/package.json b/packages/diagnostic/package.json
index d93db097dfe..e320f1c0b5c 100644
--- a/packages/diagnostic/package.json
+++ b/packages/diagnostic/package.json
@@ -1,6 +1,7 @@
{
"name": "@warp-drive/diagnostic",
- "version": "0.0.0-alpha.122",
+ "version": "4.12.9-alpha.3",
+ "private": true,
"description": "⚡️ A Lightweight Modern Test Runner",
"keywords": [
"test",
@@ -74,7 +75,7 @@
},
"peerDependencies": {
"ember-source": "3.28.12 || ^4.0.4 || ^5.0.0 || ^6.0.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"ember-cli-test-loader": ">= 3.1.0"
},
"peerDependenciesMeta": {
@@ -102,8 +103,8 @@
"@babel/preset-typescript": "^7.24.1",
"@babel/runtime": "^7.24.5",
"@warp-drive/internal-config": "workspace:*",
- "bun-types": "^1.2.2",
- "@ember/test-helpers": "5.1.0",
+ "bun-types": "^1.1.30",
+ "@ember/test-helpers": "4.0.4",
"ember-source": "~5.12.0",
"@glimmer/component": "^1.1.2",
"ember-cli-test-loader": "^3.1.0",
diff --git a/packages/graph/eslint.config.mjs b/packages/graph/eslint.config.mjs
index 63296bac802..c42683457ca 100644
--- a/packages/graph/eslint.config.mjs
+++ b/packages/graph/eslint.config.mjs
@@ -2,6 +2,7 @@
import { globalIgnores } from '@warp-drive/internal-config/eslint/ignore.js';
import * as node from '@warp-drive/internal-config/eslint/node.js';
import * as typescript from '@warp-drive/internal-config/eslint/typescript.js';
+import { externals } from './vite.config.mjs';
/** @type {import('eslint').Linter.FlatConfig[]} */
export default [
@@ -11,7 +12,7 @@ export default [
// browser (js/ts) ================
typescript.browser({
srcDirs: ['src'],
- allowedImports: ['@ember/debug'],
+ allowedImports: externals,
}),
// node (module) ================
diff --git a/packages/graph/package.json b/packages/graph/package.json
index 72f2e66b3cc..d67a302f0ee 100644
--- a/packages/graph/package.json
+++ b/packages/graph/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/graph",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Provides a normalized graph for managing relationships between resources",
"keywords": [
"ember-addon"
@@ -63,7 +63,7 @@
"@warp-drive/core-types": "workspace:*"
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"devDependencies": {
diff --git a/packages/graph/src/-private/-diff.ts b/packages/graph/src/-private/-diff.ts
index 12cc805eb5d..0041ab66a66 100644
--- a/packages/graph/src/-private/-diff.ts
+++ b/packages/graph/src/-private/-diff.ts
@@ -1,6 +1,6 @@
import { deprecate } from '@ember/debug';
-import { DEPRECATE_NON_UNIQUE_PAYLOADS } from '@warp-drive/build-config/deprecations';
+import { DEPRECATE_NON_UNIQUE_PAYLOADS, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -14,7 +14,6 @@ import replaceRelatedRecord from './operations/replace-related-record';
import replaceRelatedRecords from './operations/replace-related-records';
function _deprecatedCompare(
- priorLocalState: T[] | null,
newState: T[],
newMembers: Set,
prevState: T[],
@@ -31,7 +30,6 @@ function _deprecatedCompare(
const duplicates = new Map();
const finalSet = new Set();
const finalState: T[] = [];
- const priorLocalLength = priorLocalState?.length ?? 0;
for (let i = 0, j = 0; i < iterationLength; i++) {
let adv = false;
@@ -71,11 +69,6 @@ function _deprecatedCompare(
// j is always less than i and so if i < prevLength, j < prevLength
if (member !== prevState[j]) {
changed = true;
- } else if (!changed && j < priorLocalLength) {
- const priorLocalMember = priorLocalState![j];
- if (priorLocalMember !== member) {
- changed = true;
- }
}
if (!newMembers.has(prevMember)) {
@@ -107,7 +100,6 @@ function _deprecatedCompare(
}
function _compare(
- priorLocalState: T[] | null,
finalState: T[],
finalSet: Set,
prevState: T[],
@@ -122,7 +114,6 @@ function _compare(
let changed: boolean = finalSet.size !== prevSet.size;
const added = new Set();
const removed = new Set();
- const priorLocalLength = priorLocalState?.length ?? 0;
for (let i = 0; i < iterationLength; i++) {
let member: T | undefined;
@@ -144,11 +135,6 @@ function _compare(
// detect reordering
if (equalLength && member !== prevMember) {
changed = true;
- } else if (equalLength && !changed && i < priorLocalLength) {
- const priorLocalMember = priorLocalState![i];
- if (priorLocalMember !== prevMember) {
- changed = true;
- }
}
if (!finalSet.has(prevMember)) {
@@ -183,24 +169,16 @@ export function diffCollection(
onDel: (v: StableRecordIdentifier) => void
): Diff {
const finalSet = new Set(finalState);
- const { localState: priorLocalState, remoteState, remoteMembers } = relationship;
+ const { remoteState, remoteMembers } = relationship;
if (DEPRECATE_NON_UNIQUE_PAYLOADS) {
if (finalState.length !== finalSet.size) {
- const { diff, duplicates } = _deprecatedCompare(
- priorLocalState,
- finalState,
- finalSet,
- remoteState,
- remoteMembers,
- onAdd,
- onDel
- );
+ const { diff, duplicates } = _deprecatedCompare(finalState, finalSet, remoteState, remoteMembers, onAdd, onDel);
if (DEBUG) {
deprecate(
`Expected all entries in the relationship ${relationship.definition.type}:${relationship.definition.key} to be unique, see log for a list of duplicate entry indeces`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-non-unique-relationship-entries',
for: 'ember-data',
@@ -221,7 +199,7 @@ export function diffCollection(
);
}
- return _compare(priorLocalState, finalState, finalSet, remoteState, remoteMembers, onAdd, onDel);
+ return _compare(finalState, finalSet, remoteState, remoteMembers, onAdd, onDel);
}
export function computeLocalState(storage: CollectionEdge): StableRecordIdentifier[] {
@@ -298,6 +276,10 @@ export function _addLocal(
relationship.localState.push(value);
}
}
+ assert(
+ `Expected relationship to be dirty when adding a local mutation`,
+ relationship.localState || relationship.isDirty
+ );
return true;
}
diff --git a/packages/graph/src/-private/-edge-definition.ts b/packages/graph/src/-private/-edge-definition.ts
index 9c157e4d437..a6868f487db 100644
--- a/packages/graph/src/-private/-edge-definition.ts
+++ b/packages/graph/src/-private/-edge-definition.ts
@@ -1,4 +1,5 @@
import type Store from '@ember-data/store';
+import { DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -124,7 +125,6 @@ export interface UpgradedMeta {
isCollection: boolean;
isPolymorphic: boolean;
resetOnRemoteUpdate: boolean;
- isLinksMode: boolean;
inverseKind: 'implicit' | RelationshipFieldKind;
/**
@@ -141,7 +141,6 @@ export interface UpgradedMeta {
inverseIsImplicit: boolean;
inverseIsCollection: boolean;
inverseIsPolymorphic: boolean;
- inverseIsLinksMode: boolean;
}
export interface EdgeDefinition {
@@ -197,7 +196,6 @@ function syncMeta(definition: UpgradedMeta, inverseDefinition: UpgradedMeta) {
definition.inverseIsCollection = inverseDefinition.isCollection;
definition.inverseIsPolymorphic = inverseDefinition.isPolymorphic;
definition.inverseIsImplicit = inverseDefinition.isImplicit;
- definition.inverseIsLinksMode = inverseDefinition.isLinksMode;
const resetOnRemoteUpdate =
definition.resetOnRemoteUpdate === false || inverseDefinition.resetOnRemoteUpdate === false ? false : true;
definition.resetOnRemoteUpdate = resetOnRemoteUpdate;
@@ -218,20 +216,18 @@ function upgradeMeta(meta: RelationshipField): UpgradedMeta {
niceMeta.isImplicit = false;
niceMeta.isCollection = meta.kind === 'hasMany';
niceMeta.isPolymorphic = options && !!options.polymorphic;
- niceMeta.isLinksMode = options.linksMode ?? false;
niceMeta.inverseKey = (options && options.inverse) || STR_LATER;
niceMeta.inverseType = STR_LATER;
niceMeta.inverseIsAsync = BOOL_LATER;
niceMeta.inverseIsImplicit = (options && options.inverse === null) || BOOL_LATER;
niceMeta.inverseIsCollection = BOOL_LATER;
- niceMeta.inverseIsLinksMode = BOOL_LATER;
- // prettier-ignore
- niceMeta.resetOnRemoteUpdate = !isLegacyField(meta) ? false
- : meta.options?.linksMode ? false
- : meta.options?.resetOnRemoteUpdate === false ? false
- : true;
+ niceMeta.resetOnRemoteUpdate = isLegacyField(meta)
+ ? meta.options?.resetOnRemoteUpdate === false
+ ? false
+ : true
+ : false;
return niceMeta;
}
@@ -586,12 +582,27 @@ export function upgradeDefinition(
return info;
}
+type RelationshipDefinition = RelationshipField & {
+ _inverseKey: (store: Store, modelClass: unknown) => string | null;
+};
+
+function metaIsRelationshipDefinition(meta: FieldSchema): meta is RelationshipDefinition {
+ return typeof (meta as RelationshipDefinition)._inverseKey === 'function';
+}
+
function inverseForRelationship(store: Store, identifier: StableRecordIdentifier | { type: string }, key: string) {
const definition = store.schema.fields(identifier).get(key);
if (!definition) {
return null;
}
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
+ if (metaIsRelationshipDefinition(definition)) {
+ const modelClass = store.modelFor(identifier.type);
+ return definition._inverseKey(store, modelClass);
+ }
+ }
+
assert(`Expected ${key} to be a relationship`, isRelationshipField(definition));
assert(
`Expected the relationship defintion to specify the inverse type or null.`,
diff --git a/packages/graph/src/-private/-utils.ts b/packages/graph/src/-private/-utils.ts
index f0082161238..ccdf4aa740d 100644
--- a/packages/graph/src/-private/-utils.ts
+++ b/packages/graph/src/-private/-utils.ts
@@ -143,7 +143,7 @@ export function removeIdentifierCompletelyFromRelationship(
// we shouldn't be notifying here though, figure out where
// a notification was missed elsewhere.
if (!silenceNotifications) {
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
}
} else if (isHasMany(relationship)) {
@@ -164,7 +164,7 @@ export function removeIdentifierCompletelyFromRelationship(
// we shouldn't be notifying here though, figure out where
// a notification was missed elsewhere.
if (!silenceNotifications) {
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
}
}
@@ -174,14 +174,8 @@ export function removeIdentifierCompletelyFromRelationship(
}
}
-export function notifyChange(graph: Graph, relationship: CollectionEdge | ResourceEdge): void {
- if (!relationship.accessed) {
- return;
- }
-
- const identifier = relationship.identifier;
- const key = relationship.definition.key;
-
+// TODO add silencing at the graph level
+export function notifyChange(graph: Graph, identifier: StableRecordIdentifier, key: string) {
if (identifier === graph._removing) {
if (LOG_GRAPH) {
// eslint-disable-next-line no-console
diff --git a/packages/graph/src/-private/coerce-id.ts b/packages/graph/src/-private/coerce-id.ts
index 7ad7db9c748..4c44a8b22e0 100644
--- a/packages/graph/src/-private/coerce-id.ts
+++ b/packages/graph/src/-private/coerce-id.ts
@@ -1,6 +1,6 @@
import { deprecate } from '@ember/debug';
-import { DEPRECATE_NON_STRICT_ID } from '@warp-drive/build-config/deprecations';
+import { DEPRECATE_NON_STRICT_ID, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
// Used by the store to normalize IDs entering the store. Despite the fact
@@ -24,7 +24,7 @@ export function coerceId(id: Coercable): string | null {
`The resource id '<${typeof id}> ${String(
id
)} ' is not normalized. Update your application code to use '${JSON.stringify(normalized)}' instead.`,
- normalized === id,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : normalized === id,
{
id: 'ember-data:deprecate-non-strict-id',
until: '6.0',
diff --git a/packages/graph/src/-private/debug/assert-polymorphic-type.ts b/packages/graph/src/-private/debug/assert-polymorphic-type.ts
index 597af2e72eb..ee164ad6bb8 100644
--- a/packages/graph/src/-private/debug/assert-polymorphic-type.ts
+++ b/packages/graph/src/-private/debug/assert-polymorphic-type.ts
@@ -1,11 +1,34 @@
/* eslint-disable @typescript-eslint/no-shadow */
-import type { CacheCapabilitiesManager } from '@ember-data/store/types';
+import type Mixin from '@ember/object/mixin';
+
+import type Store from '@ember-data/store';
+import type { CacheCapabilitiesManager, ModelSchema } from '@ember-data/store/types';
+import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
import { isLegacyField, isRelationshipField, temporaryConvertToLegacy, type UpgradedMeta } from '../-edge-definition';
+type Model = ModelSchema;
+
+// A pile of soft-lies to deal with mixin APIs
+type ModelWithMixinApis = Model & {
+ isModel?: boolean;
+ __isMixin?: boolean;
+ __mixin: Mixin;
+ PrototypeMixin: Mixin;
+ detect: (mixin: Model | Mixin | ModelWithMixinApis) => boolean;
+ prototype: Model;
+ [Symbol.hasInstance](model: Model): true;
+};
+
+function assertModelSchemaIsModel(
+ schema: ModelSchema | Model | ModelWithMixinApis
+): asserts schema is ModelWithMixinApis {
+ assert(`Expected Schema to be an instance of Model`, 'isModel' in schema && schema.isModel === true);
+}
+
/*
Assert that `addedRecord` has a valid type so it can be added to the
relationship of the `record`.
@@ -27,6 +50,21 @@ let assertPolymorphicType: (
let assertInheritedSchema: (definition: UpgradedMeta, type: string) => void;
if (DEBUG) {
+ const checkPolymorphic = function checkPolymorphic(modelClass: ModelSchema, addedModelClass: ModelSchema) {
+ assertModelSchemaIsModel(modelClass);
+ assertModelSchemaIsModel(addedModelClass);
+
+ if (modelClass.__isMixin) {
+ return (
+ modelClass.__mixin.detect(addedModelClass.PrototypeMixin) ||
+ // handle native class extension e.g. `class Post extends Model.extend(Commentable) {}`
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ modelClass.__mixin.detect(Object.getPrototypeOf(addedModelClass).PrototypeMixin)
+ );
+ }
+ return addedModelClass.prototype instanceof modelClass || modelClass.detect(addedModelClass);
+ };
+
function validateSchema(definition: UpgradedMeta, meta: PrintConfig) {
const errors = new Map();
@@ -61,9 +99,9 @@ if (DEBUG) {
kind: string;
options: {
as?: string;
- async: boolean;
+ async?: boolean;
polymorphic?: boolean;
- inverse: string | null;
+ inverse?: string | null;
};
};
type RelationshipSchemaError = 'name' | 'type' | 'kind' | 'as' | 'async' | 'polymorphic' | 'inverse';
@@ -136,7 +174,6 @@ if (DEBUG) {
kind: definition.inverseKind,
isAsync: definition.inverseIsAsync,
isPolymorphic: true,
- isLinksMode: definition.isLinksMode,
isCollection: definition.inverseIsCollection,
isImplicit: definition.inverseIsImplicit,
inverseKey: definition.key,
@@ -144,7 +181,6 @@ if (DEBUG) {
inverseKind: definition.kind,
inverseIsAsync: definition.isAsync,
inverseIsPolymorphic: definition.isPolymorphic,
- inverseIsLinksMode: definition.inverseIsLinksMode,
inverseIsImplicit: definition.isImplicit,
inverseIsCollection: definition.isCollection,
resetOnRemoteUpdate: definition.resetOnRemoteUpdate,
@@ -213,69 +249,143 @@ if (DEBUG) {
if (parentDefinition.inverseIsImplicit) {
return;
}
+ let asserted = false;
+
if (parentDefinition.isPolymorphic) {
- let meta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey);
- assert(
- `No '${parentDefinition.inverseKey}' field exists on '${
- addedIdentifier.type
- }'. To use this type in the polymorphic relationship '${parentDefinition.inverseType}.${
- parentDefinition.key
- }' the relationships schema definition for ${addedIdentifier.type} should include:${expectedSchema(
- parentDefinition
- )}`,
- meta
- );
- assert(
- `Expected the field ${parentDefinition.inverseKey} to be a relationship`,
- meta && isRelationshipField(meta)
- );
- meta = isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta);
+ const rawMeta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey);
assert(
- `You should not specify both options.as and options.inverse as null on ${addedIdentifier.type}.${parentDefinition.inverseKey}, as if there is no inverse field there is no abstract type to conform to. You may have intended for this relationship to be polymorphic, or you may have mistakenly set inverse to null.`,
- !(meta.options.inverse === null && meta?.options.as?.length)
- );
- const errors = validateSchema(parentDefinition, meta);
- assert(
- `The schema for the relationship '${parentDefinition.inverseKey}' on '${
- addedIdentifier.type
- }' type does not correctly implement '${parentDefinition.type}' and thus cannot be assigned to the '${
- parentDefinition.key
- }' relationship in '${
- parentIdentifier.type
- }'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below:${printSchema(
- meta,
- errors
- )}`,
- errors.size === 0
+ `Expected to find a relationship field schema for ${parentDefinition.inverseKey} on ${addedIdentifier.type} but none was found`,
+ !rawMeta || isRelationshipField(rawMeta)
);
+ const meta = rawMeta && (isLegacyField(rawMeta) ? rawMeta : temporaryConvertToLegacy(rawMeta));
+
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ if (meta?.options?.as) {
+ asserted = true;
+ assert(
+ `No '${parentDefinition.inverseKey}' field exists on '${addedIdentifier.type}'. To use this type in the polymorphic relationship '${parentDefinition.inverseType}.${parentDefinition.key}' the relationships schema definition for ${addedIdentifier.type} should include:${expectedSchema(parentDefinition)}`,
+ meta
+ );
+ assert(
+ `You should not specify both options.as and options.inverse as null on ${addedIdentifier.type}.${parentDefinition.inverseKey}, as if there is no inverse field there is no abstract type to conform to. You may have intended for this relationship to be polymorphic, or you may have mistakenly set inverse to null.`,
+ !(meta.options.inverse === null && meta?.options.as?.length > 0)
+ );
+ const errors = validateSchema(parentDefinition, meta);
+ assert(
+ `The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not correctly implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below:${printSchema(meta, errors)}`,
+ errors.size === 0
+ );
+ }
+ } else {
+ assert(
+ `No '${parentDefinition.inverseKey}' field exists on '${
+ addedIdentifier.type
+ }'. To use this type in the polymorphic relationship '${parentDefinition.inverseType}.${
+ parentDefinition.key
+ }' the relationships schema definition for ${addedIdentifier.type} should include:${expectedSchema(
+ parentDefinition
+ )}`,
+ meta
+ );
+ assert(
+ `Expected the field ${parentDefinition.inverseKey} to be a relationship`,
+ meta && isRelationshipField(meta)
+ );
+ assert(
+ `You should not specify both options.as and options.inverse as null on ${addedIdentifier.type}.${parentDefinition.inverseKey}, as if there is no inverse field there is no abstract type to conform to. You may have intended for this relationship to be polymorphic, or you may have mistakenly set inverse to null.`,
+ !(meta.options.inverse === null && meta?.options.as?.length)
+ );
+ const errors = validateSchema(parentDefinition, meta);
+ assert(
+ `The schema for the relationship '${parentDefinition.inverseKey}' on '${
+ addedIdentifier.type
+ }' type does not correctly implement '${parentDefinition.type}' and thus cannot be assigned to the '${
+ parentDefinition.key
+ }' relationship in '${
+ parentIdentifier.type
+ }'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below:${printSchema(
+ meta,
+ errors
+ )}`,
+ errors.size === 0
+ );
+ }
} else if (addedIdentifier.type !== parentDefinition.type) {
// if we are not polymorphic
// then the addedIdentifier.type must be the same as the parentDefinition.type
- let meta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey);
+ const rawMeta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey);
assert(
- `Expected the field ${parentDefinition.inverseKey} to be a relationship`,
- !meta || isRelationshipField(meta)
+ `Expected to find a relationship field schema for ${parentDefinition.inverseKey} on ${addedIdentifier.type} but none was found`,
+ !rawMeta || isRelationshipField(rawMeta)
);
- meta = meta && (isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta));
- if (meta?.options.as === parentDefinition.type) {
- // inverse is likely polymorphic but missing the polymorphic flag
- let meta = store.schema.fields({ type: parentDefinition.inverseType }).get(parentDefinition.key);
- assert(`Expected the field ${parentDefinition.key} to be a relationship`, meta && isRelationshipField(meta));
- meta = isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta);
- const errors = validateSchema(definitionWithPolymorphic(inverseDefinition(parentDefinition)), meta);
- assert(
- `The '<${addedIdentifier.type}>.${
- parentDefinition.inverseKey
- }' relationship cannot be used polymorphically because '<${parentDefinition.inverseType}>.${
- parentDefinition.key
- } is not a polymorphic relationship. To use this relationship in a polymorphic manner, fix the following schema issues on the relationships schema for '${
- parentDefinition.inverseType
- }':${printSchema(meta, errors)}`
- );
- } else {
+ const meta = rawMeta && (isLegacyField(rawMeta) ? rawMeta : temporaryConvertToLegacy(rawMeta));
+
+ if (!DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ if (meta?.options.as === parentDefinition.type) {
+ // inverse is likely polymorphic but missing the polymorphic flag
+ const inverseMeta = store.schema.fields({ type: parentDefinition.inverseType }).get(parentDefinition.key);
+ assert(
+ `Expected to find a relationship field schema for ${parentDefinition.inverseKey} on ${addedIdentifier.type} but none was found`,
+ inverseMeta && isRelationshipField(inverseMeta)
+ );
+ const legacyInverseMeta =
+ inverseMeta && (isLegacyField(inverseMeta) ? inverseMeta : temporaryConvertToLegacy(inverseMeta));
+ const errors = validateSchema(
+ definitionWithPolymorphic(inverseDefinition(parentDefinition)),
+ legacyInverseMeta
+ );
+ assert(
+ `The '<${addedIdentifier.type}>.${parentDefinition.inverseKey}' relationship cannot be used polymorphically because '<${parentDefinition.inverseType}>.${parentDefinition.key} is not a polymorphic relationship. To use this relationship in a polymorphic manner, fix the following schema issues on the relationships schema for '${parentDefinition.inverseType}':${printSchema(legacyInverseMeta, errors)}`
+ );
+ } else {
+ assert(
+ `The '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If this relationship should be polymorphic, mark ${parentDefinition.inverseType}.${parentDefinition.key} as \`polymorphic: true\` and ${addedIdentifier.type}.${parentDefinition.inverseKey} as implementing it via \`as: '${parentDefinition.type}'\`.`
+ );
+ }
+ } else if ((meta?.options?.as?.length ?? 0) > 0) {
+ asserted = true;
assert(
- `The '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If this relationship should be polymorphic, mark ${parentDefinition.inverseType}.${parentDefinition.key} as \`polymorphic: true\` and ${addedIdentifier.type}.${parentDefinition.inverseKey} as implementing it via \`as: '${parentDefinition.type}'\`.`
+ `Expected the field ${parentDefinition.inverseKey} to be a relationship`,
+ !meta || isRelationshipField(meta)
);
+ const legacyMeta = meta && (isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta));
+ if (legacyMeta?.options.as === parentDefinition.type) {
+ // inverse is likely polymorphic but missing the polymorphic flag
+ let meta = store.schema.fields({ type: parentDefinition.inverseType }).get(parentDefinition.key);
+ assert(`Expected the field ${parentDefinition.key} to be a relationship`, meta && isRelationshipField(meta));
+ meta = isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta);
+ const errors = validateSchema(definitionWithPolymorphic(inverseDefinition(parentDefinition)), meta);
+ assert(
+ `The '<${addedIdentifier.type}>.${
+ parentDefinition.inverseKey
+ }' relationship cannot be used polymorphically because '<${parentDefinition.inverseType}>.${
+ parentDefinition.key
+ } is not a polymorphic relationship. To use this relationship in a polymorphic manner, fix the following schema issues on the relationships schema for '${
+ parentDefinition.inverseType
+ }':${printSchema(meta, errors)}`
+ );
+ } else {
+ assert(
+ `The '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If this relationship should be polymorphic, mark ${parentDefinition.inverseType}.${parentDefinition.key} as \`polymorphic: true\` and ${addedIdentifier.type}.${parentDefinition.inverseKey} as implementing it via \`as: '${parentDefinition.type}'\`.`
+ );
+ }
+ }
+ }
+
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ if (!asserted) {
+ const storeService = (store as unknown as { _store: Store })._store;
+ const addedModelName = addedIdentifier.type;
+ const parentModelName = parentIdentifier.type;
+ const key = parentDefinition.key;
+ const relationshipModelName = parentDefinition.type;
+ const relationshipClass = storeService.modelFor(relationshipModelName);
+ const addedClass = storeService.modelFor(addedModelName);
+
+ const assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
+ const isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
+
+ assert(assertionMessage, isPolymorphic);
}
}
};
diff --git a/packages/graph/src/-private/edges/collection.ts b/packages/graph/src/-private/edges/collection.ts
index a61e678aa04..ed61a0c6a7b 100644
--- a/packages/graph/src/-private/edges/collection.ts
+++ b/packages/graph/src/-private/edges/collection.ts
@@ -22,24 +22,8 @@ export interface CollectionEdge {
links: Links | PaginationLinks | null;
localState: StableRecordIdentifier[] | null;
- /**
- * Whether the localState for this edge is out-of-sync
- * with the remoteState.
- *
- * if state.hasReceivedData=false we are also
- * not dirty since there is nothing to sync with.
- *
- * @typedoc
- */
isDirty: boolean;
transactionRef: number;
- /**
- * Whether data for this edge has been accessed at least once
- * via `graph.getData`
- *
- * @typedoc
- */
- accessed: boolean;
_diff?: {
add: Set;
@@ -61,15 +45,13 @@ export function createCollectionEdge(definition: UpgradedMeta, identifier: Stabl
links: null,
localState: null,
- isDirty: false,
+ isDirty: true,
transactionRef: 0,
- accessed: false,
_diff: undefined,
};
}
export function legacyGetCollectionRelationshipData(source: CollectionEdge): CollectionRelationship {
- source.accessed = true;
const payload: CollectionRelationship = {};
if (source.state.hasReceivedData) {
diff --git a/packages/graph/src/-private/edges/resource.ts b/packages/graph/src/-private/edges/resource.ts
index 5776734009c..3d5c615335a 100644
--- a/packages/graph/src/-private/edges/resource.ts
+++ b/packages/graph/src/-private/edges/resource.ts
@@ -23,7 +23,6 @@ export interface ResourceEdge {
meta: Meta | null;
links: Links | PaginationLinks | null;
transactionRef: number;
- accessed: boolean;
}
export function createResourceEdge(definition: UpgradedMeta, identifier: StableRecordIdentifier): ResourceEdge {
@@ -36,12 +35,10 @@ export function createResourceEdge(definition: UpgradedMeta, identifier: StableR
remoteState: null,
meta: null,
links: null,
- accessed: false,
};
}
export function legacyGetResourceRelationshipData(source: ResourceEdge): ResourceRelationship {
- source.accessed = true;
let data: StableRecordIdentifier | null | undefined;
const payload: ResourceRelationship = {};
if (source.localState) {
diff --git a/packages/graph/src/-private/graph.ts b/packages/graph/src/-private/graph.ts
index 18211af7fa2..b2a5b5b87f2 100644
--- a/packages/graph/src/-private/graph.ts
+++ b/packages/graph/src/-private/graph.ts
@@ -559,7 +559,7 @@ export class Graph {
this._willSyncLocal = false;
const updated = this._updatedRelationships;
this._updatedRelationships = new Set();
- updated.forEach((rel) => notifyChange(this, rel));
+ updated.forEach((rel) => notifyChange(this, rel.identifier, rel.definition.key));
}
destroy() {
@@ -641,7 +641,7 @@ function destroyRelationship(graph: Graph, rel: GraphEdge, silenceNotifications?
// leave the ui relationship populated since the record is destroyed and
// internally we've fully cleaned up.
if (!rel.definition.isAsync && !silenceNotifications) {
- /*#__NOINLINE__*/ notifyChange(graph, rel);
+ /*#__NOINLINE__*/ notifyChange(graph, rel.identifier, rel.definition.key);
}
}
}
@@ -713,7 +713,7 @@ function removeDematerializedInverse(
}
if (!silenceNotifications) {
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
} else {
if (!relationship.definition.isAsync || (inverseIdentifier && isNew(inverseIdentifier))) {
@@ -728,7 +728,7 @@ function removeDematerializedInverse(
}
if (!silenceNotifications) {
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
}
}
@@ -753,7 +753,7 @@ function removeCompletelyFromInverse(graph: Graph, relationship: GraphEdge) {
if (!relationship.definition.isAsync) {
clearRelationship(relationship);
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
} else {
relationship.remoteMembers.clear();
diff --git a/packages/graph/src/-private/operations/add-to-related-records.ts b/packages/graph/src/-private/operations/add-to-related-records.ts
index 9757e040964..2c08cb4981b 100644
--- a/packages/graph/src/-private/operations/add-to-related-records.ts
+++ b/packages/graph/src/-private/operations/add-to-related-records.ts
@@ -19,13 +19,6 @@ export default function addToRelatedRecords(graph: Graph, op: AddToRelatedRecord
`You can only '${op.op}' on a hasMany relationship. ${record.type}.${op.field} is a ${relationship.definition.kind}`,
isHasMany(relationship)
);
-
- // if we are not dirty but have a null localState then we
- // are mutating a relationship that has never been fetched
- // so we initialize localState to an empty array
- if (!relationship.isDirty && !relationship.localState) {
- relationship.localState = [];
- }
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
addRelatedRecord(graph, relationship, record, value[i], index !== undefined ? index + i : index, isRemote);
@@ -34,7 +27,7 @@ export default function addToRelatedRecords(graph: Graph, op: AddToRelatedRecord
addRelatedRecord(graph, relationship, record, value, index, isRemote);
}
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
function addRelatedRecord(
diff --git a/packages/graph/src/-private/operations/merge-identifier.ts b/packages/graph/src/-private/operations/merge-identifier.ts
index 91459c1f8e3..8c95f40179b 100644
--- a/packages/graph/src/-private/operations/merge-identifier.ts
+++ b/packages/graph/src/-private/operations/merge-identifier.ts
@@ -40,7 +40,7 @@ function mergeBelongsTo(graph: Graph, rel: ResourceEdge, op: MergeOperation): vo
}
if (rel.localState === op.record) {
rel.localState = op.value;
- notifyChange(graph, rel);
+ notifyChange(graph, rel.identifier, rel.definition.key);
}
}
@@ -63,7 +63,7 @@ function mergeHasMany(graph: Graph, rel: CollectionEdge, op: MergeOperation): vo
rel.isDirty = true;
}
if (rel.isDirty) {
- notifyChange(graph, rel);
+ notifyChange(graph, rel.identifier, rel.definition.key);
}
}
diff --git a/packages/graph/src/-private/operations/remove-from-related-records.ts b/packages/graph/src/-private/operations/remove-from-related-records.ts
index 1d0f02543da..63f198d136f 100644
--- a/packages/graph/src/-private/operations/remove-from-related-records.ts
+++ b/packages/graph/src/-private/operations/remove-from-related-records.ts
@@ -30,7 +30,7 @@ export default function removeFromRelatedRecords(graph: Graph, op: RemoveFromRel
} else {
removeRelatedRecord(graph, relationship, record, value, isRemote);
}
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
function removeRelatedRecord(
diff --git a/packages/graph/src/-private/operations/replace-related-record.ts b/packages/graph/src/-private/operations/replace-related-record.ts
index 6acd0977d00..ee8833f2cbb 100644
--- a/packages/graph/src/-private/operations/replace-related-record.ts
+++ b/packages/graph/src/-private/operations/replace-related-record.ts
@@ -1,6 +1,9 @@
import { deprecate } from '@ember/debug';
-import { DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE } from '@warp-drive/build-config/deprecations';
+import {
+ DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE,
+ DISABLE_6X_DEPRECATIONS,
+} from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -97,7 +100,7 @@ export default function replaceRelatedRecord(graph: Graph, op: ReplaceRelatedRec
} belongsTo relationship but will not be once this deprecation is resolved:\n\n\t${
localState ? 'Added: ' + localState.lid + '\n\t' : ''
}${existingState ? 'Removed: ' + existingState.lid : ''}`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-relationship-remote-update-clearing-local-state',
for: 'ember-data',
@@ -107,7 +110,7 @@ export default function replaceRelatedRecord(graph: Graph, op: ReplaceRelatedRec
}
);
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
}
}
@@ -156,7 +159,7 @@ export default function replaceRelatedRecord(graph: Graph, op: ReplaceRelatedRec
// and we can safely sync the new remoteState to local
if (localState !== remoteState && localState === existingState) {
relationship.localState = remoteState;
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
// But when localState does not match the new remoteState and
// and localState !== existingState then we know we have a local mutation
// that has not been persisted yet.
@@ -176,7 +179,7 @@ export default function replaceRelatedRecord(graph: Graph, op: ReplaceRelatedRec
} belongsTo relationship but will not be once this deprecation is resolved:\n\n\t${
localState ? 'Added: ' + localState.lid + '\n\t' : ''
}${existingState ? 'Removed: ' + existingState.lid : ''}`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-relationship-remote-update-clearing-local-state',
for: 'ember-data',
@@ -186,10 +189,10 @@ export default function replaceRelatedRecord(graph: Graph, op: ReplaceRelatedRec
}
);
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
}
} else {
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
}
}
diff --git a/packages/graph/src/-private/operations/replace-related-records.ts b/packages/graph/src/-private/operations/replace-related-records.ts
index 5f89d9015e2..6cc3c58c6a7 100644
--- a/packages/graph/src/-private/operations/replace-related-records.ts
+++ b/packages/graph/src/-private/operations/replace-related-records.ts
@@ -1,6 +1,9 @@
import { deprecate } from '@ember/debug';
-import { DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE } from '@warp-drive/build-config/deprecations';
+import {
+ DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE,
+ DISABLE_6X_DEPRECATIONS,
+} from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -80,12 +83,18 @@ function replaceRelatedRecordsLocal(graph: Graph, op: ReplaceRelatedRecordsOpera
const relationship = graph.get(op.record, op.field);
assert(`expected hasMany relationship`, isHasMany(relationship));
+ // relationships for newly created records begin in the dirty state, so if updated
+ // before flushed we would fail to notify. This check helps us avoid that.
+ const isMaybeFirstUpdate =
+ relationship.remoteState.length === 0 &&
+ relationship.localState === null &&
+ relationship.state.hasReceivedData === false;
relationship.state.hasReceivedData = true;
const { additions, removals } = relationship;
const { inverseKey, type } = relationship.definition;
const { record } = op;
const wasDirty = relationship.isDirty;
- let localBecameDirty = false;
+ relationship.isDirty = false;
const onAdd = (identifier: StableRecordIdentifier) => {
// Since we are diffing against the remote state, we check
@@ -99,8 +108,7 @@ function replaceRelatedRecordsLocal(graph: Graph, op: ReplaceRelatedRecordsOpera
graph.registerPolymorphicType(type, identifier.type);
}
- // we've added a record locally that wasn't in the local state before
- localBecameDirty = true;
+ relationship.isDirty = true;
addToInverse(graph, identifier, inverseKey, op.record, isRemote);
if (removalsHas) {
@@ -114,8 +122,7 @@ function replaceRelatedRecordsLocal(graph: Graph, op: ReplaceRelatedRecordsOpera
// if our previous local state had contained this identifier
const additionsHas = additions?.has(identifier);
if (additionsHas || !removals?.has(identifier)) {
- // we've removed a record locally that was in the local state before
- localBecameDirty = true;
+ relationship.isDirty = true;
removeFromInverse(graph, identifier, inverseKey, record, isRemote);
if (additionsHas) {
@@ -125,38 +132,41 @@ function replaceRelatedRecordsLocal(graph: Graph, op: ReplaceRelatedRecordsOpera
};
const diff = diffCollection(identifiers, relationship, onAdd, onRemove);
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ let becameDirty = relationship.isDirty || diff.changed;
// any additions no longer in the local state
- // also need to be removed from the inverse
+ // need to be removed from the inverse
if (additions && additions.size > 0) {
additions.forEach((identifier) => {
if (!diff.add.has(identifier)) {
- localBecameDirty = true;
+ becameDirty = true;
onRemove(identifier);
}
});
}
// any removals no longer in the local state
- // also need to be added back to the inverse
+ // need to be added back to the inverse
if (removals && removals.size > 0) {
removals.forEach((identifier) => {
if (!diff.del.has(identifier)) {
- localBecameDirty = true;
+ becameDirty = true;
onAdd(identifier);
}
});
}
- const becameDirty = diff.changed || localBecameDirty;
relationship.additions = diff.add;
relationship.removals = diff.del;
relationship.localState = diff.finalState;
+ relationship.isDirty = wasDirty;
- // we only notify if the localState changed and were not already dirty before
- // because if we were already dirty then we have already notified
- if (becameDirty && !wasDirty) {
- notifyChange(graph, relationship);
+ if (
+ isMaybeFirstUpdate ||
+ !wasDirty /*&& becameDirty // TODO to guard like this we need to detect reorder when diffing local */
+ ) {
+ notifyChange(graph, op.record, op.field);
}
}
@@ -171,15 +181,6 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
if (isRemote) {
graph._addToTransaction(relationship);
}
-
- const wasDirty = relationship.isDirty;
- // if this is our first time receiving data
- // we need to mark the relationship as dirty
- // so that non-materializing APIs like `hasManyReference.value()`
- // will get notified and updated.
- if (!relationship.state.hasReceivedData) {
- relationship.isDirty = true;
- }
relationship.state.hasReceivedData = true;
// cache existing state
@@ -237,13 +238,7 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
if (DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE) {
// only do this for legacy hasMany, not collection
// and provide a way to incrementally migrate
- if (
- // we do not guard by diff.changed here
- // because we want to clear local changes even if
- // no change has occurred to preserve the legacy behavior
- relationship.definition.kind === 'hasMany' &&
- relationship.definition.resetOnRemoteUpdate !== false
- ) {
+ if (relationship.definition.kind === 'hasMany' && relationship.definition.resetOnRemoteUpdate !== false) {
const deprecationInfo: {
removals: StableRecordIdentifier[];
additions: StableRecordIdentifier[];
@@ -262,7 +257,7 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
// if we are still in removals at this point then
// we were not "committed" which means we are present
// in the remoteMembers. So we "add back" on the inverse.
- addToInverse(graph, identifier, definition.inverseKey, op.record, false);
+ addToInverse(graph, identifier, definition.inverseKey, op.record, isRemote);
});
relationship.removals = null;
}
@@ -278,7 +273,7 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
deprecationInfo.additions.push(identifier);
relationship.isDirty = true;
relationship.additions!.delete(identifier);
- removeFromInverse(graph, identifier, definition.inverseKey, op.record, false);
+ removeFromInverse(graph, identifier, definition.inverseKey, op.record, isRemote);
}
});
if (relationship.additions.size === 0) {
@@ -295,7 +290,7 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
} hasMany relationship but will not be once this deprecation is resolved by opting into the new behavior:\n\n\tAdded: [${deprecationInfo.additions
.map((i) => i.lid)
.join(', ')}]\n\tRemoved: [${deprecationInfo.removals.map((i) => i.lid).join(', ')}]`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-relationship-remote-update-clearing-local-state',
for: 'ember-data',
@@ -308,7 +303,7 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
}
}
- if (relationship.isDirty && !wasDirty) {
+ if (relationship.isDirty) {
flushCanonical(graph, relationship);
}
}
@@ -347,8 +342,7 @@ export function addToInverse(
removeFromInverse(graph, relationship.localState, relationship.definition.inverseKey, identifier, isRemote);
}
relationship.localState = value;
-
- notifyChange(graph, relationship);
+ notifyChange(graph, identifier, key);
}
} else if (isHasMany(relationship)) {
if (isRemote) {
@@ -370,15 +364,8 @@ export function addToInverse(
}
}
} else {
- // if we are not dirty but have a null localState then we
- // are mutating a relationship that has never been fetched
- // so we initialize localState to an empty array
- if (!relationship.isDirty && !relationship.localState) {
- relationship.localState = [];
- }
-
if (_addLocal(graph, identifier, relationship, value, null)) {
- notifyChange(graph, relationship);
+ notifyChange(graph, identifier, key);
}
}
} else {
@@ -404,7 +391,7 @@ export function notifyInverseOfPotentialMaterialization(
) {
const relationship = graph.get(identifier, key);
if (isHasMany(relationship) && isRemote && relationship.remoteMembers.has(value)) {
- notifyChange(graph, relationship);
+ notifyChange(graph, identifier, key);
}
}
@@ -426,17 +413,17 @@ export function removeFromInverse(
if (relationship.localState === value) {
relationship.localState = null;
- notifyChange(graph, relationship);
+ notifyChange(graph, identifier, key);
}
} else if (isHasMany(relationship)) {
if (isRemote) {
graph._addToTransaction(relationship);
if (_removeRemote(relationship, value)) {
- notifyChange(graph, relationship);
+ notifyChange(graph, identifier, key);
}
} else {
if (_removeLocal(relationship, value)) {
- notifyChange(graph, relationship);
+ notifyChange(graph, identifier, key);
}
}
} else {
@@ -452,7 +439,5 @@ export function removeFromInverse(
}
function flushCanonical(graph: Graph, rel: CollectionEdge) {
- if (rel.accessed) {
- graph._scheduleLocalSync(rel);
- }
+ graph._scheduleLocalSync(rel);
}
diff --git a/packages/graph/src/-private/operations/update-relationship.ts b/packages/graph/src/-private/operations/update-relationship.ts
index 6f8c7bdbc16..96fd82f7887 100644
--- a/packages/graph/src/-private/operations/update-relationship.ts
+++ b/packages/graph/src/-private/operations/update-relationship.ts
@@ -157,7 +157,7 @@ export default function updateRelationshipOperation(graph: Graph, op: UpdateRela
) {
relationship.state.isStale = true;
- notifyChange(graph, relationship);
+ notifyChange(graph, relationship.identifier, relationship.definition.key);
} else {
relationship.state.isStale = false;
}
diff --git a/packages/graph/vite.config.mjs b/packages/graph/vite.config.mjs
index 7936563a474..a548ae349de 100644
--- a/packages/graph/vite.config.mjs
+++ b/packages/graph/vite.config.mjs
@@ -1,6 +1,7 @@
import { createConfig } from '@warp-drive/internal-config/vite/config.js';
export const externals = [
+ '@ember/object/mixin', // type only
'@ember/debug', // assert, deprecate
];
diff --git a/packages/holodeck/README.md b/packages/holodeck/README.md
index 0f736954864..4a8263a486a 100644
--- a/packages/holodeck/README.md
+++ b/packages/holodeck/README.md
@@ -107,8 +107,8 @@ import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
import { MockServerHandler } from '@warp-drive/holodeck';
-const manager = new RequestManager()
- .use([new MockServerHandler(testContext), Fetch]);
+const manager = new RequestManager();
+manager.use([new MockServerHandler(testContext), Fetch]);
```
From within a test this might look like:
@@ -121,8 +121,8 @@ import { module, test } from 'qunit';
module('my module', function() {
test('my test', async function() {
- const manager = new RequestManager()
- .use([new MockServerHandler(this), Fetch]);
+ const manager = new RequestManager();
+ manager.use([new MockServerHandler(this), Fetch]);
});
});
```
diff --git a/packages/holodeck/package.json b/packages/holodeck/package.json
index 2d97785a827..f05a0039a90 100644
--- a/packages/holodeck/package.json
+++ b/packages/holodeck/package.json
@@ -1,7 +1,8 @@
{
"name": "@warp-drive/holodeck",
"description": "⚡️ Simple, Fast HTTP Mocking for Tests",
- "version": "0.0.0-alpha.122",
+ "version": "4.12.9-alpha.3",
+ "private": true,
"license": "MIT",
"author": "Chris Thoburn ",
"repository": {
@@ -23,7 +24,7 @@
"dependencies": {
"@hono/node-server": "^1.11.1",
"chalk": "^5.3.0",
- "hono": "^4.7.0"
+ "hono": "^4.6.5"
},
"type": "module",
"files": [
diff --git a/packages/json-api/package.json b/packages/json-api/package.json
index 6a093e3682c..e536ae89016 100644
--- a/packages/json-api/package.json
+++ b/packages/json-api/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/json-api",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Provides a JSON:API document and resource cache implementation for EmberData",
"keywords": [
"ember-addon"
@@ -72,7 +72,7 @@
"@warp-drive/core-types": "workspace:*"
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"devDependencies": {
diff --git a/packages/json-api/src/-private/cache.ts b/packages/json-api/src/-private/cache.ts
index ccefec1b8e2..4f6cbeb4fea 100644
--- a/packages/json-api/src/-private/cache.ts
+++ b/packages/json-api/src/-private/cache.ts
@@ -4,7 +4,6 @@
import type { CollectionEdge, Graph, GraphEdge, ImplicitEdge, ResourceEdge } from '@ember-data/graph/-private';
import { graphFor, isBelongsTo, peekGraph } from '@ember-data/graph/-private';
import type Store from '@ember-data/store';
-import { logGroup } from '@ember-data/store/-private';
import type { CacheCapabilitiesManager } from '@ember-data/store/types';
import { LOG_MUTATIONS, LOG_OPERATIONS, LOG_REQUESTS } from '@warp-drive/build-config/debugging';
import { DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE } from '@warp-drive/build-config/deprecations';
@@ -50,8 +49,6 @@ import type {
SingleResourceRelationship,
} from '@warp-drive/core-types/spec/json-api-raw';
-import { validateDocumentFields } from './validate-document-fields';
-
type IdentifierCache = Store['identifierCache'];
type InternalCapabilitiesManager = CacheCapabilitiesManager & { _store: Store };
@@ -216,10 +213,6 @@ export default class JSONAPICache implements Cache {
let i: number, length: number;
const { identifierCache } = this._capabilities;
- if (DEBUG) {
- validateDocumentFields(this._capabilities.schema, jsonApiDoc);
- }
-
if (LOG_REQUESTS) {
const Counts = new Map();
if (included) {
@@ -277,6 +270,11 @@ export default class JSONAPICache implements Cache {
);
}
+ assert(
+ `Expected a resource object in the 'data' property in the document provided to the cache, but was ${typeof jsonApiDoc.data}`,
+ typeof jsonApiDoc.data === 'object'
+ );
+
const identifier = putOne(this, identifierCache, jsonApiDoc.data);
return this._putDocument(
doc as StructuredDataDocument,
@@ -335,7 +333,7 @@ export default class JSONAPICache implements Cache {
const hasExisting = this.__documents.has(identifier.lid);
this.__documents.set(identifier.lid, doc as StructuredDocument);
- this._capabilities.notifyChange(identifier, hasExisting ? 'updated' : 'added', null);
+ this._capabilities.notifyChange(identifier, hasExisting ? 'updated' : 'added');
}
return resourceDocument;
@@ -384,22 +382,16 @@ export default class JSONAPICache implements Cache {
*/
mutate(mutation: LocalRelationshipOperation): void {
if (LOG_MUTATIONS) {
- logGroup('cache', 'mutate', mutation.record.type, mutation.record.lid, mutation.field, mutation.op);
try {
const _data = JSON.parse(JSON.stringify(mutation)) as object;
// eslint-disable-next-line no-console
- console.log(_data);
+ console.log(`EmberData | Mutation - update ${mutation.op}`, _data);
} catch {
// eslint-disable-next-line no-console
- console.log(mutation);
+ console.log(`EmberData | Mutation - update ${mutation.op}`, mutation);
}
}
this.__graph.update(mutation, false);
-
- if (LOG_MUTATIONS) {
- // eslint-disable-next-line no-console
- console.groupEnd();
- }
}
/**
@@ -523,7 +515,7 @@ export default class JSONAPICache implements Cache {
data: ExistingResourceObject,
calculateChanges?: boolean
): void | string[] {
- let changedKeys: Set | undefined;
+ let changedKeys: string[] | undefined;
const peeked = this.__safePeek(identifier, false);
const existed = !!peeked;
const cached = peeked || this._createCache(identifier);
@@ -532,50 +524,38 @@ export default class JSONAPICache implements Cache {
const isUpdate = /*#__NOINLINE__*/ !_isEmpty(peeked) && !isLoading;
if (LOG_OPERATIONS) {
- logGroup(
- 'cache',
- 'upsert',
- identifier.type,
- identifier.lid,
- existed ? 'merged' : 'inserted',
- calculateChanges ? 'has-subscription' : ''
- );
try {
const _data = JSON.parse(JSON.stringify(data)) as object;
-
// eslint-disable-next-line no-console
- console.log(_data);
+ console.log(`EmberData | Operation - upsert (${existed ? 'merge' : 'insert'})`, _data);
} catch {
// eslint-disable-next-line no-console
- console.log(data);
+ console.log(`EmberData | Operation - upsert (${existed ? 'merge' : 'insert'})`, data);
}
}
if (cached.isNew) {
cached.isNew = false;
- this._capabilities.notifyChange(identifier, 'identity', null);
- this._capabilities.notifyChange(identifier, 'state', null);
+ this._capabilities.notifyChange(identifier, 'identity');
+ this._capabilities.notifyChange(identifier, 'state');
}
- // if no cache entry existed, no record exists / property has been accessed
- // and thus we do not need to notify changes to any properties.
- if (calculateChanges && existed && data.attributes) {
- changedKeys = calculateChangedKeys(cached, data.attributes);
+ if (calculateChanges) {
+ changedKeys = existed ? calculateChangedKeys(cached, data.attributes) : Object.keys(data.attributes || {});
}
cached.remoteAttrs = Object.assign(
cached.remoteAttrs || (Object.create(null) as Record),
data.attributes
);
-
if (cached.localAttrs) {
- if (patchLocalAttributes(cached, changedKeys)) {
- this._capabilities.notifyChange(identifier, 'state', null);
+ if (patchLocalAttributes(cached)) {
+ this._capabilities.notifyChange(identifier, 'state');
}
}
if (!isUpdate) {
- this._capabilities.notifyChange(identifier, 'added', null);
+ this._capabilities.notifyChange(identifier, 'added');
}
if (data.id) {
@@ -586,16 +566,11 @@ export default class JSONAPICache implements Cache {
setupRelationships(this.__graph, this._capabilities, identifier, data);
}
- if (changedKeys?.size) {
+ if (changedKeys && changedKeys.length) {
notifyAttributes(this._capabilities, identifier, changedKeys);
}
- if (LOG_OPERATIONS) {
- // eslint-disable-next-line no-console
- console.groupEnd();
- }
-
- return changedKeys?.size ? Array.from(changedKeys) : undefined;
+ return changedKeys;
}
// Cache Forking Support
@@ -787,7 +762,7 @@ export default class JSONAPICache implements Cache {
}
}
- this._capabilities.notifyChange(identifier, 'added', null);
+ this._capabilities.notifyChange(identifier, 'added');
return createOptions;
}
@@ -893,7 +868,7 @@ export default class JSONAPICache implements Cache {
isNew: false,
});
cached.isDeletionCommitted = true;
- this._capabilities.notifyChange(identifier, 'removed', null);
+ this._capabilities.notifyChange(identifier, 'removed');
// TODO @runspired should we early exit here?
}
@@ -915,7 +890,7 @@ export default class JSONAPICache implements Cache {
cached.id = data.id;
}
if (identifier === committedIdentifier && identifier.id !== existingId) {
- this._capabilities.notifyChange(identifier, 'identity', null);
+ this._capabilities.notifyChange(identifier, 'identity');
}
assert(
@@ -958,7 +933,7 @@ export default class JSONAPICache implements Cache {
}
newCanonicalAttributes = data.attributes;
}
- const changedKeys = newCanonicalAttributes && calculateChangedKeys(cached, newCanonicalAttributes);
+ const changedKeys = calculateChangedKeys(cached, newCanonicalAttributes);
cached.remoteAttrs = Object.assign(
cached.remoteAttrs || (Object.create(null) as Record),
@@ -966,15 +941,15 @@ export default class JSONAPICache implements Cache {
newCanonicalAttributes
);
cached.inflightAttrs = null;
- patchLocalAttributes(cached, changedKeys);
+ patchLocalAttributes(cached);
if (cached.errors) {
cached.errors = null;
- this._capabilities.notifyChange(identifier, 'errors', null);
+ this._capabilities.notifyChange(identifier, 'errors');
}
- if (changedKeys?.size) notifyAttributes(this._capabilities, identifier, changedKeys);
- this._capabilities.notifyChange(identifier, 'state', null);
+ notifyAttributes(this._capabilities, identifier, changedKeys);
+ this._capabilities.notifyChange(identifier, 'state');
const included = payload && payload.included;
if (included) {
@@ -1015,7 +990,7 @@ export default class JSONAPICache implements Cache {
if (errors) {
cached.errors = errors;
}
- this._capabilities.notifyChange(identifier, 'errors', null);
+ this._capabilities.notifyChange(identifier, 'errors');
}
/**
@@ -1065,7 +1040,7 @@ export default class JSONAPICache implements Cache {
if (areAllModelsUnloaded(storeWrapper, relatedIdentifiers)) {
for (let i = 0; i < relatedIdentifiers.length; ++i) {
const relatedIdentifier = relatedIdentifiers[i];
- storeWrapper.notifyChange(relatedIdentifier, 'removed', null);
+ storeWrapper.notifyChange(relatedIdentifier, 'removed');
removed = true;
storeWrapper.disconnectRecord(relatedIdentifier);
}
@@ -1095,7 +1070,7 @@ export default class JSONAPICache implements Cache {
}
if (!removed && removeFromRecordArray) {
- storeWrapper.notifyChange(identifier, 'removed', null);
+ storeWrapper.notifyChange(identifier, 'removed');
}
}
@@ -1382,13 +1357,13 @@ export default class JSONAPICache implements Cache {
if (cached.errors) {
cached.errors = null;
- this._capabilities.notifyChange(identifier, 'errors', null);
+ this._capabilities.notifyChange(identifier, 'errors');
}
- this._capabilities.notifyChange(identifier, 'state', null);
+ this._capabilities.notifyChange(identifier, 'state');
if (dirtyKeys && dirtyKeys.length) {
- notifyAttributes(this._capabilities, identifier, new Set(dirtyKeys));
+ notifyAttributes(this._capabilities, identifier, dirtyKeys);
}
return dirtyKeys || [];
@@ -1489,7 +1464,7 @@ export default class JSONAPICache implements Cache {
const cached = this.__peek(identifier, false);
cached.isDeleted = isDeleted;
// > Note: Graph removal for isNew handled by unloadRecord
- this._capabilities.notifyChange(identifier, 'state', null);
+ this._capabilities.notifyChange(identifier, 'state');
}
/**
@@ -1685,18 +1660,14 @@ function getDefaultValue(
}
}
-function notifyAttributes(
- storeWrapper: CacheCapabilitiesManager,
- identifier: StableRecordIdentifier,
- keys?: Set
-) {
+function notifyAttributes(storeWrapper: CacheCapabilitiesManager, identifier: StableRecordIdentifier, keys?: string[]) {
if (!keys) {
- storeWrapper.notifyChange(identifier, 'attributes', null);
+ storeWrapper.notifyChange(identifier, 'attributes');
return;
}
- for (const key of keys) {
- storeWrapper.notifyChange(identifier, 'attributes', key);
+ for (let i = 0; i < keys.length; i++) {
+ storeWrapper.notifyChange(identifier, 'attributes', keys[i]);
}
}
@@ -1705,35 +1676,35 @@ function notifyAttributes(
There seems to be a potential bug here, where we will return keys that are not
in the schema
*/
-function calculateChangedKeys(
- cached: CachedResource,
- updates: Exclude
-): Set {
- const changedKeys = new Set();
- const keys = Object.keys(updates);
- const length = keys.length;
- const localAttrs = cached.localAttrs;
-
- const original: Record = Object.assign(
- Object.create(null) as Record,
- cached.remoteAttrs,
- cached.inflightAttrs
- );
+function calculateChangedKeys(cached: CachedResource, updates?: ExistingResourceObject['attributes']): string[] {
+ const changedKeys: string[] = [];
+
+ if (updates) {
+ const keys = Object.keys(updates);
+ const length = keys.length;
+ const localAttrs = cached.localAttrs;
+
+ const original: Record = Object.assign(
+ Object.create(null) as Record,
+ cached.remoteAttrs,
+ cached.inflightAttrs
+ );
- for (let i = 0; i < length; i++) {
- const key = keys[i];
- const value = updates[key];
+ for (let i = 0; i < length; i++) {
+ const key = keys[i];
+ const value = updates[key];
- // A value in localAttrs means the user has a local change to
- // this attribute. We never override this value when merging
- // updates from the backend so we should not sent a change
- // notification if the server value differs from the original.
- if (localAttrs && localAttrs[key] !== undefined) {
- continue;
- }
+ // A value in localAttrs means the user has a local change to
+ // this attribute. We never override this value when merging
+ // updates from the backend so we should not sent a change
+ // notification if the server value differs from the original.
+ if (localAttrs && localAttrs[key] !== undefined) {
+ continue;
+ }
- if (original[key] !== value) {
- changedKeys.add(key);
+ if (original[key] !== value) {
+ changedKeys.push(key);
+ }
}
}
@@ -1826,7 +1797,7 @@ function isRelationship(field: FieldSchema): field is LegacyRelationshipSchema |
return RelationshipKinds.has(field.kind);
}
-function patchLocalAttributes(cached: CachedResource, changedRemoteKeys?: Set): boolean {
+function patchLocalAttributes(cached: CachedResource): boolean {
const { localAttrs, remoteAttrs, inflightAttrs, defaultAttrs, changes } = cached;
if (!localAttrs) {
cached.changes = null;
@@ -1846,11 +1817,6 @@ function patchLocalAttributes(cached: CachedResource, changedRemoteKeys?: Set",
"repository": {
@@ -85,7 +85,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"peerDependencies": {
diff --git a/packages/legacy-compat/src/builders/utils.ts b/packages/legacy-compat/src/builders/utils.ts
index 2cc2c71689f..dc29dd7bb26 100644
--- a/packages/legacy-compat/src/builders/utils.ts
+++ b/packages/legacy-compat/src/builders/utils.ts
@@ -1,7 +1,7 @@
import { deprecate } from '@ember/debug';
import { dasherize } from '@ember-data/request-utils/string';
-import { DEPRECATE_NON_STRICT_TYPES } from '@warp-drive/build-config/deprecations';
+import { DEPRECATE_NON_STRICT_TYPES, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
import type { ResourceIdentifierObject } from '@warp-drive/core-types/spec/json-api-raw';
export function isMaybeIdentifier(
@@ -21,7 +21,7 @@ export function normalizeModelName(type: string): string {
deprecate(
`The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`,
- result === type,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : result === type,
{
id: 'ember-data:deprecate-non-strict-types',
until: '6.0',
diff --git a/packages/legacy-compat/src/index.ts b/packages/legacy-compat/src/index.ts
index 62a23740ef7..c9077b86e46 100644
--- a/packages/legacy-compat/src/index.ts
+++ b/packages/legacy-compat/src/index.ts
@@ -1,12 +1,14 @@
import { getOwner } from '@ember/application';
+import { deprecate } from '@ember/debug';
import type Store from '@ember-data/store';
import { recordIdentifierFor } from '@ember-data/store';
-import { _deprecatingNormalize } from '@ember-data/store/-private';
+import { DEPRECATE_JSON_API_FALLBACK } from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
import type { ObjectValue } from '@warp-drive/core-types/json/raw';
import { FetchManager, upgradeStore } from './-private';
+import { normalizeModelName } from './builders/utils';
import type { AdapterPayload, MinimumAdapterInterface } from './legacy-network-handler/minimum-adapter-interface';
import type {
MinimumSerializerInterface,
@@ -68,7 +70,7 @@ export function adapterFor(this: Store, modelName: string, _allowMissing?: true)
this._adapterCache =
this._adapterCache || (Object.create(null) as Record);
- const normalizedModelName = _deprecatingNormalize(modelName);
+ const normalizedModelName = normalizeModelName(modelName);
const { _adapterCache } = this;
let adapter: (MinimumAdapterInterface & { store: Store }) | undefined = _adapterCache[normalizedModelName];
@@ -93,6 +95,28 @@ export function adapterFor(this: Store, modelName: string, _allowMissing?: true)
return adapter;
}
+ if (DEPRECATE_JSON_API_FALLBACK) {
+ // final fallback, no model specific adapter, no application adapter, no
+ // `adapter` property on store: use json-api adapter
+ adapter = _adapterCache['-json-api'] || owner.lookup('adapter:-json-api');
+ if (adapter !== undefined) {
+ deprecate(
+ `Your application is utilizing a deprecated hidden fallback adapter (-json-api). Please implement an application adapter to function as your fallback.`,
+ false,
+ {
+ id: 'ember-data:deprecate-secret-adapter-fallback',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.5', enabled: '4.5' },
+ }
+ );
+ _adapterCache[normalizedModelName] = adapter;
+ _adapterCache['-json-api'] = adapter;
+
+ return adapter;
+ }
+ }
+
assert(
`No adapter was found for '${modelName}' and no 'application' adapter was found as a fallback.`,
_allowMissing
@@ -129,7 +153,7 @@ export function serializerFor(this: Store, modelName: string): MinimumSerializer
upgradeStore(this);
this._serializerCache =
this._serializerCache || (Object.create(null) as Record);
- const normalizedModelName = _deprecatingNormalize(modelName);
+ const normalizedModelName = normalizeModelName(modelName);
const { _serializerCache } = this;
let serializer: (MinimumSerializerInterface & { store: Store }) | undefined = _serializerCache[normalizedModelName];
@@ -190,7 +214,7 @@ export function normalize(this: Store, modelName: string, payload: ObjectValue)
`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${typeof modelName}`,
typeof modelName === 'string'
);
- const normalizedModelName = _deprecatingNormalize(modelName);
+ const normalizedModelName = normalizeModelName(modelName);
const serializer = this.serializerFor(normalizedModelName);
const schema = this.modelFor(normalizedModelName);
assert(
@@ -265,7 +289,7 @@ export function pushPayload(this: Store, modelName: string, inputPayload: Object
);
const payload: ObjectValue = inputPayload || (modelName as unknown as ObjectValue);
- const normalizedModelName = inputPayload ? _deprecatingNormalize(modelName) : 'application';
+ const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
const serializer = this.serializerFor(normalizedModelName);
assert(
diff --git a/packages/legacy-compat/src/legacy-network-handler/fetch-manager.ts b/packages/legacy-compat/src/legacy-network-handler/fetch-manager.ts
index 1e30751209e..68ae09dd30c 100644
--- a/packages/legacy-compat/src/legacy-network-handler/fetch-manager.ts
+++ b/packages/legacy-compat/src/legacy-network-handler/fetch-manager.ts
@@ -1,4 +1,4 @@
-import { warn } from '@ember/debug';
+import { deprecate, warn } from '@ember/debug';
import { dependencySatisfies, importSync, macroCondition } from '@embroider/macros';
@@ -13,6 +13,7 @@ import type {
} from '@ember-data/store/-private';
import { coerceId } from '@ember-data/store/-private';
import type { FindRecordOptions, ModelSchema } from '@ember-data/store/types';
+import { DEPRECATE_RSVP_PROMISE } from '@warp-drive/build-config/deprecations';
import { DEBUG, TESTING } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import { getOrSetGlobal } from '@warp-drive/core-types/-private';
@@ -28,6 +29,7 @@ import type { AdapterPayload, MinimumAdapterInterface } from './minimum-adapter-
import type { MinimumSerializerInterface } from './minimum-serializer-interface';
import { normalizeResponseHelper } from './serializer-response';
import { Snapshot } from './snapshot';
+import { _objectIsAlive } from './utils';
type Deferred = ReturnType>;
type AdapterErrors = Error & { errors?: string[]; isAdapterError?: true };
@@ -611,6 +613,7 @@ function _flushPendingSave(store: Store, pending: PendingSaveItem) {
const modelName = snapshot.modelName;
const modelClass = store.modelFor(modelName);
+ const record = store._instanceCache.getRecord(identifier);
assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter);
assert(
@@ -627,6 +630,24 @@ function _flushPendingSave(store: Store, pending: PendingSaveItem) {
);
promise = promise.then((adapterPayload) => {
+ if (!_objectIsAlive(record)) {
+ if (DEPRECATE_RSVP_PROMISE) {
+ deprecate(
+ `A Promise while saving ${modelName} did not resolve by the time your model was destroyed. This will error in a future release.`,
+ false,
+ {
+ id: 'ember-data:rsvp-unresolved-async',
+ until: '5.0',
+ for: '@ember-data/store',
+ since: {
+ available: '4.5',
+ enabled: '4.5',
+ },
+ }
+ );
+ }
+ }
+
if (adapterPayload) {
return normalizeResponseHelper(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
}
diff --git a/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.ts b/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.ts
index d1618279415..6864f61fa05 100644
--- a/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.ts
+++ b/packages/legacy-compat/src/legacy-network-handler/legacy-data-fetch.ts
@@ -1,8 +1,12 @@
+import { deprecate } from '@ember/debug';
+
import type Store from '@ember-data/store';
-import type { BaseFinderOptions } from '@ember-data/store/types';
+import type { BaseFinderOptions, ModelSchema } from '@ember-data/store/types';
+import { DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE, DEPRECATE_RSVP_PROMISE } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
+import type { StableExistingRecordIdentifier } from '@warp-drive/core-types/identifier';
import type { LegacyRelationshipSchema as RelationshipSchema } from '@warp-drive/core-types/schema/fields';
import type { ExistingResourceObject, JsonApiDocument } from '@warp-drive/core-types/spec/json-api-raw';
@@ -10,6 +14,7 @@ import { upgradeStore } from '../-private';
import { iterateData, payloadIsNotBlank } from './legacy-data-utils';
import type { MinimumAdapterInterface } from './minimum-adapter-interface';
import { normalizeResponseHelper } from './serializer-response';
+import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './utils';
export function _findHasMany(
adapter: MinimumAdapterInterface,
@@ -18,9 +23,9 @@ export function _findHasMany(
link: string | null | { href: string },
relationship: RelationshipSchema,
options: BaseFinderOptions
-) {
+): Promise {
upgradeStore(store);
- const promise = Promise.resolve().then(() => {
+ let promise: Promise = Promise.resolve().then(() => {
const snapshot = store._fetchManager.createSnapshot(identifier, options);
const useLink = !link || typeof link === 'string';
const relatedLink = useLink ? link : link.href;
@@ -35,7 +40,28 @@ export function _findHasMany(
return adapter.findHasMany(store, snapshot, relatedLink, relationship);
});
- return promise.then((adapterPayload) => {
+ promise = guardDestroyedStore(promise, store);
+ promise = promise.then((adapterPayload) => {
+ const record = store._instanceCache.getRecord(identifier);
+
+ if (!_objectIsAlive(record)) {
+ if (DEPRECATE_RSVP_PROMISE) {
+ deprecate(
+ `A Promise for fetching ${relationship.type} did not resolve by the time your model was destroyed. This will error in a future release.`,
+ false,
+ {
+ id: 'ember-data:rsvp-unresolved-async',
+ until: '5.0',
+ for: '@ember-data/store',
+ since: {
+ available: '4.5',
+ enabled: '4.5',
+ },
+ }
+ );
+ }
+ }
+
assert(
`You made a 'findHasMany' request for a ${identifier.type}'s '${
relationship.name
@@ -59,6 +85,14 @@ export function _findHasMany(
payload = syncRelationshipDataFromLink(store, payload, identifier as ResourceIdentity, relationship);
return store._push(payload, true);
}, null);
+
+ if (DEPRECATE_RSVP_PROMISE) {
+ const record = store._instanceCache.getRecord(identifier);
+
+ promise = _guard(promise, _bind(_objectIsAlive, record));
+ }
+
+ return promise as Promise;
}
export function _findBelongsTo(
@@ -69,7 +103,7 @@ export function _findBelongsTo(
options: BaseFinderOptions
) {
upgradeStore(store);
- const promise = Promise.resolve().then(() => {
+ let promise = Promise.resolve().then(() => {
const adapter = store.adapterFor(identifier.type);
assert(`You tried to load a belongsTo relationship but you have no adapter (for ${identifier.type})`, adapter);
assert(
@@ -86,7 +120,33 @@ export function _findBelongsTo(
return adapter.findBelongsTo(store, snapshot, relatedLink, relationship);
});
+ if (DEPRECATE_RSVP_PROMISE) {
+ const record = store._instanceCache.getRecord(identifier);
+ promise = guardDestroyedStore(promise, store);
+ promise = _guard(promise, _bind(_objectIsAlive, record));
+ }
+
return promise.then((adapterPayload) => {
+ if (DEPRECATE_RSVP_PROMISE) {
+ const record = store._instanceCache.getRecord(identifier);
+
+ if (!_objectIsAlive(record)) {
+ deprecate(
+ `A Promise for fetching ${relationship.type} did not resolve by the time your model was destroyed. This will error in a future release.`,
+ false,
+ {
+ id: 'ember-data:rsvp-unresolved-async',
+ until: '5.0',
+ for: '@ember-data/store',
+ since: {
+ available: '4.5',
+ enabled: '4.5',
+ },
+ }
+ );
+ }
+ }
+
const modelClass = store.modelFor(relationship.type);
const serializer = store.serializerFor(relationship.type);
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findBelongsTo');
@@ -226,11 +286,24 @@ function ensureRelationshipIsSetToParent(
}
}
+type LegacyRelationshipDefinition = { _inverseKey: (store: Store, modelClass: ModelSchema) => string | null };
+
+function metaIsRelationshipDefinition(meta: unknown): meta is LegacyRelationshipDefinition {
+ return typeof meta === 'object' && !!meta && '_inverseKey' in meta && typeof meta._inverseKey === 'function';
+}
+
function inverseForRelationship(store: Store, identifier: { type: string; id?: string }, key: string) {
const definition = store.schema.fields(identifier).get(key);
if (!definition) {
return null;
}
+
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
+ if (metaIsRelationshipDefinition(definition)) {
+ const modelClass = store.modelFor(identifier.type);
+ return definition._inverseKey(store, modelClass);
+ }
+ }
assert(
`Expected the field definition to be a relationship`,
definition.kind === 'hasMany' || definition.kind === 'belongsTo'
diff --git a/packages/legacy-compat/src/legacy-network-handler/snapshot-record-array.ts b/packages/legacy-compat/src/legacy-network-handler/snapshot-record-array.ts
index 1121ec58fc8..a6e996c9bd9 100644
--- a/packages/legacy-compat/src/legacy-network-handler/snapshot-record-array.ts
+++ b/packages/legacy-compat/src/legacy-network-handler/snapshot-record-array.ts
@@ -1,14 +1,19 @@
/**
@module @ember-data/legacy-compat
*/
+
+import { deprecate } from '@ember/debug';
+
import type Store from '@ember-data/store';
import type { LiveArray } from '@ember-data/store/-private';
import { SOURCE } from '@ember-data/store/-private';
import type { FindAllOptions, ModelSchema } from '@ember-data/store/types';
+import { DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS } from '@warp-drive/build-config/deprecations';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
import { upgradeStore } from '../-private';
import type { Snapshot } from './snapshot';
+
/**
SnapshotRecordArray is not directly instantiable.
Instances are provided to consuming application's
@@ -180,3 +185,29 @@ export class SnapshotRecordArray {
return this._snapshots;
}
}
+
+if (DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS) {
+ /**
+ The type of the underlying records for the snapshots in the array, as a Model
+
+ @deprecated
+ @property type
+ @public
+ @type {Model}
+ */
+ Object.defineProperty(SnapshotRecordArray.prototype, 'type', {
+ get() {
+ deprecate(
+ `Using SnapshotRecordArray.type to access the ModelClass for a record is deprecated. Use store.modelFor() instead.`,
+ false,
+ {
+ id: 'ember-data:deprecate-snapshot-model-class-access',
+ until: '5.0',
+ for: 'ember-data',
+ since: { available: '4.5.0', enabled: '4.5.0' },
+ }
+ );
+ return (this as SnapshotRecordArray)._recordArray.type as ModelSchema;
+ },
+ });
+}
diff --git a/packages/legacy-compat/src/legacy-network-handler/snapshot.ts b/packages/legacy-compat/src/legacy-network-handler/snapshot.ts
index 1198930fc9a..becb2a7524d 100644
--- a/packages/legacy-compat/src/legacy-network-handler/snapshot.ts
+++ b/packages/legacy-compat/src/legacy-network-handler/snapshot.ts
@@ -1,11 +1,14 @@
/**
@module @ember-data/store
*/
+import { deprecate } from '@ember/debug';
+
import { dependencySatisfies, importSync } from '@embroider/macros';
import type { CollectionEdge, ResourceEdge } from '@ember-data/graph/-private';
import type Store from '@ember-data/store';
-import type { FindRecordOptions } from '@ember-data/store/types';
+import type { FindRecordOptions, ModelSchema } from '@ember-data/store/types';
+import { DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -46,6 +49,16 @@ export class Snapshot {
declare adapterOptions?: Record;
declare _store: Store;
+ /**
+ The type of the underlying record for this snapshot, as a Model.
+
+ @property type
+ @public
+ @deprecated
+ @type {Model}
+ */
+ declare type: ModelSchema;
+
/**
* @method constructor
* @constructor
@@ -561,3 +574,21 @@ export class Snapshot {
return serializer.serialize(this, options);
}
}
+
+if (DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS) {
+ Object.defineProperty(Snapshot.prototype, 'type', {
+ get(this: Snapshot) {
+ deprecate(
+ `Using Snapshot.type to access the ModelClass for a record is deprecated. Use store.modelFor() instead.`,
+ false,
+ {
+ id: 'ember-data:deprecate-snapshot-model-class-access',
+ until: '5.0',
+ for: 'ember-data',
+ since: { available: '4.5.0', enabled: '4.5.0' },
+ }
+ );
+ return this._store.modelFor(this.identifier.type);
+ },
+ });
+}
diff --git a/packages/legacy-compat/src/legacy-network-handler/utils.ts b/packages/legacy-compat/src/legacy-network-handler/utils.ts
new file mode 100644
index 00000000000..69c64fd7e71
--- /dev/null
+++ b/packages/legacy-compat/src/legacy-network-handler/utils.ts
@@ -0,0 +1,57 @@
+import { deprecate } from '@ember/debug';
+
+import type Store from '@ember-data/store';
+import { DEPRECATE_RSVP_PROMISE } from '@warp-drive/build-config/deprecations';
+
+function isObject(value: unknown): value is T {
+ return value !== null && typeof value === 'object';
+}
+
+export function _objectIsAlive(object: unknown): boolean {
+ return isObject<{ isDestroyed: boolean; isDestroying: boolean }>(object)
+ ? !(object.isDestroyed || object.isDestroying)
+ : false;
+}
+
+export function guardDestroyedStore(promise: Promise, store: Store): Promise {
+ return promise.then((_v) => {
+ if (!_objectIsAlive(store)) {
+ if (DEPRECATE_RSVP_PROMISE) {
+ deprecate(
+ `A Promise did not resolve by the time the store was destroyed. This will error in a future release.`,
+ false,
+ {
+ id: 'ember-data:rsvp-unresolved-async',
+ until: '5.0',
+ for: '@ember-data/store',
+ since: {
+ available: '4.5',
+ enabled: '4.5',
+ },
+ }
+ );
+ }
+ }
+
+ return _v;
+ });
+}
+
+export function _bind boolean>(fn: T, ...args: unknown[]) {
+ return function () {
+ // eslint-disable-next-line prefer-spread
+ return fn.apply(undefined, args);
+ };
+}
+
+export function _guard(promise: Promise, test: () => boolean): Promise {
+ const guarded = promise.finally(() => {
+ if (!test()) {
+ // @ts-expect-error this is a private RSVPPromise API that won't always be there
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions, @typescript-eslint/no-unsafe-member-access
+ guarded._subscribers ? (guarded._subscribers.length = 0) : null;
+ }
+ });
+
+ return guarded;
+}
diff --git a/packages/model/eslint.config.mjs b/packages/model/eslint.config.mjs
index 9ca51cdf5b4..c42683457ca 100644
--- a/packages/model/eslint.config.mjs
+++ b/packages/model/eslint.config.mjs
@@ -2,6 +2,7 @@
import { globalIgnores } from '@warp-drive/internal-config/eslint/ignore.js';
import * as node from '@warp-drive/internal-config/eslint/node.js';
import * as typescript from '@warp-drive/internal-config/eslint/typescript.js';
+import { externals } from './vite.config.mjs';
/** @type {import('eslint').Linter.FlatConfig[]} */
export default [
@@ -11,17 +12,7 @@ export default [
// browser (js/ts) ================
typescript.browser({
srcDirs: ['src'],
- allowedImports: [
- '@ember/array',
- '@ember/array/proxy',
- '@ember/debug',
- '@ember/object/internals',
- '@ember/object/proxy',
- '@ember/object/computed',
- '@ember/object',
- '@ember/application',
- '@ember/object/promise-proxy-mixin',
- ],
+ allowedImports: externals,
}),
// node (module) ================
diff --git a/packages/model/package.json b/packages/model/package.json
index d08bf043f8d..71c34256d9c 100644
--- a/packages/model/package.json
+++ b/packages/model/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/model",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "A basic Ember implementation of a resource presentation layer for use with @ember-data/store",
"keywords": [
"ember-addon"
@@ -96,7 +96,7 @@
},
"dependencies": {
"@ember/edition-utils": "^1.2.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"ember-cli-string-utils": "^1.1.0",
"ember-cli-test-info": "^1.0.0",
"inflection": "~3.0.0",
@@ -118,7 +118,7 @@
"@glimmer/component": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
- "decorator-transforms": "^2.3.0",
+ "decorator-transforms": "^2.2.2",
"ember-source": "~5.12.0",
"expect-type": "^0.20.0",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
diff --git a/packages/model/src/-private/belongs-to.ts b/packages/model/src/-private/belongs-to.ts
index 2f3d5890956..95bbacb4889 100644
--- a/packages/model/src/-private/belongs-to.ts
+++ b/packages/model/src/-private/belongs-to.ts
@@ -1,6 +1,14 @@
-import { warn } from '@ember/debug';
+import { deprecate, warn } from '@ember/debug';
import { computed } from '@ember/object';
+import { dasherize, singularize } from '@ember-data/request-utils/string';
+import {
+ DEPRECATE_NON_STRICT_TYPES,
+ DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC,
+ DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
+ DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE,
+ DISABLE_6X_DEPRECATIONS,
+} from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { TypeFromInstance } from '@warp-drive/core-types/record';
@@ -8,7 +16,7 @@ import { RecordStore } from '@warp-drive/core-types/symbols';
import { lookupLegacySupport } from './legacy-relationships-support';
import type { MinimalLegacyRecord } from './model-methods';
-import { isElementDescriptor, normalizeModelName } from './util';
+import { isElementDescriptor } from './util';
/**
@module @ember-data/model
*/
@@ -20,7 +28,6 @@ export type RelationshipOptions = {
inverse: null | (IsUnknown extends true ? string : keyof NoNull & string);
polymorphic?: boolean;
as?: string;
- linksMode?: true;
resetOnRemoteUpdate?: boolean;
};
@@ -34,22 +41,116 @@ export type NoNull = Exclude;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export type RelationshipDecorator = (target: This, key: string, desc?: PropertyDescriptor) => void; // BelongsToDecoratorObject;
+function normalizeType(type: string) {
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
+ if (!type) {
+ return;
+ }
+ }
+
+ if (DEPRECATE_NON_STRICT_TYPES) {
+ const result = singularize(dasherize(type));
+
+ deprecate(
+ `The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : result === type,
+ {
+ id: 'ember-data:deprecate-non-strict-types',
+ until: '6.0',
+ for: 'ember-data',
+ since: {
+ available: '4.13',
+ enabled: '5.3',
+ },
+ }
+ );
+
+ return result;
+ }
+
+ return type;
+}
+
function _belongsTo(
type: string,
options: RelationshipOptions
): RelationshipDecorator {
- assert(
- `Expected options.async from @belongsTo('${type}', options) to be a boolean`,
- options && typeof options.async === 'boolean'
- );
- assert(
- `Expected options.inverse from @belongsTo('${type}', options) to be either null or the string type of the related resource.`,
- options.inverse === null || (typeof options.inverse === 'string' && options.inverse.length > 0)
- );
+ let opts = options;
+ let rawType: string | undefined = type;
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
+ if (typeof type !== 'string' || !type.length) {
+ deprecate('belongsTo() must specify the string type of the related resource as the first parameter', false, {
+ id: 'ember-data:deprecate-non-strict-relationships',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ });
+
+ if (typeof type === 'object') {
+ opts = type;
+ rawType = undefined;
+ } else {
+ opts = options;
+ rawType = type;
+ }
+
+ assert(
+ 'The first argument to belongsTo must be a string representing a model type key, not an instance of ' +
+ typeof rawType +
+ ". E.g., to define a relation to the Person model, use belongsTo('person')",
+ typeof rawType === 'string' || typeof rawType === 'undefined'
+ );
+ }
+ }
+
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC) {
+ if (!opts || typeof opts.async !== 'boolean') {
+ opts = opts || {};
+ if (!('async' in opts)) {
+ // @ts-expect-error the inbound signature is strict to convince the user to use the non-deprecated signature
+ opts.async = true as Async;
+ }
+ deprecate('belongsTo(, ) must specify options.async as either `true` or `false`.', false, {
+ id: 'ember-data:deprecate-non-strict-relationships',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ });
+ } else {
+ assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
+ }
+ } else {
+ assert(`Expected belongsTo options.async to be a boolean`, opts && typeof opts.async === 'boolean');
+ }
+
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
+ if (opts.inverse !== null && (typeof opts.inverse !== 'string' || opts.inverse.length === 0)) {
+ deprecate(
+ 'belongsTo(, ) must specify options.inverse as either `null` or the name of the field on the related resource type.',
+ false,
+ {
+ id: 'ember-data:deprecate-non-strict-relationships',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Expected belongsTo options.inverse to be either null or the string type of the related resource.`,
+ opts.inverse === null || (typeof opts.inverse === 'string' && opts.inverse.length > 0)
+ );
+ }
+ } else {
+ assert(
+ `Expected belongsTo options.inverse to be either null or the string type of the related resource.`,
+ opts.inverse === null || (typeof opts.inverse === 'string' && opts.inverse.length > 0)
+ );
+ }
const meta = {
- type: normalizeModelName(type),
- options: options,
+ type: normalizeType(type),
+ options: opts,
kind: 'belongsTo',
name: '',
};
@@ -286,11 +387,17 @@ export function belongsTo(
type?: TypeFromInstance>,
options?: RelationshipOptions
): RelationshipDecorator {
- if (DEBUG) {
+ if (!DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
assert(
- `belongsTo must be invoked with a type and options. Did you mean \`@belongsTo(${type}, { async: false, inverse: null })\`?`,
+ `belongsTo must be invoked with a type and options. Did you mean \`@belongsTo(, { async: false, inverse: null })\`?`,
!isElementDescriptor(arguments as unknown as unknown[])
);
+ return _belongsTo(type!, options!);
+ } else {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return isElementDescriptor(arguments as unknown as any[])
+ ? // @ts-expect-error the inbound signature is strict to convince the user to use the non-deprecated signature
+ (_belongsTo()(...arguments) as RelationshipDecorator)
+ : _belongsTo(type!, options!);
}
- return _belongsTo(type!, options!);
}
diff --git a/packages/model/src/-private/debug/assert-polymorphic-type.ts b/packages/model/src/-private/debug/assert-polymorphic-type.ts
index 49c451e0f99..bf9f57b79ee 100644
--- a/packages/model/src/-private/debug/assert-polymorphic-type.ts
+++ b/packages/model/src/-private/debug/assert-polymorphic-type.ts
@@ -1,8 +1,31 @@
+import type Mixin from '@ember/object/mixin';
+
import type { UpgradedMeta } from '@ember-data/graph/-private';
import type Store from '@ember-data/store';
+import type { ModelSchema } from '@ember-data/store/types';
+import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
+import type { FieldSchema, LegacyRelationshipSchema } from '@warp-drive/core-types/schema/fields';
+
+import { Model } from '../model';
+
+// A pile of soft-lies to deal with mixin APIs
+type ModelWithMixinApis = Model & {
+ __isMixin?: boolean;
+ __mixin: Mixin;
+ PrototypeMixin: Mixin;
+ detect: (mixin: Model | Mixin | ModelWithMixinApis) => boolean;
+ prototype: Model;
+ [Symbol.hasInstance](model: Model): true;
+};
+
+function assertModelSchemaIsModel(
+ schema: ModelSchema | Model | ModelWithMixinApis
+): asserts schema is ModelWithMixinApis {
+ assert(`Expected Schema to be an instance of Model`, schema instanceof Model);
+}
/*
Assert that `addedRecord` has a valid type so it can be added to the
@@ -24,6 +47,26 @@ let assertPolymorphicType: (
) => void;
if (DEBUG) {
+ const checkPolymorphic = function checkPolymorphic(modelClass: ModelSchema, addedModelClass: ModelSchema) {
+ assertModelSchemaIsModel(modelClass);
+ assertModelSchemaIsModel(addedModelClass);
+
+ if (modelClass.__isMixin) {
+ return (
+ modelClass.__mixin.detect(addedModelClass.PrototypeMixin) ||
+ // handle native class extension e.g. `class Post extends Model.extend(Commentable) {}`
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ modelClass.__mixin.detect(Object.getPrototypeOf(addedModelClass).PrototypeMixin)
+ );
+ }
+
+ return addedModelClass.prototype instanceof modelClass || modelClass.detect(addedModelClass);
+ };
+
+ const isRelationshipField = function isRelationshipField(meta: FieldSchema): meta is LegacyRelationshipSchema {
+ return meta.kind === 'hasMany' || meta.kind === 'belongsTo';
+ };
+
// eslint-disable-next-line @typescript-eslint/no-shadow
assertPolymorphicType = function assertPolymorphicType(
parentIdentifier: StableRecordIdentifier,
@@ -34,16 +77,45 @@ if (DEBUG) {
if (parentDefinition.inverseIsImplicit) {
return;
}
+ let asserted = false;
if (parentDefinition.isPolymorphic) {
const meta = store.schema.fields(addedIdentifier)?.get(parentDefinition.inverseKey);
assert(
- `Expected the schema for the field ${parentDefinition.inverseKey} on ${addedIdentifier.type} to be for a legacy relationship`,
- !meta || meta.kind === 'belongsTo' || meta.kind === 'hasMany'
- );
- assert(
- `The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
- meta?.options.as === parentDefinition.type
+ `Expected to find a relationship field schema for ${parentDefinition.inverseKey} on ${addedIdentifier.type} but none was found`,
+ meta && isRelationshipField(meta)
);
+
+ if (!DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ assert(
+ `The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
+ meta.options.as === parentDefinition.type
+ );
+ } else if ((meta.options.as?.length ?? 0) > 0) {
+ asserted = true;
+ assert(
+ `The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
+ meta.options.as === parentDefinition.type
+ );
+ }
+
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ if (!asserted) {
+ store = (store as unknown as { _store: Store })._store
+ ? (store as unknown as { _store: Store })._store
+ : store; // allow usage with storeWrapper
+ const addedModelName = addedIdentifier.type;
+ const parentModelName = parentIdentifier.type;
+ const key = parentDefinition.key;
+ const relationshipModelName = parentDefinition.type;
+ const relationshipClass = store.modelFor(relationshipModelName);
+ const addedClass = store.modelFor(addedModelName);
+
+ const assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
+ const isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
+
+ assert(assertionMessage, isPolymorphic);
+ }
+ }
}
};
}
diff --git a/packages/model/src/-private/deprecated-promise-proxy.ts b/packages/model/src/-private/deprecated-promise-proxy.ts
new file mode 100644
index 00000000000..0adb72058ae
--- /dev/null
+++ b/packages/model/src/-private/deprecated-promise-proxy.ts
@@ -0,0 +1,73 @@
+import { deprecate } from '@ember/debug';
+import { get } from '@ember/object';
+
+import { DEBUG } from '@warp-drive/build-config/env';
+
+import { PromiseObject } from './promise-proxy-base';
+
+function promiseObject(promise: Promise): PromiseObject {
+ return PromiseObject.create({ promise }) as PromiseObject;
+}
+
+// constructor is accessed in some internals but not including it in the copyright for the deprecation
+const ALLOWABLE_METHODS = ['constructor', 'then', 'catch', 'finally'];
+const ALLOWABLE_PROPS = ['__ec_yieldable__', '__ec_cancel__'];
+const PROXIED_OBJECT_PROPS = ['content', 'isPending', 'isSettled', 'isRejected', 'isFulfilled', 'promise', 'reason'];
+
+const ProxySymbolString = String(Symbol.for('PROXY_CONTENT'));
+
+export function deprecatedPromiseObject(promise: Promise): PromiseObject {
+ const promiseObjectProxy: PromiseObject = promiseObject(promise);
+ if (!DEBUG) {
+ return promiseObjectProxy;
+ }
+ const handler = {
+ get(target: object, prop: string, receiver: object): unknown {
+ if (typeof prop === 'symbol') {
+ if (String(prop) === ProxySymbolString) {
+ return;
+ }
+ return Reflect.get(target, prop, receiver);
+ }
+
+ if (prop === 'constructor') {
+ return target.constructor;
+ }
+
+ if (ALLOWABLE_PROPS.includes(prop)) {
+ return target[prop];
+ }
+
+ if (!ALLOWABLE_METHODS.includes(prop)) {
+ deprecate(
+ `Accessing ${prop} is deprecated. The return type is being changed from PromiseObjectProxy to a Promise. The only available methods to access on this promise are .then, .catch and .finally`,
+ false,
+ {
+ id: 'ember-data:model-save-promise',
+ until: '5.0',
+ for: '@ember-data/store',
+ since: {
+ available: '4.4',
+ enabled: '4.4',
+ },
+ }
+ );
+ } else {
+ return (target[prop] as () => unknown).bind(target);
+ }
+
+ if (PROXIED_OBJECT_PROPS.includes(prop)) {
+ return target[prop];
+ }
+
+ const value: unknown = get(target, prop);
+ if (value && typeof value === 'function' && typeof value.bind === 'function') {
+ return value.bind(receiver);
+ }
+
+ return undefined;
+ },
+ };
+
+ return new Proxy(promiseObjectProxy, handler) as PromiseObject;
+}
diff --git a/packages/model/src/-private/has-many.ts b/packages/model/src/-private/has-many.ts
index 9a6fcb286c3..305ae73431c 100644
--- a/packages/model/src/-private/has-many.ts
+++ b/packages/model/src/-private/has-many.ts
@@ -1,11 +1,17 @@
/**
@module @ember-data/model
*/
-import { deprecate } from '@ember/debug';
+import { deprecate, inspect } from '@ember/debug';
import { computed } from '@ember/object';
import { dasherize, singularize } from '@ember-data/request-utils/string';
-import { DEPRECATE_NON_STRICT_TYPES } from '@warp-drive/build-config/deprecations';
+import {
+ DEPRECATE_NON_STRICT_TYPES,
+ DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC,
+ DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
+ DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE,
+ DISABLE_6X_DEPRECATIONS,
+} from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { TypeFromInstance } from '@warp-drive/core-types/record';
@@ -17,12 +23,18 @@ import type { MinimalLegacyRecord } from './model-methods';
import { isElementDescriptor } from './util';
function normalizeType(type: string) {
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
+ if (!type) {
+ return;
+ }
+ }
+
if (DEPRECATE_NON_STRICT_TYPES) {
const result = singularize(dasherize(type));
deprecate(
`The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`,
- result === type,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : result === type,
{
id: 'ember-data:deprecate-non-strict-types',
until: '6.0',
@@ -44,7 +56,66 @@ function _hasMany(
type: string,
options: RelationshipOptions
): RelationshipDecorator {
- assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
+ if (typeof type !== 'string' || !type.length) {
+ deprecate(
+ 'hasMany(, ) must specify the string type of the related resource as the first parameter',
+ false,
+ {
+ id: 'ember-data:deprecate-non-strict-relationships',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ }
+ );
+ if (typeof type === 'object') {
+ options = type;
+ type = undefined as unknown as string;
+ }
+
+ assert(
+ `The first argument to hasMany must be a string representing a model type key, not an instance of ${inspect(
+ type
+ )}. E.g., to define a relation to the Comment model, use hasMany('comment')`,
+ typeof type === 'string' || typeof type === 'undefined'
+ );
+ }
+ }
+
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC) {
+ if (!options || typeof options.async !== 'boolean') {
+ options = options || {};
+ if (!('async' in options)) {
+ // @ts-expect-error the inbound signature is strict to convince the user to use the non-deprecated signature
+ options.async = true;
+ }
+ deprecate('hasMany(, ) must specify options.async as either `true` or `false`.', false, {
+ id: 'ember-data:deprecate-non-strict-relationships',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ });
+ } else {
+ assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
+ }
+ } else {
+ assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
+ }
+
+ if (DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE) {
+ if (options.inverse !== null && (typeof options.inverse !== 'string' || options.inverse.length === 0)) {
+ deprecate(
+ 'hasMany(, ) must specify options.inverse as either `null` or the name of the field on the related resource type.',
+ false,
+ {
+ id: 'ember-data:deprecate-non-strict-relationships',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ }
+ );
+ }
+ }
// Metadata about relationships is stored on the meta of
// the relationship. This is used for introspection and
@@ -266,11 +337,17 @@ export function hasMany(
type?: TypeFromInstance>,
options?: RelationshipOptions
): RelationshipDecorator {
- if (DEBUG) {
+ if (!DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) {
assert(
- `hasMany must be invoked with a type and options. Did you mean \`@hasMany(${type}, { async: false, inverse: null })\`?`,
+ `hasMany must be invoked with a type and options. Did you mean \`@hasMany(, { async: false, inverse: null })\`?`,
!isElementDescriptor(arguments as unknown as unknown[])
);
+ return _hasMany(type!, options!);
+ } else {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return isElementDescriptor(arguments as unknown as any[])
+ ? // @ts-expect-error the inbound signature is strict to convince the user to use the non-deprecated signature
+ (_hasMany()(...arguments) as RelationshipDecorator)
+ : _hasMany(type!, options!);
}
- return _hasMany(type!, options!);
}
diff --git a/packages/model/src/-private/legacy-relationships-support.ts b/packages/model/src/-private/legacy-relationships-support.ts
index a7288a1ae2d..ef6fe4831f4 100644
--- a/packages/model/src/-private/legacy-relationships-support.ts
+++ b/packages/model/src/-private/legacy-relationships-support.ts
@@ -1,9 +1,10 @@
+import { deprecate } from '@ember/debug';
+
import { dependencySatisfies, importSync, macroCondition } from '@embroider/macros';
import type { CollectionEdge, Graph, GraphEdge, ResourceEdge, UpgradedMeta } from '@ember-data/graph/-private';
import { upgradeStore } from '@ember-data/legacy-compat/-private';
import type Store from '@ember-data/store';
-import type { Document } from '@ember-data/store';
import type { LiveArray } from '@ember-data/store/-private';
import {
fastPush,
@@ -14,6 +15,7 @@ import {
storeFor,
} from '@ember-data/store/-private';
import type { BaseFinderOptions } from '@ember-data/store/types';
+import { DEPRECATE_PROMISE_PROXIES } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -22,7 +24,6 @@ import type { Cache } from '@warp-drive/core-types/cache';
import type { CollectionRelationship } from '@warp-drive/core-types/cache/relationship';
import type { LocalRelationshipOperation } from '@warp-drive/core-types/graph';
import type { OpaqueRecordInstance, TypeFromInstanceOrString } from '@warp-drive/core-types/record';
-import { EnableHydration } from '@warp-drive/core-types/request';
import type {
CollectionResourceRelationship,
InnerRelationshipDocument,
@@ -471,22 +472,12 @@ export class LegacySupport {
assert(`Expected collection to be an array`, !identifiers || Array.isArray(identifiers));
assert(`Expected stable identifiers`, !identifiers || identifiers.every(isStableIdentifier));
- const req = field.options.linksMode
- ? {
- url: getRelatedLink(resource),
- op: 'findHasMany',
- method: 'GET' as const,
- records: identifiers || [],
- data: request,
- [EnableHydration]: false,
- }
- : {
- op: 'findHasMany',
- records: identifiers || [],
- data: request,
- cacheOptions: { [Symbol.for('wd:skip-cache')]: true },
- };
- return this.store.request(req) as unknown as Promise;
+ return this.store.request({
+ op: 'findHasMany',
+ records: identifiers || [],
+ data: request,
+ cacheOptions: { [Symbol.for('wd:skip-cache')]: true },
+ }) as unknown as Promise;
}
const preferLocalCache = hasReceivedData && !isEmpty;
@@ -563,28 +554,14 @@ export class LegacySupport {
// fetch via link
if (shouldFindViaLink) {
- const req = field.options.linksMode
- ? {
- url: getRelatedLink(resource),
- op: 'findBelongsTo',
- method: 'GET' as const,
- records: identifier ? [identifier] : [],
- data: request,
- [EnableHydration]: false,
- }
- : {
- op: 'findBelongsTo',
- records: identifier ? [identifier] : [],
- data: request,
- cacheOptions: { [Symbol.for('wd:skip-cache')]: true },
- };
- const future = this.store.request(req);
+ const future = this.store.request({
+ op: 'findBelongsTo',
+ records: identifier ? [identifier] : [],
+ data: request,
+ cacheOptions: { [Symbol.for('wd:skip-cache')]: true },
+ });
this._pending[key] = future
- .then((doc) =>
- field.options.linksMode
- ? (doc.content as unknown as Document).data!
- : doc.content
- )
+ .then((doc) => doc.content)
.finally(() => {
this._pending[key] = undefined;
});
@@ -659,13 +636,6 @@ export class LegacySupport {
}
}
-function getRelatedLink(resource: SingleResourceRelationship | CollectionResourceRelationship): string {
- const related = resource.links?.related;
- assert(`Expected a related link`, related);
-
- return typeof related === 'object' ? related.href : related;
-}
-
function handleCompletedRelationshipRequest(
recordExt: LegacySupport,
key: string,
@@ -750,6 +720,30 @@ function extractIdentifierFromRecord(record: PromiseProxyRecord | OpaqueRecordIn
return null;
}
+ if (DEPRECATE_PROMISE_PROXIES) {
+ if (isPromiseRecord(record)) {
+ const content = record.content;
+ assert(
+ 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.',
+ content !== undefined
+ );
+ deprecate(
+ `You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-proxies',
+ until: '5.0',
+ since: {
+ enabled: '4.7',
+ available: '4.7',
+ },
+ for: 'ember-data',
+ }
+ );
+ return content ? recordIdentifierFor(content) : null;
+ }
+ }
+
return recordIdentifierFor(record);
}
@@ -791,3 +785,7 @@ export function areAllInverseRecordsLoaded(store: Store, resource: InnerRelation
function isBelongsTo(relationship: GraphEdge): relationship is ResourceEdge {
return relationship.definition.kind === 'belongsTo';
}
+
+function isPromiseRecord(record: PromiseProxyRecord | OpaqueRecordInstance): record is PromiseProxyRecord {
+ return typeof record === 'object' && !!record && 'then' in record;
+}
diff --git a/packages/model/src/-private/many-array.ts b/packages/model/src/-private/many-array.ts
index c514e4860ce..01c73832264 100644
--- a/packages/model/src/-private/many-array.ts
+++ b/packages/model/src/-private/many-array.ts
@@ -17,7 +17,11 @@ import {
import type { BaseFinderOptions, ModelSchema } from '@ember-data/store/types';
import type { Signal } from '@ember-data/tracking/-private';
import { addToTransaction } from '@ember-data/tracking/-private';
-import { DEPRECATE_MANY_ARRAY_DUPLICATES } from '@warp-drive/build-config/deprecations';
+import {
+ DEPRECATE_MANY_ARRAY_DUPLICATES,
+ DEPRECATE_PROMISE_PROXIES,
+ DISABLE_6X_DEPRECATIONS,
+} from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
import type { Cache } from '@warp-drive/core-types/cache';
@@ -317,11 +321,12 @@ export class RelatedCollection extends LiveArray {
// dedupe
const current = new Set(adds);
const unique = Array.from(current);
+ const uniqueIdentifiers = Array.from(new Set(newValues));
const newArgs = ([start, deleteCount] as unknown[]).concat(unique);
const result = Reflect.apply(target[prop], receiver, newArgs) as OpaqueRecordInstance[];
- mutateReplaceRelatedRecords(this, extractIdentifiersFromRecords(unique), _SIGNAL);
+ mutateReplaceRelatedRecords(this, uniqueIdentifiers, _SIGNAL);
return result;
}
@@ -485,10 +490,39 @@ function extractIdentifiersFromRecords(records: OpaqueRecordInstance[]): StableR
}
function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | OpaqueRecordInstance) {
+ if (DEPRECATE_PROMISE_PROXIES) {
+ if (isPromiseRecord(recordOrPromiseRecord)) {
+ const content = recordOrPromiseRecord.content;
+ assert(
+ 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo relationship.',
+ content !== undefined && content !== null
+ );
+ deprecate(
+ `You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-proxies',
+ until: '5.0',
+ since: {
+ enabled: '4.7',
+ available: '4.7',
+ },
+ for: 'ember-data',
+ }
+ );
+ assertRecordPassedToHasMany(content);
+ return recordIdentifierFor(content);
+ }
+ }
+
assertRecordPassedToHasMany(recordOrPromiseRecord);
return recordIdentifierFor(recordOrPromiseRecord);
}
+function isPromiseRecord(record: PromiseProxyRecord | OpaqueRecordInstance): record is PromiseProxyRecord {
+ return Boolean(typeof record === 'object' && record && 'then' in record);
+}
+
function assertNoDuplicates(
collection: RelatedCollection,
target: StableRecordIdentifier[],
@@ -511,7 +545,7 @@ function assertNoDuplicates(
.map((r) => (isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid))
.sort((a, b) => a.localeCompare(b))
.join('\n\t- ')}`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-many-array-duplicates',
for: 'ember-data',
diff --git a/packages/model/src/-private/model-methods.ts b/packages/model/src/-private/model-methods.ts
index 8af6b3cba97..ed8b690f087 100644
--- a/packages/model/src/-private/model-methods.ts
+++ b/packages/model/src/-private/model-methods.ts
@@ -5,10 +5,12 @@ import { upgradeStore } from '@ember-data/legacy-compat/-private';
import type Store from '@ember-data/store';
import { recordIdentifierFor } from '@ember-data/store';
import { peekCache } from '@ember-data/store/-private';
+import { DEPRECATE_SAVE_PROMISE_ACCESS } from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
import type { ChangedAttributesHash } from '@warp-drive/core-types/cache';
import { RecordStore } from '@warp-drive/core-types/symbols';
+import { deprecatedPromiseObject } from './deprecated-promise-proxy';
import type { Errors } from './errors';
import { lookupLegacySupport } from './legacy-relationships-support';
import type RecordState from './record-state';
@@ -88,6 +90,10 @@ export function reload(this: T, options: Record(this: T, options?: Record(this: T, options?:
return Promise.resolve(this);
}
return this.save(options).then((_) => {
+ // run(() => {
this.unloadRecord();
+ // });
return this;
});
}
diff --git a/packages/model/src/-private/model.ts b/packages/model/src/-private/model.ts
index c003b428c6a..ea2eff41537 100644
--- a/packages/model/src/-private/model.ts
+++ b/packages/model/src/-private/model.ts
@@ -2,6 +2,7 @@
@module @ember-data/model
*/
+import { deprecate, warn } from '@ember/debug';
import EmberObject from '@ember/object';
import type { Snapshot } from '@ember-data/legacy-compat/-private';
@@ -11,6 +12,12 @@ import { recordIdentifierFor, storeFor } from '@ember-data/store';
import { coerceId } from '@ember-data/store/-private';
import { compat } from '@ember-data/tracking';
import { defineSignal } from '@ember-data/tracking/-private';
+import {
+ DEPRECATE_EARLY_STATIC,
+ DEPRECATE_MODEL_REOPEN,
+ DEPRECATE_NON_EXPLICIT_POLYMORPHISM,
+ DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
+} from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -38,6 +45,7 @@ import notifyChanges from './notify-changes';
import RecordState, { notifySignal, tagged } from './record-state';
import type BelongsToReference from './references/belongs-to';
import type HasManyReference from './references/has-many';
+import { relationshipFromMeta } from './relationship-meta';
import type {
_MaybeBelongsToFields,
isSubClass,
@@ -1168,22 +1176,49 @@ class Model extends EmberObject implements MinimalLegacyRecord {
@param {store} store an instance of Store
@return {Model} the type of the relationship, or undefined
*/
- static typeForRelationship(name: string, store: Store) {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ static typeForRelationship(name: string, store: Store): typeof Model | undefined {
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const relationship = this.relationshipsByName.get(name);
+ // @ts-expect-error
return relationship && store.modelFor(relationship.type);
}
@computeOnce
static get inverseMap(): Record {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
return Object.create(null) as Record;
}
@@ -1221,10 +1256,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
@return {Object} the inverse relationship, or null
*/
static inverseFor(name: string, store: Store): LegacyRelationshipSchema | null {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const inverseMap = this.inverseMap;
if (inverseMap[name]) {
return inverseMap[name];
@@ -1237,10 +1285,27 @@ class Model extends EmberObject implements MinimalLegacyRecord {
//Calculate the inverse, ignoring the cache
static _findInverseFor(name: string, store: Store): LegacyRelationshipSchema | null {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
+
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ return legacyFindInverseFor(this, name, store);
+ }
const relationship = this.relationshipsByName.get(name)!;
assert(`No relationship named '${name}' on '${this.modelName}' exists.`, relationship);
@@ -1322,10 +1387,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
@computeOnce
static get relationships(): Map {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const map = new Map();
const relationshipsByName = this.relationshipsByName;
@@ -1380,10 +1458,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
*/
@computeOnce
static get relationshipNames() {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const names: { hasMany: string[]; belongsTo: string[] } = {
hasMany: [],
belongsTo: [],
@@ -1433,10 +1524,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
*/
@computeOnce
static get relatedTypes(): string[] {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const types: string[] = [];
@@ -1496,10 +1600,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
*/
@computeOnce
static get relationshipsByName(): Map {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const map = new Map();
const rels = this.relationshipsObject;
const relationships = Object.keys(rels);
@@ -1516,10 +1633,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
@computeOnce
static get relationshipsObject(): Record {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const relationships = Object.create(null) as Record;
const modelName = this.modelName;
@@ -1530,7 +1660,10 @@ class Model extends EmberObject implements MinimalLegacyRecord {
// TODO deprecate key being here
(meta as unknown as { key: string }).key = name;
meta.name = name;
- relationships[name] = meta;
+ const parentModelName = meta.options?.as ?? modelName;
+ relationships[name] = DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE
+ ? relationshipFromMeta(meta, parentModelName)
+ : meta;
assert(`Expected options in meta`, meta.options && typeof meta.options === 'object');
assert(
@@ -1584,10 +1717,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
*/
@computeOnce
static get fields(): Map {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const map = new Map();
this.eachComputedProperty((name, meta) => {
@@ -1620,10 +1766,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
) => void,
binding?: T
): void {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
this.relationshipsByName.forEach((relationship, name) => {
callback.call(binding, name as MaybeRelationshipFields, relationship);
@@ -1643,10 +1802,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
@param {any} binding the value to which the callback's `this` should be bound
*/
static eachRelatedType(callback: (this: T | undefined, type: string) => void, binding?: T) {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const relationshipTypes = this.relatedTypes;
@@ -1666,10 +1838,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
knownSide: LegacyRelationshipSchema,
store: Store
): 'oneToOne' | 'oneToMany' | 'manyToOne' | 'manyToMany' | 'oneToNone' | 'manyToNone' {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const knownKey = knownSide.name;
const knownKind = knownSide.kind;
@@ -1730,10 +1915,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
*/
@computeOnce
static get attributes(): Map {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const map = new Map();
@@ -1795,10 +1993,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
*/
@computeOnce
static get transformedAttributes() {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
const map = new Map();
@@ -1859,10 +2070,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
callback: (this: T | undefined, key: MaybeAttrFields, attribute: LegacyAttributeField) => void,
binding?: T
): void {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
this.attributes.forEach((meta, name) => {
callback.call(binding, name as MaybeAttrFields, meta);
@@ -1918,10 +2142,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
callback: (this: T | undefined, key: Exclude, type: string) => void,
binding?: T
): void {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
this.transformedAttributes.forEach((type: string, name) => {
callback.call(binding, name as Exclude, type);
@@ -1936,10 +2173,23 @@ class Model extends EmberObject implements MinimalLegacyRecord {
@static
*/
static toString() {
- assert(
- `Accessing schema information on Models without looking up the model via the store is disallowed.`,
- this.modelName
- );
+ if (DEPRECATE_EARLY_STATIC) {
+ deprecate(
+ `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`,
+ Boolean(this.modelName),
+ {
+ id: 'ember-data:deprecate-early-static',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ } else {
+ assert(
+ `Accessing schema information on Models without looking up the model via the store is disallowed.`,
+ this.modelName
+ );
+ }
return `model:${this.modelName}`;
}
@@ -2016,8 +2266,40 @@ if (DEBUG) {
}
};
- delete (Model as unknown as { reopen: unknown }).reopen;
- delete (Model as unknown as { reopenClass: unknown }).reopenClass;
+ if (DEPRECATE_MODEL_REOPEN) {
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ const originalReopen = Model.reopen;
+ const originalReopenClass = Model.reopenClass;
+
+ // @ts-expect-error Intentional override
+ Model.reopen = function deprecatedReopen() {
+ deprecate(`Model.reopen is deprecated. Use Foo extends Model to extend your class instead.`, false, {
+ id: 'ember-data:deprecate-model-reopen',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ });
+ return originalReopen.call(this, ...arguments);
+ };
+
+ // @ts-expect-error Intentional override
+ Model.reopenClass = function deprecatedReopenClass() {
+ deprecate(
+ `Model.reopenClass is deprecated. Use Foo extends Model to add static methods and properties to your class instead.`,
+ false,
+ {
+ id: 'ember-data:deprecate-model-reopenclass',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ return originalReopenClass.call(this, ...arguments);
+ };
+ } else {
+ delete (Model as unknown as { reopen: unknown }).reopen;
+ delete (Model as unknown as { reopenClass: unknown }).reopenClass;
+ }
}
export { Model };
@@ -2030,3 +2312,218 @@ function isRelationshipSchema(meta: unknown): meta is LegacyRelationshipSchema {
function isAttributeSchema(meta: unknown): meta is LegacyAttributeField {
return typeof meta === 'object' && meta !== null && 'kind' in meta && meta.kind === 'attribute';
}
+
+function findPossibleInverses(
+ Klass: typeof Model,
+ inverseType: typeof Model,
+ name: string,
+ relationshipsSoFar?: LegacyRelationshipSchema[]
+) {
+ const possibleRelationships = relationshipsSoFar || [];
+
+ const relationshipMap = inverseType.relationships;
+ if (!relationshipMap) {
+ return possibleRelationships;
+ }
+
+ const relationshipsForType = relationshipMap.get(Klass.modelName);
+ const relationships = Array.isArray(relationshipsForType)
+ ? relationshipsForType.filter((relationship) => {
+ const optionsForRelationship = relationship.options;
+
+ if (!optionsForRelationship.inverse && optionsForRelationship.inverse !== null) {
+ return true;
+ }
+
+ return name === optionsForRelationship.inverse;
+ })
+ : null;
+
+ if (relationships) {
+ // eslint-disable-next-line prefer-spread
+ possibleRelationships.push.apply(possibleRelationships, relationships);
+ }
+
+ //Recurse to support polymorphism
+ if (Klass.superclass) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ findPossibleInverses(Klass.superclass, inverseType, name, possibleRelationships);
+ }
+
+ return possibleRelationships;
+}
+
+function legacyFindInverseFor(Klass: typeof Model, name: string, store: Store) {
+ const relationship = Klass.relationshipsByName.get(name);
+ assert(`No relationship named '${name}' on '${Klass.modelName}' exists.`, relationship);
+
+ const { options } = relationship;
+ const isPolymorphic = options.polymorphic;
+
+ //If inverse is manually specified to be null, like `comments: hasMany('message', { inverse: null })`
+ const isExplicitInverseNull = options.inverse === null;
+ const isAbstractType = !isExplicitInverseNull && isPolymorphic && !store.schema.hasResource(relationship);
+
+ if (isExplicitInverseNull || isAbstractType) {
+ assert(
+ `No schema for the abstract type '${relationship.type}' for the polymorphic relationship '${name}' on '${Klass.modelName}' was provided by the SchemaDefinitionService.`,
+ !isPolymorphic || isExplicitInverseNull
+ );
+ return null;
+ }
+
+ let fieldOnInverse: string | null | undefined;
+ let inverseKind: 'belongsTo' | 'hasMany';
+ let inverseRelationship: LegacyRelationshipSchema | undefined;
+ let inverseOptions: LegacyRelationshipSchema['options'] | undefined;
+ const inverseSchema = Klass.typeForRelationship(name, store);
+ assert(`No model was found for '${relationship.type}'`, inverseSchema);
+
+ // if the type does not exist and we are not polymorphic
+ //If inverse is specified manually, return the inverse
+ if (options.inverse !== undefined) {
+ fieldOnInverse = options.inverse!;
+ inverseRelationship = inverseSchema?.relationshipsByName.get(fieldOnInverse);
+
+ assert(
+ `We found no field named '${fieldOnInverse}' on the schema for '${inverseSchema.modelName}' to be the inverse of the '${name}' relationship on '${Klass.modelName}'. This is most likely due to a missing field on your model definition.`,
+ inverseRelationship
+ );
+
+ // TODO probably just return the whole inverse here
+
+ inverseKind = inverseRelationship.kind;
+
+ inverseOptions = inverseRelationship.options;
+ } else {
+ //No inverse was specified manually, we need to use a heuristic to guess one
+ const parentModelName = relationship.options?.as ?? Klass.modelName;
+ if (relationship.type === parentModelName) {
+ warn(
+ `Detected a reflexive relationship named '${name}' on the schema for '${relationship.type}' without an inverse option. Look at https://guides.emberjs.com/current/models/relationships/#toc_reflexive-relations for how to explicitly specify inverses.`,
+ false,
+ {
+ id: 'ds.model.reflexive-relationship-without-inverse',
+ }
+ );
+ }
+
+ let possibleRelationships = findPossibleInverses(Klass, inverseSchema, name);
+
+ if (possibleRelationships.length === 0) {
+ return null;
+ }
+
+ if (DEBUG) {
+ const filteredRelationships = possibleRelationships.filter((possibleRelationship) => {
+ const optionsForRelationship = possibleRelationship.options;
+ return name === optionsForRelationship.inverse;
+ });
+
+ assert(
+ "You defined the '" +
+ name +
+ "' relationship on " +
+ String(Klass) +
+ ', but you defined the inverse relationships of type ' +
+ inverseSchema.toString() +
+ ' multiple times. Look at https://guides.emberjs.com/current/models/relationships/#toc_explicit-inverses for how to explicitly specify inverses',
+ filteredRelationships.length < 2
+ );
+ }
+
+ const explicitRelationship = possibleRelationships.find((rel) => rel.options?.inverse === name);
+ if (explicitRelationship) {
+ possibleRelationships = [explicitRelationship];
+ }
+
+ assert(
+ "You defined the '" +
+ name +
+ "' relationship on " +
+ String(Klass) +
+ ', but multiple possible inverse relationships of type ' +
+ String(Klass) +
+ ' were found on ' +
+ String(inverseSchema) +
+ '. Look at https://guides.emberjs.com/current/models/relationships/#toc_explicit-inverses for how to explicitly specify inverses',
+ possibleRelationships.length === 1
+ );
+
+ fieldOnInverse = possibleRelationships[0].name;
+ inverseKind = possibleRelationships[0].kind;
+ inverseOptions = possibleRelationships[0].options;
+ }
+
+ assert(`inverseOptions should be set by now`, inverseOptions);
+
+ // ensure inverse is properly configured
+ if (DEBUG) {
+ if (isPolymorphic) {
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ if (!inverseOptions.as) {
+ deprecate(
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
+ false,
+ {
+ id: 'ember-data:non-explicit-relationships',
+ since: { enabled: '4.7', available: '4.7' },
+ until: '5.0',
+ for: 'ember-data',
+ }
+ );
+ }
+ } else {
+ assert(
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
+ inverseOptions.as
+ );
+ assert(
+ `options.as should match the expected type of the polymorphic relationship. Expected field '${fieldOnInverse}' on type '${inverseSchema.modelName}' to specify '${relationship.type}' but found '${inverseOptions.as}'`,
+ !!inverseOptions.as && relationship.type === inverseOptions.as
+ );
+ }
+ }
+ }
+
+ // ensure we are properly configured
+ if (DEBUG) {
+ if (inverseOptions.polymorphic) {
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
+ if (!options.as) {
+ deprecate(
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${Klass.modelName}' is misconfigured.`,
+ false,
+ {
+ id: 'ember-data:non-explicit-relationships',
+ since: { enabled: '4.7', available: '4.7' },
+ until: '5.0',
+ for: 'ember-data',
+ }
+ );
+ }
+ } else {
+ assert(
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${Klass.modelName}' is misconfigured.`,
+ options.as
+ );
+ assert(
+ `options.as should match the expected type of the polymorphic relationship. Expected field '${name}' on type '${Klass.modelName}' to specify '${inverseRelationship!.type}' but found '${options.as}'`,
+ !!options.as && inverseRelationship!.type === options.as
+ );
+ }
+ }
+ }
+
+ assert(
+ `The ${inverseSchema.modelName}:${fieldOnInverse} relationship declares 'inverse: null', but it was resolved as the inverse for ${Klass.modelName}:${name}.`,
+ inverseOptions.inverse !== null
+ );
+
+ return {
+ type: inverseSchema.modelName,
+ name: fieldOnInverse,
+ kind: inverseKind,
+ options: inverseOptions,
+ };
+}
diff --git a/packages/model/src/-private/notify-changes.ts b/packages/model/src/-private/notify-changes.ts
index ee7c85fefe3..c97c80b4644 100644
--- a/packages/model/src/-private/notify-changes.ts
+++ b/packages/model/src/-private/notify-changes.ts
@@ -16,33 +16,26 @@ export default function notifyChanges(
record: Model,
store: Store
) {
- switch (value) {
- case 'added':
- case 'attributes':
- if (key) {
- notifyAttribute(store, identifier, key, record);
- } else {
- record.eachAttribute((name) => {
- notifyAttribute(store, identifier, name, record);
- });
- }
- break;
-
- case 'relationships':
- if (key) {
- const meta = (record.constructor as typeof Model).relationshipsByName.get(key);
- assert(`Expected to find a relationship for ${key} on ${identifier.type}`, meta);
- notifyRelationship(identifier, key, record, meta);
- } else {
- record.eachRelationship((name, meta) => {
- notifyRelationship(identifier, name, record, meta);
- });
- }
- break;
-
- case 'identity':
- record.notifyPropertyChange('id');
- break;
+ if (value === 'attributes') {
+ if (key) {
+ notifyAttribute(store, identifier, key, record);
+ } else {
+ record.eachAttribute((name) => {
+ notifyAttribute(store, identifier, name, record);
+ });
+ }
+ } else if (value === 'relationships') {
+ if (key) {
+ const meta = (record.constructor as typeof Model).relationshipsByName.get(key);
+ assert(`Expected to find a relationship for ${key} on ${identifier.type}`, meta);
+ notifyRelationship(identifier, key, record, meta);
+ } else {
+ record.eachRelationship((name, meta) => {
+ notifyRelationship(identifier, name, record, meta);
+ });
+ }
+ } else if (value === 'identity') {
+ record.notifyPropertyChange('id');
}
}
diff --git a/packages/model/src/-private/promise-many-array.ts b/packages/model/src/-private/promise-many-array.ts
index 8aa0873ac0b..7fe925d9023 100644
--- a/packages/model/src/-private/promise-many-array.ts
+++ b/packages/model/src/-private/promise-many-array.ts
@@ -1,7 +1,18 @@
+import ArrayMixin, { NativeArray } from '@ember/array';
+import type ArrayProxy from '@ember/array/proxy';
+import { deprecate } from '@ember/debug';
+import Ember from 'ember';
+
+import type { CreateRecordProperties } from '@ember-data/store/-private';
import type { BaseFinderOptions } from '@ember-data/store/types';
import { compat } from '@ember-data/tracking';
import { defineSignal } from '@ember-data/tracking/-private';
-import { DEPRECATE_COMPUTED_CHAINS } from '@warp-drive/build-config/deprecations';
+import {
+ DEPRECATE_A_USAGE,
+ DEPRECATE_COMPUTED_CHAINS,
+ DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS,
+} from '@warp-drive/build-config/deprecations';
+import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { RelatedCollection as ManyArray } from './many-array';
@@ -31,16 +42,46 @@ export interface HasManyProxyCreateArgs {
@class PromiseManyArray
@public
*/
+export interface PromiseManyArray extends Omit, 'destroy' | 'forEach'> {
+ createRecord(hash: CreateRecordProperties): T;
+ reload(options: Omit): PromiseManyArray;
+}
export class PromiseManyArray {
declare promise: Promise> | null;
declare isDestroyed: boolean;
+ // @deprecated (isDestroyed is not deprecated)
+ declare isDestroying: boolean;
declare content: ManyArray | null;
constructor(promise: Promise>, content?: ManyArray) {
this._update(promise, content);
this.isDestroyed = false;
+ this.isDestroying = false;
+
+ if (DEPRECATE_A_USAGE) {
+ const meta = Ember.meta(this);
+ meta.hasMixin = (mixin: object) => {
+ deprecate(`Do not use A() on an EmberData PromiseManyArray`, false, {
+ id: 'ember-data:no-a-with-array-like',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ });
+ if (mixin === NativeArray || mixin === ArrayMixin) {
+ return true;
+ }
+ return false;
+ };
+ } else if (DEBUG) {
+ const meta = Ember.meta(this);
+ meta.hasMixin = (mixin: object) => {
+ assert(`Do not use A() on an EmberData PromiseManyArray`);
+ };
+ }
}
+ //---- Methods/Properties on ArrayProxy that we will keep as our API
+
/**
* Retrieve the length of the content
* @property length
@@ -156,6 +197,7 @@ export class PromiseManyArray {
//---- Methods on EmberObject that we should keep
destroy() {
+ this.isDestroying = true;
this.isDestroyed = true;
this.content = null;
this.promise = null;
@@ -217,7 +259,7 @@ if (DEPRECATE_COMPUTED_CHAINS) {
return this.content?.length && this.content;
},
};
- compat(desc);
+ compat(PromiseManyArray.prototype, '[]', desc);
// ember-source < 3.23 (e.g. 3.20 lts)
// requires that the tag `'[]'` be notified
@@ -227,6 +269,62 @@ if (DEPRECATE_COMPUTED_CHAINS) {
Object.defineProperty(PromiseManyArray.prototype, '[]', desc);
}
+if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) {
+ PromiseManyArray.prototype.createRecord = function createRecord(
+ this: PromiseManyArray,
+ hash: CreateRecordProperties
+ ) {
+ deprecate(
+ `The createRecord method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-many-array-behaviors',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ }
+ );
+ assert('You are trying to createRecord on an async manyArray before it has been created', this.content);
+ return this.content.createRecord(hash);
+ };
+
+ Object.defineProperty(PromiseManyArray.prototype, 'firstObject', {
+ get() {
+ deprecate(
+ `The firstObject property on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-many-array-behaviors',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ }
+ );
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
+ return this.content ? this.content.firstObject : undefined;
+ },
+ });
+
+ Object.defineProperty(PromiseManyArray.prototype, 'lastObject', {
+ get() {
+ deprecate(
+ `The lastObject property on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-many-array-behaviors',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ }
+ );
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
+ return this.content ? this.content.lastObject : undefined;
+ },
+ });
+}
+
function tapPromise(proxy: PromiseManyArray, promise: Promise>) {
proxy.isPending = true;
proxy.isSettled = false;
@@ -249,3 +347,105 @@ function tapPromise(proxy: PromiseManyArray, promise: Promise
}
);
}
+
+if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) {
+ const EmberObjectMethods = [
+ 'addObserver',
+ 'cacheFor',
+ 'decrementProperty',
+ 'get',
+ 'getProperties',
+ 'incrementProperty',
+ 'notifyPropertyChange',
+ 'removeObserver',
+ 'set',
+ 'setProperties',
+ 'toggleProperty',
+ ];
+ EmberObjectMethods.forEach((method) => {
+ PromiseManyArray.prototype[method] = function delegatedMethod(...args) {
+ deprecate(
+ `The ${method} method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-many-array-behaviors',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ }
+ );
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
+ return Ember[method](this, ...args);
+ };
+ });
+
+ const InheritedProxyMethods = [
+ 'addArrayObserver',
+ 'addObject',
+ 'addObjects',
+ 'any',
+ 'arrayContentDidChange',
+ 'arrayContentWillChange',
+ 'clear',
+ 'compact',
+ 'every',
+ 'filter',
+ 'filterBy',
+ 'find',
+ 'findBy',
+ 'getEach',
+ 'includes',
+ 'indexOf',
+ 'insertAt',
+ 'invoke',
+ 'isAny',
+ 'isEvery',
+ 'lastIndexOf',
+ 'map',
+ 'mapBy',
+ // TODO update RFC to note objectAt was deprecated (forEach was left for iteration)
+ 'objectAt',
+ 'objectsAt',
+ 'popObject',
+ 'pushObject',
+ 'pushObjects',
+ 'reduce',
+ 'reject',
+ 'rejectBy',
+ 'removeArrayObserver',
+ 'removeAt',
+ 'removeObject',
+ 'removeObjects',
+ 'replace',
+ 'reverseObjects',
+ 'setEach',
+ 'setObjects',
+ 'shiftObject',
+ 'slice',
+ 'sortBy',
+ 'toArray',
+ 'uniq',
+ 'uniqBy',
+ 'unshiftObject',
+ 'unshiftObjects',
+ 'without',
+ ];
+ InheritedProxyMethods.forEach((method) => {
+ PromiseManyArray.prototype[method] = function proxiedMethod(...args) {
+ deprecate(
+ `The ${method} method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-many-array-behaviors',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ }
+ );
+ assert(`Cannot call ${method} before content is assigned.`, this.content);
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
+ return this.content[method](...args);
+ };
+ });
+}
diff --git a/packages/model/src/-private/references/belongs-to.ts b/packages/model/src/-private/references/belongs-to.ts
index f5363c091f9..c3b227115e5 100644
--- a/packages/model/src/-private/references/belongs-to.ts
+++ b/packages/model/src/-private/references/belongs-to.ts
@@ -1,8 +1,11 @@
+import { deprecate } from '@ember/debug';
+
import type { Graph, ResourceEdge } from '@ember-data/graph/-private';
import type Store from '@ember-data/store';
import type { NotificationType } from '@ember-data/store';
import { cached, compat } from '@ember-data/tracking';
import { defineSignal } from '@ember-data/tracking/-private';
+import { DEPRECATE_PROMISE_PROXIES } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
import type { StableExistingRecordIdentifier } from '@warp-drive/core-types/identifier';
@@ -477,7 +480,32 @@ export default class BelongsToReference<
@param {Boolean} [skipFetch] if `true`, do not attempt to fetch unloaded records
@return {Promise}
*/
- async push(doc: SingleResourceDocument, skipFetch?: boolean): Promise {
+ async push(
+ maybeDoc: SingleResourceDocument | Promise,
+ skipFetch?: boolean
+ ): Promise {
+ let doc: SingleResourceDocument = maybeDoc as SingleResourceDocument;
+ if (DEPRECATE_PROMISE_PROXIES) {
+ if ((maybeDoc as { then: unknown }).then) {
+ doc = await maybeDoc;
+ if (doc !== maybeDoc) {
+ deprecate(
+ `You passed in a Promise to a Reference API that now expects a resolved value. await the value before setting it.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-proxies',
+ until: '5.0',
+ since: {
+ enabled: '4.7',
+ available: '4.7',
+ },
+ for: 'ember-data',
+ }
+ );
+ }
+ }
+ }
+
const { store } = this;
const isResourceData = doc.data && isMaybeResource(doc.data);
const added = isResourceData
diff --git a/packages/model/src/-private/references/has-many.ts b/packages/model/src/-private/references/has-many.ts
index 6a44d3ec4d7..b41ecfed519 100644
--- a/packages/model/src/-private/references/has-many.ts
+++ b/packages/model/src/-private/references/has-many.ts
@@ -1,9 +1,12 @@
+import { deprecate } from '@ember/debug';
+
import type { CollectionEdge, Graph } from '@ember-data/graph/-private';
import type Store from '@ember-data/store';
import type { NotificationType } from '@ember-data/store';
import type { BaseFinderOptions } from '@ember-data/store/types';
import { cached, compat } from '@ember-data/tracking';
import { defineSignal } from '@ember-data/tracking/-private';
+import { DEPRECATE_PROMISE_PROXIES } from '@warp-drive/build-config/deprecations';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
@@ -149,9 +152,8 @@ export default class HasManyReference<
@cached
@compat
get identifiers(): StableRecordIdentifier>[] {
- ensureRefCanSubscribe(this);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
- this._ref;
+ this._ref; // consume the tracked prop
const resource = this._resource();
@@ -495,9 +497,31 @@ export default class HasManyReference<
@return {Promise}
*/
async push(
- doc: ExistingResourceObject[] | CollectionResourceDocument,
+ maybeDoc: ExistingResourceObject[] | CollectionResourceDocument,
skipFetch?: boolean
): Promise | void> {
+ let doc = maybeDoc;
+ if (DEPRECATE_PROMISE_PROXIES) {
+ if ((maybeDoc as unknown as { then: unknown }).then) {
+ doc = await (maybeDoc as unknown as Promise);
+ if (doc !== maybeDoc) {
+ deprecate(
+ `You passed in a Promise to a Reference API that now expects a resolved value. await the value before setting it.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-proxies',
+ until: '5.0',
+ since: {
+ enabled: '4.7',
+ available: '4.7',
+ },
+ for: 'ember-data',
+ }
+ );
+ }
+ }
+ }
+
const { store } = this;
const dataDoc = Array.isArray(doc) ? { data: doc } : doc;
const isResourceData = Array.isArray(dataDoc.data) && dataDoc.data.length > 0 && isMaybeResource(dataDoc.data[0]);
@@ -605,7 +629,11 @@ export default class HasManyReference<
this.___identifier
)!;
- if (!ensureRefCanSubscribe(this)) {
+ const loaded = this._isLoaded();
+
+ if (!loaded) {
+ // subscribe to changes
+ // for when we are not loaded yet
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
this._ref;
return null;
@@ -754,22 +782,3 @@ export function isMaybeResource(object: ExistingResourceObject | ResourceIdentif
const keys = Object.keys(object).filter((k) => k !== 'id' && k !== 'type' && k !== 'lid');
return keys.length > 0;
}
-
-function ensureRefCanSubscribe(rel: HasManyReference) {
- const loaded = rel._isLoaded();
-
- if (!loaded) {
- // subscribe to changes
- // for when we are not loaded yet
- //
- // because the graph optimizes the case where a relationship has never been subscribed,
- // we force accessed to be true here. When we make the graph public we should create a
- // subscribe/unsubscribe API
- const edge = rel.graph.get(rel.___identifier, rel.key);
- assert(`Expected a hasMany relationship for ${rel.___identifier.type}:${rel.key}`, 'accessed' in edge);
- edge.accessed = true;
-
- return false;
- }
- return true;
-}
diff --git a/packages/model/src/-private/relationship-meta.ts b/packages/model/src/-private/relationship-meta.ts
new file mode 100644
index 00000000000..e03a181c389
--- /dev/null
+++ b/packages/model/src/-private/relationship-meta.ts
@@ -0,0 +1,93 @@
+import { dasherize, singularize } from '@ember-data/request-utils/string';
+import type Store from '@ember-data/store';
+import { DEBUG } from '@warp-drive/build-config/env';
+import type { LegacyRelationshipSchema } from '@warp-drive/core-types/schema/fields';
+
+import type { Model } from './model';
+
+function typeForRelationshipMeta(meta: LegacyRelationshipSchema): string {
+ let modelName = dasherize(meta.type || meta.name);
+
+ if (meta.kind === 'hasMany') {
+ modelName = singularize(modelName);
+ }
+
+ return modelName;
+}
+
+function shouldFindInverse(relationshipMeta: LegacyRelationshipSchema): boolean {
+ const options = relationshipMeta.options;
+ return !(options && options.inverse === null);
+}
+
+class RelationshipDefinition {
+ declare _type: string;
+ declare __inverseKey: string | null;
+ declare __hasCalculatedInverse: boolean;
+ declare parentModelName: string;
+ declare inverseIsAsync: string | null;
+ declare meta: LegacyRelationshipSchema;
+
+ constructor(meta: LegacyRelationshipSchema, parentModelName: string) {
+ this._type = '';
+ this.__inverseKey = '';
+ this.__hasCalculatedInverse = false;
+ this.parentModelName = parentModelName;
+ this.meta = meta;
+ }
+
+ get kind(): 'belongsTo' | 'hasMany' {
+ return this.meta.kind;
+ }
+ get type(): string {
+ if (this._type) {
+ return this._type;
+ }
+ this._type = typeForRelationshipMeta(this.meta);
+ return this._type;
+ }
+ get options() {
+ return this.meta.options;
+ }
+ get name(): string {
+ return this.meta.name;
+ }
+
+ _inverseKey(store: Store, modelClass: typeof Model): string | null {
+ if (this.__hasCalculatedInverse === false) {
+ this._calculateInverse(store, modelClass);
+ }
+ return this.__inverseKey;
+ }
+
+ _calculateInverse(store: Store, modelClass: typeof Model): void {
+ this.__hasCalculatedInverse = true;
+ let inverseKey: string | null = null;
+ let inverse: LegacyRelationshipSchema | null = null;
+
+ if (shouldFindInverse(this.meta)) {
+ inverse = modelClass.inverseFor(this.name, store);
+ }
+ // TODO make this error again for the non-polymorphic case
+ if (DEBUG) {
+ if (!this.options.polymorphic) {
+ modelClass.typeForRelationship(this.name, store);
+ }
+ }
+
+ if (inverse) {
+ inverseKey = inverse.name;
+ } else {
+ inverseKey = null;
+ }
+ this.__inverseKey = inverseKey;
+ }
+}
+export type { RelationshipDefinition };
+
+export function relationshipFromMeta(
+ meta: LegacyRelationshipSchema,
+ parentModelName: string
+): LegacyRelationshipSchema {
+ return new RelationshipDefinition(meta, parentModelName) as unknown as LegacyRelationshipSchema;
+}
diff --git a/packages/model/src/-private/schema-provider.ts b/packages/model/src/-private/schema-provider.ts
index 88fe9563043..d906b9b29f2 100644
--- a/packages/model/src/-private/schema-provider.ts
+++ b/packages/model/src/-private/schema-provider.ts
@@ -3,7 +3,11 @@ import { deprecate } from '@ember/debug';
import type Store from '@ember-data/store';
import type { SchemaService } from '@ember-data/store/types';
-import { ENABLE_LEGACY_SCHEMA_SERVICE } from '@warp-drive/build-config/deprecations';
+import {
+ DEPRECATE_STRING_ARG_SCHEMAS,
+ DISABLE_6X_DEPRECATIONS,
+ ENABLE_LEGACY_SCHEMA_SERVICE,
+} from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
import type { RecordIdentifier, StableRecordIdentifier } from '@warp-drive/core-types/identifier';
import type { ObjectValue } from '@warp-drive/core-types/json/raw';
@@ -164,31 +168,60 @@ export class ModelSchemaProvider implements SchemaService {
if (ENABLE_LEGACY_SCHEMA_SERVICE) {
ModelSchemaProvider.prototype.doesTypeExist = function (type: string): boolean {
- deprecate(`Use \`schema.hasResource({ type })\` instead of \`schema.doesTypeExist(type)\``, false, {
- id: 'ember-data:schema-service-updates',
- until: '6.0',
- for: 'ember-data',
- since: {
- available: '4.13',
- enabled: '5.4',
- },
- });
+ deprecate(
+ `Use \`schema.hasResource({ type })\` instead of \`schema.doesTypeExist(type)\``,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:schema-service-updates',
+ until: '6.0',
+ for: 'ember-data',
+ since: {
+ available: '4.13',
+ enabled: '5.4',
+ },
+ }
+ );
return this.hasResource({ type });
};
ModelSchemaProvider.prototype.attributesDefinitionFor = function (
resource: RecordIdentifier | { type: string }
): AttributesSchema {
- deprecate(`Use \`schema.fields({ type })\` instead of \`schema.attributesDefinitionFor({ type })\``, false, {
- id: 'ember-data:schema-service-updates',
- until: '6.0',
- for: 'ember-data',
- since: {
- available: '4.13',
- enabled: '5.4',
- },
- });
- const type = normalizeModelName(resource.type);
+ let rawType: string;
+ if (DEPRECATE_STRING_ARG_SCHEMAS) {
+ if (typeof resource === 'string') {
+ deprecate(
+ `relationshipsDefinitionFor expects either a record identifier or an argument of shape { type: string }, received a string.`,
+ false,
+ {
+ id: 'ember-data:deprecate-string-arg-schemas',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.5', available: '4.5' },
+ }
+ );
+ rawType = resource;
+ } else {
+ rawType = resource.type;
+ }
+ } else {
+ rawType = resource.type;
+ }
+
+ deprecate(
+ `Use \`schema.fields({ type })\` instead of \`schema.attributesDefinitionFor({ type })\``,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:schema-service-updates',
+ until: '6.0',
+ for: 'ember-data',
+ since: {
+ available: '4.13',
+ enabled: '5.4',
+ },
+ }
+ );
+ const type = normalizeModelName(rawType);
if (!this._schemas.has(type)) {
this._loadModelSchema(type);
@@ -200,16 +233,41 @@ if (ENABLE_LEGACY_SCHEMA_SERVICE) {
ModelSchemaProvider.prototype.relationshipsDefinitionFor = function (
resource: RecordIdentifier | { type: string }
): RelationshipsSchema {
- deprecate(`Use \`schema.fields({ type })\` instead of \`schema.relationshipsDefinitionFor({ type })\``, false, {
- id: 'ember-data:schema-service-updates',
- until: '6.0',
- for: 'ember-data',
- since: {
- available: '4.13',
- enabled: '5.4',
- },
- });
- const type = normalizeModelName(resource.type);
+ let rawType: string;
+ if (DEPRECATE_STRING_ARG_SCHEMAS) {
+ if (typeof resource === 'string') {
+ deprecate(
+ `relationshipsDefinitionFor expects either a record identifier or an argument of shape { type: string }, received a string.`,
+ false,
+ {
+ id: 'ember-data:deprecate-string-arg-schemas',
+ for: 'ember-data',
+ until: '5.0',
+ since: { enabled: '4.5', available: '4.5' },
+ }
+ );
+ rawType = resource;
+ } else {
+ rawType = resource.type;
+ }
+ } else {
+ rawType = resource.type;
+ }
+
+ deprecate(
+ `Use \`schema.fields({ type })\` instead of \`schema.relationshipsDefinitionFor({ type })\``,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:schema-service-updates',
+ until: '6.0',
+ for: 'ember-data',
+ since: {
+ available: '4.13',
+ enabled: '5.4',
+ },
+ }
+ );
+ const type = normalizeModelName(rawType);
if (!this._schemas.has(type)) {
this._loadModelSchema(type);
diff --git a/packages/model/src/-private/util.ts b/packages/model/src/-private/util.ts
index b6f924f360d..b9f0380dde3 100644
--- a/packages/model/src/-private/util.ts
+++ b/packages/model/src/-private/util.ts
@@ -1,7 +1,7 @@
import { deprecate } from '@ember/debug';
import { dasherize } from '@ember-data/request-utils/string';
-import { DEPRECATE_NON_STRICT_TYPES } from '@warp-drive/build-config/deprecations';
+import { DEPRECATE_NON_STRICT_TYPES, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
export type DecoratorPropertyDescriptor = (PropertyDescriptor & { initializer?: () => unknown }) | undefined;
@@ -31,7 +31,7 @@ export function normalizeModelName(type: string): string {
deprecate(
`The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`,
- result === type,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : result === type,
{
id: 'ember-data:deprecate-non-strict-types',
until: '6.0',
diff --git a/packages/model/src/migration-support.ts b/packages/model/src/migration-support.ts
index e839365cc62..4ade9960611 100644
--- a/packages/model/src/migration-support.ts
+++ b/packages/model/src/migration-support.ts
@@ -6,7 +6,6 @@ import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
import { getOrSetGlobal } from '@warp-drive/core-types/-private';
import type { ObjectValue } from '@warp-drive/core-types/json/raw';
-import type { TypedRecordInstance } from '@warp-drive/core-types/record';
import type { Derivation, HashFn, Transformation } from '@warp-drive/core-types/schema/concepts';
import type {
ArrayField,
@@ -38,12 +37,6 @@ import {
import RecordState from './-private/record-state';
import { buildSchema } from './hooks';
-export type WithLegacyDerivations = T &
- MinimalLegacyRecord & {
- belongsTo: typeof belongsTo;
- hasMany: typeof hasMany;
- };
-
type AttributesSchema = ReturnType>;
type RelationshipsSchema = ReturnType>;
diff --git a/packages/model/vite.config.mjs b/packages/model/vite.config.mjs
index 0bd74020510..c06cb9e5c61 100644
--- a/packages/model/vite.config.mjs
+++ b/packages/model/vite.config.mjs
@@ -1,6 +1,7 @@
import { createConfig } from '@warp-drive/internal-config/vite/config.js';
export const externals = [
+ 'ember',
'@ember/service',
'@ember/debug',
'@ember/object/computed',
diff --git a/packages/request-utils/package.json b/packages/request-utils/package.json
index af46288c246..24349279d96 100644
--- a/packages/request-utils/package.json
+++ b/packages/request-utils/package.json
@@ -1,7 +1,7 @@
{
"name": "@ember-data/request-utils",
"description": "Request Building Utilities for use with EmberData",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": false,
"license": "MIT",
"author": "Chris Thoburn ",
@@ -66,7 +66,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"devDependencies": {
diff --git a/packages/request-utils/src/deprecation-support.ts b/packages/request-utils/src/deprecation-support.ts
index 15a4d9ab05c..d0229dc3906 100644
--- a/packages/request-utils/src/deprecation-support.ts
+++ b/packages/request-utils/src/deprecation-support.ts
@@ -2,7 +2,7 @@ import { deprecate } from '@ember/debug';
import { dependencySatisfies, importSync, macroCondition } from '@embroider/macros';
-import { DEPRECATE_EMBER_INFLECTOR } from '@warp-drive/build-config/deprecations';
+import { DEPRECATE_EMBER_INFLECTOR, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
import { defaultRules as WarpDriveDefaults } from './-private/string/inflections';
import { irregular, plural, singular, uncountable } from './string';
@@ -85,7 +85,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for pluralization.\nPlease \`import { plural } from '@ember-data/request-utils/string';\` instead to register a custom pluralization rule for use with EmberData.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
@@ -109,7 +109,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for singularization.\nPlease \`import { singular } from '@ember-data/request-utils/string';\` instead to register a custom singularization rule for use with EmberData.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
@@ -141,7 +141,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for irregular rules.\nPlease \`import { irregular } from '@ember-data/request-utils/string';\` instead to register a custom irregular rule for use with EmberData for '${actualSingle}' <=> '${plur}'.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
@@ -165,7 +165,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for uncountable rules.\nPlease \`import { uncountable } from '@ember-data/request-utils/string';\` instead to register a custom uncountable rule for '${word}' for use with EmberData.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
@@ -184,7 +184,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for pluralization.\nPlease \`import { plural } from '@ember-data/request-utils/string';\` instead to register a custom pluralization rule for use with EmberData.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
@@ -205,7 +205,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for singularization.\nPlease \`import { singular } from '@ember-data/request-utils/string';\` instead to register a custom singularization rule for use with EmberData.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
@@ -226,7 +226,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for irregular rules.\nPlease \`import { irregular } from '@ember-data/request-utils/string';\` instead to register a custom irregular rule for use with EmberData.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
@@ -247,7 +247,7 @@ if (DEPRECATE_EMBER_INFLECTOR) {
deprecate(
`WarpDrive/EmberData no longer uses ember-inflector for uncountable rules.\nPlease \`import { uncountable } from '@ember-data/request-utils/string';\` instead to register a custom uncountable rule for use with EmberData.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'warp-drive.ember-inflector',
until: '6.0.0',
diff --git a/packages/request-utils/src/index.ts b/packages/request-utils/src/index.ts
index 337195d6c4e..6a52199e6b0 100644
--- a/packages/request-utils/src/index.ts
+++ b/packages/request-utils/src/index.ts
@@ -1,5 +1,6 @@
import { deprecate } from '@ember/debug';
+import { DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
import type { Cache } from '@warp-drive/core-types/cache';
@@ -753,7 +754,7 @@ export class CachePolicy {
const _config = arguments.length === 1 ? config : (arguments[1] as unknown as PolicyConfig);
deprecate(
`Passing a Store to the CachePolicy is deprecated, please pass only a config instead.`,
- arguments.length === 1,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : arguments.length === 1,
{
id: 'ember-data:request-utils:lifetimes-service-store-arg',
since: {
@@ -934,7 +935,7 @@ export class LifetimesService extends CachePolicy {
constructor(config: PolicyConfig) {
deprecate(
`\`import { LifetimesService } from '@ember-data/request-utils';\` is deprecated, please use \`import { CachePolicy } from '@ember-data/request-utils';\` instead.`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-lifetimes-service-import',
since: {
diff --git a/packages/request/README.md b/packages/request/README.md
index 01fa9c1aca0..72d246df68c 100644
--- a/packages/request/README.md
+++ b/packages/request/README.md
@@ -63,8 +63,8 @@ import Fetch from '@ember-data/request/fetch';
import { apiUrl } from './config';
// ... create manager and add our Fetch handler
-const manager = new RequestManager()
- .use([Fetch]);
+const manager = new RequestManager();
+manager.use([Fetch]);
// ... execute a request
const response = await manager.request({
@@ -441,9 +441,13 @@ import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
class extends Store {
- requestManager = new RequestManager()
- .use([Fetch])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
@@ -459,16 +463,19 @@ Additional handlers or a service injection like the above would need to be done
consuming application in order to make broader use of `RequestManager`.
```ts
-import Store from 'ember-data/store';
-import { CacheHandler } from '@ember-data/store';
+import Store, { CacheHandler } from 'ember-data/store';
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
export default class extends Store {
- requestManager = new RequestManager()
- .use([LegacyNetworkHandler, Fetch])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([LegacyNetworkHandler, Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
diff --git a/packages/request/package.json b/packages/request/package.json
index 13833d8c79d..f0f56ea9cf4 100644
--- a/packages/request/package.json
+++ b/packages/request/package.json
@@ -1,7 +1,7 @@
{
"name": "@ember-data/request",
"description": "⚡️ A simple, small and fast framework-agnostic library to make `fetch` happen",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"license": "MIT",
"author": "Chris Thoburn ",
"repository": {
@@ -44,7 +44,7 @@
},
"dependencies": {
"@ember/test-waiters": "^3.1.0 || ^4.0.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"devDependencies": {
diff --git a/packages/request/src/-private/manager.ts b/packages/request/src/-private/manager.ts
index aa05ed5e807..df5ed2669b9 100644
--- a/packages/request/src/-private/manager.ts
+++ b/packages/request/src/-private/manager.ts
@@ -54,8 +54,8 @@ import Fetch from '@ember-data/request/fetch';
import { apiUrl } from './config';
// ... create manager and add our Fetch handler
-const manager = new RequestManager()
- .use([Fetch]);
+const manager = new RequestManager();
+manager.use([Fetch]);
// ... execute a request
const response = await manager.request({
@@ -383,9 +383,13 @@ import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
class extends Store {
- requestManager = new RequestManager()
- .use([Fetch])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
@@ -401,16 +405,19 @@ Additional handlers or a service injection like the above would need to be done
consuming application in order to make broader use of `RequestManager`.
```ts
-import Store from 'ember-data/store';
-import { CacheHandler } from '@ember-data/store';
+import Store, { CacheHandler } from 'ember-data/store';
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
export default class extends Store {
- requestManager = new RequestManager()
- .use([LegacyNetworkHandler, Fetch])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([LegacyNetworkHandler, Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
diff --git a/packages/request/src/-private/types.ts b/packages/request/src/-private/types.ts
index dc506ee6866..27d0243ffdf 100644
--- a/packages/request/src/-private/types.ts
+++ b/packages/request/src/-private/types.ts
@@ -205,8 +205,9 @@ In the case of the `Future` being returned, `Stream` proxying is automatic and i
Request handlers are registered by configuring the manager via `use`
```ts
-const manager = new RequestManager()
- .use([Handler1, Handler2]);
+const manager = new RequestManager();
+
+manager.use([Handler1, Handler2]);
```
Handlers will be invoked in the order they are registered ("fifo", first-in first-out), and may only be registered up until the first request is made. It is recommended but not required to register all handlers at one time in order to ensure explicitly visible handler ordering.
diff --git a/packages/rest/package.json b/packages/rest/package.json
index 6abbbd29537..a5e51eae31c 100644
--- a/packages/rest/package.json
+++ b/packages/rest/package.json
@@ -1,7 +1,7 @@
{
"name": "@ember-data/rest",
"description": "REST Format Support for EmberData",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": false,
"license": "MIT",
"author": "Chris Thoburn ",
@@ -22,7 +22,7 @@
"extends": "../../package.json"
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"peerDependencies": {
diff --git a/packages/schema-record/package.json b/packages/schema-record/package.json
index 3574be9f876..254325d5d3c 100644
--- a/packages/schema-record/package.json
+++ b/packages/schema-record/package.json
@@ -1,6 +1,6 @@
{
"name": "@warp-drive/schema-record",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Schema Driven Resource Presentation for WarpDrive and EmberData",
"keywords": [
"ember-addon"
@@ -78,7 +78,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"devDependencies": {
diff --git a/packages/schema-record/src/record.ts b/packages/schema-record/src/record.ts
index 6d5bf01f6b2..c6ee6388bcf 100644
--- a/packages/schema-record/src/record.ts
+++ b/packages/schema-record/src/record.ts
@@ -10,7 +10,6 @@ import type { StableRecordIdentifier } from '@warp-drive/core-types';
import type { ArrayValue, ObjectValue, Value } from '@warp-drive/core-types/json/raw';
import { STRUCTURED } from '@warp-drive/core-types/request';
import type { FieldSchema } from '@warp-drive/core-types/schema/fields';
-import type { SingleResourceRelationship } from '@warp-drive/core-types/spec/json-api-raw';
import { RecordStore } from '@warp-drive/core-types/symbols';
import {
@@ -311,13 +310,6 @@ export class SchemaRecord {
Mode[Editable]
);
case 'belongsTo':
- if (field.options.linksMode) {
- entangleSignal(signals, receiver, field.name);
- const rawValue = cache.getRelationship(identifier, field.name) as SingleResourceRelationship;
-
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
- return rawValue.data ? store.peekRecord(rawValue.data) : null;
- }
if (!HAS_MODEL_PACKAGE) {
assert(
`Cannot use belongsTo fields in your schema unless @ember-data/model is installed to provide legacy model support. ${field.name} should likely be migrated to be a resource field.`
diff --git a/packages/serializer/README.md b/packages/serializer/README.md
index 3a5fb86afc9..3aa85809d9b 100644
--- a/packages/serializer/README.md
+++ b/packages/serializer/README.md
@@ -60,9 +60,13 @@ import RequestManager from '@ember-data/request';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
export default class extends Store {
- requestManager = new RequestManager()
- .use([LegacyNetworkHandler])
- .useCache(CacheHandler);
+ requestManager = new RequestManager();
+
+ constructor(args) {
+ super(args);
+ this.requestManager.use([LegacyNetworkHandler]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
diff --git a/packages/serializer/package.json b/packages/serializer/package.json
index a15ee758d82..5a78b81880d 100644
--- a/packages/serializer/package.json
+++ b/packages/serializer/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/serializer",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "Provides Legacy JSON, JSON:API and REST Implementations of the Serializer Interface for use with @ember-data/store",
"keywords": [
"ember-addon"
@@ -81,7 +81,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"ember-cli-test-info": "^1.0.0",
"ember-cli-string-utils": "^1.1.0",
"ember-cli-path-utils": "^1.0.0",
@@ -103,7 +103,7 @@
"@glimmer/component": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
- "decorator-transforms": "^2.3.0",
+ "decorator-transforms": "^2.2.2",
"ember-source": "~5.12.0",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
"typescript": "^5.7.2",
diff --git a/packages/store/README.md b/packages/store/README.md
index 8daa70a0c62..64456d52d5c 100644
--- a/packages/store/README.md
+++ b/packages/store/README.md
@@ -108,9 +108,12 @@ import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
export default class extends Store {
- requestManager = new RequestManager()
- .use([Fetch])
- .useCache(CacheHandler);
+ constructor() {
+ super(...arguments);
+ this.requestManager = new RequestManager();
+ this.requestManager.use([Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
}
```
diff --git a/packages/store/package.json b/packages/store/package.json
index f89129e5ace..c2a485e2c64 100644
--- a/packages/store/package.json
+++ b/packages/store/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/store",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"description": "The core of EmberData. Provides the Store service which coordinates the cache with the network and presentation layers.",
"keywords": [
"ember-addon"
@@ -55,7 +55,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"peerDependencies": {
@@ -76,7 +76,7 @@
"@glimmer/component": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
- "decorator-transforms": "^2.3.0",
+ "decorator-transforms": "^2.2.2",
"ember-source": "~5.12.0",
"expect-type": "^0.20.0",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
diff --git a/packages/store/src/-private.ts b/packages/store/src/-private.ts
index be4cea8214b..9100d7b2a94 100644
--- a/packages/store/src/-private.ts
+++ b/packages/store/src/-private.ts
@@ -1,6 +1,11 @@
/**
@module @ember-data/store
*/
+import { assert, deprecate } from '@ember/debug';
+
+import { DEPRECATE_HELPERS } from '@warp-drive/build-config/deprecations';
+
+import { normalizeModelName as _normalize } from './-private/utils/normalize-model-name';
export { Store, storeFor } from './-private/store-service';
@@ -47,7 +52,33 @@ export { peekCache, removeRecordDataFor } from './-private/caches/cache-utils';
// @ember-data/model needs these temporarily
export { setRecordIdentifier, StoreMap } from './-private/caches/instance-cache';
export { setCacheFor } from './-private/caches/cache-utils';
-export { normalizeModelName as _deprecatingNormalize } from './-private/utils/normalize-model-name';
export type { StoreRequestInput } from './-private/cache-handler/handler';
-export { log, logGroup } from './-private/debug/utils';
+/**
+ This method normalizes a modelName into the format EmberData uses
+ internally by dasherizing it.
+
+ @method normalizeModelName
+ @static
+ @public
+ @deprecated
+ @for @ember-data/store
+ @param {String} modelName
+ @return {String} normalizedModelName
+*/
+export function normalizeModelName(modelName: string) {
+ if (DEPRECATE_HELPERS) {
+ deprecate(
+ `the helper function normalizeModelName is deprecated. You should use model names that are already normalized, or use string helpers of your own. This function is primarily an alias for dasherize from @ember/string.`,
+ false,
+ {
+ id: 'ember-data:deprecate-normalize-modelname-helper',
+ for: 'ember-data',
+ until: '5.0',
+ since: { available: '4.7', enabled: '4.7' },
+ }
+ );
+ return _normalize(modelName);
+ }
+ assert(`normalizeModelName support has been removed`);
+}
diff --git a/packages/store/src/-private/cache-handler/handler.ts b/packages/store/src/-private/cache-handler/handler.ts
index 59dddbe4594..6ec9006e5e7 100644
--- a/packages/store/src/-private/cache-handler/handler.ts
+++ b/packages/store/src/-private/cache-handler/handler.ts
@@ -44,7 +44,7 @@ export type LooseStoreRequestInfo = Omit<
export type StoreRequestInput = ImmutableRequestInfo | LooseStoreRequestInfo;
export interface StoreRequestContext extends RequestContext {
- request: ImmutableRequestInfo & { store: Store };
+ request: ImmutableRequestInfo & { store: Store; [EnableHydration]?: boolean };
}
/**
diff --git a/packages/store/src/-private/caches/identifier-cache.ts b/packages/store/src/-private/caches/identifier-cache.ts
index 2dec77bea22..b6602336689 100644
--- a/packages/store/src/-private/caches/identifier-cache.ts
+++ b/packages/store/src/-private/caches/identifier-cache.ts
@@ -39,10 +39,11 @@ import { hasId, hasLid, hasType } from './resource-utils';
type ResourceData = unknown;
+const IDENTIFIERS = getOrSetGlobal('IDENTIFIERS', new Set());
const DOCUMENTS = getOrSetGlobal('DOCUMENTS', new Set());
export function isStableIdentifier(identifier: unknown): identifier is StableRecordIdentifier {
- return (identifier as StableRecordIdentifier)[CACHE_OWNER] !== undefined;
+ return (identifier as StableRecordIdentifier)[CACHE_OWNER] !== undefined || IDENTIFIERS.has(identifier);
}
export function isDocumentIdentifier(identifier: unknown): identifier is StableDocumentIdentifier {
@@ -621,6 +622,7 @@ export class IdentifierCache {
identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
}
identifier[CACHE_OWNER] = undefined;
+ IDENTIFIERS.delete(identifier);
this._forget(identifier, 'record');
if (LOG_IDENTIFIERS) {
// eslint-disable-next-line no-console
@@ -647,6 +649,8 @@ function makeStableRecordIdentifier(
bucket: IdentifierBucket,
clientOriginated: boolean
): StableRecordIdentifier {
+ IDENTIFIERS.add(recordIdentifier);
+
if (DEBUG) {
// we enforce immutability in dev
// but preserve our ability to do controlled updates to the reference
@@ -689,6 +693,7 @@ function makeStableRecordIdentifier(
});
wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
+ IDENTIFIERS.add(wrapper);
DEBUG_MAP.set(wrapper, recordIdentifier);
wrapper = freeze(wrapper);
return wrapper;
diff --git a/packages/store/src/-private/caches/instance-cache.ts b/packages/store/src/-private/caches/instance-cache.ts
index 46482bed496..1fa2a0bd4f6 100644
--- a/packages/store/src/-private/caches/instance-cache.ts
+++ b/packages/store/src/-private/caches/instance-cache.ts
@@ -16,7 +16,6 @@ import type {
} from '@warp-drive/core-types/spec/json-api-raw';
import type { OpaqueRecordInstance } from '../../-types/q/record-instance';
-import { log, logGroup } from '../debug/utils';
import RecordReference from '../legacy-model-support/record-reference';
import { CacheCapabilitiesManager } from '../managers/cache-capabilities-manager';
import type { CacheManager } from '../managers/cache-manager';
@@ -207,11 +206,8 @@ export class InstanceCache {
this.__instances.record.set(identifier, record);
if (LOG_INSTANCE_CACHE) {
- logGroup('reactive-ui', '', identifier.type, identifier.lid, 'created', '');
// eslint-disable-next-line no-console
- console.log({ properties });
- // eslint-disable-next-line no-console
- console.groupEnd();
+ console.log(`InstanceCache: created Record for ${String(identifier)}`, properties);
}
}
@@ -264,7 +260,8 @@ export class InstanceCache {
removeRecordDataFor(identifier);
this.store._requestCache._clearEntries(identifier);
if (LOG_INSTANCE_CACHE) {
- log('reactive-ui', '', identifier.type, identifier.lid, 'disconnected', '');
+ // eslint-disable-next-line no-console
+ console.log(`InstanceCache: disconnected ${String(identifier)}`);
}
}
diff --git a/packages/store/src/-private/managers/cache-capabilities-manager.ts b/packages/store/src/-private/managers/cache-capabilities-manager.ts
index 941c6ba60ea..d614eca9799 100644
--- a/packages/store/src/-private/managers/cache-capabilities-manager.ts
+++ b/packages/store/src/-private/managers/cache-capabilities-manager.ts
@@ -72,13 +72,13 @@ export class CacheCapabilitiesManager implements StoreWrapper {
});
}
- notifyChange(identifier: StableRecordIdentifier, namespace: 'added' | 'removed', key: null): void;
- notifyChange(identifier: StableDocumentIdentifier, namespace: 'added' | 'updated' | 'removed', key: null): void;
- notifyChange(identifier: StableRecordIdentifier, namespace: NotificationType, key: string | null): void;
+ notifyChange(identifier: StableRecordIdentifier, namespace: 'added' | 'removed'): void;
+ notifyChange(identifier: StableDocumentIdentifier, namespace: 'added' | 'updated' | 'removed'): void;
+ notifyChange(identifier: StableRecordIdentifier, namespace: NotificationType, key?: string): void;
notifyChange(
identifier: StableRecordIdentifier | StableDocumentIdentifier,
namespace: NotificationType | 'added' | 'removed' | 'updated',
- key: string | null
+ key?: string
): void {
assert(`Expected a stable identifier`, isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
diff --git a/packages/store/src/-private/managers/notification-manager.ts b/packages/store/src/-private/managers/notification-manager.ts
index b74fe6eaaf9..ddd36534d9a 100644
--- a/packages/store/src/-private/managers/notification-manager.ts
+++ b/packages/store/src/-private/managers/notification-manager.ts
@@ -4,13 +4,12 @@
import { _backburner } from '@ember/runloop';
-import { LOG_METRIC_COUNTS, LOG_NOTIFICATIONS } from '@warp-drive/build-config/debugging';
+import { LOG_NOTIFICATIONS } from '@warp-drive/build-config/debugging';
import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import type { StableDocumentIdentifier, StableRecordIdentifier } from '@warp-drive/core-types/identifier';
import { isDocumentIdentifier, isStableIdentifier } from '../caches/identifier-cache';
-import { log } from '../debug/utils';
import type { Store } from '../store-service';
export type UnsubscribeToken = object;
@@ -20,7 +19,9 @@ const CacheOperations = new Set(['added', 'removed', 'state', 'updated', 'invali
export type CacheOperation = 'added' | 'removed' | 'updated' | 'state';
export type DocumentCacheOperation = 'invalidated' | 'added' | 'removed' | 'updated' | 'state';
-function isCacheOperationValue(value: NotificationType | DocumentCacheOperation): value is DocumentCacheOperation {
+function isCacheOperationValue(
+ value: NotificationType | CacheOperation | DocumentCacheOperation
+): value is DocumentCacheOperation {
return CacheOperations.has(value);
}
@@ -29,12 +30,11 @@ function runLoopIsFlushing(): boolean {
return !!_backburner.currentInstance && _backburner._autorun !== true;
}
-export type NotificationType = 'attributes' | 'relationships' | 'identity' | 'errors' | 'meta' | CacheOperation;
+export type NotificationType = 'attributes' | 'relationships' | 'identity' | 'errors' | 'meta' | 'state';
export interface NotificationCallback {
(identifier: StableRecordIdentifier, notificationType: 'attributes' | 'relationships', key?: string): void;
(identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'state'): void;
- (identifier: StableRecordIdentifier, notificationType: CacheOperation): void;
// (identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
}
@@ -48,15 +48,6 @@ export interface DocumentOperationCallback {
(identifier: StableDocumentIdentifier, notificationType: DocumentCacheOperation): void;
}
-function count(label: string) {
- // @ts-expect-error
- // eslint-disable-next-line
- globalThis.counts = globalThis.counts || {};
- // @ts-expect-error
- // eslint-disable-next-line
- globalThis.counts[label] = (globalThis.counts[label] || 0) + 1;
-}
-
function _unsubscribe(
tokens: Map,
token: UnsubscribeToken,
@@ -213,6 +204,11 @@ export default class NotificationManager {
return false;
}
+ if (LOG_NOTIFICATIONS) {
+ // eslint-disable-next-line no-console
+ console.log(`Buffering Notify: ${String(identifier.lid)}\t${value}\t${key || ''}`);
+ }
+
const hasSubscribers = Boolean(this._cache.get(identifier)?.size);
if (isCacheOperationValue(value) || hasSubscribers) {
@@ -223,23 +219,7 @@ export default class NotificationManager {
}
buffer.push([value, key]);
- if (LOG_METRIC_COUNTS) {
- count(`notify ${'type' in identifier ? identifier.type : ''} ${value} ${key}`);
- }
- if (!this._scheduleNotify()) {
- if (LOG_NOTIFICATIONS) {
- log(
- 'notify',
- 'buffered',
- `${'type' in identifier ? identifier.type : 'document'}`,
- identifier.lid,
- `${value}`,
- key || ''
- );
- }
- }
- } else if (LOG_METRIC_COUNTS) {
- count(`DISCARDED notify ${'type' in identifier ? identifier.type : ''} ${value} ${key}`);
+ this._scheduleNotify();
}
return hasSubscribers;
@@ -249,22 +229,21 @@ export default class NotificationManager {
this._onFlushCB = cb;
}
- _scheduleNotify(): boolean {
+ _scheduleNotify() {
const asyncFlush = this.store._enableAsyncFlush;
if (this._hasFlush) {
if (asyncFlush !== false && !runLoopIsFlushing()) {
- return false;
+ return;
}
}
if (asyncFlush && !runLoopIsFlushing()) {
this._hasFlush = true;
- return false;
+ return;
}
this._flush();
- return true;
}
_flush() {
@@ -293,14 +272,8 @@ export default class NotificationManager {
key?: string
): boolean {
if (LOG_NOTIFICATIONS) {
- log(
- 'notify',
- '',
- `${'type' in identifier ? identifier.type : 'document'}`,
- identifier.lid,
- `${value}`,
- key || ''
- );
+ // eslint-disable-next-line no-console
+ console.log(`Notifying: ${String(identifier)}\t${value}\t${key || ''}`);
}
// TODO for documents this will need to switch based on Identifier kind
diff --git a/packages/store/src/-private/proxies/promise-proxies.ts b/packages/store/src/-private/proxies/promise-proxies.ts
new file mode 100644
index 00000000000..bc2d0f69b10
--- /dev/null
+++ b/packages/store/src/-private/proxies/promise-proxies.ts
@@ -0,0 +1,225 @@
+import { deprecate } from '@ember/debug';
+import { get } from '@ember/object';
+
+import { DEBUG } from '@warp-drive/build-config/env';
+
+import type IdentifierArray from '../record-arrays/identifier-array';
+import { PromiseArrayProxy, PromiseObjectProxy } from './promise-proxy-base';
+
+/**
+ @module @ember-data/store
+*/
+
+/**
+ A `PromiseArray` is an object that acts like both an `Ember.Array`
+ and a promise. When the promise is resolved the resulting value
+ will be set to the `PromiseArray`'s `content` property. This makes
+ it easy to create data bindings with the `PromiseArray` that will be
+ updated when the promise resolves.
+
+ This class should not be imported and instantiated directly.
+
+ For more information see the [Ember.PromiseProxyMixin
+ documentation](/ember/release/classes/PromiseProxyMixin).
+
+ Example
+
+ ```javascript
+ let promiseArray = PromiseArray.create({
+ promise: $.getJSON('/some/remote/data.json')
+ });
+
+ promiseArray.length; // 0
+
+ promiseArray.then(function() {
+ promiseArray.length; // 100
+ });
+ ```
+
+ @class PromiseArray
+ @public
+ @extends Ember.ArrayProxy
+ @uses Ember.PromiseProxyMixin
+*/
+
+/**
+ A `PromiseObject` is an object that acts like both an `EmberObject`
+ and a promise. When the promise is resolved, then the resulting value
+ will be set to the `PromiseObject`'s `content` property. This makes
+ it easy to create data bindings with the `PromiseObject` that will
+ be updated when the promise resolves.
+
+ This class should not be imported and instantiated directly.
+
+ For more information see the [Ember.PromiseProxyMixin
+ documentation](/ember/release/classes/PromiseProxyMixin.html).
+
+ Example
+
+ ```javascript
+ let promiseObject = PromiseObject.create({
+ promise: $.getJSON('/some/remote/data.json')
+ });
+
+ promiseObject.name; // null
+
+ promiseObject.then(function() {
+ promiseObject.name; // 'Tomster'
+ });
+ ```
+
+ @class PromiseObject
+ @public
+ @extends Ember.ObjectProxy
+ @uses Ember.PromiseProxyMixin
+*/
+export { PromiseObjectProxy as PromiseObject };
+
+function _promiseObject(promise: Promise): Promise {
+ return PromiseObjectProxy.create({ promise }) as Promise;
+}
+
+function _promiseArray(promise: Promise>): Promise> {
+ // @ts-expect-error this bucket of lies allows us to avoid typing the promise proxy which would
+ // require us to override a lot of Ember's types.
+ return PromiseArrayProxy.create({ promise }) as unknown as Promise>;
+}
+
+// constructor is accessed in some internals but not including it in the copyright for the deprecation
+const ALLOWABLE_METHODS = ['constructor', 'then', 'catch', 'finally'];
+const ALLOWABLE_PROPS = ['__ec_yieldable__', '__ec_cancel__'] as const;
+const PROXIED_ARRAY_PROPS = [
+ 'length',
+ '[]',
+ 'firstObject',
+ 'lastObject',
+ 'meta',
+ 'content',
+ 'isPending',
+ 'isSettled',
+ 'isRejected',
+ 'isFulfilled',
+ 'promise',
+ 'reason',
+];
+const PROXIED_OBJECT_PROPS = ['content', 'isPending', 'isSettled', 'isRejected', 'isFulfilled', 'promise', 'reason'];
+
+function isAllowedProp(prop: string): prop is (typeof ALLOWABLE_PROPS)[number] {
+ return ALLOWABLE_PROPS.includes(prop as (typeof ALLOWABLE_PROPS)[number]);
+}
+
+type SensitiveArray = {
+ __ec_yieldable__: unknown;
+ __ec_cancel__: unknown;
+} & Promise>;
+
+type SensitiveObject = {
+ __ec_yieldable__: unknown;
+ __ec_cancel__: unknown;
+} & Promise;
+
+export function promiseArray(promise: Promise>): Promise> {
+ const promiseObjectProxy = _promiseArray(promise);
+ if (!DEBUG) {
+ return promiseObjectProxy;
+ }
+ const handler = {
+ get(target: SensitiveArray, prop: string, receiver: object): unknown {
+ if (typeof prop === 'symbol') {
+ return Reflect.get(target, prop, receiver);
+ }
+ if (isAllowedProp(prop)) {
+ return target[prop];
+ }
+ if (!ALLOWABLE_METHODS.includes(prop)) {
+ deprecate(
+ `Accessing ${prop} on this PromiseArray is deprecated. The return type is being changed from PromiseArray to a Promise. The only available methods to access on this promise are .then, .catch and .finally`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-proxies',
+ until: '5.0',
+ for: '@ember-data/store',
+ since: {
+ available: '4.7',
+ enabled: '4.7',
+ },
+ }
+ );
+ }
+
+ // @ts-expect-error difficult to coerce target to the classic ember proxy
+ const value: unknown = target[prop];
+ if (value && typeof value === 'function' && typeof value.bind === 'function') {
+ return value.bind(target);
+ }
+
+ if (PROXIED_ARRAY_PROPS.includes(prop)) {
+ return value;
+ }
+
+ return undefined;
+ },
+ };
+
+ return new Proxy(promiseObjectProxy, handler);
+}
+
+const ProxySymbolString = String(Symbol.for('PROXY_CONTENT'));
+
+export function promiseObject(promise: Promise): Promise {
+ const promiseObjectProxy = _promiseObject(promise);
+ if (!DEBUG) {
+ return promiseObjectProxy;
+ }
+ const handler = {
+ get(target: SensitiveObject, prop: string, receiver: object): unknown {
+ if (typeof prop === 'symbol') {
+ if (String(prop) === ProxySymbolString) {
+ return;
+ }
+ return Reflect.get(target, prop, receiver);
+ }
+
+ if (prop === 'constructor') {
+ return target.constructor;
+ }
+
+ if (isAllowedProp(prop)) {
+ return target[prop];
+ }
+
+ if (!ALLOWABLE_METHODS.includes(prop)) {
+ deprecate(
+ `Accessing ${prop} on this PromiseObject is deprecated. The return type is being changed from PromiseObject to a Promise. The only available methods to access on this promise are .then, .catch and .finally`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-proxies',
+ until: '5.0',
+ for: '@ember-data/store',
+ since: {
+ available: '4.7',
+ enabled: '4.7',
+ },
+ }
+ );
+ } else {
+ // @ts-expect-error difficult to coerce target to the classic ember proxy
+ return (target[prop] as () => unknown).bind(target);
+ }
+
+ if (PROXIED_OBJECT_PROPS.includes(prop)) {
+ // @ts-expect-error difficult to coerce target to the classic ember proxy
+ return target[prop];
+ }
+
+ const value: unknown = get(target, prop);
+ if (value && typeof value === 'function' && typeof value.bind === 'function') {
+ return value.bind(receiver);
+ }
+
+ return undefined;
+ },
+ };
+
+ return new Proxy(promiseObjectProxy, handler);
+}
diff --git a/packages/store/src/-private/proxies/promise-proxy-base.d.ts b/packages/store/src/-private/proxies/promise-proxy-base.d.ts
new file mode 100644
index 00000000000..8c54a04b1de
--- /dev/null
+++ b/packages/store/src/-private/proxies/promise-proxy-base.d.ts
@@ -0,0 +1,65 @@
+import ArrayProxy from '@ember/array/proxy';
+import ObjectProxy from '@ember/object/proxy';
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export interface PromiseArrayProxy extends ArrayProxy, Promise {}
+export class PromiseArrayProxy extends ArrayProxy {
+ declare content: T;
+
+ /*
+ * If the proxied promise is rejected this will contain the reason
+ * provided.
+ */
+ declare reason: string | Error;
+ /*
+ * Once the proxied promise has settled this will become `false`.
+ */
+ declare isPending: boolean;
+ /*
+ * Once the proxied promise has settled this will become `true`.
+ */
+ declare isSettled: boolean;
+ /*
+ * Will become `true` if the proxied promise is rejected.
+ */
+ declare isRejected: boolean;
+ /*
+ * Will become `true` if the proxied promise is fulfilled.
+ */
+ declare isFulfilled: boolean;
+ /*
+ * The promise whose fulfillment value is being proxied by this object.
+ */
+ declare promise: Promise;
+}
+
+export interface PromiseObjectProxy extends ObjectProxy, Promise {}
+export class PromiseObjectProxy extends ObjectProxy {
+ declare content?: T | null;
+
+ /*
+ * If the proxied promise is rejected this will contain the reason
+ * provided.
+ */
+ reason: string | Error;
+ /*
+ * Once the proxied promise has settled this will become `false`.
+ */
+ isPending: boolean;
+ /*
+ * Once the proxied promise has settled this will become `true`.
+ */
+ isSettled: boolean;
+ /*
+ * Will become `true` if the proxied promise is rejected.
+ */
+ isRejected: boolean;
+ /*
+ * Will become `true` if the proxied promise is fulfilled.
+ */
+ isFulfilled: boolean;
+ /*
+ * The promise whose fulfillment value is being proxied by this object.
+ */
+ promise: Promise;
+}
diff --git a/packages/store/src/-private/proxies/promise-proxy-base.js b/packages/store/src/-private/proxies/promise-proxy-base.js
new file mode 100644
index 00000000000..87c98734d04
--- /dev/null
+++ b/packages/store/src/-private/proxies/promise-proxy-base.js
@@ -0,0 +1,9 @@
+import ArrayProxy from '@ember/array/proxy';
+import { reads } from '@ember/object/computed';
+import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
+import ObjectProxy from '@ember/object/proxy';
+
+export class PromiseArrayProxy extends ArrayProxy.extend(PromiseProxyMixin) {
+ @reads('content.meta') meta;
+}
+export const PromiseObjectProxy = ObjectProxy.extend(PromiseProxyMixin);
diff --git a/packages/store/src/-private/record-arrays/identifier-array.ts b/packages/store/src/-private/record-arrays/identifier-array.ts
index a5b1f79aa42..06b83a9633d 100644
--- a/packages/store/src/-private/record-arrays/identifier-array.ts
+++ b/packages/store/src/-private/record-arrays/identifier-array.ts
@@ -1,6 +1,11 @@
/**
@module @ember-data/store
*/
+import { deprecate } from '@ember/debug';
+import { get, set } from '@ember/object';
+import { compare } from '@ember/utils';
+import Ember from 'ember';
+
import { compat } from '@ember-data/tracking';
import type { Signal } from '@ember-data/tracking/-private';
import {
@@ -10,7 +15,14 @@ import {
defineSignal,
subscribe,
} from '@ember-data/tracking/-private';
-import { DEPRECATE_COMPUTED_CHAINS } from '@warp-drive/build-config/deprecations';
+import {
+ DEPRECATE_A_USAGE,
+ DEPRECATE_ARRAY_LIKE,
+ DEPRECATE_COMPUTED_CHAINS,
+ DEPRECATE_PROMISE_PROXIES,
+ DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS,
+} from '@warp-drive/build-config/deprecations';
+import { DEBUG } from '@warp-drive/build-config/env';
import { assert } from '@warp-drive/build-config/macros';
import { getOrSetGlobal } from '@warp-drive/core-types/-private';
import type { StableRecordIdentifier } from '@warp-drive/core-types/identifier';
@@ -22,6 +34,7 @@ import type { OpaqueRecordInstance } from '../../-types/q/record-instance';
import { isStableIdentifier } from '../caches/identifier-cache';
import { recordIdentifierFor } from '../caches/instance-cache';
import type { RecordArrayManager } from '../managers/record-array-manager';
+import { promiseArray } from '../proxies/promise-proxies';
import type { Store } from '../store-service';
import { NativeProxy } from './native-proxy-type-fix';
@@ -94,6 +107,19 @@ export type IdentifierArrayCreateOptions = {
meta?: Record | null;
};
+function deprecateArrayLike(className: string, fnName: string, replName: string) {
+ deprecate(
+ `The \`${fnName}\` method on the class ${className} is deprecated. Use the native array method \`${replName}\` instead.`,
+ false,
+ {
+ id: 'ember-data:deprecate-array-like',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ }
+ );
+}
+
interface PrivateState {
links: Links | PaginationLinks | null;
meta: Record | null;
@@ -183,6 +209,16 @@ export class IdentifierArray {
declare links: Links | PaginationLinks | null;
declare meta: Record | null;
declare modelName?: TypeFromInstanceOrString;
+
+ /**
+ The modelClass represented by this record array.
+
+ @property type
+ @public
+ @deprecated
+ @type {subclass of Model}
+ */
+ declare type: unknown;
/**
The store that created this record array.
@@ -300,6 +336,7 @@ export class IdentifierArray {
}
const args: unknown[] = Array.prototype.slice.call(arguments);
assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
+
transaction = true;
const result = self[MUTATE]!(target, receiver, prop as string, args, _SIGNAL);
transaction = false;
@@ -313,6 +350,18 @@ export class IdentifierArray {
}
if (isSelfProp(self, prop)) {
+ if (DEPRECATE_ARRAY_LIKE) {
+ if (prop === 'firstObject') {
+ deprecateArrayLike(self.DEPRECATED_CLASS_NAME, prop as string, '[0]');
+ // @ts-expect-error adding MutableArray method calling index signature
+ return receiver[0];
+ } else if (prop === 'lastObject') {
+ deprecateArrayLike(self.DEPRECATED_CLASS_NAME, prop as string, 'at(-1)');
+ // @ts-expect-error adding MutableArray method calling index signature
+ return receiver[receiver.length - 1];
+ }
+ }
+
if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
return self[prop];
}
@@ -436,6 +485,28 @@ export class IdentifierArray {
},
}) as IdentifierArray;
+ if (DEPRECATE_A_USAGE) {
+ const meta = Ember.meta(this);
+ meta.hasMixin = (mixin: object) => {
+ deprecate(`Do not call A() on EmberData RecordArrays`, false, {
+ id: 'ember-data:no-a-with-array-like',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ });
+ // @ts-expect-error ArrayMixin is more than a type
+ if (mixin === NativeArray || mixin === ArrayMixin) {
+ return true;
+ }
+ return false;
+ };
+ } else if (DEBUG) {
+ const meta = Ember.meta(this);
+ meta.hasMixin = (mixin: object) => {
+ assert(`Do not call A() on EmberData RecordArrays`);
+ };
+ }
+
createArrayTags(proxy, _SIGNAL);
this[NOTIFY] = this[NOTIFY].bind(proxy);
@@ -515,9 +586,14 @@ export class IdentifierArray {
@public
@return {Promise} promise
*/
- save(): Promise {
+ save(): Promise> {
const promise = Promise.all(this.map((record) => this.store.saveRecord(record))).then(() => this);
+ if (DEPRECATE_PROMISE_PROXIES) {
+ // @ts-expect-error IdentifierArray is not a MutableArray
+ return promiseArray>(promise);
+ }
+
return promise;
}
}
@@ -542,6 +618,31 @@ Object.defineProperty(IdentifierArray.prototype, '[]', desc);
defineSignal(IdentifierArray.prototype, 'isUpdating', false);
+export default IdentifierArray;
+
+if (DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS) {
+ Object.defineProperty(IdentifierArray.prototype, 'type', {
+ get() {
+ deprecate(
+ `Using RecordArray.type to access the ModelClass for a record is deprecated. Use store.modelFor() instead.`,
+ false,
+ {
+ id: 'ember-data:deprecate-snapshot-model-class-access',
+ until: '5.0',
+ for: 'ember-data',
+ since: { available: '4.5.0', enabled: '4.5.0' },
+ }
+ );
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ if (!this.modelName) {
+ return null;
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
+ return this.store.modelFor(this.modelName);
+ },
+ });
+}
+
export type CollectionCreateOptions = IdentifierArrayCreateOptions & {
manager: RecordArrayManager;
query: ImmutableRequestInfo | Record | null;
@@ -570,6 +671,11 @@ export class Collection extends IdentifierArray {
// both being valid options to pass through here.
const promise = store.query(this.modelName, query as Record, { _recordArray: this });
+ if (DEPRECATE_PROMISE_PROXIES) {
+ // @ts-expect-error Collection is not a MutableArray
+ return promiseArray(promise);
+ }
+
return promise;
}
@@ -583,7 +689,415 @@ export class Collection extends IdentifierArray {
Collection.prototype.query = null;
// Ensure instanceof works correctly
-// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
+//Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
+
+if (DEPRECATE_ARRAY_LIKE) {
+ IdentifierArray.prototype.DEPRECATED_CLASS_NAME = 'RecordArray';
+ Collection.prototype.DEPRECATED_CLASS_NAME = 'RecordArray';
+ const EmberObjectMethods = [
+ 'addObserver',
+ 'cacheFor',
+ 'decrementProperty',
+ 'get',
+ 'getProperties',
+ 'incrementProperty',
+ 'notifyPropertyChange',
+ 'removeObserver',
+ 'set',
+ 'setProperties',
+ 'toggleProperty',
+ ] as const;
+ EmberObjectMethods.forEach((method) => {
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype[method] = function delegatedMethod(...args: unknown[]): unknown {
+ deprecate(
+ `The EmberObject ${method} method on the class ${this.DEPRECATED_CLASS_NAME} is deprecated. Use dot-notation javascript get/set access instead.`,
+ false,
+ {
+ id: 'ember-data:deprecate-array-like',
+ until: '5.0',
+ since: { enabled: '4.7', available: '4.7' },
+ for: 'ember-data',
+ }
+ );
+ // @ts-expect-error ember is missing types for some methods
+ return (Ember[method] as (...args: unknown[]) => unknown)(this, ...args);
+ };
+ });
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.addObject = function (obj: OpaqueRecordInstance) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'addObject', 'push');
+ const index = this.indexOf(obj);
+ if (index === -1) {
+ this.push(obj);
+ }
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.addObjects = function (objs: OpaqueRecordInstance[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'addObjects', 'push');
+ objs.forEach((obj: OpaqueRecordInstance) => {
+ const index = this.indexOf(obj);
+ if (index === -1) {
+ this.push(obj);
+ }
+ });
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.popObject = function () {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'popObject', 'pop');
+ return this.pop() as OpaqueRecordInstance;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.pushObject = function (obj: OpaqueRecordInstance) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'pushObject', 'push');
+ this.push(obj);
+ return obj;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.pushObjects = function (objs: OpaqueRecordInstance[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'pushObjects', 'push');
+ this.push(...objs);
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.shiftObject = function () {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'shiftObject', 'shift');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.shift()!;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.unshiftObject = function (obj: OpaqueRecordInstance) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'unshiftObject', 'unshift');
+ this.unshift(obj);
+ return obj;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.unshiftObjects = function (objs: OpaqueRecordInstance[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'unshiftObjects', 'unshift');
+ this.unshift(...objs);
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.objectAt = function (index: number) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'objectAt', 'at');
+ //For negative index values go back from the end of the array
+ const arrIndex = Math.sign(index) === -1 ? this.length + index : index;
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this[arrIndex];
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.objectsAt = function (indices: number[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'objectsAt', 'at');
+ // @ts-expect-error adding MutableArray method
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
+ return indices.map((index) => this.objectAt(index)!);
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.removeAt = function (index: number) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'removeAt', 'splice');
+ this.splice(index, 1);
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.insertAt = function (index: number, obj: OpaqueRecordInstance) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'insertAt', 'splice');
+ this.splice(index, 0, obj);
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.removeObject = function (obj: OpaqueRecordInstance) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'removeObject', 'splice');
+ const index = this.indexOf(obj);
+ if (index !== -1) {
+ this.splice(index, 1);
+ }
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.removeObjects = function (objs: OpaqueRecordInstance[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'removeObjects', 'splice');
+ objs.forEach((obj) => {
+ const index = this.indexOf(obj);
+ if (index !== -1) {
+ this.splice(index, 1);
+ }
+ });
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.toArray = function () {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'toArray', 'slice');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.slice();
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.replace = function (idx: number, amt: number, objects?: OpaqueRecordInstance[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'replace', 'splice');
+ if (objects) {
+ this.splice(idx, amt, ...objects);
+ } else {
+ this.splice(idx, amt);
+ }
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.clear = function () {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'clear', 'length = 0');
+ this.splice(0, this.length);
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.setObjects = function (objects: OpaqueRecordInstance[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'setObjects', '`arr.length = 0; arr.push(objects);`');
+ assert(
+ `${this.DEPRECATED_CLASS_NAME}.setObjects expects to receive an array as its argument`,
+ Array.isArray(objects)
+ );
+ this.splice(0, this.length);
+ this.push(...objects);
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.reverseObjects = function () {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'reverseObjects', 'reverse');
+ this.reverse();
+ return this;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.compact = function () {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'compact', 'filter');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.filter((v) => v !== null && v !== undefined);
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.any = function (callback, target) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'any', 'some');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ return this.some(callback, target);
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.isAny = function (prop, value) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'isAny', 'some');
+ const hasValue = arguments.length === 2;
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ return this.some((v) => (hasValue ? v[prop] === value : v[prop] === true));
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.isEvery = function (prop, value) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'isEvery', 'every');
+ const hasValue = arguments.length === 2;
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ return this.every((v) => (hasValue ? v[prop] === value : v[prop] === true));
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.getEach = function (key: string) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'getEach', 'map');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.map((value) => get(value, key));
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.mapBy = function (key: string) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'mapBy', 'map');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.map((value) => get(value, key));
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.findBy = function (key: string, value?: unknown) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'findBy', 'find');
+ if (arguments.length === 2) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.find((val) => {
+ return get(val, key) === value;
+ });
+ } else {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.find((val) => Boolean(get(val, key)));
+ }
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.filterBy = function (key: string, value?: unknown) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'filterBy', 'filter');
+ if (arguments.length === 2) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.filter((record) => {
+ return get(record, key) === value;
+ });
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.filter((record) => {
+ return Boolean(get(record, key));
+ });
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.sortBy = function (...sortKeys: string[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'sortBy', '.slice().sort');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.slice().sort((a, b) => {
+ for (let i = 0; i < sortKeys.length; i++) {
+ const key = sortKeys[i];
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const propA = get(a, key);
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const propB = get(b, key);
+ // return 1 or -1 else continue to the next sortKey
+ const compareValue = compare(propA, propB);
+
+ if (compareValue) {
+ return compareValue;
+ }
+ }
+ return 0;
+ });
+ };
+
+ // @ts-expect-error
+ IdentifierArray.prototype.invoke = function (key: string, ...args: unknown[]) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'invoke', 'forEach');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ return this.map((value) => (value[key] as (...args: unknown[]) => unknown)(...args));
+ };
+
+ // @ts-expect-error
+ IdentifierArray.prototype.addArrayObserver = function () {
+ deprecateArrayLike(
+ this.DEPRECATED_CLASS_NAME,
+ 'addArrayObserver',
+ 'derived state or reacting at the change source'
+ );
+ };
+
+ // @ts-expect-error
+ IdentifierArray.prototype.removeArrayObserver = function () {
+ deprecateArrayLike(
+ this.DEPRECATED_CLASS_NAME,
+ 'removeArrayObserver',
+ 'derived state or reacting at the change source'
+ );
+ };
+
+ // @ts-expect-error
+ IdentifierArray.prototype.arrayContentWillChange = function () {
+ deprecateArrayLike(
+ this.DEPRECATED_CLASS_NAME,
+ 'arrayContentWillChange',
+ 'derived state or reacting at the change source'
+ );
+ };
+
+ // @ts-expect-error
+ IdentifierArray.prototype.arrayContentDidChange = function () {
+ deprecateArrayLike(
+ this.DEPRECATED_CLASS_NAME,
+ 'arrayContentDidChange',
+ 'derived state or reacting at the change source.'
+ );
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.reject = function (callback, target?: unknown) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'reject', 'filter');
+ assert('`reject` expects a function as first argument.', typeof callback === 'function');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.filter((...args) => {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
+ return !callback.apply(target, args);
+ });
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.rejectBy = function (key: string, value?: unknown) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'rejectBy', 'filter');
+ if (arguments.length === 2) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.filter((record) => {
+ return get(record, key) !== value;
+ });
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.filter((record) => {
+ return !get(record, key);
+ });
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.setEach = function (key: string, value: unknown) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'setEach', 'forEach');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ this.forEach((item) => set(item, key, value));
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.uniq = function () {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'uniq', 'filter');
+ // all current managed arrays are already enforced as unique
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return this.slice();
+ };
+
+ // @ts-expect-error
+ IdentifierArray.prototype.uniqBy = function (key: string) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'uniqBy', 'filter');
+ // all current managed arrays are already enforced as unique
+ const seen = new Set();
+ const result: OpaqueRecordInstance[] = [];
+ this.forEach((item) => {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const value = get(item, key);
+ if (seen.has(value)) {
+ return;
+ }
+ seen.add(value);
+ result.push(item);
+ });
+ return result;
+ };
+
+ // @ts-expect-error adding MutableArray method
+ IdentifierArray.prototype.without = function (value: OpaqueRecordInstance) {
+ deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'without', 'slice');
+ const newArr = this.slice();
+ const index = this.indexOf(value);
+ if (index !== -1) {
+ newArr.splice(index, 1);
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
+ return newArr;
+ };
+
+ // @ts-expect-error
+ IdentifierArray.prototype.firstObject = null;
+ // @ts-expect-error
+ IdentifierArray.prototype.lastObject = null;
+}
type PromiseProxyRecord = { then(): void; content: OpaqueRecordInstance | null | undefined };
@@ -601,11 +1115,25 @@ function assertRecordPassedToHasMany(record: OpaqueRecordInstance | PromiseProxy
);
}
-function extractIdentifierFromRecord(record: PromiseProxyRecord | OpaqueRecordInstance | null) {
- if (!record) {
+function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | OpaqueRecordInstance | null) {
+ if (!recordOrPromiseRecord) {
return null;
}
- assertRecordPassedToHasMany(record);
- return recordIdentifierFor(record);
+ if (isPromiseRecord(recordOrPromiseRecord)) {
+ const content = recordOrPromiseRecord.content;
+ assert(
+ 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo relationship.',
+ content !== undefined && content !== null
+ );
+ assertRecordPassedToHasMany(content);
+ return recordIdentifierFor(content);
+ }
+
+ assertRecordPassedToHasMany(recordOrPromiseRecord);
+ return recordIdentifierFor(recordOrPromiseRecord);
+}
+
+function isPromiseRecord(record: PromiseProxyRecord | OpaqueRecordInstance): record is PromiseProxyRecord {
+ return Boolean(typeof record === 'object' && record && 'then' in record);
}
diff --git a/packages/store/src/-private/store-service.ts b/packages/store/src/-private/store-service.ts
index a1c5cd98917..00323a16347 100644
--- a/packages/store/src/-private/store-service.ts
+++ b/packages/store/src/-private/store-service.ts
@@ -10,7 +10,11 @@ import type RequestManager from '@ember-data/request';
import type { Future } from '@ember-data/request';
import { LOG_PAYLOADS, LOG_REQUESTS } from '@warp-drive/build-config/debugging';
import {
+ DEPRECATE_HAS_RECORD,
+ DEPRECATE_PROMISE_PROXIES,
DEPRECATE_STORE_EXTENDS_EMBER_OBJECT,
+ DEPRECATE_STORE_FIND,
+ DISABLE_6X_DEPRECATIONS,
ENABLE_LEGACY_SCHEMA_SERVICE,
} from '@warp-drive/build-config/deprecations';
import { DEBUG, TESTING } from '@warp-drive/build-config/env';
@@ -57,6 +61,7 @@ import { CacheManager } from './managers/cache-manager';
import NotificationManager from './managers/notification-manager';
import { RecordArrayManager } from './managers/record-array-manager';
import { RequestPromise, RequestStateService } from './network/request-cache';
+import { promiseArray, promiseObject } from './proxies/promise-proxies';
import type { Collection, IdentifierArray } from './record-arrays/identifier-array';
import { coerceId, ensureStringId } from './utils/coerce-id';
import { constructResource } from './utils/construct-resource';
@@ -215,7 +220,7 @@ const app = new EmberApp(defaults, {
});
\`\`\`
`,
- false,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
{
id: 'ember-data:deprecate-store-extends-ember-object',
until: '6.0',
@@ -486,9 +491,12 @@ export class Store extends BaseClass {
* import Fetch from '@ember-data/request/fetch';
*
* class extends Store {
- * requestManager = new RequestManager()
- * .use([Fetch])
- * .useCache(CacheHandler);
+ * constructor() {
+ * super(...arguments);
+ * this.requestManager = new RequestManager();
+ * this.requestManager.use([Fetch]);
+ * this.requestManager.useCache(CacheHandler);
+ * }
* }
* ```
*
@@ -743,11 +751,11 @@ export class Store extends BaseClass {
const opts: {
store: Store;
disableTestWaiter?: boolean;
- [EnableHydration]: boolean;
+ [EnableHydration]: true;
records?: StableRecordIdentifier[];
} = {
store: this,
- [EnableHydration]: requestConfig[EnableHydration] ?? true,
+ [EnableHydration]: true,
};
if (requestConfig.records) {
@@ -1018,6 +1026,60 @@ export class Store extends BaseClass {
}
}
+ /**
+ @method find
+ @param {String} modelName
+ @param {String|Integer} id
+ @param {Object} options
+ @return {Promise} promise
+ @deprecated
+ @private
+ */
+ find(modelName: string, id: string | number, options?: FindRecordOptions): Promise {
+ if (DEBUG) {
+ assertDestroyingStore(this, 'find');
+ }
+ // The default `model` hook in Route calls `find(modelName, id)`,
+ // that's why we have to keep this method around even though `findRecord` is
+ // the public way to get a record by modelName and id.
+ assert(
+ `Using store.find(type) has been removed. Use store.findAll(modelName) to retrieve all records for a given type.`,
+ arguments.length !== 1
+ );
+ assert(
+ `Calling store.find(modelName, id, { preload: preload }) is no longer supported. Use store.findRecord(modelName, id, { preload: preload }) instead.`,
+ !options
+ );
+ assert(`You need to pass the model name and id to the store's find method`, arguments.length === 2);
+ assert(
+ `You cannot pass '${id}' as id to the store's find method`,
+ typeof id === 'string' || typeof id === 'number'
+ );
+ assert(
+ `Calling store.find() with a query object is no longer supported. Use store.query() instead.`,
+ typeof id !== 'object'
+ );
+ assert(
+ `Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`,
+ typeof modelName === 'string'
+ );
+
+ if (DEPRECATE_STORE_FIND) {
+ deprecate(
+ `Using store.find is deprecated, use store.findRecord instead. Likely this means you are relying on the implicit store fetching behavior of routes unknowingly.`,
+ false,
+ {
+ id: 'ember-data:deprecate-store-find',
+ since: { available: '4.5', enabled: '4.5' },
+ for: 'ember-data',
+ until: '5.0',
+ }
+ );
+ return this.findRecord(modelName, id);
+ }
+ assert(`store.find has been removed. Use store.findRecord instead.`);
+ }
+
/**
This method returns a record for a given identifier or type and id combination.
@@ -1424,6 +1486,14 @@ export class Store extends BaseClass {
cacheOptions: { [SkipCache]: true },
});
+ if (DEPRECATE_PROMISE_PROXIES) {
+ return promiseObject(
+ promise.then((document) => {
+ return document.content;
+ })
+ );
+ }
+
return promise.then((document) => {
return document.content;
});
@@ -1574,6 +1644,54 @@ export class Store extends BaseClass {
return isLoaded ? (this._instanceCache.getRecord(stableIdentifier) as T) : null;
}
+ /**
+ This method returns true if a record for a given modelName and id is already
+ loaded in the store. Use this function to know beforehand if a findRecord()
+ will result in a request or that it will be a cache hit.
+
+ Example
+
+ ```javascript
+ store.hasRecordForId('post', 1); // false
+ store.findRecord('post', 1).then(function() {
+ store.hasRecordForId('post', 1); // true
+ });
+ ```
+
+ @method hasRecordForId
+ @deprecated
+ @public
+ @param {String} modelName
+ @param {(String|Integer)} id
+ @return {Boolean}
+ */
+ hasRecordForId(modelName: string, id: string | number): boolean {
+ if (DEPRECATE_HAS_RECORD) {
+ deprecate(`store.hasRecordForId has been deprecated in favor of store.peekRecord`, false, {
+ id: 'ember-data:deprecate-has-record-for-id',
+ since: { available: '4.5', enabled: '4.5' },
+ until: '5.0',
+ for: 'ember-data',
+ });
+ if (DEBUG) {
+ assertDestroyingStore(this, 'hasRecordForId');
+ }
+ assert(`You need to pass a model name to the store's hasRecordForId method`, modelName);
+ assert(
+ `Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`,
+ typeof modelName === 'string'
+ );
+
+ const type = normalizeModelName(modelName);
+ const trueId = ensureStringId(id);
+ const resource = { type, id: trueId };
+
+ const identifier = this.identifierCache.peekRecordIdentifier(resource);
+ return Boolean(identifier && this._instanceCache.recordIsLoaded(identifier));
+ }
+ assert(`store.hasRecordForId has been removed`);
+ }
+
/**
This method delegates a query to the adapter. This is the one place where
adapter-level semantics are exposed to the application.
@@ -1648,6 +1766,9 @@ export class Store extends BaseClass {
cacheOptions: { [SkipCache]: true },
});
+ if (DEPRECATE_PROMISE_PROXIES) {
+ return promiseArray(promise.then((document) => document.content)) as unknown as Promise;
+ }
return promise.then((document) => document.content);
}
@@ -1776,6 +1897,10 @@ export class Store extends BaseClass {
cacheOptions: { [SkipCache]: true },
});
+ if (DEPRECATE_PROMISE_PROXIES) {
+ return promiseObject(promise.then((document) => document.content));
+ }
+
return promise.then((document) => document.content);
}
@@ -1978,6 +2103,10 @@ export class Store extends BaseClass {
cacheOptions: { [SkipCache]: true },
});
+ if (DEPRECATE_PROMISE_PROXIES) {
+ return promiseArray(promise.then((document) => document.content)) as unknown as Promise>;
+ }
+
return promise.then((document) => document.content);
}
@@ -2389,39 +2518,51 @@ export class Store extends BaseClass {
if (ENABLE_LEGACY_SCHEMA_SERVICE) {
Store.prototype.getSchemaDefinitionService = function (): SchemaService {
assert(`You must registerSchemaDefinitionService with the store to use custom model classes`, this._schema);
- deprecate(`Use \`store.schema\` instead of \`store.getSchemaDefinitionService()\``, false, {
- id: 'ember-data:schema-service-updates',
- until: '6.0',
- for: 'ember-data',
- since: {
- available: '4.13',
- enabled: '5.4',
- },
- });
+ deprecate(
+ `Use \`store.schema\` instead of \`store.getSchemaDefinitionService()\``,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:schema-service-updates',
+ until: '6.0',
+ for: 'ember-data',
+ since: {
+ available: '4.13',
+ enabled: '5.4',
+ },
+ }
+ );
return this._schema;
};
Store.prototype.registerSchemaDefinitionService = function (schema: SchemaService) {
- deprecate(`Use \`store.createSchemaService\` instead of \`store.registerSchemaDefinitionService()\``, false, {
- id: 'ember-data:schema-service-updates',
- until: '6.0',
- for: 'ember-data',
- since: {
- available: '4.13',
- enabled: '5.4',
- },
- });
+ deprecate(
+ `Use \`store.createSchemaService\` instead of \`store.registerSchemaDefinitionService()\``,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:schema-service-updates',
+ until: '6.0',
+ for: 'ember-data',
+ since: {
+ available: '4.13',
+ enabled: '5.4',
+ },
+ }
+ );
this._schema = schema;
};
Store.prototype.registerSchema = function (schema: SchemaService) {
- deprecate(`Use \`store.createSchemaService\` instead of \`store.registerSchema()\``, false, {
- id: 'ember-data:schema-service-updates',
- until: '6.0',
- for: 'ember-data',
- since: {
- available: '4.13',
- enabled: '5.4',
- },
- });
+ deprecate(
+ `Use \`store.createSchemaService\` instead of \`store.registerSchema()\``,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS,
+ {
+ id: 'ember-data:schema-service-updates',
+ until: '6.0',
+ for: 'ember-data',
+ since: {
+ available: '4.13',
+ enabled: '5.4',
+ },
+ }
+ );
this._schema = schema;
};
}
@@ -2531,5 +2672,33 @@ function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord |
}
const extract = recordIdentifierFor;
+ if (DEPRECATE_PROMISE_PROXIES) {
+ if (isPromiseRecord(recordOrPromiseRecord)) {
+ const content = recordOrPromiseRecord.content;
+ assert(
+ 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.',
+ content !== undefined
+ );
+ deprecate(
+ `You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
+ false,
+ {
+ id: 'ember-data:deprecate-promise-proxies',
+ until: '5.0',
+ since: {
+ enabled: '4.7',
+ available: '4.7',
+ },
+ for: 'ember-data',
+ }
+ );
+ return content ? extract(content) : null;
+ }
+ }
+
return extract(recordOrPromiseRecord);
}
+
+function isPromiseRecord(record: PromiseProxyRecord | OpaqueRecordInstance): record is PromiseProxyRecord {
+ return typeof record === 'object' && !!record && 'then' in record && typeof record.then === 'function';
+}
diff --git a/packages/store/src/-private/store-service.type-test.ts b/packages/store/src/-private/store-service.type-test.ts
index f98c61a029c..5ee28e360c2 100644
--- a/packages/store/src/-private/store-service.type-test.ts
+++ b/packages/store/src/-private/store-service.type-test.ts
@@ -33,17 +33,6 @@ import { Store } from './store-service';
)
).toEqualTypeOf();
- const result2 = store.peekRecord({ type: 'user', id: '1' });
-
- expectTypeOf(result2).toBeUnknown();
- expectTypeOf(
- store.peekRecord({
- // @ts-expect-error since there is no brand, this should error
- type: 'user',
- id: '1',
- })
- ).toEqualTypeOf();
-
expectTypeOf(store.peekRecord('user', '1')).toEqualTypeOf();
expectTypeOf(
store.peekRecord(
@@ -52,15 +41,6 @@ import { Store } from './store-service';
'1'
)
).toEqualTypeOf();
-
- expectTypeOf(store.peekRecord({ type: 'user', id: '1' })).toEqualTypeOf();
- expectTypeOf(
- store.peekRecord({
- // @ts-expect-error should error since this does not match the brand
- type: 'users',
- id: '1',
- })
- ).toEqualTypeOf();
}
//////////////////////////////////
diff --git a/packages/store/src/-private/utils/coerce-id.ts b/packages/store/src/-private/utils/coerce-id.ts
index ceb1f6f8dda..2be265ef878 100644
--- a/packages/store/src/-private/utils/coerce-id.ts
+++ b/packages/store/src/-private/utils/coerce-id.ts
@@ -4,7 +4,7 @@
import { deprecate } from '@ember/debug';
-import { DEPRECATE_NON_STRICT_ID } from '@warp-drive/build-config/deprecations';
+import { DEPRECATE_NON_STRICT_ID, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
import { assert } from '@warp-drive/build-config/macros';
// Used by the store to normalize IDs entering the store. Despite the fact
@@ -28,7 +28,7 @@ export function coerceId(id: unknown): string | null {
`The resource id '<${typeof id}> ${String(
id
)} ' is not normalized. Update your application code to use '${JSON.stringify(normalized)}' instead.`,
- normalized === id,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : normalized === id,
{
id: 'ember-data:deprecate-non-strict-id',
until: '6.0',
diff --git a/packages/store/src/-private/utils/normalize-model-name.ts b/packages/store/src/-private/utils/normalize-model-name.ts
index 91e12a8e829..dba54a8b1c1 100644
--- a/packages/store/src/-private/utils/normalize-model-name.ts
+++ b/packages/store/src/-private/utils/normalize-model-name.ts
@@ -1,7 +1,7 @@
import { deprecate } from '@ember/debug';
import { dasherize } from '@ember-data/request-utils/string';
-import { DEPRECATE_NON_STRICT_TYPES } from '@warp-drive/build-config/deprecations';
+import { DEPRECATE_NON_STRICT_TYPES, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations';
export function normalizeModelName(type: string): string {
if (DEPRECATE_NON_STRICT_TYPES) {
@@ -9,7 +9,7 @@ export function normalizeModelName(type: string): string {
deprecate(
`The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`,
- result === type,
+ /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : result === type,
{
id: 'ember-data:deprecate-non-strict-types',
until: '6.0',
diff --git a/packages/store/src/-types/q/cache-capabilities-manager.ts b/packages/store/src/-types/q/cache-capabilities-manager.ts
index e9ff955b4ec..43abf1eb8e1 100644
--- a/packages/store/src/-types/q/cache-capabilities-manager.ts
+++ b/packages/store/src/-types/q/cache-capabilities-manager.ts
@@ -108,12 +108,12 @@ export type CacheCapabilitiesManager = {
* @param {string|undefined} key
* @public
*/
- notifyChange(identifier: StableRecordIdentifier, namespace: 'added' | 'removed', key: null): void;
- notifyChange(identifier: StableDocumentIdentifier, namespace: 'added' | 'updated' | 'removed', key: null): void;
- notifyChange(identifier: StableRecordIdentifier, namespace: NotificationType, key: string | null): void;
+ notifyChange(identifier: StableRecordIdentifier, namespace: 'added' | 'removed'): void;
+ notifyChange(identifier: StableDocumentIdentifier, namespace: 'added' | 'updated' | 'removed'): void;
+ notifyChange(identifier: StableRecordIdentifier, namespace: NotificationType, key?: string): void;
notifyChange(
identifier: StableRecordIdentifier | StableDocumentIdentifier,
namespace: NotificationType | 'added' | 'removed' | 'updated',
- key: string | null
+ key?: string
): void;
};
diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts
index 3370e82e74f..c30dab7d595 100644
--- a/packages/store/src/index.ts
+++ b/packages/store/src/index.ts
@@ -82,8 +82,11 @@
* import Fetch from '@ember-data/request/fetch';
*
* export default class extends Store {
- * requestManager = new RequestManager()
- * .use([Fetch]);
+ * constructor() {
+ * super(...arguments);
+ * this.requestManager = new RequestManager();
+ * this.requestManager.use([Fetch]);
+ * }
* }
* ```
*
@@ -183,6 +186,7 @@ export {
Store as default,
type StoreRequestContext,
CacheHandler,
+ normalizeModelName,
type Document,
type CachePolicy,
type StoreRequestInput,
diff --git a/packages/store/vite.config.mjs b/packages/store/vite.config.mjs
index ca9aa0e126f..ba7aaf03493 100644
--- a/packages/store/vite.config.mjs
+++ b/packages/store/vite.config.mjs
@@ -1,6 +1,20 @@
import { createConfig } from '@warp-drive/internal-config/vite/config.js';
-export const externals = ['@ember/runloop', '@ember/object', '@ember/debug'];
+export const externals = [
+ 'ember',
+ '@ember/object/computed',
+ '@ember/object/promise-proxy-mixin',
+ '@ember/object/proxy',
+ '@ember/array/proxy',
+ '@ember/application',
+ '@ember/debug',
+ '@ember/owner',
+ '@ember/utils',
+ '@ember/runloop',
+ '@ember/object',
+ '@ember/debug',
+];
+
export const entryPoints = ['./src/index.ts', './src/types.ts', './src/-private.ts'];
export default createConfig(
diff --git a/packages/tracking/README.md b/packages/tracking/README.md
index e26334bebca..9e0d2f4f934 100644
--- a/packages/tracking/README.md
+++ b/packages/tracking/README.md
@@ -33,3 +33,14 @@ pnpm add @ember-data/tracking
- 
- 
+
+## About
+
+> Note: This is a V2 Addon, but we have intentionally configured it to act and report as a V1 Addon due
+to bugs with ember-auto-import.
+>
+> We can remove the V1 tag if ember-auto-import will no longer attempt
+to load V2 addons or if it is fixed to work with V1 addons with custom addon trees and also dedupes modules for test apps.
+>
+> You can still consume this as a normal library.
+> In other projects.
diff --git a/packages/tracking/package.json b/packages/tracking/package.json
index 7153d31c9da..9cfcf5e0924 100644
--- a/packages/tracking/package.json
+++ b/packages/tracking/package.json
@@ -1,7 +1,7 @@
{
"name": "@ember-data/tracking",
"description": "Tracking Primitives for controlling change notification of Tracked properties when working with EmberData",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": false,
"license": "MIT",
"author": "Chris Thoburn ",
@@ -30,7 +30,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@warp-drive/build-config": "workspace:*"
},
"peerDependencies": {
diff --git a/packages/unpublished-eslint-rules/package.json b/packages/unpublished-eslint-rules/package.json
index 26050a0ca89..342e23a428c 100644
--- a/packages/unpublished-eslint-rules/package.json
+++ b/packages/unpublished-eslint-rules/package.json
@@ -1,7 +1,7 @@
{
"name": "eslint-plugin-ember-data-internal",
"main": "./src/index.js",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"repository": {
"type": "git",
diff --git a/packages/unpublished-test-infra/package.json b/packages/unpublished-test-infra/package.json
index 903852248fa..2fcb888e517 100644
--- a/packages/unpublished-test-infra/package.json
+++ b/packages/unpublished-test-infra/package.json
@@ -1,6 +1,6 @@
{
"name": "@ember-data/unpublished-test-infra",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "The default blueprint for ember-data private packages.",
"keywords": [
@@ -76,7 +76,7 @@
"@ember-data/tracking": "workspace:*",
"@warp-drive/diagnostic": "workspace:*",
"@warp-drive/core-types": "workspace:*",
- "@ember/test-helpers": "3.3.0 || ^4.0.4 || ^5.1.0"
+ "@ember/test-helpers": "3.3.0 || ^4.0.4"
},
"peerDependenciesMeta": {
"qunit": {
@@ -90,7 +90,7 @@
}
},
"dependencies": {
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"chalk": "^4.1.2",
"qunit": "^2.20.1",
"semver": "^7.6.3",
@@ -101,7 +101,7 @@
"@babel/plugin-transform-typescript": "^7.24.5",
"@babel/preset-env": "^7.24.5",
"@babel/preset-typescript": "^7.24.1",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@glimmer/component": "^1.1.2",
"@types/semver": "^7.5.8",
"@types/qunit": "2.19.10",
diff --git a/release/core/publish/steps/generate-mirror-tarballs.ts b/release/core/publish/steps/generate-mirror-tarballs.ts
index fc784493916..c5072e9366d 100644
--- a/release/core/publish/steps/generate-mirror-tarballs.ts
+++ b/release/core/publish/steps/generate-mirror-tarballs.ts
@@ -70,13 +70,6 @@ export async function generateMirrorTarballs(
newContents = newContents.replace(new RegExp(`"${from}`, 'g'), `"${to}`);
}
- // macros.globalConfig['WarpDrive']
- // macros.setGlobalConfig(import.meta.filename, 'WarpDrive', finalizedConfig);
- if (strat.name === '@warp-drive/build-config') {
- newContents = newContents.replace(new RegExp(`'WarpDrive'`, 'g'), `'WarpDriveMirror'`);
- }
-
- newContents = newContents.replace(/getGlobalConfig\(\)\.WarpDrive\./g, 'getGlobalConfig().WarpDriveMirror.');
newContents = newContents.replace(new RegExp(`'@ember-data/'`, 'g'), `'@ember-data-mirror/'`);
newContents = newContents.replace(new RegExp(`"@ember-data/"`, 'g'), `"@ember-data-mirror/"`);
diff --git a/release/core/publish/steps/generate-tarballs.ts b/release/core/publish/steps/generate-tarballs.ts
index 4496f5489ed..dd5713f6b77 100644
--- a/release/core/publish/steps/generate-tarballs.ts
+++ b/release/core/publish/steps/generate-tarballs.ts
@@ -307,34 +307,8 @@ async function convertTypesToModules(pkg: Package, subdir: 'unstable-preview-typ
}
}
-function exposeTypes(pkg: Package, subdir: 'unstable-preview-types' | 'preview-types' | 'types') {
- if (pkg.pkgData.exports) {
- /**
- * Allows tsconfig.json#compilerOptions#types to use import paths,
- * rather than file paths (there are no file path guarantees for any given package manager)
- */
- pkg.pkgData.exports[`./${subdir}`] = {
- /**
- * No default, import, or require here, because there are no actual modules to import.
- */
- types: `./${subdir}/index.d.ts`,
- };
-
- /**
- * For older tsconfig.json settings
- */
- pkg.pkgData.typesVersions = {
- // very loose TS version
- '*': {
- [subdir]: [`./${subdir}`],
- },
- };
- }
-}
-
async function makeTypesAlpha(pkg: Package) {
scrubTypesFromExports(pkg);
- exposeTypes(pkg, 'unstable-preview-types');
// enforce that the correct types directory is present
const present = new Set(pkg.pkgData.files);
@@ -361,7 +335,6 @@ async function makeTypesAlpha(pkg: Package) {
async function makeTypesBeta(pkg: Package) {
scrubTypesFromExports(pkg);
- exposeTypes(pkg, 'preview-types');
// enforce that the correct types directory is present
const present = new Set(pkg.pkgData.files);
diff --git a/release/strategy.json b/release/strategy.json
index b18d962e679..20149cf59a2 100644
--- a/release/strategy.json
+++ b/release/strategy.json
@@ -78,10 +78,10 @@
"mirrorPublish": false
},
"@warp-drive/ember": {
- "stage": "stable",
+ "stage": "alpha",
"types": "alpha",
"typesPublish": false,
- "mirrorPublish": true
+ "mirrorPublish": false
},
"@warp-drive/schema": {
"stage": "alpha",
@@ -89,12 +89,6 @@
"typesPublish": false,
"mirrorPublish": false
},
- "@warp-drive/experiments": {
- "stage": "beta",
- "types": "private",
- "typesPublish": false,
- "mirrorPublish": true
- },
"@warp-drive/schema-record": {
"stage": "stable",
"types": "alpha",
diff --git a/release/utils/package.ts b/release/utils/package.ts
index 150cb49a7dd..4f01e9e0b51 100644
--- a/release/utils/package.ts
+++ b/release/utils/package.ts
@@ -59,7 +59,6 @@ export type PACKAGEJSON = {
scripts?: Record;
files?: string[];
exports?: ExportConfig;
- typesVersions?: { [tsVersion: string]: { [relativeImportPath: string]: string[] } };
'ember-addon'?: {
main?: 'addon-main.js';
type?: 'addon';
diff --git a/tests/blueprints/package.json b/tests/blueprints/package.json
index e483d6d3927..5f8c7277cc2 100644
--- a/tests/blueprints/package.json
+++ b/tests/blueprints/package.json
@@ -1,6 +1,6 @@
{
"name": "blueprint-tests",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Provides tests for blueprints",
"repository": {
@@ -77,7 +77,7 @@
"@ember-data/tracking": "workspace:*",
"@ember-data/unpublished-test-infra": "workspace:*",
"@ember/edition-utils": "^1.2.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
@@ -86,7 +86,7 @@
"ember-cli": "~5.12.0",
"ember-cli-blueprint-test-helpers": "^0.19.2",
"ember-source": "~5.12.0",
- "mocha": "^10.8.2",
+ "mocha": "^10.7.3",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
"silent-error": "^1.1.1"
},
diff --git a/tests/builders/package.json b/tests/builders/package.json
index 05978262b09..24a6603870e 100644
--- a/tests/builders/package.json
+++ b/tests/builders/package.json
@@ -1,6 +1,6 @@
{
"name": "builders-test-app",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Provides tests for URL and Request Building Capabilities",
"keywords": [],
@@ -88,16 +88,16 @@
"@ember/edition-utils": "^1.2.0",
"@ember/optional-features": "^2.1.0",
"@ember/string": "^3.1.1",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
- "@embroider/addon-shim": "^1.9.0",
+ "@embroider/addon-shim": "^1.8.9",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/diagnostic": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
"@warp-drive/build-config": "workspace:*",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/docs/fixtures/expected.js b/tests/docs/fixtures/expected.js
index 61097015b4d..90248c6e9f3 100644
--- a/tests/docs/fixtures/expected.js
+++ b/tests/docs/fixtures/expected.js
@@ -91,6 +91,7 @@ module.exports = {
'(private) @ember-data/store RecordArray#store',
'(private) @ember-data/store Snapshot#constructor',
'(private) @ember-data/store Store#_push',
+ '(private) @ember-data/store Store#find',
'(private) @ember-data/store Store#init',
'(public) @ember-data/active-record/request @ember-data/active-record/request#createRecord',
'(public) @ember-data/active-record/request @ember-data/active-record/request#deleteRecord',
@@ -125,6 +126,8 @@ module.exports = {
'(public) @ember-data/adapter BuildURLMixin#urlForQuery',
'(public) @ember-data/adapter BuildURLMixin#urlForQueryRecord',
'(public) @ember-data/adapter BuildURLMixin#urlForUpdateRecord',
+ '(public) @ember-data/adapter/error @ember-data/adapter/error#errorsArrayToHash',
+ '(public) @ember-data/adapter/error @ember-data/adapter/error#errorsHashToArray',
'(public) @ember-data/adapter/json-api JSONAPIAdapter#buildQuery',
'(public) @ember-data/adapter/json-api JSONAPIAdapter#coalesceFindRequests',
'(public) @ember-data/adapter/rest RESTAdapter#buildQuery',
@@ -250,6 +253,7 @@ module.exports = {
'(public) @ember-data/legacy-compat SnapshotRecordArray#length',
'(public) @ember-data/legacy-compat SnapshotRecordArray#modelName',
'(public) @ember-data/legacy-compat SnapshotRecordArray#snapshots',
+ '(public) @ember-data/legacy-compat SnapshotRecordArray#type',
'(public) @ember-data/legacy-compat/builders @ember-data/legacy-compat/builders#findAll',
'(public) @ember-data/legacy-compat/builders @ember-data/legacy-compat/builders#findRecord',
'(public) @ember-data/legacy-compat/builders @ember-data/legacy-compat/builders#query',
@@ -437,6 +441,7 @@ module.exports = {
'(public) @ember-data/serializer/rest RESTSerializer#serialize',
'(public) @ember-data/serializer/rest RESTSerializer#serializeIntoHash',
'(public) @ember-data/serializer/rest RESTSerializer#serializePolymorphicType',
+ '(public) @ember-data/store @ember-data/store#normalizeModelName',
'(public) @ember-data/store @ember-data/store#recordIdentifierFor',
'(public) @ember-data/store @ember-data/store#setIdentifierForgetMethod',
'(public) @ember-data/store @ember-data/store#setIdentifierGenerationMethod',
@@ -528,6 +533,7 @@ module.exports = {
'(public) @ember-data/store NotificationManager#unsubscribe',
'(public) @ember-data/store RecordArray#isUpdating',
'(public) @ember-data/store RecordArray#save',
+ '(public) @ember-data/store RecordArray#type',
'(public) @ember-data/store RecordArray#update',
'(public) @ember-data/store RecordReference#id',
'(public) @ember-data/store RecordReference#identifier',
@@ -553,6 +559,7 @@ module.exports = {
'(public) @ember-data/store Snapshot#modelName',
'(public) @ember-data/store Snapshot#record',
'(public) @ember-data/store Snapshot#serialize',
+ '(public) @ember-data/store Snapshot#type',
'(public) @ember-data/store StableRecordIdentifier#id',
'(public) @ember-data/store StableRecordIdentifier#lid',
'(public) @ember-data/store StableRecordIdentifier#type',
@@ -566,6 +573,7 @@ module.exports = {
'(public) @ember-data/store Store#getReference',
'(public) @ember-data/store Store#getRequestStateService',
'(public) @ember-data/store Store#getSchemaDefinitionService',
+ '(public) @ember-data/store Store#hasRecordForId',
'(public) @ember-data/store Store#identifierCache',
'(public) @ember-data/store Store#identifierCache',
'(public) @ember-data/store Store#instantiateRecord (hook)',
@@ -595,23 +603,39 @@ module.exports = {
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_GRAPH',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_IDENTIFIERS',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_INSTANCE_CACHE',
- '(public) @warp-drive/build-config/debugging DebugLogging#LOG_METRIC_COUNTS',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_MUTATIONS',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_NOTIFICATIONS',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_OPERATIONS',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_PAYLOADS',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_REQUEST_STATUS',
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_REQUESTS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_A_USAGE',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_ARRAY_LIKE',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_COMPUTED_CHAINS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_EARLY_STATIC',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_EMBER_INFLECTOR',
- '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_LEGACY_IMPORTS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_HAS_RECORD',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_HELPERS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_JSON_API_FALLBACK',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_MANY_ARRAY_DUPLICATES',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_MODEL_REOPEN',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_NON_EXPLICIT_POLYMORPHISM',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_NON_STRICT_ID',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_NON_STRICT_TYPES',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_NON_UNIQUE_PAYLOADS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_PROMISE_PROXIES',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_RSVP_PROMISE',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_SAVE_PROMISE_ACCESS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_STORE_EXTENDS_EMBER_OBJECT',
- '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DISABLE_7X_DEPRECATIONS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_STORE_FIND',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_STRING_ARG_SCHEMAS',
+ '(public) @warp-drive/build-config/deprecations CurrentDeprecations#DISABLE_6X_DEPRECATIONS',
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#ENABLE_LEGACY_SCHEMA_SERVICE',
],
};
diff --git a/tests/docs/package.json b/tests/docs/package.json
index 913d05a9e6b..346ba9e65a3 100644
--- a/tests/docs/package.json
+++ b/tests/docs/package.json
@@ -1,6 +1,6 @@
{
"name": "docs-tests",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Provides tests for blueprints",
"repository": {
diff --git a/tests/ember-data__adapter/app/services/store.ts b/tests/ember-data__adapter/app/services/store.ts
index 987a8e3118d..2e360e8abb2 100644
--- a/tests/ember-data__adapter/app/services/store.ts
+++ b/tests/ember-data__adapter/app/services/store.ts
@@ -18,7 +18,12 @@ import type { StableRecordIdentifier } from '@warp-drive/core-types';
import type { TypeFromInstance } from '@warp-drive/core-types/record';
export default class Store extends BaseStore {
- requestManager = new RequestManager().use([LegacyNetworkHandler, Fetch]).useCache(CacheHandler);
+ constructor(args: unknown) {
+ super(args);
+ this.requestManager = new RequestManager();
+ this.requestManager.use([LegacyNetworkHandler, Fetch]);
+ this.requestManager.useCache(CacheHandler);
+ }
createSchemaService(): ReturnType {
return buildSchema(this);
diff --git a/tests/ember-data__adapter/package.json b/tests/ember-data__adapter/package.json
index 589bfb331c0..0fb10aa0f81 100644
--- a/tests/ember-data__adapter/package.json
+++ b/tests/ember-data__adapter/package.json
@@ -1,6 +1,6 @@
{
"name": "ember-data__adapter",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Tests for @ember-data/adapter",
"repository": {
@@ -85,16 +85,16 @@
"@ember-data/tracking": "workspace:*",
"@ember-data/unpublished-test-infra": "workspace:*",
"@ember/optional-features": "^2.1.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0 || ^4.0.0",
- "@embroider/addon-shim": "^1.9.0",
+ "@embroider/addon-shim": "^1.8.9",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/build-config": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
"@warp-drive/diagnostic": "workspace:*",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/ember-data__graph/ember-cli-build.js b/tests/ember-data__graph/ember-cli-build.js
index 436ede741f6..837d9d3e6d3 100644
--- a/tests/ember-data__graph/ember-cli-build.js
+++ b/tests/ember-data__graph/ember-cli-build.js
@@ -20,6 +20,9 @@ module.exports = async function (defaults) {
setConfig(app, __dirname, {
compatWith: process.env.EMBER_DATA_FULL_COMPAT ? '99.0' : null,
+ deprecations: {
+ DISABLE_6X_DEPRECATIONS: false,
+ },
});
app.import('node_modules/@warp-drive/diagnostic/dist/styles/dom-reporter.css');
diff --git a/tests/ember-data__graph/package.json b/tests/ember-data__graph/package.json
index 329e74c0052..24335ca3c12 100644
--- a/tests/ember-data__graph/package.json
+++ b/tests/ember-data__graph/package.json
@@ -1,6 +1,6 @@
{
"name": "ember-data__graph",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Provides tests for @ember-data/graph",
"keywords": [],
@@ -79,17 +79,17 @@
"@ember-data/unpublished-test-infra": "workspace:*",
"@ember/edition-utils": "^1.2.0",
"@ember/optional-features": "^2.1.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
- "@embroider/addon-shim": "^1.9.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/addon-shim": "^1.8.9",
+ "@embroider/macros": "^1.16.6",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@warp-drive/build-config": "workspace:*",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
"@warp-drive/diagnostic": "workspace:*",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/ember-data__graph/tests/integration/graph/diff-preservation-test.ts b/tests/ember-data__graph/tests/integration/graph/diff-preservation-test.ts
index 8cbe448ea0b..c83a198176d 100644
--- a/tests/ember-data__graph/tests/integration/graph/diff-preservation-test.ts
+++ b/tests/ember-data__graph/tests/integration/graph/diff-preservation-test.ts
@@ -726,165 +726,6 @@ module('Integration | Graph | Diff Preservation', function (hooks) {
'namespace apps relationship is correct'
);
});
- } else {
- deprecatedTest(
- 'updateRelationship operation from the collection side clear local state',
- { id: 'ember-data:deprecate-relationship-remote-update-clearing-local-state', count: 2, until: '6.0.0' },
- function (assert) {
- // tests that Many:Many, Many:One do not clear local state from
- // either side when updating the relationship from the Many side
- // we set the flag on the inverse to ensure that we detect this
- // from either side
- const { owner } = this;
-
- class App extends Model {
- @attr declare name: string;
- @hasMany('config', { async: false, inverse: 'app' }) declare configs: Config[];
- @hasMany('namespace', { async: false, inverse: 'apps' }) declare namespaces: Namespace | null;
- }
-
- class Namespace extends Model {
- @attr declare name: string;
- @hasMany('app', { async: false, inverse: 'namespaces' }) declare apps: App[];
- }
-
- class Config extends Model {
- @attr declare name: string;
- @belongsTo('app', { async: false, inverse: 'configs' }) declare app: App | null;
- }
-
- function identifier(type: string, id: string) {
- return store.identifierCache.getOrCreateRecordIdentifier({ type, id });
- }
-
- owner.register('model:app', App);
- owner.register('model:namespace', Namespace);
- owner.register('model:config', Config);
- const store = owner.lookup('service:store') as unknown as Store;
- const graph = graphFor(store);
- const appIdentifier = identifier('app', '1');
-
- // set initial state
- // one app, with 4 configs and 4 namespaces
- // each config belongs to the app
- // each namespace has the app and 2 more apps
- store._join(() => {
- // setup primary app relationships
- // this also convers the belongsTo side on config
- graph.push({
- op: 'updateRelationship',
- field: 'configs',
- record: appIdentifier,
- value: {
- data: [
- { type: 'config', id: '1' },
- { type: 'config', id: '2' },
- { type: 'config', id: '3' },
- { type: 'config', id: '4' },
- ],
- },
- });
- graph.push({
- op: 'updateRelationship',
- field: 'namespaces',
- record: appIdentifier,
- value: {
- data: [
- { type: 'namespace', id: '1' },
- { type: 'namespace', id: '2' },
- { type: 'namespace', id: '3' },
- { type: 'namespace', id: '4' },
- ],
- },
- });
- // setup namespace relationships
- ['1', '2', '3', '4'].forEach((id) => {
- graph.push({
- op: 'updateRelationship',
- field: 'apps',
- record: identifier('namespace', id),
- value: {
- data: [
- { type: 'app', id: '1' },
- { type: 'app', id: '2' },
- { type: 'app', id: '3' },
- ],
- },
- });
- });
- });
-
- // mutate app:1.configs, adding config:5
- // mutate app:1.namespaces, adding namespace:5
- store._join(() => {
- graph.update({
- op: 'addToRelatedRecords',
- field: 'configs',
- record: appIdentifier,
- value: identifier('config', '5'),
- });
- graph.update({
- op: 'addToRelatedRecords',
- field: 'namespaces',
- record: appIdentifier,
- value: identifier('namespace', '5'),
- });
- });
-
- // push the exact same remote state to the graph again
- // for app:1
- store._join(() => {
- // setup primary app relationships
- // this also convers the belongsTo side on config
- graph.push({
- op: 'updateRelationship',
- field: 'configs',
- record: appIdentifier,
- value: {
- data: [
- { type: 'config', id: '1' },
- { type: 'config', id: '2' },
- { type: 'config', id: '3' },
- { type: 'config', id: '4' },
- ],
- },
- });
- graph.push({
- op: 'updateRelationship',
- field: 'namespaces',
- record: appIdentifier,
- value: {
- data: [
- { type: 'namespace', id: '1' },
- { type: 'namespace', id: '2' },
- { type: 'namespace', id: '3' },
- { type: 'namespace', id: '4' },
- ],
- },
- });
- });
-
- // we should have both not err'd and still have cleared the mutated state
- const { data: configs } = graph.getData(appIdentifier, 'configs');
- assert.arrayStrictEquals(
- configs,
- [identifier('config', '1'), identifier('config', '2'), identifier('config', '3'), identifier('config', '4')],
- 'configs are correct'
- );
-
- const { data: namespaces } = graph.getData(appIdentifier, 'namespaces');
- assert.arrayStrictEquals(
- namespaces,
- [
- identifier('namespace', '1'),
- identifier('namespace', '2'),
- identifier('namespace', '3'),
- identifier('namespace', '4'),
- ],
- 'namespaces are correct'
- );
- }
- );
}
test('updateRelationship operation from the collection side does not clear local state when `resetOnRemoteUpdate: false` is set', function (assert) {
diff --git a/tests/ember-data__graph/tests/test-helper.ts b/tests/ember-data__graph/tests/test-helper.ts
index be934515e51..c380c8a49c8 100644
--- a/tests/ember-data__graph/tests/test-helper.ts
+++ b/tests/ember-data__graph/tests/test-helper.ts
@@ -17,7 +17,6 @@ configure();
setApplication(Application.create(config.APP));
void start({
tryCatch: true,
- // debug: true,
groupLogs: false,
instrument: true,
hideReport: true,
diff --git a/tests/ember-data__json-api/package.json b/tests/ember-data__json-api/package.json
index de95d13f11a..715334da9c0 100644
--- a/tests/ember-data__json-api/package.json
+++ b/tests/ember-data__json-api/package.json
@@ -1,6 +1,6 @@
{
"name": "ember-data__json-api",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Provides tests for @ember-data/json-api",
"keywords": [],
@@ -83,10 +83,10 @@
"@ember-data/unpublished-test-infra": "workspace:*",
"@ember/edition-utils": "^1.2.0",
"@ember/optional-features": "^2.1.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
- "@embroider/addon-shim": "^1.9.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/addon-shim": "^1.8.9",
+ "@embroider/macros": "^1.16.6",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
@@ -94,7 +94,7 @@
"@warp-drive/holodeck": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
"@warp-drive/build-config": "workspace:*",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/ember-data__model/package.json b/tests/ember-data__model/package.json
index aed53d139fa..fb39c597020 100644
--- a/tests/ember-data__model/package.json
+++ b/tests/ember-data__model/package.json
@@ -1,6 +1,6 @@
{
"name": "ember-data__model",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Tests for @ember-data/model",
"repository": {
@@ -72,17 +72,17 @@
"@ember-data/store": "workspace:*",
"@ember-data/tracking": "workspace:*",
"@ember/optional-features": "^2.1.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
- "@embroider/addon-shim": "^1.9.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/addon-shim": "^1.8.9",
+ "@embroider/macros": "^1.16.6",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/diagnostic": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
"@warp-drive/build-config": "workspace:*",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/ember-data__request/package.json b/tests/ember-data__request/package.json
index b3e6e74fcfe..e8486d5e74b 100644
--- a/tests/ember-data__request/package.json
+++ b/tests/ember-data__request/package.json
@@ -1,6 +1,6 @@
{
"name": "ember-data__request",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Provides tests for @ember-data/request",
"keywords": [],
@@ -51,9 +51,9 @@
"@ember-data/request-utils": "workspace:*",
"@ember/edition-utils": "^1.2.0",
"@ember/optional-features": "^2.1.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
- "@embroider/addon-shim": "^1.9.0",
+ "@embroider/addon-shim": "^1.8.9",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
@@ -61,8 +61,8 @@
"@warp-drive/diagnostic": "workspace:*",
"@warp-drive/holodeck": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
- "bun-types": "^1.2.2",
- "ember-auto-import": "2.10.0",
+ "bun-types": "^1.1.30",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/ember-data__serializer/package.json b/tests/ember-data__serializer/package.json
index c175c029329..4d98c8b5472 100644
--- a/tests/ember-data__serializer/package.json
+++ b/tests/ember-data__serializer/package.json
@@ -1,6 +1,6 @@
{
"name": "ember-data__serializer",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Tests for the @ember-data/serializer package",
"repository": {
@@ -78,14 +78,14 @@
"@ember-data/tracking": "workspace:*",
"@ember-data/unpublished-test-infra": "workspace:*",
"@ember/optional-features": "^2.1.0",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@warp-drive/core-types": "workspace:*",
"@warp-drive/internal-config": "workspace:*",
"@warp-drive/build-config": "workspace:*",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/embroider-basic-compat/package.json b/tests/embroider-basic-compat/package.json
index 506550e0d7f..13531188a48 100644
--- a/tests/embroider-basic-compat/package.json
+++ b/tests/embroider-basic-compat/package.json
@@ -1,6 +1,6 @@
{
"name": "embroider-basic-compat",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Small description for embroider-basic-compat goes here",
"repository": {
@@ -76,12 +76,12 @@
"@ember-data/unpublished-test-infra": "workspace:*",
"@ember/optional-features": "^2.1.0",
"@ember/string": "^3.1.1",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
- "@embroider/compat": "^3.8.0",
- "@embroider/core": "^3.5.0",
- "@embroider/webpack": "^4.0.9",
- "ember-auto-import": "2.10.0",
+ "@embroider/compat": "^3.6.5",
+ "@embroider/core": "^3.4.19",
+ "@embroider/webpack": "^4.0.8",
+ "ember-auto-import": "^2.8.1",
"ember-data": "workspace:*",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
"webpack": "^5.92.0",
diff --git a/tests/fastboot/package.json b/tests/fastboot/package.json
index 31bfd619928..c35948db44a 100644
--- a/tests/fastboot/package.json
+++ b/tests/fastboot/package.json
@@ -1,6 +1,6 @@
{
"name": "fastboot-test-app",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "Small description for fastboot-test-app goes here",
"repository": {
@@ -24,7 +24,7 @@
},
"dependencies": {
"@ember-data/unpublished-test-infra": "workspace:*",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-data": "workspace:*",
"pnpm-sync-dependencies-meta-injected": "0.0.14",
"webpack": "^5.92.0"
@@ -66,7 +66,7 @@
"@babel/runtime": "^7.24.5",
"@ember/optional-features": "^2.1.0",
"@ember/string": "^3.1.1",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
diff --git a/tests/full-data-asset-size-app/package.json b/tests/full-data-asset-size-app/package.json
index ea6e1f50b68..489d6fd6e28 100644
--- a/tests/full-data-asset-size-app/package.json
+++ b/tests/full-data-asset-size-app/package.json
@@ -1,6 +1,6 @@
{
"name": "full-data-asset-size-app",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "An app for determining asset-size of the meta package",
"repository": {
@@ -30,7 +30,7 @@
"@ember/optional-features": "^2.1.0",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
"ember-cli-dependency-checker": "^3.3.2",
diff --git a/tests/main/ember-cli-build.js b/tests/main/ember-cli-build.js
index 486676f5862..fa7c18ba6c6 100644
--- a/tests/main/ember-cli-build.js
+++ b/tests/main/ember-cli-build.js
@@ -57,16 +57,8 @@ module.exports = async function (defaults) {
setConfig(app, __dirname, {
compatWith: process.env.EMBER_DATA_FULL_COMPAT ? '99.0' : null,
- debug: {
- // LOG_GRAPH: true,
- // LOG_IDENTIFIERS: true,
- // LOG_NOTIFICATIONS: true,
- // LOG_INSTANCE_CACHE: true,
- // LOG_MUTATIONS: true,
- // LOG_PAYLOADS: true,
- // LOG_REQUESTS: true,
- // LOG_REQUEST_STATUS: true,
- // LOG_OPERATIONS: true,
+ deprecations: {
+ DISABLE_6X_DEPRECATIONS: false,
},
});
diff --git a/tests/main/package.json b/tests/main/package.json
index bde0d1867be..42c3dab4247 100644
--- a/tests/main/package.json
+++ b/tests/main/package.json
@@ -1,6 +1,6 @@
{
"name": "main-test-app",
- "version": "5.4.0-alpha.136",
+ "version": "4.13.0-alpha.3",
"private": true,
"description": "A data layer for your Ember applications.",
"repository": {
@@ -96,9 +96,9 @@
"@ember/edition-utils": "^1.2.0",
"@ember/optional-features": "^2.1.0",
"@ember/string": "^3.1.1",
- "@ember/test-helpers": "5.1.0",
+ "@ember/test-helpers": "4.0.4",
"@ember/test-waiters": "^3.1.0",
- "@embroider/macros": "^1.16.10",
+ "@embroider/macros": "^1.16.6",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"@glint/core": "1.5.0",
@@ -117,7 +117,7 @@
"broccoli-string-replace": "^0.1.2",
"broccoli-test-helper": "^2.0.0",
"broccoli-uglify-sourcemap": "^4.0.0",
- "ember-auto-import": "2.10.0",
+ "ember-auto-import": "^2.8.1",
"ember-cached-decorator-polyfill": "^1.0.2",
"ember-cli": "~5.12.0",
"ember-cli-babel": "^8.2.0",
@@ -133,7 +133,7 @@
"ember-inflector": "4.0.3",
"ember-load-initializers": "^2.1.2",
"ember-maybe-import-regenerator": "^1.0.0",
- "ember-template-imports": "4.3.0",
+ "ember-template-imports": "4.1.3",
"ember-qunit": "8.0.2",
"ember-resolver": "^11.0.1",
"ember-source": "~5.12.0",
diff --git a/tests/main/tests/acceptance/relationships/has-many-test.js b/tests/main/tests/acceptance/relationships/has-many-test.js
index 4548a68c6d3..7bd2f103bbb 100644
--- a/tests/main/tests/acceptance/relationships/has-many-test.js
+++ b/tests/main/tests/acceptance/relationships/has-many-test.js
@@ -17,6 +17,8 @@ import JSONAPIAdapter from '@ember-data/adapter/json-api';
import Model, { attr, belongsTo, hasMany } from '@ember-data/model';
import { LEGACY_SUPPORT } from '@ember-data/model/-private';
import JSONAPISerializer from '@ember-data/serializer/json-api';
+import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test';
+import { DEPRECATE_ARRAY_LIKE } from '@warp-drive/build-config/deprecations';
class Person extends Model {
@attr()
@@ -819,7 +821,13 @@ module('autotracking has-many', function (hooks) {
}
get sortedChildren() {
- return this.children.slice().sort((a, b) => (a.name > b.name ? 1 : -1));
+ if (DEPRECATE_ARRAY_LIKE) {
+ const result = this.children.sortBy('name');
+ assert.expectDeprecation({ id: 'ember-data:deprecate-array-like' });
+ return result;
+ } else {
+ return this.children.slice().sort((a, b) => (a.name > b.name ? 1 : -1));
+ }
}
@action
@@ -864,6 +872,219 @@ module('autotracking has-many', function (hooks) {
assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children');
});
+ deprecatedTest(
+ 'We can re-render hasMany w/PromiseManyArray.sortBy',
+ { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 3 },
+ async function (assert) {
+ class ChildrenList extends Component {
+ @service store;
+
+ get sortedChildren() {
+ const result = this.args.person.children.sortBy('name');
+ assert.expectDeprecation({ id: 'ember-data:deprecate-array-like' });
+ return result;
+ }
+
+ @action
+ createChild() {
+ const parent = this.args.person;
+ const name = 'RGB';
+ this.store.createRecord('person', { name, parent });
+ }
+ }
+
+ const layout = hbs`
+
+
+ {{this.sortedChildren.length}}
+
+ {{#each this.sortedChildren as |child|}}
+ - {{child.name}}
+ {{/each}}
+
+ `;
+ this.owner.register('component:children-list', setComponentTemplate(layout, ChildrenList));
+
+ store.createRecord('person', { id: '1', name: 'Doodad' });
+ this.person = store.peekRecord('person', '1');
+
+ await render(hbs``);
+
+ let names = findAll('li').map((e) => e.textContent);
+
+ assert.deepEqual(names, [], 'rendered no children');
+
+ await click('#createChild');
+
+ names = findAll('li').map((e) => e.textContent);
+ assert.deepEqual(names, ['RGB'], 'rendered 1 child');
+
+ await click('#createChild');
+
+ names = findAll('li').map((e) => e.textContent);
+ assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children');
+ }
+ );
+
+ deprecatedTest(
+ 'We can re-render hasMany with sort computed macro on PromiseManyArray',
+ { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 3 },
+ async function (assert) {
+ class ChildrenList extends Component {
+ @service store;
+
+ sortProperties = ['name'];
+ @sort('args.person.children', 'sortProperties') sortedChildren;
+
+ @action
+ createChild() {
+ const parent = this.args.person;
+ const name = 'RGB';
+ this.store.createRecord('person', { name, parent });
+ }
+ }
+
+ const layout = hbs`
+
+
+ {{this.sortedChildren.length}}
+
+ {{#each this.sortedChildren as |child|}}
+ - {{child.name}}
+ {{/each}}
+
+ `;
+ this.owner.register('component:children-list', setComponentTemplate(layout, ChildrenList));
+
+ store.createRecord('person', { id: '1', name: 'Doodad' });
+ this.person = store.peekRecord('person', '1');
+
+ await render(hbs``);
+
+ let names = findAll('li').map((e) => e.textContent);
+
+ assert.deepEqual(names, [], 'rendered no children');
+
+ await click('#createChild');
+
+ names = findAll('li').map((e) => e.textContent);
+ assert.deepEqual(names, ['RGB'], 'rendered 1 child');
+
+ await click('#createChild');
+
+ names = findAll('li').map((e) => e.textContent);
+ assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children');
+ assert.expectDeprecation({ id: 'ember-data:no-a-with-array-like', count: 3 });
+ }
+ );
+
+ deprecatedTest(
+ 'We can re-render hasMany with PromiseManyArray.objectAt',
+ { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 6 },
+ async function (assert) {
+ let calls = 0;
+ class ChildrenList extends Component {
+ @service store;
+
+ get firstChild() {
+ const result = this.args.person.children.objectAt(0);
+ assert.expectDeprecation({ id: 'ember-data:deprecate-array-like' });
+ return result;
+ }
+
+ get lastChild() {
+ const result = this.args.person.children.objectAt(-1);
+ assert.expectDeprecation({ id: 'ember-data:deprecate-array-like' });
+ return result;
+ }
+
+ @action
+ createChild() {
+ const parent = this.args.person;
+ const name = 'RGB ' + calls++;
+ this.store.createRecord('person', { name, parent });
+ }
+ }
+
+ const layout = hbs`
+
+
+ {{this.firstChild.name}}
+ {{this.lastChild.name}}
+ `;
+ this.owner.register('component:children-list', setComponentTemplate(layout, ChildrenList));
+
+ store.createRecord('person', { id: '1', name: 'Doodad' });
+ this.person = store.peekRecord('person', '1');
+
+ await render(hbs``);
+
+ assert.dom('h2').hasText('', 'rendered no children');
+
+ await click('#createChild');
+
+ assert.dom('h2').hasText('RGB 0', 'renders first child');
+ assert.dom('h3').hasText('RGB 0', 'renders last child');
+
+ await click('#createChild');
+
+ assert.dom('h2').hasText('RGB 0', 'renders first child');
+ assert.dom('h3').hasText('RGB 1', 'renders last child');
+ }
+ );
+
+ deprecatedTest(
+ 'We can re-render hasMany with PromiseManyArray.map',
+ { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 3 },
+ async function (assert) {
+ class ChildrenList extends Component {
+ @service store;
+
+ get children() {
+ return this.args.person.children.map((child) => child);
+ }
+
+ @action
+ createChild() {
+ const parent = this.args.person;
+ const name = 'RGB';
+ this.store.createRecord('person', { name, parent });
+ }
+ }
+
+ const layout = hbs`
+
+
+ {{this.children.length}}
+
+ {{#each this.children as |child|}}
+ - {{child.name}}
+ {{/each}}
+
+ `;
+ this.owner.register('component:children-list', setComponentTemplate(layout, ChildrenList));
+
+ store.createRecord('person', { id: '1', name: 'Doodad' });
+ this.person = store.peekRecord('person', '1');
+
+ await render(hbs``);
+
+ let names = findAll('li').map((e) => e.textContent);
+
+ assert.deepEqual(names, [], 'rendered no children');
+
+ await click('#createChild');
+
+ names = findAll('li').map((e) => e.textContent);
+ assert.deepEqual(names, ['RGB'], 'rendered 1 child');
+
+ await click('#createChild');
+
+ names = findAll('li').map((e) => e.textContent);
+ assert.deepEqual(names, ['RGB', 'RGB'], 'rendered 2 children');
+ }
+ );
+
test('We can re-render hasMany', async function (assert) {
class ChildrenList extends Component {
@service store;
diff --git a/tests/main/tests/acceptance/tracking-promise-flags-test.js b/tests/main/tests/acceptance/tracking-promise-flags-test.js
new file mode 100644
index 00000000000..f3ae8ec1117
--- /dev/null
+++ b/tests/main/tests/acceptance/tracking-promise-flags-test.js
@@ -0,0 +1,69 @@
+import { render, settled } from '@ember/test-helpers';
+
+import { module } from 'qunit';
+
+import { hbs } from 'ember-cli-htmlbars';
+import { setupRenderingTest } from 'ember-qunit';
+
+import JSONAPIAdapter from '@ember-data/adapter/json-api';
+import Model, { attr } from '@ember-data/model';
+import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test';
+
+class Widget extends Model {
+ @attr name;
+}
+
+module('acceptance/tracking-promise-flags', function (hooks) {
+ setupRenderingTest(hooks);
+
+ hooks.beforeEach(function () {
+ const { owner } = this;
+ owner.register('model:widget', Widget);
+ owner.register(
+ 'serializer:application',
+ class {
+ normalizeResponse = (_, __, data) => data;
+ static create() {
+ return new this();
+ }
+ }
+ );
+ });
+
+ deprecatedTest(
+ 'can track isPending',
+ { id: 'ember-data:deprecate-promise-proxies', until: '5.0', count: 6 },
+ async function (assert) {
+ const { owner } = this;
+ let resolve;
+ class TestAdapter extends JSONAPIAdapter {
+ findRecord() {
+ return new Promise((r) => {
+ resolve = r;
+ });
+ }
+ }
+ owner.register('adapter:application', TestAdapter);
+ const store = owner.lookup('service:store');
+ store.DISABLE_WAITER = true;
+ this.model = store.findRecord('widget', '1');
+
+ await render(hbs`{{#if this.model.isPending}}Pending{{else}}{{this.model.name}}{{/if}}`);
+
+ assert.dom().containsText('Pending');
+
+ resolve({
+ data: {
+ id: '1',
+ type: 'widget',
+ attributes: {
+ name: 'Contraption',
+ },
+ },
+ });
+ await settled();
+
+ assert.dom().containsText('Contraption');
+ }
+ );
+});
diff --git a/tests/main/tests/deprecations/deprecate-early-static-test.js b/tests/main/tests/deprecations/deprecate-early-static-test.js
new file mode 100644
index 00000000000..661c9a57b1e
--- /dev/null
+++ b/tests/main/tests/deprecations/deprecate-early-static-test.js
@@ -0,0 +1,63 @@
+import { module } from 'qunit';
+
+import { setupTest } from 'ember-qunit';
+
+import Model from '@ember-data/model';
+import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test';
+
+module('Deprecations', function (hooks) {
+ setupTest(hooks);
+
+ const StaticModelMethods = [
+ { name: 'typeForRelationship', count: 3 },
+ { name: 'inverseFor', count: 5 },
+ { name: '_findInverseFor', count: 3 },
+ { name: 'eachRelationship', count: 3 },
+ { name: 'eachRelatedType', count: 3 },
+ { name: 'determineRelationshipType', count: 1 },
+ { name: 'eachAttribute', count: 2 },
+ { name: 'eachTransformedAttribute', count: 4 },
+ { name: 'toString', count: 1 },
+ ];
+ const StaticModelGetters = [
+ { name: 'inverseMap', count: 1 },
+ { name: 'relationships', count: 3 },
+ { name: 'relationshipNames', count: 1 },
+ { name: 'relatedTypes', count: 2 },
+ { name: 'relationshipsByName', count: 2 },
+ { name: 'relationshipsObject', count: 1 },
+ { name: 'fields', count: 1 },
+ { name: 'attributes', count: 1 },
+ { name: 'transformedAttributes', count: 3 },
+ ];
+
+ function checkDeprecationForProp(prop) {
+ deprecatedTest(
+ `Accessing static prop ${prop.name} is deprecated`,
+ { id: 'ember-data:deprecate-early-static', until: '5.0', count: prop.count },
+ function (assert) {
+ class Post extends Model {}
+ Post[prop.name];
+ assert.ok(true);
+ }
+ );
+ }
+ function checkDeprecationForMethod(method) {
+ deprecatedTest(
+ `Accessing static method ${method.name} is deprecated`,
+ { id: 'ember-data:deprecate-early-static', until: '5.0', count: method.count },
+ function (assert) {
+ class Post extends Model {}
+ try {
+ Post[method.name]();
+ } catch {
+ // do nothing
+ }
+ assert.ok(true);
+ }
+ );
+ }
+
+ StaticModelGetters.forEach(checkDeprecationForProp);
+ StaticModelMethods.forEach(checkDeprecationForMethod);
+});
diff --git a/tests/main/tests/deprecations/deprecate-helpers-test.js b/tests/main/tests/deprecations/deprecate-helpers-test.js
new file mode 100644
index 00000000000..e3b09457598
--- /dev/null
+++ b/tests/main/tests/deprecations/deprecate-helpers-test.js
@@ -0,0 +1,48 @@
+import { module } from 'qunit';
+
+import { setupTest } from 'ember-qunit';
+
+import { errorsArrayToHash, errorsHashToArray } from '@ember-data/adapter/error';
+import { normalizeModelName } from '@ember-data/store';
+import { normalizeModelName as _privateNormalize } from '@ember-data/store/-private';
+import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test';
+
+module('Deprecations', function (hooks) {
+ setupTest(hooks);
+
+ deprecatedTest(
+ `Calling normalizeModelName`,
+ { id: 'ember-data:deprecate-normalize-modelname-helper', until: '5.0', count: 1 },
+ function (assert) {
+ normalizeModelName('user');
+ assert.ok(true);
+ }
+ );
+
+ deprecatedTest(
+ `Calling normalizeModelName imported from private`,
+ { id: 'ember-data:deprecate-normalize-modelname-helper', until: '5.0', count: 1 },
+ function (assert) {
+ _privateNormalize('user');
+ assert.ok(true);
+ }
+ );
+
+ deprecatedTest(
+ `Calling errorsArrayToHash`,
+ { id: 'ember-data:deprecate-errors-array-to-hash-helper', until: '5.0', count: 1 },
+ function (assert) {
+ errorsArrayToHash([]);
+ assert.ok(true);
+ }
+ );
+
+ deprecatedTest(
+ `Calling errorsHashToArray`,
+ { id: 'ember-data:deprecate-errors-hash-to-array-helper', until: '5.0', count: 1 },
+ function (assert) {
+ errorsHashToArray({});
+ assert.ok(true);
+ }
+ );
+});
diff --git a/tests/main/tests/deprecations/deprecate-reopen-class-test.js b/tests/main/tests/deprecations/deprecate-reopen-class-test.js
new file mode 100644
index 00000000000..9e4e5dd1f2f
--- /dev/null
+++ b/tests/main/tests/deprecations/deprecate-reopen-class-test.js
@@ -0,0 +1,30 @@
+import { module } from 'qunit';
+
+import { setupTest } from 'ember-qunit';
+
+import Model from '@ember-data/model';
+import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test';
+
+module('Deprecations', function (hooks) {
+ setupTest(hooks);
+
+ deprecatedTest(
+ `Calling on natively extended class`,
+ { id: 'ember-data:deprecate-model-reopenclass', until: '5.0', count: 1 },
+ function (assert) {
+ class Post extends Model {}
+ Post.reopenClass({});
+ assert.ok(true);
+ }
+ );
+
+ deprecatedTest(
+ `Calling on classic extended class`,
+ { id: 'ember-data:deprecate-model-reopenclass', until: '5.0', count: 1 },
+ function (assert) {
+ const Post = Model.extend();
+ Post.reopenClass({});
+ assert.ok(true);
+ }
+ );
+});
diff --git a/tests/main/tests/deprecations/deprecate-reopen-test.js b/tests/main/tests/deprecations/deprecate-reopen-test.js
new file mode 100644
index 00000000000..4cf4ac2c847
--- /dev/null
+++ b/tests/main/tests/deprecations/deprecate-reopen-test.js
@@ -0,0 +1,30 @@
+import { module } from 'qunit';
+
+import { setupTest } from 'ember-qunit';
+
+import Model from '@ember-data/model';
+import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test';
+
+module('Deprecations', function (hooks) {
+ setupTest(hooks);
+
+ deprecatedTest(
+ `Calling on natively extended class`,
+ { id: 'ember-data:deprecate-model-reopen', until: '5.0', count: 1 },
+ function (assert) {
+ class Post extends Model {}
+ Post.reopen({});
+ assert.ok(true);
+ }
+ );
+
+ deprecatedTest(
+ `Calling on classic extended class`,
+ { id: 'ember-data:deprecate-model-reopen', until: '5.0', count: 1 },
+ function (assert) {
+ const Post = Model.extend();
+ Post.reopen({});
+ assert.ok(true);
+ }
+ );
+});
diff --git a/tests/main/tests/integration/cache/spec-cache-errors-test.ts b/tests/main/tests/integration/cache/spec-cache-errors-test.ts
index 144446857a2..2bf29f0871c 100644
--- a/tests/main/tests/integration/cache/spec-cache-errors-test.ts
+++ b/tests/main/tests/integration/cache/spec-cache-errors-test.ts
@@ -114,11 +114,11 @@ class TestCache implements Cache {
calculateChanges?: boolean
): void | string[] {
if (!this._data.has(identifier)) {
- this.wrapper.notifyChange(identifier, 'added', null);
+ this.wrapper.notifyChange(identifier, 'added');
}
this._data.set(identifier, data);
- this.wrapper.notifyChange(identifier, 'attributes', null);
- this.wrapper.notifyChange(identifier, 'relationships', null);
+ this.wrapper.notifyChange(identifier, 'attributes');
+ this.wrapper.notifyChange(identifier, 'relationships');
}
clientDidCreate(identifier: StableRecordIdentifier, options?: Record): Record {
this._isNew = true;
@@ -354,7 +354,7 @@ module('integration/record-data Custom Cache (v2) Errors', function (hooks) {
},
},
];
- storeWrapper.notifyChange(identifier, 'errors', null);
+ storeWrapper.notifyChange(identifier, 'errors');
nameError = person.errors.errorsFor('firstName').objectAt(0);
@@ -362,7 +362,7 @@ module('integration/record-data Custom Cache (v2) Errors', function (hooks) {
assert.false(person.isValid, 'person is not valid');
errorsToReturn = [];
- storeWrapper.notifyChange(identifier, 'errors', null);
+ storeWrapper.notifyChange(identifier, 'errors');
assert.strictEqual(person.errors.errorsFor('firstName').length, 0, 'no errors on name');
assert.true(person.isValid, 'person is valid');
@@ -376,7 +376,7 @@ module('integration/record-data Custom Cache (v2) Errors', function (hooks) {
},
},
];
- storeWrapper.notifyChange(identifier, 'errors', null);
+ storeWrapper.notifyChange(identifier, 'errors');
assert.false(person.isValid, 'person is not valid');
diff --git a/tests/main/tests/integration/cache/spec-cache-state-test.ts b/tests/main/tests/integration/cache/spec-cache-state-test.ts
index d3605790ea3..b82362cb4b3 100644
--- a/tests/main/tests/integration/cache/spec-cache-state-test.ts
+++ b/tests/main/tests/integration/cache/spec-cache-state-test.ts
@@ -129,11 +129,11 @@ class TestCache implements Cache {
calculateChanges?: boolean
): void | string[] {
if (!this._data.has(identifier)) {
- this._storeWrapper.notifyChange(identifier, 'added', null);
+ this._storeWrapper.notifyChange(identifier, 'added');
}
this._data.set(identifier, data);
- this._storeWrapper.notifyChange(identifier, 'attributes', null);
- this._storeWrapper.notifyChange(identifier, 'relationships', null);
+ this._storeWrapper.notifyChange(identifier, 'attributes');
+ this._storeWrapper.notifyChange(identifier, 'relationships');
}
mutate(operation: LocalRelationshipOperation): void {
throw new Error('Method not implemented.');
@@ -145,7 +145,7 @@ class TestCache implements Cache {
clientDidCreate(identifier: StableRecordIdentifier, options?: Record