@estiva-app/ui 0.35.2 → 0.36.0

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.
Files changed (123) hide show
  1. package/dist/eslint/escape.d.ts +23 -6
  2. package/dist/eslint/escape.d.ts.map +1 -1
  3. package/dist/eslint/index.js +70 -9
  4. package/dist/eslint/index.js.map +2 -2
  5. package/dist/eslint/no-raw-element.d.ts +7 -0
  6. package/dist/eslint/no-raw-element.d.ts.map +1 -1
  7. package/dist/eslint/no-restyled-part.d.ts +8 -0
  8. package/dist/eslint/no-restyled-part.d.ts.map +1 -1
  9. package/dist/gates/app-checks.d.ts.map +1 -1
  10. package/dist/gates/{chunk-DRIHHXQK.js → chunk-CC6L4B5U.js} +23 -17
  11. package/dist/gates/chunk-CC6L4B5U.js.map +7 -0
  12. package/dist/gates/chunk-SYVKGOWM.js +1131 -0
  13. package/dist/gates/chunk-SYVKGOWM.js.map +7 -0
  14. package/dist/gates/ci.d.ts +18 -0
  15. package/dist/gates/ci.d.ts.map +1 -0
  16. package/dist/gates/cli.js +20 -3
  17. package/dist/gates/cli.js.map +2 -2
  18. package/dist/gates/create-app.d.ts.map +1 -1
  19. package/dist/gates/create-app.js +1 -1
  20. package/dist/gates/escapes.d.ts +23 -0
  21. package/dist/gates/escapes.d.ts.map +1 -0
  22. package/dist/gates/gate-config.d.ts.map +1 -1
  23. package/dist/gates/hook.d.ts +2 -0
  24. package/dist/gates/hook.d.ts.map +1 -1
  25. package/dist/gates/index.d.ts +5 -1
  26. package/dist/gates/index.d.ts.map +1 -1
  27. package/dist/gates/index.js +37 -183
  28. package/dist/gates/index.js.map +4 -4
  29. package/dist/gates/name-escapes.d.ts +14 -0
  30. package/dist/gates/name-escapes.d.ts.map +1 -0
  31. package/dist/gates/status.d.ts +3 -1
  32. package/dist/gates/status.d.ts.map +1 -1
  33. package/dist/gates/token-lint.d.ts.map +1 -1
  34. package/dist/gates/token-style.d.ts +21 -0
  35. package/dist/gates/token-style.d.ts.map +1 -0
  36. package/dist/index.js +13 -13
  37. package/dist/index.js.map +1 -1
  38. package/dist/registry/{app-3ALZE6XO.js → app-YMABQII6.js} +3 -3
  39. package/dist/registry/app.d.ts.map +1 -1
  40. package/dist/registry/behaviours.d.ts +71 -0
  41. package/dist/registry/behaviours.d.ts.map +1 -0
  42. package/dist/registry/{build-NDXMVFQ5.js → build-3EANARLQ.js} +2 -2
  43. package/dist/registry/build.d.ts.map +1 -1
  44. package/dist/registry/{chunk-3WJ7ZZ7Z.js → chunk-EVJW26UR.js} +51 -4
  45. package/dist/registry/chunk-EVJW26UR.js.map +7 -0
  46. package/dist/registry/{chunk-XVII7P6G.js → chunk-SIB44CES.js} +276 -90
  47. package/dist/registry/chunk-SIB44CES.js.map +7 -0
  48. package/dist/registry/{chunk-5K2TRR4X.js → chunk-XK747STJ.js} +18 -3
  49. package/dist/registry/chunk-XK747STJ.js.map +7 -0
  50. package/dist/registry/cli.js +7 -6
  51. package/dist/registry/cli.js.map +2 -2
  52. package/dist/registry/contract.d.ts +13 -1
  53. package/dist/registry/contract.d.ts.map +1 -1
  54. package/dist/registry/index.d.ts +1 -1
  55. package/dist/registry/index.d.ts.map +1 -1
  56. package/dist/registry/index.js +11 -5
  57. package/package.json +4 -1
  58. package/registry.json +293 -17
  59. package/skill/estiva-ui.md +2 -1
  60. package/src/AppShell.mdx +2 -1
  61. package/src/AttachmentCard.tsx +7 -7
  62. package/src/Button.tsx +1 -1
  63. package/src/Card.tsx +2 -2
  64. package/src/ChipInput.mdx +2 -0
  65. package/src/ChipInput.tsx +1 -1
  66. package/src/CommandPalette.tsx +3 -3
  67. package/src/DialogShell.tsx +1 -1
  68. package/src/EmptyState.mdx +4 -3
  69. package/src/Lightbox.tsx +1 -1
  70. package/src/NavItem.tsx +1 -1
  71. package/src/PersonTrigger.tsx +1 -1
  72. package/src/Reaction.tsx +1 -1
  73. package/src/ScrollArea.mdx +4 -0
  74. package/src/Select.tsx +1 -1
  75. package/src/TopBar.mdx +1 -1
  76. package/src/eslint/escape.ts +43 -14
  77. package/src/eslint/has-a-page-and-a-story.test.ts +1 -1
  78. package/src/eslint/index.test.ts +2 -2
  79. package/src/eslint/no-copied-look.test.ts +1 -1
  80. package/src/eslint/no-hand-rolled-behaviour.test.ts +3 -3
  81. package/src/eslint/no-handmade-empty-state.test.ts +2 -2
  82. package/src/eslint/no-handmade-header.test.ts +2 -2
  83. package/src/eslint/no-native-title.test.ts +2 -2
  84. package/src/eslint/no-raw-element.test.ts +47 -15
  85. package/src/eslint/no-raw-element.ts +53 -3
  86. package/src/eslint/no-rebuilt-behaviour.test.ts +9 -9
  87. package/src/eslint/no-restyled-part.test.ts +21 -2
  88. package/src/eslint/no-restyled-part.ts +30 -0
  89. package/src/eslint/raw-element-outside-a-wrapper.test.ts +3 -3
  90. package/src/gates/app-checks.ts +4 -3
  91. package/src/gates/ci.ts +152 -0
  92. package/src/gates/cli.ts +19 -3
  93. package/src/gates/count.ts +1 -1
  94. package/src/gates/create-app.test.ts +2 -2
  95. package/src/gates/create-app.ts +22 -16
  96. package/src/gates/escapes.ts +111 -0
  97. package/src/gates/gate-config.ts +2 -0
  98. package/src/gates/gates.test.ts +86 -1
  99. package/src/gates/hook.ts +38 -4
  100. package/src/gates/index.ts +5 -1
  101. package/src/gates/name-escapes.ts +77 -0
  102. package/src/gates/status.ts +45 -2
  103. package/src/gates/token-lint.ts +11 -4
  104. package/src/gates/token-style.ts +79 -0
  105. package/src/how.test.ts +282 -0
  106. package/src/pages.test.ts +6 -0
  107. package/src/registry/app.test.ts +34 -0
  108. package/src/registry/app.ts +52 -2
  109. package/src/registry/behaviours.ts +223 -0
  110. package/src/registry/build.ts +36 -0
  111. package/src/registry/cli.ts +5 -3
  112. package/src/registry/contract.test.ts +27 -2
  113. package/src/registry/contract.ts +28 -3
  114. package/src/registry/index.ts +1 -1
  115. package/src/registry/registry.test.ts +14 -0
  116. package/dist/gates/chunk-DBMUQAR6.js +0 -607
  117. package/dist/gates/chunk-DBMUQAR6.js.map +0 -7
  118. package/dist/gates/chunk-DRIHHXQK.js.map +0 -7
  119. package/dist/registry/chunk-3WJ7ZZ7Z.js.map +0 -7
  120. package/dist/registry/chunk-5K2TRR4X.js.map +0 -7
  121. package/dist/registry/chunk-XVII7P6G.js.map +0 -7
  122. /package/dist/registry/{app-3ALZE6XO.js.map → app-YMABQII6.js.map} +0 -0
  123. /package/dist/registry/{build-NDXMVFQ5.js.map → build-3EANARLQ.js.map} +0 -0
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/gates/count.ts", "../../src/gates/hook.ts", "../../src/gates/status.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * Write `.gates-count.json`: what the gate finds, per rule \u2014 errors, warnings\n * and escapes (seam S3 in docs/GATES.md \u00A716). Written once (\u00A723): until UIG-10\n * Peek, Ship and this package each carried the same script, differing only in\n * the repo's name and which rules it seeded.\n *\n * Runs after the gate lint (npm's `postlint:rules`), so CI runs it too. The\n * file is committed: nothing compares it yet, and the later ratchet needs the\n * history to start at the first commit. It lints with the gate's own config,\n * with escapes reported, so an escape is counted rather than silent. The file\n * is rewritten only when a count changes, so running the lint does not leave a\n * changed file behind every time.\n *\n * It fails when an `eslint-disable` switched a gate rule off: that is not an\n * escape, and the escape count cannot see it.\n */\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, relative } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\nimport { APP_RULE_IDS, countGates, PACKAGE_RULE_IDS, type GateCount } from '../eslint/index'\n\nexport interface CountOptions {\n /** The folder the gate lints from, where `.gates-count.json` is written. */\n root?: string\n /** The name written into the file: `peek`, `ship`, `estiva-ui`, or the app's. */\n repo: string\n /** `app` seeds the app rules; `package` this package's inward set (UIG-5). */\n audience?: 'app' | 'package'\n /** The gate's config file, relative to `root`. */\n config?: string\n}\n\nexport interface CountResult extends GateCount {\n changed: boolean\n /** What to print, line by line. */\n lines: string[]\n /** What to print to stderr when a rule was switched off; empty when none was. */\n failures: string[]\n}\n\n/** ESLint from where the gate lints, never from beside this file: an app's own install. */\nasync function eslintFrom(root: string): Promise<typeof ESLintClass> {\n const resolved = createRequire(join(root, 'package.json')).resolve('eslint')\n return ((await import(pathToFileURL(resolved).href)) as { ESLint: typeof ESLintClass }).ESLint\n}\n\nexport async function writeGateCount({ root = process.cwd(), repo, audience = 'app', config = 'eslint.gates.config.js' }: CountOptions): Promise<CountResult> {\n const out = join(root, '.gates-count.json')\n const ESLint = await eslintFrom(root)\n const eslint = new ESLint({\n cwd: root,\n overrideConfigFile: join(root, config),\n overrideConfig: { settings: { estiva: { reportEscapes: true } } },\n })\n // The set the gate runs, so a rule with nothing to report still has its row.\n const { rules, disabled } = countGates(await eslint.lintFiles(['.']), audience === 'package' ? PACKAGE_RULE_IDS : APP_RULE_IDS)\n\n const previous = existsSync(out) ? (JSON.parse(readFileSync(out, 'utf8')) as { rules?: unknown }) : null\n const changed = JSON.stringify(previous?.rules) !== JSON.stringify(rules)\n if (changed) {\n const count = { schemaVersion: 1, repo, generatedAt: new Date().toISOString(), rules }\n writeFileSync(out, `${JSON.stringify(count, null, 2)}\\n`)\n }\n const lines = [changed ? '.gates-count.json: written' : '.gates-count.json: unchanged']\n for (const [id, c] of Object.entries(rules)) lines.push(` ${id}: ${c.errors} errors, ${c.warnings} warnings, ${c.escapes} escapes`)\n\n const failures: string[] = []\n if (disabled.length > 0) {\n failures.push('', 'An eslint-disable switched a gate rule off. Write the reason above the element instead: // @estiva-escape: <reason>')\n for (const d of disabled) failures.push(` ${relative(root, d.filePath)}:${d.line} ${d.ruleId}`)\n }\n return { rules, disabled, changed, lines, failures }\n}\n", "/// <reference types=\"node\" />\n/**\n * The editor gate: before Claude writes a `.ts` or `.tsx` under `src/`, lint the\n * text it is about to write with the gate, and refuse the write on an error.\n * Written once (docs/GATES.md \u00A723): until UIG-10 Peek, Ship and this package\n * each carried their own copy of this script.\n *\n * A `PreToolUse` hook in the repository's committed `.claude/settings.json`\n * runs it. Claude Code documents exit 2 as blocking the tool call, with stderr\n * handed back to Claude; exit 0 raises no objection. It reads\n * `$CLAUDE_PROJECT_DIR`, the project root where the session started \u2014 a session\n * started in a folder above or below the repository gets no hook (UIG-3,\n * UIG-4 findings).\n *\n * It runs the gate's own config and reimplements nothing: a hook and a lint\n * that could disagree would be worse than neither. Tests are not checked\n * (Katerina, 13 September); stories are. This package's inward gate reads `.tsx`\n * only, as its lint does.\n *\n * Input, from Claude Code on stdin: `tool_name` and `tool_input` \u2014 `file_path`\n * and `content` for Write; `file_path`, `old_string`, `new_string` and\n * `replace_all` for Edit. An Edit carries no whole file, so the edit is applied\n * to the file on disk here. Anything it cannot read passes, and the tool itself\n * reports its own failure.\n *\n * **Search first** (UIG-20). A Write that would create a new file drawing a new\n * part, and that the gate lets through, is stopped once: the hook runs\n * `estiva-ui find` on the part's name and hands the matches back, so a session\n * sees what already exists before it adds another. The same file written again\n * in the same session goes through. The skill asks for the search; this makes\n * sure it happened. A search that cannot run stops nothing.\n */\nimport { spawnSync } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport { basename, isAbsolute, join, relative, resolve, sep } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\nimport { countGates } from '../eslint/index'\nimport { repositoryOf } from '../registry/skill'\n\nexport interface HookOptions {\n /** The repository's top folder. Defaults to `$CLAUDE_PROJECT_DIR`, then the tool call's `cwd`. */\n root?: string\n /** The folder that holds the app, relative to `root`: `.` unless it sits in a folder of its own. */\n app?: string\n /** `package` gates `.tsx` only, as this package's inward lint does. */\n audience?: 'app' | 'package'\n /** The hook's input, when not read from stdin (tests). */\n input?: unknown\n /** Where a session's searches are remembered. Defaults to a folder in the system's temp. */\n seen?: string\n}\n\nexport interface HookResult {\n /** 0 lets the write through; 2 refuses it. */\n code: 0 | 2\n /** What goes to stderr when refused. */\n message: string\n}\n\ninterface ToolCall {\n tool_name?: string\n session_id?: string\n cwd?: string\n tool_input?: { file_path?: unknown; content?: unknown; old_string?: unknown; new_string?: unknown; replace_all?: unknown }\n}\n\nconst PASS: HookResult = { code: 0, message: '' }\n/** An app's source the gate reads: every script type, with or without JSX. */\nconst APP_SOURCE = /^src\\/.+\\.[cm]?[jt]sx?$/\n\nexport async function runHook({ root, app = '.', audience = 'app', input, seen }: HookOptions = {}): Promise<HookResult> {\n let call: ToolCall\n try {\n call = (input ?? JSON.parse(readFileSync(0, 'utf8'))) as ToolCall\n } catch {\n return PASS\n }\n const args = call.tool_input ?? {}\n if (typeof args.file_path !== 'string') return PASS\n\n const top = root ?? process.env.CLAUDE_PROJECT_DIR ?? call.cwd ?? process.cwd()\n const appDir = resolve(top, app)\n const file = isAbsolute(args.file_path) ? args.file_path : resolve(call.cwd ?? top, args.file_path)\n const rel = relative(appDir, file).split(sep).join('/')\n // Every script under an app's src/, as the gate reads it (a raw <button> in a .jsx passed\n // before, the re-review after the audit before UIG-26); the package's own gate is .tsx only.\n const source = audience === 'package' ? /^src\\/.+\\.tsx$/ : APP_SOURCE\n if (rel.startsWith('../') || isAbsolute(rel) || !source.test(rel) || /\\.test\\.[cm]?[jt]sx?$/.test(rel) || /\\.d\\.[cm]?ts$/.test(rel)) return PASS\n\n const text = proposed(call.tool_name, args, file)\n if (text === null) return PASS\n\n // ESLint from the app's own install: in Ship the top folder has none.\n const resolved = createRequire(join(appDir, 'package.json')).resolve('eslint')\n const { ESLint } = (await import(pathToFileURL(resolved).href)) as { ESLint: typeof ESLintClass }\n const config = join(appDir, 'eslint.gates.config.js')\n const eslint = new ESLint({ cwd: appDir, overrideConfigFile: config })\n const results = await eslint.lintText(text, { filePath: file })\n // A rule's errors only. Code that does not parse yet (one edit of several) is\n // the typecheck's to judge; the gate looks again at the edit that completes it.\n const errors = results.flatMap((r) => r.messages.filter((m) => m.severity === 2 && m.ruleId))\n const { disabled } = countGates(results)\n if (errors.length === 0 && disabled.length === 0) return searchFirst(call, file, text, appDir, top, seen) ?? PASS\n\n const shown = (p: string) => relative(top, p).split(sep).join('/')\n const lines = [`${shown(file)} was not written: the UI Guardrails refuse it (${shown(config)}).`]\n for (const m of errors) lines.push(` ${m.line}:${m.column} ${m.message}${m.ruleId ? ` (${m.ruleId})` : ''}`)\n const keep = audience === 'package' ? 'Keep it only' : 'Keep the element only'\n for (const d of disabled) lines.push(` ${d.line} an eslint-disable switches ${d.ruleId} off. ${keep} with its reason on the line above: // @estiva-escape: <reason>`)\n return { code: 2, message: `${lines.join('\\n')}\\n` }\n}\n\n/** The part a Write would add in a new file, or null: a capitalised export, in a file that draws. */\nexport function newPart(tool: string | undefined, file: string, text: string): string | null {\n if (tool !== 'Write' || existsSync(file) || !file.endsWith('.tsx') || /\\.stories\\.tsx$/.test(file)) return null\n if (!/<\\/[A-Za-z]|\\/>/.test(text)) return null\n const named = /export\\s+(?:default\\s+)?(?:async\\s+)?(?:function|const|class)\\s+([A-Z][A-Za-z0-9]*)/.exec(text)?.[1]\n if (named) return named\n return /export\\s+default\\s+function\\s*\\(/.test(text) ? basename(file, '.tsx') : null\n}\n\n/** `PanelHeader` \u2192 `panel header`: the words the search is asked. */\nexport const wordsOf = (name: string) => name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z][a-z])/g, '$1 $2').toLowerCase()\n\n/** The catalogue command: beside this file once built, or the built one from source (tests). */\nfunction findCommand(): string | null {\n for (const at of ['../registry/cli.js', '../../dist/registry/cli.js']) {\n const path = fileURLToPath(new URL(at, import.meta.url))\n if (existsSync(path)) return path\n }\n return null\n}\n\n/** Stop a new part's first Write with what already exists; null lets it through. */\nfunction searchFirst(call: ToolCall, file: string, text: string, appDir: string, top: string, seen?: string): HookResult | null {\n const part = newPart(call.tool_name, file, text)\n const command = findCommand()\n if (!part || !command) return null\n const folder = seen ?? join(tmpdir(), 'estiva-ui-search-first')\n const mark = join(folder, createHash('sha1').update(`${call.session_id ?? ''}\\n${resolve(file)}`).digest('hex'))\n if (existsSync(mark)) return null\n const words = wordsOf(part)\n const repo = basename(repositoryOf(appDir))\n const run = spawnSync(process.execPath, [command, 'find', ...words.split(' '), '--root', appDir, '--repo', repo, '--limit', '5'], { encoding: 'utf8', timeout: 60_000 })\n if (run.status !== 0 || !run.stdout.trim()) return null\n try {\n mkdirSync(folder, { recursive: true })\n writeFileSync(mark, '')\n } catch {\n return null\n }\n const shown = relative(top, file).split(sep).join('/')\n return {\n code: 2,\n message:\n `${shown} was not written yet: it adds a new part, ${part}. Search before building (UIG-20). What already exists for \"${words}\":\\n\\n` +\n `${run.stdout.trim()}\\n\\n` +\n `If one of these does the job, use it, and read its page first. If the words were wrong, run \\`npm run ui:find <other words>\\`. ` +\n `If nothing fits, tell the person, then write the file again: it goes through.\\n`,\n }\n}\n\n/** The file as it will be after this tool call, or null when that cannot be known. */\nfunction proposed(tool: string | undefined, args: NonNullable<ToolCall['tool_input']>, file: string): string | null {\n if (tool === 'Write') return typeof args.content === 'string' ? args.content : null\n if (tool !== 'Edit' || typeof args.old_string !== 'string' || typeof args.new_string !== 'string') return null\n const { old_string: from, new_string: to } = args\n const current = existsSync(file) ? readFileSync(file, 'utf8') : ''\n // A checkout on Windows has CRLF on disk; an edit is written with LF.\n for (const text of [current, current.replace(/\\r\\n/g, '\\n')]) {\n if (!text.includes(from)) continue\n return args.replace_all === true ? text.split(from).join(to) : text.replace(from, () => to)\n }\n return null\n}\n", "/// <reference types=\"node\" />\n/**\n * gates:status \u2014 where the UI Guardrails project stands, read from the code.\n *\n * estiva-gates status one row per ticket\n * estiva-gates status --detail every check under every row\n * estiva-gates status --json machine output, read by estiva-ui's run\n * estiva-gates status --app web the app sits in `web/` (Ship's did, until PER-19)\n *\n * Every row is decided by checks on real files, a real lint run or a real\n * GitHub setting. Nothing here reads a list that someone ticks by hand.\n *\n * **One copy, in the package** (docs/GATES.md \u00A723). Until UIG-10 this engine was\n * `scripts/gates-status.mjs`, the same file copied by hand into estiva-ui, Peek\n * and Ship, with estiva-ui warning when the copies drifted. Now it ships here,\n * and a repo runs it with `estiva-gates status`. A repo still carrying its own\n * copy is named as one when estiva-ui runs it; UIG-32 moves Peek and Ship off\n * theirs.\n *\n * What differs per repo is `scripts/gates-checks.mjs`, beside the app: its\n * checks, or the checks every app runs (`appChecks`) plus its own. It sits with\n * the app because it imports the package: in an app that sits in a folder of\n * its own, the checks file sits there too (UIG-32).\n *\n * The rows and the reasons for each check are in estiva-ui docs/GATES.md \u00A715\n * and \u00A717.\n */\nimport { execFileSync, spawnSync } from 'node:child_process'\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\n\nexport type CheckResult = { result: 'pass' | 'fail' | 'part' | 'unknown'; detail: string }\ntype Maybe<T> = T | Promise<T>\n\nexport interface LintProbe {\n /** The folder ESLint runs from, relative to the repo: `.` unless the app sits in a folder of its own. */\n cwd?: string\n /** The config file, relative to `cwd`. */\n config: string\n /** The path the text is linted as, relative to `cwd`. Nothing is written. */\n file: string\n code: string\n expect: 'error' | 'warning' | 'none'\n /** Words the expected message must contain. */\n mentions?: string\n}\n\n/** The helpers a checks file is written with. */\nexport interface GateHelpers {\n PASS(detail: string): CheckResult\n FAIL(detail: string): CheckResult\n PART(detail: string): CheckResult\n UNKNOWN(detail: string): CheckResult\n exists(rel: string): boolean\n read(rel: string): string\n listFiles(rel: string, test: (name: string, path: string) => boolean): string[]\n file(rel: string): CheckResult\n committed(rel: string): CheckResult\n contains(rel: string, needle: string | RegExp, label?: string): CheckResult\n lacks(rel: string, needle: string | RegExp, label?: string): CheckResult\n script(pkgRel: string, name: string): CheckResult\n loads(rel: string): Promise<CheckResult>\n ci(script: string | RegExp): CheckResult\n ciJob(job: string, script: string): CheckResult\n hook(settingsRel: string, needle: string): CheckResult\n json(rel: string, test?: (data: unknown) => boolean, label?: string): CheckResult\n lint(probe: LintProbe): Promise<CheckResult>\n protectedBranch(pattern: RegExp): CheckResult\n gh(args: string[], label: string): CheckResult\n share(files: string[], test: (file: string) => boolean, label: string): CheckResult\n /** Build the catalogue of the app in `appDir` (UIG-13): passes when every part is described and sorted. */\n catalogue(appDir: string): Promise<CheckResult>\n /** Every reusable part keeps the usage-page contract (UIG-19): the package's one copy of it. */\n contract(appDir: string): Promise<CheckResult>\n /**\n * What loads into every Claude session stays small (UIG-21): `CLAUDE.md` and\n * every file it imports with `@` come to fewer than `limit` lines, and each\n * `.claude/rules` file has `paths:`, so it loads only where it applies.\n */\n instructions(limit?: number): CheckResult\n}\n\nexport interface GateCheck {\n what: string\n run: () => Maybe<CheckResult>\n aggregate?: boolean\n}\nexport interface GateTicket {\n ref: string\n title?: string\n owner: boolean\n checks: GateCheck[]\n}\nexport interface TicketListEntry {\n ref: string\n owner: string\n title: string\n parts?: string[]\n aggregate?: boolean\n}\nexport interface GateSpec {\n repo: string\n tickets: GateTicket[]\n /** estiva-ui only: every ticket, and the repos it joins in. */\n all?: TicketListEntry[]\n /** estiva-ui only: the repos beside it. `app` is the folder its app sits in, when not the top. */\n siblings?: { name: string; path: string; env: string; app?: string }[]\n}\n\ninterface Row {\n ref: string\n title?: string\n owner?: boolean\n ownerRepo?: string\n checks: (CheckResult & { repo: string; what: string })[]\n status?: string\n aggregate?: boolean\n}\ninterface Report {\n schema: 1\n repo: string\n branch: string | null\n commit: string | null\n engine: string\n rows: Row[]\n}\n\nconst { version } = createRequire(import.meta.url)('../../package.json') as { version: string }\n/** What a report says ran it. A copy of the old script reports a hash instead. */\nexport const ENGINE = `@estiva-app/ui@${version}`\n\nconst PASS = (detail: string): CheckResult => ({ result: 'pass', detail })\nconst FAIL = (detail: string): CheckResult => ({ result: 'fail', detail })\nconst PART = (detail: string): CheckResult => ({ result: 'part', detail })\nconst UNKNOWN = (detail: string): CheckResult => ({ result: 'unknown', detail })\nconst said = (e: unknown) => {\n const err = e as { stdout?: unknown; stderr?: unknown; message?: string; code?: string }\n return { text: `${err.stdout ?? ''}${err.stderr ?? ''}`, message: String(err.message ?? e), code: err.code }\n}\n\n/** The helpers, bound to one repository. */\nexport function helpers(ROOT: string): GateHelpers {\n const abs = (rel: string) => resolve(ROOT, rel)\n const exists = (rel: string) => existsSync(abs(rel))\n const read = (rel: string) => readFileSync(abs(rel), 'utf8')\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: ROOT, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()\n } catch {\n return null\n }\n }\n const listFiles = (rel: string, test: (name: string, path: string) => boolean) => {\n const out: string[] = []\n const walk = (dir: string) => {\n if (!existsSync(dir)) return\n for (const name of readdirSync(dir)) {\n if (['node_modules', '.git', 'dist', 'storybook-static', '.verify-shots'].includes(name)) continue\n const p = join(dir, name)\n if (statSync(p).isDirectory()) walk(p)\n else if (test(name, p)) out.push(relative(ROOT, p).replace(/\\\\/g, '/'))\n }\n }\n walk(abs(rel))\n return out\n }\n const escape = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n return {\n PASS, FAIL, PART, UNKNOWN, exists, read, listFiles,\n // The registry bundle's own builder, loaded only when a check asks for it:\n // it reads TypeScript with TypeScript, which the other checks never need.\n async catalogue(appDir) {\n try {\n const { buildAppRegistry, validateRegistry } = await import('../registry/index')\n const registry = buildAppRegistry({ root: abs(appDir) })\n const problems = validateRegistry(registry)\n if (problems.length) return FAIL(`${problems.length} problems, the first: ${problems[0]}`)\n return PASS(`${registry.entries.length} parts in ${registry.builtFrom.files} files, each described and sorted`)\n } catch (error) {\n const lines = String(error instanceof Error ? error.message : error).split('\\n')\n return FAIL(lines.length > 1 ? `${lines.length - 1} problems, the first: ${lines[1].trim()}` : lines[0])\n }\n },\n\n async contract(appDir) {\n try {\n const { buildAppRegistry, contractProblems, CONTRACT_KINDS } = await import('../registry/index')\n const registry = buildAppRegistry({ root: abs(appDir) })\n const problems = contractProblems(registry, abs(appDir))\n const owed = registry.entries.filter((entry) => (CONTRACT_KINDS as readonly string[]).includes(entry.app?.class ?? '')).length\n if (problems.length) return FAIL(`${problems.length} of ${owed}; the first: ${problems[0]}`)\n return PASS(`all ${owed} reusable parts keep the usage-page contract and are drawn somewhere`)\n } catch (error) {\n return FAIL(String(error instanceof Error ? error.message : error).split('\\n')[0])\n }\n },\n\n instructions(limit = 200) {\n if (!exists('CLAUDE.md')) return FAIL('there is no CLAUDE.md')\n // As `wc -l` counts them: a last line ending in a newline is not one more.\n const lines = (rel: string) => read(rel).replace(/\\r?\\n$/, '').split(/\\r?\\n/).length\n const imports = [...read('CLAUDE.md').matchAll(/^@(\\S+)\\s*$/gm)].map((m) => m[1])\n const missing = imports.filter((rel) => !exists(rel))\n if (missing.length) return FAIL(`CLAUDE.md imports ${missing.join(', ')}, which is not there`)\n const total = imports.reduce((n, rel) => n + lines(rel), lines('CLAUDE.md'))\n const what = imports.length ? `CLAUDE.md with ${imports.join(', ')}` : 'CLAUDE.md'\n if (total >= limit) return FAIL(`${what}: ${total} lines load into every session; the limit is ${limit}`)\n const rules = listFiles('.claude/rules', (n) => n.endsWith('.md'))\n const unscoped = rules.filter((rel) => {\n const front = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(read(rel))\n return !front || !/^paths:/m.test(front[1])\n })\n if (unscoped.length) return FAIL(`${unscoped.join(', ')} has no paths:, so it loads into every session`)\n return PASS(`${what}: ${total} lines in every session; ${rules.length ? `${rules.length} rule files, each loading only for its paths` : 'no rule files'}`)\n },\n\n file(rel) {\n return exists(rel) ? PASS(`${rel} exists`) : FAIL(`${rel} does not exist`)\n },\n\n committed(rel) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n return git('ls-files', '--error-unmatch', rel) ? PASS(`${rel} is committed`) : FAIL(`${rel} exists but is not committed`)\n },\n\n contains(rel, needle, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n const hit = typeof needle === 'string' ? read(rel).includes(needle) : needle.test(read(rel))\n return hit ? PASS(label ?? `${rel} has ${needle}`) : FAIL(label ? `not yet: ${label}` : `${rel} has no ${needle}`)\n },\n\n lacks(rel, needle, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n const hit = typeof needle === 'string' ? read(rel).includes(needle) : needle.test(read(rel))\n return hit ? FAIL(`${rel} still has ${needle}`) : PASS(label ?? `${rel} has no ${needle}`)\n },\n\n script(pkgRel, name) {\n if (!exists(pkgRel)) return FAIL(`${pkgRel} does not exist`)\n const cmd = (JSON.parse(read(pkgRel)) as { scripts?: Record<string, string> }).scripts?.[name]\n return cmd ? PASS(`${pkgRel} runs \"${name}\": ${cmd}`) : FAIL(`${pkgRel} has no \"${name}\" script`)\n },\n\n async loads(rel) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n try {\n const mod = (await import(pathToFileURL(abs(rel)).href)) as { default?: unknown }\n return mod.default ? PASS(`${rel} loads`) : FAIL(`${rel} has no default export`)\n } catch (e) {\n return FAIL(`${rel} does not load: ${said(e).message.split('\\n')[0]}`)\n }\n },\n\n /** A workflow step that runs `npm run <script>`; a regex matches the script name. */\n ci(script) {\n const files = listFiles('.github/workflows', (n) => /\\.ya?ml$/.test(n))\n const name = typeof script === 'string' ? escape(script) : script.source\n const step = new RegExp(`npm run (${name})(?![\\\\w:-])`)\n for (const f of files) {\n const m = read(f).match(step)\n if (m) return PASS(`${f} runs npm run ${m[1]}`)\n }\n return FAIL(`no workflow runs npm run ${script}`)\n },\n\n /**\n * A workflow job, by its id, with a step that runs `npm run <script>`. Branch\n * protection requires a whole job by its name, so a required check needs the\n * job, not the step somewhere (UIG-6). A job is a key two spaces in under\n * `jobs:`, and ends at the next line that is not indented further; a comment\n * line does not count.\n */\n ciJob(job, script) {\n const files = listFiles('.github/workflows', (n) => /\\.ya?ml$/.test(n))\n const header = new RegExp(`^ ${escape(job)}:\\\\s*(#.*)?$`)\n const step = new RegExp(`^(?!\\\\s*#).*npm run ${escape(script)}(?![\\\\w:-])`, 'm')\n for (const f of files) {\n const lines = read(f).split(/\\r?\\n/)\n const from = lines.findIndex((l) => /^jobs:\\s*$/.test(l))\n const start = from === -1 ? -1 : lines.findIndex((l, i) => i > from && header.test(l))\n if (start === -1) continue\n const end = lines.findIndex((l, i) => i > start && /^ {0,2}\\S/.test(l))\n const body = lines.slice(start + 1, end === -1 ? undefined : end).join('\\n')\n return step.test(body) ? PASS(`${f}: the job ${job} runs npm run ${script}`) : FAIL(`${f}: the job ${job} does not run npm run ${script}`)\n }\n return FAIL(`no workflow has a job ${job}`)\n },\n\n hook(settingsRel, needle) {\n if (!exists(settingsRel)) return FAIL(`${settingsRel} does not exist`)\n let s: { hooks?: { PreToolUse?: { matcher?: string; hooks?: { command?: string }[] }[] } }\n try {\n s = JSON.parse(read(settingsRel))\n } catch {\n return FAIL(`${settingsRel} is not valid JSON`)\n }\n const entries = (s.hooks?.PreToolUse ?? []).flatMap((m) => (m.hooks ?? []).map((x) => ({ matcher: m.matcher ?? '', command: x.command ?? '' })))\n const hit = entries.find((e) => e.command.includes(needle))\n if (!hit) return FAIL(entries.length ? `${settingsRel} has PreToolUse hooks, none running ${needle}` : `${settingsRel} has no PreToolUse hook`)\n // A hook that is written but never fires, or fires and cannot run, guards\n // nothing \u2014 and Claude Code lets a write through on any exit but 2 (audit\n // A2: Ship's hook pointed at an install its checkout did not have). So the\n // hook is run, as Claude Code would run it, on a raw <button>.\n let matches: boolean\n try {\n matches = hit.matcher === '' || hit.matcher === '*' || new RegExp(`^(?:${hit.matcher})$`).test('Write')\n } catch {\n return FAIL(`${settingsRel}: the hook's matcher \"${hit.matcher}\" is not a pattern`)\n }\n if (!matches) return FAIL(`${settingsRel}: the hook runs only for \"${hit.matcher}\", never for a Write`)\n const top = ROOT.split('\\\\').join('/')\n const app = /--app\\s+(\"?)([^\"\\s]+)\\1/.exec(hit.command)?.[2] ?? '.'\n const input = JSON.stringify({\n hook_event_name: 'PreToolUse',\n cwd: top,\n tool_name: 'Write',\n tool_input: { file_path: `${resolve(ROOT, app, 'src', '__gates_hook_probe__.tsx').split('\\\\').join('/')}`, content: 'export function Probe() {\\n return <div><button type=\"button\">x</button></div>\\n}\\n' },\n })\n const command = hit.command.replace(/\\$\\{?CLAUDE_PROJECT_DIR\\}?/g, top)\n const run = spawnSync(command, { cwd: ROOT, input, shell: true, encoding: 'utf8', timeout: 120_000, env: { ...process.env, CLAUDE_PROJECT_DIR: top } })\n // Refused for the button, not for something else about a new file (in the package a new\n // file with no page is refused too): nested, so the package's inward rule reads it as well.\n if (run.status === 2 && /<button>/.test(`${run.stderr ?? ''}${run.stdout ?? ''}`)) return PASS(`${settingsRel}: the hook ran and refused a raw <button> (exit 2)`)\n const why = `${run.stderr ?? ''}${run.stdout ?? ''}`.trim().split(/\\r?\\n/)[0] || (run.error ? String(run.error.message) : 'no output')\n return FAIL(`${settingsRel}: the hook ran and let a raw <button> through (exit ${run.status ?? 'none'}): ${why}`)\n },\n\n json(rel, test = () => true, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n let data: unknown\n try {\n data = JSON.parse(read(rel))\n } catch {\n return FAIL(`${rel} is not valid JSON`)\n }\n if (!git('ls-files', '--error-unmatch', rel)) return FAIL(`${rel} is not committed`)\n return test(data) ? PASS(label ?? `${rel} is committed and parses`) : FAIL(`${rel} parses, but ${label ?? 'fails its test'}`)\n },\n\n /** Lints a line of code that is never written to disk, the way the hook will. */\n async lint({ cwd = '.', config, file, code, expect, mentions }) {\n const cfg = join(cwd, config)\n if (!exists(cfg)) return FAIL(`${cfg} does not exist`)\n let ESLint: typeof ESLintClass\n try {\n const req = createRequire(join(abs(cwd), 'package.json'))\n ;({ ESLint } = (await import(pathToFileURL(req.resolve('eslint')).href)) as { ESLint: typeof ESLintClass })\n } catch (e) {\n return UNKNOWN(`could not load eslint from ${cwd}: ${said(e).message.split('\\n')[0]}`)\n }\n let results: ESLintClass.LintResult[]\n try {\n const eslint = new ESLint({ cwd: abs(cwd), overrideConfigFile: config })\n results = await eslint.lintText(code, { filePath: join(abs(cwd), file), warnIgnored: true })\n } catch (e) {\n return UNKNOWN(`${cfg} could not lint ${file}: ${said(e).message.split('\\n')[0]}`)\n }\n const messages = results.flatMap((r) => r.messages)\n const ignored = messages.some((m) => /ignored/i.test(m.message) && !m.ruleId)\n const fatal = messages.find((m) => m.fatal && !/ignored/i.test(m.message))\n if (fatal) return UNKNOWN(`${file} did not parse: ${fatal.message}`)\n const want = expect === 'error' ? 2 : expect === 'warning' ? 1 : 0\n if (want === 0) {\n const any = messages.filter((m) => m.ruleId && m.severity === 2)\n return any.length ? FAIL(`${file} gets an error it should not: ${any[0].message}`) : PASS(`${file}: no error${ignored ? ' (not linted)' : ''}`)\n }\n if (ignored) return FAIL(`${cfg} does not lint ${file}`)\n const hit = messages.find((m) => m.ruleId && m.severity === want && (!mentions || m.message.includes(mentions)))\n const kind = want === 2 ? 'error' : 'warning'\n return hit ? PASS(`${file} gets ${want === 2 ? 'an' : 'a'} ${kind}: ${hit.message}`) : FAIL(`${file} gets no ${kind}${mentions ? ` naming ${mentions}` : ''}`)\n },\n\n /**\n * A GitHub setting, not a file, so it is asked of GitHub: the checks a merge\n * into the default branch must pass, from the rulesets in force on it\n * (`rules/branches/<branch>`, which anyone who can read the repository may\n * see) and from classic protection as `branches/<branch>` reports it. Not\n * `branches/<branch>/protection`: GitHub answers \"Not Found\" there to anyone\n * who is not an admin, and it knows nothing of rulesets (UIG-6).\n */\n protectedBranch(pattern) {\n const url = git('remote', 'get-url', 'origin')\n const slug = url?.match(/github\\.com[:/](.+?)(?:\\.git)?$/)?.[1]\n if (!slug) return UNKNOWN('no GitHub remote')\n const branch = (git('symbolic-ref', '--short', 'refs/remotes/origin/HEAD') ?? 'origin/main').replace(/^origin\\//, '')\n const ask = (path: string) => JSON.parse(execFileSync('gh', ['api', path], { stdio: ['ignore', 'pipe', 'pipe'], timeout: 20000 }).toString())\n let rules: { type: string; parameters?: { required_status_checks?: { context: string }[] } }[]\n let classic: { contexts?: string[]; checks?: { context: string }[] } | undefined\n try {\n rules = ask(`repos/${slug}/rules/branches/${branch}`)\n classic = ask(`repos/${slug}/branches/${branch}`).protection?.required_status_checks\n } catch (e) {\n const s = said(e)\n return UNKNOWN(`could not ask GitHub (${s.code === 'ENOENT' ? 'gh is not installed' : s.text.split('\\n')[0] || s.message})`)\n }\n const names = [\n ...rules.filter((r) => r.type === 'required_status_checks').flatMap((r) => r.parameters?.required_status_checks ?? []).map((c) => c.context),\n ...(classic?.contexts ?? []),\n ...(classic?.checks ?? []).map((c) => c.context),\n ]\n const hit = names.find((n) => pattern.test(n))\n if (hit) return PASS(`${slug} ${branch} requires \"${hit}\"`)\n return FAIL(names.length ? `${slug} ${branch} requires ${names.map((n) => `\"${n}\"`).join(', ')}, none matching ${pattern}` : `${slug} ${branch} requires no check to merge`)\n },\n\n gh(args, label) {\n try {\n execFileSync('gh', args, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 20000 })\n return PASS(label)\n } catch (e) {\n const s = said(e)\n if (/could not resolve|not found|HTTP 404/i.test(s.text)) return FAIL(`not yet: ${label}`)\n return UNKNOWN(`could not ask GitHub (${s.code === 'ENOENT' ? 'gh is not installed' : s.text.split('\\n')[0] || s.message})`)\n }\n },\n\n /** How many of `files` pass `test`: all is a pass, some is a part. */\n share(files, test, label) {\n if (!files.length) return FAIL(`no files to check for: ${label}`)\n const ok = files.filter(test).length\n const detail = `${ok} of ${files.length}: ${label}`\n return ok === files.length ? PASS(detail) : ok ? PART(detail) : FAIL(detail)\n },\n }\n}\n\n// \u2500\u2500 running the checks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst STATUS: Record<string, string> = { done: '\u2705', started: '\uD83D\uDEA7', none: '\u2B1C', unknown: '\u2754' }\n\nfunction statusOf(checks: CheckResult[]) {\n const n = (r: CheckResult['result']) => checks.filter((c) => c.result === r).length\n const pass = n('pass'), part = n('part'), fail = n('fail'), unknown = n('unknown')\n if (!checks.length) return 'unknown'\n if (pass === checks.length) return 'done'\n if (pass + part > 0) return fail === 0 && unknown > 0 ? 'unknown' : 'started'\n return fail > 0 ? 'none' : 'unknown'\n}\n\nasync function runRepo(ROOT: string, app: string, h: GateHelpers): Promise<{ spec: GateSpec; report: Report }> {\n // The checks file sits with the app, because it imports `appChecks` from the\n // package: for an app in a folder of its own, that install is there, not in\n // the repo's top folder (UIG-32). Everything it names is still read from the top.\n const checksFile = join(ROOT, app, 'scripts', 'gates-checks.mjs')\n const define = ((await import(pathToFileURL(checksFile).href)) as { default: (h: GateHelpers) => GateSpec }).default\n const spec = define(h)\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: ROOT, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()\n } catch {\n return null\n }\n }\n const rows: Row[] = []\n for (const t of spec.tickets) {\n const checks: Row['checks'] = []\n for (const c of t.checks ?? []) {\n if (c.aggregate) continue\n let r: CheckResult\n try {\n r = await c.run()\n } catch (e) {\n r = UNKNOWN(`the check threw: ${said(e).message}`)\n }\n checks.push({ repo: spec.repo, what: c.what, ...r })\n }\n rows.push({ ref: t.ref, title: t.title, owner: t.owner, checks })\n }\n return { spec, report: { schema: 1, repo: spec.repo, branch: git('rev-parse', '--abbrev-ref', 'HEAD'), commit: git('rev-parse', '--short', 'HEAD'), engine: ENGINE, rows } }\n}\n\n/**\n * Another repository's report. A repo on the package's engine runs it from its\n * own install; a repo still carrying `scripts/gates-status.mjs` runs that copy.\n *\n * `app` is the folder that repo's app sits in, when not the top, where its\n * install and its checks file are. The report is still of the whole repo.\n */\nfunction runSibling(dir: string, app: string, shown: string): { report?: Report; error?: string } {\n const copy = join(dir, 'scripts', 'gates-status.mjs')\n const installed = createRequire(join(dir, app, 'package.json'))\n let script = copy\n const args = ['--json']\n if (!existsSync(copy)) {\n try {\n script = join(installed.resolve('@estiva-app/ui/package.json'), '..', 'dist', 'gates', 'cli.js')\n args.unshift('status')\n if (app !== '.') args.push('--app', app)\n } catch {\n return { error: `${shown} has neither its own gates-status.mjs nor @estiva-app/ui installed${app === '.' ? '' : ` in ${app}`}` }\n }\n if (!existsSync(script)) return { error: `${shown} has an @estiva-app/ui with no gates engine: install 0.21.0 or later` }\n }\n try {\n const out = execFileSync(process.execPath, [script, ...args], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 300000, maxBuffer: 16 * 1024 * 1024 })\n return { report: JSON.parse(out.toString()) as Report }\n } catch (e) {\n const err = e as { stderr?: unknown; message?: string }\n return { error: `its gates:status failed: ${String(err.stderr ?? err.message).split('\\n').filter(Boolean).slice(-1)[0]}` }\n }\n}\n\n/**\n * How far a checkout is behind its remote's main, after fetching it. Status reads\n * the checkout, not main: a copy seven commits behind once reported a finished\n * ticket as not started (audit A4). Empty when it is level; a warning otherwise.\n */\nexport function behindMain(dir: string): string {\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: dir, stdio: ['ignore', 'pipe', 'ignore'], timeout: 30000 }).toString().trim()\n } catch {\n return null\n }\n }\n if (git('rev-parse', '--is-inside-work-tree') !== 'true') return ''\n const fetched = git('fetch', '-q', 'origin') !== null\n const main = (git('symbolic-ref', '--short', 'refs/remotes/origin/HEAD') ?? 'origin/main').trim()\n const count = Number(git('rev-list', '--count', `HEAD..${main}`))\n const stale = fetched ? '' : ' (could not fetch: compared with the last fetch)'\n if (!Number.isFinite(count) || count === 0) return stale ? ` \u00B7${stale}` : ''\n return ` \u00B7 \u26A0\uFE0F ${count} commit${count === 1 ? '' : 's'} behind ${main}${stale}: pull, or this reads old code`\n}\n\nfunction pad(s: string, n: number) {\n const len = [...s].length\n return len >= n ? s : s + ' '.repeat(n - len)\n}\n\nfunction printRows(rows: Row[], { showOwner, detail }: { showOwner: boolean; detail: boolean }) {\n const lines: string[] = []\n const width = Math.max(...rows.map((r) => [...(r.title ?? '')].length))\n for (const r of rows) {\n const pass = r.checks.filter((c) => c.result === 'pass').length\n const count = r.checks.length ? `${pass} of ${r.checks.length}` : 'no checks here'\n lines.push(`${STATUS[r.status ?? 'unknown']} ${pad(r.ref, 7)} ${pad(r.title ?? '', width)} ${showOwner ? pad(r.ownerRepo ?? '', 10) : ''}${count}`)\n if (detail || r.status === 'started' || r.status === 'unknown') {\n for (const c of r.checks) {\n const mark = { pass: '\u2713', part: '\u00BD', fail: '\u2717', unknown: '?' }[c.result]\n lines.push(` ${mark} ${showOwner ? `${pad(c.repo, 10)} ` : ''}${c.what} \u2014 ${c.detail}`)\n }\n }\n }\n return lines.join('\\n')\n}\n\nfunction summary(rows: Row[]) {\n const n = (s: string) => rows.filter((r) => r.status === s).length\n return `${STATUS.done} ${n('done')} done \u00B7 ${STATUS.started} ${n('started')} started \u00B7 ${STATUS.none} ${n('none')} not started \u00B7 ${STATUS.unknown} ${n('unknown')} could not check`\n}\n\nexport interface StatusOptions {\n /** The repository to report on. */\n root?: string\n /** The folder that holds the app, relative to `root`: `.` unless it sits in a folder of its own. */\n app?: string\n json?: boolean\n detail?: boolean\n}\n\n/** Print where the project stands. Returns what was printed, or the JSON report. */\nexport async function runStatus({ root = process.cwd(), app = '.', json = false, detail = false }: StatusOptions = {}): Promise<string> {\n const ROOT = resolve(root)\n const h = helpers(ROOT)\n const shown = (dir: string) => (relative(ROOT, dir) || dir).replace(/\\\\/g, '/')\n const { spec, report } = await runRepo(ROOT, app, h)\n\n if (json) return JSON.stringify(report)\n\n const out: string[] = []\n out.push('UI Guardrails \u00B7 gates:status', 'Read from the code. Nothing here is a hand-ticked list.', '')\n out.push(`${pad(spec.repo, 10)} ${report.branch} @ ${report.commit}${behindMain(ROOT)}`)\n\n if (!spec.all) {\n // A sibling repo on its own: its own rows, then its parts of rows owned elsewhere.\n const own = report.rows.filter((r) => r.owner).map((r) => ({ ...r, ownerRepo: spec.repo, status: statusOf(r.checks) }))\n const parts = report.rows.filter((r) => !r.owner).map((r) => ({ ...r, ownerRepo: '', status: statusOf(r.checks) }))\n out.push('', `Tickets ${spec.repo} owns (${own.length}):`, printRows(own, { showOwner: false, detail }))\n out.push('', `Parts of tickets another repo owns, checked here (${parts.length}):`, printRows(parts, { showOwner: false, detail }))\n out.push('', 'For every ticket across the repos, run npm run gates:status in estiva-ui.')\n return out.join('\\n')\n }\n\n // estiva-ui: gather the sibling checkouts and join everything into one row per ticket.\n const reports: Report[] = [report]\n const found: { name: string; note: string; missing?: boolean }[] = []\n for (const s of spec.siblings ?? []) {\n const dir = resolve(ROOT, process.env[s.env] ?? s.path)\n if (!existsSync(join(dir, 'package.json'))) {\n found.push({ name: s.name, note: `not found at ${shown(dir)} (set ${s.env} to point at it)`, missing: true })\n continue\n }\n const r = runSibling(dir, s.app ?? '.', shown(dir))\n if (!r.report) {\n found.push({ name: s.name, note: r.error ?? 'no report', missing: true })\n continue\n }\n const copy = r.report.engine !== ENGINE ? (r.report.engine.startsWith('@estiva-app/ui@') ? ` \u00B7 on ${r.report.engine}'s engine` : ' \u00B7 runs its own copy of the status engine; UIG-32 moves it onto the package') : ''\n found.push({ name: s.name, note: `${r.report.branch} @ ${r.report.commit}, found at ${shown(dir)}${copy}${behindMain(dir)}` })\n reports.push(r.report)\n }\n for (const f of found) out.push(`${pad(f.name, 10)} ${f.note}`)\n\n const problems: string[] = []\n const known = new Set(spec.all.map((t) => t.ref))\n for (const rep of reports) {\n for (const r of rep.rows.filter((r) => !known.has(r.ref))) problems.push(`${rep.repo} reports ${r.ref}, which is not one of the tickets`)\n }\n const rows: Row[] = spec.all.map((t) => {\n const pieces = reports.flatMap((rep) => rep.rows.filter((r) => r.ref === t.ref).map((r) => ({ rep, r })))\n const claims = pieces.filter((p) => p.r.owner).map((p) => p.rep.repo)\n const ownerMissing = found.some((f) => f.name === t.owner && f.missing)\n if (!ownerMissing && (claims.length !== 1 || claims[0] !== t.owner)) {\n problems.push(`${t.ref}: should be owned by ${t.owner}, is claimed by ${claims.join(' and ') || 'no repo'}`)\n }\n for (const p of pieces.filter((p) => !p.r.owner && !(t.parts ?? []).includes(p.rep.repo))) {\n problems.push(`${t.ref}: ${p.rep.repo} checks a part of it, but the ticket list does not name ${p.rep.repo}`)\n }\n const checks = pieces.flatMap((p) => p.r.checks)\n for (const f of found.filter((f) => f.missing)) {\n if ((t.parts ?? []).includes(f.name) || t.owner === f.name) checks.push({ repo: f.name, what: `${f.name}'s part`, result: 'unknown', detail: f.note })\n }\n return { ref: t.ref, title: t.title, ownerRepo: t.owner, checks, aggregate: t.aggregate }\n })\n for (const row of rows) row.status = statusOf(row.checks)\n for (const row of rows.filter((r) => r.aggregate)) {\n const others = rows.filter((r) => r !== row)\n const done = others.filter((r) => r.status === 'done').length\n row.checks.push({ repo: 'all', what: 'every other ticket is done', ...(done === others.length ? PASS(`${done} of ${others.length} done`) : FAIL(`${done} of ${others.length} done`)) })\n row.status = statusOf(row.checks)\n }\n\n out.push('', printRows(rows, { showOwner: true, detail }), '', summary(rows))\n const owners = Object.entries(rows.reduce<Record<string, number>>((a, r) => ({ ...a, [r.ownerRepo ?? '']: (a[r.ownerRepo ?? ''] ?? 0) + 1 }), {}))\n .map(([k, v]) => `${k} ${v}`)\n .join(', ')\n out.push(`${rows.length} tickets. Owned by ${owners}.`)\n const unchecked = found.filter((f) => f.missing && spec.all!.some((t) => t.owner === f.name)).map((f) => f.name)\n if (problems.length) out.push(`\u26A0\uFE0F Ownership does not add up:\\n ${problems.join('\\n ')}`)\n else if (unchecked.length) out.push(`\u2754 Ownership not fully checked: ${unchecked.join(' and ')} could not be read.`)\n else out.push('\u2705 Each ticket is owned by exactly one repo, and every repo agrees.')\n if (!detail) out.push('Add -- --detail to see every check.')\n return out.join('\\n')\n}\n"],
5
- "mappings": "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",
6
- "names": ["existsSync", "readFileSync", "writeFileSync", "createRequire", "join", "relative", "pathToFileURL", "countGates", "readFileSync", "relative", "createRequire", "join", "pathToFileURL", "countGates", "existsSync", "writeFileSync", "spawnSync", "existsSync", "readFileSync", "createRequire", "join", "relative", "resolve", "pathToFileURL", "PASS", "r", "p", "f"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/gates/create-app.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * `create-estiva-app <name>` \u2014 a new Estiva app that runs, with every gate on\n * (UIG-10).\n *\n * npx -p @estiva-app/ui create-estiva-app leaf [--title Leaf] [--theme light]\n *\n * The command is `create-app-cli.ts`; this file is what it runs, and what a test reads.\n *\n * **It reads only this package** (Katerina's ruling of 17 September, docs/GATES.md\n * \u00A723). Never Peek, never Ship: they are private, and reading them would carry\n * their history into every new app. What a new app needs to run a gate is this\n * package's own \u2014 the rules, the token and gate configs, the hook, the count,\n * gates:status and the checks every app runs \u2014 so the app gets short files that\n * import them, and a gate added later arrives with a version bump.\n *\n * What only a new app has is written here: its first page in the sidebar frame\n * (ruled the same day), its one theme, sign-in the way Ship signs in, its one\n * relay connection the way Peek and Ship hold theirs, its tests, its stories,\n * the CI workflow with the job `gate`, and its README and CLAUDE.md.\n *\n * The relay (UIG-10, reopened 18 September): a made app gets `protocol`,\n * `platform` and `interop` and is connected from its first commit, or runs alone\n * when no relay is set. The wiring is the app's own file, using `platform` as it\n * is \u2014 not a helper in `platform` \u2014 because a package takes code a real app has\n * already run (ADR 0002 \u00A710), and Peek and Ship each hold theirs the same way.\n *\n * Tool versions are this package's own (its devDependencies build and test the\n * same tools). The ones it does not use itself ({@link ASKED_OF_NPM}) are asked of\n * the npm registry when the app is made. The lockfile is the app's first\n * `npm install`; make it on Linux.\n */\nimport { execFileSync } from 'node:child_process'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { APP_RULE_IDS } from '../eslint/index'\nimport { loaderText, SEARCH_RULES } from '../registry/skill'\n\ninterface PackageJson {\n version: string\n devDependencies: Record<string, string>\n}\n\nexport interface CreateAppOptions {\n /** The folder and npm name: lowercase letters, digits and dashes. */\n name: string\n /** What the app is called on screen. */\n title?: string\n /** One of this package's themes. */\n theme?: string\n /** The folder the app's folder is made in. */\n parent?: string\n /** The `@estiva-app/ui` dependency, when not this package's own version (a tarball, for a proof). */\n ui?: string\n /** Versions of the packages this package does not itself use; asked of npm when absent. */\n versions?: Record<string, string>\n}\n\nconst here = dirname(fileURLToPath(import.meta.url))\nconst packageRoot = resolve(here, '..', '..')\n\n/** What a made app depends on that this package does not use itself: asked of npm when the app is made. */\nexport const ASKED_OF_NPM = ['@estiva-app/identity', '@estiva-app/interop', '@estiva-app/platform', '@estiva-app/protocol', 'eslint-plugin-react-hooks']\n\n/** The package's themes, read from its tokens.css: `light` is the block with no attribute. */\nexport function themes(): string[] {\n const css = readFileSync(join(packageRoot, 'tokens.css'), 'utf8')\n return ['light', ...new Set([...css.matchAll(/data-theme='([\\w-]+)'/g)].map((m) => m[1]))]\n}\n\n/** Every file a new app starts with, by path. Nothing is written. */\nexport function appFiles({ name, title = name, theme = 'light', ui, versions = {} }: CreateAppOptions): Record<string, string> {\n if (!/^[a-z][a-z0-9-]*$/.test(name)) throw new Error(`\"${name}\" is not a name an app can have: lowercase letters, digits and dashes, starting with a letter`)\n const known = themes()\n if (!known.includes(theme)) throw new Error(`\"${theme}\" is not one of the package's themes: ${known.join(', ')}`)\n\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8')) as PackageJson\n const own = (dep: string) => {\n const range = pkg.devDependencies[dep] ?? versions[dep]\n if (!range) throw new Error(`no version for ${dep}`)\n return range\n }\n const deps = (names: string[]) => Object.fromEntries(names.map((n) => [n, own(n)]))\n\n const packageJson = {\n name,\n private: true,\n version: '0.0.0',\n type: 'module',\n scripts: {\n dev: 'vite',\n build: 'tsc -b && vite build',\n preview: 'vite preview',\n typecheck: 'tsc -b',\n lint: 'eslint .',\n 'lint:tokens': 'eslint --config eslint.tokens.config.js .',\n 'lint:rules': 'eslint --config eslint.gates.config.js .',\n 'postlint:rules': `estiva-gates count --repo ${name}`,\n 'gates:status': 'estiva-gates status',\n 'ui:find': 'estiva-ui find',\n registry: 'estiva-ui build',\n 'registry:check': 'estiva-ui check',\n test: 'vitest run',\n storybook: 'storybook dev -p 6006',\n 'build-storybook': 'storybook build',\n },\n dependencies: {\n '@estiva-app/identity': own('@estiva-app/identity'),\n '@estiva-app/interop': own('@estiva-app/interop'),\n '@estiva-app/platform': own('@estiva-app/platform'),\n '@estiva-app/protocol': own('@estiva-app/protocol'),\n '@estiva-app/ui': ui ?? `^${pkg.version}`,\n ...deps(['@tabler/icons-react', 'react', 'react-dom']),\n },\n devDependencies: deps([\n '@eslint/js', '@storybook/addon-docs', '@storybook/react-vite', '@testing-library/dom', '@testing-library/react',\n '@types/node', '@types/react', '@types/react-dom', '@vitejs/plugin-react', 'autoprefixer', 'eslint',\n 'eslint-plugin-better-tailwindcss', 'eslint-plugin-react-hooks', 'globals', 'jsdom', 'postcss', 'storybook',\n 'tailwindcss', 'typescript', 'typescript-eslint', 'vite', 'vitest',\n ]),\n }\n\n const count = { schemaVersion: 1, repo: name, generatedAt: new Date().toISOString(), rules: Object.fromEntries(APP_RULE_IDS.map((id) => [id, { errors: 0, warnings: 0, escapes: 0 }])) }\n const themeAttr = ` data-theme=\"${theme}\"`\n const tsBase = {\n target: 'ES2023',\n module: 'ESNext',\n skipLibCheck: true,\n moduleResolution: 'bundler',\n allowImportingTsExtensions: true,\n verbatimModuleSyntax: true,\n moduleDetection: 'force',\n noEmit: true,\n strict: true,\n noUnusedLocals: false,\n noUnusedParameters: false,\n erasableSyntaxOnly: true,\n noFallthroughCasesInSwitch: true,\n noUncheckedSideEffectImports: true,\n }\n const json = (value: unknown) => `${JSON.stringify(value, null, 2)}\\n`\n\n return {\n 'package.json': json(packageJson),\n\n // registry.json: the app's catalogue, written only when `npm run registry` is asked\n // to. It is built fresh from the code every time it is read, so it is never kept.\n '.gitignore': ['node_modules', 'dist', 'storybook-static', '*.local', '*.log', '*.tsbuildinfo', '.DS_Store', 'registry.json', ''].join('\\n'),\n\n '.env.example': `# Estiva ID, for signing in. Copy this file to .env.local and fill both in.\n#\n# Left empty, the app offers no sign-in at all and runs anonymous: it never\n# reaches the real Estiva ID by accident.\n#\n# The app must be registered with that Estiva ID first, as its own app, with\n# this redirect address: the origin the app runs on, and a slash\n# (http://localhost:5173/). The real Estiva ID registers a new app on the\n# server; a local one (http://localhost:8787) in its database.\nVITE_ESTIVA_ID_ORIGIN=\nVITE_ESTIVA_ID_CLIENT_ID=${name}\n\n# The relay: the workspace this app reads and writes.\n#\n# Left empty, the app runs alone. It opens no connection, and its home page says\n# so. It also needs sign-in above: the relay refuses anything before sign-in, so\n# with a relay and no sign-in there is still no connection. And Estiva ID must\n# allow this app to sign in to that relay (the README says how).\n#\n# A relay running on this machine is http://localhost:3000.\nVITE_RELAY_URL=\n`,\n\n 'index.html': `<!doctype html>\n<html lang=\"en\"${themeAttr}>\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${title}</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/src/main.tsx\"></script>\n </body>\n</html>\n`,\n\n 'tsconfig.json': json({ files: [], references: [{ path: './tsconfig.app.json' }, { path: './tsconfig.node.json' }] }),\n 'tsconfig.app.json': json({\n compilerOptions: { tsBuildInfoFile: './node_modules/.tmp/tsconfig.app.tsbuildinfo', ...tsBase, useDefineForClassFields: true, lib: ['ES2023', 'DOM', 'DOM.Iterable'], types: ['vite/client'], jsx: 'react-jsx', paths: { '@/*': ['./src/*'] } },\n include: ['src', '.storybook'],\n }),\n 'tsconfig.node.json': json({\n compilerOptions: { tsBuildInfoFile: './node_modules/.tmp/tsconfig.node.tsbuildinfo', ...tsBase, lib: ['ES2023'], types: ['node'] },\n include: ['vite.config.ts'],\n }),\n\n 'vite.config.ts': `import { fileURLToPath } from 'node:url'\nimport react from '@vitejs/plugin-react'\nimport { defineConfig } from 'vitest/config'\n\n// \\`react\\` is deduped because @estiva-app/ui declares it a peer: two copies in one\n// tree is the \"invalid hook call\" crash, and it appears at runtime.\nexport default defineConfig({\n plugins: [react()],\n resolve: {\n alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },\n dedupe: ['react', 'react-dom'],\n },\n test: {\n environment: 'jsdom',\n },\n})\n`,\n\n 'tailwind.config.js': `import estiva, { estivaContent } from '@estiva-app/ui/tailwind-preset'\n\n/**\n * The package's preset, and nothing of the app's own: a token is added to the\n * package, in every theme, never here.\n *\n * \\`estivaContent\\` is the package's own files. Tailwind does not merge \\`content\\`\n * from a preset, so without it every class used only by a package part is\n * purged, and the app builds clean while rendering at the wrong size.\n *\n * @type {import('tailwindcss').Config}\n */\nexport default {\n presets: [estiva],\n content: [...estivaContent, './index.html', './src/**/*.{ts,tsx}', './.storybook/**/*.{ts,tsx}'],\n}\n`,\n 'postcss.config.js': `export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n}\n`,\n\n 'eslint.config.js': `import js from '@eslint/js'\nimport { gateLint, TOKEN_LINT_IGNORES, tokenLint, tokenValues } from '@estiva-app/ui/gates'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport { defineConfig, globalIgnores } from 'eslint/config'\nimport globals from 'globals'\nimport tseslint from 'typescript-eslint'\n\n/**\n * Everything, in one lint (\\`npm run lint\\`, a CI step): TypeScript's and React's\n * recommended rules, the token contract and the UI Guardrails' rules. A new app\n * has no backlog, so all of it is a gate from the first commit.\n *\n * The token contract and the gate are the package's (\\`@estiva-app/ui/gates\\`),\n * imported, never copied: a rule written later arrives with a version bump.\n */\nexport default defineConfig([\n globalIgnores(TOKEN_LINT_IGNORES),\n {\n files: ['**/*.{ts,tsx}'],\n extends: [js.configs.recommended, tseslint.configs.recommended, reactHooks.configs.flat.recommended],\n languageOptions: { globals: globals.browser },\n },\n tokenLint(),\n tokenValues(),\n gateLint(),\n])\n`,\n 'eslint.tokens.config.js': `import { tokenConfig } from '@estiva-app/ui/gates'\nimport reactHooks from 'eslint-plugin-react-hooks'\n\n// The token contract on its own (\\`npm run lint:tokens\\`), the package's piece.\n// React's hooks plugin is named so its directives do not break it; TypeScript's\n// the package names itself.\nexport default tokenConfig({ quiet: { 'react-hooks': reactHooks } })\n`,\n 'eslint.gates.config.js': `import { gateConfig } from '@estiva-app/ui/gates'\nimport reactHooks from 'eslint-plugin-react-hooks'\n\n/**\n * The UI Guardrails' rules on their own \u2014 \\`npm run lint:rules\\`, CI's job \\`gate\\`\n * (GitHub requires it on main: never rename it), and what the editor hook in\n * \\`.claude/settings.json\\` lints a proposed write with. The package's gate,\n * imported. React's hooks plugin is named so its directives do not break it;\n * TypeScript's the package names itself.\n *\n * A place that keeps something the gate refuses says why, on the line above:\n * \\`// @estiva-escape: <reason>\\`. Never \\`eslint-disable\\`: the count refuses it.\n */\nexport default gateConfig({ quiet: { 'react-hooks': reactHooks } })\n`,\n\n '.claude/settings.json': json({\n hooks: {\n PreToolUse: [{ matcher: 'Edit|Write', hooks: [{ type: 'command', command: 'node \"$CLAUDE_PROJECT_DIR/node_modules/@estiva-app/ui/dist/gates/cli.js\" hook' }] }],\n },\n permissions: { allow: SEARCH_RULES },\n }),\n\n // The Claude skill (UIG-20): its trigger, and a line reading the installed package's text in.\n '.claude/skills/estiva-ui/SKILL.md': loaderText(resolve('/app'), resolve('/app/node_modules/@estiva-app/ui')),\n\n 'scripts/gates-checks.mjs': `import { appChecks } from '@estiva-app/ui/gates'\n\n/**\n * What gates:status checks in ${name}: the gate checks every app runs, from the\n * package (\\`appChecks\\`), and nothing of its own yet. A check about this app's\n * own code goes beside them.\n */\nexport default function define(h) {\n return { repo: '${name}', tickets: appChecks(h, { page: 'src/pages/HomePage.tsx' }) }\n}\n`,\n '.gates-count.json': json(count),\n 'docs/GATES-DEBT.md': `# What ${title} owes the gates\n\nNothing. ${title} was made with every gate on, at zero.\n\nIt should stay that way. A place that keeps something the gate refuses says why on\nthe line above it, \\`// @estiva-escape: <reason>\\`, and the count lists it; a whole\nfile that cannot pass yet goes here, with its reason.\n`,\n\n '.github/workflows/deploy.yml': `name: deploy\n\n# Every pull request and every push to main runs the checks. There is no deploy\n# job yet: a new app has nowhere to go until it has a home. When it does, the job\n# goes here, and it needs \\`check\\` and \\`gate\\` first.\n\non:\n push:\n branches: [main]\n pull_request:\n\nconcurrency:\n group: deploy-${name}-\\${{ github.ref }}\n cancel-in-progress: true\n\njobs:\n check:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 24\n cache: npm\n - run: npm ci\n - name: Typecheck the app\n run: npx tsc -b tsconfig.app.json\n - name: Typecheck the settings files\n run: npx tsc -b tsconfig.node.json\n - name: Lint \u2014 everything, the token contract included\n run: npm run lint\n - name: Test\n run: npm test\n - name: Build\n run: npm run build\n # A class only a package part uses reaches the stylesheet only if\n # tailwind.config.js spreads estivaContent. When it does not, the build still\n # succeeds and parts render at the wrong size, so this is where it is caught.\n - name: The package's classes survive Tailwind's purge\n run: |\n if ! grep -qr \"max-h-72\" dist/assets/*.css; then\n echo \"::error::a class used only by @estiva-app/ui is missing from the built CSS \u2014 check estivaContent is spread into tailwind.config.js\"\n exit 1\n fi\n\n # The UI Guardrails, the one check a merge requires: the package's gate rules\n # (each refusal names what to use instead), the token contract, and the\n # catalogue. \\`npm run lint:rules\\` also writes .gates-count.json and fails on\n # an eslint-disable that switches a rule off.\n #\n # A job of its own, because GitHub can require only a whole job, by its name:\n # the rule on main requires \\`gate\\`. Renaming it leaves every pull request\n # waiting for a check that never reports.\n gate:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 24\n cache: npm\n - run: npm ci\n - name: Gate lint\n run: npm run lint:rules\n # Colours, type, corners and shadows from the preset only. In \\`check\\` too,\n # inside \\`npm run lint\\`, but only \\`gate\\` blocks a merge.\n - name: Token lint\n run: npm run lint:tokens\n # Every part of the app says in one line what it is for, so the catalogue\n # (\\`npm run ui:find\\`) can offer it before someone builds it again.\n - name: Every part is described\n run: npm run registry:check\n`,\n\n 'src/index.css': `@import '@estiva-app/ui/tokens.css';\n@import '@estiva-app/ui/base.css';\n\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml,\nbody,\n#root {\n height: 100%;\n}\n`,\n 'src/vite-env.d.ts': `/// <reference types=\"vite/client\" />\n\ninterface ImportMetaEnv {\n readonly VITE_ESTIVA_ID_ORIGIN?: string\n readonly VITE_ESTIVA_ID_CLIENT_ID?: string\n readonly VITE_RELAY_URL?: string\n}\n`,\n 'src/config.ts': `/** What the app is called on screen. */\nexport const APP_TITLE = ${JSON.stringify(title)}\n\n/**\n * Estiva ID, or \\`null\\` when this build offers no sign-in at all. Empty is a real\n * mode, not a broken one: the app runs anonymous, and a local build can never\n * reach the real Estiva ID by accident. See .env.example.\n */\nexport const ID_CONFIG: { base: string; clientId: string } | null =\n import.meta.env.VITE_ESTIVA_ID_ORIGIN && import.meta.env.VITE_ESTIVA_ID_CLIENT_ID\n ? { base: import.meta.env.VITE_ESTIVA_ID_ORIGIN.replace(/\\\\/+$/, ''), clientId: import.meta.env.VITE_ESTIVA_ID_CLIENT_ID }\n : null\n\n/**\n * The relay, the workspace this app reads and writes, or \\`null\\` when this build\n * has none. Empty is a real mode too: the app runs alone and opens no connection,\n * and a local build can never reach the real relay by accident. See .env.example.\n */\nexport const RELAY_URL: string | null = import.meta.env.VITE_RELAY_URL?.trim().replace(/\\\\/+$/, '') || null\n\n/** The relay as a person would name it: host and port, no scheme. */\nexport function relayLabel(url: string): string {\n try {\n return new URL(url).host\n } catch {\n return url\n }\n}\n`,\n 'src/auth/estivaId.ts': `import { createEstivaId, type ShellReason, type StoredToken } from '@estiva-app/identity'\nimport { ID_CONFIG } from '../config'\n\n/**\n * Signing in with Estiva ID, the way Ship does: \\`@estiva-app/identity\\` holds the\n * flow, this file the app's choices.\n *\n * - The redirect is the origin and a slash, never the current path: Estiva ID\n * compares it exactly against the one address registered for the app.\n * - The session lives in localStorage, so it outlives a tab; a sign-in's\n * single-use credentials in sessionStorage, per tab.\n */\nconst client = ID_CONFIG\n ? createEstivaId({\n base: ID_CONFIG.base,\n clientId: ID_CONFIG.clientId,\n redirectUri: () => \\`\\${window.location.origin}/\\`,\n storage: () => (typeof localStorage === 'undefined' ? null : localStorage),\n pendingStore: () => (typeof sessionStorage === 'undefined' ? null : sessionStorage),\n keyPrefix: '${name}.estiva-id',\n navigate: (url) => window.location.assign(url),\n })\n : null\n\nexport const signInAvailable = client !== null\n\nexport function currentToken(): StoredToken | null {\n return client?.validToken(Date.now() + 60_000) ?? null\n}\n\nexport function startRenewal(): void {\n client?.scheduleRenewal()\n}\n\nexport async function beginSignIn(options: { silent?: boolean } = {}): Promise<void> {\n await client?.beginSignIn(\\`\\${window.location.pathname}\\${window.location.search}\\`, options)\n}\n\nexport function beginSignOut(): void {\n client?.beginSignOut()\n}\n\nexport async function completeSignIn(): Promise<{ token: StoredToken; returnTo: string }> {\n if (!client) throw new Error('this build offers no sign-in')\n return client.completeSignIn(window.location.search)\n}\n\nexport function stashShellReason(reason: ShellReason): void {\n client?.stashShellReason(reason)\n}\n\nexport function takeShellReason() {\n return client?.takeShellReason() ?? null\n}\n\nexport function callbackParams(): { code?: string; error?: string } | null {\n const params = new URLSearchParams(window.location.search)\n const code = params.get('code') ?? undefined\n const error = params.get('error') ?? undefined\n return code || error ? { code, error } : null\n}\n\nexport function clearQuery(): void {\n window.history.replaceState({}, '', window.location.pathname)\n}\n\nexport function guardAttemptedAt(): number | null {\n if (typeof sessionStorage === 'undefined') return null\n const raw = sessionStorage.getItem('estiva.authShell.silentAttempt')\n const at = raw ? Number(raw) : NaN\n return Number.isFinite(at) ? at : null\n}\n\n/**\n * Who Estiva ID says this is. Asked of its directory with the token, so the\n * answer also proves Estiva ID accepted the sign-in.\n */\nexport async function whoAmI(): Promise<{ name?: string; email?: string } | null> {\n const token = currentToken()\n if (!ID_CONFIG || !token) return null\n const response = await fetch(\\`\\${ID_CONFIG.base}/directory/\\${token.pubkey}\\`, { headers: { Authorization: \\`Bearer \\${token.accessToken}\\` } })\n if (!response.ok) return null\n const entry = (await response.json()) as { displayName?: string; email?: string }\n return { name: entry.displayName, email: entry.email }\n}\n`,\n 'src/auth/boot.ts': `import { decideBoot, type ShellReason, type ShellState } from '@estiva-app/identity'\nimport {\n beginSignIn,\n callbackParams,\n clearQuery,\n completeSignIn,\n currentToken,\n guardAttemptedAt,\n signInAvailable,\n startRenewal,\n stashShellReason,\n takeShellReason,\n} from './estivaId'\n\nexport type BootResult = { kind: 'app' } | { kind: 'shell'; state: ShellState } | { kind: 'leaving' }\n\n/**\n * Settle who this load belongs to, before the app renders. The decision is\n * \\`decideBoot\\` from \\`@estiva-app/identity\\`; this is the reading and acting\n * around it. It never throws: a blank page is worse than an honest shell.\n */\nexport async function bootAuth(): Promise<BootResult> {\n if (!signInAvailable) return { kind: 'app' }\n\n const action = decideBoot({\n callback: callbackParams(),\n hasValidToken: currentToken() !== null,\n guardAttemptedAt: guardAttemptedAt(),\n enteredThisPageLoad: false,\n now: Date.now(),\n })\n\n switch (action.do) {\n case 'enter':\n startRenewal()\n return { kind: 'app' }\n case 'complete_callback':\n try {\n await completeSignIn()\n clearQuery()\n startRenewal()\n return { kind: 'app' }\n } catch (cause) {\n clearQuery()\n stashShellReason('exchange_failed')\n return shell('exchange_failed', cause)\n }\n case 'probe_silently':\n try {\n await beginSignIn({ silent: true })\n return { kind: 'leaving' }\n } catch {\n return shell('network')\n }\n case 'prompt_passkey':\n return { kind: 'shell', state: { phase: 'authenticating', reason: takeShellReason() ?? action.reason } }\n case 'fail':\n return shell(action.reason)\n }\n}\n\nfunction shell(reason: ShellReason, cause?: unknown): BootResult {\n if (cause) console.warn('[auth] entry failed:', cause)\n return { kind: 'shell', state: { phase: 'failed', reason } }\n}\n\nexport async function enterFromShell(): Promise<void> {\n await beginSignIn()\n}\n`,\n 'src/auth/AuthShell.tsx': `import { CONTINUE_LABEL, isRecoverable, RETRY_LABEL, SHELL_COPY, type ShellState } from '@estiva-app/identity'\nimport { Button } from '@estiva-app/ui'\nimport { APP_TITLE } from '../config'\n\nexport interface AuthShellProps {\n state: ShellState\n onContinue: () => void\n}\n\n/**\n * What shows while nobody is signed in: the app's name, one line and at most one\n * button. The words are \\`@estiva-app/identity\\`'s, so every app says the same.\n */\nexport function AuthShell({ state, onContinue }: AuthShellProps) {\n const waiting = state.phase === 'checking' || state.phase === 'entering' || state.phase === 'ready'\n return (\n <div className=\"fixed inset-0 z-50 flex flex-col items-center justify-center gap-4 bg-bg-surface px-6 text-center\">\n <h1 className=\"text-h3 text-text-primary\">{APP_TITLE}</h1>\n {!waiting && (\n <>\n <p className=\"max-w-prose text-body-2 text-text-secondary\">{SHELL_COPY[state.reason]}</p>\n {isRecoverable(state.reason) && <Button onClick={onContinue}>{state.phase === 'authenticating' ? CONTINUE_LABEL : RETRY_LABEL}</Button>}\n </>\n )}\n </div>\n )\n}\n`,\n 'src/relay/client.ts': `import { signViaEstivaId } from '@estiva-app/identity'\nimport { browserOnlineSource, createLiveClientHolder, type LiveClient } from '@estiva-app/platform'\nimport { currentToken } from '../auth/estivaId'\nimport { ID_CONFIG, RELAY_URL } from '../config'\n\n/**\n * The event kinds this app reads. They are the app's own decision, and the one\n * blank a new app fills in: Ship reads seven (projects, issues, changes,\n * messages, comments, deletions and edits), Peek its own. Until there are kinds\n * here the app is connected and reads nothing, which is a correct state.\n */\nexport const KINDS: number[] = []\n\n/**\n * The tab's one relay client. \\`@estiva-app/platform\\` hands out a holder and\n * keeps nothing, so this line is where \"one connection per tab\" lives. The relay\n * signs a connection in once and caps subscriptions per connection, so a\n * connection per component is wrong, not only wasteful; React's StrictMode,\n * which mounts everything twice, is what would show it.\n *\n * Peek and Ship hold theirs the same way. Everything the client needs is handed\n * in and nothing here reads the app's own data, so it could move into\n * \\`@estiva-app/platform\\` as it is, if that is ever worth doing.\n */\nconst holder = createLiveClientHolder()\n\n/**\n * The client, or \\`null\\` when this build cannot have one: no relay is set, or\n * there is no sign-in. The relay refuses anything before sign-in, so a socket\n * without it would only connect, be refused and retry.\n */\nexport function relayClient(): LiveClient | null {\n const base = ID_CONFIG?.base\n if (!RELAY_URL || !base) return null\n return holder.get({\n relayUrl: RELAY_URL,\n // Read on every connect, never kept: a token kept from the first connect\n // outlives a silent renewal as a dead one, and the next reconnect fails.\n getCredential: () => {\n const token = currentToken()\n return token ? { accessToken: token.accessToken, pubkey: token.pubkey } : null\n },\n // \\`expectedPubkey\\` is a check, not a request. Estiva ID signs as the token's\n // owner whatever it is handed, so a mismatch comes back as a valid event\n // signed by somebody else. Always pass it.\n sign: (unsigned, token, expectedPubkey) => signViaEstivaId(unsigned, { base, token, expectedPubkey }),\n online: typeof window === 'undefined' ? undefined : browserOnlineSource(window),\n // Names this app in the relay's own logs.\n subscriptionPrefix: '${name}-',\n log: (message, detail) => console.debug('[relay]', message, detail ?? ''),\n })\n}\n`,\n 'src/relay/useRelayState.ts': `import type { RelayState } from '@estiva-app/protocol'\nimport { useSyncExternalStore } from 'react'\nimport { relayClient } from './client'\n\nconst subscribe = (onChange: () => void): (() => void) => relayClient()?.onState(onChange) ?? (() => {})\nconst snapshot = (): RelayState | 'off' => relayClient()?.state() ?? 'off'\n\n/** The connection's state, for a page to show: \\`'off'\\` when this build has no client. */\nexport function useRelayState(): RelayState | 'off' {\n return useSyncExternalStore(subscribe, snapshot, () => 'off')\n}\n`,\n 'src/relay/client.test.ts': `import { afterEach, describe, expect, it, vi } from 'vitest'\n\n/**\n * The tab's one relay connection. A fake socket stands in for the relay, so this\n * runs with no network, and counts the sockets opened.\n */\nclass FakeSocket {\n static opened = 0\n onopen: (() => void) | null = null\n onmessage: ((event: { data: unknown }) => void) | null = null\n onclose: (() => void) | null = null\n onerror: (() => void) | null = null\n constructor() {\n FakeSocket.opened += 1\n }\n send() {}\n close() {}\n}\n\nafterEach(() => {\n vi.unstubAllEnvs()\n vi.unstubAllGlobals()\n vi.resetModules()\n FakeSocket.opened = 0\n})\n\ndescribe('the relay client', () => {\n it('opens no connection when no relay is set', async () => {\n vi.stubEnv('VITE_RELAY_URL', '')\n vi.stubEnv('VITE_ESTIVA_ID_ORIGIN', 'http://localhost:8787')\n vi.stubEnv('VITE_ESTIVA_ID_CLIENT_ID', '${name}')\n vi.stubGlobal('WebSocket', FakeSocket)\n const { relayClient } = await import('./client')\n expect(relayClient()).toBeNull()\n expect(FakeSocket.opened).toBe(0)\n })\n\n it('opens no connection with a relay and no sign-in', async () => {\n vi.stubEnv('VITE_RELAY_URL', 'http://localhost:3000')\n vi.stubEnv('VITE_ESTIVA_ID_ORIGIN', '')\n vi.stubGlobal('WebSocket', FakeSocket)\n const { relayClient } = await import('./client')\n expect(relayClient()).toBeNull()\n expect(FakeSocket.opened).toBe(0)\n })\n\n it('holds one connection per tab, however often it is asked', async () => {\n vi.stubEnv('VITE_RELAY_URL', 'http://localhost:3000')\n vi.stubEnv('VITE_ESTIVA_ID_ORIGIN', 'http://localhost:8787')\n vi.stubEnv('VITE_ESTIVA_ID_CLIENT_ID', '${name}')\n vi.stubGlobal('WebSocket', FakeSocket)\n const { relayClient } = await import('./client')\n const first = relayClient()\n expect(first).not.toBeNull()\n expect(relayClient()).toBe(first)\n expect(FakeSocket.opened).toBe(1)\n first?.close()\n })\n})\n`,\n 'src/main.tsx': `import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport './index.css'\nimport { AuthShell } from './auth/AuthShell'\nimport { bootAuth, enterFromShell } from './auth/boot'\n\n// Settle who this load belongs to first, then render: the app itself is imported\n// only once sign-in is settled, so nothing in it evaluates as the wrong person.\nvoid (async () => {\n const root = createRoot(document.getElementById('root')!)\n const boot = await bootAuth()\n if (boot.kind === 'leaving') return\n if (boot.kind === 'shell') {\n root.render(\n <StrictMode>\n <AuthShell state={boot.state} onContinue={() => void enterFromShell()} />\n </StrictMode>,\n )\n return\n }\n const { App } = await import('./App')\n root.render(\n <StrictMode>\n <App />\n </StrictMode>,\n )\n})()\n`,\n 'src/App.tsx': `import { AppShell, IdentityMenu, NavItem, Sidebar, type Identity } from '@estiva-app/ui'\nimport { IconHome } from '@tabler/icons-react'\nimport { useEffect, useState } from 'react'\nimport { beginSignOut, currentToken, whoAmI } from './auth/estivaId'\nimport { APP_TITLE, ID_CONFIG, RELAY_URL } from './config'\nimport { HomePage } from './pages/HomePage'\nimport { useRelayState } from './relay/useRelayState'\n\n/** The frame: the package's AppShell with a sidebar, and the one page. */\nexport function App() {\n const signedIn = currentToken() !== null\n const [me, setMe] = useState<Identity>({})\n const relayState = useRelayState()\n\n useEffect(() => {\n if (!signedIn) return\n let live = true\n void whoAmI().then((who) => {\n if (live && who) setMe(who)\n })\n return () => {\n live = false\n }\n }, [signedIn])\n\n return (\n <AppShell\n logo={APP_TITLE}\n identity={<IdentityMenu me={me} signedIn={signedIn} idBase={ID_CONFIG?.base} onSignOut={signedIn ? beginSignOut : undefined} />}\n nav={\n <Sidebar>\n <NavItem href=\"/\" label=\"Home\" icon={<IconHome size={16} stroke={1.5} />} active />\n </Sidebar>\n }\n >\n <HomePage relay={RELAY_URL} state={relayState} name={me.name} />\n </AppShell>\n )\n}\n`,\n 'src/pages/HomePage.tsx': `import type { RelayState } from '@estiva-app/protocol'\nimport { EmptyState } from '@estiva-app/ui'\nimport { relayLabel } from '../config'\n\nexport interface HomePageProps {\n /** The relay this build is set to, or \\`null\\` when the app runs alone. */\n relay: string | null\n /** The connection's state: \\`'off'\\` when this build has no client. */\n state: RelayState | 'off'\n /** The signed-in person's name, once Estiva ID has said it. */\n name?: string\n}\n\nconst SAYS: Record<RelayState, (host: string, name?: string) => string> = {\n connecting: (host) => \\`Connecting to \\${host}\u2026\\`,\n authenticating: (host) => \\`Signing in to \\${host}\u2026\\`,\n live: (host, name) => (name ? \\`Connected to \\${host} as \\${name}.\\` : \\`Connected to \\${host}.\\`),\n reconnecting: (host) => \\`Reconnecting to \\${host}\u2026\\`,\n failed: (host) => \\`Could not connect to \\${host}.\\`,\n}\n\n/**\n * The first page. What it becomes is this app's own work; until then it says\n * whether the app is connected to the relay, and as whom.\n *\n * The empty state goes straight into the frame's \\`main\\`, with no box around it:\n * \\`main\\` is a flex column, so the empty state takes the room left and centres\n * in it both ways, as its own page says. A box around it would place it\n * instead, and no gate reads a box. The connection is a quiet caption after it,\n * at the foot of the page.\n */\nexport function HomePage({ relay, state, name }: HomePageProps) {\n const line = !relay\n ? 'Running alone: no relay is set. Set VITE_RELAY_URL in .env.local to connect.'\n : state === 'off'\n ? \\`Not connected: \\${relayLabel(relay)} needs sign-in, and this build has none.\\`\n : SAYS[state](relayLabel(relay), name)\n return (\n <>\n <EmptyState message=\"Nothing here yet.\" />\n <p className=\"px-6 pb-6 text-center text-caption text-text-muted\">{line}</p>\n </>\n )\n}\n`,\n 'src/pages/HomePage.stories.tsx': `import type { Meta, StoryObj } from '@storybook/react-vite'\nimport { HomePage } from './HomePage'\n\nconst meta = {\n title: 'Pages/Home',\n component: HomePage,\n parameters: { layout: 'fullscreen' },\n // Stands in for the frame's main: a flex column the height of the screen,\n // which is what the page is drawn into in the app.\n decorators: [\n (Story) => (\n <div className=\"flex h-screen flex-col\">\n <Story />\n </div>\n ),\n ],\n} satisfies Meta<typeof HomePage>\n\nexport default meta\ntype Story = StoryObj<typeof meta>\n\n/** No relay set: the app runs alone, the way it starts. */\nexport const RunningAlone: Story = { args: { relay: null, state: 'off' } }\n\n/** A relay set, before it has signed the connection in. */\nexport const Connecting: Story = { args: { relay: 'http://localhost:3000', state: 'connecting' } }\n\n/** Connected, as the person who signed in. */\nexport const Connected: Story = { args: { relay: 'http://localhost:3000', state: 'live', name: 'Alex Kim' } }\n`,\n 'src/App.test.tsx': `import { render, screen } from '@testing-library/react'\nimport { describe, expect, it } from 'vitest'\nimport { App } from './App'\nimport { APP_TITLE } from './config'\n\ndescribe('${title}', () => {\n it('opens in its frame, anonymous in a build with no sign-in', () => {\n render(<App />)\n expect(screen.getAllByText(APP_TITLE).length).toBeGreaterThan(0)\n expect(screen.getByText('Nothing here yet.')).toBeTruthy()\n expect(screen.getByText(/^Running alone/)).toBeTruthy()\n expect(screen.getByRole('link', { name: 'Home' })).toBeTruthy()\n })\n})\n`,\n\n '.storybook/main.ts': `import type { StorybookConfig } from '@storybook/react-vite'\n\nconst config: StorybookConfig = {\n framework: '@storybook/react-vite',\n stories: ['../src/**/*.stories.@(ts|tsx)'],\n addons: ['@storybook/addon-docs'],\n}\n\nexport default config\n`,\n '.storybook/preview.tsx': `import type { Preview } from '@storybook/react-vite'\nimport '../src/index.css'\n\n// The app's one theme, selected the way the app selects it: on <html>.\ndocument.documentElement.dataset.theme = ${JSON.stringify(theme)}\n\nconst preview: Preview = {\n // Every story file gets a Docs page, as in Peek and Ship: the catalogue links each\n // part to it, and \\`npm run registry:check\\` in CI's job \\`gate\\` fails a link that\n // leads nowhere.\n tags: ['autodocs'],\n parameters: { layout: 'centered' },\n}\n\nexport default preview\n`,\n\n 'README.md': `# ${title}\n\nAn Estiva app, made with \\`create-estiva-app\\` from \\`@estiva-app/ui\\`. It starts\nwith the package's sidebar frame, one theme (\\`${theme}\\`), sign-in with Estiva ID,\nand every UI Guardrails gate on, at zero.\n\n## Run it\n\n\\`\\`\\`sh\nnpm install\nnpm run dev\n\\`\\`\\`\n\nWith no settings it runs **anonymous**: no sign-in is offered, and nothing reaches\nthe real Estiva ID. To sign in, copy \\`.env.example\\` to \\`.env.local\\` and fill it in.\nThe app must first be registered with that Estiva ID as its own app.\n\n## The relay\n\n${title} is connected to the relay, the workspace, from its first commit, as\nwhoever signed in. \\`src/relay/client.ts\\` holds the tab's one connection: ask it\nfor \\`relayClient()\\`, and never open a socket of your own. The home page shows the\nconnection's state.\n\nWith \\`VITE_RELAY_URL\\` empty it runs alone and opens no connection. The relay\nalso needs sign-in, so with a relay and no sign-in there is no connection either.\n\nThree packages come with it: \\`@estiva-app/protocol\\` (the wire: events, ids,\nsigning, the relay clients), \\`@estiva-app/platform\\` (the one connection a tab\nholds) and \\`@estiva-app/interop\\` (showing another app's objects, from the\nmanifest that app publishes).\n\n## What you fill in\n\n1. \\`.env.local\\`: Estiva ID and the relay (see \\`.env.example\\`).\n2. \\`KINDS\\` in \\`src/relay/client.ts\\`: the event kinds ${title} reads. Until then\n it is connected and reads nothing.\n3. The product: its pages, and the fold that turns what arrives on the\n connection into the app's state.\n\nBefore ${title} can sign in on the real Estiva ID it has to be registered there\nas its own app, with:\n\n- its exact redirect address\n- every event kind it will sign, **including 22242**, the relay's sign-in handshake\n- the relay's address, allowed for that handshake. Estiva ID signs a handshake\n only for a relay both the app and the deployment allow.\n\nAnything left out fails at the very last step: every screen looks right, and\nnothing arrives.\n\n## The checks\n\n| command | what |\n|---|---|\n| \\`npm run typecheck\\` | TypeScript, the app and its settings files |\n| \\`npm run lint\\` | everything: TypeScript's and React's rules, the token contract, the gate |\n| \\`npm run lint:rules\\` | the gate alone, and \\`.gates-count.json\\` \u2014 CI's job \\`gate\\` |\n| \\`npm test\\` | the tests |\n| \\`npm run build\\` | the build |\n| \\`npm run gates:status\\` | which gates are on, read from the code |\n| \\`npm run ui:find <words>\\` | what the package and this app already have for it |\n| \\`npm run registry:check\\` | every part says what it is for \u2014 CI's job \\`gate\\` |\n| \\`npm run storybook\\` | the stories |\n\nThe gates are the package's, imported rather than copied, so a rule written later\narrives with an ordinary version bump. A Claude session started in this folder is\nstopped before it writes code the gate refuses (\\`.claude/settings.json\\`).\n\n## Once it is on GitHub\n\nRequire the check \\`gate\\` before anything merges into \\`main\\`: a ruleset on \\`main\\`\n(Settings \u2192 Rules \u2192 Rulesets) with \"Require status checks to pass\", the check\n\\`gate\\`, and no one allowed to bypass it. Until then \\`npm run gates:status\\` shows\nUIG-6 as not done.\n`,\n 'CLAUDE.md': `# ${title}, for Claude Code\n\n${title} is an Estiva app. Its parts, tokens and gates come from \\`@estiva-app/ui\\`.\n\n**Use the package's parts, never a raw element or a hand-built look.** The gate\nrefuses code that reaches past the package, and each refusal names what to use\ninstead. What it refuses is the package's to say, and grows with it: the\n\\`estiva-ui\\` skill lists it. It runs before you write\n(the hook in \\`.claude/settings.json\\`, for a session started in this folder), in\n\\`npm run lint:rules\\` and in CI. A session started elsewhere: run\n\\`npm run lint:rules\\` after changing \\`src/\\`, and fix what it reports. Keep something\nonly with its reason on the line above, \\`// @estiva-escape: <reason>\\`, never with\n\\`eslint-disable\\`.\n\n**Tokens only.** Colours, type, corners and shadows come from the package's preset.\n\n**One relay connection per tab.** \\`relayClient()\\` in \\`src/relay/client.ts\\` is the\nconnection. Never open a socket and never make a second holder: the relay signs a\nconnection in once, and caps subscriptions per connection.\n\n**Look before you build.** \\`npm run ui:find <what it does>\\` searches the package's\nparts and this app's own. Use what it finds.\n\n**Every part says what it is for.** A new part gets a one-line \\`/** \u2026 */\\` comment\ndirectly above it. \\`npm run registry:check\\` and CI's job \\`gate\\` refuse a part without one.\n\n**The count starts at zero and stays there** (\\`.gates-count.json\\`, \\`docs/GATES-DEBT.md\\`).\n\nWhat each gate is and how it is wired: the package's README,\n\\`node_modules/@estiva-app/ui/README.md\\`.\n`,\n }\n}\n\n/** The versions of what this package does not use itself, from the npm registry. */\nexport function askNpm(names: string[]): Record<string, string> {\n const npm = process.env.npm_execpath\n const out: Record<string, string> = {}\n for (const name of names) {\n const args = ['view', name, 'version']\n const version = (npm ? execFileSync(process.execPath, [npm, ...args]) : execFileSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, { shell: process.platform === 'win32' }))\n .toString()\n .trim()\n if (!/^\\d+\\.\\d+\\.\\d+/.test(version)) throw new Error(`npm did not say which version of ${name} is current: \"${version}\"`)\n out[name] = `^${version}`\n }\n return out\n}\n\n/** Write the app. Refuses a folder that already exists. Returns the folder. */\nexport function createApp(options: CreateAppOptions): string {\n const dir = resolve(options.parent ?? process.cwd(), options.name)\n if (existsSync(dir)) throw new Error(`${dir} already exists: create-estiva-app never writes into a folder that is there`)\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8')) as PackageJson\n const missing = ASKED_OF_NPM.filter((n) => !pkg.devDependencies[n] && !options.versions?.[n])\n const versions = { ...(missing.length ? askNpm(missing) : {}), ...options.versions }\n const files = appFiles({ ...options, versions })\n for (const [rel, text] of Object.entries(files)) {\n const path = join(dir, rel)\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, text)\n }\n return dir\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/registry/app.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * An app's catalogue (UIG-13): every part in an app's own code, what it is for,\n * where it is used, and which of four kinds it is.\n *\n * **Built fresh, never committed** (Katerina, 18 September 2026). The package is\n * public and Peek and Ship are private, so an app's catalogue lives nowhere but\n * in its own code: `estiva-ui find` and `estiva-ui check` build it each time,\n * which takes about a second, and `estiva-ui build` writes it to a file only\n * when asked. A file that is never kept can never drift.\n *\n * **A file is not a part, and a folder says nothing.** Peek's `components/ui`\n * holds one-line pass-ons beside 187-line components, one file can hold six\n * parts, and a part can draw nothing at all (a provider, a listener). So the\n * target set is every name an app's `.tsx` files export that is a part: a\n * function that draws, or a name drawn as a tag somewhere in the app. The kind\n * is read from the code, and only from the code:\n *\n * - **re-export** \u2014 its file only hands a package part on.\n * - **one-off** \u2014 one file of the app uses it.\n * - **reusable** \u2014 two or more do.\n * - **promote-candidate** \u2014 two or more do, and everything it uses is already in\n * the package, so it could move there as it is. Katerina rules on each.\n * - **unused** \u2014 no file of the app uses it; its stories or tests may.\n *\n * Where the count is wrong \u2014 a general part only one screen uses yet \u2014 a person\n * writes the kind beside the part, with the reason, and the catalogue shows it:\n *\n * /**\n * * A timeline of what happened to a thing.\n * * @registry reusable: general; one screen uses it so far\n * *\\/\n *\n * TypeScript is the app's own install, as it is for the package's builder.\n */\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'\nimport { basename, dirname, extname, join, relative, resolve, sep } from 'node:path'\nimport ts from 'typescript'\nimport { firstSentence, pageOpening, readModule, sanitize, toId, PACKAGE_IMPORT, type Resolved } from './build'\nimport { looksOf, parseForLooks } from '../eslint/looks-of'\nimport { SCHEMA_VERSION, type AppFacts, type EntryClass, type FileWithoutPart, type Registry, type RegistryEntry } from './schema'\n\nexport interface AppBuildOptions {\n /** The app's folder: where its `package.json` and `src/` are. Ship's is `web`. */\n root?: string\n /** The name its entries are recorded under \u2014 `peek`, `ship`. Defaults to the app's package name. */\n repo?: string\n /** The package's own catalogue, for the parts an app part shares a name with. Defaults to the one this builder ships beside. */\n packageRegistry?: Registry | null\n /** The packages the package itself depends on. An import of one never ties a part to its app. Defaults to the package's own. */\n packageDependencies?: string[]\n}\n\n/** The kinds a person may write beside a part. A pass-on is a fact about its file; so is being used nowhere. */\nconst WRITABLE: EntryClass[] = ['reusable', 'one-off', 'promote-candidate']\nconst WRITTEN = /^@registry\\s+([a-z-]+)\\s*:\\s*(.+)$/m\n\nconst isStory = (file: string) => /\\.stories\\.[cm]?[jt]sx?$/.test(file)\nconst isTest = (file: string) => /\\.(test|spec)\\.[cm]?[jt]sx?$/.test(file) || /(^|\\/)__(tests|mocks)__\\//.test(file)\nconst isPascal = (name: string) => /^[A-Z][A-Za-z0-9]*$/.test(name) && !/^[A-Z0-9_]+$/.test(name)\n/**\n * What a dynamic `import('./x')` takes from its file. Written as\n * `import('./Settings').then((m) => ({ default: m.SettingsPage }))`, only the names\n * read off the module; otherwise the whole module (`*`), whose default a caller\n * then takes.\n */\nfunction pickedFrom(call: ts.CallExpression): { imported: string; local: string }[] {\n const then = call.parent\n const invoked = then?.parent\n if (then && ts.isPropertyAccessExpression(then) && then.name.text === 'then' && invoked && ts.isCallExpression(invoked)) {\n const callback = invoked.arguments[0]\n const param = callback && (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback)) ? callback.parameters[0]?.name : undefined\n if (param && ts.isIdentifier(param)) {\n const names = new Set<string>()\n const visit = (n: ts.Node) => {\n if (ts.isPropertyAccessExpression(n) && ts.isIdentifier(n.expression) && n.expression.text === param.text) names.add(n.name.text)\n ts.forEachChild(n, visit)\n }\n visit(callback)\n if (names.size) return [...names].map((imported) => ({ imported, local: '(dynamic)' }))\n }\n }\n return [{ imported: '*', local: '(dynamic)' }]\n}\n\n/**\n * `export default memo(TopicRow)`: the default is TopicRow, wrapped. A call on\n * a name, with no function written into it, hands that name out, so the export\n * is read as if it named it \u2014 one part, whose comment and props are its own.\n */\nfunction unwrapDefault<T extends { isDefault: boolean; local: string | null; node: ts.Node | null }>(ex: T): T {\n if (!ex.isDefault || ex.local || !ex.node || !ts.isExpression(ex.node)) return ex\n const name = handedOut(ex.node)\n return name ? { ...ex, local: name, node: null } : ex\n}\n\n/**\n * The name an `export default \u2026` hands out, under whatever it is wrapped in:\n * `memo(forwardRef(Row))`, `Row as ComponentType`, `Row satisfies FC`,\n * `styled(Row)\\`\u2026\\``. `null` when what is exported is written right there.\n */\nfunction handedOut(expression: ts.Expression): string | null {\n let e: ts.Expression = expression\n for (;;) {\n if (ts.isIdentifier(e)) return e.text\n if (ts.isParenthesizedExpression(e) || ts.isAsExpression(e) || ts.isSatisfiesExpression(e) || ts.isNonNullExpression(e) || ts.isTypeAssertionExpression(e)) e = e.expression\n else if (ts.isTaggedTemplateExpression(e)) e = e.tag\n else if (ts.isCallExpression(e) && e.arguments[0] && !e.arguments.some((a) => ts.isArrowFunction(a) || ts.isFunctionExpression(a))) e = e.arguments[0]\n else return null\n }\n}\n\n/** `memo-page` \u2192 `MemoPage`, `topic_view` \u2192 `TopicView`. */\nconst pascalOf = (stem: string) => stem.split(/[^A-Za-z0-9]+/).filter(Boolean).map((word) => word[0].toUpperCase() + word.slice(1)).join('')\n/** A default with no name of its own takes its file's \u2014 or, for `settings/index.tsx`, its folder's. */\nconst defaultNameOf = (file: string) => {\n const stem = basename(file, extname(file))\n return pascalOf(stem === 'index' ? basename(dirname(file)) : stem)\n}\nconst CODE = ['.tsx', '.ts', '.jsx', '.js', '.mts', '.cts']\n\n/** `@scope/name/sub` \u2192 `@scope/name`; `name/sub` \u2192 `name`. */\nfunction packageOf(specifier: string): string {\n const parts = specifier.split('/')\n return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]\n}\n\n/** What the package this builder belongs to depends on, and its catalogue. Read from beside the builder. */\nfunction packageSelf(): { dependencies: string[]; registry: Registry | null } {\n const at = (rel: string) => new URL(rel, import.meta.url)\n const manifest = JSON.parse(readFileSync(at('../../package.json'), 'utf8')) as { name: string; dependencies?: Record<string, string>; peerDependencies?: Record<string, string> }\n const registryUrl = at('../../registry.json')\n const registry = existsSync(registryUrl) ? (JSON.parse(readFileSync(registryUrl, 'utf8')) as Registry) : null\n return { dependencies: [manifest.name, ...Object.keys(manifest.dependencies ?? {}), ...Object.keys(manifest.peerDependencies ?? {})], registry }\n}\n\n/**\n * The import aliases an app's `tsconfig*.json` files declare \u2014 `@/*` \u2192\n * `<root>/src/*` \u2014 and the `baseUrl` folders a bare `src/Card` is read from.\n */\nfunction readAliases(root: string): { aliases: { prefix: string; target: string }[]; bases: string[] } {\n const aliases: { prefix: string; target: string }[] = []\n const bases: string[] = []\n for (const name of readdirSync(root).filter((file) => /^tsconfig.*\\.json$/.test(file)).sort()) {\n const read = ts.readConfigFile(join(root, name), ts.sys.readFile)\n const options = (read.config?.compilerOptions ?? {}) as { paths?: Record<string, string[]>; baseUrl?: string }\n const base = resolve(root, options.baseUrl ?? '.')\n if (options.baseUrl !== undefined && !bases.includes(base)) bases.push(base)\n for (const [pattern, targets] of Object.entries(options.paths ?? {})) {\n if (!pattern.endsWith('/*') || !targets[0]?.endsWith('/*')) continue\n const prefix = pattern.slice(0, -1)\n if (!aliases.some((alias) => alias.prefix === prefix)) aliases.push({ prefix, target: resolve(base, targets[0].slice(0, -2)) })\n }\n }\n return { aliases, bases }\n}\n\nfunction walk(dir: string): string[] {\n return readdirSync(dir).flatMap((name) => {\n if (name === 'node_modules' || name.startsWith('.')) return []\n const path = join(dir, name)\n return statSync(path).isDirectory() ? walk(path) : [path]\n })\n}\n\n/** One import, re-export or dynamic import of a file, resolved. */\ninterface Link {\n specifier: string\n /** An app code file, relative to the app; `null` for a package; `'?'` for anything else. */\n target: string | null\n /** `imported` name \u2192 local name; `*` for a namespace or a dynamic import. Type-only names are left out. */\n names: { imported: string; local: string }[]\n typeOnly: boolean\n kind: 'import' | 're-export' | 'dynamic'\n}\n\n/** A part a file exports, before anything is known about its use. */\ninterface Found {\n file: string\n /** The name it is exported as; a default export takes its own name, or the file's. */\n name: string\n /** The name it has inside its file, where it has one. */\n local: string | null\n defaultExport: boolean\n /** A pass-on: the package and the name it hands on. */\n from: { specifier: string; name: string } | null\n}\n\n/** Everything one file says, read once. */\ninterface FileFacts {\n file: string\n source: string\n sf: ts.SourceFile\n links: Link[]\n /** `exported name` \u2192 local name, and whether it is the default. */\n exports: { name: string; local: string | null; isDefault: boolean; node: ts.Node | null }[]\n /** Pass-ons and barrel entries: `export { A } from '\u2026'`. */\n reexports: { name: string; source: string; specifier: string; target: string | null }[]\n starFrom: { specifier: string; target: string | null }[]\n /** `export * as Parts from './parts'`: a name that stands for another file's parts. */\n namespaces: { name: string; target: string | null }[]\n /** Every name drawn as a JSX tag in this file. */\n tags: Set<string>\n /** Each imported value name \u2192 where it came from, for `import { X } from '\u2026'` then `export { X }`. */\n bindings: Map<string, { specifier: string; imported: string; target: string | null }>\n}\n\n/** Whether a node draws: JSX, or `createElement` / `createPortal`. */\nfunction draws(node: ts.Node): boolean {\n let found = false\n const visit = (n: ts.Node) => {\n if (found) return\n if (ts.isJsxElement(n) || ts.isJsxSelfClosingElement(n) || ts.isJsxFragment(n)) {\n found = true\n return\n }\n if (ts.isCallExpression(n)) {\n const callee = ts.isPropertyAccessExpression(n.expression) ? n.expression.name.text : ts.isIdentifier(n.expression) ? n.expression.text : ''\n if (callee === 'createElement' || callee === 'createPortal') {\n found = true\n return\n }\n }\n ts.forEachChild(n, visit)\n }\n visit(node)\n return found\n}\n\n/**\n * Whether a file names `local` anywhere but in `home` (its own declaration), its\n * imports and its export lines \u2014 as a tag, a value, or passed along. A property\n * name, an attribute name and a type are not uses of the part.\n */\nfunction mentions(sf: ts.SourceFile, local: string, home: ts.Node | null): boolean {\n let named = false\n const visit = (n: ts.Node) => {\n if (named) return\n if (ts.isIdentifier(n) && n.text === local) {\n const parent = n.parent\n // The name of something else that happens to be spelled the same \u2014 a\n // property of a type or a class, a member, another declaration \u2014 is not the part.\n const declares = (ts.isPropertySignature(parent) || ts.isPropertyDeclaration(parent) || ts.isMethodDeclaration(parent) || ts.isMethodSignature(parent) || ts.isGetAccessorDeclaration(parent) || ts.isSetAccessorDeclaration(parent) || ts.isEnumMember(parent) || ts.isVariableDeclaration(parent) || ts.isFunctionDeclaration(parent) || ts.isClassDeclaration(parent) || ts.isParameter(parent) || ts.isBindingElement(parent)) && (parent as { name?: ts.Node }).name === n\n const bindingKey = ts.isBindingElement(parent) && parent.propertyName === n\n const isName = declares || bindingKey || (ts.isPropertyAccessExpression(parent) && parent.name === n) || (ts.isPropertyAssignment(parent) && parent.name === n) || ts.isJsxAttribute(parent) || ts.isExportSpecifier(parent) || ts.isImportSpecifier(parent) || ts.isJsxClosingElement(parent)\n // `SlashMenu.displayName = 'SlashMenu'` sets something on the part; it does not use it.\n const configures = ts.isPropertyAccessExpression(parent) && parent.expression === n && ts.isBinaryExpression(parent.parent) && parent.parent.left === parent && parent.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken\n if (configures) {\n ts.forEachChild(n, visit)\n return\n }\n let inType = false\n for (let a: ts.Node | undefined = parent; a && a !== sf; a = a.parent) if (ts.isTypeNode(a)) inType = true\n if (!isName && !inType && !shadowed(n, local, sf)) named = true\n }\n ts.forEachChild(n, visit)\n }\n for (const s of sf.statements) {\n if (s === home || ts.isExportDeclaration(s) || ts.isImportDeclaration(s)) continue\n // `export default Row`, `memo(forwardRef(Row))`, `Row as ComponentType` hand the\n // part out; they do not use it. Anything else written into the default is code.\n if (ts.isExportAssignment(s) && handedOut(s.expression)) continue\n visit(s)\n }\n return named\n}\n\n/** Whether a binding name \u2014 `Icon`, `{ icon: Icon }`, `[Icon]` \u2014 binds `local`. */\nfunction binds(name: ts.BindingName | undefined, local: string): boolean {\n if (!name) return false\n if (ts.isIdentifier(name)) return name.text === local\n return name.elements.some((element) => !ts.isOmittedExpression(element) && binds(element.name, local))\n}\n\n/**\n * Whether `n` means something declared closer than the file: a parameter\n * (`Item({ icon: Icon }) { return <Icon /> }`), or a constant, function or class\n * of an enclosing block. That `Icon` is not the file's part called Icon.\n */\nfunction shadowed(n: ts.Node, local: string, sf: ts.SourceFile): boolean {\n for (let a: ts.Node | undefined = n.parent; a && a !== sf; a = a.parent) {\n if (ts.isFunctionLike(a)) {\n if (a.parameters.some((p) => binds(p.name, local))) return true\n if ((ts.isFunctionExpression(a) || ts.isClassExpression(a)) && a.name?.text === local) return true\n }\n if (ts.isCatchClause(a) && binds(a.variableDeclaration?.name, local)) return true\n if (ts.isBlock(a) || ts.isModuleBlock(a) || ts.isCaseClause(a) || ts.isDefaultClause(a)) {\n for (const s of a.statements) {\n if (ts.isVariableStatement(s) && s.declarationList.declarations.some((d) => binds(d.name, local))) return true\n if ((ts.isFunctionDeclaration(s) || ts.isClassDeclaration(s)) && s.name?.text === local) return true\n }\n }\n if ((ts.isForStatement(a) || ts.isForOfStatement(a) || ts.isForInStatement(a)) && a.initializer && ts.isVariableDeclarationList(a.initializer) && a.initializer.declarations.some((d) => binds(d.name, local))) return true\n }\n return false\n}\n\n/**\n * The thing a declaration holds, if it is a function, a class, or a call that\n * wraps one (`forwardRef`, `memo`) \u2014 as a constant, or written straight into\n * `export default \u2026`.\n */\nfunction drawingBody(node: ts.Node): ts.Node | null {\n if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) return node\n const init = ts.isVariableDeclaration(node) ? node.initializer : ts.isExpression(node) ? node : undefined\n if (!init) return null\n if (ts.isArrowFunction(init) || ts.isFunctionExpression(init) || ts.isClassExpression(init)) return init\n if (ts.isCallExpression(init)) {\n const callee = init.expression.getText()\n if (/(^|\\.)createContext$/.test(callee)) return null\n if (init.arguments.some((a) => ts.isArrowFunction(a) || ts.isFunctionExpression(a))) return init\n }\n return null\n}\n\n/**\n * Read one app and return its catalogue.\n *\n * Throws on anything it cannot explain, with every problem at once: a part with\n * no purpose, a written kind it does not know. A catalogue that quietly drops\n * what it could not read would answer \"we have nothing for that\" when the\n * truth is \"the builder gave up\".\n */\nexport function buildAppRegistry({ root = process.cwd(), repo, packageRegistry, packageDependencies }: AppBuildOptions = {}): Registry {\n const app = resolve(root)\n const src = join(app, 'src')\n if (!existsSync(src)) throw new Error(`${app} has no src/ folder: estiva-ui reads an app's parts from src/`)\n const manifest = JSON.parse(readFileSync(join(app, 'package.json'), 'utf8')) as { name?: string; version?: string; scripts?: Record<string, string> }\n const self = packageRegistry === undefined || packageDependencies === undefined ? packageSelf() : null\n const theRegistry = packageRegistry === undefined ? (self?.registry ?? null) : packageRegistry\n const allowed = new Set(packageDependencies ?? self?.dependencies ?? [])\n const repoName = repo ?? manifest.name ?? basename(app)\n const { aliases, bases } = readAliases(app)\n const rel = (abs: string) => relative(app, abs).split(sep).join('/')\n\n const files = walk(src)\n .filter((path) => CODE.includes(extname(path)) && !path.endsWith('.d.ts'))\n .map(rel)\n .sort()\n const known = new Set(files)\n\n /** Where an import points: an app code file, a package (`null`), or something else (`'?'`). */\n const resolveSpecifier = (from: string, specifier: string): string | null => {\n let base: string | null = null\n if (specifier.startsWith('.')) base = resolve(app, dirname(from), specifier)\n else {\n const alias = aliases.find((a) => specifier.startsWith(a.prefix))\n if (alias) base = join(alias.target, specifier.slice(alias.prefix.length))\n }\n const lookUp = (at: string): string | null => {\n const stem = at.replace(/\\.(js|jsx)$/, '')\n for (const candidate of [at, ...CODE.map((e) => stem + e), ...CODE.map((e) => join(at, `index${e}`))]) {\n const r = rel(candidate)\n if (known.has(r)) return r\n }\n return null\n }\n if (base === null) {\n // A bare `src/Card` is the app's own when its tsconfig's baseUrl says so; otherwise a package.\n for (const folder of bases) {\n const hit = lookUp(join(folder, specifier))\n if (hit) return hit\n }\n return null\n }\n return lookUp(base) ?? '?'\n }\n\n // \u2500\u2500 Read every file once \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const facts = new Map<string, FileFacts>()\n for (const file of files) {\n const source = readFileSync(join(app, file), 'utf8')\n const sf = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, /x$/.test(file) ? ts.ScriptKind.TSX : ts.ScriptKind.TS)\n const links: Link[] = []\n const exports: FileFacts['exports'] = []\n const reexports: FileFacts['reexports'] = []\n const starFrom: FileFacts['starFrom'] = []\n const namespaces: FileFacts['namespaces'] = []\n for (const statement of sf.statements) {\n if (ts.isImportDeclaration(statement)) {\n if (!ts.isStringLiteral(statement.moduleSpecifier)) continue\n const specifier = statement.moduleSpecifier.text\n const clause = statement.importClause\n const names: Link['names'] = []\n if (clause && !clause.isTypeOnly) {\n if (clause.name) names.push({ imported: 'default', local: clause.name.text })\n const bindings = clause.namedBindings\n if (bindings && ts.isNamedImports(bindings)) for (const e of bindings.elements) if (!e.isTypeOnly) names.push({ imported: (e.propertyName ?? e.name).text, local: e.name.text })\n if (bindings && ts.isNamespaceImport(bindings)) names.push({ imported: '*', local: bindings.name.text })\n }\n links.push({ specifier, target: resolveSpecifier(file, specifier), names, typeOnly: !!clause?.isTypeOnly || (clause !== undefined && names.length === 0 && !!clause.namedBindings), kind: 'import' })\n continue\n }\n if (ts.isExportDeclaration(statement)) {\n const specifier = statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : null\n if (specifier === null) {\n if (!statement.isTypeOnly && statement.exportClause && ts.isNamedExports(statement.exportClause)) {\n // `export { Page as default }` is the file's default, named Page.\n for (const e of statement.exportClause.elements) if (!e.isTypeOnly) exports.push({ name: e.name.text, local: (e.propertyName ?? e.name).text, isDefault: e.name.text === 'default', node: null })\n }\n continue\n }\n const target = resolveSpecifier(file, specifier)\n links.push({ specifier, target, names: [], typeOnly: statement.isTypeOnly, kind: 're-export' })\n if (statement.isTypeOnly) continue\n if (!statement.exportClause) starFrom.push({ specifier, target })\n else if (ts.isNamedExports(statement.exportClause)) {\n for (const e of statement.exportClause.elements) if (!e.isTypeOnly) reexports.push({ name: e.name.text, source: (e.propertyName ?? e.name).text, specifier, target })\n } else if (ts.isNamespaceExport(statement.exportClause)) {\n // `export * as Parts from './parts'`: a caller's `Parts.Card` is the parts file's Card.\n namespaces.push({ name: statement.exportClause.name.text, target })\n }\n continue\n }\n if (ts.isExportAssignment(statement) && !statement.isExportEquals) {\n const e = statement.expression\n exports.push({ name: 'default', local: ts.isIdentifier(e) ? e.text : null, isDefault: true, node: ts.isIdentifier(e) ? null : e })\n continue\n }\n const mods = ts.canHaveModifiers(statement) ? (ts.getModifiers(statement) ?? []) : []\n if (!mods.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) continue\n const isDefault = mods.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)\n // An overload's signature is not a part of its own: the part is the one with a body.\n if (ts.isFunctionDeclaration(statement) && !statement.body) continue\n if (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) {\n exports.push({ name: isDefault ? 'default' : (statement.name?.text ?? 'default'), local: statement.name?.text ?? null, isDefault, node: statement })\n } else if (ts.isVariableStatement(statement)) {\n for (const d of statement.declarationList.declarations) if (ts.isIdentifier(d.name)) exports.push({ name: d.name.text, local: d.name.text, isDefault: false, node: d })\n }\n }\n const tags = new Set<string>()\n const visit = (n: ts.Node) => {\n if (ts.isCallExpression(n) && n.expression.kind === ts.SyntaxKind.ImportKeyword && n.arguments[0] && ts.isStringLiteralLike(n.arguments[0])) {\n const specifier = n.arguments[0].text\n links.push({ specifier, target: resolveSpecifier(file, specifier), names: pickedFrom(n), typeOnly: false, kind: 'dynamic' })\n }\n if ((ts.isJsxOpeningElement(n) || ts.isJsxSelfClosingElement(n)) && ts.isIdentifier(n.tagName)) tags.add(n.tagName.text)\n ts.forEachChild(n, visit)\n }\n visit(sf)\n const bindings: FileFacts['bindings'] = new Map()\n for (const link of links) if (link.kind === 'import') for (const n of link.names) if (n.imported !== '*') bindings.set(n.local, { specifier: link.specifier, imported: n.imported, target: link.target })\n facts.set(file, { file, source, sf, links, exports, reexports, starFrom, namespaces, tags, bindings })\n }\n\n const drawnAnywhere = new Set<string>()\n for (const f of facts.values()) for (const tag of f.tags) drawnAnywhere.add(tag)\n\n /** A top-level declaration of a file, by its local name. */\n const declarationOf = (f: FileFacts, local: string): ts.Node | null => {\n for (const s of f.sf.statements) {\n // Past an overload's signature, to the function with a body.\n if (ts.isFunctionDeclaration(s) && s.name?.text === local && !s.body) continue\n if ((ts.isFunctionDeclaration(s) || ts.isClassDeclaration(s)) && s.name?.text === local) return s\n if (ts.isVariableStatement(s)) for (const d of s.declarationList.declarations) if (ts.isIdentifier(d.name) && d.name.text === local) return d\n }\n return null\n }\n\n // \u2500\u2500 The parts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const problems: string[] = []\n const found: Found[] = []\n const targets = files.filter((file) => file.endsWith('.tsx') && !isStory(file) && !isTest(file))\n for (const file of targets) {\n const f = facts.get(file)!\n for (const written of f.exports) {\n const ex = unwrapDefault(written)\n // `export default memo(Avatar)` with Avatar imported: not Avatar handed on, but a\n // new part of this file that wraps it, named after the file.\n // Only a name that is a part's: `createBrowserRouter(routes)` hands out no part.\n if (ex !== written && ex.local && isPascal(ex.local) && !declarationOf(f, ex.local) && f.bindings.has(ex.local)) {\n found.push({ file, name: defaultNameOf(file), local: null, defaultExport: true, from: null })\n continue\n }\n // `export function App` and `export default App` \u2014 or `export default memo(App)`: one part, not two.\n if (ex.isDefault && ex.local && f.exports.some((other) => !other.isDefault && other.local === ex.local)) continue\n const node = ex.node ?? (ex.local ? declarationOf(f, ex.local) : null)\n // A default with no name of its own takes its file's, as a name: `memo-page.tsx` \u2192 MemoPage.\n const own = ex.local ?? (ex.isDefault ? defaultNameOf(file) : ex.name)\n const name = ex.isDefault ? own : ex.name\n if (!isPascal(name)) continue\n // `import { SkeletonBar } from '@estiva-app/ui'` and later `export { SkeletonBar }`:\n // handed on, exactly as `export { SkeletonBar } from '@estiva-app/ui'` is.\n // From another file of the app it is a barrel, and the part is listed there.\n const bound = node === null && ex.local ? f.bindings.get(ex.local) : undefined\n if (bound) {\n if (bound.target === null) found.push({ file, name, local: null, defaultExport: ex.isDefault, from: { specifier: bound.specifier, name: bound.imported } })\n continue\n }\n // `export const SkeletonSidebarList = SkeletonList`, SkeletonList imported: the\n // package's part under a second name, handed on. From an app file, a barrel.\n const alias = node && ts.isVariableDeclaration(node) && node.initializer && ts.isIdentifier(node.initializer) ? f.bindings.get(node.initializer.text) : undefined\n if (alias) {\n if (alias.target === null) found.push({ file, name, local: null, defaultExport: ex.isDefault, from: { specifier: alias.specifier, name: alias.imported } })\n continue\n }\n const body = node ? drawingBody(node) : null\n const isPart = (body !== null && draws(body)) || drawnAnywhere.has(ex.local ?? name)\n if (isPart) found.push({ file, name, local: ex.local, defaultExport: ex.isDefault, from: null })\n }\n for (const re of f.reexports) {\n // Handing on another file of the app is a barrel: the part is listed where it is written.\n if (re.target !== null || !isPascal(re.name)) continue\n found.push({ file, name: re.name, local: null, defaultExport: false, from: { specifier: re.specifier, name: re.source } })\n }\n for (const star of f.starFrom) {\n if (star.target === null) problems.push(`${file} hands on everything from '${star.specifier}' with export *: name the parts it hands on, so the catalogue can list them`)\n }\n }\n\n // \u2500\u2500 Where each is used \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const key = (file: string, name: string) => `${file}#${name}`\n const byKey = new Map(found.map((p) => [key(p.file, p.name), p]))\n const partsIn = (file: string) => found.filter((p) => p.file === file)\n /** The part a file's default export is, however it was written. */\n const defaultPartOf = (file: string): Found | null => {\n const written = facts.get(file)?.exports.find((e) => e.isDefault)\n if (!written) return null\n const d = unwrapDefault(written)\n return partsIn(file).find((p) => p.defaultExport || (d.local !== null && p.local === d.local)) ?? null\n }\n\n /** What `name`, imported from `file`, really is: follows barrels to the file that writes it. */\n const origin = (file: string, name: string, seen = new Set<string>()): Found | null => {\n if (seen.has(key(file, name))) return null\n seen.add(key(file, name))\n const direct = byKey.get(key(file, name))\n if (direct) return direct\n const f = facts.get(file)\n if (!f) return null\n // `export default Foo` elsewhere in the file: the default is Foo's part.\n if (name === 'default') {\n const d = defaultPartOf(file)\n if (d) return d\n }\n const re = f.reexports.find((r) => r.name === name)\n if (re && re.target && re.target !== '?') return origin(re.target, re.source, seen)\n // `import { X } from './X'` and `export { X }`: a barrel written in two lines.\n const ex = f.exports.find((e) => (name === 'default' ? e.isDefault : e.name === name && !e.isDefault))\n const renamed = ex?.node && ts.isVariableDeclaration(ex.node) && ex.node.initializer && ts.isIdentifier(ex.node.initializer) ? f.bindings.get(ex.node.initializer.text) : undefined\n const bound = (ex?.local && !ex.node && !declarationOf(f, ex.local) ? f.bindings.get(ex.local) : undefined) ?? renamed\n if (bound?.target && bound.target !== '?') return origin(bound.target, bound.imported, seen)\n for (const star of f.starFrom) if (star.target && star.target !== '?') {\n const hit = origin(star.target, name, seen)\n if (hit) return hit\n }\n return null\n }\n /** Every part a file hands out, through its barrels too. */\n const everyPartOf = (file: string, seen = new Set<string>()): Found[] => {\n if (seen.has(file)) return []\n seen.add(file)\n const f = facts.get(file)\n if (!f) return []\n const out = [...partsIn(file)]\n for (const re of f.reexports) if (re.target && re.target !== '?') { const o = origin(re.target, re.source); if (o) out.push(o) }\n for (const star of f.starFrom) if (star.target && star.target !== '?') out.push(...everyPartOf(star.target, seen))\n for (const ns of f.namespaces) if (ns.target && ns.target !== '?') out.push(...everyPartOf(ns.target, seen))\n return out\n }\n /** The parts a namespace export stands for, followed through barrels. */\n const namespaceParts = (file: string, name: string, seen = new Set<string>()): Found[] => {\n if (seen.has(`${file}#${name}`)) return []\n seen.add(`${file}#${name}`)\n const f = facts.get(file)\n if (!f) return []\n const ns = f.namespaces.find((x) => x.name === name)\n if (ns) return ns.target && ns.target !== '?' ? everyPartOf(ns.target) : []\n const re = f.reexports.find((r) => r.name === name)\n if (re?.target && re.target !== '?') return namespaceParts(re.target, re.source, seen)\n return f.starFrom.flatMap((star) => (star.target && star.target !== '?' ? namespaceParts(star.target, name, seen) : []))\n }\n\n const users = new Map<string, { app: Set<string>; stories: Set<string>; tests: Set<string> }>()\n for (const p of found) users.set(key(p.file, p.name), { app: new Set(), stories: new Set(), tests: new Set() })\n for (const f of facts.values()) {\n const bucket = (p: Found) => {\n const u = users.get(key(p.file, p.name))!\n return isStory(f.file) ? u.stories : isTest(f.file) ? u.tests : u.app\n }\n for (const link of f.links) {\n if (link.kind === 're-export' || link.typeOnly || !link.target || link.target === '?') continue\n for (const n of link.names) {\n if (n.imported === '*') {\n // `lazy(() => import('./Page'))` loads the default; a namespace may use any.\n const theDefault = link.kind === 'dynamic' ? defaultPartOf(link.target) : null\n const chosen = theDefault ? [theDefault] : everyPartOf(link.target)\n for (const p of chosen) if (p.file !== f.file) bucket(p).add(f.file)\n continue\n }\n const p = origin(link.target, n.imported)\n // A file that imports a part only to export it again hands it on; it does not use it.\n const handsOn = f.exports.some((e) => e.local === n.local && e.node === null) && !mentions(f.sf, n.local, null)\n if (handsOn) continue\n if (p) {\n if (p.file !== f.file) bucket(p).add(f.file)\n continue\n }\n // `import { Parts } from './barrel'`, where the barrel says `export * as Parts from './parts'`.\n for (const part of namespaceParts(link.target, n.imported)) if (part.file !== f.file) bucket(part).add(f.file)\n }\n }\n }\n // Its own file uses it when something there, other than its own declaration and\n // the export line, names it \u2014 a card drawn by the list beside it.\n for (const p of found) {\n if (p.from || !p.local) continue\n const f = facts.get(p.file)!\n const decl = declarationOf(f, p.local)\n const home = decl ? (ts.isVariableDeclaration(decl) ? decl.parent.parent : decl) : null\n if (mentions(f.sf, p.local, home)) users.get(key(p.file, p.name))!.app.add(p.file)\n }\n\n // \u2500\u2500 What ties each file to the app \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // A file is tied when it imports a package the package does not itself depend\n // on, something that is not code, or an app file that is tied. Types count: a\n // part typed by Peek's data is Peek's.\n const directTies = new Map<string, string[]>()\n const appDeps = new Map<string, string[]>()\n for (const f of facts.values()) {\n const ties: string[] = []\n const deps: string[] = []\n for (const link of f.links) {\n if (link.target === null) {\n const tie = link.specifier.startsWith('node:') ? link.specifier : allowed.has(packageOf(link.specifier)) ? null : packageOf(link.specifier)\n if (tie && !ties.includes(tie)) ties.push(tie)\n } else if (link.target === '?') {\n if (!ties.includes(link.specifier)) ties.push(link.specifier)\n } else if (link.target !== f.file && !deps.includes(link.target)) deps.push(link.target)\n }\n directTies.set(f.file, ties)\n appDeps.set(f.file, deps)\n }\n const tied = new Set([...directTies].filter(([, t]) => t.length > 0).map(([file]) => file))\n for (let changed = true; changed; ) {\n changed = false\n for (const [file, deps] of appDeps) if (!tied.has(file) && deps.some((d) => tied.has(d))) (tied.add(file), (changed = true))\n }\n const tiesOf = (file: string) => [...directTies.get(file)!, ...appDeps.get(file)!.filter((d) => tied.has(d))].sort()\n\n // \u2500\u2500 Stories: which one shows a part \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const stories = files.filter(isStory).map((file) => {\n const f = facts.get(file)!\n let title: string | null = null\n let component: string | null = null\n let noDocs = false\n const names: string[] = []\n for (const s of f.sf.statements) {\n if (!ts.isVariableStatement(s)) continue\n const exported = (ts.getModifiers(s) ?? []).some((m) => m.kind === ts.SyntaxKind.ExportKeyword)\n for (const d of s.declarationList.declarations) {\n if (!ts.isIdentifier(d.name)) continue\n if (d.name.text === 'meta') {\n let init = d.initializer\n while (init && (ts.isSatisfiesExpression(init) || ts.isAsExpression(init) || ts.isParenthesizedExpression(init))) init = init.expression\n if (init && ts.isObjectLiteralExpression(init)) {\n for (const prop of init.properties) {\n if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue\n if (prop.name.text === 'title' && ts.isStringLiteralLike(prop.initializer)) title = prop.initializer.text\n if (prop.name.text === 'component' && ts.isIdentifier(prop.initializer)) component = prop.initializer.text\n if (prop.name.text === 'tags' && ts.isArrayLiteralExpression(prop.initializer)) noDocs = prop.initializer.elements.some((e) => ts.isStringLiteralLike(e) && e.text === '!autodocs')\n }\n }\n } else if (exported) names.push(d.name.text)\n }\n }\n // Which part each local name is, through the imports.\n const imported = new Map<string, Found>()\n for (const link of f.links) {\n if (!link.target || link.target === '?') continue\n for (const n of link.names) {\n if (n.imported === '*') continue\n const p = origin(link.target, n.imported)\n if (p) imported.set(n.local, p)\n }\n }\n return { file, title, component: component ? (imported.get(component) ?? null) : null, noDocs, names, imports: [...imported.values()] }\n })\n const storyOf = (p: Found) => {\n const stem = (file: string) => basename(file).replace(/\\.stories\\.[cm]?[jt]sx?$/, '')\n const own = basename(p.file, extname(p.file))\n // A story file named after the part draws it even when its stories are of a\n // presentational half in the same file \u2014 `FilesPanel.stories.tsx` drawing\n // `FilesPanelView` is FilesPanel's picture, and its page attaches there (UIG-19).\n const sameFile = (s: (typeof stories)[number]) => s.imports.some((q) => q.file === p.file)\n const candidates = stories.filter((s) => s.title && s.names.length && (s.component === p || (s.imports.includes(p) && (stem(s.file) === p.name || stem(s.file) === own)) || (stem(s.file) === p.name && sameFile(s))))\n candidates.sort((a, b) => Number(b.component === p) - Number(a.component === p) || a.file.localeCompare(b.file))\n const s = candidates[0]\n if (!s || !s.title) return { docsId: null, storyId: null }\n return { docsId: s.noDocs ? null : `${sanitize(s.title)}--docs`, storyId: toId(s.title, s.names.includes(p.name) ? p.name : s.names[0]) }\n }\n\n // \u2500\u2500 Props, read the way the package's are \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n type Module = ReturnType<typeof readModule>\n const modules = new Map<string, Module>()\n const moduleAt = (file: string): Module => {\n const held = modules.get(file)\n if (held) return held\n const read = readModule(facts.get(file)!.source, (specifier, typeName): Resolved | undefined => {\n // A wrapper typed by the package's own props takes what the package's part\n // takes. The package names every part's props type after the part \u2014\n // `SearchInputProps` is `SearchInput`'s, all 65 of them \u2014 so its entry answers.\n if (specifier === PACKAGE_IMPORT) {\n const part = typeName.endsWith('Props') ? theRegistry?.entries.find((entry) => entry.name === typeName.slice(0, -'Props'.length)) : undefined\n return part ? { props: part.props, variants: part.variants } : undefined\n }\n const target = resolveSpecifier(file, specifier)\n return target && target !== '?' ? moduleAt(target).resolveName(typeName) : undefined\n // A comment directly above a part is that part's, first in its file or not.\n }, { lendFirstComment: false })\n modules.set(file, read)\n return read\n }\n\n // \u2500\u2500 The entries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const namesakes = new Set((theRegistry?.entries ?? []).map((e) => e.name))\n const importPathOf = (file: string) => {\n const abs = join(app, file)\n const alias = aliases.find((a) => abs.startsWith(a.target + sep))\n const stem = file.replace(/\\.[cm]?[jt]sx?$/, '').replace(/\\/index$/, '')\n return alias ? `${alias.prefix}${relative(alias.target, join(app, stem)).split(sep).join('/')}` : stem\n }\n const port = /(?:-p|--port)[\\s=]+(\\d+)/.exec(manifest.scripts?.storybook ?? '')?.[1] ?? '6006'\n\n const entries: RegistryEntry[] = []\n for (const p of found) {\n const u = users.get(key(p.file, p.name))!\n const usedIn = [...u.app].sort()\n const pass = p.from\n const onePart = partsIn(p.file).filter((q) => !q.from).length === 1\n const module = pass ? null : moduleAt(p.file)\n // A default written as an expression has no name in its file: the builder keeps it as `default`.\n const declared = pass ? undefined : module!.declarations.get(p.local ?? 'default')\n const header = module?.headerDoc ?? ''\n // The file's default may carry its comment on `export default memo(Row)` rather than on Row.\n const doc = declared?.doc || (defaultPartOf(p.file) === p ? (module?.declarations.get('default')?.doc ?? '') : '')\n\n // Purpose: its page, else its own comment, else \u2014 for a file of one part \u2014\n // the file's. A pass-on says whose it is.\n const page = pass ? null : join(dirname(p.file), `${p.name}.mdx`).split(sep).join('/')\n const pageText = page && existsSync(join(app, page)) ? pageOpening(readFileSync(join(app, page), 'utf8')) : ''\n let purpose = ''\n let purposeFrom: RegistryEntry['purposeFrom'] = 'comment'\n if (pass) {\n const as = pass.name === p.name ? '' : ` as ${p.name}`\n purpose = pass.specifier === PACKAGE_IMPORT ? `The package's ${pass.name}, handed on${as} so the app can import it from ${importPathOf(p.file)}.` : `${pass.name} from ${pass.specifier}, handed on${as} so the app can import it from ${importPathOf(p.file)}.`\n purposeFrom = 'package'\n } else if (firstSentence(pageText)) {\n purpose = firstSentence(pageText)\n purposeFrom = 'page'\n } else if (firstSentence(doc)) {\n purpose = firstSentence(doc)\n } else if (onePart && firstSentence(header)) {\n purpose = firstSentence(header)\n purposeFrom = 'file'\n }\n if (!purpose) {\n problems.push(`${p.name} in ${p.file} has no one-line description. Write one as a /** \u2026 */ comment directly above it${onePart ? ', or at the top of the file' : ''}.`)\n continue\n }\n\n // Its kind: written beside it, or read from the evidence.\n const writtenLine = WRITTEN.exec(doc) ?? (onePart ? WRITTEN.exec(header) : null)\n const ties = pass ? [] : tiesOf(p.file)\n let cls: EntryClass\n let reason: string\n let written = false\n if (pass) {\n cls = 're-export'\n reason = `Its file only hands on ${pass.specifier === PACKAGE_IMPORT ? `the package's ${pass.name}` : `${pass.name} from ${pass.specifier}`}.`\n if (writtenLine) problems.push(`${p.name} in ${p.file} is a pass-on, so its kind is read from the file: remove \"@registry ${writtenLine[1]}\"`)\n } else if (writtenLine) {\n const said = writtenLine[1] as EntryClass\n if (!WRITABLE.includes(said)) {\n problems.push(`${p.name} in ${p.file} says \"@registry ${writtenLine[1]}\": the kinds a person may write are ${WRITABLE.join(', ')}`)\n continue\n }\n cls = said\n reason = writtenLine[2].trim()\n written = true\n } else if (usedIn.length === 0) {\n cls = 'unused'\n reason = u.stories.size ? 'No file of the app uses it; only its stories do.' : u.tests.size ? 'No file of the app uses it; only its tests do.' : 'Nothing uses it.'\n } else if (usedIn.length === 1) {\n cls = 'one-off'\n reason = `Used in one place: ${usedIn[0]}.`\n } else if (ties.length) {\n cls = 'reusable'\n reason = `Used in ${usedIn.length} places, and tied to the app by ${ties[0]}${ties.length > 1 ? ` and ${ties.length - 1} more` : ''}.`\n } else {\n cls = 'promote-candidate'\n reason = `Used in ${usedIn.length} places, and everything it uses is already in the package.`\n }\n\n const shape: Resolved = declared ? module!.resolve(declared.propsType) : { props: [], variants: [] }\n const facts_: AppFacts = {\n class: cls,\n reason,\n written,\n handsOn: pass ? (pass.specifier === PACKAGE_IMPORT ? pass.name : `${pass.name} from ${pass.specifier}`) : null,\n usedIn,\n tiedTo: ties,\n packageNamesake: namesakes.has(p.name) && !(pass && pass.specifier === PACKAGE_IMPORT && pass.name === p.name) ? p.name : null,\n defaultExport: p.defaultExport,\n }\n const { docsId, storyId } = pass ? { docsId: null, storyId: null } : storyOf(p)\n entries.push({\n name: p.name,\n repo: repoName,\n kind: 'component',\n importPath: importPathOf(p.file),\n sourceFile: p.file,\n purpose,\n purposeFrom,\n props: shape.props,\n variants: shape.variants,\n ownsBehaviours: [],\n status: declared?.deprecated ? 'deprecated' : 'stable',\n migrationStage: null,\n docsId,\n storyId,\n docPage: pageText ? page : null,\n app: facts_,\n // A part handed on from the package looks like the package's (UIG-25).\n looks: pass ? [] : (looksOf(parseForLooks(join(root, p.file), facts.get(p.file)!.source), [p.name]).get(p.name) ?? []),\n })\n }\n\n if (problems.length) throw new Error(`the catalogue of ${repoName} cannot be built:\\n ${problems.join('\\n ')}`)\n\n entries.sort((a, b) => a.name.localeCompare(b.name) || a.sourceFile.localeCompare(b.sourceFile))\n const withParts = new Set(found.map((p) => p.file))\n const filesWithoutParts: FileWithoutPart[] = targets\n .filter((file) => !withParts.has(file))\n .map((file) => {\n const f = facts.get(file)!\n const names = [...f.exports.map((e) => (e.isDefault ? (e.local ?? 'a default') : e.name)), ...f.reexports.map((r) => r.name)]\n return { file, reason: names.length ? `It exports no part, only ${names.join(', ')}.` : 'It exports nothing.' }\n })\n\n return {\n schemaVersion: SCHEMA_VERSION,\n builtFrom: {\n kind: 'app',\n repo: repoName,\n package: manifest.name ?? repoName,\n packageVersion: manifest.version ?? '0.0.0',\n exports: found.length,\n typeExports: 0,\n files: targets.length,\n },\n storybook: {\n docsPath: '/?path=/docs/{docsId}',\n storyPath: '/?path=/story/{storyId}',\n devUrl: `http://localhost:${port}`,\n },\n entries,\n excluded: [],\n filesWithoutParts,\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["bindings", "p", "s"]
7
- }