@usebetterdev/tenant-prisma 0.2.1-beta.1 → 0.3.0-beta.3
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 +12 -5
- package/dist/index.cjs +11 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -5
- package/dist/index.d.ts +34 -5
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -5,17 +5,19 @@ Prisma adapter for [@usebetterdev/tenant](https://github.com/usebetter-dev/usebe
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pnpm add @usebetterdev/tenant @prisma/client
|
|
8
|
+
pnpm add @usebetterdev/tenant @prisma/client @prisma/adapter-pg
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import { PrismaClient } from "
|
|
14
|
+
import { PrismaClient } from "./generated/prisma/client.js";
|
|
15
|
+
import { PrismaPg } from "@prisma/adapter-pg";
|
|
15
16
|
import { betterTenant } from "@usebetterdev/tenant";
|
|
16
17
|
import { prismaDatabase } from "@usebetterdev/tenant/prisma";
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
|
20
|
+
const prisma = new PrismaClient({ adapter });
|
|
19
21
|
|
|
20
22
|
const tenant = betterTenant({
|
|
21
23
|
database: prismaDatabase(prisma),
|
|
@@ -52,6 +54,11 @@ Integration tests use [@testcontainers/postgresql](https://www.npmjs.com/package
|
|
|
52
54
|
|
|
53
55
|
**Run integration tests:** From the repo root, run `pnpm run test:integration` or `pnpm run test --filter @usebetterdev/tenant-prisma`.
|
|
54
56
|
|
|
55
|
-
## Peer
|
|
57
|
+
## Peer dependencies
|
|
56
58
|
|
|
57
|
-
Requires
|
|
59
|
+
Requires Prisma 7+:
|
|
60
|
+
|
|
61
|
+
- `@prisma/client` (>= 7.0.0)
|
|
62
|
+
- `@prisma/adapter-pg` (>= 7.0.0)
|
|
63
|
+
|
|
64
|
+
Prisma 7 uses the `prisma-client` generator with a required `output` path. Adjust the `PrismaClient` import to match your `output` configuration in `schema.prisma`.
|
package/dist/index.cjs
CHANGED
|
@@ -52,8 +52,7 @@ function rowToTenant(row) {
|
|
|
52
52
|
id: String(row.id),
|
|
53
53
|
name: String(row.name),
|
|
54
54
|
slug: String(row.slug),
|
|
55
|
-
createdAt
|
|
56
|
-
...row
|
|
55
|
+
createdAt
|
|
57
56
|
};
|
|
58
57
|
}
|
|
59
58
|
function assertSafeIdentifier(name) {
|
|
@@ -69,8 +68,10 @@ function createGetTenantRepository(tableName = "tenants") {
|
|
|
69
68
|
const tx = database;
|
|
70
69
|
return {
|
|
71
70
|
async create(data) {
|
|
71
|
+
const id = crypto.randomUUID();
|
|
72
72
|
const rows = await tx.$queryRawUnsafe(
|
|
73
|
-
`INSERT INTO ${tableName} (name, slug) VALUES ($1, $2) RETURNING *`,
|
|
73
|
+
`INSERT INTO ${tableName} (id, name, slug) VALUES ($1::uuid, $2, $3) RETURNING *`,
|
|
74
|
+
id,
|
|
74
75
|
data.name,
|
|
75
76
|
data.slug
|
|
76
77
|
);
|
|
@@ -151,6 +152,13 @@ function createGetTenantRepository(tableName = "tenants") {
|
|
|
151
152
|
`DELETE FROM ${tableName} WHERE id = $1::uuid`,
|
|
152
153
|
tenantId
|
|
153
154
|
);
|
|
155
|
+
},
|
|
156
|
+
async count() {
|
|
157
|
+
const rows = await tx.$queryRawUnsafe(
|
|
158
|
+
`SELECT count(*)::int AS count FROM ${tableName}`
|
|
159
|
+
);
|
|
160
|
+
const row = rows[0];
|
|
161
|
+
return Number(row?.count ?? 0);
|
|
154
162
|
}
|
|
155
163
|
};
|
|
156
164
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/repository.ts","../src/database.ts"],"sourcesContent":["export { prismaDatabase } from \"./database.js\";\nexport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\n","import type { TenantAdapter } from \"@usebetterdev/tenant-core\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Creates a TenantAdapter for Prisma (PostgreSQL).\n *\n * Uses an interactive transaction per runWithTenant:\n * BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n *\n * The handle passed to fn is the Prisma transaction client; use only this\n * handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = 'true'\n * so RLS policies that allow when current_setting('app.bypass_rls', true) = 'true'\n * permit access (CLI generates this bypass policy).\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @returns TenantAdapter implementation\n */\nexport function prismaAdapter(\n prisma: PrismaClientLike,\n): TenantAdapter<PrismaTransactionClient, PrismaTransactionClient> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: PrismaTransactionClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx: PrismaTransactionClient) => {\n await tx.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true)`;\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: PrismaTransactionClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx: PrismaTransactionClient) => {\n await tx.$executeRaw`SELECT set_config('app.bypass_rls', 'true', true)`;\n return fn(tx);\n });\n },\n };\n}\n","import type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport type { PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Converts a raw DB row to the Tenant shape expected by core.\n */\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.created_at ?? row.createdAt;\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 * Validate table name to prevent SQL injection. Only allows [a-zA-Z_][a-zA-Z0-9_]*.\n */\nfunction assertSafeIdentifier(name: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(\n `better-tenant: invalid table name \"${name}\" — must be [a-zA-Z_][a-zA-Z0-9_]*`,\n );\n }\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n *\n * Uses raw SQL ($queryRawUnsafe / $executeRawUnsafe) on the Prisma transaction\n * client so the adapter works without requiring a generated `Tenant` model in\n * the user's Prisma schema. The tenants table must match the CLI-generated shape\n * (id UUID, name TEXT, slug TEXT, created_at TIMESTAMPTZ).\n *\n * Table name is validated at creation time to prevent injection — only\n * alphanumeric + underscore identifiers are accepted.\n *\n * @param tableName - SQL table name (default: \"tenants\")\n */\nexport function createGetTenantRepository(\n tableName = \"tenants\",\n): (database: PrismaTransactionClient) => TenantRepository {\n assertSafeIdentifier(tableName);\n\n return (database: PrismaTransactionClient) => {\n const tx = database;\n\n return {\n async create(data) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `INSERT INTO ${tableName} (name, slug) VALUES ($1, $2) RETURNING *`,\n data.name,\n data.slug,\n );\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);\n },\n\n async getById(tenantId: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async getBySlug(slug: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE slug = $1 LIMIT 1`,\n slug,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async update(tenantId, data) {\n const setClauses: string[] = [];\n const values: unknown[] = [];\n\n if (data.name !== undefined) {\n values.push(data.name);\n setClauses.push(`name = $${String(values.length)}`);\n }\n if (data.slug !== undefined) {\n values.push(data.slug);\n setClauses.push(`slug = $${String(values.length)}`);\n }\n\n if (setClauses.length === 0) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\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);\n }\n\n values.push(tenantId);\n const query = `UPDATE ${tableName} SET ${setClauses.join(\", \")} WHERE id = $${String(values.length)}::uuid RETURNING *`;\n\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n query,\n ...values,\n );\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);\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 tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} ORDER BY created_at ASC LIMIT $1 OFFSET $2`,\n limit,\n offset,\n );\n return rows.map(rowToTenant);\n },\n\n async delete(tenantId) {\n await tx.$executeRawUnsafe(\n `DELETE FROM ${tableName} WHERE id = $1::uuid`,\n tenantId,\n );\n },\n };\n };\n}\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { prismaAdapter } from \"./adapter.js\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\nimport { createGetTenantRepository } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Prisma (PostgreSQL).\n *\n * Bundles prismaAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: prismaDatabase(prisma) })`.\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @param options - Optional: custom table name (default: \"tenants\")\n */\nexport function prismaDatabase(\n prisma: PrismaClientLike,\n options?: { tableName?: string },\n): DatabaseProvider<PrismaTransactionClient, PrismaTransactionClient> {\n return {\n adapter: prismaAdapter(prisma),\n getTenantRepository: createGetTenantRepository(options?.tableName),\n };\n}\n"],"mappings":"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","names":["rows","row"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/repository.ts","../src/database.ts"],"sourcesContent":["export { prismaDatabase } from \"./database.js\";\nexport type {\n PrismaClientLike,\n PrismaTransactionClient,\n WithOptionalTenant,\n} from \"./types.js\";\n","import type { TenantAdapter } from \"@usebetterdev/tenant-core\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Creates a TenantAdapter for Prisma (PostgreSQL).\n *\n * Uses an interactive transaction per runWithTenant:\n * BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n *\n * The handle passed to fn is the Prisma transaction client; use only this\n * handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = 'true'\n * so RLS policies that allow when current_setting('app.bypass_rls', true) = 'true'\n * permit access (CLI generates this bypass policy).\n *\n * Generic `TxClient` flows from `PrismaClientLike<TxClient>` so\n * `getDatabase()` returns the user's full transaction client type.\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @returns TenantAdapter implementation\n */\nexport function prismaAdapter<\n TxClient extends PrismaTransactionClient = PrismaTransactionClient,\n>(\n prisma: PrismaClientLike<TxClient>,\n): TenantAdapter<TxClient, TxClient> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: TxClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx) => {\n await tx.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true)`;\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: TxClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx) => {\n await tx.$executeRaw`SELECT set_config('app.bypass_rls', 'true', true)`;\n return fn(tx);\n });\n },\n };\n}\n","import type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport type { PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Converts a raw DB row to the Tenant shape expected by core.\n */\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.created_at ?? row.createdAt;\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 };\n}\n\n/**\n * Validate table name to prevent SQL injection. Only allows [a-zA-Z_][a-zA-Z0-9_]*.\n */\nfunction assertSafeIdentifier(name: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(\n `better-tenant: invalid table name \"${name}\" — must be [a-zA-Z_][a-zA-Z0-9_]*`,\n );\n }\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n *\n * Uses raw SQL ($queryRawUnsafe / $executeRawUnsafe) on the Prisma transaction\n * client so the adapter works without requiring a generated `Tenant` model in\n * the user's Prisma schema. The tenants table must match the CLI-generated shape\n * (id UUID, name TEXT, slug TEXT, created_at TIMESTAMPTZ).\n *\n * Table name is validated at creation time to prevent injection — only\n * alphanumeric + underscore identifiers are accepted.\n *\n * @param tableName - SQL table name (default: \"tenants\")\n */\nexport function createGetTenantRepository(\n tableName = \"tenants\",\n): (database: PrismaTransactionClient) => TenantRepository {\n assertSafeIdentifier(tableName);\n\n return (database: PrismaTransactionClient) => {\n const tx = database;\n\n return {\n async create(data) {\n const id = crypto.randomUUID();\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `INSERT INTO ${tableName} (id, name, slug) VALUES ($1::uuid, $2, $3) RETURNING *`,\n id,\n data.name,\n data.slug,\n );\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);\n },\n\n async getById(tenantId: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async getBySlug(slug: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE slug = $1 LIMIT 1`,\n slug,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async update(tenantId, data) {\n const setClauses: string[] = [];\n const values: unknown[] = [];\n\n if (data.name !== undefined) {\n values.push(data.name);\n setClauses.push(`name = $${String(values.length)}`);\n }\n if (data.slug !== undefined) {\n values.push(data.slug);\n setClauses.push(`slug = $${String(values.length)}`);\n }\n\n if (setClauses.length === 0) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\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);\n }\n\n values.push(tenantId);\n const query = `UPDATE ${tableName} SET ${setClauses.join(\", \")} WHERE id = $${String(values.length)}::uuid RETURNING *`;\n\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n query,\n ...values,\n );\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);\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 tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} ORDER BY created_at ASC LIMIT $1 OFFSET $2`,\n limit,\n offset,\n );\n return rows.map(rowToTenant);\n },\n\n async delete(tenantId) {\n await tx.$executeRawUnsafe(\n `DELETE FROM ${tableName} WHERE id = $1::uuid`,\n tenantId,\n );\n },\n\n async count() {\n const rows = await tx.$queryRawUnsafe<{ count: bigint | number | string }[]>(\n `SELECT count(*)::int AS count FROM ${tableName}`,\n );\n const row = rows[0];\n return Number(row?.count ?? 0);\n },\n };\n };\n}\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { prismaAdapter } from \"./adapter.js\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\nimport { createGetTenantRepository } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Prisma (PostgreSQL).\n *\n * Bundles prismaAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: prismaDatabase(prisma) })`.\n *\n * Generic `TxClient` is inferred from the PrismaClient's `$transaction` callback,\n * so `getDatabase()` returns the user's full transaction client type with model\n * methods (e.g. `db.project.findMany()`) instead of just raw query methods.\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @param options - Optional: custom table name (default: \"tenants\")\n */\nexport function prismaDatabase<\n TxClient extends PrismaTransactionClient = PrismaTransactionClient,\n>(\n prisma: PrismaClientLike<TxClient>,\n options?: { tableName?: string },\n): DatabaseProvider<TxClient, TxClient> {\n return {\n adapter: prismaAdapter(prisma),\n getTenantRepository: createGetTenantRepository(options?.tableName),\n };\n}\n"],"mappings":"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","names":["rows","row"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -16,13 +16,38 @@ type PrismaTransactionClient = {
|
|
|
16
16
|
* Minimal PrismaClient shape: must support interactive `$transaction`.
|
|
17
17
|
*
|
|
18
18
|
* Matches `new PrismaClient()` — no generated model types required at build time.
|
|
19
|
+
*
|
|
20
|
+
* Generic `TxClient` allows the transaction client type to flow through
|
|
21
|
+
* so `getDatabase()` returns the user's full Prisma transaction client
|
|
22
|
+
* (with model methods like `db.project.findMany()`) instead of `unknown`.
|
|
19
23
|
*/
|
|
20
|
-
type PrismaClientLike = PrismaTransactionClient & {
|
|
21
|
-
$transaction<T>(fn: (tx:
|
|
24
|
+
type PrismaClientLike<TxClient extends PrismaTransactionClient = PrismaTransactionClient> = PrismaTransactionClient & {
|
|
25
|
+
$transaction<T>(fn: (tx: TxClient) => Promise<T>, options?: {
|
|
22
26
|
maxWait?: number;
|
|
23
27
|
timeout?: number;
|
|
28
|
+
isolationLevel?: string;
|
|
24
29
|
}): Promise<T>;
|
|
25
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Makes `tenantId` optional in a Prisma create/update input type.
|
|
33
|
+
*
|
|
34
|
+
* When a model has a `@relation` to a Tenant model, Prisma generates `tenantId`
|
|
35
|
+
* as required in `UncheckedCreateInput` regardless of `@default(dbgenerated(...))`.
|
|
36
|
+
* Wrap your data type with this utility to safely omit `tenantId` — the
|
|
37
|
+
* `set_tenant_id()` trigger fills it from the session variable at INSERT time.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import type { WithOptionalTenant } from "@usebetterdev/tenant/prisma";
|
|
42
|
+
* import type { ProjectUncheckedCreateInput } from "./generated/prisma/models/Project.js";
|
|
43
|
+
*
|
|
44
|
+
* const data: WithOptionalTenant<ProjectUncheckedCreateInput> = { name: "Alpha" };
|
|
45
|
+
* await db.project.create({ data: data as ProjectUncheckedCreateInput });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
type WithOptionalTenant<T> = Omit<T, "tenantId" | "tenant"> & {
|
|
49
|
+
tenantId?: string;
|
|
50
|
+
};
|
|
26
51
|
|
|
27
52
|
/**
|
|
28
53
|
* Creates a DatabaseProvider for Prisma (PostgreSQL).
|
|
@@ -30,11 +55,15 @@ type PrismaClientLike = PrismaTransactionClient & {
|
|
|
30
55
|
* Bundles prismaAdapter + createGetTenantRepository into a single config value
|
|
31
56
|
* for use with `betterTenant({ database: prismaDatabase(prisma) })`.
|
|
32
57
|
*
|
|
58
|
+
* Generic `TxClient` is inferred from the PrismaClient's `$transaction` callback,
|
|
59
|
+
* so `getDatabase()` returns the user's full transaction client type with model
|
|
60
|
+
* methods (e.g. `db.project.findMany()`) instead of just raw query methods.
|
|
61
|
+
*
|
|
33
62
|
* @param prisma - PrismaClient instance (or anything matching PrismaClientLike)
|
|
34
63
|
* @param options - Optional: custom table name (default: "tenants")
|
|
35
64
|
*/
|
|
36
|
-
declare function prismaDatabase(prisma: PrismaClientLike
|
|
65
|
+
declare function prismaDatabase<TxClient extends PrismaTransactionClient = PrismaTransactionClient>(prisma: PrismaClientLike<TxClient>, options?: {
|
|
37
66
|
tableName?: string;
|
|
38
|
-
}): DatabaseProvider<
|
|
67
|
+
}): DatabaseProvider<TxClient, TxClient>;
|
|
39
68
|
|
|
40
|
-
export { type PrismaClientLike, type PrismaTransactionClient, prismaDatabase };
|
|
69
|
+
export { type PrismaClientLike, type PrismaTransactionClient, type WithOptionalTenant, prismaDatabase };
|
package/dist/index.d.ts
CHANGED
|
@@ -16,13 +16,38 @@ type PrismaTransactionClient = {
|
|
|
16
16
|
* Minimal PrismaClient shape: must support interactive `$transaction`.
|
|
17
17
|
*
|
|
18
18
|
* Matches `new PrismaClient()` — no generated model types required at build time.
|
|
19
|
+
*
|
|
20
|
+
* Generic `TxClient` allows the transaction client type to flow through
|
|
21
|
+
* so `getDatabase()` returns the user's full Prisma transaction client
|
|
22
|
+
* (with model methods like `db.project.findMany()`) instead of `unknown`.
|
|
19
23
|
*/
|
|
20
|
-
type PrismaClientLike = PrismaTransactionClient & {
|
|
21
|
-
$transaction<T>(fn: (tx:
|
|
24
|
+
type PrismaClientLike<TxClient extends PrismaTransactionClient = PrismaTransactionClient> = PrismaTransactionClient & {
|
|
25
|
+
$transaction<T>(fn: (tx: TxClient) => Promise<T>, options?: {
|
|
22
26
|
maxWait?: number;
|
|
23
27
|
timeout?: number;
|
|
28
|
+
isolationLevel?: string;
|
|
24
29
|
}): Promise<T>;
|
|
25
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Makes `tenantId` optional in a Prisma create/update input type.
|
|
33
|
+
*
|
|
34
|
+
* When a model has a `@relation` to a Tenant model, Prisma generates `tenantId`
|
|
35
|
+
* as required in `UncheckedCreateInput` regardless of `@default(dbgenerated(...))`.
|
|
36
|
+
* Wrap your data type with this utility to safely omit `tenantId` — the
|
|
37
|
+
* `set_tenant_id()` trigger fills it from the session variable at INSERT time.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import type { WithOptionalTenant } from "@usebetterdev/tenant/prisma";
|
|
42
|
+
* import type { ProjectUncheckedCreateInput } from "./generated/prisma/models/Project.js";
|
|
43
|
+
*
|
|
44
|
+
* const data: WithOptionalTenant<ProjectUncheckedCreateInput> = { name: "Alpha" };
|
|
45
|
+
* await db.project.create({ data: data as ProjectUncheckedCreateInput });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
type WithOptionalTenant<T> = Omit<T, "tenantId" | "tenant"> & {
|
|
49
|
+
tenantId?: string;
|
|
50
|
+
};
|
|
26
51
|
|
|
27
52
|
/**
|
|
28
53
|
* Creates a DatabaseProvider for Prisma (PostgreSQL).
|
|
@@ -30,11 +55,15 @@ type PrismaClientLike = PrismaTransactionClient & {
|
|
|
30
55
|
* Bundles prismaAdapter + createGetTenantRepository into a single config value
|
|
31
56
|
* for use with `betterTenant({ database: prismaDatabase(prisma) })`.
|
|
32
57
|
*
|
|
58
|
+
* Generic `TxClient` is inferred from the PrismaClient's `$transaction` callback,
|
|
59
|
+
* so `getDatabase()` returns the user's full transaction client type with model
|
|
60
|
+
* methods (e.g. `db.project.findMany()`) instead of just raw query methods.
|
|
61
|
+
*
|
|
33
62
|
* @param prisma - PrismaClient instance (or anything matching PrismaClientLike)
|
|
34
63
|
* @param options - Optional: custom table name (default: "tenants")
|
|
35
64
|
*/
|
|
36
|
-
declare function prismaDatabase(prisma: PrismaClientLike
|
|
65
|
+
declare function prismaDatabase<TxClient extends PrismaTransactionClient = PrismaTransactionClient>(prisma: PrismaClientLike<TxClient>, options?: {
|
|
37
66
|
tableName?: string;
|
|
38
|
-
}): DatabaseProvider<
|
|
67
|
+
}): DatabaseProvider<TxClient, TxClient>;
|
|
39
68
|
|
|
40
|
-
export { type PrismaClientLike, type PrismaTransactionClient, prismaDatabase };
|
|
69
|
+
export { type PrismaClientLike, type PrismaTransactionClient, type WithOptionalTenant, prismaDatabase };
|
package/dist/index.js
CHANGED
|
@@ -26,8 +26,7 @@ function rowToTenant(row) {
|
|
|
26
26
|
id: String(row.id),
|
|
27
27
|
name: String(row.name),
|
|
28
28
|
slug: String(row.slug),
|
|
29
|
-
createdAt
|
|
30
|
-
...row
|
|
29
|
+
createdAt
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
32
|
function assertSafeIdentifier(name) {
|
|
@@ -43,8 +42,10 @@ function createGetTenantRepository(tableName = "tenants") {
|
|
|
43
42
|
const tx = database;
|
|
44
43
|
return {
|
|
45
44
|
async create(data) {
|
|
45
|
+
const id = crypto.randomUUID();
|
|
46
46
|
const rows = await tx.$queryRawUnsafe(
|
|
47
|
-
`INSERT INTO ${tableName} (name, slug) VALUES ($1, $2) RETURNING *`,
|
|
47
|
+
`INSERT INTO ${tableName} (id, name, slug) VALUES ($1::uuid, $2, $3) RETURNING *`,
|
|
48
|
+
id,
|
|
48
49
|
data.name,
|
|
49
50
|
data.slug
|
|
50
51
|
);
|
|
@@ -125,6 +126,13 @@ function createGetTenantRepository(tableName = "tenants") {
|
|
|
125
126
|
`DELETE FROM ${tableName} WHERE id = $1::uuid`,
|
|
126
127
|
tenantId
|
|
127
128
|
);
|
|
129
|
+
},
|
|
130
|
+
async count() {
|
|
131
|
+
const rows = await tx.$queryRawUnsafe(
|
|
132
|
+
`SELECT count(*)::int AS count FROM ${tableName}`
|
|
133
|
+
);
|
|
134
|
+
const row = rows[0];
|
|
135
|
+
return Number(row?.count ?? 0);
|
|
128
136
|
}
|
|
129
137
|
};
|
|
130
138
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter.ts","../src/repository.ts","../src/database.ts"],"sourcesContent":["import type { TenantAdapter } from \"@usebetterdev/tenant-core\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Creates a TenantAdapter for Prisma (PostgreSQL).\n *\n * Uses an interactive transaction per runWithTenant:\n * BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n *\n * The handle passed to fn is the Prisma transaction client; use only this\n * handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = 'true'\n * so RLS policies that allow when current_setting('app.bypass_rls', true) = 'true'\n * permit access (CLI generates this bypass policy).\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @returns TenantAdapter implementation\n */\nexport function prismaAdapter(\n prisma: PrismaClientLike,\n): TenantAdapter<PrismaTransactionClient, PrismaTransactionClient> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: PrismaTransactionClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx: PrismaTransactionClient) => {\n await tx.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true)`;\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: PrismaTransactionClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx: PrismaTransactionClient) => {\n await tx.$executeRaw`SELECT set_config('app.bypass_rls', 'true', true)`;\n return fn(tx);\n });\n },\n };\n}\n","import type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport type { PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Converts a raw DB row to the Tenant shape expected by core.\n */\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.created_at ?? row.createdAt;\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 * Validate table name to prevent SQL injection. Only allows [a-zA-Z_][a-zA-Z0-9_]*.\n */\nfunction assertSafeIdentifier(name: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(\n `better-tenant: invalid table name \"${name}\" — must be [a-zA-Z_][a-zA-Z0-9_]*`,\n );\n }\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n *\n * Uses raw SQL ($queryRawUnsafe / $executeRawUnsafe) on the Prisma transaction\n * client so the adapter works without requiring a generated `Tenant` model in\n * the user's Prisma schema. The tenants table must match the CLI-generated shape\n * (id UUID, name TEXT, slug TEXT, created_at TIMESTAMPTZ).\n *\n * Table name is validated at creation time to prevent injection — only\n * alphanumeric + underscore identifiers are accepted.\n *\n * @param tableName - SQL table name (default: \"tenants\")\n */\nexport function createGetTenantRepository(\n tableName = \"tenants\",\n): (database: PrismaTransactionClient) => TenantRepository {\n assertSafeIdentifier(tableName);\n\n return (database: PrismaTransactionClient) => {\n const tx = database;\n\n return {\n async create(data) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `INSERT INTO ${tableName} (name, slug) VALUES ($1, $2) RETURNING *`,\n data.name,\n data.slug,\n );\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);\n },\n\n async getById(tenantId: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async getBySlug(slug: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE slug = $1 LIMIT 1`,\n slug,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async update(tenantId, data) {\n const setClauses: string[] = [];\n const values: unknown[] = [];\n\n if (data.name !== undefined) {\n values.push(data.name);\n setClauses.push(`name = $${String(values.length)}`);\n }\n if (data.slug !== undefined) {\n values.push(data.slug);\n setClauses.push(`slug = $${String(values.length)}`);\n }\n\n if (setClauses.length === 0) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\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);\n }\n\n values.push(tenantId);\n const query = `UPDATE ${tableName} SET ${setClauses.join(\", \")} WHERE id = $${String(values.length)}::uuid RETURNING *`;\n\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n query,\n ...values,\n );\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);\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 tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} ORDER BY created_at ASC LIMIT $1 OFFSET $2`,\n limit,\n offset,\n );\n return rows.map(rowToTenant);\n },\n\n async delete(tenantId) {\n await tx.$executeRawUnsafe(\n `DELETE FROM ${tableName} WHERE id = $1::uuid`,\n tenantId,\n );\n },\n };\n };\n}\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { prismaAdapter } from \"./adapter.js\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\nimport { createGetTenantRepository } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Prisma (PostgreSQL).\n *\n * Bundles prismaAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: prismaDatabase(prisma) })`.\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @param options - Optional: custom table name (default: \"tenants\")\n */\nexport function prismaDatabase(\n prisma: PrismaClientLike,\n options?: { tableName?: string },\n): DatabaseProvider<PrismaTransactionClient, PrismaTransactionClient> {\n return {\n adapter: prismaAdapter(prisma),\n getTenantRepository: createGetTenantRepository(options?.tableName),\n };\n}\n"],"mappings":"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","names":["rows","row"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts","../src/repository.ts","../src/database.ts"],"sourcesContent":["import type { TenantAdapter } from \"@usebetterdev/tenant-core\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Creates a TenantAdapter for Prisma (PostgreSQL).\n *\n * Uses an interactive transaction per runWithTenant:\n * BEGIN → SET LOCAL app.current_tenant = tenantId → fn(tx) → COMMIT.\n *\n * The handle passed to fn is the Prisma transaction client; use only this\n * handle for tenant-scoped queries so RLS applies.\n *\n * runAsSystem runs in a transaction with SET LOCAL app.bypass_rls = 'true'\n * so RLS policies that allow when current_setting('app.bypass_rls', true) = 'true'\n * permit access (CLI generates this bypass policy).\n *\n * Generic `TxClient` flows from `PrismaClientLike<TxClient>` so\n * `getDatabase()` returns the user's full transaction client type.\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @returns TenantAdapter implementation\n */\nexport function prismaAdapter<\n TxClient extends PrismaTransactionClient = PrismaTransactionClient,\n>(\n prisma: PrismaClientLike<TxClient>,\n): TenantAdapter<TxClient, TxClient> {\n return {\n async runWithTenant<T>(\n tenantId: string,\n fn: (database: TxClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx) => {\n await tx.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true)`;\n return fn(tx);\n });\n },\n\n async runAsSystem<T>(\n fn: (database: TxClient) => Promise<T>,\n ): Promise<T> {\n return prisma.$transaction(async (tx) => {\n await tx.$executeRaw`SELECT set_config('app.bypass_rls', 'true', true)`;\n return fn(tx);\n });\n },\n };\n}\n","import type { Tenant, TenantRepository } from \"@usebetterdev/tenant-core\";\nimport type { PrismaTransactionClient } from \"./types.js\";\n\n/**\n * Converts a raw DB row to the Tenant shape expected by core.\n */\nfunction rowToTenant(row: Record<string, unknown>): Tenant {\n const createdAt = row.created_at ?? row.createdAt;\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 };\n}\n\n/**\n * Validate table name to prevent SQL injection. Only allows [a-zA-Z_][a-zA-Z0-9_]*.\n */\nfunction assertSafeIdentifier(name: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(\n `better-tenant: invalid table name \"${name}\" — must be [a-zA-Z_][a-zA-Z0-9_]*`,\n );\n }\n}\n\n/**\n * Creates getTenantRepository for use with betterTenant({ getTenantRepository }).\n *\n * Uses raw SQL ($queryRawUnsafe / $executeRawUnsafe) on the Prisma transaction\n * client so the adapter works without requiring a generated `Tenant` model in\n * the user's Prisma schema. The tenants table must match the CLI-generated shape\n * (id UUID, name TEXT, slug TEXT, created_at TIMESTAMPTZ).\n *\n * Table name is validated at creation time to prevent injection — only\n * alphanumeric + underscore identifiers are accepted.\n *\n * @param tableName - SQL table name (default: \"tenants\")\n */\nexport function createGetTenantRepository(\n tableName = \"tenants\",\n): (database: PrismaTransactionClient) => TenantRepository {\n assertSafeIdentifier(tableName);\n\n return (database: PrismaTransactionClient) => {\n const tx = database;\n\n return {\n async create(data) {\n const id = crypto.randomUUID();\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `INSERT INTO ${tableName} (id, name, slug) VALUES ($1::uuid, $2, $3) RETURNING *`,\n id,\n data.name,\n data.slug,\n );\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);\n },\n\n async getById(tenantId: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async getBySlug(slug: string) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE slug = $1 LIMIT 1`,\n slug,\n );\n const row = rows[0];\n if (!row || typeof row !== \"object\") {\n return null;\n }\n return rowToTenant(row);\n },\n\n async update(tenantId, data) {\n const setClauses: string[] = [];\n const values: unknown[] = [];\n\n if (data.name !== undefined) {\n values.push(data.name);\n setClauses.push(`name = $${String(values.length)}`);\n }\n if (data.slug !== undefined) {\n values.push(data.slug);\n setClauses.push(`slug = $${String(values.length)}`);\n }\n\n if (setClauses.length === 0) {\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} WHERE id = $1::uuid LIMIT 1`,\n tenantId,\n );\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);\n }\n\n values.push(tenantId);\n const query = `UPDATE ${tableName} SET ${setClauses.join(\", \")} WHERE id = $${String(values.length)}::uuid RETURNING *`;\n\n const rows = await tx.$queryRawUnsafe<Record<string, unknown>[]>(\n query,\n ...values,\n );\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);\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 tx.$queryRawUnsafe<Record<string, unknown>[]>(\n `SELECT * FROM ${tableName} ORDER BY created_at ASC LIMIT $1 OFFSET $2`,\n limit,\n offset,\n );\n return rows.map(rowToTenant);\n },\n\n async delete(tenantId) {\n await tx.$executeRawUnsafe(\n `DELETE FROM ${tableName} WHERE id = $1::uuid`,\n tenantId,\n );\n },\n\n async count() {\n const rows = await tx.$queryRawUnsafe<{ count: bigint | number | string }[]>(\n `SELECT count(*)::int AS count FROM ${tableName}`,\n );\n const row = rows[0];\n return Number(row?.count ?? 0);\n },\n };\n };\n}\n","import type { DatabaseProvider } from \"@usebetterdev/tenant-core\";\nimport { prismaAdapter } from \"./adapter.js\";\nimport type { PrismaClientLike, PrismaTransactionClient } from \"./types.js\";\nimport { createGetTenantRepository } from \"./repository.js\";\n\n/**\n * Creates a DatabaseProvider for Prisma (PostgreSQL).\n *\n * Bundles prismaAdapter + createGetTenantRepository into a single config value\n * for use with `betterTenant({ database: prismaDatabase(prisma) })`.\n *\n * Generic `TxClient` is inferred from the PrismaClient's `$transaction` callback,\n * so `getDatabase()` returns the user's full transaction client type with model\n * methods (e.g. `db.project.findMany()`) instead of just raw query methods.\n *\n * @param prisma - PrismaClient instance (or anything matching PrismaClientLike)\n * @param options - Optional: custom table name (default: \"tenants\")\n */\nexport function prismaDatabase<\n TxClient extends PrismaTransactionClient = PrismaTransactionClient,\n>(\n prisma: PrismaClientLike<TxClient>,\n options?: { tableName?: string },\n): DatabaseProvider<TxClient, TxClient> {\n return {\n adapter: prismaAdapter(prisma),\n getTenantRepository: createGetTenantRepository(options?.tableName),\n };\n}\n"],"mappings":"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","names":["rows","row"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebetterdev/tenant-prisma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-beta.3",
|
|
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",
|
|
@@ -24,14 +24,18 @@
|
|
|
24
24
|
"README.md"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@usebetterdev/tenant-core": "0.
|
|
27
|
+
"@usebetterdev/tenant-core": "0.3.0-beta.3"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@prisma/client": ">=
|
|
30
|
+
"@prisma/client": ">=7.0.0",
|
|
31
|
+
"@prisma/adapter-pg": ">=7.0.0"
|
|
31
32
|
},
|
|
32
33
|
"peerDependenciesMeta": {
|
|
33
34
|
"@prisma/client": {
|
|
34
35
|
"optional": false
|
|
36
|
+
},
|
|
37
|
+
"@prisma/adapter-pg": {
|
|
38
|
+
"optional": false
|
|
35
39
|
}
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
@@ -42,7 +46,7 @@
|
|
|
42
46
|
"tsup": "^8.3.5",
|
|
43
47
|
"typescript": "~5.7.2",
|
|
44
48
|
"vitest": "^2.1.6",
|
|
45
|
-
"@usebetterdev/test-utils": "0.
|
|
49
|
+
"@usebetterdev/test-utils": "0.3.0-beta.3"
|
|
46
50
|
},
|
|
47
51
|
"engines": {
|
|
48
52
|
"node": ">=22"
|