@yielded/auth-persistence 0.1.0-beta.18 → 0.1.0-beta.19

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.
Files changed (75) hide show
  1. package/README.md +5 -2
  2. package/dist/Adapter.d.mts +2 -2
  3. package/dist/Adapter.mjs +2 -2
  4. package/dist/internal/configuration.d.mts +4 -3
  5. package/dist/internal/configuration.mjs.map +1 -1
  6. package/dist/internal/crypto.mjs +12 -0
  7. package/dist/internal/crypto.mjs.map +1 -0
  8. package/dist/internal/passkey/admission.mjs +19 -15
  9. package/dist/internal/passkey/admission.mjs.map +1 -1
  10. package/dist/internal/passkey/credentials.mjs +4 -4
  11. package/dist/internal/passkey/credentials.mjs.map +1 -1
  12. package/dist/internal/passkey/enrollment.mjs +6 -6
  13. package/dist/internal/passkey/enrollment.mjs.map +1 -1
  14. package/dist/internal/passkey/flow.mjs +7 -7
  15. package/dist/internal/passkey/flow.mjs.map +1 -1
  16. package/dist/internal/passkey/management.mjs +2 -2
  17. package/dist/internal/passkey/management.mjs.map +1 -1
  18. package/dist/internal/passkey/registration-ceremony.mjs +2 -2
  19. package/dist/internal/passkey/registration-ceremony.mjs.map +1 -1
  20. package/dist/internal/passkey/registration-custody.mjs +1 -1
  21. package/dist/internal/passkey/registration-custody.mjs.map +1 -1
  22. package/dist/internal/passkey/registration-write.mjs +12 -12
  23. package/dist/internal/passkey/registration-write.mjs.map +1 -1
  24. package/dist/internal/passkey/state.d.mts +1 -1
  25. package/dist/internal/passkey/state.mjs +8 -9
  26. package/dist/internal/passkey/state.mjs.map +1 -1
  27. package/dist/internal/passkey/target.d.mts +1 -1
  28. package/dist/internal/passkey/target.mjs.map +1 -1
  29. package/dist/internal/passkey/write-state.mjs +13 -13
  30. package/dist/internal/passkey/write-state.mjs.map +1 -1
  31. package/dist/internal/passkey/write-target.mjs.map +1 -1
  32. package/dist/internal/passkey-kernel.d.mts +512 -56
  33. package/dist/internal/passkeys.mjs +32 -14
  34. package/dist/internal/passkeys.mjs.map +1 -1
  35. package/dist/internal/password-kernel.d.mts +1 -1
  36. package/dist/internal/persistence.d.mts +2 -2
  37. package/dist/internal/persistence.mjs +16 -7
  38. package/dist/internal/persistence.mjs.map +1 -1
  39. package/dist/internal/phone-kernel.d.mts +5 -5
  40. package/dist/internal/phone-kernel.mjs +14 -14
  41. package/dist/internal/phone-kernel.mjs.map +1 -1
  42. package/dist/internal/registration.mjs +8 -7
  43. package/dist/internal/registration.mjs.map +1 -1
  44. package/dist/internal/storage-mapping.d.mts +5 -4
  45. package/dist/internal/storage-mapping.mjs +16 -13
  46. package/dist/internal/storage-mapping.mjs.map +1 -1
  47. package/dist/internal/storage-validation.d.mts +13 -3
  48. package/dist/internal/storage-validation.mjs +128 -68
  49. package/dist/internal/storage-validation.mjs.map +1 -1
  50. package/dist/internal/transaction-execution-kernel.d.mts +4 -4
  51. package/dist/internal/transaction-execution-kernel.mjs +7 -5
  52. package/dist/internal/transaction-execution-kernel.mjs.map +1 -1
  53. package/package.json +1 -1
  54. package/src/Adapter.ts +7 -1
  55. package/src/internal/configuration.ts +4 -2
  56. package/src/internal/crypto.ts +15 -0
  57. package/src/internal/passkey/admission.ts +22 -15
  58. package/src/internal/passkey/credentials.ts +4 -4
  59. package/src/internal/passkey/enrollment.ts +7 -7
  60. package/src/internal/passkey/flow.ts +8 -8
  61. package/src/internal/passkey/management.ts +7 -2
  62. package/src/internal/passkey/registration-ceremony.ts +2 -2
  63. package/src/internal/passkey/registration-custody.ts +1 -1
  64. package/src/internal/passkey/registration-write.ts +12 -12
  65. package/src/internal/passkey/state.ts +10 -13
  66. package/src/internal/passkey/target.ts +16 -8
  67. package/src/internal/passkey/write-state.ts +10 -14
  68. package/src/internal/passkey/write-target.ts +11 -3
  69. package/src/internal/passkeys.ts +121 -70
  70. package/src/internal/persistence.ts +28 -11
  71. package/src/internal/phone-kernel.ts +26 -17
  72. package/src/internal/registration.ts +10 -8
  73. package/src/internal/storage-mapping.ts +21 -13
  74. package/src/internal/storage-validation.ts +219 -139
  75. package/src/internal/transaction-execution-kernel.ts +30 -11
