@stacksjs/buddy 0.70.257 → 0.70.259

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 (113) hide show
  1. package/dist/cli.d.ts +11 -1
  2. package/dist/cli.js +5 -164
  3. package/dist/commands/about.js +1 -35
  4. package/dist/commands/add.js +1 -25
  5. package/dist/commands/ai-context.js +4 -27
  6. package/dist/commands/auth.js +1 -68
  7. package/dist/commands/build.js +1 -221
  8. package/dist/commands/cd.js +8 -39
  9. package/dist/commands/changelog.js +1 -32
  10. package/dist/commands/clean.js +1 -44
  11. package/dist/commands/cloud.js +2 -519
  12. package/dist/commands/commit.js +1 -16
  13. package/dist/commands/completion.js +6 -42
  14. package/dist/commands/config-migrate.js +6 -94
  15. package/dist/commands/configure.js +2 -45
  16. package/dist/commands/create.js +1 -162
  17. package/dist/commands/deploy.d.ts +10 -4
  18. package/dist/commands/deploy.js +13 -1664
  19. package/dist/commands/desktop-apple.js +12 -399
  20. package/dist/commands/dev.js +6 -1083
  21. package/dist/commands/dns.js +3 -103
  22. package/dist/commands/docs/buddy-commands.js +10 -132
  23. package/dist/commands/docs/buddy-commands.test.js +1 -38
  24. package/dist/commands/docs/framework-repo.js +1 -14
  25. package/dist/commands/docs/framework-repo.test.js +1 -68
  26. package/dist/commands/docs/generated-artifacts.js +3 -70
  27. package/dist/commands/docs/generated-artifacts.test.js +1 -19
  28. package/dist/commands/docs/links.js +2 -85
  29. package/dist/commands/docs/links.test.js +4 -66
  30. package/dist/commands/docs.js +1 -24
  31. package/dist/commands/doctor.js +2 -478
  32. package/dist/commands/domains.js +2 -152
  33. package/dist/commands/email.js +31 -503
  34. package/dist/commands/env.js +1 -312
  35. package/dist/commands/extension.js +1 -239
  36. package/dist/commands/features.js +9 -345
  37. package/dist/commands/fresh.js +1 -46
  38. package/dist/commands/generate.js +2 -152
  39. package/dist/commands/http.js +1 -33
  40. package/dist/commands/index.js +1 -57
  41. package/dist/commands/install.js +1 -22
  42. package/dist/commands/key.js +1 -23
  43. package/dist/commands/link.js +2 -147
  44. package/dist/commands/lint.js +2 -46
  45. package/dist/commands/list.js +1 -140
  46. package/dist/commands/mail.js +15 -1477
  47. package/dist/commands/maintenance.js +1 -141
  48. package/dist/commands/make.js +1 -392
  49. package/dist/commands/migrate-project.js +1 -49
  50. package/dist/commands/migrate.js +27 -625
  51. package/dist/commands/outdated.js +1 -19
  52. package/dist/commands/package.js +1 -17
  53. package/dist/commands/phone.js +17 -181
  54. package/dist/commands/ports.js +2 -118
  55. package/dist/commands/prepublish.js +1 -17
  56. package/dist/commands/projects.js +1 -29
  57. package/dist/commands/protocol/craft-evidence.js +4 -98
  58. package/dist/commands/protocol/craft-evidence.test.js +1 -20
  59. package/dist/commands/protocol/desktop-lifecycle-report.js +3 -57
  60. package/dist/commands/protocol/desktop-lifecycle-report.test.js +1 -32
  61. package/dist/commands/protocol/desktop-support.js +2 -32
  62. package/dist/commands/protocol/desktop-workflow.test.js +3 -35
  63. package/dist/commands/protocol/driver-registry.js +2 -71
  64. package/dist/commands/protocol/pantry-evidence.js +4 -144
  65. package/dist/commands/protocol/pantry-evidence.test.js +1 -29
  66. package/dist/commands/protocol/release-manifest.js +3 -102
  67. package/dist/commands/protocol/release-manifest.test.js +2 -34
  68. package/dist/commands/protocol/run-conformance.js +4 -346
  69. package/dist/commands/protocol/run-conformance.test.js +1 -52
  70. package/dist/commands/protocol/run-driver-contracts.js +4 -83
  71. package/dist/commands/protocol/run-driver-contracts.test.js +1 -18
  72. package/dist/commands/protocol/source-manifest.js +3 -113
  73. package/dist/commands/protocol/sync-suite.js +3 -136
  74. package/dist/commands/protocol/sync-suite.test.js +1 -44
  75. package/dist/commands/protocol.js +1 -60
  76. package/dist/commands/publish.js +8 -500
  77. package/dist/commands/queue.js +1 -248
  78. package/dist/commands/release.js +1 -42
  79. package/dist/commands/route.js +1 -21
  80. package/dist/commands/run-tool.js +1 -9
  81. package/dist/commands/saas.js +1 -25
  82. package/dist/commands/schedule.js +1 -61
  83. package/dist/commands/search.js +1 -84
  84. package/dist/commands/seed.js +1 -47
  85. package/dist/commands/serve.js +1 -55
  86. package/dist/commands/setup-ai.js +2 -121
  87. package/dist/commands/setup.js +2 -305
  88. package/dist/commands/share.js +1 -214
  89. package/dist/commands/sms.js +32 -319
  90. package/dist/commands/stacks.js +1 -72
  91. package/dist/commands/telemetry.js +1 -74
  92. package/dist/commands/test.js +1 -130
  93. package/dist/commands/tinker.js +1 -37
  94. package/dist/commands/types.js +1 -18
  95. package/dist/commands/upgrade.js +1 -98
  96. package/dist/commands/user.js +3 -84
  97. package/dist/commands/version.js +1 -14
  98. package/dist/config.js +1 -231
  99. package/dist/custom-cli.js +1 -23
  100. package/dist/database-preflight.js +1 -88
  101. package/dist/global-options.js +1 -4
  102. package/dist/index.js +1 -1
  103. package/dist/lazy-commands.js +1 -241
  104. package/dist/migrators/index.js +2 -62
  105. package/dist/migrators/laravel/index.js +1 -148
  106. package/dist/migrators/laravel/migrations.js +4 -231
  107. package/dist/migrators/laravel/models.js +8 -111
  108. package/dist/migrators/rails/index.js +1 -11
  109. package/dist/production-server.js +1 -257
  110. package/dist/project-setup.js +1 -36
  111. package/dist/serve-entry.js +1 -5
  112. package/dist/version-info.js +1 -5
  113. package/package.json +54 -47
