@prisma-next/extension-supabase 0.14.0-dev.6 → 0.14.0-dev.61

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.
@@ -30,7 +30,7 @@ import type {
30
30
  } from '@prisma-next/contract/types';
31
31
 
32
32
  export type StorageHash =
33
- StorageHashBase<'sha256:61dac13aa56bcb5b7f1c5441c2bd8fc07d88f6c870c80b4c1640fd0d180fb423'>;
33
+ StorageHashBase<'sha256:01edbb5adbbc0711bed516ea61b0585de2aa906f9aebacedbead613eff89f194'>;
34
34
  export type ExecutionHash = ExecutionHashBase<string>;
35
35
  export type ProfileHash =
36
36
  ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>;
@@ -51,6 +51,12 @@ export type FieldOutputTypes = {
51
51
  readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
52
52
  readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
53
53
  };
54
+ readonly AuthSession: {
55
+ readonly id: CodecTypes['pg/uuid@1']['output'];
56
+ readonly user_id: CodecTypes['pg/uuid@1']['output'];
57
+ readonly aal: 'aal1' | 'aal2' | 'aal3' | null;
58
+ readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
59
+ };
54
60
  readonly AuthUser: {
55
61
  readonly id: CodecTypes['pg/uuid@1']['output'];
56
62
  readonly email: CodecTypes['pg/text@1']['output'];
@@ -83,6 +89,12 @@ export type FieldInputTypes = {
83
89
  readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
84
90
  readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
85
91
  };
92
+ readonly AuthSession: {
93
+ readonly id: CodecTypes['pg/uuid@1']['input'];
94
+ readonly user_id: CodecTypes['pg/uuid@1']['input'];
95
+ readonly aal: 'aal1' | 'aal2' | 'aal3' | null;
96
+ readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
97
+ };
86
98
  readonly AuthUser: {
87
99
  readonly id: CodecTypes['pg/uuid@1']['input'];
88
100
  readonly email: CodecTypes['pg/text@1']['input'];
@@ -106,11 +118,91 @@ export type FieldInputTypes = {
106
118
  };
107
119
  };
108
120
  };
121
+ export type StorageColumnTypes = {
122
+ readonly auth: {
123
+ readonly identities: {
124
+ readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
125
+ readonly id: CodecTypes['pg/uuid@1']['output'];
126
+ readonly provider: CodecTypes['pg/text@1']['output'];
127
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
128
+ readonly user_id: CodecTypes['pg/uuid@1']['output'];
129
+ };
130
+ readonly sessions: {
131
+ readonly aal: 'aal1' | 'aal2' | 'aal3' | null;
132
+ readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
133
+ readonly id: CodecTypes['pg/uuid@1']['output'];
134
+ readonly user_id: CodecTypes['pg/uuid@1']['output'];
135
+ };
136
+ readonly users: {
137
+ readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
138
+ readonly email: CodecTypes['pg/text@1']['output'];
139
+ readonly id: CodecTypes['pg/uuid@1']['output'];
140
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
141
+ };
142
+ };
143
+ readonly public: {};
144
+ readonly storage: {
145
+ readonly buckets: {
146
+ readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
147
+ readonly id: CodecTypes['pg/text@1']['output'];
148
+ readonly name: CodecTypes['pg/text@1']['output'];
149
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
150
+ };
151
+ readonly objects: {
152
+ readonly bucket_id: CodecTypes['pg/text@1']['output'];
153
+ readonly created_at: CodecTypes['pg/timestamptz@1']['output'];
154
+ readonly id: CodecTypes['pg/uuid@1']['output'];
155
+ readonly name: CodecTypes['pg/text@1']['output'];
156
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['output'];
157
+ };
158
+ };
159
+ };
160
+ export type StorageColumnInputTypes = {
161
+ readonly auth: {
162
+ readonly identities: {
163
+ readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
164
+ readonly id: CodecTypes['pg/uuid@1']['input'];
165
+ readonly provider: CodecTypes['pg/text@1']['input'];
166
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
167
+ readonly user_id: CodecTypes['pg/uuid@1']['input'];
168
+ };
169
+ readonly sessions: {
170
+ readonly aal: 'aal1' | 'aal2' | 'aal3' | null;
171
+ readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
172
+ readonly id: CodecTypes['pg/uuid@1']['input'];
173
+ readonly user_id: CodecTypes['pg/uuid@1']['input'];
174
+ };
175
+ readonly users: {
176
+ readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
177
+ readonly email: CodecTypes['pg/text@1']['input'];
178
+ readonly id: CodecTypes['pg/uuid@1']['input'];
179
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
180
+ };
181
+ };
182
+ readonly public: {};
183
+ readonly storage: {
184
+ readonly buckets: {
185
+ readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
186
+ readonly id: CodecTypes['pg/text@1']['input'];
187
+ readonly name: CodecTypes['pg/text@1']['input'];
188
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
189
+ };
190
+ readonly objects: {
191
+ readonly bucket_id: CodecTypes['pg/text@1']['input'];
192
+ readonly created_at: CodecTypes['pg/timestamptz@1']['input'];
193
+ readonly id: CodecTypes['pg/uuid@1']['input'];
194
+ readonly name: CodecTypes['pg/text@1']['input'];
195
+ readonly updated_at: CodecTypes['pg/timestamptz@1']['input'];
196
+ };
197
+ };
198
+ };
109
199
  export type TypeMaps = TypeMapsType<
