cloudflare-next-intl 0.8.29 → 0.8.30

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.
@@ -127,8 +127,11 @@ export function createSendSignInLinkAction(locale, actionCodeSettings) {
127
127
  const { auth } = await getFirebaseAuthClient();
128
128
  const { sendSignInLinkToEmail } = await getFirebaseAuthModule();
129
129
  const email = (formData.get('email')?.toString() ?? '').trim();
130
+ const url = new URL(actionCodeSettings.url);
131
+ url.searchParams.set('email', email);
132
+ const settingsWithEmail = { ...actionCodeSettings, url: url.toString() };
130
133
  try {
131
- await sendSignInLinkToEmail(auth, email, actionCodeSettings);
134
+ await sendSignInLinkToEmail(auth, email, settingsWithEmail);
132
135
  return { success: true, email };
133
136
  }
134
137
  catch (e) {
package/llms.txt CHANGED
@@ -34,7 +34,7 @@ other subpath can be used.
34
34
  - `./firebaseAuthServerProvider` — server-side equivalent provider (not used by the default auto-wiring path — see its doc comment).
35
35
  - `./useFirebaseAuthUser` — `useAuthUser()`; resolves to RSC or client implementation via the `react-server` condition. Client variant throws `"useAuthUser must be used within an AuthUserProvider"` if called outside one.
36
36
  - `./getFirebaseAuthUser` — `getAuthUser()`; unconditional server-only export of the same RSC implementation `useFirebaseAuthUser` resolves to via `react-server`. Use this when you want `await` to be visible from the type itself — TypeScript doesn't evaluate the `react-server` condition, so `useFirebaseAuthUser` always types as its client (sync) signature in editors regardless of call site.
37
- - `./firebaseAuthActions` — `createLoginAction`/`createSignUpAction`/`createForgotPasswordAction`: factories returning React `useActionState`-shaped form actions. `createSendSignInLinkAction`: same factory shape, for passwordless email-link sign-in — returns `{ success: true, email }` so the caller can persist the trimmed email (e.g. to `localStorage`) for the completion step. `completeSignInWithLink(locale, url, email)`: plain async function (not `useActionState`-shaped) that completes a passwordless sign-in from the emailed link's landing page — call from an effect on mount, not a form submit.
37
+ - `./firebaseAuthActions` — `createLoginAction`/`createSignUpAction`/`createForgotPasswordAction`: factories returning React `useActionState`-shaped form actions. `createSendSignInLinkAction`: same factory shape, for passwordless email-link sign-in — appends the user's email as an `email` query parameter to `actionCodeSettings.url` and returns `{ success: true, email }` so the caller can persist the trimmed email (e.g. to `localStorage`) for the completion step. `completeSignInWithLink(locale, url, email)`: plain async function (not `useActionState`-shaped) that completes a passwordless sign-in from the emailed link's landing page — call from an effect on mount, not a form submit.
38
38
  - `./firebaseAuthMiddleware` — `updateSession`: session-cookie refresh, called automatically by `./middleware`'s default handler.
39
39
 
40
40
  ## `cookieConsent*` subpaths (require `cookieConsent` set on your `RoutingConfig`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.8.29",
3
+ "version": "0.8.30",
4
4
  "description": "Optimized Next Intl Package Special for App Router and Cloudflare",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,178 +0,0 @@
1
- /**
2
- * One filter's operator and value, e.g. `{ age: ['gt', 18] }`. The bare-value
3
- * form `{ id: 5 }` (see {@link SupabaseWhere}) is shorthand for `['eq', 5]`.
4
- * `['not', operator, value]` negates any operator (`['not', 'eq', 5]` is
5
- * `column <> 5` via PostgREST's `not.eq.5`).
6
- *
7
- * This is the complete operator set `@supabase/supabase-js`'s query builder
8
- * exposes for `.from(table)` — there's no separate "Supabase client" query
9
- * layer on top of `postgrest-js`; `supabase.from()` returns a `postgrest-js`
10
- * builder directly, so this list is also the ceiling for what `supabase-js`
11
- * itself can express against a single table. What's still out of reach:
12
- * joins/embedded resources (pass a nested `columns` string instead — that's
13
- * plain PostgREST syntax, not a filter), and anything needing more than one
14
- * table in a single round-trip. Those need `cfni_exec`/`withPublicDb`/
15
- * `withUserDb`, or a Postgres function called via {@link supabaseRpc}.
16
- */
17
- export type SupabaseFilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'likeAllOf' | 'likeAnyOf' | 'ilike' | 'ilikeAllOf' | 'ilikeAnyOf' | 'regexMatch' | 'regexIMatch' | 'is' | 'isDistinct' | 'in' | 'contains' | 'containedBy' | 'overlaps' | 'rangeGt' | 'rangeGte' | 'rangeLt' | 'rangeLte' | 'rangeAdjacent';
18
- export type SupabaseFilterValue = unknown | [SupabaseFilterOperator, unknown] | ['not', SupabaseFilterOperator, unknown];
19
- /** Equality (or `[operator, value]`) filters, ANDed together column by column. */
20
- export type SupabaseWhere = Record<string, SupabaseFilterValue>;
21
- export interface SupabaseTextSearch {
22
- column: string;
23
- query: string;
24
- type?: 'plain' | 'phrase' | 'websearch';
25
- config?: string;
26
- }
27
- export interface SupabaseOrderBy {
28
- column: string;
29
- ascending?: boolean;
30
- nullsFirst?: boolean;
31
- }
32
- export interface SupabaseSelectOptions {
33
- /** Column list, PostgREST syntax (e.g. `'id,name'`, or `'*, related(*)'` for an embedded resource). Defaults to `'*'`. */
34
- columns?: string;
35
- /** Filters, ANDed together — see {@link SupabaseWhere}. */
36
- where?: SupabaseWhere;
37
- /** Shorthand for several `eq` filters at once — PostgREST's `.match()`. ANDed with `where`. */
38
- match?: Record<string, unknown>;
39
- /** A raw PostgREST `or()` filter string (e.g. `'age.gt.18,status.eq.active'`), for filters spanning multiple columns. ANDed with `where`/`match`. */
40
- or?: string | {
41
- filters: string;
42
- referencedTable?: string;
43
- };
44
- /** Full-text search via PostgREST's `@@` operators. */
45
- textSearch?: SupabaseTextSearch;
46
- /** One or more `order by` clauses, applied in array order. */
47
- orderBy?: SupabaseOrderBy | SupabaseOrderBy[];
48
- /** Maximum rows to return. */
49
- limit?: number;
50
- /** Zero-indexed, inclusive `[from, to]` row range — PostgREST pagination. */
51
- range?: [number, number];
52
- /** Resolve to the single matching row instead of an array; errors if there isn't exactly one. */
53
- single?: boolean;
54
- /** Resolve to the single matching row, or `null` if there are none; errors if there's more than one. */
55
- maybeSingle?: boolean;
56
- /** Also return the total matching row count (`'exact'`, `'planned'`, or `'estimated'`). */
57
- count?: 'exact' | 'planned' | 'estimated';
58
- }
59
- export interface SupabaseMutationOptions {
60
- /** Filters, ANDed together — see {@link SupabaseWhere}. At least one of `where`/`match`/`or` is required for update/delete. */
61
- where?: SupabaseWhere;
62
- /** Shorthand for several `eq` filters at once. ANDed with `where`. */
63
- match?: Record<string, unknown>;
64
- /** A raw PostgREST `or()` filter string, for filters spanning multiple columns. ANDed with `where`/`match`. */
65
- or?: string | {
66
- filters: string;
67
- referencedTable?: string;
68
- };
69
- }
70
- export interface SupabaseUpsertOptions {
71
- /** Column(s) forming the conflict target, comma-separated. Defaults to the table's primary key. */
72
- onConflict?: string;
73
- /** Skip (rather than update) rows that already exist. Defaults to `false`. */
74
- ignoreDuplicates?: boolean;
75
- }
76
- export interface SupabaseResult<T> {
77
- rows: T[];
78
- /** Present only when `count` was requested on a select. */
79
- count: number | null;
80
- }
81
- /**
82
- * Selects rows from `table` through the Supabase REST API
83
- * (`@supabase/supabase-js`'s `.from(table).select()`), as the **anonymous**
84
- * role — no `cfni_exec`, no raw SQL, only what PostgREST's REST API itself
85
- * supports: a column list, filters (equality or a named operator), one or
86
- * more `order by` clauses, `limit`/`range` pagination, `single`/`maybeSingle`,
87
- * and an optional row count.
88
- *
89
- * Reach for this — or {@link supabaseSelectAsUser} — instead of
90
- * `withPublicDb`/`withUserDb` when `db.supabase.rawSql` is `false`, or
91
- * whenever a query is simple enough that avoiding `cfni_exec` entirely is
92
- * preferable. Cross-table joins/aggregates spanning more than an embedded
93
- * resource (`columns: '*, related(*)'`) still need `withPublicDb`/
94
- * `withUserDb`, or a Postgres function called via {@link supabaseRpc}.
95
- *
96
- * @param table Table (or view) name.
97
- * @param options Column list, filters, ordering, pagination, count.
98
- * @returns The matching rows, and the total count when `options.count` is set.
99
- * @throws If `db`/`db.supabase` is not set, or PostgREST rejects the request.
100
- */
101
- export declare function supabaseSelect<T = Record<string, unknown>>(table: string, options?: SupabaseSelectOptions): Promise<SupabaseResult<T>>;
102
- /**
103
- * Same as {@link supabaseSelect}, but authenticated as the **signed-in
104
- * user**: the bearer token comes from `db.getAccessToken`, or the signed-in
105
- * Firebase user's ID token when `firebaseAuth` is configured — see
106
- * {@link resolveAccessToken}. PostgREST resolves the caller as
107
- * `authenticated` and RLS applies exactly as it would for `withUserDb`.
108
- */
109
- export declare function supabaseSelectAsUser<T = Record<string, unknown>>(table: string, options?: SupabaseSelectOptions): Promise<SupabaseResult<T>>;
110
- /**
111
- * Inserts one or more rows into `table` through the Supabase REST API, as
112
- * the **anonymous** role. See {@link supabaseSelect} for when to use this
113
- * over `withPublicDb`.
114
- *
115
- * @param table Table name.
116
- * @param values A single row, or an array of rows, to insert.
117
- * @returns The inserted row(s) as returned by PostgREST.
118
- */
119
- export declare function supabaseInsert<T = Record<string, unknown>>(table: string, values: Record<string, unknown> | Record<string, unknown>[]): Promise<T[]>;
120
- /** Same as {@link supabaseInsert}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
121
- export declare function supabaseInsertAsUser<T = Record<string, unknown>>(table: string, values: Record<string, unknown> | Record<string, unknown>[]): Promise<T[]>;
122
- /**
123
- * Inserts one or more rows into `table`, or updates them on a conflict —
124
- * PostgREST's `upsert`, i.e. `insert ... on conflict (...) do update`. As
125
- * the **anonymous** role.
126
- *
127
- * @param table Table name.
128
- * @param values A single row, or an array of rows, to upsert.
129
- * @param options `onConflict` (defaults to the table's primary key) and
130
- * `ignoreDuplicates` (skip instead of update on conflict).
131
- * @returns The upserted row(s) as returned by PostgREST.
132
- */
133
- export declare function supabaseUpsert<T = Record<string, unknown>>(table: string, values: Record<string, unknown> | Record<string, unknown>[], options?: SupabaseUpsertOptions): Promise<T[]>;
134
- /** Same as {@link supabaseUpsert}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
135
- export declare function supabaseUpsertAsUser<T = Record<string, unknown>>(table: string, values: Record<string, unknown> | Record<string, unknown>[], options?: SupabaseUpsertOptions): Promise<T[]>;
136
- /**
137
- * Updates rows in `table` matching `options.where` through the Supabase
138
- * REST API, as the **anonymous** role. See {@link supabaseSelect} for when
139
- * to use this over `withPublicDb`.
140
- *
141
- * @param table Table name.
142
- * @param values Columns to set.
143
- * @param options `where` — filters selecting which rows to update; required,
144
- * since an unfiltered update would touch every row.
145
- * @returns The updated rows as returned by PostgREST.
146
- */
147
- export declare function supabaseUpdate<T = Record<string, unknown>>(table: string, values: Record<string, unknown>, options: SupabaseMutationOptions): Promise<T[]>;
148
- /** Same as {@link supabaseUpdate}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
149
- export declare function supabaseUpdateAsUser<T = Record<string, unknown>>(table: string, values: Record<string, unknown>, options: SupabaseMutationOptions): Promise<T[]>;
150
- /**
151
- * Deletes rows in `table` matching `options.where` through the Supabase REST
152
- * API, as the **anonymous** role. See {@link supabaseSelect} for when to
153
- * use this over `withPublicDb`.
154
- *
155
- * @param table Table name.
156
- * @param options `where` — filters selecting which rows to delete; required,
157
- * since an unfiltered delete would remove every row.
158
- * @returns The deleted rows as returned by PostgREST.
159
- */
160
- export declare function supabaseDelete<T = Record<string, unknown>>(table: string, options: SupabaseMutationOptions): Promise<T[]>;
161
- /** Same as {@link supabaseDelete}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
162
- export declare function supabaseDeleteAsUser<T = Record<string, unknown>>(table: string, options: SupabaseMutationOptions): Promise<T[]>;
163
- /**
164
- * Calls a Postgres function through PostgREST's `.rpc()` — the same
165
- * mechanism `cfni_exec` itself uses, but for a function you define yourself.
166
- * This is how to run anything the `select`/`insert`/`upsert`/`update`/
167
- * `delete` helpers above can't express (joins, aggregates, custom logic)
168
- * without needing `cfni_exec`/raw SQL: write a regular Postgres function,
169
- * `grant execute` to `anon`/`authenticated`, and call it by name.
170
- *
171
- * As the **anonymous** role.
172
- *
173
- * @param fn Function name.
174
- * @param args Named arguments, matching the function's parameter names.
175
- */
176
- export declare function supabaseRpc<T = unknown>(fn: string, args?: Record<string, unknown>): Promise<T>;
177
- /** Same as {@link supabaseRpc}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
178
- export declare function supabaseRpcAsUser<T = unknown>(fn: string, args?: Record<string, unknown>): Promise<T>;
@@ -1,262 +0,0 @@
1
- import config from '../config/intl_config';
2
- import requireDbConfig from './require_config';
3
- import resolveSupabaseEndpoint from './supabase_config';
4
- import resolveAccessToken from './access_token';
5
- function describeQueryFailure(error) {
6
- return `db: Supabase rejected the request — ${error.message}.`;
7
- }
8
- async function buildClient(supabase, bearerToken) {
9
- const { url, anonKey } = await resolveSupabaseEndpoint(supabase);
10
- const { createClient } = await import('@supabase/supabase-js');
11
- return createClient(url, anonKey, { accessToken: async () => bearerToken });
12
- }
13
- function applyFilters(query, where) {
14
- let result = query;
15
- if (!where)
16
- return result;
17
- for (const [column, filter] of Object.entries(where)) {
18
- if (Array.isArray(filter) && filter[0] === 'not') {
19
- const [, operator, value] = filter;
20
- result = result.not(column, operator, value);
21
- continue;
22
- }
23
- const [operator, value] = Array.isArray(filter)
24
- ? [filter[0], filter[1]]
25
- : ['eq', filter];
26
- result = result[operator](column, value);
27
- }
28
- return result;
29
- }
30
- function applyExtras(query, extras) {
31
- let result = query;
32
- if (extras.match)
33
- result = result.match(extras.match);
34
- if (extras.or) {
35
- const or = typeof extras.or === 'string' ? { filters: extras.or, referencedTable: undefined } : extras.or;
36
- result = result.or(or.filters, { referencedTable: or.referencedTable });
37
- }
38
- if (extras.textSearch) {
39
- result = result.textSearch(extras.textSearch.column, extras.textSearch.query, {
40
- type: extras.textSearch.type,
41
- config: extras.textSearch.config,
42
- });
43
- }
44
- return result;
45
- }
46
- function applyOrder(query, orderBy) {
47
- if (!orderBy)
48
- return query;
49
- let result = query;
50
- for (const clause of Array.isArray(orderBy) ? orderBy : [orderBy]) {
51
- result = result.order(clause.column, { ascending: clause.ascending, nullsFirst: clause.nullsFirst });
52
- }
53
- return result;
54
- }
55
- async function requireSupabaseConfig() {
56
- const db = config.db;
57
- requireDbConfig(db);
58
- if (!db.supabase) {
59
- throw new Error('db: supabaseSelect/supabaseInsert/supabaseUpsert/supabaseUpdate/supabaseDelete/' +
60
- 'supabaseRpc need `db.supabase` (a project URL and anon key) on your RoutingConfig, ' +
61
- 'even in connection-string mode.');
62
- }
63
- return db.supabase;
64
- }
65
- async function anonClient() {
66
- const supabase = await requireSupabaseConfig();
67
- const { anonKey } = await resolveSupabaseEndpoint(supabase);
68
- return buildClient(supabase, anonKey);
69
- }
70
- async function userClient() {
71
- const supabase = await requireSupabaseConfig();
72
- const token = await resolveAccessToken(config);
73
- return buildClient(supabase, token);
74
- }
75
- /**
76
- * Selects rows from `table` through the Supabase REST API
77
- * (`@supabase/supabase-js`'s `.from(table).select()`), as the **anonymous**
78
- * role — no `cfni_exec`, no raw SQL, only what PostgREST's REST API itself
79
- * supports: a column list, filters (equality or a named operator), one or
80
- * more `order by` clauses, `limit`/`range` pagination, `single`/`maybeSingle`,
81
- * and an optional row count.
82
- *
83
- * Reach for this — or {@link supabaseSelectAsUser} — instead of
84
- * `withPublicDb`/`withUserDb` when `db.supabase.rawSql` is `false`, or
85
- * whenever a query is simple enough that avoiding `cfni_exec` entirely is
86
- * preferable. Cross-table joins/aggregates spanning more than an embedded
87
- * resource (`columns: '*, related(*)'`) still need `withPublicDb`/
88
- * `withUserDb`, or a Postgres function called via {@link supabaseRpc}.
89
- *
90
- * @param table Table (or view) name.
91
- * @param options Column list, filters, ordering, pagination, count.
92
- * @returns The matching rows, and the total count when `options.count` is set.
93
- * @throws If `db`/`db.supabase` is not set, or PostgREST rejects the request.
94
- */
95
- export async function supabaseSelect(table, options = {}) {
96
- return runSelect(await anonClient(), table, options);
97
- }
98
- /**
99
- * Same as {@link supabaseSelect}, but authenticated as the **signed-in
100
- * user**: the bearer token comes from `db.getAccessToken`, or the signed-in
101
- * Firebase user's ID token when `firebaseAuth` is configured — see
102
- * {@link resolveAccessToken}. PostgREST resolves the caller as
103
- * `authenticated` and RLS applies exactly as it would for `withUserDb`.
104
- */
105
- export async function supabaseSelectAsUser(table, options = {}) {
106
- return runSelect(await userClient(), table, options);
107
- }
108
- async function runSelect(client, table, options) {
109
- let query = client.from(table).select(options.columns ?? '*', options.count ? { count: options.count } : undefined);
110
- query = applyFilters(query, options.where);
111
- query = applyExtras(query, options);
112
- query = applyOrder(query, options.orderBy);
113
- if (options.limit !== undefined)
114
- query = query.limit(options.limit);
115
- if (options.range)
116
- query = query.range(options.range[0], options.range[1]);
117
- if (options.single)
118
- query = query.single();
119
- else if (options.maybeSingle)
120
- query = query.maybeSingle();
121
- const { data, error, count } = (await query);
122
- if (error)
123
- throw new Error(describeQueryFailure(error));
124
- const rows = options.single || options.maybeSingle ? (data === null ? [] : [data]) : (data ?? []);
125
- return { rows, count };
126
- }
127
- /**
128
- * Inserts one or more rows into `table` through the Supabase REST API, as
129
- * the **anonymous** role. See {@link supabaseSelect} for when to use this
130
- * over `withPublicDb`.
131
- *
132
- * @param table Table name.
133
- * @param values A single row, or an array of rows, to insert.
134
- * @returns The inserted row(s) as returned by PostgREST.
135
- */
136
- export async function supabaseInsert(table, values) {
137
- return runInsert(await anonClient(), table, values);
138
- }
139
- /** Same as {@link supabaseInsert}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
140
- export async function supabaseInsertAsUser(table, values) {
141
- return runInsert(await userClient(), table, values);
142
- }
143
- async function runInsert(client, table, values) {
144
- const { data, error } = (await client.from(table).insert(values).select());
145
- if (error)
146
- throw new Error(describeQueryFailure(error));
147
- return data ?? [];
148
- }
149
- /**
150
- * Inserts one or more rows into `table`, or updates them on a conflict —
151
- * PostgREST's `upsert`, i.e. `insert ... on conflict (...) do update`. As
152
- * the **anonymous** role.
153
- *
154
- * @param table Table name.
155
- * @param values A single row, or an array of rows, to upsert.
156
- * @param options `onConflict` (defaults to the table's primary key) and
157
- * `ignoreDuplicates` (skip instead of update on conflict).
158
- * @returns The upserted row(s) as returned by PostgREST.
159
- */
160
- export async function supabaseUpsert(table, values, options = {}) {
161
- return runUpsert(await anonClient(), table, values, options);
162
- }
163
- /** Same as {@link supabaseUpsert}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
164
- export async function supabaseUpsertAsUser(table, values, options = {}) {
165
- return runUpsert(await userClient(), table, values, options);
166
- }
167
- async function runUpsert(client, table, values, options) {
168
- const { data, error } = (await client
169
- .from(table)
170
- .upsert(values, { onConflict: options.onConflict, ignoreDuplicates: options.ignoreDuplicates })
171
- .select());
172
- if (error)
173
- throw new Error(describeQueryFailure(error));
174
- return data ?? [];
175
- }
176
- /**
177
- * Updates rows in `table` matching `options.where` through the Supabase
178
- * REST API, as the **anonymous** role. See {@link supabaseSelect} for when
179
- * to use this over `withPublicDb`.
180
- *
181
- * @param table Table name.
182
- * @param values Columns to set.
183
- * @param options `where` — filters selecting which rows to update; required,
184
- * since an unfiltered update would touch every row.
185
- * @returns The updated rows as returned by PostgREST.
186
- */
187
- export async function supabaseUpdate(table, values, options) {
188
- return runUpdate(await anonClient(), table, values, options);
189
- }
190
- /** Same as {@link supabaseUpdate}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
191
- export async function supabaseUpdateAsUser(table, values, options) {
192
- return runUpdate(await userClient(), table, values, options);
193
- }
194
- async function runUpdate(client, table, values, options) {
195
- requireMutationFilter(options);
196
- let query = client.from(table).update(values);
197
- query = applyFilters(query, options.where);
198
- query = applyExtras(query, options);
199
- const { data, error } = (await query.select());
200
- if (error)
201
- throw new Error(describeQueryFailure(error));
202
- return data ?? [];
203
- }
204
- /**
205
- * Deletes rows in `table` matching `options.where` through the Supabase REST
206
- * API, as the **anonymous** role. See {@link supabaseSelect} for when to
207
- * use this over `withPublicDb`.
208
- *
209
- * @param table Table name.
210
- * @param options `where` — filters selecting which rows to delete; required,
211
- * since an unfiltered delete would remove every row.
212
- * @returns The deleted rows as returned by PostgREST.
213
- */
214
- export async function supabaseDelete(table, options) {
215
- return runDelete(await anonClient(), table, options);
216
- }
217
- /** Same as {@link supabaseDelete}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
218
- export async function supabaseDeleteAsUser(table, options) {
219
- return runDelete(await userClient(), table, options);
220
- }
221
- async function runDelete(client, table, options) {
222
- requireMutationFilter(options);
223
- let query = client.from(table).delete();
224
- query = applyFilters(query, options.where);
225
- query = applyExtras(query, options);
226
- const { data, error } = (await query.select());
227
- if (error)
228
- throw new Error(describeQueryFailure(error));
229
- return data ?? [];
230
- }
231
- /**
232
- * Calls a Postgres function through PostgREST's `.rpc()` — the same
233
- * mechanism `cfni_exec` itself uses, but for a function you define yourself.
234
- * This is how to run anything the `select`/`insert`/`upsert`/`update`/
235
- * `delete` helpers above can't express (joins, aggregates, custom logic)
236
- * without needing `cfni_exec`/raw SQL: write a regular Postgres function,
237
- * `grant execute` to `anon`/`authenticated`, and call it by name.
238
- *
239
- * As the **anonymous** role.
240
- *
241
- * @param fn Function name.
242
- * @param args Named arguments, matching the function's parameter names.
243
- */
244
- export async function supabaseRpc(fn, args) {
245
- return runRpc(await anonClient(), fn, args);
246
- }
247
- /** Same as {@link supabaseRpc}, authenticated as the signed-in user — see {@link supabaseSelectAsUser}. */
248
- export async function supabaseRpcAsUser(fn, args) {
249
- return runRpc(await userClient(), fn, args);
250
- }
251
- async function runRpc(client, fn, args) {
252
- const { data, error } = (await client.rpc(fn, args));
253
- if (error)
254
- throw new Error(describeQueryFailure(error));
255
- return data;
256
- }
257
- function requireMutationFilter(options) {
258
- const hasWhere = options.where && Object.keys(options.where).length > 0;
259
- if (!hasWhere && !options.match && !options.or) {
260
- throw new Error('db: one of `where`/`match`/`or` is required — an unfiltered update/delete would affect every row.');
261
- }
262
- }