anhedral 0.8.5 → 0.9.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 (126) hide show
  1. package/README.md +140 -34
  2. package/assets/anhedral-cli-init-technical.svg +8 -8
  3. package/assets/anhedral-cli-init.svg +9 -9
  4. package/assets/security/LICENSES.txt +63 -0
  5. package/assets/security/audit-deps.mjs +295 -0
  6. package/assets/security/braces-regression.mjs +102 -0
  7. package/assets/security/braces@3.0.3.patch +285 -0
  8. package/assets/security/decode-uri-component@0.2.2.patch +149 -0
  9. package/assets/security/decoder-regression.mjs +37 -0
  10. package/assets/security/forge-regression.mjs +46 -0
  11. package/assets/security/manifest.json +53 -0
  12. package/assets/security/node-forge@1.4.0.patch +26 -0
  13. package/dist/access.d.ts +10 -0
  14. package/dist/access.js +93 -0
  15. package/dist/auth-schema-snapshot.d.ts +346 -0
  16. package/dist/auth-schema-snapshot.js +347 -0
  17. package/dist/auth-schema.d.ts +1 -0
  18. package/dist/auth-schema.js +57 -0
  19. package/dist/bin.js +353 -10
  20. package/dist/bounded-file.d.ts +2 -0
  21. package/dist/bounded-file.js +30 -0
  22. package/dist/capabilities.d.ts +19 -33
  23. package/dist/capabilities.js +73 -71
  24. package/dist/cloudflare.d.ts +14 -0
  25. package/dist/cloudflare.js +66 -0
  26. package/dist/command.js +12 -3
  27. package/dist/delivery.d.ts +118 -0
  28. package/dist/delivery.js +611 -0
  29. package/dist/dependencies.d.ts +22 -0
  30. package/dist/dependencies.js +109 -67
  31. package/dist/dependency-order.d.ts +2 -0
  32. package/dist/dependency-order.js +24 -0
  33. package/dist/index.d.ts +18 -1
  34. package/dist/index.js +12 -1
  35. package/dist/managed-hosting.d.ts +49 -0
  36. package/dist/managed-hosting.js +373 -0
  37. package/dist/management.d.ts +3 -0
  38. package/dist/management.js +47 -0
  39. package/dist/platforms/desktop.d.ts +2 -2
  40. package/dist/platforms/desktop.js +22 -12
  41. package/dist/platforms/extension.d.ts +2 -2
  42. package/dist/platforms/extension.js +12 -4
  43. package/dist/platforms/mobile.d.ts +2 -2
  44. package/dist/platforms/mobile.js +13 -7
  45. package/dist/pnpm-policy.d.ts +4 -0
  46. package/dist/pnpm-policy.js +66 -0
  47. package/dist/project-state.d.ts +28 -0
  48. package/dist/project-state.js +226 -0
  49. package/dist/provider-readiness.d.ts +30 -0
  50. package/dist/provider-readiness.js +87 -0
  51. package/dist/provision.d.ts +46 -0
  52. package/dist/provision.js +197 -0
  53. package/dist/readiness.d.ts +4 -1
  54. package/dist/readiness.js +80 -8
  55. package/dist/registry/add.d.ts +22 -0
  56. package/dist/registry/add.js +198 -0
  57. package/dist/registry/bootstrap.d.ts +2 -0
  58. package/dist/registry/bootstrap.js +14 -0
  59. package/dist/registry/commands.d.ts +2 -0
  60. package/dist/registry/commands.js +99 -0
  61. package/dist/registry/context.d.ts +3 -0
  62. package/dist/registry/context.js +21 -0
  63. package/dist/registry/options.d.ts +2 -0
  64. package/dist/registry/options.js +65 -0
  65. package/dist/registry/receipt.d.ts +3 -0
  66. package/dist/registry/receipt.js +73 -0
  67. package/dist/registry/remote.d.ts +25 -0
  68. package/dist/registry/remote.js +168 -0
  69. package/dist/registry/resolve.d.ts +10 -0
  70. package/dist/registry/resolve.js +98 -0
  71. package/dist/registry/types.d.ts +64 -0
  72. package/dist/registry/usage.d.ts +1 -0
  73. package/dist/registry/usage.js +38 -0
  74. package/dist/render.js +3 -3
  75. package/dist/repository.d.ts +24 -0
  76. package/dist/repository.js +96 -0
  77. package/dist/scaffold/api.d.ts +2 -0
  78. package/dist/scaffold/api.js +13 -0
  79. package/dist/scaffold/auth.d.ts +3 -0
  80. package/dist/scaffold/auth.js +95 -0
  81. package/dist/scaffold/bootstrap.d.ts +2 -0
  82. package/dist/scaffold/bootstrap.js +34 -0
  83. package/dist/scaffold/compose.d.ts +4 -0
  84. package/dist/scaffold/compose.js +34 -0
  85. package/dist/scaffold/database.d.ts +2 -0
  86. package/dist/scaffold/database.js +43 -0
  87. package/dist/scaffold/documentation.d.ts +3 -0
  88. package/dist/scaffold/documentation.js +91 -0
  89. package/dist/scaffold/environment.d.ts +2 -0
  90. package/dist/scaffold/environment.js +5 -0
  91. package/dist/scaffold/files.d.ts +25 -0
  92. package/dist/scaffold/files.js +108 -0
  93. package/dist/scaffold/integrations.d.ts +3 -0
  94. package/dist/scaffold/integrations.js +7 -0
  95. package/dist/scaffold/services.d.ts +5 -0
  96. package/dist/scaffold/services.js +84 -0
  97. package/dist/scaffold/web.d.ts +2 -0
  98. package/dist/scaffold/web.js +102 -0
  99. package/dist/scaffold/workspace.d.ts +3 -0
  100. package/dist/scaffold/workspace.js +150 -0
  101. package/dist/security-backports.d.ts +3 -0
  102. package/dist/security-backports.js +26 -0
  103. package/dist/stack-registry.d.ts +80 -0
  104. package/dist/stack-registry.js +424 -0
  105. package/dist/standard.d.ts +6 -17
  106. package/dist/standard.js +41 -585
  107. package/dist/transaction.js +46 -26
  108. package/dist/util.js +12 -2
  109. package/dist/vercel.d.ts +98 -0
  110. package/dist/vercel.js +796 -0
  111. package/dist/version.d.ts +1 -1
  112. package/dist/version.js +1 -1
  113. package/dist/vinext.d.ts +1 -0
  114. package/dist/vinext.js +83 -0
  115. package/dist/workspace-graph.d.ts +7 -0
  116. package/dist/workspace-graph.js +73 -0
  117. package/docs/application-stack-standard.md +75 -674
  118. package/docs/delivery-workflow.md +83 -0
  119. package/docs/managed-hosting-architecture.md +87 -0
  120. package/docs/sponsorships.md +25 -0
  121. package/package.json +23 -28
  122. package/dist/project.d.ts +0 -4
  123. package/dist/standard-products.d.ts +0 -2
  124. package/dist/standard-products.js +0 -6
  125. /package/assets/{images/svg/logo-white-subtract.svg → anhedral-mark.svg} +0 -0
  126. /package/dist/{project.js → registry/types.js} +0 -0
