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