@orkestrel/scaffold 0.0.50 → 0.0.52

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 (66) hide show
  1. package/dist/bin/main.js +46 -17
  2. package/dist/bin/main.js.map +1 -1
  3. package/dist/host/AGENTS.md +2 -2
  4. package/dist/host/CLAUDE.md +3 -1
  5. package/dist/host/agents/orchestration.md +109 -274
  6. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +12 -8
  7. package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +2 -2
  8. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +15 -11
  9. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +38 -12
  10. package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +107 -0
  11. package/dist/host/agents/skills/{orkestrel-human-journey → orkestrel-prove-journey}/SKILL.md +1 -1
  12. package/dist/host/agents/skills/{orkestrel-human-journey → orkestrel-prove-journey}/agents/openai.yaml +1 -1
  13. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +77 -0
  14. package/dist/host/agents/skills/orkestrel-publish/agents/openai.yaml +4 -0
  15. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +97 -0
  16. package/dist/host/agents/skills/orkestrel-publish/references/window.md +95 -0
  17. package/dist/host/agents/templates/brief.md +157 -0
  18. package/dist/host/{codex/agents/claude.toml → agents/transports/claude.md} +16 -10
  19. package/dist/host/{claude/agents → agents/transports}/codex.md +13 -12
  20. package/dist/host/claude/agents/analyst.md +11 -10
  21. package/dist/host/claude/agents/application.md +6 -3
  22. package/dist/host/claude/agents/builder.md +11 -10
  23. package/dist/host/claude/agents/checker.md +20 -10
  24. package/dist/host/claude/agents/grok.md +8 -7
  25. package/dist/host/claude/agents/implementer.md +7 -4
  26. package/dist/host/claude/agents/orkestrel.md +27 -21
  27. package/dist/host/claude/agents/planner.md +13 -5
  28. package/dist/host/claude/agents/researcher.md +8 -7
  29. package/dist/host/claude/agents/reviewer.md +6 -4
  30. package/dist/host/claude/agents/scout.md +6 -7
  31. package/dist/host/claude/agents/sol.md +9 -8
  32. package/dist/host/claude/agents/verifier.md +6 -3
  33. package/dist/host/claude/rules/architecture.md +6 -6
  34. package/dist/host/claude/rules/documentation.md +2 -0
  35. package/dist/host/claude/rules/tests.md +4 -2
  36. package/dist/host/claude/rules/workspace.md +1 -1
  37. package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +12 -8
  38. package/dist/host/claude/skills/orkestrel-debrief/SKILL.md +1 -1
  39. package/dist/host/claude/skills/{orkestrel-human-journey → orkestrel-prove-journey}/SKILL.md +2 -2
  40. package/dist/host/claude/skills/orkestrel-publish/SKILL.md +12 -0
  41. package/dist/host/codex/agents/analyst.toml +3 -0
  42. package/dist/host/codex/agents/application.toml +3 -0
  43. package/dist/host/codex/agents/builder.toml +10 -7
  44. package/dist/host/codex/agents/checker.toml +14 -3
  45. package/dist/host/codex/agents/grok.toml +5 -2
  46. package/dist/host/codex/agents/implementer.toml +3 -2
  47. package/dist/host/codex/agents/opus.toml +4 -3
  48. package/dist/host/codex/agents/orkestrel.toml +14 -9
  49. package/dist/host/codex/agents/planner.toml +7 -5
  50. package/dist/host/codex/agents/researcher.toml +6 -4
  51. package/dist/host/codex/agents/reviewer.toml +4 -3
  52. package/dist/host/codex/agents/scout.toml +4 -1
  53. package/dist/host/codex/agents/verifier.toml +3 -2
  54. package/dist/host/codex/config.toml +1 -1
  55. package/dist/host/guides/scaffold.md +48 -44
  56. package/dist/host/manifest.json +132 -84
  57. package/dist/host/tests/config.test.ts +57 -0
  58. package/dist/src/core/index.cjs +455 -406
  59. package/dist/src/core/index.cjs.map +1 -1
  60. package/dist/src/core/index.d.cts +51 -48
  61. package/dist/src/core/index.d.ts +51 -48
  62. package/dist/src/core/index.js +455 -406
  63. package/dist/src/core/index.js.map +1 -1
  64. package/package.json +2 -2
  65. /package/dist/host/agents/skills/{orkestrel-human-journey → orkestrel-prove-journey}/references/captures.md +0 -0
  66. /package/dist/host/agents/skills/{orkestrel-human-journey → orkestrel-prove-journey}/references/layer.md +0 -0
@@ -3,7 +3,7 @@ import { fillTemplate } from "@orkestrel/template";
3
3
  import { Emitter } from "@orkestrel/emitter";
