@abloatai/ablo 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -2
- package/README.md +102 -86
- package/dist/BaseSyncedStore.d.ts +85 -88
- package/dist/BaseSyncedStore.js +134 -151
- package/dist/Database.d.ts +68 -69
- package/dist/Database.js +316 -135
- package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
- package/dist/{ObjectPool.js → InstanceCache.js} +85 -83
- package/dist/LazyReferenceCollection.d.ts +11 -15
- package/dist/LazyReferenceCollection.js +12 -16
- package/dist/Model.d.ts +54 -52
- package/dist/Model.js +78 -62
- package/dist/ModelRegistry.d.ts +21 -19
- package/dist/ModelRegistry.js +23 -27
- package/dist/NetworkMonitor.d.ts +5 -6
- package/dist/NetworkMonitor.js +5 -6
- package/dist/SyncClient.d.ts +122 -118
- package/dist/SyncClient.js +541 -245
- package/dist/adapters/alwaysOnline.d.ts +6 -8
- package/dist/adapters/alwaysOnline.js +6 -8
- package/dist/adapters/inMemoryStorage.d.ts +10 -9
- package/dist/adapters/inMemoryStorage.js +21 -9
- package/dist/agent/Agent.d.ts +27 -32
- package/dist/agent/Agent.js +18 -19
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +5 -5
- package/dist/agent/session.d.ts +47 -44
- package/dist/agent/session.js +37 -48
- package/dist/agent/types.d.ts +26 -31
- package/dist/agent/types.js +6 -7
- package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
- package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
- package/dist/ai-sdk/coordinationContext.d.ts +46 -0
- package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +26 -33
- package/dist/ai-sdk/index.d.ts +25 -22
- package/dist/ai-sdk/index.js +25 -22
- package/dist/ai-sdk/wrap.d.ts +6 -7
- package/dist/ai-sdk/wrap.js +1 -1
- package/dist/auth/credentialPolicy.d.ts +69 -74
- package/dist/auth/credentialPolicy.js +51 -56
- package/dist/auth/credentialSource.d.ts +6 -5
- package/dist/auth/credentialSource.js +9 -10
- package/dist/auth/index.d.ts +59 -58
- package/dist/auth/index.js +31 -37
- package/dist/auth/schemas.d.ts +5 -4
- package/dist/auth/schemas.js +5 -4
- package/dist/batching/index.d.ts +19 -21
- package/dist/batching/index.js +14 -17
- package/dist/cli.cjs +173 -121
- package/dist/client/Ablo.d.ts +97 -74
- package/dist/client/Ablo.js +129 -163
- package/dist/client/ApiClient.d.ts +30 -19
- package/dist/client/ApiClient.js +442 -81
- package/dist/client/auth.d.ts +47 -47
- package/dist/client/auth.js +108 -117
- package/dist/client/claimHeartbeatLoop.d.ts +50 -0
- package/dist/client/claimHeartbeatLoop.js +88 -0
- package/dist/client/consoleLogger.d.ts +5 -6
- package/dist/client/consoleLogger.js +5 -6
- package/dist/client/createInternalComponents.d.ts +16 -17
- package/dist/client/createInternalComponents.js +26 -31
- package/dist/client/createModelProxy.d.ts +130 -120
- package/dist/client/createModelProxy.js +152 -122
- package/dist/client/credentialEndpoint.d.ts +40 -42
- package/dist/client/credentialEndpoint.js +35 -36
- package/dist/client/functionalUpdate.d.ts +29 -27
- package/dist/client/functionalUpdate.js +21 -21
- package/dist/client/hostedEndpoints.d.ts +9 -12
- package/dist/client/hostedEndpoints.js +9 -12
- package/dist/client/httpClient.d.ts +59 -53
- package/dist/client/httpClient.js +29 -31
- package/dist/client/identity.d.ts +15 -20
- package/dist/client/identity.js +47 -58
- package/dist/client/modelRegistration.d.ts +5 -9
- package/dist/client/modelRegistration.js +78 -87
- package/dist/client/options.d.ts +157 -157
- package/dist/client/options.js +3 -7
- package/dist/client/registerDataSource.d.ts +9 -9
- package/dist/client/registerDataSource.js +15 -16
- package/dist/client/resourceTypes.d.ts +64 -75
- package/dist/client/resourceTypes.js +4 -10
- package/dist/client/schemaConfig.d.ts +31 -43
- package/dist/client/schemaConfig.js +38 -50
- package/dist/client/sessionMint.d.ts +16 -12
- package/dist/client/sessionMint.js +26 -31
- package/dist/client/validateAbloOptions.d.ts +12 -14
- package/dist/client/validateAbloOptions.js +8 -9
- package/dist/client/writeOptionsSchema.d.ts +18 -16
- package/dist/client/writeOptionsSchema.js +23 -20
- package/dist/client/wsMutationExecutor.d.ts +16 -20
- package/dist/client/wsMutationExecutor.js +18 -23
- package/dist/commit/contract.d.ts +493 -0
- package/dist/commit/contract.js +187 -0
- package/dist/commit/index.d.ts +6 -0
- package/dist/commit/index.js +5 -0
- package/dist/context.d.ts +6 -4
- package/dist/context.js +6 -4
- package/dist/coordination/index.d.ts +10 -8
- package/dist/coordination/index.js +14 -12
- package/dist/coordination/schema.d.ts +176 -128
- package/dist/coordination/schema.js +197 -133
- package/dist/coordination/trace.d.ts +9 -10
- package/dist/coordination/trace.js +13 -14
- package/dist/core/DatabaseManager.d.ts +5 -7
- package/dist/core/DatabaseManager.js +15 -19
- package/dist/core/QueryProcessor.d.ts +7 -9
- package/dist/core/QueryProcessor.js +22 -28
- package/dist/core/QueryView.d.ts +8 -8
- package/dist/core/QueryView.js +2 -2
- package/dist/core/StoreManager.d.ts +14 -14
- package/dist/core/StoreManager.js +33 -24
- package/dist/core/ViewRegistry.d.ts +5 -5
- package/dist/core/ViewRegistry.js +4 -4
- package/dist/core/index.d.ts +17 -12
- package/dist/core/index.js +32 -26
- package/dist/core/openIDBWithTimeout.d.ts +38 -36
- package/dist/core/openIDBWithTimeout.js +42 -43
- package/dist/core/queryUtils.d.ts +45 -0
- package/dist/core/queryUtils.js +69 -0
- package/dist/core/storeContract.d.ts +63 -61
- package/dist/core/storeContract.js +8 -12
- package/dist/environment.d.ts +28 -0
- package/dist/environment.js +21 -0
- package/dist/errorCodes.d.ts +107 -99
- package/dist/errorCodes.js +137 -134
- package/dist/errors.d.ts +160 -166
- package/dist/errors.js +155 -158
- package/dist/index.d.ts +36 -27
- package/dist/index.js +91 -86
- package/dist/interfaces/index.d.ts +102 -113
- package/dist/interfaces/index.js +5 -4
- package/dist/keys/index.d.ts +27 -29
- package/dist/keys/index.js +41 -40
- package/dist/mutators/RecordingTransaction.d.ts +16 -16
- package/dist/mutators/RecordingTransaction.js +31 -37
- package/dist/mutators/Transaction.d.ts +18 -26
- package/dist/mutators/Transaction.js +14 -20
- package/dist/mutators/UndoManager.d.ts +124 -131
- package/dist/mutators/UndoManager.js +177 -156
- package/dist/mutators/defineMutators.d.ts +23 -34
- package/dist/mutators/defineMutators.js +14 -20
- package/dist/mutators/inverseOp.d.ts +12 -15
- package/dist/mutators/inverseOp.js +12 -15
- package/dist/mutators/mutateActions.d.ts +10 -9
- package/dist/mutators/mutateActions.js +1 -1
- package/dist/mutators/readerActions.d.ts +9 -8
- package/dist/mutators/readerActions.js +2 -2
- package/dist/mutators/undoApply.d.ts +31 -27
- package/dist/mutators/undoApply.js +26 -24
- package/dist/policy/index.d.ts +5 -3
- package/dist/policy/index.js +5 -3
- package/dist/policy/types.d.ts +104 -100
- package/dist/policy/types.js +67 -66
- package/dist/query/client.d.ts +28 -23
- package/dist/query/client.js +45 -43
- package/dist/query/types.d.ts +37 -60
- package/dist/query/types.js +13 -33
- package/dist/react/AbloProvider.d.ts +1 -1
- package/dist/react/AbloProvider.js +2 -2
- package/dist/react/context.d.ts +25 -28
- package/dist/react/context.js +9 -10
- package/dist/react/index.d.ts +41 -42
- package/dist/react/index.js +37 -38
- package/dist/react/internalContext.d.ts +17 -19
- package/dist/react/useAblo.d.ts +28 -25
- package/dist/react/useAblo.js +41 -17
- package/dist/react/useCurrentUserId.d.ts +8 -7
- package/dist/react/useCurrentUserId.js +8 -7
- package/dist/react/useErrorListener.d.ts +7 -7
- package/dist/react/useErrorListener.js +10 -11
- package/dist/react/useMutationFailureListener.d.ts +8 -8
- package/dist/react/useMutationFailureListener.js +8 -8
- package/dist/react/useMutators.d.ts +11 -11
- package/dist/react/useMutators.js +3 -3
- package/dist/react/useReactive.js +2 -2
- package/dist/react/useSyncStatus.d.ts +4 -6
- package/dist/react/useUndoScope.d.ts +7 -9
- package/dist/react/useUndoScope.js +1 -1
- package/dist/schema/coordination.d.ts +21 -25
- package/dist/schema/coordination.js +21 -25
- package/dist/schema/ddl.d.ts +43 -39
- package/dist/schema/ddl.js +75 -68
- package/dist/schema/ddlLock.d.ts +20 -24
- package/dist/schema/ddlLock.js +18 -23
- package/dist/schema/diff.d.ts +99 -61
- package/dist/schema/diff.js +43 -34
- package/dist/schema/field.d.ts +37 -42
- package/dist/schema/field.js +35 -48
- package/dist/schema/generate.d.ts +12 -12
- package/dist/schema/generate.js +12 -12
- package/dist/schema/index.d.ts +3 -3
- package/dist/schema/index.js +21 -23
- package/dist/schema/model.d.ts +118 -143
- package/dist/schema/model.js +22 -33
- package/dist/schema/openapi.d.ts +10 -9
- package/dist/schema/openapi.js +5 -3
- package/dist/schema/queries.d.ts +29 -31
- package/dist/schema/queries.js +23 -25
- package/dist/schema/relation.d.ts +89 -99
- package/dist/schema/relation.js +13 -13
- package/dist/schema/residency.d.ts +16 -13
- package/dist/schema/residency.js +16 -13
- package/dist/schema/roles.d.ts +36 -43
- package/dist/schema/roles.js +31 -37
- package/dist/schema/schema.d.ts +64 -43
- package/dist/schema/schema.js +31 -32
- package/dist/schema/select.d.ts +13 -13
- package/dist/schema/select.js +13 -13
- package/dist/schema/serialize.d.ts +28 -31
- package/dist/schema/serialize.js +27 -31
- package/dist/schema/sugar.d.ts +17 -32
- package/dist/schema/sugar.js +14 -29
- package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +26 -49
- package/dist/schema/syncDeltaRow.js +89 -0
- package/dist/schema/tenancy.d.ts +44 -46
- package/dist/schema/tenancy.js +46 -48
- package/dist/server/adapter.d.ts +58 -58
- package/dist/server/adapter.js +13 -14
- package/dist/server/commit.d.ts +60 -64
- package/dist/server/index.d.ts +9 -10
- package/dist/server/index.js +1 -1
- package/dist/server/readConfig.d.ts +70 -0
- package/dist/server/readConfig.js +8 -0
- package/dist/server/storageMode.d.ts +23 -0
- package/dist/server/storageMode.js +17 -0
- package/dist/source/adapter.d.ts +30 -25
- package/dist/source/adapter.js +10 -10
- package/dist/source/adapters/drizzle.d.ts +28 -23
- package/dist/source/adapters/drizzle.js +30 -25
- package/dist/source/adapters/kysely.d.ts +27 -25
- package/dist/source/adapters/kysely.js +24 -23
- package/dist/source/adapters/memory.d.ts +8 -7
- package/dist/source/adapters/memory.js +9 -8
- package/dist/source/adapters/prisma.d.ts +13 -12
- package/dist/source/adapters/prisma.js +22 -25
- package/dist/source/conformance.d.ts +18 -11
- package/dist/source/conformance.js +17 -11
- package/dist/source/connector.d.ts +31 -32
- package/dist/source/connector.js +28 -28
- package/dist/source/connectorProtocol.d.ts +160 -0
- package/dist/source/connectorProtocol.js +162 -0
- package/dist/source/contract.d.ts +26 -27
- package/dist/source/contract.js +28 -29
- package/dist/source/factory.d.ts +46 -58
- package/dist/source/factory.js +22 -27
- package/dist/source/index.d.ts +7 -9
- package/dist/source/index.js +12 -14
- package/dist/source/migrations.d.ts +9 -9
- package/dist/source/migrations.js +9 -9
- package/dist/source/next.d.ts +9 -10
- package/dist/source/next.js +6 -7
- package/dist/source/pushQueue.d.ts +69 -47
- package/dist/source/pushQueue.js +32 -28
- package/dist/source/signing.d.ts +46 -17
- package/dist/source/signing.js +28 -11
- package/dist/source/types.d.ts +121 -104
- package/dist/source/types.js +13 -14
- package/dist/stores/ObjectStore.d.ts +24 -12
- package/dist/stores/ObjectStore.js +38 -16
- package/dist/stores/ObjectStoreContract.d.ts +14 -15
- package/dist/stores/SyncActionStore.d.ts +7 -11
- package/dist/stores/SyncActionStore.js +13 -17
- package/dist/surface.d.ts +28 -21
- package/dist/surface.js +29 -20
- package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +36 -42
- package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +76 -76
- package/dist/sync/ConnectionManager.d.ts +39 -50
- package/dist/sync/ConnectionManager.js +55 -66
- package/dist/sync/NetworkProbe.d.ts +24 -29
- package/dist/sync/NetworkProbe.js +63 -69
- package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +42 -41
- package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +59 -54
- package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +43 -57
- package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +43 -51
- package/dist/sync/SyncWebSocket.d.ts +141 -166
- package/dist/sync/SyncWebSocket.js +191 -223
- package/dist/sync/awaitClaimGrant.d.ts +18 -18
- package/dist/sync/awaitClaimGrant.js +11 -11
- package/dist/sync/bootstrapApply.d.ts +34 -24
- package/dist/sync/bootstrapApply.js +27 -19
- package/dist/sync/commitFrames.d.ts +21 -20
- package/dist/sync/commitFrames.js +18 -18
- package/dist/sync/createClaimStream.d.ts +23 -22
- package/dist/sync/createClaimStream.js +105 -23
- package/dist/sync/createPresenceStream.d.ts +19 -18
- package/dist/sync/createPresenceStream.js +25 -26
- package/dist/sync/createSnapshot.d.ts +12 -14
- package/dist/sync/createSnapshot.js +20 -26
- package/dist/sync/credentialLifecycle.d.ts +104 -104
- package/dist/sync/credentialLifecycle.js +140 -147
- package/dist/sync/deltaPipeline.d.ts +36 -34
- package/dist/sync/deltaPipeline.js +64 -65
- package/dist/sync/groupChange.d.ts +63 -61
- package/dist/sync/groupChange.js +74 -78
- package/dist/sync/heartbeat.d.ts +34 -33
- package/dist/sync/heartbeat.js +31 -31
- package/dist/sync/participants.d.ts +19 -19
- package/dist/sync/persistedPrefix.d.ts +12 -0
- package/dist/sync/persistedPrefix.js +22 -0
- package/dist/sync/schemas.d.ts +3 -2
- package/dist/sync/schemas.js +14 -10
- package/dist/sync/syncCursor.d.ts +17 -21
- package/dist/sync/syncCursor.js +17 -21
- package/dist/sync/syncPlan.d.ts +28 -36
- package/dist/sync/syncPlan.js +18 -19
- package/dist/sync/syncPosition.d.ts +54 -49
- package/dist/sync/syncPosition.js +57 -52
- package/dist/sync/wsFrameHandlers.d.ts +35 -36
- package/dist/sync/wsFrameHandlers.js +63 -67
- package/dist/testing/fixtures/bootstrap.d.ts +12 -6
- package/dist/testing/fixtures/bootstrap.js +12 -6
- package/dist/testing/fixtures/deltas.d.ts +30 -33
- package/dist/testing/fixtures/deltas.js +30 -33
- package/dist/testing/fixtures/models.d.ts +11 -10
- package/dist/testing/fixtures/models.js +11 -10
- package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
- package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
- package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -15
- package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +12 -10
- package/dist/testing/helpers/wait.d.ts +13 -8
- package/dist/testing/helpers/wait.js +13 -8
- package/dist/testing/index.d.ts +5 -3
- package/dist/testing/index.js +3 -2
- package/dist/testing/mocks/FakeDatabase.d.ts +18 -0
- package/dist/testing/mocks/FakeDatabase.js +10 -0
- package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
- package/dist/testing/mocks/MockMutationExecutor.js +15 -14
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
- package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
- package/dist/testing/mocks/MockSyncContext.d.ts +20 -17
- package/dist/testing/mocks/MockSyncContext.js +15 -13
- package/dist/testing/mocks/MockSyncStore.d.ts +10 -10
- package/dist/testing/mocks/MockSyncStore.js +11 -11
- package/dist/testing/mocks/MockWebSocket.d.ts +28 -23
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +244 -181
- package/dist/transactions/TransactionQueue.js +929 -423
- package/dist/transactions/TransactionStore.d.ts +6 -4
- package/dist/transactions/TransactionStore.js +6 -4
- package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
- package/dist/transactions/UnconfirmedWrites.js +104 -0
- package/dist/transactions/coalesceRules.d.ts +41 -17
- package/dist/transactions/coalesceRules.js +40 -17
- package/dist/transactions/commitEnvelope.d.ts +132 -0
- package/dist/transactions/commitEnvelope.js +139 -0
- package/dist/transactions/commitOutboxStore.d.ts +32 -0
- package/dist/transactions/commitOutboxStore.js +26 -0
- package/dist/transactions/commitPayload.d.ts +63 -52
- package/dist/transactions/commitPayload.js +54 -57
- package/dist/transactions/deltaConfirmation.d.ts +20 -22
- package/dist/transactions/deltaConfirmation.js +37 -45
- package/dist/transactions/httpCommitEnvelope.d.ts +43 -0
- package/dist/transactions/httpCommitEnvelope.js +179 -0
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/replayValidation.d.ts +182 -0
- package/dist/transactions/replayValidation.js +156 -0
- package/dist/types/global.d.ts +46 -41
- package/dist/types/global.js +20 -19
- package/dist/types/index.d.ts +71 -77
- package/dist/types/index.js +22 -22
- package/dist/types/modelData.d.ts +6 -8
- package/dist/types/modelData.js +5 -7
- package/dist/types/participant.d.ts +10 -11
- package/dist/types/participant.js +6 -8
- package/dist/types/streams.d.ts +208 -195
- package/dist/types/streams.js +7 -7
- package/dist/utils/asyncIterator.d.ts +25 -32
- package/dist/utils/asyncIterator.js +25 -32
- package/dist/utils/duration.d.ts +12 -15
- package/dist/utils/duration.js +12 -15
- package/dist/utils/mobxSetup.d.ts +53 -0
- package/dist/utils/{mobx-setup.js → mobxSetup.js} +42 -98
- package/dist/webhooks/events.d.ts +21 -16
- package/dist/webhooks/events.js +10 -8
- package/dist/webhooks/index.d.ts +5 -7
- package/dist/webhooks/index.js +5 -7
- package/dist/wire/bootstrapReason.d.ts +9 -0
- package/dist/wire/bootstrapReason.js +8 -0
- package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
- package/dist/wire/delta.js +114 -0
- package/dist/wire/errorEnvelope.d.ts +30 -31
- package/dist/wire/errorEnvelope.js +34 -40
- package/dist/wire/frames.d.ts +315 -86
- package/dist/wire/frames.js +47 -33
- package/dist/wire/index.d.ts +18 -14
- package/dist/wire/index.js +32 -27
- package/dist/wire/listEnvelope.d.ts +16 -23
- package/dist/wire/listEnvelope.js +7 -6
- package/dist/wire/protocol.d.ts +25 -32
- package/dist/wire/protocol.js +25 -32
- package/dist/wire/protocolVersion.d.ts +44 -40
- package/dist/wire/protocolVersion.js +44 -40
- package/docs/api.md +10 -10
- package/docs/coordination.md +59 -0
- package/docs/mcp.md +1 -1
- package/package.json +17 -11
- package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
- package/dist/ai-sdk/coordination-context.d.ts +0 -52
- package/dist/core/query-utils.d.ts +0 -34
- package/dist/core/query-utils.js +0 -59
- package/dist/schema/sync-delta-row.js +0 -103
- package/dist/schema/sync-delta-wire.js +0 -102
- package/dist/server/read-config.d.ts +0 -67
- package/dist/server/read-config.js +0 -8
- package/dist/server/storage-mode.d.ts +0 -8
- package/dist/server/storage-mode.js +0 -28
- package/dist/source/connector-protocol.d.ts +0 -159
- package/dist/source/connector-protocol.js +0 -161
- package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
- package/dist/transactions/OptimisticEchoTracker.js +0 -104
- package/dist/transactions/mutation-error-handler.d.ts +0 -5
- package/dist/transactions/mutation-error-handler.js +0 -39
- package/dist/transactions/optimistic.d.ts +0 -24
- package/dist/transactions/optimistic.js +0 -45
- package/dist/transactions/persistedReplay.d.ts +0 -93
- package/dist/transactions/persistedReplay.js +0 -105
- package/dist/utils/mobx-setup.d.ts +0 -42
|
@@ -53,7 +53,7 @@ export function useUndoScope(schemaOrName, nameOrOptions, maybeOptions) {
|
|
|
53
53
|
useEffect(() => {
|
|
54
54
|
setTick(0);
|
|
55
55
|
}, [scope]);
|
|
56
|
-
// Re-render on
|
|
56
|
+
// Re-render on any stack change — including entries recorded from the local-
|
|
57
57
|
// mutation stream, which don't otherwise trigger a React update. Without this
|
|
58
58
|
// `canUndo`/`canRedo` go stale in every consumer that didn't itself call
|
|
59
59
|
// undo/redo (e.g. a keyboard handler whose Cmd+Z gate then never fires).
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (`relation.belongsTo()`, `field.string()`) — and the way modern libraries
|
|
7
|
-
* compose config (Better Auth's `plugins: [admin(), twoFactor()]`, shadcn's
|
|
8
|
-
* `cx(a, b)`) — instead of a raw disposition map:
|
|
2
|
+
* Authoring helpers for a model's `conflict` axis — the setting that decides
|
|
3
|
+
* what happens when two writers touch the same row. Instead of writing a raw
|
|
4
|
+
* disposition map, you compose small, named functions the way the rest of the
|
|
5
|
+
* schema DSL reads (`relation.belongsTo()`, `field.string()`):
|
|
9
6
|
*
|
|
10
7
|
* ```ts
|
|
11
8
|
* import { coordination, humansOverwrite, agentsReject } from '@abloatai/ablo/schema';
|
|
@@ -14,12 +11,11 @@
|
|
|
14
11
|
* // → { user: 'overwrite', agent: 'reject' } (a human's write wins, an agent's yields)
|
|
15
12
|
* ```
|
|
16
13
|
*
|
|
17
|
-
* Each helper is named for the
|
|
18
|
-
* `overwrite | reject | notify` vocabulary
|
|
19
|
-
* and returns a partial {@link ConflictAxis}. {@link coordination} merges
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* nicer authoring surface.
|
|
14
|
+
* Each helper is named for the disposition it applies — drawn from the same
|
|
15
|
+
* `overwrite | reject | notify` vocabulary the write guards use (`onStale`) —
|
|
16
|
+
* and returns a partial {@link ConflictAxis}. {@link coordination} merges the
|
|
17
|
+
* pieces, with later rules winning on key collisions. The result is plain,
|
|
18
|
+
* serializable data that the engine reads at commit time.
|
|
23
19
|
*/
|
|
24
20
|
import type { ConflictAxis } from '../policy/types.js';
|
|
25
21
|
/**
|
|
@@ -27,28 +23,28 @@ import type { ConflictAxis } from '../policy/types.js';
|
|
|
27
23
|
* disposition helper below. Compose with {@link coordination}.
|
|
28
24
|
*/
|
|
29
25
|
export type ConflictRule = ConflictAxis;
|
|
30
|
-
/** A human's conflicting write
|
|
26
|
+
/** A human's conflicting write wins and overwrites the other; it is never blocked. Among humans this gives last-write-wins. */
|
|
31
27
|
export declare const humansOverwrite: () => ConflictRule;
|
|
32
|
-
/** A human's conflicting write is
|
|
28
|
+
/** A human's conflicting write is rejected, yielding to a held claim or a stale snapshot. */
|
|
33
29
|
export declare const humansReject: () => ConflictRule;
|
|
34
|
-
/** A human's stale write
|
|
30
|
+
/** A human's stale write triggers a notification: it re-reads and re-applies rather than clobbering. */
|
|
35
31
|
export declare const humansNotify: () => ConflictRule;
|
|
36
|
-
/** An agent's conflicting write
|
|
32
|
+
/** An agent's conflicting write wins and overwrites the other (rarely what you want). */
|
|
37
33
|
export declare const agentsOverwrite: () => ConflictRule;
|
|
38
|
-
/** An agent's conflicting write is
|
|
34
|
+
/** An agent's conflicting write is rejected, yielding to a held claim or a stale snapshot. */
|
|
39
35
|
export declare const agentsReject: () => ConflictRule;
|
|
40
|
-
/** An agent's stale write
|
|
36
|
+
/** An agent's stale write triggers a notification: it re-reads and re-applies rather than clobbering. */
|
|
41
37
|
export declare const agentsNotify: () => ConflictRule;
|
|
42
|
-
/** A system
|
|
38
|
+
/** A system or automation write wins and overwrites the other. */
|
|
43
39
|
export declare const systemOverwrite: () => ConflictRule;
|
|
44
|
-
/** A system
|
|
40
|
+
/** A system or automation write is rejected. */
|
|
45
41
|
export declare const systemReject: () => ConflictRule;
|
|
46
|
-
/** A system
|
|
42
|
+
/** A system or automation stale write triggers a notification: it re-reads and re-applies. */
|
|
47
43
|
export declare const systemNotify: () => ConflictRule;
|
|
48
44
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
45
|
+
* Merges coordination rules into a single {@link ConflictAxis}. Later rules win
|
|
46
|
+
* on key collisions, and a committer kind you leave out falls through to the
|
|
47
|
+
* engine's default at commit time.
|
|
52
48
|
*
|
|
53
49
|
* ```ts
|
|
54
50
|
* coordination(humansOverwrite(), agentsReject()) // → { user: 'overwrite', agent: 'reject' }
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (`relation.belongsTo()`, `field.string()`) — and the way modern libraries
|
|
7
|
-
* compose config (Better Auth's `plugins: [admin(), twoFactor()]`, shadcn's
|
|
8
|
-
* `cx(a, b)`) — instead of a raw disposition map:
|
|
2
|
+
* Authoring helpers for a model's `conflict` axis — the setting that decides
|
|
3
|
+
* what happens when two writers touch the same row. Instead of writing a raw
|
|
4
|
+
* disposition map, you compose small, named functions the way the rest of the
|
|
5
|
+
* schema DSL reads (`relation.belongsTo()`, `field.string()`):
|
|
9
6
|
*
|
|
10
7
|
* ```ts
|
|
11
8
|
* import { coordination, humansOverwrite, agentsReject } from '@abloatai/ablo/schema';
|
|
@@ -14,38 +11,37 @@
|
|
|
14
11
|
* // → { user: 'overwrite', agent: 'reject' } (a human's write wins, an agent's yields)
|
|
15
12
|
* ```
|
|
16
13
|
*
|
|
17
|
-
* Each helper is named for the
|
|
18
|
-
* `overwrite | reject | notify` vocabulary
|
|
19
|
-
* and returns a partial {@link ConflictAxis}. {@link coordination} merges
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* nicer authoring surface.
|
|
14
|
+
* Each helper is named for the disposition it applies — drawn from the same
|
|
15
|
+
* `overwrite | reject | notify` vocabulary the write guards use (`onStale`) —
|
|
16
|
+
* and returns a partial {@link ConflictAxis}. {@link coordination} merges the
|
|
17
|
+
* pieces, with later rules winning on key collisions. The result is plain,
|
|
18
|
+
* serializable data that the engine reads at commit time.
|
|
23
19
|
*/
|
|
24
20
|
// ── Humans (user sessions) ──────────────────────────────────────────────
|
|
25
|
-
/** A human's conflicting write
|
|
21
|
+
/** A human's conflicting write wins and overwrites the other; it is never blocked. Among humans this gives last-write-wins. */
|
|
26
22
|
export const humansOverwrite = () => ({ user: 'overwrite' });
|
|
27
|
-
/** A human's conflicting write is
|
|
23
|
+
/** A human's conflicting write is rejected, yielding to a held claim or a stale snapshot. */
|
|
28
24
|
export const humansReject = () => ({ user: 'reject' });
|
|
29
|
-
/** A human's stale write
|
|
25
|
+
/** A human's stale write triggers a notification: it re-reads and re-applies rather than clobbering. */
|
|
30
26
|
export const humansNotify = () => ({ user: 'notify' });
|
|
31
27
|
// ── Agents (AI) ─────────────────────────────────────────────────────────
|
|
32
|
-
/** An agent's conflicting write
|
|
28
|
+
/** An agent's conflicting write wins and overwrites the other (rarely what you want). */
|
|
33
29
|
export const agentsOverwrite = () => ({ agent: 'overwrite' });
|
|
34
|
-
/** An agent's conflicting write is
|
|
30
|
+
/** An agent's conflicting write is rejected, yielding to a held claim or a stale snapshot. */
|
|
35
31
|
export const agentsReject = () => ({ agent: 'reject' });
|
|
36
|
-
/** An agent's stale write
|
|
32
|
+
/** An agent's stale write triggers a notification: it re-reads and re-applies rather than clobbering. */
|
|
37
33
|
export const agentsNotify = () => ({ agent: 'notify' });
|
|
38
34
|
// ── System / automation ─────────────────────────────────────────────────
|
|
39
|
-
/** A system
|
|
35
|
+
/** A system or automation write wins and overwrites the other. */
|
|
40
36
|
export const systemOverwrite = () => ({ system: 'overwrite' });
|
|
41
|
-
/** A system
|
|
37
|
+
/** A system or automation write is rejected. */
|
|
42
38
|
export const systemReject = () => ({ system: 'reject' });
|
|
43
|
-
/** A system
|
|
39
|
+
/** A system or automation stale write triggers a notification: it re-reads and re-applies. */
|
|
44
40
|
export const systemNotify = () => ({ system: 'notify' });
|
|
45
41
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
42
|
+
* Merges coordination rules into a single {@link ConflictAxis}. Later rules win
|
|
43
|
+
* on key collisions, and a committer kind you leave out falls through to the
|
|
44
|
+
* engine's default at commit time.
|
|
49
45
|
*
|
|
50
46
|
* ```ts
|
|
51
47
|
* coordination(humansOverwrite(), agentsReject()) // → { user: 'overwrite', agent: 'reject' }
|
package/dist/schema/ddl.d.ts
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Turns a schema definition into the ordered Postgres DDL that provisions and
|
|
3
|
+
* migrates its tables. A schema built with `defineSchema(...)` and serialized to
|
|
4
|
+
* {@link SchemaJSON} is the single source of truth, and this module lowers it to
|
|
5
|
+
* ordered SQL strings.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* generators, so the SQL — column types, RLS, enum checks — is identical no
|
|
9
|
-
* matter who runs it. There is no second type map.
|
|
7
|
+
* The same generators run wherever tables are created — in a hosted server
|
|
8
|
+
* applying them to the Postgres it manages, and in the `ablo migrate` CLI
|
|
9
|
+
* applying them to a customer's own Postgres — so the SQL, from column types to
|
|
10
|
+
* row-level security to enum checks, is identical no matter who runs it.
|
|
10
11
|
*
|
|
11
|
-
* Everything here is pure
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Everything here is pure: it returns strings and touches no database. The
|
|
13
|
+
* execution side — the transaction and advisory lock that actually run the
|
|
14
|
+
* statements — lives with each caller, because it is coupled to that caller's
|
|
15
|
+
* Postgres client and error types.
|
|
14
16
|
*
|
|
15
|
-
* -
|
|
16
|
-
* EXISTS
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* - {@link generateProvisionPlan} builds an additive, idempotent plan (CREATE
|
|
18
|
+
* and ADD … IF NOT EXISTS, plus row-level security) that never loses data —
|
|
19
|
+
* the "create my tables" primitive.
|
|
20
|
+
* - {@link generateMigrationPlan} is its destructive-aware counterpart, driven
|
|
21
|
+
* by a {@link diffSchema} step list: drops, renames, type casts, and
|
|
22
|
+
* backfills.
|
|
19
23
|
*/
|
|
20
24
|
import type { SchemaJSON, ModelJSON } from './serialize.js';
|
|
21
25
|
import type { MigrationStep, BackfillValue } from './diff.js';
|
|
@@ -23,30 +27,30 @@ export interface ProvisionPlan {
|
|
|
23
27
|
/** The Postgres schema the tables live in (`app_<id>` or `public`). */
|
|
24
28
|
readonly appSchema: string;
|
|
25
29
|
/** Ordered, idempotent DDL statements. Safe to run repeatedly. Executors run
|
|
26
|
-
* these together in
|
|
30
|
+
* these together in one transaction. */
|
|
27
31
|
readonly statements: readonly string[];
|
|
28
|
-
/** Post-commit,
|
|
29
|
-
* CONCURRENTLY`) — run
|
|
30
|
-
* transaction, best-effort. Keeps the lock-heavy
|
|
31
|
-
* main transaction so adding a foreign key never freezes a large, live
|
|
32
|
-
*
|
|
32
|
+
/** Post-commit, non-transactional DDL (`VALIDATE CONSTRAINT`, `CREATE INDEX
|
|
33
|
+
* CONCURRENTLY`) — run after {@link statements} commit, each outside any
|
|
34
|
+
* transaction, best-effort. Keeps the lock-heavy and scan-heavy work off the
|
|
35
|
+
* main transaction so adding a foreign key never freezes a large, live table.
|
|
36
|
+
* Optional: when absent, there is nothing to run. */
|
|
33
37
|
readonly concurrent?: readonly string[];
|
|
34
38
|
}
|
|
35
39
|
export interface ProvisionOptions {
|
|
36
40
|
/**
|
|
37
|
-
* Emit `DEFERRABLE INITIALLY DEFERRED`
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
41
|
+
* Emit `DEFERRABLE INITIALLY DEFERRED` foreign-key constraints for the
|
|
42
|
+
* belongsTo relations that opt in; see {@link foreignKeyStatements} for exactly
|
|
43
|
+
* which relations qualify. Off by default, so soft references keep out-of-order
|
|
44
|
+
* sync robust. Turn it on for a customer's own database, where a clean,
|
|
45
|
+
* navigable relational schema is wanted and the database starts empty, so a
|
|
46
|
+
* constraint has nothing to fail against.
|
|
43
47
|
*/
|
|
44
48
|
readonly foreignKeys?: boolean;
|
|
45
49
|
}
|
|
46
50
|
export interface MigrationPlan {
|
|
47
51
|
/** The app Postgres schema the DDL targets (`app_<id>` or `public`). */
|
|
48
52
|
readonly appSchema: string;
|
|
49
|
-
/** Ordered DDL statements (expand → contract). Run in
|
|
53
|
+
/** Ordered DDL statements (expand → contract). Run in one transaction. */
|
|
50
54
|
readonly statements: readonly string[];
|
|
51
55
|
/** Post-commit, non-transactional DDL — see {@link ProvisionPlan.concurrent}. */
|
|
52
56
|
readonly concurrent?: readonly string[];
|
|
@@ -55,10 +59,10 @@ export interface MigrationPlan {
|
|
|
55
59
|
export declare function appSchemaName(organizationId: string): string;
|
|
56
60
|
export declare function camelToSnake(identifier: string): string;
|
|
57
61
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
+
* Converts snake_case to camelCase — the inverse of {@link camelToSnake}. This
|
|
63
|
+
* is the read-side translation: a column read back from a customer's own
|
|
64
|
+
* database maps to the same JavaScript field the SDK wrote, so
|
|
65
|
+
* `camelToSnake('operatorId') === 'operator_id'` and
|
|
62
66
|
* `snakeToCamel('operator_id') === 'operatorId'` round-trip.
|
|
63
67
|
*/
|
|
64
68
|
export declare function snakeToCamel(identifier: string): string;
|
|
@@ -66,13 +70,13 @@ export declare function snakeToCamel(identifier: string): string;
|
|
|
66
70
|
export declare function q(identifier: string): string;
|
|
67
71
|
export declare function sqlType(fieldType: ModelJSON['fields'][string]['type']): string;
|
|
68
72
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
73
|
+
* Builds the additive, idempotent provisioning plan for an app. Pure — it does
|
|
74
|
+
* not touch a database.
|
|
71
75
|
*
|
|
72
|
-
* `targetSchema` is where the tables live:
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* skipped
|
|
76
|
+
* `targetSchema` is where the tables live: a per-app Postgres schema such as
|
|
77
|
+
* `app_<id>`, or `public` when the database itself is the isolation boundary
|
|
78
|
+
* (for example a customer's own database). For `public` the `CREATE SCHEMA`
|
|
79
|
+
* statement is skipped, since it always exists.
|
|
76
80
|
*/
|
|
77
81
|
export declare function generateProvisionPlan(schema: SchemaJSON, targetSchema: string, opts?: ProvisionOptions): ProvisionPlan;
|
|
78
82
|
/**
|
|
@@ -87,7 +91,7 @@ export declare function generateMigrationPlan(steps: readonly MigrationStep[], o
|
|
|
87
91
|
/** Constant seed values that let a required-field add / made-required step
|
|
88
92
|
* set NOT NULL on a non-empty table. Keyed by (model, field). */
|
|
89
93
|
readonly backfills?: readonly BackfillValue[];
|
|
90
|
-
/** Emit
|
|
91
|
-
*
|
|
94
|
+
/** Emit deferrable foreign-key constraints for the relations that opt in.
|
|
95
|
+
* Off by default — see {@link ProvisionOptions.foreignKeys}. */
|
|
92
96
|
readonly foreignKeys?: boolean;
|
|
93
97
|
}): MigrationPlan;
|
package/dist/schema/ddl.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Turns a schema definition into the ordered Postgres DDL that provisions and
|
|
3
|
+
* migrates its tables. A schema built with `defineSchema(...)` and serialized to
|
|
4
|
+
* {@link SchemaJSON} is the single source of truth, and this module lowers it to
|
|
5
|
+
* ordered SQL strings.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* generators, so the SQL — column types, RLS, enum checks — is identical no
|
|
9
|
-
* matter who runs it. There is no second type map.
|
|
7
|
+
* The same generators run wherever tables are created — in a hosted server
|
|
8
|
+
* applying them to the Postgres it manages, and in the `ablo migrate` CLI
|
|
9
|
+
* applying them to a customer's own Postgres — so the SQL, from column types to
|
|
10
|
+
* row-level security to enum checks, is identical no matter who runs it.
|
|
10
11
|
*
|
|
11
|
-
* Everything here is pure
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Everything here is pure: it returns strings and touches no database. The
|
|
13
|
+
* execution side — the transaction and advisory lock that actually run the
|
|
14
|
+
* statements — lives with each caller, because it is coupled to that caller's
|
|
15
|
+
* Postgres client and error types.
|
|
14
16
|
*
|
|
15
|
-
* -
|
|
16
|
-
* EXISTS
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* - {@link generateProvisionPlan} builds an additive, idempotent plan (CREATE
|
|
18
|
+
* and ADD … IF NOT EXISTS, plus row-level security) that never loses data —
|
|
19
|
+
* the "create my tables" primitive.
|
|
20
|
+
* - {@link generateMigrationPlan} is its destructive-aware counterpart, driven
|
|
21
|
+
* by a {@link diffSchema} step list: drops, renames, type casts, and
|
|
22
|
+
* backfills.
|
|
19
23
|
*/
|
|
20
24
|
import { AbloValidationError } from '../errors.js';
|
|
21
25
|
import { resolveTenancy, tenancyColumn } from './tenancy.js';
|
|
@@ -33,10 +37,10 @@ export function camelToSnake(identifier) {
|
|
|
33
37
|
return identifier.replace(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* Converts snake_case to camelCase — the inverse of {@link camelToSnake}. This
|
|
41
|
+
* is the read-side translation: a column read back from a customer's own
|
|
42
|
+
* database maps to the same JavaScript field the SDK wrote, so
|
|
43
|
+
* `camelToSnake('operatorId') === 'operator_id'` and
|
|
40
44
|
* `snakeToCamel('operator_id') === 'operatorId'` round-trip.
|
|
41
45
|
*/
|
|
42
46
|
export function snakeToCamel(identifier) {
|
|
@@ -70,7 +74,7 @@ const BASE_COLUMNS = new Set(['id', 'organization_id', 'created_by', 'created_at
|
|
|
70
74
|
/**
|
|
71
75
|
* A Postgres-identifier-safe constraint name ≤63 bytes. When the natural
|
|
72
76
|
* `<table>_<col>_<suffix>` exceeds the limit, fall back to a deterministic
|
|
73
|
-
* hashed form so the name stays stable
|
|
77
|
+
* hashed form so the name stays stable and matches what Postgres actually stores
|
|
74
78
|
* — a silently-truncated name would never match the DO-block existence guard,
|
|
75
79
|
* breaking idempotency (re-adds every push) and risking prefix collisions.
|
|
76
80
|
*/
|
|
@@ -86,45 +90,47 @@ function constraintName(table, col, suffix) {
|
|
|
86
90
|
return `${prefix}_${hash}_${suffix}`;
|
|
87
91
|
}
|
|
88
92
|
/**
|
|
89
|
-
*
|
|
93
|
+
* Builds the foreign-key constraints for a model's belongsTo relations that opt
|
|
94
|
+
* in by setting `{ fk: true }`.
|
|
90
95
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* or at an absent row and break sync.
|
|
96
|
+
* The `fk` marker is deliberately separate from `parent`: `parent` controls
|
|
97
|
+
* sync-group fan-out and visibility, while `fk` requests physical referential
|
|
98
|
+
* integrity in the database. A relation sets `fk` only when its target lives in
|
|
99
|
+
* the same database, is written in the same commit, and is a strong, contained
|
|
100
|
+
* entity. Soft references — provenance or template pointers such as
|
|
101
|
+
* `sourceSlideId` or `templateId` — stay plain columns; a hard foreign key there
|
|
102
|
+
* would reject a write that points across scopes or at an absent row and break
|
|
103
|
+
* sync.
|
|
100
104
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* production
|
|
104
|
-
* lock)
|
|
105
|
-
* CONSTRAINT`,
|
|
106
|
-
* (`CREATE INDEX CONCURRENTLY`) are returned
|
|
107
|
-
* ForeignKeyDdl.concurrent}, run after commit, outside any transaction, and
|
|
108
|
-
* best-effort: if existing data violates a freshly
|
|
109
|
-
* skipped (logged, never fatal), the constraint still enforces
|
|
110
|
-
* and nothing is destroyed.
|
|
105
|
+
* On a live, populated table a plain `ADD CONSTRAINT` takes a heavy lock and
|
|
106
|
+
* scans the whole child table, which would freeze writes on a customer's
|
|
107
|
+
* production database. To avoid that, the constraint is added `NOT VALID`
|
|
108
|
+
* (instant, no scan, brief lock) inside the transaction, and the existing-row
|
|
109
|
+
* check (`VALIDATE CONSTRAINT`, which allows concurrent writes) plus the child
|
|
110
|
+
* index (`CREATE INDEX CONCURRENTLY`) are returned separately in {@link
|
|
111
|
+
* ForeignKeyDdl.concurrent}, to run after commit, outside any transaction, and
|
|
112
|
+
* best-effort: if existing data violates a freshly added constraint the
|
|
113
|
+
* validation is skipped (logged, never fatal), the constraint still enforces
|
|
114
|
+
* every new write, and nothing is destroyed.
|
|
111
115
|
*
|
|
112
|
-
* The
|
|
113
|
-
*
|
|
114
|
-
* would change data
|
|
115
|
-
* until re-bootstrap — and would
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
116
|
+
* The constraint is a `DEFERRABLE INITIALLY DEFERRED` integrity guard with `ON
|
|
117
|
+
* DELETE NO ACTION`; it never mutates a child row itself. A `SET NULL` or
|
|
118
|
+
* `CASCADE` action would change data in the database with no matching
|
|
119
|
+
* sync_delta — invisible to other clients until they re-bootstrap — and would
|
|
120
|
+
* override the application layer's own onDelete handling. The application layer
|
|
121
|
+
* owns deletes and nullification and emits the deltas; the deferred check only
|
|
122
|
+
* verifies, at commit time (so a same-batch child-before-parent write and the
|
|
123
|
+
* application's own cascade both pass), that integrity holds, failing loudly
|
|
124
|
+
* only when a dangling reference is left behind.
|
|
120
125
|
*
|
|
121
|
-
*
|
|
122
|
-
* carries the wrong delete action (a hand-added or
|
|
123
|
-
* recreated
|
|
124
|
-
*
|
|
126
|
+
* Emission is idempotent and authoritative: a same-named constraint that is not
|
|
127
|
+
* deferrable or carries the wrong delete action (a hand-added or older foreign
|
|
128
|
+
* key) is dropped and recreated, while an already-correct one is left untouched
|
|
129
|
+
* with no revalidation cost. It runs in a final pass, after every referenced
|
|
130
|
+
* table exists.
|
|
125
131
|
*
|
|
126
|
-
* The
|
|
127
|
-
* (`fieldMeta.column ?? camelToSnake(field)`), not from `rel.foreignKeyColumn
|
|
132
|
+
* The foreign-key column is resolved the same way the table loop names columns
|
|
133
|
+
* (`fieldMeta.column ?? camelToSnake(field)`), not from `rel.foreignKeyColumn`:
|
|
128
134
|
* the table loop ignores relation casing, so trusting `foreignKeyColumn` would
|
|
129
135
|
* mismatch the real column whenever `casing` is unset.
|
|
130
136
|
*/
|
|
@@ -143,7 +149,7 @@ function foreignKeyStatements(table, model, models, qs) {
|
|
|
143
149
|
const concurrent = [];
|
|
144
150
|
for (const rel of Object.values(model.relations)) {
|
|
145
151
|
if (rel.type !== 'belongsTo')
|
|
146
|
-
continue; // only relations whose FK column lives on
|
|
152
|
+
continue; // only relations whose FK column lives on this table
|
|
147
153
|
if (rel.options?.fk !== true)
|
|
148
154
|
continue; // explicit `fk` marker — decoupled from `parent` (visibility)
|
|
149
155
|
const targetModel = models[rel.target];
|
|
@@ -171,7 +177,7 @@ function foreignKeyStatements(table, model, models, qs) {
|
|
|
171
177
|
`REFERENCES ${targetQt} (${q('id')}) ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED NOT VALID;\n` +
|
|
172
178
|
` END IF;\nEND $$;`);
|
|
173
179
|
// Post-commit, non-blocking: validate existing rows (SHARE UPDATE EXCLUSIVE,
|
|
174
|
-
// allows concurrent writes) then index the child column (Postgres does
|
|
180
|
+
// allows concurrent writes) then index the child column (Postgres does not
|
|
175
181
|
// auto-index the referencing column → parent deletes would seq-scan it).
|
|
176
182
|
concurrent.push(`ALTER TABLE ${qt} VALIDATE CONSTRAINT ${q(cname)};`);
|
|
177
183
|
concurrent.push(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${q(iname)} ON ${qt} (${q(col)});`);
|
|
@@ -180,13 +186,13 @@ function foreignKeyStatements(table, model, models, qs) {
|
|
|
180
186
|
}
|
|
181
187
|
// ── Provisioning (additive, idempotent) ─────────────────────────────────────
|
|
182
188
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
189
|
+
* Builds the additive, idempotent provisioning plan for an app. Pure — it does
|
|
190
|
+
* not touch a database.
|
|
185
191
|
*
|
|
186
|
-
* `targetSchema` is where the tables live:
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* skipped
|
|
192
|
+
* `targetSchema` is where the tables live: a per-app Postgres schema such as
|
|
193
|
+
* `app_<id>`, or `public` when the database itself is the isolation boundary
|
|
194
|
+
* (for example a customer's own database). For `public` the `CREATE SCHEMA`
|
|
195
|
+
* statement is skipped, since it always exists.
|
|
190
196
|
*/
|
|
191
197
|
export function generateProvisionPlan(schema, targetSchema, opts = {}) {
|
|
192
198
|
const appSchema = targetSchema;
|
|
@@ -194,10 +200,11 @@ export function generateProvisionPlan(schema, targetSchema, opts = {}) {
|
|
|
194
200
|
const statements = appSchema === 'public' ? [] : [`CREATE SCHEMA IF NOT EXISTS ${qs};`];
|
|
195
201
|
const concurrent = [];
|
|
196
202
|
for (const [key, model] of Object.entries(schema.models)) {
|
|
197
|
-
// Control-plane models (
|
|
198
|
-
// emitted into a tenant database — only `tenant`-plane
|
|
199
|
-
//
|
|
200
|
-
//
|
|
203
|
+
// Control-plane models (the engine's own sync log, attribution, and audit
|
|
204
|
+
// tables) are never emitted into a tenant database — only `tenant`-plane
|
|
205
|
+
// models are. A model with no declared plane defaults to `tenant`. This
|
|
206
|
+
// declared boundary is what makes the set of tables a customer's own
|
|
207
|
+
// database receives derivable instead of hand-coded.
|
|
201
208
|
if ((model.plane ?? 'tenant') === 'control')
|
|
202
209
|
continue;
|
|
203
210
|
// Default the physical table to the model key when `tableName` is omitted —
|
|
@@ -316,7 +323,7 @@ export function generateMigrationPlan(steps, opts) {
|
|
|
316
323
|
const statements = [];
|
|
317
324
|
const concurrent = [];
|
|
318
325
|
// The app schema must exist before any statement targets it. On a fresh
|
|
319
|
-
// org's
|
|
326
|
+
// org's first push (`prev = null`) the migration plan is the provisioning —
|
|
320
327
|
// `app_<orgId>` has never been created, and skipping this line made every
|
|
321
328
|
// first push die with `3F000 invalid_schema_name` at statement 0. Idempotent
|
|
322
329
|
// (`IF NOT EXISTS`), so emitting it on every later migration is free.
|
|
@@ -464,8 +471,8 @@ export function generateMigrationPlan(steps, opts) {
|
|
|
464
471
|
}
|
|
465
472
|
}
|
|
466
473
|
}
|
|
467
|
-
// Foreign keys (opt-in). Reconcile against the
|
|
468
|
-
// create_model steps: a parent edge
|
|
474
|
+
// Foreign keys (opt-in). Reconcile against the full `next` schema, not just
|
|
475
|
+
// create_model steps: a parent edge added to an existing model surfaces only as
|
|
469
476
|
// an add_field (relation changes aren't diffed), so a create_model-only pass
|
|
470
477
|
// would never materialize its FK. The DO-block is authoritative + idempotent
|
|
471
478
|
// (a no-op when the constraint is already correct), so emitting the full set
|
package/dist/schema/ddlLock.d.ts
CHANGED
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Lock settings for schema-change (DDL) statements. When a schema push alters a
|
|
3
|
+
* table, these values control how quickly the change gives up on a contended
|
|
4
|
+
* lock and how many times it retries. The command-line `ablo migrate` and the
|
|
5
|
+
* host that applies a schema push both resolve their lock behavior through this
|
|
6
|
+
* module, so tuning the environment variables below changes both paths the same
|
|
7
|
+
* way.
|
|
5
8
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
9
|
+
* The defaults follow the standard safe-migration recipe: a short `lock_timeout`
|
|
10
|
+
* so a blocked `ALTER` aborts quickly instead of parking an `ACCESS EXCLUSIVE`
|
|
11
|
+
* lock request at the head of the queue — which would freeze every other query
|
|
12
|
+
* on that table behind it — paired with a bounded retry-and-backoff on the
|
|
13
|
+
* resulting timeout.
|
|
8
14
|
*
|
|
9
|
-
* The
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* The settings themselves are the battle-tested ones every mature migration
|
|
15
|
-
* tool uses (GitLab `with_lock_retries`, Strong Migrations, Doctolib
|
|
16
|
-
* `safe-pg-migrations`): a LOW `lock_timeout` so a blocked ALTER aborts fast
|
|
17
|
-
* instead of parking an ACCESS EXCLUSIVE request at the head of the lock
|
|
18
|
-
* queue (which would freeze every query on that table behind it), plus a
|
|
19
|
-
* bounded retry-with-backoff on the `55P03` abort.
|
|
20
|
-
*
|
|
21
|
-
* Env knobs (read at CALL time, not import time, so tests and long-lived
|
|
22
|
-
* processes see updates): `ABLO_SCHEMA_LOCK_TIMEOUT` / `ABLO_SCHEMA_LOCK_ATTEMPTS`
|
|
23
|
-
* — the older `ABLO_DDL_*` names are still honored so existing setups don't
|
|
24
|
-
* break.
|
|
15
|
+
* The environment variables are read each time a resolver is called, not once
|
|
16
|
+
* at import, so a long-running process or a test that changes them sees the
|
|
17
|
+
* update: `ABLO_SCHEMA_LOCK_TIMEOUT` and `ABLO_SCHEMA_LOCK_ATTEMPTS`. The older
|
|
18
|
+
* `ABLO_DDL_*` names are also accepted.
|
|
25
19
|
*/
|
|
26
|
-
/** Postgres SQLSTATE `
|
|
27
|
-
*
|
|
20
|
+
/** The Postgres SQLSTATE `55P03` (`lock_not_available`), raised when a statement
|
|
21
|
+
* gives up waiting for a lock after `lock_timeout`. This is the one DDL failure
|
|
22
|
+
* worth retrying; any other error is a genuine problem. */
|
|
28
23
|
export declare const PG_LOCK_NOT_AVAILABLE = "55P03";
|
|
29
|
-
/** The
|
|
24
|
+
/** The subset of environment variables the resolvers in this module read. It is
|
|
25
|
+
* passed in explicitly so tests can supply their own values. */
|
|
30
26
|
export type DdlLockEnv = Readonly<Record<string, string | undefined>>;
|
|
31
27
|
/** `lock_timeout` for the DDL transaction (a Postgres duration string). */
|
|
32
28
|
export declare function resolveDdlLockTimeout(env?: DdlLockEnv): string;
|