@tearleads/client-sdk 0.1.37 → 0.1.39
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.d.ts +3 -0
- package/client/Tearleads.js +20 -12
- package/client/Tearleads.js.map +1 -1
- package/client/organizations/organizationGroupHistory.js +1 -1
- package/client/organizations/organizationGroupHistory.js.map +1 -1
- package/client/organizations/organizationHistory.js +1 -1
- package/client/organizations/organizationHistory.js.map +1 -1
- package/client/session/index.js +2 -2
- package/client/session/index.js.map +1 -1
- package/client/session/sessionOrganizationCreation.js +1 -0
- package/client/session/sessionOrganizationCreation.js.map +1 -1
- package/client/session/sessionTypes.d.ts +3 -0
- package/client/session/sessionTypes.js.map +1 -1
- package/client/workflowRuntime.d.ts +2 -0
- package/client/workflowRuntime.js +34 -16
- package/client/workflowRuntime.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicyVerification.js +1 -2
- package/data/keyingProjectionVerification/principalPolicyVerification.js.map +1 -1
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js +39 -51
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js.map +1 -1
- package/data/keyingProjectionVerification/types.d.ts +1 -1
- package/data/keyingProjectionVerification/types.js.map +1 -1
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.d.ts +16 -0
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +23 -0
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
- package/data/persistence/organizations/organizationAccessRevocationPersistence.js +4 -0
- package/data/persistence/organizations/organizationAccessRevocationPersistence.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelPersistence.js +7 -1
- package/data/persistence/organizations/organizationReadModelPersistence.js.map +1 -1
- package/data/principals/principalRecoveryEpoch.js +18 -0
- package/data/principals/principalRecoveryEpoch.js.map +1 -0
- package/data/trustedUserIdentity/index.d.ts +1 -1
- package/data/trustedUserIdentity/index.js +1 -1
- package/data/trustedUserIdentity/index.js.map +1 -1
- package/data/trustedUserIdentity/service.d.ts +12 -1
- package/data/trustedUserIdentity/service.js +27 -3
- package/data/trustedUserIdentity/service.js.map +1 -1
- package/package.json +1 -1
- package/workflows/organizations/createOrganization.d.ts +3 -0
- package/workflows/organizations/createOrganization.js +1 -0
- package/workflows/organizations/createOrganization.js.map +1 -1
- package/workflows/organizations/currentGroupPolicyMutationContext.js +5 -5
- package/workflows/organizations/currentGroupPolicyMutationContext.js.map +1 -1
- package/workflows/principals/principalCurrentRecoveryBatch.js +1 -1
- package/workflows/principals/principalCurrentRecoveryBatch.js.map +1 -1
- package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -1
- package/workflows/principals/principalRecoveryMemo.d.ts +9 -4
- package/workflows/principals/principalRecoveryMemo.js +94 -34
- package/workflows/principals/principalRecoveryMemo.js.map +1 -1
- package/workflows/principals/principalRecoveryMemoValidity.js +33 -0
- package/workflows/principals/principalRecoveryMemoValidity.js.map +1 -0
- package/workflows/principals/principalRecoveryOutage.js +2 -2
- package/workflows/principals/principalRecoveryOutage.js.map +1 -1
- package/workflows/principals/recoverCurrentOrganizationPolicy.js +8 -3
- package/workflows/principals/recoverCurrentOrganizationPolicy.js.map +1 -1
- package/workflows/principals/recoverPrincipalPolicyHistory.js +42 -8
- package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/recoverPublicPrincipalHistory.js +5 -5
- package/workflows/principals/recoverPublicPrincipalHistory.js.map +1 -1
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.d.ts +9 -2
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +8 -2
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/runtimePolicyRecovery.js +4 -1
- package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
- package/workflows/principals/runtimePolicyWarmer.js +12 -4
- package/workflows/principals/runtimePolicyWarmer.js.map +1 -1
- package/workflows/registration/organizationProvisioningPersistence.d.ts +3 -0
- package/workflows/registration/organizationProvisioningPersistence.js +27 -1
- package/workflows/registration/organizationProvisioningPersistence.js.map +1 -1
- package/workflows/registration/persistRegistrationBootstrap.d.ts +3 -0
- package/workflows/registration/persistRegistrationBootstrap.js +7 -1
- package/workflows/registration/persistRegistrationBootstrap.js.map +1 -1
- package/workflows/registration/provisionedPrincipalHistory.d.ts +45 -0
- package/workflows/registration/provisionedPrincipalHistory.js +104 -0
- package/workflows/registration/provisionedPrincipalHistory.js.map +1 -0
- package/workflows/registration/provisioningAcknowledgement.js +18 -0
- package/workflows/registration/provisioningAcknowledgement.js.map +1 -1
- package/workflows/registration/registerIdentity.d.ts +3 -0
- package/workflows/registration/registerIdentity.js +1 -0
- package/workflows/registration/registerIdentity.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documentDiscoveryEvidencePersistence.js","sourceRoot":"","sources":["documentDiscoveryEvidencePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray, lte, min, sql } from \"drizzle-orm\";\nimport { readStringArray } from \"../../recordReaders\";\nimport {\n documentDiscoveryHeads,\n documentDiscoverySequence,\n pendingDocumentDiscoveries,\n} from \"../../sqlite/documentDiscoveryEvidenceSchema\";\nimport {\n accessManifestCheckpoints,\n documentContainerProjectionTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport { loadDocumentPurgeCheckpoint } from \"../documentPurgeCheckpointPersistence\";\nimport { heldTombstoneRetryDelayMs } from \"./containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DiscoveredDocumentCandidate,\n isDiscoveredDocumentCandidate,\n readStoredDiscoveryCandidate,\n} from \"./documentDiscoveryCandidate\";\n\nimport { isDocumentDiscoveryGenerationCurrent } from \"./documentDiscoveryReset\";\n\nexport type { DiscoveredDocumentCandidate } from \"./documentDiscoveryCandidate\";\nexport interface CachedDiscoveryHead {\n readonly organizationId: string | null;\n readonly accessEpoch: number;\n readonly accessStateHash?: string;\n readonly linkedContainerIds: readonly string[];\n}\ninterface RemovedDiscoveryPlacement {\n readonly containerId: string;\n readonly documentId: string;\n}\nexport interface DocumentDiscoveryEvidenceStore {\n begin(): Promise<number>;\n isCurrent(generation: number): Promise<boolean>;\n stage(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed?: readonly RemovedDiscoveryPlacement[],\n ): Promise<boolean>;\n pending(\n containerIds: readonly string[],\n limit: number,\n ): Promise<readonly DiscoveredDocumentCandidate[]>;\n defer(input: DiscoveredDocumentCandidate, generation: number): Promise<void>;\n acknowledge(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ): Promise<void>;\n hasPending(containerIds: readonly string[]): Promise<boolean>;\n retryDelay(containerIds: readonly string[]): Promise<number | null>;\n loadHead(\n documentId: string,\n manifestHash: string | null | undefined,\n ): Promise<CachedDiscoveryHead | null>;\n saveHead(\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ): Promise<boolean>;\n}\n\nconst pending = pendingDocumentDiscoveries;\nconst heads = documentDiscoveryHeads;\nconst ID_BATCH_SIZE = 400;\nconst batches = (ids: readonly string[]) =>\n Array.from({ length: Math.ceil(ids.length / ID_BATCH_SIZE) }, (_, i) =>\n ids.slice(i * ID_BATCH_SIZE, (i + 1) * ID_BATCH_SIZE),\n );\nconst identity = (input: DiscoveredDocumentCandidate) =>\n and(\n eq(pending.containerId, input.containerId),\n eq(pending.documentId, input.documentId),\n eq(pending.inputJson, JSON.stringify(input)),\n );\n\nclass SqlDocumentDiscoveryEvidenceStore\n implements DocumentDiscoveryEvidenceStore\n{\n constructor(\n private readonly execSql: ExecSql,\n private readonly now: () => number,\n ) {}\n\n private read = async () => {\n await ensureSqlTables(this.execSql, documentContainerProjectionTables);\n return getClientSQLitePersistenceRuntime(this.execSql).db;\n };\n private write = async <T>(\n operation: (\n db: ReturnType<typeof getClientSQLitePersistenceRuntime>[\"db\"],\n ) => Promise<T>,\n ) =>\n runSerializedSqlMutation(this.execSql, async (locked) => {\n await ensureSqlTables(locked, documentContainerProjectionTables);\n return operation(getClientSQLitePersistenceRuntime(locked).db);\n });\n begin = async () => {\n let generation = 0;\n await this.write(async (db) => {\n const [row] = await db\n .insert(documentDiscoverySequence)\n .values({ id: \"requests\", generation: 1 })\n .onConflictDoUpdate({\n target: documentDiscoverySequence.id,\n set: {\n generation: sql`${documentDiscoverySequence.generation} + 1`,\n },\n })\n .returning({ generation: documentDiscoverySequence.generation });\n if (!row || !Number.isSafeInteger(row.generation))\n throw new Error(\"Document discovery generation is exhausted\");\n generation = row.generation;\n });\n return generation;\n };\n isCurrent = async (generation: number) =>\n isDocumentDiscoveryGenerationCurrent(await this.read(), generation);\n stage = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed: readonly RemovedDiscoveryPlacement[] = [],\n ) => {\n return this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation)))\n return false;\n if (inputs.length === 0 && removed.length === 0) return true;\n await db.transaction(async (tx) => {\n for (const input of inputs) {\n if (!isDiscoveredDocumentCandidate(input)) continue;\n for (const containerId of input.listedContainerIds) {\n const rowInput = {\n ...input,\n containerId,\n listedContainerIds: [containerId],\n };\n const inputJson = JSON.stringify(rowInput);\n await tx\n .insert(pending)\n .values({\n containerId,\n documentId: input.documentId,\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: 0,\n })\n .onConflictDoUpdate({\n target: [pending.containerId, pending.documentId],\n set: {\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.retryAt} else 0 end`,\n attempts: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.attempts} else 0 end`,\n },\n setWhere: lte(pending.generation, generation),\n })\n .run();\n }\n }\n // These are untrusted, unapplied hints only. Removing an existing local\n // placement still requires the separate signed tombstone evidence gate.\n for (const row of removed) {\n await tx\n .delete(pending)\n .where(\n and(\n eq(pending.containerId, row.containerId),\n eq(pending.documentId, row.documentId),\n lte(pending.generation, generation),\n ),\n )\n .run();\n }\n });\n return true;\n });\n };\n pending = async (containerIds: readonly string[], limit: number) => {\n const db = await this.read();\n const result: DiscoveredDocumentCandidate[] = [];\n for (const batch of batches(containerIds)) {\n if (result.length >= limit) break;\n const rows = await db\n .select({ inputJson: pending.inputJson })\n .from(pending)\n .where(\n and(\n inArray(pending.containerId, [...batch]),\n lte(pending.retryAt, this.now()),\n ),\n )\n .orderBy(asc(pending.retryAt), asc(pending.documentId))\n .limit(limit - result.length);\n result.push(\n ...rows.map((row) => readStoredDiscoveryCandidate(row.inputJson)),\n );\n }\n return result;\n };\n defer = (input: DiscoveredDocumentCandidate, generation: number) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n const [row] = await db\n .select({ attempts: pending.attempts })\n .from(pending)\n .where(identity(input))\n .limit(1);\n if (!row) return;\n const attempts = Math.min(row.attempts + 1, 8);\n await db\n .update(pending)\n .set({\n attempts,\n retryAt: this.now() + heldTombstoneRetryDelayMs(attempts),\n })\n .where(identity(input))\n .run();\n });\n acknowledge = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n await db.transaction(async (tx) => {\n for (const input of inputs)\n await tx.delete(pending).where(identity(input)).run();\n });\n });\n hasPending = async (containerIds: readonly string[]) => {\n const db = await this.read();\n for (const batch of batches(containerIds)) {\n if (\n (\n await db\n .select({ documentId: pending.documentId })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]))\n .limit(1)\n ).length\n )\n return true;\n }\n return false;\n };\n retryDelay = async (containerIds: readonly string[]) => {\n const db = await this.read();\n let due: number | null = null;\n for (const batch of batches(containerIds)) {\n const [row] = await db\n .select({ due: min(pending.retryAt) })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]));\n if (row?.due !== null && row?.due !== undefined)\n due = Math.min(due ?? row.due, row.due);\n }\n return due === null ? null : Math.max(0, due - this.now());\n };\n loadHead = async (\n documentId: string,\n manifestHash: string | null | undefined,\n ) => {\n if (await loadDocumentPurgeCheckpoint(this.execSql, documentId))\n return {\n organizationId: null,\n accessEpoch: Number.MAX_SAFE_INTEGER,\n linkedContainerIds: [],\n };\n if (!manifestHash) return null;\n const db = await this.read();\n const [row] = await db\n .select()\n .from(heads)\n .where(\n and(\n eq(heads.documentId, documentId),\n eq(heads.manifestHash, manifestHash),\n ),\n )\n .limit(1);\n if (!row) return null;\n // A different verifier (for example tombstone removal) can advance the\n // signed checkpoint without updating documents.accessEpoch or this cache.\n // Old-organization pins survive recovery; only the cached signed head's\n // organization can authorize reuse. Missing pins require full verification.\n const checkpoints = await db\n .select()\n .from(accessManifestCheckpoints)\n .where(\n and(\n eq(accessManifestCheckpoints.objectKind, \"document\"),\n eq(accessManifestCheckpoints.objectId, documentId),\n eq(accessManifestCheckpoints.organizationId, row.organizationId),\n ),\n )\n .limit(1);\n if (\n checkpoints.length !== 1 ||\n checkpoints[0]?.manifestHash !== row.manifestHash ||\n checkpoints[0]?.epoch !== row.accessEpoch\n )\n return null;\n return {\n organizationId: row.organizationId,\n accessEpoch: row.accessEpoch,\n accessStateHash: row.manifestHash,\n linkedContainerIds: readStringArray(\n JSON.parse(row.linksJson),\n \"stored discovery links\",\n ),\n };\n };\n saveHead = (\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ) =>\n this.write(async (db) => {\n if (\n !head.accessStateHash ||\n !head.organizationId ||\n !(await isDocumentDiscoveryGenerationCurrent(db, generation))\n )\n return false;\n const row = {\n documentId,\n organizationId: head.organizationId,\n manifestHash: head.accessStateHash,\n accessEpoch: head.accessEpoch,\n linksJson: JSON.stringify(head.linkedContainerIds),\n };\n await db\n .insert(heads)\n .values(row)\n .onConflictDoUpdate({\n target: [heads.documentId, heads.organizationId],\n set: row,\n setWhere: lte(heads.accessEpoch, head.accessEpoch),\n })\n .run();\n return true;\n });\n}\n\nexport function createDocumentDiscoveryEvidenceStore(\n execSql: ExecSql,\n now = Date.now,\n): DocumentDiscoveryEvidenceStore {\n return new SqlDocumentDiscoveryEvidenceStore(execSql, now);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"documentDiscoveryEvidencePersistence.js","sourceRoot":"","sources":["documentDiscoveryEvidencePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray, lte, min, sql } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport { readStringArray } from \"../../recordReaders\";\nimport {\n documentDiscoveryHeads,\n documentDiscoverySequence,\n pendingDocumentDiscoveries,\n} from \"../../sqlite/documentDiscoveryEvidenceSchema\";\nimport {\n accessManifestCheckpoints,\n documentContainerProjectionTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport { loadDocumentPurgeCheckpoint } from \"../documentPurgeCheckpointPersistence\";\nimport { heldTombstoneRetryDelayMs } from \"./containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DiscoveredDocumentCandidate,\n isDiscoveredDocumentCandidate,\n readStoredDiscoveryCandidate,\n} from \"./documentDiscoveryCandidate\";\n\nimport { isDocumentDiscoveryGenerationCurrent } from \"./documentDiscoveryReset\";\n\nexport type { DiscoveredDocumentCandidate } from \"./documentDiscoveryCandidate\";\nexport interface CachedDiscoveryHead {\n readonly organizationId: string | null;\n readonly accessEpoch: number;\n readonly accessStateHash?: string;\n readonly linkedContainerIds: readonly string[];\n}\ninterface RemovedDiscoveryPlacement {\n readonly containerId: string;\n readonly documentId: string;\n}\nexport interface DocumentDiscoveryEvidenceStore {\n begin(): Promise<number>;\n isCurrent(generation: number): Promise<boolean>;\n stage(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed?: readonly RemovedDiscoveryPlacement[],\n ): Promise<boolean>;\n pending(\n containerIds: readonly string[],\n limit: number,\n ): Promise<readonly DiscoveredDocumentCandidate[]>;\n defer(input: DiscoveredDocumentCandidate, generation: number): Promise<void>;\n acknowledge(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ): Promise<void>;\n hasPending(containerIds: readonly string[]): Promise<boolean>;\n retryDelay(containerIds: readonly string[]): Promise<number | null>;\n loadHead(\n documentId: string,\n manifestHash: string | null | undefined,\n ): Promise<CachedDiscoveryHead | null>;\n saveHead(\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ): Promise<boolean>;\n}\n\nconst pending = pendingDocumentDiscoveries;\nconst heads = documentDiscoveryHeads;\nconst ID_BATCH_SIZE = 400;\nconst batches = (ids: readonly string[]) =>\n Array.from({ length: Math.ceil(ids.length / ID_BATCH_SIZE) }, (_, i) =>\n ids.slice(i * ID_BATCH_SIZE, (i + 1) * ID_BATCH_SIZE),\n );\nconst identity = (input: DiscoveredDocumentCandidate) =>\n and(\n eq(pending.containerId, input.containerId),\n eq(pending.documentId, input.documentId),\n eq(pending.inputJson, JSON.stringify(input)),\n );\n\nclass SqlDocumentDiscoveryEvidenceStore\n implements DocumentDiscoveryEvidenceStore\n{\n constructor(\n private readonly execSql: ExecSql,\n private readonly now: () => number,\n ) {}\n\n private read = async () => {\n await ensureSqlTables(this.execSql, documentContainerProjectionTables);\n return getClientSQLitePersistenceRuntime(this.execSql).db;\n };\n private write = async <T>(\n operation: (\n db: ReturnType<typeof getClientSQLitePersistenceRuntime>[\"db\"],\n ) => Promise<T>,\n ) =>\n runSerializedSqlMutation(this.execSql, async (locked) => {\n await ensureSqlTables(locked, documentContainerProjectionTables);\n return operation(getClientSQLitePersistenceRuntime(locked).db);\n });\n begin = async () => {\n let generation = 0;\n await this.write(async (db) => {\n const [row] = await db\n .insert(documentDiscoverySequence)\n .values({ id: \"requests\", generation: 1 })\n .onConflictDoUpdate({\n target: documentDiscoverySequence.id,\n set: {\n generation: sql`${documentDiscoverySequence.generation} + 1`,\n },\n })\n .returning({ generation: documentDiscoverySequence.generation });\n if (!row || !Number.isSafeInteger(row.generation))\n throw new Error(\"Document discovery generation is exhausted\");\n generation = row.generation;\n });\n return generation;\n };\n isCurrent = async (generation: number) =>\n isDocumentDiscoveryGenerationCurrent(await this.read(), generation);\n stage = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed: readonly RemovedDiscoveryPlacement[] = [],\n ) => {\n return this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation)))\n return false;\n if (inputs.length === 0 && removed.length === 0) return true;\n await db.transaction(async (tx) => {\n for (const input of inputs) {\n if (!isDiscoveredDocumentCandidate(input)) continue;\n for (const containerId of input.listedContainerIds) {\n const rowInput = {\n ...input,\n containerId,\n listedContainerIds: [containerId],\n };\n const inputJson = JSON.stringify(rowInput);\n await tx\n .insert(pending)\n .values({\n containerId,\n documentId: input.documentId,\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: 0,\n })\n .onConflictDoUpdate({\n target: [pending.containerId, pending.documentId],\n set: {\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.retryAt} else 0 end`,\n attempts: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.attempts} else 0 end`,\n },\n setWhere: lte(pending.generation, generation),\n })\n .run();\n }\n }\n // These are untrusted, unapplied hints only. Removing an existing local\n // placement still requires the separate signed tombstone evidence gate.\n for (const row of removed) {\n await tx\n .delete(pending)\n .where(\n and(\n eq(pending.containerId, row.containerId),\n eq(pending.documentId, row.documentId),\n lte(pending.generation, generation),\n ),\n )\n .run();\n }\n });\n return true;\n });\n };\n pending = async (containerIds: readonly string[], limit: number) => {\n const db = await this.read();\n const result: DiscoveredDocumentCandidate[] = [];\n for (const batch of batches(containerIds)) {\n if (result.length >= limit) break;\n const rows = await db\n .select({ inputJson: pending.inputJson })\n .from(pending)\n .where(\n and(\n inArray(pending.containerId, [...batch]),\n lte(pending.retryAt, this.now()),\n ),\n )\n .orderBy(asc(pending.retryAt), asc(pending.documentId))\n .limit(limit - result.length);\n result.push(\n ...rows.map((row) => readStoredDiscoveryCandidate(row.inputJson)),\n );\n }\n return result;\n };\n defer = (input: DiscoveredDocumentCandidate, generation: number) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n const [row] = await db\n .select({ attempts: pending.attempts })\n .from(pending)\n .where(identity(input))\n .limit(1);\n if (!row) return;\n const attempts = Math.min(row.attempts + 1, 8);\n await db\n .update(pending)\n .set({\n attempts,\n retryAt: this.now() + heldTombstoneRetryDelayMs(attempts),\n })\n .where(identity(input))\n .run();\n });\n acknowledge = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n await db.transaction(async (tx) => {\n for (const input of inputs)\n await tx.delete(pending).where(identity(input)).run();\n });\n });\n hasPending = async (containerIds: readonly string[]) => {\n const db = await this.read();\n for (const batch of batches(containerIds)) {\n if (\n (\n await db\n .select({ documentId: pending.documentId })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]))\n .limit(1)\n ).length\n )\n return true;\n }\n return false;\n };\n retryDelay = async (containerIds: readonly string[]) => {\n const db = await this.read();\n let due: number | null = null;\n for (const batch of batches(containerIds)) {\n const [row] = await db\n .select({ due: min(pending.retryAt) })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]));\n if (row?.due !== null && row?.due !== undefined)\n due = Math.min(due ?? row.due, row.due);\n }\n return due === null ? null : Math.max(0, due - this.now());\n };\n loadHead = async (\n documentId: string,\n manifestHash: string | null | undefined,\n ) => {\n if (await loadDocumentPurgeCheckpoint(this.execSql, documentId))\n return {\n organizationId: null,\n accessEpoch: Number.MAX_SAFE_INTEGER,\n linkedContainerIds: [],\n };\n if (!manifestHash) return null;\n const db = await this.read();\n const [row] = await db\n .select()\n .from(heads)\n .where(\n and(\n eq(heads.documentId, documentId),\n eq(heads.manifestHash, manifestHash),\n ),\n )\n .limit(1);\n if (!row) return null;\n // A different verifier (for example tombstone removal) can advance the\n // signed checkpoint without updating documents.accessEpoch or this cache.\n // Old-organization pins survive recovery; only the cached signed head's\n // organization can authorize reuse. Missing pins require full verification.\n const checkpoints = await db\n .select()\n .from(accessManifestCheckpoints)\n .where(\n and(\n eq(accessManifestCheckpoints.objectKind, \"document\"),\n eq(accessManifestCheckpoints.objectId, documentId),\n eq(accessManifestCheckpoints.organizationId, row.organizationId),\n ),\n )\n .limit(1);\n if (\n checkpoints.length !== 1 ||\n checkpoints[0]?.manifestHash !== row.manifestHash ||\n checkpoints[0]?.epoch !== row.accessEpoch\n )\n return null;\n return {\n organizationId: row.organizationId,\n accessEpoch: row.accessEpoch,\n accessStateHash: row.manifestHash,\n linkedContainerIds: readStringArray(\n JSON.parse(row.linksJson),\n \"stored discovery links\",\n ),\n };\n };\n saveHead = (\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ) =>\n this.write(async (db) => {\n if (\n !head.accessStateHash ||\n !head.organizationId ||\n !(await isDocumentDiscoveryGenerationCurrent(db, generation))\n )\n return false;\n const row = {\n documentId,\n organizationId: head.organizationId,\n manifestHash: head.accessStateHash,\n accessEpoch: head.accessEpoch,\n linksJson: JSON.stringify(head.linkedContainerIds),\n };\n await db\n .insert(heads)\n .values(row)\n .onConflictDoUpdate({\n target: [heads.documentId, heads.organizationId],\n set: row,\n setWhere: lte(heads.accessEpoch, head.accessEpoch),\n })\n .run();\n return true;\n });\n}\n\n/**\n * A document head this client signed, already checked against the server's\n * create acknowledgement and pinned as its access-manifest checkpoint in the\n * same transaction. It is the same signed head a discovery projection would\n * verify, so it seeds the exact-head cache; `loadHead` still reuses it only\n * while that checkpoint matches.\n */\nexport interface AuthoredDiscoveryHead {\n readonly documentId: string;\n readonly organizationId: string;\n readonly accessEpoch: number;\n readonly accessStateHash: string;\n readonly linkedContainerIds: readonly string[];\n}\n\n/** Writes authored heads inside the caller's serialized write transaction. */\nexport async function primeAuthoredDocumentDiscoveryHeads(\n execSql: ExecSql,\n authored: readonly AuthoredDiscoveryHead[],\n): Promise<void> {\n const db = getClientSQLitePersistenceRuntime(execSql).db;\n for (const head of authored) {\n const row = {\n documentId: head.documentId,\n organizationId: head.organizationId,\n manifestHash: head.accessStateHash,\n accessEpoch: head.accessEpoch,\n linksJson: JSON.stringify(uniqueSortedStrings(head.linkedContainerIds)),\n };\n await db\n .insert(heads)\n .values(row)\n .onConflictDoUpdate({\n target: [heads.documentId, heads.organizationId],\n set: row,\n setWhere: lte(heads.accessEpoch, head.accessEpoch),\n })\n .run();\n }\n}\n\nexport function createDocumentDiscoveryEvidenceStore(\n execSql: ExecSql,\n now = Date.now,\n): DocumentDiscoveryEvidenceStore {\n return new SqlDocumentDiscoveryEvidenceStore(execSql, now);\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { and, eq } from "drizzle-orm";
|
|
2
|
+
import { invalidatePrincipalRecovery } from "../../principals/principalRecoveryEpoch.js";
|
|
2
3
|
import { organizationDataUsageTables } from "../../sqlite/organizationDataUsageSchema.js";
|
|
3
4
|
import { organizationReadModelRequesters } from "../../sqlite/organizationReadModelSchema.js";
|
|
4
5
|
import { organizationReadModelTables } from "../../sqlite/schema.js";
|
|
@@ -41,5 +42,8 @@ export async function purgeOrganizationAccessProjection(input) {
|
|
|
41
42
|
tx,
|
|
42
43
|
});
|
|
43
44
|
});
|
|
45
|
+
// A denial is a realtime-hinted access change: in-flight operations recover
|
|
46
|
+
// the organization's directory again instead of reusing their memo.
|
|
47
|
+
invalidatePrincipalRecovery(input.execSql, input.organizationId);
|
|
44
48
|
}
|
|
45
49
|
//# sourceMappingURL=organizationAccessRevocationPersistence.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organizationAccessRevocationPersistence.js","sourceRoot":"","sources":["organizationAccessRevocationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAC3F,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAE1F,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,iDAAiD,EAAE,MAAM,oCAAoC,CAAC;AACvG,OAAO,EAAE,iDAAiD,EAAE,MAAM,8BAA8B,CAAC;AAEjG,MAAM,kCAAkC,GAAG;IACzC,GAAG,2BAA2B;IAC9B,GAAG,2BAA2B;CAC/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CAAC,KAIvD;IACC,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,kCAAkC,CAAC,CAAC;IACzE,MAAM,iCAAiC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,WAAW,CAChE,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,EAAE;aACL,MAAM,CAAC,+BAA+B,CAAC;aACvC,KAAK,CACJ,GAAG,CACD,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,EACD,EAAE,CAAC,+BAA+B,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAClE,CACF;aACA,GAAG,EAAE,CAAC;QACT,MAAM,CAAC,kBAAkB,CAAC,GAAG,MAAM,EAAE;aAClC,MAAM,CAAC,EAAE,MAAM,EAAE,+BAA+B,CAAC,MAAM,EAAE,CAAC;aAC1D,IAAI,CAAC,+BAA+B,CAAC;aACrC,KAAK,CACJ,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,CACF;aACA,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,MAAM,iDAAiD,CAAC;gBACtD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,EAAE;aACH,CAAC,CAAC;QACL,CAAC;QACD,MAAM,iDAAiD,CAAC;YACtD,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,EAAE;SACH,CAAC,CAAC;IACL,CAAC,CACF,CAAC;
|
|
1
|
+
{"version":3,"file":"organizationAccessRevocationPersistence.js","sourceRoot":"","sources":["organizationAccessRevocationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAC3F,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAE1F,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,iDAAiD,EAAE,MAAM,oCAAoC,CAAC;AACvG,OAAO,EAAE,iDAAiD,EAAE,MAAM,8BAA8B,CAAC;AAEjG,MAAM,kCAAkC,GAAG;IACzC,GAAG,2BAA2B;IAC9B,GAAG,2BAA2B;CAC/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CAAC,KAIvD;IACC,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,kCAAkC,CAAC,CAAC;IACzE,MAAM,iCAAiC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,WAAW,CAChE,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,EAAE;aACL,MAAM,CAAC,+BAA+B,CAAC;aACvC,KAAK,CACJ,GAAG,CACD,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,EACD,EAAE,CAAC,+BAA+B,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAClE,CACF;aACA,GAAG,EAAE,CAAC;QACT,MAAM,CAAC,kBAAkB,CAAC,GAAG,MAAM,EAAE;aAClC,MAAM,CAAC,EAAE,MAAM,EAAE,+BAA+B,CAAC,MAAM,EAAE,CAAC;aAC1D,IAAI,CAAC,+BAA+B,CAAC;aACrC,KAAK,CACJ,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,CACF;aACA,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,MAAM,iDAAiD,CAAC;gBACtD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,EAAE;aACH,CAAC,CAAC;QACL,CAAC;QACD,MAAM,iDAAiD,CAAC;YACtD,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,EAAE;SACH,CAAC,CAAC;IACL,CAAC,CACF,CAAC;IACF,4EAA4E;IAC5E,oEAAoE;IACpE,2BAA2B,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;AACnE,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { invalidatePrincipalRecovery } from \"../../principals/principalRecoveryEpoch\";\nimport { organizationDataUsageTables } from \"../../sqlite/organizationDataUsageSchema\";\nimport { organizationReadModelRequesters } from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ensureSqlTables } from \"../../sqlite/sqlTableSchema\";\nimport { purgeOrganizationDataUsageProjectionInTransaction } from \"./organizationDataUsagePersistence\";\nimport { purgeOrganizationReadModelProjectionInTransaction } from \"./organizationReadModelPurge\";\n\nconst organizationAccessProjectionTables = [\n ...organizationReadModelTables,\n ...organizationDataUsageTables,\n];\n\n/**\n * Purge one requester's access to an organization's presentation projections\n * after the server denied that requester. The denial is requester-scoped: the\n * shared read-model rows stay while another local identity in the same\n * database still holds a requester row, and organization-wide rows fall with\n * the last requester.\n */\nexport async function purgeOrganizationAccessProjection(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly requesterUserId: string;\n}): Promise<void> {\n await ensureSqlTables(input.execSql, organizationAccessProjectionTables);\n await getClientSQLitePersistenceRuntime(input.execSql).transaction(\n async (tx) => {\n await tx\n .delete(organizationReadModelRequesters)\n .where(\n and(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n eq(organizationReadModelRequesters.userId, input.requesterUserId),\n ),\n )\n .run();\n const [remainingRequester] = await tx\n .select({ userId: organizationReadModelRequesters.userId })\n .from(organizationReadModelRequesters)\n .where(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n )\n .limit(1);\n if (!remainingRequester) {\n await purgeOrganizationReadModelProjectionInTransaction({\n organizationId: input.organizationId,\n tx,\n });\n }\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId: input.organizationId,\n requesterUserId: input.requesterUserId,\n tx,\n });\n },\n );\n // A denial is a realtime-hinted access change: in-flight operations recover\n // the organization's directory again instead of reusing their memo.\n invalidatePrincipalRecovery(input.execSql, input.organizationId);\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { eq } from "drizzle-orm";
|
|
2
|
+
import { invalidatePrincipalRecovery } from "../../principals/principalRecoveryEpoch.js";
|
|
2
3
|
import { organizationDataUsageTables } from "../../sqlite/organizationDataUsageSchema.js";
|
|
3
4
|
import { organizationReadModelDirectoryUsers, organizationReadModelGroups, organizationReadModelRequesters, organizationReadModelState, } from "../../sqlite/organizationReadModelSchema.js";
|
|
4
5
|
import { organizationReadModelTables } from "../../sqlite/schema.js";
|
|
@@ -231,7 +232,7 @@ export async function applyOrganizationReadModelResponse(input) {
|
|
|
231
232
|
...organizationReadModelTables,
|
|
232
233
|
...organizationDataUsageTables,
|
|
233
234
|
]);
|
|
234
|
-
|
|
235
|
+
const disposition = await getClientSQLitePersistenceRuntime(input.execSql).transaction(async (tx) => {
|
|
235
236
|
const current = await loadCurrentState(tx, input.response.organizationId);
|
|
236
237
|
const disposition = resolveApplyDisposition({
|
|
237
238
|
current,
|
|
@@ -287,6 +288,11 @@ export async function applyOrganizationReadModelResponse(input) {
|
|
|
287
288
|
.run();
|
|
288
289
|
return "applied";
|
|
289
290
|
});
|
|
291
|
+
// An applied change follows an organization hint or mutation; in-flight
|
|
292
|
+
// operations recover the directory again instead of reusing their memo.
|
|
293
|
+
if (disposition === "applied")
|
|
294
|
+
invalidatePrincipalRecovery(input.execSql, input.response.organizationId);
|
|
295
|
+
return disposition;
|
|
290
296
|
}
|
|
291
297
|
export async function purgeOrganizationReadModelProjection(execSql, organizationId, options = {}) {
|
|
292
298
|
await ensureSqlTables(execSql, organizationReadModelTables);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organizationReadModelPersistence.js","sourceRoot":"","sources":["organizationReadModelPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ListOrganizationGroupsResponse,\n OrganizationReadModelDirectoryResponse,\n OrganizationReadModelResponse,\n} from \"@tearleads/validators/response\";\nimport { eq } from \"drizzle-orm\";\nimport { organizationDataUsageTables } from \"../../sqlite/organizationDataUsageSchema\";\nimport {\n organizationReadModelDirectoryUsers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { clearOrganizationPresentationDenialInTransaction } from \"./organizationPresentationDenialPersistence\";\nimport { applyOrganizationReadModelGrantsLane } from \"./organizationReadModelGrantPersistence\";\nimport {\n applyGroupMembershipsLane,\n assertStoredGroupMembershipBindings,\n} from \"./organizationReadModelMembershipPersistence\";\nimport {\n applyOrganizationPolicyLane,\n replaceGroupPolicyHeads,\n} from \"./organizationReadModelPolicyHeadPersistence\";\nimport {\n loadOrganizationReadModelProjectionInTransaction,\n type OrganizationReadModelProjection,\n} from \"./organizationReadModelProjection\";\nimport {\n loadWithOrganizationReadModelIntegrityPurge,\n pruneInactiveOrganizationRequestersInTransaction,\n purgeOrganizationReadModelProjectionInTransaction,\n} from \"./organizationReadModelPurge\";\nimport { assertOrganizationReadModelResponse } from \"./organizationReadModelResponseAssertions\";\n\nexport type { OrganizationReadModelProjection } from \"./organizationReadModelProjection\";\n\nconst DIRECTORY_INSERT_BATCH_SIZE = 64;\nconst GROUP_INSERT_BATCH_SIZE = 90;\n\ntype ApplyOrganizationReadModelResponseResult =\n | \"already-applied\"\n | \"applied\"\n | \"stale\";\n\ninterface CurrentOrganizationReadModelState {\n readonly cursor: string;\n readonly memberGroupId: string;\n readonly profileDocumentId: string | null;\n}\n\nasync function replaceDirectoryLane(input: {\n readonly directory: OrganizationReadModelDirectoryResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelDirectoryUsers)\n .where(\n eq(\n organizationReadModelDirectoryUsers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n\n const rows = input.directory.users.map((user, sortOrder) => ({\n organizationId: input.organizationId,\n userId: user.userId,\n sortOrder,\n signingKeyFingerprint: user.signingKeyFingerprint,\n signingPublicKey: user.signingPublicKey,\n encapsulationPublicKey: user.encapsulationPublicKey,\n encapsulationKeyFingerprint: user.encapsulationKeyFingerprint,\n isPersonalOrganizationOwner: user.isPersonalOrganizationOwner,\n createdAt: user.createdAt,\n status: user.status,\n profileDocumentId: user.profileDocumentId,\n joinedAt: user.joinedAt,\n updatedAt: user.updatedAt,\n disabledAt: user.disabledAt,\n disabledByUserId: user.disabledByUserId,\n }));\n for (\n let index = 0;\n index < rows.length;\n index += DIRECTORY_INSERT_BATCH_SIZE\n ) {\n await input.tx\n .insert(organizationReadModelDirectoryUsers)\n .values(rows.slice(index, index + DIRECTORY_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function upsertRequester(input: {\n readonly currentUserId: string;\n readonly isOrgAdmin: boolean;\n readonly now: string;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const requesterRow = {\n organizationId: input.organizationId,\n userId: input.currentUserId,\n isOrgAdmin: input.isOrgAdmin,\n updatedAt: input.now,\n };\n await input.tx\n .insert(organizationReadModelRequesters)\n .values(requesterRow)\n .onConflictDoUpdate({\n target: [\n organizationReadModelRequesters.organizationId,\n organizationReadModelRequesters.userId,\n ],\n set: {\n isOrgAdmin: requesterRow.isOrgAdmin,\n updatedAt: requesterRow.updatedAt,\n },\n })\n .run();\n // Storing an authoritative response proves access: retire the durable\n // denial marker for this scope in the same transaction.\n await clearOrganizationPresentationDenialInTransaction({\n organizationId: input.organizationId,\n projection: \"readModel\",\n requesterUserId: input.currentUserId,\n tx: input.tx,\n });\n}\n\n/** The directory lane is the authoritative roster: requester rows for users\n * it no longer lists as active must fall with it. */\nasync function pruneRequestersForDirectoryLane(input: {\n readonly currentUserId: string;\n readonly directory: OrganizationReadModelDirectoryResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await pruneInactiveOrganizationRequestersInTransaction({\n activeUserIds: input.directory.users\n .filter((user) => user.status === \"active\")\n .map((user) => user.userId),\n organizationId: input.organizationId,\n retainUserId: input.currentUserId,\n tx: input.tx,\n });\n}\n\nasync function replaceGroupsLane(input: {\n readonly groups: ListOrganizationGroupsResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const previousGroups = await input.tx\n .select({\n groupId: organizationReadModelGroups.groupId,\n name: organizationReadModelGroups.name,\n })\n .from(organizationReadModelGroups)\n .where(\n eq(organizationReadModelGroups.organizationId, input.organizationId),\n );\n const names = new Map(\n previousGroups.map((group) => [group.groupId, group.name]),\n );\n await input.tx\n .delete(organizationReadModelGroups)\n .where(eq(organizationReadModelGroups.organizationId, input.organizationId))\n .run();\n await replaceGroupPolicyHeads(input);\n\n const rows = input.groups.groups.map((group, sortOrder) => ({\n organizationId: input.organizationId,\n groupId: group.groupId,\n sortOrder,\n name: names.get(group.groupId) ?? \"\",\n createdAt: group.createdAt,\n isBuiltin: group.isBuiltin,\n stateHash: group.currentState?.stateHash ?? null,\n stateVersion: group.currentState?.version ?? null,\n keyEpoch: group.currentState?.keyEpoch ?? null,\n memberCount: group.currentState?.memberCount ?? null,\n }));\n for (let index = 0; index < rows.length; index += GROUP_INSERT_BATCH_SIZE) {\n await input.tx\n .insert(organizationReadModelGroups)\n .values(rows.slice(index, index + GROUP_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function loadCurrentState(\n tx: ClientSQLiteTransactionScope,\n organizationId: string,\n): Promise<CurrentOrganizationReadModelState | null> {\n const [current] = await tx\n .select({\n cursor: organizationReadModelState.cursor,\n memberGroupId: organizationReadModelState.memberGroupId,\n profileDocumentId: organizationReadModelState.profileDocumentId,\n })\n .from(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, organizationId))\n .limit(1);\n return current ?? null;\n}\n\nfunction resolveApplyDisposition(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): ApplyOrganizationReadModelResponseResult | null {\n const currentCursor = input.current?.cursor ?? null;\n if (currentCursor !== input.requestedCursor) {\n return currentCursor === input.response.nextCursor\n ? \"already-applied\"\n : \"stale\";\n }\n if (!input.current && input.response.mode !== \"snapshot\") {\n throw new Error(\"Organization read-model delta requires a local snapshot\");\n }\n return null;\n}\n\nasync function replaceResponseLanes(input: {\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const directory = input.response.lanes.directory;\n if (directory) {\n await replaceDirectoryLane({\n directory,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n const groups = input.response.lanes.groups;\n if (groups) {\n await replaceGroupsLane({\n groups,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n const organizationPolicy = input.response.lanes.organizationPolicy;\n if (organizationPolicy) {\n await applyOrganizationPolicyLane({\n organizationId: input.response.organizationId,\n organizationPolicy,\n tx: input.tx,\n });\n }\n const groupMemberships = input.response.lanes.groupMemberships;\n if (groupMemberships) {\n await applyGroupMembershipsLane({\n lane: groupMemberships,\n organizationId: input.response.organizationId,\n replaceAll: input.response.mode === \"snapshot\",\n tx: input.tx,\n });\n }\n const grants = input.response.lanes.grants;\n if (grants) {\n await applyOrganizationReadModelGrantsLane({\n lane: grants,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n}\n\nasync function applyResponseLanes(input: {\n readonly currentUserId: string;\n readonly memberGroupId: string;\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await replaceResponseLanes({ response: input.response, tx: input.tx });\n const directoryLane = input.response.lanes.directory;\n if (directoryLane) {\n await pruneRequestersForDirectoryLane({\n currentUserId: input.currentUserId,\n directory: directoryLane,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n if (input.response.lanes.groups || input.response.lanes.groupMemberships) {\n await assertStoredGroupMembershipBindings({\n memberGroupId: input.memberGroupId,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n}\n\nfunction requireMemberGroupId(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly response: OrganizationReadModelResponse;\n}): string {\n const memberGroupId =\n input.response.lanes.groups?.memberGroupId ?? input.current?.memberGroupId;\n if (!memberGroupId) {\n throw new Error(\"Organization read-model member group is missing\");\n }\n return memberGroupId;\n}\n\nexport async function loadOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n currentUserId: string,\n): Promise<OrganizationReadModelProjection | null> {\n return loadWithOrganizationReadModelIntegrityPurge({\n execSql,\n organizationId,\n load: (tx) =>\n loadOrganizationReadModelProjectionInTransaction({\n currentUserId,\n organizationId,\n tx,\n }),\n });\n}\n\nexport async function applyOrganizationReadModelResponse(input: {\n readonly currentUserId: string;\n readonly execSql: ExecSql;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): Promise<ApplyOrganizationReadModelResponseResult> {\n assertOrganizationReadModelResponse({\n currentUserId: input.currentUserId,\n response: input.response,\n });\n // The requester prune below also retires pruned users' usage projections.\n await ensureSqlTables(input.execSql, [\n ...organizationReadModelTables,\n ...organizationDataUsageTables,\n ]);\n\n return getClientSQLitePersistenceRuntime(input.execSql).transaction(\n async (tx) => {\n const current = await loadCurrentState(tx, input.response.organizationId);\n const disposition = resolveApplyDisposition({\n current,\n requestedCursor: input.requestedCursor,\n response: input.response,\n });\n if (disposition === \"stale\") {\n return disposition;\n }\n const now = new Date().toISOString();\n await upsertRequester({\n currentUserId: input.currentUserId,\n isOrgAdmin: input.response.currentUser.isOrgAdmin,\n now,\n organizationId: input.response.organizationId,\n tx,\n });\n if (disposition === \"already-applied\") {\n return disposition;\n }\n const memberGroupId = requireMemberGroupId({\n current,\n response: input.response,\n });\n await applyResponseLanes({\n currentUserId: input.currentUserId,\n memberGroupId,\n response: input.response,\n tx,\n });\n const directoryLane = input.response.lanes.directory;\n const stateRow = {\n organizationId: input.response.organizationId,\n cursor: input.response.nextCursor,\n profileDocumentId:\n directoryLane !== undefined\n ? directoryLane.profileDocumentId\n : (current?.profileDocumentId ?? null),\n memberGroupId,\n updatedAt: now,\n };\n await tx\n .insert(organizationReadModelState)\n .values(stateRow)\n .onConflictDoUpdate({\n target: organizationReadModelState.organizationId,\n set: {\n cursor: stateRow.cursor,\n profileDocumentId: stateRow.profileDocumentId,\n memberGroupId: stateRow.memberGroupId,\n updatedAt: stateRow.updatedAt,\n },\n })\n .run();\n\n return \"applied\";\n },\n );\n}\n\nexport async function purgeOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n options: { readonly onlyRequesterUserId?: string } = {},\n): Promise<void> {\n await ensureSqlTables(execSql, organizationReadModelTables);\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await purgeOrganizationReadModelProjectionInTransaction({\n onlyRequesterUserId: options.onlyRequesterUserId,\n organizationId,\n tx,\n });\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"organizationReadModelPersistence.js","sourceRoot":"","sources":["organizationReadModelPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ListOrganizationGroupsResponse,\n OrganizationReadModelDirectoryResponse,\n OrganizationReadModelResponse,\n} from \"@tearleads/validators/response\";\nimport { eq } from \"drizzle-orm\";\nimport { invalidatePrincipalRecovery } from \"../../principals/principalRecoveryEpoch\";\nimport { organizationDataUsageTables } from \"../../sqlite/organizationDataUsageSchema\";\nimport {\n organizationReadModelDirectoryUsers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { clearOrganizationPresentationDenialInTransaction } from \"./organizationPresentationDenialPersistence\";\nimport { applyOrganizationReadModelGrantsLane } from \"./organizationReadModelGrantPersistence\";\nimport {\n applyGroupMembershipsLane,\n assertStoredGroupMembershipBindings,\n} from \"./organizationReadModelMembershipPersistence\";\nimport {\n applyOrganizationPolicyLane,\n replaceGroupPolicyHeads,\n} from \"./organizationReadModelPolicyHeadPersistence\";\nimport {\n loadOrganizationReadModelProjectionInTransaction,\n type OrganizationReadModelProjection,\n} from \"./organizationReadModelProjection\";\nimport {\n loadWithOrganizationReadModelIntegrityPurge,\n pruneInactiveOrganizationRequestersInTransaction,\n purgeOrganizationReadModelProjectionInTransaction,\n} from \"./organizationReadModelPurge\";\nimport { assertOrganizationReadModelResponse } from \"./organizationReadModelResponseAssertions\";\n\nexport type { OrganizationReadModelProjection } from \"./organizationReadModelProjection\";\n\nconst DIRECTORY_INSERT_BATCH_SIZE = 64;\nconst GROUP_INSERT_BATCH_SIZE = 90;\n\ntype ApplyOrganizationReadModelResponseResult =\n | \"already-applied\"\n | \"applied\"\n | \"stale\";\n\ninterface CurrentOrganizationReadModelState {\n readonly cursor: string;\n readonly memberGroupId: string;\n readonly profileDocumentId: string | null;\n}\n\nasync function replaceDirectoryLane(input: {\n readonly directory: OrganizationReadModelDirectoryResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelDirectoryUsers)\n .where(\n eq(\n organizationReadModelDirectoryUsers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n\n const rows = input.directory.users.map((user, sortOrder) => ({\n organizationId: input.organizationId,\n userId: user.userId,\n sortOrder,\n signingKeyFingerprint: user.signingKeyFingerprint,\n signingPublicKey: user.signingPublicKey,\n encapsulationPublicKey: user.encapsulationPublicKey,\n encapsulationKeyFingerprint: user.encapsulationKeyFingerprint,\n isPersonalOrganizationOwner: user.isPersonalOrganizationOwner,\n createdAt: user.createdAt,\n status: user.status,\n profileDocumentId: user.profileDocumentId,\n joinedAt: user.joinedAt,\n updatedAt: user.updatedAt,\n disabledAt: user.disabledAt,\n disabledByUserId: user.disabledByUserId,\n }));\n for (\n let index = 0;\n index < rows.length;\n index += DIRECTORY_INSERT_BATCH_SIZE\n ) {\n await input.tx\n .insert(organizationReadModelDirectoryUsers)\n .values(rows.slice(index, index + DIRECTORY_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function upsertRequester(input: {\n readonly currentUserId: string;\n readonly isOrgAdmin: boolean;\n readonly now: string;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const requesterRow = {\n organizationId: input.organizationId,\n userId: input.currentUserId,\n isOrgAdmin: input.isOrgAdmin,\n updatedAt: input.now,\n };\n await input.tx\n .insert(organizationReadModelRequesters)\n .values(requesterRow)\n .onConflictDoUpdate({\n target: [\n organizationReadModelRequesters.organizationId,\n organizationReadModelRequesters.userId,\n ],\n set: {\n isOrgAdmin: requesterRow.isOrgAdmin,\n updatedAt: requesterRow.updatedAt,\n },\n })\n .run();\n // Storing an authoritative response proves access: retire the durable\n // denial marker for this scope in the same transaction.\n await clearOrganizationPresentationDenialInTransaction({\n organizationId: input.organizationId,\n projection: \"readModel\",\n requesterUserId: input.currentUserId,\n tx: input.tx,\n });\n}\n\n/** The directory lane is the authoritative roster: requester rows for users\n * it no longer lists as active must fall with it. */\nasync function pruneRequestersForDirectoryLane(input: {\n readonly currentUserId: string;\n readonly directory: OrganizationReadModelDirectoryResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await pruneInactiveOrganizationRequestersInTransaction({\n activeUserIds: input.directory.users\n .filter((user) => user.status === \"active\")\n .map((user) => user.userId),\n organizationId: input.organizationId,\n retainUserId: input.currentUserId,\n tx: input.tx,\n });\n}\n\nasync function replaceGroupsLane(input: {\n readonly groups: ListOrganizationGroupsResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const previousGroups = await input.tx\n .select({\n groupId: organizationReadModelGroups.groupId,\n name: organizationReadModelGroups.name,\n })\n .from(organizationReadModelGroups)\n .where(\n eq(organizationReadModelGroups.organizationId, input.organizationId),\n );\n const names = new Map(\n previousGroups.map((group) => [group.groupId, group.name]),\n );\n await input.tx\n .delete(organizationReadModelGroups)\n .where(eq(organizationReadModelGroups.organizationId, input.organizationId))\n .run();\n await replaceGroupPolicyHeads(input);\n\n const rows = input.groups.groups.map((group, sortOrder) => ({\n organizationId: input.organizationId,\n groupId: group.groupId,\n sortOrder,\n name: names.get(group.groupId) ?? \"\",\n createdAt: group.createdAt,\n isBuiltin: group.isBuiltin,\n stateHash: group.currentState?.stateHash ?? null,\n stateVersion: group.currentState?.version ?? null,\n keyEpoch: group.currentState?.keyEpoch ?? null,\n memberCount: group.currentState?.memberCount ?? null,\n }));\n for (let index = 0; index < rows.length; index += GROUP_INSERT_BATCH_SIZE) {\n await input.tx\n .insert(organizationReadModelGroups)\n .values(rows.slice(index, index + GROUP_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function loadCurrentState(\n tx: ClientSQLiteTransactionScope,\n organizationId: string,\n): Promise<CurrentOrganizationReadModelState | null> {\n const [current] = await tx\n .select({\n cursor: organizationReadModelState.cursor,\n memberGroupId: organizationReadModelState.memberGroupId,\n profileDocumentId: organizationReadModelState.profileDocumentId,\n })\n .from(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, organizationId))\n .limit(1);\n return current ?? null;\n}\n\nfunction resolveApplyDisposition(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): ApplyOrganizationReadModelResponseResult | null {\n const currentCursor = input.current?.cursor ?? null;\n if (currentCursor !== input.requestedCursor) {\n return currentCursor === input.response.nextCursor\n ? \"already-applied\"\n : \"stale\";\n }\n if (!input.current && input.response.mode !== \"snapshot\") {\n throw new Error(\"Organization read-model delta requires a local snapshot\");\n }\n return null;\n}\n\nasync function replaceResponseLanes(input: {\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const directory = input.response.lanes.directory;\n if (directory) {\n await replaceDirectoryLane({\n directory,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n const groups = input.response.lanes.groups;\n if (groups) {\n await replaceGroupsLane({\n groups,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n const organizationPolicy = input.response.lanes.organizationPolicy;\n if (organizationPolicy) {\n await applyOrganizationPolicyLane({\n organizationId: input.response.organizationId,\n organizationPolicy,\n tx: input.tx,\n });\n }\n const groupMemberships = input.response.lanes.groupMemberships;\n if (groupMemberships) {\n await applyGroupMembershipsLane({\n lane: groupMemberships,\n organizationId: input.response.organizationId,\n replaceAll: input.response.mode === \"snapshot\",\n tx: input.tx,\n });\n }\n const grants = input.response.lanes.grants;\n if (grants) {\n await applyOrganizationReadModelGrantsLane({\n lane: grants,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n}\n\nasync function applyResponseLanes(input: {\n readonly currentUserId: string;\n readonly memberGroupId: string;\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await replaceResponseLanes({ response: input.response, tx: input.tx });\n const directoryLane = input.response.lanes.directory;\n if (directoryLane) {\n await pruneRequestersForDirectoryLane({\n currentUserId: input.currentUserId,\n directory: directoryLane,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n if (input.response.lanes.groups || input.response.lanes.groupMemberships) {\n await assertStoredGroupMembershipBindings({\n memberGroupId: input.memberGroupId,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n}\n\nfunction requireMemberGroupId(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly response: OrganizationReadModelResponse;\n}): string {\n const memberGroupId =\n input.response.lanes.groups?.memberGroupId ?? input.current?.memberGroupId;\n if (!memberGroupId) {\n throw new Error(\"Organization read-model member group is missing\");\n }\n return memberGroupId;\n}\n\nexport async function loadOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n currentUserId: string,\n): Promise<OrganizationReadModelProjection | null> {\n return loadWithOrganizationReadModelIntegrityPurge({\n execSql,\n organizationId,\n load: (tx) =>\n loadOrganizationReadModelProjectionInTransaction({\n currentUserId,\n organizationId,\n tx,\n }),\n });\n}\n\nexport async function applyOrganizationReadModelResponse(input: {\n readonly currentUserId: string;\n readonly execSql: ExecSql;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): Promise<ApplyOrganizationReadModelResponseResult> {\n assertOrganizationReadModelResponse({\n currentUserId: input.currentUserId,\n response: input.response,\n });\n // The requester prune below also retires pruned users' usage projections.\n await ensureSqlTables(input.execSql, [\n ...organizationReadModelTables,\n ...organizationDataUsageTables,\n ]);\n\n const disposition = await getClientSQLitePersistenceRuntime(\n input.execSql,\n ).transaction(\n async (tx): Promise<ApplyOrganizationReadModelResponseResult> => {\n const current = await loadCurrentState(tx, input.response.organizationId);\n const disposition = resolveApplyDisposition({\n current,\n requestedCursor: input.requestedCursor,\n response: input.response,\n });\n if (disposition === \"stale\") {\n return disposition;\n }\n const now = new Date().toISOString();\n await upsertRequester({\n currentUserId: input.currentUserId,\n isOrgAdmin: input.response.currentUser.isOrgAdmin,\n now,\n organizationId: input.response.organizationId,\n tx,\n });\n if (disposition === \"already-applied\") {\n return disposition;\n }\n const memberGroupId = requireMemberGroupId({\n current,\n response: input.response,\n });\n await applyResponseLanes({\n currentUserId: input.currentUserId,\n memberGroupId,\n response: input.response,\n tx,\n });\n const directoryLane = input.response.lanes.directory;\n const stateRow = {\n organizationId: input.response.organizationId,\n cursor: input.response.nextCursor,\n profileDocumentId:\n directoryLane !== undefined\n ? directoryLane.profileDocumentId\n : (current?.profileDocumentId ?? null),\n memberGroupId,\n updatedAt: now,\n };\n await tx\n .insert(organizationReadModelState)\n .values(stateRow)\n .onConflictDoUpdate({\n target: organizationReadModelState.organizationId,\n set: {\n cursor: stateRow.cursor,\n profileDocumentId: stateRow.profileDocumentId,\n memberGroupId: stateRow.memberGroupId,\n updatedAt: stateRow.updatedAt,\n },\n })\n .run();\n\n return \"applied\";\n },\n );\n // An applied change follows an organization hint or mutation; in-flight\n // operations recover the directory again instead of reusing their memo.\n if (disposition === \"applied\")\n invalidatePrincipalRecovery(input.execSql, input.response.organizationId);\n return disposition;\n}\n\nexport async function purgeOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n options: { readonly onlyRequesterUserId?: string } = {},\n): Promise<void> {\n await ensureSqlTables(execSql, organizationReadModelTables);\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await purgeOrganizationReadModelProjectionInTransaction({\n onlyRequesterUserId: options.onlyRequesterUserId,\n organizationId,\n tx,\n });\n });\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Per database and organization, the number of applied organization hints in
|
|
2
|
+
// this process. Operation-scoped principal recovery records the value before
|
|
3
|
+
// producing a result and recovers again once it changes. Hints are lossy, so
|
|
4
|
+
// this only shortens reuse; each operation still discovers the current head.
|
|
5
|
+
const epochs = new WeakMap();
|
|
6
|
+
export function principalRecoveryEpoch(execSql, organizationId) {
|
|
7
|
+
return epochs.get(execSql)?.get(organizationId) ?? 0;
|
|
8
|
+
}
|
|
9
|
+
/** Evict recovered directory and Admins views for this organization. */
|
|
10
|
+
export function invalidatePrincipalRecovery(execSql, organizationId) {
|
|
11
|
+
let scoped = epochs.get(execSql);
|
|
12
|
+
if (!scoped) {
|
|
13
|
+
scoped = new Map();
|
|
14
|
+
epochs.set(execSql, scoped);
|
|
15
|
+
}
|
|
16
|
+
scoped.set(organizationId, (scoped.get(organizationId) ?? 0) + 1);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=principalRecoveryEpoch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalRecoveryEpoch.js","sourceRoot":"","sources":["principalRecoveryEpoch.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,MAAM,MAAM,GAAG,IAAI,OAAO,EAAgC,CAAC;AAE3D,MAAM,UAAU,sBAAsB,CACpC,OAAgB,EAChB,cAAsB;IAEtB,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,2BAA2B,CACzC,OAAgB,EAChB,cAAsB;IAEtB,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACpE,CAAC","sourcesContent":["import type { ExecSql } from \"../sqlite/sqlSchema\";\n\n// Per database and organization, the number of applied organization hints in\n// this process. Operation-scoped principal recovery records the value before\n// producing a result and recovers again once it changes. Hints are lossy, so\n// this only shortens reuse; each operation still discovers the current head.\nconst epochs = new WeakMap<ExecSql, Map<string, number>>();\n\nexport function principalRecoveryEpoch(\n execSql: ExecSql,\n organizationId: string,\n): number {\n return epochs.get(execSql)?.get(organizationId) ?? 0;\n}\n\n/** Evict recovered directory and Admins views for this organization. */\nexport function invalidatePrincipalRecovery(\n execSql: ExecSql,\n organizationId: string,\n): void {\n let scoped = epochs.get(execSql);\n if (!scoped) {\n scoped = new Map();\n epochs.set(execSql, scoped);\n }\n scoped.set(organizationId, (scoped.get(organizationId) ?? 0) + 1);\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createApiUserIdentitySource } from "./apiAdapter.js";
|
|
2
2
|
export { requireTrustedUserIdentityResolver } from "./requiredResolver.js";
|
|
3
|
-
export { createTrustedUserIdentityService } from "./service.js";
|
|
3
|
+
export { createTrustedUserIdentityService, loadPinnedUserIdentity, } from "./service.js";
|
|
4
4
|
export { resolveIdentityTrustDomain } from "./trustDomain.js";
|
|
5
5
|
export type { LocalUserIdentityCandidate, TrustedUserIdentity, TrustedUserIdentityResolver, } from "./types.js";
|
|
6
6
|
export { validateUserIdentityKeys } from "./validation.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createApiUserIdentitySource } from "./apiAdapter.js";
|
|
2
2
|
export { requireTrustedUserIdentityResolver } from "./requiredResolver.js";
|
|
3
|
-
export { createTrustedUserIdentityService } from "./service.js";
|
|
3
|
+
export { createTrustedUserIdentityService, loadPinnedUserIdentity, } from "./service.js";
|
|
4
4
|
export { resolveIdentityTrustDomain } from "./trustDomain.js";
|
|
5
5
|
export { validateUserIdentityKeys } from "./validation.js";
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,kCAAkC,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,kCAAkC,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EACL,gCAAgC,EAChC,sBAAsB,GACvB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAM3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export { createApiUserIdentitySource } from \"./apiAdapter\";\nexport { requireTrustedUserIdentityResolver } from \"./requiredResolver\";\nexport {\n createTrustedUserIdentityService,\n loadPinnedUserIdentity,\n} from \"./service\";\nexport { resolveIdentityTrustDomain } from \"./trustDomain\";\nexport type {\n LocalUserIdentityCandidate,\n TrustedUserIdentity,\n TrustedUserIdentityResolver,\n} from \"./types\";\nexport { validateUserIdentityKeys } from \"./validation\";\n"]}
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { ExecSql } from "../sqlite/sqlSchema.js";
|
|
2
|
+
import { type TrustedUserIdentity, type TrustedUserIdentityService, type TrustedUserIdentityServiceDependencies } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Read-only: the identity this trust store already pins for `userId`, or null.
|
|
5
|
+
* The stored keys must still match their stored fingerprints. Never inserts,
|
|
6
|
+
* compares against or replaces a pin.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadPinnedUserIdentity(input: {
|
|
9
|
+
readonly execSql: ExecSql;
|
|
10
|
+
readonly identityTrustDomain: string;
|
|
11
|
+
readonly userId: string;
|
|
12
|
+
}): Promise<TrustedUserIdentity | null>;
|
|
2
13
|
export declare function createTrustedUserIdentityService(dependencies: TrustedUserIdentityServiceDependencies): TrustedUserIdentityService;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { KeyingVerificationError } from "../../internal/crypto/index.js";
|
|
2
|
-
import { bytesToBase64 } from "../../internal/encoding/index.js";
|
|
3
|
-
import { compareOrInsertTrustedUserIdentityPin, loadTrustedUserIdForSigningKey, TrustedUserIdentityPinCorruptError, TrustedUserIdentityPinMismatchError, } from "../persistence/trustedUserIdentityPinPersistence.js";
|
|
2
|
+
import { base64ToBytes, bytesToBase64 } from "../../internal/encoding/index.js";
|
|
3
|
+
import { compareOrInsertTrustedUserIdentityPin, loadTrustedUserIdentityPin, loadTrustedUserIdForSigningKey, TrustedUserIdentityPinCorruptError, TrustedUserIdentityPinMismatchError, } from "../persistence/trustedUserIdentityPinPersistence.js";
|
|
4
4
|
import { TrustedUserIdentityPinContentionError } from "../persistence/trustedUserIdentityPinTransaction.js";
|
|
5
5
|
import { DatabaseUnavailableError } from "../sync/databaseUnavailable.js";
|
|
6
6
|
import { brandTrustedUserIdentity, TRUSTED_USER_IDENTITY_ENCAPSULATION_SUITE, TRUSTED_USER_IDENTITY_FORMAT_VERSION, TRUSTED_USER_IDENTITY_SIGNING_SUITE, } from "./types.js";
|
|
7
|
-
import { assertTrustedUserIdentityUserId, validateLocalUserIdentity, validateRemoteUserIdentity, } from "./validation.js";
|
|
7
|
+
import { assertTrustedUserIdentityUserId, validateLocalUserIdentity, validateRemoteUserIdentity, validateUserIdentityKeys, } from "./validation.js";
|
|
8
8
|
function requireTrustStore(dependencies) {
|
|
9
9
|
// A missing trust domain is a static misconfiguration and must stay loud.
|
|
10
10
|
if (!dependencies.identityTrustDomain) {
|
|
@@ -85,6 +85,30 @@ async function pinIdentity(input) {
|
|
|
85
85
|
});
|
|
86
86
|
return brandTrustedUserIdentity(input.identity);
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Read-only: the identity this trust store already pins for `userId`, or null.
|
|
90
|
+
* The stored keys must still match their stored fingerprints. Never inserts,
|
|
91
|
+
* compares against or replaces a pin.
|
|
92
|
+
*/
|
|
93
|
+
export async function loadPinnedUserIdentity(input) {
|
|
94
|
+
assertTrustedUserIdentityUserId(input.userId);
|
|
95
|
+
const pin = await loadTrustedUserIdentityPin(input);
|
|
96
|
+
if (!pin)
|
|
97
|
+
return null;
|
|
98
|
+
const keys = await validateUserIdentityKeys({
|
|
99
|
+
candidate: {
|
|
100
|
+
encapsulationPublicKey: base64ToBytes(pin.encapsulationPublicKey),
|
|
101
|
+
signingKeyFingerprint: pin.signingKeyFingerprint,
|
|
102
|
+
signingPublicKey: base64ToBytes(pin.signingPublicKey),
|
|
103
|
+
},
|
|
104
|
+
encapsulationKeyFingerprint: pin.encapsulationKeyFingerprint,
|
|
105
|
+
});
|
|
106
|
+
return brandTrustedUserIdentity({
|
|
107
|
+
...keys,
|
|
108
|
+
identityTrustDomain: input.identityTrustDomain,
|
|
109
|
+
userId: input.userId,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
88
112
|
export function createTrustedUserIdentityService(dependencies) {
|
|
89
113
|
return {
|
|
90
114
|
async boundUserId(signingKeyFingerprint) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["service.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { bytesToBase64 } from \"@tearleads/encoding\";\nimport {\n compareOrInsertTrustedUserIdentityPin,\n loadTrustedUserIdForSigningKey,\n TrustedUserIdentityPinCorruptError,\n TrustedUserIdentityPinMismatchError,\n} from \"../persistence/trustedUserIdentityPinPersistence\";\nimport { TrustedUserIdentityPinContentionError } from \"../persistence/trustedUserIdentityPinTransaction\";\nimport { DatabaseUnavailableError } from \"../sync/databaseUnavailable\";\nimport {\n brandTrustedUserIdentity,\n TRUSTED_USER_IDENTITY_ENCAPSULATION_SUITE,\n TRUSTED_USER_IDENTITY_FORMAT_VERSION,\n TRUSTED_USER_IDENTITY_SIGNING_SUITE,\n type TrustedUserIdentity,\n type TrustedUserIdentityService,\n type TrustedUserIdentityServiceDependencies,\n type ValidatedUserIdentityCandidate,\n} from \"./types\";\nimport {\n assertTrustedUserIdentityUserId,\n validateLocalUserIdentity,\n validateRemoteUserIdentity,\n} from \"./validation\";\n\nfunction requireTrustStore(\n dependencies: TrustedUserIdentityServiceDependencies,\n): {\n readonly execSql: NonNullable<\n ReturnType<TrustedUserIdentityServiceDependencies[\"getExecSql\"]>\n >;\n readonly identityTrustDomain: string;\n} {\n // A missing trust domain is a static misconfiguration and must stay loud.\n if (!dependencies.identityTrustDomain) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Trusted user identity resolution requires a stable API trust domain\",\n );\n }\n // An absent trust store is not: getExecSql() returns null exactly while the\n // database is not ready, which happens routinely when the runtime is swapped\n // under in-flight sync work. Callers that tolerate a vanishing database\n // discriminate on the error type, so this must not share the code above.\n const execSql = dependencies.getExecSql();\n if (!execSql) {\n throw new DatabaseUnavailableError(\n \"Trusted user identity resolution requires a ready SQLite trust store\",\n );\n }\n return { execSql, identityTrustDomain: dependencies.identityTrustDomain };\n}\n\nasync function compareWithDurablePin(input: {\n readonly execSql: NonNullable<\n ReturnType<TrustedUserIdentityServiceDependencies[\"getExecSql\"]>\n >;\n readonly firstSeenAt: string;\n readonly identity: ValidatedUserIdentityCandidate;\n}): Promise<void> {\n try {\n await compareOrInsertTrustedUserIdentityPin({\n execSql: input.execSql,\n pin: {\n encapsulationKeyFingerprint: input.identity.encapsulationKeyFingerprint,\n encapsulationPublicKey: bytesToBase64(\n input.identity.encapsulationPublicKey,\n ),\n encapsulationSuite: TRUSTED_USER_IDENTITY_ENCAPSULATION_SUITE,\n firstSeenAt: input.firstSeenAt,\n formatVersion: TRUSTED_USER_IDENTITY_FORMAT_VERSION,\n identityTrustDomain: input.identity.identityTrustDomain,\n signingKeyFingerprint: input.identity.signingKeyFingerprint,\n signingPublicKey: bytesToBase64(input.identity.signingPublicKey),\n signingSuite: TRUSTED_USER_IDENTITY_SIGNING_SUITE,\n userId: input.identity.userId,\n },\n });\n } catch (error) {\n if (error instanceof TrustedUserIdentityPinMismatchError) {\n throw new KeyingVerificationError(\n \"equivocation\",\n error.existing.userId !== error.candidate.userId\n ? `Signing key for ${input.identity.userId} is already bound to ${error.existing.userId}`\n : `Trusted user identity changed for ${input.identity.userId}`,\n );\n }\n if (error instanceof TrustedUserIdentityPinCorruptError) {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n `Trusted user identity pin is corrupt for ${input.identity.userId}`,\n );\n }\n if (error instanceof TrustedUserIdentityPinContentionError) {\n // Lock contention says nothing about the identity; the caller retries\n // once the trust store is free, so this must not be recordable evidence.\n throw new DatabaseUnavailableError(\n `Trusted user identity pin store is busy for ${input.identity.userId}`,\n );\n }\n throw error;\n }\n}\n\nasync function resolveCandidate(\n dependencies: TrustedUserIdentityServiceDependencies,\n userId: string,\n identityTrustDomain: string,\n): Promise<ValidatedUserIdentityCandidate | null> {\n const localUserId = dependencies.getLocalUserId();\n if (userId === localUserId) {\n const localIdentity = dependencies.getLocalIdentity(userId);\n if (!localIdentity) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Authoritative local identity keys are unavailable\",\n );\n }\n return validateLocalUserIdentity({\n candidate: localIdentity,\n identityTrustDomain,\n userId,\n });\n }\n\n const remoteIdentity = await dependencies.remoteSource.load(userId);\n return remoteIdentity\n ? validateRemoteUserIdentity({\n candidate: remoteIdentity,\n identityTrustDomain,\n requestedUserId: userId,\n })\n : null;\n}\n\nasync function pinIdentity(input: {\n readonly dependencies: TrustedUserIdentityServiceDependencies;\n readonly execSql: NonNullable<\n ReturnType<TrustedUserIdentityServiceDependencies[\"getExecSql\"]>\n >;\n readonly identity: ValidatedUserIdentityCandidate;\n}): Promise<TrustedUserIdentity> {\n await compareWithDurablePin({\n execSql: input.execSql,\n firstSeenAt: (input.dependencies.now?.() ?? new Date()).toISOString(),\n identity: input.identity,\n });\n return brandTrustedUserIdentity(input.identity);\n}\n\nexport function createTrustedUserIdentityService(\n dependencies: TrustedUserIdentityServiceDependencies,\n): TrustedUserIdentityService {\n return {\n async boundUserId(signingKeyFingerprint) {\n const { execSql, identityTrustDomain } = requireTrustStore(dependencies);\n return loadTrustedUserIdForSigningKey({\n execSql,\n identityTrustDomain,\n signingKeyFingerprint,\n });\n },\n async pinLocal(userId, candidate) {\n assertTrustedUserIdentityUserId(userId);\n const { execSql, identityTrustDomain } = requireTrustStore(dependencies);\n const identity = await validateLocalUserIdentity({\n candidate,\n identityTrustDomain,\n userId,\n });\n return pinIdentity({ dependencies, execSql, identity });\n },\n async resolve(userId) {\n assertTrustedUserIdentityUserId(userId);\n const { execSql, identityTrustDomain } = requireTrustStore(dependencies);\n try {\n const identity = await resolveCandidate(\n dependencies,\n userId,\n identityTrustDomain,\n );\n if (!identity) {\n return null;\n }\n return await pinIdentity({ dependencies, execSql, identity });\n } catch (error) {\n dependencies.remoteSource.invalidate(userId);\n throw error;\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["service.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { base64ToBytes, bytesToBase64 } from \"@tearleads/encoding\";\nimport {\n compareOrInsertTrustedUserIdentityPin,\n loadTrustedUserIdentityPin,\n loadTrustedUserIdForSigningKey,\n TrustedUserIdentityPinCorruptError,\n TrustedUserIdentityPinMismatchError,\n} from \"../persistence/trustedUserIdentityPinPersistence\";\nimport { TrustedUserIdentityPinContentionError } from \"../persistence/trustedUserIdentityPinTransaction\";\nimport type { ExecSql } from \"../sqlite/sqlSchema\";\nimport { DatabaseUnavailableError } from \"../sync/databaseUnavailable\";\nimport {\n brandTrustedUserIdentity,\n TRUSTED_USER_IDENTITY_ENCAPSULATION_SUITE,\n TRUSTED_USER_IDENTITY_FORMAT_VERSION,\n TRUSTED_USER_IDENTITY_SIGNING_SUITE,\n type TrustedUserIdentity,\n type TrustedUserIdentityService,\n type TrustedUserIdentityServiceDependencies,\n type ValidatedUserIdentityCandidate,\n} from \"./types\";\nimport {\n assertTrustedUserIdentityUserId,\n validateLocalUserIdentity,\n validateRemoteUserIdentity,\n validateUserIdentityKeys,\n} from \"./validation\";\n\nfunction requireTrustStore(\n dependencies: TrustedUserIdentityServiceDependencies,\n): {\n readonly execSql: NonNullable<\n ReturnType<TrustedUserIdentityServiceDependencies[\"getExecSql\"]>\n >;\n readonly identityTrustDomain: string;\n} {\n // A missing trust domain is a static misconfiguration and must stay loud.\n if (!dependencies.identityTrustDomain) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Trusted user identity resolution requires a stable API trust domain\",\n );\n }\n // An absent trust store is not: getExecSql() returns null exactly while the\n // database is not ready, which happens routinely when the runtime is swapped\n // under in-flight sync work. Callers that tolerate a vanishing database\n // discriminate on the error type, so this must not share the code above.\n const execSql = dependencies.getExecSql();\n if (!execSql) {\n throw new DatabaseUnavailableError(\n \"Trusted user identity resolution requires a ready SQLite trust store\",\n );\n }\n return { execSql, identityTrustDomain: dependencies.identityTrustDomain };\n}\n\nasync function compareWithDurablePin(input: {\n readonly execSql: NonNullable<\n ReturnType<TrustedUserIdentityServiceDependencies[\"getExecSql\"]>\n >;\n readonly firstSeenAt: string;\n readonly identity: ValidatedUserIdentityCandidate;\n}): Promise<void> {\n try {\n await compareOrInsertTrustedUserIdentityPin({\n execSql: input.execSql,\n pin: {\n encapsulationKeyFingerprint: input.identity.encapsulationKeyFingerprint,\n encapsulationPublicKey: bytesToBase64(\n input.identity.encapsulationPublicKey,\n ),\n encapsulationSuite: TRUSTED_USER_IDENTITY_ENCAPSULATION_SUITE,\n firstSeenAt: input.firstSeenAt,\n formatVersion: TRUSTED_USER_IDENTITY_FORMAT_VERSION,\n identityTrustDomain: input.identity.identityTrustDomain,\n signingKeyFingerprint: input.identity.signingKeyFingerprint,\n signingPublicKey: bytesToBase64(input.identity.signingPublicKey),\n signingSuite: TRUSTED_USER_IDENTITY_SIGNING_SUITE,\n userId: input.identity.userId,\n },\n });\n } catch (error) {\n if (error instanceof TrustedUserIdentityPinMismatchError) {\n throw new KeyingVerificationError(\n \"equivocation\",\n error.existing.userId !== error.candidate.userId\n ? `Signing key for ${input.identity.userId} is already bound to ${error.existing.userId}`\n : `Trusted user identity changed for ${input.identity.userId}`,\n );\n }\n if (error instanceof TrustedUserIdentityPinCorruptError) {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n `Trusted user identity pin is corrupt for ${input.identity.userId}`,\n );\n }\n if (error instanceof TrustedUserIdentityPinContentionError) {\n // Lock contention says nothing about the identity; the caller retries\n // once the trust store is free, so this must not be recordable evidence.\n throw new DatabaseUnavailableError(\n `Trusted user identity pin store is busy for ${input.identity.userId}`,\n );\n }\n throw error;\n }\n}\n\nasync function resolveCandidate(\n dependencies: TrustedUserIdentityServiceDependencies,\n userId: string,\n identityTrustDomain: string,\n): Promise<ValidatedUserIdentityCandidate | null> {\n const localUserId = dependencies.getLocalUserId();\n if (userId === localUserId) {\n const localIdentity = dependencies.getLocalIdentity(userId);\n if (!localIdentity) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Authoritative local identity keys are unavailable\",\n );\n }\n return validateLocalUserIdentity({\n candidate: localIdentity,\n identityTrustDomain,\n userId,\n });\n }\n\n const remoteIdentity = await dependencies.remoteSource.load(userId);\n return remoteIdentity\n ? validateRemoteUserIdentity({\n candidate: remoteIdentity,\n identityTrustDomain,\n requestedUserId: userId,\n })\n : null;\n}\n\nasync function pinIdentity(input: {\n readonly dependencies: TrustedUserIdentityServiceDependencies;\n readonly execSql: NonNullable<\n ReturnType<TrustedUserIdentityServiceDependencies[\"getExecSql\"]>\n >;\n readonly identity: ValidatedUserIdentityCandidate;\n}): Promise<TrustedUserIdentity> {\n await compareWithDurablePin({\n execSql: input.execSql,\n firstSeenAt: (input.dependencies.now?.() ?? new Date()).toISOString(),\n identity: input.identity,\n });\n return brandTrustedUserIdentity(input.identity);\n}\n\n/**\n * Read-only: the identity this trust store already pins for `userId`, or null.\n * The stored keys must still match their stored fingerprints. Never inserts,\n * compares against or replaces a pin.\n */\nexport async function loadPinnedUserIdentity(input: {\n readonly execSql: ExecSql;\n readonly identityTrustDomain: string;\n readonly userId: string;\n}): Promise<TrustedUserIdentity | null> {\n assertTrustedUserIdentityUserId(input.userId);\n const pin = await loadTrustedUserIdentityPin(input);\n if (!pin) return null;\n const keys = await validateUserIdentityKeys({\n candidate: {\n encapsulationPublicKey: base64ToBytes(pin.encapsulationPublicKey),\n signingKeyFingerprint: pin.signingKeyFingerprint,\n signingPublicKey: base64ToBytes(pin.signingPublicKey),\n },\n encapsulationKeyFingerprint: pin.encapsulationKeyFingerprint,\n });\n return brandTrustedUserIdentity({\n ...keys,\n identityTrustDomain: input.identityTrustDomain,\n userId: input.userId,\n });\n}\n\nexport function createTrustedUserIdentityService(\n dependencies: TrustedUserIdentityServiceDependencies,\n): TrustedUserIdentityService {\n return {\n async boundUserId(signingKeyFingerprint) {\n const { execSql, identityTrustDomain } = requireTrustStore(dependencies);\n return loadTrustedUserIdForSigningKey({\n execSql,\n identityTrustDomain,\n signingKeyFingerprint,\n });\n },\n async pinLocal(userId, candidate) {\n assertTrustedUserIdentityUserId(userId);\n const { execSql, identityTrustDomain } = requireTrustStore(dependencies);\n const identity = await validateLocalUserIdentity({\n candidate,\n identityTrustDomain,\n userId,\n });\n return pinIdentity({ dependencies, execSql, identity });\n },\n async resolve(userId) {\n assertTrustedUserIdentityUserId(userId);\n const { execSql, identityTrustDomain } = requireTrustStore(dependencies);\n try {\n const identity = await resolveCandidate(\n dependencies,\n userId,\n identityTrustDomain,\n );\n if (!identity) {\n return null;\n }\n return await pinIdentity({ dependencies, execSql, identity });\n } catch (error) {\n dependencies.remoteSource.invalidate(userId);\n throw error;\n }\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import type { CreateOrganizationRequest } from "../../internal/validators/reques
|
|
|
3
3
|
import type { CreateOrganizationResponse } from "../../internal/validators/response/index.js";
|
|
4
4
|
import type { DocumentProjectorRegistryInput } from "../../data/documents/documentKinds.js";
|
|
5
5
|
import type { ExecSqlClientLike } from "../../data/sqlite/sqlSchema.js";
|
|
6
|
+
import type { ProvisionedPrincipalHistoryPrimer } from "../registration/provisionedPrincipalHistory.js";
|
|
6
7
|
import { type ProvisionedSystemContainerSpec } from "../registration/registerIdentity.js";
|
|
7
8
|
import { removeNativeSubscriptionRestoreProvisioningAttempt } from "./organizationProvisioningAttempt.js";
|
|
8
9
|
export { removeNativeSubscriptionRestoreProvisioningAttempt };
|
|
@@ -29,6 +30,8 @@ export interface CreateOrganizationInput {
|
|
|
29
30
|
logError?: ((message: string | Error, cause?: unknown) => void) | undefined;
|
|
30
31
|
/** Overrides the seeded organization profile name; see registration. */
|
|
31
32
|
organizationProfileName?: string | undefined;
|
|
33
|
+
/** Seeds verified recovery progress for the founding policy heads. */
|
|
34
|
+
primePrincipalHistory?: ProvisionedPrincipalHistoryPrimer | undefined;
|
|
32
35
|
/**
|
|
33
36
|
* App-owned system containers to provision atomically with the new
|
|
34
37
|
* organization (e.g. a trash bin). See registration.
|
|
@@ -115,6 +115,7 @@ export async function createOrganization(input) {
|
|
|
115
115
|
log: input.log,
|
|
116
116
|
logError: input.logError,
|
|
117
117
|
organizationMetadataBootstrap: artifacts.organizationMetadataBootstrap,
|
|
118
|
+
primePrincipalHistory: input.primePrincipalHistory,
|
|
118
119
|
response,
|
|
119
120
|
rootContainer: artifacts.rootContainer,
|
|
120
121
|
rootMetadataDocument: artifacts.rootMetadataDocument,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createOrganization.js","sourceRoot":"","sources":["createOrganization.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type EncapsulationKeyPair,\n type SigningKeyPair,\n signOrganizationReplacementAuthorization,\n} from \"@tearleads/crypto\";\nimport type { CreateOrganizationRequest } from \"@tearleads/validators/request\";\nimport type { CreateOrganizationResponse } from \"@tearleads/validators/response\";\nimport type { DocumentProjectorRegistryInput } from \"../../data/documents/documentKinds\";\nimport type { ExecSqlClientLike } from \"../../data/sqlite/sqlSchema\";\nimport { createExecSql } from \"../../data/sqlite/sqlSchema\";\nimport {\n buildOrganizationProvisioningArtifacts,\n type OrganizationProvisioningArtifacts,\n type OrganizationProvisioningArtifactsInput,\n type ProvisionedSystemContainerSpec,\n persistOrganizationProvisioningState,\n} from \"../registration/registerIdentity\";\nimport {\n makeOrganizationProvisioningAttemptDurable,\n removeNativeSubscriptionRestoreProvisioningAttempt,\n} from \"./organizationProvisioningAttempt\";\nimport { pinOrganizationReplacementResponse } from \"./organizationReplacementVerification\";\n\nexport { removeNativeSubscriptionRestoreProvisioningAttempt };\n\nexport interface CreateOrganizationApi {\n createOrganization(\n request: CreateOrganizationRequest,\n ): Promise<CreateOrganizationResponse | null>;\n}\n\nexport interface CreateOrganizationInput {\n apiClient: CreateOrganizationApi;\n dbClient: ExecSqlClientLike;\n documentProjectors?: DocumentProjectorRegistryInput | undefined;\n encapsulationKeyPair: EncapsulationKeyPair;\n /**\n * Returns false once the identity that supplied the key pairs is no longer\n * active. Checked before the remote create (so a stale request is never\n * submitted) and again before local persistence: an identity switch closes\n * or renews the captured database client, so persisting would write another\n * identity's bootstrap — or throw — after the org already exists remotely.\n * The caller discards the result.\n */\n isIdentityCurrent?: (() => boolean) | undefined;\n /** @internal Forwarded to the bootstrap persist; test-only seam. */\n onPersistQueued?: (() => void) | undefined;\n log?: ((message: string) => void) | undefined;\n logError?: ((message: string | Error, cause?: unknown) => void) | undefined;\n /** Overrides the seeded organization profile name; see registration. */\n organizationProfileName?: string | undefined;\n /**\n * App-owned system containers to provision atomically with the new\n * organization (e.g. a trash bin). See registration.\n */\n provisionedSystemContainers?:\n | ReadonlyArray<ProvisionedSystemContainerSpec>\n | undefined;\n /** Purged organization whose personal-org generation this replaces. */\n replacesOrganizationId?: string | undefined;\n /** Marks and durably replays a fresh native-subscription restore target. */\n nativeSubscriptionRestore?: boolean | undefined;\n /** Overrides the seeded self roster-profile nickname; see registration. */\n rosterProfileNickname?: string | undefined;\n signingKeyPair: SigningKeyPair;\n /**\n * The already-registered user creating the organization; they become its\n * founding admin. The server independently requires this to match the\n * authenticated session.\n */\n userId: string;\n}\n\nfunction isAdoptedReplacementResponse(input: {\n creation: CreateOrganizationInput;\n request: CreateOrganizationRequest;\n response: CreateOrganizationResponse;\n}): boolean {\n if (\n input.response.organizationId === input.request.organizationId &&\n input.response.rootContainerId === input.request.rootContainerId\n ) {\n return false;\n }\n if (\n !input.creation.replacesOrganizationId ||\n input.response.userId !== input.creation.userId\n ) {\n throw new Error(\n \"Organization provisioning returned an unexpected organization\",\n );\n }\n input.creation.log?.(\n `Organization replacement adopted (${input.response.organizationId})`,\n );\n return true;\n}\n\nasync function buildOrganizationCandidate(\n input: CreateOrganizationInput,\n rootContainerId: string,\n): Promise<OrganizationProvisioningArtifacts | null> {\n const artifactsInput: OrganizationProvisioningArtifactsInput = {\n encapsulationKeyPair: input.encapsulationKeyPair,\n organizationProfileName: input.organizationProfileName,\n provisionedSystemContainers: input.provisionedSystemContainers,\n rootContainerId,\n rosterProfileNickname: input.rosterProfileNickname,\n signingKeyPair: input.signingKeyPair,\n userId: input.userId,\n };\n const artifacts: OrganizationProvisioningArtifacts =\n await buildOrganizationProvisioningArtifacts(artifactsInput);\n\n if (input.replacesOrganizationId) {\n artifacts.replacementAuthorization =\n await signOrganizationReplacementAuthorization(\n {\n ...artifacts,\n replacesOrganizationId: input.replacesOrganizationId,\n rootContainerId,\n userId: input.userId,\n },\n input.signingKeyPair,\n );\n }\n\n if (\n input.replacesOrganizationId &&\n input.isIdentityCurrent &&\n !input.isIdentityCurrent()\n ) {\n input.log?.(\n \"Organization creation aborted: identity changed while building artifacts\",\n );\n return null;\n }\n\n return artifacts;\n}\n\n/**\n * Provisions an additional organization for an already-registered user, reusing\n * the exact artifact-build and local-persistence path as registration\n * ({@link buildOrganizationProvisioningArtifacts} /\n * {@link persistOrganizationProvisioningState}). Unlike registration it mints a\n * fresh root container id, signs with the caller's existing identity, and needs\n * no auth challenge — the user is already authenticated.\n */\nexport async function createOrganization(\n input: CreateOrganizationInput,\n): Promise<CreateOrganizationResponse | null> {\n input.log?.(\"Creating organization...\");\n\n const rootContainerId = crypto.randomUUID();\n const candidateArtifacts = await buildOrganizationCandidate(\n input,\n rootContainerId,\n );\n\n if (!candidateArtifacts) return null;\n\n const durableAttempt = await makeOrganizationProvisioningAttemptDurable({\n artifacts: candidateArtifacts,\n canStartDurableMutation: input.isIdentityCurrent,\n dbClient: input.dbClient,\n nativeSubscriptionRestore: input.nativeSubscriptionRestore,\n replacesOrganizationId: input.replacesOrganizationId,\n rootContainerId,\n userId: input.userId,\n });\n if (!durableAttempt) {\n input.log?.(\n \"Organization creation aborted: identity changed before saving artifacts\",\n );\n return null;\n }\n const {\n artifacts,\n request,\n rootContainerId: durableRootContainerId,\n } = durableAttempt;\n\n if (input.isIdentityCurrent && !input.isIdentityCurrent()) {\n input.log?.(\n \"Organization creation aborted: identity changed before provisioning\",\n );\n return null;\n }\n\n const response = await input.apiClient.createOrganization(request);\n if (!response) {\n return null;\n }\n\n if (input.isIdentityCurrent && !input.isIdentityCurrent()) {\n input.log?.(\n \"Organization creation discarded: identity changed while provisioning\",\n );\n return null;\n }\n\n if (\n input.replacesOrganizationId &&\n !(await pinOrganizationReplacementResponse({\n replacesOrganizationId: input.replacesOrganizationId,\n response,\n signingPublicKey: input.signingKeyPair.signingPublicKey,\n userId: input.userId,\n execSql: createExecSql(input.dbClient),\n stillCurrent: input.isIdentityCurrent,\n }))\n )\n return null;\n\n if (isAdoptedReplacementResponse({ creation: input, request, response })) {\n // Another device authorized this winning genesis with the same identity.\n // Its encrypted bootstrap will hydrate normally; persisting\n // this device's losing candidate would instead create an unrelated local\n // root. The session reset rebinds retained local data to the winning ids.\n return response;\n }\n\n input.log?.(`Organization created (${response.organizationId})`);\n await persistOrganizationProvisioningState({\n bootstrap: artifacts.bootstrap,\n canStartDurableMutation: input.isIdentityCurrent,\n onPersistQueued: input.onPersistQueued,\n containerId: durableRootContainerId,\n dbClient: input.dbClient,\n documentProjectors: input.documentProjectors,\n initialAdminGroup: artifacts.initialAdminGroup,\n initialMemberGroup: artifacts.initialMemberGroup,\n initialOrganizationPolicy: artifacts.initialOrganizationPolicy,\n log: input.log,\n logError: input.logError,\n organizationMetadataBootstrap: artifacts.organizationMetadataBootstrap,\n response,\n rootContainer: artifacts.rootContainer,\n rootMetadataDocument: artifacts.rootMetadataDocument,\n rootMetadataDocumentRequest: artifacts.rootMetadataDocumentRequest,\n rosterProfileBootstrap: artifacts.rosterProfileBootstrap,\n systemContainerBootstraps: artifacts.systemContainerBootstraps,\n });\n\n return response;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"createOrganization.js","sourceRoot":"","sources":["createOrganization.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type EncapsulationKeyPair,\n type SigningKeyPair,\n signOrganizationReplacementAuthorization,\n} from \"@tearleads/crypto\";\nimport type { CreateOrganizationRequest } from \"@tearleads/validators/request\";\nimport type { CreateOrganizationResponse } from \"@tearleads/validators/response\";\nimport type { DocumentProjectorRegistryInput } from \"../../data/documents/documentKinds\";\nimport type { ExecSqlClientLike } from \"../../data/sqlite/sqlSchema\";\nimport { createExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { ProvisionedPrincipalHistoryPrimer } from \"../registration/provisionedPrincipalHistory\";\nimport {\n buildOrganizationProvisioningArtifacts,\n type OrganizationProvisioningArtifacts,\n type OrganizationProvisioningArtifactsInput,\n type ProvisionedSystemContainerSpec,\n persistOrganizationProvisioningState,\n} from \"../registration/registerIdentity\";\nimport {\n makeOrganizationProvisioningAttemptDurable,\n removeNativeSubscriptionRestoreProvisioningAttempt,\n} from \"./organizationProvisioningAttempt\";\nimport { pinOrganizationReplacementResponse } from \"./organizationReplacementVerification\";\n\nexport { removeNativeSubscriptionRestoreProvisioningAttempt };\n\nexport interface CreateOrganizationApi {\n createOrganization(\n request: CreateOrganizationRequest,\n ): Promise<CreateOrganizationResponse | null>;\n}\n\nexport interface CreateOrganizationInput {\n apiClient: CreateOrganizationApi;\n dbClient: ExecSqlClientLike;\n documentProjectors?: DocumentProjectorRegistryInput | undefined;\n encapsulationKeyPair: EncapsulationKeyPair;\n /**\n * Returns false once the identity that supplied the key pairs is no longer\n * active. Checked before the remote create (so a stale request is never\n * submitted) and again before local persistence: an identity switch closes\n * or renews the captured database client, so persisting would write another\n * identity's bootstrap — or throw — after the org already exists remotely.\n * The caller discards the result.\n */\n isIdentityCurrent?: (() => boolean) | undefined;\n /** @internal Forwarded to the bootstrap persist; test-only seam. */\n onPersistQueued?: (() => void) | undefined;\n log?: ((message: string) => void) | undefined;\n logError?: ((message: string | Error, cause?: unknown) => void) | undefined;\n /** Overrides the seeded organization profile name; see registration. */\n organizationProfileName?: string | undefined;\n /** Seeds verified recovery progress for the founding policy heads. */\n primePrincipalHistory?: ProvisionedPrincipalHistoryPrimer | undefined;\n /**\n * App-owned system containers to provision atomically with the new\n * organization (e.g. a trash bin). See registration.\n */\n provisionedSystemContainers?:\n | ReadonlyArray<ProvisionedSystemContainerSpec>\n | undefined;\n /** Purged organization whose personal-org generation this replaces. */\n replacesOrganizationId?: string | undefined;\n /** Marks and durably replays a fresh native-subscription restore target. */\n nativeSubscriptionRestore?: boolean | undefined;\n /** Overrides the seeded self roster-profile nickname; see registration. */\n rosterProfileNickname?: string | undefined;\n signingKeyPair: SigningKeyPair;\n /**\n * The already-registered user creating the organization; they become its\n * founding admin. The server independently requires this to match the\n * authenticated session.\n */\n userId: string;\n}\n\nfunction isAdoptedReplacementResponse(input: {\n creation: CreateOrganizationInput;\n request: CreateOrganizationRequest;\n response: CreateOrganizationResponse;\n}): boolean {\n if (\n input.response.organizationId === input.request.organizationId &&\n input.response.rootContainerId === input.request.rootContainerId\n ) {\n return false;\n }\n if (\n !input.creation.replacesOrganizationId ||\n input.response.userId !== input.creation.userId\n ) {\n throw new Error(\n \"Organization provisioning returned an unexpected organization\",\n );\n }\n input.creation.log?.(\n `Organization replacement adopted (${input.response.organizationId})`,\n );\n return true;\n}\n\nasync function buildOrganizationCandidate(\n input: CreateOrganizationInput,\n rootContainerId: string,\n): Promise<OrganizationProvisioningArtifacts | null> {\n const artifactsInput: OrganizationProvisioningArtifactsInput = {\n encapsulationKeyPair: input.encapsulationKeyPair,\n organizationProfileName: input.organizationProfileName,\n provisionedSystemContainers: input.provisionedSystemContainers,\n rootContainerId,\n rosterProfileNickname: input.rosterProfileNickname,\n signingKeyPair: input.signingKeyPair,\n userId: input.userId,\n };\n const artifacts: OrganizationProvisioningArtifacts =\n await buildOrganizationProvisioningArtifacts(artifactsInput);\n\n if (input.replacesOrganizationId) {\n artifacts.replacementAuthorization =\n await signOrganizationReplacementAuthorization(\n {\n ...artifacts,\n replacesOrganizationId: input.replacesOrganizationId,\n rootContainerId,\n userId: input.userId,\n },\n input.signingKeyPair,\n );\n }\n\n if (\n input.replacesOrganizationId &&\n input.isIdentityCurrent &&\n !input.isIdentityCurrent()\n ) {\n input.log?.(\n \"Organization creation aborted: identity changed while building artifacts\",\n );\n return null;\n }\n\n return artifacts;\n}\n\n/**\n * Provisions an additional organization for an already-registered user, reusing\n * the exact artifact-build and local-persistence path as registration\n * ({@link buildOrganizationProvisioningArtifacts} /\n * {@link persistOrganizationProvisioningState}). Unlike registration it mints a\n * fresh root container id, signs with the caller's existing identity, and needs\n * no auth challenge — the user is already authenticated.\n */\nexport async function createOrganization(\n input: CreateOrganizationInput,\n): Promise<CreateOrganizationResponse | null> {\n input.log?.(\"Creating organization...\");\n\n const rootContainerId = crypto.randomUUID();\n const candidateArtifacts = await buildOrganizationCandidate(\n input,\n rootContainerId,\n );\n\n if (!candidateArtifacts) return null;\n\n const durableAttempt = await makeOrganizationProvisioningAttemptDurable({\n artifacts: candidateArtifacts,\n canStartDurableMutation: input.isIdentityCurrent,\n dbClient: input.dbClient,\n nativeSubscriptionRestore: input.nativeSubscriptionRestore,\n replacesOrganizationId: input.replacesOrganizationId,\n rootContainerId,\n userId: input.userId,\n });\n if (!durableAttempt) {\n input.log?.(\n \"Organization creation aborted: identity changed before saving artifacts\",\n );\n return null;\n }\n const {\n artifacts,\n request,\n rootContainerId: durableRootContainerId,\n } = durableAttempt;\n\n if (input.isIdentityCurrent && !input.isIdentityCurrent()) {\n input.log?.(\n \"Organization creation aborted: identity changed before provisioning\",\n );\n return null;\n }\n\n const response = await input.apiClient.createOrganization(request);\n if (!response) {\n return null;\n }\n\n if (input.isIdentityCurrent && !input.isIdentityCurrent()) {\n input.log?.(\n \"Organization creation discarded: identity changed while provisioning\",\n );\n return null;\n }\n\n if (\n input.replacesOrganizationId &&\n !(await pinOrganizationReplacementResponse({\n replacesOrganizationId: input.replacesOrganizationId,\n response,\n signingPublicKey: input.signingKeyPair.signingPublicKey,\n userId: input.userId,\n execSql: createExecSql(input.dbClient),\n stillCurrent: input.isIdentityCurrent,\n }))\n )\n return null;\n\n if (isAdoptedReplacementResponse({ creation: input, request, response })) {\n // Another device authorized this winning genesis with the same identity.\n // Its encrypted bootstrap will hydrate normally; persisting\n // this device's losing candidate would instead create an unrelated local\n // root. The session reset rebinds retained local data to the winning ids.\n return response;\n }\n\n input.log?.(`Organization created (${response.organizationId})`);\n await persistOrganizationProvisioningState({\n bootstrap: artifacts.bootstrap,\n canStartDurableMutation: input.isIdentityCurrent,\n onPersistQueued: input.onPersistQueued,\n containerId: durableRootContainerId,\n dbClient: input.dbClient,\n documentProjectors: input.documentProjectors,\n initialAdminGroup: artifacts.initialAdminGroup,\n initialMemberGroup: artifacts.initialMemberGroup,\n initialOrganizationPolicy: artifacts.initialOrganizationPolicy,\n log: input.log,\n logError: input.logError,\n organizationMetadataBootstrap: artifacts.organizationMetadataBootstrap,\n primePrincipalHistory: input.primePrincipalHistory,\n response,\n rootContainer: artifacts.rootContainer,\n rootMetadataDocument: artifacts.rootMetadataDocument,\n rootMetadataDocumentRequest: artifacts.rootMetadataDocumentRequest,\n rosterProfileBootstrap: artifacts.rosterProfileBootstrap,\n systemContainerBootstraps: artifacts.systemContainerBootstraps,\n });\n\n return response;\n}\n"]}
|