@abloatai/ablo 0.25.0 → 0.27.0
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/AGENTS.md +5 -3
- package/CHANGELOG.md +34 -0
- package/README.md +104 -88
- package/dist/BaseSyncedStore.d.ts +140 -266
- package/dist/BaseSyncedStore.js +338 -739
- package/dist/Database.d.ts +62 -77
- package/dist/Database.js +106 -127
- package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
- package/dist/{ObjectPool.js → InstanceCache.js} +91 -83
- package/dist/LazyReferenceCollection.d.ts +11 -15
- package/dist/LazyReferenceCollection.js +16 -15
- package/dist/Model.d.ts +37 -52
- package/dist/Model.js +52 -69
- package/dist/ModelRegistry.d.ts +46 -25
- package/dist/ModelRegistry.js +32 -30
- package/dist/NetworkMonitor.d.ts +5 -6
- package/dist/NetworkMonitor.js +6 -7
- package/dist/SyncClient.d.ts +119 -109
- package/dist/SyncClient.js +303 -224
- package/dist/SyncEngineContext.d.ts +1 -3
- package/dist/SyncEngineContext.js +1 -2
- package/dist/adapters/alwaysOnline.d.ts +6 -8
- package/dist/adapters/alwaysOnline.js +6 -8
- package/dist/adapters/inMemoryStorage.d.ts +9 -9
- package/dist/adapters/inMemoryStorage.js +9 -9
- package/dist/agent/Agent.d.ts +39 -31
- package/dist/agent/Agent.js +35 -23
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +5 -5
- package/dist/agent/session.d.ts +47 -44
- package/dist/agent/session.js +37 -48
- package/dist/agent/types.d.ts +26 -31
- package/dist/agent/types.js +6 -7
- package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
- package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
- package/dist/ai-sdk/coordinationContext.d.ts +46 -0
- package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +30 -31
- package/dist/ai-sdk/index.d.ts +25 -22
- package/dist/ai-sdk/index.js +25 -22
- package/dist/ai-sdk/wrap.d.ts +7 -8
- package/dist/ai-sdk/wrap.js +2 -2
- package/dist/auth/credentialPolicy.d.ts +74 -71
- package/dist/auth/credentialPolicy.js +51 -56
- package/dist/auth/credentialSource.d.ts +7 -18
- package/dist/auth/credentialSource.js +10 -18
- package/dist/auth/index.d.ts +59 -58
- package/dist/auth/index.js +34 -40
- package/dist/auth/schemas.d.ts +5 -4
- package/dist/auth/schemas.js +5 -4
- package/dist/batching/index.d.ts +19 -21
- package/dist/batching/index.js +14 -17
- package/dist/cli.cjs +483 -369
- package/dist/client/Ablo.d.ts +107 -836
- package/dist/client/Ablo.js +174 -833
- package/dist/client/ApiClient.d.ts +44 -20
- package/dist/client/ApiClient.js +193 -44
- package/dist/client/auth.d.ts +51 -60
- package/dist/client/auth.js +137 -110
- package/dist/client/claimHeartbeatLoop.d.ts +50 -0
- package/dist/client/claimHeartbeatLoop.js +88 -0
- package/dist/client/consoleLogger.d.ts +35 -0
- package/dist/client/consoleLogger.js +44 -0
- package/dist/client/createInternalComponents.d.ts +14 -17
- package/dist/client/createInternalComponents.js +26 -31
- package/dist/client/createModelProxy.d.ts +130 -120
- package/dist/client/createModelProxy.js +158 -124
- package/dist/client/credentialEndpoint.d.ts +61 -0
- package/dist/client/credentialEndpoint.js +86 -0
- package/dist/client/functionalUpdate.d.ts +29 -27
- package/dist/client/functionalUpdate.js +21 -21
- package/dist/client/hostedEndpoints.d.ts +21 -0
- package/dist/client/hostedEndpoints.js +21 -0
- package/dist/client/httpClient.d.ts +58 -54
- package/dist/client/httpClient.js +29 -31
- package/dist/client/identity.d.ts +15 -20
- package/dist/client/identity.js +49 -59
- package/dist/client/modelRegistration.d.ts +10 -0
- package/dist/client/modelRegistration.js +301 -0
- package/dist/client/options.d.ts +373 -0
- package/dist/client/options.js +6 -0
- package/dist/client/registerDataSource.d.ts +9 -9
- package/dist/client/registerDataSource.js +15 -16
- package/dist/client/resourceTypes.d.ts +333 -0
- package/dist/client/resourceTypes.js +7 -0
- package/dist/client/schemaConfig.d.ts +44 -0
- package/dist/client/schemaConfig.js +176 -0
- package/dist/client/sessionMint.d.ts +17 -13
- package/dist/client/sessionMint.js +26 -31
- package/dist/client/validateAbloOptions.d.ts +12 -14
- package/dist/client/validateAbloOptions.js +9 -10
- package/dist/client/writeOptionsSchema.d.ts +18 -16
- package/dist/client/writeOptionsSchema.js +23 -20
- package/dist/client/wsMutationExecutor.d.ts +28 -0
- package/dist/client/wsMutationExecutor.js +71 -0
- package/dist/context.d.ts +6 -4
- package/dist/context.js +6 -7
- package/dist/coordination/index.d.ts +13 -4
- package/dist/coordination/index.js +29 -4
- package/dist/coordination/schema.d.ts +176 -128
- package/dist/coordination/schema.js +197 -133
- package/dist/coordination/trace.d.ts +9 -11
- package/dist/coordination/trace.js +13 -15
- package/dist/core/DatabaseManager.d.ts +5 -8
- package/dist/core/DatabaseManager.js +38 -40
- package/dist/core/QueryProcessor.d.ts +7 -9
- package/dist/core/QueryProcessor.js +27 -34
- package/dist/core/QueryView.d.ts +17 -5
- package/dist/core/QueryView.js +6 -7
- package/dist/core/StoreManager.d.ts +14 -16
- package/dist/core/StoreManager.js +26 -25
- package/dist/core/ViewRegistry.d.ts +5 -5
- package/dist/core/ViewRegistry.js +4 -4
- package/dist/core/index.d.ts +18 -13
- package/dist/core/index.js +32 -26
- package/dist/core/openIDBWithTimeout.d.ts +38 -36
- package/dist/core/openIDBWithTimeout.js +57 -54
- package/dist/core/queryUtils.d.ts +45 -0
- package/dist/core/queryUtils.js +69 -0
- package/dist/core/storeContract.d.ts +145 -0
- package/dist/core/storeContract.js +12 -0
- package/dist/environment.d.ts +28 -0
- package/dist/environment.js +21 -0
- package/dist/errorCodes.d.ts +118 -101
- package/dist/errorCodes.js +277 -260
- package/dist/errors.d.ts +170 -165
- package/dist/errors.js +161 -151
- package/dist/index.d.ts +30 -27
- package/dist/index.js +90 -82
- package/dist/interfaces/index.d.ts +108 -133
- package/dist/interfaces/index.js +5 -4
- package/dist/keys/index.d.ts +27 -29
- package/dist/keys/index.js +59 -49
- package/dist/mutators/RecordingTransaction.d.ts +16 -16
- package/dist/mutators/RecordingTransaction.js +31 -37
- package/dist/mutators/Transaction.d.ts +18 -26
- package/dist/mutators/Transaction.js +14 -20
- package/dist/mutators/UndoManager.d.ts +122 -131
- package/dist/mutators/UndoManager.js +149 -155
- package/dist/mutators/defineMutators.d.ts +24 -37
- package/dist/mutators/defineMutators.js +14 -20
- package/dist/mutators/inverseOp.d.ts +12 -15
- package/dist/mutators/inverseOp.js +12 -15
- package/dist/mutators/mutateActions.d.ts +10 -9
- package/dist/mutators/mutateActions.js +1 -1
- package/dist/mutators/readerActions.d.ts +9 -8
- package/dist/mutators/readerActions.js +2 -2
- package/dist/mutators/undoApply.d.ts +31 -27
- package/dist/mutators/undoApply.js +26 -24
- package/dist/policy/index.d.ts +5 -3
- package/dist/policy/index.js +5 -3
- package/dist/policy/types.d.ts +105 -101
- package/dist/policy/types.js +67 -66
- package/dist/query/client.d.ts +32 -16
- package/dist/query/client.js +103 -72
- package/dist/query/types.d.ts +37 -60
- package/dist/query/types.js +13 -33
- package/dist/react/AbloProvider.d.ts +7 -11
- package/dist/react/AbloProvider.js +24 -17
- package/dist/react/context.d.ts +27 -146
- package/dist/react/context.js +9 -10
- package/dist/react/index.d.ts +41 -42
- package/dist/react/index.js +37 -38
- package/dist/react/internalContext.d.ts +17 -19
- package/dist/react/useAblo.d.ts +23 -22
- package/dist/react/useAblo.js +17 -15
- package/dist/react/useCurrentUserId.d.ts +8 -7
- package/dist/react/useCurrentUserId.js +8 -7
- package/dist/react/useErrorListener.d.ts +7 -7
- package/dist/react/useErrorListener.js +11 -12
- package/dist/react/useMutationFailureListener.d.ts +8 -8
- package/dist/react/useMutationFailureListener.js +9 -9
- package/dist/react/useMutators.d.ts +11 -11
- package/dist/react/useMutators.js +10 -4
- package/dist/react/useReactive.js +2 -3
- package/dist/react/useSyncStatus.d.ts +4 -6
- package/dist/react/useUndoScope.d.ts +7 -9
- package/dist/react/useUndoScope.js +3 -3
- package/dist/schema/coordination.d.ts +21 -25
- package/dist/schema/coordination.js +21 -25
- package/dist/schema/ddl.d.ts +43 -39
- package/dist/schema/ddl.js +75 -68
- package/dist/schema/ddlLock.d.ts +35 -0
- package/dist/schema/ddlLock.js +46 -0
- package/dist/schema/diff.d.ts +99 -61
- package/dist/schema/diff.js +43 -34
- package/dist/schema/field.d.ts +37 -42
- package/dist/schema/field.js +36 -49
- package/dist/schema/generate.d.ts +12 -12
- package/dist/schema/generate.js +12 -12
- package/dist/schema/index.d.ts +5 -4
- package/dist/schema/index.js +29 -21
- package/dist/schema/model.d.ts +121 -146
- package/dist/schema/model.js +24 -35
- package/dist/schema/openapi.d.ts +10 -9
- package/dist/schema/openapi.js +7 -1
- package/dist/schema/queries.d.ts +30 -32
- package/dist/schema/queries.js +24 -25
- package/dist/schema/relation.d.ts +89 -99
- package/dist/schema/relation.js +13 -13
- package/dist/schema/residency.d.ts +38 -0
- package/dist/schema/residency.js +30 -0
- package/dist/schema/roles.d.ts +45 -27
- package/dist/schema/roles.js +52 -21
- package/dist/schema/schema.d.ts +36 -45
- package/dist/schema/schema.js +42 -39
- package/dist/schema/select.d.ts +13 -13
- package/dist/schema/select.js +13 -13
- package/dist/schema/serialize.d.ts +36 -39
- package/dist/schema/serialize.js +27 -31
- package/dist/schema/sugar.d.ts +17 -32
- package/dist/schema/sugar.js +14 -29
- package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +27 -50
- package/dist/schema/syncDeltaRow.js +89 -0
- package/dist/schema/tenancy.d.ts +44 -46
- package/dist/schema/tenancy.js +46 -48
- package/dist/server/adapter.d.ts +58 -58
- package/dist/server/adapter.js +13 -14
- package/dist/server/commit.d.ts +60 -64
- package/dist/server/index.d.ts +9 -10
- package/dist/server/index.js +1 -1
- package/dist/server/readConfig.d.ts +70 -0
- package/dist/server/readConfig.js +8 -0
- package/dist/server/storageMode.d.ts +23 -0
- package/dist/server/storageMode.js +17 -0
- package/dist/source/adapter.d.ts +31 -26
- package/dist/source/adapter.js +10 -10
- package/dist/source/adapters/drizzle.d.ts +28 -23
- package/dist/source/adapters/drizzle.js +34 -28
- package/dist/source/adapters/kysely.d.ts +27 -25
- package/dist/source/adapters/kysely.js +28 -26
- package/dist/source/adapters/memory.d.ts +8 -7
- package/dist/source/adapters/memory.js +10 -9
- package/dist/source/adapters/prisma.d.ts +13 -12
- package/dist/source/adapters/prisma.js +27 -29
- package/dist/source/conformance.d.ts +18 -11
- package/dist/source/conformance.js +27 -19
- package/dist/source/connector.d.ts +31 -32
- package/dist/source/connector.js +30 -28
- package/dist/source/connectorProtocol.d.ts +160 -0
- package/dist/source/connectorProtocol.js +162 -0
- package/dist/source/contract.d.ts +26 -27
- package/dist/source/contract.js +28 -29
- package/dist/source/factory.d.ts +94 -0
- package/dist/source/factory.js +268 -0
- package/dist/source/index.d.ts +10 -462
- package/dist/source/index.js +17 -421
- package/dist/source/migrations.d.ts +9 -9
- package/dist/source/migrations.js +9 -9
- package/dist/source/next.d.ts +10 -11
- package/dist/source/next.js +7 -8
- package/dist/source/pushQueue.d.ts +70 -48
- package/dist/source/pushQueue.js +36 -29
- package/dist/source/signing.d.ts +88 -0
- package/dist/source/signing.js +159 -0
- package/dist/source/types.d.ts +351 -0
- package/dist/source/types.js +43 -0
- package/dist/stores/ObjectStore.d.ts +11 -12
- package/dist/stores/ObjectStore.js +34 -35
- package/dist/stores/ObjectStoreContract.d.ts +12 -15
- package/dist/stores/SyncActionStore.d.ts +8 -12
- package/dist/stores/SyncActionStore.js +77 -46
- package/dist/surface.d.ts +28 -21
- package/dist/surface.js +28 -20
- package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +37 -45
- package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +101 -80
- package/dist/sync/ConnectionManager.d.ts +47 -50
- package/dist/sync/ConnectionManager.js +74 -70
- package/dist/sync/NetworkProbe.d.ts +27 -31
- package/dist/sync/NetworkProbe.js +67 -72
- package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +49 -36
- package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +79 -54
- package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +45 -59
- package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +44 -52
- package/dist/sync/SyncWebSocket.d.ts +175 -250
- package/dist/sync/SyncWebSocket.js +431 -769
- package/dist/sync/awaitClaimGrant.d.ts +18 -18
- package/dist/sync/awaitClaimGrant.js +38 -30
- package/dist/sync/bootstrapApply.d.ts +70 -0
- package/dist/sync/bootstrapApply.js +73 -0
- package/dist/sync/commitFrames.d.ts +44 -0
- package/dist/sync/commitFrames.js +94 -0
- package/dist/sync/createClaimStream.d.ts +23 -22
- package/dist/sync/createClaimStream.js +108 -25
- package/dist/sync/createPresenceStream.d.ts +19 -18
- package/dist/sync/createPresenceStream.js +25 -26
- package/dist/sync/createSnapshot.d.ts +13 -17
- package/dist/sync/createSnapshot.js +20 -26
- package/dist/sync/credentialLifecycle.d.ts +175 -0
- package/dist/sync/credentialLifecycle.js +322 -0
- package/dist/sync/deltaPipeline.d.ts +113 -0
- package/dist/sync/deltaPipeline.js +261 -0
- package/dist/sync/groupChange.d.ts +113 -0
- package/dist/sync/groupChange.js +242 -0
- package/dist/sync/heartbeat.d.ts +63 -0
- package/dist/sync/heartbeat.js +91 -0
- package/dist/sync/participants.d.ts +27 -27
- package/dist/sync/schemas.d.ts +3 -2
- package/dist/sync/schemas.js +14 -10
- package/dist/sync/syncCursor.d.ts +40 -0
- package/dist/sync/syncCursor.js +55 -0
- package/dist/sync/syncPlan.d.ts +54 -0
- package/dist/sync/syncPlan.js +50 -0
- package/dist/sync/syncPosition.d.ts +54 -49
- package/dist/sync/syncPosition.js +57 -52
- package/dist/sync/wsFrameHandlers.d.ts +116 -0
- package/dist/sync/wsFrameHandlers.js +374 -0
- package/dist/testing/fixtures/bootstrap.d.ts +21 -17
- package/dist/testing/fixtures/bootstrap.js +12 -6
- package/dist/testing/fixtures/deltas.d.ts +31 -34
- package/dist/testing/fixtures/deltas.js +30 -33
- package/dist/testing/fixtures/models.d.ts +11 -10
- package/dist/testing/fixtures/models.js +12 -10
- package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
- package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
- package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -18
- package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +14 -11
- package/dist/testing/helpers/wait.d.ts +13 -8
- package/dist/testing/helpers/wait.js +13 -8
- package/dist/testing/index.d.ts +4 -4
- package/dist/testing/index.js +3 -3
- package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
- package/dist/testing/mocks/MockMutationExecutor.js +15 -14
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
- package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
- package/dist/testing/mocks/MockSyncContext.d.ts +21 -34
- package/dist/testing/mocks/MockSyncContext.js +16 -45
- package/dist/testing/mocks/MockSyncStore.d.ts +14 -14
- package/dist/testing/mocks/MockSyncStore.js +11 -11
- package/dist/testing/mocks/MockWebSocket.d.ts +28 -24
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +190 -221
- package/dist/transactions/TransactionQueue.js +424 -822
- package/dist/transactions/TransactionStore.d.ts +20 -0
- package/dist/transactions/TransactionStore.js +53 -0
- package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
- package/dist/transactions/UnconfirmedWrites.js +104 -0
- package/dist/transactions/coalesceRules.d.ts +58 -0
- package/dist/transactions/coalesceRules.js +140 -0
- package/dist/transactions/commitPayload.d.ts +130 -0
- package/dist/transactions/commitPayload.js +143 -0
- package/dist/transactions/deltaConfirmation.d.ts +58 -0
- package/dist/transactions/deltaConfirmation.js +215 -0
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/replayValidation.d.ts +99 -0
- package/dist/transactions/replayValidation.js +111 -0
- package/dist/types/global.d.ts +46 -41
- package/dist/types/global.js +20 -19
- package/dist/types/index.d.ts +74 -80
- package/dist/types/index.js +22 -27
- package/dist/types/modelData.d.ts +10 -0
- package/dist/types/modelData.js +9 -0
- package/dist/types/participant.d.ts +20 -0
- package/dist/types/participant.js +10 -0
- package/dist/types/streams.d.ts +216 -209
- package/dist/types/streams.js +7 -7
- package/dist/utils/asyncIterator.d.ts +25 -32
- package/dist/utils/asyncIterator.js +25 -32
- package/dist/utils/duration.d.ts +12 -15
- package/dist/utils/duration.js +12 -15
- package/dist/utils/mobxSetup.d.ts +53 -0
- package/dist/utils/{mobx-setup.js → mobxSetup.js} +44 -100
- package/dist/webhooks/events.d.ts +21 -16
- package/dist/webhooks/events.js +10 -8
- package/dist/webhooks/index.d.ts +5 -7
- package/dist/webhooks/index.js +5 -7
- package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
- package/dist/wire/delta.js +114 -0
- package/dist/wire/errorEnvelope.d.ts +35 -27
- package/dist/wire/errorEnvelope.js +38 -32
- package/dist/wire/frames.d.ts +150 -67
- package/dist/wire/frames.js +48 -1
- package/dist/wire/index.d.ts +18 -13
- package/dist/wire/index.js +36 -13
- package/dist/wire/listEnvelope.d.ts +16 -23
- package/dist/wire/listEnvelope.js +7 -6
- package/dist/wire/protocol.d.ts +38 -0
- package/dist/wire/protocol.js +38 -0
- package/dist/wire/protocolVersion.d.ts +60 -0
- package/dist/wire/protocolVersion.js +67 -0
- package/docs/api-keys.md +4 -3
- package/docs/coordination.md +59 -0
- package/docs/examples/existing-python-backend.md +3 -3
- package/docs/identity.md +4 -4
- package/docs/integration-guide.md +1 -1
- package/docs/react.md +1 -1
- package/docs/sessions.md +5 -7
- package/package.json +24 -21
- package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
- package/dist/ai-sdk/coordination-context.d.ts +0 -52
- package/dist/client/index.d.ts +0 -36
- package/dist/client/index.js +0 -33
- package/dist/config/index.d.ts +0 -10
- package/dist/config/index.js +0 -12
- package/dist/core/query-utils.d.ts +0 -34
- package/dist/core/query-utils.js +0 -59
- package/dist/interfaces/headless.d.ts +0 -95
- package/dist/interfaces/headless.js +0 -41
- package/dist/query/index.d.ts +0 -6
- package/dist/query/index.js +0 -5
- package/dist/realtime/index.d.ts +0 -10
- package/dist/realtime/index.js +0 -9
- package/dist/schema/plane.d.ts +0 -23
- package/dist/schema/plane.js +0 -19
- package/dist/schema/sync-delta-row.js +0 -103
- package/dist/schema/sync-delta-wire.js +0 -102
- package/dist/server/next.d.ts +0 -51
- package/dist/server/next.js +0 -47
- package/dist/server/read-config.d.ts +0 -67
- package/dist/server/read-config.js +0 -8
- package/dist/server/storage-mode.d.ts +0 -1
- package/dist/server/storage-mode.js +0 -18
- package/dist/source/connector-protocol.d.ts +0 -159
- package/dist/source/connector-protocol.js +0 -161
- package/dist/sync/OfflineFlush.d.ts +0 -9
- package/dist/sync/OfflineFlush.js +0 -22
- package/dist/sync/OfflineTransactionStore.d.ts +0 -37
- package/dist/sync/OfflineTransactionStore.js +0 -263
- package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
- package/dist/transactions/OptimisticEchoTracker.js +0 -104
- package/dist/transactions/index.d.ts +0 -16
- package/dist/transactions/index.js +0 -7
- package/dist/transactions/mutation-error-handler.d.ts +0 -5
- package/dist/transactions/mutation-error-handler.js +0 -39
- package/dist/utils/mobx-setup.d.ts +0 -42
package/dist/query/client.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP client for the
|
|
2
|
+
* The HTTP client for the sync query endpoint.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* {@link postQuery} is a small wrapper over `fetch` that POSTs a
|
|
5
|
+
* {@link QueryBatch} as JSON to `/sync/query`, attaches the bearer credential
|
|
6
|
+
* as an `Authorization` header, and parses the response into a typed
|
|
7
|
+
* {@link QueryBatchResult}. An HTTP failure is not thrown: it is logged, and
|
|
8
|
+
* every query in the batch comes back with an empty result, so a
|
|
9
|
+
* fire-and-forget caller cannot crash on an unhandled rejection.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* without duplicating the fetch boilerplate.
|
|
11
|
+
* Higher-level query helpers build on this to issue structured queries without
|
|
12
|
+
* repeating the fetch and error-handling boilerplate.
|
|
13
13
|
*/
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
import { translateHttpError } from '../errors.js';
|
|
16
|
+
import { classifyRecovery } from '../errorCodes.js';
|
|
16
17
|
import { withAuthHeaders } from '../auth/credentialSource.js';
|
|
17
18
|
import { getContext } from '../context.js';
|
|
18
19
|
// ── Response validation ─────────────────────────────────────────────────
|
|
19
20
|
//
|
|
20
|
-
// Each result slot is an array of rows
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// null.
|
|
21
|
+
// Each result slot is an array of rows, or an object for a bundled response.
|
|
22
|
+
// A per-query failure on the server surfaces here as an empty array rather
|
|
23
|
+
// than an error, so emptiness alone does not distinguish "no rows" from
|
|
24
|
+
// "the query failed." Parsing through Zod normalizes a `null` slot into an
|
|
25
|
+
// empty array, so callers never receive a raw null.
|
|
26
26
|
const QueryResultSchema = z
|
|
27
27
|
.union([z.array(z.unknown()), z.record(z.string(), z.unknown()), z.null()])
|
|
28
28
|
.transform((val) => {
|
|
@@ -36,71 +36,102 @@ const QueryBatchResultSchema = z
|
|
|
36
36
|
})
|
|
37
37
|
.passthrough();
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
40
|
-
* QueryBatchResult.
|
|
39
|
+
* Sends a batch of queries to `/sync/query` and returns the parsed
|
|
40
|
+
* {@link QueryBatchResult}. An HTTP failure is not thrown: it is logged, and
|
|
41
|
+
* every query in the batch comes back with an empty result, which keeps a
|
|
42
|
+
* fire-and-forget caller from crashing on an unhandled rejection. A 401 may
|
|
43
|
+
* first be handed to {@link PostQueryOptions.recoverCredential} for a single
|
|
44
|
+
* retry.
|
|
41
45
|
*
|
|
42
|
-
* The
|
|
43
|
-
*
|
|
44
|
-
*
|
|
46
|
+
* The response preserves order: `results[i]` corresponds to the query at
|
|
47
|
+
* `queries[i]`, so callers can rely on index alignment to pull typed results
|
|
48
|
+
* out of a multi-query batch.
|
|
45
49
|
*/
|
|
46
50
|
export async function postQuery(options, batch) {
|
|
47
51
|
const url = `${options.baseUrl}/sync/query`;
|
|
48
52
|
const timeout = options.fetchTimeout ?? 30_000;
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
// At most two attempts: the original request, plus one replay after a
|
|
54
|
+
// successful credential recovery (see `recoverCredential`). A second auth
|
|
55
|
+
// rejection falls through to the log-and-empty path, so a wedged credential
|
|
56
|
+
// can never retry-loop.
|
|
57
|
+
for (let attempt = 0;; attempt++) {
|
|
58
|
+
// Race the fetch against a timeout so hung requests don't block
|
|
59
|
+
// the calling helper indefinitely. Fresh controller per attempt.
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timer = setTimeout(() => { controller.abort(); }, timeout);
|
|
62
|
+
try {
|
|
63
|
+
// Recomputed per attempt: `withAuthHeaders` reads the live credential
|
|
64
|
+
// source, so a replay after recovery carries the freshly-minted key.
|
|
65
|
+
const headers = withAuthHeaders(options.getAuthToken, { 'Content-Type': 'application/json' }, options.capabilityToken);
|
|
66
|
+
const response = await fetch(url, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers,
|
|
69
|
+
body: JSON.stringify(batch),
|
|
70
|
+
signal: controller.signal,
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
// Build the typed AbloError for this HTTP failure (the same
|
|
74
|
+
// code-to-class map the throwing paths use) so the log carries a
|
|
75
|
+
// registry `code` — for example an authentication error with
|
|
76
|
+
// `session_expired` on a 401 — rather than a bare status. This path
|
|
77
|
+
// deliberately does not throw, since a fire-and-forget caller could
|
|
78
|
+
// crash on an unhandled rejection. It returns empty slots while still
|
|
79
|
+
// logging a legible Ablo error.
|
|
80
|
+
let body = null;
|
|
81
|
+
try {
|
|
82
|
+
body = await response.clone().json();
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// non-JSON error page — translateHttpError falls back to status text
|
|
86
|
+
}
|
|
87
|
+
const err = translateHttpError(response.status, body);
|
|
88
|
+
// On a 401, hand the failure to the recovery hook once. The recovery
|
|
89
|
+
// class routes the outcome: an expired access credential is re-minted
|
|
90
|
+
// and the request replays; a lost session is terminal and stops here;
|
|
91
|
+
// anything else stops. A bare 401 with no readable code is treated as
|
|
92
|
+
// an expired access credential, so the only terminal path is the
|
|
93
|
+
// re-mint itself coming back empty, never an ambiguous status.
|
|
94
|
+
if (attempt === 0 && response.status === 401 && options.recoverCredential) {
|
|
95
|
+
const recovery = typeof err.code === 'string'
|
|
96
|
+
? classifyRecovery(err.code)
|
|
97
|
+
: 'access_credential_expiry';
|
|
98
|
+
const outcome = await options.recoverCredential(recovery);
|
|
99
|
+
if (outcome === 'retry') {
|
|
100
|
+
getContext().logger.debug('[postQuery] credential recovered — replaying query once', {
|
|
101
|
+
code: err.code ?? response.status,
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Logged through the level-gated logger, so it honors ABLO_LOG_LEVEL:
|
|
107
|
+
// a `warn` line the app developer can read (the models, the typed
|
|
108
|
+
// message, and a wire `code`), with the forensic detail on a companion
|
|
109
|
+
// `debug` line. The read stays empty until the underlying cause, such
|
|
110
|
+
// as auth or network, is resolved.
|
|
111
|
+
const models = batch.queries.map((q) => q.model).join(', ');
|
|
112
|
+
getContext().logger.warn(`Could not load ${models} — ${err.message} (code: ${err.code ?? response.status}). No results were returned.`);
|
|
113
|
+
getContext().logger.debug('[postQuery.error] query http failure', {
|
|
114
|
+
type: err.type,
|
|
115
|
+
code: err.code ?? response.status,
|
|
116
|
+
models,
|
|
117
|
+
message: err.message,
|
|
118
|
+
});
|
|
119
|
+
return { results: batch.queries.map(() => []) };
|
|
76
120
|
}
|
|
77
|
-
|
|
78
|
-
|
|
121
|
+
const raw = await response.json();
|
|
122
|
+
const parsed = QueryBatchResultSchema.safeParse(raw);
|
|
123
|
+
if (!parsed.success) {
|
|
124
|
+
// A malformed server response isn't something the consumer can act on
|
|
125
|
+
// (server/protocol issue) → debug, gated like everything else.
|
|
126
|
+
getContext().logger.debug('[postQuery.error] malformed response', {
|
|
127
|
+
issues: parsed.error.issues,
|
|
128
|
+
});
|
|
129
|
+
return { results: batch.queries.map(() => []) };
|
|
79
130
|
}
|
|
80
|
-
|
|
81
|
-
const models = batch.queries.map((q) => q.model).join(', ');
|
|
82
|
-
getContext().logger.warn(`Could not load ${models} — ${err.message} (code: ${err.code ?? response.status}). No results were returned.`);
|
|
83
|
-
getContext().logger.debug('[postQuery.error] query http failure', {
|
|
84
|
-
type: err.type,
|
|
85
|
-
code: err.code ?? response.status,
|
|
86
|
-
models,
|
|
87
|
-
message: err.message,
|
|
88
|
-
});
|
|
89
|
-
return { results: batch.queries.map(() => []) };
|
|
131
|
+
return parsed.data;
|
|
90
132
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!parsed.success) {
|
|
94
|
-
// A malformed server response isn't something the consumer can act on
|
|
95
|
-
// (server/protocol issue) → debug, gated like everything else.
|
|
96
|
-
getContext().logger.debug('[postQuery.error] malformed response', {
|
|
97
|
-
issues: parsed.error.issues,
|
|
98
|
-
});
|
|
99
|
-
return { results: batch.queries.map(() => []) };
|
|
133
|
+
finally {
|
|
134
|
+
clearTimeout(timer);
|
|
100
135
|
}
|
|
101
|
-
return parsed.data;
|
|
102
|
-
}
|
|
103
|
-
finally {
|
|
104
|
-
clearTimeout(timer);
|
|
105
136
|
}
|
|
106
137
|
}
|
package/dist/query/types.d.ts
CHANGED
|
@@ -1,44 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Structured query types for the
|
|
2
|
+
* Structured query types for the `/sync/query` endpoint.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A query describes a filtered read in a compact wire format. `where` is a flat
|
|
5
|
+
* list of `[column, operator, value]` conditions combined with AND, and
|
|
6
|
+
* `related` names the schema relations to fetch alongside each row. The server
|
|
7
|
+
* compiles a query against your schema: it reads the model's relation metadata
|
|
8
|
+
* to turn `related: ['layers']` into the right join, and turns each condition
|
|
9
|
+
* into a WHERE fragment. The protocol carries no model-specific logic, so
|
|
10
|
+
* adding a model or relation is a schema change rather than a server change.
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* plus a PK-only `ids: string[]` batch field. That worked for simple cases
|
|
13
|
-
* but collapsed on two real workloads:
|
|
14
|
-
*
|
|
15
|
-
* 1. "Fetch all layers for these slide IDs" — the IDs are foreign keys
|
|
16
|
-
* (`SlideLayer.slideId`), not primary keys. The old `ids` field
|
|
17
|
-
* filtered on `id`, silently returning empty.
|
|
18
|
-
*
|
|
19
|
-
* 2. "Fetch all layers for this deck" — needs a JOIN through
|
|
20
|
-
* `slides.deck_id → slide_layers.slide_id`. Equality-only `where` had
|
|
21
|
-
* no way to express it, so the Go server hardcoded a dispatch case.
|
|
22
|
-
*
|
|
23
|
-
* Both are generic patterns ("batch by FK column", "filter via relation")
|
|
24
|
-
* that should be first-class in the protocol, not model-specific escape
|
|
25
|
-
* hatches on the server. This shape matches Zero's ZQL:
|
|
26
|
-
*
|
|
27
|
-
* - `where('slideId', 'IN', ids)` → `['slideId', 'IN', ids]`
|
|
28
|
-
* - `.related('layers')` → `related: ['layers']`
|
|
29
|
-
*
|
|
30
|
-
* The server's compiler stays schema-driven: given a model name, it reads
|
|
31
|
-
* the schema's declared relations to emit JOIN SQL, and given a `[col, op,
|
|
32
|
-
* val]` tuple it emits a WHERE fragment — never a switch on specific model
|
|
33
|
-
* names. Adding a new model or relation is a schema change, not a server
|
|
34
|
-
* change.
|
|
12
|
+
* The `IN` operator lets you batch a read by any column, including a foreign
|
|
13
|
+
* key — for example, fetching every layer whose `slideId` falls in a set of
|
|
14
|
+
* ids.
|
|
35
15
|
*/
|
|
36
16
|
/** Primitive operand types allowed in a where clause. */
|
|
37
17
|
export type WherePrimitive = string | number | boolean | null;
|
|
38
18
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
19
|
+
* The comparison operators a {@link WhereClause} may use: equality and
|
|
20
|
+
* inequality, ordering, set membership (`IN` / `NOT IN`), null checks
|
|
21
|
+
* (`IS` / `IS NOT`), and case-sensitive or case-insensitive pattern
|
|
22
|
+
* matching (`LIKE`, `ILIKE`, and their negations).
|
|
42
23
|
*/
|
|
43
24
|
export type WhereOp = '=' | '!=' | '<' | '<=' | '>' | '>=' | 'IN' | 'NOT IN' | 'IS' | 'IS NOT' | 'LIKE' | 'NOT LIKE' | 'ILIKE' | 'NOT ILIKE';
|
|
44
25
|
/**
|
|
@@ -77,20 +58,17 @@ export interface Query {
|
|
|
77
58
|
*/
|
|
78
59
|
model: string;
|
|
79
60
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* primary-key-only `ids` field is subsumed by this form.
|
|
61
|
+
* The conditions to filter by, combined with AND. Empty or omitted returns
|
|
62
|
+
* every row the caller may see; reads are still scoped to the caller's
|
|
63
|
+
* organization on the server. Use `['col', 'IN', values]` to batch a read by
|
|
64
|
+
* any column.
|
|
85
65
|
*/
|
|
86
66
|
where?: readonly WhereClause[];
|
|
87
67
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* Results come back as nested objects under the relation key:
|
|
68
|
+
* The relations to fetch with each row, named as they are declared on this
|
|
69
|
+
* model in the schema. The server resolves each name from the schema's
|
|
70
|
+
* relation metadata and joins the related rows in. They come back nested
|
|
71
|
+
* under the relation key:
|
|
94
72
|
*
|
|
95
73
|
* { __typename: 'Slide', id: '…', layers: [{ __typename: 'SlideLayer', … }] }
|
|
96
74
|
*/
|
|
@@ -119,25 +97,24 @@ export interface QueryBatch {
|
|
|
119
97
|
/** Response body from POST /sync/query. */
|
|
120
98
|
export interface QueryBatchResult {
|
|
121
99
|
/**
|
|
122
|
-
*
|
|
123
|
-
* `queries[i]`. Each element is an array of rows
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* any `related` keys nested under the row.
|
|
100
|
+
* The result of each query, in request order: `results[i]` corresponds to
|
|
101
|
+
* `queries[i]`. Each element is an array of rows, or an object bundling
|
|
102
|
+
* several named row collections when the source returns more than one. Every
|
|
103
|
+
* row carries a `__typename` field naming its model, so callers can dispatch
|
|
104
|
+
* on it, along with any requested `related` rows nested under their relation
|
|
105
|
+
* key.
|
|
129
106
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
107
|
+
* A query that fails on the server comes back as an empty array, which is
|
|
108
|
+
* indistinguishable from a query that simply matched nothing — treat an empty
|
|
109
|
+
* result as "no rows", not as proof the query succeeded. The element type is
|
|
110
|
+
* `unknown` because one batch can mix row shapes, so narrow each result
|
|
111
|
+
* before use.
|
|
135
112
|
*/
|
|
136
113
|
results: unknown[];
|
|
137
114
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
115
|
+
* The server watermark observed after the batch ran. Model reads expose this
|
|
116
|
+
* as `stamp`, and callers thread it into `commits.create({ readAt })` so the
|
|
117
|
+
* server can reject a write built on stale data.
|
|
141
118
|
*/
|
|
142
119
|
lastSyncId?: number;
|
|
143
120
|
}
|
package/dist/query/types.js
CHANGED
|
@@ -1,36 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Structured query types for the
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* 1. "Fetch all layers for these slide IDs" — the IDs are foreign keys
|
|
16
|
-
* (`SlideLayer.slideId`), not primary keys. The old `ids` field
|
|
17
|
-
* filtered on `id`, silently returning empty.
|
|
18
|
-
*
|
|
19
|
-
* 2. "Fetch all layers for this deck" — needs a JOIN through
|
|
20
|
-
* `slides.deck_id → slide_layers.slide_id`. Equality-only `where` had
|
|
21
|
-
* no way to express it, so the Go server hardcoded a dispatch case.
|
|
22
|
-
*
|
|
23
|
-
* Both are generic patterns ("batch by FK column", "filter via relation")
|
|
24
|
-
* that should be first-class in the protocol, not model-specific escape
|
|
25
|
-
* hatches on the server. This shape matches Zero's ZQL:
|
|
26
|
-
*
|
|
27
|
-
* - `where('slideId', 'IN', ids)` → `['slideId', 'IN', ids]`
|
|
28
|
-
* - `.related('layers')` → `related: ['layers']`
|
|
29
|
-
*
|
|
30
|
-
* The server's compiler stays schema-driven: given a model name, it reads
|
|
31
|
-
* the schema's declared relations to emit JOIN SQL, and given a `[col, op,
|
|
32
|
-
* val]` tuple it emits a WHERE fragment — never a switch on specific model
|
|
33
|
-
* names. Adding a new model or relation is a schema change, not a server
|
|
34
|
-
* change.
|
|
2
|
+
* Structured query types for the `/sync/query` endpoint.
|
|
3
|
+
*
|
|
4
|
+
* A query describes a filtered read in a compact wire format. `where` is a flat
|
|
5
|
+
* list of `[column, operator, value]` conditions combined with AND, and
|
|
6
|
+
* `related` names the schema relations to fetch alongside each row. The server
|
|
7
|
+
* compiles a query against your schema: it reads the model's relation metadata
|
|
8
|
+
* to turn `related: ['layers']` into the right join, and turns each condition
|
|
9
|
+
* into a WHERE fragment. The protocol carries no model-specific logic, so
|
|
10
|
+
* adding a model or relation is a schema change rather than a server change.
|
|
11
|
+
*
|
|
12
|
+
* The `IN` operator lets you batch a read by any column, including a foreign
|
|
13
|
+
* key — for example, fetching every layer whose `slideId` falls in a set of
|
|
14
|
+
* ids.
|
|
35
15
|
*/
|
|
36
16
|
export {};
|
|
@@ -34,13 +34,9 @@ import { type SyncStoreContract } from './context.js';
|
|
|
34
34
|
*
|
|
35
35
|
* ```tsx
|
|
36
36
|
* // Build once at module scope — a new instance per render tears down the socket.
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* fetch('/api/ablo-session', { method: 'POST' })
|
|
41
|
-
* .then((r) => r.json())
|
|
42
|
-
* .then((d) => d.token),
|
|
43
|
-
* });
|
|
37
|
+
* // The endpoint string points at your session-mint route (`ablo init`
|
|
38
|
+
* // scaffolds it); the SDK fetches it and keeps the token fresh.
|
|
39
|
+
* const ablo = Ablo({ schema, authEndpoint: '/api/ablo-session' });
|
|
44
40
|
*
|
|
45
41
|
* <AbloProvider client={ablo}>
|
|
46
42
|
* <App />
|
|
@@ -152,9 +148,9 @@ export type MeshParticipantStatus = ParticipantStatus;
|
|
|
152
148
|
export interface UseWatchReturn {
|
|
153
149
|
readonly participant: EngineParticipant | null;
|
|
154
150
|
/** Everyone else on the engine's sync groups (`participant.presence.others`), bridged to React. */
|
|
155
|
-
readonly peers:
|
|
151
|
+
readonly peers: readonly Peer[];
|
|
156
152
|
/** Active claim claims by peers (`participant.claims.others`), bridged to React. */
|
|
157
|
-
readonly claims:
|
|
153
|
+
readonly claims: readonly Claim[];
|
|
158
154
|
readonly status: ParticipantStatus;
|
|
159
155
|
readonly error: Error | null;
|
|
160
156
|
}
|
|
@@ -196,7 +192,7 @@ export declare function useWatch(opts: UseWatchOptions): UseWatchReturn;
|
|
|
196
192
|
* const alone = !peers.some((p) => p.participantKind === 'user');
|
|
197
193
|
* ```
|
|
198
194
|
*/
|
|
199
|
-
export declare function usePeers(scope?: ParticipantScope):
|
|
195
|
+
export declare function usePeers(scope?: ParticipantScope): readonly Peer[];
|
|
200
196
|
/**
|
|
201
197
|
* Returns the raw `SyncEngine` proxy. Typically you want the typed
|
|
202
198
|
* hooks (`useQuery`, `useOne`, `useMutate`) — this is for rare cases
|
|
@@ -217,7 +213,7 @@ export declare function useSync<R extends SchemaRecord = SchemaRecord>(): Ablo<R
|
|
|
217
213
|
/**
|
|
218
214
|
* Returns the underlying `SyncStoreContract` (the BaseSyncedStore).
|
|
219
215
|
* Most consumers should prefer the typed hooks (`useQuery` etc.); this
|
|
220
|
-
* is for advanced cases like direct
|
|
216
|
+
* is for advanced cases like direct InstanceCache access or custom
|
|
221
217
|
* reactive bridges. Throws if the provider hasn't mounted the store
|
|
222
218
|
* yet — wrap consumers in `<ClientSideSuspense>` to gate correctly.
|
|
223
219
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, useState, createContext, } from 'react';
|
|
4
|
+
import { Ablo } from '../client/Ablo.js';
|
|
4
5
|
import { createParticipantClaimId, parseParticipantTtlSeconds, resolveParticipantSyncGroups, } from '../sync/participants.js';
|
|
5
6
|
import { SyncContext } from './context.js';
|
|
6
7
|
import { AbloInternalContext } from './internalContext.js';
|
|
@@ -77,18 +78,24 @@ export function AbloProvider(props) {
|
|
|
77
78
|
// It does NOT dispose the client (consumer-owned) and does NOT touch auth.
|
|
78
79
|
useEffect(() => {
|
|
79
80
|
let stale = false;
|
|
80
|
-
const unsubscribeSession = engine.onSessionError(
|
|
81
|
+
const unsubscribeSession = engine.onSessionError((err) => {
|
|
81
82
|
errorEmitter.emit(err);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
void (async () => {
|
|
84
|
+
try {
|
|
85
|
+
await engine.purge();
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
try {
|
|
89
|
+
await onSessionExpiredRef.current?.();
|
|
90
|
+
}
|
|
91
|
+
catch (hookErr) {
|
|
92
|
+
errorEmitter.emit(hookErr);
|
|
93
|
+
}
|
|
94
|
+
})().catch(() => {
|
|
95
|
+
// Only a throwing errorEmitter subscriber can land here — it was
|
|
96
|
+
// already the error-reporting path, so swallow rather than surface
|
|
97
|
+
// an unhandled rejection loop.
|
|
98
|
+
});
|
|
92
99
|
});
|
|
93
100
|
engine
|
|
94
101
|
.ready()
|
|
@@ -122,7 +129,7 @@ export function AbloProvider(props) {
|
|
|
122
129
|
}
|
|
123
130
|
};
|
|
124
131
|
window.addEventListener('beforeunload', handler);
|
|
125
|
-
return () => window.removeEventListener('beforeunload', handler);
|
|
132
|
+
return () => { window.removeEventListener('beforeunload', handler); };
|
|
126
133
|
}, [engine, preventUnsavedChanges]);
|
|
127
134
|
// ── SyncContext value (for useQuery/useOne/useMutate hooks) ──────
|
|
128
135
|
//
|
|
@@ -259,7 +266,7 @@ export function useWatch(opts) {
|
|
|
259
266
|
// Subscribe the connection to the scope's sync groups while mounted +
|
|
260
267
|
// connected — the area-of-interest navigation primitive. No claim, no
|
|
261
268
|
// TTL: a viewer just receives the scope's deltas. Hysteresis (warm TTL)
|
|
262
|
-
// lives in the store's
|
|
269
|
+
// lives in the store's SubscriptionManager, so a quick unmount/remount
|
|
263
270
|
// (tab flip) doesn't re-bootstrap.
|
|
264
271
|
useEffect(() => {
|
|
265
272
|
const scope = opts.scope;
|
|
@@ -389,7 +396,7 @@ export function usePeers(scope) {
|
|
|
389
396
|
// rarely; a frame of stale presence is harmless (same rationale as
|
|
390
397
|
// useWatch's peers bridge).
|
|
391
398
|
setPeers(compute());
|
|
392
|
-
return presence.onChange(() => setPeers(compute()));
|
|
399
|
+
return presence.onChange(() => { setPeers(compute()); });
|
|
393
400
|
}, [engine, scopeKey]);
|
|
394
401
|
return peers;
|
|
395
402
|
}
|
|
@@ -424,7 +431,7 @@ export function useSync() {
|
|
|
424
431
|
/**
|
|
425
432
|
* Returns the underlying `SyncStoreContract` (the BaseSyncedStore).
|
|
426
433
|
* Most consumers should prefer the typed hooks (`useQuery` etc.); this
|
|
427
|
-
* is for advanced cases like direct
|
|
434
|
+
* is for advanced cases like direct InstanceCache access or custom
|
|
428
435
|
* reactive bridges. Throws if the provider hasn't mounted the store
|
|
429
436
|
* yet — wrap consumers in `<ClientSideSuspense>` to gate correctly.
|
|
430
437
|
*
|
|
@@ -442,7 +449,7 @@ export function useSync() {
|
|
|
442
449
|
*/
|
|
443
450
|
export function useSyncStore() {
|
|
444
451
|
const sync = useContext(SyncContext);
|
|
445
|
-
if (!sync
|
|
452
|
+
if (!sync?.store) {
|
|
446
453
|
throw new AbloValidationError('useSyncStore: the sync engine has not yet initialized. Wrap ' +
|
|
447
454
|
'consumers in <ClientSideSuspense> or guard on useSyncStatus().', { code: 'sync_not_ready' });
|
|
448
455
|
}
|