@@ -1 +1 @@
1
- {"version":3,"file":"storage-mapping.mjs","names":["ProofRecord"],"sources":["../../src/internal/storage-mapping.ts"],"sourcesContent":["import { randomId } from \"@yielded/auth-crypto\";\nimport { EmailCredentialSnapshot } from \"@yielded/auth/Email\";\nimport { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n PasswordAttemptId,\n PasswordCredentialSnapshot,\n EncodedPasswordHash,\n type PasswordReplacement,\n} from \"@yielded/auth/Password\";\nimport {\n ProofBinding,\n ProofContinuationId,\n ProofDeliveryId,\n ProofId,\n ProofPurpose,\n ProofRequestId,\n ProofRequestReceipt,\n ProofVersion,\n} from \"@yielded/auth/Proofs\";\nimport { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SecurityRevision,\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Redacted, Schema } from \"effect\";\n\nimport type { MappingInput } from \"./configuration\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { requiredEmailAddressConstraints, type AnyEmailAddressMapping } from \"./models/email-model\";\nimport {\n requiredPasswordConstraints,\n type AnyPasswordPersistenceMapping,\n} from \"./models/password-model\";\nimport { requiredProofConstraints, type AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport type { StatefulSessionMapping } from \"./models/session-model\";\nimport type { TableModel, SqlFragment } from \"./query-operations\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\n\nconst failure = (cause: unknown) => PersistenceMappingError.make({ operation: \"decode\", cause });\n\nconst decode = <S extends Schema.Codec<unknown, unknown>>(schema: S, value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(failure));\n\nconst string = (value: unknown) => Schema.decodeUnknownSync(Schema.String)(value);\nconst tuple = Schema.encodeSync(Schema.fromJsonString(Schema.Array(Schema.String)));\nconst bindingJson = Schema.fromJsonString(ProofBinding);\nconst receiptJson = Schema.fromJsonString(ProofRequestReceipt);\n\nconst ProofRecord = Schema.Struct({\n moduleId: Schema.String,\n purpose: ProofPurpose,\n proofId: ProofId,\n requestId: ProofRequestId,\n fingerprint: TokenDigest,\n deliveryId: ProofDeliveryId,\n binding: ProofBinding,\n verifier: Schema.Struct({ keyId: Schema.String, digest: TokenDigest }),\n issuedAtMillis: Schema.Int,\n expiresAtMillis: Schema.Int,\n version: ProofVersion,\n});\n\nconst ProofContinuation = Schema.Struct({\n moduleId: Schema.String,\n purpose: ProofPurpose,\n continuationId: ProofContinuationId,\n digest: TokenDigest,\n proofId: ProofId,\n seriesKey: Schema.String,\n binding: ProofBinding,\n expiresAtMillis: Schema.Int,\n version: ProofVersion,\n});\n\n/** Derive shared row mappings from a managed or custom storage layout.\n * Each factory requires its role tables to exist. Adapter authors supply typed\n * table handles and dialect-specific clocks/commit predicates, and may refine\n * authority policy for explicit services. This does not alter composed defaults.\n */\nexport const makeMappings = (input: MappingInput) => {\n const table = (role: StorageRole) => {\n const found = input.tables[role];\n\n if (found === undefined) throw failure(`Missing ${role} mapping`);\n\n return found;\n };\n\n const mapped = (role: StorageRole) => ({\n table: table(role),\n ...Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])),\n });\n\n const { subjects: s, encodeInstant: instant, decodeInstant: readInstant } = input;\n const subjectId = { toNative: s.toNative, toSubject: s.toSubject, equals: Object.is };\n\n const subject = {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n isActiveStatus: (value: unknown) => Object.is(value, s.activeValue),\n d1ActiveStatusValue: s.activeValue,\n decodeRequirement: s.requirements,\n decodeActionRequirement: s.actionRequirements,\n nextSecurityRevisionSync: () => SecurityRevision.make(randomId()),\n };\n\n const authorityCredential = () => ({\n ...mapped(\"credentials\"),\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n revision: \"revision\",\n status: \"active\",\n isActiveStatus: (value: unknown) => value === true,\n encodeInsert: (row: object) => ({ ...row, active: true }),\n encodeRevision: (revision: SecurityRevision) => ({ revision }),\n });\n\n const authority = () => ({ subjectId, subject, credential: authorityCredential() });\n\n const proofs = (): AnyProofPersistenceMapping<SqlFragment> => ({\n constraints: requiredProofConstraints,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateVersionSync: () => ProofVersion.make(randomId()),\n isRequestConflict: () => false,\n isSeriesConflict: () => false,\n isCommandConflict: () => false,\n scopeKeys: ({ binding }) => ({\n series: tuple([\n binding.identifier.namespace,\n binding.identifier.value,\n binding._tag === \"Identifier\" ? \"anonymous\" : binding.revision.subjectId,\n ]),\n identifier: tuple([binding.identifier.namespace, binding.identifier.value]),\n subject: binding._tag === \"Identifier\" ? \"anonymous\" : binding.revision.subjectId,\n }),\n authority: {\n ...authority(),\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n isCurrent: (request, rows) =>\n request.binding._tag === \"Identifier\"\n ? rows.length === 0\n : request.binding._tag === \"IdentifierChange\"\n ? rows.length === 0 ||\n (rows.length === 1 &&\n rows.every(\n (row) =>\n Object.is(row.subjectId, request.nativeSubjectId) &&\n row.active === true &&\n row.verifiedAt === null,\n ))\n : rows.length === 1 &&\n rows.every(\n (row) => Object.is(row.subjectId, request.nativeSubjectId) && row.active === true,\n ),\n },\n },\n request: {\n ...mapped(\"proofRequests\"),\n moduleId: \"moduleId\",\n requestId: \"requestId\",\n fingerprint: \"fingerprint\",\n proofId: \"proofId\",\n purpose: \"purpose\",\n keyId: \"keyId\",\n createdAt: \"createdAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: ({ record, retentionUntilMillis }) => ({\n moduleId: record.moduleId,\n requestId: record.requestId,\n fingerprint: record.fingerprint,\n proofId: record.proofId,\n purpose: record.purpose,\n keyId: record.verifier.keyId,\n createdAt: instant(record.issuedAtMillis),\n retentionUntil: instant(retentionUntilMillis),\n receipt: Schema.encodeSync(receiptJson)({\n requestId: record.requestId,\n reference: {\n proofId: record.proofId,\n purpose: record.purpose,\n keyId: record.verifier.keyId,\n },\n }),\n }),\n decodeReceipt: (row) => decode(receiptJson, row.receipt),\n },\n series: {\n ...mapped(\"proofSeries\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n scopeKey: \"scopeKey\",\n activeProofId: \"activeProofId\",\n lastIssueAt: \"lastIssueAt\",\n version: \"version\",\n encodeInsert: (row) => ({ ...row, activeProofId: null, lastIssueAt: null }),\n },\n generation: {\n ...mapped(\"proofGenerations\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n proofId: \"proofId\",\n requestId: \"requestId\",\n seriesKey: \"seriesKey\",\n deliveryId: \"deliveryId\",\n binding: \"binding\",\n verifierKeyId: \"verifierKeyId\",\n verifierDigest: \"verifierDigest\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n version: \"version\",\n state: \"state\",\n sendCount: \"sendCount\",\n deliveryState: \"deliveryState\",\n claimVersion: \"claimVersion\",\n claimDeadline: \"claimDeadline\",\n retryAt: \"retryAt\",\n deliveryRetryMillis: \"deliveryRetryMillis\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: ({\n record,\n seriesKey,\n retentionUntilMillis,\n state,\n deliveryState,\n policy,\n }) => ({\n moduleId: record.moduleId,\n purpose: record.purpose,\n proofId: record.proofId,\n requestId: record.requestId,\n seriesKey,\n deliveryId: record.deliveryId,\n binding: Schema.encodeSync(bindingJson)(record.binding),\n verifierKeyId: record.verifier.keyId,\n verifierDigest: record.verifier.digest,\n issuedAt: instant(record.issuedAtMillis),\n expiresAt: instant(record.expiresAtMillis),\n version: record.version,\n state,\n sendCount: 0,\n deliveryState,\n claimVersion: null,\n claimDeadline: null,\n retryAt: null,\n deliveryRetryMillis: policy.deliveryRetryMillis,\n retentionUntil: instant(retentionUntilMillis),\n fingerprint: record.fingerprint,\n }),\n decodeBinding: (row) => decode(bindingJson, row.binding),\n decodeRecord: (row) =>\n Effect.gen(function* () {\n return yield* decode(ProofRecord, {\n moduleId: row.moduleId,\n purpose: row.purpose,\n proofId: row.proofId,\n requestId: row.requestId,\n fingerprint: row.fingerprint,\n deliveryId: row.deliveryId,\n binding: yield* decode(bindingJson, row.binding),\n verifier: { keyId: row.verifierKeyId, digest: row.verifierDigest },\n issuedAtMillis: yield* readInstant(row.issuedAt),\n expiresAtMillis: yield* readInstant(row.expiresAt),\n version: row.version,\n });\n }),\n },\n continuation: {\n ...mapped(\"proofContinuations\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n continuationId: \"continuationId\",\n digest: \"digest\",\n proofId: \"proofId\",\n seriesKey: \"seriesKey\",\n binding: \"binding\",\n expiresAt: \"expiresAt\",\n consumed: \"consumed\",\n version: \"version\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (record) => ({\n ...record,\n binding: Schema.encodeSync(bindingJson)(record.binding),\n expiresAt: instant(record.expiresAtMillis),\n consumed: false,\n retentionUntil: instant(record.retentionUntilMillis),\n }),\n decode: (row) =>\n Effect.gen(function* () {\n return yield* decode(ProofContinuation, {\n ...row,\n binding: yield* decode(bindingJson, row.binding),\n expiresAtMillis: yield* readInstant(row.expiresAt),\n });\n }),\n },\n rateScope: {\n ...mapped(\"proofScopes\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n encodeInsert: (row) => ({ ...row }),\n },\n abuseEvent: {\n ...mapped(\"proofAbuse\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n commandId: \"commandId\",\n occurredAt: \"occurredAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({\n ...row,\n occurredAt: instant(row.occurredAtMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n }),\n },\n failureEvent: {\n ...mapped(\"proofFailures\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n seriesKey: \"seriesKey\",\n commandId: \"commandId\",\n occurredAt: \"occurredAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({\n ...row,\n occurredAt: instant(row.occurredAtMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n }),\n },\n command: {\n ...mapped(\"proofCommands\"),\n moduleId: \"moduleId\",\n commandId: \"commandId\",\n kind: \"kind\",\n decision: \"decision\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({ ...row, retentionUntil: instant(row.retentionUntilMillis) }),\n },\n });\n\n const passwords = (): AnyPasswordPersistenceMapping<SqlFragment> => {\n const verifier = (replacement: PasswordReplacement) => ({\n verifier: Redacted.value(replacement.verifier),\n normalization: replacement.normalization,\n });\n\n return {\n subjectId,\n subject,\n authorityCredential: authorityCredential(),\n constraints: requiredPasswordConstraints,\n isCommandConflict: () => false,\n isRateScopeConflict: () => false,\n scopeKeys: ({ moduleId, action, identifier, subjectId }) => ({\n action: tuple([moduleId, action]),\n identifier: tuple([identifier.namespace, identifier.value]),\n ...(subjectId === undefined ? {} : { subject: subjectId }),\n }),\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateAttemptIdSync: () => PasswordAttemptId.make(randomId()),\n allocateCredentialIdSync: randomId,\n allocateRevisionSync: () => SecurityRevision.make(randomId()),\n commandRetentionMillis: 86_400_000,\n sessionInvalidation: \"same-authority-immediate\",\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true,\n encodeInitialInsert: (identifier, subjectId, revision) => ({\n ...identifier,\n subjectId,\n revision,\n verifiedAt: null,\n active: true,\n }),\n },\n credential: {\n ...mapped(\"passwords\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n verifier: \"verifier\",\n normalization: \"normalization\",\n encodeInsert: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n encodeVerifier: (hash, verifierVersion) => ({\n verifier: Redacted.value(hash),\n verifierVersion,\n }),\n encodeReplacement: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n const id = yield* s.toSubject(row[s.id]);\n const hash = yield* decode(EncodedPasswordHash, credential.verifier);\n\n return yield* decode(PasswordCredentialSnapshot, {\n moduleId,\n revision: {\n subjectId: id,\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n verifierVersion: credential.verifierVersion,\n verifier: hash,\n normalization: credential.normalization,\n identifier: yield* decode(LoginIdentifier, {\n namespace: identifier.namespace,\n value: identifier.value,\n }),\n identifierBindingRevision: identifier.revision,\n ...(identifier.verifiedAt === null\n ? {}\n : { identifierVerifiedAtMillis: yield* readInstant(identifier.verifiedAt) }),\n });\n }),\n },\n attempt: {\n ...mapped(\"passwordAttempts\"),\n moduleId: \"moduleId\",\n action: \"action\",\n attemptId: \"attemptId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n securityRevision: \"securityRevision\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n identifierBindingRevision: \"identifierBindingRevision\",\n admittedAt: \"admittedAt\",\n deadline: \"deadline\",\n retentionUntil: \"retentionUntil\",\n state: \"state\",\n encodeInsert: (row, { nativeSubjectId, state }) => ({\n moduleId: row.moduleId,\n action: row.action,\n attemptId: row.attemptId,\n identifierNamespace: row.identifier.namespace,\n identifierValue: row.identifier.value,\n subjectId: nativeSubjectId ?? null,\n credentialId: row.credentialId ?? null,\n securityRevision: row.securityRevision ?? null,\n credentialRevision: row.credentialRevision ?? null,\n verifierVersion: row.verifierVersion ?? null,\n identifierBindingRevision: row.identifierBindingRevision ?? null,\n admittedAt: instant(row.admittedAtMillis),\n deadline: instant(row.deadlineMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n state,\n }),\n },\n rateScope: {\n ...mapped(\"passwordScopes\"),\n moduleId: \"moduleId\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n encodeInsert: (row) => ({ ...row }),\n },\n charge: {\n ...mapped(\"passwordCharges\"),\n moduleId: \"moduleId\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n attemptId: \"attemptId\",\n occurredAt: \"occurredAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({\n ...row,\n occurredAt: instant(row.occurredAtMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n }),\n },\n command: {\n ...mapped(\"passwordCommands\"),\n moduleId: \"moduleId\",\n commandId: \"commandId\",\n action: \"action\",\n bindingDigest: \"bindingDigest\",\n decision: \"decision\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({ ...row, retentionUntil: instant(row.retentionUntilMillis) }),\n },\n };\n };\n\n const emails = (): AnyEmailAddressMapping<SqlFragment> => ({\n subjectId,\n subject,\n constraints: requiredEmailAddressConstraints,\n addressCardinality: \"single\",\n changeDisposition: \"retire-source\",\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialIdSync: randomId,\n allocateRevisionSync: () => SecurityRevision.make(randomId()),\n commandRetentionMillis: 86_400_000,\n sessionInvalidation: \"same-authority-immediate\",\n isCommandConflict: () => false,\n isIdentifierConflict: () => false,\n isCredentialConflict: () => false,\n authorityCredential: {\n ...authorityCredential(),\n encodeActivation: (revision) => ({ revision, active: true }),\n encodeRetirement: (revision) => ({ revision, active: false }),\n },\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true && row.verifiedAt !== null,\n isMutableTarget: (row) => row.active === true && row.verifiedAt === null,\n encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({\n ...identifier,\n subjectId,\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeVerification: ({ verifiedAtMillis, bindingRevision }) => ({\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeRetirement: ({ bindingRevision }) => ({ revision: bindingRevision, active: false }),\n },\n credential: {\n table: table(\"emailCredentials\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n credentialRevision: \"credentialRevision\",\n status: \"active\",\n isActiveStatus: (value) => value === true,\n encodeVerifiedInsert: ({ identifier, ...row }) => ({\n ...row,\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n active: true,\n }),\n encodeActivation: ({ identifier, credentialRevision }) => ({\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n credentialRevision,\n active: true,\n }),\n encodeRetirement: ({ credentialRevision }) => ({ credentialRevision, active: false }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n return yield* decode(EmailCredentialSnapshot, {\n moduleId,\n identifier: { namespace: identifier.namespace, value: identifier.value },\n identifierRevision: identifier.revision,\n verifiedAtMillis: yield* readInstant(identifier.verifiedAt),\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n revision: {\n subjectId: yield* s.toSubject(row[s.id]),\n securityRevision: row[s.securityRevision],\n credentials: [\n { credentialId: credential.credentialId, revision: credential.credentialRevision },\n ],\n },\n });\n }),\n },\n command: {\n table: table(\"emailCommands\"),\n moduleId: \"moduleId\",\n commandId: \"commandId\",\n action: \"action\",\n bindingDigest: \"bindingDigest\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: ({ retentionUntilMillis, ...row }) => ({\n ...row,\n retentionUntil: instant(retentionUntilMillis),\n }),\n },\n });\n\n const sessions = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n ): StatefulSessionMapping<\n C[\"Type\"],\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n string\n > => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n ...SessionMetadata.fields,\n claims: Schema.Unknown,\n digest: TokenDigest,\n version: SecurityRevision,\n provenance: SessionAuthenticationProvenance,\n credentialVersion: SessionCredentialVersion,\n }),\n );\n\n const encodeRow = (value: StatefulSessionRecord<C[\"Type\"]>) => ({\n digest: value.digest,\n version: value.version,\n securityRevision: value.securityRevision,\n issuedAt: instant(DateTime.toEpochMillis(value.issuedAt)),\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n absoluteExpiresAt: instant(DateTime.toEpochMillis(value.absoluteExpiresAt)),\n record: Schema.encodeSync(record)({\n ...value,\n claims: Schema.encodeSync(claims)(value.claims),\n }),\n });\n\n return {\n ...authority(),\n isConstraintConflict: () => false,\n constraints: { sessionDigest: \"unique(session.digest)\", flowId: \"unique(flow.flowId)\" },\n sessionId: {\n toNative: (id) => Effect.succeed(id),\n toSession: (id) => decode(SessionId, id),\n equals: Object.is,\n },\n flow: {\n table: table(\"sessionFlows\"),\n flowId: \"flowId\",\n subjectId: \"subjectId\",\n state: \"state\",\n pendingDigest: \"pendingDigest\",\n dedupUntil: \"dedupUntil\",\n pendingStateValue: \"pending\",\n establishedStateValue: \"established\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n decodeInstant: (value) => readInstant(value).pipe(Effect.map(DateTime.makeUnsafe)),\n encodePendingInsert: (row) => ({\n flowId: row.evidence.flowId,\n subjectId: row.subjectId,\n state: \"pending\",\n pendingDigest: row.pendingDigest,\n dedupUntil: instant(DateTime.toEpochMillis(row.dedupUntil)),\n }),\n encodeEstablishedInsert: (row) => ({\n flowId: row.evidence.flowId,\n subjectId: row.subjectId,\n state: \"established\",\n pendingDigest: null,\n dedupUntil: instant(DateTime.toEpochMillis(row.dedupUntil)),\n }),\n },\n session: {\n table: table(\"sessions\"),\n sessionId: \"sessionId\",\n subjectId: \"subjectId\",\n digest: \"digest\",\n version: \"version\",\n securityRevision: \"securityRevision\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n absoluteExpiresAt: \"absoluteExpiresAt\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateIdSync: randomId,\n allocateVersionSync: () => SecurityRevision.make(randomId()),\n encodeInsert: (value, ids) => ({ ...encodeRow(value), ...ids }),\n encodeRotation: encodeRow,\n decode: (row) =>\n Effect.gen(function* () {\n const saved = yield* decode(record, row.record);\n const decodedClaims = yield* decode(claims, saved.claims);\n\n return { ...saved, claims: decodedClaims };\n }),\n },\n };\n };\n\n return { table, authority, proofs, passwords, emails, sessions, string };\n};\n"],"mappings":";;;;;;;;;;;;;;AA0CA,MAAM,WAAW,UAAmB,wBAAwB,KAAK;CAAE,WAAW;CAAU;AAAM,CAAC;AAE/F,MAAM,UAAoD,QAAW,UACnE,OAAO,oBAAoB,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,SAAS,OAAO,CAAC;AAEzE,MAAM,UAAU,UAAmB,OAAO,kBAAkB,OAAO,MAAM,CAAC,CAAC,KAAK;AAChF,MAAM,QAAQ,OAAO,WAAW,OAAO,eAAe,OAAO,MAAM,OAAO,MAAM,CAAC,CAAC;AAClF,MAAM,cAAc,OAAO,eAAe,YAAY;AACtD,MAAM,cAAc,OAAO,eAAe,mBAAmB;AAE7D,MAAMA,gBAAc,OAAO,OAAO;CAChC,UAAU,OAAO;CACjB,SAAS;CACT,SAAS;CACT,WAAW;CACX,aAAa;CACb,YAAY;CACZ,SAAS;CACT,UAAU,OAAO,OAAO;EAAE,OAAO,OAAO;EAAQ,QAAQ;CAAY,CAAC;CACrE,gBAAgB,OAAO;CACvB,iBAAiB,OAAO;CACxB,SAAS;AACX,CAAC;AAED,MAAM,oBAAoB,OAAO,OAAO;CACtC,UAAU,OAAO;CACjB,SAAS;CACT,gBAAgB;CAChB,QAAQ;CACR,SAAS;CACT,WAAW,OAAO;CAClB,SAAS;CACT,iBAAiB,OAAO;CACxB,SAAS;AACX,CAAC;;;;;;AAOD,MAAa,gBAAgB,UAAwB;CACnD,MAAM,SAAS,SAAsB;EACnC,MAAM,QAAQ,MAAM,OAAO;EAE3B,IAAI,UAAU,KAAA,GAAW,MAAM,QAAQ,WAAW,KAAK,SAAS;EAEhE,OAAO;CACT;CAEA,MAAM,UAAU,UAAuB;EACrC,OAAO,MAAM,IAAI;EACjB,GAAG,OAAO,YAAY,OAAO,KAAK,cAAc,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;CACzF;CAEA,MAAM,EAAE,UAAU,GAAG,eAAe,SAAS,eAAe,gBAAgB;CAC5E,MAAM,YAAY;EAAE,UAAU,EAAE;EAAU,WAAW,EAAE;EAAW,QAAQ,OAAO;CAAG;CAEpF,MAAM,UAAU;EACd,OAAO,EAAE;EACT,IAAI,EAAE;EACN,QAAQ,EAAE;EACV,kBAAkB,EAAE;EACpB,iBAAiB,UAAmB,OAAO,GAAG,OAAO,EAAE,WAAW;EAClE,qBAAqB,EAAE;EACvB,mBAAmB,EAAE;EACrB,yBAAyB,EAAE;EAC3B,gCAAgC,iBAAiB,KAAK,SAAS,CAAC;CAClE;CAEA,MAAM,6BAA6B;EACjC,GAAG,OAAO,aAAa;EACvB,WAAW;EACX,cAAc;EACd,UAAU;EACV,QAAQ;EACR,iBAAiB,UAAmB,UAAU;EAC9C,eAAe,SAAiB;GAAE,GAAG;GAAK,QAAQ;EAAK;EACvD,iBAAiB,cAAgC,EAAE,SAAS;CAC9D;CAEA,MAAM,mBAAmB;EAAE;EAAW;EAAS,YAAY,oBAAoB;CAAE;CAEjF,MAAM,gBAAyD;EAC7D,aAAa;EACb,eAAe;EACf,eAAe;EACf,2BAA2B,aAAa,KAAK,SAAS,CAAC;EACvD,yBAAyB;EACzB,wBAAwB;EACxB,yBAAyB;EACzB,YAAY,EAAE,eAAe;GAC3B,QAAQ,MAAM;IACZ,QAAQ,WAAW;IACnB,QAAQ,WAAW;IACnB,QAAQ,SAAS,eAAe,cAAc,QAAQ,SAAS;GACjE,CAAC;GACD,YAAY,MAAM,CAAC,QAAQ,WAAW,WAAW,QAAQ,WAAW,KAAK,CAAC;GAC1E,SAAS,QAAQ,SAAS,eAAe,cAAc,QAAQ,SAAS;EAC1E;EACA,WAAW;GACT,GAAG,UAAU;GACb,YAAY;IACV,OAAO,MAAM,aAAa;IAC1B,WAAW;IACX,OAAO;IACP,YAAY,SAAS,SACnB,QAAQ,QAAQ,SAAS,eACrB,KAAK,WAAW,IAChB,QAAQ,QAAQ,SAAS,qBACvB,KAAK,WAAW,KACf,KAAK,WAAW,KACf,KAAK,OACF,QACC,OAAO,GAAG,IAAI,WAAW,QAAQ,eAAe,KAChD,IAAI,WAAW,QACf,IAAI,eAAe,IACvB,IACF,KAAK,WAAW,KAChB,KAAK,OACF,QAAQ,OAAO,GAAG,IAAI,WAAW,QAAQ,eAAe,KAAK,IAAI,WAAW,IAC/E;GACV;EACF;EACA,SAAS;GACP,GAAG,OAAO,eAAe;GACzB,UAAU;GACV,WAAW;GACX,aAAa;GACb,SAAS;GACT,SAAS;GACT,OAAO;GACP,WAAW;GACX,gBAAgB;GAChB,eAAe,EAAE,QAAQ,4BAA4B;IACnD,UAAU,OAAO;IACjB,WAAW,OAAO;IAClB,aAAa,OAAO;IACpB,SAAS,OAAO;IAChB,SAAS,OAAO;IAChB,OAAO,OAAO,SAAS;IACvB,WAAW,QAAQ,OAAO,cAAc;IACxC,gBAAgB,QAAQ,oBAAoB;IAC5C,SAAS,OAAO,WAAW,WAAW,CAAC,CAAC;KACtC,WAAW,OAAO;KAClB,WAAW;MACT,SAAS,OAAO;MAChB,SAAS,OAAO;MAChB,OAAO,OAAO,SAAS;KACzB;IACF,CAAC;GACH;GACA,gBAAgB,QAAQ,OAAO,aAAa,IAAI,OAAO;EACzD;EACA,QAAQ;GACN,GAAG,OAAO,aAAa;GACvB,UAAU;GACV,SAAS;GACT,UAAU;GACV,eAAe;GACf,aAAa;GACb,SAAS;GACT,eAAe,SAAS;IAAE,GAAG;IAAK,eAAe;IAAM,aAAa;GAAK;EAC3E;EACA,YAAY;GACV,GAAG,OAAO,kBAAkB;GAC5B,UAAU;GACV,SAAS;GACT,SAAS;GACT,WAAW;GACX,WAAW;GACX,YAAY;GACZ,SAAS;GACT,eAAe;GACf,gBAAgB;GAChB,UAAU;GACV,WAAW;GACX,SAAS;GACT,OAAO;GACP,WAAW;GACX,eAAe;GACf,cAAc;GACd,eAAe;GACf,SAAS;GACT,qBAAqB;GACrB,gBAAgB;GAChB,eAAe,EACb,QACA,WACA,sBACA,OACA,eACA,cACK;IACL,UAAU,OAAO;IACjB,SAAS,OAAO;IAChB,SAAS,OAAO;IAChB,WAAW,OAAO;IAClB;IACA,YAAY,OAAO;IACnB,SAAS,OAAO,WAAW,WAAW,CAAC,CAAC,OAAO,OAAO;IACtD,eAAe,OAAO,SAAS;IAC/B,gBAAgB,OAAO,SAAS;IAChC,UAAU,QAAQ,OAAO,cAAc;IACvC,WAAW,QAAQ,OAAO,eAAe;IACzC,SAAS,OAAO;IAChB;IACA,WAAW;IACX;IACA,cAAc;IACd,eAAe;IACf,SAAS;IACT,qBAAqB,OAAO;IAC5B,gBAAgB,QAAQ,oBAAoB;IAC5C,aAAa,OAAO;GACtB;GACA,gBAAgB,QAAQ,OAAO,aAAa,IAAI,OAAO;GACvD,eAAe,QACb,OAAO,IAAI,aAAa;IACtB,OAAO,OAAO,OAAOA,eAAa;KAChC,UAAU,IAAI;KACd,SAAS,IAAI;KACb,SAAS,IAAI;KACb,WAAW,IAAI;KACf,aAAa,IAAI;KACjB,YAAY,IAAI;KAChB,SAAS,OAAO,OAAO,aAAa,IAAI,OAAO;KAC/C,UAAU;MAAE,OAAO,IAAI;MAAe,QAAQ,IAAI;KAAe;KACjE,gBAAgB,OAAO,YAAY,IAAI,QAAQ;KAC/C,iBAAiB,OAAO,YAAY,IAAI,SAAS;KACjD,SAAS,IAAI;IACf,CAAC;GACH,CAAC;EACL;EACA,cAAc;GACZ,GAAG,OAAO,oBAAoB;GAC9B,UAAU;GACV,SAAS;GACT,gBAAgB;GAChB,QAAQ;GACR,SAAS;GACT,WAAW;GACX,SAAS;GACT,WAAW;GACX,UAAU;GACV,SAAS;GACT,gBAAgB;GAChB,eAAe,YAAY;IACzB,GAAG;IACH,SAAS,OAAO,WAAW,WAAW,CAAC,CAAC,OAAO,OAAO;IACtD,WAAW,QAAQ,OAAO,eAAe;IACzC,UAAU;IACV,gBAAgB,QAAQ,OAAO,oBAAoB;GACrD;GACA,SAAS,QACP,OAAO,IAAI,aAAa;IACtB,OAAO,OAAO,OAAO,mBAAmB;KACtC,GAAG;KACH,SAAS,OAAO,OAAO,aAAa,IAAI,OAAO;KAC/C,iBAAiB,OAAO,YAAY,IAAI,SAAS;IACnD,CAAC;GACH,CAAC;EACL;EACA,WAAW;GACT,GAAG,OAAO,aAAa;GACvB,UAAU;GACV,SAAS;GACT,QAAQ;GACR,WAAW;GACX,UAAU;GACV,eAAe,SAAS,EAAE,GAAG,IAAI;EACnC;EACA,YAAY;GACV,GAAG,OAAO,YAAY;GACtB,UAAU;GACV,SAAS;GACT,QAAQ;GACR,WAAW;GACX,UAAU;GACV,WAAW;GACX,YAAY;GACZ,gBAAgB;GAChB,eAAe,SAAS;IACtB,GAAG;IACH,YAAY,QAAQ,IAAI,gBAAgB;IACxC,gBAAgB,QAAQ,IAAI,oBAAoB;GAClD;EACF;EACA,cAAc;GACZ,GAAG,OAAO,eAAe;GACzB,UAAU;GACV,SAAS;GACT,WAAW;GACX,WAAW;GACX,YAAY;GACZ,gBAAgB;GAChB,eAAe,SAAS;IACtB,GAAG;IACH,YAAY,QAAQ,IAAI,gBAAgB;IACxC,gBAAgB,QAAQ,IAAI,oBAAoB;GAClD;EACF;EACA,SAAS;GACP,GAAG,OAAO,eAAe;GACzB,UAAU;GACV,WAAW;GACX,MAAM;GACN,UAAU;GACV,gBAAgB;GAChB,eAAe,SAAS;IAAE,GAAG;IAAK,gBAAgB,QAAQ,IAAI,oBAAoB;GAAE;EACtF;CACF;CAEA,MAAM,kBAA8D;EAClE,MAAM,YAAY,iBAAsC;GACtD,UAAU,SAAS,MAAM,YAAY,QAAQ;GAC7C,eAAe,YAAY;EAC7B;EAEA,OAAO;GACL;GACA;GACA,qBAAqB,oBAAoB;GACzC,aAAa;GACb,yBAAyB;GACzB,2BAA2B;GAC3B,YAAY,EAAE,UAAU,QAAQ,YAAY,iBAAiB;IAC3D,QAAQ,MAAM,CAAC,UAAU,MAAM,CAAC;IAChC,YAAY,MAAM,CAAC,WAAW,WAAW,WAAW,KAAK,CAAC;IAC1D,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,UAAU;GAC1D;GACA,eAAe;GACf,eAAe;GACf,6BAA6B,kBAAkB,KAAK,SAAS,CAAC;GAC9D,0BAA0B;GAC1B,4BAA4B,iBAAiB,KAAK,SAAS,CAAC;GAC5D,wBAAwB;GACxB,qBAAqB;GACrB,YAAY;IACV,OAAO,MAAM,aAAa;IAC1B,WAAW;IACX,OAAO;IACP,WAAW;IACX,YAAY;IACZ,iBAAiB;IACjB,YAAY,QAAQ,IAAI,WAAW;IACnC,sBAAsB,YAAY,WAAW,cAAc;KACzD,GAAG;KACH;KACA;KACA,YAAY;KACZ,QAAQ;IACV;GACF;GACA,YAAY;IACV,GAAG,OAAO,WAAW;IACrB,UAAU;IACV,WAAW;IACX,cAAc;IACd,oBAAoB;IACpB,iBAAiB;IACjB,UAAU;IACV,eAAe;IACf,eAAe,EAAE,aAAa,GAAG,WAAW;KAAE,GAAG;KAAK,GAAG,SAAS,WAAW;IAAE;IAC/E,iBAAiB,MAAM,qBAAqB;KAC1C,UAAU,SAAS,MAAM,IAAI;KAC7B;IACF;IACA,oBAAoB,EAAE,aAAa,GAAG,WAAW;KAAE,GAAG;KAAK,GAAG,SAAS,WAAW;IAAE;IACpF,SAAS,EAAE,UAAU,SAAS,KAAK,YAAY,iBAC7C,OAAO,IAAI,aAAa;KACtB,MAAM,KAAK,OAAO,EAAE,UAAU,IAAI,EAAE,GAAG;KACvC,MAAM,OAAO,OAAO,OAAO,qBAAqB,WAAW,QAAQ;KAEnE,OAAO,OAAO,OAAO,4BAA4B;MAC/C;MACA,UAAU;OACR,WAAW;OACX,kBAAkB,IAAI,EAAE;OACxB,aAAa,CACX;QACE,cAAc,WAAW;QACzB,UAAU,WAAW;OACvB,CACF;MACF;MACA,cAAc,WAAW;MACzB,oBAAoB,WAAW;MAC/B,iBAAiB,WAAW;MAC5B,UAAU;MACV,eAAe,WAAW;MAC1B,YAAY,OAAO,OAAO,iBAAiB;OACzC,WAAW,WAAW;OACtB,OAAO,WAAW;MACpB,CAAC;MACD,2BAA2B,WAAW;MACtC,GAAI,WAAW,eAAe,OAC1B,CAAC,IACD,EAAE,4BAA4B,OAAO,YAAY,WAAW,UAAU,EAAE;KAC9E,CAAC;IACH,CAAC;GACL;GACA,SAAS;IACP,GAAG,OAAO,kBAAkB;IAC5B,UAAU;IACV,QAAQ;IACR,WAAW;IACX,qBAAqB;IACrB,iBAAiB;IACjB,WAAW;IACX,cAAc;IACd,kBAAkB;IAClB,oBAAoB;IACpB,iBAAiB;IACjB,2BAA2B;IAC3B,YAAY;IACZ,UAAU;IACV,gBAAgB;IAChB,OAAO;IACP,eAAe,KAAK,EAAE,iBAAiB,aAAa;KAClD,UAAU,IAAI;KACd,QAAQ,IAAI;KACZ,WAAW,IAAI;KACf,qBAAqB,IAAI,WAAW;KACpC,iBAAiB,IAAI,WAAW;KAChC,WAAW,mBAAmB;KAC9B,cAAc,IAAI,gBAAgB;KAClC,kBAAkB,IAAI,oBAAoB;KAC1C,oBAAoB,IAAI,sBAAsB;KAC9C,iBAAiB,IAAI,mBAAmB;KACxC,2BAA2B,IAAI,6BAA6B;KAC5D,YAAY,QAAQ,IAAI,gBAAgB;KACxC,UAAU,QAAQ,IAAI,cAAc;KACpC,gBAAgB,QAAQ,IAAI,oBAAoB;KAChD;IACF;GACF;GACA,WAAW;IACT,GAAG,OAAO,gBAAgB;IAC1B,UAAU;IACV,QAAQ;IACR,WAAW;IACX,UAAU;IACV,eAAe,SAAS,EAAE,GAAG,IAAI;GACnC;GACA,QAAQ;IACN,GAAG,OAAO,iBAAiB;IAC3B,UAAU;IACV,QAAQ;IACR,WAAW;IACX,UAAU;IACV,WAAW;IACX,YAAY;IACZ,gBAAgB;IAChB,eAAe,SAAS;KACtB,GAAG;KACH,YAAY,QAAQ,IAAI,gBAAgB;KACxC,gBAAgB,QAAQ,IAAI,oBAAoB;IAClD;GACF;GACA,SAAS;IACP,GAAG,OAAO,kBAAkB;IAC5B,UAAU;IACV,WAAW;IACX,QAAQ;IACR,eAAe;IACf,UAAU;IACV,gBAAgB;IAChB,eAAe,SAAS;KAAE,GAAG;KAAK,gBAAgB,QAAQ,IAAI,oBAAoB;IAAE;GACtF;EACF;CACF;CAEA,MAAM,gBAAqD;EACzD;EACA;EACA,aAAa;EACb,oBAAoB;EACpB,mBAAmB;EACnB,eAAe;EACf,eAAe;EACf,0BAA0B;EAC1B,4BAA4B,iBAAiB,KAAK,SAAS,CAAC;EAC5D,wBAAwB;EACxB,qBAAqB;EACrB,yBAAyB;EACzB,4BAA4B;EAC5B,4BAA4B;EAC5B,qBAAqB;GACnB,GAAG,oBAAoB;GACvB,mBAAmB,cAAc;IAAE;IAAU,QAAQ;GAAK;GAC1D,mBAAmB,cAAc;IAAE;IAAU,QAAQ;GAAM;EAC7D;EACA,YAAY;GACV,OAAO,MAAM,aAAa;GAC1B,WAAW;GACX,OAAO;GACP,WAAW;GACX,YAAY;GACZ,iBAAiB;GACjB,YAAY,QAAQ,IAAI,WAAW,QAAQ,IAAI,eAAe;GAC9D,kBAAkB,QAAQ,IAAI,WAAW,QAAQ,IAAI,eAAe;GACpE,uBAAuB,EAAE,YAAY,WAAW,kBAAkB,uBAAuB;IACvF,GAAG;IACH;IACA,YAAY,QAAQ,gBAAgB;IACpC,UAAU;IACV,QAAQ;GACV;GACA,qBAAqB,EAAE,kBAAkB,uBAAuB;IAC9D,YAAY,QAAQ,gBAAgB;IACpC,UAAU;IACV,QAAQ;GACV;GACA,mBAAmB,EAAE,uBAAuB;IAAE,UAAU;IAAiB,QAAQ;GAAM;EACzF;EACA,YAAY;GACV,OAAO,MAAM,kBAAkB;GAC/B,UAAU;GACV,WAAW;GACX,cAAc;GACd,qBAAqB;GACrB,iBAAiB;GACjB,oBAAoB;GACpB,QAAQ;GACR,iBAAiB,UAAU,UAAU;GACrC,uBAAuB,EAAE,YAAY,GAAG,WAAW;IACjD,GAAG;IACH,qBAAqB,WAAW;IAChC,iBAAiB,WAAW;IAC5B,QAAQ;GACV;GACA,mBAAmB,EAAE,YAAY,0BAA0B;IACzD,qBAAqB,WAAW;IAChC,iBAAiB,WAAW;IAC5B;IACA,QAAQ;GACV;GACA,mBAAmB,EAAE,0BAA0B;IAAE;IAAoB,QAAQ;GAAM;GACnF,SAAS,EAAE,UAAU,SAAS,KAAK,YAAY,iBAC7C,OAAO,IAAI,aAAa;IACtB,OAAO,OAAO,OAAO,yBAAyB;KAC5C;KACA,YAAY;MAAE,WAAW,WAAW;MAAW,OAAO,WAAW;KAAM;KACvE,oBAAoB,WAAW;KAC/B,kBAAkB,OAAO,YAAY,WAAW,UAAU;KAC1D,cAAc,WAAW;KACzB,oBAAoB,WAAW;KAC/B,UAAU;MACR,WAAW,OAAO,EAAE,UAAU,IAAI,EAAE,GAAG;MACvC,kBAAkB,IAAI,EAAE;MACxB,aAAa,CACX;OAAE,cAAc,WAAW;OAAc,UAAU,WAAW;MAAmB,CACnF;KACF;IACF,CAAC;GACH,CAAC;EACL;EACA,SAAS;GACP,OAAO,MAAM,eAAe;GAC5B,UAAU;GACV,WAAW;GACX,QAAQ;GACR,eAAe;GACf,gBAAgB;GAChB,eAAe,EAAE,sBAAsB,GAAG,WAAW;IACnD,GAAG;IACH,gBAAgB,QAAQ,oBAAoB;GAC9C;EACF;CACF;CAEA,MAAM,YACJ,WAUG;EACH,MAAM,SAAS,OAAO,eACpB,OAAO,OAAO;GACZ,GAAG,gBAAgB;GACnB,QAAQ,OAAO;GACf,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,mBAAmB;EACrB,CAAC,CACH;EAEA,MAAM,aAAa,WAA6C;GAC9D,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,kBAAkB,MAAM;GACxB,UAAU,QAAQ,SAAS,cAAc,MAAM,QAAQ,CAAC;GACxD,WAAW,QAAQ,SAAS,cAAc,MAAM,SAAS,CAAC;GAC1D,mBAAmB,QAAQ,SAAS,cAAc,MAAM,iBAAiB,CAAC;GAC1E,QAAQ,OAAO,WAAW,MAAM,CAAC,CAAC;IAChC,GAAG;IACH,QAAQ,OAAO,WAAW,MAAM,CAAC,CAAC,MAAM,MAAM;GAChD,CAAC;EACH;EAEA,OAAO;GACL,GAAG,UAAU;GACb,4BAA4B;GAC5B,aAAa;IAAE,eAAe;IAA0B,QAAQ;GAAsB;GACtF,WAAW;IACT,WAAW,OAAO,OAAO,QAAQ,EAAE;IACnC,YAAY,OAAO,OAAO,WAAW,EAAE;IACvC,QAAQ,OAAO;GACjB;GACA,MAAM;IACJ,OAAO,MAAM,cAAc;IAC3B,QAAQ;IACR,WAAW;IACX,OAAO;IACP,eAAe;IACf,YAAY;IACZ,mBAAmB;IACnB,uBAAuB;IACvB,gBAAgB,SAAS,QAAQ,SAAS,cAAc,IAAI,CAAC;IAC7D,gBAAgB,UAAU,YAAY,KAAK,CAAC,CAAC,KAAK,OAAO,IAAI,SAAS,UAAU,CAAC;IACjF,sBAAsB,SAAS;KAC7B,QAAQ,IAAI,SAAS;KACrB,WAAW,IAAI;KACf,OAAO;KACP,eAAe,IAAI;KACnB,YAAY,QAAQ,SAAS,cAAc,IAAI,UAAU,CAAC;IAC5D;IACA,0BAA0B,SAAS;KACjC,QAAQ,IAAI,SAAS;KACrB,WAAW,IAAI;KACf,OAAO;KACP,eAAe;KACf,YAAY,QAAQ,SAAS,cAAc,IAAI,UAAU,CAAC;IAC5D;GACF;GACA,SAAS;IACP,OAAO,MAAM,UAAU;IACvB,WAAW;IACX,WAAW;IACX,QAAQ;IACR,SAAS;IACT,kBAAkB;IAClB,UAAU;IACV,WAAW;IACX,mBAAmB;IACnB,gBAAgB,SAAS,QAAQ,SAAS,cAAc,IAAI,CAAC;IAC7D,gBAAgB;IAChB,2BAA2B,iBAAiB,KAAK,SAAS,CAAC;IAC3D,eAAe,OAAO,SAAS;KAAE,GAAG,UAAU,KAAK;KAAG,GAAG;IAAI;IAC7D,gBAAgB;IAChB,SAAS,QACP,OAAO,IAAI,aAAa;KACtB,MAAM,QAAQ,OAAO,OAAO,QAAQ,IAAI,MAAM;KAC9C,MAAM,gBAAgB,OAAO,OAAO,QAAQ,MAAM,MAAM;KAExD,OAAO;MAAE,GAAG;MAAO,QAAQ;KAAc;IAC3C,CAAC;GACL;EACF;CACF;CAEA,OAAO;EAAE;EAAO;EAAW;EAAQ;EAAW;EAAQ;EAAU;CAAO;AACzE"}
1
+ {"version":3,"file":"storage-mapping.mjs","names":["ProofRecord"],"sources":["../../src/internal/storage-mapping.ts"],"sourcesContent":["import { EmailCredentialSnapshot } from \"@yielded/auth/Email\";\nimport { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n PasswordAttemptId,\n PasswordCredentialSnapshot,\n EncodedPasswordHash,\n type PasswordReplacement,\n} from \"@yielded/auth/Password\";\nimport {\n ProofBinding,\n ProofContinuationId,\n ProofDeliveryId,\n ProofId,\n ProofPurpose,\n ProofRequestId,\n ProofRequestReceipt,\n ProofVersion,\n} from \"@yielded/auth/Proofs\";\nimport { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SecurityRevision,\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { Crypto, DateTime, Effect, Redacted, Schema } from \"effect\";\n\nimport type { MappingInput } from \"./configuration\";\nimport { randomId } from \"./crypto\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { requiredEmailAddressConstraints, type AnyEmailAddressMapping } from \"./models/email-model\";\nimport {\n requiredPasswordConstraints,\n type AnyPasswordPersistenceMapping,\n} from \"./models/password-model\";\nimport { requiredProofConstraints, type AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport type { StatefulSessionMapping } from \"./models/session-model\";\nimport type { TableModel, SqlFragment } from \"./query-operations\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\n\nconst failure = (cause: unknown) => PersistenceMappingError.make({ operation: \"decode\", cause });\n\nconst decode = <S extends Schema.Codec<unknown, unknown>>(schema: S, value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(failure));\n\nconst string = (value: unknown) => Schema.decodeUnknownSync(Schema.String)(value);\nconst tuple = Schema.encodeSync(Schema.fromJsonString(Schema.Array(Schema.String)));\nconst bindingJson = Schema.fromJsonString(ProofBinding);\nconst receiptJson = Schema.fromJsonString(ProofRequestReceipt);\n\nconst ProofRecord = Schema.Struct({\n moduleId: Schema.String,\n purpose: ProofPurpose,\n proofId: ProofId,\n requestId: ProofRequestId,\n fingerprint: TokenDigest,\n deliveryId: ProofDeliveryId,\n binding: ProofBinding,\n verifier: Schema.Struct({ keyId: Schema.String, digest: TokenDigest }),\n issuedAtMillis: Schema.Int,\n expiresAtMillis: Schema.Int,\n version: ProofVersion,\n});\n\nconst ProofContinuation = Schema.Struct({\n moduleId: Schema.String,\n purpose: ProofPurpose,\n continuationId: ProofContinuationId,\n digest: TokenDigest,\n proofId: ProofId,\n seriesKey: Schema.String,\n binding: ProofBinding,\n expiresAtMillis: Schema.Int,\n version: ProofVersion,\n});\n\n/** Derive shared row mappings from a managed or custom storage layout.\n * Acquire Effect Crypto once; the returned allocators retain that implementation.\n * Each factory requires its role tables to exist. Adapter authors supply typed\n * table handles and dialect-specific clocks/commit predicates, and may refine\n * authority policy for explicit services. This does not alter composed defaults.\n */\nexport const makeMappings = Effect.fnUntraced(function* (input: MappingInput) {\n const crypto = yield* Crypto.Crypto;\n\n const allocate = randomId.pipe(\n Effect.provideService(Crypto.Crypto, crypto),\n Effect.mapError(failure),\n );\n\n const table = (role: StorageRole) => {\n const found = input.tables[role];\n\n if (found === undefined) throw failure(`Missing ${role} mapping`);\n\n return found;\n };\n\n const mapped = (role: StorageRole) => ({\n table: table(role),\n ...Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])),\n });\n\n const { subjects: s, encodeInstant: instant, decodeInstant: readInstant } = input;\n const subjectId = { toNative: s.toNative, toSubject: s.toSubject, equals: Object.is };\n\n const subject = {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n isActiveStatus: (value: unknown) => Object.is(value, s.activeValue),\n d1ActiveStatusValue: s.activeValue,\n decodeRequirement: s.requirements,\n decodeActionRequirement: s.actionRequirements,\n nextSecurityRevision: () => allocate.pipe(Effect.map(SecurityRevision.make)),\n };\n\n const authorityCredential = () => ({\n ...mapped(\"credentials\"),\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n revision: \"revision\",\n status: \"active\",\n isActiveStatus: (value: unknown) => value === true,\n encodeInsert: (row: object) => ({ ...row, active: true }),\n encodeRevision: (revision: SecurityRevision) => ({ revision }),\n });\n\n const authority = () => ({ subjectId, subject, credential: authorityCredential() });\n\n const proofs = (): AnyProofPersistenceMapping<SqlFragment> => ({\n constraints: requiredProofConstraints,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateVersion: allocate.pipe(Effect.map(ProofVersion.make)),\n isRequestConflict: () => false,\n isSeriesConflict: () => false,\n isCommandConflict: () => false,\n scopeKeys: ({ binding }) => ({\n series: tuple([\n binding.identifier.namespace,\n binding.identifier.value,\n binding._tag === \"Identifier\" ? \"anonymous\" : binding.revision.subjectId,\n ]),\n identifier: tuple([binding.identifier.namespace, binding.identifier.value]),\n subject: binding._tag === \"Identifier\" ? \"anonymous\" : binding.revision.subjectId,\n }),\n authority: {\n ...authority(),\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n isCurrent: (request, rows) =>\n request.binding._tag === \"Identifier\"\n ? rows.length === 0\n : request.binding._tag === \"IdentifierChange\"\n ? rows.length === 0 ||\n (rows.length === 1 &&\n rows.every(\n (row) =>\n Object.is(row.subjectId, request.nativeSubjectId) &&\n row.active === true &&\n row.verifiedAt === null,\n ))\n : rows.length === 1 &&\n rows.every(\n (row) => Object.is(row.subjectId, request.nativeSubjectId) && row.active === true,\n ),\n },\n },\n request: {\n ...mapped(\"proofRequests\"),\n moduleId: \"moduleId\",\n requestId: \"requestId\",\n fingerprint: \"fingerprint\",\n proofId: \"proofId\",\n purpose: \"purpose\",\n keyId: \"keyId\",\n createdAt: \"createdAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: ({ record, retentionUntilMillis }) => ({\n moduleId: record.moduleId,\n requestId: record.requestId,\n fingerprint: record.fingerprint,\n proofId: record.proofId,\n purpose: record.purpose,\n keyId: record.verifier.keyId,\n createdAt: instant(record.issuedAtMillis),\n retentionUntil: instant(retentionUntilMillis),\n receipt: Schema.encodeSync(receiptJson)({\n requestId: record.requestId,\n reference: {\n proofId: record.proofId,\n purpose: record.purpose,\n keyId: record.verifier.keyId,\n },\n }),\n }),\n decodeReceipt: (row) => decode(receiptJson, row.receipt),\n },\n series: {\n ...mapped(\"proofSeries\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n scopeKey: \"scopeKey\",\n activeProofId: \"activeProofId\",\n lastIssueAt: \"lastIssueAt\",\n version: \"version\",\n encodeInsert: (row) => ({ ...row, activeProofId: null, lastIssueAt: null }),\n },\n generation: {\n ...mapped(\"proofGenerations\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n proofId: \"proofId\",\n requestId: \"requestId\",\n seriesKey: \"seriesKey\",\n deliveryId: \"deliveryId\",\n binding: \"binding\",\n verifierKeyId: \"verifierKeyId\",\n verifierDigest: \"verifierDigest\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n version: \"version\",\n state: \"state\",\n sendCount: \"sendCount\",\n deliveryState: \"deliveryState\",\n claimVersion: \"claimVersion\",\n claimDeadline: \"claimDeadline\",\n retryAt: \"retryAt\",\n deliveryRetryMillis: \"deliveryRetryMillis\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: ({\n record,\n seriesKey,\n retentionUntilMillis,\n state,\n deliveryState,\n policy,\n }) => ({\n moduleId: record.moduleId,\n purpose: record.purpose,\n proofId: record.proofId,\n requestId: record.requestId,\n seriesKey,\n deliveryId: record.deliveryId,\n binding: Schema.encodeSync(bindingJson)(record.binding),\n verifierKeyId: record.verifier.keyId,\n verifierDigest: record.verifier.digest,\n issuedAt: instant(record.issuedAtMillis),\n expiresAt: instant(record.expiresAtMillis),\n version: record.version,\n state,\n sendCount: 0,\n deliveryState,\n claimVersion: null,\n claimDeadline: null,\n retryAt: null,\n deliveryRetryMillis: policy.deliveryRetryMillis,\n retentionUntil: instant(retentionUntilMillis),\n fingerprint: record.fingerprint,\n }),\n decodeBinding: (row) => decode(bindingJson, row.binding),\n decodeRecord: (row) =>\n Effect.gen(function* () {\n return yield* decode(ProofRecord, {\n moduleId: row.moduleId,\n purpose: row.purpose,\n proofId: row.proofId,\n requestId: row.requestId,\n fingerprint: row.fingerprint,\n deliveryId: row.deliveryId,\n binding: yield* decode(bindingJson, row.binding),\n verifier: { keyId: row.verifierKeyId, digest: row.verifierDigest },\n issuedAtMillis: yield* readInstant(row.issuedAt),\n expiresAtMillis: yield* readInstant(row.expiresAt),\n version: row.version,\n });\n }),\n },\n continuation: {\n ...mapped(\"proofContinuations\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n continuationId: \"continuationId\",\n digest: \"digest\",\n proofId: \"proofId\",\n seriesKey: \"seriesKey\",\n binding: \"binding\",\n expiresAt: \"expiresAt\",\n consumed: \"consumed\",\n version: \"version\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (record) => ({\n ...record,\n binding: Schema.encodeSync(bindingJson)(record.binding),\n expiresAt: instant(record.expiresAtMillis),\n consumed: false,\n retentionUntil: instant(record.retentionUntilMillis),\n }),\n decode: (row) =>\n Effect.gen(function* () {\n return yield* decode(ProofContinuation, {\n ...row,\n binding: yield* decode(bindingJson, row.binding),\n expiresAtMillis: yield* readInstant(row.expiresAt),\n });\n }),\n },\n rateScope: {\n ...mapped(\"proofScopes\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n encodeInsert: (row) => ({ ...row }),\n },\n abuseEvent: {\n ...mapped(\"proofAbuse\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n commandId: \"commandId\",\n occurredAt: \"occurredAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({\n ...row,\n occurredAt: instant(row.occurredAtMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n }),\n },\n failureEvent: {\n ...mapped(\"proofFailures\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n seriesKey: \"seriesKey\",\n commandId: \"commandId\",\n occurredAt: \"occurredAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({\n ...row,\n occurredAt: instant(row.occurredAtMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n }),\n },\n command: {\n ...mapped(\"proofCommands\"),\n moduleId: \"moduleId\",\n commandId: \"commandId\",\n kind: \"kind\",\n decision: \"decision\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({ ...row, retentionUntil: instant(row.retentionUntilMillis) }),\n },\n });\n\n const passwords = (): AnyPasswordPersistenceMapping<SqlFragment> => {\n const verifier = (replacement: PasswordReplacement) => ({\n verifier: Redacted.value(replacement.verifier),\n normalization: replacement.normalization,\n });\n\n return {\n subjectId,\n subject,\n authorityCredential: authorityCredential(),\n constraints: requiredPasswordConstraints,\n isCommandConflict: () => false,\n isRateScopeConflict: () => false,\n scopeKeys: ({ moduleId, action, identifier, subjectId }) => ({\n action: tuple([moduleId, action]),\n identifier: tuple([identifier.namespace, identifier.value]),\n ...(subjectId === undefined ? {} : { subject: subjectId }),\n }),\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateAttemptId: allocate.pipe(Effect.map(PasswordAttemptId.make)),\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n commandRetentionMillis: 86_400_000,\n sessionInvalidation: \"same-authority-immediate\",\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true,\n encodeInitialInsert: (identifier, subjectId, revision) => ({\n ...identifier,\n subjectId,\n revision,\n verifiedAt: null,\n active: true,\n }),\n },\n credential: {\n ...mapped(\"passwords\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n verifier: \"verifier\",\n normalization: \"normalization\",\n encodeInsert: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n encodeVerifier: (hash, verifierVersion) => ({\n verifier: Redacted.value(hash),\n verifierVersion,\n }),\n encodeReplacement: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n const id = yield* s.toSubject(row[s.id]);\n const hash = yield* decode(EncodedPasswordHash, credential.verifier);\n\n return yield* decode(PasswordCredentialSnapshot, {\n moduleId,\n revision: {\n subjectId: id,\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n verifierVersion: credential.verifierVersion,\n verifier: hash,\n normalization: credential.normalization,\n identifier: yield* decode(LoginIdentifier, {\n namespace: identifier.namespace,\n value: identifier.value,\n }),\n identifierBindingRevision: identifier.revision,\n ...(identifier.verifiedAt === null\n ? {}\n : { identifierVerifiedAtMillis: yield* readInstant(identifier.verifiedAt) }),\n });\n }),\n },\n attempt: {\n ...mapped(\"passwordAttempts\"),\n moduleId: \"moduleId\",\n action: \"action\",\n attemptId: \"attemptId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n securityRevision: \"securityRevision\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n identifierBindingRevision: \"identifierBindingRevision\",\n admittedAt: \"admittedAt\",\n deadline: \"deadline\",\n retentionUntil: \"retentionUntil\",\n state: \"state\",\n encodeInsert: (row, { nativeSubjectId, state }) => ({\n moduleId: row.moduleId,\n action: row.action,\n attemptId: row.attemptId,\n identifierNamespace: row.identifier.namespace,\n identifierValue: row.identifier.value,\n subjectId: nativeSubjectId ?? null,\n credentialId: row.credentialId ?? null,\n securityRevision: row.securityRevision ?? null,\n credentialRevision: row.credentialRevision ?? null,\n verifierVersion: row.verifierVersion ?? null,\n identifierBindingRevision: row.identifierBindingRevision ?? null,\n admittedAt: instant(row.admittedAtMillis),\n deadline: instant(row.deadlineMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n state,\n }),\n },\n rateScope: {\n ...mapped(\"passwordScopes\"),\n moduleId: \"moduleId\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n encodeInsert: (row) => ({ ...row }),\n },\n charge: {\n ...mapped(\"passwordCharges\"),\n moduleId: \"moduleId\",\n action: \"action\",\n scopeKind: \"scopeKind\",\n scopeKey: \"scopeKey\",\n attemptId: \"attemptId\",\n occurredAt: \"occurredAt\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({\n ...row,\n occurredAt: instant(row.occurredAtMillis),\n retentionUntil: instant(row.retentionUntilMillis),\n }),\n },\n command: {\n ...mapped(\"passwordCommands\"),\n moduleId: \"moduleId\",\n commandId: \"commandId\",\n action: \"action\",\n bindingDigest: \"bindingDigest\",\n decision: \"decision\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: (row) => ({ ...row, retentionUntil: instant(row.retentionUntilMillis) }),\n },\n };\n };\n\n const emails = (): AnyEmailAddressMapping<SqlFragment> => ({\n subjectId,\n subject,\n constraints: requiredEmailAddressConstraints,\n addressCardinality: \"single\",\n changeDisposition: \"retire-source\",\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n commandRetentionMillis: 86_400_000,\n sessionInvalidation: \"same-authority-immediate\",\n isCommandConflict: () => false,\n isIdentifierConflict: () => false,\n isCredentialConflict: () => false,\n authorityCredential: {\n ...authorityCredential(),\n encodeActivation: (revision) => ({ revision, active: true }),\n encodeRetirement: (revision) => ({ revision, active: false }),\n },\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true && row.verifiedAt !== null,\n isMutableTarget: (row) => row.active === true && row.verifiedAt === null,\n encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({\n ...identifier,\n subjectId,\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeVerification: ({ verifiedAtMillis, bindingRevision }) => ({\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeRetirement: ({ bindingRevision }) => ({ revision: bindingRevision, active: false }),\n },\n credential: {\n table: table(\"emailCredentials\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n credentialRevision: \"credentialRevision\",\n status: \"active\",\n isActiveStatus: (value) => value === true,\n encodeVerifiedInsert: ({ identifier, ...row }) => ({\n ...row,\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n active: true,\n }),\n encodeActivation: ({ identifier, credentialRevision }) => ({\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n credentialRevision,\n active: true,\n }),\n encodeRetirement: ({ credentialRevision }) => ({ credentialRevision, active: false }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n return yield* decode(EmailCredentialSnapshot, {\n moduleId,\n identifier: { namespace: identifier.namespace, value: identifier.value },\n identifierRevision: identifier.revision,\n verifiedAtMillis: yield* readInstant(identifier.verifiedAt),\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n revision: {\n subjectId: yield* s.toSubject(row[s.id]),\n securityRevision: row[s.securityRevision],\n credentials: [\n { credentialId: credential.credentialId, revision: credential.credentialRevision },\n ],\n },\n });\n }),\n },\n command: {\n table: table(\"emailCommands\"),\n moduleId: \"moduleId\",\n commandId: \"commandId\",\n action: \"action\",\n bindingDigest: \"bindingDigest\",\n retentionUntil: \"retentionUntil\",\n encodeInsert: ({ retentionUntilMillis, ...row }) => ({\n ...row,\n retentionUntil: instant(retentionUntilMillis),\n }),\n },\n });\n\n const sessions = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n ): StatefulSessionMapping<\n C[\"Type\"],\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n string\n > => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n ...SessionMetadata.fields,\n claims: Schema.Unknown,\n digest: TokenDigest,\n version: SecurityRevision,\n provenance: SessionAuthenticationProvenance,\n credentialVersion: SessionCredentialVersion,\n }),\n );\n\n const encodeRow = (value: StatefulSessionRecord<C[\"Type\"]>) => ({\n digest: value.digest,\n version: value.version,\n securityRevision: value.securityRevision,\n issuedAt: instant(DateTime.toEpochMillis(value.issuedAt)),\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n absoluteExpiresAt: instant(DateTime.toEpochMillis(value.absoluteExpiresAt)),\n record: Schema.encodeSync(record)({\n ...value,\n claims: Schema.encodeSync(claims)(value.claims),\n }),\n });\n\n return {\n ...authority(),\n isConstraintConflict: () => false,\n constraints: { sessionDigest: \"unique(session.digest)\", flowId: \"unique(flow.flowId)\" },\n sessionId: {\n toNative: (id) => Effect.succeed(id),\n toSession: (id) => decode(SessionId, id),\n equals: Object.is,\n },\n flow: {\n table: table(\"sessionFlows\"),\n flowId: \"flowId\",\n subjectId: \"subjectId\",\n state: \"state\",\n pendingDigest: \"pendingDigest\",\n dedupUntil: \"dedupUntil\",\n pendingStateValue: \"pending\",\n establishedStateValue: \"established\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n decodeInstant: (value) => readInstant(value).pipe(Effect.map(DateTime.makeUnsafe)),\n encodePendingInsert: (row) => ({\n flowId: row.evidence.flowId,\n subjectId: row.subjectId,\n state: \"pending\",\n pendingDigest: row.pendingDigest,\n dedupUntil: instant(DateTime.toEpochMillis(row.dedupUntil)),\n }),\n encodeEstablishedInsert: (row) => ({\n flowId: row.evidence.flowId,\n subjectId: row.subjectId,\n state: \"established\",\n pendingDigest: null,\n dedupUntil: instant(DateTime.toEpochMillis(row.dedupUntil)),\n }),\n },\n session: {\n table: table(\"sessions\"),\n sessionId: \"sessionId\",\n subjectId: \"subjectId\",\n digest: \"digest\",\n version: \"version\",\n securityRevision: \"securityRevision\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n absoluteExpiresAt: \"absoluteExpiresAt\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateId: allocate,\n allocateVersion: allocate.pipe(Effect.map(SecurityRevision.make)),\n encodeInsert: (value, ids) => ({ ...encodeRow(value), ...ids }),\n encodeRotation: encodeRow,\n decode: (row) =>\n Effect.gen(function* () {\n const saved = yield* decode(record, row.record);\n const decodedClaims = yield* decode(claims, saved.claims);\n\n return { ...saved, claims: decodedClaims };\n }),\n },\n };\n };\n\n return { table, authority, proofs, passwords, emails, sessions, string };\n});\n"],"mappings":"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"}
@@ -11,8 +11,18 @@ export interface PhysicalStorageTable {
11
11
  readonly name: string;
12
12
  }>>;
