@orkestrel/scaffold 0.0.80 → 0.0.82

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 (36) hide show
  1. package/dist/bin/main.js +167 -13
  2. package/dist/bin/main.js.map +1 -1
  3. package/dist/host/AGENTS.md +5 -3
  4. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +2 -2
  5. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +41 -38
  6. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +3 -3
  7. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +3 -3
  8. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +5 -5
  9. package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +3 -3
  10. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +9 -9
  11. package/dist/host/claude/agents/orkestrel.md +52 -52
  12. package/dist/host/claude/rules/application.md +20 -6
  13. package/dist/host/claude/rules/architecture.md +2 -2
  14. package/dist/host/claude/rules/browser.md +9 -0
  15. package/dist/host/claude/rules/documentation.md +2 -1
  16. package/dist/host/claude/rules/styles.md +35 -12
  17. package/dist/host/claude/rules/tests.md +15 -10
  18. package/dist/host/claude/rules/workspace.md +128 -98
  19. package/dist/host/claude/skills/orkestrel-journey/SKILL.md +1 -1
  20. package/dist/host/configs/helpers.ts +157 -9
  21. package/dist/host/configs/policy.ts +64 -61
  22. package/dist/host/dotfiles/oxlintrc.json +132 -16
  23. package/dist/host/dotfiles/prettierignore +1 -1
  24. package/dist/host/guides/README.md +9 -5
  25. package/dist/host/guides/scaffold.md +475 -115
  26. package/dist/host/manifest.json +27 -27
  27. package/dist/host/tests/config.test.ts +996 -39
  28. package/dist/host/tests/policy.test.ts +11 -0
  29. package/dist/host/tests/setupPolicy.ts +396 -22
  30. package/dist/src/core/index.cjs +1263 -161
  31. package/dist/src/core/index.cjs.map +1 -1
  32. package/dist/src/core/index.d.cts +297 -35
  33. package/dist/src/core/index.d.ts +297 -35
  34. package/dist/src/core/index.js +1243 -162
  35. package/dist/src/core/index.js.map +1 -1
  36. package/package.json +3 -3
@@ -4,7 +4,7 @@ let _orkestrel_template = require("@orkestrel/template");
4
4
  let _orkestrel_emitter = require("@orkestrel/emitter");
