@marvalt/digivalt-core 0.2.30 → 0.2.32

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/GLOBAL-SHORTCODES.md +7 -7
  3. package/README.md +10 -8
  4. package/bin/env-example-fragments.cjs +6 -0
  5. package/bin/init.cjs +44 -5
  6. package/dist/generators/index.d.ts +3 -0
  7. package/dist/generators/prerender-seo.d.ts +6 -0
  8. package/dist/generators/seo-manifest.d.ts +3 -0
  9. package/dist/generators/seo-urls.d.ts +29 -0
  10. package/dist/generators/sitemap-paths.d.ts +5 -9
  11. package/dist/generators.cjs +1005 -155
  12. package/dist/generators.cjs.map +1 -1
  13. package/dist/generators.d.ts +86 -6
  14. package/dist/generators.esm.js +992 -156
  15. package/dist/generators.esm.js.map +1 -1
  16. package/dist/index.cjs +1019 -156
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +103 -9
  19. package/dist/index.esm.js +1006 -157
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/registry/index.d.ts +20 -0
  22. package/dist/registry.cjs +1528 -0
  23. package/dist/registry.cjs.map +1 -0
  24. package/dist/registry.d.ts +26 -0
  25. package/dist/registry.esm.js +1523 -0
  26. package/dist/registry.esm.js.map +1 -0
  27. package/dist/seo/DigiValtRouteSeo.d.ts +12 -0
  28. package/dist/seo/DigiValtSeoHead.d.ts +2 -0
  29. package/dist/seo/buildPageJsonLd.d.ts +11 -0
  30. package/dist/seo/html.d.ts +8 -0
  31. package/dist/seo/index.d.ts +7 -0
  32. package/dist/seo/resolvePageSeo.d.ts +17 -0
  33. package/dist/seo/seoTypes.d.ts +57 -0
  34. package/dist/seo.cjs +307 -86
  35. package/dist/seo.cjs.map +1 -1
  36. package/dist/seo.d.ts +105 -2
  37. package/dist/seo.esm.js +300 -87
  38. package/dist/seo.esm.js.map +1 -1
  39. package/dist/static/wpStatic.d.ts +16 -0
  40. package/dist/static.cjs +10 -0
  41. package/dist/static.cjs.map +1 -1
  42. package/dist/static.d.ts +38 -2
  43. package/dist/static.esm.js +10 -1
  44. package/dist/static.esm.js.map +1 -1
  45. package/dist/utils/notifications.d.ts +3 -0
  46. package/docs/AI-LOCAL-WORDPRESS-BOOTSTRAP.md +7 -3
  47. package/docs/AI-MASTER-PROMPT.md +37 -0
  48. package/docs/ENVIRONMENT.md +3 -3
  49. package/docs/README.md +4 -3
  50. package/docs/SEO-PERFORMANCE.md +42 -11
  51. package/docs/SHORTCODES.md +4 -5
  52. package/package.json +17 -6
  53. package/template/DIGIVALT_SETUP.md +14 -8
  54. package/template/functions/robots.txt.ts +27 -0
  55. package/template/functions/sitemap.xml.ts +143 -65
  56. package/template/scripts/generate.ts +3 -10
  57. package/template/scripts/prerender-seo.ts +10 -0
  58. package/docs/LOVABLE-MASTER-PROMPT.md +0 -140
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marvalt/digivalt-core",
3
- "version": "0.2.30",
3
+ "version": "0.2.32",
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",
@@ -42,6 +42,11 @@
42
42
  "import": "./dist/seo.esm.js",
43
43
  "require": "./dist/seo.cjs"
44
44
  },
45
+ "./registry": {
46
+ "types": "./dist/registry.d.ts",
47
+ "import": "./dist/registry.esm.js",
48
+ "require": "./dist/registry.cjs"
49
+ },
45
50
  "./integrations": {
46
51
  "types": "./dist/integrations.d.ts",
47
52
  "import": "./dist/integrations.esm.js",
@@ -101,24 +106,30 @@
101
106
  "typescript": "^5.5.3"
102
107
  },
