anhedral 0.8.6 → 0.10.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 (123) hide show
  1. package/README.md +137 -38
  2. package/assets/anhedral-cli-init-technical.svg +15 -15
  3. package/assets/anhedral-cli-init.svg +16 -16
  4. package/dist/access.d.ts +10 -0
  5. package/dist/access.js +93 -0
  6. package/dist/auth-schema-snapshot.d.ts +346 -0
  7. package/dist/auth-schema-snapshot.js +347 -0
  8. package/dist/auth-schema.d.ts +1 -0
  9. package/dist/auth-schema.js +57 -0
  10. package/dist/bin.js +349 -39
  11. package/dist/bounded-file.d.ts +2 -0
  12. package/dist/bounded-file.js +30 -0
  13. package/dist/capabilities.d.ts +24 -42
  14. package/dist/capabilities.js +73 -75
  15. package/dist/cloudflare.d.ts +14 -0
  16. package/dist/cloudflare.js +66 -0
  17. package/dist/command.js +12 -3
  18. package/dist/delivery.d.ts +118 -0
  19. package/dist/delivery.js +611 -0
  20. package/dist/dependencies.d.ts +24 -3
  21. package/dist/dependencies.js +112 -74
  22. package/dist/dependency-order.d.ts +2 -0
  23. package/dist/dependency-order.js +24 -0
  24. package/dist/index.d.ts +16 -3
  25. package/dist/index.js +11 -2
  26. package/dist/managed-hosting.d.ts +49 -0
  27. package/dist/managed-hosting.js +373 -0
  28. package/dist/management.d.ts +3 -0
  29. package/dist/management.js +47 -0
  30. package/dist/platforms/desktop.d.ts +2 -2
  31. package/dist/platforms/desktop.js +25 -15
  32. package/dist/platforms/extension.d.ts +2 -2
  33. package/dist/platforms/extension.js +104 -66
  34. package/dist/platforms/mobile.d.ts +2 -2
  35. package/dist/platforms/mobile.js +13 -7
  36. package/dist/pnpm-policy.d.ts +0 -2
  37. package/dist/pnpm-policy.js +18 -13
  38. package/dist/project-state.d.ts +28 -0
  39. package/dist/project-state.js +226 -0
  40. package/dist/provider-readiness.d.ts +30 -0
  41. package/dist/provider-readiness.js +87 -0
  42. package/dist/provision.d.ts +46 -0
  43. package/dist/provision.js +197 -0
  44. package/dist/readiness.d.ts +4 -2
  45. package/dist/readiness.js +64 -14
  46. package/dist/registry/add.d.ts +18 -0
  47. package/dist/registry/add.js +163 -0
  48. package/dist/registry/bootstrap.d.ts +2 -0
  49. package/dist/registry/bootstrap.js +14 -0
  50. package/dist/registry/commands.d.ts +2 -0
  51. package/dist/registry/commands.js +48 -0
  52. package/dist/registry/context.d.ts +3 -0
  53. package/dist/registry/context.js +21 -0
  54. package/dist/registry/options.d.ts +2 -0
  55. package/dist/registry/options.js +65 -0
  56. package/dist/registry/receipt.d.ts +2 -0
  57. package/dist/registry/receipt.js +52 -0
  58. package/dist/registry/resolve.d.ts +10 -0
  59. package/dist/registry/resolve.js +98 -0
  60. package/dist/registry/types.d.ts +64 -0
  61. package/dist/registry/usage.d.ts +1 -0
  62. package/dist/registry/usage.js +37 -0
  63. package/dist/render.d.ts +0 -4
  64. package/dist/render.js +3 -16
  65. package/dist/repository.d.ts +24 -0
  66. package/dist/repository.js +96 -0
  67. package/dist/scaffold/api.d.ts +2 -0
  68. package/dist/scaffold/api.js +13 -0
  69. package/dist/scaffold/auth.d.ts +3 -0
  70. package/dist/scaffold/auth.js +95 -0
  71. package/dist/scaffold/bootstrap.d.ts +3 -0
  72. package/dist/scaffold/bootstrap.js +39 -0
  73. package/dist/scaffold/compose.d.ts +4 -0
  74. package/dist/scaffold/compose.js +34 -0
  75. package/dist/scaffold/database.d.ts +2 -0
  76. package/dist/scaffold/database.js +43 -0
  77. package/dist/scaffold/documentation.d.ts +3 -0
  78. package/dist/scaffold/documentation.js +91 -0
  79. package/dist/scaffold/environment.d.ts +2 -0
  80. package/dist/scaffold/environment.js +5 -0
  81. package/dist/scaffold/files.d.ts +25 -0
  82. package/dist/scaffold/files.js +108 -0
  83. package/dist/scaffold/integrations.d.ts +3 -0
  84. package/dist/scaffold/integrations.js +7 -0
  85. package/dist/scaffold/services.d.ts +5 -0
  86. package/dist/scaffold/services.js +84 -0
  87. package/dist/scaffold/web.d.ts +2 -0
  88. package/dist/scaffold/web.js +102 -0
  89. package/dist/scaffold/workspace.d.ts +3 -0
  90. package/dist/scaffold/workspace.js +150 -0
  91. package/dist/security-backports.js +7 -2
  92. package/dist/stack-registry.d.ts +80 -0
  93. package/dist/stack-registry.js +424 -0
  94. package/dist/standard.d.ts +6 -19
  95. package/dist/standard.js +45 -658
  96. package/dist/transaction.js +47 -56
  97. package/dist/util.js +12 -2
  98. package/dist/vercel.d.ts +98 -0
  99. package/dist/vercel.js +796 -0
  100. package/dist/version.d.ts +1 -1
  101. package/dist/version.js +1 -1
  102. package/dist/vinext.d.ts +1 -0
  103. package/dist/vinext.js +83 -0
  104. package/dist/workspace-graph.d.ts +7 -0
  105. package/dist/workspace-graph.js +73 -0
  106. package/docs/application-stack-standard.md +76 -91
  107. package/docs/delivery-workflow.md +83 -0
  108. package/docs/managed-hosting-architecture.md +87 -0
  109. package/docs/sponsorships.md +25 -0
  110. package/package.json +23 -29
  111. package/dist/evidence-validation.d.ts +0 -2
  112. package/dist/evidence-validation.js +0 -18
  113. package/dist/foundation.d.ts +0 -6
  114. package/dist/foundation.js +0 -49
  115. package/dist/progress-view.d.ts +0 -57
  116. package/dist/progress-view.js +0 -69
  117. package/dist/progress.d.ts +0 -95
  118. package/dist/progress.js +0 -327
  119. package/dist/project.d.ts +0 -5
  120. package/dist/standard-products.d.ts +0 -2
  121. package/dist/standard-products.js +0 -6
  122. /package/assets/{images/svg/logo-white-subtract.svg → anhedral-mark.svg} +0 -0
  123. /package/dist/{project.js → registry/types.js} +0 -0
