anhedral 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +29 -256
  2. package/assets/anhedral-cli-init-technical.svg +322 -0
  3. package/assets/anhedral-cli-init.svg +257 -173
  4. package/dist/bin.js +28 -162
  5. package/dist/dependencies.d.ts +7 -56
  6. package/dist/dependencies.js +96 -305
  7. package/dist/index.d.ts +2 -7
  8. package/dist/index.js +1 -4
  9. package/dist/{templates → platforms}/desktop.d.ts +1 -1
  10. package/dist/{templates → platforms}/desktop.js +28 -266
  11. package/dist/{templates → platforms}/extension.d.ts +1 -1
  12. package/dist/platforms/extension.js +401 -0
  13. package/dist/{templates → platforms}/mobile.d.ts +1 -1
  14. package/dist/platforms/mobile.js +232 -0
  15. package/dist/project.d.ts +4 -0
  16. package/dist/project.js +1 -0
  17. package/dist/standard-products.d.ts +2 -0
  18. package/dist/standard-products.js +6 -0
  19. package/dist/standard.d.ts +18 -0
  20. package/dist/standard.js +578 -0
  21. package/dist/version.d.ts +1 -1
  22. package/dist/version.js +1 -1
  23. package/docs/application-stack-standard.md +700 -0
  24. package/package.json +34 -49
  25. package/CHANGELOG.md +0 -132
  26. package/CONTRIBUTING.md +0 -73
  27. package/SECURITY.md +0 -37
  28. package/SKILL.md +0 -244
  29. package/assets/images/svg/ably-symbol-color.svg +0 -8
  30. package/bin/anhedral.js +0 -2
  31. package/dist/architecture/contributions.d.ts +0 -28
  32. package/dist/architecture/contributions.js +0 -125
  33. package/dist/architecture/index.d.ts +0 -12
  34. package/dist/architecture/index.js +0 -6
  35. package/dist/architecture/manifest.d.ts +0 -53
  36. package/dist/architecture/manifest.js +0 -283
  37. package/dist/architecture/modules.d.ts +0 -37
  38. package/dist/architecture/modules.js +0 -184
  39. package/dist/architecture/plan.d.ts +0 -58
  40. package/dist/architecture/plan.js +0 -145
  41. package/dist/architecture/templates.d.ts +0 -10
  42. package/dist/architecture/templates.js +0 -23
  43. package/dist/cli.d.ts +0 -32
  44. package/dist/cli.js +0 -235
  45. package/dist/composers/root.d.ts +0 -6
  46. package/dist/composers/root.js +0 -35
  47. package/dist/print.d.ts +0 -8
  48. package/dist/print.js +0 -48
  49. package/dist/prompts.d.ts +0 -5
  50. package/dist/prompts.js +0 -19
  51. package/dist/scaffold.d.ts +0 -76
  52. package/dist/scaffold.js +0 -1895
  53. package/dist/template-source.d.ts +0 -20
  54. package/dist/template-source.js +0 -180
  55. package/dist/templates/api.d.ts +0 -2
  56. package/dist/templates/api.js +0 -3122
  57. package/dist/templates/assets-private-proxy.d.ts +0 -3
  58. package/dist/templates/assets-private-proxy.js +0 -277
  59. package/dist/templates/electron-updater.d.ts +0 -3
  60. package/dist/templates/electron-updater.js +0 -358
  61. package/dist/templates/extension.js +0 -732
  62. package/dist/templates/mobile.js +0 -720
  63. package/dist/templates/shared.d.ts +0 -2
  64. package/dist/templates/shared.js +0 -520
  65. package/dist/templates/web.d.ts +0 -2
  66. package/dist/templates/web.js +0 -412
  67. package/dist/toolchain.d.ts +0 -3
  68. package/dist/toolchain.js +0 -8
  69. package/dist/ui.d.ts +0 -33
  70. package/dist/ui.js +0 -199
  71. package/dist/workspace-config.d.ts +0 -11
  72. package/dist/workspace-config.js +0 -372
  73. package/docs/conventions.md +0 -92
  74. package/docs/master-stack-map.md +0 -199
  75. package/docs/output-tree-contract.md +0 -482
  76. package/docs/references/manual-scaffolding.md +0 -403
  77. package/docs/references/provisioning.md +0 -220
  78. package/scripts/render-master-stack-map.mjs +0 -510
  79. package/templates/api-fastify/apps/api/tsconfig.json +0 -20
  80. package/templates/catalog.json +0 -23
  81. package/templates/db-drizzle/packages/db/tsconfig.json +0 -14
  82. package/templates/desktop-electron/apps/desktop/tsconfig.json +0 -37
  83. package/templates/extension-wxt/apps/extension/tsconfig.json +0 -41
  84. package/templates/mobile-expo/apps/mobile/expo-env.d.ts +0 -1
  85. package/templates/web-next/apps/web/next-env.d.ts +0 -6
