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