cloudflare-next-intl 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -7
- package/dist/src/db/connection.js +4 -2
- package/dist/src/db/context.js +1 -1
- package/dist/src/db/resolve_config_value.d.ts +9 -0
- package/dist/src/db/resolve_config_value.js +10 -0
- package/dist/src/db/supabase_config.d.ts +2 -1
- package/dist/src/db/supabase_config.js +5 -3
- package/dist/src/db/supabase_transport.js +1 -1
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/types.d.ts +16 -6
- package/llms.txt +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -411,10 +411,12 @@ export default setIntlConfig({
|
|
|
411
411
|
|
|
412
412
|
`db` fields (all optional):
|
|
413
413
|
|
|
414
|
-
- `connectionString` — a Postgres connection string
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
414
|
+
- `connectionString` — a Postgres connection string, or a sync/async function
|
|
415
|
+
returning one (resolved on each connect, so the value can come from a secret
|
|
416
|
+
store). Omit to resolve it from the Hyperdrive binding named by
|
|
417
|
+
`hyperdriveBinding` instead (the normal production setup); a value here
|
|
418
|
+
always wins over the binding, which is what makes local dev / build-time
|
|
419
|
+
evaluation work.
|
|
418
420
|
- `hyperdriveBinding` — name of the Hyperdrive binding on `env` to read a
|
|
419
421
|
connection string from when `connectionString` is not set. Defaults to
|
|
420
422
|
`'HYPERDRIVE'`. Requires `generate.getCloudflareContext` to be configured.
|
|
@@ -472,11 +474,35 @@ bundles any of them.
|
|
|
472
474
|
|
|
473
475
|
`db.supabase` fields (all optional):
|
|
474
476
|
|
|
475
|
-
- `url` — project URL. Defaults to
|
|
476
|
-
|
|
477
|
-
|
|
477
|
+
- `url` — project URL, or a sync/async function returning one. Defaults to
|
|
478
|
+
`NEXT_PUBLIC_SUPABASE_URL`.
|
|
479
|
+
- `anonKey` — anon/publishable key, or a sync/async function returning one.
|
|
480
|
+
Defaults to `NEXT_PUBLIC_SUPABASE_ANON_KEY`. Never put a service-role key
|
|
481
|
+
here.
|
|
478
482
|
- `execFunction` — name of the exec function. Defaults to `'cfni_exec'`.
|
|
479
483
|
|
|
484
|
+
Any of `connectionString`, `supabase.url`, and `supabase.anonKey` may be a
|
|
485
|
+
function instead of a string, resolved (and awaited) at use time rather than
|
|
486
|
+
when the config object is built — useful when the value lives in a secret
|
|
487
|
+
store or a Cloudflare binding rather than an environment variable:
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
export default setIntlConfig({
|
|
491
|
+
locales: ["en", "uk"] as const,
|
|
492
|
+
defaultLocale: "en",
|
|
493
|
+
generate: { getCloudflareContext },
|
|
494
|
+
db: {
|
|
495
|
+
supabase: {
|
|
496
|
+
url: "https://abc.supabase.co",
|
|
497
|
+
anonKey: async () => {
|
|
498
|
+
const { env } = await getCloudflareContext({ async: true });
|
|
499
|
+
return env.SUPABASE_ANON_KEY.get();
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
480
506
|
`db.getAccessToken` resolves the JWT `withUserDb` sends as
|
|
481
507
|
`Authorization: Bearer`, which is what makes PostgREST resolve the caller as
|
|
482
508
|
`authenticated`. Omit it when `firebaseAuth` is configured — the signed-in
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import reportError from '../error_handling/report_error';
|
|
2
2
|
import requireDbConfig from './require_config';
|
|
3
|
+
import resolveConfigValue from './resolve_config_value';
|
|
3
4
|
const DEFAULT_BINDING = 'HYPERDRIVE';
|
|
4
5
|
const DEFAULT_DISCONNECT_TIMEOUT_MS = 2000;
|
|
5
6
|
let connectionString = null;
|
|
@@ -30,8 +31,9 @@ export function resetConnectionState() {
|
|
|
30
31
|
* Cloudflare Hyperdrive binding named by `db.hyperdriveBinding`.
|
|
31
32
|
*/
|
|
32
33
|
async function resolveConnectionString(config, db) {
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const configured = await resolveConfigValue(db.connectionString);
|
|
35
|
+
if (configured)
|
|
36
|
+
return configured;
|
|
35
37
|
const getContext = config.generate?.getCloudflareContext;
|
|
36
38
|
if (getContext) {
|
|
37
39
|
const context = await getContext({ async: true });
|
package/dist/src/db/context.js
CHANGED
|
@@ -60,7 +60,7 @@ export async function withPublicDb(fn) {
|
|
|
60
60
|
requireDbConfig(db);
|
|
61
61
|
if (resolveDbMode(db) === 'supabase') {
|
|
62
62
|
const supabase = db.supabase;
|
|
63
|
-
const { anonKey } = resolveSupabaseEndpoint(supabase);
|
|
63
|
+
const { anonKey } = await resolveSupabaseEndpoint(supabase);
|
|
64
64
|
return fn(await supabaseDb(supabase, anonKey));
|
|
65
65
|
}
|
|
66
66
|
const client = await connectToPostgres(config);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConfigValue } from '../types/types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a config value that may have been supplied directly or as a
|
|
4
|
+
* sync/async function.
|
|
5
|
+
*
|
|
6
|
+
* @param value The configured value or resolver.
|
|
7
|
+
* @returns The resolved value, or `undefined` when nothing was configured.
|
|
8
|
+
*/
|
|
9
|
+
export default function resolveConfigValue<T>(value: ConfigValue<T | undefined> | undefined): Promise<T | undefined>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a config value that may have been supplied directly or as a
|
|
3
|
+
* sync/async function.
|
|
4
|
+
*
|
|
5
|
+
* @param value The configured value or resolver.
|
|
6
|
+
* @returns The resolved value, or `undefined` when nothing was configured.
|
|
7
|
+
*/
|
|
8
|
+
export default async function resolveConfigValue(value) {
|
|
9
|
+
return typeof value === 'function' ? value() : value;
|
|
10
|
+
}
|
|
@@ -13,6 +13,7 @@ export interface ResolvedSupabaseEndpoint {
|
|
|
13
13
|
*
|
|
14
14
|
* @param supabase The `db.supabase` config block.
|
|
15
15
|
* @returns The project URL and anon key to build a Supabase client from.
|
|
16
|
+
* Values given as functions are resolved here.
|
|
16
17
|
* @throws If neither config nor environment supplies a URL or an anon key.
|
|
17
18
|
*/
|
|
18
|
-
export default function resolveSupabaseEndpoint(supabase: SupabaseDbConfig): ResolvedSupabaseEndpoint
|
|
19
|
+
export default function resolveSupabaseEndpoint(supabase: SupabaseDbConfig): Promise<ResolvedSupabaseEndpoint>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import resolveConfigValue from './resolve_config_value';
|
|
1
2
|
/**
|
|
2
3
|
* Resolves the Supabase project URL and anon key, preferring explicit config
|
|
3
4
|
* over the `NEXT_PUBLIC_SUPABASE_URL`/`NEXT_PUBLIC_SUPABASE_ANON_KEY`
|
|
@@ -5,15 +6,16 @@
|
|
|
5
6
|
*
|
|
6
7
|
* @param supabase The `db.supabase` config block.
|
|
7
8
|
* @returns The project URL and anon key to build a Supabase client from.
|
|
9
|
+
* Values given as functions are resolved here.
|
|
8
10
|
* @throws If neither config nor environment supplies a URL or an anon key.
|
|
9
11
|
*/
|
|
10
|
-
export default function resolveSupabaseEndpoint(supabase) {
|
|
11
|
-
const url = supabase.url ?? process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
12
|
+
export default async function resolveSupabaseEndpoint(supabase) {
|
|
13
|
+
const url = (await resolveConfigValue(supabase.url)) ?? process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
12
14
|
if (!url) {
|
|
13
15
|
throw new Error('db: could not resolve a Supabase project URL. Set `db.supabase.url` ' +
|
|
14
16
|
'or the NEXT_PUBLIC_SUPABASE_URL environment variable.');
|
|
15
17
|
}
|
|
16
|
-
const anonKey = supabase.anonKey ?? process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
18
|
+
const anonKey = (await resolveConfigValue(supabase.anonKey)) ?? process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
17
19
|
if (!anonKey) {
|
|
18
20
|
throw new Error('db: could not resolve a Supabase anon key. Set `db.supabase.anonKey` ' +
|
|
19
21
|
'or the NEXT_PUBLIC_SUPABASE_ANON_KEY environment variable.');
|
|
@@ -21,11 +21,11 @@ const DEFAULT_EXEC_FUNCTION = 'cfni_exec';
|
|
|
21
21
|
* @returns A callback suitable for `drizzle-orm/pg-proxy`'s `drizzle()`.
|
|
22
22
|
*/
|
|
23
23
|
export default function createSupabaseTransport(supabase, bearerToken) {
|
|
24
|
-
const { url, anonKey } = resolveSupabaseEndpoint(supabase);
|
|
25
24
|
const execFunction = supabase.execFunction ?? DEFAULT_EXEC_FUNCTION;
|
|
26
25
|
let clientPromise = null;
|
|
27
26
|
async function getClient() {
|
|
28
27
|
clientPromise ?? (clientPromise = (async () => {
|
|
28
|
+
const { url, anonKey } = await resolveSupabaseEndpoint(supabase);
|
|
29
29
|
const { createClient } = await import('@supabase/supabase-js');
|
|
30
30
|
return createClient(url, anonKey, { accessToken: async () => bearerToken });
|
|
31
31
|
})());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig, CookieConsentAnalyticsConfig, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, DbRoutingConfig, SupabaseDbConfig, } from './types';
|
|
1
|
+
export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig, CookieConsentAnalyticsConfig, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, DbRoutingConfig, SupabaseDbConfig, ConfigValue, } from './types';
|
|
@@ -817,18 +817,27 @@ export interface IntlSitemap {
|
|
|
817
817
|
lastModified: Date | string | undefined;
|
|
818
818
|
videos?: Videos[] | undefined;
|
|
819
819
|
}
|
|
820
|
+
/**
|
|
821
|
+
* A config value that may be given directly, or as a sync/async function
|
|
822
|
+
* resolved at use time. The function form lets a value come from a secret
|
|
823
|
+
* store, a Cloudflare binding, or any other source that isn't available when
|
|
824
|
+
* the config object is first created.
|
|
825
|
+
*/
|
|
826
|
+
export type ConfigValue<T> = T | (() => T | Promise<T>);
|
|
820
827
|
export interface SupabaseDbConfig {
|
|
821
828
|
/**
|
|
822
829
|
* Supabase project URL, e.g. `https://abc.supabase.co`. Defaults to
|
|
823
|
-
* `process.env.NEXT_PUBLIC_SUPABASE_URL`.
|
|
830
|
+
* `process.env.NEXT_PUBLIC_SUPABASE_URL`. May be a function (sync or
|
|
831
|
+
* async) resolved on each use.
|
|
824
832
|
*/
|
|
825
|
-
url?: string
|
|
833
|
+
url?: ConfigValue<string | undefined>;
|
|
826
834
|
/**
|
|
827
835
|
* Supabase anon (publishable) key. Defaults to
|
|
828
836
|
* `process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY`. This is the only key the
|
|
829
|
-
* `db` module ever needs — never put a service-role key here.
|
|
837
|
+
* `db` module ever needs — never put a service-role key here. May be a
|
|
838
|
+
* function (sync or async) resolved on each use.
|
|
830
839
|
*/
|
|
831
|
-
anonKey?: string
|
|
840
|
+
anonKey?: ConfigValue<string | undefined>;
|
|
832
841
|
/**
|
|
833
842
|
* Name of the Postgres function that runs the generated SQL. Defaults to
|
|
834
843
|
* `'cfni_exec'` — the function shipped in `supabase/cfni_exec.sql`.
|
|
@@ -840,9 +849,10 @@ export interface DbRoutingConfig {
|
|
|
840
849
|
* Postgres connection string. Omit to resolve it from the Cloudflare
|
|
841
850
|
* Hyperdrive binding named by `hyperdriveBinding` instead (the normal
|
|
842
851
|
* production setup); a value here always wins over the binding, which is
|
|
843
|
-
* what makes local dev / build-time evaluation work.
|
|
852
|
+
* what makes local dev / build-time evaluation work. May be a function
|
|
853
|
+
* (sync or async) resolved on each connect.
|
|
844
854
|
*/
|
|
845
|
-
connectionString?: string
|
|
855
|
+
connectionString?: ConfigValue<string | undefined>;
|
|
846
856
|
/**
|
|
847
857
|
* Name of the Hyperdrive binding on `env` whose `connectionString` is used
|
|
848
858
|
* when `connectionString` is not set. Defaults to `'HYPERDRIVE'`. Requires
|
package/llms.txt
CHANGED
|
@@ -50,8 +50,8 @@ other subpath can be used.
|
|
|
50
50
|
|
|
51
51
|
Two transports, picked by which `db` fields are set — `pg`/`drizzle-orm`/`@supabase/supabase-js` ship as dependencies and load via dynamic `import()`, so nothing bundles unless a `db` export is called.
|
|
52
52
|
|
|
53
|
-
- Direct Postgres (wins if configured): `db.connectionString` — Postgres connection string; when omitted, resolved from the Hyperdrive binding named by `db.hyperdriveBinding` (default `'HYPERDRIVE'`) via `generate.getCloudflareContext`. An explicit `connectionString` always wins (enables local dev / build-time evaluation).
|
|
54
|
-
- Supabase Data API (used only when neither of the above is set): `db.supabase` — `{ url?, anonKey?, execFunction? }
|
|
53
|
+
- Direct Postgres (wins if configured): `db.connectionString` — Postgres connection string or a sync/async function returning one (resolved on each connect); when omitted, resolved from the Hyperdrive binding named by `db.hyperdriveBinding` (default `'HYPERDRIVE'`) via `generate.getCloudflareContext`. An explicit `connectionString` always wins (enables local dev / build-time evaluation).
|
|
54
|
+
- Supabase Data API (used only when neither of the above is set): `db.supabase` — `{ url?, anonKey?, execFunction? }` where `url`/`anonKey` each accept a string or a sync/async function returning one, defaulting `url`/`anonKey` to `NEXT_PUBLIC_SUPABASE_URL`/`NEXT_PUBLIC_SUPABASE_ANON_KEY`. Requires installing `supabase/cfni_exec.sql` (a `security invoker` SQL-exec function) in your database. No multi-statement transactions — each statement in a `withUserDb` callback is its own round-trip.
|
|
55
55
|
- `db.disconnectAfterRequest` — direct-Postgres mode only: closes the pooled client once the last in-flight `withPublicDb`/`withUserDb` call of the request finishes. Defaults to `true`; set `false` to keep the connection open for the isolate's lifetime (holds a Hyperdrive slot between requests).
|
|
56
56
|
- `db.authenticatedRole` — direct-Postgres mode only: Postgres role assumed inside `withUserDb`'s transaction. Defaults to `'authenticated'` (Supabase RLS convention).
|
|
57
57
|
- `db.getUserId` — direct-Postgres mode only: resolves the user id injected as `request.jwt.claims->>'sub'` in `withUserDb`. Omit when `firebaseAuth` is configured — the uid is then taken automatically from the signed-in Firebase user via this package's own `getAuthUser()`.
|