package/dist/cli.js DELETED
@@ -1,235 +0,0 @@
1
- import path from 'node:path';
2
- import { env } from 'node:process';
3
- import { TOOLCHAIN_CHANNELS, resolveToolchainChannel } from './toolchain.js';
4
- import { resolveModules } from './architecture/modules.js';
5
- import { packageNameFromText } from './render.js';
6
- import { isNativeStylingLibrary, isUiTarget, parseUiComponentList, } from './ui.js';
7
- export const USAGE = `
8
- anhedral new <directory> [modules...] [--ui <components>] [--native-styling <nativewind|uniwind>] [--toolchain <latest|stable>] [--skip-install] [--dry-run] [--json] [--verbose]
9
- anhedral init [modules...] [--ui <components>] [--native-styling <nativewind|uniwind>] [--toolchain <latest|stable>] [--skip-install] [--dry-run] [--json] [--verbose]
10
- anhedral add <module...> [--skip-install] [--dry-run] [--json] [--verbose]
11
- anhedral ui add <component...> [--target <client>] [--skip-install] [--dry-run] [--json] [--verbose]
12
- anhedral upgrade [--skip-install] [--dry-run] [--json] [--verbose]
13
- anhedral doctor [--json] [--verbose]
14
- anhedral --version
15
-
16
- Commands:
17
- anhedral new my-app
18
- Generate the complete TypeScript stack in a new directory. With no module flags, every module is included.
19
- anhedral new my-app --web --api --db --auth
20
- Generate a web app, Fastify API, shared database package, and auth wiring.
21
- anhedral init --web --api --db --auth
22
- Generate the same readable workspace in the current empty directory.
23
- anhedral add mobile extension
24
- Add missing modules to an existing Anhedral project.
25
- anhedral ui add button dialog --target mobile
26
- Add React Native Reusables components to Expo. DOM clients use shadcn/ui.
27
- anhedral upgrade
28
- Transactionally upgrade a supported older Anhedral project before adding modules.
29
- `;
30
- export function parseNewProjectRequest(args) {
31
- const [directory, ...moduleArgs] = args;
32
- if (!directory || directory.startsWith('--'))
33
- throw new Error('anhedral new requires a destination directory before module flags');
34
- return Object.freeze({ directory, moduleArgs: Object.freeze(moduleArgs) });
35
- }
36
- export const APP_MODULES = ['web', 'mobile', 'api', 'desktop', 'extension'];
37
- export const FEATURE_MODULES = ['db', 'auth', 'billing', 'storage', 'native-subscriptions', 'electron-updater'];
38
- export function parseCli(args) {
39
- const flags = { modules: new Set(), uiComponents: [] };
40
- for (let index = 0; index < args.length; index += 1) {
41
- const token = args[index];
42
- if (!token.startsWith('--')) {
43
- const moduleName = normalizeModuleName(token);
44
- if (moduleName) {
45
- flags.modules.add(moduleName);
46
- continue;
47
- }
48
- throw new Error(`Unexpected argument: ${token}. Use module names, module flags, --toolchain, or --skip-install`);
49
- }
50
- if (token === '--skip-install') {
51
- flags.skipInstall = true;
52
- continue;
53
- }
54
- if (token === '--dry-run') {
55
- flags.dryRun = true;
56
- continue;
57
- }
58
- if (token === '--json') {
59
- flags.json = true;
60
- continue;
61
- }
62
- if (token === '--verbose') {
63
- flags.verbose = true;
64
- continue;
65
- }
66
- if (token === '--ui') {
67
- const value = args[index + 1];
68
- if (!value || value.startsWith('--'))
69
- throw new Error('Missing value for --ui');
70
- flags.uiComponents.push(...parseUiComponentList(value));
71
- index += 1;
72
- continue;
73
- }
74
- if (token.startsWith('--ui=')) {
75
- flags.uiComponents.push(...parseUiComponentList(token.slice('--ui='.length)));
76
- continue;
77
- }
78
- if (token === '--native-styling') {
79
- const value = args[index + 1];
80
- if (!isNativeStylingLibrary(value))
81
- throw new Error('--native-styling must be nativewind or uniwind');
82
- flags.nativeStyling = value;
83
- index += 1;
84
- continue;
85
- }
86
- if (token.startsWith('--native-styling=')) {
87
- const value = token.slice('--native-styling='.length);
88
- if (!isNativeStylingLibrary(value))
89
- throw new Error('--native-styling must be nativewind or uniwind');
90
- flags.nativeStyling = value;
91
- continue;
92
- }
93
- if (token === '--toolchain') {
94
- const value = args[index + 1];
95
- if (!value || value.startsWith('--')) {
96
- throw new Error('Missing value for --toolchain');
97
- }
98
- flags.toolchain = value;
99
- index += 1;
100
- continue;
101
- }
102
- if (token.startsWith('--toolchain=')) {
103
- const value = token.slice('--toolchain='.length);
104
- if (!value) {
105
- throw new Error('Missing value for --toolchain');
106
- }
107
- flags.toolchain = value;
108
- continue;
109
- }
110
- const moduleName = normalizeModuleName(token.slice(2));
111
- if (moduleName) {
112
- flags.modules.add(moduleName);
113
- continue;
114
- }
115
- throw new Error(`Unknown flag: ${token}`);
116
- }
117
- return flags;
118
- }
119
- export function normalizeModuleName(raw) {
120
- if (APP_MODULES.includes(raw))
121
- return raw;
122
- if (FEATURE_MODULES.includes(raw))
123
- return raw;
124
- return null;
125
- }
126
- export function deriveProjectName(cwd) {
127
- return packageNameFromText(path.basename(cwd));
128
- }
129
- export function deriveDisplayName(cwd) {
130
- const base = path.basename(cwd).trim();
131
- return base || 'Anhedral App';
132
- }
133
- export function buildOptions(flags) {
134
- return buildOptionsForRoot(flags, process.cwd());
135
- }
136
- export function buildOptionsForRoot(flags, root) {
137
- const resolvedRoot = path.resolve(root);
138
- const projectName = deriveProjectName(resolvedRoot);
139
- const displayName = deriveDisplayName(resolvedRoot);
140
- if (flags.toolchain != null && !TOOLCHAIN_CHANNELS.includes(flags.toolchain)) {
141
- throw new Error(`--toolchain must be one of: ${TOOLCHAIN_CHANNELS.join(', ')}`);
142
- }
143
- const requestedModules = flags.modules.size === 0
144
- ? [...APP_MODULES, ...FEATURE_MODULES]
145
- : [...flags.modules];
146
- const resolution = resolveModules(requestedModules);
147
- return {
148
- projectName,
149
- displayName,
150
- modules: [...resolution.requestedModules],
151
- uiComponents: Array.from(new Set(flags.uiComponents)),
152
- nativeStyling: flags.nativeStyling ?? 'nativewind',
153
- skipInstall: flags.skipInstall === true || env.ANHEDRAL_SKIP_INSTALL === '1',
154
- dryRun: flags.dryRun === true,
155
- json: flags.json === true,
156
- toolchainChannel: resolveToolchainChannel(flags.toolchain ?? env.ANHEDRAL_TOOLCHAIN),
157
- rootDirectory: resolvedRoot,
158
- };
159
- }
160
- export function parseUiAddOptions(args) {
161
- const components = [];
162
- const targets = [];
163
- let skipInstall = env.ANHEDRAL_SKIP_INSTALL === '1';
164
- let dryRun = false;
165
- let json = false;
166
- for (let index = 0; index < args.length; index += 1) {
167
- const token = args[index];
168
- if (!token.startsWith('--')) {
169
- components.push(...parseUiComponentList(token));
170
- continue;
171
- }
172
- if (token === '--target') {
173
- const value = args[index + 1];
174
- if (!isUiTarget(value))
175
- throw new Error(`--target must be one of: web, mobile, desktop, extension`);
176
- targets.push(value);
177
- index += 1;
178
- continue;
179
- }
180
- if (token.startsWith('--target=')) {
181
- const value = token.slice('--target='.length);
182
- if (!isUiTarget(value))
183
- throw new Error(`--target must be one of: web, mobile, desktop, extension`);
184
- targets.push(value);
185
- continue;
186
- }
187
- if (token === '--skip-install') {
188
- skipInstall = true;
189
- continue;
190
- }
191
- if (token === '--dry-run') {
192
- dryRun = true;
193
- continue;
194
- }
195
- if (token === '--json') {
196
- json = true;
197
- continue;
198
- }
199
- if (token === '--verbose') {
200
- continue;
201
- }
202
- throw new Error(`Unknown UI option: ${token}`);
203
- }
204
- if (components.length === 0)
205
- throw new Error('anhedral ui add requires at least one component');
206
- return {
207
- components: Array.from(new Set(components)),
208
- targets: Array.from(new Set(targets)),
209
- skipInstall,
210
- dryRun,
211
- json,
212
- };
213
- }
214
- export function buildAddOptions(modules, flags) {
215
- const requestedModules = [...modules, ...flags.modules];
216
- if (requestedModules.length === 0) {
217
- throw new Error('anhedral add requires at least one module');
218
- }
219
- const normalizedModules = requestedModules.map((moduleName) => {
220
- const normalized = normalizeModuleName(moduleName);
221
- if (!normalized) {
222
- throw new Error(`Unknown module: ${moduleName}`);
223
- }
224
- return normalized;
225
- });
226
- return {
227
- modules: Array.from(new Set(normalizedModules)),
228
- skipInstall: flags.skipInstall === true || env.ANHEDRAL_SKIP_INSTALL === '1',
229
- dryRun: flags.dryRun === true,
230
- json: flags.json === true,
231
- ...((flags.toolchain ?? env.ANHEDRAL_TOOLCHAIN) != null
232
- ? { toolchainChannel: resolveToolchainChannel(flags.toolchain ?? env.ANHEDRAL_TOOLCHAIN) }
233
- : {}),
234
- };
235
- }
@@ -1,6 +0,0 @@
1
- import type { CompositionModel } from '../architecture/contributions.js';
2
- export type RootEnvironmentContext = {
3
- readonly corsOrigins: readonly string[];
4
- };
5
- export declare function composeRootEnvironment(model: CompositionModel, context: RootEnvironmentContext): readonly string[];
6
- export declare function composeVercelCrons(model: CompositionModel): readonly Record<string, string>[];
@@ -1,35 +0,0 @@
1
- const ROOT_ENVIRONMENT_ORDER = [
2
- 'ANHEDRAL_DEMO',
3
- 'PORT',
4
- 'TRUST_PROXY_HOPS',
5
- 'CORS_ORIGINS',
6
- 'BASE_URL',
7
- 'DATABASE_URL',
8
- 'CLERK_PUBLISHABLE_KEY',
9
- 'CLERK_SECRET_KEY',
10
- 'RC_WEBHOOK_SECRET',
11
- 'RC_SECRET_API_KEY',
12
- 'RC_ENTITLEMENT_ID',
13
- 'ABLY_API_KEY',
14
- 'R2_ACCOUNT_ID',
15
- 'R2_ACCESS_KEY_ID',
16
- 'R2_SECRET_ACCESS_KEY',
17
- 'R2_BUCKET_NAME',
18
- 'R2_PREFIX',
19
- 'R2_PROXY_READ_URL_TTL_SECONDS',
20
- 'DESKTOP_UPDATE_BASE_URL',
21
- 'CLOUDFLARE_API_TOKEN',
22
- 'CRON_SECRET',
23
- ];
24
- export function composeRootEnvironment(model, context) {
25
- const environment = new Map(model.environment.map((entry) => [entry.name, entry.defaultValue]));
26
- return Object.freeze(ROOT_ENVIRONMENT_ORDER.flatMap((name) => {
27
- if (!environment.has(name))
28
- return [];
29
- const value = name === 'CORS_ORIGINS' ? context.corsOrigins.join(',') : environment.get(name);
30
- return [`${name}=${value}`];
31
- }));
32
- }
33
- export function composeVercelCrons(model) {
34
- return Object.freeze(model.crons.map(({ path, schedule }) => Object.freeze({ path, schedule })));
35
- }
package/dist/print.d.ts DELETED
@@ -1,8 +0,0 @@
1
- export declare const anhedralPrint: {
2
- banner(message: string): void;
3
- section(message: string): void;
4
- step(message: string): void;
5
- done(message: string): void;
6
- info(message: string): void;
7
- fail(message: string): void;
8
- };
package/dist/print.js DELETED
@@ -1,48 +0,0 @@
1
- import { stdout } from 'node:process';
2
- const USE_COLOR = Boolean(stdout.isTTY) && process.env.NO_COLOR == null;
3
- function isQuiet() {
4
- return process.env.ANHEDRAL_QUIET === '1';
5
- }
6
- function paint(code, message) {
7
- if (!USE_COLOR)
8
- return message;
9
- return `\x1b[${code}m${message}\x1b[0m`;
10
- }
11
- const dim = (value) => paint('2', value);
12
- const bold = (value) => paint('1', value);
13
- const cyan = (value) => paint('36', value);
14
- const green = (value) => paint('32', value);
15
- const red = (value) => paint('31', value);
16
- const magenta = (value) => paint('35', value);
17
- export const anhedralPrint = {
18
- banner(message) {
19
- if (isQuiet())
20
- return;
21
- console.log(`${bold(magenta('>'))} ${bold(message)}\n`);
22
- },
23
- section(message) {
24
- if (isQuiet())
25
- return;
26
- console.log(`\n${cyan('==')} ${bold(message)}`);
27
- },
28
- step(message) {
29
- if (isQuiet())
30
- return;
31
- console.log(` ${cyan('>')} ${message}`);
32
- },
33
- done(message) {
34
- if (isQuiet())
35
- return;
36
- console.log(` ${green('ok')} ${message}`);
37
- },
38
- info(message) {
39
- if (isQuiet())
40
- return;
41
- console.log(` ${dim(message)}`);
42
- },
43
- fail(message) {
44
- if (isQuiet())
45
- return;
46
- console.log(` ${red('x')} ${message}`);
47
- },
48
- };
package/dist/prompts.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export declare function shouldPromptForInitModules(args: readonly string[], isTTY: boolean): boolean;
2
- export declare function hasUiSelection(args: readonly string[]): boolean;
3
- export declare function parsePromptModules(input: string, fallback: readonly string[]): string[];
4
- export declare const DEFAULT_PROMPT_APP_MODULES: readonly ["web", "mobile", "api", "desktop", "extension"];
5
- export declare const DEFAULT_PROMPT_FEATURE_MODULES: readonly ["db", "auth", "billing", "storage", "native-subscriptions", "electron-updater"];
package/dist/prompts.js DELETED
@@ -1,19 +0,0 @@
1
- import { APP_MODULES, FEATURE_MODULES, normalizeModuleName } from './cli.js';
2
- function hasModuleSelection(args) {
3
- return args.some((arg) => normalizeModuleName(arg.startsWith('--') ? arg.slice(2) : arg) != null);
4
- }
5
- export function shouldPromptForInitModules(args, isTTY) {
6
- return isTTY && !args.includes('--json') && !hasModuleSelection(args);
7
- }
8
- export function hasUiSelection(args) {
9
- return args.some((arg) => arg === '--ui' || arg.startsWith('--ui='));
10
- }
11
- export function parsePromptModules(input, fallback) {
12
- const tokens = input
13
- .split(/[\s,]+/)
14
- .map((entry) => entry.trim())
15
- .filter(Boolean);
16
- return tokens.length > 0 ? tokens : [...fallback];
17
- }
18
- export const DEFAULT_PROMPT_APP_MODULES = APP_MODULES;
19
- export const DEFAULT_PROMPT_FEATURE_MODULES = FEATURE_MODULES;
@@ -1,76 +0,0 @@
1
- import { type ToolchainChannel } from './toolchain.js';
2
- import { type ModuleId } from './architecture/index.js';
3
- import { type NativeStylingLibrary, type UiTarget } from './ui.js';
4
- export type AppSelections = {
5
- web: boolean;
6
- mobile: boolean;
7
- api: boolean;
8
- desktop: boolean;
9
- extension: boolean;
10
- };
11
- export type FeatureSelections = {
12
- database: boolean;
13
- auth: boolean;
14
- billing: boolean;
15
- storage: boolean;
16
- nativeSubscriptions: boolean;
17
- electronUpdater: boolean;
18
- };
19
- export interface InitOptions {
20
- projectName: string;
21
- displayName: string;
22
- modules: ModuleId[];
23
- skipInstall: boolean;
24
- dryRun: boolean;
25
- json: boolean;
26
- toolchainChannel: ToolchainChannel;
27
- uiComponents?: string[];
28
- nativeStyling?: NativeStylingLibrary;
29
- /** Destination root. Omit to scaffold the current working directory. */
30
- rootDirectory?: string;
31
- }
32
- export interface AddOptions {
33
- modules: ModuleId[];
34
- skipInstall: boolean;
35
- dryRun: boolean;
36
- json: boolean;
37
- toolchainChannel?: ToolchainChannel;
38
- }
39
- export interface UiAddOptions {
40
- components: string[];
41
- targets: UiTarget[];
42
- skipInstall: boolean;
43
- dryRun: boolean;
44
- json: boolean;
45
- }
46
- export interface UpgradeOptions {
47
- skipInstall: boolean;
48
- dryRun: boolean;
49
- json: boolean;
50
- }
51
- export interface ProjectOptions {
52
- projectName: string;
53
- displayName: string;
54
- apps: AppSelections;
55
- features: FeatureSelections;
56
- skipInstall?: boolean;
57
- nativeStyling?: NativeStylingLibrary;
58
- }
59
- export declare function supportsMobileInstallNode(version: string): boolean;
60
- export type DoctorIssue = {
61
- path: string;
62
- severity: 'error' | 'warning';
63
- message: string;
64
- };
65
- export type DoctorReport = {
66
- ok: boolean;
67
- schemaVersion: number;
68
- generatorVersion: string;
69
- issues: DoctorIssue[];
70
- };
71
- export declare function isSupportedProjectUpgrade(fromVersion: string, toVersion: string): boolean;
72
- export declare function scaffoldProject(inputOptions: InitOptions): Promise<void>;
73
- export declare function scaffoldUiComponents(uiOptions: UiAddOptions): Promise<void>;
74
- export declare function scaffoldUpgradeProject(upgradeOptions: UpgradeOptions): Promise<void>;
75
- export declare function scaffoldAddModules(addOptions: AddOptions): Promise<void>;
76
- export declare function doctorProject(): DoctorReport;