@orkestrel/scaffold 0.0.15 → 0.0.17

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.
@@ -227,7 +227,7 @@ var DEFAULT_VERSION = "0.0.1";
227
227
  /** The `engines.node` range the `blueprint` builder fills. */
228
228
  var DEFAULT_ENGINES = `>=${MINIMUM_NODE_VERSION}`;
229
229
  /** The devDependency range generated packages pin `@orkestrel/scaffold` at. */
230
- var SCAFFOLD_RANGE = "^0.0.15";
230
+ var SCAFFOLD_RANGE = "^0.0.17";
231
231
  /** Tooling versions shared by scaffold and every generated workspace. */
232
232
  var BASE_DEV_DEPENDENCIES = Object.freeze({
233
233
  "@microsoft/api-extractor": "^7.58.12",
@@ -4140,9 +4140,7 @@ ${EXPORT_KEYWORD} ${FUNCTION_KEYWORD} isBrowserVuePath(path: string): boolean {
4140
4140
  content: `import { globSync{{browserPolicySpecifier}} } from 'node:fs'
4141
4141
  import { describe, expect, it } from 'vitest'
4142
4142
  import { isBrowserVuePath } from './setup.js'
4143
- import { inspectCodingWorkspace } from './setupPolicy.js'
4144
- {{browserPolicyImport}}
4145
- {{vuePolicyImport}}
4143
+ import { inspectCodingWorkspace } from './setupPolicy.js'{{browserPolicyImport}}{{vuePolicyImport}}
4146
4144
 
4147
4145
  describe('repository coding law', () => {
4148
4146
  it('keeps Vue single-file components exclusively in browser environments', () => {
@@ -4153,8 +4151,7 @@ describe('repository coding law', () => {
4153
4151
 
4154
4152
  it('enforces source placement, exports, readonly contracts, and syntax law', () => {
4155
4153
  {{workspacePolicyAssertion}}
4156
- })
4157
- {{browserPolicyTest}}
4154
+ }){{browserPolicyTest}}
4158
4155
  })
4159
4156
  `
4160
4157
  }),
@@ -5876,7 +5873,7 @@ ${renderedProjects}
5876
5873
  ${fitsPrintWidth(inlineRegistrations) ? inlineRegistrations : ` [
5877
5874
  ${renderedRegistrations.map((registration) => ` ${registration},`).join("\n")}
5878
5875
  ],`}
5879
- chromiumPath !== undefined,
5876
+ browserOptions !== undefined,
5880
5877
  process.argv,
5881
5878
  ),`;
5882
5879
  }
@@ -5900,6 +5897,8 @@ ${renderedRegistrations.map((registration) => ` ${registration},`).join("\n")}
5900
5897
  */
5901
5898
  function viteHeader(machinery) {
5902
5899
  const { browser: needsBrowser, vue: needsVue, output: needsOutput } = machinery;
5900
+ const playwrightTypeImports = needsBrowser ? `import type { PlaywrightProviderOptions } from '@vitest/browser-playwright'
5901
+ ` : "";
5903
5902
  const playwrightImports = needsBrowser ? `import { playwright } from '@vitest/browser-playwright'
5904
5903
  import { chromium } from 'playwright'
5905
5904
  ` : "";
@@ -6210,7 +6209,9 @@ import { parse as parseVue } from 'vue/compiler-sfc'
6210
6209
  ? undefined
6211
6210
  : packageRootOf(packageName, physicalSource)
6212
6211
  if (packageRoot === undefined || !containedPath(packageRoot, physicalSource)) {
6213
- this.error('Resolved dependencies must remain inside their physical package root')
6212
+ return this.error(
6213
+ 'Resolved dependencies must remain inside their physical package root',
6214
+ )
6214
6215
  }
6215
6216
  trustedPackageRoots.add(packageRoot)
6216
6217
  }
@@ -6218,7 +6219,7 @@ import { parse as parseVue } from 'vue/compiler-sfc'
6218
6219
  }
6219
6220
  const resolvedSource = workspacePath(physicalSource)
6220
6221
  if (resolvedSource === undefined) {
6221
- this.error('Environment modules cannot import files outside the workspace')
6222
+ return this.error('Environment modules cannot import files outside the workspace')
6222
6223
  }
6223
6224
  const assetError = environmentPathError(owner, resolvedSource)
6224
6225
  if (assetError !== undefined) this.error(assetError)
@@ -7247,7 +7248,7 @@ ${needsVue ? ` configureServer(server) {
7247
7248
  }
