ras-stack 0.33.0 → 0.35.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.
package/README.md CHANGED
@@ -89,12 +89,12 @@ Start with the narrowest public entrypoint that owns the repeated mechanic:
89
89
 
90
90
  ## Guides 📚
91
91
 
92
- | Guide | What it covers |
93
- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
94
- | [Application primitives](docs/application-primitives.md) | Authentication, request security, databases, realtime clients, email, uploads, and stateful development resources |
95
- | [Repository tooling](docs/repository-tooling.md) | TypeScript and Oxlint configuration, generated policy, fleet checks, GitHub Actions, previews, releases, and production runtime composition |
96
- | [PostHog integration](docs/posthog.md) | Browser/server setup, identity and session correlation, ingest proxying, shutdown, coverage declarations, and source-map responsibility |
97
- | [Full-stack example](docs/full-stack-example.md) | The `workspace:*` integration contract, local development, production container, and two-browser journey |
92
+ | Guide | What it covers |
93
+ | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
94
+ | [Application primitives](docs/application-primitives.md) | Authentication, request security, databases, realtime clients, email, uploads, and stateful development resources |
95
+ | [Repository tooling](docs/repository-tooling.md) | TypeScript and Oxlint configuration, generated policy, GitHub Actions, previews, releases, and production runtime composition |
96
+ | [PostHog integration](docs/posthog.md) | Browser/server setup, identity and session correlation, ingest proxying, shutdown, coverage declarations, and source-map responsibility |
97
+ | [Full-stack example](docs/full-stack-example.md) | The `workspace:*` integration contract, local development, production container, and two-browser journey |
98
98
 
99
99
  ## Development 🛠️
100
100
 
@@ -1,23 +1,11 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { resolve } from 'node:path';
3
- import { fleetConfig, fleetMarkdown, inspectFleet } from './fleet.js';
4
1
  import { checkRepositoryPolicy, syncAdoptionPolicy, syncRepositoryPolicy } from './index.js';
