@nxgt/janus-drizzle 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,7 +101,7 @@ tables.
101
101
 
102
102
  | Table | Keys and indexes |
103
103
  | --- | --- |
104
- | `users` | `id` · `(id, type)` unique, for the logins' foreign key · `(type, id)` for listing · a check that a password has both its hash and its date, or neither · a check that a second factor (`second_factor_method`, `_secret`, `_confirmed_at`, `_last_step`) has its method and its secret, or none of the four · a check that the method is `totp` and the last step not negative |
104
+ | `users` | `id` · `(id, type)` unique, for the logins' foreign key · `(type, id)` for listing · a check that a password has both its hash and its date, or neither · a check that a second factor (`second_factor_method`, `_secret`, `_confirmed_at`, `_last_step`, `_recovery_codes`) has its method and its secret, or none of the five · a check that the method is `totp` and the last step not negative |
105
105
  | `logins` | primary key `(type, login)`: **a login is unique per user type** · `(user_id, type)` references the user, `on delete cascade` |
106
106
  | `sessions` | `id` · `token_hash` unique · `user_id` · `expires_at`, for `collectExpired()` |
107
107
  | `tokens` | `token_hash` · `user_id` · a check on `kind` · `code_hash`, and `attempts`, `not null default 0`, checked `>= 0` |
@@ -116,6 +116,8 @@ requires. No secret is stored: sessions and tokens hold `sha256` of the secret,
116
116
  passwords a self-describing hash, and `second_factor_secret` a TOTP secret
117
117
  `@nxgt/janus` has already sealed with your application's key —
118
118
  `v1.<key id>.…` — which the store keeps byte for byte.
119
+ `second_factor_recovery_codes` holds keyed hashes of the recovery codes, never
120
+ a code, in the order written; no codes are `null`, read as `[]`.
119
121
 
120
122
  ## Traps
121
123
 
@@ -131,6 +133,12 @@ passwords a self-describing hash, and `second_factor_secret` a TOTP secret
131
133
  Run `bunx drizzle-kit generate`, then `bunx drizzle-kit migrate`, before
132
134
  deploying — with `--config` naming Janus's config if it has its own.