5
5
  var package_default = {
6
6
  name: "@orkestrel/scaffold",
7
- version: "0.0.80",
7
+ version: "0.0.82",
8
8
  description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
9
9
  keywords: [
10
10
  "audit",
@@ -103,9 +103,9 @@ var package_default = {
103
103
  },
104
104
  devDependencies: {
105
105
  "@microsoft/api-extractor": "^7.59.3",
106
- "@orkestrel/guide": "^0.0.20",
106
+ "@orkestrel/guide": "^0.0.21",
107
107
  "@orkestrel/html": "^0.0.11",
108
- "@orkestrel/probe": "^0.0.18",
108
+ "@orkestrel/probe": "^0.0.19",
109
109
  "@orkestrel/test": "^0.0.24",
110
110
  "@types/node": "^26.6.3",
111
111
  "@vitest/browser-playwright": "^4.1.11",
@@ -134,6 +134,44 @@ var ENVIRONMENTS = Object.freeze([
134
134
  "browser",
135
135
  "server"
136
136
  ]);
137
+ /** Lists the surfaces an extension may extend, frozen. */
138
+ var SURFACES = Object.freeze(["browser", "styles"]);
139
+ /** Lists the axes a browser extension may occupy, frozen. */
140
+ var AXES = Object.freeze(["src", "app"]);
141
+ /** Lists the supported browser frameworks, frozen. */
142
+ var FRAMEWORKS = Object.freeze(["vue"]);
143
+ /** Describes the tooling and package boundaries of each browser framework, frozen. */
144
+ var FRAMEWORK_MATRIX = Object.freeze({ vue: Object.freeze({
145
+ plugin: "@vitejs/plugin-vue",
146
+ checker: "vue-tsc",
147
+ suffixes: Object.freeze(["vue"]),
148
+ refused: Object.freeze(["vue", "@vue/"]),
149
+ dependencies: Object.freeze({
150
+ "@vitejs/plugin-vue": "^6.0.8",
151
+ vue: "^3.5.40",
152
+ "vue-tsc": "^3.3.7"
153
+ })
154
+ }) });
155
+ /** Names the base stylesheet marker. */
156
+ var STYLES_ENTRY_PATH = "src/styles/index.scss";
157
+ /** Names the themes build entry marker. */
158
+ var THEMES_ENTRY_PATH = "src/styles/themes/sheet.ts";
159
+ /** Names the themes stylesheet marker. */
160
+ var THEMES_BARREL_PATH = "src/styles/themes/index.scss";
161
+ /** Names the build entry beside each stylesheet marker. */
162
+ var SHEET_ENTRY_NAME = "sheet.ts";
163
+ /** Names the physical showcase output directory. */
164
+ var SHOWCASE_PAGES_PATH = "showcase";
165
+ /** Lists the development dependencies the styles surface adds, frozen. */
166
+ var STYLES_DEV_DEPENDENCIES = Object.freeze({ sass: "^1.105.1" });
167
+ /** Lists the names a styles extension cannot occupy, frozen. */
168
+ var RESERVED_SHEET_NAMES = Object.freeze([
169
+ ...ENVIRONMENTS,
170
+ "bin",
171
+ "styles",
172
+ "themes",
173
+ ...FRAMEWORKS
174
+ ]);
137
175
  /**
138
176
  * Lists the `Group` values in plan order, frozen.
139
177
  *
@@ -626,13 +664,10 @@ var SOURCE_BROWSER_DEV_DEPENDENCIES = Object.freeze({
626
664
  });
627
665
  /** Names the development dependency every private `app` environment adds. */
628
666
  var APP_DEV_DEPENDENCIES = Object.freeze({ "@orkestrel/contract": package_default.dependencies["@orkestrel/contract"] });
629
- /** Lists the development dependencies a private Vue browser application adds. */
667
+ /** Lists the development dependencies a private browser application adds. */
630
668
  var APP_BROWSER_DEV_DEPENDENCIES = Object.freeze({
631
669
  ...SOURCE_BROWSER_DEV_DEPENDENCIES,
632
- "@orkestrel/html": package_default.devDependencies["@orkestrel/html"],
633
- "@vitejs/plugin-vue": "^6.0.8",
634
- vue: "^3.5.40",
635
- "vue-tsc": "^3.3.7"
670
+ "@orkestrel/html": package_default.devDependencies["@orkestrel/html"]
636
671
  });
637
672
  /**
638
673
  * Names the development dependency used only by the optional single-file showcase build.
@@ -696,9 +731,7 @@ var CONFIG_TEMPLATES = Object.freeze({
696
731
  "forceConsistentCasingInFileNames": true,
697
732
  "skipLibCheck": true,
698
733
  "noEmit": true,
699
- "paths": {
700
734
  {{paths}}
701
- }
702
735
  },
703
736
  "exclude": ["node_modules", "dist", "tmp"]
704
737
  }
@@ -815,6 +848,27 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
815
848
  `
816
849
  }),
817
850
  factories: Object.freeze({
851
+ sheet: `export function sheetProject(name: string, override?: UserConfig): UserConfig {
852
+ const project: UserConfig = {
853
+ resolve,
854
+ optimizeDeps,
855
+ publicDir: false,
856
+ test: {
857
+ name: { label: name, color: 'magenta' },
858
+ root: resolveWorkspacePath('.'),
859
+ isolate: false,
860
+ setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts', './tests/setupStyles.ts'],
861
+ browser: {
862
+ enabled: true,
863
+ provider: playwright(browserOptions),
864
+ instances: [{ browser: 'chromium', headless: true }],
865
+ },
866
+ fileParallelism: false,
867
+ },
868
+ }
869
+ return mergeOverride(project, override)
870
+ }
871
+ `,
818
872
  src: Object.freeze({
819
873
  core: `export function srcCore(override?: UserConfig): UserConfig {
820
874
  const project: UserConfig = {
@@ -837,9 +891,51 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
837
891
  return mergeOverride(project, override)
838
892
  }
839
893
  `,
840
- browser: `export function srcBrowser(override?: UserConfig): UserConfig {
894
+ vue: `function resolveVueFilename(): string {
895
+ return 'index.js'
896
+ }
897
+
898
+ export function srcVue(override?: UserConfig): UserConfig {
899
+ const project: UserConfig = {
900
+ resolve,
901
+ optimizeDeps: { include: [...optimizeDeps.include, 'vue'] },
902
+ publicDir: false,
903
+ plugins: [outputBoundary('dist/src/vue'), environmentBoundary('src/vue'), vue()],
904
+ build: {
905
+ emptyOutDir: true,
906
+ sourcemap: true,
907
+ minify: false,
908
+ lib: {
909
+ entry: resolveWorkspacePath('src/vue/index.ts'),
910
+ formats: ['es'],
911
+ fileName: resolveVueFilename,
912
+ },
913
+ outDir: 'dist/src/vue',
914
+ rolldownOptions: { onLog: enforceBuildLog },
915
+ },
916
+ test: {
917
+ name: { label: 'src:vue', color: 'green' },
918
+ include: ['tests/src/vue/**/*.test.ts'],
919
+ setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
920
+ browser: {
921
+ enabled: true,
922
+ provider: playwright(browserOptions),
923
+ instances: [{ browser: 'chromium', headless: true }],
924
+ },
925
+ fileParallelism: false,
926
+ },
927
+ }
928
+ return mergeOverride(project, override)
929
+ }
930
+ `,
931
+ browser: `function resolveBrowserFilename(): string {
932
+ return 'index.js'
933
+ }
934
+
935
+ export function srcBrowser(override?: UserConfig): UserConfig {
841
936
  const project: UserConfig = {
842
937
  resolve,
938
+ optimizeDeps,
843
939
  publicDir: false,
844
940
  plugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],
845
941
  build: {
@@ -849,7 +945,7 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
849
945
  lib: {
850
946
  entry: resolveWorkspacePath('src/browser/index.ts'),
851
947
  formats: ['es'],
852
- fileName: () => 'index.js',
948
+ fileName: resolveBrowserFilename,
853
949
  },
854
950
  outDir: 'dist/src/browser',
855
951
  rolldownOptions: {
@@ -874,7 +970,11 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
874
970
  return mergeOverride(project, override)
875
971
  }
876
972
  `,
877
- server: `export function srcServer(override?: UserConfig): UserConfig {
973
+ server: `function resolveServerFilename(format: string): string {
974
+ return format === 'es' ? 'index.js' : 'index.cjs'
975
+ }
976
+
977
+ export function srcServer(override?: UserConfig): UserConfig {
878
978
  const project: UserConfig = {
879
979
  resolve,
880
980
  publicDir: false,
@@ -886,7 +986,7 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
886
986
  lib: {
887
987
  entry: resolveWorkspacePath('src/server/index.ts'),
888
988
  formats: ['es', 'cjs'],
889
- fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
989
+ fileName: resolveServerFilename,
890
990
  },
891
991
  outDir: 'dist/src/server',
892
992
  target: 'node22',
@@ -908,7 +1008,15 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
908
1008
  return mergeOverride(project, override)
909
1009
  }
910
1010
  `,
911
- bin: `export function srcBin(override?: UserConfig): UserConfig {
1011
+ bin: `function resolveBinFilename(): string {
1012
+ return 'main.js'
1013
+ }
1014
+
1015
+ function resolveBinExternal(id: string): boolean {
1016
+ return id.startsWith('@src/') || resolveExternal(id, { peers, refused: [], siblings: [] })
1017
+ }
1018
+
1019
+ export function srcBin(override?: UserConfig): UserConfig {
912
1020
  const project: UserConfig = {
913
1021
  resolve,
914
1022
  publicDir: false,
@@ -920,17 +1028,13 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
920
1028
  lib: {
921
1029
  entry: resolveWorkspacePath('${BIN_ENTRY_PATH}'),
922
1030
  formats: ['es'],
923
- fileName: () => 'main.js',
1031
+ fileName: resolveBinFilename,
924
1032
  },
925
1033
  outDir: 'dist/bin',
926
1034
  target: 'node22',
927
1035
  rolldownOptions: {
928
1036
  onLog: enforceBuildLog,
929
- external: (id: string) =>
930
- id.startsWith('node:') ||
931
- id.startsWith('@orkestrel/') ||
932
- id.startsWith('@src/') ||
933
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),
1037
+ external: resolveBinExternal,
934
1038
  },
935
1039
  },
936
1040
  test: {
@@ -970,7 +1074,8 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
970
1074
  const output = 'dist/app/browser'
971
1075
  const project: UserConfig = {
972
1076
  resolve,
973
- plugins: [outputBoundary(output), environmentBoundary('app/browser'), vue()],
1077
+ optimizeDeps,
1078
+ plugins: [outputBoundary(output), environmentBoundary('app/browser')],
974
1079
  root: resolveWorkspacePath('app/browser'),
975
1080
  publicDir: false,
976
1081
  build: {
@@ -999,20 +1104,47 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
999
1104
  return mergeOverride(project, override)
1000
1105
  }
1001
1106
  {{showcaseFactory}}{{journeyFactory}}`,
1107
+ vue: `export function appVue(override?: UserConfig): UserConfig {
1108
+ const output = 'dist/app/vue'
1109
+ const browser = appBrowser()
1110
+ const project: UserConfig = {
1111
+ ...browser,
1112
+ optimizeDeps: { include: [...optimizeDeps.include, 'vue'] },
1113
+ plugins: [outputBoundary(output), environmentBoundary('app/vue'), vue()],
1114
+ root: resolveWorkspacePath('app/vue'),
1115
+ build: {
1116
+ ...browser.build,
1117
+ outDir: resolveWorkspacePath(output),
1118
+ rolldownOptions: {
1119
+ ...browser.build?.rolldownOptions,
1120
+ input: resolveWorkspacePath('app/vue/index.html'),
1121
+ },
1122
+ },
1123
+ test: {
1124
+ ...browser.test,
1125
+ name: { label: 'app:vue', color: 'magenta' },
1126
+ include: ['tests/app/vue/**/*.test.ts'],
1127
+ {{journeyExclude}} },
1128
+ }
1129
+ return mergeOverride(project, override)
1130
+ }
1131
+ `,
1002
1132
  journey: `
1003
1133
  // Replace the journey fields directly: merging would concatenate the ordinary include
1004
1134
  // and retain the exclusion of the journey suite.
1005
1135
  export function appJourney(
1006
1136
  variant: JourneyVariant,
1007
1137
  variants: readonly JourneyVariant[],
1138
+ mode?: string,
1008
1139
  ): UserConfig {
1009
- const browser = appBrowser()
1140
+ const application = resolveApplication(mode, applications)
1141
+ const browser = applications[application]()
1010
1142
  return {
1011
1143
  ...browser,
1012
1144
  test: {
1013
1145
  ...browser.test,
1014
1146
  name: { label: \`journey:\${variant.name}\`, color: 'green' },
1015
- include: ['tests/app/browser/integration.test.ts'],
1147
+ include: [\`tests/app/\${application}/integration.test.ts\`],
1016
1148
  exclude: [],
1017
1149
  provide: { variant: variant.name, variants, capture },
1018
1150
  browser: {
@@ -1025,8 +1157,39 @@ export function appJourney(
1025
1157
  }
1026
1158
  `,
1027
1159
  showcase: `
1028
- export function appShowcase(override?: UserConfig): UserConfig {
1029
- const output = 'dist/showcase'
1160
+ const showcaseStamp: PluginOption = {
1161
+ name: 'orkestrel-showcase-html',
1162
+ enforce: 'post',
1163
+ generateBundle(_, bundle) {
1164
+ for (const file of Object.values(bundle)) {
1165
+ if (
1166
+ file.type === 'asset' &&
1167
+ typeof file.source === 'string' &&
1168
+ file.fileName.endsWith('.html')
1169
+ ) {
1170
+ file.source = stampPage(file.source)
1171
+ }
1172
+ }
1173
+ },
1174
+ }
1175
+
1176
+ function createShowcaseNaming(application: string): PluginOption {
1177
+ return {
1178
+ name: 'orkestrel-showcase-name',
1179
+ enforce: 'post',
1180
+ writeBundle(options) {
1181
+ if (options.dir === undefined) throw new Error('The showcase output is absent.')
1182
+ renameSync(
1183
+ resolvePath(options.dir, 'index.html'),
1184
+ resolvePath(options.dir, application + '.html'),
1185
+ )
1186
+ },
1187
+ }
1188
+ }
1189
+
1190
+ export function appShowcase(mode: string, override?: UserConfig): UserConfig {
1191
+ const application = resolveApplication(mode, applications)
1192
+ const output = 'showcase'
1030
1193
  const showcase: UserConfig = {
1031
1194
  plugins: [
1032
1195
  outputBoundary(output),
@@ -1034,19 +1197,8 @@ export function appShowcase(override?: UserConfig): UserConfig {
1034
1197
  removeViteModuleLoader: true,
1035
1198
  useRecommendedBuildConfig: true,
1036
1199
  }),
1037
- {
1038
- name: 'orkestrel-showcase-html',
1039
- transformIndexHtml: {
1040
- order: 'post',
1041
- handler(html) {
1042
- const stamp = new Date().toISOString()
1043
- return html.replace(
1044
- '</head>',
1045
- '\t\t<meta name="build-id" content="' + stamp + '" />\\n\t</head>',
1046
- )
1047
- },
1048
- },
1049
- },
1200
+ showcaseStamp,
1201
+ createShowcaseNaming(application),
1050
1202
  ],
1051
1203
  build: {
1052
1204
  // The \`appBrowser\` factory sets \`assetsInlineLimit\` to 0, so every asset becomes
@@ -1055,6 +1207,7 @@ export function appShowcase(override?: UserConfig): UserConfig {
1055
1207
  // true, so this line takes effect only in a workspace that turns that option off.
1056
1208
  assetsInlineLimit: 4096,
1057
1209
  cssMinify: 'lightningcss',
1210
+ emptyOutDir: false,
1058
1211
  minify: 'oxc',
1059
1212
  modulePreload: false,
1060
1213
  outDir: resolveWorkspacePath(output),
@@ -1063,10 +1216,18 @@ export function appShowcase(override?: UserConfig): UserConfig {
1063
1216
  target: 'esnext',
1064
1217
  },
1065
1218
  }
1066
- return appBrowser(mergeOverride(showcase, override))
1219
+ return applications[application](mergeOverride(showcase, override))
1067
1220
  }
1068
1221
  `,
1069
- server: `export function appServer(override?: UserConfig): UserConfig {
1222
+ server: `function resolveAppFilename(): string {
1223
+ return 'main.cjs'
1224
+ }
1225
+
1226
+ function resolveAppExternal(id: string): boolean {
1227
+ return id.startsWith('node:')
1228
+ }
1229
+
1230
+ export function appServer(override?: UserConfig): UserConfig {
1070
1231
  const project: UserConfig = {
1071
1232
  resolve,
1072
1233
  publicDir: false,
@@ -1076,13 +1237,13 @@ export function appShowcase(override?: UserConfig): UserConfig {
1076
1237
  lib: {
1077
1238
  entry: resolveWorkspacePath('app/server/main.ts'),
1078
1239
  formats: ['cjs'],
1079
- fileName: () => 'main.cjs',
1240
+ fileName: resolveAppFilename,
1080
1241
  },
1081
1242
  outDir: resolveWorkspacePath('dist/app/server'),
1082
1243
  target: 'node22',
1083
1244
  rolldownOptions: {
1084
1245
  onLog: enforceBuildLog,
1085
- external: (id: string) => id.startsWith('node:'),
1246
+ external: resolveAppExternal,
1086
1247
  },
1087
1248
  },
1088
1249
  test: {
@@ -1135,8 +1296,10 @@ export function appShowcase(override?: UserConfig): UserConfig {
1135
1296
  test: {
1136
1297
  name: { label: 'setup', color: 'white' },
1137
1298
  include: ['tests/setup*.test.ts'],
1138
- exclude: ['tests/setupBrowser.test.ts'],
1299
+ exclude: ['tests/setupBrowser.test.ts', 'tests/setupStyles.test.ts'],
1139
1300
  setupFiles: ['./tests/setup.ts'],
1301
+ pool: 'threads',
1302
+ isolate: false,
1140
1303
  environment: 'node',
1141
1304
  browser: { enabled: false },
1142
1305
  },
@@ -1147,9 +1310,10 @@ export function appShowcase(override?: UserConfig): UserConfig {
1147
1310
  browser: `export function setupBrowser(override?: UserConfig): UserConfig {
1148
1311
  const project: UserConfig = {
1149
1312
  resolve,
1313
+ {{optimization}},
1150
1314
  {{plugins}} test: {
1151
1315
  name: { label: 'setup:browser', color: 'blue' },
1152
- include: ['tests/setupBrowser.test.ts'],
1316
+ include: ['tests/setupBrowser.test.ts', 'tests/setupStyles.test.ts'],
1153
1317
  setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
1154
1318
  {{global}} browser: {
1155
1319
  enabled: true,
@@ -1203,7 +1367,9 @@ export function conformance(override?: UserConfig): UserConfig {
1203
1367
  test: {
1204
1368
  name: { label: 'conformance', color: 'magenta' },
1205
1369
  include: ['${CONFORMANCE_TEST_PATH}'],
1206
- setupFiles: ['./tests/setup.ts'],
1370
+ setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
1371
+ pool: 'threads',
1372
+ isolate: false,
1207
1373
  environment: 'node',
1208
1374
  browser: { enabled: false },
1209
1375
  },
@@ -1269,7 +1435,17 @@ export function probe(override?: UserConfig): UserConfig {
1269
1435
  return mergeOverride(project, override)
1270
1436
  }
1271
1437
  `,
1272
- integration: `export function integration(override?: UserConfig): UserConfig {
1438
+ integration: Object.freeze({
1439
+ sheet: `export function integration(override?: UserConfig): UserConfig {
1440
+ const project = sheetProject('integration', {
1441
+ test: {
1442
+ include: ['tests/integration.test.ts'],
1443
+ {{global}} },
1444
+ })
1445
+ return mergeOverride(project, override)
1446
+ }
1447
+ `,
1448
+ module: `export function integration(override?: UserConfig): UserConfig {
1273
1449
  const project: UserConfig = {
1274
1450
  resolve,
1275
1451
  test: {
@@ -1282,8 +1458,51 @@ export function probe(override?: UserConfig): UserConfig {
1282
1458
  return mergeOverride(project, override)
1283
1459
  }
1284
1460
  `
1461
+ })
1285
1462
  }),
1286
1463
  tsconfigs: Object.freeze({
1464
+ vue: Object.freeze({
1465
+ src: `{
1466
+ "extends": "../../tsconfig.json",
1467
+ "compilerOptions": {
1468
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
1469
+ "types": ["vite/client"],
1470
+ "noEmit": false,
1471
+ "declaration": true,
1472
+ "emitDeclarationOnly": true,
1473
+ "rootDir": "../../src",
1474
+ "outDir": "../../dist/src"
1475
+ },
1476
+ "include": [
1477
+ "../../src/vue/**/*.cts",
1478
+ "../../src/vue/**/*.mts",
1479
+ "../../src/vue/**/*.ts",
1480
+ "../../src/vue/**/*.tsx"
1481
+ ]
1482
+ }
1483
+ `,
1484
+ app: `{
1485
+ "extends": "../../tsconfig.json",
1486
+ "compilerOptions": {
1487
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
1488
+ "types": ["vite/client", "vue"]
1489
+ },
1490
+ "include": [
1491
+ {{include}}
1492
+ ]
1493
+ }
1494
+ `
1495
+ }),
1496
+ sheet: `{
1497
+ "extends": "../../tsconfig.json",
1498
+ "compilerOptions": {
1499
+ "lib": ["ESNext"],
1500
+ "types": ["vite/client"],
1501
+ "noEmit": true
1502
+ },
1503
+ "include": ["../../src/{{name}}/**/*.ts"]
1504
+ }
1505
+ `,
1287
1506
  src: Object.freeze({
1288
1507
  core: `{
1289
1508
  "extends": "../../tsconfig.json",
@@ -1367,7 +1586,7 @@ export function probe(override?: UserConfig): UserConfig {
1367
1586
  "extends": "../../tsconfig.json",
1368
1587
  "compilerOptions": {
1369
1588
  "lib": ["ESNext", "DOM", "DOM.Iterable"],
1370
- "types": ["vite/client", "vue"]
1589
+ "types": ["vite/client"]
1371
1590
  },
1372
1591
  "include": [
1373
1592
  {{include}}
@@ -1417,9 +1636,38 @@ export function probe(override?: UserConfig): UserConfig {
1417
1636
  `
1418
1637
  }),
1419
1638
  vites: Object.freeze({
1639
+ sheet: `import { defineConfig } from 'vitest/config'
1640
+ import { outputBoundary } from '../helpers.js'
1641
+ import { resolveWorkspacePath, sheetProject } from '../../vite.config.ts'
1642
+
1643
+ export default defineConfig((invocation) => {
1644
+ const project = sheetProject('src:{{label}}', {
1645
+ plugins: [outputBoundary('dist/src/{{name}}')],
1646
+ build: {
1647
+ outDir: 'dist/src/{{name}}',
1648
+ emptyOutDir: true,
1649
+ // Preserve the authored layer-order statement in the published sheet.
1650
+ cssMinify: false,
1651
+ lib: {
1652
+ entry: resolveWorkspacePath('src/{{name}}/sheet.ts'),
1653
+ formats: ['es'],
1654
+ fileName: 'index',
1655
+ cssFileName: 'index',
1656
+ },
1657
+ },
1658
+ test: { include: ['tests/src/{{name}}/**/*.test.ts'] },
1659
+ })
1660
+ return { ...project, mode: invocation.mode }
1661
+ })
1662
+ `,
1420
1663
  src: Object.freeze({
1421
1664
  core: `import { defineConfig } from 'vite'
1422
- import { declarationRollup, environmentBoundary, outputBoundary } from '../helpers.js'
1665
+ import {
1666
+ declarationRollup,
1667
+ environmentBoundary,
1668
+ outputBoundary,
1669
+ resolveExternal,
1670
+ } from '../helpers.js'
1423
1671
  import { peers, srcCore, resolveWorkspacePath } from '../../vite.config.ts'
1424
1672
 
1425
1673
  export default defineConfig(
@@ -1440,11 +1688,43 @@ export default defineConfig(
1440
1688
  fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
1441
1689
  },
1442
1690
  outDir: 'dist/src/core',
1691
+ rolldownOptions: {
1692
+ external: (id: string) => resolveExternal(id, { peers, refused: [], siblings: [] }),
1693
+ },
1694
+ },
1695
+ }),
1696
+ )
1697
+ `,
1698
+ vue: `import { defineConfig } from 'vite'
1699
+ import {
1700
+ declarationRollup,
1701
+ resolveExternal,
1702
+ rewriteBrowserSpecifier,
1703
+ rewriteCoreSpecifier,
1704
+ } from '../helpers.js'
1705
+ import { peers, srcVue, resolveWorkspacePath } from '../../vite.config.ts'
1706
+
1707
+ const siblings = {
1708
+ {{siblings}}
1709
+ }
1710
+
1711
+ export default defineConfig(
1712
+ srcVue({
1713
+ plugins: [
1714
+ declarationRollup({
1715
+ project: resolveWorkspacePath('configs/src/tsconfig.vue.json'),
1716
+ rewrite: (content) => rewriteBrowserSpecifier(rewriteCoreSpecifier(content)),
1717
+ }),
1718
+ ],
1719
+ build: {
1443
1720
  rolldownOptions: {
1444
1721
  external: (id: string) =>
1445
- id.startsWith('node:') ||
1446
- id.startsWith('@orkestrel/') ||
1447
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),
1722
+ resolveExternal(id, {
1723
+ peers,
1724
+ refused: {{refused}},
1725
+ siblings: Object.keys(siblings),
1726
+ }),
1727
+ output: { paths: siblings },
1448
1728
  },
1449
1729
  },
1450
1730
  }),
@@ -1486,7 +1766,8 @@ export default defineConfig(
1486
1766
  `
1487
1767
  }),
1488
1768
  bin: `import { defineConfig } from 'vite'
1489
- import { srcBin } from '../../vite.config.ts'
1769
+ import { resolveExternal } from '../helpers.js'
1770
+ import { peers, srcBin } from '../../vite.config.ts'
1490
1771
 
1491
1772
  // The \`scaffold\` executable build — a single ESM lib file, no declarations (an
1492
1773
  // executable ships no types), with the \`#!/usr/bin/env node\` shebang re-emitted through
@@ -1497,6 +1778,8 @@ export default defineConfig(
1497
1778
  srcBin({
1498
1779
  build: {
1499
1780
  rolldownOptions: {
1781
+ external: (id: string) =>
1782
+ id.startsWith('@src/') || resolveExternal(id, { peers, refused: [], siblings: [] }),
1500
1783
  output: {
1501
1784
  banner: '#!/usr/bin/env node',
1502
1785
  {{paths}}
@@ -1507,6 +1790,11 @@ export default defineConfig(
1507
1790
  )
1508
1791
  `,
1509
1792
  app: Object.freeze({
1793
+ vue: `import { defineConfig } from 'vite'
1794
+ import { appVue } from '../../vite.config.ts'
1795
+
1796
+ export default defineConfig(appVue())
1797
+ `,
1510
1798
  browser: `import { defineConfig } from 'vite'
1511
1799
  import { appBrowser } from '../../vite.config.ts'
1512
1800
 
@@ -1520,7 +1808,7 @@ export default defineConfig(appServer())
1520
1808
  showcase: `import { defineConfig } from 'vite'
1521
1809
  import { appShowcase } from '../../vite.config.ts'
1522
1810
 
1523
- export default defineConfig(appShowcase())
1811
+ export default defineConfig(({ mode }) => appShowcase(mode))
1524
1812
  `,
1525
1813
  journey: `import type { JourneyVariant } from '@orkestrel/test'
1526
1814
  import { defineConfig } from 'vitest/config'
@@ -1532,11 +1820,11 @@ const VARIANTS: readonly JourneyVariant[] = Object.freeze([
1532
1820
  { name: 'compact', width: 390, height: 844 },
1533
1821
  ])
1534
1822
 
1535
- export default defineConfig({
1823
+ export default defineConfig(({ mode }) => ({
1536
1824
  test: {
1537
- projects: VARIANTS.map((variant) => () => appJourney(variant, VARIANTS)),
1825
+ projects: VARIANTS.map((variant) => () => appJourney(variant, VARIANTS, mode)),
1538
1826
  },
1539
- })
1827
+ }))
1540
1828
  `
1541
1829
  })
1542
1830
  }),
@@ -1879,7 +2167,38 @@ export function resolveBrowser(
1879
2167
  */
1880
2168
  var ARTIFACT_TEMPLATES = Object.freeze({
1881
2169
  source: Object.freeze({
2170
+ sheet: Object.freeze({
2171
+ "index.scss": "@use 'tokens';\n@use 'elements';\n@use 'components';\n@use 'utilities';\n",
2172
+ "_tokens.scss": "@layer theme, reset, base, elements, components, utilities;\n",
2173
+ "_mixins.scss": "",
2174
+ "elements/_index.scss": "",
2175
+ "components/_index.scss": "",
2176
+ "utilities/_index.scss": "",
2177
+ "sheet.ts": "import './index.scss'\n",
2178
+ "index.ts": "export * from './sheet.js'\n"
2179
+ }),
2180
+ themes: Object.freeze({
2181
+ "index.scss": "@use '../tokens';\n@use 'default';\n",
2182
+ "_default.scss": "",
2183
+ "sheet.ts": "import './index.scss'\n"
2184
+ }),
1882
2185
  empty: "",
2186
+ barrel: "// TODO: [Feature] Export the browser extension API.\n",
2187
+ main: `document.body
2188
+ .appendChild(document.createElement('main'))
2189
+ .appendChild(document.createElement('h1')).textContent ={{heading}}
2190
+ `,
2191
+ vue: Object.freeze({
2192
+ "main.ts": `import { createApp } from 'vue'
2193
+ import App from './App.vue'
2194
+
2195
+ createApp(App).mount(document.body.appendChild(document.createElement('main')))
2196
+ `,
2197
+ "App.vue": `<template>
2198
+ <h1>{{heading}}</h1>
2199
+ </template>
2200
+ `
2201
+ }),
1883
2202
  browser: `<!doctype html>
1884
2203
  <html lang="en">
1885
2204
  <head>
@@ -1894,9 +2213,263 @@ var ARTIFACT_TEMPLATES = Object.freeze({
1894
2213
  `
1895
2214
  }),
1896
2215
  tests: Object.freeze({
2216
+ journey: `import {
2217
+ ACCESSIBLE_ROLES,
2218
+ createPortfolio,
2219
+ expandCaptures,
2220
+ isRendered,
2221
+ readRefusal,
2222
+ readStyle,
2223
+ } from '@orkestrel/test/browser'
2224
+ {{imports}}import { page } from 'vitest/browser'
2225
+ import { describe, expect, inject, it } from 'vitest'
2226
+
2227
+ const VARIANT = inject('variant')
2228
+ const VARIANTS = inject('variants')
2229
+ const CAPTURE = inject('capture')
2230
+
2231
+ describe('{{application}} arrival journey', () => {
2232
+ it('proves the declared arrival families', async () => {
2233
+ const families = new Set(['Journey', 'Refusal', 'Matrix', ...(CAPTURE ? ['Capture'] : [])])
2234
+ const proven = new Set<string>()
2235
+ const states = ['{{application}}-arrival']
2236
+ const placed = new Set<string>()
2237
+ const portfolio = createPortfolio({
2238
+ states,
2239
+ variants: VARIANTS,
2240
+ variant: VARIANT,
2241
+ directory: '../../../tmp/captures/states',
2242
+ enabled: CAPTURE,
2243
+ })
2244
+ {{mount}} try {
2245
+ {{arrival}} const heading = page
2246
+ .getByRole('heading', {
2247
+ name: {{heading}},
2248
+ exact: true,
2249
+ level: 1,
2250
+ })
2251
+ .element()
2252
+ expect(isRendered(heading)).toBe(true)
2253
+ proven.add('Journey')
2254
+ await portfolio.place('{{application}}-arrival')
2255
+ placed.add('{{application}}-arrival')
2256
+
2257
+ for (const role of ACCESSIBLE_ROLES) expect(page.getByRole(role).elements()).toEqual([])
2258
+ expect(readRefusal('Continue')).toBe(
2259
+ 'No interactive element has the accessible name "Continue"',
2260
+ )
2261
+ proven.add('Refusal')
2262
+
2263
+ const measured = new Set<string>()
2264
+ for (const variant of VARIANTS) {
2265
+ await page.viewport(variant.width, variant.height)
2266
+ expect(readStyle(heading, 'display')).toBe('block')
2267
+ expect(window.innerWidth).toBe(variant.width)
2268
+ measured.add(variant.name)
2269
+ }
2270
+ expect(measured).toEqual(new Set(VARIANTS.map(({ name }) => name)))
2271
+ proven.add('Matrix')
2272
+
2273
+ const files = expandCaptures(states, VARIANTS)
2274
+ expect(portfolio.files).toEqual(files)
2275
+ expect(new Set(files).size).toBe(states.length * VARIANTS.length)
2276
+ expect(placed).toEqual(new Set(states))
2277
+ const expected = CAPTURE
2278
+ ? expandCaptures(
2279
+ states,
2280
+ VARIANTS.filter(({ name }) => name === VARIANT),
2281
+ )
2282
+ : []
2283
+ expect(portfolio.paths.map((path) => path.replaceAll('\\\\', '/').split('/').at(-1))).toEqual(
2284
+ expected,
2285
+ )
2286
+ if (CAPTURE) proven.add('Capture')
2287
+ expect(proven).toEqual(families)
2288
+ } finally {
2289
+ {{cleanup}} const variant = VARIANTS.find(({ name }) => name === VARIANT)
2290
+ if (variant !== undefined) await page.viewport(variant.width, variant.height)
2291
+ }
2292
+ })
2293
+ })
2294
+ `,
2295
+ browser: `import type { JourneyVariant } from '@orkestrel/test'
2296
+
2297
+ declare module 'vitest' {
2298
+ export interface ProvidedContext {
2299
+ readonly variant: string
2300
+ readonly variants: readonly JourneyVariant[]
2301
+ readonly capture: boolean
2302
+ }
2303
+ }
2304
+ `,
2305
+ sheet: `import sheet from '/dist/src/{{name}}/index.css?raw'
2306
+ import { describe, expect, it } from 'vitest'
2307
+ import { adoptSheet, readLayerNames } from '{{setup}}'
2308
+
2309
+ describe('{{name}} entry', () => {
2310
+ it('opens with the declared cascade order', () => {
2311
+ const adopted = adoptSheet(sheet)
2312
+ try {
2313
+ expect(adopted.sheet.cssRules[0]?.cssText).toBe(
2314
+ '@layer theme, reset, base, elements, components, utilities;',
2315
+ )
2316
+ expect(readLayerNames(adopted.sheet).slice(0, 6)).toEqual([
2317
+ 'theme',
2318
+ 'reset',
2319
+ 'base',
2320
+ 'elements',
2321
+ 'components',
2322
+ 'utilities',
2323
+ ])
2324
+ } finally {
2325
+ adopted.release()
2326
+ }
2327
+ })
2328
+ })
2329
+ `,
2330
+ styles: Object.freeze({
2331
+ module: `/** Carries a CSSOM rule and its enclosing layer. */
2332
+ export interface SheetEntry {
2333
+ readonly rule: CSSRule
2334
+ readonly layer: string | undefined
2335
+ }
2336
+
2337
+ /** Describes an adopted stylesheet and its idempotent release operation. */
2338
+ export interface SheetAdoption {
2339
+ readonly sheet: CSSStyleSheet
2340
+ release(): void
2341
+ }
2342
+
2343
+ /**
2344
+ * Adopts CSS text into the document with an idempotent release operation.
2345
+ * @param text - The compiled sheet text.
2346
+ * @returns The adopted sheet and its release operation.
2347
+ * @example
2348
+ * \`\`\`ts
2349
+ * const adopted = adoptSheet('.sample { color: red }')
2350
+ * adopted.release()
2351
+ * \`\`\`
2352
+ */
2353
+ export function adoptSheet(text: string): SheetAdoption {
2354
+ const sheet = new CSSStyleSheet()
2355
+ sheet.replaceSync(text)
2356
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]
2357
+ return {
2358
+ sheet,
2359
+ release: () => {
2360
+ document.adoptedStyleSheets = document.adoptedStyleSheets.filter(
2361
+ (candidate) => candidate !== sheet,
2362
+ )
2363
+ },
2364
+ }
2365
+ }
2366
+
2367
+ /**
2368
+ * Reads layer statements and blocks in declaration order, including repeated names.
2369
+ * @param sheet - The parsed sheet.
2370
+ * @returns Qualified layer names, with an empty name for an anonymous layer.
2371
+ * @example
2372
+ * \`\`\`ts
2373
+ * readLayerNames(new CSSStyleSheet()) // []
2374
+ * \`\`\`
2375
+ */
2376
+ export function readLayerNames(sheet: CSSStyleSheet): readonly string[] {
2377
+ const pending: SheetEntry[] = Array.from(sheet.cssRules, (rule) => ({
2378
+ rule,
2379
+ layer: undefined,
2380
+ })).reverse()
2381
+ const names: string[] = []
2382
+ while (pending.length > 0) {
2383
+ const entry = pending.pop()
2384
+ if (entry === undefined) break
2385
+ const { rule, layer } = entry
2386
+ const declared =
2387
+ rule instanceof CSSLayerStatementRule
2388
+ ? rule.nameList
2389
+ : rule instanceof CSSLayerBlockRule
2390
+ ? [rule.name]
2391
+ : []
2392
+ for (const name of declared) names.push(layer === undefined ? name : layer + '.' + name)
2393
+ if (!(rule instanceof CSSGroupingRule)) continue
2394
+ const parent =
2395
+ rule instanceof CSSLayerBlockRule
2396
+ ? layer === undefined
2397
+ ? rule.name
2398
+ : layer + '.' + rule.name
2399
+ : layer
2400
+ for (const child of Array.from(rule.cssRules).reverse()) {
2401
+ pending.push({ rule: child, layer: parent })
2402
+ }
2403
+ }
2404
+ return names
2405
+ }
2406
+ `,
2407
+ proof: `import type { SheetAdoption } from './setupStyles.js'
2408
+ import { describe, expect, it } from 'vitest'
2409
+ import { adoptSheet, readLayerNames } from './setupStyles.js'
2410
+
2411
+ describe('CSSOM instruments', () => {
2412
+ it('adopts and releases only its own sheet, including repeated release', () => {
2413
+ const previous = [...document.adoptedStyleSheets]
2414
+ const first: SheetAdoption = adoptSheet('.sample { color: red }')
2415
+ try {
2416
+ const second = adoptSheet('.other { display: block }')
2417
+ try {
2418
+ expect(document.adoptedStyleSheets).toEqual([...previous, first.sheet, second.sheet])
2419
+ expect(first.sheet.cssRules.length).toBe(1)
2420
+ first.release()
2421
+ first.release()
2422
+ expect(document.adoptedStyleSheets).toEqual([...previous, second.sheet])
2423
+ } finally {
2424
+ second.release()
2425
+ }
2426
+ } finally {
2427
+ first.release()
2428
+ }
2429
+ expect(document.adoptedStyleSheets).toEqual(previous)
2430
+ })
2431
+
2432
+ it('reads statements, blocks, nested conditions, and anonymous layers in order', () => {
2433
+ const adopted = adoptSheet(
2434
+ '@layer first, second; @layer first { @media all { @layer nested {} } } @layer {}',
2435
+ )
2436
+ try {
2437
+ expect(readLayerNames(adopted.sheet)).toEqual([
2438
+ 'first',
2439
+ 'second',
2440
+ 'first',
2441
+ 'first.nested',
2442
+ '',
2443
+ ])
2444
+ } finally {
2445
+ adopted.release()
2446
+ }
2447
+ })
2448
+
2449
+ it('returns no layer names for an empty sheet or an unlayered rule', () => {
2450
+ const sheet = new CSSStyleSheet()
2451
+ expect(readLayerNames(sheet)).toEqual([])
2452
+ sheet.replaceSync('.sample { color: red }')
2453
+ expect(readLayerNames(sheet)).toEqual([])
2454
+ })
2455
+ })
2456
+ `
2457
+ }),
1897
2458
  setup: "",
1898
- global: `export function setup(): void {}
2459
+ global: Object.freeze({
2460
+ module: `export function setup(): void {}
1899
2461
  `,
2462
+ proof: `import { describe, expect, it } from 'vitest'
2463
+ import { setup } from './setupGlobal.js'
2464
+
2465
+ describe('global setup', () => {
2466
+ it('runs without producing a teardown', () => {
2467
+ expect(setup()).toBeUndefined()
2468
+ expect(setup()).toBeUndefined()
2469
+ })
2470
+ })
2471
+ `
2472
+ }),
1900
2473
  entry: `import * as entry from {{specifier}}
1901
2474
  import { describe, expect, it } from 'vitest'
1902
2475
 
@@ -3256,6 +3829,80 @@ function isCollection(value) {
3256
3829
  */
3257
3830
  var isEnvironment = (0, _orkestrel_contract.literalOf)(ENVIRONMENTS);
3258
3831
  /**
3832
+ * Narrows a value to a surface an extension can extend.
3833
+ * @param value - The candidate surface.
3834
+ * @returns True if the value is a supported surface; false otherwise.
3835
+ * @example
3836
+ * ```ts
3837
+ * import { isSurface } from '@orkestrel/scaffold'
3838
+ *
3839
+ * isSurface('styles') // true
3840
+ * isSurface('themes') // false
3841
+ * ```
3842
+ */
3843
+ var isSurface = (0, _orkestrel_contract.literalOf)(SURFACES);
3844
+ /**
3845
+ * Narrows a value to a non-reserved sheet face name.
3846
+ * @param value - The candidate name.
3847
+ * @returns True if the name matches the workspace pattern and is not reserved; false otherwise.
3848
+ * @example
3849
+ * ```ts
3850
+ * import { isSheetName } from '@orkestrel/scaffold'
3851
+ *
3852
+ * isSheetName('print') // true
3853
+ * isSheetName('styles') // false
3854
+ * ```
3855
+ */
3856
+ function isSheetName(value) {
3857
+ return (0, _orkestrel_contract.isString)(value) && value.length <= 203 && NAME_PATTERN.test(value) && !RESERVED_SHEET_NAMES.includes(value);
3858
+ }
3859
+ /**
3860
+ * Narrows a value to a supported browser framework with distinct occupied axes.
3861
+ * @param value - The candidate extension.
3862
+ * @returns True if the value is an admitted extension; false otherwise.
3863
+ * @example
3864
+ * ```ts
3865
+ * import { isBrowserExtension } from '@orkestrel/scaffold'
3866
+ *
3867
+ * isBrowserExtension({ surface: 'browser', name: 'vue', axes: ['app'] }) // true
3868
+ * isBrowserExtension(undefined) // false
3869
+ * ```
3870
+ */
3871
+ var isBrowserExtension = (0, _orkestrel_contract.andOf)((0, _orkestrel_contract.recordOf)({
3872
+ surface: (0, _orkestrel_contract.literalOf)("browser"),
3873
+ name: (0, _orkestrel_contract.literalOf)(FRAMEWORKS),
3874
+ axes: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)((0, _orkestrel_contract.literalOf)(AXES)))
3875
+ }), (extension) => new Set(extension.axes).size === extension.axes.length);
3876
+ /**
3877
+ * Narrows a value to a named non-reserved stylesheet face.
3878
+ * @param value - The candidate extension.
3879
+ * @returns True if the value is an admitted extension; false otherwise.
3880
+ * @example
3881
+ * ```ts
3882
+ * import { isStylesExtension } from '@orkestrel/scaffold'
3883
+ *
3884
+ * isStylesExtension({ surface: 'styles', name: 'print' }) // true
3885
+ * isStylesExtension(undefined) // false
3886
+ * ```
3887
+ */
3888
+ var isStylesExtension = (0, _orkestrel_contract.recordOf)({
3889
+ surface: (0, _orkestrel_contract.literalOf)("styles"),
3890
+ name: isSheetName
3891
+ });
3892
+ /**
3893
+ * Narrows a value to an extension of a supported surface.
3894
+ * @param value - The candidate extension.
3895
+ * @returns True if the value is an admitted extension; false otherwise.
3896
+ * @example
3897
+ * ```ts
3898
+ * import { isExtension } from '@orkestrel/scaffold'
3899
+ *
3900
+ * isExtension({ surface: 'styles', name: 'print' }) // true
3901
+ * isExtension(undefined) // false
3902
+ * ```
3903
+ */
3904
+ var isExtension = (0, _orkestrel_contract.unionOf)(isBrowserExtension, isStylesExtension);
3905
+ /**
3259
3906
  * Narrows a value to one {@link Group} a plan selects over.
3260
3907
  *
3261
3908
  * @example
@@ -3392,6 +4039,9 @@ var isBlueprint = (0, _orkestrel_contract.recordOf)({
3392
4039
  keywords: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)),
3393
4040
  src: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isEnvironment)),
3394
4041
  app: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isEnvironment)),
4042
+ extensions: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isExtension)),
4043
+ styles: _orkestrel_contract.isBoolean,
4044
+ themes: _orkestrel_contract.isBoolean,
3395
4045
  dependencies: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isDependency)),
3396
4046
  peers: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isDependency)),
3397
4047
  extras: (0, _orkestrel_contract.andOf)(isCollection, (0, _orkestrel_contract.arrayOf)(isDependency)),
@@ -3686,6 +4336,34 @@ function cloneValue(value) {
3686
4336
  //#endregion
3687
4337
  //#region src/core/parsers.ts
3688
4338
  /**
4339
+ * Coerces an extension value or a `surface:name` selection into an extension.
4340
+ * @param value - An extension record or its selection text.
4341
+ * @returns The extension, or `undefined` for a malformed or unsupported selection.
4342
+ * @remarks Browser text has empty axes; the creating command supplies its selected browser axes.
4343
+ * @example
4344
+ * ```ts
4345
+ * import { parseExtension } from '@orkestrel/scaffold'
4346
+ *
4347
+ * parseExtension('browser:vue') // { surface: 'browser', name: 'vue', axes: [] }
4348
+ * parseExtension('styles:print') // { surface: 'styles', name: 'print' }
4349
+ * ```
4350
+ */
4351
+ function parseExtension(value) {
4352
+ if (isExtension(value)) return value;
4353
+ if (!(0, _orkestrel_contract.isString)(value)) return void 0;
4354
+ const [surface, name, excess] = value.split(":");
4355
+ if (excess !== void 0 || !isSurface(surface)) return void 0;
4356
+ const candidate = surface === "browser" ? {
4357
+ surface,
4358
+ name,
4359
+ axes: []
4360
+ } : {
4361
+ surface,
4362
+ name
4363
+ };
4364
+ return isExtension(candidate) ? candidate : void 0;
4365
+ }
4366
+ /**
3689
4367
  * Coerces an untrusted value to a {@link Blueprint}.
3690
4368
  *
3691
4369
  * @param value - The value to parse.
@@ -4771,6 +5449,67 @@ function renderSkillPointer(canonical, name) {
4771
5449
  //#endregion
4772
5450
  //#region src/core/compilers.ts
4773
5451
  /**
5452
+ * Projects a blueprint into browser extensions restricted to occupied browser axes.
5453
+ * @param blueprint - The workspace specification.
5454
+ * @returns Browser extensions with their occupied axes, omitting unplaced extensions.
5455
+ * @example
5456
+ * ```ts
5457
+ * import { blueprintToFaces, createBlueprint } from '@orkestrel/scaffold'
5458
+ *
5459
+ * blueprintToFaces(createBlueprint('desk', { app: ['core'] })).length // 0
5460
+ * ```
5461
+ */
5462
+ function blueprintToFaces(blueprint) {
5463
+ return blueprint.extensions.flatMap((extension) => {
5464
+ if (extension.surface !== "browser") return [];
5465
+ const axes = extension.axes.filter((axis) => blueprint[axis].includes("browser"));
5466
+ return axes.length === 0 ? [] : [{
5467
+ ...extension,
5468
+ axes
5469
+ }];
5470
+ });
5471
+ }
5472
+ /**
5473
+ * Projects a blueprint into its validated sheet-face names in stable order.
5474
+ * @param blueprint - The workspace specification.
5475
+ * @returns The base face followed by named styles extensions; themes remain a separate target.
5476
+ * @example
5477
+ * ```ts
5478
+ * import { blueprintToSheets, createBlueprint } from '@orkestrel/scaffold'
5479
+ *
5480
+ * blueprintToSheets(createBlueprint('paper', { styles: true })) // ['styles']
5481
+ * ```
5482
+ */
5483
+ function blueprintToSheets(blueprint) {
5484
+ return [...blueprint.styles ? ["styles"] : [], ...(0, _orkestrel_contract.sortValues)(blueprint.extensions.flatMap((extension) => extension.surface === "styles" && isSheetName(extension.name) ? [extension.name] : []))];
5485
+ }
5486
+ /**
5487
+ * Projects published environments and sheets into the manifest export map.
5488
+ * @param blueprint - The workspace specification.
5489
+ * @returns JavaScript conditions and CSS and SCSS subpaths, without an invented JavaScript root.
5490
+ * @example
5491
+ * ```ts
5492
+ * import { blueprintToExports, createBlueprint } from '@orkestrel/scaffold'
5493
+ *
5494
+ * blueprintToExports(createBlueprint('paper', { styles: true }))['./styles']
5495
+ * // './dist/src/styles/index.css'
5496
+ * ```
5497
+ */
5498
+ function blueprintToExports(blueprint) {
5499
+ const exports = { ...srcToExports(blueprint.src) };
5500
+ for (const extension of blueprintToFaces(blueprint)) {
5501
+ if (extension.surface !== "browser" || !extension.axes.includes("src")) continue;
5502
+ exports[`./${extension.name}`] = pathToCondition(`./dist/src/${extension.name}/index`, ["es"]);
5503
+ if (blueprint.src.includes("browser")) exports["./browser"] = pathToCondition("./dist/src/browser/index", ["es"]);
5504
+ }
5505
+ for (const name of [...blueprintToSheets(blueprint), ...blueprint.themes ? ["styles/themes"] : []]) {
5506
+ exports[`./${name}`] = `./dist/src/${name}/index.css`;
5507
+ exports[`./${name}/scss`] = `./src/${name}/index.scss`;
5508
+ }
5509
+ if (Object.keys(exports).length > 0) exports["./package.json"] = "./package.json";
5510
+ return exports;
5511
+ }
5512
+ /**
4774
5513
  * Builds one `exports` condition block for a built environment.
4775
5514
  *
4776
5515
  * @param path - The extensionless `dist` path both conditions point at.
@@ -4917,18 +5656,21 @@ function srcToExports(src) {
4917
5656
  * ```
4918
5657
  */
4919
5658
  function blueprintToDevDependencies(blueprint) {
5659
+ const sheets = blueprintToSheets(blueprint).length > 0 || blueprint.themes;
4920
5660
  const merged = {
4921
5661
  ...BASE_DEV_DEPENDENCIES,
5662
+ ...sheets ? STYLES_DEV_DEPENDENCIES : {},
4922
5663
  ...blueprint.src.length > 0 || blueprint.bin ? DECLARATION_DEV_DEPENDENCIES : {},
4923
- ...blueprint.src.includes("browser") || blueprint.setup.includes("browser") ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
5664
+ ...blueprint.src.includes("browser") || blueprint.setup.includes("browser") || sheets ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
4924
5665
  ...blueprint.app.length > 0 ? APP_DEV_DEPENDENCIES : {},
4925
5666
  ...blueprint.app.includes("browser") ? APP_BROWSER_DEV_DEPENDENCIES : {},
4926
5667
  ...blueprint.showcase && blueprint.app.includes("browser") ? SHOWCASE_DEV_DEPENDENCIES : {},
4927
5668
  ...blueprint.app.includes("server") ? APP_SERVER_DEV_DEPENDENCIES : {}
4928
5669
  };
5670
+ for (const framework of blueprintToMachinery(blueprint).frameworks) Object.assign(merged, FRAMEWORK_MATRIX[framework].dependencies);
4929
5671
  for (const extra of blueprint.extras) merged[extra.name] = extra.range;
4930
5672
  for (const peer of blueprint.peers) if (!Object.hasOwn(merged, peer.name)) merged[peer.name] = peer.range;
4931
- const own = blueprint.src.length > 0 ? `@orkestrel/${blueprint.name}` : blueprint.name;
5673
+ const own = blueprint.src.length > 0 || sheets ? `@orkestrel/${blueprint.name}` : blueprint.name;
4932
5674
  const runtime = new Set(blueprint.dependencies.map((dependency) => dependency.name));
4933
5675
  return Object.fromEntries(Object.entries(merged).filter(([name]) => name !== own && !runtime.has(name)).sort(([left], [right]) => (0, _orkestrel_contract.compareValues)(left, right)));
4934
5676
  }
@@ -4969,11 +5711,22 @@ function blueprintToDevDependencies(blueprint) {
4969
5711
  * ```
4970
5712
  */
4971
5713
  function blueprintToScripts(blueprint) {
4972
- const publishes = blueprint.src.length > 0;
5714
+ const source = blueprintToFaces(blueprint).flatMap((extension) => extension.surface === "browser" && extension.axes.includes("src") ? [extension.name] : []);
5715
+ const application = blueprintToFaces(blueprint).flatMap((extension) => extension.surface === "browser" && extension.axes.includes("app") ? [extension.name] : []);
5716
+ const sheets = blueprintToSheets(blueprint);
5717
+ const targets = [...sheets, ...blueprint.themes && !blueprint.styles ? ["themes"] : []];
5718
+ const checker = application.includes("vue") ? FRAMEWORK_MATRIX.vue.checker : "tsc";
5719
+ const publishes = blueprint.src.length > 0 || targets.length > 0;
4973
5720
  const integrates = blueprint.integration;
4974
5721
  const compiles = publishes || blueprint.bin;
4975
5722
  const runtime = blueprint.app.filter((environment) => environment !== "core");
4976
5723
  const vitest = "vitest run --config vite.config.ts --no-cache --reporter=dot";
5724
+ const checks = [
5725
+ ...blueprint.src,
5726
+ ...source,
5727
+ ...sheets,
5728
+ ...blueprint.bin ? ["bin"] : []
5729
+ ].map((name) => `npm run check:src:${name}`);
4977
5730
  const scripts = {
4978
5731
  clean: "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
4979
5732
  copy: "node -e \"const fs=require('node:fs'),p=require('node:path'),a=process.argv[1],b=process.argv[2];fs.mkdirSync(p.dirname(b),{recursive:true});fs.cpSync(a,b,{force:true});console.log('Copied: '+a+' to '+b)\"",
@@ -4982,30 +5735,32 @@ function blueprintToScripts(blueprint) {
4982
5735
  lint: "oxlint --config .oxlintrc.json --fix .",
4983
5736
  "lint:check": "oxlint --config .oxlintrc.json --deny-warnings .",
4984
5737
  check: [
4985
- "tsc --noEmit --project tsconfig.json",
4986
- ...compiles ? ["npm run check:src"] : [],
5738
+ `${checker} --noEmit --project tsconfig.json`,
5739
+ ...checks.length > 0 ? ["npm run check:src"] : [],
4987
5740
  ...blueprint.app.length > 0 ? ["npm run check:app"] : [],
4988
5741
  ...blueprint.skills ? ["npm run check:skills"] : []
4989
5742
  ].join(" && ")
4990
5743
  };
4991
- if (compiles) {
4992
- scripts["check:src"] = [...blueprint.src.map((environment) => `npm run check:src:${environment}`), ...blueprint.bin ? ["npm run check:src:bin"] : []].join(" && ");
5744
+ if (checks.length > 0) {
5745
+ scripts["check:src"] = checks.join(" && ");
4993
5746
  for (const environment of blueprint.src) scripts[`check:src:${environment}`] = `tsc --noEmit -p configs/src/tsconfig.${environment}.json`;
4994
5747
  if (blueprint.bin) scripts["check:src:bin"] = "tsc --noEmit -p configs/src/tsconfig.bin.json";
5748
+ for (const name of [...sheets, ...source]) scripts[`check:src:${name}`] = `tsc --noEmit -p configs/src/tsconfig.${name}.json`;
4995
5749
  }
4996
5750
  if (blueprint.app.length > 0) {
4997
- scripts["check:app"] = blueprint.app.map((environment) => `npm run check:app:${environment}`).join(" && ");
4998
- for (const environment of blueprint.app) scripts[`check:app:${environment}`] = environment === "browser" ? "vue-tsc --noEmit -p configs/app/tsconfig.browser.json" : `tsc --noEmit -p configs/app/tsconfig.${environment}.json`;
5751
+ scripts["check:app"] = [...blueprint.app, ...application].map((environment) => `npm run check:app:${environment}`).join(" && ");
5752
+ for (const environment of blueprint.app) scripts[`check:app:${environment}`] = `tsc --noEmit -p configs/app/tsconfig.${environment}.json`;
5753
+ for (const name of application) scripts[`check:app:${name}`] = `${FRAMEWORK_MATRIX[name].checker} --noEmit -p configs/app/tsconfig.${name}.json`;
4999
5754
  }
5000
5755
  if (blueprint.skills) scripts["check:skills"] = `tsc --noEmit -p ${SKILLS_CONFIG_PATH}`;
5001
5756
  scripts.test = [
5002
5757
  ...compiles ? ["npm run test:src"] : [],
5003
5758
  ...blueprint.app.length > 0 ? ["npm run test:app"] : [],
5004
- ...blueprint.journey && blueprint.app.includes("browser") ? ["npm run test:journey"] : [],
5759
+ ...blueprint.journey && blueprint.app.includes("browser") ? ["npm run test:journey", ...application.map((name) => `npm run test:journey:${name}`)] : [],
5005
5760
  "npm run test:policy",
5006
5761
  "npm run test:config",
5007
- ...blueprint.setup.includes("node") ? ["npm run test:setup"] : [],
5008
- ...blueprint.setup.includes("browser") ? ["npm run test:setup:browser"] : [],
5762
+ ...blueprint.setup.includes("node") || blueprint.global ? ["npm run test:setup"] : [],
5763
+ ...blueprint.setup.includes("browser") || targets.length > 0 ? ["npm run test:setup:browser"] : [],
5009
5764
  ...blueprint.skills ? ["npm run test:skills"] : [],
5010
5765
  ...blueprint.guides ? ["npm run test:guides"] : [],
5011
5766
  ...blueprint.conformance ? ["npm run test:conformance"] : [],
@@ -5016,20 +5771,32 @@ function blueprintToScripts(blueprint) {
5016
5771
  scripts["test:src"] = [
5017
5772
  vitest,
5018
5773
  ...blueprint.src.map((environment) => `--project ${SRC_MATRIX[environment].project}`),
5774
+ ...source.map((name) => `--project src:${name}`),
5019
5775
  ...blueprint.bin ? ["--project src:bin"] : []
5020
5776
  ].join(" ");
5021
5777
  for (const environment of blueprint.src) scripts[`test:src:${environment}`] = `${vitest} --project ${SRC_MATRIX[environment].project}`;
5022
5778
  if (blueprint.bin) scripts["test:src:bin"] = `${vitest} --project src:bin`;
5779
+ for (const name of source) scripts[`test:src:${name}`] = `${vitest} --project src:${name}`;
5780
+ scripts["test:src"] = [...blueprint.src.length > 0 || blueprint.bin ? [scripts["test:src"]] : [], ...targets.map((name) => `npm run test:src:${name}`)].join(" && ");
5781
+ for (const name of targets) scripts[`test:src:${name}`] = `npm run build:src:${name} && ${vitest} --project src:${name}`;
5023
5782
  }
5024
5783
  if (blueprint.app.length > 0) {
5025
- scripts["test:app"] = [vitest, ...blueprint.app.map((environment) => `--project ${APP_MATRIX[environment].project}`)].join(" ");
5784
+ scripts["test:app"] = [
5785
+ vitest,
5786
+ ...blueprint.app.map((environment) => `--project ${APP_MATRIX[environment].project}`),
5787
+ ...application.map((name) => `--project app:${name}`)
5788
+ ].join(" ");
5026
5789
  for (const environment of blueprint.app) scripts[`test:app:${environment}`] = `${vitest} --project ${APP_MATRIX[environment].project}`;
5790
+ for (const name of application) scripts[`test:app:${name}`] = `${vitest} --project app:${name}`;
5027
5791
  }
5028
5792
  scripts["test:policy"] = `${vitest} --project policy`;
5029
5793
  scripts["test:config"] = `${vitest} --project config`;
5030
- if (blueprint.setup.includes("node")) scripts["test:setup"] = `${vitest} --project setup`;
5031
- if (blueprint.setup.includes("browser")) scripts["test:setup:browser"] = `${vitest} --project setup:browser`;
5032
- if (blueprint.journey && blueprint.app.includes("browser")) scripts["test:journey"] = `vitest run --config ${JOURNEY_CONFIG_PATH} --no-cache --reporter=dot`;
5794
+ if (blueprint.setup.includes("node") || blueprint.global) scripts["test:setup"] = `${vitest} --project setup`;
5795
+ if (blueprint.setup.includes("browser") || targets.length > 0) scripts["test:setup:browser"] = `${vitest} --project setup:browser`;
5796
+ if (blueprint.journey && blueprint.app.includes("browser")) {
5797
+ scripts["test:journey"] = `vitest run --config ${JOURNEY_CONFIG_PATH} --no-cache --reporter=dot`;
5798
+ for (const name of application) scripts[`test:journey:${name}`] = `${scripts["test:journey"]} --mode ${name}`;
5799
+ }
5033
5800
  if (blueprint.skills) scripts["test:skills"] = `${vitest} --project skills`;
5034
5801
  if (blueprint.guides) scripts["test:guides"] = `node --experimental-strip-types ${GUIDES_TEST_PATH}`;
5035
5802
  if (blueprint.conformance) scripts["test:conformance"] = `${vitest} --project conformance`;
@@ -5044,23 +5811,34 @@ function blueprintToScripts(blueprint) {
5044
5811
  ...blueprint.app.length > 0 ? ["npm run build:app"] : []
5045
5812
  ].join(" && ");
5046
5813
  if (compiles) {
5047
- scripts["build:src"] = [...blueprint.src.map((environment) => `npm run build:src:${environment}`), ...blueprint.bin ? ["npm run build:src:bin"] : []].join(" && ");
5814
+ scripts["build:src"] = [
5815
+ ...blueprint.src.map((environment) => `npm run build:src:${environment}`),
5816
+ ...source.map((name) => `npm run build:src:${name}`),
5817
+ ...targets.map((name) => `npm run build:src:${name}`),
5818
+ ...blueprint.bin ? ["npm run build:src:bin"] : []
5819
+ ].join(" && ");
5048
5820
  for (const environment of blueprint.src) {
5049
5821
  const build = `vite build --config configs/src/vite.${environment}.config.ts`;
5050
5822
  scripts[`build:src:${environment}`] = SRC_MATRIX[environment].formats.includes("cjs") ? `${build} && npm run copy dist/src/${environment}/index.d.ts dist/src/${environment}/index.d.cts` : build;
5051
5823
  }
5052
5824
  if (blueprint.bin) scripts["build:src:bin"] = "vite build --config configs/src/vite.bin.config.ts";
5825
+ for (const name of source) scripts[`build:src:${name}`] = `vite build --config configs/src/vite.${name}.config.ts`;
5826
+ for (const name of targets) scripts[`build:src:${name}`] = [`vite build --config configs/src/vite.${name}.config.ts`, ...name === "styles" && blueprint.themes ? ["vite build --config configs/src/vite.themes.config.ts"] : []].join(" && ");
5053
5827
  }
5054
5828
  if (blueprint.app.length > 0) {
5055
- scripts["build:app"] = runtime.length > 0 ? runtime.map((environment) => `npm run build:app:${environment}`).join(" && ") : "npm run check:app:core";
5056
- for (const environment of runtime) scripts[`build:app:${environment}`] = `vite build --config configs/app/vite.${environment}.config.ts`;
5829
+ scripts["build:app"] = runtime.length > 0 ? [...runtime, ...application].map((environment) => `npm run build:app:${environment}`).join(" && ") : "npm run check:app:core";
5830
+ for (const environment of [...runtime, ...application]) scripts[`build:app:${environment}`] = `vite build --config configs/app/vite.${environment}.config.ts`;
5057
5831
  }
5058
5832
  if (blueprint.app.includes("browser")) {
5059
5833
  scripts.dev = "vite --config configs/app/vite.browser.config.ts";
5834
+ for (const name of application) scripts[`dev:${name}`] = `vite --config configs/app/vite.${name}.config.ts`;
5060
5835
  if (blueprint.showcase) {
5061
5836
  scripts.showcase = `vite --config ${SHOWCASE_CONFIG_PATH}`;
5062
5837
  scripts["build:showcase"] = `vite build --config ${SHOWCASE_CONFIG_PATH}`;
5063
- scripts.show = "npm run format && npm run build:showcase && npm run copy dist/showcase/index.html demo/showcase.html";
5838
+ for (const name of application) {
5839
+ scripts[`showcase:${name}`] = `${scripts.showcase} --mode ${name}`;
5840
+ scripts[`build:showcase:${name}`] = `${scripts["build:showcase"]} --mode ${name}`;
5841
+ }
5064
5842
  }
5065
5843
  }
5066
5844
  if (blueprint.app.includes("server")) {
@@ -5070,7 +5848,9 @@ function blueprintToScripts(blueprint) {
5070
5848
  if (publishes) {
5071
5849
  scripts.prepack = "npm run build";
5072
5850
  scripts.prepublishOnly = [
5073
- "npm run format:check && npm run lint:check && npm run check && npm run build && npm test",
5851
+ "npm run format:check && npm run lint:check && npm run check && npm run build",
5852
+ ...blueprint.showcase && blueprint.app.includes("browser") ? ["npm run build:showcase", ...application.map((name) => `npm run build:showcase:${name}`)] : [],
5853
+ "npm test",
5074
5854
  RELEASE_PROOF_COMMAND,
5075
5855
  ...blueprint.service ? ["npm run test:service"] : []
5076
5856
  ].join(" && ");
@@ -5088,6 +5868,8 @@ function blueprintToScripts(blueprint) {
5088
5868
  * benchmark workbench scripts. A workspace carrying guides adds `test:guides`,
5089
5869
  * whose package-owned entry runs the guides project and handles explicit parity
5090
5870
  * rewrites, and one carrying skills adds `test:skills`. Publishing adds the pack and publication lifecycle scripts.
5871
+ * Sheet and framework faces add their check and build scripts, application frameworks add dev scripts,
5872
+ * and selected showcases and journeys add their mode scripts.
5091
5873
  * Aggregate test scripts and
5092
5874
  * maintainer-owned gate chains stay outside the region.
5093
5875
  *
@@ -5111,13 +5893,18 @@ function blueprintToScripts(blueprint) {
5111
5893
  */
5112
5894
  function blueprintToWritableScripts(blueprint) {
5113
5895
  const scripts = blueprintToScripts(blueprint);
5896
+ const sheets = [...blueprintToSheets(blueprint), ...blueprint.themes && !blueprint.styles ? ["themes"] : []];
5114
5897
  const writable = [];
5115
5898
  for (const [name, command] of Object.entries(scripts)) {
5116
- if (!(name.startsWith("test:") && name !== "test:src" && name !== "test:app")) continue;
5899
+ const direct = name.startsWith("test:") && name !== "test:src" && name !== "test:app";
5900
+ const sheet = sheets.some((face) => name === `build:src:${face}` || name === `check:src:${face}`);
5901
+ const framework = blueprintToFaces(blueprint).some((extension) => extension.surface === "browser" && extension.axes.some((axis) => name === `check:${axis}:${extension.name}` || name === `build:${axis}:${extension.name}` || axis === "app" && name === `dev:${extension.name}`));
5902
+ const showcase = name === "showcase" || name.startsWith("showcase:") || name === "build:showcase" || name.startsWith("build:showcase:");
5903
+ if (!direct && !sheet && !framework && !showcase) continue;
5117
5904
  writable.push({
5118
5905
  name,
5119
5906
  command,
5120
- accepted: name === "test:guides" ? ["vitest run --config vite.config.ts --no-cache --reporter=dot --project guides"] : []
5907
+ accepted: name === "test:guides" ? ["vitest run --config vite.config.ts --no-cache --reporter=dot --project guides"] : name === "build:src:styles" && blueprint.themes ? ["vite build --config configs/src/vite.styles.config.ts"] : sheets.some((face) => name === `test:src:${face}`) ? [command.slice(command.indexOf(" && ") + 4)] : []
5121
5908
  });
5122
5909
  }
5123
5910
  const prepack = scripts.prepack;
@@ -5173,7 +5960,9 @@ function blueprintToWritableScripts(blueprint) {
5173
5960
  * ```
5174
5961
  */
5175
5962
  function blueprintToManifest(blueprint) {
5176
- const publishes = blueprint.src.length > 0;
5963
+ const sheets = blueprintToSheets(blueprint);
5964
+ const targets = [...sheets, ...blueprint.themes ? ["styles/themes"] : []];
5965
+ const publishes = blueprint.src.length > 0 || targets.length > 0;
5177
5966
  const name = publishes ? `@orkestrel/${blueprint.name}` : blueprint.name;
5178
5967
  const dependencies = {};
5179
5968
  for (const dependency of [...blueprint.dependencies].sort((left, right) => (0, _orkestrel_contract.compareValues)(left.name, right.name))) dependencies[dependency.name] = dependency.range;
@@ -5200,18 +5989,19 @@ function blueprintToManifest(blueprint) {
5200
5989
  url: `git+https://github.com/orkestrel/${blueprint.name}.git`
5201
5990
  } } : {},
5202
5991
  ...blueprint.bin ? { bin: { [blueprint.name]: "./dist/bin/main.js" } } : {},
5203
- files: blueprint.bin ? [
5204
- "dist/src",
5205
- "dist/bin",
5992
+ files: [
5993
+ blueprint.bin || publishes ? "dist/src" : "dist/app",
5994
+ ...blueprint.bin ? ["dist/bin"] : [],
5995
+ ...targets.map((face) => `!dist/src/${face}/index.js`),
5996
+ ...sheets.map((face) => `src/${face}/**/*.scss`),
5997
+ ...blueprint.themes && !blueprint.styles ? ["src/styles/themes/**/*.scss"] : [],
5206
5998
  "README.md"
5207
- ] : publishes ? ["dist/src", "README.md"] : ["dist/app", "README.md"],
5999
+ ],
5208
6000
  type: "module",
5209
6001
  ...publishes ? {
5210
- sideEffects: blueprint.bin ? [`./${BIN_ENTRY_PATH}`, "./dist/bin/main.js"] : false,
5211
- main: entry.main,
5212
- module: entry.module,
5213
- ...entry.types === void 0 ? {} : { types: entry.types },
5214
- exports: srcToExports(blueprint.src),
6002
+ sideEffects: targets.length > 0 || blueprint.bin ? [...targets.length > 0 ? ["**/*.css", "**/*.scss"] : [], ...blueprint.bin ? [`./${BIN_ENTRY_PATH}`, "./dist/bin/main.js"] : []] : false,
6003
+ ...blueprint.src.length > 0 ? entry : {},
6004
+ exports: blueprintToExports(blueprint),
5215
6005
  publishConfig: { access: "public" }
5216
6006
  } : {},
5217
6007
  scripts: blueprintToScripts(blueprint),
@@ -5248,15 +6038,16 @@ function blueprintToManifest(blueprint) {
5248
6038
  *
5249
6039
  * const blueprint = createBlueprint('router', { app: ['browser'] })
5250
6040
  *
5251
- * blueprintToMachinery(blueprint).vue // true
6041
+ * blueprintToMachinery(blueprint).frameworks // []
5252
6042
  * ```
5253
6043
  */
5254
6044
  function blueprintToMachinery(blueprint) {
5255
6045
  const hosted = blueprint.app.some((environment) => environment !== "core");
6046
+ const frameworks = FRAMEWORKS.filter((framework) => blueprintToFaces(blueprint).some((extension) => extension.surface === "browser" && extension.name === framework && extension.axes.some((axis) => blueprint[axis].includes("browser"))));
5256
6047
  return {
5257
- browser: blueprint.src.includes("browser") || blueprint.app.includes("browser") || blueprint.setup.includes("browser"),
5258
- vue: blueprint.app.includes("browser"),
5259
- output: blueprint.src.length > 0 || blueprint.bin || hosted,
6048
+ browser: blueprint.src.includes("browser") || blueprint.app.includes("browser") || blueprint.setup.includes("browser") || blueprintToSheets(blueprint).length > 0 || blueprint.themes || frameworks.length > 0,
6049
+ frameworks,
6050
+ output: blueprint.src.length > 0 || blueprint.bin || hosted || blueprintToSheets(blueprint).length > 0 || blueprint.themes,
5260
6051
  showcase: blueprint.showcase && blueprint.app.includes("browser")
5261
6052
  };
5262
6053
  }
@@ -5292,6 +6083,12 @@ function blueprintToMachinery(blueprint) {
5292
6083
  */
5293
6084
  function blueprintToRootTsconfig(blueprint) {
5294
6085
  const aliases = [];
6086
+ for (const extension of blueprintToFaces(blueprint)) {
6087
+ if (extension.surface !== "browser") continue;
6088
+ for (const axis of extension.axes) aliases.push([`@${axis}/${extension.name}`, `./${axis}/${extension.name}/index.ts`]);
6089
+ if (extension.axes.includes("src")) aliases.push([`@orkestrel/${blueprint.name}/${extension.name}`, `./src/${extension.name}/index.ts`]);
6090
+ }
6091
+ for (const name of blueprintToSheets(blueprint)) aliases.push([`@src/${name}`, `./src/${name}/index.ts`]);
5295
6092
  for (const environment of ENVIRONMENTS) if (blueprint.src.includes(environment)) aliases.push([`@src/${environment}`, `./src/${environment}/index.ts`]);
5296
6093
  for (const environment of ENVIRONMENTS) if (blueprint.app.includes(environment)) aliases.push([`@app/${environment}`, `./app/${environment}/index.ts`]);
5297
6094
  const root = srcToRoot(blueprint.src);
@@ -5307,7 +6104,47 @@ function blueprintToRootTsconfig(blueprint) {
5307
6104
  if (matchesPrintWidth(line)) return line;
5308
6105
  return `\t\t\t"${specifier}": [\n\t\t\t\t"${target}"\n\t\t\t]${separator}`;
5309
6106
  }).join("\n");
5310
- return (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.root.tsconfig, { paths });
6107
+ return (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.root.tsconfig, { paths: aliases.length === 0 ? " \"paths\": {}" : `\t\t"paths": {\n${paths}\n\t\t}` });
6108
+ }
6109
+ /**
6110
+ * Projects a blueprint into the project labels its root Vitest configuration registers.
6111
+ *
6112
+ * @param blueprint - The workspace specification.
6113
+ * @returns Project labels in registration order, including wrapper-registered sheet faces.
6114
+ *
6115
+ * @example
6116
+ * ```ts
6117
+ * import { blueprintToProjects, createBlueprint } from '@orkestrel/scaffold'
6118
+ *
6119
+ * blueprintToProjects(createBlueprint('desk')).includes('policy') // true
6120
+ * ```
6121
+ */
6122
+ function blueprintToProjects(blueprint) {
6123
+ const sheets = blueprintToSheets(blueprint);
6124
+ const styled = sheets.length > 0 || blueprint.themes;
6125
+ const faces = blueprintToFaces(blueprint);
6126
+ const projects = sheets.map((name) => `src:${name}`);
6127
+ if (blueprint.themes && !blueprint.styles) projects.push("src:themes");
6128
+ for (const axis of AXES) {
6129
+ for (const environment of ENVIRONMENTS) {
6130
+ if (axis === "app" && environment === "server") continue;
6131
+ if (blueprint[axis].includes(environment)) projects.push(`${axis}:${environment}`);
6132
+ }
6133
+ for (const face of faces) if (face.surface === "browser" && face.axes.includes(axis)) projects.push(`${axis}:${face.name}`);
6134
+ if (axis === "src" && blueprint.bin) projects.push("src:bin");
6135
+ if (axis === "app" && blueprint.app.includes("server")) projects.push("app:server");
6136
+ }
6137
+ projects.push("policy", "config");
6138
+ if (blueprint.setup.includes("node") || blueprint.global) projects.push("setup");
6139
+ if (blueprint.setup.includes("browser") || styled) projects.push("setup:browser");
6140
+ if (blueprint.guides) projects.push("guides");
6141
+ if (blueprint.skills) projects.push("skills");
6142
+ if (blueprint.conformance) projects.push("conformance");
6143
+ if (blueprint.service) projects.push("service");
6144
+ if (blueprint.src.length > 0 || styled) projects.push("distribution");
6145
+ if (blueprint.integration) projects.push("integration");
6146
+ projects.push("probe");
6147
+ return projects;
5311
6148
  }
5312
6149
  /**
5313
6150
  * Compiles the root Vite and Vitest configuration for a blueprint.
@@ -5331,44 +6168,58 @@ function blueprintToRootTsconfig(blueprint) {
5331
6168
  */
5332
6169
  function blueprintToRootVite(blueprint) {
5333
6170
  const machinery = blueprintToMachinery(blueprint);
5334
- const publishes = blueprint.src.length > 0;
6171
+ const planned = new Set(blueprintToProjects(blueprint));
6172
+ const sheets = blueprintToSheets(blueprint);
6173
+ const styled = sheets.length > 0 || blueprint.themes;
5335
6174
  const global = blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "";
5336
6175
  const imports = [];
5337
6176
  if (machinery.browser) imports.push("import { playwright } from '@vitest/browser-playwright'");
5338
- if (machinery.vue) imports.push("import vue from '@vitejs/plugin-vue'");
6177
+ if (machinery.frameworks.includes("vue")) imports.push("import vue from '@vitejs/plugin-vue'");
5339
6178
  if (machinery.showcase) imports.push("import { viteSingleFile } from 'vite-plugin-singlefile'");
6179
+ if (machinery.showcase) {
6180
+ imports.push("import { renameSync } from 'node:fs'");
6181
+ imports.push("import { resolve as resolvePath } from 'node:path'");
6182
+ }
5340
6183
  const factories = [];
5341
6184
  const projects = [];
5342
- if (blueprint.src.includes("core")) {
6185
+ const external = "external: resolveSourceExternal,";
6186
+ if (blueprint.src.includes("browser") || blueprint.src.includes("server")) factories.push(blueprint.src.includes("core") ? `function resolveSourceExternal(id: string): boolean {
6187
+ return (
6188
+ id === '@src/core' ||
6189
+ resolveExternal(id, {
6190
+ peers,
6191
+ refused: [],
6192
+ siblings: [resolveWorkspacePath('src/core/index.ts')],
6193
+ })
6194
+ )
6195
+ }
6196
+ ` : `function resolveSourceExternal(id: string): boolean {
6197
+ return resolveExternal(id, { peers, refused: [], siblings: [] })
6198
+ }
6199
+ `);
6200
+ if (styled) {
6201
+ factories.push(CONFIG_TEMPLATES.factories.sheet);
6202
+ for (const name of sheets) projects.push(`'./configs/src/vite.${name}.config.ts'`);
6203
+ if (planned.has("src:themes")) projects.push("'./configs/src/vite.themes.config.ts'");
6204
+ }
6205
+ if (planned.has("src:core")) {
5343
6206
  factories.push(CONFIG_TEMPLATES.factories.src.core);
5344
6207
  projects.push("srcCore");
5345
6208
  }
5346
- if (blueprint.src.includes("browser")) {
6209
+ if (planned.has("src:browser")) {
5347
6210
  const core = blueprint.src.includes("core");
5348
6211
  factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.src.browser, {
5349
- external: core ? `external: (id: string) =>
5350
- id === '@src/core' ||
5351
- id.startsWith('@orkestrel/') ||
5352
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
5353
- id.startsWith('@orkestrel/') ||
5354
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
6212
+ external,
5355
6213
  output: core ? " output: { paths: { '@src/core': '../core/index.js' } }," : " output: {},",
5356
6214
  exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : "",
5357
6215
  global
5358
6216
  }));
5359
6217
  projects.push("srcBrowser");
5360
6218
  }
5361
- if (blueprint.src.includes("server")) {
6219
+ if (planned.has("src:server")) {
5362
6220
  const core = blueprint.src.includes("core");
5363
6221
  factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.src.server, {
5364
- external: core ? `external: (id: string) =>
5365
- id === '@src/core' ||
5366
- id.startsWith('node:') ||
5367
- id.startsWith('@orkestrel/') ||
5368
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
5369
- id.startsWith('node:') ||
5370
- id.startsWith('@orkestrel/') ||
5371
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
6222
+ external,
5372
6223
  output: core ? `\t\t\t\toutput: [
5373
6224
  {
5374
6225
  format: 'es',
@@ -5385,15 +6236,19 @@ function blueprintToRootVite(blueprint) {
5385
6236
  }));
5386
6237
  projects.push("srcServer");
5387
6238
  }
5388
- if (blueprint.bin) {
6239
+ if (planned.has("src:vue")) {
6240
+ factories.push(CONFIG_TEMPLATES.factories.src.vue);
6241
+ projects.push("srcVue");
6242
+ }
6243
+ if (planned.has("src:bin")) {
5389
6244
  factories.push(CONFIG_TEMPLATES.factories.src.bin);
5390
6245
  projects.push("srcBin");
5391
6246
  }
5392
- if (blueprint.app.includes("core")) {
6247
+ if (planned.has("app:core")) {
5393
6248
  factories.push(CONFIG_TEMPLATES.factories.app.core);
5394
6249
  projects.push("appCore");
5395
6250
  }
5396
- if (blueprint.app.includes("browser")) {
6251
+ if (planned.has("app:browser")) {
5397
6252
  const showcaseFactory = machinery.showcase ? CONFIG_TEMPLATES.factories.app.showcase : "";
5398
6253
  factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.app.browser, {
5399
6254
  showcaseFactory,
@@ -5402,7 +6257,11 @@ function blueprintToRootVite(blueprint) {
5402
6257
  }));
5403
6258
  projects.push("appBrowser");
5404
6259
  }
5405
- if (blueprint.app.includes("server")) {
6260
+ if (planned.has("app:vue")) {
6261
+ factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.app.vue, { journeyExclude: blueprint.journey ? " exclude: ['tests/app/vue/integration.test.ts'],\n" : " exclude: [],\n" }));
6262
+ projects.push("appVue");
6263
+ }
6264
+ if (planned.has("app:server")) {
5406
6265
  factories.push(CONFIG_TEMPLATES.factories.app.server);
5407
6266
  projects.push("appServer");
5408
6267
  }
@@ -5410,39 +6269,40 @@ function blueprintToRootVite(blueprint) {
5410
6269
  projects.push("policy");
5411
6270
  factories.push(CONFIG_TEMPLATES.factories.config);
5412
6271
  projects.push("config");
5413
- if (blueprint.setup.includes("node")) {
6272
+ if (planned.has("setup")) {
5414
6273
  factories.push(CONFIG_TEMPLATES.factories.setup);
5415
6274
  projects.push("setup");
5416
6275
  }
5417
- if (blueprint.setup.includes("browser")) {
6276
+ if (planned.has("setup:browser")) {
5418
6277
  factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.browser, {
5419
- plugins: machinery.vue ? " plugins: [vue()],\n" : "",
6278
+ optimization: machinery.frameworks.includes("vue") ? "optimizeDeps: { include: [...optimizeDeps.include, 'vue'] }" : "optimizeDeps",
6279
+ plugins: machinery.frameworks.includes("vue") ? " plugins: [vue()],\n" : "",
5420
6280
  global
5421
6281
  }));
5422
6282
  projects.push("setupBrowser");
5423
6283
  }
5424
- if (blueprint.guides) {
6284
+ if (planned.has("guides")) {
5425
6285
  factories.push(CONFIG_TEMPLATES.factories.guides);
5426
6286
  projects.push("guides");
5427
6287
  }
5428
- if (blueprint.skills) {
6288
+ if (planned.has("skills")) {
5429
6289
  factories.push(CONFIG_TEMPLATES.factories.skills);
5430
6290
  projects.push("skills");
5431
6291
  }
5432
- if (blueprint.conformance) {
6292
+ if (planned.has("conformance")) {
5433
6293
  factories.push(CONFIG_TEMPLATES.factories.conformance);
5434
6294
  projects.push("conformance");
5435
6295
  }
5436
- if (blueprint.service) {
6296
+ if (planned.has("service")) {
5437
6297
  factories.push(CONFIG_TEMPLATES.factories.service);
5438
6298
  projects.push("service");
5439
6299
  }
5440
- if (publishes) {
6300
+ if (planned.has("distribution")) {
5441
6301
  factories.push(CONFIG_TEMPLATES.factories.distribution);
5442
6302
  projects.push("distribution");
5443
6303
  }
5444
- if (blueprint.integration) {
5445
- factories.push((0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.factories.integration, { global }));
6304
+ if (planned.has("integration")) {
6305
+ factories.push((0, _orkestrel_template.fillTemplate)(styled ? CONFIG_TEMPLATES.factories.integration.sheet : CONFIG_TEMPLATES.factories.integration.module, { global }));
5446
6306
  projects.push("integration");
5447
6307
  }
5448
6308
  factories.push(CONFIG_TEMPLATES.factories.probe);
@@ -5455,14 +6315,24 @@ ${projects.map((project) => `\t\t\t${project},`).join("\n")}
5455
6315
  const helpers = [
5456
6316
  "enforceBuildLog",
5457
6317
  "environmentBoundary",
5458
- "outputBoundary"
6318
+ "outputBoundary",
6319
+ "resolveApplication",
6320
+ "resolveExternal",
6321
+ "stampPage"
5459
6322
  ].filter((helper) => body.includes(helper));
6323
+ const helperImport = `import { ${helpers.join(", ")} } from './configs/helpers.js'\n`;
5460
6324
  return (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.root.vite, {
5461
- journey: blueprint.journey && machinery.vue ? "import type { JourneyVariant } from '@orkestrel/test'\n" : "",
6325
+ journey: blueprint.journey && blueprint.app.includes("browser") ? "import type { JourneyVariant } from '@orkestrel/test'\n" : "",
5462
6326
  imports: imports.length === 0 ? "" : `${imports.join("\n")}\n`,
5463
- helpers: helpers.length === 0 ? "" : `import { ${helpers.join(", ")} } from './configs/helpers.js'\n`,
6327
+ helpers: helpers.length === 0 ? "" : matchesPrintWidth(helperImport.trimEnd()) ? helperImport : `import {\n${helpers.map((helper) => `\t${helper},`).join("\n")}\n} from './configs/helpers.js'\n`,
5464
6328
  browsers: machinery.browser ? "import { resolveBrowser, resolvePinnedBrowser } from './configs/browsers.js'\n" : "",
5465
- options: (machinery.browser ? "const browserOptions = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)\n\n" : "") + (blueprint.journey && machinery.vue ? "const capture = process.env.CAPTURE === '1'\n\n" : ""),
6329
+ options: (machinery.browser ? `const browserOptions = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)
6330
+
6331
+ const optimizeDeps = {\n\tinclude: ${JSON.stringify([
6332
+ "@orkestrel/test",
6333
+ "@orkestrel/test/browser",
6334
+ ...Object.hasOwn(blueprintToDevDependencies(blueprint), "@orkestrel/contract") || blueprint.dependencies.some((dependency) => dependency.name === "@orkestrel/contract") || blueprint.peers.some((peer) => peer.name === "@orkestrel/contract") ? ["@orkestrel/contract"] : []
6335
+ ]).replaceAll("\"", "'").replaceAll(",", ", ")},\n}\n\n` : "") + (blueprint.journey && blueprint.app.includes("browser") ? "const capture = process.env.CAPTURE === '1'\n\n" : "") + ((blueprint.journey || blueprint.showcase) && blueprint.app.includes("browser") ? `const applications = {\n\tbrowser: appBrowser,\n${blueprintToFaces(blueprint).filter((extension) => extension.surface === "browser" && extension.axes.includes("app")).map((extension) => `\t${extension.name}: app${extension.name[0]?.toUpperCase()}${extension.name.slice(1)},\n`).join("")}}\n\n` : ""),
5466
6336
  factories: body,
5467
6337
  projects: projectRows
5468
6338
  });
@@ -5503,6 +6373,26 @@ function blueprintToConfigArtifacts(blueprint) {
5503
6373
  origin: "template",
5504
6374
  content: CONFIG_TEMPLATES.browsers
5505
6375
  });
6376
+ for (const name of [...blueprintToSheets(blueprint), ...blueprint.themes ? ["styles/themes"] : []]) {
6377
+ const label = name === "styles/themes" ? "themes" : name;
6378
+ artifacts.push({
6379
+ path: `configs/src/vite.${label}.config.ts`,
6380
+ group: "configs",
6381
+ ownership: "content",
6382
+ origin: "template",
6383
+ content: (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.vites.sheet, {
6384
+ name,
6385
+ label
6386
+ })
6387
+ });
6388
+ if (name !== "styles/themes") artifacts.push({
6389
+ path: `configs/src/tsconfig.${name}.json`,
6390
+ group: "configs",
6391
+ ownership: "content",
6392
+ origin: "template",
6393
+ content: (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.tsconfigs.sheet, { name })
6394
+ });
6395
+ }
5506
6396
  for (const environment of blueprint.src) for (const path of SRC_MATRIX[environment].configs) {
5507
6397
  let content = CONFIG_TEMPLATES.vites.src.core;
5508
6398
  if (path === "configs/src/tsconfig.core.json") content = CONFIG_TEMPLATES.tsconfigs.src.core;
@@ -5540,11 +6430,10 @@ ${paths.join("\n")}
5540
6430
  "../../app/browser/**/*.cts",
5541
6431
  "../../app/browser/**/*.mts",
5542
6432
  "../../app/browser/**/*.ts",
5543
- "../../app/browser/**/*.tsx",
5544
- "../../app/browser/**/*.vue"
6433
+ "../../app/browser/**/*.tsx"
5545
6434
  ];
5546
6435
  if (blueprint.app.includes("core")) include.push("../../app/core/**/*.cts", "../../app/core/**/*.mts", "../../app/core/**/*.ts", "../../app/core/**/*.tsx");
5547
- include.push("../../tests/app/browser/**/*.cts", "../../tests/app/browser/**/*.mts", "../../tests/app/browser/**/*.ts", "../../tests/app/browser/**/*.tsx", "../../tests/app/browser/**/*.vue", "../../tests/setup.ts", "../../tests/setupBrowser.ts");
6436
+ include.push("../../tests/app/browser/**/*.cts", "../../tests/app/browser/**/*.mts", "../../tests/app/browser/**/*.ts", "../../tests/app/browser/**/*.tsx", "../../tests/setup.ts", "../../tests/setupBrowser.ts");
5548
6437
  content = (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.tsconfigs.app.browser, { include: include.map((entry, index) => `\t\t${JSON.stringify(entry)}${index === include.length - 1 ? "" : ","}`).join("\n") });
5549
6438
  } else if (path === "configs/app/vite.server.config.ts") content = CONFIG_TEMPLATES.vites.app.server;
5550
6439
  else if (path === "configs/app/tsconfig.server.json") {
@@ -5567,6 +6456,58 @@ ${paths.join("\n")}
5567
6456
  content
5568
6457
  });
5569
6458
  }
6459
+ for (const extension of blueprintToFaces(blueprint)) {
6460
+ if (extension.surface !== "browser") continue;
6461
+ for (const axis of extension.axes) {
6462
+ const include = [
6463
+ ...[
6464
+ "cts",
6465
+ "mts",
6466
+ "ts",
6467
+ "tsx",
6468
+ ...FRAMEWORK_MATRIX[extension.name].suffixes
6469
+ ].map((suffix) => `../../app/${extension.name}/**/*.${suffix}`),
6470
+ ...blueprint.app.includes("core") ? [
6471
+ "cts",
6472
+ "mts",
6473
+ "ts",
6474
+ "tsx"
6475
+ ].map((suffix) => `../../app/core/**/*.${suffix}`) : [],
6476
+ ...[
6477
+ "cts",
6478
+ "mts",
6479
+ "ts",
6480
+ "tsx",
6481
+ ...FRAMEWORK_MATRIX[extension.name].suffixes
6482
+ ].map((suffix) => `../../tests/app/${extension.name}/**/*.${suffix}`),
6483
+ "../../tests/setup.ts",
6484
+ "../../tests/setupBrowser.ts"
6485
+ ];
6486
+ const siblings = blueprint.src.filter((environment) => environment === "core" || environment === "browser").flatMap((environment) => ["ts", "js"].map((suffix) => {
6487
+ const key = `\t[resolveWorkspacePath('src/${environment}/index.${suffix}').replaceAll('\\\\', '/')]:`;
6488
+ const value = `'@orkestrel/${blueprint.name}${environment === "browser" ? "/browser" : ""}',`;
6489
+ return matchesPrintWidth(`${key} ${value}`) ? `${key} ${value}` : `${key}\n\t\t${value}`;
6490
+ })).join("\n");
6491
+ artifacts.push({
6492
+ path: `configs/${axis}/vite.${extension.name}.config.ts`,
6493
+ group: "configs",
6494
+ ownership: "content",
6495
+ origin: "template",
6496
+ environment: "browser",
6497
+ content: axis === "src" ? (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.vites.src.vue, {
6498
+ siblings,
6499
+ refused: `[${FRAMEWORK_MATRIX[extension.name].refused.map((name) => serializeTypeScriptString(name)).join(", ")}]`
6500
+ }) : CONFIG_TEMPLATES.vites.app.vue
6501
+ }, {
6502
+ path: `configs/${axis}/tsconfig.${extension.name}.json`,
6503
+ group: "configs",
6504
+ ownership: "content",
6505
+ origin: "template",
6506
+ environment: "browser",
6507
+ content: axis === "src" ? CONFIG_TEMPLATES.tsconfigs.vue.src : (0, _orkestrel_template.fillTemplate)(CONFIG_TEMPLATES.tsconfigs.vue.app, { include: include.map((entry, index) => `\t\t${JSON.stringify(entry)}${index === include.length - 1 ? "" : ","}`).join("\n") })
6508
+ });
6509
+ }
6510
+ }
5570
6511
  if (blueprint.showcase && blueprint.app.includes("browser")) artifacts.push({
5571
6512
  path: SHOWCASE_CONFIG_PATH,
5572
6513
  group: "configs",
@@ -5596,15 +6537,11 @@ ${paths.join("\n")}
5596
6537
  * Compiles every artifact in the `source` group.
5597
6538
  *
5598
6539
  * @param blueprint - The workspace specification.
5599
- * @returns Empty published barrels, selected application entries, and the optional bin entry.
6540
+ * @returns Published barrels, sheet seeds, selected application entries, and the optional bin entry.
5600
6541
  *
5601
6542
  * @remarks
5602
- * The barrels and every runtime entry intentionally hold nothing. A generated
5603
- * sample entity would read as package implementation, so the
5604
- * scaffold establishes only the selected environment boundaries. An application
5605
- * entry is empty for the same reason the bin entry is, and because the vendored
5606
- * lint config refuses an unassigned import outside a stylesheet, so the entry
5607
- * cannot start by importing its barrel for effect either.
6543
+ * Barrels, server entries, and bin entries hold no starter implementation. Browser entries
6544
+ * render the workspace name as a level-one heading; the Vue face mounts its arrival component.
5608
6545
  *
5609
6546
  * @example
5610
6547
  * ```ts
@@ -5617,6 +6554,20 @@ ${paths.join("\n")}
5617
6554
  */
5618
6555
  function blueprintToSourceArtifacts(blueprint) {
5619
6556
  const artifacts = [];
6557
+ if (blueprint.themes && !blueprint.styles) artifacts.push({
6558
+ path: "src/styles/_tokens.scss",
6559
+ group: "source",
6560
+ ownership: "birth",
6561
+ origin: "template",
6562
+ content: ARTIFACT_TEMPLATES.source.sheet["_tokens.scss"]
6563
+ });
6564
+ for (const name of [...blueprintToSheets(blueprint), ...blueprint.themes ? ["styles/themes"] : []]) for (const [path, content] of Object.entries(name === "styles/themes" ? ARTIFACT_TEMPLATES.source.themes : ARTIFACT_TEMPLATES.source.sheet)) artifacts.push({
6565
+ path: `src/${name}/${path}`,
6566
+ group: "source",
6567
+ ownership: "birth",
6568
+ origin: "template",
6569
+ content
6570
+ });
5620
6571
  for (const environment of blueprint.src) artifacts.push({
5621
6572
  path: `src/${environment}/index.ts`,
5622
6573
  group: "source",
@@ -5640,7 +6591,7 @@ function blueprintToSourceArtifacts(blueprint) {
5640
6591
  ownership: "birth",
5641
6592
  origin: "template",
5642
6593
  environment,
5643
- content: ARTIFACT_TEMPLATES.source.empty
6594
+ content: (0, _orkestrel_template.fillTemplate)(ARTIFACT_TEMPLATES.source.main, { heading: `${matchesPrintWidth(`\t.appendChild(document.createElement('h1')).textContent = ${serializeTypeScriptString(blueprint.name)}`) ? " " : "\n "}${serializeTypeScriptString(blueprint.name)}` })
5644
6595
  }, {
5645
6596
  path: "app/browser/index.html",
5646
6597
  group: "source",
@@ -5658,6 +6609,36 @@ function blueprintToSourceArtifacts(blueprint) {
5658
6609
  content: ARTIFACT_TEMPLATES.source.empty
5659
6610
  });
5660
6611
  }
6612
+ for (const extension of blueprintToFaces(blueprint)) {
6613
+ if (extension.surface !== "browser") continue;
6614
+ for (const axis of extension.axes) {
6615
+ artifacts.push({
6616
+ path: `${axis}/${extension.name}/index.ts`,
6617
+ group: "source",
6618
+ ownership: "birth",
6619
+ origin: "template",
6620
+ environment: "browser",
6621
+ content: axis === "app" ? ARTIFACT_TEMPLATES.source.empty : ARTIFACT_TEMPLATES.source.barrel
6622
+ });
6623
+ if (axis !== "app") continue;
6624
+ artifacts.push({
6625
+ path: `app/${extension.name}/index.html`,
6626
+ group: "source",
6627
+ ownership: "birth",
6628
+ origin: "template",
6629
+ environment: "browser",
6630
+ content: ARTIFACT_TEMPLATES.source.browser
6631
+ });
6632
+ for (const [path, content] of Object.entries(ARTIFACT_TEMPLATES.source.vue)) artifacts.push({
6633
+ path: `app/${extension.name}/${path}`,
6634
+ group: "source",
6635
+ ownership: "birth",
6636
+ origin: "template",
6637
+ environment: "browser",
6638
+ content: (0, _orkestrel_template.fillTemplate)(content, { heading: matchesPrintWidth(`\t<h1>${blueprint.name}</h1>`) ? blueprint.name : `\n\t\t${blueprint.name}\n\t` })
6639
+ });
6640
+ }
6641
+ }
5661
6642
  if (blueprint.bin) artifacts.push({
5662
6643
  path: BIN_ENTRY_PATH,
5663
6644
  group: "source",
@@ -5671,7 +6652,7 @@ function blueprintToSourceArtifacts(blueprint) {
5671
6652
  * Compiles every artifact in the `tests` group that is not vendored from the host.
5672
6653
  *
5673
6654
  * @param blueprint - The workspace specification.
5674
- * @returns Shared setup modules, axis tests, and the optional integration seed.
6655
+ * @returns Shared setup modules and proofs, axis and sheet tests, journey suites, and the optional integration seed.
5675
6656
  *
5676
6657
  * @remarks
5677
6658
  * `tests/setupPolicy.ts`, `tests/policy.test.ts`, and `tests/config.test.ts` are
@@ -5710,15 +6691,53 @@ function blueprintToTestArtifacts(blueprint) {
5710
6691
  origin: "template",
5711
6692
  content: ARTIFACT_TEMPLATES.tests.setup
5712
6693
  }];
6694
+ const sheets = [...blueprintToSheets(blueprint), ...blueprint.themes ? ["styles/themes"] : []];
6695
+ for (const extension of blueprintToFaces(blueprint)) {
6696
+ if (extension.surface !== "browser") continue;
6697
+ for (const axis of extension.axes) artifacts.push({
6698
+ path: `tests/${axis}/${extension.name}/index.test.ts`,
6699
+ group: "tests",
6700
+ ownership: "birth",
6701
+ origin: "template",
6702
+ environment: "browser",
6703
+ content: (0, _orkestrel_template.fillTemplate)(ARTIFACT_TEMPLATES.tests.entry, {
6704
+ specifier: serializeTypeScriptString(`@${axis}/${extension.name}`),
6705
+ label: serializeTypeScriptString(`${axis} ${extension.name} entry`)
6706
+ })
6707
+ });
6708
+ }
6709
+ if (sheets.length > 0) artifacts.push({
6710
+ path: "tests/setupStyles.ts",
6711
+ group: "tests",
6712
+ ownership: "birth",
6713
+ origin: "template",
6714
+ content: ARTIFACT_TEMPLATES.tests.styles.module
6715
+ }, {
6716
+ path: "tests/setupStyles.test.ts",
6717
+ group: "tests",
6718
+ ownership: "birth",
6719
+ origin: "template",
6720
+ content: ARTIFACT_TEMPLATES.tests.styles.proof
6721
+ });
6722
+ for (const name of sheets) artifacts.push({
6723
+ path: `tests/src/${name}/index.test.ts`,
6724
+ group: "tests",
6725
+ ownership: "birth",
6726
+ origin: "template",
6727
+ content: (0, _orkestrel_template.fillTemplate)(ARTIFACT_TEMPLATES.tests.sheet, {
6728
+ name,
6729
+ setup: name === "styles/themes" ? "../../../setupStyles.js" : "../../setupStyles.js"
6730
+ })
6731
+ });
5713
6732
  if (blueprintToMachinery(blueprint).browser) artifacts.push({
5714
6733
  path: "tests/setupBrowser.ts",
5715
6734
  group: "tests",
5716
6735
  ownership: "birth",
5717
6736
  origin: "template",
5718
6737
  environment: "browser",
5719
- content: ARTIFACT_TEMPLATES.tests.setup
6738
+ content: blueprint.journey ? ARTIFACT_TEMPLATES.tests.browser : ARTIFACT_TEMPLATES.tests.setup
5720
6739
  });
5721
- if (blueprint.src.includes("server") || blueprint.app.includes("server") || blueprint.bin) artifacts.push({
6740
+ if (blueprint.src.includes("server") || blueprint.app.includes("server") || blueprint.bin || blueprint.conformance) artifacts.push({
5722
6741
  path: "tests/setupServer.ts",
5723
6742
  group: "tests",
5724
6743
  ownership: "birth",
@@ -5733,13 +6752,40 @@ function blueprintToTestArtifacts(blueprint) {
5733
6752
  origin: "template",
5734
6753
  content: ARTIFACT_TEMPLATES.tests.setup
5735
6754
  });
5736
- if (blueprint.global) artifacts.push({
5737
- path: GLOBAL_SETUP_PATH,
5738
- group: "tests",
5739
- ownership: "birth",
5740
- origin: "template",
5741
- content: ARTIFACT_TEMPLATES.tests.global
5742
- });
6755
+ if (blueprint.global) {
6756
+ artifacts.push({
6757
+ path: GLOBAL_SETUP_PATH,
6758
+ group: "tests",
6759
+ ownership: "birth",
6760
+ origin: "template",
6761
+ content: ARTIFACT_TEMPLATES.tests.global.module
6762
+ });
6763
+ artifacts.push({
6764
+ path: "tests/setupGlobal.test.ts",
6765
+ group: "tests",
6766
+ ownership: "birth",
6767
+ origin: "template",
6768
+ content: ARTIFACT_TEMPLATES.tests.global.proof
6769
+ });
6770
+ }
6771
+ if (blueprint.journey && blueprint.app.includes("browser")) {
6772
+ const applications = ["browser", ...blueprintToFaces(blueprint).flatMap((extension) => extension.surface === "browser" && extension.axes.includes("app") ? [extension.name] : [])];
6773
+ for (const application of applications) artifacts.push({
6774
+ path: `tests/app/${application}/integration.test.ts`,
6775
+ group: "tests",
6776
+ ownership: "birth",
6777
+ origin: "template",
6778
+ environment: "browser",
6779
+ content: (0, _orkestrel_template.fillTemplate)(ARTIFACT_TEMPLATES.tests.journey, {
6780
+ application,
6781
+ heading: serializeTypeScriptString(blueprint.name),
6782
+ imports: application === "vue" ? "import { createApp } from 'vue'\nimport App from '../../../app/vue/App.vue'\n" : "",
6783
+ mount: application === "vue" ? " const container = document.body.appendChild(document.createElement('main'))\n const app = createApp(App)\n" : "",
6784
+ arrival: application === "vue" ? " app.mount(container)\n" : " await import('../../../app/browser/main.js')\n",
6785
+ cleanup: application === "vue" ? " app.unmount()\n container.remove()\n" : " for (const main of page.getByRole('main').elements()) main.remove()\n"
6786
+ })
6787
+ });
6788
+ }
5743
6789
  for (const environment of blueprint.src) artifacts.push({
5744
6790
  path: `tests/src/${environment}/index.test.ts`,
5745
6791
  group: "tests",
@@ -5774,7 +6820,7 @@ function blueprintToTestArtifacts(blueprint) {
5774
6820
  label: serializeTypeScriptString(`app ${environment} entry`)
5775
6821
  })
5776
6822
  });
5777
- if (blueprint.src.length > 0) {
6823
+ if (blueprint.src.length > 0 || blueprintToSheets(blueprint).length > 0 || blueprint.themes) {
5778
6824
  const browser = blueprint.src.includes("browser");
5779
6825
  const distribution = ARTIFACT_TEMPLATES.tests.distribution;
5780
6826
  artifacts.push({
@@ -5843,7 +6889,12 @@ function blueprintToGuideArtifacts(blueprint) {
5843
6889
  const tests = [];
5844
6890
  const directories = [];
5845
6891
  const guide = `guides/${blueprint.name}.md`;
5846
- for (const environment of blueprint.src) {
6892
+ for (const environment of [
6893
+ ...blueprint.src,
6894
+ ...blueprintToFaces(blueprint).filter((extension) => extension.axes.includes("src")).map((extension) => extension.name),
6895
+ ...blueprintToSheets(blueprint),
6896
+ ...blueprint.themes ? ["styles/themes"] : []
6897
+ ]) {
5847
6898
  source.push(` - [\`src/${environment}\`](../src/${environment})`);
5848
6899
  tests.push(` - [\`tests/src/${environment}\`](../tests/src/${environment})`);
5849
6900
  directories.push(`- [\`src/${environment}\`](../src/${environment})\n - Guide: Not created. Create this file when the workspace has a public surface:\n \`${guide}\`\n - Tests: [\`tests/src/${environment}\`](../tests/src/${environment})`);
@@ -5853,7 +6904,7 @@ function blueprintToGuideArtifacts(blueprint) {
5853
6904
  tests.push(" - [`tests/src/bin`](../tests/src/bin)");
5854
6905
  directories.push(`- [\`src/bin\`](../src/bin)\n - Guide: Not created. Create this file when the workspace has a public surface:\n \`${guide}\`\n - Tests: [\`tests/src/bin\`](../tests/src/bin)`);
5855
6906
  }
5856
- for (const environment of blueprint.app) {
6907
+ for (const environment of [...blueprint.app, ...blueprintToFaces(blueprint).filter((extension) => extension.axes.includes("app")).map((extension) => extension.name)]) {
5857
6908
  source.push(` - [\`app/${environment}\`](../app/${environment})`);
5858
6909
  tests.push(` - [\`tests/app/${environment}\`](../tests/app/${environment})`);
5859
6910
  directories.push(`- [\`app/${environment}\`](../app/${environment})\n - Guide: Not created. Create this file when the workspace has a public surface:\n \`${guide}\`\n - Tests: [\`tests/app/${environment}\`](../tests/app/${environment})`);
@@ -5865,7 +6916,7 @@ function blueprintToGuideArtifacts(blueprint) {
5865
6916
  origin: "template",
5866
6917
  content: (0, _orkestrel_template.fillTemplate)(ARTIFACT_TEMPLATES.guides.readme, {
5867
6918
  source: source.join("\n"),
5868
- tests: tests.join("\n"),
6919
+ tests: tests.join("\n") + (blueprint.showcase && blueprint.app.includes("browser") ? "\n - Showcase:\n" + ["browser", ...blueprintToFaces(blueprint).filter((extension) => extension.axes.includes("app")).map((extension) => extension.name)].map((application) => ` - [\`showcase/${application}.html\`](../showcase/${application}.html)`).join("\n") : ""),
5869
6920
  directories: directories.join("\n"),
5870
6921
  guide: blueprint.name
5871
6922
  })
@@ -5904,7 +6955,7 @@ function blueprintToGuideArtifacts(blueprint) {
5904
6955
  * ```
5905
6956
  */
5906
6957
  function blueprintToDocumentArtifacts(blueprint) {
5907
- const publishes = blueprint.src.length > 0;
6958
+ const publishes = blueprint.src.length > 0 || blueprintToSheets(blueprint).length > 0 || blueprint.themes;
5908
6959
  const name = publishes ? `@orkestrel/${blueprint.name}` : blueprint.name;
5909
6960
  const description = blueprint.description ?? (publishes ? `The ${name} package.` : `The ${name} application.`);
5910
6961
  return [{
@@ -6696,7 +7747,8 @@ function dependenciesToQuestions(dependencies, field, name, range) {
6696
7747
  * These do not, because each describes a workspace it can describe honestly but
6697
7748
  * will not create: a published axis of several environments without core,
6698
7749
  * whose manifest names a core build the workspace never runs; a showcase flag
6699
- * whose required browser axis is absent, which emits nothing; and an
7750
+ * or journey flag whose required browser axis is absent, which emits nothing; an unplaced browser
7751
+ * extension or a styles extension without the styles surface; and an
6700
7752
  * integration flag over fewer than two environments, whose seed does emit and
6701
7753
  * has nothing to compose across. Blocking any of them closed the gate for every
6702
7754
  * verb. The verbs that read an existing workspace need the plan the gate
@@ -6737,7 +7789,7 @@ function blueprintToQuestions(blueprint) {
6737
7789
  message: `${blueprint.engines} is not a supported minimum-Node floor.`,
6738
7790
  blocking: true
6739
7791
  });
6740
- if (blueprint.src.length === 0 && blueprint.app.length === 0) questions.push({
7792
+ if (blueprint.src.length === 0 && blueprint.app.length === 0 && blueprintToSheets(blueprint).length === 0 && !blueprint.themes) questions.push({
6741
7793
  field: "src",
6742
7794
  message: "A workspace declares at least one environment on src or app.",
6743
7795
  blocking: true,
@@ -6763,6 +7815,32 @@ function blueprintToQuestions(blueprint) {
6763
7815
  candidates: ENVIRONMENTS
6764
7816
  });
6765
7817
  }
7818
+ const extensions = /* @__PURE__ */ new Set();
7819
+ for (const extension of blueprint.extensions) {
7820
+ const selection = `${extension.surface}:${extension.name}`;
7821
+ if (extensions.has(selection)) questions.push({
7822
+ field: "extensions",
7823
+ message: `${selection} is declared more than once on extensions.`,
7824
+ blocking: true
7825
+ });
7826
+ extensions.add(selection);
7827
+ if (extension.surface === "browser") {
7828
+ if (extension.axes.length === 0) questions.push({
7829
+ field: "extensions",
7830
+ message: `${selection} occupies no axis.`,
7831
+ blocking: false
7832
+ });
7833
+ for (const axis of extension.axes) if (!blueprint[axis].includes("browser")) questions.push({
7834
+ field: "extensions",
7835
+ message: `${selection} occupies ${axis}, whose selection lacks browser.`,
7836
+ blocking: false
7837
+ });
7838
+ } else if (!blueprint.styles) questions.push({
7839
+ field: "extensions",
7840
+ message: `${selection} extends a styles surface this workspace does not declare.`,
7841
+ blocking: false
7842
+ });
7843
+ }
6766
7844
  if (blueprint.integration && blueprint.src.length + blueprint.app.length < 2) questions.push({
6767
7845
  field: "integration",
6768
7846
  message: "integration drives features across environments, and this workspace declares fewer than two, so its seed composes nothing.",
@@ -6992,6 +8070,9 @@ function createBlueprint(name, input) {
6992
8070
  keywords: input?.keywords ?? [],
6993
8071
  src: input?.src ?? [],
6994
8072
  app: input?.app ?? [],
8073
+ extensions: input?.extensions ?? [],
8074
+ styles: input?.styles ?? false,
8075
+ themes: input?.themes ?? false,
6995
8076
  dependencies: input?.dependencies ?? [],
6996
8077
  peers: input?.peers ?? [],
6997
8078
  extras: input?.extras ?? [],
@@ -7322,6 +8403,7 @@ exports.APP_DEV_DEPENDENCIES = APP_DEV_DEPENDENCIES;
7322
8403
  exports.APP_MATRIX = APP_MATRIX;
7323
8404
  exports.APP_SERVER_DEV_DEPENDENCIES = APP_SERVER_DEV_DEPENDENCIES;
7324
8405
  exports.ARTIFACT_TEMPLATES = ARTIFACT_TEMPLATES;
8406
+ exports.AXES = AXES;
7325
8407
  exports.BASE_DEV_DEPENDENCIES = BASE_DEV_DEPENDENCIES;
7326
8408
  exports.BIN_CONFIGS = BIN_CONFIGS;
7327
8409
  exports.BIN_ENTRY_PATH = BIN_ENTRY_PATH;
@@ -7344,6 +8426,8 @@ exports.EXECUTABLE_PATHS = EXECUTABLE_PATHS;
7344
8426
  exports.EXTRA_RANGE_PATTERN = EXTRA_RANGE_PATTERN;
7345
8427
  exports.FLOOR_RANGE_PATTERN = FLOOR_RANGE_PATTERN;
7346
8428
  exports.FOREIGN_NAME_PATTERN = FOREIGN_NAME_PATTERN;
8429
+ exports.FRAMEWORKS = FRAMEWORKS;
8430
+ exports.FRAMEWORK_MATRIX = FRAMEWORK_MATRIX;
7347
8431
  exports.GLOBAL_SETUP_PATH = GLOBAL_SETUP_PATH;
7348
8432
  exports.GROUPS = GROUPS;
7349
8433
  exports.GUIDES_TEST_PATH = GUIDES_TEST_PATH;
@@ -7376,18 +8460,26 @@ exports.ORKESTREL_RANGE_PATTERN = ORKESTREL_RANGE_PATTERN;
7376
8460
  exports.PRINT_WIDTH = PRINT_WIDTH;
7377
8461
  exports.REFERENCE_PATHS = REFERENCE_PATHS;
7378
8462
  exports.RELEASE_PROOF_COMMAND = RELEASE_PROOF_COMMAND;
8463
+ exports.RESERVED_SHEET_NAMES = RESERVED_SHEET_NAMES;
7379
8464
  exports.SEED_GUIDE_PATHS = SEED_GUIDE_PATHS;
7380
8465
  exports.SERVICE_SCRIPT_PATH = SERVICE_SCRIPT_PATH;
7381
8466
  exports.SERVICE_SETUP_PATH = SERVICE_SETUP_PATH;
7382
8467
  exports.SERVICE_TEST_INCLUDE = SERVICE_TEST_INCLUDE;
8468
+ exports.SHEET_ENTRY_NAME = SHEET_ENTRY_NAME;
7383
8469
  exports.SHOWCASE_CONFIG_PATH = SHOWCASE_CONFIG_PATH;
7384
8470
  exports.SHOWCASE_DEV_DEPENDENCIES = SHOWCASE_DEV_DEPENDENCIES;
8471
+ exports.SHOWCASE_PAGES_PATH = SHOWCASE_PAGES_PATH;
7385
8472
  exports.SKILLS_CONFIG_PATH = SKILLS_CONFIG_PATH;
7386
8473
  exports.SOURCE_BROWSER_DEV_DEPENDENCIES = SOURCE_BROWSER_DEV_DEPENDENCIES;
7387
8474
  exports.SRC_MATRIX = SRC_MATRIX;
8475
+ exports.STYLES_DEV_DEPENDENCIES = STYLES_DEV_DEPENDENCIES;
8476
+ exports.STYLES_ENTRY_PATH = STYLES_ENTRY_PATH;
8477
+ exports.SURFACES = SURFACES;
7388
8478
  exports.ScaffoldError = ScaffoldError;
7389
8479
  exports.TAB_WIDTH = TAB_WIDTH;
7390
8480
  exports.TARGET_SKILL_NAMES = TARGET_SKILL_NAMES;
8481
+ exports.THEMES_BARREL_PATH = THEMES_BARREL_PATH;
8482
+ exports.THEMES_ENTRY_PATH = THEMES_ENTRY_PATH;
7391
8483
  exports.VERSION_PATTERN = VERSION_PATTERN;
7392
8484
  exports.WORKSPACE_DEV_ENGINES = WORKSPACE_DEV_ENGINES;
7393
8485
  exports.WORKSPACE_OWNED_PATHS = WORKSPACE_OWNED_PATHS;
@@ -7398,15 +8490,19 @@ exports.artifactsToQuestions = artifactsToQuestions;
7398
8490
  exports.blueprintToConfigArtifacts = blueprintToConfigArtifacts;
7399
8491
  exports.blueprintToDevDependencies = blueprintToDevDependencies;
7400
8492
  exports.blueprintToDocumentArtifacts = blueprintToDocumentArtifacts;
8493
+ exports.blueprintToExports = blueprintToExports;
8494
+ exports.blueprintToFaces = blueprintToFaces;
7401
8495
  exports.blueprintToGuideArtifacts = blueprintToGuideArtifacts;
7402
8496
  exports.blueprintToHostArtifacts = blueprintToHostArtifacts;
7403
8497
  exports.blueprintToMachinery = blueprintToMachinery;
7404
8498
  exports.blueprintToManifest = blueprintToManifest;
7405
8499
  exports.blueprintToOrchestrationArtifacts = blueprintToOrchestrationArtifacts;
8500
+ exports.blueprintToProjects = blueprintToProjects;
7406
8501
  exports.blueprintToQuestions = blueprintToQuestions;
7407
8502
  exports.blueprintToRootTsconfig = blueprintToRootTsconfig;
7408
8503
  exports.blueprintToRootVite = blueprintToRootVite;
7409
8504
  exports.blueprintToScripts = blueprintToScripts;
8505
+ exports.blueprintToSheets = blueprintToSheets;
7410
8506
  exports.blueprintToSourceArtifacts = blueprintToSourceArtifacts;
7411
8507
  exports.blueprintToTestArtifacts = blueprintToTestArtifacts;
7412
8508
  exports.blueprintToWritableScripts = blueprintToWritableScripts;
@@ -7426,6 +8522,7 @@ exports.inferGroup = inferGroup;
7426
8522
  exports.isArtifact = isArtifact;
7427
8523
  exports.isAudit = isAudit;
7428
8524
  exports.isBlueprint = isBlueprint;
8525
+ exports.isBrowserExtension = isBrowserExtension;
7429
8526
  exports.isCanonPath = isCanonPath;
7430
8527
  exports.isCatalogEntry = isCatalogEntry;
7431
8528
  exports.isCollection = isCollection;
@@ -7436,6 +8533,7 @@ exports.isDeferredPath = isDeferredPath;
7436
8533
  exports.isDependency = isDependency;
7437
8534
  exports.isDependencyName = isDependencyName;
7438
8535
  exports.isEnvironment = isEnvironment;
8536
+ exports.isExtension = isExtension;
7439
8537
  exports.isFinding = isFinding;
7440
8538
  exports.isFloorPath = isFloorPath;
7441
8539
  exports.isGroup = isGroup;
@@ -7449,7 +8547,10 @@ exports.isPlan = isPlan;
7449
8547
  exports.isQuestion = isQuestion;
7450
8548
  exports.isRetainedPath = isRetainedPath;
7451
8549
  exports.isScaffoldError = isScaffoldError;
8550
+ exports.isSheetName = isSheetName;
7452
8551
  exports.isSnapshot = isSnapshot;
8552
+ exports.isStylesExtension = isStylesExtension;
8553
+ exports.isSurface = isSurface;
7453
8554
  exports.manifestToDependencies = manifestToDependencies;
7454
8555
  exports.manifestToName = manifestToName;
7455
8556
  exports.matchesDriftReachability = matchesDriftReachability;
@@ -7461,6 +8562,7 @@ exports.nameToGuide = nameToGuide;
7461
8562
  exports.overridesToQuestions = overridesToQuestions;
7462
8563
  exports.parseBlueprint = parseBlueprint;
7463
8564
  exports.parseCompilerOptions = parseCompilerOptions;
8565
+ exports.parseExtension = parseExtension;
7464
8566
  exports.parseGroups = parseGroups;
7465
8567
  exports.parseSnapshot = parseSnapshot;
7466
8568
  exports.pathToCondition = pathToCondition;