@marvalt/digivalt-core 0.2.6 → 0.2.8

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,20 @@ 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.8] - 2026-04-03
9
+
10
+ ### Changed
11
+ - **Init env template:** `.env.example` managed SEO block comments now point to **`react-helmet-async` / `HelmetProvider`**, **`DigiValtSeoHead`**, and **`node_modules/@marvalt/digivalt-core/docs/SEO-PERFORMANCE.md`**.
12
+
13
+ ## [0.2.7] - 2026-04-03
14
+
15
+ ### Added
16
+ - **`digivalt-init`:** Creates or reconciles **`.env.example`** and **`.env.local.example`** via a DigiValt-managed marker block (`bin/env-example-fragments.cjs`); preserves custom lines outside the block; appends with a warning when an existing file has no markers.
17
+
18
+ ### Changed
19
+ - **Template `scripts/deploy-secrets.js`:** Allowlist extended with **`VITE_SEO_*`** and **`TURNSTILE_*`** (e.g. server-only `TURNSTILE_SECRET_KEY`).
20
+ - **Docs:** `DIGIVALT_SETUP.md` (template + landing), `README.md` init / deploy-secrets notes updated for env templates and SEO secrets.
21
+
8
22
  ## [0.2.6] - 2026-04-09
9
23
 
10
24
  ### Changed (breaking)
package/README.md CHANGED
@@ -43,6 +43,7 @@ Useful flags:
43
43
  What init now does:
44
44
 
45
45
  - copies missing template files into the app
46
+ - creates or updates **`.env.example`** and **`.env.local.example`** using a marker-wrapped DigiValt-managed block (safe to rerun; custom lines outside the block are preserved)
46
47
  - adds or updates `generate`, `build`, and `build:dev` scripts when the app is still on plain Vite wiring
47
48
  - refreshes `scripts/generate.ts` when it matches an older DigiVAlt-managed version
48
49
  - warns instead of overwriting custom build logic or custom generator scripts
@@ -64,6 +65,6 @@ Use `node scripts/deploy-secrets.js` after `digivalt-init`.
64
65
 
65
66
  The helper now:
66
67
 
67
- - uploads only DigiVAlt-related environment variables
68
+ - uploads only DigiVAlt-related environment variables (including **`VITE_SEO_*`** and server-only **`TURNSTILE_SECRET_KEY`** when present)
68
69
  - skips known local-only variables such as `VITE_LOCAL_DEVELOPMENT`
