@marvalt/digivalt-core 0.2.0 โ†’ 0.2.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.1] - 2026-03-30
9
+
10
+ ### Changed
11
+ - Upgraded `digivalt-init` from a template copier into a preflight tool that inspects DigiVAlt wiring in existing Lovable/Vite apps.
12
+ - Added safe `package.json` patching for `generate`, `build`, and `build:dev` when apps are still on plain Vite scripts.
13
+ - Added stale `scripts/generate.ts` refresh for known DigiVAlt-managed legacy versions.
14
+ - Added warnings for legacy workarounds such as alias-link scripts, old root generator imports, and Vite `noExternal` compatibility hacks.
15
+ - Expanded smoke coverage for fresh installs, upgrades, idempotent reruns, custom build scripts, and dry-run previews.
16
+
17
+ ### Migration Notes
18
+ - Rerun `npx digivalt-init` after upgrading to `0.2.1` so existing apps can pick up the improved build wiring checks and warnings.
19
+
8
20
  ## [0.2.0] - 2026-02-19
9
21
 
10
22
  ### Changed
package/README.md CHANGED
@@ -23,12 +23,27 @@ The root package export remains as a thin compatibility layer for generator-focu
23
23
 
24
24
  ## Init Workflow
25
25
 
26
- Run `npx digivalt-init` inside a Vite/Lovable app root. The command now validates the target folder before copying templates.
26
+ Run `npx digivalt-init` inside a Vite/Lovable app root. The command now validates the target folder, inspects existing DigiVAlt wiring, and patches safe cases automatically.
27
27
 
28
28
  Useful flags:
29
29
 
30
30
  - `--target <path>` to initialize a different app folder
31
31
  - `--dry-run` to preview copied files without writing anything
32
+ - `--force-template` to overwrite DigiVAlt-managed template files intentionally
33
+
34
+ What init now does:
35
+
36
+ - copies missing template files into the app
37
+ - adds or updates `generate`, `build`, and `build:dev` scripts when the app is still on plain Vite wiring
38
+ - refreshes `scripts/generate.ts` when it matches an older DigiVAlt-managed version
39
+ - warns instead of overwriting custom build logic or custom generator scripts
40
+ - reports stale legacy workarounds such as alias-link scripts and old root package generator imports
41
+
42
+ When to rerun it:
43
+
44
+ - after first installing DigiVAlt in a fresh app
45
+ - after upgrading `@marvalt/digivalt-core` when scaffolded build wiring changes
46
+ - before publishing a new app if you want a quick preflight of the DigiVAlt setup
32
47
 
33
48
  ## Cloudflare Secrets
34
49
 
package/bin/init.cjs CHANGED
@@ -6,19 +6,65 @@ const path = require('path');
6
6
  const sourceDir = path.join(__dirname, '..', 'template');
7
7
  const args = process.argv.slice(2);
8
8
 
9
+ const EXPECTED_GENERATE_SCRIPT =
10
+ 'npx vite-node --options.deps.inline="@marvalt/digivalt-core/generators" scripts/generate.ts';
11
+ const EXPECTED_BUILD_SCRIPT = `${EXPECTED_GENERATE_SCRIPT} && vite build`;
12
+ const EXPECTED_BUILD_DEV_SCRIPT = `${EXPECTED_GENERATE_SCRIPT} && vite build --mode development`;
13
+
14
+ const TEMPLATE_MANAGED_FILES = new Set([
15
+ '.dev.vars.example',
16
+ 'DIGIVALT_SETUP.md',
17
+ 'functions/api/fetch-with-access.ts',
18
+ 'functions/api/gravity-forms-submit.ts',
19
+ 'functions/api/mautic-submit.ts',
20
+ 'functions/api/webhook.js',
21
+ 'scripts/deploy-secrets.js',
22
+ 'scripts/generate.ts',
23
+ 'wrangler.toml',
24
+ ]);
25
+
26
+ const LEGACY_GENERATE_MARKERS = [
27
+ "import { generators } from '@marvalt/digivalt-core';",
28
+ 'const gfSuccess = await generators.generateGravityFormsData();',
29
+ 'const mauticSuccess = await generators.generateMauticData();',
30
+ "import dotenv from 'dotenv';",
31
+ 'dotenv.config();',
32
+ ];
33
+
34
+ const LEGACY_BUILD_MARKERS = [
35
+ 'link-digivalt-alias',
36
+ '@marvalt/digivalt-core',
37
+ ];
38
+
9
39
  function getArgValue(flag) {
10
40
  const index = args.indexOf(flag);
11
41
  return index >= 0 ? args[index + 1] : undefined;
12
42
  }