@@ -1,113 +1,3 @@
1
- import { createHash } from "node:crypto";
2
- import { execFileSync } from "node:child_process";
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
- import { dirname, extname, resolve } from "node:path";
5
- const root = resolve(import.meta.dir, "../../../../../../.."), outputPath = resolve(root, ".github/protocol/evidence/source-manifest.json");
6
- function runGit(...arguments_) {
7
- try {
8
- return execFileSync("git", arguments_, { cwd: root, maxBuffer: 67108864 });
9
- } catch (error) {
10
- throw Error(`git ${arguments_.join(" ")} failed: ${error instanceof Error ? error.message : String(error)}`);
11
- }
12
- }
13
- function revision(value) {
14
- return runGit("rev-parse", `${value}^{commit}`).toString().trim();
15
- }
16
- function showJson(sourceRevision, path) {
17
- return JSON.parse(runGit("show", `${sourceRevision}:${path}`).toString());
18
- }
19
- function category(path) {
20
- if (/(^|\/)(test|tests|__tests__)(\/|$)|\.(test|spec)\.[^.]+$/.test(path))
21
- return "tests";
22
- if (path.startsWith(".github/protocol/suite/"))
23
- return "vendored-protocol";
24
- if (/(^|\/)(dist|generated|coverage)(\/|$)|\.d\.ts$/.test(path))
25
- return "generated";
26
- if (/\.(md|mdx|rst|txt)$/.test(path) || /(^|\/)docs\//.test(path))
27
- return "documentation";
28
- if (/(^|\/)(config|configs|\.github)(\/|$)|(^|\/)(tsconfig|package|bun\.lock|pantry)\b/.test(path))
29
- return "configuration";
30
- if (/\.(ts|tsx|js|jsx|vue|svelte|zig|rs|go|py|sh)$/.test(path))
31
- return "source";
32
- return "assets-and-data";
33
- }
34
- function addCount(record, key, bytes) {
35
- const current = record[key] || { files: 0, bytes: 0 };
36
- record[key] = { files: current.files + 1, bytes: current.bytes + bytes };
37
- }
38
- export function buildSourceManifest(requestedRevision) {
39
- const sourceRevision = revision(requestedRevision), sourceTree = runGit("rev-parse", `${sourceRevision}^{tree}`).toString().trim(), treeListing = runGit("ls-tree", "-r", "-l", sourceRevision).toString(), lines = treeListing.trim().split(`
40
- `).filter(Boolean), categories = {}, extensions = {};
41
- let totalBytes = 0;
42
- const paths = [];
43
- for (const line of lines) {
44
- const match = line.match(/^\d+\s+blob\s+[0-9a-f]+\s+(\d+)\t(.+)$/);
45
- if (!match)
46
- continue;
47
- const bytes = Number(match[1]), path = match[2];
48
- if (path == null)
49
- continue;
50
- paths.push(path);
51
- totalBytes += bytes;
52
- addCount(categories, category(path), bytes);
53
- const extension = extname(path).toLowerCase() || "[none]";
54
- extensions[extension] = (extensions[extension] || 0) + 1;
55
- }
56
- const packages = paths.filter((path) => path === "package.json" || path.endsWith("/package.json")).flatMap((path) => {
57
- const pkg = showJson(sourceRevision, path);
58
- if (!pkg.name || !pkg.version)
59
- return [];
60
- return [{ name: pkg.name, path, version: pkg.version, publishable: pkg.private !== !0 }];
61
- }).sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path)), rootPackage = showJson(sourceRevision, "package.json"), suiteLock = showJson(sourceRevision, ".github/protocol/suite.lock.json");
62
- return {
63
- schemaVersion: "1.0.0",
64
- repository: "https://github.com/stacksjs/stacks",
65
- sourceRevision,
66
- sourceTree,
67
- sourceDigest: `sha256:${createHash("sha256").update(treeListing).digest("hex")}`,
68
- generatedAt: "derived-from-immutable-git-objects",
69
- classification: {
70
- status: "reference-implementation",
71
- conformance: "unverified",
72
- notes: "Reference implementation status does not imply a protocol profile claim; consult the report for per-requirement evidence."
73
- },
74
- prerequisites: rootPackage.system || {},
75
- packages,
76
- inventory: {
77
- totalFiles: lines.length,
78
- totalBytes,
79
- categories: Object.fromEntries(Object.entries(categories).sort(([a], [b]) => a.localeCompare(b))),
80
- extensions: Object.fromEntries(Object.entries(extensions).sort(([a], [b]) => a.localeCompare(b)))
81
- },
82
- protocolSuite: { version: suiteLock.protocolVersion, rfcsRevision: suiteLock.rfcsRevision }
83
- };
84
- }
85
- function render(manifest) {
86
- return `${JSON.stringify(manifest, null, 2)}
87
- `;
88
- }
89
- export async function run() {
90
- const mode = process.argv.includes("--write") ? "write" : process.argv.includes("--check") ? "check" : null;
91
- if (!mode) {
92
- console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/source-manifest.ts --write [--revision <ref>] | --check");
93
- process.exit(2);
94
- }
95
- if (mode === "write") {
96
- const index = process.argv.indexOf("--revision"), requestedRevision = index === -1 ? "HEAD" : process.argv[index + 1];
97
- if (!requestedRevision)
98
- throw Error("--revision requires a Git ref");
99
- const manifest = buildSourceManifest(requestedRevision);
100
- mkdirSync(dirname(outputPath), { recursive: !0 });
101
- writeFileSync(outputPath, render(manifest));
102
- console.log(`Wrote source manifest for ${manifest.sourceRevision} (${manifest.inventory.totalFiles} files, ${manifest.packages.length} packages)`);
103
- } else {
104
- if (!existsSync(outputPath))
105
- throw Error("source manifest is missing; run bun run protocol:manifest");
106
- const current = JSON.parse(readFileSync(outputPath, "utf8")), expected = render(buildSourceManifest(current.sourceRevision));
107
- if (readFileSync(outputPath, "utf8") !== expected)
108
- throw Error(`source manifest is stale or modified; run bun storage/framework/core/buddy/src/commands/protocol/source-manifest.ts --write --revision ${current.sourceRevision}`);
109
- console.log(`Source manifest matches ${current.sourceRevision}`);
110
- }
111
- }
112
- if (import.meta.main)
113
- await run();
1
+ import{createHash}from"node:crypto";import{execFileSync}from"node:child_process";import{existsSync,mkdirSync,readFileSync,writeFileSync}from"node:fs";import{dirname,extname,resolve}from"node:path";const root=resolve(import.meta.dir,"../../../../../../.."),outputPath=resolve(root,".github/protocol/evidence/source-manifest.json");function runGit(...arguments_){try{return execFileSync("git",arguments_,{cwd:root,maxBuffer:67108864})}catch(error){throw Error(`git ${arguments_.join(" ")} failed: ${error instanceof Error?error.message:String(error)}`)}}function revision(value){return runGit("rev-parse",`${value}^{commit}`).toString().trim()}function showJson(sourceRevision,path){return JSON.parse(runGit("show",`${sourceRevision}:${path}`).toString())}function category(path){if(/(^|\/)(test|tests|__tests__)(\/|$)|\.(test|spec)\.[^.]+$/.test(path))return"tests";if(path.startsWith(".github/protocol/suite/"))return"vendored-protocol";if(/(^|\/)(dist|generated|coverage)(\/|$)|\.d\.ts$/.test(path))return"generated";if(/\.(md|mdx|rst|txt)$/.test(path)||/(^|\/)docs\//.test(path))return"documentation";if(/(^|\/)(config|configs|\.github)(\/|$)|(^|\/)(tsconfig|package|bun\.lock|pantry)\b/.test(path))return"configuration";if(/\.(ts|tsx|js|jsx|vue|svelte|zig|rs|go|py|sh)$/.test(path))return"source";return"assets-and-data"}function addCount(record,key,bytes){const current=record[key]||{files:0,bytes:0};record[key]={files:current.files+1,bytes:current.bytes+bytes}}export function buildSourceManifest(requestedRevision){const sourceRevision=revision(requestedRevision),sourceTree=runGit("rev-parse",`${sourceRevision}^{tree}`).toString().trim(),treeListing=runGit("ls-tree","-r","-l",sourceRevision).toString(),lines=treeListing.trim().split(`
2
+ `).filter(Boolean),categories={},extensions={};let totalBytes=0;const paths=[];for(const line of lines){const match=line.match(/^\d+\s+blob\s+[0-9a-f]+\s+(\d+)\t(.+)$/);if(!match)continue;const bytes=Number(match[1]),path=match[2];if(path==null)continue;paths.push(path);totalBytes+=bytes;addCount(categories,category(path),bytes);const extension=extname(path).toLowerCase()||"[none]";extensions[extension]=(extensions[extension]||0)+1}const packages=paths.filter((path)=>path==="package.json"||path.endsWith("/package.json")).flatMap((path)=>{const pkg=showJson(sourceRevision,path);if(!pkg.name||!pkg.version)return[];return[{name:pkg.name,path,version:pkg.version,publishable:pkg.private!==!0}]}).sort((a,b)=>a.name.localeCompare(b.name)||a.path.localeCompare(b.path)),rootPackage=showJson(sourceRevision,"package.json"),suiteLock=showJson(sourceRevision,".github/protocol/suite.lock.json");return{schemaVersion:"1.0.0",repository:"https://github.com/stacksjs/stacks",sourceRevision,sourceTree,sourceDigest:`sha256:${createHash("sha256").update(treeListing).digest("hex")}`,generatedAt:"derived-from-immutable-git-objects",classification:{status:"reference-implementation",conformance:"unverified",notes:"Reference implementation status does not imply a protocol profile claim; consult the report for per-requirement evidence."},prerequisites:rootPackage.system||{},packages,inventory:{totalFiles:lines.length,totalBytes,categories:Object.fromEntries(Object.entries(categories).sort(([a],[b])=>a.localeCompare(b))),extensions:Object.fromEntries(Object.entries(extensions).sort(([a],[b])=>a.localeCompare(b)))},protocolSuite:{version:suiteLock.protocolVersion,rfcsRevision:suiteLock.rfcsRevision}}}function render(manifest){return`${JSON.stringify(manifest,null,2)}
3
+ `}export async function run(){const mode=process.argv.includes("--write")?"write":process.argv.includes("--check")?"check":null;if(!mode){console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/source-manifest.ts --write [--revision <ref>] | --check");process.exit(2)}if(mode==="write"){const index=process.argv.indexOf("--revision"),requestedRevision=index===-1?"HEAD":process.argv[index+1];if(!requestedRevision)throw Error("--revision requires a Git ref");const manifest=buildSourceManifest(requestedRevision);mkdirSync(dirname(outputPath),{recursive:!0});writeFileSync(outputPath,render(manifest));console.log(`Wrote source manifest for ${manifest.sourceRevision} (${manifest.inventory.totalFiles} files, ${manifest.packages.length} packages)`)}else{if(!existsSync(outputPath))throw Error("source manifest is missing; run bun run protocol:manifest");const current=JSON.parse(readFileSync(outputPath,"utf8")),expected=render(buildSourceManifest(current.sourceRevision));if(readFileSync(outputPath,"utf8")!==expected)throw Error(`source manifest is stale or modified; run bun storage/framework/core/buddy/src/commands/protocol/source-manifest.ts --write --revision ${current.sourceRevision}`);console.log(`Source manifest matches ${current.sourceRevision}`)}}if(import.meta.main)await run();
@@ -1,137 +1,4 @@
1
- import { createHash } from "node:crypto";
2
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
- import { basename, dirname, relative, resolve } from "node:path";
4
- const root = resolve(import.meta.dir, "../../../../../../.."), suiteRoot = resolve(root, ".github/protocol/suite/1.0-draft"), lockPath = resolve(root, ".github/protocol/suite.lock.json");
5
- function digest(data) {
6
- return `sha256:${createHash("sha256").update(data).digest("hex")}`;
7
- }
8
- function filesBelow(directory) {
9
- return readdirSync(directory, { withFileTypes: !0 }).flatMap((entry) => {
10
- const path = resolve(directory, entry.name);
11
- return entry.isDirectory() ? filesBelow(path) : [path];
12
- }).sort();
13
- }
14
- function argument(name) {
15
- const index = process.argv.indexOf(name);
16
- return index === -1 ? void 0 : process.argv[index + 1];
17
- }
18
- function gitRevision(repository) {
19
- const result = Bun.spawnSync(["git", "-C", repository, "rev-parse", "HEAD"]);
20
- if (result.exitCode !== 0)
21
- throw Error(`Could not resolve the RFC repository revision: ${result.stderr.toString().trim()}`);
22
- return result.stdout.toString().trim();
23
- }
24
- function writeSnapshot(sourceRepository) {
25
- const sourceRoot = resolve(sourceRepository, "protocol/1.0-draft");
26
- if (!existsSync(resolve(sourceRoot, "catalog.json")))
27
- throw Error(`No protocol suite found at ${sourceRoot}`);
28
- rmSync(suiteRoot, { force: !0, recursive: !0 });
29
- const files = {}, sourceFiles = [
30
- ...filesBelow(sourceRoot).map((sourcePath) => ({ sourcePath, path: relative(sourceRoot, sourcePath) })),
31
- ...["LICENSE-SPECIFICATION.md", "LICENSE-FIXTURES.md"].map((path) => ({ sourcePath: resolve(sourceRepository, path), path }))
32
- ];
33
- for (const { sourcePath, path } of sourceFiles) {
34
- const outputPath = resolve(suiteRoot, path), contents = readFileSync(sourcePath);
35
- mkdirSync(dirname(outputPath), { recursive: !0 });
36
- writeFileSync(outputPath, contents);
37
- files[path] = digest(contents);
38
- }
39
- const lock = {
40
- protocolVersion: "1.0-draft",
41
- rfcsRevision: gitRevision(sourceRepository),
42
- sourceRepository: "https://github.com/stacksjs/rfcs",
43
- files
44
- };
45
- mkdirSync(dirname(lockPath), { recursive: !0 });
46
- writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}
47
- `);
48
- console.log(`Pinned ${Object.keys(files).length} protocol files at ${lock.rfcsRevision}`);
49
- }
50
- function checkSnapshot() {
51
- if (!existsSync(lockPath))
52
- throw Error(".github/protocol/suite.lock.json is missing; run bun run protocol:sync");
53
- const lock = JSON.parse(readFileSync(lockPath, "utf8"));
54
- if (!/^[0-9a-f]{40}$/.test(lock.rfcsRevision))
55
- throw Error("suite lock has an invalid RFC revision");
56
- const actualFiles = filesBelow(suiteRoot).map((path) => relative(suiteRoot, path)), expectedFiles = Object.keys(lock.files).sort(), unexpected = actualFiles.filter((path) => !lock.files[path]), missing = expectedFiles.filter((path) => !actualFiles.includes(path)), changed = expectedFiles.filter((path) => {
57
- const absolutePath = resolve(suiteRoot, path);
58
- return existsSync(absolutePath) && digest(readFileSync(absolutePath)) !== lock.files[path];
59
- }), errors = [
60
- ...missing.map((path) => `missing: ${path}`),
61
- ...unexpected.map((path) => `unexpected: ${path}`),
62
- ...changed.map((path) => `modified: ${path}`)
63
- ];
64
- if (errors.length > 0)
65
- throw Error(`Vendored protocol suite does not match its lock:
1
+ import{createHash}from"node:crypto";import{existsSync,mkdirSync,readFileSync,readdirSync,rmSync,writeFileSync}from"node:fs";import{basename,dirname,relative,resolve}from"node:path";const root=resolve(import.meta.dir,"../../../../../../.."),suiteRoot=resolve(root,".github/protocol/suite/1.0-draft"),lockPath=resolve(root,".github/protocol/suite.lock.json");function digest(data){return`sha256:${createHash("sha256").update(data).digest("hex")}`}function filesBelow(directory){return readdirSync(directory,{withFileTypes:!0}).flatMap((entry)=>{const path=resolve(directory,entry.name);return entry.isDirectory()?filesBelow(path):[path]}).sort()}function argument(name){const index=process.argv.indexOf(name);return index===-1?void 0:process.argv[index+1]}function gitRevision(repository){const result=Bun.spawnSync(["git","-C",repository,"rev-parse","HEAD"]);if(result.exitCode!==0)throw Error(`Could not resolve the RFC repository revision: ${result.stderr.toString().trim()}`);return result.stdout.toString().trim()}function writeSnapshot(sourceRepository){const sourceRoot=resolve(sourceRepository,"protocol/1.0-draft");if(!existsSync(resolve(sourceRoot,"catalog.json")))throw Error(`No protocol suite found at ${sourceRoot}`);rmSync(suiteRoot,{force:!0,recursive:!0});const files={},sourceFiles=[...filesBelow(sourceRoot).map((sourcePath)=>({sourcePath,path:relative(sourceRoot,sourcePath)})),...["LICENSE-SPECIFICATION.md","LICENSE-FIXTURES.md"].map((path)=>({sourcePath:resolve(sourceRepository,path),path}))];for(const{sourcePath,path}of sourceFiles){const outputPath=resolve(suiteRoot,path),contents=readFileSync(sourcePath);mkdirSync(dirname(outputPath),{recursive:!0});writeFileSync(outputPath,contents);files[path]=digest(contents)}const lock={protocolVersion:"1.0-draft",rfcsRevision:gitRevision(sourceRepository),sourceRepository:"https://github.com/stacksjs/rfcs",files};mkdirSync(dirname(lockPath),{recursive:!0});writeFileSync(lockPath,`${JSON.stringify(lock,null,2)}
2
+ `);console.log(`Pinned ${Object.keys(files).length} protocol files at ${lock.rfcsRevision}`)}function checkSnapshot(){if(!existsSync(lockPath))throw Error(".github/protocol/suite.lock.json is missing; run bun run protocol:sync");const lock=JSON.parse(readFileSync(lockPath,"utf8"));if(!/^[0-9a-f]{40}$/.test(lock.rfcsRevision))throw Error("suite lock has an invalid RFC revision");const actualFiles=filesBelow(suiteRoot).map((path)=>relative(suiteRoot,path)),expectedFiles=Object.keys(lock.files).sort(),unexpected=actualFiles.filter((path)=>!lock.files[path]),missing=expectedFiles.filter((path)=>!actualFiles.includes(path)),changed=expectedFiles.filter((path)=>{const absolutePath=resolve(suiteRoot,path);return existsSync(absolutePath)&&digest(readFileSync(absolutePath))!==lock.files[path]}),errors=[...missing.map((path)=>`missing: ${path}`),...unexpected.map((path)=>`unexpected: ${path}`),...changed.map((path)=>`modified: ${path}`)];if(errors.length>0)throw Error(`Vendored protocol suite does not match its lock:
66
3
  ${errors.join(`
67
- `)}`);
68
- console.log(`Protocol suite matches ${basename(lock.sourceRepository)}@${lock.rfcsRevision} (${actualFiles.length} files)`);
69
- }
70
- export function duplicateRequirementIds(ids) {
71
- const seen = new Set, duplicates = new Set;
72
- for (const id of ids)
73
- if (seen.has(id))
74
- duplicates.add(id);
75
- else
76
- seen.add(id);
77
- return [...duplicates].sort();
78
- }
79
- function checkCatalog() {
80
- const catalogPath = resolve(suiteRoot, "catalog.json");
81
- if (!existsSync(catalogPath))
82
- throw Error(`protocol catalog missing at ${relative(root, catalogPath)}; run bun run protocol:sync`);
83
- const catalog = JSON.parse(readFileSync(catalogPath, "utf8")), requirements = Array.isArray(catalog.requirements) ? catalog.requirements : [];
84
- if (requirements.length === 0)
85
- throw Error("protocol catalog has no requirements");
86
- const ids = requirements.map((requirement) => requirement?.id), missing = ids.filter((id) => typeof id !== "string" || id.length === 0).length;
87
- if (missing > 0)
88
- throw Error(`protocol catalog has ${missing} requirement(s) without a string id`);
89
- const duplicates = duplicateRequirementIds(ids);
90
- if (duplicates.length > 0)
91
- throw Error(`protocol catalog has duplicate requirement id(s): ${duplicates.join(", ")}`);
92
- console.log(`Protocol catalog: ${ids.length} requirement ids, all unique`);
93
- }
94
- export function danglingFixtureRequirements(fixtures, catalogIds) {
95
- const dangling = [];
96
- for (const fixture of fixtures)
97
- for (const requirement of fixture.requirements ?? [])
98
- if (!catalogIds.has(requirement))
99
- dangling.push(`${fixture.id} -> ${requirement}`);
100
- return dangling;
101
- }
102
- function checkFixtures() {
103
- const fixturesPath = resolve(suiteRoot, "fixtures/conformance.json");
104
- if (!existsSync(fixturesPath))
105
- throw Error(`protocol fixture corpus missing at ${relative(root, fixturesPath)}; run bun run protocol:sync`);
106
- const corpus = JSON.parse(readFileSync(fixturesPath, "utf8")), fixtures = Array.isArray(corpus.fixtures) ? corpus.fixtures : [];
107
- if (fixtures.length === 0)
108
- throw Error("protocol fixture corpus has no fixtures");
109
- const ids = fixtures.map((fixture) => fixture?.id), missing = ids.filter((id) => typeof id !== "string" || id.length === 0).length;
110
- if (missing > 0)
111
- throw Error(`protocol fixture corpus has ${missing} fixture(s) without a string id`);
112
- const duplicates = duplicateRequirementIds(ids);
113
- if (duplicates.length > 0)
114
- throw Error(`protocol fixture corpus has duplicate fixture id(s): ${duplicates.join(", ")}`);
115
- const catalog = JSON.parse(readFileSync(resolve(suiteRoot, "catalog.json"), "utf8")), catalogIds = new Set((catalog.requirements ?? []).map((requirement) => requirement?.id).filter((id) => typeof id === "string")), normalized = fixtures.map((fixture) => ({
116
- id: String(fixture?.id),
117
- requirements: Array.isArray(fixture?.requirements) ? fixture.requirements.filter((requirement) => typeof requirement === "string") : []
118
- })), dangling = danglingFixtureRequirements(normalized, catalogIds);
119
- if (dangling.length > 0)
120
- throw Error(`protocol fixtures reference unknown requirement id(s): ${dangling.slice(0, 10).join(", ")}`);
121
- console.log(`Protocol fixtures: ${ids.length} fixtures, ids unique, all requirement refs resolve`);
122
- }
123
- export async function run() {
124
- if (process.argv.includes("--write")) {
125
- const source = resolve(argument("--source") || process.env.STACKS_RFC_SOURCE || resolve(root, "../rfcs"));
126
- writeSnapshot(source);
127
- } else if (process.argv.includes("--check")) {
128
- checkSnapshot();
129
- checkCatalog();
130
- checkFixtures();
131
- } else {
132
- console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/sync-suite.ts --write [--source ../rfcs] | --check");
133
- process.exit(2);
134
- }
135
- }
136
- if (import.meta.main)
137
- await run();
4
+ `)}`);console.log(`Protocol suite matches ${basename(lock.sourceRepository)}@${lock.rfcsRevision} (${actualFiles.length} files)`)}export function duplicateRequirementIds(ids){const seen=new Set,duplicates=new Set;for(const id of ids)if(seen.has(id))duplicates.add(id);else seen.add(id);return[...duplicates].sort()}function checkCatalog(){const catalogPath=resolve(suiteRoot,"catalog.json");if(!existsSync(catalogPath))throw Error(`protocol catalog missing at ${relative(root,catalogPath)}; run bun run protocol:sync`);const catalog=JSON.parse(readFileSync(catalogPath,"utf8")),requirements=Array.isArray(catalog.requirements)?catalog.requirements:[];if(requirements.length===0)throw Error("protocol catalog has no requirements");const ids=requirements.map((requirement)=>requirement?.id),missing=ids.filter((id)=>typeof id!=="string"||id.length===0).length;if(missing>0)throw Error(`protocol catalog has ${missing} requirement(s) without a string id`);const duplicates=duplicateRequirementIds(ids);if(duplicates.length>0)throw Error(`protocol catalog has duplicate requirement id(s): ${duplicates.join(", ")}`);console.log(`Protocol catalog: ${ids.length} requirement ids, all unique`)}export function danglingFixtureRequirements(fixtures,catalogIds){const dangling=[];for(const fixture of fixtures)for(const requirement of fixture.requirements??[])if(!catalogIds.has(requirement))dangling.push(`${fixture.id} -> ${requirement}`);return dangling}function checkFixtures(){const fixturesPath=resolve(suiteRoot,"fixtures/conformance.json");if(!existsSync(fixturesPath))throw Error(`protocol fixture corpus missing at ${relative(root,fixturesPath)}; run bun run protocol:sync`);const corpus=JSON.parse(readFileSync(fixturesPath,"utf8")),fixtures=Array.isArray(corpus.fixtures)?corpus.fixtures:[];if(fixtures.length===0)throw Error("protocol fixture corpus has no fixtures");const ids=fixtures.map((fixture)=>fixture?.id),missing=ids.filter((id)=>typeof id!=="string"||id.length===0).length;if(missing>0)throw Error(`protocol fixture corpus has ${missing} fixture(s) without a string id`);const duplicates=duplicateRequirementIds(ids);if(duplicates.length>0)throw Error(`protocol fixture corpus has duplicate fixture id(s): ${duplicates.join(", ")}`);const catalog=JSON.parse(readFileSync(resolve(suiteRoot,"catalog.json"),"utf8")),catalogIds=new Set((catalog.requirements??[]).map((requirement)=>requirement?.id).filter((id)=>typeof id==="string")),normalized=fixtures.map((fixture)=>({id:String(fixture?.id),requirements:Array.isArray(fixture?.requirements)?fixture.requirements.filter((requirement)=>typeof requirement==="string"):[]})),dangling=danglingFixtureRequirements(normalized,catalogIds);if(dangling.length>0)throw Error(`protocol fixtures reference unknown requirement id(s): ${dangling.slice(0,10).join(", ")}`);console.log(`Protocol fixtures: ${ids.length} fixtures, ids unique, all requirement refs resolve`)}export async function run(){if(process.argv.includes("--write")){const source=resolve(argument("--source")||process.env.STACKS_RFC_SOURCE||resolve(root,"../rfcs"));writeSnapshot(source)}else if(process.argv.includes("--check")){checkSnapshot();checkCatalog();checkFixtures()}else{console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/sync-suite.ts --write [--source ../rfcs] | --check");process.exit(2)}}if(import.meta.main)await run();
@@ -1,44 +1 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { describe, expect, it } from "bun:test";
4
- import { danglingFixtureRequirements, duplicateRequirementIds } from "./sync-suite";
5
- describe("protocol requirement-id uniqueness (stacksjs/stacks#2050)", () => {
6
- it("reports no duplicates for a unique list", () => {
7
- expect(duplicateRequirementIds(["CORE-CONV-01", "CORE-CONV-02", "CORE-MVA-01"])).toEqual([]);
8
- });
9
- it("reports each duplicated id once, sorted", () => {
10
- expect(duplicateRequirementIds(["B-1", "A-1", "B-1", "A-1", "A-1", "C-1"])).toEqual(["A-1", "B-1"]);
11
- });
12
- it("handles an empty list", () => {
13
- expect(duplicateRequirementIds([])).toEqual([]);
14
- });
15
- it("the vendored catalog.json has unique requirement ids", () => {
16
- const ids = JSON.parse(readFileSync(resolve(import.meta.dir, "../../../../../../../.github/protocol/suite/1.0-draft/catalog.json"), "utf8")).requirements.map((requirement) => requirement.id);
17
- expect(ids.length).toBeGreaterThan(0);
18
- expect(duplicateRequirementIds(ids)).toEqual([]);
19
- });
20
- });
21
- describe("protocol fixture-corpus integrity (stacksjs/stacks#2051)", () => {
22
- const catalogIds = new Set(["CORE-CONV-01", "CORE-CONV-02", "CORE-MVA-01"]);
23
- it("reports no dangling refs when every requirement resolves", () => {
24
- expect(danglingFixtureRequirements([
25
- { id: "fixture.a", requirements: ["CORE-CONV-01", "CORE-CONV-02"] },
26
- { id: "fixture.b", requirements: ["CORE-MVA-01"] }
27
- ], catalogIds)).toEqual([]);
28
- });
29
- it("reports each unknown requirement ref as `fixture -> requirement`", () => {
30
- expect(danglingFixtureRequirements([
31
- { id: "fixture.a", requirements: ["CORE-CONV-01", "CORE-GONE-99"] },
32
- { id: "fixture.b", requirements: ["NOPE-01"] }
33
- ], catalogIds)).toEqual(["fixture.a -> CORE-GONE-99", "fixture.b -> NOPE-01"]);
34
- });
35
- it("tolerates fixtures with no requirements", () => {
36
- expect(danglingFixtureRequirements([{ id: "fixture.a" }], catalogIds)).toEqual([]);
37
- });
38
- it("the vendored fixture corpus has unique ids and no dangling requirement refs", () => {
39
- const base = resolve(import.meta.dir, "../../../../../../../.github/protocol/suite/1.0-draft"), corpus = JSON.parse(readFileSync(resolve(base, "fixtures/conformance.json"), "utf8")), catalog = JSON.parse(readFileSync(resolve(base, "catalog.json"), "utf8")), realIds = new Set(catalog.requirements.map((requirement) => requirement.id));
40
- expect(corpus.fixtures.length).toBeGreaterThan(0);
41
- expect(duplicateRequirementIds(corpus.fixtures.map((fixture) => fixture.id))).toEqual([]);
42
- expect(danglingFixtureRequirements(corpus.fixtures, realIds)).toEqual([]);
43
- });
44
- });
1
+ import{readFileSync}from"node:fs";import{resolve}from"node:path";import{describe,expect,it}from"bun:test";import{danglingFixtureRequirements,duplicateRequirementIds}from"./sync-suite";describe("protocol requirement-id uniqueness (stacksjs/stacks#2050)",()=>{it("reports no duplicates for a unique list",()=>{expect(duplicateRequirementIds(["CORE-CONV-01","CORE-CONV-02","CORE-MVA-01"])).toEqual([])});it("reports each duplicated id once, sorted",()=>{expect(duplicateRequirementIds(["B-1","A-1","B-1","A-1","A-1","C-1"])).toEqual(["A-1","B-1"])});it("handles an empty list",()=>{expect(duplicateRequirementIds([])).toEqual([])});it("the vendored catalog.json has unique requirement ids",()=>{const ids=JSON.parse(readFileSync(resolve(import.meta.dir,"../../../../../../../.github/protocol/suite/1.0-draft/catalog.json"),"utf8")).requirements.map((requirement)=>requirement.id);expect(ids.length).toBeGreaterThan(0);expect(duplicateRequirementIds(ids)).toEqual([])})});describe("protocol fixture-corpus integrity (stacksjs/stacks#2051)",()=>{const catalogIds=new Set(["CORE-CONV-01","CORE-CONV-02","CORE-MVA-01"]);it("reports no dangling refs when every requirement resolves",()=>{expect(danglingFixtureRequirements([{id:"fixture.a",requirements:["CORE-CONV-01","CORE-CONV-02"]},{id:"fixture.b",requirements:["CORE-MVA-01"]}],catalogIds)).toEqual([])});it("reports each unknown requirement ref as `fixture -> requirement`",()=>{expect(danglingFixtureRequirements([{id:"fixture.a",requirements:["CORE-CONV-01","CORE-GONE-99"]},{id:"fixture.b",requirements:["NOPE-01"]}],catalogIds)).toEqual(["fixture.a -> CORE-GONE-99","fixture.b -> NOPE-01"])});it("tolerates fixtures with no requirements",()=>{expect(danglingFixtureRequirements([{id:"fixture.a"}],catalogIds)).toEqual([])});it("the vendored fixture corpus has unique ids and no dangling requirement refs",()=>{const base=resolve(import.meta.dir,"../../../../../../../.github/protocol/suite/1.0-draft"),corpus=JSON.parse(readFileSync(resolve(base,"fixtures/conformance.json"),"utf8")),catalog=JSON.parse(readFileSync(resolve(base,"catalog.json"),"utf8")),realIds=new Set(catalog.requirements.map((requirement)=>requirement.id));expect(corpus.fixtures.length).toBeGreaterThan(0);expect(duplicateRequirementIds(corpus.fixtures.map((fixture)=>fixture.id))).toEqual([]);expect(danglingFixtureRequirements(corpus.fixtures,realIds)).toEqual([])})});
@@ -1,60 +1 @@
1
- import { run as runConformance } from "./protocol/run-conformance";
2
- import { run as runCraft } from "./protocol/craft-evidence";
3
- import { run as runDesktop } from "./protocol/desktop-support";
4
- import { run as runDrivers } from "./protocol/driver-registry";
5
- import { run as runDriverContracts } from "./protocol/run-driver-contracts";
6
- import { run as runPantry } from "./protocol/pantry-evidence";
7
- import { run as runRelease } from "./protocol/release-manifest";
8
- import { run as runManifest } from "./protocol/source-manifest";
9
- import { run as runSync } from "./protocol/sync-suite";
10
- import { runTool } from "./run-tool";
11
- export function protocol(buddy) {
12
- buddy.command("protocol:conformance", "Generate the Stacks protocol conformance report").action(async () => {
13
- await runConformance();
14
- });
15
- buddy.command("protocol:sync", "Sync the vendored protocol suite from stacksjs/rfcs").option("--source <path>", "Path to a local rfcs checkout", { default: void 0 }).action(async (options) => {
16
- await runTool(runSync, "--write", ...options.source ? ["--source", options.source] : []);
17
- });
18
- buddy.command("protocol:check", "Verify the vendored protocol suite is pinned + internally consistent").action(async () => {
19
- await runTool(runSync, "--check");
20
- });
21
- buddy.command("protocol:manifest", "Write the protocol source manifest").option("--revision <ref>", "Source revision to pin (default HEAD)", { default: void 0 }).action(async (options) => {
22
- await runTool(runManifest, "--write", ...options.revision ? ["--revision", options.revision] : []);
23
- });
24
- buddy.command("protocol:manifest:check", "Verify the protocol source manifest is current").action(async () => {
25
- await runTool(runManifest, "--check");
26
- });
27
- buddy.command("protocol:release", "Write the protocol release manifest").option("--tag <tag>", "Release tag", { default: void 0 }).action(async (options) => {
28
- await runTool(runRelease, "--write", ...options.tag ? ["--tag", options.tag] : []);
29
- });
30
- buddy.command("protocol:release:check", "Verify the protocol release manifest is current").action(async () => {
31
- await runTool(runRelease, "--check");
32
- });
33
- buddy.command("protocol:drivers", "Write the driver capability registry evidence").action(async () => {
34
- await runTool(runDrivers, "--write");
35
- });
36
- buddy.command("protocol:drivers:check", "Verify the driver capability registry evidence").action(async () => {
37
- await runTool(runDrivers, "--check");
38
- });
39
- buddy.command("protocol:drivers:test", "Run the driver contract suite").action(async () => {
40
- await runTool(runDriverContracts);
41
- });
42
- buddy.command("protocol:desktop", "Write the desktop support matrix evidence").action(async () => {
43
- await runTool(runDesktop, "--write");
44
- });
45
- buddy.command("protocol:desktop:check", "Verify the desktop support matrix evidence").action(async () => {
46
- await runTool(runDesktop, "--check");
47
- });
48
- buddy.command("protocol:pantry", "Write the pantry evidence").action(async () => {
49
- await runTool(runPantry, "--write");
50
- });
51
- buddy.command("protocol:pantry:check", "Verify the pantry evidence").action(async () => {
52
- await runTool(runPantry, "--check");
53
- });
54
- buddy.command("protocol:craft", "Write the Craft evidence").action(async () => {
55
- await runTool(runCraft, "--write");
56
- });
57
- buddy.command("protocol:craft:check", "Verify the Craft evidence").action(async () => {
58
- await runTool(runCraft, "--check");
59
- });
60
- }
1
+ import{run as runConformance}from"./protocol/run-conformance";import{run as runCraft}from"./protocol/craft-evidence";import{run as runDesktop}from"./protocol/desktop-support";import{run as runDrivers}from"./protocol/driver-registry";import{run as runDriverContracts}from"./protocol/run-driver-contracts";import{run as runPantry}from"./protocol/pantry-evidence";import{run as runRelease}from"./protocol/release-manifest";import{run as runManifest}from"./protocol/source-manifest";import{run as runSync}from"./protocol/sync-suite";import{runTool}from"./run-tool";export function protocol(buddy){buddy.command("protocol:conformance","Generate the Stacks protocol conformance report").action(async()=>{await runConformance()});buddy.command("protocol:sync","Sync the vendored protocol suite from stacksjs/rfcs").option("--source <path>","Path to a local rfcs checkout",{default:void 0}).action(async(options)=>{await runTool(runSync,"--write",...options.source?["--source",options.source]:[])});buddy.command("protocol:check","Verify the vendored protocol suite is pinned + internally consistent").action(async()=>{await runTool(runSync,"--check")});buddy.command("protocol:manifest","Write the protocol source manifest").option("--revision <ref>","Source revision to pin (default HEAD)",{default:void 0}).action(async(options)=>{await runTool(runManifest,"--write",...options.revision?["--revision",options.revision]:[])});buddy.command("protocol:manifest:check","Verify the protocol source manifest is current").action(async()=>{await runTool(runManifest,"--check")});buddy.command("protocol:release","Write the protocol release manifest").option("--tag <tag>","Release tag",{default:void 0}).action(async(options)=>{await runTool(runRelease,"--write",...options.tag?["--tag",options.tag]:[])});buddy.command("protocol:release:check","Verify the protocol release manifest is current").action(async()=>{await runTool(runRelease,"--check")});buddy.command("protocol:drivers","Write the driver capability registry evidence").action(async()=>{await runTool(runDrivers,"--write")});buddy.command("protocol:drivers:check","Verify the driver capability registry evidence").action(async()=>{await runTool(runDrivers,"--check")});buddy.command("protocol:drivers:test","Run the driver contract suite").action(async()=>{await runTool(runDriverContracts)});buddy.command("protocol:desktop","Write the desktop support matrix evidence").action(async()=>{await runTool(runDesktop,"--write")});buddy.command("protocol:desktop:check","Verify the desktop support matrix evidence").action(async()=>{await runTool(runDesktop,"--check")});buddy.command("protocol:pantry","Write the pantry evidence").action(async()=>{await runTool(runPantry,"--write")});buddy.command("protocol:pantry:check","Verify the pantry evidence").action(async()=>{await runTool(runPantry,"--check")});buddy.command("protocol:craft","Write the Craft evidence").action(async()=>{await runTool(runCraft,"--write")});buddy.command("protocol:craft:check","Verify the Craft evidence").action(async()=>{await runTool(runCraft,"--check")})}