cloudflare-next-intl 0.7.8 → 0.8.0
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 +141 -0
- package/bin/db_codegen.mjs +110 -0
- package/dist/src/db/access_token.d.ts +14 -0
- package/dist/src/db/access_token.js +30 -0
- package/dist/src/db/codegen_paths.d.ts +13 -0
- package/dist/src/db/codegen_paths.js +32 -0
- package/dist/src/db/connection.d.ts +42 -0
- package/dist/src/db/connection.js +178 -0
- package/dist/src/db/context.d.ts +57 -0
- package/dist/src/db/context.js +125 -0
- package/dist/src/db/helpers.d.ts +57 -0
- package/dist/src/db/helpers.js +112 -0
- package/dist/src/db/index.d.ts +24 -0
- package/dist/src/db/index.js +22 -0
- package/dist/src/db/require_config.d.ts +15 -0
- package/dist/src/db/require_config.js +20 -0
- package/dist/src/db/resolve_mode.d.ts +17 -0
- package/dist/src/db/resolve_mode.js +18 -0
- package/dist/src/db/supabase_config.d.ts +18 -0
- package/dist/src/db/supabase_config.js +22 -0
- package/dist/src/db/supabase_transport.d.ts +29 -0
- package/dist/src/db/supabase_transport.js +49 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/types/types.d.ts +91 -0
- package/llms.txt +35 -0
- package/package.json +22 -5
- package/supabase/cfni_exec.sql +34 -0
package/llms.txt
CHANGED
|
@@ -23,6 +23,8 @@ other subpath can be used.
|
|
|
23
23
|
- `./localeStaticParams` — `generateStaticParams` helper for locale segments.
|
|
24
24
|
- `./use` — `useLocale`/`useTranslations`; resolves to the RSC or client implementation automatically via the `react-server` export condition. Both throw `"... must be used within an IntlProvider"` if called outside one.
|
|
25
25
|
- `./ThemeSwitcher` — optional light/dark toggle component.
|
|
26
|
+
- `./db` — `withPublicDb(fn)` / `withUserDb(fn, uid?)` server-side Postgres/Drizzle context helpers (require `db` set on your `RoutingConfig`; direct Postgres or Supabase Data API, see below).
|
|
27
|
+
- `./dbHelpers` — generic Drizzle SQL helper functions (`excluded`, `onConflictSet`, `ago`, `currentDate`, `windowCount`, `unnestLateral`, `ascNullsLast`, `alwaysTrue`, `lateral`, `aliasColumn`, `minOf`, `maxOf`, `roundReal`, `multiply`, `scalarFromCte`) for use with `./db`.
|
|
26
28
|
|
|
27
29
|
## `firebaseAuth*` subpaths (require `firebaseAuth` set on your `RoutingConfig`)
|
|
28
30
|
|
|
@@ -44,6 +46,38 @@ other subpath can be used.
|
|
|
44
46
|
- `./cookieConsentAnalytics` — `CookieConsentAnalytics`: gates Cloudflare Web Analytics / Google Ads / Google Analytics / AdSense / Microsoft Clarity behind consent; rendered automatically by `IntlProvider` when `cookieConsent.secrets` or `getSecrets` is set (and `autoWireAnalytics !== false`). Never renders in local dev (`NODE_ENV === 'development'`) unless `cookieConsent.enableAnalyticsInDevMode` is `true`.
|
|
45
47
|
- Country-based gating (`cookieConsent.getCountryCode` / `getCloudflareContext` + `gdprCountries`): resolved server-side by `IntlProvider` into a `requiresConsent` boolean passed to `CookieConsentProvider`. Neither getter set → gating off, consent always implicitly granted. `getCountryCode` (direct country resolver) takes precedence over `getCloudflareContext` (reads `cf.country`) when both are set. `getCloudflareContext` accepts `@opennextjs/cloudflare`'s `getCloudflareContext` function directly (its exact overloaded signature — `CookieConsentGetCloudflareContext`), called internally with `{ async: true }`. Unresolved country (or a `null` context) always requires consent (fail-safe). `./cookieConsent` also exports `defaultGdprCountries` (EU/EEA + UK + Switzerland).
|
|
46
48
|
|
|
49
|
+
## `db*` subpaths (require `db` set on your `RoutingConfig`)
|
|
50
|
+
|
|
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
|
+
|
|
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? }`, 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
|
+
- `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
|
+
- `db.authenticatedRole` — direct-Postgres mode only: Postgres role assumed inside `withUserDb`'s transaction. Defaults to `'authenticated'` (Supabase RLS convention).
|
|
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()`.
|
|
58
|
+
- `db.getAccessToken` — Supabase mode only: resolves the JWT sent as `Authorization: Bearer` in `withUserDb`, which is what makes PostgREST resolve `authenticated` and apply RLS. Omit when `firebaseAuth` is configured — the signed-in user's Firebase ID token is used automatically.
|
|
59
|
+
- `db.disconnectTimeoutMs` — direct-Postgres mode only: ms `disconnectPostgres` waits for `client.end()` before giving up. Defaults to `2000`.
|
|
60
|
+
- `withPublicDb(fn)` — anonymous role. Direct-Postgres mode: the request's pooled connection, no transaction, no role switch. Supabase mode: the anon key as the PostgREST bearer token. Either way, no user id is attached — RLS keyed on `auth.jwt()` denies access.
|
|
61
|
+
- `withUserDb(fn, uid?)` — signed-in-user role. Direct-Postgres mode: a transaction with `set_config('request.jwt.claims', ...)` + `set local role`, `uid` resolution order explicit arg → `db.getUserId()` → Firebase auth uid → throws. Supabase mode: identity rides on the JWT from `db.getAccessToken`/Firebase instead (`uid` param is ignored), no transaction wraps the call.
|
|
62
|
+
- `./dbHelpers` functions are plain Drizzle `sql`-building utilities with no config dependency — usable standalone.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// src/i18n/intl_config.ts
|
|
66
|
+
import { getCloudflareContext } from "@opennextjs/cloudflare";
|
|
67
|
+
|
|
68
|
+
export default setIntlConfig({
|
|
69
|
+
locales: ["en", "uk"] as const,
|
|
70
|
+
defaultLocale: "en",
|
|
71
|
+
generate: { getCloudflareContext },
|
|
72
|
+
db: { hyperdriveBinding: "HYPERDRIVE" },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// anywhere on the server
|
|
76
|
+
import { withPublicDb } from "cloudflare-next-intl/db";
|
|
77
|
+
|
|
78
|
+
const rows = await withPublicDb((db) => db.select().from(bonds).limit(10));
|
|
79
|
+
```
|
|
80
|
+
|
|
47
81
|
## Conventions
|
|
48
82
|
|
|
49
83
|
- Every exported function/component has a JSDoc comment with an `@example` where usage isn't obvious from the signature alone.
|
|
@@ -56,3 +90,4 @@ other subpath can be used.
|
|
|
56
90
|
- Missing `firebaseAuth` on `RoutingConfig` throws immediately from any `firebaseAuth*` export, naming the missing config field.
|
|
57
91
|
- `useLocale`/`useTranslations` (from `./use`) and `useAuthUser` (from `./useFirebaseAuthUser`) resolve to a Server- or Client-Component implementation automatically via the `react-server` export condition — always import from the subpath, never from an internal file path.
|
|
58
92
|
- No unified `useFirebaseAuthUser` re-export inside `cloudflare-next-intl/firebaseAuth`'s barrel — that barrel can't replicate the `react-server` condition split, so import `cloudflare-next-intl/useFirebaseAuthUser` directly for the hook.
|
|
93
|
+
- Missing `db` on `RoutingConfig` throws immediately from `withPublicDb`/`withUserDb`, naming the missing config field. `pg`, `drizzle-orm`, and `@supabase/supabase-js` ship as dependencies and load via dynamic `import()` — nothing to install yourself, and nothing bundles unless a `db` export is called.
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-next-intl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Optimized Next Intl Package Special for App Router and Cloudflare",
|
|
5
|
-
|
|
6
5
|
"main": "dist/index.js",
|
|
7
6
|
"types": "dist/index.d.ts",
|
|
8
7
|
"sideEffects": false,
|
|
8
|
+
"bin": {
|
|
9
|
+
"cfni-db-codegen": "bin/db_codegen.mjs"
|
|
10
|
+
},
|
|
9
11
|
"files": [
|
|
10
12
|
"dist",
|
|
13
|
+
"bin",
|
|
14
|
+
"supabase",
|
|
11
15
|
"LICENSE",
|
|
12
16
|
"README.md",
|
|
13
17
|
"llms.txt"
|
|
@@ -172,6 +176,14 @@
|
|
|
172
176
|
"./createServerErrorAction": {
|
|
173
177
|
"types": "./dist/src/error_handling/create_server_error_action.d.ts",
|
|
174
178
|
"import": "./dist/src/error_handling/create_server_error_action.js"
|
|
179
|
+
},
|
|
180
|
+
"./db": {
|
|
181
|
+
"types": "./dist/src/db/index.d.ts",
|
|
182
|
+
"import": "./dist/src/db/index.js"
|
|
183
|
+
},
|
|
184
|
+
"./dbHelpers": {
|
|
185
|
+
"types": "./dist/src/db/helpers.d.ts",
|
|
186
|
+
"import": "./dist/src/db/helpers.js"
|
|
175
187
|
}
|
|
176
188
|
},
|
|
177
189
|
"scripts": {
|
|
@@ -216,8 +228,11 @@
|
|
|
216
228
|
"homepage": "https://github.com/demian-ilnytskyi/cloudflare-next-intl#readme",
|
|
217
229
|
"dependencies": {
|
|
218
230
|
"@microsoft/clarity": "^1.0.2",
|
|
231
|
+
"@supabase/supabase-js": "^2.112.3",
|
|
232
|
+
"drizzle-orm": "^0.45.2",
|
|
219
233
|
"firebase": "^12.17.0",
|
|
220
|
-
"jose": "^6.2.8"
|
|
234
|
+
"jose": "^6.2.8",
|
|
235
|
+
"pg": "^8.23.0"
|
|
221
236
|
},
|
|
222
237
|
"peerDependencies": {
|
|
223
238
|
"next": ">=12.0.0",
|
|
@@ -236,16 +251,18 @@
|
|
|
236
251
|
"@testing-library/jest-dom": "^7.0.0",
|
|
237
252
|
"@testing-library/react": "^16.3.2",
|
|
238
253
|
"@types/node": "^20.14.5",
|
|
254
|
+
"@types/pg": "^8.23.1",
|
|
239
255
|
"@types/react": "^19.0.0",
|
|
240
256
|
"@types/react-dom": "^19.0.0",
|
|
241
257
|
"@vitest/coverage-v8": "^3.2.7",
|
|
258
|
+
"drizzle-kit": "^0.31.10",
|
|
242
259
|
"eslint": "^9.28.0",
|
|
243
260
|
"eslint-config-next": "15.3.3",
|
|
244
261
|
"eslint-config-prettier": "^10.1.2",
|
|
245
262
|
"jsdom": "^29.1.1",
|
|
246
263
|
"next": "^15.3.0",
|
|
247
|
-
"react": "
|
|
248
|
-
"react-dom": "
|
|
264
|
+
"react": "19.2.0",
|
|
265
|
+
"react-dom": "19.2.0",
|
|
249
266
|
"tsup": "^8.5.1",
|
|
250
267
|
"typescript": "^5.5.3",
|
|
251
268
|
"typescript-eslint": "^8.33.1",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
-- Executes a statement generated by cloudflare-next-intl's `db` module and
|
|
2
|
+
-- returns its rows as positional JSON arrays, which is the shape
|
|
3
|
+
-- drizzle-orm/pg-proxy maps result columns from.
|
|
4
|
+
--
|
|
5
|
+
-- SECURITY INVOKER is load-bearing: the statement runs with the privileges of
|
|
6
|
+
-- the caller (anon or authenticated, per the request's JWT), so row-level
|
|
7
|
+
-- security still applies exactly as it does over the REST API. Do not change
|
|
8
|
+
-- this to SECURITY DEFINER, and do not add a role parameter — either would
|
|
9
|
+
-- turn this into a privilege-escalation primitive.
|
|
10
|
+
create or replace function public.cfni_exec(statement text, params jsonb default '[]'::jsonb)
|
|
11
|
+
returns json
|
|
12
|
+
language plpgsql
|
|
13
|
+
security invoker
|
|
14
|
+
as $$
|
|
15
|
+
declare
|
|
16
|
+
result json;
|
|
17
|
+
args text[];
|
|
18
|
+
begin
|
|
19
|
+
select coalesce(array_agg(value #>> '{}' order by ordinality), '{}')
|
|
20
|
+
into args
|
|
21
|
+
from jsonb_array_elements(params) with ordinality as t(value, ordinality);
|
|
22
|
+
|
|
23
|
+
execute format('select coalesce(json_agg(json_build_array(r.*)), ''[]''::json) from (%s) r', statement)
|
|
24
|
+
into result
|
|
25
|
+
using args;
|
|
26
|
+
|
|
27
|
+
return result;
|
|
28
|
+
end;
|
|
29
|
+
$$;
|
|
30
|
+
|
|
31
|
+
revoke all on function public.cfni_exec(text, jsonb) from public;
|
|
32
|
+
grant execute on function public.cfni_exec(text, jsonb) to authenticated;
|
|
33
|
+
-- Grant to anon only if your app calls withPublicDb:
|
|
34
|
+
grant execute on function public.cfni_exec(text, jsonb) to anon;
|