13
13
  }
14
- /** Validate physical uniqueness before the first authentication operation. A
15
- * partial or expression index cannot establish these unconditional guarantees. */
16
- export declare const validateStorage: (dialect: "mysql" | "pg" | "sqlite", table: PhysicalStorageTable, required: readonly (readonly string[])[]) => Effect.Effect<undefined, PersistenceConfigurationError, SqlClient>;
14
+ /** Share metadata only while constructing one service graph. Captured contexts
15
+ * cannot reuse it after acquisition succeeds, fails, or is interrupted. */
16
+ export declare const withStorageValidation: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
17
+ /** Requirements checked against one physical catalog snapshot. */
18
+ export interface StorageValidation {
19
+ readonly table: PhysicalStorageTable;
20
+ readonly required: ReadonlyArray<ReadonlyArray<string>>;
21
+ }
22
+ /** Batch physical metadata reads, reusing only the current acquisition snapshot.
23
+ * Every requirement is still checked; later acquisitions read fresh metadata. */
24
+ export declare const validateStorageBatch: (dialect: "mysql" | "pg" | "sqlite", requirements: readonly StorageValidation[]) => Effect.Effect<undefined, PersistenceConfigurationError, SqlClient>;
25
+ /** Validate physical columns and unconditional uniqueness before operations run. */
26
+ export declare const validateStorage: (dialect: "pg" | "mysql" | "sqlite", table: PhysicalStorageTable, required: ReadonlyArray<ReadonlyArray<string>>) => Effect.Effect<undefined, PersistenceConfigurationError, SqlClient>;
17
27
  //#endregion