110
200
  CodecTypes,
111
201
  QueryOperationTypes,
112
202
  FieldOutputTypes,
113
- FieldInputTypes
203
+ FieldInputTypes,
204
+ StorageColumnTypes,
205
+ StorageColumnInputTypes
114
206
  >;
115
207
 
116
208
  type ContractBase = Omit<
@@ -118,7 +210,7 @@ type ContractBase = Omit<
118
210
  readonly namespaces: {
119
211
  readonly auth: {
120
212
  readonly id: 'auth';
121
- readonly kind: 'sql-namespace';
213
+ readonly kind: 'postgres-schema';
122
214
  readonly entries: {
123
215
  readonly table: {
124
216
  readonly identities: {
@@ -158,6 +250,38 @@ type ContractBase = Omit<
158
250
  indexes: readonly [];
159
251
  foreignKeys: readonly [];
160
252
  };
253
+ readonly sessions: {
254
+ columns: {
255
+ readonly id: {
256
+ readonly nativeType: 'uuid';
257
+ readonly codecId: 'pg/uuid@1';
258
+ readonly nullable: false;
259
+ readonly typeRef: 'Uuid';
260
+ };
261
+ readonly user_id: {
262
+ readonly nativeType: 'uuid';
263
+ readonly codecId: 'pg/uuid@1';
264
+ readonly nullable: false;
265
+ readonly typeRef: 'Uuid';
266
+ };
267
+ readonly aal: {
268
+ readonly nativeType: 'auth.aal_level';
269
+ readonly codecId: 'pg/enum@1';
270
+ readonly nullable: true;
271
+ readonly typeParams: { readonly typeName: 'auth.aal_level' };
272
+ };
273
+ readonly created_at: {
274
+ readonly nativeType: 'timestamptz';
275
+ readonly codecId: 'pg/timestamptz@1';
276
+ readonly nullable: false;
277
+ readonly typeRef: 'Timestamptz';
278
+ };
279
+ };
280
+ primaryKey: { readonly columns: readonly ['id'] };
281
+ uniques: readonly [];
282
+ indexes: readonly [];
283
+ foreignKeys: readonly [];
284
+ };
161
285
  readonly users: {
162
286
  columns: {
163
287
  readonly id: {
@@ -190,16 +314,22 @@ type ContractBase = Omit<
190
314
  foreignKeys: readonly [];
191
315
  };
192
316
  };
317
+ readonly valueSet: {
318
+ readonly AalLevel: {
319
+ readonly kind: 'valueSet';
320
+ readonly values: readonly ['aal1', 'aal2', 'aal3'];
321
+ };
322
+ };
193
323
  };
194
324
  };
195
325
  readonly public: {
196
326
  readonly id: 'public';
197
- readonly kind: 'sql-namespace';
327
+ readonly kind: 'postgres-schema';
198
328
  readonly entries: { readonly table: {} };
199
329
  };
200
330
  readonly storage: {
201
331
  readonly id: 'storage';
202
- readonly kind: 'sql-namespace';
332
+ readonly kind: 'postgres-schema';
203
333
  readonly entries: {
204
334
  readonly table: {
205
335
  readonly buckets: {
@@ -298,6 +428,7 @@ type ContractBase = Omit<
298
428
  readonly namespace: 'auth' & NamespaceId;
299
429
  readonly model: 'AuthIdentity';
300
430
  };
431
+ readonly sessions: { readonly namespace: 'auth' & NamespaceId; readonly model: 'AuthSession' };
301
432
  readonly buckets: {
302
433
  readonly namespace: 'storage' & NamespaceId;
303
434
  readonly model: 'StorageBucket';
@@ -347,6 +478,41 @@ type ContractBase = Omit<
347
478
  };
348
479
  };
349
480
  };
481
+ readonly AuthSession: {
482
+ readonly fields: {
483
+ readonly id: {
484
+ readonly nullable: false;
485
+ readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
486
+ };
487
+ readonly user_id: {
488
+ readonly nullable: false;
489
+ readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' };
490
+ };
491
+ readonly aal: {
492
+ readonly nullable: true;
493
+ readonly type: {
494
+ readonly kind: 'scalar';
495
+ readonly codecId: 'pg/enum@1';
496
+ readonly typeParams: { readonly typeName: 'auth.aal_level' };
497
+ };
498
+ };
499
+ readonly created_at: {
500
+ readonly nullable: false;
501
+ readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/timestamptz@1' };
502
+ };
503
+ };
504
+ readonly relations: Record<string, never>;
505
+ readonly storage: {
506
+ readonly table: 'sessions';
507
+ readonly namespaceId: 'auth';
508
+ readonly fields: {
509
+ readonly id: { readonly column: 'id' };
510
+ readonly user_id: { readonly column: 'user_id' };
511
+ readonly aal: { readonly column: 'aal' };
512
+ readonly created_at: { readonly column: 'created_at' };
513
+ };
514
+ };
515
+ };
350
516
  readonly AuthUser: {
351
517
  readonly fields: {
352
518
  readonly id: {
@@ -467,6 +633,7 @@ type ContractBase = Omit<
467
633
  readonly enums: true;
468
634
  readonly lateral: true;
469
635
  readonly returning: true;
636
+ readonly scalarList: true;
470
637
  };
471
638
  };
472
639
  readonly extensionPacks: {};
@@ -16,6 +16,10 @@
16
16
  "model": "StorageObject",
17
17
  "namespace": "storage"
18
18
  },
19
+ "sessions": {
20
+ "model": "AuthSession",
21
+ "namespace": "auth"
22
+ },
19
23
  "users": {
20
24
  "model": "AuthUser",
21
25
  "namespace": "auth"
@@ -86,6 +90,60 @@
86
90
  "table": "identities"
87
91
  }
88
92
  },
93
+ "AuthSession": {
94
+ "fields": {
95
+ "aal": {
96
+ "nullable": true,
97
+ "type": {
98
+ "codecId": "pg/enum@1",
99
+ "kind": "scalar",
100
+ "typeParams": {
101
+ "typeName": "auth.aal_level"
102
+ }
103
+ }
104
+ },
105
+ "created_at": {
106
+ "nullable": false,
107
+ "type": {
108
+ "codecId": "pg/timestamptz@1",
109
+ "kind": "scalar"
110
+ }
111
+ },
112
+ "id": {
113
+ "nullable": false,
114
+ "type": {
115
+ "codecId": "pg/uuid@1",
116
+ "kind": "scalar"
117
+ }
118
+ },
119
+ "user_id": {
120
+ "nullable": false,
121
+ "type": {
122
+ "codecId": "pg/uuid@1",
123
+ "kind": "scalar"
124
+ }
125
+ }
126
+ },
127
+ "relations": {},
128
+ "storage": {
129
+ "fields": {
130
+ "aal": {
131
+ "column": "aal"
132
+ },
133
+ "created_at": {
134
+ "column": "created_at"
135
+ },
136
+ "id": {
137
+ "column": "id"
138
+ },
139
+ "user_id": {
140
+ "column": "user_id"
141
+ }
142
+ },
143
+ "namespaceId": "auth",
144
+ "table": "sessions"
145
+ }
146
+ },
89
147
  "AuthUser": {
90
148
  "fields": {
91
149
  "created_at": {
@@ -261,6 +319,17 @@
261
319
  "namespaces": {
262
320
  "auth": {
263
321
  "entries": {
322
+ "native_enum": {
323
+ "AalLevel": {
324
+ "kind": "postgres-enum",
325
+ "members": [
326
+ "aal1",
327
+ "aal2",
328
+ "aal3"
329
+ ],
330
+ "typeName": "aal_level"
331
+ }
332
+ },
264
333
  "table": {
265
334
  "identities": {
266
335
  "columns": {
@@ -303,6 +372,50 @@
303
372
  },
304
373
  "uniques": []
305
374
  },
375
+ "sessions": {
376
+ "columns": {
377
+ "aal": {
378
+ "codecId": "pg/enum@1",
379
+ "nativeType": "auth.aal_level",
380
+ "nullable": true,
381
+ "typeParams": {
382
+ "typeName": "auth.aal_level"
383
+ },
384
+ "valueSet": {
385
+ "entityKind": "valueSet",
386
+ "entityName": "AalLevel",
387
+ "namespaceId": "auth",
388
+ "plane": "storage"
389
+ }
390
+ },
391
+ "created_at": {
392
+ "codecId": "pg/timestamptz@1",
393
+ "nativeType": "timestamptz",
394
+ "nullable": false,
395
+ "typeRef": "Timestamptz"
396
+ },
397
+ "id": {
398
+ "codecId": "pg/uuid@1",
399
+ "nativeType": "uuid",
400
+ "nullable": false,
401
+ "typeRef": "Uuid"
402
+ },
403
+ "user_id": {
404
+ "codecId": "pg/uuid@1",
405
+ "nativeType": "uuid",
406
+ "nullable": false,
407
+ "typeRef": "Uuid"
408
+ }
409
+ },
410
+ "foreignKeys": [],
411
+ "indexes": [],
412
+ "primaryKey": {
413
+ "columns": [
414
+ "id"
415
+ ]
416
+ },
417
+ "uniques": []
418
+ },
306
419
  "users": {
307
420
  "columns": {
308
421
  "created_at": {
@@ -338,6 +451,16 @@
338
451
  },
339
452
  "uniques": []
340
453
  }
454
+ },
455
+ "valueSet": {
456
+ "AalLevel": {
457
+ "kind": "valueSet",
458
+ "values": [
459
+ "aal1",
460
+ "aal2",
461
+ "aal3"
462
+ ]
463
+ }
341
464
  }
342
465
  },
343
466
  "id": "auth",
@@ -433,7 +556,7 @@
433
556
  "kind": "postgres-schema"
434
557
  }
435
558
  },
436
- "storageHash": "sha256:61dac13aa56bcb5b7f1c5441c2bd8fc07d88f6c870c80b4c1640fd0d180fb423",
559
+ "storageHash": "sha256:01edbb5adbbc0711bed516ea61b0585de2aa906f9aebacedbead613eff89f194",
437
560
  "types": {
438
561
  "Timestamptz": {
439
562
  "codecId": "pg/timestamptz@1",
@@ -460,7 +583,8 @@
460
583
  "defaultInInsert": true,
461
584
  "enums": true,
462
585
  "lateral": true,
463
- "returning": true
586
+ "returning": true,
587
+ "scalarList": true
464
588
  }
465
589
  },
466
590
  "extensionPacks": {},
@@ -24,6 +24,22 @@ namespace auth {
24
24
 
25
25
  @@map("identities")
26
26
  }
27
+
28
+ native_enum AalLevel {
29
+ aal1 = "aal1"
30
+ aal2 = "aal2"
31
+ aal3 = "aal3"
32
+ @@map("aal_level")
33
+ }
34
+
35
+ model AuthSession {
36
+ id Uuid @id
37
+ user_id Uuid
38
+ aal pg.enum(AalLevel)?
39
+ created_at Timestamptz
40
+
41
+ @@map("sessions")
42
+ }
27
43
  }
28
44
 
29
45
  namespace storage {
@@ -2,9 +2,12 @@ import { supabaseRuntimeDescriptor } from '../runtime/descriptor';
2
2
 
3
3
  export default supabaseRuntimeDescriptor;
4
4
 
5
+ export type { SupabaseExtensionContract } from '../runtime/ext-contract-type';
5
6
  export type {
6
7
  RoleBoundDb,
8
+ ServiceRoleDb,
7
9
  SupabaseDb,
10
+ SupabaseInternalDb,
8
11
  SupabaseOptions,
9
12
  SupabaseOptionsWithContract,
10
13
  SupabaseOptionsWithContractJson,
@@ -0,0 +1,9 @@
1
+ import type { Contract } from '../contract/contract.d';
2
+
3
+ /**
4
+ * The Supabase extension's own emitted contract type (`auth`, `storage`
5
+ * namespaces), re-exported under a distinct name so the runtime facade can
6
+ * reference it alongside the framework `Contract` (`@prisma-next/contract/types`)
7
+ * without an aliased import. Backs the `service_role` `.supabase` secondary root.
8
+ */
9
+ export type SupabaseExtensionContract = Contract;
@@ -6,6 +6,10 @@ import type {
6
6
  AsyncIterableResult,
7
7
  RuntimeExecuteOptions,
8
8
  } from '@prisma-next/framework-components/runtime';
9
+ import {
10
+ buildNamespacedNativeEnums,
11
+ type NamespacedNativeEnums,
12
+ } from '@prisma-next/postgres/runtime';
9
13
  import { sql } from '@prisma-next/sql-builder/runtime';
10
14
  import type { Db } from '@prisma-next/sql-builder/types';
11
15
  import type { SqlStorage } from '@prisma-next/sql-contract/types';
@@ -32,7 +36,9 @@ import { ifDefined } from '@prisma-next/utils/defined';
32
36
  import { createRemoteJWKSet, type JWTVerifyResult, jwtVerify } from 'jose';
33
37
  import type { Client } from 'pg';
34
38
  import { Pool } from 'pg';
39
+ import extensionContractJson from '../contract/contract.json' with { type: 'json' };
35
40
  import { supabaseRuntimeDescriptor } from './descriptor';
41
+ import type { SupabaseExtensionContract } from './ext-contract-type';
36
42
  import type { SupabaseRoleBinding, SupabaseRuntime } from './supabase-runtime';
37
43
  import { SupabaseRuntimeImpl } from './supabase-runtime';
38
44
 
@@ -71,12 +77,41 @@ export interface RoleBoundDb<TContract extends Contract<SqlStorage>> {
71
77
  transaction<R>(fn: (tx: TransactionContext) => PromiseLike<R>): Promise<R>;
72
78
  }
73
79
 
80
+ /**
81
+ * Query surface for the Supabase-internal contract (`auth`, `storage`). Exposed
82
+ * as a separate secondary root — never merged into the app contract — and only
83
+ * reachable through `service_role`, the one role with grants on those schemas
84
+ * over a direct Postgres connection.
85
+ *
86
+ * Deliberately omits `transaction` (which {@link RoleBoundDb} has): the primary
87
+ * app root and this secondary root are served by separate runtimes that do not
88
+ * share one pinned connection, so a transaction spanning both is out of scope for v1.
89
+ */
90
+ export interface SupabaseInternalDb {
91
+ readonly sql: Db<SupabaseExtensionContract>;
92
+ readonly orm: OrmClient<SupabaseExtensionContract>;
93
+ readonly nativeEnums: NamespacedNativeEnums<SupabaseExtensionContract>;
94
+ execute<Row>(
95
+ plan: (SqlExecutionPlan<Row> | SqlQueryPlan<Row>) & { readonly _row?: Row },
96
+ options?: RuntimeExecuteOptions,
97
+ ): AsyncIterableResult<Row>;
98
+ }
99
+
100
+ /**
101
+ * The `service_role` db: the app-contract role-bound surface (its `.sql` / `.orm`
102
+ * are app-only, exactly like `asUser` / `asAnon`), plus a `.supabase` secondary
103
+ * root for the Supabase-internal namespaces.
104
+ */
105
+ export type ServiceRoleDb<TContract extends Contract<SqlStorage>> = RoleBoundDb<TContract> & {
106
+ readonly supabase: SupabaseInternalDb;
107
+ };
108
+
74
109
  export interface SupabaseDb<TContract extends Contract<SqlStorage>> {
75
110
  readonly context: ExecutionContext<TContract>;
76
111
  readonly stack: SqlExecutionStackWithDriver<SupabaseTargetId>;
77
112
  asUser(jwt: string): Promise<RoleBoundDb<TContract>>;
78
113
  asAnon(): RoleBoundDb<TContract>;
79
- asServiceRole(): RoleBoundDb<TContract>;
114
+ asServiceRole(): ServiceRoleDb<TContract>;
80
115
  close(): Promise<void>;
81
116
  [Symbol.asyncDispose](): Promise<void>;
82
117
  }
@@ -138,11 +173,13 @@ const contractSerializer = new PostgresContractSerializer();
138
173
  function resolveContract<TContract extends Contract<SqlStorage>>(
139
174
  options: SupabaseOptions<TContract>,
140
175
  ): TContract {
141
- const contractInput = hasContractJson(options) ? options.contractJson : options.contract;
176
+ const contractJson = hasContractJson(options)
177
+ ? options.contractJson
178
+ : contractSerializer.serializeContract(options.contract);
142
179
  return blindCast<
143
180
  TContract,
144
181
  'contractSerializer.deserializeContract returns a validated TContract'
145
- >(contractSerializer.deserializeContract(contractInput));
182
+ >(contractSerializer.deserializeContract(contractJson));
146
183
  }
147
184
 
148
185
  function resolveKeyMaterial<TContract extends Contract<SqlStorage>>(
@@ -197,6 +234,20 @@ function withSupabaseDescriptor(
197
234
  : [...packs, supabaseRuntimeDescriptor];
198
235
  }
199
236
 
237
+ /**
238
+ * Deserializes the Supabase extension's own emitted contract into a runtime
239
+ * contract: namespaces hydrate into `PostgresSchema` instances (with
240
+ * `qualifyTable`), and `typeRef` columns (`Timestamptz`, `Uuid`) resolve
241
+ * through the codec registry. Exposed only via `service_role`'s `.supabase`
242
+ * secondary root — never merged into the app contract.
243
+ */
244
+ function buildExtensionContract(): SupabaseExtensionContract {
245
+ return blindCast<
246
+ SupabaseExtensionContract,
247
+ 'deserializeContract hydrates JSON namespaces into PostgresSchema instances with qualifyTable'
248
+ >(contractSerializer.deserializeContract(extensionContractJson));
249
+ }
250
+
200
251
  export default async function supabase<TContract extends Contract<SqlStorage>>(
201
252
  options: SupabaseOptionsWithContract<TContract>,
202
253
  ): Promise<SupabaseDb<TContract>>;
@@ -254,18 +305,20 @@ export default async function supabase<TContract extends Contract<SqlStorage>>(
254
305
  }
255
306
  }
256
307
 
257
- function buildRoleBoundDb(binding: SupabaseRoleBinding): RoleBoundDb<TContract> {
258
- const roleSql: Db<TContract> = sql<TContract>({ context, rawCodecInferer });
259
- const roleOrm: OrmClient<TContract> = orm({
308
+ function buildRoleBoundDbWithContext<C extends Contract<SqlStorage>>(
309
+ binding: SupabaseRoleBinding,
310
+ roleContext: ExecutionContext<C>,
311
+ roleRuntime: SupabaseRuntime & SupabaseRuntimeImpl<C>,
312
+ ): RoleBoundDb<C> {
313
+ const roleSql: Db<C> = sql<C>({ context: roleContext, rawCodecInferer });
314
+ const roleOrm: OrmClient<C> = orm({
260
315
  runtime: {
261
316
  execute(plan) {
262
- return runtime.executeWithRole(plan, binding);
317
+ return roleRuntime.executeWithRole(plan, binding);
263
318
  },
264
- // connection() returns a role session; this is the enforcement path for ORM scope
265
- // operations (mutations, includes) — every statement runs role-bound.
266
- connection: () => runtime.openRoleSession(binding),
319
+ connection: () => roleRuntime.openRoleSession(binding),
267
320
  },
268
- context,
321
+ context: roleContext,
269
322
  });
270
323
 
271
324
  return {
@@ -276,14 +329,63 @@ export default async function supabase<TContract extends Contract<SqlStorage>>(
276
329
  plan: (SqlExecutionPlan<Row> | SqlQueryPlan<Row>) & { readonly _row?: Row },
277
330
  execOptions?: RuntimeExecuteOptions,
278
331
  ): AsyncIterableResult<Row> {
279
- return runtime.executeWithRole<Row>(plan, binding, execOptions);
332
+ return roleRuntime.executeWithRole<Row>(plan, binding, execOptions);
280
333
  },
281
334
  transaction<R>(fn: (tx: TransactionContext) => PromiseLike<R>): Promise<R> {
282
- return withTransaction({ connection: () => runtime.openRoleSession(binding) }, fn);
335
+ return withTransaction({ connection: () => roleRuntime.openRoleSession(binding) }, fn);
283
336
  },
284
337
  };
285
338
  }
286
339
 
340
+ function buildRoleBoundDb(binding: SupabaseRoleBinding): RoleBoundDb<TContract> {
341
+ return buildRoleBoundDbWithContext(binding, context, runtime);
342
+ }
343
+
344
+ const serviceRoleBinding: SupabaseRoleBinding = { role: 'service_role', claims: {} };
345
+
346
+ // The Supabase-internal contract (auth/storage) as a separate secondary root.
347
+ // It is contract-bound: a plan built against it carries the extension's
348
+ // storageHash, so it must run on a runtime bound to the extension contract —
349
+ // the app runtime would reject it (PLAN.HASH_MISMATCH). This runtime shares
350
+ // the same driver (one pool, no second connection) and disables marker
351
+ // verification: the extension contract is external and owns no app-space
352
+ // marker, so its hashes must not be checked against the DB marker.
353
+ const extContract = buildExtensionContract();
354
+ const extContext = createExecutionContext({ contract: extContract, stack });
355
+ const extRuntime: SupabaseRuntime & SupabaseRuntimeImpl<SupabaseExtensionContract> =
356
+ new SupabaseRuntimeImpl({
357
+ context: extContext,
358
+ adapter: stackInstance.adapter,
359
+ driver,
360
+ verifyMarker: false,
361
+ ...ifDefined('middleware', options.middleware),
362
+ });
363
+
364
+ const extNativeEnums = blindCast<
365
+ NamespacedNativeEnums<SupabaseExtensionContract>,
366
+ 'buildNamespacedNativeEnums returns the namespace-keyed accessor map this contract types'
367
+ >(Object.freeze(buildNamespacedNativeEnums(extContract.storage)));
368
+
369
+ const supabaseInternal: SupabaseInternalDb = {
370
+ sql: sql<SupabaseExtensionContract>({ context: extContext, rawCodecInferer }),
371
+ orm: orm({
372
+ runtime: {
373
+ execute(plan) {
374
+ return extRuntime.executeWithRole(plan, serviceRoleBinding);
375
+ },
376
+ connection: () => extRuntime.openRoleSession(serviceRoleBinding),
377
+ },
378
+ context: extContext,
379
+ }),
380
+ nativeEnums: extNativeEnums,
381
+ execute<Row>(
382
+ plan: (SqlExecutionPlan<Row> | SqlQueryPlan<Row>) & { readonly _row?: Row },
383
+ execOptions?: RuntimeExecuteOptions,
384
+ ): AsyncIterableResult<Row> {
385
+ return extRuntime.executeWithRole<Row>(plan, serviceRoleBinding, execOptions);
386
+ },
387
+ };
388
+
287
389
  async function closeDb(): Promise<void> {
288
390
  if (closed) return;
289
391
  closed = true;
@@ -313,8 +415,9 @@ export default async function supabase<TContract extends Contract<SqlStorage>>(
313
415
  return buildRoleBoundDb({ role: 'anon', claims: {} });
314
416
  },
315
417
 
316
- asServiceRole(): RoleBoundDb<TContract> {
317
- return buildRoleBoundDb({ role: 'service_role', claims: {} });
418
+ asServiceRole(): ServiceRoleDb<TContract> {
419
+ const roleBound = buildRoleBoundDbWithContext(serviceRoleBinding, context, runtime);
420
+ return { ...roleBound, supabase: supabaseInternal };
318
421
  },
319
422
 
320
423
  close: closeDb,