@abloatai/humans 0.37.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/README.md +52 -0
- package/dist/Ablo.d.ts +208 -0
- package/dist/Ablo.js +120 -0
- package/dist/client.d.ts +317 -0
- package/dist/client.js +13 -0
- package/dist/core.d.ts +35 -0
- package/dist/core.js +48 -0
- package/dist/humans.d.ts +28 -0
- package/dist/humans.js +34 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/local/BaseSyncedStore.d.ts +807 -0
- package/dist/local/BaseSyncedStore.js +1516 -0
- package/dist/local/Database.d.ts +322 -0
- package/dist/local/Database.js +1589 -0
- package/dist/local/InstanceCache.d.ts +255 -0
- package/dist/local/InstanceCache.js +1263 -0
- package/dist/local/LazyReferenceCollection.d.ts +177 -0
- package/dist/local/LazyReferenceCollection.js +461 -0
- package/dist/local/Model.d.ts +475 -0
- package/dist/local/Model.js +950 -0
- package/dist/local/ModelRegistry.d.ts +225 -0
- package/dist/local/ModelRegistry.js +539 -0
- package/dist/local/NetworkMonitor.d.ts +28 -0
- package/dist/local/NetworkMonitor.js +79 -0
- package/dist/local/RuntimeContext.d.ts +52 -0
- package/dist/local/RuntimeContext.js +80 -0
- package/dist/local/SyncClient.d.ts +516 -0
- package/dist/local/SyncClient.js +1754 -0
- package/dist/local/adapters/alwaysOnline.d.ts +14 -0
- package/dist/local/adapters/alwaysOnline.js +17 -0
- package/dist/local/adapters/inMemoryStorage.d.ts +37 -0
- package/dist/local/adapters/inMemoryStorage.js +122 -0
- package/dist/local/client/clientPrelude.d.ts +52 -0
- package/dist/local/client/clientPrelude.js +60 -0
- package/dist/local/client/consoleLogger.d.ts +35 -0
- package/dist/local/client/consoleLogger.js +44 -0
- package/dist/local/client/createInternalComponents.d.ts +50 -0
- package/dist/local/client/createInternalComponents.js +98 -0
- package/dist/local/client/createModelProxy.d.ts +248 -0
- package/dist/local/client/createModelProxy.js +880 -0
- package/dist/local/client/modelRegistration.d.ts +10 -0
- package/dist/local/client/modelRegistration.js +316 -0
- package/dist/local/client/options.d.ts +440 -0
- package/dist/local/client/options.js +7 -0
- package/dist/local/client/reactiveEngine.d.ts +53 -0
- package/dist/local/client/reactiveEngine.js +705 -0
- package/dist/local/client/resourceTypes.d.ts +12 -0
- package/dist/local/client/resourceTypes.js +10 -0
- package/dist/local/client/schemaConfig.d.ts +44 -0
- package/dist/local/client/schemaConfig.js +185 -0
- package/dist/local/client/storeCluster.d.ts +46 -0
- package/dist/local/client/storeCluster.js +133 -0
- package/dist/local/client/storeLifecycle.d.ts +61 -0
- package/dist/local/client/storeLifecycle.js +236 -0
- package/dist/local/client/validateAbloOptions.d.ts +42 -0
- package/dist/local/client/validateAbloOptions.js +43 -0
- package/dist/local/client/wsMutationExecutor.d.ts +27 -0
- package/dist/local/client/wsMutationExecutor.js +72 -0
- package/dist/local/context.d.ts +42 -0
- package/dist/local/context.js +81 -0
- package/dist/local/coordination/ClaimLog.d.ts +26 -0
- package/dist/local/coordination/ClaimLog.js +32 -0
- package/dist/local/interfaces/index.d.ts +311 -0
- package/dist/local/interfaces/index.js +9 -0
- package/dist/local/localModelContract.d.ts +14 -0
- package/dist/local/localModelContract.js +1 -0
- package/dist/local/logPosition.d.ts +31 -0
- package/dist/local/logPosition.js +53 -0
- package/dist/local/mutationPersistence.d.ts +6 -0
- package/dist/local/mutationPersistence.js +1 -0
- package/dist/local/mutators/RecordingMutation.d.ts +36 -0
- package/dist/local/mutators/RecordingMutation.js +182 -0
- package/dist/local/mutators/Transaction.d.ts +40 -0
- package/dist/local/mutators/Transaction.js +58 -0
- package/dist/local/mutators/UndoManager.d.ts +258 -0
- package/dist/local/mutators/UndoManager.js +665 -0
- package/dist/local/mutators/defineMutators.d.ts +60 -0
- package/dist/local/mutators/defineMutators.js +18 -0
- package/dist/local/mutators/inverseOp.d.ts +126 -0
- package/dist/local/mutators/inverseOp.js +71 -0
- package/dist/local/mutators/mutateActions.d.ts +45 -0
- package/dist/local/mutators/mutateActions.js +105 -0
- package/dist/local/mutators/readerActions.d.ts +33 -0
- package/dist/local/mutators/readerActions.js +57 -0
- package/dist/local/mutators/undoApply.d.ts +51 -0
- package/dist/local/mutators/undoApply.js +117 -0
- package/dist/local/persistence.d.ts +7 -0
- package/dist/local/persistence.js +9 -0
- package/dist/local/query/QueryProcessor.d.ts +75 -0
- package/dist/local/query/QueryProcessor.js +255 -0
- package/dist/local/query/client.d.ts +64 -0
- package/dist/local/query/client.js +138 -0
- package/dist/local/query/types.d.ts +85 -0
- package/dist/local/query/types.js +16 -0
- package/dist/local/schema/serialize.d.ts +1 -0
- package/dist/local/schema/serialize.js +1 -0
- package/dist/local/store/queryApi.d.ts +13 -0
- package/dist/local/store/queryApi.js +35 -0
- package/dist/local/storeContract.d.ts +145 -0
- package/dist/local/storeContract.js +12 -0
- package/dist/local/stores/DatabaseManager.d.ts +112 -0
- package/dist/local/stores/DatabaseManager.js +400 -0
- package/dist/local/stores/ObjectStore.d.ts +115 -0
- package/dist/local/stores/ObjectStore.js +393 -0
- package/dist/local/stores/ObjectStoreContract.d.ts +38 -0
- package/dist/local/stores/ObjectStoreContract.js +1 -0
- package/dist/local/stores/StoreManager.d.ts +114 -0
- package/dist/local/stores/StoreManager.js +304 -0
- package/dist/local/stores/SyncActionStore.d.ts +99 -0
- package/dist/local/stores/SyncActionStore.js +506 -0
- package/dist/local/stores/openIDBWithTimeout.d.ts +65 -0
- package/dist/local/stores/openIDBWithTimeout.js +153 -0
- package/dist/local/stores/persistenceCleanup.d.ts +7 -0
- package/dist/local/stores/persistenceCleanup.js +26 -0
- package/dist/local/stores/persistenceIdentity.d.ts +27 -0
- package/dist/local/stores/persistenceIdentity.js +38 -0
- package/dist/local/stores/syncAction.d.ts +26 -0
- package/dist/local/stores/syncAction.js +16 -0
- package/dist/local/stores/v1PersistenceDeletion.d.ts +8 -0
- package/dist/local/stores/v1PersistenceDeletion.js +16 -0
- package/dist/local/sync/BootstrapFetcher.d.ts +284 -0
- package/dist/local/sync/BootstrapFetcher.js +964 -0
- package/dist/local/sync/ConnectionManager.d.ts +8 -0
- package/dist/local/sync/ConnectionManager.js +8 -0
- package/dist/local/sync/OnDemandLoader.d.ts +231 -0
- package/dist/local/sync/OnDemandLoader.js +743 -0
- package/dist/local/sync/SubscriptionManager.d.ts +159 -0
- package/dist/local/sync/SubscriptionManager.js +243 -0
- package/dist/local/sync/SyncWebSocket.d.ts +173 -0
- package/dist/local/sync/SyncWebSocket.js +438 -0
- package/dist/local/sync/bootstrapApply.d.ts +73 -0
- package/dist/local/sync/bootstrapApply.js +73 -0
- package/dist/local/sync/commitFrames.d.ts +8 -0
- package/dist/local/sync/commitFrames.js +8 -0
- package/dist/local/sync/connectionManagerLifecycle.d.ts +23 -0
- package/dist/local/sync/connectionManagerLifecycle.js +126 -0
- package/dist/local/sync/contextPorts.d.ts +18 -0
- package/dist/local/sync/contextPorts.js +31 -0
- package/dist/local/sync/createClaimStream.d.ts +64 -0
- package/dist/local/sync/createClaimStream.js +475 -0
- package/dist/local/sync/createSnapshot.d.ts +29 -0
- package/dist/local/sync/createSnapshot.js +116 -0
- package/dist/local/sync/credentialLifecycle.d.ts +7 -0
- package/dist/local/sync/credentialLifecycle.js +7 -0
- package/dist/local/sync/deltaPipeline.d.ts +116 -0
- package/dist/local/sync/deltaPipeline.js +357 -0
- package/dist/local/sync/groupChange.d.ts +116 -0
- package/dist/local/sync/groupChange.js +244 -0
- package/dist/local/sync/initialize.d.ts +27 -0
- package/dist/local/sync/initialize.js +137 -0
- package/dist/local/sync/participants.d.ts +132 -0
- package/dist/local/sync/participants.js +342 -0
- package/dist/local/sync/persistedPrefix.d.ts +12 -0
- package/dist/local/sync/persistedPrefix.js +22 -0
- package/dist/local/sync/reconnect.d.ts +23 -0
- package/dist/local/sync/reconnect.js +55 -0
- package/dist/local/sync/schemaDrift.d.ts +55 -0
- package/dist/local/sync/schemaDrift.js +53 -0
- package/dist/local/sync/schemas.d.ts +71 -0
- package/dist/local/sync/schemas.js +94 -0
- package/dist/local/sync/socketEventWiring.d.ts +31 -0
- package/dist/local/sync/socketEventWiring.js +130 -0
- package/dist/local/sync/syncCursor.d.ts +40 -0
- package/dist/local/sync/syncCursor.js +55 -0
- package/dist/local/sync/syncPlan.d.ts +54 -0
- package/dist/local/sync/syncPlan.js +50 -0
- package/dist/local/sync/terminalSessionLifecycle.d.ts +20 -0
- package/dist/local/sync/terminalSessionLifecycle.js +50 -0
- package/dist/local/sync/wsFrameHandlers.d.ts +8 -0
- package/dist/local/sync/wsFrameHandlers.js +8 -0
- package/dist/local/transactions/databaseCommitOutbox.d.ts +15 -0
- package/dist/local/transactions/databaseCommitOutbox.js +16 -0
- package/dist/local/transactions/localMutation.d.ts +10 -0
- package/dist/local/transactions/localMutation.js +37 -0
- package/dist/local/transactions/mutations/MutationQueue.d.ts +511 -0
- package/dist/local/transactions/mutations/MutationQueue.js +1498 -0
- package/dist/local/transactions/mutations/MutationStore.d.ts +20 -0
- package/dist/local/transactions/mutations/MutationStore.js +53 -0
- package/dist/local/transactions/mutations/UnconfirmedWrites.d.ts +82 -0
- package/dist/local/transactions/mutations/UnconfirmedWrites.js +104 -0
- package/dist/local/transactions/mutations/batchProcessing.d.ts +64 -0
- package/dist/local/transactions/mutations/batchProcessing.js +349 -0
- package/dist/local/transactions/mutations/coalesceRules.d.ts +58 -0
- package/dist/local/transactions/mutations/coalesceRules.js +140 -0
- package/dist/local/transactions/mutations/commitApi.d.ts +19 -0
- package/dist/local/transactions/mutations/commitApi.js +74 -0
- package/dist/local/transactions/mutations/commitLane.d.ts +70 -0
- package/dist/local/transactions/mutations/commitLane.js +140 -0
- package/dist/local/transactions/mutations/commitLatency.d.ts +52 -0
- package/dist/local/transactions/mutations/commitLatency.js +130 -0
- package/dist/local/transactions/mutations/commitPayload.d.ts +165 -0
- package/dist/local/transactions/mutations/commitPayload.js +152 -0
- package/dist/local/transactions/mutations/commitTransport.d.ts +36 -0
- package/dist/local/transactions/mutations/commitTransport.js +104 -0
- package/dist/local/transactions/mutations/deltaConfirmation.d.ts +63 -0
- package/dist/local/transactions/mutations/deltaConfirmation.js +235 -0
- package/dist/local/transactions/mutations/durableCommitRestore.d.ts +17 -0
- package/dist/local/transactions/mutations/durableCommitRestore.js +95 -0
- package/dist/local/transactions/mutations/durableWriteStore.d.ts +14 -0
- package/dist/local/transactions/mutations/durableWriteStore.js +12 -0
- package/dist/local/transactions/mutations/executionSelection.d.ts +6 -0
- package/dist/local/transactions/mutations/executionSelection.js +42 -0
- package/dist/local/transactions/mutations/failureHandling.d.ts +16 -0
- package/dist/local/transactions/mutations/failureHandling.js +130 -0
- package/dist/local/transactions/mutations/failurePolicy.d.ts +13 -0
- package/dist/local/transactions/mutations/failurePolicy.js +48 -0
- package/dist/local/transactions/mutations/localMutation.d.ts +58 -0
- package/dist/local/transactions/mutations/localMutation.js +75 -0
- package/dist/local/transactions/mutations/modelOperations.d.ts +44 -0
- package/dist/local/transactions/mutations/modelOperations.js +144 -0
- package/dist/local/transactions/mutations/mutationPersistence.d.ts +25 -0
- package/dist/local/transactions/mutations/mutationPersistence.js +188 -0
- package/dist/local/transactions/mutations/pendingDrain.d.ts +33 -0
- package/dist/local/transactions/mutations/pendingDrain.js +112 -0
- package/dist/local/transactions/mutations/processingScheduler.d.ts +14 -0
- package/dist/local/transactions/mutations/processingScheduler.js +24 -0
- package/dist/local/transactions/mutations/queueCoalescing.d.ts +13 -0
- package/dist/local/transactions/mutations/queueCoalescing.js +35 -0
- package/dist/local/transactions/mutations/replayValidation.d.ts +187 -0
- package/dist/local/transactions/mutations/replayValidation.js +164 -0
- package/dist/local/transactions/reconnectDrain.d.ts +11 -0
- package/dist/local/transactions/reconnectDrain.js +13 -0
- package/dist/local/utils/mobxSetup.d.ts +53 -0
- package/dist/local/utils/mobxSetup.js +330 -0
- package/dist/local/views/QueryView.d.ts +79 -0
- package/dist/local/views/QueryView.js +218 -0
- package/dist/local/views/ViewRegistry.d.ts +20 -0
- package/dist/local/views/ViewRegistry.js +57 -0
- package/dist/local/views/incrementalView.d.ts +45 -0
- package/dist/local/views/incrementalView.js +69 -0
- package/dist/plugin.d.ts +285 -0
- package/dist/plugin.js +106 -0
- package/dist/presenceStream.d.ts +69 -0
- package/dist/presenceStream.js +200 -0
- package/dist/react/AbloProvider.d.ts +242 -0
- package/dist/react/AbloProvider.js +456 -0
- package/dist/react/ClientSideSuspense.d.ts +36 -0
- package/dist/react/ClientSideSuspense.js +17 -0
- package/dist/react/DefaultFallback.d.ts +24 -0
- package/dist/react/DefaultFallback.js +43 -0
- package/dist/react/context.d.ts +55 -0
- package/dist/react/context.js +29 -0
- package/dist/react/createAbloReact.d.ts +50 -0
- package/dist/react/createAbloReact.js +48 -0
- package/dist/react/internalContext.d.ts +33 -0
- package/dist/react/internalContext.js +3 -0
- package/dist/react/useAblo.d.ts +96 -0
- package/dist/react/useAblo.js +120 -0
- package/dist/react/useCurrentUserId.d.ts +2 -0
- package/dist/react/useCurrentUserId.js +12 -0
- package/dist/react/useErrorListener.d.ts +2 -0
- package/dist/react/useErrorListener.js +14 -0
- package/dist/react/useMutationFailureListener.d.ts +8 -0
- package/dist/react/useMutationFailureListener.js +19 -0
- package/dist/react/useMutators.d.ts +56 -0
- package/dist/react/useMutators.js +84 -0
- package/dist/react/useSyncStatus.d.ts +19 -0
- package/dist/react/useSyncStatus.js +37 -0
- package/dist/react/useUndoScope.d.ts +34 -0
- package/dist/react/useUndoScope.js +73 -0
- package/dist/react.d.ts +18 -0
- package/dist/react.js +14 -0
- package/dist/reactRuntime.d.ts +4 -0
- package/dist/reactRuntime.js +2 -0
- package/dist/surface.d.ts +36 -0
- package/dist/surface.js +77 -0
- package/dist/useReactive.d.ts +6 -0
- package/dist/useReactive.js +43 -0
- package/package.json +119 -0
- package/src/Ablo.ts +456 -0
- package/src/client.ts +374 -0
- package/src/core.ts +104 -0
- package/src/humans.ts +61 -0
- package/src/index.ts +40 -0
- package/src/local/BaseSyncedStore.ts +1991 -0
- package/src/local/Database.ts +2052 -0
- package/src/local/InstanceCache.ts +1503 -0
- package/src/local/LazyReferenceCollection.ts +563 -0
- package/src/local/Model.ts +1124 -0
- package/src/local/ModelRegistry.ts +762 -0
- package/src/local/NetworkMonitor.ts +88 -0
- package/src/local/RuntimeContext.ts +141 -0
- package/src/local/SyncClient.ts +2131 -0
- package/src/local/adapters/alwaysOnline.ts +20 -0
- package/src/local/adapters/inMemoryStorage.ts +141 -0
- package/src/local/client/clientPrelude.ts +112 -0
- package/src/local/client/consoleLogger.ts +60 -0
- package/src/local/client/createInternalComponents.ts +163 -0
- package/src/local/client/createModelProxy.ts +1390 -0
- package/src/local/client/modelRegistration.ts +350 -0
- package/src/local/client/options.ts +526 -0
- package/src/local/client/reactiveEngine.ts +935 -0
- package/src/local/client/resourceTypes.ts +37 -0
- package/src/local/client/schemaConfig.ts +194 -0
- package/src/local/client/storeCluster.ts +172 -0
- package/src/local/client/storeLifecycle.ts +343 -0
- package/src/local/client/validateAbloOptions.ts +95 -0
- package/src/local/client/wsMutationExecutor.ts +110 -0
- package/src/local/context.ts +96 -0
- package/src/local/coordination/ClaimLog.ts +39 -0
- package/src/local/interfaces/index.ts +468 -0
- package/src/local/localModelContract.ts +15 -0
- package/src/local/logPosition.ts +84 -0
- package/src/local/mutationPersistence.ts +7 -0
- package/src/local/mutators/RecordingMutation.ts +222 -0
- package/src/local/mutators/Transaction.ts +97 -0
- package/src/local/mutators/UndoManager.ts +741 -0
- package/src/local/mutators/defineMutators.ts +76 -0
- package/src/local/mutators/inverseOp.ts +83 -0
- package/src/local/mutators/mutateActions.ts +167 -0
- package/src/local/mutators/readerActions.ts +99 -0
- package/src/local/mutators/undoApply.ts +141 -0
- package/src/local/persistence.ts +16 -0
- package/src/local/query/QueryProcessor.ts +347 -0
- package/src/local/query/client.ts +197 -0
- package/src/local/query/types.ts +102 -0
- package/src/local/schema/serialize.ts +1 -0
- package/src/local/store/queryApi.ts +56 -0
- package/src/local/storeContract.ts +146 -0
- package/src/local/stores/DatabaseManager.ts +507 -0
- package/src/local/stores/ObjectStore.ts +449 -0
- package/src/local/stores/ObjectStoreContract.ts +48 -0
- package/src/local/stores/StoreManager.ts +388 -0
- package/src/local/stores/SyncActionStore.ts +579 -0
- package/src/local/stores/openIDBWithTimeout.ts +195 -0
- package/src/local/stores/persistenceCleanup.ts +43 -0
- package/src/local/stores/persistenceIdentity.ts +83 -0
- package/src/local/stores/syncAction.ts +21 -0
- package/src/local/stores/v1PersistenceDeletion.ts +21 -0
- package/src/local/sync/BootstrapFetcher.ts +1224 -0
- package/src/local/sync/ConnectionManager.ts +15 -0
- package/src/local/sync/OnDemandLoader.ts +927 -0
- package/src/local/sync/SubscriptionManager.ts +300 -0
- package/src/local/sync/SyncWebSocket.ts +584 -0
- package/src/local/sync/bootstrapApply.ts +130 -0
- package/src/local/sync/commitFrames.ts +16 -0
- package/src/local/sync/connectionManagerLifecycle.ts +158 -0
- package/src/local/sync/contextPorts.ts +37 -0
- package/src/local/sync/createClaimStream.ts +668 -0
- package/src/local/sync/createSnapshot.ts +160 -0
- package/src/local/sync/credentialLifecycle.ts +18 -0
- package/src/local/sync/deltaPipeline.ts +473 -0
- package/src/local/sync/groupChange.ts +343 -0
- package/src/local/sync/initialize.ts +205 -0
- package/src/local/sync/participants.ts +564 -0
- package/src/local/sync/persistedPrefix.ts +27 -0
- package/src/local/sync/reconnect.ts +88 -0
- package/src/local/sync/schemaDrift.ts +86 -0
- package/src/local/sync/schemas.ts +118 -0
- package/src/local/sync/socketEventWiring.ts +196 -0
- package/src/local/sync/syncCursor.ts +62 -0
- package/src/local/sync/syncPlan.ts +89 -0
- package/src/local/sync/terminalSessionLifecycle.ts +66 -0
- package/src/local/sync/wsFrameHandlers.ts +20 -0
- package/src/local/transactions/databaseCommitOutbox.ts +33 -0
- package/src/local/transactions/localMutation.ts +58 -0
- package/src/local/transactions/mutations/MutationQueue.ts +1998 -0
- package/src/local/transactions/mutations/MutationStore.ts +65 -0
- package/src/local/transactions/mutations/UnconfirmedWrites.ts +133 -0
- package/src/local/transactions/mutations/batchProcessing.ts +469 -0
- package/src/local/transactions/mutations/coalesceRules.ts +192 -0
- package/src/local/transactions/mutations/commitApi.ts +97 -0
- package/src/local/transactions/mutations/commitLane.ts +191 -0
- package/src/local/transactions/mutations/commitLatency.ts +164 -0
- package/src/local/transactions/mutations/commitPayload.ts +281 -0
- package/src/local/transactions/mutations/commitTransport.ts +174 -0
- package/src/local/transactions/mutations/deltaConfirmation.ts +298 -0
- package/src/local/transactions/mutations/durableCommitRestore.ts +128 -0
- package/src/local/transactions/mutations/durableWriteStore.ts +21 -0
- package/src/local/transactions/mutations/executionSelection.ts +42 -0
- package/src/local/transactions/mutations/failureHandling.ts +154 -0
- package/src/local/transactions/mutations/failurePolicy.ts +61 -0
- package/src/local/transactions/mutations/localMutation.ts +135 -0
- package/src/local/transactions/mutations/modelOperations.ts +210 -0
- package/src/local/transactions/mutations/mutationPersistence.ts +231 -0
- package/src/local/transactions/mutations/pendingDrain.ts +160 -0
- package/src/local/transactions/mutations/processingScheduler.ts +35 -0
- package/src/local/transactions/mutations/queueCoalescing.ts +45 -0
- package/src/local/transactions/mutations/replayValidation.ts +192 -0
- package/src/local/transactions/reconnectDrain.ts +24 -0
- package/src/local/utils/mobxSetup.ts +388 -0
- package/src/local/views/QueryView.ts +311 -0
- package/src/local/views/ViewRegistry.ts +61 -0
- package/src/local/views/incrementalView.ts +92 -0
- package/src/plugin.ts +396 -0
- package/src/presenceStream.ts +279 -0
- package/src/react/AbloProvider.tsx +744 -0
- package/src/react/ClientSideSuspense.tsx +57 -0
- package/src/react/DefaultFallback.tsx +60 -0
- package/src/react/context.ts +89 -0
- package/src/react/createAbloReact.ts +116 -0
- package/src/react/internalContext.ts +38 -0
- package/src/react/useAblo.ts +280 -0
- package/src/react/useCurrentUserId.ts +17 -0
- package/src/react/useErrorListener.ts +22 -0
- package/src/react/useMutationFailureListener.ts +34 -0
- package/src/react/useMutators.ts +184 -0
- package/src/react/useSyncStatus.ts +42 -0
- package/src/react/useUndoScope.ts +143 -0
- package/src/react.ts +69 -0
- package/src/reactRuntime.ts +10 -0
- package/src/surface.ts +106 -0
- package/src/useReactive.ts +51 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typed react binding — the schema generic is captured ONCE, at a factory
|
|
3
|
+
* call in app code, and every hook the factory returns is born typed. This is
|
|
4
|
+
* a schema-bound shape with no module augmentation or generic parameters at call sites,
|
|
5
|
+
* and — once the legacy generic erasure retires — no casts anywhere on the
|
|
6
|
+
* path from context to component.
|
|
7
|
+
*
|
|
8
|
+
* The app's one binding file, by convention:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* // lib/ablo.ts
|
|
12
|
+
* import { createAbloReact } from '@abloatai/ablo/react';
|
|
13
|
+
* import { schema } from './schema';
|
|
14
|
+
*
|
|
15
|
+
* export const { AbloProvider, useAblo } = createAbloReact(schema);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Components then import `useAblo` from `lib/ablo` and never spell a type
|
|
19
|
+
* argument; `useAblo()` is `Ablo<S> | null`, and a selector's `ablo`
|
|
20
|
+
* parameter is the reactive-read view of the same `S`.
|
|
21
|
+
*/
|
|
22
|
+
import { type ReactElement } from 'react';
|
|
23
|
+
import { type AbloProviderProps } from './AbloProvider.js';
|
|
24
|
+
import { type AbloSelector, type ModelClientSelector, type UseAbloHydratedModelResult, type UseAbloModelOptions, type UseAbloModelResult } from './useAblo.js';
|
|
25
|
+
import type { AbloClient as Ablo } from '../client.js';
|
|
26
|
+
import type { ModelOperations } from '../local/client/createModelProxy.js';
|
|
27
|
+
import type { Schema, SchemaRecord } from '@abloatai/transaction/schema/schema';
|
|
28
|
+
/** What a binding returns: the provider and the hook, with `S` fixed. */
|
|
29
|
+
export interface AbloReactBinding<S extends SchemaRecord> {
|
|
30
|
+
/** `AbloProvider` with its `client` prop typed `Ablo<S>` — same component,
|
|
31
|
+
* no per-app generics. */
|
|
32
|
+
AbloProvider: (props: AbloProviderProps<S>) => ReactElement;
|
|
33
|
+
/** `useAblo` with the schema bound — the same overloads as the global
|
|
34
|
+
* hook, minus the type arguments. */
|
|
35
|
+
useAblo: {
|
|
36
|
+
(): Ablo<S> | null;
|
|
37
|
+
<T>(select: AbloSelector<S, T>): T | undefined;
|
|
38
|
+
<T, C>(modelClientOrSelect: ModelOperations<T, C> | ModelClientSelector<S, T, C>, id: string, options: UseAbloModelOptions<T> & {
|
|
39
|
+
readonly initial: T;
|
|
40
|
+
}): UseAbloHydratedModelResult<T>;
|
|
41
|
+
<T, C>(modelClientOrSelect: ModelOperations<T, C> | ModelClientSelector<S, T, C>, id: string, options?: UseAbloModelOptions<T>): UseAbloModelResult<T>;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Bind the react surface to one schema. The schema value is taken for
|
|
46
|
+
* inference — write `createAbloReact(schema)`, never a hand-spelled type
|
|
47
|
+
* argument — and it is the seam where the binding's own typed context arrives
|
|
48
|
+
* when the legacy erasure retires (docs/plans/typed-react-binding.md, step 3).
|
|
49
|
+
*/
|
|
50
|
+
export declare function createAbloReact<S extends SchemaRecord>(schema: Schema<S>): AbloReactBinding<S>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* The typed react binding — the schema generic is captured ONCE, at a factory
|
|
4
|
+
* call in app code, and every hook the factory returns is born typed. This is
|
|
5
|
+
* a schema-bound shape with no module augmentation or generic parameters at call sites,
|
|
6
|
+
* and — once the legacy generic erasure retires — no casts anywhere on the
|
|
7
|
+
* path from context to component.
|
|
8
|
+
*
|
|
9
|
+
* The app's one binding file, by convention:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // lib/ablo.ts
|
|
13
|
+
* import { createAbloReact } from '@abloatai/ablo/react';
|
|
14
|
+
* import { schema } from './schema';
|
|
15
|
+
*
|
|
16
|
+
* export const { AbloProvider, useAblo } = createAbloReact(schema);
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Components then import `useAblo` from `lib/ablo` and never spell a type
|
|
20
|
+
* argument; `useAblo()` is `Ablo<S> | null`, and a selector's `ablo`
|
|
21
|
+
* parameter is the reactive-read view of the same `S`.
|
|
22
|
+
*/
|
|
23
|
+
import { createContext, createElement, useContext } from 'react';
|
|
24
|
+
import { AbloProvider, } from './AbloProvider.js';
|
|
25
|
+
import { useAbloImpl, } from './useAblo.js';
|
|
26
|
+
/**
|
|
27
|
+
* Bind the react surface to one schema. The schema value is taken for
|
|
28
|
+
* inference — write `createAbloReact(schema)`, never a hand-spelled type
|
|
29
|
+
* argument — and it is the seam where the binding's own typed context arrives
|
|
30
|
+
* when the legacy erasure retires (docs/plans/typed-react-binding.md, step 3).
|
|
31
|
+
*/
|
|
32
|
+
export function createAbloReact(schema) {
|
|
33
|
+
void schema;
|
|
34
|
+
// The binding's own context — created here, AFTER the schema generic is
|
|
35
|
+
// known, so it is typed `Ablo<S>` from birth. A hook that reads it never rebinds and
|
|
36
|
+
// never casts; a binding hook mounted under a legacy provider (no bound
|
|
37
|
+
// provider in the tree) reads `null` here and falls through to the shared
|
|
38
|
+
// implementation's internal-context fallback.
|
|
39
|
+
const BoundClientContext = createContext(null);
|
|
40
|
+
function BoundAbloProvider(props) {
|
|
41
|
+
return createElement(BoundClientContext.Provider, { value: props.client }, createElement((AbloProvider), props));
|
|
42
|
+
}
|
|
43
|
+
function useBoundAblo(modelOrSelect, id, options) {
|
|
44
|
+
const bound = useContext(BoundClientContext);
|
|
45
|
+
return useAbloImpl(bound, modelOrSelect, id, options);
|
|
46
|
+
}
|
|
47
|
+
return { AbloProvider: BoundAbloProvider, useAblo: useBoundAblo };
|
|
48
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AbloClient as Ablo } from '../client.js';
|
|
2
|
+
import type { SchemaRecord } from '@abloatai/transaction/schema/schema';
|
|
3
|
+
/**
|
|
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.
|
|
9
|
+
*/
|
|
10
|
+
export interface AbloInternalContextValue {
|
|
11
|
+
/**
|
|
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.
|
|
15
|
+
*/
|
|
16
|
+
currentUserId: string | null;
|
|
17
|
+
/** Subscribe to provider-level errors: engine errors, bootstrap failures, and session issues. */
|
|
18
|
+
subscribeError: (listener: (error: Error) => void) => () => void;
|
|
19
|
+
/** Emit an error to every subscribed listener. The provider calls this for you. */
|
|
20
|
+
emitError: (error: Error) => void;
|
|
21
|
+
/**
|
|
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.
|
|
26
|
+
*
|
|
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.
|
|
30
|
+
*/
|
|
31
|
+
engine: Ablo<SchemaRecord> | null;
|
|
32
|
+
}
|
|
33
|
+
export declare const AbloInternalContext: import("react").Context<AbloInternalContextValue | null>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { AbloClient as Ablo, AbloReads } from '../client.js';
|
|
2
|
+
import type { ModelClaim } from '@abloatai/transaction/coordination';
|
|
3
|
+
import { type ModelOperations } from '../local/client/createModelProxy.js';
|
|
4
|
+
import type { SchemaRecord } from '@abloatai/transaction/schema/schema';
|
|
5
|
+
import type { ResolveSchema } from '@abloatai/transaction/types/global';
|
|
6
|
+
/**
|
|
7
|
+
* The app's resolved schema-record type. It reads your `Register` module
|
|
8
|
+
* augmentation when you declare one and falls back to the loose
|
|
9
|
+
* {@link SchemaRecord} otherwise, so `useAblo()` returns a fully typed client
|
|
10
|
+
* without you passing `<(typeof schema)['models']>` at every call site.
|
|
11
|
+
*/
|
|
12
|
+
type DefaultModels = ResolveSchema extends {
|
|
13
|
+
models: infer M;
|
|
14
|
+
} ? M extends SchemaRecord ? M : SchemaRecord : SchemaRecord;
|
|
15
|
+
export type ModelClientSelector<R extends SchemaRecord, T, C> = (ablo: AbloReads<R>) => ModelOperations<T, C>;
|
|
16
|
+
export type AbloSelector<R extends SchemaRecord, T> = (ablo: AbloReads<R>) => T;
|
|
17
|
+
export interface UseAbloModelOptions<T> {
|
|
18
|
+
/**
|
|
19
|
+
* An initial row, usually from a server component or a route loader. The hook
|
|
20
|
+
* returns it until sync delivers a newer row for the same id.
|
|
21
|
+
*/
|
|
22
|
+
readonly initial?: T;
|
|
23
|
+
}
|
|
24
|
+
export interface UseAbloModelResult<T> {
|
|
25
|
+
/** The current row for the id, or `initial` until the row has synced. */
|
|
26
|
+
readonly data: T | undefined;
|
|
27
|
+
/** The work claims currently held on this row by any participant. */
|
|
28
|
+
readonly claims: readonly ModelClaim[];
|
|
29
|
+
/** True while another participant holds a claim — handy for disabling UI. */
|
|
30
|
+
readonly claimed: boolean;
|
|
31
|
+
}
|
|
32
|
+
export type UseAbloHydratedModelResult<T> = Omit<UseAbloModelResult<T>, 'data'> & {
|
|
33
|
+
readonly data: T;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Reads Ablo from inside an `<AbloProvider>` subtree. Called with no arguments
|
|
37
|
+
* it returns the typed client for use in callbacks and effects; called with a
|
|
38
|
+
* selector it subscribes the component to a reactive read — such as one
|
|
39
|
+
* `ablo.<model>` row — and re-renders when that read changes.
|
|
40
|
+
*
|
|
41
|
+
* You can call it with no type arguments once you declare the `Register` module
|
|
42
|
+
* augmentation (`declare module '@abloatai/ablo' { interface Register {
|
|
43
|
+
* Schema: typeof schema } }`); the default type then resolves through your
|
|
44
|
+
* schema's models, so call sites stay clean:
|
|
45
|
+
*
|
|
46
|
+
* **Prefer the binding.** `createAbloReact(schema)` captures the schema once
|
|
47
|
+
* in your app's binding file and returns a `useAblo` that needs none of the
|
|
48
|
+
* typing arrangements below — no type argument, no `Register` declaration
|
|
49
|
+
* (see `react.md`). Passing an explicit schema type argument to THIS hook is
|
|
50
|
+
* deprecated in favor of that binding; it keeps working for shared packages
|
|
51
|
+
* that cannot bind a concrete schema.
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* // With the Register augmentation (recommended):
|
|
55
|
+
* const ablo = useAblo();
|
|
56
|
+
* if (!ablo) return <Loading />;
|
|
57
|
+
* const doc = await ablo.documents.get({ id }); // async server read
|
|
58
|
+
*
|
|
59
|
+
* // Reactive selector (a synchronous local snapshot). The selector's reads
|
|
60
|
+
* // are typed as snapshot rows — data fields + computeds, no relation
|
|
61
|
+
* // accessors — matching what the hook actually returns:
|
|
62
|
+
* const doc = useAblo((ablo) => ablo.documents.local.get(id)) ?? serverDoc;
|
|
63
|
+
* const active = useAblo((ablo) => ablo.documents.claim.state({ id }));
|
|
64
|
+
*
|
|
65
|
+
* // Without the augmentation, pass the schema as a type argument:
|
|
66
|
+
* const ablo = useAblo<(typeof schema)['models']>();
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* The no-argument form returns `null` while the engine is still bootstrapping.
|
|
70
|
+
* Branch on `null` and render a loading state — or gate on `useSyncStatus()`
|
|
71
|
+
* reaching `'connected'` — before calling model methods.
|
|
72
|
+
*/
|
|
73
|
+
export declare function useAblo<R extends SchemaRecord = DefaultModels>(): Ablo<R> | null;
|
|
74
|
+
export declare function useAblo<R extends SchemaRecord = DefaultModels, T = unknown>(select: AbloSelector<R, T>): T | undefined;
|
|
75
|
+
export declare function useAblo<T, C>(modelClient: ModelOperations<T, C>, id: string, options: UseAbloModelOptions<T> & {
|
|
76
|
+
readonly initial: T;
|
|
77
|
+
}): UseAbloHydratedModelResult<T>;
|
|
78
|
+
export declare function useAblo<R extends SchemaRecord = DefaultModels, T = Record<string, unknown>, C = unknown>(select: ModelClientSelector<R, T, C>, id: string, options: UseAbloModelOptions<T> & {
|
|
79
|
+
readonly initial: T;
|
|
80
|
+
}): UseAbloHydratedModelResult<T>;
|
|
81
|
+
export declare function useAblo<T, C>(modelClient: ModelOperations<T, C>, id: string, options?: UseAbloModelOptions<T>): UseAbloModelResult<T>;
|
|
82
|
+
export declare function useAblo<R extends SchemaRecord = DefaultModels, T = Record<string, unknown>, C = unknown>(select: ModelClientSelector<R, T, C>, id: string, options?: UseAbloModelOptions<T>): UseAbloModelResult<T>;
|
|
83
|
+
/**
|
|
84
|
+
* @internal The one implementation behind `useAblo` and the bound hooks a
|
|
85
|
+
* `createAbloReact` binding returns — written once so the reactive read path
|
|
86
|
+
* cannot fork between the global hook and a factory's.
|
|
87
|
+
*
|
|
88
|
+
* `boundClient` is a binding's own context value — typed `Ablo<S>` at the
|
|
89
|
+
* factory, so that path never rebinds and never casts. `null` means "no
|
|
90
|
+
* binding provider in this tree": the global hook always passes it, and a
|
|
91
|
+
* binding hook mounted under a legacy provider falls through to the erased
|
|
92
|
+
* internal context, which is what keeps both mounts working while the last
|
|
93
|
+
* legacy mount migrates.
|
|
94
|
+
*/
|
|
95
|
+
export declare function useAbloImpl<R extends SchemaRecord, T = Record<string, unknown>, C = unknown>(boundClient: Ablo<R> | null, modelOrSelect?: ModelOperations<T, C> | ModelClientSelector<R, T, C> | AbloSelector<R, T>, id?: string, options?: UseAbloModelOptions<T>): Ablo<R> | null | UseAbloModelResult<T> | T | undefined;
|
|
96
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext, useEffect, useState } from 'react';
|
|
3
|
+
import { AbloInternalContext } from './internalContext.js';
|
|
4
|
+
import { getModelClientMeta, } from '../local/client/createModelProxy.js';
|
|
5
|
+
import { Model } from '../local/Model.js';
|
|
6
|
+
import { useReactive } from '../useReactive.js';
|
|
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 reactive-read view of a client — the identical runtime object, with
|
|
19
|
+
* model reads typed as snapshot rows, because everything a selector returns
|
|
20
|
+
* is converted through `snapshotValue` before the hook hands it back. Same
|
|
21
|
+
* generic in and out, so this compiles with no schema rebinding.
|
|
22
|
+
*/
|
|
23
|
+
function reactiveReads(engine) {
|
|
24
|
+
return engine;
|
|
25
|
+
}
|
|
26
|
+
function readModelResult(engine, modelClient, id, initial) {
|
|
27
|
+
if (!modelClient || id === undefined) {
|
|
28
|
+
return { data: initial, claims: EMPTY_CLAIMS, claimed: false };
|
|
29
|
+
}
|
|
30
|
+
const data = snapshotValue(modelClient.local.get(id) ?? initial);
|
|
31
|
+
const meta = getModelClientMeta(modelClient);
|
|
32
|
+
const claims = meta && engine
|
|
33
|
+
? engine.claims.list({ model: meta.key, id })
|
|
34
|
+
: EMPTY_CLAIMS;
|
|
35
|
+
return { data, claims, claimed: claims.length > 0 };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Projects a reactive read into the value that `useReactive` caches and
|
|
39
|
+
* returns.
|
|
40
|
+
*
|
|
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.
|
|
47
|
+
*/
|
|
48
|
+
function snapshotValue(value) {
|
|
49
|
+
if (value instanceof Model) {
|
|
50
|
+
return value.toReactiveSnapshot();
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
return value.map((item) => snapshotValue(item));
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
export function useAblo(modelOrSelect, id, options) {
|
|
58
|
+
return useAbloImpl(null, modelOrSelect, id, options);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* @internal The one implementation behind `useAblo` and the bound hooks a
|
|
62
|
+
* `createAbloReact` binding returns — written once so the reactive read path
|
|
63
|
+
* cannot fork between the global hook and a factory's.
|
|
64
|
+
*
|
|
65
|
+
* `boundClient` is a binding's own context value — typed `Ablo<S>` at the
|
|
66
|
+
* factory, so that path never rebinds and never casts. `null` means "no
|
|
67
|
+
* binding provider in this tree": the global hook always passes it, and a
|
|
68
|
+
* binding hook mounted under a legacy provider falls through to the erased
|
|
69
|
+
* internal context, which is what keeps both mounts working while the last
|
|
70
|
+
* legacy mount migrates.
|
|
71
|
+
*/
|
|
72
|
+
export function useAbloImpl(boundClient, modelOrSelect, id, options) {
|
|
73
|
+
const ctx = useContext(AbloInternalContext);
|
|
74
|
+
// The bound client wins — it is already `Ablo<R>`, no rebinding. The
|
|
75
|
+
// fallback is the ONE remaining schema rebind in the SDK; it retires with
|
|
76
|
+
// the last legacy provider mount (docs/plans/typed-react-binding.md).
|
|
77
|
+
const engine = boundClient ?? (ctx?.engine ? rebindEngine(ctx.engine) : null);
|
|
78
|
+
const initial = options?.initial;
|
|
79
|
+
const isSelectorOnly = typeof modelOrSelect === 'function' && id === undefined;
|
|
80
|
+
const modelClient = typeof modelOrSelect === 'function' && id !== undefined
|
|
81
|
+
? engine
|
|
82
|
+
? modelOrSelect(reactiveReads(engine))
|
|
83
|
+
: undefined
|
|
84
|
+
: typeof modelOrSelect === 'function'
|
|
85
|
+
? undefined
|
|
86
|
+
: modelOrSelect;
|
|
87
|
+
// Claims arrive through an event emitter (engine.claims), not through MobX, so
|
|
88
|
+
// the useReactive reactions below cannot track them; we bridge changes with a
|
|
89
|
+
// setState bump instead. Only the model-row form (`id !== undefined`) reads
|
|
90
|
+
// claims, so we subscribe only when `id` is set. The selector-only form never
|
|
91
|
+
// reads claims, and subscribing it to the workspace-wide claim stream would
|
|
92
|
+
// re-render and recompute it on every claim or presence change anywhere — a
|
|
93
|
+
// real storm during AI editing or live collaboration — for a value that cannot
|
|
94
|
+
// change.
|
|
95
|
+
const [claimVersion, setClaimVersion] = useState(0);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (!engine || id === undefined)
|
|
98
|
+
return;
|
|
99
|
+
return engine.claims.onChange(() => { setClaimVersion((version) => version + 1); });
|
|
100
|
+
}, [engine, id]);
|
|
101
|
+
const selected = useReactive(() => {
|
|
102
|
+
if (!engine || !isSelectorOnly || typeof modelOrSelect !== 'function') {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
// The selector runs against the real engine — reads inside it return the
|
|
106
|
+
// pool's model instances. `snapshotValue` then converts the RESULT to
|
|
107
|
+
// plain snapshot rows, which is what the selector's `AbloReads`
|
|
108
|
+
// parameter type already promised.
|
|
109
|
+
return snapshotValue(modelOrSelect(reactiveReads(engine)));
|
|
110
|
+
});
|
|
111
|
+
const modelResult = useReactive(() => {
|
|
112
|
+
void claimVersion;
|
|
113
|
+
return readModelResult(engine, modelClient, id, initial);
|
|
114
|
+
});
|
|
115
|
+
if (isSelectorOnly)
|
|
116
|
+
return selected;
|
|
117
|
+
if (modelOrSelect)
|
|
118
|
+
return modelResult;
|
|
119
|
+
return engine;
|
|
120
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { AbloInternalContext } from './internalContext.js';
|
|
4
|
+
import { AbloValidationError } from '@abloatai/transaction/errors';
|
|
5
|
+
/** Returns the application user id supplied to the nearest AbloProvider. */
|
|
6
|
+
export function useCurrentUserId() {
|
|
7
|
+
const context = useContext(AbloInternalContext);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new AbloValidationError('useCurrentUserId: no <AbloProvider> mounted above this component.', { code: 'no_ablo_provider' });
|
|
10
|
+
}
|
|
11
|
+
return context.currentUserId;
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
3
|
+
import { AbloInternalContext } from './internalContext.js';
|
|
4
|
+
import { AbloValidationError } from '@abloatai/transaction/errors';
|
|
5
|
+
/** Subscribe to provider-level errors without causing component re-renders. */
|
|
6
|
+
export function useErrorListener(listener) {
|
|
7
|
+
const context = useContext(AbloInternalContext);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new AbloValidationError('useErrorListener: no <AbloProvider> mounted above this component.', { code: 'no_ablo_provider' });
|
|
10
|
+
}
|
|
11
|
+
const listenerRef = useRef(listener);
|
|
12
|
+
listenerRef.current = listener;
|
|
13
|
+
useEffect(() => context.subscribeError((error) => listenerRef.current(error)), [context]);
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type QueuedMutation } from '../reactRuntime.js';
|
|
2
|
+
export interface MutationFailurePayload {
|
|
3
|
+
transaction: QueuedMutation;
|
|
4
|
+
error: Error;
|
|
5
|
+
permanent?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Subscribe to optimistic mutation rollbacks and permanent write failures. */
|
|
8
|
+
export declare function useMutationFailureListener(listener: (payload: MutationFailurePayload) => void): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
3
|
+
import { AbloInternalContext, } from '../reactRuntime.js';
|
|
4
|
+
import { AbloValidationError } from '@abloatai/transaction/errors';
|
|
5
|
+
/** Subscribe to optimistic mutation rollbacks and permanent write failures. */
|
|
6
|
+
export function useMutationFailureListener(listener) {
|
|
7
|
+
const context = useContext(AbloInternalContext);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new AbloValidationError('useMutationFailureListener: no <AbloProvider> mounted above this component.', { code: 'no_ablo_provider' });
|
|
10
|
+
}
|
|
11
|
+
const listenerRef = useRef(listener);
|
|
12
|
+
listenerRef.current = listener;
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const engine = context.engine;
|
|
15
|
+
if (!engine)
|
|
16
|
+
return;
|
|
17
|
+
return engine.onMutationFailure((payload) => listenerRef.current(payload));
|
|
18
|
+
}, [context, context.engine]);
|
|
19
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Schema } from '@abloatai/transaction/schema/schema';
|
|
2
|
+
import type { MutatorDefs } from '../local/mutators/defineMutators.js';
|
|
3
|
+
import type { UndoScope } from '../local/mutators/UndoManager.js';
|
|
4
|
+
import type { ResolveSchema } from '@abloatai/transaction/types/global';
|
|
5
|
+
/**
|
|
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.
|
|
9
|
+
*
|
|
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
|
+
*
|
|
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
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Map a `MutatorFn` onto its invoker form — strip `tx`, keep `args`/return.
|
|
22
|
+
*
|
|
23
|
+
* Uses nested `infer O` so the `args`/`result` types are extracted from the
|
|
24
|
+
* function signature without binding the `tx` parameter to a specific
|
|
25
|
+
* `Transaction<S>` variance. Function parameters are contravariant, so a
|
|
26
|
+
* match against `MutatorFn<Schema>` would reject mutators declared against
|
|
27
|
+
* a narrower schema (e.g. `Transaction<typeof appSchema>`). The two-step
|
|
28
|
+
* inference sidesteps that without resorting to `any`/`unknown` placeholders.
|
|
29
|
+
*/
|
|
30
|
+
export type InvokerFor<F> = F extends (options: infer O) => Promise<infer R> ? O extends {
|
|
31
|
+
args: infer A;
|
|
32
|
+
} ? (args: A) => Promise<R> : never : never;
|
|
33
|
+
/**
|
|
34
|
+
* The hook's return shape: same tree as the input `MutatorDefs`, every leaf
|
|
35
|
+
* rewritten to its invoker form.
|
|
36
|
+
*/
|
|
37
|
+
export type MutatorInvokers<M> = {
|
|
38
|
+
[K in keyof M]: {
|
|
39
|
+
[N in keyof M[K]]: InvokerFor<M[K][N]>;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Options passed to `useMutators`. When `undoScope` is set, every mutator
|
|
44
|
+
* invocation is wrapped in a `RecordingMutation` and its inverses are
|
|
45
|
+
* pushed to the scope as one undo entry.
|
|
46
|
+
*/
|
|
47
|
+
export interface UseMutatorsOptions<S extends Schema> {
|
|
48
|
+
/** Target undo scope for recording inverses. Omit to disable recording. */
|
|
49
|
+
undoScope?: UndoScope<S>;
|
|
50
|
+
}
|
|
51
|
+
/** Mutator invokers (explicit schema arg). */
|
|
52
|
+
export declare function useMutators<S extends Schema, M extends MutatorDefs<S>>(schema: S, mutators: M, options?: UseMutatorsOptions<S>): MutatorInvokers<M>;
|
|
53
|
+
/** Mutator invokers via the `Register` module augmentation. Schema comes
|
|
54
|
+
* from the `SyncProvider`'s context; the mutator tree is typed against
|
|
55
|
+
* `ResolveSchema` at the call site. */
|
|
56
|
+
export declare function useMutators<M extends ResolveSchema extends Schema ? MutatorDefs<ResolveSchema> : MutatorDefs<Schema>>(mutators: M, options?: UseMutatorsOptions<ResolveSchema extends Schema ? ResolveSchema : Schema>): MutatorInvokers<M>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { createTransaction } from '../local/mutators/Transaction.js';
|
|
4
|
+
import { createRecordingMutation } from '../local/mutators/RecordingMutation.js';
|
|
5
|
+
import { useSyncContext } from './context.js';
|
|
6
|
+
import { AbloValidationError } from '@abloatai/transaction/errors';
|
|
7
|
+
import { getContext } from '../local/context.js';
|
|
8
|
+
export function useMutators(schemaOrMutators, mutatorsOrOptions, maybeOptions) {
|
|
9
|
+
const { store, organizationId, schema: ctxSchema } = useSyncContext();
|
|
10
|
+
// Disambiguate: explicit-schema path has the schema object in first slot;
|
|
11
|
+
// the global-resolved path has the mutator tree there. A schema object
|
|
12
|
+
// has a `.models` property; a mutator tree doesn't.
|
|
13
|
+
const isExplicit = typeof schemaOrMutators === 'object' &&
|
|
14
|
+
schemaOrMutators !== null &&
|
|
15
|
+
'models' in schemaOrMutators;
|
|
16
|
+
const schema = isExplicit ? schemaOrMutators : ctxSchema;
|
|
17
|
+
const mutators = (isExplicit ? mutatorsOrOptions : schemaOrMutators);
|
|
18
|
+
const options = (isExplicit ? maybeOptions : mutatorsOrOptions);
|
|
19
|
+
if (!schema) {
|
|
20
|
+
throw new AbloValidationError('useMutators: no schema available. Pass the schema as the first arg, ' +
|
|
21
|
+
'or build the <AbloProvider> above with `Ablo({ schema })` so the ' +
|
|
22
|
+
'zero-arg overload can read it from context.', { code: 'mutators_schema_missing' });
|
|
23
|
+
}
|
|
24
|
+
const { undoScope } = options ?? {};
|
|
25
|
+
return useMemo(() => {
|
|
26
|
+
const out = {};
|
|
27
|
+
for (const modelKey of Object.keys(mutators)) {
|
|
28
|
+
const group = mutators[modelKey];
|
|
29
|
+
if (!group)
|
|
30
|
+
continue;
|
|
31
|
+
const invokers = {};
|
|
32
|
+
for (const mutatorName of Object.keys(group)) {
|
|
33
|
+
const maybeFn = group[mutatorName];
|
|
34
|
+
if (!maybeFn)
|
|
35
|
+
continue;
|
|
36
|
+
// Bind the narrowed value: `noUncheckedIndexedAccess` types the indexed
|
|
37
|
+
// read as `Fn | undefined`, and that narrowing doesn't survive into the
|
|
38
|
+
// deferred invoker closures below — a non-optional local does.
|
|
39
|
+
const fn = maybeFn;
|
|
40
|
+
const label = `${String(modelKey)}.${mutatorName}`;
|
|
41
|
+
invokers[mutatorName] = async (args) => {
|
|
42
|
+
// Recording path: wrap the transaction so each write snapshots its
|
|
43
|
+
// inverse. On success, push the captured entry to the scope.
|
|
44
|
+
//
|
|
45
|
+
// The whole snapshot → write → record sequence runs on the scope's
|
|
46
|
+
// serialization chain so concurrent invocations (a caller may fire
|
|
47
|
+
// writes without awaiting them) record in invocation order and never
|
|
48
|
+
// interleave their shared-model snapshots. See UndoScope.runRecorded.
|
|
49
|
+
if (undoScope) {
|
|
50
|
+
return undoScope.runRecorded(async () => {
|
|
51
|
+
const recording = createRecordingMutation(schema, store, organizationId);
|
|
52
|
+
try {
|
|
53
|
+
const result = await fn({ tx: recording.tx, args });
|
|
54
|
+
const entry = recording.getEntry(label);
|
|
55
|
+
if (entry)
|
|
56
|
+
undoScope.record(entry);
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
// The error is re-thrown to the caller's await (the real
|
|
61
|
+
// consumer surface), so this is a forensic follow-on → debug.
|
|
62
|
+
getContext().logger.debug(`[useMutators] mutator "${label}" threw`, { error: err });
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Non-recording path — plain transaction, no inverse capture.
|
|
68
|
+
const tx = createTransaction(schema, store, organizationId);
|
|
69
|
+
try {
|
|
70
|
+
return await fn({ tx, args });
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
// Re-thrown to the caller's await (the real consumer surface) →
|
|
74
|
+
// this duplicate is forensic, debug only.
|
|
75
|
+
getContext().logger.debug(`[useMutators] mutator "${label}" threw`, { error: err });
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
out[modelKey] = invokers;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}, [schema, mutators, store, organizationId, undoScope]);
|
|
84
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type SyncStatusSnapshot = {
|
|
2
|
+
readonly name: 'initial';
|
|
3
|
+
} | {
|
|
4
|
+
readonly name: 'connecting';
|
|
5
|
+
readonly progress: number;
|
|
6
|
+
} | {
|
|
7
|
+
readonly name: 'connected';
|
|
8
|
+
readonly hasUnsyncedChanges: boolean;
|
|
9
|
+
} | {
|
|
10
|
+
readonly name: 'reconnecting';
|
|
11
|
+
readonly reason?: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly name: 'disconnected';
|
|
14
|
+
readonly reason?: string;
|
|
15
|
+
} | {
|
|
16
|
+
readonly name: 'needs-auth';
|
|
17
|
+
};
|
|
18
|
+
/** Reactively exposes the local store's connection and settlement status. */
|
|
19
|
+
export declare function useSyncStatus(): SyncStatusSnapshot;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { useSyncContext, } from './context.js';
|
|
4
|
+
import { useReactive } from '../useReactive.js';
|
|
5
|
+
/** Reactively exposes the local store's connection and settlement status. */
|
|
6
|
+
export function useSyncStatus() {
|
|
7
|
+
const { store } = useSyncContext();
|
|
8
|
+
const compute = useCallback(() => deriveStatus(store), [store]);
|
|
9
|
+
return useReactive(compute, sameSnapshot);
|
|
10
|
+
}
|
|
11
|
+
function deriveStatus(store) {
|
|
12
|
+
const { state, progress, pendingChanges, isSessionError, error } = store.syncStatus;
|
|
13
|
+
if (isSessionError)
|
|
14
|
+
return { name: 'needs-auth' };
|
|
15
|
+
if (state === 'reconnecting')
|
|
16
|
+
return { name: 'reconnecting', reason: error?.message };
|
|
17
|
+
if (state === 'offline')
|
|
18
|
+
return { name: 'disconnected', reason: 'offline' };
|
|
19
|
+
if (state === 'error')
|
|
20
|
+
return { name: 'disconnected', reason: error?.message };
|
|
21
|
+
if (store.isReady)
|
|
22
|
+
return { name: 'connected', hasUnsyncedChanges: pendingChanges > 0 };
|
|
23
|
+
if (state === 'idle' || state === 'syncing')
|
|
24
|
+
return { name: 'connecting', progress };
|
|
25
|
+
return { name: 'initial' };
|
|
26
|
+
}
|
|
27
|
+
function sameSnapshot(a, b) {
|
|
28
|
+
if (a.name !== b.name)
|
|
29
|
+
return false;
|
|
30
|
+
if (a.name === 'initial' || a.name === 'needs-auth')
|
|
31
|
+
return true;
|
|
32
|
+
if (a.name === 'connecting')
|
|
33
|
+
return b.name === 'connecting' && a.progress === b.progress;
|
|
34
|
+
if (a.name === 'connected')
|
|
35
|
+
return b.name === 'connected' && a.hasUnsyncedChanges === b.hasUnsyncedChanges;
|
|
36
|
+
return (b.name === 'reconnecting' || b.name === 'disconnected') && a.reason === b.reason;
|
|
37
|
+
}
|