4
4
  var package_default = {
5
5
  name: "@orkestrel/scaffold",
6
- version: "0.0.50",
6
+ version: "0.0.52",
7
7
  description: "Scaffold workspaces with five commands: new, audit, repair, catalog, and overwrite.",
8
8
  keywords: [
9
9
  "audit",
@@ -99,7 +99,7 @@ var package_default = {
99
99
  "@microsoft/api-extractor": "^7.59.0",
100
100
  "@orkestrel/guide": "^0.0.13",
101
101
  "@orkestrel/html": "^0.0.5",
102
- "@orkestrel/probe": "^0.0.3",
102
+ "@orkestrel/probe": "^0.0.4",
103
103
  "@orkestrel/test": "^0.0.11",
104
104
  "@types/node": "^26.2.0",
105
105
  "@vitest/browser-playwright": "^4.1.11",
@@ -224,6 +224,8 @@ var HOST_PATHS = Object.freeze([
224
224
  "LICENSE",
225
225
  ".agents/orchestration.md",
226
226
  ".agents/skills",
227
+ ".agents/templates",
228
+ ".agents/transports",
227
229
  ".claude/agents",
228
230
  ".claude/rules",
229
231
  ".claude/skills",
@@ -560,7 +562,7 @@ var CONFIG_TEMPLATES = Object.freeze({
560
562
  }
561
563
  `,
562
564
  vite: `import type { {{viteTypes}} } from 'vite'
563
- {{imports}}import { defineConfig, mergeConfig } from 'vitest/config'
565
+ {{imports}}import { defineConfig } from 'vitest/config'
564
566
  import manifest from './package.json' with { type: 'json' }
565
567
  import tsconfig from './tsconfig.json' with { type: 'json' }
566
568
  {{helpers}}{{browsers}}import { fileURLToPath, URL } from 'node:url'
@@ -599,160 +601,140 @@ const resolve = {
599
601
  }),
600
602
  factories: Object.freeze({
601
603
  src: Object.freeze({
602
- core: `export const srcCore = (options?: UserConfig): UserConfig =>
603
- mergeConfig(
604
- {
605
- resolve,
606
- publicDir: false,
607
- build: {
608
- emptyOutDir: true,
609
- sourcemap: true,
610
- minify: false,
611
- rolldownOptions: { onLog: enforceBuildLog },
612
- },
613
- test: {
614
- name: { label: 'src:core', color: 'magenta' },
615
- include: ['tests/src/core/**/*.test.ts'],
616
- setupFiles: ['./tests/setup.ts'],
617
- environment: 'node',
618
- browser: { enabled: false },
619
- },
620
- },
621
- options ?? {},
622
- )
604
+ core: `export const srcCore = (): UserConfig => ({
605
+ resolve,
606
+ publicDir: false,
607
+ build: {
608
+ emptyOutDir: true,
609
+ sourcemap: true,
610
+ minify: false,
611
+ rolldownOptions: { onLog: enforceBuildLog },
612
+ },
613
+ test: {
614
+ name: { label: 'src:core', color: 'magenta' },
615
+ include: ['tests/src/core/**/*.test.ts'],
616
+ setupFiles: ['./tests/setup.ts'],
617
+ environment: 'node',
618
+ browser: { enabled: false },
619
+ },
620
+ })
623
621
  `,
624
- browser: `export const srcBrowser = (options?: UserConfig): UserConfig =>
625
- mergeConfig(
626
- {
627
- resolve,
628
- publicDir: false,
629
- plugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],
630
- build: {
631
- emptyOutDir: true,
632
- sourcemap: true,
633
- minify: false,
634
- lib: {
635
- entry: resolveWorkspacePath('src/browser/index.ts'),
636
- formats: ['es'],
637
- fileName: () => 'index.js',
638
- },
639
- outDir: 'dist/src/browser',
640
- rolldownOptions: {
641
- onLog: enforceBuildLog,
642
- {{external}}
622
+ browser: `export const srcBrowser = (): UserConfig => ({
623
+ resolve,
624
+ publicDir: false,
625
+ plugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],
626
+ build: {
627
+ emptyOutDir: true,
628
+ sourcemap: true,
629
+ minify: false,
630
+ lib: {
631
+ entry: resolveWorkspacePath('src/browser/index.ts'),
632
+ formats: ['es'],
633
+ fileName: () => 'index.js',
634
+ },
635
+ outDir: 'dist/src/browser',
636
+ rolldownOptions: {
637
+ onLog: enforceBuildLog,
638
+ {{external}}
643
639
  {{output}}
644
- },
645
- },
646
- test: {
647
- name: { label: 'src:browser', color: 'yellow' },
648
- include: ['tests/src/browser/**/*.test.ts'],
649
- {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
640
+ },
641
+ },
642
+ test: {
643
+ name: { label: 'src:browser', color: 'yellow' },
644
+ include: ['tests/src/browser/**/*.test.ts'],
645
+ {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
650
646
  {{global}}
651
- browser: {
652
- enabled: true,
653
- provider: playwright(browserOptions),
654
- instances: [{ browser: 'chromium', headless: true }],
655
- },
656
- fileParallelism: false,
657
- },
647
+ browser: {
648
+ enabled: true,
649
+ provider: playwright(browserOptions),
650
+ instances: [{ browser: 'chromium', headless: true }],
658
651
  },
659
- options ?? {},
660
- )
652
+ fileParallelism: false,
653
+ },
654
+ })
661
655
  `,
662
- server: `export const srcServer = (options?: UserConfig): UserConfig =>
663
- mergeConfig(
664
- {
665
- resolve,
666
- publicDir: false,
667
- plugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],
668
- build: {
669
- emptyOutDir: true,
670
- sourcemap: true,
671
- minify: false,
672
- lib: {
673
- entry: resolveWorkspacePath('src/server/index.ts'),
674
- formats: ['es', 'cjs'],
675
- fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
676
- },
677
- outDir: 'dist/src/server',
678
- target: 'node22',
679
- rolldownOptions: {
680
- onLog: enforceBuildLog,
681
- platform: 'node',
682
- {{external}}
656
+ server: `export const srcServer = (): UserConfig => ({
657
+ resolve,
658
+ publicDir: false,
659
+ plugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],
660
+ build: {
661
+ emptyOutDir: true,
662
+ sourcemap: true,
663
+ minify: false,
664
+ lib: {
665
+ entry: resolveWorkspacePath('src/server/index.ts'),
666
+ formats: ['es', 'cjs'],
667
+ fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
668
+ },
669
+ outDir: 'dist/src/server',
670
+ target: 'node22',
671
+ rolldownOptions: {
672
+ onLog: enforceBuildLog,
673
+ platform: 'node',
674
+ {{external}}
683
675
  {{output}}
684
- },
685
- },
686
- test: {
687
- name: { label: 'src:server', color: 'red' },
688
- include: ['tests/src/server/**/*.test.ts'],
689
- {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
690
- environment: 'node',
691
- browser: { enabled: false },
692
- },
693
676
  },
694
- options ?? {},
695
- )
677
+ },
678
+ test: {
679
+ name: { label: 'src:server', color: 'red' },
680
+ include: ['tests/src/server/**/*.test.ts'],
681
+ {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
682
+ environment: 'node',
683
+ browser: { enabled: false },
684
+ },
685
+ })
696
686
  `,
697
- bin: `export const srcBin = (options?: UserConfig): UserConfig =>
698
- mergeConfig(
699
- {
700
- resolve,
701
- publicDir: false,
702
- plugins: [outputBoundary('dist/bin')],
703
- build: {
704
- emptyOutDir: true,
705
- sourcemap: true,
706
- minify: false,
707
- lib: {
708
- entry: resolveWorkspacePath('${BIN_ENTRY_PATH}'),
709
- formats: ['es'],
710
- fileName: () => 'main.js',
711
- },
712
- outDir: 'dist/bin',
713
- target: 'node22',
714
- rolldownOptions: {
715
- onLog: enforceBuildLog,
716
- external: (id: string) =>
717
- id.startsWith('node:') ||
718
- id.startsWith('@orkestrel/') ||
719
- id.startsWith('@src/') ||
720
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),
721
- },
722
- },
723
- test: {
724
- name: { label: 'src:bin', color: 'yellow' },
725
- include: ['tests/src/bin/**/*.test.ts'],
726
- setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
727
- environment: 'node',
728
- browser: { enabled: false },
729
- // A bin test drives the real executable over a real temporary repository, so it
730
- // spends seconds in process startup and filesystem work rather than milliseconds.
731
- // Vitest's five-second default clears one alone and times out under a full suite.
732
- testTimeout: 15_000,
733
- },
687
+ bin: `export const srcBin = (): UserConfig => ({
688
+ resolve,
689
+ publicDir: false,
690
+ plugins: [outputBoundary('dist/bin')],
691
+ build: {
692
+ emptyOutDir: true,
693
+ sourcemap: true,
694
+ minify: false,
695
+ lib: {
696
+ entry: resolveWorkspacePath('${BIN_ENTRY_PATH}'),
697
+ formats: ['es'],
698
+ fileName: () => 'main.js',
734
699
  },
735
- options ?? {},
736
- )
700
+ outDir: 'dist/bin',
701
+ target: 'node22',
702
+ rolldownOptions: {
703
+ onLog: enforceBuildLog,
704
+ external: (id: string) =>
705
+ id.startsWith('node:') ||
706
+ id.startsWith('@orkestrel/') ||
707
+ id.startsWith('@src/') ||
708
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),
709
+ },
710
+ },
711
+ test: {
712
+ name: { label: 'src:bin', color: 'yellow' },
713
+ include: ['tests/src/bin/**/*.test.ts'],
714
+ setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
715
+ environment: 'node',
716
+ browser: { enabled: false },
717
+ // A bin test drives the real executable over a real temporary repository, so it
718
+ // spends seconds in process startup and filesystem work rather than milliseconds.
719
+ // Vitest's five-second default clears one alone and times out under a full suite.
720
+ testTimeout: 15_000,
721
+ },
722
+ })
737
723
  `
738
724
  }),
739
725
  app: Object.freeze({
740
- core: `export const appCore = (options?: UserConfig): UserConfig =>
741
- mergeConfig(
742
- {
743
- resolve,
744
- publicDir: false,
745
- plugins: [environmentBoundary('app/core')],
746
- test: {
747
- name: { label: 'app:core', color: 'cyan' },
748
- include: ['tests/app/core/**/*.test.ts'],
749
- setupFiles: ['./tests/setup.ts'],
750
- environment: 'node',
751
- browser: { enabled: false },
752
- },
753
- },
754
- options ?? {},
755
- )
726
+ core: `export const appCore = (): UserConfig => ({
727
+ resolve,
728
+ publicDir: false,
729
+ plugins: [environmentBoundary('app/core')],
730
+ test: {
731
+ name: { label: 'app:core', color: 'cyan' },
732
+ include: ['tests/app/core/**/*.test.ts'],
733
+ setupFiles: ['./tests/setup.ts'],
734
+ environment: 'node',
735
+ browser: { enabled: false },
736
+ },
737
+ })
756
738
  `,
757
739
  browser: `function applicationBrowser(showcase: boolean): UserConfig {
758
740
  const output = showcase ? 'dist/showcase' : 'dist/app/browser'
@@ -784,197 +766,157 @@ const resolve = {
784
766
  }
785
767
  }
786
768
 
787
- export function appBrowser(options?: UserConfig): UserConfig {
788
- return mergeConfig(applicationBrowser(false), options ?? {})
769
+ export function appBrowser(): UserConfig {
770
+ return applicationBrowser(false)
789
771
  }
790
772
  {{showcaseFactory}}`,
791
- server: `export const appServer = (options?: UserConfig): UserConfig =>
792
- mergeConfig(
793
- {
794
- resolve,
795
- publicDir: false,
796
- plugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],
797
- build: {
798
- emptyOutDir: true,
799
- lib: {
800
- entry: resolveWorkspacePath('app/server/main.ts'),
801
- formats: ['cjs'],
802
- fileName: () => 'main.cjs',
803
- },
804
- outDir: resolveWorkspacePath('dist/app/server'),
805
- target: 'node22',
806
- rolldownOptions: {
807
- onLog: enforceBuildLog,
808
- external: (id: string) => id.startsWith('node:'),
809
- },
810
- },
811
- test: {
812
- name: { label: 'app:server', color: 'green' },
813
- include: ['tests/app/server/**/*.test.ts'],
814
- setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
815
- environment: 'node',
816
- browser: { enabled: false },
817
- },
773
+ server: `export const appServer = (): UserConfig => ({
774
+ resolve,
775
+ publicDir: false,
776
+ plugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],
777
+ build: {
778
+ emptyOutDir: true,
779
+ lib: {
780
+ entry: resolveWorkspacePath('app/server/main.ts'),
781
+ formats: ['cjs'],
782
+ fileName: () => 'main.cjs',
818
783
  },
819
- options ?? {},
820
- )
784
+ outDir: resolveWorkspacePath('dist/app/server'),
785
+ target: 'node22',
786
+ rolldownOptions: {
787
+ onLog: enforceBuildLog,
788
+ external: (id: string) => id.startsWith('node:'),
789
+ },
790
+ },
791
+ test: {
792
+ name: { label: 'app:server', color: 'green' },
793
+ include: ['tests/app/server/**/*.test.ts'],
794
+ setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
795
+ environment: 'node',
796
+ browser: { enabled: false },
797
+ },
798
+ })
821
799
  `
822
800
  }),
823
- policy: `export const policy = (options?: UserConfig): UserConfig =>
824
- mergeConfig(
825
- {
826
- resolve,
827
- test: {
828
- name: { label: 'policy', color: 'white' },
829
- include: ['tests/policy.test.ts'],
830
- setupFiles: ['./tests/setup.ts'],
831
- environment: 'node',
832
- browser: { enabled: false },
833
- },
834
- },
835
- options ?? {},
836
- )
801
+ policy: `export const policy = (): UserConfig => ({
802
+ resolve,
803
+ test: {
804
+ name: { label: 'policy', color: 'white' },
805
+ include: ['tests/policy.test.ts'],
806
+ setupFiles: ['./tests/setup.ts'],
807
+ environment: 'node',
808
+ browser: { enabled: false },
809
+ },
810
+ })
837
811
  `,
838
- config: `export const config = (options?: UserConfig): UserConfig =>
839
- mergeConfig(
840
- {
841
- resolve,
842
- test: {
843
- name: { label: 'config', color: 'yellow' },
844
- include: ['tests/config.test.ts'],
845
- setupFiles: ['./tests/setup.ts'],
846
- environment: 'node',
847
- browser: { enabled: false },
848
- // A config test validates every target wrapper and runs the real linter twice with
849
- // 15-second child caps, so this budget clears both caps and reports their diagnostics.
850
- testTimeout: 45_000,
851
- },
852
- },
853
- options ?? {},
854
- )
812
+ config: `export const config = (): UserConfig => ({
813
+ resolve,
814
+ test: {
815
+ name: { label: 'config', color: 'yellow' },
816
+ include: ['tests/config.test.ts'],
817
+ setupFiles: ['./tests/setup.ts'],
818
+ environment: 'node',
819
+ browser: { enabled: false },
820
+ // A config test validates every target wrapper and runs the real linter twice with
821
+ // 15-second child caps, so this budget clears both caps and reports their diagnostics.
822
+ testTimeout: 45_000,
823
+ },
824
+ })
855
825
  `,
856
- setup: `export const setup = (options?: UserConfig): UserConfig =>
857
- mergeConfig(
858
- {
859
- resolve,
860
- test: {
861
- name: { label: 'setup', color: 'white' },
862
- include: ['tests/setup*.test.ts'],
863
- setupFiles: ['./tests/setup.ts'],
864
- environment: 'node',
865
- browser: { enabled: false },
866
- },
867
- },
868
- options ?? {},
869
- )
826
+ setup: `export const setup = (): UserConfig => ({
827
+ resolve,
828
+ test: {
829
+ name: { label: 'setup', color: 'white' },
830
+ include: ['tests/setup*.test.ts'],
831
+ setupFiles: ['./tests/setup.ts'],
832
+ environment: 'node',
833
+ browser: { enabled: false },
834
+ },
835
+ })
870
836
  `,
871
- guides: `export const guides = (options?: UserConfig): UserConfig =>
872
- mergeConfig(
873
- {
874
- resolve,
875
- test: {
876
- name: { label: 'guides', color: 'green' },
877
- include: ['${GUIDES_TEST_PATH}'],
878
- exclude: ['tests/src/**/*.test.ts', 'tests/app/**/*.test.ts', 'tests/setup.test.ts'],
879
- setupFiles: ['./tests/setup.ts'],
880
- environment: 'node',
881
- browser: { enabled: false },
882
- },
883
- },
884
- options ?? {},
885
- )
837
+ guides: `export const guides = (): UserConfig => ({
838
+ resolve,
839
+ test: {
840
+ name: { label: 'guides', color: 'green' },
841
+ include: ['${GUIDES_TEST_PATH}'],
842
+ exclude: ['tests/src/**/*.test.ts', 'tests/app/**/*.test.ts', 'tests/setup.test.ts'],
843
+ setupFiles: ['./tests/setup.ts'],
844
+ environment: 'node',
845
+ browser: { enabled: false },
846
+ },
847
+ })
886
848
  `,
887
849
  conformance: `// Where this package drifts from the official tooling it stays compatible with.
888
850
  // The subject is this package, so the proof is hermetic and stays in \`npm test\`.
889
- export const conformance = (options?: UserConfig): UserConfig =>
890
- mergeConfig(
891
- {
892
- resolve,
893
- test: {
894
- name: { label: 'conformance', color: 'magenta' },
895
- include: ['${CONFORMANCE_TEST_PATH}'],
896
- setupFiles: ['./tests/setup.ts'],
897
- environment: 'node',
898
- browser: { enabled: false },
899
- },
900
- },
901
- options ?? {},
902
- )
851
+ export const conformance = (): UserConfig => ({
852
+ resolve,
853
+ test: {
854
+ name: { label: 'conformance', color: 'magenta' },
855
+ include: ['${CONFORMANCE_TEST_PATH}'],
856
+ setupFiles: ['./tests/setup.ts'],
857
+ environment: 'node',
858
+ browser: { enabled: false },
859
+ },
860
+ })
903
861
  `,
904
862
  service: `// The live external services this package drives. It starts nothing itself:
905
863
  // \`scripts/service.sh\` provisions, \`tests/setupService.ts\` proves readiness, and
906
864
  // the project stays out of \`npm test\` because a real service answers it.
907
- export const service = (options?: UserConfig): UserConfig =>
908
- mergeConfig(
909
- {
910
- resolve,
911
- test: {
912
- name: { label: 'service', color: 'red' },
913
- include: ['${SERVICE_TEST_INCLUDE}'],
914
- setupFiles: ['./tests/setup.ts', './tests/setupService.ts'],
915
- environment: 'node',
916
- browser: { enabled: false },
917
- testTimeout: 120_000,
918
- hookTimeout: 120_000,
919
- fileParallelism: false,
920
- },
921
- },
922
- options ?? {},
923
- )
865
+ export const service = (): UserConfig => ({
866
+ resolve,
867
+ test: {
868
+ name: { label: 'service', color: 'red' },
869
+ include: ['${SERVICE_TEST_INCLUDE}'],
870
+ setupFiles: ['./tests/setup.ts', './tests/setupService.ts'],
871
+ environment: 'node',
872
+ browser: { enabled: false },
873
+ testTimeout: 120_000,
874
+ hookTimeout: 120_000,
875
+ fileParallelism: false,
876
+ },
877
+ })
924
878
  `,
925
- distribution: `export const distribution = (options?: UserConfig): UserConfig =>
926
- mergeConfig(
927
- {
928
- resolve,
929
- test: {
930
- name: { label: 'distribution', color: 'cyan' },
931
- include: ['${DISTRIBUTION_TEST_PATH}'],
932
- setupFiles: ['./tests/setup.ts'],
933
- environment: 'node',
934
- testTimeout: 120_000,
935
- hookTimeout: 120_000,
936
- fileParallelism: false,
937
- },
938
- },
939
- options ?? {},
940
- )
879
+ distribution: `export const distribution = (): UserConfig => ({
880
+ resolve,
881
+ test: {
882
+ name: { label: 'distribution', color: 'cyan' },
883
+ include: ['${DISTRIBUTION_TEST_PATH}'],
884
+ setupFiles: ['./tests/setup.ts'],
885
+ environment: 'node',
886
+ testTimeout: 120_000,
887
+ hookTimeout: 120_000,
888
+ fileParallelism: false,
889
+ },
890
+ })
941
891
  `,
942
892
  probe: `// A workbench, not a proof. No gate selects this project. Run in test mode by the
943
893
  // \`test:probe\` script, it collects \`tmp/probe/**/*.test.ts\`. Run in benchmark mode by the
944
894
  // \`test:bench\` script, the same workbench also collects \`tests/**/*.test.ts\` for a \`bench\` block,
945
895
  // so a suite may carry a bench beside its ordinary tests without a second project. The mode
946
896
  // guard around each \`bench\` call keeps it out of test mode, so it never executes there.
947
- export const probe = (options?: UserConfig): UserConfig =>
948
- mergeConfig(
949
- {
950
- resolve,
951
- test: {
952
- name: { label: 'probe', color: 'gray' },
953
- include: ['tmp/probe/**/*.test.ts'],
954
- setupFiles: ['./tests/setup.ts'],
955
- environment: 'node',
956
- browser: { enabled: false },
957
- fileParallelism: false,
958
- pool: 'threads',
959
- benchmark: { include: ['tmp/probe/**/*.test.ts', 'tests/**/*.test.ts'] },
960
- },
961
- },
962
- options ?? {},
963
- )
897
+ export const probe = (): UserConfig => ({
898
+ resolve,
899
+ test: {
900
+ name: { label: 'probe', color: 'black' },
901
+ include: ['tmp/probe/**/*.test.ts'],
902
+ setupFiles: ['./tests/setup.ts'],
903
+ environment: 'node',
904
+ browser: { enabled: false },
905
+ fileParallelism: false,
906
+ pool: 'threads',
907
+ benchmark: { include: ['tmp/probe/**/*.test.ts', 'tests/**/*.test.ts'] },
908
+ },
909
+ })
964
910
  `,
965
- integration: `export const integration = (options?: UserConfig): UserConfig =>
966
- mergeConfig(
967
- {
968
- resolve,
969
- test: {
970
- name: { label: 'integration', color: 'blue' },
971
- include: ['${INTEGRATION_TEST_PATH}'],
972
- setupFiles: ['./tests/setup.ts'],
973
- {{global}} environment: 'node',
974
- },
975
- },
976
- options ?? {},
977
- )
911
+ integration: `export const integration = (): UserConfig => ({
912
+ resolve,
913
+ test: {
914
+ name: { label: 'integration', color: 'blue' },
915
+ include: ['${INTEGRATION_TEST_PATH}'],
916
+ setupFiles: ['./tests/setup.ts'],
917
+ {{global}} environment: 'node',
918
+ },
919
+ })
978
920
  `
979
921
  }),
980
922
  tsconfigs: Object.freeze({
@@ -1102,13 +1044,13 @@ export const probe = (options?: UserConfig): UserConfig =>
1102
1044
  }),
1103
1045
  vites: Object.freeze({
1104
1046
  src: Object.freeze({
1105
- core: `import { defineConfig } from 'vite'
1047
+ core: `import { defineConfig, mergeConfig } from 'vite'
1106
1048
  import dts from 'vite-plugin-dts'
1107
1049
  import { environmentBoundary, outputBoundary } from '../helpers.js'
1108
1050
  import { peers, srcCore, resolveWorkspacePath } from '../../vite.config.ts'
1109
1051
 
1110
1052
  export default defineConfig(
1111
- srcCore({
1053
+ mergeConfig(srcCore(), {
1112
1054
  publicDir: false,
1113
1055
  plugins: [
1114
1056
  outputBoundary('dist/src/core'),
@@ -1130,7 +1072,7 @@ export default defineConfig(
1130
1072
  lib: {
1131
1073
  entry: resolveWorkspacePath('src/core/index.ts'),
1132
1074
  formats: ['es', 'cjs'],
1133
- fileName: (format) => (format === 'es' ? 'index.js' : 'index.cjs'),
1075
+ fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
1134
1076
  },
1135
1077
  outDir: 'dist/src/core',
1136
1078
  rolldownOptions: {
@@ -1143,17 +1085,17 @@ export default defineConfig(
1143
1085
  }),
1144
1086
  )
1145
1087
  `,
1146
- browser: `import { defineConfig } from 'vite'
1088
+ browser: `import { defineConfig, mergeConfig } from 'vite'
1147
1089
  import dts from 'vite-plugin-dts'
1148
1090
  import { srcBrowser, resolveWorkspacePath } from '../../vite.config.ts'
1149
1091
 
1150
1092
  // vite-plugin-dts rolls this face into one declaration, and the roll-up reaches
1151
1093
  // src/core through a relative source path the tarball does not carry. The path
1152
1094
  // keeps each source module's own depth, so a module in a browser subfolder emits
1153
- // one that leaves dist/src entirely. The rewrite below externalizes core through
1154
- // the package's own published root export, on the final roll-up only.
1095
+ // one that leaves dist/src entirely. The following rewrite externalizes core
1096
+ // through the package's own published root export, on the final roll-up only.
1155
1097
  export default defineConfig(
1156
- srcBrowser({
1098
+ mergeConfig(srcBrowser(), {
1157
1099
  plugins: [
1158
1100
  dts({
1159
1101
  tsconfigPath: resolveWorkspacePath('configs/src/tsconfig.browser.json'),
@@ -1168,16 +1110,16 @@ export default defineConfig(
1168
1110
  }),
1169
1111
  )
1170
1112
  `,
1171
- server: `import { defineConfig } from 'vite'
1113
+ server: `import { defineConfig, mergeConfig } from 'vite'
1172
1114
  import dts from 'vite-plugin-dts'
1173
1115
  import { srcServer, resolveWorkspacePath } from '../../vite.config.ts'
1174
1116
 
1175
1117
  // vite-plugin-dts rolls this face into one declaration, and the roll-up reaches
1176
- // src/core through a relative source path the tarball does not carry. The rewrite
1177
- // below externalizes core through the package's own published root export, on the
1178
- // final roll-up only.
1118
+ // src/core through a relative source path the tarball does not carry. The
1119
+ // following rewrite externalizes core through the package's own published root
1120
+ // export, on the final roll-up only.
1179
1121
  export default defineConfig(
1180
- srcServer({
1122
+ mergeConfig(srcServer(), {
1181
1123
  plugins: [
1182
1124
  dts({
1183
1125
  tsconfigPath: resolveWorkspacePath('configs/src/tsconfig.server.json'),
@@ -1193,7 +1135,7 @@ export default defineConfig(
1193
1135
  )
1194
1136
  `
1195
1137
  }),
1196
- bin: `import { defineConfig } from 'vite'
1138
+ bin: `import { defineConfig, mergeConfig } from 'vite'
1197
1139
  import { srcBin } from '../../vite.config.ts'
1198
1140
 
1199
1141
  // The \`scaffold\` executable build — a single ESM lib file, no declarations (an
@@ -1202,7 +1144,7 @@ import { srcBin } from '../../vite.config.ts'
1202
1144
  // \`output.paths\` rewriting the externalized \`@src/*\` specifiers to the built sibling
1203
1145
  // src environments (relative to \`dist/bin/\`), so the emitted bin resolves at runtime.
1204
1146
  export default defineConfig(
1205
- srcBin({
1147
+ mergeConfig(srcBin(), {
1206
1148
  build: {
1207
1149
  rolldownOptions: {
1208
1150
  output: {
@@ -1640,8 +1582,8 @@ const SHELL = process.platform === 'win32'
1640
1582
  // Release is the publish gate, so evidence it cannot obtain fails there and skips
1641
1583
  // everywhere else: a gate that passes on missing evidence proves nothing.
1642
1584
  const RELEASE = import.meta.env.MODE === 'release'
1643
- // The built output directory a browser face is published from. Every selection here
1644
- // reads this prefix off the export TARGET and never off the subpath name. A
1585
+ // The built output directory convention a browser face may publish from. Every
1586
+ // selection reads this prefix off the export target and never off the subpath name. A
1645
1587
  // workspace whose only published face is the browser one publishes that face at the
1646
1588
  // root subpath, so a rule keyed on the subpath name drives a browser bundle through
1647
1589
  // Node and the miss is silent.
@@ -1929,11 +1871,33 @@ function readPackageType(installed: string, target: string): unknown {
1929
1871
  }
1930
1872
  }
1931
1873
 
1932
- // Whether the declaration selected by a typed CommonJS consumer admits that entry
1933
- // to its compile probe. A \`.d.cts\` declaration admits and a \`.d.mts\`
1934
- // declaration refuses. A \`.d.ts\` declaration takes its own nearest package scope.
1935
- // Runtime target format remains the runtime drive's separate question.
1874
+ function resolvesBrowser(entry: unknown): boolean {
1875
+ const module = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
1876
+ if (module !== undefined && module.startsWith(BROWSER_OUTPUT)) return true
1877
+ if (module === undefined) return false
1878
+ const imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)
1879
+ const required = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
1880
+ return module !== imported && module !== required
1881
+ }
1882
+
1883
+ // Whether the target selected by Node's CommonJS conditions is a module require can
1884
+ // load. A JavaScript target takes its own nearest package scope. Native addons and
1885
+ // extensionless targets have their own CommonJS handlers.
1936
1886
  function resolvesCommonJS(entry: unknown, installed: string): boolean {
1887
+ const target = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
1888
+ if (target === undefined) return false
1889
+ const name = target.slice(target.lastIndexOf('/') + 1)
1890
+ if (name.endsWith('.cjs')) return true
1891
+ if (name.endsWith('.mjs')) return false
1892
+ if (name.endsWith('.node')) return true
1893
+ if (!name.includes('.')) return true
1894
+ return name.endsWith('.js') && readPackageType(installed, target) !== 'module'
1895
+ }
1896
+
1897
+ // Whether the declaration selected by a typed CommonJS consumer admits that entry.
1898
+ // A \`.d.cts\` declaration admits and a \`.d.mts\` declaration refuses. A \`.d.ts\`
1899
+ // declaration takes its own nearest package scope.
1900
+ function declaresCommonJS(entry: unknown, installed: string): boolean {
1937
1901
  const declaration = resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed)
1938
1902
  if (declaration === undefined) return false
1939
1903
  if (declaration.endsWith('.d.cts')) return true
@@ -1996,13 +1960,13 @@ function selectEntries(entries: readonly Entry[], conditions: readonly string[])
1996
1960
  // Require-loadable entries that declare CommonJS support but a typed CommonJS
1997
1961
  // consumer cannot compile against. A default branch resolving under the require
1998
1962
  // condition set makes no CommonJS claim.
1999
- function selectUntypable(entries: readonly Entry[]): readonly Entry[] {
1963
+ function selectUntypable(entries: readonly Entry[], installed: string): readonly Entry[] {
2000
1964
  return entries.filter(
2001
1965
  (entry) =>
2002
1966
  entry.required &&
2003
1967
  isRecord(entry.mapping) &&
2004
1968
  Object.hasOwn(entry.mapping, 'require') &&
2005
- !entry.commonjs,
1969
+ !declaresCommonJS(entry.mapping, installed),
2006
1970
  )
2007
1971
  }
2008
1972
 
@@ -2135,9 +2099,11 @@ function buildStage(): Stage {
2135
2099
  continue
2136
2100
  }
2137
2101
  const imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)
2138
- const commonjs = resolvesCommonJS(entry, installed)
2139
- const required = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs) !== undefined
2140
- const module = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
2102
+ const requiredTarget = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
2103
+ const browserTarget = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
2104
+ const browser = resolvesBrowser(entry)
2105
+ const required = requiredTarget !== undefined && !(browser && requiredTarget === browserTarget)
2106
+ const commonjs = required && resolvesCommonJS(entry, installed)
2141
2107
  entries.push({
2142
2108
  subpath,
2143
2109
  specifier: subpath === '.' ? name : \`\${name}\${subpath.slice(1)}\`,
@@ -2149,8 +2115,8 @@ function buildStage(): Stage {
2149
2115
  browser:
2150
2116
  declaration.browser === undefined ? undefined : join(installed, declaration.browser),
2151
2117
  },
2152
- browser: module !== undefined && module.startsWith(BROWSER_OUTPUT),
2153
- module: imported !== undefined,
2118
+ browser,
2119
+ module: imported !== undefined && !(browser && imported === browserTarget),
2154
2120
  commonjs,
2155
2121
  required,
2156
2122
  })
@@ -2191,6 +2157,74 @@ function openStage(): Stage | undefined {
2191
2157
  const STAGE = openStage()
2192
2158
  const STAGED = STAGE !== undefined
2193
2159
 
2160
+ describe('distribution classifiers', () => {
2161
+ it('classifies synthetic export mappings without a registry stage', () => {
2162
+ const root = join(SCRATCH, 'classifiers')
2163
+ writeFile(
2164
+ join(root, 'package.json'),
2165
+ JSON.stringify({
2166
+ type: 'commonjs',
2167
+ exports: {
2168
+ condition: { browser: './b.js', default: './n.js' },
2169
+ convention: { default: './dist/src/browser/index.js' },
2170
+ universal: { default: './shared.js' },
2171
+ 'import-shared': {
2172
+ browser: './shared.mjs',
2173
+ import: './shared.mjs',
2174
+ default: './node.js',
2175
+ },
2176
+ 'require-shared': {
2177
+ browser: './shared.cjs',
2178
+ require: './shared.cjs',
2179
+ default: './node.js',
2180
+ },
2181
+ node: { node: './node.js', default: './node.js' },
2182
+ silent: { 'module-sync': './x.cjs', import: './x.mjs' },
2183
+ module: { require: './x.mjs' },
2184
+ 'nested-module': { require: './module/x.js' },
2185
+ 'nested-commonjs': { require: './commonjs/x.js' },
2186
+ esm: { import: './x.mjs' },
2187
+ },
2188
+ }),
2189
+ )
2190
+ writeFile(join(root, 'module/package.json'), '{ "type": "module" }\\n')
2191
+ writeFile(join(root, 'commonjs/package.json'), '{ "type": "commonjs" }\\n')
2192
+ const manifest = readJson(join(root, 'package.json'))
2193
+ if (!isRecord(manifest) || !isRecord(manifest.exports)) {
2194
+ throw new Error('The classifier fixture declares no exports map')
2195
+ }
2196
+ const mappings = manifest.exports
2197
+ expect({
2198
+ condition: resolvesBrowser(mappings.condition),
2199
+ convention: resolvesBrowser(mappings.convention),
2200
+ universal: resolvesBrowser(mappings.universal),
2201
+ import: resolvesBrowser(mappings['import-shared']),
2202
+ require: resolvesBrowser(mappings['require-shared']),
2203
+ node: resolvesBrowser(mappings.node),
2204
+ }).toStrictEqual({
2205
+ condition: true,
2206
+ convention: true,
2207
+ universal: false,
2208
+ import: false,
2209
+ require: false,
2210
+ node: false,
2211
+ })
2212
+ expect({
2213
+ silent: resolvesCommonJS(mappings.silent, root),
2214
+ module: resolvesCommonJS(mappings.module, root),
2215
+ nestedModule: resolvesCommonJS(mappings['nested-module'], root),
2216
+ nestedCommonJS: resolvesCommonJS(mappings['nested-commonjs'], root),
2217
+ esm: resolvesCommonJS(mappings.esm, root),
2218
+ }).toStrictEqual({
2219
+ silent: true,
2220
+ module: false,
2221
+ nestedModule: false,
2222
+ nestedCommonJS: true,
2223
+ esm: false,
2224
+ })
2225
+ })
2226
+ })
2227
+
2194
2228
  // The staged consumer, or a skip naming what the run could not reach. \`it.skipIf\`
2195
2229
  // carries no reason, so the gate sits here where the test context can state one.
2196
2230
  function requireStage(context: TestContext): Stage {
@@ -2238,7 +2272,7 @@ describe('installed package consumer', () => {
2238
2272
  (entry) => !entry.module && !entry.required && !entry.browser,
2239
2273
  )
2240
2274
  expect(unreachable.map((entry) => entry.subpath)).toStrictEqual([])
2241
- const untypable = selectUntypable(stage.entries)
2275
+ const untypable = selectUntypable(stage.entries, stage.installed)
2242
2276
  expect(untypable.map((entry) => entry.subpath)).toStrictEqual([])
2243
2277
  })
2244
2278
 
@@ -2283,7 +2317,7 @@ describe('installed package consumer', () => {
2283
2317
 
2284
2318
  for (const entry of STAGE?.entries ?? []) {
2285
2319
  describe(\`installed entry \${entry.subpath}\`, () => {
2286
- it.runIf(entry.module && !entry.browser)(
2320
+ it.runIf(entry.module)(
2287
2321
  'publishes what it declares to a Node import, and no more',
2288
2322
  (context) => {
2289
2323
  const declaration = entry.declaration.module
@@ -2295,7 +2329,7 @@ for (const entry of STAGE?.entries ?? []) {
2295
2329
  },
2296
2330
  )
2297
2331
 
2298
- it.runIf(!entry.browser && entry.required)(
2332
+ it.runIf(entry.required)(
2299
2333
  'publishes what it declares to a Node require, and no more',
2300
2334
  (context) => {
2301
2335
  const declaration = entry.declaration.commonjs
@@ -4317,7 +4351,7 @@ function blueprintToScripts(blueprint) {
4317
4351
  scripts["serve:build"] = "npm run build:app:server && npm run serve";
4318
4352
  }
4319
4353
  if (publishes) {
4320
- scripts.prepack = scripts.build;
4354
+ scripts.prepack = "npm run build";
4321
4355
  scripts.prepublishOnly = [
4322
4356
  "npm run format:check && npm run lint:check && npm run check && npm run build && npm test",
4323
4357
  RELEASE_PROOF_COMMAND,
@@ -4330,19 +4364,21 @@ function blueprintToScripts(blueprint) {
4330
4364
  * Project a blueprint into the manifest scripts a region write may replace.
4331
4365
  *
4332
4366
  * @param blueprint - The workspace specification.
4333
- * @returns One entry per writable script, or none when the workspace publishes
4334
- * nothing and therefore declares neither script.
4367
+ * @returns One entry per writable script.
4335
4368
  *
4336
4369
  * @remarks
4337
- * Publishing is what selects these scripts, so a private workspace answers an
4338
- * empty region and the write leaves its manifest alone.
4339
- *
4340
- * `accepted` carries the predecessor a target can hold before this package
4341
- * generated the packed-package proof: the same gate chain without
4370
+ * Every direct `test:<project>` script is writable, together with the probe and
4371
+ * benchmark workbench scripts. Publishing adds the pack and publication
4372
+ * lifecycle scripts. Aggregate test scripts and maintainer-owned gate chains
4373
+ * stay outside the region.
4374
+ *
4375
+ * `accepted` carries each generated predecessor the region can replace. The
4376
+ * pack hook accepts the build chain emitted before it delegated to `build`. The
4377
+ * publication hook accepts the same gate chain without
4342
4378
  * {@link RELEASE_PROOF_COMMAND}. The value being written is always writable, so
4343
- * it is not repeated there. Any other value is a chain the workspace author
4344
- * customized, and {@link replaceManifestScripts} refuses the whole region
4345
- * rather than taking it.
4379
+ * it is not repeated there. Any other value is a script the workspace author
4380
+ * customized, and {@link replaceManifestScripts} retains it while writing the
4381
+ * other named scripts independently.
4346
4382
  *
4347
4383
  * @example
4348
4384
  * ```ts
@@ -4350,23 +4386,36 @@ function blueprintToScripts(blueprint) {
4350
4386
  *
4351
4387
  * const blueprint = createBlueprint('router', { src: ['core'] })
4352
4388
  *
4353
- * blueprintToWritableScripts(blueprint)[0]?.name // 'test:distribution'
4389
+ * blueprintToWritableScripts(blueprint)[0]?.name // 'test:src:core'
4354
4390
  * ```
4355
4391
  */
4356
4392
  function blueprintToWritableScripts(blueprint) {
4357
4393
  const scripts = blueprintToScripts(blueprint);
4358
- const distribution = scripts["test:distribution"];
4394
+ const writable = [];
4395
+ for (const [name, command] of Object.entries(scripts)) {
4396
+ if (!name.startsWith("test:") || name === "test:src" || name === "test:app") continue;
4397
+ writable.push({
4398
+ name,
4399
+ command,
4400
+ accepted: []
4401
+ });
4402
+ }
4403
+ const prepack = scripts.prepack;
4404
+ if (prepack !== void 0) {
4405
+ const predecessor = scripts.build;
4406
+ writable.push({
4407
+ name: "prepack",
4408
+ command: prepack,
4409
+ accepted: predecessor === void 0 ? [] : [predecessor]
4410
+ });
4411
+ }
4359
4412
  const prepublish = scripts.prepublishOnly;
4360
- if (distribution === void 0 || prepublish === void 0) return [];
4361
- return [{
4362
- name: "test:distribution",
4363
- command: distribution,
4364
- accepted: []
4365
- }, {
4413
+ if (prepublish !== void 0) writable.push({
4366
4414
  name: "prepublishOnly",
4367
4415
  command: prepublish,
4368
4416
  accepted: [prepublish.replace(` && ${RELEASE_PROOF_COMMAND}`, "")]
4369
- }];
4417
+ });
4418
+ return writable;
4370
4419
  }
4371
4420
  /**
4372
4421
  * Compile a blueprint into its `package.json` content.
@@ -4549,14 +4598,14 @@ function blueprintToRootVite(blueprint) {
4549
4598
  const core = blueprint.src.includes("core");
4550
4599
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.src.browser, {
4551
4600
  external: core ? `external: (id: string) =>
4552
- id === '@src/core' ||
4553
- id.startsWith('@orkestrel/') ||
4554
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4555
- id.startsWith('@orkestrel/') ||
4556
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4557
- output: core ? " output: { paths: { '@src/core': '../core/index.js' } }," : " output: {},",
4558
- exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : "",
4559
- global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : ""
4601
+ id === '@src/core' ||
4602
+ id.startsWith('@orkestrel/') ||
4603
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4604
+ id.startsWith('@orkestrel/') ||
4605
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4606
+ output: core ? " output: { paths: { '@src/core': '../core/index.js' } }," : " output: {},",
4607
+ exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : "",
4608
+ global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : ""
4560
4609
  }));
4561
4610
  projects.push("srcBrowser");
4562
4611
  }
@@ -4564,26 +4613,26 @@ function blueprintToRootVite(blueprint) {
4564
4613
  const core = blueprint.src.includes("core");
4565
4614
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.src.server, {
4566
4615
  external: core ? `external: (id: string) =>
4567
- id === '@src/core' ||
4568
- id.startsWith('node:') ||
4569
- id.startsWith('@orkestrel/') ||
4570
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4571
- id.startsWith('node:') ||
4572
- id.startsWith('@orkestrel/') ||
4573
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4574
- output: core ? `\t\t\t\t\toutput: [
4575
- {
4576
- format: 'es',
4577
- entryFileNames: 'index.js',
4578
- paths: { '@src/core': '../core/index.js' },
4579
- },
4580
- {
4581
- format: 'cjs',
4582
- entryFileNames: 'index.cjs',
4583
- paths: { '@src/core': '../core/index.cjs' },
4584
- },
4585
- ],` : " output: {},",
4586
- exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : ""
4616
+ id === '@src/core' ||
4617
+ id.startsWith('node:') ||
4618
+ id.startsWith('@orkestrel/') ||
4619
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4620
+ id.startsWith('node:') ||
4621
+ id.startsWith('@orkestrel/') ||
4622
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4623
+ output: core ? `\t\t\toutput: [
4624
+ {
4625
+ format: 'es',
4626
+ entryFileNames: 'index.js',
4627
+ paths: { '@src/core': '../core/index.js' },
4628
+ },
4629
+ {
4630
+ format: 'cjs',
4631
+ entryFileNames: 'index.cjs',
4632
+ paths: { '@src/core': '../core/index.cjs' },
4633
+ },
4634
+ ],` : " output: {},",
4635
+ exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : ""
4587
4636
  }));
4588
4637
  projects.push("srcServer");
4589
4638
  }
@@ -4678,7 +4727,7 @@ export function appShowcase(): UserConfig {
4678
4727
  projects.push("distribution");
4679
4728
  }
4680
4729
  if (blueprint.integration) {
4681
- factories.push(fillTemplate(CONFIG_TEMPLATES.factories.integration, { global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "" }));
4730
+ factories.push(fillTemplate(CONFIG_TEMPLATES.factories.integration, { global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "" }));
4682
4731
  projects.push("integration");
4683
4732
  }
4684
4733
  factories.push(CONFIG_TEMPLATES.factories.probe);
@@ -5381,24 +5430,23 @@ function replaceManifestRanges(manifest, pins) {
5381
5430
  *
5382
5431
  * @param manifest - The manifest text to compile.
5383
5432
  * @param scripts - The scripts to write, each with the predecessors it accepts.
5384
- * @returns The manifest carrying every named script, or `undefined` when a
5385
- * named script holds a value outside what it accepts, or when the text carries
5386
- * no readable `scripts` object to write into.
5433
+ * @returns The manifest carrying every absent or accepted named script and
5434
+ * retaining every differing string value, or `undefined` when a planned key
5435
+ * holds a non-string value or the text carries no readable `scripts` object to
5436
+ * write into.
5387
5437
  *
5388
5438
  * @remarks
5389
5439
  * The compiler replaces values in place instead of serializing the manifest, so
5390
5440
  * description, keywords, dependencies, key order, indentation, and every byte
5391
5441
  * outside the replaced ranges survive. A named script the manifest already
5392
- * declares is overwritten only when its value is the one being written or one
5393
- * of its {@link ManifestScript.accepted} predecessors; anything else is a chain
5394
- * the workspace author customized, and the whole region is refused without a
5395
- * byte moving. A named script the manifest does not declare is appended after
5396
- * the last declared script, copying that section's indentation. A region
5397
- * declaring nothing takes every named script as its first entries, indented
5398
- * from the line its own opening brace sits on.
5399
- *
5400
- * The refusal is whole rather than per script, so a manifest never ends up
5401
- * holding one written script beside one refused one.
5442
+ * declares is overwritten only when its value is one of its
5443
+ * {@link ManifestScript.accepted} predecessors. The planned value stands. Any
5444
+ * other string is a chain the workspace author customized, so it stays
5445
+ * byte-identical while the other named scripts are written independently. A
5446
+ * named script the manifest does not declare is appended after the last
5447
+ * declared script, copying that section's indentation. A region declaring
5448
+ * nothing takes every named script as its first entries, indented from the line
5449
+ * its own opening brace sits on.
5402
5450
  *
5403
5451
  * @example
5404
5452
  * ```ts
@@ -5420,7 +5468,6 @@ function replaceManifestScripts(manifest, scripts) {
5420
5468
  if (!Object.hasOwn(declared, script.name)) continue;
5421
5469
  const value = declared[script.name];
5422
5470
  if (!isString(value)) return void 0;
5423
- if (value !== script.command && !script.accepted.includes(value)) return void 0;
5424
5471
  }
5425
5472
  let depth = 0;
5426
5473
  let cursor = 0;
@@ -5548,6 +5595,8 @@ function replaceManifestScripts(manifest, scripts) {
5548
5595
  const script = scripts.find((entry) => entry.name === key);
5549
5596
  if (script === void 0) continue;
5550
5597
  written.add(script.name);
5598
+ const value = declared[script.name];
5599
+ if (value === script.command || !isString(value) || !script.accepted.includes(value)) continue;
5551
5600
  edits.push({
5552
5601
  start: valueStart,
5553
5602
  end: valueEnd + 1,