13
43
 
14
44
  const dryRun = args.includes('--dry-run');
45
+ const forceTemplate = args.includes('--force-template');
15
46
  const explicitTarget = getArgValue('--target');
16
47
  const targetDir = path.resolve(explicitTarget || process.cwd());
17
48
 
18
- console.log('๐Ÿš€ Initializing DigiVAlt Cloudflare Proxy Templates...');
19
- console.log(`๐Ÿ“ Target directory: ${targetDir}`);
20
- if (dryRun) {
21
- console.log('๐Ÿงช Dry run enabled. No files will be written.');
49
+ const report = {
50
+ copied: [],
51
+ overwritten: [],
52
+ skipped: [],
53
+ patchedScripts: [],
54
+ refreshedFiles: [],
55
+ warnings: [],
56
+ manualActions: [],
57
+ };
58
+
59
+ function logHeader() {
60
+ console.log('๐Ÿš€ Initializing DigiVAlt Cloudflare Proxy Templates...');
61
+ console.log(`๐Ÿ“ Target directory: ${targetDir}`);
62
+ if (dryRun) {
63
+ console.log('๐Ÿงช Dry run enabled. No files will be written.');
64
+ }
65
+ if (forceTemplate) {
66
+ console.log('โ™ป๏ธ Force template mode enabled for DigiVAlt-managed files.');
67
+ }
22
68
  }
23
69
 
24
70
  function ensureValidTarget(dir) {
@@ -37,41 +83,341 @@ function ensureValidTarget(dir) {
37
83
  'No package.json found in the target directory. Run `digivalt-init` inside a Vite/Lovable app root, or pass `--target <app-path>`.'
38
84
  );
39
85
  }
86
+
87
+ return packageJsonPath;
88
+ }
89
+
90
+ function normalizeLineEndings(value) {
91
+ return value.replace(/\r\n/g, '\n').trim();
92
+ }
93
+
94
+ function normalizeScript(value) {
95
+ return String(value || '')
96
+ .replace(/\s+/g, ' ')
97
+ .trim();
98
+ }
99
+
100
+ function readTextIfExists(filePath) {
101
+ return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
40
102
  }
41
103
 
