@warp-drive/core 5.9.0-beta.0 → 5.9.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/declarations/aliases-BWYW5EhX.d.ts +17 -0
- package/declarations/aliases-BWYW5EhX.d.ts.map +1 -0
- package/declarations/build-config/babel-macros.d.ts +2 -1
- package/declarations/build-config/canary-features.d.ts +1 -1
- package/declarations/build-config/debugging.d.ts +1 -1
- package/declarations/build-config/deprecations.d.ts +1 -1
- package/declarations/build-config/env.d.ts +2 -1
- package/declarations/build-config/macros.d.ts +2 -1
- package/declarations/build-config.d.ts +2 -18
- package/declarations/change-C7kqv2kW.d.ts +41 -0
- package/declarations/change-C7kqv2kW.d.ts.map +1 -0
- package/declarations/concepts-LHsLJZMl.d.ts +77 -0
- package/declarations/concepts-LHsLJZMl.d.ts.map +1 -0
- package/declarations/configure-B_F639tN.d.ts +134 -0
- package/declarations/configure-B_F639tN.d.ts.map +1 -0
- package/declarations/configure.d.ts +2 -7
- package/declarations/document-BEn91SwT.d.ts +129 -0
- package/declarations/document-BEn91SwT.d.ts.map +1 -0
- package/declarations/error-50OSEhsF.d.ts +77 -0
- package/declarations/error-50OSEhsF.d.ts.map +1 -0
- package/declarations/fields-FM7peCiM.d.ts +2327 -0
- package/declarations/fields-FM7peCiM.d.ts.map +1 -0
- package/declarations/graph/-private.d.ts +2 -13
- package/declarations/graph-DLqYXP2z.d.ts +186 -0
- package/declarations/graph-DLqYXP2z.d.ts.map +1 -0
- package/declarations/identifier-DQnqOSJK.d.ts +112 -0
- package/declarations/identifier-DQnqOSJK.d.ts.map +1 -0
- package/declarations/index.d.ts +2 -96
- package/declarations/internal-Cz-893zD.d.ts +8690 -0
- package/declarations/internal-Cz-893zD.d.ts.map +1 -0
- package/declarations/json-api-raw-C5APJHHB.d.ts +378 -0
- package/declarations/json-api-raw-C5APJHHB.d.ts.map +1 -0
- package/declarations/mutations-CqtoGB4t.d.ts +165 -0
- package/declarations/mutations-CqtoGB4t.d.ts.map +1 -0
- package/declarations/operations-D1qA6r0_.d.ts +244 -0
- package/declarations/operations-D1qA6r0_.d.ts.map +1 -0
- package/declarations/params-DrEx3bf8.d.ts +41 -0
- package/declarations/params-DrEx3bf8.d.ts.map +1 -0
- package/declarations/raw-D5hnjiq7.d.ts +31 -0
- package/declarations/raw-D5hnjiq7.d.ts.map +1 -0
- package/declarations/reactive/-private.d.ts +2 -2
- package/declarations/reactive.d.ts +2 -286
- package/declarations/record-D3QQcaIR.d.ts +254 -0
- package/declarations/record-D3QQcaIR.d.ts.map +1 -0
- package/declarations/relationship-CmHzMPKf.d.ts +65 -0
- package/declarations/relationship-CmHzMPKf.d.ts.map +1 -0
- package/declarations/request.d.ts +2 -52
- package/declarations/signals/-leaked.d.ts +63 -2
- package/declarations/signals/-leaked.d.ts.map +1 -0
- package/declarations/store/-private.d.ts +2 -24
- package/declarations/store.d.ts +8358 -2
- package/declarations/store.d.ts.map +1 -0
- package/declarations/symbols-DjRDCeX0.d.ts +88 -0
- package/declarations/symbols-DjRDCeX0.d.ts.map +1 -0
- package/declarations/types/-private.d.ts +71 -17
- package/declarations/types/-private.d.ts.map +1 -0
- package/declarations/types/cache/aliases.d.ts +2 -1
- package/declarations/types/cache/change.d.ts +2 -6
- package/declarations/types/cache/mutations.d.ts +2 -38
- package/declarations/types/cache/operations.d.ts +2 -168
- package/declarations/types/cache/relationship.d.ts +2 -13
- package/declarations/types/cache.d.ts +2 -429
- package/declarations/types/graph.d.ts +2 -47
- package/declarations/types/identifier.d.ts +2 -86
- package/declarations/types/json/raw.d.ts +2 -6
- package/declarations/types/params.d.ts +2 -8
- package/declarations/types/record.d.ts +2 -269
- package/declarations/types/request.d.ts +2 -367
- package/declarations/types/runtime.d.ts +53 -12
- package/declarations/types/runtime.d.ts.map +1 -0
- package/declarations/types/schema/concepts.d.ts +2 -23
- package/declarations/types/schema/fields.d.ts +2 -2200
- package/declarations/types/schema/fields.type-test.d.ts +1 -1
- package/declarations/types/schema/schema-service.d.ts +2 -380
- package/declarations/types/spec/document.d.ts +2 -67
- package/declarations/types/spec/error.d.ts +2 -18
- package/declarations/types/spec/json-api-raw.d.ts +2 -139
- package/declarations/types/symbols.d.ts +2 -78
- package/declarations/types/utils.d.ts +2 -5
- package/declarations/types.d.ts +3 -11
- package/declarations/utils/string.d.ts +51 -44
- package/declarations/utils/string.d.ts.map +1 -0
- package/declarations/utils-BtURiy8l.d.ts +24 -0
- package/declarations/utils-BtURiy8l.d.ts.map +1 -0
- package/dist/-private-DMzvF_Q6.js +120 -0
- package/dist/-private-DMzvF_Q6.js.map +1 -0
- package/dist/-private-HjaQ1wp1.js +1695 -0
- package/dist/-private-HjaQ1wp1.js.map +1 -0
- package/dist/-private-b1g4dhl0.js +22 -0
- package/dist/-private-b1g4dhl0.js.map +1 -0
- package/dist/build-config/babel-macros.js +3 -1
- package/dist/build-config/canary-features.js +1 -1
- package/dist/build-config/debugging.js +1 -1
- package/dist/build-config/deprecations.js +1 -1
- package/dist/build-config/env.js +3 -1
- package/dist/build-config/macros.js +3 -1
- package/dist/build-config.js +3 -1
- package/dist/configure.js +3 -2
- package/dist/graph/-private.js +2183 -3192
- package/dist/graph/-private.js.map +1 -0
- package/dist/index.js +3 -8
- package/dist/reactive/-private.js +3 -1
- package/dist/reactive.js +5 -4
- package/dist/request--vRFALbQ.js +766 -0
- package/dist/request--vRFALbQ.js.map +1 -0
- package/dist/request.js +2 -48
- package/dist/signals/-leaked.js +3 -1
- package/dist/src-Dex_phWI.js +9088 -0
- package/dist/src-Dex_phWI.js.map +1 -0
- package/dist/store/-private.js +3 -1
- package/dist/store-DNF9Rgoh.js +462 -0
- package/dist/store-DNF9Rgoh.js.map +1 -0
- package/dist/store.js +3 -1
- package/dist/types/-private.js +1 -77
- package/dist/types/identifier.js +60 -52
- package/dist/types/identifier.js.map +1 -0
- package/dist/types/record.js +211 -181
- package/dist/types/record.js.map +1 -0
- package/dist/types/request.js +138 -69
- package/dist/types/request.js.map +1 -0
- package/dist/types/runtime.js +54 -26
- package/dist/types/runtime.js.map +1 -0
- package/dist/types/schema/fields.js +501 -495
- package/dist/types/schema/fields.js.map +1 -0
- package/dist/types/schema/fields.type-test.js +79 -0
- package/dist/types/schema/fields.type-test.js.map +1 -0
- package/dist/types/symbols.js +84 -77
- package/dist/types/symbols.js.map +1 -0
- package/dist/unpkg/dev/-private-2RfQ2761.js +111 -0
- package/dist/unpkg/dev/-private-2RfQ2761.js.map +1 -0
- package/dist/unpkg/dev/-private-CM1zxNws.js +1684 -0
- package/dist/unpkg/dev/-private-CM1zxNws.js.map +1 -0
- package/dist/unpkg/dev/-private-DL4tfrv9.js +22 -0
- package/dist/unpkg/dev/-private-DL4tfrv9.js.map +1 -0
- package/dist/unpkg/dev/build-config/babel-macros.js +3 -1
- package/dist/unpkg/dev/build-config/canary-features.js +1 -1
- package/dist/unpkg/dev/build-config/debugging.js +1 -1
- package/dist/unpkg/dev/build-config/deprecations.js +1 -1
- package/dist/unpkg/dev/build-config/env.js +3 -1
- package/dist/unpkg/dev/build-config/macros.js +3 -1
- package/dist/unpkg/dev/build-config.js +3 -1
- package/dist/unpkg/dev/configure.js +3 -1
- package/dist/unpkg/dev/graph/-private.js +1941 -2949
- package/dist/unpkg/dev/graph/-private.js.map +1 -0
- package/dist/unpkg/dev/index.js +3 -6
- package/dist/unpkg/dev/reactive/-private.js +3 -1
- package/dist/unpkg/dev/reactive.js +5 -3
- package/dist/unpkg/dev/request-DAuXbfYi.js +756 -0
- package/dist/unpkg/dev/request-DAuXbfYi.js.map +1 -0
- package/dist/unpkg/dev/request.js +2 -48
- package/dist/unpkg/dev/runtime-EoVKAhTv.js +110 -0
- package/dist/unpkg/dev/runtime-EoVKAhTv.js.map +1 -0
- package/dist/unpkg/dev/signals/-leaked.js +3 -1
- package/dist/unpkg/dev/src-Bxlr_vue.js +8192 -0
- package/dist/unpkg/dev/src-Bxlr_vue.js.map +1 -0
- package/dist/unpkg/dev/store/-private.js +3 -55
- package/dist/unpkg/dev/store-DmiFvT1B.js +427 -0
- package/dist/unpkg/dev/store-DmiFvT1B.js.map +1 -0
- package/dist/unpkg/dev/store.js +1 -556
- package/dist/unpkg/dev/types/-private.js +1 -67
- package/dist/unpkg/dev/types/identifier.js +63 -56
- package/dist/unpkg/dev/types/identifier.js.map +1 -0
- package/dist/unpkg/dev/types/record.js +211 -181
- package/dist/unpkg/dev/types/record.js.map +1 -0
- package/dist/unpkg/dev/types/request.js +138 -69
- package/dist/unpkg/dev/types/request.js.map +1 -0
- package/dist/unpkg/dev/types/runtime.js +54 -26
- package/dist/unpkg/dev/types/runtime.js.map +1 -0
- package/dist/unpkg/dev/types/schema/fields.js +501 -495
- package/dist/unpkg/dev/types/schema/fields.js.map +1 -0
- package/dist/unpkg/dev/types/schema/fields.type-test.js +79 -0
- package/dist/unpkg/dev/types/schema/fields.type-test.js.map +1 -0
- package/dist/unpkg/dev/types/symbols.js +84 -77
- package/dist/unpkg/dev/types/symbols.js.map +1 -0
- package/dist/unpkg/dev/utils/string.js +79 -85
- package/dist/unpkg/dev/utils/string.js.map +1 -0
- package/dist/unpkg/dev-deprecated/-private-2RfQ2761.js +111 -0
- package/dist/unpkg/dev-deprecated/-private-2RfQ2761.js.map +1 -0
- package/dist/unpkg/dev-deprecated/-private-CM1zxNws.js +1684 -0
- package/dist/unpkg/dev-deprecated/-private-CM1zxNws.js.map +1 -0
- package/dist/unpkg/dev-deprecated/-private-DL4tfrv9.js +22 -0
- package/dist/unpkg/dev-deprecated/-private-DL4tfrv9.js.map +1 -0
- package/dist/unpkg/dev-deprecated/build-config/babel-macros.js +3 -1
- package/dist/unpkg/dev-deprecated/build-config/canary-features.js +1 -1
- package/dist/unpkg/dev-deprecated/build-config/debugging.js +1 -1
- package/dist/unpkg/dev-deprecated/build-config/deprecations.js +1 -1
- package/dist/unpkg/dev-deprecated/build-config/env.js +3 -1
- package/dist/unpkg/dev-deprecated/build-config/macros.js +3 -1
- package/dist/unpkg/dev-deprecated/build-config.js +3 -1
- package/dist/unpkg/dev-deprecated/configure.js +3 -1
- package/dist/unpkg/dev-deprecated/graph/-private.js +2060 -3143
- package/dist/unpkg/dev-deprecated/graph/-private.js.map +1 -0
- package/dist/unpkg/dev-deprecated/index.js +3 -5
- package/dist/unpkg/dev-deprecated/reactive/-private.js +3 -1
- package/dist/unpkg/dev-deprecated/reactive.js +5 -3
- package/dist/unpkg/dev-deprecated/request-DAuXbfYi.js +756 -0
- package/dist/unpkg/dev-deprecated/request-DAuXbfYi.js.map +1 -0
- package/dist/unpkg/dev-deprecated/request.js +2 -48
- package/dist/unpkg/dev-deprecated/runtime-BacZMq1r.js +110 -0
- package/dist/unpkg/dev-deprecated/runtime-BacZMq1r.js.map +1 -0
- package/dist/unpkg/dev-deprecated/signals/-leaked.js +3 -1
- package/dist/unpkg/dev-deprecated/src-DEl2Ca3T.js +8745 -0
- package/dist/unpkg/dev-deprecated/src-DEl2Ca3T.js.map +1 -0
- package/dist/unpkg/dev-deprecated/store/-private.js +3 -1
- package/dist/unpkg/dev-deprecated/store-TcP2H4Af.js +427 -0
- package/dist/unpkg/dev-deprecated/store-TcP2H4Af.js.map +1 -0
- package/dist/unpkg/dev-deprecated/store.js +1 -556
- package/dist/unpkg/dev-deprecated/types/-private.js +1 -67
- package/dist/unpkg/dev-deprecated/types/identifier.js +63 -56
- package/dist/unpkg/dev-deprecated/types/identifier.js.map +1 -0
- package/dist/unpkg/dev-deprecated/types/record.js +211 -181
- package/dist/unpkg/dev-deprecated/types/record.js.map +1 -0
- package/dist/unpkg/dev-deprecated/types/request.js +138 -69
- package/dist/unpkg/dev-deprecated/types/request.js.map +1 -0
- package/dist/unpkg/dev-deprecated/types/runtime.js +54 -26
- package/dist/unpkg/dev-deprecated/types/runtime.js.map +1 -0
- package/dist/unpkg/dev-deprecated/types/schema/fields.js +501 -495
- package/dist/unpkg/dev-deprecated/types/schema/fields.js.map +1 -0
- package/dist/unpkg/dev-deprecated/types/schema/fields.type-test.js +79 -0
- package/dist/unpkg/dev-deprecated/types/schema/fields.type-test.js.map +1 -0
- package/dist/unpkg/dev-deprecated/types/symbols.js +84 -77
- package/dist/unpkg/dev-deprecated/types/symbols.js.map +1 -0
- package/dist/unpkg/dev-deprecated/utils/string.js +79 -85
- package/dist/unpkg/dev-deprecated/utils/string.js.map +1 -0
- package/dist/unpkg/prod/-private-BK71-uXR.js +99 -0
- package/dist/unpkg/prod/-private-BK71-uXR.js.map +1 -0
- package/dist/unpkg/prod/-private-TNaS5Piv.js +1494 -0
- package/dist/unpkg/prod/-private-TNaS5Piv.js.map +1 -0
- package/dist/unpkg/prod/-private-Z9i0icpE.js +22 -0
- package/dist/unpkg/prod/-private-Z9i0icpE.js.map +1 -0
- package/dist/unpkg/prod/build-config/babel-macros.js +3 -1
- package/dist/unpkg/prod/build-config/canary-features.js +1 -1
- package/dist/unpkg/prod/build-config/debugging.js +1 -1
- package/dist/unpkg/prod/build-config/deprecations.js +1 -1
- package/dist/unpkg/prod/build-config/env.js +3 -1
- package/dist/unpkg/prod/build-config/macros.js +3 -1
- package/dist/unpkg/prod/build-config.js +3 -1
- package/dist/unpkg/prod/configure.js +3 -2
- package/dist/unpkg/prod/graph/-private.js +1475 -2095
- package/dist/unpkg/prod/graph/-private.js.map +1 -0
- package/dist/unpkg/prod/index.js +2 -482
- package/dist/unpkg/prod/reactive/-private.js +3 -1
- package/dist/unpkg/prod/reactive.js +5 -30
- package/dist/unpkg/prod/request-BcJNzSgv.js +480 -0
- package/dist/unpkg/prod/request-BcJNzSgv.js.map +1 -0
- package/dist/unpkg/prod/request.js +3 -2
- package/dist/unpkg/prod/signals/-leaked.js +3 -1
- package/dist/unpkg/prod/src-Dzz-j6pp.js +6799 -0
- package/dist/unpkg/prod/src-Dzz-j6pp.js.map +1 -0
- package/dist/unpkg/prod/store/-private.js +3 -126
- package/dist/unpkg/prod/store-BWhmb7Ob.js +359 -0
- package/dist/unpkg/prod/store-BWhmb7Ob.js.map +1 -0
- package/dist/unpkg/prod/store.js +1 -435
- package/dist/unpkg/prod/types/-private.js +1 -47
- package/dist/unpkg/prod/types/identifier.js +63 -56
- package/dist/unpkg/prod/types/identifier.js.map +1 -0
- package/dist/unpkg/prod/types/record.js +211 -181
- package/dist/unpkg/prod/types/record.js.map +1 -0
- package/dist/unpkg/prod/types/request.js +138 -69
- package/dist/unpkg/prod/types/request.js.map +1 -0
- package/dist/unpkg/prod/types/runtime.js +54 -26
- package/dist/unpkg/prod/types/runtime.js.map +1 -0
- package/dist/unpkg/prod/types/schema/fields.js +501 -495
- package/dist/unpkg/prod/types/schema/fields.js.map +1 -0
- package/dist/unpkg/prod/types/schema/fields.type-test.js +79 -0
- package/dist/unpkg/prod/types/schema/fields.type-test.js.map +1 -0
- package/dist/unpkg/prod/types/symbols.js +84 -77
- package/dist/unpkg/prod/types/symbols.js.map +1 -0
- package/dist/unpkg/prod/utils/string.js +70 -66
- package/dist/unpkg/prod/utils/string.js.map +1 -0
- package/dist/unpkg/prod-deprecated/-private-BK71-uXR.js +99 -0
- package/dist/unpkg/prod-deprecated/-private-BK71-uXR.js.map +1 -0
- package/dist/unpkg/prod-deprecated/-private-TNaS5Piv.js +1494 -0
- package/dist/unpkg/prod-deprecated/-private-TNaS5Piv.js.map +1 -0
- package/dist/unpkg/prod-deprecated/-private-Z9i0icpE.js +22 -0
- package/dist/unpkg/prod-deprecated/-private-Z9i0icpE.js.map +1 -0
- package/dist/unpkg/prod-deprecated/build-config/babel-macros.js +3 -1
- package/dist/unpkg/prod-deprecated/build-config/canary-features.js +1 -1
- package/dist/unpkg/prod-deprecated/build-config/debugging.js +1 -1
- package/dist/unpkg/prod-deprecated/build-config/deprecations.js +1 -1
- package/dist/unpkg/prod-deprecated/build-config/env.js +3 -1
- package/dist/unpkg/prod-deprecated/build-config/macros.js +3 -1
- package/dist/unpkg/prod-deprecated/build-config.js +3 -1
- package/dist/unpkg/prod-deprecated/configure.js +3 -2
- package/dist/unpkg/prod-deprecated/graph/-private.js +1586 -2266
- package/dist/unpkg/prod-deprecated/graph/-private.js.map +1 -0
- package/dist/unpkg/prod-deprecated/index.js +2 -482
- package/dist/unpkg/prod-deprecated/reactive/-private.js +3 -1
- package/dist/unpkg/prod-deprecated/reactive.js +5 -5
- package/dist/unpkg/prod-deprecated/request-BcJNzSgv.js +480 -0
- package/dist/unpkg/prod-deprecated/request-BcJNzSgv.js.map +1 -0
- package/dist/unpkg/prod-deprecated/request.js +3 -2
- package/dist/unpkg/prod-deprecated/signals/-leaked.js +3 -1
- package/dist/unpkg/prod-deprecated/src-BOifHFQ5.js +7232 -0
- package/dist/unpkg/prod-deprecated/src-BOifHFQ5.js.map +1 -0
- package/dist/unpkg/prod-deprecated/store/-private.js +3 -88
- package/dist/unpkg/prod-deprecated/store-BWhmb7Ob.js +359 -0
- package/dist/unpkg/prod-deprecated/store-BWhmb7Ob.js.map +1 -0
- package/dist/unpkg/prod-deprecated/store.js +1 -435
- package/dist/unpkg/prod-deprecated/types/-private.js +1 -47
- package/dist/unpkg/prod-deprecated/types/identifier.js +63 -56
- package/dist/unpkg/prod-deprecated/types/identifier.js.map +1 -0
- package/dist/unpkg/prod-deprecated/types/record.js +211 -181
- package/dist/unpkg/prod-deprecated/types/record.js.map +1 -0
- package/dist/unpkg/prod-deprecated/types/request.js +138 -69
- package/dist/unpkg/prod-deprecated/types/request.js.map +1 -0
- package/dist/unpkg/prod-deprecated/types/runtime.js +54 -26
- package/dist/unpkg/prod-deprecated/types/runtime.js.map +1 -0
- package/dist/unpkg/prod-deprecated/types/schema/fields.js +501 -495
- package/dist/unpkg/prod-deprecated/types/schema/fields.js.map +1 -0
- package/dist/unpkg/prod-deprecated/types/schema/fields.type-test.js +79 -0
- package/dist/unpkg/prod-deprecated/types/schema/fields.type-test.js.map +1 -0
- package/dist/unpkg/prod-deprecated/types/symbols.js +84 -77
- package/dist/unpkg/prod-deprecated/types/symbols.js.map +1 -0
- package/dist/unpkg/prod-deprecated/utils/string.js +70 -66
- package/dist/unpkg/prod-deprecated/utils/string.js.map +1 -0
- package/dist/utils/string.js +84 -85
- package/dist/utils/string.js.map +1 -0
- package/package.json +9 -9
- package/declarations/graph/-private/-diff.d.ts +0 -19
- package/declarations/graph/-private/-edge-definition.d.ts +0 -149
- package/declarations/graph/-private/-state.d.ts +0 -9
- package/declarations/graph/-private/-utils.d.ts +0 -25
- package/declarations/graph/-private/coerce-id.d.ts +0 -4
- package/declarations/graph/-private/debug/assert-polymorphic-type.d.ts +0 -6
- package/declarations/graph/-private/edges/collection.d.ts +0 -39
- package/declarations/graph/-private/edges/implicit.d.ts +0 -43
- package/declarations/graph/-private/edges/resource.d.ts +0 -23
- package/declarations/graph/-private/graph.d.ts +0 -56
- package/declarations/graph/-private/normalize-link.d.ts +0 -2
- package/declarations/graph/-private/operations/add-to-related-records.d.ts +0 -4
- package/declarations/graph/-private/operations/merge-identifier.d.ts +0 -3
- package/declarations/graph/-private/operations/remove-from-related-records.d.ts +0 -4
- package/declarations/graph/-private/operations/replace-related-record.d.ts +0 -3
- package/declarations/graph/-private/operations/replace-related-records.d.ts +0 -7
- package/declarations/graph/-private/operations/update-relationship.d.ts +0 -9
- package/declarations/reactive/-private/default-mode.d.ts +0 -73
- package/declarations/reactive/-private/document.d.ts +0 -142
- package/declarations/reactive/-private/fields/extension.d.ts +0 -8
- package/declarations/reactive/-private/fields/get-field-key.d.ts +0 -8
- package/declarations/reactive/-private/fields/managed-array.d.ts +0 -18
- package/declarations/reactive/-private/fields/managed-object.d.ts +0 -17
- package/declarations/reactive/-private/fields/many-array-manager.d.ts +0 -19
- package/declarations/reactive/-private/hooks.d.ts +0 -5
- package/declarations/reactive/-private/kind/alias-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/array-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/attribute-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/belongs-to-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/collection-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/derived-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/generic-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/has-many-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/hash-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/identity-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/local-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/object-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/resource-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/schema-array-field.d.ts +0 -4
- package/declarations/reactive/-private/kind/schema-object-field.d.ts +0 -4
- package/declarations/reactive/-private/record.d.ts +0 -83
- package/declarations/reactive/-private/schema.d.ts +0 -326
- package/declarations/reactive/-private/symbols.d.ts +0 -5
- package/declarations/request/-private/context.d.ts +0 -39
- package/declarations/request/-private/debug.d.ts +0 -4
- package/declarations/request/-private/fetch.d.ts +0 -33
- package/declarations/request/-private/future.d.ts +0 -7
- package/declarations/request/-private/manager.d.ts +0 -158
- package/declarations/request/-private/promise-cache.d.ts +0 -28
- package/declarations/request/-private/types.d.ts +0 -234
- package/declarations/request/-private/utils.d.ts +0 -57
- package/declarations/signals/-private.d.ts +0 -6
- package/declarations/signals/promise-state.d.ts +0 -264
- package/declarations/signals/reactivity/configure.d.ts +0 -138
- package/declarations/signals/reactivity/internal.d.ts +0 -154
- package/declarations/signals/reactivity/signal.d.ts +0 -56
- package/declarations/signals/request-state.d.ts +0 -372
- package/declarations/signals/request-subscription.d.ts +0 -177
- package/declarations/store/-private/cache-handler/handler.d.ts +0 -56
- package/declarations/store/-private/cache-handler/types.d.ts +0 -84
- package/declarations/store/-private/cache-handler/utils.d.ts +0 -31
- package/declarations/store/-private/caches/instance-cache.d.ts +0 -58
- package/declarations/store/-private/caches/resource-utils.d.ts +0 -9
- package/declarations/store/-private/debug/utils.d.ts +0 -7
- package/declarations/store/-private/default-cache-policy.d.ts +0 -387
- package/declarations/store/-private/managers/cache-capabilities-manager.d.ts +0 -34
- package/declarations/store/-private/managers/cache-key-manager.d.ts +0 -304
- package/declarations/store/-private/managers/cache-manager.d.ts +0 -367
- package/declarations/store/-private/managers/notification-manager.d.ts +0 -83
- package/declarations/store/-private/managers/record-array-manager.d.ts +0 -100
- package/declarations/store/-private/network/request-cache.d.ts +0 -103
- package/declarations/store/-private/record-arrays/-utils.d.ts +0 -80
- package/declarations/store/-private/record-arrays/legacy-live-array.d.ts +0 -81
- package/declarations/store/-private/record-arrays/legacy-many-array.d.ts +0 -133
- package/declarations/store/-private/record-arrays/legacy-query.d.ts +0 -81
- package/declarations/store/-private/record-arrays/native-proxy-type-fix.d.ts +0 -3
- package/declarations/store/-private/record-arrays/resource-array.d.ts +0 -67
- package/declarations/store/-private/store-service.d.ts +0 -874
- package/declarations/store/-private/utils/coerce-id.d.ts +0 -4
- package/declarations/store/-private/utils/construct-resource.d.ts +0 -6
- package/declarations/store/-private/utils/is-non-empty-string.d.ts +0 -1
- package/declarations/store/-private/utils/normalize-model-name.d.ts +0 -1
- package/declarations/store/-private/utils/uuid-polyfill.d.ts +0 -1
- package/declarations/store/-types/q/cache-capabilities-manager.d.ts +0 -90
- package/declarations/store/-types/q/identifier.d.ts +0 -23
- package/declarations/store/-types/q/record-instance.d.ts +0 -22
- package/declarations/store/-types/q/store.d.ts +0 -31
- package/declarations/store/deprecated/-private.d.ts +0 -223
- package/declarations/store/deprecated/store.d.ts +0 -784
- package/dist/configure-DPUFCemT.js +0 -1940
- package/dist/default-cache-policy-D7_u4YRH.js +0 -572
- package/dist/future-BKkJJkj7.js +0 -678
- package/dist/index-CQP2NSqg.js +0 -9982
- package/dist/symbols-3C1OkYtZ.js +0 -39
- package/dist/unpkg/dev/-leaked-Co0EI6Go.js +0 -1939
- package/dist/unpkg/dev/-private-sql1_mdx.js +0 -39
- package/dist/unpkg/dev/future-DFfOzSoe.js +0 -672
- package/dist/unpkg/dev/index-BzcBOnw9.js +0 -9392
- package/dist/unpkg/dev/runtime-E1M51_-O.js +0 -135
- package/dist/unpkg/dev-deprecated/-leaked-DjMeRqdU.js +0 -1939
- package/dist/unpkg/dev-deprecated/-private-3C1OkYtZ.js +0 -39
- package/dist/unpkg/dev-deprecated/future-DFfOzSoe.js +0 -672
- package/dist/unpkg/dev-deprecated/index-6TnTv-WG.js +0 -10007
- package/dist/unpkg/dev-deprecated/runtime-DAu5b0IR.js +0 -135
- package/dist/unpkg/prod/-leaked-DUONXQDB.js +0 -1676
- package/dist/unpkg/prod/-private-sql1_mdx.js +0 -39
- package/dist/unpkg/prod/handler-EU_8ncB2.js +0 -1619
- package/dist/unpkg/prod/promise-cache-DIT8Ypjq.js +0 -19
- package/dist/unpkg/prod/request-BrJSCG6r.js +0 -421
- package/dist/unpkg/prod/schema-BSkHyoWz.js +0 -5219
- package/dist/unpkg/prod-deprecated/-leaked-DRNv9VIX.js +0 -1676
- package/dist/unpkg/prod-deprecated/-private-3C1OkYtZ.js +0 -39
- package/dist/unpkg/prod-deprecated/handler-CCIu4sQ3.js +0 -334
- package/dist/unpkg/prod-deprecated/hooks-Dv4Np0MY.js +0 -26
- package/dist/unpkg/prod-deprecated/promise-cache-DIT8Ypjq.js +0 -19
- package/dist/unpkg/prod-deprecated/request-BrJSCG6r.js +0 -421
- package/dist/unpkg/prod-deprecated/schema-CJcjHv0E.js +0 -6939
|
@@ -1,2381 +1,1696 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as setTransient, r as peekTransient, t as getOrSetGlobal } from "../-private-BK71-uXR.js";
|
|
2
|
+
|
|
3
|
+
//#region src/graph/-private/-utils.ts
|
|
4
|
+
/** Retrieves the private {@link Store} instance from a capabilities manager (or any object exposing `_store`). */
|
|
2
5
|
function getStore(wrapper) {
|
|
3
|
-
|
|
6
|
+
return wrapper._store;
|
|
4
7
|
}
|
|
8
|
+
/** Reads a value from a two-level keyed cache, without creating the first-level entry if it is missing. */
|
|
5
9
|
function expandingGet(cache, key1, key2) {
|
|
6
|
-
|
|
7
|
-
return mainCache[key2];
|
|
10
|
+
return (cache[key1] = cache[key1] || Object.create(null))[key2];
|
|
8
11
|
}
|
|
12
|
+
/** Writes a value into a two-level keyed cache, lazily creating the first-level entry if it does not yet exist. */
|
|
9
13
|
function expandingSet(cache, key1, key2, value) {
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
const mainCache = cache[key1] = cache[key1] || Object.create(null);
|
|
15
|
+
mainCache[key2] = value;
|
|
12
16
|
}
|
|
17
|
+
/** Returns whether the given resource is new, i.e. it has no `id` or is flagged as new by the cache. */
|
|
13
18
|
function checkIfNew(store, resourceKey) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
return store.cache.isNew(resourceKey);
|
|
19
|
+
if (!resourceKey.id) return true;
|
|
20
|
+
return store.cache.isNew(resourceKey);
|
|
18
21
|
}
|
|
22
|
+
/** A type-guard returning whether the given edge is a `belongsTo` {@link ResourceEdge}. */
|
|
19
23
|
function isBelongsTo(relationship) {
|
|
20
|
-
|
|
24
|
+
return relationship.definition.kind === "belongsTo";
|
|
21
25
|
}
|
|
26
|
+
/** A type-guard returning whether the given edge is an {@link ImplicitEdge}. */
|
|
22
27
|
function isImplicit(relationship) {
|
|
23
|
-
|
|
28
|
+
return relationship.definition.isImplicit;
|
|
24
29
|
}
|
|
30
|
+
/** A type-guard returning whether the given edge is a `hasMany` {@link CollectionEdge}. */
|
|
25
31
|
function isHasMany(relationship) {
|
|
26
|
-
|
|
32
|
+
return relationship.definition.kind === "hasMany";
|
|
27
33
|
}
|
|
34
|
+
/** Invokes `cb` for every resource identifier currently related through `rel`, covering both local and remote membership regardless of the edge's kind. */
|
|
28
35
|
function forAllRelatedIdentifiers(rel, cb) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
rel.additions?.forEach(cb);
|
|
45
|
-
} else {
|
|
46
|
-
rel.localMembers.forEach(cb);
|
|
47
|
-
rel.remoteMembers.forEach(inverseIdentifier => {
|
|
48
|
-
if (!rel.localMembers.has(inverseIdentifier)) {
|
|
49
|
-
cb(inverseIdentifier);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
36
|
+
if (isBelongsTo(rel)) {
|
|
37
|
+
if (rel.remoteState) cb(rel.remoteState);
|
|
38
|
+
if (rel.localState && rel.localState !== rel.remoteState) cb(rel.localState);
|
|
39
|
+
} else if (isHasMany(rel)) {
|
|
40
|
+
for (let i = 0; i < rel.remoteState.length; i++) {
|
|
41
|
+
const inverseIdentifier = rel.remoteState[i];
|
|
42
|
+
cb(inverseIdentifier);
|
|
43
|
+
}
|
|
44
|
+
rel.additions?.forEach(cb);
|
|
45
|
+
} else {
|
|
46
|
+
rel.localMembers.forEach(cb);
|
|
47
|
+
rel.remoteMembers.forEach((inverseIdentifier) => {
|
|
48
|
+
if (!rel.localMembers.has(inverseIdentifier)) cb(inverseIdentifier);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
53
51
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*/
|
|
52
|
+
/**
|
|
53
|
+
* Removes the given identifier from BOTH remote AND local state.
|
|
54
|
+
*
|
|
55
|
+
* This method is useful when either a deletion or a rollback on a new record
|
|
56
|
+
* needs to entirely purge itself from an inverse relationship.
|
|
57
|
+
*/
|
|
61
58
|
function removeIdentifierCompletelyFromRelationship(graph, relationship, value, silenceNotifications) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// This allows dematerialized inverses to be rematerialized
|
|
88
|
-
// we shouldn't be notifying here though, figure out where
|
|
89
|
-
// a notification was missed elsewhere.
|
|
90
|
-
{
|
|
91
|
-
notifyChange(graph, relationship);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
relationship.remoteMembers.delete(value);
|
|
97
|
-
relationship.localMembers.delete(value);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
59
|
+
if (isBelongsTo(relationship)) {
|
|
60
|
+
if (relationship.remoteState === value) relationship.remoteState = null;
|
|
61
|
+
if (relationship.localState === value) {
|
|
62
|
+
relationship.localState = null;
|
|
63
|
+
if (!silenceNotifications) notifyChange(graph, relationship);
|
|
64
|
+
}
|
|
65
|
+
} else if (isHasMany(relationship)) {
|
|
66
|
+
relationship.remoteMembers.delete(value);
|
|
67
|
+
relationship.additions?.delete(value);
|
|
68
|
+
const wasInRemovals = relationship.removals?.delete(value);
|
|
69
|
+
const canonicalIndex = relationship.remoteState.indexOf(value);
|
|
70
|
+
if (canonicalIndex !== -1) relationship.remoteState.splice(canonicalIndex, 1);
|
|
71
|
+
if (!wasInRemovals) {
|
|
72
|
+
const currentIndex = relationship.localState?.indexOf(value);
|
|
73
|
+
if (currentIndex !== -1 && currentIndex !== void 0) {
|
|
74
|
+
relationship.localState.splice(currentIndex, 1);
|
|
75
|
+
if (!silenceNotifications) notifyChange(graph, relationship);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
relationship.remoteMembers.delete(value);
|
|
80
|
+
relationship.localMembers.delete(value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Notifies the store that a relationship has changed, provided the relationship has been accessed and is not currently being removed. */
|
|
100
84
|
function notifyChange(graph, relationship) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (resourceKey === graph._removing) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
graph.store.notifyChange(resourceKey, 'relationships', key);
|
|
110
|
-
}
|
|
111
|
-
function isLegacyField(field) {
|
|
112
|
-
return field.kind === 'belongsTo' || field.kind === 'hasMany';
|
|
113
|
-
}
|
|
114
|
-
function temporaryConvertToLegacy(field) {
|
|
115
|
-
return {
|
|
116
|
-
kind: field.kind === 'resource' ? 'belongsTo' : 'hasMany',
|
|
117
|
-
name: field.name,
|
|
118
|
-
type: field.type,
|
|
119
|
-
options: Object.assign({}, {
|
|
120
|
-
async: false,
|
|
121
|
-
inverse: null,
|
|
122
|
-
resetOnRemoteUpdate: false
|
|
123
|
-
}, field.options)
|
|
124
|
-
};
|
|
85
|
+
if (!relationship.accessed) return;
|
|
86
|
+
const resourceKey = relationship.identifier;
|
|
87
|
+
const key = relationship.definition.key;
|
|
88
|
+
if (resourceKey === graph._removing) return;
|
|
89
|
+
graph.store.notifyChange(resourceKey, "relationships", key);
|
|
125
90
|
}
|
|
126
91
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
* Given RHS (Right Hand Side)
|
|
130
|
-
*
|
|
131
|
-
* ```ts
|
|
132
|
-
* class User extends Model {
|
|
133
|
-
* @hasMany('animal', { async: false, inverse: 'owner' }) pets;
|
|
134
|
-
* }
|
|
135
|
-
* ```
|
|
136
|
-
*
|
|
137
|
-
* Given LHS (Left Hand Side)
|
|
138
|
-
*
|
|
139
|
-
* ```ts
|
|
140
|
-
* class Animal extends Model {
|
|
141
|
-
* @belongsTo('user', { async: false, inverse: 'pets' }) owner;
|
|
142
|
-
* }
|
|
143
|
-
* ```
|
|
144
|
-
*
|
|
145
|
-
* The UpgradedMeta for the RHS would be:
|
|
146
|
-
*
|
|
147
|
-
* ```ts
|
|
148
|
-
* {
|
|
149
|
-
* kind: 'hasMany',
|
|
150
|
-
* key: 'pets',
|
|
151
|
-
* type: 'animal',
|
|
152
|
-
* isAsync: false,
|
|
153
|
-
* isImplicit: false,
|
|
154
|
-
* isCollection: true,
|
|
155
|
-
* isPolymorphic: false,
|
|
156
|
-
* inverseKind: 'belongsTo',
|
|
157
|
-
* inverseKey: 'owner',
|
|
158
|
-
* inverseType: 'user',
|
|
159
|
-
* inverseIsAsync: false,
|
|
160
|
-
* inverseIsImplicit: false,
|
|
161
|
-
* inverseIsCollection: false,
|
|
162
|
-
* inverseIsPolymorphic: false,
|
|
163
|
-
* }
|
|
164
|
-
* ```
|
|
165
|
-
*
|
|
166
|
-
* The UpgradeMeta for the LHS would be:
|
|
167
|
-
*
|
|
168
|
-
* ```ts
|
|
169
|
-
* {
|
|
170
|
-
* kind: 'belongsTo',
|
|
171
|
-
* key: 'owner',
|
|
172
|
-
* type: 'user',
|
|
173
|
-
* isAsync: false,
|
|
174
|
-
* isImplicit: false,
|
|
175
|
-
* isCollection: false,
|
|
176
|
-
* isPolymorphic: false,
|
|
177
|
-
* inverseKind: 'hasMany',
|
|
178
|
-
* inverseKey: 'pets',
|
|
179
|
-
* inverseType: 'animal',
|
|
180
|
-
* inverseIsAsync: false,
|
|
181
|
-
* inverseIsImplicit: false,
|
|
182
|
-
* inverseIsCollection: true,
|
|
183
|
-
* inverseIsPolymorphic: false,
|
|
184
|
-
* }
|
|
185
|
-
* ```
|
|
186
|
-
*
|
|
187
|
-
* @private
|
|
188
|
-
*/
|
|
189
|
-
|
|
190
|
-
const BOOL_LATER = null;
|
|
191
|
-
const STR_LATER = '';
|
|
192
|
-
const IMPLICIT_KEY_RAND = Date.now();
|
|
193
|
-
function implicitKeyFor(type, key) {
|
|
194
|
-
return `implicit-${type}:${key}${IMPLICIT_KEY_RAND}`;
|
|
195
|
-
}
|
|
196
|
-
function syncMeta(definition, inverseDefinition) {
|
|
197
|
-
definition.inverseKind = inverseDefinition.kind;
|
|
198
|
-
definition.inverseKey = inverseDefinition.key;
|
|
199
|
-
definition.inverseName = inverseDefinition.name;
|
|
200
|
-
definition.inverseType = inverseDefinition.type;
|
|
201
|
-
definition.inverseIsAsync = inverseDefinition.isAsync;
|
|
202
|
-
definition.inverseIsCollection = inverseDefinition.isCollection;
|
|
203
|
-
definition.inverseIsPolymorphic = inverseDefinition.isPolymorphic;
|
|
204
|
-
definition.inverseIsImplicit = inverseDefinition.isImplicit;
|
|
205
|
-
definition.inverseIsLinksMode = inverseDefinition.isLinksMode;
|
|
206
|
-
const resetOnRemoteUpdate = definition.resetOnRemoteUpdate === false || inverseDefinition.resetOnRemoteUpdate === false ? false : true;
|
|
207
|
-
definition.resetOnRemoteUpdate = resetOnRemoteUpdate;
|
|
208
|
-
inverseDefinition.resetOnRemoteUpdate = resetOnRemoteUpdate;
|
|
209
|
-
}
|
|
210
|
-
function upgradeMeta(meta) {
|
|
211
|
-
if (!isLegacyField(meta)) {
|
|
212
|
-
meta = temporaryConvertToLegacy(meta);
|
|
213
|
-
}
|
|
214
|
-
const niceMeta = {};
|
|
215
|
-
const options = meta.options;
|
|
216
|
-
niceMeta.kind = meta.kind;
|
|
217
|
-
niceMeta.key = meta.sourceKey ?? meta.name;
|
|
218
|
-
niceMeta.name = meta.name;
|
|
219
|
-
niceMeta.type = meta.type;
|
|
220
|
-
niceMeta.isAsync = options.async;
|
|
221
|
-
niceMeta.isImplicit = false;
|
|
222
|
-
niceMeta.isCollection = meta.kind === 'hasMany';
|
|
223
|
-
niceMeta.isPolymorphic = options && !!options.polymorphic;
|
|
224
|
-
niceMeta.isLinksMode = options.linksMode ?? false;
|
|
225
|
-
niceMeta.inverseKey = options && options.inverse || STR_LATER;
|
|
226
|
-
niceMeta.inverseName = options && options.inverse || STR_LATER;
|
|
227
|
-
niceMeta.inverseType = STR_LATER;
|
|
228
|
-
niceMeta.inverseIsAsync = BOOL_LATER;
|
|
229
|
-
niceMeta.inverseIsImplicit = options && options.inverse === null || BOOL_LATER;
|
|
230
|
-
niceMeta.inverseIsCollection = BOOL_LATER;
|
|
231
|
-
niceMeta.inverseIsLinksMode = BOOL_LATER;
|
|
232
|
-
|
|
233
|
-
// prettier-ignore
|
|
234
|
-
niceMeta.resetOnRemoteUpdate = !isLegacyField(meta) ? false : meta.options?.linksMode ? false : meta.options?.resetOnRemoteUpdate === false ? false : true;
|
|
235
|
-
return niceMeta;
|
|
236
|
-
}
|
|
237
|
-
function isLHS(info, type, key) {
|
|
238
|
-
const isSelfReferential = info.isSelfReferential;
|
|
239
|
-
const isRelationship = key === info.lhs_relationshipName;
|
|
240
|
-
if (isRelationship === true) {
|
|
241
|
-
return isSelfReferential === true ||
|
|
242
|
-
// itself
|
|
243
|
-
type === info.lhs_baseModelName ||
|
|
244
|
-
// base or non-polymorphic
|
|
245
|
-
// if the other side is polymorphic then we need to scan our modelNames
|
|
246
|
-
info.rhs_isPolymorphic && info.lhs_modelNames.includes(type) // polymorphic
|
|
247
|
-
;
|
|
248
|
-
}
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
function upgradeDefinition(graph, key, propertyName, isImplicit = false) {
|
|
252
|
-
const cache = graph._definitionCache;
|
|
253
|
-
const storeWrapper = graph.store;
|
|
254
|
-
const polymorphicLookup = graph._potentialPolymorphicTypes;
|
|
255
|
-
const {
|
|
256
|
-
type
|
|
257
|
-
} = key;
|
|
258
|
-
let cached = /*#__NOINLINE__*/expandingGet(cache, type, propertyName);
|
|
259
|
-
|
|
260
|
-
// CASE: We have a cached resolution (null if no relationship exists)
|
|
261
|
-
if (cached !== undefined) {
|
|
262
|
-
return cached;
|
|
263
|
-
}
|
|
264
|
-
const relationships = storeWrapper.schema.fields(key);
|
|
265
|
-
const relationshipsBySourceKey = storeWrapper.schema.cacheFields?.(key) ?? relationships;
|
|
266
|
-
const meta = relationshipsBySourceKey.get(propertyName);
|
|
267
|
-
if (!meta) {
|
|
268
|
-
// TODO potentially we should just be permissive here since this is an implicit relationship
|
|
269
|
-
// and not require the lookup table to be populated
|
|
270
|
-
if (polymorphicLookup[type]) {
|
|
271
|
-
const altTypes = Object.keys(polymorphicLookup[type]);
|
|
272
|
-
for (let i = 0; i < altTypes.length; i++) {
|
|
273
|
-
const _cached = expandingGet(cache, altTypes[i], propertyName);
|
|
274
|
-
if (_cached) {
|
|
275
|
-
/*#__NOINLINE__*/expandingSet(cache, type, propertyName, _cached);
|
|
276
|
-
_cached.rhs_modelNames.push(type);
|
|
277
|
-
return _cached;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
cache[type][propertyName] = null;
|
|
282
|
-
return null;
|
|
283
|
-
}
|
|
284
|
-
const definition = /*#__NOINLINE__*/upgradeMeta(meta);
|
|
285
|
-
let inverseDefinition;
|
|
286
|
-
let inverseKey;
|
|
287
|
-
const inverseType = definition.type;
|
|
288
|
-
|
|
289
|
-
// CASE: Inverse is explicitly null
|
|
290
|
-
if (definition.inverseKey === null) {
|
|
291
|
-
inverseDefinition = null;
|
|
292
|
-
} else {
|
|
293
|
-
inverseKey = /*#__NOINLINE__*/inverseForRelationship(getStore(storeWrapper), key, propertyName);
|
|
294
|
-
|
|
295
|
-
// CASE: If we are polymorphic, and we declared an inverse that is non-null
|
|
296
|
-
// we must assume that the lack of inverseKey means that there is no
|
|
297
|
-
// concrete type as the baseType, so we must construct and artificial
|
|
298
|
-
// placeholder
|
|
299
|
-
if (!inverseKey && definition.isPolymorphic && definition.inverseKey) {
|
|
300
|
-
inverseDefinition = {
|
|
301
|
-
kind: 'belongsTo',
|
|
302
|
-
// this must be updated when we find the first belongsTo or hasMany definition that matches
|
|
303
|
-
key: definition.inverseKey,
|
|
304
|
-
name: definition.inverseName,
|
|
305
|
-
type: type,
|
|
306
|
-
isAsync: false,
|
|
307
|
-
// this must be updated when we find the first belongsTo or hasMany definition that matches
|
|
308
|
-
isImplicit: false,
|
|
309
|
-
isCollection: false,
|
|
310
|
-
// this must be updated when we find the first belongsTo or hasMany definition that matches
|
|
311
|
-
isPolymorphic: false
|
|
312
|
-
}; // the rest of the fields are populated by syncMeta
|
|
313
|
-
|
|
314
|
-
// CASE: Inverse resolves to null
|
|
315
|
-
} else if (!inverseKey) {
|
|
316
|
-
inverseDefinition = null;
|
|
317
|
-
} else {
|
|
318
|
-
// CASE: We have an explicit inverse or were able to resolve one
|
|
319
|
-
// for the inverse we use "name" for lookup not "sourceKey"
|
|
320
|
-
const inverseDefinitions = storeWrapper.schema.fields({
|
|
321
|
-
type: inverseType
|
|
322
|
-
});
|
|
323
|
-
const metaFromInverse = inverseDefinitions.get(inverseKey);
|
|
324
|
-
inverseDefinition = upgradeMeta(metaFromInverse);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// CASE: We have no inverse
|
|
329
|
-
if (!inverseDefinition) {
|
|
330
|
-
// polish off meta
|
|
331
|
-
inverseKey = /*#__NOINLINE__*/implicitKeyFor(type, propertyName);
|
|
332
|
-
inverseDefinition = {
|
|
333
|
-
kind: 'implicit',
|
|
334
|
-
key: inverseKey,
|
|
335
|
-
type: type,
|
|
336
|
-
isAsync: false,
|
|
337
|
-
isImplicit: true,
|
|
338
|
-
isCollection: true,
|
|
339
|
-
// with implicits any number of records could point at us
|
|
340
|
-
isPolymorphic: false
|
|
341
|
-
}; // the rest of the fields are populated by syncMeta
|
|
342
|
-
|
|
343
|
-
syncMeta(definition, inverseDefinition);
|
|
344
|
-
syncMeta(inverseDefinition, definition);
|
|
345
|
-
const info = {
|
|
346
|
-
lhs_key: `${type}:${propertyName}`,
|
|
347
|
-
lhs_modelNames: [type],
|
|
348
|
-
lhs_baseModelName: type,
|
|
349
|
-
lhs_relationshipName: propertyName,
|
|
350
|
-
lhs_definition: definition,
|
|
351
|
-
lhs_isPolymorphic: definition.isPolymorphic,
|
|
352
|
-
rhs_key: inverseDefinition.key,
|
|
353
|
-
rhs_modelNames: [inverseType],
|
|
354
|
-
rhs_baseModelName: inverseType,
|
|
355
|
-
rhs_relationshipName: inverseDefinition.key,
|
|
356
|
-
rhs_definition: inverseDefinition,
|
|
357
|
-
rhs_isPolymorphic: false,
|
|
358
|
-
hasInverse: false,
|
|
359
|
-
isSelfReferential: type === inverseType,
|
|
360
|
-
// this could be wrong if we are self-referential but also polymorphic
|
|
361
|
-
isReflexive: false // we can't be reflexive if we don't define an inverse
|
|
362
|
-
};
|
|
363
|
-
expandingSet(cache, inverseType, inverseKey, info);
|
|
364
|
-
expandingSet(cache, type, propertyName, info);
|
|
365
|
-
return info;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// CASE: We do have an inverse
|
|
369
|
-
const baseType = inverseDefinition.type;
|
|
370
|
-
cached = expandingGet(cache, baseType, propertyName) || expandingGet(cache, inverseType, inverseKey);
|
|
371
|
-
if (cached) {
|
|
372
|
-
const _isLHS = cached.lhs_baseModelName === baseType;
|
|
373
|
-
const modelNames = _isLHS ? cached.lhs_modelNames : cached.rhs_modelNames;
|
|
374
|
-
// make this lookup easier in the future by caching the key
|
|
375
|
-
modelNames.push(type);
|
|
376
|
-
expandingSet(cache, type, propertyName, cached);
|
|
377
|
-
return cached;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// this is our first time so polish off the metas
|
|
381
|
-
syncMeta(definition, inverseDefinition);
|
|
382
|
-
syncMeta(inverseDefinition, definition);
|
|
383
|
-
const lhs_modelNames = [type];
|
|
384
|
-
if (type !== baseType) {
|
|
385
|
-
lhs_modelNames.push(baseType);
|
|
386
|
-
}
|
|
387
|
-
const isSelfReferential = baseType === inverseType;
|
|
388
|
-
const info = {
|
|
389
|
-
lhs_key: `${baseType}:${propertyName}`,
|
|
390
|
-
lhs_modelNames,
|
|
391
|
-
lhs_baseModelName: baseType,
|
|
392
|
-
lhs_relationshipName: propertyName,
|
|
393
|
-
lhs_definition: definition,
|
|
394
|
-
lhs_isPolymorphic: definition.isPolymorphic,
|
|
395
|
-
rhs_key: `${inverseType}:${inverseKey}`,
|
|
396
|
-
rhs_modelNames: [inverseType],
|
|
397
|
-
rhs_baseModelName: inverseType,
|
|
398
|
-
rhs_relationshipName: inverseKey,
|
|
399
|
-
rhs_definition: inverseDefinition,
|
|
400
|
-
rhs_isPolymorphic: inverseDefinition.isPolymorphic,
|
|
401
|
-
hasInverse: true,
|
|
402
|
-
isSelfReferential,
|
|
403
|
-
isReflexive: isSelfReferential && propertyName === inverseKey
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
// Create entries for the baseModelName as well as modelName to speed up
|
|
407
|
-
// inverse lookups
|
|
408
|
-
expandingSet(cache, baseType, propertyName, info);
|
|
409
|
-
expandingSet(cache, type, propertyName, info);
|
|
410
|
-
|
|
411
|
-
// Greedily populate the inverse
|
|
412
|
-
expandingSet(cache, inverseType, inverseKey, info);
|
|
413
|
-
return info;
|
|
414
|
-
}
|
|
415
|
-
function inverseForRelationship(store, resourceKey, key) {
|
|
416
|
-
const fields = store.schema.fields(resourceKey);
|
|
417
|
-
const definition = fields.get(key);
|
|
418
|
-
if (!definition) {
|
|
419
|
-
return null;
|
|
420
|
-
}
|
|
421
|
-
return definition.options.inverse;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/*
|
|
425
|
-
case many:1
|
|
426
|
-
========
|
|
427
|
-
In a bi-directional graph with Many:1 edges, adding a value
|
|
428
|
-
results in up-to 3 discrete value transitions, while removing
|
|
429
|
-
a value is only 2 transitions.
|
|
430
|
-
|
|
431
|
-
For adding C to A
|
|
432
|
-
If: A <<-> B, C <->> D is the initial state,
|
|
433
|
-
and: B <->> A <<-> C, D is the final state
|
|
434
|
-
|
|
435
|
-
then we would undergo the following transitions.
|
|
436
|
-
|
|
437
|
-
add C to A
|
|
438
|
-
remove C from D
|
|
439
|
-
add A to C
|
|
440
|
-
|
|
441
|
-
For removing B from A
|
|
442
|
-
If: A <<-> B, C <->> D is the initial state,
|
|
443
|
-
and: A, B, C <->> D is the final state
|
|
444
|
-
|
|
445
|
-
then we would undergo the following transitions.
|
|
446
|
-
|
|
447
|
-
remove B from A
|
|
448
|
-
remove A from B
|
|
449
|
-
|
|
450
|
-
case many:many
|
|
451
|
-
===========
|
|
452
|
-
In a bi-directional graph with Many:Many edges, adding or
|
|
453
|
-
removing a value requires only 2 value transitions.
|
|
454
|
-
|
|
455
|
-
For Adding
|
|
456
|
-
If: A<<->>B, C<<->>D is the initial state (double arrows representing the many side)
|
|
457
|
-
And: D<<->>C<<->>A<<->>B is the final state
|
|
458
|
-
|
|
459
|
-
Then we would undergo two transitions.
|
|
460
|
-
|
|
461
|
-
add C to A.
|
|
462
|
-
add A to C
|
|
463
|
-
|
|
464
|
-
For Removing
|
|
465
|
-
If: A<<->>B, C<<->>D is the initial state (double arrows representing the many side)
|
|
466
|
-
And: A, B, C<<->>D is the final state
|
|
467
|
-
|
|
468
|
-
Then we would undergo two transitions.
|
|
469
|
-
|
|
470
|
-
remove B from A
|
|
471
|
-
remove A from B
|
|
472
|
-
|
|
473
|
-
case many:?
|
|
474
|
-
========
|
|
475
|
-
In a uni-directional graph with Many:? edges (modeled in WarpDrive with `inverse:null`) with
|
|
476
|
-
artificial (implicit) inverses, replacing a value results in 2 discrete value transitions.
|
|
477
|
-
This is because a Many:? relationship is effectively Many:Many.
|
|
478
|
-
*/
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/graph/-private/operations/replace-related-records.ts
|
|
479
94
|
function replaceRelatedRecords(graph, op, isRemote) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
} else {
|
|
483
|
-
replaceRelatedRecordsLocal(graph, op, isRemote);
|
|
484
|
-
}
|
|
95
|
+
if (isRemote) replaceRelatedRecordsRemote(graph, op, isRemote);
|
|
96
|
+
else replaceRelatedRecordsLocal(graph, op, isRemote);
|
|
485
97
|
}
|
|
486
98
|
function replaceRelatedRecordsLocal(graph, op, isRemote) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
const diff = diffCollection(resourceKeys, relationship, onAdd, onRemove);
|
|
534
|
-
|
|
535
|
-
// any additions no longer in the local state
|
|
536
|
-
// also need to be removed from the inverse
|
|
537
|
-
if (additions && additions.size > 0) {
|
|
538
|
-
additions.forEach(resourceKey => {
|
|
539
|
-
if (!diff.add.has(resourceKey)) {
|
|
540
|
-
localBecameDirty = true;
|
|
541
|
-
onRemove(resourceKey);
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// any removals no longer in the local state
|
|
547
|
-
// also need to be added back to the inverse
|
|
548
|
-
if (removals && removals.size > 0) {
|
|
549
|
-
removals.forEach(resourceKey => {
|
|
550
|
-
if (!diff.del.has(resourceKey)) {
|
|
551
|
-
localBecameDirty = true;
|
|
552
|
-
onAdd(resourceKey);
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
const becameDirty = diff.changed || localBecameDirty;
|
|
557
|
-
relationship.additions = diff.add;
|
|
558
|
-
relationship.removals = diff.del;
|
|
559
|
-
relationship.localState = diff.finalState;
|
|
560
|
-
|
|
561
|
-
// we only notify if the localState changed and were not already dirty before
|
|
562
|
-
// because if we were already dirty then we have already notified
|
|
563
|
-
if (becameDirty && !wasDirty) {
|
|
564
|
-
notifyChange(graph, relationship);
|
|
565
|
-
}
|
|
99
|
+
const resourceKeys = op.value;
|
|
100
|
+
const relationship = graph.get(op.record, op.field);
|
|
101
|
+
relationship.state.hasReceivedData = true;
|
|
102
|
+
const { additions, removals } = relationship;
|
|
103
|
+
const { inverseKey, type } = relationship.definition;
|
|
104
|
+
const { record } = op;
|
|
105
|
+
const wasDirty = relationship.isDirty;
|
|
106
|
+
let localBecameDirty = false;
|
|
107
|
+
const onAdd = (resourceKey) => {
|
|
108
|
+
const removalsHas = removals?.has(resourceKey);
|
|
109
|
+
if (removalsHas || !additions?.has(resourceKey)) {
|
|
110
|
+
if (type !== resourceKey.type) graph.registerPolymorphicType(type, resourceKey.type);
|
|
111
|
+
localBecameDirty = true;
|
|
112
|
+
addToInverse(graph, resourceKey, inverseKey, op.record, isRemote);
|
|
113
|
+
if (removalsHas) removals.delete(resourceKey);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const onRemove = (resourceKey) => {
|
|
117
|
+
const additionsHas = additions?.has(resourceKey);
|
|
118
|
+
if (additionsHas || !removals?.has(resourceKey)) {
|
|
119
|
+
localBecameDirty = true;
|
|
120
|
+
removeFromInverse(graph, resourceKey, inverseKey, record, isRemote);
|
|
121
|
+
if (additionsHas) additions.delete(resourceKey);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const diff = diffCollection(resourceKeys, relationship, onAdd, onRemove);
|
|
125
|
+
if (additions && additions.size > 0) additions.forEach((resourceKey) => {
|
|
126
|
+
if (!diff.add.has(resourceKey)) {
|
|
127
|
+
localBecameDirty = true;
|
|
128
|
+
onRemove(resourceKey);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
if (removals && removals.size > 0) removals.forEach((resourceKey) => {
|
|
132
|
+
if (!diff.del.has(resourceKey)) {
|
|
133
|
+
localBecameDirty = true;
|
|
134
|
+
onAdd(resourceKey);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const becameDirty = diff.changed || localBecameDirty;
|
|
138
|
+
relationship.additions = diff.add;
|
|
139
|
+
relationship.removals = diff.del;
|
|
140
|
+
relationship.localState = diff.finalState;
|
|
141
|
+
if (becameDirty && !wasDirty) notifyChange(graph, relationship);
|
|
566
142
|
}
|
|
567
143
|
function replaceRelatedRecordsRemote(graph, op, isRemote) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// TODO unsure if we need this but it
|
|
621
|
-
// may allow us to more efficiently patch
|
|
622
|
-
// the associated ManyArray
|
|
623
|
-
relationship._diff = diff;
|
|
624
|
-
{
|
|
625
|
-
// only do this for legacy hasMany, not collection
|
|
626
|
-
// and provide a way to incrementally migrate
|
|
627
|
-
if (
|
|
628
|
-
// we do not guard by diff.changed here
|
|
629
|
-
// because we want to clear local changes even if
|
|
630
|
-
// no change has occurred to preserve the legacy behavior
|
|
631
|
-
relationship.definition.kind === 'hasMany' && relationship.definition.resetOnRemoteUpdate !== false && (diff.changed || wasDirty)) {
|
|
632
|
-
if (relationship.removals) {
|
|
633
|
-
relationship.isDirty = true;
|
|
634
|
-
relationship.removals.forEach(resourceKey => {
|
|
635
|
-
// reverse the removal
|
|
636
|
-
// if we are still in removals at this point then
|
|
637
|
-
// we were not "committed" which means we are present
|
|
638
|
-
// in the remoteMembers. So we "add back" on the inverse.
|
|
639
|
-
addToInverse(graph, resourceKey, definition.inverseKey, op.record, false);
|
|
640
|
-
});
|
|
641
|
-
relationship.removals = null;
|
|
642
|
-
}
|
|
643
|
-
if (relationship.additions) {
|
|
644
|
-
relationship.additions.forEach(resourceKey => {
|
|
645
|
-
// reverse the addition
|
|
646
|
-
// if we are still in additions at this point then
|
|
647
|
-
// we were not "committed" which means we are not present
|
|
648
|
-
// in the remoteMembers. So we "remove" from the inverse.
|
|
649
|
-
// however we only do this if we are not a "new" record.
|
|
650
|
-
if (!checkIfNew(graph._realStore, resourceKey)) {
|
|
651
|
-
relationship.isDirty = true;
|
|
652
|
-
relationship.additions.delete(resourceKey);
|
|
653
|
-
removeFromInverse(graph, resourceKey, definition.inverseKey, op.record, false);
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
if (relationship.additions.size === 0) {
|
|
657
|
-
relationship.additions = null;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
if (relationship.isDirty && !wasDirty) {
|
|
663
|
-
flushCanonical(graph, relationship);
|
|
664
|
-
}
|
|
144
|
+
const resourceKeys = op.value;
|
|
145
|
+
const relationship = graph.get(op.record, op.field);
|
|
146
|
+
if (isRemote) graph._addToTransaction(relationship);
|
|
147
|
+
const wasDirty = relationship.isDirty;
|
|
148
|
+
if (!relationship.state.hasReceivedData) relationship.isDirty = true;
|
|
149
|
+
relationship.state.hasReceivedData = true;
|
|
150
|
+
const { definition } = relationship;
|
|
151
|
+
const { type } = relationship.definition;
|
|
152
|
+
const diff = diffCollection(resourceKeys, relationship, (resourceKey) => {
|
|
153
|
+
if (type !== resourceKey.type) graph.registerPolymorphicType(type, resourceKey.type);
|
|
154
|
+
if (relationship.additions?.has(resourceKey)) relationship.additions.delete(resourceKey);
|
|
155
|
+
addToInverse(graph, resourceKey, definition.inverseKey, op.record, isRemote);
|
|
156
|
+
}, (resourceKey) => {
|
|
157
|
+
if (relationship.removals?.has(resourceKey)) relationship.removals.delete(resourceKey);
|
|
158
|
+
removeFromInverse(graph, resourceKey, definition.inverseKey, op.record, isRemote);
|
|
159
|
+
});
|
|
160
|
+
relationship.remoteMembers = diff.finalSet;
|
|
161
|
+
relationship.remoteState = diff.finalState;
|
|
162
|
+
if (diff.changed) relationship.isDirty = true;
|
|
163
|
+
relationship._diff = diff;
|
|
164
|
+
if (relationship.definition.kind === "hasMany" && relationship.definition.resetOnRemoteUpdate !== false && (diff.changed || wasDirty)) {
|
|
165
|
+
const deprecationInfo = {
|
|
166
|
+
removals: [],
|
|
167
|
+
additions: [],
|
|
168
|
+
triggered: false
|
|
169
|
+
};
|
|
170
|
+
if (relationship.removals) {
|
|
171
|
+
relationship.isDirty = true;
|
|
172
|
+
relationship.removals.forEach((resourceKey) => {
|
|
173
|
+
deprecationInfo.triggered = true;
|
|
174
|
+
deprecationInfo.removals.push(resourceKey);
|
|
175
|
+
addToInverse(graph, resourceKey, definition.inverseKey, op.record, false);
|
|
176
|
+
});
|
|
177
|
+
relationship.removals = null;
|
|
178
|
+
}
|
|
179
|
+
if (relationship.additions) {
|
|
180
|
+
relationship.additions.forEach((resourceKey) => {
|
|
181
|
+
if (!checkIfNew(graph._realStore, resourceKey)) {
|
|
182
|
+
deprecationInfo.triggered = true;
|
|
183
|
+
deprecationInfo.additions.push(resourceKey);
|
|
184
|
+
relationship.isDirty = true;
|
|
185
|
+
relationship.additions.delete(resourceKey);
|
|
186
|
+
removeFromInverse(graph, resourceKey, definition.inverseKey, op.record, false);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
if (relationship.additions.size === 0) relationship.additions = null;
|
|
190
|
+
}
|
|
191
|
+
if (deprecationInfo.triggered) {}
|
|
192
|
+
}
|
|
193
|
+
if (relationship.isDirty && !wasDirty) flushCanonical(graph, relationship);
|
|
665
194
|
}
|
|
666
195
|
function addToInverse(graph, resourceKey, key, value, isRemote) {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
relationship.state.hasReceivedData = true;
|
|
707
|
-
flushCanonical(graph, relationship);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
} else {
|
|
711
|
-
// if we are not dirty but have a null localState then we
|
|
712
|
-
// are mutating a relationship that has never been fetched
|
|
713
|
-
// so we initialize localState to an empty array
|
|
714
|
-
if (!relationship.isDirty && !relationship.localState) {
|
|
715
|
-
relationship.localState = [];
|
|
716
|
-
}
|
|
717
|
-
if (_add(graph, resourceKey, relationship, value, null, isRemote)) {
|
|
718
|
-
notifyChange(graph, relationship);
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
} else {
|
|
722
|
-
if (isRemote) {
|
|
723
|
-
if (!relationship.remoteMembers.has(value)) {
|
|
724
|
-
relationship.remoteMembers.add(value);
|
|
725
|
-
relationship.localMembers.add(value);
|
|
726
|
-
}
|
|
727
|
-
} else {
|
|
728
|
-
if (!relationship.localMembers.has(value)) {
|
|
729
|
-
relationship.localMembers.add(value);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
196
|
+
const relationship = graph.get(resourceKey, key);
|
|
197
|
+
const { type } = relationship.definition;
|
|
198
|
+
if (type !== value.type) graph.registerPolymorphicType(type, value.type);
|
|
199
|
+
if (isBelongsTo(relationship)) {
|
|
200
|
+
relationship.state.hasReceivedData = true;
|
|
201
|
+
relationship.state.isEmpty = false;
|
|
202
|
+
if (isRemote) {
|
|
203
|
+
graph._addToTransaction(relationship);
|
|
204
|
+
if (relationship.remoteState !== null) removeFromInverse(graph, relationship.remoteState, relationship.definition.inverseKey, resourceKey, isRemote);
|
|
205
|
+
relationship.remoteState = value;
|
|
206
|
+
}
|
|
207
|
+
if (relationship.localState !== value) {
|
|
208
|
+
if (!isRemote && relationship.localState) removeFromInverse(graph, relationship.localState, relationship.definition.inverseKey, resourceKey, isRemote);
|
|
209
|
+
relationship.localState = value;
|
|
210
|
+
notifyChange(graph, relationship);
|
|
211
|
+
}
|
|
212
|
+
} else if (isHasMany(relationship)) {
|
|
213
|
+
if (isRemote) {
|
|
214
|
+
if (!relationship.remoteMembers.has(value)) {
|
|
215
|
+
graph._addToTransaction(relationship);
|
|
216
|
+
relationship.remoteState.push(value);
|
|
217
|
+
relationship.remoteMembers.add(value);
|
|
218
|
+
if (relationship.additions?.has(value)) relationship.additions.delete(value);
|
|
219
|
+
else {
|
|
220
|
+
relationship.isDirty = true;
|
|
221
|
+
relationship.state.hasReceivedData = true;
|
|
222
|
+
flushCanonical(graph, relationship);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
if (!relationship.isDirty && !relationship.localState) relationship.localState = [];
|
|
227
|
+
if (_add(graph, resourceKey, relationship, value, null, isRemote)) notifyChange(graph, relationship);
|
|
228
|
+
}
|
|
229
|
+
} else if (isRemote) {
|
|
230
|
+
if (!relationship.remoteMembers.has(value)) {
|
|
231
|
+
relationship.remoteMembers.add(value);
|
|
232
|
+
relationship.localMembers.add(value);
|
|
233
|
+
}
|
|
234
|
+
} else if (!relationship.localMembers.has(value)) relationship.localMembers.add(value);
|
|
733
235
|
}
|
|
734
236
|
function notifyInverseOfPotentialMaterialization(graph, resourceKey, key, value, isRemote) {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
notifyChange(graph, relationship);
|
|
738
|
-
}
|
|
237
|
+
const relationship = graph.get(resourceKey, key);
|
|
238
|
+
if (isHasMany(relationship) && isRemote && relationship.remoteMembers.has(value)) notifyChange(graph, relationship);
|
|
739
239
|
}
|
|
740
240
|
function removeFromInverse(graph, resourceKey, key, value, isRemote) {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
} else {
|
|
764
|
-
if (isRemote) {
|
|
765
|
-
relationship.remoteMembers.delete(value);
|
|
766
|
-
relationship.localMembers.delete(value);
|
|
767
|
-
} else {
|
|
768
|
-
if (value && relationship.localMembers.has(value)) {
|
|
769
|
-
relationship.localMembers.delete(value);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
}
|
|
241
|
+
const relationship = graph.get(resourceKey, key);
|
|
242
|
+
if (isBelongsTo(relationship)) {
|
|
243
|
+
relationship.state.isEmpty = true;
|
|
244
|
+
if (isRemote) {
|
|
245
|
+
graph._addToTransaction(relationship);
|
|
246
|
+
relationship.remoteState = null;
|
|
247
|
+
}
|
|
248
|
+
if (relationship.localState === value) {
|
|
249
|
+
relationship.localState = null;
|
|
250
|
+
notifyChange(graph, relationship);
|
|
251
|
+
}
|
|
252
|
+
} else if (isHasMany(relationship)) {
|
|
253
|
+
if (isRemote) {
|
|
254
|
+
graph._addToTransaction(relationship);
|
|
255
|
+
if (_removeRemote(relationship, value)) notifyChange(graph, relationship);
|
|
256
|
+
} else if (_removeLocal(relationship, value)) notifyChange(graph, relationship);
|
|
257
|
+
} else if (isRemote) {
|
|
258
|
+
relationship.remoteMembers.delete(value);
|
|
259
|
+
relationship.localMembers.delete(value);
|
|
260
|
+
} else if (value && relationship.localMembers.has(value)) relationship.localMembers.delete(value);
|
|
773
261
|
}
|
|
774
262
|
function flushCanonical(graph, rel) {
|
|
775
|
-
|
|
776
|
-
graph._scheduleLocalSync(rel);
|
|
777
|
-
}
|
|
263
|
+
if (rel.accessed) graph._scheduleLocalSync(rel);
|
|
778
264
|
}
|
|
779
|
-
function replaceRelatedRecord(graph, op, isRemote = false) {
|
|
780
|
-
const relationship = graph.get(op.record, op.field);
|
|
781
|
-
if (isRemote) {
|
|
782
|
-
graph._addToTransaction(relationship);
|
|
783
|
-
}
|
|
784
|
-
const {
|
|
785
|
-
definition,
|
|
786
|
-
state
|
|
787
|
-
} = relationship;
|
|
788
|
-
const prop = isRemote ? 'remoteState' : 'localState';
|
|
789
|
-
const existingState = relationship[prop];
|
|
790
|
-
|
|
791
|
-
/*
|
|
792
|
-
case 1:1
|
|
793
|
-
========
|
|
794
|
-
In a bi-directional graph with 1:1 edges, replacing a value
|
|
795
|
-
results in up-to 4 discrete value transitions.
|
|
796
|
-
If: A <-> B, C <-> D is the initial state,
|
|
797
|
-
and: A <-> C, B, D is the final state
|
|
798
|
-
then we would undergo the following 4 transitions.
|
|
799
|
-
remove A from B
|
|
800
|
-
add C to A
|
|
801
|
-
remove C from D
|
|
802
|
-
add A to C
|
|
803
|
-
case 1:many
|
|
804
|
-
===========
|
|
805
|
-
In a bi-directional graph with 1:Many edges, replacing a value
|
|
806
|
-
results in up-to 3 discrete value transitions.
|
|
807
|
-
If: A<->>B<<->D, C<<->D is the initial state (double arrows representing the many side)
|
|
808
|
-
And: A<->>C<<->D, B<<->D is the final state
|
|
809
|
-
Then we would undergo three transitions.
|
|
810
|
-
remove A from B
|
|
811
|
-
add C to A.
|
|
812
|
-
add A to C
|
|
813
|
-
case 1:?
|
|
814
|
-
========
|
|
815
|
-
In a uni-directional graph with 1:? edges (modeled in WarpDrive with `inverse:null`) with
|
|
816
|
-
artificial (implicit) inverses, replacing a value results in up-to 3 discrete value transitions.
|
|
817
|
-
This is because a 1:? relationship is effectively 1:many.
|
|
818
|
-
If: A->B, C->B is the initial state
|
|
819
|
-
And: A->C, C->B is the final state
|
|
820
|
-
Then we would undergo three transitions.
|
|
821
|
-
Remove A from B
|
|
822
|
-
Add C to A
|
|
823
|
-
Add A to C
|
|
824
|
-
*/
|
|
825
265
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (isRemote) {
|
|
881
|
-
const {
|
|
882
|
-
localState,
|
|
883
|
-
remoteState
|
|
884
|
-
} = relationship;
|
|
885
|
-
if (localState && checkIfNew(graph._realStore, localState) && !remoteState) {
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
// when localState does not match the new remoteState and
|
|
889
|
-
// localState === existingState then we had no local mutation
|
|
890
|
-
// and we can safely sync the new remoteState to local
|
|
891
|
-
if (localState !== remoteState && localState === existingState) {
|
|
892
|
-
relationship.localState = remoteState;
|
|
893
|
-
notifyChange(graph, relationship);
|
|
894
|
-
// But when localState does not match the new remoteState and
|
|
895
|
-
// and localState !== existingState then we know we have a local mutation
|
|
896
|
-
// that has not been persisted yet.
|
|
897
|
-
} else {
|
|
898
|
-
if (localState !== remoteState && localState !== existingState && relationship.definition.resetOnRemoteUpdate !== false) {
|
|
899
|
-
relationship.localState = remoteState;
|
|
900
|
-
notifyChange(graph, relationship);
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
} else {
|
|
904
|
-
notifyChange(graph, relationship);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/graph/-private/operations/replace-related-record.ts
|
|
268
|
+
function replaceRelatedRecord(graph, op, isRemote = false) {
|
|
269
|
+
const relationship = graph.get(op.record, op.field);
|
|
270
|
+
if (isRemote) graph._addToTransaction(relationship);
|
|
271
|
+
const { definition, state } = relationship;
|
|
272
|
+
const prop = isRemote ? "remoteState" : "localState";
|
|
273
|
+
const existingState = relationship[prop];
|
|
274
|
+
if (op.value === existingState) {
|
|
275
|
+
state.hasReceivedData = true;
|
|
276
|
+
if (isRemote) {
|
|
277
|
+
state.hasReceivedRemoteData = true;
|
|
278
|
+
const { localState } = relationship;
|
|
279
|
+
if (localState && checkIfNew(graph._realStore, localState) && !existingState) return;
|
|
280
|
+
if (existingState && localState === existingState) notifyInverseOfPotentialMaterialization(graph, existingState, definition.inverseKey, op.record, isRemote);
|
|
281
|
+
else if (localState !== op.value && relationship.definition.resetOnRemoteUpdate !== false) {
|
|
282
|
+
relationship.localState = existingState;
|
|
283
|
+
notifyChange(graph, relationship);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (existingState) removeFromInverse(graph, existingState, definition.inverseKey, op.record, isRemote);
|
|
289
|
+
relationship[prop] = op.value;
|
|
290
|
+
state.hasReceivedData = true;
|
|
291
|
+
/**
|
|
292
|
+
* Local mutations never give us additional information about
|
|
293
|
+
* whether the relationship has received *remote* data. This is
|
|
294
|
+
* what `getRemoteRelationship` relies on to avoid reflecting
|
|
295
|
+
* local-only edits.
|
|
296
|
+
*/
|
|
297
|
+
if (isRemote) state.hasReceivedRemoteData = true;
|
|
298
|
+
state.isEmpty = op.value === null;
|
|
299
|
+
state.isStale = false;
|
|
300
|
+
state.hasFailedLoadAttempt = false;
|
|
301
|
+
if (op.value) {
|
|
302
|
+
if (definition.type !== op.value.type) graph.registerPolymorphicType(definition.type, op.value.type);
|
|
303
|
+
addToInverse(graph, op.value, definition.inverseKey, op.record, isRemote);
|
|
304
|
+
}
|
|
305
|
+
if (isRemote) {
|
|
306
|
+
const { localState, remoteState } = relationship;
|
|
307
|
+
if (localState && checkIfNew(graph._realStore, localState) && !remoteState) return;
|
|
308
|
+
if (localState !== remoteState && localState === existingState) {
|
|
309
|
+
relationship.localState = remoteState;
|
|
310
|
+
notifyChange(graph, relationship);
|
|
311
|
+
} else if (localState !== remoteState && localState !== existingState && relationship.definition.resetOnRemoteUpdate !== false) {
|
|
312
|
+
relationship.localState = remoteState;
|
|
313
|
+
notifyChange(graph, relationship);
|
|
314
|
+
}
|
|
315
|
+
} else notifyChange(graph, relationship);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/graph/-private/-diff.ts
|
|
907
320
|
function _deprecatedCompare(priorLocalState, newState, newMembers, prevState, prevSet, onAdd, onDel, remoteClearsLocal) {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
// if remote order hasn't changed but local order differs
|
|
976
|
-
// and we are configured to reset on remote update (which is deprecated)
|
|
977
|
-
// then we still need to mark the relationship as changed
|
|
978
|
-
} else {
|
|
979
|
-
if (remoteClearsLocal) {
|
|
980
|
-
if (!changed && j < priorLocalLength) {
|
|
981
|
-
const priorLocalMember = priorLocalState[j];
|
|
982
|
-
if (priorLocalMember !== member) {
|
|
983
|
-
changed = true;
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
if (!newMembers.has(prevMember)) {
|
|
989
|
-
changed = true;
|
|
990
|
-
removed.add(prevMember);
|
|
991
|
-
onDel(prevMember);
|
|
992
|
-
}
|
|
993
|
-
} else if (adv && j < prevLength && member !== prevState[j]) {
|
|
994
|
-
changed = true;
|
|
995
|
-
}
|
|
996
|
-
if (adv) {
|
|
997
|
-
j++;
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
const diff = {
|
|
1001
|
-
add: added,
|
|
1002
|
-
del: removed,
|
|
1003
|
-
finalState,
|
|
1004
|
-
finalSet,
|
|
1005
|
-
changed,
|
|
1006
|
-
remoteOrderChanged
|
|
1007
|
-
};
|
|
1008
|
-
return {
|
|
1009
|
-
diff,
|
|
1010
|
-
duplicates
|
|
1011
|
-
};
|
|
321
|
+
const newLength = newState.length;
|
|
322
|
+
const prevLength = prevState.length;
|
|
323
|
+
const iterationLength = Math.max(newLength, prevLength);
|
|
324
|
+
let changed = newMembers.size !== prevSet.size;
|
|
325
|
+
let remoteOrderChanged = false;
|
|
326
|
+
const added = /* @__PURE__ */ new Set();
|
|
327
|
+
const removed = /* @__PURE__ */ new Set();
|
|
328
|
+
const duplicates = /* @__PURE__ */ new Map();
|
|
329
|
+
const finalSet = /* @__PURE__ */ new Set();
|
|
330
|
+
const finalState = [];
|
|
331
|
+
const priorLocalLength = priorLocalState?.length ?? 0;
|
|
332
|
+
for (let i = 0, j = 0; i < iterationLength; i++) {
|
|
333
|
+
let adv = false;
|
|
334
|
+
let member;
|
|
335
|
+
if (i < newLength) {
|
|
336
|
+
member = newState[i];
|
|
337
|
+
if (!finalSet.has(member)) {
|
|
338
|
+
finalState[j] = member;
|
|
339
|
+
finalSet.add(member);
|
|
340
|
+
adv = true;
|
|
341
|
+
if (!prevSet.has(member)) {
|
|
342
|
+
if (i < priorLocalLength) {
|
|
343
|
+
if (priorLocalState[i] !== member) changed = true;
|
|
344
|
+
}
|
|
345
|
+
added.add(member);
|
|
346
|
+
onAdd(member);
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
let list = duplicates.get(member);
|
|
350
|
+
if (list === void 0) {
|
|
351
|
+
list = [];
|
|
352
|
+
duplicates.set(member, list);
|
|
353
|
+
}
|
|
354
|
+
list.push(i);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (i < prevLength) {
|
|
358
|
+
const prevMember = prevState[i];
|
|
359
|
+
if (member !== prevState[j]) {
|
|
360
|
+
remoteOrderChanged = true;
|
|
361
|
+
if (!remoteClearsLocal && i < priorLocalLength) {
|
|
362
|
+
if (priorLocalState[j] !== member) changed = true;
|
|
363
|
+
} else changed = true;
|
|
364
|
+
} else if (remoteClearsLocal) {
|
|
365
|
+
if (!changed && j < priorLocalLength) {
|
|
366
|
+
if (priorLocalState[j] !== member) changed = true;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (!newMembers.has(prevMember)) {
|
|
370
|
+
changed = true;
|
|
371
|
+
removed.add(prevMember);
|
|
372
|
+
onDel(prevMember);
|
|
373
|
+
}
|
|
374
|
+
} else if (adv && j < prevLength && member !== prevState[j]) changed = true;
|
|
375
|
+
if (adv) j++;
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
diff: {
|
|
379
|
+
add: added,
|
|
380
|
+
del: removed,
|
|
381
|
+
finalState,
|
|
382
|
+
finalSet,
|
|
383
|
+
changed,
|
|
384
|
+
remoteOrderChanged
|
|
385
|
+
},
|
|
386
|
+
duplicates
|
|
387
|
+
};
|
|
1012
388
|
}
|
|
1013
389
|
function _compare(priorLocalState, finalState, finalSet, prevState, prevSet, onAdd, onDel, remoteClearsLocal) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
// of the finalState then we must have changed
|
|
1063
|
-
|
|
1064
|
-
changed = true;
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
// if remote order hasn't changed but local order differs
|
|
1068
|
-
// and we are configured to reset on remote update (which is deprecated)
|
|
1069
|
-
// then we still need to mark the relationship as changed
|
|
1070
|
-
} else {
|
|
1071
|
-
if (remoteClearsLocal) {
|
|
1072
|
-
if (equalLength && !changed && i < priorLocalLength) {
|
|
1073
|
-
const priorLocalMember = priorLocalState[i];
|
|
1074
|
-
if (priorLocalMember !== prevMember) {
|
|
1075
|
-
changed = true;
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
if (!finalSet.has(prevMember)) {
|
|
1081
|
-
// if we are within finalLength, we can only be "changed" if we've already exceeded
|
|
1082
|
-
// the index range of priorLocalState, as otherwise the previous member may still
|
|
1083
|
-
// be removed.
|
|
1084
|
-
//
|
|
1085
|
-
// prior local: [1, 2, 3, 4]
|
|
1086
|
-
// final state: [1, 2, 3]
|
|
1087
|
-
// prev remote state: [1, 2, 5, 3, 4]
|
|
1088
|
-
// i === 2
|
|
1089
|
-
// prevMember === 5
|
|
1090
|
-
// !finalSet.has(prevMember) === true
|
|
1091
|
-
//
|
|
1092
|
-
// because we will become changed at i===3,
|
|
1093
|
-
// we do not need to worry about becoming changed at i===2
|
|
1094
|
-
// as the arrays until now are still the same
|
|
1095
|
-
//
|
|
1096
|
-
// prior local: [1, 2, 3]
|
|
1097
|
-
// final state: [1, 2, 3, 4]
|
|
1098
|
-
// prev remote state: [1, 2, 5, 3, 4]
|
|
1099
|
-
// i === 2
|
|
1100
|
-
// prevMember === 5
|
|
1101
|
-
// !finalSet.has(prevMember) === true
|
|
1102
|
-
//
|
|
1103
|
-
// because we will become changed at i===3
|
|
1104
|
-
// we do not need to worry about becoming changed at i===2
|
|
1105
|
-
//
|
|
1106
|
-
// prior local: [1, 2, 3]
|
|
1107
|
-
// final state: [1, 2, 3]
|
|
1108
|
-
// prev remote state: [1, 2, 5, 3, 4]
|
|
1109
|
-
// i === 2
|
|
1110
|
-
// prevMember === 5
|
|
1111
|
-
// !finalSet.has(prevMember) === true
|
|
1112
|
-
//
|
|
1113
|
-
// because we have same length and same membership order
|
|
1114
|
-
// we do not need to worry about becoming changed at i===2
|
|
1115
|
-
//
|
|
1116
|
-
// if you do not have a priorLocalState you can't be changed
|
|
1117
|
-
// ergo, we never need to set changed in this branch.
|
|
1118
|
-
// this log can still be useful for debugging.
|
|
1119
|
-
|
|
1120
|
-
//
|
|
1121
|
-
// we do still set remoteOrderChanged as it has
|
|
1122
|
-
remoteOrderChanged = true;
|
|
1123
|
-
removed.add(prevMember);
|
|
1124
|
-
onDel(prevMember);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
return {
|
|
1129
|
-
add: added,
|
|
1130
|
-
del: removed,
|
|
1131
|
-
finalState,
|
|
1132
|
-
finalSet,
|
|
1133
|
-
changed,
|
|
1134
|
-
remoteOrderChanged
|
|
1135
|
-
};
|
|
390
|
+
const finalLength = finalState.length;
|
|
391
|
+
const prevLength = prevState.length;
|
|
392
|
+
const iterationLength = Math.max(finalLength, prevLength);
|
|
393
|
+
const equalLength = priorLocalState ? finalLength === priorLocalState.length : finalLength === prevLength;
|
|
394
|
+
let remoteOrderChanged = finalSet.size !== prevSet.size;
|
|
395
|
+
let changed = priorLocalState ? finalSet.size !== priorLocalState.length : remoteOrderChanged;
|
|
396
|
+
const added = /* @__PURE__ */ new Set();
|
|
397
|
+
const removed = /* @__PURE__ */ new Set();
|
|
398
|
+
const priorLocalLength = priorLocalState?.length ?? 0;
|
|
399
|
+
for (let i = 0; i < iterationLength; i++) {
|
|
400
|
+
let member;
|
|
401
|
+
if (i < finalLength) {
|
|
402
|
+
member = finalState[i];
|
|
403
|
+
if (!prevSet.has(member)) {
|
|
404
|
+
if (i < priorLocalLength) {
|
|
405
|
+
if (priorLocalState[i] !== member) changed = true;
|
|
406
|
+
}
|
|
407
|
+
added.add(member);
|
|
408
|
+
onAdd(member);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (i < prevLength) {
|
|
412
|
+
const prevMember = prevState[i];
|
|
413
|
+
if (equalLength && member !== prevMember) {
|
|
414
|
+
remoteOrderChanged = true;
|
|
415
|
+
if (i < priorLocalLength) {
|
|
416
|
+
if (priorLocalState[i] !== member) changed = true;
|
|
417
|
+
} else if (i < finalLength) changed = true;
|
|
418
|
+
} else if (remoteClearsLocal) {
|
|
419
|
+
if (equalLength && !changed && i < priorLocalLength) {
|
|
420
|
+
if (priorLocalState[i] !== prevMember) changed = true;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (!finalSet.has(prevMember)) {
|
|
424
|
+
remoteOrderChanged = true;
|
|
425
|
+
removed.add(prevMember);
|
|
426
|
+
onDel(prevMember);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
add: added,
|
|
432
|
+
del: removed,
|
|
433
|
+
finalState,
|
|
434
|
+
finalSet,
|
|
435
|
+
changed,
|
|
436
|
+
remoteOrderChanged
|
|
437
|
+
};
|
|
1136
438
|
}
|
|
1137
439
|
function diffCollection(finalState, relationship, onAdd, onDel) {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (finalState.length !== finalSet.size) {
|
|
1146
|
-
const {
|
|
1147
|
-
diff
|
|
1148
|
-
} = _deprecatedCompare(priorLocalState, finalState, finalSet, remoteState, remoteMembers, onAdd, onDel, relationship.definition.resetOnRemoteUpdate);
|
|
1149
|
-
return diff;
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
return _compare(priorLocalState, finalState, finalSet, remoteState, remoteMembers, onAdd, onDel, relationship.definition.resetOnRemoteUpdate);
|
|
440
|
+
const finalSet = new Set(finalState);
|
|
441
|
+
const { localState: priorLocalState, remoteState, remoteMembers } = relationship;
|
|
442
|
+
if (finalState.length !== finalSet.size) {
|
|
443
|
+
const { diff, duplicates } = _deprecatedCompare(priorLocalState, finalState, finalSet, remoteState, remoteMembers, onAdd, onDel, relationship.definition.resetOnRemoteUpdate);
|
|
444
|
+
return diff;
|
|
445
|
+
}
|
|
446
|
+
return _compare(priorLocalState, finalState, finalSet, remoteState, remoteMembers, onAdd, onDel, relationship.definition.resetOnRemoteUpdate);
|
|
1153
447
|
}
|
|
1154
448
|
function computeLocalState(storage) {
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
storage.isDirty = false;
|
|
1168
|
-
return state;
|
|
449
|
+
if (!storage.isDirty && storage.localState) return storage.localState;
|
|
450
|
+
const state = storage.remoteState.slice();
|
|
451
|
+
storage.removals?.forEach((v) => {
|
|
452
|
+
const index = state.indexOf(v);
|
|
453
|
+
state.splice(index, 1);
|
|
454
|
+
});
|
|
455
|
+
storage.additions?.forEach((v) => {
|
|
456
|
+
state.push(v);
|
|
457
|
+
});
|
|
458
|
+
storage.localState = state;
|
|
459
|
+
storage.isDirty = false;
|
|
460
|
+
return state;
|
|
1169
461
|
}
|
|
1170
|
-
|
|
1171
462
|
/**
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
463
|
+
* A function which attempts to add a value to the local state of a collection
|
|
464
|
+
* relationship, and returns true if the value was added, or false if it was
|
|
465
|
+
* already present.
|
|
466
|
+
*
|
|
467
|
+
* It will not generate a notification, will not update the relationships to dirty,
|
|
468
|
+
* and will not update the inverse relationships, making it suitable for use as
|
|
469
|
+
* an internal util to perform the just the addition to a specific side of a
|
|
470
|
+
* relationship.
|
|
471
|
+
*
|
|
472
|
+
* @internal
|
|
473
|
+
*/
|
|
1183
474
|
function _add(graph, record, relationship, value, index, isRemote) {
|
|
1184
|
-
|
|
475
|
+
return !isRemote ? _addLocal(graph, record, relationship, value, index) : _addRemote(graph, record, relationship, value, index);
|
|
1185
476
|
}
|
|
1186
477
|
function _addRemote(graph, record, relationship, value, index) {
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
// remove from additions if present
|
|
1207
|
-
if (additions?.has(value)) {
|
|
1208
|
-
additions.delete(value);
|
|
1209
|
-
|
|
1210
|
-
// nothing more to do this was our state already
|
|
1211
|
-
return false;
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
// if the relationship already needs to recalc, we don't bother
|
|
1215
|
-
// attempting to patch the localState
|
|
1216
|
-
if (relationship.isDirty) {
|
|
1217
|
-
return true;
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// if we have existing localState
|
|
1221
|
-
// we attempt to patch it without blowing it away
|
|
1222
|
-
// as this is more efficient than recomputing
|
|
1223
|
-
// it allows us to preserve local ordering
|
|
1224
|
-
// to a small extent. Local ordering should not
|
|
1225
|
-
// be relied upon as any remote change could blow it away
|
|
1226
|
-
if (relationship.localState) {
|
|
1227
|
-
if (!hasValidIndex) {
|
|
1228
|
-
relationship.localState.push(value);
|
|
1229
|
-
} else if (index === 0) {
|
|
1230
|
-
relationship.localState.unshift(value);
|
|
1231
|
-
} else if (!removals?.size) {
|
|
1232
|
-
relationship.localState.splice(index, 0, value);
|
|
1233
|
-
} else {
|
|
1234
|
-
relationship.isDirty = true;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
return true;
|
|
478
|
+
const { remoteMembers, additions, removals, remoteState } = relationship;
|
|
479
|
+
if (remoteMembers.has(value)) return false;
|
|
480
|
+
remoteMembers.add(value);
|
|
481
|
+
const hasValidIndex = index !== null && index >= 0 && index < remoteState.length;
|
|
482
|
+
if (hasValidIndex) remoteState.splice(index, 0, value);
|
|
483
|
+
else remoteState.push(value);
|
|
484
|
+
if (additions?.has(value)) {
|
|
485
|
+
additions.delete(value);
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
if (relationship.isDirty) return true;
|
|
489
|
+
if (relationship.localState) {
|
|
490
|
+
if (!hasValidIndex) relationship.localState.push(value);
|
|
491
|
+
else if (index === 0) relationship.localState.unshift(value);
|
|
492
|
+
else if (!removals?.size) relationship.localState.splice(index, 0, value);
|
|
493
|
+
else relationship.isDirty = true;
|
|
494
|
+
}
|
|
495
|
+
return true;
|
|
1238
496
|
}
|
|
1239
497
|
function _addLocal(graph, record, relationship, value, index) {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
additions.add(value);
|
|
1257
|
-
const {
|
|
1258
|
-
type
|
|
1259
|
-
} = relationship.definition;
|
|
1260
|
-
if (type !== value.type) {
|
|
1261
|
-
graph.registerPolymorphicType(value.type, type);
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
// if we have existing localState
|
|
1266
|
-
// and we have an index
|
|
1267
|
-
// apply the change, as this is more efficient
|
|
1268
|
-
// than recomputing localState and
|
|
1269
|
-
// it allows us to preserve local ordering
|
|
1270
|
-
// to a small extend. Local ordering should not
|
|
1271
|
-
// be relied upon as any remote change will blow it away
|
|
1272
|
-
if (relationship.localState) {
|
|
1273
|
-
if (index !== null) {
|
|
1274
|
-
relationship.localState.splice(index, 0, value);
|
|
1275
|
-
} else {
|
|
1276
|
-
relationship.localState.push(value);
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
return true;
|
|
498
|
+
const { remoteMembers, removals } = relationship;
|
|
499
|
+
let additions = relationship.additions;
|
|
500
|
+
if ((remoteMembers.has(value) || additions?.has(value)) && !removals?.has(value)) return false;
|
|
501
|
+
if (removals?.has(value)) removals.delete(value);
|
|
502
|
+
else {
|
|
503
|
+
if (!additions) additions = relationship.additions = /* @__PURE__ */ new Set();
|
|
504
|
+
relationship.state.hasReceivedData = true;
|
|
505
|
+
additions.add(value);
|
|
506
|
+
const { type } = relationship.definition;
|
|
507
|
+
if (type !== value.type) graph.registerPolymorphicType(value.type, type);
|
|
508
|
+
}
|
|
509
|
+
if (relationship.localState) {
|
|
510
|
+
if (index !== null) relationship.localState.splice(index, 0, value);
|
|
511
|
+
else relationship.localState.push(value);
|
|
512
|
+
}
|
|
513
|
+
return true;
|
|
1280
514
|
}
|
|
1281
515
|
function _remove(graph, record, relationship, value, index, isRemote) {
|
|
1282
|
-
|
|
516
|
+
return !isRemote ? _removeLocal(relationship, value) : _removeRemote(relationship, value);
|
|
1283
517
|
}
|
|
1284
518
|
function _removeLocal(relationship, value) {
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
removals = relationship.removals = new Set();
|
|
1299
|
-
}
|
|
1300
|
-
removals.add(value);
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
// if we have existing localState
|
|
1304
|
-
// apply the change, as this is more efficient
|
|
1305
|
-
// than recomputing localState and
|
|
1306
|
-
// it allows us to preserve local ordering
|
|
1307
|
-
// to a small extend. Local ordering should not
|
|
1308
|
-
// be relied upon as any remote change will blow it away
|
|
1309
|
-
if (relationship.localState) {
|
|
1310
|
-
const index = relationship.localState.indexOf(value);
|
|
1311
|
-
relationship.localState.splice(index, 1);
|
|
1312
|
-
}
|
|
1313
|
-
return true;
|
|
519
|
+
const { remoteMembers, additions } = relationship;
|
|
520
|
+
let removals = relationship.removals;
|
|
521
|
+
if (!(remoteMembers.has(value) || additions?.has(value)) || removals?.has(value)) return false;
|
|
522
|
+
if (additions?.has(value)) additions.delete(value);
|
|
523
|
+
else {
|
|
524
|
+
if (!removals) removals = relationship.removals = /* @__PURE__ */ new Set();
|
|
525
|
+
removals.add(value);
|
|
526
|
+
}
|
|
527
|
+
if (relationship.localState) {
|
|
528
|
+
const index = relationship.localState.indexOf(value);
|
|
529
|
+
relationship.localState.splice(index, 1);
|
|
530
|
+
}
|
|
531
|
+
return true;
|
|
1314
532
|
}
|
|
1315
533
|
function _removeRemote(relationship, value) {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
// remove from removals if present
|
|
1332
|
-
if (removals?.has(value)) {
|
|
1333
|
-
removals.delete(value);
|
|
1334
|
-
|
|
1335
|
-
// nothing more to do this was our state already
|
|
1336
|
-
return false;
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
// if we have existing localState
|
|
1340
|
-
// and we have an index
|
|
1341
|
-
// apply the change, as this is more efficient
|
|
1342
|
-
// than recomputing localState and
|
|
1343
|
-
// it allows us to preserve local ordering
|
|
1344
|
-
// to a small extend. Local ordering should not
|
|
1345
|
-
// be relied upon as any remote change will blow it away
|
|
1346
|
-
if (relationship.localState) {
|
|
1347
|
-
index = relationship.localState.indexOf(value);
|
|
1348
|
-
relationship.localState.splice(index, 1);
|
|
1349
|
-
}
|
|
1350
|
-
return true;
|
|
534
|
+
const { remoteMembers, additions, removals, remoteState } = relationship;
|
|
535
|
+
if (!remoteMembers.has(value)) return false;
|
|
536
|
+
remoteMembers.delete(value);
|
|
537
|
+
let index = remoteState.indexOf(value);
|
|
538
|
+
remoteState.splice(index, 1);
|
|
539
|
+
if (removals?.has(value)) {
|
|
540
|
+
removals.delete(value);
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
if (relationship.localState) {
|
|
544
|
+
index = relationship.localState.indexOf(value);
|
|
545
|
+
relationship.localState.splice(index, 1);
|
|
546
|
+
}
|
|
547
|
+
return true;
|
|
1351
548
|
}
|
|
1352
549
|
function rollbackRelationship(graph, key, field, relationship) {
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
550
|
+
if (isBelongsTo(relationship)) replaceRelatedRecord(graph, {
|
|
551
|
+
op: "replaceRelatedRecord",
|
|
552
|
+
record: key,
|
|
553
|
+
field,
|
|
554
|
+
value: relationship.remoteState
|
|
555
|
+
}, false);
|
|
556
|
+
else {
|
|
557
|
+
replaceRelatedRecords(graph, {
|
|
558
|
+
op: "replaceRelatedRecords",
|
|
559
|
+
record: key,
|
|
560
|
+
field,
|
|
561
|
+
value: relationship.remoteState.slice()
|
|
562
|
+
}, false);
|
|
563
|
+
notifyChange(graph, relationship);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
//#endregion
|
|
568
|
+
//#region src/graph/-private/-edge-definition.ts
|
|
569
|
+
function isLegacyField(field) {
|
|
570
|
+
return field.kind === "belongsTo" || field.kind === "hasMany";
|
|
1373
571
|
}
|
|
1374
|
-
function
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
572
|
+
function temporaryConvertToLegacy(field) {
|
|
573
|
+
return {
|
|
574
|
+
kind: field.kind === "resource" ? "belongsTo" : "hasMany",
|
|
575
|
+
name: field.name,
|
|
576
|
+
type: field.type,
|
|
577
|
+
options: Object.assign({}, {
|
|
578
|
+
async: false,
|
|
579
|
+
inverse: null,
|
|
580
|
+
resetOnRemoteUpdate: false
|
|
581
|
+
}, field.options)
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
*
|
|
586
|
+
* Given RHS (Right Hand Side)
|
|
587
|
+
*
|
|
588
|
+
* ```ts
|
|
589
|
+
* class User extends Model {
|
|
590
|
+
* @hasMany('animal', { async: false, inverse: 'owner' }) pets;
|
|
591
|
+
* }
|
|
592
|
+
* ```
|
|
593
|
+
*
|
|
594
|
+
* Given LHS (Left Hand Side)
|
|
595
|
+
*
|
|
596
|
+
* ```ts
|
|
597
|
+
* class Animal extends Model {
|
|
598
|
+
* @belongsTo('user', { async: false, inverse: 'pets' }) owner;
|
|
599
|
+
* }
|
|
600
|
+
* ```
|
|
601
|
+
*
|
|
602
|
+
* The UpgradedMeta for the RHS would be:
|
|
603
|
+
*
|
|
604
|
+
* ```ts
|
|
605
|
+
* {
|
|
606
|
+
* kind: 'hasMany',
|
|
607
|
+
* key: 'pets',
|
|
608
|
+
* type: 'animal',
|
|
609
|
+
* isAsync: false,
|
|
610
|
+
* isImplicit: false,
|
|
611
|
+
* isCollection: true,
|
|
612
|
+
* isPolymorphic: false,
|
|
613
|
+
* inverseKind: 'belongsTo',
|
|
614
|
+
* inverseKey: 'owner',
|
|
615
|
+
* inverseType: 'user',
|
|
616
|
+
* inverseIsAsync: false,
|
|
617
|
+
* inverseIsImplicit: false,
|
|
618
|
+
* inverseIsCollection: false,
|
|
619
|
+
* inverseIsPolymorphic: false,
|
|
620
|
+
* }
|
|
621
|
+
* ```
|
|
622
|
+
*
|
|
623
|
+
* The UpgradeMeta for the LHS would be:
|
|
624
|
+
*
|
|
625
|
+
* ```ts
|
|
626
|
+
* {
|
|
627
|
+
* kind: 'belongsTo',
|
|
628
|
+
* key: 'owner',
|
|
629
|
+
* type: 'user',
|
|
630
|
+
* isAsync: false,
|
|
631
|
+
* isImplicit: false,
|
|
632
|
+
* isCollection: false,
|
|
633
|
+
* isPolymorphic: false,
|
|
634
|
+
* inverseKind: 'hasMany',
|
|
635
|
+
* inverseKey: 'pets',
|
|
636
|
+
* inverseType: 'animal',
|
|
637
|
+
* inverseIsAsync: false,
|
|
638
|
+
* inverseIsImplicit: false,
|
|
639
|
+
* inverseIsCollection: true,
|
|
640
|
+
* inverseIsPolymorphic: false,
|
|
641
|
+
* }
|
|
642
|
+
* ```
|
|
643
|
+
*
|
|
644
|
+
* @private
|
|
645
|
+
*/
|
|
646
|
+
const BOOL_LATER = null;
|
|
647
|
+
const STR_LATER = "";
|
|
648
|
+
const IMPLICIT_KEY_RAND = Date.now();
|
|
649
|
+
function implicitKeyFor(type, key) {
|
|
650
|
+
return `implicit-${type}:${key}${IMPLICIT_KEY_RAND}`;
|
|
1383
651
|
}
|
|
652
|
+
function syncMeta(definition, inverseDefinition) {
|
|
653
|
+
definition.inverseKind = inverseDefinition.kind;
|
|
654
|
+
definition.inverseKey = inverseDefinition.key;
|
|
655
|
+
definition.inverseName = inverseDefinition.name;
|
|
656
|
+
definition.inverseType = inverseDefinition.type;
|
|
657
|
+
definition.inverseIsAsync = inverseDefinition.isAsync;
|
|
658
|
+
definition.inverseIsCollection = inverseDefinition.isCollection;
|
|
659
|
+
definition.inverseIsPolymorphic = inverseDefinition.isPolymorphic;
|
|
660
|
+
definition.inverseIsImplicit = inverseDefinition.isImplicit;
|
|
661
|
+
definition.inverseIsLinksMode = inverseDefinition.isLinksMode;
|
|
662
|
+
const resetOnRemoteUpdate = definition.resetOnRemoteUpdate === false || inverseDefinition.resetOnRemoteUpdate === false ? false : true;
|
|
663
|
+
definition.resetOnRemoteUpdate = resetOnRemoteUpdate;
|
|
664
|
+
inverseDefinition.resetOnRemoteUpdate = resetOnRemoteUpdate;
|
|
665
|
+
}
|
|
666
|
+
function upgradeMeta(meta) {
|
|
667
|
+
if (!isLegacyField(meta)) meta = temporaryConvertToLegacy(meta);
|
|
668
|
+
const niceMeta = {};
|
|
669
|
+
const options = meta.options;
|
|
670
|
+
niceMeta.kind = meta.kind;
|
|
671
|
+
niceMeta.key = meta.sourceKey ?? meta.name;
|
|
672
|
+
niceMeta.name = meta.name;
|
|
673
|
+
niceMeta.type = meta.type;
|
|
674
|
+
niceMeta.isAsync = options.async;
|
|
675
|
+
niceMeta.isImplicit = false;
|
|
676
|
+
niceMeta.isCollection = meta.kind === "hasMany";
|
|
677
|
+
niceMeta.isPolymorphic = options && !!options.polymorphic;
|
|
678
|
+
niceMeta.isLinksMode = options.linksMode ?? false;
|
|
679
|
+
niceMeta.inverseKey = options && options.inverse || STR_LATER;
|
|
680
|
+
niceMeta.inverseName = options && options.inverse || STR_LATER;
|
|
681
|
+
niceMeta.inverseType = STR_LATER;
|
|
682
|
+
niceMeta.inverseIsAsync = BOOL_LATER;
|
|
683
|
+
niceMeta.inverseIsImplicit = options && options.inverse === null || BOOL_LATER;
|
|
684
|
+
niceMeta.inverseIsCollection = BOOL_LATER;
|
|
685
|
+
niceMeta.inverseIsLinksMode = BOOL_LATER;
|
|
686
|
+
niceMeta.resetOnRemoteUpdate = !isLegacyField(meta) ? false : meta.options?.linksMode ? false : meta.options?.resetOnRemoteUpdate === false ? false : true;
|
|
687
|
+
return niceMeta;
|
|
688
|
+
}
|
|
689
|
+
function isLHS(info, type, key) {
|
|
690
|
+
const isSelfReferential = info.isSelfReferential;
|
|
691
|
+
if (key === info.lhs_relationshipName === true) return isSelfReferential === true || type === info.lhs_baseModelName || info.rhs_isPolymorphic && info.lhs_modelNames.includes(type);
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
function upgradeDefinition(graph, key, propertyName, isImplicit = false) {
|
|
695
|
+
const cache = graph._definitionCache;
|
|
696
|
+
const storeWrapper = graph.store;
|
|
697
|
+
const polymorphicLookup = graph._potentialPolymorphicTypes;
|
|
698
|
+
const { type } = key;
|
|
699
|
+
let cached = expandingGet(cache, type, propertyName);
|
|
700
|
+
if (cached !== void 0) return cached;
|
|
701
|
+
const relationships = storeWrapper.schema.fields(key);
|
|
702
|
+
const meta = (storeWrapper.schema.cacheFields?.(key) ?? relationships).get(propertyName);
|
|
703
|
+
if (!meta) {
|
|
704
|
+
if (polymorphicLookup[type]) {
|
|
705
|
+
const altTypes = Object.keys(polymorphicLookup[type]);
|
|
706
|
+
for (let i = 0; i < altTypes.length; i++) {
|
|
707
|
+
const _cached = expandingGet(cache, altTypes[i], propertyName);
|
|
708
|
+
if (_cached) {
|
|
709
|
+
expandingSet(cache, type, propertyName, _cached);
|
|
710
|
+
_cached.rhs_modelNames.push(type);
|
|
711
|
+
return _cached;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
cache[type][propertyName] = null;
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
const definition = upgradeMeta(meta);
|
|
719
|
+
let inverseDefinition;
|
|
720
|
+
let inverseKey;
|
|
721
|
+
const inverseType = definition.type;
|
|
722
|
+
if (definition.inverseKey === null) inverseDefinition = null;
|
|
723
|
+
else {
|
|
724
|
+
inverseKey = inverseForRelationship(getStore(storeWrapper), key, propertyName);
|
|
725
|
+
if (!inverseKey && definition.isPolymorphic && definition.inverseKey) inverseDefinition = {
|
|
726
|
+
kind: "belongsTo",
|
|
727
|
+
key: definition.inverseKey,
|
|
728
|
+
name: definition.inverseName,
|
|
729
|
+
type,
|
|
730
|
+
isAsync: false,
|
|
731
|
+
isImplicit: false,
|
|
732
|
+
isCollection: false,
|
|
733
|
+
isPolymorphic: false
|
|
734
|
+
};
|
|
735
|
+
else if (!inverseKey) inverseDefinition = null;
|
|
736
|
+
else inverseDefinition = upgradeMeta(storeWrapper.schema.fields({ type: inverseType }).get(inverseKey));
|
|
737
|
+
}
|
|
738
|
+
if (!inverseDefinition) {
|
|
739
|
+
inverseKey = implicitKeyFor(type, propertyName);
|
|
740
|
+
inverseDefinition = {
|
|
741
|
+
kind: "implicit",
|
|
742
|
+
key: inverseKey,
|
|
743
|
+
type,
|
|
744
|
+
isAsync: false,
|
|
745
|
+
isImplicit: true,
|
|
746
|
+
isCollection: true,
|
|
747
|
+
isPolymorphic: false
|
|
748
|
+
};
|
|
749
|
+
syncMeta(definition, inverseDefinition);
|
|
750
|
+
syncMeta(inverseDefinition, definition);
|
|
751
|
+
const info = {
|
|
752
|
+
lhs_key: `${type}:${propertyName}`,
|
|
753
|
+
lhs_modelNames: [type],
|
|
754
|
+
lhs_baseModelName: type,
|
|
755
|
+
lhs_relationshipName: propertyName,
|
|
756
|
+
lhs_definition: definition,
|
|
757
|
+
lhs_isPolymorphic: definition.isPolymorphic,
|
|
758
|
+
rhs_key: inverseDefinition.key,
|
|
759
|
+
rhs_modelNames: [inverseType],
|
|
760
|
+
rhs_baseModelName: inverseType,
|
|
761
|
+
rhs_relationshipName: inverseDefinition.key,
|
|
762
|
+
rhs_definition: inverseDefinition,
|
|
763
|
+
rhs_isPolymorphic: false,
|
|
764
|
+
hasInverse: false,
|
|
765
|
+
isSelfReferential: type === inverseType,
|
|
766
|
+
isReflexive: false
|
|
767
|
+
};
|
|
768
|
+
expandingSet(cache, inverseType, inverseKey, info);
|
|
769
|
+
expandingSet(cache, type, propertyName, info);
|
|
770
|
+
return info;
|
|
771
|
+
}
|
|
772
|
+
const baseType = inverseDefinition.type;
|
|
773
|
+
cached = expandingGet(cache, baseType, propertyName) || expandingGet(cache, inverseType, inverseKey);
|
|
774
|
+
if (cached) {
|
|
775
|
+
const _isLHS = cached.lhs_baseModelName === baseType;
|
|
776
|
+
_isLHS ? cached.lhs_relationshipName : cached.rhs_relationshipName;
|
|
777
|
+
cached.lhs_baseModelName === baseType && cached.lhs_relationshipName === propertyName || cached.rhs_baseModelName === baseType && cached.rhs_relationshipName;
|
|
778
|
+
(_isLHS ? cached.lhs_modelNames : cached.rhs_modelNames).push(type);
|
|
779
|
+
expandingSet(cache, type, propertyName, cached);
|
|
780
|
+
return cached;
|
|
781
|
+
}
|
|
782
|
+
syncMeta(definition, inverseDefinition);
|
|
783
|
+
syncMeta(inverseDefinition, definition);
|
|
784
|
+
const lhs_modelNames = [type];
|
|
785
|
+
if (type !== baseType) lhs_modelNames.push(baseType);
|
|
786
|
+
const isSelfReferential = baseType === inverseType;
|
|
787
|
+
const info = {
|
|
788
|
+
lhs_key: `${baseType}:${propertyName}`,
|
|
789
|
+
lhs_modelNames,
|
|
790
|
+
lhs_baseModelName: baseType,
|
|
791
|
+
lhs_relationshipName: propertyName,
|
|
792
|
+
lhs_definition: definition,
|
|
793
|
+
lhs_isPolymorphic: definition.isPolymorphic,
|
|
794
|
+
rhs_key: `${inverseType}:${inverseKey}`,
|
|
795
|
+
rhs_modelNames: [inverseType],
|
|
796
|
+
rhs_baseModelName: inverseType,
|
|
797
|
+
rhs_relationshipName: inverseKey,
|
|
798
|
+
rhs_definition: inverseDefinition,
|
|
799
|
+
rhs_isPolymorphic: inverseDefinition.isPolymorphic,
|
|
800
|
+
hasInverse: true,
|
|
801
|
+
isSelfReferential,
|
|
802
|
+
isReflexive: isSelfReferential && propertyName === inverseKey
|
|
803
|
+
};
|
|
804
|
+
expandingSet(cache, baseType, propertyName, info);
|
|
805
|
+
expandingSet(cache, type, propertyName, info);
|
|
806
|
+
expandingSet(cache, inverseType, inverseKey, info);
|
|
807
|
+
return info;
|
|
808
|
+
}
|
|
809
|
+
function inverseForRelationship(store, resourceKey, key) {
|
|
810
|
+
const definition = store.schema.fields(resourceKey).get(key);
|
|
811
|
+
if (!definition) return null;
|
|
812
|
+
return definition.options.inverse;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
//#endregion
|
|
816
|
+
//#region src/graph/-private/-state.ts
|
|
817
|
+
function createState() {
|
|
818
|
+
return {
|
|
819
|
+
hasReceivedData: false,
|
|
820
|
+
hasReceivedRemoteData: false,
|
|
821
|
+
isEmpty: true,
|
|
822
|
+
isStale: false,
|
|
823
|
+
hasFailedLoadAttempt: false,
|
|
824
|
+
shouldForceReload: false,
|
|
825
|
+
hasDematerializedInverse: false
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
//#endregion
|
|
830
|
+
//#region src/graph/-private/edges/collection.ts
|
|
831
|
+
/**
|
|
832
|
+
* Stores the graph's internal state for one side of a collection (`hasMany`) relationship.
|
|
833
|
+
*/
|
|
834
|
+
/** Creates a new {@link CollectionEdge} for the given definition and identifier. */
|
|
1384
835
|
function createCollectionEdge(definition, identifier) {
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
836
|
+
return {
|
|
837
|
+
definition,
|
|
838
|
+
identifier,
|
|
839
|
+
state: createState(),
|
|
840
|
+
remoteMembers: /* @__PURE__ */ new Set(),
|
|
841
|
+
remoteState: [],
|
|
842
|
+
additions: null,
|
|
843
|
+
removals: null,
|
|
844
|
+
meta: null,
|
|
845
|
+
links: null,
|
|
846
|
+
localState: null,
|
|
847
|
+
isDirty: false,
|
|
848
|
+
transactionRef: 0,
|
|
849
|
+
accessed: false,
|
|
850
|
+
_diff: void 0
|
|
851
|
+
};
|
|
1401
852
|
}
|
|
1402
853
|
const cp = structuredClone;
|
|
854
|
+
/**
|
|
855
|
+
* Builds a legacy `{ data, links, meta }` {@link CollectionRelationship} payload for this edge,
|
|
856
|
+
* using either the remote or the computed local state, and marks the edge as accessed.
|
|
857
|
+
*/
|
|
1403
858
|
function legacyGetCollectionRelationshipData(source, getRemoteState) {
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
payload.links = cp(source.links);
|
|
1411
|
-
}
|
|
1412
|
-
if (source.meta) {
|
|
1413
|
-
payload.meta = cp(source.meta);
|
|
1414
|
-
}
|
|
1415
|
-
return payload;
|
|
859
|
+
source.accessed = true;
|
|
860
|
+
const payload = {};
|
|
861
|
+
if (source.state.hasReceivedData) payload.data = getRemoteState ? source.remoteState.slice() : computeLocalState(source);
|
|
862
|
+
if (source.links) payload.links = cp(source.links);
|
|
863
|
+
if (source.meta) payload.meta = cp(source.meta);
|
|
864
|
+
return payload;
|
|
1416
865
|
}
|
|
1417
866
|
|
|
867
|
+
//#endregion
|
|
868
|
+
//#region src/graph/-private/edges/implicit.ts
|
|
869
|
+
/**
|
|
870
|
+
* The relationship definition (schema metadata) backing an {@link ImplicitEdge}, tagging
|
|
871
|
+
* the {@link UpgradedMeta} as belonging to an implicit inverse relationship.
|
|
872
|
+
*/
|
|
1418
873
|
/**
|
|
1419
|
-
|
|
1420
|
-
|
|
874
|
+
Implicit relationships are relationships which have not been declared but the inverse side exists on
|
|
875
|
+
another record somewhere
|
|
1421
876
|
|
|
1422
|
-
|
|
877
|
+
For example consider the following two models
|
|
1423
878
|
|
|
1424
|
-
|
|
879
|
+
::: code-group
|
|
1425
880
|
|
|
1426
|
-
|
|
1427
|
-
|
|
881
|
+
```js [./models/comment.js]
|
|
882
|
+
import { Model, attr } from '@warp-drive/legacy/model';
|
|
1428
883
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
884
|
+
export default class Comment extends Model {
|
|
885
|
+
@attr text;
|
|
886
|
+
}
|
|
887
|
+
```
|
|
1433
888
|
|
|
1434
|
-
|
|
1435
|
-
|
|
889
|
+
```js [./models/post.js]
|
|
890
|
+
import { Model, attr, hasMany } from '@warp-drive/legacy/model';
|
|
1436
891
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
892
|
+
export default class Post extends Model {
|
|
893
|
+
@attr title;
|
|
894
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
895
|
+
}
|
|
896
|
+
```
|
|
1442
897
|
|
|
1443
|
-
|
|
898
|
+
:::
|
|
1444
899
|
|
|
1445
|
-
|
|
1446
|
-
|
|
900
|
+
Then we would have a implicit 'post' relationship for the comment record in order
|
|
901
|
+
to be do things like remove the comment from the post if the comment were to be deleted.
|
|
1447
902
|
*/
|
|
1448
|
-
|
|
903
|
+
/** Creates a new {@link ImplicitEdge} for the given definition and identifier. */
|
|
1449
904
|
function createImplicitEdge(definition, identifier) {
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
905
|
+
return {
|
|
906
|
+
definition,
|
|
907
|
+
identifier,
|
|
908
|
+
localMembers: /* @__PURE__ */ new Set(),
|
|
909
|
+
remoteMembers: /* @__PURE__ */ new Set()
|
|
910
|
+
};
|
|
1456
911
|
}
|
|
1457
912
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
*
|
|
1461
|
-
* @private
|
|
1462
|
-
*/
|
|
1463
|
-
|
|
913
|
+
//#endregion
|
|
914
|
+
//#region src/graph/-private/edges/resource.ts
|
|
1464
915
|
function createResourceEdge(definition, identifier) {
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
916
|
+
return {
|
|
917
|
+
definition,
|
|
918
|
+
identifier,
|
|
919
|
+
state: createState(),
|
|
920
|
+
transactionRef: 0,
|
|
921
|
+
localState: null,
|
|
922
|
+
remoteState: null,
|
|
923
|
+
meta: null,
|
|
924
|
+
links: null,
|
|
925
|
+
accessed: false
|
|
926
|
+
};
|
|
1476
927
|
}
|
|
1477
928
|
function legacyGetResourceRelationshipData(source, getRemoteState) {
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
}
|
|
1492
|
-
if (data !== undefined) {
|
|
1493
|
-
payload.data = data;
|
|
1494
|
-
}
|
|
1495
|
-
if (source.meta) {
|
|
1496
|
-
payload.meta = source.meta;
|
|
1497
|
-
}
|
|
1498
|
-
return payload;
|
|
1499
|
-
}
|
|
929
|
+
source.accessed = true;
|
|
930
|
+
let data;
|
|
931
|
+
const payload = {};
|
|
932
|
+
if (getRemoteState && (source.remoteState || source.state.hasReceivedRemoteData)) data = source.remoteState ?? null;
|
|
933
|
+
else if (!getRemoteState && (source.localState || source.state.hasReceivedData)) data = source.localState ?? null;
|
|
934
|
+
if (source.links) payload.links = source.links;
|
|
935
|
+
if (data !== void 0) payload.data = data;
|
|
936
|
+
if (source.meta) payload.meta = source.meta;
|
|
937
|
+
return payload;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
//#endregion
|
|
941
|
+
//#region src/graph/-private/operations/add-to-related-records.ts
|
|
1500
942
|
function addToRelatedRecords(graph, op, isRemote) {
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
// if we are not dirty but have a null localState then we
|
|
1521
|
-
// are mutating a relationship that has never been fetched
|
|
1522
|
-
// so we initialize localState to an empty array
|
|
1523
|
-
if (!relationship.isDirty && !relationship.localState) {
|
|
1524
|
-
relationship.localState = [];
|
|
1525
|
-
}
|
|
1526
|
-
if (Array.isArray(value)) {
|
|
1527
|
-
for (let i = 0; i < value.length; i++) {
|
|
1528
|
-
addRelatedRecord(graph, relationship, record, value[i], index !== undefined ? index + i : null, isRemote);
|
|
1529
|
-
}
|
|
1530
|
-
} else {
|
|
1531
|
-
addRelatedRecord(graph, relationship, record, value, index ?? null, isRemote);
|
|
1532
|
-
}
|
|
1533
|
-
notifyChange(graph, relationship);
|
|
943
|
+
const { record, value, index } = op;
|
|
944
|
+
const relationship = graph.get(record, op.field);
|
|
945
|
+
const _isBelongsTo = isBelongsTo(relationship);
|
|
946
|
+
if (isRemote && _isBelongsTo) {
|
|
947
|
+
if (value !== relationship.remoteState) {
|
|
948
|
+
const newOp = {
|
|
949
|
+
op: "replaceRelatedRecord",
|
|
950
|
+
record,
|
|
951
|
+
field: op.field,
|
|
952
|
+
value
|
|
953
|
+
};
|
|
954
|
+
return replaceRelatedRecord(graph, newOp, isRemote);
|
|
955
|
+
}
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (!relationship.isDirty && !relationship.localState) relationship.localState = [];
|
|
959
|
+
if (Array.isArray(value)) for (let i = 0; i < value.length; i++) addRelatedRecord(graph, relationship, record, value[i], index !== void 0 ? index + i : null, isRemote);
|
|
960
|
+
else addRelatedRecord(graph, relationship, record, value, index ?? null, isRemote);
|
|
961
|
+
notifyChange(graph, relationship);
|
|
1534
962
|
}
|
|
1535
963
|
function addRelatedRecord(graph, relationship, record, value, index, isRemote) {
|
|
1536
|
-
|
|
1537
|
-
addToInverse(graph, value, relationship.definition.inverseKey, record, isRemote);
|
|
1538
|
-
}
|
|
964
|
+
if (_add(graph, record, relationship, value, index, isRemote)) addToInverse(graph, value, relationship.definition.inverseKey, record, isRemote);
|
|
1539
965
|
}
|
|
966
|
+
|
|
967
|
+
//#endregion
|
|
968
|
+
//#region src/graph/-private/operations/merge-identifier.ts
|
|
1540
969
|
function mergeIdentifier(graph, op, relationships) {
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
mergeIdentifierForRelationship(graph, op, rel);
|
|
1547
|
-
});
|
|
970
|
+
Object.keys(relationships).forEach((key) => {
|
|
971
|
+
const rel = relationships[key];
|
|
972
|
+
if (!rel) return;
|
|
973
|
+
mergeIdentifierForRelationship(graph, op, rel);
|
|
974
|
+
});
|
|
1548
975
|
}
|
|
1549
976
|
function mergeIdentifierForRelationship(graph, op, rel) {
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
});
|
|
977
|
+
rel.identifier = op.value;
|
|
978
|
+
forAllRelatedIdentifiers(rel, (identifier) => {
|
|
979
|
+
mergeInRelationship(graph, graph.get(identifier, rel.definition.inverseKey), op);
|
|
980
|
+
});
|
|
1555
981
|
}
|
|
1556
982
|
function mergeInRelationship(graph, rel, op) {
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
mergeHasMany(graph, rel, op);
|
|
1561
|
-
} else {
|
|
1562
|
-
mergeImplicit(graph, rel, op);
|
|
1563
|
-
}
|
|
983
|
+
if (isBelongsTo(rel)) mergeBelongsTo(graph, rel, op);
|
|
984
|
+
else if (isHasMany(rel)) mergeHasMany(graph, rel, op);
|
|
985
|
+
else mergeImplicit(graph, rel, op);
|
|
1564
986
|
}
|
|
1565
987
|
function mergeBelongsTo(graph, rel, op) {
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
notifyChange(graph, rel);
|
|
1572
|
-
}
|
|
988
|
+
if (rel.remoteState === op.record) rel.remoteState = op.value;
|
|
989
|
+
if (rel.localState === op.record) {
|
|
990
|
+
rel.localState = op.value;
|
|
991
|
+
notifyChange(graph, rel);
|
|
992
|
+
}
|
|
1573
993
|
}
|
|
1574
994
|
function mergeHasMany(graph, rel, op) {
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
notifyChange(graph, rel);
|
|
1594
|
-
}
|
|
995
|
+
if (rel.remoteMembers.has(op.record)) {
|
|
996
|
+
rel.remoteMembers.delete(op.record);
|
|
997
|
+
rel.remoteMembers.add(op.value);
|
|
998
|
+
const index = rel.remoteState.indexOf(op.record);
|
|
999
|
+
rel.remoteState.splice(index, 1, op.value);
|
|
1000
|
+
rel.isDirty = true;
|
|
1001
|
+
}
|
|
1002
|
+
if (rel.additions?.has(op.record)) {
|
|
1003
|
+
rel.additions.delete(op.record);
|
|
1004
|
+
rel.additions.add(op.value);
|
|
1005
|
+
rel.isDirty = true;
|
|
1006
|
+
}
|
|
1007
|
+
if (rel.removals?.has(op.record)) {
|
|
1008
|
+
rel.removals.delete(op.record);
|
|
1009
|
+
rel.removals.add(op.value);
|
|
1010
|
+
rel.isDirty = true;
|
|
1011
|
+
}
|
|
1012
|
+
if (rel.isDirty) notifyChange(graph, rel);
|
|
1595
1013
|
}
|
|
1596
1014
|
function mergeImplicit(graph, rel, op) {
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
}
|
|
1015
|
+
if (rel.remoteMembers.has(op.record)) {
|
|
1016
|
+
rel.remoteMembers.delete(op.record);
|
|
1017
|
+
rel.remoteMembers.add(op.value);
|
|
1018
|
+
}
|
|
1019
|
+
if (rel.localMembers.has(op.record)) {
|
|
1020
|
+
rel.localMembers.delete(op.record);
|
|
1021
|
+
rel.localMembers.add(op.value);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
//#endregion
|
|
1026
|
+
//#region src/graph/-private/operations/remove-from-related-records.ts
|
|
1606
1027
|
function removeFromRelatedRecords(graph, op, isRemote) {
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
for (let i = 0; i < value.length; i++) {
|
|
1626
|
-
removeRelatedRecord(graph, record, relationship, value[i], op.index ?? null, isRemote);
|
|
1627
|
-
}
|
|
1628
|
-
} else {
|
|
1629
|
-
removeRelatedRecord(graph, record, relationship, value, op.index ?? null, isRemote);
|
|
1630
|
-
}
|
|
1631
|
-
notifyChange(graph, relationship);
|
|
1028
|
+
const { record, value } = op;
|
|
1029
|
+
const relationship = graph.get(record, op.field);
|
|
1030
|
+
const _isBelongsTo = isBelongsTo(relationship);
|
|
1031
|
+
if (isRemote && _isBelongsTo) {
|
|
1032
|
+
if (value === relationship.remoteState) {
|
|
1033
|
+
const newOp = {
|
|
1034
|
+
op: "replaceRelatedRecord",
|
|
1035
|
+
record,
|
|
1036
|
+
field: op.field,
|
|
1037
|
+
value: null
|
|
1038
|
+
};
|
|
1039
|
+
return replaceRelatedRecord(graph, newOp, isRemote);
|
|
1040
|
+
}
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
if (Array.isArray(value)) for (let i = 0; i < value.length; i++) removeRelatedRecord(graph, record, relationship, value[i], op.index ?? null, isRemote);
|
|
1044
|
+
else removeRelatedRecord(graph, record, relationship, value, op.index ?? null, isRemote);
|
|
1045
|
+
notifyChange(graph, relationship);
|
|
1632
1046
|
}
|
|
1633
1047
|
function removeRelatedRecord(graph, record, relationship, value, index, isRemote) {
|
|
1634
|
-
|
|
1635
|
-
removeFromInverse(graph, value, relationship.definition.inverseKey, record, isRemote);
|
|
1636
|
-
}
|
|
1048
|
+
if (_remove(graph, record, relationship, value, index, isRemote)) removeFromInverse(graph, value, relationship.definition.inverseKey, record, isRemote);
|
|
1637
1049
|
}
|
|
1638
1050
|
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
already an object it's returned without any modifications.
|
|
1642
|
-
|
|
1643
|
-
See http://jsonapi.org/format/#document-links for more information.
|
|
1644
|
-
*/
|
|
1051
|
+
//#endregion
|
|
1052
|
+
//#region src/graph/-private/normalize-link.ts
|
|
1645
1053
|
function _normalizeLink(link) {
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
return {
|
|
1651
|
-
href: link
|
|
1652
|
-
};
|
|
1653
|
-
}
|
|
1054
|
+
switch (typeof link) {
|
|
1055
|
+
case "object": return link;
|
|
1056
|
+
case "string": return { href: link };
|
|
1057
|
+
}
|
|
1654
1058
|
}
|
|
1655
1059
|
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
state and blowing away any local changes (excepting new records).
|
|
1659
|
-
*/
|
|
1060
|
+
//#endregion
|
|
1061
|
+
//#region src/graph/-private/operations/update-relationship.ts
|
|
1660
1062
|
function updateRelationshipOperation(graph, op) {
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
op: 'replaceRelatedRecord',
|
|
1711
|
-
record: identifier,
|
|
1712
|
-
field: op.field,
|
|
1713
|
-
value: null
|
|
1714
|
-
}, true);
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
if (payload.links) {
|
|
1718
|
-
const originalLinks = relationship.links;
|
|
1719
|
-
relationship.links = payload.links;
|
|
1720
|
-
if (payload.links.related) {
|
|
1721
|
-
const relatedLink = _normalizeLink(payload.links.related);
|
|
1722
|
-
const currentLink = originalLinks && originalLinks.related ? _normalizeLink(originalLinks.related) : null;
|
|
1723
|
-
const currentLinkHref = currentLink ? currentLink.href : null;
|
|
1724
|
-
if (relatedLink && relatedLink.href && relatedLink.href !== currentLinkHref) {
|
|
1725
|
-
hasUpdatedLink = true;
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
/*
|
|
1731
|
-
Data being pushed into the relationship might contain only data or links,
|
|
1732
|
-
or a combination of both.
|
|
1733
|
-
IF contains only data
|
|
1734
|
-
IF contains both links and data
|
|
1735
|
-
state.isEmpty -> true if is empty array (has-many) or is null (belongs-to)
|
|
1736
|
-
state.hasReceivedData -> true
|
|
1737
|
-
hasDematerializedInverse -> false
|
|
1738
|
-
state.isStale -> false
|
|
1739
|
-
allInverseRecordsAreLoaded -> run-check-to-determine
|
|
1740
|
-
IF contains only links
|
|
1741
|
-
state.isStale -> true
|
|
1742
|
-
*/
|
|
1743
|
-
relationship.state.hasFailedLoadAttempt = false;
|
|
1744
|
-
if (hasRelationshipDataProperty) {
|
|
1745
|
-
const relationshipIsEmpty = payload.data === null || Array.isArray(payload.data) && payload.data.length === 0;
|
|
1746
|
-
|
|
1747
|
-
// we don't need to notify here as the update op we pushed in above will notify once
|
|
1748
|
-
// membership is in the correct state.
|
|
1749
|
-
relationship.state.hasReceivedData = true;
|
|
1750
|
-
relationship.state.isStale = false;
|
|
1751
|
-
relationship.state.hasDematerializedInverse = false;
|
|
1752
|
-
relationship.state.isEmpty = relationshipIsEmpty;
|
|
1753
|
-
} else if (hasUpdatedLink) {
|
|
1754
|
-
// only notify stale if we have not previously received membership data.
|
|
1755
|
-
// within this same transaction
|
|
1756
|
-
// this prevents refetching when only one side of the relationship in the
|
|
1757
|
-
// payload contains the info while the other side contains just a link
|
|
1758
|
-
// this only works when the side with just a link is a belongsTo, as we
|
|
1759
|
-
// don't know if a hasMany has full information or not.
|
|
1760
|
-
// see #7049 for context.
|
|
1761
|
-
if (isCollection || !relationship.state.hasReceivedData || isStaleTransaction(relationship.transactionRef, graph._transaction)) {
|
|
1762
|
-
relationship.state.isStale = true;
|
|
1763
|
-
notifyChange(graph, relationship);
|
|
1764
|
-
} else {
|
|
1765
|
-
relationship.state.isStale = false;
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1063
|
+
const relationship = graph.get(op.record, op.field);
|
|
1064
|
+
const { definition, identifier } = relationship;
|
|
1065
|
+
const { isCollection } = definition;
|
|
1066
|
+
const payload = op.value;
|
|
1067
|
+
let hasRelationshipDataProperty = false;
|
|
1068
|
+
let hasUpdatedLink = false;
|
|
1069
|
+
if (payload.meta) relationship.meta = payload.meta;
|
|
1070
|
+
if (payload.data !== void 0) {
|
|
1071
|
+
hasRelationshipDataProperty = true;
|
|
1072
|
+
if (isCollection) {
|
|
1073
|
+
if (payload.data === null) payload.data = [];
|
|
1074
|
+
const cache = graph.store.cacheKeyManager;
|
|
1075
|
+
graph.update({
|
|
1076
|
+
op: "replaceRelatedRecords",
|
|
1077
|
+
record: identifier,
|
|
1078
|
+
field: op.field,
|
|
1079
|
+
value: upgradeIdentifiers(payload.data, cache)
|
|
1080
|
+
}, true);
|
|
1081
|
+
} else graph.update({
|
|
1082
|
+
op: "replaceRelatedRecord",
|
|
1083
|
+
record: identifier,
|
|
1084
|
+
field: op.field,
|
|
1085
|
+
value: payload.data ? graph.store.cacheKeyManager.upgradeIdentifier(payload.data) : null
|
|
1086
|
+
}, true);
|
|
1087
|
+
}
|
|
1088
|
+
if (payload.links) {
|
|
1089
|
+
const originalLinks = relationship.links;
|
|
1090
|
+
relationship.links = payload.links;
|
|
1091
|
+
if (payload.links.related) {
|
|
1092
|
+
const relatedLink = _normalizeLink(payload.links.related);
|
|
1093
|
+
const currentLink = originalLinks && originalLinks.related ? _normalizeLink(originalLinks.related) : null;
|
|
1094
|
+
const currentLinkHref = currentLink ? currentLink.href : null;
|
|
1095
|
+
if (relatedLink && relatedLink.href && relatedLink.href !== currentLinkHref) hasUpdatedLink = true;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
relationship.state.hasFailedLoadAttempt = false;
|
|
1099
|
+
if (hasRelationshipDataProperty) {
|
|
1100
|
+
const relationshipIsEmpty = payload.data === null || Array.isArray(payload.data) && payload.data.length === 0;
|
|
1101
|
+
relationship.state.hasReceivedData = true;
|
|
1102
|
+
relationship.state.hasReceivedRemoteData = true;
|
|
1103
|
+
relationship.state.isStale = false;
|
|
1104
|
+
relationship.state.hasDematerializedInverse = false;
|
|
1105
|
+
relationship.state.isEmpty = relationshipIsEmpty;
|
|
1106
|
+
} else if (hasUpdatedLink) {
|
|
1107
|
+
if (isCollection || !relationship.state.hasReceivedData || isStaleTransaction(relationship.transactionRef, graph._transaction)) {
|
|
1108
|
+
relationship.state.isStale = true;
|
|
1109
|
+
notifyChange(graph, relationship);
|
|
1110
|
+
} else relationship.state.isStale = false;
|
|
1111
|
+
}
|
|
1768
1112
|
}
|
|
1769
1113
|
function isStaleTransaction(relationshipTransactionId, graphTransactionId) {
|
|
1770
|
-
|
|
1771
|
-
// relationship has never notified
|
|
1772
|
-
graphTransactionId === null ||
|
|
1773
|
-
// we are not in a transaction
|
|
1774
|
-
relationshipTransactionId < graphTransactionId // we are not part of the current transaction
|
|
1775
|
-
;
|
|
1114
|
+
return relationshipTransactionId === 0 || graphTransactionId === null || relationshipTransactionId < graphTransactionId;
|
|
1776
1115
|
}
|
|
1777
1116
|
function upgradeIdentifiers(arr, cache) {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
}
|
|
1781
|
-
return arr;
|
|
1782
|
-
}
|
|
1783
|
-
const Graphs = getOrSetGlobal('Graphs', new Map());
|
|
1784
|
-
/*
|
|
1785
|
-
* Graph acts as the cache for relationship data. It allows for
|
|
1786
|
-
* us to ask about and update relationships for a given Identifier
|
|
1787
|
-
* without requiring other objects for that Identifier to be
|
|
1788
|
-
* instantiated (such as `RecordData` or a `Record`)
|
|
1789
|
-
*
|
|
1790
|
-
* This also allows for us to make more substantive changes to relationships
|
|
1791
|
-
* with increasingly minor alterations to other portions of the internals
|
|
1792
|
-
* over time.
|
|
1793
|
-
*
|
|
1794
|
-
* The graph is made up of nodes and edges. Each unique identifier gets
|
|
1795
|
-
* its own node, which is a dictionary with a list of that node's edges
|
|
1796
|
-
* (or connections) to other nodes. In `Model` terms, a node represents a
|
|
1797
|
-
* record instance, with each key (an edge) in the dictionary correlating
|
|
1798
|
-
* to either a `hasMany` or `belongsTo` field on that record instance.
|
|
1799
|
-
*
|
|
1800
|
-
* The value for each key, or `edge` is the identifier(s) the node relates
|
|
1801
|
-
* to in the graph from that key.
|
|
1802
|
-
*/
|
|
1803
|
-
class Graph {
|
|
1804
|
-
constructor(store) {
|
|
1805
|
-
this._definitionCache = Object.create(null);
|
|
1806
|
-
this._metaCache = Object.create(null);
|
|
1807
|
-
this._potentialPolymorphicTypes = Object.create(null);
|
|
1808
|
-
this.identifiers = new Map();
|
|
1809
|
-
this.store = store;
|
|
1810
|
-
this._realStore = store._store;
|
|
1811
|
-
this.isDestroyed = false;
|
|
1812
|
-
this._willSyncRemote = false;
|
|
1813
|
-
this._willSyncLocal = false;
|
|
1814
|
-
this._pushedUpdates = {
|
|
1815
|
-
belongsTo: undefined,
|
|
1816
|
-
hasMany: undefined,
|
|
1817
|
-
deletions: []
|
|
1818
|
-
};
|
|
1819
|
-
this._updatedRelationships = new Set();
|
|
1820
|
-
this._transaction = null;
|
|
1821
|
-
this._removing = null;
|
|
1822
|
-
this.silenceNotifications = false;
|
|
1823
|
-
}
|
|
1824
|
-
has(resourceKey, propertyName) {
|
|
1825
|
-
const relationships = this.identifiers.get(resourceKey);
|
|
1826
|
-
if (!relationships) {
|
|
1827
|
-
return false;
|
|
1828
|
-
}
|
|
1829
|
-
return relationships[propertyName] !== undefined;
|
|
1830
|
-
}
|
|
1831
|
-
getDefinition(resourceKey, propertyName) {
|
|
1832
|
-
let defs = this._metaCache[resourceKey.type];
|
|
1833
|
-
let meta = defs?.[propertyName];
|
|
1834
|
-
if (!meta) {
|
|
1835
|
-
const info = /*#__NOINLINE__*/upgradeDefinition(this, resourceKey, propertyName);
|
|
1836
|
-
|
|
1837
|
-
// if (info.rhs_definition?.kind === 'implicit') {
|
|
1838
|
-
// we should possibly also do this
|
|
1839
|
-
// but it would result in being extremely permissive for other relationships by accident
|
|
1840
|
-
// this.registerPolymorphicType(info.rhs_baseModelName, identifier.type);
|
|
1841
|
-
// }
|
|
1842
|
-
|
|
1843
|
-
meta = /*#__NOINLINE__*/isLHS(info, resourceKey.type, propertyName) ? info.lhs_definition : info.rhs_definition;
|
|
1844
|
-
defs = this._metaCache[resourceKey.type] = defs || {};
|
|
1845
|
-
defs[propertyName] = meta;
|
|
1846
|
-
}
|
|
1847
|
-
return meta;
|
|
1848
|
-
}
|
|
1849
|
-
get(resourceKey, propertyName) {
|
|
1850
|
-
let relationships = this.identifiers.get(resourceKey);
|
|
1851
|
-
if (!relationships) {
|
|
1852
|
-
relationships = Object.create(null);
|
|
1853
|
-
this.identifiers.set(resourceKey, relationships);
|
|
1854
|
-
}
|
|
1855
|
-
let relationship = relationships[propertyName];
|
|
1856
|
-
if (!relationship) {
|
|
1857
|
-
const meta = this.getDefinition(resourceKey, propertyName);
|
|
1858
|
-
if (meta.kind === 'belongsTo') {
|
|
1859
|
-
relationship = relationships[propertyName] = createResourceEdge(meta, resourceKey);
|
|
1860
|
-
} else if (meta.kind === 'hasMany') {
|
|
1861
|
-
relationship = relationships[propertyName] = createCollectionEdge(meta, resourceKey);
|
|
1862
|
-
} else {
|
|
1863
|
-
relationship = relationships[propertyName] = createImplicitEdge(meta, resourceKey);
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1866
|
-
return relationship;
|
|
1867
|
-
}
|
|
1868
|
-
getData(resourceKey, propertyName) {
|
|
1869
|
-
const relationship = this.get(resourceKey, propertyName);
|
|
1870
|
-
if (isBelongsTo(relationship)) {
|
|
1871
|
-
return legacyGetResourceRelationshipData(relationship, false);
|
|
1872
|
-
}
|
|
1873
|
-
return legacyGetCollectionRelationshipData(relationship, false);
|
|
1874
|
-
}
|
|
1875
|
-
getRemoteData(resourceKey, propertyName) {
|
|
1876
|
-
const relationship = this.get(resourceKey, propertyName);
|
|
1877
|
-
if (isBelongsTo(relationship)) {
|
|
1878
|
-
return legacyGetResourceRelationshipData(relationship, true);
|
|
1879
|
-
}
|
|
1880
|
-
return legacyGetCollectionRelationshipData(relationship, true);
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
/*
|
|
1884
|
-
* Allows for the graph to dynamically discover polymorphic connections
|
|
1885
|
-
* without needing to walk prototype chains.
|
|
1886
|
-
*
|
|
1887
|
-
* Used by edges when an added `type` does not match the expected `type`
|
|
1888
|
-
* for that edge.
|
|
1889
|
-
*
|
|
1890
|
-
* Currently we assert before calling this. For a public API we will want
|
|
1891
|
-
* to call out to the schema manager to ask if we should consider these
|
|
1892
|
-
* types as equivalent for a given relationship.
|
|
1893
|
-
*/
|
|
1894
|
-
registerPolymorphicType(type1, type2) {
|
|
1895
|
-
const typeCache = this._potentialPolymorphicTypes;
|
|
1896
|
-
let t1 = typeCache[type1];
|
|
1897
|
-
if (!t1) {
|
|
1898
|
-
t1 = typeCache[type1] = Object.create(null);
|
|
1899
|
-
}
|
|
1900
|
-
t1[type2] = true;
|
|
1901
|
-
let t2 = typeCache[type2];
|
|
1902
|
-
if (!t2) {
|
|
1903
|
-
t2 = typeCache[type2] = Object.create(null);
|
|
1904
|
-
}
|
|
1905
|
-
t2[type1] = true;
|
|
1906
|
-
}
|
|
1907
|
-
isReleasable(resourceKey) {
|
|
1908
|
-
const relationships = this.identifiers.get(resourceKey);
|
|
1909
|
-
if (!relationships) {
|
|
1910
|
-
return true;
|
|
1911
|
-
}
|
|
1912
|
-
const keys = Object.keys(relationships);
|
|
1913
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1914
|
-
const relationship = relationships[keys[i]];
|
|
1915
|
-
// account for previously unloaded relationships
|
|
1916
|
-
// typically from a prior deletion of a record that pointed to this one implicitly
|
|
1917
|
-
if (relationship === undefined) {
|
|
1918
|
-
continue;
|
|
1919
|
-
}
|
|
1920
|
-
if (relationship.definition.inverseIsAsync && !checkIfNew(this._realStore, resourceKey)) {
|
|
1921
|
-
return false;
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
return true;
|
|
1925
|
-
}
|
|
1926
|
-
unload(resourceKey, silenceNotifications) {
|
|
1927
|
-
const relationships = this.identifiers.get(resourceKey);
|
|
1928
|
-
if (relationships) {
|
|
1929
|
-
// cleans up the graph but retains some nodes
|
|
1930
|
-
// to allow for rematerialization
|
|
1931
|
-
Object.keys(relationships).forEach(key => {
|
|
1932
|
-
const rel = relationships[key];
|
|
1933
|
-
if (!rel) {
|
|
1934
|
-
return;
|
|
1935
|
-
}
|
|
1936
|
-
/*#__NOINLINE__*/
|
|
1937
|
-
destroyRelationship(this, rel, silenceNotifications);
|
|
1938
|
-
if (/*#__NOINLINE__*/isImplicit(rel)) {
|
|
1939
|
-
// @ts-expect-error
|
|
1940
|
-
relationships[key] = undefined;
|
|
1941
|
-
}
|
|
1942
|
-
});
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
_isDirty(resourceKey, field) {
|
|
1946
|
-
const relationships = this.identifiers.get(resourceKey);
|
|
1947
|
-
if (!relationships) {
|
|
1948
|
-
return false;
|
|
1949
|
-
}
|
|
1950
|
-
const relationship = relationships[field];
|
|
1951
|
-
if (!relationship) {
|
|
1952
|
-
return false;
|
|
1953
|
-
}
|
|
1954
|
-
if (isBelongsTo(relationship)) {
|
|
1955
|
-
return relationship.localState !== relationship.remoteState;
|
|
1956
|
-
} else if (isHasMany(relationship)) {
|
|
1957
|
-
const hasAdditions = relationship.additions !== null && relationship.additions.size > 0;
|
|
1958
|
-
const hasRemovals = relationship.removals !== null && relationship.removals.size > 0;
|
|
1959
|
-
return hasAdditions || hasRemovals || isReordered(relationship);
|
|
1960
|
-
}
|
|
1961
|
-
return false;
|
|
1962
|
-
}
|
|
1963
|
-
getChanged(resourceKey) {
|
|
1964
|
-
const relationships = this.identifiers.get(resourceKey);
|
|
1965
|
-
const changed = new Map();
|
|
1966
|
-
if (!relationships) {
|
|
1967
|
-
return changed;
|
|
1968
|
-
}
|
|
1969
|
-
const keys = Object.keys(relationships);
|
|
1970
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1971
|
-
const field = keys[i];
|
|
1972
|
-
const relationship = relationships[field];
|
|
1973
|
-
if (!relationship) {
|
|
1974
|
-
continue;
|
|
1975
|
-
}
|
|
1976
|
-
if (isBelongsTo(relationship)) {
|
|
1977
|
-
if (relationship.localState !== relationship.remoteState) {
|
|
1978
|
-
changed.set(field, {
|
|
1979
|
-
kind: 'resource',
|
|
1980
|
-
remoteState: relationship.remoteState,
|
|
1981
|
-
localState: relationship.localState
|
|
1982
|
-
});
|
|
1983
|
-
}
|
|
1984
|
-
} else if (isHasMany(relationship)) {
|
|
1985
|
-
const hasAdditions = relationship.additions !== null && relationship.additions.size > 0;
|
|
1986
|
-
const hasRemovals = relationship.removals !== null && relationship.removals.size > 0;
|
|
1987
|
-
const reordered = isReordered(relationship);
|
|
1988
|
-
if (hasAdditions || hasRemovals || reordered) {
|
|
1989
|
-
changed.set(field, {
|
|
1990
|
-
kind: 'collection',
|
|
1991
|
-
additions: new Set(relationship.additions),
|
|
1992
|
-
removals: new Set(relationship.removals),
|
|
1993
|
-
remoteState: relationship.remoteState,
|
|
1994
|
-
localState: legacyGetCollectionRelationshipData(relationship, false).data || [],
|
|
1995
|
-
reordered
|
|
1996
|
-
});
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
return changed;
|
|
2001
|
-
}
|
|
2002
|
-
hasChanged(resourceKey) {
|
|
2003
|
-
const relationships = this.identifiers.get(resourceKey);
|
|
2004
|
-
if (!relationships) {
|
|
2005
|
-
return false;
|
|
2006
|
-
}
|
|
2007
|
-
const keys = Object.keys(relationships);
|
|
2008
|
-
for (let i = 0; i < keys.length; i++) {
|
|
2009
|
-
if (this._isDirty(resourceKey, keys[i])) {
|
|
2010
|
-
return true;
|
|
2011
|
-
}
|
|
2012
|
-
}
|
|
2013
|
-
return false;
|
|
2014
|
-
}
|
|
2015
|
-
rollback(resourceKey) {
|
|
2016
|
-
const relationships = this.identifiers.get(resourceKey);
|
|
2017
|
-
const changed = [];
|
|
2018
|
-
if (!relationships) {
|
|
2019
|
-
return changed;
|
|
2020
|
-
}
|
|
2021
|
-
const keys = Object.keys(relationships);
|
|
2022
|
-
for (let i = 0; i < keys.length; i++) {
|
|
2023
|
-
const field = keys[i];
|
|
2024
|
-
const relationship = relationships[field];
|
|
2025
|
-
if (!relationship) {
|
|
2026
|
-
continue;
|
|
2027
|
-
}
|
|
2028
|
-
if (this._isDirty(resourceKey, field)) {
|
|
2029
|
-
rollbackRelationship(this, resourceKey, field, relationship);
|
|
2030
|
-
changed.push(field);
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
return changed;
|
|
2034
|
-
}
|
|
2035
|
-
remove(resourceKey) {
|
|
2036
|
-
this._removing = resourceKey;
|
|
2037
|
-
this.unload(resourceKey);
|
|
2038
|
-
this.identifiers.delete(resourceKey);
|
|
2039
|
-
this._removing = null;
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
/*
|
|
2043
|
-
* Remote state changes
|
|
2044
|
-
*/
|
|
2045
|
-
push(op) {
|
|
2046
|
-
if (op.op === 'deleteRecord') {
|
|
2047
|
-
this._pushedUpdates.deletions.push(op);
|
|
2048
|
-
} else {
|
|
2049
|
-
const definition = this.getDefinition(op.record, op.field);
|
|
2050
|
-
addPending(this._pushedUpdates, definition, op);
|
|
2051
|
-
}
|
|
2052
|
-
if (!this._willSyncRemote) {
|
|
2053
|
-
this._willSyncRemote = true;
|
|
2054
|
-
const store = getStore(this.store);
|
|
2055
|
-
if (!store._cbs) {
|
|
2056
|
-
store._run(() => this._flushRemoteQueue());
|
|
2057
|
-
} else {
|
|
2058
|
-
store._schedule('coalesce', () => this._flushRemoteQueue());
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
|
|
2063
|
-
/*
|
|
2064
|
-
* Local state changes
|
|
2065
|
-
*/
|
|
2066
|
-
|
|
2067
|
-
update(op, isRemote = false) {
|
|
2068
|
-
switch (op.op) {
|
|
2069
|
-
case 'mergeIdentifiers':
|
|
2070
|
-
{
|
|
2071
|
-
const relationships = this.identifiers.get(op.record);
|
|
2072
|
-
if (relationships) {
|
|
2073
|
-
/*#__NOINLINE__*/mergeIdentifier(this, op, relationships);
|
|
2074
|
-
}
|
|
2075
|
-
break;
|
|
2076
|
-
}
|
|
2077
|
-
case 'update':
|
|
2078
|
-
case 'updateRelationship':
|
|
2079
|
-
/*#__NOINLINE__*/updateRelationshipOperation(this, op);
|
|
2080
|
-
break;
|
|
2081
|
-
case 'deleteRecord':
|
|
2082
|
-
{
|
|
2083
|
-
const identifier = op.record;
|
|
2084
|
-
const relationships = this.identifiers.get(identifier);
|
|
2085
|
-
if (relationships) {
|
|
2086
|
-
Object.keys(relationships).forEach(key => {
|
|
2087
|
-
const rel = relationships[key];
|
|
2088
|
-
if (!rel) {
|
|
2089
|
-
return;
|
|
2090
|
-
}
|
|
2091
|
-
// works together with the has check
|
|
2092
|
-
// @ts-expect-error
|
|
2093
|
-
relationships[key] = undefined;
|
|
2094
|
-
/*#__NOINLINE__*/
|
|
2095
|
-
removeCompletelyFromInverse(this, rel);
|
|
2096
|
-
});
|
|
2097
|
-
this.identifiers.delete(identifier);
|
|
2098
|
-
}
|
|
2099
|
-
break;
|
|
2100
|
-
}
|
|
2101
|
-
case 'replaceRelatedRecord':
|
|
2102
|
-
/*#__NOINLINE__*/replaceRelatedRecord(this, op, isRemote);
|
|
2103
|
-
break;
|
|
2104
|
-
case 'add':
|
|
2105
|
-
/*#__NOINLINE__*/addToRelatedRecords(this, op, isRemote);
|
|
2106
|
-
break;
|
|
2107
|
-
case 'remove':
|
|
2108
|
-
/*#__NOINLINE__*/removeFromRelatedRecords(this, op, isRemote);
|
|
2109
|
-
break;
|
|
2110
|
-
case 'replaceRelatedRecords':
|
|
2111
|
-
/*#__NOINLINE__*/replaceRelatedRecords(this, op, isRemote);
|
|
2112
|
-
break;
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
_scheduleLocalSync(relationship) {
|
|
2116
|
-
this._updatedRelationships.add(relationship);
|
|
2117
|
-
if (!this._willSyncLocal) {
|
|
2118
|
-
this._willSyncLocal = true;
|
|
2119
|
-
getStore(this.store)._schedule('sync', () => this._flushLocalQueue());
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
_flushRemoteQueue() {
|
|
2123
|
-
if (!this._willSyncRemote) {
|
|
2124
|
-
return;
|
|
2125
|
-
}
|
|
2126
|
-
let transactionRef = peekTransient('transactionRef') ?? 0;
|
|
2127
|
-
this._transaction = ++transactionRef;
|
|
2128
|
-
setTransient('transactionRef', transactionRef);
|
|
2129
|
-
this._willSyncRemote = false;
|
|
2130
|
-
const updates = this._pushedUpdates;
|
|
2131
|
-
const {
|
|
2132
|
-
deletions,
|
|
2133
|
-
hasMany,
|
|
2134
|
-
belongsTo
|
|
2135
|
-
} = updates;
|
|
2136
|
-
updates.deletions = [];
|
|
2137
|
-
updates.hasMany = undefined;
|
|
2138
|
-
updates.belongsTo = undefined;
|
|
2139
|
-
for (let i = 0; i < deletions.length; i++) {
|
|
2140
|
-
this.update(deletions[i], true);
|
|
2141
|
-
}
|
|
2142
|
-
if (hasMany) {
|
|
2143
|
-
flushPending(this, hasMany);
|
|
2144
|
-
}
|
|
2145
|
-
if (belongsTo) {
|
|
2146
|
-
flushPending(this, belongsTo);
|
|
2147
|
-
}
|
|
2148
|
-
this._transaction = null;
|
|
2149
|
-
}
|
|
2150
|
-
_addToTransaction(relationship) {
|
|
2151
|
-
relationship.transactionRef = this._transaction;
|
|
2152
|
-
}
|
|
2153
|
-
_flushLocalQueue() {
|
|
2154
|
-
if (!this._willSyncLocal) {
|
|
2155
|
-
return;
|
|
2156
|
-
}
|
|
2157
|
-
if (this.silenceNotifications) {
|
|
2158
|
-
this.silenceNotifications = false;
|
|
2159
|
-
this._updatedRelationships = new Set();
|
|
2160
|
-
return;
|
|
2161
|
-
}
|
|
2162
|
-
this._willSyncLocal = false;
|
|
2163
|
-
const updated = this._updatedRelationships;
|
|
2164
|
-
this._updatedRelationships = new Set();
|
|
2165
|
-
updated.forEach(rel => notifyChange(this, rel));
|
|
2166
|
-
}
|
|
2167
|
-
destroy() {
|
|
2168
|
-
Graphs.delete(this.store);
|
|
2169
|
-
this.identifiers.clear();
|
|
2170
|
-
this.store = null;
|
|
2171
|
-
this.isDestroyed = true;
|
|
2172
|
-
}
|
|
1117
|
+
for (let i = 0; i < arr.length; i++) arr[i] = cache.upgradeIdentifier(arr[i]);
|
|
1118
|
+
return arr;
|
|
2173
1119
|
}
|
|
1120
|
+
|
|
1121
|
+
//#endregion
|
|
1122
|
+
//#region src/graph/-private/graph.ts
|
|
1123
|
+
/**
|
|
1124
|
+
* A single edge (relationship field) tracked by the {@link Graph} for a resource. Which kind of edge
|
|
1125
|
+
* a field is depends on its relationship definition:
|
|
1126
|
+
*
|
|
1127
|
+
* - {@link ResourceEdge}: the "one" side of a `belongsTo`/resource relationship
|
|
1128
|
+
* - {@link CollectionEdge}: the "many" side of a `hasMany`/collection relationship
|
|
1129
|
+
* - {@link ImplicitEdge}: a synthesized inverse used when a relationship has no field defined for its inverse
|
|
1130
|
+
*/
|
|
1131
|
+
const Graphs = getOrSetGlobal("Graphs", /* @__PURE__ */ new Map());
|
|
1132
|
+
/**
|
|
1133
|
+
* Graph is WarpDrive's internal, private cache of all resource relationships.
|
|
1134
|
+
*
|
|
1135
|
+
* It acts as the cache for relationship data, allowing us to ask about and update
|
|
1136
|
+
* relationships for a given Identifier without requiring other objects for that
|
|
1137
|
+
* Identifier to be instantiated (such as `RecordData` or a `Record`).
|
|
1138
|
+
*
|
|
1139
|
+
* This also allows for us to make more substantive changes to relationships
|
|
1140
|
+
* with increasingly minor alterations to other portions of the internals
|
|
1141
|
+
* over time.
|
|
1142
|
+
*
|
|
1143
|
+
* The graph is made up of nodes and edges. Each unique identifier gets
|
|
1144
|
+
* its own node, which is a dictionary with a list of that node's edges
|
|
1145
|
+
* (or connections) to other nodes. In `Model` terms, a node represents a
|
|
1146
|
+
* record instance, with each key (an edge) in the dictionary correlating
|
|
1147
|
+
* to either a `hasMany` or `belongsTo` field on that record instance.
|
|
1148
|
+
*
|
|
1149
|
+
* The value for each key, or `edge` is the identifier(s) the node relates
|
|
1150
|
+
* to in the graph from that key.
|
|
1151
|
+
*/
|
|
1152
|
+
var Graph = class {
|
|
1153
|
+
/**
|
|
1154
|
+
* Cache of resolved {@link EdgeDefinition}s (the paired LHS/RHS relationship definitions
|
|
1155
|
+
* for a `type` + `key`), keyed by resource `type` and then field name. Populated lazily by
|
|
1156
|
+
* `upgradeDefinition` and consulted by {@link Graph.getDefinition}.
|
|
1157
|
+
*/
|
|
1158
|
+
/**
|
|
1159
|
+
* Cache of the resolved {@link UpgradedMeta} for a given resource `type` and field name, as
|
|
1160
|
+
* returned by {@link Graph.getDefinition}. Derived from `_definitionCache` the first time a
|
|
1161
|
+
* field's definition is looked up.
|
|
1162
|
+
*/
|
|
1163
|
+
/**
|
|
1164
|
+
* Tracks pairs of resource types that have been discovered to be polymorphically related, so
|
|
1165
|
+
* that the graph can recognize this connection without needing to walk prototype/schema
|
|
1166
|
+
* inheritance chains. Populated by {@link Graph.registerPolymorphicType}.
|
|
1167
|
+
*/
|
|
1168
|
+
/**
|
|
1169
|
+
* The nodes of the graph. Each {@link ResourceKey} maps to a dictionary of its relationship
|
|
1170
|
+
* fields (edges), keyed by field name.
|
|
1171
|
+
*/
|
|
1172
|
+
/**
|
|
1173
|
+
* The `CacheCapabilitiesManager` (store wrapper) this graph was created for. Used to schedule
|
|
1174
|
+
* work and to notify of relationship changes.
|
|
1175
|
+
*/
|
|
1176
|
+
/**
|
|
1177
|
+
* The real underlying `Store` instance backing {@link Graph.store}, used for capabilities
|
|
1178
|
+
* (such as checking whether a resource is a new/unsaved client-side record) that are not
|
|
1179
|
+
* exposed on `CacheCapabilitiesManager`.
|
|
1180
|
+
*/
|
|
1181
|
+
/**
|
|
1182
|
+
* `true` once {@link Graph.destroy} has been called. After this point the graph should no
|
|
1183
|
+
* longer be used.
|
|
1184
|
+
*/
|
|
1185
|
+
/**
|
|
1186
|
+
* `true` when a flush of the remote-update queue has been scheduled but has not yet run.
|
|
1187
|
+
* Set by {@link Graph.push} and cleared by {@link Graph._flushRemoteQueue}.
|
|
1188
|
+
*/
|
|
1189
|
+
/**
|
|
1190
|
+
* `true` when a flush of the local-update notification queue has been scheduled but has not
|
|
1191
|
+
* yet run. Set by {@link Graph._scheduleLocalSync} and cleared by
|
|
1192
|
+
* {@link Graph._flushLocalQueue}.
|
|
1193
|
+
*/
|
|
1194
|
+
/**
|
|
1195
|
+
* When `true`, the next scheduled local sync will be dropped without notifying subscribers of
|
|
1196
|
+
* the relationships that changed. Consumed and reset by {@link Graph._flushLocalQueue}.
|
|
1197
|
+
*/
|
|
1198
|
+
/**
|
|
1199
|
+
* Remote relationship operations and record deletions that have been pushed via
|
|
1200
|
+
* {@link Graph.push} but not yet flushed into the graph by {@link Graph._flushRemoteQueue}.
|
|
1201
|
+
*/
|
|
1202
|
+
/**
|
|
1203
|
+
* The set of {@link CollectionEdge}s that have local changes pending notification, scheduled
|
|
1204
|
+
* via {@link Graph._scheduleLocalSync} and drained by {@link Graph._flushLocalQueue}.
|
|
1205
|
+
*/
|
|
1206
|
+
/**
|
|
1207
|
+
* The identifier of the current remote-update transaction, or `null` when no transaction is
|
|
1208
|
+
* in progress. Set by {@link Graph._flushRemoteQueue} and stamped onto edges via
|
|
1209
|
+
* {@link Graph._addToTransaction}.
|
|
1210
|
+
*/
|
|
1211
|
+
/**
|
|
1212
|
+
* The {@link ResourceKey} currently being removed via {@link Graph.remove}, or `null`. Used to
|
|
1213
|
+
* guard against re-entrant removal of the same resource.
|
|
1214
|
+
*/
|
|
1215
|
+
constructor(store) {
|
|
1216
|
+
this._definitionCache = Object.create(null);
|
|
1217
|
+
this._metaCache = Object.create(null);
|
|
1218
|
+
this._potentialPolymorphicTypes = Object.create(null);
|
|
1219
|
+
this.identifiers = /* @__PURE__ */ new Map();
|
|
1220
|
+
this.store = store;
|
|
1221
|
+
this._realStore = store._store;
|
|
1222
|
+
this.isDestroyed = false;
|
|
1223
|
+
this._willSyncRemote = false;
|
|
1224
|
+
this._willSyncLocal = false;
|
|
1225
|
+
this._pushedUpdates = {
|
|
1226
|
+
belongsTo: void 0,
|
|
1227
|
+
hasMany: void 0,
|
|
1228
|
+
deletions: []
|
|
1229
|
+
};
|
|
1230
|
+
this._updatedRelationships = /* @__PURE__ */ new Set();
|
|
1231
|
+
this._transaction = null;
|
|
1232
|
+
this._removing = null;
|
|
1233
|
+
this.silenceNotifications = false;
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Returns `true` if an edge has already been created in the graph for the given resource and
|
|
1237
|
+
* field name, without creating one if it does not yet exist.
|
|
1238
|
+
*/
|
|
1239
|
+
has(resourceKey, propertyName) {
|
|
1240
|
+
const relationships = this.identifiers.get(resourceKey);
|
|
1241
|
+
if (!relationships) return false;
|
|
1242
|
+
return relationships[propertyName] !== void 0;
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Returns the resolved {@link UpgradedMeta} (relationship definition) for the given resource
|
|
1246
|
+
* `type` and field name, computing and caching it via `upgradeDefinition` on first access.
|
|
1247
|
+
*/
|
|
1248
|
+
getDefinition(resourceKey, propertyName) {
|
|
1249
|
+
let defs = this._metaCache[resourceKey.type];
|
|
1250
|
+
let meta = defs?.[propertyName];
|
|
1251
|
+
if (!meta) {
|
|
1252
|
+
const info = upgradeDefinition(this, resourceKey, propertyName);
|
|
1253
|
+
meta = isLHS(info, resourceKey.type, propertyName) ? info.lhs_definition : info.rhs_definition;
|
|
1254
|
+
defs = this._metaCache[resourceKey.type] = defs || {};
|
|
1255
|
+
defs[propertyName] = meta;
|
|
1256
|
+
}
|
|
1257
|
+
return meta;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Returns the {@link GraphEdge} for the given resource and field name, creating it (as a
|
|
1261
|
+
* {@link ResourceEdge}, {@link CollectionEdge}, or {@link ImplicitEdge} depending on the
|
|
1262
|
+
* field's kind) if it does not already exist.
|
|
1263
|
+
*/
|
|
1264
|
+
get(resourceKey, propertyName) {
|
|
1265
|
+
let relationships = this.identifiers.get(resourceKey);
|
|
1266
|
+
if (!relationships) {
|
|
1267
|
+
relationships = Object.create(null);
|
|
1268
|
+
this.identifiers.set(resourceKey, relationships);
|
|
1269
|
+
}
|
|
1270
|
+
let relationship = relationships[propertyName];
|
|
1271
|
+
if (!relationship) {
|
|
1272
|
+
const meta = this.getDefinition(resourceKey, propertyName);
|
|
1273
|
+
if (meta.kind === "belongsTo") relationship = relationships[propertyName] = createResourceEdge(meta, resourceKey);
|
|
1274
|
+
else if (meta.kind === "hasMany") relationship = relationships[propertyName] = createCollectionEdge(meta, resourceKey);
|
|
1275
|
+
else relationship = relationships[propertyName] = createImplicitEdge(meta, resourceKey);
|
|
1276
|
+
}
|
|
1277
|
+
return relationship;
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Returns the local (current, potentially dirty) JSON:API relationship data for a `belongsTo`
|
|
1281
|
+
* or `hasMany` field. Asserts if the field is an implicit relationship.
|
|
1282
|
+
*/
|
|
1283
|
+
getData(resourceKey, propertyName) {
|
|
1284
|
+
const relationship = this.get(resourceKey, propertyName);
|
|
1285
|
+
if (isBelongsTo(relationship)) return legacyGetResourceRelationshipData(relationship, false);
|
|
1286
|
+
return legacyGetCollectionRelationshipData(relationship, false);
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Returns the remote (last known server-confirmed) JSON:API relationship data for a
|
|
1290
|
+
* `belongsTo` or `hasMany` field. Asserts if the field is an implicit relationship.
|
|
1291
|
+
*/
|
|
1292
|
+
getRemoteData(resourceKey, propertyName) {
|
|
1293
|
+
const relationship = this.get(resourceKey, propertyName);
|
|
1294
|
+
if (isBelongsTo(relationship)) return legacyGetResourceRelationshipData(relationship, true);
|
|
1295
|
+
return legacyGetCollectionRelationshipData(relationship, true);
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Allows for the graph to dynamically discover polymorphic connections
|
|
1299
|
+
* without needing to walk prototype chains.
|
|
1300
|
+
*
|
|
1301
|
+
* Used by edges when an added `type` does not match the expected `type`
|
|
1302
|
+
* for that edge.
|
|
1303
|
+
*
|
|
1304
|
+
* Currently we assert before calling this. For a public API we will want
|
|
1305
|
+
* to call out to the schema manager to ask if we should consider these
|
|
1306
|
+
* types as equivalent for a given relationship.
|
|
1307
|
+
*/
|
|
1308
|
+
registerPolymorphicType(type1, type2) {
|
|
1309
|
+
const typeCache = this._potentialPolymorphicTypes;
|
|
1310
|
+
let t1 = typeCache[type1];
|
|
1311
|
+
if (!t1) t1 = typeCache[type1] = Object.create(null);
|
|
1312
|
+
t1[type2] = true;
|
|
1313
|
+
let t2 = typeCache[type2];
|
|
1314
|
+
if (!t2) t2 = typeCache[type2] = Object.create(null);
|
|
1315
|
+
t2[type1] = true;
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Determines whether a resource is safe to fully release from memory. A resource is not
|
|
1319
|
+
* releasable if it has a relationship whose inverse is async and the resource itself is not a
|
|
1320
|
+
* new (unsaved, client-only) record, since the inverse may still need to reference it.
|
|
1321
|
+
*/
|
|
1322
|
+
isReleasable(resourceKey) {
|
|
1323
|
+
const relationships = this.identifiers.get(resourceKey);
|
|
1324
|
+
if (!relationships) return true;
|
|
1325
|
+
const keys = Object.keys(relationships);
|
|
1326
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1327
|
+
const relationship = relationships[keys[i]];
|
|
1328
|
+
if (relationship === void 0) continue;
|
|
1329
|
+
if (relationship.definition.inverseIsAsync && !checkIfNew(this._realStore, resourceKey)) return false;
|
|
1330
|
+
}
|
|
1331
|
+
return true;
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Cleans up all relationships for a resource that is being dematerialized, notifying inverses
|
|
1335
|
+
* so they can update their own state. This retains the graph's node structure (so it can be
|
|
1336
|
+
* rematerialized later) except for implicit edges, which are discarded outright.
|
|
1337
|
+
*/
|
|
1338
|
+
unload(resourceKey, silenceNotifications) {
|
|
1339
|
+
const relationships = this.identifiers.get(resourceKey);
|
|
1340
|
+
if (relationships) Object.keys(relationships).forEach((key) => {
|
|
1341
|
+
const rel = relationships[key];
|
|
1342
|
+
if (!rel) return;
|
|
1343
|
+
destroyRelationship(this, rel, silenceNotifications);
|
|
1344
|
+
if (isImplicit(rel)) relationships[key] = void 0;
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Returns `true` if the given field on a resource has uncommitted local changes: for a
|
|
1349
|
+
* `belongsTo` this means local and remote state differ; for a `hasMany` this means there are
|
|
1350
|
+
* pending additions, removals, or a reordering relative to remote state.
|
|
1351
|
+
*/
|
|
1352
|
+
_isDirty(resourceKey, field) {
|
|
1353
|
+
const relationships = this.identifiers.get(resourceKey);
|
|
1354
|
+
if (!relationships) return false;
|
|
1355
|
+
const relationship = relationships[field];
|
|
1356
|
+
if (!relationship) return false;
|
|
1357
|
+
if (isBelongsTo(relationship)) return relationship.localState !== relationship.remoteState;
|
|
1358
|
+
else if (isHasMany(relationship)) {
|
|
1359
|
+
const hasAdditions = relationship.additions !== null && relationship.additions.size > 0;
|
|
1360
|
+
const hasRemovals = relationship.removals !== null && relationship.removals.size > 0;
|
|
1361
|
+
return hasAdditions || hasRemovals || isReordered(relationship);
|
|
1362
|
+
}
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Builds a map, keyed by field name, of every relationship on a resource that has uncommitted
|
|
1367
|
+
* local changes, describing each change as a {@link RelationshipDiff}.
|
|
1368
|
+
*/
|
|
1369
|
+
getChanged(resourceKey) {
|
|
1370
|
+
const relationships = this.identifiers.get(resourceKey);
|
|
1371
|
+
const changed = /* @__PURE__ */ new Map();
|
|
1372
|
+
if (!relationships) return changed;
|
|
1373
|
+
const keys = Object.keys(relationships);
|
|
1374
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1375
|
+
const field = keys[i];
|
|
1376
|
+
const relationship = relationships[field];
|
|
1377
|
+
if (!relationship) continue;
|
|
1378
|
+
if (isBelongsTo(relationship)) {
|
|
1379
|
+
if (relationship.localState !== relationship.remoteState) changed.set(field, {
|
|
1380
|
+
kind: "resource",
|
|
1381
|
+
remoteState: relationship.remoteState,
|
|
1382
|
+
localState: relationship.localState
|
|
1383
|
+
});
|
|
1384
|
+
} else if (isHasMany(relationship)) {
|
|
1385
|
+
const hasAdditions = relationship.additions !== null && relationship.additions.size > 0;
|
|
1386
|
+
const hasRemovals = relationship.removals !== null && relationship.removals.size > 0;
|
|
1387
|
+
const reordered = isReordered(relationship);
|
|
1388
|
+
if (hasAdditions || hasRemovals || reordered) changed.set(field, {
|
|
1389
|
+
kind: "collection",
|
|
1390
|
+
additions: new Set(relationship.additions),
|
|
1391
|
+
removals: new Set(relationship.removals),
|
|
1392
|
+
remoteState: relationship.remoteState,
|
|
1393
|
+
localState: legacyGetCollectionRelationshipData(relationship, false).data || [],
|
|
1394
|
+
reordered
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return changed;
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Returns `true` if any relationship field on the resource has uncommitted local changes.
|
|
1402
|
+
*/
|
|
1403
|
+
hasChanged(resourceKey) {
|
|
1404
|
+
const relationships = this.identifiers.get(resourceKey);
|
|
1405
|
+
if (!relationships) return false;
|
|
1406
|
+
const keys = Object.keys(relationships);
|
|
1407
|
+
for (let i = 0; i < keys.length; i++) if (this._isDirty(resourceKey, keys[i])) return true;
|
|
1408
|
+
return false;
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Reverts all locally dirty relationships on a resource back to their remote state, returning
|
|
1412
|
+
* the list of field names that were rolled back.
|
|
1413
|
+
*/
|
|
1414
|
+
rollback(resourceKey) {
|
|
1415
|
+
const relationships = this.identifiers.get(resourceKey);
|
|
1416
|
+
const changed = [];
|
|
1417
|
+
if (!relationships) return changed;
|
|
1418
|
+
const keys = Object.keys(relationships);
|
|
1419
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1420
|
+
const field = keys[i];
|
|
1421
|
+
const relationship = relationships[field];
|
|
1422
|
+
if (!relationship) continue;
|
|
1423
|
+
if (this._isDirty(resourceKey, field)) {
|
|
1424
|
+
rollbackRelationship(this, resourceKey, field, relationship);
|
|
1425
|
+
changed.push(field);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
return changed;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Fully removes a resource and all of its relationships from the graph, unloading its edges
|
|
1432
|
+
* first so inverses are properly notified/cleaned up.
|
|
1433
|
+
*/
|
|
1434
|
+
remove(resourceKey) {
|
|
1435
|
+
this._removing = resourceKey;
|
|
1436
|
+
this.unload(resourceKey);
|
|
1437
|
+
this.identifiers.delete(resourceKey);
|
|
1438
|
+
this._removing = null;
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Queues a remote relationship operation (or record deletion) to be applied to the graph.
|
|
1442
|
+
* Pushed operations are batched and flushed asynchronously (see {@link Graph._flushRemoteQueue})
|
|
1443
|
+
* rather than applied immediately.
|
|
1444
|
+
*/
|
|
1445
|
+
push(op) {
|
|
1446
|
+
if (op.op === "deleteRecord") this._pushedUpdates.deletions.push(op);
|
|
1447
|
+
else {
|
|
1448
|
+
const definition = this.getDefinition(op.record, op.field);
|
|
1449
|
+
addPending(this._pushedUpdates, definition, op);
|
|
1450
|
+
}
|
|
1451
|
+
if (!this._willSyncRemote) {
|
|
1452
|
+
this._willSyncRemote = true;
|
|
1453
|
+
const store = getStore(this.store);
|
|
1454
|
+
if (!store._cbs) store._run(() => this._flushRemoteQueue());
|
|
1455
|
+
else store._schedule("coalesce", () => this._flushRemoteQueue());
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Applies a remote relationship operation or identifier merge directly to the graph. Pass
|
|
1460
|
+
* `isRemote: true` to indicate the operation represents confirmed server state rather than a
|
|
1461
|
+
* local (client-side) change.
|
|
1462
|
+
*/
|
|
1463
|
+
/**
|
|
1464
|
+
* Applies a local (client-side) relationship operation directly to the graph, such as adding,
|
|
1465
|
+
* removing, or replacing related records before it has been confirmed by the server.
|
|
1466
|
+
*/
|
|
1467
|
+
/**
|
|
1468
|
+
* Implementation for both the remote and local {@link Graph.update} overloads: dispatches the
|
|
1469
|
+
* given operation (`mergeIdentifiers`, `updateRelationship`, `deleteRecord`, `add`, `remove`,
|
|
1470
|
+
* `replaceRelatedRecord`, or `replaceRelatedRecords`) to the appropriate handler.
|
|
1471
|
+
*/
|
|
1472
|
+
update(op, isRemote = false) {
|
|
1473
|
+
switch (op.op) {
|
|
1474
|
+
case "mergeIdentifiers": {
|
|
1475
|
+
const relationships = this.identifiers.get(op.record);
|
|
1476
|
+
if (relationships) mergeIdentifier(this, op, relationships);
|
|
1477
|
+
break;
|
|
1478
|
+
}
|
|
1479
|
+
case "update":
|
|
1480
|
+
case "updateRelationship":
|
|
1481
|
+
updateRelationshipOperation(this, op);
|
|
1482
|
+
break;
|
|
1483
|
+
case "deleteRecord": {
|
|
1484
|
+
const identifier = op.record;
|
|
1485
|
+
const relationships = this.identifiers.get(identifier);
|
|
1486
|
+
if (relationships) {
|
|
1487
|
+
Object.keys(relationships).forEach((key) => {
|
|
1488
|
+
const rel = relationships[key];
|
|
1489
|
+
if (!rel) return;
|
|
1490
|
+
relationships[key] = void 0;
|
|
1491
|
+
removeCompletelyFromInverse(this, rel);
|
|
1492
|
+
});
|
|
1493
|
+
this.identifiers.delete(identifier);
|
|
1494
|
+
}
|
|
1495
|
+
break;
|
|
1496
|
+
}
|
|
1497
|
+
case "replaceRelatedRecord":
|
|
1498
|
+
replaceRelatedRecord(this, op, isRemote);
|
|
1499
|
+
break;
|
|
1500
|
+
case "add":
|
|
1501
|
+
addToRelatedRecords(this, op, isRemote);
|
|
1502
|
+
break;
|
|
1503
|
+
case "remove":
|
|
1504
|
+
removeFromRelatedRecords(this, op, isRemote);
|
|
1505
|
+
break;
|
|
1506
|
+
case "replaceRelatedRecords": replaceRelatedRecords(this, op, isRemote);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Marks a {@link CollectionEdge} as having local changes that need to be notified, and
|
|
1511
|
+
* schedules a flush of the local sync queue (see {@link Graph._flushLocalQueue}) if one is not
|
|
1512
|
+
* already pending.
|
|
1513
|
+
*/
|
|
1514
|
+
_scheduleLocalSync(relationship) {
|
|
1515
|
+
this._updatedRelationships.add(relationship);
|
|
1516
|
+
if (!this._willSyncLocal) {
|
|
1517
|
+
this._willSyncLocal = true;
|
|
1518
|
+
getStore(this.store)._schedule("sync", () => this._flushLocalQueue());
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Flushes all remote operations queued via {@link Graph.push}, applying them within a single
|
|
1523
|
+
* transaction (see {@link Graph._transaction} and {@link Graph._addToTransaction}) so that
|
|
1524
|
+
* dependent edges updated during the flush can be distinguished from those that were not.
|
|
1525
|
+
*/
|
|
1526
|
+
_flushRemoteQueue() {
|
|
1527
|
+
if (!this._willSyncRemote) return;
|
|
1528
|
+
let transactionRef = peekTransient("transactionRef") ?? 0;
|
|
1529
|
+
this._transaction = ++transactionRef;
|
|
1530
|
+
setTransient("transactionRef", transactionRef);
|
|
1531
|
+
this._willSyncRemote = false;
|
|
1532
|
+
const updates = this._pushedUpdates;
|
|
1533
|
+
const { deletions, hasMany, belongsTo } = updates;
|
|
1534
|
+
updates.deletions = [];
|
|
1535
|
+
updates.hasMany = void 0;
|
|
1536
|
+
updates.belongsTo = void 0;
|
|
1537
|
+
for (let i = 0; i < deletions.length; i++) this.update(deletions[i], true);
|
|
1538
|
+
if (hasMany) flushPending(this, hasMany);
|
|
1539
|
+
if (belongsTo) flushPending(this, belongsTo);
|
|
1540
|
+
this._transaction = null;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Stamps the current remote-update transaction id onto an edge, marking it as having been
|
|
1544
|
+
* touched during the in-progress {@link Graph._flushRemoteQueue} run. Must only be called while
|
|
1545
|
+
* a transaction is active.
|
|
1546
|
+
*/
|
|
1547
|
+
_addToTransaction(relationship) {
|
|
1548
|
+
relationship.transactionRef = this._transaction;
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Flushes the queue of relationships scheduled via {@link Graph._scheduleLocalSync}, notifying
|
|
1552
|
+
* subscribers of each changed relationship — unless {@link Graph.silenceNotifications} is set,
|
|
1553
|
+
* in which case the queue is discarded without notifying.
|
|
1554
|
+
*/
|
|
1555
|
+
_flushLocalQueue() {
|
|
1556
|
+
if (!this._willSyncLocal) return;
|
|
1557
|
+
if (this.silenceNotifications) {
|
|
1558
|
+
this.silenceNotifications = false;
|
|
1559
|
+
this._updatedRelationships = /* @__PURE__ */ new Set();
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
this._willSyncLocal = false;
|
|
1563
|
+
const updated = this._updatedRelationships;
|
|
1564
|
+
this._updatedRelationships = /* @__PURE__ */ new Set();
|
|
1565
|
+
updated.forEach((rel) => notifyChange(this, rel));
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Tears down the graph: clears all tracked identifiers/edges, removes it from the global
|
|
1569
|
+
* {@link Graphs} registry, and marks it as destroyed. In debug builds this also asserts that no
|
|
1570
|
+
* other live, non-destroying graphs remain registered, to help catch memory leaks from
|
|
1571
|
+
* improperly torn down store instances.
|
|
1572
|
+
*/
|
|
1573
|
+
destroy() {
|
|
1574
|
+
Graphs.delete(this.store);
|
|
1575
|
+
this.identifiers.clear();
|
|
1576
|
+
this.store = null;
|
|
1577
|
+
this.isDestroyed = true;
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
2174
1580
|
function flushPending(graph, ops) {
|
|
2175
|
-
|
|
2176
|
-
for (const opList of type.values()) {
|
|
2177
|
-
flushPendingList(graph, opList);
|
|
2178
|
-
}
|
|
2179
|
-
}
|
|
1581
|
+
for (const type of ops.values()) for (const opList of type.values()) flushPendingList(graph, opList);
|
|
2180
1582
|
}
|
|
2181
1583
|
function flushPendingList(graph, opList) {
|
|
2182
|
-
|
|
2183
|
-
graph.update(opList[i], true);
|
|
2184
|
-
}
|
|
1584
|
+
for (let i = 0; i < opList.length; i++) graph.update(opList[i], true);
|
|
2185
1585
|
}
|
|
2186
|
-
|
|
2187
|
-
// Handle dematerialization for relationship `rel`. In all cases, notify the
|
|
2188
|
-
// relationship of the dematerialization: this is done so the relationship can
|
|
2189
|
-
// notify its inverse which needs to update state
|
|
2190
|
-
//
|
|
2191
|
-
// If the inverse is sync, unloading this record is treated as a client-side
|
|
2192
|
-
// delete, so we remove the inverse records from this relationship to
|
|
2193
|
-
// disconnect the graph. Because it's not async, we don't need to keep around
|
|
2194
|
-
// the identifier as an id-wrapper for references
|
|
2195
1586
|
function destroyRelationship(graph, rel, silenceNotifications) {
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
if (!rel.definition.inverseIsImplicit) {
|
|
2209
|
-
/*#__NOINLINE__*/forAllRelatedIdentifiers(rel, inverseIdentifer => /*#__NOINLINE__*/notifyInverseOfDematerialization(graph, inverseIdentifer, inverseKey, identifier, silenceNotifications));
|
|
2210
|
-
}
|
|
2211
|
-
if (!rel.definition.inverseIsImplicit && !rel.definition.inverseIsAsync) {
|
|
2212
|
-
rel.state.isStale = true;
|
|
2213
|
-
/*#__NOINLINE__*/
|
|
2214
|
-
clearRelationship(rel);
|
|
2215
|
-
|
|
2216
|
-
// necessary to clear relationships in the ui from dematerialized records
|
|
2217
|
-
// hasMany is managed by Model which calls `retreiveLatest` after
|
|
2218
|
-
// dematerializing the resource-cache instance.
|
|
2219
|
-
// but sync belongsTo requires this since they don't have a proxy to update.
|
|
2220
|
-
// so we have to notify so it will "update" to null.
|
|
2221
|
-
// we should discuss whether we still care about this, probably fine to just
|
|
2222
|
-
// leave the ui relationship populated since the record is destroyed and
|
|
2223
|
-
// internally we've fully cleaned up.
|
|
2224
|
-
if (!rel.definition.isAsync && !silenceNotifications) {
|
|
2225
|
-
/*#__NOINLINE__*/notifyChange(graph, rel);
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
1587
|
+
if (isImplicit(rel)) {
|
|
1588
|
+
if (graph.isReleasable(rel.identifier)) removeCompletelyFromInverse(graph, rel);
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
const { identifier } = rel;
|
|
1592
|
+
const { inverseKey } = rel.definition;
|
|
1593
|
+
if (!rel.definition.inverseIsImplicit) forAllRelatedIdentifiers(rel, (inverseIdentifer) => notifyInverseOfDematerialization(graph, inverseIdentifer, inverseKey, identifier, silenceNotifications));
|
|
1594
|
+
if (!rel.definition.inverseIsImplicit && !rel.definition.inverseIsAsync) {
|
|
1595
|
+
rel.state.isStale = true;
|
|
1596
|
+
clearRelationship(rel);
|
|
1597
|
+
if (!rel.definition.isAsync && !silenceNotifications) notifyChange(graph, rel);
|
|
1598
|
+
}
|
|
2228
1599
|
}
|
|
2229
1600
|
function notifyInverseOfDematerialization(graph, inverseIdentifier, inverseKey, resourceKey, silenceNotifications) {
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
const relationship = graph.get(inverseIdentifier, inverseKey);
|
|
2234
|
-
|
|
2235
|
-
// For remote members, it is possible that inverseRecordData has already been associated to
|
|
2236
|
-
// to another record. For such cases, do not dematerialize the inverseRecordData
|
|
2237
|
-
if (!isBelongsTo(relationship) || !relationship.localState || resourceKey === relationship.localState) {
|
|
2238
|
-
/*#__NOINLINE__*/removeDematerializedInverse(graph, relationship, resourceKey, silenceNotifications);
|
|
2239
|
-
}
|
|
1601
|
+
if (!graph.has(inverseIdentifier, inverseKey)) return;
|
|
1602
|
+
const relationship = graph.get(inverseIdentifier, inverseKey);
|
|
1603
|
+
if (!isBelongsTo(relationship) || !relationship.localState || resourceKey === relationship.localState) removeDematerializedInverse(graph, relationship, resourceKey, silenceNotifications);
|
|
2240
1604
|
}
|
|
2241
1605
|
function clearRelationship(relationship) {
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
1606
|
+
if (isBelongsTo(relationship)) {
|
|
1607
|
+
relationship.localState = null;
|
|
1608
|
+
relationship.remoteState = null;
|
|
1609
|
+
relationship.state.hasReceivedData = false;
|
|
1610
|
+
relationship.state.isEmpty = true;
|
|
1611
|
+
} else {
|
|
1612
|
+
relationship.remoteMembers.clear();
|
|
1613
|
+
relationship.remoteState = [];
|
|
1614
|
+
relationship.additions = null;
|
|
1615
|
+
relationship.removals = null;
|
|
1616
|
+
relationship.localState = null;
|
|
1617
|
+
}
|
|
2254
1618
|
}
|
|
2255
1619
|
function removeDematerializedInverse(graph, relationship, inverseIdentifier, silenceNotifications) {
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
} else {
|
|
2276
|
-
relationship.state.hasDematerializedInverse = true;
|
|
2277
|
-
}
|
|
2278
|
-
if (!silenceNotifications) {
|
|
2279
|
-
notifyChange(graph, relationship);
|
|
2280
|
-
}
|
|
2281
|
-
} else {
|
|
2282
|
-
if (!relationship.definition.isAsync || inverseIdentifier && checkIfNew(graph._realStore, inverseIdentifier)) {
|
|
2283
|
-
// unloading inverse of a sync relationship is treated as a client-side
|
|
2284
|
-
// delete, so actually remove the models don't merely invalidate the cp
|
|
2285
|
-
// cache.
|
|
2286
|
-
// if the record being unloaded only exists on the client, we similarly
|
|
2287
|
-
// treat it as a client side delete
|
|
2288
|
-
/*#__NOINLINE__*/
|
|
2289
|
-
removeIdentifierCompletelyFromRelationship(graph, relationship, inverseIdentifier);
|
|
2290
|
-
} else {
|
|
2291
|
-
relationship.state.hasDematerializedInverse = true;
|
|
2292
|
-
}
|
|
2293
|
-
if (!silenceNotifications) {
|
|
2294
|
-
notifyChange(graph, relationship);
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
1620
|
+
if (isBelongsTo(relationship)) {
|
|
1621
|
+
const localInverse = relationship.localState;
|
|
1622
|
+
if (!relationship.definition.isAsync || localInverse && checkIfNew(graph._realStore, localInverse)) {
|
|
1623
|
+
if (relationship.localState === localInverse && localInverse !== null) relationship.localState = null;
|
|
1624
|
+
if (relationship.remoteState === localInverse && localInverse !== null) {
|
|
1625
|
+
relationship.remoteState = null;
|
|
1626
|
+
relationship.state.hasReceivedData = true;
|
|
1627
|
+
relationship.state.isEmpty = true;
|
|
1628
|
+
if (relationship.localState && !checkIfNew(graph._realStore, relationship.localState)) relationship.localState = null;
|
|
1629
|
+
}
|
|
1630
|
+
} else relationship.state.hasDematerializedInverse = true;
|
|
1631
|
+
if (!silenceNotifications) notifyChange(graph, relationship);
|
|
1632
|
+
} else {
|
|
1633
|
+
if (!relationship.definition.isAsync || inverseIdentifier && checkIfNew(graph._realStore, inverseIdentifier)) removeIdentifierCompletelyFromRelationship(graph, relationship, inverseIdentifier);
|
|
1634
|
+
else relationship.state.hasDematerializedInverse = true;
|
|
1635
|
+
if (!silenceNotifications) notifyChange(graph, relationship);
|
|
1636
|
+
}
|
|
2297
1637
|
}
|
|
2298
1638
|
function removeCompletelyFromInverse(graph, relationship) {
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
if (!relationship.definition.isAsync) {
|
|
2317
|
-
clearRelationship(relationship);
|
|
2318
|
-
notifyChange(graph, relationship);
|
|
2319
|
-
}
|
|
2320
|
-
} else {
|
|
2321
|
-
relationship.remoteMembers.clear();
|
|
2322
|
-
relationship.localMembers.clear();
|
|
2323
|
-
}
|
|
1639
|
+
const { identifier } = relationship;
|
|
1640
|
+
const { inverseKey } = relationship.definition;
|
|
1641
|
+
forAllRelatedIdentifiers(relationship, (inverseIdentifier) => {
|
|
1642
|
+
if (graph.has(inverseIdentifier, inverseKey)) removeIdentifierCompletelyFromRelationship(graph, graph.get(inverseIdentifier, inverseKey), identifier);
|
|
1643
|
+
});
|
|
1644
|
+
if (isBelongsTo(relationship)) {
|
|
1645
|
+
if (!relationship.definition.isAsync) clearRelationship(relationship);
|
|
1646
|
+
relationship.localState = null;
|
|
1647
|
+
} else if (isHasMany(relationship)) {
|
|
1648
|
+
if (!relationship.definition.isAsync) {
|
|
1649
|
+
clearRelationship(relationship);
|
|
1650
|
+
notifyChange(graph, relationship);
|
|
1651
|
+
}
|
|
1652
|
+
} else {
|
|
1653
|
+
relationship.remoteMembers.clear();
|
|
1654
|
+
relationship.localMembers.clear();
|
|
1655
|
+
}
|
|
2324
1656
|
}
|
|
2325
1657
|
function addPending(cache, definition, op) {
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
1658
|
+
const cacheForKind = cache[definition.kind] = cache[definition.kind] || /* @__PURE__ */ new Map();
|
|
1659
|
+
let cacheForType = cacheForKind.get(definition.inverseType);
|
|
1660
|
+
if (!cacheForType) {
|
|
1661
|
+
cacheForType = /* @__PURE__ */ new Map();
|
|
1662
|
+
cacheForKind.set(definition.inverseType, cacheForType);
|
|
1663
|
+
}
|
|
1664
|
+
let cacheForField = cacheForType.get(op.field);
|
|
1665
|
+
if (!cacheForField) {
|
|
1666
|
+
cacheForField = [];
|
|
1667
|
+
cacheForType.set(op.field, cacheForField);
|
|
1668
|
+
}
|
|
1669
|
+
cacheForField.push(op);
|
|
2338
1670
|
}
|
|
2339
1671
|
function isReordered(relationship) {
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
}
|
|
2363
|
-
if (additions && additions.has(localMember)) {
|
|
2364
|
-
// increment j to skip this localMember
|
|
2365
|
-
// decrement i to repeat this remoteMember
|
|
2366
|
-
j++;
|
|
2367
|
-
i--;
|
|
2368
|
-
continue;
|
|
2369
|
-
}
|
|
2370
|
-
return true;
|
|
2371
|
-
}
|
|
2372
|
-
|
|
2373
|
-
// if we made it here, increment j
|
|
2374
|
-
j++;
|
|
2375
|
-
}
|
|
2376
|
-
return false;
|
|
2377
|
-
}
|
|
2378
|
-
|
|
1672
|
+
if (relationship.isDirty) return false;
|
|
1673
|
+
const { remoteState, localState, additions, removals } = relationship;
|
|
1674
|
+
if (localState === null) return false;
|
|
1675
|
+
for (let i = 0, j = 0; i < remoteState.length; i++) {
|
|
1676
|
+
const member = remoteState[i];
|
|
1677
|
+
const localMember = localState[j];
|
|
1678
|
+
if (member !== localMember) {
|
|
1679
|
+
if (removals && removals.has(member)) continue;
|
|
1680
|
+
if (additions && additions.has(localMember)) {
|
|
1681
|
+
j++;
|
|
1682
|
+
i--;
|
|
1683
|
+
continue;
|
|
1684
|
+
}
|
|
1685
|
+
return true;
|
|
1686
|
+
}
|
|
1687
|
+
j++;
|
|
1688
|
+
}
|
|
1689
|
+
return false;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
//#endregion
|
|
1693
|
+
//#region src/graph/-private.ts
|
|
2379
1694
|
/**
|
|
2380
1695
|
Provides a performance tuned normalized graph for intelligently managing relationships between resources based on identity
|
|
2381
1696
|
|
|
@@ -2383,26 +1698,31 @@ While this Graph is abstract, it currently is a private implementation required
|
|
|
2383
1698
|
|
|
2384
1699
|
We intend to make this Graph public API after some additional iteration during the 5.x timeframe, until then all APIs should be considered experimental and unstable, not fit for direct application or 3rd party library usage.
|
|
2385
1700
|
|
|
2386
|
-
|
|
1701
|
+
@module
|
|
2387
1702
|
*/
|
|
2388
|
-
|
|
2389
1703
|
function isStore(maybeStore) {
|
|
2390
|
-
|
|
1704
|
+
return maybeStore._instanceCache !== void 0;
|
|
2391
1705
|
}
|
|
2392
1706
|
function getWrapper(store) {
|
|
2393
|
-
|
|
1707
|
+
return isStore(store) ? store._instanceCache._storeWrapper : store;
|
|
2394
1708
|
}
|
|
1709
|
+
/** Returns the {@link Graph} associated with the given store or capabilities manager, if one has been created. */
|
|
2395
1710
|
function peekGraph(store) {
|
|
2396
|
-
|
|
1711
|
+
return Graphs.get(getWrapper(store));
|
|
2397
1712
|
}
|
|
1713
|
+
/** The type of the {@link peekGraph} function. */
|
|
1714
|
+
/** Returns the {@link Graph} for the given store, creating (and caching) one first if it does not yet exist. */
|
|
2398
1715
|
function graphFor(store) {
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
}
|
|
2408
|
-
|
|
1716
|
+
const wrapper = getWrapper(store);
|
|
1717
|
+
let graph = Graphs.get(wrapper);
|
|
1718
|
+
if (!graph) {
|
|
1719
|
+
graph = new Graph(wrapper);
|
|
1720
|
+
Graphs.set(wrapper, graph);
|
|
1721
|
+
getStore(wrapper)._graph = graph;
|
|
1722
|
+
}
|
|
1723
|
+
return graph;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
//#endregion
|
|
1727
|
+
export { graphFor, isBelongsTo, peekGraph };
|
|
1728
|
+
//# sourceMappingURL=-private.js.map
|