@yielded/auth-persistence 0.1.0-beta.11 → 0.1.0-beta.12

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 (66) hide show
  1. package/README.md +6 -1
  2. package/dist/Adapter.d.mts +2 -2
  3. package/dist/Adapter.mjs +4 -4
  4. package/dist/internal/PasswordPreparedPostconditions.d.mts +1 -1
  5. package/dist/internal/configuration.d.mts +1 -1
  6. package/dist/internal/email-kernel.d.mts +5 -5
  7. package/dist/internal/email-kernel.mjs +8 -6
  8. package/dist/internal/email-kernel.mjs.map +1 -1
  9. package/dist/internal/models/email-model.d.mts +1 -1
  10. package/dist/internal/oauth-server.mjs +1 -1
  11. package/dist/internal/oauth-server.mjs.map +1 -1
  12. package/dist/internal/passkey/target.mjs +21 -12
  13. package/dist/internal/passkey/target.mjs.map +1 -1
  14. package/dist/internal/passkey/write-target.mjs +6 -6
  15. package/dist/internal/passkey/write-target.mjs.map +1 -1
  16. package/dist/internal/passkey-kernel.d.mts +9 -12
  17. package/dist/internal/passkeys.mjs +8 -7
  18. package/dist/internal/passkeys.mjs.map +1 -1
  19. package/dist/internal/password-kernel.d.mts +3 -3
  20. package/dist/internal/password-kernel.mjs +3 -2
  21. package/dist/internal/password-kernel.mjs.map +1 -1
  22. package/dist/internal/persistence.d.mts +3 -2
  23. package/dist/internal/persistence.mjs +22 -26
  24. package/dist/internal/persistence.mjs.map +1 -1
  25. package/dist/internal/proof-kernel.d.mts +2 -2
  26. package/dist/internal/proof-kernel.mjs +2 -1
  27. package/dist/internal/proof-kernel.mjs.map +1 -1
  28. package/dist/internal/registration.mjs +6 -1
  29. package/dist/internal/registration.mjs.map +1 -1
  30. package/dist/internal/session-kernel.d.mts +19 -9
  31. package/dist/internal/session-kernel.mjs +155 -147
  32. package/dist/internal/session-kernel.mjs.map +1 -1
  33. package/dist/internal/sql-persistence.mjs +2 -3
  34. package/dist/internal/sql-persistence.mjs.map +1 -1
  35. package/dist/internal/sql-query.mjs +4 -2
  36. package/dist/internal/sql-query.mjs.map +1 -1
  37. package/dist/internal/standalone.d.mts +2 -4
  38. package/dist/internal/standalone.mjs +2 -2
  39. package/dist/internal/standalone.mjs.map +1 -1
  40. package/dist/internal/storage-mapping.mjs +2 -2
  41. package/dist/internal/storage-validation.mjs +4 -2
  42. package/dist/internal/storage-validation.mjs.map +1 -1
  43. package/dist/internal/transaction-execution-kernel.d.mts +6 -7
  44. package/dist/internal/transaction-execution-kernel.mjs +9 -7
  45. package/dist/internal/transaction-execution-kernel.mjs.map +1 -1
  46. package/dist/internal/transaction-kernel.d.mts +7 -3
  47. package/dist/internal/transaction-kernel.mjs +5 -2
  48. package/dist/internal/transaction-kernel.mjs.map +1 -1
  49. package/package.json +1 -1
  50. package/src/Adapter.ts +6 -1
  51. package/src/internal/email-kernel.ts +30 -35
  52. package/src/internal/oauth-server.ts +1 -1
  53. package/src/internal/passkey/target.ts +36 -43
  54. package/src/internal/passkey/write-target.ts +4 -14
  55. package/src/internal/passkeys.ts +4 -9
  56. package/src/internal/password-kernel.ts +2 -2
  57. package/src/internal/persistence.ts +77 -68
  58. package/src/internal/proof-kernel.ts +2 -2
  59. package/src/internal/registration.ts +22 -7
  60. package/src/internal/session-kernel.ts +1122 -1139
  61. package/src/internal/sql-persistence.ts +1 -1
  62. package/src/internal/sql-query.ts +5 -3
  63. package/src/internal/standalone.ts +5 -7
  64. package/src/internal/storage-validation.ts +20 -16
  65. package/src/internal/transaction-execution-kernel.ts +17 -15
  66. package/src/internal/transaction-kernel.ts +7 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sql-persistence.mjs","names":[],"sources":["../../src/internal/sql-persistence.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\n\nimport { PersistenceConfigurationError, type PersistenceApi } from \"./configuration\";\nimport { makeComposedPasskeys } from \"./passkeys\";\nimport { createPersistence } from \"./persistence\";\nimport { makeSqlDatabase, sqlQueryOperations } from \"./sql-query\";\nimport { Table } from \"./sql-table\";\nimport type { StorageTable } from \"./storage-tables\";\n\nconst table = (definition: StorageTable) =>\n new Table(definition.name, definition.columns, definition.unique, definition.schema);\n\nexport const AuthPersistence: PersistenceApi<Table> & { readonly table: typeof table } = {\n ...createPersistence({\n makeTable: table,\n describe: (table: Table): StorageTable => ({\n name: table.name,\n ...(table.schema === undefined ? {} : { schema: table.schema }),\n columns: Object.fromEntries(\n Object.entries(table.columns).map(([key, column]) => [key, column.options]),\n ),\n unique: table.unique,\n }),\n operations: sqlQueryOperations,\n passkeys: makeComposedPasskeys(sqlQueryOperations),\n acquire: Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n const dialect = sql.onDialectOrElse({\n pg: () => \"pg\" as const,\n sqlite: () => \"sqlite\" as const,\n orElse: () => undefined,\n });\n\n if (dialect === undefined)\n return yield* PersistenceConfigurationError.make({\n reason: \"Use an explicit adapter for this SQL dialect\",\n });\n\n return makeSqlDatabase(sql, dialect);\n }),\n }),\n table,\n};\n"],"mappings":";;;;;;;;AAUA,MAAM,SAAS,eACb,IAAI,MAAM,WAAW,MAAM,WAAW,SAAS,WAAW,QAAQ,WAAW,MAAM;AAErF,MAAa,kBAA4E;CACvF,GAAG,kBAAkB;EACnB,WAAW;EACX,WAAW,WAAgC;GACzC,MAAM,MAAM;GACZ,GAAI,MAAM,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;GAC7D,SAAS,OAAO,YACd,OAAO,QAAQ,MAAM,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY,CAAC,KAAK,OAAO,OAAO,CAAC,CAC5E;GACA,QAAQ,MAAM;EAChB;EACA,YAAY;EACZ,UAAU,qBAAqB,kBAAkB;EACjD,SAAS,OAAO,IAAI,aAAa;GAC/B,MAAM,MAAM,OAAO,UAAU;GAE7B,MAAM,UAAU,IAAI,gBAAgB;IAClC,UAAU;IACV,cAAc;IACd,cAAc,KAAA;GAChB,CAAC;GAED,IAAI,YAAY,KAAA,GACd,OAAO,OAAO,8BAA8B,KAAK,EAC/C,QAAQ,+CACV,CAAC;GAEH,OAAO,gBAAgB,KAAK,OAAO;EACrC,CAAC;CACH,CAAC;CACD;AACF"}