133
135
  [Upgrading](docs/guide/migrations.md#upgrading) shows what it writes.
136
+ - **Upgrading to 0.4 needs a migration too.** `users` gained
137
+ `second_factor_recovery_codes`, and its whole-factor check now covers it.
138
+ Deployed without it, every query on `users` fails with `STORE_FAILED`,
139
+ caused by `column "second_factor_recovery_codes" does not exist`.
140
+ [To 0.4](docs/guide/migrations.md#to-04-recovery-codes) shows the two
141
+ statements drizzle-kit writes.
134
142
  - **Your migrations create the tables, not this package.** The core never
135
143
  manages a schema, and the stores create nothing per request. Without the
136
144
  exports in your schema file, the first sign-up fails with `STORE_FAILED`,
package/dist/index.js CHANGED
@@ -110,6 +110,7 @@ function usersTable(table) {
110
110
  secondFactorSecret: text2("second_factor_secret"),
111
111
  secondFactorConfirmedAt: at("second_factor_confirmed_at"),
112
112
  secondFactorLastStep: integer2("second_factor_last_step"),
113
+ secondFactorRecoveryCodes: text2("second_factor_recovery_codes").array(),
113
114
  emailVerifiedAt: at("email_verified_at"),
114
115
  version: integer2("version").notNull(),
115
116
  createdAt: at("created_at").notNull(),
@@ -118,7 +119,7 @@ function usersTable(table) {
118
119
  unique3("users_id_type_unique").on(t.id, t.type),
119
120
  index5("users_type_id").on(t.type, t.id),
120
121
  check2("users_password_whole", sql2`(${t.passwordHash} is null) = (${t.passwordUpdatedAt} is null)`),
121
- check2("users_second_factor_whole", sql2`(${t.secondFactorMethod} is null) = (${t.secondFactorSecret} is null) and (${t.secondFactorMethod} is not null or (${t.secondFactorConfirmedAt} is null and ${t.secondFactorLastStep} is null))`),
122
+ check2("users_second_factor_whole", sql2`(${t.secondFactorMethod} is null) = (${t.secondFactorSecret} is null) and (${t.secondFactorMethod} is not null or (${t.secondFactorConfirmedAt} is null and ${t.secondFactorLastStep} is null and ${t.secondFactorRecoveryCodes} is null))`),
122
123
  check2("users_second_factor_values", sql2`${t.secondFactorMethod} in ('totp') and ${t.secondFactorLastStep} >= 0`)
123
124
  ]);
124
125
  }
@@ -313,11 +314,13 @@ function toUserSet(patch) {
313
314
  return set;
314
315
  }
315
316
  function toSecondFactorColumns(secondFactor) {
317
+ const codes = secondFactor?.recoveryCodes ?? [];
316
318
  return {
317
319
  secondFactorMethod: secondFactor?.method ?? null,
318
320
  secondFactorSecret: secondFactor?.secret ?? null,
319
321
  secondFactorConfirmedAt: secondFactor?.confirmedAt ?? null,
320
- secondFactorLastStep: secondFactor?.lastStep ?? null
322
+ secondFactorLastStep: secondFactor?.lastStep ?? null,
323
+ secondFactorRecoveryCodes: codes.length === 0 ? null : [...codes]
321
324
  };
322
325
  }
323
326
  function toUser(row) {
@@ -333,7 +336,8 @@ function toUser(row) {
333
336
  method: row.secondFactorMethod,
334
337
  secret: row.secondFactorSecret,
335
338
  confirmedAt: row.secondFactorConfirmedAt,
336
- lastStep: row.secondFactorLastStep
339
+ lastStep: row.secondFactorLastStep,
340
+ recoveryCodes: [...row.secondFactorRecoveryCodes ?? []]
337
341
  },
338
342
  emailVerifiedAt: row.emailVerifiedAt,
339
343
  version: row.version,
@@ -556,5 +560,5 @@ export {
556
560
  defineJanusTables
557
561
  };
558
562
 
559
- //# debugId=3F6F0185055BC57C64756E2164756E21
563
+ //# debugId=BBBB8A4136A5079264756E2164756E21
560
564
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -8,21 +8,21 @@
8
8
  "import { index, unique } from 'drizzle-orm/pg-core';\nimport { key, type TableFactory } from './columns';\n\n/**\n * Permission tuples, one row each: `record:r1#owners@patient:u1`, or\n * `…@team:t1#members` for a subject set, whose `subject_relation` an\n * entity's row leaves `null`.\n *\n * **The uniqueness of a tuple is the unique constraint**, `nulls not\n * distinct` so two rows for one entity collide — PostgreSQL 15 or later.\n * Its column order is the reverse index `findObjects` pages: every equality\n * first, the object id last.\n */\nexport function relationsTable(table: TableFactory) {\n\treturn table(\n\t\t'relations',\n\t\t{\n\t\t\tobjectType: key('object_type').notNull(),\n\t\t\tobjectId: key('object_id').notNull(),\n\t\t\trelation: key('relation').notNull(),\n\t\t\tsubjectType: key('subject_type').notNull(),\n\t\t\tsubjectId: key('subject_id').notNull(),\n\t\t\tsubjectRelation: key('subject_relation'),\n\t\t},\n\t\t(t) => [\n\t\t\tunique('relations_tuple_unique')\n\t\t\t\t.on(\n\t\t\t\t\tt.subjectType,\n\t\t\t\t\tt.subjectId,\n\t\t\t\t\tt.subjectRelation,\n\t\t\t\t\tt.relation,\n\t\t\t\t\tt.objectType,\n\t\t\t\t\tt.objectId,\n\t\t\t\t)\n\t\t\t\t.nullsNotDistinct(),\n\t\t\t/** One hop forwards: `findSubjectSets`, `findEntities`, `deleteEntity` as the object. */\n\t\t\tindex('relations_object').on(t.objectType, t.objectId, t.relation),\n\t\t],\n\t);\n}\n",
9
9
  "import { index, unique } from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/**\n * Sessions. No foreign key to the user: deleting a user deletes the user,\n * and the core deletes the sessions next — as it does when they live\n * elsewhere, in Redis.\n */\nexport function sessionsTable(table: TableFactory) {\n\treturn table(\n\t\t'sessions',\n\t\t{\n\t\t\tid: key('id').primaryKey(),\n\t\t\t/** `sha256` of the session token. The secret itself is never stored. */\n\t\t\ttokenHash: key('token_hash').notNull(),\n\t\t\tuserId: key('user_id').notNull(),\n\t\t\tauthenticatedAt: at('authenticated_at').notNull(),\n\t\t\texpiresAt: at('expires_at').notNull(),\n\t\t\trevokedAt: at('revoked_at'),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\tunique('sessions_token_hash_unique').on(t.tokenHash),\n\t\t\tindex('sessions_user_id').on(t.userId),\n\t\t\t/** `deleteExpiredSessions`: PostgreSQL has no TTL, so the core collects. */\n\t\t\tindex('sessions_expires_at').on(t.expiresAt),\n\t\t],\n\t);\n}\n",
10
10
  "import { sql } from 'drizzle-orm';\nimport { check, index, integer, text } from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/** One-time tokens, keyed by their hash. */\nexport function tokensTable(table: TableFactory) {\n\treturn table(\n\t\t'tokens',\n\t\t{\n\t\t\ttokenHash: key('token_hash').primaryKey(),\n\t\t\tkind: text('kind', {\n\t\t\t\tenum: ['verifyEmail', 'resetPassword', 'secondFactor', 'signInCode'],\n\t\t\t}).notNull(),\n\t\t\tuserId: key('user_id').notNull(),\n\t\t\taddress: text('address').notNull(),\n\t\t\t/** A sign-in code's hash, keyed by the token's secret. */\n\t\t\tcodeHash: text('code_hash'),\n\t\t\t/** Codes tried against it. The default fills the rows a migration finds. */\n\t\t\tattempts: integer('attempts').notNull().default(0),\n\t\t\texpiresAt: at('expires_at').notNull(),\n\t\t\tspentAt: at('spent_at'),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\tcheck(\n\t\t\t\t'tokens_kind',\n\t\t\t\tsql`${t.kind} in ('verifyEmail', 'resetPassword', 'secondFactor', 'signInCode')`,\n\t\t\t),\n\t\t\tcheck('tokens_attempts', sql`${t.attempts} >= 0`),\n\t\t\tindex('tokens_user_id').on(t.userId),\n\t\t],\n\t);\n}\n",
11
- "import { sql } from 'drizzle-orm';\nimport {\n\tboolean,\n\tcheck,\n\tindex,\n\tinteger,\n\tjsonb,\n\ttext,\n\tunique,\n} from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/**\n * Users and their passwords: one row each. `logins` is the port's array,\n * verbatim and in order; its uniqueness is `logins`' primary key.\n */\nexport function usersTable(table: TableFactory) {\n\treturn table(\n\t\t'users',\n\t\t{\n\t\t\t/** The UUIDv7 the core minted. `text`, not `uuid`: the store parses nothing. */\n\t\t\tid: key('id').primaryKey(),\n\t\t\ttype: key('type').notNull(),\n\t\t\tschemaVersion: text('schema_version').notNull(),\n\t\t\tactive: boolean('active').notNull(),\n\t\t\t/** The application's fields. The core validated them already. */\n\t\t\tfields: jsonb('fields').notNull(),\n\t\t\tlogins: text('logins').array().notNull(),\n\t\t\tpasswordHash: text('password_hash'),\n\t\t\tpasswordUpdatedAt: at('password_updated_at'),\n\t\t\t/** `'totp'`, or `null` for a user with no second factor. */\n\t\t\tsecondFactorMethod: text('second_factor_method', { enum: ['totp'] }),\n\t\t\t/** Sealed by the core with the application's key: never the plain secret. */\n\t\t\tsecondFactorSecret: text('second_factor_secret'),\n\t\t\tsecondFactorConfirmedAt: at('second_factor_confirmed_at'),\n\t\t\tsecondFactorLastStep: integer('second_factor_last_step'),\n\t\t\temailVerifiedAt: at('email_verified_at'),\n\t\t\tversion: integer('version').notNull(),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t\tupdatedAt: at('updated_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\t/** What `logins` references, so a login's type is its user's. */\n\t\t\tunique('users_id_type_unique').on(t.id, t.type),\n\t\t\t/** `listUsers` reads one type in id order. */\n\t\t\tindex('users_type_id').on(t.type, t.id),\n\t\t\t/** A password is a hash and when it was set, or neither. */\n\t\t\tcheck(\n\t\t\t\t'users_password_whole',\n\t\t\t\tsql`(${t.passwordHash} is null) = (${t.passwordUpdatedAt} is null)`,\n\t\t\t),\n\t\t\t/**\n\t\t\t * A second factor is a method and a secret, or neither; its\n\t\t\t * confirmation and last step exist only beside them.\n\t\t\t */\n\t\t\tcheck(\n\t\t\t\t'users_second_factor_whole',\n\t\t\t\tsql`(${t.secondFactorMethod} is null) = (${t.secondFactorSecret} is null) and (${t.secondFactorMethod} is not null or (${t.secondFactorConfirmedAt} is null and ${t.secondFactorLastStep} is null))`,\n\t\t\t),\n\t\t\t/** `'totp'` is the one method; a step counts up from the epoch. */\n\t\t\tcheck(\n\t\t\t\t'users_second_factor_values',\n\t\t\t\tsql`${t.secondFactorMethod} in ('totp') and ${t.secondFactorLastStep} >= 0`,\n\t\t\t),\n\t\t],\n\t);\n}\n",
11
+ "import { sql } from 'drizzle-orm';\nimport {\n\tboolean,\n\tcheck,\n\tindex,\n\tinteger,\n\tjsonb,\n\ttext,\n\tunique,\n} from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/**\n * Users and their passwords: one row each. `logins` is the port's array,\n * verbatim and in order; its uniqueness is `logins`' primary key.\n */\nexport function usersTable(table: TableFactory) {\n\treturn table(\n\t\t'users',\n\t\t{\n\t\t\t/** The UUIDv7 the core minted. `text`, not `uuid`: the store parses nothing. */\n\t\t\tid: key('id').primaryKey(),\n\t\t\ttype: key('type').notNull(),\n\t\t\tschemaVersion: text('schema_version').notNull(),\n\t\t\tactive: boolean('active').notNull(),\n\t\t\t/** The application's fields. The core validated them already. */\n\t\t\tfields: jsonb('fields').notNull(),\n\t\t\tlogins: text('logins').array().notNull(),\n\t\t\tpasswordHash: text('password_hash'),\n\t\t\tpasswordUpdatedAt: at('password_updated_at'),\n\t\t\t/** `'totp'`, or `null` for a user with no second factor. */\n\t\t\tsecondFactorMethod: text('second_factor_method', { enum: ['totp'] }),\n\t\t\t/** Sealed by the core with the application's key: never the plain secret. */\n\t\t\tsecondFactorSecret: text('second_factor_secret'),\n\t\t\tsecondFactorConfirmedAt: at('second_factor_confirmed_at'),\n\t\t\tsecondFactorLastStep: integer('second_factor_last_step'),\n\t\t\t/**\n\t\t\t * The recovery codes' keyed hashes, in order — `null` for none, read\n\t\t\t * as `[]`, so a row written before the column existed reads as no\n\t\t\t * codes.\n\t\t\t */\n\t\t\tsecondFactorRecoveryCodes: text('second_factor_recovery_codes').array(),\n\t\t\temailVerifiedAt: at('email_verified_at'),\n\t\t\tversion: integer('version').notNull(),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t\tupdatedAt: at('updated_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\t/** What `logins` references, so a login's type is its user's. */\n\t\t\tunique('users_id_type_unique').on(t.id, t.type),\n\t\t\t/** `listUsers` reads one type in id order. */\n\t\t\tindex('users_type_id').on(t.type, t.id),\n\t\t\t/** A password is a hash and when it was set, or neither. */\n\t\t\tcheck(\n\t\t\t\t'users_password_whole',\n\t\t\t\tsql`(${t.passwordHash} is null) = (${t.passwordUpdatedAt} is null)`,\n\t\t\t),\n\t\t\t/**\n\t\t\t * A second factor is a method and a secret, or neither; its\n\t\t\t * confirmation, last step and recovery codes exist only beside them.\n\t\t\t */\n\t\t\tcheck(\n\t\t\t\t'users_second_factor_whole',\n\t\t\t\tsql`(${t.secondFactorMethod} is null) = (${t.secondFactorSecret} is null) and (${t.secondFactorMethod} is not null or (${t.secondFactorConfirmedAt} is null and ${t.secondFactorLastStep} is null and ${t.secondFactorRecoveryCodes} is null))`,\n\t\t\t),\n\t\t\t/** `'totp'` is the one method; a step counts up from the epoch. */\n\t\t\tcheck(\n\t\t\t\t'users_second_factor_values',\n\t\t\t\tsql`${t.secondFactorMethod} in ('totp') and ${t.secondFactorLastStep} >= 0`,\n\t\t\t),\n\t\t],\n\t);\n}\n",
12
12
  "import { toDataError } from '@nxgt/drizzle';\nimport { JanusError, StoreConflict, StoreFailure } from '@nxgt/janus';\n\n/**\n * **The database's errors, as the port's.** One file, and one default.\n *\n * | What happens | Becomes |\n * | --- | --- |\n * | A login another user of the type holds | `StoreConflict('login')`, carrying `login` and `userType` |\n * | A row with this id already there | nothing: the insert is a retry, and answers what is stored |\n * | A unique violation on **any other constraint** | **`StoreFailure`** |\n * | A check or foreign key refusing a row | **`StoreFailure`** |\n * | Anything else, absolutely | `StoreFailure`, with `@nxgt/drizzle`'s `DataError` as `cause` |\n *\n * The first two are never errors here: a login is written with `on conflict\n * do nothing`, and a login the statement did not write is one somebody else\n * holds — so no conflict is ever told apart by parsing a driver's message.\n *\n * The bold rows go against the obvious reading. A violation the adapter did\n * not mean to cause is an adapter bug, and reporting it as `LOGIN_TAKEN`\n * would tell somebody their login is taken when it is not. The core\n * validated every record before it reached a store, so a constraint refusing\n * one means the adapter and the port disagree — never the caller's fault.\n *\n * **The default is a failure, not an absence.** That branch is what keeps the\n * port's first rule: nothing here answers `null` for an error.\n */\n\nexport type Slot = 'users' | 'sessions' | 'tokens' | 'relations';\n\n/** Runs one port method, and makes every rejection one the port allows. */\nexport async function run<T>(\n\tslot: Slot,\n\toperation: string,\n\tbody: () => Promise<T>,\n): Promise<T> {\n\ttry {\n\t\treturn await body();\n\t} catch (error) {\n\t\t// Already the port's — a conflict, a NOT_FOUND after a read.\n\t\tif (error instanceof JanusError) throw error;\n\t\tthrow new StoreFailure(`${slot}.${operation}: the store could not answer`, {\n\t\t\tslot,\n\t\t\toperation,\n\t\t\tcause: toDataError(error),\n\t\t});\n\t}\n}\n\n/** The refusal the port promises for a taken login. */\nexport function loginTaken(\n\toperation: string,\n\ttype: string,\n\tlogin: string,\n): StoreConflict {\n\treturn new StoreConflict(\n\t\t'login',\n\t\t`${operation}: the login is taken by another ${type}`,\n\t\t{ login, userType: type, operation },\n\t);\n}\n",
13
13
  "import type {\n\tCursorPage,\n\tEntity,\n\tRelationTuple,\n\tSubjectSet,\n} from '@nxgt/janus';\nimport type { ObjectPageRequest } from '@nxgt/janus/permissions';\nimport { and, asc, eq, gt, isNotNull, isNull } from 'drizzle-orm';\nimport type { RelationContext } from './context';\nimport { matching, subjectIs } from './tuples';\n\nexport async function has(\n\t{ db, relations }: RelationContext,\n\ttuple: RelationTuple,\n): Promise<boolean> {\n\tconst found = await db\n\t\t.select({ relation: relations.relation })\n\t\t.from(relations)\n\t\t.where(matching(relations, tuple))\n\t\t.limit(1);\n\treturn found.length === 1;\n}\n\nexport async function findSubjectSets(\n\tcontext: RelationContext,\n\tobject: Entity,\n\trelation: string,\n): Promise<SubjectSet[]> {\n\treturn (await holding(context, object, relation, true)).map(\n\t\t(row): SubjectSet => ({\n\t\t\ttype: row.type,\n\t\t\tid: row.id,\n\t\t\trelation: row.relation ?? '',\n\t\t}),\n\t);\n}\n\nexport async function findEntities(\n\tcontext: RelationContext,\n\tobject: Entity,\n\trelation: string,\n): Promise<Entity[]> {\n\treturn (await holding(context, object, relation, false)).map(\n\t\t(row): Entity => ({ type: row.type, id: row.id }),\n\t);\n}\n\n/** One hop from an object: its subject sets, or its entities. */\nfunction holding(\n\t{ db, relations }: RelationContext,\n\tobject: Entity,\n\trelation: string,\n\tsets: boolean,\n) {\n\treturn db\n\t\t.select({\n\t\t\ttype: relations.subjectType,\n\t\t\tid: relations.subjectId,\n\t\t\trelation: relations.subjectRelation,\n\t\t})\n\t\t.from(relations)\n\t\t.where(\n\t\t\tand(\n\t\t\t\teq(relations.objectType, object.type),\n\t\t\t\teq(relations.objectId, object.id),\n\t\t\t\teq(relations.relation, relation),\n\t\t\t\tsets\n\t\t\t\t\t? isNotNull(relations.subjectRelation)\n\t\t\t\t\t: isNull(relations.subjectRelation),\n\t\t\t),\n\t\t);\n}\n\n/** A page of object ids, in id order, after the cursor. */\nexport async function findObjects(\n\t{ db, relations }: RelationContext,\n\t{ type, relation, subject, after, limit }: ObjectPageRequest,\n): Promise<CursorPage<string>> {\n\tconst conditions = [\n\t\tsubjectIs(relations, subject),\n\t\teq(relations.relation, relation),\n\t\teq(relations.objectType, type),\n\t];\n\tif (after !== null) conditions.push(gt(relations.objectId, after));\n\tconst found = await db\n\t\t.select({ id: relations.objectId })\n\t\t.from(relations)\n\t\t.where(and(...conditions))\n\t\t.orderBy(asc(relations.objectId))\n\t\t.limit(limit + 1);\n\tconst items = found.slice(0, limit).map((row) => row.id);\n\tconst last = items.at(-1);\n\treturn {\n\t\titems,\n\t\tnextCursor: found.length > limit && last !== undefined ? last : null,\n\t};\n}\n",
14
14
  "import { isSubjectSet, type RelationTuple, type Subject } from '@nxgt/janus';\nimport { and, eq, isNull, type SQL } from 'drizzle-orm';\nimport type { Relations } from './context';\n\n/** Tuples in one order: their notation, compared by code unit. */\nexport function inOrder(\n\ttuples: readonly RelationTuple[],\n): readonly RelationTuple[] {\n\tconst keyOf = (tuple: RelationTuple) =>\n\t\tJSON.stringify([\n\t\t\ttuple.object.type,\n\t\t\ttuple.object.id,\n\t\t\ttuple.relation,\n\t\t\ttuple.subject.type,\n\t\t\ttuple.subject.id,\n\t\t\tisSubjectSet(tuple.subject) ? tuple.subject.relation : null,\n\t\t]);\n\treturn [...tuples].sort((a, b) => {\n\t\tconst [x, y] = [keyOf(a), keyOf(b)];\n\t\treturn x < y ? -1 : x > y ? 1 : 0;\n\t});\n}\n\n/** A tuple as a row: an entity's `subject_relation` is `null`. */\nexport function rowOf(tuple: RelationTuple): Relations['$inferInsert'] {\n\treturn {\n\t\tobjectType: tuple.object.type,\n\t\tobjectId: tuple.object.id,\n\t\trelation: tuple.relation,\n\t\tsubjectType: tuple.subject.type,\n\t\tsubjectId: tuple.subject.id,\n\t\tsubjectRelation: isSubjectSet(tuple.subject)\n\t\t\t? tuple.subject.relation\n\t\t\t: null,\n\t};\n}\n\n/** Exactly this subject: a subject set is not its entity, and the reverse. */\nexport function subjectIs(\n\trelations: Relations,\n\tsubject: Subject,\n): SQL | undefined {\n\treturn and(\n\t\teq(relations.subjectType, subject.type),\n\t\teq(relations.subjectId, subject.id),\n\t\tisSubjectSet(subject)\n\t\t\t? eq(relations.subjectRelation, subject.relation)\n\t\t\t: isNull(relations.subjectRelation),\n\t);\n}\n\n/** Exactly this tuple. */\nexport function matching(\n\trelations: Relations,\n\ttuple: RelationTuple,\n): SQL | undefined {\n\treturn and(\n\t\teq(relations.objectType, tuple.object.type),\n\t\teq(relations.objectId, tuple.object.id),\n\t\teq(relations.relation, tuple.relation),\n\t\tsubjectIs(relations, tuple.subject),\n\t);\n}\n",
15
15
  "import { type PgDatabase, withTransaction } from '@nxgt/drizzle/pg';\nimport type { Entity, RelationTuple } from '@nxgt/janus';\nimport { and, eq, or } from 'drizzle-orm';\nimport type { RelationContext, Relations } from './context';\nimport { inOrder, matching, rowOf } from './tuples';\n\n/** One tuple in one statement; more in one transaction. */\nexport async function write(\n\t{ db, relations }: RelationContext,\n\tadd: readonly RelationTuple[],\n\tremove: readonly RelationTuple[],\n): Promise<void> {\n\tif (add.length + remove.length <= 1) return apply(db, relations, add, remove);\n\tawait withTransaction(db, (tx) => apply(tx, relations, add, remove));\n}\n\n/** Removals first, then additions. */\nasync function apply(\n\ttx: PgDatabase,\n\trelations: Relations,\n\tadd: readonly RelationTuple[],\n\tremove: readonly RelationTuple[],\n): Promise<void> {\n\t// In one order, so two writes touching the same tuples take their locks\n\t// alike and never deadlock.\n\tfor (const tuple of inOrder(remove)) {\n\t\tawait tx.delete(relations).where(matching(relations, tuple));\n\t}\n\tif (add.length > 0) {\n\t\t// A stored tuple meets the unique constraint, and is a no-op.\n\t\tawait tx\n\t\t\t.insert(relations)\n\t\t\t.values(inOrder(add).map(rowOf))\n\t\t\t.onConflictDoNothing();\n\t}\n}\n\n/** Every tuple naming the entity, as object or as subject. */\nexport async function deleteEntity(\n\t{ db, relations }: RelationContext,\n\tentity: Entity,\n): Promise<number> {\n\tconst deleted = await db\n\t\t.delete(relations)\n\t\t.where(\n\t\t\tor(\n\t\t\t\tand(\n\t\t\t\t\teq(relations.objectType, entity.type),\n\t\t\t\t\teq(relations.objectId, entity.id),\n\t\t\t\t),\n\t\t\t\tand(\n\t\t\t\t\teq(relations.subjectType, entity.type),\n\t\t\t\t\teq(relations.subjectId, entity.id),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\t.returning({ relation: relations.relation });\n\treturn deleted.length;\n}\n",
16
16
  "import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { RelationStore } from '@nxgt/janus/permissions';\nimport { type DrizzleAdapterOptions, defineJanusTables } from '../tables';\nimport { run } from '../translate';\nimport type { RelationContext } from './context';\nimport { findEntities, findObjects, findSubjectSets, has } from './reads';\nimport { deleteEntity, write } from './writes';\n\n/**\n * The relation store `permissions()` takes, over one PostgreSQL database\n * through Drizzle — and the one `janus({ relations })` deletes a user's\n * tuples from.\n *\n * ```ts\n * const access = permissions({ model, store: createDrizzleRelations(db) });\n * ```\n *\n * **A write of more than one tuple is a transaction**, `@nxgt/drizzle`'s\n * `withTransaction`: removals first, then additions, all or nothing. It is not\n * retried here — a write is idempotent, so the caller retries.\n */\nexport function createDrizzleRelations(\n\tdb: PgDatabase,\n\toptions: DrizzleAdapterOptions<'relations'> = {},\n): RelationStore {\n\tconst tables = options.tables ?? defineJanusTables();\n\tconst context: RelationContext = { db, relations: tables.relations };\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('relations', operation, body);\n\n\treturn {\n\t\twrite: ({ add = [], remove = [] }) =>\n\t\t\trun$('write', () => write(context, add, remove)),\n\t\thas: (tuple) => run$('has', () => has(context, tuple)),\n\t\tfindSubjectSets: (object, relation) =>\n\t\t\trun$('findSubjectSets', () => findSubjectSets(context, object, relation)),\n\t\tfindEntities: (object, relation) =>\n\t\t\trun$('findEntities', () => findEntities(context, object, relation)),\n\t\t// The request is unpacked before `run$`, as `write`'s is and as it was\n\t\t// before the split, so a malformed request throws the caller's\n\t\t// TypeError at once instead of reading as an outage.\n\t\tfindObjects: ({ type, relation, subject, after, limit }) =>\n\t\t\trun$('findObjects', () =>\n\t\t\t\tfindObjects(context, { type, relation, subject, after, limit }),\n\t\t\t),\n\t\tdeleteEntity: (entity) =>\n\t\t\trun$('deleteEntity', () => deleteEntity(context, entity)),\n\t};\n}\n",
17
17
  "import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { SessionStore } from '@nxgt/janus';\nimport { and, eq, isNull, lte, ne, sql } from 'drizzle-orm';\nimport { run } from '../translate';\nimport type { IdentityTables } from './identity-tables';\nimport { stamp, toSession } from './records';\n\nexport function sessionStore(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n): SessionStore {\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('sessions', operation, body);\n\n\treturn {\n\t\tinsertSession: (record) =>\n\t\t\trun$('insertSession', async () => {\n\t\t\t\t// Idempotent under retry: a session with this id is already there.\n\t\t\t\t// A collision on the token hash is not a retry, and fails.\n\t\t\t\tawait db\n\t\t\t\t\t.insert(tables.sessions)\n\t\t\t\t\t.values(record)\n\t\t\t\t\t.onConflictDoNothing({ target: tables.sessions.id });\n\t\t\t}),\n\n\t\tfindSessionByTokenHash: (tokenHash) =>\n\t\t\trun$('findSessionByTokenHash', async () => {\n\t\t\t\tconst [row] = await db\n\t\t\t\t\t.select()\n\t\t\t\t\t.from(tables.sessions)\n\t\t\t\t\t.where(eq(tables.sessions.tokenHash, tokenHash));\n\t\t\t\treturn row === undefined ? null : toSession(row);\n\t\t\t}),\n\n\t\textendSession: (id, expiresAt) =>\n\t\t\trun$('extendSession', async () => {\n\t\t\t\t// `revoked_at is null` in the condition: an extension racing a\n\t\t\t\t// revocation matches nothing, and never brings the session back.\n\t\t\t\tconst [row] = await db\n\t\t\t\t\t.update(tables.sessions)\n\t\t\t\t\t.set({ expiresAt })\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(eq(tables.sessions.id, id), isNull(tables.sessions.revokedAt)),\n\t\t\t\t\t)\n\t\t\t\t\t.returning();\n\t\t\t\treturn row === undefined ? null : toSession(row);\n\t\t\t}),\n\n\t\trevokeSession: (id, at) =>\n\t\t\trun$('revokeSession', () => revokeSession(db, tables, id, at)),\n\n\t\trevokeUserSessions: (userId, at, except) =>\n\t\t\trun$('revokeUserSessions', () =>\n\t\t\t\trevokeUserSessions(db, tables, userId, at, except),\n\t\t\t),\n\n\t\tdeleteUserSessions: (userId) =>\n\t\t\trun$('deleteUserSessions', async () => {\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.sessions)\n\t\t\t\t\t.where(eq(tables.sessions.userId, userId))\n\t\t\t\t\t.returning({ id: tables.sessions.id });\n\t\t\t\treturn deleted.length;\n\t\t\t}),\n\n\t\tdeleteExpiredSessions: (before) =>\n\t\t\trun$('deleteExpiredSessions', async () => {\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.sessions)\n\t\t\t\t\t.where(lte(tables.sessions.expiresAt, before))\n\t\t\t\t\t.returning({ id: tables.sessions.id });\n\t\t\t\treturn deleted.length;\n\t\t\t}),\n\t};\n}\n\nasync function revokeSession(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\tid: string,\n\tat: Date,\n): Promise<boolean> {\n\t// `coalesce`, so a session already revoked keeps its first\n\t// `revokedAt` and still counts as matched.\n\tconst matched = await db\n\t\t.update(tables.sessions)\n\t\t.set({\n\t\t\trevokedAt: sql`coalesce(${tables.sessions.revokedAt}, ${stamp(at)})`,\n\t\t})\n\t\t.where(eq(tables.sessions.id, id))\n\t\t.returning({ id: tables.sessions.id });\n\treturn matched.length === 1;\n}\n\nasync function revokeUserSessions(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\tuserId: string,\n\tat: Date,\n\texcept: string | undefined,\n): Promise<number> {\n\tconst standing = and(\n\t\teq(tables.sessions.userId, userId),\n\t\tisNull(tables.sessions.revokedAt),\n\t);\n\tconst revoked = await db\n\t\t.update(tables.sessions)\n\t\t.set({ revokedAt: at })\n\t\t.where(\n\t\t\texcept === undefined\n\t\t\t\t? standing\n\t\t\t\t: and(standing, ne(tables.sessions.id, except)),\n\t\t)\n\t\t.returning({ id: tables.sessions.id });\n\treturn revoked.length;\n}\n",
18
- "import type { Id, SessionRecord, UserPatch, UserRecord } from '@nxgt/janus';\nimport { type SQL, sql } from 'drizzle-orm';\nimport type { JanusTables } from '../tables';\n\n// ─── Rows and records ─────────────────────────────────────────────────────\n//\n// Each record is rebuilt field by field: the port's records are exactly what\n// it declares, and a row carries columns the record names differently.\n\ntype UserRow = JanusTables['users']['$inferSelect'];\ntype UserInsert = JanusTables['users']['$inferInsert'];\ntype SessionRow = JanusTables['sessions']['$inferSelect'];\n\nexport function toUserRow(record: UserRecord): UserInsert {\n\treturn {\n\t\tid: record.id,\n\t\ttype: record.type,\n\t\tschemaVersion: record.schemaVersion,\n\t\tactive: record.active,\n\t\tfields: record.fields,\n\t\tlogins: [...record.logins],\n\t\tpasswordHash: record.password?.hash ?? null,\n\t\tpasswordUpdatedAt: record.password?.updatedAt ?? null,\n\t\t...toSecondFactorColumns(record.secondFactor),\n\t\temailVerifiedAt: record.emailVerifiedAt,\n\t\tversion: record.version,\n\t\tcreatedAt: record.createdAt,\n\t\tupdatedAt: record.updatedAt,\n\t};\n}\n\n/**\n * The columns a patch names. A key present with `undefined` is absent, as the\n * port says — never an erasure. `password: null` is named, and removes the\n * password.\n */\nexport function toUserSet(patch: UserPatch): Partial<UserInsert> {\n\tconst set: Partial<UserInsert> = {\n\t\tupdatedAt: patch.updatedAt,\n\t};\n\tif (patch.schemaVersion !== undefined)\n\t\tset.schemaVersion = patch.schemaVersion;\n\tif (patch.active !== undefined) set.active = patch.active;\n\tif (patch.fields !== undefined) set.fields = patch.fields;\n\tif (patch.logins !== undefined) set.logins = [...patch.logins];\n\tif (patch.emailVerifiedAt !== undefined)\n\t\tset.emailVerifiedAt = patch.emailVerifiedAt;\n\tif (patch.password !== undefined) {\n\t\tset.passwordHash = patch.password?.hash ?? null;\n\t\tset.passwordUpdatedAt = patch.password?.updatedAt ?? null;\n\t}\n\tif (patch.secondFactor !== undefined) {\n\t\tObject.assign(set, toSecondFactorColumns(patch.secondFactor));\n\t}\n\treturn set;\n}\n\n/** A second factor as its four columns, all `null` for none. */\nfunction toSecondFactorColumns(secondFactor: UserRecord['secondFactor']) {\n\treturn {\n\t\tsecondFactorMethod: secondFactor?.method ?? null,\n\t\tsecondFactorSecret: secondFactor?.secret ?? null,\n\t\tsecondFactorConfirmedAt: secondFactor?.confirmedAt ?? null,\n\t\tsecondFactorLastStep: secondFactor?.lastStep ?? null,\n\t};\n}\n\nexport function toUser(row: UserRow): UserRecord {\n\treturn {\n\t\tid: row.id as Id,\n\t\ttype: row.type,\n\t\tschemaVersion: row.schemaVersion,\n\t\tactive: row.active,\n\t\tfields: row.fields as UserRecord['fields'],\n\t\tlogins: [...row.logins],\n\t\tpassword:\n\t\t\trow.passwordHash === null || row.passwordUpdatedAt === null\n\t\t\t\t? null\n\t\t\t\t: { hash: row.passwordHash, updatedAt: row.passwordUpdatedAt },\n\t\tsecondFactor:\n\t\t\trow.secondFactorMethod === null || row.secondFactorSecret === null\n\t\t\t\t? null\n\t\t\t\t: {\n\t\t\t\t\t\tmethod: row.secondFactorMethod,\n\t\t\t\t\t\tsecret: row.secondFactorSecret,\n\t\t\t\t\t\tconfirmedAt: row.secondFactorConfirmedAt,\n\t\t\t\t\t\tlastStep: row.secondFactorLastStep,\n\t\t\t\t\t},\n\t\temailVerifiedAt: row.emailVerifiedAt,\n\t\tversion: row.version,\n\t\tcreatedAt: row.createdAt,\n\t\tupdatedAt: row.updatedAt,\n\t};\n}\n\nexport function toSession(row: SessionRow): SessionRecord {\n\treturn {\n\t\tid: row.id,\n\t\ttokenHash: row.tokenHash,\n\t\tuserId: row.userId as Id,\n\t\tauthenticatedAt: row.authenticatedAt,\n\t\texpiresAt: row.expiresAt,\n\t\trevokedAt: row.revokedAt,\n\t\tcreatedAt: row.createdAt,\n\t};\n}\n\n/**\n * A `Date` inside a `sql` template, as every driver takes it: `timestamptz`\n * from its ISO string. Drizzle's column mapping does not reach a raw\n * template, and postgres.js refuses a `Date` object there.\n */\nexport function stamp(at: Date): SQL {\n\treturn sql`${at.toISOString()}::timestamptz`;\n}\n",
18
+ "import type { Id, SessionRecord, UserPatch, UserRecord } from '@nxgt/janus';\nimport { type SQL, sql } from 'drizzle-orm';\nimport type { JanusTables } from '../tables';\n\n// ─── Rows and records ─────────────────────────────────────────────────────\n//\n// Each record is rebuilt field by field: the port's records are exactly what\n// it declares, and a row carries columns the record names differently.\n\ntype UserRow = JanusTables['users']['$inferSelect'];\ntype UserInsert = JanusTables['users']['$inferInsert'];\ntype SessionRow = JanusTables['sessions']['$inferSelect'];\n\nexport function toUserRow(record: UserRecord): UserInsert {\n\treturn {\n\t\tid: record.id,\n\t\ttype: record.type,\n\t\tschemaVersion: record.schemaVersion,\n\t\tactive: record.active,\n\t\tfields: record.fields,\n\t\tlogins: [...record.logins],\n\t\tpasswordHash: record.password?.hash ?? null,\n\t\tpasswordUpdatedAt: record.password?.updatedAt ?? null,\n\t\t...toSecondFactorColumns(record.secondFactor),\n\t\temailVerifiedAt: record.emailVerifiedAt,\n\t\tversion: record.version,\n\t\tcreatedAt: record.createdAt,\n\t\tupdatedAt: record.updatedAt,\n\t};\n}\n\n/**\n * The columns a patch names. A key present with `undefined` is absent, as the\n * port says — never an erasure. `password: null` is named, and removes the\n * password.\n */\nexport function toUserSet(patch: UserPatch): Partial<UserInsert> {\n\tconst set: Partial<UserInsert> = {\n\t\tupdatedAt: patch.updatedAt,\n\t};\n\tif (patch.schemaVersion !== undefined)\n\t\tset.schemaVersion = patch.schemaVersion;\n\tif (patch.active !== undefined) set.active = patch.active;\n\tif (patch.fields !== undefined) set.fields = patch.fields;\n\tif (patch.logins !== undefined) set.logins = [...patch.logins];\n\tif (patch.emailVerifiedAt !== undefined)\n\t\tset.emailVerifiedAt = patch.emailVerifiedAt;\n\tif (patch.password !== undefined) {\n\t\tset.passwordHash = patch.password?.hash ?? null;\n\t\tset.passwordUpdatedAt = patch.password?.updatedAt ?? null;\n\t}\n\tif (patch.secondFactor !== undefined) {\n\t\tObject.assign(set, toSecondFactorColumns(patch.secondFactor));\n\t}\n\treturn set;\n}\n\n/**\n * A second factor as its five columns, all `null` for none. No recovery\n * codes are written `null` too, never `{}`, and read back as `[]`: a row\n * then holds a code array only while it holds codes, so an instance of 0.3\n * — which knows four columns — can still remove a factor that has none.\n */\nfunction toSecondFactorColumns(secondFactor: UserRecord['secondFactor']) {\n\tconst codes = secondFactor?.recoveryCodes ?? [];\n\treturn {\n\t\tsecondFactorMethod: secondFactor?.method ?? null,\n\t\tsecondFactorSecret: secondFactor?.secret ?? null,\n\t\tsecondFactorConfirmedAt: secondFactor?.confirmedAt ?? null,\n\t\tsecondFactorLastStep: secondFactor?.lastStep ?? null,\n\t\tsecondFactorRecoveryCodes: codes.length === 0 ? null : [...codes],\n\t};\n}\n\nexport function toUser(row: UserRow): UserRecord {\n\treturn {\n\t\tid: row.id as Id,\n\t\ttype: row.type,\n\t\tschemaVersion: row.schemaVersion,\n\t\tactive: row.active,\n\t\tfields: row.fields as UserRecord['fields'],\n\t\tlogins: [...row.logins],\n\t\tpassword:\n\t\t\trow.passwordHash === null || row.passwordUpdatedAt === null\n\t\t\t\t? null\n\t\t\t\t: { hash: row.passwordHash, updatedAt: row.passwordUpdatedAt },\n\t\tsecondFactor:\n\t\t\trow.secondFactorMethod === null || row.secondFactorSecret === null\n\t\t\t\t? null\n\t\t\t\t: {\n\t\t\t\t\t\tmethod: row.secondFactorMethod,\n\t\t\t\t\t\tsecret: row.secondFactorSecret,\n\t\t\t\t\t\tconfirmedAt: row.secondFactorConfirmedAt,\n\t\t\t\t\t\tlastStep: row.secondFactorLastStep,\n\t\t\t\t\t\trecoveryCodes: [...(row.secondFactorRecoveryCodes ?? [])],\n\t\t\t\t\t},\n\t\temailVerifiedAt: row.emailVerifiedAt,\n\t\tversion: row.version,\n\t\tcreatedAt: row.createdAt,\n\t\tupdatedAt: row.updatedAt,\n\t};\n}\n\nexport function toSession(row: SessionRow): SessionRecord {\n\treturn {\n\t\tid: row.id,\n\t\ttokenHash: row.tokenHash,\n\t\tuserId: row.userId as Id,\n\t\tauthenticatedAt: row.authenticatedAt,\n\t\texpiresAt: row.expiresAt,\n\t\trevokedAt: row.revokedAt,\n\t\tcreatedAt: row.createdAt,\n\t};\n}\n\n/**\n * A `Date` inside a `sql` template, as every driver takes it: `timestamptz`\n * from its ISO string. Drizzle's column mapping does not reach a raw\n * template, and postgres.js refuses a `Date` object there.\n */\nexport function stamp(at: Date): SQL {\n\treturn sql`${at.toISOString()}::timestamptz`;\n}\n",
19
19
  "import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { Id, TokenKind, TokenRecord, TokenStore } from '@nxgt/janus';\nimport { and, eq, isNull, ne, sql } from 'drizzle-orm';\nimport { run } from '../translate';\nimport type { IdentityTables } from './identity-tables';\nimport { stamp } from './records';\n\nexport function tokenStore(db: PgDatabase, tables: IdentityTables): TokenStore {\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('tokens', operation, body);\n\n\treturn {\n\t\tinsertToken: (record) =>\n\t\t\trun$('insertToken', async () => {\n\t\t\t\t// The hash is the key: a collision is a retry.\n\t\t\t\tawait db.insert(tables.tokens).values(record).onConflictDoNothing();\n\t\t\t}),\n\n\t\tconsumeToken: (tokenHash, kind, at) =>\n\t\t\trun$('consumeToken', () => consumeToken(db, tables, tokenHash, kind, at)),\n\n\t\tcountAttempt: (tokenHash, kind) =>\n\t\t\trun$('countAttempt', () => countAttempt(db, tables, tokenHash, kind)),\n\n\t\tspendUserTokens: (userId, kind, at, except) =>\n\t\t\trun$('spendUserTokens', async () => {\n\t\t\t\t// One statement: each row is written under its lock, and\n\t\t\t\t// re-checked after a concurrent `consumeToken` committed, so the\n\t\t\t\t// two never both spend it.\n\t\t\t\tconst spent = await db\n\t\t\t\t\t.update(tables.tokens)\n\t\t\t\t\t.set({ spentAt: at })\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(\n\t\t\t\t\t\t\teq(tables.tokens.userId, userId),\n\t\t\t\t\t\t\teq(tables.tokens.kind, kind),\n\t\t\t\t\t\t\tisNull(tables.tokens.spentAt),\n\t\t\t\t\t\t\texcept === undefined\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: ne(tables.tokens.tokenHash, except),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\t.returning({ tokenHash: tables.tokens.tokenHash });\n\t\t\t\treturn spent.length;\n\t\t\t}),\n\n\t\tdeleteUserTokens: (userId) =>\n\t\t\trun$('deleteUserTokens', async () => {\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.tokens)\n\t\t\t\t\t.where(eq(tables.tokens.userId, userId))\n\t\t\t\t\t.returning({ tokenHash: tables.tokens.tokenHash });\n\t\t\t\treturn deleted.length;\n\t\t\t}),\n\t};\n}\n\nasync function consumeToken(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\ttokenHash: string,\n\tkind: TokenKind,\n\tat: Date,\n): Promise<TokenRecord | null> {\n\t// **One conditional write**, answering the row as it was before\n\t// it. `for update` makes a concurrent call wait for this one and\n\t// then read the spent row, so exactly one caller ever reads\n\t// `spentAt: null`; `coalesce` keeps a first `spentAt`.\n\tconst before = db.$with('before').as(\n\t\tdb\n\t\t\t.select()\n\t\t\t.from(tables.tokens)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(tables.tokens.tokenHash, tokenHash),\n\t\t\t\t\teq(tables.tokens.kind, kind),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.for('update'),\n\t);\n\tconst [spent] = await db\n\t\t.with(before)\n\t\t.update(tables.tokens)\n\t\t.set({\n\t\t\tspentAt: sql`coalesce(${tables.tokens.spentAt}, ${stamp(at)})`,\n\t\t})\n\t\t.from(before)\n\t\t.where(eq(tables.tokens.tokenHash, before.tokenHash))\n\t\t.returning({\n\t\t\ttokenHash: before.tokenHash,\n\t\t\tkind: before.kind,\n\t\t\tuserId: before.userId,\n\t\t\taddress: before.address,\n\t\t\tcodeHash: before.codeHash,\n\t\t\tattempts: before.attempts,\n\t\t\texpiresAt: before.expiresAt,\n\t\t\tspentAt: before.spentAt,\n\t\t\tcreatedAt: before.createdAt,\n\t\t});\n\treturn spent === undefined ? null : { ...spent, userId: spent.userId as Id };\n}\n\nasync function countAttempt(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\ttokenHash: string,\n\tkind: TokenKind,\n): Promise<TokenRecord | null> {\n\t// **One conditional write**: `attempts + 1` on the row, under the\n\t// row's lock, so twenty concurrent calls answer twenty counts. A\n\t// spent token matches nothing here, and is read as it is below.\n\tconst match = and(\n\t\teq(tables.tokens.tokenHash, tokenHash),\n\t\teq(tables.tokens.kind, kind),\n\t);\n\tconst [counted] = await db\n\t\t.update(tables.tokens)\n\t\t.set({ attempts: sql`${tables.tokens.attempts} + 1` })\n\t\t.where(and(match, isNull(tables.tokens.spentAt)))\n\t\t.returning();\n\tconst row =\n\t\tcounted ?? (await db.select().from(tables.tokens).where(match))[0];\n\treturn row === undefined ? null : { ...row, userId: row.userId as Id };\n}\n",
20
20
  "import { type PgDatabase, withTransaction } from '@nxgt/drizzle/pg';\nimport type { UserPatch, UserRecord, UserStore } from '@nxgt/janus';\nimport { NotFoundError, StoreConflict } from '@nxgt/janus';\nimport { and, asc, eq, gt, sql } from 'drizzle-orm';\nimport { run } from '../translate';\nimport type { IdentityTables } from './identity-tables';\nimport { claimLogins } from './logins';\nimport { toUser, toUserRow, toUserSet } from './records';\n\nexport function userStore(db: PgDatabase, tables: IdentityTables): UserStore {\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('users', operation, body);\n\n\treturn {\n\t\tinsertUser: (record) =>\n\t\t\trun$('insertUser', () => insertUser(db, tables, record)),\n\n\t\tfindUser: (id) => run$('findUser', () => findUser(db, tables, id)),\n\n\t\tfindUserByLogin: (type, login) =>\n\t\t\trun$('findUserByLogin', async () => {\n\t\t\t\tconst [row] = await db\n\t\t\t\t\t.select({ user: tables.users })\n\t\t\t\t\t.from(tables.logins)\n\t\t\t\t\t.innerJoin(tables.users, eq(tables.users.id, tables.logins.userId))\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(eq(tables.logins.type, type), eq(tables.logins.login, login)),\n\t\t\t\t\t);\n\t\t\t\treturn row === undefined ? null : toUser(row.user);\n\t\t\t}),\n\n\t\tlistUsers: ({ type, after, limit }) =>\n\t\t\trun$('listUsers', async () => {\n\t\t\t\t// One more than the page, to know whether another follows.\n\t\t\t\tconst found = await db\n\t\t\t\t\t.select()\n\t\t\t\t\t.from(tables.users)\n\t\t\t\t\t.where(\n\t\t\t\t\t\tafter === null\n\t\t\t\t\t\t\t? eq(tables.users.type, type)\n\t\t\t\t\t\t\t: and(eq(tables.users.type, type), gt(tables.users.id, after)),\n\t\t\t\t\t)\n\t\t\t\t\t.orderBy(asc(tables.users.id))\n\t\t\t\t\t.limit(limit + 1);\n\t\t\t\tconst items = found.slice(0, limit).map(toUser);\n\t\t\t\tconst last = items.at(-1);\n\t\t\t\treturn {\n\t\t\t\t\titems,\n\t\t\t\t\tnextCursor:\n\t\t\t\t\t\tfound.length > limit && last !== undefined ? last.id : null,\n\t\t\t\t};\n\t\t\t}),\n\n\t\tupdateUser: (id, patch, ifVersion) =>\n\t\t\trun$('updateUser', () =>\n\t\t\t\twithTransaction(db, (tx) =>\n\t\t\t\t\tupdateUser(tx, tables, id, patch, ifVersion),\n\t\t\t\t),\n\t\t\t),\n\n\t\tdeleteUser: (id) =>\n\t\t\trun$('deleteUser', async () => {\n\t\t\t\t// The logins go with it: their foreign key cascades.\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.users)\n\t\t\t\t\t.where(eq(tables.users.id, id))\n\t\t\t\t\t.returning({ id: tables.users.id });\n\t\t\t\treturn deleted.length === 1;\n\t\t\t}),\n\t};\n}\n\nasync function findUser(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\tid: string,\n): Promise<UserRecord | null> {\n\tconst [row] = await db\n\t\t.select()\n\t\t.from(tables.users)\n\t\t.where(eq(tables.users.id, id));\n\treturn row === undefined ? null : toUser(row);\n}\n\nasync function insertUser(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\trecord: UserRecord,\n): Promise<UserRecord> {\n\tconst inserted = await withTransaction(db, async (tx) => {\n\t\tconst [row] = await tx\n\t\t\t.insert(tables.users)\n\t\t\t.values(toUserRow(record))\n\t\t\t.onConflictDoNothing({ target: tables.users.id })\n\t\t\t.returning();\n\t\tif (row === undefined) return null;\n\t\tawait claimLogins(tx, tables, 'insertUser', record, record.logins);\n\t\treturn toUser(row);\n\t});\n\tif (inserted !== null) return inserted;\n\n\t// A retry whose first attempt landed: the user with this id is\n\t// there, and is the answer — logins included, which are theirs.\n\tconst stored = await findUser(db, tables, record.id);\n\tif (stored !== null) return stored;\n\t// Deleted between the two statements: a retry of nothing.\n\tthrow new NotFoundError('insertUser: the user was deleted meanwhile', {\n\t\tuserId: record.id,\n\t\toperation: 'insertUser',\n\t});\n}\n\nasync function updateUser(\n\ttx: PgDatabase,\n\ttables: IdentityTables,\n\tid: string,\n\tpatch: UserPatch,\n\tifVersion: number,\n): Promise<UserRecord> {\n\tconst [row] = await tx\n\t\t.update(tables.users)\n\t\t.set({\n\t\t\t...toUserSet(patch),\n\t\t\tversion: sql`${tables.users.version} + 1`,\n\t\t})\n\t\t.where(and(eq(tables.users.id, id), eq(tables.users.version, ifVersion)))\n\t\t.returning();\n\n\tif (row === undefined) {\n\t\t// The write matched nothing, which alone cannot tell an unknown\n\t\t// id from a moved version: read again, and say which.\n\t\tconst [stored] = await tx\n\t\t\t.select({ version: tables.users.version })\n\t\t\t.from(tables.users)\n\t\t\t.where(eq(tables.users.id, id));\n\t\tif (stored === undefined) {\n\t\t\tthrow new NotFoundError('updateUser: no user has this id', {\n\t\t\t\tuserId: id,\n\t\t\t\toperation: 'updateUser',\n\t\t\t});\n\t\t}\n\t\tthrow new StoreConflict(\n\t\t\t'version',\n\t\t\t`updateUser: expected version ${ifVersion}, found ${stored.version}`,\n\t\t\t{\n\t\t\t\tuserId: id,\n\t\t\t\texpectedVersion: ifVersion,\n\t\t\t\tactualVersion: stored.version,\n\t\t\t\toperation: 'updateUser',\n\t\t\t},\n\t\t);\n\t}\n\n\tif (patch.logins !== undefined) {\n\t\tawait tx.delete(tables.logins).where(eq(tables.logins.userId, id));\n\t\tawait claimLogins(tx, tables, 'updateUser', row, patch.logins);\n\t}\n\treturn toUser(row);\n}\n",
21
21
  "import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport { loginTaken } from '../translate';\nimport type { IdentityTables } from './identity-tables';\n\n/**\n * Writes the logins of one user, and throws the port's conflict for the\n * first one another user of the type holds. `on conflict do nothing`: a\n * login the statement did not write is one somebody else holds, and a\n * concurrent sign-up waits for the first to commit, then writes nothing.\n */\nexport async function claimLogins(\n\ttx: PgDatabase,\n\ttables: IdentityTables,\n\toperation: string,\n\tuser: { readonly id: string; readonly type: string },\n\tlogins: readonly string[],\n): Promise<void> {\n\t// Sorted: two transactions claiming the same logins take their locks in\n\t// one order, and never deadlock — the loser waits, then finds them taken.\n\tconst distinct = [...new Set(logins)].sort();\n\tif (distinct.length === 0) return;\n\tconst written = await tx\n\t\t.insert(tables.logins)\n\t\t.values(\n\t\t\tdistinct.map((login) => ({ type: user.type, login, userId: user.id })),\n\t\t)\n\t\t.onConflictDoNothing()\n\t\t.returning({ login: tables.logins.login });\n\tif (written.length === distinct.length) return;\n\tconst claimed = new Set(written.map((row) => row.login));\n\tconst taken = distinct.find((login) => !claimed.has(login)) ?? '';\n\tthrow loginTaken(operation, user.type, taken);\n}\n",
22
22
  "import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { JanusStores } from '@nxgt/janus';\nimport { type DrizzleAdapterOptions, defineJanusTables } from '../tables';\nimport type { IdentityTable } from './identity-tables';\nimport { sessionStore } from './sessions';\nimport { tokenStore } from './tokens';\nimport { userStore } from './users';\n\n/**\n * The three stores `janus()` takes, over one PostgreSQL database through\n * Drizzle.\n *\n * ```ts\n * const db = drizzle(process.env.DATABASE_URL);\n * const auth = janus({ user, password: { login: 'email' }, store: createDrizzleStores(db), hasher });\n * ```\n *\n * **Every read is one statement**, whose absence is an empty result, turned\n * into `null` here; everything that arrives as a rejection is a failure. There\n * is no path on which an absence and an outage travel the same channel.\n *\n * **A user is written in one transaction** with its logins, whose table holds\n * their uniqueness — `@nxgt/drizzle`'s `withTransaction`, inside the method,\n * as rule 5 allows. Every other write is one statement.\n *\n * `sessions.deleteExpiredSessions` is implemented: PostgreSQL has no TTL, so\n * `auth.collectExpired()` is how lapsed sessions leave the table.\n *\n * `{ tables }` is what your schema file exports; absent, the tables in the\n * connection's `search_path`.\n */\nexport function createDrizzleStores(\n\tdb: PgDatabase,\n\toptions: DrizzleAdapterOptions<IdentityTable> = {},\n): JanusStores {\n\tconst tables = options.tables ?? defineJanusTables();\n\treturn {\n\t\tusers: userStore(db, tables),\n\t\tsessions: sessionStore(db, tables),\n\t\ttokens: tokenStore(db, tables),\n\t};\n}\n",
23
23
  "import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { JanusStores } from '@nxgt/janus';\nimport type { RelationStore } from '@nxgt/janus/permissions';\nimport { createDrizzleRelations } from './relations';\nimport { createDrizzleStores } from './stores';\nimport type { DrizzleAdapterOptions } from './tables';\n\n/** The whole adapter over one database, keyed as `janus()` takes it. */\nexport interface DrizzleAdapter {\n\t/** The identity stores: `createDrizzleStores(db)`. */\n\treadonly store: JanusStores;\n\t/** The relation store: `createDrizzleRelations(db)`. */\n\treadonly relations: RelationStore;\n}\n\n/**\n * The identity stores and the relation store over one database, under the\n * names `janus()` takes them — so one spread wires both, and deleting a user\n * deletes every tuple naming them:\n *\n * ```ts\n * const postgres = createDrizzleAdapter(db);\n * const auth = janus({ user, password: { login: 'email' }, hasher, ...postgres });\n * const access = permissions({ model, store: postgres.relations });\n * ```\n *\n * It connects to nothing and creates nothing: `db` is your Drizzle instance,\n * and the tables are your migrations'. `{ tables }` is what your schema file\n * exports, needed when they are in a PostgreSQL schema of their own. An\n * application that only authenticates can take `createDrizzleStores(db)` alone.\n */\nexport function createDrizzleAdapter(\n\tdb: PgDatabase,\n\toptions: DrizzleAdapterOptions = {},\n): DrizzleAdapter {\n\treturn {\n\t\tstore: createDrizzleStores(db, options),\n\t\trelations: createDrizzleRelations(db, options),\n\t};\n}\n"
24
24
  ],
25
- "mappings": "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",
26
- "debugId": "3F6F0185055BC57C64756E2164756E21",
25
+ "mappings": ";AAAA;;;ACAA;;;ACEA;AAUO,IAAM,MAAM,WAAiD;AAAA,EACnE,UAAU,MAAM;AACjB,CAAC;AAKM,IAAM,KAAK,CAAC,SAClB,UAAU,MAAM,EAAE,cAAc,MAAM,WAAW,EAAE,CAAC;;;ADT9C,SAAS,WAAW,CAC1B,OACA,OACC;AAAA,EACD,OAAO,MACN,UACA;AAAA,IACC,MAAM,IAAI,MAAM,EAAE,QAAQ;AAAA,IAC1B,OAAO,IAAI,OAAO,EAAE,QAAQ;AAAA,IAC5B,QAAQ,IAAI,SAAS,EAAE,QAAQ;AAAA,EAChC,GACA,CAAC,MAAM;AAAA,IACN,WAAW,EAAE,MAAM,eAAe,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAAA,IAC9D,WAAW;AAAA,MACV,MAAM;AAAA,MACN,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI;AAAA,MAC1B,gBAAgB,CAAC,MAAM,IAAI,MAAM,IAAI;AAAA,IACtC,CAAC,EAAE,SAAS,SAAS;AAAA,IACrB,MAAM,gBAAgB,EAAE,GAAG,EAAE,MAAM;AAAA,EACpC,CACD;AAAA;;;AE/BD,kBAAS;AAaF,SAAS,cAAc,CAAC,OAAqB;AAAA,EACnD,OAAO,MACN,aACA;AAAA,IACC,YAAY,IAAI,aAAa,EAAE,QAAQ;AAAA,IACvC,UAAU,IAAI,WAAW,EAAE,QAAQ;AAAA,IACnC,UAAU,IAAI,UAAU,EAAE,QAAQ;AAAA,IAClC,aAAa,IAAI,cAAc,EAAE,QAAQ;AAAA,IACzC,WAAW,IAAI,YAAY,EAAE,QAAQ;AAAA,IACrC,iBAAiB,IAAI,kBAAkB;AAAA,EACxC,GACA,CAAC,MAAM;AAAA,IACN,OAAO,wBAAwB,EAC7B,GACA,EAAE,aACF,EAAE,WACF,EAAE,iBACF,EAAE,UACF,EAAE,YACF,EAAE,QACH,EACC,iBAAiB;AAAA,IAEnB,OAAM,kBAAkB,EAAE,GAAG,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ;AAAA,EAClE,CACD;AAAA;;;ACtCD,kBAAS,kBAAO;AAQT,SAAS,aAAa,CAAC,OAAqB;AAAA,EAClD,OAAO,MACN,YACA;AAAA,IACC,IAAI,IAAI,IAAI,EAAE,WAAW;AAAA,IAEzB,WAAW,IAAI,YAAY,EAAE,QAAQ;AAAA,IACrC,QAAQ,IAAI,SAAS,EAAE,QAAQ;AAAA,IAC/B,iBAAiB,GAAG,kBAAkB,EAAE,QAAQ;AAAA,IAChD,WAAW,GAAG,YAAY,EAAE,QAAQ;AAAA,IACpC,WAAW,GAAG,YAAY;AAAA,IAC1B,WAAW,GAAG,YAAY,EAAE,QAAQ;AAAA,EACrC,GACA,CAAC,MAAM;AAAA,IACN,QAAO,4BAA4B,EAAE,GAAG,EAAE,SAAS;AAAA,IACnD,OAAM,kBAAkB,EAAE,GAAG,EAAE,MAAM;AAAA,IAErC,OAAM,qBAAqB,EAAE,GAAG,EAAE,SAAS;AAAA,EAC5C,CACD;AAAA;;;AC3BD;AACA,yBAAgB;AAIT,SAAS,WAAW,CAAC,OAAqB;AAAA,EAChD,OAAO,MACN,UACA;AAAA,IACC,WAAW,IAAI,YAAY,EAAE,WAAW;AAAA,IACxC,MAAM,KAAK,QAAQ;AAAA,MAClB,MAAM,CAAC,eAAe,iBAAiB,gBAAgB,YAAY;AAAA,IACpE,CAAC,EAAE,QAAQ;AAAA,IACX,QAAQ,IAAI,SAAS,EAAE,QAAQ;AAAA,IAC/B,SAAS,KAAK,SAAS,EAAE,QAAQ;AAAA,IAEjC,UAAU,KAAK,WAAW;AAAA,IAE1B,UAAU,QAAQ,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IACjD,WAAW,GAAG,YAAY,EAAE,QAAQ;AAAA,IACpC,SAAS,GAAG,UAAU;AAAA,IACtB,WAAW,GAAG,YAAY,EAAE,QAAQ;AAAA,EACrC,GACA,CAAC,MAAM;AAAA,IACN,MACC,eACA,MAAM,EAAE,wEACT;AAAA,IACA,MAAM,mBAAmB,MAAM,EAAE,eAAe;AAAA,IAChD,OAAM,gBAAgB,EAAE,GAAG,EAAE,MAAM;AAAA,EACpC,CACD;AAAA;;;AC/BD,gBAAS;AACT;AAAA;AAAA,WAEC;AAAA,WACA;AAAA,aACA;AAAA;AAAA,UAEA;AAAA,YACA;AAAA;AAQM,SAAS,UAAU,CAAC,OAAqB;AAAA,EAC/C,OAAO,MACN,SACA;AAAA,IAEC,IAAI,IAAI,IAAI,EAAE,WAAW;AAAA,IACzB,MAAM,IAAI,MAAM,EAAE,QAAQ;AAAA,IAC1B,eAAe,MAAK,gBAAgB,EAAE,QAAQ;AAAA,IAC9C,QAAQ,QAAQ,QAAQ,EAAE,QAAQ;AAAA,IAElC,QAAQ,MAAM,QAAQ,EAAE,QAAQ;AAAA,IAChC,QAAQ,MAAK,QAAQ,EAAE,MAAM,EAAE,QAAQ;AAAA,IACvC,cAAc,MAAK,eAAe;AAAA,IAClC,mBAAmB,GAAG,qBAAqB;AAAA,IAE3C,oBAAoB,MAAK,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AAAA,IAEnE,oBAAoB,MAAK,sBAAsB;AAAA,IAC/C,yBAAyB,GAAG,4BAA4B;AAAA,IACxD,sBAAsB,SAAQ,yBAAyB;AAAA,IAMvD,2BAA2B,MAAK,8BAA8B,EAAE,MAAM;AAAA,IACtE,iBAAiB,GAAG,mBAAmB;AAAA,IACvC,SAAS,SAAQ,SAAS,EAAE,QAAQ;AAAA,IACpC,WAAW,GAAG,YAAY,EAAE,QAAQ;AAAA,IACpC,WAAW,GAAG,YAAY,EAAE,QAAQ;AAAA,EACrC,GACA,CAAC,MAAM;AAAA,IAEN,QAAO,sBAAsB,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI;AAAA,IAE9C,OAAM,eAAe,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;AAAA,IAEtC,OACC,wBACA,QAAO,EAAE,4BAA4B,EAAE,4BACxC;AAAA,IAKA,OACC,6BACA,QAAO,EAAE,kCAAkC,EAAE,oCAAoC,EAAE,sCAAsC,EAAE,uCAAuC,EAAE,oCAAoC,EAAE,qCAC3M;AAAA,IAEA,OACC,8BACA,OAAM,EAAE,sCAAsC,EAAE,2BACjD;AAAA,EACD,CACD;AAAA;;;ANfM,SAAS,iBAAiB,CAAC,UAA8B,CAAC,GAAG;AAAA,EACnE,QAAQ,WAAW;AAAA,EACnB,MAAM,QAAS,WAAW,YAAY,UAAU,OAAO;AAAA,EACvD,MAAM,QAAQ,WAAW,KAAK;AAAA,EAC9B,MAAM,SAAS,YAAY,OAAO,KAAK;AAAA,EACvC,MAAM,WAAW,cAAc,KAAK;AAAA,EACpC,MAAM,SAAS,YAAY,KAAK;AAAA,EAChC,MAAM,YAAY,eAAe,KAAK;AAAA,EACtC,OAAO,EAAE,OAAO,QAAQ,UAAU,QAAQ,UAAU;AAAA;;;AOhErD;AACA;AA8BA,eAAsB,GAAM,CAC3B,MACA,WACA,MACa;AAAA,EACb,IAAI;AAAA,IACH,OAAO,MAAM,KAAK;AAAA,IACjB,OAAO,OAAO;AAAA,IAEf,IAAI,iBAAiB;AAAA,MAAY,MAAM;AAAA,IACvC,MAAM,IAAI,aAAa,GAAG,QAAQ,yCAAyC;AAAA,MAC1E;AAAA,MACA;AAAA,MACA,OAAO,YAAY,KAAK;AAAA,IACzB,CAAC;AAAA;AAAA;AAKI,SAAS,UAAU,CACzB,WACA,MACA,OACgB;AAAA,EAChB,OAAO,IAAI,cACV,SACA,GAAG,4CAA4C,QAC/C,EAAE,OAAO,UAAU,MAAM,UAAU,CACpC;AAAA;;;ACpDD,gBAAS,iBAAU,8BAAmB;;;ACPtC;AACA;AAIO,SAAS,OAAO,CACtB,QAC2B;AAAA,EAC3B,MAAM,QAAQ,CAAC,UACd,KAAK,UAAU;AAAA,IACd,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,IACb,MAAM;AAAA,IACN,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,aAAa,MAAM,OAAO,IAAI,MAAM,QAAQ,WAAW;AAAA,EACxD,CAAC;AAAA,EACF,OAAO,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM;AAAA,IACjC,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAAA,IAClC,OAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI;AAAA,GAChC;AAAA;AAIK,SAAS,KAAK,CAAC,OAAiD;AAAA,EACtE,OAAO;AAAA,IACN,YAAY,MAAM,OAAO;AAAA,IACzB,UAAU,MAAM,OAAO;AAAA,IACvB,UAAU,MAAM;AAAA,IAChB,aAAa,MAAM,QAAQ;AAAA,IAC3B,WAAW,MAAM,QAAQ;AAAA,IACzB,iBAAiB,aAAa,MAAM,OAAO,IACxC,MAAM,QAAQ,WACd;AAAA,EACJ;AAAA;AAIM,SAAS,SAAS,CACxB,WACA,SACkB;AAAA,EAClB,OAAO,IACN,GAAG,UAAU,aAAa,QAAQ,IAAI,GACtC,GAAG,UAAU,WAAW,QAAQ,EAAE,GAClC,aAAa,OAAO,IACjB,GAAG,UAAU,iBAAiB,QAAQ,QAAQ,IAC9C,OAAO,UAAU,eAAe,CACpC;AAAA;AAIM,SAAS,QAAQ,CACvB,WACA,OACkB;AAAA,EAClB,OAAO,IACN,GAAG,UAAU,YAAY,MAAM,OAAO,IAAI,GAC1C,GAAG,UAAU,UAAU,MAAM,OAAO,EAAE,GACtC,GAAG,UAAU,UAAU,MAAM,QAAQ,GACrC,UAAU,WAAW,MAAM,OAAO,CACnC;AAAA;;;ADlDD,eAAsB,GAAG,GACtB,IAAI,aACN,OACmB;AAAA,EACnB,MAAM,QAAQ,MAAM,GAClB,OAAO,EAAE,UAAU,UAAU,SAAS,CAAC,EACvC,KAAK,SAAS,EACd,MAAM,SAAS,WAAW,KAAK,CAAC,EAChC,MAAM,CAAC;AAAA,EACT,OAAO,MAAM,WAAW;AAAA;AAGzB,eAAsB,eAAe,CACpC,SACA,QACA,UACwB;AAAA,EACxB,QAAQ,MAAM,QAAQ,SAAS,QAAQ,UAAU,IAAI,GAAG,IACvD,CAAC,SAAqB;AAAA,IACrB,MAAM,IAAI;AAAA,IACV,IAAI,IAAI;AAAA,IACR,UAAU,IAAI,YAAY;AAAA,EAC3B,EACD;AAAA;AAGD,eAAsB,YAAY,CACjC,SACA,QACA,UACoB;AAAA,EACpB,QAAQ,MAAM,QAAQ,SAAS,QAAQ,UAAU,KAAK,GAAG,IACxD,CAAC,SAAiB,EAAE,MAAM,IAAI,MAAM,IAAI,IAAI,GAAG,EAChD;AAAA;AAID,SAAS,OAAO,GACb,IAAI,aACN,QACA,UACA,MACC;AAAA,EACD,OAAO,GACL,OAAO;AAAA,IACP,MAAM,UAAU;AAAA,IAChB,IAAI,UAAU;AAAA,IACd,UAAU,UAAU;AAAA,EACrB,CAAC,EACA,KAAK,SAAS,EACd,MACA,KACC,IAAG,UAAU,YAAY,OAAO,IAAI,GACpC,IAAG,UAAU,UAAU,OAAO,EAAE,GAChC,IAAG,UAAU,UAAU,QAAQ,GAC/B,OACG,UAAU,UAAU,eAAe,IACnC,QAAO,UAAU,eAAe,CACpC,CACD;AAAA;AAIF,eAAsB,WAAW,GAC9B,IAAI,eACJ,MAAM,UAAU,SAAS,OAAO,SACJ;AAAA,EAC9B,MAAM,aAAa;AAAA,IAClB,UAAU,WAAW,OAAO;AAAA,IAC5B,IAAG,UAAU,UAAU,QAAQ;AAAA,IAC/B,IAAG,UAAU,YAAY,IAAI;AAAA,EAC9B;AAAA,EACA,IAAI,UAAU;AAAA,IAAM,WAAW,KAAK,GAAG,UAAU,UAAU,KAAK,CAAC;AAAA,EACjE,MAAM,QAAQ,MAAM,GAClB,OAAO,EAAE,IAAI,UAAU,SAAS,CAAC,EACjC,KAAK,SAAS,EACd,MAAM,KAAI,GAAG,UAAU,CAAC,EACxB,QAAQ,IAAI,UAAU,QAAQ,CAAC,EAC/B,MAAM,QAAQ,CAAC;AAAA,EACjB,MAAM,QAAQ,MAAM,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;AAAA,EACvD,MAAM,OAAO,MAAM,GAAG,EAAE;AAAA,EACxB,OAAO;AAAA,IACN;AAAA,IACA,YAAY,MAAM,SAAS,SAAS,SAAS,YAAY,OAAO;AAAA,EACjE;AAAA;;;AE/FD;AAEA,gBAAS,YAAK;AAKd,eAAsB,KAAK,GACxB,IAAI,aACN,KACA,QACgB;AAAA,EAChB,IAAI,IAAI,SAAS,OAAO,UAAU;AAAA,IAAG,OAAO,MAAM,IAAI,WAAW,KAAK,MAAM;AAAA,EAC5E,MAAM,gBAAgB,IAAI,CAAC,OAAO,MAAM,IAAI,WAAW,KAAK,MAAM,CAAC;AAAA;AAIpE,eAAe,KAAK,CACnB,IACA,WACA,KACA,QACgB;AAAA,EAGhB,WAAW,SAAS,QAAQ,MAAM,GAAG;AAAA,IACpC,MAAM,GAAG,OAAO,SAAS,EAAE,MAAM,SAAS,WAAW,KAAK,CAAC;AAAA,EAC5D;AAAA,EACA,IAAI,IAAI,SAAS,GAAG;AAAA,IAEnB,MAAM,GACJ,OAAO,SAAS,EAChB,OAAO,QAAQ,GAAG,EAAE,IAAI,KAAK,CAAC,EAC9B,oBAAoB;AAAA,EACvB;AAAA;AAID,eAAsB,YAAY,GAC/B,IAAI,aACN,QACkB;AAAA,EAClB,MAAM,UAAU,MAAM,GACpB,OAAO,SAAS,EAChB,MACA,GACC,KACC,IAAG,UAAU,YAAY,OAAO,IAAI,GACpC,IAAG,UAAU,UAAU,OAAO,EAAE,CACjC,GACA,KACC,IAAG,UAAU,aAAa,OAAO,IAAI,GACrC,IAAG,UAAU,WAAW,OAAO,EAAE,CAClC,CACD,CACD,EACC,UAAU,EAAE,UAAU,UAAU,SAAS,CAAC;AAAA,EAC5C,OAAO,QAAQ;AAAA;;;ACpCT,SAAS,sBAAsB,CACrC,IACA,UAA8C,CAAC,GAC/B;AAAA,EAChB,MAAM,SAAS,QAAQ,UAAU,kBAAkB;AAAA,EACnD,MAAM,UAA2B,EAAE,IAAI,WAAW,OAAO,UAAU;AAAA,EACnE,MAAM,OAAO,CAAI,WAAmB,SACnC,IAAI,aAAa,WAAW,IAAI;AAAA,EAEjC,OAAO;AAAA,IACN,OAAO,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC,QAC7B,KAAK,SAAS,MAAM,MAAM,SAAS,KAAK,MAAM,CAAC;AAAA,IAChD,KAAK,CAAC,UAAU,KAAK,OAAO,MAAM,IAAI,SAAS,KAAK,CAAC;AAAA,IACrD,iBAAiB,CAAC,QAAQ,aACzB,KAAK,mBAAmB,MAAM,gBAAgB,SAAS,QAAQ,QAAQ,CAAC;AAAA,IACzE,cAAc,CAAC,QAAQ,aACtB,KAAK,gBAAgB,MAAM,aAAa,SAAS,QAAQ,QAAQ,CAAC;AAAA,IAInE,aAAa,GAAG,MAAM,UAAU,SAAS,OAAO,YAC/C,KAAK,eAAe,MACnB,YAAY,SAAS,EAAE,MAAM,UAAU,SAAS,OAAO,MAAM,CAAC,CAC/D;AAAA,IACD,cAAc,CAAC,WACd,KAAK,gBAAgB,MAAM,aAAa,SAAS,MAAM,CAAC;AAAA,EAC1D;AAAA;;;AC7CD,gBAAS,YAAK,eAAI,yBAAiB;;;ACDnC,gBAAmB;AAYZ,SAAS,SAAS,CAAC,QAAgC;AAAA,EACzD,OAAO;AAAA,IACN,IAAI,OAAO;AAAA,IACX,MAAM,OAAO;AAAA,IACb,eAAe,OAAO;AAAA,IACtB,QAAQ,OAAO;AAAA,IACf,QAAQ,OAAO;AAAA,IACf,QAAQ,CAAC,GAAG,OAAO,MAAM;AAAA,IACzB,cAAc,OAAO,UAAU,QAAQ;AAAA,IACvC,mBAAmB,OAAO,UAAU,aAAa;AAAA,OAC9C,sBAAsB,OAAO,YAAY;AAAA,IAC5C,iBAAiB,OAAO;AAAA,IACxB,SAAS,OAAO;AAAA,IAChB,WAAW,OAAO;AAAA,IAClB,WAAW,OAAO;AAAA,EACnB;AAAA;AAQM,SAAS,SAAS,CAAC,OAAuC;AAAA,EAChE,MAAM,MAA2B;AAAA,IAChC,WAAW,MAAM;AAAA,EAClB;AAAA,EACA,IAAI,MAAM,kBAAkB;AAAA,IAC3B,IAAI,gBAAgB,MAAM;AAAA,EAC3B,IAAI,MAAM,WAAW;AAAA,IAAW,IAAI,SAAS,MAAM;AAAA,EACnD,IAAI,MAAM,WAAW;AAAA,IAAW,IAAI,SAAS,MAAM;AAAA,EACnD,IAAI,MAAM,WAAW;AAAA,IAAW,IAAI,SAAS,CAAC,GAAG,MAAM,MAAM;AAAA,EAC7D,IAAI,MAAM,oBAAoB;AAAA,IAC7B,IAAI,kBAAkB,MAAM;AAAA,EAC7B,IAAI,MAAM,aAAa,WAAW;AAAA,IACjC,IAAI,eAAe,MAAM,UAAU,QAAQ;AAAA,IAC3C,IAAI,oBAAoB,MAAM,UAAU,aAAa;AAAA,EACtD;AAAA,EACA,IAAI,MAAM,iBAAiB,WAAW;AAAA,IACrC,OAAO,OAAO,KAAK,sBAAsB,MAAM,YAAY,CAAC;AAAA,EAC7D;AAAA,EACA,OAAO;AAAA;AASR,SAAS,qBAAqB,CAAC,cAA0C;AAAA,EACxE,MAAM,QAAQ,cAAc,iBAAiB,CAAC;AAAA,EAC9C,OAAO;AAAA,IACN,oBAAoB,cAAc,UAAU;AAAA,IAC5C,oBAAoB,cAAc,UAAU;AAAA,IAC5C,yBAAyB,cAAc,eAAe;AAAA,IACtD,sBAAsB,cAAc,YAAY;AAAA,IAChD,2BAA2B,MAAM,WAAW,IAAI,OAAO,CAAC,GAAG,KAAK;AAAA,EACjE;AAAA;AAGM,SAAS,MAAM,CAAC,KAA0B;AAAA,EAChD,OAAO;AAAA,IACN,IAAI,IAAI;AAAA,IACR,MAAM,IAAI;AAAA,IACV,eAAe,IAAI;AAAA,IACnB,QAAQ,IAAI;AAAA,IACZ,QAAQ,IAAI;AAAA,IACZ,QAAQ,CAAC,GAAG,IAAI,MAAM;AAAA,IACtB,UACC,IAAI,iBAAiB,QAAQ,IAAI,sBAAsB,OACpD,OACA,EAAE,MAAM,IAAI,cAAc,WAAW,IAAI,kBAAkB;AAAA,IAC/D,cACC,IAAI,uBAAuB,QAAQ,IAAI,uBAAuB,OAC3D,OACA;AAAA,MACA,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,MACd,eAAe,CAAC,GAAI,IAAI,6BAA6B,CAAC,CAAE;AAAA,IACzD;AAAA,IACH,iBAAiB,IAAI;AAAA,IACrB,SAAS,IAAI;AAAA,IACb,WAAW,IAAI;AAAA,IACf,WAAW,IAAI;AAAA,EAChB;AAAA;AAGM,SAAS,SAAS,CAAC,KAAgC;AAAA,EACzD,OAAO;AAAA,IACN,IAAI,IAAI;AAAA,IACR,WAAW,IAAI;AAAA,IACf,QAAQ,IAAI;AAAA,IACZ,iBAAiB,IAAI;AAAA,IACrB,WAAW,IAAI;AAAA,IACf,WAAW,IAAI;AAAA,IACf,WAAW,IAAI;AAAA,EAChB;AAAA;AAQM,SAAS,KAAK,CAAC,IAAe;AAAA,EACpC,OAAO,OAAM,GAAG,YAAY;AAAA;;;ADlHtB,SAAS,YAAY,CAC3B,IACA,QACe;AAAA,EACf,MAAM,OAAO,CAAI,WAAmB,SACnC,IAAI,YAAY,WAAW,IAAI;AAAA,EAEhC,OAAO;AAAA,IACN,eAAe,CAAC,WACf,KAAK,iBAAiB,YAAY;AAAA,MAGjC,MAAM,GACJ,OAAO,OAAO,QAAQ,EACtB,OAAO,MAAM,EACb,oBAAoB,EAAE,QAAQ,OAAO,SAAS,GAAG,CAAC;AAAA,KACpD;AAAA,IAEF,wBAAwB,CAAC,cACxB,KAAK,0BAA0B,YAAY;AAAA,MAC1C,OAAO,OAAO,MAAM,GAClB,OAAO,EACP,KAAK,OAAO,QAAQ,EACpB,MAAM,IAAG,OAAO,SAAS,WAAW,SAAS,CAAC;AAAA,MAChD,OAAO,QAAQ,YAAY,OAAO,UAAU,GAAG;AAAA,KAC/C;AAAA,IAEF,eAAe,CAAC,IAAI,cACnB,KAAK,iBAAiB,YAAY;AAAA,MAGjC,OAAO,OAAO,MAAM,GAClB,OAAO,OAAO,QAAQ,EACtB,IAAI,EAAE,UAAU,CAAC,EACjB,MACA,KAAI,IAAG,OAAO,SAAS,IAAI,EAAE,GAAG,QAAO,OAAO,SAAS,SAAS,CAAC,CAClE,EACC,UAAU;AAAA,MACZ,OAAO,QAAQ,YAAY,OAAO,UAAU,GAAG;AAAA,KAC/C;AAAA,IAEF,eAAe,CAAC,IAAI,OACnB,KAAK,iBAAiB,MAAM,cAAc,IAAI,QAAQ,IAAI,EAAE,CAAC;AAAA,IAE9D,oBAAoB,CAAC,QAAQ,IAAI,WAChC,KAAK,sBAAsB,MAC1B,mBAAmB,IAAI,QAAQ,QAAQ,IAAI,MAAM,CAClD;AAAA,IAED,oBAAoB,CAAC,WACpB,KAAK,sBAAsB,YAAY;AAAA,MACtC,MAAM,UAAU,MAAM,GACpB,OAAO,OAAO,QAAQ,EACtB,MAAM,IAAG,OAAO,SAAS,QAAQ,MAAM,CAAC,EACxC,UAAU,EAAE,IAAI,OAAO,SAAS,GAAG,CAAC;AAAA,MACtC,OAAO,QAAQ;AAAA,KACf;AAAA,IAEF,uBAAuB,CAAC,WACvB,KAAK,yBAAyB,YAAY;AAAA,MACzC,MAAM,UAAU,MAAM,GACpB,OAAO,OAAO,QAAQ,EACtB,MAAM,IAAI,OAAO,SAAS,WAAW,MAAM,CAAC,EAC5C,UAAU,EAAE,IAAI,OAAO,SAAS,GAAG,CAAC;AAAA,MACtC,OAAO,QAAQ;AAAA,KACf;AAAA,EACH;AAAA;AAGD,eAAe,aAAa,CAC3B,IACA,QACA,IACA,IACmB;AAAA,EAGnB,MAAM,UAAU,MAAM,GACpB,OAAO,OAAO,QAAQ,EACtB,IAAI;AAAA,IACJ,WAAW,gBAAe,OAAO,SAAS,cAAc,MAAM,EAAE;AAAA,EACjE,CAAC,EACA,MAAM,IAAG,OAAO,SAAS,IAAI,EAAE,CAAC,EAChC,UAAU,EAAE,IAAI,OAAO,SAAS,GAAG,CAAC;AAAA,EACtC,OAAO,QAAQ,WAAW;AAAA;AAG3B,eAAe,kBAAkB,CAChC,IACA,QACA,QACA,IACA,QACkB;AAAA,EAClB,MAAM,WAAW,KAChB,IAAG,OAAO,SAAS,QAAQ,MAAM,GACjC,QAAO,OAAO,SAAS,SAAS,CACjC;AAAA,EACA,MAAM,UAAU,MAAM,GACpB,OAAO,OAAO,QAAQ,EACtB,IAAI,EAAE,WAAW,GAAG,CAAC,EACrB,MACA,WAAW,YACR,WACA,KAAI,UAAU,GAAG,OAAO,SAAS,IAAI,MAAM,CAAC,CAChD,EACC,UAAU,EAAE,IAAI,OAAO,SAAS,GAAG,CAAC;AAAA,EACtC,OAAO,QAAQ;AAAA;;;AEhHhB,gBAAS,YAAK,eAAI,eAAQ,YAAI;AAKvB,SAAS,UAAU,CAAC,IAAgB,QAAoC;AAAA,EAC9E,MAAM,OAAO,CAAI,WAAmB,SACnC,IAAI,UAAU,WAAW,IAAI;AAAA,EAE9B,OAAO;AAAA,IACN,aAAa,CAAC,WACb,KAAK,eAAe,YAAY;AAAA,MAE/B,MAAM,GAAG,OAAO,OAAO,MAAM,EAAE,OAAO,MAAM,EAAE,oBAAoB;AAAA,KAClE;AAAA,IAEF,cAAc,CAAC,WAAW,MAAM,OAC/B,KAAK,gBAAgB,MAAM,aAAa,IAAI,QAAQ,WAAW,MAAM,EAAE,CAAC;AAAA,IAEzE,cAAc,CAAC,WAAW,SACzB,KAAK,gBAAgB,MAAM,aAAa,IAAI,QAAQ,WAAW,IAAI,CAAC;AAAA,IAErE,iBAAiB,CAAC,QAAQ,MAAM,IAAI,WACnC,KAAK,mBAAmB,YAAY;AAAA,MAInC,MAAM,QAAQ,MAAM,GAClB,OAAO,OAAO,MAAM,EACpB,IAAI,EAAE,SAAS,GAAG,CAAC,EACnB,MACA,KACC,IAAG,OAAO,OAAO,QAAQ,MAAM,GAC/B,IAAG,OAAO,OAAO,MAAM,IAAI,GAC3B,QAAO,OAAO,OAAO,OAAO,GAC5B,WAAW,YACR,YACA,IAAG,OAAO,OAAO,WAAW,MAAM,CACtC,CACD,EACC,UAAU,EAAE,WAAW,OAAO,OAAO,UAAU,CAAC;AAAA,MAClD,OAAO,MAAM;AAAA,KACb;AAAA,IAEF,kBAAkB,CAAC,WAClB,KAAK,oBAAoB,YAAY;AAAA,MACpC,MAAM,UAAU,MAAM,GACpB,OAAO,OAAO,MAAM,EACpB,MAAM,IAAG,OAAO,OAAO,QAAQ,MAAM,CAAC,EACtC,UAAU,EAAE,WAAW,OAAO,OAAO,UAAU,CAAC;AAAA,MAClD,OAAO,QAAQ;AAAA,KACf;AAAA,EACH;AAAA;AAGD,eAAe,YAAY,CAC1B,IACA,QACA,WACA,MACA,IAC8B;AAAA,EAK9B,MAAM,SAAS,GAAG,MAAM,QAAQ,EAAE,GACjC,GACE,OAAO,EACP,KAAK,OAAO,MAAM,EAClB,MACA,KACC,IAAG,OAAO,OAAO,WAAW,SAAS,GACrC,IAAG,OAAO,OAAO,MAAM,IAAI,CAC5B,CACD,EACC,IAAI,QAAQ,CACf;AAAA,EACA,OAAO,SAAS,MAAM,GACpB,KAAK,MAAM,EACX,OAAO,OAAO,MAAM,EACpB,IAAI;AAAA,IACJ,SAAS,gBAAe,OAAO,OAAO,YAAY,MAAM,EAAE;AAAA,EAC3D,CAAC,EACA,KAAK,MAAM,EACX,MAAM,IAAG,OAAO,OAAO,WAAW,OAAO,SAAS,CAAC,EACnD,UAAU;AAAA,IACV,WAAW,OAAO;AAAA,IAClB,MAAM,OAAO;AAAA,IACb,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,UAAU,OAAO;AAAA,IACjB,UAAU,OAAO;AAAA,IACjB,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,IAChB,WAAW,OAAO;AAAA,EACnB,CAAC;AAAA,EACF,OAAO,UAAU,YAAY,OAAO,KAAK,OAAO,QAAQ,MAAM,OAAa;AAAA;AAG5E,eAAe,YAAY,CAC1B,IACA,QACA,WACA,MAC8B;AAAA,EAI9B,MAAM,QAAQ,KACb,IAAG,OAAO,OAAO,WAAW,SAAS,GACrC,IAAG,OAAO,OAAO,MAAM,IAAI,CAC5B;AAAA,EACA,OAAO,WAAW,MAAM,GACtB,OAAO,OAAO,MAAM,EACpB,IAAI,EAAE,UAAU,OAAM,OAAO,OAAO,eAAe,CAAC,EACpD,MAAM,KAAI,OAAO,QAAO,OAAO,OAAO,OAAO,CAAC,CAAC,EAC/C,UAAU;AAAA,EACZ,MAAM,MACL,YAAY,MAAM,GAAG,OAAO,EAAE,KAAK,OAAO,MAAM,EAAE,MAAM,KAAK,GAAG;AAAA,EACjE,OAAO,QAAQ,YAAY,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAa;AAAA;;;AC1HtE,4BAA0B;AAE1B,yCAAwB;AACxB,gBAAS,aAAK,YAAK,WAAI,YAAI;;;ACO3B,eAAsB,WAAW,CAChC,IACA,QACA,WACA,MACA,QACgB;AAAA,EAGhB,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,EAAE,KAAK;AAAA,EAC3C,IAAI,SAAS,WAAW;AAAA,IAAG;AAAA,EAC3B,MAAM,UAAU,MAAM,GACpB,OAAO,OAAO,MAAM,EACpB,OACA,SAAS,IAAI,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,OAAO,QAAQ,KAAK,GAAG,EAAE,CACtE,EACC,oBAAoB,EACpB,UAAU,EAAE,OAAO,OAAO,OAAO,MAAM,CAAC;AAAA,EAC1C,IAAI,QAAQ,WAAW,SAAS;AAAA,IAAQ;AAAA,EACxC,MAAM,UAAU,IAAI,IAAI,QAAQ,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;AAAA,EACvD,MAAM,QAAQ,SAAS,KAAK,CAAC,UAAU,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK;AAAA,EAC/D,MAAM,WAAW,WAAW,KAAK,MAAM,KAAK;AAAA;;;ADtBtC,SAAS,SAAS,CAAC,IAAgB,QAAmC;AAAA,EAC5E,MAAM,OAAO,CAAI,WAAmB,SACnC,IAAI,SAAS,WAAW,IAAI;AAAA,EAE7B,OAAO;AAAA,IACN,YAAY,CAAC,WACZ,KAAK,cAAc,MAAM,WAAW,IAAI,QAAQ,MAAM,CAAC;AAAA,IAExD,UAAU,CAAC,OAAO,KAAK,YAAY,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;AAAA,IAEjE,iBAAiB,CAAC,MAAM,UACvB,KAAK,mBAAmB,YAAY;AAAA,MACnC,OAAO,OAAO,MAAM,GAClB,OAAO,EAAE,MAAM,OAAO,MAAM,CAAC,EAC7B,KAAK,OAAO,MAAM,EAClB,UAAU,OAAO,OAAO,IAAG,OAAO,MAAM,IAAI,OAAO,OAAO,MAAM,CAAC,EACjE,MACA,KAAI,IAAG,OAAO,OAAO,MAAM,IAAI,GAAG,IAAG,OAAO,OAAO,OAAO,KAAK,CAAC,CACjE;AAAA,MACD,OAAO,QAAQ,YAAY,OAAO,OAAO,IAAI,IAAI;AAAA,KACjD;AAAA,IAEF,WAAW,GAAG,MAAM,OAAO,YAC1B,KAAK,aAAa,YAAY;AAAA,MAE7B,MAAM,QAAQ,MAAM,GAClB,OAAO,EACP,KAAK,OAAO,KAAK,EACjB,MACA,UAAU,OACP,IAAG,OAAO,MAAM,MAAM,IAAI,IAC1B,KAAI,IAAG,OAAO,MAAM,MAAM,IAAI,GAAG,IAAG,OAAO,MAAM,IAAI,KAAK,CAAC,CAC/D,EACC,QAAQ,KAAI,OAAO,MAAM,EAAE,CAAC,EAC5B,MAAM,QAAQ,CAAC;AAAA,MACjB,MAAM,QAAQ,MAAM,MAAM,GAAG,KAAK,EAAE,IAAI,MAAM;AAAA,MAC9C,MAAM,OAAO,MAAM,GAAG,EAAE;AAAA,MACxB,OAAO;AAAA,QACN;AAAA,QACA,YACC,MAAM,SAAS,SAAS,SAAS,YAAY,KAAK,KAAK;AAAA,MACzD;AAAA,KACA;AAAA,IAEF,YAAY,CAAC,IAAI,OAAO,cACvB,KAAK,cAAc,MAClB,iBAAgB,IAAI,CAAC,OACpB,WAAW,IAAI,QAAQ,IAAI,OAAO,SAAS,CAC5C,CACD;AAAA,IAED,YAAY,CAAC,OACZ,KAAK,cAAc,YAAY;AAAA,MAE9B,MAAM,UAAU,MAAM,GACpB,OAAO,OAAO,KAAK,EACnB,MAAM,IAAG,OAAO,MAAM,IAAI,EAAE,CAAC,EAC7B,UAAU,EAAE,IAAI,OAAO,MAAM,GAAG,CAAC;AAAA,MACnC,OAAO,QAAQ,WAAW;AAAA,KAC1B;AAAA,EACH;AAAA;AAGD,eAAe,QAAQ,CACtB,IACA,QACA,IAC6B;AAAA,EAC7B,OAAO,OAAO,MAAM,GAClB,OAAO,EACP,KAAK,OAAO,KAAK,EACjB,MAAM,IAAG,OAAO,MAAM,IAAI,EAAE,CAAC;AAAA,EAC/B,OAAO,QAAQ,YAAY,OAAO,OAAO,GAAG;AAAA;AAG7C,eAAe,UAAU,CACxB,IACA,QACA,QACsB;AAAA,EACtB,MAAM,WAAW,MAAM,iBAAgB,IAAI,OAAO,OAAO;AAAA,IACxD,OAAO,OAAO,MAAM,GAClB,OAAO,OAAO,KAAK,EACnB,OAAO,UAAU,MAAM,CAAC,EACxB,oBAAoB,EAAE,QAAQ,OAAO,MAAM,GAAG,CAAC,EAC/C,UAAU;AAAA,IACZ,IAAI,QAAQ;AAAA,MAAW,OAAO;AAAA,IAC9B,MAAM,YAAY,IAAI,QAAQ,cAAc,QAAQ,OAAO,MAAM;AAAA,IACjE,OAAO,OAAO,GAAG;AAAA,GACjB;AAAA,EACD,IAAI,aAAa;AAAA,IAAM,OAAO;AAAA,EAI9B,MAAM,SAAS,MAAM,SAAS,IAAI,QAAQ,OAAO,EAAE;AAAA,EACnD,IAAI,WAAW;AAAA,IAAM,OAAO;AAAA,EAE5B,MAAM,IAAI,cAAc,8CAA8C;AAAA,IACrE,QAAQ,OAAO;AAAA,IACf,WAAW;AAAA,EACZ,CAAC;AAAA;AAGF,eAAe,UAAU,CACxB,IACA,QACA,IACA,OACA,WACsB;AAAA,EACtB,OAAO,OAAO,MAAM,GAClB,OAAO,OAAO,KAAK,EACnB,IAAI;AAAA,OACD,UAAU,KAAK;AAAA,IAClB,SAAS,OAAM,OAAO,MAAM;AAAA,EAC7B,CAAC,EACA,MAAM,KAAI,IAAG,OAAO,MAAM,IAAI,EAAE,GAAG,IAAG,OAAO,MAAM,SAAS,SAAS,CAAC,CAAC,EACvE,UAAU;AAAA,EAEZ,IAAI,QAAQ,WAAW;AAAA,IAGtB,OAAO,UAAU,MAAM,GACrB,OAAO,EAAE,SAAS,OAAO,MAAM,QAAQ,CAAC,EACxC,KAAK,OAAO,KAAK,EACjB,MAAM,IAAG,OAAO,MAAM,IAAI,EAAE,CAAC;AAAA,IAC/B,IAAI,WAAW,WAAW;AAAA,MACzB,MAAM,IAAI,cAAc,mCAAmC;AAAA,QAC1D,QAAQ;AAAA,QACR,WAAW;AAAA,MACZ,CAAC;AAAA,IACF;AAAA,IACA,MAAM,IAAI,eACT,WACA,gCAAgC,oBAAoB,OAAO,WAC3D;AAAA,MACC,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,eAAe,OAAO;AAAA,MACtB,WAAW;AAAA,IACZ,CACD;AAAA,EACD;AAAA,EAEA,IAAI,MAAM,WAAW,WAAW;AAAA,IAC/B,MAAM,GAAG,OAAO,OAAO,MAAM,EAAE,MAAM,IAAG,OAAO,OAAO,QAAQ,EAAE,CAAC;AAAA,IACjE,MAAM,YAAY,IAAI,QAAQ,cAAc,KAAK,MAAM,MAAM;AAAA,EAC9D;AAAA,EACA,OAAO,OAAO,GAAG;AAAA;;;AE9HX,SAAS,mBAAmB,CAClC,IACA,UAAgD,CAAC,GACnC;AAAA,EACd,MAAM,SAAS,QAAQ,UAAU,kBAAkB;AAAA,EACnD,OAAO;AAAA,IACN,OAAO,UAAU,IAAI,MAAM;AAAA,IAC3B,UAAU,aAAa,IAAI,MAAM;AAAA,IACjC,QAAQ,WAAW,IAAI,MAAM;AAAA,EAC9B;AAAA;;;ACTM,SAAS,oBAAoB,CACnC,IACA,UAAiC,CAAC,GACjB;AAAA,EACjB,OAAO;AAAA,IACN,OAAO,oBAAoB,IAAI,OAAO;AAAA,IACtC,WAAW,uBAAuB,IAAI,OAAO;AAAA,EAC9C;AAAA;",
26
+ "debugId": "BBBB8A4136A5079264756E2164756E21",
27
27
  "names": []
28
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/stores/records.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAM,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,KAAK,GAAG,EAAO,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAO7C,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACpD,KAAK,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACvD,KAAK,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;AAE1D,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAgBxD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB/D;AAYD,wBAAgB,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CA0B/C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,aAAa,CAUxD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,IAAI,GAAG,GAAG,CAEnC"}
1
+ {"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/stores/records.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAM,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,KAAK,GAAG,EAAO,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAO7C,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACpD,KAAK,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACvD,KAAK,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;AAE1D,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAgBxD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB/D;AAmBD,wBAAgB,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CA2B/C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,aAAa,CAUxD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,IAAI,GAAG,GAAG,CAEnC"}
@@ -238,6 +238,21 @@ export declare function defineJanusTables(options?: JanusTablesOptions): {
238
238
  identity: undefined;
239
239
  generated: undefined;
240
240
  }>;
241
+ secondFactorRecoveryCodes: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").SetDimensions<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, 1>, {
242
+ name: string;
243
+ tableName: "users";
244
+ dataType: "string";
245
+ data: string[];
246
+ driverParam: string | string[];
247
+ notNull: false;
248
+ hasDefault: false;
249
+ isPrimaryKey: false;
250
+ isAutoincrement: false;
251
+ hasRuntimeDefault: false;
252
+ enumValues: undefined;
253
+ identity: undefined;
254
+ generated: undefined;
255
+ }>;
241
256
  emailVerifiedAt: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").PgTimestampBuilder, {
