@prisma-next/extension-supabase 0.14.0-dev.4 → 0.14.0-dev.40

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.
@@ -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;
@@ -32,7 +32,9 @@ import { ifDefined } from '@prisma-next/utils/defined';
32
32
  import { createRemoteJWKSet, type JWTVerifyResult, jwtVerify } from 'jose';
33
33
  import type { Client } from 'pg';
34
34
  import { Pool } from 'pg';
35
+ import extensionContractJson from '../contract/contract.json' with { type: 'json' };
35
36
  import { supabaseRuntimeDescriptor } from './descriptor';
37
+ import type { SupabaseExtensionContract } from './ext-contract-type';
36
38
  import type { SupabaseRoleBinding, SupabaseRuntime } from './supabase-runtime';
37
39
  import { SupabaseRuntimeImpl } from './supabase-runtime';
38
40
 
@@ -71,12 +73,40 @@ export interface RoleBoundDb<TContract extends Contract<SqlStorage>> {
71
73
  transaction<R>(fn: (tx: TransactionContext) => PromiseLike<R>): Promise<R>;
72
74
  }
73
75
 
76
+ /**
77
+ * Query surface for the Supabase-internal contract (`auth`, `storage`). Exposed
78
+ * as a separate secondary root — never merged into the app contract — and only
79
+ * reachable through `service_role`, the one role with grants on those schemas
80
+ * over a direct Postgres connection.
81
+ *
82
+ * Deliberately omits `transaction` (which {@link RoleBoundDb} has): the primary
83
+ * app root and this secondary root are served by separate runtimes that do not
84
+ * share one pinned connection, so a transaction spanning both is out of scope for v1.
85
+ */
86
+ export interface SupabaseInternalDb {
87
+ readonly sql: Db<SupabaseExtensionContract>;
88
+ readonly orm: OrmClient<SupabaseExtensionContract>;
89
+ execute<Row>(
90
+ plan: (SqlExecutionPlan<Row> | SqlQueryPlan<Row>) & { readonly _row?: Row },
91
+ options?: RuntimeExecuteOptions,
92
+ ): AsyncIterableResult<Row>;
93
+ }
94
+
95
+ /**
96
+ * The `service_role` db: the app-contract role-bound surface (its `.sql` / `.orm`
97
+ * are app-only, exactly like `asUser` / `asAnon`), plus a `.supabase` secondary
98
+ * root for the Supabase-internal namespaces.
99
+ */
100
+ export type ServiceRoleDb<TContract extends Contract<SqlStorage>> = RoleBoundDb<TContract> & {
101
+ readonly supabase: SupabaseInternalDb;
102
+ };
103
+
74
104
  export interface SupabaseDb<TContract extends Contract<SqlStorage>> {
75
105
  readonly context: ExecutionContext<TContract>;
76
106
  readonly stack: SqlExecutionStackWithDriver<SupabaseTargetId>;
77
107
  asUser(jwt: string): Promise<RoleBoundDb<TContract>>;
78
108
  asAnon(): RoleBoundDb<TContract>;
79
- asServiceRole(): RoleBoundDb<TContract>;
109
+ asServiceRole(): ServiceRoleDb<TContract>;
80
110
  close(): Promise<void>;
81
111
  [Symbol.asyncDispose](): Promise<void>;
82
112
  }
@@ -138,11 +168,13 @@ const contractSerializer = new PostgresContractSerializer();
138
168
  function resolveContract<TContract extends Contract<SqlStorage>>(
139
169
  options: SupabaseOptions<TContract>,
140
170
  ): TContract {
141
- const contractInput = hasContractJson(options) ? options.contractJson : options.contract;
171
+ const contractJson = hasContractJson(options)
172
+ ? options.contractJson
173
+ : contractSerializer.serializeContract(options.contract);
142
174
  return blindCast<
143
175
  TContract,
144
176
  'contractSerializer.deserializeContract returns a validated TContract'
145
- >(contractSerializer.deserializeContract(contractInput));
177
+ >(contractSerializer.deserializeContract(contractJson));
146
178
  }
147
179
 
148
180
  function resolveKeyMaterial<TContract extends Contract<SqlStorage>>(
@@ -197,6 +229,20 @@ function withSupabaseDescriptor(
197
229
  : [...packs, supabaseRuntimeDescriptor];
198
230
  }
199
231
 
232
+ /**
233
+ * Deserializes the Supabase extension's own emitted contract into a runtime
234
+ * contract: namespaces hydrate into `PostgresSchema` instances (with
235
+ * `qualifyTable`), and `typeRef` columns (`Timestamptz`, `Uuid`) resolve
236
+ * through the codec registry. Exposed only via `service_role`'s `.supabase`
237
+ * secondary root — never merged into the app contract.
238
+ */
239
+ function buildExtensionContract(): SupabaseExtensionContract {
240
+ return blindCast<
241
+ SupabaseExtensionContract,
242
+ 'deserializeContract hydrates JSON namespaces into PostgresSchema instances with qualifyTable'
243
+ >(contractSerializer.deserializeContract(extensionContractJson));
244
+ }
245
+
200
246
  export default async function supabase<TContract extends Contract<SqlStorage>>(
201
247
  options: SupabaseOptionsWithContract<TContract>,
202
248
  ): Promise<SupabaseDb<TContract>>;
@@ -254,18 +300,20 @@ export default async function supabase<TContract extends Contract<SqlStorage>>(
254
300
  }
255
301
  }
