@warp-drive-mirror/core 5.9.0-alpha.3 → 5.9.0-alpha.31
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-B_RTqxGA.js +3530 -0
- package/dist/-private-B_RTqxGA.js.map +1 -0
- package/dist/-private-CFGb9QrK.js +22 -0
- package/dist/-private-CFGb9QrK.js.map +1 -0
- package/dist/-private-c45j0xNN.js +120 -0
- package/dist/-private-c45j0xNN.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/{declarations → dist}/build-config/canary-features.d.ts +1 -1
- 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-DD-zXzqT.js +766 -0
- package/dist/request-DD-zXzqT.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-CWAyEAhY.js +9227 -0
- package/dist/src-CWAyEAhY.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-BSbRfPj6.js +111 -0
- package/dist/unpkg/dev/-private-BSbRfPj6.js.map +1 -0
- package/dist/unpkg/dev/-private-CI7-XyWy.js +22 -0
- package/dist/unpkg/dev/-private-CI7-XyWy.js.map +1 -0
- package/dist/unpkg/dev/-private-ClYYG5n6.js +3519 -0
- package/dist/unpkg/dev/-private-ClYYG5n6.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-k0FpbUb3.js +756 -0
- package/dist/unpkg/dev/request-k0FpbUb3.js.map +1 -0
- package/dist/unpkg/dev/request.js +2 -48
- package/dist/unpkg/dev/runtime-FdPeCUwk.js +110 -0
- package/dist/unpkg/dev/runtime-FdPeCUwk.js.map +1 -0
- package/dist/unpkg/dev/signals/-leaked.js +3 -1
- package/dist/unpkg/dev/src-Pv70PU2b.js +8331 -0
- package/dist/unpkg/dev/src-Pv70PU2b.js.map +1 -0
- package/dist/unpkg/dev/store/-private.js +3 -55
- package/dist/unpkg/dev/store-BRZE8mQW.js +427 -0
- package/dist/unpkg/dev/store-BRZE8mQW.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-BSbRfPj6.js +111 -0
- package/dist/unpkg/dev-deprecated/-private-BSbRfPj6.js.map +1 -0
- package/dist/unpkg/dev-deprecated/-private-CI7-XyWy.js +22 -0
- package/dist/unpkg/dev-deprecated/-private-CI7-XyWy.js.map +1 -0
- package/dist/unpkg/dev-deprecated/-private-ClYYG5n6.js +3519 -0
- package/dist/unpkg/dev-deprecated/-private-ClYYG5n6.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-k0FpbUb3.js +756 -0
- package/dist/unpkg/dev-deprecated/request-k0FpbUb3.js.map +1 -0
- package/dist/unpkg/dev-deprecated/request.js +2 -48
- package/dist/unpkg/dev-deprecated/runtime-Klu1azLv.js +110 -0
- package/dist/unpkg/dev-deprecated/runtime-Klu1azLv.js.map +1 -0
- package/dist/unpkg/dev-deprecated/signals/-leaked.js +3 -1
- package/dist/unpkg/dev-deprecated/src-BoM1vFkf.js +8884 -0
- package/dist/unpkg/dev-deprecated/src-BoM1vFkf.js.map +1 -0
- package/dist/unpkg/dev-deprecated/store/-private.js +3 -1
- package/dist/unpkg/dev-deprecated/store-C6m5EkF2.js +427 -0
- package/dist/unpkg/dev-deprecated/store-C6m5EkF2.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-BT0vK_7v.js +3294 -0
- package/dist/unpkg/prod/-private-BT0vK_7v.js.map +1 -0
- package/dist/unpkg/prod/-private-Bo8Uf8WT.js +22 -0
- package/dist/unpkg/prod/-private-Bo8Uf8WT.js.map +1 -0
- package/dist/unpkg/prod/-private-C9DBkMSp.js +99 -0
- package/dist/unpkg/prod/-private-C9DBkMSp.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-BhRVm8-O.js +480 -0
- package/dist/unpkg/prod/request-BhRVm8-O.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-DVgNwy7L.js +6942 -0
- package/dist/unpkg/prod/src-DVgNwy7L.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-BT0vK_7v.js +3294 -0
- package/dist/unpkg/prod-deprecated/-private-BT0vK_7v.js.map +1 -0
- package/dist/unpkg/prod-deprecated/-private-Bo8Uf8WT.js +22 -0
- package/dist/unpkg/prod-deprecated/-private-Bo8Uf8WT.js.map +1 -0
- package/dist/unpkg/prod-deprecated/-private-C9DBkMSp.js +99 -0
- package/dist/unpkg/prod-deprecated/-private-C9DBkMSp.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-BhRVm8-O.js +480 -0
- package/dist/unpkg/prod-deprecated/request-BhRVm8-O.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-DumNml55.js +7375 -0
- package/dist/unpkg/prod-deprecated/src-DumNml55.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/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 -371
- 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-BlQ9ebNX.js +0 -1940
- package/dist/default-cache-policy-D7_u4YRH.js +0 -572
- package/dist/future-CPfkKOvf.js +0 -678
- package/dist/index-D4jKuR0r.js +0 -10000
- package/dist/symbols-3C1OkYtZ.js +0 -39
- package/dist/unpkg/dev/-leaked-BYj8zMi0.js +0 -1939
- package/dist/unpkg/dev/-private-sql1_mdx.js +0 -39
- package/dist/unpkg/dev/future-DUuPqyLj.js +0 -672
- package/dist/unpkg/dev/index-BrSRAfLa.js +0 -9410
- package/dist/unpkg/dev/runtime-DGG4CvlW.js +0 -135
- package/dist/unpkg/dev-deprecated/-leaked-D0KHC8Qz.js +0 -1939
- package/dist/unpkg/dev-deprecated/-private-3C1OkYtZ.js +0 -39
- package/dist/unpkg/dev-deprecated/future-DUuPqyLj.js +0 -672
- package/dist/unpkg/dev-deprecated/index-RnWJEamT.js +0 -10025
- package/dist/unpkg/dev-deprecated/runtime-DfhJzpZH.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-DeouKoRZ.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-DlxmDxns.js +0 -5237
- 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-BVKoq9bM.js +0 -334
- package/dist/unpkg/prod-deprecated/hooks-CfCFjO7d.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-_W00LRDb.js +0 -6957
|
@@ -0,0 +1,3530 @@
|
|
|
1
|
+
import { a as setTransient, r as peekTransient, t as getOrSetGlobal } from "./-private-c45j0xNN.js";
|
|
2
|
+
import { f as setPromiseResult, o as DOMError, u as getPromiseResult } from "./request-DD-zXzqT.js";
|
|
3
|
+
import { getGlobalConfig, macroCondition } from "@embroider/macros";
|
|
4
|
+
|
|
5
|
+
//#region src/signals/reactivity/configure.ts
|
|
6
|
+
const ARRAY_SIGNAL = getOrSetGlobal("#[]", Symbol("#[]"));
|
|
7
|
+
const OBJECT_SIGNAL = getOrSetGlobal("#{}", Symbol("#{}"));
|
|
8
|
+
/**
|
|
9
|
+
* An Opaque type that represents a framework specific or TC39 signal.
|
|
10
|
+
*
|
|
11
|
+
* It may be an array of signals or a single signal.
|
|
12
|
+
*
|
|
13
|
+
* @private
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The hooks which MUST be configured in order to use reactive arrays,
|
|
17
|
+
* resources and documents with framework specfic signals or TC39 signals.
|
|
18
|
+
*
|
|
19
|
+
* Support for multiple frameworks simultaneously can be done via
|
|
20
|
+
* this abstraction by returning multiple signals from the `createSignal`
|
|
21
|
+
* method, and consuming the correct one via the correct framework via
|
|
22
|
+
* the `consumeSignal` and `notifySignal` methods.
|
|
23
|
+
*
|
|
24
|
+
* Unlike many signals implementations, WarpDrive does not wrap values as
|
|
25
|
+
* signals directly, but instead uses signals to alert the reactive layer
|
|
26
|
+
* to changes in the underlying cache. E.g. a signal is associated to a value,
|
|
27
|
+
* but does not serve as the cache for that value directly. We refer to this as
|
|
28
|
+
* a "gate", the pattern has also been called "side-signals".
|
|
29
|
+
*
|
|
30
|
+
* A no-op implementation is allowed, though it may lead to performance issues
|
|
31
|
+
* in locations that use createMemo as no memoization would be done. This is
|
|
32
|
+
* typically desirable only when integrating with a framework that does its own
|
|
33
|
+
* memoization and does not integrate with any signals-like primitive. For these
|
|
34
|
+
* scenarios you may also be interested in integrating with the {@link NotificationManager}
|
|
35
|
+
* more directly.
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* Contains information a {@link SignalHooks} implementation may want
|
|
41
|
+
* to use, such as the specialized key used for the signal
|
|
42
|
+
* representing an array's contents / length.
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* interface HooksOptions {
|
|
46
|
+
* wellknown: {
|
|
47
|
+
* Array: symbol | string;
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @public
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* Configures the signals implementation to use. Supports multiple
|
|
56
|
+
* implementations simultaneously.
|
|
57
|
+
*
|
|
58
|
+
* See {@link HooksOptions} for the options passed to the provided function
|
|
59
|
+
* when called.
|
|
60
|
+
*
|
|
61
|
+
* See {@link SignalHooks} for the implementation the callback function should
|
|
62
|
+
* return.
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
65
|
+
* @param buildConfig - a function that takes options and returns a configuration object
|
|
66
|
+
*/
|
|
67
|
+
function setupSignals(buildConfig) {
|
|
68
|
+
const hooks = buildConfig({ wellknown: { Array: ARRAY_SIGNAL } });
|
|
69
|
+
setTransient("signalHooks", hooks);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Internal method for consuming the configured `createSignal` hook
|
|
73
|
+
*
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
function createSignal(obj, key) {
|
|
77
|
+
const signalHooks = peekTransient("signalHooks");
|
|
78
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
79
|
+
if (!test) throw new Error(`Signal hooks not configured`);
|
|
80
|
+
})(signalHooks);
|
|
81
|
+
return signalHooks.createSignal(obj, key);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Internal method for consuming the configured `consumeSignal` hook
|
|
85
|
+
*
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
function consumeSignal(signal) {
|
|
89
|
+
const signalHooks = peekTransient("signalHooks");
|
|
90
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
91
|
+
if (!test) throw new Error(`Signal hooks not configured`);
|
|
92
|
+
})(signalHooks);
|
|
93
|
+
return signalHooks.consumeSignal(signal);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Internal method for consuming the configured `notifySignal` hook
|
|
97
|
+
*
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
function notifySignal(signal) {
|
|
101
|
+
const signalHooks = peekTransient("signalHooks");
|
|
102
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
103
|
+
if (!test) throw new Error(`Signal hooks not configured`);
|
|
104
|
+
})(signalHooks);
|
|
105
|
+
return signalHooks.notifySignal(signal);
|
|
106
|
+
}
|
|
107
|
+
function createMemo(object, key, fn) {
|
|
108
|
+
const signalHooks = peekTransient("signalHooks");
|
|
109
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
110
|
+
if (!test) throw new Error(`Signal hooks not configured`);
|
|
111
|
+
})(signalHooks);
|
|
112
|
+
return signalHooks.createMemo(object, key, fn);
|
|
113
|
+
}
|
|
114
|
+
function willSyncFlushWatchers() {
|
|
115
|
+
const signalHooks = peekTransient("signalHooks");
|
|
116
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
117
|
+
if (!test) throw new Error(`Signal hooks not configured`);
|
|
118
|
+
})(signalHooks);
|
|
119
|
+
return signalHooks.willSyncFlushWatchers();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Wraps `promise` using the configured {@link SignalHooks.waitFor}, if any,
|
|
123
|
+
* for things like test-waiters. Returns `promise` unchanged if no
|
|
124
|
+
* `waitFor` hook is configured.
|
|
125
|
+
*/
|
|
126
|
+
function waitFor(promise) {
|
|
127
|
+
const signalHooks = peekTransient("signalHooks");
|
|
128
|
+
if (signalHooks?.waitFor) return signalHooks.waitFor(promise);
|
|
129
|
+
return promise;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/signals/reactivity/internal.ts
|
|
134
|
+
const INITIALIZER_PROTO = { isInitializer: true };
|
|
135
|
+
/**
|
|
136
|
+
* Wraps `fn` so that a signal can recognize it as a lazy initializer
|
|
137
|
+
* for its value rather than the value itself, deferring the call to
|
|
138
|
+
* `fn` until the signal is first accessed.
|
|
139
|
+
*/
|
|
140
|
+
function makeInitializer(fn) {
|
|
141
|
+
return Object.assign(Object.create(INITIALIZER_PROTO), { value: fn });
|
|
142
|
+
}
|
|
143
|
+
function isInitializer(obj) {
|
|
144
|
+
return typeof obj === "object" && obj !== null && Object.getPrototypeOf(obj) === INITIALIZER_PROTO;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A WarpDriveSignal is a wrapper around a framework specific or TC39 signal
|
|
148
|
+
* that enables us to store and manage the signal in a universal way.
|
|
149
|
+
*
|
|
150
|
+
* WarpDrive uses signals to manage three separate concepts:
|
|
151
|
+
*
|
|
152
|
+
* - as a `storage` for a value local to the object that we want to be reactive
|
|
153
|
+
* (see `@local` schema field for an example)
|
|
154
|
+
* - as a `gate` for a memoized getter that we want to act as a reactive property
|
|
155
|
+
* but whose value is computed/pulled from a non-reactive source elsewhere
|
|
156
|
+
* and whose latest value is stored in the signal
|
|
157
|
+
* (see `field` schema field for an example)
|
|
158
|
+
* - as a `gate` with a manually managed value updated on pull when `isStale` is true
|
|
159
|
+
*
|
|
160
|
+
*
|
|
161
|
+
* It offers
|
|
162
|
+
*
|
|
163
|
+
* - a non-reactive way to access/update the current value
|
|
164
|
+
* - a non-reactive way to mark the signal as dirtied
|
|
165
|
+
* - a non-reactive way to store content for why the signal was dirtied
|
|
166
|
+
* - access to the underlying Signal(s) in-use
|
|
167
|
+
*
|
|
168
|
+
* For debugging:
|
|
169
|
+
* - the "key" or "name" of the signal
|
|
170
|
+
* - the "object identity" or "context" to which the signal is attached
|
|
171
|
+
*
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
/**
|
|
175
|
+
* We attach signals to their context object via
|
|
176
|
+
* a Map attached to the object via this symbol.
|
|
177
|
+
*
|
|
178
|
+
* This allows us to store multiple signals
|
|
179
|
+
* on the same object with smaller memory
|
|
180
|
+
* overhead and no WeakMap lookups.
|
|
181
|
+
*
|
|
182
|
+
* Performance sensitive objects should
|
|
183
|
+
* pre-warm their shape by assigning this
|
|
184
|
+
* during initialization.
|
|
185
|
+
*
|
|
186
|
+
* ```ts
|
|
187
|
+
* initializeSignalStore(obj);
|
|
188
|
+
* ```
|
|
189
|
+
*
|
|
190
|
+
* @private
|
|
191
|
+
*/
|
|
192
|
+
const Signals = getOrSetGlobal("Signals", Symbol("Signals"));
|
|
193
|
+
/**
|
|
194
|
+
* A type util to recast the object as having a signal store.
|
|
195
|
+
*
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
function upgradeWithSignals(obj) {}
|
|
199
|
+
/**
|
|
200
|
+
* A util that will create a signal store on the object
|
|
201
|
+
* if it does not already exist and returns the associated
|
|
202
|
+
* signal store.
|
|
203
|
+
*
|
|
204
|
+
* @private
|
|
205
|
+
*/
|
|
206
|
+
function withSignalStore(obj) {
|
|
207
|
+
/* @__PURE__ */ upgradeWithSignals(obj);
|
|
208
|
+
if (!obj[Signals]) initializeSignalStore(obj);
|
|
209
|
+
return obj[Signals];
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* A util that will create a signal store on the object
|
|
213
|
+
* if it does not already exist.
|
|
214
|
+
*
|
|
215
|
+
* Useful for pre-warming the shape of an object to ensure
|
|
216
|
+
* a key-transition to add it is not required later.
|
|
217
|
+
*
|
|
218
|
+
* @private
|
|
219
|
+
*/
|
|
220
|
+
function initializeSignalStore(obj) {
|
|
221
|
+
/* @__PURE__ */ upgradeWithSignals(obj);
|
|
222
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
223
|
+
if (!test) throw new Error(`Signal store already exists on object`);
|
|
224
|
+
})(!obj[Signals]);
|
|
225
|
+
obj[Signals] = /* @__PURE__ */ new Map();
|
|
226
|
+
}
|
|
227
|
+
function createInternalSignal(signals, obj, key, initialValue) {
|
|
228
|
+
const warpDriveSignal = {
|
|
229
|
+
key,
|
|
230
|
+
context: obj,
|
|
231
|
+
signal: createSignal(obj, key),
|
|
232
|
+
value: isInitializer(initialValue) ? initialValue.value.call(obj) : initialValue,
|
|
233
|
+
isStale: false
|
|
234
|
+
};
|
|
235
|
+
signals.set(key, warpDriveSignal);
|
|
236
|
+
return warpDriveSignal;
|
|
237
|
+
}
|
|
238
|
+
function getOrCreateInternalSignal(signals, obj, key, initialValue) {
|
|
239
|
+
let signal = peekInternalSignal(signals, key);
|
|
240
|
+
if (!signal) signal = createInternalSignal(signals, obj, key, initialValue);
|
|
241
|
+
return signal;
|
|
242
|
+
}
|
|
243
|
+
function createInternalMemo(signals, object, key, fn) {
|
|
244
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
245
|
+
if (!test) throw new Error(`Expected no signal/memo to exist for key "${String(key)}"`);
|
|
246
|
+
})(!peekInternalSignal(signals, key));
|
|
247
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) return withFrame(signals, object, key, fn);
|
|
248
|
+
else {
|
|
249
|
+
const memo = createMemo(object, key, fn);
|
|
250
|
+
signals.set(key, memo);
|
|
251
|
+
return memo;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Looks up the {@link WarpDriveSignal} stored for `key` in `signals`,
|
|
256
|
+
* if one has already been created, without creating or consuming it.
|
|
257
|
+
*/
|
|
258
|
+
function peekInternalSignal(signals, key) {
|
|
259
|
+
return signals?.get(key);
|
|
260
|
+
}
|
|
261
|
+
function consumeInternalSignal(signal) {
|
|
262
|
+
TrackingFrame?.signals.add(signal);
|
|
263
|
+
consumeSignal(signal.signal);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Marks `signal` as stale and notifies its underlying framework/TC39
|
|
267
|
+
* signal, scheduling any of its consumers for re-render/re-computation.
|
|
268
|
+
*/
|
|
269
|
+
function notifyInternalSignal(signal) {
|
|
270
|
+
if (signal) {
|
|
271
|
+
signal.isStale = true;
|
|
272
|
+
notifySignal(signal.signal);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
let TrackingFrame = null;
|
|
276
|
+
/**
|
|
277
|
+
* This is currently just for signals debugging, but it could be used in production
|
|
278
|
+
* if we wanted to eliminate the need for frameworks to implement createMemo / to
|
|
279
|
+
* allow us to add our own Watcher.
|
|
280
|
+
*
|
|
281
|
+
* @internal
|
|
282
|
+
*/
|
|
283
|
+
function withFrame(signals, object, key, fn) {
|
|
284
|
+
const frameSignals = /* @__PURE__ */ new Set();
|
|
285
|
+
const frameFn = () => {
|
|
286
|
+
if (frameSignals.size) frameSignals.clear();
|
|
287
|
+
TrackingFrame = {
|
|
288
|
+
object,
|
|
289
|
+
key,
|
|
290
|
+
signals: frameSignals,
|
|
291
|
+
parent: TrackingFrame
|
|
292
|
+
};
|
|
293
|
+
try {
|
|
294
|
+
return fn();
|
|
295
|
+
} finally {
|
|
296
|
+
TrackingFrame = TrackingFrame.parent;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const memo = createMemo(object, key, frameFn);
|
|
300
|
+
memo.signals = frameSignals;
|
|
301
|
+
signals.set(key, memo);
|
|
302
|
+
return memo;
|
|
303
|
+
}
|
|
304
|
+
function isMemo(obj) {
|
|
305
|
+
return typeof obj === "function" && obj.signals instanceof Set;
|
|
306
|
+
}
|
|
307
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) globalThis.debugWarpDriveSignals = (obj, key) => {
|
|
308
|
+
/* @__PURE__ */ upgradeWithSignals(obj);
|
|
309
|
+
const signals = obj[Signals];
|
|
310
|
+
if (!signals) {
|
|
311
|
+
log("The object has no associated signals");
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
if (key) {
|
|
315
|
+
const signal = signals.get(key);
|
|
316
|
+
if (!signal) {
|
|
317
|
+
log(`No signal found for key "${String(key)}"`);
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
log(signal);
|
|
321
|
+
if (isMemo(signal)) {
|
|
322
|
+
colorizeLines(printMemo(signal, key));
|
|
323
|
+
return true;
|
|
324
|
+
} else {
|
|
325
|
+
colorizeLines(printSignal(signal, key));
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const lines = [];
|
|
330
|
+
for (const [k, signal] of signals) {
|
|
331
|
+
if (isMemo(signal)) continue;
|
|
332
|
+
printSignal(signal, k, lines);
|
|
333
|
+
}
|
|
334
|
+
for (const [k, signal] of signals) if (isMemo(signal)) printMemo(signal, k, lines);
|
|
335
|
+
log(signals);
|
|
336
|
+
colorizeLines(lines);
|
|
337
|
+
return true;
|
|
338
|
+
};
|
|
339
|
+
const LightColors = {
|
|
340
|
+
red: "color: red;",
|
|
341
|
+
green: "color: green;",
|
|
342
|
+
reset: "color: inherit;"
|
|
343
|
+
};
|
|
344
|
+
const DarkColors = {
|
|
345
|
+
red: "color: red;",
|
|
346
|
+
green: "color: lightgreen;",
|
|
347
|
+
reset: "color: inherit;"
|
|
348
|
+
};
|
|
349
|
+
function isLightMode() {
|
|
350
|
+
if (window?.matchMedia?.("(prefers-color-scheme: light)").matches) return true;
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
const RED = {};
|
|
354
|
+
const GREEN = {};
|
|
355
|
+
const RESET = {};
|
|
356
|
+
const EOL = {};
|
|
357
|
+
function colorizeLines(lines) {
|
|
358
|
+
const Colors = isLightMode() ? LightColors : DarkColors;
|
|
359
|
+
const colors = [];
|
|
360
|
+
let line = "";
|
|
361
|
+
for (const str of lines) if (str === RED) {
|
|
362
|
+
colors.push(Colors.red);
|
|
363
|
+
line += "%c";
|
|
364
|
+
} else if (str === GREEN) {
|
|
365
|
+
colors.push(Colors.green);
|
|
366
|
+
line += "%c";
|
|
367
|
+
} else if (str === RESET) {
|
|
368
|
+
colors.push(Colors.reset);
|
|
369
|
+
line += "%c";
|
|
370
|
+
} else if (str === EOL) line += "\n";
|
|
371
|
+
else line += str;
|
|
372
|
+
log(line, ...colors);
|
|
373
|
+
}
|
|
374
|
+
function log(...args) {
|
|
375
|
+
console.log(...args);
|
|
376
|
+
}
|
|
377
|
+
function isDirty(signal) {
|
|
378
|
+
return signal.isStale;
|
|
379
|
+
}
|
|
380
|
+
function isDirtyMemo(memo) {
|
|
381
|
+
for (const signal of memo.signals) {
|
|
382
|
+
if (isMemo(signal)) continue;
|
|
383
|
+
if (isDirty(signal)) return true;
|
|
384
|
+
}
|
|
385
|
+
for (const signal of memo.signals) if (isMemo(signal)) return isDirtyMemo(signal);
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
function printSignal(signal, key, lines = [], depth = 0) {
|
|
389
|
+
const _dirty = isDirty(signal);
|
|
390
|
+
lines.push(`${"".padStart(depth * 2, " ")}${_dirty ? "❌" : "✅"} `, _dirty ? RED : GREEN, `${String(key)}`, RESET, EOL);
|
|
391
|
+
return lines;
|
|
392
|
+
}
|
|
393
|
+
function printMemo(memo, key, lines = [], depth = 0) {
|
|
394
|
+
const _dirty = isDirtyMemo(memo);
|
|
395
|
+
lines.push(`${"".padStart(depth * 2, " ")}${_dirty ? "❌" : "✅"} `, _dirty ? RED : GREEN, `<memo> ${String(key)}`, RESET, `: (consumes ${memo.signals.size} signals)`, EOL);
|
|
396
|
+
for (const signal of memo.signals) {
|
|
397
|
+
if (isMemo(signal)) continue;
|
|
398
|
+
printSignal(signal, signal.key, lines, depth + 1);
|
|
399
|
+
}
|
|
400
|
+
for (const signal of memo.signals) if (isMemo(signal)) printMemo(signal, signal.key, lines, depth + 1);
|
|
401
|
+
return lines;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/signals/reactivity/signal.ts
|
|
406
|
+
/**
|
|
407
|
+
* Creates a signal for the key/object pairing and subscribes to the signal.
|
|
408
|
+
*
|
|
409
|
+
* Use when you need to ensure a signal exists and is subscribed to.
|
|
410
|
+
*
|
|
411
|
+
* @private
|
|
412
|
+
*/
|
|
413
|
+
function entangleSignal(signals, obj, key, initialValue) {
|
|
414
|
+
let internalSignal = peekInternalSignal(signals, key);
|
|
415
|
+
if (!internalSignal) internalSignal = createInternalSignal(signals, obj, key, initialValue);
|
|
416
|
+
consumeInternalSignal(internalSignal);
|
|
417
|
+
return internalSignal;
|
|
418
|
+
}
|
|
419
|
+
function entangleInitiallyStaleSignal(signals, obj, key, initialValue) {
|
|
420
|
+
let internalSignal = peekInternalSignal(signals, key);
|
|
421
|
+
if (!internalSignal) {
|
|
422
|
+
internalSignal = createInternalSignal(signals, obj, key, initialValue);
|
|
423
|
+
internalSignal.isStale = true;
|
|
424
|
+
}
|
|
425
|
+
consumeInternalSignal(internalSignal);
|
|
426
|
+
return internalSignal;
|
|
427
|
+
}
|
|
428
|
+
function createSignalDescriptor(key, intialValue) {
|
|
429
|
+
return {
|
|
430
|
+
enumerable: true,
|
|
431
|
+
configurable: false,
|
|
432
|
+
get() {
|
|
433
|
+
const internalSignal = entangleSignal(withSignalStore(this), this, key, intialValue);
|
|
434
|
+
internalSignal.isStale = false;
|
|
435
|
+
return internalSignal.value;
|
|
436
|
+
},
|
|
437
|
+
set(value) {
|
|
438
|
+
const signals = withSignalStore(this);
|
|
439
|
+
const internalSignal = getOrCreateInternalSignal(signals, this, key, intialValue);
|
|
440
|
+
if (internalSignal.value !== value) {
|
|
441
|
+
internalSignal.value = value;
|
|
442
|
+
notifyInternalSignal(internalSignal);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* define an enumerable signal property.
|
|
449
|
+
*
|
|
450
|
+
* Akin to Object.defineProperty.
|
|
451
|
+
*
|
|
452
|
+
* The signal will be lazily created when accessed and scoped to the
|
|
453
|
+
* instance of the object.
|
|
454
|
+
*
|
|
455
|
+
* @private
|
|
456
|
+
*/
|
|
457
|
+
function defineSignal(obj, key, v) {
|
|
458
|
+
Object.defineProperty(obj, key, createSignalDescriptor(key, v));
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Define a non-enumerable signal property.
|
|
462
|
+
*
|
|
463
|
+
* @private
|
|
464
|
+
*/
|
|
465
|
+
function defineNonEnumerableSignal(obj, key, v) {
|
|
466
|
+
const desc = createSignalDescriptor(key, v);
|
|
467
|
+
desc.enumerable = false;
|
|
468
|
+
Object.defineProperty(obj, key, desc);
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Decorator version of creating a signal.
|
|
472
|
+
*/
|
|
473
|
+
function signal(target, key, descriptor) {
|
|
474
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
475
|
+
if (!test) throw new Error("You attempted to use @signal(), which is not necessary nor supported. Remove the parentheses and you will be good to go!");
|
|
476
|
+
})(target !== void 0);
|
|
477
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
478
|
+
if (!test) throw new Error(`You attempted to use @signal on with ${arguments.length > 1 ? "arguments" : "an argument"} ( @signal(${Array.from(arguments).map((d) => `'${d}'`).join(", ")}) ), which is not supported. Dependencies are automatically tracked, so you can just use \`@signal\``);
|
|
479
|
+
})(typeof target === "object" && typeof key === "string" && typeof descriptor === "object" && arguments.length === 3);
|
|
480
|
+
return createSignalDescriptor(key, descriptor.initializer ? makeInitializer(descriptor.initializer) : null);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Decorator version of creating a memoized getter
|
|
484
|
+
*/
|
|
485
|
+
function memoized(target, key, descriptor) {
|
|
486
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
487
|
+
if (!test) throw new Error("You attempted to use @memoized(), which is not necessary nor supported. Remove the parentheses and you will be good to go!");
|
|
488
|
+
})(target !== void 0);
|
|
489
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
490
|
+
if (!test) throw new Error(`You attempted to use @memoized on with ${arguments.length > 1 ? "arguments" : "an argument"} ( @memoized(${Array.from(arguments).map((d) => `'${d}'`).join(", ")}), which is not supported. Dependencies are automatically tracked, so you can just use \`@memoized\``);
|
|
491
|
+
})(typeof target === "object" && typeof key === "string" && typeof descriptor === "object" && arguments.length === 3);
|
|
492
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
493
|
+
if (!test) throw new Error(`The @memoized decorator must be applied to getters. '${key}' is not a getter.`);
|
|
494
|
+
})(typeof descriptor.get === "function");
|
|
495
|
+
const getter = descriptor.get;
|
|
496
|
+
descriptor.get = function() {
|
|
497
|
+
const signals = withSignalStore(this);
|
|
498
|
+
let memoSignal = signals.get(key);
|
|
499
|
+
if (!memoSignal) memoSignal = createInternalMemo(signals, this, key, getter.bind(this));
|
|
500
|
+
return memoSignal();
|
|
501
|
+
};
|
|
502
|
+
return descriptor;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Decorator version of creating a gate.
|
|
506
|
+
*
|
|
507
|
+
* @private
|
|
508
|
+
*/
|
|
509
|
+
function gate(_target, key, desc) {
|
|
510
|
+
const getter = desc.get;
|
|
511
|
+
const setter = desc.set;
|
|
512
|
+
const isLocal = desc.isLocal;
|
|
513
|
+
desc.get = function() {
|
|
514
|
+
const signals = withSignalStore(this);
|
|
515
|
+
let internalSignal = peekInternalSignal(signals, key);
|
|
516
|
+
if (!internalSignal) internalSignal = createInternalSignal(signals, this, key, getter.call(this));
|
|
517
|
+
else if (internalSignal.isStale) {
|
|
518
|
+
internalSignal.isStale = false;
|
|
519
|
+
internalSignal.value = getter.call(this);
|
|
520
|
+
}
|
|
521
|
+
consumeInternalSignal(internalSignal);
|
|
522
|
+
return internalSignal.value;
|
|
523
|
+
};
|
|
524
|
+
if (setter) desc.set = function(v) {
|
|
525
|
+
const signals = withSignalStore(this);
|
|
526
|
+
let internalSignal = peekInternalSignal(signals, key);
|
|
527
|
+
if (!internalSignal) {
|
|
528
|
+
internalSignal = createInternalSignal(signals, this, key, void 0);
|
|
529
|
+
internalSignal.isStale = true;
|
|
530
|
+
}
|
|
531
|
+
setter.call(this, v);
|
|
532
|
+
if (isLocal) {
|
|
533
|
+
internalSignal.isStale = true;
|
|
534
|
+
notifyInternalSignal(internalSignal);
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
return desc;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Defines a reactive "gated" property on `obj` using `desc` as its
|
|
541
|
+
* getter/setter descriptor.
|
|
542
|
+
*
|
|
543
|
+
* Unlike a signal defined with {@link defineSignal}, a gated property's
|
|
544
|
+
* consumers are not notified when the property is set, since its update
|
|
545
|
+
* is expected to be controlled externally (for instance, by a signal it
|
|
546
|
+
* derives its value from) unless the descriptor opts into being locally
|
|
547
|
+
* managed.
|
|
548
|
+
*
|
|
549
|
+
* Akin to `Object.defineProperty`.
|
|
550
|
+
*
|
|
551
|
+
* @private
|
|
552
|
+
*/
|
|
553
|
+
function defineGate(obj, key, desc) {
|
|
554
|
+
const options = Object.assign({
|
|
555
|
+
enumerable: true,
|
|
556
|
+
configurable: false
|
|
557
|
+
}, gate(obj, key, desc));
|
|
558
|
+
Object.defineProperty(obj, key, options);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region src/signals/request-state.ts
|
|
563
|
+
const RequestCache = /* @__PURE__ */ new WeakMap();
|
|
564
|
+
function isAbortError(error) {
|
|
565
|
+
return error instanceof DOMError && error.name === "AbortError";
|
|
566
|
+
}
|
|
567
|
+
function upgradeLoadingState(state) {
|
|
568
|
+
return state;
|
|
569
|
+
}
|
|
570
|
+
async function watchStream(stream, loadingState) {
|
|
571
|
+
const state = upgradeLoadingState(loadingState);
|
|
572
|
+
const reader = stream.getReader();
|
|
573
|
+
let bytesLoaded = 0;
|
|
574
|
+
let shouldForward = state._stream !== null && state._stream.readable.locked;
|
|
575
|
+
let isForwarding = shouldForward;
|
|
576
|
+
let writer = state._stream?.writable.getWriter();
|
|
577
|
+
const buffer = [];
|
|
578
|
+
state._isPending = false;
|
|
579
|
+
state._isStarted = true;
|
|
580
|
+
state._startTime = performance.now();
|
|
581
|
+
while (true) {
|
|
582
|
+
const { value, done } = await reader.read();
|
|
583
|
+
if (done) break;
|
|
584
|
+
bytesLoaded += value.byteLength;
|
|
585
|
+
state._bytesLoaded = bytesLoaded;
|
|
586
|
+
state._lastPacketTime = performance.now();
|
|
587
|
+
shouldForward = shouldForward || state._stream !== null && state._stream.readable.locked;
|
|
588
|
+
if (shouldForward) {
|
|
589
|
+
if (!isForwarding) {
|
|
590
|
+
isForwarding = true;
|
|
591
|
+
writer = state._stream.writable.getWriter();
|
|
592
|
+
for (const item of buffer) {
|
|
593
|
+
await writer.ready;
|
|
594
|
+
await writer.write(item);
|
|
595
|
+
}
|
|
596
|
+
buffer.length = 0;
|
|
597
|
+
}
|
|
598
|
+
await writer.ready;
|
|
599
|
+
await writer.write(value);
|
|
600
|
+
} else buffer.push(value);
|
|
601
|
+
}
|
|
602
|
+
if (isForwarding) {
|
|
603
|
+
await writer.ready;
|
|
604
|
+
await writer.close();
|
|
605
|
+
} else if (state._stream) {
|
|
606
|
+
await state._stream.readable.cancel("The Stream Has Already Ended");
|
|
607
|
+
state._stream = null;
|
|
608
|
+
}
|
|
609
|
+
state._endTime = performance.now();
|
|
610
|
+
state._isComplete = true;
|
|
611
|
+
state._isStarted = false;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Lazily consumes the stream of a request, providing a number of
|
|
615
|
+
* reactive properties that can be used to build UIs that respond
|
|
616
|
+
* to the progress of a request.
|
|
617
|
+
*
|
|
618
|
+
* @hideconstructor
|
|
619
|
+
*/
|
|
620
|
+
var RequestLoadingState = class {
|
|
621
|
+
/** @internal */
|
|
622
|
+
/** @internal */
|
|
623
|
+
/** @internal */
|
|
624
|
+
/** @internal */
|
|
625
|
+
/** @internal */
|
|
626
|
+
/** @internal */
|
|
627
|
+
/** @internal */
|
|
628
|
+
/** @internal */
|
|
629
|
+
/** @internal */
|
|
630
|
+
/** @internal */
|
|
631
|
+
/** @internal */
|
|
632
|
+
/** @internal */
|
|
633
|
+
_stream = null;
|
|
634
|
+
/** @internal */
|
|
635
|
+
_future;
|
|
636
|
+
/** @internal */
|
|
637
|
+
_triggered = false;
|
|
638
|
+
/** @internal */
|
|
639
|
+
_trigger() {
|
|
640
|
+
if (this._triggered) return;
|
|
641
|
+
this._triggered = true;
|
|
642
|
+
const promise = this._future.getStream();
|
|
643
|
+
if (promise.sizeHint) this._sizeHint = promise.sizeHint;
|
|
644
|
+
this.promise = promise.then((stream) => {
|
|
645
|
+
if (!stream) {
|
|
646
|
+
this._isPending = false;
|
|
647
|
+
this._isComplete = true;
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
return watchStream(stream, this);
|
|
651
|
+
}, (error) => {
|
|
652
|
+
this._isPending = false;
|
|
653
|
+
this._isStarted = false;
|
|
654
|
+
if (isAbortError(error)) {
|
|
655
|
+
this._isCancelled = true;
|
|
656
|
+
this._isComplete = true;
|
|
657
|
+
}
|
|
658
|
+
this._isErrored = true;
|
|
659
|
+
this._error = error;
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Resolves once the stream has finished being consumed, or rejects
|
|
664
|
+
* if the request errors. `null` until first accessed.
|
|
665
|
+
*/
|
|
666
|
+
promise = null;
|
|
667
|
+
/**
|
|
668
|
+
* Whether the stream has not yet started being consumed.
|
|
669
|
+
*/
|
|
670
|
+
get isPending() {
|
|
671
|
+
this._trigger();
|
|
672
|
+
return this._isPending;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* The total size of the response in bytes, if known ahead of time
|
|
676
|
+
* (via a `Content-Length` header or similar).
|
|
677
|
+
*/
|
|
678
|
+
get sizeHint() {
|
|
679
|
+
this._trigger();
|
|
680
|
+
return this._sizeHint;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* A readable stream of the response content. Accessing this property
|
|
684
|
+
* begins consumption of the underlying request stream.
|
|
685
|
+
*/
|
|
686
|
+
get stream() {
|
|
687
|
+
this._trigger();
|
|
688
|
+
if (!this._stream) {
|
|
689
|
+
if (this._isComplete || this._isCancelled || this._isErrored) return null;
|
|
690
|
+
this._stream = new TransformStream();
|
|
691
|
+
}
|
|
692
|
+
return this._stream.readable;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Whether the stream has started being consumed but has not yet completed.
|
|
696
|
+
*/
|
|
697
|
+
get isStarted() {
|
|
698
|
+
this._trigger();
|
|
699
|
+
return this._isStarted;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* The number of bytes loaded so far.
|
|
703
|
+
*/
|
|
704
|
+
get bytesLoaded() {
|
|
705
|
+
this._trigger();
|
|
706
|
+
return this._bytesLoaded;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* The `performance.now()` timestamp at which the stream started being consumed.
|
|
710
|
+
*/
|
|
711
|
+
get startTime() {
|
|
712
|
+
this._trigger();
|
|
713
|
+
return this._startTime;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* The `performance.now()` timestamp at which the stream finished being consumed.
|
|
717
|
+
*/
|
|
718
|
+
get endTime() {
|
|
719
|
+
this._trigger();
|
|
720
|
+
return this._endTime;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* The `performance.now()` timestamp at which the last chunk of data was received.
|
|
724
|
+
*/
|
|
725
|
+
get lastPacketTime() {
|
|
726
|
+
this._trigger();
|
|
727
|
+
return this._lastPacketTime;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Whether the stream has finished being consumed.
|
|
731
|
+
*/
|
|
732
|
+
get isComplete() {
|
|
733
|
+
this._trigger();
|
|
734
|
+
return this._isComplete;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Whether the request was aborted before the stream finished being consumed.
|
|
738
|
+
*/
|
|
739
|
+
get isCancelled() {
|
|
740
|
+
this._trigger();
|
|
741
|
+
return this._isCancelled;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Whether an error occurred while consuming the stream.
|
|
745
|
+
*/
|
|
746
|
+
get isErrored() {
|
|
747
|
+
this._trigger();
|
|
748
|
+
return this._isErrored;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* The error that occurred while consuming the stream, if any.
|
|
752
|
+
*/
|
|
753
|
+
get error() {
|
|
754
|
+
this._trigger();
|
|
755
|
+
return this._error;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* The number of milliseconds elapsed since the stream started being
|
|
759
|
+
* consumed, using the last packet time (or end time, once complete)
|
|
760
|
+
* as the endpoint.
|
|
761
|
+
*/
|
|
762
|
+
get elapsedTime() {
|
|
763
|
+
return (this.endTime || this.lastPacketTime) - this.startTime;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* The ratio (0 to 1) of {@link RequestLoadingState.bytesLoaded | bytesLoaded}
|
|
767
|
+
* to {@link RequestLoadingState.sizeHint | sizeHint}, or `0` if no size hint is available.
|
|
768
|
+
*/
|
|
769
|
+
get completedRatio() {
|
|
770
|
+
return this.sizeHint ? this.bytesLoaded / this.sizeHint : 0;
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* The inverse of {@link RequestLoadingState.completedRatio | completedRatio}.
|
|
774
|
+
*/
|
|
775
|
+
get remainingRatio() {
|
|
776
|
+
return 1 - this.completedRatio;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* The total number of milliseconds elapsed between
|
|
780
|
+
* {@link RequestLoadingState.startTime | startTime} and {@link RequestLoadingState.endTime | endTime}.
|
|
781
|
+
*/
|
|
782
|
+
get duration() {
|
|
783
|
+
return this.endTime - this.startTime;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* The average download speed in bytes per second.
|
|
787
|
+
*/
|
|
788
|
+
get speed() {
|
|
789
|
+
return this.bytesLoaded / (this.elapsedTime / 1e3);
|
|
790
|
+
}
|
|
791
|
+
constructor(future) {
|
|
792
|
+
this._future = future;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Aborts the underlying request.
|
|
796
|
+
*/
|
|
797
|
+
abort = () => {
|
|
798
|
+
this._future.abort();
|
|
799
|
+
};
|
|
800
|
+
};
|
|
801
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_isPending", true);
|
|
802
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_isStarted", false);
|
|
803
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_isComplete", false);
|
|
804
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_isCancelled", false);
|
|
805
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_isErrored", false);
|
|
806
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_error", null);
|
|
807
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_sizeHint", 0);
|
|
808
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_bytesLoaded", 0);
|
|
809
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_startTime", 0);
|
|
810
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_endTime", 0);
|
|
811
|
+
defineNonEnumerableSignal(RequestLoadingState.prototype, "_lastPacketTime", 0);
|
|
812
|
+
/**
|
|
813
|
+
* The state of a request in the "pending"
|
|
814
|
+
* state. This is the default initial state.
|
|
815
|
+
*
|
|
816
|
+
* Extends the {@link PendingPromise} interface.
|
|
817
|
+
*
|
|
818
|
+
*/
|
|
819
|
+
/**
|
|
820
|
+
* The state of a request in the "fulfilled" state.
|
|
821
|
+
* This is the state of a request that has resolved
|
|
822
|
+
* successfully.
|
|
823
|
+
*
|
|
824
|
+
* Extends the {@link ResolvedPromise} interface.
|
|
825
|
+
*
|
|
826
|
+
*/
|
|
827
|
+
/**
|
|
828
|
+
* The state of a request in the "rejected" state.
|
|
829
|
+
* This is the state of a request that has rejected
|
|
830
|
+
* with an error.
|
|
831
|
+
*
|
|
832
|
+
* Extends the {@link RejectedPromise} interface.
|
|
833
|
+
*
|
|
834
|
+
*/
|
|
835
|
+
/**
|
|
836
|
+
* The state of a request in the "cancelled" state.
|
|
837
|
+
* This is the state of a promise that has been
|
|
838
|
+
* cancelled.
|
|
839
|
+
*
|
|
840
|
+
*/
|
|
841
|
+
/**
|
|
842
|
+
* RequestState extends the concept of {@link PromiseState} to provide a reactive
|
|
843
|
+
* wrapper for a request {@link Future} which allows you write declarative code
|
|
844
|
+
* around a Future's control flow.
|
|
845
|
+
*
|
|
846
|
+
* It is useful in both Template and JavaScript contexts, allowing you
|
|
847
|
+
* to quickly derive behaviors and data from pending, error and success
|
|
848
|
+
* states.
|
|
849
|
+
*
|
|
850
|
+
* The key difference between a {@link Promise} and a Future is that Futures provide
|
|
851
|
+
* access to a {@link ReadableStream | stream} of their content, the {@link RequestKey} of the request (if any)
|
|
852
|
+
* as well as the ability to attempt to {@link Future.abort | abort} the request.
|
|
853
|
+
*
|
|
854
|
+
* ```ts
|
|
855
|
+
* interface Future<T> extends Promise<T>> {
|
|
856
|
+
* getStream(): Promise<ReadableStream>;
|
|
857
|
+
* abort(): void;
|
|
858
|
+
* lid: RequestKey | null;
|
|
859
|
+
* }
|
|
860
|
+
* ```
|
|
861
|
+
*
|
|
862
|
+
* These additional APIs allow us to craft even richer state experiences.
|
|
863
|
+
*
|
|
864
|
+
* To get the state of a request, use {@link getRequestState}.
|
|
865
|
+
*
|
|
866
|
+
* See also:
|
|
867
|
+
* - {@link PendingRequest}
|
|
868
|
+
* - {@link ResolvedRequest}
|
|
869
|
+
* - {@link RejectedRequest}
|
|
870
|
+
* - {@link CancelledRequest}
|
|
871
|
+
*
|
|
872
|
+
*/
|
|
873
|
+
const RequestStateProto = {};
|
|
874
|
+
function performRefresh(requester, request, isReload) {
|
|
875
|
+
const req = Object.assign({}, request);
|
|
876
|
+
const cacheOptions = Object.assign({}, req.cacheOptions);
|
|
877
|
+
if (isReload) cacheOptions.reload = true;
|
|
878
|
+
else if (isReload === false) {
|
|
879
|
+
delete cacheOptions.reload;
|
|
880
|
+
cacheOptions.backgroundReload = true;
|
|
881
|
+
} else {
|
|
882
|
+
delete cacheOptions.backgroundReload;
|
|
883
|
+
delete cacheOptions.reload;
|
|
884
|
+
}
|
|
885
|
+
req.cacheOptions = cacheOptions;
|
|
886
|
+
return requester.request(req);
|
|
887
|
+
}
|
|
888
|
+
defineSignal(RequestStateProto, "reason", null);
|
|
889
|
+
defineSignal(RequestStateProto, "value", null);
|
|
890
|
+
defineSignal(RequestStateProto, "result", null);
|
|
891
|
+
defineSignal(RequestStateProto, "error", null);
|
|
892
|
+
defineSignal(RequestStateProto, "status", "pending");
|
|
893
|
+
defineSignal(RequestStateProto, "isPending", true);
|
|
894
|
+
defineSignal(RequestStateProto, "isLoading", true);
|
|
895
|
+
defineSignal(RequestStateProto, "isSuccess", false);
|
|
896
|
+
defineSignal(RequestStateProto, "isError", false);
|
|
897
|
+
defineSignal(RequestStateProto, "request", null);
|
|
898
|
+
defineSignal(RequestStateProto, "response", null);
|
|
899
|
+
Object.defineProperty(RequestStateProto, "isCancelled", { get() {
|
|
900
|
+
return this.isError && isAbortError(this.reason);
|
|
901
|
+
} });
|
|
902
|
+
Object.defineProperty(RequestStateProto, "loadingState", { get() {
|
|
903
|
+
if (!this._loadingState) this._loadingState = new RequestLoadingState(this._request);
|
|
904
|
+
return this._loadingState;
|
|
905
|
+
} });
|
|
906
|
+
function createRequestState(future) {
|
|
907
|
+
const state = getPromiseResult(future);
|
|
908
|
+
const promiseState = Object.create(RequestStateProto);
|
|
909
|
+
promiseState._request = future;
|
|
910
|
+
promiseState.reload = () => {
|
|
911
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
912
|
+
if (!test) throw new Error(`Cannot reload a request that is still pending. Await or abort the original request first.`);
|
|
913
|
+
})(!promiseState.isPending);
|
|
914
|
+
return performRefresh(future.requester, promiseState.request, true);
|
|
915
|
+
};
|
|
916
|
+
promiseState.refresh = (usePolicy = false) => {
|
|
917
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
918
|
+
if (!test) throw new Error(`Cannot refresh a request that is still pending. Await or abort the original request first.`);
|
|
919
|
+
})(!promiseState.isPending);
|
|
920
|
+
return performRefresh(future.requester, promiseState.request, usePolicy === true ? null : false);
|
|
921
|
+
};
|
|
922
|
+
if (state) {
|
|
923
|
+
if (state.isError) {
|
|
924
|
+
promiseState.error = state.result;
|
|
925
|
+
promiseState.reason = state.result;
|
|
926
|
+
promiseState.status = "rejected";
|
|
927
|
+
promiseState.isError = true;
|
|
928
|
+
promiseState.isPending = false;
|
|
929
|
+
promiseState.isLoading = false;
|
|
930
|
+
promiseState.request = state.result.request;
|
|
931
|
+
promiseState.response = state.result.response;
|
|
932
|
+
} else {
|
|
933
|
+
promiseState.result = state.result.content;
|
|
934
|
+
promiseState.value = state.result.content;
|
|
935
|
+
promiseState.status = "fulfilled";
|
|
936
|
+
promiseState.isSuccess = true;
|
|
937
|
+
promiseState.isPending = false;
|
|
938
|
+
promiseState.isLoading = false;
|
|
939
|
+
promiseState.request = state.result.request;
|
|
940
|
+
promiseState.response = state.result.response;
|
|
941
|
+
}
|
|
942
|
+
} else future.then((result) => {
|
|
943
|
+
setPromiseResult(future, {
|
|
944
|
+
isError: false,
|
|
945
|
+
result
|
|
946
|
+
});
|
|
947
|
+
promiseState.result = result.content;
|
|
948
|
+
promiseState.value = result.content;
|
|
949
|
+
promiseState.status = "fulfilled";
|
|
950
|
+
promiseState.isSuccess = true;
|
|
951
|
+
promiseState.isPending = false;
|
|
952
|
+
promiseState.isLoading = false;
|
|
953
|
+
promiseState.request = result.request;
|
|
954
|
+
promiseState.response = result.response;
|
|
955
|
+
}, (error) => {
|
|
956
|
+
setPromiseResult(future, {
|
|
957
|
+
isError: true,
|
|
958
|
+
result: error
|
|
959
|
+
});
|
|
960
|
+
promiseState.error = error;
|
|
961
|
+
promiseState.reason = error;
|
|
962
|
+
promiseState.status = "rejected";
|
|
963
|
+
promiseState.isError = true;
|
|
964
|
+
promiseState.isPending = false;
|
|
965
|
+
promiseState.isLoading = false;
|
|
966
|
+
promiseState.request = error.request;
|
|
967
|
+
promiseState.response = error.response;
|
|
968
|
+
});
|
|
969
|
+
return promiseState;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* `getRequestState` can be used in both JavaScript and Template contexts.
|
|
973
|
+
*
|
|
974
|
+
* ```ts
|
|
975
|
+
* import { getRequestState } from '@warp-drive-mirror/ember';
|
|
976
|
+
*
|
|
977
|
+
* const state = getRequestState(future);
|
|
978
|
+
* ```
|
|
979
|
+
*
|
|
980
|
+
* For instance, we could write a getter on a component that updates whenever
|
|
981
|
+
* the request state advances or the future changes, by combining the function
|
|
982
|
+
* with the use of `@cached`
|
|
983
|
+
*
|
|
984
|
+
* ```ts
|
|
985
|
+
* class Component {
|
|
986
|
+
* @cached
|
|
987
|
+
* get title() {
|
|
988
|
+
* const state = getRequestState(this.args.request);
|
|
989
|
+
* if (state.isPending) {
|
|
990
|
+
* return 'loading...';
|
|
991
|
+
* }
|
|
992
|
+
* if (state.isError) { return null; }
|
|
993
|
+
* return state.result.title;
|
|
994
|
+
* }
|
|
995
|
+
* }
|
|
996
|
+
* ```
|
|
997
|
+
*
|
|
998
|
+
* Or in a template as a helper:
|
|
999
|
+
*
|
|
1000
|
+
* ```gjs
|
|
1001
|
+
* import { getRequestState } from '@warp-drive-mirror/ember';
|
|
1002
|
+
*
|
|
1003
|
+
* <template>
|
|
1004
|
+
* {{#let (getRequestState @request) as |state|}}
|
|
1005
|
+
* {{#if state.isPending}}
|
|
1006
|
+
* <Spinner />
|
|
1007
|
+
* {{else if state.isError}}
|
|
1008
|
+
* <ErrorForm @error={{state.error}} />
|
|
1009
|
+
* {{else}}
|
|
1010
|
+
* <h1>{{state.result.title}}</h1>
|
|
1011
|
+
* {{/if}}
|
|
1012
|
+
* {{/let}}
|
|
1013
|
+
* </template>
|
|
1014
|
+
* ```
|
|
1015
|
+
*
|
|
1016
|
+
* If looking to use in a template, consider also the `<Request />` component
|
|
1017
|
+
* which offers a number of additional capabilities for requests *beyond* what
|
|
1018
|
+
* `RequestState` provides.
|
|
1019
|
+
*
|
|
1020
|
+
*/
|
|
1021
|
+
function getRequestState(future) {
|
|
1022
|
+
let state = RequestCache.get(future);
|
|
1023
|
+
if (!state) {
|
|
1024
|
+
state = createRequestState(future);
|
|
1025
|
+
RequestCache.set(future, state);
|
|
1026
|
+
}
|
|
1027
|
+
return state;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
//#endregion
|
|
1031
|
+
//#region ../../node_modules/.pnpm/decorat_5d4eca388601a94e7fbc656401b751d1/node_modules/decorator-transforms/dist/runtime--fcdnjmJ.js
|
|
1032
|
+
const deferred = /* @__PURE__ */ new WeakMap();
|
|
1033
|
+
function deferDecorator(proto, prop, desc) {
|
|
1034
|
+
let map = deferred.get(proto);
|
|
1035
|
+
if (!map) {
|
|
1036
|
+
map = /* @__PURE__ */ new Map();
|
|
1037
|
+
deferred.set(proto, map);
|
|
1038
|
+
}
|
|
1039
|
+
map.set(prop, desc);
|
|
1040
|
+
}
|
|
1041
|
+
function findDeferredDecorator(target, prop) {
|
|
1042
|
+
var _a;
|
|
1043
|
+
let cursor = target.prototype;
|
|
1044
|
+
while (cursor) {
|
|
1045
|
+
let desc = (_a = deferred.get(cursor)) == null ? void 0 : _a.get(prop);
|
|
1046
|
+
if (desc) return desc;
|
|
1047
|
+
cursor = Object.getPrototypeOf(cursor);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function decorateFieldV2(prototype, prop, decorators, initializer) {
|
|
1051
|
+
let desc = {
|
|
1052
|
+
configurable: true,
|
|
1053
|
+
enumerable: true,
|
|
1054
|
+
writable: true,
|
|
1055
|
+
initializer: null
|
|
1056
|
+
};
|
|
1057
|
+
if (initializer) desc.initializer = initializer;
|
|
1058
|
+
for (let decorator of decorators) desc = decorator(prototype, prop, desc) || desc;
|
|
1059
|
+
if (desc.initializer === void 0) Object.defineProperty(prototype, prop, desc);
|
|
1060
|
+
else deferDecorator(prototype, prop, desc);
|
|
1061
|
+
}
|
|
1062
|
+
function decorateMethodV2(prototype, prop, decorators) {
|
|
1063
|
+
let desc = { ...Object.getOwnPropertyDescriptor(prototype, prop) };
|
|
1064
|
+
for (let decorator of decorators) desc = decorator(prototype, prop, desc) || desc;
|
|
1065
|
+
if (desc.initializer !== void 0) {
|
|
1066
|
+
desc.value = desc.initializer ? desc.initializer.call(prototype) : void 0;
|
|
1067
|
+
desc.initializer = void 0;
|
|
1068
|
+
}
|
|
1069
|
+
Object.defineProperty(prototype, prop, desc);
|
|
1070
|
+
}
|
|
1071
|
+
function initializeDeferredDecorator(target, prop) {
|
|
1072
|
+
let desc = findDeferredDecorator(target.constructor, prop);
|
|
1073
|
+
if (desc) Object.defineProperty(target, prop, {
|
|
1074
|
+
enumerable: desc.enumerable,
|
|
1075
|
+
configurable: desc.configurable,
|
|
1076
|
+
writable: desc.writable,
|
|
1077
|
+
value: desc.initializer ? desc.initializer.call(target) : void 0
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
//#endregion
|
|
1082
|
+
//#region src/signals/request-subscription.ts
|
|
1083
|
+
const DEFAULT_DEADLINE = 3e4;
|
|
1084
|
+
/**
|
|
1085
|
+
* A symbol used to dispose of a {@link RequestSubscription} when the
|
|
1086
|
+
* component or context using it is torn down.
|
|
1087
|
+
*/
|
|
1088
|
+
const DISPOSE = Symbol.dispose || Symbol.for("dispose");
|
|
1089
|
+
function isNeverString(val) {
|
|
1090
|
+
return val;
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* The individual autorefresh strategies a {@link RequestSubscription}
|
|
1094
|
+
* may combine, see {@link AutorefreshBehaviorCombos}.
|
|
1095
|
+
*/
|
|
1096
|
+
/**
|
|
1097
|
+
* The value accepted by {@link SubscriptionArgs.autorefresh}: either a
|
|
1098
|
+
* boolean, a single {@link AutorefreshBehaviorType}, or a comma-separated
|
|
1099
|
+
* combination of up to three of them.
|
|
1100
|
+
*/
|
|
1101
|
+
/**
|
|
1102
|
+
* Utilities to assist in recovering from the error.
|
|
1103
|
+
*/
|
|
1104
|
+
/** @deprecated use {@link RecoveryFeatures} */
|
|
1105
|
+
/**
|
|
1106
|
+
* Utilities for keeping the request fresh
|
|
1107
|
+
*/
|
|
1108
|
+
/**
|
|
1109
|
+
* The args accepted by the `<Request />` component.
|
|
1110
|
+
*/
|
|
1111
|
+
/**
|
|
1112
|
+
* The args accepted by a {@link RequestSubscription}.
|
|
1113
|
+
*/
|
|
1114
|
+
/**
|
|
1115
|
+
* A reactive class
|
|
1116
|
+
*
|
|
1117
|
+
* @hideconstructor
|
|
1118
|
+
*/
|
|
1119
|
+
var RequestSubscription = class {
|
|
1120
|
+
/**
|
|
1121
|
+
* Whether the browser reports that the network is online.
|
|
1122
|
+
*/
|
|
1123
|
+
/**
|
|
1124
|
+
* Whether the browser reports that the tab is hidden.
|
|
1125
|
+
*/
|
|
1126
|
+
/**
|
|
1127
|
+
* Whether the component is currently refreshing the request.
|
|
1128
|
+
*/
|
|
1129
|
+
/**
|
|
1130
|
+
* The most recent blocking request that was made, typically
|
|
1131
|
+
* the result of a reload.
|
|
1132
|
+
*
|
|
1133
|
+
* This will never be the original request passed as an arg to
|
|
1134
|
+
* the component.
|
|
1135
|
+
*
|
|
1136
|
+
* @internal
|
|
1137
|
+
*/
|
|
1138
|
+
/**
|
|
1139
|
+
* The most recent request that was made, typically due to either a
|
|
1140
|
+
* reload or a refresh.
|
|
1141
|
+
*
|
|
1142
|
+
* This will never be the original request passed as an arg to
|
|
1143
|
+
* the component.
|
|
1144
|
+
*
|
|
1145
|
+
* @internal
|
|
1146
|
+
*/
|
|
1147
|
+
/**
|
|
1148
|
+
* The time at which the network was reported as offline.
|
|
1149
|
+
*
|
|
1150
|
+
* @internal
|
|
1151
|
+
*/
|
|
1152
|
+
/** @internal */
|
|
1153
|
+
/** @internal */
|
|
1154
|
+
/** @internal */
|
|
1155
|
+
/** @internal */
|
|
1156
|
+
/** @internal */
|
|
1157
|
+
/**
|
|
1158
|
+
* The event listener for network status changes,
|
|
1159
|
+
* cached to use the reference for removal.
|
|
1160
|
+
*
|
|
1161
|
+
* @internal
|
|
1162
|
+
*/
|
|
1163
|
+
/**
|
|
1164
|
+
* The event listener for visibility status changes,
|
|
1165
|
+
* cached to use the reference for removal.
|
|
1166
|
+
*
|
|
1167
|
+
* @internal
|
|
1168
|
+
*/
|
|
1169
|
+
/**
|
|
1170
|
+
* The last request passed as an arg to the component,
|
|
1171
|
+
* cached for comparison.
|
|
1172
|
+
*
|
|
1173
|
+
* @internal
|
|
1174
|
+
*/
|
|
1175
|
+
/**
|
|
1176
|
+
* The last query passed as an arg to the component,
|
|
1177
|
+
* cached for comparison.
|
|
1178
|
+
*
|
|
1179
|
+
* @internal
|
|
1180
|
+
*/
|
|
1181
|
+
/** @internal */
|
|
1182
|
+
/** @internal */
|
|
1183
|
+
/** @internal */
|
|
1184
|
+
/**
|
|
1185
|
+
* The Store this subscription subscribes to or the RequestManager
|
|
1186
|
+
* which issues this request.
|
|
1187
|
+
*/
|
|
1188
|
+
/**
|
|
1189
|
+
* The Store or RequestManager that the last subscription is attached to.
|
|
1190
|
+
*
|
|
1191
|
+
* This differs from 'store' because a <Request /> may be passed a
|
|
1192
|
+
* request originating from a different store than the <Request />
|
|
1193
|
+
* component would use if it were to issue the request itself.
|
|
1194
|
+
*
|
|
1195
|
+
* @internal
|
|
1196
|
+
*/
|
|
1197
|
+
_requester;
|
|
1198
|
+
constructor(store, args) {
|
|
1199
|
+
this._args = args;
|
|
1200
|
+
this.store = store;
|
|
1201
|
+
this._subscribedTo = null;
|
|
1202
|
+
this._subscription = null;
|
|
1203
|
+
this._intervalStart = null;
|
|
1204
|
+
this._invalidated = false;
|
|
1205
|
+
this._nextInterval = null;
|
|
1206
|
+
this._requester = null;
|
|
1207
|
+
this.isDestroyed = false;
|
|
1208
|
+
this[DISPOSE] = _DISPOSE$2;
|
|
1209
|
+
this._installListeners();
|
|
1210
|
+
this._beginPolling();
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* @internal
|
|
1214
|
+
*/
|
|
1215
|
+
async _beginPolling() {
|
|
1216
|
+
try {
|
|
1217
|
+
if (!this.isIdle) await this.request;
|
|
1218
|
+
} catch {} finally {
|
|
1219
|
+
if (!this.isDestroyed) this._scheduleInterval();
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Whether neither a `request` nor a `query` arg was provided, and so
|
|
1224
|
+
* this subscription has nothing to fetch or monitor.
|
|
1225
|
+
*/
|
|
1226
|
+
get isIdle() {
|
|
1227
|
+
const { request, query } = this._args;
|
|
1228
|
+
return Boolean(!request && !query);
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* The set of {@link AutorefreshBehaviorType}s this subscription is
|
|
1232
|
+
* configured to autorefresh for, derived from {@link SubscriptionArgs.autorefresh}.
|
|
1233
|
+
*/
|
|
1234
|
+
static {
|
|
1235
|
+
decorateMethodV2(this.prototype, "isIdle", [memoized]);
|
|
1236
|
+
}
|
|
1237
|
+
get autorefreshTypes() {
|
|
1238
|
+
const { autorefresh } = this._args;
|
|
1239
|
+
let types;
|
|
1240
|
+
if (autorefresh === true) types = ["online", "invalid"];
|
|
1241
|
+
else if (typeof autorefresh === "string") types = autorefresh.split(",");
|
|
1242
|
+
else types = [];
|
|
1243
|
+
return new Set(types);
|
|
1244
|
+
}
|
|
1245
|
+
/** @internal */
|
|
1246
|
+
static {
|
|
1247
|
+
decorateMethodV2(this.prototype, "autorefreshTypes", [memoized]);
|
|
1248
|
+
}
|
|
1249
|
+
async _scheduleInterval() {
|
|
1250
|
+
const { autorefreshThreshold } = this._args;
|
|
1251
|
+
if (typeof window === "undefined" || !(typeof autorefreshThreshold === "number" && autorefreshThreshold > 0) || !this.autorefreshTypes.has("interval") || this._intervalStart !== null) return;
|
|
1252
|
+
if (this._latestRequest) {
|
|
1253
|
+
try {
|
|
1254
|
+
await this._latestRequest;
|
|
1255
|
+
} catch {}
|
|
1256
|
+
if (this.isDestroyed) return;
|
|
1257
|
+
}
|
|
1258
|
+
this._intervalStart = Date.now();
|
|
1259
|
+
this._nextInterval = setTimeout(() => {
|
|
1260
|
+
this._maybeUpdate();
|
|
1261
|
+
}, autorefreshThreshold);
|
|
1262
|
+
}
|
|
1263
|
+
/** @internal */
|
|
1264
|
+
_clearInterval() {
|
|
1265
|
+
if (this._nextInterval) {
|
|
1266
|
+
clearTimeout(this._nextInterval);
|
|
1267
|
+
this._intervalStart = null;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* @internal
|
|
1272
|
+
*/
|
|
1273
|
+
_updateSubscriptions() {
|
|
1274
|
+
if (this.isIdle) return;
|
|
1275
|
+
const requestId = this._request.lid;
|
|
1276
|
+
if (this._subscribedTo === requestId) return;
|
|
1277
|
+
this._removeSubscriptions();
|
|
1278
|
+
const store = this._getRequester();
|
|
1279
|
+
this._requester = store;
|
|
1280
|
+
if (requestId && isStore(store)) {
|
|
1281
|
+
this._subscribedTo = requestId;
|
|
1282
|
+
this._subscription = store.notifications.subscribe(requestId, (_id, op) => {
|
|
1283
|
+
if (this._isUpdating) return;
|
|
1284
|
+
switch (op) {
|
|
1285
|
+
case "invalidated":
|
|
1286
|
+
if (this.autorefreshTypes.has("invalid")) {
|
|
1287
|
+
this._invalidated = true;
|
|
1288
|
+
this._maybeUpdate();
|
|
1289
|
+
}
|
|
1290
|
+
break;
|
|
1291
|
+
case "state": {
|
|
1292
|
+
const priority = store.requestManager._deduped.get(requestId)?.priority;
|
|
1293
|
+
const state = this.reqState;
|
|
1294
|
+
if (!priority) this.isRefreshing = false;
|
|
1295
|
+
else if (priority.blocking && !state.isLoading) {
|
|
1296
|
+
this.isRefreshing = false;
|
|
1297
|
+
this._maybeUpdate("policy", true);
|
|
1298
|
+
} else this.isRefreshing = true;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* @internal
|
|
1306
|
+
*/
|
|
1307
|
+
_removeSubscriptions() {
|
|
1308
|
+
const store = this._requester;
|
|
1309
|
+
if (this._subscription && store && isStore(store)) {
|
|
1310
|
+
store.notifications.unsubscribe(this._subscription);
|
|
1311
|
+
this._subscribedTo = null;
|
|
1312
|
+
this._subscription = null;
|
|
1313
|
+
this._requester = null;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Install the event listeners for network and visibility changes.
|
|
1318
|
+
*
|
|
1319
|
+
* This is only done in environments that provide a fully-featured global
|
|
1320
|
+
* `window` and/or `document`. Some non-DOM environments (e.g. React Native,
|
|
1321
|
+
* SSR/Node, workers) may provide a `window` global without the APIs we
|
|
1322
|
+
* rely on here (or no `window`/`document` at all), in which case we skip
|
|
1323
|
+
* attaching the corresponding listeners rather than throwing.
|
|
1324
|
+
*
|
|
1325
|
+
* @internal
|
|
1326
|
+
*/
|
|
1327
|
+
_installListeners() {
|
|
1328
|
+
const hasWindow = typeof window !== "undefined";
|
|
1329
|
+
const hasDocument = typeof document !== "undefined";
|
|
1330
|
+
const hasWindowEvents = hasWindow && typeof window.addEventListener === "function";
|
|
1331
|
+
const hasDocumentEvents = hasDocument && typeof document.addEventListener === "function";
|
|
1332
|
+
this.isOnline = hasWindow && typeof window.navigator?.onLine === "boolean" ? window.navigator.onLine : true;
|
|
1333
|
+
this._unavailableStart = this.isOnline ? null : Date.now();
|
|
1334
|
+
this.isHidden = hasDocument && typeof document.visibilityState === "string" && document.visibilityState === "hidden";
|
|
1335
|
+
if (hasWindowEvents) {
|
|
1336
|
+
this._onlineChanged = (event) => {
|
|
1337
|
+
this.isOnline = event.type === "online";
|
|
1338
|
+
if (event.type === "offline" && this._unavailableStart === null) this._unavailableStart = Date.now();
|
|
1339
|
+
this._maybeUpdate();
|
|
1340
|
+
};
|
|
1341
|
+
window.addEventListener("online", this._onlineChanged, {
|
|
1342
|
+
passive: true,
|
|
1343
|
+
capture: true
|
|
1344
|
+
});
|
|
1345
|
+
window.addEventListener("offline", this._onlineChanged, {
|
|
1346
|
+
passive: true,
|
|
1347
|
+
capture: true
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
if (hasDocumentEvents) {
|
|
1351
|
+
this._backgroundChanged = () => {
|
|
1352
|
+
const isHidden = document.visibilityState === "hidden";
|
|
1353
|
+
this.isHidden = isHidden;
|
|
1354
|
+
if (isHidden && this._unavailableStart === null) this._unavailableStart = Date.now();
|
|
1355
|
+
this._maybeUpdate();
|
|
1356
|
+
};
|
|
1357
|
+
document.addEventListener("visibilitychange", this._backgroundChanged, {
|
|
1358
|
+
passive: true,
|
|
1359
|
+
capture: true
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* If the network is online and the tab is visible, either reload or refresh the request
|
|
1365
|
+
* based on the component's configuration and the requested update mode.
|
|
1366
|
+
*
|
|
1367
|
+
* Valid modes are:
|
|
1368
|
+
*
|
|
1369
|
+
* - `'reload'`: Force a reload of the request.
|
|
1370
|
+
* - `'refresh'`: Refresh the request in the background.
|
|
1371
|
+
* - `'policy'`: Make the request, letting the store's configured CachePolicy decide whether to reload, refresh, or do nothing.
|
|
1372
|
+
* - `undefined`: Make the request using the component's autorefreshBehavior setting if the autorefreshThreshold has passed.
|
|
1373
|
+
*
|
|
1374
|
+
* @internal
|
|
1375
|
+
*/
|
|
1376
|
+
_maybeUpdate(mode, silent) {
|
|
1377
|
+
if (this.isIdle) return;
|
|
1378
|
+
const { reqState } = this;
|
|
1379
|
+
if (reqState.isPending) return;
|
|
1380
|
+
if (!Boolean(this.isOnline && !this.isHidden && (mode || this.autorefreshTypes.size))) {
|
|
1381
|
+
if (!silent && mode && mode !== "_invalidated") throw new Error(`Reload not available: the network is not online or the tab is hidden`);
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
const { autorefreshTypes } = this;
|
|
1385
|
+
let shouldAttempt = this._invalidated || Boolean(mode);
|
|
1386
|
+
if (!shouldAttempt && autorefreshTypes.has("online")) {
|
|
1387
|
+
const { _unavailableStart } = this;
|
|
1388
|
+
const { autorefreshThreshold } = this._args;
|
|
1389
|
+
const deadline = typeof autorefreshThreshold === "number" ? autorefreshThreshold : DEFAULT_DEADLINE;
|
|
1390
|
+
shouldAttempt = Boolean(_unavailableStart && Date.now() - _unavailableStart > deadline);
|
|
1391
|
+
}
|
|
1392
|
+
if (!shouldAttempt && autorefreshTypes.has("interval")) {
|
|
1393
|
+
const { _intervalStart } = this;
|
|
1394
|
+
const { autorefreshThreshold } = this._args;
|
|
1395
|
+
if (_intervalStart && typeof autorefreshThreshold === "number" && autorefreshThreshold > 0) shouldAttempt = Boolean(Date.now() - _intervalStart >= autorefreshThreshold);
|
|
1396
|
+
}
|
|
1397
|
+
this._unavailableStart = null;
|
|
1398
|
+
this._invalidated = false;
|
|
1399
|
+
if (shouldAttempt) {
|
|
1400
|
+
this._clearInterval();
|
|
1401
|
+
this._isUpdating = true;
|
|
1402
|
+
const val = (mode === "_invalidated" ? null : mode) ?? this._args.autorefreshBehavior ?? "policy";
|
|
1403
|
+
const requester = this._getRequester();
|
|
1404
|
+
if (!reqState._request.requester) reqState._request.requester = requester;
|
|
1405
|
+
switch (val) {
|
|
1406
|
+
case "reload":
|
|
1407
|
+
this._latestRequest = reqState.reload();
|
|
1408
|
+
break;
|
|
1409
|
+
case "refresh":
|
|
1410
|
+
this._latestRequest = reqState.refresh();
|
|
1411
|
+
break;
|
|
1412
|
+
case "policy":
|
|
1413
|
+
this._latestRequest = reqState.refresh(true);
|
|
1414
|
+
break;
|
|
1415
|
+
default: macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1416
|
+
if (!test) throw new Error(`Invalid ${mode ? "update mode" : "@autorefreshBehavior"} for <Request />: ${isNeverString(val)}`);
|
|
1417
|
+
})(false);
|
|
1418
|
+
}
|
|
1419
|
+
if (val !== "refresh") this._localRequest = this._latestRequest;
|
|
1420
|
+
this._scheduleInterval();
|
|
1421
|
+
this._latestRequest.finally(() => {
|
|
1422
|
+
this._isUpdating = false;
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* @internal
|
|
1428
|
+
*/
|
|
1429
|
+
_getRequester() {
|
|
1430
|
+
if (this._args.request?.requester) return this._args.request.requester;
|
|
1431
|
+
return this.store;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Retry the request, reloading it from the server.
|
|
1435
|
+
*/
|
|
1436
|
+
retry = async () => {
|
|
1437
|
+
this._maybeUpdate("reload");
|
|
1438
|
+
await this._localRequest;
|
|
1439
|
+
};
|
|
1440
|
+
/**
|
|
1441
|
+
* Refresh the request, updating it in the background.
|
|
1442
|
+
*/
|
|
1443
|
+
refresh = async () => {
|
|
1444
|
+
this._maybeUpdate("refresh");
|
|
1445
|
+
await this._latestRequest;
|
|
1446
|
+
};
|
|
1447
|
+
/**
|
|
1448
|
+
* features to yield to the error slot of a component
|
|
1449
|
+
*/
|
|
1450
|
+
get errorFeatures() {
|
|
1451
|
+
return {
|
|
1452
|
+
isHidden: this.isHidden,
|
|
1453
|
+
isOnline: this.isOnline,
|
|
1454
|
+
retry: this.retry
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* features to yield to the content slot of a component
|
|
1459
|
+
*/
|
|
1460
|
+
static {
|
|
1461
|
+
decorateMethodV2(this.prototype, "errorFeatures", [memoized]);
|
|
1462
|
+
}
|
|
1463
|
+
get contentFeatures() {
|
|
1464
|
+
const feat = {
|
|
1465
|
+
isHidden: this.isHidden,
|
|
1466
|
+
isOnline: this.isOnline,
|
|
1467
|
+
reload: this.retry,
|
|
1468
|
+
refresh: this.refresh,
|
|
1469
|
+
isRefreshing: this.isRefreshing,
|
|
1470
|
+
latestRequest: this._latestRequest
|
|
1471
|
+
};
|
|
1472
|
+
if (feat.isRefreshing) feat.abort = () => {
|
|
1473
|
+
this._latestRequest?.abort();
|
|
1474
|
+
};
|
|
1475
|
+
return feat;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* @internal
|
|
1479
|
+
*/
|
|
1480
|
+
static {
|
|
1481
|
+
decorateMethodV2(this.prototype, "contentFeatures", [memoized]);
|
|
1482
|
+
}
|
|
1483
|
+
get _request() {
|
|
1484
|
+
const { request, query } = this._args;
|
|
1485
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1486
|
+
if (!test) throw new Error(`Cannot use both @request and @query args with the <Request> component`);
|
|
1487
|
+
})(!request || !query);
|
|
1488
|
+
const { _localRequest, _originalRequest, _originalQuery } = this;
|
|
1489
|
+
if (_localRequest && request === _originalRequest && query === _originalQuery) return _localRequest;
|
|
1490
|
+
this._originalQuery = query;
|
|
1491
|
+
this._originalRequest = request;
|
|
1492
|
+
if (request) return request;
|
|
1493
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1494
|
+
if (!test) throw new Error(`You must provide either @request or an @query arg with the <Request> component`);
|
|
1495
|
+
})(query);
|
|
1496
|
+
return this.store.request(query);
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* The {@link Future} this subscription is currently monitoring, and
|
|
1500
|
+
* (re-)subscribes to notifications for as a side effect of access.
|
|
1501
|
+
*/
|
|
1502
|
+
static {
|
|
1503
|
+
decorateMethodV2(this.prototype, "_request", [memoized]);
|
|
1504
|
+
}
|
|
1505
|
+
get request() {
|
|
1506
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) try {
|
|
1507
|
+
const request = this._request;
|
|
1508
|
+
this._updateSubscriptions();
|
|
1509
|
+
return request;
|
|
1510
|
+
} catch (e) {
|
|
1511
|
+
console.log(e);
|
|
1512
|
+
throw new Error(`Unable to initialize the request`, { cause: e });
|
|
1513
|
+
}
|
|
1514
|
+
else {
|
|
1515
|
+
const request = this._request;
|
|
1516
|
+
this._updateSubscriptions();
|
|
1517
|
+
return request;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* The {@link RequestState} for {@link RequestSubscription.request | request}.
|
|
1522
|
+
*/
|
|
1523
|
+
static {
|
|
1524
|
+
decorateMethodV2(this.prototype, "request", [memoized]);
|
|
1525
|
+
}
|
|
1526
|
+
get reqState() {
|
|
1527
|
+
return getRequestState(this.request);
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* The resolved content of the request, once it has succeeded.
|
|
1531
|
+
*/
|
|
1532
|
+
get result() {
|
|
1533
|
+
return this.reqState.result;
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
defineSignal(RequestSubscription.prototype, "isOnline", true);
|
|
1537
|
+
defineSignal(RequestSubscription.prototype, "isHidden", false);
|
|
1538
|
+
defineSignal(RequestSubscription.prototype, "isRefreshing", false);
|
|
1539
|
+
defineSignal(RequestSubscription.prototype, "_localRequest", void 0);
|
|
1540
|
+
defineSignal(RequestSubscription.prototype, "_latestRequest", void 0);
|
|
1541
|
+
function isStore(store) {
|
|
1542
|
+
return "requestManager" in store;
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Creates a {@link RequestSubscription}, the reactive class powering the
|
|
1546
|
+
* `<Request />` component's autorefresh, retry, and refresh behaviors.
|
|
1547
|
+
*/
|
|
1548
|
+
function createRequestSubscription(store, args) {
|
|
1549
|
+
return new RequestSubscription(store, args);
|
|
1550
|
+
}
|
|
1551
|
+
function upgradeSubscription$2(sub) {
|
|
1552
|
+
return sub;
|
|
1553
|
+
}
|
|
1554
|
+
function _DISPOSE$2() {
|
|
1555
|
+
const self = upgradeSubscription$2(this);
|
|
1556
|
+
self.isDestroyed = true;
|
|
1557
|
+
self._removeSubscriptions();
|
|
1558
|
+
self._clearInterval();
|
|
1559
|
+
if (self._onlineChanged && typeof window !== "undefined" && typeof window.removeEventListener === "function") {
|
|
1560
|
+
window.removeEventListener("online", self._onlineChanged, {
|
|
1561
|
+
passive: true,
|
|
1562
|
+
capture: true
|
|
1563
|
+
});
|
|
1564
|
+
window.removeEventListener("offline", self._onlineChanged, {
|
|
1565
|
+
passive: true,
|
|
1566
|
+
capture: true
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
if (self._backgroundChanged && typeof document !== "undefined" && typeof document.removeEventListener === "function") document.removeEventListener("visibilitychange", self._backgroundChanged, {
|
|
1570
|
+
passive: true,
|
|
1571
|
+
capture: true
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
//#endregion
|
|
1576
|
+
//#region src/signals/page-cache.ts
|
|
1577
|
+
const { abs } = Math;
|
|
1578
|
+
/**
|
|
1579
|
+
* Extracts the `data` member of a document type, or the document type itself
|
|
1580
|
+
* when it has no `data` member.
|
|
1581
|
+
*/
|
|
1582
|
+
/**
|
|
1583
|
+
* The element type of a collection document's `data` array — the item type an
|
|
1584
|
+
* infinite view renders. See {@link PaginationState.data}.
|
|
1585
|
+
*/
|
|
1586
|
+
/** @internal */
|
|
1587
|
+
function getHref(link) {
|
|
1588
|
+
if (!link) return null;
|
|
1589
|
+
if (typeof link === "string") return link;
|
|
1590
|
+
return link.href;
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* A single page of a paginated collection: its request, its loaded document, and
|
|
1594
|
+
* its links to neighboring pages in the shared page graph.
|
|
1595
|
+
*
|
|
1596
|
+
* Pages are yielded to consumers as `Readonly<PageCache>` — through
|
|
1597
|
+
* {@link PaginationState.activePage}, {@link PaginationState.pages}, and the
|
|
1598
|
+
* relational getters below — to read a page's data and request status when
|
|
1599
|
+
* building pagination UIs:
|
|
1600
|
+
*
|
|
1601
|
+
* ```ts
|
|
1602
|
+
* const page = paginationState.activePage;
|
|
1603
|
+
*
|
|
1604
|
+
* if (page?.isLoading) {
|
|
1605
|
+
* // show a spinner
|
|
1606
|
+
* } else if (page?.isError) {
|
|
1607
|
+
* // show page.reason
|
|
1608
|
+
* } else {
|
|
1609
|
+
* // render page.data
|
|
1610
|
+
* }
|
|
1611
|
+
* ```
|
|
1612
|
+
*
|
|
1613
|
+
* @since 5.9.0
|
|
1614
|
+
* @public
|
|
1615
|
+
* @hideconstructor
|
|
1616
|
+
*/
|
|
1617
|
+
var PageCache = class {
|
|
1618
|
+
static {
|
|
1619
|
+
decorateFieldV2(this.prototype, "request", [signal]);
|
|
1620
|
+
}
|
|
1621
|
+
#request = (initializeDeferredDecorator(this, "request"), void 0);
|
|
1622
|
+
/** @internal */
|
|
1623
|
+
/**
|
|
1624
|
+
* The request that loaded (or is loading) this page, or `null` if the page is
|
|
1625
|
+
* known from links but was never requested. Wrap it in a `<Request>` component
|
|
1626
|
+
* to render the page's loading, error, and content states.
|
|
1627
|
+
*/
|
|
1628
|
+
static {
|
|
1629
|
+
decorateFieldV2(this.prototype, "state", [signal]);
|
|
1630
|
+
}
|
|
1631
|
+
#state = (initializeDeferredDecorator(this, "state"), void 0);
|
|
1632
|
+
/** @internal */
|
|
1633
|
+
static {
|
|
1634
|
+
decorateFieldV2(this.prototype, "selfLink", [signal]);
|
|
1635
|
+
}
|
|
1636
|
+
#selfLink = (initializeDeferredDecorator(this, "selfLink"), void 0);
|
|
1637
|
+
/** The `self` link of this page — the URL that identifies it in the collection. */
|
|
1638
|
+
static {
|
|
1639
|
+
decorateFieldV2(this.prototype, "prevLink", [signal]);
|
|
1640
|
+
}
|
|
1641
|
+
#prevLink = (initializeDeferredDecorator(this, "prevLink"), void 0);
|
|
1642
|
+
/** The `prev` link of this page, or `null` at the start of the collection. */
|
|
1643
|
+
static {
|
|
1644
|
+
decorateFieldV2(this.prototype, "nextLink", [signal]);
|
|
1645
|
+
}
|
|
1646
|
+
#nextLink = (initializeDeferredDecorator(this, "nextLink"), void 0);
|
|
1647
|
+
/** The `next` link of this page, or `null` at the end of the collection. */
|
|
1648
|
+
static {
|
|
1649
|
+
decorateFieldV2(this.prototype, "firstLink", [signal]);
|
|
1650
|
+
}
|
|
1651
|
+
#firstLink = (initializeDeferredDecorator(this, "firstLink"), void 0);
|
|
1652
|
+
/** The `first` link of the collection, when the response exposed one. */
|
|
1653
|
+
static {
|
|
1654
|
+
decorateFieldV2(this.prototype, "lastLink", [signal]);
|
|
1655
|
+
}
|
|
1656
|
+
#lastLink = (initializeDeferredDecorator(this, "lastLink"), void 0);
|
|
1657
|
+
/** The `last` link of the collection, when the response exposed one. */
|
|
1658
|
+
static {
|
|
1659
|
+
decorateFieldV2(this.prototype, "pageNumber", [signal]);
|
|
1660
|
+
}
|
|
1661
|
+
#pageNumber = (initializeDeferredDecorator(this, "pageNumber"), void 0);
|
|
1662
|
+
/**
|
|
1663
|
+
* The 1-based page number, or `0` when unknown (for example cursor-based
|
|
1664
|
+
* pagination, where pages have no ordinal position). Derived from the
|
|
1665
|
+
* collection's {@link PageHints}.
|
|
1666
|
+
*/
|
|
1667
|
+
static {
|
|
1668
|
+
decorateFieldV2(this.prototype, "before", [signal]);
|
|
1669
|
+
}
|
|
1670
|
+
#before = (initializeDeferredDecorator(this, "before"), void 0);
|
|
1671
|
+
/** @internal */
|
|
1672
|
+
static {
|
|
1673
|
+
decorateFieldV2(this.prototype, "after", [signal]);
|
|
1674
|
+
}
|
|
1675
|
+
#after = (initializeDeferredDecorator(this, "after"), void 0);
|
|
1676
|
+
/** @internal */
|
|
1677
|
+
constructor(manager, url) {
|
|
1678
|
+
this.manager = manager;
|
|
1679
|
+
this.pageNumber = 0;
|
|
1680
|
+
this.selfLink = url;
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* The document this page's request resolved to, or `null` while it has not
|
|
1684
|
+
* resolved.
|
|
1685
|
+
*/
|
|
1686
|
+
get value() {
|
|
1687
|
+
return this.state?.value;
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* The `data` member of the loaded document — the page's items — or `null`
|
|
1691
|
+
* while the page has not loaded.
|
|
1692
|
+
*/
|
|
1693
|
+
static {
|
|
1694
|
+
decorateMethodV2(this.prototype, "value", [memoized]);
|
|
1695
|
+
}
|
|
1696
|
+
get data() {
|
|
1697
|
+
return this.value?.data ?? null;
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Whether a request has ever been issued for this page. `false` for pages that
|
|
1701
|
+
* are known only from links.
|
|
1702
|
+
*/
|
|
1703
|
+
static {
|
|
1704
|
+
decorateMethodV2(this.prototype, "data", [memoized]);
|
|
1705
|
+
}
|
|
1706
|
+
get isRequested() {
|
|
1707
|
+
return Boolean(this.state);
|
|
1708
|
+
}
|
|
1709
|
+
/** Whether this page's request has settled (successfully or with an error). */
|
|
1710
|
+
static {
|
|
1711
|
+
decorateMethodV2(this.prototype, "isRequested", [memoized]);
|
|
1712
|
+
}
|
|
1713
|
+
get isLoaded() {
|
|
1714
|
+
return this.isSuccess || this.isError;
|
|
1715
|
+
}
|
|
1716
|
+
/** Whether this page's request is currently in flight. */
|
|
1717
|
+
static {
|
|
1718
|
+
decorateMethodV2(this.prototype, "isLoaded", [memoized]);
|
|
1719
|
+
}
|
|
1720
|
+
get isLoading() {
|
|
1721
|
+
return Boolean(this.state?.isLoading);
|
|
1722
|
+
}
|
|
1723
|
+
/** Whether this page's request resolved successfully. */
|
|
1724
|
+
static {
|
|
1725
|
+
decorateMethodV2(this.prototype, "isLoading", [memoized]);
|
|
1726
|
+
}
|
|
1727
|
+
get isSuccess() {
|
|
1728
|
+
return Boolean(this.state?.isSuccess);
|
|
1729
|
+
}
|
|
1730
|
+
/** Whether this page's request was cancelled (aborted). */
|
|
1731
|
+
static {
|
|
1732
|
+
decorateMethodV2(this.prototype, "isSuccess", [memoized]);
|
|
1733
|
+
}
|
|
1734
|
+
get isCancelled() {
|
|
1735
|
+
return Boolean(this.state?.isCancelled);
|
|
1736
|
+
}
|
|
1737
|
+
/** Whether this page's request rejected with an error. */
|
|
1738
|
+
static {
|
|
1739
|
+
decorateMethodV2(this.prototype, "isCancelled", [memoized]);
|
|
1740
|
+
}
|
|
1741
|
+
get isError() {
|
|
1742
|
+
return Boolean(this.state?.isError);
|
|
1743
|
+
}
|
|
1744
|
+
/** The error this page's request rejected with, or `null` if it did not reject. */
|
|
1745
|
+
static {
|
|
1746
|
+
decorateMethodV2(this.prototype, "isError", [memoized]);
|
|
1747
|
+
}
|
|
1748
|
+
get reason() {
|
|
1749
|
+
return this.state?.reason ?? null;
|
|
1750
|
+
}
|
|
1751
|
+
/** The page at this page's `prev` link, or `null` at the start of the collection. */
|
|
1752
|
+
static {
|
|
1753
|
+
decorateMethodV2(this.prototype, "reason", [memoized]);
|
|
1754
|
+
}
|
|
1755
|
+
get prev() {
|
|
1756
|
+
const url = this.prevLink;
|
|
1757
|
+
return url ? this.manager.getPageCache(url) : null;
|
|
1758
|
+
}
|
|
1759
|
+
/** The page at this page's `next` link, or `null` at the end of the collection. */
|
|
1760
|
+
static {
|
|
1761
|
+
decorateMethodV2(this.prototype, "prev", [memoized]);
|
|
1762
|
+
}
|
|
1763
|
+
get next() {
|
|
1764
|
+
const url = this.nextLink;
|
|
1765
|
+
return url ? this.manager.getPageCache(url) : null;
|
|
1766
|
+
}
|
|
1767
|
+
/** The first page of the collection, when the response exposed a `first` link. */
|
|
1768
|
+
static {
|
|
1769
|
+
decorateMethodV2(this.prototype, "next", [memoized]);
|
|
1770
|
+
}
|
|
1771
|
+
get first() {
|
|
1772
|
+
const url = this.firstLink;
|
|
1773
|
+
return url ? this.manager.getPageCache(url) : null;
|
|
1774
|
+
}
|
|
1775
|
+
/** The last page of the collection, when the response exposed a `last` link. */
|
|
1776
|
+
static {
|
|
1777
|
+
decorateMethodV2(this.prototype, "first", [memoized]);
|
|
1778
|
+
}
|
|
1779
|
+
get last() {
|
|
1780
|
+
const url = this.lastLink;
|
|
1781
|
+
return url ? this.manager.getPageCache(url) : null;
|
|
1782
|
+
}
|
|
1783
|
+
/** @internal */
|
|
1784
|
+
static {
|
|
1785
|
+
decorateMethodV2(this.prototype, "last", [memoized]);
|
|
1786
|
+
}
|
|
1787
|
+
get isLinked() {
|
|
1788
|
+
return Boolean(this.before || this.after);
|
|
1789
|
+
}
|
|
1790
|
+
/** @internal */
|
|
1791
|
+
static {
|
|
1792
|
+
decorateMethodV2(this.prototype, "isLinked", [memoized]);
|
|
1793
|
+
}
|
|
1794
|
+
async load(request) {
|
|
1795
|
+
try {
|
|
1796
|
+
this.request = request;
|
|
1797
|
+
this.state = getRequestState(this.request);
|
|
1798
|
+
const content = (await this.request).content;
|
|
1799
|
+
const self = getHref(content?.links?.self);
|
|
1800
|
+
const first = getHref(content?.links?.first);
|
|
1801
|
+
const last = getHref(content?.links?.last);
|
|
1802
|
+
const next = getHref(content?.links?.next);
|
|
1803
|
+
const prev = getHref(content?.links?.prev);
|
|
1804
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1805
|
+
if (!test) throw new Error("Expected the page to have a self link");
|
|
1806
|
+
})(self);
|
|
1807
|
+
const pageNumber = this.getPageNumber(content);
|
|
1808
|
+
if (pageNumber) this.pageNumber = pageNumber;
|
|
1809
|
+
const firstPage = first ? this.manager.getPageCache(first) : null;
|
|
1810
|
+
const lastPage = last ? this.manager.getPageCache(last) : null;
|
|
1811
|
+
const prevPage = prev ? this.manager.getPageCache(prev) : null;
|
|
1812
|
+
const nextPage = next ? this.manager.getPageCache(next) : null;
|
|
1813
|
+
if (firstPage) {
|
|
1814
|
+
this.firstLink = first;
|
|
1815
|
+
firstPage.setPageNumber(1);
|
|
1816
|
+
if (!firstPage.after) firstPage.updateLinkage({ after: this });
|
|
1817
|
+
}
|
|
1818
|
+
if (lastPage) {
|
|
1819
|
+
this.lastLink = last;
|
|
1820
|
+
lastPage.setPageNumber(this.manager.totalPages);
|
|
1821
|
+
if (!lastPage.before) lastPage.updateLinkage({ before: this });
|
|
1822
|
+
}
|
|
1823
|
+
if (nextPage) {
|
|
1824
|
+
this.nextLink = next;
|
|
1825
|
+
nextPage.setPageNumber(this.pageNumber + 1);
|
|
1826
|
+
nextPage.updateLinks({ prev: self });
|
|
1827
|
+
}
|
|
1828
|
+
if (prevPage) {
|
|
1829
|
+
this.prevLink = prev;
|
|
1830
|
+
prevPage.setPageNumber(this.pageNumber - 1);
|
|
1831
|
+
prevPage.updateLinks({ next: self });
|
|
1832
|
+
}
|
|
1833
|
+
if (this.isLinked) {
|
|
1834
|
+
if (prevPage) prevPage.updateLinkage({ after: this });
|
|
1835
|
+
if (nextPage) nextPage.updateLinkage({ before: this });
|
|
1836
|
+
} else if (prevPage?.isLinked) {
|
|
1837
|
+
this.updateLinkage({ before: prevPage });
|
|
1838
|
+
if (nextPage) nextPage.updateLinkage({ before: this });
|
|
1839
|
+
} else if (nextPage?.isLinked) {
|
|
1840
|
+
this.updateLinkage({ after: nextPage });
|
|
1841
|
+
if (prevPage) prevPage.updateLinkage({ after: this });
|
|
1842
|
+
} else if ((prevPage || nextPage) && (firstPage || lastPage)) this.lookupLinkage(firstPage, lastPage);
|
|
1843
|
+
else if (prevPage || nextPage) this.updateLinkage({
|
|
1844
|
+
before: prevPage,
|
|
1845
|
+
after: nextPage
|
|
1846
|
+
});
|
|
1847
|
+
return content;
|
|
1848
|
+
} catch {}
|
|
1849
|
+
return null;
|
|
1850
|
+
}
|
|
1851
|
+
/** @internal */
|
|
1852
|
+
updateLinkage({ before, after }) {
|
|
1853
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1854
|
+
if (!test) throw new Error("Expected at least one of before or after page states to link to this page");
|
|
1855
|
+
})(before || after);
|
|
1856
|
+
const leftSide = before ?? after?.before;
|
|
1857
|
+
const rightSide = after ?? before?.after;
|
|
1858
|
+
if (leftSide && leftSide !== this) {
|
|
1859
|
+
leftSide.setAfter(this);
|
|
1860
|
+
this.setBefore(leftSide);
|
|
1861
|
+
}
|
|
1862
|
+
if (rightSide && rightSide !== this) {
|
|
1863
|
+
rightSide.setBefore(this);
|
|
1864
|
+
this.setAfter(rightSide);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
/** @internal */
|
|
1868
|
+
lookupLinkage(firstPage, lastPage) {
|
|
1869
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1870
|
+
if (!test) throw new Error("Expected at least one of firstPage or lastPage to lookup linkage against");
|
|
1871
|
+
})(firstPage || lastPage);
|
|
1872
|
+
if (firstPage && lastPage) {
|
|
1873
|
+
if (abs(this.pageNumber - firstPage.pageNumber) <= abs(this.pageNumber - lastPage.pageNumber)) this.lookupLinkageForward(firstPage);
|
|
1874
|
+
else this.lookupLinkageBackwards(lastPage);
|
|
1875
|
+
} else if (firstPage) this.lookupLinkageForward(firstPage);
|
|
1876
|
+
else if (lastPage) this.lookupLinkageBackwards(lastPage);
|
|
1877
|
+
}
|
|
1878
|
+
/** @internal */
|
|
1879
|
+
lookupLinkageForward(page) {
|
|
1880
|
+
while (page.after && page.after.pageNumber < this.pageNumber) page = page.after;
|
|
1881
|
+
this.updateLinkage({ before: page });
|
|
1882
|
+
}
|
|
1883
|
+
/** @internal */
|
|
1884
|
+
lookupLinkageBackwards(page) {
|
|
1885
|
+
while (page.before && page.before.pageNumber > this.pageNumber) page = page.before;
|
|
1886
|
+
this.updateLinkage({ after: page });
|
|
1887
|
+
}
|
|
1888
|
+
/** @internal */
|
|
1889
|
+
setBefore(page) {
|
|
1890
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1891
|
+
if (!test) throw new Error("Expected the before page to be a different page, got the same page");
|
|
1892
|
+
})(page !== this);
|
|
1893
|
+
this.before = page;
|
|
1894
|
+
}
|
|
1895
|
+
/** @internal */
|
|
1896
|
+
setAfter(page) {
|
|
1897
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
1898
|
+
if (!test) throw new Error("Expected the after page to be a different page, got the same page");
|
|
1899
|
+
})(page !== this);
|
|
1900
|
+
this.after = page;
|
|
1901
|
+
}
|
|
1902
|
+
/** @internal */
|
|
1903
|
+
getPageNumber(document) {
|
|
1904
|
+
return this.manager.readPageHints(document).currentPage;
|
|
1905
|
+
}
|
|
1906
|
+
/** @internal */
|
|
1907
|
+
updateLinks = ({ prev, next, first, last }) => {
|
|
1908
|
+
if (prev) this.prevLink = prev;
|
|
1909
|
+
if (next) this.nextLink = next;
|
|
1910
|
+
if (first) this.firstLink = first;
|
|
1911
|
+
if (last) this.lastLink = last;
|
|
1912
|
+
};
|
|
1913
|
+
/** @internal */
|
|
1914
|
+
setPageNumber = (pageNumber) => {
|
|
1915
|
+
if (!this.pageNumber) this.pageNumber = pageNumber;
|
|
1916
|
+
};
|
|
1917
|
+
};
|
|
1918
|
+
|
|
1919
|
+
//#endregion
|
|
1920
|
+
//#region src/signals/pagination-cache.ts
|
|
1921
|
+
const PaginationCacheMap = /* @__PURE__ */ new Map();
|
|
1922
|
+
/**
|
|
1923
|
+
* A hint function for extracting the `currentPage` and `totalPages` from a loaded
|
|
1924
|
+
* document, for when the response does not expose these values through the
|
|
1925
|
+
* default `meta` locations. Provided by the consumer via `<Paginate />`'s
|
|
1926
|
+
* `@pageHints` arg:
|
|
1927
|
+
*
|
|
1928
|
+
* ```gts
|
|
1929
|
+
* <Paginate @pageHints={{...}} />
|
|
1930
|
+
* ```
|
|
1931
|
+
*
|
|
1932
|
+
* Since these values are attached to the shared {@link PaginationCache}, the hint is
|
|
1933
|
+
* a property of the *collection*, not the component. Every `<Paginate />` sharing a
|
|
1934
|
+
* collection must provide the same function reference — define it once at module
|
|
1935
|
+
* scope and import it everywhere.
|
|
1936
|
+
*
|
|
1937
|
+
* @since 5.9.0
|
|
1938
|
+
* @public
|
|
1939
|
+
*/
|
|
1940
|
+
/**
|
|
1941
|
+
* The default {@link PageHints}. Reads `currentPage`/`page` and `totalPages` from the
|
|
1942
|
+
* document `meta`, matching the behavior used before `pageHints` was configurable.
|
|
1943
|
+
* Used whenever the consumer does not provide a `pageHints` function.
|
|
1944
|
+
*
|
|
1945
|
+
* @since 5.9.0
|
|
1946
|
+
* @public
|
|
1947
|
+
*/
|
|
1948
|
+
const defaultPageHints = (document) => {
|
|
1949
|
+
return {
|
|
1950
|
+
currentPage: document.meta?.page ?? document.meta?.currentPage ?? 0,
|
|
1951
|
+
totalPages: document.meta?.totalPages ?? 0
|
|
1952
|
+
};
|
|
1953
|
+
};
|
|
1954
|
+
/**
|
|
1955
|
+
* The global, shared cache for a paginated collection. Keyed by the collection's
|
|
1956
|
+
* `first` (or `self`) link, so that multiple components paging the same collection
|
|
1957
|
+
* share loaded pages, the page graph, and `totalPages`.
|
|
1958
|
+
*
|
|
1959
|
+
* This holds only shared, request-agnostic data. Per-component state (the active
|
|
1960
|
+
* page, navigation) lives on {@link PaginationState}, which is the API consumers
|
|
1961
|
+
* interact with — the cache itself is plumbing shared between the pagination
|
|
1962
|
+
* classes.
|
|
1963
|
+
*
|
|
1964
|
+
* @since 5.9.0
|
|
1965
|
+
* @public
|
|
1966
|
+
* @hideconstructor
|
|
1967
|
+
*/
|
|
1968
|
+
var PaginationCache = class {
|
|
1969
|
+
static {
|
|
1970
|
+
decorateFieldV2(this.prototype, "firstPage", [signal]);
|
|
1971
|
+
}
|
|
1972
|
+
#firstPage = (initializeDeferredDecorator(this, "firstPage"), void 0);
|
|
1973
|
+
/**
|
|
1974
|
+
* The cache key this collection is stored under in the module-level cache —
|
|
1975
|
+
* its `first` (or `self`) link. This is the collection's identity: two
|
|
1976
|
+
* requests whose documents resolve to the same key are pages of the same
|
|
1977
|
+
* collection.
|
|
1978
|
+
*
|
|
1979
|
+
* @internal
|
|
1980
|
+
*/
|
|
1981
|
+
/** @internal */
|
|
1982
|
+
static {
|
|
1983
|
+
decorateFieldV2(this.prototype, "totalPages", [signal]);
|
|
1984
|
+
}
|
|
1985
|
+
#totalPages = (initializeDeferredDecorator(this, "totalPages"), void 0);
|
|
1986
|
+
/**
|
|
1987
|
+
* The total number of pages in the collection, or `0` when unknown. Consumers
|
|
1988
|
+
* should read this via {@link PaginationState.totalPages}.
|
|
1989
|
+
*/
|
|
1990
|
+
static {
|
|
1991
|
+
decorateFieldV2(this.prototype, "isNumbered", [signal]);
|
|
1992
|
+
}
|
|
1993
|
+
#isNumbered = (initializeDeferredDecorator(this, "isNumbered"), void 0);
|
|
1994
|
+
/**
|
|
1995
|
+
* Whether this collection has server-known page numbers. Stays `false` for
|
|
1996
|
+
* cursor-based collections (opaque `prev`/`next`, no index/total), which drives
|
|
1997
|
+
* whether numbered links are produced. Set once a {@link PageHints} yields a real
|
|
1998
|
+
* `currentPage`/`totalPages`.
|
|
1999
|
+
*
|
|
2000
|
+
* @internal
|
|
2001
|
+
*/
|
|
2002
|
+
/** @internal */
|
|
2003
|
+
/** @internal */
|
|
2004
|
+
pageHints = defaultPageHints;
|
|
2005
|
+
constructor(key) {
|
|
2006
|
+
this.key = key;
|
|
2007
|
+
this.pagesCache = /* @__PURE__ */ new Map();
|
|
2008
|
+
this.firstPage = null;
|
|
2009
|
+
this.totalPages = 0;
|
|
2010
|
+
this.isNumbered = false;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Installs the consumer-provided {@link PageHints} onto the shared cache. The first
|
|
2014
|
+
* explicit hint wins; passing `undefined` (no hint) is a no-op that preserves the
|
|
2015
|
+
* default. In dev, asserts that components sharing a collection do not provide
|
|
2016
|
+
* diverging hint functions.
|
|
2017
|
+
*
|
|
2018
|
+
* @internal
|
|
2019
|
+
*/
|
|
2020
|
+
installPageHints(pageHints) {
|
|
2021
|
+
if (!pageHints) return;
|
|
2022
|
+
if (this.pageHints === defaultPageHints) {
|
|
2023
|
+
this.pageHints = pageHints;
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
2027
|
+
if (!test) throw new Error("Received diverging `pageHints` functions for the same paginated collection. Provide the same function reference to every <Paginate /> sharing a collection (define it once at module scope).");
|
|
2028
|
+
})(this.pageHints === pageHints);
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Runs the active {@link PageHints} against a document.
|
|
2032
|
+
*
|
|
2033
|
+
* Both values are *hints*, not requirements. A value of `0` means "unknown" —
|
|
2034
|
+
* the response did not expose it and no `pageHints` function derived it. When a
|
|
2035
|
+
* value is present (`> 0`) it is relied upon (numbered links, total, sparse
|
|
2036
|
+
* placement of jumped-to pages); when absent the page graph falls back to pure
|
|
2037
|
+
* `prev`/`next` adjacency (as in cursor/infinite streams).
|
|
2038
|
+
*
|
|
2039
|
+
* @internal
|
|
2040
|
+
*/
|
|
2041
|
+
readPageHints(document) {
|
|
2042
|
+
const { currentPage, totalPages } = this.pageHints(document);
|
|
2043
|
+
if (currentPage > 0 || totalPages > 0) this.isNumbered = true;
|
|
2044
|
+
return {
|
|
2045
|
+
currentPage,
|
|
2046
|
+
totalPages
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
/** @internal */
|
|
2050
|
+
getPageCache(url) {
|
|
2051
|
+
let page = this.pagesCache.get(url);
|
|
2052
|
+
if (!page) {
|
|
2053
|
+
page = new PageCache(this, url);
|
|
2054
|
+
this.pagesCache.set(url, page);
|
|
2055
|
+
}
|
|
2056
|
+
return page;
|
|
2057
|
+
}
|
|
2058
|
+
/** @internal */
|
|
2059
|
+
loadPage(url, request) {
|
|
2060
|
+
const page = this.getPageCache(url);
|
|
2061
|
+
if ((!page.isLoaded || page.isError) && request) {
|
|
2062
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
2063
|
+
if (!test) throw new Error("Expected a request to a load a page");
|
|
2064
|
+
})(request);
|
|
2065
|
+
page.load(request).then((document) => {
|
|
2066
|
+
this.updateFirstPage(page);
|
|
2067
|
+
if (document) this.totalPages = this.getTotalPages(document);
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
return page;
|
|
2071
|
+
}
|
|
2072
|
+
/** @internal */
|
|
2073
|
+
updateFirstPage(page) {
|
|
2074
|
+
let maybeFirstPage = page;
|
|
2075
|
+
while (maybeFirstPage.before) maybeFirstPage = maybeFirstPage.before;
|
|
2076
|
+
if (!this.firstPage || this.firstPage.pageNumber > maybeFirstPage.pageNumber) this.firstPage = maybeFirstPage;
|
|
2077
|
+
}
|
|
2078
|
+
/** @internal */
|
|
2079
|
+
getTotalPages(document) {
|
|
2080
|
+
return this.readPageHints(document).totalPages;
|
|
2081
|
+
}
|
|
2082
|
+
/**
|
|
2083
|
+
* Every page known to the shared graph, in order — loaded pages and pages
|
|
2084
|
+
* known only from links — across all components sharing the collection.
|
|
2085
|
+
*
|
|
2086
|
+
* This is the whole-collection view. For the run a single component is
|
|
2087
|
+
* viewing, use {@link PaginationState.pages}.
|
|
2088
|
+
*
|
|
2089
|
+
* ```ts
|
|
2090
|
+
* const cache = getPaginationCache(firstLink);
|
|
2091
|
+
* for (const page of cache.pages) {
|
|
2092
|
+
* // page.isLoaded, page.pageNumber, page.data, ...
|
|
2093
|
+
* }
|
|
2094
|
+
* ```
|
|
2095
|
+
*/
|
|
2096
|
+
get pages() {
|
|
2097
|
+
const self = this;
|
|
2098
|
+
return { *[Symbol.iterator]() {
|
|
2099
|
+
let page = self.firstPage;
|
|
2100
|
+
while (page) {
|
|
2101
|
+
yield page;
|
|
2102
|
+
page = page.after;
|
|
2103
|
+
}
|
|
2104
|
+
} };
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* The items of every loaded page in the shared graph, flattened into one
|
|
2108
|
+
* contiguous iterable in page order. Pages that are known but not loaded
|
|
2109
|
+
* contribute nothing.
|
|
2110
|
+
*
|
|
2111
|
+
* This is the whole-collection view. For the run a single component is
|
|
2112
|
+
* viewing, use {@link PaginationState.data}.
|
|
2113
|
+
*/
|
|
2114
|
+
static {
|
|
2115
|
+
decorateMethodV2(this.prototype, "pages", [memoized]);
|
|
2116
|
+
}
|
|
2117
|
+
get data() {
|
|
2118
|
+
const self = this;
|
|
2119
|
+
return { *[Symbol.iterator]() {
|
|
2120
|
+
for (const page of self.pages) if (page.data) yield* page.data;
|
|
2121
|
+
} };
|
|
2122
|
+
}
|
|
2123
|
+
static {
|
|
2124
|
+
decorateMethodV2(this.prototype, "data", [memoized]);
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
/**
|
|
2128
|
+
* Get the shared {@link PaginationCache} for a given cache key (the collection's
|
|
2129
|
+
* `first` or `self` link). Returns the same instance for the same key for the
|
|
2130
|
+
* lifetime of the module.
|
|
2131
|
+
*
|
|
2132
|
+
* @since 5.9.0
|
|
2133
|
+
* @public
|
|
2134
|
+
*/
|
|
2135
|
+
function getPaginationCache(key) {
|
|
2136
|
+
let cache = PaginationCacheMap.get(key);
|
|
2137
|
+
if (!cache) {
|
|
2138
|
+
cache = new PaginationCache(key);
|
|
2139
|
+
PaginationCacheMap.set(key, cache);
|
|
2140
|
+
}
|
|
2141
|
+
return cache;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Clears the module-level pagination cache used by {@link getPaginationCache}.
|
|
2145
|
+
* Primarily intended for test isolation, since the cache is keyed by url and
|
|
2146
|
+
* otherwise persists for the lifetime of the module.
|
|
2147
|
+
*
|
|
2148
|
+
* @since 5.9.0
|
|
2149
|
+
* @public
|
|
2150
|
+
*/
|
|
2151
|
+
function clearPaginationCache() {
|
|
2152
|
+
PaginationCacheMap.clear();
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
//#endregion
|
|
2156
|
+
//#region src/signals/pagination-state.ts
|
|
2157
|
+
/**
|
|
2158
|
+
* The two navigation surfaces a `<Paginate />` component can drive. Selecting
|
|
2159
|
+
* one (via the component's `@mode` arg) narrows the yielded state so the two
|
|
2160
|
+
* APIs cannot be mixed:
|
|
2161
|
+
*
|
|
2162
|
+
* - `'paged'` — single-page view, see {@link PagedPaginationState}.
|
|
2163
|
+
* - `'infinite'` — accumulated view, see {@link InfinitePaginationState}.
|
|
2164
|
+
*/
|
|
2165
|
+
/**
|
|
2166
|
+
* The part of a {@link PaginationState} available to both navigation surfaces.
|
|
2167
|
+
*/
|
|
2168
|
+
/**
|
|
2169
|
+
* The paged (single-page view) surface of a {@link PaginationState}: render
|
|
2170
|
+
* {@link PaginationState.activePageRequest | activePageRequest}, navigate with
|
|
2171
|
+
* {@link PaginationState.loadPage | loadPage}. This is what the `<Paginate />`
|
|
2172
|
+
* component yields in `'paged'` mode (the default).
|
|
2173
|
+
*/
|
|
2174
|
+
/**
|
|
2175
|
+
* The infinite (accumulated view) surface of a {@link PaginationState}: render
|
|
2176
|
+
* {@link PaginationState.data | data}, grow it with
|
|
2177
|
+
* {@link PaginationState.loadNext | loadNext}/{@link PaginationState.loadPrev | loadPrev}.
|
|
2178
|
+
* This is what the `<Paginate />` component yields in `'infinite'` mode.
|
|
2179
|
+
*/
|
|
2180
|
+
/**
|
|
2181
|
+
* Resolves a {@link PaginateMode} to the surface it exposes, so a component
|
|
2182
|
+
* generic over the mode can yield only that surface.
|
|
2183
|
+
*/
|
|
2184
|
+
/**
|
|
2185
|
+
* The per-component, local pagination state. It houses the state that is unique
|
|
2186
|
+
* to a single component instance — the active page and navigation — while
|
|
2187
|
+
* referencing a shared {@link PaginationCache} for the page graph and data.
|
|
2188
|
+
*
|
|
2189
|
+
* This is the object yielded by the `<Paginate />` component, narrowed by the
|
|
2190
|
+
* component's `@mode` arg to one of its two navigation surfaces so the two
|
|
2191
|
+
* APIs cannot be mixed:
|
|
2192
|
+
*
|
|
2193
|
+
* - **Paged** (`@mode="paged"`, the default): {@link PagedPaginationState} —
|
|
2194
|
+
* render {@link activePageRequest}, navigate with {@link loadPage} (what the
|
|
2195
|
+
* numbered/relational links call). Reads {@link activePage}.
|
|
2196
|
+
* - **Infinite** (`@mode="infinite"`): {@link InfinitePaginationState} — render
|
|
2197
|
+
* {@link data}, wrap {@link nextRequest}/{@link previousRequest} in `<Request>`
|
|
2198
|
+
* for loading state, and grow the view with {@link loadNext}/{@link loadPrev}.
|
|
2199
|
+
*
|
|
2200
|
+
* Both surfaces read the same shared page graph; the mode only selects which
|
|
2201
|
+
* API is exposed.
|
|
2202
|
+
*
|
|
2203
|
+
* Instances are created via {@link getPaginationState} (or by the `<Paginate />`
|
|
2204
|
+
* component on your behalf), never constructed directly.
|
|
2205
|
+
*
|
|
2206
|
+
* @since 5.9.0
|
|
2207
|
+
* @public
|
|
2208
|
+
* @hideconstructor
|
|
2209
|
+
*/
|
|
2210
|
+
var PaginationState = class {
|
|
2211
|
+
/** @internal */
|
|
2212
|
+
/** @internal */
|
|
2213
|
+
/** @internal */
|
|
2214
|
+
/**
|
|
2215
|
+
* The shared page graph and data this component reads from.
|
|
2216
|
+
*
|
|
2217
|
+
* @internal
|
|
2218
|
+
*/
|
|
2219
|
+
/**
|
|
2220
|
+
* The page the paged surface is currently showing. Starts at the page this
|
|
2221
|
+
* component first loaded and moves whenever {@link loadPage} runs (for
|
|
2222
|
+
* example a numbered link is clicked).
|
|
2223
|
+
*/
|
|
2224
|
+
/**
|
|
2225
|
+
* The page this component first loaded, used to seed the active page and frontier.
|
|
2226
|
+
*
|
|
2227
|
+
* @internal
|
|
2228
|
+
*/
|
|
2229
|
+
/**
|
|
2230
|
+
* The first and last loaded pages of the contiguous run this component is
|
|
2231
|
+
* viewing. Drives the infinite surface — {@link data} walks from
|
|
2232
|
+
* `frontierStart` to `frontierEnd`, and {@link loadPrev}/{@link loadNext} extend
|
|
2233
|
+
* them backward/forward. Both seed to {@link initialPage}; a purely paged
|
|
2234
|
+
* consumer never extends them, so they stay put and are effectively unused.
|
|
2235
|
+
*
|
|
2236
|
+
* @internal
|
|
2237
|
+
*/
|
|
2238
|
+
/** @internal */
|
|
2239
|
+
/**
|
|
2240
|
+
* The request of the latest {@link adoptPage} call — only that call may
|
|
2241
|
+
* commit. Later calls (or a {@link loadPage} navigation) claim the slot,
|
|
2242
|
+
* superseding in-flight adoptions so the newest navigation always wins.
|
|
2243
|
+
* Untracked bookkeeping.
|
|
2244
|
+
*
|
|
2245
|
+
* @internal
|
|
2246
|
+
*/
|
|
2247
|
+
constructor(request, pageHints) {
|
|
2248
|
+
this.store = request.requester;
|
|
2249
|
+
this.request = request;
|
|
2250
|
+
this.pageHints = pageHints;
|
|
2251
|
+
this._adoptTarget = null;
|
|
2252
|
+
this.setup();
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* The request for the {@link activePage}, for the paged surface to render. This
|
|
2256
|
+
* is what a single-page view wraps in a `<Request>` to show the active page's
|
|
2257
|
+
* loading, error, and content states:
|
|
2258
|
+
*
|
|
2259
|
+
* ```gts
|
|
2260
|
+
* <Paginate @request={{this.request}}>
|
|
2261
|
+
* <:content as |pages|>
|
|
2262
|
+
* <Request @request={{pages.activePageRequest}}>
|
|
2263
|
+
* <:content as |result|>
|
|
2264
|
+
* {{#each result.data as |item|}}...{{/each}}
|
|
2265
|
+
* </:content>
|
|
2266
|
+
* </Request>
|
|
2267
|
+
* </:content>
|
|
2268
|
+
* </Paginate>
|
|
2269
|
+
* ```
|
|
2270
|
+
*/
|
|
2271
|
+
get activePageRequest() {
|
|
2272
|
+
return this.activePage?.request ?? null;
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* The total number of pages in the collection, or `0` when it is unknown (for
|
|
2276
|
+
* example a cursor-based collection that reports no total).
|
|
2277
|
+
*
|
|
2278
|
+
* ```gts
|
|
2279
|
+
* <p>Page {{pages.activePage.pageNumber}} of {{pages.totalPages}}</p>
|
|
2280
|
+
* ```
|
|
2281
|
+
*/
|
|
2282
|
+
static {
|
|
2283
|
+
decorateMethodV2(this.prototype, "activePageRequest", [memoized]);
|
|
2284
|
+
}
|
|
2285
|
+
get totalPages() {
|
|
2286
|
+
return this.paginationCache?.totalPages ?? 0;
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* The pages of the run this component is viewing, from the backward frontier
|
|
2290
|
+
* to the forward frontier inclusive, in order. Part of the infinite surface:
|
|
2291
|
+
* it is the same run as {@link data}, yielding the {@link PageCache} objects
|
|
2292
|
+
* instead of their flattened items — use it when the UI needs per-page
|
|
2293
|
+
* boundaries or request states.
|
|
2294
|
+
*
|
|
2295
|
+
* Grows as {@link loadNext}/{@link loadPrev} extend the frontier. Scoped to
|
|
2296
|
+
* this component's frontier (not the shared cache), so components paging the
|
|
2297
|
+
* same collection to different extents each see only what they have scrolled
|
|
2298
|
+
* through. For every page known to the whole collection, see
|
|
2299
|
+
* {@link PaginationCache.pages}.
|
|
2300
|
+
*/
|
|
2301
|
+
static {
|
|
2302
|
+
decorateMethodV2(this.prototype, "totalPages", [memoized]);
|
|
2303
|
+
}
|
|
2304
|
+
get pages() {
|
|
2305
|
+
const self = this;
|
|
2306
|
+
return { *[Symbol.iterator]() {
|
|
2307
|
+
const end = self.frontierEnd;
|
|
2308
|
+
let page = self.frontierStart;
|
|
2309
|
+
while (page) {
|
|
2310
|
+
yield page;
|
|
2311
|
+
if (page === end) break;
|
|
2312
|
+
page = page.after;
|
|
2313
|
+
}
|
|
2314
|
+
} };
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* The accumulated items across the loaded run, from the backward frontier to
|
|
2318
|
+
* the forward frontier inclusive — the single set an infinite collection renders.
|
|
2319
|
+
* Grows as {@link loadNext}/{@link loadPrev} extend the frontier. Scoped to this
|
|
2320
|
+
* component's frontier (not the shared cache), so components paging the same
|
|
2321
|
+
* collection to different extents each see only what they have scrolled through.
|
|
2322
|
+
*
|
|
2323
|
+
* The flattened items of {@link pages}, as one contiguous iterable. For the
|
|
2324
|
+
* items of every loaded page in the whole collection, see
|
|
2325
|
+
* {@link PaginationCache.data}.
|
|
2326
|
+
*
|
|
2327
|
+
* ```gts
|
|
2328
|
+
* <Paginate @request={{this.request}} @mode="infinite">
|
|
2329
|
+
* <:content as |pages features|>
|
|
2330
|
+
* {{#each pages.data as |item|}}...{{/each}}
|
|
2331
|
+
* {{#if pages.hasNext}}
|
|
2332
|
+
* <button {{on "click" features.loadNext}}>Load more</button>
|
|
2333
|
+
* {{/if}}
|
|
2334
|
+
* </:content>
|
|
2335
|
+
* </Paginate>
|
|
2336
|
+
* ```
|
|
2337
|
+
*/
|
|
2338
|
+
static {
|
|
2339
|
+
decorateMethodV2(this.prototype, "pages", [memoized]);
|
|
2340
|
+
}
|
|
2341
|
+
get data() {
|
|
2342
|
+
const self = this;
|
|
2343
|
+
return { *[Symbol.iterator]() {
|
|
2344
|
+
for (const page of self.pages) if (page.data) yield* page.data;
|
|
2345
|
+
} };
|
|
2346
|
+
}
|
|
2347
|
+
/**
|
|
2348
|
+
* Whether a page exists after the forward frontier — i.e. there is more to load
|
|
2349
|
+
* going forward. Use to hide the trailing load-more sentinel at end-of-list:
|
|
2350
|
+
*
|
|
2351
|
+
* ```gts
|
|
2352
|
+
* {{#if pages.hasNext}}
|
|
2353
|
+
* <button {{on "click" features.loadNext}}>Load more</button>
|
|
2354
|
+
* {{/if}}
|
|
2355
|
+
* ```
|
|
2356
|
+
*/
|
|
2357
|
+
static {
|
|
2358
|
+
decorateMethodV2(this.prototype, "data", [memoized]);
|
|
2359
|
+
}
|
|
2360
|
+
get hasNext() {
|
|
2361
|
+
return Boolean(this.frontierEnd?.nextLink);
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* Whether a page exists before the backward frontier.
|
|
2365
|
+
*/
|
|
2366
|
+
static {
|
|
2367
|
+
decorateMethodV2(this.prototype, "hasNext", [memoized]);
|
|
2368
|
+
}
|
|
2369
|
+
get hasPrevious() {
|
|
2370
|
+
return Boolean(this.frontierStart?.prevLink);
|
|
2371
|
+
}
|
|
2372
|
+
/**
|
|
2373
|
+
* The request for the page just after the forward frontier, for the infinite
|
|
2374
|
+
* surface. `null` until {@link loadNext} fires it (so a `<Request>` wrapping it
|
|
2375
|
+
* renders its idle block), the in-flight `Future` while that page loads, then
|
|
2376
|
+
* `null` again once the frontier advances onto it. Also `null` at end-of-list.
|
|
2377
|
+
*
|
|
2378
|
+
* ```gts
|
|
2379
|
+
* {{#if pages.hasNext}}
|
|
2380
|
+
* <Request @request={{pages.nextRequest}}>
|
|
2381
|
+
* <:idle><button {{on "click" features.loadNext}}>Load more</button></:idle>
|
|
2382
|
+
* <:loading><Spinner /></:loading>
|
|
2383
|
+
* </Request>
|
|
2384
|
+
* {{/if}}
|
|
2385
|
+
* ```
|
|
2386
|
+
*/
|
|
2387
|
+
static {
|
|
2388
|
+
decorateMethodV2(this.prototype, "hasPrevious", [memoized]);
|
|
2389
|
+
}
|
|
2390
|
+
get nextRequest() {
|
|
2391
|
+
const url = this.frontierEnd?.nextLink;
|
|
2392
|
+
if (!url) return null;
|
|
2393
|
+
return this.paginationCache?.getPageCache(url).request ?? null;
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* The request for the page just before the backward frontier. Mirror of
|
|
2397
|
+
* {@link nextRequest} for {@link loadPrev}.
|
|
2398
|
+
*/
|
|
2399
|
+
static {
|
|
2400
|
+
decorateMethodV2(this.prototype, "nextRequest", [memoized]);
|
|
2401
|
+
}
|
|
2402
|
+
get previousRequest() {
|
|
2403
|
+
const url = this.frontierStart?.prevLink;
|
|
2404
|
+
if (!url) return null;
|
|
2405
|
+
return this.paginationCache?.getPageCache(url).request ?? null;
|
|
2406
|
+
}
|
|
2407
|
+
/** @internal */
|
|
2408
|
+
static {
|
|
2409
|
+
decorateMethodV2(this.prototype, "previousRequest", [memoized]);
|
|
2410
|
+
}
|
|
2411
|
+
async setup() {
|
|
2412
|
+
let document;
|
|
2413
|
+
try {
|
|
2414
|
+
document = await this.request;
|
|
2415
|
+
} catch {
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
const content = document.content;
|
|
2419
|
+
const selfLink = getHref(content.links?.self);
|
|
2420
|
+
const firstLink = getHref(content.links?.first);
|
|
2421
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
2422
|
+
if (!test) throw new Error("Expected the initial document to have a self link");
|
|
2423
|
+
})(selfLink);
|
|
2424
|
+
const cache = getPaginationCache(firstLink ?? selfLink ?? "");
|
|
2425
|
+
cache.installPageHints(this.pageHints);
|
|
2426
|
+
this.paginationCache = cache;
|
|
2427
|
+
cache.totalPages = cache.getTotalPages(content);
|
|
2428
|
+
this.activePage = this.initialPage = cache.loadPage(selfLink, this.request);
|
|
2429
|
+
this.frontierStart = this.frontierEnd = this.initialPage;
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Commits a resolved, membership-proven page into this state, making it the
|
|
2433
|
+
* {@link activePage}. The synchronous commit step under {@link adoptPage},
|
|
2434
|
+
* which is where the resolution and membership verification live.
|
|
2435
|
+
*
|
|
2436
|
+
* It only activates the page and keeps the infinite surface's frontier
|
|
2437
|
+
* coherent — a page already inside the run leaves the frontier untouched,
|
|
2438
|
+
* an adjacent page extends it, and a disjoint page resets the run to the
|
|
2439
|
+
* adopted page (jump semantics).
|
|
2440
|
+
*
|
|
2441
|
+
* @internal
|
|
2442
|
+
*/
|
|
2443
|
+
_activate(page, url) {
|
|
2444
|
+
this.activePage = page;
|
|
2445
|
+
for (const existing of this.pages) if (existing === page) return;
|
|
2446
|
+
if (this.frontierEnd?.nextLink === url) this.frontierEnd = page;
|
|
2447
|
+
else if (this.frontierStart?.prevLink === url) this.frontierStart = page;
|
|
2448
|
+
else this.frontierStart = this.frontierEnd = page;
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Adopts an externally-issued request into this pagination, making its page
|
|
2452
|
+
* the {@link activePage} — the programmatic entry point for route-driven
|
|
2453
|
+
* navigation when managing a `PaginationState` directly (the `<Paginate />`
|
|
2454
|
+
* component uses the same mechanism for a changed `@request` arg).
|
|
2455
|
+
*
|
|
2456
|
+
* Awaits the request and verifies that its document is a page of this
|
|
2457
|
+
* state's collection (same `first` — or `self` — link). On a match, the
|
|
2458
|
+
* page is loaded (a page already in the shared cache is reused) and — once
|
|
2459
|
+
* its request settles — committed: it becomes the active page, and the
|
|
2460
|
+
* infinite surface's run is kept coherent (a page already inside the run
|
|
2461
|
+
* leaves it untouched, an adjacent page extends it, a disjoint page resets
|
|
2462
|
+
* the run to the adopted page). The commit happens only after the page has
|
|
2463
|
+
* loaded, so the previous page stays active — and rendered — while the
|
|
2464
|
+
* adoption resolves. Returns the page's document.
|
|
2465
|
+
*
|
|
2466
|
+
* Concurrent calls race safely: the latest call wins. An earlier in-flight
|
|
2467
|
+
* adoption is superseded and commits nothing, and a {@link loadPage}
|
|
2468
|
+
* navigation also supersedes a pending adoption (the user's click is the
|
|
2469
|
+
* newer intent).
|
|
2470
|
+
*
|
|
2471
|
+
* Returns `null` — leaving the state untouched — whenever the adoption does
|
|
2472
|
+
* not commit:
|
|
2473
|
+
*
|
|
2474
|
+
* - the request (or its page's load) rejected
|
|
2475
|
+
* - the document is a page of a *different* collection
|
|
2476
|
+
* - this state has not finished setting up its own collection yet
|
|
2477
|
+
* - the call was superseded by a newer navigation
|
|
2478
|
+
*
|
|
2479
|
+
* ```ts
|
|
2480
|
+
* const pages = getPaginationState(initialRequest);
|
|
2481
|
+
* // later, e.g. in a route model hook reacting to a ?page= param:
|
|
2482
|
+
* const adopted = await pages.adoptPage(store.request(query));
|
|
2483
|
+
* if (adopted === null) {
|
|
2484
|
+
* // not part of this collection — start a fresh pagination
|
|
2485
|
+
* }
|
|
2486
|
+
* ```
|
|
2487
|
+
*
|
|
2488
|
+
* It is a stable reference, so it is safe to pass around as an "action" or
|
|
2489
|
+
* "event" handler.
|
|
2490
|
+
*/
|
|
2491
|
+
adoptPage = async (request) => {
|
|
2492
|
+
this._adoptTarget = request;
|
|
2493
|
+
let content;
|
|
2494
|
+
try {
|
|
2495
|
+
content = (await request).content;
|
|
2496
|
+
} catch {
|
|
2497
|
+
return null;
|
|
2498
|
+
}
|
|
2499
|
+
if (this._adoptTarget !== request) return null;
|
|
2500
|
+
const cache = this.paginationCache;
|
|
2501
|
+
if (!cache) return null;
|
|
2502
|
+
const selfLink = getHref(content.links?.self);
|
|
2503
|
+
const firstLink = getHref(content.links?.first);
|
|
2504
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
2505
|
+
if (!test) throw new Error("Expected the adopted document to have a self link");
|
|
2506
|
+
})(selfLink);
|
|
2507
|
+
if (!selfLink || (firstLink ?? selfLink) !== cache.key) return null;
|
|
2508
|
+
const page = cache.loadPage(selfLink, request);
|
|
2509
|
+
try {
|
|
2510
|
+
await page.request;
|
|
2511
|
+
} catch {
|
|
2512
|
+
return null;
|
|
2513
|
+
}
|
|
2514
|
+
if (this._adoptTarget !== request) return null;
|
|
2515
|
+
this._activate(page, selfLink);
|
|
2516
|
+
return page.value;
|
|
2517
|
+
};
|
|
2518
|
+
/**
|
|
2519
|
+
* Extends the backward frontier by one page, prepending it to {@link data}.
|
|
2520
|
+
* The frontier advances only once the page has loaded, so
|
|
2521
|
+
* {@link previousRequest} tracks the in-flight page meanwhile. Returns the
|
|
2522
|
+
* loaded value, or `null` when there is no previous page or the load fails.
|
|
2523
|
+
*
|
|
2524
|
+
* On failure the frontier stays put and {@link previousRequest} keeps
|
|
2525
|
+
* resolving to the failed page, so a wrapping `<Request>` renders its error
|
|
2526
|
+
* block. Calling again retries: the failed page is re-requested with a
|
|
2527
|
+
* forced reload.
|
|
2528
|
+
*
|
|
2529
|
+
* In templates it is also available as the `loadPrev` content feature
|
|
2530
|
+
* ({@link InfinitePaginationContentFeatures.loadPrev}) yielded by
|
|
2531
|
+
* `<Paginate />`; the two are the same function.
|
|
2532
|
+
*
|
|
2533
|
+
* It is a stable reference, so it is safe to pass around as an "action" or
|
|
2534
|
+
* "event" handler.
|
|
2535
|
+
*/
|
|
2536
|
+
loadPrev = async () => {
|
|
2537
|
+
return this._extend("prev");
|
|
2538
|
+
};
|
|
2539
|
+
/**
|
|
2540
|
+
* Extends the forward frontier by one page, appending it to {@link data}.
|
|
2541
|
+
* Mirror of {@link loadPrev}.
|
|
2542
|
+
*
|
|
2543
|
+
* In templates it is also available as the `loadNext` content feature
|
|
2544
|
+
* ({@link InfinitePaginationContentFeatures.loadNext}) yielded by
|
|
2545
|
+
* `<Paginate />`; the two are the same function.
|
|
2546
|
+
*
|
|
2547
|
+
* ```gts
|
|
2548
|
+
* <button {{on "click" pages.loadNext}}>Load more</button>
|
|
2549
|
+
* ```
|
|
2550
|
+
*/
|
|
2551
|
+
loadNext = async () => {
|
|
2552
|
+
return this._extend("next");
|
|
2553
|
+
};
|
|
2554
|
+
/** @internal */
|
|
2555
|
+
_extend = async (dir) => {
|
|
2556
|
+
const cache = this.paginationCache;
|
|
2557
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
2558
|
+
if (!test) throw new Error("Expected the pagination cache to be set up before loading a page");
|
|
2559
|
+
})(cache);
|
|
2560
|
+
const frontier = dir === "next" ? this.frontierEnd : this.frontierStart;
|
|
2561
|
+
const url = dir === "next" ? frontier?.nextLink : frontier?.prevLink;
|
|
2562
|
+
if (!url) return null;
|
|
2563
|
+
const page = cache.getPageCache(url);
|
|
2564
|
+
if (!page.isRequested || page.isError) {
|
|
2565
|
+
const request = this.store.request(page.isError ? {
|
|
2566
|
+
method: "GET",
|
|
2567
|
+
url,
|
|
2568
|
+
cacheOptions: { reload: true }
|
|
2569
|
+
} : {
|
|
2570
|
+
method: "GET",
|
|
2571
|
+
url
|
|
2572
|
+
});
|
|
2573
|
+
cache.loadPage(url, request);
|
|
2574
|
+
}
|
|
2575
|
+
try {
|
|
2576
|
+
await page.request;
|
|
2577
|
+
} catch {
|
|
2578
|
+
return null;
|
|
2579
|
+
}
|
|
2580
|
+
if (dir === "next") this.frontierEnd = page;
|
|
2581
|
+
else this.frontierStart = page;
|
|
2582
|
+
return page.value;
|
|
2583
|
+
};
|
|
2584
|
+
/**
|
|
2585
|
+
* Loads a specific page by its URL and makes it the {@link activePage},
|
|
2586
|
+
* requesting it first if it is not already loaded. This is the paged surface's
|
|
2587
|
+
* navigation entry point, called by the numbered and relational links.
|
|
2588
|
+
*
|
|
2589
|
+
* In templates it is also available as the `loadPage` content feature
|
|
2590
|
+
* ({@link PagedPaginationContentFeatures.loadPage}) yielded by `<Paginate />`;
|
|
2591
|
+
* the two are the same function.
|
|
2592
|
+
*
|
|
2593
|
+
* It is a stable reference, so it is safe to pass around as an "action" or
|
|
2594
|
+
* "event" handler. Returns the page's value, or `null` if it has none or
|
|
2595
|
+
* the load fails.
|
|
2596
|
+
*
|
|
2597
|
+
* On failure the page stays active and {@link activePageRequest} resolves to
|
|
2598
|
+
* it, so a wrapping `<Request>` renders its error block. Calling again (for
|
|
2599
|
+
* example clicking the page's link a second time) retries: the failed page
|
|
2600
|
+
* is re-requested with a forced reload.
|
|
2601
|
+
*
|
|
2602
|
+
* ```gts
|
|
2603
|
+
* <EachLink @pages={{pages}}>
|
|
2604
|
+
* <:link as |link|>
|
|
2605
|
+
* <button {{on "click" (fn features.loadPage link.url)}}>{{link.text}}</button>
|
|
2606
|
+
* </:link>
|
|
2607
|
+
* </EachLink>
|
|
2608
|
+
* ```
|
|
2609
|
+
*/
|
|
2610
|
+
loadPage = async (url) => {
|
|
2611
|
+
const cache = this.paginationCache;
|
|
2612
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
2613
|
+
if (!test) throw new Error("Expected the pagination cache to be set up before loading a page");
|
|
2614
|
+
})(cache);
|
|
2615
|
+
this._adoptTarget = null;
|
|
2616
|
+
const page = cache.getPageCache(url);
|
|
2617
|
+
this.activePage = page;
|
|
2618
|
+
if (!page.isLoaded || page.isError) {
|
|
2619
|
+
const request = this.store.request(page.isError ? {
|
|
2620
|
+
method: "GET",
|
|
2621
|
+
url,
|
|
2622
|
+
cacheOptions: { reload: true }
|
|
2623
|
+
} : {
|
|
2624
|
+
method: "GET",
|
|
2625
|
+
url
|
|
2626
|
+
});
|
|
2627
|
+
cache.loadPage(url, request);
|
|
2628
|
+
try {
|
|
2629
|
+
await page.request;
|
|
2630
|
+
} catch {
|
|
2631
|
+
return null;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
return page.value;
|
|
2635
|
+
};
|
|
2636
|
+
};
|
|
2637
|
+
defineSignal(PaginationState.prototype, "paginationCache", null);
|
|
2638
|
+
defineSignal(PaginationState.prototype, "activePage", null);
|
|
2639
|
+
defineSignal(PaginationState.prototype, "initialPage", null);
|
|
2640
|
+
defineSignal(PaginationState.prototype, "frontierStart", null);
|
|
2641
|
+
defineSignal(PaginationState.prototype, "frontierEnd", null);
|
|
2642
|
+
const PaginationStateCache = /* @__PURE__ */ new WeakMap();
|
|
2643
|
+
/**
|
|
2644
|
+
* Get the {@link PaginationState} for a given request. Returns the same instance
|
|
2645
|
+
* for the same request future, so that repeated calls (e.g. in a template, or
|
|
2646
|
+
* alongside the `<Paginate />` component using the same request) share the same
|
|
2647
|
+
* local pagination state. Keyed by request identity, just like
|
|
2648
|
+
* {@link getRequestState}.
|
|
2649
|
+
*
|
|
2650
|
+
* ```ts
|
|
2651
|
+
* const future = store.request(query);
|
|
2652
|
+
* const pages = getPaginationState(future);
|
|
2653
|
+
*
|
|
2654
|
+
* await future;
|
|
2655
|
+
* pages.totalPages; // now known, if the response exposed it
|
|
2656
|
+
* await pages.loadNext();
|
|
2657
|
+
* ```
|
|
2658
|
+
*
|
|
2659
|
+
* @since 5.9.0
|
|
2660
|
+
* @public
|
|
2661
|
+
*/
|
|
2662
|
+
function getPaginationState(request, pageHints) {
|
|
2663
|
+
let state = PaginationStateCache.get(request);
|
|
2664
|
+
if (!state) {
|
|
2665
|
+
state = new PaginationState(request, pageHints);
|
|
2666
|
+
PaginationStateCache.set(request, state);
|
|
2667
|
+
}
|
|
2668
|
+
return state;
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
//#endregion
|
|
2672
|
+
//#region src/signals/pagination-links.ts
|
|
2673
|
+
const PaginationLinksCache = /* @__PURE__ */ new WeakMap();
|
|
2674
|
+
/**
|
|
2675
|
+
* A single numbered link, e.g. page `3`. Loads its page and makes it active when
|
|
2676
|
+
* {@link setActive} runs. Carries its {@link index} and its
|
|
2677
|
+
* {@link distanceFromActiveIndex} so a UI can style links by how far they sit
|
|
2678
|
+
* from the current page.
|
|
2679
|
+
*
|
|
2680
|
+
* ```gts
|
|
2681
|
+
* <EachLink @pages={{pages}} as |state|>
|
|
2682
|
+
* {{#each state.links as |link|}}
|
|
2683
|
+
* {{#if link.isReal}}
|
|
2684
|
+
* <button class={{if link.isCurrent "active"}} {{on "click" link.setActive}}>
|
|
2685
|
+
* {{link.text}}
|
|
2686
|
+
* </button>
|
|
2687
|
+
* {{/if}}
|
|
2688
|
+
* {{/each}}
|
|
2689
|
+
* </EachLink>
|
|
2690
|
+
* ```
|
|
2691
|
+
*
|
|
2692
|
+
* @since 5.9.0
|
|
2693
|
+
* @public
|
|
2694
|
+
* @hideconstructor
|
|
2695
|
+
*/
|
|
2696
|
+
var RealPaginationLink = class {
|
|
2697
|
+
isReal = true;
|
|
2698
|
+
url;
|
|
2699
|
+
index;
|
|
2700
|
+
isCurrent;
|
|
2701
|
+
distanceFromActiveIndex;
|
|
2702
|
+
/** @internal */
|
|
2703
|
+
constructor(url, index, activeIndex, loadPage) {
|
|
2704
|
+
this.url = url;
|
|
2705
|
+
this.index = index;
|
|
2706
|
+
this.isCurrent = index === activeIndex;
|
|
2707
|
+
this.distanceFromActiveIndex = Math.abs(index - activeIndex);
|
|
2708
|
+
this._loadPage = loadPage;
|
|
2709
|
+
}
|
|
2710
|
+
get text() {
|
|
2711
|
+
return `${this.index}`;
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Loads this link's page and makes it the active page on the associated
|
|
2715
|
+
* {@link PaginationState}. It is a stable reference, so it is safe to pass
|
|
2716
|
+
* around as an "action" or "event" handler:
|
|
2717
|
+
*
|
|
2718
|
+
* ```gts
|
|
2719
|
+
* <button {{on "click" link.setActive}}>{{link.text}}</button>
|
|
2720
|
+
* ```
|
|
2721
|
+
*/
|
|
2722
|
+
setActive = () => {
|
|
2723
|
+
return this._loadPage(this.url);
|
|
2724
|
+
};
|
|
2725
|
+
};
|
|
2726
|
+
/**
|
|
2727
|
+
* A stand-in for a run of pages that have not been loaded yet, rendered as an
|
|
2728
|
+
* ellipsis between numbered links. It covers an {@link indexRange} rather than a
|
|
2729
|
+
* single page and has no page to navigate to.
|
|
2730
|
+
*
|
|
2731
|
+
* ```gts
|
|
2732
|
+
* <EachLink @pages={{pages}} as |state|>
|
|
2733
|
+
* {{#each state.links as |link|}}
|
|
2734
|
+
* {{#unless link.isReal}}
|
|
2735
|
+
* <span title="{{link.rangeSize}} more pages">{{link.text}}</span>
|
|
2736
|
+
* {{/unless}}
|
|
2737
|
+
* {{/each}}
|
|
2738
|
+
* </EachLink>
|
|
2739
|
+
* ```
|
|
2740
|
+
*
|
|
2741
|
+
* @since 5.9.0
|
|
2742
|
+
* @public
|
|
2743
|
+
* @hideconstructor
|
|
2744
|
+
*/
|
|
2745
|
+
var PlaceholderPaginationLink = class {
|
|
2746
|
+
isReal = false;
|
|
2747
|
+
/**
|
|
2748
|
+
* The inclusive `[start, end]` page-number range of not-yet-loaded pages this
|
|
2749
|
+
* placeholder stands in for.
|
|
2750
|
+
*/
|
|
2751
|
+
indexRange;
|
|
2752
|
+
/** @internal */
|
|
2753
|
+
activeIndex;
|
|
2754
|
+
text = ".";
|
|
2755
|
+
constructor(index, activeIndex) {
|
|
2756
|
+
this.indexRange = index;
|
|
2757
|
+
this.activeIndex = activeIndex;
|
|
2758
|
+
}
|
|
2759
|
+
/** The number of pages this placeholder covers. */
|
|
2760
|
+
get rangeSize() {
|
|
2761
|
+
return this.indexRange[1] - this.indexRange[0] + 1;
|
|
2762
|
+
}
|
|
2763
|
+
/**
|
|
2764
|
+
* The distance between the active page and the nearest edge of this
|
|
2765
|
+
* placeholder's index range.
|
|
2766
|
+
*/
|
|
2767
|
+
get distanceFromActiveIndex() {
|
|
2768
|
+
const [start, end] = this.indexRange;
|
|
2769
|
+
return Math.min(Math.abs(start - this.activeIndex), Math.abs(end - this.activeIndex));
|
|
2770
|
+
}
|
|
2771
|
+
/** @internal */
|
|
2772
|
+
_mergeRange(newRange) {
|
|
2773
|
+
const [oldStart, oldEnd] = this.indexRange;
|
|
2774
|
+
const [newStart, newEnd] = newRange;
|
|
2775
|
+
const mergedRange = [Math.min(oldStart, newStart), Math.max(oldEnd, newEnd)];
|
|
2776
|
+
this.indexRange = mergedRange;
|
|
2777
|
+
}
|
|
2778
|
+
};
|
|
2779
|
+
/**
|
|
2780
|
+
* A relational (`first`/`prev`/`next`/`last`) navigation link, relative to the
|
|
2781
|
+
* active page. Unlike {@link RealPaginationLink} these carry no ordinal index —
|
|
2782
|
+
* they are the only links available for cursor-based pagination, where pages are
|
|
2783
|
+
* chained purely by opaque relational links with no page number or total. They
|
|
2784
|
+
* are also available in numbered pagination as a convenience.
|
|
2785
|
+
*
|
|
2786
|
+
* ```gts
|
|
2787
|
+
* <EachLink @pages={{pages}} as |state|>
|
|
2788
|
+
* {{#if state.prev}}<button {{on "click" state.prev.setActive}}>Previous</button>{{/if}}
|
|
2789
|
+
* {{#if state.next}}<button {{on "click" state.next.setActive}}>Next</button>{{/if}}
|
|
2790
|
+
* </EachLink>
|
|
2791
|
+
* ```
|
|
2792
|
+
*
|
|
2793
|
+
* @since 5.9.0
|
|
2794
|
+
* @public
|
|
2795
|
+
* @hideconstructor
|
|
2796
|
+
*/
|
|
2797
|
+
var RelationalPaginationLink = class {
|
|
2798
|
+
isReal = true;
|
|
2799
|
+
rel;
|
|
2800
|
+
url;
|
|
2801
|
+
/**
|
|
2802
|
+
* Whether this link points at the page that is already active — for example
|
|
2803
|
+
* the `first` link while the first page is being viewed. Useful for
|
|
2804
|
+
* disabling the control.
|
|
2805
|
+
*/
|
|
2806
|
+
isCurrent;
|
|
2807
|
+
/** @internal */
|
|
2808
|
+
constructor(rel, url, isCurrent, loadPage) {
|
|
2809
|
+
this.rel = rel;
|
|
2810
|
+
this.url = url;
|
|
2811
|
+
this.isCurrent = isCurrent;
|
|
2812
|
+
this._loadPage = loadPage;
|
|
2813
|
+
}
|
|
2814
|
+
get text() {
|
|
2815
|
+
return this.rel;
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Loads this link's page and makes it the active page on the associated
|
|
2819
|
+
* {@link PaginationState}. It is a stable reference, so it is safe to pass
|
|
2820
|
+
* around as an "action" or "event" handler:
|
|
2821
|
+
*
|
|
2822
|
+
* ```gts
|
|
2823
|
+
* <button {{on "click" link.setActive}}>{{link.text}}</button>
|
|
2824
|
+
* ```
|
|
2825
|
+
*/
|
|
2826
|
+
setActive = () => {
|
|
2827
|
+
return this._loadPage(this.url);
|
|
2828
|
+
};
|
|
2829
|
+
};
|
|
2830
|
+
/**
|
|
2831
|
+
* A member of {@link PaginationLinks.links}: either a numbered
|
|
2832
|
+
* {@link RealPaginationLink} or a {@link PlaceholderPaginationLink} standing in
|
|
2833
|
+
* for a gap. Discriminate with {@link RealPaginationLink.isReal | isReal}:
|
|
2834
|
+
*
|
|
2835
|
+
* ```ts
|
|
2836
|
+
* for (const link of links.links) {
|
|
2837
|
+
* if (link.isReal) {
|
|
2838
|
+
* // numbered link: link.index, link.setActive
|
|
2839
|
+
* } else {
|
|
2840
|
+
* // gap: link.indexRange
|
|
2841
|
+
* }
|
|
2842
|
+
* }
|
|
2843
|
+
* ```
|
|
2844
|
+
*/
|
|
2845
|
+
/**
|
|
2846
|
+
* The reactive set of navigation links derived from a {@link PaginationState}.
|
|
2847
|
+
* Where `PaginationState` tracks which pages are loaded and which one is active,
|
|
2848
|
+
* `PaginationLinks` turns that page graph into the links a UI renders to move
|
|
2849
|
+
* between pages.
|
|
2850
|
+
*
|
|
2851
|
+
* It provides two kinds of link:
|
|
2852
|
+
*
|
|
2853
|
+
* - {@link links}: the numbered links, with {@link PlaceholderPaginationLink}
|
|
2854
|
+
* placeholders standing in for gaps of not-yet-loaded pages. Only numbered
|
|
2855
|
+
* collections (where the server exposes page numbers) produce these.
|
|
2856
|
+
* - {@link prev} and {@link next}: the relational links for the active page.
|
|
2857
|
+
* Available in both numbered and cursor-based pagination, and the only links a
|
|
2858
|
+
* cursor-based collection has.
|
|
2859
|
+
* - {@link first} and {@link last}: the relational links to the collection's
|
|
2860
|
+
* edges, when the response exposes them. Usually present on every page —
|
|
2861
|
+
* including the edge page itself, where the link's
|
|
2862
|
+
* {@link RelationalPaginationLink.isCurrent | isCurrent} is `true`.
|
|
2863
|
+
*
|
|
2864
|
+
* Every link updates as pages load and as the active page changes, since they
|
|
2865
|
+
* read straight from the state's shared page graph. To get the links for a
|
|
2866
|
+
* state, use {@link getPaginationLinks}.
|
|
2867
|
+
*
|
|
2868
|
+
* See also:
|
|
2869
|
+
* - {@link RealPaginationLink}
|
|
2870
|
+
* - {@link PlaceholderPaginationLink}
|
|
2871
|
+
* - {@link RelationalPaginationLink}
|
|
2872
|
+
*
|
|
2873
|
+
* @since 5.9.0
|
|
2874
|
+
* @public
|
|
2875
|
+
* @hideconstructor
|
|
2876
|
+
*/
|
|
2877
|
+
var PaginationLinks = class {
|
|
2878
|
+
/** @internal */
|
|
2879
|
+
constructor(paginationState) {
|
|
2880
|
+
this.paginationState = paginationState;
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* The relational `first` link of the collection, or `null` when the active
|
|
2884
|
+
* page's response did not expose one. Unlike {@link prev}/{@link next} it is
|
|
2885
|
+
* usually present on every page — including the first page itself, where the
|
|
2886
|
+
* link's {@link RelationalPaginationLink.isCurrent | isCurrent} is `true`
|
|
2887
|
+
* (useful for disabling the control).
|
|
2888
|
+
*/
|
|
2889
|
+
get first() {
|
|
2890
|
+
const activePage = this.paginationState.activePage;
|
|
2891
|
+
const url = activePage?.firstLink;
|
|
2892
|
+
return url ? new RelationalPaginationLink("first", url, url === activePage?.selfLink, this.paginationState.loadPage) : null;
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* The relational `prev` link for the active page, or `null` at the start of the
|
|
2896
|
+
* collection. Available in both numbered and cursor pagination.
|
|
2897
|
+
*/
|
|
2898
|
+
static {
|
|
2899
|
+
decorateMethodV2(this.prototype, "first", [memoized]);
|
|
2900
|
+
}
|
|
2901
|
+
get prev() {
|
|
2902
|
+
const activePage = this.paginationState.activePage;
|
|
2903
|
+
const url = activePage?.prevLink;
|
|
2904
|
+
return url ? new RelationalPaginationLink("prev", url, url === activePage?.selfLink, this.paginationState.loadPage) : null;
|
|
2905
|
+
}
|
|
2906
|
+
/**
|
|
2907
|
+
* The relational `next` link for the active page, or `null` at the end of the
|
|
2908
|
+
* collection. Available in both numbered and cursor pagination.
|
|
2909
|
+
*/
|
|
2910
|
+
static {
|
|
2911
|
+
decorateMethodV2(this.prototype, "prev", [memoized]);
|
|
2912
|
+
}
|
|
2913
|
+
get next() {
|
|
2914
|
+
const activePage = this.paginationState.activePage;
|
|
2915
|
+
const url = activePage?.nextLink;
|
|
2916
|
+
return url ? new RelationalPaginationLink("next", url, url === activePage?.selfLink, this.paginationState.loadPage) : null;
|
|
2917
|
+
}
|
|
2918
|
+
/**
|
|
2919
|
+
* The relational `last` link of the collection, or `null` when the active
|
|
2920
|
+
* page's response did not expose one. Mirror of {@link first} for the end of
|
|
2921
|
+
* the collection.
|
|
2922
|
+
*/
|
|
2923
|
+
static {
|
|
2924
|
+
decorateMethodV2(this.prototype, "next", [memoized]);
|
|
2925
|
+
}
|
|
2926
|
+
get last() {
|
|
2927
|
+
const activePage = this.paginationState.activePage;
|
|
2928
|
+
const url = activePage?.lastLink;
|
|
2929
|
+
return url ? new RelationalPaginationLink("last", url, url === activePage?.selfLink, this.paginationState.loadPage) : null;
|
|
2930
|
+
}
|
|
2931
|
+
/**
|
|
2932
|
+
* The numbered links and placeholders, derived from the shared page graph.
|
|
2933
|
+
*
|
|
2934
|
+
* Only produced for numbered pagination (where the server exposes page numbers).
|
|
2935
|
+
* Cursor-based collections have no page numbers, so this is empty — use
|
|
2936
|
+
* {@link prev}/{@link next} for cursor navigation.
|
|
2937
|
+
*/
|
|
2938
|
+
static {
|
|
2939
|
+
decorateMethodV2(this.prototype, "last", [memoized]);
|
|
2940
|
+
}
|
|
2941
|
+
get links() {
|
|
2942
|
+
const links = [];
|
|
2943
|
+
const cache = this.paginationState.paginationCache;
|
|
2944
|
+
if (!cache || !cache.isNumbered) return links;
|
|
2945
|
+
const { firstPage, totalPages } = cache;
|
|
2946
|
+
const activeIndex = this.paginationState.activePage?.pageNumber ?? 0;
|
|
2947
|
+
if (firstPage) {
|
|
2948
|
+
let previousPage = null;
|
|
2949
|
+
let currentPage = firstPage;
|
|
2950
|
+
while (currentPage) {
|
|
2951
|
+
if (previousPage && currentPage.pageNumber - previousPage.pageNumber > 1) links.push(new PlaceholderPaginationLink([previousPage.pageNumber + 1, currentPage.pageNumber - 1], activeIndex));
|
|
2952
|
+
links.push(new RealPaginationLink(currentPage.selfLink ?? "", currentPage.pageNumber, activeIndex, this.paginationState.loadPage));
|
|
2953
|
+
previousPage = currentPage;
|
|
2954
|
+
currentPage = currentPage.after;
|
|
2955
|
+
}
|
|
2956
|
+
if (previousPage && previousPage.pageNumber < totalPages) links.push(new PlaceholderPaginationLink([previousPage.pageNumber + 1, totalPages], activeIndex));
|
|
2957
|
+
}
|
|
2958
|
+
return links;
|
|
2959
|
+
}
|
|
2960
|
+
static {
|
|
2961
|
+
decorateMethodV2(this.prototype, "links", [memoized]);
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
/**
|
|
2965
|
+
* Get the {@link PaginationLinks} for a given {@link PaginationState}. Returns
|
|
2966
|
+
* the same instance for the same state, so repeated calls (for example a
|
|
2967
|
+
* `<Paginate />` component and a separate links component reading the same state)
|
|
2968
|
+
* share one set of links. Keyed by state identity, the same way
|
|
2969
|
+
* {@link getPaginationState} is keyed by request and {@link getRequestState} by
|
|
2970
|
+
* future.
|
|
2971
|
+
*
|
|
2972
|
+
* ```ts
|
|
2973
|
+
* const links = getPaginationLinks(paginationState);
|
|
2974
|
+
*
|
|
2975
|
+
* for (const link of links.links) {
|
|
2976
|
+
* if (link.isReal) {
|
|
2977
|
+
* // render a numbered link, using link.setActive as the click handler
|
|
2978
|
+
* } else {
|
|
2979
|
+
* // render an ellipsis for the gap link.indexRange covers
|
|
2980
|
+
* }
|
|
2981
|
+
* }
|
|
2982
|
+
* ```
|
|
2983
|
+
*
|
|
2984
|
+
* @since 5.9.0
|
|
2985
|
+
* @public
|
|
2986
|
+
*/
|
|
2987
|
+
function getPaginationLinks(state) {
|
|
2988
|
+
let links = PaginationLinksCache.get(state);
|
|
2989
|
+
if (!links) {
|
|
2990
|
+
links = new PaginationLinks(state);
|
|
2991
|
+
PaginationLinksCache.set(state, links);
|
|
2992
|
+
}
|
|
2993
|
+
return links;
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
//#endregion
|
|
2997
|
+
//#region src/signals/pagination-subscription.ts
|
|
2998
|
+
/**
|
|
2999
|
+
* The content features available in both pagination modes: the state and
|
|
3000
|
+
* controls of the initial request.
|
|
3001
|
+
*/
|
|
3002
|
+
/**
|
|
3003
|
+
* The content features yielded in `'paged'` mode: navigation happens by
|
|
3004
|
+
* loading a specific page.
|
|
3005
|
+
*/
|
|
3006
|
+
/**
|
|
3007
|
+
* The content features yielded in `'infinite'` mode: navigation happens by
|
|
3008
|
+
* extending the loaded run at either end.
|
|
3009
|
+
*/
|
|
3010
|
+
/**
|
|
3011
|
+
* The full set of content features a {@link PaginationSubscription} builds —
|
|
3012
|
+
* both modes' surfaces. The `<Paginate />` component narrows this to one mode
|
|
3013
|
+
* via {@link PaginationContentFeaturesFor} before yielding.
|
|
3014
|
+
*/
|
|
3015
|
+
/**
|
|
3016
|
+
* Resolves a {@link PaginateMode} to the content features it exposes. Mirror of
|
|
3017
|
+
* {@link PaginationStateFor}.
|
|
3018
|
+
*/
|
|
3019
|
+
/**
|
|
3020
|
+
* Lifecycle glue for the `<Paginate />` component. Owns the initial
|
|
3021
|
+
* {@link RequestSubscription} (loading/error state, autorefresh, disposal) and
|
|
3022
|
+
* the per-component {@link PaginationState} that it hands to the component.
|
|
3023
|
+
*
|
|
3024
|
+
* @since 5.9.0
|
|
3025
|
+
* @public
|
|
3026
|
+
* @hideconstructor
|
|
3027
|
+
*/
|
|
3028
|
+
var PaginationSubscription = class {
|
|
3029
|
+
/** @internal */
|
|
3030
|
+
/** @internal */
|
|
3031
|
+
/** @internal */
|
|
3032
|
+
/**
|
|
3033
|
+
* The Store this subscription subscribes to or the RequestManager
|
|
3034
|
+
* which issues this request.
|
|
3035
|
+
*/
|
|
3036
|
+
/**
|
|
3037
|
+
* The wrapped {@link RequestSubscription} monitoring the initial request.
|
|
3038
|
+
* Created eagerly: constructing one consumes reactive state (its own
|
|
3039
|
+
* signals), so building it lazily inside a tracked getter would cause the
|
|
3040
|
+
* getter to invalidate — and the subscription to be recreated, losing its
|
|
3041
|
+
* reloaded request — whenever `retry`/`reload` runs.
|
|
3042
|
+
*
|
|
3043
|
+
* @internal
|
|
3044
|
+
*/
|
|
3045
|
+
/**
|
|
3046
|
+
* The first request this subscription saw — the one that keys the
|
|
3047
|
+
* {@link PaginationState} until a navigation resolves to a different
|
|
3048
|
+
* collection. Plain (untracked) because it is written during the first
|
|
3049
|
+
* computation of {@link paginationState}.
|
|
3050
|
+
*
|
|
3051
|
+
* @internal
|
|
3052
|
+
*/
|
|
3053
|
+
/**
|
|
3054
|
+
* The request a resolved navigation swapped the {@link PaginationState} to,
|
|
3055
|
+
* when the navigation turned out to target a different collection. A signal:
|
|
3056
|
+
* writing it (always outside of render, from {@link _resolveNavigation})
|
|
3057
|
+
* invalidates {@link paginationState} so the fresh state takes over.
|
|
3058
|
+
*
|
|
3059
|
+
* @internal
|
|
3060
|
+
*/
|
|
3061
|
+
/**
|
|
3062
|
+
* The changed request currently being resolved by {@link _resolveNavigation},
|
|
3063
|
+
* both to avoid kicking the same navigation twice and as the latest-wins
|
|
3064
|
+
* guard when several changes race. Untracked bookkeeping.
|
|
3065
|
+
*
|
|
3066
|
+
* @internal
|
|
3067
|
+
*/
|
|
3068
|
+
constructor(store, args) {
|
|
3069
|
+
this._args = args;
|
|
3070
|
+
this.store = store;
|
|
3071
|
+
this.isDestroyed = false;
|
|
3072
|
+
this._seedRequest = null;
|
|
3073
|
+
this._navTarget = null;
|
|
3074
|
+
this[DISPOSE] = _DISPOSE$1;
|
|
3075
|
+
this._requestSubscription = createRequestSubscription(store, args);
|
|
3076
|
+
}
|
|
3077
|
+
/**
|
|
3078
|
+
* The per-component pagination state yielded to the component.
|
|
3079
|
+
*
|
|
3080
|
+
* Keyed to the request that started the collection, not the current
|
|
3081
|
+
* `@request` arg: when the arg changes, the existing state keeps rendering
|
|
3082
|
+
* while {@link _resolveNavigation} resolves the new request in the
|
|
3083
|
+
* background. A request that resolves to a page of the same collection is
|
|
3084
|
+
* adopted into this state as the new active page (route-driven navigation,
|
|
3085
|
+
* e.g. the browser back button); one that resolves to a different
|
|
3086
|
+
* collection swaps in a fresh state (a true reset).
|
|
3087
|
+
*
|
|
3088
|
+
* A background `refresh` does not swap the request, so the pagination
|
|
3089
|
+
* state is stable across refreshes.
|
|
3090
|
+
*/
|
|
3091
|
+
get paginationState() {
|
|
3092
|
+
const request = this._requestSubscription.request;
|
|
3093
|
+
const active = this._navRequest ?? this._seedRequest;
|
|
3094
|
+
if (!active) {
|
|
3095
|
+
this._seedRequest = request;
|
|
3096
|
+
return getPaginationState(request, this._args.pageHints);
|
|
3097
|
+
}
|
|
3098
|
+
if (request !== active && request !== this._navTarget) {
|
|
3099
|
+
this._navTarget = request;
|
|
3100
|
+
this._resolveNavigation(request);
|
|
3101
|
+
}
|
|
3102
|
+
return getPaginationState(active, this._args.pageHints);
|
|
3103
|
+
}
|
|
3104
|
+
/**
|
|
3105
|
+
* Resolves a changed `@request` arg against the current collection via
|
|
3106
|
+
* {@link PaginationState.adoptPage}: a request that resolves to a page of
|
|
3107
|
+
* the same collection is adopted into the existing state; one that resolves
|
|
3108
|
+
* to a different collection (or arrives before a collection is loaded) swaps
|
|
3109
|
+
* the state for a fresh one keyed to this request.
|
|
3110
|
+
*
|
|
3111
|
+
* A rejected request changes nothing here — the request subscription
|
|
3112
|
+
* already surfaces the failure (error/cancelled states), and the existing
|
|
3113
|
+
* pagination state is kept for recovery. The request is awaited before
|
|
3114
|
+
* `adoptPage` so that rejection and the latest-wins guard are handled
|
|
3115
|
+
* before anything is adopted.
|
|
3116
|
+
*
|
|
3117
|
+
* @internal
|
|
3118
|
+
*/
|
|
3119
|
+
static {
|
|
3120
|
+
decorateMethodV2(this.prototype, "paginationState", [memoized]);
|
|
3121
|
+
}
|
|
3122
|
+
async _resolveNavigation(request) {
|
|
3123
|
+
try {
|
|
3124
|
+
await request;
|
|
3125
|
+
} catch {
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
if (this.isDestroyed || this._navTarget !== request) return;
|
|
3129
|
+
const adopted = await this.paginationState.adoptPage(request);
|
|
3130
|
+
if (this.isDestroyed || this._navTarget !== request) return;
|
|
3131
|
+
if (adopted === null) this._navRequest = request;
|
|
3132
|
+
}
|
|
3133
|
+
/**
|
|
3134
|
+
* Whether there is no request or query to monitor, so the component has
|
|
3135
|
+
* nothing to load.
|
|
3136
|
+
*/
|
|
3137
|
+
get isIdle() {
|
|
3138
|
+
return this._requestSubscription.isIdle;
|
|
3139
|
+
}
|
|
3140
|
+
/**
|
|
3141
|
+
* Whether the collection is still blocking-loading: no {@link PaginationState}
|
|
3142
|
+
* has finished setting up yet. Only the very first page load (or the reset
|
|
3143
|
+
* to a different collection) blocks here — extending the collection with
|
|
3144
|
+
* `loadNext`/`loadPrev` does not, and neither does a changed `@request` arg
|
|
3145
|
+
* while a collection is already on screen (that is {@link isNavigating}).
|
|
3146
|
+
*
|
|
3147
|
+
* Remains `true` for the moment between the request resolving and the
|
|
3148
|
+
* {@link paginationState} finishing its setup from the response, so that
|
|
3149
|
+
* {@link isSuccess} consumers never see a success state with an empty
|
|
3150
|
+
* pagination surface.
|
|
3151
|
+
*/
|
|
3152
|
+
static {
|
|
3153
|
+
decorateMethodV2(this.prototype, "isIdle", [memoized]);
|
|
3154
|
+
}
|
|
3155
|
+
get isLoading() {
|
|
3156
|
+
const { reqState } = this._requestSubscription;
|
|
3157
|
+
return this.paginationState.paginationCache === null && (reqState.isLoading || reqState.isSuccess);
|
|
3158
|
+
}
|
|
3159
|
+
/**
|
|
3160
|
+
* Whether a changed `@request` arg is resolving while a collection is
|
|
3161
|
+
* already on screen — route-driven navigation, e.g. the browser back
|
|
3162
|
+
* button. The content stays rendered (see {@link isSuccess}); consumers can
|
|
3163
|
+
* use this to show a lightweight navigation indicator, the same way a
|
|
3164
|
+
* `loadPage` call surfaces through the active page's request state.
|
|
3165
|
+
*/
|
|
3166
|
+
static {
|
|
3167
|
+
decorateMethodV2(this.prototype, "isLoading", [memoized]);
|
|
3168
|
+
}
|
|
3169
|
+
get isNavigating() {
|
|
3170
|
+
return this.paginationState.paginationCache !== null && this._requestSubscription.reqState.isLoading;
|
|
3171
|
+
}
|
|
3172
|
+
/**
|
|
3173
|
+
* The {@link RequestLoadingState} for the initial request, for building UIs
|
|
3174
|
+
* that respond to download progress.
|
|
3175
|
+
*/
|
|
3176
|
+
static {
|
|
3177
|
+
decorateMethodV2(this.prototype, "isNavigating", [memoized]);
|
|
3178
|
+
}
|
|
3179
|
+
get loadingState() {
|
|
3180
|
+
return this._requestSubscription.reqState.loadingState;
|
|
3181
|
+
}
|
|
3182
|
+
/**
|
|
3183
|
+
* Whether the component has a set-up {@link paginationState} to render and
|
|
3184
|
+
* the current request did not fail. Stays `true` while a changed `@request`
|
|
3185
|
+
* arg resolves ({@link isNavigating}), so the existing content keeps
|
|
3186
|
+
* rendering instead of falling back to a blocking loading state.
|
|
3187
|
+
*/
|
|
3188
|
+
static {
|
|
3189
|
+
decorateMethodV2(this.prototype, "loadingState", [memoized]);
|
|
3190
|
+
}
|
|
3191
|
+
get isSuccess() {
|
|
3192
|
+
const { reqState } = this._requestSubscription;
|
|
3193
|
+
return this.paginationState.paginationCache !== null && !reqState.isError && !reqState.isCancelled;
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Whether the initial request was cancelled (aborted).
|
|
3197
|
+
*/
|
|
3198
|
+
static {
|
|
3199
|
+
decorateMethodV2(this.prototype, "isSuccess", [memoized]);
|
|
3200
|
+
}
|
|
3201
|
+
get isCancelled() {
|
|
3202
|
+
return this._requestSubscription.reqState.isCancelled;
|
|
3203
|
+
}
|
|
3204
|
+
/**
|
|
3205
|
+
* Whether the initial request rejected with an error.
|
|
3206
|
+
*/
|
|
3207
|
+
static {
|
|
3208
|
+
decorateMethodV2(this.prototype, "isCancelled", [memoized]);
|
|
3209
|
+
}
|
|
3210
|
+
get isError() {
|
|
3211
|
+
return this._requestSubscription.reqState.isError;
|
|
3212
|
+
}
|
|
3213
|
+
/**
|
|
3214
|
+
* The error the initial request rejected with, or `null` if it did not reject.
|
|
3215
|
+
*/
|
|
3216
|
+
static {
|
|
3217
|
+
decorateMethodV2(this.prototype, "isError", [memoized]);
|
|
3218
|
+
}
|
|
3219
|
+
get reason() {
|
|
3220
|
+
return this._requestSubscription.reqState.reason;
|
|
3221
|
+
}
|
|
3222
|
+
/**
|
|
3223
|
+
* Error features to yield to the error slot of a component
|
|
3224
|
+
*/
|
|
3225
|
+
static {
|
|
3226
|
+
decorateMethodV2(this.prototype, "reason", [memoized]);
|
|
3227
|
+
}
|
|
3228
|
+
get errorFeatures() {
|
|
3229
|
+
return {
|
|
3230
|
+
isHidden: this._requestSubscription.isHidden,
|
|
3231
|
+
isOnline: this._requestSubscription.isOnline,
|
|
3232
|
+
retry: this._requestSubscription.retry
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Content features to yield to the content slot of a component
|
|
3237
|
+
*/
|
|
3238
|
+
static {
|
|
3239
|
+
decorateMethodV2(this.prototype, "errorFeatures", [memoized]);
|
|
3240
|
+
}
|
|
3241
|
+
get contentFeatures() {
|
|
3242
|
+
const contentFeatures = this._requestSubscription.contentFeatures;
|
|
3243
|
+
const { paginationState } = this;
|
|
3244
|
+
const feat = {
|
|
3245
|
+
...contentFeatures,
|
|
3246
|
+
isNavigating: this.isNavigating,
|
|
3247
|
+
loadPrev: paginationState.loadPrev,
|
|
3248
|
+
loadNext: paginationState.loadNext,
|
|
3249
|
+
loadPage: paginationState.loadPage
|
|
3250
|
+
};
|
|
3251
|
+
if (feat.isRefreshing) feat.abort = () => {
|
|
3252
|
+
contentFeatures.latestRequest?.abort();
|
|
3253
|
+
};
|
|
3254
|
+
return feat;
|
|
3255
|
+
}
|
|
3256
|
+
/**
|
|
3257
|
+
* The initial request `Future` this subscription is monitoring, i.e. the first
|
|
3258
|
+
* page load. This is the future the {@link paginationState} is built from.
|
|
3259
|
+
*
|
|
3260
|
+
* @internal
|
|
3261
|
+
*/
|
|
3262
|
+
static {
|
|
3263
|
+
decorateMethodV2(this.prototype, "contentFeatures", [memoized]);
|
|
3264
|
+
}
|
|
3265
|
+
get request() {
|
|
3266
|
+
return this._requestSubscription.request;
|
|
3267
|
+
}
|
|
3268
|
+
static {
|
|
3269
|
+
decorateMethodV2(this.prototype, "request", [memoized]);
|
|
3270
|
+
}
|
|
3271
|
+
};
|
|
3272
|
+
defineSignal(PaginationSubscription.prototype, "_navRequest", null);
|
|
3273
|
+
/**
|
|
3274
|
+
* Creates the {@link PaginationSubscription} a `<Paginate />` component uses to
|
|
3275
|
+
* manage its request lifecycle and pagination state. Pass the result back into
|
|
3276
|
+
* the component via `@subscription` to manage the lifecycle externally.
|
|
3277
|
+
*
|
|
3278
|
+
* ```ts
|
|
3279
|
+
* const subscription = createPaginationSubscription(store, { request });
|
|
3280
|
+
*
|
|
3281
|
+
* subscription.paginationState; // the per-component PaginationState
|
|
3282
|
+
* subscription[DISPOSE](); // tear down when the owning component unmounts
|
|
3283
|
+
* ```
|
|
3284
|
+
*
|
|
3285
|
+
* @since 5.9.0
|
|
3286
|
+
* @public
|
|
3287
|
+
*/
|
|
3288
|
+
function createPaginationSubscription(store, args) {
|
|
3289
|
+
return new PaginationSubscription(store, args);
|
|
3290
|
+
}
|
|
3291
|
+
function upgradeSubscription$1(sub) {
|
|
3292
|
+
return sub;
|
|
3293
|
+
}
|
|
3294
|
+
function _DISPOSE$1() {
|
|
3295
|
+
const self = upgradeSubscription$1(this);
|
|
3296
|
+
self.isDestroyed = true;
|
|
3297
|
+
self._requestSubscription?.[DISPOSE]?.();
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
//#endregion
|
|
3301
|
+
//#region src/signals/pagination-links-subscription.ts
|
|
3302
|
+
/**
|
|
3303
|
+
* The framework-agnostic core of a pagination links component (for example the
|
|
3304
|
+
* `<EachLink />` component of `@warp-drive-mirror/ember`): it owns the component
|
|
3305
|
+
* lifecycle, while the links themselves live on the {@link PaginationLinks}
|
|
3306
|
+
* it exposes.
|
|
3307
|
+
*
|
|
3308
|
+
* Given the {@link PagedPaginationState} a `<Paginate />` component is driving,
|
|
3309
|
+
* {@link paginationLinks} derives that state's `PaginationLinks` — the numbered
|
|
3310
|
+
* links (with placeholders for gaps) and the relational
|
|
3311
|
+
* `first`/`prev`/`next`/`last` links a component yields for the consumer to
|
|
3312
|
+
* render. All of them read from the same shared page graph as the
|
|
3313
|
+
* `<Paginate />` component, so they stay in sync as pages load and the active
|
|
3314
|
+
* page changes.
|
|
3315
|
+
*
|
|
3316
|
+
* @since 5.9.0
|
|
3317
|
+
* @public
|
|
3318
|
+
* @hideconstructor
|
|
3319
|
+
*/
|
|
3320
|
+
var PaginationLinksSubscription = class {
|
|
3321
|
+
/** @internal */
|
|
3322
|
+
/** @internal */
|
|
3323
|
+
constructor(args) {
|
|
3324
|
+
this._args = args;
|
|
3325
|
+
this.isDestroyed = false;
|
|
3326
|
+
this[DISPOSE] = _DISPOSE;
|
|
3327
|
+
}
|
|
3328
|
+
/**
|
|
3329
|
+
* The {@link PaginationLinks} derived from the {@link PagedPaginationState}
|
|
3330
|
+
* passed as an arg — the surface a links component yields to its consumer:
|
|
3331
|
+
* the numbered {@link PaginationLinks.links | links} (empty for cursor-based
|
|
3332
|
+
* collections) and the relational {@link PaginationLinks.first | first}/
|
|
3333
|
+
* {@link PaginationLinks.prev | prev}/{@link PaginationLinks.next | next}/
|
|
3334
|
+
* {@link PaginationLinks.last | last} links.
|
|
3335
|
+
*
|
|
3336
|
+
* Recomputes when the `pages` arg changes, so a component whose pagination
|
|
3337
|
+
* resets to a different collection derives fresh links automatically.
|
|
3338
|
+
*/
|
|
3339
|
+
get paginationLinks() {
|
|
3340
|
+
return getPaginationLinks(this._args.pages);
|
|
3341
|
+
}
|
|
3342
|
+
static {
|
|
3343
|
+
decorateMethodV2(this.prototype, "paginationLinks", [memoized]);
|
|
3344
|
+
}
|
|
3345
|
+
};
|
|
3346
|
+
/**
|
|
3347
|
+
* Creates the {@link PaginationLinksSubscription} a links component (such as
|
|
3348
|
+
* `<EachLink />`) uses to derive its links from a {@link PagedPaginationState}.
|
|
3349
|
+
*
|
|
3350
|
+
* ```ts
|
|
3351
|
+
* const subscription = createPaginationLinksSubscription({ pages: paginationState });
|
|
3352
|
+
*
|
|
3353
|
+
* subscription.paginationLinks; // the derived PaginationLinks
|
|
3354
|
+
* subscription[DISPOSE](); // tear down when the owning component unmounts
|
|
3355
|
+
* ```
|
|
3356
|
+
*
|
|
3357
|
+
* @since 5.9.0
|
|
3358
|
+
* @public
|
|
3359
|
+
*/
|
|
3360
|
+
function createPaginationLinksSubscription(args) {
|
|
3361
|
+
return new PaginationLinksSubscription(args);
|
|
3362
|
+
}
|
|
3363
|
+
function upgradeSubscription(sub) {
|
|
3364
|
+
return sub;
|
|
3365
|
+
}
|
|
3366
|
+
function _DISPOSE() {
|
|
3367
|
+
const self = upgradeSubscription(this);
|
|
3368
|
+
self.isDestroyed = true;
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
//#endregion
|
|
3372
|
+
//#region src/signals/promise-state.ts
|
|
3373
|
+
const PromiseCache = /* @__PURE__ */ new WeakMap();
|
|
3374
|
+
/**
|
|
3375
|
+
* The state of a promise in the "pending"
|
|
3376
|
+
* state. This is the default initial state.
|
|
3377
|
+
*
|
|
3378
|
+
*/
|
|
3379
|
+
/**
|
|
3380
|
+
* The state of a promise in the "fulfilled" state.
|
|
3381
|
+
* This is the state of a promise that has resolved
|
|
3382
|
+
* successfully.
|
|
3383
|
+
*
|
|
3384
|
+
*/
|
|
3385
|
+
/**
|
|
3386
|
+
* The state of a promise in the "rejected" state.
|
|
3387
|
+
* This is the state of a promise that has rejected
|
|
3388
|
+
* with an error.
|
|
3389
|
+
*
|
|
3390
|
+
*/
|
|
3391
|
+
/**
|
|
3392
|
+
* The state of a promise. This is the type that is returned
|
|
3393
|
+
* from `getPromiseState`.
|
|
3394
|
+
*
|
|
3395
|
+
* See also:
|
|
3396
|
+
* - {@link PendingPromise}
|
|
3397
|
+
* - {@link ResolvedPromise}
|
|
3398
|
+
* - {@link RejectedPromise}
|
|
3399
|
+
*
|
|
3400
|
+
*/
|
|
3401
|
+
const PromiseStateProto = {};
|
|
3402
|
+
defineSignal(PromiseStateProto, "reason", null);
|
|
3403
|
+
defineSignal(PromiseStateProto, "value", null);
|
|
3404
|
+
defineSignal(PromiseStateProto, "result", null);
|
|
3405
|
+
defineSignal(PromiseStateProto, "error", null);
|
|
3406
|
+
defineSignal(PromiseStateProto, "status", "pending");
|
|
3407
|
+
defineSignal(PromiseStateProto, "isPending", true);
|
|
3408
|
+
defineSignal(PromiseStateProto, "isLoading", true);
|
|
3409
|
+
defineSignal(PromiseStateProto, "isSuccess", false);
|
|
3410
|
+
defineSignal(PromiseStateProto, "isError", false);
|
|
3411
|
+
function createPromiseState(promise) {
|
|
3412
|
+
const state = getPromiseResult(promise);
|
|
3413
|
+
const promiseState = Object.create(PromiseStateProto);
|
|
3414
|
+
if (state) {
|
|
3415
|
+
if (state.isError) {
|
|
3416
|
+
promiseState.error = state.result;
|
|
3417
|
+
promiseState.reason = state.result;
|
|
3418
|
+
promiseState.status = "rejected";
|
|
3419
|
+
promiseState.isError = true;
|
|
3420
|
+
promiseState.isPending = false;
|
|
3421
|
+
promiseState.isLoading = false;
|
|
3422
|
+
} else {
|
|
3423
|
+
promiseState.result = state.result;
|
|
3424
|
+
promiseState.value = state.result;
|
|
3425
|
+
promiseState.status = "fulfilled";
|
|
3426
|
+
promiseState.isSuccess = true;
|
|
3427
|
+
promiseState.isPending = false;
|
|
3428
|
+
promiseState.isLoading = false;
|
|
3429
|
+
}
|
|
3430
|
+
} else promise.then((result) => {
|
|
3431
|
+
setPromiseResult(promise, {
|
|
3432
|
+
isError: false,
|
|
3433
|
+
result
|
|
3434
|
+
});
|
|
3435
|
+
promiseState.result = result;
|
|
3436
|
+
promiseState.value = result;
|
|
3437
|
+
promiseState.status = "fulfilled";
|
|
3438
|
+
promiseState.isSuccess = true;
|
|
3439
|
+
promiseState.isPending = false;
|
|
3440
|
+
promiseState.isLoading = false;
|
|
3441
|
+
}, (error) => {
|
|
3442
|
+
setPromiseResult(promise, {
|
|
3443
|
+
isError: true,
|
|
3444
|
+
result: error
|
|
3445
|
+
});
|
|
3446
|
+
promiseState.error = error;
|
|
3447
|
+
promiseState.reason = error;
|
|
3448
|
+
promiseState.status = "rejected";
|
|
3449
|
+
promiseState.isError = true;
|
|
3450
|
+
promiseState.isPending = false;
|
|
3451
|
+
promiseState.isLoading = false;
|
|
3452
|
+
});
|
|
3453
|
+
return promiseState;
|
|
3454
|
+
}
|
|
3455
|
+
const LegacyPromiseProxy = Symbol.for("LegacyPromiseProxy");
|
|
3456
|
+
function isLegacyAwaitable(promise) {
|
|
3457
|
+
return LegacyPromiseProxy in promise && "promise" in promise && promise[LegacyPromiseProxy] === true;
|
|
3458
|
+
}
|
|
3459
|
+
function getPromise(promise) {
|
|
3460
|
+
return isLegacyAwaitable(promise) ? promise.promise : promise;
|
|
3461
|
+
}
|
|
3462
|
+
/**
|
|
3463
|
+
* Returns a reactive state-machine for the provided promise or awaitable.
|
|
3464
|
+
*
|
|
3465
|
+
* Repeat calls to `getPromiseState` with the same promise will return the same state object
|
|
3466
|
+
* making is safe and easy to use in templates and JavaScript code to produce reactive
|
|
3467
|
+
* behaviors around promises.
|
|
3468
|
+
*
|
|
3469
|
+
* `getPromiseState` can be used in both JavaScript and Template contexts.
|
|
3470
|
+
*
|
|
3471
|
+
* ```ts
|
|
3472
|
+
* import { getPromiseState } from '@warp-drive-mirror/ember';
|
|
3473
|
+
*
|
|
3474
|
+
* const state = getPromiseState(promise);
|
|
3475
|
+
* ```
|
|
3476
|
+
*
|
|
3477
|
+
* For instance, we could write a getter on a component that updates whenever
|
|
3478
|
+
* the promise state advances or the promise changes, by combining the function
|
|
3479
|
+
* with the use of `@cached`
|
|
3480
|
+
*
|
|
3481
|
+
* ```ts
|
|
3482
|
+
* class Component {
|
|
3483
|
+
* @cached
|
|
3484
|
+
* get title() {
|
|
3485
|
+
* const state = getPromiseState(this.args.request);
|
|
3486
|
+
* if (state.isPending) {
|
|
3487
|
+
* return 'loading...';
|
|
3488
|
+
* }
|
|
3489
|
+
* if (state.isError) { return null; }
|
|
3490
|
+
* return state.result.title;
|
|
3491
|
+
* }
|
|
3492
|
+
* }
|
|
3493
|
+
* ```
|
|
3494
|
+
*
|
|
3495
|
+
* Or in a template as a helper:
|
|
3496
|
+
*
|
|
3497
|
+
* ```gjs
|
|
3498
|
+
* import { getPromiseState } from '@warp-drive-mirror/ember';
|
|
3499
|
+
*
|
|
3500
|
+
* <template>
|
|
3501
|
+
* {{#let (getPromiseState @request) as |state|}}
|
|
3502
|
+
* {{#if state.isPending}} <Spinner />
|
|
3503
|
+
* {{else if state.isError}} <ErrorForm @error={{state.error}} />
|
|
3504
|
+
* {{else}}
|
|
3505
|
+
* <h1>{{state.result.title}}</h1>
|
|
3506
|
+
* {{/if}}
|
|
3507
|
+
* {{/let}}
|
|
3508
|
+
* </template>
|
|
3509
|
+
* ```
|
|
3510
|
+
*
|
|
3511
|
+
* If looking to use in a template, consider also the `<Await />` component.
|
|
3512
|
+
*
|
|
3513
|
+
* See also {@link PromiseState}
|
|
3514
|
+
*/
|
|
3515
|
+
function getPromiseState(promise) {
|
|
3516
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) && ((test) => {
|
|
3517
|
+
if (!test) throw new Error(`getPromiseState expects to be called with a promise: called with ${String(promise)}`);
|
|
3518
|
+
})(promise);
|
|
3519
|
+
const _promise = getPromise(promise);
|
|
3520
|
+
let state = PromiseCache.get(_promise);
|
|
3521
|
+
if (!state) {
|
|
3522
|
+
state = createPromiseState(_promise);
|
|
3523
|
+
PromiseCache.set(_promise, state);
|
|
3524
|
+
}
|
|
3525
|
+
return state;
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
//#endregion
|
|
3529
|
+
export { ARRAY_SIGNAL as A, createInternalMemo as C, notifyInternalSignal as D, makeInitializer as E, setupSignals as M, waitFor as N, peekInternalSignal as O, willSyncFlushWatchers as P, consumeInternalSignal as S, getOrCreateInternalSignal as T, entangleSignal as _, getPaginationState as a, signal as b, getPaginationCache as c, getRequestState as d, createSignalDescriptor as f, entangleInitiallyStaleSignal as g, defineSignal as h, getPaginationLinks as i, OBJECT_SIGNAL as j, withSignalStore as k, DISPOSE as l, defineNonEnumerableSignal as m, createPaginationLinksSubscription as n, clearPaginationCache as o, defineGate as p, createPaginationSubscription as r, defaultPageHints as s, getPromiseState as t, createRequestSubscription as u, gate as v, createInternalSignal as w, Signals as x, memoized as y };
|
|
3530
|
+
//# sourceMappingURL=-private-B_RTqxGA.js.map
|