18
28
  //# sourceMappingURL=storage-validation.d.mts.map
@@ -1,85 +1,145 @@
1
1
  import { PersistenceConfigurationError } from "./configuration.mjs";
2
- import { Effect, Schema } from "effect";
2
+ import { Context, Effect, Schema } from "effect";
3
3
  import { SqlClient } from "effect/sql/SqlClient";
4
4
  //#region src/internal/storage-validation.ts
5
- const Column = Schema.Struct({
6
- name: Schema.String,
7
- pk: Schema.Int
5
+ const Columns = Schema.Array(Schema.String);
6
+ const Keys = Schema.Array(Columns);
7
+ const Metadata = Schema.Struct({
8
+ relation: Schema.String,
9
+ columns: Columns,
10
+ keys: Keys
8
11
  });
9
- const Index = Schema.Struct({
12
+ const PgMetadata = Schema.Array(Metadata);
13
+ const SqliteMetadata = Schema.Array(Schema.Struct({
14
+ relation: Schema.String,
15
+ columns: Schema.fromJsonString(Columns),
16
+ keys: Schema.fromJsonString(Keys)
17
+ }));
18
+ const MysqlColumns = Schema.Array(Schema.Struct({
19
+ relation: Schema.String,
20
+ name: Schema.String
21
+ }));
22
+ const MysqlIndexes = Schema.Array(Schema.Struct({
23
+ relation: Schema.String,
10
24
  name: Schema.String,
11
- unique: Schema.Int,
12
- partial: Schema.Int
13
- });
14
- const IndexColumn = Schema.Struct({ name: Schema.NullOr(Schema.String) });
15
- const PgColumns = Schema.Array(Schema.Struct({ name: Schema.String }));
16
- const PgKeys = Schema.Array(Schema.Struct({ columns: Schema.Array(Schema.String) }));
25
+ columnName: Schema.NullOr(Schema.String),
26
+ prefix: Schema.NullOr(Schema.Number)
27
+ }));
17
28
  const quote = (value) => "\"" + value.replaceAll("\"", "\"\"") + "\"";
18
29
  const qualifiedTableName = (table) => (table.schema === void 0 ? "" : quote(table.schema) + ".") + quote(table.name);
19
- /** Validate physical uniqueness before the first authentication operation. A
20
- * partial or expression index cannot establish these unconditional guarantees. */
21
- const validateStorage = Effect.fnUntraced(function* (dialect, table, required) {
30
+ const validateKeys = Effect.fnUntraced(function* (table, required, physicalKeys) {
31
+ for (const keys of required) {
32
+ const columns = keys.map((key) => table.columns[key]?.name);
33
+ if (columns.some((column) => column === void 0) || !physicalKeys.some((actual) => actual.length > 0 && actual.length <= columns.length && actual.every((column) => columns.includes(column)))) return yield* PersistenceConfigurationError.make({ reason: `Missing unique key on ${table.name} (${keys.join(", ")})` });
34
+ }
35
+ });
36
+ const CurrentMetadata = Context.Reference("@yielded/auth-persistence/StorageMetadata", { defaultValue: () => void 0 });
37
+ /** Share metadata only while constructing one service graph. Captured contexts
38
+ * cannot reuse it after acquisition succeeds, fails, or is interrupted. */
39
+ const withStorageValidation = Effect.fnUntraced(function* (effect) {
40
+ if ((yield* CurrentMetadata)?.active) return yield* effect;
41
+ const cache = {
42
+ active: true,
43
+ clients: /* @__PURE__ */ new Map()
44
+ };
45
+ return yield* effect.pipe(Effect.provideService(CurrentMetadata, cache), Effect.ensuring(Effect.sync(() => {
46
+ cache.active = false;
47
+ cache.clients.clear();
48
+ })));
49
+ });
50
+ /** Batch physical metadata reads, reusing only the current acquisition snapshot.
51
+ * Every requirement is still checked; later acquisitions read fresh metadata. */
52
+ const validateStorageBatch = Effect.fnUntraced(function* (dialect, requirements) {
53
+ if (requirements.length === 0) return;
54
+ if (dialect === "sqlite" && requirements.some(({ table }) => table.schema !== void 0 && table.schema !== "main")) return yield* PersistenceConfigurationError.make({ reason: "SQLite storage must belong to the main database" });
22
55
  const client = yield* SqlClient;
23
- const physicalKeys = yield* dialect === "sqlite" ? Effect.gen(function* () {
24
- if (table.schema !== void 0 && table.schema !== "main") return yield* PersistenceConfigurationError.make({ reason: "SQLite storage must belong to the main database" });
25
- const columns = yield* client`select name, pk from pragma_table_info(${table.name})`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Column))));
26
- for (const column of Object.values(table.columns)) if (!columns.some((actual) => actual.name === column.name)) return yield* PersistenceConfigurationError.make({ reason: `Missing SQL column ${table.name}.${column.name}` });
27
- const keys = [];
28
- const primary = columns.filter((column) => column.pk > 0).sort((a, b) => a.pk - b.pk);
29
- if (primary.length > 0) keys.push(primary.map((column) => column.name));
30
- const indexes = yield* client`select name, "unique", partial from pragma_index_list(${table.name})`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Index))));
31
- for (const index of indexes) {
32
- if (index.unique !== 1 || index.partial !== 0) continue;
33
- const columns = yield* client`select name from pragma_index_info(${index.name})`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(IndexColumn))));
34
- if (columns.every((column) => column.name !== null)) keys.push(columns.flatMap((column) => column.name === null ? [] : [column.name]));
35
- }
36
- return keys;
37
- }) : dialect === "mysql" ? Effect.gen(function* () {
38
- const columns = yield* client`
39
- select column_name as name from information_schema.columns
40
- where table_schema = coalesce(${table.schema ?? null}, database())
41
- and table_name = ${table.name}
42
- `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PgColumns)));
43
- for (const column of Object.values(table.columns)) if (!columns.some((actual) => actual.name === column.name)) return yield* PersistenceConfigurationError.make({ reason: `Missing SQL column ${table.name}.${column.name}` });
44
- const indexes = yield* client`
45
- select index_name as name, column_name as columnName, sub_part as prefix
46
- from information_schema.statistics
47
- where table_schema = coalesce(${table.schema ?? null}, database())
48
- and table_name = ${table.name} and non_unique = 0
49
- order by index_name, seq_in_index
50
- `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({
51
- name: Schema.String,
52
- columnName: Schema.NullOr(Schema.String),
53
- prefix: Schema.NullOr(Schema.Number)
54
- })))));
55
- const keys = /* @__PURE__ */ new Map();
56
- for (const index of indexes) {
57
- const columns = keys.get(index.name) ?? [];
58
- columns.push(index);
59
- keys.set(index.name, columns);
60
- }
61
- return [...keys.values()].filter((columns) => columns.every((column) => column.columnName !== null && column.prefix === null)).map((columns) => columns.flatMap((column) => column.columnName === null ? [] : [column.columnName]));
62
- }) : Effect.gen(function* () {
63
- const relation = qualifiedTableName(table);
64
- const columns = yield* client`select attname as name from pg_attribute where attrelid = to_regclass(${relation}) and attnum > 0 and not attisdropped`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PgColumns)));
65
- for (const column of Object.values(table.columns)) if (!columns.some((actual) => actual.name === column.name)) return yield* PersistenceConfigurationError.make({ reason: `Missing SQL column ${relation}.${column.name}` });
66
- return (yield* client`
67
- select array_agg(a.attname order by k.ordinality) as columns
56
+ const cache = yield* CurrentMetadata;
57
+ const byRelation = cache?.active ? cache.clients.get(client) ?? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Map();
58
+ if (cache?.active) cache.clients.set(client, byRelation);
59
+ const missing = [...new Map(requirements.map(({ table }) => [qualifiedTableName(table), table])).entries()].filter(([relation]) => !byRelation.has(`${dialect}:${relation}`));
60
+ const relations = missing.map(([relation]) => relation);
61
+ if (relations.length > 0) {
62
+ const metadata = yield* dialect === "sqlite" ? Effect.gen(function* () {
63
+ const requested = client.join(", ", false)(missing.map(([relation, table]) => client`(${relation}, ${table.name})`));
64
+ return yield* client`
65
+ with requested(relation, name) as (values ${requested})
66
+ select requested.relation,
67
+ (select json_group_array(name) from pragma_table_info(requested.name)) as columns,
68
+ (select json_group_array(json(storage_key.columns)) from (
69
+ select json_group_array(name) as columns from (
70
+ select name from pragma_table_info(requested.name) where pk > 0 order by pk
71
+ )
72
+ union all
73
+ select (select json_group_array(name) from pragma_index_info(i.name)) as columns
74
+ from pragma_index_list(requested.name) i
75
+ where i."unique" = 1 and i.partial = 0
76
+ and not exists(select 1 from pragma_index_info(i.name) where name is null)
77
+ ) storage_key where json_array_length(storage_key.columns) > 0) as keys
78
+ from requested
79
+ `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(SqliteMetadata)));
80
+ }) : dialect === "mysql" ? Effect.gen(function* () {
81
+ const requested = client.join(" union all ", false)(missing.map(([relation, table]) => client`select ${relation} as relation, ${table.schema ?? null} as schemaName, ${table.name} as tableName`));
82
+ const columns = yield* client`
83
+ select requested.relation, c.column_name as name
84
+ from (${requested}) requested join information_schema.columns c
85
+ on c.table_schema = coalesce(requested.schemaName, database()) and c.table_name = requested.tableName
86
+ `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(MysqlColumns)));
87
+ const indexes = yield* client`
88
+ select requested.relation, i.index_name as name, i.column_name as columnName, i.sub_part as prefix
89
+ from (${requested}) requested join information_schema.statistics i
90
+ on i.table_schema = coalesce(requested.schemaName, database()) and i.table_name = requested.tableName
91
+ where i.non_unique = 0 order by requested.relation, i.index_name, i.seq_in_index
92
+ `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(MysqlIndexes)));
93
+ return relations.map((relation) => {
94
+ const grouped = /* @__PURE__ */ new Map();
95
+ for (const index of indexes) {
96
+ if (index.relation !== relation) continue;
97
+ const entries = grouped.get(index.name) ?? [];
98
+ entries.push(index);
99
+ grouped.set(index.name, entries);
100
+ }
101
+ return {
102
+ relation,
103
+ columns: columns.filter((column) => column.relation === relation).map((column) => column.name),
104
+ keys: [...grouped.values()].filter((entries) => entries.every((entry) => entry.columnName !== null && entry.prefix === null)).map((entries) => entries.flatMap((entry) => entry.columnName === null ? [] : [entry.columnName]))
105
+ };
106
+ });
107
+ }) : Effect.gen(function* () {
108
+ const requested = client.join(", ", false)(relations.map((relation) => client`(${relation}::text)`));
109
+ return yield* client`
110
+ select requested.relation,
111
+ array(select a.attname::text from pg_attribute a
112
+ where a.attrelid = to_regclass(requested.relation)
113
+ and a.attnum > 0 and not a.attisdropped) as columns,
114
+ coalesce((select jsonb_agg(storage_key.columns) from (
115
+ select array_agg(a.attname::text order by k.ordinality) as columns
68
116
  from pg_index i
69
117
  cross join lateral unnest(i.indkey) with ordinality as k(attnum, ordinality)
70
118
  join pg_attribute a on a.attrelid = i.indrelid and a.attnum = k.attnum
71
- where i.indrelid = to_regclass(${relation}) and i.indisunique and i.indisvalid
72
- and i.indimmediate and i.indpred is null and i.indexprs is null
119
+ where i.indrelid = to_regclass(requested.relation)
120
+ and i.indisunique and i.indisvalid and i.indimmediate
121
+ and i.indpred is null and i.indexprs is null
73
122
  and k.ordinality <= i.indnkeyatts
74
123
  group by i.indexrelid
75
- `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PgKeys)))).map((index) => index.columns);
76
- });
77
- for (const keys of required) {
78
- const columns = keys.map((key) => table.columns[key]?.name);
79
- if (columns.some((column) => column === void 0) || !physicalKeys.some((actual) => actual.length > 0 && actual.length <= columns.length && actual.every((column) => columns.includes(column)))) return yield* PersistenceConfigurationError.make({ reason: `Missing unique key on ${table.name} (${keys.join(", ")})` });
124
+ ) storage_key), '[]'::jsonb) as keys
125
+ from (values ${requested}) requested(relation)
126
+ `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PgMetadata)));
127
+ });
128
+ for (const row of metadata) byRelation.set(`${dialect}:${row.relation}`, row);
129
+ }
130
+ for (const { table, required } of requirements) {
131
+ const relation = qualifiedTableName(table);
132
+ const actual = byRelation.get(`${dialect}:${relation}`);
133
+ for (const column of Object.values(table.columns)) if (actual === void 0 || !actual.columns.includes(column.name)) return yield* PersistenceConfigurationError.make({ reason: `Missing SQL column ${relation}.${column.name}` });
134
+ yield* validateKeys(table, required, actual?.keys ?? []);
80
135
  }
