@usebetterdev/tenant-drizzle 0.2.0-beta.12 → 0.2.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,23 +27,18 @@ const tenant = betterTenant({
27
27
  // In handlers: tenant.getDatabase() returns the transaction handle; tenant.getContext().tenant is the loaded Tenant (set loadTenant: false to skip loading).
28
28
  ```
29
29
 
30
- `drizzleDatabase(db)` bundles `drizzleAdapter` and `createGetTenantRepository` into a single `DatabaseProvider`. If you use a custom tenants table, construct the provider manually:
30
+ `drizzleDatabase(db)` bundles the adapter and tenant repository into a single `DatabaseProvider`. If you use a custom tenants table, pass it via the `table` option:
31
31
 
32
32
  ```ts
33
- import { drizzleAdapter, createGetTenantRepository } from "@usebetterdev/tenant/drizzle";
33
+ import { drizzleDatabase } from "@usebetterdev/tenant/drizzle";
34
34
 
35
35
  const tenant = betterTenant({
36
- database: {
37
- adapter: drizzleAdapter(db),
38
- getTenantRepository: createGetTenantRepository(myCustomTenantsTable),
39
- },
36
+ database: drizzleDatabase(db, { table: myCustomTenantsTable }),
40
37
  tenantResolver: { header: "x-tenant-id" },
41
38
  });
42
39
  ```
43
40
 
44
- The `getTenantRepository` is required in the `database` provider. If you construct the provider manually instead of using `drizzleDatabase()`, you must include it.
45
-
46
- Table shape for tenants must match the CLI-generated schema: `id` (UUID), `name`, `slug`, `created_at`. Use the exported `tenantsTable` or pass your own table with the same column contract to `createGetTenantRepository`.
41
+ Table shape for tenants must match the CLI-generated schema: `id` (UUID), `name`, `slug`, `created_at`. Use the exported `tenantsTable` or pass your own table with the same column contract.
47
42
 
48
43
  ## Behaviour
49
44
 
@@ -60,4 +55,4 @@ Integration tests in `src/adapter.integration.test.ts` use [@testcontainers/post
60
55
 
61
56
  ## Peer dependency
62
57
 
63
- Requires `pg` (node-postgres). Install it in your app when using this adapter.
58
+ Requires either `pg` (node-postgres) or `postgres` (postgres.js). Install whichever driver you use.
package/dist/index.cjs CHANGED
@@ -20,9 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- createGetTenantRepository: () => createGetTenantRepository,
24
- drizzleAdapter: () => drizzleAdapter,
25
23
  drizzleDatabase: () => drizzleDatabase,
24
+ tenantId: () => tenantId,
26
25
  tenantsTable: () => tenantsTable
27
26
  });
28
27
  module.exports = __toCommonJS(index_exports);
@@ -31,10 +30,10 @@ module.exports = __toCommonJS(index_exports);
31
30
  var import_drizzle_orm = require("drizzle-orm");
32
31
  function drizzleAdapter(database) {
33
32
  return {
34
- async runWithTenant(tenantId, fn) {
33
+ async runWithTenant(tenantId2, fn) {
35
34
  return database.transaction(async (tx) => {
36
35
  await tx.execute(
37
- import_drizzle_orm.sql`SELECT set_config('app.current_tenant', ${tenantId}, true)`
36
+ import_drizzle_orm.sql`SELECT set_config('app.current_tenant', ${tenantId2}, true)`
38
37
  );
39
38
  return fn(tx);
40
39
  });
@@ -51,9 +50,10 @@ function drizzleAdapter(database) {
51
50
  }
52
51
 
53
52
  // src/repository.ts
54
- var import_drizzle_orm2 = require("drizzle-orm");
53
+ var import_drizzle_orm3 = require("drizzle-orm");
55
54
 
56
55
  // src/schema.ts
56
+ var import_drizzle_orm2 = require("drizzle-orm");
57
57
  var import_pg_core = require("drizzle-orm/pg-core");
58
58
  var tenantsTable = (0, import_pg_core.pgTable)("tenants", {
59
59
  id: (0, import_pg_core.uuid)("id").primaryKey().defaultRandom(),
@@ -61,6 +61,9 @@ var tenantsTable = (0, import_pg_core.pgTable)("tenants", {
61
61
  slug: (0, import_pg_core.text)("slug").notNull().unique(),
62
62
  createdAt: (0, import_pg_core.timestamp)("created_at", { withTimezone: true }).defaultNow().notNull()
63
63
  });
64
+ var tenantId = {
65
+ tenantId: (0, import_pg_core.uuid)("tenant_id").notNull().references(() => tenantsTable.id).default(import_drizzle_orm2.sql`(current_setting('app.current_tenant', true))::uuid`)
66
+ };
64
67
 
65
68
  // src/repository.ts
66
69
  function rowToTenant(row) {
@@ -91,27 +94,27 @@ function createGetTenantRepository(table = tenantsTable) {
91
94
  }
92
95
  return rowToTenant(row);
93
96
  },
94
- async update(tenantId, data) {
97
+ async update(tenantId2, data) {
95
98
  const set = {};
96
99
  if (data.name !== void 0) set.name = data.name;
97
100
  if (data.slug !== void 0) set.slug = data.slug;
98
101
  if (Object.keys(set).length === 0) {
99
- const rows2 = await db.select().from(table).where((0, import_drizzle_orm2.eq)(table.id, tenantId)).limit(1);
102
+ const rows2 = await db.select().from(table).where((0, import_drizzle_orm3.eq)(table.id, tenantId2)).limit(1);
100
103
  const row2 = Array.isArray(rows2) ? rows2[0] : void 0;
101
104
  if (!row2 || typeof row2 !== "object") {
102
105
  throw new Error("better-tenant: tenant not found");
103
106
  }
104
107
  return rowToTenant(row2);
105
108
  }
106
- const rows = await db.update(table).set(set).where((0, import_drizzle_orm2.eq)(table.id, tenantId)).returning();
109
+ const rows = await db.update(table).set(set).where((0, import_drizzle_orm3.eq)(table.id, tenantId2)).returning();
107
110
  const row = rows[0];
108
111
  if (!row || typeof row !== "object") {
109
112
  throw new Error("better-tenant: tenant not found");
110
113
  }
111
114
  return rowToTenant(row);
112
115
  },
113
- async getById(tenantId) {
114
- const rows = await db.select().from(table).where((0, import_drizzle_orm2.eq)(table.id, tenantId)).limit(1);
116
+ async getById(tenantId2) {
117
+ const rows = await db.select().from(table).where((0, import_drizzle_orm3.eq)(table.id, tenantId2)).limit(1);
115
118
  const row = Array.isArray(rows) ? rows[0] : void 0;
116
119
  if (!row || typeof row !== "object") {
117
120
  return null;
@@ -119,7 +122,7 @@ function createGetTenantRepository(table = tenantsTable) {
119
122
  return rowToTenant(row);
120
123
  },
121
124
  async getBySlug(slug) {
122
- const rows = await db.select().from(table).where((0, import_drizzle_orm2.eq)(table.slug, slug)).limit(1);
125
+ const rows = await db.select().from(table).where((0, import_drizzle_orm3.eq)(table.slug, slug)).limit(1);
123
126
  const row = Array.isArray(rows) ? rows[0] : void 0;
124
127
  if (!row || typeof row !== "object") {
125
128
  return null;
@@ -127,15 +130,15 @@ function createGetTenantRepository(table = tenantsTable) {
127
130
  return rowToTenant(row);
128
131
  },
129
132
  async list(options = {}) {
130
- const limit = Math.min(options.limit ?? 50, 50);
133
+ const limit = options.limit ?? 50;
131
134
  const offset = Math.max(0, options.offset ?? 0);
132
135
  const rows = await db.select().from(table).limit(limit).offset(offset);
133
136
  return (Array.isArray(rows) ? rows : []).map(
134
137
  (r) => rowToTenant(r)
135
138
  );
136
139
  },
137
- async delete(tenantId) {
138
- await db.delete(table).where((0, import_drizzle_orm2.eq)(table.id, tenantId));
140
+ async delete(tenantId2) {
141
+ await db.delete(table).where((0, import_drizzle_orm3.eq)(table.id, tenantId2));
139
142
  }
140
143
  };
141
144
  };
@@ -150,9 +153,8 @@ function drizzleDatabase(db, options) {
150
153
  }
151
154
  // Annotate the CommonJS export names for ESM import in node:
152
155
  0 && (module.exports = {
153
- createGetTenantRepository,
154
- drizzleAdapter,
155
156
  drizzleDatabase,
157
+ tenantId,
156
158
  tenantsTable
157
159
  });
158
160
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/repository.ts","../src/schema.ts","../src/database.ts"],"sourcesContent":["export { drizzleAdapter } from \"./adapter.js\";\nexport type { DrizzlePgDatabase, DrizzlePgTransaction } from \"./adapter.js\";\nexport { drizzleDatabase } from \"./database.js\";\nexport { tenantsTable } from \"./schema.js\";\nexport {\n createGetTenantRepository,\n type TenantsTableLike,\n} from \"./repository.js\";\n","import { sql } from \"drizzle-orm\";\nimport type { TenantAdapter } from \"@usebetterdev/tenant-core\";\n\n/**\n * Minimal shape for a Drizzle pg transaction: must support execute(sql).\n * The actual Drizzle transaction extends this with insert, select, etc.\n */\nexport interface DrizzlePgTransaction {\n execute(query: ReturnType<typeof sql> | unknown): Promise<unknown>;\n}\n\n/**\n * Minimal shape for a Drizzle pg database: must support transaction().\n * Generic over TTx so the full transaction type (with insert, select, etc.) is preserved.\n */\nexport interface DrizzlePgDatabase<TTx extends DrizzlePgTransaction = DrizzlePgTransaction> {\n transaction<T>(\n callback: (tx: TTx) => Promise<T>,\n ): Promise<T>;\n}\n\n/**\n * Creates a TenantAdapter for Drizzle (PostgreSQL).\n *\n * Uses a transaction per runWithTenant: BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n * The handle passed to fn is the transaction runner; use only this handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = true so RLS policies that allow\n * when current_setting('app.bypass_rls', true) = 'true' permit access (CLI generates this bypass policy).\n *\n * @param database - Drizzle pg database (from drizzle(pool) or similar)\n * @returns TenantAdapter implementation\n */\nexport function drizzleAdapter<TTx extends DrizzlePgTransaction>(\n database: DrizzlePgDatabase<TTx>,\n): TenantAdapter<TTx, TTx> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.current_tenant', ${tenantId}, true)`,\n );\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.bypass_rls', 'true', true)`,\n );\n return fn(tx);\n });\n },\n };\n}\n","import { eq } from \"drizzle-orm\";\nimport type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport { tenantsTable } from \"./schema.js\";\n\n/** Table shape expected by createGetTenantRepository: id, name, slug, createdAt (or created_at). */\nexport type TenantsTableLike = typeof tenantsTable;\n\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.createdAt ?? row.created_at;\n if (createdAt === undefined || createdAt === null) {\n throw new Error(\"better-tenant: row missing created_at / createdAt\");\n }\n return {\n id: String(row.id),\n name: String(row.name),\n slug: String(row.slug),\n createdAt: createdAt as Date | string,\n ...row,\n };\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n * Pass the tenants table (e.g. tenantsTable from this package or your own with id, name, slug, created_at).\n * The returned function is called by core with the system database handle (from adapter.runAsSystem).\n */\nexport function createGetTenantRepository(\n table: TenantsTableLike = tenantsTable,\n): (database: unknown) => TenantRepository {\n return (database: unknown) => {\n const db = database as {\n insert: (t: TenantsTableLike) => {\n values: (v: { name: string; slug: string }) => {\n returning: () => Promise<unknown[]>;\n };\n };\n update: (t: TenantsTableLike) => {\n set: (v: Partial<{ name: string; slug: string }>) => {\n where: (c: unknown) => { returning: () => Promise<unknown[]> };\n };\n };\n select: () => {\n from: (t: TenantsTableLike) => {\n where: (c: unknown) => { limit: (n: number) => Promise<unknown[]> };\n limit: (n: number) => { offset: (n: number) => Promise<unknown[]> };\n };\n };\n delete: (t: TenantsTableLike) => {\n where: (c: unknown) => Promise<unknown>;\n };\n };\n\n return {\n async create(data) {\n const rows = await db\n .insert(table)\n .values({\n name: data.name,\n slug: data.slug,\n })\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: createTenant failed to return row\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async update(tenantId, data) {\n const set: Partial<{ name: string; slug: string }> = {};\n if (data.name !== undefined) set.name = data.name;\n if (data.slug !== undefined) set.slug = data.slug;\n if (Object.keys(set).length === 0) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n }\n const rows = await db\n .update(table)\n .set(set)\n .where(eq(table.id, tenantId))\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getById(tenantId: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getBySlug(slug: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.slug, slug))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async list(options = {}) {\n const limit = Math.min(options.limit ?? 50, 50);\n const offset = Math.max(0, options.offset ?? 0);\n const rows = await db.select().from(table).limit(limit).offset(offset);\n return (Array.isArray(rows) ? rows : []).map((r) =>\n rowToTenant(r as Record<string, unknown>),\n );\n },\n\n async delete(tenantId) {\n await db.delete(table).where(eq(table.id, tenantId));\n },\n };\n };\n}\n","import { pgTable, text, timestamp, uuid } from \"drizzle-orm/pg-core\";\n\n/**\n * Standard tenants table schema matching the CLI-generated shape.\n * Columns: id (UUID), name, slug, created_at.\n * Use this table or provide your own with the same column contract for createGetTenantRepository.\n */\nexport const tenantsTable = pgTable(\"tenants\", {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n name: text(\"name\").notNull(),\n slug: text(\"slug\").notNull().unique(),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { drizzleAdapter } from \"./adapter.js\";\nimport type { DrizzlePgDatabase, DrizzlePgTransaction } from \"./adapter.js\";\nimport { createGetTenantRepository, type TenantsTableLike } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Drizzle (PostgreSQL).\n *\n * Bundles drizzleAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: drizzleDatabase(db) })`.\n *\n * @param db - Drizzle pg database (from drizzle(pool) or similar)\n * @param options - Optional: custom tenants table\n */\nexport function drizzleDatabase<TTx extends DrizzlePgTransaction>(\n db: DrizzlePgDatabase<TTx>,\n options?: { table?: TenantsTableLike },\n): DatabaseProvider<TTx, TTx> {\n return {\n adapter: drizzleAdapter(db),\n getTenantRepository: createGetTenantRepository(options?.table),\n };\n}\n"],"mappings":"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","names":["import_drizzle_orm","rows","row"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/repository.ts","../src/schema.ts","../src/database.ts"],"sourcesContent":["export { drizzleDatabase } from \"./database.js\";\nexport { tenantsTable, tenantId } from \"./schema.js\";\n","import { sql } from \"drizzle-orm\";\nimport type { TenantAdapter } from \"@usebetterdev/tenant-core\";\n\n/**\n * Minimal shape for a Drizzle pg transaction: must support execute(sql).\n * The actual Drizzle transaction extends this with insert, select, etc.\n */\nexport interface DrizzlePgTransaction {\n execute(query: ReturnType<typeof sql> | unknown): Promise<unknown>;\n}\n\n/**\n * Minimal shape for a Drizzle pg database: must support transaction().\n * Generic over TTx so the full transaction type (with insert, select, etc.) is preserved.\n */\nexport interface DrizzlePgDatabase<TTx extends DrizzlePgTransaction = DrizzlePgTransaction> {\n transaction<T>(\n callback: (tx: TTx) => Promise<T>,\n ): Promise<T>;\n}\n\n/**\n * Creates a TenantAdapter for Drizzle (PostgreSQL).\n *\n * Uses a transaction per runWithTenant: BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n * The handle passed to fn is the transaction runner; use only this handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = true so RLS policies that allow\n * when current_setting('app.bypass_rls', true) = 'true' permit access (CLI generates this bypass policy).\n *\n * @param database - Drizzle pg database (from drizzle(pool) or similar)\n * @returns TenantAdapter implementation\n */\nexport function drizzleAdapter<TTx extends DrizzlePgTransaction>(\n database: DrizzlePgDatabase<TTx>,\n): TenantAdapter<TTx, TTx> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.current_tenant', ${tenantId}, true)`,\n );\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.bypass_rls', 'true', true)`,\n );\n return fn(tx);\n });\n },\n };\n}\n","import { eq } from \"drizzle-orm\";\nimport type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport { tenantsTable } from \"./schema.js\";\n\n/** Table shape expected by createGetTenantRepository: id, name, slug, createdAt (or created_at). */\nexport type TenantsTableLike = typeof tenantsTable;\n\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.createdAt ?? row.created_at;\n if (createdAt === undefined || createdAt === null) {\n throw new Error(\"better-tenant: row missing created_at / createdAt\");\n }\n return {\n id: String(row.id),\n name: String(row.name),\n slug: String(row.slug),\n createdAt: createdAt as Date | string,\n ...row,\n };\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n * Pass the tenants table (e.g. tenantsTable from this package or your own with id, name, slug, created_at).\n * The returned function is called by core with the system database handle (from adapter.runAsSystem).\n */\nexport function createGetTenantRepository(\n table: TenantsTableLike = tenantsTable,\n): (database: unknown) => TenantRepository {\n return (database: unknown) => {\n const db = database as {\n insert: (t: TenantsTableLike) => {\n values: (v: { name: string; slug: string }) => {\n returning: () => Promise<unknown[]>;\n };\n };\n update: (t: TenantsTableLike) => {\n set: (v: Partial<{ name: string; slug: string }>) => {\n where: (c: unknown) => { returning: () => Promise<unknown[]> };\n };\n };\n select: () => {\n from: (t: TenantsTableLike) => {\n where: (c: unknown) => { limit: (n: number) => Promise<unknown[]> };\n limit: (n: number) => { offset: (n: number) => Promise<unknown[]> };\n };\n };\n delete: (t: TenantsTableLike) => {\n where: (c: unknown) => Promise<unknown>;\n };\n };\n\n return {\n async create(data) {\n const rows = await db\n .insert(table)\n .values({\n name: data.name,\n slug: data.slug,\n })\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: createTenant failed to return row\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async update(tenantId, data) {\n const set: Partial<{ name: string; slug: string }> = {};\n if (data.name !== undefined) set.name = data.name;\n if (data.slug !== undefined) set.slug = data.slug;\n if (Object.keys(set).length === 0) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n }\n const rows = await db\n .update(table)\n .set(set)\n .where(eq(table.id, tenantId))\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getById(tenantId: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getBySlug(slug: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.slug, slug))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async list(options = {}) {\n const limit = options.limit ?? 50;\n const offset = Math.max(0, options.offset ?? 0);\n const rows = await db.select().from(table).limit(limit).offset(offset);\n return (Array.isArray(rows) ? rows : []).map((r) =>\n rowToTenant(r as Record<string, unknown>),\n );\n },\n\n async delete(tenantId) {\n await db.delete(table).where(eq(table.id, tenantId));\n },\n };\n };\n}\n","import { sql } from \"drizzle-orm\";\nimport { pgTable, text, timestamp, uuid } from \"drizzle-orm/pg-core\";\n\n/**\n * Standard tenants table schema matching the CLI-generated shape.\n * Columns: id (UUID), name, slug, created_at.\n * Use this table or provide your own with the same column contract for createGetTenantRepository.\n */\nexport const tenantsTable = pgTable(\"tenants\", {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n name: text(\"name\").notNull(),\n slug: text(\"slug\").notNull().unique(),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n\n/**\n * Pre-configured tenant_id column for use in tenant-scoped tables.\n * References tenants.id and auto-populates from the current session variable.\n *\n * Usage:\n * ```ts\n * import { tenantId } from \"@usebetterdev/tenant/drizzle\";\n *\n * export const projectsTable = pgTable(\"projects\", {\n * id: serial(\"id\").primaryKey(),\n * name: text(\"name\").notNull(),\n * ...tenantId,\n * });\n * ```\n */\nexport const tenantId = {\n tenantId: uuid(\"tenant_id\")\n .notNull()\n .references(() => tenantsTable.id)\n .default(sql`(current_setting('app.current_tenant', true))::uuid`),\n} as const;\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { drizzleAdapter } from \"./adapter.js\";\nimport type { DrizzlePgDatabase, DrizzlePgTransaction } from \"./adapter.js\";\nimport { createGetTenantRepository, type TenantsTableLike } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Drizzle (PostgreSQL).\n *\n * Bundles drizzleAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: drizzleDatabase(db) })`.\n *\n * @param db - Drizzle pg database (from drizzle(pool) or similar)\n * @param options - Optional: custom tenants table\n */\nexport function drizzleDatabase<TTx extends DrizzlePgTransaction>(\n db: DrizzlePgDatabase<TTx>,\n options?: { table?: TenantsTableLike },\n): DatabaseProvider<TTx, TTx> {\n return {\n adapter: drizzleAdapter(db),\n getTenantRepository: createGetTenantRepository(options?.table),\n };\n}\n"],"mappings":"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","names":["tenantId","import_drizzle_orm","import_drizzle_orm","tenantId","rows","row"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
+ import { DatabaseProvider } from '@usebetterdev/tenant-core';
2
+ import * as drizzle_orm from 'drizzle-orm';
1
3
  import { sql } from 'drizzle-orm';
2
- import { TenantAdapter, TenantRepository, DatabaseProvider } from '@usebetterdev/tenant-core';
3
4
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
5
 
5
6
  /**
@@ -16,19 +17,6 @@ interface DrizzlePgTransaction {
16
17
  interface DrizzlePgDatabase<TTx extends DrizzlePgTransaction = DrizzlePgTransaction> {
17
18
  transaction<T>(callback: (tx: TTx) => Promise<T>): Promise<T>;
18
19
  }
19
- /**
20
- * Creates a TenantAdapter for Drizzle (PostgreSQL).
21
- *
22
- * Uses a transaction per runWithTenant: BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.
23
- * The handle passed to fn is the transaction runner; use only this handle for tenant-scoped queries so RLS applies.
24
- *
25
- * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = true so RLS policies that allow
26
- * when current_setting('app.bypass_rls', true) = 'true' permit access (CLI generates this bypass policy).
27
- *
28
- * @param database - Drizzle pg database (from drizzle(pool) or similar)
29
- * @returns TenantAdapter implementation
30
- */
31
- declare function drizzleAdapter<TTx extends DrizzlePgTransaction>(database: DrizzlePgDatabase<TTx>): TenantAdapter<TTx, TTx>;
32
20
 
33
21
  /**
34
22
  * Standard tenants table schema matching the CLI-generated shape.
@@ -110,15 +98,27 @@ declare const tenantsTable: drizzle_orm_pg_core.PgTableWithColumns<{
110
98
  };
111
99
  dialect: "pg";
112
100
  }>;
101
+ /**
102
+ * Pre-configured tenant_id column for use in tenant-scoped tables.
103
+ * References tenants.id and auto-populates from the current session variable.
104
+ *
105
+ * Usage:
106
+ * ```ts
107
+ * import { tenantId } from "@usebetterdev/tenant/drizzle";
108
+ *
109
+ * export const projectsTable = pgTable("projects", {
110
+ * id: serial("id").primaryKey(),
111
+ * name: text("name").notNull(),
112
+ * ...tenantId,
113
+ * });
114
+ * ```
115
+ */
116
+ declare const tenantId: {
117
+ readonly tenantId: drizzle_orm.HasDefault<drizzle_orm.NotNull<drizzle_orm_pg_core.PgUUIDBuilderInitial<"tenant_id">>>;
118
+ };
113
119
 
114
120
  /** Table shape expected by createGetTenantRepository: id, name, slug, createdAt (or created_at). */
115
121
  type TenantsTableLike = typeof tenantsTable;
116
- /**
117
- * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).
118
- * Pass the tenants table (e.g. tenantsTable from this package or your own with id, name, slug, created_at).
119
- * The returned function is called by core with the system database handle (from adapter.runAsSystem).
120
- */
121
- declare function createGetTenantRepository(table?: TenantsTableLike): (database: unknown) => TenantRepository;
122
122
 
123
123
  /**
124
124
  * Creates a DatabaseProvider for Drizzle (PostgreSQL).
@@ -133,4 +133,4 @@ declare function drizzleDatabase<TTx extends DrizzlePgTransaction>(db: DrizzlePg
133
133
  table?: TenantsTableLike;
134
134
  }): DatabaseProvider<TTx, TTx>;
135
135
 
136
- export { type DrizzlePgDatabase, type DrizzlePgTransaction, type TenantsTableLike, createGetTenantRepository, drizzleAdapter, drizzleDatabase, tenantsTable };
136
+ export { drizzleDatabase, tenantId, tenantsTable };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ import { DatabaseProvider } from '@usebetterdev/tenant-core';
2
+ import * as drizzle_orm from 'drizzle-orm';
1
3
  import { sql } from 'drizzle-orm';
2
- import { TenantAdapter, TenantRepository, DatabaseProvider } from '@usebetterdev/tenant-core';
3
4
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
5
 
5
6
  /**
@@ -16,19 +17,6 @@ interface DrizzlePgTransaction {
16
17
  interface DrizzlePgDatabase<TTx extends DrizzlePgTransaction = DrizzlePgTransaction> {
17
18
  transaction<T>(callback: (tx: TTx) => Promise<T>): Promise<T>;
18
19
  }
19
- /**
20
- * Creates a TenantAdapter for Drizzle (PostgreSQL).
21
- *
22
- * Uses a transaction per runWithTenant: BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.
23
- * The handle passed to fn is the transaction runner; use only this handle for tenant-scoped queries so RLS applies.
24
- *
25
- * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = true so RLS policies that allow
26
- * when current_setting('app.bypass_rls', true) = 'true' permit access (CLI generates this bypass policy).
27
- *
28
- * @param database - Drizzle pg database (from drizzle(pool) or similar)
29
- * @returns TenantAdapter implementation
30
- */
31
- declare function drizzleAdapter<TTx extends DrizzlePgTransaction>(database: DrizzlePgDatabase<TTx>): TenantAdapter<TTx, TTx>;
32
20
 
33
21
  /**
34
22
  * Standard tenants table schema matching the CLI-generated shape.
@@ -110,15 +98,27 @@ declare const tenantsTable: drizzle_orm_pg_core.PgTableWithColumns<{
110
98
  };
111
99
  dialect: "pg";
112
100
  }>;
101
+ /**
102
+ * Pre-configured tenant_id column for use in tenant-scoped tables.
103
+ * References tenants.id and auto-populates from the current session variable.
104
+ *
105
+ * Usage:
106
+ * ```ts
107
+ * import { tenantId } from "@usebetterdev/tenant/drizzle";
108
+ *
109
+ * export const projectsTable = pgTable("projects", {
110
+ * id: serial("id").primaryKey(),
111
+ * name: text("name").notNull(),
112
+ * ...tenantId,
113
+ * });
114
+ * ```
115
+ */
116
+ declare const tenantId: {
117
+ readonly tenantId: drizzle_orm.HasDefault<drizzle_orm.NotNull<drizzle_orm_pg_core.PgUUIDBuilderInitial<"tenant_id">>>;
118
+ };
113
119
 
114
120
  /** Table shape expected by createGetTenantRepository: id, name, slug, createdAt (or created_at). */
115
121
  type TenantsTableLike = typeof tenantsTable;
116
- /**
117
- * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).
118
- * Pass the tenants table (e.g. tenantsTable from this package or your own with id, name, slug, created_at).
119
- * The returned function is called by core with the system database handle (from adapter.runAsSystem).
120
- */
121
- declare function createGetTenantRepository(table?: TenantsTableLike): (database: unknown) => TenantRepository;
122
122
 
123
123
  /**
124
124
  * Creates a DatabaseProvider for Drizzle (PostgreSQL).
@@ -133,4 +133,4 @@ declare function drizzleDatabase<TTx extends DrizzlePgTransaction>(db: DrizzlePg
133
133
  table?: TenantsTableLike;
134
134
  }): DatabaseProvider<TTx, TTx>;
135
135
 
136
- export { type DrizzlePgDatabase, type DrizzlePgTransaction, type TenantsTableLike, createGetTenantRepository, drizzleAdapter, drizzleDatabase, tenantsTable };
136
+ export { drizzleDatabase, tenantId, tenantsTable };
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import { sql } from "drizzle-orm";
3
3
  function drizzleAdapter(database) {
4
4
  return {
5
- async runWithTenant(tenantId, fn) {
5
+ async runWithTenant(tenantId2, fn) {
6
6
  return database.transaction(async (tx) => {
7
7
  await tx.execute(
8
- sql`SELECT set_config('app.current_tenant', ${tenantId}, true)`
8
+ sql`SELECT set_config('app.current_tenant', ${tenantId2}, true)`
9
9
  );
10
10
  return fn(tx);
11
11
  });
@@ -25,6 +25,7 @@ function drizzleAdapter(database) {
25
25
  import { eq } from "drizzle-orm";
26
26
 
27
27
  // src/schema.ts
28
+ import { sql as sql2 } from "drizzle-orm";
28
29
  import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
29
30
  var tenantsTable = pgTable("tenants", {
30
31
  id: uuid("id").primaryKey().defaultRandom(),
@@ -32,6 +33,9 @@ var tenantsTable = pgTable("tenants", {
32
33
  slug: text("slug").notNull().unique(),
33
34
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
34
35
  });
36
+ var tenantId = {
37
+ tenantId: uuid("tenant_id").notNull().references(() => tenantsTable.id).default(sql2`(current_setting('app.current_tenant', true))::uuid`)
38
+ };
35
39
 
36
40
  // src/repository.ts
37
41
  function rowToTenant(row) {
@@ -62,27 +66,27 @@ function createGetTenantRepository(table = tenantsTable) {
62
66
  }
63
67
  return rowToTenant(row);
64
68
  },
65
- async update(tenantId, data) {
69
+ async update(tenantId2, data) {
66
70
  const set = {};
67
71
  if (data.name !== void 0) set.name = data.name;
68
72
  if (data.slug !== void 0) set.slug = data.slug;
69
73
  if (Object.keys(set).length === 0) {
70
- const rows2 = await db.select().from(table).where(eq(table.id, tenantId)).limit(1);
74
+ const rows2 = await db.select().from(table).where(eq(table.id, tenantId2)).limit(1);
71
75
  const row2 = Array.isArray(rows2) ? rows2[0] : void 0;
72
76
  if (!row2 || typeof row2 !== "object") {
73
77
  throw new Error("better-tenant: tenant not found");
74
78
  }
75
79
  return rowToTenant(row2);
76
80
  }
77
- const rows = await db.update(table).set(set).where(eq(table.id, tenantId)).returning();
81
+ const rows = await db.update(table).set(set).where(eq(table.id, tenantId2)).returning();
78
82
  const row = rows[0];
79
83
  if (!row || typeof row !== "object") {
80
84
  throw new Error("better-tenant: tenant not found");
81
85
  }
82
86
  return rowToTenant(row);
83
87
  },
84
- async getById(tenantId) {
85
- const rows = await db.select().from(table).where(eq(table.id, tenantId)).limit(1);
88
+ async getById(tenantId2) {
89
+ const rows = await db.select().from(table).where(eq(table.id, tenantId2)).limit(1);
86
90
  const row = Array.isArray(rows) ? rows[0] : void 0;
87
91
  if (!row || typeof row !== "object") {
88
92
  return null;
@@ -98,15 +102,15 @@ function createGetTenantRepository(table = tenantsTable) {
98
102
  return rowToTenant(row);
99
103
  },
100
104
  async list(options = {}) {
101
- const limit = Math.min(options.limit ?? 50, 50);
105
+ const limit = options.limit ?? 50;
102
106
  const offset = Math.max(0, options.offset ?? 0);
103
107
  const rows = await db.select().from(table).limit(limit).offset(offset);
104
108
  return (Array.isArray(rows) ? rows : []).map(
105
109
  (r) => rowToTenant(r)
106
110
  );
107
111
  },
108
- async delete(tenantId) {
109
- await db.delete(table).where(eq(table.id, tenantId));
112
+ async delete(tenantId2) {
113
+ await db.delete(table).where(eq(table.id, tenantId2));
110
114
  }
111
115
  };
112
116
  };
@@ -120,9 +124,8 @@ function drizzleDatabase(db, options) {
120
124
  };
121
125
  }
122
126
  export {
123
- createGetTenantRepository,
124
- drizzleAdapter,
125
127
  drizzleDatabase,
128
+ tenantId,
126
129
  tenantsTable
127
130
  };
128
131
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter.ts","../src/repository.ts","../src/schema.ts","../src/database.ts"],"sourcesContent":["import { sql } from \"drizzle-orm\";\nimport type { TenantAdapter } from \"@usebetterdev/tenant-core\";\n\n/**\n * Minimal shape for a Drizzle pg transaction: must support execute(sql).\n * The actual Drizzle transaction extends this with insert, select, etc.\n */\nexport interface DrizzlePgTransaction {\n execute(query: ReturnType<typeof sql> | unknown): Promise<unknown>;\n}\n\n/**\n * Minimal shape for a Drizzle pg database: must support transaction().\n * Generic over TTx so the full transaction type (with insert, select, etc.) is preserved.\n */\nexport interface DrizzlePgDatabase<TTx extends DrizzlePgTransaction = DrizzlePgTransaction> {\n transaction<T>(\n callback: (tx: TTx) => Promise<T>,\n ): Promise<T>;\n}\n\n/**\n * Creates a TenantAdapter for Drizzle (PostgreSQL).\n *\n * Uses a transaction per runWithTenant: BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n * The handle passed to fn is the transaction runner; use only this handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = true so RLS policies that allow\n * when current_setting('app.bypass_rls', true) = 'true' permit access (CLI generates this bypass policy).\n *\n * @param database - Drizzle pg database (from drizzle(pool) or similar)\n * @returns TenantAdapter implementation\n */\nexport function drizzleAdapter<TTx extends DrizzlePgTransaction>(\n database: DrizzlePgDatabase<TTx>,\n): TenantAdapter<TTx, TTx> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.current_tenant', ${tenantId}, true)`,\n );\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.bypass_rls', 'true', true)`,\n );\n return fn(tx);\n });\n },\n };\n}\n","import { eq } from \"drizzle-orm\";\nimport type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport { tenantsTable } from \"./schema.js\";\n\n/** Table shape expected by createGetTenantRepository: id, name, slug, createdAt (or created_at). */\nexport type TenantsTableLike = typeof tenantsTable;\n\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.createdAt ?? row.created_at;\n if (createdAt === undefined || createdAt === null) {\n throw new Error(\"better-tenant: row missing created_at / createdAt\");\n }\n return {\n id: String(row.id),\n name: String(row.name),\n slug: String(row.slug),\n createdAt: createdAt as Date | string,\n ...row,\n };\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n * Pass the tenants table (e.g. tenantsTable from this package or your own with id, name, slug, created_at).\n * The returned function is called by core with the system database handle (from adapter.runAsSystem).\n */\nexport function createGetTenantRepository(\n table: TenantsTableLike = tenantsTable,\n): (database: unknown) => TenantRepository {\n return (database: unknown) => {\n const db = database as {\n insert: (t: TenantsTableLike) => {\n values: (v: { name: string; slug: string }) => {\n returning: () => Promise<unknown[]>;\n };\n };\n update: (t: TenantsTableLike) => {\n set: (v: Partial<{ name: string; slug: string }>) => {\n where: (c: unknown) => { returning: () => Promise<unknown[]> };\n };\n };\n select: () => {\n from: (t: TenantsTableLike) => {\n where: (c: unknown) => { limit: (n: number) => Promise<unknown[]> };\n limit: (n: number) => { offset: (n: number) => Promise<unknown[]> };\n };\n };\n delete: (t: TenantsTableLike) => {\n where: (c: unknown) => Promise<unknown>;\n };\n };\n\n return {\n async create(data) {\n const rows = await db\n .insert(table)\n .values({\n name: data.name,\n slug: data.slug,\n })\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: createTenant failed to return row\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async update(tenantId, data) {\n const set: Partial<{ name: string; slug: string }> = {};\n if (data.name !== undefined) set.name = data.name;\n if (data.slug !== undefined) set.slug = data.slug;\n if (Object.keys(set).length === 0) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n }\n const rows = await db\n .update(table)\n .set(set)\n .where(eq(table.id, tenantId))\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getById(tenantId: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getBySlug(slug: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.slug, slug))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async list(options = {}) {\n const limit = Math.min(options.limit ?? 50, 50);\n const offset = Math.max(0, options.offset ?? 0);\n const rows = await db.select().from(table).limit(limit).offset(offset);\n return (Array.isArray(rows) ? rows : []).map((r) =>\n rowToTenant(r as Record<string, unknown>),\n );\n },\n\n async delete(tenantId) {\n await db.delete(table).where(eq(table.id, tenantId));\n },\n };\n };\n}\n","import { pgTable, text, timestamp, uuid } from \"drizzle-orm/pg-core\";\n\n/**\n * Standard tenants table schema matching the CLI-generated shape.\n * Columns: id (UUID), name, slug, created_at.\n * Use this table or provide your own with the same column contract for createGetTenantRepository.\n */\nexport const tenantsTable = pgTable(\"tenants\", {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n name: text(\"name\").notNull(),\n slug: text(\"slug\").notNull().unique(),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { drizzleAdapter } from \"./adapter.js\";\nimport type { DrizzlePgDatabase, DrizzlePgTransaction } from \"./adapter.js\";\nimport { createGetTenantRepository, type TenantsTableLike } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Drizzle (PostgreSQL).\n *\n * Bundles drizzleAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: drizzleDatabase(db) })`.\n *\n * @param db - Drizzle pg database (from drizzle(pool) or similar)\n * @param options - Optional: custom tenants table\n */\nexport function drizzleDatabase<TTx extends DrizzlePgTransaction>(\n db: DrizzlePgDatabase<TTx>,\n options?: { table?: TenantsTableLike },\n): DatabaseProvider<TTx, TTx> {\n return {\n adapter: drizzleAdapter(db),\n getTenantRepository: createGetTenantRepository(options?.table),\n };\n}\n"],"mappings":"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","names":["rows","row"]}
1
+ {"version":3,"sources":["../src/adapter.ts","../src/repository.ts","../src/schema.ts","../src/database.ts"],"sourcesContent":["import { sql } from \"drizzle-orm\";\nimport type { TenantAdapter } from \"@usebetterdev/tenant-core\";\n\n/**\n * Minimal shape for a Drizzle pg transaction: must support execute(sql).\n * The actual Drizzle transaction extends this with insert, select, etc.\n */\nexport interface DrizzlePgTransaction {\n execute(query: ReturnType<typeof sql> | unknown): Promise<unknown>;\n}\n\n/**\n * Minimal shape for a Drizzle pg database: must support transaction().\n * Generic over TTx so the full transaction type (with insert, select, etc.) is preserved.\n */\nexport interface DrizzlePgDatabase<TTx extends DrizzlePgTransaction = DrizzlePgTransaction> {\n transaction<T>(\n callback: (tx: TTx) => Promise<T>,\n ): Promise<T>;\n}\n\n/**\n * Creates a TenantAdapter for Drizzle (PostgreSQL).\n *\n * Uses a transaction per runWithTenant: BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n * The handle passed to fn is the transaction runner; use only this handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = true so RLS policies that allow\n * when current_setting('app.bypass_rls', true) = 'true' permit access (CLI generates this bypass policy).\n *\n * @param database - Drizzle pg database (from drizzle(pool) or similar)\n * @returns TenantAdapter implementation\n */\nexport function drizzleAdapter<TTx extends DrizzlePgTransaction>(\n database: DrizzlePgDatabase<TTx>,\n): TenantAdapter<TTx, TTx> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.current_tenant', ${tenantId}, true)`,\n );\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: TTx) => Promise<T>,\n ): Promise<T> {\n return database.transaction(async (tx) => {\n await tx.execute(\n sql`SELECT set_config('app.bypass_rls', 'true', true)`,\n );\n return fn(tx);\n });\n },\n };\n}\n","import { eq } from \"drizzle-orm\";\nimport type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport { tenantsTable } from \"./schema.js\";\n\n/** Table shape expected by createGetTenantRepository: id, name, slug, createdAt (or created_at). */\nexport type TenantsTableLike = typeof tenantsTable;\n\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.createdAt ?? row.created_at;\n if (createdAt === undefined || createdAt === null) {\n throw new Error(\"better-tenant: row missing created_at / createdAt\");\n }\n return {\n id: String(row.id),\n name: String(row.name),\n slug: String(row.slug),\n createdAt: createdAt as Date | string,\n ...row,\n };\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n * Pass the tenants table (e.g. tenantsTable from this package or your own with id, name, slug, created_at).\n * The returned function is called by core with the system database handle (from adapter.runAsSystem).\n */\nexport function createGetTenantRepository(\n table: TenantsTableLike = tenantsTable,\n): (database: unknown) => TenantRepository {\n return (database: unknown) => {\n const db = database as {\n insert: (t: TenantsTableLike) => {\n values: (v: { name: string; slug: string }) => {\n returning: () => Promise<unknown[]>;\n };\n };\n update: (t: TenantsTableLike) => {\n set: (v: Partial<{ name: string; slug: string }>) => {\n where: (c: unknown) => { returning: () => Promise<unknown[]> };\n };\n };\n select: () => {\n from: (t: TenantsTableLike) => {\n where: (c: unknown) => { limit: (n: number) => Promise<unknown[]> };\n limit: (n: number) => { offset: (n: number) => Promise<unknown[]> };\n };\n };\n delete: (t: TenantsTableLike) => {\n where: (c: unknown) => Promise<unknown>;\n };\n };\n\n return {\n async create(data) {\n const rows = await db\n .insert(table)\n .values({\n name: data.name,\n slug: data.slug,\n })\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: createTenant failed to return row\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async update(tenantId, data) {\n const set: Partial<{ name: string; slug: string }> = {};\n if (data.name !== undefined) set.name = data.name;\n if (data.slug !== undefined) set.slug = data.slug;\n if (Object.keys(set).length === 0) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n }\n const rows = await db\n .update(table)\n .set(set)\n .where(eq(table.id, tenantId))\n .returning();\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n throw new Error(\"better-tenant: tenant not found\");\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getById(tenantId: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.id, tenantId))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async getBySlug(slug: string) {\n const rows = await db\n .select()\n .from(table)\n .where(eq(table.slug, slug))\n .limit(1);\n const row = Array.isArray(rows) ? rows[0] : undefined;\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row as Record<string, unknown>);\n },\n\n async list(options = {}) {\n const limit = options.limit ?? 50;\n const offset = Math.max(0, options.offset ?? 0);\n const rows = await db.select().from(table).limit(limit).offset(offset);\n return (Array.isArray(rows) ? rows : []).map((r) =>\n rowToTenant(r as Record<string, unknown>),\n );\n },\n\n async delete(tenantId) {\n await db.delete(table).where(eq(table.id, tenantId));\n },\n };\n };\n}\n","import { sql } from \"drizzle-orm\";\nimport { pgTable, text, timestamp, uuid } from \"drizzle-orm/pg-core\";\n\n/**\n * Standard tenants table schema matching the CLI-generated shape.\n * Columns: id (UUID), name, slug, created_at.\n * Use this table or provide your own with the same column contract for createGetTenantRepository.\n */\nexport const tenantsTable = pgTable(\"tenants\", {\n id: uuid(\"id\").primaryKey().defaultRandom(),\n name: text(\"name\").notNull(),\n slug: text(\"slug\").notNull().unique(),\n createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n\n/**\n * Pre-configured tenant_id column for use in tenant-scoped tables.\n * References tenants.id and auto-populates from the current session variable.\n *\n * Usage:\n * ```ts\n * import { tenantId } from \"@usebetterdev/tenant/drizzle\";\n *\n * export const projectsTable = pgTable(\"projects\", {\n * id: serial(\"id\").primaryKey(),\n * name: text(\"name\").notNull(),\n * ...tenantId,\n * });\n * ```\n */\nexport const tenantId = {\n tenantId: uuid(\"tenant_id\")\n .notNull()\n .references(() => tenantsTable.id)\n .default(sql`(current_setting('app.current_tenant', true))::uuid`),\n} as const;\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { drizzleAdapter } from \"./adapter.js\";\nimport type { DrizzlePgDatabase, DrizzlePgTransaction } from \"./adapter.js\";\nimport { createGetTenantRepository, type TenantsTableLike } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Drizzle (PostgreSQL).\n *\n * Bundles drizzleAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: drizzleDatabase(db) })`.\n *\n * @param db - Drizzle pg database (from drizzle(pool) or similar)\n * @param options - Optional: custom tenants table\n */\nexport function drizzleDatabase<TTx extends DrizzlePgTransaction>(\n db: DrizzlePgDatabase<TTx>,\n options?: { table?: TenantsTableLike },\n): DatabaseProvider<TTx, TTx> {\n return {\n adapter: drizzleAdapter(db),\n getTenantRepository: createGetTenantRepository(options?.table),\n };\n}\n"],"mappings":"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","names":["tenantId","sql","tenantId","rows","row"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebetterdev/tenant-drizzle",
3
- "version": "0.2.0-beta.12",
3
+ "version": "0.2.0-beta.16",
4
4
  "repository": "github:usebetter-dev/usebetter",
5
5
  "bugs": "https://github.com/usebetter-dev/usebetter/issues",
6
6
  "homepage": "https://github.com/usebetter-dev/usebetter#readme",
@@ -25,14 +25,18 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "drizzle-orm": "^0.36.0",
28
- "@usebetterdev/tenant-core": "0.2.0-beta.12"
28
+ "@usebetterdev/tenant-core": "0.2.0-beta.16"
29
29
  },
30
30
  "peerDependencies": {
31
- "pg": ">=8.0.0"
31
+ "pg": ">=8.0.0",
32
+ "postgres": ">=3.0.0"
32
33
  },
33
34
  "peerDependenciesMeta": {
34
35
  "pg": {
35
- "optional": false
36
+ "optional": true
37
+ },
38
+ "postgres": {
39
+ "optional": true
36
40
  }
37
41
  },
38
42
  "devDependencies": {
@@ -43,7 +47,7 @@
43
47
  "tsup": "^8.3.5",
44
48
  "typescript": "~5.7.2",
45
49
  "vitest": "^2.1.6",
46
- "@usebetterdev/test-utils": "0.2.0-beta.12"
50
+ "@usebetterdev/test-utils": "0.2.0-beta.16"
47
51
  },
48
52
  "engines": {
49
53
  "node": ">=22"