anhedral 0.8.5 → 0.8.6

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 (46) hide show
  1. package/README.md +14 -11
  2. package/assets/anhedral-cli-init-technical.svg +9 -9
  3. package/assets/anhedral-cli-init.svg +9 -9
  4. package/assets/security/LICENSES.txt +63 -0
  5. package/assets/security/audit-deps.mjs +295 -0
  6. package/assets/security/braces-regression.mjs +102 -0
  7. package/assets/security/braces@3.0.3.patch +285 -0
  8. package/assets/security/decode-uri-component@0.2.2.patch +149 -0
  9. package/assets/security/decoder-regression.mjs +37 -0
  10. package/assets/security/forge-regression.mjs +46 -0
  11. package/assets/security/manifest.json +53 -0
  12. package/assets/security/node-forge@1.4.0.patch +26 -0
  13. package/dist/bin.js +39 -2
  14. package/dist/capabilities.d.ts +11 -7
  15. package/dist/capabilities.js +26 -22
  16. package/dist/dependencies.d.ts +3 -2
  17. package/dist/dependencies.js +44 -40
  18. package/dist/evidence-validation.d.ts +2 -0
  19. package/dist/evidence-validation.js +18 -0
  20. package/dist/foundation.d.ts +6 -0
  21. package/dist/foundation.js +49 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +1 -0
  24. package/dist/platforms/desktop.js +3 -3
  25. package/dist/platforms/extension.js +64 -94
  26. package/dist/platforms/mobile.js +1 -1
  27. package/dist/pnpm-policy.d.ts +6 -0
  28. package/dist/pnpm-policy.js +61 -0
  29. package/dist/progress-view.d.ts +57 -0
  30. package/dist/progress-view.js +69 -0
  31. package/dist/progress.d.ts +95 -0
  32. package/dist/progress.js +327 -0
  33. package/dist/project.d.ts +1 -0
  34. package/dist/readiness.d.ts +1 -0
  35. package/dist/readiness.js +7 -2
  36. package/dist/render.d.ts +4 -0
  37. package/dist/render.js +13 -0
  38. package/dist/security-backports.d.ts +3 -0
  39. package/dist/security-backports.js +21 -0
  40. package/dist/standard.d.ts +2 -0
  41. package/dist/standard.js +161 -98
  42. package/dist/transaction.js +34 -5
  43. package/dist/version.d.ts +1 -1
  44. package/dist/version.js +1 -1
  45. package/docs/application-stack-standard.md +85 -669
  46. package/package.json +10 -9