81
- }, (effect, _dialect, table) => effect.pipe(Effect.mapError((error) => Schema.is(PersistenceConfigurationError)(error) ? error : PersistenceConfigurationError.make({ reason: `Cannot validate SQL storage for ${table.name}` }))));
136
+ }, Effect.mapError((error) => Schema.is(PersistenceConfigurationError)(error) ? error : PersistenceConfigurationError.make({ reason: "Cannot validate SQL storage metadata" })));
137
+ /** Validate physical columns and unconditional uniqueness before operations run. */
138
+ const validateStorage = (dialect, table, required) => validateStorageBatch(dialect, [{
139
+ table,
140
+ required
141
+ }]);
82
142
  //#endregion
83
- export { qualifiedTableName, validateStorage };
143
+ export { qualifiedTableName, validateStorage, validateStorageBatch, withStorageValidation };
84
144
 
85
145
  //# sourceMappingURL=storage-validation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-validation.mjs","names":[],"sources":["../../src/internal/storage-validation.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\n\nimport { PersistenceConfigurationError } from \"./configuration\";\nimport type { StorageTable } from \"./storage-tables\";\n\nconst Column = Schema.Struct({ name: Schema.String, pk: Schema.Int });\nconst Index = Schema.Struct({ name: Schema.String, unique: Schema.Int, partial: Schema.Int });\nconst IndexColumn = Schema.Struct({ name: Schema.NullOr(Schema.String) });\nconst PgColumns = Schema.Array(Schema.Struct({ name: Schema.String }));\nconst PgKeys = Schema.Array(Schema.Struct({ columns: Schema.Array(Schema.String) }));\nconst quote = (value: string) => '\"' + value.replaceAll('\"', '\"\"') + '\"';\n\n/** Physical metadata needed for catalog validation; value codecs remain adapter-owned. */\nexport interface PhysicalStorageTable {\n readonly name: string;\n readonly schema?: string;\n readonly columns: Readonly<Record<string, { readonly name: string }>>;\n}\n\nexport const qualifiedTableName = (table: Pick<StorageTable, \"name\" | \"schema\">) =>\n (table.schema === undefined ? \"\" : quote(table.schema) + \".\") + quote(table.name);\n\n/** Validate physical uniqueness before the first authentication operation. A\n * partial or expression index cannot establish these unconditional guarantees. */\nexport const validateStorage = Effect.fnUntraced(\n function* (\n dialect: \"pg\" | \"mysql\" | \"sqlite\",\n table: PhysicalStorageTable,\n required: ReadonlyArray<ReadonlyArray<string>>,\n ) {\n const client = yield* SqlClient;\n\n const physicalKeys: ReadonlyArray<ReadonlyArray<string>> = yield* dialect === \"sqlite\"\n ? Effect.gen(function* () {\n if (table.schema !== undefined && table.schema !== \"main\")\n return yield* PersistenceConfigurationError.make({\n reason: \"SQLite storage must belong to the main database\",\n });\n\n const columns = yield* client`select name, pk from pragma_table_info(${table.name})`.pipe(\n Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Column))),\n );\n\n for (const column of Object.values(table.columns)) {\n if (!columns.some((actual) => actual.name === column.name))\n return yield* PersistenceConfigurationError.make({\n reason: `Missing SQL column ${table.name}.${column.name}`,\n });\n }\n const keys: string[][] = [];\n const primary = columns.filter((column) => column.pk > 0).sort((a, b) => a.pk - b.pk);\n\n if (primary.length > 0) keys.push(primary.map((column) => column.name));\n\n const indexes =\n yield* client`select name, \"unique\", partial from pragma_index_list(${table.name})`.pipe(\n Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Index))),\n );\n\n for (const index of indexes) {\n if (index.unique !== 1 || index.partial !== 0) continue;\n\n const columns = yield* client`select name from pragma_index_info(${index.name})`.pipe(\n Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(IndexColumn))),\n );\n\n if (columns.every((column) => column.name !== null))\n keys.push(columns.flatMap((column) => (column.name === null ? [] : [column.name])));\n }\n\n return keys;\n })\n : dialect === \"mysql\"\n ? Effect.gen(function* () {\n const columns = yield* client`\n select column_name as name from information_schema.columns\n where table_schema = coalesce(${table.schema ?? null}, database())\n and table_name = ${table.name}\n `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PgColumns)));\n\n for (const column of Object.values(table.columns)) {\n if (!columns.some((actual) => actual.name === column.name))\n return yield* PersistenceConfigurationError.make({\n reason: `Missing SQL column ${table.name}.${column.name}`,\n });\n }\n\n const indexes = yield* client`\n select index_name as name, column_name as columnName, sub_part as prefix\n from information_schema.statistics\n where table_schema = coalesce(${table.schema ?? null}, database())\n and table_name = ${table.name} and non_unique = 0\n order by index_name, seq_in_index\n `.pipe(\n Effect.flatMap(\n Schema.decodeUnknownEffect(\n Schema.Array(\n Schema.Struct({\n name: Schema.String,\n columnName: Schema.NullOr(Schema.String),\n prefix: Schema.NullOr(Schema.Number),\n }),\n ),\n ),\n ),\n );\n\n const keys = new Map<string, Array<(typeof indexes)[number]>>();\n\n for (const index of indexes) {\n const columns = keys.get(index.name) ?? [];\n\n columns.push(index);\n keys.set(index.name, columns);\n }\n\n return [...keys.values()]\n .filter((columns) =>\n columns.every((column) => column.columnName !== null && column.prefix === null),\n )\n .map((columns) =>\n columns.flatMap((column) =>\n column.columnName === null ? [] : [column.columnName],\n ),\n );\n })\n : Effect.gen(function* () {\n const relation = qualifiedTableName(table);\n\n const columns =\n yield* client`select attname as name from pg_attribute where attrelid = to_regclass(${relation}) and attnum > 0 and not attisdropped`.pipe(\n Effect.flatMap(Schema.decodeUnknownEffect(PgColumns)),\n );\n\n for (const column of Object.values(table.columns)) {\n if (!columns.some((actual) => actual.name === column.name))\n return yield* PersistenceConfigurationError.make({\n reason: `Missing SQL column ${relation}.${column.name}`,\n });\n }\n\n const indexes = yield* client`\n select array_agg(a.attname order by k.ordinality) as columns\n from pg_index i\n cross join lateral unnest(i.indkey) with ordinality as k(attnum, ordinality)\n join pg_attribute a on a.attrelid = i.indrelid and a.attnum = k.attnum\n where i.indrelid = to_regclass(${relation}) and i.indisunique and i.indisvalid\n and i.indimmediate and i.indpred is null and i.indexprs is null\n and k.ordinality <= i.indnkeyatts\n group by i.indexrelid\n `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PgKeys)));\n\n return indexes.map((index) => index.columns);\n });\n\n for (const keys of required) {\n const columns = keys.map((key) => table.columns[key]?.name);\n\n if (\n columns.some((column) => column === undefined) ||\n !physicalKeys.some(\n // A stronger key also guarantees the required tuple's uniqueness.\n (actual) =>\n actual.length > 0 &&\n actual.length <= columns.length &&\n actual.every((column) => columns.includes(column)),\n )\n )\n return yield* PersistenceConfigurationError.make({\n reason: `Missing unique key on ${table.name} (${keys.join(\", \")})`,\n });\n }\n },\n (effect, _dialect, table) =>\n effect.pipe(\n Effect.mapError((error) =>\n Schema.is(PersistenceConfigurationError)(error)\n ? error\n : PersistenceConfigurationError.make({\n reason: `Cannot validate SQL storage for ${table.name}`,\n }),\n ),\n ),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage-validation.mjs","names":[],"sources":["../../src/internal/storage-validation.ts"],"sourcesContent":["import { Context, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\n\nimport { PersistenceConfigurationError } from \"./configuration\";\nimport type { StorageTable } from \"./storage-tables\";\n\nconst Columns = Schema.Array(Schema.String);\nconst Keys = Schema.Array(Columns);\nconst Metadata = Schema.Struct({ relation: Schema.String, columns: Columns, keys: Keys });\nconst PgMetadata = Schema.Array(Metadata);\n\nconst SqliteMetadata = Schema.Array(\n Schema.Struct({\n relation: Schema.String,\n columns: Schema.fromJsonString(Columns),\n keys: Schema.fromJsonString(Keys),\n }),\n);\n\nconst MysqlColumns = Schema.Array(Schema.Struct({ relation: Schema.String, name: Schema.String }));\n\nconst MysqlIndexes = Schema.Array(\n Schema.Struct({\n relation: Schema.String,\n name: Schema.String,\n columnName: Schema.NullOr(Schema.String),\n prefix: Schema.NullOr(Schema.Number),\n }),\n);\n\nconst quote = (value: string) => '\"' + value.replaceAll('\"', '\"\"') + '\"';\n\n/** Physical metadata needed for catalog validation; value codecs remain adapter-owned. */\nexport interface PhysicalStorageTable {\n readonly name: string;\n readonly schema?: string;\n readonly columns: Readonly<Record<string, { readonly name: string }>>;\n}\n\nexport const qualifiedTableName = (table: Pick<StorageTable, \"name\" | \"schema\">) =>\n (table.schema === undefined ? \"\" : quote(table.schema) + \".\") + quote(table.name);\n\nconst validateKeys = Effect.fnUntraced(function* (\n table: PhysicalStorageTable,\n required: ReadonlyArray<ReadonlyArray<string>>,\n physicalKeys: ReadonlyArray<ReadonlyArray<string>>,\n) {\n for (const keys of required) {\n const columns = keys.map((key) => table.columns[key]?.name);\n\n if (\n columns.some((column) => column === undefined) ||\n !physicalKeys.some(\n // A stronger key also guarantees the required tuple's uniqueness.\n (actual) =>\n actual.length > 0 &&\n actual.length <= columns.length &&\n actual.every((column) => columns.includes(column)),\n )\n )\n return yield* PersistenceConfigurationError.make({\n reason: `Missing unique key on ${table.name} (${keys.join(\", \")})`,\n });\n }\n});\n\ninterface MetadataCache {\n active: boolean;\n readonly clients: Map<SqlClient, Map<string, typeof Metadata.Type>>;\n}\n\nconst CurrentMetadata = Context.Reference<MetadataCache | undefined>(\n \"@yielded/auth-persistence/StorageMetadata\",\n { defaultValue: () => undefined },\n);\n\n/** Share metadata only while constructing one service graph. Captured contexts\n * cannot reuse it after acquisition succeeds, fails, or is interrupted. */\nexport const withStorageValidation = Effect.fnUntraced(function* <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n): Effect.fn.Return<A, E, R> {\n const parent = yield* CurrentMetadata;\n\n if (parent?.active) return yield* effect;\n const cache: MetadataCache = { active: true, clients: new Map() };\n\n return yield* effect.pipe(\n Effect.provideService(CurrentMetadata, cache),\n Effect.ensuring(\n Effect.sync(() => {\n cache.active = false;\n cache.clients.clear();\n }),\n ),\n );\n});\n\n/** Requirements checked against one physical catalog snapshot. */\nexport interface StorageValidation {\n readonly table: PhysicalStorageTable;\n readonly required: ReadonlyArray<ReadonlyArray<string>>;\n}\n\n/** Batch physical metadata reads, reusing only the current acquisition snapshot.\n * Every requirement is still checked; later acquisitions read fresh metadata. */\nexport const validateStorageBatch = Effect.fnUntraced(\n function* (dialect: \"pg\" | \"mysql\" | \"sqlite\", requirements: ReadonlyArray<StorageValidation>) {\n if (requirements.length === 0) return;\n if (\n dialect === \"sqlite\" &&\n requirements.some(({ table }) => table.schema !== undefined && table.schema !== \"main\")\n )\n return yield* PersistenceConfigurationError.make({\n reason: \"SQLite storage must belong to the main database\",\n });\n const client = yield* SqlClient;\n const cache = yield* CurrentMetadata;\n\n const byRelation = cache?.active\n ? (cache.clients.get(client) ?? new Map<string, typeof Metadata.Type>())\n : new Map<string, typeof Metadata.Type>();\n\n if (cache?.active) cache.clients.set(client, byRelation);\n\n const tables = new Map(requirements.map(({ table }) => [qualifiedTableName(table), table]));\n\n const missing = [...tables.entries()].filter(\n ([relation]) => !byRelation.has(`${dialect}:${relation}`),\n );\n\n const relations = missing.map(([relation]) => relation);\n\n if (relations.length > 0) {\n const metadata = yield* dialect === \"sqlite\"\n ? Effect.gen(function* () {\n const requested = client.join(\n \", \",\n false,\n )(missing.map(([relation, table]) => client`(${relation}, ${table.name})`));\n\n return yield* client`\n with requested(relation, name) as (values ${requested})\n select requested.relation,\n (select json_group_array(name) from pragma_table_info(requested.name)) as columns,\n (select json_group_array(json(storage_key.columns)) from (\n select json_group_array(name) as columns from (\n select name from pragma_table_info(requested.name) where pk > 0 order by pk\n )\n union all\n select (select json_group_array(name) from pragma_index_info(i.name)) as columns\n from pragma_index_list(requested.name) i\n where i.\"unique\" = 1 and i.partial = 0\n and not exists(select 1 from pragma_index_info(i.name) where name is null)\n ) storage_key where json_array_length(storage_key.columns) > 0) as keys\n from requested\n `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(SqliteMetadata)));\n })\n : dialect === \"mysql\"\n ? Effect.gen(function* () {\n const requested = client.join(\n \" union all \",\n false,\n )(\n missing.map(\n ([relation, table]) =>\n client`select ${relation} as relation, ${table.schema ?? null} as schemaName, ${table.name} as tableName`,\n ),\n );\n\n const columns = yield* client`\n select requested.relation, c.column_name as name\n from (${requested}) requested join information_schema.columns c\n on c.table_schema = coalesce(requested.schemaName, database()) and c.table_name = requested.tableName\n `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(MysqlColumns)));\n\n const indexes = yield* client`\n select requested.relation, i.index_name as name, i.column_name as columnName, i.sub_part as prefix\n from (${requested}) requested join information_schema.statistics i\n on i.table_schema = coalesce(requested.schemaName, database()) and i.table_name = requested.tableName\n where i.non_unique = 0 order by requested.relation, i.index_name, i.seq_in_index\n `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(MysqlIndexes)));\n\n return relations.map((relation) => {\n const grouped = new Map<string, Array<(typeof indexes)[number]>>();\n\n for (const index of indexes) {\n if (index.relation !== relation) continue;\n const entries = grouped.get(index.name) ?? [];\n\n entries.push(index);\n grouped.set(index.name, entries);\n }\n\n return {\n relation,\n columns: columns\n .filter((column) => column.relation === relation)\n .map((column) => column.name),\n keys: [...grouped.values()]\n .filter((entries) =>\n entries.every((entry) => entry.columnName !== null && entry.prefix === null),\n )\n .map((entries) =>\n entries.flatMap((entry) =>\n entry.columnName === null ? [] : [entry.columnName],\n ),\n ),\n };\n });\n })\n : Effect.gen(function* () {\n const requested = client.join(\n \", \",\n false,\n )(relations.map((relation) => client`(${relation}::text)`));\n\n return yield* client`\n select requested.relation,\n array(select a.attname::text from pg_attribute a\n where a.attrelid = to_regclass(requested.relation)\n and a.attnum > 0 and not a.attisdropped) as columns,\n coalesce((select jsonb_agg(storage_key.columns) from (\n select array_agg(a.attname::text order by k.ordinality) as columns\n from pg_index i\n cross join lateral unnest(i.indkey) with ordinality as k(attnum, ordinality)\n join pg_attribute a on a.attrelid = i.indrelid and a.attnum = k.attnum\n where i.indrelid = to_regclass(requested.relation)\n and i.indisunique and i.indisvalid and i.indimmediate\n and i.indpred is null and i.indexprs is null\n and k.ordinality <= i.indnkeyatts\n group by i.indexrelid\n ) storage_key), '[]'::jsonb) as keys\n from (values ${requested}) requested(relation)\n `.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PgMetadata)));\n });\n\n for (const row of metadata) byRelation.set(`${dialect}:${row.relation}`, row);\n }\n\n for (const { table, required } of requirements) {\n const relation = qualifiedTableName(table);\n const actual = byRelation.get(`${dialect}:${relation}`);\n\n for (const column of Object.values(table.columns)) {\n if (actual === undefined || !actual.columns.includes(column.name))\n return yield* PersistenceConfigurationError.make({\n reason: `Missing SQL column ${relation}.${column.name}`,\n });\n }\n yield* validateKeys(table, required, actual?.keys ?? []);\n }\n },\n Effect.mapError((error) =>\n Schema.is(PersistenceConfigurationError)(error)\n ? error\n : PersistenceConfigurationError.make({ reason: \"Cannot validate SQL storage metadata\" }),\n ),\n);\n\n/** Validate physical columns and unconditional uniqueness before operations run. */\nexport const validateStorage = (\n dialect: \"pg\" | \"mysql\" | \"sqlite\",\n table: PhysicalStorageTable,\n required: ReadonlyArray<ReadonlyArray<string>>,\n) => validateStorageBatch(dialect, [{ table, required }]);\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { NativeDatabase, TransactionNativeDatabase, TransactionOwner, makeTransactionKernel } from "./transaction-kernel.mjs";
