@prisma-next/extension-supabase 0.13.0-dev.2 → 0.13.0-dev.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pack.mjs +11 -13
- package/dist/pack.mjs.map +1 -1
- package/dist/package-CRj0p75l.mjs +6 -0
- package/dist/package-CRj0p75l.mjs.map +1 -0
- package/dist/runtime.d.mts +110 -3
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +278 -3
- package/dist/runtime.mjs.map +1 -1
- package/dist/test/utils.d.mts +7 -6
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +19 -6
- package/dist/test/utils.mjs.map +1 -1
- package/package.json +28 -25
- package/src/contract/contract.d.ts +22 -22
- package/src/contract/contract.json +10 -10
- package/src/exports/runtime.ts +17 -29
- package/src/runtime/descriptor.ts +26 -0
- package/src/runtime/supabase-runtime.ts +171 -0
- package/src/runtime/supabase.ts +323 -0
package/dist/test/utils.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
//#region test/supabase-bootstrap.ts
|
|
2
2
|
/**
|
|
3
|
-
* Seeds the database with the external Supabase schemas and
|
|
4
|
-
* caller passes an already-connected `pg.Client` — this function does not
|
|
5
|
-
* open or close connections
|
|
6
|
-
* test's other setup steps.
|
|
3
|
+
* Seeds the database with the external Supabase schemas, tables, roles, and grants.
|
|
4
|
+
* The caller passes an already-connected `pg.Client` — this function does not
|
|
5
|
+
* open or close connections.
|
|
7
6
|
*
|
|
8
7
|
* Creates two schemas (`auth`, `storage`) and four tables whose columns
|
|
9
8
|
* exactly match the `@prisma-next/extension-supabase` contract:
|
|
@@ -13,8 +12,10 @@
|
|
|
13
12
|
* - `storage.buckets` — id text PK, name text, created_at timestamptz, updated_at timestamptz
|
|
14
13
|
* - `storage.objects` — id uuid PK, bucket_id text, name text, created_at timestamptz, updated_at timestamptz
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
* the `
|
|
15
|
+
* Creates the three Postgres roles and grants that mirror a real Supabase database.
|
|
16
|
+
* `ALTER DEFAULT PRIVILEGES` covers tables created after the shim runs (e.g. via `dbInit`).
|
|
17
|
+
* WAL grants are guarded by a schema-existence check — a PGlite single-connection
|
|
18
|
+
* accommodation so role-bound sessions can interleave with the WAL drain query.
|
|
18
19
|
*/
|
|
19
20
|
async function bootstrapSupabaseShim(client) {
|
|
20
21
|
await client.query("CREATE SCHEMA IF NOT EXISTS auth");
|
|
@@ -57,6 +58,18 @@ async function bootstrapSupabaseShim(client) {
|
|
|
57
58
|
PRIMARY KEY (id)
|
|
58
59
|
)
|
|
59
60
|
`);
|
|
61
|
+
await client.query("CREATE ROLE anon NOLOGIN");
|
|
62
|
+
await client.query("CREATE ROLE authenticated NOLOGIN");
|
|
63
|
+
await client.query("CREATE ROLE service_role NOLOGIN BYPASSRLS");
|
|
64
|
+
await client.query("GRANT USAGE ON SCHEMA public TO anon, authenticated, service_role");
|
|
65
|
+
await client.query("GRANT USAGE ON SCHEMA auth, storage TO anon, authenticated, service_role");
|
|
66
|
+
await client.query("GRANT ALL ON ALL TABLES IN SCHEMA auth TO service_role");
|
|
67
|
+
await client.query("GRANT ALL ON ALL TABLES IN SCHEMA storage TO service_role");
|
|
68
|
+
await client.query("GRANT SELECT ON ALL TABLES IN SCHEMA auth TO anon, authenticated");
|
|
69
|
+
await client.query("GRANT SELECT ON ALL TABLES IN SCHEMA storage TO anon, authenticated");
|
|
70
|
+
await client.query("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO service_role");
|
|
71
|
+
await client.query("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, UPDATE ON TABLES TO authenticated");
|
|
72
|
+
await client.query("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO anon");
|
|
60
73
|
}
|
|
61
74
|
//#endregion
|
|
62
75
|
export { bootstrapSupabaseShim };
|
package/dist/test/utils.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":[],"sources":["../../test/supabase-bootstrap.ts"],"sourcesContent":["/**\n * Shared Supabase test fixture — seeds the external schemas and
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../../test/supabase-bootstrap.ts"],"sourcesContent":["/**\n * Shared Supabase test fixture — seeds the external schemas, tables, roles, and grants.\n *\n * Seeds a Postgres/PGlite database with the external Supabase schemas and\n * tables that the framework verifier expects when a composed contract declares\n * `auth.*` and `storage.*` tables as `external`. Without these tables present,\n * `db init`/`db update` will fail at the verify step because the framework\n * confirms declared `external` tables exist.\n *\n * Also creates the three Postgres roles (`anon`, `authenticated`, `service_role`)\n * with grants that mirror a real Supabase database. `ALTER DEFAULT PRIVILEGES`\n * ensures tables created after the shim runs (e.g. `public.profile` via `dbInit`)\n * are automatically accessible to the roles.\n *\n * The caller owns the client lifecycle — pass any already-connected `pg.Client`\n * (e.g. one the test is sharing across setup steps, or one bound to a\n * transaction for isolation). Convenience wrapper for tests that don't already\n * have one:\n *\n * @example\n * ```ts\n * import { withClient } from '@prisma-next/test-utils';\n * import { bootstrapSupabaseShim } from '@prisma-next/extension-supabase/test/utils';\n *\n * await withClient(connectionString, async (client) => {\n * await bootstrapSupabaseShim(client);\n * });\n * ```\n */\nimport type { Client } from 'pg';\n\n/**\n * Seeds the database with the external Supabase schemas, tables, roles, and grants.\n * The caller passes an already-connected `pg.Client` — this function does not\n * open or close connections.\n *\n * Creates two schemas (`auth`, `storage`) and four tables whose columns\n * exactly match the `@prisma-next/extension-supabase` contract:\n *\n * - `auth.users` — id uuid PK, email text, created_at timestamptz, updated_at timestamptz\n * - `auth.identities` — id uuid PK, user_id uuid, provider text, created_at timestamptz, updated_at timestamptz\n * - `storage.buckets` — id text PK, name text, created_at timestamptz, updated_at timestamptz\n * - `storage.objects` — id uuid PK, bucket_id text, name text, created_at timestamptz, updated_at timestamptz\n *\n * Creates the three Postgres roles and grants that mirror a real Supabase database.\n * `ALTER DEFAULT PRIVILEGES` covers tables created after the shim runs (e.g. via `dbInit`).\n * WAL grants are guarded by a schema-existence check — a PGlite single-connection\n * accommodation so role-bound sessions can interleave with the WAL drain query.\n */\nexport async function bootstrapSupabaseShim(client: Client): Promise<void> {\n await client.query('CREATE SCHEMA IF NOT EXISTS auth');\n await client.query('CREATE SCHEMA IF NOT EXISTS storage');\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS auth.users (\n id uuid NOT NULL,\n email text NOT NULL,\n created_at timestamptz NOT NULL,\n updated_at timestamptz NOT NULL,\n PRIMARY KEY (id)\n )\n `);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS auth.identities (\n id uuid NOT NULL,\n user_id uuid NOT NULL,\n provider text NOT NULL,\n created_at timestamptz NOT NULL,\n updated_at timestamptz NOT NULL,\n PRIMARY KEY (id)\n )\n `);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS storage.buckets (\n id text NOT NULL,\n name text NOT NULL,\n created_at timestamptz NOT NULL,\n updated_at timestamptz NOT NULL,\n PRIMARY KEY (id)\n )\n `);\n\n await client.query(`\n CREATE TABLE IF NOT EXISTS storage.objects (\n id uuid NOT NULL,\n bucket_id text NOT NULL,\n name text NOT NULL,\n created_at timestamptz NOT NULL,\n updated_at timestamptz NOT NULL,\n PRIMARY KEY (id)\n )\n `);\n\n // Roles + grants mirror a real Supabase database; WAL grants are a\n // PGlite-single-connection test accommodation.\n await client.query('CREATE ROLE anon NOLOGIN');\n await client.query('CREATE ROLE authenticated NOLOGIN');\n await client.query('CREATE ROLE service_role NOLOGIN BYPASSRLS');\n await client.query('GRANT USAGE ON SCHEMA public TO anon, authenticated, service_role');\n await client.query('GRANT USAGE ON SCHEMA auth, storage TO anon, authenticated, service_role');\n await client.query('GRANT ALL ON ALL TABLES IN SCHEMA auth TO service_role');\n await client.query('GRANT ALL ON ALL TABLES IN SCHEMA storage TO service_role');\n await client.query('GRANT SELECT ON ALL TABLES IN SCHEMA auth TO anon, authenticated');\n await client.query('GRANT SELECT ON ALL TABLES IN SCHEMA storage TO anon, authenticated');\n\n // Default privileges cover tables created after this shim runs (e.g. public.profile via dbInit).\n await client.query(\n 'ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO service_role',\n );\n await client.query(\n 'ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, UPDATE ON TABLES TO authenticated',\n );\n await client.query('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO anon');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiDA,eAAsB,sBAAsB,QAA+B;CACzE,MAAM,OAAO,MAAM,kCAAkC;CACrD,MAAM,OAAO,MAAM,qCAAqC;CAExD,MAAM,OAAO,MAAM;;;;;;;;GAQlB;CAED,MAAM,OAAO,MAAM;;;;;;;;;GASlB;CAED,MAAM,OAAO,MAAM;;;;;;;;GAQlB;CAED,MAAM,OAAO,MAAM;;;;;;;;;GASlB;CAID,MAAM,OAAO,MAAM,0BAA0B;CAC7C,MAAM,OAAO,MAAM,mCAAmC;CACtD,MAAM,OAAO,MAAM,4CAA4C;CAC/D,MAAM,OAAO,MAAM,mEAAmE;CACtF,MAAM,OAAO,MAAM,0EAA0E;CAC7F,MAAM,OAAO,MAAM,wDAAwD;CAC3E,MAAM,OAAO,MAAM,2DAA2D;CAC9E,MAAM,OAAO,MAAM,kEAAkE;CACrF,MAAM,OAAO,MAAM,qEAAqE;CAGxF,MAAM,OAAO,MACX,+EACF;CACA,MAAM,OAAO,MACX,2FACF;CACA,MAAM,OAAO,MAAM,0EAA0E;AAC/F"}
|
package/package.json
CHANGED
|
@@ -1,44 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/extension-supabase",
|
|
3
|
-
"version": "0.13.0-dev.
|
|
3
|
+
"version": "0.13.0-dev.21",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@prisma-next/
|
|
9
|
-
"@prisma-next/contract
|
|
10
|
-
"@prisma-next/
|
|
11
|
-
"@prisma-next/
|
|
12
|
-
"@prisma-next/
|
|
13
|
-
"@prisma-next/sql-
|
|
14
|
-
"@prisma-next/
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"@prisma-next/
|
|
18
|
-
"@prisma-next/sql
|
|
19
|
-
"@prisma-next/
|
|
8
|
+
"@prisma-next/adapter-postgres": "0.13.0-dev.21",
|
|
9
|
+
"@prisma-next/contract": "0.13.0-dev.21",
|
|
10
|
+
"@prisma-next/driver-postgres": "0.13.0-dev.21",
|
|
11
|
+
"@prisma-next/postgres": "0.13.0-dev.21",
|
|
12
|
+
"@prisma-next/sql-builder": "0.13.0-dev.21",
|
|
13
|
+
"@prisma-next/sql-orm-client": "0.13.0-dev.21",
|
|
14
|
+
"@prisma-next/target-postgres": "0.13.0-dev.21",
|
|
15
|
+
"jose": "^5",
|
|
16
|
+
"pg": "8.21.0",
|
|
17
|
+
"@prisma-next/contract-authoring": "0.13.0-dev.21",
|
|
18
|
+
"@prisma-next/family-sql": "0.13.0-dev.21",
|
|
19
|
+
"@prisma-next/framework-components": "0.13.0-dev.21",
|
|
20
|
+
"@prisma-next/migration-tools": "0.13.0-dev.21",
|
|
21
|
+
"@prisma-next/sql-contract": "0.13.0-dev.21",
|
|
22
|
+
"@prisma-next/sql-contract-ts": "0.13.0-dev.21",
|
|
23
|
+
"@prisma-next/sql-operations": "0.13.0-dev.21",
|
|
24
|
+
"@prisma-next/sql-relational-core": "0.13.0-dev.21",
|
|
25
|
+
"@prisma-next/sql-runtime": "0.13.0-dev.21",
|
|
26
|
+
"@prisma-next/sql-schema-ir": "0.13.0-dev.21",
|
|
27
|
+
"@prisma-next/utils": "0.13.0-dev.21",
|
|
20
28
|
"@standard-schema/spec": "^1.1.0",
|
|
21
29
|
"arktype": "^2.2.0"
|
|
22
30
|
},
|
|
23
31
|
"devDependencies": {
|
|
24
|
-
"@prisma-next/
|
|
25
|
-
"@prisma-next/
|
|
26
|
-
"@prisma-next/
|
|
27
|
-
"@prisma-next/
|
|
28
|
-
"@prisma-next/
|
|
29
|
-
"@prisma-next/
|
|
30
|
-
"@prisma-next/target-postgres": "0.13.0-dev.2",
|
|
31
|
-
"@prisma-next/test-utils": "0.13.0-dev.2",
|
|
32
|
-
"@prisma-next/tsconfig": "0.13.0-dev.2",
|
|
33
|
-
"@prisma-next/tsdown": "0.13.0-dev.2",
|
|
32
|
+
"@prisma-next/cli": "0.13.0-dev.21",
|
|
33
|
+
"@prisma-next/operations": "0.13.0-dev.21",
|
|
34
|
+
"@prisma-next/sql-contract-psl": "0.13.0-dev.21",
|
|
35
|
+
"@prisma-next/test-utils": "0.13.0-dev.21",
|
|
36
|
+
"@prisma-next/tsconfig": "0.13.0-dev.21",
|
|
37
|
+
"@prisma-next/tsdown": "0.13.0-dev.21",
|
|
34
38
|
"@types/pg": "8.20.0",
|
|
35
|
-
"pg": "8.21.0",
|
|
36
39
|
"tsdown": "0.22.1",
|
|
37
40
|
"typescript": "5.9.3",
|
|
38
41
|
"vitest": "4.1.8"
|
|
39
42
|
},
|
|
40
43
|
"peerDependencies": {
|
|
41
|
-
"@prisma-next/adapter-postgres": "0.13.0-dev.
|
|
44
|
+
"@prisma-next/adapter-postgres": "0.13.0-dev.21",
|
|
42
45
|
"typescript": ">=5.9"
|
|
43
46
|
},
|
|
44
47
|
"peerDependenciesMeta": {
|
|
@@ -31,7 +31,7 @@ import type {
|
|
|
31
31
|
} from '@prisma-next/contract/types';
|
|
32
32
|
|
|
33
33
|
export type StorageHash =
|
|
34
|
-
StorageHashBase<'sha256:
|
|
34
|
+
StorageHashBase<'sha256:27dcfcb121eec1827ff7464f6262582a413af76d4d86627d93db97bb2108410a'>;
|
|
35
35
|
export type ExecutionHash = ExecutionHashBase<string>;
|
|
36
36
|
export type ProfileHash =
|
|
37
37
|
ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>;
|
|
@@ -45,14 +45,14 @@ type DefaultLiteralValue<CodecId extends string, _Encoded> = CodecId extends key
|
|
|
45
45
|
|
|
46
46
|
export type FieldOutputTypes = {
|
|
47
47
|
readonly AuthIdentity: {
|
|
48
|
-
readonly id: CodecTypes['pg/
|
|
49
|
-
readonly user_id: CodecTypes['pg/
|
|
48
|
+
readonly id: CodecTypes['pg/uuid@1']['output'];
|
|
49
|
+
readonly user_id: CodecTypes['pg/uuid@1']['output'];
|
|
50
50
|
readonly provider: CodecTypes['pg/text@1']['output'];
|
|
51
51
|
readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
|
|
52
52
|
readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
|
|
53
53
|
};
|
|
54
54
|
readonly AuthUser: {
|
|
55
|
-
readonly id: CodecTypes['pg/
|
|
55
|
+
readonly id: CodecTypes['pg/uuid@1']['output'];
|
|
56
56
|
readonly email: CodecTypes['pg/text@1']['output'];
|
|
57
57
|
readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
|
|
58
58
|
readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
|
|
@@ -64,7 +64,7 @@ export type FieldOutputTypes = {
|
|
|
64
64
|
readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
|
|
65
65
|
};
|
|
66
66
|
readonly StorageObject: {
|
|
67
|
-
readonly id: CodecTypes['pg/
|
|
67
|
+
readonly id: CodecTypes['pg/uuid@1']['output'];
|
|
68
68
|
readonly bucket_id: CodecTypes['pg/text@1']['output'];
|
|
69
69
|
readonly name: CodecTypes['pg/text@1']['output'];
|
|
70
70
|
readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
|
|
@@ -73,14 +73,14 @@ export type FieldOutputTypes = {
|
|
|
73
73
|
};
|
|
74
74
|
export type FieldInputTypes = {
|
|
75
75
|
readonly AuthIdentity: {
|
|
76
|
-
readonly id: CodecTypes['pg/
|
|
77
|
-
readonly user_id: CodecTypes['pg/
|
|
76
|
+
readonly id: CodecTypes['pg/uuid@1']['input'];
|
|
77
|
+
readonly user_id: CodecTypes['pg/uuid@1']['input'];
|
|
78
78
|
readonly provider: CodecTypes['pg/text@1']['input'];
|
|
79
79
|
readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
|
|
80
80
|
readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
|
|
81
81
|
};
|
|
82
82
|
readonly AuthUser: {
|
|
83
|
-
readonly id: CodecTypes['pg/
|
|
83
|
+
readonly id: CodecTypes['pg/uuid@1']['input'];
|
|
84
84
|
readonly email: CodecTypes['pg/text@1']['input'];
|
|
85
85
|
readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
|
|
86
86
|
readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
|
|
@@ -92,7 +92,7 @@ export type FieldInputTypes = {
|
|
|
92
92
|
readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
|
|
93
93
|
};
|
|
94
94
|
readonly StorageObject: {
|
|
95
|
-
readonly id: CodecTypes['pg/
|
|
95
|
+
readonly id: CodecTypes['pg/uuid@1']['input'];
|
|
96
96
|
readonly bucket_id: CodecTypes['pg/text@1']['input'];
|
|
97
97
|
readonly name: CodecTypes['pg/text@1']['input'];
|
|
98
98
|
readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
|
|
@@ -124,13 +124,13 @@ type ContractBase = Omit<
|
|
|
124
124
|
columns: {
|
|
125
125
|
readonly id: {
|
|
126
126
|
readonly nativeType: 'uuid';
|
|
127
|
-
readonly codecId: 'pg/
|
|
127
|
+
readonly codecId: 'pg/uuid@1';
|
|
128
128
|
readonly nullable: false;
|
|
129
129
|
readonly typeRef: 'Uuid';
|
|
130
130
|
};
|
|
131
131
|
readonly user_id: {
|
|
132
132
|
readonly nativeType: 'uuid';
|
|
133
|
-
readonly codecId: 'pg/
|
|
133
|
+
readonly codecId: 'pg/uuid@1';
|
|
134
134
|
readonly nullable: false;
|
|
135
135
|
readonly typeRef: 'Uuid';
|
|
136
136
|
};
|
|
@@ -161,7 +161,7 @@ type ContractBase = Omit<
|
|
|
161
161
|
columns: {
|
|
162
162
|
readonly id: {
|
|
163
163
|
readonly nativeType: 'uuid';
|
|
164
|
-
readonly codecId: 'pg/
|
|
164
|
+
readonly codecId: 'pg/uuid@1';
|
|
165
165
|
readonly nullable: false;
|
|
166
166
|
readonly typeRef: 'Uuid';
|
|
167
167
|
};
|
|
@@ -235,7 +235,7 @@ type ContractBase = Omit<
|
|
|
235
235
|
columns: {
|
|
236
236
|
readonly id: {
|
|
237
237
|
readonly nativeType: 'uuid';
|
|
238
|
-
readonly codecId: 'pg/
|
|
238
|
+
readonly codecId: 'pg/uuid@1';
|
|
239
239
|
readonly nullable: false;
|
|
240
240
|
readonly typeRef: 'Uuid';
|
|
241
241
|
};
|
|
@@ -274,7 +274,7 @@ type ContractBase = Omit<
|
|
|
274
274
|
readonly types: {
|
|
275
275
|
readonly Uuid: {
|
|
276
276
|
readonly kind: 'codec-instance';
|
|
277
|
-
readonly codecId: 'pg/
|
|
277
|
+
readonly codecId: 'pg/uuid@1';
|
|
278
278
|
readonly nativeType: 'uuid';
|
|
279
279
|
readonly typeParams: Record<string, never>;
|
|
280
280
|
};
|
|
@@ -292,11 +292,11 @@ type ContractBase = Omit<
|
|
|
292
292
|
readonly fields: {
|
|
293
293
|
readonly id: {
|
|
294
294
|
readonly nullable: false;
|
|
295
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
295
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
296
296
|
};
|
|
297
297
|
readonly user_id: {
|
|
298
298
|
readonly nullable: false;
|
|
299
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
299
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
300
300
|
};
|
|
301
301
|
readonly provider: {
|
|
302
302
|
readonly nullable: false;
|
|
@@ -328,7 +328,7 @@ type ContractBase = Omit<
|
|
|
328
328
|
readonly fields: {
|
|
329
329
|
readonly id: {
|
|
330
330
|
readonly nullable: false;
|
|
331
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
331
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
332
332
|
};
|
|
333
333
|
readonly email: {
|
|
334
334
|
readonly nullable: false;
|
|
@@ -390,7 +390,7 @@ type ContractBase = Omit<
|
|
|
390
390
|
readonly fields: {
|
|
391
391
|
readonly id: {
|
|
392
392
|
readonly nullable: false;
|
|
393
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
393
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
394
394
|
};
|
|
395
395
|
readonly bucket_id: {
|
|
396
396
|
readonly nullable: false;
|
|
@@ -451,11 +451,11 @@ type ContractBase = Omit<
|
|
|
451
451
|
readonly fields: {
|
|
452
452
|
readonly id: {
|
|
453
453
|
readonly nullable: false;
|
|
454
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
454
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
455
455
|
};
|
|
456
456
|
readonly user_id: {
|
|
457
457
|
readonly nullable: false;
|
|
458
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
458
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
459
459
|
};
|
|
460
460
|
readonly provider: {
|
|
461
461
|
readonly nullable: false;
|
|
@@ -487,7 +487,7 @@ type ContractBase = Omit<
|
|
|
487
487
|
readonly fields: {
|
|
488
488
|
readonly id: {
|
|
489
489
|
readonly nullable: false;
|
|
490
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
490
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
491
491
|
};
|
|
492
492
|
readonly email: {
|
|
493
493
|
readonly nullable: false;
|
|
@@ -553,7 +553,7 @@ type ContractBase = Omit<
|
|
|
553
553
|
readonly fields: {
|
|
554
554
|
readonly id: {
|
|
555
555
|
readonly nullable: false;
|
|
556
|
-
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/
|
|
556
|
+
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
|
|
557
557
|
};
|
|
558
558
|
readonly bucket_id: {
|
|
559
559
|
readonly nullable: false;
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"id": {
|
|
38
38
|
"nullable": false,
|
|
39
39
|
"type": {
|
|
40
|
-
"codecId": "pg/
|
|
40
|
+
"codecId": "pg/uuid@1",
|
|
41
41
|
"kind": "scalar"
|
|
42
42
|
}
|
|
43
43
|
},
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"user_id": {
|
|
59
59
|
"nullable": false,
|
|
60
60
|
"type": {
|
|
61
|
-
"codecId": "pg/
|
|
61
|
+
"codecId": "pg/uuid@1",
|
|
62
62
|
"kind": "scalar"
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"id": {
|
|
106
106
|
"nullable": false,
|
|
107
107
|
"type": {
|
|
108
|
-
"codecId": "pg/
|
|
108
|
+
"codecId": "pg/uuid@1",
|
|
109
109
|
"kind": "scalar"
|
|
110
110
|
}
|
|
111
111
|
},
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"id": {
|
|
212
212
|
"nullable": false,
|
|
213
213
|
"type": {
|
|
214
|
-
"codecId": "pg/
|
|
214
|
+
"codecId": "pg/uuid@1",
|
|
215
215
|
"kind": "scalar"
|
|
216
216
|
}
|
|
217
217
|
},
|
|
@@ -278,7 +278,7 @@
|
|
|
278
278
|
"typeRef": "Timestamptz"
|
|
279
279
|
},
|
|
280
280
|
"id": {
|
|
281
|
-
"codecId": "pg/
|
|
281
|
+
"codecId": "pg/uuid@1",
|
|
282
282
|
"nativeType": "uuid",
|
|
283
283
|
"nullable": false,
|
|
284
284
|
"typeRef": "Uuid"
|
|
@@ -295,7 +295,7 @@
|
|
|
295
295
|
"typeRef": "Timestamptz"
|
|
296
296
|
},
|
|
297
297
|
"user_id": {
|
|
298
|
-
"codecId": "pg/
|
|
298
|
+
"codecId": "pg/uuid@1",
|
|
299
299
|
"nativeType": "uuid",
|
|
300
300
|
"nullable": false,
|
|
301
301
|
"typeRef": "Uuid"
|
|
@@ -324,7 +324,7 @@
|
|
|
324
324
|
"nullable": false
|
|
325
325
|
},
|
|
326
326
|
"id": {
|
|
327
|
-
"codecId": "pg/
|
|
327
|
+
"codecId": "pg/uuid@1",
|
|
328
328
|
"nativeType": "uuid",
|
|
329
329
|
"nullable": false,
|
|
330
330
|
"typeRef": "Uuid"
|
|
@@ -408,7 +408,7 @@
|
|
|
408
408
|
"typeRef": "Timestamptz"
|
|
409
409
|
},
|
|
410
410
|
"id": {
|
|
411
|
-
"codecId": "pg/
|
|
411
|
+
"codecId": "pg/uuid@1",
|
|
412
412
|
"nativeType": "uuid",
|
|
413
413
|
"nullable": false,
|
|
414
414
|
"typeRef": "Uuid"
|
|
@@ -440,7 +440,7 @@
|
|
|
440
440
|
"kind": "postgres-schema"
|
|
441
441
|
}
|
|
442
442
|
},
|
|
443
|
-
"storageHash": "sha256:
|
|
443
|
+
"storageHash": "sha256:27dcfcb121eec1827ff7464f6262582a413af76d4d86627d93db97bb2108410a",
|
|
444
444
|
"types": {
|
|
445
445
|
"Timestamptz": {
|
|
446
446
|
"codecId": "pg/timestamptz@1",
|
|
@@ -448,7 +448,7 @@
|
|
|
448
448
|
"nativeType": "timestamptz"
|
|
449
449
|
},
|
|
450
450
|
"Uuid": {
|
|
451
|
-
"codecId": "pg/
|
|
451
|
+
"codecId": "pg/uuid@1",
|
|
452
452
|
"kind": "codec-instance",
|
|
453
453
|
"nativeType": "uuid"
|
|
454
454
|
}
|
package/src/exports/runtime.ts
CHANGED
|
@@ -1,31 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
* Minimal M1 runtime descriptor for the Supabase extension.
|
|
3
|
-
*
|
|
4
|
-
* The Supabase pack contributes no runtime codec types or query operations in
|
|
5
|
-
* M1 — `auth.*`/`storage.*` are external tables accessed via the stock
|
|
6
|
-
* postgres runtime, not through a custom codec or operation surface. This
|
|
7
|
-
* descriptor exists so the postgres runtime's contract-requirements check
|
|
8
|
-
* (which verifies every `extensionPacks` entry in the emitted `contract.json`
|
|
9
|
-
* has a matching runtime component) passes.
|
|
10
|
-
*
|
|
11
|
-
* TODO(M2): Replace with the real SupabaseRuntime that adds
|
|
12
|
-
* `asUser()`/`asAnon()` role-binding and the Supabase auth surface.
|
|
13
|
-
*/
|
|
14
|
-
import type { SqlRuntimeExtensionDescriptor } from '@prisma-next/sql-runtime';
|
|
15
|
-
|
|
16
|
-
const supabaseRuntimeDescriptor: SqlRuntimeExtensionDescriptor<'postgres'> = {
|
|
17
|
-
kind: 'extension' as const,
|
|
18
|
-
id: 'supabase',
|
|
19
|
-
version: '0.12.0',
|
|
20
|
-
familyId: 'sql' as const,
|
|
21
|
-
targetId: 'postgres' as const,
|
|
22
|
-
codecs: () => [],
|
|
23
|
-
create() {
|
|
24
|
-
return {
|
|
25
|
-
familyId: 'sql' as const,
|
|
26
|
-
targetId: 'postgres' as const,
|
|
27
|
-
};
|
|
28
|
-
},
|
|
29
|
-
};
|
|
1
|
+
import { supabaseRuntimeDescriptor } from '../runtime/descriptor';
|
|
30
2
|
|
|
31
3
|
export default supabaseRuntimeDescriptor;
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
RoleBoundDb,
|
|
7
|
+
SupabaseDb,
|
|
8
|
+
SupabaseOptions,
|
|
9
|
+
SupabaseOptionsWithContract,
|
|
10
|
+
SupabaseOptionsWithContractJson,
|
|
11
|
+
SupabaseTargetId,
|
|
12
|
+
} from '../runtime/supabase';
|
|
13
|
+
export { default as supabase, InvalidJwtError, SupabaseConfigError } from '../runtime/supabase';
|
|
14
|
+
export type {
|
|
15
|
+
RoleSession,
|
|
16
|
+
SupabaseRoleBinding,
|
|
17
|
+
SupabaseRuntime,
|
|
18
|
+
} from '../runtime/supabase-runtime';
|
|
19
|
+
export { SupabaseRuntimeImpl } from '../runtime/supabase-runtime';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SqlRuntimeExtensionDescriptor } from '@prisma-next/sql-runtime';
|
|
2
|
+
import packageJson from '../../package.json' with { type: 'json' };
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tells the runtime that the Supabase pack's runtime component is available.
|
|
6
|
+
*
|
|
7
|
+
* When a contract declares the Supabase pack, the runtime checks that a
|
|
8
|
+
* matching descriptor has been registered. Without this, loading a Supabase
|
|
9
|
+
* contract errors with "pack runtime component missing". The `supabase()`
|
|
10
|
+
* factory registers this descriptor automatically — app code never needs to
|
|
11
|
+
* reference it directly.
|
|
12
|
+
*/
|
|
13
|
+
export const supabaseRuntimeDescriptor: SqlRuntimeExtensionDescriptor<'postgres'> = {
|
|
14
|
+
kind: 'extension' as const,
|
|
15
|
+
id: 'supabase',
|
|
16
|
+
version: packageJson.version,
|
|
17
|
+
familyId: 'sql' as const,
|
|
18
|
+
targetId: 'postgres' as const,
|
|
19
|
+
codecs: () => [],
|
|
20
|
+
create() {
|
|
21
|
+
return {
|
|
22
|
+
familyId: 'sql' as const,
|
|
23
|
+
targetId: 'postgres' as const,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import type { RuntimeExecuteOptions } from '@prisma-next/framework-components/runtime';
|
|
3
|
+
import { AsyncIterableResult } from '@prisma-next/framework-components/runtime';
|
|
4
|
+
import { type PostgresRuntime, PostgresRuntimeImpl } from '@prisma-next/postgres/runtime';
|
|
5
|
+
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
6
|
+
import type { SqlExecutionPlan, SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
|
|
7
|
+
import type {
|
|
8
|
+
PreparedStatement,
|
|
9
|
+
PreparedStatementImpl,
|
|
10
|
+
RuntimeConnection,
|
|
11
|
+
RuntimeTransaction,
|
|
12
|
+
} from '@prisma-next/sql-runtime';
|
|
13
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
14
|
+
|
|
15
|
+
export interface SupabaseRuntime extends PostgresRuntime {}
|
|
16
|
+
|
|
17
|
+
export interface SupabaseRoleBinding {
|
|
18
|
+
// TODO(TML-2501): role names move to the Supabase extension contract (roles as first-class IR) when postgres-rls lands.
|
|
19
|
+
readonly role: 'anon' | 'authenticated' | 'service_role';
|
|
20
|
+
readonly claims?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A connection with a Supabase role already bound via session-scoped set_config.
|
|
25
|
+
* Implements `RuntimeConnection` so it plugs into ORM scope machinery and `withTransaction`.
|
|
26
|
+
*/
|
|
27
|
+
export interface RoleSession extends RuntimeConnection {}
|
|
28
|
+
|
|
29
|
+
export class SupabaseRuntimeImpl<
|
|
30
|
+
TContract extends Contract<SqlStorage> = Contract<SqlStorage>,
|
|
31
|
+
> extends PostgresRuntimeImpl<TContract> {
|
|
32
|
+
/**
|
|
33
|
+
* Opens a raw connection and applies role + JWT claims via session-scoped set_config.
|
|
34
|
+
* On bind failure, destroys the connection before rethrowing — no leaked connections.
|
|
35
|
+
* Not on the `SupabaseRuntime` interface; consumed by the facade, not by app code.
|
|
36
|
+
*/
|
|
37
|
+
async openRoleSession(binding: SupabaseRoleBinding): Promise<RoleSession> {
|
|
38
|
+
const conn = await this.acquireRawConnection();
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await conn.query('SELECT set_config($1, $2, false)', ['role', binding.role]);
|
|
42
|
+
await conn.query('SELECT set_config($1, $2, false)', [
|
|
43
|
+
'request.jwt.claims',
|
|
44
|
+
JSON.stringify(binding.claims ?? {}),
|
|
45
|
+
]);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
await conn.destroy(err).catch(() => undefined);
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const self = this;
|
|
52
|
+
|
|
53
|
+
const session: RoleSession = {
|
|
54
|
+
execute<Row>(
|
|
55
|
+
plan: (SqlExecutionPlan<unknown> | SqlQueryPlan<unknown>) & { readonly _row?: Row },
|
|
56
|
+
options?: RuntimeExecuteOptions,
|
|
57
|
+
): AsyncIterableResult<Row> {
|
|
58
|
+
return self.executeAgainstQueryable<Row>(plan, conn, { ...options, scope: 'connection' });
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
executePrepared<Params, Row>(
|
|
62
|
+
ps: PreparedStatement<Params, Row>,
|
|
63
|
+
params: Params,
|
|
64
|
+
options?: RuntimeExecuteOptions,
|
|
65
|
+
): AsyncIterableResult<Row> {
|
|
66
|
+
return self.executePreparedAgainstQueryable(
|
|
67
|
+
blindCast<
|
|
68
|
+
PreparedStatementImpl<Params, Row>,
|
|
69
|
+
'PreparedStatement is PreparedStatementImpl; the impl class is the only concrete form'
|
|
70
|
+
>(ps),
|
|
71
|
+
blindCast<
|
|
72
|
+
Record<string, unknown>,
|
|
73
|
+
'params are structurally Record<string, unknown> at runtime'
|
|
74
|
+
>(params),
|
|
75
|
+
conn,
|
|
76
|
+
{ ...options, scope: 'connection' },
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
async transaction(): Promise<RuntimeTransaction> {
|
|
81
|
+
const tx = await conn.beginTransaction();
|
|
82
|
+
return {
|
|
83
|
+
async commit(): Promise<void> {
|
|
84
|
+
await tx.commit();
|
|
85
|
+
},
|
|
86
|
+
async rollback(): Promise<void> {
|
|
87
|
+
await tx.rollback();
|
|
88
|
+
},
|
|
89
|
+
execute<Row>(
|
|
90
|
+
plan: (SqlExecutionPlan<unknown> | SqlQueryPlan<unknown>) & { readonly _row?: Row },
|
|
91
|
+
options?: RuntimeExecuteOptions,
|
|
92
|
+
): AsyncIterableResult<Row> {
|
|
93
|
+
return self.executeAgainstQueryable<Row>(plan, tx, {
|
|
94
|
+
...options,
|
|
95
|
+
scope: 'transaction',
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
executePrepared<Params, Row>(
|
|
99
|
+
ps: PreparedStatement<Params, Row>,
|
|
100
|
+
params: Params,
|
|
101
|
+
options?: RuntimeExecuteOptions,
|
|
102
|
+
): AsyncIterableResult<Row> {
|
|
103
|
+
return self.executePreparedAgainstQueryable(
|
|
104
|
+
blindCast<
|
|
105
|
+
PreparedStatementImpl<Params, Row>,
|
|
106
|
+
'PreparedStatement is PreparedStatementImpl; the impl class is the only concrete form'
|
|
107
|
+
>(ps),
|
|
108
|
+
blindCast<
|
|
109
|
+
Record<string, unknown>,
|
|
110
|
+
'params are structurally Record<string, unknown> at runtime'
|
|
111
|
+
>(params),
|
|
112
|
+
tx,
|
|
113
|
+
{ ...options, scope: 'transaction' },
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Resets all session-local config then releases the connection back to the pool.
|
|
121
|
+
* If RESET ALL fails, destroys the connection instead — pool-poisoning guarantee.
|
|
122
|
+
*/
|
|
123
|
+
async release(): Promise<void> {
|
|
124
|
+
try {
|
|
125
|
+
await conn.query('RESET ALL');
|
|
126
|
+
await conn.release();
|
|
127
|
+
} catch (resetError) {
|
|
128
|
+
await conn.destroy(resetError).catch(() => undefined);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
async destroy(reason?: unknown): Promise<void> {
|
|
133
|
+
await conn.destroy(reason);
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return session;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Opens a role session, executes the plan, then releases after the stream drains.
|
|
142
|
+
* On mid-stream error, destroys the session instead of releasing.
|
|
143
|
+
*/
|
|
144
|
+
executeWithRole<Row>(
|
|
145
|
+
plan: SqlExecutionPlan<Row> | SqlQueryPlan<Row>,
|
|
146
|
+
binding: SupabaseRoleBinding,
|
|
147
|
+
options?: RuntimeExecuteOptions,
|
|
148
|
+
): AsyncIterableResult<Row> {
|
|
149
|
+
const self = this;
|
|
150
|
+
|
|
151
|
+
const generator = async function* (): AsyncGenerator<Row, void, unknown> {
|
|
152
|
+
const session = await self.openRoleSession(binding);
|
|
153
|
+
let errored = false;
|
|
154
|
+
try {
|
|
155
|
+
for await (const row of session.execute(plan, options)) {
|
|
156
|
+
yield row;
|
|
157
|
+
}
|
|
158
|
+
} catch (err) {
|
|
159
|
+
errored = true;
|
|
160
|
+
await session.destroy(err).catch(() => undefined);
|
|
161
|
+
throw err;
|
|
162
|
+
} finally {
|
|
163
|
+
if (!errored) {
|
|
164
|
+
await session.release();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
return new AsyncIterableResult(generator());
|
|
170
|
+
}
|
|
171
|
+
}
|