1
+ {"version":3,"file":"sql-persistence.mjs","names":[],"sources":["../../src/internal/sql-persistence.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\n\nimport { PersistenceConfigurationError, type PersistenceApi } from \"./configuration\";\nimport { makeComposedPasskeys } from \"./passkeys\";\nimport { createPersistence } from \"./persistence\";\nimport { makeSqlDatabase, sqlQueryOperations } from \"./sql-query\";\nimport { Table } from \"./sql-table\";\nimport type { StorageTable } from \"./storage-tables\";\n\nconst table = (definition: StorageTable) =>\n new Table(definition.name, definition.columns, definition.unique, definition.schema);\n\nexport const AuthPersistence: PersistenceApi<Table> & { readonly table: typeof table } = {\n ...createPersistence({\n makeTable: table,\n describe: (table: Table): StorageTable => ({\n name: table.name,\n ...(table.schema === undefined ? {} : { schema: table.schema }),\n columns: Object.fromEntries(\n Object.entries(table.columns).map(([key, column]) => [key, column.options]),\n ),\n unique: table.unique,\n }),\n operations: sqlQueryOperations,\n passkeys: makeComposedPasskeys(sqlQueryOperations),\n acquire: Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n const dialect = sql.onDialectOrElse({\n pg: () => \"pg\" as const,\n sqlite: () => \"sqlite\" as const,\n orElse: () => undefined,\n });\n\n if (dialect === undefined)\n return yield* PersistenceConfigurationError.make({\n reason: \"Use an explicit adapter for this SQL dialect\",\n });\n\n return yield* makeSqlDatabase(dialect);\n }),\n }),\n table,\n};\n"],"mappings":";;;;;;;;AAUA,MAAM,SAAS,eACb,IAAI,MAAM,WAAW,MAAM,WAAW,SAAS,WAAW,QAAQ,WAAW,MAAM;AAErF,MAAa,kBAA4E;CACvF,GAAG,kBAAkB;EACnB,WAAW;EACX,WAAW,WAAgC;GACzC,MAAM,MAAM;GACZ,GAAI,MAAM,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;GAC7D,SAAS,OAAO,YACd,OAAO,QAAQ,MAAM,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY,CAAC,KAAK,OAAO,OAAO,CAAC,CAC5E;GACA,QAAQ,MAAM;EAChB;EACA,YAAY;EACZ,UAAU,qBAAqB,kBAAkB;EACjD,SAAS,OAAO,IAAI,aAAa;GAG/B,MAAM,WAAU,OAFG,UAAU,UAAA,CAET,gBAAgB;IAClC,UAAU;IACV,cAAc;IACd,cAAc,KAAA;GAChB,CAAC;GAED,IAAI,YAAY,KAAA,GACd,OAAO,OAAO,8BAA8B,KAAK,EAC/C,QAAQ,+CACV,CAAC;GAEH,OAAO,OAAO,gBAAgB,OAAO;EACvC,CAAC;CACH,CAAC;CACD;AACF"}
@@ -1,6 +1,7 @@
1
1
  import { PersistenceMappingError } from "./mapping-error.mjs";
2
2
  import { Fragment, Table, identifier } from "./sql-table.mjs";
3
3
  import { Effect, Predicate } from "effect";
4
+ import { SqlClient } from "effect/sql/SqlClient";
4
5
  //#region src/internal/sql-query.ts