2
- import { Context, Effect } from "effect";
2
+ import { Context, Crypto, Effect, PlatformError } from "effect";
3
3
  import { CurrentCommitJournal, HookConfigurationError, LifecycleHooks } from "@yielded/auth/Hooks";
4
4
  import * as SqlClient$1 from "effect/sql/SqlClient";
5
5
  import { Statement } from "effect/sql/Statement";
@@ -26,12 +26,12 @@ export interface TransactionExecution<Failure, OwnerId, Requirements = Lifecycle
26
26
  readonly active: () => boolean;
27
27
  readonly admit: Effect.Effect<void, Failure>;
28
28
  readonly poison: () => void;
29
- readonly run: <A, E, R>(body: Effect.Effect<A, E, R>, mutation?: boolean) => Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | Requirements>;
29
+ readonly run: <A, E, R>(body: Effect.Effect<A, E, R>, mutation?: boolean) => Effect.Effect<A, Failure, Exclude<Exclude<R, OwnerId | CurrentCommitJournal>, Crypto.Crypto> | Requirements>;
30
30
  }
31
31
  export declare const makeTransactionExecutionKernel: (transactions: Pick<ReturnType<typeof makeTransactionKernel>, "makeTransactionOwner" | "reportTransactionFailure">) => {
32
32
  sqlClientTransactionStandaloneGuard: <Failure>(unavailable: () => Failure, transactionService: SqlClient$1.SqlClient["transactionService"] | undefined) => Effect.Effect<void, Failure, never>;
33
- makeTransactionExecution: <Failure, OwnerId>(ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>, configuration: TransactionTargetConfiguration<Failure>, unavailable: () => Failure, nonce: () => string, bound?: TransactionBound<Failure> | undefined) => Effect.Effect<TransactionExecution<Failure, OwnerId, LifecycleHooks>, never, NativeDatabase>;
34
- coordinateTransactionOwner: <Failure, OwnerId, Resources, AllocationError, AllocationRequirements, Services, Transaction, A, E, R>(database: TransactionNativeDatabase, ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>, configuration: TransactionTargetConfiguration<Failure>, allocate: Effect.Effect<Resources, AllocationError, AllocationRequirements>, unavailable: () => Failure, nonce: () => string, services: (execution: TransactionExecution<Failure, OwnerId>, resources: Resources) => Services, owner: (transaction: Transaction, services: Services, append: (statement: Statement<any>) => void) => Effect.Effect<A, E, R>) => Effect.Effect<A, TransactionCoordinatorError<E, Failure>, Exclude<R, CurrentCommitJournal> | LifecycleHooks | AllocationRequirements>;
33
+ makeTransactionExecution: <Failure, OwnerId>(ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>, configuration: TransactionTargetConfiguration<Failure>, unavailable: () => Failure, nonce: Effect.Effect<string, PlatformError.PlatformError, Crypto.Crypto>, bound?: TransactionBound<Failure> | undefined) => Effect.Effect<TransactionExecution<Failure, OwnerId, LifecycleHooks>, never, Crypto.Crypto | NativeDatabase>;
34
+ coordinateTransactionOwner: <Failure, OwnerId, Resources, AllocationError, AllocationRequirements, Services, Transaction, A, E, R>(database: TransactionNativeDatabase, ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>, configuration: TransactionTargetConfiguration<Failure>, allocate: Effect.Effect<Resources, AllocationError, AllocationRequirements>, unavailable: () => Failure, nonce: Effect.Effect<string, PlatformError.PlatformError, Crypto.Crypto>, services: (execution: TransactionExecution<Failure, OwnerId>, resources: Resources) => Services, owner: (transaction: Transaction, services: Services, append: (statement: Statement<any>) => void) => Effect.Effect<A, E, R>) => Effect.Effect<A, TransactionCoordinatorError<E, Failure>, Exclude<R, CurrentCommitJournal> | LifecycleHooks | AllocationRequirements | Crypto.Crypto>;
35
35
  };
