@storyshelf/auth 0.7.0 → 0.8.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/dist/auth-tables-pg.d.mts +0 -2
- package/dist/auth-tables-pg.mjs +1 -2
- package/dist/auth-tables.d.mts +0 -2
- package/dist/auth-tables.mjs +1 -2
- package/dist/bootstrap.d.mts +0 -2
- package/dist/bootstrap.mjs +1 -2
- package/dist/config.d.mts +0 -2
- package/dist/config.mjs +1 -2
- package/dist/db-bridge.d.mts +0 -2
- package/dist/db-bridge.mjs +3 -3
- package/dist/db-bridge.mjs.map +1 -1
- package/dist/db-values.mjs +1 -2
- package/dist/db-where.mjs +1 -2
- package/dist/engine.d.mts +0 -2
- package/dist/engine.mjs +1 -2
- package/dist/groups.d.mts +0 -2
- package/dist/groups.mjs +1 -2
- package/dist/invites.d.mts +0 -2
- package/dist/invites.mjs +1 -2
- package/dist/passkeys.d.mts +0 -2
- package/dist/passkeys.mjs +1 -2
- package/dist/presets/enterprise.d.mts +0 -2
- package/dist/presets/enterprise.mjs +1 -2
- package/dist/presets/social.d.mts +0 -2
- package/dist/presets/social.mjs +1 -2
- package/dist/presets/sso.d.mts +0 -2
- package/dist/presets/sso.mjs +1 -2
- package/dist/sessions.d.mts +0 -2
- package/dist/sessions.mjs +1 -2
- package/package.json +7 -7
|
@@ -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
|
package/dist/auth-tables-pg.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/auth-tables.d.mts
CHANGED
|
@@ -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
|
package/dist/auth-tables.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/bootstrap.d.mts
CHANGED
|
@@ -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
|
package/dist/bootstrap.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
package/dist/db-bridge.d.mts
CHANGED
|
@@ -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
|
package/dist/db-bridge.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/db-bridge.mjs.map
CHANGED
|
@@ -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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"}
|
package/dist/db-values.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/dist/invites.d.mts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/passkeys.d.mts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
package/dist/presets/social.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
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
|
package/dist/presets/sso.d.mts
CHANGED
|
@@ -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
|
package/dist/presets/sso.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
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
|
package/dist/sessions.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 { 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
|
-
|
|
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.
|
|
3
|
+
"version": "0.8.0",
|
|
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.
|
|
58
|
-
"better-auth": "1.7.
|
|
59
|
-
"zod": "^4.
|
|
57
|
+
"@storyshelf/core": "0.8.0",
|
|
58
|
+
"better-auth": "1.7.14",
|
|
59
|
+
"zod": "^4.6.7"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@storyshelf/db-postgres": "0.
|
|
63
|
-
"@storyshelf/db-sqlite": "0.
|
|
62
|
+
"@storyshelf/db-postgres": "0.8.0",
|
|
63
|
+
"@storyshelf/db-sqlite": "0.8.0",
|
|
64
64
|
"@types/node": "^26.0.0",
|
|
65
65
|
"@vitest/coverage-v8": "^4.0.0",
|
|
66
66
|
"oxfmt": "^0.65.0",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"oxlint-tsgolint": "^7.0.2001",
|
|
69
69
|
"tsdown": "^0.22.14",
|
|
70
70
|
"typescript": "^7.0.2",
|
|
71
|
-
"vitest": "^5.0.
|
|
71
|
+
"vitest": "^5.0.5"
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
74
74
|
"node": ">=24"
|