@orkestrel/scaffold 0.0.81 → 0.0.83

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 (47) hide show
  1. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +14 -3
  2. package/dist/bin/main.js +295 -13
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +5 -3
  5. package/dist/host/agents/orchestration.md +1 -0
  6. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +2 -2
  7. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +43 -39
  8. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +3 -3
  9. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +10 -9
  10. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +5 -5
  11. package/dist/host/agents/skills/orkestrel-journey/references/recorded.md +77 -0
  12. package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +3 -3
  13. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +9 -9
  14. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +4 -0
  15. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +22 -4
  16. package/dist/host/claude/agents/orkestrel.md +10 -10
  17. package/dist/host/claude/rules/application.md +20 -6
  18. package/dist/host/claude/rules/architecture.md +2 -2
  19. package/dist/host/claude/rules/browser.md +9 -0
  20. package/dist/host/claude/rules/documentation.md +2 -1
  21. package/dist/host/claude/rules/quality.md +5 -0
  22. package/dist/host/claude/rules/styles.md +35 -12
  23. package/dist/host/claude/rules/tests.md +15 -10
  24. package/dist/host/claude/rules/workspace.md +128 -98
  25. package/dist/host/claude/skills/orkestrel-journey/SKILL.md +1 -1
  26. package/dist/host/configs/helpers.ts +157 -9
  27. package/dist/host/configs/policy.ts +64 -61
  28. package/dist/host/dotfiles/oxlintrc.json +132 -16
  29. package/dist/host/dotfiles/prettierignore +1 -1
  30. package/dist/host/guides/README.md +9 -5
  31. package/dist/host/guides/guide.md +4 -1
  32. package/dist/host/guides/scaffold.md +482 -115
  33. package/dist/host/manifest.json +38 -32
  34. package/dist/host/scripts/codex.sh +0 -0
  35. package/dist/host/scripts/cursor.sh +0 -0
  36. package/dist/host/scripts/deps.sh +0 -0
  37. package/dist/host/scripts/ollama.sh +0 -0
  38. package/dist/host/tests/config.test.ts +996 -39
  39. package/dist/host/tests/policy.test.ts +11 -0
  40. package/dist/host/tests/setupPolicy.ts +396 -22
  41. package/dist/src/core/index.cjs +1289 -164
  42. package/dist/src/core/index.cjs.map +1 -1
  43. package/dist/src/core/index.d.cts +313 -35
  44. package/dist/src/core/index.d.ts +313 -35
  45. package/dist/src/core/index.js +1268 -165
  46. package/dist/src/core/index.js.map +1 -1
  47. package/package.json +7 -6
@@ -3,7 +3,7 @@ import { fillTemplate } from "@orkestrel/template";
3
3
  import { Emitter } from "@orkestrel/emitter";
