@tearleads/client-sdk 0.1.44 → 0.1.46
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/client/Tearleads.js +3 -0
- package/client/Tearleads.js.map +1 -1
- package/client/userIdentities.d.ts +11 -1
- package/client/userIdentities.js +26 -8
- package/client/userIdentities.js.map +1 -1
- package/client/workflowRuntime.d.ts +2 -1
- package/client/workflowRuntime.js +1 -0
- package/client/workflowRuntime.js.map +1 -1
- package/data/persistence/container-contents/containerContentsPersistence.js +2 -3
- package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerCreateIntentSettlement.js +16 -16
- package/data/persistence/container-contents/containerCreateIntentSettlement.js.map +1 -1
- package/data/persistence/container-contents/containerDeletionPersistence.js +5 -11
- package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerDocumentReassignment.js +13 -13
- package/data/persistence/container-contents/containerDocumentReassignment.js.map +1 -1
- package/data/persistence/container-contents/containerHydrationPersistence.js +7 -4
- package/data/persistence/container-contents/containerHydrationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerIntentPersistence.js +43 -42
- package/data/persistence/container-contents/containerIntentPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js +15 -20
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMoveAbandonPersistence.js +1 -6
- package/data/persistence/container-contents/containerMoveAbandonPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMoveIntentRows.js +9 -8
- package/data/persistence/container-contents/containerMoveIntentRows.js.map +1 -1
- package/data/persistence/container-contents/containerReconcilePersistence.js +4 -8
- package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
- package/data/persistence/container-contents/containerRecoveryPersistence.js +20 -19
- package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerRemovalWork.js +10 -10
- package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
- package/data/persistence/container-contents/containerStructuralRepair.js +21 -44
- package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
- package/data/persistence/container-contents/documentLinkRemovalRepair.js +12 -11
- package/data/persistence/container-contents/documentLinkRemovalRepair.js.map +1 -1
- package/data/persistence/container-contents/documentMoveIntentPersistence.js +53 -28
- package/data/persistence/container-contents/documentMoveIntentPersistence.js.map +1 -1
- package/data/persistence/container-contents/documentPlacementIntentEnqueue.js +9 -8
- package/data/persistence/container-contents/documentPlacementIntentEnqueue.js.map +1 -1
- package/data/persistence/container-contents/documentPurgePlacement.js +6 -6
- package/data/persistence/container-contents/documentPurgePlacement.js.map +1 -1
- package/data/persistence/container-contents/dormantContainerMetadata.js +1 -5
- package/data/persistence/container-contents/dormantContainerMetadata.js.map +1 -1
- package/data/persistence/container-contents/pendingContainerCreate.js +5 -5
- package/data/persistence/container-contents/pendingContainerCreate.js.map +1 -1
- package/data/persistence/containers/containerPersistence.js +10 -27
- package/data/persistence/containers/containerPersistence.js.map +1 -1
- package/data/persistence/containers/documentContainerProjectionPersistence.js +2 -2
- package/data/persistence/containers/documentContainerProjectionPersistence.js.map +1 -1
- package/data/persistence/containers/documentPlacement.js +10 -10
- package/data/persistence/containers/documentPlacement.js.map +1 -1
- package/data/persistence/documents/internal/discardDocument.js +5 -5
- package/data/persistence/documents/internal/discardDocument.js.map +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js +7 -9
- package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentMutationPersistence.js +6 -8
- package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
- package/data/persistence/documents/internal/ensureDocumentsSchema.js +2 -2
- package/data/persistence/documents/internal/ensureDocumentsSchema.js.map +1 -1
- package/data/persistence/documents/internal/orphanSideRows.js +3 -22
- package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
- package/data/persistence/organizations/organizationDataUsagePersistence.js +31 -64
- package/data/persistence/organizations/organizationDataUsagePersistence.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelPurge.js +1 -5
- package/data/persistence/organizations/organizationReadModelPurge.js.map +1 -1
- package/data/persistence/principalCurrentAcknowledgementPersistence.js +24 -17
- package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -1
- package/data/persistence/principalHistoryIncompleteRetention.js +3 -9
- package/data/persistence/principalHistoryIncompleteRetention.js.map +1 -1
- package/data/persistence/principalHistoryStagePersistence.d.ts +3 -9
- package/data/persistence/principalHistoryStagePersistence.js +15 -25
- package/data/persistence/principalHistoryStagePersistence.js.map +1 -1
- package/data/persistence/principalHistoryStageRetention.js +16 -15
- package/data/persistence/principalHistoryStageRetention.js.map +1 -1
- package/data/persistence/principalHistoryUnpublishedRetention.js +6 -13
- package/data/persistence/principalHistoryUnpublishedRetention.js.map +1 -1
- package/data/sqlite/containerSchema.d.ts +84 -180
- package/data/sqlite/containerSchema.js +14 -23
- package/data/sqlite/containerSchema.js.map +1 -1
- package/data/sqlite/documentPlacementIntentSchema.js +3 -50
- package/data/sqlite/documentPlacementIntentSchema.js.map +1 -1
- package/data/sqlite/documentSyncFailurePersistence.d.ts +16 -6
- package/data/sqlite/documentSyncFailurePersistence.js +44 -54
- package/data/sqlite/documentSyncFailurePersistence.js.map +1 -1
- package/data/sqlite/{documentPlacementIntentSchema.d.ts → localIntentSchema.d.ts} +162 -88
- package/data/sqlite/localIntentSchema.js +90 -0
- package/data/sqlite/localIntentSchema.js.map +1 -0
- package/data/sqlite/organizationDataUsageSchema.js +11 -17
- package/data/sqlite/organizationDataUsageSchema.js.map +1 -1
- package/data/sqlite/principalHistoryRetentionSchema.js +0 -15
- package/data/sqlite/principalHistoryRetentionSchema.js.map +1 -1
- package/data/sqlite/principalHistoryStageSchema.d.ts +44 -2
- package/data/sqlite/principalHistoryStageSchema.js +14 -2
- package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
- package/data/sqlite/schema.d.ts +947 -2112
- package/data/sqlite/schema.js +26 -134
- package/data/sqlite/schema.js.map +1 -1
- package/data/trustedUserIdentity/identityLink.d.ts +12 -0
- package/data/trustedUserIdentity/identityLink.js +117 -0
- package/data/trustedUserIdentity/identityLink.js.map +1 -0
- package/data/trustedUserIdentity/index.d.ts +2 -1
- package/data/trustedUserIdentity/index.js +1 -0
- package/data/trustedUserIdentity/index.js.map +1 -1
- package/data/trustedUserIdentity/service.js +48 -2
- package/data/trustedUserIdentity/service.js.map +1 -1
- package/data/trustedUserIdentity/types.d.ts +3 -0
- package/data/trustedUserIdentity/types.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/documentPriming.js +6 -10
- package/workflows/container-contents/documentPriming.js.map +1 -1
- package/workflows/container-contents/documentQueries/sql.js +11 -10
- package/workflows/container-contents/documentQueries/sql.js.map +1 -1
- package/workflows/container-contents/documentQueries.js +2 -2
- package/workflows/container-contents/documentQueries.js.map +1 -1
- package/workflows/container-contents/pendingDownloads.js +2 -5
- package/workflows/container-contents/pendingDownloads.js.map +1 -1
- package/workflows/container-contents/pendingWrites/deferredTails.js +3 -11
- package/workflows/container-contents/pendingWrites/deferredTails.js.map +1 -1
- package/workflows/container-contents/pendingWrites/sources.js +56 -80
- package/workflows/container-contents/pendingWrites/sources.js.map +1 -1
- package/workflows/container-contents/pendingWrites.js +6 -4
- package/workflows/container-contents/pendingWrites.js.map +1 -1
- package/workflows/container-contents/remoteContainerState.js +2 -1
- package/workflows/container-contents/remoteContainerState.js.map +1 -1
- package/workflows/sync/remoteReset.js +16 -23
- package/workflows/sync/remoteReset.js.map +1 -1
- package/workflows/sync/remoteResetPrincipalScope.js +1 -2
- package/workflows/sync/remoteResetPrincipalScope.js.map +1 -1
- package/workflows/sync/remoteResetScope.js +15 -8
- package/workflows/sync/remoteResetScope.js.map +1 -1
|
@@ -1,79 +1,68 @@
|
|
|
1
1
|
import { and, eq, isNotNull } from "drizzle-orm";
|
|
2
|
-
import {
|
|
3
|
-
import { getClientSQLitePersistenceRuntime } from "./sqlitePersistenceRuntime.js";
|
|
2
|
+
import { documents, documentTables, localIntents, localIntentTables, } from "./schema.js";
|
|
3
|
+
import { getClientSQLitePersistenceRuntime, } from "./sqlitePersistenceRuntime.js";
|
|
4
4
|
import { ensureSqlTables } from "./sqlSchema.js";
|
|
5
|
+
/** The `documents` column values of a scope with no recorded failure. */
|
|
6
|
+
export const CLEARED_DOCUMENT_SYNC_FAILURE = {
|
|
7
|
+
syncFailureAttemptedAt: null,
|
|
8
|
+
syncFailureMessage: null,
|
|
9
|
+
syncFailureStatus: null,
|
|
10
|
+
};
|
|
11
|
+
function scopeWhere(scope) {
|
|
12
|
+
return and(eq(documents.appKind, scope.appKind), eq(documents.localId, scope.localId));
|
|
13
|
+
}
|
|
5
14
|
/**
|
|
6
|
-
*
|
|
7
|
-
* scope. The durable pending-update queue has no status columns, so
|
|
8
|
-
*
|
|
9
|
-
*
|
|
15
|
+
* Record the last terminal failure or incoming-update quarantine on a document
|
|
16
|
+
* scope's row. The durable pending-update queue has no status columns, so
|
|
17
|
+
* these columns let the write-queue view explain a blocked document. A newer
|
|
18
|
+
* failure replaces the old. A scope with no `documents` row has nothing left
|
|
19
|
+
* to explain, so the update is a no-op there.
|
|
10
20
|
*/
|
|
11
21
|
export async function recordDocumentSyncFailure(execSql, scope, failure) {
|
|
12
22
|
await getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {
|
|
13
23
|
await db
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
message: failure.message,
|
|
20
|
-
attemptedAt: failure.attemptedAt,
|
|
21
|
-
})
|
|
22
|
-
.onConflictDoUpdate({
|
|
23
|
-
target: [documentSyncFailures.appKind, documentSyncFailures.localId],
|
|
24
|
-
set: {
|
|
25
|
-
status: failure.status,
|
|
26
|
-
message: failure.message,
|
|
27
|
-
attemptedAt: failure.attemptedAt,
|
|
28
|
-
},
|
|
24
|
+
.update(documents)
|
|
25
|
+
.set({
|
|
26
|
+
syncFailureAttemptedAt: failure.attemptedAt,
|
|
27
|
+
syncFailureMessage: failure.message,
|
|
28
|
+
syncFailureStatus: failure.status,
|
|
29
29
|
})
|
|
30
|
+
.where(scopeWhere(scope))
|
|
30
31
|
.run();
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
|
-
* Whether any queued local write has recorded a terminal failure — a
|
|
35
|
-
*
|
|
35
|
+
* Whether any queued local write has recorded a terminal failure — a document
|
|
36
|
+
* row carrying a sync failure or an intent row carrying `last_error`. This is
|
|
36
37
|
* the evidence gate for recovery triggers (e.g. re-arming write lanes when org
|
|
37
38
|
* access is restored): without it a transient denial during bootstrap would
|
|
38
39
|
* fire recovery work with nothing to recover, racing the startup sync passes.
|
|
39
40
|
*/
|
|
40
41
|
export async function hasRecordedTerminalSyncFailures(execSql) {
|
|
41
|
-
await ensureSqlTables(execSql, [
|
|
42
|
-
...documentTables,
|
|
43
|
-
...containerCreateIntentTables,
|
|
44
|
-
...containerMoveIntentTables,
|
|
45
|
-
...documentMoveIntentTables,
|
|
46
|
-
]);
|
|
42
|
+
await ensureSqlTables(execSql, [...documentTables, ...localIntentTables]);
|
|
47
43
|
const { db } = getClientSQLitePersistenceRuntime(execSql);
|
|
48
44
|
const [failure] = await db
|
|
49
|
-
.select({ localId:
|
|
50
|
-
.from(
|
|
45
|
+
.select({ localId: documents.localId })
|
|
46
|
+
.from(documents)
|
|
47
|
+
.where(isNotNull(documents.syncFailureMessage))
|
|
51
48
|
.limit(1);
|
|
52
49
|
if (failure) {
|
|
53
50
|
return true;
|
|
54
51
|
}
|
|
55
|
-
const [
|
|
56
|
-
.select({ id:
|
|
57
|
-
.from(
|
|
58
|
-
.where(isNotNull(
|
|
59
|
-
.limit(1);
|
|
60
|
-
if (createIntent) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
const [moveIntent] = await db
|
|
64
|
-
.select({ id: containerMoveIntents.id })
|
|
65
|
-
.from(containerMoveIntents)
|
|
66
|
-
.where(isNotNull(containerMoveIntents.lastError))
|
|
52
|
+
const [intent] = await db
|
|
53
|
+
.select({ id: localIntents.id })
|
|
54
|
+
.from(localIntents)
|
|
55
|
+
.where(isNotNull(localIntents.lastError))
|
|
67
56
|
.limit(1);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.
|
|
74
|
-
.
|
|
75
|
-
.
|
|
76
|
-
|
|
57
|
+
return intent !== undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Clear a scope's recorded failure inside a caller's transaction. */
|
|
60
|
+
export async function clearDocumentSyncFailureInTransaction(tx, scope) {
|
|
61
|
+
await tx
|
|
62
|
+
.update(documents)
|
|
63
|
+
.set(CLEARED_DOCUMENT_SYNC_FAILURE)
|
|
64
|
+
.where(scopeWhere(scope))
|
|
65
|
+
.run();
|
|
77
66
|
}
|
|
78
67
|
/**
|
|
79
68
|
* Drop the recorded failure for a scope. Called when a later sync pass for the
|
|
@@ -83,8 +72,9 @@ export async function hasRecordedTerminalSyncFailures(execSql) {
|
|
|
83
72
|
export async function clearDocumentSyncFailure(execSql, scope) {
|
|
84
73
|
await getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {
|
|
85
74
|
await db
|
|
86
|
-
.
|
|
87
|
-
.
|
|
75
|
+
.update(documents)
|
|
76
|
+
.set(CLEARED_DOCUMENT_SYNC_FAILURE)
|
|
77
|
+
.where(scopeWhere(scope))
|
|
88
78
|
.run();
|
|
89
79
|
});
|
|
90
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documentSyncFailurePersistence.js","sourceRoot":"","sources":["documentSyncFailurePersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"documentSyncFailurePersistence.js","sourceRoot":"","sources":["documentSyncFailurePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, isNotNull } from \"drizzle-orm\";\nimport type { DocumentScope } from \"./documentPersistenceTypes\";\nimport {\n documents,\n documentTables,\n localIntents,\n localIntentTables,\n} from \"./schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"./sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"./sqlSchema\";\n\ninterface DocumentSyncFailureFields {\n readonly attemptedAt: string;\n readonly message: string;\n readonly status: number | null;\n}\n\n/** The `documents` column values of a scope with no recorded failure. */\nexport const CLEARED_DOCUMENT_SYNC_FAILURE = {\n syncFailureAttemptedAt: null,\n syncFailureMessage: null,\n syncFailureStatus: null,\n} as const;\n\nfunction scopeWhere(scope: DocumentScope) {\n return and(\n eq(documents.appKind, scope.appKind),\n eq(documents.localId, scope.localId),\n );\n}\n\n/**\n * Record the last terminal failure or incoming-update quarantine on a document\n * scope's row. The durable pending-update queue has no status columns, so\n * these columns let the write-queue view explain a blocked document. A newer\n * failure replaces the old. A scope with no `documents` row has nothing left\n * to explain, so the update is a no-op there.\n */\nexport async function recordDocumentSyncFailure(\n execSql: ExecSql,\n scope: DocumentScope,\n failure: DocumentSyncFailureFields,\n): Promise<void> {\n await getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {\n await db\n .update(documents)\n .set({\n syncFailureAttemptedAt: failure.attemptedAt,\n syncFailureMessage: failure.message,\n syncFailureStatus: failure.status,\n })\n .where(scopeWhere(scope))\n .run();\n });\n}\n\n/**\n * Whether any queued local write has recorded a terminal failure — a document\n * row carrying a sync failure or an intent row carrying `last_error`. This is\n * the evidence gate for recovery triggers (e.g. re-arming write lanes when org\n * access is restored): without it a transient denial during bootstrap would\n * fire recovery work with nothing to recover, racing the startup sync passes.\n */\nexport async function hasRecordedTerminalSyncFailures(\n execSql: ExecSql,\n): Promise<boolean> {\n await ensureSqlTables(execSql, [...documentTables, ...localIntentTables]);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [failure] = await db\n .select({ localId: documents.localId })\n .from(documents)\n .where(isNotNull(documents.syncFailureMessage))\n .limit(1);\n if (failure) {\n return true;\n }\n const [intent] = await db\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(isNotNull(localIntents.lastError))\n .limit(1);\n return intent !== undefined;\n}\n\n/** Clear a scope's recorded failure inside a caller's transaction. */\nexport async function clearDocumentSyncFailureInTransaction(\n tx: ClientSQLiteTransactionScope,\n scope: DocumentScope,\n): Promise<void> {\n await tx\n .update(documents)\n .set(CLEARED_DOCUMENT_SYNC_FAILURE)\n .where(scopeWhere(scope))\n .run();\n}\n\n/**\n * Drop the recorded failure for a scope. Called when a later sync pass for the\n * scope succeeds and whenever the scope's local sync state is torn down, so a\n * stale failure never outlives the condition it describes.\n */\nexport async function clearDocumentSyncFailure(\n execSql: ExecSql,\n scope: DocumentScope,\n): Promise<void> {\n await getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {\n await db\n .update(documents)\n .set(CLEARED_DOCUMENT_SYNC_FAILURE)\n .where(scopeWhere(scope))\n .run();\n });\n}\n"]}
|
|
@@ -1,39 +1,61 @@
|
|
|
1
|
+
import { type SQL } from "drizzle-orm";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Lanes of the durable local outbox. Each lane keeps at most one intent per
|
|
4
|
+
* subject: a container may hold a create and a move at once, while a
|
|
5
|
+
* document's move and link coalesce into one placement intent.
|
|
6
|
+
*/
|
|
7
|
+
export declare const LOCAL_INTENT_LANES: {
|
|
8
|
+
readonly containerCreate: "container.create";
|
|
9
|
+
readonly containerMove: "container.move";
|
|
10
|
+
readonly documentPlacement: "document.placement";
|
|
11
|
+
};
|
|
12
|
+
export type LocalIntentLane = (typeof LOCAL_INTENT_LANES)[keyof typeof LOCAL_INTENT_LANES];
|
|
13
|
+
/**
|
|
14
|
+
* Device-first structural writes waiting for remote replay: container creates,
|
|
15
|
+
* container moves, and document placement (move and link) intents.
|
|
3
16
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
17
|
+
* Every lane shares one revision protocol: `id` is rewritten on each enqueue or
|
|
18
|
+
* repair, settlement compares `(id, updatedAt)` before acting, and replay runs
|
|
19
|
+
* in `createdAt` order. Remote reset deletes the scoped intents of every lane
|
|
20
|
+
* (and re-queues creates for surviving containers); nothing here survives a
|
|
21
|
+
* reset. Durable requests that must survive reset (the principal mutation
|
|
22
|
+
* journal and organization provisioning attempts) live in their own tables.
|
|
7
23
|
*
|
|
8
24
|
* Columns:
|
|
9
|
-
* - `id`:
|
|
10
|
-
* - `
|
|
11
|
-
* - `
|
|
12
|
-
*
|
|
13
|
-
* - `
|
|
14
|
-
*
|
|
15
|
-
* - `
|
|
25
|
+
* - `id`: Revision token and primary key.
|
|
26
|
+
* - `lane`: One of `LOCAL_INTENT_LANES`.
|
|
27
|
+
* - `intentType`: `container.create`, `container.move`, `document.move`, or
|
|
28
|
+
* `document.link`. A placement intent switches between the last two in place.
|
|
29
|
+
* - `subjectId`: Container id for container lanes, server document id for the
|
|
30
|
+
* placement lane.
|
|
31
|
+
* - `localId`: Local document row the placement intent updates; null for
|
|
32
|
+
* container lanes.
|
|
33
|
+
* - `targetContainerId`: Desired parent (container lanes) or target container
|
|
34
|
+
* (placement lane).
|
|
35
|
+
* - `sourceContainerId`: Parent observed when a container move was first
|
|
36
|
+
* queued, or the source container a document move unlinks.
|
|
37
|
+
* - `replaceLinkedContainers`: Placement only — the final remote link set is
|
|
16
38
|
* only the target container.
|
|
17
|
-
* - `
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* - `
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* - `
|
|
39
|
+
* - `remoteContainerId`, `remoteMetadataDocumentId`,
|
|
40
|
+
* `remoteMetadataAccessStateHash`: Remote identities a container create
|
|
41
|
+
* records once accepted.
|
|
42
|
+
* - `syncStatus`: Lane-specific state (`pending`/`synced` for creates,
|
|
43
|
+
* `pending`/`blocked` for container moves, `pending`/`blocked`/`denied`/
|
|
44
|
+
* `unavailable` for placements).
|
|
45
|
+
* - `lastError`, `lastAttemptedAt`: Replay diagnostics.
|
|
46
|
+
* - `createdAt`: FIFO replay order; `updatedAt`: last target/status change.
|
|
24
47
|
*
|
|
25
48
|
* Indexes:
|
|
26
|
-
* - `
|
|
27
|
-
* - `
|
|
28
|
-
* - `(syncStatus, createdAt)` supports listing pending intents in queue order.
|
|
49
|
+
* - `(lane, subjectId)` is unique so repeated enqueues coalesce per lane.
|
|
50
|
+
* - `(lane, syncStatus, createdAt)` lists a lane's queue in order.
|
|
29
51
|
*/
|
|
30
|
-
export declare const
|
|
31
|
-
name: "
|
|
52
|
+
export declare const localIntents: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
53
|
+
name: "local_intents";
|
|
32
54
|
schema: undefined;
|
|
33
55
|
columns: {
|
|
34
56
|
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
35
57
|
name: "id";
|
|
36
|
-
tableName: "
|
|
58
|
+
tableName: "local_intents";
|
|
37
59
|
dataType: "string";
|
|
38
60
|
columnType: "SQLiteText";
|
|
39
61
|
data: string;
|
|
@@ -50,9 +72,9 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
50
72
|
}, {}, {
|
|
51
73
|
length: number | undefined;
|
|
52
74
|
}>;
|
|
53
|
-
|
|
54
|
-
name: "
|
|
55
|
-
tableName: "
|
|
75
|
+
lane: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
76
|
+
name: "lane";
|
|
77
|
+
tableName: "local_intents";
|
|
56
78
|
dataType: "string";
|
|
57
79
|
columnType: "SQLiteText";
|
|
58
80
|
data: string;
|
|
@@ -69,9 +91,9 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
69
91
|
}, {}, {
|
|
70
92
|
length: number | undefined;
|
|
71
93
|
}>;
|
|
72
|
-
|
|
73
|
-
name: "
|
|
74
|
-
tableName: "
|
|
94
|
+
intentType: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
95
|
+
name: "intent_type";
|
|
96
|
+
tableName: "local_intents";
|
|
75
97
|
dataType: "string";
|
|
76
98
|
columnType: "SQLiteText";
|
|
77
99
|
data: string;
|
|
@@ -88,9 +110,9 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
88
110
|
}, {}, {
|
|
89
111
|
length: number | undefined;
|
|
90
112
|
}>;
|
|
91
|
-
|
|
92
|
-
name: "
|
|
93
|
-
tableName: "
|
|
113
|
+
subjectId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
114
|
+
name: "subject_id";
|
|
115
|
+
tableName: "local_intents";
|
|
94
116
|
dataType: "string";
|
|
95
117
|
columnType: "SQLiteText";
|
|
96
118
|
data: string;
|
|
@@ -107,9 +129,9 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
107
129
|
}, {}, {
|
|
108
130
|
length: number | undefined;
|
|
109
131
|
}>;
|
|
110
|
-
|
|
111
|
-
name: "
|
|
112
|
-
tableName: "
|
|
132
|
+
localId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
133
|
+
name: "local_id";
|
|
134
|
+
tableName: "local_intents";
|
|
113
135
|
dataType: "string";
|
|
114
136
|
columnType: "SQLiteText";
|
|
115
137
|
data: string;
|
|
@@ -126,31 +148,33 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
126
148
|
}, {}, {
|
|
127
149
|
length: number | undefined;
|
|
128
150
|
}>;
|
|
129
|
-
|
|
130
|
-
name: "
|
|
131
|
-
tableName: "
|
|
132
|
-
dataType: "
|
|
133
|
-
columnType: "
|
|
134
|
-
data:
|
|
135
|
-
driverParam:
|
|
151
|
+
targetContainerId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
152
|
+
name: "target_container_id";
|
|
153
|
+
tableName: "local_intents";
|
|
154
|
+
dataType: "string";
|
|
155
|
+
columnType: "SQLiteText";
|
|
156
|
+
data: string;
|
|
157
|
+
driverParam: string;
|
|
136
158
|
notNull: true;
|
|
137
|
-
hasDefault:
|
|
159
|
+
hasDefault: false;
|
|
138
160
|
isPrimaryKey: false;
|
|
139
161
|
isAutoincrement: false;
|
|
140
162
|
hasRuntimeDefault: false;
|
|
141
|
-
enumValues:
|
|
163
|
+
enumValues: [string, ...string[]];
|
|
142
164
|
baseColumn: never;
|
|
143
165
|
identity: undefined;
|
|
144
166
|
generated: undefined;
|
|
145
|
-
}, {}, {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
167
|
+
}, {}, {
|
|
168
|
+
length: number | undefined;
|
|
169
|
+
}>;
|
|
170
|
+
sourceContainerId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
171
|
+
name: "source_container_id";
|
|
172
|
+
tableName: "local_intents";
|
|
149
173
|
dataType: "string";
|
|
150
174
|
columnType: "SQLiteText";
|
|
151
175
|
data: string;
|
|
152
176
|
driverParam: string;
|
|
153
|
-
notNull:
|
|
177
|
+
notNull: false;
|
|
154
178
|
hasDefault: false;
|
|
155
179
|
isPrimaryKey: false;
|
|
156
180
|
isAutoincrement: false;
|
|
@@ -162,14 +186,31 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
162
186
|
}, {}, {
|
|
163
187
|
length: number | undefined;
|
|
164
188
|
}>;
|
|
165
|
-
|
|
166
|
-
name: "
|
|
167
|
-
tableName: "
|
|
189
|
+
replaceLinkedContainers: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
190
|
+
name: "replace_linked_containers";
|
|
191
|
+
tableName: "local_intents";
|
|
192
|
+
dataType: "boolean";
|
|
193
|
+
columnType: "SQLiteBoolean";
|
|
194
|
+
data: boolean;
|
|
195
|
+
driverParam: number;
|
|
196
|
+
notNull: true;
|
|
197
|
+
hasDefault: true;
|
|
198
|
+
isPrimaryKey: false;
|
|
199
|
+
isAutoincrement: false;
|
|
200
|
+
hasRuntimeDefault: false;
|
|
201
|
+
enumValues: undefined;
|
|
202
|
+
baseColumn: never;
|
|
203
|
+
identity: undefined;
|
|
204
|
+
generated: undefined;
|
|
205
|
+
}, {}, {}>;
|
|
206
|
+
remoteContainerId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
207
|
+
name: "remote_container_id";
|
|
208
|
+
tableName: "local_intents";
|
|
168
209
|
dataType: "string";
|
|
169
210
|
columnType: "SQLiteText";
|
|
170
211
|
data: string;
|
|
171
212
|
driverParam: string;
|
|
172
|
-
notNull:
|
|
213
|
+
notNull: false;
|
|
173
214
|
hasDefault: false;
|
|
174
215
|
isPrimaryKey: false;
|
|
175
216
|
isAutoincrement: false;
|
|
@@ -181,9 +222,9 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
181
222
|
}, {}, {
|
|
182
223
|
length: number | undefined;
|
|
183
224
|
}>;
|
|
184
|
-
|
|
185
|
-
name: "
|
|
186
|
-
tableName: "
|
|
225
|
+
remoteMetadataDocumentId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
226
|
+
name: "remote_metadata_document_id";
|
|
227
|
+
tableName: "local_intents";
|
|
187
228
|
dataType: "string";
|
|
188
229
|
columnType: "SQLiteText";
|
|
189
230
|
data: string;
|
|
@@ -200,9 +241,9 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
200
241
|
}, {}, {
|
|
201
242
|
length: number | undefined;
|
|
202
243
|
}>;
|
|
203
|
-
|
|
204
|
-
name: "
|
|
205
|
-
tableName: "
|
|
244
|
+
remoteMetadataAccessStateHash: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
245
|
+
name: "remote_metadata_access_state_hash";
|
|
246
|
+
tableName: "local_intents";
|
|
206
247
|
dataType: "string";
|
|
207
248
|
columnType: "SQLiteText";
|
|
208
249
|
data: string;
|
|
@@ -219,9 +260,9 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
219
260
|
}, {}, {
|
|
220
261
|
length: number | undefined;
|
|
221
262
|
}>;
|
|
222
|
-
|
|
223
|
-
name: "
|
|
224
|
-
tableName: "
|
|
263
|
+
syncStatus: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
264
|
+
name: "sync_status";
|
|
265
|
+
tableName: "local_intents";
|
|
225
266
|
dataType: "string";
|
|
226
267
|
columnType: "SQLiteText";
|
|
227
268
|
data: string;
|
|
@@ -238,14 +279,14 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
238
279
|
}, {}, {
|
|
239
280
|
length: number | undefined;
|
|
240
281
|
}>;
|
|
241
|
-
|
|
242
|
-
name: "
|
|
243
|
-
tableName: "
|
|
282
|
+
lastError: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
283
|
+
name: "last_error";
|
|
284
|
+
tableName: "local_intents";
|
|
244
285
|
dataType: "string";
|
|
245
286
|
columnType: "SQLiteText";
|
|
246
287
|
data: string;
|
|
247
288
|
driverParam: string;
|
|
248
|
-
notNull:
|
|
289
|
+
notNull: false;
|
|
249
290
|
hasDefault: false;
|
|
250
291
|
isPrimaryKey: false;
|
|
251
292
|
isAutoincrement: false;
|
|
@@ -257,22 +298,14 @@ export declare const documentMoveIntents: import("drizzle-orm/sqlite-core").SQLi
|
|
|
257
298
|
}, {}, {
|
|
258
299
|
length: number | undefined;
|
|
259
300
|
}>;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
/** Additional links share the parent placement intent revision and settlement. */
|
|
264
|
-
export declare const documentIntentLinkTargets: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
265
|
-
name: "document_intent_link_targets";
|
|
266
|
-
schema: undefined;
|
|
267
|
-
columns: {
|
|
268
|
-
intentId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
269
|
-
name: "intent_id";
|
|
270
|
-
tableName: "document_intent_link_targets";
|
|
301
|
+
lastAttemptedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
302
|
+
name: "last_attempted_at";
|
|
303
|
+
tableName: "local_intents";
|
|
271
304
|
dataType: "string";
|
|
272
305
|
columnType: "SQLiteText";
|
|
273
306
|
data: string;
|
|
274
307
|
driverParam: string;
|
|
275
|
-
notNull:
|
|
308
|
+
notNull: false;
|
|
276
309
|
hasDefault: false;
|
|
277
310
|
isPrimaryKey: false;
|
|
278
311
|
isAutoincrement: false;
|
|
@@ -284,28 +317,28 @@ export declare const documentIntentLinkTargets: import("drizzle-orm/sqlite-core"
|
|
|
284
317
|
}, {}, {
|
|
285
318
|
length: number | undefined;
|
|
286
319
|
}>;
|
|
287
|
-
|
|
288
|
-
name: "
|
|
289
|
-
tableName: "
|
|
320
|
+
createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
321
|
+
name: "created_at";
|
|
322
|
+
tableName: "local_intents";
|
|
290
323
|
dataType: "string";
|
|
291
324
|
columnType: "SQLiteText";
|
|
292
|
-
data:
|
|
325
|
+
data: string;
|
|
293
326
|
driverParam: string;
|
|
294
327
|
notNull: true;
|
|
295
|
-
hasDefault:
|
|
328
|
+
hasDefault: false;
|
|
296
329
|
isPrimaryKey: false;
|
|
297
330
|
isAutoincrement: false;
|
|
298
331
|
hasRuntimeDefault: false;
|
|
299
|
-
enumValues: [
|
|
332
|
+
enumValues: [string, ...string[]];
|
|
300
333
|
baseColumn: never;
|
|
301
334
|
identity: undefined;
|
|
302
335
|
generated: undefined;
|
|
303
336
|
}, {}, {
|
|
304
337
|
length: number | undefined;
|
|
305
338
|
}>;
|
|
306
|
-
|
|
307
|
-
name: "
|
|
308
|
-
tableName: "
|
|
339
|
+
updatedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
340
|
+
name: "updated_at";
|
|
341
|
+
tableName: "local_intents";
|
|
309
342
|
dataType: "string";
|
|
310
343
|
columnType: "SQLiteText";
|
|
311
344
|
data: string;
|
|
@@ -325,3 +358,44 @@ export declare const documentIntentLinkTargets: import("drizzle-orm/sqlite-core"
|
|
|
325
358
|
};
|
|
326
359
|
dialect: "sqlite";
|
|
327
360
|
}>;
|
|
361
|
+
/** The conflict target that coalesces repeated enqueues within a lane. */
|
|
362
|
+
export declare const LOCAL_INTENT_SUBJECT: (import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
363
|
+
name: "lane";
|
|
364
|
+
tableName: "local_intents";
|
|
365
|
+
dataType: "string";
|
|
366
|
+
columnType: "SQLiteText";
|
|
367
|
+
data: string;
|
|
368
|
+
driverParam: string;
|
|
369
|
+
notNull: true;
|
|
370
|
+
hasDefault: false;
|
|
371
|
+
isPrimaryKey: false;
|
|
372
|
+
isAutoincrement: false;
|
|
373
|
+
hasRuntimeDefault: false;
|
|
374
|
+
enumValues: [string, ...string[]];
|
|
375
|
+
baseColumn: never;
|
|
376
|
+
identity: undefined;
|
|
377
|
+
generated: undefined;
|
|
378
|
+
}, {}, {
|
|
379
|
+
length: number | undefined;
|
|
380
|
+
}> | import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
381
|
+
name: "subject_id";
|
|
382
|
+
tableName: "local_intents";
|
|
383
|
+
dataType: "string";
|
|
384
|
+
columnType: "SQLiteText";
|
|
385
|
+
data: string;
|
|
386
|
+
driverParam: string;
|
|
387
|
+
notNull: true;
|
|
388
|
+
hasDefault: false;
|
|
389
|
+
isPrimaryKey: false;
|
|
390
|
+
isAutoincrement: false;
|
|
391
|
+
hasRuntimeDefault: false;
|
|
392
|
+
enumValues: [string, ...string[]];
|
|
393
|
+
baseColumn: never;
|
|
394
|
+
identity: undefined;
|
|
395
|
+
generated: undefined;
|
|
396
|
+
}, {}, {
|
|
397
|
+
length: number | undefined;
|
|
398
|
+
}>)[];
|
|
399
|
+
export declare function inLocalIntentLane(lane: LocalIntentLane): SQL;
|
|
400
|
+
/** Both container lanes: what a container's own removal or reset discards. */
|
|
401
|
+
export declare function inContainerIntentLanes(): SQL;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { eq, inArray } from "drizzle-orm";
|
|
2
|
+
import { index, integer, primaryKey, sqliteTable, text, unique, } from "drizzle-orm/sqlite-core";
|
|
3
|
+
/**
|
|
4
|
+
* Lanes of the durable local outbox. Each lane keeps at most one intent per
|
|
5
|
+
* subject: a container may hold a create and a move at once, while a
|
|
6
|
+
* document's move and link coalesce into one placement intent.
|
|
7
|
+
*/
|
|
8
|
+
export const LOCAL_INTENT_LANES = {
|
|
9
|
+
containerCreate: "container.create",
|
|
10
|
+
containerMove: "container.move",
|
|
11
|
+
documentPlacement: "document.placement",
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Device-first structural writes waiting for remote replay: container creates,
|
|
15
|
+
* container moves, and document placement (move and link) intents.
|
|
16
|
+
*
|
|
17
|
+
* Every lane shares one revision protocol: `id` is rewritten on each enqueue or
|
|
18
|
+
* repair, settlement compares `(id, updatedAt)` before acting, and replay runs
|
|
19
|
+
* in `createdAt` order. Remote reset deletes the scoped intents of every lane
|
|
20
|
+
* (and re-queues creates for surviving containers); nothing here survives a
|
|
21
|
+
* reset. Durable requests that must survive reset (the principal mutation
|
|
22
|
+
* journal and organization provisioning attempts) live in their own tables.
|
|
23
|
+
*
|
|
24
|
+
* Columns:
|
|
25
|
+
* - `id`: Revision token and primary key.
|
|
26
|
+
* - `lane`: One of `LOCAL_INTENT_LANES`.
|
|
27
|
+
* - `intentType`: `container.create`, `container.move`, `document.move`, or
|
|
28
|
+
* `document.link`. A placement intent switches between the last two in place.
|
|
29
|
+
* - `subjectId`: Container id for container lanes, server document id for the
|
|
30
|
+
* placement lane.
|
|
31
|
+
* - `localId`: Local document row the placement intent updates; null for
|
|
32
|
+
* container lanes.
|
|
33
|
+
* - `targetContainerId`: Desired parent (container lanes) or target container
|
|
34
|
+
* (placement lane).
|
|
35
|
+
* - `sourceContainerId`: Parent observed when a container move was first
|
|
36
|
+
* queued, or the source container a document move unlinks.
|
|
37
|
+
* - `replaceLinkedContainers`: Placement only — the final remote link set is
|
|
38
|
+
* only the target container.
|
|
39
|
+
* - `remoteContainerId`, `remoteMetadataDocumentId`,
|
|
40
|
+
* `remoteMetadataAccessStateHash`: Remote identities a container create
|
|
41
|
+
* records once accepted.
|
|
42
|
+
* - `syncStatus`: Lane-specific state (`pending`/`synced` for creates,
|
|
43
|
+
* `pending`/`blocked` for container moves, `pending`/`blocked`/`denied`/
|
|
44
|
+
* `unavailable` for placements).
|
|
45
|
+
* - `lastError`, `lastAttemptedAt`: Replay diagnostics.
|
|
46
|
+
* - `createdAt`: FIFO replay order; `updatedAt`: last target/status change.
|
|
47
|
+
*
|
|
48
|
+
* Indexes:
|
|
49
|
+
* - `(lane, subjectId)` is unique so repeated enqueues coalesce per lane.
|
|
50
|
+
* - `(lane, syncStatus, createdAt)` lists a lane's queue in order.
|
|
51
|
+
*/
|
|
52
|
+
export const localIntents = sqliteTable("local_intents", {
|
|
53
|
+
id: text("id"),
|
|
54
|
+
lane: text("lane").notNull(),
|
|
55
|
+
intentType: text("intent_type").notNull(),
|
|
56
|
+
subjectId: text("subject_id").notNull(),
|
|
57
|
+
localId: text("local_id"),
|
|
58
|
+
targetContainerId: text("target_container_id").notNull(),
|
|
59
|
+
sourceContainerId: text("source_container_id"),
|
|
60
|
+
replaceLinkedContainers: integer("replace_linked_containers", {
|
|
61
|
+
mode: "boolean",
|
|
62
|
+
})
|
|
63
|
+
.notNull()
|
|
64
|
+
.default(false),
|
|
65
|
+
remoteContainerId: text("remote_container_id"),
|
|
66
|
+
remoteMetadataDocumentId: text("remote_metadata_document_id"),
|
|
67
|
+
remoteMetadataAccessStateHash: text("remote_metadata_access_state_hash"),
|
|
68
|
+
syncStatus: text("sync_status").notNull(),
|
|
69
|
+
lastError: text("last_error"),
|
|
70
|
+
lastAttemptedAt: text("last_attempted_at"),
|
|
71
|
+
createdAt: text("created_at").notNull(),
|
|
72
|
+
updatedAt: text("updated_at").notNull(),
|
|
73
|
+
}, (table) => [
|
|
74
|
+
primaryKey({ columns: [table.id] }),
|
|
75
|
+
unique().on(table.lane, table.subjectId),
|
|
76
|
+
index("local_intents_lane_status_created_idx").on(table.lane, table.syncStatus, table.createdAt),
|
|
77
|
+
]);
|
|
78
|
+
/** The conflict target that coalesces repeated enqueues within a lane. */
|
|
79
|
+
export const LOCAL_INTENT_SUBJECT = [localIntents.lane, localIntents.subjectId];
|
|
80
|
+
export function inLocalIntentLane(lane) {
|
|
81
|
+
return eq(localIntents.lane, lane);
|
|
82
|
+
}
|
|
83
|
+
/** Both container lanes: what a container's own removal or reset discards. */
|
|
84
|
+
export function inContainerIntentLanes() {
|
|
85
|
+
return inArray(localIntents.lane, [
|
|
86
|
+
LOCAL_INTENT_LANES.containerCreate,
|
|
87
|
+
LOCAL_INTENT_LANES.containerMove,
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=localIntentSchema.js.map
|