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