69
70
  - fails with a non-zero exit code when Wrangler or project detection fails
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Managed inner bodies for digivalt-init (placed between start/end markers).
3
+ * Keep in sync with generators, getIntegrationConfig / vite-env, and deploy-secrets allowlist.
4
+ */
5
+
6
+ const ENV_EXAMPLE_MANAGED_BODY = `# WordPress / static generation (public URLs)
7
+ VITE_WORDPRESS_API_URL=https://cms.example.com/wp-json
8
+ # direct | cloudflare_proxy | supabase_proxy
9
+ VITE_AUTH_MODE=direct
10
+ VITE_CLOUDFLARE_WORKER_URL=
11
+ VITE_LOCAL_DEVELOPMENT=false
12
+
13
+ # Multi-frontend (digivalt-multi-frontends)
14
+ VITE_FRONTEND_ID=my-app
15
+ VITE_FRONTEND_NAME=My App
16
+
17
+ VITE_ENABLED_POST_TYPES=posts,pages
18
+ VITE_DEFAULT_MAX_ITEMS=200
19
+ VITE_USE_DSTYLER=true
20
+
21
+ # Optional: Gravity Forms API base if not derived from WordPress URL
22
+ # VITE_GRAVITY_FORMS_API_URL=
23
+
24
+ # Integration URLs only (secrets belong in .env.local)
25
+ VITE_MAUTIC_URL=
26
+ VITE_MAUTIC_PROXY_URL=
27
+ VITE_SUITECRM_URL=
28
+ VITE_SUITECRM_PROXY_URL=
29
+ VITE_SUPABASE_URL=
30
+ VITE_SUPABASE_ANON_KEY=
31
+
32
+ VITE_CHATWOOT_BASE_URL=
33
+ VITE_CHATWOOT_WEBSITE_TOKEN=
34
+
35
+ # Cloudflare account (wrangler / deploy-secrets; not a Vite embed unless duplicated as VITE_)
36
+ CLOUDFLARE_ACCOUNT_ID=
37
+
38
+ # SEO — consumed by getSEOConfig / DigiValtSeoHead (peer: react-helmet-async + root HelmetProvider).
39
+ # Guide: node_modules/@marvalt/digivalt-core/docs/SEO-PERFORMANCE.md
40
+ VITE_SEO_SITE_URL=https://www.example.com
41
+ VITE_SEO_SITE_NAME=Example Site
42
+ VITE_SEO_DEFAULT_TITLE=Example Site
43
+ VITE_SEO_DEFAULT_DESCRIPTION=A short description for search results.
44
+ VITE_SEO_DEFAULT_KEYWORDS=
45
+ VITE_SEO_DEFAULT_OG_IMAGE=https://www.example.com/og-default.jpg
46
+ VITE_SEO_TWITTER_SITE=@example
47
+ VITE_SEO_FACEBOOK_APP_ID=
48
+ VITE_SEO_GOOGLE_SITE_VERIFICATION=
49
+ VITE_SEO_BING_SITE_VERIFICATION=
50
+
51
+ # Performance toggles (optional; omit to use defaults in getPerformanceConfig)
52
+ # VITE_PERF_LAZY_LOADING=true
53
+ # VITE_PERF_IMAGE_OPTIMIZATION=true
54
+ # VITE_PERF_CODE_SPLITTING=true
55
+ # VITE_PERF_SERVICE_WORKER=false
56
+ # VITE_PERF_CACHING=true`;
57
+
58
+ const ENV_LOCAL_EXAMPLE_MANAGED_BODY = `# WordPress application password (static generation + API)
59
+ VITE_WP_API_USERNAME=
60
+ VITE_WP_APP_PASSWORD=
61
+ # Optional aliases for Node-only scripts (see digivalt-core generators)
62
+ # WP_API_USERNAME=
63
+ # WP_APP_PASSWORD=
64
+
65
+ # Cloudflare Access (WordPress behind Zero Trust)
66
+ VITE_CF_ACCESS_CLIENT_ID=
67
+ VITE_CF_ACCESS_CLIENT_SECRET=
68
+
69
+ # Mautic API credentials
70
+ VITE_MAUTIC_API_PUBLIC_KEY=
71
+ VITE_MAUTIC_API_SECRET_KEY=
72
+
73
+ # Gravity Forms REST consumer keys (if used)
74
+ VITE_GF_CONSUMER_KEY=
75
+ VITE_GF_CONSUMER_SECRET=
76
+
77
+ # SuiteCRM (when SuiteCRM static generation is enabled)
78
+ VITE_SUITECRM_CLIENT_ID=
79
+ VITE_SUITECRM_CLIENT_SECRET=
80
+ VITE_SUITECRM_USERNAME=
81
+ VITE_SUITECRM_PASSWORD=
82
+
83
+ # Supabase service role (only if needed; never expose in client bundles inappropriately)
84
+ # VITE_SUPABASE_SERVICE_ROLE_KEY=
85
+
86
+ # cf-wp-webhook-refresh + Cloudflare Pages Functions
87
+ VITE_FRONTEND_SECRET=
88
+ VITE_WP_REFRESH_SECRET=
89
+
90
+ # Turnstile — site key for browser; secret is server-only for Pages Functions
91
+ VITE_TURNSTILE_SITE_KEY=
92
+ TURNSTILE_SECRET_KEY=
93
+
94
+ # Optional: npm publish / CI
95
+ # NPM_TOKEN=
96
+ `;
97
+
98
+ module.exports = {
99
+ ENV_EXAMPLE_MANAGED_BODY,
100
+ ENV_LOCAL_EXAMPLE_MANAGED_BODY,
101
+ };
package/bin/init.cjs CHANGED
@@ -3,6 +3,15 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
+ const {
7
+ ENV_EXAMPLE_MANAGED_BODY,
8
+ ENV_LOCAL_EXAMPLE_MANAGED_BODY,
9
+ } = require('./env-example-fragments.cjs');
10
+
11
+ const ENV_MANAGED_START =
12
+ '# --- DigiValt: managed by digivalt-init (do not edit this block by hand; rerun init to refresh) ---';
13
+ const ENV_MANAGED_END = '# --- End DigiValt ---';
14
+
6
15
  const sourceDir = path.join(__dirname, '..', 'template');
7
16
  const args = process.argv.slice(2);
