cloudflare-next-intl 0.9.6 → 0.9.9

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.
Files changed (85) hide show
  1. package/bin/check_dynamic_pages.mjs +24 -0
  2. package/dist/src/client/components/client_provider.js +1 -1
  3. package/dist/src/client/components/client_provider_static.js +1 -1
  4. package/dist/src/client/components/locale_link.d.ts +1 -1
  5. package/dist/src/client/components/locale_link_client.js +1 -1
  6. package/dist/src/client/hooks/use_path_name.js +1 -1
  7. package/dist/src/cloudflare_email/escape_html.d.ts +1 -0
  8. package/dist/src/cloudflare_email/escape_html.js +7 -0
  9. package/dist/src/cloudflare_email/index.d.ts +3 -0
  10. package/dist/src/cloudflare_email/index.js +3 -0
  11. package/dist/src/cloudflare_email/resolve_email_binding.d.ts +11 -0
  12. package/dist/src/cloudflare_email/resolve_email_binding.js +8 -0
  13. package/dist/src/cloudflare_email/send_transactional_email.d.ts +18 -0
  14. package/dist/src/cloudflare_email/send_transactional_email.js +36 -0
  15. package/dist/src/cloudflare_fetch/fetch_text.d.ts +5 -0
  16. package/dist/src/cloudflare_fetch/fetch_text.js +17 -0
  17. package/dist/src/cloudflare_fetch/fetch_with_fallback.d.ts +2 -0
  18. package/dist/src/cloudflare_fetch/fetch_with_fallback.js +7 -0
  19. package/dist/src/cloudflare_fetch/index.d.ts +3 -0
  20. package/dist/src/cloudflare_fetch/index.js +3 -0
  21. package/dist/src/cloudflare_fetch/resolve_assets_binding.d.ts +5 -0
  22. package/dist/src/cloudflare_fetch/resolve_assets_binding.js +9 -0
  23. package/dist/src/config/middleware.d.ts +2 -2
  24. package/dist/src/config/middleware.js +1 -1
  25. package/dist/src/cookie_consent/client/components/auto_analytics_events.js +1 -1
  26. package/dist/src/cookie_consent/client/components/cookie_consent_analytics.js +1 -1
  27. package/dist/src/cookie_consent/client/components/default_privacy_policy_link.js +1 -1
  28. package/dist/src/cookie_consent/client/cookie_consent_provider.js +1 -1
  29. package/dist/src/db/connection.js +15 -5
  30. package/dist/src/db/context.js +2 -2
  31. package/dist/src/db/resolve_hyperdrive_connection_string.d.ts +5 -0
  32. package/dist/src/db/resolve_hyperdrive_connection_string.js +10 -0
  33. package/dist/src/db/resolve_mode.d.ts +2 -2
  34. package/dist/src/db/resolve_mode.js +7 -1
  35. package/dist/src/dynamic_pages_check/check_dynamic_pages.d.ts +16 -0
  36. package/dist/src/dynamic_pages_check/check_dynamic_pages.js +38 -0
  37. package/dist/src/dynamic_pages_check/detect_dynamic_usage.d.ts +5 -0
  38. package/dist/src/dynamic_pages_check/detect_dynamic_usage.js +16 -0
  39. package/dist/src/dynamic_pages_check/find_page_files.d.ts +1 -0
  40. package/dist/src/dynamic_pages_check/find_page_files.js +24 -0
  41. package/dist/src/dynamic_pages_check/index.d.ts +3 -0
  42. package/dist/src/dynamic_pages_check/index.js +3 -0
  43. package/dist/src/dynamic_pages_check/insert_dynamic_export.d.ts +1 -0
  44. package/dist/src/dynamic_pages_check/insert_dynamic_export.js +43 -0
  45. package/dist/src/error_handling/create_server_error_action.js +1 -1
  46. package/dist/src/errors_board/client/error_detail_view.d.ts +7 -0
  47. package/dist/src/errors_board/client/error_detail_view.js +35 -0
  48. package/dist/src/errors_board/client/error_row.d.ts +7 -0
  49. package/dist/src/errors_board/client/error_row.js +9 -0
  50. package/dist/src/errors_board/client/error_ui_client.d.ts +14 -0
  51. package/dist/src/errors_board/client/error_ui_client.js +26 -0
  52. package/dist/src/errors_board/client/errors_filter_form.d.ts +8 -0
  53. package/dist/src/errors_board/client/errors_filter_form.js +6 -0
  54. package/dist/src/errors_board/client/errors_list_client.d.ts +15 -0
  55. package/dist/src/errors_board/client/errors_list_client.js +92 -0
  56. package/dist/src/errors_board/client/errors_login_form.d.ts +5 -0
  57. package/dist/src/errors_board/client/errors_login_form.js +21 -0
  58. package/dist/src/errors_board/client/errors_stat_strip.d.ts +6 -0
  59. package/dist/src/errors_board/client/errors_stat_strip.js +19 -0
  60. package/dist/src/errors_board/server/actions_factory.d.ts +24 -0
  61. package/dist/src/errors_board/server/actions_factory.js +39 -0
  62. package/dist/src/errors_board/server/errors_repository.d.ts +83 -0
  63. package/dist/src/errors_board/server/errors_repository.js +199 -0
  64. package/dist/src/errors_board/server/gate.d.ts +19 -0
  65. package/dist/src/errors_board/server/gate.js +68 -0
  66. package/dist/src/errors_board/server/index.d.ts +3 -0
  67. package/dist/src/errors_board/server/index.js +3 -0
  68. package/dist/src/errors_board/shared/error_ui_helpers.d.ts +13 -0
  69. package/dist/src/errors_board/shared/error_ui_helpers.js +60 -0
  70. package/dist/src/firebase_auth/client/auth_user_provider.js +1 -1
  71. package/dist/src/firebase_auth/middleware/update_session.d.ts +1 -1
  72. package/dist/src/firebase_auth/middleware/update_session.js +1 -1
  73. package/dist/src/firebase_auth/server/auth_user_server_provider.js +3 -3
  74. package/dist/src/firebase_auth/server/clear_session_action.js +1 -1
  75. package/dist/src/firebase_auth/server/firebase_server.js +1 -1
  76. package/dist/src/image_optimizer/next_image_shim.d.ts +2 -2
  77. package/dist/src/image_optimizer/next_image_shim.js +1 -1
  78. package/dist/src/server/components/link.d.ts +1 -1
  79. package/dist/src/server/components/link.js +1 -1
  80. package/dist/src/server/components/server_provider.js +2 -2
  81. package/dist/src/server/components/server_provider_static.js +2 -2
  82. package/dist/src/server/functions/geo.js +2 -2
  83. package/dist/src/server/functions/server.js +2 -2
  84. package/dist/src/types/types.d.ts +2 -1
  85. package/package.json +47 -2
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ // Usage: cfni-check-dynamic-pages [--app-dir=src/app] [--mode=off|report|fix] [--skip=a/page.tsx,b/page.tsx]
3
+ // Env equivalents: CFNI_DYNAMIC_PAGES_APP_DIR, CFNI_DYNAMIC_PAGES_MODE, CFNI_DYNAMIC_PAGES_SKIP (comma-separated).
4
+ import { resolve } from 'node:path';
5
+ import { checkDynamicPages } from '../dist/src/dynamic_pages_check/check_dynamic_pages.js';
6
+
7
+ function argValue(name) {
8
+ const prefix = `--${name}=`;
9
+ const arg = process.argv.slice(2).find((a) => a.startsWith(prefix));
10
+ return arg ? arg.slice(prefix.length) : undefined;
11
+ }
12
+
13
+ const appDir = resolve(argValue('app-dir') ?? process.env.CFNI_DYNAMIC_PAGES_APP_DIR ?? 'src/app');
14
+ const mode = argValue('mode') ?? process.env.CFNI_DYNAMIC_PAGES_MODE ?? 'report';
15
+ const skipRaw = argValue('skip') ?? process.env.CFNI_DYNAMIC_PAGES_SKIP ?? '';
16
+ const skip = skipRaw.split(',').map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
17
+
18
+ const reports = await checkDynamicPages({ appDir, mode, skip });
19
+
20
+ if (reports.length === 0) {
21
+ console.log(mode === 'off' ? 'checkDynamicPages: disabled (mode=off).' : `checkDynamicPages: no page/route files found under ${appDir}.`);
22
+ } else {
23
+ for (const { file, action } of reports) console.log(`${action.padEnd(24)} ${file}`);
24
+ }
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { setLocaleCache, setMessageForLocaleCache } from "../../general/cache_variables.js";
4
4
  import { createContext, useMemo } from "react";
