anhedral 0.10.0 → 0.10.1
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 +2 -2
- package/assets/security/audit-deps.mjs +6 -1
- package/dist/bounded-file.d.ts +1 -0
- package/dist/bounded-file.js +1 -0
- package/dist/capabilities.d.ts +4 -4
- package/dist/capabilities.js +1 -1
- package/dist/dependencies.d.ts +32 -0
- package/dist/dependencies.js +47 -1
- package/dist/platforms/desktop.js +4 -5
- package/dist/platforms/extension.js +9 -73
- package/dist/platforms/mobile.js +6 -47
- package/dist/readiness.js +11 -3
- package/dist/registry/add.js +6 -3
- package/dist/registry/receipt.js +1 -0
- package/dist/registry/resolve.js +19 -13
- package/dist/registry/types.d.ts +1 -0
- package/dist/render.d.ts +2 -0
- package/dist/render.js +10 -0
- package/dist/scaffold/api.js +20 -3
- package/dist/scaffold/auth.js +73 -17
- package/dist/scaffold/bootstrap.d.ts +2 -0
- package/dist/scaffold/bootstrap.js +9 -0
- package/dist/scaffold/database.js +12 -6
- package/dist/scaffold/documentation.js +11 -8
- package/dist/scaffold/environment.d.ts +2 -0
- package/dist/scaffold/environment.js +18 -1
- package/dist/scaffold/files.d.ts +1 -0
- package/dist/scaffold/files.js +15 -4
- package/dist/scaffold/services.js +37 -13
- package/dist/scaffold/stack-documentation.d.ts +3 -0
- package/dist/scaffold/stack-documentation.js +52 -0
- package/dist/scaffold/styling.d.ts +3 -0
- package/dist/scaffold/styling.js +84 -0
- package/dist/scaffold/web.js +31 -11
- package/dist/scaffold/workspace.js +38 -22
- package/dist/stack-registry.d.ts +2 -2
- package/dist/stack-registry.js +38 -11
- package/dist/standard.js +2 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/vinext.js +9 -18
- package/dist/workspace-graph.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Starter prompt:
|
|
|
25
25
|
|
|
26
26
|
Codex asks about the app, explains the stack, implements the product, configures selected services, and delivers into your authorized accounts. The CLI commands below are also available independently.
|
|
27
27
|
|
|
28
|
-
Use Node.js 22.13+
|
|
28
|
+
Use Node.js 22.13+ on the 22.x line or Node.js 24+, with pnpm 10.34.5, for generated workspaces. Generate on Linux, macOS, or WSL.
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
# Inspect a plan first: no project files or cloud resources are created
|
|
@@ -51,7 +51,7 @@ Anhedral Init is free and open source. Sponsorship helps fund ongoing developmen
|
|
|
51
51
|
|
|
52
52
|
## Choose your stack
|
|
53
53
|
|
|
54
|
-
The bundled stack registry drives composition of every supported stack part. Run `anhedral registry list` or `anhedral registry view better-auth --json` to inspect items, destinations, dependencies and selection constraints. `new --dry-run --json` includes the resolved dependency graph. Scaffolding installs recipes in dependency order into `apps/*` and `packages/*`, validates local workspace references and cycles, and writes `anhedral.registry.json` with recipe versions and the resulting workspace graph. The same catalog is bundled with the plugin at build time.
|
|
54
|
+
The bundled stack registry drives composition of every supported stack part. Run `anhedral registry list` or `anhedral registry view better-auth --json` to inspect items, destinations, dependencies and selection constraints. `new --dry-run --json` includes the resolved dependency graph. Scaffolding installs recipes in dependency order into `apps/*` and `packages/*`, validates local workspace references and cycles, and writes `anhedral.registry.json` with recipe versions and the resulting workspace graph. The same catalog is bundled with the plugin at build time. Generated `docs/stack.md` lists the current resolved options, targets, environment names, required setup and verification; additions refresh it without rewriting the application README.
|
|
55
55
|
|
|
56
56
|
Registry items are trusted recipes shipped in the CLI release: foundations, apps, services, shared packages and runtime integrations. They follow shadcn's source ownership and dependency composition practices; this catalog is an Anhedral schema, not a shadcn registry payload. Runtime-aware recipes coordinate bindings, manifests and auth routes across workspaces. Each registry entry owns its recipe, provider requirements, environment variables, bindings and composition rules. Resolved plans name the exact generated destinations, including runtime-dependent packages. Recipe versions follow the CLI release. The official shadcn bootstrap is pinned to 4.21.4; commit the generated source and lockfile to preserve the application artifact. Incremental `add` supports only the bundled catalog maintained and tested by Anhedral. External registries and customer-authored registry payloads are unsupported. Additions refuse to overwrite edited or untracked source; runtime relocation requires a deliberate migration.
|
|
57
57
|
|
|
@@ -115,11 +115,16 @@ function validateAdvisory(advisory) {
|
|
|
115
115
|
ensure(Array.isArray(advisory.findings), 'Malformed pnpm advisory');
|
|
116
116
|
ensure(advisory.findings.length > 0, 'Malformed pnpm advisory');
|
|
117
117
|
advisory.findings.forEach(validateFinding);
|
|
118
|
+
const versions = advisory.findings.map((finding) => finding.version);
|
|
119
|
+
ensure(new Set(versions).size === versions.length, 'Duplicate pnpm advisory versions');
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
function validateCount(advisories, severity, count) {
|
|
121
123
|
ensure(Number.isInteger(count) && count >= 0, 'Malformed pnpm vulnerability counts');
|
|
122
|
-
|
|
124
|
+
// The pnpm 10 registry endpoint counts affected versions, not unique advisory IDs.
|
|
125
|
+
const details = advisories
|
|
126
|
+
.filter((advisory) => advisory.severity === severity)
|
|
127
|
+
.reduce((total, advisory) => total + advisory.findings.length, 0);
|
|
123
128
|
ensure(count === details, `Missing ${severity} advisory details: count ${count}, details ${details}`);
|
|
124
129
|
}
|
|
125
130
|
|
package/dist/bounded-file.d.ts
CHANGED
package/dist/bounded-file.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { closeSync, constants, fstatSync, lstatSync, openSync, readSync } from 'node:fs';
|
|
2
|
+
export const MAX_PROJECT_JSON_BYTES = 2 * 1024 * 1024;
|
|
2
3
|
/** Reject devices, links and oversized or growing files before parsing local input. */
|
|
3
4
|
export function readBoundedFile(filePath, maximumBytes) {
|
|
4
5
|
const stat = lstatSync(filePath);
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare const CAPABILITIES: Record<StandardProduct, Capability>;
|
|
|
6
6
|
export declare const CAPABILITY_REGISTRY: {
|
|
7
7
|
schemaVersion: number;
|
|
8
8
|
cliVersion: string;
|
|
9
|
-
capabilities: Record<"realtime" | "electron" | "wxt" | "expo" | "
|
|
9
|
+
capabilities: Record<"realtime" | "electron" | "wxt" | "expo" | "vinext" | "next" | "workflows" | "styling" | "hono" | "neon" | "d1" | "local-data" | "clerk" | "better-auth" | "r2" | "kv" | "queues" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog", Capability>;
|
|
10
10
|
};
|
|
11
11
|
export declare function createSetupPlan(options: StandardOptions, registry?: import("./registry/resolve.js").ResolvedStackRegistry): {
|
|
12
12
|
schemaVersion: number;
|
|
@@ -15,10 +15,10 @@ export declare function createSetupPlan(options: StandardOptions, registry?: imp
|
|
|
15
15
|
standard: string;
|
|
16
16
|
root: string;
|
|
17
17
|
name: string;
|
|
18
|
-
products: ("realtime" | "electron" | "wxt" | "expo" | "
|
|
18
|
+
products: ("realtime" | "electron" | "wxt" | "expo" | "vinext" | "next" | "workflows" | "styling" | "hono" | "neon" | "d1" | "local-data" | "clerk" | "better-auth" | "r2" | "kv" | "queues" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog")[];
|
|
19
19
|
hosting: import("./registry/types.js").Hosting;
|
|
20
20
|
registry: import("./registry/resolve.js").ResolvedStackRegistry;
|
|
21
|
-
bootstrap: ("pnpm" | "shadcn@4.21.4" | "dlx" | "init" | "--monorepo" | "--template"
|
|
21
|
+
bootstrap: ("pnpm" | "next" | "shadcn@4.21.4" | "dlx" | "init" | "--monorepo" | "--template")[];
|
|
22
22
|
dryRun: boolean;
|
|
23
23
|
setup: {
|
|
24
24
|
tools: string[];
|
|
@@ -32,7 +32,7 @@ export declare function createSetupPlan(options: StandardOptions, registry?: imp
|
|
|
32
32
|
credentials: readonly string[];
|
|
33
33
|
resources: readonly string[];
|
|
34
34
|
verify: readonly string[];
|
|
35
|
-
id: "realtime" | "electron" | "wxt" | "expo" | "
|
|
35
|
+
id: "realtime" | "electron" | "wxt" | "expo" | "vinext" | "next" | "workflows" | "styling" | "hono" | "neon" | "d1" | "local-data" | "clerk" | "better-auth" | "r2" | "kv" | "queues" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog";
|
|
36
36
|
}[];
|
|
37
37
|
completion: string[];
|
|
38
38
|
resourcesProvisioned: boolean;
|
package/dist/capabilities.js
CHANGED
|
@@ -61,7 +61,7 @@ export function createSetupPlan(options, registry = resolveStackRegistry(options
|
|
|
61
61
|
tools: [
|
|
62
62
|
...new Set([
|
|
63
63
|
'Git',
|
|
64
|
-
'Node.js 22.13+ for generated workspaces',
|
|
64
|
+
'Node.js 22.13+ on 22.x, or Node.js 24+ for generated workspaces',
|
|
65
65
|
PACKAGE_MANAGER.replace('@', ' '),
|
|
66
66
|
'GitHub integration or gh',
|
|
67
67
|
'Fallow',
|
package/dist/dependencies.d.ts
CHANGED
|
@@ -2,14 +2,21 @@ export interface DependencyGroup {
|
|
|
2
2
|
dependencies?: Record<string, string>;
|
|
3
3
|
devDependencies?: Record<string, string>;
|
|
4
4
|
}
|
|
5
|
+
export declare const NATIVE_ESLINT_VERSION = "10.0.3";
|
|
5
6
|
export declare const DESKTOP_DEPENDENCIES: DependencyGroup;
|
|
6
7
|
export declare const EXTENSION_DEPENDENCIES: DependencyGroup;
|
|
7
8
|
export declare const MOBILE_APP_DEPENDENCIES: DependencyGroup;
|
|
8
9
|
export declare const MOBILE_NATIVEWIND_DEPENDENCIES: DependencyGroup;
|
|
10
|
+
export declare const WORKSPACE_NODE_ENGINE = "^22.13.0 || >=24.0.0";
|
|
9
11
|
export declare const PACKAGE_MANAGER = "pnpm@10.34.5";
|
|
10
12
|
export declare const EAS_CLI_VERSION = "21.0.1";
|
|
11
13
|
export declare const SHADCN_VERSION = "4.21.4";
|
|
14
|
+
export declare const NEXT_VERCEL_VERSION = "16.4.0";
|
|
15
|
+
export declare const NEXT_OPENNEXT_VERSION = "16.3.8";
|
|
12
16
|
export declare const STACK_DEPENDENCIES: {
|
|
17
|
+
readonly 'better-auth': "1.7.7";
|
|
18
|
+
readonly '@better-auth/drizzle-adapter': "1.7.7";
|
|
19
|
+
readonly '@neondatabase/serverless': "1.2.0";
|
|
13
20
|
readonly 'drizzle-orm': "0.45.4";
|
|
14
21
|
readonly '@clerk/nextjs': "7.9.14";
|
|
15
22
|
readonly '@clerk/backend': "3.23.2";
|
|
@@ -30,3 +37,28 @@ export declare const STACK_DEPENDENCIES: {
|
|
|
30
37
|
readonly stripe: "20.4.1";
|
|
31
38
|
readonly 'posthog-node': "5.55.2";
|
|
32
39
|
};
|
|
40
|
+
/** Vinext's RSC/React versions form a separately verified compatibility group. */
|
|
41
|
+
export declare const VINEXT_DEPENDENCIES: DependencyGroup;
|
|
42
|
+
/** Separate HTTP and inspector ports let all selected Workers run together. */
|
|
43
|
+
export declare const WORKER_DEV_PORTS: {
|
|
44
|
+
readonly api: {
|
|
45
|
+
readonly http: 8787;
|
|
46
|
+
readonly inspector: 9231;
|
|
47
|
+
};
|
|
48
|
+
readonly realtime: {
|
|
49
|
+
readonly http: 8788;
|
|
50
|
+
readonly inspector: 9232;
|
|
51
|
+
};
|
|
52
|
+
readonly jobs: {
|
|
53
|
+
readonly http: 8789;
|
|
54
|
+
readonly inspector: 9233;
|
|
55
|
+
};
|
|
56
|
+
readonly scheduled: {
|
|
57
|
+
readonly http: 8790;
|
|
58
|
+
readonly inspector: 9234;
|
|
59
|
+
};
|
|
60
|
+
readonly workflows: {
|
|
61
|
+
readonly http: 8791;
|
|
62
|
+
readonly inspector: 9235;
|
|
63
|
+
};
|
|
64
|
+
};
|
package/dist/dependencies.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// renovate: datasource=npm depName=eslint
|
|
2
|
+
export const NATIVE_ESLINT_VERSION = '10.0.3';
|
|
1
3
|
export const DESKTOP_DEPENDENCIES = {
|
|
2
4
|
dependencies: {
|
|
3
5
|
// renovate: datasource=npm depName=react
|
|
@@ -24,7 +26,7 @@ export const DESKTOP_DEPENDENCIES = {
|
|
|
24
26
|
electron: '43.7.8',
|
|
25
27
|
// renovate: datasource=npm depName=electron-builder
|
|
26
28
|
'electron-builder': '26.15.3',
|
|
27
|
-
eslint:
|
|
29
|
+
eslint: NATIVE_ESLINT_VERSION,
|
|
28
30
|
// renovate: datasource=npm depName=tailwindcss
|
|
29
31
|
tailwindcss: '4.1.18',
|
|
30
32
|
// renovate: datasource=npm depName=postcss
|
|
@@ -129,12 +131,23 @@ export const MOBILE_NATIVEWIND_DEPENDENCIES = {
|
|
|
129
131
|
tailwindcss: '3.4.19',
|
|
130
132
|
},
|
|
131
133
|
};
|
|
134
|
+
export const WORKSPACE_NODE_ENGINE = '^22.13.0 || >=24.0.0';
|
|
132
135
|
export const PACKAGE_MANAGER = 'pnpm@10.34.5';
|
|
133
136
|
// renovate: datasource=npm depName=eas-cli
|
|
134
137
|
export const EAS_CLI_VERSION = '21.0.1';
|
|
135
138
|
// renovate: datasource=npm depName=shadcn
|
|
136
139
|
export const SHADCN_VERSION = '4.21.4';
|
|
140
|
+
// renovate: datasource=npm depName=next
|
|
141
|
+
export const NEXT_VERCEL_VERSION = '16.4.0';
|
|
142
|
+
// renovate: datasource=npm depName=next
|
|
143
|
+
export const NEXT_OPENNEXT_VERSION = '16.3.8';
|
|
137
144
|
export const STACK_DEPENDENCIES = {
|
|
145
|
+
// renovate: datasource=npm depName=better-auth
|
|
146
|
+
'better-auth': '1.7.7',
|
|
147
|
+
// renovate: datasource=npm depName=@better-auth/drizzle-adapter
|
|
148
|
+
'@better-auth/drizzle-adapter': '1.7.7',
|
|
149
|
+
// renovate: datasource=npm depName=@neondatabase/serverless
|
|
150
|
+
'@neondatabase/serverless': '1.2.0',
|
|
138
151
|
// renovate: datasource=npm depName=drizzle-orm
|
|
139
152
|
'drizzle-orm': '0.45.4',
|
|
140
153
|
// renovate: datasource=npm depName=@clerk/nextjs
|
|
@@ -174,3 +187,36 @@ export const STACK_DEPENDENCIES = {
|
|
|
174
187
|
// renovate: datasource=npm depName=posthog-node
|
|
175
188
|
'posthog-node': '5.55.2',
|
|
176
189
|
};
|
|
190
|
+
/** Vinext's RSC/React versions form a separately verified compatibility group. */
|
|
191
|
+
export const VINEXT_DEPENDENCIES = {
|
|
192
|
+
dependencies: {
|
|
193
|
+
// renovate: datasource=npm depName=vinext
|
|
194
|
+
vinext: '1.1.0',
|
|
195
|
+
next: NEXT_VERCEL_VERSION,
|
|
196
|
+
// renovate: datasource=npm depName=react
|
|
197
|
+
react: '19.2.8',
|
|
198
|
+
// renovate: datasource=npm depName=react-dom
|
|
199
|
+
'react-dom': '19.2.8',
|
|
200
|
+
// renovate: datasource=npm depName=react-server-dom-webpack
|
|
201
|
+
'react-server-dom-webpack': '19.2.8',
|
|
202
|
+
},
|
|
203
|
+
devDependencies: {
|
|
204
|
+
// renovate: datasource=npm depName=vite
|
|
205
|
+
vite: '8.3.4',
|
|
206
|
+
// renovate: datasource=npm depName=@cloudflare/vite-plugin
|
|
207
|
+
'@cloudflare/vite-plugin': '1.63.1',
|
|
208
|
+
// renovate: datasource=npm depName=@vitejs/plugin-rsc
|
|
209
|
+
'@vitejs/plugin-rsc': '0.5.36',
|
|
210
|
+
// renovate: datasource=npm depName=@vitejs/plugin-react
|
|
211
|
+
'@vitejs/plugin-react': '6.1.2',
|
|
212
|
+
wrangler: STACK_DEPENDENCIES.wrangler,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
/** Separate HTTP and inspector ports let all selected Workers run together. */
|
|
216
|
+
export const WORKER_DEV_PORTS = {
|
|
217
|
+
api: { http: 8787, inspector: 9231 },
|
|
218
|
+
realtime: { http: 8788, inspector: 9232 },
|
|
219
|
+
jobs: { http: 8789, inspector: 9233 },
|
|
220
|
+
scheduled: { http: 8790, inspector: 9234 },
|
|
221
|
+
workflows: { http: 8791, inspector: 9235 },
|
|
222
|
+
};
|
|
@@ -107,9 +107,6 @@ export default defineConfig({
|
|
|
107
107
|
build: {
|
|
108
108
|
outDir: 'dist/renderer',
|
|
109
109
|
emptyOutDir: true,
|
|
110
|
-
// Clerk is already loaded behind a dynamic import. Its optional account-management
|
|
111
|
-
// surface is intentionally a large async chunk and does not inflate initial rendering.
|
|
112
|
-
chunkSizeWarningLimit: 1600,
|
|
113
110
|
},
|
|
114
111
|
});
|
|
115
112
|
`);
|
|
@@ -261,7 +258,7 @@ function isAllowedAppNavigation(value: string): boolean {
|
|
|
261
258
|
}
|
|
262
259
|
|
|
263
260
|
// Product-owned window options and navigation policy belong in this file.
|
|
264
|
-
//
|
|
261
|
+
// main.ts imports this seam and manages application lifecycle.
|
|
265
262
|
export function createAppWindow(): BrowserWindow {
|
|
266
263
|
const window = new BrowserWindow({
|
|
267
264
|
width: 1180,
|
|
@@ -306,11 +303,13 @@ export function createAppWindow(): BrowserWindow {
|
|
|
306
303
|
writeFile(path.join(dir, 'src/main/main.ts'), `import { app, BrowserWindow, session } from 'electron';
|
|
307
304
|
import { createAppWindow } from './app-window.js';
|
|
308
305
|
|
|
309
|
-
|
|
310
306
|
app.whenReady().then(() => {
|
|
311
307
|
session.defaultSession.setPermissionCheckHandler(() => false);
|
|
312
308
|
session.defaultSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
|
|
313
309
|
createAppWindow();
|
|
310
|
+
}).catch((error) => {
|
|
311
|
+
console.error('Unable to start the desktop app:', error);
|
|
312
|
+
app.quit();
|
|
314
313
|
});
|
|
315
314
|
app.on('window-all-closed', () => {
|
|
316
315
|
if (process.platform !== 'darwin') app.quit();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NATIVE_ESLINT_VERSION } from '../dependencies.js';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import { writeReactLintConfig } from './lint.js';
|
|
3
4
|
import { writeFile } from '../util.js';
|
|
@@ -41,11 +42,14 @@ function writePackageJson(dir, projectName) {
|
|
|
41
42
|
typecheck: 'tsc --noEmit',
|
|
42
43
|
lint: 'eslint src --max-warnings 0',
|
|
43
44
|
},
|
|
44
|
-
dependencies:
|
|
45
|
+
dependencies: {
|
|
46
|
+
...EXTENSION_DEPENDENCIES.dependencies,
|
|
47
|
+
'@workspace/styling': 'workspace:*',
|
|
48
|
+
},
|
|
45
49
|
devDependencies: {
|
|
46
50
|
...EXTENSION_DEPENDENCIES.devDependencies,
|
|
47
51
|
'@workspace/eslint-config': 'workspace:*',
|
|
48
|
-
eslint:
|
|
52
|
+
eslint: NATIVE_ESLINT_VERSION,
|
|
49
53
|
},
|
|
50
54
|
}, null, 2) + '\n');
|
|
51
55
|
}
|
|
@@ -55,13 +59,12 @@ function writeWxtConfig(dir, displayName) {
|
|
|
55
59
|
const actionTitleLiteral = jsString(`Open ${displayName}`);
|
|
56
60
|
writeFile(path.join(dir, 'wxt.config.ts'), `import { defineConfig } from 'wxt';
|
|
57
61
|
|
|
58
|
-
|
|
59
62
|
export default defineConfig({
|
|
60
63
|
srcDir: 'src',
|
|
64
|
+
dev: { server: { port: 3001, strictPort: true } },
|
|
61
65
|
manifest: () => {
|
|
62
66
|
const crxPublicKey = import.meta.env.VITE_CRX_PUBLIC_KEY || '';
|
|
63
67
|
|
|
64
|
-
|
|
65
68
|
return {
|
|
66
69
|
name: ${nameLiteral},
|
|
67
70
|
description: ${descriptionLiteral},
|
|
@@ -176,51 +179,7 @@ function writeTailwindConfig(dir) {
|
|
|
176
179
|
writeFile(path.join(dir, 'tailwind.config.cjs'), `/** @type {import('tailwindcss').Config} */
|
|
177
180
|
module.exports = {
|
|
178
181
|
content: ['./src/**/*.{ts,tsx,html}'],
|
|
179
|
-
|
|
180
|
-
extend: {
|
|
181
|
-
colors: {
|
|
182
|
-
border: 'hsl(var(--border))',
|
|
183
|
-
input: 'hsl(var(--input))',
|
|
184
|
-
ring: 'hsl(var(--ring))',
|
|
185
|
-
background: 'hsl(var(--background))',
|
|
186
|
-
foreground: 'hsl(var(--foreground))',
|
|
187
|
-
primary: {
|
|
188
|
-
DEFAULT: 'hsl(var(--primary))',
|
|
189
|
-
foreground: 'hsl(var(--primary-foreground))',
|
|
190
|
-
},
|
|
191
|
-
secondary: {
|
|
192
|
-
DEFAULT: 'hsl(var(--secondary))',
|
|
193
|
-
foreground: 'hsl(var(--secondary-foreground))',
|
|
194
|
-
},
|
|
195
|
-
destructive: {
|
|
196
|
-
DEFAULT: 'hsl(var(--destructive))',
|
|
197
|
-
foreground: 'hsl(var(--destructive-foreground))',
|
|
198
|
-
},
|
|
199
|
-
muted: {
|
|
200
|
-
DEFAULT: 'hsl(var(--muted))',
|
|
201
|
-
foreground: 'hsl(var(--muted-foreground))',
|
|
202
|
-
},
|
|
203
|
-
accent: {
|
|
204
|
-
DEFAULT: 'hsl(var(--accent))',
|
|
205
|
-
foreground: 'hsl(var(--accent-foreground))',
|
|
206
|
-
},
|
|
207
|
-
popover: {
|
|
208
|
-
DEFAULT: 'hsl(var(--popover))',
|
|
209
|
-
foreground: 'hsl(var(--popover-foreground))',
|
|
210
|
-
},
|
|
211
|
-
card: {
|
|
212
|
-
DEFAULT: 'hsl(var(--card))',
|
|
213
|
-
foreground: 'hsl(var(--card-foreground))',
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
borderRadius: {
|
|
217
|
-
lg: 'var(--radius)',
|
|
218
|
-
md: 'calc(var(--radius) - 2px)',
|
|
219
|
-
sm: 'calc(var(--radius) - 4px)',
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
plugins: [],
|
|
182
|
+
presets: [require('@workspace/styling/tailwind')],
|
|
224
183
|
};
|
|
225
184
|
`);
|
|
226
185
|
}
|
|
@@ -259,7 +218,7 @@ export default defineBackground(() => {
|
|
|
259
218
|
// Open the side panel when the extension icon is clicked.
|
|
260
219
|
(chrome as ChromeWithSidePanel).sidePanel
|
|
261
220
|
.setPanelBehavior({ openPanelOnActionClick: true })
|
|
262
|
-
.catch(() =>
|
|
221
|
+
.catch((error) => console.error('Unable to configure the side panel:', error));
|
|
263
222
|
});
|
|
264
223
|
`);
|
|
265
224
|
}
|
|
@@ -328,7 +287,6 @@ export function SidePanelApp() {
|
|
|
328
287
|
}
|
|
329
288
|
}, []);
|
|
330
289
|
|
|
331
|
-
|
|
332
290
|
return (
|
|
333
291
|
<div style={{ padding: 24 }}>
|
|
334
292
|
<h2>Welcome!</h2>
|
|
@@ -351,28 +309,6 @@ function writeStyles(dir) {
|
|
|
351
309
|
@tailwind utilities;
|
|
352
310
|
|
|
353
311
|
@layer base {
|
|
354
|
-
:root {
|
|
355
|
-
--background: 0 0% 100%;
|
|
356
|
-
--foreground: 0 0% 9%;
|
|
357
|
-
--card: 0 0% 100%;
|
|
358
|
-
--card-foreground: 0 0% 9%;
|
|
359
|
-
--popover: 0 0% 100%;
|
|
360
|
-
--popover-foreground: 0 0% 9%;
|
|
361
|
-
--primary: 0 0% 9%;
|
|
362
|
-
--primary-foreground: 0 0% 98%;
|
|
363
|
-
--secondary: 0 0% 96.1%;
|
|
364
|
-
--secondary-foreground: 0 0% 9%;
|
|
365
|
-
--muted: 0 0% 96.1%;
|
|
366
|
-
--muted-foreground: 0 0% 45.1%;
|
|
367
|
-
--accent: 0 0% 96.1%;
|
|
368
|
-
--accent-foreground: 0 0% 9%;
|
|
369
|
-
--destructive: 0 84.2% 60.2%;
|
|
370
|
-
--destructive-foreground: 0 0% 98%;
|
|
371
|
-
--border: 0 0% 89.8%;
|
|
372
|
-
--input: 0 0% 89.8%;
|
|
373
|
-
--ring: 0 0% 63.9%;
|
|
374
|
-
--radius: 0.5rem;
|
|
375
|
-
}
|
|
376
312
|
|
|
377
313
|
* {
|
|
378
314
|
border-color: hsl(var(--border));
|
package/dist/platforms/mobile.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NATIVE_ESLINT_VERSION } from '../dependencies.js';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import { writeReactLintConfig } from './lint.js';
|
|
3
4
|
import { appendGitignore, writeFile } from '../util.js';
|
|
@@ -62,26 +63,9 @@ module.exports = withNativeWind(config, { input: './global.css', inlineRem: 16 }
|
|
|
62
63
|
|
|
63
64
|
/** @type {import('tailwindcss').Config} */
|
|
64
65
|
module.exports = {
|
|
65
|
-
darkMode: 'class',
|
|
66
66
|
content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
|
|
67
|
-
presets: [require('nativewind/preset')],
|
|
68
|
-
theme: {
|
|
69
|
-
extend: {
|
|
70
|
-
colors: {
|
|
71
|
-
border: 'hsl(var(--border))', input: 'hsl(var(--input))', ring: 'hsl(var(--ring))',
|
|
72
|
-
background: 'hsl(var(--background))', foreground: 'hsl(var(--foreground))',
|
|
73
|
-
primary: { DEFAULT: 'hsl(var(--primary))', foreground: 'hsl(var(--primary-foreground))' },
|
|
74
|
-
secondary: { DEFAULT: 'hsl(var(--secondary))', foreground: 'hsl(var(--secondary-foreground))' },
|
|
75
|
-
destructive: { DEFAULT: 'hsl(var(--destructive))', foreground: 'hsl(var(--destructive-foreground))' },
|
|
76
|
-
muted: { DEFAULT: 'hsl(var(--muted))', foreground: 'hsl(var(--muted-foreground))' },
|
|
77
|
-
accent: { DEFAULT: 'hsl(var(--accent))', foreground: 'hsl(var(--accent-foreground))' },
|
|
78
|
-
popover: { DEFAULT: 'hsl(var(--popover))', foreground: 'hsl(var(--popover-foreground))' },
|
|
79
|
-
card: { DEFAULT: 'hsl(var(--card))', foreground: 'hsl(var(--card-foreground))' },
|
|
80
|
-
},
|
|
81
|
-
borderRadius: { lg: 'var(--radius)', md: 'calc(var(--radius) - 2px)', sm: 'calc(var(--radius) - 4px)' },
|
|
82
|
-
borderWidth: { hairline: hairlineWidth() },
|
|
83
|
-
},
|
|
84
|
-
},
|
|
67
|
+
presets: [require('nativewind/preset'), require('@workspace/styling/tailwind')],
|
|
68
|
+
theme: { extend: { borderWidth: { hairline: hairlineWidth() } } },
|
|
85
69
|
future: { hoverOnlyWhenSupported: true },
|
|
86
70
|
plugins: [require('tailwindcss-animate')],
|
|
87
71
|
};
|
|
@@ -89,31 +73,6 @@ module.exports = {
|
|
|
89
73
|
writeFile(path.join(dir, 'global.css'), `@tailwind base;
|
|
90
74
|
@tailwind components;
|
|
91
75
|
@tailwind utilities;
|
|
92
|
-
|
|
93
|
-
@layer base {
|
|
94
|
-
:root {
|
|
95
|
-
--background: 0 0% 100%; --foreground: 0 0% 3.9%;
|
|
96
|
-
--card: 0 0% 100%; --card-foreground: 0 0% 3.9%;
|
|
97
|
-
--popover: 0 0% 100%; --popover-foreground: 0 0% 3.9%;
|
|
98
|
-
--primary: 0 0% 9%; --primary-foreground: 0 0% 98%;
|
|
99
|
-
--secondary: 0 0% 96.1%; --secondary-foreground: 0 0% 9%;
|
|
100
|
-
--muted: 0 0% 96.1%; --muted-foreground: 0 0% 45.1%;
|
|
101
|
-
--accent: 0 0% 96.1%; --accent-foreground: 0 0% 9%;
|
|
102
|
-
--destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%;
|
|
103
|
-
--border: 0 0% 89.8%; --input: 0 0% 89.8%; --ring: 0 0% 63%; --radius: 0.625rem;
|
|
104
|
-
}
|
|
105
|
-
.dark:root {
|
|
106
|
-
--background: 0 0% 3.9%; --foreground: 0 0% 98%;
|
|
107
|
-
--card: 0 0% 3.9%; --card-foreground: 0 0% 98%;
|
|
108
|
-
--popover: 0 0% 3.9%; --popover-foreground: 0 0% 98%;
|
|
109
|
-
--primary: 0 0% 98%; --primary-foreground: 0 0% 9%;
|
|
110
|
-
--secondary: 0 0% 14.9%; --secondary-foreground: 0 0% 98%;
|
|
111
|
-
--muted: 0 0% 14.9%; --muted-foreground: 0 0% 63.9%;
|
|
112
|
-
--accent: 0 0% 14.9%; --accent-foreground: 0 0% 98%;
|
|
113
|
-
--destructive: 0 70.9% 59.4%; --destructive-foreground: 0 0% 98%;
|
|
114
|
-
--border: 0 0% 14.9%; --input: 0 0% 14.9%; --ring: 300 0% 45%;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
76
|
`);
|
|
118
77
|
}
|
|
119
78
|
function expoScheme(projectName) {
|
|
@@ -160,11 +119,12 @@ export async function scaffoldExpo({ root, options }) {
|
|
|
160
119
|
dependencies: {
|
|
161
120
|
...MOBILE_APP_DEPENDENCIES.dependencies,
|
|
162
121
|
...MOBILE_NATIVEWIND_DEPENDENCIES.dependencies,
|
|
122
|
+
'@workspace/styling': 'workspace:*',
|
|
163
123
|
},
|
|
164
124
|
devDependencies: {
|
|
165
125
|
...MOBILE_APP_DEPENDENCIES.devDependencies,
|
|
166
126
|
'@workspace/eslint-config': 'workspace:*',
|
|
167
|
-
eslint:
|
|
127
|
+
eslint: NATIVE_ESLINT_VERSION,
|
|
168
128
|
},
|
|
169
129
|
}, null, 2) + '\n');
|
|
170
130
|
writeFile(path.join(dir, 'app.json'), JSON.stringify({
|
|
@@ -200,7 +160,6 @@ export async function scaffoldExpo({ root, options }) {
|
|
|
200
160
|
import { PortalHost } from '@rn-primitives/portal';
|
|
201
161
|
import { Stack } from 'expo-router';
|
|
202
162
|
|
|
203
|
-
|
|
204
163
|
export default function RootLayout() {
|
|
205
164
|
return <><Stack /><PortalHost /></>;
|
|
206
165
|
}
|
|
@@ -215,7 +174,7 @@ export default function HomeScreen() {
|
|
|
215
174
|
<StatusBar style="auto" />
|
|
216
175
|
<View style={styles.content}>
|
|
217
176
|
<Text accessibilityRole="header" style={styles.title}>{${nameLiteral}}</Text>
|
|
218
|
-
<Text selectable style={styles.subtitle}>
|
|
177
|
+
<Text selectable style={styles.subtitle}>Expo application scaffold generated by Anhedral.</Text>
|
|
219
178
|
<Button onPress={() => void Linking.openURL('https://docs.expo.dev')}>Learn more</Button>
|
|
220
179
|
</View>
|
|
221
180
|
</ScrollView>
|
package/dist/readiness.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readBoundedFile, MAX_PROJECT_JSON_BYTES } from './bounded-file.js';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { isDeepStrictEqual } from 'node:util';
|
|
4
5
|
import { inspectWorkspaceGraph } from './workspace-graph.js';
|
|
@@ -8,11 +9,16 @@ import { STANDARD_PRODUCTS } from './stack-registry.js';
|
|
|
8
9
|
export function inspectProject(root) {
|
|
9
10
|
const checks = [];
|
|
10
11
|
const record = (id, ok, detail) => checks.push({ id, ok, detail });
|
|
11
|
-
const read = (file) => JSON.parse(
|
|
12
|
+
const read = (file) => JSON.parse(readBoundedFile(path.join(root, file), MAX_PROJECT_JSON_BYTES).toString('utf8'));
|
|
12
13
|
record('workspace', existsSync(path.join(root, 'pnpm-workspace.yaml')), 'pnpm workspace manifest');
|
|
13
14
|
record('lockfile', existsSync(path.join(root, 'pnpm-lock.yaml')), 'Committed dependency resolution required');
|
|
14
15
|
try {
|
|
15
16
|
const plan = read('anhedral.setup.json');
|
|
17
|
+
const recordedVersion = plan?.cliVersion;
|
|
18
|
+
const compatible = recordedVersion === CAPABILITY_REGISTRY.cliVersion;
|
|
19
|
+
record('cli-compatibility', compatible, /^\d+\.\d+\.\d+$/.test(recordedVersion ?? '')
|
|
20
|
+
? `Use anhedral@${recordedVersion} for this project or review upgrade differences`
|
|
21
|
+
: 'Use the recorded project CLI version or review upgrade differences');
|
|
16
22
|
validateSetupPlan(plan);
|
|
17
23
|
const standard = read('anhedral.standard.json');
|
|
18
24
|
record('setup-plan', true, `Requirements recorded by CLI ${plan.cliVersion}`);
|
|
@@ -40,7 +46,6 @@ export function inspectProject(root) {
|
|
|
40
46
|
catch {
|
|
41
47
|
record('workspace-graph', false, 'Invalid workspace manifests, references or dependency cycles');
|
|
42
48
|
}
|
|
43
|
-
record('cli-compatibility', plan.cliVersion === CAPABILITY_REGISTRY.cliVersion, 'Use the project CLI version or review upgrade differences');
|
|
44
49
|
record('selection', standard.schemaVersion === 1 &&
|
|
45
50
|
JSON.stringify(standard.products) === JSON.stringify(plan.products) &&
|
|
46
51
|
standard.hosting ===
|
|
@@ -68,6 +73,9 @@ export function validateSetupPlan(plan) {
|
|
|
68
73
|
value.registryVersion !== 1 ||
|
|
69
74
|
typeof value.cliVersion !== 'string')
|
|
70
75
|
throw new Error('Unsupported setup plan version.');
|
|
76
|
+
if (/^\d+\.\d+\.\d+$/.test(value.cliVersion) &&
|
|
77
|
+
value.cliVersion !== CAPABILITY_REGISTRY.cliVersion)
|
|
78
|
+
throw new Error(`Use anhedral@${value.cliVersion} for this project or review upgrade differences.`);
|
|
71
79
|
if (!Array.isArray(value.products) ||
|
|
72
80
|
!value.products.length ||
|
|
73
81
|
!value.products.every((id) => STANDARD_PRODUCTS.includes(id)))
|
package/dist/registry/add.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { readBoundedFile, MAX_PROJECT_JSON_BYTES } from '../bounded-file.js';
|
|
1
2
|
import { configureSecurityBackports } from '../security-backports.js';
|
|
2
|
-
import { bootstrapWorkspace } from '../scaffold/bootstrap.js';
|
|
3
|
+
import { bootstrapWorkspace, assertWorkspaceRuntime } from '../scaffold/bootstrap.js';
|
|
3
4
|
import { registerApplication } from '../project-state.js';
|
|
4
5
|
import { registerVercelApplication } from '../vercel.js';
|
|
5
6
|
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
@@ -22,6 +23,8 @@ export async function addStackItems(root, request, run = execFile) {
|
|
|
22
23
|
for (const key of Object.keys(request))
|
|
23
24
|
if (!['items', 'dryRun', 'skipInstall'].includes(key))
|
|
24
25
|
throw new Error(`Unknown add option: ${key}. Only bundled registry items are supported.`);
|
|
26
|
+
if (!request.dryRun)
|
|
27
|
+
assertWorkspaceRuntime(run);
|
|
25
28
|
root = realpathSync(root);
|
|
26
29
|
if (!request.items.length)
|
|
27
30
|
throw new Error('Select at least one registry item.');
|
|
@@ -62,7 +65,7 @@ export async function addStackItems(root, request, run = execFile) {
|
|
|
62
65
|
throw new Error(`Adding this selection relocates ${item.name}; migrate its existing source explicitly.`);
|
|
63
66
|
}
|
|
64
67
|
const before = sourceFiles(root);
|
|
65
|
-
const receiptText =
|
|
68
|
+
const receiptText = readBoundedFile(path.join(root, 'anhedral.registry.json'), MAX_PROJECT_JSON_BYTES).toString('utf8');
|
|
66
69
|
const commitPaths = [];
|
|
67
70
|
const conflicts = [];
|
|
68
71
|
const result = {
|
|
@@ -151,7 +154,7 @@ export async function addStackItems(root, request, run = execFile) {
|
|
|
151
154
|
commitPaths.push('anhedral.registry.json');
|
|
152
155
|
// Detect concurrent changes even to source that this addition would otherwise preserve.
|
|
153
156
|
if (!isDeepStrictEqual(sourceFiles(root), before) ||
|
|
154
|
-
|
|
157
|
+
readBoundedFile(path.join(root, 'anhedral.registry.json'), MAX_PROJECT_JSON_BYTES).toString('utf8') !== receiptText)
|
|
155
158
|
throw new Error('Workspace changed during addition; retry after reviewing source.');
|
|
156
159
|
},
|
|
157
160
|
afterCommit: () => {
|
package/dist/registry/receipt.js
CHANGED
package/dist/registry/resolve.js
CHANGED
|
@@ -50,6 +50,23 @@ export function resolveStandardProducts(selected, hosting, hostingSpecified) {
|
|
|
50
50
|
throw new Error('--hosting applies to the web application.');
|
|
51
51
|
resolveStackRegistry({ products: [...selected], hosting });
|
|
52
52
|
}
|
|
53
|
+
function itemDependencies(name, context) {
|
|
54
|
+
const definition = getStackDefinition(name);
|
|
55
|
+
const dependencies = new Set(definition.registryDependencies);
|
|
56
|
+
if (definition.runtime) {
|
|
57
|
+
if (!hasServer(context) && definition.runtime !== 'optional-worker')
|
|
58
|
+
throw new Error(`${name} requires a server runtime.`);
|
|
59
|
+
if (needsWorker(name, context) &&
|
|
60
|
+
!context.has('hono') &&
|
|
61
|
+
!(context.options.hosting === 'cloudflare' && (context.has('vinext') || context.has('next'))))
|
|
62
|
+
throw new Error(`${name} requires a Cloudflare runtime.`);
|
|
63
|
+
if (hasServer(context))
|
|
64
|
+
dependencies.add(context.has('hono') ? 'hono' : context.has('vinext') ? 'vinext' : 'next');
|
|
65
|
+
}
|
|
66
|
+
for (const dependency of definition.resolveDependencies?.(context) ?? [])
|
|
67
|
+
dependencies.add(dependency);
|
|
68
|
+
return dependencies;
|
|
69
|
+
}
|
|
53
70
|
export function resolveStackRegistry(options) {
|
|
54
71
|
const selected = new Set(options.products);
|
|
55
72
|
validateProducts(selected, options.hosting);
|
|
@@ -62,20 +79,9 @@ export function resolveStackRegistry(options) {
|
|
|
62
79
|
return;
|
|
63
80
|
const definition = getStackDefinition(name);
|
|
64
81
|
const metadata = getStackItem(name);
|
|
65
|
-
const dependencies =
|
|
66
|
-
if (definition.runtime) {
|
|
67
|
-
if (!hasServer(context) && definition.runtime !== 'optional-worker')
|
|
68
|
-
throw new Error(`${name} requires a server runtime.`);
|
|
69
|
-
if (needsWorker(name, context) &&
|
|
70
|
-
!context.has('hono') &&
|
|
71
|
-
!(options.hosting === 'cloudflare' && (context.has('vinext') || context.has('next'))))
|
|
72
|
-
throw new Error(`${name} requires a Cloudflare runtime.`);
|
|
73
|
-
if (hasServer(context))
|
|
74
|
-
dependencies.add(context.has('hono') ? 'hono' : context.has('vinext') ? 'vinext' : 'next');
|
|
75
|
-
}
|
|
76
|
-
for (const dependency of definition.resolveDependencies?.(context) ?? [])
|
|
77
|
-
dependencies.add(dependency);
|
|
82
|
+
const dependencies = itemDependencies(name, context);
|
|
78
83
|
metadata.registryDependencies = [...dependencies];
|
|
84
|
+
metadata.environment = definition.resolveEnvironment?.(context) ?? metadata.environment;
|
|
79
85
|
metadata.targets = definition.resolveTargets?.(context) ?? metadata.targets;
|
|
80
86
|
if (definition.configure)
|
|
81
87
|
metadata.bindings = definition.configure(context);
|
package/dist/registry/types.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export interface StackRegistryItem {
|
|
|
57
57
|
export interface ItemDefinition extends Omit<StackRegistryItem, 'bindings'> {
|
|
58
58
|
install?: Recipe;
|
|
59
59
|
configure?: (context: RecipeContext) => Json;
|
|
60
|
+
resolveEnvironment?: (context: RecipeContext) => string[];
|
|
60
61
|
resolveTargets?: (context: RecipeContext) => string[];
|
|
61
62
|
resolvePackage?: (context: RecipeContext, recipe: PackageRecipe) => PackageRecipe;
|
|
62
63
|
resolveDependencies?: (context: RecipeContext) => string[];
|
package/dist/render.d.ts
CHANGED
|
@@ -8,3 +8,5 @@ export declare function markdownHeading(value: string): string;
|
|
|
8
8
|
export declare function packageNameFromText(value: string): string;
|
|
9
9
|
export declare function childPackageName(projectName: string, suffix: string): string;
|
|
10
10
|
export declare function identifierSegment(value: string): string;
|
|
11
|
+
/** Keep provider resource names within the shared Workers/R2/Queues naming limits. */
|
|
12
|
+
export declare function resourceName(projectName: string, suffix: string): string;
|
package/dist/render.js
CHANGED
|
@@ -72,3 +72,13 @@ export function identifierSegment(value) {
|
|
|
72
72
|
.replace(/^-+|-+$/g, '');
|
|
73
73
|
return segment || 'app';
|
|
74
74
|
}
|
|
75
|
+
/** Keep provider resource names within the shared Workers/R2/Queues naming limits. */
|
|
76
|
+
export function resourceName(projectName, suffix) {
|
|
77
|
+
const segment = identifierSegment(projectName);
|
|
78
|
+
const qualifier = identifierSegment(suffix);
|
|
79
|
+
if (qualifier.length > 40)
|
|
80
|
+
throw new Error('Resource suffix is too long.');
|
|
81
|
+
const digest = createHash('sha256').update(projectName).digest('hex').slice(0, 8);
|
|
82
|
+
const base = segment === projectName ? segment : `${segment}-${digest}`;
|
|
83
|
+
return `${compactPackageSegment(base, 62 - qualifier.length)}-${qualifier}`;
|
|
84
|
+
}
|