7248
7249
  }
7249
7250
  `;
7250
- return `${viteTypeImports}
7251
+ return `${playwrightTypeImports}${viteTypeImports}
7251
7252
  ${needsBrowser ? `import { isCSSRequest, parseSync, preprocessCSS, transformWithOxc, Visitor } from 'vite'
7252
7253
  ` : `import { parseSync, transformWithOxc, Visitor } from 'vite'
7253
7254
  `}import { defineConfig, mergeConfig } from 'vitest/config'
@@ -7255,7 +7256,7 @@ import tsconfig from './tsconfig.json' with { type: 'json' }
7255
7256
  import { fileURLToPath, URL } from 'node:url'
7256
7257
  import { isBuiltin } from 'node:module'
7257
7258
  import {
7258
- closeSync,
7259
+ ${needsBrowser ? " accessSync,\n" : ""} closeSync,
7259
7260
  constants as FS_CONSTANTS,
7260
7261
  existsSync,
7261
7262
  fstatSync,
@@ -7264,7 +7265,15 @@ import {
7264
7265
  ${needsBrowser ? " readdirSync,\n" : ""} readSync,
7265
7266
  realpathSync,
7266
7267
  ${needsBrowser ? " statSync,\n" : ""}} from 'node:fs'
7267
- import { ${needsBrowser ? "basename, " : ""}dirname, isAbsolute, relative, resolve as resolvePath, sep } from 'node:path'
7268
+ ${needsBrowser ? `import {
7269
+ basename,
7270
+ join,
7271
+ dirname,
7272
+ isAbsolute,
7273
+ relative,
7274
+ resolve as resolvePath,
7275
+ sep,
7276
+ } from 'node:path'` : `import { dirname, isAbsolute, relative, resolve as resolvePath, sep } from 'node:path'`}
7268
7277
  ${playwrightImports}${vueImports}${needsBrowser ? `\n/** Chromium executable layouts inside a \`chromium-<revision>\` browsers-directory entry, per platform. */
