@storyshelf/auth 0.7.0 → 0.9.0-next-d2cc082

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.
@@ -1,6 +1,5 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/auth-tables-pg.d.ts
4
3
  /** Narrow `user` table definition (Better Auth base model). */
5
4
  declare const authUserPg: import("drizzle-orm/pg-core").PgTableWithColumns<{
6
5
  name: "user";
@@ -1966,6 +1965,5 @@ declare const baseAuthTablesPg: {
1966
1965
  };
1967
1966
  /** Date-valued fields per base model (ISO strings at rest, Dates in flight). */
1968
1967
  declare const baseAuthDateFieldsPg: Record<string, string[]>;
1969
- //#endregion
1970
1968
  export { authAccountPg, authPasskeyPg, authSessionPg, authSsoProviderPg, authUserPg, authVerificationPg, baseAuthDateFieldsPg, baseAuthTablesPg };
1971
1969
  //# sourceMappingURL=auth-tables-pg.d.mts.map
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { boolean, integer, pgTable, text, timestamp } from "@storyshelf/core/orm/pg-core";
5
- //#region src/auth-tables-pg.ts
5
+ // src/auth-tables-pg.ts
6
6
  /**
7
7
  * Auth table definitions for Postgres (pg-core mirrors of `auth-tables.ts`).
8
8
  *
@@ -161,7 +161,6 @@ const baseAuthDateFieldsPg = {
161
161
  passkey: ["createdAt", "updatedAt"],
162
162
  ssoProvider: []
163
163
  };
164
- //#endregion
165
164
  export { authAccountPg, authPasskeyPg, authSessionPg, authSsoProviderPg, authUserPg, authVerificationPg, baseAuthDateFieldsPg, baseAuthTablesPg };
166
165
 
167
166
  //# sourceMappingURL=auth-tables-pg.mjs.map
@@ -1,6 +1,5 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/auth-tables.d.ts
4
3
  /** Table handles for the Better Auth base models, keyed by model name. */
5
4
  declare const baseAuthTables: {
6
5
  user: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
@@ -1087,6 +1086,5 @@ declare const baseAuthTables: {
1087
1086
  };
1088
1087
  /** Date-valued fields per base model (ISO strings at rest, Dates in flight). */
1089
1088
  declare const baseAuthDateFields: Record<string, string[]>;
1090
- //#endregion
1091
1089
  export { baseAuthDateFields, baseAuthTables };
1092
1090
  //# sourceMappingURL=auth-tables.d.mts.map
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { integer, sqliteTable, text } from "@storyshelf/core/orm/sqlite-core";
5
- //#region src/auth-tables.ts
5
+ // src/auth-tables.ts
6
6
  /**
7
7
  * Auth table definitions: drizzle handles for the Better Auth base models.
8
8
  *
@@ -116,7 +116,6 @@ const baseAuthDateFields = {
116
116
  passkey: ["createdAt", "updatedAt"],
117
117
  ssoProvider: []
118
118
  };
119
- //#endregion
120
119
  export { authAccount, authPasskey, authSession, authSsoProvider, authUser, authVerification, baseAuthDateFields, baseAuthTables };
121
120
 
122
121
  //# sourceMappingURL=auth-tables.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
4
- //#region src/bootstrap.d.ts
5
4
  /** Input for {@link ensurePasswordAdmin}. */
6
5
  interface PasswordAdminInput {
7
6
  email: string;
@@ -10,6 +9,5 @@ interface PasswordAdminInput {
10
9
  }
11
10
  /** Provision (or refresh the credential of) the env-driven local admin. */
12
11
  declare function ensurePasswordAdmin(db: DatabaseAdapter, input: PasswordAdminInput): Promise<void>;
13
- //#endregion
14
12
  export { PasswordAdminInput, ensurePasswordAdmin };
15
13
  //# sourceMappingURL=bootstrap.d.mts.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { MIN_PASSWORD_LENGTH, ensureEngineIdentity, tableColumn } from "./invites.mjs";
5
5
  import { eq } from "@storyshelf/core/orm";
6
6
  import { ulid } from "@storyshelf/core/utils";
7
- //#region src/bootstrap.ts
7
+ // src/bootstrap.ts
8
8
  /** Matches what Better Auth accepts at sign-in: the domain needs a dot. */
9
9
  const ENGINE_EMAIL = /^[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+$/u;
10
10
  /** Find the env admin row, or stage it (admin role, no credential yet). */
@@ -37,7 +37,6 @@ async function ensurePasswordAdmin(db, input) {
37
37
  const shelfUser = await stagePasswordAdmin(db, email, input.name ?? "Admin", now);
38
38
  await ensureEngineIdentity(db, shelfUser, input.password);
39
39
  }
40
- //#endregion
41
40
  export { ensurePasswordAdmin };
42
41
 
43
42
  //# sourceMappingURL=bootstrap.mjs.map
package/dist/config.d.mts CHANGED
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { ShelfAuthOptions } from "./engine.mjs";
4
4
  import { z } from "zod";
5
- //#region src/config.d.ts
6
5
  /** Extract the variable name from an `{env:NAME}` ref, or null. */
7
6
  declare function parseEnvRef(value: string): string | null;
8
7
  /**
@@ -22,6 +21,5 @@ interface RawShelfAuthOptions extends Omit<ShelfAuthOptions, "plugins"> {
22
21
  /** Zod schema validating shelf auth options (before secret resolution). */
23
22
  declare const authOptionsSchema: z.ZodType<Omit<ShelfAuthOptions, "plugins">>;
24
23
  declare function resolveAuthOptions(input: RawShelfAuthOptions): Promise<ShelfAuthOptions>;
25
- //#endregion
26
24
  export { RawShelfAuthOptions, ResolveSecrets, authOptionsSchema, parseEnvRef, resolveAuthOptions };
27
25
  //# sourceMappingURL=config.d.mts.map
package/dist/config.mjs CHANGED
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { z } from "zod";
5
- //#region src/config.ts
5
+ // src/config.ts
6
6
  /**
7
7
  * Auth config-as-code: validated, secret-safe engine options.
8
8
  *
@@ -198,7 +198,6 @@ async function resolveAuthOptions(input) {
198
198
  ...plugins ? { plugins } : {}
199
199
  };
200
200
  }
201
- //#endregion
202
201
  export { authOptionsSchema, parseEnvRef, resolveAuthOptions };
203
202
 
204
203
  //# sourceMappingURL=config.mjs.map
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
3
3
  import { Table } from "@storyshelf/core/orm";
4
4
  import { DBAdapterInstance } from "better-auth";
5
5
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
6
- //#region src/db-bridge.d.ts
7
6
  /** Model→table registry plus per-model date fields. */
8
7
  interface AuthBridgeSchema {
9
8
  /** Drizzle table handles keyed by Better Auth model name. */
@@ -15,6 +14,5 @@ interface AuthBridgeSchema {
15
14
  }
16
15
  /** Build the Better Auth database adapter over a shelf database. */
17
16
  declare function createShelfDbBridge(db: DatabaseAdapter, schema: AuthBridgeSchema): DBAdapterInstance;
18
- //#endregion
19
17
  export { AuthBridgeSchema, createShelfDbBridge };
20
18
  //# sourceMappingURL=db-bridge.d.mts.map
@@ -5,7 +5,7 @@ import { applySelect, toDriverRow, toModelRow } from "./db-values.mjs";
5
5
  import { buildCondition, buildOrderBy } from "./db-where.mjs";
6
6
  import { getTableColumns } from "@storyshelf/core/orm";
7
7
  import { ulid } from "@storyshelf/core/utils";
8
- //#region src/db-bridge.ts
8
+ // src/db-bridge.ts
9
9
  function tableFor(schema, model) {
10
10
  const table = schema.tables[model];
11
11
  if (!table) throw new Error(`Unknown auth model "${model}"`);
@@ -197,12 +197,12 @@ function createShelfDbBridge(db, schema) {
197
197
  options: {
198
198
  adapterName: "storyshelf",
199
199
  supportsDates: false,
200
- supportsJSON: false
200
+ supportsJSON: false,
201
+ adapterConfig: { transaction: typeof db.transact === "function" }
201
202
  }
202
203
  };
203
204
  };
204
205
  }
205
- //#endregion
206
206
  export { createShelfDbBridge };
207
207
 
208
208
  //# sourceMappingURL=db-bridge.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"db-bridge.mjs","names":[],"sources":["../src/db-bridge.ts"],"sourcesContent":["import type { DatabaseAdapter, ListOptions, TxStore } from \"@storyshelf/core/adapter/database\";\nimport type { SQL, Table } from \"@storyshelf/core/orm\";\nimport { getTableColumns } from \"@storyshelf/core/orm\";\nimport { ulid } from \"@storyshelf/core/utils\";\n/**\n * Opaque Better Auth database bridge over the shelf DatabaseAdapter.\n *\n * Better Auth talks to `DBAdapterInstance` (create/findOne/findMany/count/\n * update/updateMany/delete/deleteMany/consumeOne/incrementOne/transaction);\n * this maps each call onto {@link DatabaseAdapter} CRUD over an explicit\n * model→table registry. No dialect knowledge: sqlite, Turso, and Postgres\n * drivers all work untouched, present and future.\n */\nimport type { DBAdapterInstance, DBTransactionAdapter, Where } from \"better-auth\";\nimport { applySelect, toDriverRow, toModelRow } from \"./db-values.ts\";\nimport { buildCondition, buildOrderBy } from \"./db-where.ts\";\n\n/** Model→table registry plus per-model date fields. */\nexport interface AuthBridgeSchema {\n /** Drizzle table handles keyed by Better Auth model name. */\n tables: Record<string, Table>;\n /** Date-valued fields per model (ISO at rest, Date in flight). */\n dateFields: Record<string, readonly string[]>;\n /** Id generator for creates missing an id (defaults to ULID). */\n generateId?: (model: string) => string;\n}\n\n/** CRUD surface the bridge is built over (adapter or transaction store). */\ntype CrudStore = Pick<TxStore, \"insert\" | \"update\" | \"get\" | \"remove\" | \"list\" | \"count\">;\n\ninterface OpInput {\n model: string;\n}\n\nfunction tableFor(schema: AuthBridgeSchema, model: string): Table {\n const table = schema.tables[model];\n if (!table) {\n throw new Error(`Unknown auth model \"${model}\"`);\n }\n return table;\n}\n\nfunction datesFor(schema: AuthBridgeSchema, model: string): readonly string[] {\n return schema.dateFields[model] ?? [];\n}\n\nfunction defaultGenerateId(): string {\n return ulid();\n}\n\nfunction whereOf(table: Table, where: readonly Where[] | undefined): SQL | undefined {\n if (!where || where.length === 0) {\n return undefined;\n }\n return buildCondition(table, where);\n}\n\nasync function findFirst(\n store: CrudStore,\n schema: AuthBridgeSchema,\n model: string,\n where: readonly Where[] | undefined,\n): Promise<Record<string, unknown> | null> {\n const table = tableFor(schema, model);\n const rows = (await store.list(table, { where: whereOf(table, where), limit: 1 })) as Record<\n string,\n unknown\n >[];\n const row = rows[0];\n return row ? toModelRow(row, datesFor(schema, model)) : null;\n}\n\nasync function updateById(\n store: CrudStore,\n schema: AuthBridgeSchema,\n model: string,\n id: string,\n update: Record<string, unknown>,\n): Promise<Record<string, unknown> | null> {\n const table = tableFor(schema, model);\n const existing = await store.get(table, id);\n if (!existing) {\n return null;\n }\n const updated = await store.update(table, id, toDriverRow(update));\n return toModelRow(updated, datesFor(schema, model));\n}\n\nasync function opCreate(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { data: Record<string, unknown> },\n): Promise<Record<string, unknown>> {\n const table = tableFor(schema, input.model);\n const generate = schema.generateId ?? defaultGenerateId;\n const data =\n input.data[\"id\"] === undefined || input.data[\"id\"] === null\n ? { ...input.data, id: generate(input.model) }\n : input.data;\n const inserted = await store.insert(table, toDriverRow(data));\n return toModelRow(inserted, datesFor(schema, input.model));\n}\n\nasync function opUpdate(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n): Promise<Record<string, unknown> | null> {\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row || typeof row[\"id\"] !== \"string\") {\n return null;\n }\n return await updateById(store, schema, input.model, row[\"id\"], input.update);\n}\n\nasync function opUpdateMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n): Promise<number> {\n const table = tableFor(schema, input.model);\n const rows = (await store.list(table, {\n where: whereOf(table, input.where),\n })) as Record<string, unknown>[];\n const ids = rows.map((row) => row[\"id\"]).filter((id): id is string => typeof id === \"string\");\n await Promise.all(\n ids.map(async (id) => await updateById(store, schema, input.model, id, input.update)),\n );\n return ids.length;\n}\n\nasync function opDeleteMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[] },\n): Promise<number> {\n const table = tableFor(schema, input.model);\n const rows = (await store.list(table, {\n where: whereOf(table, input.where),\n })) as Record<string, unknown>[];\n const ids = rows.map((row) => row[\"id\"]).filter((id): id is string => typeof id === \"string\");\n await Promise.all(\n ids.map(async (id) => {\n await store.remove(table, id);\n }),\n );\n return ids.length;\n}\n\nasync function opConsumeOne(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[] },\n): Promise<Record<string, unknown> | null> {\n // Runs directly on the given store: the top-level binding wraps this in\n // `transact` for atomicity, while transaction-scoped bindings pass the\n // ambient `tx` so Better Auth's own transaction wrapper never nests a\n // second `begin` on single-connection drivers (sqlite).\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row || typeof row[\"id\"] !== \"string\") {\n return null;\n }\n await store.remove(tableFor(schema, input.model), row[\"id\"]);\n return row;\n}\n\nasync function opIncrementOne(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & {\n where: Where[];\n increment: Record<string, number>;\n set?: Record<string, unknown>;\n },\n): Promise<Record<string, unknown> | null> {\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row || typeof row[\"id\"] !== \"string\") {\n return null;\n }\n const deltas = Object.fromEntries(\n Object.entries(input.increment).map(([field, delta]) => {\n const current = row[field];\n return [field, (typeof current === \"number\" ? current : 0) + delta];\n }),\n );\n return await updateById(store, schema, input.model, row[\"id\"], {\n ...deltas,\n ...input.set,\n });\n}\n\nfunction hasColumn(table: Table, field: string): boolean {\n return getTableColumns(table)[field] !== undefined;\n}\n\nasync function joinMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n parentModel: string,\n parentId: unknown,\n joinedModel: string,\n limit: number | undefined,\n): Promise<Record<string, unknown>[]> {\n const table = tableFor(schema, joinedModel);\n const opts: ListOptions = { limit };\n const where = whereOf(table, [{ field: `${parentModel}Id`, value: parentId as never }]);\n if (where) {\n opts.where = where;\n }\n const rows = (await store.list(table, opts)) as Record<string, unknown>[];\n return rows.map((row) => toModelRow(row, datesFor(schema, joinedModel)));\n}\n\nasync function joinRelation(\n store: CrudStore,\n schema: AuthBridgeSchema,\n parentModel: string,\n parent: Record<string, unknown>,\n joinedModel: string,\n limit: number | undefined,\n): Promise<Record<string, unknown>[] | Record<string, unknown> | null> {\n const table = tableFor(schema, joinedModel);\n if (hasColumn(table, `${parentModel}Id`)) {\n return await joinMany(store, schema, parentModel, parent[\"id\"], joinedModel, limit);\n }\n if (typeof parent[`${joinedModel}Id`] === \"string\") {\n return await findFirst(store, schema, joinedModel, [\n { field: \"id\", value: parent[`${joinedModel}Id`] as never },\n ]);\n }\n throw new Error(`Unknown auth relation \"${parentModel}.${joinedModel}\"`);\n}\n\nasync function attachJoins(\n store: CrudStore,\n schema: AuthBridgeSchema,\n parentModel: string,\n parent: Record<string, unknown>,\n join: Record<string, boolean | { limit?: number }> | undefined,\n): Promise<Record<string, unknown>> {\n if (!join) {\n return {};\n }\n const attached: Record<string, unknown> = {};\n const entries = Object.entries(join);\n await Promise.all(\n entries.map(async ([joinedModel, joinAttr]) => {\n const limit = typeof joinAttr === \"object\" ? joinAttr.limit : undefined;\n attached[joinedModel] = await joinRelation(\n store,\n schema,\n parentModel,\n parent,\n joinedModel,\n limit,\n );\n }),\n );\n return attached;\n}\n\ntype FindManyInput = OpInput & {\n where?: Where[];\n limit?: number;\n offset?: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n select?: string[];\n join?: Record<string, boolean | { limit?: number }>;\n};\n\nasync function listRows(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: FindManyInput,\n): Promise<Record<string, unknown>[]> {\n const table = tableFor(schema, input.model);\n const opts: ListOptions = { limit: input.limit, offset: input.offset };\n const where = whereOf(table, input.where);\n if (where) {\n opts.where = where;\n }\n const orderBy = buildOrderBy(table, input.sortBy);\n if (orderBy) {\n opts.orderBy = orderBy;\n }\n const listed: Record<string, unknown>[] = await store.list(table, opts);\n return listed;\n}\n\nasync function opFindMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: FindManyInput,\n): Promise<Record<string, unknown>[]> {\n const rows = await listRows(store, schema, input);\n return await Promise.all(\n rows.map(async (row) => {\n const withJoins = input.join\n ? { ...row, ...(await attachJoins(store, schema, input.model, row, input.join)) }\n : row;\n return applySelect(toModelRow(withJoins, datesFor(schema, input.model)), input.select);\n }),\n );\n}\n\n/** One bound operation set (adapter top-level or transaction store). */\ninterface JoinSpec {\n join?: Record<string, boolean | { limit?: number }>;\n}\n\ninterface BoundOps {\n create(input: OpInput & { data: Record<string, unknown> }): Promise<Record<string, unknown>>;\n findOne(\n input: OpInput & { where: Where[]; select?: string[] } & JoinSpec,\n ): Promise<Record<string, unknown> | null>;\n findMany(\n input: OpInput & {\n where?: Where[];\n limit?: number;\n offset?: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n select?: string[];\n } & JoinSpec,\n ): Promise<Record<string, unknown>[]>;\n count(input: OpInput & { where?: Where[] }): Promise<number>;\n update(\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n ): Promise<Record<string, unknown> | null>;\n updateMany(input: OpInput & { where: Where[]; update: Record<string, unknown> }): Promise<number>;\n delete(input: OpInput & { where: Where[] }): Promise<void>;\n deleteMany(input: OpInput & { where: Where[] }): Promise<number>;\n consumeOne(input: OpInput & { where: Where[] }): Promise<Record<string, unknown> | null>;\n incrementOne(\n input: OpInput & {\n where: Where[];\n increment: Record<string, number>;\n set?: Record<string, unknown>;\n },\n ): Promise<Record<string, unknown> | null>;\n}\n\nasync function opFindOne(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[]; select?: string[] } & JoinSpec,\n): Promise<Record<string, unknown> | null> {\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row) {\n return null;\n }\n const withJoins = input.join\n ? { ...row, ...(await attachJoins(store, schema, input.model, row, input.join)) }\n : row;\n return applySelect(withJoins, input.select);\n}\n\n/** Read operations bound to one store. */\nfunction bindQueryOps(\n store: CrudStore,\n schema: AuthBridgeSchema,\n): Pick<BoundOps, \"findOne\" | \"findMany\" | \"count\"> {\n return {\n findOne: async (\n input: OpInput & { where: Where[]; select?: string[] } & JoinSpec,\n ): Promise<Record<string, unknown> | null> => await opFindOne(store, schema, input),\n findMany: async (\n input: OpInput & {\n where?: Where[];\n limit?: number;\n offset?: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n select?: string[];\n } & JoinSpec,\n ): Promise<Record<string, unknown>[]> => await opFindMany(store, schema, input),\n count: async (input: OpInput & { where?: Where[] }): Promise<number> => {\n const table = tableFor(schema, input.model);\n return await store.count(table, whereOf(table, input.where));\n },\n };\n}\n\n/** Write operations bound to one store (transaction-aware where marked). */\nfunction bindMutationOps(\n store: CrudStore,\n schema: AuthBridgeSchema,\n db: DatabaseAdapter,\n): Omit<BoundOps, \"findOne\" | \"findMany\" | \"count\"> {\n return {\n create: async (\n input: OpInput & { data: Record<string, unknown> },\n ): Promise<Record<string, unknown>> => await opCreate(store, schema, input),\n update: async (\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n ): Promise<Record<string, unknown> | null> => await opUpdate(store, schema, input),\n updateMany: async (\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n ): Promise<number> => await opUpdateMany(store, schema, input),\n delete: async (input: OpInput & { where: Where[] }): Promise<void> => {\n const row = await findFirst(store, schema, input.model, input.where);\n if (row && typeof row[\"id\"] === \"string\") {\n await store.remove(tableFor(schema, input.model), row[\"id\"]);\n }\n },\n deleteMany: async (input: OpInput & { where: Where[] }): Promise<number> =>\n await opDeleteMany(store, schema, input),\n consumeOne: async (\n input: OpInput & { where: Where[] },\n ): Promise<Record<string, unknown> | null> => {\n // Atomic at the top level when the driver offers transact; inside a\n // Better Auth transaction `store` is the ambient `tx`, which must run\n // directly (a second `begin` fails on single-connection sqlite).\n if (store === db && db.transact) {\n return await db.transact(async (tx) => await opConsumeOne(tx, schema, input));\n }\n return await opConsumeOne(store, schema, input);\n },\n incrementOne: async (\n input: OpInput & {\n where: Where[];\n increment: Record<string, number>;\n set?: Record<string, unknown>;\n },\n ): Promise<Record<string, unknown> | null> => {\n if (store === db && db.transact) {\n return await db.transact(async (tx) => await opIncrementOne(tx, schema, input));\n }\n return await opIncrementOne(store, schema, input);\n },\n };\n}\n\n/** Build the Better Auth database adapter over a shelf database. */\nexport function createShelfDbBridge(\n db: DatabaseAdapter,\n schema: AuthBridgeSchema,\n): DBAdapterInstance {\n return (_options: unknown) => {\n const ops: BoundOps = { ...bindQueryOps(db, schema), ...bindMutationOps(db, schema, db) };\n return {\n ...ops,\n transaction: async <R>(callback: (trx: DBTransactionAdapter) => Promise<R>): Promise<R> => {\n if (db.transact) {\n return await db.transact(async (tx) => {\n const txOps: BoundOps = {\n ...bindQueryOps(tx, schema),\n ...bindMutationOps(tx, schema, db),\n };\n return await callback(txOps as never);\n });\n }\n return await callback(ops as never);\n },\n options: { adapterName: \"storyshelf\", supportsDates: false, supportsJSON: false },\n } as never;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"db-bridge.mjs","names":[],"sources":["../src/db-bridge.ts"],"sourcesContent":["import type { DatabaseAdapter, ListOptions, TxStore } from \"@storyshelf/core/adapter/database\";\nimport type { SQL, Table } from \"@storyshelf/core/orm\";\nimport { getTableColumns } from \"@storyshelf/core/orm\";\nimport { ulid } from \"@storyshelf/core/utils\";\n/**\n * Opaque Better Auth database bridge over the shelf DatabaseAdapter.\n *\n * Better Auth talks to `DBAdapterInstance` (create/findOne/findMany/count/\n * update/updateMany/delete/deleteMany/consumeOne/incrementOne/transaction);\n * this maps each call onto {@link DatabaseAdapter} CRUD over an explicit\n * model→table registry. No dialect knowledge: sqlite, Turso, and Postgres\n * drivers all work untouched, present and future.\n */\nimport type { DBAdapterInstance, DBTransactionAdapter, Where } from \"better-auth\";\nimport { applySelect, toDriverRow, toModelRow } from \"./db-values.ts\";\nimport { buildCondition, buildOrderBy } from \"./db-where.ts\";\n\n/** Model→table registry plus per-model date fields. */\nexport interface AuthBridgeSchema {\n /** Drizzle table handles keyed by Better Auth model name. */\n tables: Record<string, Table>;\n /** Date-valued fields per model (ISO at rest, Date in flight). */\n dateFields: Record<string, readonly string[]>;\n /** Id generator for creates missing an id (defaults to ULID). */\n generateId?: (model: string) => string;\n}\n\n/** CRUD surface the bridge is built over (adapter or transaction store). */\ntype CrudStore = Pick<TxStore, \"insert\" | \"update\" | \"get\" | \"remove\" | \"list\" | \"count\">;\n\ninterface OpInput {\n model: string;\n}\n\nfunction tableFor(schema: AuthBridgeSchema, model: string): Table {\n const table = schema.tables[model];\n if (!table) {\n throw new Error(`Unknown auth model \"${model}\"`);\n }\n return table;\n}\n\nfunction datesFor(schema: AuthBridgeSchema, model: string): readonly string[] {\n return schema.dateFields[model] ?? [];\n}\n\nfunction defaultGenerateId(): string {\n return ulid();\n}\n\nfunction whereOf(table: Table, where: readonly Where[] | undefined): SQL | undefined {\n if (!where || where.length === 0) {\n return undefined;\n }\n return buildCondition(table, where);\n}\n\nasync function findFirst(\n store: CrudStore,\n schema: AuthBridgeSchema,\n model: string,\n where: readonly Where[] | undefined,\n): Promise<Record<string, unknown> | null> {\n const table = tableFor(schema, model);\n const rows = (await store.list(table, { where: whereOf(table, where), limit: 1 })) as Record<\n string,\n unknown\n >[];\n const row = rows[0];\n return row ? toModelRow(row, datesFor(schema, model)) : null;\n}\n\nasync function updateById(\n store: CrudStore,\n schema: AuthBridgeSchema,\n model: string,\n id: string,\n update: Record<string, unknown>,\n): Promise<Record<string, unknown> | null> {\n const table = tableFor(schema, model);\n const existing = await store.get(table, id);\n if (!existing) {\n return null;\n }\n const updated = await store.update(table, id, toDriverRow(update));\n return toModelRow(updated, datesFor(schema, model));\n}\n\nasync function opCreate(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { data: Record<string, unknown> },\n): Promise<Record<string, unknown>> {\n const table = tableFor(schema, input.model);\n const generate = schema.generateId ?? defaultGenerateId;\n const data =\n input.data[\"id\"] === undefined || input.data[\"id\"] === null\n ? { ...input.data, id: generate(input.model) }\n : input.data;\n const inserted = await store.insert(table, toDriverRow(data));\n return toModelRow(inserted, datesFor(schema, input.model));\n}\n\nasync function opUpdate(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n): Promise<Record<string, unknown> | null> {\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row || typeof row[\"id\"] !== \"string\") {\n return null;\n }\n return await updateById(store, schema, input.model, row[\"id\"], input.update);\n}\n\nasync function opUpdateMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n): Promise<number> {\n const table = tableFor(schema, input.model);\n const rows = (await store.list(table, {\n where: whereOf(table, input.where),\n })) as Record<string, unknown>[];\n const ids = rows.map((row) => row[\"id\"]).filter((id): id is string => typeof id === \"string\");\n await Promise.all(\n ids.map(async (id) => await updateById(store, schema, input.model, id, input.update)),\n );\n return ids.length;\n}\n\nasync function opDeleteMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[] },\n): Promise<number> {\n const table = tableFor(schema, input.model);\n const rows = (await store.list(table, {\n where: whereOf(table, input.where),\n })) as Record<string, unknown>[];\n const ids = rows.map((row) => row[\"id\"]).filter((id): id is string => typeof id === \"string\");\n await Promise.all(\n ids.map(async (id) => {\n await store.remove(table, id);\n }),\n );\n return ids.length;\n}\n\nasync function opConsumeOne(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[] },\n): Promise<Record<string, unknown> | null> {\n // Runs directly on the given store: the top-level binding wraps this in\n // `transact` for atomicity, while transaction-scoped bindings pass the\n // ambient `tx` so Better Auth's own transaction wrapper never nests a\n // second `begin` on single-connection drivers (sqlite).\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row || typeof row[\"id\"] !== \"string\") {\n return null;\n }\n await store.remove(tableFor(schema, input.model), row[\"id\"]);\n return row;\n}\n\nasync function opIncrementOne(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & {\n where: Where[];\n increment: Record<string, number>;\n set?: Record<string, unknown>;\n },\n): Promise<Record<string, unknown> | null> {\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row || typeof row[\"id\"] !== \"string\") {\n return null;\n }\n const deltas = Object.fromEntries(\n Object.entries(input.increment).map(([field, delta]) => {\n const current = row[field];\n return [field, (typeof current === \"number\" ? current : 0) + delta];\n }),\n );\n return await updateById(store, schema, input.model, row[\"id\"], {\n ...deltas,\n ...input.set,\n });\n}\n\nfunction hasColumn(table: Table, field: string): boolean {\n return getTableColumns(table)[field] !== undefined;\n}\n\nasync function joinMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n parentModel: string,\n parentId: unknown,\n joinedModel: string,\n limit: number | undefined,\n): Promise<Record<string, unknown>[]> {\n const table = tableFor(schema, joinedModel);\n const opts: ListOptions = { limit };\n const where = whereOf(table, [{ field: `${parentModel}Id`, value: parentId as never }]);\n if (where) {\n opts.where = where;\n }\n const rows = (await store.list(table, opts)) as Record<string, unknown>[];\n return rows.map((row) => toModelRow(row, datesFor(schema, joinedModel)));\n}\n\nasync function joinRelation(\n store: CrudStore,\n schema: AuthBridgeSchema,\n parentModel: string,\n parent: Record<string, unknown>,\n joinedModel: string,\n limit: number | undefined,\n): Promise<Record<string, unknown>[] | Record<string, unknown> | null> {\n const table = tableFor(schema, joinedModel);\n if (hasColumn(table, `${parentModel}Id`)) {\n return await joinMany(store, schema, parentModel, parent[\"id\"], joinedModel, limit);\n }\n if (typeof parent[`${joinedModel}Id`] === \"string\") {\n return await findFirst(store, schema, joinedModel, [\n { field: \"id\", value: parent[`${joinedModel}Id`] as never },\n ]);\n }\n throw new Error(`Unknown auth relation \"${parentModel}.${joinedModel}\"`);\n}\n\nasync function attachJoins(\n store: CrudStore,\n schema: AuthBridgeSchema,\n parentModel: string,\n parent: Record<string, unknown>,\n join: Record<string, boolean | { limit?: number }> | undefined,\n): Promise<Record<string, unknown>> {\n if (!join) {\n return {};\n }\n const attached: Record<string, unknown> = {};\n const entries = Object.entries(join);\n await Promise.all(\n entries.map(async ([joinedModel, joinAttr]) => {\n const limit = typeof joinAttr === \"object\" ? joinAttr.limit : undefined;\n attached[joinedModel] = await joinRelation(\n store,\n schema,\n parentModel,\n parent,\n joinedModel,\n limit,\n );\n }),\n );\n return attached;\n}\n\ntype FindManyInput = OpInput & {\n where?: Where[];\n limit?: number;\n offset?: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n select?: string[];\n join?: Record<string, boolean | { limit?: number }>;\n};\n\nasync function listRows(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: FindManyInput,\n): Promise<Record<string, unknown>[]> {\n const table = tableFor(schema, input.model);\n const opts: ListOptions = { limit: input.limit, offset: input.offset };\n const where = whereOf(table, input.where);\n if (where) {\n opts.where = where;\n }\n const orderBy = buildOrderBy(table, input.sortBy);\n if (orderBy) {\n opts.orderBy = orderBy;\n }\n const listed: Record<string, unknown>[] = await store.list(table, opts);\n return listed;\n}\n\nasync function opFindMany(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: FindManyInput,\n): Promise<Record<string, unknown>[]> {\n const rows = await listRows(store, schema, input);\n return await Promise.all(\n rows.map(async (row) => {\n const withJoins = input.join\n ? { ...row, ...(await attachJoins(store, schema, input.model, row, input.join)) }\n : row;\n return applySelect(toModelRow(withJoins, datesFor(schema, input.model)), input.select);\n }),\n );\n}\n\n/** One bound operation set (adapter top-level or transaction store). */\ninterface JoinSpec {\n join?: Record<string, boolean | { limit?: number }>;\n}\n\ninterface BoundOps {\n create(input: OpInput & { data: Record<string, unknown> }): Promise<Record<string, unknown>>;\n findOne(\n input: OpInput & { where: Where[]; select?: string[] } & JoinSpec,\n ): Promise<Record<string, unknown> | null>;\n findMany(\n input: OpInput & {\n where?: Where[];\n limit?: number;\n offset?: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n select?: string[];\n } & JoinSpec,\n ): Promise<Record<string, unknown>[]>;\n count(input: OpInput & { where?: Where[] }): Promise<number>;\n update(\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n ): Promise<Record<string, unknown> | null>;\n updateMany(input: OpInput & { where: Where[]; update: Record<string, unknown> }): Promise<number>;\n delete(input: OpInput & { where: Where[] }): Promise<void>;\n deleteMany(input: OpInput & { where: Where[] }): Promise<number>;\n consumeOne(input: OpInput & { where: Where[] }): Promise<Record<string, unknown> | null>;\n incrementOne(\n input: OpInput & {\n where: Where[];\n increment: Record<string, number>;\n set?: Record<string, unknown>;\n },\n ): Promise<Record<string, unknown> | null>;\n}\n\nasync function opFindOne(\n store: CrudStore,\n schema: AuthBridgeSchema,\n input: OpInput & { where: Where[]; select?: string[] } & JoinSpec,\n): Promise<Record<string, unknown> | null> {\n const row = await findFirst(store, schema, input.model, input.where);\n if (!row) {\n return null;\n }\n const withJoins = input.join\n ? { ...row, ...(await attachJoins(store, schema, input.model, row, input.join)) }\n : row;\n return applySelect(withJoins, input.select);\n}\n\n/** Read operations bound to one store. */\nfunction bindQueryOps(\n store: CrudStore,\n schema: AuthBridgeSchema,\n): Pick<BoundOps, \"findOne\" | \"findMany\" | \"count\"> {\n return {\n findOne: async (\n input: OpInput & { where: Where[]; select?: string[] } & JoinSpec,\n ): Promise<Record<string, unknown> | null> => await opFindOne(store, schema, input),\n findMany: async (\n input: OpInput & {\n where?: Where[];\n limit?: number;\n offset?: number;\n sortBy?: { field: string; direction: \"asc\" | \"desc\" };\n select?: string[];\n } & JoinSpec,\n ): Promise<Record<string, unknown>[]> => await opFindMany(store, schema, input),\n count: async (input: OpInput & { where?: Where[] }): Promise<number> => {\n const table = tableFor(schema, input.model);\n return await store.count(table, whereOf(table, input.where));\n },\n };\n}\n\n/** Write operations bound to one store (transaction-aware where marked). */\nfunction bindMutationOps(\n store: CrudStore,\n schema: AuthBridgeSchema,\n db: DatabaseAdapter,\n): Omit<BoundOps, \"findOne\" | \"findMany\" | \"count\"> {\n return {\n create: async (\n input: OpInput & { data: Record<string, unknown> },\n ): Promise<Record<string, unknown>> => await opCreate(store, schema, input),\n update: async (\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n ): Promise<Record<string, unknown> | null> => await opUpdate(store, schema, input),\n updateMany: async (\n input: OpInput & { where: Where[]; update: Record<string, unknown> },\n ): Promise<number> => await opUpdateMany(store, schema, input),\n delete: async (input: OpInput & { where: Where[] }): Promise<void> => {\n const row = await findFirst(store, schema, input.model, input.where);\n if (row && typeof row[\"id\"] === \"string\") {\n await store.remove(tableFor(schema, input.model), row[\"id\"]);\n }\n },\n deleteMany: async (input: OpInput & { where: Where[] }): Promise<number> =>\n await opDeleteMany(store, schema, input),\n consumeOne: async (\n input: OpInput & { where: Where[] },\n ): Promise<Record<string, unknown> | null> => {\n // Atomic at the top level when the driver offers transact; inside a\n // Better Auth transaction `store` is the ambient `tx`, which must run\n // directly (a second `begin` fails on single-connection sqlite).\n if (store === db && db.transact) {\n return await db.transact(async (tx) => await opConsumeOne(tx, schema, input));\n }\n return await opConsumeOne(store, schema, input);\n },\n incrementOne: async (\n input: OpInput & {\n where: Where[];\n increment: Record<string, number>;\n set?: Record<string, unknown>;\n },\n ): Promise<Record<string, unknown> | null> => {\n if (store === db && db.transact) {\n return await db.transact(async (tx) => await opIncrementOne(tx, schema, input));\n }\n return await opIncrementOne(store, schema, input);\n },\n };\n}\n\n/** Build the Better Auth database adapter over a shelf database. */\nexport function createShelfDbBridge(\n db: DatabaseAdapter,\n schema: AuthBridgeSchema,\n): DBAdapterInstance {\n return (_options: unknown) => {\n const ops: BoundOps = { ...bindQueryOps(db, schema), ...bindMutationOps(db, schema, db) };\n return {\n ...ops,\n transaction: async <R>(callback: (trx: DBTransactionAdapter) => Promise<R>): Promise<R> => {\n if (db.transact) {\n return await db.transact(async (tx) => {\n const txOps: BoundOps = {\n ...bindQueryOps(tx, schema),\n ...bindMutationOps(tx, schema, db),\n };\n return await callback(txOps as never);\n });\n }\n return await callback(ops as never);\n },\n options: {\n adapterName: \"storyshelf\",\n supportsDates: false,\n supportsJSON: false,\n adapterConfig: { transaction: typeof db.transact === \"function\" },\n },\n } as never;\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/db-values.ts
4
+ // src/db-values.ts
5
5
  /**
6
6
  * Value conversion between Better Auth models and driver rows.
7
7
  *
@@ -35,7 +35,6 @@ function applySelect(row, select) {
35
35
  if (!select || select.length === 0) return row;
36
36
  return Object.fromEntries(select.map((field) => [field, row[field]]));
37
37
  }
38
- //#endregion
39
38
  export { applySelect, stripUndefined, toDriverRow, toDriverValue, toModelRow };
40
39
 
41
40
  //# sourceMappingURL=db-values.mjs.map
package/dist/db-where.mjs CHANGED
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { and, asc, desc, eq, getTableColumns, gt, gte, inArray, lt, lte, ne, not, or, sql } from "@storyshelf/core/orm";
5
- //#region src/db-where.ts
5
+ // src/db-where.ts
6
6
  function columnOf(table, field) {
7
7
  const column = getTableColumns(table)[field];
8
8
  if (!column) throw new Error(`Unknown auth field "${field}"`);
@@ -79,7 +79,6 @@ function buildOrderBy(table, sortBy) {
79
79
  const column = columnOf(table, sortBy.field);
80
80
  return sortBy.direction === "desc" ? desc(column) : asc(column);
81
81
  }
82
- //#endregion
83
82
  export { buildCondition, buildOrderBy };
84
83
 
85
84
  //# sourceMappingURL=db-where.mjs.map
package/dist/engine.d.mts CHANGED
@@ -5,7 +5,6 @@ import { BetterAuthPlugin } from "better-auth";
5
5
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
6
6
  import { EmailSender } from "@storyshelf/core/adapter/email-sender";
7
7
  import { Logger } from "@storyshelf/core/logger";
8
- //#region src/engine.d.ts
9
8
  /** Provider id minted on every engine identity (method attribution is future work). */
10
9
  declare const ENGINE_PROVIDER_ID = "better-auth";
11
10
  /** Options for {@link createShelfAuth}. */
@@ -203,6 +202,5 @@ interface ShelfAuth {
203
202
  * the Auth singleton (check/destroySession, invites, descriptors).
204
203
  */
205
204
  declare function createShelfAuth(options: ShelfAuthOptions): ShelfAuth;
206
- //#endregion
207
205
  export { ENGINE_PROVIDER_ID, EngineSessionUser, ShelfAuth, ShelfAuthInstance, ShelfAuthOptions, ShelfOAuthProvider, ShelfPasskeyOptions, ShelfSSOGroupSync, ShelfSSOOidc, ShelfSSOOptions, ShelfSSOProvider, ShelfSSOSaml, ShelfSocialProvider, createShelfAuth };
208
206
  //# sourceMappingURL=engine.d.mts.map
package/dist/engine.mjs CHANGED
@@ -14,7 +14,7 @@ import { UserModel } from "@storyshelf/core/models";
14
14
  import { SESSION_COOKIE } from "@storyshelf/core/types";
15
15
  import { betterAuth } from "better-auth";
16
16
  import { genericOAuth } from "better-auth/plugins";
17
- //#region src/engine.ts
17
+ // src/engine.ts
18
18
  /**
19
19
  * Shelf auth engine: Better Auth protocol plumbing behind the Auth singleton.
20
20
  *
@@ -438,7 +438,6 @@ function createShelfAuth(options) {
438
438
  adapter: buildAdapter(shelf, db, options)
439
439
  };
440
440
  }
441
- //#endregion
442
441
  export { ENGINE_PROVIDER_ID, createShelfAuth };
443
442
 
444
443
  //# sourceMappingURL=engine.mjs.map
package/dist/groups.d.mts CHANGED
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
3
3
  import { ShelfSSOProvider } from "./engine.mjs";
4
4
  import { SiteRole } from "@storyshelf/core/types";
5
5
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
6
- //#region src/groups.d.ts
7
6
  /** Default claim name when recipes leave `groups.claim` unset. */
8
7
  declare const DEFAULT_GROUP_CLAIM = "groups";
9
8
  /**
@@ -49,6 +48,5 @@ declare function provisionHook(db: DatabaseAdapter, providers: ShelfSSOProvider[
49
48
  * shelf row on first SSO login when the mirror hook has not run yet.
50
49
  */
51
50
  declare function syncSsoGroups(db: DatabaseAdapter, providers: ShelfSSOProvider[], input: SsoGroupSyncInput): Promise<void>;
52
- //#endregion
53
51
  export { DEFAULT_GROUP_CLAIM, GroupProvisionData, SsoGroupSyncInput, extractGroups, provisionHook, resolveSiteRole, syncSsoGroups };
54
52
  //# sourceMappingURL=groups.d.mts.map
package/dist/groups.mjs CHANGED
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { MemberModel, ProjectGroupMappingModel, ProjectModel, UserModel } from "@storyshelf/core/models";
5
- //#region src/groups.ts
5
+ // src/groups.ts
6
6
  /** Default claim name when recipes leave `groups.claim` unset. */
7
7
  const DEFAULT_GROUP_CLAIM = "groups";
8
8
  /** Rank for top-match resolution (highest role wins). */
@@ -123,7 +123,6 @@ async function syncSsoGroups(db, providers, input) {
123
123
  await syncSiteRole(db, input, resolveSiteRole((await new UserModel(db).get(input.userId))?.role ?? "member", groups, sync.admins));
124
124
  await syncProjectRoles(db, input.userId, groups);
125
125
  }
126
- //#endregion
127
126
  export { DEFAULT_GROUP_CLAIM, extractGroups, provisionHook, resolveSiteRole, syncSsoGroups };
128
127
 
129
128
  //# sourceMappingURL=groups.mjs.map
@@ -4,7 +4,6 @@ import { Table } from "@storyshelf/core/orm";
4
4
  import { MIN_PASSWORD_LENGTH } from "@storyshelf/core/auth";
5
5
  import { AuthUser } from "@storyshelf/core/types";
6
6
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
7
- //#region src/invites.d.ts
8
7
  /** Input for {@link issueInvite}. */
9
8
  interface IssueInviteInput {
10
9
  email: string;
@@ -35,6 +34,5 @@ declare function verifyInvite(db: DatabaseAdapter, input: InviteTokenInput): Pro
35
34
  }>;
36
35
  /** Consume an invite: set the engine credential and return the shelf user. */
37
36
  declare function acceptInvite(db: DatabaseAdapter, input: AcceptInviteInput): Promise<AuthUser>;
38
- //#endregion
39
37
  export { AcceptInviteInput, InviteTokenInput, IssueInviteInput, MIN_PASSWORD_LENGTH, acceptInvite, issueInvite, verifyInvite };
40
38
  //# sourceMappingURL=invites.d.mts.map
package/dist/invites.mjs CHANGED
@@ -6,7 +6,7 @@ import { eq, getTableColumns, sql } from "@storyshelf/core/orm";
6
6
  import { randomToken, sha256, timingSafeEqualString, ulid } from "@storyshelf/core/utils";
7
7
  import { MIN_PASSWORD_LENGTH } from "@storyshelf/core/auth";
8
8
  import { hashPassword } from "better-auth/crypto";
9
- //#region src/invites.ts
9
+ // src/invites.ts
10
10
  /**
11
11
  * Invite-only local accounts on the engine: our tables own the invite,
12
12
  * Better Auth owns the credential.
@@ -230,7 +230,6 @@ async function acceptInvite(db, input) {
230
230
  throw error;
231
231
  }
232
232
  }
233
- //#endregion
234
233
  export { MIN_PASSWORD_LENGTH, acceptInvite, ensureEngineIdentity, issueInvite, tableColumn, verifyInvite };
235
234
 
236
235
  //# sourceMappingURL=invites.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
4
- //#region src/passkeys.d.ts
5
4
  /** One registered passkey for the profile page. */
6
5
  interface EnginePasskeyInfo {
7
6
  id: string;
@@ -16,6 +15,5 @@ interface EnginePasskeyInfo {
16
15
  declare function listUserPasskeys(db: DatabaseAdapter, userId: string): Promise<EnginePasskeyInfo[]>;
17
16
  /** Whether the user can change a local password (has a credential account). */
18
17
  declare function hasPasswordCredential(db: DatabaseAdapter, userId: string): Promise<boolean>;
19
- //#endregion
20
18
  export { EnginePasskeyInfo, hasPasswordCredential, listUserPasskeys };
21
19
  //# sourceMappingURL=passkeys.d.mts.map
package/dist/passkeys.mjs CHANGED
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { baseAuthTables } from "./auth-tables.mjs";
5
5
  import { tableColumn } from "./invites.mjs";
6
6
  import { eq } from "@storyshelf/core/orm";
7
- //#region src/passkeys.ts
7
+ // src/passkeys.ts
8
8
  /** Registered passkeys for a user, oldest first. */
9
9
  async function listUserPasskeys(db, userId) {
10
10
  return (await db.list(baseAuthTables.passkey, { where: eq(tableColumn(baseAuthTables.passkey, "userId"), userId) })).map((row) => ({
@@ -21,7 +21,6 @@ async function listUserPasskeys(db, userId) {
21
21
  async function hasPasswordCredential(db, userId) {
22
22
  return (await db.list(baseAuthTables.account, { where: eq(tableColumn(baseAuthTables.account, "userId"), userId) })).some((row) => row["providerId"] === "credential");
23
23
  }
24
- //#endregion
25
24
  export { hasPasswordCredential, listUserPasskeys };
26
25
 
27
26
  //# sourceMappingURL=passkeys.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { ShelfOAuthProvider } from "../engine.mjs";
4
- //#region src/presets/enterprise.d.ts
5
4
  /** Client credentials shared by the enterprise presets. */
6
5
  interface EnterpriseClientOptions {
7
6
  clientId: string;
@@ -19,6 +18,5 @@ declare function oktaPreset(options: EnterpriseClientOptions & {
19
18
  declare function auth0Preset(options: EnterpriseClientOptions & {
20
19
  domain: string;
21
20
  }): ShelfOAuthProvider;
22
- //#endregion
23
21
  export { EnterpriseClientOptions, auth0Preset, keycloakPreset, oktaPreset };
24
22
  //# sourceMappingURL=enterprise.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/presets/enterprise.ts
4
+ // src/presets/enterprise.ts
5
5
  /** Keycloak realm preset (discovery over the realm URL). */
6
6
  function keycloakPreset(realmUrl, options) {
7
7
  return {
@@ -38,7 +38,6 @@ function auth0Preset(options) {
38
38
  userInfoUrl: `${issuer}userinfo`
39
39
  };
40
40
  }
41
- //#endregion
42
41
  export { auth0Preset, keycloakPreset, oktaPreset };
43
42
 
44
43
  //# sourceMappingURL=enterprise.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { ShelfSocialProvider } from "../engine.mjs";
4
- //#region src/presets/social.d.ts
5
4
  /** Client credentials shared by the OAuth social presets. */
6
5
  interface SocialClientOptions {
7
6
  clientId: string;
@@ -29,6 +28,5 @@ declare function cognitoPreset(options: {
29
28
  clientSecret?: string;
30
29
  label?: string;
31
30
  }): ShelfSocialProvider;
32
- //#endregion
33
31
  export { SocialClientOptions, cognitoPreset, entraPreset, githubPreset, gitlabPreset, googlePreset };
34
32
  //# sourceMappingURL=social.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/presets/social.ts
4
+ // src/presets/social.ts
5
5
  /** GitHub social login (`github`). */
6
6
  function githubPreset(options) {
7
7
  return {
@@ -54,7 +54,6 @@ function cognitoPreset(options) {
54
54
  }
55
55
  };
56
56
  }
57
- //#endregion
58
57
  export { cognitoPreset, entraPreset, githubPreset, gitlabPreset, googlePreset };
59
58
 
60
59
  //# sourceMappingURL=social.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { ShelfSSOProvider } from "../engine.mjs";
4
- //#region src/presets/sso.d.ts
5
4
  /** Client credentials shared by the OIDC SSO presets. */
6
5
  interface SSOClientOptions {
7
6
  /** Email domain routing key (e.g. "acme.example"). */
@@ -67,6 +66,5 @@ declare function ssoCallbackUrls(baseURL: string, providerId: string): {
67
66
  metadata: string;
68
67
  oidcCallback: string;
69
68
  };
70
- //#endregion
71
69
  export { SAMLGroupOptions, SAMLIdPOptions, SSOClientOptions, samlPreset, ssoAuth0Preset, ssoCallbackUrls, ssoEntraPreset, ssoGooglePreset, ssoKeycloakPreset, ssoOktaPreset };
72
70
  //# sourceMappingURL=sso.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/presets/sso.ts
4
+ // src/presets/sso.ts
5
5
  /** Build the `groups` entry when either knob is set (else no group reads). */
6
6
  function groupSyncOf(options) {
7
7
  if (options.claim === void 0 && options.admins === void 0) return;
@@ -130,7 +130,6 @@ function ssoCallbackUrls(baseURL, providerId) {
130
130
  oidcCallback: `${root}/api/auth/sso/callback/${providerId}`
131
131
  };
132
132
  }
133
- //#endregion
134
133
  export { samlPreset, ssoAuth0Preset, ssoCallbackUrls, ssoEntraPreset, ssoGooglePreset, ssoKeycloakPreset, ssoOktaPreset };
135
134
 
136
135
  //# sourceMappingURL=sso.mjs.map
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { unsignedToken } from "@storyshelf/core/auth";
4
4
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
5
- //#region src/sessions.d.ts
6
5
  /** One device session for the profile page. */
7
6
  interface EngineSessionInfo {
8
7
  id: string;
@@ -14,6 +13,5 @@ interface EngineSessionInfo {
14
13
  }
15
14
  /** Active sessions for a user, newest first (expired rows are dropped). */
16
15
  declare function listUserSessions(db: DatabaseAdapter, userId: string): Promise<EngineSessionInfo[]>;
17
- //#endregion
18
16
  export { EngineSessionInfo, listUserSessions, unsignedToken };
19
17
  //# sourceMappingURL=sessions.d.mts.map
package/dist/sessions.mjs CHANGED
@@ -5,7 +5,7 @@ import { baseAuthTables } from "./auth-tables.mjs";
5
5
  import { tableColumn } from "./invites.mjs";
6
6
  import { eq } from "@storyshelf/core/orm";
7
7
  import { unsignedToken } from "@storyshelf/core/auth";
8
- //#region src/sessions.ts
8
+ // src/sessions.ts
9
9
  /** Delete every session row for a user (eager revocation on disable). */
10
10
  async function deleteUserSessions(db, userId) {
11
11
  const rows = await db.list(baseAuthTables.session, { where: eq(tableColumn(baseAuthTables.session, "userId"), userId) });
@@ -39,7 +39,6 @@ async function listUserSessions(db, userId) {
39
39
  expiresAt: String(row["expiresAt"])
40
40
  })).toSorted((a, b) => a.createdAt === b.createdAt ? a.id.localeCompare(b.id) : a.createdAt < b.createdAt ? 1 : -1);
41
41
  }
42
- //#endregion
43
42
  export { deleteSessionByToken, deleteUserSessions, listUserSessions, unsignedToken };
44
43
 
45
44
  //# sourceMappingURL=sessions.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storyshelf/auth",
3
- "version": "0.7.0",
3
+ "version": "0.9.0-next-d2cc082",
4
4
  "description": "Better Auth engine for StoryShelf: opaque DBAdapter bridge, shelf factory, and auth presets.",
5
5
  "keywords": [
6
6
  "auth",
@@ -54,13 +54,13 @@
54
54
  "dependencies": {
55
55
  "@better-auth/passkey": "1.7.6",
56
56
  "@better-auth/sso": "1.7.6",
57
- "@storyshelf/core": "0.7.0",
58
- "better-auth": "1.7.6",
59
- "zod": "^4.2.0"
57
+ "@storyshelf/core": "0.9.0-next-d2cc082",
58
+ "better-auth": "1.7.14",
59
+ "zod": "^4.6.7"
60
60
  },
61
61
  "devDependencies": {
62
- "@storyshelf/db-postgres": "0.7.0",
63
- "@storyshelf/db-sqlite": "0.7.0",
62
+ "@storyshelf/db-postgres": "0.9.0-next-d2cc082",
63
+ "@storyshelf/db-sqlite": "0.9.0-next-d2cc082",
64
64
  "@types/node": "^26.0.0",
65
65
  "@vitest/coverage-v8": "^4.0.0",
66
66
  "oxfmt": "^0.65.0",
@@ -68,18 +68,9 @@
68
68
  "oxlint-tsgolint": "^7.0.2001",
69
69
  "tsdown": "^0.22.14",
70
70
  "typescript": "^7.0.2",
71
- "vitest": "^5.0.0"
71
+ "vitest": "^5.0.5"
72
72
  },
73
73
  "engines": {
74
74
  "node": ">=24"
75
- },
76
- "jsr": {
77
- "runtimeCompat": {
78
- "node": true,
79
- "deno": true,
80
- "browser": false,
81
- "workerd": false,
82
- "bun": true
83
- }
84
75
  }
85
76
  }