@tearleads/client-sdk 0.1.38 → 0.1.40
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/organizationGroupPresentation.d.ts +1 -12
- 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/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/organizationReadModelGrantPersistence.js +2 -1
- package/data/persistence/organizations/organizationReadModelGrantPersistence.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelMemberLoad.d.ts +9 -0
- package/data/persistence/organizations/organizationReadModelMemberLoad.js +29 -21
- package/data/persistence/organizations/organizationReadModelMemberLoad.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelMembershipPersistence.js +91 -46
- package/data/persistence/organizations/organizationReadModelMembershipPersistence.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelPersistence.js +95 -73
- package/data/persistence/organizations/organizationReadModelPersistence.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelProjection.js +78 -89
- package/data/persistence/organizations/organizationReadModelProjection.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelPurge.js +14 -15
- package/data/persistence/organizations/organizationReadModelPurge.js.map +1 -1
- package/data/sqlite/organizationReadModelSchema.js +23 -28
- package/data/sqlite/organizationReadModelSchema.js.map +1 -1
- package/data/sqlite/organizationReadModelTableRegistry.d.ts +116 -263
- package/data/sqlite/organizationReadModelTableRegistry.js +10 -7
- package/data/sqlite/organizationReadModelTableRegistry.js.map +1 -1
- package/data/sqlite/schema.d.ts +116 -263
- package/data/sqlite/sqliteBindBudget.js +30 -0
- package/data/sqlite/sqliteBindBudget.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/internal/validators/operation/organizations.d.ts +0 -16
- package/internal/validators/response/index.d.ts +1 -1
- package/internal/validators/response/index.js.map +1 -1
- package/internal/validators/response/organizationReadModel.d.ts +10 -48
- package/internal/validators/response/organizationReadModel.js +9 -1
- package/internal/validators/response/organizationReadModel.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/readModel.d.ts +8 -3
- package/workflows/organizations/readModel.js.map +1 -1
- package/workflows/organizations/readModelProjection.d.ts +2 -12
- package/workflows/organizations/readModelProjection.js +1 -1
- package/workflows/organizations/readModelProjection.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
- package/workflows/sync/remoteReset.js +1 -3
- package/workflows/sync/remoteReset.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelPolicyHeadPersistence.js +0 -64
- package/data/persistence/organizations/organizationReadModelPolicyHeadPersistence.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflowRuntime.js","sourceRoot":"","sources":["workflowRuntime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { DocumentProjectorRegistry } from \"../data/documents/documentKinds\";\nimport type { DomainScope } from \"../data/domainScope\";\nimport { runWithSecurityIncidentReporting } from \"../data/keyingProjectionVerification/error\";\nimport type { PrincipalHistoryProtectionLease } from \"../data/principals/principalHistoryProtection\";\nimport type { SecurityIncidentReporter } from \"../data/securityIncidents\";\nimport { unavailableExecSql } from \"../data/sqlite/sqlSchema\";\nimport {\n createApiUserIdentitySource,\n createTrustedUserIdentityService,\n type LocalUserIdentityCandidate,\n type TrustedUserIdentityResolver,\n} from \"../data/trustedUserIdentity\";\nimport type { ContainerContentsRootAdopter } from \"../workflows/container-contents/runtime\";\nimport type {\n WorkflowRuntimeAuthInput,\n WorkflowRuntimeCryptoInput,\n WorkflowRuntimeGroups,\n WorkflowRuntimeInfraInput,\n WorkflowRuntimeStateInput,\n WorkflowRuntimeUtilInput,\n} from \"../workflows/runtimeInput\";\nimport type { Blobs } from \"./blobs\";\nimport type { Database } from \"./database\";\nimport type { Events } from \"./events\";\nimport type { Identity } from \"./identity\";\nimport { createListenerSet } from \"./listenerSet\";\nimport type { Network } from \"./network\";\nimport {\n createPrincipalHistoryProtectionCustody,\n type PrincipalHistoryKeyProvider,\n} from \"./principalHistoryProtection\";\nimport {\n createPrincipalMutationApiCustody,\n type PrincipalMutationApi,\n} from \"./principalMutationApi\";\nimport { adoptSessionRootContainer } from \"./rootContainerAdoption\";\nimport { acknowledgedSessionRoot } from \"./session/sessionRootAuthority\";\nimport type { Session, SessionSnapshot } from \"./session/sessionTypes\";\nimport type { SyncBillingGate } from \"./syncBillingGate\";\n\ntype HostWorkflowRuntimeUtilInput = Omit<\n WorkflowRuntimeUtilInput,\n \"reportSecurityIncident\"\n>;\n\nexport interface WorkflowRuntimeInput\n extends Omit<WorkflowRuntimeGroups, \"util\"> {\n readonly util: HostWorkflowRuntimeUtilInput;\n}\n\nexport type RuntimeListener = () => void;\n\nexport interface Runtime {\n readonly version: number;\n input(containerId?: string | null | undefined): WorkflowRuntimeInput;\n subscribe(listener: RuntimeListener): () => void;\n}\n\nexport interface InternalWorkflowRuntimeInput extends WorkflowRuntimeGroups {\n readonly apiClient: PrincipalMutationApi;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly withPrincipalHistoryProtection?:\n | PrincipalHistoryProtectionLease\n | undefined;\n}\n\nexport interface InternalRuntime {\n retirePrincipalHistoryProtection(): void;\n readonly sessionGeneration: number;\n readonly adoptRootContainer: ContainerContentsRootAdopter;\n readonly publicRuntime: Runtime;\n boundUserId(signingKeyFingerprint: string): Promise<string | null>;\n pinLocalUserIdentity(\n userId: string,\n candidate: LocalUserIdentityCandidate,\n ): Promise<void>;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\ninterface WorkflowRuntimeDependencies {\n api: ApiClient;\n blobs: Blobs;\n database: Database;\n documentProjectors: DocumentProjectorRegistry;\n events: Events;\n getDomainScope: () => DomainScope;\n identity: Identity;\n identityTrustDomain: string | null;\n log: (message: string) => void;\n logError: (message: string | Error, cause?: unknown) => void;\n network: Network;\n peerScope?: string | null;\n principalHistoryKeyProvider?: PrincipalHistoryKeyProvider | undefined;\n principalMutationTimeoutMs?: number | undefined;\n reportSecurityIncident: SecurityIncidentReporter;\n session: Session;\n syncBillingGate?: SyncBillingGate | undefined;\n}\n\nfunction createRuntimeMutationApi(\n dependencies: WorkflowRuntimeDependencies,\n readGeneration: () => number,\n) {\n return createPrincipalMutationApiCustody({\n api: dependencies.api,\n principalMutationTimeoutMs: dependencies.principalMutationTimeoutMs,\n readScope: () => {\n const { signingKeyPair, signingFingerprint } = dependencies.identity;\n const { userId, isAuthenticated } = dependencies.session;\n const identityTrustDomain = dependencies.identityTrustDomain;\n if (\n !isAuthenticated ||\n !userId ||\n !signingKeyPair ||\n !signingFingerprint ||\n !identityTrustDomain ||\n dependencies.database.status !== \"ready\"\n )\n return null;\n return {\n database: dependencies.getDomainScope(),\n execSql: dependencies.database.requireExecSql(\"principal mutation\"),\n generation: readGeneration(),\n identityTrustDomain,\n signingFingerprint,\n signingKeyPair,\n userId,\n };\n },\n });\n}\n\nexport function createRuntime(\n dependencies: WorkflowRuntimeDependencies,\n): InternalRuntime {\n const trustedUserIdentityService = createTrustedUserIdentityService({\n getExecSql: () =>\n dependencies.database.status === \"ready\"\n ? dependencies.database.requireExecSql(\"trusted user identity\")\n : null,\n getLocalIdentity: () => {\n const encapsulationKeyPair = dependencies.identity.encapsulationKeyPair;\n const signingKeyPair = dependencies.identity.signingKeyPair;\n if (!encapsulationKeyPair || !signingKeyPair) {\n return null;\n }\n return {\n encapsulationPublicKey: encapsulationKeyPair.publicKey,\n signingKeyFingerprint: dependencies.identity.signingFingerprint,\n signingPublicKey: signingKeyPair.signingPublicKey,\n };\n },\n getLocalUserId: () => dependencies.session.userId,\n identityTrustDomain: dependencies.identityTrustDomain,\n remoteSource: createApiUserIdentitySource(dependencies.api),\n });\n const resolveTrustedUserIdentity: TrustedUserIdentityResolver = (userId) =>\n runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.resolve\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.resolve(userId),\n );\n const runtimeSubscription = createRuntimeSubscription(dependencies);\n const historyProtection = createPrincipalHistoryProtectionCustody({\n keyProvider: dependencies.principalHistoryKeyProvider,\n readScope: () => {\n const signingFingerprint = dependencies.identity.signingFingerprint;\n const identityTrustDomain = dependencies.identityTrustDomain;\n if (\n !signingFingerprint ||\n !identityTrustDomain ||\n dependencies.database.status !== \"ready\"\n )\n return null;\n return {\n signingFingerprint,\n identityTrustDomain,\n database: dependencies.getDomainScope(),\n generation: runtimeSubscription.recoveryGeneration,\n };\n },\n });\n const runtimeInput = createRuntimeInputFactory(\n dependencies,\n resolveTrustedUserIdentity,\n historyProtection.bind,\n createRuntimeMutationApi(\n dependencies,\n () => runtimeSubscription.mutationGeneration,\n ).bind,\n );\n\n return {\n retirePrincipalHistoryProtection: historyProtection.retire,\n get sessionGeneration() {\n return runtimeSubscription.sessionGeneration;\n },\n adoptRootContainer: (input) =>\n adoptSessionRootContainer(dependencies, input),\n boundUserId: (signingKeyFingerprint) =>\n trustedUserIdentityService.boundUserId(signingKeyFingerprint),\n async pinLocalUserIdentity(userId, candidate) {\n await runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.pin_local\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.pinLocal(userId, candidate),\n );\n },\n publicRuntime: {\n get version() {\n return runtimeSubscription.version;\n },\n input: runtimeInput.hostInput,\n subscribe: runtimeSubscription.subscribe,\n },\n workflowInput: runtimeInput.workflowInput,\n };\n}\n\nfunction createRuntimeSubscription(dependencies: WorkflowRuntimeDependencies) {\n const listeners = createListenerSet();\n let version = 0;\n let sessionGeneration = 0;\n let recoveryGeneration = 0;\n let mutationGeneration = 0;\n let sessionSnapshot = dependencies.session.snapshot;\n const notifyListeners = () => {\n version += 1;\n listeners.notify();\n };\n\n const notifyAuthority = () => {\n recoveryGeneration += 1;\n mutationGeneration += 1;\n notifyListeners();\n };\n dependencies.database.subscribe(notifyAuthority);\n dependencies.events.subscribe(notifyListeners);\n dependencies.identity.subscribe(notifyAuthority);\n dependencies.network.subscribe(() => notifyListeners());\n dependencies.session.subscribe(() => {\n const next = dependencies.session.snapshot;\n if (sessionAuthorityChanged(sessionSnapshot, next)) {\n sessionGeneration += 1;\n recoveryGeneration += 1;\n }\n if (sessionScopeChanged(sessionSnapshot, next)) mutationGeneration += 1;\n sessionSnapshot = next;\n notifyListeners();\n });\n dependencies.syncBillingGate?.subscribe(notifyListeners);\n\n return {\n get mutationGeneration() {\n return mutationGeneration;\n },\n get recoveryGeneration() {\n return recoveryGeneration;\n },\n get sessionGeneration() {\n return sessionGeneration;\n },\n get version() {\n return version;\n },\n subscribe(listener: RuntimeListener): () => void {\n return listeners.subscribe(listener);\n },\n };\n}\n\ninterface RuntimeInputFactory {\n hostInput(containerId?: string | null | undefined): WorkflowRuntimeInput;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\nfunction sessionAuthInput({\n session,\n identity,\n}: WorkflowRuntimeDependencies): WorkflowRuntimeAuthInput {\n const signingFingerprint = identity.snapshot.signingFingerprint;\n return {\n defaultOrganizationId: session.defaultOrganizationId,\n rootContainerId: acknowledgedSessionRoot(session, signingFingerprint),\n isAuthenticated: session.isAuthenticated,\n isRoot: session.isRoot,\n organizationId: session.organizationId,\n userId: session.userId,\n };\n}\n\n/** Fields whose change invalidates work keyed on the session's authority. */\nfunction sessionAuthorityChanged(\n previous: SessionSnapshot,\n next: SessionSnapshot,\n): boolean {\n return (\n next.authToken !== previous.authToken || sessionScopeChanged(previous, next)\n );\n}\n\n// A transport token renewal cannot turn a definite write refusal into saved work.\n// Journal custody still expires on every change to its identity or storage scope.\nfunction sessionScopeChanged(\n previous: SessionSnapshot,\n next: SessionSnapshot,\n): boolean {\n return (\n next.isAuthenticated !== previous.isAuthenticated ||\n next.isRoot !== previous.isRoot ||\n next.organizationId !== previous.organizationId ||\n next.userId !== previous.userId\n );\n}\n\nfunction createRuntimeInputFactory(\n dependencies: WorkflowRuntimeDependencies,\n resolveTrustedUserIdentity: TrustedUserIdentityResolver,\n bindHistoryProtection: () => PrincipalHistoryProtectionLease | undefined,\n bindMutationApi: () => PrincipalMutationApi,\n): RuntimeInputFactory {\n let auth: WorkflowRuntimeAuthInput | undefined;\n let crypto: WorkflowRuntimeCryptoInput | undefined;\n let infra: WorkflowRuntimeInfraInput | undefined;\n let state: WorkflowRuntimeStateInput | undefined;\n let util: WorkflowRuntimeUtilInput | undefined;\n let hostUtil: HostWorkflowRuntimeUtilInput | undefined;\n\n const workflowInput = (\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput => {\n const dbStatus = dependencies.database.status;\n const execSql =\n dbStatus === \"ready\"\n ? dependencies.database.requireExecSql(\"tearleads.runtime.input\")\n : unavailableExecSql;\n const nextContainerId =\n (containerId === undefined\n ? dependencies.session.containerId\n : containerId) ?? null;\n\n auth = reuseIfShallowEqual(auth, sessionAuthInput(dependencies));\n crypto = reuseIfShallowEqual(crypto, {\n encapsulationKeyPair: dependencies.identity.encapsulationKeyPair,\n signingFingerprint: dependencies.identity.signingFingerprint,\n signingKeyPair: dependencies.identity.signingKeyPair,\n });\n infra = reuseIfShallowEqual(infra, {\n blobStore: dependencies.blobs.store,\n dbStatus,\n documentProjectors: dependencies.documentProjectors,\n // Runtime consumers branch on dbStatus before touching SQLite. The\n // fallback preserves the runtime shape and catches lifecycle bugs.\n execSql,\n });\n state = reuseIfShallowEqual(state, {\n containerId: nextContainerId,\n domainScope: dependencies.getDomainScope(),\n events: dependencies.events.events,\n // Resolved sync-online: workflows treat local-only mode exactly like being\n // offline (no server I/O), while `tearleads.network` stays the truthful\n // connectivity signal for host/UI. See Session.syncEnabled.\n online: dependencies.network.online && dependencies.session.syncEnabled,\n peerScope: dependencies.peerScope ?? null,\n serverEventsConnectionGeneration:\n dependencies.events.connectionGeneration,\n });\n if (\n !util ||\n util.log !== dependencies.log ||\n util.logError !== dependencies.logError ||\n util.reportSecurityIncident !== dependencies.reportSecurityIncident\n ) {\n util = {\n isRemoteSyncBlocked: (organizationId) =>\n dependencies.syncBillingGate?.isBlockedForOrganization(\n organizationId,\n ) ?? false,\n log: dependencies.log,\n logError: dependencies.logError,\n reportSecurityIncident: dependencies.reportSecurityIncident,\n };\n }\n\n return {\n apiClient: bindMutationApi(),\n auth,\n crypto,\n infra,\n state,\n util,\n resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: bindHistoryProtection(),\n };\n };\n\n return {\n hostInput(containerId) {\n const {\n apiClient: _apiClient,\n resolveTrustedUserIdentity: _resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: _withPrincipalHistoryProtection,\n util: workflowUtil,\n ...input\n } = workflowInput(containerId);\n hostUtil = reuseIfShallowEqual(hostUtil, {\n isRemoteSyncBlocked: workflowUtil.isRemoteSyncBlocked,\n log: workflowUtil.log,\n logError: workflowUtil.logError,\n });\n return { ...input, util: hostUtil };\n },\n workflowInput,\n };\n}\n\n/**\n * Reuses the current runtime-input group when every field of the freshly\n * constructed `next` literal is reference-equal, so unchanged groups keep a\n * stable identity across runtime reads.\n */\nfunction reuseIfShallowEqual<T extends object>(\n current: T | undefined,\n next: T,\n): T {\n if (!current) {\n return next;\n }\n for (const key of Object.keys(next)) {\n if (Reflect.get(current, key) !== Reflect.get(next, key)) {\n return next;\n }\n }\n return current;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workflowRuntime.js","sourceRoot":"","sources":["workflowRuntime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { DocumentProjectorRegistry } from \"../data/documents/documentKinds\";\nimport type { DomainScope } from \"../data/domainScope\";\nimport { runWithSecurityIncidentReporting } from \"../data/keyingProjectionVerification/error\";\nimport type { PrincipalHistoryProtectionLease } from \"../data/principals/principalHistoryProtection\";\nimport type { SecurityIncidentReporter } from \"../data/securityIncidents\";\nimport { type ExecSql, unavailableExecSql } from \"../data/sqlite/sqlSchema\";\nimport {\n createApiUserIdentitySource,\n createTrustedUserIdentityService,\n type LocalUserIdentityCandidate,\n loadPinnedUserIdentity,\n type TrustedUserIdentityResolver,\n} from \"../data/trustedUserIdentity\";\nimport type { ContainerContentsRootAdopter } from \"../workflows/container-contents/runtime\";\nimport {\n createProvisionedPrincipalHistoryPrimer,\n type ProvisionedPrincipalHistoryPrimer,\n} from \"../workflows/registration/provisionedPrincipalHistory\";\nimport type {\n WorkflowRuntimeAuthInput,\n WorkflowRuntimeCryptoInput,\n WorkflowRuntimeGroups,\n WorkflowRuntimeInfraInput,\n WorkflowRuntimeStateInput,\n WorkflowRuntimeUtilInput,\n} from \"../workflows/runtimeInput\";\nimport type { Blobs } from \"./blobs\";\nimport type { Database } from \"./database\";\nimport type { Events } from \"./events\";\nimport type { Identity } from \"./identity\";\nimport { createListenerSet } from \"./listenerSet\";\nimport type { Network } from \"./network\";\nimport {\n createPrincipalHistoryProtectionCustody,\n type PrincipalHistoryKeyProvider,\n} from \"./principalHistoryProtection\";\nimport {\n createPrincipalMutationApiCustody,\n type PrincipalMutationApi,\n} from \"./principalMutationApi\";\nimport { adoptSessionRootContainer } from \"./rootContainerAdoption\";\nimport { acknowledgedSessionRoot } from \"./session/sessionRootAuthority\";\nimport type { Session, SessionSnapshot } from \"./session/sessionTypes\";\nimport type { SyncBillingGate } from \"./syncBillingGate\";\n\ntype HostWorkflowRuntimeUtilInput = Omit<\n WorkflowRuntimeUtilInput,\n \"reportSecurityIncident\"\n>;\n\nexport interface WorkflowRuntimeInput\n extends Omit<WorkflowRuntimeGroups, \"util\"> {\n readonly util: HostWorkflowRuntimeUtilInput;\n}\n\nexport type RuntimeListener = () => void;\n\nexport interface Runtime {\n readonly version: number;\n input(containerId?: string | null | undefined): WorkflowRuntimeInput;\n subscribe(listener: RuntimeListener): () => void;\n}\n\nexport interface InternalWorkflowRuntimeInput extends WorkflowRuntimeGroups {\n readonly apiClient: PrincipalMutationApi;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly withPrincipalHistoryProtection?:\n | PrincipalHistoryProtectionLease\n | undefined;\n}\n\nexport interface InternalRuntime {\n retirePrincipalHistoryProtection(): void;\n readonly sessionGeneration: number;\n readonly adoptRootContainer: ContainerContentsRootAdopter;\n readonly publicRuntime: Runtime;\n boundUserId(signingKeyFingerprint: string): Promise<string | null>;\n pinLocalUserIdentity(\n userId: string,\n candidate: LocalUserIdentityCandidate,\n ): Promise<void>;\n readonly primeProvisionedPrincipalHistory: ProvisionedPrincipalHistoryPrimer;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\ninterface WorkflowRuntimeDependencies {\n api: ApiClient;\n blobs: Blobs;\n database: Database;\n documentProjectors: DocumentProjectorRegistry;\n events: Events;\n getDomainScope: () => DomainScope;\n identity: Identity;\n identityTrustDomain: string | null;\n log: (message: string) => void;\n logError: (message: string | Error, cause?: unknown) => void;\n network: Network;\n peerScope?: string | null;\n principalHistoryKeyProvider?: PrincipalHistoryKeyProvider | undefined;\n principalMutationTimeoutMs?: number | undefined;\n reportSecurityIncident: SecurityIncidentReporter;\n session: Session;\n syncBillingGate?: SyncBillingGate | undefined;\n}\n\nfunction createRuntimeMutationApi(\n dependencies: WorkflowRuntimeDependencies,\n readGeneration: () => number,\n) {\n return createPrincipalMutationApiCustody({\n api: dependencies.api,\n principalMutationTimeoutMs: dependencies.principalMutationTimeoutMs,\n readScope: () => {\n const { signingKeyPair, signingFingerprint } = dependencies.identity;\n const { userId, isAuthenticated } = dependencies.session;\n const identityTrustDomain = dependencies.identityTrustDomain;\n if (\n !isAuthenticated ||\n !userId ||\n !signingKeyPair ||\n !signingFingerprint ||\n !identityTrustDomain ||\n dependencies.database.status !== \"ready\"\n )\n return null;\n return {\n database: dependencies.getDomainScope(),\n execSql: dependencies.database.requireExecSql(\"principal mutation\"),\n generation: readGeneration(),\n identityTrustDomain,\n signingFingerprint,\n signingKeyPair,\n userId,\n };\n },\n });\n}\n\nfunction localIdentityCandidate(\n identity: Identity,\n): LocalUserIdentityCandidate | null {\n const { encapsulationKeyPair, signingKeyPair } = identity;\n if (!encapsulationKeyPair || !signingKeyPair) return null;\n return {\n encapsulationPublicKey: encapsulationKeyPair.publicKey,\n signingKeyFingerprint: identity.signingFingerprint,\n signingPublicKey: signingKeyPair.signingPublicKey,\n };\n}\n\n/** Priming reads the runtime's database, identity and trust pins; it pins nothing. */\nfunction createRuntimeProvisioningPrimer(\n dependencies: WorkflowRuntimeDependencies,\n bindLease: () => PrincipalHistoryProtectionLease | undefined,\n readyExecSql: () => ExecSql | null,\n): ProvisionedPrincipalHistoryPrimer {\n return createProvisionedPrincipalHistoryPrimer({\n bindLease,\n execSqlFor: (database) =>\n dependencies.database.client === database ? readyExecSql() : null,\n loadPinnedIdentity: async (execSql, userId) =>\n dependencies.identityTrustDomain\n ? loadPinnedUserIdentity({\n execSql,\n identityTrustDomain: dependencies.identityTrustDomain,\n userId,\n })\n : null,\n logError: dependencies.logError,\n signingFingerprint: () => dependencies.identity.signingFingerprint,\n });\n}\n\nexport function createRuntime(\n dependencies: WorkflowRuntimeDependencies,\n): InternalRuntime {\n const readyExecSql = () =>\n dependencies.database.status === \"ready\"\n ? dependencies.database.requireExecSql(\"trusted user identity\")\n : null;\n const trustedUserIdentityService = createTrustedUserIdentityService({\n getExecSql: readyExecSql,\n getLocalIdentity: () => localIdentityCandidate(dependencies.identity),\n getLocalUserId: () => dependencies.session.userId,\n identityTrustDomain: dependencies.identityTrustDomain,\n remoteSource: createApiUserIdentitySource(dependencies.api),\n });\n const resolveTrustedUserIdentity: TrustedUserIdentityResolver = (userId) =>\n runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.resolve\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.resolve(userId),\n );\n const runtimeSubscription = createRuntimeSubscription(dependencies);\n const historyProtection = createPrincipalHistoryProtectionCustody({\n keyProvider: dependencies.principalHistoryKeyProvider,\n readScope: () => {\n const signingFingerprint = dependencies.identity.signingFingerprint;\n const identityTrustDomain = dependencies.identityTrustDomain;\n if (\n !signingFingerprint ||\n !identityTrustDomain ||\n dependencies.database.status !== \"ready\"\n )\n return null;\n return {\n signingFingerprint,\n identityTrustDomain,\n database: dependencies.getDomainScope(),\n generation: runtimeSubscription.recoveryGeneration,\n };\n },\n });\n const runtimeInput = createRuntimeInputFactory(\n dependencies,\n resolveTrustedUserIdentity,\n historyProtection.bind,\n createRuntimeMutationApi(\n dependencies,\n () => runtimeSubscription.mutationGeneration,\n ).bind,\n );\n\n return {\n retirePrincipalHistoryProtection: historyProtection.retire,\n get sessionGeneration() {\n return runtimeSubscription.sessionGeneration;\n },\n adoptRootContainer: (input) =>\n adoptSessionRootContainer(dependencies, input),\n boundUserId: (signingKeyFingerprint) =>\n trustedUserIdentityService.boundUserId(signingKeyFingerprint),\n async pinLocalUserIdentity(userId, candidate) {\n await runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.pin_local\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.pinLocal(userId, candidate),\n );\n },\n primeProvisionedPrincipalHistory: createRuntimeProvisioningPrimer(\n dependencies,\n historyProtection.bind,\n readyExecSql,\n ),\n publicRuntime: {\n get version() {\n return runtimeSubscription.version;\n },\n input: runtimeInput.hostInput,\n subscribe: runtimeSubscription.subscribe,\n },\n workflowInput: runtimeInput.workflowInput,\n };\n}\n\nfunction createRuntimeSubscription(dependencies: WorkflowRuntimeDependencies) {\n const listeners = createListenerSet();\n let version = 0;\n let sessionGeneration = 0;\n let recoveryGeneration = 0;\n let mutationGeneration = 0;\n let sessionSnapshot = dependencies.session.snapshot;\n const notifyListeners = () => {\n version += 1;\n listeners.notify();\n };\n\n const notifyAuthority = () => {\n recoveryGeneration += 1;\n mutationGeneration += 1;\n notifyListeners();\n };\n dependencies.database.subscribe(notifyAuthority);\n dependencies.events.subscribe(notifyListeners);\n dependencies.identity.subscribe(notifyAuthority);\n dependencies.network.subscribe(() => notifyListeners());\n dependencies.session.subscribe(() => {\n const next = dependencies.session.snapshot;\n if (sessionAuthorityChanged(sessionSnapshot, next)) {\n sessionGeneration += 1;\n recoveryGeneration += 1;\n }\n if (sessionScopeChanged(sessionSnapshot, next)) mutationGeneration += 1;\n sessionSnapshot = next;\n notifyListeners();\n });\n dependencies.syncBillingGate?.subscribe(notifyListeners);\n\n return {\n get mutationGeneration() {\n return mutationGeneration;\n },\n get recoveryGeneration() {\n return recoveryGeneration;\n },\n get sessionGeneration() {\n return sessionGeneration;\n },\n get version() {\n return version;\n },\n subscribe(listener: RuntimeListener): () => void {\n return listeners.subscribe(listener);\n },\n };\n}\n\ninterface RuntimeInputFactory {\n hostInput(containerId?: string | null | undefined): WorkflowRuntimeInput;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\nfunction sessionAuthInput({\n session,\n identity,\n}: WorkflowRuntimeDependencies): WorkflowRuntimeAuthInput {\n const signingFingerprint = identity.snapshot.signingFingerprint;\n return {\n defaultOrganizationId: session.defaultOrganizationId,\n rootContainerId: acknowledgedSessionRoot(session, signingFingerprint),\n isAuthenticated: session.isAuthenticated,\n isRoot: session.isRoot,\n organizationId: session.organizationId,\n userId: session.userId,\n };\n}\n\n/** Fields whose change invalidates work keyed on the session's authority. */\nfunction sessionAuthorityChanged(\n previous: SessionSnapshot,\n next: SessionSnapshot,\n): boolean {\n return (\n next.authToken !== previous.authToken || sessionScopeChanged(previous, next)\n );\n}\n\n// A transport token renewal cannot turn a definite write refusal into saved work.\n// Journal custody still expires on every change to its identity or storage scope.\nfunction sessionScopeChanged(\n previous: SessionSnapshot,\n next: SessionSnapshot,\n): boolean {\n return (\n next.isAuthenticated !== previous.isAuthenticated ||\n next.isRoot !== previous.isRoot ||\n next.organizationId !== previous.organizationId ||\n next.userId !== previous.userId\n );\n}\n\nfunction createRuntimeInputFactory(\n dependencies: WorkflowRuntimeDependencies,\n resolveTrustedUserIdentity: TrustedUserIdentityResolver,\n bindHistoryProtection: () => PrincipalHistoryProtectionLease | undefined,\n bindMutationApi: () => PrincipalMutationApi,\n): RuntimeInputFactory {\n let auth: WorkflowRuntimeAuthInput | undefined;\n let crypto: WorkflowRuntimeCryptoInput | undefined;\n let infra: WorkflowRuntimeInfraInput | undefined;\n let state: WorkflowRuntimeStateInput | undefined;\n let util: WorkflowRuntimeUtilInput | undefined;\n let hostUtil: HostWorkflowRuntimeUtilInput | undefined;\n\n const workflowInput = (\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput => {\n const dbStatus = dependencies.database.status;\n const execSql =\n dbStatus === \"ready\"\n ? dependencies.database.requireExecSql(\"tearleads.runtime.input\")\n : unavailableExecSql;\n const nextContainerId =\n (containerId === undefined\n ? dependencies.session.containerId\n : containerId) ?? null;\n\n auth = reuseIfShallowEqual(auth, sessionAuthInput(dependencies));\n crypto = reuseIfShallowEqual(crypto, {\n encapsulationKeyPair: dependencies.identity.encapsulationKeyPair,\n signingFingerprint: dependencies.identity.signingFingerprint,\n signingKeyPair: dependencies.identity.signingKeyPair,\n });\n infra = reuseIfShallowEqual(infra, {\n blobStore: dependencies.blobs.store,\n dbStatus,\n documentProjectors: dependencies.documentProjectors,\n // Runtime consumers branch on dbStatus before touching SQLite. The\n // fallback preserves the runtime shape and catches lifecycle bugs.\n execSql,\n });\n state = reuseIfShallowEqual(state, {\n containerId: nextContainerId,\n domainScope: dependencies.getDomainScope(),\n events: dependencies.events.events,\n // Resolved sync-online: workflows treat local-only mode exactly like being\n // offline (no server I/O), while `tearleads.network` stays the truthful\n // connectivity signal for host/UI. See Session.syncEnabled.\n online: dependencies.network.online && dependencies.session.syncEnabled,\n peerScope: dependencies.peerScope ?? null,\n serverEventsConnectionGeneration:\n dependencies.events.connectionGeneration,\n });\n if (\n !util ||\n util.log !== dependencies.log ||\n util.logError !== dependencies.logError ||\n util.reportSecurityIncident !== dependencies.reportSecurityIncident\n ) {\n util = {\n isRemoteSyncBlocked: (organizationId) =>\n dependencies.syncBillingGate?.isBlockedForOrganization(\n organizationId,\n ) ?? false,\n log: dependencies.log,\n logError: dependencies.logError,\n reportSecurityIncident: dependencies.reportSecurityIncident,\n };\n }\n\n return {\n apiClient: bindMutationApi(),\n auth,\n crypto,\n infra,\n state,\n util,\n resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: bindHistoryProtection(),\n };\n };\n\n return {\n hostInput(containerId) {\n const {\n apiClient: _apiClient,\n resolveTrustedUserIdentity: _resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: _withPrincipalHistoryProtection,\n util: workflowUtil,\n ...input\n } = workflowInput(containerId);\n hostUtil = reuseIfShallowEqual(hostUtil, {\n isRemoteSyncBlocked: workflowUtil.isRemoteSyncBlocked,\n log: workflowUtil.log,\n logError: workflowUtil.logError,\n });\n return { ...input, util: hostUtil };\n },\n workflowInput,\n };\n}\n\n/**\n * Reuses the current runtime-input group when every field of the freshly\n * constructed `next` literal is reference-equal, so unchanged groups keep a\n * stable identity across runtime reads.\n */\nfunction reuseIfShallowEqual<T extends object>(\n current: T | undefined,\n next: T,\n): T {\n if (!current) {\n return next;\n }\n for (const key of Object.keys(next)) {\n if (Reflect.get(current, key) !== Reflect.get(next, key)) {\n return next;\n }\n }\n return current;\n}\n"]}
|
|
@@ -23,4 +23,20 @@ export interface DocumentDiscoveryEvidenceStore {
|
|
|
23
23
|
loadHead(documentId: string, manifestHash: string | null | undefined): Promise<CachedDiscoveryHead | null>;
|
|
24
24
|
saveHead(documentId: string, head: CachedDiscoveryHead, generation: number): Promise<boolean>;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* A document head this client signed, already checked against the server's
|
|
28
|
+
* create acknowledgement and pinned as its access-manifest checkpoint in the
|
|
29
|
+
* same transaction. It is the same signed head a discovery projection would
|
|
30
|
+
* verify, so it seeds the exact-head cache; `loadHead` still reuses it only
|
|
31
|
+
* while that checkpoint matches.
|
|
32
|
+
*/
|
|
33
|
+
export interface AuthoredDiscoveryHead {
|
|
34
|
+
readonly documentId: string;
|
|
35
|
+
readonly organizationId: string;
|
|
36
|
+
readonly accessEpoch: number;
|
|
37
|
+
readonly accessStateHash: string;
|
|
38
|
+
readonly linkedContainerIds: readonly string[];
|
|
39
|
+
}
|
|
40
|
+
/** Writes authored heads inside the caller's serialized write transaction. */
|
|
41
|
+
export declare function primeAuthoredDocumentDiscoveryHeads(execSql: ExecSql, authored: readonly AuthoredDiscoveryHead[]): Promise<void>;
|
|
26
42
|
export declare function createDocumentDiscoveryEvidenceStore(execSql: ExecSql, now?: () => number): DocumentDiscoveryEvidenceStore;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { and, asc, eq, inArray, lte, min, sql } from "drizzle-orm";
|
|
2
|
+
import { uniqueSortedStrings } from "../../documents/shared/readers.js";
|
|
2
3
|
import { readStringArray } from "../../recordReaders.js";
|
|
3
4
|
import { documentDiscoveryHeads, documentDiscoverySequence, pendingDocumentDiscoveries, } from "../../sqlite/documentDiscoveryEvidenceSchema.js";
|
|
4
5
|
import { accessManifestCheckpoints, documentContainerProjectionTables, } from "../../sqlite/schema.js";
|
|
@@ -231,6 +232,28 @@ class SqlDocumentDiscoveryEvidenceStore {
|
|
|
231
232
|
return true;
|
|
232
233
|
});
|
|
233
234
|
}
|
|
235
|
+
/** Writes authored heads inside the caller's serialized write transaction. */
|
|
236
|
+
export async function primeAuthoredDocumentDiscoveryHeads(execSql, authored) {
|
|
237
|
+
const db = getClientSQLitePersistenceRuntime(execSql).db;
|
|
238
|
+
for (const head of authored) {
|
|
239
|
+
const row = {
|
|
240
|
+
documentId: head.documentId,
|
|
241
|
+
organizationId: head.organizationId,
|
|
242
|
+
manifestHash: head.accessStateHash,
|
|
243
|
+
accessEpoch: head.accessEpoch,
|
|
244
|
+
linksJson: JSON.stringify(uniqueSortedStrings(head.linkedContainerIds)),
|
|
245
|
+
};
|
|
246
|
+
await db
|
|
247
|
+
.insert(heads)
|
|
248
|
+
.values(row)
|
|
249
|
+
.onConflictDoUpdate({
|
|
250
|
+
target: [heads.documentId, heads.organizationId],
|
|
251
|
+
set: row,
|
|
252
|
+
setWhere: lte(heads.accessEpoch, head.accessEpoch),
|
|
253
|
+
})
|
|
254
|
+
.run();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
234
257
|
export function createDocumentDiscoveryEvidenceStore(execSql, now = Date.now) {
|
|
235
258
|
return new SqlDocumentDiscoveryEvidenceStore(execSql, now);
|
|
236
259
|
}
|
|
@@ -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,8 +1,9 @@
|
|
|
1
1
|
import { isOrganizationContainerGrantSubjectType, isOrganizationGroupContainerAccessLevel, } from "../../../internal/validators/response/index.js";
|
|
2
2
|
import { asc, eq } from "drizzle-orm";
|
|
3
3
|
import { organizationReadModelContainerGrants } from "../../sqlite/organizationReadModelSchema.js";
|
|
4
|
+
import { sqliteInsertBatchSize } from "../../sqlite/sqliteBindBudget.js";
|
|
4
5
|
import { OrganizationReadModelIntegrityError } from "./organizationReadModelProtocol.js";
|
|
5
|
-
const GRANT_INSERT_BATCH_SIZE =
|
|
6
|
+
const GRANT_INSERT_BATCH_SIZE = sqliteInsertBatchSize(organizationReadModelContainerGrants);
|
|
6
7
|
function grantIdentity(grant) {
|
|
7
8
|
return `${grant.containerId}\0${grant.subjectType}\0${grant.subjectId}`;
|
|
8
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organizationReadModelGrantPersistence.js","sourceRoot":"","sources":["organizationReadModelGrantPersistence.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,uCAAuC,EACvC,uCAAuC,GACxC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,oCAAoC,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"organizationReadModelGrantPersistence.js","sourceRoot":"","sources":["organizationReadModelGrantPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n OrganizationContainerGrantResponse,\n OrganizationContainerGrantsResponse,\n} from \"@tearleads/validators/response\";\nimport {\n isOrganizationContainerGrantSubjectType,\n isOrganizationGroupContainerAccessLevel,\n} from \"@tearleads/validators/response\";\nimport { asc, eq } from \"drizzle-orm\";\nimport { organizationReadModelContainerGrants } from \"../../sqlite/organizationReadModelSchema\";\nimport { sqliteInsertBatchSize } from \"../../sqlite/sqliteBindBudget\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { OrganizationReadModelIntegrityError } from \"./organizationReadModelProtocol\";\n\nconst GRANT_INSERT_BATCH_SIZE = sqliteInsertBatchSize(\n organizationReadModelContainerGrants,\n);\n\ntype SelectedGrant = typeof organizationReadModelContainerGrants.$inferSelect;\n\nfunction grantIdentity(\n grant: Pick<\n OrganizationContainerGrantResponse,\n \"containerId\" | \"subjectId\" | \"subjectType\"\n >,\n): string {\n return `${grant.containerId}\\0${grant.subjectType}\\0${grant.subjectId}`;\n}\n\nfunction hasValidGrantSubjectBinding(\n grant: OrganizationContainerGrantResponse,\n): boolean {\n const validUser =\n grant.subjectType === \"user\" &&\n grant.userId === grant.subjectId &&\n grant.groupId === null;\n const validGroup =\n grant.subjectType === \"group\" &&\n grant.userId === null &&\n grant.signingKeyFingerprint === null &&\n grant.groupId === grant.subjectId;\n return validUser || validGroup;\n}\n\nfunction assertGrantsLane(input: {\n readonly lane: OrganizationContainerGrantsResponse;\n readonly organizationId: string;\n}): void {\n if (input.lane.organizationId !== input.organizationId) {\n throw new Error(\"Organization read-model grants scope does not match\");\n }\n const identities = input.lane.grants.map(grantIdentity);\n if (new Set(identities).size !== identities.length) {\n throw new Error(\"Organization read-model grants contain duplicates\");\n }\n for (const grant of input.lane.grants) {\n if (!hasValidGrantSubjectBinding(grant)) {\n throw new Error(\n \"Organization read-model grant subject binding is invalid\",\n );\n }\n }\n}\n\nexport async function applyOrganizationReadModelGrantsLane(input: {\n readonly lane: OrganizationContainerGrantsResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n assertGrantsLane(input);\n await input.tx\n .delete(organizationReadModelContainerGrants)\n .where(\n eq(\n organizationReadModelContainerGrants.organizationId,\n input.organizationId,\n ),\n )\n .run();\n\n const rows = input.lane.grants.map((grant, sortOrder) => ({\n ...grant,\n organizationId: input.organizationId,\n sortOrder,\n }));\n for (let index = 0; index < rows.length; index += GRANT_INSERT_BATCH_SIZE) {\n await input.tx\n .insert(organizationReadModelContainerGrants)\n .values(rows.slice(index, index + GRANT_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nfunction toGrant(row: SelectedGrant): OrganizationContainerGrantResponse {\n if (!isOrganizationGroupContainerAccessLevel(row.accessLevel)) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization grant access level is invalid\",\n );\n }\n if (!isOrganizationContainerGrantSubjectType(row.subjectType)) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization grant subject type is invalid\",\n );\n }\n const grant: OrganizationContainerGrantResponse = {\n accessLevel: row.accessLevel,\n containerId: row.containerId,\n createdAt: row.createdAt,\n depth: row.depth,\n isBuiltin: row.isBuiltin,\n metadataAccessEpoch: row.metadataAccessEpoch,\n metadataAccessStateHash: row.metadataAccessStateHash,\n metadataDocumentId: row.metadataDocumentId,\n parentId: row.parentId,\n updatedAt: row.updatedAt,\n subjectType: row.subjectType,\n subjectId: row.subjectId,\n userId: row.userId,\n signingKeyFingerprint: row.signingKeyFingerprint,\n groupId: row.groupId,\n };\n if (!hasValidGrantSubjectBinding(grant)) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization grant subject binding is invalid\",\n );\n }\n return grant;\n}\n\nexport async function loadOrganizationReadModelGrantsInTransaction(input: {\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<OrganizationContainerGrantsResponse> {\n const rows = await input.tx\n .select()\n .from(organizationReadModelContainerGrants)\n .where(\n eq(\n organizationReadModelContainerGrants.organizationId,\n input.organizationId,\n ),\n )\n .orderBy(\n asc(organizationReadModelContainerGrants.sortOrder),\n asc(organizationReadModelContainerGrants.containerId),\n asc(organizationReadModelContainerGrants.subjectType),\n asc(organizationReadModelContainerGrants.subjectId),\n );\n return {\n organizationId: input.organizationId,\n grants: rows.map(toGrant),\n };\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OrganizationReadModelGroupMemberResponse } from "../../../internal/validators/response/index.js";
|
|
2
|
+
import type { ExecSql } from "../../sqlite/sqlSchema.js";
|
|
3
|
+
/** Locally projected members of one group, by user and role. */
|
|
4
|
+
export interface OrganizationReadModelGroupMembers {
|
|
5
|
+
readonly groupId: string;
|
|
6
|
+
readonly members: OrganizationReadModelGroupMemberResponse[];
|
|
7
|
+
readonly organizationId: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function loadOrganizationReadModelGroupMembers(execSql: ExecSql, organizationId: string, groupId: string, currentUserId: string): Promise<OrganizationReadModelGroupMembers | null>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { and, asc, eq } from "drizzle-orm";
|
|
2
|
-
import { organizationReadModelGroupMembers,
|
|
2
|
+
import { organizationReadModelGroupMembers, organizationReadModelGroups, organizationReadModelRequesters, organizationReadModelState, } from "../../sqlite/organizationReadModelSchema.js";
|
|
3
3
|
import { OrganizationReadModelIntegrityError } from "./organizationReadModelProtocol.js";
|
|
4
4
|
import { loadWithOrganizationReadModelIntegrityPurge } from "./organizationReadModelPurge.js";
|
|
5
5
|
function requireOrganizationRole(value) {
|
|
@@ -15,47 +15,48 @@ function toGroupMember(row, expectedStateHash) {
|
|
|
15
15
|
return {
|
|
16
16
|
userId: row.userId,
|
|
17
17
|
role: requireOrganizationRole(row.role),
|
|
18
|
-
signingKeyFingerprint: row.signingKeyFingerprint,
|
|
19
|
-
signingPublicKey: row.signingPublicKey,
|
|
20
|
-
encapsulationPublicKey: row.encapsulationPublicKey,
|
|
21
|
-
encapsulationKeyFingerprint: row.encapsulationKeyFingerprint,
|
|
22
18
|
};
|
|
23
19
|
}
|
|
24
20
|
async function loadMembershipRows(input) {
|
|
25
21
|
return input.tx
|
|
26
22
|
.select({
|
|
27
|
-
encapsulationKeyFingerprint: organizationReadModelGroupMembers.encapsulationKeyFingerprint,
|
|
28
|
-
encapsulationPublicKey: organizationReadModelGroupMembers.encapsulationPublicKey,
|
|
29
23
|
userId: organizationReadModelGroupMembers.userId,
|
|
30
24
|
role: organizationReadModelGroupMembers.role,
|
|
31
|
-
signingKeyFingerprint: organizationReadModelGroupMembers.signingKeyFingerprint,
|
|
32
|
-
signingPublicKey: organizationReadModelGroupMembers.signingPublicKey,
|
|
33
25
|
stateHash: organizationReadModelGroupMembers.stateHash,
|
|
34
26
|
})
|
|
35
27
|
.from(organizationReadModelGroupMembers)
|
|
36
28
|
.where(and(eq(organizationReadModelGroupMembers.organizationId, input.organizationId), eq(organizationReadModelGroupMembers.groupId, input.groupId)))
|
|
37
29
|
.orderBy(asc(organizationReadModelGroupMembers.sortOrder), asc(organizationReadModelGroupMembers.userId));
|
|
38
30
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
.where(and(eq(organizationReadModelGroupMemberships.organizationId, input.organizationId), eq(organizationReadModelGroupMemberships.groupId, input.groupId)))
|
|
44
|
-
.limit(1);
|
|
45
|
-
if (!head) {
|
|
46
|
-
return null;
|
|
31
|
+
/** The reserved Members head lives on the state row; others on catalog rows. */
|
|
32
|
+
async function loadMembershipHead(input) {
|
|
33
|
+
if (input.groupId === input.memberGroup.groupId) {
|
|
34
|
+
return { memberCount: null, stateHash: input.memberGroup.stateHash };
|
|
47
35
|
}
|
|
48
|
-
const rows = await loadMembershipRows(input);
|
|
49
36
|
const [group] = await input.tx
|
|
50
37
|
.select({
|
|
51
38
|
memberCount: organizationReadModelGroups.memberCount,
|
|
39
|
+
membersStateHash: organizationReadModelGroups.membersStateHash,
|
|
52
40
|
stateHash: organizationReadModelGroups.stateHash,
|
|
53
41
|
})
|
|
54
42
|
.from(organizationReadModelGroups)
|
|
55
43
|
.where(and(eq(organizationReadModelGroups.organizationId, input.organizationId), eq(organizationReadModelGroups.groupId, input.groupId)))
|
|
56
44
|
.limit(1);
|
|
57
|
-
if (group
|
|
58
|
-
|
|
45
|
+
if (!group?.membersStateHash) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (group.stateHash !== group.membersStateHash) {
|
|
49
|
+
throw new OrganizationReadModelIntegrityError("Stored organization group membership binding is invalid");
|
|
50
|
+
}
|
|
51
|
+
return { memberCount: group.memberCount, stateHash: group.membersStateHash };
|
|
52
|
+
}
|
|
53
|
+
async function loadOrganizationReadModelGroupMembersInTransaction(input) {
|
|
54
|
+
const head = await loadMembershipHead(input);
|
|
55
|
+
if (!head) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const rows = await loadMembershipRows(input);
|
|
59
|
+
if (head.memberCount !== null && head.memberCount !== rows.length) {
|
|
59
60
|
throw new OrganizationReadModelIntegrityError("Stored organization group membership binding is invalid");
|
|
60
61
|
}
|
|
61
62
|
return {
|
|
@@ -70,7 +71,10 @@ export async function loadOrganizationReadModelGroupMembers(execSql, organizatio
|
|
|
70
71
|
organizationId,
|
|
71
72
|
load: async (tx) => {
|
|
72
73
|
const [state] = await tx
|
|
73
|
-
.select({
|
|
74
|
+
.select({
|
|
75
|
+
memberGroupId: organizationReadModelState.memberGroupId,
|
|
76
|
+
memberGroupStateHash: organizationReadModelState.memberGroupStateHash,
|
|
77
|
+
})
|
|
74
78
|
.from(organizationReadModelState)
|
|
75
79
|
.where(eq(organizationReadModelState.organizationId, organizationId))
|
|
76
80
|
.limit(1);
|
|
@@ -87,6 +91,10 @@ export async function loadOrganizationReadModelGroupMembers(execSql, organizatio
|
|
|
87
91
|
}
|
|
88
92
|
return loadOrganizationReadModelGroupMembersInTransaction({
|
|
89
93
|
groupId,
|
|
94
|
+
memberGroup: {
|
|
95
|
+
groupId: state.memberGroupId,
|
|
96
|
+
stateHash: state.memberGroupStateHash,
|
|
97
|
+
},
|
|
90
98
|
organizationId,
|
|
91
99
|
tx,
|
|
92
100
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organizationReadModelMemberLoad.js","sourceRoot":"","sources":["organizationReadModelMemberLoad.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,iCAAiC,EACjC,qCAAqC,EACrC,2BAA2B,EAC3B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAAE,mCAAmC,EAAE,MAAM,iCAAiC,CAAC;AACtF,OAAO,EAAE,2CAA2C,EAAE,MAAM,8BAA8B,CAAC;AAY3F,SAAS,uBAAuB,CAAC,KAAa;IAC5C,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QAC5C,MAAM,IAAI,mCAAmC,CAC3C,kDAAkD,CACnD,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CACpB,GAAwB,EACxB,iBAAyB;IAEzB,IAAI,GAAG,CAAC,SAAS,KAAK,iBAAiB,EAAE,CAAC;QACxC,MAAM,IAAI,mCAAmC,CAC3C,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,uBAAuB,CAAC,GAAG,CAAC,IAAI,CAAC;QACvC,qBAAqB,EAAE,GAAG,CAAC,qBAAqB;QAChD,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,sBAAsB,EAAE,GAAG,CAAC,sBAAsB;QAClD,2BAA2B,EAAE,GAAG,CAAC,2BAA2B;KAC7D,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,KAIjC;IACC,OAAO,KAAK,CAAC,EAAE;SACZ,MAAM,CAAC;QACN,2BAA2B,EACzB,iCAAiC,CAAC,2BAA2B;QAC/D,sBAAsB,EACpB,iCAAiC,CAAC,sBAAsB;QAC1D,MAAM,EAAE,iCAAiC,CAAC,MAAM;QAChD,IAAI,EAAE,iCAAiC,CAAC,IAAI;QAC5C,qBAAqB,EACnB,iCAAiC,CAAC,qBAAqB;QACzD,gBAAgB,EAAE,iCAAiC,CAAC,gBAAgB;QACpE,SAAS,EAAE,iCAAiC,CAAC,SAAS;KACvD,CAAC;SACD,IAAI,CAAC,iCAAiC,CAAC;SACvC,KAAK,CACJ,GAAG,CACD,EAAE,CACA,iCAAiC,CAAC,cAAc,EAChD,KAAK,CAAC,cAAc,CACrB,EACD,EAAE,CAAC,iCAAiC,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAC7D,CACF;SACA,OAAO,CACN,GAAG,CAAC,iCAAiC,CAAC,SAAS,CAAC,EAChD,GAAG,CAAC,iCAAiC,CAAC,MAAM,CAAC,CAC9C,CAAC;AACN,CAAC;AAED,KAAK,UAAU,kDAAkD,CAAC,KAIjE;IACC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,KAAK,CAAC,EAAE;SAC1B,MAAM,CAAC,EAAE,SAAS,EAAE,qCAAqC,CAAC,SAAS,EAAE,CAAC;SACtE,IAAI,CAAC,qCAAqC,CAAC;SAC3C,KAAK,CACJ,GAAG,CACD,EAAE,CACA,qCAAqC,CAAC,cAAc,EACpD,KAAK,CAAC,cAAc,CACrB,EACD,EAAE,CAAC,qCAAqC,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CACjE,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC,EAAE;SAC3B,MAAM,CAAC;QACN,WAAW,EAAE,2BAA2B,CAAC,WAAW;QACpD,SAAS,EAAE,2BAA2B,CAAC,SAAS;KACjD,CAAC;SACD,IAAI,CAAC,2BAA2B,CAAC;SACjC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,2BAA2B,CAAC,cAAc,EAAE,KAAK,CAAC,cAAc,CAAC,EACpE,EAAE,CAAC,2BAA2B,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CACvD,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,IACE,KAAK;QACL,CAAC,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,CAAC,MAAM,CAAC,EACzE,CAAC;QACD,MAAM,IAAI,mCAAmC,CAC3C,yDAAyD,CAC1D,CAAC;IACJ,CAAC;IACD,OAAO;QACL,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;KAC/D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qCAAqC,CACzD,OAAgB,EAChB,cAAsB,EACtB,OAAe,EACf,aAAqB;IAErB,OAAO,2CAA2C,CAAC;QACjD,OAAO;QACP,cAAc;QACd,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;YACjB,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,EAAE;iBACrB,MAAM,CAAC,EAAE,cAAc,EAAE,0BAA0B,CAAC,cAAc,EAAE,CAAC;iBACrE,IAAI,CAAC,0BAA0B,CAAC;iBAChC,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;iBACpE,KAAK,CAAC,CAAC,CAAC,CAAC;YACZ,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,EAAE;iBACzB,MAAM,CAAC,EAAE,MAAM,EAAE,+BAA+B,CAAC,MAAM,EAAE,CAAC;iBAC1D,IAAI,CAAC,+BAA+B,CAAC;iBACrC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,+BAA+B,CAAC,cAAc,EAAE,cAAc,CAAC,EAClE,EAAE,CAAC,+BAA+B,CAAC,MAAM,EAAE,aAAa,CAAC,CAC1D,CACF;iBACA,KAAK,CAAC,CAAC,CAAC,CAAC;YACZ,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,kDAAkD,CAAC;gBACxD,OAAO;gBACP,cAAc;gBACd,EAAE;aACH,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n OrganizationGroupMemberResponse,\n OrganizationGroupMembersResponse,\n OrganizationRole,\n} from \"@tearleads/validators/response\";\nimport { and, asc, eq } from \"drizzle-orm\";\nimport {\n organizationReadModelGroupMembers,\n organizationReadModelGroupMemberships,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { OrganizationReadModelIntegrityError } from \"./organizationReadModelProtocol\";\nimport { loadWithOrganizationReadModelIntegrityPurge } from \"./organizationReadModelPurge\";\n\ninterface SelectedGroupMember {\n readonly encapsulationKeyFingerprint: string | null;\n readonly encapsulationPublicKey: string | null;\n readonly userId: string;\n readonly role: string;\n readonly signingKeyFingerprint: string | null;\n readonly signingPublicKey: string | null;\n readonly stateHash: string;\n}\n\nfunction requireOrganizationRole(value: string): OrganizationRole {\n if (value !== \"member\" && value !== \"admin\") {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization group member role is invalid\",\n );\n }\n return value;\n}\n\nfunction toGroupMember(\n row: SelectedGroupMember,\n expectedStateHash: string,\n): OrganizationGroupMemberResponse {\n if (row.stateHash !== expectedStateHash) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization group membership state is inconsistent\",\n );\n }\n return {\n userId: row.userId,\n role: requireOrganizationRole(row.role),\n signingKeyFingerprint: row.signingKeyFingerprint,\n signingPublicKey: row.signingPublicKey,\n encapsulationPublicKey: row.encapsulationPublicKey,\n encapsulationKeyFingerprint: row.encapsulationKeyFingerprint,\n };\n}\n\nasync function loadMembershipRows(input: {\n readonly groupId: string;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<SelectedGroupMember[]> {\n return input.tx\n .select({\n encapsulationKeyFingerprint:\n organizationReadModelGroupMembers.encapsulationKeyFingerprint,\n encapsulationPublicKey:\n organizationReadModelGroupMembers.encapsulationPublicKey,\n userId: organizationReadModelGroupMembers.userId,\n role: organizationReadModelGroupMembers.role,\n signingKeyFingerprint:\n organizationReadModelGroupMembers.signingKeyFingerprint,\n signingPublicKey: organizationReadModelGroupMembers.signingPublicKey,\n stateHash: organizationReadModelGroupMembers.stateHash,\n })\n .from(organizationReadModelGroupMembers)\n .where(\n and(\n eq(\n organizationReadModelGroupMembers.organizationId,\n input.organizationId,\n ),\n eq(organizationReadModelGroupMembers.groupId, input.groupId),\n ),\n )\n .orderBy(\n asc(organizationReadModelGroupMembers.sortOrder),\n asc(organizationReadModelGroupMembers.userId),\n );\n}\n\nasync function loadOrganizationReadModelGroupMembersInTransaction(input: {\n readonly groupId: string;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<OrganizationGroupMembersResponse | null> {\n const [head] = await input.tx\n .select({ stateHash: organizationReadModelGroupMemberships.stateHash })\n .from(organizationReadModelGroupMemberships)\n .where(\n and(\n eq(\n organizationReadModelGroupMemberships.organizationId,\n input.organizationId,\n ),\n eq(organizationReadModelGroupMemberships.groupId, input.groupId),\n ),\n )\n .limit(1);\n if (!head) {\n return null;\n }\n\n const rows = await loadMembershipRows(input);\n const [group] = await input.tx\n .select({\n memberCount: organizationReadModelGroups.memberCount,\n stateHash: organizationReadModelGroups.stateHash,\n })\n .from(organizationReadModelGroups)\n .where(\n and(\n eq(organizationReadModelGroups.organizationId, input.organizationId),\n eq(organizationReadModelGroups.groupId, input.groupId),\n ),\n )\n .limit(1);\n if (\n group &&\n (group.stateHash !== head.stateHash || group.memberCount !== rows.length)\n ) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization group membership binding is invalid\",\n );\n }\n return {\n organizationId: input.organizationId,\n groupId: input.groupId,\n members: rows.map((row) => toGroupMember(row, head.stateHash)),\n };\n}\n\nexport async function loadOrganizationReadModelGroupMembers(\n execSql: ExecSql,\n organizationId: string,\n groupId: string,\n currentUserId: string,\n): Promise<OrganizationGroupMembersResponse | null> {\n return loadWithOrganizationReadModelIntegrityPurge({\n execSql,\n organizationId,\n load: async (tx) => {\n const [state] = await tx\n .select({ organizationId: organizationReadModelState.organizationId })\n .from(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, organizationId))\n .limit(1);\n if (!state) {\n return null;\n }\n const [requester] = await tx\n .select({ userId: organizationReadModelRequesters.userId })\n .from(organizationReadModelRequesters)\n .where(\n and(\n eq(organizationReadModelRequesters.organizationId, organizationId),\n eq(organizationReadModelRequesters.userId, currentUserId),\n ),\n )\n .limit(1);\n if (!requester) {\n return null;\n }\n return loadOrganizationReadModelGroupMembersInTransaction({\n groupId,\n organizationId,\n tx,\n });\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"organizationReadModelMemberLoad.js","sourceRoot":"","sources":["organizationReadModelMemberLoad.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n OrganizationReadModelGroupMemberResponse,\n OrganizationRole,\n} from \"@tearleads/validators/response\";\nimport { and, asc, eq } from \"drizzle-orm\";\nimport {\n organizationReadModelGroupMembers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { OrganizationReadModelIntegrityError } from \"./organizationReadModelProtocol\";\nimport { loadWithOrganizationReadModelIntegrityPurge } from \"./organizationReadModelPurge\";\n\ninterface SelectedGroupMember {\n readonly userId: string;\n readonly role: string;\n readonly stateHash: string;\n}\n\n/** Locally projected members of one group, by user and role. */\nexport interface OrganizationReadModelGroupMembers {\n readonly groupId: string;\n readonly members: OrganizationReadModelGroupMemberResponse[];\n readonly organizationId: string;\n}\n\nfunction requireOrganizationRole(value: string): OrganizationRole {\n if (value !== \"member\" && value !== \"admin\") {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization group member role is invalid\",\n );\n }\n return value;\n}\n\nfunction toGroupMember(\n row: SelectedGroupMember,\n expectedStateHash: string,\n): OrganizationReadModelGroupMemberResponse {\n if (row.stateHash !== expectedStateHash) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization group membership state is inconsistent\",\n );\n }\n return {\n userId: row.userId,\n role: requireOrganizationRole(row.role),\n };\n}\n\nasync function loadMembershipRows(input: {\n readonly groupId: string;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<SelectedGroupMember[]> {\n return input.tx\n .select({\n userId: organizationReadModelGroupMembers.userId,\n role: organizationReadModelGroupMembers.role,\n stateHash: organizationReadModelGroupMembers.stateHash,\n })\n .from(organizationReadModelGroupMembers)\n .where(\n and(\n eq(\n organizationReadModelGroupMembers.organizationId,\n input.organizationId,\n ),\n eq(organizationReadModelGroupMembers.groupId, input.groupId),\n ),\n )\n .orderBy(\n asc(organizationReadModelGroupMembers.sortOrder),\n asc(organizationReadModelGroupMembers.userId),\n );\n}\n\ninterface StoredMembershipHead {\n readonly memberCount: number | null;\n readonly stateHash: string;\n}\n\n/** The reserved Members head lives on the state row; others on catalog rows. */\nasync function loadMembershipHead(input: {\n readonly groupId: string;\n readonly memberGroup: {\n readonly groupId: string;\n readonly stateHash: string;\n };\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<StoredMembershipHead | null> {\n if (input.groupId === input.memberGroup.groupId) {\n return { memberCount: null, stateHash: input.memberGroup.stateHash };\n }\n const [group] = await input.tx\n .select({\n memberCount: organizationReadModelGroups.memberCount,\n membersStateHash: organizationReadModelGroups.membersStateHash,\n stateHash: organizationReadModelGroups.stateHash,\n })\n .from(organizationReadModelGroups)\n .where(\n and(\n eq(organizationReadModelGroups.organizationId, input.organizationId),\n eq(organizationReadModelGroups.groupId, input.groupId),\n ),\n )\n .limit(1);\n if (!group?.membersStateHash) {\n return null;\n }\n if (group.stateHash !== group.membersStateHash) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization group membership binding is invalid\",\n );\n }\n return { memberCount: group.memberCount, stateHash: group.membersStateHash };\n}\n\nasync function loadOrganizationReadModelGroupMembersInTransaction(input: {\n readonly groupId: string;\n readonly memberGroup: {\n readonly groupId: string;\n readonly stateHash: string;\n };\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<OrganizationReadModelGroupMembers | null> {\n const head = await loadMembershipHead(input);\n if (!head) {\n return null;\n }\n const rows = await loadMembershipRows(input);\n if (head.memberCount !== null && head.memberCount !== rows.length) {\n throw new OrganizationReadModelIntegrityError(\n \"Stored organization group membership binding is invalid\",\n );\n }\n return {\n organizationId: input.organizationId,\n groupId: input.groupId,\n members: rows.map((row) => toGroupMember(row, head.stateHash)),\n };\n}\n\nexport async function loadOrganizationReadModelGroupMembers(\n execSql: ExecSql,\n organizationId: string,\n groupId: string,\n currentUserId: string,\n): Promise<OrganizationReadModelGroupMembers | null> {\n return loadWithOrganizationReadModelIntegrityPurge({\n execSql,\n organizationId,\n load: async (tx) => {\n const [state] = await tx\n .select({\n memberGroupId: organizationReadModelState.memberGroupId,\n memberGroupStateHash: organizationReadModelState.memberGroupStateHash,\n })\n .from(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, organizationId))\n .limit(1);\n if (!state) {\n return null;\n }\n const [requester] = await tx\n .select({ userId: organizationReadModelRequesters.userId })\n .from(organizationReadModelRequesters)\n .where(\n and(\n eq(organizationReadModelRequesters.organizationId, organizationId),\n eq(organizationReadModelRequesters.userId, currentUserId),\n ),\n )\n .limit(1);\n if (!requester) {\n return null;\n }\n return loadOrganizationReadModelGroupMembersInTransaction({\n groupId,\n memberGroup: {\n groupId: state.memberGroupId,\n stateHash: state.memberGroupStateHash,\n },\n organizationId,\n tx,\n });\n },\n });\n}\n"]}
|