cloudflare-next-intl 0.8.9 → 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.
- package/README.md +9 -0
- package/bin/db_codegen.mjs +34 -12
- package/bin/ddl_order.mjs +46 -0
- package/bin/ephemeral_pg.mjs +84 -0
- package/dist/src/db/codegen_paths.d.ts +1 -0
- package/dist/src/db/codegen_paths.js +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -778,6 +778,15 @@ them and fails naming the first one that is stale.
|
|
|
778
778
|
npx cfni-db-codegen --out-dir=src/shared/db/generated --out-dir=../other-app/src/db/generated
|
|
779
779
|
```
|
|
780
780
|
|
|
781
|
+
If no `--db-url`/`CODEGEN_DATABASE_URL` is set and nothing is reachable at the
|
|
782
|
+
local Supabase default, `cfni-db-codegen` falls back to a throwaway,
|
|
783
|
+
local-only Postgres started via the optional `embedded-postgres` package (a
|
|
784
|
+
prebuilt binary, no Docker required): install it once with
|
|
785
|
+
`npm install --save-dev embedded-postgres`, and no local DB setup is needed —
|
|
786
|
+
the DDL in `--ddl-dir` is loaded into it, introspected, and it's torn down
|
|
787
|
+
after. Passing an explicit `--db-url`/`CODEGEN_DATABASE_URL` skips this
|
|
788
|
+
fallback entirely and fails loudly if that target is unreachable.
|
|
789
|
+
|
|
781
790
|
##### Keeping `cfni_exec.sql` in sync (`--rpc-dir`/`--rpc-file-name`/`--tests-dir`/`--tests-file-name`/`--force`/`--skip-exec`)
|
|
782
791
|
|
|
783
792
|
After a successful (non-`--check`) run, `cfni-db-codegen` also copies
|
package/bin/db_codegen.mjs
CHANGED
|
@@ -37,26 +37,34 @@ import { join, relative } from 'node:path';
|
|
|
37
37
|
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
|
+
import { startEphemeralPostgres } from './ephemeral_pg.mjs';
|
|
41
|
+
import { orderedSqlFiles } from './ddl_order.mjs';
|
|
40
42
|
|
|
41
43
|
const paths = resolveCodegenPaths(process.argv.slice(2), process.env, process.cwd());
|
|
42
44
|
|
|
43
|
-
async function
|
|
45
|
+
async function isReachable(url) {
|
|
44
46
|
const client = new Client({ connectionString: url, connectionTimeoutMillis: paths.timeoutMs });
|
|
45
47
|
try {
|
|
46
48
|
await client.connect();
|
|
47
49
|
await client.end();
|
|
48
|
-
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
49
52
|
await client.end().catch(() => { /* already failed to connect */ });
|
|
50
|
-
|
|
51
|
-
console.error("\n drizzle-kit pull needs a live Postgres to introspect — any one works, this script has no Docker dependency of its own. Pick one:");
|
|
52
|
-
console.error(" - Local Supabase (needs Docker running): ./supabase/scripts/db_start.sh --reset");
|
|
53
|
-
console.error(" - A native Postgres you already have: CODEGEN_DATABASE_URL=postgresql://... npm run db:codegen");
|
|
54
|
-
console.error(" - A remote/staging database: CODEGEN_DATABASE_URL=postgresql://... npm run db:codegen");
|
|
55
|
-
console.error(` Slow/cold-starting target? Raise the timeout: CODEGEN_CONNECT_TIMEOUT_MS=15000 npm run db:codegen`);
|
|
56
|
-
process.exit(1);
|
|
53
|
+
return false;
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
56
|
|
|
57
|
+
function failUnreachable(url) {
|
|
58
|
+
console.error(`❌ Could not reach Postgres at ${url}`);
|
|
59
|
+
console.error("\n drizzle-kit pull needs a live Postgres to introspect — any one works, this script has no Docker dependency of its own. Pick one:");
|
|
60
|
+
console.error(" - Local Supabase (needs Docker running): ./supabase/scripts/db_start.sh --reset");
|
|
61
|
+
console.error(" - A native Postgres you already have: CODEGEN_DATABASE_URL=postgresql://... npm run db:codegen");
|
|
62
|
+
console.error(" - A remote/staging database: CODEGEN_DATABASE_URL=postgresql://... npm run db:codegen");
|
|
63
|
+
console.error(" - Zero setup (no Docker/Postgres at all): npm install --save-dev embedded-postgres (auto-used as a fallback)");
|
|
64
|
+
console.error(` Slow/cold-starting target? Raise the timeout: CODEGEN_CONNECT_TIMEOUT_MS=15000 npm run db:codegen`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
60
68
|
function sqlFiles(dir) {
|
|
61
69
|
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
62
70
|
const path = join(dir, entry.name);
|
|
@@ -87,10 +95,24 @@ if (paths.check) {
|
|
|
87
95
|
process.exit(0);
|
|
88
96
|
}
|
|
89
97
|
|
|
90
|
-
|
|
98
|
+
let effectiveDbUrl = paths.dbUrl;
|
|
99
|
+
let ephemeral = null;
|
|
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
|
+
}
|
|
91
107
|
|
|
92
|
-
rmSync(paths.pullDir, { recursive: true, force: true });
|
|
93
|
-
execFileSync('npx', ['drizzle-kit', 'pull', ...(paths.drizzleConfig ? [`--config=${paths.drizzleConfig}`] : [])], {
|
|
108
|
+
rmSync(paths.pullDir, { recursive: true, force: true });
|
|
109
|
+
execFileSync('npx', ['drizzle-kit', 'pull', ...(paths.drizzleConfig ? [`--config=${paths.drizzleConfig}`] : [])], {
|
|
110
|
+
stdio: 'inherit',
|
|
111
|
+
env: { ...process.env, CODEGEN_DATABASE_URL: effectiveDbUrl },
|
|
112
|
+
});
|
|
113
|
+
} finally {
|
|
114
|
+
if (ephemeral) await ephemeral.stop();
|
|
115
|
+
}
|
|
94
116
|
|
|
95
117
|
const pulled = join(paths.pullDir, "schema.ts");
|
|
96
118
|
if (!existsSync(pulled)) {
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Spins up a throwaway, local-only Postgres (via `embedded-postgres`, a
|
|
2
|
+
// prebuilt binary — no Docker) so `cfni-db-codegen` can introspect DDL
|
|
3
|
+
// without any live Postgres already running. Used only as a fallback when
|
|
4
|
+
// no --db-url/CODEGEN_DATABASE_URL was given and nothing is reachable at the
|
|
5
|
+
// local Supabase default.
|
|
6
|
+
import { readFileSync, rmSync } from 'node:fs';
|
|
7
|
+
import { relative } from 'node:path';
|
|
8
|
+
import { Client } from 'pg';
|
|
9
|
+
|
|
10
|
+
const EPHEMERAL_PORT = 54329;
|
|
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
|
+
];
|
|
26
|
+
|
|
27
|
+
/** Starts an ephemeral Postgres, loads every file in `sqlFiles` into it, and
|
|
28
|
+
* returns { url, stop() }. Caller must call stop() when done, even on error. */
|
|
29
|
+
export async function startEphemeralPostgres(sqlFiles) {
|
|
30
|
+
const dataDir = DATA_DIR;
|
|
31
|
+
let EmbeddedPostgres;
|
|
32
|
+
try {
|
|
33
|
+
({ default: EmbeddedPostgres } = await import('embedded-postgres'));
|
|
34
|
+
} catch {
|
|
35
|
+
return null; // optional dep not installed — caller falls back to its normal error message
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
39
|
+
const pg = new EmbeddedPostgres({
|
|
40
|
+
databaseDir: dataDir,
|
|
41
|
+
port: EPHEMERAL_PORT,
|
|
42
|
+
user: 'postgres',
|
|
43
|
+
password: 'postgres',
|
|
44
|
+
persistent: false,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
console.log('ℹ️ No reachable Postgres found — starting an ephemeral one (embedded-postgres, no Docker needed)…');
|
|
48
|
+
try {
|
|
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();
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
await pg.stop().catch(() => { /* best-effort */ });
|
|
73
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
url: EPHEMERAL_URL,
|
|
79
|
+
async stop() {
|
|
80
|
+
await pg.stop().catch(() => { /* best-effort */ });
|
|
81
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -52,6 +52,7 @@ export default function resolveCodegenPaths(argv, env, cwd) {
|
|
|
52
52
|
pullDir: resolve(outDir, '..', '.drizzle-pull'),
|
|
53
53
|
manifest: join(outDir, 'manifest.json'),
|
|
54
54
|
dbUrl: flag(argv, 'db-url') ?? env.CODEGEN_DATABASE_URL ?? DEFAULT_DB_URL,
|
|
55
|
+
dbUrlExplicit: flag(argv, 'db-url') !== undefined || env.CODEGEN_DATABASE_URL !== undefined,
|
|
55
56
|
check: argv.includes('--check'),
|
|
56
57
|
timeoutMs: Number(env.CODEGEN_CONNECT_TIMEOUT_MS) || DEFAULT_TIMEOUT_MS,
|
|
57
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.
|
|
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",
|
|
@@ -243,6 +243,7 @@
|
|
|
243
243
|
"@microsoft/clarity": "^1.0.2",
|
|
244
244
|
"@supabase/supabase-js": "^2.112.3",
|
|
245
245
|
"drizzle-orm": "^0.45.2",
|
|
246
|
+
"embedded-postgres": "^18.4.0-beta.17",
|
|
246
247
|
"firebase": "^12.17.0",
|
|
247
248
|
"jose": "^6.2.8",
|
|
248
249
|
"pg": "^8.23.0"
|