36
36
  //#endregion
37
37
  //# sourceMappingURL=transaction-execution-kernel.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { requireStandalone } from "./standalone.mjs";
2
2
  import { NativeDatabase } from "./transaction-kernel.mjs";
3
- import { Context, Effect } from "effect";
3
+ import { Context, Crypto, Effect } from "effect";
4
4
  import { CurrentCommitJournal, coordinateCommit, hasCommitScope } from "@yielded/auth/Hooks";
5
5
  //#region src/internal/transaction-execution-kernel.ts
6
6
  const makeTransactionExecutionKernel = (transactions) => {
@@ -8,6 +8,7 @@ const makeTransactionExecutionKernel = (transactions) => {
8
8
  const sqlClientTransactionStandaloneGuard = (unavailable, transactionService) => requireStandalone(unavailable, transactionService);
9
9
  const makeTransactionExecution = Effect.fnUntraced(function* (ownerTag, configuration, unavailable, nonce, bound) {
10
10
  const database = yield* NativeDatabase;
11
+ const crypto = yield* Crypto.Crypto;
11
12
  const invariant = (value) => {
12
13
  if (!value) throw unavailable();
13
14
  };
@@ -44,7 +45,7 @@ const makeTransactionExecutionKernel = (transactions) => {
44
45
  }))) : Effect.gen(function* () {
45
46
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
46
47
  yield* configuration.standaloneGuard(database.$client.transactionService);
47
- const marker = nonce();
48
+ const marker = yield* nonce;
48
49
  return (yield* coordinateCommit((journal) => {
49
50
  const run = (tx) => Effect.gen(function* () {
50
51
  const owner = makeTransactionOwner(tx, journal, marker, unavailable, {
@@ -61,7 +62,7 @@ const makeTransactionExecutionKernel = (transactions) => {
61
62
  });
62
63
  return configuration.mode === "batch" ? run(database) : configuration.transaction !== void 0 ? configuration.transaction(database, run) : database.transaction(run);
63
64
  }, { mode: configuration.mode })).value;
64
- })).pipe((operation) => reportTransactionFailure(operation, unavailable), Effect.mapError(unavailable), Effect.catchDefect(() => Effect.fail(unavailable())));
65
+ })).pipe(Effect.provideService(Crypto.Crypto, crypto), (operation) => reportTransactionFailure(operation, unavailable), Effect.mapError(unavailable), Effect.catchDefect(() => Effect.fail(unavailable())));
65
66
  });