242
257
  name: string;
243
258
  tableName: "users";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAW,MAAM,qBAAqB,CAAC;AAQ7D;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6BAA6B;AAC7B,MAAM,WAAW,kBAAkB;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASjE;AAED,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB,CACrC,CAAC,SAAS,MAAM,WAAW,GAAG,MAAM,WAAW;IAE/C,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;CACvC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAW,MAAM,qBAAqB,CAAC;AAQ7D;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6BAA6B;AAC7B,MAAM,WAAW,kBAAkB;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASjE;AAED,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB,CACrC,CAAC,SAAS,MAAM,WAAW,GAAG,MAAM,WAAW;IAE/C,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;CACvC"}
@@ -195,6 +195,21 @@ export declare function usersTable(table: TableFactory): import("drizzle-orm/pg-
195
195
  identity: undefined;
196
196
  generated: undefined;
197
197
  }>;
198
+ secondFactorRecoveryCodes: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").SetDimensions<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, 1>, {
199
+ name: string;
200
+ tableName: "users";
201
+ dataType: "string";
202
+ data: string[];
203
+ driverParam: string | string[];
204
+ notNull: false;
205
+ hasDefault: false;
206
+ isPrimaryKey: false;
207
+ isAutoincrement: false;
208
+ hasRuntimeDefault: false;
209
+ enumValues: undefined;
210
+ identity: undefined;
211
+ generated: undefined;
212
+ }>;
198
213
  emailVerifiedAt: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").PgTimestampBuilder, {
