{"version":3,"file":"model.mjs","names":[],"sources":["../../src/drizzle/model.ts"],"sourcesContent":["import {\n  ExternalIdentityMutation,\n  SubjectProvisioner,\n  type SubjectProvisioningInput,\n  type ExternalIdentity,\n  type LoginIdentifier,\n} from \"@yielded/auth/Identity\";\nimport { getTableColumns, type AnyColumn, type InferInsertModel, type Table } from \"drizzle-orm\";\nimport * as Context from \"effect/Context\";\nimport * as DateTime from \"effect/DateTime\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Schema from \"effect/Schema\";\n\nexport {\n  PersistenceMappingError,\n  isMappedConstraintConflict,\n} from \"@yielded/auth-persistence/Adapter\";\n\nimport type { PersistenceMappingError, SubjectIdCodec } from \"@yielded/auth-persistence/Adapter\";\n\nexport class SubjectProvisioningError extends Schema.TaggedError<SubjectProvisioningError>()(\n  \"SubjectProvisioningError\",\n  { message: Schema.String },\n) {}\n\nexport interface InstantCodec<NativeInstant> {\n  readonly encode: (instant: DateTime.Utc) => NativeInstant;\n  readonly decode: (native: NativeInstant) => Effect.Effect<DateTime.Utc, PersistenceMappingError>;\n}\n\ntype ColumnKey<T extends Table> = Extract<keyof T[\"_\"][\"columns\"], string>;\n\nexport interface RequiredIdentityConstraints {\n  readonly identifier: \"unique(namespace,value)\";\n  readonly externalIdentity: \"unique(provider,issuer,subject)\";\n  readonly provisioningRequest: \"unique(requestId)\";\n  readonly provisioningSubject: \"notNull(provisioningRequest.subjectId)\";\n}\n\nexport interface RequiredSubjectProvisioningConstraints {\n  readonly identifier: \"unique(namespace,value)\";\n  readonly provisioningRequest: \"unique(requestId)\";\n  readonly provisioningSubject: \"notNull(provisioningRequest.subjectId)\";\n}\n\nexport interface RequiredExternalIdentityConstraints {\n  readonly externalIdentity: \"unique(provider,issuer,subject)\";\n}\n\nexport const requiredIdentityConstraints: RequiredIdentityConstraints = {\n  identifier: \"unique(namespace,value)\",\n  externalIdentity: \"unique(provider,issuer,subject)\",\n  provisioningRequest: \"unique(requestId)\",\n  provisioningSubject: \"notNull(provisioningRequest.subjectId)\",\n};\n\nexport const requiredSubjectProvisioningConstraints: RequiredSubjectProvisioningConstraints = {\n  identifier: \"unique(namespace,value)\",\n  provisioningRequest: \"unique(requestId)\",\n  provisioningSubject: \"notNull(provisioningRequest.subjectId)\",\n};\n\nexport const requiredExternalIdentityConstraints: RequiredExternalIdentityConstraints = {\n  externalIdentity: \"unique(provider,issuer,subject)\",\n};\n\nexport type { SubjectIdCodec } from \"@yielded/auth-persistence/Adapter\";\n\nexport interface SubjectProvisioningTables<\n  Subject extends Table,\n  Identifier extends Table,\n  Request extends Table,\n  NativeId,\n> {\n  readonly constraints: RequiredSubjectProvisioningConstraints;\n  readonly subject: {\n    readonly table: Subject;\n    readonly id: ColumnKey<Subject>;\n    readonly status: ColumnKey<Subject>;\n    readonly isActiveStatus: (value: unknown) => boolean;\n    readonly encodeInsert: (\n      input: SubjectProvisioningInput,\n      allocatedId: NativeId | undefined,\n    ) => InferInsertModel<Subject>;\n    readonly allocateId?: Effect.Effect<NativeId, PersistenceMappingError>;\n    /** Required instead of allocateId when a synchronous-only DO transaction preallocates IDs. */\n    readonly allocateIdSync?: () => NativeId;\n    /** Decodes MySQL `$returningId()` for an autoincrement/runtime-default key. */\n    readonly decodeGeneratedId?: (\n      rows: ReadonlyArray<unknown>,\n    ) => Effect.Effect<NativeId, PersistenceMappingError>;\n  };\n  readonly identifier: {\n    readonly table: Identifier;\n    readonly namespace: ColumnKey<Identifier>;\n    readonly value: ColumnKey<Identifier>;\n    readonly subjectId: ColumnKey<Identifier>;\n    readonly encodeInsert: (\n      identifier: LoginIdentifier,\n      subjectId: NativeId,\n      verifiedAt: DateTime.Utc | undefined,\n    ) => InferInsertModel<Identifier>;\n  };\n  readonly provisioningRequest: {\n    readonly table: Request;\n    readonly requestId: ColumnKey<Request>;\n    readonly fingerprint: ColumnKey<Request>;\n    readonly subjectId: ColumnKey<Request>;\n    readonly encodeInsert: (\n      requestId: string,\n      fingerprint: string,\n      subjectId: NativeId,\n    ) => InferInsertModel<Request>;\n  };\n  readonly subjectId: SubjectIdCodec<NativeId>;\n  /** Narrows only database constraint failures; every other cause is unavailable. */\n  readonly isConstraintConflict: (cause: unknown) => boolean;\n}\n\nexport interface ExternalIdentityTables<Subject extends Table, External extends Table, NativeId> {\n  readonly constraints: RequiredExternalIdentityConstraints;\n  readonly subject: {\n    readonly table: Subject;\n    readonly id: ColumnKey<Subject>;\n    readonly status: ColumnKey<Subject>;\n    readonly isActiveStatus: (value: unknown) => boolean;\n  };\n  readonly externalIdentity: {\n    readonly table: External;\n    readonly provider: ColumnKey<External>;\n    readonly issuer: ColumnKey<External>;\n    readonly subject: ColumnKey<External>;\n    readonly subjectId: ColumnKey<External>;\n    readonly encodeInsert: (\n      identity: ExternalIdentity,\n      subjectId: NativeId,\n    ) => InferInsertModel<External>;\n  };\n  readonly subjectId: SubjectIdCodec<NativeId>;\n  readonly isConstraintConflict: (cause: unknown) => boolean;\n}\n\nexport interface IdentityTables<\n  Subject extends Table,\n  Identifier extends Table,\n  External extends Table,\n  Request extends Table,\n  NativeId,\n> {\n  readonly constraints: RequiredIdentityConstraints;\n  readonly subject: SubjectProvisioningTables<Subject, Identifier, Request, NativeId>[\"subject\"];\n  readonly identifier: SubjectProvisioningTables<\n    Subject,\n    Identifier,\n    Request,\n    NativeId\n  >[\"identifier\"];\n  readonly externalIdentity: ExternalIdentityTables<\n    Subject,\n    External,\n    NativeId\n  >[\"externalIdentity\"];\n  readonly provisioningRequest: SubjectProvisioningTables<\n    Subject,\n    Identifier,\n    Request,\n    NativeId\n  >[\"provisioningRequest\"];\n  readonly subjectId: SubjectIdCodec<NativeId>;\n  readonly isConstraintConflict: (cause: unknown) => boolean;\n}\n\n/** Extra encoders used by D1's ordered batch when the subject id is database-generated. */\nexport interface D1SubjectProvisioningMapping<\n  Subject extends Table,\n  Identifier extends Table,\n  Request extends Table,\n  NativeId,\n> extends SubjectProvisioningTables<Subject, Identifier, Request, NativeId> {\n  readonly d1: {\n    readonly requestInsertWithoutSubject: (\n      requestId: string,\n      fingerprint: string,\n    ) => Partial<InferInsertModel<Request>>;\n    readonly identifierInsertWithoutSubject: (\n      identifier: LoginIdentifier,\n      verifiedAt: DateTime.Utc | undefined,\n    ) => Partial<InferInsertModel<Identifier>>;\n    /** Unshadowed hidden-rowid alias; omit for WITHOUT ROWID or preallocated IDs. */\n    readonly generatedRowIdAlias?: \"rowid\" | \"_rowid_\" | \"oid\";\n  };\n}\n\nexport interface D1ExternalIdentityMapping<\n  Subject extends Table,\n  External extends Table,\n  NativeId,\n> extends ExternalIdentityTables<Subject, External, NativeId> {\n  readonly d1: {\n    readonly activeStatusValue: unknown;\n  };\n}\n\nexport interface D1GeneratedIdentityMapping<\n  Subject extends Table,\n  Identifier extends Table,\n  External extends Table,\n  Request extends Table,\n  NativeId,\n> extends IdentityTables<Subject, Identifier, External, Request, NativeId> {\n  readonly d1: D1SubjectProvisioningMapping<Subject, Identifier, Request, NativeId>[\"d1\"] &\n    D1ExternalIdentityMapping<Subject, External, NativeId>[\"d1\"];\n}\n\nexport const provisioningFingerprint = (input: SubjectProvisioningInput): string =>\n  // oxlint-disable-next-line no-restricted-properties -- stable internal provisioning fingerprint, never external JSON.\n  JSON.stringify(\n    input.identifier === undefined\n      ? [\"v1\", null]\n      : [\n          \"v1\",\n          input.identifier.namespace,\n          input.identifier.value,\n          input.verifiedAt === undefined ? null : DateTime.toEpochMillis(input.verifiedAt),\n        ],\n  );\n\n/**\n * Drizzle wraps Effect SQL failures before adapters observe them. Give the\n * consumer classifier each semantic wrapper in the short `cause`/`reason`\n * chain without exposing query text or bound parameters through public errors.\n */\n\nexport const column = <T extends Table>(table: T, key: ColumnKey<T>): AnyColumn =>\n  getTableColumns(table)[key] as AnyColumn;\n\nexport const updateValues = <T extends Table>(\n  entries: ReadonlyArray<readonly [ColumnKey<T>, unknown]>,\n): Partial<InferInsertModel<T>> => Object.fromEntries(entries) as Partial<InferInsertModel<T>>;\n\n/** Acquire an application database in Effect and expose the identity services as one layer. */\nexport const identityServicesLayer = <E, R>(\n  acquire: Effect.Effect<\n    {\n      readonly subjectProvisioner: SubjectProvisioner[\"Service\"];\n      readonly externalIdentityMutation: ExternalIdentityMutation[\"Service\"];\n    },\n    E,\n    R\n  >,\n) =>\n  Layer.effectContext(\n    Effect.map(acquire, ({ subjectProvisioner, externalIdentityMutation }) =>\n      Context.make(SubjectProvisioner, subjectProvisioner).pipe(\n        Context.add(ExternalIdentityMutation, externalIdentityMutation),\n      ),\n    ),\n  );\n"],"mappings":";;;;;;;;;AAqBA,IAAa,2BAAb,cAA8C,OAAO,YAAsC,CAAC,CAC1F,4BACA,EAAE,SAAS,OAAO,OAAO,CAC3B,CAAC,CAAC,CAAC;AA0BH,MAAa,8BAA2D;CACtE,YAAY;CACZ,kBAAkB;CAClB,qBAAqB;CACrB,qBAAqB;AACvB;AAEA,MAAa,yCAAiF;CAC5F,YAAY;CACZ,qBAAqB;CACrB,qBAAqB;AACvB;AAEA,MAAa,sCAA2E,EACtF,kBAAkB,kCACpB;AAsJA,MAAa,2BAA2B,UAEtC,KAAK,UACH,MAAM,eAAe,KAAA,IACjB,CAAC,MAAM,IAAI,IACX;CACE;CACA,MAAM,WAAW;CACjB,MAAM,WAAW;CACjB,MAAM,eAAe,KAAA,IAAY,OAAO,SAAS,cAAc,MAAM,UAAU;AACjF,CACN;;;;;;AAQF,MAAa,UAA2B,OAAU,QAChD,gBAAgB,KAAK,CAAC,CAAC;;AAOzB,MAAa,yBACX,YASA,MAAM,cACJ,OAAO,IAAI,UAAU,EAAE,oBAAoB,+BACzC,QAAQ,KAAK,oBAAoB,kBAAkB,CAAC,CAAC,KACnD,QAAQ,IAAI,0BAA0B,wBAAwB,CAChE,CACF,CACF"}