package/dist/bin.js CHANGED
@@ -1,7 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import { runAdd, runRegistry } from './registry/commands.js';
3
+ import { registerManagedApplication, stageManagedVersion, saveManagedVersion, deployManagedVersion, managedApplicationStatus, } from './managed-hosting.js';
4
+ import { setupRepository } from './repository.js';
2
5
  import path from 'node:path';
3
- import { closeSync, openSync, readSync } from 'node:fs';
4
- import { readProjectProgress, reportProjectProgress, validateProgressReport } from './progress.js';
6
+ import { existsSync, readFileSync } from 'node:fs';
7
+ import { registerVercelApplication, connectVercelApplication, planVercelDelivery, shipVercelApplication, vercelApplicationStatus, verifyVercelApplication, rollbackVercelApplication, adoptVercelApplication, recoverVercelApplication, writeVercelOverview, } from './vercel.js';
8
+ import { registerApplication, connectApplication, planDelivery, shipApplication, applicationStatus, verifyApplication, rollbackApplication, recoverApplication, } from './delivery.js';
9
+ import { planProvisioning, provisionApplication } from './provision.js';
10
+ import { writeManagementOverview } from './management.js';
11
+ import { inspectProviderReadiness } from './provider-readiness.js';
5
12
  import { inspectProject } from './readiness.js';
6
13
  import { argv } from 'node:process';
7
14
  import { STANDARD_USAGE, parseStandardOptions, scaffoldStandardProject } from './standard.js';
@@ -18,16 +25,92 @@ async function main() {
18
25
  console.log(json ? JSON.stringify({ version: GENERATOR_VERSION }) : GENERATOR_VERSION);
19
26
  return;
20
27
  }
