cloudflare-next-intl 0.9.35 → 0.9.37
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 +32 -0
- package/dist/src/db/context.d.ts +7 -1
- package/dist/src/db/context.js +44 -6
- package/dist/src/db/index.d.ts +2 -1
- package/dist/src/db/index.js +1 -1
- package/dist/src/error_handling/create_server_error_action.js +2 -29
- package/dist/src/error_handling/error_handling_action_config.d.ts +3 -0
- package/dist/src/error_handling/error_handling_action_config.js +7 -0
- package/dist/src/error_handling/index.d.ts +1 -0
- package/dist/src/error_handling/index.js +1 -0
- package/dist/src/error_handling/report_client_error_action.d.ts +2 -0
- package/dist/src/error_handling/report_client_error_action.js +6 -0
- package/dist/src/error_handling/report_client_error_core.d.ts +3 -0
- package/dist/src/error_handling/report_client_error_core.js +31 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -754,6 +754,38 @@ query code is identical either way — switching is a config change only.
|
|
|
754
754
|
A direct connection always wins if both are configured, so adding a `supabase`
|
|
755
755
|
block cannot silently reroute live traffic.
|
|
756
756
|
|
|
757
|
+
#### Reading as the user from a cached function
|
|
758
|
+
|
|
759
|
+
`withUserDb` normally resolves the caller from the request — cookies, the
|
|
760
|
+
Firebase session — every time it runs. Next forbids that inside a function
|
|
761
|
+
wrapped in `unstable_cache`, and the background revalidation it runs for one
|
|
762
|
+
happens after the response is gone, so both fail with ``` `cookies()` cannot be
|
|
763
|
+
called inside a function cached with `unstable_cache()` ```.
|
|
764
|
+
|
|
765
|
+
Resolve the caller once, while the request still exists, and pass the plain
|
|
766
|
+
values in:
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
import { resolveUserDbCredentials, withUserDb } from "cloudflare-next-intl/db";
|
|
770
|
+
import { unstable_cache } from "next/cache";
|
|
771
|
+
|
|
772
|
+
const credentials = await resolveUserDbCredentials();
|
|
773
|
+
if (credentials.uid === null) return null;
|
|
774
|
+
|
|
775
|
+
const row = await unstable_cache(
|
|
776
|
+
() => withUserDb((db) => db.select().from(profiles).where(eq(profiles.id, credentials.uid!)), credentials),
|
|
777
|
+
["profile-row", credentials.uid],
|
|
778
|
+
{ tags: [`profile:${credentials.uid}`], revalidate: 60 },
|
|
779
|
+
)();
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
`resolveUserDbCredentials()` returns `{ uid, accessToken, role }`, each `null`
|
|
783
|
+
when it cannot be resolved — a signed-out visitor gets nulls rather than a
|
|
784
|
+
throw, so the check above is yours to make. Passing the object to `withUserDb`
|
|
785
|
+
in place of a bare `uid` makes it use those values and read nothing
|
|
786
|
+
request-scoped; it throws naming the missing field if the one that mode needs
|
|
787
|
+
is `null`.
|
|
788
|
+
|
|
757
789
|
#### Standalone usage (no Next.js / `@intl-config`)
|
|
758
790
|
|
|
759
791
|
`withPublicDb`/`withUserDb` normally read their `db` config from
|
package/dist/src/db/context.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
2
2
|
import type { DbRoutingConfig } from '../types/types.js';
|
|
3
3
|
export type DrizzleDb = NodePgDatabase<Record<string, never>>;
|
|
4
|
+
export interface UserDbCredentials {
|
|
5
|
+
uid: string | null;
|
|
6
|
+
accessToken: string | null;
|
|
7
|
+
role: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function resolveUserDbCredentials(dbOverride?: DbRoutingConfig): Promise<UserDbCredentials>;
|
|
4
10
|
export declare function withPublicDb<T>(fn: (db: DrizzleDb) => Promise<T>, dbOverride?: DbRoutingConfig): Promise<T>;
|
|
5
|
-
export declare function withUserDb<T>(fn: (db: DrizzleDb) => Promise<T>,
|
|
11
|
+
export declare function withUserDb<T>(fn: (db: DrizzleDb) => Promise<T>, auth?: string | null | UserDbCredentials, dbOverride?: DbRoutingConfig): Promise<T>;
|
|
6
12
|
export type { ExecResult as TransactionResult } from './supabase_transport.js';
|
package/dist/src/db/context.js
CHANGED
|
@@ -8,6 +8,38 @@ import resolveAccessToken from './access_token.js';
|
|
|
8
8
|
import runTransactionBatch from './transaction_batch.js';
|
|
9
9
|
import inlineParams from './inline_params.js';
|
|
10
10
|
const DEFAULT_ROLE = 'authenticated';
|
|
11
|
+
function isCredentials(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null;
|
|
13
|
+
}
|
|
14
|
+
function throwMissingCredential(what) {
|
|
15
|
+
throw new Error(`db: withUserDb was given credentials without ${what}. resolveUserDbCredentials() ` +
|
|
16
|
+
'returns nulls when nobody is signed in — check for that before calling withUserDb.');
|
|
17
|
+
}
|
|
18
|
+
export async function resolveUserDbCredentials(dbOverride) {
|
|
19
|
+
const config = await resolveDbConfig(dbOverride);
|
|
20
|
+
const db = config.db;
|
|
21
|
+
requireDbConfig(db);
|
|
22
|
+
const fromConfigUid = (await db.getUserId?.()) ?? null;
|
|
23
|
+
const fromConfigToken = (await db.getAccessToken?.()) ?? null;
|
|
24
|
+
let uid = fromConfigUid;
|
|
25
|
+
let accessToken = fromConfigToken;
|
|
26
|
+
let role = null;
|
|
27
|
+
if (config.firebaseAuth && (uid === null || accessToken === null || db.authenticatedRoleClaim !== false)) {
|
|
28
|
+
const { getAuthUser } = await import('../firebase_auth/server/use_auth_user_server.js');
|
|
29
|
+
const { user } = await getAuthUser();
|
|
30
|
+
if (user) {
|
|
31
|
+
uid ?? (uid = user.uid ?? null);
|
|
32
|
+
accessToken ?? (accessToken = (await user.getIdToken(false)) ?? null);
|
|
33
|
+
if (db.authenticatedRoleClaim !== false && typeof user.getIdTokenResult === 'function') {
|
|
34
|
+
const { claims } = await user.getIdTokenResult();
|
|
35
|
+
const claimValue = claims[db.authenticatedRoleClaim ?? 'role'];
|
|
36
|
+
if (typeof claimValue === 'string' && claimValue)
|
|
37
|
+
role = claimValue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { uid, accessToken, role };
|
|
42
|
+
}
|
|
11
43
|
async function resolveUserId(config, uid) {
|
|
12
44
|
if (uid)
|
|
13
45
|
return uid;
|
|
@@ -25,9 +57,11 @@ async function resolveUserId(config, uid) {
|
|
|
25
57
|
throw new Error('db: withUserDb could not resolve a user id. Pass one explicitly, set ' +
|
|
26
58
|
'`db.getUserId`, or configure `firebaseAuth` so the signed-in Firebase uid is used.');
|
|
27
59
|
}
|
|
28
|
-
async function resolveAuthenticatedRole(config, db) {
|
|
60
|
+
async function resolveAuthenticatedRole(config, db, claimed) {
|
|
61
|
+
if (claimed)
|
|
62
|
+
return claimed;
|
|
29
63
|
const claimField = db.authenticatedRoleClaim;
|
|
30
|
-
if (config.firebaseAuth && claimField !== false) {
|
|
64
|
+
if (config.firebaseAuth && claimField !== false && claimed === undefined) {
|
|
31
65
|
const { getAuthUser } = await import('../firebase_auth/server/use_auth_user_server.js');
|
|
32
66
|
const { user } = await getAuthUser();
|
|
33
67
|
if (user && typeof user.getIdTokenResult === 'function') {
|
|
@@ -127,17 +161,21 @@ function injectUidComment(sql, userId) {
|
|
|
127
161
|
}
|
|
128
162
|
return sql;
|
|
129
163
|
}
|
|
130
|
-
export async function withUserDb(fn,
|
|
164
|
+
export async function withUserDb(fn, auth, dbOverride) {
|
|
131
165
|
const config = await resolveDbConfig(dbOverride);
|
|
132
166
|
const db = config.db;
|
|
133
167
|
requireDbConfig(db);
|
|
168
|
+
const credentials = isCredentials(auth) ? auth : null;
|
|
169
|
+
const uid = credentials ? credentials.uid : auth ?? null;
|
|
134
170
|
const resolved = await resolveDbMode(db, config.generate);
|
|
135
171
|
if (resolved.mode === 'supabase') {
|
|
136
|
-
const token =
|
|
172
|
+
const token = credentials
|
|
173
|
+
? credentials.accessToken ?? throwMissingCredential('an access token')
|
|
174
|
+
: await resolveAccessToken(config);
|
|
137
175
|
return fn(await supabaseDb(resolved.supabase, token));
|
|
138
176
|
}
|
|
139
|
-
const userId = await resolveUserId(config, uid);
|
|
140
|
-
const role = await resolveAuthenticatedRole(config, db);
|
|
177
|
+
const userId = credentials ? uid ?? throwMissingCredential('a user id') : await resolveUserId(config, uid);
|
|
178
|
+
const role = await resolveAuthenticatedRole(config, db, credentials ? credentials.role : undefined);
|
|
141
179
|
return await withDbClient(config, async (client) => {
|
|
142
180
|
const rawClient = client;
|
|
143
181
|
const setSessionState = async () => {
|
package/dist/src/db/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { withPublicDb, withUserDb } from './context.js';
|
|
1
|
+
export { withPublicDb, withUserDb, resolveUserDbCredentials } from './context.js';
|
|
2
|
+
export type { UserDbCredentials } from './context.js';
|
|
2
3
|
export type { DrizzleDb, TransactionResult } from './context.js';
|
|
3
4
|
export { withDbClient, connectToPostgres, disconnectPostgres, resetConnectionState } from './connection.js';
|
|
4
5
|
export type { DbRoutingConfig } from '../types/types.js';
|
package/dist/src/db/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { withPublicDb, withUserDb } from './context.js';
|
|
1
|
+
export { withPublicDb, withUserDb, resolveUserDbCredentials } from './context.js';
|
|
2
2
|
export { withDbClient, connectToPostgres, disconnectPostgres, resetConnectionState } from './connection.js';
|
|
@@ -1,33 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import stringifyUnknown from './stringify_unknown.js';
|
|
3
|
-
async function resolveRequestContext() {
|
|
4
|
-
try {
|
|
5
|
-
const { headers } = await import('next/headers.js');
|
|
6
|
-
const headerList = await headers();
|
|
7
|
-
return {
|
|
8
|
-
path: headerList.get('x-pathname') ?? undefined,
|
|
9
|
-
userAgent: headerList.get('user-agent') ?? undefined,
|
|
10
|
-
referer: headerList.get('referer') ?? undefined,
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return {};
|
|
15
|
-
}
|
|
16
|
-
}
|
|
1
|
+
import { reportClientErrorCore } from './report_client_error_core.js';
|
|
17
2
|
export default function createServerErrorAction(config) {
|
|
18
3
|
return async function reportClientError(error, classOrMethodName, params) {
|
|
19
|
-
|
|
20
|
-
const isPlainParamsObject = typeof params === 'object' && params !== null && !Array.isArray(params);
|
|
21
|
-
const mergedParams = params === undefined
|
|
22
|
-
? { requestContext }
|
|
23
|
-
: isPlainParamsObject
|
|
24
|
-
? { ...params, requestContext }
|
|
25
|
-
: { params, requestContext };
|
|
26
|
-
await reportError(config, {
|
|
27
|
-
error: stringifyUnknown(error, true),
|
|
28
|
-
classOrMethodName,
|
|
29
|
-
params: mergedParams,
|
|
30
|
-
isClient: true,
|
|
31
|
-
});
|
|
4
|
+
await reportClientErrorCore(config, error, classOrMethodName, params);
|
|
32
5
|
};
|
|
33
6
|
}
|
|
@@ -10,4 +10,5 @@ export { defaultIgnoredConsoleErrors } from './default_ignored_console_errors.js
|
|
|
10
10
|
export { default as isStaleDeployError, defaultStaleDeployPatterns, setStaleDeployPatterns, getStaleDeployPatterns, } from './is_stale_deploy_error.js';
|
|
11
11
|
export { default as clearClientCache } from './clear_client_cache.js';
|
|
12
12
|
export { default as useStaleDeployRecovery, shouldRecoverFromStaleDeploy } from './use_stale_deploy_recovery.js';
|
|
13
|
+
export { setErrorHandlingActionConfig, getErrorHandlingActionConfig } from './error_handling_action_config.js';
|
|
13
14
|
export type { ErrorHandlingParams, ErrorHandlingRoutingConfig } from '../types/types.js';
|
|
@@ -8,3 +8,4 @@ export { defaultIgnoredConsoleErrors } from './default_ignored_console_errors.js
|
|
|
8
8
|
export { default as isStaleDeployError, defaultStaleDeployPatterns, setStaleDeployPatterns, getStaleDeployPatterns, } from './is_stale_deploy_error.js';
|
|
9
9
|
export { default as clearClientCache } from './clear_client_cache.js';
|
|
10
10
|
export { default as useStaleDeployRecovery, shouldRecoverFromStaleDeploy } from './use_stale_deploy_recovery.js';
|
|
11
|
+
export { setErrorHandlingActionConfig, getErrorHandlingActionConfig } from './error_handling_action_config.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
import { getErrorHandlingActionConfig } from './error_handling_action_config.js';
|
|
3
|
+
import { reportClientErrorCore } from './report_client_error_core.js';
|
|
4
|
+
export default async function reportClientError(error, classOrMethodName, params) {
|
|
5
|
+
await reportClientErrorCore(getErrorHandlingActionConfig(), error, classOrMethodName, params);
|
|
6
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ErrorHandlingParams } from '../types/types.js';
|
|
2
|
+
import { type ReportErrorConfig } from './report_error.js';
|
|
3
|
+
export declare function reportClientErrorCore(config: ReportErrorConfig | undefined, error: unknown, classOrMethodName: string, params?: ErrorHandlingParams['params']): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import reportError from './report_error.js';
|
|
2
|
+
import stringifyUnknown from './stringify_unknown.js';
|
|
3
|
+
async function resolveRequestContext() {
|
|
4
|
+
try {
|
|
5
|
+
const { headers } = await import('next/headers.js');
|
|
6
|
+
const headerList = await headers();
|
|
7
|
+
return {
|
|
8
|
+
path: headerList.get('x-pathname') ?? undefined,
|
|
9
|
+
userAgent: headerList.get('user-agent') ?? undefined,
|
|
10
|
+
referer: headerList.get('referer') ?? undefined,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function reportClientErrorCore(config, error, classOrMethodName, params) {
|
|
18
|
+
const requestContext = await resolveRequestContext();
|
|
19
|
+
const isPlainParamsObject = typeof params === 'object' && params !== null && !Array.isArray(params);
|
|
20
|
+
const mergedParams = params === undefined
|
|
21
|
+
? { requestContext }
|
|
22
|
+
: isPlainParamsObject
|
|
23
|
+
? { ...params, requestContext }
|
|
24
|
+
: { params, requestContext };
|
|
25
|
+
await reportError(config, {
|
|
26
|
+
error: stringifyUnknown(error, true),
|
|
27
|
+
classOrMethodName,
|
|
28
|
+
params: mergedParams,
|
|
29
|
+
isClient: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-next-intl",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.37",
|
|
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",
|
|
@@ -206,6 +206,10 @@
|
|
|
206
206
|
"types": "./dist/src/error_handling/create_server_error_action.d.ts",
|
|
207
207
|
"import": "./dist/src/error_handling/create_server_error_action.js"
|
|
208
208
|
},
|
|
209
|
+
"./reportClientError": {
|
|
210
|
+
"types": "./dist/src/error_handling/report_client_error_action.d.ts",
|
|
211
|
+
"import": "./dist/src/error_handling/report_client_error_action.js"
|
|
212
|
+
},
|
|
209
213
|
"./isStaleDeployError": {
|
|
210
214
|
"types": "./dist/src/error_handling/is_stale_deploy_error.d.ts",
|
|
211
215
|
"import": "./dist/src/error_handling/is_stale_deploy_error.js"
|