199
214
  name: string;
200
215
  tableName: "users";
@@ -1 +1 @@
1
- {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/tables/users.ts"],"names":[],"mappings":"AAUA,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkD7C"}
1
+ {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/tables/users.ts"],"names":[],"mappings":"AAUA,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwD7C"}
@@ -69,13 +69,14 @@ CREATE TABLE "users" (
69
69
  "second_factor_secret" text,
70
70
  "second_factor_confirmed_at" timestamp(3) with time zone,
71
71
  "second_factor_last_step" integer,
72
+ "second_factor_recovery_codes" text[],
72
73
  "email_verified_at" timestamp(3) with time zone,
73
74
  "version" integer NOT NULL,
74
75
  "created_at" timestamp(3) with time zone NOT NULL,
75
76
  "updated_at" timestamp(3) with time zone NOT NULL,
76
77
  CONSTRAINT "users_id_type_unique" UNIQUE("id","type"),
77
78
  CONSTRAINT "users_password_whole" CHECK (("password_hash" is null) = ("password_updated_at" is null)),
78
- CONSTRAINT "users_second_factor_whole" CHECK (("second_factor_method" is null) = ("second_factor_secret" is null) and ("second_factor_method" is not null or ("second_factor_confirmed_at" is null and "second_factor_last_step" is null))),
79
+ CONSTRAINT "users_second_factor_whole" CHECK (("second_factor_method" is null) = ("second_factor_secret" is null) and ("second_factor_method" is not null or ("second_factor_confirmed_at" is null and "second_factor_last_step" is null and "second_factor_recovery_codes" is null))),
79
80
  CONSTRAINT "users_second_factor_values" CHECK ("second_factor_method" in ('totp') and "second_factor_last_step" >= 0)
80
81
  );
81
82
  CREATE TABLE "logins" (
@@ -131,14 +132,19 @@ migration creates is what the conformance suites ran on.
131
132
  returned verbatim and in order, and as rows in `logins`, whose primary
132
133
  key is the per-type uniqueness. PostgreSQL has no unique index over the
133
134
  elements of an array.
134
- - **A second factor in four columns, checked whole.** A method and a secret,
135
- or neither; a confirmation date and a last step only beside them; `totp`
136
- the only method, and a last step never negative. The
135
+ - **A second factor in five columns, checked whole.** A method and a secret,
136
+ or neither; a confirmation date, a last step and recovery codes only beside
137
+ them; `totp` the only method, and a last step never negative. The
137
138
  secret is opaque to the store and kept byte for byte: `@nxgt/janus` seals
138
139
  it before the store sees it, so a dump of `users` cannot produce a code.
139
140
  Its key id is the second part of the value, so
140
141
  `where second_factor_secret like 'v1.<id>.%'` finds the secrets still sealed
141
142
  with a key being [rotated out](https://github.com/softistx/nxgt-janus/blob/develop/packages/janus/docs/guide/second-factor.md#rotating-the-keys).
143
+ - **Recovery codes as a `text[]`, `null` read as `[]`.** Each element is a
144
+ keyed hash the core wrote, never a code, kept in order. No codes — a
145
+ factor with none left, or no factor at all — is `null`, never `{}`. A row
146
+ from before the column existed reads as a factor with no codes, so an
147
+ upgrade rewrites nothing.
142
148
  - **`attempts` is `not null default 0`**, checked `>= 0`, so the rows an upgrade finds read
143
149
  as tokens with no attempt counted yet.
144
150
  - **No foreign key from sessions and tokens to users.** Deleting a user
@@ -182,6 +188,31 @@ Those are the statements drizzle-kit writes from the 0.1 tables to the 0.2
182
188
  ones, in its own order and separated by `--> statement-breakpoint`; with a
183
189
  PostgreSQL schema of its own, every name is qualified.
184
190
 
191
+ ### To 0.4: recovery codes
192
+
193
+ **A migration is required.** One nullable column, and the whole-factor check
194
+ extended to it, so the migration rewrites no row and every existing factor
195
+ reads as having no recovery codes until its user is given some:
196
+
197
+ ```sql
198
+ ALTER TABLE "users" ADD COLUMN "second_factor_recovery_codes" text[];
199
+ ALTER TABLE "users" DROP CONSTRAINT "users_second_factor_whole", ADD CONSTRAINT "users_second_factor_whole" CHECK (("second_factor_method" is null) = ("second_factor_secret" is null) and ("second_factor_method" is not null or ("second_factor_confirmed_at" is null and "second_factor_last_step" is null and "second_factor_recovery_codes" is null)));
200
+ ```
201
+
202
+ Those are the statements drizzle-kit writes from the 0.3 tables to the 0.4
203
+ ones. Re-adding the check validates every row, under a lock on `users` for
204
+ the length of one scan; the rows already satisfy it, since the column is new.
205
+ Deployed before the migration, every query on `users` fails with
206
+ `STORE_FAILED`, caused by `column "second_factor_recovery_codes" does not
207
+ exist`.
208
+
209
+ **Finish the rollout before users hold codes.** An instance still on 0.3
210
+ knows four columns: once a user holds recovery codes, its `disable` leaves
211
+ them beside a factor removed, and the check refuses the write with
212
+ `STORE_FAILED`. Nothing is lost — the call fails, and succeeds on an instance
213
+ of 0.4 — but run 0.3 and 0.4 side by side only while no user has been given
214
+ codes, which `@nxgt/janus` does from `secondFactor.activate` on.
215
+
185
216
  ## Collecting lapsed sessions
186
217
 
187
218
  PostgreSQL has no TTL. A lapsed session is refused on every read, but it stays
package/docs/roadmap.md CHANGED
@@ -36,6 +36,12 @@ Nothing yet.
36
36
 
37
37
  ## Shipped
38
38
 
39
+ - **Recovery codes on a second factor, v0.4.0** — for `@nxgt/janus` 0.10:
40
+ `users` gains `second_factor_recovery_codes`, a nullable `text[]` of the
41
+ codes' keyed hashes, read in order and `null` as `[]`, and the
42
+ `users_second_factor_whole` check keeps them to a row with a factor. A
43
+ migration, generated by drizzle-kit, adds the column without rewriting a
44
+ row.
39
45
  - **A user's tokens spent in one statement, v0.3.0** — for `@nxgt/janus`
40
46
  0.7: `spendUserTokens(userId, kind, at, except?)` spends the unspent
41
47
  tokens of one user and kind — but `except` — in one `update`, and passes
@@ -33,6 +33,7 @@ How PostgreSQL's errors become the port's:
33
33
  **Setup**
34
34
  - [`STORE_FAILED` caused by `relation "users" does not exist`](#store_failed-caused-by-relation-users-does-not-exist)
35
35
  - [`STORE_FAILED` caused by `column "second_factor_method" does not exist`](#store_failed-caused-by-column-second_factor_method-does-not-exist)
36
+ - [`STORE_FAILED` caused by `column "second_factor_recovery_codes" does not exist`](#store_failed-caused-by-column-second_factor_recovery_codes-does-not-exist)
36
37
  - [`STORE_FAILED` caused by `column "type" does not exist`](#store_failed-caused-by-column-type-does-not-exist)
37
38
  - [`schema "janus" does not exist` while migrating](#schema-janus-does-not-exist-while-migrating)
38
39
  - [`syntax error at or near "NULLS"` while migrating](#syntax-error-at-or-near-nulls-while-migrating)
@@ -204,6 +205,26 @@ bunx drizzle-kit generate --config drizzle.janus.config.ts
204
205
  bunx drizzle-kit migrate --config drizzle.janus.config.ts
205
206
  ```
206
207
 
208
+ ### `STORE_FAILED` caused by `column "second_factor_recovery_codes" does not exist`
209
+
210
+ The code is `42703`.
211
+
212
+ **When:** after upgrading to 0.4, on the first query that reads or writes a
213
+ user — a sign-up, a sign-in, `authenticate`.
214
+
215
+ **Why:** `users` gained the `second_factor_recovery_codes` column, which holds
216
+ the recovery codes' hashes, and the migration that adds it was not generated,
217
+ or not applied.
218
+
219
+ **Fix:** generate and apply it before the new version serves requests. It adds
220
+ one nullable column and re-creates the `users_second_factor_whole` check; see
221
+ [To 0.4](guide/migrations.md#to-04-recovery-codes).
222
+
223
+ ```sh
224
+ bunx drizzle-kit generate --config drizzle.janus.config.ts
225
+ bunx drizzle-kit migrate --config drizzle.janus.config.ts
226
+ ```
227
+
207
228
  ### `STORE_FAILED` caused by `column "type" does not exist`
208
229
 
209
230
  Also `column "type" of relation "users" does not exist`, or another column of
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/janus-drizzle",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "The PostgreSQL adapter for @nxgt/janus on Drizzle and @nxgt/drizzle: users, sessions, one-time tokens and permission tuples",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -51,7 +51,7 @@
51
51
  "devDependencies": {
52
52
  "@electric-sql/pglite": "0.5.8",
53
53
  "@nxgt/drizzle": "0.6.1",
54
- "@nxgt/janus": "^0.9.0",
54
+ "@nxgt/janus": "^0.10.0",
55
55
  "@types/bun": "^1.4.2",
56
56
  "@types/pg": "^8.23.1",
57
57
  "drizzle-kit": "1.0.0-rc.4",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@nxgt/drizzle": ">=0.6.1 <1",
65
- "@nxgt/janus": "^0.9.0",
65
+ "@nxgt/janus": "^0.10.0",
66
66
  "drizzle-orm": ">=1.0.0-rc.4 <2",
67
67
  "typescript": "^6.0.3"
68
68
  }