66
67
  }
67
68
  };
@@ -70,8 +71,9 @@ const makeTransactionExecutionKernel = (transactions) => {
70
71
  return Effect.gen(function* () {
71
72
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
72
73
  yield* configuration.standaloneGuard(database.$client.transactionService);
74
+ const crypto = yield* Crypto.Crypto;
73
75
  const resources = yield* allocate.pipe((operation) => reportTransactionFailure(operation, unavailable), Effect.mapError(unavailable), Effect.catchDefect(() => Effect.fail(unavailable())));
74
- const marker = nonce();
76
+ const marker = yield* nonce.pipe(Effect.mapError(unavailable));
75
77
  return (yield* coordinateCommit((journal) => {
76
78
  const run = (tx) => Effect.gen(function* () {
77
79
  const state = makeTransactionOwner(tx, journal, marker, unavailable, {
@@ -101,7 +103,7 @@ const makeTransactionExecutionKernel = (transactions) => {
101
103
  }).pipe(Effect.ensuring(Effect.sync(() => {
102
104
  bound.active = false;
103
105
  })));
104
- });
106
+ }).pipe(Effect.provideService(Crypto.Crypto, crypto));
105
107
  return configuration.mode === "batch" ? run(database) : configuration.transaction !== void 0 ? configuration.transaction(database, run) : database.transaction(run);
106
108
  }, { mode: configuration.mode })).value;
107
109
  });