@abloatai/ablo 0.26.0 → 0.28.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/CHANGELOG.md +42 -2
- package/README.md +102 -86
- package/dist/BaseSyncedStore.d.ts +85 -88
- package/dist/BaseSyncedStore.js +134 -151
- package/dist/Database.d.ts +68 -69
- package/dist/Database.js +316 -135
- package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
- package/dist/{ObjectPool.js → InstanceCache.js} +85 -83
- package/dist/LazyReferenceCollection.d.ts +11 -15
- package/dist/LazyReferenceCollection.js +12 -16
- package/dist/Model.d.ts +54 -52
- package/dist/Model.js +78 -62
- package/dist/ModelRegistry.d.ts +21 -19
- package/dist/ModelRegistry.js +23 -27
- package/dist/NetworkMonitor.d.ts +5 -6
- package/dist/NetworkMonitor.js +5 -6
- package/dist/SyncClient.d.ts +122 -118
- package/dist/SyncClient.js +541 -245
- package/dist/adapters/alwaysOnline.d.ts +6 -8
- package/dist/adapters/alwaysOnline.js +6 -8
- package/dist/adapters/inMemoryStorage.d.ts +10 -9
- package/dist/adapters/inMemoryStorage.js +21 -9
- package/dist/agent/Agent.d.ts +27 -32
- package/dist/agent/Agent.js +18 -19
- 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} +26 -33
- package/dist/ai-sdk/index.d.ts +25 -22
- package/dist/ai-sdk/index.js +25 -22
- package/dist/ai-sdk/wrap.d.ts +6 -7
- package/dist/ai-sdk/wrap.js +1 -1
- package/dist/auth/credentialPolicy.d.ts +69 -74
- package/dist/auth/credentialPolicy.js +51 -56
- package/dist/auth/credentialSource.d.ts +6 -5
- package/dist/auth/credentialSource.js +9 -10
- package/dist/auth/index.d.ts +59 -58
- package/dist/auth/index.js +31 -37
- 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 +173 -121
- package/dist/client/Ablo.d.ts +97 -74
- package/dist/client/Ablo.js +129 -163
- package/dist/client/ApiClient.d.ts +30 -19
- package/dist/client/ApiClient.js +442 -81
- package/dist/client/auth.d.ts +47 -47
- package/dist/client/auth.js +108 -117
- package/dist/client/claimHeartbeatLoop.d.ts +50 -0
- package/dist/client/claimHeartbeatLoop.js +88 -0
- package/dist/client/consoleLogger.d.ts +5 -6
- package/dist/client/consoleLogger.js +5 -6
- package/dist/client/createInternalComponents.d.ts +16 -17
- package/dist/client/createInternalComponents.js +26 -31
- package/dist/client/createModelProxy.d.ts +130 -120
- package/dist/client/createModelProxy.js +152 -122
- package/dist/client/credentialEndpoint.d.ts +40 -42
- package/dist/client/credentialEndpoint.js +35 -36
- package/dist/client/functionalUpdate.d.ts +29 -27
- package/dist/client/functionalUpdate.js +21 -21
- package/dist/client/hostedEndpoints.d.ts +9 -12
- package/dist/client/hostedEndpoints.js +9 -12
- package/dist/client/httpClient.d.ts +59 -53
- package/dist/client/httpClient.js +29 -31
- package/dist/client/identity.d.ts +15 -20
- package/dist/client/identity.js +47 -58
- package/dist/client/modelRegistration.d.ts +5 -9
- package/dist/client/modelRegistration.js +78 -87
- package/dist/client/options.d.ts +157 -157
- package/dist/client/options.js +3 -7
- package/dist/client/registerDataSource.d.ts +9 -9
- package/dist/client/registerDataSource.js +15 -16
- package/dist/client/resourceTypes.d.ts +64 -75
- package/dist/client/resourceTypes.js +4 -10
- package/dist/client/schemaConfig.d.ts +31 -43
- package/dist/client/schemaConfig.js +38 -50
- package/dist/client/sessionMint.d.ts +16 -12
- package/dist/client/sessionMint.js +26 -31
- package/dist/client/validateAbloOptions.d.ts +12 -14
- package/dist/client/validateAbloOptions.js +8 -9
- package/dist/client/writeOptionsSchema.d.ts +18 -16
- package/dist/client/writeOptionsSchema.js +23 -20
- package/dist/client/wsMutationExecutor.d.ts +16 -20
- package/dist/client/wsMutationExecutor.js +18 -23
- package/dist/commit/contract.d.ts +493 -0
- package/dist/commit/contract.js +187 -0
- package/dist/commit/index.d.ts +6 -0
- package/dist/commit/index.js +5 -0
- package/dist/context.d.ts +6 -4
- package/dist/context.js +6 -4
- package/dist/coordination/index.d.ts +10 -8
- package/dist/coordination/index.js +14 -12
- package/dist/coordination/schema.d.ts +176 -128
- package/dist/coordination/schema.js +197 -133
- package/dist/coordination/trace.d.ts +9 -10
- package/dist/coordination/trace.js +13 -14
- package/dist/core/DatabaseManager.d.ts +5 -7
- package/dist/core/DatabaseManager.js +15 -19
- package/dist/core/QueryProcessor.d.ts +7 -9
- package/dist/core/QueryProcessor.js +22 -28
- package/dist/core/QueryView.d.ts +8 -8
- package/dist/core/QueryView.js +2 -2
- package/dist/core/StoreManager.d.ts +14 -14
- package/dist/core/StoreManager.js +33 -24
- package/dist/core/ViewRegistry.d.ts +5 -5
- package/dist/core/ViewRegistry.js +4 -4
- package/dist/core/index.d.ts +17 -12
- package/dist/core/index.js +32 -26
- package/dist/core/openIDBWithTimeout.d.ts +38 -36
- package/dist/core/openIDBWithTimeout.js +42 -43
- package/dist/core/queryUtils.d.ts +45 -0
- package/dist/core/queryUtils.js +69 -0
- package/dist/core/storeContract.d.ts +63 -61
- package/dist/core/storeContract.js +8 -12
- package/dist/environment.d.ts +28 -0
- package/dist/environment.js +21 -0
- package/dist/errorCodes.d.ts +107 -99
- package/dist/errorCodes.js +137 -134
- package/dist/errors.d.ts +160 -166
- package/dist/errors.js +155 -158
- package/dist/index.d.ts +36 -27
- package/dist/index.js +91 -86
- package/dist/interfaces/index.d.ts +102 -113
- package/dist/interfaces/index.js +5 -4
- package/dist/keys/index.d.ts +27 -29
- package/dist/keys/index.js +41 -40
- 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 +124 -131
- package/dist/mutators/UndoManager.js +177 -156
- package/dist/mutators/defineMutators.d.ts +23 -34
- 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 +104 -100
- package/dist/policy/types.js +67 -66
- package/dist/query/client.d.ts +28 -23
- package/dist/query/client.js +45 -43
- package/dist/query/types.d.ts +37 -60
- package/dist/query/types.js +13 -33
- package/dist/react/AbloProvider.d.ts +1 -1
- package/dist/react/AbloProvider.js +2 -2
- package/dist/react/context.d.ts +25 -28
- 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 +28 -25
- package/dist/react/useAblo.js +41 -17
- 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 +10 -11
- package/dist/react/useMutationFailureListener.d.ts +8 -8
- package/dist/react/useMutationFailureListener.js +8 -8
- package/dist/react/useMutators.d.ts +11 -11
- package/dist/react/useMutators.js +3 -3
- package/dist/react/useReactive.js +2 -2
- package/dist/react/useSyncStatus.d.ts +4 -6
- package/dist/react/useUndoScope.d.ts +7 -9
- package/dist/react/useUndoScope.js +1 -1
- 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 +20 -24
- package/dist/schema/ddlLock.js +18 -23
- 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 +35 -48
- package/dist/schema/generate.d.ts +12 -12
- package/dist/schema/generate.js +12 -12
- package/dist/schema/index.d.ts +3 -3
- package/dist/schema/index.js +21 -23
- package/dist/schema/model.d.ts +118 -143
- package/dist/schema/model.js +22 -33
- package/dist/schema/openapi.d.ts +10 -9
- package/dist/schema/openapi.js +5 -3
- package/dist/schema/queries.d.ts +29 -31
- package/dist/schema/queries.js +23 -25
- package/dist/schema/relation.d.ts +89 -99
- package/dist/schema/relation.js +13 -13
- package/dist/schema/residency.d.ts +16 -13
- package/dist/schema/residency.js +16 -13
- package/dist/schema/roles.d.ts +36 -43
- package/dist/schema/roles.js +31 -37
- package/dist/schema/schema.d.ts +64 -43
- package/dist/schema/schema.js +31 -32
- package/dist/schema/select.d.ts +13 -13
- package/dist/schema/select.js +13 -13
- package/dist/schema/serialize.d.ts +28 -31
- 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} +26 -49
- 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 +30 -25
- package/dist/source/adapter.js +10 -10
- package/dist/source/adapters/drizzle.d.ts +28 -23
- package/dist/source/adapters/drizzle.js +30 -25
- package/dist/source/adapters/kysely.d.ts +27 -25
- package/dist/source/adapters/kysely.js +24 -23
- package/dist/source/adapters/memory.d.ts +8 -7
- package/dist/source/adapters/memory.js +9 -8
- package/dist/source/adapters/prisma.d.ts +13 -12
- package/dist/source/adapters/prisma.js +22 -25
- package/dist/source/conformance.d.ts +18 -11
- package/dist/source/conformance.js +17 -11
- package/dist/source/connector.d.ts +31 -32
- package/dist/source/connector.js +28 -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 +46 -58
- package/dist/source/factory.js +22 -27
- package/dist/source/index.d.ts +7 -9
- package/dist/source/index.js +12 -14
- package/dist/source/migrations.d.ts +9 -9
- package/dist/source/migrations.js +9 -9
- package/dist/source/next.d.ts +9 -10
- package/dist/source/next.js +6 -7
- package/dist/source/pushQueue.d.ts +69 -47
- package/dist/source/pushQueue.js +32 -28
- package/dist/source/signing.d.ts +46 -17
- package/dist/source/signing.js +28 -11
- package/dist/source/types.d.ts +121 -104
- package/dist/source/types.js +13 -14
- package/dist/stores/ObjectStore.d.ts +24 -12
- package/dist/stores/ObjectStore.js +38 -16
- package/dist/stores/ObjectStoreContract.d.ts +14 -15
- package/dist/stores/SyncActionStore.d.ts +7 -11
- package/dist/stores/SyncActionStore.js +13 -17
- package/dist/surface.d.ts +28 -21
- package/dist/surface.js +29 -20
- package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +36 -42
- package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +76 -76
- package/dist/sync/ConnectionManager.d.ts +39 -50
- package/dist/sync/ConnectionManager.js +55 -66
- package/dist/sync/NetworkProbe.d.ts +24 -29
- package/dist/sync/NetworkProbe.js +63 -69
- package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +42 -41
- package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +59 -54
- package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +43 -57
- package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +43 -51
- package/dist/sync/SyncWebSocket.d.ts +141 -166
- package/dist/sync/SyncWebSocket.js +191 -223
- package/dist/sync/awaitClaimGrant.d.ts +18 -18
- package/dist/sync/awaitClaimGrant.js +11 -11
- package/dist/sync/bootstrapApply.d.ts +34 -24
- package/dist/sync/bootstrapApply.js +27 -19
- package/dist/sync/commitFrames.d.ts +21 -20
- package/dist/sync/commitFrames.js +18 -18
- package/dist/sync/createClaimStream.d.ts +23 -22
- package/dist/sync/createClaimStream.js +105 -23
- package/dist/sync/createPresenceStream.d.ts +19 -18
- package/dist/sync/createPresenceStream.js +25 -26
- package/dist/sync/createSnapshot.d.ts +12 -14
- package/dist/sync/createSnapshot.js +20 -26
- package/dist/sync/credentialLifecycle.d.ts +104 -104
- package/dist/sync/credentialLifecycle.js +140 -147
- package/dist/sync/deltaPipeline.d.ts +36 -34
- package/dist/sync/deltaPipeline.js +64 -65
- package/dist/sync/groupChange.d.ts +63 -61
- package/dist/sync/groupChange.js +74 -78
- package/dist/sync/heartbeat.d.ts +34 -33
- package/dist/sync/heartbeat.js +31 -31
- package/dist/sync/participants.d.ts +19 -19
- package/dist/sync/persistedPrefix.d.ts +12 -0
- package/dist/sync/persistedPrefix.js +22 -0
- package/dist/sync/schemas.d.ts +3 -2
- package/dist/sync/schemas.js +14 -10
- package/dist/sync/syncCursor.d.ts +17 -21
- package/dist/sync/syncCursor.js +17 -21
- package/dist/sync/syncPlan.d.ts +28 -36
- package/dist/sync/syncPlan.js +18 -19
- package/dist/sync/syncPosition.d.ts +54 -49
- package/dist/sync/syncPosition.js +57 -52
- package/dist/sync/wsFrameHandlers.d.ts +35 -36
- package/dist/sync/wsFrameHandlers.js +63 -67
- package/dist/testing/fixtures/bootstrap.d.ts +12 -6
- package/dist/testing/fixtures/bootstrap.js +12 -6
- package/dist/testing/fixtures/deltas.d.ts +30 -33
- package/dist/testing/fixtures/deltas.js +30 -33
- package/dist/testing/fixtures/models.d.ts +11 -10
- package/dist/testing/fixtures/models.js +11 -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 -15
- package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +12 -10
- package/dist/testing/helpers/wait.d.ts +13 -8
- package/dist/testing/helpers/wait.js +13 -8
- package/dist/testing/index.d.ts +5 -3
- package/dist/testing/index.js +3 -2
- package/dist/testing/mocks/FakeDatabase.d.ts +18 -0
- package/dist/testing/mocks/FakeDatabase.js +10 -0
- 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 +20 -17
- package/dist/testing/mocks/MockSyncContext.js +15 -13
- package/dist/testing/mocks/MockSyncStore.d.ts +10 -10
- package/dist/testing/mocks/MockSyncStore.js +11 -11
- package/dist/testing/mocks/MockWebSocket.d.ts +28 -23
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +244 -181
- package/dist/transactions/TransactionQueue.js +929 -423
- package/dist/transactions/TransactionStore.d.ts +6 -4
- package/dist/transactions/TransactionStore.js +6 -4
- package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
- package/dist/transactions/UnconfirmedWrites.js +104 -0
- package/dist/transactions/coalesceRules.d.ts +41 -17
- package/dist/transactions/coalesceRules.js +40 -17
- package/dist/transactions/commitEnvelope.d.ts +132 -0
- package/dist/transactions/commitEnvelope.js +139 -0
- package/dist/transactions/commitOutboxStore.d.ts +32 -0
- package/dist/transactions/commitOutboxStore.js +26 -0
- package/dist/transactions/commitPayload.d.ts +63 -52
- package/dist/transactions/commitPayload.js +54 -57
- package/dist/transactions/deltaConfirmation.d.ts +20 -22
- package/dist/transactions/deltaConfirmation.js +37 -45
- package/dist/transactions/httpCommitEnvelope.d.ts +43 -0
- package/dist/transactions/httpCommitEnvelope.js +179 -0
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/replayValidation.d.ts +182 -0
- package/dist/transactions/replayValidation.js +156 -0
- package/dist/types/global.d.ts +46 -41
- package/dist/types/global.js +20 -19
- package/dist/types/index.d.ts +71 -77
- package/dist/types/index.js +22 -22
- package/dist/types/modelData.d.ts +6 -8
- package/dist/types/modelData.js +5 -7
- package/dist/types/participant.d.ts +10 -11
- package/dist/types/participant.js +6 -8
- package/dist/types/streams.d.ts +208 -195
- 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} +42 -98
- 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/wire/bootstrapReason.d.ts +9 -0
- package/dist/wire/bootstrapReason.js +8 -0
- 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 +30 -31
- package/dist/wire/errorEnvelope.js +34 -40
- package/dist/wire/frames.d.ts +315 -86
- package/dist/wire/frames.js +47 -33
- package/dist/wire/index.d.ts +18 -14
- package/dist/wire/index.js +32 -27
- package/dist/wire/listEnvelope.d.ts +16 -23
- package/dist/wire/listEnvelope.js +7 -6
- package/dist/wire/protocol.d.ts +25 -32
- package/dist/wire/protocol.js +25 -32
- package/dist/wire/protocolVersion.d.ts +44 -40
- package/dist/wire/protocolVersion.js +44 -40
- package/docs/api.md +10 -10
- package/docs/coordination.md +59 -0
- package/docs/mcp.md +1 -1
- package/package.json +17 -11
- package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
- package/dist/ai-sdk/coordination-context.d.ts +0 -52
- package/dist/core/query-utils.d.ts +0 -34
- package/dist/core/query-utils.js +0 -59
- package/dist/schema/sync-delta-row.js +0 -103
- package/dist/schema/sync-delta-wire.js +0 -102
- 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 -8
- package/dist/server/storage-mode.js +0 -28
- package/dist/source/connector-protocol.d.ts +0 -159
- package/dist/source/connector-protocol.js +0 -161
- package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
- package/dist/transactions/OptimisticEchoTracker.js +0 -104
- package/dist/transactions/mutation-error-handler.d.ts +0 -5
- package/dist/transactions/mutation-error-handler.js +0 -39
- package/dist/transactions/optimistic.d.ts +0 -24
- package/dist/transactions/optimistic.js +0 -45
- package/dist/transactions/persistedReplay.d.ts +0 -93
- package/dist/transactions/persistedReplay.js +0 -105
- package/dist/utils/mobx-setup.d.ts +0 -42
package/dist/react/index.js
CHANGED
|
@@ -1,48 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* React bindings for `@abloatai/ablo`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* # Provider
|
|
5
|
+
*
|
|
6
|
+
* Build a client once — at module scope or with `useMemo` — and wrap your tree:
|
|
7
|
+
*
|
|
8
|
+
* const ablo = Ablo({ schema, apiKey })
|
|
6
9
|
* <AbloProvider client={ablo} fallback={<Skeleton/>}>
|
|
7
|
-
* — `client` is the only required prop (construct it yourself; the provider
|
|
8
|
-
* is the thin reactive binding, like `<Elements stripe={...}>`). `userId`
|
|
9
|
-
* is optional + informational. Owns sync engine + multiplayer lifecycle;
|
|
10
|
-
* the `fallback` prop
|
|
11
|
-
* gates children on first bootstrap. Pass `fallback="passthrough"`
|
|
12
|
-
* to disable the gate.
|
|
13
|
-
* <ClientSideSuspense fallback={<Skeleton/>}> — NESTED gate inside an
|
|
14
|
-
* already-ready provider. Use only when you need a separate gate
|
|
15
|
-
* for a heavy subtree (e.g. a canvas) while app chrome renders
|
|
16
|
-
* immediately. The provider-level `fallback` is the default path.
|
|
17
10
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
11
|
+
* `client` is the only required prop; you construct it, and the provider is the
|
|
12
|
+
* thin reactive binding around it. The provider owns the sync-engine and
|
|
13
|
+
* multiplayer lifecycle. Its `fallback` gates children until the first sync
|
|
14
|
+
* bootstrap completes — pass `fallback="passthrough"` to render children
|
|
15
|
+
* immediately. `userId` is optional and informational.
|
|
16
|
+
*
|
|
17
|
+
* {@link ClientSideSuspense} adds a nested gate inside an already-ready
|
|
18
|
+
* provider. Reach for it only when a heavy subtree, such as a canvas, needs its
|
|
19
|
+
* own gate while the rest of the app renders right away; the provider-level
|
|
20
|
+
* `fallback` is the usual path.
|
|
21
|
+
*
|
|
22
|
+
* # Data hooks
|
|
23
|
+
*
|
|
24
|
+
* useAblo((ablo) => ablo.tasks.get(id)) — subscribe to a local snapshot (the main read API)
|
|
25
|
+
* useAblo() — the typed client, for callbacks and effects:
|
|
26
|
+
* synchronous local reads (`ablo.<model>.get`/`getAll`),
|
|
27
|
+
* async server reads (`retrieve`/`list`),
|
|
28
|
+
* and writes (`create`/`update`/`delete`)
|
|
29
|
+
* useMutators(defs, opts?) — define custom mutators
|
|
30
|
+
* useUndoScope(name) — per-surface undo and redo
|
|
31
|
+
*
|
|
32
|
+
* # Status and errors
|
|
33
|
+
*
|
|
34
|
+
* useSyncStatus() — a discriminated-union snapshot of the sync lifecycle
|
|
35
|
+
* useErrorListener(cb) — an imperative error callback, for telemetry or toasts
|
|
36
|
+
* useCurrentUserId() — the provider's `userId` prop
|
|
26
37
|
*
|
|
27
|
-
*
|
|
28
|
-
* useSyncStatus() — tagged-union lifecycle snapshot
|
|
29
|
-
* useErrorListener(cb) — imperative error callback (Sentry/Datadog)
|
|
30
|
-
* useCurrentUserId() — the provider's userId prop
|
|
38
|
+
* # Multiplayer
|
|
31
39
|
*
|
|
32
|
-
* Multiplayer
|
|
33
|
-
*
|
|
34
|
-
* useWatch({ scope }) — join multiplayer for a scope, get peers/claims
|
|
40
|
+
* Multiplayer is always available, because `<AbloProvider>` always constructs a
|
|
41
|
+
* client:
|
|
35
42
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* <AbloProvider>. Access the raw engine with `useSync()`.
|
|
39
|
-
* Removed: createAbloContext() factory + its returned AbloProvider —
|
|
40
|
-
* multiplayer is now always-on inside <AbloProvider>. Schema-typed
|
|
41
|
-
* participant hooks ship in a follow-up release.
|
|
42
|
-
* Removed: withSync (no-op alias of observer). Import observer
|
|
43
|
-
* from mobx-react-lite directly if you still need it.
|
|
44
|
-
* Changed: useSyncStatus() now returns a discriminated union. See the
|
|
45
|
-
* migration notes in CHANGELOG.md.
|
|
43
|
+
* useAblo((ablo) => ablo.<model>.claim.state(...)) — reactive coordination reads
|
|
44
|
+
* useWatch({ scope }) — join a scope to get its peers and claims
|
|
46
45
|
*/
|
|
47
46
|
// ── Umbrella provider + lifecycle hooks ────────────────────────────
|
|
48
47
|
export { AbloProvider, useWatch, usePeers, useSync, useSyncStore, } from './AbloProvider.js';
|
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
import type { Ablo } from '../client/Ablo.js';
|
|
2
2
|
import type { SchemaRecord } from '../schema/schema.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Consumers should NOT use this directly — access the fields via
|
|
10
|
-
* the typed hooks (`useCurrentUserId`, `useErrorListener`, etc.).
|
|
4
|
+
* The context that `<AbloProvider>` populates for its own hooks. It is kept
|
|
5
|
+
* separate from the data-hook context, which carries the store and schema,
|
|
6
|
+
* because these fields belong to the provider rather than to the store. Read
|
|
7
|
+
* them through the typed hooks such as `useCurrentUserId` and
|
|
8
|
+
* `useErrorListener` rather than reaching into this context directly.
|
|
11
9
|
*/
|
|
12
10
|
export interface AbloInternalContextValue {
|
|
13
11
|
/**
|
|
14
|
-
*
|
|
15
|
-
* identity is server
|
|
12
|
+
* The application user id, when your app passed one to `<AbloProvider>`. Sync
|
|
13
|
+
* identity is derived on the server from the API key, so this is `null`
|
|
14
|
+
* unless you set it, and it is not required for sync to work.
|
|
16
15
|
*/
|
|
17
16
|
currentUserId: string | null;
|
|
18
|
-
/** Subscribe to provider-level errors
|
|
17
|
+
/** Subscribe to provider-level errors: engine errors, bootstrap failures, and session issues. */
|
|
19
18
|
subscribeError: (listener: (error: Error) => void) => () => void;
|
|
20
|
-
/**
|
|
19
|
+
/** Emit an error to every subscribed listener. The provider calls this for you. */
|
|
21
20
|
emitError: (error: Error) => void;
|
|
22
21
|
/**
|
|
23
|
-
* The
|
|
24
|
-
* resolves.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* and shouldn't be coerced through each other.
|
|
22
|
+
* The typed `Ablo` client for this provider, or `null` until the first sync
|
|
23
|
+
* bootstrap resolves. It is held here so `useSync()` can return it without
|
|
24
|
+
* reaching into the store; the client and the store are sibling objects, and
|
|
25
|
+
* neither is derived from the other.
|
|
28
26
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* It is typed loosely as `Ablo<SchemaRecord>` because generics do not flow
|
|
28
|
+
* through React context. `useSync<R>()` restores the precise type through its
|
|
29
|
+
* own generic; the runtime value is the fully typed client.
|
|
32
30
|
*/
|
|
33
31
|
engine: Ablo<SchemaRecord> | null;
|
|
34
32
|
}
|
package/dist/react/useAblo.d.ts
CHANGED
|
@@ -1,63 +1,66 @@
|
|
|
1
|
-
import type { Ablo, ModelClaim } from '../client/Ablo.js';
|
|
1
|
+
import type { Ablo, AbloReads, ModelClaim } from '../client/Ablo.js';
|
|
2
2
|
import type { ModelOperations } from '../client/createModelProxy.js';
|
|
3
3
|
import type { SchemaRecord } from '../schema/schema.js';
|
|
4
4
|
import type { ResolveSchema } from '../types/global.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `<(typeof schema)['models']>` at every call site.
|
|
6
|
+
* The app's resolved schema-record type. It reads your `Register` module
|
|
7
|
+
* augmentation when you declare one and falls back to the loose
|
|
8
|
+
* {@link SchemaRecord} otherwise, so `useAblo()` returns a fully typed client
|
|
9
|
+
* without you passing `<(typeof schema)['models']>` at every call site.
|
|
11
10
|
*/
|
|
12
11
|
type DefaultModels = ResolveSchema extends {
|
|
13
12
|
models: infer M;
|
|
14
13
|
} ? M extends SchemaRecord ? M : SchemaRecord : SchemaRecord;
|
|
15
|
-
type ModelClientSelector<R extends SchemaRecord, T, C> = (ablo:
|
|
16
|
-
type AbloSelector<R extends SchemaRecord, T> = (ablo:
|
|
14
|
+
type ModelClientSelector<R extends SchemaRecord, T, C> = (ablo: AbloReads<R>) => ModelOperations<T, C>;
|
|
15
|
+
type AbloSelector<R extends SchemaRecord, T> = (ablo: AbloReads<R>) => T;
|
|
17
16
|
export interface UseAbloModelOptions<T> {
|
|
18
17
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* An initial row, usually from a server component or a route loader. The hook
|
|
19
|
+
* returns it until sync delivers a newer row for the same id.
|
|
21
20
|
*/
|
|
22
21
|
readonly initial?: T;
|
|
23
22
|
}
|
|
24
23
|
export interface UseAbloModelResult<T> {
|
|
25
|
-
/**
|
|
24
|
+
/** The current row for the id, or `initial` until the row has synced. */
|
|
26
25
|
readonly data: T | undefined;
|
|
27
|
-
/**
|
|
26
|
+
/** The work claims currently held on this row by any participant. */
|
|
28
27
|
readonly claims: readonly ModelClaim[];
|
|
29
|
-
/**
|
|
28
|
+
/** True while another participant holds a claim — handy for disabling UI. */
|
|
30
29
|
readonly claimed: boolean;
|
|
31
30
|
}
|
|
32
31
|
export type UseAbloHydratedModelResult<T> = Omit<UseAbloModelResult<T>, 'data'> & {
|
|
33
32
|
readonly data: T;
|
|
34
33
|
};
|
|
35
34
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Reads Ablo from inside an `<AbloProvider>` subtree. Called with no arguments
|
|
36
|
+
* it returns the typed client for use in callbacks and effects; called with a
|
|
37
|
+
* selector it subscribes the component to a reactive read — such as one
|
|
38
|
+
* `ablo.<model>` row — and re-renders when that read changes.
|
|
38
39
|
*
|
|
39
|
-
*
|
|
40
|
-
* augmentation (`declare module '@abloatai/ablo' { interface Register {
|
|
41
|
-
* typeof schema } }`)
|
|
42
|
-
*
|
|
40
|
+
* You can call it with no type arguments once you declare the `Register` module
|
|
41
|
+
* augmentation (`declare module '@abloatai/ablo' { interface Register {
|
|
42
|
+
* Schema: typeof schema } }`); the default type then resolves through your
|
|
43
|
+
* schema's models, so call sites stay clean:
|
|
43
44
|
*
|
|
44
45
|
* ```ts
|
|
45
|
-
* // With Register augmentation (recommended):
|
|
46
|
+
* // With the Register augmentation (recommended):
|
|
46
47
|
* const ablo = useAblo();
|
|
47
48
|
* if (!ablo) return <Loading />;
|
|
48
49
|
* const doc = await ablo.documents.retrieve({ id }); // async server read
|
|
49
50
|
*
|
|
50
|
-
* // Reactive selector (
|
|
51
|
+
* // Reactive selector (a synchronous local snapshot). The selector's reads
|
|
52
|
+
* // are typed as snapshot rows — data fields + computeds, no relation
|
|
53
|
+
* // accessors — matching what the hook actually returns:
|
|
51
54
|
* const doc = useAblo((ablo) => ablo.documents.get(id)) ?? serverDoc;
|
|
52
55
|
* const active = useAblo((ablo) => ablo.documents.claim.state({ id }));
|
|
53
56
|
*
|
|
54
|
-
* // Without augmentation, pass the schema
|
|
57
|
+
* // Without the augmentation, pass the schema as a type argument:
|
|
55
58
|
* const ablo = useAblo<(typeof schema)['models']>();
|
|
56
59
|
* ```
|
|
57
60
|
*
|
|
58
|
-
*
|
|
59
|
-
* and render a loading state
|
|
60
|
-
* `'connected'`
|
|
61
|
+
* The no-argument form returns `null` while the engine is still bootstrapping.
|
|
62
|
+
* Branch on `null` and render a loading state — or gate on `useSyncStatus()`
|
|
63
|
+
* reaching `'connected'` — before calling model methods.
|
|
61
64
|
*/
|
|
62
65
|
export declare function useAblo<R extends SchemaRecord = DefaultModels>(): Ablo<R> | null;
|
|
63
66
|
export declare function useAblo<R extends SchemaRecord = DefaultModels, T = unknown>(select: AbloSelector<R, T>): T | undefined;
|
package/dist/react/useAblo.js
CHANGED
|
@@ -5,6 +5,24 @@ import { getModelClientMeta } from '../client/createModelProxy.js';
|
|
|
5
5
|
import { Model } from '../Model.js';
|
|
6
6
|
import { useReactive } from './useReactive.js';
|
|
7
7
|
const EMPTY_CLAIMS = Object.freeze([]);
|
|
8
|
+
/**
|
|
9
|
+
* Restore the caller's schema generics on the context-held engine. React
|
|
10
|
+
* context erases generics (see `AbloInternalContextValue.engine`), so this is
|
|
11
|
+
* the one deliberate rebind point: the runtime value is the fully typed
|
|
12
|
+
* client, and `R` is the compile-time view the calling hook declared.
|
|
13
|
+
*/
|
|
14
|
+
function rebindEngine(engine) {
|
|
15
|
+
return engine;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The same rebind viewed through the reactive-read surface — the identical
|
|
19
|
+
* runtime object, with model reads typed as snapshot rows, because everything
|
|
20
|
+
* a selector returns is converted through `snapshotValue` before the hook
|
|
21
|
+
* hands it back.
|
|
22
|
+
*/
|
|
23
|
+
function reactiveReads(engine) {
|
|
24
|
+
return rebindEngine(engine);
|
|
25
|
+
}
|
|
8
26
|
function readModelResult(engine, modelClient, id, initial) {
|
|
9
27
|
if (!modelClient || id === undefined) {
|
|
10
28
|
return { data: initial, claims: EMPTY_CLAIMS, claimed: false };
|
|
@@ -17,14 +35,15 @@ function readModelResult(engine, modelClient, id, initial) {
|
|
|
17
35
|
return { data, claims, claimed: claims.length > 0 };
|
|
18
36
|
}
|
|
19
37
|
/**
|
|
20
|
-
*
|
|
38
|
+
* Projects a reactive read into the value that `useReactive` caches and
|
|
39
|
+
* returns.
|
|
21
40
|
*
|
|
22
|
-
* For a `Model`, this
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
41
|
+
* For a `Model`, this reads the row's fields through `toReactiveSnapshot`
|
|
42
|
+
* rather than returning the instance itself. Property access is what subscribes
|
|
43
|
+
* the reaction to those fields, so the read has to happen inside this tracked
|
|
44
|
+
* function; returning the live instance without reading its fields would leave
|
|
45
|
+
* the component blind to later edits. The fresh object it produces also lets
|
|
46
|
+
* `useReactive`'s equality check detect an in-place update.
|
|
28
47
|
*/
|
|
29
48
|
function snapshotValue(value) {
|
|
30
49
|
if (value instanceof Model) {
|
|
@@ -42,18 +61,19 @@ export function useAblo(modelOrSelect, id, options) {
|
|
|
42
61
|
const isSelectorOnly = typeof modelOrSelect === 'function' && id === undefined;
|
|
43
62
|
const modelClient = typeof modelOrSelect === 'function' && id !== undefined
|
|
44
63
|
? engine
|
|
45
|
-
? modelOrSelect(engine)
|
|
64
|
+
? modelOrSelect(reactiveReads(engine))
|
|
46
65
|
: undefined
|
|
47
66
|
: typeof modelOrSelect === 'function'
|
|
48
67
|
? undefined
|
|
49
68
|
: modelOrSelect;
|
|
50
|
-
// Claims
|
|
51
|
-
// reactions below cannot track them
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
// re-render
|
|
56
|
-
// storm during AI editing
|
|
69
|
+
// Claims arrive through an event emitter (engine.claims), not through MobX, so
|
|
70
|
+
// the useReactive reactions below cannot track them; we bridge changes with a
|
|
71
|
+
// setState bump instead. Only the model-row form (`id !== undefined`) reads
|
|
72
|
+
// claims, so we subscribe only when `id` is set. The selector-only form never
|
|
73
|
+
// reads claims, and subscribing it to the workspace-wide claim stream would
|
|
74
|
+
// re-render and recompute it on every claim or presence change anywhere — a
|
|
75
|
+
// real storm during AI editing or live collaboration — for a value that cannot
|
|
76
|
+
// change.
|
|
57
77
|
const [claimVersion, setClaimVersion] = useState(0);
|
|
58
78
|
useEffect(() => {
|
|
59
79
|
if (!engine || id === undefined)
|
|
@@ -64,7 +84,11 @@ export function useAblo(modelOrSelect, id, options) {
|
|
|
64
84
|
if (!engine || !isSelectorOnly || typeof modelOrSelect !== 'function') {
|
|
65
85
|
return undefined;
|
|
66
86
|
}
|
|
67
|
-
return
|
|
87
|
+
// The selector runs against the real engine — reads inside it return the
|
|
88
|
+
// pool's model instances. `snapshotValue` then converts the RESULT to
|
|
89
|
+
// plain snapshot rows, which is what the selector's `AbloReads`
|
|
90
|
+
// parameter type already promised.
|
|
91
|
+
return snapshotValue(modelOrSelect(reactiveReads(engine)));
|
|
68
92
|
});
|
|
69
93
|
const modelResult = useReactive(() => {
|
|
70
94
|
void claimVersion;
|
|
@@ -74,5 +98,5 @@ export function useAblo(modelOrSelect, id, options) {
|
|
|
74
98
|
return selected;
|
|
75
99
|
if (modelOrSelect)
|
|
76
100
|
return modelResult;
|
|
77
|
-
return engine;
|
|
101
|
+
return engine === null ? null : rebindEngine(engine);
|
|
78
102
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Returns the
|
|
3
|
-
*
|
|
2
|
+
* Returns the application user id passed to the nearest `<AbloProvider>`, or
|
|
3
|
+
* `null` when your app did not provide one.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Sync identity is resolved on the server from the API key or session, so this
|
|
6
|
+
* value is not required for sync to connect. It is here for your app's own
|
|
7
|
+
* fields — an assignee id, a presence label, a permission check — where the
|
|
8
|
+
* current user matters to your data rather than to the sync layer. Reach for it
|
|
9
|
+
* in leaf components that need the id, for example to fill in a mutation
|
|
10
|
+
* payload.
|
|
8
11
|
*
|
|
9
|
-
* Use this in leaf components that need the current user ID for
|
|
10
|
-
* mutation payloads, presence labels, permission checks, etc.
|
|
11
12
|
* @example
|
|
12
13
|
* function TaskRow({ id }) {
|
|
13
14
|
* const userId = useCurrentUserId();
|
|
@@ -3,15 +3,16 @@ import { useContext } from 'react';
|
|
|
3
3
|
import { AbloInternalContext } from './internalContext.js';
|
|
4
4
|
import { AbloValidationError } from '../errors.js';
|
|
5
5
|
/**
|
|
6
|
-
* Returns the
|
|
7
|
-
*
|
|
6
|
+
* Returns the application user id passed to the nearest `<AbloProvider>`, or
|
|
7
|
+
* `null` when your app did not provide one.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Sync identity is resolved on the server from the API key or session, so this
|
|
10
|
+
* value is not required for sync to connect. It is here for your app's own
|
|
11
|
+
* fields — an assignee id, a presence label, a permission check — where the
|
|
12
|
+
* current user matters to your data rather than to the sync layer. Reach for it
|
|
13
|
+
* in leaf components that need the id, for example to fill in a mutation
|
|
14
|
+
* payload.
|
|
12
15
|
*
|
|
13
|
-
* Use this in leaf components that need the current user ID for
|
|
14
|
-
* mutation payloads, presence labels, permission checks, etc.
|
|
15
16
|
* @example
|
|
16
17
|
* function TaskRow({ id }) {
|
|
17
18
|
* const userId = useCurrentUserId();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Registers a callback that runs whenever the provider surfaces an error. This
|
|
3
|
+
* covers engine errors such as bootstrap failures and mutation rejections,
|
|
4
|
+
* WebSocket errors, and uncaught exceptions thrown inside `postBootstrap`
|
|
5
|
+
* hooks.
|
|
6
6
|
*
|
|
7
|
-
* Use
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Use it for side effects that should not cause a re-render — telemetry,
|
|
8
|
+
* logging, or a toast. The callback is held in a ref, so a re-render does not
|
|
9
|
+
* resubscribe.
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
12
|
* function ErrorToaster() {
|
|
@@ -3,14 +3,14 @@ import { useContext, useEffect, useRef } from 'react';
|
|
|
3
3
|
import { AbloInternalContext } from './internalContext.js';
|
|
4
4
|
import { AbloValidationError } from '../errors.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Registers a callback that runs whenever the provider surfaces an error. This
|
|
7
|
+
* covers engine errors such as bootstrap failures and mutation rejections,
|
|
8
|
+
* WebSocket errors, and uncaught exceptions thrown inside `postBootstrap`
|
|
9
|
+
* hooks.
|
|
10
10
|
*
|
|
11
|
-
* Use
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Use it for side effects that should not cause a re-render — telemetry,
|
|
12
|
+
* logging, or a toast. The callback is held in a ref, so a re-render does not
|
|
13
|
+
* resubscribe.
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* function ErrorToaster() {
|
|
@@ -27,10 +27,9 @@ export function useErrorListener(listener) {
|
|
|
27
27
|
throw new AbloValidationError('useErrorListener: no <AbloProvider> mounted above this component. ' +
|
|
28
28
|
'Wrap your tree with <AbloProvider ...> from @abloatai/ablo/react.', { code: 'no_ablo_provider' });
|
|
29
29
|
}
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// arrows without thrashing the subscription.
|
|
30
|
+
// Hold the latest callback in a ref so the subscription stays stable across
|
|
31
|
+
// renders. Late-binding the listener this way lets callers pass an inline
|
|
32
|
+
// arrow without resubscribing on every render.
|
|
34
33
|
const ref = useRef(listener);
|
|
35
34
|
ref.current = listener;
|
|
36
35
|
useEffect(() => {
|
|
@@ -5,15 +5,15 @@ export interface MutationFailurePayload {
|
|
|
5
5
|
permanent?: boolean;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Subscribes a listener to mutation failures. The callback fires whenever the
|
|
9
|
+
* transaction queue rolls back an optimistic write — both permanent rejections
|
|
10
|
+
* (a validation, foreign-key, or authorization error) and rollbacks after the
|
|
11
|
+
* retries are exhausted (for example, the connection drops mid-write).
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* subscription
|
|
13
|
+
* A single listener mounted near the top of your component tree can turn these
|
|
14
|
+
* otherwise-silent rollbacks into toasts or banners. The callback is held in a
|
|
15
|
+
* ref, so re-renders do not tear down and re-create the underlying
|
|
16
|
+
* subscription.
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* function MutationFailureBoundary() {
|
|
@@ -3,15 +3,15 @@ import { useContext, useEffect, useRef } from 'react';
|
|
|
3
3
|
import { AbloInternalContext } from './internalContext.js';
|
|
4
4
|
import { AbloValidationError } from '../errors.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Subscribes a listener to mutation failures. The callback fires whenever the
|
|
7
|
+
* transaction queue rolls back an optimistic write — both permanent rejections
|
|
8
|
+
* (a validation, foreign-key, or authorization error) and rollbacks after the
|
|
9
|
+
* retries are exhausted (for example, the connection drops mid-write).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* subscription
|
|
11
|
+
* A single listener mounted near the top of your component tree can turn these
|
|
12
|
+
* otherwise-silent rollbacks into toasts or banners. The callback is held in a
|
|
13
|
+
* ref, so re-renders do not tear down and re-create the underlying
|
|
14
|
+
* subscription.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
17
|
* function MutationFailureBoundary() {
|
|
@@ -3,19 +3,19 @@ import type { MutatorDefs } from '../mutators/defineMutators.js';
|
|
|
3
3
|
import type { UndoScope } from '../mutators/UndoManager.js';
|
|
4
4
|
import type { ResolveSchema } from '../types/global.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Turns a mutator tree built with `defineMutators` into callable invokers. The
|
|
7
|
+
* returned object mirrors that tree one-to-one, but each leaf becomes an
|
|
8
|
+
* `(args) => Promise<TResult>` function.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* 2. Calls the user's mutator with `{ tx, args }`.
|
|
12
|
-
* 3. Returns the mutator's resolved value.
|
|
10
|
+
* Each invocation builds a fresh `Transaction` bound to the current store and
|
|
11
|
+
* organization, calls your mutator with `{ tx, args }`, and returns whatever
|
|
12
|
+
* the mutator resolves to.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* If a mutator throws, the error propagates to the caller and any writes it
|
|
15
|
+
* already dispatched stay in place — there is no automatic rollback. Wrap the
|
|
16
|
+
* call in your own try/catch and issue compensating writes when you need to
|
|
17
|
+
* undo a partial change, or pass an `undoScope` (see {@link UseMutatorsOptions})
|
|
18
|
+
* to record inverses for undo and redo.
|
|
19
19
|
*/
|
|
20
20
|
/**
|
|
21
21
|
* Map a `MutatorFn` onto its invoker form — strip `tx`, keep `args`/return.
|
|
@@ -43,8 +43,8 @@ export function useMutators(schemaOrMutators, mutatorsOrOptions, maybeOptions) {
|
|
|
43
43
|
// inverse. On success, push the captured entry to the scope.
|
|
44
44
|
//
|
|
45
45
|
// The whole snapshot → write → record sequence runs on the scope's
|
|
46
|
-
// serialization chain so concurrent invocations (
|
|
47
|
-
// writes
|
|
46
|
+
// serialization chain so concurrent invocations (a caller may fire
|
|
47
|
+
// writes without awaiting them) record in invocation order and never
|
|
48
48
|
// interleave their shared-model snapshots. See UndoScope.runRecorded.
|
|
49
49
|
if (undoScope) {
|
|
50
50
|
return undoScope.runRecorded(async () => {
|
|
@@ -64,7 +64,7 @@ export function useMutators(schemaOrMutators, mutatorsOrOptions, maybeOptions) {
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
-
// Non-recording path — plain transaction,
|
|
67
|
+
// Non-recording path — plain transaction, no inverse capture.
|
|
68
68
|
const tx = createTransaction(schema, store, organizationId);
|
|
69
69
|
try {
|
|
70
70
|
return await fn({ tx, args });
|
|
@@ -48,7 +48,7 @@ export function useReactive(compute, equals = defaultEquals) {
|
|
|
48
48
|
// When `compute` identity changes, its closed-over observable source
|
|
49
49
|
// may have swapped (e.g. useQuery memoized a new QueryView because
|
|
50
50
|
// the where clause changed). The MobX reaction subscribed in
|
|
51
|
-
// `subscribe` only tracks the observables read on its
|
|
51
|
+
// `subscribe` only tracks the observables read on its first run; if
|
|
52
52
|
// the source swaps without a re-subscription, the reaction never
|
|
53
53
|
// re-tracks the new observables and `getSnapshot` keeps returning
|
|
54
54
|
// the stale value forever.
|
|
@@ -71,7 +71,7 @@ export function useReactive(compute, equals = defaultEquals) {
|
|
|
71
71
|
// `compute` is a fresh inline arrow at virtually every call site, so this
|
|
72
72
|
// branch runs on essentially every render. Reconcile the snapshot against
|
|
73
73
|
// the latest closure, but only force a re-subscription when the value
|
|
74
|
-
//
|
|
74
|
+
// actually changed. For the dominant case (same observable source, new
|
|
75
75
|
// arrow identity, unchanged value) this avoids tearing down + recreating
|
|
76
76
|
// the MobX reaction — and its double-compute — on every render. A genuine
|
|
77
77
|
// source swap (a memoized compute closing over a new observable source)
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* states
|
|
4
|
-
* variant carries only the fields that make sense in that
|
|
5
|
-
*
|
|
6
|
-
* Inspired by Liveblocks' `useStatus()` and Zero's `useConnectionState()`:
|
|
7
|
-
* one hook, one switch, no six-boolean guessing games.
|
|
2
|
+
* A snapshot of the current sync status, modeled as a discriminated union so
|
|
3
|
+
* impossible states — such as "connected and offline" at once — cannot be
|
|
4
|
+
* represented. Each variant carries only the fields that make sense in that
|
|
5
|
+
* state, so a single `switch` on `name` narrows to exactly what you can read.
|
|
8
6
|
*
|
|
9
7
|
* Variants:
|
|
10
8
|
* - `initial` — the provider just mounted; no connection attempt yet.
|
|
@@ -2,16 +2,14 @@ import type { Schema } from '../schema/schema.js';
|
|
|
2
2
|
import type { UndoScope, UndoScopeOptions } from '../mutators/UndoManager.js';
|
|
3
3
|
import type { ResolveSchema } from '../types/global.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Provides per-surface undo and redo for mutator invocations. Each named scope
|
|
6
|
+
* owns an independent undo/redo stack, so different parts of your app — a deck
|
|
7
|
+
* editor, a sidebar form — can undo separately without stepping on each other.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `scope` into `useMutators(schema, mutators, { undoScope: scope })` and the
|
|
12
|
-
* invocations become recorded. `undo()` / `redo()` replay the inverses /
|
|
13
|
-
* forwards as new transactions that do NOT re-record (the manager pushes
|
|
14
|
-
* them between the two stacks explicitly).
|
|
9
|
+
* Wire the returned `scope` into `useMutators(schema, mutators, { undoScope:
|
|
10
|
+
* scope })` and those invocations become recorded. `undo()` and `redo()` replay
|
|
11
|
+
* the captured inverses and forwards as new transactions that do not record
|
|
12
|
+
* themselves; the manager moves the entry between the two stacks explicitly.
|
|
15
13
|
*
|
|
16
14
|
* @example
|
|
17
15
|
* const { undo, redo, canUndo, canRedo, scope } = useUndoScope('deck-editor');
|