4
4
  var package_default = {
5
5
  name: "@orkestrel/scaffold",
6
- version: "0.0.81",
6
+ version: "0.0.83",
7
7
  description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
8
8
  keywords: [
9
9
  "audit",
@@ -94,16 +94,17 @@ var package_default = {
94
94
  },
95
95
  dependencies: {
96
96
  "@orkestrel/console": "^0.0.15",
97
- "@orkestrel/contract": "^0.0.18",
97
+ "@orkestrel/contract": "^0.0.19",
98
98
  "@orkestrel/emitter": "^0.0.11",
99
- "@orkestrel/markdown": "^0.0.16",
99
+ "@orkestrel/markdown": "^0.0.17",
100
100
  "@orkestrel/process": "^0.0.14",
101
101
  "@orkestrel/template": "^0.0.9"
102
102
  },
103
103
  devDependencies: {
104
104
  "@microsoft/api-extractor": "^7.59.3",
105
- "@orkestrel/guide": "^0.0.21",
106
- "@orkestrel/html": "^0.0.11",
105
+ "@orkestrel/browser": "^0.0.20",
106
+ "@orkestrel/guide": "^0.0.22",
107
+ "@orkestrel/html": "^0.0.12",
107
108
  "@orkestrel/probe": "^0.0.19",
108
109
  "@orkestrel/test": "^0.0.24",
109
110
  "@types/node": "^26.6.3",
@@ -112,7 +113,7 @@ var package_default = {
112
113
  "oxlint": "^1.86.0",
113
114
  "playwright": "^1.63.0",
114
115
  "typescript": "^6.0.3",
115
- "vite": "^8.3.1",
116
+ "vite": "^8.3.2",
116
117
  "vitest": "^4.1.11"
117
118
  },
118
119
  engines: { "node": ">=22.18.0" }
@@ -133,6 +134,44 @@ var ENVIRONMENTS = Object.freeze([
133
134
  "browser",
134
135
  "server"
135
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
+ ]);
136
175
  /**
137
176
  * Lists the `Group` values in plan order, frozen.
138
177
  *
@@ -623,15 +662,21 @@ var SOURCE_BROWSER_DEV_DEPENDENCIES = Object.freeze({
623
662
  "@vitest/browser-playwright": package_default.devDependencies["@vitest/browser-playwright"],
624
663
  playwright: package_default.devDependencies.playwright
625
664
  });
665
+ /**
666
+ * Names the development dependency a browser or styles surface adds.
667
+ *
668
+ * @remarks
669
+ * The package ships the `browse` binary, an MCP server that drives a rendered page and records
670
+ * journeys, and `.claude/rules/quality.md` § Instruments names its registration.
671
+ * `blueprintToDevDependencies` names the blueprints that receive it.
672
+ */
673
+ var BROWSE_DEV_DEPENDENCIES = Object.freeze({ "@orkestrel/browser": package_default.devDependencies["@orkestrel/browser"] });
626
674
  /** Names the development dependency every private `app` environment adds. */
627
675
  var APP_DEV_DEPENDENCIES = Object.freeze({ "@orkestrel/contract": package_default.dependencies["@orkestrel/contract"] });
628
- /** Lists the development dependencies a private Vue browser application adds. */
676
+ /** Lists the development dependencies a private browser application adds. */
629
677
  var APP_BROWSER_DEV_DEPENDENCIES = Object.freeze({
630
678
  ...SOURCE_BROWSER_DEV_DEPENDENCIES,
631
- "@orkestrel/html": package_default.devDependencies["@orkestrel/html"],
632
- "@vitejs/plugin-vue": "^6.0.8",
633
- vue: "^3.5.40",
634
- "vue-tsc": "^3.3.7"
679
+ "@orkestrel/html": package_default.devDependencies["@orkestrel/html"]
635
680
  });
636
681
  /**
637
682
  * Names the development dependency used only by the optional single-file showcase build.
@@ -695,9 +740,7 @@ var CONFIG_TEMPLATES = Object.freeze({
695
740
  "forceConsistentCasingInFileNames": true,
696
741
  "skipLibCheck": true,
697
742
  "noEmit": true,
698
- "paths": {
699
743
  {{paths}}
700
- }
701
744
  },
702
745
  "exclude": ["node_modules", "dist", "tmp"]
703
746
  }
@@ -814,6 +857,27 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
814
857
  `
815
858
  }),
816
859
  factories: Object.freeze({
860
+ sheet: `export function sheetProject(name: string, override?: UserConfig): UserConfig {
861
+ const project: UserConfig = {
862
+ resolve,
863
+ optimizeDeps,
864
+ publicDir: false,
865
+ test: {
866
+ name: { label: name, color: 'magenta' },
867
+ root: resolveWorkspacePath('.'),
868
+ isolate: false,
869
+ setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts', './tests/setupStyles.ts'],
870
+ browser: {
871
+ enabled: true,
872
+ provider: playwright(browserOptions),
873
+ instances: [{ browser: 'chromium', headless: true }],
874
+ },
875
+ fileParallelism: false,
876
+ },
877
+ }
878
+ return mergeOverride(project, override)
879
+ }
880
+ `,
817
881
  src: Object.freeze({
818
882
  core: `export function srcCore(override?: UserConfig): UserConfig {
819
883
  const project: UserConfig = {
@@ -836,9 +900,51 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
836
900
  return mergeOverride(project, override)
837
901
  }
838
902
  `,
839
- browser: `export function srcBrowser(override?: UserConfig): UserConfig {
903
+ vue: `function resolveVueFilename(): string {
904
+ return 'index.js'
905
+ }
906
+
907
+ export function srcVue(override?: UserConfig): UserConfig {
840
908
  const project: UserConfig = {
841
909
  resolve,
910
+ optimizeDeps: { include: [...optimizeDeps.include, 'vue'] },
911
+ publicDir: false,
912
+ plugins: [outputBoundary('dist/src/vue'), environmentBoundary('src/vue'), vue()],
913
+ build: {
914
+ emptyOutDir: true,
915
+ sourcemap: true,
916
+ minify: false,
917
+ lib: {
918
+ entry: resolveWorkspacePath('src/vue/index.ts'),
919
+ formats: ['es'],
920
+ fileName: resolveVueFilename,
921
+ },
922
+ outDir: 'dist/src/vue',
923
+ rolldownOptions: { onLog: enforceBuildLog },
924
+ },
925
+ test: {
926
+ name: { label: 'src:vue', color: 'green' },
927
+ include: ['tests/src/vue/**/*.test.ts'],
928
+ setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
929
+ browser: {
930
+ enabled: true,
931
+ provider: playwright(browserOptions),
932
+ instances: [{ browser: 'chromium', headless: true }],
933
+ },
934
+ fileParallelism: false,
935
+ },
936
+ }
937
+ return mergeOverride(project, override)
938
+ }
939
+ `,
940
+ browser: `function resolveBrowserFilename(): string {
941
+ return 'index.js'
942
+ }
943
+
944
+ export function srcBrowser(override?: UserConfig): UserConfig {
945
+ const project: UserConfig = {
946
+ resolve,
947
+ optimizeDeps,
842
948
  publicDir: false,
843
949
  plugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],
844
950
  build: {
@@ -848,7 +954,7 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
848
954
  lib: {
849
955
  entry: resolveWorkspacePath('src/browser/index.ts'),
850
956
  formats: ['es'],
851
- fileName: () => 'index.js',
957
+ fileName: resolveBrowserFilename,
852
958
  },
853
959
  outDir: 'dist/src/browser',
854
960
  rolldownOptions: {
@@ -873,7 +979,11 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
873
979
  return mergeOverride(project, override)
874
980
  }
875
981
  `,
876
- server: `export function srcServer(override?: UserConfig): UserConfig {
982
+ server: `function resolveServerFilename(format: string): string {
983
+ return format === 'es' ? 'index.js' : 'index.cjs'
984
+ }
985
+
986
+ export function srcServer(override?: UserConfig): UserConfig {
877
987
  const project: UserConfig = {
878
988
  resolve,
879
989
  publicDir: false,
@@ -885,7 +995,7 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
885
995
  lib: {
886
996
  entry: resolveWorkspacePath('src/server/index.ts'),
887
997
  formats: ['es', 'cjs'],
888
- fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
998
+ fileName: resolveServerFilename,
889
999
  },
890
1000
  outDir: 'dist/src/server',
891
1001
  target: 'node22',
@@ -907,7 +1017,15 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
907
1017
  return mergeOverride(project, override)
908
1018
  }
909
1019
  `,
910
- bin: `export function srcBin(override?: UserConfig): UserConfig {
1020
+ bin: `function resolveBinFilename(): string {
1021
+ return 'main.js'
1022
+ }
1023
+
1024
+ function resolveBinExternal(id: string): boolean {
1025
+ return id.startsWith('@src/') || resolveExternal(id, { peers, refused: [], siblings: [] })
1026
+ }
1027
+
1028
+ export function srcBin(override?: UserConfig): UserConfig {
911
1029
  const project: UserConfig = {
912
1030
  resolve,
913
1031
  publicDir: false,
@@ -919,17 +1037,13 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
919
1037
  lib: {
920
1038
  entry: resolveWorkspacePath('${BIN_ENTRY_PATH}'),
921
1039
  formats: ['es'],
922
- fileName: () => 'main.js',
1040
+ fileName: resolveBinFilename,
923
1041
  },
924
1042
  outDir: 'dist/bin',
925
1043
  target: 'node22',
926
1044
  rolldownOptions: {
927
1045
  onLog: enforceBuildLog,
928
- external: (id: string) =>
929
- id.startsWith('node:') ||
930
- id.startsWith('@orkestrel/') ||
931
- id.startsWith('@src/') ||
932
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),
1046
+ external: resolveBinExternal,
933
1047
  },
934
1048
  },
935
1049
  test: {
@@ -969,7 +1083,8 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
969
1083
  const output = 'dist/app/browser'
970
1084
  const project: UserConfig = {
971
1085
  resolve,
972
- plugins: [outputBoundary(output), environmentBoundary('app/browser'), vue()],
1086
+ optimizeDeps,
1087
+ plugins: [outputBoundary(output), environmentBoundary('app/browser')],
973
1088
  root: resolveWorkspacePath('app/browser'),
974
1089
  publicDir: false,
975
1090
  build: {
@@ -998,20 +1113,47 @@ function isNamedPlugin(plugin: PluginOption): plugin is { name: string } {
998
1113
  return mergeOverride(project, override)
999
1114
  }
1000
1115
  {{showcaseFactory}}{{journeyFactory}}`,
1116
+ vue: `export function appVue(override?: UserConfig): UserConfig {
1117
+ const output = 'dist/app/vue'
1118
+ const browser = appBrowser()
1119
+ const project: UserConfig = {
1120
+ ...browser,
1121
+ optimizeDeps: { include: [...optimizeDeps.include, 'vue'] },
1122
+ plugins: [outputBoundary(output), environmentBoundary('app/vue'), vue()],
1123
+ root: resolveWorkspacePath('app/vue'),
1124
+ build: {
1125
+ ...browser.build,
1126
+ outDir: resolveWorkspacePath(output),
1127
+ rolldownOptions: {
1128
+ ...browser.build?.rolldownOptions,
1129
+ input: resolveWorkspacePath('app/vue/index.html'),
1130
+ },
1131
+ },
1132
+ test: {
1133
+ ...browser.test,
1134
+ name: { label: 'app:vue', color: 'magenta' },
1135
+ include: ['tests/app/vue/**/*.test.ts'],
1136
+ {{journeyExclude}} },
1137
+ }
1138
+ return mergeOverride(project, override)
1139
+ }
1140
+ `,
1001
1141
  journey: `
1002
1142
  // Replace the journey fields directly: merging would concatenate the ordinary include
1003
1143
  // and retain the exclusion of the journey suite.
1004
1144
  export function appJourney(
1005
1145
  variant: JourneyVariant,
1006
1146
  variants: readonly JourneyVariant[],
1147
+ mode?: string,
1007
1148
  ): UserConfig {
1008
- const browser = appBrowser()
1149
+ const application = resolveApplication(mode, applications)
1150
+ const browser = applications[application]()
1009
1151
  return {
1010
1152
  ...browser,
1011
1153
  test: {
1012
1154
  ...browser.test,
1013
1155
  name: { label: \`journey:\${variant.name}\`, color: 'green' },
1014
- include: ['tests/app/browser/integration.test.ts'],
1156
+ include: [\`tests/app/\${application}/integration.test.ts\`],
1015
1157
  exclude: [],
1016
1158
  provide: { variant: variant.name, variants, capture },
1017
1159
  browser: {
@@ -1024,8 +1166,39 @@ export function appJourney(
1024
1166
  }
1025
1167
  `,
1026
1168
  showcase: `
1027
- export function appShowcase(override?: UserConfig): UserConfig {
1028
- const output = 'dist/showcase'
1169
+ const showcaseStamp: PluginOption = {
1170
+ name: 'orkestrel-showcase-html',
1171
+ enforce: 'post',
1172
+ generateBundle(_, bundle) {
1173
+ for (const file of Object.values(bundle)) {
1174
+ if (
1175
+ file.type === 'asset' &&
1176
+ typeof file.source === 'string' &&
1177
+ file.fileName.endsWith('.html')
1178
+ ) {
1179
+ file.source = stampPage(file.source)
1180
+ }
1181
+ }
1182
+ },
1183
+ }
1184
+
1185
+ function createShowcaseNaming(application: string): PluginOption {
1186
+ return {
1187
+ name: 'orkestrel-showcase-name',
1188
+ enforce: 'post',
1189
+ writeBundle(options) {
1190
+ if (options.dir === undefined) throw new Error('The showcase output is absent.')
1191
+ renameSync(
1192
+ resolvePath(options.dir, 'index.html'),
1193
+ resolvePath(options.dir, application + '.html'),
1194
+ )
1195
+ },
1196
+ }
1197
+ }
1198
+
1199
+ export function appShowcase(mode: string, override?: UserConfig): UserConfig {
1200
+ const application = resolveApplication(mode, applications)
1201
+ const output = 'showcase'
1029
1202
  const showcase: UserConfig = {
1030
1203
  plugins: [
1031
1204
  outputBoundary(output),
@@ -1033,19 +1206,8 @@ export function appShowcase(override?: UserConfig): UserConfig {
1033
1206
  removeViteModuleLoader: true,
1034
1207
  useRecommendedBuildConfig: true,
1035
1208
  }),
1036
- {
1037
- name: 'orkestrel-showcase-html',
1038
- transformIndexHtml: {
1039
- order: 'post',
1040
- handler(html) {
1041
- const stamp = new Date().toISOString()
1042
- return html.replace(
1043
- '</head>',
1044
- '\t\t<meta name="build-id" content="' + stamp + '" />\\n\t</head>',
1045
- )
1046
- },
1047
- },
1048
- },
1209
+ showcaseStamp,
1210
+ createShowcaseNaming(application),
1049
1211
  ],
1050
1212
  build: {
1051
1213
  // The \`appBrowser\` factory sets \`assetsInlineLimit\` to 0, so every asset becomes
@@ -1054,6 +1216,7 @@ export function appShowcase(override?: UserConfig): UserConfig {
1054
1216
  // true, so this line takes effect only in a workspace that turns that option off.
1055
1217
  assetsInlineLimit: 4096,
1056
1218
  cssMinify: 'lightningcss',
1219
+ emptyOutDir: false,
1057
1220
  minify: 'oxc',
1058
1221
  modulePreload: false,
1059
1222
  outDir: resolveWorkspacePath(output),
@@ -1062,10 +1225,18 @@ export function appShowcase(override?: UserConfig): UserConfig {
1062
1225
  target: 'esnext',
1063
1226
  },
1064
1227
  }
1065
- return appBrowser(mergeOverride(showcase, override))
1228
+ return applications[application](mergeOverride(showcase, override))
1066
1229
  }
1067
1230
  `,
1068
- server: `export function appServer(override?: UserConfig): UserConfig {
1231
+ server: `function resolveAppFilename(): string {
1232
+ return 'main.cjs'
1233
+ }
1234
+
1235
+ function resolveAppExternal(id: string): boolean {
1236
+ return id.startsWith('node:')
1237
+ }
1238
+
1239
+ export function appServer(override?: UserConfig): UserConfig {
1069
1240
  const project: UserConfig = {
1070
1241
  resolve,
1071
1242
  publicDir: false,
@@ -1075,13 +1246,13 @@ export function appShowcase(override?: UserConfig): UserConfig {
1075
1246
  lib: {
1076
1247
  entry: resolveWorkspacePath('app/server/main.ts'),
1077
1248
  formats: ['cjs'],
1078
- fileName: () => 'main.cjs',
1249
+ fileName: resolveAppFilename,
1079
1250
  },
1080
1251
  outDir: resolveWorkspacePath('dist/app/server'),
1081
1252
  target: 'node22',
1082
1253
  rolldownOptions: {
1083
1254
  onLog: enforceBuildLog,
1084
- external: (id: string) => id.startsWith('node:'),
1255
+ external: resolveAppExternal,
1085
1256
  },
1086
1257
  },
1087
1258
  test: {
@@ -1134,8 +1305,10 @@ export function appShowcase(override?: UserConfig): UserConfig {
1134
1305
  test: {
1135
1306
  name: { label: 'setup', color: 'white' },
1136
1307
  include: ['tests/setup*.test.ts'],
1137
- exclude: ['tests/setupBrowser.test.ts'],
1308
+ exclude: ['tests/setupBrowser.test.ts', 'tests/setupStyles.test.ts'],
1138
1309
  setupFiles: ['./tests/setup.ts'],
1310
+ pool: 'threads',
1311
+ isolate: false,
1139
1312
  environment: 'node',
1140
1313
  browser: { enabled: false },
1141
1314
  },
@@ -1146,9 +1319,10 @@ export function appShowcase(override?: UserConfig): UserConfig {
1146
1319
  browser: `export function setupBrowser(override?: UserConfig): UserConfig {
1147
1320
  const project: UserConfig = {
1148
1321
  resolve,
1322
+ {{optimization}},
1149
1323
  {{plugins}} test: {
1150
1324
  name: { label: 'setup:browser', color: 'blue' },
1151
- include: ['tests/setupBrowser.test.ts'],
1325
+ include: ['tests/setupBrowser.test.ts', 'tests/setupStyles.test.ts'],
1152
1326
  setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
1153
1327
  {{global}} browser: {
1154
1328
  enabled: true,
@@ -1202,7 +1376,9 @@ export function conformance(override?: UserConfig): UserConfig {
1202
1376
  test: {
1203
1377
  name: { label: 'conformance', color: 'magenta' },
1204
1378
  include: ['${CONFORMANCE_TEST_PATH}'],
1205
- setupFiles: ['./tests/setup.ts'],
1379
+ setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
1380
+ pool: 'threads',
1381
+ isolate: false,
1206
1382
  environment: 'node',
1207
1383
  browser: { enabled: false },
1208
1384
  },
@@ -1268,7 +1444,17 @@ export function probe(override?: UserConfig): UserConfig {
1268
1444
  return mergeOverride(project, override)
1269
1445
  }
1270
1446
  `,
1271
- integration: `export function integration(override?: UserConfig): UserConfig {
1447
+ integration: Object.freeze({
1448
+ sheet: `export function integration(override?: UserConfig): UserConfig {
1449
+ const project = sheetProject('integration', {
1450
+ test: {
1451
+ include: ['tests/integration.test.ts'],
1452
+ {{global}} },
1453
+ })
1454
+ return mergeOverride(project, override)
1455
+ }
1456
+ `,
1457
+ module: `export function integration(override?: UserConfig): UserConfig {
1272
1458
  const project: UserConfig = {
1273
1459
  resolve,
1274
1460
  test: {
@@ -1281,8 +1467,51 @@ export function probe(override?: UserConfig): UserConfig {
1281
1467
  return mergeOverride(project, override)
1282
1468
  }
1283
1469
  `
1470
+ })
1284
1471
  }),
1285
1472
  tsconfigs: Object.freeze({
1473
+ vue: Object.freeze({
1474
+ src: `{
1475
+ "extends": "../../tsconfig.json",
1476
+ "compilerOptions": {
1477
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
1478
+ "types": ["vite/client"],
1479
+ "noEmit": false,
1480
+ "declaration": true,
1481
+ "emitDeclarationOnly": true,
1482
+ "rootDir": "../../src",
1483
+ "outDir": "../../dist/src"
1484
+ },
1485
+ "include": [
1486
+ "../../src/vue/**/*.cts",
1487
+ "../../src/vue/**/*.mts",
1488
+ "../../src/vue/**/*.ts",
1489
+ "../../src/vue/**/*.tsx"
1490
+ ]
1491
+ }
1492
+ `,
1493
+ app: `{
1494
+ "extends": "../../tsconfig.json",
1495
+ "compilerOptions": {
1496
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
1497
+ "types": ["vite/client", "vue"]
1498
+ },
1499
+ "include": [
1500
+ {{include}}
1501
+ ]
1502
+ }
1503
+ `
1504
+ }),
1505
+ sheet: `{
1506
+ "extends": "../../tsconfig.json",
1507
+ "compilerOptions": {
1508
+ "lib": ["ESNext"],
1509
+ "types": ["vite/client"],
1510
+ "noEmit": true
1511
+ },
1512
+ "include": ["../../src/{{name}}/**/*.ts"]
1513
+ }
1514
+ `,
1286
1515
  src: Object.freeze({
1287
1516
  core: `{
1288
1517
  "extends": "../../tsconfig.json",
@@ -1366,7 +1595,7 @@ export function probe(override?: UserConfig): UserConfig {
1366
1595
  "extends": "../../tsconfig.json",
1367
1596
  "compilerOptions": {
1368
1597
  "lib": ["ESNext", "DOM", "DOM.Iterable"],
1369
- "types": ["vite/client", "vue"]
1598
+ "types": ["vite/client"]
1370
1599
  },
1371
1600
  "include": [
1372
1601
  {{include}}
@@ -1416,9 +1645,38 @@ export function probe(override?: UserConfig): UserConfig {
1416
1645
  `
1417
1646
  }),
1418
1647
  vites: Object.freeze({
1648
+ sheet: `import { defineConfig } from 'vitest/config'
1649
+ import { outputBoundary } from '../helpers.js'
1650
+ import { resolveWorkspacePath, sheetProject } from '../../vite.config.ts'
1651
+
1652
+ export default defineConfig((invocation) => {
1653
+ const project = sheetProject('src:{{label}}', {
1654
+ plugins: [outputBoundary('dist/src/{{name}}')],
1655
+ build: {
1656
+ outDir: 'dist/src/{{name}}',
1657
+ emptyOutDir: true,
1658
+ // Preserve the authored layer-order statement in the published sheet.
1659
+ cssMinify: false,
1660
+ lib: {
1661
+ entry: resolveWorkspacePath('src/{{name}}/sheet.ts'),
1662
+ formats: ['es'],
1663
+ fileName: 'index',
1664
+ cssFileName: 'index',
1665
+ },
1666
+ },
1667
+ test: { include: ['tests/src/{{name}}/**/*.test.ts'] },
1668
+ })
1669
+ return { ...project, mode: invocation.mode }
1670
+ })
1671
+ `,
1419
1672
  src: Object.freeze({
1420
1673
  core: `import { defineConfig } from 'vite'
1421
- import { declarationRollup, environmentBoundary, outputBoundary } from '../helpers.js'
1674
+ import {
1675
+ declarationRollup,
1676
+ environmentBoundary,
1677
+ outputBoundary,
1678
+ resolveExternal,
1679
+ } from '../helpers.js'
1422
1680
  import { peers, srcCore, resolveWorkspacePath } from '../../vite.config.ts'
1423
1681
 
1424
1682
  export default defineConfig(
@@ -1439,11 +1697,43 @@ export default defineConfig(
1439
1697
  fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
1440
1698
  },
1441
1699
  outDir: 'dist/src/core',
1700
+ rolldownOptions: {
1701
+ external: (id: string) => resolveExternal(id, { peers, refused: [], siblings: [] }),
1702
+ },
1703
+ },
1704
+ }),
1705
+ )
1706
+ `,
1707
+ vue: `import { defineConfig } from 'vite'
1708
+ import {
1709
+ declarationRollup,
1710
+ resolveExternal,
1711
+ rewriteBrowserSpecifier,
1712
+ rewriteCoreSpecifier,
1713
+ } from '../helpers.js'
1714
+ import { peers, srcVue, resolveWorkspacePath } from '../../vite.config.ts'
1715
+
1716
+ const siblings = {
1717
+ {{siblings}}
1718
+ }
1719
+
1720
+ export default defineConfig(
1721
+ srcVue({
1722
+ plugins: [
1723
+ declarationRollup({
1724
+ project: resolveWorkspacePath('configs/src/tsconfig.vue.json'),
1725
+ rewrite: (content) => rewriteBrowserSpecifier(rewriteCoreSpecifier(content)),
1726
+ }),
1727
+ ],
1728
+ build: {
1442
1729
  rolldownOptions: {
1443
1730
  external: (id: string) =>
1444
- id.startsWith('node:') ||
1445
- id.startsWith('@orkestrel/') ||
1446
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),
1731
+ resolveExternal(id, {
1732
+ peers,
1733
+ refused: {{refused}},
1734
+ siblings: Object.keys(siblings),
1735
+ }),
1736
+ output: { paths: siblings },
1447
1737
  },
1448
1738
  },
1449
1739
  }),
@@ -1485,7 +1775,8 @@ export default defineConfig(
1485
1775
  `
1486
1776
  }),
1487
1777
  bin: `import { defineConfig } from 'vite'
1488
- import { srcBin } from '../../vite.config.ts'
1778
+ import { resolveExternal } from '../helpers.js'
1779
+ import { peers, srcBin } from '../../vite.config.ts'
1489
1780
 
1490
1781
  // The \`scaffold\` executable build — a single ESM lib file, no declarations (an
1491
1782
  // executable ships no types), with the \`#!/usr/bin/env node\` shebang re-emitted through
@@ -1496,6 +1787,8 @@ export default defineConfig(
1496
1787
  srcBin({
1497
1788
  build: {
1498
1789
  rolldownOptions: {
1790
+ external: (id: string) =>
1791
+ id.startsWith('@src/') || resolveExternal(id, { peers, refused: [], siblings: [] }),
1499
1792
  output: {
1500
1793
  banner: '#!/usr/bin/env node',
1501
1794
  {{paths}}
@@ -1506,6 +1799,11 @@ export default defineConfig(
1506
1799
  )
1507
1800
  `,
1508
1801
  app: Object.freeze({
1802
+ vue: `import { defineConfig } from 'vite'
1803
+ import { appVue } from '../../vite.config.ts'
1804
+
1805
+ export default defineConfig(appVue())
1806
+ `,
1509
1807
  browser: `import { defineConfig } from 'vite'
1510
1808
  import { appBrowser } from '../../vite.config.ts'
1511
1809
 
@@ -1519,7 +1817,7 @@ export default defineConfig(appServer())
1519
1817
  showcase: `import { defineConfig } from 'vite'
1520
1818
  import { appShowcase } from '../../vite.config.ts'
1521
1819
 
1522
- export default defineConfig(appShowcase())
1820
+ export default defineConfig(({ mode }) => appShowcase(mode))
1523
1821
  `,
1524
1822
  journey: `import type { JourneyVariant } from '@orkestrel/test'
1525
1823
  import { defineConfig } from 'vitest/config'
@@ -1531,11 +1829,11 @@ const VARIANTS: readonly JourneyVariant[] = Object.freeze([
1531
1829
  { name: 'compact', width: 390, height: 844 },
1532
1830
  ])
1533
1831
 
1534
- export default defineConfig({
1832
+ export default defineConfig(({ mode }) => ({
1535
1833
  test: {
1536
- projects: VARIANTS.map((variant) => () => appJourney(variant, VARIANTS)),
1834
+ projects: VARIANTS.map((variant) => () => appJourney(variant, VARIANTS, mode)),
1537
1835
  },
1538
- })
1836
+ }))
1539
1837
  `
1540
1838
  })
1541
1839
  }),
@@ -1878,7 +2176,38 @@ export function resolveBrowser(
1878
2176
  */
1879
2177
  var ARTIFACT_TEMPLATES = Object.freeze({
1880
2178
  source: Object.freeze({
2179
+ sheet: Object.freeze({
2180
+ "index.scss": "@use 'tokens';\n@use 'elements';\n@use 'components';\n@use 'utilities';\n",
2181
+ "_tokens.scss": "@layer theme, reset, base, elements, components, utilities;\n",
2182
+ "_mixins.scss": "",
2183
+ "elements/_index.scss": "",
2184
+ "components/_index.scss": "",
2185
+ "utilities/_index.scss": "",
2186
+ "sheet.ts": "import './index.scss'\n",
2187
+ "index.ts": "export * from './sheet.js'\n"
2188
+ }),
2189
+ themes: Object.freeze({
2190
+ "index.scss": "@use '../tokens';\n@use 'default';\n",
2191
+ "_default.scss": "",
2192
+ "sheet.ts": "import './index.scss'\n"
2193
+ }),
1881
2194
  empty: "",
2195
+ barrel: "// TODO: [Feature] Export the browser extension API.\n",
2196
+ main: `document.body
2197
+ .appendChild(document.createElement('main'))
2198
+ .appendChild(document.createElement('h1')).textContent ={{heading}}
2199
+ `,
2200
+ vue: Object.freeze({
2201
+ "main.ts": `import { createApp } from 'vue'
2202
+ import App from './App.vue'
2203
+
2204
+ createApp(App).mount(document.body.appendChild(document.createElement('main')))
2205
+ `,
2206
+ "App.vue": `<template>
2207
+ <h1>{{heading}}</h1>
2208
+ </template>
2209
+ `
2210
+ }),
1882
2211
  browser: `<!doctype html>
1883
2212
  <html lang="en">
1884
2213
  <head>
@@ -1893,9 +2222,263 @@ var ARTIFACT_TEMPLATES = Object.freeze({
1893
2222
  `
1894
2223
  }),
1895
2224
  tests: Object.freeze({
2225
+ journey: `import {
2226
+ ACCESSIBLE_ROLES,
2227
+ createPortfolio,
2228
+ expandCaptures,
2229
+ isRendered,
2230
+ readRefusal,
2231
+ readStyle,
2232
+ } from '@orkestrel/test/browser'
2233
+ {{imports}}import { page } from 'vitest/browser'
2234
+ import { describe, expect, inject, it } from 'vitest'
2235
+
2236
+ const VARIANT = inject('variant')
2237
+ const VARIANTS = inject('variants')
2238
+ const CAPTURE = inject('capture')
2239
+
2240
+ describe('{{application}} arrival journey', () => {
2241
+ it('proves the declared arrival families', async () => {
2242
+ const families = new Set(['Journey', 'Refusal', 'Matrix', ...(CAPTURE ? ['Capture'] : [])])
2243
+ const proven = new Set<string>()
2244
+ const states = ['{{application}}-arrival']
2245
+ const placed = new Set<string>()
2246
+ const portfolio = createPortfolio({
2247
+ states,
2248
+ variants: VARIANTS,
2249
+ variant: VARIANT,
2250
+ directory: '../../../tmp/captures/states',
2251
+ enabled: CAPTURE,
2252
+ })
2253
+ {{mount}} try {
2254
+ {{arrival}} const heading = page
2255
+ .getByRole('heading', {
2256
+ name: {{heading}},
2257
+ exact: true,
2258
+ level: 1,
2259
+ })
2260
+ .element()
2261
+ expect(isRendered(heading)).toBe(true)
2262
+ proven.add('Journey')
2263
+ await portfolio.place('{{application}}-arrival')
2264
+ placed.add('{{application}}-arrival')
2265
+
2266
+ for (const role of ACCESSIBLE_ROLES) expect(page.getByRole(role).elements()).toEqual([])
2267
+ expect(readRefusal('Continue')).toBe(
2268
+ 'No interactive element has the accessible name "Continue"',
2269
+ )
2270
+ proven.add('Refusal')
2271
+
2272
+ const measured = new Set<string>()
2273
+ for (const variant of VARIANTS) {
2274
+ await page.viewport(variant.width, variant.height)
2275
+ expect(readStyle(heading, 'display')).toBe('block')
2276
+ expect(window.innerWidth).toBe(variant.width)
2277
+ measured.add(variant.name)
2278
+ }
2279
+ expect(measured).toEqual(new Set(VARIANTS.map(({ name }) => name)))
2280
+ proven.add('Matrix')
2281
+
2282
+ const files = expandCaptures(states, VARIANTS)
2283
+ expect(portfolio.files).toEqual(files)
2284
+ expect(new Set(files).size).toBe(states.length * VARIANTS.length)
2285
+ expect(placed).toEqual(new Set(states))
2286
+ const expected = CAPTURE
2287
+ ? expandCaptures(
2288
+ states,
2289
+ VARIANTS.filter(({ name }) => name === VARIANT),
2290
+ )
2291
+ : []
2292
+ expect(portfolio.paths.map((path) => path.replaceAll('\\\\', '/').split('/').at(-1))).toEqual(
2293
+ expected,
2294
+ )
2295
+ if (CAPTURE) proven.add('Capture')
2296
+ expect(proven).toEqual(families)
2297
+ } finally {
2298
+ {{cleanup}} const variant = VARIANTS.find(({ name }) => name === VARIANT)
2299
+ if (variant !== undefined) await page.viewport(variant.width, variant.height)
2300
+ }
2301
+ })
2302
+ })
2303
+ `,
2304
+ browser: `import type { JourneyVariant } from '@orkestrel/test'
2305
+
2306
+ declare module 'vitest' {
2307
+ export interface ProvidedContext {
2308
+ readonly variant: string
2309
+ readonly variants: readonly JourneyVariant[]
2310
+ readonly capture: boolean
2311
+ }
2312
+ }
2313
+ `,
2314
+ sheet: `import sheet from '{{root}}dist/src/{{name}}/index.css?raw'
2315
+ import { describe, expect, it } from 'vitest'
2316
+ import { adoptSheet, readLayerNames } from '{{setup}}'
2317
+
2318
+ describe('{{name}} entry', () => {
2319
+ it('opens with the declared cascade order', () => {
2320
+ const adopted = adoptSheet(sheet)
2321
+ try {
2322
+ expect(adopted.sheet.cssRules[0]?.cssText).toBe(
2323
+ '@layer theme, reset, base, elements, components, utilities;',
2324
+ )
2325
+ expect(readLayerNames(adopted.sheet).slice(0, 6)).toEqual([
2326
+ 'theme',
2327
+ 'reset',
2328
+ 'base',
2329
+ 'elements',
2330
+ 'components',
2331
+ 'utilities',
2332
+ ])
2333
+ } finally {
2334
+ adopted.release()
2335
+ }
2336
+ })
2337
+ })
2338
+ `,
2339
+ styles: Object.freeze({
2340
+ module: `/** Carries a CSSOM rule and its enclosing layer. */
2341
+ export interface SheetEntry {
2342
+ readonly rule: CSSRule
2343
+ readonly layer: string | undefined
2344
+ }
2345
+
2346
+ /** Describes an adopted stylesheet and its idempotent release operation. */
2347
+ export interface SheetAdoption {
2348
+ readonly sheet: CSSStyleSheet
2349
+ release(): void
2350
+ }
2351
+
2352
+ /**
2353
+ * Adopts CSS text into the document with an idempotent release operation.
2354
+ * @param text - The compiled sheet text.
2355
+ * @returns The adopted sheet and its release operation.
2356
+ * @example
2357
+ * \`\`\`ts
2358
+ * const adopted = adoptSheet('.sample { color: red }')
2359
+ * adopted.release()
2360
+ * \`\`\`
2361
+ */
2362
+ export function adoptSheet(text: string): SheetAdoption {
2363
+ const sheet = new CSSStyleSheet()
2364
+ sheet.replaceSync(text)
2365
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]
2366
+ return {
2367
+ sheet,
2368
+ release: () => {
2369
+ document.adoptedStyleSheets = document.adoptedStyleSheets.filter(
2370
+ (candidate) => candidate !== sheet,
2371
+ )
2372
+ },
2373
+ }
2374
+ }
2375
+
2376
+ /**
2377
+ * Reads layer statements and blocks in declaration order, including repeated names.
2378
+ * @param sheet - The parsed sheet.
2379
+ * @returns Qualified layer names, with an empty name for an anonymous layer.
2380
+ * @example
2381
+ * \`\`\`ts
2382
+ * readLayerNames(new CSSStyleSheet()) // []
2383
+ * \`\`\`
2384
+ */
2385
+ export function readLayerNames(sheet: CSSStyleSheet): readonly string[] {
2386
+ const pending: SheetEntry[] = Array.from(sheet.cssRules, (rule) => ({
2387
+ rule,
2388
+ layer: undefined,
2389
+ })).reverse()
2390
+ const names: string[] = []
2391
+ while (pending.length > 0) {
2392
+ const entry = pending.pop()
2393
+ if (entry === undefined) break
2394
+ const { rule, layer } = entry
2395
+ const declared =
2396
+ rule instanceof CSSLayerStatementRule
2397
+ ? rule.nameList
2398
+ : rule instanceof CSSLayerBlockRule
2399
+ ? [rule.name]
2400
+ : []
2401
+ for (const name of declared) names.push(layer === undefined ? name : layer + '.' + name)
2402
+ if (!(rule instanceof CSSGroupingRule)) continue
2403
+ const parent =
2404
+ rule instanceof CSSLayerBlockRule
2405
+ ? layer === undefined
2406
+ ? rule.name
2407
+ : layer + '.' + rule.name
2408
+ : layer
2409
+ for (const child of Array.from(rule.cssRules).reverse()) {
2410
+ pending.push({ rule: child, layer: parent })
2411
+ }
2412
+ }
2413
+ return names
2414
+ }
2415
+ `,
2416
+ proof: `import type { SheetAdoption } from './setupStyles.js'
2417
+ import { describe, expect, it } from 'vitest'
2418
+ import { adoptSheet, readLayerNames } from './setupStyles.js'
2419
+
2420
+ describe('CSSOM instruments', () => {
2421
+ it('adopts and releases only its own sheet, including repeated release', () => {
2422
+ const previous = [...document.adoptedStyleSheets]
2423
+ const first: SheetAdoption = adoptSheet('.sample { color: red }')
2424
+ try {
2425
+ const second = adoptSheet('.other { display: block }')
2426
+ try {
2427
+ expect(document.adoptedStyleSheets).toEqual([...previous, first.sheet, second.sheet])
2428
+ expect(first.sheet.cssRules.length).toBe(1)
2429
+ first.release()
2430
+ first.release()
2431
+ expect(document.adoptedStyleSheets).toEqual([...previous, second.sheet])
2432
+ } finally {
2433
+ second.release()
2434
+ }
2435
+ } finally {
2436
+ first.release()
2437
+ }
2438
+ expect(document.adoptedStyleSheets).toEqual(previous)
2439
+ })
2440
+
2441
+ it('reads statements, blocks, nested conditions, and anonymous layers in order', () => {
2442
+ const adopted = adoptSheet(
2443
+ '@layer first, second; @layer first { @media all { @layer nested {} } } @layer {}',
2444
+ )
2445
+ try {
2446
+ expect(readLayerNames(adopted.sheet)).toEqual([
2447
+ 'first',
2448
+ 'second',
2449
+ 'first',
2450
+ 'first.nested',
2451
+ '',
2452
+ ])
2453
+ } finally {
2454
+ adopted.release()
2455
+ }
2456
+ })
2457
+
2458
+ it('returns no layer names for an empty sheet or an unlayered rule', () => {
2459
+ const sheet = new CSSStyleSheet()
2460
+ expect(readLayerNames(sheet)).toEqual([])
2461
+ sheet.replaceSync('.sample { color: red }')
2462
+ expect(readLayerNames(sheet)).toEqual([])
2463
+ })
2464
+ })
2465
+ `
2466
+ }),
1896
2467
  setup: "",
1897
- global: `export function setup(): void {}
2468
+ global: Object.freeze({
2469
+ module: `export function setup(): void {}
1898
2470
  `,
2471
+ proof: `import { describe, expect, it } from 'vitest'
2472
+ import { setup } from './setupGlobal.js'
2473
+
2474
+ describe('global setup', () => {
2475
+ it('runs without producing a teardown', () => {
2476
+ expect(setup()).toBeUndefined()
2477
+ expect(setup()).toBeUndefined()
2478
+ })
2479
+ })
2480
+ `
2481
+ }),
1899
2482
  entry: `import * as entry from {{specifier}}
1900
2483
  import { describe, expect, it } from 'vitest'
1901
2484
 
@@ -3255,6 +3838,80 @@ function isCollection(value) {
3255
3838
  */
3256
3839
  var isEnvironment = literalOf(ENVIRONMENTS);
3257
3840
  /**
3841
+ * Narrows a value to a surface an extension can extend.
3842
+ * @param value - The candidate surface.
3843
+ * @returns True if the value is a supported surface; false otherwise.
3844
+ * @example
3845
+ * ```ts
3846
+ * import { isSurface } from '@orkestrel/scaffold'
3847
+ *
3848
+ * isSurface('styles') // true
3849
+ * isSurface('themes') // false
3850
+ * ```
3851
+ */
3852
+ var isSurface = literalOf(SURFACES);
3853
+ /**
3854
+ * Narrows a value to a non-reserved sheet face name.
3855
+ * @param value - The candidate name.
3856
+ * @returns True if the name matches the workspace pattern and is not reserved; false otherwise.
3857
+ * @example
3858
+ * ```ts
3859
+ * import { isSheetName } from '@orkestrel/scaffold'
3860
+ *
3861
+ * isSheetName('print') // true
3862
+ * isSheetName('styles') // false
3863
+ * ```
3864
+ */
3865
+ function isSheetName(value) {
3866
+ return isString(value) && value.length <= 203 && NAME_PATTERN.test(value) && !RESERVED_SHEET_NAMES.includes(value);
3867
+ }
3868
+ /**
3869
+ * Narrows a value to a supported browser framework with distinct occupied axes.
3870
+ * @param value - The candidate extension.
3871
+ * @returns True if the value is an admitted extension; false otherwise.
3872
+ * @example
3873
+ * ```ts
3874
+ * import { isBrowserExtension } from '@orkestrel/scaffold'
3875
+ *
3876
+ * isBrowserExtension({ surface: 'browser', name: 'vue', axes: ['app'] }) // true
3877
+ * isBrowserExtension(undefined) // false
3878
+ * ```
3879
+ */
3880
+ var isBrowserExtension = andOf(recordOf({
3881
+ surface: literalOf("browser"),
3882
+ name: literalOf(FRAMEWORKS),
3883
+ axes: andOf(isCollection, arrayOf(literalOf(AXES)))
3884
+ }), (extension) => new Set(extension.axes).size === extension.axes.length);
3885
+ /**
3886
+ * Narrows a value to a named non-reserved stylesheet face.
3887
+ * @param value - The candidate extension.
3888
+ * @returns True if the value is an admitted extension; false otherwise.
3889
+ * @example
3890
+ * ```ts
3891
+ * import { isStylesExtension } from '@orkestrel/scaffold'
3892
+ *
3893
+ * isStylesExtension({ surface: 'styles', name: 'print' }) // true
3894
+ * isStylesExtension(undefined) // false
3895
+ * ```
3896
+ */
3897
+ var isStylesExtension = recordOf({
3898
+ surface: literalOf("styles"),
3899
+ name: isSheetName
3900
+ });
3901
+ /**
3902
+ * Narrows a value to an extension of a supported surface.
3903
+ * @param value - The candidate extension.
3904
+ * @returns True if the value is an admitted extension; false otherwise.
3905
+ * @example
3906
+ * ```ts
3907
+ * import { isExtension } from '@orkestrel/scaffold'
3908
+ *
3909
+ * isExtension({ surface: 'styles', name: 'print' }) // true
3910
+ * isExtension(undefined) // false
3911
+ * ```
3912
+ */
3913
+ var isExtension = unionOf(isBrowserExtension, isStylesExtension);
3914
+ /**
3258
3915
  * Narrows a value to one {@link Group} a plan selects over.
3259
3916
  *
3260
3917
  * @example
@@ -3391,6 +4048,9 @@ var isBlueprint = recordOf({
3391
4048
  keywords: andOf(isCollection, arrayOf(isString)),
3392
4049
  src: andOf(isCollection, arrayOf(isEnvironment)),
3393
4050
  app: andOf(isCollection, arrayOf(isEnvironment)),
4051
+ extensions: andOf(isCollection, arrayOf(isExtension)),
4052
+ styles: isBoolean,
4053
+ themes: isBoolean,
3394
4054
  dependencies: andOf(isCollection, arrayOf(isDependency)),
3395
4055
  peers: andOf(isCollection, arrayOf(isDependency)),
3396
4056
  extras: andOf(isCollection, arrayOf(isDependency)),
@@ -3685,6 +4345,34 @@ function cloneValue(value) {
3685
4345
  //#endregion
3686
4346
  //#region src/core/parsers.ts
3687
4347
  /**
4348
+ * Coerces an extension value or a `surface:name` selection into an extension.
4349
+ * @param value - An extension record or its selection text.
4350
+ * @returns The extension, or `undefined` for a malformed or unsupported selection.
4351
+ * @remarks Browser text has empty axes; the creating command supplies its selected browser axes.
4352
+ * @example
4353
+ * ```ts
4354
+ * import { parseExtension } from '@orkestrel/scaffold'
4355
+ *
4356
+ * parseExtension('browser:vue') // { surface: 'browser', name: 'vue', axes: [] }
4357
+ * parseExtension('styles:print') // { surface: 'styles', name: 'print' }
4358
+ * ```
4359
+ */
4360
+ function parseExtension(value) {
4361
+ if (isExtension(value)) return value;
4362
+ if (!isString(value)) return void 0;
4363
+ const [surface, name, excess] = value.split(":");
4364
+ if (excess !== void 0 || !isSurface(surface)) return void 0;
4365
+ const candidate = surface === "browser" ? {
4366
+ surface,
4367
+ name,
4368
+ axes: []
4369
+ } : {
4370
+ surface,
4371
+ name
4372
+ };
4373
+ return isExtension(candidate) ? candidate : void 0;
4374
+ }
4375
+ /**
3688
4376
  * Coerces an untrusted value to a {@link Blueprint}.
3689
4377
  *
3690
4378
  * @param value - The value to parse.
@@ -4770,6 +5458,67 @@ function renderSkillPointer(canonical, name) {
4770
5458
  //#endregion
4771
5459
  //#region src/core/compilers.ts
4772
5460
  /**
5461
+ * Projects a blueprint into browser extensions restricted to occupied browser axes.
5462
+ * @param blueprint - The workspace specification.
5463
+ * @returns Browser extensions with their occupied axes, omitting unplaced extensions.
5464
+ * @example
5465
+ * ```ts
5466
+ * import { blueprintToFaces, createBlueprint } from '@orkestrel/scaffold'
5467
+ *
5468
+ * blueprintToFaces(createBlueprint('desk', { app: ['core'] })).length // 0
5469
+ * ```
5470
+ */
5471
+ function blueprintToFaces(blueprint) {
5472
+ return blueprint.extensions.flatMap((extension) => {
5473
+ if (extension.surface !== "browser") return [];
5474
+ const axes = extension.axes.filter((axis) => blueprint[axis].includes("browser"));
5475
+ return axes.length === 0 ? [] : [{
5476
+ ...extension,
5477
+ axes
5478
+ }];
5479
+ });
5480
+ }
5481
+ /**
5482
+ * Projects a blueprint into its validated sheet-face names in stable order.
5483
+ * @param blueprint - The workspace specification.
5484
+ * @returns The base face followed by named styles extensions; themes remain a separate target.
5485
+ * @example
5486
+ * ```ts
5487
+ * import { blueprintToSheets, createBlueprint } from '@orkestrel/scaffold'
5488
+ *
5489
+ * blueprintToSheets(createBlueprint('paper', { styles: true })) // ['styles']
5490
+ * ```
5491
+ */
5492
+ function blueprintToSheets(blueprint) {
5493
+ return [...blueprint.styles ? ["styles"] : [], ...sortValues(blueprint.extensions.flatMap((extension) => extension.surface === "styles" && isSheetName(extension.name) ? [extension.name] : []))];
5494
+ }
5495
+ /**
5496
+ * Projects published environments and sheets into the manifest export map.
5497
+ * @param blueprint - The workspace specification.
5498
+ * @returns JavaScript conditions and CSS and SCSS subpaths, without an invented JavaScript root.
5499
+ * @example
5500
+ * ```ts
5501
+ * import { blueprintToExports, createBlueprint } from '@orkestrel/scaffold'
5502
+ *
5503
+ * blueprintToExports(createBlueprint('paper', { styles: true }))['./styles']
5504
+ * // './dist/src/styles/index.css'
5505
+ * ```
5506
+ */
5507
+ function blueprintToExports(blueprint) {
5508
+ const exports = { ...srcToExports(blueprint.src) };
5509
+ for (const extension of blueprintToFaces(blueprint)) {
5510
+ if (extension.surface !== "browser" || !extension.axes.includes("src")) continue;
5511
+ exports[`./${extension.name}`] = pathToCondition(`./dist/src/${extension.name}/index`, ["es"]);
5512
+ if (blueprint.src.includes("browser")) exports["./browser"] = pathToCondition("./dist/src/browser/index", ["es"]);
5513
+ }
5514
+ for (const name of [...blueprintToSheets(blueprint), ...blueprint.themes ? ["styles/themes"] : []]) {
5515
+ exports[`./${name}`] = `./dist/src/${name}/index.css`;
5516
+ exports[`./${name}/scss`] = `./src/${name}/index.scss`;
5517
+ }
5518
+ if (Object.keys(exports).length > 0) exports["./package.json"] = "./package.json";
5519
+ return exports;
5520
+ }
5521
+ /**
4773
5522
  * Builds one `exports` condition block for a built environment.
4774
5523
  *
4775
5524
  * @param path - The extensionless `dist` path both conditions point at.
@@ -4895,6 +5644,12 @@ function srcToExports(src) {
4895
5644
  * the peer's floor states what consumers may supply rather than what this workspace
4896
5645
  * develops against.
4897
5646
  *
5647
+ * A workspace that renders a surface receives {@link BROWSE_DEV_DEPENDENCIES}: a browser
5648
+ * environment on either axis, a sheet, or themes. A framework face counts without a clause of
5649
+ * its own, because a face is selected only on an axis that carries a browser environment.
5650
+ * A browser setup runtime alone receives nothing, because it renders no surface for `browse`
5651
+ * to drive.
5652
+ *
4898
5653
  * A peer is declared here as well as under `peerDependencies`, because a peer is
4899
5654
  * not installed by the workspace that declares it and developing against one
4900
5655
  * requires it present. A runtime dependency is the opposite case and is removed:
@@ -4916,18 +5671,23 @@ function srcToExports(src) {
4916
5671
  * ```
4917
5672
  */
4918
5673
  function blueprintToDevDependencies(blueprint) {
5674
+ const sheets = blueprintToSheets(blueprint).length > 0 || blueprint.themes;
5675
+ const rendered = blueprint.src.includes("browser") || blueprint.app.includes("browser") || sheets;
4919
5676
  const merged = {
4920
5677
  ...BASE_DEV_DEPENDENCIES,
5678
+ ...rendered ? BROWSE_DEV_DEPENDENCIES : {},
5679
+ ...sheets ? STYLES_DEV_DEPENDENCIES : {},
4921
5680
  ...blueprint.src.length > 0 || blueprint.bin ? DECLARATION_DEV_DEPENDENCIES : {},
4922
- ...blueprint.src.includes("browser") || blueprint.setup.includes("browser") ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
5681
+ ...blueprint.src.includes("browser") || blueprint.setup.includes("browser") || sheets ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
4923
5682
  ...blueprint.app.length > 0 ? APP_DEV_DEPENDENCIES : {},
4924
5683
  ...blueprint.app.includes("browser") ? APP_BROWSER_DEV_DEPENDENCIES : {},
4925
5684
  ...blueprint.showcase && blueprint.app.includes("browser") ? SHOWCASE_DEV_DEPENDENCIES : {},
4926
5685
  ...blueprint.app.includes("server") ? APP_SERVER_DEV_DEPENDENCIES : {}
4927
5686
  };
5687
+ for (const framework of blueprintToMachinery(blueprint).frameworks) Object.assign(merged, FRAMEWORK_MATRIX[framework].dependencies);
4928
5688
  for (const extra of blueprint.extras) merged[extra.name] = extra.range;
4929
5689
  for (const peer of blueprint.peers) if (!Object.hasOwn(merged, peer.name)) merged[peer.name] = peer.range;
4930
- const own = blueprint.src.length > 0 ? `@orkestrel/${blueprint.name}` : blueprint.name;
5690
+ const own = blueprint.src.length > 0 || sheets ? `@orkestrel/${blueprint.name}` : blueprint.name;
4931
5691
  const runtime = new Set(blueprint.dependencies.map((dependency) => dependency.name));
4932
5692
  return Object.fromEntries(Object.entries(merged).filter(([name]) => name !== own && !runtime.has(name)).sort(([left], [right]) => compareValues(left, right)));
4933
5693
  }
@@ -4968,11 +5728,22 @@ function blueprintToDevDependencies(blueprint) {
4968
5728
  * ```
4969
5729
  */
4970
5730
  function blueprintToScripts(blueprint) {
4971
- const publishes = blueprint.src.length > 0;
5731
+ const source = blueprintToFaces(blueprint).flatMap((extension) => extension.surface === "browser" && extension.axes.includes("src") ? [extension.name] : []);
5732
+ const application = blueprintToFaces(blueprint).flatMap((extension) => extension.surface === "browser" && extension.axes.includes("app") ? [extension.name] : []);
5733
+ const sheets = blueprintToSheets(blueprint);
5734
+ const targets = [...sheets, ...blueprint.themes && !blueprint.styles ? ["themes"] : []];
5735
+ const checker = application.includes("vue") ? FRAMEWORK_MATRIX.vue.checker : "tsc";
5736
+ const publishes = blueprint.src.length > 0 || targets.length > 0;
4972
5737
  const integrates = blueprint.integration;
4973
5738
  const compiles = publishes || blueprint.bin;
4974
5739
  const runtime = blueprint.app.filter((environment) => environment !== "core");
4975
5740
  const vitest = "vitest run --config vite.config.ts --no-cache --reporter=dot";
5741
+ const checks = [
5742
+ ...blueprint.src,
5743
+ ...source,
5744
+ ...sheets,
5745
+ ...blueprint.bin ? ["bin"] : []
5746
+ ].map((name) => `npm run check:src:${name}`);
4976
5747
  const scripts = {
4977
5748
  clean: "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
4978
5749
  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)\"",
@@ -4981,30 +5752,32 @@ function blueprintToScripts(blueprint) {
4981
5752
  lint: "oxlint --config .oxlintrc.json --fix .",
4982
5753
  "lint:check": "oxlint --config .oxlintrc.json --deny-warnings .",
4983
5754
  check: [
4984
- "tsc --noEmit --project tsconfig.json",
4985
- ...compiles ? ["npm run check:src"] : [],
5755
+ `${checker} --noEmit --project tsconfig.json`,
5756
+ ...checks.length > 0 ? ["npm run check:src"] : [],
4986
5757
  ...blueprint.app.length > 0 ? ["npm run check:app"] : [],
4987
5758
  ...blueprint.skills ? ["npm run check:skills"] : []
4988
5759
  ].join(" && ")
4989
5760
  };
4990
- if (compiles) {
4991
- scripts["check:src"] = [...blueprint.src.map((environment) => `npm run check:src:${environment}`), ...blueprint.bin ? ["npm run check:src:bin"] : []].join(" && ");
5761
+ if (checks.length > 0) {
5762
+ scripts["check:src"] = checks.join(" && ");
4992
5763
  for (const environment of blueprint.src) scripts[`check:src:${environment}`] = `tsc --noEmit -p configs/src/tsconfig.${environment}.json`;
4993
5764
  if (blueprint.bin) scripts["check:src:bin"] = "tsc --noEmit -p configs/src/tsconfig.bin.json";
5765
+ for (const name of [...sheets, ...source]) scripts[`check:src:${name}`] = `tsc --noEmit -p configs/src/tsconfig.${name}.json`;
4994
5766
  }
4995
5767
  if (blueprint.app.length > 0) {
4996
- scripts["check:app"] = blueprint.app.map((environment) => `npm run check:app:${environment}`).join(" && ");
4997
- 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`;
5768
+ scripts["check:app"] = [...blueprint.app, ...application].map((environment) => `npm run check:app:${environment}`).join(" && ");
5769
+ for (const environment of blueprint.app) scripts[`check:app:${environment}`] = `tsc --noEmit -p configs/app/tsconfig.${environment}.json`;
5770
+ for (const name of application) scripts[`check:app:${name}`] = `${FRAMEWORK_MATRIX[name].checker} --noEmit -p configs/app/tsconfig.${name}.json`;
4998
5771
  }
4999
5772
  if (blueprint.skills) scripts["check:skills"] = `tsc --noEmit -p ${SKILLS_CONFIG_PATH}`;
5000
5773
  scripts.test = [
5001
5774
  ...compiles ? ["npm run test:src"] : [],
5002
5775
  ...blueprint.app.length > 0 ? ["npm run test:app"] : [],
5003
- ...blueprint.journey && blueprint.app.includes("browser") ? ["npm run test:journey"] : [],
5776
+ ...blueprint.journey && blueprint.app.includes("browser") ? ["npm run test:journey", ...application.map((name) => `npm run test:journey:${name}`)] : [],
5004
5777
  "npm run test:policy",
5005
5778
  "npm run test:config",
5006
- ...blueprint.setup.includes("node") ? ["npm run test:setup"] : [],
5007
- ...blueprint.setup.includes("browser") ? ["npm run test:setup:browser"] : [],
5779
+ ...blueprint.setup.includes("node") || blueprint.global ? ["npm run test:setup"] : [],
5780
+ ...blueprint.setup.includes("browser") || targets.length > 0 ? ["npm run test:setup:browser"] : [],
5008
5781
  ...blueprint.skills ? ["npm run test:skills"] : [],
5009
5782
  ...blueprint.guides ? ["npm run test:guides"] : [],
5010
5783
  ...blueprint.conformance ? ["npm run test:conformance"] : [],
@@ -5015,20 +5788,32 @@ function blueprintToScripts(blueprint) {
5015
5788
  scripts["test:src"] = [
5016
5789
  vitest,
5017
5790
  ...blueprint.src.map((environment) => `--project ${SRC_MATRIX[environment].project}`),
5791
+ ...source.map((name) => `--project src:${name}`),
5018
5792
  ...blueprint.bin ? ["--project src:bin"] : []
5019
5793
  ].join(" ");
5020
5794
  for (const environment of blueprint.src) scripts[`test:src:${environment}`] = `${vitest} --project ${SRC_MATRIX[environment].project}`;
5021
5795
  if (blueprint.bin) scripts["test:src:bin"] = `${vitest} --project src:bin`;
5796
+ for (const name of source) scripts[`test:src:${name}`] = `${vitest} --project src:${name}`;
5797
+ scripts["test:src"] = [...blueprint.src.length > 0 || blueprint.bin ? [scripts["test:src"]] : [], ...targets.map((name) => `npm run test:src:${name}`)].join(" && ");
5798
+ for (const name of targets) scripts[`test:src:${name}`] = `npm run build:src:${name} && ${vitest} --project src:${name}`;
5022
5799
  }
5023
5800
  if (blueprint.app.length > 0) {
5024
- scripts["test:app"] = [vitest, ...blueprint.app.map((environment) => `--project ${APP_MATRIX[environment].project}`)].join(" ");
5801
+ scripts["test:app"] = [
5802
+ vitest,
5803
+ ...blueprint.app.map((environment) => `--project ${APP_MATRIX[environment].project}`),
5804
+ ...application.map((name) => `--project app:${name}`)
5805
+ ].join(" ");
5025
5806
  for (const environment of blueprint.app) scripts[`test:app:${environment}`] = `${vitest} --project ${APP_MATRIX[environment].project}`;
5807
+ for (const name of application) scripts[`test:app:${name}`] = `${vitest} --project app:${name}`;
5026
5808
  }
5027
5809
  scripts["test:policy"] = `${vitest} --project policy`;
5028
5810
  scripts["test:config"] = `${vitest} --project config`;
5029
- if (blueprint.setup.includes("node")) scripts["test:setup"] = `${vitest} --project setup`;
5030
- if (blueprint.setup.includes("browser")) scripts["test:setup:browser"] = `${vitest} --project setup:browser`;
5031
- if (blueprint.journey && blueprint.app.includes("browser")) scripts["test:journey"] = `vitest run --config ${JOURNEY_CONFIG_PATH} --no-cache --reporter=dot`;
5811
+ if (blueprint.setup.includes("node") || blueprint.global) scripts["test:setup"] = `${vitest} --project setup`;
5812
+ if (blueprint.setup.includes("browser") || targets.length > 0) scripts["test:setup:browser"] = `${vitest} --project setup:browser`;
5813
+ if (blueprint.journey && blueprint.app.includes("browser")) {
5814
+ scripts["test:journey"] = `vitest run --config ${JOURNEY_CONFIG_PATH} --no-cache --reporter=dot`;
5815
+ for (const name of application) scripts[`test:journey:${name}`] = `${scripts["test:journey"]} --mode ${name}`;
5816
+ }
5032
5817
  if (blueprint.skills) scripts["test:skills"] = `${vitest} --project skills`;
5033
5818
  if (blueprint.guides) scripts["test:guides"] = `node --experimental-strip-types ${GUIDES_TEST_PATH}`;
5034
5819
  if (blueprint.conformance) scripts["test:conformance"] = `${vitest} --project conformance`;
@@ -5043,23 +5828,34 @@ function blueprintToScripts(blueprint) {
5043
5828
  ...blueprint.app.length > 0 ? ["npm run build:app"] : []
5044
5829
  ].join(" && ");
5045
5830
  if (compiles) {
5046
- scripts["build:src"] = [...blueprint.src.map((environment) => `npm run build:src:${environment}`), ...blueprint.bin ? ["npm run build:src:bin"] : []].join(" && ");
5831
+ scripts["build:src"] = [
5832
+ ...blueprint.src.map((environment) => `npm run build:src:${environment}`),
5833
+ ...source.map((name) => `npm run build:src:${name}`),
5834
+ ...targets.map((name) => `npm run build:src:${name}`),
5835
+ ...blueprint.bin ? ["npm run build:src:bin"] : []
5836
+ ].join(" && ");
5047
5837
  for (const environment of blueprint.src) {
5048
5838
  const build = `vite build --config configs/src/vite.${environment}.config.ts`;
5049
5839
  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;
5050
5840
  }
5051
5841
  if (blueprint.bin) scripts["build:src:bin"] = "vite build --config configs/src/vite.bin.config.ts";
5842
+ for (const name of source) scripts[`build:src:${name}`] = `vite build --config configs/src/vite.${name}.config.ts`;
5843
+ 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(" && ");
5052
5844
  }
5053
5845
  if (blueprint.app.length > 0) {
5054
- scripts["build:app"] = runtime.length > 0 ? runtime.map((environment) => `npm run build:app:${environment}`).join(" && ") : "npm run check:app:core";
5055
- for (const environment of runtime) scripts[`build:app:${environment}`] = `vite build --config configs/app/vite.${environment}.config.ts`;
5846
+ scripts["build:app"] = runtime.length > 0 ? [...runtime, ...application].map((environment) => `npm run build:app:${environment}`).join(" && ") : "npm run check:app:core";
5847
+ for (const environment of [...runtime, ...application]) scripts[`build:app:${environment}`] = `vite build --config configs/app/vite.${environment}.config.ts`;
5056
5848
  }
5057
5849
  if (blueprint.app.includes("browser")) {
5058
5850
  scripts.dev = "vite --config configs/app/vite.browser.config.ts";
5851
+ for (const name of application) scripts[`dev:${name}`] = `vite --config configs/app/vite.${name}.config.ts`;
5059
5852
  if (blueprint.showcase) {
5060
5853
  scripts.showcase = `vite --config ${SHOWCASE_CONFIG_PATH}`;
5061
5854
  scripts["build:showcase"] = `vite build --config ${SHOWCASE_CONFIG_PATH}`;
5062
- scripts.show = "npm run format && npm run build:showcase && npm run copy dist/showcase/index.html demo/showcase.html";
5855
+ for (const name of application) {
5856
+ scripts[`showcase:${name}`] = `${scripts.showcase} --mode ${name}`;
5857
+ scripts[`build:showcase:${name}`] = `${scripts["build:showcase"]} --mode ${name}`;
5858
+ }
5063
5859
  }
5064
5860
  }
5065
5861
  if (blueprint.app.includes("server")) {
@@ -5069,7 +5865,9 @@ function blueprintToScripts(blueprint) {
5069
5865
  if (publishes) {
5070
5866
  scripts.prepack = "npm run build";
5071
5867
  scripts.prepublishOnly = [
5072
- "npm run format:check && npm run lint:check && npm run check && npm run build && npm test",
5868
+ "npm run format:check && npm run lint:check && npm run check && npm run build",
5869
+ ...blueprint.showcase && blueprint.app.includes("browser") ? ["npm run build:showcase", ...application.map((name) => `npm run build:showcase:${name}`)] : [],
5870
+ "npm test",
5073
5871
  RELEASE_PROOF_COMMAND,
5074
5872
  ...blueprint.service ? ["npm run test:service"] : []
5075
5873
  ].join(" && ");
@@ -5087,6 +5885,8 @@ function blueprintToScripts(blueprint) {
5087
5885
  * benchmark workbench scripts. A workspace carrying guides adds `test:guides`,
5088
5886
  * whose package-owned entry runs the guides project and handles explicit parity
5089
5887
  * rewrites, and one carrying skills adds `test:skills`. Publishing adds the pack and publication lifecycle scripts.
5888
+ * Sheet and framework faces add their check and build scripts, application frameworks add dev scripts,
5889
+ * and selected showcases and journeys add their mode scripts.
5090
5890
  * Aggregate test scripts and
5091
5891
  * maintainer-owned gate chains stay outside the region.
5092
5892
  *
@@ -5110,13 +5910,18 @@ function blueprintToScripts(blueprint) {
5110
5910
  */
5111
5911
  function blueprintToWritableScripts(blueprint) {
5112
5912
  const scripts = blueprintToScripts(blueprint);
5913
+ const sheets = [...blueprintToSheets(blueprint), ...blueprint.themes && !blueprint.styles ? ["themes"] : []];
5113
5914
  const writable = [];
5114
5915
  for (const [name, command] of Object.entries(scripts)) {
5115
- if (!(name.startsWith("test:") && name !== "test:src" && name !== "test:app")) continue;
5916
+ const direct = name.startsWith("test:") && name !== "test:src" && name !== "test:app";
5917
+ const sheet = sheets.some((face) => name === `build:src:${face}` || name === `check:src:${face}`);
5918
+ 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}`));
5919
+ const showcase = name === "showcase" || name.startsWith("showcase:") || name === "build:showcase" || name.startsWith("build:showcase:");
5920
+ if (!direct && !sheet && !framework && !showcase) continue;
5116
5921
  writable.push({
5117
5922
  name,
5118
5923
  command,
5119
- accepted: name === "test:guides" ? ["vitest run --config vite.config.ts --no-cache --reporter=dot --project guides"] : []
5924
+ 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)] : []
5120
5925
  });
5121
5926
  }
5122
5927
  const prepack = scripts.prepack;
@@ -5172,7 +5977,9 @@ function blueprintToWritableScripts(blueprint) {
5172
5977
  * ```
5173
5978
  */
5174
5979
  function blueprintToManifest(blueprint) {
5175
- const publishes = blueprint.src.length > 0;
5980
+ const sheets = blueprintToSheets(blueprint);
5981
+ const targets = [...sheets, ...blueprint.themes ? ["styles/themes"] : []];
5982
+ const publishes = blueprint.src.length > 0 || targets.length > 0;
5176
5983
  const name = publishes ? `@orkestrel/${blueprint.name}` : blueprint.name;
5177
5984
  const dependencies = {};
5178
5985
  for (const dependency of [...blueprint.dependencies].sort((left, right) => compareValues(left.name, right.name))) dependencies[dependency.name] = dependency.range;
@@ -5199,18 +6006,19 @@ function blueprintToManifest(blueprint) {
5199
6006
  url: `git+https://github.com/orkestrel/${blueprint.name}.git`
5200
6007
  } } : {},
5201
6008
  ...blueprint.bin ? { bin: { [blueprint.name]: "./dist/bin/main.js" } } : {},
5202
- files: blueprint.bin ? [
5203
- "dist/src",
5204
- "dist/bin",
6009
+ files: [
6010
+ blueprint.bin || publishes ? "dist/src" : "dist/app",
6011
+ ...blueprint.bin ? ["dist/bin"] : [],
6012
+ ...targets.map((face) => `!dist/src/${face}/index.js`),
6013
+ ...sheets.map((face) => `src/${face}/**/*.scss`),
6014
+ ...blueprint.themes && !blueprint.styles ? ["src/styles/themes/**/*.scss"] : [],
5205
6015
  "README.md"
5206
- ] : publishes ? ["dist/src", "README.md"] : ["dist/app", "README.md"],
6016
+ ],
5207
6017
  type: "module",
5208
6018
  ...publishes ? {
5209
- sideEffects: blueprint.bin ? [`./${BIN_ENTRY_PATH}`, "./dist/bin/main.js"] : false,
5210
- main: entry.main,
5211
- module: entry.module,
5212
- ...entry.types === void 0 ? {} : { types: entry.types },
5213
- exports: srcToExports(blueprint.src),
6019
+ sideEffects: targets.length > 0 || blueprint.bin ? [...targets.length > 0 ? ["**/*.css", "**/*.scss"] : [], ...blueprint.bin ? [`./${BIN_ENTRY_PATH}`, "./dist/bin/main.js"] : []] : false,
6020
+ ...blueprint.src.length > 0 ? entry : {},
6021
+ exports: blueprintToExports(blueprint),
5214
6022
  publishConfig: { access: "public" }
5215
6023
  } : {},
5216
6024
  scripts: blueprintToScripts(blueprint),
@@ -5247,15 +6055,16 @@ function blueprintToManifest(blueprint) {
5247
6055
  *
5248
6056
  * const blueprint = createBlueprint('router', { app: ['browser'] })
5249
6057
  *
5250
- * blueprintToMachinery(blueprint).vue // true
6058
+ * blueprintToMachinery(blueprint).frameworks // []
5251
6059
  * ```
5252
6060
  */
5253
6061
  function blueprintToMachinery(blueprint) {
5254
6062
  const hosted = blueprint.app.some((environment) => environment !== "core");
6063
+ const frameworks = FRAMEWORKS.filter((framework) => blueprintToFaces(blueprint).some((extension) => extension.surface === "browser" && extension.name === framework && extension.axes.some((axis) => blueprint[axis].includes("browser"))));
5255
6064
  return {
5256
- browser: blueprint.src.includes("browser") || blueprint.app.includes("browser") || blueprint.setup.includes("browser"),
5257
- vue: blueprint.app.includes("browser"),
5258
- output: blueprint.src.length > 0 || blueprint.bin || hosted,
6065
+ browser: blueprint.src.includes("browser") || blueprint.app.includes("browser") || blueprint.setup.includes("browser") || blueprintToSheets(blueprint).length > 0 || blueprint.themes || frameworks.length > 0,
6066
+ frameworks,
6067
+ output: blueprint.src.length > 0 || blueprint.bin || hosted || blueprintToSheets(blueprint).length > 0 || blueprint.themes,
5259
6068
  showcase: blueprint.showcase && blueprint.app.includes("browser")
5260
6069
  };
5261
6070
  }
@@ -5291,6 +6100,12 @@ function blueprintToMachinery(blueprint) {
5291
6100
  */
5292
6101
  function blueprintToRootTsconfig(blueprint) {
5293
6102
  const aliases = [];
6103
+ for (const extension of blueprintToFaces(blueprint)) {
6104
+ if (extension.surface !== "browser") continue;
6105
+ for (const axis of extension.axes) aliases.push([`@${axis}/${extension.name}`, `./${axis}/${extension.name}/index.ts`]);
6106
+ if (extension.axes.includes("src")) aliases.push([`@orkestrel/${blueprint.name}/${extension.name}`, `./src/${extension.name}/index.ts`]);
6107
+ }
6108
+ for (const name of blueprintToSheets(blueprint)) aliases.push([`@src/${name}`, `./src/${name}/index.ts`]);
5294
6109
  for (const environment of ENVIRONMENTS) if (blueprint.src.includes(environment)) aliases.push([`@src/${environment}`, `./src/${environment}/index.ts`]);
5295
6110
  for (const environment of ENVIRONMENTS) if (blueprint.app.includes(environment)) aliases.push([`@app/${environment}`, `./app/${environment}/index.ts`]);
5296
6111
  const root = srcToRoot(blueprint.src);
@@ -5306,7 +6121,47 @@ function blueprintToRootTsconfig(blueprint) {
5306
6121
  if (matchesPrintWidth(line)) return line;
5307
6122
  return `\t\t\t"${specifier}": [\n\t\t\t\t"${target}"\n\t\t\t]${separator}`;
5308
6123
  }).join("\n");
5309
- return fillTemplate(CONFIG_TEMPLATES.root.tsconfig, { paths });
6124
+ return fillTemplate(CONFIG_TEMPLATES.root.tsconfig, { paths: aliases.length === 0 ? " \"paths\": {}" : `\t\t"paths": {\n${paths}\n\t\t}` });
6125
+ }
6126
+ /**
6127
+ * Projects a blueprint into the project labels its root Vitest configuration registers.
6128
+ *
6129
+ * @param blueprint - The workspace specification.
6130
+ * @returns Project labels in registration order, including wrapper-registered sheet faces.
6131
+ *
6132
+ * @example
6133
+ * ```ts
6134
+ * import { blueprintToProjects, createBlueprint } from '@orkestrel/scaffold'
6135
+ *
6136
+ * blueprintToProjects(createBlueprint('desk')).includes('policy') // true
6137
+ * ```
6138
+ */
6139
+ function blueprintToProjects(blueprint) {
6140
+ const sheets = blueprintToSheets(blueprint);
6141
+ const styled = sheets.length > 0 || blueprint.themes;
6142
+ const faces = blueprintToFaces(blueprint);
6143
+ const projects = sheets.map((name) => `src:${name}`);
6144
+ if (blueprint.themes && !blueprint.styles) projects.push("src:themes");
6145
+ for (const axis of AXES) {
6146
+ for (const environment of ENVIRONMENTS) {
6147
+ if (axis === "app" && environment === "server") continue;
6148
+ if (blueprint[axis].includes(environment)) projects.push(`${axis}:${environment}`);
6149
+ }
6150
+ for (const face of faces) if (face.surface === "browser" && face.axes.includes(axis)) projects.push(`${axis}:${face.name}`);
6151
+ if (axis === "src" && blueprint.bin) projects.push("src:bin");
6152
+ if (axis === "app" && blueprint.app.includes("server")) projects.push("app:server");
6153
+ }
6154
+ projects.push("policy", "config");
6155
+ if (blueprint.setup.includes("node") || blueprint.global) projects.push("setup");
6156
+ if (blueprint.setup.includes("browser") || styled) projects.push("setup:browser");
6157
+ if (blueprint.guides) projects.push("guides");
6158
+ if (blueprint.skills) projects.push("skills");
6159
+ if (blueprint.conformance) projects.push("conformance");
6160
+ if (blueprint.service) projects.push("service");
6161
+ if (blueprint.src.length > 0 || styled) projects.push("distribution");
6162
+ if (blueprint.integration) projects.push("integration");
6163
+ projects.push("probe");
6164
+ return projects;
5310
6165
  }
5311
6166
  /**
5312
6167
  * Compiles the root Vite and Vitest configuration for a blueprint.
@@ -5330,44 +6185,58 @@ function blueprintToRootTsconfig(blueprint) {
5330
6185
  */
5331
6186
  function blueprintToRootVite(blueprint) {
5332
6187
  const machinery = blueprintToMachinery(blueprint);
5333
- const publishes = blueprint.src.length > 0;
6188
+ const planned = new Set(blueprintToProjects(blueprint));
6189
+ const sheets = blueprintToSheets(blueprint);
6190
+ const styled = sheets.length > 0 || blueprint.themes;
5334
6191
  const global = blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "";
5335
6192
  const imports = [];
5336
6193
  if (machinery.browser) imports.push("import { playwright } from '@vitest/browser-playwright'");
5337
- if (machinery.vue) imports.push("import vue from '@vitejs/plugin-vue'");
6194
+ if (machinery.frameworks.includes("vue")) imports.push("import vue from '@vitejs/plugin-vue'");
5338
6195
  if (machinery.showcase) imports.push("import { viteSingleFile } from 'vite-plugin-singlefile'");
6196
+ if (machinery.showcase) {
6197
+ imports.push("import { renameSync } from 'node:fs'");
6198
+ imports.push("import { resolve as resolvePath } from 'node:path'");
6199
+ }
5339
6200
  const factories = [];
5340
6201
  const projects = [];
5341
- if (blueprint.src.includes("core")) {
6202
+ const external = "external: resolveSourceExternal,";
6203
+ if (blueprint.src.includes("browser") || blueprint.src.includes("server")) factories.push(blueprint.src.includes("core") ? `function resolveSourceExternal(id: string): boolean {
6204
+ return (
6205
+ id === '@src/core' ||
6206
+ resolveExternal(id, {
6207
+ peers,
6208
+ refused: [],
6209
+ siblings: [resolveWorkspacePath('src/core/index.ts')],
6210
+ })
6211
+ )
6212
+ }
6213
+ ` : `function resolveSourceExternal(id: string): boolean {
6214
+ return resolveExternal(id, { peers, refused: [], siblings: [] })
6215
+ }
6216
+ `);
6217
+ if (styled) {
6218
+ factories.push(CONFIG_TEMPLATES.factories.sheet);
6219
+ for (const name of sheets) projects.push(`'./configs/src/vite.${name}.config.ts'`);
6220
+ if (planned.has("src:themes")) projects.push("'./configs/src/vite.themes.config.ts'");
6221
+ }
6222
+ if (planned.has("src:core")) {
5342
6223
  factories.push(CONFIG_TEMPLATES.factories.src.core);
5343
6224
  projects.push("srcCore");
5344
6225
  }
5345
- if (blueprint.src.includes("browser")) {
6226
+ if (planned.has("src:browser")) {
5346
6227
  const core = blueprint.src.includes("core");
5347
6228
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.src.browser, {
5348
- external: core ? `external: (id: string) =>
5349
- id === '@src/core' ||
5350
- id.startsWith('@orkestrel/') ||
5351
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
5352
- id.startsWith('@orkestrel/') ||
5353
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
6229
+ external,
5354
6230
  output: core ? " output: { paths: { '@src/core': '../core/index.js' } }," : " output: {},",
5355
6231
  exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : "",
5356
6232
  global
5357
6233
  }));
5358
6234
  projects.push("srcBrowser");
5359
6235
  }
5360
- if (blueprint.src.includes("server")) {
6236
+ if (planned.has("src:server")) {
5361
6237
  const core = blueprint.src.includes("core");
5362
6238
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.src.server, {
5363
- external: core ? `external: (id: string) =>
5364
- id === '@src/core' ||
5365
- id.startsWith('node:') ||
5366
- id.startsWith('@orkestrel/') ||
5367
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
5368
- id.startsWith('node:') ||
5369
- id.startsWith('@orkestrel/') ||
5370
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
6239
+ external,
5371
6240
  output: core ? `\t\t\t\toutput: [
5372
6241
  {
5373
6242
  format: 'es',
@@ -5384,15 +6253,19 @@ function blueprintToRootVite(blueprint) {
5384
6253
  }));
5385
6254
  projects.push("srcServer");
5386
6255
  }
5387
- if (blueprint.bin) {
6256
+ if (planned.has("src:vue")) {
6257
+ factories.push(CONFIG_TEMPLATES.factories.src.vue);
6258
+ projects.push("srcVue");
6259
+ }
6260
+ if (planned.has("src:bin")) {
5388
6261
  factories.push(CONFIG_TEMPLATES.factories.src.bin);
5389
6262
  projects.push("srcBin");
5390
6263
  }
5391
- if (blueprint.app.includes("core")) {
6264
+ if (planned.has("app:core")) {
5392
6265
  factories.push(CONFIG_TEMPLATES.factories.app.core);
5393
6266
  projects.push("appCore");
5394
6267
  }
5395
- if (blueprint.app.includes("browser")) {
6268
+ if (planned.has("app:browser")) {
5396
6269
  const showcaseFactory = machinery.showcase ? CONFIG_TEMPLATES.factories.app.showcase : "";
5397
6270
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.app.browser, {
5398
6271
  showcaseFactory,
@@ -5401,7 +6274,11 @@ function blueprintToRootVite(blueprint) {
5401
6274
  }));
5402
6275
  projects.push("appBrowser");
5403
6276
  }
5404
- if (blueprint.app.includes("server")) {
6277
+ if (planned.has("app:vue")) {
6278
+ factories.push(fillTemplate(CONFIG_TEMPLATES.factories.app.vue, { journeyExclude: blueprint.journey ? " exclude: ['tests/app/vue/integration.test.ts'],\n" : " exclude: [],\n" }));
6279
+ projects.push("appVue");
6280
+ }
6281
+ if (planned.has("app:server")) {
5405
6282
  factories.push(CONFIG_TEMPLATES.factories.app.server);
5406
6283
  projects.push("appServer");
5407
6284
  }
@@ -5409,39 +6286,40 @@ function blueprintToRootVite(blueprint) {
5409
6286
  projects.push("policy");
5410
6287
  factories.push(CONFIG_TEMPLATES.factories.config);
5411
6288
  projects.push("config");
5412
- if (blueprint.setup.includes("node")) {
6289
+ if (planned.has("setup")) {
5413
6290
  factories.push(CONFIG_TEMPLATES.factories.setup);
5414
6291
  projects.push("setup");
5415
6292
  }
5416
- if (blueprint.setup.includes("browser")) {
6293
+ if (planned.has("setup:browser")) {
5417
6294
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.browser, {
5418
- plugins: machinery.vue ? " plugins: [vue()],\n" : "",
6295
+ optimization: machinery.frameworks.includes("vue") ? "optimizeDeps: { include: [...optimizeDeps.include, 'vue'] }" : "optimizeDeps",
6296
+ plugins: machinery.frameworks.includes("vue") ? " plugins: [vue()],\n" : "",
5419
6297
  global
5420
6298
  }));
5421
6299
  projects.push("setupBrowser");
5422
6300
  }
5423
- if (blueprint.guides) {
6301
+ if (planned.has("guides")) {
5424
6302
  factories.push(CONFIG_TEMPLATES.factories.guides);
5425
6303
  projects.push("guides");
5426
6304
  }
5427
- if (blueprint.skills) {
6305
+ if (planned.has("skills")) {
5428
6306
  factories.push(CONFIG_TEMPLATES.factories.skills);
5429
6307
  projects.push("skills");
5430
6308
  }
5431
- if (blueprint.conformance) {
6309
+ if (planned.has("conformance")) {
5432
6310
  factories.push(CONFIG_TEMPLATES.factories.conformance);
5433
6311
  projects.push("conformance");
5434
6312
  }
5435
- if (blueprint.service) {
6313
+ if (planned.has("service")) {
5436
6314
  factories.push(CONFIG_TEMPLATES.factories.service);
5437
6315
  projects.push("service");
5438
6316
  }
5439
- if (publishes) {
6317
+ if (planned.has("distribution")) {
5440
6318
  factories.push(CONFIG_TEMPLATES.factories.distribution);
5441
6319
  projects.push("distribution");
5442
6320
  }
5443
- if (blueprint.integration) {
5444
- factories.push(fillTemplate(CONFIG_TEMPLATES.factories.integration, { global }));
6321
+ if (planned.has("integration")) {
6322
+ factories.push(fillTemplate(styled ? CONFIG_TEMPLATES.factories.integration.sheet : CONFIG_TEMPLATES.factories.integration.module, { global }));
5445
6323
  projects.push("integration");
5446
6324
  }
5447
6325
  factories.push(CONFIG_TEMPLATES.factories.probe);
@@ -5454,14 +6332,24 @@ ${projects.map((project) => `\t\t\t${project},`).join("\n")}
5454
6332
  const helpers = [
5455
6333
  "enforceBuildLog",
5456
6334
  "environmentBoundary",
5457
- "outputBoundary"
6335
+ "outputBoundary",
6336
+ "resolveApplication",
6337
+ "resolveExternal",
6338
+ "stampPage"
5458
6339
  ].filter((helper) => body.includes(helper));
6340
+ const helperImport = `import { ${helpers.join(", ")} } from './configs/helpers.js'\n`;
5459
6341
  return fillTemplate(CONFIG_TEMPLATES.root.vite, {
5460
- journey: blueprint.journey && machinery.vue ? "import type { JourneyVariant } from '@orkestrel/test'\n" : "",
6342
+ journey: blueprint.journey && blueprint.app.includes("browser") ? "import type { JourneyVariant } from '@orkestrel/test'\n" : "",
5461
6343
  imports: imports.length === 0 ? "" : `${imports.join("\n")}\n`,
5462
- helpers: helpers.length === 0 ? "" : `import { ${helpers.join(", ")} } from './configs/helpers.js'\n`,
6344
+ helpers: helpers.length === 0 ? "" : matchesPrintWidth(helperImport.trimEnd()) ? helperImport : `import {\n${helpers.map((helper) => `\t${helper},`).join("\n")}\n} from './configs/helpers.js'\n`,
5463
6345
  browsers: machinery.browser ? "import { resolveBrowser, resolvePinnedBrowser } from './configs/browsers.js'\n" : "",
5464
- 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" : ""),
6346
+ options: (machinery.browser ? `const browserOptions = resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)
6347
+
6348
+ const optimizeDeps = {\n\tinclude: ${JSON.stringify([
6349
+ "@orkestrel/test",
6350
+ "@orkestrel/test/browser",
6351
+ ...Object.hasOwn(blueprintToDevDependencies(blueprint), "@orkestrel/contract") || blueprint.dependencies.some((dependency) => dependency.name === "@orkestrel/contract") || blueprint.peers.some((peer) => peer.name === "@orkestrel/contract") ? ["@orkestrel/contract"] : []
6352
+ ]).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` : ""),
5465
6353
  factories: body,
5466
6354
  projects: projectRows
5467
6355
  });
@@ -5502,6 +6390,26 @@ function blueprintToConfigArtifacts(blueprint) {
5502
6390
  origin: "template",
5503
6391
  content: CONFIG_TEMPLATES.browsers
5504
6392
  });
6393
+ for (const name of [...blueprintToSheets(blueprint), ...blueprint.themes ? ["styles/themes"] : []]) {
6394
+ const label = name === "styles/themes" ? "themes" : name;
6395
+ artifacts.push({
6396
+ path: `configs/src/vite.${label}.config.ts`,
6397
+ group: "configs",
6398
+ ownership: "content",
6399
+ origin: "template",
6400
+ content: fillTemplate(CONFIG_TEMPLATES.vites.sheet, {
6401
+ name,
6402
+ label
6403
+ })
6404
+ });
6405
+ if (name !== "styles/themes") artifacts.push({
6406
+ path: `configs/src/tsconfig.${name}.json`,
6407
+ group: "configs",
6408
+ ownership: "content",
6409
+ origin: "template",
6410
+ content: fillTemplate(CONFIG_TEMPLATES.tsconfigs.sheet, { name })
6411
+ });
6412
+ }
5505
6413
  for (const environment of blueprint.src) for (const path of SRC_MATRIX[environment].configs) {
5506
6414
  let content = CONFIG_TEMPLATES.vites.src.core;
5507
6415
  if (path === "configs/src/tsconfig.core.json") content = CONFIG_TEMPLATES.tsconfigs.src.core;
@@ -5539,11 +6447,10 @@ ${paths.join("\n")}
5539
6447
  "../../app/browser/**/*.cts",
5540
6448
  "../../app/browser/**/*.mts",
5541
6449
  "../../app/browser/**/*.ts",
5542
- "../../app/browser/**/*.tsx",
5543
- "../../app/browser/**/*.vue"
6450
+ "../../app/browser/**/*.tsx"
5544
6451
  ];
5545
6452
  if (blueprint.app.includes("core")) include.push("../../app/core/**/*.cts", "../../app/core/**/*.mts", "../../app/core/**/*.ts", "../../app/core/**/*.tsx");
5546
- 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");
6453
+ include.push("../../tests/app/browser/**/*.cts", "../../tests/app/browser/**/*.mts", "../../tests/app/browser/**/*.ts", "../../tests/app/browser/**/*.tsx", "../../tests/setup.ts", "../../tests/setupBrowser.ts");
5547
6454
  content = fillTemplate(CONFIG_TEMPLATES.tsconfigs.app.browser, { include: include.map((entry, index) => `\t\t${JSON.stringify(entry)}${index === include.length - 1 ? "" : ","}`).join("\n") });
5548
6455
  } else if (path === "configs/app/vite.server.config.ts") content = CONFIG_TEMPLATES.vites.app.server;
5549
6456
  else if (path === "configs/app/tsconfig.server.json") {
@@ -5566,6 +6473,58 @@ ${paths.join("\n")}
5566
6473
  content
5567
6474
  });
5568
6475
  }
6476
+ for (const extension of blueprintToFaces(blueprint)) {
6477
+ if (extension.surface !== "browser") continue;
6478
+ for (const axis of extension.axes) {
6479
+ const include = [
6480
+ ...[
6481
+ "cts",
6482
+ "mts",
6483
+ "ts",
6484
+ "tsx",
6485
+ ...FRAMEWORK_MATRIX[extension.name].suffixes
6486
+ ].map((suffix) => `../../app/${extension.name}/**/*.${suffix}`),
6487
+ ...blueprint.app.includes("core") ? [
6488
+ "cts",
6489
+ "mts",
6490
+ "ts",
6491
+ "tsx"
6492
+ ].map((suffix) => `../../app/core/**/*.${suffix}`) : [],
6493
+ ...[
6494
+ "cts",
6495
+ "mts",
6496
+ "ts",
6497
+ "tsx",
6498
+ ...FRAMEWORK_MATRIX[extension.name].suffixes
6499
+ ].map((suffix) => `../../tests/app/${extension.name}/**/*.${suffix}`),
6500
+ "../../tests/setup.ts",
6501
+ "../../tests/setupBrowser.ts"
6502
+ ];
6503
+ const siblings = blueprint.src.filter((environment) => environment === "core" || environment === "browser").flatMap((environment) => ["ts", "js"].map((suffix) => {
6504
+ const key = `\t[resolveWorkspacePath('src/${environment}/index.${suffix}').replaceAll('\\\\', '/')]:`;
6505
+ const value = `'@orkestrel/${blueprint.name}${environment === "browser" ? "/browser" : ""}',`;
6506
+ return matchesPrintWidth(`${key} ${value}`) ? `${key} ${value}` : `${key}\n\t\t${value}`;
6507
+ })).join("\n");
6508
+ artifacts.push({
6509
+ path: `configs/${axis}/vite.${extension.name}.config.ts`,
6510
+ group: "configs",
6511
+ ownership: "content",
6512
+ origin: "template",
6513
+ environment: "browser",
6514
+ content: axis === "src" ? fillTemplate(CONFIG_TEMPLATES.vites.src.vue, {
6515
+ siblings,
6516
+ refused: `[${FRAMEWORK_MATRIX[extension.name].refused.map((name) => serializeTypeScriptString(name)).join(", ")}]`
6517
+ }) : CONFIG_TEMPLATES.vites.app.vue
6518
+ }, {
6519
+ path: `configs/${axis}/tsconfig.${extension.name}.json`,
6520
+ group: "configs",
6521
+ ownership: "content",
6522
+ origin: "template",
6523
+ environment: "browser",
6524
+ content: axis === "src" ? CONFIG_TEMPLATES.tsconfigs.vue.src : fillTemplate(CONFIG_TEMPLATES.tsconfigs.vue.app, { include: include.map((entry, index) => `\t\t${JSON.stringify(entry)}${index === include.length - 1 ? "" : ","}`).join("\n") })
6525
+ });
6526
+ }
6527
+ }
5569
6528
  if (blueprint.showcase && blueprint.app.includes("browser")) artifacts.push({
5570
6529
  path: SHOWCASE_CONFIG_PATH,
5571
6530
  group: "configs",
@@ -5595,15 +6554,11 @@ ${paths.join("\n")}
5595
6554
  * Compiles every artifact in the `source` group.
5596
6555
  *
5597
6556
  * @param blueprint - The workspace specification.
5598
- * @returns Empty published barrels, selected application entries, and the optional bin entry.
6557
+ * @returns Published barrels, sheet seeds, selected application entries, and the optional bin entry.
5599
6558
  *
5600
6559
  * @remarks
5601
- * The barrels and every runtime entry intentionally hold nothing. A generated
5602
- * sample entity would read as package implementation, so the
5603
- * scaffold establishes only the selected environment boundaries. An application
5604
- * entry is empty for the same reason the bin entry is, and because the vendored
5605
- * lint config refuses an unassigned import outside a stylesheet, so the entry
5606
- * cannot start by importing its barrel for effect either.
6560
+ * Barrels, server entries, and bin entries hold no starter implementation. Browser entries
6561
+ * render the workspace name as a level-one heading; the Vue face mounts its arrival component.
5607
6562
  *
5608
6563
  * @example
5609
6564
  * ```ts
@@ -5616,6 +6571,20 @@ ${paths.join("\n")}
5616
6571
  */
5617
6572
  function blueprintToSourceArtifacts(blueprint) {
5618
6573
  const artifacts = [];
6574
+ if (blueprint.themes && !blueprint.styles) artifacts.push({
6575
+ path: "src/styles/_tokens.scss",
6576
+ group: "source",
6577
+ ownership: "birth",
6578
+ origin: "template",
6579
+ content: ARTIFACT_TEMPLATES.source.sheet["_tokens.scss"]
6580
+ });
6581
+ 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({
6582
+ path: `src/${name}/${path}`,
6583
+ group: "source",
6584
+ ownership: "birth",
6585
+ origin: "template",
6586
+ content
6587
+ });
5619
6588
  for (const environment of blueprint.src) artifacts.push({
5620
6589
  path: `src/${environment}/index.ts`,
5621
6590
  group: "source",
@@ -5639,7 +6608,7 @@ function blueprintToSourceArtifacts(blueprint) {
5639
6608
  ownership: "birth",
5640
6609
  origin: "template",
5641
6610
  environment,
5642
- content: ARTIFACT_TEMPLATES.source.empty
6611
+ content: fillTemplate(ARTIFACT_TEMPLATES.source.main, { heading: `${matchesPrintWidth(`\t.appendChild(document.createElement('h1')).textContent = ${serializeTypeScriptString(blueprint.name)}`) ? " " : "\n "}${serializeTypeScriptString(blueprint.name)}` })
5643
6612
  }, {
5644
6613
  path: "app/browser/index.html",
5645
6614
  group: "source",
@@ -5657,6 +6626,36 @@ function blueprintToSourceArtifacts(blueprint) {
5657
6626
  content: ARTIFACT_TEMPLATES.source.empty
5658
6627
  });
5659
6628
  }
6629
+ for (const extension of blueprintToFaces(blueprint)) {
6630
+ if (extension.surface !== "browser") continue;
6631
+ for (const axis of extension.axes) {
6632
+ artifacts.push({
6633
+ path: `${axis}/${extension.name}/index.ts`,
6634
+ group: "source",
6635
+ ownership: "birth",
6636
+ origin: "template",
6637
+ environment: "browser",
6638
+ content: axis === "app" ? ARTIFACT_TEMPLATES.source.empty : ARTIFACT_TEMPLATES.source.barrel
6639
+ });
6640
+ if (axis !== "app") continue;
6641
+ artifacts.push({
6642
+ path: `app/${extension.name}/index.html`,
6643
+ group: "source",
6644
+ ownership: "birth",
6645
+ origin: "template",
6646
+ environment: "browser",
6647
+ content: ARTIFACT_TEMPLATES.source.browser
6648
+ });
6649
+ for (const [path, content] of Object.entries(ARTIFACT_TEMPLATES.source.vue)) artifacts.push({
6650
+ path: `app/${extension.name}/${path}`,
6651
+ group: "source",
6652
+ ownership: "birth",
6653
+ origin: "template",
6654
+ environment: "browser",
6655
+ content: fillTemplate(content, { heading: matchesPrintWidth(`\t<h1>${blueprint.name}</h1>`) ? blueprint.name : `\n\t\t${blueprint.name}\n\t` })
6656
+ });
6657
+ }
6658
+ }
5660
6659
  if (blueprint.bin) artifacts.push({
5661
6660
  path: BIN_ENTRY_PATH,
5662
6661
  group: "source",
@@ -5670,7 +6669,7 @@ function blueprintToSourceArtifacts(blueprint) {
5670
6669
  * Compiles every artifact in the `tests` group that is not vendored from the host.
5671
6670
  *
5672
6671
  * @param blueprint - The workspace specification.
5673
- * @returns Shared setup modules, axis tests, and the optional integration seed.
6672
+ * @returns Shared setup modules and proofs, axis and sheet tests, journey suites, and the optional integration seed.
5674
6673
  *
5675
6674
  * @remarks
5676
6675
  * `tests/setupPolicy.ts`, `tests/policy.test.ts`, and `tests/config.test.ts` are
@@ -5709,15 +6708,57 @@ function blueprintToTestArtifacts(blueprint) {
5709
6708
  origin: "template",
5710
6709
  content: ARTIFACT_TEMPLATES.tests.setup
5711
6710
  }];
6711
+ const sheets = [...blueprintToSheets(blueprint), ...blueprint.themes ? ["styles/themes"] : []];
6712
+ for (const extension of blueprintToFaces(blueprint)) {
6713
+ if (extension.surface !== "browser") continue;
6714
+ for (const axis of extension.axes) artifacts.push({
6715
+ path: `tests/${axis}/${extension.name}/index.test.ts`,
6716
+ group: "tests",
6717
+ ownership: "birth",
6718
+ origin: "template",
6719
+ environment: "browser",
6720
+ content: fillTemplate(ARTIFACT_TEMPLATES.tests.entry, {
6721
+ specifier: serializeTypeScriptString(`@${axis}/${extension.name}`),
6722
+ label: serializeTypeScriptString(`${axis} ${extension.name} entry`)
6723
+ })
6724
+ });
6725
+ }
6726
+ if (sheets.length > 0) artifacts.push({
6727
+ path: "tests/setupStyles.ts",
6728
+ group: "tests",
6729
+ ownership: "birth",
6730
+ origin: "template",
6731
+ content: ARTIFACT_TEMPLATES.tests.styles.module
6732
+ }, {
6733
+ path: "tests/setupStyles.test.ts",
6734
+ group: "tests",
6735
+ ownership: "birth",
6736
+ origin: "template",
6737
+ content: ARTIFACT_TEMPLATES.tests.styles.proof
6738
+ });
6739
+ for (const name of sheets) {
6740
+ const tests = "../".repeat(name.split("/").length + 1);
6741
+ artifacts.push({
6742
+ path: `tests/src/${name}/index.test.ts`,
6743
+ group: "tests",
6744
+ ownership: "birth",
6745
+ origin: "template",
6746
+ content: fillTemplate(ARTIFACT_TEMPLATES.tests.sheet, {
6747
+ name,
6748
+ root: `${tests}../`,
6749
+ setup: `${tests}setupStyles.js`
6750
+ })
6751
+ });
6752
+ }
5712
6753
  if (blueprintToMachinery(blueprint).browser) artifacts.push({
5713
6754
  path: "tests/setupBrowser.ts",
5714
6755
  group: "tests",
5715
6756
  ownership: "birth",
5716
6757
  origin: "template",
5717
6758
  environment: "browser",
5718
- content: ARTIFACT_TEMPLATES.tests.setup
6759
+ content: blueprint.journey ? ARTIFACT_TEMPLATES.tests.browser : ARTIFACT_TEMPLATES.tests.setup
5719
6760
  });
5720
- if (blueprint.src.includes("server") || blueprint.app.includes("server") || blueprint.bin) artifacts.push({
6761
+ if (blueprint.src.includes("server") || blueprint.app.includes("server") || blueprint.bin || blueprint.conformance) artifacts.push({
5721
6762
  path: "tests/setupServer.ts",
5722
6763
  group: "tests",
5723
6764
  ownership: "birth",
@@ -5732,13 +6773,40 @@ function blueprintToTestArtifacts(blueprint) {
5732
6773
  origin: "template",
5733
6774
  content: ARTIFACT_TEMPLATES.tests.setup
5734
6775
  });
5735
- if (blueprint.global) artifacts.push({
5736
- path: GLOBAL_SETUP_PATH,
5737
- group: "tests",
5738
- ownership: "birth",
5739
- origin: "template",
5740
- content: ARTIFACT_TEMPLATES.tests.global
5741
- });
6776
+ if (blueprint.global) {
6777
+ artifacts.push({
6778
+ path: GLOBAL_SETUP_PATH,
6779
+ group: "tests",
6780
+ ownership: "birth",
6781
+ origin: "template",
6782
+ content: ARTIFACT_TEMPLATES.tests.global.module
6783
+ });
6784
+ artifacts.push({
6785
+ path: "tests/setupGlobal.test.ts",
6786
+ group: "tests",
6787
+ ownership: "birth",
6788
+ origin: "template",
6789
+ content: ARTIFACT_TEMPLATES.tests.global.proof
6790
+ });
6791
+ }
6792
+ if (blueprint.journey && blueprint.app.includes("browser")) {
6793
+ const applications = ["browser", ...blueprintToFaces(blueprint).flatMap((extension) => extension.surface === "browser" && extension.axes.includes("app") ? [extension.name] : [])];
6794
+ for (const application of applications) artifacts.push({
6795
+ path: `tests/app/${application}/integration.test.ts`,
6796
+ group: "tests",
6797
+ ownership: "birth",
6798
+ origin: "template",
6799
+ environment: "browser",
6800
+ content: fillTemplate(ARTIFACT_TEMPLATES.tests.journey, {
6801
+ application,
6802
+ heading: serializeTypeScriptString(blueprint.name),
6803
+ imports: application === "vue" ? "import { createApp } from 'vue'\nimport App from '../../../app/vue/App.vue'\n" : "",
6804
+ mount: application === "vue" ? " const container = document.body.appendChild(document.createElement('main'))\n const app = createApp(App)\n" : "",
6805
+ arrival: application === "vue" ? " app.mount(container)\n" : " await import('../../../app/browser/main.js')\n",
6806
+ cleanup: application === "vue" ? " app.unmount()\n container.remove()\n" : " for (const main of page.getByRole('main').elements()) main.remove()\n"
6807
+ })
6808
+ });
6809
+ }
5742
6810
  for (const environment of blueprint.src) artifacts.push({
5743
6811
  path: `tests/src/${environment}/index.test.ts`,
5744
6812
  group: "tests",
@@ -5773,7 +6841,7 @@ function blueprintToTestArtifacts(blueprint) {
5773
6841
  label: serializeTypeScriptString(`app ${environment} entry`)
5774
6842
  })
5775
6843
  });
5776
- if (blueprint.src.length > 0) {
6844
+ if (blueprint.src.length > 0 || blueprintToSheets(blueprint).length > 0 || blueprint.themes) {
5777
6845
  const browser = blueprint.src.includes("browser");
5778
6846
  const distribution = ARTIFACT_TEMPLATES.tests.distribution;
5779
6847
  artifacts.push({
@@ -5842,7 +6910,12 @@ function blueprintToGuideArtifacts(blueprint) {
5842
6910
  const tests = [];
5843
6911
  const directories = [];
5844
6912
  const guide = `guides/${blueprint.name}.md`;
5845
- for (const environment of blueprint.src) {
6913
+ for (const environment of [
6914
+ ...blueprint.src,
6915
+ ...blueprintToFaces(blueprint).filter((extension) => extension.axes.includes("src")).map((extension) => extension.name),
6916
+ ...blueprintToSheets(blueprint),
6917
+ ...blueprint.themes ? ["styles/themes"] : []
6918
+ ]) {
5846
6919
  source.push(` - [\`src/${environment}\`](../src/${environment})`);
5847
6920
  tests.push(` - [\`tests/src/${environment}\`](../tests/src/${environment})`);
5848
6921
  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})`);
@@ -5852,7 +6925,7 @@ function blueprintToGuideArtifacts(blueprint) {
5852
6925
  tests.push(" - [`tests/src/bin`](../tests/src/bin)");
5853
6926
  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)`);
5854
6927
  }
5855
- for (const environment of blueprint.app) {
6928
+ for (const environment of [...blueprint.app, ...blueprintToFaces(blueprint).filter((extension) => extension.axes.includes("app")).map((extension) => extension.name)]) {
5856
6929
  source.push(` - [\`app/${environment}\`](../app/${environment})`);
5857
6930
  tests.push(` - [\`tests/app/${environment}\`](../tests/app/${environment})`);
5858
6931
  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})`);
@@ -5864,7 +6937,7 @@ function blueprintToGuideArtifacts(blueprint) {
5864
6937
  origin: "template",
5865
6938
  content: fillTemplate(ARTIFACT_TEMPLATES.guides.readme, {
5866
6939
  source: source.join("\n"),
5867
- tests: tests.join("\n"),
6940
+ 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") : ""),
5868
6941
  directories: directories.join("\n"),
5869
6942
  guide: blueprint.name
5870
6943
  })
@@ -5903,7 +6976,7 @@ function blueprintToGuideArtifacts(blueprint) {
5903
6976
  * ```
5904
6977
  */
5905
6978
  function blueprintToDocumentArtifacts(blueprint) {
5906
- const publishes = blueprint.src.length > 0;
6979
+ const publishes = blueprint.src.length > 0 || blueprintToSheets(blueprint).length > 0 || blueprint.themes;
5907
6980
  const name = publishes ? `@orkestrel/${blueprint.name}` : blueprint.name;
5908
6981
  const description = blueprint.description ?? (publishes ? `The ${name} package.` : `The ${name} application.`);
5909
6982
  return [{
@@ -6695,7 +7768,8 @@ function dependenciesToQuestions(dependencies, field, name, range) {
6695
7768
  * These do not, because each describes a workspace it can describe honestly but
6696
7769
  * will not create: a published axis of several environments without core,
6697
7770
  * whose manifest names a core build the workspace never runs; a showcase flag
6698
- * whose required browser axis is absent, which emits nothing; and an
7771
+ * or journey flag whose required browser axis is absent, which emits nothing; an unplaced browser
7772
+ * extension or a styles extension without the styles surface; and an
6699
7773
  * integration flag over fewer than two environments, whose seed does emit and
6700
7774
  * has nothing to compose across. Blocking any of them closed the gate for every
6701
7775
  * verb. The verbs that read an existing workspace need the plan the gate
@@ -6736,7 +7810,7 @@ function blueprintToQuestions(blueprint) {
6736
7810
  message: `${blueprint.engines} is not a supported minimum-Node floor.`,
6737
7811
  blocking: true
6738
7812
  });
6739
- if (blueprint.src.length === 0 && blueprint.app.length === 0) questions.push({
7813
+ if (blueprint.src.length === 0 && blueprint.app.length === 0 && blueprintToSheets(blueprint).length === 0 && !blueprint.themes) questions.push({
6740
7814
  field: "src",
6741
7815
  message: "A workspace declares at least one environment on src or app.",
6742
7816
  blocking: true,
@@ -6762,6 +7836,32 @@ function blueprintToQuestions(blueprint) {
6762
7836
  candidates: ENVIRONMENTS
6763
7837
  });
6764
7838
  }
7839
+ const extensions = /* @__PURE__ */ new Set();
7840
+ for (const extension of blueprint.extensions) {
7841
+ const selection = `${extension.surface}:${extension.name}`;
7842
+ if (extensions.has(selection)) questions.push({
7843
+ field: "extensions",
7844
+ message: `${selection} is declared more than once on extensions.`,
7845
+ blocking: true
7846
+ });
7847
+ extensions.add(selection);
7848
+ if (extension.surface === "browser") {
7849
+ if (extension.axes.length === 0) questions.push({
7850
+ field: "extensions",
7851
+ message: `${selection} occupies no axis.`,
7852
+ blocking: false
7853
+ });
7854
+ for (const axis of extension.axes) if (!blueprint[axis].includes("browser")) questions.push({
7855
+ field: "extensions",
7856
+ message: `${selection} occupies ${axis}, whose selection lacks browser.`,
7857
+ blocking: false
7858
+ });
7859
+ } else if (!blueprint.styles) questions.push({
7860
+ field: "extensions",
7861
+ message: `${selection} extends a styles surface this workspace does not declare.`,
7862
+ blocking: false
7863
+ });
7864
+ }
6765
7865
  if (blueprint.integration && blueprint.src.length + blueprint.app.length < 2) questions.push({
6766
7866
  field: "integration",
6767
7867
  message: "integration drives features across environments, and this workspace declares fewer than two, so its seed composes nothing.",
@@ -6991,6 +8091,9 @@ function createBlueprint(name, input) {
6991
8091
  keywords: input?.keywords ?? [],
6992
8092
  src: input?.src ?? [],
6993
8093
  app: input?.app ?? [],
8094
+ extensions: input?.extensions ?? [],
8095
+ styles: input?.styles ?? false,
8096
+ themes: input?.themes ?? false,
6994
8097
  dependencies: input?.dependencies ?? [],
6995
8098
  peers: input?.peers ?? [],
6996
8099
  extras: input?.extras ?? [],
@@ -7316,6 +8419,6 @@ var Compiler = class {
7316
8419
  }
7317
8420
  };
7318
8421
  //#endregion
7319
- export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, CANON_PATHS, CATALOG_AGENT_PATH, CATALOG_CLOSING_MARKER, CATALOG_OPENING_MARKER, CONFIG_TEMPLATES, CONFORMANCE_TEST_PATH, CONTROL_CHARACTER_PATTERN, Compiler, DECLARATION_DEV_DEPENDENCIES, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, DISTRIBUTION_TEST_PATH, ENGINES_PATTERN, ENVIRONMENTS, EXECUTABLE_PATHS, EXTRA_RANGE_PATTERN, FLOOR_RANGE_PATTERN, FOREIGN_NAME_PATTERN, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_INVENTORY_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, JOURNEY_CONFIG_PATH, MANIFEST_PATH, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_AUDIT_FINDINGS, MAX_COLLECTION_ITEMS, MAX_DEPENDENCY_NAME_LENGTH, MAX_MANIFEST_BYTES, MAX_NAME_LENGTH, MAX_PATH_LENGTH, MAX_RANGE_LENGTH, MAX_REGISTRY_BYTES, MAX_SCRIPT_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, MAX_TOTAL_REGISTRY_BYTES, MINIMUM_NODE_VERSION, MINIMUM_NPM_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, PRINT_WIDTH, REFERENCE_PATHS, RELEASE_PROOF_COMMAND, SEED_GUIDE_PATHS, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SKILLS_CONFIG_PATH, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, ScaffoldError, TAB_WIDTH, TARGET_SKILL_NAMES, VERSION_PATTERN, WORKSPACE_DEV_ENGINES, WORKSPACE_OWNED_PATHS, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToGuideArtifacts, blueprintToHostArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToOrchestrationArtifacts, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSourceArtifacts, blueprintToTestArtifacts, blueprintToWritableScripts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, contentToHex, createBlueprint, dependenciesToQuestions, extractRangeMajor, extractVersion, inferDrift, inferGroup, isArtifact, isAudit, isBlueprint, isCanonPath, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDeferredPath, isDependency, isDependencyName, isEnvironment, isFinding, isFloorPath, isGroup, isGroups, isHex, isManifestScript, isMirror, isOverride, isPath, isPlan, isQuestion, isRetainedPath, isScaffoldError, isSnapshot, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesPrintWidth, matchesRange, nameToGuide, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, renderSkillPointer, replaceManifestRanges, replaceManifestScripts, replacePlanRanges, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
8422
+ export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, AXES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, BROWSE_DEV_DEPENDENCIES, CANON_PATHS, CATALOG_AGENT_PATH, CATALOG_CLOSING_MARKER, CATALOG_OPENING_MARKER, CONFIG_TEMPLATES, CONFORMANCE_TEST_PATH, CONTROL_CHARACTER_PATTERN, Compiler, DECLARATION_DEV_DEPENDENCIES, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, DISTRIBUTION_TEST_PATH, ENGINES_PATTERN, ENVIRONMENTS, EXECUTABLE_PATHS, EXTRA_RANGE_PATTERN, FLOOR_RANGE_PATTERN, FOREIGN_NAME_PATTERN, FRAMEWORKS, FRAMEWORK_MATRIX, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_INVENTORY_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, JOURNEY_CONFIG_PATH, MANIFEST_PATH, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_AUDIT_FINDINGS, MAX_COLLECTION_ITEMS, MAX_DEPENDENCY_NAME_LENGTH, MAX_MANIFEST_BYTES, MAX_NAME_LENGTH, MAX_PATH_LENGTH, MAX_RANGE_LENGTH, MAX_REGISTRY_BYTES, MAX_SCRIPT_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, MAX_TOTAL_REGISTRY_BYTES, MINIMUM_NODE_VERSION, MINIMUM_NPM_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, PRINT_WIDTH, REFERENCE_PATHS, RELEASE_PROOF_COMMAND, RESERVED_SHEET_NAMES, SEED_GUIDE_PATHS, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHEET_ENTRY_NAME, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SHOWCASE_PAGES_PATH, SKILLS_CONFIG_PATH, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, STYLES_DEV_DEPENDENCIES, STYLES_ENTRY_PATH, SURFACES, ScaffoldError, TAB_WIDTH, TARGET_SKILL_NAMES, THEMES_BARREL_PATH, THEMES_ENTRY_PATH, VERSION_PATTERN, WORKSPACE_DEV_ENGINES, WORKSPACE_OWNED_PATHS, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToExports, blueprintToFaces, blueprintToGuideArtifacts, blueprintToHostArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToOrchestrationArtifacts, blueprintToProjects, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSheets, blueprintToSourceArtifacts, blueprintToTestArtifacts, blueprintToWritableScripts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, contentToHex, createBlueprint, dependenciesToQuestions, extractRangeMajor, extractVersion, inferDrift, inferGroup, isArtifact, isAudit, isBlueprint, isBrowserExtension, isCanonPath, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDeferredPath, isDependency, isDependencyName, isEnvironment, isExtension, isFinding, isFloorPath, isGroup, isGroups, isHex, isManifestScript, isMirror, isOverride, isPath, isPlan, isQuestion, isRetainedPath, isScaffoldError, isSheetName, isSnapshot, isStylesExtension, isSurface, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesPrintWidth, matchesRange, nameToGuide, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseExtension, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, renderSkillPointer, replaceManifestRanges, replaceManifestScripts, replacePlanRanges, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
7320
8423
 
7321
8424
  //# sourceMappingURL=index.js.map