irgen 0.2.2 → 0.2.3

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
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to the `irgen` project will be documented in this file.
4
4
 
5
+ ## [0.2.3] - 2026-01-21
6
+
7
+ ### Bug Fixes
8
+ - **Frontend Emitter**: Fixed `ReferenceError: rowActionIcons is not defined` in `frontend-react.ts` when rendering table row actions.
9
+ - **Frontend Components**: Resolved `[object Object]` AST leak in code blocks by ensuring `React` is correctly imported and utilized for runtime element creation.
10
+
11
+ ### Documentation & Verification
12
+ - **Test Suite**: Verified all core golden tests (Backend, Electron, Static Site) pass with the latest fixes.
13
+ - **Cleanup**: Improved documentation quality and consistency in core emitters.
14
+
5
15
  ## [0.2.2] - 2026-01-18
6
16
 
7
17
  ### Core Extensions & Architecture (Phase 10)
package/README.md CHANGED
@@ -160,3 +160,5 @@ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for details on:
160
160
  - [x] Automated Testing
161
161
  - [x] Rich Frontend Components & Routing
162
162
  - [x] Frontend SSG/Hybrid (Vite prerender)
163
+
164
+ See [docs/ROADMAP-FUTURE.md](docs/ROADMAP-FUTURE.md) for the upcoming **v0.3.0 (Enterprise & Ecosystem)** plan.
@@ -220,7 +220,9 @@ export async function loadFrontendDsl(entry) {
220
220
  // reset
221
221
  _global.__IR_CURRENT_FRONTEND = null;
222
222
  try {
223
- await import(url);
223
+ // Add cache buster to force re-execution if file is already in module cache
224
+ const cacheBuster = `?cb=${Date.now()}`;
225
+ await import(url + cacheBuster);
224
226
  }
225
227
  catch (err) {
226
228
  // Some environments (tsx ESM loader) may fail resolving .ts imports via file URL.
@@ -92,7 +92,9 @@ export async function loadDsl(entry) {
92
92
  // reset
93
93
  _global.__IR_CURRENT_BACKEND = null;
94
94
  try {
95
- await import(url);
95
+ // Add cache buster to force re-execution if file is already in module cache
96
+ const cacheBuster = `?cb=${Date.now()}`;
97
+ await import(url + cacheBuster);
96
98
  }
97
99
  catch (err) {
98
100
  const errMessage = err instanceof Error ? err.message : String(err);
@@ -41,8 +41,8 @@ export function emitComponent(project, frontendDir, component, ir) {
41
41
  namedImports: ["Prism as SyntaxHighlighter"],
42
42
  });
43
43
  sf.addImportDeclaration({
44
- moduleSpecifier: "react-syntax-highlighter/dist/esm/styles/prism",
45
- namedImports: ["oneDark"],
44
+ moduleSpecifier: "react-syntax-highlighter/dist/esm/styles/prism/one-dark",
45
+ defaultImport: "oneDark",
46
46
  });
47
47
  }
48
48
  // Import layout child components if any
@@ -1162,8 +1162,10 @@ export function emitComponent(project, frontendDir, component, ir) {
1162
1162
  const toExpr = actionExpr(action.onClick.to);
1163
1163
  const confirmMsg = action.onClick.confirmMessage;
1164
1164
  const confirmSnippet = confirmMsg ? `if (!window.confirm(${JSON.stringify(confirmMsg)})) return; ` : "";
1165
+ const iconName = rowActionIcons[label];
1166
+ const iconSnippet = iconName ? `React.createElement((Icons as any)["${iconName}"] || Icons.Square, { size: 14, className: "mr-1.5" })` : "null";
1165
1167
  writer.writeLine(` <button type="button" className="${tableActionButtonClass}" onClick={(e) => { e.stopPropagation(); ${confirmSnippet} let target = evalLogic(${toExpr}, undefined, { item }); if (typeof target === "string" && target.includes(":")) { target = target.replace(/:([A-Za-z0-9_]+)/g, (_: any, key: string) => String(item[key] ?? "")); } if (target) navigate(String(target)); }}>`);
1166
- writer.writeLine(` {rowActionIcons["${label}"] ? React.createElement((Icons as any)[rowActionIcons["${label}"]] || Icons.Square, { size: 14, className: "mr-1.5" }) : null}`);
1168
+ writer.writeLine(` {${iconSnippet}}`);
1167
1169
  writer.writeLine(` ${label}`);
1168
1170
  writer.writeLine(` </button>`);
1169
1171
  }
@@ -1172,8 +1174,10 @@ export function emitComponent(project, frontendDir, component, ir) {
1172
1174
  const argsCode = argsExpr !== "undefined" ? `evalLogic(${argsExpr}, undefined, { item })` : "{}";
1173
1175
  const confirmMsg = action.onClick.confirmMessage;
1174
1176
  const confirmSnippet = confirmMsg ? `if (!window.confirm(${JSON.stringify(confirmMsg)})) return; ` : "";
1177
+ const iconName = rowActionIcons[label];
1178
+ const iconSnippet = iconName ? `React.createElement((Icons as any)["${iconName}"] || Icons.Square, { size: 14, className: "mr-1.5" })` : "null";
1175
1179
  writer.writeLine(` <button type="button" className="${tableActionButtonClass}" onClick={(e) => { e.stopPropagation(); ${confirmSnippet} const input = ${argsCode}; rowActionOp_${idx}.execute(input, { kind: "tableRow", pageId: "", rowId: String(item["slug"] ?? i) }); }}>`);
1176
- writer.writeLine(` {rowActionIcons["${label}"] ? React.createElement((Icons as any)[rowActionIcons["${label}"]] || Icons.Square, { size: 14, className: "mr-1.5" }) : null}`);
1180
+ writer.writeLine(` {${iconSnippet}}`);
1177
1181
  writer.writeLine(` ${label}`);
1178
1182
  writer.writeLine(` </button>`);
1179
1183
  }
@@ -1218,6 +1222,33 @@ export function emitComponent(project, frontendDir, component, ir) {
1218
1222
  }
1219
1223
  export function emitMarketingComponent(writer, m, policy, actionHandlers) {
1220
1224
  const primaryColor = policy.styling.theme.primaryColor;
1225
+ const basePath = policy.framework?.rendering?.basePath ?? "/";
1226
+ const normalizeBasePath = (value) => {
1227
+ if (!value || value === "/")
1228
+ return "";
1229
+ let normalized = value.trim();
1230
+ if (!normalized.startsWith("/"))
1231
+ normalized = `/${normalized}`;
1232
+ if (normalized.endsWith("/"))
1233
+ normalized = normalized.slice(0, -1);
1234
+ return normalized;
1235
+ };
1236
+ const resolveHref = (href) => {
1237
+ if (!href)
1238
+ return "#";
1239
+ if (href.startsWith("#"))
1240
+ return href;
1241
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(href) || href.startsWith("//"))
1242
+ return href;
1243
+ const base = normalizeBasePath(basePath);
1244
+ if (!base)
1245
+ return href;
1246
+ if (!href.startsWith("/"))
1247
+ return `${base}/${href}`;
1248
+ if (href === base || href.startsWith(`${base}/`))
1249
+ return href;
1250
+ return `${base}${href}`;
1251
+ };
1221
1252
  const marketingVisual = policy.visual?.marketing ?? {};
1222
1253
  const radiusMap = { none: "rounded-none", sm: "rounded-sm", md: "rounded-md", lg: "rounded-lg", full: "rounded-full" };
1223
1254
  const radius = radiusMap[policy.styling.theme.borderRadius] || "rounded-xl";
@@ -1245,11 +1276,12 @@ export function emitMarketingComponent(writer, m, policy, actionHandlers) {
1245
1276
  m.actions.forEach((a, idx) => {
1246
1277
  const btnCls = a.variant === "primary" ? `bg-[${primaryColor}] text-white` : "bg-white/10 text-white border border-white/20 hover:bg-white/20";
1247
1278
  const handler = actionHandlers?.[idx];
1279
+ const href = resolveHref(a.href);
1248
1280
  if (handler) {
1249
1281
  writer.writeLine(` <button type="button" onClick={${handler}} className="inline-flex items-center gap-2 px-6 py-3 font-semibold ${radius} transition-all ${btnCls}">`);
1250
1282
  }
1251
1283
  else {
1252
- writer.writeLine(` <a href="${a.href ?? "#"}" className="inline-flex items-center gap-2 px-6 py-3 font-semibold ${radius} transition-all ${btnCls}">`);
1284
+ writer.writeLine(` <a href="${href}" className="inline-flex items-center gap-2 px-6 py-3 font-semibold ${radius} transition-all ${btnCls}">`);
1253
1285
  }
1254
1286
  if (a.icon)
1255
1287
  writer.writeLine(` {React.createElement((Icons as any)["${a.icon}"], { size: 20 })}`);
@@ -1377,11 +1409,12 @@ export function emitMarketingComponent(writer, m, policy, actionHandlers) {
1377
1409
  writer.writeLine(` <div className="flex justify-center gap-4 pt-4">`);
1378
1410
  m.actions.forEach((a, idx) => {
1379
1411
  const handler = actionHandlers?.[idx];
1412
+ const href = resolveHref(a.href);
1380
1413
  if (handler) {
1381
1414
  writer.writeLine(` <button type="button" onClick={${handler}} className="inline-flex items-center gap-2 px-8 py-3.5 font-bold ${radius} bg-[${primaryColor}] text-white hover:scale-105 active:scale-95 shadow-xl shadow-[${primaryColor}]/20 transition-all">`);
1382
1415
  }
1383
1416
  else {
1384
- writer.writeLine(` <a href="${a.href ?? "#"}" className="inline-flex items-center gap-2 px-8 py-3.5 font-bold ${radius} bg-[${primaryColor}] text-white hover:scale-105 active:scale-95 shadow-xl shadow-[${primaryColor}]/20 transition-all">`);
1417
+ writer.writeLine(` <a href="${href}" className="inline-flex items-center gap-2 px-8 py-3.5 font-bold ${radius} bg-[${primaryColor}] text-white hover:scale-105 active:scale-95 shadow-xl shadow-[${primaryColor}]/20 transition-all">`);
1385
1418
  }
1386
1419
  if (a.icon)
1387
1420
  writer.writeLine(` {React.createElement((Icons as any)["${a.icon}"], { size: 20 })}`);
@@ -262,7 +262,9 @@ if ('serviceWorker' in navigator) {
262
262
  writer.writeLine("const adminMainClass = `${adminOffsetClass} max-w-none mx-auto ${contentPaddingClass} py-6`;");
263
263
  writer.writeLine("const defaultMainClass = `${contentWidthClass} mx-auto ${contentPaddingClass} ${densityClass} ${motionPageEnter}`;");
264
264
  writer.writeLine("const docsMainClass = `${visualContentWidth === \"full\" ? \"max-w-none\" : \"max-w-[1400px]\"} mx-auto ${docsPaddingClass} ${docsDensityClass} ${motionDocsEnter}`;");
265
- writer.writeLine("const isActivePath = (path: string) => relativePath === path;");
265
+ writer.writeLine("const normalizePath = (p: string) => (p && p.length > 1 ? p.replace(/\\/$/, \"\") : p || \"/\");");
266
+ writer.writeLine("const normalizedPath = normalizePath(relativePath);");
267
+ writer.writeLine("const isActivePath = (path: string) => normalizePath(path) === normalizedPath;");
266
268
  writer.writeLine(`const docsLinks = ${JSON.stringify(docsLinks, null, 2)};`);
267
269
  writer.writeLine(`const defaultDocsGroupLabel = ${JSON.stringify(docsGroupLabel)};`);
268
270
  writer.writeLine("const docsSidebarGroups = docsLinks.reduce((acc, link) => {");
@@ -272,8 +274,8 @@ if ('serviceWorker' in navigator) {
272
274
  writer.writeLine(" group.items.push(link);");
273
275
  writer.writeLine(" return acc;");
274
276
  writer.writeLine("}, [] as Array<{ label: string; items: typeof docsLinks }>)");
275
- writer.writeLine("const docsPaths = docsLinks.map((link) => link.path);");
276
- writer.writeLine("const isDocsRoute = docsPaths.includes(relativePath);");
277
+ writer.writeLine("const docsPaths = docsLinks.map((link) => normalizePath(link.path));");
278
+ writer.writeLine("const isDocsRoute = docsPaths.includes(normalizedPath);");
277
279
  writer.writeLine("const topbarLinks = Array.isArray(visualNavItems.topbar) && visualNavItems.topbar.length > 0 ? visualNavItems.topbar : " + JSON.stringify(navbarLinks) + ";");
278
280
  writer.writeLine("const sidebarLinks = Array.isArray(visualNavItems.sidebar) && visualNavItems.sidebar.length > 0 ? visualNavItems.sidebar : " + JSON.stringify(navbarLinks) + ";");
279
281
  writer.writeLine("const footerLinks = Array.isArray(visualFooterLinks) ? visualFooterLinks : [{ label: \"Terms\", href: \"#\" }, { label: \"Privacy\", href: \"#\" }, { label: \"Contact\", href: \"#\" }];");
@@ -281,31 +281,85 @@ export declare const FrontendPolicySchema: z.ZodDefault<z.ZodObject<{
281
281
  fromDir?: string | undefined;
282
282
  } | undefined;
283
283
  }>>;
284
- }, "strip", z.ZodTypeAny, {
285
- build: {
286
- postbuild?: string | undefined;
287
- copyTo?: {
288
- enabled: boolean;
289
- targetRoot: string;
290
- fromDir: string;
291
- basePath?: string | undefined;
292
- } | undefined;
293
- copyToPublic?: {
294
- enabled: boolean;
295
- targetRoot: string;
296
- fromDir: string;
297
- basePath?: string | undefined;
298
- } | undefined;
299
- };
300
- styling: {
284
+ visual: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
285
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
286
+ styling: z.ZodDefault<z.ZodObject<{
287
+ cssFramework: z.ZodDefault<z.ZodEnum<["tailwind", "none"]>>;
288
+ themePack: z.ZodDefault<z.ZodEnum<["default", "admin"]>>;
289
+ theme: z.ZodDefault<z.ZodObject<{
290
+ primaryColor: z.ZodDefault<z.ZodString>;
291
+ borderRadius: z.ZodDefault<z.ZodEnum<["none", "sm", "md", "lg", "full"]>>;
292
+ }, "strip", z.ZodTypeAny, {
293
+ primaryColor: string;
294
+ borderRadius: "none" | "sm" | "md" | "lg" | "full";
295
+ }, {
296
+ primaryColor?: string | undefined;
297
+ borderRadius?: "none" | "sm" | "md" | "lg" | "full" | undefined;
298
+ }>>;
299
+ }, "strip", z.ZodTypeAny, {
301
300
  cssFramework: "none" | "tailwind";
302
301
  themePack: "default" | "admin";
303
302
  theme: {
304
303
  primaryColor: string;
305
304
  borderRadius: "none" | "sm" | "md" | "lg" | "full";
306
305
  };
307
- };
308
- framework: {
306
+ }, {
307
+ cssFramework?: "none" | "tailwind" | undefined;
308
+ themePack?: "default" | "admin" | undefined;
309
+ theme?: {
310
+ primaryColor?: string | undefined;
311
+ borderRadius?: "none" | "sm" | "md" | "lg" | "full" | undefined;
312
+ } | undefined;
313
+ }>>;
314
+ framework: z.ZodDefault<z.ZodObject<{
315
+ library: z.ZodDefault<z.ZodEnum<["react"]>>;
316
+ runtime: z.ZodDefault<z.ZodEnum<["vite", "none"]>>;
317
+ router: z.ZodDefault<z.ZodEnum<["react-router-dom", "none"]>>;
318
+ iconLibrary: z.ZodDefault<z.ZodEnum<["lucide-react", "none"]>>;
319
+ rendering: z.ZodDefault<z.ZodObject<{
320
+ mode: z.ZodDefault<z.ZodEnum<["csr", "ssg", "hybrid"]>>;
321
+ basePath: z.ZodDefault<z.ZodString>;
322
+ prerender: z.ZodDefault<z.ZodObject<{
323
+ enabled: z.ZodDefault<z.ZodBoolean>;
324
+ routes: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"auto">, z.ZodArray<z.ZodString, "many">]>>;
325
+ outDir: z.ZodDefault<z.ZodString>;
326
+ emitSitemap: z.ZodDefault<z.ZodBoolean>;
327
+ emitRobotsTxt: z.ZodDefault<z.ZodBoolean>;
328
+ }, "strip", z.ZodTypeAny, {
329
+ enabled: boolean;
330
+ outDir: string;
331
+ routes: string[] | "auto";
332
+ emitSitemap: boolean;
333
+ emitRobotsTxt: boolean;
334
+ }, {
335
+ enabled?: boolean | undefined;
336
+ outDir?: string | undefined;
337
+ routes?: string[] | "auto" | undefined;
338
+ emitSitemap?: boolean | undefined;
339
+ emitRobotsTxt?: boolean | undefined;
340
+ }>>;
341
+ }, "strip", z.ZodTypeAny, {
342
+ basePath: string;
343
+ mode: "csr" | "ssg" | "hybrid";
344
+ prerender: {
345
+ enabled: boolean;
346
+ outDir: string;
347
+ routes: string[] | "auto";
348
+ emitSitemap: boolean;
349
+ emitRobotsTxt: boolean;
350
+ };
351
+ }, {
352
+ basePath?: string | undefined;
353
+ mode?: "csr" | "ssg" | "hybrid" | undefined;
354
+ prerender?: {
355
+ enabled?: boolean | undefined;
356
+ outDir?: string | undefined;
357
+ routes?: string[] | "auto" | undefined;
358
+ emitSitemap?: boolean | undefined;
359
+ emitRobotsTxt?: boolean | undefined;
360
+ } | undefined;
361
+ }>>;
362
+ }, "strip", z.ZodTypeAny, {
309
363
  library: "react";
310
364
  runtime: "none" | "vite";
311
365
  router: "none" | "react-router-dom";
@@ -321,9 +375,72 @@ export declare const FrontendPolicySchema: z.ZodDefault<z.ZodObject<{
321
375
  emitRobotsTxt: boolean;
322
376
  };
323
377
  };
324
- };
325
- }, {
326
- build?: {
378
+ }, {
379
+ library?: "react" | undefined;
380
+ runtime?: "none" | "vite" | undefined;
381
+ router?: "none" | "react-router-dom" | undefined;
382
+ iconLibrary?: "none" | "lucide-react" | undefined;
383
+ rendering?: {
384
+ basePath?: string | undefined;
385
+ mode?: "csr" | "ssg" | "hybrid" | undefined;
386
+ prerender?: {
387
+ enabled?: boolean | undefined;
388
+ outDir?: string | undefined;
389
+ routes?: string[] | "auto" | undefined;
390
+ emitSitemap?: boolean | undefined;
391
+ emitRobotsTxt?: boolean | undefined;
392
+ } | undefined;
393
+ } | undefined;
394
+ }>>;
395
+ build: z.ZodDefault<z.ZodObject<{
396
+ postbuild: z.ZodOptional<z.ZodString>;
397
+ copyTo: z.ZodOptional<z.ZodObject<{
398
+ enabled: z.ZodDefault<z.ZodBoolean>;
399
+ targetRoot: z.ZodDefault<z.ZodString>;
400
+ basePath: z.ZodOptional<z.ZodString>;
401
+ fromDir: z.ZodDefault<z.ZodString>;
402
+ }, "strip", z.ZodTypeAny, {
403
+ enabled: boolean;
404
+ targetRoot: string;
405
+ fromDir: string;
406
+ basePath?: string | undefined;
407
+ }, {
408
+ enabled?: boolean | undefined;
409
+ basePath?: string | undefined;
410
+ targetRoot?: string | undefined;
411
+ fromDir?: string | undefined;
412
+ }>>;
413
+ copyToPublic: z.ZodOptional<z.ZodObject<{
414
+ enabled: z.ZodDefault<z.ZodBoolean>;
415
+ targetRoot: z.ZodDefault<z.ZodString>;
416
+ basePath: z.ZodOptional<z.ZodString>;
417
+ fromDir: z.ZodDefault<z.ZodString>;
418
+ }, "strip", z.ZodTypeAny, {
419
+ enabled: boolean;
420
+ targetRoot: string;
421
+ fromDir: string;
422
+ basePath?: string | undefined;
423
+ }, {
424
+ enabled?: boolean | undefined;
425
+ basePath?: string | undefined;
426
+ targetRoot?: string | undefined;
427
+ fromDir?: string | undefined;
428
+ }>>;
429
+ }, "strip", z.ZodTypeAny, {
430
+ postbuild?: string | undefined;
431
+ copyTo?: {
432
+ enabled: boolean;
433
+ targetRoot: string;
434
+ fromDir: string;
435
+ basePath?: string | undefined;
436
+ } | undefined;
437
+ copyToPublic?: {
438
+ enabled: boolean;
439
+ targetRoot: string;
440
+ fromDir: string;
441
+ basePath?: string | undefined;
442
+ } | undefined;
443
+ }, {
327
444
  postbuild?: string | undefined;
328
445
  copyTo?: {
329
446
  enabled?: boolean | undefined;
@@ -337,16 +454,102 @@ export declare const FrontendPolicySchema: z.ZodDefault<z.ZodObject<{
337
454
  targetRoot?: string | undefined;
338
455
  fromDir?: string | undefined;
339
456
  } | undefined;
340
- } | undefined;
341
- styling?: {
457
+ }>>;
458
+ visual: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
459
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
460
+ styling: z.ZodDefault<z.ZodObject<{
461
+ cssFramework: z.ZodDefault<z.ZodEnum<["tailwind", "none"]>>;
462
+ themePack: z.ZodDefault<z.ZodEnum<["default", "admin"]>>;
463
+ theme: z.ZodDefault<z.ZodObject<{
464
+ primaryColor: z.ZodDefault<z.ZodString>;
465
+ borderRadius: z.ZodDefault<z.ZodEnum<["none", "sm", "md", "lg", "full"]>>;
466
+ }, "strip", z.ZodTypeAny, {
467
+ primaryColor: string;
468
+ borderRadius: "none" | "sm" | "md" | "lg" | "full";
469
+ }, {
470
+ primaryColor?: string | undefined;
471
+ borderRadius?: "none" | "sm" | "md" | "lg" | "full" | undefined;
472
+ }>>;
473
+ }, "strip", z.ZodTypeAny, {
474
+ cssFramework: "none" | "tailwind";
475
+ themePack: "default" | "admin";
476
+ theme: {
477
+ primaryColor: string;
478
+ borderRadius: "none" | "sm" | "md" | "lg" | "full";
479
+ };
480
+ }, {
342
481
  cssFramework?: "none" | "tailwind" | undefined;
343
482
  themePack?: "default" | "admin" | undefined;
344
483
  theme?: {
345
484
  primaryColor?: string | undefined;
346
485
  borderRadius?: "none" | "sm" | "md" | "lg" | "full" | undefined;
347
486
  } | undefined;
348
- } | undefined;
349
- framework?: {
487
+ }>>;
488
+ framework: z.ZodDefault<z.ZodObject<{
489
+ library: z.ZodDefault<z.ZodEnum<["react"]>>;
490
+ runtime: z.ZodDefault<z.ZodEnum<["vite", "none"]>>;
491
+ router: z.ZodDefault<z.ZodEnum<["react-router-dom", "none"]>>;
492
+ iconLibrary: z.ZodDefault<z.ZodEnum<["lucide-react", "none"]>>;
493
+ rendering: z.ZodDefault<z.ZodObject<{
494
+ mode: z.ZodDefault<z.ZodEnum<["csr", "ssg", "hybrid"]>>;
495
+ basePath: z.ZodDefault<z.ZodString>;
496
+ prerender: z.ZodDefault<z.ZodObject<{
497
+ enabled: z.ZodDefault<z.ZodBoolean>;
498
+ routes: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"auto">, z.ZodArray<z.ZodString, "many">]>>;
499
+ outDir: z.ZodDefault<z.ZodString>;
500
+ emitSitemap: z.ZodDefault<z.ZodBoolean>;
501
+ emitRobotsTxt: z.ZodDefault<z.ZodBoolean>;
502
+ }, "strip", z.ZodTypeAny, {
503
+ enabled: boolean;
504
+ outDir: string;
505
+ routes: string[] | "auto";
506
+ emitSitemap: boolean;
507
+ emitRobotsTxt: boolean;
508
+ }, {
509
+ enabled?: boolean | undefined;
510
+ outDir?: string | undefined;
511
+ routes?: string[] | "auto" | undefined;
512
+ emitSitemap?: boolean | undefined;
513
+ emitRobotsTxt?: boolean | undefined;
514
+ }>>;
515
+ }, "strip", z.ZodTypeAny, {
516
+ basePath: string;
517
+ mode: "csr" | "ssg" | "hybrid";
518
+ prerender: {
519
+ enabled: boolean;
520
+ outDir: string;
521
+ routes: string[] | "auto";
522
+ emitSitemap: boolean;
523
+ emitRobotsTxt: boolean;
524
+ };
525
+ }, {
526
+ basePath?: string | undefined;
527
+ mode?: "csr" | "ssg" | "hybrid" | undefined;
528
+ prerender?: {
529
+ enabled?: boolean | undefined;
530
+ outDir?: string | undefined;
531
+ routes?: string[] | "auto" | undefined;
532
+ emitSitemap?: boolean | undefined;
533
+ emitRobotsTxt?: boolean | undefined;
534
+ } | undefined;
535
+ }>>;
536
+ }, "strip", z.ZodTypeAny, {
537
+ library: "react";
538
+ runtime: "none" | "vite";
539
+ router: "none" | "react-router-dom";
540
+ iconLibrary: "none" | "lucide-react";
541
+ rendering: {
542
+ basePath: string;
543
+ mode: "csr" | "ssg" | "hybrid";
544
+ prerender: {
545
+ enabled: boolean;
546
+ outDir: string;
547
+ routes: string[] | "auto";
548
+ emitSitemap: boolean;
549
+ emitRobotsTxt: boolean;
550
+ };
551
+ };
552
+ }, {
350
553
  library?: "react" | undefined;
351
554
  runtime?: "none" | "vite" | undefined;
352
555
  router?: "none" | "react-router-dom" | undefined;
@@ -362,7 +565,71 @@ export declare const FrontendPolicySchema: z.ZodDefault<z.ZodObject<{
362
565
  emitRobotsTxt?: boolean | undefined;
363
566
  } | undefined;
364
567
  } | undefined;
365
- } | undefined;
366
- }>>;
568
+ }>>;
569
+ build: z.ZodDefault<z.ZodObject<{
570
+ postbuild: z.ZodOptional<z.ZodString>;
571
+ copyTo: z.ZodOptional<z.ZodObject<{
572
+ enabled: z.ZodDefault<z.ZodBoolean>;
573
+ targetRoot: z.ZodDefault<z.ZodString>;
574
+ basePath: z.ZodOptional<z.ZodString>;
575
+ fromDir: z.ZodDefault<z.ZodString>;
576
+ }, "strip", z.ZodTypeAny, {
577
+ enabled: boolean;
578
+ targetRoot: string;
579
+ fromDir: string;
580
+ basePath?: string | undefined;
581
+ }, {
582
+ enabled?: boolean | undefined;
583
+ basePath?: string | undefined;
584
+ targetRoot?: string | undefined;
585
+ fromDir?: string | undefined;
586
+ }>>;
587
+ copyToPublic: z.ZodOptional<z.ZodObject<{
588
+ enabled: z.ZodDefault<z.ZodBoolean>;
589
+ targetRoot: z.ZodDefault<z.ZodString>;
590
+ basePath: z.ZodOptional<z.ZodString>;
591
+ fromDir: z.ZodDefault<z.ZodString>;
592
+ }, "strip", z.ZodTypeAny, {
593
+ enabled: boolean;
594
+ targetRoot: string;
595
+ fromDir: string;
596
+ basePath?: string | undefined;
597
+ }, {
598
+ enabled?: boolean | undefined;
599
+ basePath?: string | undefined;
600
+ targetRoot?: string | undefined;
601
+ fromDir?: string | undefined;
602
+ }>>;
603
+ }, "strip", z.ZodTypeAny, {
604
+ postbuild?: string | undefined;
605
+ copyTo?: {
606
+ enabled: boolean;
607
+ targetRoot: string;
608
+ fromDir: string;
609
+ basePath?: string | undefined;
610
+ } | undefined;
611
+ copyToPublic?: {
612
+ enabled: boolean;
613
+ targetRoot: string;
614
+ fromDir: string;
615
+ basePath?: string | undefined;
616
+ } | undefined;
617
+ }, {
618
+ postbuild?: string | undefined;
619
+ copyTo?: {
620
+ enabled?: boolean | undefined;
621
+ basePath?: string | undefined;
622
+ targetRoot?: string | undefined;
623
+ fromDir?: string | undefined;
624
+ } | undefined;
625
+ copyToPublic?: {
626
+ enabled?: boolean | undefined;
627
+ basePath?: string | undefined;
628
+ targetRoot?: string | undefined;
629
+ fromDir?: string | undefined;
630
+ } | undefined;
631
+ }>>;
632
+ visual: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
633
+ }, z.ZodTypeAny, "passthrough">>>;
367
634
  export type FrontendPolicy = z.infer<typeof FrontendPolicySchema>;
368
635
  export declare function normalizeFrontendPolicy(input: unknown): FrontendPolicy;
@@ -42,7 +42,8 @@ export const FrontendPolicySchema = z.object({
42
42
  fromDir: z.string().default("dist"),
43
43
  }).optional(),
44
44
  }).default({}),
45
- }).default({});
45
+ visual: z.record(z.any()).optional(),
46
+ }).passthrough().default({});
46
47
  export function normalizeFrontendPolicy(input) {
47
48
  return FrontendPolicySchema.parse(input || {});
48
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "irgen",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "a policy-driven, IR-based code generation toolchain for backend, frontend, and beyond.",