@orkestrel/scaffold 0.0.50 → 0.0.51

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