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.
- package/README.md +14 -11
- package/assets/anhedral-cli-init-technical.svg +9 -9
- package/assets/anhedral-cli-init.svg +9 -9
- package/assets/security/LICENSES.txt +63 -0
- package/assets/security/audit-deps.mjs +295 -0
- package/assets/security/braces-regression.mjs +102 -0
- package/assets/security/braces@3.0.3.patch +285 -0
- package/assets/security/decode-uri-component@0.2.2.patch +149 -0
- package/assets/security/decoder-regression.mjs +37 -0
- package/assets/security/forge-regression.mjs +46 -0
- package/assets/security/manifest.json +53 -0
- package/assets/security/node-forge@1.4.0.patch +26 -0
- package/dist/bin.js +39 -2
- package/dist/capabilities.d.ts +11 -7
- package/dist/capabilities.js +26 -22
- package/dist/dependencies.d.ts +3 -2
- package/dist/dependencies.js +44 -40
- package/dist/evidence-validation.d.ts +2 -0
- package/dist/evidence-validation.js +18 -0
- package/dist/foundation.d.ts +6 -0
- package/dist/foundation.js +49 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/platforms/desktop.js +3 -3
- package/dist/platforms/extension.js +64 -94
- package/dist/platforms/mobile.js +1 -1
- package/dist/pnpm-policy.d.ts +6 -0
- package/dist/pnpm-policy.js +61 -0
- package/dist/progress-view.d.ts +57 -0
- package/dist/progress-view.js +69 -0
- package/dist/progress.d.ts +95 -0
- package/dist/progress.js +327 -0
- package/dist/project.d.ts +1 -0
- package/dist/readiness.d.ts +1 -0
- package/dist/readiness.js +7 -2
- package/dist/render.d.ts +4 -0
- package/dist/render.js +13 -0
- package/dist/security-backports.d.ts +3 -0
- package/dist/security-backports.js +21 -0
- package/dist/standard.d.ts +2 -0
- package/dist/standard.js +161 -98
- package/dist/transaction.js +34 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/application-stack-standard.md +85 -669
- 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;
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -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
|
};
|
package/dist/capabilities.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { GENERATOR_VERSION } from './version.js';
|
|
2
2
|
import { PACKAGE_MANAGER } from './dependencies.js';
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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('
|
|
11
|
-
wxt: capability('Chrome
|
|
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'], ['
|
|
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,
|
|
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'], ['
|
|
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('
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
};
|
package/dist/dependencies.d.ts
CHANGED
|
@@ -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
|
-
export declare const EAS_CLI_VERSION = "
|
|
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";
|
package/dist/dependencies.js
CHANGED
|
@@ -1,86 +1,86 @@
|
|
|
1
1
|
export const DESKTOP_DEPENDENCIES = {
|
|
2
2
|
"dependencies": {
|
|
3
3
|
// renovate: datasource=npm depName=react
|
|
4
|
-
"react": '19.
|
|
4
|
+
"react": '19.3.0',
|
|
5
5
|
// renovate: datasource=npm depName=react-dom
|
|
6
|
-
"react-dom": '19.
|
|
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.
|
|
10
|
+
"tailwind-merge": '3.7.0'
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
// renovate: datasource=npm depName=@tailwindcss/postcss
|
|
14
|
-
"@tailwindcss/postcss": '4.
|
|
14
|
+
"@tailwindcss/postcss": '4.3.3',
|
|
15
15
|
// renovate: datasource=npm depName=@vitejs/plugin-react
|
|
16
|
-
"@vitejs/plugin-react": '
|
|
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.
|
|
20
|
+
"@types/react": '19.3.0',
|
|
21
21
|
// renovate: datasource=npm depName=@types/react-dom
|
|
22
|
-
"@types/react-dom": '19.
|
|
22
|
+
"@types/react-dom": '19.3.0',
|
|
23
23
|
// renovate: datasource=npm depName=electron
|
|
24
|
-
"electron": '
|
|
24
|
+
"electron": '44.6.0',
|
|
25
25
|
// renovate: datasource=npm depName=electron-builder
|
|
26
26
|
"electron-builder": '26.15.3',
|
|
27
|
-
"eslint": '
|
|
27
|
+
"eslint": '9.39.5',
|
|
28
28
|
// renovate: datasource=npm depName=tailwindcss
|
|
29
|
-
"tailwindcss": '4.
|
|
29
|
+
"tailwindcss": '4.3.3',
|
|
30
30
|
// renovate: datasource=npm depName=postcss
|
|
31
|
-
"postcss": '8.5.
|
|
31
|
+
"postcss": '8.5.29',
|
|
32
32
|
// renovate: datasource=npm depName=typescript
|
|
33
|
-
"typescript": '
|
|
33
|
+
"typescript": '6.0.3',
|
|
34
34
|
// renovate: datasource=npm depName=vite
|
|
35
|
-
"vite": '
|
|
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.
|
|
41
|
+
"react": '19.3.0',
|
|
42
42
|
// renovate: datasource=npm depName=react-dom
|
|
43
|
-
"react-dom": '19.
|
|
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.
|
|
47
|
+
"tailwind-merge": '3.7.0'
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
// renovate: datasource=npm depName=@types/chrome
|
|
51
|
-
"@types/chrome": '0.
|
|
51
|
+
"@types/chrome": '0.3.4',
|
|
52
52
|
// renovate: datasource=npm depName=@types/react
|
|
53
|
-
"@types/react": '19.
|
|
53
|
+
"@types/react": '19.3.0',
|
|
54
54
|
// renovate: datasource=npm depName=@types/react-dom
|
|
55
|
-
"@types/react-dom": '19.
|
|
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
|
|
59
|
-
"
|
|
58
|
+
// renovate: datasource=npm depName=@tailwindcss/postcss
|
|
59
|
+
"@tailwindcss/postcss": '4.3.3',
|
|
60
60
|
// renovate: datasource=npm depName=postcss
|
|
61
|
-
"postcss": '8.5.
|
|
61
|
+
"postcss": '8.5.29',
|
|
62
62
|
// renovate: datasource=npm depName=tailwindcss
|
|
63
|
-
"tailwindcss": '3.
|
|
63
|
+
"tailwindcss": '4.3.3',
|
|
64
64
|
// renovate: datasource=npm depName=typescript
|
|
65
|
-
"typescript": '
|
|
65
|
+
"typescript": '6.0.3',
|
|
66
66
|
// renovate: datasource=npm depName=wxt
|
|
67
|
-
"wxt": '0.
|
|
67
|
+
"wxt": '0.21.4',
|
|
68
68
|
// renovate: datasource=npm depName=vite
|
|
69
|
-
"vite": '
|
|
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.
|
|
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.
|
|
79
|
+
"expo": '57.0.27',
|
|
80
80
|
// renovate: datasource=npm depName=expo-linking
|
|
81
|
-
"expo-linking": '57.0.
|
|
81
|
+
"expo-linking": '57.0.12',
|
|
82
82
|
// renovate: datasource=npm depName=expo-router
|
|
83
|
-
"expo-router": '57.0.
|
|
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.
|
|
91
|
+
"react-native": '0.86.3',
|
|
92
92
|
// renovate: datasource=npm depName=react-native-reanimated
|
|
93
|
-
"react-native-reanimated": '4.5.
|
|
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.
|
|
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.
|
|
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.
|
|
115
|
+
"@testing-library/dom": '10.4.2',
|
|
114
116
|
// renovate: datasource=npm depName=@react-native/metro-config
|
|
115
|
-
"@react-native/metro-config": '0.86.
|
|
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.
|
|
127
|
+
"nativewind": '4.2.7',
|
|
126
128
|
// renovate: datasource=npm depName=react-native-css-interop
|
|
127
|
-
"react-native-css-interop": '0.2.
|
|
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@
|
|
134
|
+
export const PACKAGE_MANAGER = 'pnpm@12.9.1';
|
|
133
135
|
// renovate: datasource=npm depName=eas-cli
|
|
134
|
-
export const EAS_CLI_VERSION = '
|
|
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,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 + '.');
|