256
302
 
257
- function buildRoleBoundDb(binding: SupabaseRoleBinding): RoleBoundDb<TContract> {
258
- const roleSql: Db<TContract> = sql<TContract>({ context, rawCodecInferer });
259
- const roleOrm: OrmClient<TContract> = orm({
303
+ function buildRoleBoundDbWithContext<C extends Contract<SqlStorage>>(
304
+ binding: SupabaseRoleBinding,
305
+ roleContext: ExecutionContext<C>,
306
+ roleRuntime: SupabaseRuntime & SupabaseRuntimeImpl<C>,
307
+ ): RoleBoundDb<C> {
308
+ const roleSql: Db<C> = sql<C>({ context: roleContext, rawCodecInferer });
309
+ const roleOrm: OrmClient<C> = orm({
260
310
  runtime: {
261
311
  execute(plan) {
262
- return runtime.executeWithRole(plan, binding);
312
+ return roleRuntime.executeWithRole(plan, binding);
263
313
  },
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),
314
+ connection: () => roleRuntime.openRoleSession(binding),
267
315
  },
268
- context,
316
+ context: roleContext,
269
317
  });
270
318
 
271
319
  return {
@@ -276,14 +324,57 @@ export default async function supabase<TContract extends Contract<SqlStorage>>(
276
324
  plan: (SqlExecutionPlan<Row> | SqlQueryPlan<Row>) & { readonly _row?: Row },
277
325
  execOptions?: RuntimeExecuteOptions,
278
326
  ): AsyncIterableResult<Row> {
279
- return runtime.executeWithRole<Row>(plan, binding, execOptions);
327
+ return roleRuntime.executeWithRole<Row>(plan, binding, execOptions);
280
328
  },
281
329
  transaction<R>(fn: (tx: TransactionContext) => PromiseLike<R>): Promise<R> {
282
- return withTransaction({ connection: () => runtime.openRoleSession(binding) }, fn);
330
+ return withTransaction({ connection: () => roleRuntime.openRoleSession(binding) }, fn);
283
331
  },
284
332
  };
285
333
  }
286
334
 
335
+ function buildRoleBoundDb(binding: SupabaseRoleBinding): RoleBoundDb<TContract> {
336
+ return buildRoleBoundDbWithContext(binding, context, runtime);
337
+ }
338
+
339
+ const serviceRoleBinding: SupabaseRoleBinding = { role: 'service_role', claims: {} };
340
+
341
+ // The Supabase-internal contract (auth/storage) as a separate secondary root.
342
+ // It is contract-bound: a plan built against it carries the extension's
343
+ // storageHash, so it must run on a runtime bound to the extension contract —
344
+ // the app runtime would reject it (PLAN.HASH_MISMATCH). This runtime shares
345
+ // the same driver (one pool, no second connection) and disables marker
346
+ // verification: the extension contract is external and owns no app-space
347
+ // marker, so its hashes must not be checked against the DB marker.
348
+ const extContract = buildExtensionContract();
349
+ const extContext = createExecutionContext({ contract: extContract, stack });
350
+ const extRuntime: SupabaseRuntime & SupabaseRuntimeImpl<SupabaseExtensionContract> =
351
+ new SupabaseRuntimeImpl({
352
+ context: extContext,
353
+ adapter: stackInstance.adapter,
354
+ driver,
355
+ verifyMarker: false,
356
+ ...ifDefined('middleware', options.middleware),
357
+ });
358
+
359
+ const supabaseInternal: SupabaseInternalDb = {
360
+ sql: sql<SupabaseExtensionContract>({ context: extContext, rawCodecInferer }),
361
+ orm: orm({
362
+ runtime: {
363
+ execute(plan) {
364
+ return extRuntime.executeWithRole(plan, serviceRoleBinding);
365
+ },
366
+ connection: () => extRuntime.openRoleSession(serviceRoleBinding),
367
+ },
368
+ context: extContext,
369
+ }),
370
+ execute<Row>(
371
+ plan: (SqlExecutionPlan<Row> | SqlQueryPlan<Row>) & { readonly _row?: Row },
372
+ execOptions?: RuntimeExecuteOptions,
373
+ ): AsyncIterableResult<Row> {
374
+ return extRuntime.executeWithRole<Row>(plan, serviceRoleBinding, execOptions);
375
+ },
376
+ };
377
+
287
378
  async function closeDb(): Promise<void> {
288
379
  if (closed) return;
289
380
  closed = true;
@@ -313,8 +404,9 @@ export default async function supabase<TContract extends Contract<SqlStorage>>(
313
404
  return buildRoleBoundDb({ role: 'anon', claims: {} });
314
405
  },
315
406
 
316
- asServiceRole(): RoleBoundDb<TContract> {
317
- return buildRoleBoundDb({ role: 'service_role', claims: {} });
407
+ asServiceRole(): ServiceRoleDb<TContract> {
408
+ const roleBound = buildRoleBoundDbWithContext(serviceRoleBinding, context, runtime);
409
+ return { ...roleBound, supabase: supabaseInternal };
318
410
  },
319
411
 
320
412
  close: closeDb,
@@ -1,6 +0,0 @@
1
- //#region package.json
2
- var version = "0.14.0-dev.4";
3
- //#endregion
4
- export { version as t };
5
-
6
- //# sourceMappingURL=package-Ch-ESuiR.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"package-Ch-ESuiR.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}