5
- import dynamic from "next/dynamic";
5
+ import dynamic from "next/dynamic.js";
6
6
  import config from "@intl-config";
7
7
  import installConsoleErrorOverride from "../../error_handling/install_console_error_override.js";
8
8
  import installGlobalErrorOverride from "../../error_handling/install_global_error_override.js";
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { setLocaleCache, setMessageForLocaleCache } from "../../general/cache_variables.js";
4
4
  import { createContext, useMemo } from "react";
5
- import dynamic from "next/dynamic";
5
+ import dynamic from "next/dynamic.js";
6
6
  import config from "@intl-config";
7
7
  import installConsoleErrorOverride from "../../error_handling/install_console_error_override.js";
8
8
  import installGlobalErrorOverride from "../../error_handling/install_global_error_override.js";
@@ -1,4 +1,4 @@
1
- import { type LinkProps } from 'next/link';
1
+ import { type LinkProps } from 'next/link.js';
2
2
  import { type ComponentProps } from 'react';
3
3
  type NextLinkProps = Omit<ComponentProps<'a'>, keyof LinkProps> & Omit<LinkProps, 'locale' | 'href' | 'prefetch' | 'onNavigate' | 'hrefLang' | 'replace' | 'scroll'>;
4
4
  export type LocaleLinkProps = NextLinkProps & {
@@ -5,7 +5,7 @@ import config from '../../config/intl_config.js';
5
5
  import usePathname from '../hooks/use_path_name.js';
6
6
  import { localeCookieName } from '../../config/cookie_key.js';
7
7
  import setCookie from '../functions/set_cookie.js';
8
- import { useSearchParams } from 'next/navigation';
8
+ import { useSearchParams } from 'next/navigation.js';
9
9
  function ClientLocaleLinkComponent({ locale, className, ...rest }, ref) {
10
10
  const pathname = usePathname();
11
11
  const searchParams = useSearchParams();
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { usePathname as nextUsePathname } from "next/navigation";
2
+ import { usePathname as nextUsePathname } from "next/navigation.js";
3
3
  import { useLocale } from "./client_hooks.js";
4
4
  export default function usePathname() {
5
5
  const pathname = nextUsePathname();
@@ -0,0 +1 @@
1
+ export declare function escapeHtml(value: string): string;
@@ -0,0 +1,7 @@
1
+ export function escapeHtml(value) {
2
+ return value
3
+ .replace(/&/g, '&amp;')
4
+ .replace(/</g, '&lt;')
5
+ .replace(/>/g, '&gt;')
6
+ .replace(/"/g, '&quot;');
7
+ }
@@ -0,0 +1,3 @@
1
+ export { sendTransactionalEmail, type TransactionalEmailOutcome, type TransactionalEmailContent, type SendTransactionalEmailOptions } from './send_transactional_email.js';
2
+ export { resolveEmailBinding, type EmailBindingLike } from './resolve_email_binding.js';
3
+ export { escapeHtml } from './escape_html.js';
@@ -0,0 +1,3 @@
1
+ export { sendTransactionalEmail } from './send_transactional_email.js';
2
+ export { resolveEmailBinding } from './resolve_email_binding.js';
3
+ export { escapeHtml } from './escape_html.js';
@@ -0,0 +1,11 @@
1
+ import type { GenerateRoutingConfig } from '../types/types.js';
2
+ export interface EmailBindingLike {
3
+ send(message: {
4
+ to: string;
5
+ from: string;
6
+ subject: string;
7
+ html?: string;
8
+ text?: string;
9
+ }): Promise<unknown>;
10
+ }
11
+ export declare function resolveEmailBinding(generate?: GenerateRoutingConfig, bindingName?: string): Promise<EmailBindingLike | null>;
@@ -0,0 +1,8 @@
1
+ import { resolveEnv } from '../server/functions/geo.js';
2
+ export async function resolveEmailBinding(generate, bindingName = 'EMAIL') {
3
+ const env = await resolveEnv(generate);
4
+ const candidate = env?.[bindingName];
5
+ if (!candidate || typeof candidate !== 'object')
6
+ return null;
7
+ return typeof candidate.send === 'function' ? candidate : null;
8
+ }
@@ -0,0 +1,18 @@
1
+ import { type ReportErrorConfig } from '../error_handling/report_error.js';
2
+ import type { GenerateRoutingConfig } from '../types/types.js';
3
+ export type TransactionalEmailOutcome = 'sent' | 'unavailable' | 'failed';
4
+ export interface TransactionalEmailContent {
5
+ subject: string;
6
+ text: string;
7
+ html: string;
8
+ }
9
+ export interface SendTransactionalEmailOptions extends ReportErrorConfig {
10
+ generate?: GenerateRoutingConfig;
11
+ senderAddress: string;
12
+ bindingName?: string;
13
+ restAccountId?: string;
14
+ restToken?: string;
15
+ }
16
+ export declare function sendTransactionalEmail(message: {
17
+ to: string;
18
+ } & TransactionalEmailContent, options: SendTransactionalEmailOptions, reportAs: string): Promise<TransactionalEmailOutcome>;
@@ -0,0 +1,36 @@
1
+ import { resolveEmailBinding } from './resolve_email_binding.js';
2
+ import reportError from '../error_handling/report_error.js';
3
+ function isUsableCredential(value) {
4
+ return value.length > 0 && !value.includes('$(');
5
+ }
6
+ async function sendOverRest(message, options, reportAs) {
7
+ const accountId = (options.restAccountId ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? '').trim();
8
+ const token = (options.restToken ?? process.env.CLOUDFLARE_EMAIL_TOKEN ?? '').trim();
9
+ if (!isUsableCredential(accountId) || !isUsableCredential(token))
10
+ return 'unavailable';
11
+ const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/email/sending/send`, {
12
+ method: 'POST',
13
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
14
+ body: JSON.stringify(message),
15
+ });
16
+ if (!response.ok) {
17
+ await reportError(options, { error: new Error(`email/sending/send responded ${response.status}`), classOrMethodName: `${reportAs}.rest` });
18
+ return 'failed';
19
+ }
20
+ return 'sent';
21
+ }
22
+ export async function sendTransactionalEmail(message, options, reportAs) {
23
+ try {
24
+ const binding = await resolveEmailBinding(options.generate, options.bindingName);
25
+ const fullMessage = { ...message, from: options.senderAddress };
26
+ if (binding) {
27
+ await binding.send(fullMessage);
28
+ return 'sent';
29
+ }
30
+ return await sendOverRest(fullMessage, options, reportAs);
31
+ }
32
+ catch (error) {
33
+ await reportError(options, { error, classOrMethodName: reportAs });
34
+ return 'failed';
35
+ }
36
+ }
@@ -0,0 +1,5 @@
1
+ import { type ReportErrorConfig } from '../error_handling/report_error.js';
2
+ import type { GenerateRoutingConfig } from '../types/types.js';
3
+ export declare function fetchText(input: RequestInfo | URL, init: RequestInit, config: (ReportErrorConfig & {
4
+ generate?: GenerateRoutingConfig;
5
+ }) | undefined, reportAs: string): Promise<string | null>;
@@ -0,0 +1,17 @@
1
+ import { fetchWithCloudflareFallback } from './fetch_with_fallback.js';
2
+ import reportError from '../error_handling/report_error.js';
3
+ const MAX_ERROR_BODY_LENGTH = 500;
4
+ export async function fetchText(input, init, config, reportAs) {
5
+ try {
6
+ const response = await fetchWithCloudflareFallback(input, init, config?.generate);
7
+ if (!response.ok) {
8
+ const body = (await response.text()).slice(0, MAX_ERROR_BODY_LENGTH);
9
+ throw new Error(body || `HTTP ${response.status}`);
10
+ }
11
+ return await response.text();
12
+ }
13
+ catch (error) {
14
+ await reportError(config, { error, classOrMethodName: reportAs, params: { input: String(input) } });
15
+ return null;
16
+ }
17
+ }
@@ -0,0 +1,2 @@
1
+ import type { GenerateRoutingConfig } from '../types/types.js';
2
+ export declare function fetchWithCloudflareFallback(input: RequestInfo | URL, init: RequestInit, generate?: GenerateRoutingConfig): Promise<Response>;
@@ -0,0 +1,7 @@
1
+ import { resolveAssetsBinding } from './resolve_assets_binding.js';
2
+ export async function fetchWithCloudflareFallback(input, init, generate) {
3
+ const binding = await resolveAssetsBinding(generate);
4
+ if (binding)
5
+ return binding.fetch(input, init);
6
+ return fetch(input, { ...init, cache: 'no-store' });
7
+ }
@@ -0,0 +1,3 @@
1
+ export { fetchText } from './fetch_text.js';
2
+ export { fetchWithCloudflareFallback } from './fetch_with_fallback.js';
3
+ export { resolveAssetsBinding, type AssetsBindingLike } from './resolve_assets_binding.js';
@@ -0,0 +1,3 @@
1
+ export { fetchText } from './fetch_text.js';
2
+ export { fetchWithCloudflareFallback } from './fetch_with_fallback.js';
3
+ export { resolveAssetsBinding } from './resolve_assets_binding.js';
@@ -0,0 +1,5 @@
1
+ import type { GenerateRoutingConfig } from '../types/types.js';
2
+ export interface AssetsBindingLike {
3
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
4
+ }
5
+ export declare function resolveAssetsBinding(generate?: GenerateRoutingConfig): Promise<AssetsBindingLike | null>;
@@ -0,0 +1,9 @@
1
+ import { resolveEnv } from '../server/functions/geo.js';
2
+ export async function resolveAssetsBinding(generate) {
3
+ const env = await resolveEnv(generate);
4
+ const candidate = env?.ASSETS;
5
+ if (!candidate || typeof candidate !== 'object')
6
+ return null;
7
+ const fetchFn = candidate.fetch;
8
+ return typeof fetchFn === 'function' ? candidate : null;
9
+ }
@@ -1,5 +1,5 @@
1
- import type { NextRequest } from 'next/server';
2
- import { NextResponse } from 'next/server';
1
+ import type { NextRequest } from 'next/server.js';
2
+ import { NextResponse } from 'next/server.js';
3
3
  import type { MiddlewareCustomHandler } from '../types/types.js';
4
4
  export declare const localesSet: Set<string>;
5
5
  export default function intlMiddleware(request: NextRequest, options?: {
@@ -1,4 +1,4 @@
1
- import { NextResponse } from 'next/server';
1
+ import { NextResponse } from 'next/server.js';
2
2
  import { languageDetecotr } from '../server/functions/get_user_locale.js';
3
3
  import config from './intl_config.js';
4
4
  import { isBotCookieKey, localeCookieName } from './cookie_key.js';
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { usePathname } from 'next/navigation';
2
+ import { usePathname } from 'next/navigation.js';
3
3
  import { useReportWebVitals } from 'next/web-vitals';
4
4
  import { useEffect } from 'react';
5
5
  import useCookieConsent from '../use_cookie_consent.js';
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect } from 'react';
4
- import dynamic from 'next/dynamic';
4
+ import dynamic from 'next/dynamic.js';
5
5
  import useCookieConsent from '../use_cookie_consent.js';
6
6
  const ClarityScript = dynamic(() => import('./clarity_script.js'));
7
7
  export default function CookieConsentAnalytics({ config }) {
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import NextLink from 'next/link';
3
+ import NextLink from 'next/link.js';
4
4
  import config from '../../../config/intl_config.js';
5
5
  import { getLocaleCache } from '../../../general/cache_variables.js';
6
6
  export default function DefaultPrivacyPolicyLink({ privacyPolicyPath, text, className, style }) {
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
4
- import { usePathname } from 'next/navigation';
4
+ import { usePathname } from 'next/navigation.js';
5
5
  import config from '../../config/intl_config.js';
6
6
  import requireCookieConsentConfig from '../require_config.js';
7
7
  import getCookie from '../../client/functions/get_cookie.js';
@@ -1,7 +1,7 @@
1
1
  import reportError from '../error_handling/report_error.js';
2
2
  import requireDbConfig from './require_config.js';
3
3
  import resolveConfigValue from './resolve_config_value.js';
4
- import { resolveEnv } from '../server/functions/geo.js';
4
+ import { resolveHyperdriveConnectionString } from './resolve_hyperdrive_connection_string.js';
5
5
  const BENIGN_DISCONNECT_PATTERN = /(connection terminated|connection closed|socket closed|unexpected eof)/i;
6
6
  let pgModule;
7
7
  function loadPg() {
@@ -12,15 +12,23 @@ async function resolveConnectionString(db, generate) {
12
12
  const configured = await resolveConfigValue(db.connectionString);
13
13
  if (configured)
14
14
  return configured;
15
- const env = await resolveEnv(generate);
16
- const hyperdriveConn = env?.HYPERDRIVE?.connectionString;
17
- if (hyperdriveConn && hyperdriveConn !== 'postgresql://user:pass@localhost:5432/db') {
18
- return hyperdriveConn;
15
+ if (db.autoHyperdrive !== false) {
16
+ const hyperdriveConn = await resolveHyperdriveConnectionString(generate);
17
+ if (hyperdriveConn)
18
+ return hyperdriveConn;
19
19
  }
20
20
  throw new Error('db: could not resolve a Postgres connection string. Set `db.connectionString` ' +
21
21
  'to a connection string, or to a function returning one (e.g. reading a ' +
22
22
  'Hyperdrive binding off `env` or `getCloudflareContext().env`).');
23
23
  }
24
+ async function resetSessionState(client) {
25
+ try {
26
+ await client.query('discard all');
27
+ }
28
+ catch {
29
+ await client.query('reset role').catch(() => undefined);
30
+ }
31
+ }
24
32
  export async function withDbClient(config, queryFn) {
25
33
  const db = config.db;
26
34
  requireDbConfig(db);
@@ -33,6 +41,7 @@ export async function withDbClient(config, queryFn) {
33
41
  try {
34
42
  await client.connect();
35
43
  connected = true;
44
+ await resetSessionState(client);
36
45
  }
37
46
  catch (error) {
38
47
  const message = error instanceof Error ? error.message : String(error ?? '');
@@ -83,6 +92,7 @@ export async function connectToPostgres(config) {
83
92
  void reportError({ errorHandling: config.errorHandling, generate: config.generate }, { error, classOrMethodName: 'db.connectToPostgres.clientError' });
84
93
  });
85
94
  await client.connect();
95
+ await resetSessionState(client);
86
96
  return client;
87
97
  }
88
98
  export async function disconnectPostgres(client) {
@@ -100,7 +100,7 @@ export async function withPublicDb(fn, dbOverride) {
100
100
  const config = await resolveDbConfig(dbOverride);
101
101
  const db = config.db;
102
102
  requireDbConfig(db);
103
- const resolved = await resolveDbMode(db);
103
+ const resolved = await resolveDbMode(db, config.generate);
104
104
  if (resolved.mode === 'supabase') {
105
105
  const { anonKey } = await resolveSupabaseEndpoint(resolved.supabase);
106
106
  return fn(await supabaseDb(resolved.supabase, anonKey));
@@ -130,7 +130,7 @@ export async function withUserDb(fn, uid, dbOverride) {
130
130
  const config = await resolveDbConfig(dbOverride);
131
131
  const db = config.db;
132
132
  requireDbConfig(db);
133
- const resolved = await resolveDbMode(db);
133
+ const resolved = await resolveDbMode(db, config.generate);
134
134
  if (resolved.mode === 'supabase') {
135
135
  const token = await resolveAccessToken(config);
136
136
  return fn(await supabaseDb(resolved.supabase, token));
@@ -0,0 +1,5 @@
1
+ import type { GenerateRoutingConfig } from '../types/types.js';
2
+ export interface HyperdriveBindingLike {
3
+ connectionString: string;
4
+ }
5
+ export declare function resolveHyperdriveConnectionString(generate?: GenerateRoutingConfig): Promise<string | undefined>;
@@ -0,0 +1,10 @@
1
+ import { resolveEnv } from '../server/functions/geo.js';
2
+ const WRANGLER_DEV_PLACEHOLDER = 'postgresql://user:pass@localhost:5432/db';
3
+ export async function resolveHyperdriveConnectionString(generate) {
4
+ const env = await resolveEnv(generate);
5
+ const binding = env?.HYPERDRIVE;
6
+ const connectionString = binding?.connectionString;
7
+ if (!connectionString || connectionString === WRANGLER_DEV_PLACEHOLDER)
8
+ return undefined;
9
+ return connectionString;
10
+ }
@@ -1,4 +1,4 @@
1
- import type { DbRoutingConfig, SupabaseDbConfig } from '../types/types.js';
1
+ import type { DbRoutingConfig, GenerateRoutingConfig, SupabaseDbConfig } from '../types/types.js';
2
2
  export type DbMode = 'postgres' | 'supabase';
3
3
  export type ResolvedDbMode = {
4
4
  mode: 'postgres';
@@ -7,4 +7,4 @@ export type ResolvedDbMode = {
7
7
  mode: 'supabase';
8
8
  supabase: SupabaseDbConfig;
9
9
  };
10
- export default function resolveDbMode(db: DbRoutingConfig): Promise<ResolvedDbMode>;
10
+ export default function resolveDbMode(db: DbRoutingConfig, generate?: GenerateRoutingConfig): Promise<ResolvedDbMode>;
@@ -1,8 +1,14 @@
1
1
  import resolveConfigValue from './resolve_config_value.js';
2
- export default async function resolveDbMode(db) {
2
+ import { resolveHyperdriveConnectionString } from './resolve_hyperdrive_connection_string.js';
3
+ export default async function resolveDbMode(db, generate) {
3
4
  const connectionString = await resolveConfigValue(db.connectionString);
4
5
  if (connectionString)
5
6
  return { mode: 'postgres', connectionString };
7
+ if (db.autoHyperdrive !== false) {
8
+ const hyperdriveConnectionString = await resolveHyperdriveConnectionString(generate);
9
+ if (hyperdriveConnectionString)
10
+ return { mode: 'postgres', connectionString: hyperdriveConnectionString };
11
+ }
6
12
  if (db.supabase)
7
13
  return { mode: 'supabase', supabase: db.supabase };
8
14
  return { mode: 'postgres', connectionString: undefined };
@@ -0,0 +1,16 @@
1
+ export type DynamicPagesCheckMode = 'off' | 'report' | 'fix';
2
+ export interface CheckDynamicPagesOptions {
3
+ appDir: string;
4
+ mode?: DynamicPagesCheckMode;
5
+ skip?: readonly string[];
6
+ }
7
+ export interface CheckDynamicPagesReport {
8
+ file: string;
9
+ action: 'added-force-dynamic' | 'would-add-force-dynamic' | 'already-declared' | 'no-dynamic-usage-detected' | 'skipped';
10
+ }
11
+ export interface CheckDynamicPagesIo {
12
+ findPageFiles?: (appDir: string) => string[];
13
+ readFile?: (file: string) => string;
14
+ writeFile?: (file: string, contents: string) => void;
15
+ }
16
+ export declare function checkDynamicPages(options: CheckDynamicPagesOptions, io?: CheckDynamicPagesIo): Promise<CheckDynamicPagesReport[]>;
@@ -0,0 +1,38 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { findPageFiles as findPageFilesImpl } from './find_page_files.js';
3
+ import { detectDynamicUsage } from './detect_dynamic_usage.js';
4
+ import { insertDynamicExport } from './insert_dynamic_export.js';
5
+ export async function checkDynamicPages(options, io = {}) {
6
+ const mode = options.mode ?? 'report';
7
+ if (mode === 'off')
8
+ return [];
9
+ const findPageFiles = io.findPageFiles ?? findPageFilesImpl;
10
+ const readFile = io.readFile ?? ((file) => readFileSync(file, 'utf8'));
11
+ const writeFile = io.writeFile ?? ((file, contents) => writeFileSync(file, contents, 'utf8'));
12
+ const skipSet = new Set(options.skip ?? []);
13
+ const reports = [];
14
+ for (const file of findPageFiles(options.appDir)) {
15
+ if (skipSet.has(file)) {
16
+ reports.push({ file, action: 'skipped' });
17
+ continue;
18
+ }
19
+ const source = readFile(file);
20
+ const detection = detectDynamicUsage(source);
21
+ if (detection.hasExplicitDynamicExport) {
22
+ reports.push({ file, action: 'already-declared' });
23
+ continue;
24
+ }
25
+ if (detection.detectedDynamicApis.length === 0) {
26
+ reports.push({ file, action: 'no-dynamic-usage-detected' });
27
+ continue;
28
+ }
29
+ if (mode === 'fix') {
30
+ writeFile(file, insertDynamicExport(source, 'force-dynamic'));
31
+ reports.push({ file, action: 'added-force-dynamic' });
32
+ }
33
+ else {
34
+ reports.push({ file, action: 'would-add-force-dynamic' });
35
+ }
36
+ }
37
+ return reports;
38
+ }
@@ -0,0 +1,5 @@
1
+ export interface DynamicDetectionResult {
2
+ hasExplicitDynamicExport: boolean;
3
+ detectedDynamicApis: string[];
4
+ }
5
+ export declare function detectDynamicUsage(sourceText: string): DynamicDetectionResult;
@@ -0,0 +1,16 @@
1
+ const DYNAMIC_API_CHECKS = [
2
+ { name: 'cookies()', pattern: /\bcookies\s*\(/ },
3
+ { name: 'headers()', pattern: /\bheaders\s*\(\s*\)/ },
4
+ { name: 'searchParams', pattern: /\bsearchParams\b/ },
5
+ { name: 'unstable_noStore()', pattern: /\bunstable_noStore\s*\(/ },
6
+ { name: 'connection()', pattern: /\bconnection\s*\(\s*\)/ },
7
+ { name: 'cache: "no-store"', pattern: /cache:\s*['"]no-store['"]/ },
8
+ { name: 'next: { revalidate: 0 }', pattern: /next:\s*\{\s*revalidate:\s*0\s*[,}]/ },
9
+ ];
10
+ const EXPLICIT_DYNAMIC_EXPORT = /export\s+const\s+dynamic\s*=/;
11
+ export function detectDynamicUsage(sourceText) {
12
+ return {
13
+ hasExplicitDynamicExport: EXPLICIT_DYNAMIC_EXPORT.test(sourceText),
14
+ detectedDynamicApis: DYNAMIC_API_CHECKS.filter(({ pattern }) => pattern.test(sourceText)).map(({ name }) => name),
15
+ };
16
+ }
@@ -0,0 +1 @@
1
+ export declare function findPageFiles(appDir: string): string[];
@@ -0,0 +1,24 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ const PAGE_FILE_NAMES = new Set(['page.tsx', 'page.ts', 'page.jsx', 'page.js', 'route.ts', 'route.js']);
4
+ export function findPageFiles(appDir) {
5
+ let entries;
6
+ try {
7
+ entries = readdirSync(appDir, { withFileTypes: true });
8
+ }
9
+ catch {
10
+ return [];
11
+ }
12
+ const files = [];
13
+ for (const entry of entries) {
14
+ if (entry.isDirectory()) {
15
+ if (entry.name === 'node_modules' || entry.name.startsWith('.'))
16
+ continue;
17
+ files.push(...findPageFiles(join(appDir, entry.name)));
18
+ }
19
+ else if (PAGE_FILE_NAMES.has(entry.name)) {
20
+ files.push(join(appDir, entry.name));
21
+ }
22
+ }
23
+ return files;
24
+ }
@@ -0,0 +1,3 @@
1
+ export { checkDynamicPages, type DynamicPagesCheckMode, type CheckDynamicPagesOptions, type CheckDynamicPagesReport, type CheckDynamicPagesIo } from './check_dynamic_pages.js';
2
+ export { findPageFiles } from './find_page_files.js';
3
+ export { detectDynamicUsage, type DynamicDetectionResult } from './detect_dynamic_usage.js';
@@ -0,0 +1,3 @@
1
+ export { checkDynamicPages } from './check_dynamic_pages.js';
2
+ export { findPageFiles } from './find_page_files.js';
3
+ export { detectDynamicUsage } from './detect_dynamic_usage.js';
@@ -0,0 +1 @@
1
+ export declare function insertDynamicExport(sourceText: string, value: 'force-static' | 'force-dynamic'): string;
@@ -0,0 +1,43 @@
1
+ function findLeadingImportBlockEnd(sourceText) {
2
+ const lines = sourceText.split('\n');
3
+ let offset = 0;
4
+ let lastImportEnd = -1;
5
+ let depth = 0;
6
+ let inImport = false;
7
+ for (const line of lines) {
8
+ const lineEnd = offset + line.length;
9
+ const trimmed = line.trim();
10
+ if (!inImport) {
11
+ if (trimmed === '' || trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
12
+ offset = lineEnd + 1;
13
+ continue;
14
+ }
15
+ if (!/^import\b/.test(trimmed))
16
+ break;
17
+ inImport = true;
18
+ depth = 0;
19
+ }
20
+ for (const char of line) {
21
+ if (char === '{' || char === '(' || char === '[')
22
+ depth += 1;
23
+ else if (char === '}' || char === ')' || char === ']')
24
+ depth -= 1;
25
+ }
26
+ if (inImport && depth <= 0 && /;\s*$/.test(line)) {
27
+ inImport = false;
28
+ lastImportEnd = lineEnd;
29
+ }
30
+ offset = lineEnd + 1;
31
+ }
32
+ return lastImportEnd;
33
+ }
34
+ export function insertDynamicExport(sourceText, value) {
35
+ const block = `// Auto-inserted by cloudflare-next-intl's checkDynamicPages (mode: "fix") — remove this line, or set \`dynamic\` yourself, to override.\nexport const dynamic = "${value}";\n`;
36
+ const lastImportEnd = findLeadingImportBlockEnd(sourceText);
37
+ if (lastImportEnd === -1) {
38
+ return `${block}\n${sourceText}`;
39
+ }
40
+ const before = sourceText.slice(0, lastImportEnd).replace(/\n+$/, '');
41
+ const after = sourceText.slice(lastImportEnd).replace(/^\n+/, '');
42
+ return `${before}\n\n${block}\n${after}`;
43
+ }
@@ -2,7 +2,7 @@ import reportError from './report_error.js';
2
2
  import stringifyUnknown from './stringify_unknown.js';
3
3
  async function resolveRequestContext() {
4
4
  try {
5
- const { headers } = await import('next/headers');
5
+ const { headers } = await import('next/headers.js');
6
6
  const headerList = await headers();
7
7
  return {
8
8
  path: headerList.get('x-pathname') ?? undefined,
@@ -0,0 +1,7 @@
1
+ import type { ErrorRow } from '../server/errors_repository.js';
2
+ import type { ErrorsActions } from '../server/actions_factory.js';
3
+ export default function ErrorDetailView({ row, actions, onDeleted, }: {
4
+ row: ErrorRow;
5
+ actions: ErrorsActions;
6
+ onDeleted: () => void;
7
+ }): Component;