@orkestrel/scaffold 0.0.31 → 0.0.32

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.
@@ -42,7 +42,7 @@ describe('root configuration', () => {
42
42
  for (const key of required.keys()) {
43
43
  if (!absent.has(key)) throw new Error('The alias population carries no required entry')
44
44
  }
45
- }).toThrowError('The alias population carries no required entry')
45
+ }).toThrow('The alias population carries no required entry')
46
46
  for (const [key, expected] of required) {
47
47
  const values = declared.get(key)
48
48
  if (values === undefined) throw new Error(`${key} is not declared`)
@@ -102,7 +102,14 @@ describe('root configuration', () => {
102
102
  setup: ['./tests/setup.ts', './tests/setupServer.ts'],
103
103
  })
104
104
  }
105
- for (const label of ['policy', 'config', 'guides', 'conformance', 'integration']) {
105
+ for (const label of [
106
+ 'policy',
107
+ 'config',
108
+ 'guides',
109
+ 'conformance',
110
+ 'distribution',
111
+ 'integration',
112
+ ]) {
106
113
  if (!existsSync(resolve(root, `tests/${label}.test.ts`))) continue
107
114
  expected.set(label, {
108
115
  include: `tests/${label}.test.ts`,
@@ -218,7 +225,7 @@ describe('root configuration', () => {
218
225
  expect(missing.delete('probe')).toBe(true)
219
226
  expect(() => {
220
227
  for (const [label, project] of expected) expect(missing.get(label)).toStrictEqual(project)
221
- }).toThrowError(/strictly equal/u)
228
+ }).toThrow(/strictly equal/u)
222
229
 
223
230
  const misconfigured = new Map(configured)
224
231
  const probe = misconfigured.get('probe')
@@ -227,7 +234,7 @@ describe('root configuration', () => {
227
234
  expect(() => {
228
235
  for (const [label, project] of expected)
229
236
  expect(misconfigured.get(label)).toStrictEqual(project)
230
- }).toThrowError(/strictly equal/u)
237
+ }).toThrow(/strictly equal/u)
231
238
  })
232
239
 
233
240
  it('requires and validates every selected target wrapper', async () => {
@@ -344,10 +351,10 @@ describe('root configuration', () => {
344
351
  const controlFound = ['configs/app/vite.server.config.ts']
345
352
  expect(() => {
346
353
  for (const wrapper of controlRequired) expect(controlFound).toContain(wrapper)
347
- }).toThrowError(/vite\.browser/u)
348
- expect(() =>
349
- expect(resolve(root, 'dist/actual')).toBe(resolve(root, 'dist/control')),
350
- ).toThrowError(/expected/u)
354
+ }).toThrow(/vite\.browser/u)
355
+ expect(() => expect(resolve(root, 'dist/actual')).toBe(resolve(root, 'dist/control'))).toThrow(
356
+ /expected/u,
357
+ )
351
358
  })
352
359
 
353
360
  it('registers proof scripts in the correct gate', () => {
@@ -359,8 +366,10 @@ describe('root configuration', () => {
359
366
  if (typeof scripts !== 'object' || scripts === null) {
360
367
  throw new Error('The package manifest carries no scripts')
361
368
  }
369
+ const publishes = Object.getOwnPropertyDescriptor(manifest, 'private')?.value !== true
362
370
  const test = Object.getOwnPropertyDescriptor(scripts, 'test')?.value
363
371
  const config = Object.getOwnPropertyDescriptor(scripts, 'test:config')?.value
372
+ const distribution = Object.getOwnPropertyDescriptor(scripts, 'test:distribution')?.value
364
373
  const integration = Object.getOwnPropertyDescriptor(scripts, 'test:integration')?.value
365
374
  const conformance = Object.getOwnPropertyDescriptor(scripts, 'test:conformance')?.value
366
375
  const service = Object.getOwnPropertyDescriptor(scripts, 'test:service')?.value
@@ -390,24 +399,39 @@ describe('root configuration', () => {
390
399
  expect(registered.has('config')).toBe(true)
391
400
  expect(registered.has('control')).toBe(false)
392
401
  const hasConformance = registered.has('conformance')
402
+ const hasDistribution = registered.has('distribution')
393
403
  const hasService = registered.has('service')
394
404
  expect(config).toBe(
395
405
  'vitest run --config vite.config.ts --no-cache --reporter=dot --project config',
396
406
  )
397
407
  expect(typeof test === 'string' && test.includes('npm run test:config')).toBe(true)
408
+ expect(distribution).toBe(
409
+ hasDistribution
410
+ ? 'vitest run --config vite.config.ts --no-cache --reporter=dot --project distribution'
411
+ : undefined,
412
+ )
413
+ expect(typeof test === 'string' && test.includes('test:distribution')).toBe(false)
414
+ expect(typeof publish === 'string' && publish.includes('npm run test:distribution')).toBe(
415
+ hasDistribution && publishes,
416
+ )
417
+ expect(typeof publish === 'string').toBe(publishes)
398
418
  expect(integration).toBe(
399
419
  hasIntegration
400
420
  ? 'vitest run --config vite.config.ts --no-cache --reporter=dot --project integration'
401
421
  : undefined,
402
422
  )
403
- expect(typeof test === 'string' && test.includes('test:integration')).toBe(false)
404
- expect(typeof publish === 'string' && publish.includes('npm run test:integration')).toBe(
423
+ // The integration seed composes barrels and starts no process, so it runs in
424
+ // `test` like any other hermetic proof. `prepublishOnly` reaches it through
425
+ // `npm test` rather than through a second direct invocation.
426
+ expect(typeof test === 'string' && test.includes('npm run test:integration')).toBe(
405
427
  hasIntegration,
406
428
  )
429
+ expect(typeof publish === 'string' && publish.includes('npm run test:integration')).toBe(false)
407
430
  // A registered project no gate runs is a proof that never executes, and it
408
431
  // never fails, so the suite reports green while carrying it. Conformance is
409
- // hermetic and belongs to `test`; the live-service project drives a real
410
- // service and belongs to `prepublishOnly` alone.
432
+ // hermetic and belongs to `test`. A publishing workspace isolates the
433
+ // live-service project in `prepublishOnly`; a private workspace reaches it
434
+ // from `test`, because npm never runs a private package's publish lifecycle.
411
435
  expect(conformance).toBe(
412
436
  hasConformance
413
437
  ? 'vitest run --config vite.config.ts --no-cache --reporter=dot --project conformance'
@@ -421,8 +445,12 @@ describe('root configuration', () => {
421
445
  ? 'vitest run --config vite.config.ts --no-cache --reporter=dot --project service'
422
446
  : undefined,
423
447
  )
424
- expect(typeof test === 'string' && test.includes('test:service')).toBe(false)
425
- expect(typeof publish === 'string' && publish.includes('npm run test:service')).toBe(hasService)
448
+ expect(typeof test === 'string' && test.includes('npm run test:service')).toBe(
449
+ hasService && !publishes,
450
+ )
451
+ expect(typeof publish === 'string' && publish.includes('npm run test:service')).toBe(
452
+ hasService && publishes,
453
+ )
426
454
  })
427
455
  })
428
456
 
@@ -562,9 +590,9 @@ describe('configuration helpers', () => {
562
590
  expect(() => configHelpers.enforceOutputPath(expected, expected)).not.toThrow()
563
591
  expect(() =>
564
592
  configHelpers.enforceOutputPath(resolve(root, 'dist/outside-control'), expected),
565
- ).toThrowError('Build output must use its exact configured workspace directory')
593
+ ).toThrow('Build output must use its exact configured workspace directory')
566
594
  const outside = resolve(dirname(root), 'outside-config-control')
567
- expect(() => configHelpers.enforceOutputPath(outside, outside)).toThrowError(
595
+ expect(() => configHelpers.enforceOutputPath(outside, outside)).toThrow(
568
596
  'Build output must remain inside the workspace',
569
597
  )
570
598
  })
@@ -611,7 +639,7 @@ describe('configuration helpers', () => {
611
639
  error: expect.unreachable,
612
640
  resolve: Promise.resolve.bind(Promise, { id: source }),
613
641
  }
614
- await expect(Reflect.apply(hook, context, ['@src/server', source])).rejects.toThrowError(
642
+ await expect(Reflect.apply(hook, context, ['@src/server', source])).rejects.toThrow(
615
643
  'Browser modules cannot depend on Node or server-only modules',
616
644
  )
617
645
  await expect(Reflect.apply(hook, context, ['@src/core', source])).resolves.toBeNull()
@@ -7,6 +7,8 @@ import {
7
7
  inspectPolicySources,
8
8
  inspectPolicyWorkspace,
9
9
  POLICY_CONTROLS,
10
+ stemToPolicyCandidates,
11
+ testToPolicyStem,
10
12
  } from './setupPolicy.js'
11
13
 
12
14
  describe('fleet policy register', () => {
@@ -25,8 +27,258 @@ describe('fleet policy register', () => {
25
27
  'tests/app/browser/routes.test.ts',
26
28
  'tests/app/edge/deep/integration.test.ts',
27
29
  ]
28
- const sources = new Set(['src/worker/Worker.ts', 'app/browser/routes.ts'])
29
- expect(inspectPolicyMirrorPaths(tests, sources)).toEqual([])
30
+ const modules = new Set(['src/worker/Worker.ts', 'app/browser/routes.ts'])
31
+ expect(inspectPolicyMirrorPaths(tests, modules)).toEqual([])
32
+ })
33
+
34
+ it('derives mirror candidates in module resolution order', () => {
35
+ const stem = testToPolicyStem('tests/app/core/setupBrowser.test.ts')
36
+ expect(stem).toBe('app/core/setupBrowser')
37
+ expect(stemToPolicyCandidates('app/core/setupBrowser')).toEqual([
38
+ 'app/core/setupBrowser.cts',
39
+ 'app/core/setupBrowser.mts',
40
+ 'app/core/setupBrowser.ts',
41
+ 'app/core/setupBrowser.tsx',
42
+ 'app/core/setupBrowser.vue',
43
+ 'app/core/setupBrowser.scss',
44
+ 'app/core/setupBrowser.css',
45
+ 'app/core/_setupBrowser.scss',
46
+ 'app/core/_setupBrowser.css',
47
+ 'tests/app/core/setupBrowser.ts',
48
+ ])
49
+ expect(testToPolicyStem('tests/app/core/integration.test.ts')).toBeUndefined()
50
+ })
51
+ })
52
+
53
+ describe('policy population controls', () => {
54
+ it('accepts a Vue module mirror', () => {
55
+ expect(
56
+ inspectPolicyControl({
57
+ label: 'accepts a Vue module mirror',
58
+ membership: 'registered module extensions with an exact test stem',
59
+ rule: 'mirror',
60
+ files: [
61
+ { path: 'app/browser/Widget.vue', content: '<template></template>\n' },
62
+ { path: 'tests/app/browser/Widget.test.ts', content: '' },
63
+ ],
64
+ }),
65
+ ).toEqual([])
66
+ })
67
+
68
+ it('accepts a TSX module mirror', () => {
69
+ expect(
70
+ inspectPolicyControl({
71
+ label: 'accepts a TSX module mirror',
72
+ membership: 'registered module extensions with an exact test stem',
73
+ rule: 'mirror',
74
+ files: [
75
+ { path: 'app/browser/Widget.tsx', content: '' },
76
+ { path: 'tests/app/browser/Widget.test.ts', content: '' },
77
+ ],
78
+ }),
79
+ ).toEqual([])
80
+ })
81
+
82
+ it('accepts an MTS module mirror', () => {
83
+ expect(
84
+ inspectPolicyControl({
85
+ label: 'accepts an MTS module mirror',
86
+ membership: 'registered module extensions with an exact test stem',
87
+ rule: 'mirror',
88
+ files: [
89
+ { path: 'app/browser/Widget.mts', content: '' },
90
+ { path: 'tests/app/browser/Widget.test.ts', content: '' },
91
+ ],
92
+ }),
93
+ ).toEqual([])
94
+ })
95
+
96
+ it('accepts a CTS module mirror', () => {
97
+ expect(
98
+ inspectPolicyControl({
99
+ label: 'accepts a CTS module mirror',
100
+ membership: 'registered module extensions with an exact test stem',
101
+ rule: 'mirror',
102
+ files: [
103
+ { path: 'app/browser/Widget.cts', content: '' },
104
+ { path: 'tests/app/browser/Widget.test.ts', content: '' },
105
+ ],
106
+ }),
107
+ ).toEqual([])
108
+ })
109
+
110
+ it('accepts a Sass partial module mirror', () => {
111
+ expect(
112
+ inspectPolicyControl({
113
+ label: 'accepts a Sass partial module mirror',
114
+ membership: 'registered partial extensions with an exact underscore-free test stem',
115
+ rule: 'mirror',
116
+ files: [
117
+ { path: 'app/browser/styles/_tokens.scss', content: '' },
118
+ { path: 'tests/app/browser/styles/tokens.test.ts', content: '' },
119
+ ],
120
+ }),
121
+ ).toEqual([])
122
+ })
123
+
124
+ it('accepts a setup module mirror inside tests', () => {
125
+ expect(
126
+ inspectPolicyControl({
127
+ label: 'accepts a setup module mirror inside tests',
128
+ membership: 'tests-axis setup modules with an exact test stem',
129
+ rule: 'mirror',
130
+ files: [
131
+ { path: 'tests/app/core/setup.ts', content: '' },
132
+ { path: 'tests/app/core/setup.test.ts', content: '' },
133
+ ],
134
+ }),
135
+ ).toEqual([])
136
+ })
137
+
138
+ it('excludes a .d.ts ambient declaration from placement', () => {
139
+ expect(
140
+ inspectPolicyControl({
141
+ label: 'excludes a .d.ts ambient declaration from placement',
142
+ membership: 'ambient TypeScript declarations',
143
+ rule: 'type',
144
+ files: [
145
+ {
146
+ path: 'app/browser/env.d.ts',
147
+ content: 'export interface EnvironmentInterface {}\n',
148
+ },
149
+ ],
150
+ }),
151
+ ).toEqual([])
152
+ })
153
+
154
+ it('excludes a .d.mts ambient declaration from placement', () => {
155
+ expect(
156
+ inspectPolicyControl({
157
+ label: 'excludes a .d.mts ambient declaration from placement',
158
+ membership: 'ambient TypeScript declarations',
159
+ rule: 'type',
160
+ files: [
161
+ {
162
+ path: 'app/browser/env.d.mts',
163
+ content: 'export interface EnvironmentInterface {}\n',
164
+ },
165
+ ],
166
+ }),
167
+ ).toEqual([])
168
+ })
169
+
170
+ it('excludes a .d.cts ambient declaration from placement', () => {
171
+ expect(
172
+ inspectPolicyControl({
173
+ label: 'excludes a .d.cts ambient declaration from placement',
174
+ membership: 'ambient TypeScript declarations',
175
+ rule: 'type',
176
+ files: [
177
+ {
178
+ path: 'app/browser/env.d.cts',
179
+ content: 'export interface EnvironmentInterface {}\n',
180
+ },
181
+ ],
182
+ }),
183
+ ).toEqual([])
184
+ })
185
+
186
+ it('accepts a direct callback argument in constants.ts', () => {
187
+ expect(
188
+ inspectPolicyControl({
189
+ label: 'accepts a direct callback argument in constants.ts',
190
+ membership: 'anonymous callbacks passed directly as call arguments',
191
+ rule: 'function',
192
+ files: [
193
+ {
194
+ path: 'app/edge/constants.ts',
195
+ content: 'export const LABELS = Object.freeze(COLUMNS.map((column) => column.label))\n',
196
+ },
197
+ ],
198
+ }),
199
+ ).toEqual([])
200
+ })
201
+
202
+ it('accepts a helper namespace in helpers.ts', () => {
203
+ expect(
204
+ inspectPolicyControl({
205
+ label: 'accepts a helper namespace in helpers.ts',
206
+ membership: 'camelCase helper namespaces containing function behavior',
207
+ rule: 'data',
208
+ files: [
209
+ {
210
+ path: 'app/edge/helpers.ts',
211
+ content: 'export const formatters = Object.freeze({ money: build(fmt) })\n',
212
+ },
213
+ ],
214
+ }),
215
+ ).toEqual([])
216
+ })
217
+
218
+ it('accepts a concise-body direct return in constants.ts', () => {
219
+ expect(
220
+ inspectPolicyControl({
221
+ label: 'accepts a concise-body direct return in constants.ts',
222
+ membership: 'anonymous functions returned directly through a concise arrow body',
223
+ rule: 'function',
224
+ files: [
225
+ {
226
+ path: 'app/edge/constants.ts',
227
+ content: 'export const WRAPPED = wrap(() => () => 1)\n',
228
+ },
229
+ ],
230
+ }),
231
+ ).toEqual([])
232
+ })
233
+
234
+ it('accepts a returned function inside a direct callback', () => {
235
+ expect(
236
+ inspectPolicyControl({
237
+ label: 'accepts a returned function inside a direct callback',
238
+ membership: 'anonymous functions returned directly by a return statement',
239
+ rule: 'function',
240
+ files: [
241
+ {
242
+ path: 'app/edge/constants.ts',
243
+ content:
244
+ 'export const LABELS = Object.freeze(COLUMNS.map((column) => { return () => column.label }))\n',
245
+ },
246
+ ],
247
+ }),
248
+ ).toEqual([])
249
+ })
250
+
251
+ it('accepts directly returned functions through callback control flow', () => {
252
+ expect(
253
+ inspectPolicyControl({
254
+ label: 'accepts directly returned functions through callback control flow',
255
+ membership: 'anonymous functions returned directly through control-flow branches',
256
+ rule: 'function',
257
+ files: [
258
+ {
259
+ path: 'app/edge/constants.ts',
260
+ content:
261
+ 'export const VALUES = Object.freeze(C.map((c) => { if (c) return () => 1; return () => 2 }))\n',
262
+ },
263
+ ],
264
+ }),
265
+ ).toEqual([])
266
+ })
267
+
268
+ it('excludes Vue text from the placement population', () => {
269
+ expect(
270
+ inspectPolicyControl({
271
+ label: 'excludes Vue text from the placement population',
272
+ membership: 'files outside the declared TypeScript source extensions',
273
+ rule: 'type',
274
+ files: [
275
+ {
276
+ path: 'app/browser/Panel.vue',
277
+ content: '<script setup lang="ts">\nexport interface PanelInterface {}\n</script>\n',
278
+ },
279
+ ],
280
+ }),
281
+ ).toEqual([])
30
282
  })
31
283
  })
32
284