5
2
  export async function runPolicyCli(arguments_) {
6
3
  const [command, argument] = arguments_;
7
- if (command !== 'check' && command !== 'sync' && command !== 'fleet') {
8
- console.error('usage: ras policy <check|sync|fleet> [adoption]');
4
+ if (command !== 'check' && command !== 'sync') {
5
+ console.error('usage: ras policy <check|sync> [adoption]');
9
6
  process.exitCode = 2;
10
7
  return;
11
8
  }
12
- if (command === 'fleet') {
13
- const path = resolve(argument ?? 'ras-stack.fleet.json');
14
- const config = fleetConfig(JSON.parse(await readFile(path, 'utf8')));
15
- const results = await inspectFleet(config);
16
- process.stdout.write(fleetMarkdown(results));
17
- if (results.some((result) => result.drift.length > 0))
18
- process.exitCode = 1;
19
- return;
20
- }
21
9
  const adoption = argument === 'adoption';
22
10
  const changed = adoption
23
11
  ? await syncAdoptionPolicy(process.cwd(), command === 'check' ? 'check' : 'write')
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/policy/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAE5F,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAoB;IACrD,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAA;IACtC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACrE,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAA;QAChE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,IAAI,sBAAsB,CAAC,CAAA;QACxD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;QACpE,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;QAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;QAC5C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QAC3E,OAAM;IACR,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,KAAK,UAAU,CAAA;IACxC,MAAM,OAAO,GAAG,QAAQ;QACtB,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAClF,CAAC,CAAC,OAAO,KAAK,OAAO;YACnB,CAAC,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC5C,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAA;IACxD,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,OAAO;YAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrD,OAAO,CAAC,KAAK,CAAC,sBAAsB,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAA;QACxF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC","sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { fleetConfig, fleetMarkdown, inspectFleet } from './fleet.js'\nimport { checkRepositoryPolicy, syncAdoptionPolicy, syncRepositoryPolicy } from './index.js'\n\nexport async function runPolicyCli(arguments_: string[]): Promise<void> {\n const [command, argument] = arguments_\n if (command !== 'check' && command !== 'sync' && command !== 'fleet') {\n console.error('usage: ras policy <check|sync|fleet> [adoption]')\n process.exitCode = 2\n return\n }\n\n if (command === 'fleet') {\n const path = resolve(argument ?? 'ras-stack.fleet.json')\n const config = fleetConfig(JSON.parse(await readFile(path, 'utf8')))\n const results = await inspectFleet(config)\n process.stdout.write(fleetMarkdown(results))\n if (results.some((result) => result.drift.length > 0)) process.exitCode = 1\n return\n }\n\n const adoption = argument === 'adoption'\n const changed = adoption\n ? await syncAdoptionPolicy(process.cwd(), command === 'check' ? 'check' : 'write')\n : command === 'check'\n ? await checkRepositoryPolicy(process.cwd())\n : await syncRepositoryPolicy(process.cwd(), 'write')\n if (command === 'check' && changed.length > 0) {\n for (const message of changed) console.error(message)\n console.error(`run ras policy sync${adoption ? ' adoption' : ''} and commit the result`)\n process.exitCode = 1\n } else if (command === 'sync') {\n for (const path of changed) console.log(`updated ${path}`)\n }\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/policy/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAE5F,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAoB;IACrD,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAA;IACtC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9C,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAA;QAC1D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,KAAK,UAAU,CAAA;IACxC,MAAM,OAAO,GAAG,QAAQ;QACtB,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAClF,CAAC,CAAC,OAAO,KAAK,OAAO;YACnB,CAAC,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC5C,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAA;IACxD,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,OAAO;YAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrD,OAAO,CAAC,KAAK,CAAC,sBAAsB,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAA;QACxF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC","sourcesContent":["import { checkRepositoryPolicy, syncAdoptionPolicy, syncRepositoryPolicy } from './index.js'\n\nexport async function runPolicyCli(arguments_: string[]): Promise<void> {\n const [command, argument] = arguments_\n if (command !== 'check' && command !== 'sync') {\n console.error('usage: ras policy <check|sync> [adoption]')\n process.exitCode = 2\n return\n }\n\n const adoption = argument === 'adoption'\n const changed = adoption\n ? await syncAdoptionPolicy(process.cwd(), command === 'check' ? 'check' : 'write')\n : command === 'check'\n ? await checkRepositoryPolicy(process.cwd())\n : await syncRepositoryPolicy(process.cwd(), 'write')\n if (command === 'check' && changed.length > 0) {\n for (const message of changed) console.error(message)\n console.error(`run ras policy sync${adoption ? ' adoption' : ''} and commit the result`)\n process.exitCode = 1\n } else if (command === 'sync') {\n for (const path of changed) console.log(`updated ${path}`)\n }\n}\n"]}
@@ -1,5 +1,3 @@
1
- export { fleetConfig, fleetMarkdown, inspectFleet, loadGitHubAdoptionSnapshot } from './fleet.js';
2
- export type { FleetConfig, FleetRepository, FleetResult } from './fleet.js';
3
1
  type PolicySelection = boolean | {
4
2
  overrides?: Record<string, unknown>;
5
3
  };
@@ -29,3 +27,4 @@ export declare function syncAdoptionPolicy(root: string, mode: 'check' | 'write'
29
27
  export declare function adoptionDrift(root: string, policy: AdoptionPolicy): Promise<string[]>;
30
28
  export declare function adoptionSnapshotDrift(snapshot: AdoptionSnapshot, policy: AdoptionPolicy): string[];
31
29
  export declare function renderedPolicyFiles(root: string, config: RepositoryPolicy): Promise<Map<string, string>>;
30
+ export {};
@@ -1,7 +1,6 @@
1
1
  import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { parseDocument, stringify } from 'yaml';
4
- export { fleetConfig, fleetMarkdown, inspectFleet, loadGitHubAdoptionSnapshot } from './fleet.js';
5
4
  const changesetsPolicy = {
6
5
  $schema: 'https://unpkg.com/@changesets/config@3.1.2/schema.json',
7
6
  changelog: '@changesets/cli/changelog',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { parseDocument, stringify } from 'yaml'\n\nexport { fleetConfig, fleetMarkdown, inspectFleet, loadGitHubAdoptionSnapshot } from './fleet.js'\nexport type { FleetConfig, FleetRepository, FleetResult } from './fleet.js'\n\nconst changesetsPolicy = {\n $schema: 'https://unpkg.com/@changesets/config@3.1.2/schema.json',\n changelog: '@changesets/cli/changelog',\n commit: false,\n fixed: [],\n linked: [],\n access: 'public',\n baseBranch: 'main',\n updateInternalDependencies: 'patch',\n ignore: [],\n}\n\nconst dependabotPolicy = {\n version: 2,\n updates: [\n {\n 'package-ecosystem': 'npm',\n directory: '/',\n schedule: { interval: 'weekly' },\n cooldown: { 'default-days': 7 },\n groups: { 'javascript-dependencies': { patterns: ['*'] } },\n },\n {\n 'package-ecosystem': 'github-actions',\n directory: '/',\n schedule: { interval: 'weekly' },\n cooldown: { 'default-days': 7 },\n },\n ],\n}\n\ntype PolicySelection = boolean | { overrides?: Record<string, unknown> }\n\nexport type RepositoryPolicy = {\n changesets?: PolicySelection\n dependabot?: PolicySelection\n pnpm?: false | { minimumReleaseAge?: number }\n adoption?: false | AdoptionPolicy\n}\n\nexport type AdoptionPolicy = {\n minimumRasStackVersion?: string\n minimumWorkflowRasStackVersion?: string\n node?: string\n pnpm?: string\n just?: string\n requiredReferences?: string[]\n}\n\nexport type AdoptionSnapshot = {\n manifest: Record<string, unknown>\n files: Map<string, string>\n}\n\nexport async function syncRepositoryPolicy(root: string, mode: 'check' | 'write') {\n const config = repositoryPolicy(await readFile(join(root, 'ras-stack.policy.json'), 'utf8'))\n const files = await renderedPolicyFiles(root, config)\n const compared = await Promise.all(\n [...files].map(async ([path, expected]) => {\n const absolute = join(root, path)\n const actual = await readFile(absolute, 'utf8').catch(() => undefined)\n if (actual === expected) return undefined\n if (mode === 'write') {\n await mkdir(dirname(absolute), { recursive: true })\n await writeFile(absolute, expected)\n }\n return path\n }),\n )\n return compared.filter((path) => path !== undefined)\n}\n\nexport async function checkRepositoryPolicy(root: string) {\n const files = await syncRepositoryPolicy(root, 'check')\n const config = repositoryPolicy(await readFile(join(root, 'ras-stack.policy.json'), 'utf8'))\n return [...files.map((path) => `policy drift: ${path}`), ...(config.adoption ? await adoptionDrift(root, config.adoption) : [])]\n}\n\nexport async function syncAdoptionPolicy(root: string, mode: 'check' | 'write') {\n const config = repositoryPolicy(await readFile(join(root, 'ras-stack.policy.json'), 'utf8'))\n if (!config.adoption) return []\n const files = await renderedAdoptionFiles(root, config.adoption)\n const changed = await Promise.all(\n [...files].map(async ([path, expected]) => {\n const absolute = join(root, path)\n const actual = await readFile(absolute, 'utf8')\n if (actual === expected) return undefined\n if (mode === 'write') await writeFile(absolute, expected)\n return path\n }),\n )\n return changed.filter((path) => path !== undefined)\n}\n\nexport async function adoptionDrift(root: string, policy: AdoptionPolicy) {\n const manifest = JSON.parse(await readFile(join(root, 'package.json'), 'utf8')) as Record<string, unknown>\n const files = await adoptionFiles(root)\n return adoptionSnapshotDrift({ manifest, files }, policy)\n}\n\nexport function adoptionSnapshotDrift(snapshot: AdoptionSnapshot, policy: AdoptionPolicy) {\n const drift: string[] = []\n const { manifest, files } = snapshot\n if (policy.node && manifest.engines && plainObject(manifest.engines) && manifest.engines.node !== policy.node) {\n drift.push(`toolchain drift: package.json engines.node must be ${policy.node}`)\n }\n if (policy.pnpm && manifest.packageManager !== `pnpm@${policy.pnpm}`) {\n drift.push(`toolchain drift: package.json packageManager must be pnpm@${policy.pnpm}`)\n }\n if (policy.minimumRasStackVersion) {\n const dependency = dependencyVersion(manifest, 'ras-stack')\n if (dependency && compareVersions(dependency, policy.minimumRasStackVersion) < 0) {\n drift.push(`ras-stack drift: package.json uses ${dependency}, minimum is ${policy.minimumRasStackVersion}`)\n }\n }\n\n const workflows = new Map([...files].filter(([path]) => path.startsWith('.github/') && /\\.ya?ml$/.test(path)))\n const workflowMinimum = policy.minimumWorkflowRasStackVersion ?? policy.minimumRasStackVersion\n for (const [path, source] of workflows) {\n if (workflowMinimum) {\n for (const match of source.matchAll(/richardsolomou\\/ras-stack\\/[^\\s'\"}]+@v(\\d+\\.\\d+\\.\\d+)/g)) {\n const version = match[1]\n if (version && compareVersions(version, workflowMinimum) < 0) {\n drift.push(`ras-stack drift: ${path} uses v${version}, minimum is v${workflowMinimum}`)\n }\n }\n }\n }\n if (\n policy.just &&\n ![...workflows.values()].some(\n (source) => source.includes(`just-version: '${policy.just}'`) || source.includes(`just-version: ${policy.just}`),\n )\n ) {\n drift.push(`toolchain drift: no workflow declares just-version ${policy.just}`)\n }\n for (const reference of policy.requiredReferences ?? []) {\n if (![...files.values()].some((source) => source.includes(reference))) {\n drift.push(`shared config drift: no configuration references ${reference}`)\n }\n }\n return [...new Set(drift)]\n}\n\nexport async function renderedPolicyFiles(root: string, config: RepositoryPolicy) {\n const files = new Map<string, string>()\n if (config.changesets) {\n files.set('.changeset/config.json', `${JSON.stringify(selectedPolicy(changesetsPolicy, config.changesets), null, 2)}\\n`)\n }\n if (config.dependabot) {\n files.set('.github/dependabot.yml', stringify(selectedPolicy(dependabotPolicy, config.dependabot), { lineWidth: 0, singleQuote: true }))\n }\n if (config.pnpm) {\n const path = join(root, 'pnpm-workspace.yaml')\n const document = parseDocument(await readFile(path, 'utf8').catch(() => ''))\n document.set('minimumReleaseAge', config.pnpm.minimumReleaseAge ?? 10_080)\n files.set('pnpm-workspace.yaml', document.toString({ lineWidth: 0 }))\n }\n return files\n}\n\nfunction repositoryPolicy(source: string): RepositoryPolicy {\n const value = JSON.parse(source) as unknown\n if (!plainObject(value)) throw new Error('ras-stack.policy.json must contain an object')\n for (const name of ['changesets', 'dependabot'] as const) validateSelection(name, value[name])\n const pnpm = value.pnpm\n if (pnpm !== undefined && pnpm !== false) {\n if (!plainObject(pnpm)) throw new Error('pnpm policy must be false or an object')\n const age = pnpm.minimumReleaseAge\n if (age !== undefined && (typeof age !== 'number' || !Number.isInteger(age) || age < 0)) {\n throw new Error('pnpm.minimumReleaseAge must be a non-negative integer')\n }\n }\n const adoption = value.adoption\n if (adoption !== undefined && adoption !== false) {\n if (!plainObject(adoption)) throw new Error('adoption policy must be false or an object')\n for (const key of ['minimumRasStackVersion', 'minimumWorkflowRasStackVersion', 'node', 'pnpm', 'just']) {\n if (adoption[key] !== undefined && typeof adoption[key] !== 'string') throw new Error(`adoption.${key} must be a string`)\n }\n if (adoption.requiredReferences !== undefined && !stringArray(adoption.requiredReferences)) {\n throw new Error('adoption.requiredReferences must be an array of strings')\n }\n }\n return value\n}\n\nfunction dependencyVersion(manifest: Record<string, unknown>, name: string) {\n for (const field of ['dependencies', 'devDependencies']) {\n const dependencies = manifest[field]\n const value = plainObject(dependencies) ? dependencies[name] : undefined\n if (typeof value === 'string') return /\\d+\\.\\d+\\.\\d+/.exec(value)?.[0]\n }\n return undefined\n}\n\nfunction compareVersions(left: string, right: string) {\n const a = left.split('.').map(Number)\n const b = right.split('.').map(Number)\n for (let index = 0; index < 3; index += 1) {\n if (a[index] !== b[index]) return (a[index] ?? 0) - (b[index] ?? 0)\n }\n return 0\n}\n\nasync function textFiles(directory: string, root = directory): Promise<Map<string, string>> {\n const files = new Map<string, string>()\n const entries = await readdir(directory, { withFileTypes: true }).catch(() => [])\n const results = await Promise.all(\n entries.map(async (entry) => {\n const path = join(directory, entry.name)\n if (entry.isDirectory()) return textFiles(path, root)\n if (/\\.ya?ml$/.test(entry.name)) return new Map([[path.slice(root.length + 1), await readFile(path, 'utf8')]])\n return new Map<string, string>()\n }),\n )\n for (const result of results) {\n for (const [path, source] of result) files.set(path, source)\n }\n return files\n}\n\nasync function adoptionFiles(root: string) {\n const files = await textFiles(join(root, '.github'), root)\n const entries = await readdir(root, { withFileTypes: true })\n await Promise.all(\n entries.map(async (entry) => {\n if (!entry.isFile() || !/^(?:ras-stack\\.policy|oxlint|tsconfig(?:\\.[^.]+)*)\\.json$/.test(entry.name)) return\n files.set(entry.name, await readFile(join(root, entry.name), 'utf8'))\n }),\n )\n return files\n}\n\nasync function renderedAdoptionFiles(root: string, policy: AdoptionPolicy) {\n const files = new Map<string, string>()\n const manifestPath = join(root, 'package.json')\n const manifestSource = await readFile(manifestPath, 'utf8')\n const manifest = JSON.parse(manifestSource) as Record<string, unknown>\n if (policy.node) {\n const engines = plainObject(manifest.engines) ? manifest.engines : {}\n manifest.engines = { ...engines, node: policy.node }\n }\n if (policy.pnpm) manifest.packageManager = `pnpm@${policy.pnpm}`\n if (policy.minimumRasStackVersion) {\n for (const field of ['dependencies', 'devDependencies']) {\n const dependencies = manifest[field]\n if (!plainObject(dependencies) || typeof dependencies['ras-stack'] !== 'string') continue\n dependencies['ras-stack'] = versionAtLeast(dependencies['ras-stack'], policy.minimumRasStackVersion)\n }\n }\n const renderedManifest = `${JSON.stringify(manifest, null, 2)}\\n`\n if (renderedManifest !== manifestSource) files.set('package.json', renderedManifest)\n\n const workflowVersion = policy.minimumWorkflowRasStackVersion ?? policy.minimumRasStackVersion\n const workflows = await textFiles(join(root, '.github'), root)\n for (const [path, source] of workflows) {\n let rendered = source\n if (workflowVersion) {\n rendered = rendered.replace(/(richardsolomou\\/ras-stack\\/[^\\s'\"}]+@v)(\\d+\\.\\d+\\.\\d+)/g, (reference, prefix, current) =>\n compareVersions(current, workflowVersion) < 0 ? `${prefix}${workflowVersion}` : reference,\n )\n }\n if (policy.just) {\n rendered = rendered.replace(/(just-version:\\s*)(['\"]?)\\d+\\.\\d+\\.\\d+\\2/g, `$1$2${policy.just}$2`)\n rendered = rendered.replace(\n /(richardsolomou\\/ras-stack\\/actions\\/setup-just@[^\\n]+\\n(?:(?!\\n\\s*-\\s+uses:)[\\s\\S]){0,300}?\\n\\s+version:\\s*)(['\"]?)\\d+\\.\\d+\\.\\d+\\2/g,\n `$1$2${policy.just}$2`,\n )\n }\n if (rendered !== source) files.set(path, rendered)\n }\n return files\n}\n\nfunction versionAtLeast(current: string, version: string) {\n const match = /^(\\s*(?:\\^|~|>=|>|<=|<|=)?\\s*)\\d+\\.\\d+\\.\\d+(.*)$/.exec(current)\n const currentVersion = /\\d+\\.\\d+\\.\\d+/.exec(current)?.[0]\n if (currentVersion && compareVersions(currentVersion, version) >= 0) return current\n return match ? `${match[1]}${version}${match[2]}` : version\n}\n\nfunction validateSelection(name: string, value: unknown) {\n if (value === undefined || typeof value === 'boolean') return\n if (!plainObject(value) || (value.overrides !== undefined && !plainObject(value.overrides))) {\n throw new Error(`${name} policy must be a boolean or an object with overrides`)\n }\n}\n\nfunction selectedPolicy(base: Record<string, unknown>, selection: Exclude<PolicySelection, false>) {\n return selection === true ? base : deepMerge(base, selection.overrides ?? {})\n}\n\nfunction deepMerge(base: Record<string, unknown>, overrides: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n [...new Set([...Object.keys(base), ...Object.keys(overrides)])].map((key) => {\n const left = base[key]\n const right = overrides[key]\n return [key, plainObject(left) && plainObject(right) ? deepMerge(left, right) : right === undefined ? left : right]\n }),\n )\n}\n\nfunction plainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction stringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === 'string')\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAE/C,MAAM,gBAAgB,GAAG;IACvB,OAAO,EAAE,wDAAwD;IACjE,SAAS,EAAE,2BAA2B;IACtC,MAAM,EAAE,KAAK;IACb,KAAK,EAAE,EAAE;IACT,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,MAAM;IAClB,0BAA0B,EAAE,OAAO;IACnC,MAAM,EAAE,EAAE;CACX,CAAA;AAED,MAAM,gBAAgB,GAAG;IACvB,OAAO,EAAE,CAAC;IACV,OAAO,EAAE;QACP;YACE,mBAAmB,EAAE,KAAK;YAC1B,SAAS,EAAE,GAAG;YACd,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;YAChC,QAAQ,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE;YAC/B,MAAM,EAAE,EAAE,yBAAyB,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE;SAC3D;QACD;YACE,mBAAmB,EAAE,gBAAgB;YACrC,SAAS,EAAE,GAAG;YACd,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;YAChC,QAAQ,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE;SAChC;KACF;CACF,CAAA;AAyBD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAuB;IAC9E,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;IAC5F,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACrD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACtE,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QACzC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACnD,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QACrC,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CACH,CAAA;IACD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAY;IACtD,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACvD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;IAC5F,OAAO,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAClI,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY,EAAE,IAAuB;IAC5E,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;IAC5F,IAAI,CAAC,MAAM,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAA;IAC/B,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;IAChE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC/C,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QACzC,IAAI,IAAI,KAAK,OAAO;YAAE,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QACzD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CACH,CAAA;IACD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,MAAsB;IACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAA4B,CAAA;IAC1G,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,qBAAqB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,CAAC,CAAA;AAC3D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAA0B,EAAE,MAAsB;IACtF,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAA;IACpC,IAAI,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,OAAO,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9G,KAAK,CAAC,IAAI,CAAC,sDAAsD,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACjF,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,cAAc,KAAK,QAAQ,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,6DAA6D,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACxF,CAAC;IACD,IAAI,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA;QAC3D,IAAI,UAAU,IAAI,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,sBAAsB,CAAC,GAAG,CAAC,EAAE,CAAC;YACjF,KAAK,CAAC,IAAI,CAAC,sCAAsC,UAAU,gBAAgB,MAAM,CAAC,sBAAsB,EAAE,CAAC,CAAA;QAC7G,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC9G,MAAM,eAAe,GAAG,MAAM,CAAC,8BAA8B,IAAI,MAAM,CAAC,sBAAsB,CAAA;IAC9F,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QACvC,IAAI,eAAe,EAAE,CAAC;YACpB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,wDAAwD,CAAC,EAAE,CAAC;gBAC9F,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;gBACxB,IAAI,OAAO,IAAI,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC7D,KAAK,CAAC,IAAI,CAAC,oBAAoB,IAAI,UAAU,OAAO,iBAAiB,eAAe,EAAE,CAAC,CAAA;gBACzF,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,IACE,MAAM,CAAC,IAAI;QACX,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC3B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,iBAAiB,MAAM,CAAC,IAAI,EAAE,CAAC,CACjH,EACD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,sDAAsD,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACjF,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,kBAAkB,IAAI,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YACtE,KAAK,CAAC,IAAI,CAAC,oDAAoD,SAAS,EAAE,CAAC,CAAA;QAC7E,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,MAAwB;IAC9E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,KAAK,CAAC,GAAG,CAAC,wBAAwB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;IAC1H,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,KAAK,CAAC,GAAG,CAAC,wBAAwB,EAAE,SAAS,CAAC,cAAc,CAAC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC1I,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAA;QAC9C,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5E,QAAQ,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,IAAI,MAAM,CAAC,CAAA;QAC1E,KAAK,CAAC,GAAG,CAAC,qBAAqB,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACvE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAY,CAAA;IAC3C,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;IACxF,KAAK,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,YAAY,CAAU;QAAE,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;IAC9F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACvB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACzC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QACjF,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAA;QAClC,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;YACxF,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAA;IAC/B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACjD,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;QACzF,KAAK,MAAM,GAAG,IAAI,CAAC,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YACvG,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,GAAG,mBAAmB,CAAC,CAAA;QAC3H,CAAC;QACD,IAAI,QAAQ,CAAC,kBAAkB,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC3F,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAiC,EAAE,IAAY;IACxE,KAAK,MAAM,KAAK,IAAI,CAAC,cAAc,EAAE,iBAAiB,CAAC,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;QACpC,MAAM,KAAK,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACxE,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACxE,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,KAAa;IAClD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACrC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACtC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;IACrE,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,SAAiB,EAAE,IAAI,GAAG,SAAS;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IACjF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACxC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,OAAO,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACrD,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9G,OAAO,IAAI,GAAG,EAAkB,CAAA;IAClC,CAAC,CAAC,CACH,CAAA;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC9D,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,IAAY;IACvC,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5D,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,2DAA2D,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAM;QAC5G,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;IACvE,CAAC,CAAC,CACH,CAAA;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,IAAY,EAAE,MAAsB;IACvE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAA;IAC/C,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAA4B,CAAA;IACtE,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QACrE,QAAQ,CAAC,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAA;IACtD,CAAC;IACD,IAAI,MAAM,CAAC,IAAI;QAAE,QAAQ,CAAC,cAAc,GAAG,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAA;IAChE,IAAI,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAClC,KAAK,MAAM,KAAK,IAAI,CAAC,cAAc,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;YACpC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,IAAI,OAAO,YAAY,CAAC,WAAW,CAAC,KAAK,QAAQ;gBAAE,SAAQ;YACzF,YAAY,CAAC,WAAW,CAAC,GAAG,cAAc,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,sBAAsB,CAAC,CAAA;QACtG,CAAC;IACH,CAAC;IACD,MAAM,gBAAgB,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAA;IACjE,IAAI,gBAAgB,KAAK,cAAc;QAAE,KAAK,CAAC,GAAG,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IAEpF,MAAM,eAAe,GAAG,MAAM,CAAC,8BAA8B,IAAI,MAAM,CAAC,sBAAsB,CAAA;IAC9F,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAA;IAC9D,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QACvC,IAAI,QAAQ,GAAG,MAAM,CAAA;QACrB,IAAI,eAAe,EAAE,CAAC;YACpB,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,0DAA0D,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CACrH,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,SAAS,CAC1F,CAAA;QACH,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,2CAA2C,EAAE,OAAO,MAAM,CAAC,IAAI,IAAI,CAAC,CAAA;YAChG,QAAQ,GAAG,QAAQ,CAAC,OAAO,CACzB,sIAAsI,EACtI,OAAO,MAAM,CAAC,IAAI,IAAI,CACvB,CAAA;QACH,CAAC;QACD,IAAI,QAAQ,KAAK,MAAM;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IACpD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,cAAc,CAAC,OAAe,EAAE,OAAe;IACtD,MAAM,KAAK,GAAG,kDAAkD,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC9E,MAAM,cAAc,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACzD,IAAI,cAAc,IAAI,eAAe,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAA;IACnF,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAA;AAC7D,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY,EAAE,KAAc;IACrD,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAM;IAC7D,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,uDAAuD,CAAC,CAAA;IACjF,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,IAA6B,EAAE,SAA0C;IAC/F,OAAO,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,IAAI,EAAE,CAAC,CAAA;AAC/E,CAAC;AAED,SAAS,SAAS,CAAC,IAA6B,EAAE,SAAkC;IAClF,OAAO,MAAM,CAAC,WAAW,CACvB,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QACtB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;QAC5B,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACrH,CAAC,CAAC,CACH,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAA;AAChF,CAAC","sourcesContent":["import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { parseDocument, stringify } from 'yaml'\n\nconst changesetsPolicy = {\n $schema: 'https://unpkg.com/@changesets/config@3.1.2/schema.json',\n changelog: '@changesets/cli/changelog',\n commit: false,\n fixed: [],\n linked: [],\n access: 'public',\n baseBranch: 'main',\n updateInternalDependencies: 'patch',\n ignore: [],\n}\n\nconst dependabotPolicy = {\n version: 2,\n updates: [\n {\n 'package-ecosystem': 'npm',\n directory: '/',\n schedule: { interval: 'weekly' },\n cooldown: { 'default-days': 7 },\n groups: { 'javascript-dependencies': { patterns: ['*'] } },\n },\n {\n 'package-ecosystem': 'github-actions',\n directory: '/',\n schedule: { interval: 'weekly' },\n cooldown: { 'default-days': 7 },\n },\n ],\n}\n\ntype PolicySelection = boolean | { overrides?: Record<string, unknown> }\n\nexport type RepositoryPolicy = {\n changesets?: PolicySelection\n dependabot?: PolicySelection\n pnpm?: false | { minimumReleaseAge?: number }\n adoption?: false | AdoptionPolicy\n}\n\nexport type AdoptionPolicy = {\n minimumRasStackVersion?: string\n minimumWorkflowRasStackVersion?: string\n node?: string\n pnpm?: string\n just?: string\n requiredReferences?: string[]\n}\n\nexport type AdoptionSnapshot = {\n manifest: Record<string, unknown>\n files: Map<string, string>\n}\n\nexport async function syncRepositoryPolicy(root: string, mode: 'check' | 'write') {\n const config = repositoryPolicy(await readFile(join(root, 'ras-stack.policy.json'), 'utf8'))\n const files = await renderedPolicyFiles(root, config)\n const compared = await Promise.all(\n [...files].map(async ([path, expected]) => {\n const absolute = join(root, path)\n const actual = await readFile(absolute, 'utf8').catch(() => undefined)\n if (actual === expected) return undefined\n if (mode === 'write') {\n await mkdir(dirname(absolute), { recursive: true })\n await writeFile(absolute, expected)\n }\n return path\n }),\n )\n return compared.filter((path) => path !== undefined)\n}\n\nexport async function checkRepositoryPolicy(root: string) {\n const files = await syncRepositoryPolicy(root, 'check')\n const config = repositoryPolicy(await readFile(join(root, 'ras-stack.policy.json'), 'utf8'))\n return [...files.map((path) => `policy drift: ${path}`), ...(config.adoption ? await adoptionDrift(root, config.adoption) : [])]\n}\n\nexport async function syncAdoptionPolicy(root: string, mode: 'check' | 'write') {\n const config = repositoryPolicy(await readFile(join(root, 'ras-stack.policy.json'), 'utf8'))\n if (!config.adoption) return []\n const files = await renderedAdoptionFiles(root, config.adoption)\n const changed = await Promise.all(\n [...files].map(async ([path, expected]) => {\n const absolute = join(root, path)\n const actual = await readFile(absolute, 'utf8')\n if (actual === expected) return undefined\n if (mode === 'write') await writeFile(absolute, expected)\n return path\n }),\n )\n return changed.filter((path) => path !== undefined)\n}\n\nexport async function adoptionDrift(root: string, policy: AdoptionPolicy) {\n const manifest = JSON.parse(await readFile(join(root, 'package.json'), 'utf8')) as Record<string, unknown>\n const files = await adoptionFiles(root)\n return adoptionSnapshotDrift({ manifest, files }, policy)\n}\n\nexport function adoptionSnapshotDrift(snapshot: AdoptionSnapshot, policy: AdoptionPolicy) {\n const drift: string[] = []\n const { manifest, files } = snapshot\n if (policy.node && manifest.engines && plainObject(manifest.engines) && manifest.engines.node !== policy.node) {\n drift.push(`toolchain drift: package.json engines.node must be ${policy.node}`)\n }\n if (policy.pnpm && manifest.packageManager !== `pnpm@${policy.pnpm}`) {\n drift.push(`toolchain drift: package.json packageManager must be pnpm@${policy.pnpm}`)\n }\n if (policy.minimumRasStackVersion) {\n const dependency = dependencyVersion(manifest, 'ras-stack')\n if (dependency && compareVersions(dependency, policy.minimumRasStackVersion) < 0) {\n drift.push(`ras-stack drift: package.json uses ${dependency}, minimum is ${policy.minimumRasStackVersion}`)\n }\n }\n\n const workflows = new Map([...files].filter(([path]) => path.startsWith('.github/') && /\\.ya?ml$/.test(path)))\n const workflowMinimum = policy.minimumWorkflowRasStackVersion ?? policy.minimumRasStackVersion\n for (const [path, source] of workflows) {\n if (workflowMinimum) {\n for (const match of source.matchAll(/richardsolomou\\/ras-stack\\/[^\\s'\"}]+@v(\\d+\\.\\d+\\.\\d+)/g)) {\n const version = match[1]\n if (version && compareVersions(version, workflowMinimum) < 0) {\n drift.push(`ras-stack drift: ${path} uses v${version}, minimum is v${workflowMinimum}`)\n }\n }\n }\n }\n if (\n policy.just &&\n ![...workflows.values()].some(\n (source) => source.includes(`just-version: '${policy.just}'`) || source.includes(`just-version: ${policy.just}`),\n )\n ) {\n drift.push(`toolchain drift: no workflow declares just-version ${policy.just}`)\n }\n for (const reference of policy.requiredReferences ?? []) {\n if (![...files.values()].some((source) => source.includes(reference))) {\n drift.push(`shared config drift: no configuration references ${reference}`)\n }\n }\n return [...new Set(drift)]\n}\n\nexport async function renderedPolicyFiles(root: string, config: RepositoryPolicy) {\n const files = new Map<string, string>()\n if (config.changesets) {\n files.set('.changeset/config.json', `${JSON.stringify(selectedPolicy(changesetsPolicy, config.changesets), null, 2)}\\n`)\n }\n if (config.dependabot) {\n files.set('.github/dependabot.yml', stringify(selectedPolicy(dependabotPolicy, config.dependabot), { lineWidth: 0, singleQuote: true }))\n }\n if (config.pnpm) {\n const path = join(root, 'pnpm-workspace.yaml')\n const document = parseDocument(await readFile(path, 'utf8').catch(() => ''))\n document.set('minimumReleaseAge', config.pnpm.minimumReleaseAge ?? 10_080)\n files.set('pnpm-workspace.yaml', document.toString({ lineWidth: 0 }))\n }\n return files\n}\n\nfunction repositoryPolicy(source: string): RepositoryPolicy {\n const value = JSON.parse(source) as unknown\n if (!plainObject(value)) throw new Error('ras-stack.policy.json must contain an object')\n for (const name of ['changesets', 'dependabot'] as const) validateSelection(name, value[name])\n const pnpm = value.pnpm\n if (pnpm !== undefined && pnpm !== false) {\n if (!plainObject(pnpm)) throw new Error('pnpm policy must be false or an object')\n const age = pnpm.minimumReleaseAge\n if (age !== undefined && (typeof age !== 'number' || !Number.isInteger(age) || age < 0)) {\n throw new Error('pnpm.minimumReleaseAge must be a non-negative integer')\n }\n }\n const adoption = value.adoption\n if (adoption !== undefined && adoption !== false) {\n if (!plainObject(adoption)) throw new Error('adoption policy must be false or an object')\n for (const key of ['minimumRasStackVersion', 'minimumWorkflowRasStackVersion', 'node', 'pnpm', 'just']) {\n if (adoption[key] !== undefined && typeof adoption[key] !== 'string') throw new Error(`adoption.${key} must be a string`)\n }\n if (adoption.requiredReferences !== undefined && !stringArray(adoption.requiredReferences)) {\n throw new Error('adoption.requiredReferences must be an array of strings')\n }\n }\n return value\n}\n\nfunction dependencyVersion(manifest: Record<string, unknown>, name: string) {\n for (const field of ['dependencies', 'devDependencies']) {\n const dependencies = manifest[field]\n const value = plainObject(dependencies) ? dependencies[name] : undefined\n if (typeof value === 'string') return /\\d+\\.\\d+\\.\\d+/.exec(value)?.[0]\n }\n return undefined\n}\n\nfunction compareVersions(left: string, right: string) {\n const a = left.split('.').map(Number)\n const b = right.split('.').map(Number)\n for (let index = 0; index < 3; index += 1) {\n if (a[index] !== b[index]) return (a[index] ?? 0) - (b[index] ?? 0)\n }\n return 0\n}\n\nasync function textFiles(directory: string, root = directory): Promise<Map<string, string>> {\n const files = new Map<string, string>()\n const entries = await readdir(directory, { withFileTypes: true }).catch(() => [])\n const results = await Promise.all(\n entries.map(async (entry) => {\n const path = join(directory, entry.name)\n if (entry.isDirectory()) return textFiles(path, root)\n if (/\\.ya?ml$/.test(entry.name)) return new Map([[path.slice(root.length + 1), await readFile(path, 'utf8')]])\n return new Map<string, string>()\n }),\n )\n for (const result of results) {\n for (const [path, source] of result) files.set(path, source)\n }\n return files\n}\n\nasync function adoptionFiles(root: string) {\n const files = await textFiles(join(root, '.github'), root)\n const entries = await readdir(root, { withFileTypes: true })\n await Promise.all(\n entries.map(async (entry) => {\n if (!entry.isFile() || !/^(?:ras-stack\\.policy|oxlint|tsconfig(?:\\.[^.]+)*)\\.json$/.test(entry.name)) return\n files.set(entry.name, await readFile(join(root, entry.name), 'utf8'))\n }),\n )\n return files\n}\n\nasync function renderedAdoptionFiles(root: string, policy: AdoptionPolicy) {\n const files = new Map<string, string>()\n const manifestPath = join(root, 'package.json')\n const manifestSource = await readFile(manifestPath, 'utf8')\n const manifest = JSON.parse(manifestSource) as Record<string, unknown>\n if (policy.node) {\n const engines = plainObject(manifest.engines) ? manifest.engines : {}\n manifest.engines = { ...engines, node: policy.node }\n }\n if (policy.pnpm) manifest.packageManager = `pnpm@${policy.pnpm}`\n if (policy.minimumRasStackVersion) {\n for (const field of ['dependencies', 'devDependencies']) {\n const dependencies = manifest[field]\n if (!plainObject(dependencies) || typeof dependencies['ras-stack'] !== 'string') continue\n dependencies['ras-stack'] = versionAtLeast(dependencies['ras-stack'], policy.minimumRasStackVersion)\n }\n }\n const renderedManifest = `${JSON.stringify(manifest, null, 2)}\\n`\n if (renderedManifest !== manifestSource) files.set('package.json', renderedManifest)\n\n const workflowVersion = policy.minimumWorkflowRasStackVersion ?? policy.minimumRasStackVersion\n const workflows = await textFiles(join(root, '.github'), root)\n for (const [path, source] of workflows) {\n let rendered = source\n if (workflowVersion) {\n rendered = rendered.replace(/(richardsolomou\\/ras-stack\\/[^\\s'\"}]+@v)(\\d+\\.\\d+\\.\\d+)/g, (reference, prefix, current) =>\n compareVersions(current, workflowVersion) < 0 ? `${prefix}${workflowVersion}` : reference,\n )\n }\n if (policy.just) {\n rendered = rendered.replace(/(just-version:\\s*)(['\"]?)\\d+\\.\\d+\\.\\d+\\2/g, `$1$2${policy.just}$2`)\n rendered = rendered.replace(\n /(richardsolomou\\/ras-stack\\/actions\\/setup-just@[^\\n]+\\n(?:(?!\\n\\s*-\\s+uses:)[\\s\\S]){0,300}?\\n\\s+version:\\s*)(['\"]?)\\d+\\.\\d+\\.\\d+\\2/g,\n `$1$2${policy.just}$2`,\n )\n }\n if (rendered !== source) files.set(path, rendered)\n }\n return files\n}\n\nfunction versionAtLeast(current: string, version: string) {\n const match = /^(\\s*(?:\\^|~|>=|>|<=|<|=)?\\s*)\\d+\\.\\d+\\.\\d+(.*)$/.exec(current)\n const currentVersion = /\\d+\\.\\d+\\.\\d+/.exec(current)?.[0]\n if (currentVersion && compareVersions(currentVersion, version) >= 0) return current\n return match ? `${match[1]}${version}${match[2]}` : version\n}\n\nfunction validateSelection(name: string, value: unknown) {\n if (value === undefined || typeof value === 'boolean') return\n if (!plainObject(value) || (value.overrides !== undefined && !plainObject(value.overrides))) {\n throw new Error(`${name} policy must be a boolean or an object with overrides`)\n }\n}\n\nfunction selectedPolicy(base: Record<string, unknown>, selection: Exclude<PolicySelection, false>) {\n return selection === true ? base : deepMerge(base, selection.overrides ?? {})\n}\n\nfunction deepMerge(base: Record<string, unknown>, overrides: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n [...new Set([...Object.keys(base), ...Object.keys(overrides)])].map((key) => {\n const left = base[key]\n const right = overrides[key]\n return [key, plainObject(left) && plainObject(right) ? deepMerge(left, right) : right === undefined ? left : right]\n }),\n )\n}\n\nfunction plainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction stringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === 'string')\n}\n"]}
@@ -1,4 +1,43 @@
1
1
  import type { PostHog, PostHogOptions } from 'posthog-node';
2
+ import type { RpcErrorContext, RpcLogger } from '../server/rpc.js';
2
3
  import type { PostHogEnvironment } from './config.js';
4
+ export type PostHogLogValue = string | number | boolean | null | PostHogLogValue[];
5
+ export type PostHogLogRecord = {
6
+ body: string;
7
+ severityText?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
8
+ timestamp?: number;
9
+ attributes?: Record<string, unknown>;
10
+ };
11
+ export type ManagedPostHogServerTelemetryOptions = {
12
+ environment: PostHogEnvironment | undefined;
13
+ serviceName: string;
14
+ serviceVersion?: string;
15
+ deploymentEnvironment?: string;
16
+ clientOptions?: Omit<PostHogOptions, 'host'>;
17
+ onError?: (error: unknown) => void;
18
+ };
19
+ export type PostHogServerTelemetry = ReturnType<typeof createManagedPostHogServerTelemetry>;
20
+ export type PostHogRpcLoggerOptions = {
21
+ logError?: (error: unknown, context: RpcErrorContext) => void;
22
+ resolveAuthenticatedDistinctId?: (request: Request) => string | undefined | Promise<string | undefined>;
23
+ allowAnonymousDistinctId?: boolean;
24
+ fallbackDistinctId?: string;
25
+ };
26
+ type PostHogShutdownProcess = {
27
+ pid: number;
28
+ on(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void;
29
+ off(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void;
30
+ kill(pid: number, signal: 'SIGINT' | 'SIGTERM'): void;
31
+ };
3
32
  export declare function createPostHogServerClient(environment: PostHogEnvironment | undefined, options?: Omit<PostHogOptions, 'host'>): Promise<PostHog | undefined>;
4
33
  export declare function shutdownPostHogServerClient(client: PostHog | undefined, timeoutMs?: number): Promise<void>;
34
+ export declare function createManagedPostHogServerTelemetry(options: ManagedPostHogServerTelemetryOptions): {
35
+ start(): Promise<void>;
36
+ capture(distinctId: string, event: string, properties?: Record<string, unknown>): Promise<void>;
37
+ exception(error: unknown, distinctId?: string, properties?: Record<string, unknown>): Promise<void>;
38
+ log(record: PostHogLogRecord): Promise<void>;
39
+ shutdown(timeoutMs?: number): Promise<void>;
40
+ };
41
+ export declare function createPostHogRpcLogger(telemetry: PostHogServerTelemetry, options?: PostHogRpcLoggerOptions): RpcLogger;
42
+ export declare function installPostHogServerTelemetryShutdown(telemetry: Pick<PostHogServerTelemetry, 'shutdown'>, target?: PostHogShutdownProcess): () => void;
43
+ export {};
@@ -1,3 +1,4 @@
1
+ import { postHogRequestContext } from './request.js';
1
2
  export async function createPostHogServerClient(environment, options = {}) {
2
3
  if (!environment)
3
4
  return undefined;
@@ -9,9 +10,236 @@ export async function createPostHogServerClient(environment, options = {}) {
9
10
  });
10
11
  }
11
12
  export async function shutdownPostHogServerClient(client, timeoutMs = 10_000) {
12
- if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0)
13
- throw new Error('timeoutMs must be a non-negative integer');
13
+ assertShutdownTimeout(timeoutMs);
14
14
  // oxlint-disable-next-line no-underscore-dangle -- posthog-node's async shutdown API is named `_shutdown`.
15
15
  await client?._shutdown(timeoutMs);
16
16
  }
17
+ export function createManagedPostHogServerTelemetry(options) {
18
+ let client;
19
+ let logProvider;
20
+ let closed = false;
21
+ const report = (error) => {
22
+ try {
23
+ options.onError?.(error);
24
+ }
25
+ catch { }
26
+ };
27
+ const getClient = () => {
28
+ if (closed || !options.environment)
29
+ return undefined;
30
+ if (!client) {
31
+ const pending = createPostHogServerClient(options.environment, options.clientOptions);
32
+ client = pending;
33
+ void pending.catch(() => {
34
+ if (client === pending)
35
+ client = undefined;
36
+ });
37
+ }
38
+ return client;
39
+ };
40
+ const getLogProvider = () => {
41
+ if (closed || !options.environment)
42
+ return undefined;
43
+ if (!logProvider) {
44
+ const pending = createPostHogLogProvider(options);
45
+ logProvider = pending;
46
+ void pending.catch(() => {
47
+ if (logProvider === pending)
48
+ logProvider = undefined;
49
+ });
50
+ }
51
+ return logProvider;
52
+ };
53
+ const safely = async (work) => {
54
+ if (closed || !options.environment)
55
+ return;
56
+ try {
57
+ await work();
58
+ }
59
+ catch (error) {
60
+ report(error);
61
+ }
62
+ };
63
+ return {
64
+ async start() {
65
+ await safely(async () => {
66
+ await Promise.all([getClient(), getLogProvider()]);
67
+ });
68
+ },
69
+ async capture(distinctId, event, properties) {
70
+ await safely(async () => {
71
+ const value = await getClient();
72
+ value?.capture({ distinctId, event, ...(properties ? { properties } : {}) });
73
+ });
74
+ },
75
+ async exception(error, distinctId = 'server', properties) {
76
+ await safely(async () => {
77
+ const value = await getClient();
78
+ value?.captureException(error, distinctId, properties);
79
+ });
80
+ },
81
+ async log(record) {
82
+ await safely(async () => {
83
+ const provider = await getLogProvider();
84
+ const attributes = boundedAttributes(record.attributes);
85
+ provider?.getLogger(options.serviceName).emit({
86
+ body: boundedString(record.body),
87
+ ...(record.severityText ? { severityText: record.severityText } : {}),
88
+ ...(record.timestamp === undefined ? {} : { timestamp: record.timestamp }),
89
+ ...(attributes ? { attributes } : {}),
90
+ });
91
+ });
92
+ },
93
+ async shutdown(timeoutMs = 10_000) {
94
+ if (closed)
95
+ return;
96
+ assertShutdownTimeout(timeoutMs);
97
+ closed = true;
98
+ const results = await Promise.allSettled([
99
+ client?.then((value) => shutdownPostHogServerClient(value, timeoutMs)),
100
+ logProvider?.then((value) => shutdownPostHogLogProvider(value, timeoutMs)),
101
+ ]);
102
+ for (const result of results)
103
+ if (result.status === 'rejected')
104
+ report(result.reason);
105
+ },
106
+ };
107
+ }
108
+ export function createPostHogRpcLogger(telemetry, options = {}) {
109
+ return async (error, context, request) => {
110
+ try {
111
+ options.logError?.(error, context);
112
+ }
113
+ catch { }
114
+ let authenticatedDistinctId;
115
+ if (request && options.resolveAuthenticatedDistinctId) {
116
+ try {
117
+ authenticatedDistinctId = await options.resolveAuthenticatedDistinctId(request);
118
+ }
119
+ catch { }
120
+ }
121
+ const correlation = request
122
+ ? postHogRequestContext(request, {
123
+ ...(authenticatedDistinctId ? { authenticatedDistinctId } : {}),
124
+ ...(options.allowAnonymousDistinctId === undefined ? {} : { allowAnonymousDistinctId: options.allowAnonymousDistinctId }),
125
+ })
126
+ : { properties: {} };
127
+ const distinctId = correlation.distinctId ?? options.fallbackDistinctId ?? 'server';
128
+ const properties = {
129
+ ...correlation.properties,
130
+ ...(context.method ? { request_method: context.method } : {}),
131
+ ...(context.path ? { request_path: context.path } : {}),
132
+ };
133
+ await Promise.all([
134
+ telemetry.exception(error, distinctId, properties),
135
+ telemetry.log({
136
+ body: 'server function failed',
137
+ severityText: 'error',
138
+ attributes: { ...properties, posthogDistinctId: distinctId },
139
+ }),
140
+ ]);
141
+ };
142
+ }
143
+ export function installPostHogServerTelemetryShutdown(telemetry, target = process) {
144
+ let shuttingDown = false;
145
+ const listeners = new Map();
146
+ const remove = () => {
147
+ for (const [signal, listener] of listeners)
148
+ target.off(signal, listener);
149
+ listeners.clear();
150
+ };
151
+ for (const signal of ['SIGINT', 'SIGTERM']) {
152
+ const listener = () => {
153
+ if (shuttingDown)
154
+ return;
155
+ shuttingDown = true;
156
+ void telemetry
157
+ .shutdown()
158
+ .catch(() => undefined)
159
+ .finally(() => {
160
+ remove();
161
+ target.kill(target.pid, signal);
162
+ });
163
+ };
164
+ listeners.set(signal, listener);
165
+ target.on(signal, listener);
166
+ }
167
+ return remove;
168
+ }
169
+ async function createPostHogLogProvider(options) {
170
+ if (!options.environment)
171
+ return undefined;
172
+ const [{ OTLPLogExporter }, { resourceFromAttributes }, { BatchLogRecordProcessor, LoggerProvider }] = await Promise.all([
173
+ import('@opentelemetry/exporter-logs-otlp-http'),
174
+ import('@opentelemetry/resources'),
175
+ import('@opentelemetry/sdk-logs'),
176
+ ]);
177
+ const exporter = new OTLPLogExporter({
178
+ url: `${options.environment.host.replace(/\/$/, '')}/i/v1/logs`,
179
+ headers: { Authorization: `Bearer ${options.environment.projectToken}` },
180
+ });
181
+ return new LoggerProvider({
182
+ resource: resourceFromAttributes({
183
+ 'service.name': options.serviceName,
184
+ ...(options.serviceVersion ? { 'service.version': options.serviceVersion } : {}),
185
+ ...(options.deploymentEnvironment ? { 'deployment.environment': options.deploymentEnvironment } : {}),
186
+ }),
187
+ processors: [new BatchLogRecordProcessor({ exporter })],
188
+ });
189
+ }
190
+ function boundedAttributes(attributes) {
191
+ if (!attributes)
192
+ return undefined;
193
+ return Object.fromEntries(Object.entries(attributes)
194
+ .slice(0, 64)
195
+ .map(([key, value]) => [key.slice(0, 128), boundedLogValue(value, 0)])
196
+ .filter((entry) => entry[1] !== undefined));
197
+ }
198
+ function boundedLogValue(value, depth) {
199
+ if (value === null || typeof value === 'number' || typeof value === 'boolean')
200
+ return value;
201
+ if (typeof value === 'string')
202
+ return boundedString(value);
203
+ if (depth >= 3)
204
+ return '[Truncated]';
205
+ if (Array.isArray(value)) {
206
+ return value
207
+ .slice(0, 32)
208
+ .map((entry) => boundedLogValue(entry, depth + 1))
209
+ .filter((entry) => entry !== undefined);
210
+ }
211
+ if (value === undefined)
212
+ return undefined;
213
+ try {
214
+ return boundedString(JSON.stringify(value));
215
+ }
216
+ catch {
217
+ return '[Unserializable value]';
218
+ }
219
+ }
220
+ function boundedString(value) {
221
+ return value.slice(0, 2_048);
222
+ }
223
+ async function shutdownPostHogLogProvider(provider, timeoutMs) {
224
+ if (!provider)
225
+ return;
226
+ let timeout;
227
+ try {
228
+ await Promise.race([
229
+ provider.shutdown(),
230
+ new Promise((_resolve, reject) => {
231
+ timeout = setTimeout(() => reject(new Error(`PostHog log shutdown timed out after ${timeoutMs}ms`)), timeoutMs);
232
+ timeout.unref?.();
233
+ }),
234
+ ]);
235
+ }
236
+ finally {
237
+ if (timeout)
238
+ clearTimeout(timeout);
239
+ }
240
+ }
241
+ function assertShutdownTimeout(timeoutMs) {
242
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0)
243
+ throw new Error('timeoutMs must be a non-negative integer');
244
+ }
17
245
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/posthog/server.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,WAA2C,EAC3C,OAAO,GAAiC,EAAE;IAE1C,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAA;IAClC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAA;IAChD,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC,YAAY,EAAE;QAC3C,IAAI,EAAE,WAAW,CAAC,IAAI;QACtB,0BAA0B,EAAE,IAAI;QAChC,GAAG,OAAO;KACX,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,MAA2B,EAAE,SAAS,GAAG,MAAM;IAC/F,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAClH,2GAA2G;IAC3G,MAAM,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;AACpC,CAAC","sourcesContent":["import type { PostHog, PostHogOptions } from 'posthog-node'\nimport type { PostHogEnvironment } from './config.js'\n\nexport async function createPostHogServerClient(\n environment: PostHogEnvironment | undefined,\n options: Omit<PostHogOptions, 'host'> = {},\n): Promise<PostHog | undefined> {\n if (!environment) return undefined\n const { PostHog } = await import('posthog-node')\n return new PostHog(environment.projectToken, {\n host: environment.host,\n enableExceptionAutocapture: true,\n ...options,\n })\n}\n\nexport async function shutdownPostHogServerClient(client: PostHog | undefined, timeoutMs = 10_000) {\n if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0) throw new Error('timeoutMs must be a non-negative integer')\n // oxlint-disable-next-line no-underscore-dangle -- posthog-node's async shutdown API is named `_shutdown`.\n await client?._shutdown(timeoutMs)\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/posthog/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PostHog, PostHogOptions } from 'posthog-node'\nimport type { RpcErrorContext, RpcLogger } from '../server/rpc.js'\nimport type { PostHogEnvironment } from './config.js'\nimport { postHogRequestContext } from './request.js'\n\ntype LogProvider = InstanceType<(typeof import('@opentelemetry/sdk-logs'))['LoggerProvider']>\n\nexport type PostHogLogValue = string | number | boolean | null | PostHogLogValue[]\n\nexport type PostHogLogRecord = {\n body: string\n severityText?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n timestamp?: number\n attributes?: Record<string, unknown>\n}\n\nexport type ManagedPostHogServerTelemetryOptions = {\n environment: PostHogEnvironment | undefined\n serviceName: string\n serviceVersion?: string\n deploymentEnvironment?: string\n clientOptions?: Omit<PostHogOptions, 'host'>\n onError?: (error: unknown) => void\n}\n\nexport type PostHogServerTelemetry = ReturnType<typeof createManagedPostHogServerTelemetry>\n\nexport type PostHogRpcLoggerOptions = {\n logError?: (error: unknown, context: RpcErrorContext) => void\n resolveAuthenticatedDistinctId?: (request: Request) => string | undefined | Promise<string | undefined>\n allowAnonymousDistinctId?: boolean\n fallbackDistinctId?: string\n}\n\ntype PostHogShutdownProcess = {\n pid: number\n on(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void\n off(signal: 'SIGINT' | 'SIGTERM', listener: () => void): void\n kill(pid: number, signal: 'SIGINT' | 'SIGTERM'): void\n}\n\nexport async function createPostHogServerClient(\n environment: PostHogEnvironment | undefined,\n options: Omit<PostHogOptions, 'host'> = {},\n): Promise<PostHog | undefined> {\n if (!environment) return undefined\n const { PostHog } = await import('posthog-node')\n return new PostHog(environment.projectToken, {\n host: environment.host,\n enableExceptionAutocapture: true,\n ...options,\n })\n}\n\nexport async function shutdownPostHogServerClient(client: PostHog | undefined, timeoutMs = 10_000) {\n assertShutdownTimeout(timeoutMs)\n // oxlint-disable-next-line no-underscore-dangle -- posthog-node's async shutdown API is named `_shutdown`.\n await client?._shutdown(timeoutMs)\n}\n\nexport function createManagedPostHogServerTelemetry(options: ManagedPostHogServerTelemetryOptions) {\n let client: Promise<PostHog | undefined> | undefined\n let logProvider: Promise<LogProvider | undefined> | undefined\n let closed = false\n\n const report = (error: unknown) => {\n try {\n options.onError?.(error)\n } catch {}\n }\n\n const getClient = () => {\n if (closed || !options.environment) return undefined\n if (!client) {\n const pending = createPostHogServerClient(options.environment, options.clientOptions)\n client = pending\n void pending.catch(() => {\n if (client === pending) client = undefined\n })\n }\n return client\n }\n\n const getLogProvider = () => {\n if (closed || !options.environment) return undefined\n if (!logProvider) {\n const pending = createPostHogLogProvider(options)\n logProvider = pending\n void pending.catch(() => {\n if (logProvider === pending) logProvider = undefined\n })\n }\n return logProvider\n }\n\n const safely = async (work: () => void | Promise<void>) => {\n if (closed || !options.environment) return\n try {\n await work()\n } catch (error) {\n report(error)\n }\n }\n\n return {\n async start() {\n await safely(async () => {\n await Promise.all([getClient(), getLogProvider()])\n })\n },\n async capture(distinctId: string, event: string, properties?: Record<string, unknown>) {\n await safely(async () => {\n const value = await getClient()\n value?.capture({ distinctId, event, ...(properties ? { properties } : {}) })\n })\n },\n async exception(error: unknown, distinctId = 'server', properties?: Record<string, unknown>) {\n await safely(async () => {\n const value = await getClient()\n value?.captureException(error, distinctId, properties)\n })\n },\n async log(record: PostHogLogRecord) {\n await safely(async () => {\n const provider = await getLogProvider()\n const attributes = boundedAttributes(record.attributes)\n provider?.getLogger(options.serviceName).emit({\n body: boundedString(record.body),\n ...(record.severityText ? { severityText: record.severityText } : {}),\n ...(record.timestamp === undefined ? {} : { timestamp: record.timestamp }),\n ...(attributes ? { attributes } : {}),\n })\n })\n },\n async shutdown(timeoutMs = 10_000) {\n if (closed) return\n assertShutdownTimeout(timeoutMs)\n closed = true\n const results = await Promise.allSettled([\n client?.then((value) => shutdownPostHogServerClient(value, timeoutMs)),\n logProvider?.then((value) => shutdownPostHogLogProvider(value, timeoutMs)),\n ])\n for (const result of results) if (result.status === 'rejected') report(result.reason)\n },\n }\n}\n\nexport function createPostHogRpcLogger(telemetry: PostHogServerTelemetry, options: PostHogRpcLoggerOptions = {}): RpcLogger {\n return async (error, context, request) => {\n try {\n options.logError?.(error, context)\n } catch {}\n let authenticatedDistinctId: string | undefined\n if (request && options.resolveAuthenticatedDistinctId) {\n try {\n authenticatedDistinctId = await options.resolveAuthenticatedDistinctId(request)\n } catch {}\n }\n const correlation = request\n ? postHogRequestContext(request, {\n ...(authenticatedDistinctId ? { authenticatedDistinctId } : {}),\n ...(options.allowAnonymousDistinctId === undefined ? {} : { allowAnonymousDistinctId: options.allowAnonymousDistinctId }),\n })\n : { properties: {} }\n const distinctId = correlation.distinctId ?? options.fallbackDistinctId ?? 'server'\n const properties = {\n ...correlation.properties,\n ...(context.method ? { request_method: context.method } : {}),\n ...(context.path ? { request_path: context.path } : {}),\n }\n await Promise.all([\n telemetry.exception(error, distinctId, properties),\n telemetry.log({\n body: 'server function failed',\n severityText: 'error',\n attributes: { ...properties, posthogDistinctId: distinctId },\n }),\n ])\n }\n}\n\nexport function installPostHogServerTelemetryShutdown(\n telemetry: Pick<PostHogServerTelemetry, 'shutdown'>,\n target: PostHogShutdownProcess = process,\n) {\n let shuttingDown = false\n const listeners = new Map<'SIGINT' | 'SIGTERM', () => void>()\n const remove = () => {\n for (const [signal, listener] of listeners) target.off(signal, listener)\n listeners.clear()\n }\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n const listener = () => {\n if (shuttingDown) return\n shuttingDown = true\n void telemetry\n .shutdown()\n .catch(() => undefined)\n .finally(() => {\n remove()\n target.kill(target.pid, signal)\n })\n }\n listeners.set(signal, listener)\n target.on(signal, listener)\n }\n return remove\n}\n\nasync function createPostHogLogProvider(options: ManagedPostHogServerTelemetryOptions): Promise<LogProvider | undefined> {\n if (!options.environment) return undefined\n const [{ OTLPLogExporter }, { resourceFromAttributes }, { BatchLogRecordProcessor, LoggerProvider }] = await Promise.all([\n import('@opentelemetry/exporter-logs-otlp-http'),\n import('@opentelemetry/resources'),\n import('@opentelemetry/sdk-logs'),\n ])\n const exporter = new OTLPLogExporter({\n url: `${options.environment.host.replace(/\\/$/, '')}/i/v1/logs`,\n headers: { Authorization: `Bearer ${options.environment.projectToken}` },\n })\n return new LoggerProvider({\n resource: resourceFromAttributes({\n 'service.name': options.serviceName,\n ...(options.serviceVersion ? { 'service.version': options.serviceVersion } : {}),\n ...(options.deploymentEnvironment ? { 'deployment.environment': options.deploymentEnvironment } : {}),\n }),\n processors: [new BatchLogRecordProcessor({ exporter })],\n })\n}\n\nfunction boundedAttributes(attributes: Record<string, unknown> | undefined) {\n if (!attributes) return undefined\n return Object.fromEntries(\n Object.entries(attributes)\n .slice(0, 64)\n .map(([key, value]) => [key.slice(0, 128), boundedLogValue(value, 0)])\n .filter((entry): entry is [string, PostHogLogValue] => entry[1] !== undefined),\n )\n}\n\nfunction boundedLogValue(value: unknown, depth: number): PostHogLogValue | undefined {\n if (value === null || typeof value === 'number' || typeof value === 'boolean') return value\n if (typeof value === 'string') return boundedString(value)\n if (depth >= 3) return '[Truncated]'\n if (Array.isArray(value)) {\n return value\n .slice(0, 32)\n .map((entry) => boundedLogValue(entry, depth + 1))\n .filter((entry): entry is PostHogLogValue => entry !== undefined)\n }\n if (value === undefined) return undefined\n try {\n return boundedString(JSON.stringify(value))\n } catch {\n return '[Unserializable value]'\n }\n}\n\nfunction boundedString(value: string) {\n return value.slice(0, 2_048)\n}\n\nasync function shutdownPostHogLogProvider(provider: LogProvider | undefined, timeoutMs: number) {\n if (!provider) return\n let timeout: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n provider.shutdown(),\n new Promise<never>((_resolve, reject) => {\n timeout = setTimeout(() => reject(new Error(`PostHog log shutdown timed out after ${timeoutMs}ms`)), timeoutMs)\n timeout.unref?.()\n }),\n ])\n } finally {\n if (timeout) clearTimeout(timeout)\n }\n}\n\nfunction assertShutdownTimeout(timeoutMs: number) {\n if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0) throw new Error('timeoutMs must be a non-negative integer')\n}\n"]}
@@ -1,7 +1,8 @@
1
- export type RpcLogger = (error: unknown, context: {
1
+ export type RpcErrorContext = {
2
2
  method?: string;
3
3
  path?: string;
4
- }) => void;
4
+ };
5
+ export type RpcLogger = (error: unknown, context: RpcErrorContext, request?: Request) => void | Promise<void>;
5
6
  export type RpcOptions = {
6
7
  getRequest?: () => Request;
7
8
  requireMutation?: (request: Request) => void;
@@ -6,7 +6,11 @@ export function createRpc(options = {}) {
6
6
  catch (error) {
7
7
  if (error instanceof Response)
8
8
  throw new Error((await error.text()) || `request failed (${error.status})`, { cause: error });
9
- options.logError?.(error, requestContext(options.getRequest));
9
+ const request = resolveRequest(options.getRequest);
10
+ try {
11
+ await options.logError?.(error, requestContext(request), request);
12
+ }
13
+ catch { }
10
14
  throw error;
11
15
  }
12
16
  }
@@ -20,13 +24,15 @@ export function createRpc(options = {}) {
20
24
  }
21
25
  return { rpc, mutationRpc };
22
26
  }
23
- function requestContext(getRequest) {
27
+ function resolveRequest(getRequest) {
24
28
  try {
25
- const request = getRequest?.();
26
- return request ? { method: request.method, path: new URL(request.url).pathname } : {};
29
+ return getRequest?.();
27
30
  }
28
31
  catch {
29
- return {};
32
+ return undefined;
30
33
  }
31
34
  }
35
+ function requestContext(request) {
36
+ return request ? { method: request.method, path: new URL(request.url).pathname } : {};
37
+ }
32
38
  //# sourceMappingURL=rpc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/server/rpc.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,SAAS,CAAC,OAAO,GAAe,EAAE;IAChD,KAAK,UAAU,GAAG,CAAI,IAA0B;QAC9C,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAA;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,mBAAmB,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;YAC5H,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAA;YAC7D,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED,SAAS,WAAW,CAAI,IAA0B,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,EAAE;QAClF,OAAO,GAAG,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;YAChE,OAAO,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,CAAA;YAClC,OAAO,IAAI,EAAE,CAAA;QACf,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAA;AAC7B,CAAC;AAED,SAAS,cAAc,CAAC,UAAuC;IAC7D,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,UAAU,EAAE,EAAE,CAAA;QAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC","sourcesContent":["export type RpcLogger = (error: unknown, context: { method?: string; path?: string }) => void\n\nexport type RpcOptions = {\n getRequest?: () => Request\n requireMutation?: (request: Request) => void\n logError?: RpcLogger\n}\n\nexport function createRpc(options: RpcOptions = {}) {\n async function rpc<T>(work: () => Promise<T> | T): Promise<T> {\n try {\n return await work()\n } catch (error) {\n if (error instanceof Response) throw new Error((await error.text()) || `request failed (${error.status})`, { cause: error })\n options.logError?.(error, requestContext(options.getRequest))\n throw error\n }\n }\n\n function mutationRpc<T>(work: () => Promise<T> | T, request = options.getRequest?.()) {\n return rpc(() => {\n if (!request) throw new Error('mutation request is unavailable')\n options.requireMutation?.(request)\n return work()\n })\n }\n\n return { rpc, mutationRpc }\n}\n\nfunction requestContext(getRequest: (() => Request) | undefined) {\n try {\n const request = getRequest?.()\n return request ? { method: request.method, path: new URL(request.url).pathname } : {}\n } catch {\n return {}\n }\n}\n"]}
1
+ {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/server/rpc.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,SAAS,CAAC,OAAO,GAAe,EAAE;IAChD,KAAK,UAAU,GAAG,CAAI,IAA0B;QAC9C,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAA;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,mBAAmB,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;YAC5H,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;YAClD,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;YACnE,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED,SAAS,WAAW,CAAI,IAA0B,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,EAAE;QAClF,OAAO,GAAG,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;YAChE,OAAO,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,CAAA;YAClC,OAAO,IAAI,EAAE,CAAA;QACf,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAA;AAC7B,CAAC;AAED,SAAS,cAAc,CAAC,UAAuC;IAC7D,IAAI,CAAC;QACH,OAAO,UAAU,EAAE,EAAE,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,OAA4B;IAClD,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AACvF,CAAC","sourcesContent":["export type RpcErrorContext = { method?: string; path?: string }\nexport type RpcLogger = (error: unknown, context: RpcErrorContext, request?: Request) => void | Promise<void>\n\nexport type RpcOptions = {\n getRequest?: () => Request\n requireMutation?: (request: Request) => void\n logError?: RpcLogger\n}\n\nexport function createRpc(options: RpcOptions = {}) {\n async function rpc<T>(work: () => Promise<T> | T): Promise<T> {\n try {\n return await work()\n } catch (error) {\n if (error instanceof Response) throw new Error((await error.text()) || `request failed (${error.status})`, { cause: error })\n const request = resolveRequest(options.getRequest)\n try {\n await options.logError?.(error, requestContext(request), request)\n } catch {}\n throw error\n }\n }\n\n function mutationRpc<T>(work: () => Promise<T> | T, request = options.getRequest?.()) {\n return rpc(() => {\n if (!request) throw new Error('mutation request is unavailable')\n options.requireMutation?.(request)\n return work()\n })\n }\n\n return { rpc, mutationRpc }\n}\n\nfunction resolveRequest(getRequest: (() => Request) | undefined) {\n try {\n return getRequest?.()\n } catch {\n return undefined\n }\n}\n\nfunction requestContext(request: Request | undefined): RpcErrorContext {\n return request ? { method: request.method, path: new URL(request.url).pathname } : {}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ras-stack",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "description": "Composable full-stack primitives shared across Richard Solomou's applications.",
5
5
  "keywords": [
6
6
  "authentication",
@@ -156,6 +156,10 @@
156
156
  },
157
157
  "devDependencies": {
158
158
  "@changesets/cli": "^2.31.1",
159
+ "@opentelemetry/api-logs": "^0.221.0",
160
+ "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
161
+ "@opentelemetry/resources": "^2.10.0",
162
+ "@opentelemetry/sdk-logs": "^0.221.0",
159
163
  "@playwright/test": "1.62.1",
160
164
  "@posthog/react": "^1.10.3",
161
165
  "@tanstack/react-query": "^5.101.4",
@@ -183,6 +187,10 @@
183
187
  "vitest": "^4.1.10"
184
188
  },
185
189
  "peerDependencies": {
190
+ "@opentelemetry/api-logs": "^0.221.0",
191
+ "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
192
+ "@opentelemetry/resources": "^2.10.0",
193
+ "@opentelemetry/sdk-logs": "^0.221.0",
186
194
  "@posthog/react": ">=1 <2",
187
195
  "@tanstack/react-query": ">=5 <6",
188
196
  "@tanstack/react-start": ">=1 <2",
@@ -197,6 +205,18 @@
197
205
  "tus-js-client": ">=4 <5"
198
206
  },
199
207
  "peerDependenciesMeta": {
208
+ "@opentelemetry/api-logs": {
209
+ "optional": true
210
+ },
211
+ "@opentelemetry/exporter-logs-otlp-http": {
212
+ "optional": true
213
+ },
214
+ "@opentelemetry/resources": {
215
+ "optional": true
216
+ },
217
+ "@opentelemetry/sdk-logs": {
218
+ "optional": true
219
+ },
200
220
  "@posthog/react": {
201
221
  "optional": true
202
222
  },
@@ -1,22 +0,0 @@
1
- import type { AdoptionPolicy, AdoptionSnapshot } from './index.js';
2
- export type FleetRepository = {
3
- repository: string;
4
- ref?: string;
5
- adoption: AdoptionPolicy;
6
- };
7
- export type FleetConfig = {
8
- repositories: FleetRepository[];
9
- };
10
- export type FleetResult = {
11
- repository: string;
12
- ref: string;
13
- drift: string[];
14
- };
15
- export declare function fleetConfig(source: unknown): FleetConfig;
16
- export declare function inspectFleet(config: FleetConfig, load?: (repository: string, ref: string) => Promise<AdoptionSnapshot>): Promise<{
17
- repository: string;
18
- ref: string;
19
- drift: string[];
20
- }[]>;
21
- export declare function fleetMarkdown(results: FleetResult[]): string;
22
- export declare function loadGitHubAdoptionSnapshot(repository: string, ref: string): Promise<AdoptionSnapshot>;
@@ -1,80 +0,0 @@
1
- import { adoptionSnapshotDrift } from './index.js';
2
- export function fleetConfig(source) {
3
- if (!source || typeof source !== 'object' || !('repositories' in source) || !Array.isArray(source.repositories)) {
4
- throw new Error('fleet configuration must contain a repositories array');
5
- }
6
- if (source.repositories.length === 0 || source.repositories.length > 100) {
7
- throw new Error('fleet configuration must contain between 1 and 100 repositories');
8
- }
9
- for (const entry of source.repositories) {
10
- if (!entry || typeof entry !== 'object')
11
- throw new Error('each fleet repository must be an object');
12
- if (!('repository' in entry) || typeof entry.repository !== 'string' || !/^[a-z\d][\w.-]*\/[a-z\d][\w.-]*$/i.test(entry.repository)) {
13
- throw new Error('each fleet repository must use an owner/name identifier');
14
- }
15
- if ('ref' in entry && entry.ref !== undefined && typeof entry.ref !== 'string') {
16
- throw new Error('fleet repository ref must be a string');
17
- }
18
- if (!('adoption' in entry) || !entry.adoption || typeof entry.adoption !== 'object') {
19
- throw new Error('each fleet repository must declare an adoption policy');
20
- }
21
- }
22
- return source;
23
- }
24
- export async function inspectFleet(config, load = loadGitHubAdoptionSnapshot) {
25
- return Promise.all(config.repositories.map(async ({ repository, ref = 'main', adoption }) => ({
26
- repository,
27
- ref,
28
- drift: adoptionSnapshotDrift(await load(repository, ref), adoption),
29
- })));
30
- }
31
- export function fleetMarkdown(results) {
32
- const healthy = results.filter((result) => result.drift.length === 0).length;
33
- const lines = [`# ras-stack fleet health`, '', `${healthy}/${results.length} repositories conform to their declared adoption policy.`, ''];
34
- for (const result of results) {
35
- lines.push(`## ${result.drift.length === 0 ? '✅' : '❌'} ${result.repository}@${result.ref}`, '');
36
- if (result.drift.length === 0)
37
- lines.push('No drift detected.', '');
38
- else
39
- for (const message of result.drift)
40
- lines.push(`- ${message}`);
41
- if (result.drift.length > 0)
42
- lines.push('');
43
- }
44
- return `${lines.join('\n').trimEnd()}\n`;
45
- }
46
- export async function loadGitHubAdoptionSnapshot(repository, ref) {
47
- const headers = { Accept: 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28' };
48
- const token = process.env.GITHUB_TOKEN;
49
- if (token)
50
- headers.Authorization = `Bearer ${token}`;
51
- const tree = await githubJson(`https://api.github.com/repos/${repository}/git/trees/${encodeURIComponent(ref)}?recursive=1`, headers);
52
- if (tree.truncated)
53
- throw new Error(`${repository}@${ref}: GitHub returned a truncated tree`);
54
- const selected = tree.tree.filter((entry) => entry.type === 'blob' && entry.sha && selectedMetadataPath(entry.path));
55
- const sources = await Promise.all(selected.map(async (entry) => {
56
- const blob = await githubJson(`https://api.github.com/repos/${repository}/git/blobs/${entry.sha}`, headers);
57
- if (blob.encoding !== 'base64')
58
- throw new Error(`${repository}@${ref}:${entry.path}: unsupported GitHub blob encoding`);
59
- return [entry.path, Buffer.from(blob.content.replaceAll('\n', ''), 'base64').toString('utf8')];
60
- }));
61
- const files = new Map(sources);
62
- const manifestSource = files.get('package.json');
63
- if (!manifestSource)
64
- throw new Error(`${repository}@${ref}: package.json was not found`);
65
- return { manifest: JSON.parse(manifestSource), files };
66
- }
67
- function selectedMetadataPath(path) {
68
- return (path === 'package.json' ||
69
- path === 'ras-stack.policy.json' ||
70
- path === 'oxlint.json' ||
71
- /^tsconfig(?:\.[^.]+)*\.json$/.test(path) ||
72
- /^\.github\/.*\.ya?ml$/.test(path));
73
- }
74
- async function githubJson(url, headers) {
75
- const response = await fetch(url, { headers });
76
- if (!response.ok)
77
- throw new Error(`GitHub request failed with ${response.status}: ${url}`);
78
- return (await response.json());
79
- }
80
- //# sourceMappingURL=fleet.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fleet.js","sourceRoot":"","sources":["../../src/policy/fleet.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AdoptionPolicy, AdoptionSnapshot } from './index.js'\nimport { adoptionSnapshotDrift } from './index.js'\n\nexport type FleetRepository = {\n repository: string\n ref?: string\n adoption: AdoptionPolicy\n}\n\nexport type FleetConfig = {\n repositories: FleetRepository[]\n}\n\nexport type FleetResult = {\n repository: string\n ref: string\n drift: string[]\n}\n\nexport function fleetConfig(source: unknown): FleetConfig {\n if (!source || typeof source !== 'object' || !('repositories' in source) || !Array.isArray(source.repositories)) {\n throw new Error('fleet configuration must contain a repositories array')\n }\n if (source.repositories.length === 0 || source.repositories.length > 100) {\n throw new Error('fleet configuration must contain between 1 and 100 repositories')\n }\n for (const entry of source.repositories) {\n if (!entry || typeof entry !== 'object') throw new Error('each fleet repository must be an object')\n if (!('repository' in entry) || typeof entry.repository !== 'string' || !/^[a-z\\d][\\w.-]*\\/[a-z\\d][\\w.-]*$/i.test(entry.repository)) {\n throw new Error('each fleet repository must use an owner/name identifier')\n }\n if ('ref' in entry && entry.ref !== undefined && typeof entry.ref !== 'string') {\n throw new Error('fleet repository ref must be a string')\n }\n if (!('adoption' in entry) || !entry.adoption || typeof entry.adoption !== 'object') {\n throw new Error('each fleet repository must declare an adoption policy')\n }\n }\n return source as FleetConfig\n}\n\nexport async function inspectFleet(\n config: FleetConfig,\n load: (repository: string, ref: string) => Promise<AdoptionSnapshot> = loadGitHubAdoptionSnapshot,\n) {\n return Promise.all(\n config.repositories.map(async ({ repository, ref = 'main', adoption }) => ({\n repository,\n ref,\n drift: adoptionSnapshotDrift(await load(repository, ref), adoption),\n })),\n )\n}\n\nexport function fleetMarkdown(results: FleetResult[]) {\n const healthy = results.filter((result) => result.drift.length === 0).length\n const lines = [`# ras-stack fleet health`, '', `${healthy}/${results.length} repositories conform to their declared adoption policy.`, '']\n for (const result of results) {\n lines.push(`## ${result.drift.length === 0 ? '✅' : '❌'} ${result.repository}@${result.ref}`, '')\n if (result.drift.length === 0) lines.push('No drift detected.', '')\n else for (const message of result.drift) lines.push(`- ${message}`)\n if (result.drift.length > 0) lines.push('')\n }\n return `${lines.join('\\n').trimEnd()}\\n`\n}\n\nexport async function loadGitHubAdoptionSnapshot(repository: string, ref: string): Promise<AdoptionSnapshot> {\n const headers: Record<string, string> = { Accept: 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28' }\n const token = process.env.GITHUB_TOKEN\n if (token) headers.Authorization = `Bearer ${token}`\n const tree = await githubJson<GitTree>(\n `https://api.github.com/repos/${repository}/git/trees/${encodeURIComponent(ref)}?recursive=1`,\n headers,\n )\n if (tree.truncated) throw new Error(`${repository}@${ref}: GitHub returned a truncated tree`)\n const selected = tree.tree.filter((entry) => entry.type === 'blob' && entry.sha && selectedMetadataPath(entry.path))\n const sources = await Promise.all(\n selected.map(async (entry) => {\n const blob = await githubJson<GitBlob>(`https://api.github.com/repos/${repository}/git/blobs/${entry.sha}`, headers)\n if (blob.encoding !== 'base64') throw new Error(`${repository}@${ref}:${entry.path}: unsupported GitHub blob encoding`)\n return [entry.path, Buffer.from(blob.content.replaceAll('\\n', ''), 'base64').toString('utf8')] as const\n }),\n )\n const files = new Map(sources)\n const manifestSource = files.get('package.json')\n if (!manifestSource) throw new Error(`${repository}@${ref}: package.json was not found`)\n return { manifest: JSON.parse(manifestSource) as Record<string, unknown>, files }\n}\n\nfunction selectedMetadataPath(path: string) {\n return (\n path === 'package.json' ||\n path === 'ras-stack.policy.json' ||\n path === 'oxlint.json' ||\n /^tsconfig(?:\\.[^.]+)*\\.json$/.test(path) ||\n /^\\.github\\/.*\\.ya?ml$/.test(path)\n )\n}\n\nasync function githubJson<T>(url: string, headers: Record<string, string>): Promise<T> {\n const response = await fetch(url, { headers })\n if (!response.ok) throw new Error(`GitHub request failed with ${response.status}: ${url}`)\n return (await response.json()) as T\n}\n\ntype GitTree = { truncated: boolean; tree: { path: string; type: string; sha?: string }[] }\ntype GitBlob = { encoding: string; content: string }\n"]}