create-mercato-app 0.6.1-develop.3164.d94717d609 → 0.6.1-develop.3167.1.7aedd7b2ba
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 +13 -0
- package/package.json +1 -1
- package/template/AGENTS.md +14 -0
- package/template/scripts/dev-database-url.mjs +343 -0
- package/template/scripts/dev.mjs +39 -0
- package/template/scripts/setup.mjs +12 -3
package/README.md
CHANGED
|
@@ -126,6 +126,19 @@ The standalone dev splash also exposes a GitHub publishing panel after `yarn dev
|
|
|
126
126
|
yarn setup:reinstall
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
To run several persistent local apps against the same PostgreSQL server, pass an optional database-name override. The flag is purely additive — omitting it preserves existing behavior.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# explicit name; .env is updated by default after a confirmation prompt
|
|
133
|
+
yarn setup --database-name=client_a
|
|
134
|
+
|
|
135
|
+
# bare flag derives the database name from the current directory name
|
|
136
|
+
yarn setup --database-name
|
|
137
|
+
|
|
138
|
+
# one-off run that only injects DATABASE_URL into the current child env
|
|
139
|
+
yarn dev --database-name=review_1720 --no-update-env
|
|
140
|
+
```
|
|
141
|
+
|
|
129
142
|
3. Manual alternative if you want to edit the environment first:
|
|
130
143
|
```bash
|
|
131
144
|
cp .env.example .env
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -105,6 +105,20 @@ yarn reinstall
|
|
|
105
105
|
| `OM_DEV_SPLASH_CLAUDE_CODE_PATH` | auto-detect | Optional path override for the Claude Code CLI. |
|
|
106
106
|
| `OM_DEV_SPLASH_CODEX_PATH` | auto-detect | Optional path override for the Codex CLI. |
|
|
107
107
|
| `OM_DEV_AUTO_MIGRATE` | `1` | When set to `1` (default), `yarn dev` runs `yarn db:migrate` once at startup before Next.js boots. Set to `0` to disable. See "Single-shot Database Migrations" below. |
|
|
108
|
+
| `OM_DEV_DATABASE_NAME` | unset | Same as passing `--database-name=<value>` to `yarn dev` / `yarn setup`. CLI flag wins. |
|
|
109
|
+
| `OM_DEV_DATABASE_UPDATE_ENV` | unset | Non-interactive answer for the `.env` update prompt (`true`/`false`). Equivalent to `--update-env` / `--no-update-env`. |
|
|
110
|
+
|
|
111
|
+
### Persistent Parallel Local Databases
|
|
112
|
+
|
|
113
|
+
Add `--database-name[=<name>]` to `yarn dev`, `yarn dev:greenfield`, or `yarn setup` to point this app at an isolated PostgreSQL database without manually editing `.env` first. Behavior:
|
|
114
|
+
|
|
115
|
+
- `yarn dev` (no flag) is unchanged — no prompt, no `.env` mutation.
|
|
116
|
+
- `yarn setup --database-name=client_a` rewrites the `DATABASE_URL` database segment in `./.env` after a confirmation prompt (default yes).
|
|
117
|
+
- `yarn dev --database-name` (bare flag) derives the database name from the current directory.
|
|
118
|
+
- `yarn dev --database-name=review_1720 --no-update-env` injects the rewritten URL into the current child process only and leaves `.env` untouched.
|
|
119
|
+
- Non-interactive runs (`CI=true` or piped stdin) default to updating `.env`; pass `--no-update-env` to opt out.
|
|
120
|
+
|
|
121
|
+
The override only changes the database segment of `DATABASE_URL`. Credentials, host, port, query strings (`?schema=…`, `?sslmode=…`), and other env variables are preserved verbatim.
|
|
108
122
|
|
|
109
123
|
## Infrastructure
|
|
110
124
|
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import readline from 'node:readline'
|
|
4
|
+
|
|
5
|
+
const DATABASE_URL_KEY = 'DATABASE_URL'
|
|
6
|
+
const DATABASE_NAME_FLAG = '--database-name'
|
|
7
|
+
const NO_UPDATE_ENV_FLAG = '--no-update-env'
|
|
8
|
+
const UPDATE_ENV_FLAG = '--update-env'
|
|
9
|
+
const DATABASE_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/
|
|
10
|
+
const MAX_DATABASE_NAME_LENGTH = 63
|
|
11
|
+
|
|
12
|
+
export function collectForwardedSetupFlags(argv) {
|
|
13
|
+
const out = []
|
|
14
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
15
|
+
const arg = argv[index]
|
|
16
|
+
if (arg === DATABASE_NAME_FLAG) {
|
|
17
|
+
out.push(arg)
|
|
18
|
+
const next = argv[index + 1]
|
|
19
|
+
if (typeof next === 'string' && !next.startsWith('-')) {
|
|
20
|
+
out.push(next)
|
|
21
|
+
index += 1
|
|
22
|
+
}
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
if (typeof arg === 'string' && arg.startsWith(`${DATABASE_NAME_FLAG}=`)) {
|
|
26
|
+
out.push(arg)
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
if (arg === NO_UPDATE_ENV_FLAG || arg === UPDATE_ENV_FLAG) {
|
|
30
|
+
out.push(arg)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return out
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseDatabaseNameArgs(argv) {
|
|
37
|
+
const remaining = []
|
|
38
|
+
let provided = false
|
|
39
|
+
let rawValue = null
|
|
40
|
+
let updateEnv = null
|
|
41
|
+
|
|
42
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
43
|
+
const arg = argv[index]
|
|
44
|
+
if (arg === DATABASE_NAME_FLAG) {
|
|
45
|
+
provided = true
|
|
46
|
+
const next = argv[index + 1]
|
|
47
|
+
if (typeof next === 'string' && !next.startsWith('-')) {
|
|
48
|
+
rawValue = next
|
|
49
|
+
index += 1
|
|
50
|
+
} else {
|
|
51
|
+
rawValue = null
|
|
52
|
+
}
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
if (typeof arg === 'string' && arg.startsWith(`${DATABASE_NAME_FLAG}=`)) {
|
|
56
|
+
provided = true
|
|
57
|
+
rawValue = arg.slice(DATABASE_NAME_FLAG.length + 1)
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
if (arg === NO_UPDATE_ENV_FLAG) {
|
|
61
|
+
updateEnv = false
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
if (arg === UPDATE_ENV_FLAG) {
|
|
65
|
+
updateEnv = true
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
remaining.push(arg)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
provided,
|
|
73
|
+
rawValue,
|
|
74
|
+
updateEnv,
|
|
75
|
+
remainingArgv: remaining,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function deriveDatabaseNameFromCwd(cwd) {
|
|
80
|
+
const basename = path.basename(String(cwd ?? ''))
|
|
81
|
+
const lower = basename.toLowerCase()
|
|
82
|
+
const withUnderscores = lower.replace(/[^a-z0-9]+/g, '_')
|
|
83
|
+
const trimmed = withUnderscores.replace(/^_+|_+$/g, '')
|
|
84
|
+
if (!trimmed) return 'open_mercato_dev'
|
|
85
|
+
if (/^[0-9]/.test(trimmed)) return `om_${trimmed}`
|
|
86
|
+
return trimmed
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function validateDatabaseName(name) {
|
|
90
|
+
if (typeof name !== 'string') {
|
|
91
|
+
return { ok: false, reason: 'Database name must be a string.' }
|
|
92
|
+
}
|
|
93
|
+
if (name.length === 0) {
|
|
94
|
+
return { ok: false, reason: 'Database name must not be empty.' }
|
|
95
|
+
}
|
|
96
|
+
if (name.length > MAX_DATABASE_NAME_LENGTH) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
reason: `Database name must be ${MAX_DATABASE_NAME_LENGTH} characters or fewer.`,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!DATABASE_NAME_PATTERN.test(name)) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
reason: 'Database name must start with a letter or underscore and contain only letters, digits, underscores, or hyphens.',
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return { ok: true }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function resolveDatabaseName({ rawValue, cwd }) {
|
|
112
|
+
const trimmed = typeof rawValue === 'string' ? rawValue.trim() : ''
|
|
113
|
+
if (!trimmed) {
|
|
114
|
+
const derived = deriveDatabaseNameFromCwd(cwd)
|
|
115
|
+
return { name: derived, source: 'cwd' }
|
|
116
|
+
}
|
|
117
|
+
return { name: trimmed, source: 'explicit' }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function rewriteDatabaseUrl(url, databaseName) {
|
|
121
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
122
|
+
throw new Error('DATABASE_URL is empty.')
|
|
123
|
+
}
|
|
124
|
+
const parsed = new URL(url)
|
|
125
|
+
parsed.pathname = `/${encodeURIComponent(databaseName)}`
|
|
126
|
+
return parsed.toString()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function updateDatabaseUrlInEnvText(source, databaseName) {
|
|
130
|
+
const lines = source.split(/\r?\n/)
|
|
131
|
+
let replaced = false
|
|
132
|
+
let changed = false
|
|
133
|
+
let previousValue = null
|
|
134
|
+
let nextValue = null
|
|
135
|
+
|
|
136
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
137
|
+
const line = lines[index]
|
|
138
|
+
const match = line.match(/^(\s*(?:export\s+)?DATABASE_URL\s*=\s*)(.*)$/)
|
|
139
|
+
if (!match) continue
|
|
140
|
+
const prefix = match[1]
|
|
141
|
+
const rawCurrent = match[2]
|
|
142
|
+
const { value: currentValue, quote } = stripEnvValueQuotes(rawCurrent)
|
|
143
|
+
if (replaced) {
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
replaced = true
|
|
147
|
+
previousValue = currentValue
|
|
148
|
+
try {
|
|
149
|
+
nextValue = rewriteDatabaseUrl(currentValue, databaseName)
|
|
150
|
+
} catch (error) {
|
|
151
|
+
throw new Error(`Failed to rewrite ${DATABASE_URL_KEY}: ${error instanceof Error ? error.message : String(error)}`)
|
|
152
|
+
}
|
|
153
|
+
if (nextValue === currentValue) {
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
lines[index] = `${prefix}${quote}${nextValue}${quote}`
|
|
157
|
+
changed = true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!replaced) {
|
|
161
|
+
throw new Error(`No ${DATABASE_URL_KEY} entry found in env file.`)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
text: lines.join('\n'),
|
|
166
|
+
changed,
|
|
167
|
+
previousValue,
|
|
168
|
+
nextValue,
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function stripEnvValueQuotes(rawValue) {
|
|
173
|
+
if (typeof rawValue !== 'string') return { value: '', quote: '' }
|
|
174
|
+
const trimmed = rawValue.replace(/\s+#.*$/, '').trim()
|
|
175
|
+
if (
|
|
176
|
+
trimmed.length >= 2
|
|
177
|
+
&& (
|
|
178
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"'))
|
|
179
|
+
|| (trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
180
|
+
)
|
|
181
|
+
) {
|
|
182
|
+
return { value: trimmed.slice(1, -1), quote: trimmed[0] }
|
|
183
|
+
}
|
|
184
|
+
return { value: trimmed, quote: '' }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function readEnvDatabaseUrl(source) {
|
|
188
|
+
for (const line of source.split(/\r?\n/)) {
|
|
189
|
+
const match = line.match(/^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/)
|
|
190
|
+
if (!match) continue
|
|
191
|
+
return stripEnvValueQuotes(match[1]).value
|
|
192
|
+
}
|
|
193
|
+
return null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function isNonInteractiveEnvironment({ env, stdinIsTTY }) {
|
|
197
|
+
if (env && typeof env === 'object') {
|
|
198
|
+
const ci = String(env.CI ?? '').trim().toLowerCase()
|
|
199
|
+
if (['1', 'true', 'yes', 'on'].includes(ci)) return true
|
|
200
|
+
}
|
|
201
|
+
if (stdinIsTTY === false) return true
|
|
202
|
+
return false
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function parseUpdateEnvAnswer(answer) {
|
|
206
|
+
if (typeof answer !== 'string') return null
|
|
207
|
+
const normalized = answer.trim().toLowerCase()
|
|
208
|
+
if (normalized === '') return true
|
|
209
|
+
if (['y', 'yes', '1', 'true'].includes(normalized)) return true
|
|
210
|
+
if (['n', 'no', '0', 'false'].includes(normalized)) return false
|
|
211
|
+
return null
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function promptUpdateEnv({ databaseName, input, output }) {
|
|
215
|
+
if (!input || !output) return true
|
|
216
|
+
const rl = readline.createInterface({ input, output })
|
|
217
|
+
try {
|
|
218
|
+
return await new Promise((resolve) => {
|
|
219
|
+
rl.question(`[dev] Update .env to use database "${databaseName}"? [Y/n] `, (answer) => {
|
|
220
|
+
const parsed = parseUpdateEnvAnswer(answer)
|
|
221
|
+
resolve(parsed === null ? true : parsed)
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
} finally {
|
|
225
|
+
rl.close()
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function resolveUpdateEnvDecisionFromEnv(env) {
|
|
230
|
+
if (!env || typeof env !== 'object') return null
|
|
231
|
+
const raw = env.OM_DEV_DATABASE_UPDATE_ENV
|
|
232
|
+
if (typeof raw !== 'string') return null
|
|
233
|
+
const normalized = raw.trim().toLowerCase()
|
|
234
|
+
if (['', '1', 'true', 'yes', 'on', 'y'].includes(normalized)) return true
|
|
235
|
+
if (['0', 'false', 'no', 'off', 'n'].includes(normalized)) return false
|
|
236
|
+
return null
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function readDatabaseNameEnvOverride(env) {
|
|
240
|
+
if (!env || typeof env !== 'object') return null
|
|
241
|
+
const raw = env.OM_DEV_DATABASE_NAME
|
|
242
|
+
if (typeof raw !== 'string') return null
|
|
243
|
+
const trimmed = raw.trim()
|
|
244
|
+
if (trimmed.length === 0) return null
|
|
245
|
+
return trimmed
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function resolveDatabaseNameOverride(options) {
|
|
249
|
+
const {
|
|
250
|
+
argv = [],
|
|
251
|
+
env = {},
|
|
252
|
+
cwd = process.cwd(),
|
|
253
|
+
envFilePath,
|
|
254
|
+
stdin = null,
|
|
255
|
+
stdout = null,
|
|
256
|
+
logger = noopLogger(),
|
|
257
|
+
fsImpl = fs,
|
|
258
|
+
} = options
|
|
259
|
+
|
|
260
|
+
const parsed = parseDatabaseNameArgs(argv)
|
|
261
|
+
const envOverrideName = readDatabaseNameEnvOverride(env)
|
|
262
|
+
|
|
263
|
+
const flagPresent = parsed.provided || envOverrideName !== null
|
|
264
|
+
if (!flagPresent) {
|
|
265
|
+
return { applied: false, remainingArgv: parsed.remainingArgv }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const rawValue = parsed.provided ? parsed.rawValue : envOverrideName
|
|
269
|
+
const resolved = resolveDatabaseName({ rawValue, cwd })
|
|
270
|
+
|
|
271
|
+
const validation = validateDatabaseName(resolved.name)
|
|
272
|
+
if (!validation.ok) {
|
|
273
|
+
throw new Error(`Invalid database name "${resolved.name}": ${validation.reason}`)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!envFilePath) {
|
|
277
|
+
throw new Error('Cannot resolve env file path for database-name override.')
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!fsImpl.existsSync(envFilePath)) {
|
|
281
|
+
throw new Error(`Env file not found at ${envFilePath}. Cannot apply --database-name.`)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const envSource = fsImpl.readFileSync(envFilePath, 'utf8')
|
|
285
|
+
const previousUrl = readEnvDatabaseUrl(envSource)
|
|
286
|
+
if (!previousUrl) {
|
|
287
|
+
throw new Error(`Env file ${envFilePath} does not declare ${DATABASE_URL_KEY}.`)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const rewritten = updateDatabaseUrlInEnvText(envSource, resolved.name)
|
|
291
|
+
|
|
292
|
+
logger.info?.(`[dev] Using database "${resolved.name}" from ${parsed.provided ? '--database-name' : 'OM_DEV_DATABASE_NAME'}.`)
|
|
293
|
+
|
|
294
|
+
let updateEnvDecision
|
|
295
|
+
if (parsed.updateEnv === false) {
|
|
296
|
+
updateEnvDecision = false
|
|
297
|
+
} else if (parsed.updateEnv === true) {
|
|
298
|
+
updateEnvDecision = true
|
|
299
|
+
} else {
|
|
300
|
+
const envDecision = resolveUpdateEnvDecisionFromEnv(env)
|
|
301
|
+
if (envDecision !== null) {
|
|
302
|
+
updateEnvDecision = envDecision
|
|
303
|
+
} else if (isNonInteractiveEnvironment({ env, stdinIsTTY: stdin?.isTTY })) {
|
|
304
|
+
updateEnvDecision = true
|
|
305
|
+
} else {
|
|
306
|
+
updateEnvDecision = await promptUpdateEnv({
|
|
307
|
+
databaseName: resolved.name,
|
|
308
|
+
input: stdin,
|
|
309
|
+
output: stdout,
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (updateEnvDecision) {
|
|
315
|
+
if (rewritten.changed) {
|
|
316
|
+
fsImpl.writeFileSync(envFilePath, rewritten.text)
|
|
317
|
+
logger.info?.(`[dev] Updated ${path.basename(envFilePath)} ${DATABASE_URL_KEY}.`)
|
|
318
|
+
} else {
|
|
319
|
+
logger.info?.(`[dev] ${path.basename(envFilePath)} already targets database "${resolved.name}".`)
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
logger.info?.(`[dev] Leaving ${path.basename(envFilePath)} unchanged; child commands will use database "${resolved.name}" for this run.`)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
applied: true,
|
|
327
|
+
databaseName: resolved.name,
|
|
328
|
+
source: resolved.source,
|
|
329
|
+
envFilePath,
|
|
330
|
+
previousDatabaseUrl: previousUrl,
|
|
331
|
+
nextDatabaseUrl: rewritten.nextValue,
|
|
332
|
+
envFileUpdated: updateEnvDecision && rewritten.changed,
|
|
333
|
+
envFileWriteSkipped: !updateEnvDecision,
|
|
334
|
+
childEnv: { [DATABASE_URL_KEY]: rewritten.nextValue },
|
|
335
|
+
remainingArgv: parsed.remainingArgv,
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function noopLogger() {
|
|
340
|
+
return { info: () => {}, warn: () => {}, error: () => {} }
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export const __DATABASE_URL_KEY__ = DATABASE_URL_KEY
|
package/template/scripts/dev.mjs
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
resolveDevBaseUrl,
|
|
31
31
|
resolveSplashUrl as resolveSplashAccessUrl,
|
|
32
32
|
} from './dev-splash-url.mjs'
|
|
33
|
+
import { resolveDatabaseNameOverride } from './dev-database-url.mjs'
|
|
33
34
|
|
|
34
35
|
function detectDevRuntimeMode() {
|
|
35
36
|
const cwd = process.cwd()
|
|
@@ -573,6 +574,39 @@ function ensureStandaloneEnvFile() {
|
|
|
573
574
|
}
|
|
574
575
|
}
|
|
575
576
|
|
|
577
|
+
function resolveDatabaseEnvFilePath() {
|
|
578
|
+
return isMonorepo
|
|
579
|
+
? path.join(process.cwd(), 'apps', 'mercato', '.env')
|
|
580
|
+
: path.join(process.cwd(), '.env')
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async function applyDatabaseNameOverrideIfRequested() {
|
|
584
|
+
let result
|
|
585
|
+
try {
|
|
586
|
+
result = await resolveDatabaseNameOverride({
|
|
587
|
+
argv: args,
|
|
588
|
+
env: process.env,
|
|
589
|
+
cwd: process.cwd(),
|
|
590
|
+
envFilePath: resolveDatabaseEnvFilePath(),
|
|
591
|
+
stdin: process.stdin,
|
|
592
|
+
stdout: process.stdout,
|
|
593
|
+
logger: { info: (msg) => console.log(msg) },
|
|
594
|
+
})
|
|
595
|
+
} catch (error) {
|
|
596
|
+
console.error(`❌ ${error instanceof Error ? error.message : String(error)}`)
|
|
597
|
+
shutdown(1)
|
|
598
|
+
return null
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (result?.applied) {
|
|
602
|
+
process.env.DATABASE_URL = result.childEnv.DATABASE_URL
|
|
603
|
+
updateSplashState({
|
|
604
|
+
activity: `Using database "${result.databaseName}" for this run`,
|
|
605
|
+
})
|
|
606
|
+
}
|
|
607
|
+
return result
|
|
608
|
+
}
|
|
609
|
+
|
|
576
610
|
function normalizeLocaleToken(value) {
|
|
577
611
|
return String(value ?? '').trim().toLowerCase().replace(/_/g, '-')
|
|
578
612
|
}
|
|
@@ -1613,6 +1647,7 @@ async function runClassicGreenfieldDev() {
|
|
|
1613
1647
|
|
|
1614
1648
|
async function runStandaloneSetup() {
|
|
1615
1649
|
ensureStandaloneEnvFile()
|
|
1650
|
+
await applyDatabaseNameOverrideIfRequested()
|
|
1616
1651
|
if (standaloneLocalRegistryRefresh) {
|
|
1617
1652
|
await runStage('🧼 Clearing local Open Mercato cache', ['cache', 'clean', '--all'], {
|
|
1618
1653
|
stageCurrent: 0,
|
|
@@ -1639,6 +1674,7 @@ async function runStandaloneSetup() {
|
|
|
1639
1674
|
|
|
1640
1675
|
async function runClassicStandaloneSetup() {
|
|
1641
1676
|
ensureStandaloneEnvFile()
|
|
1677
|
+
await applyDatabaseNameOverrideIfRequested()
|
|
1642
1678
|
if (standaloneLocalRegistryRefresh) {
|
|
1643
1679
|
await runRawYarnCommand(['cache', 'clean', '--all'])
|
|
1644
1680
|
}
|
|
@@ -1675,6 +1711,7 @@ async function main() {
|
|
|
1675
1711
|
await runStandaloneSetup()
|
|
1676
1712
|
return
|
|
1677
1713
|
}
|
|
1714
|
+
await applyDatabaseNameOverrideIfRequested()
|
|
1678
1715
|
if (classic) {
|
|
1679
1716
|
await runClassicStandaloneDev()
|
|
1680
1717
|
return
|
|
@@ -1699,6 +1736,8 @@ async function main() {
|
|
|
1699
1736
|
return
|
|
1700
1737
|
}
|
|
1701
1738
|
|
|
1739
|
+
await applyDatabaseNameOverrideIfRequested()
|
|
1740
|
+
|
|
1702
1741
|
if (appOnly) {
|
|
1703
1742
|
launchMonorepoAppDev()
|
|
1704
1743
|
return
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process'
|
|
2
2
|
import { existsSync } from 'node:fs'
|
|
3
|
+
import { collectForwardedSetupFlags } from './dev-database-url.mjs'
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
5
|
+
const argv = process.argv.slice(2)
|
|
6
|
+
const reinstall = argv.includes('--reinstall')
|
|
7
|
+
const classic = argv.includes('--classic')
|
|
8
|
+
const forwardedDatabaseFlags = collectForwardedSetupFlags(argv)
|
|
6
9
|
|
|
7
10
|
if (!existsSync('node_modules/cross-spawn')) {
|
|
8
11
|
const bootstrap = spawnSync('yarn', ['install'], {
|
|
@@ -14,7 +17,13 @@ if (!existsSync('node_modules/cross-spawn')) {
|
|
|
14
17
|
|
|
15
18
|
const result = spawnSync(
|
|
16
19
|
process.execPath,
|
|
17
|
-
[
|
|
20
|
+
[
|
|
21
|
+
'./scripts/dev.mjs',
|
|
22
|
+
'--setup',
|
|
23
|
+
...(reinstall ? ['--reinstall'] : []),
|
|
24
|
+
...(classic ? ['--classic'] : []),
|
|
25
|
+
...forwardedDatabaseFlags,
|
|
26
|
+
],
|
|
18
27
|
{
|
|
19
28
|
stdio: 'inherit',
|
|
20
29
|
shell: process.platform === 'win32',
|