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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { json, patchPackage, put, readJson } from './files.js';
|
|
2
|
+
/** Shared data and a Tailwind v3 preset for Expo/NativeWind and WXT. */
|
|
3
|
+
export function scaffoldStyling({ root }) {
|
|
4
|
+
put(root, 'packages/styling/src/tokens.cjs', `exports.light = {
|
|
5
|
+
'--background': '0 0% 100%',
|
|
6
|
+
'--foreground': '0 0% 9%',
|
|
7
|
+
'--card': '0 0% 100%',
|
|
8
|
+
'--card-foreground': '0 0% 9%',
|
|
9
|
+
'--popover': '0 0% 100%',
|
|
10
|
+
'--popover-foreground': '0 0% 9%',
|
|
11
|
+
'--primary': '0 0% 9%',
|
|
12
|
+
'--primary-foreground': '0 0% 98%',
|
|
13
|
+
'--secondary': '0 0% 96.1%',
|
|
14
|
+
'--secondary-foreground': '0 0% 9%',
|
|
15
|
+
'--muted': '0 0% 96.1%',
|
|
16
|
+
'--muted-foreground': '0 0% 45.1%',
|
|
17
|
+
'--accent': '0 0% 96.1%',
|
|
18
|
+
'--accent-foreground': '0 0% 9%',
|
|
19
|
+
'--destructive': '0 84.2% 60.2%',
|
|
20
|
+
'--destructive-foreground': '0 0% 98%',
|
|
21
|
+
'--border': '0 0% 89.8%',
|
|
22
|
+
'--input': '0 0% 89.8%',
|
|
23
|
+
'--ring': '0 0% 63.9%',
|
|
24
|
+
'--radius': '0.5rem',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
exports.dark = {
|
|
28
|
+
...exports.light,
|
|
29
|
+
'--background': '0 0% 9%',
|
|
30
|
+
'--foreground': '0 0% 98%',
|
|
31
|
+
'--card': '0 0% 9%',
|
|
32
|
+
'--card-foreground': '0 0% 98%',
|
|
33
|
+
'--popover': '0 0% 9%',
|
|
34
|
+
'--popover-foreground': '0 0% 98%',
|
|
35
|
+
'--primary': '0 0% 98%',
|
|
36
|
+
'--primary-foreground': '0 0% 9%',
|
|
37
|
+
'--secondary': '0 0% 14.9%',
|
|
38
|
+
'--secondary-foreground': '0 0% 98%',
|
|
39
|
+
'--muted': '0 0% 14.9%',
|
|
40
|
+
'--muted-foreground': '0 0% 63.9%',
|
|
41
|
+
'--accent': '0 0% 14.9%',
|
|
42
|
+
'--accent-foreground': '0 0% 98%',
|
|
43
|
+
'--destructive': '0 70.9% 59.4%',
|
|
44
|
+
'--border': '0 0% 14.9%',
|
|
45
|
+
'--input': '0 0% 14.9%',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
exports.spacing = { small: 4, medium: 8, large: 16 };
|
|
49
|
+
`);
|
|
50
|
+
put(root, 'packages/styling/tailwind.cjs', `const { light, dark } = require('./src/tokens.cjs');
|
|
51
|
+
const color = (name) => 'hsl(var(--' + name + '))';
|
|
52
|
+
const surface = (name) => ({ DEFAULT: color(name), foreground: color(name + '-foreground') });
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
darkMode: 'class',
|
|
56
|
+
theme: {
|
|
57
|
+
extend: {
|
|
58
|
+
colors: {
|
|
59
|
+
border: color('border'), input: color('input'), ring: color('ring'),
|
|
60
|
+
background: color('background'), foreground: color('foreground'),
|
|
61
|
+
primary: surface('primary'), secondary: surface('secondary'),
|
|
62
|
+
destructive: surface('destructive'), muted: surface('muted'),
|
|
63
|
+
accent: surface('accent'), popover: surface('popover'), card: surface('card'),
|
|
64
|
+
},
|
|
65
|
+
borderRadius: {
|
|
66
|
+
lg: 'var(--radius)', md: 'calc(var(--radius) - 2px)', sm: 'calc(var(--radius) - 4px)',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
plugins: [function themeVariables({ addBase }) {
|
|
71
|
+
addBase({ ':root': light, '.dark:root': dark });
|
|
72
|
+
}],
|
|
73
|
+
};
|
|
74
|
+
`);
|
|
75
|
+
patchPackage(root, 'packages/styling/package.json', {
|
|
76
|
+
exports: { '.': './src/index.ts', './tailwind': './tailwind.cjs' },
|
|
77
|
+
});
|
|
78
|
+
const config = readJson(root, 'packages/styling/tsconfig.json');
|
|
79
|
+
json(root, 'packages/styling/tsconfig.json', {
|
|
80
|
+
...config,
|
|
81
|
+
compilerOptions: { ...config.compilerOptions, allowJs: true },
|
|
82
|
+
include: ['src/**/*.ts', 'src/**/*.cjs', 'tailwind.cjs'],
|
|
83
|
+
});
|
|
84
|
+
}
|
package/dist/scaffold/web.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resourceName } from '../render.js';
|
|
2
|
+
import { writeEnvironmentFile } from './environment.js';
|
|
3
|
+
import { STACK_DEPENDENCIES, NEXT_VERCEL_VERSION, NEXT_OPENNEXT_VERSION } from '../dependencies.js';
|
|
2
4
|
import { existsSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
|
3
5
|
import path from 'node:path';
|
|
4
6
|
import { configureVinext } from '../vinext.js';
|
|
@@ -46,10 +48,28 @@ function cleanShadcnStarter(root) {
|
|
|
46
48
|
const themePath = 'apps/web/components/theme-provider.tsx';
|
|
47
49
|
if (existsSync(path.join(root, themePath))) {
|
|
48
50
|
const source = readFileSync(path.join(root, themePath), 'utf8');
|
|
49
|
-
|
|
50
|
-
put(root, themePath, source
|
|
51
|
+
const updated = source
|
|
51
52
|
.replace(/target.tagName === "INPUT" \|\|\s*target.tagName === "TEXTAREA" \|\|\s*target.tagName === "SELECT"/, '["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName)')
|
|
52
|
-
.replace(/if \(event.defaultPrevented \|\| event.repeat\) \{\s*return\s*\}\s*if \(event.metaKey \|\| event.ctrlKey \|\| event.altKey\) \{\s*return\s*\}\s*if \(event.key.toLowerCase\(\) !== "d"\) \{\s*return\s*\}\s*if \(isTypingTarget\(event.target\)\) \{\s*return\s*\}/, 'if (
|
|
53
|
+
.replace(/if \(event.defaultPrevented \|\| event.repeat\) \{\s*return\s*\}\s*if \(event.metaKey \|\| event.ctrlKey \|\| event.altKey\) \{\s*return\s*\}\s*if \(event.key.toLowerCase\(\) !== "d"\) \{\s*return\s*\}\s*if \(isTypingTarget\(event.target\)\) \{\s*return\s*\}/, 'if (!isThemeShortcut(event) || isTypingTarget(event.target)) return');
|
|
54
|
+
// Group modifier handling while preserving short-circuiting and avoiding guard arrays.
|
|
55
|
+
const modifierHelper = `function hasShortcutModifier(event: KeyboardEvent) {
|
|
56
|
+
return event.metaKey || event.ctrlKey || event.altKey
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isThemeShortcut(event: KeyboardEvent) {
|
|
60
|
+
if (event.defaultPrevented || event.repeat) return false
|
|
61
|
+
return !hasShortcutModifier(event) && event.key.toLowerCase() === "d"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
`;
|
|
65
|
+
const needsModifierHelper = updated.includes('isThemeShortcut(event)') &&
|
|
66
|
+
!updated.includes('function hasShortcutModifier(');
|
|
67
|
+
const content = needsModifierHelper
|
|
68
|
+
? updated.replace(/(?:export )?function ThemeProvider\b/, (declaration) => modifierHelper + declaration)
|
|
69
|
+
: updated;
|
|
70
|
+
if (needsModifierHelper && content === updated)
|
|
71
|
+
throw new Error('The upstream theme provider changed; review its shortcut integration.');
|
|
72
|
+
put(root, themePath, content);
|
|
53
73
|
}
|
|
54
74
|
}
|
|
55
75
|
export function scaffoldWeb({ root, options, bindings, runtimeApp, has }) {
|
|
@@ -59,28 +79,28 @@ export function scaffoldWeb({ root, options, bindings, runtimeApp, has }) {
|
|
|
59
79
|
scripts: { typecheck: 'tsc --noEmit', lint: 'eslint . --max-warnings 0' },
|
|
60
80
|
});
|
|
61
81
|
if (has('vinext')) {
|
|
62
|
-
configureVinext(root,
|
|
82
|
+
configureVinext(root, resourceName(options.name, 'web'), runtimeApp === 'web' ? bindings : {});
|
|
63
83
|
}
|
|
64
84
|
else if (options.hosting === 'cloudflare') {
|
|
65
85
|
patchPackage(root, 'apps/web/package.json', {
|
|
66
86
|
dependencies: {
|
|
67
87
|
'@opennextjs/cloudflare': STACK_DEPENDENCIES['@opennextjs/cloudflare'],
|
|
68
|
-
next:
|
|
88
|
+
next: NEXT_OPENNEXT_VERSION,
|
|
69
89
|
},
|
|
70
90
|
devDependencies: { wrangler: STACK_DEPENDENCIES['wrangler'] },
|
|
71
91
|
scripts: {
|
|
72
|
-
preview: 'opennextjs-cloudflare build && opennextjs-cloudflare preview',
|
|
92
|
+
preview: 'opennextjs-cloudflare build && opennextjs-cloudflare preview --port 3000',
|
|
73
93
|
deploy: 'opennextjs-cloudflare build && opennextjs-cloudflare deploy',
|
|
74
94
|
'build:worker': 'opennextjs-cloudflare build',
|
|
75
95
|
'cf:typegen': 'wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts',
|
|
76
96
|
},
|
|
77
97
|
});
|
|
78
98
|
json(root, 'apps/web/wrangler.jsonc', {
|
|
79
|
-
...workerConfig(
|
|
99
|
+
...workerConfig(resourceName(options.name, 'web')),
|
|
80
100
|
compatibility_flags: ['nodejs_compat', 'global_fetch_strictly_public'],
|
|
81
101
|
main: '.open-next/worker.js',
|
|
82
102
|
assets: { directory: '.open-next/assets', binding: 'ASSETS' },
|
|
83
|
-
services: [{ binding: 'WORKER_SELF_REFERENCE', service:
|
|
103
|
+
services: [{ binding: 'WORKER_SELF_REFERENCE', service: resourceName(options.name, 'web') }],
|
|
84
104
|
...(runtimeApp === 'web' ? bindings : {}),
|
|
85
105
|
});
|
|
86
106
|
const nextConfig = ['next.config.ts', 'next.config.mjs', 'next.config.js'].find((file) => existsSync(path.join(root, 'apps/web', file)));
|
|
@@ -90,10 +110,10 @@ export function scaffoldWeb({ root, options, bindings, runtimeApp, has }) {
|
|
|
90
110
|
}
|
|
91
111
|
put(root, 'apps/web/open-next.config.ts', 'import { defineCloudflareConfig } from "@opennextjs/cloudflare";\nexport default defineCloudflareConfig();\n');
|
|
92
112
|
put(root, 'apps/web/public/_headers', '/_next/static/*\n Cache-Control: public,max-age=31536000,immutable\n');
|
|
93
|
-
|
|
113
|
+
writeEnvironmentFile(root, 'apps/web/.dev.vars.example', { NEXTJS_ENV: 'development' });
|
|
94
114
|
}
|
|
95
115
|
else {
|
|
96
|
-
patchPackage(root, 'apps/web/package.json', { dependencies: { next:
|
|
116
|
+
patchPackage(root, 'apps/web/package.json', { dependencies: { next: NEXT_VERCEL_VERSION } });
|
|
97
117
|
put(root, 'apps/web/next.config.ts', 'import type { NextConfig } from "next";\nconst config: NextConfig = { transpilePackages: ["@workspace/ui"], cacheComponents: true, partialPrefetching: true };\nexport default config;\n');
|
|
98
118
|
for (const file of ['next.config.js', 'next.config.mjs'])
|
|
99
119
|
rmSync(path.join(root, 'apps/web', file), { force: true });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { STACK_DEPENDENCIES } from '../dependencies.js';
|
|
1
|
+
import { STACK_DEPENDENCIES, WORKSPACE_NODE_ENGINE } from '../dependencies.js';
|
|
2
2
|
import { setPnpmPolicy } from '../pnpm-policy.js';
|
|
3
3
|
import { writeSecurityBackports } from '../security-backports.js';
|
|
4
4
|
import { BOOTSTRAP_COMMAND } from '../registry/bootstrap.js';
|
|
@@ -7,14 +7,16 @@ import { PACKAGE_MANAGER } from '../dependencies.js';
|
|
|
7
7
|
import { createSetupPlan } from '../capabilities.js';
|
|
8
8
|
import { json, put, readJson, patchPackage, } from './files.js';
|
|
9
9
|
import { writeSetupDocumentation, workerBuildSuffix } from './documentation.js';
|
|
10
|
+
import { writeStackDocumentation } from './stack-documentation.js';
|
|
10
11
|
import { writeEnvironmentExample } from './environment.js';
|
|
11
|
-
|
|
12
|
+
function securityOverrides(options) {
|
|
12
13
|
const has = (value) => options.products.includes(value);
|
|
13
|
-
|
|
14
|
-
const securityOverrides = {
|
|
14
|
+
return {
|
|
15
15
|
...(has('wxt') ? { '@wxt-dev/module-react>@vitejs/plugin-react': '5.2.0' } : {}),
|
|
16
16
|
...(has('next') || has('vinext') ? { 'sharp@<0.35.5': '0.35.5' } : {}),
|
|
17
|
-
|
|
17
|
+
...(options.products.some((product) => ['vinext', 'next', 'expo', 'electron', 'wxt', 'neon', 'd1'].includes(product))
|
|
18
|
+
? { 'source-map-js@<1.2.2': '1.2.2' }
|
|
19
|
+
: {}),
|
|
18
20
|
...(has('next') && options.hosting === 'cloudflare'
|
|
19
21
|
? { 'glob@>=10.2.0 <10.5.0': '10.5.0', 'glob@>=11.0.0 <11.1.0': '11.1.0' }
|
|
20
22
|
: {}),
|
|
@@ -34,11 +36,34 @@ export function writeStandardRoot(root, options, registry, updating = false) {
|
|
|
34
36
|
'brace-expansion@>=3 <4': '3.0.9',
|
|
35
37
|
'brace-expansion@>=4 <5.0.12': '5.0.12',
|
|
36
38
|
};
|
|
39
|
+
}
|
|
40
|
+
function writeTurboConfiguration(root, registry) {
|
|
41
|
+
const turbo = readJson(root, 'turbo.json');
|
|
42
|
+
turbo.globalEnv = [
|
|
43
|
+
...new Set([...(turbo.globalEnv ?? []), ...registry.items.flatMap((item) => item.environment)]),
|
|
44
|
+
].sort();
|
|
45
|
+
turbo.tasks = {
|
|
46
|
+
...turbo.tasks,
|
|
47
|
+
dev: { ...turbo.tasks.dev, cache: false, persistent: true },
|
|
48
|
+
lint: { ...turbo.tasks.lint, dependsOn: ['^lint'] },
|
|
49
|
+
typecheck: { ...turbo.tasks.typecheck, dependsOn: ['^typecheck'] },
|
|
50
|
+
test: { dependsOn: ['^build'], outputs: ['coverage/**'] },
|
|
51
|
+
build: {
|
|
52
|
+
...turbo.tasks.build,
|
|
53
|
+
dependsOn: [...new Set(['^build', ...(turbo.tasks.build?.dependsOn ?? [])])],
|
|
54
|
+
outputs: ['.next/**', '!.next/cache/**', '.open-next/**', 'dist/**', '.output/**'],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
json(root, 'turbo.json', turbo);
|
|
58
|
+
}
|
|
59
|
+
export function writeStandardRoot(root, options, registry, updating = false) {
|
|
60
|
+
const has = (value) => options.products.includes(value);
|
|
61
|
+
const existing = readJson(root, 'package.json');
|
|
37
62
|
patchPackage(root, 'package.json', {
|
|
38
63
|
name: options.name,
|
|
39
64
|
private: true,
|
|
40
65
|
packageManager: PACKAGE_MANAGER,
|
|
41
|
-
engines: { node:
|
|
66
|
+
engines: { node: WORKSPACE_NODE_ENGINE },
|
|
42
67
|
scripts: {
|
|
43
68
|
dev: 'turbo dev',
|
|
44
69
|
build: 'turbo build',
|
|
@@ -55,7 +80,10 @@ export function writeStandardRoot(root, options, registry, updating = false) {
|
|
|
55
80
|
},
|
|
56
81
|
devDependencies: { fallow: STACK_DEPENDENCIES['fallow'] },
|
|
57
82
|
// Upstream starter lockfiles can retain vulnerable transitive versions. Keep fixes within compatible majors.
|
|
58
|
-
pnpm: {
|
|
83
|
+
pnpm: {
|
|
84
|
+
...existing.pnpm,
|
|
85
|
+
overrides: { ...existing.pnpm?.overrides, ...securityOverrides(options) },
|
|
86
|
+
},
|
|
59
87
|
});
|
|
60
88
|
put(root, 'scripts/audit.mjs', `import { spawnSync } from 'node:child_process';
|
|
61
89
|
const base = process.env.AUDIT_BASE;
|
|
@@ -90,21 +118,7 @@ process.exitCode = result.status ?? 1;
|
|
|
90
118
|
'**/cloudflare-env.d.ts',
|
|
91
119
|
],
|
|
92
120
|
});
|
|
93
|
-
|
|
94
|
-
turbo.globalEnv = [...new Set(registry.items.flatMap((item) => item.environment))].sort();
|
|
95
|
-
turbo.tasks = {
|
|
96
|
-
...turbo.tasks,
|
|
97
|
-
dev: { ...turbo.tasks.dev, cache: false, persistent: true },
|
|
98
|
-
lint: { ...turbo.tasks.lint, dependsOn: ['^lint'] },
|
|
99
|
-
typecheck: { ...turbo.tasks.typecheck, dependsOn: ['^typecheck'] },
|
|
100
|
-
test: { dependsOn: ['^build'], outputs: ['coverage/**'] },
|
|
101
|
-
build: {
|
|
102
|
-
...turbo.tasks.build,
|
|
103
|
-
dependsOn: [...new Set(['^build', ...(turbo.tasks.build?.dependsOn ?? [])])],
|
|
104
|
-
outputs: ['.next/**', '!.next/cache/**', '.open-next/**', 'dist/**', '.output/**'],
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
json(root, 'turbo.json', turbo);
|
|
121
|
+
writeTurboConfiguration(root, registry);
|
|
108
122
|
setPnpmPolicy(root, {
|
|
109
123
|
allowBuilds: undefined,
|
|
110
124
|
onlyBuiltDependencies: [
|
|
@@ -128,8 +142,10 @@ process.exitCode = result.status ?? 1;
|
|
|
128
142
|
'!.env.example',
|
|
129
143
|
'.fallow/',
|
|
130
144
|
'.anhedral/',
|
|
145
|
+
'.anhedral-audit-report.json',
|
|
131
146
|
]);
|
|
132
147
|
writeEnvironmentExample(root, registry);
|
|
148
|
+
writeStackDocumentation(root, registry);
|
|
133
149
|
json(root, 'anhedral.standard.json', {
|
|
134
150
|
schemaVersion: 1,
|
|
135
151
|
standard: 'anhedral-application-stack',
|
package/dist/stack-registry.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ declare const PRODUCT_ITEMS: {
|
|
|
30
30
|
styling: ItemDefinition;
|
|
31
31
|
};
|
|
32
32
|
export type StandardProduct = keyof typeof PRODUCT_ITEMS;
|
|
33
|
-
export declare const STANDARD_PRODUCTS: readonly ("realtime" | "electron" | "wxt" | "expo" | "
|
|
33
|
+
export declare const STANDARD_PRODUCTS: readonly ("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")[];
|
|
34
34
|
declare const DEFINITIONS: {
|
|
35
35
|
vinext: ItemDefinition;
|
|
36
36
|
next: ItemDefinition;
|
|
@@ -67,7 +67,7 @@ declare const DEFINITIONS: {
|
|
|
67
67
|
export type StackItemId = keyof typeof DEFINITIONS;
|
|
68
68
|
export declare function getStackDefinition(name: string): ItemDefinition;
|
|
69
69
|
export declare function getStackItem(name: string): StackRegistryItem;
|
|
70
|
-
export declare const STACK_ITEMS: Record<"realtime" | "electron" | "eslint" | "typescript" | "wxt" | "expo" | "
|
|
70
|
+
export declare const STACK_ITEMS: Record<"realtime" | "electron" | "eslint" | "typescript" | "wxt" | "expo" | "vinext" | "next" | "workflows" | "ui" | "workspace" | "styling" | "hono" | "neon" | "d1" | "local-data" | "clerk" | "better-auth" | "r2" | "kv" | "queues" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog", StackRegistryItem>;
|
|
71
71
|
export declare const STACK_REGISTRY: {
|
|
72
72
|
schemaVersion: number;
|
|
73
73
|
name: string;
|
package/dist/stack-registry.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { scaffoldStyling } from './scaffold/styling.js';
|
|
2
|
+
import { resourceName } from './render.js';
|
|
1
3
|
import { STACK_DEPENDENCIES } from './dependencies.js';
|
|
2
4
|
import { dependencyOrder } from './dependency-order.js';
|
|
3
5
|
import { GENERATOR_VERSION } from './version.js';
|
|
@@ -55,7 +57,7 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
55
57
|
type: 'application',
|
|
56
58
|
description: 'Expo mobile application with native UI',
|
|
57
59
|
targets: ['apps/mobile'],
|
|
58
|
-
registryDependencies: ['workspace', 'typescript', 'eslint'],
|
|
60
|
+
registryDependencies: ['workspace', 'typescript', 'eslint', 'styling'],
|
|
59
61
|
setup: requirements('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']),
|
|
60
62
|
install: scaffoldExpo,
|
|
61
63
|
},
|
|
@@ -71,7 +73,7 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
71
73
|
type: 'application',
|
|
72
74
|
description: 'WXT browser side-panel extension',
|
|
73
75
|
targets: ['apps/extension'],
|
|
74
|
-
registryDependencies: ['workspace', 'typescript', 'eslint'],
|
|
76
|
+
registryDependencies: ['workspace', 'typescript', 'eslint', 'styling'],
|
|
75
77
|
setup: requirements('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']),
|
|
76
78
|
install: scaffoldWxt,
|
|
77
79
|
},
|
|
@@ -106,7 +108,7 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
106
108
|
d1_databases: [
|
|
107
109
|
{
|
|
108
110
|
binding: 'DB',
|
|
109
|
-
database_name:
|
|
111
|
+
database_name: resourceName(options.name, 'db'),
|
|
110
112
|
database_id: 'REPLACE_WITH_D1_ID',
|
|
111
113
|
migrations_dir: '../../packages/db/migrations',
|
|
112
114
|
},
|
|
@@ -120,7 +122,12 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
120
122
|
targets: ['packages/local-data'],
|
|
121
123
|
registryDependencies: ['workspace', 'typescript', 'eslint'],
|
|
122
124
|
setup: requirements('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']),
|
|
123
|
-
package: packageSource('local-data',
|
|
125
|
+
package: packageSource('local-data', `// Device-local SQLite and filesystem need platform-specific adapters.
|
|
126
|
+
export interface LocalDataStore {
|
|
127
|
+
read(key: string): Promise<string | null>;
|
|
128
|
+
write(key: string, value: string): Promise<void>;
|
|
129
|
+
}
|
|
130
|
+
`, {}),
|
|
124
131
|
},
|
|
125
132
|
clerk: {
|
|
126
133
|
type: 'integration',
|
|
@@ -130,7 +137,12 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
130
137
|
registryDependencies: ['workspace', 'typescript', 'eslint'],
|
|
131
138
|
setup: requirements('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']),
|
|
132
139
|
install: scaffoldClerk,
|
|
133
|
-
environment: ['NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY', 'CLERK_SECRET_KEY'],
|
|
140
|
+
environment: ['NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY', 'CLERK_PUBLISHABLE_KEY', 'CLERK_SECRET_KEY'],
|
|
141
|
+
resolveEnvironment: ({ has }) => [
|
|
142
|
+
'CLERK_SECRET_KEY',
|
|
143
|
+
...(has('vinext') || has('next') ? ['NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY'] : []),
|
|
144
|
+
...(has('hono') ? ['CLERK_PUBLISHABLE_KEY'] : []),
|
|
145
|
+
],
|
|
134
146
|
runtime: 'server',
|
|
135
147
|
resolveDependencies: ({ has }) => ['vinext', 'next', 'hono'].filter((id) => has(id)),
|
|
136
148
|
resolveTargets: ({ has }) => [
|
|
@@ -162,7 +174,7 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
162
174
|
]),
|
|
163
175
|
install: scaffoldPrivateFiles,
|
|
164
176
|
configure: ({ options }) => ({
|
|
165
|
-
r2_buckets: [{ binding: 'FILES', bucket_name:
|
|
177
|
+
r2_buckets: [{ binding: 'FILES', bucket_name: resourceName(options.name, 'private-files') }],
|
|
166
178
|
}),
|
|
167
179
|
runtime: 'worker',
|
|
168
180
|
resolveTargets: ({ runtimeApp }) => [`apps/${runtimeApp}/private-files.md`],
|
|
@@ -194,7 +206,7 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
194
206
|
setup: requirements('Deferred jobs', ['Wrangler'], ['Cloudflare Queues configuration access'], [], ['Queue, producer/consumer bindings and failure handling'], ['Retries, idempotency, concurrency and dead-letter recovery']),
|
|
195
207
|
install: scaffoldQueues,
|
|
196
208
|
configure: ({ options }) => ({
|
|
197
|
-
queues: { producers: [{ binding: 'JOBS', queue:
|
|
209
|
+
queues: { producers: [{ binding: 'JOBS', queue: resourceName(options.name, 'jobs') }] },
|
|
198
210
|
}),
|
|
199
211
|
runtime: 'optional-worker',
|
|
200
212
|
resolveTargets: ({ runtimeApp, has }) => [
|
|
@@ -298,7 +310,9 @@ const PRODUCT_ITEMS = defineItems({
|
|
|
298
310
|
'Basin dataset/stream, ANALYTICS binding, optional private R2 sink and authorized sink writes',
|
|
299
311
|
], ['Private ingestion, event delivery, query results and archive retrieval when selected']),
|
|
300
312
|
package: packageSource('analytics', `export interface EventStream { send(events: Record<string, unknown>[]): Promise<void> }
|
|
301
|
-
export async function recordEvents(stream: EventStream, events: Record<string, unknown>[]) {
|
|
313
|
+
export async function recordEvents(stream: EventStream, events: Record<string, unknown>[]) {
|
|
314
|
+
await stream.send(events);
|
|
315
|
+
}
|
|
302
316
|
`, {}),
|
|
303
317
|
resolvePackage: ({ has }, recipe) => ({
|
|
304
318
|
...recipe,
|
|
@@ -325,8 +339,21 @@ export async function recordEvents(stream: EventStream, events: Record<string, u
|
|
|
325
339
|
description: 'Shared platform-independent styling tokens',
|
|
326
340
|
targets: ['packages/styling'],
|
|
327
341
|
registryDependencies: ['workspace', 'typescript', 'eslint'],
|
|
328
|
-
setup: requirements('Shared styling definitions', [], [], [], [
|
|
329
|
-
|
|
342
|
+
setup: requirements('Shared styling definitions', [], [], [], [
|
|
343
|
+
'Shared light/dark tokens and Tailwind v3 preset for Expo/WXT; adapt platform typography and components',
|
|
344
|
+
], ['Consistency across selected interfaces; preserve platform-specific components']),
|
|
345
|
+
package: packageSource('styling', `import { light, dark, spacing } from './tokens.cjs';
|
|
346
|
+
|
|
347
|
+
export const styling = {
|
|
348
|
+
colors: {
|
|
349
|
+
foreground: 'hsl(' + light['--foreground'] + ')',
|
|
350
|
+
background: 'hsl(' + light['--background'] + ')',
|
|
351
|
+
},
|
|
352
|
+
spacing,
|
|
353
|
+
themes: { light, dark },
|
|
354
|
+
};
|
|
355
|
+
`, {}),
|
|
356
|
+
install: scaffoldStyling,
|
|
330
357
|
},
|
|
331
358
|
});
|
|
332
359
|
export const STANDARD_PRODUCTS = Object.freeze(Object.keys(PRODUCT_ITEMS));
|
|
@@ -366,7 +393,7 @@ function freeze(value) {
|
|
|
366
393
|
}
|
|
367
394
|
const DEFINITIONS = freeze({ ...FOUNDATION_ITEMS, ...PRODUCT_ITEMS });
|
|
368
395
|
function describe(definition) {
|
|
369
|
-
const { install: _install, configure: _configure, resolveTargets: _targets, resolveDependencies: _dependencies, resolvePackage: _package, fallbackDependency: _fallback, ...metadata } = definition;
|
|
396
|
+
const { install: _install, configure: _configure, resolveTargets: _targets, resolveEnvironment: _environment, resolveDependencies: _dependencies, resolvePackage: _package, fallbackDependency: _fallback, ...metadata } = definition;
|
|
370
397
|
return structuredClone(metadata);
|
|
371
398
|
}
|
|
372
399
|
export function getStackDefinition(name) {
|
package/dist/standard.js
CHANGED
|
@@ -8,7 +8,7 @@ import { registerVercelApplication } from './vercel.js';
|
|
|
8
8
|
import { registerApplication } from './delivery.js';
|
|
9
9
|
import { runStagedTransaction } from './transaction.js';
|
|
10
10
|
import { createSetupPlan } from './capabilities.js';
|
|
11
|
-
import { bootstrapWorkspace, assertBootstrapPlatform } from './scaffold/bootstrap.js';
|
|
11
|
+
import { bootstrapWorkspace, assertBootstrapPlatform, assertWorkspaceRuntime, } from './scaffold/bootstrap.js';
|
|
12
12
|
import { composeStack } from './scaffold/compose.js';
|
|
13
13
|
import { parseStandardOptions } from './registry/options.js';
|
|
14
14
|
import { STANDARD_USAGE } from './registry/usage.js';
|
|
@@ -21,6 +21,7 @@ export async function scaffoldStandardProject(options, run = execFile) {
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
assertBootstrapPlatform(run);
|
|
24
|
+
assertWorkspaceRuntime(run);
|
|
24
25
|
const root = options.root;
|
|
25
26
|
const previousQuiet = process.env.ANHEDRAL_QUIET;
|
|
26
27
|
if (options.json)
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const GENERATOR_VERSION = "0.10.
|
|
1
|
+
export declare const GENERATOR_VERSION = "0.10.1";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const GENERATOR_VERSION = '0.10.
|
|
1
|
+
export const GENERATOR_VERSION = '0.10.1';
|
package/dist/vinext.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VINEXT_DEPENDENCIES } from './dependencies.js';
|
|
1
2
|
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { writeFile } from './util.js';
|
|
@@ -8,28 +9,14 @@ export function configureVinext(root, workerName, bindings) {
|
|
|
8
9
|
const pkg = JSON.parse(readFileSync(path.join(root, 'apps/web/package.json'), 'utf8'));
|
|
9
10
|
delete pkg.dependencies?.['@opennextjs/cloudflare'];
|
|
10
11
|
pkg.type = 'module';
|
|
11
|
-
pkg.dependencies = {
|
|
12
|
-
|
|
13
|
-
vinext: '1.1.0',
|
|
14
|
-
next: '16.4.0',
|
|
15
|
-
react: '19.2.8',
|
|
16
|
-
'react-dom': '19.2.8',
|
|
17
|
-
'react-server-dom-webpack': '19.2.8',
|
|
18
|
-
};
|
|
19
|
-
pkg.devDependencies = {
|
|
20
|
-
...pkg.devDependencies,
|
|
21
|
-
vite: '8.3.4',
|
|
22
|
-
'@cloudflare/vite-plugin': '1.63.1',
|
|
23
|
-
'@vitejs/plugin-rsc': '0.5.36',
|
|
24
|
-
'@vitejs/plugin-react': '6.1.2',
|
|
25
|
-
wrangler: '4.149.0',
|
|
26
|
-
};
|
|
12
|
+
pkg.dependencies = { ...pkg.dependencies, ...VINEXT_DEPENDENCIES.dependencies };
|
|
13
|
+
pkg.devDependencies = { ...pkg.devDependencies, ...VINEXT_DEPENDENCIES.devDependencies };
|
|
27
14
|
pkg.scripts = {
|
|
28
15
|
...pkg.scripts,
|
|
29
16
|
dev: 'vite dev --host 127.0.0.1 --port 3000',
|
|
30
17
|
build: 'vite build',
|
|
31
18
|
start: 'vinext start',
|
|
32
|
-
preview: 'wrangler dev --config dist/server/wrangler.json --persist-to .wrangler/state',
|
|
19
|
+
preview: 'wrangler dev --config dist/server/wrangler.json --port 3000 --persist-to .wrangler/state',
|
|
33
20
|
deploy: 'vite build && wrangler deploy --config dist/server/wrangler.json',
|
|
34
21
|
'build:worker': 'vite build',
|
|
35
22
|
typecheck: 'wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts && tsc --noEmit',
|
|
@@ -77,7 +64,11 @@ export default config;
|
|
|
77
64
|
const uiFile = path.join(root, 'packages/ui/package.json');
|
|
78
65
|
if (existsSync(uiFile)) {
|
|
79
66
|
const ui = JSON.parse(readFileSync(uiFile, 'utf8'));
|
|
80
|
-
ui.dependencies = {
|
|
67
|
+
ui.dependencies = {
|
|
68
|
+
...ui.dependencies,
|
|
69
|
+
react: VINEXT_DEPENDENCIES.dependencies.react,
|
|
70
|
+
'react-dom': VINEXT_DEPENDENCIES.dependencies['react-dom'],
|
|
71
|
+
};
|
|
81
72
|
writeFile(uiFile, JSON.stringify(ui, null, 2) + '\n');
|
|
82
73
|
}
|
|
83
74
|
}
|
package/dist/workspace-graph.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readBoundedFile, MAX_PROJECT_JSON_BYTES } from './bounded-file.js';
|
|
2
|
+
import { existsSync, lstatSync, readdirSync } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { dependencyOrder } from './dependency-order.js';
|
|
4
5
|
const dependencyFields = [
|
|
@@ -12,7 +13,7 @@ function readManifest(file, directory) {
|
|
|
12
13
|
throw new Error(`Workspace manifest missing: ${directory}.`);
|
|
13
14
|
if (lstatSync(file).isSymbolicLink())
|
|
14
15
|
throw new Error(`Workspace symlinks are unsupported: ${directory}/package.json.`);
|
|
15
|
-
const value = JSON.parse(
|
|
16
|
+
const value = JSON.parse(readBoundedFile(file, MAX_PROJECT_JSON_BYTES).toString('utf8'));
|
|
16
17
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
17
18
|
throw new Error(`Invalid workspace manifest: ${directory}.`);
|
|
18
19
|
const manifest = value;
|
package/package.json
CHANGED