21
- if (command === 'progress') {
22
- runProgress(args);
28
+ if (command === 'add') {
29
+ await runAdd(args);
30
+ return;
31
+ }
32
+ if (command === 'registry') {
33
+ await runRegistry(args, json);
34
+ return;
35
+ }
36
+ if (command === 'cloud') {
37
+ await runCloud(args);
38
+ return;
39
+ }
40
+ if (command === 'repository') {
41
+ if (args.includes('--help') || args.includes('-h')) {
42
+ console.log(STANDARD_USAGE);
43
+ return;
44
+ }
45
+ let owner;
46
+ let name;
47
+ let visibility = 'private';
48
+ let create = false;
49
+ const directories = [];
50
+ for (let index = 0; index < args.length; index++) {
51
+ const flag = args[index];
52
+ if (flag === '--json')
53
+ continue;
54
+ if (flag === '--create') {
55
+ create = true;
56
+ continue;
57
+ }
58
+ if (flag === '--owner' || flag === '--name' || flag === '--visibility') {
59
+ const value = args[++index];
60
+ if (!value || value.startsWith('-'))
61
+ throw new Error('Missing repository option value.');
62
+ if (flag === '--owner') {
63
+ if (owner)
64
+ throw new Error('Specify one GitHub owner.');
65
+ owner = value;
66
+ }
67
+ else if (flag === '--name') {
68
+ if (name)
69
+ throw new Error('Specify one repository name.');
70
+ name = value;
71
+ }
72
+ else {
73
+ if (!['private', 'public'].includes(value))
74
+ throw new Error('Visibility must be private or public.');
75
+ visibility = value;
76
+ }
77
+ continue;
78
+ }
79
+ if (flag.startsWith('-'))
80
+ throw new Error('Unknown repository option.');
81
+ directories.push(flag);
82
+ }
83
+ if (directories.length > 1)
84
+ throw new Error('Specify one application directory.');
85
+ console.log(JSON.stringify(await setupRepository(path.resolve(directories[0] ?? process.cwd()), {
86
+ owner,
87
+ name,
88
+ visibility,
89
+ create,
90
+ }), null, 2));
91
+ return;
92
+ }
93
+ if ([
94
+ 'register',
95
+ 'connect',
96
+ 'provision',
97
+ 'ship',
98
+ 'status',
99
+ 'manage',
100
+ 'verify',
101
+ 'rollback',
102
+ 'adopt',
103
+ 'recover',
104
+ ].includes(command ?? '')) {
105
+ await runDelivery(command, args);
23
106
  return;
24
107
  }
25
108
  if (command === 'doctor') {
26
- runDoctor(args);
109
+ await runDoctor(args);
27
110
  return;
28
111
  }
29
112
  if (command !== 'new' && command !== 'init') {
30
- throw new Error(`Unknown command: ${command}. Use new, init, doctor, progress, --help, or --version.`);
113
+ throw new Error(`Unknown command: ${command}. Use new, init, add, registry, doctor, repository, cloud, register, connect, provision, ship, status, manage, verify, rollback, adopt, recover, --help, or --version.`);
31
114
  }
32
115
  if (args.some((arg) => ['--help', '-h'].includes(arg))) {
33
116
  console.log(json ? JSON.stringify({ usage: STANDARD_USAGE }) : STANDARD_USAGE);
@@ -37,44 +120,247 @@ async function main() {
37
120
  if (options)
38
121
  await scaffoldStandardProject(options);
39
122
  }
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))));
123
+ async function runCloud(args) {
124
+ const [action, ...flags] = args;
125
+ if (!action || flags.includes('--help') || action === '--help') {
126
+ console.log('anhedral cloud register|stage|save|deploy|status [directory] [--artifact directory] [--version stage-id] [--audience private|public] [--json]');
127
+ return;
128
+ }
129
+ let artifact;
130
+ let version;
131
+ let audience = 'private';
132
+ const directories = [];
133
+ for (let index = 0; index < flags.length; index++) {
134
+ const flag = flags[index];
135
+ if (flag === '--json')
136
+ continue;
137
+ if ((flag === '--artifact' && action === 'stage') ||
138
+ (flag === '--version' && ['save', 'deploy'].includes(action)) ||
139
+ (flag === '--audience' && action === 'register')) {
140
+ const value = flags[++index];
141
+ if (!value || value.startsWith('-'))
142
+ throw new Error('Missing cloud option value.');
143
+ if (flag === '--artifact') {
144
+ if (artifact)
145
+ throw new Error('Specify one artifact.');
146
+ artifact = value;
147
+ }
148
+ else if (flag === '--version') {
149
+ if (version)
150
+ throw new Error('Specify one version.');
151
+ version = value;
152
+ }
153
+ else {
154
+ if (value !== 'private' && value !== 'public')
155
+ throw new Error('Audience must be private or public.');
156
+ audience = value;
157
+ }
158
+ continue;
159
+ }
160
+ if (flag.startsWith('-'))
161
+ throw new Error('Unknown cloud option.');
162
+ directories.push(flag);
163
+ }
164
+ if (directories.length > 1)
165
+ throw new Error('Specify one application directory.');
166
+ const root = path.resolve(directories[0] ?? process.cwd());
167
+ let result;
168
+ if (action === 'register')
169
+ result = registerManagedApplication(root, audience);
170
+ else if (action === 'stage') {
171
+ if (!artifact)
172
+ throw new Error('Stage requires --artifact <complete-build-directory>.');
173
+ result = await stageManagedVersion(root, path.resolve(artifact));
174
+ }
175
+ else if (action === 'save' || action === 'deploy') {
176
+ if (!version)
177
+ throw new Error('Save/deploy requires --version <stage-id>.');
178
+ result = await (action === 'save'
179
+ ? saveManagedVersion(root, version)
180
+ : deployManagedVersion(root, version));
181
+ }
182
+ else if (action === 'status')
183
+ result = await managedApplicationStatus(root);
184
+ else
185
+ throw new Error('Unknown cloud action.');
47
186
  console.log(JSON.stringify(result, null, 2));
48
187
  }
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));
188
+ async function runDelivery(command, args) {
189
+ if (args.includes('--help') || args.includes('-h')) {
190
+ console.log(STANDARD_USAGE);
191
+ return;
192
+ }
193
+ const positional = [];
194
+ let account;
195
+ let audience;
196
+ const emails = [];
197
+ let releaseId;
198
+ let provider;
199
+ let scope;
200
+ let use;
201
+ let create = false;
202
+ let git = false;
203
+ for (let index = 0; index < args.length; index++) {
204
+ const arg = args[index];
205
+ if (arg === '--provider') {
206
+ const value = args[++index];
207
+ if (!['vercel', 'cloudflare'].includes(value) || provider)
208
+ throw new Error('Specify one provider: vercel or cloudflare.');
209
+ provider = value;
210
+ continue;
211
+ }
212
+ if (command === 'connect' && arg === '--scope') {
213
+ if (scope)
214
+ throw new Error('Specify one Vercel scope.');
215
+ scope = args[++index];
216
+ if (!scope || scope.startsWith('-'))
217
+ throw new Error('Missing Vercel scope.');
218
+ continue;
219
+ }
220
+ if (command === 'connect' && arg === '--use') {
221
+ const value = args[++index];
222
+ if (!['personal', 'commercial'].includes(value) || use)
223
+ throw new Error('Specify --use personal or commercial.');
224
+ use = value;
225
+ continue;
226
+ }
227
+ if (command === 'connect' && arg === '--git') {
228
+ git = true;
229
+ continue;
230
+ }
231
+ if (command === 'connect' && arg === '--create') {
232
+ create = true;
233
+ continue;
234
+ }
235
+ if (command === 'rollback' && arg === '--release') {
236
+ releaseId = args[++index];
237
+ if (!releaseId || releaseId.startsWith('-'))
238
+ throw new Error('Missing release ID.');
239
+ continue;
240
+ }
241
+ if (command === 'rollback' && arg === '--data-compatible')
242
+ continue;
243
+ if (arg === '--json' ||
244
+ (['ship', 'provision'].includes(command) && arg === '--dry-run') ||
245
+ (['status', 'manage', 'verify', 'rollback', 'adopt', 'recover'].includes(command) &&
246
+ arg === '--refresh') ||
247
+ (command === 'manage' && arg === '--local'))
248
+ continue;
249
+ if (command === 'connect' && arg === '--account') {
250
+ if (account)
251
+ throw new Error('Specify one account.');
252
+ account = args[++index];
253
+ if (!account || account.startsWith('-'))
254
+ throw new Error('Missing account ID.');
255
+ continue;
256
+ }
257
+ if (command === 'connect' &&
258
+ arg === '--audience' &&
259
+ ['public', 'private'].includes(args[index + 1])) {
260
+ audience = args[index + 1];
261
+ index++;
262
+ continue;
263
+ }
264
+ if (command === 'connect' && arg === '--allow-email') {
265
+ const email = args[++index];
266
+ if (!email || email.startsWith('-'))
267
+ throw new Error('Missing allowed email address.');
268
+ emails.push(email);
269
+ continue;
63
270
  }
64
- return Buffer.concat(chunks).toString('utf8');
271
+ if (arg.startsWith('-'))
272
+ throw new Error(`Unknown ${command} option: ${arg}`);
273
+ positional.push(arg);
65
274
  }
66
- finally {
67
- if (fd !== 0)
68
- closeSync(fd);
275
+ if (positional.length > 1)
276
+ throw new Error('Specify at most one application directory.');
277
+ const root = path.resolve(positional[0] ?? process.cwd());
278
+ let result;
279
+ const web = provider === 'vercel' ||
280
+ (provider !== 'cloudflare' &&
281
+ (existsSync(path.join(root, 'anhedral.web.json')) ||
282
+ (command === 'register' &&
283
+ existsSync(path.join(root, 'anhedral.setup.json')) &&
284
+ JSON.parse(readFileSync(path.join(root, 'anhedral.setup.json'), 'utf8')).hosting ===
285
+ 'vercel')));
286
+ if (web) {
287
+ if (account || emails.length)
288
+ throw new Error('Cloudflare account/member flags require --provider cloudflare.');
289
+ if (command === 'register')
290
+ result = registerVercelApplication(root);
291
+ else if (command === 'connect') {
292
+ if (!audience || !use)
293
+ throw new Error('Vercel connect requires --audience public|private and --use personal|commercial.');
294
+ result = await connectVercelApplication(root, { scope, audience, use, create, git });
295
+ }
296
+ else if (command === 'ship')
297
+ result = args.includes('--dry-run')
298
+ ? planVercelDelivery(root)
299
+ : await shipVercelApplication(root);
300
+ else if (command === 'adopt')
301
+ result = await adoptVercelApplication(root);
302
+ else if (command === 'recover')
303
+ result = await recoverVercelApplication(root);
304
+ else if (command === 'verify')
305
+ result = await verifyVercelApplication(root);
306
+ else if (command === 'rollback') {
307
+ if (!releaseId)
308
+ throw new Error('Rollback requires --release <recorded-id>.');
309
+ result = await rollbackVercelApplication(root, releaseId, args.includes('--data-compatible'));
310
+ }
311
+ else if (command === 'status')
312
+ result = await vercelApplicationStatus(root, args.includes('--refresh'));
313
+ else if (command === 'manage') {
314
+ const snapshot = await vercelApplicationStatus(root, !args.includes('--local'));
315
+ result = { file: writeVercelOverview(root, snapshot), evidence: snapshot.evidence };
316
+ }
317
+ else
318
+ throw new Error('Vercel project creation is handled by connect --create; Cloudflare resource provisioning requires --provider cloudflare.');
319
+ console.log(JSON.stringify(result, null, 2));
320
+ return;
69
321
  }
322
+ if (scope || use || create || git)
323
+ throw new Error('Vercel connect options require a Vercel web manifest or --provider vercel.');
324
+ if (command === 'register')
325
+ result = registerApplication(root);
326
+ else if (command === 'connect') {
327
+ result = await connectApplication(root, account, audience, undefined, emails);
328
+ }
329
+ else if (command === 'provision')
330
+ result = args.includes('--dry-run') ? planProvisioning(root) : await provisionApplication(root);
331
+ else if (command === 'ship')
332
+ result = args.includes('--dry-run') ? planDelivery(root) : await shipApplication(root);
333
+ else if (command === 'adopt')
334
+ throw new Error('Git deployment adoption requires --provider vercel.');
335
+ else if (command === 'recover')
336
+ result = await recoverApplication(root);
337
+ else if (command === 'verify')
338
+ result = await verifyApplication(root);
339
+ else if (command === 'rollback') {
340
+ if (!releaseId)
341
+ throw new Error('Rollback requires --release <recorded-id>.');
342
+ result = await rollbackApplication(root, releaseId, args.includes('--data-compatible'));
343
+ }
344
+ else if (command === 'manage') {
345
+ const snapshot = await applicationStatus(root, !args.includes('--local'));
346
+ result = { file: writeManagementOverview(root, snapshot), evidence: snapshot.evidence };
347
+ }
348
+ else
349
+ result = await applicationStatus(root, args.includes('--refresh'));
350
+ console.log(JSON.stringify(result, null, 2));
70
351
  }
71
- function runDoctor(args) {
72
- const positional = args.filter((arg) => arg !== '--json');
352
+ async function runDoctor(args) {
353
+ const positional = args.filter((arg) => arg !== '--json' && arg !== '--providers');
73
354
  if (positional.length > 1 || positional.some((arg) => arg.startsWith('-')))
74
- throw new Error('Usage: anhedral doctor [directory] [--json]');
75
- const report = inspectProject(path.resolve(positional[0] ?? process.cwd()));
355
+ throw new Error('Usage: anhedral doctor [directory] [--providers] [--json]');
356
+ const root = path.resolve(positional[0] ?? process.cwd());
357
+ const report = args.includes('--providers')
358
+ ? await inspectProviderReadiness(root)
359
+ : inspectProject(root);
76
360
  console.log(JSON.stringify(report, null, json ? 2 : undefined));
77
- if (!report.localReady)
361
+ if (!report.localReady ||
362
+ ('prerequisitesReady' in report &&
363
+ (!report.prerequisitesReady || report.providerVerification !== 'verified-existing-context')))
78
364
  process.exitCode = 1;
79
365
  }
80
366
  function readOptions(command, args) {
@@ -93,9 +379,33 @@ try {
93
379
  }
94
380
  catch (error) {
95
381
  const message = error instanceof Error ? error.message : String(error);
96
- const code = error instanceof PostCommitError ? 'POST_COMMIT_FAILED'
97
- : command === 'doctor' || command === 'progress' ? 'INVALID_ARGUMENT'
98
- : command === 'new' || command === 'init' ? 'GENERATION_FAILED' : 'UNKNOWN_COMMAND';
382
+ const code = error instanceof PostCommitError
383
+ ? 'POST_COMMIT_FAILED'
384
+ : [
385
+ 'repository',
386
+ 'register',
387
+ 'connect',
388
+ 'provision',
389
+ 'ship',
390
+ 'status',
391
+ 'manage',
392
+ 'verify',
393
+ 'rollback',
394
+ 'adopt',
395
+ 'recover',
396
+ ].includes(command ?? '')
397
+ ? 'DELIVERY_FAILED'
398
+ : command === 'cloud'
399
+ ? 'HOSTING_FAILED'
400
+ : command === 'doctor'
401
+ ? 'INVALID_ARGUMENT'
402
+ : command === 'new' || command === 'init'
403
+ ? 'GENERATION_FAILED'
404
+ : command === 'add'
405
+ ? 'ADDITION_FAILED'
406
+ : command === 'registry'
407
+ ? 'REGISTRY_FAILED'
408
+ : 'UNKNOWN_COMMAND';
99
409
  console.error(json ? JSON.stringify({ error: message, code }) : `Error: ${message}`);
100
410
  process.exitCode = 1;
101
411
  }
@@ -0,0 +1,2 @@
1
+ /** Reject devices, links and oversized or growing files before parsing local input. */
2
+ export declare function readBoundedFile(filePath: string, maximumBytes: number): Buffer;
@@ -0,0 +1,30 @@
1
+ import { closeSync, constants, fstatSync, lstatSync, openSync, readSync } from 'node:fs';
2
+ /** Reject devices, links and oversized or growing files before parsing local input. */
3
+ export function readBoundedFile(filePath, maximumBytes) {
4
+ const stat = lstatSync(filePath);
5
+ if (!stat.isFile() || stat.isSymbolicLink())
6
+ throw new Error(`Input file must be a regular file: ${filePath}`);
7
+ const descriptor = openSync(filePath, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
8
+ try {
9
+ const opened = fstatSync(descriptor);
10
+ if (!opened.isFile())
11
+ throw new Error(`Input file must be a regular file: ${filePath}`);
12
+ if (opened.size > maximumBytes)
13
+ throw new Error(`Input file exceeds size limit: ${filePath}`);
14
+ const chunks = [];
15
+ let total = 0;
16
+ for (;;) {
17
+ const chunk = Buffer.alloc(Math.min(8192, maximumBytes + 1 - total));
18
+ const length = readSync(descriptor, chunk, 0, chunk.length, null);
19
+ if (!length)
20
+ return Buffer.concat(chunks);
21
+ total += length;
22
+ if (total > maximumBytes)
23
+ throw new Error(`Input file exceeds size limit: ${filePath}`);
24
+ chunks.push(chunk.subarray(0, length));
25
+ }
26
+ }
27
+ finally {
28
+ closeSync(descriptor);
29
+ }
30
+ }
@@ -1,58 +1,40 @@
1
- import type { StandardProduct } from './standard-products.js';
2
- import type { StandardOptions } from './standard.js';
3
- export interface Selection {
4
- app?: boolean;
5
- conflicts?: StandardProduct[];
6
- requiresAny?: StandardProduct[];
7
- requiresAll?: StandardProduct[];
8
- workerBinding?: boolean;
9
- }
10
- export interface Capability {
11
- purpose: string;
12
- delivery: 'starter' | 'sdk-factory' | 'integration-boundary';
13
- tools: string[];
14
- access: string[];
15
- credentials: string[];
16
- resources: string[];
17
- verify: string[];
18
- selection: Selection;
19
- }
20
- /** Setup requirements describe unfinished starters; they never grant authority or claim readiness. */
1
+ import type { StandardOptions, StandardProduct, SetupRequirements, StackSelection } from './registry/types.js';
2
+ export type Capability = SetupRequirements & {
3
+ selection: StackSelection;
4
+ };
21
5
  export declare const CAPABILITIES: Record<StandardProduct, Capability>;
22
- export declare function resolveStandardProducts(selected: Set<StandardProduct>, hosting: StandardOptions['hosting'], hostingSpecified: boolean): void;
23
6
  export declare const CAPABILITY_REGISTRY: {
24
7
  schemaVersion: number;
25
8
  cliVersion: string;
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>;
9
+ capabilities: Record<"realtime" | "electron" | "wxt" | "expo" | "next" | "vinext" | "hono" | "neon" | "better-auth" | "queues" | "workflows" | "d1" | "local-data" | "clerk" | "r2" | "kv" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog" | "styling", Capability>;
27
10
  };
28
- export declare function createSetupPlan(options: StandardOptions): {
29
- bootstrap: string[];
11
+ export declare function createSetupPlan(options: StandardOptions, registry?: import("./registry/resolve.js").ResolvedStackRegistry): {
12
+ schemaVersion: number;
13
+ cliVersion: string;
14
+ registryVersion: number;
15
+ standard: string;
16
+ root: string;
17
+ name: string;
18
+ products: ("realtime" | "electron" | "wxt" | "expo" | "next" | "vinext" | "hono" | "neon" | "better-auth" | "queues" | "workflows" | "d1" | "local-data" | "clerk" | "r2" | "kv" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog" | "styling")[];
19
+ hosting: import("./registry/types.js").Hosting;
20
+ registry: import("./registry/resolve.js").ResolvedStackRegistry;
21
+ bootstrap: ("pnpm" | "shadcn@4.21.4" | "dlx" | "init" | "--monorepo" | "--template" | "next")[];
30
22
  dryRun: boolean;
31
23
  setup: {
32
24
  tools: string[];
33
25
  access: string[];
34
26
  capabilities: {
35
- status: "starter" | "planned";
27
+ selection: StackSelection;
28
+ status: "planned" | "starter";
36
29
  purpose: string;
37
- delivery: "starter" | "sdk-factory" | "integration-boundary";
38
- tools: string[];
39
- access: string[];
40
- credentials: string[];
41
- resources: string[];
42
- verify: string[];
43
- selection: Selection;
44
- id: "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";
30
+ tools: readonly string[];
31
+ access: readonly string[];
32
+ credentials: readonly string[];
33
+ resources: readonly string[];
34
+ verify: readonly string[];
35
+ id: "realtime" | "electron" | "wxt" | "expo" | "next" | "vinext" | "hono" | "neon" | "better-auth" | "queues" | "workflows" | "d1" | "local-data" | "clerk" | "r2" | "kv" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog" | "styling";
45
36
  }[];
46
37
  completion: string[];
47
38
  resourcesProvisioned: boolean;
48
39
  };
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";
58
40
  };