103
108
  "peerDependencies": {
109
+ "@tanstack/react-query": ">=5",
104
110
  "react": ">=18",
105
111
  "react-dom": ">=18",
106
112
  "react-helmet-async": ">=2"
107
113
  },
114
+ "peerDependenciesMeta": {
115
+ "@tanstack/react-query": {
116
+ "optional": true
117
+ }
118
+ },
108
119
  "engines": {
109
120
  "node": ">=18.0.0",
110
121
  "npm": ">=8.0.0"
111
122
  },
112
123
  "dependencies": {
113
124
  "@digivalt/tracker": "^0.1.1",
114
- "@marvalt/dstyler": "^0.1.51",
125
+ "@marvalt/dstyler": "^0.1.53",
115
126
  "@marvalt/madapter": "^2.3.11",
116
127
  "@marvalt/plister": "^0.3.0",
117
- "@marvalt/sadapter": "^0.1.5",
118
- "@marvalt/shcoder": "^0.1.35",
119
- "@marvalt/tadapter": "^1.0.12",
128
+ "@marvalt/sadapter": "^0.1.6",
129
+ "@marvalt/shcoder": "^0.1.37",
130
+ "@marvalt/tadapter": "^1.0.14",
120
131
  "@marvalt/wadapter": "^2.3.61",
121
- "@marvalt/wparser": "^0.1.79",
132
+ "@marvalt/wparser": "^0.1.82",
122
133
  "@radix-ui/react-toast": "^1.2.15",
123
134
  "class-variance-authority": "^0.7.1",
124
135
  "clsx": "^2.1.1",
@@ -2,9 +2,9 @@
2
2
 
3
3
  Welcome to your new **DigiVAlt** architecture project!
4
4
 
5
- Because your Lovable interface is completely decoupled from the data pipeline, the `@marvalt/digivalt-core` package securely builds the glue between your frontend and your Cloudflare Worker backend.
5
+ Your Vite interface is decoupled from the data pipeline. `@marvalt/digivalt-core` supplies static generation and runtime helpers for your Cloudflare Pages app.
6
6
 
7
- Follow these instructions strictly to hydrate a brand new Lovable project with real data.
7
+ Follow these instructions to hydrate a new Vite project with real data.
8
8
 
9
9
  ---
10
10
 
@@ -17,7 +17,7 @@ npm install -D vite-node
17
17
  ```
18
18
 
19
19
  ## 🛠️ 2. Wire the Build Scripts
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.
20
+ `digivalt-init` preflights your app and patches safe script cases automatically. In a standard Vite app, it should add or update the DigiValt build wiring for you.
21
21
 
22
22
  Still verify your `package.json` after running init. The expected result is:
23
23
 
@@ -58,7 +58,7 @@ VITE_GRAVITY_FORMS_SECRET="your_cs_secret"
58
58
  # Local Execution Mode (Set to "direct" internally so your local UI bypasses Cloudflare)
59
59
  VITE_AUTH_MODE="direct"
60
60
  ```
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.*
61
+ *⚠️ **CRITICAL WARNING:** Never enable fixture mode (`VITE_IS_LOVABLE=true`) in `.env.local` when you need live generation. The backwards-compatible flag skips API traffic and uses committed data.*
62
62
 
63
63
  ## 🌩️ 5. Deploy Infrastructure to Cloudflare
64
64
  Once you connect this GitHub repository to the Cloudflare Pages dashboard:
@@ -77,15 +77,21 @@ Init will:
77
77
  - refresh older DigiVAlt-managed `scripts/generate.ts` files
78
78
  - reconcile the DigiValt-managed sections inside `.env.example` and `.env.local.example`
79
79
  - warn instead of overwriting custom build logic or custom generator scripts
80
- - report stale legacy workarounds that may no longer be needed
80
+ - report stale legacy workarounds and local registry files that may no longer be needed
81
+
82
+ ## App chrome and WordPress page bodies
83
+
84
+ Use `DigiValtProviders`, `DigiValtWpPage`, and `createContentRegistry` from `@marvalt/digivalt-core/registry`. Read static chrome content with `getAppChrome()` from `@marvalt/digivalt-core/static`.
85
+
86
+ > Build Header/Footer in the app. Read menus, logo, and copy from WordPress static data (`getAppChrome()`). Apply the app’s layout and Tailwind. Do not render the WordPress header/footer template part and do not pipe `footer.blocks` / `header.blocks` through wparser.
81
87
 
82
88
  ## 7. SEO and PageSpeed (optional)
83
89
 
84
90
  1. Install **`react-helmet-async`** and wrap your app with **`HelmetProvider`** at the root (next to the router).
85
91
  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`.
86
- 3. Import **`DigiValtSeoHead`** and **`JsonLd`** from **`@marvalt/digivalt-core/seo`** in your layouts or page components; pass **`pathname`** per route for correct canonical URLs.
87
- 4. **Sitemap:** **`/sitemap.xml`** is served by **`functions/sitemap.xml.ts`**. **`public/sitemap-paths.json`** is **generated** when you run **`npm run build`** (or `generate`): it merges (a) **`public/sitemap-static-paths.json`** — edit this for Lovable/SPA-only routes, (b) URLs from **`navigation_menu`** in **`wordpress-data.json`**, (c) **`link`** pathnames from CPT/post arrays listed under **`config.post_types`**. Only CMS links whose host matches **`VITE_WORDPRESS_API_URL`** / **`WORDPRESS_API_URL`** are included. Canonical `<loc>` values use **`VITE_SEO_SITE_URL`** (or **`SITE_URL`**). Do not add **`public/sitemap.xml`** (it would shadow the Function). With **`VITE_IS_LOVABLE=true`**, static fetch is skipped but sitemap paths are still regenerated from committed JSON.
88
- 5. Read the full guide shipped with the package: **`node_modules/@marvalt/digivalt-core/docs/SEO-PERFORMANCE.md`** (or in the repo: `digivalt-npm-packages/digivalt-core/docs/SEO-PERFORMANCE.md`).
92
+ 3. Import **`DigiValtRouteSeo`** (or **`DigiValtSeoHead`** / **`JsonLd`**) from **`@marvalt/digivalt-core/seo`**; pass **`pathname`** per route.
93
+ 4. **Sitemap / robots / prerender:** **`/sitemap.xml`** and **`/robots.txt`** are Pages Functions. **`public/sitemap-paths.json`** and **`seo-manifest.json`** are generated on `generate` / `build`. After `vite build`, run **`scripts/prerender-seo.ts`**. Edit **`public/sitemap-static-paths.json`** for app-only indexable routes. Do not add **`public/sitemap.xml`** or **`public/robots.txt`**. Per-URL meta lives in the **DigiValt SEO** WordPress plugin; site identity stays in **`VITE_SEO_*`**. Optional **`VITE_SEO_ROUTE_PREFIXES`** maps CPT REST slugs to app path prefixes. See **`docs/SEO-PERFORMANCE.md`**.
94
+ 5. Read the full guide shipped with the package: **`node_modules/@marvalt/digivalt-core/docs/SEO-PERFORMANCE.md`**.
89
95
 
90
96
  ## 8. Analytics (Google Analytics / Matomo)
91
97
 
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Serves /robots.txt with a Sitemap directive using VITE_SEO_SITE_URL when set.
3
+ */
4
+
5
+ function normalizeBase(raw: string | undefined): string | null {
6
+ if (!raw || typeof raw !== 'string') return null;
7
+ const t = raw.trim().replace(/\/+$/, '');
8
+ return t || null;
9
+ }
10
+
11
+ export async function onRequestGet(context: { request: Request; env: Record<string, string | undefined> }) {
12
+ const { request, env } = context;
13
+ const fromEnv = normalizeBase(env.VITE_SEO_SITE_URL || env.SITE_URL);
14
+ const origin = new URL(request.url).origin.replace(/\/+$/, '');
15
+ const base = fromEnv || origin;
16
+ const body = `User-agent: *
17
+ Allow: /
18
+
19
+ Sitemap: ${base}/sitemap.xml
20
+ `;
21
+ return new Response(body, {
22
+ headers: {
23
+ 'Content-Type': 'text/plain; charset=utf-8',
24
+ 'Cache-Control': 'public, max-age=300',
25
+ },
26
+ });
27
+ }
@@ -1,65 +1,143 @@
1
- /**
2
- * Serves /sitemap.xml from canonical site URL + public/sitemap-paths.json (generated at build).
3
- * Set VITE_SEO_SITE_URL (or SITE_URL) on Cloudflare Pages for production canonical hosts.
4
- */
5
-
6
- function normalizeBase(raw: string | undefined): string | null {
7
- if (!raw || typeof raw !== 'string') return null;
8
- const t = raw.trim().replace(/\/+$/, '');
9
- return t || null;
10
- }
11
-
12
- function escapeXml(s: string): string {
13
- return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
14
- }
15
-
16
- async function loadPaths(request: Request): Promise<string[]> {
17
- try {
18
- const jsonUrl = new URL('/sitemap-paths.json', request.url);
19
- const res = await fetch(jsonUrl.toString());
20
- if (!res.ok) return ['/'];
21
- const data: unknown = await res.json();
22
- const list: string[] = [];
23
- if (Array.isArray(data)) {
24
- for (const p of data) {
25
- if (typeof p === 'string' && p.startsWith('/')) list.push(p);
26
- }
27
- } else if (data && typeof data === 'object' && Array.isArray((data as { paths?: unknown }).paths)) {
28
- for (const p of (data as { paths: unknown[] }).paths) {
29
- if (typeof p === 'string' && p.startsWith('/')) list.push(p);
30
- }
31
- }
32
- return list.length ? list : ['/'];
33
- } catch {
34
- return ['/'];
35
- }
36
- }
37
-
38
- function buildSitemap(base: string, paths: string[]): string {
39
- const lines = paths.map((p) => {
40
- const loc = p === '/' ? base : `${base}${p.startsWith('/') ? p : `/${p}`}`;
41
- return ` <url><loc>${escapeXml(loc)}</loc></url>`;
42
- });
43
- return `<?xml version="1.0" encoding="UTF-8"?>
44
- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
45
- ${lines.join('\n')}
46
- </urlset>`;
47
- }
48
-
49
- export async function onRequestGet(context: { request: Request; env: Record<string, string | undefined> }) {
50
- const { request, env } = context;
51
- const fromEnv = normalizeBase(env.VITE_SEO_SITE_URL || env.SITE_URL);
52
- const origin = new URL(request.url).origin.replace(/\/+$/, '');
53
- const base = fromEnv || origin;
54
-
55
- const rawPaths = await loadPaths(request);
56
- const paths = [...new Set(rawPaths.length ? rawPaths : ['/'])];
57
-
58
- const xml = buildSitemap(base, paths);
59
- return new Response(xml, {
60
- headers: {
61
- 'Content-Type': 'application/xml; charset=utf-8',
62
- 'Cache-Control': 'public, max-age=300',
63
- },
64
- });
65
- }
1
+ /**
2
+ * Serves /sitemap.xml from canonical site URL + public/sitemap-paths.json (generated at build).
3
+ * Set VITE_SEO_SITE_URL (or SITE_URL) on Cloudflare Pages for production canonical hosts.
4
+ */
5
+
6
+ const IMAGE_NS = 'http://www.google.com/schemas/sitemap-image/1.1';
7
+
8
+ function normalizeBase(raw: string | undefined): string | null {
9
+ if (!raw || typeof raw !== 'string') return null;
10
+ const t = raw.trim().replace(/\/+$/, '');
11
+ return t || null;
12
+ }
13
+
14
+ function escapeXml(s: string): string {
15
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
16
+ }
17
+
18
+ type SitemapUrl = { path: string; lastmod?: string; image?: string };
19
+
20
+ function asUrls(data: unknown): SitemapUrl[] {
21
+ if (!data || typeof data !== 'object') return [];
22
+ const rec = data as { urls?: unknown; paths?: unknown };
23
+ if (Array.isArray(rec.urls)) {
24
+ return rec.urls
25
+ .map((u) => {
26
+ if (typeof u === 'string' && u.startsWith('/')) return { path: u };
27
+ if (u && typeof u === 'object' && typeof (u as { path?: unknown }).path === 'string') {
28
+ const row = u as { path: string; lastmod?: unknown; image?: unknown };
29
+ return {
30
+ path: row.path,
31
+ lastmod: typeof row.lastmod === 'string' ? row.lastmod : undefined,
32
+ image: typeof row.image === 'string' ? row.image : undefined,
33
+ };
34
+ }
35
+ return null;
36
+ })
37
+ .filter((u): u is SitemapUrl => !!u && u.path.startsWith('/'));
38
+ }
39
+ if (Array.isArray(rec.paths)) {
40
+ return rec.paths.filter((p): p is string => typeof p === 'string' && p.startsWith('/')).map((path) => ({ path }));
41
+ }
42
+ return [];
43
+ }
44
+
45
+ async function fetchJson(request: Request, pathname: string): Promise<unknown> {
46
+ const jsonUrl = new URL(pathname, request.url);
47
+ const res = await fetch(jsonUrl.toString());
48
+ if (!res.ok) return null;
49
+ return res.json();
50
+ }
51
+
52
+ async function loadUrls(request: Request): Promise<SitemapUrl[]> {
53
+ try {
54
+ const data = await fetchJson(request, '/sitemap-paths.json');
55
+ if (!data || typeof data !== 'object') return [{ path: '/' }];
56
+ const rec = data as { index?: unknown; parts?: unknown };
57
+ if (rec.index === true && typeof rec.parts === 'number' && rec.parts > 1) {
58
+ const partParam = new URL(request.url).searchParams.get('part');
59
+ if (partParam === null) {
60
+ return [];
61
+ }
62
+ const part = Number(partParam);
63
+ if (!Number.isInteger(part) || part < 0 || part >= rec.parts) return [{ path: '/' }];
64
+ const shard = await fetchJson(request, `/sitemap-paths-${part}.json`);
65
+ const urls = asUrls(shard);
66
+ return urls.length ? urls : [{ path: '/' }];
67
+ }
68
+ const urls = asUrls(data);
69
+ return urls.length ? urls : [{ path: '/' }];
70
+ } catch {
71
+ return [{ path: '/' }];
72
+ }
73
+ }
74
+
75
+ async function loadIndexParts(request: Request): Promise<number | null> {
76
+ try {
77
+ const data = await fetchJson(request, '/sitemap-paths.json');
78
+ if (data && typeof data === 'object') {
79
+ const rec = data as { index?: unknown; parts?: unknown };
80
+ if (rec.index === true && typeof rec.parts === 'number' && rec.parts > 1) return rec.parts;
81
+ }
82
+ } catch {
83
+ /* ignore */
84
+ }
85
+ return null;
86
+ }
87
+
88
+ function locFor(base: string, p: string): string {
89
+ return p === '/' ? base : `${base}${p.startsWith('/') ? p : `/${p}`}`;
90
+ }
91
+
92
+ function buildUrlset(base: string, urls: SitemapUrl[]): string {
93
+ const lines = urls.map((u) => {
94
+ const loc = locFor(base, u.path);
95
+ const lastmod = u.lastmod ? `\n <lastmod>${escapeXml(u.lastmod)}</lastmod>` : '';
96
+ const image = u.image
97
+ ? `\n <image:image>\n <image:loc>${escapeXml(u.image)}</image:loc>\n </image:image>`
98
+ : '';
99
+ return ` <url>\n <loc>${escapeXml(loc)}</loc>${lastmod}${image}\n </url>`;
100
+ });
101
+ return `<?xml version="1.0" encoding="UTF-8"?>
102
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="${IMAGE_NS}">
103
+ ${lines.join('\n')}
104
+ </urlset>`;
105
+ }
106
+
107
+ function buildIndex(base: string, parts: number): string {
108
+ const items = [];
109
+ for (let i = 0; i < parts; i++) {
110
+ items.push(` <sitemap><loc>${escapeXml(`${base}/sitemap.xml?part=${i}`)}</loc></sitemap>`);
111
+ }
112
+ return `<?xml version="1.0" encoding="UTF-8"?>
113
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
114
+ ${items.join('\n')}
115
+ </sitemapindex>`;
116
+ }
117
+
118
+ export async function onRequestGet(context: { request: Request; env: Record<string, string | undefined> }) {
119
+ const { request, env } = context;
120
+ const fromEnv = normalizeBase(env.VITE_SEO_SITE_URL || env.SITE_URL);
121
+ const origin = new URL(request.url).origin.replace(/\/+$/, '');
122
+ const base = fromEnv || origin;
123
+ const part = new URL(request.url).searchParams.get('part');
124
+
125
+ const indexParts = await loadIndexParts(request);
126
+ if (indexParts && part === null) {
127
+ return new Response(buildIndex(base, indexParts), {
128
+ headers: {
129
+ 'Content-Type': 'application/xml; charset=utf-8',
130
+ 'Cache-Control': 'public, max-age=300',
131
+ },
132
+ });
133
+ }
134
+
135
+ const urls = await loadUrls(request);
136
+ const xml = buildUrlset(base, urls.length ? urls : [{ path: '/' }]);
137
+ return new Response(xml, {
138
+ headers: {
139
+ 'Content-Type': 'application/xml; charset=utf-8',
140
+ 'Cache-Control': 'public, max-age=300',
141
+ },
142
+ });
143
+ }
@@ -1,15 +1,7 @@
1
- import { generateAllData, generateSitemapPathsSafe } from '@marvalt/digivalt-core/generators';
1
+ import { generateAllData, generateSitemapPathsSafe, generateSeoManifestSafe } from '@marvalt/digivalt-core/generators';
2
2
 
3
3
  async function runGenerators() {
4
- console.log('🚀 Starting DigiVAlt static data generation for Landing Project...');
5
-
6
- if (process.env.VITE_IS_LOVABLE === 'true') {
7
- console.log(
8
- '⚡ Lovable Environment Detected (VITE_IS_LOVABLE). Skipping static data fetch; refreshing sitemap paths from committed JSON.'
9
- );
10
- await generateSitemapPathsSafe();
11
- return;
12
- }
4
+ console.log('🚀 Starting DigiVAlt static data generation...');
13
5
 
14
6
  try {
15
7
  await generateAllData();
@@ -17,6 +9,7 @@ async function runGenerators() {
17
9
  } catch (error) {
18
10
  console.error('❌ Data fetching encountered an error, but continuing build gracefully:', error);
19
11
  await generateSitemapPathsSafe();
12
+ await generateSeoManifestSafe();
20
13
  }
21
14
  }
22
15
 
@@ -0,0 +1,10 @@
1
+ import { prerenderSeoHtml } from '@marvalt/digivalt-core/generators';
2
+
3
+ async function run() {
4
+ await prerenderSeoHtml();
5
+ }
6
+
7
+ run().catch((e) => {
8
+ console.error('❌ SEO prerender failed:', e);
9
+ process.exit(1);
10
+ });
@@ -1,140 +0,0 @@
1
- # AI master prompt — DigiVAlt full local WordPress control
2
-
3
- Use this as a single copy-paste master prompt in Cursor, Claude Code, Antigravity, Lovable, or similar AI coding tools.
4
-
5
- The objective is a DigiValt app scaffold that is production-shaped from day one, where after generation a human only needs to set `.env` / `.env.local` values before first local run:
6
-
7
- ```bash
8
- npm run build && npm run dev
9
- ```
10
-
11
- ## Scope
12
-
13
- - **AI responsibilities:** scaffold code, install/wire dependencies, run `digivalt-init`, ensure scripts/files are correct, keep cloud/function wiring and SEO enabled, and build a WordPress-driven shell.
14
- - **Human responsibilities:** set real env values on local PC and cloud dashboard, run first local build/dev, replace placeholder static data with real generated exports as needed.
15
-
16
- ## Canonical sources
17
-
18
- - [README.md](../README.md)
19
- - [template/DIGIVALT_SETUP.md](../template/DIGIVALT_SETUP.md)
20
- - [template/scripts/generate.ts](../template/scripts/generate.ts)
21
- - [docs/SEO-PERFORMANCE.md](./SEO-PERFORMANCE.md)
22
- - [digivalt-landing/DIGIVALT_SETUP.md](../../../digivalt-landing/DIGIVALT_SETUP.md)
23
-
24
- ---
25
-
26
- ## Master prompt text (paste below into your AI tool)
27
-
28
- You are a senior full-stack engineer scaffolding a DigiValt consumer app (Vite + React + TypeScript) with full WordPress-controlled static content behavior. This workflow must work the same whether run from Cursor, Claude Code, Antigravity, Lovable, or another AI assistant.
29
-
30
- ### Non-negotiables
31
-
32
- - Install `@marvalt/digivalt-core@latest`.
33
- - Use `npx digivalt-init` to enforce DigiValt-managed scaffold files and scripts.
34
- - Keep Cloudflare Pages Functions + `wrangler.toml` enabled.
35
- - Keep SEO enabled by default (Helmet provider + DigiValt SEO helpers).
36
- - Keep local secrets out of git and ignore `.env`, `.env.local`, `.env.*.local`, `.dev.vars`.
37
- - Do not hand-maintain `public/sitemap-paths.json`; it is generated.
38
- - Do not create `public/sitemap.xml` when using `functions/sitemap.xml.ts`.
39
-
40
- ### File/path requirements
41
-
42
- Use these exact static data paths:
43
-
44
- - `public/wordpress-data.json`
45
- - `public/gravity-forms-data.json`
46
- - `src/data/mautic-data.json`
47
-
48
- If real exports are unavailable, use template starter fixtures (or equivalent minimal placeholders) so the app boots. Mark placeholders clearly.
49
-
50
- ### Build wiring requirements
51
-
52
- Ensure these commands exist and are used:
53
-
54
- ```bash
55
- npm install @marvalt/digivalt-core@latest
56
- npm install -D vite-node
57
- npx digivalt-init
58
- npm install react-helmet-async
59
- ```
60
-
61
- Expected scripts:
62
-
63
- - `generate`: `npx vite-node --options.deps.inline="@marvalt/digivalt-core/generators" scripts/generate.ts`
64
- - `build`: `npx vite-node --options.deps.inline="@marvalt/digivalt-core/generators" scripts/generate.ts && vite build`
65
- - `build:dev`: `npx vite-node --options.deps.inline="@marvalt/digivalt-core/generators" scripts/generate.ts && vite build --mode development`
66
- - `dev`: `vite`
67
-
68
- ### `scripts/generate.ts` behavior
69
-
70
- Import from `@marvalt/digivalt-core/generators` only:
71
-
72
- - In hosted fixture mode (`VITE_IS_LOVABLE=true`): skip fetch, run `generateSitemapPathsSafe()`.
73
- - In normal local mode (default): run `generateAllData()` then sitemap; on fetch failure still run sitemap safe fallback.
74
-
75
- ### WordPress-driven runtime wiring (must implement)
76
-
77
- After `loadWordPressData()` from `@marvalt/digivalt-core/static`, wire the app shell and routes using WordPress static data:
78
-
79
- 1. Menus from `getNavigationMenu()` / `getFooterNavigationMenu()` / `getMenuById()` (+ `getParsedHeader()` / `getHeaderMenuId()` / `getFooterMenuId()`). Assign classic menus in WP under **Appearance → Menus → Manage Locations** (`{frontendId}-header|footer`, then `default-*`).
80
- 2. Optional header/footer template blocks from `getHeader()` / `getFooter()` (legacy). Prefer app-owned chrome + location menus for multi-frontend.
81
- 3. Site identity from `getSiteSettings()` / `getLogoUrl()` / `getSiteIconUrl()`.
82
- 4. Theme styles from `getWordPressData().theme_styles` mapped into app styling/tokens.
83
- 5. Pages/posts/CPT routes from `getWordPressPages()`, `getWordPressPageBySlug()`, `getWordPressPosts()`, and `front_page`.
84
-
85
- For forms, support Gravity Forms and Mautic via `@marvalt/digivalt-core/services` and/or JSON static stores.
86
-
87
- ### Cloudflare and sitemap requirements
88
-
89
- - Keep `functions/sitemap.xml.ts`.
90
- - Keep `functions/api/*` handlers from template and keep `wrangler.toml`.
91
- - Ensure sitemap flow is active:
92
- - input: `public/sitemap-static-paths.json`
93
- - generated output: `public/sitemap-paths.json`
94
- - runtime endpoint: `/sitemap.xml` from Pages Function
95
-
96
- ### AI output constraints
97
-
98
- - Do not request humans to hand-edit code before first run.
99
- - After scaffolding, the only required manual step before first run is env values in `.env` / `.env.local`.
100
- - Provide a final validation checklist and commands:
101
- - `npm run build`
102
- - `npm run dev`
103
-
104
- ---
105
-
106
- ## Built-in defaults (use unless humans override later)
107
-
108
- - Package: `@marvalt/digivalt-core@latest`
109
- - Static data: template starter fixtures for WordPress, Gravity Forms, Mautic
110
- - SEO: ON
111
- - Cloudflare Functions + `wrangler.toml`: ON
112
- - Form/page mapping: derive from WordPress content and shortcodes first (`gf_embed`, `mautic_form`, `phplist_subscribe`; see `GLOBAL-SHORTCODES.md`)
113
-
114
- ---
115
-
116
- ## Pre-flight checklist
117
-
118
- | Step | Done |
119
- |------|------|
120
- | `@marvalt/digivalt-core@latest` installed | ☐ |
121
- | `digivalt-init` run and scripts verified | ☐ |
122
- | Required static data files exist (`wordpress`, `gravity-forms`, `mautic`) | ☐ |
123
- | `sitemap-static-paths.json` exists and `sitemap-paths.json` is generated | ☐ |
124
- | Cloudflare functions/wrangler files present | ☐ |
125
- | SEO imports + `HelmetProvider` wired | ☐ |
126
- | `.gitignore` excludes local secret files | ☐ |
127
-
128
- ---
129
-
130
- ## Reference paths in this monorepo
131
-
132
- | Concern | File |
133
- |---------|------|
134
- | Local setup baseline | [digivalt-landing/DIGIVALT_SETUP.md](../../../digivalt-landing/DIGIVALT_SETUP.md) |
135
- | Template setup baseline | [template/DIGIVALT_SETUP.md](../template/DIGIVALT_SETUP.md) |
136
- | Init behavior and script expectations | [README.md](../README.md) |
137
- | Generator script template | [template/scripts/generate.ts](../template/scripts/generate.ts) |
138
- | Landing generator implementation | [digivalt-landing/scripts/generate.ts](../../../digivalt-landing/scripts/generate.ts) |
139
- | SEO and sitemap behavior | [docs/SEO-PERFORMANCE.md](./SEO-PERFORMANCE.md) |
140
- | WP static getters and shell helpers | [src/static/wpStatic.ts](../src/static/wpStatic.ts) |