@abloatai/ablo 0.36.0 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +55 -2014
- package/NOTICE +2 -2
- package/README.md +45 -63
- package/assets/banner.png +0 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +2 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -0
- package/dist/coordination.d.ts +2 -0
- package/dist/coordination.d.ts.map +1 -0
- package/dist/coordination.js +2 -0
- package/dist/coordination.js.map +1 -0
- package/dist/index.d.ts +3 -114
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -163
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +4 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +3 -0
- package/dist/react.js.map +1 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/dist/source-conformance.d.ts +2 -0
- package/dist/source-conformance.d.ts.map +1 -0
- package/dist/source-conformance.js +2 -0
- package/dist/source-conformance.js.map +1 -0
- package/dist/source-drizzle.d.ts +2 -0
- package/dist/source-drizzle.d.ts.map +1 -0
- package/dist/source-drizzle.js +2 -0
- package/dist/source-drizzle.js.map +1 -0
- package/dist/source-kysely.d.ts +2 -0
- package/dist/source-kysely.d.ts.map +1 -0
- package/dist/source-kysely.js +2 -0
- package/dist/source-kysely.js.map +1 -0
- package/dist/source-next.d.ts +2 -0
- package/dist/source-next.d.ts.map +1 -0
- package/dist/source-next.js +2 -0
- package/dist/source-next.js.map +1 -0
- package/dist/source.d.ts +2 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +2 -0
- package/dist/source.js.map +1 -0
- package/dist/wire.d.ts +2 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +2 -0
- package/dist/wire.js.map +1 -0
- package/docs/agents.md +1 -1
- package/docs/api-keys.md +7 -6
- package/docs/api.md +10 -10
- package/docs/client-behavior.md +5 -5
- package/docs/coordination.md +52 -62
- package/docs/data-sources.md +1 -1
- package/docs/examples/agent-human.md +4 -4
- package/docs/examples/ai-sdk-tool.md +1 -1
- package/docs/examples/existing-python-backend.md +15 -4
- package/docs/examples/nextjs.md +27 -6
- package/docs/examples/server-agent.md +2 -2
- package/docs/how-it-works.md +4 -4
- package/docs/identity.md +2 -1
- package/docs/integration-guide.md +24 -13
- package/docs/internal/README.md +18 -0
- package/docs/internal/agent-fleet-coordination-design.md +171 -0
- package/docs/internal/agent-orchestration.md +58 -0
- package/docs/internal/commit-identifiers.md +91 -0
- package/docs/internal/concurrency-open-decisions.md +37 -0
- package/docs/internal/data-source-reverse-channel.md +150 -0
- package/docs/internal/per-field-conflict-detection.md +165 -0
- package/docs/internal/postgres-replication.md +64 -0
- package/docs/internal/serializable-schema.md +119 -0
- package/docs/internal/structure.md +32 -0
- package/docs/mcp.md +2 -2
- package/docs/migration.md +3 -3
- package/docs/quickstart.md +2 -2
- package/docs/react.md +5 -5
- package/docs/schema-contract.md +3 -3
- package/docs/sessions.md +91 -37
- package/examples/README.md +2 -2
- package/examples/data-source/README.md +1 -1
- package/examples/data-source/ablo-driver.ts +1 -1
- package/examples/data-source/customer-server.ts +1 -1
- package/examples/data-source/run.ts +1 -1
- package/examples/data-source/schema.ts +1 -1
- package/examples/quickstart.ts +2 -2
- package/llms.txt +8 -8
- package/package.json +63 -166
- package/bin/ablo.cjs +0 -39
- package/dist/BaseSyncedStore.d.ts +0 -843
- package/dist/BaseSyncedStore.js +0 -1971
- package/dist/Database.d.ts +0 -323
- package/dist/Database.js +0 -1502
- package/dist/InstanceCache.d.ts +0 -237
- package/dist/InstanceCache.js +0 -1166
- package/dist/LazyReferenceCollection.d.ts +0 -177
- package/dist/LazyReferenceCollection.js +0 -461
- package/dist/Model.d.ts +0 -454
- package/dist/Model.js +0 -919
- package/dist/ModelRegistry.d.ts +0 -225
- package/dist/ModelRegistry.js +0 -539
- package/dist/NetworkMonitor.d.ts +0 -28
- package/dist/NetworkMonitor.js +0 -79
- package/dist/RuntimeContext.d.ts +0 -52
- package/dist/RuntimeContext.js +0 -80
- package/dist/SyncClient.d.ts +0 -541
- package/dist/SyncClient.js +0 -2202
- package/dist/adapters/alwaysOnline.d.ts +0 -14
- package/dist/adapters/alwaysOnline.js +0 -17
- package/dist/adapters/inMemoryStorage.d.ts +0 -31
- package/dist/adapters/inMemoryStorage.js +0 -110
- package/dist/ai-sdk/coordinatedTool.d.ts +0 -120
- package/dist/ai-sdk/coordinatedTool.js +0 -134
- package/dist/ai-sdk/coordinationContext.d.ts +0 -46
- package/dist/ai-sdk/coordinationContext.js +0 -106
- package/dist/ai-sdk/index.d.ts +0 -121
- package/dist/ai-sdk/index.js +0 -121
- package/dist/ai-sdk/wrap.d.ts +0 -65
- package/dist/ai-sdk/wrap.js +0 -39
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +0 -8
- package/dist/batching/index.d.ts +0 -55
- package/dist/batching/index.js +0 -147
- package/dist/client/Ablo.d.ts +0 -231
- package/dist/client/Ablo.js +0 -160
- package/dist/client/abloClient.d.ts +0 -309
- package/dist/client/abloClient.js +0 -13
- package/dist/client/clientPrelude.d.ts +0 -52
- package/dist/client/clientPrelude.js +0 -60
- package/dist/client/consoleLogger.d.ts +0 -35
- package/dist/client/consoleLogger.js +0 -44
- package/dist/client/coreClient.d.ts +0 -60
- package/dist/client/coreClient.js +0 -118
- package/dist/client/createInternalComponents.d.ts +0 -50
- package/dist/client/createInternalComponents.js +0 -98
- package/dist/client/createModelProxy.d.ts +0 -248
- package/dist/client/createModelProxy.js +0 -884
- package/dist/client/humans.d.ts +0 -69
- package/dist/client/humans.js +0 -78
- package/dist/client/modelRegistration.d.ts +0 -10
- package/dist/client/modelRegistration.js +0 -312
- package/dist/client/options.d.ts +0 -461
- package/dist/client/options.js +0 -7
- package/dist/client/reactiveEngine.d.ts +0 -53
- package/dist/client/reactiveEngine.js +0 -688
- package/dist/client/resourceTypes.d.ts +0 -12
- package/dist/client/resourceTypes.js +0 -10
- package/dist/client/schemaConfig.d.ts +0 -44
- package/dist/client/schemaConfig.js +0 -185
- package/dist/client/storeCluster.d.ts +0 -47
- package/dist/client/storeCluster.js +0 -118
- package/dist/client/storeLifecycle.d.ts +0 -61
- package/dist/client/storeLifecycle.js +0 -231
- package/dist/client/validateAbloOptions.d.ts +0 -42
- package/dist/client/validateAbloOptions.js +0 -43
- package/dist/client/wsMutationExecutor.d.ts +0 -27
- package/dist/client/wsMutationExecutor.js +0 -72
- package/dist/context.d.ts +0 -42
- package/dist/context.js +0 -81
- package/dist/coordination/ClaimLog.d.ts +0 -26
- package/dist/coordination/ClaimLog.js +0 -32
- package/dist/coordination/index.d.ts +0 -1
- package/dist/coordination/index.js +0 -8
- package/dist/core/index.d.ts +0 -33
- package/dist/core/index.js +0 -48
- package/dist/docs/catalog.d.ts +0 -72
- package/dist/docs/catalog.js +0 -230
- package/dist/docs/index.d.ts +0 -10
- package/dist/docs/index.js +0 -10
- package/dist/environment.d.ts +0 -1
- package/dist/environment.js +0 -8
- package/dist/interfaces/index.d.ts +0 -311
- package/dist/interfaces/index.js +0 -9
- package/dist/keys/index.d.ts +0 -1
- package/dist/keys/index.js +0 -8
- package/dist/mutators/RecordingMutation.d.ts +0 -36
- package/dist/mutators/RecordingMutation.js +0 -182
- package/dist/mutators/Transaction.d.ts +0 -40
- package/dist/mutators/Transaction.js +0 -58
- package/dist/mutators/UndoManager.d.ts +0 -258
- package/dist/mutators/UndoManager.js +0 -658
- package/dist/mutators/defineMutators.d.ts +0 -60
- package/dist/mutators/defineMutators.js +0 -18
- package/dist/mutators/inverseOp.d.ts +0 -126
- package/dist/mutators/inverseOp.js +0 -71
- package/dist/mutators/mutateActions.d.ts +0 -45
- package/dist/mutators/mutateActions.js +0 -105
- package/dist/mutators/readerActions.d.ts +0 -33
- package/dist/mutators/readerActions.js +0 -57
- package/dist/mutators/undoApply.d.ts +0 -51
- package/dist/mutators/undoApply.js +0 -117
- package/dist/policy/index.d.ts +0 -21
- package/dist/policy/index.js +0 -20
- package/dist/query/QueryProcessor.d.ts +0 -75
- package/dist/query/QueryProcessor.js +0 -255
- package/dist/query/client.d.ts +0 -64
- package/dist/query/client.js +0 -138
- package/dist/query/types.d.ts +0 -85
- package/dist/query/types.js +0 -16
- package/dist/react/AbloProvider.d.ts +0 -242
- package/dist/react/AbloProvider.js +0 -458
- package/dist/react/ClientSideSuspense.d.ts +0 -36
- package/dist/react/ClientSideSuspense.js +0 -17
- package/dist/react/DefaultFallback.d.ts +0 -24
- package/dist/react/DefaultFallback.js +0 -43
- package/dist/react/context.d.ts +0 -55
- package/dist/react/context.js +0 -29
- package/dist/react/createAbloReact.d.ts +0 -56
- package/dist/react/createAbloReact.js +0 -51
- package/dist/react/index.d.ts +0 -62
- package/dist/react/index.js +0 -69
- package/dist/react/internalContext.d.ts +0 -33
- package/dist/react/internalContext.js +0 -3
- package/dist/react/useAblo.d.ts +0 -82
- package/dist/react/useAblo.js +0 -120
- package/dist/react/useCurrentUserId.d.ts +0 -22
- package/dist/react/useCurrentUserId.js +0 -34
- package/dist/react/useErrorListener.d.ts +0 -20
- package/dist/react/useErrorListener.js +0 -38
- package/dist/react/useMutationFailureListener.d.ts +0 -26
- package/dist/react/useMutationFailureListener.js +0 -38
- package/dist/react/useMutators.d.ts +0 -56
- package/dist/react/useMutators.js +0 -84
- package/dist/react/useReactive.d.ts +0 -35
- package/dist/react/useReactive.js +0 -123
- package/dist/react/useSyncStatus.d.ts +0 -59
- package/dist/react/useSyncStatus.js +0 -76
- package/dist/react/useUndoScope.d.ts +0 -34
- package/dist/react/useUndoScope.js +0 -81
- package/dist/schema/coordination.d.ts +0 -112
- package/dist/schema/coordination.js +0 -133
- package/dist/schema/ddl.d.ts +0 -97
- package/dist/schema/ddl.js +0 -491
- package/dist/schema/ddlLock.d.ts +0 -35
- package/dist/schema/ddlLock.js +0 -46
- package/dist/schema/diff.d.ts +0 -225
- package/dist/schema/diff.js +0 -289
- package/dist/schema/generate.d.ts +0 -19
- package/dist/schema/generate.js +0 -86
- package/dist/schema/index.d.ts +0 -42
- package/dist/schema/index.js +0 -80
- package/dist/schema/queries.d.ts +0 -201
- package/dist/schema/queries.js +0 -144
- package/dist/schema/select.d.ts +0 -40
- package/dist/schema/select.js +0 -90
- package/dist/schema/serialize.d.ts +0 -115
- package/dist/schema/serialize.js +0 -265
- package/dist/schema/sugar.d.ts +0 -109
- package/dist/schema/sugar.js +0 -83
- package/dist/schema/syncDeltaRow.d.ts +0 -6
- package/dist/schema/syncDeltaRow.js +0 -6
- package/dist/server/adapter.d.ts +0 -173
- package/dist/server/adapter.js +0 -18
- package/dist/server/commit.d.ts +0 -107
- package/dist/server/commit.js +0 -1
- package/dist/server/index.d.ts +0 -14
- package/dist/server/index.js +0 -2
- package/dist/server/readConfig.d.ts +0 -80
- package/dist/server/readConfig.js +0 -8
- package/dist/server/storageMode.d.ts +0 -23
- package/dist/server/storageMode.js +0 -17
- package/dist/source/adapter.d.ts +0 -83
- package/dist/source/adapter.js +0 -24
- package/dist/source/adapters/drizzle.d.ts +0 -48
- package/dist/source/adapters/drizzle.js +0 -219
- package/dist/source/adapters/kysely.d.ts +0 -42
- package/dist/source/adapters/kysely.js +0 -205
- package/dist/source/adapters/kyselyMutationCore.d.ts +0 -76
- package/dist/source/adapters/kyselyMutationCore.js +0 -125
- package/dist/source/adapters/memory.d.ts +0 -13
- package/dist/source/adapters/memory.js +0 -130
- package/dist/source/adapters/prisma.d.ts +0 -63
- package/dist/source/adapters/prisma.js +0 -202
- package/dist/source/conformance.d.ts +0 -37
- package/dist/source/conformance.js +0 -215
- package/dist/source/connector.d.ts +0 -95
- package/dist/source/connector.js +0 -266
- package/dist/source/connectorProtocol.d.ts +0 -154
- package/dist/source/connectorProtocol.js +0 -163
- package/dist/source/contract.d.ts +0 -195
- package/dist/source/contract.js +0 -164
- package/dist/source/factory.d.ts +0 -92
- package/dist/source/factory.js +0 -286
- package/dist/source/idempotency.d.ts +0 -61
- package/dist/source/idempotency.js +0 -144
- package/dist/source/index.d.ts +0 -23
- package/dist/source/index.js +0 -30
- package/dist/source/migrations.d.ts +0 -21
- package/dist/source/migrations.js +0 -103
- package/dist/source/next.d.ts +0 -32
- package/dist/source/next.js +0 -25
- package/dist/source/pushQueue.d.ts +0 -134
- package/dist/source/pushQueue.js +0 -256
- package/dist/source/signing.d.ts +0 -92
- package/dist/source/signing.js +0 -162
- package/dist/source/types.d.ts +0 -401
- package/dist/source/types.js +0 -59
- package/dist/storeContract.d.ts +0 -145
- package/dist/storeContract.js +0 -12
- package/dist/stores/DatabaseManager.d.ts +0 -107
- package/dist/stores/DatabaseManager.js +0 -388
- package/dist/stores/ObjectStore.d.ts +0 -115
- package/dist/stores/ObjectStore.js +0 -393
- package/dist/stores/ObjectStoreContract.d.ts +0 -38
- package/dist/stores/ObjectStoreContract.js +0 -1
- package/dist/stores/StoreManager.d.ts +0 -114
- package/dist/stores/StoreManager.js +0 -304
- package/dist/stores/SyncActionStore.d.ts +0 -99
- package/dist/stores/SyncActionStore.js +0 -506
- package/dist/stores/openIDBWithTimeout.d.ts +0 -65
- package/dist/stores/openIDBWithTimeout.js +0 -153
- package/dist/stores/syncAction.d.ts +0 -26
- package/dist/stores/syncAction.js +0 -16
- package/dist/surface.d.ts +0 -36
- package/dist/surface.js +0 -75
- package/dist/sync/BootstrapFetcher.d.ts +0 -284
- package/dist/sync/BootstrapFetcher.js +0 -964
- package/dist/sync/ConnectionManager.d.ts +0 -8
- package/dist/sync/ConnectionManager.js +0 -8
- package/dist/sync/OnDemandLoader.d.ts +0 -231
- package/dist/sync/OnDemandLoader.js +0 -743
- package/dist/sync/SubscriptionManager.d.ts +0 -159
- package/dist/sync/SubscriptionManager.js +0 -243
- package/dist/sync/SyncWebSocket.d.ts +0 -173
- package/dist/sync/SyncWebSocket.js +0 -438
- package/dist/sync/awaitClaimGrant.d.ts +0 -6
- package/dist/sync/awaitClaimGrant.js +0 -6
- package/dist/sync/bootstrapApply.d.ts +0 -73
- package/dist/sync/bootstrapApply.js +0 -73
- package/dist/sync/commitFrames.d.ts +0 -8
- package/dist/sync/commitFrames.js +0 -8
- package/dist/sync/contextPorts.d.ts +0 -18
- package/dist/sync/contextPorts.js +0 -31
- package/dist/sync/createClaimStream.d.ts +0 -7
- package/dist/sync/createClaimStream.js +0 -7
- package/dist/sync/createPresenceStream.d.ts +0 -69
- package/dist/sync/createPresenceStream.js +0 -200
- package/dist/sync/createSnapshot.d.ts +0 -29
- package/dist/sync/createSnapshot.js +0 -118
- package/dist/sync/credentialLifecycle.d.ts +0 -7
- package/dist/sync/credentialLifecycle.js +0 -7
- package/dist/sync/deltaPipeline.d.ts +0 -114
- package/dist/sync/deltaPipeline.js +0 -278
- package/dist/sync/groupChange.d.ts +0 -116
- package/dist/sync/groupChange.js +0 -244
- package/dist/sync/participants.d.ts +0 -132
- package/dist/sync/participants.js +0 -346
- package/dist/sync/persistedPrefix.d.ts +0 -12
- package/dist/sync/persistedPrefix.js +0 -22
- package/dist/sync/schemaDrift.d.ts +0 -55
- package/dist/sync/schemaDrift.js +0 -53
- package/dist/sync/schemas.d.ts +0 -71
- package/dist/sync/schemas.js +0 -94
- package/dist/sync/syncCursor.d.ts +0 -40
- package/dist/sync/syncCursor.js +0 -55
- package/dist/sync/syncPlan.d.ts +0 -54
- package/dist/sync/syncPlan.js +0 -50
- package/dist/sync/wsFrameHandlers.d.ts +0 -8
- package/dist/sync/wsFrameHandlers.js +0 -8
- package/dist/syncLog/contract.d.ts +0 -20
- package/dist/syncLog/contract.js +0 -19
- package/dist/syncLog/index.d.ts +0 -1
- package/dist/syncLog/index.js +0 -1
- package/dist/transaction/ablo.d.ts +0 -88
- package/dist/transaction/ablo.js +0 -33
- package/dist/transaction/auth/apiKey.d.ts +0 -152
- package/dist/transaction/auth/apiKey.js +0 -419
- package/dist/transaction/auth/bootstrapScope.d.ts +0 -15
- package/dist/transaction/auth/bootstrapScope.js +0 -1
- package/dist/transaction/auth/capability.d.ts +0 -212
- package/dist/transaction/auth/capability.js +0 -224
- package/dist/transaction/auth/credentialEndpoint.d.ts +0 -61
- package/dist/transaction/auth/credentialEndpoint.js +0 -86
- package/dist/transaction/auth/credentialPolicy.d.ts +0 -148
- package/dist/transaction/auth/credentialPolicy.js +0 -125
- package/dist/transaction/auth/credentialSource.d.ts +0 -30
- package/dist/transaction/auth/credentialSource.js +0 -55
- package/dist/transaction/auth/hostedEndpoints.d.ts +0 -21
- package/dist/transaction/auth/hostedEndpoints.js +0 -21
- package/dist/transaction/auth/identity.d.ts +0 -55
- package/dist/transaction/auth/identity.js +0 -210
- package/dist/transaction/auth/index.d.ts +0 -162
- package/dist/transaction/auth/index.js +0 -304
- package/dist/transaction/auth/schemas.d.ts +0 -59
- package/dist/transaction/auth/schemas.js +0 -85
- package/dist/transaction/auth/sessionMint.d.ts +0 -28
- package/dist/transaction/auth/sessionMint.js +0 -85
- package/dist/transaction/coordination/awaitClaimGrant.d.ts +0 -56
- package/dist/transaction/coordination/awaitClaimGrant.js +0 -124
- package/dist/transaction/coordination/claimHeartbeatLoop.d.ts +0 -84
- package/dist/transaction/coordination/claimHeartbeatLoop.js +0 -108
- package/dist/transaction/coordination/claimMeta.d.ts +0 -49
- package/dist/transaction/coordination/claimMeta.js +0 -52
- package/dist/transaction/coordination/createClaimStream.d.ts +0 -64
- package/dist/transaction/coordination/createClaimStream.js +0 -475
- package/dist/transaction/coordination/events.d.ts +0 -74
- package/dist/transaction/coordination/events.js +0 -7
- package/dist/transaction/coordination/index.d.ts +0 -19
- package/dist/transaction/coordination/index.js +0 -45
- package/dist/transaction/coordination/locator.d.ts +0 -104
- package/dist/transaction/coordination/locator.js +0 -102
- package/dist/transaction/coordination/schema.d.ts +0 -1536
- package/dist/transaction/coordination/schema.js +0 -1177
- package/dist/transaction/coordination/targetConflict.d.ts +0 -2
- package/dist/transaction/coordination/targetConflict.js +0 -107
- package/dist/transaction/coordination/trace.d.ts +0 -78
- package/dist/transaction/coordination/trace.js +0 -138
- package/dist/transaction/durableWrites.d.ts +0 -62
- package/dist/transaction/durableWrites.js +0 -71
- package/dist/transaction/environment.d.ts +0 -105
- package/dist/transaction/environment.js +0 -108
- package/dist/transaction/errorCodes.d.ts +0 -403
- package/dist/transaction/errorCodes.js +0 -484
- package/dist/transaction/errors.d.ts +0 -428
- package/dist/transaction/errors.js +0 -686
- package/dist/transaction/footprint.d.ts +0 -111
- package/dist/transaction/footprint.js +0 -0
- package/dist/transaction/index.d.ts +0 -20
- package/dist/transaction/index.js +0 -20
- package/dist/transaction/keys/index.d.ts +0 -87
- package/dist/transaction/keys/index.js +0 -207
- package/dist/transaction/log/syncDeltaRow.d.ts +0 -158
- package/dist/transaction/log/syncDeltaRow.js +0 -95
- package/dist/transaction/logPosition.d.ts +0 -97
- package/dist/transaction/logPosition.js +0 -125
- package/dist/transaction/logger.d.ts +0 -16
- package/dist/transaction/logger.js +0 -7
- package/dist/transaction/observability.d.ts +0 -53
- package/dist/transaction/observability.js +0 -19
- package/dist/transaction/persistence.d.ts +0 -12
- package/dist/transaction/persistence.js +0 -11
- package/dist/transaction/plugin.d.ts +0 -285
- package/dist/transaction/plugin.js +0 -106
- package/dist/transaction/policy/types.d.ts +0 -217
- package/dist/transaction/policy/types.js +0 -126
- package/dist/transaction/resources/functionalUpdate.d.ts +0 -79
- package/dist/transaction/resources/functionalUpdate.js +0 -87
- package/dist/transaction/resources/httpResources.d.ts +0 -321
- package/dist/transaction/resources/httpResources.js +0 -7
- package/dist/transaction/resources/modelOperations.d.ts +0 -427
- package/dist/transaction/resources/modelOperations.js +0 -12
- package/dist/transaction/resources/mutationOptions.d.ts +0 -66
- package/dist/transaction/resources/mutationOptions.js +0 -9
- package/dist/transaction/resources/where.d.ts +0 -101
- package/dist/transaction/resources/where.js +0 -115
- package/dist/transaction/resources/writeOptionsSchema.d.ts +0 -47
- package/dist/transaction/resources/writeOptionsSchema.js +0 -73
- package/dist/transaction/schema/field.d.ts +0 -120
- package/dist/transaction/schema/field.js +0 -265
- package/dist/transaction/schema/fieldRef.d.ts +0 -38
- package/dist/transaction/schema/fieldRef.js +0 -11
- package/dist/transaction/schema/loadStrategy.d.ts +0 -45
- package/dist/transaction/schema/loadStrategy.js +0 -46
- package/dist/transaction/schema/model.d.ts +0 -379
- package/dist/transaction/schema/model.js +0 -123
- package/dist/transaction/schema/openapi.d.ts +0 -58
- package/dist/transaction/schema/openapi.js +0 -501
- package/dist/transaction/schema/relation.d.ts +0 -204
- package/dist/transaction/schema/relation.js +0 -104
- package/dist/transaction/schema/residency.d.ts +0 -29
- package/dist/transaction/schema/residency.js +0 -25
- package/dist/transaction/schema/roles.d.ts +0 -249
- package/dist/transaction/schema/roles.js +0 -230
- package/dist/transaction/schema/schema.d.ts +0 -351
- package/dist/transaction/schema/schema.js +0 -325
- package/dist/transaction/schema/tenancy.d.ts +0 -139
- package/dist/transaction/schema/tenancy.js +0 -190
- package/dist/transaction/transactionLayer.d.ts +0 -82
- package/dist/transaction/transactionLayer.js +0 -24
- package/dist/transaction/transactions/settlement/commitEnvelope.d.ts +0 -143
- package/dist/transaction/transactions/settlement/commitEnvelope.js +0 -161
- package/dist/transaction/transactions/settlement/httpCommitEnvelope.d.ts +0 -53
- package/dist/transaction/transactions/settlement/httpCommitEnvelope.js +0 -207
- package/dist/transaction/transactions/settlement/idempotencyKey.d.ts +0 -10
- package/dist/transaction/transactions/settlement/idempotencyKey.js +0 -9
- package/dist/transaction/transactions/settlement/pendingWrite.d.ts +0 -112
- package/dist/transaction/transactions/settlement/pendingWrite.js +0 -20
- package/dist/transaction/transport/commitFrames.d.ts +0 -90
- package/dist/transaction/transport/commitFrames.js +0 -134
- package/dist/transaction/transport/connectionManager.d.ts +0 -215
- package/dist/transaction/transport/connectionManager.js +0 -673
- package/dist/transaction/transport/credentialLifecycle.d.ts +0 -177
- package/dist/transaction/transport/credentialLifecycle.js +0 -324
- package/dist/transaction/transport/heartbeat.d.ts +0 -65
- package/dist/transaction/transport/heartbeat.js +0 -93
- package/dist/transaction/transport/httpClient.d.ts +0 -131
- package/dist/transaction/transport/httpClient.js +0 -146
- package/dist/transaction/transport/httpOptions.d.ts +0 -33
- package/dist/transaction/transport/httpOptions.js +0 -12
- package/dist/transaction/transport/httpTransport.d.ts +0 -8
- package/dist/transaction/transport/httpTransport.js +0 -1388
- package/dist/transaction/transport/networkProbe.d.ts +0 -84
- package/dist/transaction/transport/networkProbe.js +0 -207
- package/dist/transaction/transport/wsFrameHandlers.d.ts +0 -128
- package/dist/transaction/transport/wsFrameHandlers.js +0 -429
- package/dist/transaction/transport/wsTransport.d.ts +0 -574
- package/dist/transaction/transport/wsTransport.js +0 -1023
- package/dist/transaction/types/assertExact.d.ts +0 -17
- package/dist/transaction/types/assertExact.js +0 -1
- package/dist/transaction/types/global.d.ts +0 -107
- package/dist/transaction/types/global.js +0 -40
- package/dist/transaction/types/index.d.ts +0 -205
- package/dist/transaction/types/index.js +0 -56
- package/dist/transaction/types/modelData.d.ts +0 -10
- package/dist/transaction/types/modelData.js +0 -9
- package/dist/transaction/types/participant.d.ts +0 -20
- package/dist/transaction/types/participant.js +0 -10
- package/dist/transaction/types/streams.d.ts +0 -550
- package/dist/transaction/types/streams.js +0 -11
- package/dist/transaction/utils/asyncIterator.d.ts +0 -34
- package/dist/transaction/utils/asyncIterator.js +0 -135
- package/dist/transaction/utils/duration.d.ts +0 -50
- package/dist/transaction/utils/duration.js +0 -77
- package/dist/transaction/utils/json.d.ts +0 -57
- package/dist/transaction/utils/json.js +0 -276
- package/dist/transaction/wire/accountResponses.d.ts +0 -420
- package/dist/transaction/wire/accountResponses.js +0 -290
- package/dist/transaction/wire/auth.d.ts +0 -56
- package/dist/transaction/wire/auth.js +0 -63
- package/dist/transaction/wire/bootstrapReason.d.ts +0 -9
- package/dist/transaction/wire/bootstrapReason.js +0 -8
- package/dist/transaction/wire/claimEvent.d.ts +0 -76
- package/dist/transaction/wire/claimEvent.js +0 -73
- package/dist/transaction/wire/claims.d.ts +0 -530
- package/dist/transaction/wire/claims.js +0 -327
- package/dist/transaction/wire/commit.d.ts +0 -603
- package/dist/transaction/wire/commit.js +0 -321
- package/dist/transaction/wire/delta.d.ts +0 -250
- package/dist/transaction/wire/delta.js +0 -147
- package/dist/transaction/wire/errorEnvelope.d.ts +0 -72
- package/dist/transaction/wire/errorEnvelope.js +0 -123
- package/dist/transaction/wire/feedCursor.d.ts +0 -60
- package/dist/transaction/wire/feedCursor.js +0 -82
- package/dist/transaction/wire/feedEvent.d.ts +0 -204
- package/dist/transaction/wire/feedEvent.js +0 -65
- package/dist/transaction/wire/frames.d.ts +0 -194
- package/dist/transaction/wire/frames.js +0 -50
- package/dist/transaction/wire/inboundFrames.d.ts +0 -562
- package/dist/transaction/wire/inboundFrames.js +0 -116
- package/dist/transaction/wire/index.d.ts +0 -54
- package/dist/transaction/wire/index.js +0 -83
- package/dist/transaction/wire/listEnvelope.d.ts +0 -37
- package/dist/transaction/wire/listEnvelope.js +0 -42
- package/dist/transaction/wire/modelMutations.d.ts +0 -31
- package/dist/transaction/wire/modelMutations.js +0 -52
- package/dist/transaction/wire/modelResponses.d.ts +0 -85
- package/dist/transaction/wire/modelResponses.js +0 -43
- package/dist/transaction/wire/modelShape.d.ts +0 -78
- package/dist/transaction/wire/modelShape.js +0 -74
- package/dist/transaction/wire/protocol.d.ts +0 -38
- package/dist/transaction/wire/protocol.js +0 -38
- package/dist/transaction/wire/protocolVersion.d.ts +0 -73
- package/dist/transaction/wire/protocolVersion.js +0 -83
- package/dist/transactions/mutations/MutationQueue.d.ts +0 -661
- package/dist/transactions/mutations/MutationQueue.js +0 -2807
- package/dist/transactions/mutations/MutationStore.d.ts +0 -20
- package/dist/transactions/mutations/MutationStore.js +0 -53
- package/dist/transactions/mutations/UnconfirmedWrites.d.ts +0 -82
- package/dist/transactions/mutations/UnconfirmedWrites.js +0 -104
- package/dist/transactions/mutations/coalesceRules.d.ts +0 -58
- package/dist/transactions/mutations/coalesceRules.js +0 -140
- package/dist/transactions/mutations/commitLatency.d.ts +0 -52
- package/dist/transactions/mutations/commitLatency.js +0 -130
- package/dist/transactions/mutations/commitOutboxStore.d.ts +0 -28
- package/dist/transactions/mutations/commitOutboxStore.js +0 -26
- package/dist/transactions/mutations/commitPayload.d.ts +0 -165
- package/dist/transactions/mutations/commitPayload.js +0 -152
- package/dist/transactions/mutations/deltaConfirmation.d.ts +0 -63
- package/dist/transactions/mutations/deltaConfirmation.js +0 -235
- package/dist/transactions/mutations/durableWriteStore.d.ts +0 -14
- package/dist/transactions/mutations/durableWriteStore.js +0 -12
- package/dist/transactions/mutations/optimisticApply.d.ts +0 -49
- package/dist/transactions/mutations/optimisticApply.js +0 -65
- package/dist/transactions/mutations/replayValidation.d.ts +0 -187
- package/dist/transactions/mutations/replayValidation.js +0 -164
- package/dist/utils/mobxSetup.d.ts +0 -53
- package/dist/utils/mobxSetup.js +0 -330
- package/dist/views/QueryView.d.ts +0 -79
- package/dist/views/QueryView.js +0 -218
- package/dist/views/ViewRegistry.d.ts +0 -20
- package/dist/views/ViewRegistry.js +0 -55
- package/dist/views/incrementalView.d.ts +0 -45
- package/dist/views/incrementalView.js +0 -69
- package/dist/webhooks/events.d.ts +0 -43
- package/dist/webhooks/events.js +0 -42
- package/dist/webhooks/index.d.ts +0 -8
- package/dist/webhooks/index.js +0 -8
- package/dist/wire/index.d.ts +0 -1
- package/dist/wire/index.js +0 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-next.d.ts","sourceRoot":"","sources":["../src/source-next.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-next.js","sourceRoot":"","sources":["../src/source-next.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC"}
|
package/dist/source.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC"}
|
package/dist/source.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC"}
|
package/dist/wire.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
|
package/dist/wire.js
ADDED
package/dist/wire.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.js","sourceRoot":"","sources":["../src/wire.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
|
package/docs/agents.md
CHANGED
|
@@ -34,7 +34,7 @@ const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, transport: "http"
|
|
|
34
34
|
// `retrieve` resolves to the row, or `undefined` when none matches.
|
|
35
35
|
const open = await ablo.tasks.list({ where: { status: "todo" } });
|
|
36
36
|
|
|
37
|
-
const task = await ablo.tasks.
|
|
37
|
+
const task = await ablo.tasks.get({ id: open[0].id });
|
|
38
38
|
if (!task) throw new Error("task not found");
|
|
39
39
|
|
|
40
40
|
console.log(task.title);
|
package/docs/api-keys.md
CHANGED
|
@@ -33,8 +33,8 @@ That's the whole story: one knob, filled by audience.
|
|
|
33
33
|
|---|---|---|
|
|
34
34
|
| publishable `pk_` (client-safe) | `pk_` | browser: read-only |
|
|
35
35
|
| secret `sk_` (server, full) | `sk_` | server: full authority |
|
|
36
|
-
| restricted `rk_` (granular) | `rk_` | scoped
|
|
37
|
-
| ephemeral key (client, customer-scoped) | `ek_` | per-user browser sessions (`sessions.create({ user })`) |
|
|
36
|
+
| restricted `rk_` (granular) | `rk_` | scoped agents (`agents.create({ can })`) |
|
|
37
|
+
| ephemeral key (client, customer-scoped) | `ek_` | per-user browser sessions (`sessions.create({ user, can })`) |
|
|
38
38
|
|
|
39
39
|
Mode lives in the prefix too — `sk_test_` / `sk_live_` — exactly like Stripe. The
|
|
40
40
|
`apiKey` resolver fetching an `ek_` is Ablo's ephemeral-key flow: server mints, client holds.
|
|
@@ -42,7 +42,7 @@ Mode lives in the prefix too — `sk_test_` / `sk_live_` — exactly like Stripe
|
|
|
42
42
|
**Why a function for browser writes?** Anything you ship to a browser must be public, and a
|
|
43
43
|
public `pk_` is **read-only** — it can't carry one specific user's write authority. So when
|
|
44
44
|
the browser writes *as the logged-in user*, your backend (which holds the secret `sk_` and
|
|
45
|
-
knows who's signed in) mints a short-lived per-user token with `sessions.create({ user })`,
|
|
45
|
+
knows who's signed in) mints a short-lived per-user token with `sessions.create({ user, can })`,
|
|
46
46
|
and the browser's `apiKey` function fetches it. You don't manage refresh — the SDK calls the
|
|
47
47
|
function once before connecting and then keeps the token fresh (re-mint before expiry, and on
|
|
48
48
|
tab-focus / network-online / device-wake). This is the Stripe ephemeral-key / Supabase
|
|
@@ -58,10 +58,11 @@ knobs, and you set exactly one.
|
|
|
58
58
|
|
|
59
59
|
| Mint | Call | Result |
|
|
60
60
|
|---|---|---|
|
|
61
|
-
| Human end-user session | `await server.sessions.create({ user: { id } })` | `ek_` (
|
|
62
|
-
|
|
|
61
|
+
| Human end-user session | `await server.sessions.create({ user: { id }, can: { tasks: ['read'] } })` | `ek_` (scoped to `can`) |
|
|
62
|
+
| Ready agent client | `await server.agents.create({ can: { tasks: ['update'] } })` | Auto-refreshing client scoped to `can` |
|
|
63
|
+
| Raw delegated agent token | `await server.sessions.create({ agent: { id }, can: { tasks: ['update'] } })` | `rk_` for another runtime |
|
|
63
64
|
|
|
64
|
-
The principal kind comes from *which* shape you pass — `{ user }` → `user`, `{ agent, can }` → `agent`.
|
|
65
|
+
The principal kind comes from *which* shape you pass — `{ user, can }` → `user`, `{ agent, can }` → `agent`.
|
|
65
66
|
|
|
66
67
|
## Server-Side API Keys
|
|
67
68
|
|
package/docs/api.md
CHANGED
|
@@ -13,10 +13,10 @@ row, you can optionally `claim` it so they serialize instead of clobbering
|
|
|
13
13
|
each other.
|
|
14
14
|
|
|
15
15
|
Two things to know before the method list. **Reads come in two flavors:**
|
|
16
|
-
`
|
|
16
|
+
`get({ id })` / `list({ where })` are async — they answer from what is
|
|
17
17
|
already local and fall back to the server. Put `local.` in front of either and
|
|
18
18
|
you get the same read restricted to what is already here, which is why it can
|
|
19
|
-
return a value rather than a promise: `local.
|
|
19
|
+
return a value rather than a promise: `local.get(id)`, `local.list({ where })`,
|
|
20
20
|
`local.count({ where })`. Use those in render, after data has synced.
|
|
21
21
|
**Claims don't lock.** If another writer holds the row, `claim` waits
|
|
22
22
|
for them, re-reads the fresh row, then hands it to you — so two writers
|
|
@@ -38,7 +38,7 @@ const schema = defineSchema({
|
|
|
38
38
|
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
39
39
|
|
|
40
40
|
await ablo.ready();
|
|
41
|
-
const report = await ablo.weatherReports.
|
|
41
|
+
const report = await ablo.weatherReports.get({ id: 'report_stockholm' });
|
|
42
42
|
if (!report) throw new Error('Row not found');
|
|
43
43
|
|
|
44
44
|
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' }, wait: 'confirmed' });
|
|
@@ -51,24 +51,24 @@ agents, read the [Integration Guide](./integration-guide.md).
|
|
|
51
51
|
|
|
52
52
|
Each schema model becomes a typed model on the client:
|
|
53
53
|
|
|
54
|
-
- `ablo.weatherReports.
|
|
54
|
+
- `ablo.weatherReports.get({ id })` reads one row asynchronously (server read).
|
|
55
55
|
- `ablo.weatherReports.list({ where })` reads a collection asynchronously (server read).
|
|
56
|
-
- `ablo.weatherReports.local.
|
|
56
|
+
- `ablo.weatherReports.local.get(id)` reads one row synchronously from the local graph.
|
|
57
57
|
- `ablo.weatherReports.create({ data })` creates a row.
|
|
58
58
|
- `ablo.weatherReports.update({ id, data, ...options })` updates a row.
|
|
59
59
|
- `ablo.weatherReports.delete({ id, ...options })` deletes a row.
|
|
60
60
|
|
|
61
|
-
`local.` narrows a read to what has already synced. `
|
|
61
|
+
`local.` narrows a read to what has already synced. `get({ id })` and
|
|
62
62
|
`list({ where })` answer from the local graph and fall back to IndexedDB and
|
|
63
63
|
then the network, so reach for them when the row may not be here yet.
|
|
64
|
-
`local.
|
|
64
|
+
`local.get(id)` and `local.list({ where })` are the same reads with the
|
|
65
65
|
fallback removed — nothing to await, so they return a value.
|
|
66
66
|
|
|
67
67
|
| Method | Returns | Use when |
|
|
68
68
|
|---|---|---|
|
|
69
|
-
| `
|
|
69
|
+
| `get({ id })` | `Promise<T \| undefined>` | You need one row, hydrating from local store and server. |
|
|
70
70
|
| `list({ where })` | `Promise<T[]>` | You need to hydrate a collection from local store and server. |
|
|
71
|
-
| `local.
|
|
71
|
+
| `local.get(id)` | `T \| undefined` | You want a synchronous snapshot of one local row. |
|
|
72
72
|
| `local.list(options?)` | `T[]` | You want a synchronous snapshot of a local collection. |
|
|
73
73
|
| `local.count(options?)` | `number` | You want a synchronous count of local rows. |
|
|
74
74
|
| `create({ data, ...options })` | `Promise<T>` | You want to create through the schema model. |
|
|
@@ -179,7 +179,7 @@ endpoint documents that model's real field contract instead of a generic blob.
|
|
|
179
179
|
|---|---|
|
|
180
180
|
| `ablo.<model>.create({ data })` | `POST /api/v1/models/{model}` |
|
|
181
181
|
| `ablo.<model>.list({ where })` | `GET /api/v1/models/{model}` |
|
|
182
|
-
| `ablo.<model>.
|
|
182
|
+
| `ablo.<model>.get({ id })` | `GET /api/v1/models/{model}/{id}` |
|
|
183
183
|
| `ablo.<model>.update({ id, data })` | `PATCH /api/v1/models/{model}/{id}` |
|
|
184
184
|
| `ablo.<model>.delete({ id })` | `DELETE /api/v1/models/{model}/{id}` |
|
|
185
185
|
| `ablo.<model>.claim({ id })` | `POST /api/v1/models/{model}/{id}/claim` |
|
package/docs/client-behavior.md
CHANGED
|
@@ -53,8 +53,8 @@ Each schema model becomes a typed model:
|
|
|
53
53
|
```ts
|
|
54
54
|
await ablo.ready();
|
|
55
55
|
|
|
56
|
-
const report = await ablo.weatherReports.
|
|
57
|
-
const local = ablo.weatherReports.local.
|
|
56
|
+
const report = await ablo.weatherReports.get({ id: 'report_stockholm' });
|
|
57
|
+
const local = ablo.weatherReports.local.get('report_stockholm');
|
|
58
58
|
|
|
59
59
|
await ablo.weatherReports.create({ data: { location: 'Stockholm', status: 'pending' } });
|
|
60
60
|
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' }, wait: 'confirmed' });
|
|
@@ -62,7 +62,7 @@ await ablo.weatherReports.delete({ id: 'report_stockholm', wait: 'confirmed' });
|
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
Call `retrieve`/`list` first — they fetch from the server and you `await` them.
|
|
65
|
-
After that, `local.
|
|
65
|
+
After that, `local.get`/`local.list`/`local.count` read the already-synced data instantly with
|
|
66
66
|
no `await`, and stay reactive in render. Use the async pair to load, the sync trio
|
|
67
67
|
to read.
|
|
68
68
|
|
|
@@ -79,7 +79,7 @@ through the same model client path. A human Server Action, a browser view, and a
|
|
|
79
79
|
agent worker can all use `ablo.weatherReports`:
|
|
80
80
|
|
|
81
81
|
```ts
|
|
82
|
-
const report = await ablo.weatherReports.
|
|
82
|
+
const report = await ablo.weatherReports.get({ id });
|
|
83
83
|
const snap = ablo.snapshot({ weatherReports: id });
|
|
84
84
|
|
|
85
85
|
await ablo.weatherReports.update({
|
|
@@ -93,7 +93,7 @@ await ablo.weatherReports.update({
|
|
|
93
93
|
|
|
94
94
|
Once the server accepts the write, every other connected client gets the new row
|
|
95
95
|
automatically — no polling or manual refresh on your side. React clients that use
|
|
96
|
-
`useAblo((ablo) => ablo.weatherReports.local.
|
|
96
|
+
`useAblo((ablo) => ablo.weatherReports.local.get(id))` receive the new row, and selectors
|
|
97
97
|
such as `useAblo((ablo) => ablo.weatherReports.claim.state({ id }))`
|
|
98
98
|
receive active claim state. There is
|
|
99
99
|
no extra multiplayer setup beyond routing shared state through Ablo.
|
package/docs/coordination.md
CHANGED
|
@@ -38,11 +38,10 @@ Reads stay open: reading a claimed row is allowed unless the caller explicitly
|
|
|
38
38
|
asks for claimed gating. A claim carries a TTL so a crashed holder is
|
|
39
39
|
auto-released and the queue advances.
|
|
40
40
|
|
|
41
|
-
A claim is also as narrow as you make it.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
[claiming part of a row](#claiming-part-of-a-row).
|
|
41
|
+
A claim is also as narrow as you make it. Select one or more `fields` and
|
|
42
|
+
exclusion follows the target: **two claims on different fields of the same row
|
|
43
|
+
are both granted**, and only claims that share a field queue behind each other.
|
|
44
|
+
See [claiming part of a row](#claiming-part-of-a-row).
|
|
46
45
|
|
|
47
46
|
> **Transport: both wait — only the mechanism differs.** `claim({ id })` means
|
|
48
47
|
> "serialize me behind whoever holds this row" on every transport. The
|
|
@@ -61,7 +60,7 @@ only overlapping targets queue behind each other. See
|
|
|
61
60
|
> try-claim, which resolves `null` when the target is held (a declined try
|
|
62
61
|
> is not an error) and takes no place in line. For
|
|
63
62
|
> callers that manage the wait themselves, the ticket surface remains:
|
|
64
|
-
> `ablo.claims.
|
|
63
|
+
> `ablo.claims.get({ claimId })` polls a ticket to its grant,
|
|
65
64
|
> `ablo.claims.heartbeat({ claimId })` keeps the slot, and
|
|
66
65
|
> `ablo.claims.release({ claimId })` leaves the line. And contention can be
|
|
67
66
|
> treated as a signal rather than a wait at all — catch the error, re-read
|
|
@@ -221,7 +220,7 @@ a model row. It's what `claim.state()` returns and what observers render.
|
|
|
221
220
|
|---|---|---|
|
|
222
221
|
| `id` | `string` | The claim id (distinct from the target row id). |
|
|
223
222
|
| `status` | `ClaimStatus` | `'active' \| 'queued' \| 'committed' \| 'expired' \| 'canceled'`. `active` = the holder; `queued` = waiting in line behind it. The other three are terminal states you only see on a claim you just finished: `committed` (released after a successful write), `expired` (TTL lapsed), `canceled` (released early). |
|
|
224
|
-
| `target` | `EntityRef` | What is being coordinated: the row (`{ model, id }`) plus any
|
|
223
|
+
| `target` | `EntityRef` | What is being coordinated: the row (`{ model, id }`) plus any field narrowing the holder claimed: `field?`, `fields?`, and opaque `meta?`. A target with no narrowing covers the whole row. |
|
|
225
224
|
| `description` | `string` | Peer-visible description of the work: the sentence another participant reads to decide whether to wait or move on (`'rewriting the risk section'`). Defaults to `'editing'`. |
|
|
226
225
|
| `heldBy` | `string` | Participant holding (or waiting on) it (e.g. `'agent:forecaster'`). |
|
|
227
226
|
| `participantKind` | `'user' \| 'agent' \| 'system'` | Who's behind it: a human (`user`), an AI (`agent`), or automated infrastructure (`system`). |
|
|
@@ -309,10 +308,7 @@ four axes. `claim({ id })` alone is a complete call; each axis is opt-in.
|
|
|
309
308
|
| name | type | required | description |
|
|
310
309
|
|---|---|---|---|
|
|
311
310
|
| `id` | `string` | yes | The row id: same id as `retrieve` / `update`. |
|
|
312
|
-
| `options.
|
|
313
|
-
| `options.fields` | `ClaimField[]` | no | Claim named parts of the row instead of all of it: one or several. Prefer the selector form, `fields: (f) => [f.status]`, where the model hands you its own fields, so a field it does not have stops compiling and a rename is a compile error at every use. A bare name (`'status'`) autocompletes but is not checked; an app-defined part, a cell, a section, is named with `part('B2')`. Two sets conflict where they intersect, so holders of disjoint parts do not wait for each other, see [claiming part of a row](#claiming-part-of-a-row). A name containing a comma is refused: two names in one string compare as a single unrelated name, and both writers would be granted the same part. |
|
|
314
|
-
| `options.path` | `string` | no | A hierarchical position in a document-shaped row: `'/content/3'` claims one block. Paths conflict on containment: `/content` covers `/content/3`, while `/content/3` and `/content/7` are disjoint and both granted. |
|
|
315
|
-
| `options.range` | `TargetRange` | no | A span of the row's text: `{ startLine, endLine, startColumn?, endColumn? }`. Ranges conflict only where their line intervals overlap. An editor addressing by integer position maps it onto `startLine`/`endLine`: the test is plain interval intersection. |
|
|
311
|
+
| `options.fields` | field selector | no | Claim fields declared by the model's Zod schema instead of the whole row: `fields: (task) => task.status`, or `fields: (task) => [task.status, task.title]` for several. The model supplies its own fields, so autocomplete is exact, a typo does not compile, and a schema rename is a compile error at every use. Two sets conflict where they intersect, so holders of disjoint fields do not wait for each other; see [claiming part of a row](#claiming-part-of-a-row). |
|
|
316
312
|
|
|
317
313
|
*What others see* — the presence half:
|
|
318
314
|
|
|
@@ -373,67 +369,61 @@ Name a field and a typo cannot survive — the model is already bound by the cal
|
|
|
373
369
|
so it hands you its own fields:
|
|
374
370
|
|
|
375
371
|
```ts
|
|
376
|
-
await using mine = await ablo.tasks.claim({ id, fields: (
|
|
372
|
+
await using mine = await ablo.tasks.claim({ id, fields: (task) => task.status });
|
|
377
373
|
```
|
|
378
374
|
|
|
379
|
-
`
|
|
375
|
+
`task.status` is checked against the model: a field it does not have stops
|
|
380
376
|
compiling, and renaming one is a compile error at every use. Nothing to import,
|
|
381
377
|
nothing to add to your schema file.
|
|
382
378
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
`title` unguarded.
|
|
379
|
+
The selector is the public model API. Quoted field names exist only in the wire
|
|
380
|
+
contract and low-level coordination protocol.
|
|
386
381
|
|
|
387
|
-
A claim covers the whole row only when you name nothing narrower.
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
each other.
|
|
382
|
+
A claim covers the whole row only when you name nothing narrower. Select
|
|
383
|
+
`fields` and exclusion follows them: **two claims on different
|
|
384
|
+
fields of the same row are both granted**, and only claims that share a field
|
|
385
|
+
queue behind each other.
|
|
392
386
|
|
|
393
387
|
```ts
|
|
394
|
-
//
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
388
|
+
// Two agents on the SAME order at the same time. The pricing agent holds
|
|
389
|
+
// `total` and `discount`; the fulfillment agent holds `status`. Disjoint
|
|
390
|
+
// fields, so both are granted at once and neither waits on the other.
|
|
391
|
+
await using priced = await ablo.orders.claim({
|
|
392
|
+
id: orderId,
|
|
393
|
+
fields: (o) => [o.total, o.discount],
|
|
394
|
+
description: 'repricing',
|
|
399
395
|
});
|
|
400
396
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
path: '/content/7',
|
|
406
|
-
description: 'tightening the summary',
|
|
397
|
+
await using shipping = await ablo.orders.claim({
|
|
398
|
+
id: orderId,
|
|
399
|
+
fields: (order) => order.status,
|
|
400
|
+
description: 'marking shipped',
|
|
407
401
|
});
|
|
408
402
|
```
|
|
409
403
|
|
|
410
|
-
Overlap is
|
|
404
|
+
Overlap is set intersection. Holders on `total` and on `status` proceed
|
|
405
|
+
concurrently; two claims that both name `total` queue; naming no field covers
|
|
406
|
+
every field, so it conflicts with any narrower claim on the row.
|
|
411
407
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
that addresses text by integer position rather than by line maps that
|
|
420
|
-
position axis straight onto `startLine`/`endLine`: the server's test is
|
|
421
|
-
plain interval intersection and never assumes the units are lines of a
|
|
422
|
-
file.
|
|
408
|
+
**Field is the floor.** A claim cannot be finer than a whole field, because
|
|
409
|
+
nothing writes part of a value: whichever holder commits first takes the entire
|
|
410
|
+
field. Sub-field targets, a range of text or a path into a document, are not
|
|
411
|
+
offered. Two writers holding different parts of one field is only safe once
|
|
412
|
+
concurrent edits to that field can be reconciled (operational transformation),
|
|
413
|
+
and that is not solved yet. When it is, sub-field targets return, and they
|
|
414
|
+
return working.
|
|
423
415
|
|
|
424
|
-
A claim with no target is the widest parent: it covers every
|
|
425
|
-
and conflicts with any narrower claim on it.
|
|
426
|
-
document row is therefore one claim per region — the row stays one row, and
|
|
427
|
-
the claims carve it up.
|
|
416
|
+
A claim with no target is the widest parent: it covers every field of the row
|
|
417
|
+
and conflicts with any narrower claim on it.
|
|
428
418
|
|
|
429
419
|
### Claim-gated reads
|
|
430
420
|
|
|
431
421
|
`claim.state({ id })` always returns immediately. Model reads such as
|
|
432
|
-
`ablo.<model>.local.
|
|
422
|
+
`ablo.<model>.local.get(id)` are local reads and stay available while a claim is
|
|
433
423
|
held. Server/model reads can choose a claimed policy:
|
|
434
424
|
|
|
435
425
|
```ts
|
|
436
|
-
await ablo.weatherReports.
|
|
426
|
+
await ablo.weatherReports.get({
|
|
437
427
|
id: 'report_stockholm',
|
|
438
428
|
ifClaimed: 'fail',
|
|
439
429
|
});
|
|
@@ -479,9 +469,9 @@ whole coordination API.
|
|
|
479
469
|
**One holder, and a row can have several.** This reads a row, not a target, and
|
|
480
470
|
answers with a single claim. That is the whole story for a whole-row claim, and
|
|
481
471
|
only part of it once you [claim parts of a row](#claiming-part-of-a-row): three
|
|
482
|
-
agents holding
|
|
483
|
-
|
|
484
|
-
|
|
472
|
+
agents holding `total`, `discount`, and `status` are all active at once, and
|
|
473
|
+
this read surfaces one of them. To render every holder, a badge per claimed
|
|
474
|
+
field or a chip per participant, use [`claim.list`](#claimlist).
|
|
485
475
|
|
|
486
476
|
**Example**
|
|
487
477
|
|
|
@@ -514,9 +504,9 @@ Every holder of a row. Same synchronous, reactive read as `claim.state` — off
|
|
|
514
504
|
the same local snapshot, safe to call inline in a render — and the same list
|
|
515
505
|
envelope as [`claim.queue`](#claimqueue).
|
|
516
506
|
|
|
517
|
-
Reach for it whenever a row can be claimed
|
|
518
|
-
|
|
519
|
-
|
|
507
|
+
Reach for it whenever a row can be claimed by field. One agent repricing
|
|
508
|
+
`total` while another marks `status` are two active claims on one row, and only
|
|
509
|
+
this read returns both.
|
|
520
510
|
|
|
521
511
|
**Parameters**
|
|
522
512
|
|
|
@@ -528,14 +518,14 @@ row, and only this read returns both.
|
|
|
528
518
|
when this client holds one, then the other participants'. Empty `data` when the
|
|
529
519
|
row is free.
|
|
530
520
|
|
|
531
|
-
**Example** — a
|
|
521
|
+
**Example** — a badge on every claimed field:
|
|
532
522
|
|
|
533
523
|
```tsx
|
|
534
|
-
const { data: holders } = ablo.
|
|
524
|
+
const { data: holders } = ablo.orders.claim.list({ id: orderId });
|
|
535
525
|
|
|
536
|
-
return
|
|
537
|
-
const held = holders.find((c) => c.target.
|
|
538
|
-
return <
|
|
526
|
+
return FIELDS.map((field) => {
|
|
527
|
+
const held = holders.find((c) => c.target.field === field);
|
|
528
|
+
return <FieldBadge key={field} field={field} by={held?.heldBy} note={held?.description} />;
|
|
539
529
|
});
|
|
540
530
|
```
|
|
541
531
|
|
|
@@ -791,7 +781,7 @@ try {
|
|
|
791
781
|
|
|
792
782
|
## Errors
|
|
793
783
|
|
|
794
|
-
All extend `AbloError` (`packages/
|
|
784
|
+
All extend `AbloError` (`packages/transaction/src/errors.ts`). Catch by `type` or
|
|
795
785
|
inspect the `code`.
|
|
796
786
|
|
|
797
787
|
| error | `code` | thrown when | carries |
|
package/docs/data-sources.md
CHANGED
|
@@ -182,7 +182,7 @@ await ablo.weatherReports.update({ id: 'report_stockholm', data: { high: 21 } })
|
|
|
182
182
|
await ablo.weatherReports.update({ id: 'report_stockholm', data: { high: 21 }, wait: 'confirmed' });
|
|
183
183
|
|
|
184
184
|
// Reads are live off the same stream.
|
|
185
|
-
const report = ablo.weatherReports.local.
|
|
185
|
+
const report = ablo.weatherReports.local.get('report_stockholm');
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
A commit is accepted the moment Ablo takes it (`queued`); it becomes `confirmed`
|
|
@@ -25,7 +25,7 @@ a typed error if the row moved underneath you while the agent was busy.
|
|
|
25
25
|
## Schema-Backed Worker
|
|
26
26
|
|
|
27
27
|
The worker uses the same schema client the app uses. It reads the task from the
|
|
28
|
-
server with `
|
|
28
|
+
server with `get({ id })`, claims the row, and writes through
|
|
29
29
|
`ablo.tasks.update(...)` with a stale-check so a concurrent edit can't be
|
|
30
30
|
overwritten.
|
|
31
31
|
|
|
@@ -49,8 +49,8 @@ const ablo = Ablo({
|
|
|
49
49
|
export async function markDone(taskId: string) {
|
|
50
50
|
await ablo.ready();
|
|
51
51
|
|
|
52
|
-
//
|
|
53
|
-
const task = await ablo.tasks.
|
|
52
|
+
// get({ id }) is an async server read — await it.
|
|
53
|
+
const task = await ablo.tasks.get({ id: taskId });
|
|
54
54
|
if (!task) return { status: 'not_found' };
|
|
55
55
|
|
|
56
56
|
try {
|
|
@@ -109,7 +109,7 @@ Keep workers on the same schema-backed client as the app.
|
|
|
109
109
|
import { useAblo } from '@abloatai/ablo/react';
|
|
110
110
|
|
|
111
111
|
export function TaskRow({ task: serverTask }: Props) {
|
|
112
|
-
const data = useAblo((ablo) => ablo.tasks.local.
|
|
112
|
+
const data = useAblo((ablo) => ablo.tasks.local.get(serverTask.id)) ?? serverTask;
|
|
113
113
|
const holder = useAblo((ablo) => ablo.tasks.claim.state({ id: serverTask.id }));
|
|
114
114
|
const agentActive = holder?.participantKind === 'agent';
|
|
115
115
|
|
|
@@ -54,7 +54,7 @@ const updateTask = tool({
|
|
|
54
54
|
await ablo.ready();
|
|
55
55
|
|
|
56
56
|
// retrieve hits the server for the latest row (async — await it).
|
|
57
|
-
const task = await ablo.tasks.
|
|
57
|
+
const task = await ablo.tasks.get({ id: taskId });
|
|
58
58
|
if (!task) return { ok: false, reason: 'not_found' };
|
|
59
59
|
|
|
60
60
|
// If another agent already holds this row, claim waits for them to finish,
|
|
@@ -87,13 +87,24 @@ browser only ever sees the short-lived token:
|
|
|
87
87
|
```ts
|
|
88
88
|
// web/app/api/ablo-session/route.ts
|
|
89
89
|
import { ablo } from '@/ablo';
|
|
90
|
+
import { credentialEndpointSuccessSchema } from '@abloatai/ablo/auth';
|
|
90
91
|
|
|
91
92
|
export const runtime = 'nodejs';
|
|
92
93
|
|
|
93
94
|
export async function POST() {
|
|
94
95
|
const userId = await currentUserId(); // your auth
|
|
95
|
-
const { token } = await ablo.sessions.create({
|
|
96
|
-
|
|
96
|
+
const { token, expiresAt } = await ablo.sessions.create({
|
|
97
|
+
user: { id: userId },
|
|
98
|
+
can: { tasks: ['read', 'update'] },
|
|
99
|
+
});
|
|
100
|
+
return Response.json(
|
|
101
|
+
credentialEndpointSuccessSchema.parse({
|
|
102
|
+
token,
|
|
103
|
+
expiresAt,
|
|
104
|
+
credentialKind: 'ephemeral',
|
|
105
|
+
}),
|
|
106
|
+
{ headers: { 'Cache-Control': 'no-store' } },
|
|
107
|
+
);
|
|
97
108
|
}
|
|
98
109
|
```
|
|
99
110
|
|
|
@@ -112,7 +123,7 @@ export function ReportRow({
|
|
|
112
123
|
}: {
|
|
113
124
|
report: { id: string; location: string; status: string };
|
|
114
125
|
}) {
|
|
115
|
-
const report = useAblo((ablo) => ablo.weatherReports.local.
|
|
126
|
+
const report = useAblo((ablo) => ablo.weatherReports.local.get(serverReport.id)) ?? serverReport;
|
|
116
127
|
const active = useAblo((ablo) => ablo.weatherReports.claim.state({ id: serverReport.id }));
|
|
117
128
|
const claimed = Boolean(active);
|
|
118
129
|
|
|
@@ -275,7 +286,7 @@ and timestamp. If the change originated from an Ablo commit, include the same
|
|
|
275
286
|
Agents use the same model API as the UI:
|
|
276
287
|
|
|
277
288
|
```ts
|
|
278
|
-
const report = await ablo.weatherReports.
|
|
289
|
+
const report = await ablo.weatherReports.get({ id: reportId });
|
|
279
290
|
const snap = ablo.snapshot({ weatherReports: reportId });
|
|
280
291
|
|
|
281
292
|
await ablo.weatherReports.update({
|
package/docs/examples/nextjs.md
CHANGED
|
@@ -64,13 +64,34 @@ The browser can't hold `sk_`, so a backend route mints a scoped, short-lived
|
|
|
64
64
|
// app/api/ablo-session/route.ts
|
|
65
65
|
import { ablo } from '@/lib/ablo';
|
|
66
66
|
import { getCurrentUser } from '@/auth';
|
|
67
|
+
import {
|
|
68
|
+
credentialEndpointErrorSchema,
|
|
69
|
+
credentialEndpointSuccessSchema,
|
|
70
|
+
} from '@abloatai/ablo/auth';
|
|
67
71
|
|
|
68
72
|
export async function POST() {
|
|
69
73
|
const user = await getCurrentUser();
|
|
70
|
-
if (!user)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
if (!user) {
|
|
75
|
+
return Response.json(
|
|
76
|
+
credentialEndpointErrorSchema.parse({
|
|
77
|
+
error: { code: 'session_expired' },
|
|
78
|
+
}),
|
|
79
|
+
{ status: 401, headers: { 'Cache-Control': 'no-store' } },
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const { token, expiresAt } = await ablo.sessions.create({
|
|
84
|
+
user: { id: user.id },
|
|
85
|
+
can: { tasks: ['read', 'create', 'update'] },
|
|
86
|
+
});
|
|
87
|
+
return Response.json(
|
|
88
|
+
credentialEndpointSuccessSchema.parse({
|
|
89
|
+
token,
|
|
90
|
+
expiresAt,
|
|
91
|
+
credentialKind: 'ephemeral',
|
|
92
|
+
}),
|
|
93
|
+
{ headers: { 'Cache-Control': 'no-store' } },
|
|
94
|
+
);
|
|
74
95
|
}
|
|
75
96
|
```
|
|
76
97
|
|
|
@@ -124,7 +145,7 @@ export default async function TaskPage({
|
|
|
124
145
|
}: { params: Promise<{ id: string }> }) {
|
|
125
146
|
const { id } = await params;
|
|
126
147
|
await ablo.ready();
|
|
127
|
-
const task = await ablo.tasks.
|
|
148
|
+
const task = await ablo.tasks.get({ id });
|
|
128
149
|
if (!task) return null;
|
|
129
150
|
|
|
130
151
|
return <TaskEditor task={task} />;
|
|
@@ -167,7 +188,7 @@ you — re-fetch and retry.
|
|
|
167
188
|
import { useAblo } from '@abloatai/ablo/react';
|
|
168
189
|
|
|
169
190
|
export function TaskEditor({ task: serverTask }: Props) {
|
|
170
|
-
const data = useAblo((ablo) => ablo.tasks.local.
|
|
191
|
+
const data = useAblo((ablo) => ablo.tasks.local.get(serverTask.id)) ?? serverTask;
|
|
171
192
|
const holder = useAblo((ablo) => ablo.tasks.claim.state({ id: serverTask.id }));
|
|
172
193
|
const busy = Boolean(holder);
|
|
173
194
|
|
|
@@ -38,7 +38,7 @@ const ablo = Ablo({
|
|
|
38
38
|
export async function completeTask(taskId: string) {
|
|
39
39
|
await ablo.ready();
|
|
40
40
|
|
|
41
|
-
const task = await ablo.tasks.
|
|
41
|
+
const task = await ablo.tasks.get({ id: taskId });
|
|
42
42
|
if (!task) return { status: 'not_found' };
|
|
43
43
|
|
|
44
44
|
await using claim = await ablo.tasks.claim({
|
|
@@ -58,7 +58,7 @@ export async function completeTask(taskId: string) {
|
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
`
|
|
61
|
+
`get({ id })` is an async server read — it hits the server and returns the
|
|
62
62
|
row (or `undefined`, which the early `not_found` guard handles). The update runs
|
|
63
63
|
while the claim is held, and `wait: 'confirmed'` makes it resolve only once your
|
|
64
64
|
database has confirmed the row landed.
|
package/docs/how-it-works.md
CHANGED
|
@@ -10,7 +10,7 @@ whole model — everything below explains what it means and how to use it.
|
|
|
10
10
|
await ablo.tasks.update({ id: 'task_42', data: { status: 'done' }, wait: 'confirmed' });
|
|
11
11
|
|
|
12
12
|
// Reads come back live, kept current from your database.
|
|
13
|
-
const task = ablo.tasks.local.
|
|
13
|
+
const task = ablo.tasks.local.get('task_42');
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## The mental model: read this once
|
|
@@ -42,7 +42,7 @@ echo confirms it → everyone connected sees it live.**
|
|
|
42
42
|
| Primitive | Plane | Purpose |
|
|
43
43
|
|---|---|---|
|
|
44
44
|
| `Schema` | State | Declares typed models the app and agents can read and write. |
|
|
45
|
-
| `Model` | State | The generated `ablo.<model>` model. Use `retrieve`/`list` (async reads), `local.
|
|
45
|
+
| `Model` | State | The generated `ablo.<model>` model. Use `retrieve`/`list` (async reads), `local.get`/`local.list`/`local.count` (the same verbs, synchronous and local-only), `create`, `update`, and `delete`. |
|
|
46
46
|
| `Claim` | Coordination | Who is working on a target. Taken via `ablo.<model>.claim({ id })` and read via `ablo.<model>.claim.state({ id })`. Ephemeral, never persisted. |
|
|
47
47
|
| `Commit` | Protocol | The durable write underneath model updates. Most users do not call it directly. |
|
|
48
48
|
| `Receipt` | Protocol | The lower-level durable result for custom runtimes. Schema writes use `wait: 'confirmed'`. |
|
|
@@ -106,13 +106,13 @@ export const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
|
106
106
|
await ablo.tasks.update({ id: 'task_42', data: { status: 'done' }, wait: 'confirmed' });
|
|
107
107
|
|
|
108
108
|
// 6. Read — live, no fetch loop.
|
|
109
|
-
const task = ablo.tasks.local.
|
|
109
|
+
const task = ablo.tasks.local.get('task_42');
|
|
110
110
|
|
|
111
111
|
// 7. Coordinate when more than one actor can touch a row. Hold a claim and Ablo
|
|
112
112
|
// serializes writes on that key against everyone else; read after claiming,
|
|
113
113
|
// then write. The lease releases automatically at the end of the scope.
|
|
114
114
|
await using _hold = await ablo.tasks.claim('task_42');
|
|
115
|
-
const latest = ablo.tasks.local.
|
|
115
|
+
const latest = ablo.tasks.local.get('task_42'); // read after claiming, not from memory
|
|
116
116
|
await ablo.tasks.update({ id: 'task_42', data: { status: 'done' } });
|
|
117
117
|
```
|
|
118
118
|
|
package/docs/identity.md
CHANGED
|
@@ -180,6 +180,7 @@ await mintUserSessionKey({
|
|
|
180
180
|
organizationId: schemaOwnerOrgId,
|
|
181
181
|
projectId: schemaProjectId,
|
|
182
182
|
},
|
|
183
|
+
operations: ['task.read', 'task.update'],
|
|
183
184
|
ttlSeconds: 3600,
|
|
184
185
|
});
|
|
185
186
|
```
|
|
@@ -308,7 +309,7 @@ exposed the whole table cross-tenant, so `validate_schema` rejects the removed
|
|
|
308
309
|
options as `tenancy-option-removed` errors and steers you to `policy: { by:
|
|
309
310
|
'parent' }` (FK inheritance) or, for genuinely global reference data, the
|
|
310
311
|
explicit `policy: { by: 'none' }`. See
|
|
311
|
-
`packages/
|
|
312
|
+
`packages/transaction/src/schema/model.ts` for the full option set.
|
|
312
313
|
|
|
313
314
|
## How identity reaches Ablo: the proxy model
|
|
314
315
|
|