42
- function copyRecursiveSync(src, dest) {
43
- const exists = fs.existsSync(src);
44
- const stats = exists && fs.statSync(src);
45
- const isDirectory = exists && stats.isDirectory();
46
-
47
- if (isDirectory) {
48
- if (!fs.existsSync(dest) && !dryRun) fs.mkdirSync(dest, { recursive: true });
49
- fs.readdirSync(src).forEach((childItemName) => {
50
- copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName));
104
+ function writeFileWithMode(filePath, content) {
105
+ if (!dryRun) {
106
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
107
+ fs.writeFileSync(filePath, content);
108
+ }
109
+ }
110
+
111
+ function relativeDisplay(filePath) {
112
+ const relativePath = path.relative(targetDir, filePath).replace(/\\/g, '/');
113
+ return relativePath ? `/${relativePath}` : '/';
114
+ }
115
+
116
+ function listTemplateFiles(dir, baseDir = dir) {
117
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
118
+ const results = [];
119
+
120
+ for (const entry of entries) {
121
+ const fullPath = path.join(dir, entry.name);
122
+ if (entry.isDirectory()) {
123
+ results.push(...listTemplateFiles(fullPath, baseDir));
124
+ continue;
125
+ }
126
+
127
+ results.push({
128
+ src: fullPath,
129
+ relativePath: path.relative(baseDir, fullPath).replace(/\\/g, '/'),
51
130
  });
131
+ }
132
+
133
+ return results;
134
+ }
135
+
136
+ function isKnownLegacyGenerateScript(content) {
137
+ if (!content) return false;
138
+ return LEGACY_GENERATE_MARKERS.some((marker) => content.includes(marker));
139
+ }
140
+
141
+ function isCurrentGenerateScript(content, templateContent) {
142
+ if (!content) return false;
143
+ return normalizeLineEndings(content) === normalizeLineEndings(templateContent);
144
+ }
145
+
146
+ function scriptRunsGenerate(command) {
147
+ const normalized = normalizeScript(command);
148
+ return normalized.includes('scripts/generate.ts') || normalized.includes('npm run generate');
149
+ }
150
+
151
+ function isExpectedBuildScript(command) {
152
+ return normalizeScript(command) === normalizeScript(EXPECTED_BUILD_SCRIPT);
153
+ }
154
+
155
+ function isExpectedBuildDevScript(command) {
156
+ return normalizeScript(command) === normalizeScript(EXPECTED_BUILD_DEV_SCRIPT);
157
+ }
158
+
159
+ function isPlainViteBuild(command) {
160
+ return normalizeScript(command) === 'vite build';
161
+ }
162
+
163
+ function isPlainViteBuildDev(command) {
164
+ return normalizeScript(command) === 'vite build --mode development';
165
+ }
166
+
167
+ function isLegacyGenerateCommand(command) {
168
+ const normalized = normalizeScript(command);
169
+ return normalized === 'vite-node scripts/generate.ts' || normalized === 'npx vite-node scripts/generate.ts';
170
+ }
171
+
172
+ function readJson(filePath) {
173
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
174
+ }
175
+
176
+ function writeJson(filePath, value) {
177
+ const content = `${JSON.stringify(value, null, 2)}\n`;
178
+ if (!dryRun) {
179
+ fs.writeFileSync(filePath, content);
180
+ }
181
+ }
182
+
183
+ function inspectTargetApp(targetPackageJsonPath, templateGenerateScript) {
184
+ const packageJson = readJson(targetPackageJsonPath);
185
+ const scripts = packageJson.scripts || {};
186
+ const deps = packageJson.dependencies || {};
187
+ const devDeps = packageJson.devDependencies || {};
188
+ const generateScriptPath = path.join(targetDir, 'scripts', 'generate.ts');
189
+ const generateScriptContent = readTextIfExists(generateScriptPath);
190
+ const viteConfigCandidates = ['vite.config.ts', 'vite.config.js', 'vite.config.mts', 'vite.config.mjs'];
191
+ const legacyViteConfigs = [];
192
+
193
+ for (const candidate of viteConfigCandidates) {
194
+ const configPath = path.join(targetDir, candidate);
195
+ const content = readTextIfExists(configPath);
196
+ if (!content) continue;
197
+
198
+ if (content.includes('noExternal') && content.includes('@marvalt/digivalt-core')) {
199
+ legacyViteConfigs.push(candidate);
200
+ }
201
+ }
202
+
203
+ const legacyHackFiles = [];
204
+ const aliasLinkPath = path.join(targetDir, 'scripts', 'link-digivalt-alias.cjs');
205
+ if (fs.existsSync(aliasLinkPath)) {
206
+ legacyHackFiles.push('scripts/link-digivalt-alias.cjs');
207
+ }
208
+
209
+ const postinstall = scripts.postinstall || '';
210
+ const generateState = !generateScriptContent
211
+ ? 'missing'
212
+ : isCurrentGenerateScript(generateScriptContent, templateGenerateScript)
213
+ ? 'current'
214
+ : isKnownLegacyGenerateScript(generateScriptContent)
215
+ ? 'legacy'
216
+ : 'custom';
217
+
218
+ return {
219
+ packageJson,
220
+ packageJsonPath: targetPackageJsonPath,
221
+ scripts,
222
+ generateScriptPath,
223
+ generateState,
224
+ hasViteNode: Boolean(deps['vite-node'] || devDeps['vite-node']),
225
+ buildState: isExpectedBuildScript(scripts.build)
226
+ ? 'current'
227
+ : isPlainViteBuild(scripts.build)
228
+ ? 'plain'
229
+ : scriptRunsGenerate(scripts.build)
230
+ ? 'custom-digivalt'
231
+ : scripts.build
232
+ ? 'custom'
233
+ : 'missing',
234
+ buildDevState: isExpectedBuildDevScript(scripts['build:dev'])
235
+ ? 'current'
236
+ : isPlainViteBuildDev(scripts['build:dev'])
237
+ ? 'plain'
238
+ : scriptRunsGenerate(scripts['build:dev'])
239
+ ? 'custom-digivalt'
240
+ : scripts['build:dev']
241
+ ? 'custom'
242
+ : 'missing',
243
+ generateCommandState: normalizeScript(scripts.generate) === normalizeScript(EXPECTED_GENERATE_SCRIPT)
244
+ ? 'current'
245
+ : isLegacyGenerateCommand(scripts.generate)
246
+ ? 'legacy'
247
+ : scripts.generate
248
+ ? 'custom'
249
+ : 'missing',
250
+ legacyHackFiles,
251
+ legacyPostinstall: postinstall.includes('link-digivalt-alias'),
252
+ legacyRootImport: Boolean(generateScriptContent && generateScriptContent.includes("import { generators } from '@marvalt/digivalt-core';")),
253
+ legacyViteConfigs,
254
+ };
255
+ }
256
+
257
+ function printPreflight(status) {
258
+ console.log('\n๐Ÿ”Ž Preflight');
259
+ console.log(`- vite-node installed: ${status.hasViteNode ? 'yes' : 'no'}`);
260
+ console.log(`- scripts.generate: ${status.generateCommandState}`);
261
+ console.log(`- scripts.build: ${status.buildState}`);
262
+ console.log(`- scripts.build:dev: ${status.buildDevState}`);
263
+ console.log(`- scripts/generate.ts: ${status.generateState}`);
264
+
265
+ if (status.legacyHackFiles.length > 0 || status.legacyPostinstall || status.legacyRootImport || status.legacyViteConfigs.length > 0) {
266
+ console.log('- legacy DigiVAlt wiring detected: yes');
52
267
  } else {
53
- // We only copy if it doesn't already exist to prevent overwriting user changes!
54
- if (fs.existsSync(dest)) {
55
- console.log(`โš ๏ธ Skipping ${dest.replace(targetDir, '')} (Already exists)`);
268
+ console.log('- legacy DigiVAlt wiring detected: no');
269
+ }
270
+ }
271
+
272
+ function patchPackageJson(status) {
273
+ const packageJson = status.packageJson;
274
+ const scripts = { ...(packageJson.scripts || {}) };
275
+ let changed = false;
276
+
277
+ if (!status.hasViteNode) {
278
+ report.manualActions.push('Install `vite-node` in the app if it is not already available, because DigiVAlt build wiring depends on it.');
279
+ }
280
+
281
+ if (status.generateCommandState === 'missing') {
282
+ scripts.generate = EXPECTED_GENERATE_SCRIPT;
283
+ report.patchedScripts.push('Added `scripts.generate`.');
284
+ changed = true;
285
+ } else if (status.generateCommandState === 'legacy') {
286
+ scripts.generate = EXPECTED_GENERATE_SCRIPT;
287
+ report.patchedScripts.push('Updated `scripts.generate` to the DigiVAlt generators subpath.');
288
+ changed = true;
289
+ } else if (status.generateCommandState === 'custom') {
290
+ report.manualActions.push('Review `scripts.generate` manually because it does not match the current DigiVAlt command.');
291
+ }
292
+
293
+ if (status.buildState === 'missing' || status.buildState === 'plain') {
294
+ scripts.build = EXPECTED_BUILD_SCRIPT;
295
+ report.patchedScripts.push(status.buildState === 'missing' ? 'Added `scripts.build`.' : 'Updated `scripts.build` to run static generation before `vite build`.');
296
+ changed = true;
297
+ } else if (status.buildState === 'custom') {
298
+ report.manualActions.push('Review `scripts.build` manually because it contains custom logic and was left unchanged.');
299
+ }
300
+
301
+ if (status.buildDevState === 'missing' || status.buildDevState === 'plain') {
302
+ scripts['build:dev'] = EXPECTED_BUILD_DEV_SCRIPT;
303
+ report.patchedScripts.push(status.buildDevState === 'missing' ? 'Added `scripts.build:dev`.' : 'Updated `scripts.build:dev` to run static generation before the development build.');
304
+ changed = true;
305
+ } else if (status.buildDevState === 'custom') {
306
+ report.manualActions.push('Review `scripts.build:dev` manually because it contains custom logic and was left unchanged.');
307
+ }
308
+
309
+ if (changed) {
310
+ packageJson.scripts = scripts;
311
+ writeJson(status.packageJsonPath, packageJson);
312
+ }
313
+ }
314
+
315
+ function reconcileGenerateScript(status, templateGenerateScript) {
316
+ const filePath = status.generateScriptPath;
317
+
318
+ if (status.generateState === 'missing') {
319
+ writeFileWithMode(filePath, templateGenerateScript);
320
+ report.refreshedFiles.push('Created `scripts/generate.ts` from the DigiVAlt template.');
321
+ return;
322
+ }
323
+
324
+ if (status.generateState === 'legacy' || (forceTemplate && TEMPLATE_MANAGED_FILES.has('scripts/generate.ts'))) {
325
+ writeFileWithMode(filePath, templateGenerateScript);
326
+ report.refreshedFiles.push('Refreshed `scripts/generate.ts` to the current DigiVAlt template.');
327
+ return;
328
+ }
329
+
330
+ if (status.generateState === 'custom') {
331
+ report.manualActions.push('Review `scripts/generate.ts` manually because it contains custom logic and was left unchanged.');
332
+ }
333
+ }
334
+
335
+ function copyTemplateFiles() {
336
+ const templateFiles = listTemplateFiles(sourceDir);
337
+
338
+ for (const file of templateFiles) {
339
+ if (file.relativePath === 'scripts/generate.ts') {
340
+ continue;
341
+ }
342
+
343
+ const destination = path.join(targetDir, file.relativePath);
344
+ const alreadyExists = fs.existsSync(destination);
345
+ const shouldOverwrite = alreadyExists && forceTemplate && TEMPLATE_MANAGED_FILES.has(file.relativePath);
346
+
347
+ if (alreadyExists && !shouldOverwrite) {
348
+ report.skipped.push(`Skipped ${relativeDisplay(destination)} (already exists).`);
349
+ continue;
350
+ }
351
+
352
+ const content = fs.readFileSync(file.src, 'utf8');
353
+ writeFileWithMode(destination, content);
354
+
355
+ if (alreadyExists) {
356
+ report.overwritten.push(`${dryRun ? 'Would overwrite' : 'Overwrote'} ${relativeDisplay(destination)}.`);
56
357
  } else {
57
- if (!dryRun) {
58
- fs.copyFileSync(src, dest);
59
- }
60
- console.log(`โœ… ${dryRun ? 'Would copy' : 'Copied'} ${dest.replace(targetDir, '')}`);
358
+ report.copied.push(`${dryRun ? 'Would copy' : 'Copied'} ${relativeDisplay(destination)}.`);
61
359
  }
62
360
  }
63
361
  }
64
362
 
363
+ function collectLegacyWarnings(status) {
364
+ if (status.legacyHackFiles.length > 0) {
365
+ report.warnings.push(`Legacy alias workaround detected: ${status.legacyHackFiles.join(', ')}.`);
366
+ }
367
+
368
+ if (status.legacyPostinstall) {
369
+ report.warnings.push('`scripts.postinstall` still references `link-digivalt-alias`; review whether that workaround is still needed.');
370
+ }
371
+
372
+ if (status.legacyRootImport) {
373
+ report.warnings.push('`scripts/generate.ts` still imports `generators` from the root package instead of `@marvalt/digivalt-core/generators`.');
374
+ }
375
+
376
+ if (status.legacyViteConfigs.length > 0) {
377
+ report.warnings.push(`Legacy Vite SSR/noExternal workaround detected in: ${status.legacyViteConfigs.join(', ')}.`);
378
+ }
379
+ }
380
+
381
+ function printList(title, items) {
382
+ if (items.length === 0) {
383
+ return;
384
+ }
385
+
386
+ console.log(`\n${title}`);
387
+ for (const item of items) {
388
+ console.log(`- ${item}`);
389
+ }
390
+ }
391
+
392
+ function printSummary() {
393
+ printList('๐Ÿ“ฆ Template file actions', [...report.copied, ...report.overwritten, ...report.skipped]);
394
+ printList('๐Ÿ› ๏ธ Package wiring updates', report.patchedScripts);
395
+ printList('๐Ÿ”„ Refreshed DigiVAlt files', report.refreshedFiles);
396
+ printList('โš ๏ธ Warnings', report.warnings);
397
+ printList('๐Ÿ‘‰ Manual follow-up', report.manualActions);
398
+
399
+ console.log('\n๐ŸŽ‰ DigiVAlt init completed.');
400
+ console.log('Next steps:');
401
+ console.log('1. Review any warnings or manual follow-up items above.');
402
+ console.log('2. Copy `.dev.vars.example` to `.dev.vars` if you need local Wrangler secrets.');
403
+ console.log('3. Confirm `wrangler.toml` matches your Cloudflare Pages project name.');
404
+ console.log('4. Run `npm run build` to verify static generation now runs before `vite build`.');
405
+ console.log('5. Run `node scripts/deploy-secrets.js` when you are ready to sync allowed env vars to Cloudflare.');
406
+ }
407
+
65
408
  try {
66
- ensureValidTarget(targetDir);
67
- copyRecursiveSync(sourceDir, targetDir);
68
- console.log('\n๐ŸŽ‰ DigiVAlt Cloudflare proxy routes installed successfully!');
69
- console.log('๐Ÿ‘‰ Next Steps:');
70
- console.log('1. Copy .dev.vars.example to .dev.vars and add your API keys.');
71
- console.log('2. Review `wrangler.toml` and confirm the Pages project name matches your app.');
72
- console.log('3. Sync your production credentials to Cloudflare by running:');
73
- console.log(' ๐Ÿ‘‰ node scripts/deploy-secrets.js');
74
- console.log('4. Run `npx wrangler pages dev` to test your proxy functions locally.');
409
+ logHeader();
410
+ const packageJsonPath = ensureValidTarget(targetDir);
411
+ const templateGeneratePath = path.join(sourceDir, 'scripts', 'generate.ts');
412
+ const templateGenerateScript = fs.readFileSync(templateGeneratePath, 'utf8');
413
+
414
+ const status = inspectTargetApp(packageJsonPath, templateGenerateScript);
415
+ printPreflight(status);
416
+ collectLegacyWarnings(status);
417
+ patchPackageJson(status);
418
+ reconcileGenerateScript(status, templateGenerateScript);
419
+ copyTemplateFiles();
420
+ printSummary();
75
421
  } catch (error) {
76
422
  console.error('โŒ Failed to construct templates:', error);
77
423
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marvalt/digivalt-core",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Core glue logic and shared context for DigiVAlt frontend applications",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "main": "dist/index.cjs",
@@ -17,7 +17,9 @@ npm install -D vite-node
17
17
  ```
18
18
 
19
19
  ## ๐Ÿ› ๏ธ 2. Wire the Build Scripts
20
- Open your `package.json` and replace your standard `"build"` and `"dev"` hooks so they intercept the compilation phase and trigger `scripts/generate.ts`.
20
+ `digivalt-init` now preflights your app and patches safe script cases automatically. In a standard Lovable/Vite app, it should add or update the DigiVAlt build wiring for you.
21
+
22
+ Still verify your `package.json` after running init. The expected result is:
21
23
 
22
24
  Change them exactly to this:
23
25
  ```json
@@ -59,4 +61,13 @@ node scripts/deploy-secrets.js
59
61
  ```
60
62
  3. If the script fails, fix the reported Wrangler or project-name issue before deploying.
61
63
 
64
+ ## ๐Ÿ” 5. Rerunning Init Later
65
+ Rerun `npx digivalt-init` after upgrading `@marvalt/digivalt-core` when DigiVAlt-managed scaffolding changes.
66
+
67
+ Init will:
68
+ - patch safe `package.json` script cases automatically
69
+ - refresh older DigiVAlt-managed `scripts/generate.ts` files
70
+ - warn instead of overwriting custom build logic or custom generator scripts
71
+ - report stale legacy workarounds that may no longer be needed
72
+
62
73
  You are fully onboarded and ready to develop offline!