@orkestrel/scaffold 0.0.16 → 0.0.18
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.
- package/README.md +13 -3
- package/dist/bin/scaffold.js +67 -31
- package/dist/bin/scaffold.js.map +1 -1
- package/dist/host/claude/agents/orkestrel.md +14 -14
- package/dist/host/guides/src/scaffold.md +55 -18
- package/dist/host/tests/setupPolicy.ts +4 -3
- package/dist/src/core/index.cjs +196 -62
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +3 -3
- package/dist/src/core/index.d.ts +3 -3
- package/dist/src/core/index.js +196 -62
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +51 -33
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +10 -6
- package/dist/src/server/index.d.ts +10 -6
- package/dist/src/server/index.js +49 -31
- package/dist/src/server/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -1177,7 +1177,7 @@ export declare function coreViteConfig(): string;
|
|
|
1177
1177
|
readonly content: string;
|
|
1178
1178
|
readonly freshness: Freshness;
|
|
1179
1179
|
readonly note?: string;
|
|
1180
|
-
/** SHA-256 of the observed local mirror, or `absent`; omitted outside target-aware
|
|
1180
|
+
/** SHA-256 of the observed local mirror, or `absent`; omitted outside target-aware synchronization. */
|
|
1181
1181
|
readonly baseline?: string;
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
@@ -2275,7 +2275,7 @@ export declare function coreViteConfig(): string;
|
|
|
2275
2275
|
export declare function rootViteConfig(src: readonly Environment[], facts?: ViteFacts): string;
|
|
2276
2276
|
|
|
2277
2277
|
/** The devDependency range generated packages pin `@orkestrel/scaffold` at. */
|
|
2278
|
-
export declare const SCAFFOLD_RANGE = "^0.0.
|
|
2278
|
+
export declare const SCAFFOLD_RANGE = "^0.0.18";
|
|
2279
2279
|
|
|
2280
2280
|
/**
|
|
2281
2281
|
* Carries a `ScaffoldErrorCode` + optional `context` (AGENTS §12).
|
|
@@ -2537,7 +2537,7 @@ export declare function coreViteConfig(): string;
|
|
|
2537
2537
|
export declare const SYNC_BASELINE_PATTERN: RegExp;
|
|
2538
2538
|
|
|
2539
2539
|
/**
|
|
2540
|
-
* The whole outcome of a `Sync.pull
|
|
2540
|
+
* The whole outcome of a `Sync.pull` or `Sync.mirror` synchronization.
|
|
2541
2541
|
*
|
|
2542
2542
|
* @remarks
|
|
2543
2543
|
* `clean` is `true` iff no drift AND no failures; `failed` is the count of
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -1177,7 +1177,7 @@ export declare function coreViteConfig(): string;
|
|
|
1177
1177
|
readonly content: string;
|
|
1178
1178
|
readonly freshness: Freshness;
|
|
1179
1179
|
readonly note?: string;
|
|
1180
|
-
/** SHA-256 of the observed local mirror, or `absent`; omitted outside target-aware
|
|
1180
|
+
/** SHA-256 of the observed local mirror, or `absent`; omitted outside target-aware synchronization. */
|
|
1181
1181
|
readonly baseline?: string;
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
@@ -2275,7 +2275,7 @@ export declare function coreViteConfig(): string;
|
|
|
2275
2275
|
export declare function rootViteConfig(src: readonly Environment[], facts?: ViteFacts): string;
|
|
2276
2276
|
|
|
2277
2277
|
/** The devDependency range generated packages pin `@orkestrel/scaffold` at. */
|
|
2278
|
-
export declare const SCAFFOLD_RANGE = "^0.0.
|
|
2278
|
+
export declare const SCAFFOLD_RANGE = "^0.0.18";
|
|
2279
2279
|
|
|
2280
2280
|
/**
|
|
2281
2281
|
* Carries a `ScaffoldErrorCode` + optional `context` (AGENTS §12).
|
|
@@ -2537,7 +2537,7 @@ export declare function coreViteConfig(): string;
|
|
|
2537
2537
|
export declare const SYNC_BASELINE_PATTERN: RegExp;
|
|
2538
2538
|
|
|
2539
2539
|
/**
|
|
2540
|
-
* The whole outcome of a `Sync.pull
|
|
2540
|
+
* The whole outcome of a `Sync.pull` or `Sync.mirror` synchronization.
|
|
2541
2541
|
*
|
|
2542
2542
|
* @remarks
|
|
2543
2543
|
* `clean` is `true` iff no drift AND no failures; `failed` is the count of
|
package/dist/src/core/index.js
CHANGED
|
@@ -226,7 +226,7 @@ var DEFAULT_VERSION = "0.0.1";
|
|
|
226
226
|
/** The `engines.node` range the `blueprint` builder fills. */
|
|
227
227
|
var DEFAULT_ENGINES = `>=${MINIMUM_NODE_VERSION}`;
|
|
228
228
|
/** The devDependency range generated packages pin `@orkestrel/scaffold` at. */
|
|
229
|
-
var SCAFFOLD_RANGE = "^0.0.
|
|
229
|
+
var SCAFFOLD_RANGE = "^0.0.18";
|
|
230
230
|
/** Tooling versions shared by scaffold and every generated workspace. */
|
|
231
231
|
var BASE_DEV_DEPENDENCIES = Object.freeze({
|
|
232
232
|
"@microsoft/api-extractor": "^7.58.12",
|
|
@@ -4466,9 +4466,7 @@ describe('createApplication', () => {
|
|
|
4466
4466
|
'x'.repeat(204),
|
|
4467
4467
|
\`\${' '.repeat(MAX_APPLICATION_NAME_INPUT_LENGTH)}x\`,
|
|
4468
4468
|
]) {
|
|
4469
|
-
expect(() => Reflect.apply(createApplication, undefined, [value])).
|
|
4470
|
-
ApplicationError,
|
|
4471
|
-
)
|
|
4469
|
+
expect(() => Reflect.apply(createApplication, undefined, [value])).toThrow(ApplicationError)
|
|
4472
4470
|
}
|
|
4473
4471
|
const error = new ApplicationError('CONFIG', 'invalid')
|
|
4474
4472
|
expect(isApplicationError(error)).toBe(true)
|
|
@@ -4543,7 +4541,7 @@ describe('createBrowserApplication', () => {
|
|
|
4543
4541
|
{ name: 'x'.repeat(204) },
|
|
4544
4542
|
{ name: \`\${' '.repeat(MAX_BROWSER_APPLICATION_NAME_INPUT_LENGTH)}x\` },
|
|
4545
4543
|
]) {
|
|
4546
|
-
expect(() => Reflect.apply(createBrowserApplication, undefined, [value])).
|
|
4544
|
+
expect(() => Reflect.apply(createBrowserApplication, undefined, [value])).toThrow(
|
|
4547
4545
|
BrowserApplicationError,
|
|
4548
4546
|
)
|
|
4549
4547
|
}
|
|
@@ -4568,14 +4566,14 @@ describe('createBrowserApplication', () => {
|
|
|
4568
4566
|
const revocable = Proxy.revocable({}, {})
|
|
4569
4567
|
revocable.revoke()
|
|
4570
4568
|
|
|
4571
|
-
expect(() => parseBrowserApplicationOptions(accessor)).
|
|
4569
|
+
expect(() => parseBrowserApplicationOptions(accessor)).toThrow(BrowserApplicationError)
|
|
4572
4570
|
expect(reads).toBe(0)
|
|
4573
|
-
expect(() => Reflect.apply(createBrowserApplication, undefined, [hostile])).
|
|
4571
|
+
expect(() => Reflect.apply(createBrowserApplication, undefined, [hostile])).toThrow(
|
|
4572
|
+
BrowserApplicationError,
|
|
4573
|
+
)
|
|
4574
|
+
expect(() => Reflect.apply(createBrowserApplication, undefined, [revocable.proxy])).toThrow(
|
|
4574
4575
|
BrowserApplicationError,
|
|
4575
4576
|
)
|
|
4576
|
-
expect(() =>
|
|
4577
|
-
Reflect.apply(createBrowserApplication, undefined, [revocable.proxy]),
|
|
4578
|
-
).toThrowError(BrowserApplicationError)
|
|
4579
4577
|
expect(isBrowserApplicationError(new BrowserApplicationError('CONFIG', 'invalid'))).toBe(true)
|
|
4580
4578
|
expect(isBrowserApplicationError(new Error('plain'))).toBe(false)
|
|
4581
4579
|
expect(isBrowserApplicationError(revocable.proxy)).toBe(false)
|
|
@@ -4678,20 +4676,20 @@ describe('ApplicationServer', () => {
|
|
|
4678
4676
|
})
|
|
4679
4677
|
|
|
4680
4678
|
it('validates direct options before allocating a listener', () => {
|
|
4681
|
-
expect(() => createApplicationServer({ host: ' ' })).
|
|
4679
|
+
expect(() => createApplicationServer({ host: ' ' })).toThrow(
|
|
4682
4680
|
expect.objectContaining({ code: 'CONFIG' }),
|
|
4683
4681
|
)
|
|
4684
|
-
expect(() => createApplicationServer({ port: Number.NaN })).
|
|
4682
|
+
expect(() => createApplicationServer({ port: Number.NaN })).toThrow(
|
|
4685
4683
|
expect.objectContaining({ code: 'CONFIG' }),
|
|
4686
4684
|
)
|
|
4687
|
-
expect(() => createApplicationServer({ port: -1 })).
|
|
4685
|
+
expect(() => createApplicationServer({ port: -1 })).toThrow(
|
|
4688
4686
|
expect.objectContaining({ code: 'CONFIG' }),
|
|
4689
4687
|
)
|
|
4690
|
-
expect(() => createApplicationServer({ timeout: 0 })).
|
|
4688
|
+
expect(() => createApplicationServer({ timeout: 0 })).toThrow(
|
|
4691
4689
|
expect.objectContaining({ code: 'CONFIG' }),
|
|
4692
4690
|
)
|
|
4693
4691
|
for (const value of [null, 42, [], { host: 42 }, { port: [42] }]) {
|
|
4694
|
-
expect(() => Reflect.apply(createApplicationServer, undefined, [value])).
|
|
4692
|
+
expect(() => Reflect.apply(createApplicationServer, undefined, [value])).toThrow(
|
|
4695
4693
|
expect.objectContaining({ code: 'CONFIG' }),
|
|
4696
4694
|
)
|
|
4697
4695
|
}
|
|
@@ -4711,14 +4709,10 @@ describe('ApplicationServer', () => {
|
|
|
4711
4709
|
expect(server.port).toBe(0)
|
|
4712
4710
|
|
|
4713
4711
|
process.env.APP_PORT = '1e3'
|
|
4714
|
-
expect(() => createApplicationServer()).
|
|
4715
|
-
expect.objectContaining({ code: 'CONFIG' }),
|
|
4716
|
-
)
|
|
4712
|
+
expect(() => createApplicationServer()).toThrow(expect.objectContaining({ code: 'CONFIG' }))
|
|
4717
4713
|
process.env.APP_PORT = '0'
|
|
4718
4714
|
process.env.APP_START_TIMEOUT = '0'
|
|
4719
|
-
expect(() => createApplicationServer()).
|
|
4720
|
-
expect.objectContaining({ code: 'CONFIG' }),
|
|
4721
|
-
)
|
|
4715
|
+
expect(() => createApplicationServer()).toThrow(expect.objectContaining({ code: 'CONFIG' }))
|
|
4722
4716
|
} finally {
|
|
4723
4717
|
if (previousHost === undefined) delete process.env.APP_HOST
|
|
4724
4718
|
else process.env.APP_HOST = previousHost
|
|
@@ -5087,15 +5081,13 @@ describe('application environment parsers', () => {
|
|
|
5087
5081
|
'999.999.999.999',
|
|
5088
5082
|
\`\${'a'.repeat(64)}.example\`,
|
|
5089
5083
|
])('rejects hostile APP_HOST value %s before listener allocation', (value) => {
|
|
5090
|
-
expect(() => parseApplicationHost(value)).
|
|
5091
|
-
expect.objectContaining({ code: 'CONFIG' }),
|
|
5092
|
-
)
|
|
5084
|
+
expect(() => parseApplicationHost(value)).toThrow(expect.objectContaining({ code: 'CONFIG' }))
|
|
5093
5085
|
})
|
|
5094
5086
|
|
|
5095
5087
|
it.each([null, 42, [], { port: [42] }, { timeout: 0 }])(
|
|
5096
5088
|
'rejects hostile option container or leaf value %#',
|
|
5097
5089
|
(value) => {
|
|
5098
|
-
expect(() => parseApplicationServerOptions(value)).
|
|
5090
|
+
expect(() => parseApplicationServerOptions(value)).toThrow(
|
|
5099
5091
|
expect.objectContaining({ code: 'CONFIG' }),
|
|
5100
5092
|
)
|
|
5101
5093
|
},
|
|
@@ -5118,7 +5110,7 @@ describe('application environment parsers', () => {
|
|
|
5118
5110
|
])(
|
|
5119
5111
|
'rejects inherited, unknown, symbolic, accessor, instance, and hostile proxy options %#',
|
|
5120
5112
|
(value) => {
|
|
5121
|
-
expect(() => parseApplicationServerOptions(value)).
|
|
5113
|
+
expect(() => parseApplicationServerOptions(value)).toThrow(
|
|
5122
5114
|
expect.objectContaining({ code: 'CONFIG' }),
|
|
5123
5115
|
)
|
|
5124
5116
|
},
|
|
@@ -5629,7 +5621,7 @@ function packageManifest(spec) {
|
|
|
5629
5621
|
}
|
|
5630
5622
|
if (spec.bin) scripts["test:src:bin"] = "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:bin";
|
|
5631
5623
|
if (spec.integration) scripts["test:integration"] = "vitest run --config vite.config.ts --no-cache --reporter=dot --project integration";
|
|
5632
|
-
if (spec.bin && spec.integration) scripts["test:equivalence"] = "node -e \"const c=require('node:child_process'),
|
|
5624
|
+
if (spec.bin && spec.integration) scripts["test:equivalence"] = "node -e \"const c=require('node:child_process'),p=process.env.npm_execpath;if(p===undefined)process.exit(1);const r=c.spawnSync(process.execPath,[p,'run','test:integration'],{stdio:'inherit',env:{...process.env,SCAFFOLD_BOUNDARY_EQUIVALENCE:'1'}});process.exit(r.status??1)\"";
|
|
5633
5625
|
if (spec.service) scripts["test:service"] = "vitest run --config vite.config.ts --no-cache --reporter=dot --project service";
|
|
5634
5626
|
if (spec.app.length > 0) {
|
|
5635
5627
|
scripts["test:app"] = "vitest run --config vite.config.ts --no-cache --reporter=dot " + spec.app.map((environment) => `--project ${APP_MATRIX[environment].project}`).join(" ");
|
|
@@ -5723,6 +5715,7 @@ function rootTsconfig(src, app = []) {
|
|
|
5723
5715
|
target: "ESNext",
|
|
5724
5716
|
module: "ESNext",
|
|
5725
5717
|
moduleResolution: "bundler",
|
|
5718
|
+
allowImportingTsExtensions: true,
|
|
5726
5719
|
lib: [
|
|
5727
5720
|
"ESNext",
|
|
5728
5721
|
"DOM",
|
|
@@ -5872,7 +5865,7 @@ ${renderedProjects}
|
|
|
5872
5865
|
${fitsPrintWidth(inlineRegistrations) ? inlineRegistrations : ` [
|
|
5873
5866
|
${renderedRegistrations.map((registration) => ` ${registration},`).join("\n")}
|
|
5874
5867
|
],`}
|
|
5875
|
-
|
|
5868
|
+
browserOptions !== undefined,
|
|
5876
5869
|
process.argv,
|
|
5877
5870
|
),`;
|
|
5878
5871
|
}
|
|
@@ -5896,6 +5889,8 @@ ${renderedRegistrations.map((registration) => ` ${registration},`).join("\n")}
|
|
|
5896
5889
|
*/
|
|
5897
5890
|
function viteHeader(machinery) {
|
|
5898
5891
|
const { browser: needsBrowser, vue: needsVue, output: needsOutput } = machinery;
|
|
5892
|
+
const playwrightTypeImports = needsBrowser ? `import type { PlaywrightProviderOptions } from '@vitest/browser-playwright'
|
|
5893
|
+
` : "";
|
|
5899
5894
|
const playwrightImports = needsBrowser ? `import { playwright } from '@vitest/browser-playwright'
|
|
5900
5895
|
import { chromium } from 'playwright'
|
|
5901
5896
|
` : "";
|
|
@@ -6206,7 +6201,9 @@ import { parse as parseVue } from 'vue/compiler-sfc'
|
|
|
6206
6201
|
? undefined
|
|
6207
6202
|
: packageRootOf(packageName, physicalSource)
|
|
6208
6203
|
if (packageRoot === undefined || !containedPath(packageRoot, physicalSource)) {
|
|
6209
|
-
this.error(
|
|
6204
|
+
return this.error(
|
|
6205
|
+
'Resolved dependencies must remain inside their physical package root',
|
|
6206
|
+
)
|
|
6210
6207
|
}
|
|
6211
6208
|
trustedPackageRoots.add(packageRoot)
|
|
6212
6209
|
}
|
|
@@ -6214,7 +6211,7 @@ import { parse as parseVue } from 'vue/compiler-sfc'
|
|
|
6214
6211
|
}
|
|
6215
6212
|
const resolvedSource = workspacePath(physicalSource)
|
|
6216
6213
|
if (resolvedSource === undefined) {
|
|
6217
|
-
this.error('Environment modules cannot import files outside the workspace')
|
|
6214
|
+
return this.error('Environment modules cannot import files outside the workspace')
|
|
6218
6215
|
}
|
|
6219
6216
|
const assetError = environmentPathError(owner, resolvedSource)
|
|
6220
6217
|
if (assetError !== undefined) this.error(assetError)
|
|
@@ -7243,7 +7240,7 @@ ${needsVue ? ` configureServer(server) {
|
|
|
7243
7240
|
}
|
|
7244
7241
|
}
|
|
7245
7242
|
`;
|
|
7246
|
-
return `${viteTypeImports}
|
|
7243
|
+
return `${playwrightTypeImports}${viteTypeImports}
|
|
7247
7244
|
${needsBrowser ? `import { isCSSRequest, parseSync, preprocessCSS, transformWithOxc, Visitor } from 'vite'
|
|
7248
7245
|
` : `import { parseSync, transformWithOxc, Visitor } from 'vite'
|
|
7249
7246
|
`}import { defineConfig, mergeConfig } from 'vitest/config'
|
|
@@ -7251,7 +7248,7 @@ import tsconfig from './tsconfig.json' with { type: 'json' }
|
|
|
7251
7248
|
import { fileURLToPath, URL } from 'node:url'
|
|
7252
7249
|
import { isBuiltin } from 'node:module'
|
|
7253
7250
|
import {
|
|
7254
|
-
closeSync,
|
|
7251
|
+
${needsBrowser ? " accessSync,\n" : ""} closeSync,
|
|
7255
7252
|
constants as FS_CONSTANTS,
|
|
7256
7253
|
existsSync,
|
|
7257
7254
|
fstatSync,
|
|
@@ -7260,7 +7257,15 @@ import {
|
|
|
7260
7257
|
${needsBrowser ? " readdirSync,\n" : ""} readSync,
|
|
7261
7258
|
realpathSync,
|
|
7262
7259
|
${needsBrowser ? " statSync,\n" : ""}} from 'node:fs'
|
|
7263
|
-
|
|
7260
|
+
${needsBrowser ? `import {
|
|
7261
|
+
basename,
|
|
7262
|
+
join,
|
|
7263
|
+
dirname,
|
|
7264
|
+
isAbsolute,
|
|
7265
|
+
relative,
|
|
7266
|
+
resolve as resolvePath,
|
|
7267
|
+
sep,
|
|
7268
|
+
} from 'node:path'` : `import { dirname, isAbsolute, relative, resolve as resolvePath, sep } from 'node:path'`}
|
|
7264
7269
|
${playwrightImports}${vueImports}${needsBrowser ? `\n/** Chromium executable layouts inside a \`chromium-<revision>\` browsers-directory entry, per platform. */
|
|
7265
7270
|
${EXPORT_KEYWORD} ${CONST_KEYWORD} CHROMIUM_LAYOUTS = Object.freeze([
|
|
7266
7271
|
'chrome-linux/chrome',
|
|
@@ -7271,15 +7276,64 @@ ${EXPORT_KEYWORD} ${CONST_KEYWORD} CHROMIUM_LAYOUTS = Object.freeze([
|
|
|
7271
7276
|
'chrome-mac-arm64/Chromium.app/Contents/MacOS/Chromium',
|
|
7272
7277
|
])
|
|
7273
7278
|
|
|
7279
|
+
/** Stable Playwright Chromium channels and their standard executable layouts. */
|
|
7280
|
+
${EXPORT_KEYWORD} ${CONST_KEYWORD} SYSTEM_BROWSER_CHANNELS = Object.freeze([
|
|
7281
|
+
Object.freeze({
|
|
7282
|
+
channel: 'chrome',
|
|
7283
|
+
layouts: Object.freeze({
|
|
7284
|
+
linux: '/opt/google/chrome/chrome',
|
|
7285
|
+
darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
7286
|
+
win32: Object.freeze(['Google', 'Chrome', 'Application', 'chrome.exe']),
|
|
7287
|
+
}),
|
|
7288
|
+
}),
|
|
7289
|
+
Object.freeze({
|
|
7290
|
+
channel: 'msedge',
|
|
7291
|
+
layouts: Object.freeze({
|
|
7292
|
+
linux: '/opt/microsoft/msedge/msedge',
|
|
7293
|
+
darwin: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
|
|
7294
|
+
win32: Object.freeze(['Microsoft', 'Edge', 'Application', 'msedge.exe']),
|
|
7295
|
+
}),
|
|
7296
|
+
}),
|
|
7297
|
+
])
|
|
7298
|
+
|
|
7299
|
+
/**
|
|
7300
|
+
* Determine whether a path identifies an executable regular file.
|
|
7301
|
+
*
|
|
7302
|
+
* @param path - The filesystem path to inspect.
|
|
7303
|
+
* @returns Whether the path is a regular file with execute access.
|
|
7304
|
+
*
|
|
7305
|
+
* @example
|
|
7306
|
+
* \`\`\`ts
|
|
7307
|
+
* isBrowserExecutable('/opt/google/chrome/chrome')
|
|
7308
|
+
* \`\`\`
|
|
7309
|
+
*/
|
|
7310
|
+
${EXPORT_KEYWORD} function isBrowserExecutable(path: string): boolean {
|
|
7311
|
+
try {
|
|
7312
|
+
if (!statSync(path).isFile()) return false
|
|
7313
|
+
accessSync(path, FS_CONSTANTS.X_OK)
|
|
7314
|
+
return true
|
|
7315
|
+
} catch {
|
|
7316
|
+
return false
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
|
|
7274
7320
|
/**
|
|
7275
|
-
* Resolve a launchable Chromium executable: the pinned revision when installed,
|
|
7321
|
+
* Resolve a launchable Playwright-managed Chromium executable: the pinned revision when installed,
|
|
7276
7322
|
* otherwise a \`chromium\` / \`chromium.exe\` alias or any other \`chromium-*\`
|
|
7277
7323
|
* revision under the same Playwright browsers directory. A pinned-revision miss
|
|
7278
7324
|
* is not Chromium absence — managed containers ship one usable build (often
|
|
7279
7325
|
* behind a revision-agnostic alias) for many Playwright versions.
|
|
7326
|
+
*
|
|
7327
|
+
* @param pinned - The executable path for Playwright's pinned Chromium revision.
|
|
7328
|
+
* @returns The managed executable path, or \`undefined\` when none is executable.
|
|
7329
|
+
*
|
|
7330
|
+
* @example
|
|
7331
|
+
* \`\`\`ts
|
|
7332
|
+
* resolveManagedBrowser(chromium.executablePath())
|
|
7333
|
+
* \`\`\`
|
|
7280
7334
|
*/
|
|
7281
|
-
${EXPORT_KEYWORD} function
|
|
7282
|
-
if (
|
|
7335
|
+
${EXPORT_KEYWORD} function resolveManagedBrowser(pinned: string): string | undefined {
|
|
7336
|
+
if (isBrowserExecutable(pinned)) return pinned
|
|
7283
7337
|
let revisionRoot = dirname(pinned)
|
|
7284
7338
|
for (;;) {
|
|
7285
7339
|
if (/^chromium-\\d+$/.test(basename(revisionRoot))) break
|
|
@@ -7290,7 +7344,7 @@ ${EXPORT_KEYWORD} function resolveChromium(pinned: string): string | undefined {
|
|
|
7290
7344
|
const browsersRoot = dirname(revisionRoot)
|
|
7291
7345
|
for (const alias of ['chromium', 'chromium.exe']) {
|
|
7292
7346
|
const candidate = resolvePath(browsersRoot, alias)
|
|
7293
|
-
if (
|
|
7347
|
+
if (isBrowserExecutable(candidate)) return candidate
|
|
7294
7348
|
}
|
|
7295
7349
|
let entries: readonly string[]
|
|
7296
7350
|
try {
|
|
@@ -7304,18 +7358,89 @@ ${EXPORT_KEYWORD} function resolveChromium(pinned: string): string | undefined {
|
|
|
7304
7358
|
for (const revision of revisions) {
|
|
7305
7359
|
for (const layout of CHROMIUM_LAYOUTS) {
|
|
7306
7360
|
const candidate = resolvePath(browsersRoot, revision, layout)
|
|
7307
|
-
if (
|
|
7361
|
+
if (isBrowserExecutable(candidate)) return candidate
|
|
7308
7362
|
}
|
|
7309
7363
|
}
|
|
7310
7364
|
return undefined
|
|
7311
7365
|
}
|
|
7312
7366
|
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7367
|
+
/**
|
|
7368
|
+
* Resolve the first installed stable system Chromium channel.
|
|
7369
|
+
*
|
|
7370
|
+
* @param platform - The Node platform whose standard layouts should be probed.
|
|
7371
|
+
* @param environment - The process environment supplying Windows installation roots.
|
|
7372
|
+
* @returns \`chrome\`, then \`msedge\`, or \`undefined\` when neither is executable.
|
|
7373
|
+
*
|
|
7374
|
+
* @example
|
|
7375
|
+
* \`\`\`ts
|
|
7376
|
+
* resolveSystemBrowser(process.platform, process.env)
|
|
7377
|
+
* \`\`\`
|
|
7378
|
+
*/
|
|
7379
|
+
${EXPORT_KEYWORD} function resolveSystemBrowser(
|
|
7380
|
+
platform: NodeJS.Platform,
|
|
7381
|
+
environment: NodeJS.ProcessEnv,
|
|
7382
|
+
): string | undefined {
|
|
7383
|
+
if (platform !== 'linux' && platform !== 'darwin' && platform !== 'win32') return undefined
|
|
7384
|
+
const roots = new Set<string>()
|
|
7385
|
+
if (platform === 'win32') {
|
|
7386
|
+
for (const root of [
|
|
7387
|
+
environment.LOCALAPPDATA,
|
|
7388
|
+
environment.PROGRAMFILES,
|
|
7389
|
+
environment['PROGRAMFILES(X86)'],
|
|
7390
|
+
]) {
|
|
7391
|
+
if (root !== undefined && root.length > 0) roots.add(root)
|
|
7392
|
+
}
|
|
7393
|
+
const homeDrive = environment.HOMEDRIVE
|
|
7394
|
+
if (homeDrive !== undefined && homeDrive.length > 0) {
|
|
7395
|
+
roots.add(join(homeDrive, 'Program Files'))
|
|
7396
|
+
roots.add(join(homeDrive, 'Program Files (x86)'))
|
|
7397
|
+
}
|
|
7398
|
+
}
|
|
7399
|
+
for (const browser of SYSTEM_BROWSER_CHANNELS) {
|
|
7400
|
+
if (platform === 'win32') {
|
|
7401
|
+
for (const root of roots) {
|
|
7402
|
+
if (isBrowserExecutable(join(root, ...browser.layouts.win32))) return browser.channel
|
|
7403
|
+
}
|
|
7404
|
+
continue
|
|
7405
|
+
}
|
|
7406
|
+
if (isBrowserExecutable(browser.layouts[platform])) return browser.channel
|
|
7407
|
+
}
|
|
7408
|
+
return undefined
|
|
7409
|
+
}
|
|
7410
|
+
|
|
7411
|
+
/**
|
|
7412
|
+
* Resolve launch options for a managed Chromium or stable system browser.
|
|
7413
|
+
*
|
|
7414
|
+
* @param pinned - The executable path for Playwright's pinned Chromium revision.
|
|
7415
|
+
* @param platform - The Node platform whose standard system layouts should be probed.
|
|
7416
|
+
* @param environment - The process environment supplying Windows installation roots.
|
|
7417
|
+
* @returns Provider options for managed Chromium, Chrome, or Edge, or \`undefined\`.
|
|
7418
|
+
*
|
|
7419
|
+
* @remarks
|
|
7420
|
+
* An installed pinned revision returns an empty object so Playwright retains
|
|
7421
|
+
* its default launch semantics. A different managed executable is selected by
|
|
7422
|
+
* path; a system browser is selected by its stable Playwright channel.
|
|
7423
|
+
*
|
|
7424
|
+
* @example
|
|
7425
|
+
* \`\`\`ts
|
|
7426
|
+
* resolveBrowser(chromium.executablePath(), process.platform, process.env)
|
|
7427
|
+
* \`\`\`
|
|
7428
|
+
*/
|
|
7429
|
+
${EXPORT_KEYWORD} function resolveBrowser(
|
|
7430
|
+
pinned: string,
|
|
7431
|
+
platform: NodeJS.Platform,
|
|
7432
|
+
environment: NodeJS.ProcessEnv,
|
|
7433
|
+
): PlaywrightProviderOptions | undefined {
|
|
7434
|
+
const managed = resolveManagedBrowser(pinned)
|
|
7435
|
+
if (managed !== undefined) {
|
|
7436
|
+
return managed === pinned ? {} : { launchOptions: { executablePath: managed } }
|
|
7437
|
+
}
|
|
7438
|
+
const channel = resolveSystemBrowser(platform, environment)
|
|
7439
|
+
return channel === undefined ? undefined : { launchOptions: { channel } }
|
|
7440
|
+
}
|
|
7441
|
+
|
|
7442
|
+
${CONST_KEYWORD} browserPinned = chromium.executablePath()
|
|
7443
|
+
${CONST_KEYWORD} browserOptions = resolveBrowser(browserPinned, process.platform, process.env)
|
|
7319
7444
|
` : ""}
|
|
7320
7445
|
${EXPORT_KEYWORD} function resolveWorkspacePath(relativePath: string): string {
|
|
7321
7446
|
return fileURLToPath(new URL(relativePath, import.meta.url))
|
|
@@ -7364,7 +7489,9 @@ ${needsBrowser ? `${EXPORT_KEYWORD} function gateBrowserProjects(
|
|
|
7364
7489
|
projects.push(registration.project())
|
|
7365
7490
|
}
|
|
7366
7491
|
if (gated.length === 0) return { projects }
|
|
7367
|
-
console.warn(
|
|
7492
|
+
console.warn(
|
|
7493
|
+
\`browser projects skipped: no Playwright Chromium, Chrome, or Edge found (\${gated.join(', ')})\`,
|
|
7494
|
+
)
|
|
7368
7495
|
const filters: string[] = []
|
|
7369
7496
|
let readable = true
|
|
7370
7497
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -7655,7 +7782,7 @@ ${EXPORT_KEYWORD} const srcBrowser = (config?: UserConfig): UserConfig =>
|
|
|
7655
7782
|
}),
|
|
7656
7783
|
browser: {
|
|
7657
7784
|
enabled: true,
|
|
7658
|
-
provider: playwright(
|
|
7785
|
+
provider: playwright(browserOptions),
|
|
7659
7786
|
instances: [{ browser: 'chromium', headless: true }],
|
|
7660
7787
|
},
|
|
7661
7788
|
fileParallelism: false,
|
|
@@ -7689,6 +7816,7 @@ ${EXPORT_KEYWORD} const srcServer = (config?: UserConfig): UserConfig =>
|
|
|
7689
7816
|
outDir: 'dist/src/server',
|
|
7690
7817
|
target: 'node22',
|
|
7691
7818
|
rolldownOptions: {
|
|
7819
|
+
platform: 'node',
|
|
7692
7820
|
external: (id: string) => id.startsWith('node:') || id.startsWith('@orkestrel/'),
|
|
7693
7821
|
},
|
|
7694
7822
|
},
|
|
@@ -7787,7 +7915,7 @@ ${EXPORT_KEYWORD} const srcBrowser = (config?: UserConfig): UserConfig =>
|
|
|
7787
7915
|
}),
|
|
7788
7916
|
browser: {
|
|
7789
7917
|
enabled: true,
|
|
7790
|
-
provider: playwright(
|
|
7918
|
+
provider: playwright(browserOptions),
|
|
7791
7919
|
instances: [{ browser: 'chromium', headless: true }],
|
|
7792
7920
|
},
|
|
7793
7921
|
fileParallelism: false,
|
|
@@ -7812,6 +7940,7 @@ ${EXPORT_KEYWORD} const srcServer = (config?: UserConfig): UserConfig =>
|
|
|
7812
7940
|
outDir: 'dist/src/server',
|
|
7813
7941
|
target: 'node22',
|
|
7814
7942
|
rolldownOptions: {
|
|
7943
|
+
platform: 'node',
|
|
7815
7944
|
external: (id: string) =>
|
|
7816
7945
|
id === '@src/core' || id.startsWith('node:') || id.startsWith('@orkestrel/'),
|
|
7817
7946
|
output: [
|
|
@@ -7952,7 +8081,7 @@ ${EXPORT_KEYWORD} const srcBrowser = (config?: UserConfig): UserConfig =>
|
|
|
7952
8081
|
}),
|
|
7953
8082
|
browser: {
|
|
7954
8083
|
enabled: true,
|
|
7955
|
-
provider: playwright(
|
|
8084
|
+
provider: playwright(browserOptions),
|
|
7956
8085
|
instances: [{ browser: 'chromium', headless: true }],
|
|
7957
8086
|
},
|
|
7958
8087
|
fileParallelism: false,
|
|
@@ -7996,6 +8125,7 @@ ${EXPORT_KEYWORD} const srcServer = (config?: UserConfig): UserConfig =>
|
|
|
7996
8125
|
outDir: 'dist/src/server',
|
|
7997
8126
|
target: 'node22',
|
|
7998
8127
|
rolldownOptions: {
|
|
8128
|
+
platform: 'node',
|
|
7999
8129
|
external: (id: string) =>
|
|
8000
8130
|
${coreExternal}id.startsWith('node:') || id.startsWith('@orkestrel/'),${coreOutput}
|
|
8001
8131
|
},
|
|
@@ -8081,7 +8211,7 @@ ${EXPORT_KEYWORD} function appBrowser(...config: readonly never[]): UserConfig {
|
|
|
8081
8211
|
},
|
|
8082
8212
|
browser: {
|
|
8083
8213
|
enabled: true,
|
|
8084
|
-
provider: playwright(
|
|
8214
|
+
provider: playwright(browserOptions),
|
|
8085
8215
|
instances: [{ browser: 'chromium', headless: true }],
|
|
8086
8216
|
},
|
|
8087
8217
|
fileParallelism: false,
|
|
@@ -8174,7 +8304,7 @@ import {
|
|
|
8174
8304
|
outputBoundary,
|
|
8175
8305
|
srcCore,
|
|
8176
8306
|
resolveWorkspacePath,
|
|
8177
|
-
} from '../../vite.config'
|
|
8307
|
+
} from '../../vite.config.ts'
|
|
8178
8308
|
|
|
8179
8309
|
export default defineConfig(
|
|
8180
8310
|
srcCore({
|
|
@@ -8259,7 +8389,7 @@ function srcViteConfig(environment) {
|
|
|
8259
8389
|
const anchor = environment === "browser" ? "srcBrowser" : "srcServer";
|
|
8260
8390
|
return `import { defineConfig } from 'vite'
|
|
8261
8391
|
import dts from 'vite-plugin-dts'
|
|
8262
|
-
import { ${anchor}, resolveWorkspacePath } from '../../vite.config'
|
|
8392
|
+
import { ${anchor}, resolveWorkspacePath } from '../../vite.config.ts'
|
|
8263
8393
|
|
|
8264
8394
|
// Types are bundled inline by vite-plugin-dts (see configs/src/vite.core.config.ts
|
|
8265
8395
|
// for the same pattern).
|
|
@@ -8312,7 +8442,7 @@ function binTsconfig() {
|
|
|
8312
8442
|
*/
|
|
8313
8443
|
function binViteConfig() {
|
|
8314
8444
|
return `import { defineConfig } from 'vite'
|
|
8315
|
-
import { srcBin } from '../../vite.config'
|
|
8445
|
+
import { srcBin } from '../../vite.config.ts'
|
|
8316
8446
|
|
|
8317
8447
|
// The \`scaffold\` executable build — a single ESM lib file, no declarations (an
|
|
8318
8448
|
// executable ships no types), with the \`#!/usr/bin/env node\` shebang re-emitted via
|
|
@@ -8379,7 +8509,7 @@ function appTsconfig(environment, hasCore) {
|
|
|
8379
8509
|
function appViteConfig(environment) {
|
|
8380
8510
|
const anchor = environment === "browser" ? "appBrowser" : "appServer";
|
|
8381
8511
|
return `import { defineConfig } from 'vite'
|
|
8382
|
-
import { ${anchor} } from '../../vite.config'
|
|
8512
|
+
import { ${anchor} } from '../../vite.config.ts'
|
|
8383
8513
|
|
|
8384
8514
|
export default defineConfig(${anchor}())
|
|
8385
8515
|
`;
|
|
@@ -8648,8 +8778,7 @@ function paritySpecifiers(spec) {
|
|
|
8648
8778
|
function testArtifacts(spec, pascal) {
|
|
8649
8779
|
const hasBrowser = spec.src.includes("browser") || spec.app.includes("browser");
|
|
8650
8780
|
const hasVue = spec.app.includes("browser");
|
|
8651
|
-
const
|
|
8652
|
-
const browserPolicyImport = hasBrowser ? "\nimport { chromium } from 'playwright'\nimport { resolveChromium } from '../vite.config.js'" : "";
|
|
8781
|
+
const browserPolicyImport = hasBrowser ? "\nimport { chromium } from 'playwright'\nimport { isBrowserExecutable, resolveBrowser, SYSTEM_BROWSER_CHANNELS } from '../vite.config.js'" : "";
|
|
8653
8782
|
const vuePolicyImport = hasVue ? "\nimport { parse as parseVue } from 'vue/compiler-sfc'" : "";
|
|
8654
8783
|
const workspacePolicyAssertion = hasVue ? `expect(
|
|
8655
8784
|
inspectCodingWorkspace(process.cwd(), (path, content) => {
|
|
@@ -8662,15 +8791,20 @@ function testArtifacts(spec, pascal) {
|
|
|
8662
8791
|
).toEqual([])` : "expect(inspectCodingWorkspace(process.cwd())).toEqual([])";
|
|
8663
8792
|
const browserPolicyTest = hasBrowser ? `
|
|
8664
8793
|
|
|
8665
|
-
it('resolves
|
|
8666
|
-
const
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8794
|
+
it('resolves only a real managed executable or stable system browser channel', () => {
|
|
8795
|
+
const options = resolveBrowser(chromium.executablePath(), process.platform, process.env)
|
|
8796
|
+
let valid = options === undefined
|
|
8797
|
+
if (options !== undefined) {
|
|
8798
|
+
const channel = options.launchOptions?.channel
|
|
8799
|
+
valid =
|
|
8800
|
+
channel === undefined
|
|
8801
|
+
? isBrowserExecutable(options.launchOptions?.executablePath ?? chromium.executablePath())
|
|
8802
|
+
: SYSTEM_BROWSER_CHANNELS.some((browser) => browser.channel === channel)
|
|
8803
|
+
}
|
|
8804
|
+
expect(valid).toBe(true)
|
|
8671
8805
|
})` : "";
|
|
8672
8806
|
const artifacts = [fillArtifact("tests/setup.ts", "tests", "setup", {}), fillArtifact("tests/policy.test.ts", "tests", "policyTest", {
|
|
8673
|
-
browserPolicySpecifier,
|
|
8807
|
+
browserPolicySpecifier: "",
|
|
8674
8808
|
browserPolicyImport,
|
|
8675
8809
|
browserPolicyTest,
|
|
8676
8810
|
vuePolicyImport,
|