7269
7278
  ${EXPORT_KEYWORD} ${CONST_KEYWORD} CHROMIUM_LAYOUTS = Object.freeze([
7270
7279
  'chrome-linux/chrome',
@@ -7275,15 +7284,64 @@ ${EXPORT_KEYWORD} ${CONST_KEYWORD} CHROMIUM_LAYOUTS = Object.freeze([
7275
7284
  'chrome-mac-arm64/Chromium.app/Contents/MacOS/Chromium',
7276
7285
  ])
7277
7286
 
7287
+ /** Stable Playwright Chromium channels and their standard executable layouts. */
7288
+ ${EXPORT_KEYWORD} ${CONST_KEYWORD} SYSTEM_BROWSER_CHANNELS = Object.freeze([
7289
+ Object.freeze({
7290
+ channel: 'chrome',
7291
+ layouts: Object.freeze({
7292
+ linux: '/opt/google/chrome/chrome',
7293
+ darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
7294
+ win32: Object.freeze(['Google', 'Chrome', 'Application', 'chrome.exe']),
7295
+ }),
7296
+ }),
7297
+ Object.freeze({
7298
+ channel: 'msedge',
7299
+ layouts: Object.freeze({
7300
+ linux: '/opt/microsoft/msedge/msedge',
7301
+ darwin: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
7302
+ win32: Object.freeze(['Microsoft', 'Edge', 'Application', 'msedge.exe']),
7303
+ }),
7304
+ }),
7305
+ ])
7306
+
7307
+ /**
7308
+ * Determine whether a path identifies an executable regular file.
7309
+ *
7310
+ * @param path - The filesystem path to inspect.
7311
+ * @returns Whether the path is a regular file with execute access.
7312
+ *
7313
+ * @example
7314
+ * \`\`\`ts
7315
+ * isBrowserExecutable('/opt/google/chrome/chrome')
7316
+ * \`\`\`
7317
+ */
7318
+ ${EXPORT_KEYWORD} function isBrowserExecutable(path: string): boolean {
7319
+ try {
7320
+ if (!statSync(path).isFile()) return false
7321
+ accessSync(path, FS_CONSTANTS.X_OK)
7322
+ return true
7323
+ } catch {
7324
+ return false
7325
+ }
7326
+ }
7327
+
7278
7328
  /**
7279
- * Resolve a launchable Chromium executable: the pinned revision when installed,
7329
+ * Resolve a launchable Playwright-managed Chromium executable: the pinned revision when installed,
7280
7330
  * otherwise a \`chromium\` / \`chromium.exe\` alias or any other \`chromium-*\`
7281
7331
  * revision under the same Playwright browsers directory. A pinned-revision miss
7282
7332
  * is not Chromium absence — managed containers ship one usable build (often
7283
7333
  * behind a revision-agnostic alias) for many Playwright versions.
7334
+ *
7335
+ * @param pinned - The executable path for Playwright's pinned Chromium revision.
7336
+ * @returns The managed executable path, or \`undefined\` when none is executable.
7337
+ *
7338
+ * @example
7339
+ * \`\`\`ts
7340
+ * resolveManagedBrowser(chromium.executablePath())
7341
+ * \`\`\`
7284
7342
  */
7285
- ${EXPORT_KEYWORD} function resolveChromium(pinned: string): string | undefined {
7286
- if (existsSync(pinned)) return pinned
7343
+ ${EXPORT_KEYWORD} function resolveManagedBrowser(pinned: string): string | undefined {
7344
+ if (isBrowserExecutable(pinned)) return pinned
7287
7345
  let revisionRoot = dirname(pinned)
7288
7346
  for (;;) {
7289
7347
  if (/^chromium-\\d+$/.test(basename(revisionRoot))) break
@@ -7294,7 +7352,7 @@ ${EXPORT_KEYWORD} function resolveChromium(pinned: string): string | undefined {
7294
7352
  const browsersRoot = dirname(revisionRoot)
7295
7353
  for (const alias of ['chromium', 'chromium.exe']) {
7296
7354
  const candidate = resolvePath(browsersRoot, alias)
7297
- if (existsSync(candidate) && statSync(candidate).isFile()) return candidate
7355
+ if (isBrowserExecutable(candidate)) return candidate
7298
7356
  }
7299
7357
  let entries: readonly string[]
7300
7358
  try {
@@ -7308,18 +7366,89 @@ ${EXPORT_KEYWORD} function resolveChromium(pinned: string): string | undefined {
7308
7366
  for (const revision of revisions) {
7309
7367
  for (const layout of CHROMIUM_LAYOUTS) {
7310
7368
  const candidate = resolvePath(browsersRoot, revision, layout)
7311
- if (existsSync(candidate)) return candidate
7369
+ if (isBrowserExecutable(candidate)) return candidate
7312
7370
  }
7313
7371
  }
7314
7372
  return undefined
7315
7373
  }
7316
7374
 
7317
- ${CONST_KEYWORD} chromiumPinned = chromium.executablePath()
7318
- ${CONST_KEYWORD} chromiumPath = resolveChromium(chromiumPinned)
7319
- ${CONST_KEYWORD} chromiumOptions =
7320
- chromiumPath === undefined || chromiumPath === chromiumPinned
7321
- ? {}
7322
- : { launchOptions: { executablePath: chromiumPath } }
7375
+ /**
7376
+ * Resolve the first installed stable system Chromium channel.
7377
+ *
7378
+ * @param platform - The Node platform whose standard layouts should be probed.
7379
+ * @param environment - The process environment supplying Windows installation roots.
7380
+ * @returns \`chrome\`, then \`msedge\`, or \`undefined\` when neither is executable.
7381
+ *
7382
+ * @example
7383
+ * \`\`\`ts
7384
+ * resolveSystemBrowser(process.platform, process.env)
7385
+ * \`\`\`
7386
+ */
7387
+ ${EXPORT_KEYWORD} function resolveSystemBrowser(
7388
+ platform: NodeJS.Platform,
7389
+ environment: NodeJS.ProcessEnv,
7390
+ ): string | undefined {
7391
+ if (platform !== 'linux' && platform !== 'darwin' && platform !== 'win32') return undefined
7392
+ const roots = new Set<string>()
7393
+ if (platform === 'win32') {
7394
+ for (const root of [
7395
+ environment.LOCALAPPDATA,
7396
+ environment.PROGRAMFILES,
7397
+ environment['PROGRAMFILES(X86)'],
7398
+ ]) {
7399
+ if (root !== undefined && root.length > 0) roots.add(root)
7400
+ }
7401
+ const homeDrive = environment.HOMEDRIVE
7402
+ if (homeDrive !== undefined && homeDrive.length > 0) {
7403
+ roots.add(join(homeDrive, 'Program Files'))
7404
+ roots.add(join(homeDrive, 'Program Files (x86)'))
7405
+ }
7406
+ }
7407
+ for (const browser of SYSTEM_BROWSER_CHANNELS) {
7408
+ if (platform === 'win32') {
7409
+ for (const root of roots) {
7410
+ if (isBrowserExecutable(join(root, ...browser.layouts.win32))) return browser.channel
7411
+ }
7412
+ continue
7413
+ }
7414
+ if (isBrowserExecutable(browser.layouts[platform])) return browser.channel
7415
+ }
7416
+ return undefined
7417
+ }
7418
+
7419
+ /**
7420
+ * Resolve launch options for a managed Chromium or stable system browser.
7421
+ *
7422
+ * @param pinned - The executable path for Playwright's pinned Chromium revision.
7423
+ * @param platform - The Node platform whose standard system layouts should be probed.
7424
+ * @param environment - The process environment supplying Windows installation roots.
7425
+ * @returns Provider options for managed Chromium, Chrome, or Edge, or \`undefined\`.
7426
+ *
7427
+ * @remarks
7428
+ * An installed pinned revision returns an empty object so Playwright retains
7429
+ * its default launch semantics. A different managed executable is selected by
7430
+ * path; a system browser is selected by its stable Playwright channel.
7431
+ *
7432
+ * @example
7433
+ * \`\`\`ts
7434
+ * resolveBrowser(chromium.executablePath(), process.platform, process.env)
7435
+ * \`\`\`
7436
+ */
7437
+ ${EXPORT_KEYWORD} function resolveBrowser(
7438
+ pinned: string,
7439
+ platform: NodeJS.Platform,
7440
+ environment: NodeJS.ProcessEnv,
7441
+ ): PlaywrightProviderOptions | undefined {
7442
+ const managed = resolveManagedBrowser(pinned)
7443
+ if (managed !== undefined) {
7444
+ return managed === pinned ? {} : { launchOptions: { executablePath: managed } }
7445
+ }
7446
+ const channel = resolveSystemBrowser(platform, environment)
7447
+ return channel === undefined ? undefined : { launchOptions: { channel } }
7448
+ }
7449
+
7450
+ ${CONST_KEYWORD} browserPinned = chromium.executablePath()
7451
+ ${CONST_KEYWORD} browserOptions = resolveBrowser(browserPinned, process.platform, process.env)
7323
7452
  ` : ""}
7324
7453
  ${EXPORT_KEYWORD} function resolveWorkspacePath(relativePath: string): string {
7325
7454
  return fileURLToPath(new URL(relativePath, import.meta.url))
@@ -7368,7 +7497,9 @@ ${needsBrowser ? `${EXPORT_KEYWORD} function gateBrowserProjects(
7368
7497
  projects.push(registration.project())
7369
7498
  }
7370
7499
  if (gated.length === 0) return { projects }
7371
- console.warn(\`browser projects skipped: Chromium absent (\${gated.join(', ')})\`)
7500
+ console.warn(
7501
+ \`browser projects skipped: no Playwright Chromium, Chrome, or Edge found (\${gated.join(', ')})\`,
7502
+ )
7372
7503
  const filters: string[] = []
7373
7504
  let readable = true
7374
7505
  for (let index = 0; index < argv.length; index += 1) {
@@ -7659,7 +7790,7 @@ ${EXPORT_KEYWORD} const srcBrowser = (config?: UserConfig): UserConfig =>
7659
7790
  }),
7660
7791
  browser: {
7661
7792
  enabled: true,
7662
- provider: playwright(chromiumOptions),
7793
+ provider: playwright(browserOptions),
7663
7794
  instances: [{ browser: 'chromium', headless: true }],
7664
7795
  },
7665
7796
  fileParallelism: false,
@@ -7791,7 +7922,7 @@ ${EXPORT_KEYWORD} const srcBrowser = (config?: UserConfig): UserConfig =>
7791
7922
  }),
7792
7923
  browser: {
7793
7924
  enabled: true,
7794
- provider: playwright(chromiumOptions),
7925
+ provider: playwright(browserOptions),
7795
7926
  instances: [{ browser: 'chromium', headless: true }],
7796
7927
  },
7797
7928
  fileParallelism: false,
@@ -7956,7 +8087,7 @@ ${EXPORT_KEYWORD} const srcBrowser = (config?: UserConfig): UserConfig =>
7956
8087
  }),
7957
8088
  browser: {
7958
8089
  enabled: true,
7959
- provider: playwright(chromiumOptions),
8090
+ provider: playwright(browserOptions),
7960
8091
  instances: [{ browser: 'chromium', headless: true }],
7961
8092
  },
7962
8093
  fileParallelism: false,
@@ -8085,7 +8216,7 @@ ${EXPORT_KEYWORD} function appBrowser(...config: readonly never[]): UserConfig {
8085
8216
  },
8086
8217
  browser: {
8087
8218
  enabled: true,
8088
- provider: playwright(chromiumOptions),
8219
+ provider: playwright(browserOptions),
8089
8220
  instances: [{ browser: 'chromium', headless: true }],
8090
8221
  },
8091
8222
  fileParallelism: false,
@@ -8652,9 +8783,8 @@ function paritySpecifiers(spec) {
8652
8783
  function testArtifacts(spec, pascal) {
8653
8784
  const hasBrowser = spec.src.includes("browser") || spec.app.includes("browser");
8654
8785
  const hasVue = spec.app.includes("browser");
8655
- const browserPolicySpecifier = hasBrowser ? ", accessSync, constants as FS_CONSTANTS, statSync" : "";
8656
- const browserPolicyImport = hasBrowser ? "import { chromium } from 'playwright'\nimport { resolveChromium } from '../vite.config.js'" : "";
8657
- const vuePolicyImport = hasVue ? "import { parse as parseVue } from 'vue/compiler-sfc'" : "";
8786
+ const browserPolicyImport = hasBrowser ? "\nimport { chromium } from 'playwright'\nimport { isBrowserExecutable, resolveBrowser, SYSTEM_BROWSER_CHANNELS } from '../vite.config.js'" : "";
8787
+ const vuePolicyImport = hasVue ? "\nimport { parse as parseVue } from 'vue/compiler-sfc'" : "";
8658
8788
  const workspacePolicyAssertion = hasVue ? `expect(
8659
8789
  inspectCodingWorkspace(process.cwd(), (path, content) => {
8660
8790
  const parsed = parseVue(content, { filename: path })
@@ -8665,15 +8795,21 @@ function testArtifacts(spec, pascal) {
8665
8795
  }),
8666
8796
  ).toEqual([])` : "expect(inspectCodingWorkspace(process.cwd())).toEqual([])";
8667
8797
  const browserPolicyTest = hasBrowser ? `
8668
- it('resolves Chromium only to a real executable file', () => {
8669
- const chromiumPath = resolveChromium(chromium.executablePath())
8670
- if (chromiumPath === undefined) return
8671
8798
 
8672
- expect(statSync(chromiumPath).isFile()).toBe(true)
8673
- expect(() => accessSync(chromiumPath, FS_CONSTANTS.X_OK)).not.toThrow()
8799
+ it('resolves only a real managed executable or stable system browser channel', () => {
8800
+ const options = resolveBrowser(chromium.executablePath(), process.platform, process.env)
8801
+ let valid = options === undefined
8802
+ if (options !== undefined) {
8803
+ const channel = options.launchOptions?.channel
8804
+ valid =
8805
+ channel === undefined
8806
+ ? isBrowserExecutable(options.launchOptions?.executablePath ?? chromium.executablePath())
8807
+ : SYSTEM_BROWSER_CHANNELS.some((browser) => browser.channel === channel)
8808
+ }
8809
+ expect(valid).toBe(true)
8674
8810
  })` : "";
8675
8811
  const artifacts = [fillArtifact("tests/setup.ts", "tests", "setup", {}), fillArtifact("tests/policy.test.ts", "tests", "policyTest", {
8676
- browserPolicySpecifier,
8812
+ browserPolicySpecifier: "",
8677
8813
  browserPolicyImport,
8678
8814
  browserPolicyTest,
8679
8815
  vuePolicyImport,