{"version":3,"file":"storage-validation.mjs","names":[],"sources":["../../src/internal/storage-validation.ts"],"sourcesContent":["import { reportPersistenceFailure } from \"@yielded/auth/Persistence\";\nimport * as Cause from \"effect/Cause\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Schema from \"effect/Schema\";\nimport * as SqlClient from \"effect/sql/SqlClient\";\n\nimport { PersistenceConfigurationError } from \"./configuration\";\nimport type { PhysicalTextColumn } from \"./native-sql-table\";\nimport type { StorageTable } from \"./storage-tables\";\n\nconst Columns = Schema.Array(Schema.String);\nconst Keys = Schema.Array(Columns);\n\nconst TextColumns = Schema.Array(\n  Schema.Struct({ name: Schema.String, type: Schema.String, collation: Schema.String }),\n);\n\nconst Metadata = Schema.Struct({\n  relation: Schema.String,\n  columns: Columns,\n  keys: Keys,\n  textColumns: TextColumns,\n});\n\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    textColumns: Schema.fromJsonString(TextColumns),\n  }),\n);\n\nconst MysqlColumns = Schema.Array(\n  Schema.Struct({\n    relation: Schema.String,\n    name: Schema.String,\n    type: Schema.String,\n    collation: Schema.NullOr(Schema.String),\n  }),\n);\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.Finite),\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.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\ntype Dialect = \"pg\" | \"mysql\" | \"sqlite\";\n\n/** Share physical type, collation and key observations only during acquisition. */\nconst readStorageMetadata = Effect.fnUntraced(\n  function* (\n    dialect: Dialect,\n    physical: ReadonlyArray<Pick<PhysicalStorageTable, \"name\" | \"schema\">>,\n  ) {\n    if (\n      dialect === \"sqlite\" &&\n      physical.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 captured = yield* SqlClient.SqlClient;\n    const client = captured.withoutTransforms();\n    const cache = yield* CurrentMetadata;\n\n    const byRelation = cache?.active\n      ? (cache.clients.get(captured) ?? new Map<string, typeof Metadata.Type>())\n      : new Map<string, typeof Metadata.Type>();\n\n    if (cache?.active) cache.clients.set(captured, byRelation);\n\n    const tables = new Map(physical.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            (select json_group_array(json_object('name', c.name, 'type', 'TEXT', 'collation', 'BINARY'))\n              from pragma_table_info(requested.name) c\n              join sqlite_schema s on s.name = requested.name and s.type = 'table'\n              where upper(c.type) = 'TEXT' and instr(lower(s.sql), 'collate') = 0) as \"textColumns\"\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            c.data_type as type, c.collation_name as collation\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                  textColumns: columns.flatMap((column) =>\n                    column.relation === relation &&\n                    column.collation !== null &&\n                    [\"varchar\", \"text\", \"tinytext\", \"mediumtext\", \"longtext\"].includes(column.type)\n                      ? [{ name: column.name, type: column.type, collation: column.collation }]\n                      : [],\n                  ),\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        coalesce((select jsonb_agg(jsonb_build_object(\n          'name', a.attname::text, 'type', a.atttypid::text, 'collation', a.attcollation::text))\n          from pg_attribute a where a.attrelid = to_regclass(requested.relation)\n            and a.attnum > 0 and not a.attisdropped\n            and a.atttypid in ('text'::regtype, 'varchar'::regtype)), '[]'::jsonb) as \"textColumns\"\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    return byRelation;\n  },\n  (effect) =>\n    reportPersistenceFailure(effect, Schema.is(PersistenceConfigurationError), {\n      markReported: true,\n    }),\n  Effect.catchCause((cause) =>\n    Effect.failCause(\n      Cause.map(cause, (error) =>\n        Schema.is(PersistenceConfigurationError)(error)\n          ? error\n          : PersistenceConfigurationError.make({ reason: \"Cannot validate SQL storage metadata\" }),\n      ),\n    ),\n  ),\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(function* (\n  dialect: Dialect,\n  requirements: ReadonlyArray<StorageValidation>,\n) {\n  if (requirements.length === 0) return;\n\n  const byRelation = yield* readStorageMetadata(\n    dialect,\n    requirements.map(({ table }) => table),\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\n/** Candidate declarations do not establish SQL equality compatibility. SQLite\n * omits column collations, so only TEXT tables without COLLATE qualify. */\nexport const canJoinTextColumns = Effect.fnUntraced(function* (\n  columns: ReadonlyArray<PhysicalTextColumn | undefined>,\n) {\n  const candidates = columns.filter((column) => column !== undefined);\n\n  if (candidates.length !== columns.length || candidates.length === 0) return false;\n  const client = yield* SqlClient.SqlClient;\n\n  const dialect = client.onDialectOrElse({\n    pg: () => \"pg\" as const,\n    mysql: () => \"mysql\" as const,\n    sqlite: () => \"sqlite\" as const,\n    orElse: () => undefined,\n  });\n\n  if (\n    dialect === undefined ||\n    (dialect === \"sqlite\" &&\n      candidates.some(({ table }) => table.schema !== undefined && table.schema !== \"main\"))\n  )\n    return false;\n\n  const metadata = yield* readStorageMetadata(\n    dialect,\n    candidates.map(({ table }) => table),\n  );\n\n  const physical = candidates.map(({ table, name }) =>\n    metadata\n      .get(`${dialect}:${qualifiedTableName(table)}`)\n      ?.textColumns.find((column) => column.name === name),\n  );\n\n  const first = physical[0];\n\n  return (\n    first !== undefined &&\n    physical.every((column) => column?.type === first.type && column.collation === first.collation)\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\n/** Stronger than ordinary mapped uniqueness: a generated managed pending key\n * must probe exactly one physical row. Schema metadata is acquired with the\n * service graph; migrations require reacquisition, as for all storage checks.\n * Resolve each catalog/PRAGMA in the same schema: TEMP can reuse index names. */\nexport const hasManagedSqliteCleanupKey = Effect.fnUntraced(function* (\n  table: PhysicalStorageTable,\n  key: string,\n) {\n  if (table.schema !== undefined && table.schema !== \"main\") return false;\n  const column = table.columns[key];\n\n  if (column === undefined) return false;\n  const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n\n  const rows = yield* sql`\n    select 1 as valid\n    from main.sqlite_schema s\n    where s.type = 'table' and s.name = ${table.name}\n      and instr(lower(s.sql), 'collate') = 0\n      and not exists(select 1 from main.sqlite_schema t where t.type = 'trigger' and t.tbl_name = s.name collate nocase)\n      and not exists(select 1 from temp.sqlite_schema t\n        where (t.type in ('table', 'view') and t.name = s.name collate nocase) or (t.type = 'trigger' and t.tbl_name = s.name collate nocase))\n      and exists(select 1 from pragma_table_info(s.name, 'main') c\n        where c.name = ${column.name} and upper(c.type) = 'TEXT' and c.\"notnull\" = 1)\n      and exists(select 1 from pragma_index_list(s.name, 'main') i\n        where i.\"unique\" = 1 and i.partial = 0\n          and (select count(*) from pragma_index_xinfo(i.name, 'main') x where x.key = 1) = 1\n          and exists(select 1 from pragma_index_xinfo(i.name, 'main') x\n            where x.key = 1 and x.name = ${column.name} and x.coll = 'BINARY'))`;\n\n  return rows.length === 1;\n});\n"],"mappings":"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"}