@@ -1,3 +1,4 @@
1
+ import { readBoundedFile } from './bounded-file.js';
1
2
  import { closeSync, cpSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmdirSync, rmSync, unlinkSync, writeFileSync, } from 'node:fs';
2
3
  import { createHash, randomUUID } from 'node:crypto';
3
4
  import os from 'node:os';
@@ -18,10 +19,10 @@ function assertRelativePath(relativePath) {
18
19
  normalized === '.' ||
19
20
  normalized === '..' ||
20
21
  normalized.startsWith(`..${path.sep}`) ||
21
- normalized.split(path.sep).includes('..')
22
- || rawSegments[0] === TRANSACTION_DIRECTORY
23
- || rawSegments[0] === LOCK_FILE
24
- || rawSegments[0] === JOURNAL_FILE) {
22
+ normalized.split(path.sep).includes('..') ||
23
+ rawSegments[0] === TRANSACTION_DIRECTORY ||
24
+ rawSegments[0] === LOCK_FILE ||
25
+ rawSegments[0] === JOURNAL_FILE) {
25
26
  throw new Error(`Unsafe transaction path: ${relativePath}`);
26
27
  }
27
28
  }
@@ -81,8 +82,8 @@ function normalizeTransactionPaths(rawCommitPaths, rawDeletePaths) {
81
82
  for (const written of commitPaths) {
82
83
  const deletedAncestor = ancestorInSet(written, deleteSet);
83
84
  if (deleteSet.has(written) || deletedAncestor || deleteAncestorPaths.has(written)) {
84
- throw new Error(`Ambiguous transaction write/delete paths include ${written}`
85
- + (deletedAncestor ? ` below ${deletedAncestor}` : ''));
85
+ throw new Error(`Ambiguous transaction write/delete paths include ${written}` +
86
+ (deletedAncestor ? ` below ${deletedAncestor}` : ''));
86
87
  }
87
88
  }
88
89
  const uniquePaths = [...commitPaths, ...deletePaths].sort();
@@ -100,7 +101,9 @@ function assertExactObjectKeys(value, expected, context) {
100
101
  const details = [
101
102
  missing.length > 0 ? `missing ${missing.join(', ')}` : null,
102
103
  unknown.length > 0 ? `unknown ${unknown.join(', ')}` : null,
103
- ].filter((entry) => entry !== null).join('; ');
104
+ ]
105
+ .filter((entry) => entry !== null)
106
+ .join('; ');
104
107
  throw new Error(`Invalid ${context} fields (${details}).`);
105
108
  }