8
17
 
@@ -332,6 +341,57 @@ function reconcileGenerateScript(status, templateGenerateScript) {
332
341
  }
333
342
  }
334
343
 
344
+ function buildEnvManagedBlock(managedBody) {
345
+ return `${ENV_MANAGED_START}\n${managedBody}\n${ENV_MANAGED_END}`;
346
+ }
347
+
348
+ function reconcileEnvExampleFile(relativePath, managedBody) {
349
+ const fullPath = path.join(targetDir, relativePath);
350
+ const block = buildEnvManagedBlock(managedBody);
351
+ const existing = readTextIfExists(fullPath);
352
+
353
+ const header =
354
+ relativePath === '.env.example'
355
+ ? '# DigiValt — public / team template (safe to commit). Use `.env` locally if your tooling expects `.env` only.'
356
+ : '# DigiValt — secrets template (commit as `.env.local.example`). Copy to `.env.local`; never commit real secrets.';
357
+
358
+ if (!existing) {
359
+ const out = `${header}\n\n${block}\n\n# Add project-specific variables below.\n`;
360
+ writeFileWithMode(fullPath, out);
361
+ report.refreshedFiles.push(
362
+ dryRun ? `Would create ${relativePath} with DigiValt env template.` : `Created ${relativePath} with DigiValt env template.`
363
+ );
364
+ return;
365
+ }
366
+
367
+ const startIndex = existing.indexOf(ENV_MANAGED_START);
368
+ const endIndex = existing.indexOf(ENV_MANAGED_END);
369
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
370
+ const before = existing.slice(0, startIndex).replace(/\s*$/, '');
371
+ const after = existing.slice(endIndex + ENV_MANAGED_END.length);
372
+ const out = `${before}\n\n${block}${after}`;
373
+ writeFileWithMode(fullPath, out);
374
+ report.refreshedFiles.push(
375
+ dryRun ? `Would update DigiValt block in ${relativePath}.` : `Updated DigiValt block in ${relativePath}.`
376
+ );
377
+ return;
378
+ }
379
+
380
+ report.warnings.push(
381
+ `${relativePath} had no DigiValt managed block; appended at end. Move custom content or merge manually once.`
382
+ );
383
+ const out = `${existing.replace(/\s*$/, '')}\n\n${block}\n`;
384
+ writeFileWithMode(fullPath, out);
385
+ report.refreshedFiles.push(
386
+ dryRun ? `Would append DigiValt block to ${relativePath}.` : `Appended DigiValt block to ${relativePath}.`
387
+ );
388
+ }
389
+
390
+ function reconcileEnvExampleFiles() {
391
+ reconcileEnvExampleFile('.env.example', ENV_EXAMPLE_MANAGED_BODY);
392
+ reconcileEnvExampleFile('.env.local.example', ENV_LOCAL_EXAMPLE_MANAGED_BODY);
393
+ }
394
+
335
395
  function copyTemplateFiles() {
336
396
  const templateFiles = listTemplateFiles(sourceDir);
337
397
 
@@ -399,10 +459,11 @@ function printSummary() {
399
459
  console.log('\n🎉 DigiVAlt init completed.');
400
460
  console.log('Next steps:');
401
461
  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.');
462
+ console.log('2. Copy `.env.local.example` to `.env.local`, fill secrets, and keep `.env.example` as the team-facing public template.');
463
+ console.log('3. Copy `.dev.vars.example` to `.dev.vars` if you need local Wrangler secrets.');
464
+ console.log('4. Confirm `wrangler.toml` matches your Cloudflare Pages project name.');
465
+ console.log('5. Run `npm run build` to verify static generation now runs before `vite build`.');
466
+ console.log('6. Run `node scripts/deploy-secrets.js` when you are ready to sync allowed env vars to Cloudflare Pages.');
406
467
  }
407
468
 
408
469
  try {
@@ -417,6 +478,7 @@ try {
417
478
  patchPackageJson(status);
418
479
  reconcileGenerateScript(status, templateGenerateScript);
419
480
  copyTemplateFiles();
481
+ reconcileEnvExampleFiles();
420
482
  printSummary();
421
483
  } catch (error) {
422
484
  console.error('❌ Failed to construct templates:', error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marvalt/digivalt-core",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
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",
@@ -30,7 +30,15 @@ Change them exactly to this:
30
30
  }
31
31
  ```
32
32
 
33
- ## 🔐 3. Define Secrets (`.env.local`)
33
+ ## 📄 3. Env templates (`.env.example` / `.env.local.example`)
34
+
35
+ After you run `digivalt-init`, the repo root should include **`.env.example`** (public placeholders, safe to commit) and **`.env.local.example`** (secret keys with empty values—also safe to commit as a template).
36
+
37
+ - Copy **`.env.local.example`** → **`.env.local`** and fill in real credentials. Never commit **`.env.local`**.
38
+ - Keep **`.env.example`** as the team-facing reference for non-secret `VITE_*` defaults and URLs.
39
+ - Variables between the **DigiValt-managed** marker comments are refreshed when you rerun init; add app-specific lines **outside** that block (or merge manually once if init appended a block to an older file without markers).
40
+
41
+ ## 🔐 4. Define Secrets (`.env.local`)
34
42
  Create a completely ignored `.env.local` file in the root of your project and populate all of these tightly coupled secrets:
35
43
 
36
44
  ```env
@@ -52,7 +60,7 @@ VITE_AUTH_MODE="direct"
52
60
  ```
53
61
  *⚠️ **CRITICAL WARNING:** NEVER place `VITE_IS_LOVABLE=true` inside `.env.local` locally! That variable instructs the application to mute API traffic completely, pretending the data does not exist.*
54
62
 
55
- ## 🌩️ 4. Deploy Infrastructure to Cloudflare
63
+ ## 🌩️ 5. Deploy Infrastructure to Cloudflare
56
64
  Once you connect this GitHub repository to the Cloudflare Pages dashboard:
57
65
  1. Open `wrangler.toml` and change `name="digivalt-landing-api"` to match the exact name of your Cloudflare Pages project.
58
66
  2. Run this automated script to inject your allowed DigiVAlt `.env` / `.env.local` variables into Cloudflare Pages:
@@ -61,16 +69,17 @@ node scripts/deploy-secrets.js
61
69
  ```
62
70
  3. If the script fails, fix the reported Wrangler or project-name issue before deploying.
63
71
 
64
- ## 🔁 5. Rerunning Init Later
72
+ ## 🔁 6. Rerunning Init Later
65
73
  Rerun `npx digivalt-init` after upgrading `@marvalt/digivalt-core` when DigiVAlt-managed scaffolding changes.
66
74
 
67
75
  Init will:
68
76
  - patch safe `package.json` script cases automatically
69
77
  - refresh older DigiVAlt-managed `scripts/generate.ts` files
78
+ - reconcile the DigiValt-managed sections inside `.env.example` and `.env.local.example`
70
79
  - warn instead of overwriting custom build logic or custom generator scripts
71
80
  - report stale legacy workarounds that may no longer be needed
72
81
 
73
- ## 6. SEO and PageSpeed (optional)
82
+ ## 7. SEO and PageSpeed (optional)
74
83
 
75
84
  1. Install **`react-helmet-async`** and wrap your app with **`HelmetProvider`** at the root (next to the router).
76
85
  2. Set at least **`VITE_SEO_SITE_URL`** (no trailing slash), **`VITE_SEO_SITE_NAME`**, **`VITE_SEO_DEFAULT_TITLE`**, **`VITE_SEO_DEFAULT_DESCRIPTION`**, and **`VITE_SEO_DEFAULT_OG_IMAGE`** in Cloudflare Pages / `.env.local`.
@@ -17,9 +17,13 @@ const excludedKeys = new Set([
17
17
  'VITE_LOCAL_DEVELOPMENT',
18
18
  ]);
19
19
 
20
+ // VITE_SEO_* are often public (verification meta, titles); allow upload when teams store them as Pages secrets.
21
+ // TURNSTILE_SECRET_KEY is server-only (Pages Functions), not a VITE_ prefix.
20
22
  const allowedPatterns = [
21
23
  /^CLOUDFLARE_/,
22
24
  /^CF_ACCESS_/,
25
+ /^TURNSTILE_/,
26
+ /^VITE_SEO_/,
23
27
  /^VITE_(WORDPRESS|WP|GF|GRAVITY_FORMS|FRONTEND|FRONTENDS|MAUTIC|SUITECRM|SUPABASE|CLOUDFLARE|AUTH|TURNSTILE|CHATWOOT|THEME|GOOGLE_|FACEBOOK_|LINKEDIN_)/,
24
28
  ];
25
29