cloudflare-next-intl 0.8.10 → 0.8.11

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.
@@ -38,6 +38,7 @@ import { Client } from 'pg';
38
38
  import resolveCodegenPaths from '../dist/src/db/codegen_paths.js';
39
39
  import { runInstallExecStep } from './install_exec_step.mjs';
40
40
  import { startEphemeralPostgres } from './ephemeral_pg.mjs';
41
+ import { orderedSqlFiles } from './ddl_order.mjs';
41
42
 
42
43
  const paths = resolveCodegenPaths(process.argv.slice(2), process.env, process.cwd());
43
44
 
@@ -96,15 +97,15 @@ if (paths.check) {
96
97
 
97
98
  let effectiveDbUrl = paths.dbUrl;
98
99
  let ephemeral = null;
99
- if (!(await isReachable(paths.dbUrl))) {
100
- if (paths.dbUrlExplicit) failUnreachable(paths.dbUrl);
101
- ephemeral = await startEphemeralPostgres(paths.ephemeralDir, sqlFiles(paths.ddlDir));
102
- if (!ephemeral) failUnreachable(paths.dbUrl);
103
- effectiveDbUrl = ephemeral.url;
104
- }
105
-
106
- rmSync(paths.pullDir, { recursive: true, force: true });
107
100
  try {
101
+ if (!(await isReachable(paths.dbUrl))) {
102
+ if (paths.dbUrlExplicit) failUnreachable(paths.dbUrl);
103
+ ephemeral = await startEphemeralPostgres(orderedSqlFiles(paths.ddlDir));
104
+ if (!ephemeral) failUnreachable(paths.dbUrl);
105
+ effectiveDbUrl = ephemeral.url;
106
+ }
107
+
108
+ rmSync(paths.pullDir, { recursive: true, force: true });
108
109
  execFileSync('npx', ['drizzle-kit', 'pull', ...(paths.drizzleConfig ? [`--config=${paths.drizzleConfig}`] : [])], {
109
110
  stdio: 'inherit',
110
111
  env: { ...process.env, CODEGEN_DATABASE_URL: effectiveDbUrl },
@@ -0,0 +1,46 @@
1
+ // Mirrors supabase/scripts/db_start.sh's `process_path`: within each
2
+ // directory, files/subdirectories listed in that directory's own `order.txt`
3
+ // apply first (in the listed order, recursing into subdirectories the same
4
+ // way), then everything else applies in alphabetical order. DDL is not
5
+ // generally safe to apply purely alphabetically — e.g. a function that calls
6
+ // another function must be created after it — so `order.txt` is how a
7
+ // project expresses the real dependency order.
8
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+
11
+ function orderedEntries(dir) {
12
+ const orderFile = join(dir, 'order.txt');
13
+ const applied = new Set();
14
+ const ordered = [];
15
+
16
+ if (existsSync(orderFile)) {
17
+ for (const rawLine of readFileSync(orderFile, 'utf8').split('\n')) {
18
+ const line = rawLine.trim();
19
+ if (!line || line.startsWith('#')) continue;
20
+ const path = join(dir, line);
21
+ if (existsSync(path)) {
22
+ ordered.push(path);
23
+ applied.add(line);
24
+ }
25
+ }
26
+ }
27
+
28
+ const rest = readdirSync(dir)
29
+ .filter((name) => name !== 'order.txt' && !applied.has(name))
30
+ .sort()
31
+ .map((name) => join(dir, name));
32
+
33
+ return [...ordered, ...rest];
34
+ }
35
+
36
+ /** Returns every `.sql` file under `dir`, in the order a project's own
37
+ * `order.txt` files (one per directory) say they must be applied. */
38
+ export function orderedSqlFiles(dir) {
39
+ const files = [];
40
+ for (const path of orderedEntries(dir)) {
41
+ const stat = statSync(path);
42
+ if (stat.isDirectory()) files.push(...orderedSqlFiles(path));
43
+ else if (path.endsWith('.sql')) files.push(path);
44
+ }
45
+ return files;
46
+ }
@@ -4,14 +4,30 @@
4
4
  // no --db-url/CODEGEN_DATABASE_URL was given and nothing is reachable at the
5
5
  // local Supabase default.
6
6
  import { readFileSync, rmSync } from 'node:fs';
7
+ import { relative } from 'node:path';
7
8
  import { Client } from 'pg';
8
9
 
9
10
  const EPHEMERAL_PORT = 54329;
10
11
  const EPHEMERAL_URL = `postgresql://postgres:postgres@127.0.0.1:${EPHEMERAL_PORT}/postgres`;
12
+ // Lives under this package's own install dir, not the consuming project —
13
+ // keeps a throwaway Postgres data directory out of the user's repo/tree.
14
+ const DATA_DIR = new URL('../.drizzle-ephemeral-pg', import.meta.url).pathname;
15
+
16
+ // A plain embedded-postgres cluster has none of the roles the real Supabase
17
+ // Postgres image bootstraps into every project (`anon`/`authenticated`/
18
+ // `service_role` for PostgREST, plus the admin roles some GRANT/ALTER
19
+ // statements target) — DDL that GRANTs to them fails otherwise. These are
20
+ // the standard local-dev role names Supabase itself creates; DDL never
21
+ // creates them, so codegen must.
22
+ const SUPABASE_ROLES = [
23
+ 'anon', 'authenticated', 'service_role', 'authenticator',
24
+ 'supabase_admin', 'supabase_auth_admin', 'supabase_storage_admin', 'supabase_realtime_admin',
25
+ ];
11
26
 
12
27
  /** Starts an ephemeral Postgres, loads every file in `sqlFiles` into it, and
13
28
  * returns { url, stop() }. Caller must call stop() when done, even on error. */
14
- export async function startEphemeralPostgres(dataDir, sqlFiles) {
29
+ export async function startEphemeralPostgres(sqlFiles) {
30
+ const dataDir = DATA_DIR;
15
31
  let EmbeddedPostgres;
16
32
  try {
17
33
  ({ default: EmbeddedPostgres } = await import('embedded-postgres'));
@@ -29,19 +45,33 @@ export async function startEphemeralPostgres(dataDir, sqlFiles) {
29
45
  });
30
46
 
31
47
  console.log('ℹ️ No reachable Postgres found — starting an ephemeral one (embedded-postgres, no Docker needed)…');
32
- await pg.initialise();
33
- await pg.start();
34
-
35
- const client = new Client({ connectionString: EPHEMERAL_URL });
36
- await client.connect();
37
48
  try {
38
- for (const file of sqlFiles) {
39
- const sql = readFileSync(file, 'utf8');
40
- if (sql.trim().length === 0) continue;
41
- await client.query(sql);
49
+ await pg.initialise();
50
+ await pg.start();
51
+
52
+ const client = new Client({ connectionString: EPHEMERAL_URL });
53
+ await client.connect();
54
+ try {
55
+ for (const role of SUPABASE_ROLES) {
56
+ await client.query(`CREATE ROLE ${role} NOLOGIN NOINHERIT;`);
57
+ }
58
+ for (const file of sqlFiles) {
59
+ const sql = readFileSync(file, 'utf8');
60
+ if (sql.trim().length === 0) continue;
61
+ try {
62
+ await client.query(sql);
63
+ } catch (error) {
64
+ error.message = `${relative(process.cwd(), file)}: ${error.message}`;
65
+ throw error;
66
+ }
67
+ }
68
+ } finally {
69
+ await client.end();
42
70
  }
43
- } finally {
44
- await client.end();
71
+ } catch (error) {
72
+ await pg.stop().catch(() => { /* best-effort */ });
73
+ rmSync(dataDir, { recursive: true, force: true });
74
+ throw error;
45
75
  }
46
76
 
47
77
  return {
@@ -12,7 +12,6 @@ export interface CodegenPaths {
12
12
  manifest: string;
13
13
  dbUrl: string;
14
14
  dbUrlExplicit: boolean;
15
- ephemeralDir: string;
16
15
  check: boolean;
17
16
  timeoutMs: number;
18
17
  drizzleConfig: string | null;
@@ -53,7 +53,6 @@ export default function resolveCodegenPaths(argv, env, cwd) {
53
53
  manifest: join(outDir, 'manifest.json'),
54
54
  dbUrl: flag(argv, 'db-url') ?? env.CODEGEN_DATABASE_URL ?? DEFAULT_DB_URL,
55
55
  dbUrlExplicit: flag(argv, 'db-url') !== undefined || env.CODEGEN_DATABASE_URL !== undefined,
56
- ephemeralDir: resolve(outDir, '..', '.drizzle-ephemeral-pg'),
57
56
  check: argv.includes('--check'),
58
57
  timeoutMs: Number(env.CODEGEN_CONNECT_TIMEOUT_MS) || DEFAULT_TIMEOUT_MS,
59
58
  drizzleConfig: drizzleConfig === null ? null : abs(cwd, drizzleConfig),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.8.10",
3
+ "version": "0.8.11",
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",