@abloatai/ablo 0.34.1 → 0.36.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 -1
- package/CHANGELOG.md +758 -5
- package/README.md +56 -502
- package/bin/ablo.cjs +39 -0
- package/dist/BaseSyncedStore.d.ts +176 -48
- package/dist/BaseSyncedStore.js +346 -214
- package/dist/Database.d.ts +17 -44
- package/dist/Database.js +96 -79
- package/dist/InstanceCache.d.ts +31 -6
- package/dist/InstanceCache.js +65 -30
- package/dist/LazyReferenceCollection.d.ts +3 -3
- package/dist/LazyReferenceCollection.js +4 -4
- package/dist/Model.d.ts +23 -13
- package/dist/Model.js +27 -17
- package/dist/ModelRegistry.d.ts +8 -4
- package/dist/ModelRegistry.js +20 -18
- package/dist/NetworkMonitor.d.ts +3 -1
- package/dist/NetworkMonitor.js +7 -5
- package/dist/{SyncEngineContext.d.ts → RuntimeContext.d.ts} +20 -13
- package/dist/{SyncEngineContext.js → RuntimeContext.js} +11 -12
- package/dist/SyncClient.d.ts +47 -47
- package/dist/SyncClient.js +215 -156
- package/dist/ai-sdk/coordinatedTool.d.ts +2 -2
- package/dist/ai-sdk/coordinatedTool.js +1 -1
- package/dist/ai-sdk/coordinationContext.d.ts +2 -2
- package/dist/ai-sdk/coordinationContext.js +1 -1
- package/dist/ai-sdk/wrap.d.ts +3 -3
- package/dist/ai-sdk/wrap.js +2 -2
- package/dist/auth/index.d.ts +1 -156
- package/dist/auth/index.js +8 -301
- package/dist/client/Ablo.d.ts +42 -287
- package/dist/client/Ablo.js +129 -963
- package/dist/client/abloClient.d.ts +309 -0
- package/dist/client/abloClient.js +13 -0
- package/dist/client/clientPrelude.d.ts +52 -0
- package/dist/client/clientPrelude.js +60 -0
- package/dist/client/consoleLogger.d.ts +2 -2
- package/dist/client/coreClient.d.ts +60 -0
- package/dist/client/coreClient.js +118 -0
- package/dist/client/createInternalComponents.d.ts +8 -4
- package/dist/client/createInternalComponents.js +17 -10
- package/dist/client/createModelProxy.d.ts +98 -373
- package/dist/client/createModelProxy.js +233 -139
- package/dist/client/humans.d.ts +69 -0
- package/dist/client/humans.js +78 -0
- package/dist/client/modelRegistration.d.ts +1 -1
- package/dist/client/modelRegistration.js +9 -9
- package/dist/client/options.d.ts +73 -17
- package/dist/client/reactiveEngine.d.ts +53 -0
- package/dist/client/reactiveEngine.js +688 -0
- package/dist/client/resourceTypes.d.ts +9 -250
- package/dist/client/resourceTypes.js +8 -5
- package/dist/client/schemaConfig.d.ts +4 -4
- package/dist/client/schemaConfig.js +6 -2
- package/dist/client/storeCluster.d.ts +47 -0
- package/dist/client/storeCluster.js +118 -0
- package/dist/client/storeLifecycle.d.ts +61 -0
- package/dist/client/storeLifecycle.js +231 -0
- package/dist/client/validateAbloOptions.d.ts +3 -2
- package/dist/client/validateAbloOptions.js +1 -1
- package/dist/client/wsMutationExecutor.d.ts +3 -3
- package/dist/client/wsMutationExecutor.js +3 -3
- package/dist/context.d.ts +22 -9
- package/dist/context.js +33 -9
- package/dist/coordination/ClaimLog.d.ts +26 -0
- package/dist/coordination/ClaimLog.js +32 -0
- package/dist/coordination/index.d.ts +1 -15
- package/dist/coordination/index.js +8 -31
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +2 -2
- package/dist/docs/catalog.d.ts +72 -0
- package/dist/docs/catalog.js +230 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +10 -0
- package/dist/environment.d.ts +1 -40
- package/dist/environment.js +8 -37
- package/dist/index.d.ts +44 -36
- package/dist/index.js +30 -22
- package/dist/interfaces/index.d.ts +44 -134
- package/dist/keys/index.d.ts +1 -77
- package/dist/keys/index.js +8 -190
- package/dist/mutators/{RecordingTransaction.d.ts → RecordingMutation.d.ts} +4 -4
- package/dist/mutators/{RecordingTransaction.js → RecordingMutation.js} +2 -2
- package/dist/mutators/Transaction.d.ts +1 -1
- package/dist/mutators/Transaction.js +1 -1
- package/dist/mutators/UndoManager.d.ts +6 -6
- package/dist/mutators/UndoManager.js +5 -5
- package/dist/mutators/defineMutators.d.ts +3 -3
- package/dist/mutators/defineMutators.js +1 -1
- package/dist/mutators/inverseOp.js +2 -2
- package/dist/mutators/mutateActions.d.ts +3 -3
- package/dist/mutators/mutateActions.js +1 -1
- package/dist/mutators/readerActions.d.ts +1 -1
- package/dist/mutators/undoApply.d.ts +1 -1
- package/dist/mutators/undoApply.js +1 -1
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +1 -1
- package/dist/query/client.d.ts +5 -2
- package/dist/query/client.js +10 -9
- package/dist/query/types.d.ts +6 -41
- package/dist/query/types.js +2 -2
- package/dist/react/AbloProvider.d.ts +18 -8
- package/dist/react/AbloProvider.js +10 -9
- package/dist/react/context.d.ts +3 -3
- package/dist/react/context.js +1 -1
- package/dist/react/createAbloReact.d.ts +56 -0
- package/dist/react/createAbloReact.js +51 -0
- package/dist/react/index.d.ts +6 -5
- package/dist/react/index.js +6 -3
- package/dist/react/internalContext.d.ts +1 -1
- package/dist/react/useAblo.d.ts +12 -5
- package/dist/react/useAblo.js +26 -8
- package/dist/react/useCurrentUserId.js +1 -1
- package/dist/react/useErrorListener.js +1 -1
- package/dist/react/useMutationFailureListener.d.ts +2 -2
- package/dist/react/useMutationFailureListener.js +1 -1
- package/dist/react/useMutators.d.ts +3 -3
- package/dist/react/useMutators.js +3 -3
- package/dist/react/useUndoScope.d.ts +5 -5
- package/dist/react/useUndoScope.js +1 -1
- package/dist/schema/coordination.d.ts +69 -10
- package/dist/schema/coordination.js +90 -9
- package/dist/schema/ddl.js +2 -2
- package/dist/schema/diff.d.ts +1 -1
- package/dist/schema/generate.js +1 -1
- package/dist/schema/index.d.ts +11 -10
- package/dist/schema/index.js +22 -18
- package/dist/schema/queries.d.ts +27 -27
- package/dist/schema/queries.js +23 -23
- package/dist/schema/select.d.ts +3 -3
- package/dist/schema/select.js +6 -3
- package/dist/schema/serialize.d.ts +15 -6
- package/dist/schema/serialize.js +20 -3
- package/dist/schema/sugar.d.ts +6 -7
- package/dist/schema/sugar.js +9 -12
- package/dist/schema/syncDeltaRow.d.ts +4 -152
- package/dist/schema/syncDeltaRow.js +4 -105
- package/dist/server/adapter.d.ts +18 -1
- package/dist/server/commit.d.ts +10 -16
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/readConfig.d.ts +1 -1
- package/dist/source/adapter.d.ts +7 -5
- package/dist/source/adapter.js +7 -5
- package/dist/source/adapters/drizzle.d.ts +1 -1
- package/dist/source/adapters/drizzle.js +2 -2
- package/dist/source/adapters/kysely.d.ts +1 -1
- package/dist/source/adapters/kysely.js +1 -1
- package/dist/source/adapters/kyselyMutationCore.d.ts +1 -1
- package/dist/source/adapters/kyselyMutationCore.js +2 -2
- package/dist/source/adapters/memory.js +1 -1
- package/dist/source/adapters/prisma.d.ts +8 -3
- package/dist/source/adapters/prisma.js +1 -1
- package/dist/source/connector.js +1 -1
- package/dist/source/connectorProtocol.d.ts +2 -8
- package/dist/source/connectorProtocol.js +3 -2
- package/dist/source/contract.d.ts +29 -17
- package/dist/source/contract.js +27 -22
- package/dist/source/factory.d.ts +1 -1
- package/dist/source/idempotency.js +2 -2
- package/dist/source/index.d.ts +1 -0
- package/dist/source/index.js +3 -0
- package/dist/source/next.d.ts +1 -1
- package/dist/source/signing.d.ts +9 -2
- package/dist/source/signing.js +4 -1
- package/dist/source/types.d.ts +6 -4
- package/dist/source/types.js +1 -1
- package/dist/{core/storeContract.d.ts → storeContract.d.ts} +6 -6
- package/dist/{core → stores}/DatabaseManager.d.ts +3 -1
- package/dist/{core → stores}/DatabaseManager.js +14 -13
- package/dist/stores/ObjectStore.d.ts +1 -1
- package/dist/{core → stores}/StoreManager.d.ts +9 -26
- package/dist/{core → stores}/StoreManager.js +29 -77
- package/dist/stores/SyncActionStore.d.ts +4 -2
- package/dist/stores/SyncActionStore.js +11 -17
- package/dist/stores/syncAction.d.ts +26 -0
- package/dist/stores/syncAction.js +16 -0
- package/dist/surface.d.ts +3 -3
- package/dist/surface.js +6 -4
- package/dist/sync/BootstrapFetcher.d.ts +127 -6
- package/dist/sync/BootstrapFetcher.js +511 -83
- package/dist/sync/ConnectionManager.d.ts +6 -198
- package/dist/sync/ConnectionManager.js +6 -677
- package/dist/sync/OnDemandLoader.d.ts +5 -2
- package/dist/sync/OnDemandLoader.js +61 -21
- package/dist/sync/SubscriptionManager.d.ts +13 -2
- package/dist/sync/SubscriptionManager.js +23 -5
- package/dist/sync/SyncWebSocket.d.ts +27 -510
- package/dist/sync/SyncWebSocket.js +76 -954
- package/dist/sync/awaitClaimGrant.d.ts +4 -44
- package/dist/sync/awaitClaimGrant.js +4 -109
- package/dist/sync/bootstrapApply.d.ts +3 -0
- package/dist/sync/bootstrapApply.js +2 -2
- package/dist/sync/commitFrames.d.ts +6 -40
- package/dist/sync/commitFrames.js +6 -97
- package/dist/sync/contextPorts.d.ts +18 -0
- package/dist/sync/contextPorts.js +31 -0
- package/dist/sync/createClaimStream.d.ts +5 -49
- package/dist/sync/createClaimStream.js +5 -469
- package/dist/sync/createPresenceStream.d.ts +26 -4
- package/dist/sync/createPresenceStream.js +28 -20
- package/dist/sync/createSnapshot.d.ts +2 -2
- package/dist/sync/createSnapshot.js +1 -1
- package/dist/sync/credentialLifecycle.d.ts +5 -173
- package/dist/sync/credentialLifecycle.js +5 -320
- package/dist/sync/deltaPipeline.d.ts +13 -12
- package/dist/sync/deltaPipeline.js +21 -4
- package/dist/sync/groupChange.d.ts +3 -0
- package/dist/sync/groupChange.js +16 -14
- package/dist/sync/participants.d.ts +24 -6
- package/dist/sync/participants.js +32 -23
- package/dist/sync/schemaDrift.d.ts +55 -0
- package/dist/sync/schemaDrift.js +53 -0
- package/dist/sync/schemas.d.ts +23 -33
- package/dist/sync/schemas.js +29 -20
- package/dist/sync/syncPlan.d.ts +3 -3
- package/dist/sync/wsFrameHandlers.d.ts +6 -114
- package/dist/sync/wsFrameHandlers.js +6 -392
- package/dist/syncLog/contract.d.ts +20 -0
- package/dist/syncLog/contract.js +19 -0
- package/dist/syncLog/index.d.ts +1 -0
- package/dist/syncLog/index.js +1 -0
- package/dist/transaction/ablo.d.ts +88 -0
- package/dist/transaction/ablo.js +33 -0
- package/dist/{client/auth.d.ts → transaction/auth/apiKey.d.ts} +43 -8
- package/dist/{client/auth.js → transaction/auth/apiKey.js} +19 -0
- package/dist/transaction/auth/bootstrapScope.d.ts +15 -0
- package/dist/transaction/auth/bootstrapScope.js +1 -0
- package/dist/transaction/auth/capability.d.ts +212 -0
- package/dist/transaction/auth/capability.js +224 -0
- package/dist/{auth → transaction/auth}/credentialSource.d.ts +8 -1
- package/dist/{client → transaction/auth}/identity.d.ts +8 -7
- package/dist/{client → transaction/auth}/identity.js +1 -1
- package/dist/transaction/auth/index.d.ts +162 -0
- package/dist/transaction/auth/index.js +304 -0
- package/dist/{auth → transaction/auth}/schemas.d.ts +1 -1
- package/dist/{auth → transaction/auth}/schemas.js +13 -13
- package/dist/{client → transaction/auth}/sessionMint.d.ts +8 -8
- package/dist/{client → transaction/auth}/sessionMint.js +4 -7
- package/dist/transaction/coordination/awaitClaimGrant.d.ts +56 -0
- package/dist/transaction/coordination/awaitClaimGrant.js +124 -0
- package/dist/{client → transaction/coordination}/claimHeartbeatLoop.d.ts +34 -0
- package/dist/{client → transaction/coordination}/claimHeartbeatLoop.js +20 -0
- package/dist/transaction/coordination/claimMeta.d.ts +49 -0
- package/dist/transaction/coordination/claimMeta.js +52 -0
- package/dist/transaction/coordination/createClaimStream.d.ts +64 -0
- package/dist/transaction/coordination/createClaimStream.js +475 -0
- package/dist/transaction/coordination/events.d.ts +74 -0
- package/dist/transaction/coordination/events.js +7 -0
- package/dist/transaction/coordination/index.d.ts +19 -0
- package/dist/transaction/coordination/index.js +45 -0
- package/dist/transaction/coordination/locator.d.ts +104 -0
- package/dist/transaction/coordination/locator.js +102 -0
- package/dist/transaction/coordination/schema.d.ts +1536 -0
- package/dist/transaction/coordination/schema.js +1177 -0
- package/dist/transaction/coordination/targetConflict.d.ts +2 -0
- package/dist/transaction/coordination/targetConflict.js +107 -0
- package/dist/{coordination → transaction/coordination}/trace.d.ts +7 -18
- package/dist/{coordination → transaction/coordination}/trace.js +18 -25
- package/dist/transaction/durableWrites.d.ts +62 -0
- package/dist/{client → transaction}/durableWrites.js +28 -3
- package/dist/transaction/environment.d.ts +105 -0
- package/dist/transaction/environment.js +108 -0
- package/dist/{errorCodes.d.ts → transaction/errorCodes.d.ts} +12 -12
- package/dist/{errorCodes.js → transaction/errorCodes.js} +45 -18
- package/dist/{errors.d.ts → transaction/errors.d.ts} +37 -13
- package/dist/{errors.js → transaction/errors.js} +85 -16
- package/dist/transaction/footprint.d.ts +111 -0
- package/dist/transaction/footprint.js +0 -0
- package/dist/transaction/index.d.ts +20 -0
- package/dist/transaction/index.js +20 -0
- package/dist/transaction/keys/index.d.ts +87 -0
- package/dist/transaction/keys/index.js +207 -0
- package/dist/transaction/log/syncDeltaRow.d.ts +158 -0
- package/dist/transaction/log/syncDeltaRow.js +95 -0
- package/dist/{sync/syncPosition.d.ts → transaction/logPosition.d.ts} +22 -8
- package/dist/{sync/syncPosition.js → transaction/logPosition.js} +15 -6
- package/dist/transaction/logger.d.ts +16 -0
- package/dist/transaction/logger.js +7 -0
- package/dist/transaction/observability.d.ts +53 -0
- package/dist/transaction/observability.js +19 -0
- package/dist/transaction/plugin.d.ts +285 -0
- package/dist/transaction/plugin.js +106 -0
- package/dist/{policy → transaction/policy}/types.d.ts +3 -3
- package/dist/{policy → transaction/policy}/types.js +2 -0
- package/dist/transaction/resources/httpResources.d.ts +321 -0
- package/dist/transaction/resources/httpResources.js +7 -0
- package/dist/transaction/resources/modelOperations.d.ts +427 -0
- package/dist/transaction/resources/modelOperations.js +12 -0
- package/dist/transaction/resources/mutationOptions.d.ts +66 -0
- package/dist/transaction/resources/mutationOptions.js +9 -0
- package/dist/transaction/resources/where.d.ts +101 -0
- package/dist/transaction/resources/where.js +115 -0
- package/dist/{client → transaction/resources}/writeOptionsSchema.d.ts +2 -6
- package/dist/{client → transaction/resources}/writeOptionsSchema.js +9 -5
- package/dist/{schema → transaction/schema}/field.d.ts +17 -23
- package/dist/{schema → transaction/schema}/field.js +5 -5
- package/dist/transaction/schema/fieldRef.d.ts +38 -0
- package/dist/transaction/schema/fieldRef.js +11 -0
- package/dist/transaction/schema/loadStrategy.d.ts +45 -0
- package/dist/transaction/schema/loadStrategy.js +46 -0
- package/dist/{schema → transaction/schema}/model.d.ts +50 -35
- package/dist/{schema → transaction/schema}/model.js +30 -20
- package/dist/transaction/schema/openapi.d.ts +58 -0
- package/dist/transaction/schema/openapi.js +501 -0
- package/dist/{schema → transaction/schema}/relation.d.ts +21 -16
- package/dist/{schema → transaction/schema}/relation.js +7 -7
- package/dist/{schema → transaction/schema}/residency.d.ts +0 -9
- package/dist/{schema → transaction/schema}/residency.js +0 -5
- package/dist/{schema → transaction/schema}/roles.d.ts +5 -5
- package/dist/{schema → transaction/schema}/roles.js +5 -5
- package/dist/{schema → transaction/schema}/schema.d.ts +39 -10
- package/dist/{schema → transaction/schema}/schema.js +24 -3
- package/dist/{schema → transaction/schema}/tenancy.d.ts +1 -1
- package/dist/{schema → transaction/schema}/tenancy.js +7 -4
- package/dist/transaction/transactionLayer.d.ts +82 -0
- package/dist/transaction/transactionLayer.js +24 -0
- package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.d.ts +5 -6
- package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.js +9 -13
- package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.d.ts +9 -2
- package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.js +5 -9
- package/dist/{transactions/durableWriteStore.d.ts → transaction/transactions/settlement/pendingWrite.d.ts} +11 -37
- package/dist/transaction/transactions/settlement/pendingWrite.js +20 -0
- package/dist/transaction/transport/commitFrames.d.ts +90 -0
- package/dist/transaction/transport/commitFrames.js +134 -0
- package/dist/transaction/transport/connectionManager.d.ts +215 -0
- package/dist/transaction/transport/connectionManager.js +673 -0
- package/dist/transaction/transport/credentialLifecycle.d.ts +177 -0
- package/dist/transaction/transport/credentialLifecycle.js +324 -0
- package/dist/{sync → transaction/transport}/heartbeat.d.ts +3 -1
- package/dist/{sync → transaction/transport}/heartbeat.js +6 -4
- package/dist/transaction/transport/httpClient.d.ts +131 -0
- package/dist/{client → transaction/transport}/httpClient.js +6 -5
- package/dist/transaction/transport/httpOptions.d.ts +33 -0
- package/dist/transaction/transport/httpOptions.js +12 -0
- package/dist/{client → transaction/transport}/httpTransport.js +295 -97
- package/dist/{sync/NetworkProbe.d.ts → transaction/transport/networkProbe.d.ts} +7 -4
- package/dist/{sync/NetworkProbe.js → transaction/transport/networkProbe.js} +14 -13
- package/dist/transaction/transport/wsFrameHandlers.d.ts +128 -0
- package/dist/transaction/transport/wsFrameHandlers.js +429 -0
- package/dist/transaction/transport/wsTransport.d.ts +574 -0
- package/dist/transaction/transport/wsTransport.js +1023 -0
- package/dist/transaction/types/assertExact.d.ts +17 -0
- package/dist/transaction/types/assertExact.js +1 -0
- package/dist/{types → transaction/types}/global.d.ts +17 -2
- package/dist/{types → transaction/types}/global.js +2 -1
- package/dist/{types → transaction/types}/index.d.ts +14 -46
- package/dist/{types → transaction/types}/index.js +7 -16
- package/dist/{types → transaction/types}/streams.d.ts +73 -45
- package/dist/transaction/utils/duration.d.ts +50 -0
- package/dist/{utils → transaction/utils}/duration.js +32 -0
- package/dist/{utils → transaction/utils}/json.d.ts +18 -0
- package/dist/transaction/utils/json.js +276 -0
- package/dist/transaction/wire/accountResponses.d.ts +420 -0
- package/dist/transaction/wire/accountResponses.js +290 -0
- package/dist/transaction/wire/auth.d.ts +56 -0
- package/dist/transaction/wire/auth.js +63 -0
- package/dist/transaction/wire/claimEvent.d.ts +76 -0
- package/dist/transaction/wire/claimEvent.js +73 -0
- package/dist/transaction/wire/claims.d.ts +530 -0
- package/dist/transaction/wire/claims.js +327 -0
- package/dist/{wire → transaction/wire}/commit.d.ts +125 -193
- package/dist/{wire → transaction/wire}/commit.js +68 -47
- package/dist/{wire → transaction/wire}/delta.d.ts +66 -17
- package/dist/{wire → transaction/wire}/delta.js +37 -13
- package/dist/transaction/wire/errorEnvelope.d.ts +72 -0
- package/dist/{wire → transaction/wire}/errorEnvelope.js +36 -5
- package/dist/transaction/wire/feedCursor.d.ts +60 -0
- package/dist/transaction/wire/feedCursor.js +82 -0
- package/dist/transaction/wire/feedEvent.d.ts +204 -0
- package/dist/transaction/wire/feedEvent.js +65 -0
- package/dist/transaction/wire/frames.d.ts +194 -0
- package/dist/transaction/wire/frames.js +50 -0
- package/dist/transaction/wire/inboundFrames.d.ts +562 -0
- package/dist/transaction/wire/inboundFrames.js +116 -0
- package/dist/transaction/wire/index.d.ts +54 -0
- package/dist/transaction/wire/index.js +83 -0
- package/dist/{wire → transaction/wire}/listEnvelope.d.ts +13 -14
- package/dist/transaction/wire/listEnvelope.js +42 -0
- package/dist/transaction/wire/modelMutations.d.ts +31 -0
- package/dist/transaction/wire/modelMutations.js +52 -0
- package/dist/transaction/wire/modelResponses.d.ts +85 -0
- package/dist/transaction/wire/modelResponses.js +43 -0
- package/dist/transaction/wire/modelShape.d.ts +78 -0
- package/dist/transaction/wire/modelShape.js +74 -0
- package/dist/transactions/{TransactionQueue.d.ts → mutations/MutationQueue.d.ts} +85 -38
- package/dist/transactions/{TransactionQueue.js → mutations/MutationQueue.js} +141 -80
- package/dist/transactions/{TransactionStore.d.ts → mutations/MutationStore.d.ts} +8 -8
- package/dist/transactions/{TransactionStore.js → mutations/MutationStore.js} +2 -2
- package/dist/transactions/{coalesceRules.d.ts → mutations/coalesceRules.d.ts} +10 -10
- package/dist/transactions/{coalesceRules.js → mutations/coalesceRules.js} +1 -1
- package/dist/transactions/mutations/commitLatency.d.ts +52 -0
- package/dist/transactions/mutations/commitLatency.js +130 -0
- package/dist/transactions/{commitOutboxStore.d.ts → mutations/commitOutboxStore.d.ts} +1 -5
- package/dist/transactions/{commitOutboxStore.js → mutations/commitOutboxStore.js} +1 -1
- package/dist/transactions/{commitPayload.d.ts → mutations/commitPayload.d.ts} +18 -16
- package/dist/transactions/{commitPayload.js → mutations/commitPayload.js} +15 -15
- package/dist/transactions/{deltaConfirmation.d.ts → mutations/deltaConfirmation.d.ts} +15 -11
- package/dist/transactions/{deltaConfirmation.js → mutations/deltaConfirmation.js} +14 -12
- package/dist/transactions/mutations/durableWriteStore.d.ts +14 -0
- package/dist/transactions/mutations/durableWriteStore.js +12 -0
- package/dist/transactions/{optimisticApply.d.ts → mutations/optimisticApply.d.ts} +7 -7
- package/dist/transactions/{replayValidation.d.ts → mutations/replayValidation.d.ts} +4 -3
- package/dist/transactions/{replayValidation.js → mutations/replayValidation.js} +7 -5
- package/dist/utils/mobxSetup.d.ts +1 -1
- package/dist/utils/mobxSetup.js +5 -2
- package/dist/{core → views}/QueryView.d.ts +2 -2
- package/dist/{core → views}/QueryView.js +2 -2
- package/dist/{core → views}/ViewRegistry.d.ts +1 -1
- package/dist/{core/queryUtils.d.ts → views/incrementalView.d.ts} +6 -6
- package/dist/{core/queryUtils.js → views/incrementalView.js} +6 -6
- package/dist/webhooks/events.d.ts +2 -2
- package/dist/wire/index.d.ts +1 -34
- package/dist/wire/index.js +8 -49
- package/docs/agent-messaging.md +3 -3
- package/docs/agents.md +20 -13
- package/docs/api-keys.md +14 -10
- package/docs/api.md +27 -61
- package/docs/audit.md +6 -3
- package/docs/cli.md +41 -13
- package/docs/client-behavior.md +11 -9
- package/docs/concurrency-convention.md +49 -57
- package/docs/coordination.md +283 -121
- package/docs/data-sources.md +7 -5
- package/docs/debugging.md +39 -15
- package/docs/deployment.md +267 -0
- package/docs/examples/agent-human.md +49 -42
- package/docs/examples/ai-sdk-tool.md +69 -44
- package/docs/examples/existing-python-backend.md +8 -6
- package/docs/examples/nextjs.md +129 -47
- package/docs/examples/scoped-agent.md +46 -45
- package/docs/examples/server-agent.md +46 -26
- package/docs/groups.md +87 -30
- package/docs/guarantees.md +41 -12
- package/docs/how-it-works.md +38 -12
- package/docs/idempotency.md +126 -0
- package/docs/identity.md +77 -74
- package/docs/index.md +172 -86
- package/docs/integration-guide.md +31 -19
- package/docs/mcp.md +46 -21
- package/docs/migration.md +95 -18
- package/docs/operating-on-your-database.md +3 -1
- package/docs/projects.md +3 -1
- package/docs/quickstart.md +22 -5
- package/docs/react.md +31 -18
- package/docs/schema-contract.md +5 -3
- package/docs/session-settings.md +108 -0
- package/docs/sessions.md +4 -2
- package/docs/webhooks.md +12 -10
- package/llms.txt +48 -18
- package/package.json +21 -26
- package/dist/agent/Agent.d.ts +0 -366
- package/dist/agent/Agent.js +0 -514
- package/dist/agent/index.d.ts +0 -115
- package/dist/agent/index.js +0 -128
- package/dist/agent/session.d.ts +0 -93
- package/dist/agent/session.js +0 -149
- package/dist/agent/types.d.ts +0 -68
- package/dist/agent/types.js +0 -9
- package/dist/cli.cjs +0 -286329
- package/dist/client/durableWrites.d.ts +0 -21
- package/dist/client/httpClient.d.ts +0 -80
- package/dist/coordination/schema.d.ts +0 -722
- package/dist/coordination/schema.js +0 -578
- package/dist/schema/openapi.d.ts +0 -29
- package/dist/schema/openapi.js +0 -124
- package/dist/testing/fixtures/bootstrap.d.ts +0 -49
- package/dist/testing/fixtures/bootstrap.js +0 -59
- package/dist/testing/fixtures/deltas.d.ts +0 -83
- package/dist/testing/fixtures/deltas.js +0 -136
- package/dist/testing/fixtures/models.d.ts +0 -83
- package/dist/testing/fixtures/models.js +0 -272
- package/dist/testing/helpers/reactWrapper.d.ts +0 -69
- package/dist/testing/helpers/reactWrapper.js +0 -67
- package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
- package/dist/testing/helpers/syncEngineHarness.js +0 -73
- package/dist/testing/helpers/wait.d.ts +0 -30
- package/dist/testing/helpers/wait.js +0 -49
- package/dist/testing/index.d.ts +0 -23
- package/dist/testing/index.js +0 -33
- package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
- package/dist/testing/mocks/FakeDatabase.js +0 -10
- package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
- package/dist/testing/mocks/MockMutationExecutor.js +0 -192
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
- package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
- package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
- package/dist/testing/mocks/MockSyncContext.js +0 -71
- package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
- package/dist/testing/mocks/MockSyncStore.js +0 -171
- package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
- package/dist/testing/mocks/MockWebSocket.js +0 -118
- package/dist/transactions/durableWriteStore.js +0 -30
- package/dist/utils/duration.d.ts +0 -25
- package/dist/utils/json.js +0 -88
- package/dist/wire/errorEnvelope.d.ts +0 -55
- package/dist/wire/frames.d.ts +0 -197
- package/dist/wire/frames.js +0 -49
- package/dist/wire/listEnvelope.js +0 -18
- package/docs/interaction-model.md +0 -97
- /package/dist/{core → query}/QueryProcessor.d.ts +0 -0
- /package/dist/{core → query}/QueryProcessor.js +0 -0
- /package/dist/{core/storeContract.js → storeContract.js} +0 -0
- /package/dist/{core → stores}/openIDBWithTimeout.d.ts +0 -0
- /package/dist/{core → stores}/openIDBWithTimeout.js +0 -0
- /package/dist/{client → transaction/auth}/credentialEndpoint.d.ts +0 -0
- /package/dist/{client → transaction/auth}/credentialEndpoint.js +0 -0
- /package/dist/{auth → transaction/auth}/credentialPolicy.d.ts +0 -0
- /package/dist/{auth → transaction/auth}/credentialPolicy.js +0 -0
- /package/dist/{auth → transaction/auth}/credentialSource.js +0 -0
- /package/dist/{client → transaction/auth}/hostedEndpoints.d.ts +0 -0
- /package/dist/{client → transaction/auth}/hostedEndpoints.js +0 -0
- /package/dist/{client → transaction}/persistence.d.ts +0 -0
- /package/dist/{client → transaction}/persistence.js +0 -0
- /package/dist/{client → transaction/resources}/functionalUpdate.d.ts +0 -0
- /package/dist/{client → transaction/resources}/functionalUpdate.js +0 -0
- /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.d.ts +0 -0
- /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.js +0 -0
- /package/dist/{client → transaction/transport}/httpTransport.d.ts +0 -0
- /package/dist/{types → transaction/types}/modelData.d.ts +0 -0
- /package/dist/{types → transaction/types}/modelData.js +0 -0
- /package/dist/{types → transaction/types}/participant.d.ts +0 -0
- /package/dist/{types → transaction/types}/participant.js +0 -0
- /package/dist/{types → transaction/types}/streams.js +0 -0
- /package/dist/{utils → transaction/utils}/asyncIterator.d.ts +0 -0
- /package/dist/{utils → transaction/utils}/asyncIterator.js +0 -0
- /package/dist/{wire → transaction/wire}/bootstrapReason.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/bootstrapReason.js +0 -0
- /package/dist/{wire → transaction/wire}/protocol.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/protocol.js +0 -0
- /package/dist/{wire → transaction/wire}/protocolVersion.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/protocolVersion.js +0 -0
- /package/dist/transactions/{UnconfirmedWrites.d.ts → mutations/UnconfirmedWrites.d.ts} +0 -0
- /package/dist/transactions/{UnconfirmedWrites.js → mutations/UnconfirmedWrites.js} +0 -0
- /package/dist/transactions/{optimisticApply.js → mutations/optimisticApply.js} +0 -0
- /package/dist/{core → views}/ViewRegistry.js +0 -0
package/dist/Database.js
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* server. {@link BaseSyncedStore} drives it, and {@link InstanceCache} holds the
|
|
6
6
|
* in-memory mirror of what this class persists.
|
|
7
7
|
*/
|
|
8
|
-
import { DatabaseManager } from './
|
|
9
|
-
import { StoreManager } from './
|
|
8
|
+
import { DatabaseManager } from './stores/DatabaseManager.js';
|
|
9
|
+
import { StoreManager } from './stores/StoreManager.js';
|
|
10
10
|
import { ModelRegistry } from './ModelRegistry.js';
|
|
11
|
-
import { LoadStrategy } from './types/index.js';
|
|
12
|
-
import {
|
|
13
|
-
import { AbloConnectionError, AbloValidationError } from './errors.js';
|
|
11
|
+
import { LoadStrategy } from './transaction/types/index.js';
|
|
12
|
+
import { globalRuntime } from './context.js';
|
|
13
|
+
import { AbloConnectionError, AbloValidationError } from './transaction/errors.js';
|
|
14
14
|
import { InMemoryObjectStore } from './adapters/inMemoryStorage.js';
|
|
15
|
-
import {
|
|
15
|
+
import { logPositionSchema } from './transaction/logPosition.js';
|
|
16
16
|
import { highestPersistedPrefixSyncId } from './sync/persistedPrefix.js';
|
|
17
17
|
/**
|
|
18
18
|
* Request identity excludes local timing metadata for re-entrant seals: a
|
|
@@ -110,7 +110,7 @@ export class Database {
|
|
|
110
110
|
/** Essential fields that must be preserved during partial UPDATE merges.
|
|
111
111
|
* Sourced from SyncEngineConfig.essentialFields — consumers define their own. */
|
|
112
112
|
get essentialFields() {
|
|
113
|
-
return
|
|
113
|
+
return this.runtime.config.essentialFields;
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
116
|
* When true, all IndexedDB operations are replaced with in-memory Maps.
|
|
@@ -121,13 +121,15 @@ export class Database {
|
|
|
121
121
|
* new Database(registry, bootstrap, { inMemory: true })
|
|
122
122
|
*/
|
|
123
123
|
inMemory;
|
|
124
|
+
runtime;
|
|
124
125
|
/** In-memory stores used when inMemory=true. Keyed by model name. */
|
|
125
126
|
inMemoryStores = new Map();
|
|
126
127
|
/** In-memory workspace metadata when inMemory=true. */
|
|
127
128
|
inMemoryMetadata = null;
|
|
128
129
|
constructor(modelRegistry, bootstrapHelper, options) {
|
|
129
|
-
this.
|
|
130
|
-
this.
|
|
130
|
+
this.runtime = options?.runtime ?? globalRuntime;
|
|
131
|
+
this.databaseManager = new DatabaseManager(this.runtime);
|
|
132
|
+
this.storeManager = new StoreManager(modelRegistry, this.runtime);
|
|
131
133
|
this.modelRegistry = modelRegistry;
|
|
132
134
|
this.bootstrapHelper = bootstrapHelper;
|
|
133
135
|
this.inMemory = options?.inMemory ?? false;
|
|
@@ -149,7 +151,7 @@ export class Database {
|
|
|
149
151
|
? this.inMemoryStores.get(modelName)
|
|
150
152
|
: this.storeManager.getStore(modelName);
|
|
151
153
|
if (!store && context) {
|
|
152
|
-
|
|
154
|
+
this.runtime.observability.breadcrumb(`Store not found for model: ${modelName}`, 'sync.database', 'warning', { context });
|
|
153
155
|
}
|
|
154
156
|
return store;
|
|
155
157
|
}
|
|
@@ -170,7 +172,7 @@ export class Database {
|
|
|
170
172
|
const requiredFields = this.essentialFields[modelName] ?? [];
|
|
171
173
|
const preserved = requiredFields.filter((field) => existing[field] !== undefined && delta[field] === undefined);
|
|
172
174
|
if (preserved.length > 0) {
|
|
173
|
-
|
|
175
|
+
this.runtime.logger.debug('[Database] UPDATE merged - preserved fields', {
|
|
174
176
|
modelName,
|
|
175
177
|
modelId: modelId.slice(0, 12),
|
|
176
178
|
deltaFields: Object.keys(delta),
|
|
@@ -188,7 +190,7 @@ export class Database {
|
|
|
188
190
|
// Creates InMemoryObjectStore instances for all registered models.
|
|
189
191
|
// Bootstrap via HTTP still works; only local persistence is skipped.
|
|
190
192
|
if (this.inMemory) {
|
|
191
|
-
|
|
193
|
+
this.runtime.logger.debug('Opening in-memory database (headless mode)');
|
|
192
194
|
const allModels = this.modelRegistry.getRegisteredModelNames();
|
|
193
195
|
for (const modelName of allModels) {
|
|
194
196
|
const storeName = `store_${modelName.toLowerCase()}`;
|
|
@@ -196,11 +198,11 @@ export class Database {
|
|
|
196
198
|
}
|
|
197
199
|
// Create a __transactions store for the offline queue
|
|
198
200
|
this.inMemoryStores.set('__transactions', new InMemoryObjectStore('__transactions', '__transactions'));
|
|
199
|
-
|
|
201
|
+
this.runtime.logger.info(`In-memory database opened: ${this.inMemoryStores.size} stores`);
|
|
200
202
|
return;
|
|
201
203
|
}
|
|
202
204
|
// ── Browser mode: IndexedDB (existing behavior, unchanged) ───
|
|
203
|
-
|
|
205
|
+
this.runtime.logger.debug('Opening IndexedDB database');
|
|
204
206
|
// Initialize meta database
|
|
205
207
|
await this.databaseManager.initializeMetaDatabase();
|
|
206
208
|
// Calculate database info
|
|
@@ -208,13 +210,13 @@ export class Database {
|
|
|
208
210
|
// Register database
|
|
209
211
|
await this.databaseManager.registerDatabase(this.currentDbInfo);
|
|
210
212
|
// Open workspace database
|
|
211
|
-
this.workspaceDb = await this.databaseManager.openWorkspaceDatabase(this.currentDbInfo, async (db
|
|
212
|
-
await this.storeManager.createStores(db
|
|
213
|
+
this.workspaceDb = await this.databaseManager.openWorkspaceDatabase(this.currentDbInfo, async (db) => {
|
|
214
|
+
await this.storeManager.createStores(db);
|
|
213
215
|
});
|
|
214
216
|
// Initialize stores
|
|
215
217
|
await this.storeManager.initializeStores(this.workspaceDb);
|
|
216
218
|
const readiness = await this.storeManager.checkReadinessOfStores();
|
|
217
|
-
|
|
219
|
+
this.runtime.logger.info(`Database opened: ${this.currentDbInfo.name} (${readiness.readyStores.length}/${readiness.totalStores} stores ready)`);
|
|
218
220
|
}
|
|
219
221
|
/**
|
|
220
222
|
* Shrink a record before persisting it. Drops `undefined` fields, empty
|
|
@@ -277,7 +279,7 @@ export class Database {
|
|
|
277
279
|
*/
|
|
278
280
|
markRequiresFullBootstrap() {
|
|
279
281
|
this._forceFullBootstrap = true;
|
|
280
|
-
|
|
282
|
+
this.runtime.logger.info('[Database] Marked for forced full bootstrap (sync group change)');
|
|
281
283
|
}
|
|
282
284
|
/**
|
|
283
285
|
* Smart bootstrap requirements based on data freshness
|
|
@@ -309,7 +311,7 @@ export class Database {
|
|
|
309
311
|
this._forceFullBootstrap = false;
|
|
310
312
|
const instantModels = this.modelRegistry.getModelsByLoadStrategy(LoadStrategy.instant);
|
|
311
313
|
const lazyModels = this.modelRegistry.getModelsByLoadStrategy(LoadStrategy.lazy);
|
|
312
|
-
|
|
314
|
+
this.runtime.logger.info('[Database.requiredBootstrap] Forced FULL bootstrap (sync group change)');
|
|
313
315
|
return {
|
|
314
316
|
type: 'full',
|
|
315
317
|
modelsToLoad: [...instantModels, ...lazyModels],
|
|
@@ -328,7 +330,7 @@ export class Database {
|
|
|
328
330
|
// (a corrupted negative/float cursor would previously pass `|| 0`,
|
|
329
331
|
// which only catches falsy, and get sent to the server as the resume
|
|
330
332
|
// point). Invalid → 0 → full bootstrap, the safe degradation.
|
|
331
|
-
const metadataLastSyncId =
|
|
333
|
+
const metadataLastSyncId = logPositionSchema.shape.persisted.safeParse(metadata?.lastSyncId).data ?? 0;
|
|
332
334
|
const dataAge = metadata?.updatedAt ? Date.now() - metadata.updatedAt.getTime() : Infinity;
|
|
333
335
|
// ── Cache-validity check ─────────────────────────────────────
|
|
334
336
|
//
|
|
@@ -348,7 +350,7 @@ export class Database {
|
|
|
348
350
|
// we've confirmed the data it refers to actually exists in the stores.
|
|
349
351
|
const lastSyncId = dataExists ? metadataLastSyncId : 0;
|
|
350
352
|
// Log the resolved database state for diagnostics.
|
|
351
|
-
|
|
353
|
+
this.runtime.logger.debug('[Database.requiredBootstrap] State check', {
|
|
352
354
|
readinessReady: readiness.ready,
|
|
353
355
|
hasMetadata: !!metadata,
|
|
354
356
|
metadataLastSyncId,
|
|
@@ -365,13 +367,13 @@ export class Database {
|
|
|
365
367
|
if (offline && hasLocalData) {
|
|
366
368
|
// Offline with data - use local bootstrap (only option when offline)
|
|
367
369
|
type = 'local';
|
|
368
|
-
|
|
370
|
+
this.runtime.logger.info('Offline detected with local data - using local bootstrap');
|
|
369
371
|
}
|
|
370
372
|
else {
|
|
371
373
|
// The server is the source of truth: always use a full bootstrap
|
|
372
374
|
// when online.
|
|
373
375
|
type = 'full';
|
|
374
|
-
|
|
376
|
+
this.runtime.logger.info('Full bootstrap - server is source of truth', {
|
|
375
377
|
reason: offline ? 'offline_no_data' : 'server_authoritative',
|
|
376
378
|
hasLocalData,
|
|
377
379
|
lastSyncId,
|
|
@@ -396,12 +398,12 @@ export class Database {
|
|
|
396
398
|
* shrinkage check. Caller supplies the complete list, not just
|
|
397
399
|
* team-derived groups. */
|
|
398
400
|
syncGroups, onProgress) {
|
|
399
|
-
|
|
401
|
+
this.runtime.logger.debug('Starting bootstrap fetch', {
|
|
400
402
|
type: requirements.type,
|
|
401
403
|
lastSyncId: requirements.lastSyncId,
|
|
402
404
|
modelsToLoad: requirements.modelsToLoad,
|
|
403
405
|
});
|
|
404
|
-
|
|
406
|
+
this.runtime.logger.info('Database: Starting bootstrap from Go server', {
|
|
405
407
|
type: requirements.type,
|
|
406
408
|
syncGroups,
|
|
407
409
|
modelsToLoad: requirements.modelsToLoad,
|
|
@@ -410,12 +412,12 @@ export class Database {
|
|
|
410
412
|
// Fetch before any destructive operation, so a failed network
|
|
411
413
|
// request can't leave the local store empty.
|
|
412
414
|
const startTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
413
|
-
|
|
415
|
+
this.runtime.logger.info('Fetching bootstrap data from server (before clearing local data)', {
|
|
414
416
|
type: requirements.type,
|
|
415
417
|
lastSyncId: requirements.lastSyncId,
|
|
416
418
|
});
|
|
417
419
|
const bootstrapData = await this.bootstrapHelper.fetchBootstrap(requirements.lastSyncId);
|
|
418
|
-
|
|
420
|
+
this.runtime.logger.debug('Received bootstrap response', {
|
|
419
421
|
type: bootstrapData.type,
|
|
420
422
|
lastSyncId: bootstrapData.lastSyncId,
|
|
421
423
|
hasModels: !!bootstrapData.models,
|
|
@@ -431,7 +433,7 @@ export class Database {
|
|
|
431
433
|
// Handle partial bootstrap (delta batch)
|
|
432
434
|
if (bootstrapData.type === 'partial') {
|
|
433
435
|
const deltas = bootstrapData.deltas ?? [];
|
|
434
|
-
|
|
436
|
+
this.runtime.logger.info('Processing partial bootstrap with delta batch', {
|
|
435
437
|
deltaCount: deltas.length,
|
|
436
438
|
fromSyncId: requirements.lastSyncId,
|
|
437
439
|
toSyncId: bootstrapData.lastSyncId,
|
|
@@ -444,13 +446,18 @@ export class Database {
|
|
|
444
446
|
let deltasApplied = 0;
|
|
445
447
|
let deltaResults;
|
|
446
448
|
if (deltas.length > 0) {
|
|
447
|
-
//
|
|
449
|
+
// Narrow the wire delta to what processDelta reads. The field names
|
|
450
|
+
// are the wire's own — the only change is `id` becoming `syncId`.
|
|
451
|
+
// A group-change frame carries its payload as a JSON string, decoded
|
|
452
|
+
// here exactly as the live delta path does in BaseSyncedStore.
|
|
448
453
|
const formattedDeltas = deltas.map((delta) => ({
|
|
449
454
|
syncId: delta.id,
|
|
450
|
-
actionType: delta.
|
|
455
|
+
actionType: delta.actionType,
|
|
451
456
|
modelName: delta.modelName,
|
|
452
|
-
modelId: delta.
|
|
453
|
-
data: delta.data
|
|
457
|
+
modelId: delta.modelId,
|
|
458
|
+
data: typeof delta.data === 'string'
|
|
459
|
+
? JSON.parse(delta.data)
|
|
460
|
+
: delta.data,
|
|
454
461
|
}));
|
|
455
462
|
// Use batch processing for better performance
|
|
456
463
|
const batch = await this.processDeltaBatch(formattedDeltas);
|
|
@@ -466,7 +473,7 @@ export class Database {
|
|
|
466
473
|
updatedAt: new Date(),
|
|
467
474
|
});
|
|
468
475
|
const elapsed = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
|
|
469
|
-
|
|
476
|
+
this.runtime.logger.info(`Partial bootstrap complete in ${elapsed.toFixed(2)}ms`, {
|
|
470
477
|
deltasApplied,
|
|
471
478
|
lastSyncId: bootstrapData.lastSyncId,
|
|
472
479
|
});
|
|
@@ -483,21 +490,21 @@ export class Database {
|
|
|
483
490
|
for (const [modelName, modelData] of Object.entries(bootstrapData.models)) {
|
|
484
491
|
// Handle null, undefined, or non-array data
|
|
485
492
|
if (!modelData) {
|
|
486
|
-
|
|
493
|
+
this.runtime.observability.breadcrumb(`No data received for ${modelName}`, 'sync.bootstrap', 'warning');
|
|
487
494
|
continue;
|
|
488
495
|
}
|
|
489
496
|
if (!Array.isArray(modelData)) {
|
|
490
|
-
|
|
497
|
+
this.runtime.observability.breadcrumb(`Skipping non-array data for ${modelName}`, 'sync.bootstrap', 'warning');
|
|
491
498
|
continue;
|
|
492
499
|
}
|
|
493
500
|
// Skip empty arrays silently (expected for some models)
|
|
494
501
|
if (modelData.length === 0) {
|
|
495
|
-
|
|
502
|
+
this.runtime.logger.debug(`No ${modelName} items to store (empty array)`);
|
|
496
503
|
continue;
|
|
497
504
|
}
|
|
498
505
|
const store = this.getStore(modelName, 'bootstrap');
|
|
499
506
|
if (!store) {
|
|
500
|
-
|
|
507
|
+
this.runtime.logger.debug(`[Bootstrap] NO IDB STORE for ${modelName} — ${modelData.length} items DROPPED`);
|
|
501
508
|
continue;
|
|
502
509
|
}
|
|
503
510
|
let writeErrors = 0;
|
|
@@ -515,11 +522,18 @@ export class Database {
|
|
|
515
522
|
}
|
|
516
523
|
catch (error) {
|
|
517
524
|
writeErrors++;
|
|
518
|
-
|
|
525
|
+
this.runtime.observability.breadcrumb(`Failed to store ${modelName} item`, 'sync.database', 'error', {
|
|
519
526
|
error: error instanceof Error ? error.message : String(error),
|
|
520
527
|
});
|
|
521
528
|
}
|
|
522
529
|
}
|
|
530
|
+
// The model is marked persisted below whether or not every item landed,
|
|
531
|
+
// because a partial store is still what the next sync reconciles
|
|
532
|
+
// against. Counted and surfaced here so a partial does not read as a
|
|
533
|
+
// clean bootstrap.
|
|
534
|
+
if (writeErrors > 0) {
|
|
535
|
+
this.runtime.observability.breadcrumb(`Stored ${modelName} with ${writeErrors} of ${modelData.length} items dropped`, 'sync.database', 'warning');
|
|
536
|
+
}
|
|
523
537
|
// Mark model as persisted after successful write
|
|
524
538
|
try {
|
|
525
539
|
await this.setModelPersisted(modelName, true);
|
|
@@ -536,8 +550,8 @@ export class Database {
|
|
|
536
550
|
updatedAt: new Date(),
|
|
537
551
|
});
|
|
538
552
|
const elapsed = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
|
|
539
|
-
|
|
540
|
-
|
|
553
|
+
this.runtime.logger.info(`Bootstrap complete: ${modelsLoaded} items loaded, ${modelsStored} stored to IndexedDB in ${elapsed.toFixed(2)}ms`);
|
|
554
|
+
this.runtime.analytics?.capture('bootstrap_success', {
|
|
541
555
|
responseTime: elapsed,
|
|
542
556
|
modelsLoaded,
|
|
543
557
|
});
|
|
@@ -545,12 +559,12 @@ export class Database {
|
|
|
545
559
|
}
|
|
546
560
|
catch (error) {
|
|
547
561
|
// Comprehensive error logging for bootstrap failures
|
|
548
|
-
|
|
562
|
+
this.runtime.observability.captureBootstrapFailure(error, {
|
|
549
563
|
type: requirements.type,
|
|
550
564
|
navigatorOnline: typeof navigator !== 'undefined' ? navigator.onLine : undefined,
|
|
551
565
|
});
|
|
552
566
|
// Track bootstrap failure telemetry
|
|
553
|
-
|
|
567
|
+
this.runtime.analytics?.capture('bootstrap_failed', {
|
|
554
568
|
bootstrapType: requirements.type,
|
|
555
569
|
lastSyncId: requirements.lastSyncId,
|
|
556
570
|
errorMessage: error instanceof Error ? error.message : String(error),
|
|
@@ -569,7 +583,7 @@ export class Database {
|
|
|
569
583
|
*
|
|
570
584
|
* Update deltas carry only the changed fields, so they are merged onto the
|
|
571
585
|
* existing record rather than replacing it. That preserves fields the delta
|
|
572
|
-
* omits (such as
|
|
586
|
+
* omits (such as reportId or title), and an explicit null is kept as a value,
|
|
573
587
|
* clearing that field.
|
|
574
588
|
*/
|
|
575
589
|
async processDelta(delta) {
|
|
@@ -605,7 +619,7 @@ export class Database {
|
|
|
605
619
|
// newly created, it was newly visible. We fall through to the 'I' case
|
|
606
620
|
// after a debug trace so the two can be disambiguated in logs.
|
|
607
621
|
case 'C':
|
|
608
|
-
|
|
622
|
+
this.runtime.observability.breadcrumb('Applying covering delta (gained permission)', 'sync.database', 'info', { modelName, modelId: modelId.slice(0, 12) });
|
|
609
623
|
// falls through
|
|
610
624
|
case 'I': {
|
|
611
625
|
// Skip when the delta payload was empty/null. IDB rejects
|
|
@@ -623,7 +637,7 @@ export class Database {
|
|
|
623
637
|
}
|
|
624
638
|
}
|
|
625
639
|
catch (err) {
|
|
626
|
-
|
|
640
|
+
this.runtime.observability.breadcrumb(`IndexedDB put failed for ${modelName}:${modelId}`, 'sync.database', 'error', {
|
|
627
641
|
error: err instanceof Error ? err.message : String(err),
|
|
628
642
|
});
|
|
629
643
|
throw err; // Re-throw to see the actual error
|
|
@@ -636,9 +650,9 @@ export class Database {
|
|
|
636
650
|
const existing = await store.get(modelId);
|
|
637
651
|
// Skip the update when there's no existing record to merge with:
|
|
638
652
|
// building a record from partial update data would corrupt it
|
|
639
|
-
// (missing
|
|
653
|
+
// (missing reportId, and so on).
|
|
640
654
|
if (!existing) {
|
|
641
|
-
|
|
655
|
+
this.runtime.observability.breadcrumb('Skipping UPDATE delta - no existing record to merge with', 'sync.database', 'warning', {
|
|
642
656
|
modelName,
|
|
643
657
|
modelId: modelId.slice(0, 12),
|
|
644
658
|
});
|
|
@@ -659,7 +673,7 @@ export class Database {
|
|
|
659
673
|
}
|
|
660
674
|
}
|
|
661
675
|
catch (err) {
|
|
662
|
-
|
|
676
|
+
this.runtime.observability.breadcrumb(`IndexedDB put failed for ${modelName}:${modelId}`, 'sync.database', 'error', {
|
|
663
677
|
error: err instanceof Error ? err.message : String(err),
|
|
664
678
|
});
|
|
665
679
|
throw err;
|
|
@@ -676,7 +690,7 @@ export class Database {
|
|
|
676
690
|
}
|
|
677
691
|
}
|
|
678
692
|
catch (err) {
|
|
679
|
-
|
|
693
|
+
this.runtime.observability.breadcrumb(`IndexedDB delete failed for ${modelName}:${modelId}`, 'sync.database', 'error', {
|
|
680
694
|
error: err instanceof Error ? err.message : String(err),
|
|
681
695
|
});
|
|
682
696
|
// Surface failure so caller does not mutate InstanceCache inconsistently
|
|
@@ -694,7 +708,7 @@ export class Database {
|
|
|
694
708
|
}
|
|
695
709
|
}
|
|
696
710
|
catch (err) {
|
|
697
|
-
|
|
711
|
+
this.runtime.observability.breadcrumb(`IndexedDB archive put failed for ${modelName}:${modelId}`, 'sync.database', 'error', {
|
|
698
712
|
error: err instanceof Error ? err.message : String(err),
|
|
699
713
|
});
|
|
700
714
|
throw err;
|
|
@@ -710,12 +724,16 @@ export class Database {
|
|
|
710
724
|
// queue), we return a no-op verify rather than crashing the engine.
|
|
711
725
|
case 'G':
|
|
712
726
|
case 'S':
|
|
713
|
-
|
|
727
|
+
this.runtime.observability.breadcrumb(`Group membership delta (${actionType}) reached processDelta — should be handled upstream`, 'sync.database', 'warning', { modelName, modelId: modelId.slice(0, 12), actionType });
|
|
714
728
|
return { action: 'verify', modelName, modelId, data: null };
|
|
715
|
-
default:
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
729
|
+
default: {
|
|
730
|
+
// The switch above is exhaustive over the declared action types, so
|
|
731
|
+
// this branch is only reachable when a value escapes the type — hence
|
|
732
|
+
// stringifying whatever actually arrived rather than the `never`.
|
|
733
|
+
const _exhaustive = actionType;
|
|
734
|
+
void _exhaustive;
|
|
735
|
+
throw new AbloValidationError(`Unknown action type: ${JSON.stringify(actionType)}`, { code: 'db_unknown_action_type' });
|
|
736
|
+
}
|
|
719
737
|
}
|
|
720
738
|
}
|
|
721
739
|
/**
|
|
@@ -792,7 +810,7 @@ export class Database {
|
|
|
792
810
|
}
|
|
793
811
|
}
|
|
794
812
|
if (deleteSyncIds.size > 0) {
|
|
795
|
-
|
|
813
|
+
this.runtime.logger.debug('[Database.processDeltaBatch] Built DELETE index for conflict resolution', {
|
|
796
814
|
deleteCount: deleteSyncIds.size,
|
|
797
815
|
totalDeltas: deltas.length,
|
|
798
816
|
});
|
|
@@ -826,8 +844,7 @@ export class Database {
|
|
|
826
844
|
// entity has an equal or higher sync id.
|
|
827
845
|
if (delta.actionType === 'U' ||
|
|
828
846
|
delta.actionType === 'I' ||
|
|
829
|
-
delta.actionType === 'C'
|
|
830
|
-
delta.actionType === 'M') {
|
|
847
|
+
delta.actionType === 'C') {
|
|
831
848
|
const key = `${delta.modelName}:${delta.modelId}`;
|
|
832
849
|
const deleteSyncId = deleteSyncIds.get(key);
|
|
833
850
|
if (deleteSyncId !== undefined) {
|
|
@@ -835,7 +852,7 @@ export class Database {
|
|
|
835
852
|
const deltaSyncId = delta.syncId ?? 0;
|
|
836
853
|
if (deleteSyncId >= deltaSyncId) {
|
|
837
854
|
// DELETE has equal or higher syncId - skip this UPDATE/INSERT
|
|
838
|
-
|
|
855
|
+
this.runtime.logger.debug('[Database.processDeltaBatch] Skipping stale delta (DELETE wins)', {
|
|
839
856
|
modelName: delta.modelName,
|
|
840
857
|
modelId: delta.modelId.slice(0, 12),
|
|
841
858
|
actionType: delta.actionType,
|
|
@@ -862,7 +879,7 @@ export class Database {
|
|
|
862
879
|
deltasByStore.get(delta.modelName).push({ idx, delta });
|
|
863
880
|
});
|
|
864
881
|
if (skippedDueToConflict > 0) {
|
|
865
|
-
|
|
882
|
+
this.runtime.logger.info('[Database.processDeltaBatch] Conflict resolution summary', {
|
|
866
883
|
skippedDueToConflict,
|
|
867
884
|
totalDeltas: deltas.length,
|
|
868
885
|
deleteCount: deleteSyncIds.size,
|
|
@@ -892,8 +909,8 @@ export class Database {
|
|
|
892
909
|
}
|
|
893
910
|
}
|
|
894
911
|
}
|
|
895
|
-
catch
|
|
896
|
-
|
|
912
|
+
catch {
|
|
913
|
+
this.runtime.observability.breadcrumb(`Batch read failed for ${modelName}, falling back to individual reads`, 'sync.database', 'warning');
|
|
897
914
|
// Fallback: mark all as missing for self-healing
|
|
898
915
|
for (const id of updateIds) {
|
|
899
916
|
missingIds.add(id);
|
|
@@ -905,7 +922,7 @@ export class Database {
|
|
|
905
922
|
// so its delta is skipped).
|
|
906
923
|
const failedToFetch = new Set();
|
|
907
924
|
if (missingIds.size > 0) {
|
|
908
|
-
|
|
925
|
+
this.runtime.logger.info(`[Database.processDeltaBatch] Found ${missingIds.size} missing records for ${modelName}, fetching from server...`);
|
|
909
926
|
// Fetch sequentially to avoid overwhelming server
|
|
910
927
|
for (const id of missingIds) {
|
|
911
928
|
try {
|
|
@@ -913,24 +930,24 @@ export class Database {
|
|
|
913
930
|
if (fetchedRecord) {
|
|
914
931
|
const compacted = this.compactRecord(modelName, fetchedRecord);
|
|
915
932
|
existingRecords.set(id, compacted);
|
|
916
|
-
|
|
933
|
+
this.runtime.logger.debug(`[Database.processDeltaBatch] Successfully fetched missing record: ${modelName}:${id}`);
|
|
917
934
|
}
|
|
918
935
|
else {
|
|
919
936
|
// fetchEntity returns null for 404 — entity was deleted, skip the delta
|
|
920
937
|
failedToFetch.add(id);
|
|
921
|
-
|
|
938
|
+
this.runtime.logger.debug(`[Database.processDeltaBatch] Entity not found (deleted): ${modelName}:${id}`);
|
|
922
939
|
}
|
|
923
940
|
}
|
|
924
941
|
catch (error) {
|
|
925
942
|
// Unexpected error (5xx, network failure) — mark for skipping and report
|
|
926
943
|
failedToFetch.add(id);
|
|
927
|
-
|
|
944
|
+
this.runtime.observability.breadcrumb(`Failed to fetch missing record ${modelName}:${id}`, 'sync.database', 'warning', {
|
|
928
945
|
error: error instanceof Error ? error.message : String(error),
|
|
929
946
|
});
|
|
930
947
|
}
|
|
931
948
|
}
|
|
932
949
|
if (failedToFetch.size > 0) {
|
|
933
|
-
|
|
950
|
+
this.runtime.logger.info(`[Database.processDeltaBatch] Skipping ${failedToFetch.size} stale UPDATE deltas for deleted entities`, {
|
|
934
951
|
modelName,
|
|
935
952
|
failedCount: failedToFetch.size,
|
|
936
953
|
totalMissing: missingIds.size,
|
|
@@ -982,7 +999,7 @@ export class Database {
|
|
|
982
999
|
// store nor fetchable from the server (a 404), it was deleted,
|
|
983
1000
|
// so skip it rather than create an incomplete record.
|
|
984
1001
|
if (!existing && failedToFetch.has(modelId)) {
|
|
985
|
-
|
|
1002
|
+
this.runtime.logger.debug('[Database.processDeltaBatch] Skipping UPDATE for deleted entity', {
|
|
986
1003
|
modelName,
|
|
987
1004
|
modelId: modelId.slice(0, 12),
|
|
988
1005
|
});
|
|
@@ -991,9 +1008,9 @@ export class Database {
|
|
|
991
1008
|
}
|
|
992
1009
|
// Skip the update when there's no existing record to merge with:
|
|
993
1010
|
// building a record from partial update data would corrupt it
|
|
994
|
-
// (missing
|
|
1011
|
+
// (missing reportId, and so on).
|
|
995
1012
|
if (!existing) {
|
|
996
|
-
|
|
1013
|
+
this.runtime.observability.breadcrumb('Batch: Skipping UPDATE delta - no existing record', 'sync.database', 'warning', {
|
|
997
1014
|
modelName,
|
|
998
1015
|
modelId: modelId.slice(0, 12),
|
|
999
1016
|
});
|
|
@@ -1063,13 +1080,13 @@ export class Database {
|
|
|
1063
1080
|
}
|
|
1064
1081
|
}
|
|
1065
1082
|
catch (err) {
|
|
1066
|
-
// Surface the IDB error directly — `
|
|
1083
|
+
// Surface the IDB error directly — `captureMutationFailure`
|
|
1067
1084
|
// routes to Sentry, but during interactive debugging the console
|
|
1068
1085
|
// needs to show the specific failure (e.g. `ConstraintError`,
|
|
1069
1086
|
// `DataError`, `AbortError`) so we can find what's wrong with
|
|
1070
1087
|
// the `compacted` payload shape or store schema.
|
|
1071
1088
|
const idbErr = err instanceof Error ? err : new Error(String(err));
|
|
1072
|
-
|
|
1089
|
+
this.runtime.logger.debug('[Database.processDeltaBatch] store tx FAILED', {
|
|
1073
1090
|
modelName,
|
|
1074
1091
|
storeDeltasCount: storeDeltas.length,
|
|
1075
1092
|
errorName: idbErr.name,
|
|
@@ -1082,7 +1099,7 @@ export class Database {
|
|
|
1082
1099
|
: typeof delta.data,
|
|
1083
1100
|
})),
|
|
1084
1101
|
});
|
|
1085
|
-
|
|
1102
|
+
this.runtime.observability.captureMutationFailure({
|
|
1086
1103
|
context: 'batch-indexeddb-operation',
|
|
1087
1104
|
modelName,
|
|
1088
1105
|
error: idbErr,
|
|
@@ -1108,7 +1125,7 @@ export class Database {
|
|
|
1108
1125
|
await this.updateWorkspaceMetadata({ lastSyncId: highestPersistedSyncId });
|
|
1109
1126
|
}
|
|
1110
1127
|
catch (err) {
|
|
1111
|
-
|
|
1128
|
+
this.runtime.observability.breadcrumb('Failed to update metadata after batch', 'sync.database', 'error', {
|
|
1112
1129
|
error: err instanceof Error ? err.message : String(err),
|
|
1113
1130
|
});
|
|
1114
1131
|
}
|
|
@@ -1118,7 +1135,7 @@ export class Database {
|
|
|
1118
1135
|
// persisted" signal loud when it actually happens. If this fires
|
|
1119
1136
|
// repeatedly on the same sync IDs, a specific row is un-writable
|
|
1120
1137
|
// (validation? compact issue?) and needs fixing at that layer.
|
|
1121
|
-
|
|
1138
|
+
this.runtime.logger.debug('[Database.processDeltaBatch] cursor withheld due to failed store tx', {
|
|
1122
1139
|
seen: highestSyncId,
|
|
1123
1140
|
persisted: highestPersistedSyncId,
|
|
1124
1141
|
gap: highestSyncId - highestPersistedSyncId,
|
|
@@ -1138,7 +1155,7 @@ export class Database {
|
|
|
1138
1155
|
async putRecord(modelName, id, data) {
|
|
1139
1156
|
const store = this.getStore(modelName, 'putRecord');
|
|
1140
1157
|
if (!store) {
|
|
1141
|
-
|
|
1158
|
+
this.runtime.observability.breadcrumb(`Store not found for putRecord: ${modelName}`, 'sync.database', 'warning');
|
|
1142
1159
|
return;
|
|
1143
1160
|
}
|
|
1144
1161
|
const compacted = this.compactRecord(modelName, data);
|
|
@@ -1181,7 +1198,7 @@ export class Database {
|
|
|
1181
1198
|
// Graceful degradation: skip if database is closing or not open
|
|
1182
1199
|
// This prevents "Database not opened" errors during React Strict Mode cleanup
|
|
1183
1200
|
if (!this.workspaceDb || this.isClosing) {
|
|
1184
|
-
|
|
1201
|
+
this.runtime.observability.breadcrumb('updateWorkspaceMetadata: Database not open or closing', 'sync.database', 'warning', {
|
|
1185
1202
|
hasDb: !!this.workspaceDb,
|
|
1186
1203
|
isClosing: this.isClosing,
|
|
1187
1204
|
});
|
|
@@ -1473,13 +1490,13 @@ export class Database {
|
|
|
1473
1490
|
}
|
|
1474
1491
|
await this.databaseManager.close();
|
|
1475
1492
|
this.currentDbInfo = null;
|
|
1476
|
-
|
|
1493
|
+
this.runtime.logger.debug('Database closed');
|
|
1477
1494
|
}
|
|
1478
1495
|
async clear(options = {}) {
|
|
1479
1496
|
await this.storeManager.clearAllStores();
|
|
1480
1497
|
if (options.includeWriteJournal) {
|
|
1481
1498
|
await this.transactionStore.clear();
|
|
1482
1499
|
}
|
|
1483
|
-
|
|
1500
|
+
this.runtime.logger.info('All stores cleared');
|
|
1484
1501
|
}
|
|
1485
1502
|
}
|
package/dist/InstanceCache.d.ts
CHANGED
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Model } from './Model.js';
|
|
10
10
|
import { ModelRegistry } from './ModelRegistry.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import type { RuntimeContext } from './RuntimeContext.js';
|
|
12
|
+
import { ModelScope } from './transaction/types/index.js';
|
|
13
|
+
import { ViewRegistry } from './views/ViewRegistry.js';
|
|
14
|
+
import { QueryView, type QueryViewOptions } from './views/QueryView.js';
|
|
14
15
|
/** Constructor type for Model subclasses - uses abstract to handle variance */
|
|
15
16
|
type ModelConstructor<T extends Model> = abstract new (...args: never[]) => T;
|
|
16
17
|
export { ModelScope };
|
|
@@ -19,6 +20,8 @@ interface PoolConfig {
|
|
|
19
20
|
maxAge?: number;
|
|
20
21
|
gcInterval?: number;
|
|
21
22
|
useWeakRefs?: boolean;
|
|
23
|
+
/** The owning client's runtime. Defaults to the module-global bridge. */
|
|
24
|
+
runtime?: RuntimeContext;
|
|
22
25
|
}
|
|
23
26
|
interface DeltaInfo {
|
|
24
27
|
action?: string;
|
|
@@ -38,6 +41,7 @@ export declare class InstanceCache {
|
|
|
38
41
|
private foreignKeyConfig;
|
|
39
42
|
private metrics;
|
|
40
43
|
private config;
|
|
44
|
+
private readonly runtime;
|
|
41
45
|
private gcTimer?;
|
|
42
46
|
readonly registry: ModelRegistry;
|
|
43
47
|
readonly viewRegistry: ViewRegistry;
|
|
@@ -50,6 +54,27 @@ export declare class InstanceCache {
|
|
|
50
54
|
constructor(config?: PoolConfig, modelRegistry?: ModelRegistry);
|
|
51
55
|
private resolveModel;
|
|
52
56
|
get<T extends Model = Model>(id: string): T | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Look a row up **within one model**.
|
|
59
|
+
*
|
|
60
|
+
* The pool is a single id space: `get(id)` returns whatever row carries that
|
|
61
|
+
* id, whatever model it belongs to. That is the correct storage shape — ids
|
|
62
|
+
* are globally unique, the same premise as Relay's Global Object
|
|
63
|
+
* Identification — but it means an *untyped* lookup cannot stand in for a
|
|
64
|
+
* typed one. Apollo and EmberData avoid the question by keying their identity
|
|
65
|
+
* maps on `Type:id`; with unique ids the equivalent guarantee comes from
|
|
66
|
+
* stating the expected model at the lookup instead.
|
|
67
|
+
*
|
|
68
|
+
* Returns `undefined` for a row belonging to another model: from the asking
|
|
69
|
+
* model's perspective that id is simply absent. Callers that must tell "not
|
|
70
|
+
* here" apart from "here, but another model's" should compare against
|
|
71
|
+
* {@link get}.
|
|
72
|
+
*
|
|
73
|
+
* Prefer this over `get()` anywhere the caller knows which model it wants —
|
|
74
|
+
* `get()` returning another model's row has caused three product bugs, most
|
|
75
|
+
* recently a resize gesture that reverted after every commit.
|
|
76
|
+
*/
|
|
77
|
+
getOfType<T extends Model = Model>(id: string, modelName: string): T | undefined;
|
|
53
78
|
/**
|
|
54
79
|
* Add model with deduplication support
|
|
55
80
|
*/
|
|
@@ -97,7 +122,7 @@ export declare class InstanceCache {
|
|
|
97
122
|
* data. Cleaner than `createFromData({ __typename, ...data })` — the
|
|
98
123
|
* typename lives in the arg list, not hidden inside the data object.
|
|
99
124
|
*
|
|
100
|
-
* Used for optimistic local writes: `pool.create('
|
|
125
|
+
* Used for optimistic local writes: `pool.create('Section', { id, reportId, ... })`.
|
|
101
126
|
* For hydration from server deltas (where `__typename` already rides on
|
|
102
127
|
* the payload), use `createFromData(data)` directly — that path is kept
|
|
103
128
|
* because the wire format attaches the discriminator to the data itself.
|
|
@@ -178,8 +203,8 @@ export declare class InstanceCache {
|
|
|
178
203
|
* Register a foreign key field for indexing on a model type.
|
|
179
204
|
* Call once during app initialization (e.g., after model registration).
|
|
180
205
|
*
|
|
181
|
-
* Example: registerForeignKey('
|
|
182
|
-
* This enables getByForeignKey('
|
|
206
|
+
* Example: registerForeignKey('Block', 'sectionId')
|
|
207
|
+
* This enables getByForeignKey('Block', 'sectionId', someSectionId) → O(1) lookup
|
|
183
208
|
*/
|
|
184
209
|
registerForeignKey(modelName: string, fieldName: string): void;
|
|
185
210
|
/**
|