5
6
  const render = (value, compiler) => {
6
7
  if (value instanceof Fragment) return value.render(compiler);
@@ -111,7 +112,8 @@ const compile = (state, compiler) => {
111
112
  return query;
112
113
  };
113
114
  /** Captures one Effect SQL client. Its transaction service remains the physical owner. */
114
- const makeSqlDatabase = (client, dialect) => {
115
+ const makeSqlDatabase = Effect.fnUntraced(function* (dialect) {
116
+ const client = yield* SqlClient;
115
117
  const query = (state) => {
116
118
  const execute = Effect.suspend(() => {
117
119
  const compiler = {
@@ -200,7 +202,7 @@ const makeSqlDatabase = (client, dialect) => {
200
202
  transaction: (body) => client.withTransaction(Effect.suspend(() => body(database)))
201
203
  };
202
204
  return database;
203
- };
205
+ });
204
206
  //#endregion
205
207
  export { makeSqlDatabase, sqlQueryOperations };
206
208
 
@@ -1 +1 @@
1
- {"version":3,"file":"sql-query.mjs","names":[],"sources":["../../src/internal/sql-query.ts"],"sourcesContent":["import { Effect, Predicate } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\nimport type { SqlError } from \"effect/sql/SqlError\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { QueryOperations } from \"./query-operations\";\nimport {\n type Column,\n Fragment,\n Table,\n identifier,\n type Compiler,\n type Dialect,\n type Row,\n} from \"./sql-table\";\n\nconst render = (value: unknown, compiler: Compiler): string => {\n if (value instanceof Fragment) return value.render(compiler);\n if (Predicate.hasProperty(value, \"getSQL\") && typeof value.getSQL === \"function\") {\n return render(value.getSQL(), compiler);\n }\n\n compiler.parameters.push(\n compiler.dialect === \"sqlite\" && typeof value === \"boolean\" ? Number(value) : value,\n );\n\n return compiler.dialect === \"pg\" ? `$${compiler.parameters.length}` : \"?\";\n};\n\nconst template = (parts: TemplateStringsArray, ...values: unknown[]): Fragment =>\n new Fragment((compiler) =>\n parts.reduce(\n (sql, part, i) => sql + (i === 0 ? \"\" : render(values[i - 1], compiler)) + part,\n \"\",\n ),\n );\n\nconst combine = (separator: string, values: ReadonlyArray<Fragment | undefined>) => {\n const fragments = values.filter((value) => value !== undefined);\n\n return fragments.length === 0\n ? undefined\n : new Fragment(\n (compiler) =>\n \"(\" + fragments.map((fragment) => fragment.render(compiler)).join(separator) + \")\",\n );\n};\n\nconst and = (...values: ReadonlyArray<Fragment | undefined>) => combine(\" AND \", values);\nconst or = (...values: ReadonlyArray<Fragment | undefined>) => combine(\" OR \", values);\n\nconst operations = {\n and,\n or,\n asc: (value: unknown) => template`${value} ASC`,\n eq: (left: unknown, right: unknown) => template`${left} = ${right}`,\n gt: (left: unknown, right: unknown) => template`${left} > ${right}`,\n gte: (left: unknown, right: unknown) => template`${left} >= ${right}`,\n lte: (left: unknown, right: unknown) => template`${left} <= ${right}`,\n isNull: (value: unknown) => template`${value} IS NULL`,\n notExists: (value: unknown) => template`NOT EXISTS (${value})`,\n inArray: (column: unknown, values: ReadonlyArray<unknown>) =>\n values.length === 0\n ? template`false`\n : new Fragment(\n (compiler) =>\n `${render(column, compiler)} IN (${values.map((value) => render(value, compiler)).join(\", \")})`,\n ),\n sql: Object.assign(template, {\n param: (value: unknown, column: Column) => column.mapToDriverValue(value),\n join: (values: ReadonlyArray<unknown>, separator: Fragment = template``) =>\n new Fragment((compiler) =>\n values\n .map(\n (value, index) =>\n (index === 0 ? \"\" : separator.render(compiler)) + render(value, compiler),\n )\n .join(\"\"),\n ),\n }),\n getTableColumns: (table: Table) => table.columns,\n column: (table: Table, key: string) => {\n const column = table.columns[key];\n\n if (column === undefined)\n throw PersistenceMappingError.make({ operation: \"column\", cause: key });\n\n return column;\n },\n updateValues: (entries: ReadonlyArray<readonly [string, unknown]>) => Object.fromEntries(entries),\n balancedD1And: and,\n // Raw clients use interactive transactions, so no D1 batch compaction is needed.\n compactD1GeneratedStatement: <S>(_client: unknown, statement: S) => statement,\n};\n\n// The existing kernels erase query-builder shapes at this boundary. The raw compiler\n// implements precisely those operations; no row decoding or Effect requirements are cast.\nexport const sqlQueryOperations = operations as unknown as QueryOperations;\n\ntype Selection = Readonly<Record<string, Fragment>>;\n\ninterface QueryState {\n readonly operation: \"select\" | \"insert\" | \"update\" | \"delete\";\n readonly table?: Table | Fragment;\n readonly joins?: ReadonlyArray<{\n readonly table: Table | Fragment;\n readonly on: Fragment;\n }>;\n readonly selection?: Selection;\n readonly where?: Fragment;\n readonly values?: Row;\n readonly returning?: Selection | true;\n readonly order?: ReadonlyArray<Fragment>;\n readonly limit?: number;\n readonly lock?: boolean;\n readonly conflict?: boolean;\n}\n\nconst selectionFor = (state: QueryState): Selection =>\n state.selection ?? (state.table instanceof Table ? state.table.columns : {});\n\nconst selectionFields = (selection: Selection) => {\n const used = new Set(Object.keys(selection));\n let index = 0;\n\n return Object.entries(selection).flatMap<{\n readonly key: string;\n readonly name: string | undefined;\n readonly column: Fragment;\n readonly alias: string;\n }>(([key, value]) =>\n value instanceof Table\n ? Object.entries(value.columns).map(([name, column]) => {\n let alias: string;\n\n do alias = `auth_column_${index++}`;\n while (used.has(alias));\n used.add(alias);\n\n return { key, name, column, alias };\n })\n : [{ key, name: undefined, column: value, alias: key }],\n );\n};\n\nconst projection = (selection: Selection, compiler: Compiler) =>\n selectionFields(selection)\n .map(({ column, alias }) => `${column.render(compiler)} AS ${identifier(alias)}`)\n .join(\", \");\n\n// Joined table selections retain their own column codecs and nested row names.\nconst decodeRow = (selection: Selection, row: Row): Row => {\n const entries: Array<readonly [string, unknown]> = [];\n const groups = new Map<string, Array<readonly [string, unknown]>>();\n\n for (const field of selectionFields(selection)) {\n const value = field.column.decode(row[field.alias]);\n\n if (field.name === undefined) entries.push([field.key, value]);\n else {\n const group = groups.get(field.key) ?? [];\n\n group.push([field.name, value]);\n groups.set(field.key, group);\n }\n }\n\n return Object.fromEntries([\n ...entries,\n ...Array.from(groups, ([key, values]) => [key, Object.fromEntries(values)]),\n ]);\n};\n\nconst compile = (state: QueryState, compiler: Compiler): string => {\n const table = state.table;\n\n if (table === undefined)\n throw PersistenceMappingError.make({ operation: \"query\", cause: \"Missing table\" });\n let query: string;\n\n if (state.operation === \"select\") {\n query = `SELECT ${projection(selectionFor(state), compiler)} FROM ${table.render(compiler)}`;\n for (const join of state.joins ?? [])\n query += ` INNER JOIN ${join.table.render(compiler)} ON ${join.on.render(compiler)}`;\n } else {\n if (!(table instanceof Table))\n throw PersistenceMappingError.make({\n operation: \"query\",\n cause: \"Mutation requires a table\",\n });\n const target = table.render(compiler);\n\n const fields = Object.entries(state.values ?? {}).filter(\n ([key, value]) => table.columns[key] !== undefined && value !== undefined,\n );\n\n if (state.operation === \"insert\") {\n query = `INSERT INTO ${target} (${fields.map(([key]) => identifier(table.columns[key].options.name)).join(\", \")}) VALUES (${fields.map(([, value]) => render(value, compiler)).join(\", \")})`;\n } else if (state.operation === \"update\") {\n query = `UPDATE ${target} SET ${fields.map(([key, value]) => `${identifier(table.columns[key].options.name)} = ${render(value, compiler)}`).join(\", \")}`;\n } else query = `DELETE FROM ${target}`;\n }\n if (state.where !== undefined) query += ` WHERE ${state.where.render(compiler)}`;\n if (state.order?.length)\n query += ` ORDER BY ${state.order.map((field) => field.render(compiler)).join(\", \")}`;\n if (state.limit !== undefined) query += ` LIMIT ${render(state.limit, compiler)}`;\n if (state.conflict) query += \" ON CONFLICT DO NOTHING\";\n if (state.returning)\n query += ` RETURNING ${projection(state.returning === true && table instanceof Table ? table.columns : state.returning === true ? {} : state.returning, compiler)}`;\n if (state.lock && compiler.dialect === \"pg\") query += \" FOR UPDATE\";\n\n return query;\n};\n\n/** Captures one Effect SQL client. Its transaction service remains the physical owner. */\nexport const makeSqlDatabase = (client: SqlClient, dialect: Dialect): SqlDatabase => {\n const query = (state: QueryState): Query => {\n const execute = Effect.suspend(() => {\n const compiler: Compiler = { dialect, parameters: [] };\n const text = compile(state, compiler);\n\n const selected: Selection =\n state.operation === \"select\"\n ? selectionFor(state)\n : state.returning === true\n ? state.table instanceof Table\n ? state.table.columns\n : {}\n : state.returning || {};\n\n return client\n .unsafe<Row>(text, compiler.parameters)\n .pipe(Effect.map((rows) => rows.map((row) => decodeRow(selected, row))));\n });\n\n return Object.assign(execute, {\n toSQL: () => {\n const compiler: Compiler = { dialect, parameters: [] };\n\n return { sql: compile(state, compiler), params: compiler.parameters };\n },\n getSQL: () => new Fragment((compiler) => compile(state, compiler)),\n from: (table: Table | Fragment) => query({ ...state, table }),\n innerJoin: (table: Table | Fragment, on: Fragment) =>\n query({ ...state, joins: [...(state.joins ?? []), { table, on }] }),\n where: (where: Fragment | undefined) => query({ ...state, where }),\n limit: (limit: number) => query({ ...state, limit }),\n orderBy: (...order: Fragment[]) => query({ ...state, order }),\n for: (_lock: \"update\") => query({ ...state, lock: true }),\n values: (values: Row) => query({ ...state, values }),\n set: (values: Row) => query({ ...state, values }),\n returning: (returning: Selection | true = true) => query({ ...state, returning }),\n onConflictDoNothing: () => query({ ...state, conflict: true }),\n });\n };\n\n const database: SqlDatabase = {\n $client: client,\n select: (selection?: Selection) => query({ operation: \"select\", selection }),\n insert: (table: Table) => query({ operation: \"insert\", table }),\n update: (table: Table) => query({ operation: \"update\", table }),\n delete: (table: Table) => query({ operation: \"delete\", table }),\n transaction: <A, E, R>(\n body: (database: SqlDatabase) => Effect.Effect<A, E, R>,\n ): Effect.Effect<A, E | SqlError, R> =>\n client.withTransaction(Effect.suspend(() => body(database))),\n };\n\n return database;\n};\n\ninterface Query extends Effect.Effect<Row[], SqlError> {\n toSQL(): { readonly sql: string; readonly params: ReadonlyArray<unknown> };\n getSQL(): Fragment;\n from(table: Table | Fragment): Query;\n innerJoin(table: Table | Fragment, on: Fragment): Query;\n where(where: Fragment | undefined): Query;\n limit(limit: number): Query;\n orderBy(...order: Fragment[]): Query;\n for(lock: \"update\"): Query;\n values(values: Row): Query;\n set(values: Row): Query;\n returning(returning?: Selection | true): Query;\n onConflictDoNothing(): Query;\n}\n\nexport interface SqlDatabase {\n readonly $client: SqlClient;\n select(selection?: Selection): Query;\n insert(table: Table): Query;\n update(table: Table): Query;\n delete(table: Table): Query;\n transaction<A, E, R>(\n body: (database: SqlDatabase) => Effect.Effect<A, E, R>,\n ): Effect.Effect<A, E | SqlError, R>;\n}\n\nexport const sql = template;\n"],"mappings":"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"}
1
+ {"version":3,"file":"sql-query.mjs","names":[],"sources":["../../src/internal/sql-query.ts"],"sourcesContent":["import { Effect, Predicate } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { SqlError } from \"effect/sql/SqlError\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { QueryOperations } from \"./query-operations\";\nimport {\n type Column,\n Fragment,\n Table,\n identifier,\n type Compiler,\n type Dialect,\n type Row,\n} from \"./sql-table\";\n\nconst render = (value: unknown, compiler: Compiler): string => {\n if (value instanceof Fragment) return value.render(compiler);\n if (Predicate.hasProperty(value, \"getSQL\") && typeof value.getSQL === \"function\") {\n return render(value.getSQL(), compiler);\n }\n\n compiler.parameters.push(\n compiler.dialect === \"sqlite\" && typeof value === \"boolean\" ? Number(value) : value,\n );\n\n return compiler.dialect === \"pg\" ? `$${compiler.parameters.length}` : \"?\";\n};\n\nconst template = (parts: TemplateStringsArray, ...values: unknown[]): Fragment =>\n new Fragment((compiler) =>\n parts.reduce(\n (sql, part, i) => sql + (i === 0 ? \"\" : render(values[i - 1], compiler)) + part,\n \"\",\n ),\n );\n\nconst combine = (separator: string, values: ReadonlyArray<Fragment | undefined>) => {\n const fragments = values.filter((value) => value !== undefined);\n\n return fragments.length === 0\n ? undefined\n : new Fragment(\n (compiler) =>\n \"(\" + fragments.map((fragment) => fragment.render(compiler)).join(separator) + \")\",\n );\n};\n\nconst and = (...values: ReadonlyArray<Fragment | undefined>) => combine(\" AND \", values);\nconst or = (...values: ReadonlyArray<Fragment | undefined>) => combine(\" OR \", values);\n\nconst operations = {\n and,\n or,\n asc: (value: unknown) => template`${value} ASC`,\n eq: (left: unknown, right: unknown) => template`${left} = ${right}`,\n gt: (left: unknown, right: unknown) => template`${left} > ${right}`,\n gte: (left: unknown, right: unknown) => template`${left} >= ${right}`,\n lte: (left: unknown, right: unknown) => template`${left} <= ${right}`,\n isNull: (value: unknown) => template`${value} IS NULL`,\n notExists: (value: unknown) => template`NOT EXISTS (${value})`,\n inArray: (column: unknown, values: ReadonlyArray<unknown>) =>\n values.length === 0\n ? template`false`\n : new Fragment(\n (compiler) =>\n `${render(column, compiler)} IN (${values.map((value) => render(value, compiler)).join(\", \")})`,\n ),\n sql: Object.assign(template, {\n param: (value: unknown, column: Column) => column.mapToDriverValue(value),\n join: (values: ReadonlyArray<unknown>, separator: Fragment = template``) =>\n new Fragment((compiler) =>\n values\n .map(\n (value, index) =>\n (index === 0 ? \"\" : separator.render(compiler)) + render(value, compiler),\n )\n .join(\"\"),\n ),\n }),\n getTableColumns: (table: Table) => table.columns,\n column: (table: Table, key: string) => {\n const column = table.columns[key];\n\n if (column === undefined)\n throw PersistenceMappingError.make({ operation: \"column\", cause: key });\n\n return column;\n },\n updateValues: (entries: ReadonlyArray<readonly [string, unknown]>) => Object.fromEntries(entries),\n balancedD1And: and,\n // Raw clients use interactive transactions, so no D1 batch compaction is needed.\n compactD1GeneratedStatement: <S>(_client: unknown, statement: S) => statement,\n};\n\n// The existing kernels erase query-builder shapes at this boundary. The raw compiler\n// implements precisely those operations; no row decoding or Effect requirements are cast.\nexport const sqlQueryOperations = operations as unknown as QueryOperations;\n\ntype Selection = Readonly<Record<string, Fragment>>;\n\ninterface QueryState {\n readonly operation: \"select\" | \"insert\" | \"update\" | \"delete\";\n readonly table?: Table | Fragment;\n readonly joins?: ReadonlyArray<{\n readonly table: Table | Fragment;\n readonly on: Fragment;\n }>;\n readonly selection?: Selection;\n readonly where?: Fragment;\n readonly values?: Row;\n readonly returning?: Selection | true;\n readonly order?: ReadonlyArray<Fragment>;\n readonly limit?: number;\n readonly lock?: boolean;\n readonly conflict?: boolean;\n}\n\nconst selectionFor = (state: QueryState): Selection =>\n state.selection ?? (state.table instanceof Table ? state.table.columns : {});\n\nconst selectionFields = (selection: Selection) => {\n const used = new Set(Object.keys(selection));\n let index = 0;\n\n return Object.entries(selection).flatMap<{\n readonly key: string;\n readonly name: string | undefined;\n readonly column: Fragment;\n readonly alias: string;\n }>(([key, value]) =>\n value instanceof Table\n ? Object.entries(value.columns).map(([name, column]) => {\n let alias: string;\n\n do alias = `auth_column_${index++}`;\n while (used.has(alias));\n used.add(alias);\n\n return { key, name, column, alias };\n })\n : [{ key, name: undefined, column: value, alias: key }],\n );\n};\n\nconst projection = (selection: Selection, compiler: Compiler) =>\n selectionFields(selection)\n .map(({ column, alias }) => `${column.render(compiler)} AS ${identifier(alias)}`)\n .join(\", \");\n\n// Joined table selections retain their own column codecs and nested row names.\nconst decodeRow = (selection: Selection, row: Row): Row => {\n const entries: Array<readonly [string, unknown]> = [];\n const groups = new Map<string, Array<readonly [string, unknown]>>();\n\n for (const field of selectionFields(selection)) {\n const value = field.column.decode(row[field.alias]);\n\n if (field.name === undefined) entries.push([field.key, value]);\n else {\n const group = groups.get(field.key) ?? [];\n\n group.push([field.name, value]);\n groups.set(field.key, group);\n }\n }\n\n return Object.fromEntries([\n ...entries,\n ...Array.from(groups, ([key, values]) => [key, Object.fromEntries(values)]),\n ]);\n};\n\nconst compile = (state: QueryState, compiler: Compiler): string => {\n const table = state.table;\n\n if (table === undefined)\n throw PersistenceMappingError.make({ operation: \"query\", cause: \"Missing table\" });\n let query: string;\n\n if (state.operation === \"select\") {\n query = `SELECT ${projection(selectionFor(state), compiler)} FROM ${table.render(compiler)}`;\n for (const join of state.joins ?? [])\n query += ` INNER JOIN ${join.table.render(compiler)} ON ${join.on.render(compiler)}`;\n } else {\n if (!(table instanceof Table))\n throw PersistenceMappingError.make({\n operation: \"query\",\n cause: \"Mutation requires a table\",\n });\n const target = table.render(compiler);\n\n const fields = Object.entries(state.values ?? {}).filter(\n ([key, value]) => table.columns[key] !== undefined && value !== undefined,\n );\n\n if (state.operation === \"insert\") {\n query = `INSERT INTO ${target} (${fields.map(([key]) => identifier(table.columns[key].options.name)).join(\", \")}) VALUES (${fields.map(([, value]) => render(value, compiler)).join(\", \")})`;\n } else if (state.operation === \"update\") {\n query = `UPDATE ${target} SET ${fields.map(([key, value]) => `${identifier(table.columns[key].options.name)} = ${render(value, compiler)}`).join(\", \")}`;\n } else query = `DELETE FROM ${target}`;\n }\n if (state.where !== undefined) query += ` WHERE ${state.where.render(compiler)}`;\n if (state.order?.length)\n query += ` ORDER BY ${state.order.map((field) => field.render(compiler)).join(\", \")}`;\n if (state.limit !== undefined) query += ` LIMIT ${render(state.limit, compiler)}`;\n if (state.conflict) query += \" ON CONFLICT DO NOTHING\";\n if (state.returning)\n query += ` RETURNING ${projection(state.returning === true && table instanceof Table ? table.columns : state.returning === true ? {} : state.returning, compiler)}`;\n if (state.lock && compiler.dialect === \"pg\") query += \" FOR UPDATE\";\n\n return query;\n};\n\n/** Captures one Effect SQL client. Its transaction service remains the physical owner. */\nexport const makeSqlDatabase = Effect.fnUntraced(function* (dialect: Dialect) {\n const client = yield* SqlClient;\n\n const query = (state: QueryState): Query => {\n const execute = Effect.suspend(() => {\n const compiler: Compiler = { dialect, parameters: [] };\n const text = compile(state, compiler);\n\n const selected: Selection =\n state.operation === \"select\"\n ? selectionFor(state)\n : state.returning === true\n ? state.table instanceof Table\n ? state.table.columns\n : {}\n : state.returning || {};\n\n return client\n .unsafe<Row>(text, compiler.parameters)\n .pipe(Effect.map((rows) => rows.map((row) => decodeRow(selected, row))));\n });\n\n return Object.assign(execute, {\n toSQL: () => {\n const compiler: Compiler = { dialect, parameters: [] };\n\n return { sql: compile(state, compiler), params: compiler.parameters };\n },\n getSQL: () => new Fragment((compiler) => compile(state, compiler)),\n from: (table: Table | Fragment) => query({ ...state, table }),\n innerJoin: (table: Table | Fragment, on: Fragment) =>\n query({ ...state, joins: [...(state.joins ?? []), { table, on }] }),\n where: (where: Fragment | undefined) => query({ ...state, where }),\n limit: (limit: number) => query({ ...state, limit }),\n orderBy: (...order: Fragment[]) => query({ ...state, order }),\n for: (_lock: \"update\") => query({ ...state, lock: true }),\n values: (values: Row) => query({ ...state, values }),\n set: (values: Row) => query({ ...state, values }),\n returning: (returning: Selection | true = true) => query({ ...state, returning }),\n onConflictDoNothing: () => query({ ...state, conflict: true }),\n });\n };\n\n const database: SqlDatabase = {\n $client: client,\n select: (selection?: Selection) => query({ operation: \"select\", selection }),\n insert: (table: Table) => query({ operation: \"insert\", table }),\n update: (table: Table) => query({ operation: \"update\", table }),\n delete: (table: Table) => query({ operation: \"delete\", table }),\n transaction: <A, E, R>(\n body: (database: SqlDatabase) => Effect.Effect<A, E, R>,\n ): Effect.Effect<A, E | SqlError, R> =>\n client.withTransaction(Effect.suspend(() => body(database))),\n };\n\n return database;\n});\n\ninterface Query extends Effect.Effect<Row[], SqlError> {\n toSQL(): { readonly sql: string; readonly params: ReadonlyArray<unknown> };\n getSQL(): Fragment;\n from(table: Table | Fragment): Query;\n innerJoin(table: Table | Fragment, on: Fragment): Query;\n where(where: Fragment | undefined): Query;\n limit(limit: number): Query;\n orderBy(...order: Fragment[]): Query;\n for(lock: \"update\"): Query;\n values(values: Row): Query;\n set(values: Row): Query;\n returning(returning?: Selection | true): Query;\n onConflictDoNothing(): Query;\n}\n\nexport interface SqlDatabase {\n readonly $client: SqlClient;\n select(selection?: Selection): Query;\n insert(table: Table): Query;\n update(table: Table): Query;\n delete(table: Table): Query;\n transaction<A, E, R>(\n body: (database: SqlDatabase) => Effect.Effect<A, E, R>,\n ): Effect.Effect<A, E | SqlError, R>;\n}\n\nexport const sql = template;\n"],"mappings":"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"}
@@ -1,9 +1,7 @@
1
1
  import { Context, Effect } from "effect";
2
- import * as SqlClient from "effect/sql/SqlClient";
2
+ import * as SqlClient$1 from "effect/sql/SqlClient";
3
3
  //#region src/internal/standalone.d.ts
4
4
  /** Standalone operations must observe their own durable commit. */
5
- export declare const requireStandalone: <Failure>(unavailable: () => Failure, client?: {
6
- readonly transactionService: Context.Key<SqlClient.TransactionConnection, SqlClient.TransactionConnection.Service>;
7
- }) => Effect.Effect<void, Failure>;
5
+ export declare const requireStandalone: <Failure>(unavailable: () => Failure, transactionService?: Context.Key<SqlClient$1.TransactionConnection, SqlClient$1.TransactionConnection.Service>) => Effect.Effect<void, Failure>;
8
6
  //#endregion
9
7
  //# sourceMappingURL=standalone.d.mts.map
@@ -1,9 +1,9 @@
1
1
  import { Effect } from "effect";
2
2
  //#region src/internal/standalone.ts
3
3
  /** Standalone operations must observe their own durable commit. */
4
- const requireStandalone = (unavailable, client) => Effect.withFiber((fiber) => {
4
+ const requireStandalone = (unavailable, transactionService) => Effect.withFiber((fiber) => {
5
5
  const services = fiber.context.mapUnsafe;
6
- if (client !== void 0 && services.has(client.transactionService.key)) return Effect.fail(unavailable());
6
+ if (transactionService !== void 0 && services.has(transactionService.key)) return Effect.fail(unavailable());
7
7
  for (const key of services.keys()) if (key.startsWith("@effect/sql-libsql/LibsqlClient/LibsqlTransaction/")) return Effect.fail(unavailable());
8
8
  return Effect.void;
9
9
  });
@@ -1 +1 @@
1
- {"version":3,"file":"standalone.mjs","names":[],"sources":["../../src/internal/standalone.ts"],"sourcesContent":["import { type Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/sql/SqlClient\";\n\n/** Standalone operations must observe their own durable commit. */\nexport const requireStandalone = <Failure>(\n unavailable: () => Failure,\n client?: {\n readonly transactionService: Context.Key<\n SqlClient.TransactionConnection,\n SqlClient.TransactionConnection.Service\n >;\n },\n): Effect.Effect<void, Failure> =>\n Effect.withFiber((fiber) => {\n const services = fiber.context.mapUnsafe;\n\n if (client !== undefined && services.has(client.transactionService.key))\n return Effect.fail(unavailable());\n\n // rc.117 libSQL uses a private per-client marker that is not exposed through\n // SqlClient.transactionService. Conservatively reject every ambient libSQL\n // transaction, including another client's, before issuing standalone writes.\n // Recheck this upstream detail whenever the Effect driver is upgraded.\n for (const key of services.keys()) {\n if (key.startsWith(\"@effect/sql-libsql/LibsqlClient/LibsqlTransaction/\"))\n return Effect.fail(unavailable());\n }\n\n return Effect.void;\n });\n"],"mappings":";;;AAIA,MAAa,qBACX,aACA,WAOA,OAAO,WAAW,UAAU;CAC1B,MAAM,WAAW,MAAM,QAAQ;CAE/B,IAAI,WAAW,KAAA,KAAa,SAAS,IAAI,OAAO,mBAAmB,GAAG,GACpE,OAAO,OAAO,KAAK,YAAY,CAAC;CAMlC,KAAK,MAAM,OAAO,SAAS,KAAK,GAC9B,IAAI,IAAI,WAAW,oDAAoD,GACrE,OAAO,OAAO,KAAK,YAAY,CAAC;CAGpC,OAAO,OAAO;AAChB,CAAC"}
1
+ {"version":3,"file":"standalone.mjs","names":[],"sources":["../../src/internal/standalone.ts"],"sourcesContent":["import { type Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/sql/SqlClient\";\n\n/** Standalone operations must observe their own durable commit. */\nexport const requireStandalone = <Failure>(\n unavailable: () => Failure,\n transactionService?: Context.Key<\n SqlClient.TransactionConnection,\n SqlClient.TransactionConnection.Service\n >,\n): Effect.Effect<void, Failure> =>\n Effect.withFiber((fiber) => {\n const services = fiber.context.mapUnsafe;\n\n if (transactionService !== undefined && services.has(transactionService.key))\n return Effect.fail(unavailable());\n\n // rc.117 libSQL uses a private per-client marker that is not exposed through\n // SqlClient.transactionService. Conservatively reject every ambient libSQL\n // transaction, including another client's, before issuing standalone writes.\n // Recheck this upstream detail whenever the Effect driver is upgraded.\n for (const key of services.keys()) {\n if (key.startsWith(\"@effect/sql-libsql/LibsqlClient/LibsqlTransaction/\"))\n return Effect.fail(unavailable());\n }\n\n return Effect.void;\n });\n"],"mappings":";;;AAIA,MAAa,qBACX,aACA,uBAKA,OAAO,WAAW,UAAU;CAC1B,MAAM,WAAW,MAAM,QAAQ;CAE/B,IAAI,uBAAuB,KAAA,KAAa,SAAS,IAAI,mBAAmB,GAAG,GACzE,OAAO,OAAO,KAAK,YAAY,CAAC;CAMlC,KAAK,MAAM,OAAO,SAAS,KAAK,GAC9B,IAAI,IAAI,WAAW,oDAAoD,GACrE,OAAO,OAAO,KAAK,YAAY,CAAC;CAGpC,OAAO,OAAO;AAChB,CAAC"}
@@ -1,15 +1,15 @@
1
1
  import { PersistenceMappingError } from "./mapping-error.mjs";
2
+ import { requiredProofConstraints } from "./models/proof-model.mjs";
2
3
  import { storageTables } from "./storage-tables.mjs";
3
4
  import { requiredEmailAddressConstraints } from "./models/email-model.mjs";
4
- import { requiredProofConstraints } from "./models/proof-model.mjs";
5
5
  import { requiredPasswordConstraints } from "./models/password-model.mjs";
6
6
  import { DateTime, Effect, Redacted, Schema } from "effect";
7
7
  import { randomId } from "@yielded/auth-crypto";
8
8
  import { TokenDigest } from "@yielded/auth/Schema";
9
9
  import { SecurityRevision, SessionAuthenticationProvenance, SessionCredentialVersion, SessionId, SessionMetadata } from "@yielded/auth/Sessions";
10
+ import { ProofBinding, ProofContinuationId, ProofDeliveryId, ProofId, ProofPurpose, ProofRequestId, ProofRequestReceipt, ProofVersion } from "@yielded/auth/Proofs";
10
11
  import { EmailCredentialSnapshot } from "@yielded/auth/Email";
11
12
  import { EncodedPasswordHash, PasswordAttemptId, PasswordCredentialSnapshot } from "@yielded/auth/Password";
12
- import { ProofBinding, ProofContinuationId, ProofDeliveryId, ProofId, ProofPurpose, ProofRequestId, ProofRequestReceipt, ProofVersion } from "@yielded/auth/Proofs";
13
13
  import { LoginIdentifier } from "@yielded/auth/Identity";
14
14
  //#region src/internal/storage-mapping.ts
15
15
  const failure = (cause) => PersistenceMappingError.make({
@@ -1,5 +1,6 @@
1
1
  import { PersistenceConfigurationError } from "./configuration.mjs";
2
2
  import { Effect, Schema } from "effect";
3
+ import { SqlClient } from "effect/sql/SqlClient";
3
4
  //#region src/internal/storage-validation.ts
4
5
  const Column = Schema.Struct({
5
6
  name: Schema.String,
@@ -17,7 +18,8 @@ const quote = (value) => "\"" + value.replaceAll("\"", "\"\"") + "\"";
17
18
  const qualifiedTableName = (table) => (table.schema === void 0 ? "" : quote(table.schema) + ".") + quote(table.name);
18
19
  /** Validate physical uniqueness before the first authentication operation. A
19
20
  * partial or expression index cannot establish these unconditional guarantees. */
20
- const validateStorage = (client, dialect, table, required) => Effect.gen(function* () {
21
+ const validateStorage = Effect.fnUntraced(function* (dialect, table, required) {
22
+ const client = yield* SqlClient;
21
23
  const physicalKeys = yield* dialect === "sqlite" ? Effect.gen(function* () {
22
24
  if (table.schema !== void 0 && table.schema !== "main") return yield* PersistenceConfigurationError.make({ reason: "SQLite storage must belong to the main database" });
23
25
  const columns = yield* client`select name, pk from pragma_table_info(${table.name})`.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(Column))));
@@ -51,7 +53,7 @@ const validateStorage = (client, dialect, table, required) => Effect.gen(functio
51
53
  const columns = keys.map((key) => table.columns[key]?.name);
52
54
  if (columns.some((column) => column === void 0) || !physicalKeys.some((actual) => actual.length === columns.length && actual.every((column) => columns.includes(column)))) return yield* PersistenceConfigurationError.make({ reason: `Missing unique key on ${table.name} (${keys.join(", ")})` });
53
55
  }
54
- }).pipe(Effect.mapError((error) => Schema.is(PersistenceConfigurationError)(error) ? error : PersistenceConfigurationError.make({ reason: `Cannot validate SQL storage for ${table.name}` })));
56
+ }, (effect, _dialect, table) => effect.pipe(Effect.mapError((error) => Schema.is(PersistenceConfigurationError)(error) ? error : PersistenceConfigurationError.make({ reason: `Cannot validate SQL storage for ${table.name}` }))));
55
57
  //#endregion
56
58
  export { qualifiedTableName, validateStorage };
57
59
 
@@ -1 +1 @@
1
- {"version":3,"file":"storage-validation.mjs","names":[],"sources":["../../src/internal/storage-validation.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\";\nimport type { 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\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 = (\n client: SqlClient,\n dialect: \"pg\" | \"sqlite\",\n table: StorageTable,\n required: ReadonlyArray<ReadonlyArray<string>>,\n) =>\n Effect.gen(function* () {\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 : 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 (actual) =>\n actual.length === columns.length && 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 }).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"],"mappings":"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"}
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\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\" | \"sqlite\",\n table: StorageTable,\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 : 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 (actual) =>\n actual.length === columns.length && 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,15 +1,16 @@
1
- import { TransactionNativeDatabase, TransactionOwner, makeTransactionKernel } from "./transaction-kernel.mjs";
1
+ import { NativeDatabase, TransactionNativeDatabase, TransactionOwner, makeTransactionKernel } from "./transaction-kernel.mjs";
2
2
  import { Context, Effect } from "effect";
3
3
  import { CurrentCommitJournal, HookConfigurationError, LifecycleHooks } from "@yielded/auth/Hooks";
4
+ import * as SqlClient$1 from "effect/sql/SqlClient";
4
5
  import { Statement } from "effect/sql/Statement";
5
- import * as SqlClient from "effect/sql/SqlClient";
6
6
  import * as SqlError$1 from "effect/sql/SqlError";
7
7
  //#region src/internal/transaction-execution-kernel.d.ts
8
8
  export interface TransactionTargetConfiguration<Failure> {
9
9
  readonly mode: "interactive" | "synchronous" | "batch";
10
10
  readonly dialect: "pg" | "mysql" | "sqlite";
11
11
  readonly locking: boolean;
12
- readonly standaloneGuard: (database: any) => Effect.Effect<void, Failure>;
12
+ /** The captured database's transaction marker; batch drivers may omit it. */
13
+ readonly standaloneGuard: (transactionService: SqlClient$1.SqlClient["transactionService"] | undefined) => Effect.Effect<void, Failure>;
13
14
  readonly transaction?: <A, E, R>(database: any, body: (transaction: any) => Effect.Effect<A, E, R>) => Effect.Effect<A, E | SqlError$1.SqlError | Failure, R>;
14
15
  }
15
16
  export type TransactionCoordinatorError<E, Failure> = E | Failure | HookConfigurationError | SqlError$1.SqlError;
@@ -28,10 +29,8 @@ export interface TransactionExecution<Failure, OwnerId, Requirements = Lifecycle
28
29
  readonly run: <A, E, R>(body: Effect.Effect<A, E, R>, mutation?: boolean) => Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | Requirements>;
29
30
  }
30
31
  export declare const makeTransactionExecutionKernel: (transactions: Pick<ReturnType<typeof makeTransactionKernel>, "makeTransactionOwner" | "reportTransactionFailure">) => {
31
- sqlClientTransactionStandaloneGuard: <Failure>(unavailable: () => Failure, database: {
32
- readonly $client: Pick<SqlClient.SqlClient, "transactionService">;
33
- }) => Effect.Effect<void, Failure, never>;
34
- makeTransactionExecution: <Failure, OwnerId>(ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>, database: TransactionNativeDatabase, configuration: TransactionTargetConfiguration<Failure>, unavailable: () => Failure, nonce: () => string, bound?: TransactionBound<Failure>) => TransactionExecution<Failure, OwnerId>;
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>;
35
34
  coordinateTransactionOwner: <Failure, OwnerId, Resources, AllocationError, Services, Transaction, A, E, R>(database: TransactionNativeDatabase, ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>, configuration: TransactionTargetConfiguration<Failure>, allocate: Effect.Effect<Resources, AllocationError>, 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>;
36
35
  };
37
36
  //#endregion
@@ -1,11 +1,13 @@
1
1
  import { requireStandalone } from "./standalone.mjs";
2
+ import { NativeDatabase } from "./transaction-kernel.mjs";
2
3
  import { Context, Effect } from "effect";
3
4
  import { CurrentCommitJournal, coordinateCommit, hasCommitScope } from "@yielded/auth/Hooks";
4
5
  //#region src/internal/transaction-execution-kernel.ts
5
6
  const makeTransactionExecutionKernel = (transactions) => {
6
7
  const { makeTransactionOwner, reportTransactionFailure } = transactions;
7
- const sqlClientTransactionStandaloneGuard = (unavailable, database) => requireStandalone(unavailable, database.$client);
8
- const makeTransactionExecution = (ownerTag, database, configuration, unavailable, nonce, bound) => {
8
+ const sqlClientTransactionStandaloneGuard = (unavailable, transactionService) => requireStandalone(unavailable, transactionService);
9
+ const makeTransactionExecution = Effect.fnUntraced(function* (ownerTag, configuration, unavailable, nonce, bound) {
10
+ const database = yield* NativeDatabase;
9
11
  const invariant = (value) => {
10
12
  if (!value) throw unavailable();
11
13
  };
@@ -14,7 +16,7 @@ const makeTransactionExecutionKernel = (transactions) => {
14
16
  active: () => bound === void 0 || bound.active,
15
17
  admit: bound !== void 0 ? Effect.suspend(() => bound.active ? Effect.void : Effect.fail(unavailable())) : Effect.gen(function* () {
16
18
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
17
- yield* configuration.standaloneGuard(database);
19
+ yield* configuration.standaloneGuard(database.$client.transactionService);
18
20
  }),
19
21
  poison: () => {
20
22
  if (bound !== void 0) bound.poisoned = true;
@@ -41,7 +43,7 @@ const makeTransactionExecutionKernel = (transactions) => {
41
43
  if (exit._tag === "Failure") bound.poisoned = true;
42
44
  }))) : Effect.gen(function* () {
43
45
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
44
- yield* configuration.standaloneGuard(database);
46
+ yield* configuration.standaloneGuard(database.$client.transactionService);
45
47
  const marker = nonce();
46
48
  return (yield* coordinateCommit((journal) => {
47
49
  const run = (tx) => Effect.gen(function* () {
@@ -63,11 +65,11 @@ const makeTransactionExecutionKernel = (transactions) => {
63
65
  });
64
66
  }
65
67
  };
66
- };
68
+ });
67
69
  const coordinateTransactionOwner = (database, ownerTag, configuration, allocate, unavailable, nonce, services, owner) => {
68
70
  return Effect.gen(function* () {
69
71
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
70
- yield* configuration.standaloneGuard(database);
72
+ yield* configuration.standaloneGuard(database.$client.transactionService);
71
73
  const resources = yield* allocate.pipe((operation) => reportTransactionFailure(operation, unavailable), Effect.mapError(unavailable), Effect.catchDefect(() => Effect.fail(unavailable())));
72
74
  const marker = nonce();
73
75
  return (yield* coordinateCommit((journal) => {
@@ -87,7 +89,7 @@ const makeTransactionExecutionKernel = (transactions) => {
87
89
  inFlight: 0
88
90
  };
89
91
  return yield* Effect.gen(function* () {
90
- const value = yield* owner(tx, services(makeTransactionExecution(ownerTag, tx, configuration, unavailable, nonce, bound), resources), (statement) => {
92
+ const value = yield* owner(tx, services(yield* makeTransactionExecution(ownerTag, configuration, unavailable, nonce, bound).pipe(Effect.provideService(NativeDatabase, tx)), resources), (statement) => {
91
93
  if (!bound.active) throw unavailable();
92
94
  state.statements.push(statement);
93
95
  });
@@ -1 +1 @@
1
- {"version":3,"file":"transaction-execution-kernel.mjs","names":[],"sources":["../../src/internal/transaction-execution-kernel.ts"],"sourcesContent":["import {\n coordinateCommit,\n hasCommitScope,\n CurrentCommitJournal,\n type LifecycleHooks,\n type HookConfigurationError,\n} from \"@yielded/auth/Hooks\";\n/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */\n/* oxlint-disable no-explicit-any -- private execution bridge retains exact public driver wrappers. */\nimport { Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/sql/SqlClient\";\nimport type * as SqlError from \"effect/sql/SqlError\";\nimport type { Statement } from \"effect/sql/Statement\";\n\nimport { requireStandalone } from \"./standalone\";\nimport {\n type TransactionNativeDatabase,\n type TransactionOwner,\n type makeTransactionKernel,\n} from \"./transaction-kernel\";\n\nexport interface TransactionTargetConfiguration<Failure> {\n readonly mode: \"interactive\" | \"synchronous\" | \"batch\";\n readonly dialect: \"pg\" | \"mysql\" | \"sqlite\";\n readonly locking: boolean;\n readonly standaloneGuard: (database: any) => Effect.Effect<void, Failure>;\n readonly transaction?: <A, E, R>(\n database: any,\n body: (transaction: any) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SqlError.SqlError | Failure, R>;\n}\n\nexport type TransactionCoordinatorError<E, Failure> =\n | E\n | Failure\n | HookConfigurationError\n | SqlError.SqlError;\n\nexport interface TransactionBound<Failure> {\n readonly owner: TransactionOwner<Failure>;\n used: boolean;\n poisoned: boolean;\n active: boolean;\n inFlight: number;\n}\n\nexport interface TransactionExecution<Failure, OwnerId, Requirements = LifecycleHooks> {\n readonly bound: boolean;\n readonly active: () => boolean;\n readonly admit: Effect.Effect<void, Failure>;\n readonly poison: () => void;\n readonly run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation?: boolean,\n ) => Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | Requirements>;\n}\n\nexport const makeTransactionExecutionKernel = (\n transactions: Pick<\n ReturnType<typeof makeTransactionKernel>,\n \"makeTransactionOwner\" | \"reportTransactionFailure\"\n >,\n) => {\n const { makeTransactionOwner, reportTransactionFailure } = transactions;\n\n const sqlClientTransactionStandaloneGuard = <Failure>(\n unavailable: () => Failure,\n database: {\n readonly $client: Pick<SqlClient.SqlClient, \"transactionService\">;\n },\n ) => requireStandalone(unavailable, database.$client);\n\n const makeTransactionExecution = <Failure, OwnerId>(\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n database: TransactionNativeDatabase,\n configuration: TransactionTargetConfiguration<Failure>,\n unavailable: () => Failure,\n nonce: () => string,\n bound?: TransactionBound<Failure>,\n ): TransactionExecution<Failure, OwnerId> => {\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n return {\n bound: bound !== undefined,\n active: () => bound === undefined || bound.active,\n admit:\n bound !== undefined\n ? Effect.suspend(() => (bound.active ? Effect.void : Effect.fail(unavailable())))\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n }),\n poison: () => {\n if (bound !== undefined) bound.poisoned = true;\n },\n run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation = true,\n ): Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | LifecycleHooks> => {\n return Effect.suspend(() => {\n let entered = false;\n\n const work =\n bound !== undefined\n ? Effect.gen(function* () {\n if (!bound.active) return yield* Effect.fail(unavailable());\n entered = true;\n bound.inFlight++;\n if (!mutation)\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, bound.owner).pipe(\n Context.add(CurrentCommitJournal, bound.owner.journal),\n ),\n );\n invariant(!bound.used && !bound.poisoned);\n bound.used = true;\n\n const result = yield* coordinateCommit(\n (journal) =>\n Effect.gen(function* () {\n bound.owner.guards.push(journal.prepare(undefined));\n\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, { ...bound.owner, journal }).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n }),\n { mode: configuration.mode },\n );\n\n return result.value;\n }).pipe(\n Effect.onExit((exit) =>\n Effect.sync(() => {\n if (entered) bound.inFlight--;\n if (exit._tag === \"Failure\") bound.poisoned = true;\n }),\n ),\n )\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const owner = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n locking: configuration.locking,\n mysql: configuration.dialect === \"mysql\",\n dialect: configuration.dialect,\n });\n\n const value = yield* Effect.provideContext(\n body,\n Context.make(ownerTag, owner).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n\n yield* owner.finish();\n if (configuration.mode === \"batch\")\n yield* tx.$client.batch(owner.statements);\n\n return value;\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value;\n });\n\n return work.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n });\n },\n };\n };\n\n const coordinateTransactionOwner = <\n Failure,\n OwnerId,\n Resources,\n AllocationError,\n Services,\n Transaction,\n A,\n E,\n R,\n >(\n database: TransactionNativeDatabase,\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n configuration: TransactionTargetConfiguration<Failure>,\n allocate: Effect.Effect<Resources, AllocationError>,\n unavailable: () => Failure,\n nonce: () => string,\n services: (execution: TransactionExecution<Failure, OwnerId>, resources: Resources) => Services,\n owner: (\n transaction: Transaction,\n services: Services,\n append: (statement: Statement<any>) => void,\n ) => Effect.Effect<A, E, R>,\n ): Effect.Effect<\n A,\n TransactionCoordinatorError<E, Failure>,\n Exclude<R, CurrentCommitJournal> | LifecycleHooks\n > => {\n return Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n\n // Allocation is independent of registration data and always precedes the physical owner.\n const resources = yield* allocate.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const state = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n dialect: configuration.dialect,\n mysql: configuration.dialect === \"mysql\",\n locking: configuration.locking,\n });\n\n const bound: TransactionBound<Failure> = {\n owner: state,\n used: false,\n poisoned: false,\n active: true,\n inFlight: 0,\n };\n\n return yield* Effect.gen(function* () {\n const value = yield* owner(\n tx as Transaction,\n services(\n makeTransactionExecution(\n ownerTag,\n tx,\n configuration,\n unavailable,\n nonce,\n bound,\n ),\n resources,\n ),\n (statement) => {\n if (!bound.active) throw unavailable();\n state.statements.push(statement);\n },\n );\n\n bound.active = false;\n if (bound.poisoned || bound.inFlight !== 0)\n return yield* Effect.fail(unavailable());\n yield* state.finish();\n if (configuration.mode === \"batch\") yield* tx.$client.batch(state.statements);\n\n return value;\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n bound.active = false;\n }),\n ),\n );\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value as A;\n });\n };\n\n return {\n sqlClientTransactionStandaloneGuard,\n makeTransactionExecution,\n coordinateTransactionOwner,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"transaction-execution-kernel.mjs","names":[],"sources":["../../src/internal/transaction-execution-kernel.ts"],"sourcesContent":["import {\n coordinateCommit,\n hasCommitScope,\n CurrentCommitJournal,\n type LifecycleHooks,\n type HookConfigurationError,\n} from \"@yielded/auth/Hooks\";\n/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */\n/* oxlint-disable no-explicit-any -- private execution bridge retains exact public driver wrappers. */\nimport { Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/sql/SqlClient\";\nimport type * as SqlError from \"effect/sql/SqlError\";\nimport type { Statement } from \"effect/sql/Statement\";\n\nimport { requireStandalone } from \"./standalone\";\nimport {\n NativeDatabase,\n type TransactionNativeDatabase,\n type TransactionOwner,\n type makeTransactionKernel,\n} from \"./transaction-kernel\";\n\nexport interface TransactionTargetConfiguration<Failure> {\n readonly mode: \"interactive\" | \"synchronous\" | \"batch\";\n readonly dialect: \"pg\" | \"mysql\" | \"sqlite\";\n readonly locking: boolean;\n /** The captured database's transaction marker; batch drivers may omit it. */\n readonly standaloneGuard: (\n transactionService: SqlClient.SqlClient[\"transactionService\"] | undefined,\n ) => Effect.Effect<void, Failure>;\n readonly transaction?: <A, E, R>(\n database: any,\n body: (transaction: any) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SqlError.SqlError | Failure, R>;\n}\n\nexport type TransactionCoordinatorError<E, Failure> =\n | E\n | Failure\n | HookConfigurationError\n | SqlError.SqlError;\n\nexport interface TransactionBound<Failure> {\n readonly owner: TransactionOwner<Failure>;\n used: boolean;\n poisoned: boolean;\n active: boolean;\n inFlight: number;\n}\n\nexport interface TransactionExecution<Failure, OwnerId, Requirements = LifecycleHooks> {\n readonly bound: boolean;\n readonly active: () => boolean;\n readonly admit: Effect.Effect<void, Failure>;\n readonly poison: () => void;\n readonly run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation?: boolean,\n ) => Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | Requirements>;\n}\n\nexport const makeTransactionExecutionKernel = (\n transactions: Pick<\n ReturnType<typeof makeTransactionKernel>,\n \"makeTransactionOwner\" | \"reportTransactionFailure\"\n >,\n) => {\n const { makeTransactionOwner, reportTransactionFailure } = transactions;\n\n const sqlClientTransactionStandaloneGuard = <Failure>(\n unavailable: () => Failure,\n transactionService: SqlClient.SqlClient[\"transactionService\"] | undefined,\n ) => requireStandalone(unavailable, transactionService);\n\n const makeTransactionExecution = Effect.fnUntraced(function* <Failure, OwnerId>(\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n configuration: TransactionTargetConfiguration<Failure>,\n unavailable: () => Failure,\n nonce: () => string,\n bound?: TransactionBound<Failure>,\n ): Effect.fn.Return<TransactionExecution<Failure, OwnerId>, never, NativeDatabase> {\n const database = yield* NativeDatabase;\n\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n return {\n bound: bound !== undefined,\n active: () => bound === undefined || bound.active,\n admit:\n bound !== undefined\n ? Effect.suspend(() => (bound.active ? Effect.void : Effect.fail(unavailable())))\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database.$client.transactionService);\n }),\n poison: () => {\n if (bound !== undefined) bound.poisoned = true;\n },\n run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation = true,\n ): Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | LifecycleHooks> => {\n return Effect.suspend(() => {\n let entered = false;\n\n const work =\n bound !== undefined\n ? Effect.gen(function* () {\n if (!bound.active) return yield* Effect.fail(unavailable());\n entered = true;\n bound.inFlight++;\n if (!mutation)\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, bound.owner).pipe(\n Context.add(CurrentCommitJournal, bound.owner.journal),\n ),\n );\n invariant(!bound.used && !bound.poisoned);\n bound.used = true;\n\n const result = yield* coordinateCommit(\n (journal) =>\n Effect.gen(function* () {\n bound.owner.guards.push(journal.prepare(undefined));\n\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, { ...bound.owner, journal }).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n }),\n { mode: configuration.mode },\n );\n\n return result.value;\n }).pipe(\n Effect.onExit((exit) =>\n Effect.sync(() => {\n if (entered) bound.inFlight--;\n if (exit._tag === \"Failure\") bound.poisoned = true;\n }),\n ),\n )\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database.$client.transactionService);\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const owner = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n locking: configuration.locking,\n mysql: configuration.dialect === \"mysql\",\n dialect: configuration.dialect,\n });\n\n const value = yield* Effect.provideContext(\n body,\n Context.make(ownerTag, owner).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n\n yield* owner.finish();\n if (configuration.mode === \"batch\")\n yield* tx.$client.batch(owner.statements);\n\n return value;\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value;\n });\n\n return work.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n });\n },\n };\n });\n\n const coordinateTransactionOwner = <\n Failure,\n OwnerId,\n Resources,\n AllocationError,\n Services,\n Transaction,\n A,\n E,\n R,\n >(\n database: TransactionNativeDatabase,\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n configuration: TransactionTargetConfiguration<Failure>,\n allocate: Effect.Effect<Resources, AllocationError>,\n unavailable: () => Failure,\n nonce: () => string,\n services: (execution: TransactionExecution<Failure, OwnerId>, resources: Resources) => Services,\n owner: (\n transaction: Transaction,\n services: Services,\n append: (statement: Statement<any>) => void,\n ) => Effect.Effect<A, E, R>,\n ): Effect.Effect<\n A,\n TransactionCoordinatorError<E, Failure>,\n Exclude<R, CurrentCommitJournal> | LifecycleHooks\n > => {\n return Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database.$client.transactionService);\n\n // Allocation is independent of registration data and always precedes the physical owner.\n const resources = yield* allocate.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const state = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n dialect: configuration.dialect,\n mysql: configuration.dialect === \"mysql\",\n locking: configuration.locking,\n });\n\n const bound: TransactionBound<Failure> = {\n owner: state,\n used: false,\n poisoned: false,\n active: true,\n inFlight: 0,\n };\n\n return yield* Effect.gen(function* () {\n const value = yield* owner(\n tx as Transaction,\n services(\n yield* makeTransactionExecution(\n ownerTag,\n configuration,\n unavailable,\n nonce,\n bound,\n ).pipe(Effect.provideService(NativeDatabase, tx)),\n resources,\n ),\n (statement) => {\n if (!bound.active) throw unavailable();\n state.statements.push(statement);\n },\n );\n\n bound.active = false;\n if (bound.poisoned || bound.inFlight !== 0)\n return yield* Effect.fail(unavailable());\n yield* state.finish();\n if (configuration.mode === \"batch\") yield* tx.$client.batch(state.statements);\n\n return value;\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n bound.active = false;\n }),\n ),\n );\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value as A;\n });\n };\n\n return {\n sqlClientTransactionStandaloneGuard,\n makeTransactionExecution,\n coordinateTransactionOwner,\n };\n};\n"],"mappings":"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"}
@@ -1,18 +1,22 @@
1
1
  import { QueryOperations, SqlColumn, SqlExpression, SqlFragment } from "./query-operations.mjs";
2
- import { Effect } from "effect";
2
+ import { Context, Effect } from "effect";
3
3
  import { CommitJournal, PreparedCommit } from "@yielded/auth/Hooks";
4
+ import * as SqlClient$1 from "effect/sql/SqlClient";
4
5
  import { Statement } from "effect/sql/Statement";
5
- import * as SqlClient from "effect/sql/SqlClient";
6
6
  import { SqlError } from "effect/sql/SqlError";
7
7
  //#region src/internal/transaction-kernel.d.ts
8
8
  type Table = object;
9
9
  export type Row = Record<string, any>;
10
10
  export interface TransactionNativeDatabase {
11
- readonly $client: SqlClient.SqlClient & {
11
+ readonly $client: SqlClient$1.SqlClient & {
12
12
  readonly batch: (statements: ReadonlyArray<Statement<any>>) => Effect.Effect<any, SqlError>;
13
13
  };
14
14
  readonly transaction: <A, E, R>(body: (transaction: any) => Effect.Effect<A, E, R>) => Effect.Effect<A, E | SqlError, R>;
15
15
  }
16
+ declare const NativeDatabase_base: Context.ServiceClass<NativeDatabase, "effect-auth/persistence/NativeDatabase", TransactionNativeDatabase>;
17
+ /** Root native database required only while constructing standalone services.
18
+ * Active transactions remain owned by their coordinator and journal. */
19
+ export declare class NativeDatabase extends NativeDatabase_base {}
16
20
  export interface Observation {
17
21
  readonly table: Table;
18
22
  readonly where: SqlExpression;
@@ -1,7 +1,10 @@
1
- import { Cause, Effect } from "effect";
1
+ import { Cause, Context, Effect } from "effect";
2
2
  import "@yielded/auth/Hooks";
3
3
  import { reportAuthFailure } from "@yielded/auth/Persistence";
4
4
  //#region src/internal/transaction-kernel.ts
5
+ /** Root native database required only while constructing standalone services.
6
+ * Active transactions remain owned by their coordinator and journal. */
7
+ var NativeDatabase = class extends Context.Service()("effect-auth/persistence/NativeDatabase") {};
5
8
  const makeTransactionKernel = (operations) => {
6
9
  const { eq, getTableColumns, isNull, sql, balancedD1And, compactD1GeneratedStatement } = operations;
7
10
  const isUnavailableCause = (cause, unavailable) => {
@@ -255,6 +258,6 @@ const makeTransactionKernel = (operations) => {
255
258
  };
256
259
  };
257
260
  //#endregion
258
- export { makeTransactionKernel };
261
+ export { NativeDatabase, makeTransactionKernel };
259
262
 
260
263
  //# sourceMappingURL=transaction-kernel.mjs.map