@orkestrel/scaffold 0.0.82 → 0.0.84

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.
@@ -119,7 +119,7 @@ export declare const ARTIFACT_TEMPLATES: Readonly<{
119
119
  tests: Readonly<{
120
120
  journey: "import {\n\tACCESSIBLE_ROLES,\n\tcreatePortfolio,\n\texpandCaptures,\n\tisRendered,\n\treadRefusal,\n\treadStyle,\n} from '@orkestrel/test/browser'\n{{imports}}import { page } from 'vitest/browser'\nimport { describe, expect, inject, it } from 'vitest'\n\nconst VARIANT = inject('variant')\nconst VARIANTS = inject('variants')\nconst CAPTURE = inject('capture')\n\ndescribe('{{application}} arrival journey', () => {\n\tit('proves the declared arrival families', async () => {\n\t\tconst families = new Set(['Journey', 'Refusal', 'Matrix', ...(CAPTURE ? ['Capture'] : [])])\n\t\tconst proven = new Set<string>()\n\t\tconst states = ['{{application}}-arrival']\n\t\tconst placed = new Set<string>()\n\t\tconst portfolio = createPortfolio({\n\t\t\tstates,\n\t\t\tvariants: VARIANTS,\n\t\t\tvariant: VARIANT,\n\t\t\tdirectory: '../../../tmp/captures/states',\n\t\t\tenabled: CAPTURE,\n\t\t})\n{{mount}}\t\ttry {\n{{arrival}}\t\t\tconst heading = page\n\t\t\t\t.getByRole('heading', {\n\t\t\t\t\tname: {{heading}},\n\t\t\t\t\texact: true,\n\t\t\t\t\tlevel: 1,\n\t\t\t\t})\n\t\t\t\t.element()\n\t\t\texpect(isRendered(heading)).toBe(true)\n\t\t\tproven.add('Journey')\n\t\t\tawait portfolio.place('{{application}}-arrival')\n\t\t\tplaced.add('{{application}}-arrival')\n\n\t\t\tfor (const role of ACCESSIBLE_ROLES) expect(page.getByRole(role).elements()).toEqual([])\n\t\t\texpect(readRefusal('Continue')).toBe(\n\t\t\t\t'No interactive element has the accessible name \"Continue\"',\n\t\t\t)\n\t\t\tproven.add('Refusal')\n\n\t\t\tconst measured = new Set<string>()\n\t\t\tfor (const variant of VARIANTS) {\n\t\t\t\tawait page.viewport(variant.width, variant.height)\n\t\t\t\texpect(readStyle(heading, 'display')).toBe('block')\n\t\t\t\texpect(window.innerWidth).toBe(variant.width)\n\t\t\t\tmeasured.add(variant.name)\n\t\t\t}\n\t\t\texpect(measured).toEqual(new Set(VARIANTS.map(({ name }) => name)))\n\t\t\tproven.add('Matrix')\n\n\t\t\tconst files = expandCaptures(states, VARIANTS)\n\t\t\texpect(portfolio.files).toEqual(files)\n\t\t\texpect(new Set(files).size).toBe(states.length * VARIANTS.length)\n\t\t\texpect(placed).toEqual(new Set(states))\n\t\t\tconst expected = CAPTURE\n\t\t\t\t? expandCaptures(\n\t\t\t\t\t\tstates,\n\t\t\t\t\t\tVARIANTS.filter(({ name }) => name === VARIANT),\n\t\t\t\t\t)\n\t\t\t\t: []\n\t\t\texpect(portfolio.paths.map((path) => path.replaceAll('\\\\', '/').split('/').at(-1))).toEqual(\n\t\t\t\texpected,\n\t\t\t)\n\t\t\tif (CAPTURE) proven.add('Capture')\n\t\t\texpect(proven).toEqual(families)\n\t\t} finally {\n{{cleanup}}\t\t\tconst variant = VARIANTS.find(({ name }) => name === VARIANT)\n\t\t\tif (variant !== undefined) await page.viewport(variant.width, variant.height)\n\t\t}\n\t})\n})\n";
121
121
  browser: "import type { JourneyVariant } from '@orkestrel/test'\n\ndeclare module 'vitest' {\n\texport interface ProvidedContext {\n\t\treadonly variant: string\n\t\treadonly variants: readonly JourneyVariant[]\n\t\treadonly capture: boolean\n\t}\n}\n";
122
- sheet: "import sheet from '/dist/src/{{name}}/index.css?raw'\nimport { describe, expect, it } from 'vitest'\nimport { adoptSheet, readLayerNames } from '{{setup}}'\n\ndescribe('{{name}} entry', () => {\n\tit('opens with the declared cascade order', () => {\n\t\tconst adopted = adoptSheet(sheet)\n\t\ttry {\n\t\t\texpect(adopted.sheet.cssRules[0]?.cssText).toBe(\n\t\t\t\t'@layer theme, reset, base, elements, components, utilities;',\n\t\t\t)\n\t\t\texpect(readLayerNames(adopted.sheet).slice(0, 6)).toEqual([\n\t\t\t\t'theme',\n\t\t\t\t'reset',\n\t\t\t\t'base',\n\t\t\t\t'elements',\n\t\t\t\t'components',\n\t\t\t\t'utilities',\n\t\t\t])\n\t\t} finally {\n\t\t\tadopted.release()\n\t\t}\n\t})\n})\n";
122
+ sheet: "import sheet from '{{root}}dist/src/{{name}}/index.css?raw'\nimport { describe, expect, it } from 'vitest'\nimport { adoptSheet, readLayerNames } from '{{setup}}'\n\ndescribe('{{name}} entry', () => {\n\tit('opens with the declared cascade order', () => {\n\t\tconst adopted = adoptSheet(sheet)\n\t\ttry {\n\t\t\texpect(adopted.sheet.cssRules[0]?.cssText).toBe(\n\t\t\t\t'@layer theme, reset, base, elements, components, utilities;',\n\t\t\t)\n\t\t\texpect(readLayerNames(adopted.sheet).slice(0, 6)).toEqual([\n\t\t\t\t'theme',\n\t\t\t\t'reset',\n\t\t\t\t'base',\n\t\t\t\t'elements',\n\t\t\t\t'components',\n\t\t\t\t'utilities',\n\t\t\t])\n\t\t} finally {\n\t\t\tadopted.release()\n\t\t}\n\t})\n})\n";
123
123
  styles: Readonly<{
124
124
  module: "/** Carries a CSSOM rule and its enclosing layer. */\nexport interface SheetEntry {\n\treadonly rule: CSSRule\n\treadonly layer: string | undefined\n}\n\n/** Describes an adopted stylesheet and its idempotent release operation. */\nexport interface SheetAdoption {\n\treadonly sheet: CSSStyleSheet\n\trelease(): void\n}\n\n/**\n * Adopts CSS text into the document with an idempotent release operation.\n * @param text - The compiled sheet text.\n * @returns The adopted sheet and its release operation.\n * @example\n * ```ts\n * const adopted = adoptSheet('.sample { color: red }')\n * adopted.release()\n * ```\n */\nexport function adoptSheet(text: string): SheetAdoption {\n\tconst sheet = new CSSStyleSheet()\n\tsheet.replaceSync(text)\n\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]\n\treturn {\n\t\tsheet,\n\t\trelease: () => {\n\t\t\tdocument.adoptedStyleSheets = document.adoptedStyleSheets.filter(\n\t\t\t\t(candidate) => candidate !== sheet,\n\t\t\t)\n\t\t},\n\t}\n}\n\n/**\n * Reads layer statements and blocks in declaration order, including repeated names.\n * @param sheet - The parsed sheet.\n * @returns Qualified layer names, with an empty name for an anonymous layer.\n * @example\n * ```ts\n * readLayerNames(new CSSStyleSheet()) // []\n * ```\n */\nexport function readLayerNames(sheet: CSSStyleSheet): readonly string[] {\n\tconst pending: SheetEntry[] = Array.from(sheet.cssRules, (rule) => ({\n\t\trule,\n\t\tlayer: undefined,\n\t})).reverse()\n\tconst names: string[] = []\n\twhile (pending.length > 0) {\n\t\tconst entry = pending.pop()\n\t\tif (entry === undefined) break\n\t\tconst { rule, layer } = entry\n\t\tconst declared =\n\t\t\trule instanceof CSSLayerStatementRule\n\t\t\t\t? rule.nameList\n\t\t\t\t: rule instanceof CSSLayerBlockRule\n\t\t\t\t\t? [rule.name]\n\t\t\t\t\t: []\n\t\tfor (const name of declared) names.push(layer === undefined ? name : layer + '.' + name)\n\t\tif (!(rule instanceof CSSGroupingRule)) continue\n\t\tconst parent =\n\t\t\trule instanceof CSSLayerBlockRule\n\t\t\t\t? layer === undefined\n\t\t\t\t\t? rule.name\n\t\t\t\t\t: layer + '.' + rule.name\n\t\t\t\t: layer\n\t\tfor (const child of Array.from(rule.cssRules).reverse()) {\n\t\t\tpending.push({ rule: child, layer: parent })\n\t\t}\n\t}\n\treturn names\n}\n";
125
125
  proof: "import type { SheetAdoption } from './setupStyles.js'\nimport { describe, expect, it } from 'vitest'\nimport { adoptSheet, readLayerNames } from './setupStyles.js'\n\ndescribe('CSSOM instruments', () => {\n\tit('adopts and releases only its own sheet, including repeated release', () => {\n\t\tconst previous = [...document.adoptedStyleSheets]\n\t\tconst first: SheetAdoption = adoptSheet('.sample { color: red }')\n\t\ttry {\n\t\t\tconst second = adoptSheet('.other { display: block }')\n\t\t\ttry {\n\t\t\t\texpect(document.adoptedStyleSheets).toEqual([...previous, first.sheet, second.sheet])\n\t\t\t\texpect(first.sheet.cssRules.length).toBe(1)\n\t\t\t\tfirst.release()\n\t\t\t\tfirst.release()\n\t\t\t\texpect(document.adoptedStyleSheets).toEqual([...previous, second.sheet])\n\t\t\t} finally {\n\t\t\t\tsecond.release()\n\t\t\t}\n\t\t} finally {\n\t\t\tfirst.release()\n\t\t}\n\t\texpect(document.adoptedStyleSheets).toEqual(previous)\n\t})\n\n\tit('reads statements, blocks, nested conditions, and anonymous layers in order', () => {\n\t\tconst adopted = adoptSheet(\n\t\t\t'@layer first, second; @layer first { @media all { @layer nested {} } } @layer {}',\n\t\t)\n\t\ttry {\n\t\t\texpect(readLayerNames(adopted.sheet)).toEqual([\n\t\t\t\t'first',\n\t\t\t\t'second',\n\t\t\t\t'first',\n\t\t\t\t'first.nested',\n\t\t\t\t'',\n\t\t\t])\n\t\t} finally {\n\t\t\tadopted.release()\n\t\t}\n\t})\n\n\tit('returns no layer names for an empty sheet or an unlayered rule', () => {\n\t\tconst sheet = new CSSStyleSheet()\n\t\texpect(readLayerNames(sheet)).toEqual([])\n\t\tsheet.replaceSync('.sample { color: red }')\n\t\texpect(readLayerNames(sheet)).toEqual([])\n\t})\n})\n";
@@ -375,6 +375,13 @@ export declare function blueprintToConfigArtifacts(blueprint: Blueprint): readon
375
375
  * the peer's floor states what consumers may supply rather than what this workspace
376
376
  * develops against.
377
377
  *
378
+ * A workspace that renders a surface receives {@link BROWSE_DEV_DEPENDENCIES}: a browser
379
+ * environment on either axis, a sheet, or themes. A framework face counts without a clause of
380
+ * its own, because a face is selected only on an axis that carries a browser environment.
381
+ * A browser setup runtime alone receives nothing, because it renders no surface for `browse`
382
+ * to drive. A workspace {@link BROWSE_UPSTREAM} names receives nothing either, because
383
+ * `@orkestrel/browser` depends on it at runtime and the declaration would close a cycle.
384
+ *
378
385
  * A peer is declared here as well as under `peerDependencies`, because a peer is
379
386
  * not installed by the workspace that declares it and developing against one
380
387
  * requires it present. A runtime dependency is the opposite case and is removed:
@@ -878,6 +885,28 @@ export declare function blueprintToTestArtifacts(blueprint: Blueprint): readonly
878
885
  */
879
886
  export declare function blueprintToWritableScripts(blueprint: Blueprint): readonly ManifestScript[];
880
887
 
888
+ /**
889
+ * Names the development dependency a browser or styles surface adds.
890
+ *
891
+ * @remarks
892
+ * The package ships the `browse` binary, an MCP server that drives a rendered page and records
893
+ * journeys, and `.claude/rules/quality.md` § Instruments names its registration.
894
+ * `blueprintToDevDependencies` names the blueprints that receive it, and it withholds the
895
+ * dependency from the workspaces {@link BROWSE_UPSTREAM} lists.
896
+ */
897
+ export declare const BROWSE_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
898
+
899
+ /**
900
+ * Lists the packages `@orkestrel/browser` depends on at runtime, directly or transitively, frozen.
901
+ *
902
+ * @remarks
903
+ * A workspace this list names cannot declare `@orkestrel/browser`, because the declaration closes
904
+ * a dependency cycle through the browser package's runtime dependencies. The list is the closure
905
+ * of `dependencies` and `peerDependencies` over the installed manifests, sorted by name, and moves
906
+ * with the `@orkestrel/browser` pin.
907
+ */
908
+ export declare const BROWSE_UPSTREAM: readonly string[];
909
+
881
910
  /** Represents a browser framework and its physically occupied axes. */
882
911
  export declare interface BrowserExtension {
883
912
  readonly surface: 'browser';
@@ -119,7 +119,7 @@ export declare const ARTIFACT_TEMPLATES: Readonly<{
119
119
  tests: Readonly<{
120
120
  journey: "import {\n\tACCESSIBLE_ROLES,\n\tcreatePortfolio,\n\texpandCaptures,\n\tisRendered,\n\treadRefusal,\n\treadStyle,\n} from '@orkestrel/test/browser'\n{{imports}}import { page } from 'vitest/browser'\nimport { describe, expect, inject, it } from 'vitest'\n\nconst VARIANT = inject('variant')\nconst VARIANTS = inject('variants')\nconst CAPTURE = inject('capture')\n\ndescribe('{{application}} arrival journey', () => {\n\tit('proves the declared arrival families', async () => {\n\t\tconst families = new Set(['Journey', 'Refusal', 'Matrix', ...(CAPTURE ? ['Capture'] : [])])\n\t\tconst proven = new Set<string>()\n\t\tconst states = ['{{application}}-arrival']\n\t\tconst placed = new Set<string>()\n\t\tconst portfolio = createPortfolio({\n\t\t\tstates,\n\t\t\tvariants: VARIANTS,\n\t\t\tvariant: VARIANT,\n\t\t\tdirectory: '../../../tmp/captures/states',\n\t\t\tenabled: CAPTURE,\n\t\t})\n{{mount}}\t\ttry {\n{{arrival}}\t\t\tconst heading = page\n\t\t\t\t.getByRole('heading', {\n\t\t\t\t\tname: {{heading}},\n\t\t\t\t\texact: true,\n\t\t\t\t\tlevel: 1,\n\t\t\t\t})\n\t\t\t\t.element()\n\t\t\texpect(isRendered(heading)).toBe(true)\n\t\t\tproven.add('Journey')\n\t\t\tawait portfolio.place('{{application}}-arrival')\n\t\t\tplaced.add('{{application}}-arrival')\n\n\t\t\tfor (const role of ACCESSIBLE_ROLES) expect(page.getByRole(role).elements()).toEqual([])\n\t\t\texpect(readRefusal('Continue')).toBe(\n\t\t\t\t'No interactive element has the accessible name \"Continue\"',\n\t\t\t)\n\t\t\tproven.add('Refusal')\n\n\t\t\tconst measured = new Set<string>()\n\t\t\tfor (const variant of VARIANTS) {\n\t\t\t\tawait page.viewport(variant.width, variant.height)\n\t\t\t\texpect(readStyle(heading, 'display')).toBe('block')\n\t\t\t\texpect(window.innerWidth).toBe(variant.width)\n\t\t\t\tmeasured.add(variant.name)\n\t\t\t}\n\t\t\texpect(measured).toEqual(new Set(VARIANTS.map(({ name }) => name)))\n\t\t\tproven.add('Matrix')\n\n\t\t\tconst files = expandCaptures(states, VARIANTS)\n\t\t\texpect(portfolio.files).toEqual(files)\n\t\t\texpect(new Set(files).size).toBe(states.length * VARIANTS.length)\n\t\t\texpect(placed).toEqual(new Set(states))\n\t\t\tconst expected = CAPTURE\n\t\t\t\t? expandCaptures(\n\t\t\t\t\t\tstates,\n\t\t\t\t\t\tVARIANTS.filter(({ name }) => name === VARIANT),\n\t\t\t\t\t)\n\t\t\t\t: []\n\t\t\texpect(portfolio.paths.map((path) => path.replaceAll('\\\\', '/').split('/').at(-1))).toEqual(\n\t\t\t\texpected,\n\t\t\t)\n\t\t\tif (CAPTURE) proven.add('Capture')\n\t\t\texpect(proven).toEqual(families)\n\t\t} finally {\n{{cleanup}}\t\t\tconst variant = VARIANTS.find(({ name }) => name === VARIANT)\n\t\t\tif (variant !== undefined) await page.viewport(variant.width, variant.height)\n\t\t}\n\t})\n})\n";
121
121
  browser: "import type { JourneyVariant } from '@orkestrel/test'\n\ndeclare module 'vitest' {\n\texport interface ProvidedContext {\n\t\treadonly variant: string\n\t\treadonly variants: readonly JourneyVariant[]\n\t\treadonly capture: boolean\n\t}\n}\n";
122
- sheet: "import sheet from '/dist/src/{{name}}/index.css?raw'\nimport { describe, expect, it } from 'vitest'\nimport { adoptSheet, readLayerNames } from '{{setup}}'\n\ndescribe('{{name}} entry', () => {\n\tit('opens with the declared cascade order', () => {\n\t\tconst adopted = adoptSheet(sheet)\n\t\ttry {\n\t\t\texpect(adopted.sheet.cssRules[0]?.cssText).toBe(\n\t\t\t\t'@layer theme, reset, base, elements, components, utilities;',\n\t\t\t)\n\t\t\texpect(readLayerNames(adopted.sheet).slice(0, 6)).toEqual([\n\t\t\t\t'theme',\n\t\t\t\t'reset',\n\t\t\t\t'base',\n\t\t\t\t'elements',\n\t\t\t\t'components',\n\t\t\t\t'utilities',\n\t\t\t])\n\t\t} finally {\n\t\t\tadopted.release()\n\t\t}\n\t})\n})\n";
122
+ sheet: "import sheet from '{{root}}dist/src/{{name}}/index.css?raw'\nimport { describe, expect, it } from 'vitest'\nimport { adoptSheet, readLayerNames } from '{{setup}}'\n\ndescribe('{{name}} entry', () => {\n\tit('opens with the declared cascade order', () => {\n\t\tconst adopted = adoptSheet(sheet)\n\t\ttry {\n\t\t\texpect(adopted.sheet.cssRules[0]?.cssText).toBe(\n\t\t\t\t'@layer theme, reset, base, elements, components, utilities;',\n\t\t\t)\n\t\t\texpect(readLayerNames(adopted.sheet).slice(0, 6)).toEqual([\n\t\t\t\t'theme',\n\t\t\t\t'reset',\n\t\t\t\t'base',\n\t\t\t\t'elements',\n\t\t\t\t'components',\n\t\t\t\t'utilities',\n\t\t\t])\n\t\t} finally {\n\t\t\tadopted.release()\n\t\t}\n\t})\n})\n";
123
123
  styles: Readonly<{
124
124
  module: "/** Carries a CSSOM rule and its enclosing layer. */\nexport interface SheetEntry {\n\treadonly rule: CSSRule\n\treadonly layer: string | undefined\n}\n\n/** Describes an adopted stylesheet and its idempotent release operation. */\nexport interface SheetAdoption {\n\treadonly sheet: CSSStyleSheet\n\trelease(): void\n}\n\n/**\n * Adopts CSS text into the document with an idempotent release operation.\n * @param text - The compiled sheet text.\n * @returns The adopted sheet and its release operation.\n * @example\n * ```ts\n * const adopted = adoptSheet('.sample { color: red }')\n * adopted.release()\n * ```\n */\nexport function adoptSheet(text: string): SheetAdoption {\n\tconst sheet = new CSSStyleSheet()\n\tsheet.replaceSync(text)\n\tdocument.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]\n\treturn {\n\t\tsheet,\n\t\trelease: () => {\n\t\t\tdocument.adoptedStyleSheets = document.adoptedStyleSheets.filter(\n\t\t\t\t(candidate) => candidate !== sheet,\n\t\t\t)\n\t\t},\n\t}\n}\n\n/**\n * Reads layer statements and blocks in declaration order, including repeated names.\n * @param sheet - The parsed sheet.\n * @returns Qualified layer names, with an empty name for an anonymous layer.\n * @example\n * ```ts\n * readLayerNames(new CSSStyleSheet()) // []\n * ```\n */\nexport function readLayerNames(sheet: CSSStyleSheet): readonly string[] {\n\tconst pending: SheetEntry[] = Array.from(sheet.cssRules, (rule) => ({\n\t\trule,\n\t\tlayer: undefined,\n\t})).reverse()\n\tconst names: string[] = []\n\twhile (pending.length > 0) {\n\t\tconst entry = pending.pop()\n\t\tif (entry === undefined) break\n\t\tconst { rule, layer } = entry\n\t\tconst declared =\n\t\t\trule instanceof CSSLayerStatementRule\n\t\t\t\t? rule.nameList\n\t\t\t\t: rule instanceof CSSLayerBlockRule\n\t\t\t\t\t? [rule.name]\n\t\t\t\t\t: []\n\t\tfor (const name of declared) names.push(layer === undefined ? name : layer + '.' + name)\n\t\tif (!(rule instanceof CSSGroupingRule)) continue\n\t\tconst parent =\n\t\t\trule instanceof CSSLayerBlockRule\n\t\t\t\t? layer === undefined\n\t\t\t\t\t? rule.name\n\t\t\t\t\t: layer + '.' + rule.name\n\t\t\t\t: layer\n\t\tfor (const child of Array.from(rule.cssRules).reverse()) {\n\t\t\tpending.push({ rule: child, layer: parent })\n\t\t}\n\t}\n\treturn names\n}\n";
125
125
  proof: "import type { SheetAdoption } from './setupStyles.js'\nimport { describe, expect, it } from 'vitest'\nimport { adoptSheet, readLayerNames } from './setupStyles.js'\n\ndescribe('CSSOM instruments', () => {\n\tit('adopts and releases only its own sheet, including repeated release', () => {\n\t\tconst previous = [...document.adoptedStyleSheets]\n\t\tconst first: SheetAdoption = adoptSheet('.sample { color: red }')\n\t\ttry {\n\t\t\tconst second = adoptSheet('.other { display: block }')\n\t\t\ttry {\n\t\t\t\texpect(document.adoptedStyleSheets).toEqual([...previous, first.sheet, second.sheet])\n\t\t\t\texpect(first.sheet.cssRules.length).toBe(1)\n\t\t\t\tfirst.release()\n\t\t\t\tfirst.release()\n\t\t\t\texpect(document.adoptedStyleSheets).toEqual([...previous, second.sheet])\n\t\t\t} finally {\n\t\t\t\tsecond.release()\n\t\t\t}\n\t\t} finally {\n\t\t\tfirst.release()\n\t\t}\n\t\texpect(document.adoptedStyleSheets).toEqual(previous)\n\t})\n\n\tit('reads statements, blocks, nested conditions, and anonymous layers in order', () => {\n\t\tconst adopted = adoptSheet(\n\t\t\t'@layer first, second; @layer first { @media all { @layer nested {} } } @layer {}',\n\t\t)\n\t\ttry {\n\t\t\texpect(readLayerNames(adopted.sheet)).toEqual([\n\t\t\t\t'first',\n\t\t\t\t'second',\n\t\t\t\t'first',\n\t\t\t\t'first.nested',\n\t\t\t\t'',\n\t\t\t])\n\t\t} finally {\n\t\t\tadopted.release()\n\t\t}\n\t})\n\n\tit('returns no layer names for an empty sheet or an unlayered rule', () => {\n\t\tconst sheet = new CSSStyleSheet()\n\t\texpect(readLayerNames(sheet)).toEqual([])\n\t\tsheet.replaceSync('.sample { color: red }')\n\t\texpect(readLayerNames(sheet)).toEqual([])\n\t})\n})\n";
@@ -375,6 +375,13 @@ export declare function blueprintToConfigArtifacts(blueprint: Blueprint): readon
375
375
  * the peer's floor states what consumers may supply rather than what this workspace
376
376
  * develops against.
377
377
  *
378
+ * A workspace that renders a surface receives {@link BROWSE_DEV_DEPENDENCIES}: a browser
379
+ * environment on either axis, a sheet, or themes. A framework face counts without a clause of
380
+ * its own, because a face is selected only on an axis that carries a browser environment.
381
+ * A browser setup runtime alone receives nothing, because it renders no surface for `browse`
382
+ * to drive. A workspace {@link BROWSE_UPSTREAM} names receives nothing either, because
383
+ * `@orkestrel/browser` depends on it at runtime and the declaration would close a cycle.
384
+ *
378
385
  * A peer is declared here as well as under `peerDependencies`, because a peer is
379
386
  * not installed by the workspace that declares it and developing against one
380
387
  * requires it present. A runtime dependency is the opposite case and is removed:
@@ -878,6 +885,28 @@ export declare function blueprintToTestArtifacts(blueprint: Blueprint): readonly
878
885
  */
879
886
  export declare function blueprintToWritableScripts(blueprint: Blueprint): readonly ManifestScript[];
880
887
 
888
+ /**
889
+ * Names the development dependency a browser or styles surface adds.
890
+ *
891
+ * @remarks
892
+ * The package ships the `browse` binary, an MCP server that drives a rendered page and records
893
+ * journeys, and `.claude/rules/quality.md` § Instruments names its registration.
894
+ * `blueprintToDevDependencies` names the blueprints that receive it, and it withholds the
895
+ * dependency from the workspaces {@link BROWSE_UPSTREAM} lists.
896
+ */
897
+ export declare const BROWSE_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
898
+
899
+ /**
900
+ * Lists the packages `@orkestrel/browser` depends on at runtime, directly or transitively, frozen.
901
+ *
902
+ * @remarks
903
+ * A workspace this list names cannot declare `@orkestrel/browser`, because the declaration closes
904
+ * a dependency cycle through the browser package's runtime dependencies. The list is the closure
905
+ * of `dependencies` and `peerDependencies` over the installed manifests, sorted by name, and moves
906
+ * with the `@orkestrel/browser` pin.
907
+ */
908
+ export declare const BROWSE_UPSTREAM: readonly string[];
909
+
881
910
  /** Represents a browser framework and its physically occupied axes. */
882
911
  export declare interface BrowserExtension {
883
912
  readonly surface: 'browser';
@@ -3,7 +3,7 @@ import { fillTemplate } from "@orkestrel/template";
3
3
  import { Emitter } from "@orkestrel/emitter";
4
4
  var package_default = {
5
5
  name: "@orkestrel/scaffold",
6
- version: "0.0.82",
6
+ version: "0.0.84",
7
7
  description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
8
8
  keywords: [
9
9
  "audit",
@@ -94,25 +94,26 @@ var package_default = {
94
94
  },
95
95
  dependencies: {
96
96
  "@orkestrel/console": "^0.0.15",
97
- "@orkestrel/contract": "^0.0.18",
97
+ "@orkestrel/contract": "^0.0.19",
98
98
  "@orkestrel/emitter": "^0.0.11",
99
- "@orkestrel/markdown": "^0.0.16",
99
+ "@orkestrel/markdown": "^0.0.17",
100
100
  "@orkestrel/process": "^0.0.14",
101
101
  "@orkestrel/template": "^0.0.9"
102
102
  },
103
103
  devDependencies: {
104
104
  "@microsoft/api-extractor": "^7.59.3",
105
- "@orkestrel/guide": "^0.0.21",
106
- "@orkestrel/html": "^0.0.11",
105
+ "@orkestrel/browser": "^0.0.20",
106
+ "@orkestrel/guide": "^0.0.22",
107
+ "@orkestrel/html": "^0.0.12",
107
108
  "@orkestrel/probe": "^0.0.19",
108
109
  "@orkestrel/test": "^0.0.24",
109
- "@types/node": "^26.6.3",
110
+ "@types/node": "^26.6.4",
110
111
  "@vitest/browser-playwright": "^4.1.11",
111
112
  "oxfmt": "^0.71.0",
112
113
  "oxlint": "^1.86.0",
113
114
  "playwright": "^1.63.0",
114
115
  "typescript": "^6.0.3",
115
- "vite": "^8.3.1",
116
+ "vite": "^8.3.2",
116
117
  "vitest": "^4.1.11"
117
118
  },
118
119
  engines: { "node": ">=22.18.0" }
@@ -661,6 +662,41 @@ var SOURCE_BROWSER_DEV_DEPENDENCIES = Object.freeze({
661
662
  "@vitest/browser-playwright": package_default.devDependencies["@vitest/browser-playwright"],
662
663
  playwright: package_default.devDependencies.playwright
663
664
  });
665
+ /**
666
+ * Names the development dependency a browser or styles surface adds.
667
+ *
668
+ * @remarks
669
+ * The package ships the `browse` binary, an MCP server that drives a rendered page and records
670
+ * journeys, and `.claude/rules/quality.md` § Instruments names its registration.
671
+ * `blueprintToDevDependencies` names the blueprints that receive it, and it withholds the
672
+ * dependency from the workspaces {@link BROWSE_UPSTREAM} lists.
673
+ */
674
+ var BROWSE_DEV_DEPENDENCIES = Object.freeze({ "@orkestrel/browser": package_default.devDependencies["@orkestrel/browser"] });
675
+ /**
676
+ * Lists the packages `@orkestrel/browser` depends on at runtime, directly or transitively, frozen.
677
+ *
678
+ * @remarks
679
+ * A workspace this list names cannot declare `@orkestrel/browser`, because the declaration closes
680
+ * a dependency cycle through the browser package's runtime dependencies. The list is the closure
681
+ * of `dependencies` and `peerDependencies` over the installed manifests, sorted by name, and moves
682
+ * with the `@orkestrel/browser` pin.
683
+ */
684
+ var BROWSE_UPSTREAM = Object.freeze([
685
+ "@orkestrel/abort",
686
+ "@orkestrel/codec",
687
+ "@orkestrel/contract",
688
+ "@orkestrel/emitter",
689
+ "@orkestrel/html",
690
+ "@orkestrel/markdown",
691
+ "@orkestrel/mcp",
692
+ "@orkestrel/process",
693
+ "@orkestrel/router",
694
+ "@orkestrel/server",
695
+ "@orkestrel/sse",
696
+ "@orkestrel/timeout",
697
+ "@orkestrel/tool",
698
+ "@orkestrel/websocket"
699
+ ]);
664
700
  /** Names the development dependency every private `app` environment adds. */
665
701
  var APP_DEV_DEPENDENCIES = Object.freeze({ "@orkestrel/contract": package_default.dependencies["@orkestrel/contract"] });
666
702
  /** Lists the development dependencies a private browser application adds. */
@@ -2301,7 +2337,7 @@ declare module 'vitest' {
2301
2337
  }
2302
2338
  }
2303
2339
  `,
2304
- sheet: `import sheet from '/dist/src/{{name}}/index.css?raw'
2340
+ sheet: `import sheet from '{{root}}dist/src/{{name}}/index.css?raw'
2305
2341
  import { describe, expect, it } from 'vitest'
2306
2342
  import { adoptSheet, readLayerNames } from '{{setup}}'
2307
2343
 
@@ -5634,6 +5670,13 @@ function srcToExports(src) {
5634
5670
  * the peer's floor states what consumers may supply rather than what this workspace
5635
5671
  * develops against.
5636
5672
  *
5673
+ * A workspace that renders a surface receives {@link BROWSE_DEV_DEPENDENCIES}: a browser
5674
+ * environment on either axis, a sheet, or themes. A framework face counts without a clause of
5675
+ * its own, because a face is selected only on an axis that carries a browser environment.
5676
+ * A browser setup runtime alone receives nothing, because it renders no surface for `browse`
5677
+ * to drive. A workspace {@link BROWSE_UPSTREAM} names receives nothing either, because
5678
+ * `@orkestrel/browser` depends on it at runtime and the declaration would close a cycle.
5679
+ *
5637
5680
  * A peer is declared here as well as under `peerDependencies`, because a peer is
5638
5681
  * not installed by the workspace that declares it and developing against one
5639
5682
  * requires it present. A runtime dependency is the opposite case and is removed:
@@ -5656,8 +5699,11 @@ function srcToExports(src) {
5656
5699
  */
5657
5700
  function blueprintToDevDependencies(blueprint) {
5658
5701
  const sheets = blueprintToSheets(blueprint).length > 0 || blueprint.themes;
5702
+ const rendered = blueprint.src.includes("browser") || blueprint.app.includes("browser") || sheets;
5703
+ const own = blueprint.src.length > 0 || sheets ? `@orkestrel/${blueprint.name}` : blueprint.name;
5659
5704
  const merged = {
5660
5705
  ...BASE_DEV_DEPENDENCIES,
5706
+ ...rendered && !BROWSE_UPSTREAM.includes(own) ? BROWSE_DEV_DEPENDENCIES : {},
5661
5707
  ...sheets ? STYLES_DEV_DEPENDENCIES : {},
5662
5708
  ...blueprint.src.length > 0 || blueprint.bin ? DECLARATION_DEV_DEPENDENCIES : {},
5663
5709
  ...blueprint.src.includes("browser") || blueprint.setup.includes("browser") || sheets ? SOURCE_BROWSER_DEV_DEPENDENCIES : {},
@@ -5669,7 +5715,6 @@ function blueprintToDevDependencies(blueprint) {
5669
5715
  for (const framework of blueprintToMachinery(blueprint).frameworks) Object.assign(merged, FRAMEWORK_MATRIX[framework].dependencies);
5670
5716
  for (const extra of blueprint.extras) merged[extra.name] = extra.range;
5671
5717
  for (const peer of blueprint.peers) if (!Object.hasOwn(merged, peer.name)) merged[peer.name] = peer.range;
5672
- const own = blueprint.src.length > 0 || sheets ? `@orkestrel/${blueprint.name}` : blueprint.name;
5673
5718
  const runtime = new Set(blueprint.dependencies.map((dependency) => dependency.name));
5674
5719
  return Object.fromEntries(Object.entries(merged).filter(([name]) => name !== own && !runtime.has(name)).sort(([left], [right]) => compareValues(left, right)));
5675
5720
  }
@@ -6718,16 +6763,20 @@ function blueprintToTestArtifacts(blueprint) {
6718
6763
  origin: "template",
6719
6764
  content: ARTIFACT_TEMPLATES.tests.styles.proof
6720
6765
  });
6721
- for (const name of sheets) artifacts.push({
6722
- path: `tests/src/${name}/index.test.ts`,
6723
- group: "tests",
6724
- ownership: "birth",
6725
- origin: "template",
6726
- content: fillTemplate(ARTIFACT_TEMPLATES.tests.sheet, {
6727
- name,
6728
- setup: name === "styles/themes" ? "../../../setupStyles.js" : "../../setupStyles.js"
6729
- })
6730
- });
6766
+ for (const name of sheets) {
6767
+ const tests = "../".repeat(name.split("/").length + 1);
6768
+ artifacts.push({
6769
+ path: `tests/src/${name}/index.test.ts`,
6770
+ group: "tests",
6771
+ ownership: "birth",
6772
+ origin: "template",
6773
+ content: fillTemplate(ARTIFACT_TEMPLATES.tests.sheet, {
6774
+ name,
6775
+ root: `${tests}../`,
6776
+ setup: `${tests}setupStyles.js`
6777
+ })
6778
+ });
6779
+ }
6731
6780
  if (blueprintToMachinery(blueprint).browser) artifacts.push({
6732
6781
  path: "tests/setupBrowser.ts",
6733
6782
  group: "tests",
@@ -8397,6 +8446,6 @@ var Compiler = class {
8397
8446
  }
8398
8447
  };
8399
8448
  //#endregion
8400
- export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, AXES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, CANON_PATHS, CATALOG_AGENT_PATH, CATALOG_CLOSING_MARKER, CATALOG_OPENING_MARKER, CONFIG_TEMPLATES, CONFORMANCE_TEST_PATH, CONTROL_CHARACTER_PATTERN, Compiler, DECLARATION_DEV_DEPENDENCIES, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, DISTRIBUTION_TEST_PATH, ENGINES_PATTERN, ENVIRONMENTS, EXECUTABLE_PATHS, EXTRA_RANGE_PATTERN, FLOOR_RANGE_PATTERN, FOREIGN_NAME_PATTERN, FRAMEWORKS, FRAMEWORK_MATRIX, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_INVENTORY_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, JOURNEY_CONFIG_PATH, MANIFEST_PATH, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_AUDIT_FINDINGS, MAX_COLLECTION_ITEMS, MAX_DEPENDENCY_NAME_LENGTH, MAX_MANIFEST_BYTES, MAX_NAME_LENGTH, MAX_PATH_LENGTH, MAX_RANGE_LENGTH, MAX_REGISTRY_BYTES, MAX_SCRIPT_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, MAX_TOTAL_REGISTRY_BYTES, MINIMUM_NODE_VERSION, MINIMUM_NPM_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, PRINT_WIDTH, REFERENCE_PATHS, RELEASE_PROOF_COMMAND, RESERVED_SHEET_NAMES, SEED_GUIDE_PATHS, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHEET_ENTRY_NAME, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SHOWCASE_PAGES_PATH, SKILLS_CONFIG_PATH, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, STYLES_DEV_DEPENDENCIES, STYLES_ENTRY_PATH, SURFACES, ScaffoldError, TAB_WIDTH, TARGET_SKILL_NAMES, THEMES_BARREL_PATH, THEMES_ENTRY_PATH, VERSION_PATTERN, WORKSPACE_DEV_ENGINES, WORKSPACE_OWNED_PATHS, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToExports, blueprintToFaces, blueprintToGuideArtifacts, blueprintToHostArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToOrchestrationArtifacts, blueprintToProjects, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSheets, blueprintToSourceArtifacts, blueprintToTestArtifacts, blueprintToWritableScripts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, contentToHex, createBlueprint, dependenciesToQuestions, extractRangeMajor, extractVersion, inferDrift, inferGroup, isArtifact, isAudit, isBlueprint, isBrowserExtension, isCanonPath, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDeferredPath, isDependency, isDependencyName, isEnvironment, isExtension, isFinding, isFloorPath, isGroup, isGroups, isHex, isManifestScript, isMirror, isOverride, isPath, isPlan, isQuestion, isRetainedPath, isScaffoldError, isSheetName, isSnapshot, isStylesExtension, isSurface, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesPrintWidth, matchesRange, nameToGuide, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseExtension, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, renderSkillPointer, replaceManifestRanges, replaceManifestScripts, replacePlanRanges, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
8449
+ export { APP_BROWSER_DEV_DEPENDENCIES, APP_DEV_DEPENDENCIES, APP_MATRIX, APP_SERVER_DEV_DEPENDENCIES, ARTIFACT_TEMPLATES, AXES, BASE_DEV_DEPENDENCIES, BIN_CONFIGS, BIN_ENTRY_PATH, BROWSE_DEV_DEPENDENCIES, BROWSE_UPSTREAM, CANON_PATHS, CATALOG_AGENT_PATH, CATALOG_CLOSING_MARKER, CATALOG_OPENING_MARKER, CONFIG_TEMPLATES, CONFORMANCE_TEST_PATH, CONTROL_CHARACTER_PATTERN, Compiler, DECLARATION_DEV_DEPENDENCIES, DEFAULT_ENGINES, DEFAULT_VERSION, DEPENDENCY_NAME_PATTERN, DISTRIBUTION_TEST_PATH, ENGINES_PATTERN, ENVIRONMENTS, EXECUTABLE_PATHS, EXTRA_RANGE_PATTERN, FLOOR_RANGE_PATTERN, FOREIGN_NAME_PATTERN, FRAMEWORKS, FRAMEWORK_MATRIX, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HEX_PATTERN, HOST_INVENTORY_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, INVALID_PATH_CHARACTER_PATTERN, JOURNEY_CONFIG_PATH, MANIFEST_PATH, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_HEX_LENGTH, MAX_AUDIT_FINDINGS, MAX_COLLECTION_ITEMS, MAX_DEPENDENCY_NAME_LENGTH, MAX_MANIFEST_BYTES, MAX_NAME_LENGTH, MAX_PATH_LENGTH, MAX_RANGE_LENGTH, MAX_REGISTRY_BYTES, MAX_SCRIPT_LENGTH, MAX_TOTAL_ARTIFACT_BYTES, MAX_TOTAL_REGISTRY_BYTES, MINIMUM_NODE_VERSION, MINIMUM_NPM_VERSION, NAME_PATTERN, ORCHESTRATION_PATH_NAMES, ORCHESTRATION_PATH_PREFIXES, ORKESTREL_RANGE_PATTERN, PRINT_WIDTH, REFERENCE_PATHS, RELEASE_PROOF_COMMAND, RESERVED_SHEET_NAMES, SEED_GUIDE_PATHS, SERVICE_SCRIPT_PATH, SERVICE_SETUP_PATH, SERVICE_TEST_INCLUDE, SHEET_ENTRY_NAME, SHOWCASE_CONFIG_PATH, SHOWCASE_DEV_DEPENDENCIES, SHOWCASE_PAGES_PATH, SKILLS_CONFIG_PATH, SOURCE_BROWSER_DEV_DEPENDENCIES, SRC_MATRIX, STYLES_DEV_DEPENDENCIES, STYLES_ENTRY_PATH, SURFACES, ScaffoldError, TAB_WIDTH, TARGET_SKILL_NAMES, THEMES_BARREL_PATH, THEMES_ENTRY_PATH, VERSION_PATTERN, WORKSPACE_DEV_ENGINES, WORKSPACE_OWNED_PATHS, applyOverrides, artifactToFinding, artifactToHex, artifactsToQuestions, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToDocumentArtifacts, blueprintToExports, blueprintToFaces, blueprintToGuideArtifacts, blueprintToHostArtifacts, blueprintToMachinery, blueprintToManifest, blueprintToOrchestrationArtifacts, blueprintToProjects, blueprintToQuestions, blueprintToRootTsconfig, blueprintToRootVite, blueprintToScripts, blueprintToSheets, blueprintToSourceArtifacts, blueprintToTestArtifacts, blueprintToWritableScripts, bytesToHex, catalogToLayers, cloneValue, compareVersions, computeBytes, computeHash, contentToHex, createBlueprint, dependenciesToQuestions, extractRangeMajor, extractVersion, inferDrift, inferGroup, isArtifact, isAudit, isBlueprint, isBrowserExtension, isCanonPath, isCatalogEntry, isCollection, isCompilerHooks, isCompilerOptions, isContent, isDeferredPath, isDependency, isDependencyName, isEnvironment, isExtension, isFinding, isFloorPath, isGroup, isGroups, isHex, isManifestScript, isMirror, isOverride, isPath, isPlan, isQuestion, isRetainedPath, isScaffoldError, isSheetName, isSnapshot, isStylesExtension, isSurface, manifestToDependencies, manifestToName, matchesDriftReachability, matchesEngines, matchesOrchestrationPath, matchesPrintWidth, matchesRange, nameToGuide, overridesToQuestions, parseBlueprint, parseCompilerOptions, parseExtension, parseGroups, parseSnapshot, pathToCondition, planToFindings, planToHash, planToSummary, renderSkillPointer, replaceManifestRanges, replaceManifestScripts, replacePlanRanges, selectGroups, selectHostPaths, serializeTypeScriptString, srcToEntry, srcToExports, srcToRoot };
8401
8450
 
8402
8451
  //# sourceMappingURL=index.js.map