@abloatai/ablo 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +71 -2013
- package/NOTICE +2 -2
- package/README.md +25 -71
- 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
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schema Definition + Type Inference
|
|
3
|
-
*
|
|
4
|
-
* defineSchema() wraps your models. Types are inferred via Zod — no custom type system.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* import { z } from 'zod';
|
|
8
|
-
* import { defineSchema, model, relation } from '@abloatai/ablo/schema';
|
|
9
|
-
*
|
|
10
|
-
* const schema = defineSchema({
|
|
11
|
-
* tasks: model({
|
|
12
|
-
* title: z.string(),
|
|
13
|
-
* status: z.enum(['todo', 'doing', 'done']).default('todo'),
|
|
14
|
-
* projectId: z.string().optional(),
|
|
15
|
-
* }, {
|
|
16
|
-
* project: relation.belongsTo('projects', 'projectId'),
|
|
17
|
-
* }),
|
|
18
|
-
* });
|
|
19
|
-
*
|
|
20
|
-
* type Task = InferModel<typeof schema, 'tasks'>;
|
|
21
|
-
*/
|
|
22
|
-
import { z } from 'zod';
|
|
23
|
-
import { AbloValidationError } from '../errors.js';
|
|
24
|
-
import { fieldRef } from './fieldRef.js';
|
|
25
|
-
import { scopeSchema, grantsRefSchema } from './roles.js';
|
|
26
|
-
// Sync-group roles (identity and entity) are defined in `./roles.js` and
|
|
27
|
-
// re-exported here so they can also be imported from this module. See
|
|
28
|
-
// `./roles.js` for the full vocabulary.
|
|
29
|
-
export { identityRole, entityRole, extractIdentityIds, extractEntityIds, composeIdentitySyncGroups, composeEntitySyncGroups, intersectRequestedWithAllowed, syncGroup, syncGroupSchema, syncGroupInputSchema, isSyncGroupInput, identityRoleSchema, entityRoleSchema, roleSchema, roleSourceSchema, scopeSchema, grantsRefSchema, groupsInputSchema, } from './roles.js';
|
|
30
|
-
function resolveCasing(fn) {
|
|
31
|
-
if (fn === undefined)
|
|
32
|
-
return (x) => x;
|
|
33
|
-
if (typeof fn === 'function')
|
|
34
|
-
return fn;
|
|
35
|
-
switch (fn) {
|
|
36
|
-
case 'snake_case':
|
|
37
|
-
return camelToSnake;
|
|
38
|
-
case 'camelCase':
|
|
39
|
-
return (x) => x;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/** Converts a camelCase identifier to snake_case. Kept local so the package
|
|
43
|
-
* carries no database-driver dependency, and so consumers on any stack get the
|
|
44
|
-
* same result. */
|
|
45
|
-
function camelToSnake(identifier) {
|
|
46
|
-
return identifier.replace(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Session settings Ablo already sets on its direct-write connection. A
|
|
50
|
-
* `sessionSettings` entry FORWARDS Ablo's trusted context into a setting the
|
|
51
|
-
* customer's own policies read — it may never reassign one of these, which would
|
|
52
|
-
* let a schema push relax the engine's own scoping, timeouts, or `row_security`.
|
|
53
|
-
* Shared with the engine's direct-write seam so authoring-time validation and
|
|
54
|
-
* the runtime guard read one list. See ADR 0011.
|
|
55
|
-
*/
|
|
56
|
-
export const RESERVED_SESSION_SETTINGS = [
|
|
57
|
-
'statement_timeout',
|
|
58
|
-
'lock_timeout',
|
|
59
|
-
'row_security',
|
|
60
|
-
'search_path',
|
|
61
|
-
'app.current_org_id',
|
|
62
|
-
'app.current_project_id',
|
|
63
|
-
'app.current_environment',
|
|
64
|
-
'app.current_sandbox_id',
|
|
65
|
-
'app.current_participant_id',
|
|
66
|
-
'app.current_participant_kind',
|
|
67
|
-
'app.current_user_id',
|
|
68
|
-
];
|
|
69
|
-
/**
|
|
70
|
-
* Base fields every synced model gets automatically.
|
|
71
|
-
*
|
|
72
|
-
* Exported (internal) so `parseSchema` can rebuild a model's validator the
|
|
73
|
-
* same way `defineSchema` does — `baseFieldsSchema.merge(modelSchema)` — when
|
|
74
|
-
* reconstructing a `Schema` from its JSON form.
|
|
75
|
-
*/
|
|
76
|
-
export const baseFieldsSchema = z.object({
|
|
77
|
-
id: z.string(),
|
|
78
|
-
createdAt: z.date(),
|
|
79
|
-
updatedAt: z.date(),
|
|
80
|
-
organizationId: z.string().optional(),
|
|
81
|
-
createdBy: z.string().optional(),
|
|
82
|
-
});
|
|
83
|
-
/**
|
|
84
|
-
* The base-column names every model carries automatically — the keys of
|
|
85
|
-
* {@link baseFieldsSchema}, kept here as the single source of truth. Code
|
|
86
|
-
* generation reads it to avoid emitting a base column twice, and
|
|
87
|
-
* {@link defineSchema} uses it to reject a model that redeclares one, since
|
|
88
|
-
* merging the user's field over the base field would produce a `string & Date`
|
|
89
|
-
* type and break the build.
|
|
90
|
-
*/
|
|
91
|
-
export const BASE_FIELDS = [
|
|
92
|
-
'id',
|
|
93
|
-
'createdAt',
|
|
94
|
-
'updatedAt',
|
|
95
|
-
'organizationId',
|
|
96
|
-
'createdBy',
|
|
97
|
-
];
|
|
98
|
-
// ── Factory ───────────────────────────────────────────────────────────────
|
|
99
|
-
/**
|
|
100
|
-
* Define a sync engine schema.
|
|
101
|
-
*
|
|
102
|
-
* ```ts
|
|
103
|
-
* const schema = defineSchema({
|
|
104
|
-
* tasks: model({ title: z.string(), status: z.string().default('todo') }),
|
|
105
|
-
* projects: model({ name: z.string() }),
|
|
106
|
-
* });
|
|
107
|
-
* ```
|
|
108
|
-
*/
|
|
109
|
-
/**
|
|
110
|
-
* Rejects field names that will not survive the sync driver's snake_case ↔
|
|
111
|
-
* camelCase round-trip. The driver maps identifiers like this:
|
|
112
|
-
*
|
|
113
|
-
* `content_json` → `contentJson` (snake → camel)
|
|
114
|
-
* `contentJson` → `content_json` (camel → snake)
|
|
115
|
-
* `contentJSON` → `content_j_s_o_n` (does not round-trip)
|
|
116
|
-
*
|
|
117
|
-
* A name that does not round-trip through this pair of transforms will silently
|
|
118
|
-
* fail to populate on the client: the wire delivers one casing, the model reads
|
|
119
|
-
* another, and the field lands as `undefined`. Catching it here surfaces the
|
|
120
|
-
* problem when the schema is defined rather than at runtime.
|
|
121
|
-
*
|
|
122
|
-
* The rule: a standard camelCase identifier has runs of a single uppercase
|
|
123
|
-
* letter followed by lowercase letters, never two uppercase letters in a row.
|
|
124
|
-
* `contentJSON` has `JSON` in all caps, so it is rejected.
|
|
125
|
-
*/
|
|
126
|
-
function assertRoundTrippableCamelCase(modelName, fieldName) {
|
|
127
|
-
// Base fields merged in by defineSchema are already validated; skip.
|
|
128
|
-
if (fieldName === 'id')
|
|
129
|
-
return;
|
|
130
|
-
// Leading-lowercase constraint: fields must be camelCase, not PascalCase.
|
|
131
|
-
// PascalCase is reserved for typenames.
|
|
132
|
-
const first = fieldName.charAt(0);
|
|
133
|
-
if (first >= 'A' && first <= 'Z') {
|
|
134
|
-
throw new AbloValidationError(`[defineSchema] ${modelName}.${fieldName}: field names must start lowercase ` +
|
|
135
|
-
`(camelCase). Use "${first.toLowerCase()}${fieldName.slice(1)}" instead.`, { code: 'schema_field_not_camelcase' });
|
|
136
|
-
}
|
|
137
|
-
// Two-consecutive-uppercase check. The classic failure mode is
|
|
138
|
-
// `contentJSON`, `contentHTML`, `myURLParam`, etc. These don't round-trip
|
|
139
|
-
// through `postgres.camel` — the snake_case intermediate would be
|
|
140
|
-
// `content_j_s_o_n`, which is not a column that exists.
|
|
141
|
-
for (let i = 0; i < fieldName.length - 1; i++) {
|
|
142
|
-
const a = fieldName.charAt(i);
|
|
143
|
-
const b = fieldName.charAt(i + 1);
|
|
144
|
-
const aUpper = a >= 'A' && a <= 'Z';
|
|
145
|
-
const bUpper = b >= 'A' && b <= 'Z';
|
|
146
|
-
if (aUpper && bUpper) {
|
|
147
|
-
throw new AbloValidationError(`[defineSchema] ${modelName}.${fieldName}: two consecutive uppercase ` +
|
|
148
|
-
`letters ("${a}${b}") will not round-trip through the ` +
|
|
149
|
-
`snake_case ↔ camelCase transform used by the sync driver. ` +
|
|
150
|
-
`The wire delivers camelCase (lowercase after the first letter of ` +
|
|
151
|
-
`each word); a field named "${fieldName}" would never receive its ` +
|
|
152
|
-
`value and read as undefined on the client. Use standard ` +
|
|
153
|
-
`camelCase (e.g. "contentJson" instead of "contentJSON").`, { code: 'schema_field_consecutive_caps' });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* One {@link FieldRef} per declared field, keyed by model then field.
|
|
159
|
-
*
|
|
160
|
-
* Every construction of a {@link Schema} goes through here — `defineSchema`,
|
|
161
|
-
* the `selectModels`/`omitModels` projections, and the deserializer — so a
|
|
162
|
-
* projection carries references for exactly the models it kept and a schema
|
|
163
|
-
* rebuilt from JSON carries them at all.
|
|
164
|
-
*/
|
|
165
|
-
export function buildFieldRefs(models) {
|
|
166
|
-
const refs = {};
|
|
167
|
-
for (const [model, def] of Object.entries(models)) {
|
|
168
|
-
const perField = {};
|
|
169
|
-
for (const field of Object.keys(def.shape))
|
|
170
|
-
perField[field] = fieldRef(model, field);
|
|
171
|
-
refs[model] = perField;
|
|
172
|
-
}
|
|
173
|
-
return refs;
|
|
174
|
-
}
|
|
175
|
-
export function defineSchema(models, options) {
|
|
176
|
-
// Build validators with base fields merged in, and resolve defaults for
|
|
177
|
-
// `typename` and `persist.store` so downstream code (the loader and the
|
|
178
|
-
// hydration pipeline) can rely on these fields being set without re-deriving
|
|
179
|
-
// them at every call site.
|
|
180
|
-
//
|
|
181
|
-
// Defaults:
|
|
182
|
-
// typename ← schema key (e.g. `block` → `'block'`)
|
|
183
|
-
// persist.store ← typename (only resolved when `persist` was provided)
|
|
184
|
-
//
|
|
185
|
-
// A consumer that passes `typename: 'Block'` explicitly (common when
|
|
186
|
-
// the wire shape uses PascalCase while the schema key is camelCase) keeps
|
|
187
|
-
// that value — the fallback only fires when the field is unset.
|
|
188
|
-
//
|
|
189
|
-
// The models record is rebuilt rather than mutated in place because
|
|
190
|
-
// `ModelDef`'s fields are `readonly`. The rebuild is a shallow spread per
|
|
191
|
-
// entry, so the inferred shape/relations/fields metadata references are
|
|
192
|
-
// preserved (no type inference regression at consumer call sites).
|
|
193
|
-
const validators = {};
|
|
194
|
-
const resolvedModels = {};
|
|
195
|
-
const casing = resolveCasing(options?.casing);
|
|
196
|
-
for (const [name, def] of Object.entries(models)) {
|
|
197
|
-
// Catch round-trip-hostile field names at definition time. Deferring
|
|
198
|
-
// this check to runtime means every affected field silently reads
|
|
199
|
-
// `undefined` on the client — and the author only notices when a UI
|
|
200
|
-
// that depends on the field goes blank. Throwing here makes the
|
|
201
|
-
// failure immediate and unambiguous.
|
|
202
|
-
for (const fieldName of Object.keys(def.shape)) {
|
|
203
|
-
assertRoundTrippableCamelCase(name, fieldName);
|
|
204
|
-
// Reserved base columns are merged in below via `baseFieldsSchema.merge`,
|
|
205
|
-
// and Zod `.merge` silently OVERWRITES the base field with the user's —
|
|
206
|
-
// e.g. a model declaring `createdAt: z.string()` ends up with a field
|
|
207
|
-
// typed `string & Date`, which breaks the build. Reject the collision at
|
|
208
|
-
// definition time so the author sees an unambiguous error instead.
|
|
209
|
-
if (BASE_FIELDS.includes(fieldName)) {
|
|
210
|
-
throw new AbloValidationError(`[defineSchema] ${name}.${fieldName}: field \`${fieldName}\` collides with a ` +
|
|
211
|
-
`reserved field that the SDK provides automatically ` +
|
|
212
|
-
`(${BASE_FIELDS.join(', ')}). Remove it from your model — redeclaring it ` +
|
|
213
|
-
`produces a \`string & Date\` type and breaks the build.`, { code: 'schema_reserved_field', param: `${name}.${fieldName}` });
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
validators[name] = baseFieldsSchema.merge(def.schema);
|
|
217
|
-
// Resolve every relation's `foreignKeyColumn` once, now. The builder
|
|
218
|
-
// constructs each RelationDef with `foreignKeyColumn = foreignKey`
|
|
219
|
-
// (identity) so this is a no-op when `casing` is unset — existing
|
|
220
|
-
// consumers get the same behavior they had before the option landed.
|
|
221
|
-
// When `casing: 'snake_case'` is set, every FK flips to its
|
|
222
|
-
// snake_case DB column name here and nowhere else. A field-level
|
|
223
|
-
// `.from(column)` override wins over the convention, so legacy
|
|
224
|
-
// columns stay declared in the artifact instead of rediscovered by
|
|
225
|
-
// SQL compilers. Server-side SQL compilers read the resolved value
|
|
226
|
-
// directly.
|
|
227
|
-
for (const rel of Object.values(def.relations)) {
|
|
228
|
-
const fieldColumn = def.fields[rel.foreignKey]?.column;
|
|
229
|
-
rel.foreignKeyColumn = fieldColumn ?? casing(rel.foreignKey);
|
|
230
|
-
}
|
|
231
|
-
const typename = def.typename ?? name;
|
|
232
|
-
const persist = def.persist
|
|
233
|
-
? { ...def.persist, store: def.persist.store ?? typename }
|
|
234
|
-
: undefined;
|
|
235
|
-
// Physical table defaults to the schema key — the SAME rule the
|
|
236
|
-
// provisioner/planner use (`tableName ?? key`), resolved here once so the
|
|
237
|
-
// serialized artifact always carries it. Required now that models are
|
|
238
|
-
// mutable by default: the server's `buildModelMap` rejects a mutable
|
|
239
|
-
// model with no `tableName`, which would otherwise break every commit.
|
|
240
|
-
const tableName = def.tableName ?? name;
|
|
241
|
-
resolvedModels[name] = { ...def, typename, tableName, persist };
|
|
242
|
-
}
|
|
243
|
-
validateSyncGroupSchema(resolvedModels);
|
|
244
|
-
validateSessionSettings(options?.sessionSettings ?? {});
|
|
245
|
-
return {
|
|
246
|
-
// Cast back to S: we only added values to optional fields that were
|
|
247
|
-
// already part of ModelDef, so the shape is structurally unchanged.
|
|
248
|
-
models: resolvedModels,
|
|
249
|
-
validators: validators,
|
|
250
|
-
fields: buildFieldRefs(resolvedModels),
|
|
251
|
-
identityRoles: options?.identityRoles ?? [],
|
|
252
|
-
sessionSettings: options?.sessionSettings ?? {},
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Reject session-setting mappings that couldn't do what the author intends —
|
|
257
|
-
* caught here at definition time rather than silently dropped on the write path.
|
|
258
|
-
* Each key must name a non-empty setting the customer's policies read, and must
|
|
259
|
-
* not reassign one Ablo already manages. Uniqueness needs no check: the map is
|
|
260
|
-
* keyed by the setting name, so a duplicate is unrepresentable.
|
|
261
|
-
*/
|
|
262
|
-
function validateSessionSettings(settings) {
|
|
263
|
-
for (const setting of Object.keys(settings)) {
|
|
264
|
-
const name = setting.trim();
|
|
265
|
-
if (name === '') {
|
|
266
|
-
throw new AbloValidationError(`[defineSchema] sessionSettings: a key is empty. Name the Postgres ` +
|
|
267
|
-
`setting your row-level-security policies read, e.g. ` +
|
|
268
|
-
`\`{ 'app.current_org': 'orgId' }\`.`, { code: 'schema_definition_invalid', param: 'sessionSettings' });
|
|
269
|
-
}
|
|
270
|
-
if (RESERVED_SESSION_SETTINGS.includes(name)) {
|
|
271
|
-
throw new AbloValidationError(`[defineSchema] sessionSettings: \`${name}\` is a setting Ablo already ` +
|
|
272
|
-
`manages on its write connection, so a mapping can't reassign it — ` +
|
|
273
|
-
`that would let a schema relax the engine's own scoping. Point your ` +
|
|
274
|
-
`policies at a setting your app owns (e.g. \`app.current_org\`) and ` +
|
|
275
|
-
`map Ablo's trusted identity into that instead. Reserved: ` +
|
|
276
|
-
`${RESERVED_SESSION_SETTINGS.join(', ')}.`, { code: 'schema_definition_invalid', param: `sessionSettings.${name}` });
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Validates the relation-driven sync-group declarations (`scope` and `grants`)
|
|
282
|
-
* at schema-build time, so a mistyped membership edge fails here — with a
|
|
283
|
-
* structured error (`code`, `param`, `doc_url`) pointing at the exact
|
|
284
|
-
* declaration — rather than silently mis-routing changes at runtime.
|
|
285
|
-
*/
|
|
286
|
-
function validateSyncGroupSchema(models) {
|
|
287
|
-
for (const [name, def] of Object.entries(models)) {
|
|
288
|
-
// Shape-validate the `scope` declaration via the shared Zod schema.
|
|
289
|
-
if (def.scope !== undefined && !scopeSchema.safeParse(def.scope).success) {
|
|
290
|
-
throw new AbloValidationError(`Model "${name}": scope kind "${String(def.scope)}" must be a lowercase identifier (e.g. 'workspace').`, { code: 'schema_scope_kind_invalid', param: `${name}.scope` });
|
|
291
|
-
}
|
|
292
|
-
if (!def.grants)
|
|
293
|
-
continue;
|
|
294
|
-
// Shape-validate the `grants` edge via the shared Zod schema before the
|
|
295
|
-
// cross-field (relation-exists / belongsTo) checks below.
|
|
296
|
-
if (!grantsRefSchema.safeParse(def.grants).success) {
|
|
297
|
-
throw new AbloValidationError(`Model "${name}": grants must be { subject, scope } naming two relations on this model.`, { code: 'schema_grants_shape_invalid', param: `${name}.grants` });
|
|
298
|
-
}
|
|
299
|
-
const relations = def.relations;
|
|
300
|
-
for (const role of ['subject', 'scope']) {
|
|
301
|
-
const relName = def.grants[role];
|
|
302
|
-
const rel = relations?.[relName];
|
|
303
|
-
if (!rel) {
|
|
304
|
-
throw new AbloValidationError(`Model "${name}": grants.${role} "${relName}" is not a relation on this model. ` +
|
|
305
|
-
`Declare a \`belongsTo\` relation named "${relName}" first.`, { code: 'schema_grants_relation_missing', param: `${name}.grants.${role}` });
|
|
306
|
-
}
|
|
307
|
-
if (rel.type !== 'belongsTo') {
|
|
308
|
-
throw new AbloValidationError(`Model "${name}": grants.${role} "${relName}" must be a \`belongsTo\` relation ` +
|
|
309
|
-
`(got "${rel.type}"). A membership edge points at a single subject/scope row.`, { code: 'schema_grants_relation_kind', param: `${name}.grants.${role}` });
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
// The scope edge must target a model that is actually a scope root —
|
|
313
|
-
// otherwise the resolved `<kind>:<id>` group is one nothing fans into.
|
|
314
|
-
const scopeRel = relations[def.grants.scope];
|
|
315
|
-
// Unreachable: the role loop above already threw if the scope relation
|
|
316
|
-
// is missing — this narrows the indexed access for the checks below.
|
|
317
|
-
if (!scopeRel)
|
|
318
|
-
continue;
|
|
319
|
-
const target = models[scopeRel.target];
|
|
320
|
-
if (target && !target.scope) {
|
|
321
|
-
throw new AbloValidationError(`Model "${name}": grants.scope "${def.grants.scope}" targets "${scopeRel.target}", ` +
|
|
322
|
-
`which is not a scope root. Add \`scope: true\` to the "${scopeRel.target}" model.`, { code: 'schema_grants_target_not_scope_root', param: `${scopeRel.target}.scope` });
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Defines how a model's rows are scoped to a tenant. Two forms live here, and the
|
|
3
|
-
* separation between them is deliberate:
|
|
4
|
-
*
|
|
5
|
-
* 1. The canonical form, {@link Tenancy} — a discriminated union that every
|
|
6
|
-
* consumer reads (provisioning and row-level security, introspection, the
|
|
7
|
-
* runtime, and the CLI) and the shape that crosses the wire in `ModelJSON`.
|
|
8
|
-
* One exhaustively switchable shape, so the type system enforces the isolation
|
|
9
|
-
* boundary.
|
|
10
|
-
* 2. The authoring form, {@link PolicyInput} — the `policy: { by }` option a
|
|
11
|
-
* schema author writes. The name follows SQL row-level security, where a
|
|
12
|
-
* policy is the rule that decides which rows a tenant may read.
|
|
13
|
-
* {@link resolvePolicy} converts it to the canonical {@link Tenancy} when the
|
|
14
|
-
* model is built, so the authoring vocabulary never reaches the wire or any
|
|
15
|
-
* consumer.
|
|
16
|
-
*
|
|
17
|
-
* The canonical form is an exhaustively switchable discriminated union, so the type
|
|
18
|
-
* system enforces the isolation boundary at every consumer. The authoring form is a
|
|
19
|
-
* flat object whose `by` names the choice and whose per-branch rules are checked by a
|
|
20
|
-
* refinement — the opt-out from tenant scoping (`{ by: 'none' }`) and the
|
|
21
|
-
* source-derived case (`{ by: 'source' }`) are explicit, named values rather than
|
|
22
|
-
* falsy flags, important because `none` makes an entire table readable across tenants.
|
|
23
|
-
*/
|
|
24
|
-
import { z } from 'zod';
|
|
25
|
-
/** The default physical tenancy column. This is the single canonical definition of that column name. */
|
|
26
|
-
export declare const DEFAULT_ORG_COLUMN = "organization_id";
|
|
27
|
-
/**
|
|
28
|
-
* Scopes a table's rows through a parent table, for rows that carry no tenancy
|
|
29
|
-
* column of their own (for example, blocks scoped through their section, report,
|
|
30
|
-
* and organization). This is the canonical `parent` payload; authors write the
|
|
31
|
-
* friendlier {@link PolicyInput} `{ by: 'parent', fk, parent }` form, which
|
|
32
|
-
* {@link resolvePolicy} normalizes into this.
|
|
33
|
-
*/
|
|
34
|
-
export declare const scopedViaRefSchema: z.ZodObject<{
|
|
35
|
-
localKey: z.ZodString;
|
|
36
|
-
parentTable: z.ZodString;
|
|
37
|
-
parentKey: z.ZodOptional<z.ZodString>;
|
|
38
|
-
parentOrgColumn: z.ZodOptional<z.ZodString>;
|
|
39
|
-
}, z.core.$strip>;
|
|
40
|
-
export type ScopedViaRef = z.infer<typeof scopedViaRefSchema>;
|
|
41
|
-
/** How a model's rows are scoped to a tenant — the canonical, wire-facing form. */
|
|
42
|
-
export declare const tenancySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
43
|
-
kind: z.ZodLiteral<"column">;
|
|
44
|
-
column: z.ZodString;
|
|
45
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
46
|
-
kind: z.ZodLiteral<"parent">;
|
|
47
|
-
via: z.ZodObject<{
|
|
48
|
-
localKey: z.ZodString;
|
|
49
|
-
parentTable: z.ZodString;
|
|
50
|
-
parentKey: z.ZodOptional<z.ZodString>;
|
|
51
|
-
parentOrgColumn: z.ZodOptional<z.ZodString>;
|
|
52
|
-
}, z.core.$strip>;
|
|
53
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
54
|
-
kind: z.ZodLiteral<"none">;
|
|
55
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
56
|
-
kind: z.ZodLiteral<"source">;
|
|
57
|
-
}, z.core.$strip>], "kind">;
|
|
58
|
-
export type Tenancy = z.infer<typeof tenancySchema>;
|
|
59
|
-
/**
|
|
60
|
-
* The authoring form of tenancy — what a schema author writes as the model's
|
|
61
|
-
* `policy` option. The name follows SQL row-level security, where a policy is the
|
|
62
|
-
* rule that decides which rows a tenant may read.
|
|
63
|
-
*
|
|
64
|
-
* The four choices `by` can take:
|
|
65
|
-
*
|
|
66
|
-
* - `{ by: 'column' }` — a row-local tenancy column (the default). `column`
|
|
67
|
-
* overrides the name (default {@link DEFAULT_ORG_COLUMN}).
|
|
68
|
-
* - `{ by: 'parent', fk, parent }` — inherit tenancy through a foreign key when
|
|
69
|
-
* this table has no tenancy column of its own. `parentKey` (default `'id'`) and
|
|
70
|
-
* `parentTenantColumn` (default {@link DEFAULT_ORG_COLUMN}) are optional overrides.
|
|
71
|
-
* - `{ by: 'source' }` — derive the organization from the connected source that
|
|
72
|
-
* registered the model, for a single-tenant connected database that carries no
|
|
73
|
-
* `organization_id` column. The registration is the tenant boundary.
|
|
74
|
-
* - `{ by: 'none' }` — genuinely global or reference data. This makes the whole
|
|
75
|
-
* table readable across tenants, so it is correct only for tables that have no tenant.
|
|
76
|
-
*
|
|
77
|
-
* The runtime schema is one flat object rather than a discriminated union: `by`
|
|
78
|
-
* selects the branch, and the {@link https://zod.dev | superRefine} pass enforces
|
|
79
|
-
* the per-branch field rules (`parent` requires `fk` and `parent`; the other three
|
|
80
|
-
* take no branch fields). The exported {@link PolicyInput} type is hand-written as a
|
|
81
|
-
* precise union so the compiler still requires the right fields per `by` — the flat
|
|
82
|
-
* schema validates at runtime, the union guides at author time, and the type guard
|
|
83
|
-
* below keeps the two from drifting.
|
|
84
|
-
*/
|
|
85
|
-
export declare const policyInputSchema: z.ZodObject<{
|
|
86
|
-
by: z.ZodEnum<{
|
|
87
|
-
none: "none";
|
|
88
|
-
source: "source";
|
|
89
|
-
column: "column";
|
|
90
|
-
parent: "parent";
|
|
91
|
-
}>;
|
|
92
|
-
column: z.ZodOptional<z.ZodString>;
|
|
93
|
-
fk: z.ZodOptional<z.ZodString>;
|
|
94
|
-
parent: z.ZodOptional<z.ZodString>;
|
|
95
|
-
parentKey: z.ZodOptional<z.ZodString>;
|
|
96
|
-
parentTenantColumn: z.ZodOptional<z.ZodString>;
|
|
97
|
-
}, z.core.$strip>;
|
|
98
|
-
/**
|
|
99
|
-
* The precise authoring type. Hand-written rather than inferred from
|
|
100
|
-
* {@link policyInputSchema} so the compiler enforces the per-branch fields the flat
|
|
101
|
-
* runtime schema can only check at parse time — an author writing `{ by: 'parent' }`
|
|
102
|
-
* gets a type error for the missing `fk`/`parent`, not a runtime one.
|
|
103
|
-
*/
|
|
104
|
-
export type PolicyInput = {
|
|
105
|
-
by: 'column';
|
|
106
|
-
column?: string;
|
|
107
|
-
} | {
|
|
108
|
-
by: 'parent';
|
|
109
|
-
fk: string;
|
|
110
|
-
parent: string;
|
|
111
|
-
parentKey?: string;
|
|
112
|
-
parentTenantColumn?: string;
|
|
113
|
-
} | {
|
|
114
|
-
by: 'source';
|
|
115
|
-
} | {
|
|
116
|
-
by: 'none';
|
|
117
|
-
};
|
|
118
|
-
/**
|
|
119
|
-
* Normalizes the authoring {@link PolicyInput} into the canonical {@link Tenancy}.
|
|
120
|
-
* Called once, when the model is built, so that `ModelDef`, `ModelJSON`, and every
|
|
121
|
-
* consumer see only the canonical union. Omitting `policy` defaults to a row-local
|
|
122
|
-
* `organization_id` column.
|
|
123
|
-
*/
|
|
124
|
-
export declare function resolvePolicy(input?: PolicyInput): Tenancy;
|
|
125
|
-
/**
|
|
126
|
-
* Reads the canonical {@link Tenancy} off an already-built model definition (or a
|
|
127
|
-
* parsed `ModelJSON`), defaulting to a row-local `organization_id` column when it is
|
|
128
|
-
* absent.
|
|
129
|
-
*
|
|
130
|
-
* This is the read-side helper. Consumers — provisioning and row-level security, the
|
|
131
|
-
* membership resolver, DDL generation, and the CLI — call it to get a model's
|
|
132
|
-
* tenancy without re-deriving the default each time. It is not the authoring
|
|
133
|
-
* normalizer; that is {@link resolvePolicy}.
|
|
134
|
-
*/
|
|
135
|
-
export declare function resolveTenancy(def: {
|
|
136
|
-
tenancy?: Tenancy;
|
|
137
|
-
}): Tenancy;
|
|
138
|
-
/** The physical tenancy column for a column-scoped model, else `null`. */
|
|
139
|
-
export declare function tenancyColumn(t: Tenancy): string | null;
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Defines how a model's rows are scoped to a tenant. Two forms live here, and the
|
|
3
|
-
* separation between them is deliberate:
|
|
4
|
-
*
|
|
5
|
-
* 1. The canonical form, {@link Tenancy} — a discriminated union that every
|
|
6
|
-
* consumer reads (provisioning and row-level security, introspection, the
|
|
7
|
-
* runtime, and the CLI) and the shape that crosses the wire in `ModelJSON`.
|
|
8
|
-
* One exhaustively switchable shape, so the type system enforces the isolation
|
|
9
|
-
* boundary.
|
|
10
|
-
* 2. The authoring form, {@link PolicyInput} — the `policy: { by }` option a
|
|
11
|
-
* schema author writes. The name follows SQL row-level security, where a
|
|
12
|
-
* policy is the rule that decides which rows a tenant may read.
|
|
13
|
-
* {@link resolvePolicy} converts it to the canonical {@link Tenancy} when the
|
|
14
|
-
* model is built, so the authoring vocabulary never reaches the wire or any
|
|
15
|
-
* consumer.
|
|
16
|
-
*
|
|
17
|
-
* The canonical form is an exhaustively switchable discriminated union, so the type
|
|
18
|
-
* system enforces the isolation boundary at every consumer. The authoring form is a
|
|
19
|
-
* flat object whose `by` names the choice and whose per-branch rules are checked by a
|
|
20
|
-
* refinement — the opt-out from tenant scoping (`{ by: 'none' }`) and the
|
|
21
|
-
* source-derived case (`{ by: 'source' }`) are explicit, named values rather than
|
|
22
|
-
* falsy flags, important because `none` makes an entire table readable across tenants.
|
|
23
|
-
*/
|
|
24
|
-
import { z } from 'zod';
|
|
25
|
-
/** The default physical tenancy column. This is the single canonical definition of that column name. */
|
|
26
|
-
export const DEFAULT_ORG_COLUMN = 'organization_id';
|
|
27
|
-
/**
|
|
28
|
-
* Scopes a table's rows through a parent table, for rows that carry no tenancy
|
|
29
|
-
* column of their own (for example, blocks scoped through their section, report,
|
|
30
|
-
* and organization). This is the canonical `parent` payload; authors write the
|
|
31
|
-
* friendlier {@link PolicyInput} `{ by: 'parent', fk, parent }` form, which
|
|
32
|
-
* {@link resolvePolicy} normalizes into this.
|
|
33
|
-
*/
|
|
34
|
-
export const scopedViaRefSchema = z.object({
|
|
35
|
-
/** Column on this table that points at the parent (for example, `'team_id'`). */
|
|
36
|
-
localKey: z.string().min(1),
|
|
37
|
-
/** Parent table name (e.g. `'team'`). */
|
|
38
|
-
parentTable: z.string().min(1),
|
|
39
|
-
/** Column on the parent that `localKey` references. Default `'id'`. */
|
|
40
|
-
parentKey: z.string().min(1).optional(),
|
|
41
|
-
/** Column on the parent holding the tenant id. Default {@link DEFAULT_ORG_COLUMN}. */
|
|
42
|
-
parentOrgColumn: z.string().min(1).optional(),
|
|
43
|
-
});
|
|
44
|
-
/** How a model's rows are scoped to a tenant — the canonical, wire-facing form. */
|
|
45
|
-
export const tenancySchema = z.discriminatedUnion('kind', [
|
|
46
|
-
/** Row-local tenancy column (default name `organization_id`, overridable). */
|
|
47
|
-
z.object({ kind: z.literal('column'), column: z.string().min(1) }),
|
|
48
|
-
/** Scoped through a parent table's tenancy. */
|
|
49
|
-
z.object({ kind: z.literal('parent'), via: scopedViaRefSchema }),
|
|
50
|
-
/** Not tenant-scoped (global / reference data). */
|
|
51
|
-
z.object({ kind: z.literal('none') }),
|
|
52
|
-
/**
|
|
53
|
-
* Scoped by the data source itself: the model carries no tenancy column, and the
|
|
54
|
-
* owning organization is derived from the connected source that registered it — the
|
|
55
|
-
* registration is the tenant boundary. This is the natural default for a
|
|
56
|
-
* single-tenant connected database (`ablo connect`), which holds one org's rows and
|
|
57
|
-
* so has no per-row `organization_id` to read. Carries no payload for the same
|
|
58
|
-
* reason `none` does: the binding lives in the source registration, not the model.
|
|
59
|
-
*/
|
|
60
|
-
z.object({ kind: z.literal('source') }),
|
|
61
|
-
]);
|
|
62
|
-
/**
|
|
63
|
-
* The authoring form of tenancy — what a schema author writes as the model's
|
|
64
|
-
* `policy` option. The name follows SQL row-level security, where a policy is the
|
|
65
|
-
* rule that decides which rows a tenant may read.
|
|
66
|
-
*
|
|
67
|
-
* The four choices `by` can take:
|
|
68
|
-
*
|
|
69
|
-
* - `{ by: 'column' }` — a row-local tenancy column (the default). `column`
|
|
70
|
-
* overrides the name (default {@link DEFAULT_ORG_COLUMN}).
|
|
71
|
-
* - `{ by: 'parent', fk, parent }` — inherit tenancy through a foreign key when
|
|
72
|
-
* this table has no tenancy column of its own. `parentKey` (default `'id'`) and
|
|
73
|
-
* `parentTenantColumn` (default {@link DEFAULT_ORG_COLUMN}) are optional overrides.
|
|
74
|
-
* - `{ by: 'source' }` — derive the organization from the connected source that
|
|
75
|
-
* registered the model, for a single-tenant connected database that carries no
|
|
76
|
-
* `organization_id` column. The registration is the tenant boundary.
|
|
77
|
-
* - `{ by: 'none' }` — genuinely global or reference data. This makes the whole
|
|
78
|
-
* table readable across tenants, so it is correct only for tables that have no tenant.
|
|
79
|
-
*
|
|
80
|
-
* The runtime schema is one flat object rather than a discriminated union: `by`
|
|
81
|
-
* selects the branch, and the {@link https://zod.dev | superRefine} pass enforces
|
|
82
|
-
* the per-branch field rules (`parent` requires `fk` and `parent`; the other three
|
|
83
|
-
* take no branch fields). The exported {@link PolicyInput} type is hand-written as a
|
|
84
|
-
* precise union so the compiler still requires the right fields per `by` — the flat
|
|
85
|
-
* schema validates at runtime, the union guides at author time, and the type guard
|
|
86
|
-
* below keeps the two from drifting.
|
|
87
|
-
*/
|
|
88
|
-
export const policyInputSchema = z
|
|
89
|
-
.object({
|
|
90
|
-
by: z.enum(['column', 'parent', 'none', 'source']),
|
|
91
|
-
/** `by: 'column'` — override the physical tenancy column name. Default {@link DEFAULT_ORG_COLUMN}. */
|
|
92
|
-
column: z.string().min(1).optional(),
|
|
93
|
-
/** `by: 'parent'` — column on this table that points at the parent (for example, `'sectionId'`). */
|
|
94
|
-
fk: z.string().min(1).optional(),
|
|
95
|
-
/** `by: 'parent'` — parent table name (e.g. `'sections'`). */
|
|
96
|
-
parent: z.string().min(1).optional(),
|
|
97
|
-
/** `by: 'parent'` — column on the parent that `fk` references. Default `'id'`. */
|
|
98
|
-
parentKey: z.string().min(1).optional(),
|
|
99
|
-
/** `by: 'parent'` — column on the parent holding the tenant id. Default {@link DEFAULT_ORG_COLUMN}. */
|
|
100
|
-
parentTenantColumn: z.string().min(1).optional(),
|
|
101
|
-
})
|
|
102
|
-
.superRefine((policy, ctx) => {
|
|
103
|
-
if (policy.by === 'parent') {
|
|
104
|
-
if (!policy.fk) {
|
|
105
|
-
ctx.addIssue({
|
|
106
|
-
code: z.ZodIssueCode.custom,
|
|
107
|
-
path: ['fk'],
|
|
108
|
-
message: "A `by: 'parent'` policy needs `fk` — the column on this table that points at the parent row whose tenancy it inherits.",
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
if (!policy.parent) {
|
|
112
|
-
ctx.addIssue({
|
|
113
|
-
code: z.ZodIssueCode.custom,
|
|
114
|
-
path: ['parent'],
|
|
115
|
-
message: "A `by: 'parent'` policy needs `parent` — the name of the parent table this table is scoped through.",
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// A `column` policy may carry `column`; the parent fields belong only to `parent`.
|
|
120
|
-
// Flag a field that landed on the wrong branch so a typo doesn't silently no-op.
|
|
121
|
-
const parentOnly = ['fk', 'parent', 'parentKey', 'parentTenantColumn'];
|
|
122
|
-
if (policy.by !== 'parent') {
|
|
123
|
-
for (const field of parentOnly) {
|
|
124
|
-
if (policy[field] !== undefined) {
|
|
125
|
-
ctx.addIssue({
|
|
126
|
-
code: z.ZodIssueCode.custom,
|
|
127
|
-
path: [field],
|
|
128
|
-
message: `\`${field}\` only applies to a \`by: 'parent'\` policy; remove it or set \`by: 'parent'\`.`,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (policy.by !== 'column' && policy.column !== undefined) {
|
|
134
|
-
ctx.addIssue({
|
|
135
|
-
code: z.ZodIssueCode.custom,
|
|
136
|
-
path: ['column'],
|
|
137
|
-
message: "`column` only applies to a `by: 'column'` policy; remove it or set `by: 'column'`.",
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
const _policyInputAssignable = [
|
|
142
|
-
true,
|
|
143
|
-
true,
|
|
144
|
-
];
|
|
145
|
-
void _policyInputAssignable;
|
|
146
|
-
/**
|
|
147
|
-
* Normalizes the authoring {@link PolicyInput} into the canonical {@link Tenancy}.
|
|
148
|
-
* Called once, when the model is built, so that `ModelDef`, `ModelJSON`, and every
|
|
149
|
-
* consumer see only the canonical union. Omitting `policy` defaults to a row-local
|
|
150
|
-
* `organization_id` column.
|
|
151
|
-
*/
|
|
152
|
-
export function resolvePolicy(input) {
|
|
153
|
-
if (!input)
|
|
154
|
-
return { kind: 'column', column: DEFAULT_ORG_COLUMN };
|
|
155
|
-
switch (input.by) {
|
|
156
|
-
case 'column':
|
|
157
|
-
return { kind: 'column', column: input.column ?? DEFAULT_ORG_COLUMN };
|
|
158
|
-
case 'parent':
|
|
159
|
-
return {
|
|
160
|
-
kind: 'parent',
|
|
161
|
-
via: {
|
|
162
|
-
localKey: input.fk,
|
|
163
|
-
parentTable: input.parent,
|
|
164
|
-
parentKey: input.parentKey,
|
|
165
|
-
parentOrgColumn: input.parentTenantColumn,
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
case 'source':
|
|
169
|
-
return { kind: 'source' };
|
|
170
|
-
case 'none':
|
|
171
|
-
return { kind: 'none' };
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Reads the canonical {@link Tenancy} off an already-built model definition (or a
|
|
176
|
-
* parsed `ModelJSON`), defaulting to a row-local `organization_id` column when it is
|
|
177
|
-
* absent.
|
|
178
|
-
*
|
|
179
|
-
* This is the read-side helper. Consumers — provisioning and row-level security, the
|
|
180
|
-
* membership resolver, DDL generation, and the CLI — call it to get a model's
|
|
181
|
-
* tenancy without re-deriving the default each time. It is not the authoring
|
|
182
|
-
* normalizer; that is {@link resolvePolicy}.
|
|
183
|
-
*/
|
|
184
|
-
export function resolveTenancy(def) {
|
|
185
|
-
return def.tenancy ?? { kind: 'column', column: DEFAULT_ORG_COLUMN };
|
|
186
|
-
}
|
|
187
|
-
/** The physical tenancy column for a column-scoped model, else `null`. */
|
|
188
|
-
export function tenancyColumn(t) {
|
|
189
|
-
return t.kind === 'column' ? t.column : null;
|
|
190
|
-
}
|