@tearleads/client-sdk 0.1.37 → 0.1.38
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/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/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/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/package.json +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
|
@@ -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"]}
|
package/package.json
CHANGED
|
@@ -30,6 +30,7 @@ export async function loadCurrentGroupPolicyMutationContext(input) {
|
|
|
30
30
|
{ head: adminHead, projection: authority.admins.policy.projection },
|
|
31
31
|
],
|
|
32
32
|
};
|
|
33
|
+
// A reused citation keeps the lifetime of the read that produced it.
|
|
33
34
|
const references = new Map();
|
|
34
35
|
return {
|
|
35
36
|
adminGroupId: authority.descriptor.adminGroupId,
|
|
@@ -60,18 +61,17 @@ export async function loadCurrentGroupPolicyMutationContext(input) {
|
|
|
60
61
|
if (oldest !== undefined)
|
|
61
62
|
references.delete(oldest);
|
|
62
63
|
}
|
|
63
|
-
pending = authority
|
|
64
|
-
.readGroup(input.groupId, owned)
|
|
65
|
-
.then((selected) => selected.policy);
|
|
64
|
+
pending = authority.readGroup(input.groupId, owned);
|
|
66
65
|
references.set(key, pending);
|
|
67
66
|
}
|
|
68
|
-
const
|
|
67
|
+
const selected = await pending.catch((error) => {
|
|
69
68
|
if (references.get(key) === pending)
|
|
70
69
|
references.delete(key);
|
|
71
70
|
throw error;
|
|
72
71
|
});
|
|
73
72
|
assertProjectionVerificationCurrent(stillCurrent);
|
|
74
|
-
|
|
73
|
+
assertProjectionVerificationCurrent(selected.stillCurrent);
|
|
74
|
+
return selected.policy;
|
|
75
75
|
},
|
|
76
76
|
stillCurrent,
|
|
77
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"currentGroupPolicyMutationContext.js","sourceRoot":"","sources":["currentGroupPolicyMutationContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,4BAA4B,GAE7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,kCAAkC,EAAE,MAAM,2DAA2D,CAAC;AAC/G,OAAO,EAAE,4BAA4B,EAAE,MAAM,uDAAuD,CAAC;AAGrG,OAAO,EAAE,mDAAmD,EAAE,MAAM,iCAAiC,CAAC;AACtG,OAAO,EAAE,wCAAwC,EAAE,MAAM,wCAAwC,CAAC;AAClG,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAE3E,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,qCAAqC,CAAC,KAY3D;IACC,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,sBAAsB,EAAE,GACzC,MAAM,wCAAwC,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,GAAG,EAAE,CACxB,KAAK,CAAC,YAAY,EAAE,IAAI,SAAS,CAAC,YAAY,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;IAC3E,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,0BAA0B,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IAChE,2BAA2B,CACzB,KAAK,CAAC,OAAO,EACb,sBAAsB,EACtB,KAAK,CAAC,YAAY,CACnB,CAAC;IACF,MAAM,kCAAkC,CAAC;QACvC,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC;QAC/C,YAAY;KACb,CAAC,CAAC;IACH,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,4BAA4B,CAC5C,SAAS,CAAC,UAAU,EACpB,SAAS,CAAC,UAAU,CAAC,YAAY,CAClC,CAAC;IACF,MAAM,iBAAiB,GAAqC;QAC1D,WAAW,EAAE,SAAS;QACtB,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;SACpE;KACF,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"currentGroupPolicyMutationContext.js","sourceRoot":"","sources":["currentGroupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type PrincipalPolicyExternalAuthority,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n type VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { createRuntimePrincipalPolicyCurrentResolver } from \"../principals/runtimePolicyRecovery\";\nimport { createSelectedCurrentGroupMetadataContainerVerifier } from \"./currentGroupMetadataAuthority\";\nimport { loadCurrentOrganizationMutationAuthority } from \"./currentOrganizationMutationAuthority\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\n\n/** Exact current directory, strict Admins and group evidence for one mutation. */\nexport async function loadCurrentGroupPolicyMutationContext(input: {\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly resolveCurrentPolicy: NonNullable<\n ReturnType<typeof createRuntimePrincipalPolicyCurrentResolver>\n >;\n readonly recoverPendingPrincipalMutation: (\n organizationId: string,\n ) => Promise<void>;\n readonly stillCurrent: () => boolean;\n}) {\n input = { ...input };\n const { authority, currentOrgAdminUserIds } =\n await loadCurrentOrganizationMutationAuthority(input);\n const group = await authority.readGroup(input.groupId);\n const stillCurrent = () =>\n input.stillCurrent() && authority.stillCurrent() && group.stillCurrent();\n assertProjectionVerificationCurrent(stillCurrent);\n assertGroupMetadataBinding(group.current, authority.descriptor);\n requireSignerCanManageGroup(\n group.current,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql: input.execSql,\n organizationId: input.organizationId,\n policies: [...group.dependencies, group.policy],\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const adminHead = requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.adminGroupId,\n );\n const externalAuthority: PrincipalPolicyExternalAuthority = {\n currentHead: adminHead,\n states: [\n { head: adminHead, projection: authority.admins.policy.projection },\n ],\n };\n // A reused citation keeps the lifetime of the read that produced it.\n const references = new Map<\n string,\n Promise<{\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly stillCurrent: () => boolean;\n }>\n >();\n return {\n adminGroupId: authority.descriptor.adminGroupId,\n adminCurrent: authority.admins,\n currentPolicy: group.current,\n verifiedCurrentPolicy: group.policy,\n verifyMetadataContainer:\n createSelectedCurrentGroupMetadataContainerVerifier({\n authority,\n organizationId: input.organizationId,\n stillCurrent,\n }),\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup:\n input.groupId === authority.descriptor.adminGroupId,\n localPolicyCheckpoint: group.policy.checkpoint,\n memberGroupId: authority.descriptor.memberGroupId,\n organizationDescriptor: authority.descriptor,\n organizationCurrent: authority.directory,\n async readPredecessorReference(reference: ReferencedPrincipalHead) {\n assertProjectionVerificationCurrent(stillCurrent);\n const owned = structuredClone(reference);\n const key = serializeKeyingCanonicalJson({ ...owned });\n let pending = references.get(key);\n if (!pending) {\n // Keep at most one bounded selection's citations, even across many paths.\n if (references.size === 128) {\n const oldest = references.keys().next().value;\n if (oldest !== undefined) references.delete(oldest);\n }\n pending = authority.readGroup(input.groupId, owned);\n references.set(key, pending);\n }\n const selected = await pending.catch((error: unknown) => {\n if (references.get(key) === pending) references.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(stillCurrent);\n assertProjectionVerificationCurrent(selected.stillCurrent);\n return selected.policy;\n },\n stillCurrent,\n };\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { recoverCurrentOrganizationPolicy } from "./recoverCurrentOrganizationPolicy.js";
|
|
2
2
|
import { createScopedPrincipalPolicyHistoryBatch } from "./recoverScopedPrincipalPolicyHistory.js";
|
|
3
|
-
/** Discovery and selected references share one authenticated
|
|
3
|
+
/** Discovery and selected references share one authenticated batch view. */
|
|
4
4
|
export function createPrincipalCurrentRecoveryBatch() {
|
|
5
5
|
const memo = {
|
|
6
6
|
directories: new Map(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalCurrentRecoveryBatch.js","sourceRoot":"","sources":["principalCurrentRecoveryBatch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,uCAAuC,CAAC;AAEhG,
|
|
1
|
+
{"version":3,"file":"principalCurrentRecoveryBatch.js","sourceRoot":"","sources":["principalCurrentRecoveryBatch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,uCAAuC,CAAC;AAEhG,4EAA4E;AAC5E,MAAM,UAAU,mCAAmC;IACjD,MAAM,IAAI,GAA0B;QAClC,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,MAAM,EAAE,IAAI,GAAG,EAAE;KAClB,CAAC;IACF,OAAO;QACL,OAAO,EAAE,uCAAuC,CAAC,IAAI,CAAC;QACtD,QAAQ,EAAE,CAAC,KAA+B,EAAE,EAAE,CAC5C,gCAAgC,CAAC,KAAK,EAAE,IAAI,CAAC;KAChD,CAAC;AACJ,CAAC","sourcesContent":["import type { PrincipalRecoveryContext } from \"./principalRecoveryDirectory\";\nimport type { PrincipalRecoveryMemo } from \"./principalRecoveryMemo\";\nimport { recoverCurrentOrganizationPolicy } from \"./recoverCurrentOrganizationPolicy\";\nimport { createScopedPrincipalPolicyHistoryBatch } from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Discovery and selected references share one authenticated batch view. */\nexport function createPrincipalCurrentRecoveryBatch() {\n const memo: PrincipalRecoveryMemo = {\n directories: new Map(),\n admins: new Map(),\n };\n return {\n recover: createScopedPrincipalPolicyHistoryBatch(memo),\n discover: (input: PrincipalRecoveryContext) =>\n recoverCurrentOrganizationPolicy(input, memo),\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalRecoveryLocalPreference.js","sourceRoot":"","sources":["principalRecoveryLocalPreference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"principalRecoveryLocalPreference.js","sourceRoot":"","sources":["principalRecoveryLocalPreference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;AAM/E,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,OAA2C,EAC3C,OAAmD,EACnD,kBAA2B,EAC3B,wBAAwB,GAAG,KAAK;IAEhC,IAAI,kBAAkB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC;gBAC1B,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;YACH,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,wBAAwB;gBACxB,OAAO,CAAC,WAAW;gBACnB,6BAA6B,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC;gBAEpE,OAAO,KAAK,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,CAAC,CAAC,KAAK,YAAY,iCAAiC,CAAC;gBACrD,CAAC,CACC,KAAK,YAAY,uBAAuB;oBACxC,KAAK,CAAC,IAAI,KAAK,oBAAoB,CACpC;gBAED,MAAM,KAAK,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,kCAAkC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { PrincipalHistoryRecoveryRaceError } from \"./principalHistoryRecoveryTypes\";\nimport { recoverWithPrincipalOutageFallback } from \"./principalRecoveryOutage\";\nimport type {\n RecoverScopedPrincipalPolicyHistoryOptions,\n ScopedPrincipalPolicyRecoveryBatch,\n} from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Reuse exact current artifacts, or a proved historical page within a newer prefix. */\nexport async function recoverWithPrincipalLocalPreference(\n recover: ScopedPrincipalPolicyRecoveryBatch,\n options: RecoverScopedPrincipalPolicyHistoryOptions,\n preferLocalCurrent: boolean,\n allowHistoricalReference = false,\n) {\n if (preferLocalCurrent && !options.offline) {\n try {\n const local = await recover({\n ...options,\n offline: true,\n });\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n allowHistoricalReference ||\n options.historyPage ||\n principalHeadMatchesReference(local.policy.state, options.reference)\n )\n return local;\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n !(error instanceof PrincipalHistoryRecoveryRaceError) &&\n !(\n error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\"\n )\n )\n throw error;\n }\n }\n return recoverWithPrincipalOutageFallback(recover, options);\n}\n"]}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { type ReferencedPrincipalHead } from "../../internal/crypto/index.js";
|
|
2
2
|
import type { RecoveredPrincipalPolicyHistory } from "./principalHistoryRecoveryTypes.js";
|
|
3
3
|
import { type PrincipalRecoveryContext, type RecoveredPolicyDirectory } from "./principalRecoveryDirectory.js";
|
|
4
|
-
|
|
5
|
-
value: T;
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
interface MemoEntry<T> {
|
|
5
|
+
readonly value: T;
|
|
6
|
+
/** The producing call's private lease and caller lifetime. */
|
|
7
|
+
readonly stillCurrent: () => boolean;
|
|
8
|
+
/** Organization hint epoch observed before the reads that produced value. */
|
|
9
|
+
readonly epoch: number;
|
|
10
|
+
}
|
|
11
|
+
type Memo<T> = Map<string, Promise<MemoEntry<T>>>;
|
|
8
12
|
export interface PrincipalRecoveryMemo {
|
|
9
13
|
readonly directories: Memo<RecoveredPolicyDirectory>;
|
|
10
14
|
readonly admins: Memo<RecoveredPrincipalPolicyHistory>;
|
|
11
15
|
}
|
|
12
16
|
export declare function createPrincipalRecoveryReader(input: PrincipalRecoveryContext, memo?: PrincipalRecoveryMemo): {
|
|
17
|
+
stillCurrent: () => boolean;
|
|
13
18
|
directory: (references: readonly ReferencedPrincipalHead[]) => Promise<RecoveredPolicyDirectory>;
|
|
14
19
|
admins: (expectedHead: ReferencedPrincipalHead, retainedReferences?: readonly ReferencedPrincipalHead[]) => Promise<RecoveredPrincipalPolicyHistory>;
|
|
15
20
|
};
|
|
@@ -2,23 +2,81 @@ import { KeyingVerificationError, serializeKeyingCanonicalJson, } from "../../in
|
|
|
2
2
|
import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
|
|
3
3
|
import { principalHeadMatchesReference } from "../../data/principals/organizationAuthorityDescriptor.js";
|
|
4
4
|
import { recoverPolicyDirectory, } from "./principalRecoveryDirectory.js";
|
|
5
|
+
import { adminsMemoValidity, directoryMemoValidity, } from "./principalRecoveryMemoValidity.js";
|
|
5
6
|
import { recoverPrincipalPolicyHistory } from "./recoverPrincipalPolicyHistory.js";
|
|
6
|
-
/**
|
|
7
|
-
|
|
7
|
+
/** Synchronous: no hint and no lease end since the entry's reads began. */
|
|
8
|
+
function entryLive(entry, validity) {
|
|
9
|
+
return entry.epoch === validity.epoch() && entry.stillCurrent();
|
|
10
|
+
}
|
|
11
|
+
function evict(memo, key, pending) {
|
|
12
|
+
if (memo?.get(key) === pending)
|
|
13
|
+
memo.delete(key);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Return a live entry, evicting one whose producer lease ended, that predates
|
|
17
|
+
* an organization hint, or that a durable pin has moved past. Liveness is
|
|
18
|
+
* checked again after every await, so a hint or lease end during the pin
|
|
19
|
+
* reads also evicts. The caller rechecks synchronously before use. A pin read
|
|
20
|
+
* is a snapshot; a pin that advances later is caught by the checkpoint reload
|
|
21
|
+
* in recovery, the caller's checkpoint refresh, and atomic admission.
|
|
22
|
+
*/
|
|
23
|
+
async function reusableEntry(memo, key, validity) {
|
|
24
|
+
const pending = memo?.get(key);
|
|
25
|
+
if (!pending)
|
|
26
|
+
return undefined;
|
|
27
|
+
const entry = await pending.catch(() => undefined);
|
|
28
|
+
if (entry &&
|
|
29
|
+
entryLive(entry, validity) &&
|
|
30
|
+
(await validity.covers(entry.value)) &&
|
|
31
|
+
entryLive(entry, validity))
|
|
32
|
+
return { entry, pending };
|
|
33
|
+
evict(memo, key, pending);
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Share authenticated results within one workflow operation. A later caller
|
|
38
|
+
* never receives a result whose producer lease ended, that predates an
|
|
39
|
+
* organization hint, or that a durable pin has moved past; it recovers again.
|
|
40
|
+
* A result derived from a reused source keeps the source's epoch, so a hint
|
|
41
|
+
* that arrives before it is stored still evicts it. Every value is handed out
|
|
42
|
+
* with the consumer's lifetime and its producer's, so a producer that expires
|
|
43
|
+
* after hand-out still invalidates the consumer's use.
|
|
44
|
+
*/
|
|
45
|
+
async function memoPrincipalRecovery(memo, key, stillCurrent, validity, sourceEpoch, work) {
|
|
8
46
|
assertProjectionVerificationCurrent(stillCurrent);
|
|
9
|
-
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
|
|
47
|
+
const reused = await reusableEntry(memo, key, validity);
|
|
48
|
+
if (reused) {
|
|
49
|
+
const { entry } = reused;
|
|
50
|
+
if (entryLive(entry, validity)) {
|
|
51
|
+
assertProjectionVerificationCurrent(stillCurrent);
|
|
52
|
+
return {
|
|
53
|
+
value: entry.value,
|
|
54
|
+
stillCurrent: () => stillCurrent() && entry.stillCurrent(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
evict(memo, key, reused.pending);
|
|
13
58
|
}
|
|
59
|
+
// Observed before work starts its reads; a derived result keeps its source's.
|
|
60
|
+
const epoch = sourceEpoch ?? validity.epoch();
|
|
61
|
+
const pending = work().then((value) => ({ value, stillCurrent, epoch }));
|
|
62
|
+
memo?.set(key, pending);
|
|
14
63
|
const result = await pending.catch((error) => {
|
|
15
|
-
|
|
16
|
-
memo.delete(key);
|
|
64
|
+
evict(memo, key, pending);
|
|
17
65
|
throw error;
|
|
18
66
|
});
|
|
67
|
+
// A hint during production leaves this caller's in-flight view, never a
|
|
68
|
+
// stored one: the stale epoch would evict it anyway.
|
|
69
|
+
if (result.epoch !== validity.epoch())
|
|
70
|
+
evict(memo, key, pending);
|
|
19
71
|
assertProjectionVerificationCurrent(result.stillCurrent);
|
|
20
72
|
assertProjectionVerificationCurrent(stillCurrent);
|
|
21
|
-
|
|
73
|
+
// stillCurrent already includes the lifetime of a derived value's source.
|
|
74
|
+
return { value: result.value, stillCurrent };
|
|
75
|
+
}
|
|
76
|
+
function fallsBackOnline(input, error) {
|
|
77
|
+
return (!input.offline &&
|
|
78
|
+
error instanceof KeyingVerificationError &&
|
|
79
|
+
error.code === "missing_dependency");
|
|
22
80
|
}
|
|
23
81
|
async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedReferences) {
|
|
24
82
|
// Every recovered Current already retains its own exact head.
|
|
@@ -28,10 +86,11 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
28
86
|
{ ...expectedHead },
|
|
29
87
|
selected.map((reference) => ({ ...reference })),
|
|
30
88
|
]);
|
|
31
|
-
const
|
|
32
|
-
|
|
89
|
+
const validity = adminsMemoValidity(input, expectedHead);
|
|
90
|
+
const known = (await reusableEntry(memo, key([]), validity))?.entry;
|
|
91
|
+
const sourceCurrent = known?.stillCurrent ?? (() => true);
|
|
33
92
|
const current = () => !input.signal?.aborted && input.stillCurrent() && sourceCurrent();
|
|
34
|
-
return memoPrincipalRecovery(memo, key(references), current, async () => {
|
|
93
|
+
return memoPrincipalRecovery(memo, key(references), current, validity, known?.epoch, async () => {
|
|
35
94
|
const options = {
|
|
36
95
|
...input,
|
|
37
96
|
expectedHead,
|
|
@@ -40,8 +99,6 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
40
99
|
stillCurrent: current,
|
|
41
100
|
};
|
|
42
101
|
if (known) {
|
|
43
|
-
sourceCurrent = (await known).stillCurrent;
|
|
44
|
-
assertProjectionVerificationCurrent(current);
|
|
45
102
|
try {
|
|
46
103
|
// A freshly verified exact head only needs local proofs for additional
|
|
47
104
|
// citations; their signature and private-root checks still run.
|
|
@@ -52,9 +109,7 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
52
109
|
}
|
|
53
110
|
catch (error) {
|
|
54
111
|
assertProjectionVerificationCurrent(current);
|
|
55
|
-
if (input
|
|
56
|
-
!(error instanceof KeyingVerificationError) ||
|
|
57
|
-
error.code !== "missing_dependency")
|
|
112
|
+
if (!fallsBackOnline(input, error))
|
|
58
113
|
throw error;
|
|
59
114
|
}
|
|
60
115
|
}
|
|
@@ -62,28 +117,26 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
|
|
|
62
117
|
});
|
|
63
118
|
}
|
|
64
119
|
async function readMemoizedDirectory(input, memo, key, references) {
|
|
65
|
-
const
|
|
66
|
-
|
|
120
|
+
const validity = directoryMemoValidity(input);
|
|
121
|
+
const known = references.length > 0
|
|
122
|
+
? (await reusableEntry(memo, key([]), validity))?.entry
|
|
123
|
+
: undefined;
|
|
124
|
+
const sourceCurrent = known?.stillCurrent ?? (() => true);
|
|
67
125
|
const current = () => !input.signal?.aborted && input.stillCurrent() && sourceCurrent();
|
|
68
|
-
return memoPrincipalRecovery(memo, key(references), current, async () => {
|
|
126
|
+
return memoPrincipalRecovery(memo, key(references), current, validity, known?.epoch, async () => {
|
|
69
127
|
if (!known)
|
|
70
128
|
return recoverPolicyDirectory(input, references);
|
|
71
|
-
const source = await known;
|
|
72
|
-
sourceCurrent = source.stillCurrent;
|
|
73
|
-
assertProjectionVerificationCurrent(current);
|
|
74
129
|
try {
|
|
75
|
-
// Add authenticated historical citations to this
|
|
76
|
-
// view. A newer private prefix must not silently replace
|
|
77
|
-
return await recoverPolicyDirectory({ ...input, offline: true, stillCurrent: current }, references,
|
|
130
|
+
// Add authenticated historical citations to this operation's exact
|
|
131
|
+
// directory view. A newer private prefix must not silently replace it.
|
|
132
|
+
return await recoverPolicyDirectory({ ...input, offline: true, stillCurrent: current }, references, known.value);
|
|
78
133
|
}
|
|
79
134
|
catch (error) {
|
|
80
135
|
assertProjectionVerificationCurrent(current);
|
|
81
|
-
if (input
|
|
82
|
-
!(error instanceof KeyingVerificationError) ||
|
|
83
|
-
error.code !== "missing_dependency")
|
|
136
|
+
if (!fallsBackOnline(input, error))
|
|
84
137
|
throw error;
|
|
85
138
|
}
|
|
86
|
-
return recoverPolicyDirectory({ ...input, stillCurrent: current }, references,
|
|
139
|
+
return recoverPolicyDirectory({ ...input, stillCurrent: current }, references, known.value);
|
|
87
140
|
});
|
|
88
141
|
}
|
|
89
142
|
export function createPrincipalRecoveryReader(input, memo) {
|
|
@@ -96,9 +149,17 @@ export function createPrincipalRecoveryReader(input, memo) {
|
|
|
96
149
|
...scope,
|
|
97
150
|
references.map((reference) => ({ ...reference })),
|
|
98
151
|
]);
|
|
152
|
+
// Every lifetime behind a value this reader handed out; its caller must
|
|
153
|
+
// keep checking them at the point of use.
|
|
154
|
+
const lifetimes = [];
|
|
155
|
+
const lease = (leased) => {
|
|
156
|
+
lifetimes.push(leased.stillCurrent);
|
|
157
|
+
return leased.value;
|
|
158
|
+
};
|
|
99
159
|
return {
|
|
160
|
+
stillCurrent: () => lifetimes.every((current) => current()),
|
|
100
161
|
directory: async (references) => {
|
|
101
|
-
const value = await readMemoizedDirectory(input, memo?.directories, directoryKey, references);
|
|
162
|
+
const value = lease(await readMemoizedDirectory(input, memo?.directories, directoryKey, references));
|
|
102
163
|
// Retaining extra citations does not prevent this authenticated directory
|
|
103
164
|
// from satisfying a later read with no selected historical references.
|
|
104
165
|
const unselectedKey = directoryKey([]);
|
|
@@ -107,12 +168,11 @@ export function createPrincipalRecoveryReader(input, memo) {
|
|
|
107
168
|
selected &&
|
|
108
169
|
references.length > 0 &&
|
|
109
170
|
!memo.directories.has(unselectedKey))
|
|
110
|
-
//
|
|
111
|
-
// a later caller still has a live lease of its own.
|
|
171
|
+
// The alias keeps the original lease and validity; either ending evicts it.
|
|
112
172
|
memo.directories.set(unselectedKey, selected);
|
|
113
173
|
return value;
|
|
114
174
|
},
|
|
115
|
-
admins: (expectedHead, retainedReferences = []) => readMemoizedAdmins(input, memo?.admins, scope, expectedHead, retainedReferences),
|
|
175
|
+
admins: async (expectedHead, retainedReferences = []) => lease(await readMemoizedAdmins(input, memo?.admins, scope, expectedHead, retainedReferences)),
|
|
116
176
|
};
|
|
117
177
|
}
|
|
118
178
|
//# sourceMappingURL=principalRecoveryMemo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalRecoveryMemo.js","sourceRoot":"","sources":["principalRecoveryMemo.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport {\n type PrincipalRecoveryContext,\n type RecoveredPolicyDirectory,\n recoverPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\ntype Memo<T> = Map<string, Promise<{ value: T; stillCurrent: () => boolean }>>;\n\nexport interface PrincipalRecoveryMemo {\n readonly directories: Memo<RecoveredPolicyDirectory>;\n readonly admins: Memo<RecoveredPrincipalPolicyHistory>;\n}\n\n/** Only share authenticated results within one projection collection lifetime. */\nasync function memoPrincipalRecovery<T>(\n memo: Memo<T> | undefined,\n key: string,\n stillCurrent: () => boolean,\n work: () => Promise<T>,\n): Promise<T> {\n assertProjectionVerificationCurrent(stillCurrent);\n let pending = memo?.get(key);\n if (!pending) {\n pending = work().then((value) => ({ value, stillCurrent }));\n memo?.set(key, pending);\n }\n const result = await pending.catch((error: unknown) => {\n if (memo?.get(key) === pending) memo.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(result.stillCurrent);\n assertProjectionVerificationCurrent(stillCurrent);\n return result.value;\n}\n\nasync function readMemoizedAdmins(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPrincipalPolicyHistory> | undefined,\n scope: readonly (string | boolean)[],\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[],\n) {\n // Every recovered Current already retains its own exact head.\n const references = retainedReferences.filter(\n (reference) => !principalHeadMatchesReference(expectedHead, reference),\n );\n const key = (selected: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n { ...expectedHead },\n selected.map((reference) => ({ ...reference })),\n ]);\n const known = memo?.get(key([]));\n let sourceCurrent = () => true;\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(memo, key(references), current, async () => {\n const options = {\n ...input,\n expectedHead,\n retainedReferences,\n historyVerification: \"direct-admins\" as const,\n stillCurrent: current,\n };\n if (known) {\n sourceCurrent = (await known).stillCurrent;\n assertProjectionVerificationCurrent(current);\n try {\n // A freshly verified exact head only needs local proofs for additional\n // citations; their signature and private-root checks still run.\n return await recoverPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (\n input.offline ||\n !(error instanceof KeyingVerificationError) ||\n error.code !== \"missing_dependency\"\n )\n throw error;\n }\n }\n return recoverPrincipalPolicyHistory(options);\n });\n}\n\nasync function readMemoizedDirectory(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPolicyDirectory> | undefined,\n key: (references: readonly ReferencedPrincipalHead[]) => string,\n references: readonly ReferencedPrincipalHead[],\n) {\n const known = references.length > 0 ? memo?.get(key([])) : undefined;\n let sourceCurrent = () => true;\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(memo, key(references), current, async () => {\n if (!known) return recoverPolicyDirectory(input, references);\n const source = await known;\n sourceCurrent = source.stillCurrent;\n assertProjectionVerificationCurrent(current);\n try {\n // Add authenticated historical citations to this batch's exact directory\n // view. A newer private prefix must not silently replace that view.\n return await recoverPolicyDirectory(\n { ...input, offline: true, stillCurrent: current },\n references,\n source.value,\n );\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (\n input.offline ||\n !(error instanceof KeyingVerificationError) ||\n error.code !== \"missing_dependency\"\n )\n throw error;\n }\n return recoverPolicyDirectory(\n { ...input, stillCurrent: current },\n references,\n source.value,\n );\n });\n}\n\nexport function createPrincipalRecoveryReader(\n input: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const scope = [\n input.organizationId,\n input.protection.context,\n input.offline === true,\n ];\n const directoryKey = (references: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n references.map((reference) => ({ ...reference })),\n ]);\n return {\n directory: async (references: readonly ReferencedPrincipalHead[]) => {\n const value = await readMemoizedDirectory(\n input,\n memo?.directories,\n directoryKey,\n references,\n );\n // Retaining extra citations does not prevent this authenticated directory\n // from satisfying a later read with no selected historical references.\n const unselectedKey = directoryKey([]);\n const selected = memo?.directories.get(directoryKey(references));\n if (\n memo &&\n selected &&\n references.length > 0 &&\n !memo.directories.has(unselectedKey)\n )\n // Keep the original lease: its expiry requires a new batch, even if\n // a later caller still has a live lease of its own.\n memo.directories.set(unselectedKey, selected);\n return value;\n },\n admins: (\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[] = [],\n ) =>\n readMemoizedAdmins(\n input,\n memo?.admins,\n scope,\n expectedHead,\n retainedReferences,\n ),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"principalRecoveryMemo.js","sourceRoot":"","sources":["principalRecoveryMemo.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport {\n type PrincipalRecoveryContext,\n type RecoveredPolicyDirectory,\n recoverPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport {\n adminsMemoValidity,\n directoryMemoValidity,\n type PrincipalRecoveryMemoValidity,\n} from \"./principalRecoveryMemoValidity\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\ninterface MemoEntry<T> {\n readonly value: T;\n /** The producing call's private lease and caller lifetime. */\n readonly stillCurrent: () => boolean;\n /** Organization hint epoch observed before the reads that produced value. */\n readonly epoch: number;\n}\n\ntype Memo<T> = Map<string, Promise<MemoEntry<T>>>;\n\n/** A value handed out with every lifetime it depends on. */\ninterface Leased<T> {\n readonly value: T;\n /** The consumer's lifetime and every producer lifetime behind value. */\n readonly stillCurrent: () => boolean;\n}\n\nexport interface PrincipalRecoveryMemo {\n readonly directories: Memo<RecoveredPolicyDirectory>;\n readonly admins: Memo<RecoveredPrincipalPolicyHistory>;\n}\n\n/** Synchronous: no hint and no lease end since the entry's reads began. */\nfunction entryLive<T>(\n entry: MemoEntry<T>,\n validity: PrincipalRecoveryMemoValidity<T>,\n): boolean {\n return entry.epoch === validity.epoch() && entry.stillCurrent();\n}\n\nfunction evict<T>(\n memo: Memo<T> | undefined,\n key: string,\n pending: Promise<MemoEntry<T>>,\n): void {\n if (memo?.get(key) === pending) memo.delete(key);\n}\n\n/**\n * Return a live entry, evicting one whose producer lease ended, that predates\n * an organization hint, or that a durable pin has moved past. Liveness is\n * checked again after every await, so a hint or lease end during the pin\n * reads also evicts. The caller rechecks synchronously before use. A pin read\n * is a snapshot; a pin that advances later is caught by the checkpoint reload\n * in recovery, the caller's checkpoint refresh, and atomic admission.\n */\nasync function reusableEntry<T>(\n memo: Memo<T> | undefined,\n key: string,\n validity: PrincipalRecoveryMemoValidity<T>,\n): Promise<\n { entry: MemoEntry<T>; pending: Promise<MemoEntry<T>> } | undefined\n> {\n const pending = memo?.get(key);\n if (!pending) return undefined;\n const entry = await pending.catch(() => undefined);\n if (\n entry &&\n entryLive(entry, validity) &&\n (await validity.covers(entry.value)) &&\n entryLive(entry, validity)\n )\n return { entry, pending };\n evict(memo, key, pending);\n return undefined;\n}\n\n/**\n * Share authenticated results within one workflow operation. A later caller\n * never receives a result whose producer lease ended, that predates an\n * organization hint, or that a durable pin has moved past; it recovers again.\n * A result derived from a reused source keeps the source's epoch, so a hint\n * that arrives before it is stored still evicts it. Every value is handed out\n * with the consumer's lifetime and its producer's, so a producer that expires\n * after hand-out still invalidates the consumer's use.\n */\nasync function memoPrincipalRecovery<T>(\n memo: Memo<T> | undefined,\n key: string,\n stillCurrent: () => boolean,\n validity: PrincipalRecoveryMemoValidity<T>,\n sourceEpoch: number | undefined,\n work: () => Promise<T>,\n): Promise<Leased<T>> {\n assertProjectionVerificationCurrent(stillCurrent);\n const reused = await reusableEntry(memo, key, validity);\n if (reused) {\n const { entry } = reused;\n if (entryLive(entry, validity)) {\n assertProjectionVerificationCurrent(stillCurrent);\n return {\n value: entry.value,\n stillCurrent: () => stillCurrent() && entry.stillCurrent(),\n };\n }\n evict(memo, key, reused.pending);\n }\n // Observed before work starts its reads; a derived result keeps its source's.\n const epoch = sourceEpoch ?? validity.epoch();\n const pending = work().then((value) => ({ value, stillCurrent, epoch }));\n memo?.set(key, pending);\n const result = await pending.catch((error: unknown) => {\n evict(memo, key, pending);\n throw error;\n });\n // A hint during production leaves this caller's in-flight view, never a\n // stored one: the stale epoch would evict it anyway.\n if (result.epoch !== validity.epoch()) evict(memo, key, pending);\n assertProjectionVerificationCurrent(result.stillCurrent);\n assertProjectionVerificationCurrent(stillCurrent);\n // stillCurrent already includes the lifetime of a derived value's source.\n return { value: result.value, stillCurrent };\n}\n\nfunction fallsBackOnline(\n input: PrincipalRecoveryContext,\n error: unknown,\n): boolean {\n return (\n !input.offline &&\n error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\"\n );\n}\n\nasync function readMemoizedAdmins(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPrincipalPolicyHistory> | undefined,\n scope: readonly (string | boolean)[],\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[],\n) {\n // Every recovered Current already retains its own exact head.\n const references = retainedReferences.filter(\n (reference) => !principalHeadMatchesReference(expectedHead, reference),\n );\n const key = (selected: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n { ...expectedHead },\n selected.map((reference) => ({ ...reference })),\n ]);\n const validity = adminsMemoValidity(input, expectedHead);\n const known = (await reusableEntry(memo, key([]), validity))?.entry;\n const sourceCurrent = known?.stillCurrent ?? (() => true);\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(\n memo,\n key(references),\n current,\n validity,\n known?.epoch,\n async () => {\n const options = {\n ...input,\n expectedHead,\n retainedReferences,\n historyVerification: \"direct-admins\" as const,\n stillCurrent: current,\n };\n if (known) {\n try {\n // A freshly verified exact head only needs local proofs for additional\n // citations; their signature and private-root checks still run.\n return await recoverPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (!fallsBackOnline(input, error)) throw error;\n }\n }\n return recoverPrincipalPolicyHistory(options);\n },\n );\n}\n\nasync function readMemoizedDirectory(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPolicyDirectory> | undefined,\n key: (references: readonly ReferencedPrincipalHead[]) => string,\n references: readonly ReferencedPrincipalHead[],\n) {\n const validity = directoryMemoValidity(input);\n const known =\n references.length > 0\n ? (await reusableEntry(memo, key([]), validity))?.entry\n : undefined;\n const sourceCurrent = known?.stillCurrent ?? (() => true);\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(\n memo,\n key(references),\n current,\n validity,\n known?.epoch,\n async () => {\n if (!known) return recoverPolicyDirectory(input, references);\n try {\n // Add authenticated historical citations to this operation's exact\n // directory view. A newer private prefix must not silently replace it.\n return await recoverPolicyDirectory(\n { ...input, offline: true, stillCurrent: current },\n references,\n known.value,\n );\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (!fallsBackOnline(input, error)) throw error;\n }\n return recoverPolicyDirectory(\n { ...input, stillCurrent: current },\n references,\n known.value,\n );\n },\n );\n}\n\nexport function createPrincipalRecoveryReader(\n input: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const scope = [\n input.organizationId,\n input.protection.context,\n input.offline === true,\n ];\n const directoryKey = (references: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n references.map((reference) => ({ ...reference })),\n ]);\n // Every lifetime behind a value this reader handed out; its caller must\n // keep checking them at the point of use.\n const lifetimes: (() => boolean)[] = [];\n const lease = <T>(leased: Leased<T>): T => {\n lifetimes.push(leased.stillCurrent);\n return leased.value;\n };\n return {\n stillCurrent: () => lifetimes.every((current) => current()),\n directory: async (references: readonly ReferencedPrincipalHead[]) => {\n const value = lease(\n await readMemoizedDirectory(\n input,\n memo?.directories,\n directoryKey,\n references,\n ),\n );\n // Retaining extra citations does not prevent this authenticated directory\n // from satisfying a later read with no selected historical references.\n const unselectedKey = directoryKey([]);\n const selected = memo?.directories.get(directoryKey(references));\n if (\n memo &&\n selected &&\n references.length > 0 &&\n !memo.directories.has(unselectedKey)\n )\n // The alias keeps the original lease and validity; either ending evicts it.\n memo.directories.set(unselectedKey, selected);\n return value;\n },\n admins: async (\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[] = [],\n ) =>\n lease(\n await readMemoizedAdmins(\n input,\n memo?.admins,\n scope,\n expectedHead,\n retainedReferences,\n ),\n ),\n };\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { loadPrincipalPolicyCheckpoint } from "../../data/persistence/keyingCheckpointPersistence.js";
|
|
2
|
+
import { principalRecoveryEpoch } from "../../data/principals/principalRecoveryEpoch.js";
|
|
3
|
+
async function pinCovers(input, head) {
|
|
4
|
+
const pin = await loadPrincipalPolicyCheckpoint(input.execSql, head.principalType, head.principalId);
|
|
5
|
+
return (!pin ||
|
|
6
|
+
pin.version < head.version ||
|
|
7
|
+
(pin.version === head.version && pin.stateHash === head.stateHash));
|
|
8
|
+
}
|
|
9
|
+
function organizationEpoch(input) {
|
|
10
|
+
return () => principalRecoveryEpoch(input.execSql, input.organizationId);
|
|
11
|
+
}
|
|
12
|
+
/** A directory names every group head; a newer pin on any of them evicts it. */
|
|
13
|
+
export function directoryMemoValidity(input) {
|
|
14
|
+
return {
|
|
15
|
+
epoch: organizationEpoch(input),
|
|
16
|
+
covers: async (directory) => {
|
|
17
|
+
for (const head of [
|
|
18
|
+
directory.policy.state,
|
|
19
|
+
...directory.descriptor.groupHeads,
|
|
20
|
+
])
|
|
21
|
+
if (!(await pinCovers(input, head)))
|
|
22
|
+
return false;
|
|
23
|
+
return true;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function adminsMemoValidity(input, expectedHead) {
|
|
28
|
+
return {
|
|
29
|
+
epoch: organizationEpoch(input),
|
|
30
|
+
covers: () => pinCovers(input, expectedHead),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=principalRecoveryMemoValidity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalRecoveryMemoValidity.js","sourceRoot":"","sources":["principalRecoveryMemoValidity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AAoBtF,KAAK,UAAU,SAAS,CACtB,KAA+B,EAC/B,IAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,6BAA6B,CAC7C,KAAK,CAAC,OAAO,EACb,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,WAAW,CACjB,CAAC;IACF,OAAO,CACL,CAAC,GAAG;QACJ,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO;QAC1B,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,CACnE,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAA+B;IACxD,OAAO,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;AAC3E,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CACnC,KAA+B;IAE/B,OAAO;QACL,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YAC1B,KAAK,MAAM,IAAI,IAAI;gBACjB,SAAS,CAAC,MAAM,CAAC,KAAK;gBACtB,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU;aACnC;gBACC,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;oBAAE,OAAO,KAAK,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAA+B,EAC/B,YAAwB;IAExB,OAAO;QACL,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC;KAC7C,CAAC;AACJ,CAAC","sourcesContent":["import type { ReferencedPrincipalHead } from \"@tearleads/crypto\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { principalRecoveryEpoch } from \"../../data/principals/principalRecoveryEpoch\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport type {\n PrincipalRecoveryContext,\n RecoveredPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\n\n/** What keeps a memo entry valid beyond its producer's lease. */\nexport interface PrincipalRecoveryMemoValidity<T> {\n /** Organization hint epoch; production records it before its reads. */\n readonly epoch: () => number;\n /** No durable pin has moved past or forked from a memoized head. */\n readonly covers: (value: T) => Promise<boolean>;\n}\n\ntype PinnedHead = Pick<\n ReferencedPrincipalHead,\n \"principalType\" | \"principalId\" | \"version\" | \"stateHash\"\n>;\n\nasync function pinCovers(\n input: PrincipalRecoveryContext,\n head: PinnedHead,\n): Promise<boolean> {\n const pin = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n head.principalType,\n head.principalId,\n );\n return (\n !pin ||\n pin.version < head.version ||\n (pin.version === head.version && pin.stateHash === head.stateHash)\n );\n}\n\nfunction organizationEpoch(input: PrincipalRecoveryContext) {\n return () => principalRecoveryEpoch(input.execSql, input.organizationId);\n}\n\n/** A directory names every group head; a newer pin on any of them evicts it. */\nexport function directoryMemoValidity(\n input: PrincipalRecoveryContext,\n): PrincipalRecoveryMemoValidity<RecoveredPolicyDirectory> {\n return {\n epoch: organizationEpoch(input),\n covers: async (directory) => {\n for (const head of [\n directory.policy.state,\n ...directory.descriptor.groupHeads,\n ])\n if (!(await pinCovers(input, head))) return false;\n return true;\n },\n };\n}\n\nexport function adminsMemoValidity(\n input: PrincipalRecoveryContext,\n expectedHead: PinnedHead,\n): PrincipalRecoveryMemoValidity<RecoveredPrincipalPolicyHistory> {\n return {\n epoch: organizationEpoch(input),\n covers: () => pinCovers(input, expectedHead),\n };\n}\n"]}
|