cloudflare-next-intl 0.8.10 → 0.8.12
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/bin/db_codegen.mjs +38 -9
- package/bin/ddl_order.mjs +46 -0
- package/bin/ephemeral_pg.mjs +42 -12
- package/dist/src/db/codegen_paths.d.ts +0 -1
- package/dist/src/db/codegen_paths.js +0 -1
- package/package.json +1 -1
package/bin/db_codegen.mjs
CHANGED
|
@@ -38,7 +38,9 @@ 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
|
|
|
43
|
+
const PACKAGE_ROOT = new URL('..', import.meta.url).pathname;
|
|
42
44
|
const paths = resolveCodegenPaths(process.argv.slice(2), process.env, process.cwd());
|
|
43
45
|
|
|
44
46
|
async function isReachable(url) {
|
|
@@ -96,21 +98,48 @@ if (paths.check) {
|
|
|
96
98
|
|
|
97
99
|
let effectiveDbUrl = paths.dbUrl;
|
|
98
100
|
let ephemeral = null;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
rmSync(paths.pullDir, { recursive: true, force: true });
|
|
101
|
+
// drizzle-kit's own default config resolution looks for `drizzle.config.json`
|
|
102
|
+
// in the cwd and errors out if it's missing — a project has no reason to
|
|
103
|
+
// keep one around just for this script when --db-url/CODEGEN_DATABASE_URL
|
|
104
|
+
// (or the ephemeral fallback) already says everything drizzle-kit needs. So
|
|
105
|
+
// when the caller didn't pass --drizzle-config, generate one on the fly.
|
|
106
|
+
let generatedConfigPath = null;
|
|
107
107
|
try {
|
|
108
|
-
|
|
108
|
+
if (!(await isReachable(paths.dbUrl))) {
|
|
109
|
+
if (paths.dbUrlExplicit) failUnreachable(paths.dbUrl);
|
|
110
|
+
ephemeral = await startEphemeralPostgres(orderedSqlFiles(paths.ddlDir));
|
|
111
|
+
if (!ephemeral) failUnreachable(paths.dbUrl);
|
|
112
|
+
effectiveDbUrl = ephemeral.url;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let configPath = paths.drizzleConfig;
|
|
116
|
+
if (!configPath) {
|
|
117
|
+
generatedConfigPath = join(paths.pullDir, '..', '.drizzle-config.json');
|
|
118
|
+
mkdirSync(join(paths.pullDir, '..'), { recursive: true });
|
|
119
|
+
writeFileSync(generatedConfigPath, JSON.stringify({
|
|
120
|
+
out: paths.pullDir,
|
|
121
|
+
dialect: 'postgresql',
|
|
122
|
+
dbCredentials: { url: effectiveDbUrl },
|
|
123
|
+
}, null, 2));
|
|
124
|
+
configPath = generatedConfigPath;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
rmSync(paths.pullDir, { recursive: true, force: true });
|
|
128
|
+
// drizzle-kit itself requires `drizzle-orm` at runtime. This package
|
|
129
|
+
// depends on drizzle-orm, but npm doesn't guarantee that dependency gets
|
|
130
|
+
// hoisted to the consuming project's own node_modules (it commonly stays
|
|
131
|
+
// nested under node_modules/cloudflare-next-intl/node_modules) — so
|
|
132
|
+
// running from *this* package's own directory, where it's guaranteed
|
|
133
|
+
// resolvable, avoids "please install required packages: drizzle-orm"
|
|
134
|
+
// when a consumer doesn't happen to have it hoisted.
|
|
135
|
+
execFileSync('npx', ['drizzle-kit', 'pull', `--config=${configPath}`], {
|
|
109
136
|
stdio: 'inherit',
|
|
137
|
+
cwd: PACKAGE_ROOT,
|
|
110
138
|
env: { ...process.env, CODEGEN_DATABASE_URL: effectiveDbUrl },
|
|
111
139
|
});
|
|
112
140
|
} finally {
|
|
113
141
|
if (ephemeral) await ephemeral.stop();
|
|
142
|
+
if (generatedConfigPath) rmSync(generatedConfigPath, { force: true });
|
|
114
143
|
}
|
|
115
144
|
|
|
116
145
|
const pulled = join(paths.pullDir, "schema.ts");
|
|
@@ -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
|
+
}
|
package/bin/ephemeral_pg.mjs
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
}
|
|
44
|
-
await
|
|
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 {
|
|
@@ -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),
|