106
109
  function lstatIfPresent(target) {
@@ -238,7 +241,7 @@ function parseLockOwner(lockPath) {
238
241
  }
239
242
  let value;
240
243
  try {
241
- value = JSON.parse(readFileSync(lockPath, 'utf8'));
244
+ value = JSON.parse(readBoundedFile(lockPath, 16 * 1024).toString('utf8'));
242
245
  }
243
246
  catch (error) {
244
247
  throw new Error(`Cannot read transaction lock owner at ${lockPath}; remove it only after confirming no Anhedral process is running.`, { cause: error });
@@ -401,7 +404,11 @@ function acquireTransactionLock(root) {
401
404
  createdAt: new Date().toISOString(),
402
405
  };
403
406
  const temporaryPath = `${lockPath}.${owner.token}.tmp`;
404
- writeFileSync(temporaryPath, JSON.stringify(owner) + '\n', { encoding: 'utf8', flag: 'wx', mode: 0o600 });
407
+ writeFileSync(temporaryPath, JSON.stringify(owner) + '\n', {
408
+ encoding: 'utf8',
409
+ flag: 'wx',
410
+ mode: 0o600,
411
+ });
405
412
  try {
406
413
  for (let attempt = 0; attempt < 3; attempt += 1) {
407
414
  if (clearDeadReclaimer(lockPath, reclaimPath, owner)) {
@@ -489,13 +496,17 @@ function validateJournal(root, value) {
489
496
  throw new Error(`Cannot recover unsupported transaction journal at ${path.join(root, JOURNAL_FILE)}`);
490
497
  }
491
498
  assertExactObjectKeys(value, ['version', 'backupRoot', 'stageRoot', 'entries', 'createdDirectories'], 'transaction journal');
492
- if (typeof value.backupRoot !== 'string' || typeof value.stageRoot !== 'string' || !Array.isArray(value.entries)) {
499
+ if (typeof value.backupRoot !== 'string' ||
500
+ typeof value.stageRoot !== 'string' ||
501
+ !Array.isArray(value.entries)) {
493
502
  throw new Error(`Invalid transaction journal at ${path.join(root, JOURNAL_FILE)}`);
494
503
  }
495
504
  assertJournalRoots(root, value.backupRoot, value.stageRoot);
496
505
  const seen = new Set();
497
506
  const entries = value.entries.map((entry, index) => {
498
- if (!isRecord(entry) || typeof entry.relativePath !== 'string' || typeof entry.installed !== 'boolean') {
507
+ if (!isRecord(entry) ||
508
+ typeof entry.relativePath !== 'string' ||
509
+ typeof entry.installed !== 'boolean') {
499
510
  throw new Error(`Invalid transaction journal entry at index ${index}`);
500
511
  }
501
512
  assertExactObjectKeys(entry, ['relativePath', 'backupPath', 'installed', 'installedFingerprint'], `transaction journal entry at index ${index}`);
@@ -507,15 +518,16 @@ function validateJournal(root, value) {
507
518
  throw new Error(`Invalid transaction backup path at index ${index}`);
508
519
  }
509
520
  const installedFingerprint = entry.installedFingerprint;
510
- if (installedFingerprint !== null
511
- && (typeof installedFingerprint !== 'string' || !/^[a-f0-9]{64}$/.test(installedFingerprint))) {
521
+ if (installedFingerprint !== null &&
522
+ (typeof installedFingerprint !== 'string' || !/^[a-f0-9]{64}$/.test(installedFingerprint))) {
512
523
  throw new Error(`Invalid installed transaction fingerprint at index ${index}`);
513
524
  }
514
525
  if (entry.installed ? installedFingerprint === null : installedFingerprint !== null) {
515
526
  throw new Error(`Transaction journal entry fingerprint state is inconsistent at index ${index}`);
516
527
  }
517
528
  const expectedBackup = resolveTransactionPath(value.backupRoot, entry.relativePath);
518
- if (entry.backupPath !== null && path.resolve(entry.backupPath) !== path.resolve(expectedBackup)) {
529
+ if (entry.backupPath !== null &&
530
+ path.resolve(entry.backupPath) !== path.resolve(expectedBackup)) {
519
531
  throw new Error(`Unsafe transaction backup path: ${entry.backupPath}`);
520
532
  }
521
533
  return {
@@ -544,7 +556,13 @@ function validateJournal(root, value) {
544
556
  throw new Error(`Transaction created directory is not a parent of any journal entry: ${directory}`);
545
557
  }
546
558
  }
547
- return { version: JOURNAL_VERSION, backupRoot: value.backupRoot, stageRoot: value.stageRoot, entries, createdDirectories };
559
+ return {
560
+ version: JOURNAL_VERSION,
561
+ backupRoot: value.backupRoot,
562
+ stageRoot: value.stageRoot,
563
+ entries,
564
+ createdDirectories,
565
+ };
548
566
  }
549
567
  function writeJournal(root, journal) {
550
568
  const journalPath = path.join(root, JOURNAL_FILE);
@@ -617,13 +635,13 @@ function restoreInstalledEntry(root, journal, entry, target) {
617
635
  if (entry.installed) {
618
636
  if (pathEntryExists(target)) {
619
637
  if (entry.installedFingerprint === null) {
620
- throw new Error(`Installed transaction path has no trusted fingerprint: ${entry.relativePath}. `
621
- + 'Recovery stopped without modifying it; inspect the journal, target, and backup manually.');
638
+ throw new Error(`Installed transaction path has no trusted fingerprint: ${entry.relativePath}. ` +
639
+ 'Recovery stopped without modifying it; inspect the journal, target, and backup manually.');
622
640
  }
623
641
  const actualFingerprint = fingerprintEntry(target);
624
642
  if (actualFingerprint !== entry.installedFingerprint) {
625
- throw new Error(`Installed transaction path changed after an interrupted commit: ${entry.relativePath}. `
626
- + 'Recovery stopped without modifying it; inspect the journal, target, and backup manually.');
643
+ throw new Error(`Installed transaction path changed after an interrupted commit: ${entry.relativePath}. ` +
644
+ 'Recovery stopped without modifying it; inspect the journal, target, and backup manually.');
627
645
  }
628
646
  }
629
647
  removeEntryDurably(target);
@@ -650,8 +668,7 @@ function restoreJournal(root, journal) {
650
668
  writeJournal(root, journal);
651
669
  }
652
670
  }
653
- for (const relativePath of [...journal.createdDirectories]
654
- .sort((left, right) => right.split('/').length - left.split('/').length || right.localeCompare(left))) {
671
+ for (const relativePath of [...journal.createdDirectories].sort((left, right) => right.split('/').length - left.split('/').length || right.localeCompare(left))) {
655
672
  assertNoSymlinkComponents(root, relativePath);
656
673
  const target = resolveTransactionPath(root, relativePath);
657
674
  try {
@@ -676,7 +693,7 @@ function recoverInterruptedTransactionUnlocked(root) {
676
693
  if (lstatSync(journalPath).isSymbolicLink()) {
677
694
  throw new Error(`Refusing symbolic-link transaction journal at ${journalPath}.`);
678
695
  }
679
- const journal = validateJournal(root, JSON.parse(readFileSync(journalPath, 'utf8')));
696
+ const journal = validateJournal(root, JSON.parse(readBoundedFile(journalPath, 16 * 1024 * 1024).toString('utf8')));
680
697
  restoreJournal(root, journal);
681
698
  return true;
682
699
  }
@@ -697,8 +714,8 @@ export class PostCommitError extends Error {
697
714
  committedPaths;
698
715
  constructor(committedPaths, cause) {
699
716
  const causeMessage = cause instanceof Error ? cause.message : String(cause);
700
- super(`Changes were committed successfully, but the post-commit action failed: ${causeMessage}. `
701
- + 'The generated files were kept; resolve the failure and rerun the post-commit command.', { cause });
717
+ super(`Changes were committed successfully, but the post-commit action failed: ${causeMessage}. ` +
718
+ 'The generated files were kept; resolve the failure and rerun the post-commit command.', { cause });
702
719
  this.name = 'PostCommitError';
703
720
  this.committedPaths = Object.freeze([...committedPaths]);
704
721
  }
@@ -713,7 +730,10 @@ function assertTransactionNamespace(transactionDirectory) {
713
730
  for (const entry of readdirSync(transactionDirectory)) {
714
731
  const candidate = path.join(transactionDirectory, entry);
715
732
  const stat = lstatIfPresent(candidate);
716
- if (!LOCAL_TRANSACTION_ROOT_PATTERN.test(entry) || !stat || stat.isSymbolicLink() || !stat.isDirectory()) {
733
+ if (!LOCAL_TRANSACTION_ROOT_PATTERN.test(entry) ||
734
+ !stat ||
735
+ stat.isSymbolicLink() ||
736
+ !stat.isDirectory()) {
717
737
  throw new Error(`Unknown or unsafe artifact in reserved transaction namespace requires manual inspection: ${candidate}`);
718
738
  }
719
739
  }
@@ -806,7 +826,7 @@ export async function runStagedTransaction(root, options) {
806
826
  recoverInterruptedTransactionUnlocked(root);
807
827
  if (!options.dryRun)
808
828
  cleanupOrphanTransactionRoots(root);
809
- if (await options.prepare?.() === false)
829
+ if ((await options.prepare?.()) === false)
810
830
  return Object.freeze([]);
811
831
  assertTransactionNamespace(transactionDirectory);
812
832
  namespaceValidated = true;
package/dist/util.js CHANGED
@@ -20,7 +20,13 @@ export function appendGitignore(root, lines) {
20
20
  const additions = [...new Set(lines)].filter((line) => !existingLines.has(line));
21
21
  if (additions.length === 0)
22
22
  return;
23
- const newline = existing.includes('\r\n') ? '\r\n' : existing.includes('\n') ? '\n' : existing.includes('\r') ? '\r' : '\n';
23
+ const newline = existing.includes('\r\n')
24
+ ? '\r\n'
25
+ : existing.includes('\n')
26
+ ? '\n'
27
+ : existing.includes('\r')
28
+ ? '\r'
29
+ : '\n';
24
30
  const separator = existing.length > 0 && !/(?:\r\n|\n|\r)$/.test(existing) ? newline : '';
25
31
  writeFile(filePath, `${existing}${separator}${additions.join(newline)}${newline}`);
26
32
  }
@@ -56,7 +62,11 @@ export function execFile(executable, args, cwd) {
56
62
  const display = [executable, ...args].join(' ');
57
63
  if (process.env.ANHEDRAL_VERBOSE === '1' && !quiet) {
58
64
  console.log(` $ ${display}`);
59
- const result = spawnSync(invocation.command, invocation.args, { cwd, stdio: 'inherit', shell: false });
65
+ const result = spawnSync(invocation.command, invocation.args, {
66
+ cwd,
67
+ stdio: 'inherit',
68
+ shell: false,
69
+ });
60
70
  if (result.error || result.status !== 0) {
61
71
  throw new Error(`Command failed (exit ${result.status ?? '?'}): ${display}`, result.error ? { cause: result.error } : undefined);
62
72
  }
@@ -0,0 +1,98 @@
1
+ import { type DeliveryRunner } from './project-state.js';
2
+ interface WebProject {
3
+ schemaVersion: 1;
4
+ id: string;
5
+ name: string;
6
+ ownership: 'customer';
7
+ provider: 'vercel';
8
+ directory: 'apps/web';
9
+ teamId: string | null;
10
+ projectId: string | null;
11
+ scope: string | null;
12
+ audience: 'unconfigured' | 'public' | 'private';
13
+ use: 'unconfigured' | 'personal' | 'commercial';
14
+ repository?: {
15
+ repoId: number;
16
+ nameWithOwner: string;
17
+ productionBranch?: string;
18
+ };
19
+ }
20
+ interface WebRelease {
21
+ id: string;
22
+ commit: string;
23
+ operation: 'ship' | 'rollback' | 'adopt' | 'recover';
24
+ provenance?: 'cli' | 'git';
25
+ branch?: string;
26
+ state: 'running' | 'deployed' | 'failed';
27
+ startedAt: string;
28
+ deploymentId?: string;
29
+ url?: string;
30
+ deploymentUrl?: string;
31
+ restoredReleaseId?: string;
32
+ verification?: {
33
+ state: 'passed' | 'failed';
34
+ checkedAt: string;
35
+ };
36
+ }
37
+ export declare function registerVercelApplication(root: string): WebProject;
38
+ export declare function connectVercelApplication(root: string, options: {
39
+ scope?: string;
40
+ audience: 'public' | 'private';
41
+ use: 'personal' | 'commercial';
42
+ create?: boolean;
43
+ git?: boolean;
44
+ }, run?: DeliveryRunner): Promise<{
45
+ project: WebProject;
46
+ plan: any;
47
+ planActivated: boolean;
48
+ }>;
49
+ export declare function adoptVercelApplication(root: string, run?: DeliveryRunner): Promise<{
50
+ projectId: string;
51
+ release: WebRelease;
52
+ adopted: boolean;
53
+ productVerification: string;
54
+ }>;
55
+ export declare function recoverVercelApplication(root: string, run?: DeliveryRunner): Promise<{
56
+ projectId: string;
57
+ release: WebRelease;
58
+ adopted: boolean;
59
+ productVerification: string;
60
+ }>;
61
+ export declare function planVercelDelivery(root: string): {
62
+ project: WebProject;
63
+ checks: string[];
64
+ deployment: string[];
65
+ cloudflareServices: string;
66
+ mutation: boolean;
67
+ };
68
+ export declare function vercelApplicationStatus(root: string, refresh?: boolean, run?: DeliveryRunner): Promise<{
69
+ project: WebProject;
70
+ releases: WebRelease[];
71
+ activeRecordedVersion: boolean | undefined;
72
+ evidence: string;
73
+ productVerification: string;
74
+ management: string | null;
75
+ }>;
76
+ export declare function shipVercelApplication(root: string, run?: DeliveryRunner): Promise<{
77
+ projectId: string;
78
+ release: WebRelease;
79
+ productVerification: string;
80
+ }>;
81
+ export declare function verifyVercelApplication(root: string, run?: DeliveryRunner): Promise<{
82
+ projectId: string;
83
+ releaseId: string;
84
+ verification: {
85
+ state: "passed" | "failed";
86
+ checkedAt: string;
87
+ };
88
+ }>;
89
+ export declare function rollbackVercelApplication(root: string, releaseId: string, dataCompatible: boolean, run?: DeliveryRunner): Promise<{
90
+ projectId: string;
91
+ release: WebRelease;
92
+ dataRestored: boolean;
93
+ productVerification: string;
94
+ automaticProductionUpdates: string;
95
+ nextAction: string;
96
+ }>;
97
+ export declare function writeVercelOverview(root: string, snapshot: Awaited<ReturnType<typeof vercelApplicationStatus>>): string;
98
+ export {};