@decantr/core 1.0.0-beta.8 → 1.0.0

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/dist/index.js CHANGED
@@ -89,15 +89,19 @@ function routePath(pageId, index) {
89
89
  function capitalize(str) {
90
90
  return str.charAt(0).toUpperCase() + str.slice(1);
91
91
  }
92
- function buildNavItems(pages) {
92
+ function buildNavItems(pages, routes) {
93
+ const routeLookup = new Map(
94
+ (routes ?? []).map((route) => [route.pageId, route.path])
95
+ );
93
96
  return pages.map((page, i) => ({
94
- href: routePath(page.id, i),
97
+ href: routeLookup.get(page.id) ?? routePath(page.id, i),
95
98
  icon: NAV_ICONS[page.id] || "circle",
96
99
  label: capitalize(page.id.replace(/-/g, " "))
97
100
  }));
98
101
  }
99
- function buildRecipeDecoration(recipe) {
100
- const shell = recipe.shell;
102
+ function buildThemeDecoration(theme) {
103
+ const shell = theme.shell;
104
+ if (!shell) return null;
101
105
  const shellAny = shell;
102
106
  return {
103
107
  root: shell.root || "",
@@ -105,7 +109,7 @@ function buildRecipeDecoration(recipe) {
105
109
  header: shell.header || "",
106
110
  brand: shellAny["brand"] || "",
107
111
  navLabel: shellAny["navLabel"] || "",
108
- // AUTO: default nav style is 'pill' per Decantr framework convention
112
+ // AUTO: default nav style is 'pill' when the theme does not declare one
109
113
  navStyle: shell.nav_style || "pill",
110
114
  defaultNavState: shellAny["default_nav_state"] || "expanded",
111
115
  dimensions: shell.dimensions || null
@@ -113,20 +117,18 @@ function buildRecipeDecoration(recipe) {
113
117
  }
114
118
  function buildTheme(essence, isAddon) {
115
119
  return {
116
- style: essence.theme.style,
120
+ id: essence.theme.id,
117
121
  mode: essence.theme.mode,
118
122
  shape: essence.theme.shape || null,
119
- recipe: essence.theme.recipe,
120
123
  isAddon
121
124
  };
122
125
  }
123
126
  function buildThemeFromV3(essence, isAddon) {
124
127
  const dna = essence.dna;
125
128
  return {
126
- style: dna.theme.style,
129
+ id: dna.theme.id,
127
130
  mode: dna.theme.mode,
128
131
  shape: dna.radius.philosophy || dna.theme.shape || null,
129
- recipe: dna.theme.recipe,
130
132
  isAddon
131
133
  };
132
134
  }
@@ -138,6 +140,23 @@ function blueprintPageToStructurePage(page, defaultShell) {
138
140
  ...page.surface ? { surface: page.surface } : {}
139
141
  };
140
142
  }
143
+ function buildV3Routes(essence, blueprintPages, structurePages) {
144
+ const explicitRoutes = /* @__PURE__ */ new Map();
145
+ for (const [path, entry] of Object.entries(essence.blueprint.routes ?? {})) {
146
+ if (entry?.page && !explicitRoutes.has(entry.page)) {
147
+ explicitRoutes.set(entry.page, path);
148
+ }
149
+ }
150
+ for (const page of blueprintPages) {
151
+ if (page.route && !explicitRoutes.has(page.id)) {
152
+ explicitRoutes.set(page.id, page.route);
153
+ }
154
+ }
155
+ return structurePages.map((page, i) => ({
156
+ path: explicitRoutes.get(page.id) ?? routePath(page.id, i),
157
+ pageId: page.id
158
+ }));
159
+ }
141
160
  function convertWiring(wiringResults) {
142
161
  if (wiringResults.length === 0) return null;
143
162
  const signals = [];
@@ -166,12 +185,13 @@ function convertWiring(wiringResults) {
166
185
  }
167
186
  return { signals, props, hooks: [...hookSet], hookProps };
168
187
  }
169
- function resolveVisualEffects(recipe, pattern, _slot) {
170
- if (!recipe.visual_effects?.enabled) return null;
171
- const typeMapping = recipe.visual_effects.type_mapping || {};
172
- const componentFallback = recipe.visual_effects.component_fallback || {};
173
- const intensity = recipe.visual_effects.intensity || "medium";
174
- const intensityValues = recipe.visual_effects.intensity_values || {};
188
+ function resolveVisualEffects(theme, pattern, _slot) {
189
+ const effects = theme.effects;
190
+ if (!effects?.enabled) return null;
191
+ const typeMapping = effects.type_mapping || {};
192
+ const componentFallback = effects.component_fallback || {};
193
+ const intensity = effects.intensity || "medium";
194
+ const intensityValues = effects.intensity_values || {};
175
195
  let decorators = [];
176
196
  for (const comp of pattern.components || []) {
177
197
  const effectType = componentFallback[comp];
@@ -216,30 +236,30 @@ async function resolveEssence(essence, resolver) {
216
236
  } else {
217
237
  throw new Error("Invalid essence format");
218
238
  }
219
- let recipe = null;
220
- const recipeResult = await resolver.resolve("recipe", simpleEssence.theme.recipe);
221
- if (recipeResult) {
222
- recipe = recipeResult.item;
239
+ let registryTheme = null;
240
+ const themeResult = await resolver.resolve("theme", simpleEssence.theme.id);
241
+ if (themeResult) {
242
+ registryTheme = themeResult.item;
223
243
  }
224
- const recipeSpatial = recipe?.spatial_hints;
225
- const density = computeDensity(simpleEssence.personality, recipeSpatial ? {
226
- density_bias: recipeSpatial.density_bias,
227
- content_gap_shift: recipeSpatial.content_gap_shift
244
+ const themeSpatial = registryTheme?.spatial;
245
+ const density = computeDensity(simpleEssence.personality, themeSpatial ? {
246
+ density_bias: themeSpatial.density_bias,
247
+ content_gap_shift: themeSpatial.content_gap_shift
228
248
  } : void 0);
229
- const style = simpleEssence.theme.style;
230
- const isAddon = style.startsWith("custom:") || !CORE_STYLES.has(style);
249
+ const themeId = simpleEssence.theme.id;
250
+ const isAddon = themeId.startsWith("custom:") || !CORE_STYLES.has(themeId);
231
251
  const theme = buildTheme(simpleEssence, isAddon);
232
- const resolvedPages = await resolvePages(simpleEssence.structure, resolver, recipe);
252
+ const resolvedPages = await resolvePages(simpleEssence.structure, resolver, registryTheme);
233
253
  const shellType = simpleEssence.structure[0]?.shell || "sidebar-main";
234
254
  const brand = pascalCase(simpleEssence.archetype);
235
255
  const nav = buildNavItems(simpleEssence.structure);
236
- const recipeDecoration = recipe ? buildRecipeDecoration(recipe) : null;
256
+ const decoration = registryTheme ? buildThemeDecoration(registryTheme) : null;
237
257
  const shell = {
238
258
  type: shellType,
239
259
  brand,
240
260
  nav,
241
261
  inset: false,
242
- recipe: recipeDecoration
262
+ decoration
243
263
  };
244
264
  const routes = simpleEssence.structure.map((page, i) => ({
245
265
  path: routePath(page.id, i),
@@ -248,7 +268,7 @@ async function resolveEssence(essence, resolver) {
248
268
  return {
249
269
  essence,
250
270
  pages: resolvedPages,
251
- recipe,
271
+ registryTheme,
252
272
  density: { gap: density.content_gap, level: density.level },
253
273
  theme,
254
274
  shell,
@@ -259,48 +279,45 @@ async function resolveEssence(essence, resolver) {
259
279
  }
260
280
  async function resolveV3Essence(essence, resolver) {
261
281
  const { dna, blueprint, meta } = essence;
262
- let recipe = null;
263
- const recipeResult = await resolver.resolve("recipe", dna.theme.recipe);
264
- if (recipeResult) {
265
- recipe = recipeResult.item;
266
- }
267
- const recipeSpatial = recipe?.spatial_hints;
268
- const density = computeDensity(dna.personality, recipeSpatial ? {
269
- density_bias: recipeSpatial.density_bias,
270
- content_gap_shift: recipeSpatial.content_gap_shift
282
+ let registryTheme = null;
283
+ const themeResult = await resolver.resolve("theme", dna.theme.id);
284
+ if (themeResult) {
285
+ registryTheme = themeResult.item;
286
+ }
287
+ const themeSpatial = registryTheme?.spatial;
288
+ const density = computeDensity(dna.personality, themeSpatial ? {
289
+ density_bias: themeSpatial.density_bias,
290
+ content_gap_shift: themeSpatial.content_gap_shift
271
291
  } : void 0);
272
292
  const densityResult = {
273
293
  gap: dna.spacing.content_gap || density.content_gap,
274
294
  level: dna.spacing.density || density.level
275
295
  };
276
- const style = dna.theme.style;
277
- const isAddon = style.startsWith("custom:") || !CORE_STYLES.has(style);
296
+ const themeId = dna.theme.id;
297
+ const isAddon = themeId.startsWith("custom:") || !CORE_STYLES.has(themeId);
278
298
  const theme = buildThemeFromV3(essence, isAddon);
279
299
  const blueprintPages = blueprint.pages ?? (blueprint.sections ? blueprint.sections.flatMap((s) => s.pages) : [{ id: "home", layout: ["hero"] }]);
280
300
  const defaultShell = blueprint.shell ?? (blueprint.sections?.[0]?.shell ?? "sidebar-main");
281
301
  const structurePages = blueprintPages.map(
282
302
  (p) => blueprintPageToStructurePage(p, defaultShell)
283
303
  );
284
- const resolvedPages = await resolvePages(structurePages, resolver, recipe);
304
+ const resolvedPages = await resolvePages(structurePages, resolver, registryTheme);
285
305
  const shellType = defaultShell;
286
306
  const brand = pascalCase(meta.archetype);
287
- const nav = buildNavItems(structurePages);
288
- const recipeDecoration = recipe ? buildRecipeDecoration(recipe) : null;
307
+ const routes = buildV3Routes(essence, blueprintPages, structurePages);
308
+ const nav = buildNavItems(structurePages, routes);
309
+ const decoration = registryTheme ? buildThemeDecoration(registryTheme) : null;
289
310
  const shell = {
290
311
  type: shellType,
291
312
  brand,
292
313
  nav,
293
314
  inset: false,
294
- recipe: recipeDecoration
315
+ decoration
295
316
  };
296
- const routes = structurePages.map((page, i) => ({
297
- path: routePath(page.id, i),
298
- pageId: page.id
299
- }));
300
317
  return {
301
318
  essence,
302
319
  pages: resolvedPages,
303
- recipe,
320
+ registryTheme,
304
321
  density: densityResult,
305
322
  theme,
306
323
  shell,
@@ -309,7 +326,7 @@ async function resolveV3Essence(essence, resolver) {
309
326
  isV3Source: true
310
327
  };
311
328
  }
312
- async function resolvePages(pages, resolver, recipe) {
329
+ async function resolvePages(pages, resolver, registryTheme) {
313
330
  const resolvedPages = [];
314
331
  for (const page of pages) {
315
332
  const refs = extractLayoutRefs(page.layout);
@@ -320,7 +337,7 @@ async function resolvePages(pages, resolver, recipe) {
320
337
  const preset = resolvePatternPreset(
321
338
  patternResult.item,
322
339
  ref.explicitPreset,
323
- recipe?.pattern_preferences?.default_presets
340
+ registryTheme?.pattern_preferences?.default_presets
324
341
  );
325
342
  const key = ref.alias || ref.id;
326
343
  patterns.set(key, { pattern: patternResult.item, preset });
@@ -340,32 +357,30 @@ function isPatternRef2(item) {
340
357
  function isColumnLayout2(item) {
341
358
  return typeof item === "object" && "cols" in item;
342
359
  }
343
- function shouldWrapInCard(pattern, preset, recipe) {
360
+ function shouldWrapInCard(pattern, preset, theme) {
344
361
  const layout = preset.layout.layout;
345
362
  if (layout === "hero" || layout === "row") return false;
346
363
  if (pattern.contained === false) return false;
347
- const cardWrapping = recipe?.spatial_hints?.card_wrapping;
364
+ const cardWrapping = theme?.spatial?.card_wrapping;
348
365
  if (cardWrapping === "none") return false;
349
366
  if (cardWrapping === "minimal") {
350
367
  return Object.keys(pattern.presets).length > 0;
351
368
  }
352
369
  return true;
353
370
  }
354
- function buildCardWrapping(pattern, recipe) {
355
- const mode = recipe?.spatial_hints?.card_wrapping || "always";
356
- const overrides = recipe?.pattern_overrides?.[pattern.id];
371
+ function buildCardWrapping(pattern, theme) {
372
+ const mode = theme?.spatial?.card_wrapping || "always";
357
373
  return {
358
374
  mode,
359
- headerLabel: pattern.name,
360
- background: overrides?.background?.join(" ")
375
+ headerLabel: pattern.name
361
376
  };
362
377
  }
363
- function buildPatternNode(patternId, alias, resolved, wiring, recipe, density, layer) {
378
+ function buildPatternNode(patternId, alias, resolved, wiring, theme, density, layer) {
364
379
  const pattern = resolved?.pattern;
365
380
  const preset = resolved?.preset;
366
381
  const layout = preset?.layout.layout || "column";
367
382
  const isStandalone = layout === "hero" || layout === "row";
368
- const contained = pattern && preset ? shouldWrapInCard(pattern, preset, recipe) : false;
383
+ const contained = pattern && preset ? shouldWrapInCard(pattern, preset, theme) : false;
369
384
  const components = pattern?.components || [];
370
385
  const patternMeta = {
371
386
  patternId,
@@ -377,9 +392,9 @@ function buildPatternNode(patternId, alias, resolved, wiring, recipe, density, l
377
392
  code: preset?.code ? { imports: preset.code.imports, example: preset.code.example } : null,
378
393
  components
379
394
  };
380
- const card = contained && !isStandalone && pattern ? buildCardWrapping(pattern, recipe) : null;
395
+ const card = contained && !isStandalone && pattern ? buildCardWrapping(pattern, theme) : null;
381
396
  const wireProps = wiring?.props[alias] || wiring?.props[patternId] || null;
382
- const visualEffects = recipe && pattern ? resolveVisualEffects(recipe, pattern) : null;
397
+ const visualEffects = theme && pattern ? resolveVisualEffects(theme, pattern) : null;
383
398
  return {
384
399
  type: "pattern",
385
400
  id: alias,
@@ -392,16 +407,16 @@ function buildPatternNode(patternId, alias, resolved, wiring, recipe, density, l
392
407
  ...layer ? { layer } : {}
393
408
  };
394
409
  }
395
- function buildPageIR(page, resolvedPatterns, wiring, recipe, density, layer) {
410
+ function buildPageIR(page, resolvedPatterns, wiring, theme, density, layer) {
396
411
  const children = [];
397
412
  for (const item of page.layout) {
398
413
  if (typeof item === "string") {
399
414
  const resolved = resolvedPatterns.get(item);
400
- children.push(buildPatternNode(item, item, resolved, wiring, recipe, density, layer));
415
+ children.push(buildPatternNode(item, item, resolved, wiring, theme, density, layer));
401
416
  } else if (isPatternRef2(item)) {
402
417
  const alias = item.as || item.pattern;
403
418
  const resolved = resolvedPatterns.get(alias) || resolvedPatterns.get(item.pattern);
404
- children.push(buildPatternNode(item.pattern, alias, resolved, wiring, recipe, density, layer));
419
+ children.push(buildPatternNode(item.pattern, alias, resolved, wiring, theme, density, layer));
405
420
  } else if (isColumnLayout2(item)) {
406
421
  const cols = item.cols;
407
422
  const breakpoint = item.at || null;
@@ -416,7 +431,7 @@ function buildPageIR(page, resolvedPatterns, wiring, recipe, density, layer) {
416
431
  const gridChildren = [];
417
432
  for (const col of cols) {
418
433
  const resolved = resolvedPatterns.get(col);
419
- gridChildren.push(buildPatternNode(col, col, resolved, wiring, recipe, density, layer));
434
+ gridChildren.push(buildPatternNode(col, col, resolved, wiring, theme, density, layer));
420
435
  }
421
436
  const totalCols = normalizedSpans ? Object.values(normalizedSpans).reduce((a, b) => a + b, 0) : cols.length;
422
437
  const breakpoints = item.breakpoints?.map((bp) => ({ at: bp.at, cols: bp.cols })) || null;
@@ -461,10 +476,15 @@ async function runPipeline(essence, options) {
461
476
  throw new Error(`Invalid essence: ${validation.errors.join(", ")}`);
462
477
  }
463
478
  const effectiveEssence = isV32(essence) ? essence : migrateV2ToV3(essence);
464
- const resolver = createResolver({
465
- contentRoot: options.contentRoot,
466
- overridePaths: options.overridePaths
467
- });
479
+ const resolver = options.resolver ?? (() => {
480
+ if (!options.contentRoot) {
481
+ throw new Error("Pipeline options must include either a contentRoot or a resolver.");
482
+ }
483
+ return createResolver({
484
+ contentRoot: options.contentRoot,
485
+ overridePaths: options.overridePaths
486
+ });
487
+ })();
468
488
  const resolved = await resolveEssence(effectiveEssence, resolver);
469
489
  const layer = resolved.isV3Source ? "blueprint" : void 0;
470
490
  const pageNodes = [];
@@ -473,7 +493,7 @@ async function runPipeline(essence, options) {
473
493
  rp.page,
474
494
  rp.patterns,
475
495
  rp.wiring,
476
- resolved.recipe,
496
+ resolved.registryTheme,
477
497
  { gap: resolved.density.gap },
478
498
  layer
479
499
  );
@@ -557,14 +577,853 @@ function validateIR(root) {
557
577
  });
558
578
  return errors;
559
579
  }
580
+
581
+ // src/packs.ts
582
+ import { isV3 as isV33, migrateV2ToV3 as migrateV2ToV32 } from "@decantr/essence-spec";
583
+ var EXECUTION_PACK_SCHEMA_URLS = {
584
+ scaffold: "https://decantr.ai/schemas/scaffold-pack.v1.json",
585
+ section: "https://decantr.ai/schemas/section-pack.v1.json",
586
+ page: "https://decantr.ai/schemas/page-pack.v1.json",
587
+ mutation: "https://decantr.ai/schemas/mutation-pack.v1.json",
588
+ review: "https://decantr.ai/schemas/review-pack.v1.json"
589
+ };
590
+ var PACK_MANIFEST_SCHEMA_URL = "https://decantr.ai/schemas/pack-manifest.v1.json";
591
+ var EXECUTION_PACK_BUNDLE_SCHEMA_URL = "https://decantr.ai/schemas/execution-pack-bundle.v1.json";
592
+ var SELECTED_EXECUTION_PACK_SCHEMA_URL = "https://decantr.ai/schemas/selected-execution-pack.v1.json";
593
+ var DEFAULT_TARGET = {
594
+ platform: "web",
595
+ framework: null,
596
+ runtime: null,
597
+ adapter: "generic-web"
598
+ };
599
+ var DEFAULT_TOKEN_BUDGET = {
600
+ target: 1400,
601
+ max: 2200,
602
+ strategy: [
603
+ "Prefer route summaries over repeated prose.",
604
+ "Use compact vocabulary lists instead of large reference tables.",
605
+ "Include only task-relevant examples and checks."
606
+ ]
607
+ };
608
+ var DEFAULT_SUCCESS_CHECKS = [
609
+ {
610
+ id: "route-topology",
611
+ label: "Routes and page IDs match the compiled topology.",
612
+ severity: "error"
613
+ },
614
+ {
615
+ id: "shell-consistency",
616
+ label: "The declared shell contract is preserved unless the task explicitly mutates it.",
617
+ severity: "error"
618
+ },
619
+ {
620
+ id: "theme-consistency",
621
+ label: "Theme identity and mode remain consistent across scaffolded routes.",
622
+ severity: "warn"
623
+ }
624
+ ];
625
+ var DEFAULT_SECTION_SUCCESS_CHECKS = [
626
+ {
627
+ id: "section-route-coherence",
628
+ label: "Section pages and routes remain coherent with the compiled topology.",
629
+ severity: "error"
630
+ },
631
+ {
632
+ id: "section-shell-consistency",
633
+ label: "The section shell contract stays consistent across its routes.",
634
+ severity: "error"
635
+ },
636
+ {
637
+ id: "section-pattern-coverage",
638
+ label: "Primary section patterns are represented without adding off-contract filler sections.",
639
+ severity: "warn"
640
+ }
641
+ ];
642
+ var DEFAULT_PAGE_SUCCESS_CHECKS = [
643
+ {
644
+ id: "page-route-contract",
645
+ label: "The page keeps the compiled route, shell, and section contract intact.",
646
+ severity: "error"
647
+ },
648
+ {
649
+ id: "page-pattern-contract",
650
+ label: "The page preserves its primary compiled patterns instead of drifting into unrelated layouts.",
651
+ severity: "error"
652
+ },
653
+ {
654
+ id: "page-state-contract",
655
+ label: "Any declared wiring signals remain coherent with the rendered page structure.",
656
+ severity: "warn"
657
+ }
658
+ ];
659
+ var DEFAULT_MUTATION_SUCCESS_CHECKS = {
660
+ "add-page": [
661
+ {
662
+ id: "mutation-essence-first",
663
+ label: "New pages are declared in the essence before any code generation begins.",
664
+ severity: "error"
665
+ },
666
+ {
667
+ id: "mutation-shell-contract",
668
+ label: "New routes inherit an existing shell and section contract unless the essence changes first.",
669
+ severity: "error"
670
+ },
671
+ {
672
+ id: "mutation-refresh",
673
+ label: "Refresh compiled packs after the mutation so downstream tasks read current topology.",
674
+ severity: "warn"
675
+ }
676
+ ],
677
+ modify: [
678
+ {
679
+ id: "mutation-existing-topology",
680
+ label: "Modified routes remain coherent with the compiled topology unless the essence changes first.",
681
+ severity: "error"
682
+ },
683
+ {
684
+ id: "mutation-theme-contract",
685
+ label: "Theme, shell, and page identity stay aligned with the current contract during edits.",
686
+ severity: "error"
687
+ },
688
+ {
689
+ id: "mutation-page-pack-first",
690
+ label: "Route-local edits should start from the compiled page pack rather than improvised structure.",
691
+ severity: "warn"
692
+ }
693
+ ]
694
+ };
695
+ var DEFAULT_REVIEW_SUCCESS_CHECKS = [
696
+ {
697
+ id: "review-contract-baseline",
698
+ label: "Review findings should use the compiled route, shell, and theme contract as the baseline.",
699
+ severity: "error"
700
+ },
701
+ {
702
+ id: "review-evidence",
703
+ label: "Each critique finding should cite concrete evidence from the generated workspace.",
704
+ severity: "error"
705
+ },
706
+ {
707
+ id: "review-remediation",
708
+ label: "Suggested fixes should point back to code changes or essence updates when contract drift exists.",
709
+ severity: "warn"
710
+ }
711
+ ];
712
+ var DEFAULT_REVIEW_ANTI_PATTERNS = [
713
+ {
714
+ id: "inline-styles",
715
+ summary: "Avoid inline style literals as the primary styling path.",
716
+ guidance: "Move visual styling into tokens.css and treatments.css instead of component-local style objects."
717
+ },
718
+ {
719
+ id: "hardcoded-colors",
720
+ summary: "Avoid hardcoded color literals.",
721
+ guidance: "Use CSS variables and theme decorators instead of hex, rgb, or hsl values."
722
+ },
723
+ {
724
+ id: "utility-framework-leakage",
725
+ summary: "Avoid utility-framework leakage as the primary design language.",
726
+ guidance: "Prefer compiled Decantr treatments and contract vocabulary over ad hoc utility class stacks."
727
+ }
728
+ ];
729
+ function collectPatternIds(page) {
730
+ const patternIds = [];
731
+ walkIR(page, (node) => {
732
+ if (node.type !== "pattern") return;
733
+ const patternNode = node;
734
+ patternIds.push(patternNode.pattern.patternId);
735
+ });
736
+ return [...new Set(patternIds)];
737
+ }
738
+ function summarizeRoutes(appNode) {
739
+ return appNode.children.map((page) => {
740
+ const pageNode = page;
741
+ const route = appNode.routes.find((entry) => entry.pageId === pageNode.pageId);
742
+ return {
743
+ pageId: pageNode.pageId,
744
+ path: route?.path || `/${pageNode.pageId}`,
745
+ patternIds: collectPatternIds(pageNode)
746
+ };
747
+ });
748
+ }
749
+ function summarizeSectionRoutes(appNode, input) {
750
+ return summarizeRoutes(appNode).filter((route) => input.pageIds.includes(route.pageId));
751
+ }
752
+ function summarizePageRoute(appNode, pageId) {
753
+ return summarizeRoutes(appNode).find((route) => route.pageId === pageId) ?? null;
754
+ }
755
+ function findPageNode(appNode, pageId) {
756
+ const page = appNode.children.find((node) => node.pageId === pageId);
757
+ return page ? page : null;
758
+ }
759
+ function collectPagePatterns(page) {
760
+ const patterns = [];
761
+ walkIR(page, (node) => {
762
+ if (node.type !== "pattern") return;
763
+ const patternNode = node;
764
+ patterns.push({
765
+ id: patternNode.pattern.patternId,
766
+ alias: patternNode.pattern.alias,
767
+ preset: patternNode.pattern.preset,
768
+ layout: patternNode.pattern.layout
769
+ });
770
+ });
771
+ return patterns;
772
+ }
773
+ function mergeTokenBudget(overrides) {
774
+ return {
775
+ target: overrides?.target ?? DEFAULT_TOKEN_BUDGET.target,
776
+ max: overrides?.max ?? DEFAULT_TOKEN_BUDGET.max,
777
+ strategy: overrides?.strategy ?? DEFAULT_TOKEN_BUDGET.strategy
778
+ };
779
+ }
780
+ function renderList(title, entries) {
781
+ if (entries.length === 0) return [];
782
+ return [title, ...entries.map((entry) => `- ${entry}`), ""];
783
+ }
784
+ function renderExecutionPackMarkdown(pack) {
785
+ const lines = [];
786
+ lines.push(`# ${pack.packType.charAt(0).toUpperCase()}${pack.packType.slice(1)} Pack`);
787
+ lines.push("");
788
+ lines.push(`**Objective:** ${pack.objective}`);
789
+ lines.push(`**Target:** ${pack.target.adapter}${pack.target.framework ? ` (${pack.target.framework})` : ""}`);
790
+ lines.push(`**Scope:** pages=${pack.scope.pageIds.join(", ") || "none"} | patterns=${pack.scope.patternIds.join(", ") || "none"}`);
791
+ lines.push("");
792
+ if (pack.packType === "scaffold") {
793
+ const scaffoldPack = pack;
794
+ lines.push("## Scaffold Contract");
795
+ lines.push(`- Shell: ${scaffoldPack.data.shell}`);
796
+ lines.push(`- Theme: ${scaffoldPack.data.theme.id} (${scaffoldPack.data.theme.mode})`);
797
+ lines.push(`- Routing: ${scaffoldPack.data.routing}`);
798
+ if (scaffoldPack.data.features.length > 0) {
799
+ lines.push(`- Features: ${scaffoldPack.data.features.join(", ")}`);
800
+ }
801
+ lines.push("");
802
+ lines.push("## Route Plan");
803
+ for (const route of scaffoldPack.data.routes) {
804
+ const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
805
+ lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
806
+ }
807
+ lines.push("");
808
+ }
809
+ if (pack.packType === "section") {
810
+ const sectionPack = pack;
811
+ lines.push("## Section Contract");
812
+ lines.push(`- Section: ${sectionPack.data.sectionId}`);
813
+ lines.push(`- Role: ${sectionPack.data.role}`);
814
+ lines.push(`- Shell: ${sectionPack.data.shell}`);
815
+ lines.push(`- Theme: ${sectionPack.data.theme.id} (${sectionPack.data.theme.mode})`);
816
+ if (sectionPack.data.features.length > 0) {
817
+ lines.push(`- Features: ${sectionPack.data.features.join(", ")}`);
818
+ }
819
+ if (sectionPack.data.description) {
820
+ lines.push(`- Description: ${sectionPack.data.description}`);
821
+ }
822
+ lines.push("");
823
+ lines.push("## Section Routes");
824
+ for (const route of sectionPack.data.routes) {
825
+ const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
826
+ lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
827
+ }
828
+ lines.push("");
829
+ }
830
+ if (pack.packType === "page") {
831
+ const pagePack = pack;
832
+ lines.push("## Page Contract");
833
+ lines.push(`- Page: ${pagePack.data.pageId}`);
834
+ lines.push(`- Path: ${pagePack.data.path}`);
835
+ lines.push(`- Shell: ${pagePack.data.shell}`);
836
+ if (pagePack.data.sectionId) {
837
+ const role = pagePack.data.sectionRole ? ` (${pagePack.data.sectionRole})` : "";
838
+ lines.push(`- Section: ${pagePack.data.sectionId}${role}`);
839
+ }
840
+ lines.push(`- Theme: ${pagePack.data.theme.id} (${pagePack.data.theme.mode})`);
841
+ if (pagePack.data.features.length > 0) {
842
+ lines.push(`- Features: ${pagePack.data.features.join(", ")}`);
843
+ }
844
+ if (pagePack.data.surface) {
845
+ lines.push(`- Surface: ${pagePack.data.surface}`);
846
+ }
847
+ lines.push("");
848
+ lines.push("## Page Patterns");
849
+ for (const pattern of pagePack.data.patterns) {
850
+ lines.push(`- ${pattern.alias} -> ${pattern.id} [${pattern.layout}${pattern.preset ? ` | ${pattern.preset}` : ""}]`);
851
+ }
852
+ lines.push("");
853
+ if (pagePack.data.wiringSignals.length > 0) {
854
+ lines.push("## Wiring Signals");
855
+ for (const signal of pagePack.data.wiringSignals) {
856
+ lines.push(`- ${signal}`);
857
+ }
858
+ lines.push("");
859
+ }
860
+ }
861
+ if (pack.packType === "mutation") {
862
+ const mutationPack = pack;
863
+ lines.push("## Mutation Contract");
864
+ lines.push(`- Operation: ${mutationPack.data.mutationType}`);
865
+ lines.push(`- Shell: ${mutationPack.data.shell}`);
866
+ lines.push(`- Theme: ${mutationPack.data.theme.id} (${mutationPack.data.theme.mode})`);
867
+ lines.push(`- Routing: ${mutationPack.data.routing}`);
868
+ if (mutationPack.data.features.length > 0) {
869
+ lines.push(`- Features: ${mutationPack.data.features.join(", ")}`);
870
+ }
871
+ lines.push("");
872
+ lines.push("## Route Topology");
873
+ for (const route of mutationPack.data.routes) {
874
+ const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
875
+ lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
876
+ }
877
+ lines.push("");
878
+ if (mutationPack.data.workflow.length > 0) {
879
+ lines.push("## Workflow");
880
+ for (const step of mutationPack.data.workflow) {
881
+ lines.push(`- ${step}`);
882
+ }
883
+ lines.push("");
884
+ }
885
+ }
886
+ if (pack.packType === "review") {
887
+ const reviewPack = pack;
888
+ lines.push("## Review Contract");
889
+ lines.push(`- Review Type: ${reviewPack.data.reviewType}`);
890
+ lines.push(`- Shell: ${reviewPack.data.shell}`);
891
+ lines.push(`- Theme: ${reviewPack.data.theme.id} (${reviewPack.data.theme.mode})`);
892
+ lines.push(`- Routing: ${reviewPack.data.routing}`);
893
+ if (reviewPack.data.features.length > 0) {
894
+ lines.push(`- Features: ${reviewPack.data.features.join(", ")}`);
895
+ }
896
+ lines.push("");
897
+ lines.push("## Review Topology");
898
+ for (const route of reviewPack.data.routes) {
899
+ const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
900
+ lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
901
+ }
902
+ lines.push("");
903
+ if (reviewPack.data.focusAreas.length > 0) {
904
+ lines.push("## Focus Areas");
905
+ for (const focusArea of reviewPack.data.focusAreas) {
906
+ lines.push(`- ${focusArea}`);
907
+ }
908
+ lines.push("");
909
+ }
910
+ if (reviewPack.data.workflow.length > 0) {
911
+ lines.push("## Review Workflow");
912
+ for (const step of reviewPack.data.workflow) {
913
+ lines.push(`- ${step}`);
914
+ }
915
+ lines.push("");
916
+ }
917
+ }
918
+ lines.push("## Required Setup");
919
+ if (pack.requiredSetup.length === 0) {
920
+ lines.push("- None declared.");
921
+ } else {
922
+ lines.push(...pack.requiredSetup.map((entry) => `- ${entry}`));
923
+ }
924
+ lines.push("");
925
+ lines.push("## Allowed Vocabulary");
926
+ if (pack.allowedVocabulary.length === 0) {
927
+ lines.push("- None declared.");
928
+ } else {
929
+ lines.push(...pack.allowedVocabulary.map((entry) => `- ${entry}`));
930
+ }
931
+ lines.push("");
932
+ lines.push(...renderList("## Success Checks", pack.successChecks.map((entry) => `${entry.label} [${entry.severity}]`)));
933
+ lines.push(...renderList("## Anti-Patterns", pack.antiPatterns.map((entry) => `${entry.summary}: ${entry.guidance}`)));
934
+ lines.push(...renderList("## Examples", pack.examples.map((entry) => `${entry.label} (${entry.language})`)));
935
+ lines.push("## Token Budget");
936
+ lines.push(`- Target: ${pack.tokenBudget.target}`);
937
+ lines.push(`- Max: ${pack.tokenBudget.max}`);
938
+ lines.push(...pack.tokenBudget.strategy.map((entry) => `- ${entry}`));
939
+ lines.push("");
940
+ return lines.join("\n").trimEnd() + "\n";
941
+ }
942
+ function resolvePackAdapter(target, platformType) {
943
+ if (target === "react" && platformType === "spa") return "react-vite";
944
+ if (target === "react") return "react-web";
945
+ if (target === "vue" && platformType === "spa") return "vue-vite";
946
+ if (target === "svelte" && platformType === "spa") return "sveltekit";
947
+ if (target) return target;
948
+ return "generic-web";
949
+ }
950
+ function listPackSections(essence) {
951
+ const declaredSections = essence.blueprint.sections;
952
+ if (declaredSections && declaredSections.length > 0) {
953
+ return declaredSections.map((section) => ({
954
+ id: section.id,
955
+ role: section.role,
956
+ shell: section.shell,
957
+ description: section.description,
958
+ features: section.features,
959
+ pageIds: section.pages.map((page) => page.id)
960
+ }));
961
+ }
962
+ const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
963
+ return [{
964
+ id: essence.meta.archetype || "default",
965
+ role: "primary",
966
+ shell: essence.blueprint.shell ?? "sidebar-main",
967
+ description: `${essence.meta.archetype || "Application"} section`,
968
+ features: essence.blueprint.features || [],
969
+ pageIds: pages.map((page) => page.id)
970
+ }];
971
+ }
972
+ function listPackPages(essence) {
973
+ const declaredSections = essence.blueprint.sections;
974
+ if (declaredSections && declaredSections.length > 0) {
975
+ return declaredSections.flatMap(
976
+ (section) => section.pages.map((page) => ({
977
+ pageId: page.id,
978
+ shell: page.shell_override ?? section.shell,
979
+ sectionId: section.id,
980
+ sectionRole: section.role,
981
+ features: section.features
982
+ }))
983
+ );
984
+ }
985
+ const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
986
+ const defaultShell = essence.blueprint.shell ?? "sidebar-main";
987
+ return pages.map((page) => ({
988
+ pageId: page.id,
989
+ shell: page.shell_override ?? defaultShell,
990
+ sectionId: essence.meta.archetype || "default",
991
+ sectionRole: "primary",
992
+ features: essence.blueprint.features || []
993
+ }));
994
+ }
995
+ function buildScaffoldPack(appNode, options = {}) {
996
+ const routes = summarizeRoutes(appNode);
997
+ const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
998
+ const pack = {
999
+ $schema: EXECUTION_PACK_SCHEMA_URLS.scaffold,
1000
+ packVersion: "1.0.0",
1001
+ packType: "scaffold",
1002
+ objective: options.objective ?? `Scaffold the ${appNode.theme.id} app shell and declared routes.`,
1003
+ target: {
1004
+ ...DEFAULT_TARGET,
1005
+ ...options.target
1006
+ },
1007
+ preset: options.preset ?? null,
1008
+ scope: {
1009
+ appId: appNode.id,
1010
+ pageIds: routes.map((route) => route.pageId),
1011
+ patternIds: scopePatternIds
1012
+ },
1013
+ requiredSetup: options.requiredSetup ?? [
1014
+ "Treat the declared routes as the topology source of truth.",
1015
+ "Preserve the resolved theme and shell contract unless the task explicitly mutates them."
1016
+ ],
1017
+ allowedVocabulary: [.../* @__PURE__ */ new Set([
1018
+ appNode.shell.config.type,
1019
+ appNode.theme.id,
1020
+ appNode.theme.mode,
1021
+ ...appNode.features,
1022
+ ...scopePatternIds
1023
+ ])],
1024
+ examples: options.examples ?? [],
1025
+ antiPatterns: options.antiPatterns ?? [],
1026
+ successChecks: options.successChecks ?? DEFAULT_SUCCESS_CHECKS,
1027
+ tokenBudget: mergeTokenBudget(options.tokenBudget),
1028
+ data: {
1029
+ shell: appNode.shell.config.type,
1030
+ theme: {
1031
+ id: appNode.theme.id,
1032
+ mode: appNode.theme.mode,
1033
+ shape: appNode.theme.shape
1034
+ },
1035
+ routing: appNode.routing,
1036
+ features: appNode.features,
1037
+ routes
1038
+ },
1039
+ renderedMarkdown: ""
1040
+ };
1041
+ pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
1042
+ return pack;
1043
+ }
1044
+ function buildSectionPack(appNode, input, options = {}) {
1045
+ const routes = summarizeSectionRoutes(appNode, input);
1046
+ const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
1047
+ const pack = {
1048
+ $schema: EXECUTION_PACK_SCHEMA_URLS.section,
1049
+ packVersion: "1.0.0",
1050
+ packType: "section",
1051
+ objective: options.objective ?? `Implement the ${input.id} section using the compiled ${input.shell} shell contract.`,
1052
+ target: {
1053
+ ...DEFAULT_TARGET,
1054
+ ...options.target
1055
+ },
1056
+ preset: options.preset ?? null,
1057
+ scope: {
1058
+ appId: appNode.id,
1059
+ pageIds: routes.map((route) => route.pageId),
1060
+ patternIds: scopePatternIds
1061
+ },
1062
+ requiredSetup: options.requiredSetup ?? [
1063
+ "Use the declared section routes as the source of truth for this slice of the app.",
1064
+ "Keep the section shell consistent unless the task explicitly changes the shell contract."
1065
+ ],
1066
+ allowedVocabulary: [.../* @__PURE__ */ new Set([
1067
+ input.id,
1068
+ input.role,
1069
+ input.shell,
1070
+ appNode.theme.id,
1071
+ appNode.theme.mode,
1072
+ ...input.features,
1073
+ ...scopePatternIds
1074
+ ])],
1075
+ examples: options.examples ?? [],
1076
+ antiPatterns: options.antiPatterns ?? [],
1077
+ successChecks: options.successChecks ?? DEFAULT_SECTION_SUCCESS_CHECKS,
1078
+ tokenBudget: mergeTokenBudget(options.tokenBudget),
1079
+ data: {
1080
+ sectionId: input.id,
1081
+ role: input.role,
1082
+ shell: input.shell,
1083
+ description: input.description,
1084
+ features: input.features,
1085
+ theme: {
1086
+ id: appNode.theme.id,
1087
+ mode: appNode.theme.mode,
1088
+ shape: appNode.theme.shape
1089
+ },
1090
+ routes
1091
+ },
1092
+ renderedMarkdown: ""
1093
+ };
1094
+ pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
1095
+ return pack;
1096
+ }
1097
+ function buildPagePack(appNode, input, options = {}) {
1098
+ const pageNode = findPageNode(appNode, input.pageId);
1099
+ const route = summarizePageRoute(appNode, input.pageId);
1100
+ if (!pageNode || !route) {
1101
+ throw new Error(`Unknown page for page pack: ${input.pageId}`);
1102
+ }
1103
+ const patterns = collectPagePatterns(pageNode);
1104
+ const pack = {
1105
+ $schema: EXECUTION_PACK_SCHEMA_URLS.page,
1106
+ packVersion: "1.0.0",
1107
+ packType: "page",
1108
+ objective: options.objective ?? `Implement the ${input.pageId} route using the compiled page contract.`,
1109
+ target: {
1110
+ ...DEFAULT_TARGET,
1111
+ ...options.target
1112
+ },
1113
+ preset: options.preset ?? null,
1114
+ scope: {
1115
+ appId: appNode.id,
1116
+ pageIds: [route.pageId],
1117
+ patternIds: [...new Set(patterns.map((pattern) => pattern.id))]
1118
+ },
1119
+ requiredSetup: options.requiredSetup ?? [
1120
+ "Keep the compiled route and shell contract stable for this page.",
1121
+ "Treat the listed page patterns as the primary structure for this route."
1122
+ ],
1123
+ allowedVocabulary: [...new Set([
1124
+ input.pageId,
1125
+ input.shell,
1126
+ input.sectionId ?? "",
1127
+ input.sectionRole ?? "",
1128
+ appNode.theme.id,
1129
+ appNode.theme.mode,
1130
+ ...input.features,
1131
+ ...patterns.flatMap((pattern) => [pattern.id, pattern.alias, pattern.layout])
1132
+ ].filter(Boolean))],
1133
+ examples: options.examples ?? [],
1134
+ antiPatterns: options.antiPatterns ?? [],
1135
+ successChecks: options.successChecks ?? DEFAULT_PAGE_SUCCESS_CHECKS,
1136
+ tokenBudget: mergeTokenBudget(options.tokenBudget),
1137
+ data: {
1138
+ pageId: route.pageId,
1139
+ path: route.path,
1140
+ shell: input.shell,
1141
+ sectionId: input.sectionId,
1142
+ sectionRole: input.sectionRole,
1143
+ features: input.features,
1144
+ surface: pageNode.surface,
1145
+ theme: {
1146
+ id: appNode.theme.id,
1147
+ mode: appNode.theme.mode,
1148
+ shape: appNode.theme.shape
1149
+ },
1150
+ wiringSignals: pageNode.wiring?.signals.map((signal) => signal.name) ?? [],
1151
+ patterns
1152
+ },
1153
+ renderedMarkdown: ""
1154
+ };
1155
+ pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
1156
+ return pack;
1157
+ }
1158
+ function buildMutationPack(appNode, options) {
1159
+ const routes = summarizeRoutes(appNode);
1160
+ const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
1161
+ const defaultWorkflow = options.mutationType === "add-page" ? [
1162
+ "Declare the new page in the essence before generating code.",
1163
+ "Refresh Decantr context so section and page packs include the new route.",
1164
+ "Read the relevant section pack and new page pack before implementation."
1165
+ ] : [
1166
+ "Read the page pack for the route you are modifying first.",
1167
+ "Stop and update the essence before changing route, shell, or pattern contracts.",
1168
+ "Validate and check drift after code changes complete."
1169
+ ];
1170
+ const pack = {
1171
+ $schema: EXECUTION_PACK_SCHEMA_URLS.mutation,
1172
+ packVersion: "1.0.0",
1173
+ packType: "mutation",
1174
+ objective: options.objective ?? `Execute the ${options.mutationType} workflow against the compiled app contract.`,
1175
+ target: {
1176
+ ...DEFAULT_TARGET,
1177
+ ...options.target
1178
+ },
1179
+ preset: options.preset ?? null,
1180
+ scope: {
1181
+ appId: appNode.id,
1182
+ pageIds: routes.map((route) => route.pageId),
1183
+ patternIds: scopePatternIds
1184
+ },
1185
+ requiredSetup: options.requiredSetup ?? [
1186
+ "Treat the compiled topology as the source of truth until the essence changes.",
1187
+ "Refresh Decantr context after structural mutations so downstream tasks read current packs."
1188
+ ],
1189
+ allowedVocabulary: [.../* @__PURE__ */ new Set([
1190
+ options.mutationType,
1191
+ appNode.shell.config.type,
1192
+ appNode.theme.id,
1193
+ appNode.theme.mode,
1194
+ ...appNode.features,
1195
+ ...scopePatternIds
1196
+ ])],
1197
+ examples: options.examples ?? [],
1198
+ antiPatterns: options.antiPatterns ?? [],
1199
+ successChecks: options.successChecks ?? DEFAULT_MUTATION_SUCCESS_CHECKS[options.mutationType],
1200
+ tokenBudget: mergeTokenBudget(options.tokenBudget),
1201
+ data: {
1202
+ mutationType: options.mutationType,
1203
+ shell: appNode.shell.config.type,
1204
+ theme: {
1205
+ id: appNode.theme.id,
1206
+ mode: appNode.theme.mode,
1207
+ shape: appNode.theme.shape
1208
+ },
1209
+ routing: appNode.routing,
1210
+ features: appNode.features,
1211
+ routes,
1212
+ workflow: options.workflow ?? defaultWorkflow
1213
+ },
1214
+ renderedMarkdown: ""
1215
+ };
1216
+ pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
1217
+ return pack;
1218
+ }
1219
+ function buildReviewPack(appNode, options = {}) {
1220
+ const routes = summarizeRoutes(appNode);
1221
+ const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
1222
+ const reviewType = options.reviewType ?? "app";
1223
+ const focusAreas = options.focusAreas ?? [
1224
+ "route-topology",
1225
+ "theme-consistency",
1226
+ "treatment-usage",
1227
+ "accessibility",
1228
+ "responsive-design"
1229
+ ];
1230
+ const workflow = options.workflow ?? [
1231
+ "Read the scaffold pack and page packs before evaluating generated code.",
1232
+ "Compare findings against the compiled route, shell, and theme contract first.",
1233
+ "Escalate contract drift into essence updates when the requested output intentionally changes topology or theme identity."
1234
+ ];
1235
+ const pack = {
1236
+ $schema: EXECUTION_PACK_SCHEMA_URLS.review,
1237
+ packVersion: "1.0.0",
1238
+ packType: "review",
1239
+ objective: options.objective ?? "Review generated output against the compiled Decantr contract.",
1240
+ target: {
1241
+ ...DEFAULT_TARGET,
1242
+ ...options.target
1243
+ },
1244
+ preset: options.preset ?? null,
1245
+ scope: {
1246
+ appId: appNode.id,
1247
+ pageIds: routes.map((route) => route.pageId),
1248
+ patternIds: scopePatternIds
1249
+ },
1250
+ requiredSetup: options.requiredSetup ?? [
1251
+ "Read the compiled scaffold and route packs before reviewing code.",
1252
+ "Use concrete evidence from the workspace instead of purely stylistic intuition."
1253
+ ],
1254
+ allowedVocabulary: [.../* @__PURE__ */ new Set([
1255
+ reviewType,
1256
+ appNode.shell.config.type,
1257
+ appNode.theme.id,
1258
+ appNode.theme.mode,
1259
+ ...appNode.features,
1260
+ ...scopePatternIds,
1261
+ ...focusAreas
1262
+ ])],
1263
+ examples: options.examples ?? [],
1264
+ antiPatterns: options.antiPatterns ?? DEFAULT_REVIEW_ANTI_PATTERNS,
1265
+ successChecks: options.successChecks ?? DEFAULT_REVIEW_SUCCESS_CHECKS,
1266
+ tokenBudget: mergeTokenBudget(options.tokenBudget),
1267
+ data: {
1268
+ reviewType,
1269
+ shell: appNode.shell.config.type,
1270
+ theme: {
1271
+ id: appNode.theme.id,
1272
+ mode: appNode.theme.mode,
1273
+ shape: appNode.theme.shape
1274
+ },
1275
+ routing: appNode.routing,
1276
+ features: appNode.features,
1277
+ routes,
1278
+ focusAreas,
1279
+ workflow
1280
+ },
1281
+ renderedMarkdown: ""
1282
+ };
1283
+ pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
1284
+ return pack;
1285
+ }
1286
+ async function compileExecutionPackBundle(essence, options = {}) {
1287
+ const effectiveEssence = isV33(essence) ? essence : migrateV2ToV32(essence);
1288
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
1289
+ const sharedTarget = {
1290
+ framework: effectiveEssence.meta.target || null,
1291
+ runtime: effectiveEssence.meta.platform.type || null,
1292
+ adapter: resolvePackAdapter(effectiveEssence.meta.target, effectiveEssence.meta.platform.type)
1293
+ };
1294
+ const pipeline = await runPipeline(essence, {
1295
+ contentRoot: options.contentRoot,
1296
+ overridePaths: options.overridePaths,
1297
+ resolver: options.resolver
1298
+ });
1299
+ const scaffold = buildScaffoldPack(pipeline.ir, {
1300
+ target: sharedTarget
1301
+ });
1302
+ const review = buildReviewPack(pipeline.ir, {
1303
+ target: sharedTarget
1304
+ });
1305
+ const sections = listPackSections(effectiveEssence).map((section) => buildSectionPack(pipeline.ir, section, {
1306
+ target: sharedTarget
1307
+ }));
1308
+ const pages = listPackPages(effectiveEssence).map((page) => buildPagePack(pipeline.ir, page, {
1309
+ target: sharedTarget
1310
+ }));
1311
+ const mutations = ["add-page", "modify"].map((mutationType) => buildMutationPack(pipeline.ir, {
1312
+ mutationType,
1313
+ target: sharedTarget
1314
+ }));
1315
+ const manifest = {
1316
+ $schema: PACK_MANIFEST_SCHEMA_URL,
1317
+ version: "1.0.0",
1318
+ generatedAt,
1319
+ scaffold: {
1320
+ id: "scaffold",
1321
+ markdown: "scaffold-pack.md",
1322
+ json: "scaffold-pack.json"
1323
+ },
1324
+ review: {
1325
+ id: "review",
1326
+ markdown: "review-pack.md",
1327
+ json: "review-pack.json"
1328
+ },
1329
+ sections: listPackSections(effectiveEssence).map((section) => ({
1330
+ id: section.id,
1331
+ markdown: `section-${section.id}-pack.md`,
1332
+ json: `section-${section.id}-pack.json`,
1333
+ pageIds: section.pageIds
1334
+ })),
1335
+ pages: listPackPages(effectiveEssence).map((page) => ({
1336
+ id: page.pageId,
1337
+ markdown: `page-${page.pageId}-pack.md`,
1338
+ json: `page-${page.pageId}-pack.json`,
1339
+ sectionId: page.sectionId,
1340
+ sectionRole: page.sectionRole
1341
+ })),
1342
+ mutations: ["add-page", "modify"].map((mutationType) => ({
1343
+ id: mutationType,
1344
+ markdown: `mutation-${mutationType}-pack.md`,
1345
+ json: `mutation-${mutationType}-pack.json`,
1346
+ mutationType
1347
+ }))
1348
+ };
1349
+ return {
1350
+ $schema: EXECUTION_PACK_BUNDLE_SCHEMA_URL,
1351
+ generatedAt,
1352
+ sourceEssenceVersion: essence.version,
1353
+ manifest,
1354
+ scaffold,
1355
+ review,
1356
+ sections,
1357
+ pages,
1358
+ mutations
1359
+ };
1360
+ }
1361
+ function selectExecutionPackFromBundle(bundle, selector) {
1362
+ const id = selector.id ?? null;
1363
+ let pack = null;
1364
+ switch (selector.packType) {
1365
+ case "scaffold":
1366
+ pack = bundle.scaffold;
1367
+ break;
1368
+ case "review":
1369
+ pack = bundle.review;
1370
+ break;
1371
+ case "section":
1372
+ pack = id ? bundle.sections.find((entry) => entry.data.sectionId === id) ?? null : null;
1373
+ break;
1374
+ case "page":
1375
+ pack = id ? bundle.pages.find((entry) => entry.data.pageId === id) ?? null : null;
1376
+ break;
1377
+ case "mutation":
1378
+ pack = id ? bundle.mutations.find((entry) => entry.data.mutationType === id) ?? null : null;
1379
+ break;
1380
+ default:
1381
+ pack = null;
1382
+ break;
1383
+ }
1384
+ if (!pack) {
1385
+ return null;
1386
+ }
1387
+ return {
1388
+ $schema: SELECTED_EXECUTION_PACK_SCHEMA_URL,
1389
+ generatedAt: bundle.generatedAt,
1390
+ sourceEssenceVersion: bundle.sourceEssenceVersion,
1391
+ manifest: bundle.manifest,
1392
+ selector: {
1393
+ packType: selector.packType,
1394
+ id
1395
+ },
1396
+ pack
1397
+ };
1398
+ }
1399
+ async function compileSelectedExecutionPack(essence, selector, options = {}) {
1400
+ const bundle = await compileExecutionPackBundle(essence, options);
1401
+ return selectExecutionPackFromBundle(bundle, selector);
1402
+ }
560
1403
  export {
1404
+ EXECUTION_PACK_BUNDLE_SCHEMA_URL,
1405
+ EXECUTION_PACK_SCHEMA_URLS,
1406
+ PACK_MANIFEST_SCHEMA_URL,
1407
+ SELECTED_EXECUTION_PACK_SCHEMA_URL,
1408
+ buildMutationPack,
561
1409
  buildPageIR,
1410
+ buildPagePack,
1411
+ buildReviewPack,
1412
+ buildScaffoldPack,
1413
+ buildSectionPack,
1414
+ compileExecutionPackBundle,
1415
+ compileSelectedExecutionPack,
562
1416
  countPatterns,
563
1417
  findNodes,
1418
+ listPackPages,
1419
+ listPackSections,
564
1420
  pascalCase,
1421
+ renderExecutionPackMarkdown,
565
1422
  resolveEssence,
1423
+ resolvePackAdapter,
566
1424
  resolveVisualEffects,
567
1425
  runPipeline,
1426
+ selectExecutionPackFromBundle,
568
1427
  validateIR,
569
1428
  walkIR
570
1429
  };