@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
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Resolves a participant's identity and scope when an {@link Ablo} client is
|
|
3
|
+
* constructed, following whichever of three authentication paths the caller's
|
|
4
|
+
* options select:
|
|
3
5
|
*
|
|
4
|
-
*
|
|
6
|
+
* 1. **Hosted cloud** — the caller passed an `apiKey`. The client exchanges it
|
|
7
|
+
* for a capability token and scope, then starts a scheduler that re-mints the
|
|
8
|
+
* token before it expires, so the rotation is invisible to the caller.
|
|
9
|
+
* 2. **Self-derived** — the caller passed a bearer or capability token but not
|
|
10
|
+
* an identity. The client asks the identity endpoint to recover the
|
|
11
|
+
* participant id and scope from the token.
|
|
12
|
+
* 3. **Explicit** — a self-hosted caller passed the organization id and a user
|
|
13
|
+
* or agent id directly. No server round-trip; the client trusts the caller.
|
|
5
14
|
*
|
|
6
|
-
*
|
|
7
|
-
* server-side for a capability token + scope blob, then sets
|
|
8
|
-
* up a refresh scheduler that re-mints transparently before
|
|
9
|
-
* expiry.
|
|
10
|
-
* 2. **Self-derived** — caller passed an authToken / capability
|
|
11
|
-
* token but the SDK doesn't yet know the identity. Calls
|
|
12
|
-
* `resolveIdentity` against the bootstrap endpoint to recover
|
|
13
|
-
* `participantId` + scope from the token.
|
|
14
|
-
* 3. **Legacy explicit** — self-hosted callers that pass
|
|
15
|
-
* `organizationId` + `user.id` (or `agentId`) directly. No
|
|
16
|
-
* server round-trip; SDK trusts the caller.
|
|
17
|
-
*
|
|
18
|
-
* Extracted from `Ablo.ts` so each branch is testable in isolation
|
|
19
|
-
* and the constructor body reads as a single named call rather than
|
|
20
|
-
* a 100+-line if/elif/else with three different side-effect chains.
|
|
15
|
+
* Each branch is a separate function below, so it can be read and tested on its own.
|
|
21
16
|
*/
|
|
22
17
|
import { type RefreshScheduler } from '../auth/index.js';
|
|
23
|
-
import type {
|
|
18
|
+
import type { BootstrapFetcher } from '../sync/BootstrapFetcher.js';
|
|
24
19
|
import type { SyncLogger } from '../interfaces/index.js';
|
|
25
20
|
import type { AuthCredentialSource } from '../auth/credentialSource.js';
|
|
26
21
|
import type { ApiKeySetter } from './auth.js';
|
|
@@ -42,7 +37,7 @@ export interface IdentityResolveInput {
|
|
|
42
37
|
readonly kind: 'user' | 'agent' | 'system';
|
|
43
38
|
readonly configuredApiKey: string | ApiKeySetter | null;
|
|
44
39
|
readonly configuredAuthToken: string | null;
|
|
45
|
-
readonly bootstrapHelper:
|
|
40
|
+
readonly bootstrapHelper: BootstrapFetcher;
|
|
46
41
|
readonly auth: AuthCredentialSource;
|
|
47
42
|
readonly logger: SyncLogger;
|
|
48
43
|
}
|
|
@@ -53,7 +48,7 @@ export interface ResolvedIdentity {
|
|
|
53
48
|
readonly capabilityToken: string | undefined;
|
|
54
49
|
readonly syncGroups: readonly string[] | undefined;
|
|
55
50
|
readonly participantKind: 'user' | 'agent' | 'system';
|
|
56
|
-
/**
|
|
51
|
+
/** Set only on the hosted-cloud path; the caller keeps it to stop refreshes on shutdown. */
|
|
57
52
|
readonly refreshScheduler: RefreshScheduler | null;
|
|
58
53
|
}
|
|
59
54
|
export declare function resolveParticipantIdentity(input: IdentityResolveInput): Promise<ResolvedIdentity>;
|
package/dist/client/identity.js
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Resolves a participant's identity and scope when an {@link Ablo} client is
|
|
3
|
+
* constructed, following whichever of three authentication paths the caller's
|
|
4
|
+
* options select:
|
|
3
5
|
*
|
|
4
|
-
*
|
|
6
|
+
* 1. **Hosted cloud** — the caller passed an `apiKey`. The client exchanges it
|
|
7
|
+
* for a capability token and scope, then starts a scheduler that re-mints the
|
|
8
|
+
* token before it expires, so the rotation is invisible to the caller.
|
|
9
|
+
* 2. **Self-derived** — the caller passed a bearer or capability token but not
|
|
10
|
+
* an identity. The client asks the identity endpoint to recover the
|
|
11
|
+
* participant id and scope from the token.
|
|
12
|
+
* 3. **Explicit** — a self-hosted caller passed the organization id and a user
|
|
13
|
+
* or agent id directly. No server round-trip; the client trusts the caller.
|
|
5
14
|
*
|
|
6
|
-
*
|
|
7
|
-
* server-side for a capability token + scope blob, then sets
|
|
8
|
-
* up a refresh scheduler that re-mints transparently before
|
|
9
|
-
* expiry.
|
|
10
|
-
* 2. **Self-derived** — caller passed an authToken / capability
|
|
11
|
-
* token but the SDK doesn't yet know the identity. Calls
|
|
12
|
-
* `resolveIdentity` against the bootstrap endpoint to recover
|
|
13
|
-
* `participantId` + scope from the token.
|
|
14
|
-
* 3. **Legacy explicit** — self-hosted callers that pass
|
|
15
|
-
* `organizationId` + `user.id` (or `agentId`) directly. No
|
|
16
|
-
* server round-trip; SDK trusts the caller.
|
|
17
|
-
*
|
|
18
|
-
* Extracted from `Ablo.ts` so each branch is testable in isolation
|
|
19
|
-
* and the constructor body reads as a single named call rather than
|
|
20
|
-
* a 100+-line if/elif/else with three different side-effect chains.
|
|
15
|
+
* Each branch is a separate function below, so it can be read and tested on its own.
|
|
21
16
|
*/
|
|
22
17
|
import { AbloAuthenticationError } from '../errors.js';
|
|
23
18
|
import { exchangeApiKey } from '../auth/index.js';
|
|
@@ -29,22 +24,21 @@ import { resolveApiKeyValue, resolveBootstrapBaseUrl } from './auth.js';
|
|
|
29
24
|
export async function resolveParticipantIdentity(input) {
|
|
30
25
|
const { options, internalOptions, url, kind, configuredApiKey, configuredAuthToken, bootstrapHelper, auth, logger, } = input;
|
|
31
26
|
const apiKeyValue = await resolveApiKeyValue(configuredApiKey);
|
|
32
|
-
//
|
|
33
|
-
//
|
|
27
|
+
// Resolve the http(s) base URL, coercing ws/wss to http/https even when
|
|
28
|
+
// `bootstrapBaseUrl` is an explicit override (see auth.ts).
|
|
34
29
|
const baseUrl = resolveBootstrapBaseUrl({
|
|
35
30
|
url,
|
|
36
31
|
bootstrapBaseUrl: options.bootstrapBaseUrl,
|
|
37
32
|
});
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// server round-trip is needed.
|
|
33
|
+
// An organization id plus a caller-supplied participant id is the explicit path:
|
|
34
|
+
// the caller already knows its own identity, so no server round-trip is needed.
|
|
41
35
|
const hasExplicitIdentity = internalOptions.organizationId != null &&
|
|
42
36
|
(kind === 'agent' ? options.agentId != null : options.user?.id != null);
|
|
43
|
-
// The
|
|
44
|
-
//
|
|
45
|
-
// one mint the policy performs (
|
|
46
|
-
// every other route
|
|
47
|
-
//
|
|
37
|
+
// The credential-routing decision lives in `credentialPolicy`: it classifies the
|
|
38
|
+
// apiKey by prefix (`sk_`/`ek_`/`rk_`/`pk_`) and picks a route. The hosted
|
|
39
|
+
// exchange is the one mint the policy performs (via the injected
|
|
40
|
+
// `exchangeApiKey`); every other route simply returns the bearer to use. The
|
|
41
|
+
// switch below applies scope and sets up the refresh scheduler for each case.
|
|
48
42
|
const cred = await resolveCredential({
|
|
49
43
|
apiKeyValue,
|
|
50
44
|
configuredApiKey,
|
|
@@ -68,12 +62,12 @@ export async function resolveParticipantIdentity(input) {
|
|
|
68
62
|
});
|
|
69
63
|
switch (cred.kind) {
|
|
70
64
|
case 'publishable':
|
|
71
|
-
// `pk_`
|
|
72
|
-
// as the bearer and
|
|
73
|
-
// never expires and there is nothing to refresh. The
|
|
74
|
-
//
|
|
75
|
-
// we still call `/auth/identity`
|
|
76
|
-
//
|
|
65
|
+
// `pk_` is a long-lived, browser-safe, read-only project key. It is used
|
|
66
|
+
// directly as the bearer and is never exchanged for a short-lived
|
|
67
|
+
// capability, so it never expires and there is nothing to refresh. The
|
|
68
|
+
// server resolves the organization and read-only scope from the key itself;
|
|
69
|
+
// we still call `/auth/identity` with the `pk_` bearer to learn the account
|
|
70
|
+
// scope and sync groups for the bootstrap cache.
|
|
77
71
|
return resolveViaIdentity({
|
|
78
72
|
bearer: cred.getBearer,
|
|
79
73
|
baseUrl,
|
|
@@ -105,8 +99,8 @@ export async function resolveParticipantIdentity(input) {
|
|
|
105
99
|
auth,
|
|
106
100
|
});
|
|
107
101
|
case 'explicit': {
|
|
108
|
-
//
|
|
109
|
-
//
|
|
102
|
+
// Explicit self-hosted identity: the caller supplied its own organization id
|
|
103
|
+
// and user or agent id.
|
|
110
104
|
const userId = kind === 'agent' ? options.agentId : options.user.id;
|
|
111
105
|
const accountScope = internalOptions.organizationId;
|
|
112
106
|
bootstrapHelper.setCacheScope(accountScope);
|
|
@@ -125,24 +119,21 @@ export async function resolveParticipantIdentity(input) {
|
|
|
125
119
|
}
|
|
126
120
|
}
|
|
127
121
|
/**
|
|
128
|
-
*
|
|
129
|
-
* (`ek_`/`rk_` or explicit
|
|
130
|
-
* server resolves the identity, and caller-passed
|
|
131
|
-
* server-resolved set.
|
|
122
|
+
* Resolves identity through the `/auth/identity` endpoint for the publishable
|
|
123
|
+
* (`pk_`) and pre-minted (`ek_`/`rk_` or explicit capability token) routes. The
|
|
124
|
+
* bearer is used as-is, the server resolves the identity, and any caller-passed
|
|
125
|
+
* sync groups are merged with the server-resolved set.
|
|
132
126
|
*/
|
|
133
127
|
async function resolveViaIdentity(input) {
|
|
134
128
|
const { bearer, baseUrl, options, bootstrapHelper, auth } = input;
|
|
135
129
|
const identity = await resolveIdentity({ baseUrl, authToken: bearer });
|
|
136
|
-
// Merge caller
|
|
137
|
-
// the server
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
// from `effectiveSyncGroups`, the cap-narrowed list). Empty syncGroups →
|
|
144
|
-
// server bootstrap falls back to `['default']` → no deltas fan out → live
|
|
145
|
-
// updates appear only on hard reload.
|
|
130
|
+
// Merge the caller's sync groups with the server-resolved set rather than
|
|
131
|
+
// letting the server response overwrite them. A client may compose groups such
|
|
132
|
+
// as `['default', 'org:<id>', 'user:<id>', 'team:<id>']` from the resolved
|
|
133
|
+
// session and pass them in; the identity endpoint can return an empty set (for
|
|
134
|
+
// example, for cookie-authenticated users), and an empty set makes the server
|
|
135
|
+
// bootstrap fall back to `['default']`, so no deltas fan out and live updates
|
|
136
|
+
// appear only on a hard reload. Merging keeps the caller's groups intact.
|
|
146
137
|
const callerGroups = options.syncGroups ?? [];
|
|
147
138
|
const mergedSyncGroups = callerGroups.length > 0
|
|
148
139
|
? [...new Set([...callerGroups, ...identity.syncGroups])]
|
|
@@ -161,9 +152,9 @@ async function resolveViaIdentity(input) {
|
|
|
161
152
|
};
|
|
162
153
|
}
|
|
163
154
|
async function resolveHosted(input) {
|
|
164
|
-
//
|
|
165
|
-
// already exchanged the apiKey
|
|
166
|
-
//
|
|
155
|
+
// The managed-cloud shape, `Ablo({ schema, apiKey })`. The credential policy has
|
|
156
|
+
// already exchanged the apiKey via `exchangeApiKey`; here we apply the returned
|
|
157
|
+
// scope and set up the refresh scheduler.
|
|
167
158
|
const { exchange } = input.cred;
|
|
168
159
|
const baseUrl = input.baseUrl;
|
|
169
160
|
// The refresh path re-runs `exchangeApiKey` with a freshly-resolved apiKey, so
|
|
@@ -179,12 +170,10 @@ async function resolveHosted(input) {
|
|
|
179
170
|
input.bootstrapHelper.setCacheScope(exchange.scope.organizationId);
|
|
180
171
|
input.bootstrapHelper.setSyncGroups(exchange.scope.syncGroups);
|
|
181
172
|
input.auth.setAuthToken(exchange.token);
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// rotation. Rationale + tradeoffs in
|
|
187
|
-
// `packages/sync-engine/src/auth/refreshScheduler.ts`
|
|
173
|
+
// Capability tokens carry a server-set TTL (3600s by default). Without proactive
|
|
174
|
+
// refresh, the socket would be force-closed at expiry, or the next reconnect
|
|
175
|
+
// would fail with a 401. The scheduler re-mints ahead of that, so the consumer
|
|
176
|
+
// never sees the rotation.
|
|
188
177
|
const refreshScheduler = createRefreshScheduler({
|
|
189
178
|
initialExpiresAtMs: Date.parse(exchange.expiresAt),
|
|
190
179
|
refresh: async () => {
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Extracted from `Ablo.ts` — the factory calls `registerModelsFromSchema`
|
|
9
|
-
* right after `createInternalComponents` builds the registry. The zod unwrap
|
|
10
|
-
* helpers stay module-local.
|
|
2
|
+
* Registers model classes from a declarative schema. {@link registerModelsFromSchema}
|
|
3
|
+
* walks the schema and populates the model registry: for each model it builds a
|
|
4
|
+
* dynamic {@link Model} subclass (with `${field}Json` getters, computed getters,
|
|
5
|
+
* and opt-in field-level reactivity) and registers the model's properties,
|
|
6
|
+
* relations, and any local-database indexes.
|
|
11
7
|
*/
|
|
12
8
|
import type { Schema } from '../schema/schema.js';
|
|
13
9
|
import type { ModelRegistry } from '../ModelRegistry.js';
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Extracted from `Ablo.ts` — the factory calls `registerModelsFromSchema`
|
|
9
|
-
* right after `createInternalComponents` builds the registry. The zod unwrap
|
|
10
|
-
* helpers stay module-local.
|
|
2
|
+
* Registers model classes from a declarative schema. {@link registerModelsFromSchema}
|
|
3
|
+
* walks the schema and populates the model registry: for each model it builds a
|
|
4
|
+
* dynamic {@link Model} subclass (with `${field}Json` getters, computed getters,
|
|
5
|
+
* and opt-in field-level reactivity) and registers the model's properties,
|
|
6
|
+
* relations, and any local-database indexes.
|
|
11
7
|
*/
|
|
12
8
|
import { z } from 'zod';
|
|
13
9
|
import { baseFieldsSchema } from '../schema/schema.js';
|
|
@@ -32,25 +28,22 @@ export function registerModelsFromSchema(schema, registry) {
|
|
|
32
28
|
}
|
|
33
29
|
// Create a dynamic Model subclass with JSON sub-property getters.
|
|
34
30
|
//
|
|
35
|
-
// Field-level
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
// `registerModelsFromSchema`), so the default is ~one atom per scalar field
|
|
44
|
-
// per loaded row — cheap — not a deep atom tree per blob.
|
|
31
|
+
// Field-level reactivity is on by default. A reactive read must re-render when
|
|
32
|
+
// a remote delta mutates a row in place, which is the common collaborative
|
|
33
|
+
// case; without per-field reactivity that update fires no reaction and the UI
|
|
34
|
+
// silently goes stale. A model opts out with `lazyObservable: false`, which
|
|
35
|
+
// suits only very large read-only list models where per-field atoms cost more
|
|
36
|
+
// than the coarser entry-replaced reactivity already provides. JSON fields
|
|
37
|
+
// register as reference-tracked, so the default is about one atom per scalar
|
|
38
|
+
// field per loaded row — cheap — not a deep atom tree per blob.
|
|
45
39
|
const isLazy = modelDef.lazyObservable !== false;
|
|
46
40
|
// Base provenance fields (`organizationId`, `createdBy`) live in
|
|
47
|
-
// `baseFieldsSchema`, not the per-model `shape`. The server stamps
|
|
48
|
-
// them (camelCased on the wire), but hydration
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
// `
|
|
52
|
-
//
|
|
53
|
-
// are already seeded by the base Model constructor, so they're excluded.
|
|
41
|
+
// `baseFieldsSchema`, not in the per-model `shape`. The server stamps and emits
|
|
42
|
+
// them (camelCased on the wire), but hydration only assigns keys that already
|
|
43
|
+
// exist as own or prototype properties — so without a slot here, reads like
|
|
44
|
+
// `row.createdBy` or `row.organizationId` would silently be `undefined`.
|
|
45
|
+
// `id`, `createdAt`, and `updatedAt` are already seeded by the base Model
|
|
46
|
+
// constructor, so they are excluded.
|
|
54
47
|
const fieldNames = [
|
|
55
48
|
...Object.keys(modelDef.shape),
|
|
56
49
|
...Object.keys(baseFieldsSchema.shape).filter((f) => f !== 'id' && f !== 'createdAt' && f !== 'updatedAt' && !(f in modelDef.shape)),
|
|
@@ -67,18 +60,14 @@ export function registerModelsFromSchema(schema, registry) {
|
|
|
67
60
|
autoFill: modelDef.autoFill,
|
|
68
61
|
requiredFields: modelDef.requiredFields,
|
|
69
62
|
});
|
|
70
|
-
// Collect the
|
|
71
|
-
//
|
|
72
|
-
// Matches Linear's opt-in model (see wzhudev/reverse-linear-sync-engine):
|
|
73
|
-
// `@Reference(..., { indexed: true })`. Only `belongsTo` relations that
|
|
74
|
-
// explicitly set `{ index: true }` in their options get an IDB secondary
|
|
75
|
-
// index. Every other FK (and every scalar) is resolved via in-memory
|
|
76
|
-
// ObjectPool scans, which are fast enough at org-scope sizes (~10k rows)
|
|
77
|
-
// and reactive via MobX.
|
|
63
|
+
// Collect the fields that should get a local-database secondary index.
|
|
78
64
|
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
65
|
+
// Only `belongsTo` relations that explicitly set `{ index: true }` are indexed.
|
|
66
|
+
// Every other foreign key, and every scalar, is resolved by in-memory scans,
|
|
67
|
+
// which are fast enough at organization-scope sizes (on the order of 10k rows)
|
|
68
|
+
// and stay reactive. Indexing is opt-in deliberately: auto-indexing every
|
|
69
|
+
// foreign key inflates write amplification for the many keys never queried by
|
|
70
|
+
// id, and indexing every scalar is worse still.
|
|
82
71
|
const indexedFields = new Set();
|
|
83
72
|
for (const relDef of Object.values(modelDef.relations)) {
|
|
84
73
|
if (relDef.type === 'belongsTo' && relDef.foreignKey && relDef.options?.index === true) {
|
|
@@ -89,16 +78,15 @@ export function registerModelsFromSchema(schema, registry) {
|
|
|
89
78
|
for (const [fieldName, rawZodType] of Object.entries(modelDef.shape)) {
|
|
90
79
|
const zodType = rawZodType;
|
|
91
80
|
const isOptional = zodType.isOptional?.() ?? false;
|
|
92
|
-
// A field is indexed if it
|
|
93
|
-
// relation.
|
|
94
|
-
// consumers using `field.*().indexed()
|
|
81
|
+
// A field is indexed if it is the foreign key of a
|
|
82
|
+
// `belongsTo({ index: true })` relation. A `description === 'indexed'` tag
|
|
83
|
+
// also works, for consumers using the `field.*().indexed()` builder.
|
|
95
84
|
const isIndexed = indexedFields.has(fieldName) || zodType.description === 'indexed';
|
|
96
|
-
// JSON-typed fields (per the schema's wire-type tag) are opaque
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
// only reassignment, which is how blob consumers actually use them.
|
|
85
|
+
// JSON-typed fields (per the schema's wire-type tag) are opaque blobs —
|
|
86
|
+
// chart specs, rich-text documents, style maps. Deep reactivity on them
|
|
87
|
+
// would walk every nested property and create an atom per leaf, producing a
|
|
88
|
+
// storm of updates on each commit or streaming change. Reference tracking
|
|
89
|
+
// watches only reassignment, which is how blob consumers actually use them.
|
|
102
90
|
const wireType = modelDef.fields?.[fieldName]?.type;
|
|
103
91
|
const observability = wireType === 'json' ? 'ref' : undefined;
|
|
104
92
|
registry.registerProperty(modelName, fieldName, {
|
|
@@ -185,9 +173,8 @@ function unwrapZodType(schema) {
|
|
|
185
173
|
continue;
|
|
186
174
|
}
|
|
187
175
|
if (current instanceof z.ZodDefault) {
|
|
188
|
-
// v4
|
|
189
|
-
//
|
|
190
|
-
// ZodDefault. Use it — it's the same runtime function.
|
|
176
|
+
// Zod v4 unwraps a default via `.unwrap()`, the same runtime function older
|
|
177
|
+
// versions exposed as `removeDefault`.
|
|
191
178
|
current = current.unwrap();
|
|
192
179
|
continue;
|
|
193
180
|
}
|
|
@@ -205,50 +192,55 @@ function isZodObject(schema) {
|
|
|
205
192
|
}
|
|
206
193
|
/** Create a Model subclass for a schema-defined model */
|
|
207
194
|
function createDynamicModelClass(modelName, jsonSubFields, fieldNames, computed, lazyObservable = false) {
|
|
195
|
+
// Names `toReactiveSnapshot` materializes onto plain snapshots: every
|
|
196
|
+
// `${field}Json` getter plus every schema-declared computed. Without this,
|
|
197
|
+
// snapshot rows silently drop getters that the schema's inferred row type
|
|
198
|
+
// declares — reads like `snapshot.settingsObject` would be `undefined`.
|
|
199
|
+
const derivedGetterNames = Object.freeze([
|
|
200
|
+
...jsonSubFields.map(({ fieldName }) => `${fieldName}Json`),
|
|
201
|
+
...Object.keys(computed ?? {}),
|
|
202
|
+
]);
|
|
208
203
|
const ModelClass = class extends Model {
|
|
209
204
|
_modelName = modelName;
|
|
205
|
+
getDerivedGetterNames() {
|
|
206
|
+
return derivedGetterNames;
|
|
207
|
+
}
|
|
210
208
|
constructor(data) {
|
|
211
209
|
super(data);
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
// the listener early-return on those writes so `modifiedProperties`
|
|
219
|
-
// doesn't get polluted with every field of every hydrated model.
|
|
210
|
+
// Suppress change tracking during initial hydration. `makeObservable()`
|
|
211
|
+
// installs a listener per schema property that forwards writes to the
|
|
212
|
+
// transaction queue, so direct assignments like `row.position = next` still
|
|
213
|
+
// round-trip. During construction we are writing wire data, not user edits,
|
|
214
|
+
// so this flag lets that listener skip these writes and keeps the set of
|
|
215
|
+
// modified properties from filling up with every field of every hydrated row.
|
|
220
216
|
//
|
|
221
|
-
// The listener is installed by `makeObservable()` below
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
// steps later.
|
|
217
|
+
// The listener is installed by `makeObservable()` below, so writes before
|
|
218
|
+
// that line never reach it; this flag is defensive in case a subclass or call
|
|
219
|
+
// path later reorders the steps.
|
|
225
220
|
this._isConstructing = true;
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
221
|
+
// Reactive fields must exist as own properties before `makeObservable()` runs.
|
|
222
|
+
// The base Model sets only id, createdAt, and updatedAt, so schema fields
|
|
223
|
+
// (title, userId, and so on) are initialized here for the annotations to find.
|
|
229
224
|
for (const field of fieldNames) {
|
|
230
225
|
if (!(field in this)) {
|
|
231
226
|
this[field] = data?.[field] ?? undefined;
|
|
232
227
|
}
|
|
233
228
|
}
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
// from the
|
|
237
|
-
// read-only
|
|
229
|
+
// When field-level reactivity is enabled (the default; a model turns it off
|
|
230
|
+
// with `lazyObservable: false`), make each field observable. Without it,
|
|
231
|
+
// reactivity comes only from the coarser entry-replaced pattern, which is
|
|
232
|
+
// cheap for read-only lists but invisible to in-place field mutations.
|
|
238
233
|
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
// a pass (toolbar close, deselect).
|
|
234
|
+
// Collaborative editors need field-level reactivity so both remote deltas and
|
|
235
|
+
// local edits surface through observer components without the whole cache
|
|
236
|
+
// entry being replaced. Otherwise an in-place mutation such as
|
|
237
|
+
// `row.position.x = 500` emits nothing and the UI lags until an unrelated
|
|
238
|
+
// change triggers a pass.
|
|
245
239
|
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
// correctly — reimplementing `makeObservable` inline here would miss
|
|
251
|
-
// those seams.
|
|
240
|
+
// This delegates to the inherited `Model.makeObservable()` so the annotations
|
|
241
|
+
// come from the same registry the rest of the model reads, keeping computed
|
|
242
|
+
// getters, reference collections, custom getters and setters, and change
|
|
243
|
+
// tracking all consistent; reimplementing it inline here would miss those.
|
|
252
244
|
if (lazyObservable) {
|
|
253
245
|
this.makeObservable();
|
|
254
246
|
}
|
|
@@ -258,15 +250,14 @@ function createDynamicModelClass(modelName, jsonSubFields, fieldNames, computed,
|
|
|
258
250
|
return this._modelName;
|
|
259
251
|
}
|
|
260
252
|
};
|
|
261
|
-
// Generate
|
|
253
|
+
// Generate `${field}Json` getters for JSON fields that have sub-properties.
|
|
262
254
|
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
// raw value. This replaces the hand-coded metadataObject + sub-property
|
|
266
|
-
// getter pattern that 11+ Ablo models currently repeat.
|
|
255
|
+
// Each getter reads the raw JSON from the instance, parses it, applies the
|
|
256
|
+
// sub-schema's defaults, and caches the result keyed by the raw value.
|
|
267
257
|
//
|
|
268
|
-
// Example: field named
|
|
269
|
-
//
|
|
258
|
+
// Example: a field named `metadata` with sub-schema
|
|
259
|
+
// `{ icon: z.string().default('presentation') }` yields a `metadataJson` getter
|
|
260
|
+
// returning `{ icon: 'presentation', ... }` — typed and cached.
|
|
270
261
|
for (const { fieldName, subSchema } of jsonSubFields) {
|
|
271
262
|
const getterName = `${fieldName}Json`;
|
|
272
263
|
const cacheKey = `__${fieldName}JsonCache`;
|