@@ -0,0 +1,53 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "packages": [
4
+ {
5
+ "name": "node-forge",
6
+ "version": "1.4.0",
7
+ "advisoryIds": [
8
+ "GHSA-86w9-cpqp-85rv"
9
+ ],
10
+ "patch": "node-forge@1.4.0.patch",
11
+ "patchSha256": "1b5e41ae73d70d18d2541c50e67d5b408eda4cdd52a426c3d085ec17b8f9b2a9",
12
+ "installedTreeSha256": "bb76d231d6aba9cad2da5f1ec1dee54b64c6720c5fb0b0c6aa0119b661d541c0",
13
+ "provenanceUrls": [
14
+ "https://github.com/digitalbazaar/forge/pull/1152",
15
+ "https://github.com/digitalbazaar/forge/commit/ceba34402e329f0365134f23fe19898756527d65",
16
+ "https://github.com/advisories/GHSA-86w9-cpqp-85rv"
17
+ ],
18
+ "origin": "Upstream PR1152 cardinality backport plus Anhedral strict DER NULL payload validation"
19
+ },
20
+ {
21
+ "name": "braces",
22
+ "version": "3.0.3",
23
+ "advisoryIds": [
24
+ "GHSA-vfj7-8cjw-p6xm"
25
+ ],
26
+ "patch": "braces@3.0.3.patch",
27
+ "patchSha256": "7274fa48b0167311b25a1ef8cf34f4bfe45f6ef58a0cc2813712fb7bb26e2e6a",
28
+ "installedTreeSha256": "c3d6d7c5bc0b6a578effc6d0e4bf6b384cbf04c6a81cfd2c8af10c185acdb0fb",
29
+ "provenanceUrls": [
30
+ "https://github.com/advisories/GHSA-vfj7-8cjw-p6xm",
31
+ "https://github.com/micromatch/braces/commit/74b2db2938fad48a2ea54a9c8bf27a37a62c350d",
32
+ "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
33
+ ],
34
+ "origin": "Anhedral local guarded backport; upstream advisory still lists no patched release"
35
+ },
36
+ {
37
+ "name": "decode-uri-component",
38
+ "version": "0.2.2",
39
+ "advisoryIds": [
40
+ "GHSA-vcc3-ghjq-m6fr"
41
+ ],
42
+ "patch": "decode-uri-component@0.2.2.patch",
43
+ "patchSha256": "f6a891bfb1a3d59b5b72602d4f6e5de5b6f5ee64467a9a24a1e182a3b07f8c54",
44
+ "installedTreeSha256": "821832e5d3622308a9159706eebb0b9ac702576b39e0cd4ea4fcb15cb9d7cbfb",
45
+ "provenanceUrls": [
46
+ "https://github.com/SamVerschueren/decode-uri-component/commit/fa479dafeede7bedf04e5c89aa78f2a78c664005",
47
+ "https://github.com/SamVerschueren/decode-uri-component/releases/tag/v0.5.0",
48
+ "https://github.com/advisories/GHSA-vcc3-ghjq-m6fr"
49
+ ],
50
+ "origin": "Official 0.5.0 security scanner backported to 0.2.2 while preserving its CommonJS API and plus-to-space behavior"
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,26 @@
1
+ diff --git a/lib/rsa.js b/lib/rsa.js
2
+ index a4264822b..430905451 100644
3
+ --- a/lib/rsa.js
4
+ +++ b/lib/rsa.js
5
+ @@ -1168,11 +1168,19 @@ pki.setRsaPublicKey = pki.rsa.setPublicKey = function(n, e) {
6
+ parseAllBytes: options._parseAllDigestBytes
7
+ });
8
+
9
+ - // validate DigestInfo structure and element count
10
+ + // validate DigestInfo structure and element counts (outer DigestInfo
11
+ + // and nested DigestAlgorithm). asn1.validate ignores extra children,
12
+ + // so length must be checked explicitly at each nesting level to
13
+ + // prevent low-exponent PKCS#1 v1.5 signature forgery (CVE-2026-85393).
14
+ + // Anhedral additionally rejects nonempty primitive NULL parameters:
15
+ + // DER NULL has zero content octets; child counts do not check this.
16
+ var capture = {};
17
+ var errors = [];
18
+ if(!asn1.validate(obj, digestInfoValidator, capture, errors) ||
19
+ - obj.value.length !== 2) {
20
+ + obj.value.length !== 2 ||
21
+ + obj.value[0].value.length !==
22
+ + (('parameters' in capture) ? 2 : 1) ||
23
+ + (('parameters' in capture) && capture.parameters !== '')) {
24
+ var error = new Error(
25
+ 'ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 ' +
26
+ 'DigestInfo value.');
package/dist/bin.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import path from 'node:path';
3
+ import { closeSync, openSync, readSync } from 'node:fs';
4
+ import { readProjectProgress, reportProjectProgress, validateProgressReport } from './progress.js';
3
5
  import { inspectProject } from './readiness.js';
4
6
  import { argv } from 'node:process';
5
7
  import { STANDARD_USAGE, parseStandardOptions, scaffoldStandardProject } from './standard.js';
@@ -16,12 +18,16 @@ async function main() {
16
18
  console.log(json ? JSON.stringify({ version: GENERATOR_VERSION }) : GENERATOR_VERSION);
17
19
  return;
18
20
  }
21
+ if (command === 'progress') {
22
+ runProgress(args);
23
+ return;
24
+ }
19
25
  if (command === 'doctor') {
20
26
  runDoctor(args);
21
27
  return;
22
28
  }
23
29
  if (command !== 'new' && command !== 'init') {
24
- throw new Error(`Unknown command: ${command}. Use new, init, doctor, --help, or --version.`);
30
+ throw new Error(`Unknown command: ${command}. Use new, init, doctor, progress, --help, or --version.`);
25
31
  }
26
32
  if (args.some((arg) => ['--help', '-h'].includes(arg))) {
27
33
  console.log(json ? JSON.stringify({ usage: STANDARD_USAGE }) : STANDARD_USAGE);
@@ -31,6 +37,37 @@ async function main() {
31
37
  if (options)
32
38
  await scaffoldStandardProject(options);
33
39
  }
40
+ function runProgress(args) {
41
+ const positional = args.filter(arg => arg !== '--json');
42
+ const [operation, directory, input, ...extra] = positional;
43
+ if (extra.length || !['show', 'report'].includes(operation) || !directory || (operation === 'show' && input) || (operation === 'report' && !input))
44
+ throw new Error('Usage: anhedral progress show <directory> | progress report <directory> <report.json|-> [--json]');
45
+ const root = path.resolve(directory);
46
+ const result = operation === 'show' ? readProjectProgress(root) : reportProjectProgress(root, validateProgressReport(JSON.parse(readProgressInput(input))));
47
+ console.log(JSON.stringify(result, null, 2));
48
+ }
49
+ function readProgressInput(input) {
50
+ const fd = input === '-' ? 0 : openSync(path.resolve(input), 'r');
51
+ const chunks = [];
52
+ let total = 0;
53
+ try {
54
+ for (;;) {
55
+ const chunk = Buffer.alloc(8192);
56
+ const length = readSync(fd, chunk, 0, chunk.length, null);
57
+ if (!length)
58
+ break;
59
+ total += length;
60
+ if (total > 262144)
61
+ throw new Error('Progress report exceeds size limit.');
62
+ chunks.push(chunk.subarray(0, length));
63
+ }
64
+ return Buffer.concat(chunks).toString('utf8');
65
+ }
66
+ finally {
67
+ if (fd !== 0)
68
+ closeSync(fd);
69
+ }
70
+ }
34
71
  function runDoctor(args) {
35
72
  const positional = args.filter((arg) => arg !== '--json');
36
73
  if (positional.length > 1 || positional.some((arg) => arg.startsWith('-')))
@@ -57,7 +94,7 @@ try {
57
94
  catch (error) {
58
95
  const message = error instanceof Error ? error.message : String(error);
59
96
  const code = error instanceof PostCommitError ? 'POST_COMMIT_FAILED'
60
- : command === 'doctor' ? 'INVALID_ARGUMENT'
97
+ : command === 'doctor' || command === 'progress' ? 'INVALID_ARGUMENT'
61
98
  : command === 'new' || command === 'init' ? 'GENERATION_FAILED' : 'UNKNOWN_COMMAND';
62
99
  console.error(json ? JSON.stringify({ error: message, code }) : `Error: ${message}`);
63
100
  process.exitCode = 1;
@@ -9,6 +9,7 @@ export interface Selection {
9
9
  }
10
10
  export interface Capability {
11
11
  purpose: string;
12
+ delivery: 'starter' | 'sdk-factory' | 'integration-boundary';
12
13
  tools: string[];
13
14
  access: string[];
14
15
  credentials: string[];
@@ -25,13 +26,6 @@ export declare const CAPABILITY_REGISTRY: {
25
26
  capabilities: Record<"next" | "expo" | "electron" | "wxt" | "hono" | "neon" | "d1" | "local-data" | "clerk" | "better-auth" | "r2" | "kv" | "realtime" | "queues" | "cron" | "workflows" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog" | "styling", Capability>;
26
27
  };
27
28
  export declare function createSetupPlan(options: StandardOptions): {
28
- schemaVersion: number;
29
- cliVersion: string;
30
- registryVersion: number;
31
- standard: string;
32
- root: string;
33
- products: ("next" | "expo" | "electron" | "wxt" | "hono" | "neon" | "d1" | "local-data" | "clerk" | "better-auth" | "r2" | "kv" | "realtime" | "queues" | "cron" | "workflows" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog" | "styling")[];
34
- hosting: "cloudflare" | "vercel";
35
29
  bootstrap: string[];
36
30
  dryRun: boolean;
37
31
  setup: {
@@ -40,6 +34,7 @@ export declare function createSetupPlan(options: StandardOptions): {
40
34
  capabilities: {
41
35
  status: "starter" | "planned";
42
36
  purpose: string;
37
+ delivery: "starter" | "sdk-factory" | "integration-boundary";
43
38
  tools: string[];
44
39
  access: string[];
45
40
  credentials: string[];
@@ -51,4 +46,13 @@ export declare function createSetupPlan(options: StandardOptions): {
51
46
  completion: string[];
52
47
  resourcesProvisioned: boolean;
53
48
  };
49
+ extensionSurface?: "sidepanel" | "popup" | undefined;
50
+ schemaVersion: number;
51
+ cliVersion: string;
52
+ registryVersion: number;
53
+ standard: string;
54
+ root: string;
55
+ products: ("next" | "expo" | "electron" | "wxt" | "hono" | "neon" | "d1" | "local-data" | "clerk" | "better-auth" | "r2" | "kv" | "realtime" | "queues" | "cron" | "workflows" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog" | "styling")[];
56
+ hosting: "cloudflare" | "vercel";
57
+ layout: "workspace" | "single";
54
58
  };
@@ -1,19 +1,20 @@
1
1
  import { GENERATOR_VERSION } from './version.js';
2
2
  import { PACKAGE_MANAGER } from './dependencies.js';
3
- function capability(purpose, tools, access, credentials, resources, verify, selection = {}) {
4
- return { purpose, tools, access, credentials, resources, verify, selection };
3
+ import { SHADCN_BOOTSTRAP } from './foundation.js';
4
+ function capability(purpose, tools, access, credentials, resources, verify, selection = {}, delivery = 'starter') {
5
+ return { purpose, delivery, tools, access, credentials, resources, verify, selection };
5
6
  }
6
7
  /** Setup requirements describe unfinished starters; they never grant authority or claim readiness. */
7
8
  export const CAPABILITIES = {
8
- next: capability('Web application', ['Browser control'], [], [], ['Next.js app; configure the selected hosting provider'], ['Web flows, accessibility, production hosting build and deployed preview'], { app: true }),
9
- expo: capability('Mobile application', ['Expo/EAS CLI', 'Xcode or Android SDK'], ['Client-owned Apple/Google developer accounts for store delivery'], ['Platform signing credentials; EAS access when used'], ['Expo app; native adapters, signing, store configuration'], ['Native simulator/device flows and signed native build; web export alone is insufficient'], { app: true }),
10
- electron: capability('Desktop application', ['Electron tooling', 'Platform packaging tools'], ['Signing/notarization account for the target platform'], ['Signing credentials; notarization credentials when required'], ['Electron app; packaging and distribution configuration'], ['Packaged desktop app, permissions, update/recovery behavior'], { app: true }),
11
- wxt: capability('Chrome side-panel extension', ['Chrome', 'WXT'], ['Extension store publisher access for distribution'], ['Store publishing credentials when required'], ['Extension app; minimal browser permissions and store package'], ['Installed side panel, background/content messaging and permission behavior'], { app: true }),
9
+ next: capability('Web application with Next.js rendering, routing and server integration', ['Browser testing where applicable'], [], [], ['Next.js app; configure the selected hosting provider'], ['Web flows, accessibility, production hosting build and deployed preview'], { app: true }),
10
+ expo: capability('Mobile application starter with Expo and native platform adapters', ['Expo/EAS CLI', 'Xcode or Android SDK'], ['Client-owned Apple/Google developer accounts for store delivery'], ['Platform signing credentials; EAS access when used'], ['Expo app; native adapters, signing, store configuration'], ['Pass the installed-byte dependency gate; maintain reviewed backports until supported upstream replacements are available', 'Native simulator/device flows and signed native build; web export alone is insufficient'], { app: true }),
11
+ electron: capability('Electron desktop starter for web UI reuse; assess memory, native integration, security and distribution', ['Electron tooling', 'Platform packaging tools'], ['Signing/notarization account for the target platform'], ['Signing credentials; notarization credentials when required'], ['Electron app; packaging and distribution configuration'], ['Packaged desktop app, permissions, update/recovery behavior'], { app: true }),
12
+ wxt: capability('WXT Chrome extension starter; select sidepanel or popup for the task', ['Chrome', 'WXT'], ['Extension store publisher access for distribution'], ['Store publishing credentials when required'], ['Extension app; minimal browser permissions and store package'], ['Installed selected surface, relevant messaging and minimal permission behavior'], { app: true }),
12
13
  hono: capability('Shared OpenAPI backend', ['Wrangler'], ['Cloudflare Workers deployment access'], [], ['API Worker; application routes and authorization'], ['Contract validation, unauthenticated rejection and resource ownership'], { app: true }),
13
- neon: capability('PostgreSQL persistence through Drizzle and Hyperdrive', ['Neon integration or API', 'Wrangler'], ['Neon project/branch access; Cloudflare Hyperdrive configuration access'], ['DATABASE_URL for migrations; separate least-privilege Hyperdrive origin credentials'], ['Neon project/branches, database roles, migrations, HYPERDRIVE binding'], ['Migration and runtime connectivity; disable query caching for correctness-sensitive reads'], { conflicts: ['d1'], workerBinding: true }),
14
+ neon: capability('PostgreSQL persistence through Drizzle and Hyperdrive', ['Neon integration or API', 'Wrangler'], ['Neon project/branch access; Cloudflare Hyperdrive configuration access'], ['DATABASE_URL for migrations; separate least-privilege Hyperdrive origin credentials'], ['Generated Drizzle + Hyperdrive recipe: schema/client factories, migration config and HYPERDRIVE binding; direct Neon serverless access is an existing-project adaptation'], ['Migration and runtime connectivity; disable query caching for correctness-sensitive reads'], { conflicts: ['d1'], workerBinding: true }),
14
15
  d1: capability('SQLite-compatible hosted persistence', ['Wrangler'], ['Cloudflare D1 access'], [], ['D1 database, DB binding and reviewed migrations'], ['Migration, constraints, transactional behavior and runtime queries'], { conflicts: ['neon'], workerBinding: true }),
15
- 'local-data': capability('Device-local SQLite and files', ['Selected platform database/filesystem tooling'], [], [], ['Integration boundary; implement platform adapters and explicit synchronization if needed'], ['Persistence, offline writes and recovery on the selected device']),
16
- clerk: capability('Managed identity', ['Clerk integration or dashboard'], ['Client-owned Clerk project access'], ['Clerk publishable key; server secret key; webhook secret when used'], ['Identity project, allowed URLs, SDK setup and protected routes'], ['Sign-in/out, session expiry, resource authorization and selected native client flows'], { conflicts: ['better-auth'], requiresAny: ['next', 'hono'] }),
16
+ 'local-data': capability('Device-local SQLite and files', ['Selected platform database/filesystem tooling'], [], [], ['Integration boundary; implement platform adapters and explicit synchronization if needed'], ['Persistence, offline writes and recovery on the selected device'], {}, 'integration-boundary'),
17
+ clerk: capability('Managed identity', ['Clerk integration or dashboard'], ['Client-owned Clerk project access'], ['Clerk publishable key; server secret key; webhook secret when used'], ['Identity project, allowed URLs, SDK setup and protected routes'], ['Sign-in/out, session expiry, server-side authorization and selected native flows; route-group names do not authorize requests'], { conflicts: ['better-auth'], requiresAny: ['next', 'hono'] }),
17
18
  'better-auth': capability('Application-managed identity', ['Selected database tooling'], ['Selected database migration/runtime access'], ['BETTER_AUTH_SECRET; BETTER_AUTH_URL'], ['Selected Neon or D1 database, auth schema, trusted origins and application routes'], ['Sign-in/out, session expiry, sign-up policy and resource authorization'], { conflicts: ['clerk'], requiresAny: ['neon', 'd1'], workerBinding: true }),
18
19
  r2: capability('Private remote files', ['Wrangler'], ['Cloudflare R2 bucket/binding access'], [], ['Private R2 bucket and FILES binding; authorized upload/download endpoints'], ['Public bucket access disabled; file ownership enforced; clients receive no storage credentials'], { workerBinding: true }),
19
20
  kv: capability('Eventually consistent cache/configuration', ['Wrangler'], ['Cloudflare KV namespace access'], [], ['KV namespace and CONFIG binding'], ['TTL and propagation behavior; never use KV as transactional authority'], { workerBinding: true }),
@@ -21,16 +22,16 @@ export const CAPABILITIES = {
21
22
  queues: capability('Deferred jobs', ['Wrangler'], ['Cloudflare Queues configuration access'], [], ['Queue, producer/consumer bindings and failure handling'], ['Retries, idempotency, concurrency and dead-letter recovery']),
22
23
  cron: capability('Scheduled work', ['Wrangler'], ['Cloudflare Worker deployment access'], [], ['Scheduled Worker and environment-specific Cron Triggers'], ['Schedule, idempotency, failure visibility and overlapping runs']),
23
24
  workflows: capability('Durable multi-step processing', ['Wrangler'], ['Cloudflare Workflows deployment access'], [], ['Workflow Worker/binding and resumable steps'], ['Retry/resume, idempotency, cancellation and failure recovery']),
24
- openai: capability('Direct OpenAI integration', ['OpenAI integration or API'], ['Client-owned OpenAI project and model access'], ['OPENAI_API_KEY'], ['Server SDK factory; implement application behavior and spending limits'], ['Selected model, failure handling, private inputs and cost controls'], { conflicts: ['ai-sdk'] }),
25
- 'ai-sdk': capability('Streaming AI and provider abstraction', ['Selected AI provider integration or API'], ['Selected provider project/model access'], ['OPENAI_API_KEY for the generated default; selected provider credentials otherwise'], ['Server SDK factory; implement streaming, tools and application behavior'], ['Streaming, tool authorization, interruption and spending limits'], { conflicts: ['openai'] }),
25
+ openai: capability('Direct OpenAI integration', ['OpenAI integration or API'], ['Client-owned OpenAI project and model access'], ['OPENAI_API_KEY'], ['Server SDK factory; implement application behavior and spending limits'], ['Selected model, failure handling, private inputs and cost controls'], { conflicts: ['ai-sdk'] }, 'sdk-factory'),
26
+ 'ai-sdk': capability('Streaming AI and provider abstraction', ['Selected AI provider integration or API'], ['Selected provider project/model access'], ['OPENAI_API_KEY for the generated default; selected provider credentials otherwise'], ['Server SDK factory; implement streaming, tools and application behavior'], ['Streaming, tool authorization, interruption and spending limits'], { conflicts: ['openai'] }, 'sdk-factory'),
26
27
  'workers-ai': capability('Cloudflare-hosted inference', ['Wrangler'], ['Cloudflare Workers AI model access'], [], ['AI binding and selected model invocation'], ['Supported model behavior, authorization and usage limits'], { workerBinding: true }),
27
- resend: capability('Outbound application email', ['Resend integration or API'], ['Client-owned Resend project; sending-domain DNS access'], ['RESEND_API_KEY; webhook secret when used'], ['Verified sending domain, sender and server integration'], ['Delivery, suppression/bounce handling and webhook verification; inbound routing is separate']),
28
- stripe: capability('Web payments and subscriptions', ['Stripe integration or CLI'], ['Client-owned Stripe account; test/live access as required'], ['STRIPE_SECRET_KEY; STRIPE_WEBHOOK_SECRET'], ['Products/prices, signed webhooks and entitlement implementation'], ['Test purchase/cancellation, webhook replay and transactional idempotency']),
29
- revenuecat: capability('Cross-store subscription entitlements alongside Stripe', ['RevenueCat integration or API', 'Selected mobile store tooling'], ['RevenueCat project; App Store/Play Store product access'], ['Platform public SDK keys; server API/webhook credentials as required'], ['Integration guidance only; install platform SDKs and configure offerings/store products'], ['Sandbox purchase/restore, verified webhooks and entitlement reconciliation'], { requiresAll: ['stripe'] }),
30
- sentry: capability('Additional runtime/crash diagnostics', ['Sentry integration or API'], ['Client-owned Sentry project access'], ['SENTRY_DSN; source-map upload token when required'], ['Integration boundary; install/init runtime SDKs and configure private source-map uploads'], ['Captured runtime errors, source maps, privacy filtering and alert delivery']),
31
- basin: capability('Cloudflare analytical events', ['Wrangler', 'Cloudflare integration or API'], ['Cloudflare analytics/Pipelines access; plan supporting selected services; Pipelines Setup grant for managed R2 sink setup'], [], ['Basin dataset/stream, ANALYTICS binding, optional private R2 sink and authorized sink writes'], ['Private ingestion, event delivery, query results and archive retrieval when selected'], { workerBinding: true }),
32
- posthog: capability('Specialized product analytics', ['PostHog integration or API'], ['Client-owned PostHog project access'], ['POSTHOG_API_KEY; POSTHOG_HOST; separate privileged key if needed'], ['Analytics client factory; implement consent, event schema and privacy filtering'], ['Event receipt, consent behavior and redaction of private data']),
33
- styling: capability('Shared styling definitions', [], [], [], ['Styling package; implement actual shared colors, typography, spacing and themes'], ['Consistency across selected interfaces; preserve platform-specific components']),
28
+ resend: capability('Outbound application email', ['Resend integration or API'], ['Client-owned Resend project; sending-domain DNS access'], ['RESEND_API_KEY; webhook secret when used'], ['Verified sending domain, sender and server integration'], ['Delivery, suppression/bounce handling and webhook verification; inbound routing is separate'], {}, 'sdk-factory'),
29
+ stripe: capability('Web payments and subscriptions', ['Stripe integration or CLI'], ['Client-owned Stripe account; test/live access as required'], ['STRIPE_SECRET_KEY; STRIPE_WEBHOOK_SECRET'], ['Server SDK factory; implement products/prices, signed webhooks and entitlements'], ['Test purchase/cancellation, webhook replay and transactional idempotency'], {}, 'sdk-factory'),
30
+ revenuecat: capability('Cross-store subscription entitlements alongside Stripe', ['RevenueCat integration or API', 'Selected mobile store tooling'], ['RevenueCat project; App Store/Play Store product access'], ['Platform public SDK keys; server API/webhook credentials as required'], ['Integration guidance only; install platform SDKs and configure offerings/store products'], ['Sandbox purchase/restore, verified webhooks and entitlement reconciliation'], { requiresAll: ['stripe'] }, 'integration-boundary'),
31
+ sentry: capability('Additional runtime/crash diagnostics', ['Sentry integration or API'], ['Client-owned Sentry project access'], ['SENTRY_DSN; source-map upload token when required'], ['Integration boundary; install/init runtime SDKs and configure private source-map uploads'], ['Captured runtime errors, source maps, privacy filtering and alert delivery'], {}, 'integration-boundary'),
32
+ basin: capability('Basin ingestion and analytical SQL; product analytics needs its own event model and reports', ['Wrangler', 'Cloudflare integration or API'], ['Cloudflare analytics/Pipelines access; plan supporting selected services; Pipelines Setup grant for managed R2 sink setup'], [], ['EventStream interface and ANALYTICS binding; provision Basin stream, pipeline/table, retention and reports separately; optional private R2 sink'], ['Private ingestion, event delivery, query results and archive retrieval when selected'], { workerBinding: true }, 'integration-boundary'),
33
+ posthog: capability('Specialized product analytics', ['PostHog integration or API'], ['Client-owned PostHog project access'], ['POSTHOG_API_KEY; POSTHOG_HOST; separate privileged key if needed'], ['Analytics client factory; implement consent, event schema and privacy filtering'], ['Event receipt, consent behavior and redaction of private data'], {}, 'sdk-factory'),
34
+ styling: capability('Shared styling definitions', [], [], [], ['Styling package; implement actual shared colors, typography, spacing and themes'], ['Consistency across selected interfaces; preserve platform-specific components'], {}, 'integration-boundary'),
34
35
  };
35
36
  export function resolveStandardProducts(selected, hosting, hostingSpecified) {
36
37
  if (![...selected].some((id) => CAPABILITIES[id].selection.app))
@@ -64,7 +65,7 @@ const HOSTING_SETUP = {
64
65
  },
65
66
  vercel: {
66
67
  tools: ['Vercel integration or CLI'],
67
- access: ['Client-owned Vercel project access; approved architecture exception'],
68
+ access: ['Client-owned Vercel project access'],
68
69
  },
69
70
  };
70
71
  export function createSetupPlan(options) {
@@ -76,14 +77,17 @@ export function createSetupPlan(options) {
76
77
  return {
77
78
  schemaVersion: 1, cliVersion: GENERATOR_VERSION, registryVersion: CAPABILITY_REGISTRY.schemaVersion,
78
79
  standard: 'anhedral-application-stack', root: options.root, products: options.products, hosting: options.hosting,
79
- bootstrap: ['pnpm', 'dlx', 'shadcn@latest', 'init', '--monorepo', '--template', 'next'], dryRun: options.dryRun,
80
+ layout: options.layout ?? 'workspace',
81
+ ...(options.products.includes('wxt') ? { extensionSurface: options.extensionSurface ?? 'sidepanel' } : {}),
82
+ bootstrap: options.products.includes('next') ? [...SHADCN_BOOTSTRAP] : [], dryRun: options.dryRun,
80
83
  setup: {
81
- tools: [...new Set(['Git', 'Node.js 22.13+ for generated workspaces', PACKAGE_MANAGER.replace('@', ' '), 'GitHub integration or gh', 'Fallow', 'Cloudflare security audit skill',
84
+ tools: [...new Set(['Git', 'Node.js 22.22+ for generated workspaces', PACKAGE_MANAGER.replace('@', ' '), 'GitHub integration or gh', 'Fallow',
85
+ ...(providers.includes(HOSTING_SETUP.cloudflare) ? ['Cloudflare security audit skill'] : []),
82
86
  ...providers.flatMap((provider) => provider.tools), ...capabilities.flatMap((item) => item.tools)])],
83
87
  access: ['Client-owned repository and CI access', ...providers.flatMap((provider) => provider.access)],
84
88
  capabilities,
85
89
  completion: ['Discover available plugins and fallbacks; verify the selected account and scope', 'Configure separate environments, bindings and server-side secrets',
86
- 'Implement starter behavior and meaningful tests', 'Run pnpm check plus platform/provider verification', 'Verify deployed preview; obtain required production authorization; record rollback and operating ownership'],
90
+ 'Implement starter behavior and meaningful tests', 'Run repeatable local gates and separately record time-sensitive advisory/provider checks; verify platform behavior', 'Verify deployed preview; obtain required production authorization; record rollback and operating ownership'],
87
91
  resourcesProvisioned: false,
88
92
  },
89
93
  };
@@ -6,5 +6,6 @@ export declare const DESKTOP_DEPENDENCIES: DependencyGroup;
6
6
  export declare const EXTENSION_DEPENDENCIES: DependencyGroup;
7
7
  export declare const MOBILE_APP_DEPENDENCIES: DependencyGroup;
8
8
  export declare const MOBILE_NATIVEWIND_DEPENDENCIES: DependencyGroup;
9
- export declare const PACKAGE_MANAGER = "pnpm@10.34.5";
10
- export declare const EAS_CLI_VERSION = "21.0.1";
9
+ export declare const PACKAGE_MANAGER = "pnpm@12.9.1";
10
+ export declare const EAS_CLI_VERSION = "24.11.0";
11
+ export declare const TURBO_VERSION = "2.11.7";
@@ -1,86 +1,86 @@
1
1
  export const DESKTOP_DEPENDENCIES = {
2
2
  "dependencies": {
3
3
  // renovate: datasource=npm depName=react
4
- "react": '19.2.7',
4
+ "react": '19.3.0',
5
5
  // renovate: datasource=npm depName=react-dom
6
- "react-dom": '19.2.7',
6
+ "react-dom": '19.3.0',
7
7
  // renovate: datasource=npm depName=clsx
8
8
  "clsx": '2.1.1',
9
9
  // renovate: datasource=npm depName=tailwind-merge
10
- "tailwind-merge": '3.4.0'
10
+ "tailwind-merge": '3.7.0'
11
11
  },
12
12
  "devDependencies": {
13
13
  // renovate: datasource=npm depName=@tailwindcss/postcss
14
- "@tailwindcss/postcss": '4.1.18',
14
+ "@tailwindcss/postcss": '4.3.3',
15
15
  // renovate: datasource=npm depName=@vitejs/plugin-react
16
- "@vitejs/plugin-react": '5.2.0',
16
+ "@vitejs/plugin-react": '6.1.2',
17
17
  // renovate: datasource=npm depName=@types/node
18
18
  "@types/node": '20.19.43',
19
19
  // renovate: datasource=npm depName=@types/react
20
- "@types/react": '19.2.7',
20
+ "@types/react": '19.3.0',
21
21
  // renovate: datasource=npm depName=@types/react-dom
22
- "@types/react-dom": '19.2.3',
22
+ "@types/react-dom": '19.3.0',
23
23
  // renovate: datasource=npm depName=electron
24
- "electron": '43.7.8',
24
+ "electron": '44.6.0',
25
25
  // renovate: datasource=npm depName=electron-builder
26
26
  "electron-builder": '26.15.3',
27
- "eslint": '10.0.3',
27
+ "eslint": '9.39.5',
28
28
  // renovate: datasource=npm depName=tailwindcss
29
- "tailwindcss": '4.1.18',
29
+ "tailwindcss": '4.3.3',
30
30
  // renovate: datasource=npm depName=postcss
31
- "postcss": '8.5.23',
31
+ "postcss": '8.5.29',
32
32
  // renovate: datasource=npm depName=typescript
33
- "typescript": '5.9.3',
33
+ "typescript": '6.0.3',
34
34
  // renovate: datasource=npm depName=vite
35
- "vite": '7.3.6'
35
+ "vite": '8.3.3'
36
36
  }
37
37
  };
38
38
  export const EXTENSION_DEPENDENCIES = {
39
39
  "dependencies": {
40
40
  // renovate: datasource=npm depName=react
41
- "react": '19.2.7',
41
+ "react": '19.3.0',
42
42
  // renovate: datasource=npm depName=react-dom
43
- "react-dom": '19.2.7',
43
+ "react-dom": '19.3.0',
44
44
  // renovate: datasource=npm depName=clsx
45
45
  "clsx": '2.1.1',
46
46
  // renovate: datasource=npm depName=tailwind-merge
47
- "tailwind-merge": '3.4.0'
47
+ "tailwind-merge": '3.7.0'
48
48
  },
49
49
  "devDependencies": {
50
50
  // renovate: datasource=npm depName=@types/chrome
51
- "@types/chrome": '0.1.9',
51
+ "@types/chrome": '0.3.4',
52
52
  // renovate: datasource=npm depName=@types/react
53
- "@types/react": '19.2.7',
53
+ "@types/react": '19.3.0',
54
54
  // renovate: datasource=npm depName=@types/react-dom
55
- "@types/react-dom": '19.2.3',
55
+ "@types/react-dom": '19.3.0',
56
56
  // renovate: datasource=npm depName=@wxt-dev/module-react
57
57
  "@wxt-dev/module-react": '1.2.2',
58
- // renovate: datasource=npm depName=autoprefixer
59
- "autoprefixer": '10.4.23',
58
+ // renovate: datasource=npm depName=@tailwindcss/postcss
59
+ "@tailwindcss/postcss": '4.3.3',
60
60
  // renovate: datasource=npm depName=postcss
61
- "postcss": '8.5.23',
61
+ "postcss": '8.5.29',
62
62
  // renovate: datasource=npm depName=tailwindcss
63
- "tailwindcss": '3.4.19',
63
+ "tailwindcss": '4.3.3',
64
64
  // renovate: datasource=npm depName=typescript
65
- "typescript": '5.9.3',
65
+ "typescript": '6.0.3',
66
66
  // renovate: datasource=npm depName=wxt
67
- "wxt": '0.20.27',
67
+ "wxt": '0.21.4',
68
68
  // renovate: datasource=npm depName=vite
69
- "vite": '7.3.6'
69
+ "vite": '8.3.3'
70
70
  }
71
71
  };
72
72
  export const MOBILE_APP_DEPENDENCIES = {
73
73
  "dependencies": {
74
74
  // renovate: datasource=npm depName=@rn-primitives/portal
75
- "@rn-primitives/portal": '1.5.2',
75
+ "@rn-primitives/portal": '1.5.3',
76
76
  // renovate: datasource=npm depName=clsx
77
77
  "clsx": '2.1.1',
78
78
  // renovate: datasource=npm depName=expo
79
- "expo": '57.0.8',
79
+ "expo": '57.0.27',
80
80
  // renovate: datasource=npm depName=expo-linking
81
- "expo-linking": '57.0.4',
81
+ "expo-linking": '57.0.12',
82
82
  // renovate: datasource=npm depName=expo-router
83
- "expo-router": '57.0.8',
83
+ "expo-router": '57.0.25',
84
84
  // renovate: datasource=npm depName=expo-status-bar
85
85
  "expo-status-bar": '57.0.1',
86
86
  // renovate: datasource=npm depName=react
@@ -88,31 +88,33 @@ export const MOBILE_APP_DEPENDENCIES = {
88
88
  // renovate: datasource=npm depName=react-dom
89
89
  "react-dom": '19.2.3',
90
90
  // renovate: datasource=npm depName=react-native
91
- "react-native": '0.86.0',
91
+ "react-native": '0.86.3',
92
92
  // renovate: datasource=npm depName=react-native-reanimated
93
- "react-native-reanimated": '4.5.0',
93
+ "react-native-reanimated": '4.5.1',
94
94
  // renovate: datasource=npm depName=react-native-safe-area-context
95
95
  "react-native-safe-area-context": '5.7.0',
96
96
  // renovate: datasource=npm depName=react-native-screens
97
97
  "react-native-screens": '4.26.2',
98
98
  // renovate: datasource=npm depName=react-native-worklets
99
- "react-native-worklets": '0.10.0',
99
+ "react-native-worklets": '0.10.1',
100
100
  // renovate: datasource=npm depName=react-native-web
101
101
  "react-native-web": '0.21.2',
102
102
  // renovate: datasource=npm depName=tailwind-merge
103
- "tailwind-merge": '3.5.0',
103
+ "tailwind-merge": '3.7.0',
104
104
  // renovate: datasource=npm depName=tailwindcss-animate
105
105
  "tailwindcss-animate": '1.0.7'
106
106
  },
107
107
  "devDependencies": {
108
108
  // renovate: datasource=npm depName=@babel/core
109
109
  "@babel/core": '7.29.7',
110
+ // renovate: datasource=npm depName=@babel/plugin-transform-react-jsx
111
+ "@babel/plugin-transform-react-jsx": '7.29.7',
110
112
  // renovate: datasource=npm depName=babel-preset-expo
111
113
  "babel-preset-expo": '57.0.14',
112
114
  // renovate: datasource=npm depName=@testing-library/dom
113
- "@testing-library/dom": '10.4.1',
115
+ "@testing-library/dom": '10.4.2',
114
116
  // renovate: datasource=npm depName=@react-native/metro-config
115
- "@react-native/metro-config": '0.86.0',
117
+ "@react-native/metro-config": '0.86.3',
116
118
  // renovate: datasource=npm depName=@types/react
117
119
  "@types/react": '19.2.10',
118
120
  // renovate: datasource=npm depName=typescript
@@ -122,13 +124,15 @@ export const MOBILE_APP_DEPENDENCIES = {
122
124
  export const MOBILE_NATIVEWIND_DEPENDENCIES = {
123
125
  "dependencies": {
124
126
  // renovate: datasource=npm depName=nativewind
125
- "nativewind": '4.2.6',
127
+ "nativewind": '4.2.7',
126
128
  // renovate: datasource=npm depName=react-native-css-interop
127
- "react-native-css-interop": '0.2.6',
129
+ "react-native-css-interop": '0.2.7',
128
130
  // renovate: datasource=npm depName=tailwindcss
129
131
  "tailwindcss": '3.4.19'
130
132
  }
131
133
  };
132
- export const PACKAGE_MANAGER = 'pnpm@10.34.5';
134
+ export const PACKAGE_MANAGER = 'pnpm@12.9.1';
133
135
  // renovate: datasource=npm depName=eas-cli
134
- export const EAS_CLI_VERSION = '21.0.1';
136
+ export const EAS_CLI_VERSION = '24.11.0';
137
+ // renovate: datasource=npm depName=turbo
138
+ export const TURBO_VERSION = '2.11.7';
@@ -0,0 +1,2 @@
1
+ export declare function isNonSecretEvidence(value: string): boolean;
2
+ export declare function isProgressEnvironment(value: string): boolean;
@@ -0,0 +1,18 @@
1
+ /** Shared, dependency-free validation for public progress and plugin evidence. */
2
+ const forbiddenEvidence = /(?:postgres(?:ql)?:\/\/|bearer\s+\S+|(?:api[_-]?key|secret|password|token)\s*[:=]\s*\S+|\b(?:sk|pk)_(?:live|test|proj)_\S+|\bsk-(?:proj-|live_|test_)?[a-zA-Z0-9_-]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|\bAKIA[A-Z0-9]{16}\b|\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]+|\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)/i;
3
+ export function isNonSecretEvidence(value) {
4
+ if (/[\x00-\x08\x0b-\x1f]/.test(value) || forbiddenEvidence.test(value))
5
+ return false;
6
+ for (const candidate of value.matchAll(/\b(?:https?|wss?):[^\s<>"']+/gi)) {
7
+ try {
8
+ const target = new URL(candidate[0]);
9
+ if (target.username || target.password)
10
+ return false;
11
+ }
12
+ catch { /* Non-URL prose is validated by the bounded text contract. */ }
13
+ }
14
+ return true;
15
+ }
16
+ export function isProgressEnvironment(value) {
17
+ return /^[a-zA-Z0-9_-][a-zA-Z0-9._-]{0,79}$/.test(value) && !['__proto__', 'constructor', 'prototype'].includes(value);
18
+ }
@@ -0,0 +1,6 @@
1
+ export declare const SHADCN_VERSION = "4.21.3";
2
+ export declare const SHADCN_BOOTSTRAP: string[];
3
+ /** A shared JS workspace is independent of any UI framework bootstrap. */
4
+ export declare function scaffoldFoundation(root: string): void;
5
+ /** The intentionally small single-app recipe has no shared packages or task runner. */
6
+ export declare function flattenSingleApi(root: string, name: string): void;
@@ -0,0 +1,49 @@
1
+ import { cpSync, readFileSync, rmSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { writeFile } from './util.js';
4
+ import { PACKAGE_MANAGER, TURBO_VERSION } from './dependencies.js';
5
+ import { generatedPnpmPolicy, setPnpmPolicy } from './pnpm-policy.js';
6
+ // Curated input version; upstream registry templates still require compatibility checks.
7
+ export const SHADCN_VERSION = '4.21.3';
8
+ export const SHADCN_BOOTSTRAP = ['pnpm', 'dlx', `shadcn@${SHADCN_VERSION}`, 'init', '--monorepo', '--template', 'next'];
9
+ const json = (root, file, value) => writeFile(path.join(root, file), JSON.stringify(value, null, 2) + '\n');
10
+ /** A shared JS workspace is independent of any UI framework bootstrap. */
11
+ export function scaffoldFoundation(root) {
12
+ json(root, 'package.json', { name: 'anhedral-workspace', private: true, packageManager: PACKAGE_MANAGER, devDependencies: { turbo: TURBO_VERSION } });
13
+ writeFile(path.join(root, 'pnpm-workspace.yaml'), 'packages:\n - "apps/*"\n - "packages/*"\n');
14
+ json(root, 'turbo.json', { $schema: 'https://turbo.build/schema.json', tasks: { build: { dependsOn: ['^build'] }, lint: { dependsOn: ['^lint'] }, typecheck: { dependsOn: ['^typecheck'] }, dev: { cache: false, persistent: true } } });
15
+ json(root, 'packages/eslint-config/package.json', { name: '@workspace/eslint-config', private: true, type: 'module', exports: { './base': './base.js', './react-internal': './react-internal.js' }, devDependencies: { eslint: '9.39.5', '@eslint/js': '9.39.5', 'typescript-eslint': '8.71.1', 'eslint-plugin-react-hooks': '7.1.1', typescript: '6.0.3' } });
16
+ writeFile(path.join(root, 'packages/eslint-config/base.js'), `import js from '@eslint/js';
17
+ import ts from 'typescript-eslint';
18
+ export const config = [js.configs.recommended, ...ts.configs.recommended, { ignores: ['**/dist/**', '**/.output/**', '**/.wxt/**', '**/.expo/**', '**/node_modules/**'] }];
19
+ `);
20
+ writeFile(path.join(root, 'packages/eslint-config/react-internal.js'), `import { config as base } from './base.js';
21
+ import hooks from 'eslint-plugin-react-hooks';
22
+ export const config = [...base, { files: ['**/*.{tsx,jsx}'], plugins: { 'react-hooks': hooks }, rules: { 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn' } }];
23
+ `);
24
+ json(root, 'packages/typescript-config/package.json', { name: '@workspace/typescript-config', private: true, exports: { './base.json': './base.json' } });
25
+ json(root, 'packages/typescript-config/base.json', { compilerOptions: { target: 'ES2022', module: 'ESNext', moduleResolution: 'Bundler', strict: true, skipLibCheck: true, noEmit: true } });
26
+ }
27
+ /** The intentionally small single-app recipe has no shared packages or task runner. */
28
+ export function flattenSingleApi(root, name) {
29
+ const read = (file) => JSON.parse(readFileSync(path.join(root, file), 'utf8'));
30
+ const workspace = read('package.json');
31
+ const app = read('apps/api/package.json');
32
+ cpSync(path.join(root, 'apps/api'), root, { recursive: true });
33
+ delete app.devDependencies['@workspace/eslint-config'];
34
+ const config = read('packages/eslint-config/package.json');
35
+ delete config.devDependencies['eslint-plugin-react-hooks'];
36
+ // The standalone API has no React/Babel browser-target dependency chain.
37
+ const overrides = generatedPnpmPolicy(root, 'overrides');
38
+ for (const key of ['baseline-browser-mapping@<2.11.0', 'browserslist@<4.28.7'])
39
+ delete overrides[key];
40
+ const scripts = { ...workspace.scripts, ...app.scripts, test: 'node --test', 'audit:full': 'fallow --fail-on-issues' };
41
+ json(root, 'package.json', { ...app, name, packageManager: PACKAGE_MANAGER, engines: workspace.engines, scripts, devDependencies: { ...config.devDependencies, ...app.devDependencies, eslint: '9.39.5', fallow: workspace.devDependencies.fallow } });
42
+ writeFile(path.join(root, 'eslint.config.mjs'), `import js from '@eslint/js';
43
+ import ts from 'typescript-eslint';
44
+ export default [js.configs.recommended, ...ts.configs.recommended, { ignores: ['dist/**', '.wrangler/**', 'worker-configuration.d.ts'] }, { files: ['**/*.mjs'], languageOptions: { globals: { process: 'readonly' } } }, { files: ['**/*.ts'], rules: { 'no-undef': 'off', 'no-unused-vars': 'off' } }];
45
+ `);
46
+ for (const file of ['apps', 'packages', 'turbo.json', 'pnpm-workspace.yaml'])
47
+ rmSync(path.join(root, file), { recursive: true, force: true });
48
+ setPnpmPolicy(root, { allowBuilds: { esbuild: true, workerd: true }, overrides });
49
+ }
package/dist/index.d.ts CHANGED
@@ -4,3 +4,5 @@ export { GENERATOR_VERSION } from './version.js';
4
4
  export { CAPABILITIES, CAPABILITY_REGISTRY, createSetupPlan } from './capabilities.js';
5
5
  export type { Capability } from './capabilities.js';
6
6
  export { inspectProject, validateSetupPlan } from './readiness.js';
7
+ export { PROGRESS_FILE, MILESTONES, readProjectProgress, reportProjectProgress, validateProgressReport, validateProjectProgress, projectFingerprint, evaluateProjectProgress } from './progress.js';
8
+ export type { ProjectProgress, ProgressEnvironment, ProgressReport, ProgressObservation, DiscoveryObservation, Milestone } from './progress.js';
package/dist/index.js CHANGED
@@ -2,3 +2,4 @@ export { STANDARD_PRODUCTS, STANDARD_USAGE, parseStandardOptions, applyStandardO
2
2
  export { GENERATOR_VERSION } from './version.js';
3
3
  export { CAPABILITIES, CAPABILITY_REGISTRY, createSetupPlan } from './capabilities.js';
4
4
  export { inspectProject, validateSetupPlan } from './readiness.js';
5
+ export { PROGRESS_FILE, MILESTONES, readProjectProgress, reportProjectProgress, validateProgressReport, validateProjectProgress, projectFingerprint, evaluateProjectProgress } from './progress.js';
@@ -142,8 +142,8 @@ function stop(child) {
142
142
  if (child && child.exitCode === null && !child.killed) child.kill('SIGTERM');
143
143
  }
144
144
 
145
- async function isServerReady(url) {
146
- try { return (await fetch(url)).ok; }
145
+ async function isServerReady(url, timeoutMs) {
146
+ try { return (await fetch(url, { signal: AbortSignal.timeout(timeoutMs) })).ok; }
147
147
  catch { return false; }
148
148
  }
149
149
 
@@ -151,7 +151,7 @@ async function waitForServer(server, url, timeoutMs = 30_000) {
151
151
  const deadline = Date.now() + timeoutMs;
152
152
  while (Date.now() < deadline) {
153
153
  if (server.exitCode !== null) throw new Error('Vite exited before the dev server was ready.');
154
- if (await isServerReady(url)) return;
154
+ if (await isServerReady(url, Math.max(1, Math.min(1000, deadline - Date.now())))) return;
155
155
  await new Promise((resolve) => setTimeout(resolve, 150));
156
156
  }
157
157
  throw new Error('Timed out waiting for Vite at ' + url + '.');