@stacksjs/buddy 0.70.258 → 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 (112) 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.js +13 -1676
  18. package/dist/commands/desktop-apple.js +12 -399
  19. package/dist/commands/dev.js +6 -1083
  20. package/dist/commands/dns.js +3 -103
  21. package/dist/commands/docs/buddy-commands.js +10 -132
  22. package/dist/commands/docs/buddy-commands.test.js +1 -38
  23. package/dist/commands/docs/framework-repo.js +1 -14
  24. package/dist/commands/docs/framework-repo.test.js +1 -68
  25. package/dist/commands/docs/generated-artifacts.js +3 -70
  26. package/dist/commands/docs/generated-artifacts.test.js +1 -19
  27. package/dist/commands/docs/links.js +2 -85
  28. package/dist/commands/docs/links.test.js +4 -66
  29. package/dist/commands/docs.js +1 -24
  30. package/dist/commands/doctor.js +2 -478
  31. package/dist/commands/domains.js +2 -152
  32. package/dist/commands/email.js +31 -503
  33. package/dist/commands/env.js +1 -312
  34. package/dist/commands/extension.js +1 -239
  35. package/dist/commands/features.js +9 -345
  36. package/dist/commands/fresh.js +1 -46
  37. package/dist/commands/generate.js +2 -152
  38. package/dist/commands/http.js +1 -33
  39. package/dist/commands/index.js +1 -57
  40. package/dist/commands/install.js +1 -22
  41. package/dist/commands/key.js +1 -23
  42. package/dist/commands/link.js +2 -147
  43. package/dist/commands/lint.js +2 -84
  44. package/dist/commands/list.js +1 -140
  45. package/dist/commands/mail.js +15 -1477
  46. package/dist/commands/maintenance.js +1 -141
  47. package/dist/commands/make.js +1 -392
  48. package/dist/commands/migrate-project.js +1 -49
  49. package/dist/commands/migrate.js +27 -625
  50. package/dist/commands/outdated.js +1 -19
  51. package/dist/commands/package.js +1 -17
  52. package/dist/commands/phone.js +17 -181
  53. package/dist/commands/ports.js +2 -118
  54. package/dist/commands/prepublish.js +1 -17
  55. package/dist/commands/projects.js +1 -29
  56. package/dist/commands/protocol/craft-evidence.js +4 -98
  57. package/dist/commands/protocol/craft-evidence.test.js +1 -20
  58. package/dist/commands/protocol/desktop-lifecycle-report.js +3 -57
  59. package/dist/commands/protocol/desktop-lifecycle-report.test.js +1 -32
  60. package/dist/commands/protocol/desktop-support.js +2 -32
  61. package/dist/commands/protocol/desktop-workflow.test.js +3 -35
  62. package/dist/commands/protocol/driver-registry.js +2 -71
  63. package/dist/commands/protocol/pantry-evidence.js +4 -144
  64. package/dist/commands/protocol/pantry-evidence.test.js +1 -29
  65. package/dist/commands/protocol/release-manifest.js +3 -102
  66. package/dist/commands/protocol/release-manifest.test.js +2 -34
  67. package/dist/commands/protocol/run-conformance.js +4 -346
  68. package/dist/commands/protocol/run-conformance.test.js +1 -52
  69. package/dist/commands/protocol/run-driver-contracts.js +4 -83
  70. package/dist/commands/protocol/run-driver-contracts.test.js +1 -18
  71. package/dist/commands/protocol/source-manifest.js +3 -113
  72. package/dist/commands/protocol/sync-suite.js +3 -136
  73. package/dist/commands/protocol/sync-suite.test.js +1 -44
  74. package/dist/commands/protocol.js +1 -60
  75. package/dist/commands/publish.js +8 -500
  76. package/dist/commands/queue.js +1 -248
  77. package/dist/commands/release.js +1 -42
  78. package/dist/commands/route.js +1 -21
  79. package/dist/commands/run-tool.js +1 -9
  80. package/dist/commands/saas.js +1 -25
  81. package/dist/commands/schedule.js +1 -61
  82. package/dist/commands/search.js +1 -84
  83. package/dist/commands/seed.js +1 -47
  84. package/dist/commands/serve.js +1 -55
  85. package/dist/commands/setup-ai.js +2 -121
  86. package/dist/commands/setup.js +2 -305
  87. package/dist/commands/share.js +1 -214
  88. package/dist/commands/sms.js +32 -319
  89. package/dist/commands/stacks.js +1 -72
  90. package/dist/commands/telemetry.js +1 -74
  91. package/dist/commands/test.js +1 -130
  92. package/dist/commands/tinker.js +1 -37
  93. package/dist/commands/types.js +1 -18
  94. package/dist/commands/upgrade.js +1 -98
  95. package/dist/commands/user.js +3 -84
  96. package/dist/commands/version.js +1 -14
  97. package/dist/config.js +1 -231
  98. package/dist/custom-cli.js +1 -23
  99. package/dist/database-preflight.js +1 -88
  100. package/dist/global-options.js +1 -4
  101. package/dist/index.js +1 -1
  102. package/dist/lazy-commands.js +1 -241
  103. package/dist/migrators/index.js +2 -62
  104. package/dist/migrators/laravel/index.js +1 -148
  105. package/dist/migrators/laravel/migrations.js +4 -231
  106. package/dist/migrators/laravel/models.js +8 -111
  107. package/dist/migrators/rails/index.js +1 -11
  108. package/dist/production-server.js +1 -257
  109. package/dist/project-setup.js +1 -36
  110. package/dist/serve-entry.js +1 -5
  111. package/dist/version-info.js +1 -5
  112. package/package.json +53 -46
@@ -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")})}