A CLI for publishing standard.site documents to ATProto sequoia.pub
standard site lexicon cli publishing

Refactor to Node Build #2

merged opened by stevedylan.dev targeting main from chore/refactor-build-target

Originally the CLI was using Bun for a lot of the file reading and writing, and building the CLI was using the --compile flag to create a single binary. While it looked like this was working initially, the problem was the architecture was being defaulted to my personal machine rather than being cross platform. Bun has the ability to create cross platform binaries, but then the problem becomes distributing those binaries through install scripts and a whole bunch of other fun stuff. Since we originally launched through npm we will stick with a Node build for now, but will highly consider going back to bun and doing binaries in the future.

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:ia2zdnhjaokf5lazhxrmj6eu/sh.tangled.repo.pull/3mdpr3jsxvd22
+154 -101
Diff #0
+38 -10
bun.lock
··· 24 }, 25 "packages/cli": { 26 "name": "sequoia-cli", 27 - "version": "0.0.6", 28 "bin": { 29 - "sequoia": "dist/sequoia", 30 }, 31 "dependencies": { 32 "@atproto/api": "^0.18.17", 33 "@clack/prompts": "^1.0.0", 34 "cmd-ts": "^0.14.3", 35 }, 36 "devDependencies": { 37 - "@types/bun": "latest", 38 }, 39 "peerDependencies": { 40 "typescript": "^5", ··· 188 189 "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], 190 191 "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 192 193 "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], ··· 524 525 "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], 526 527 "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 528 529 - "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], 530 531 "@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="], 532 ··· 834 835 "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], 836 837 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 838 839 "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], ··· 1094 1095 "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], 1096 1097 - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 1098 1099 "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], 1100 1101 "mini-svg-data-uri": ["mini-svg-data-uri@1.4.4", "", { "bin": { "mini-svg-data-uri": "cli.js" } }, "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="], 1102 1103 "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], 1104 1105 "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], ··· 1150 1151 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 1152 1153 "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 1154 1155 "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], ··· 1336 1337 "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 1338 1339 - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 1340 1341 "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], 1342 ··· 1442 1443 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1444 1445 "chevrotain/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], 1446 1447 "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1448 1449 "create-vocs/@clack/prompts": ["@clack/prompts@0.7.0", "", { "dependencies": { "@clack/core": "^0.3.3", "is-unicode-supported": "*", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA=="], ··· 1456 1457 "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], 1458 1459 "hast-util-from-dom/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], 1460 1461 "hast-util-from-parse5/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], ··· 1474 1475 "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], 1476 1477 "radix-ui/@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], 1478 1479 "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1480 1481 "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1482 1483 - "sequoia-cli/@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], 1484 - 1485 "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1486 1487 "@radix-ui/react-label/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], 1488 1489 "@shikijs/twoslash/twoslash/twoslash-protocol": ["twoslash-protocol@0.2.12", "", {}, "sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg=="], 1490 1491 "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1492 1493 "create-vocs/@clack/prompts/@clack/core": ["@clack/core@0.3.5", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ=="], ··· 1498 1499 "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], 1500 1501 "hast-util-from-dom/hastscript/property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], 1502 1503 "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 1504 1505 "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1506 - 1507 - "sequoia-cli/@types/bun/bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], 1508 } 1509 }
··· 24 }, 25 "packages/cli": { 26 "name": "sequoia-cli", 27 + "version": "0.1.0", 28 "bin": { 29 + "sequoia": "dist/index.js", 30 }, 31 "dependencies": { 32 "@atproto/api": "^0.18.17", 33 "@clack/prompts": "^1.0.0", 34 "cmd-ts": "^0.14.3", 35 + "glob": "^13.0.0", 36 + "mime-types": "^2.1.35", 37 + "minimatch": "^10.1.1", 38 }, 39 "devDependencies": { 40 + "@types/mime-types": "^3.0.1", 41 + "@types/node": "^20", 42 }, 43 "peerDependencies": { 44 "typescript": "^5", ··· 192 193 "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], 194 195 + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], 196 + 197 + "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], 198 + 199 "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 200 201 "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], ··· 532 533 "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], 534 535 + "@types/mime-types": ["@types/mime-types@3.0.1", "", {}, "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ=="], 536 + 537 "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 538 539 + "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], 540 541 "@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="], 542 ··· 844 845 "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], 846 847 + "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], 848 + 849 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 850 851 "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], ··· 1106 1107 "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], 1108 1109 + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], 1110 + 1111 + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], 1112 1113 "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], 1114 1115 "mini-svg-data-uri": ["mini-svg-data-uri@1.4.4", "", { "bin": { "mini-svg-data-uri": "cli.js" } }, "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="], 1116 1117 + "minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], 1118 + 1119 + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 1120 + 1121 "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], 1122 1123 "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], ··· 1168 1169 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 1170 1171 + "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], 1172 + 1173 "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 1174 1175 "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], ··· 1356 1357 "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 1358 1359 + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 1360 1361 "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], 1362 ··· 1462 1463 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1464 1465 + "bun-types/@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], 1466 + 1467 "chevrotain/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], 1468 1469 + "compressible/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 1470 + 1471 "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1472 1473 "create-vocs/@clack/prompts": ["@clack/prompts@0.7.0", "", { "dependencies": { "@clack/core": "^0.3.3", "is-unicode-supported": "*", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA=="], ··· 1480 1481 "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], 1482 1483 + "eval/@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], 1484 + 1485 "hast-util-from-dom/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], 1486 1487 "hast-util-from-parse5/hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], ··· 1500 1501 "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], 1502 1503 + "path-scurry/lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], 1504 + 1505 "radix-ui/@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], 1506 1507 "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1508 1509 "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 1510 1511 "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1512 1513 "@radix-ui/react-label/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], 1514 1515 "@shikijs/twoslash/twoslash/twoslash-protocol": ["twoslash-protocol@0.2.12", "", {}, "sha512-5qZLXVYfZ9ABdjqbvPc4RWMr7PrpPaaDSeaYY55vl/w1j6H6kzsWK/urAEIXlzYlyrFmyz1UbwIt+AA0ck+wbg=="], 1516 1517 + "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 1518 + 1519 "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1520 1521 "create-vocs/@clack/prompts/@clack/core": ["@clack/core@0.3.5", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ=="], ··· 1526 1527 "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], 1528 1529 + "eval/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 1530 + 1531 "hast-util-from-dom/hastscript/property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], 1532 1533 "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 1534 1535 "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 1536 } 1537 }
+10 -7
packages/cli/package.json
··· 1 { 2 "name": "sequoia-cli", 3 "version": "0.1.0", 4 - "module": "dist/index.js", 5 "type": "module", 6 "bin": { 7 - "sequoia": "dist/sequoia" 8 }, 9 "files": [ 10 "dist", 11 "README.md" 12 ], 13 - "main": "./dist/sequoia", 14 "exports": { 15 - ".": "./dist/sequoia" 16 }, 17 "scripts": { 18 - "build": "bun build src/index.ts --compile --outfile dist/sequoia", 19 "dev": "bun run build && bun link", 20 "deploy": "bun run build && bun publish" 21 }, 22 "devDependencies": { 23 - "@types/bun": "latest" 24 }, 25 "peerDependencies": { 26 "typescript": "^5" 27 }, 28 "dependencies": { 29 "@atproto/api": "^0.18.17", 30 "cmd-ts": "^0.14.3", 31 - "@clack/prompts": "^1.0.0" 32 } 33 }
··· 1 { 2 "name": "sequoia-cli", 3 "version": "0.1.0", 4 "type": "module", 5 "bin": { 6 + "sequoia": "dist/index.js" 7 }, 8 "files": [ 9 "dist", 10 "README.md" 11 ], 12 + "main": "./dist/index.js", 13 "exports": { 14 + ".": "./dist/index.js" 15 }, 16 "scripts": { 17 + "build": "bun build src/index.ts --target node --outdir dist", 18 "dev": "bun run build && bun link", 19 "deploy": "bun run build && bun publish" 20 }, 21 "devDependencies": { 22 + "@types/mime-types": "^3.0.1", 23 + "@types/node": "^20" 24 }, 25 "peerDependencies": { 26 "typescript": "^5" 27 }, 28 "dependencies": { 29 "@atproto/api": "^0.18.17", 30 + "@clack/prompts": "^1.0.0", 31 "cmd-ts": "^0.14.3", 32 + "glob": "^13.0.0", 33 + "mime-types": "^2.1.35", 34 + "minimatch": "^10.1.1" 35 } 36 }
+18 -8
packages/cli/src/commands/init.ts
··· 1 import { command } from "cmd-ts"; 2 import { 3 intro, ··· 16 import { createAgent, createPublication } from "../lib/atproto"; 17 import type { FrontmatterMapping } from "../lib/types"; 18 19 const onCancel = () => { 20 outro("Setup cancelled"); 21 process.exit(0); ··· 270 }); 271 272 const configPath = path.join(process.cwd(), "sequoia.json"); 273 - await Bun.write(configPath, configContent); 274 275 log.success(`Configuration saved to ${configPath}`); 276 ··· 283 const wellKnownPath = path.join(wellKnownDir, "site.standard.publication"); 284 285 // Ensure .well-known directory exists 286 - await Bun.write(path.join(wellKnownDir, ".gitkeep"), ""); 287 - await Bun.write(wellKnownPath, publicationUri); 288 289 log.success(`Created ${wellKnownPath}`); 290 291 // Update .gitignore 292 const gitignorePath = path.join(process.cwd(), ".gitignore"); 293 - const gitignoreFile = Bun.file(gitignorePath); 294 const stateFilename = ".sequoia-state.json"; 295 296 - if (await gitignoreFile.exists()) { 297 - const gitignoreContent = await gitignoreFile.text(); 298 if (!gitignoreContent.includes(stateFilename)) { 299 - await Bun.write( 300 gitignorePath, 301 gitignoreContent + `\n${stateFilename}\n`, 302 ); 303 log.info(`Added ${stateFilename} to .gitignore`); 304 } 305 } else { 306 - await Bun.write(gitignorePath, `${stateFilename}\n`); 307 log.info(`Created .gitignore with ${stateFilename}`); 308 } 309
··· 1 + import * as fs from "fs/promises"; 2 import { command } from "cmd-ts"; 3 import { 4 intro, ··· 17 import { createAgent, createPublication } from "../lib/atproto"; 18 import type { FrontmatterMapping } from "../lib/types"; 19 20 + async function fileExists(filePath: string): Promise<boolean> { 21 + try { 22 + await fs.access(filePath); 23 + return true; 24 + } catch { 25 + return false; 26 + } 27 + } 28 + 29 const onCancel = () => { 30 outro("Setup cancelled"); 31 process.exit(0); ··· 280 }); 281 282 const configPath = path.join(process.cwd(), "sequoia.json"); 283 + await fs.writeFile(configPath, configContent); 284 285 log.success(`Configuration saved to ${configPath}`); 286 ··· 293 const wellKnownPath = path.join(wellKnownDir, "site.standard.publication"); 294 295 // Ensure .well-known directory exists 296 + await fs.mkdir(wellKnownDir, { recursive: true }); 297 + await fs.writeFile(path.join(wellKnownDir, ".gitkeep"), ""); 298 + await fs.writeFile(wellKnownPath, publicationUri); 299 300 log.success(`Created ${wellKnownPath}`); 301 302 // Update .gitignore 303 const gitignorePath = path.join(process.cwd(), ".gitignore"); 304 const stateFilename = ".sequoia-state.json"; 305 306 + if (await fileExists(gitignorePath)) { 307 + const gitignoreContent = await fs.readFile(gitignorePath, "utf-8"); 308 if (!gitignoreContent.includes(stateFilename)) { 309 + await fs.writeFile( 310 gitignorePath, 311 gitignoreContent + `\n${stateFilename}\n`, 312 ); 313 log.info(`Added ${stateFilename} to .gitignore`); 314 } 315 } else { 316 + await fs.writeFile(gitignorePath, `${stateFilename}\n`); 317 log.info(`Created .gitignore with ${stateFilename}`); 318 } 319
+11 -12
packages/cli/src/commands/inject.ts
··· 1 import { command, flag, option, optional, string } from "cmd-ts"; 2 import { log } from "@clack/prompts"; 3 import * as path from "path"; 4 - import { Glob } from "bun"; 5 import { loadConfig, loadState, findConfig } from "../lib/config"; 6 7 export const injectCommand = command({ ··· 97 log.info(`Found ${pathToAtUri.size} published posts in state`); 98 99 // Scan for HTML files 100 - const glob = new Glob("**/*.html"); 101 - const htmlFiles: string[] = []; 102 - 103 - for await (const file of glob.scan(resolvedOutputDir)) { 104 - htmlFiles.push(path.join(resolvedOutputDir, file)); 105 - } 106 107 if (htmlFiles.length === 0) { 108 log.warn(`No HTML files found in ${resolvedOutputDir}`); ··· 115 let skippedCount = 0; 116 let alreadyHasCount = 0; 117 118 - for (const htmlPath of htmlFiles) { 119 // Try to match this HTML file to a published post 120 - const relativePath = path.relative(resolvedOutputDir, htmlPath); 121 const htmlDir = path.dirname(relativePath); 122 const htmlBasename = path.basename(relativePath, ".html"); 123 ··· 152 } 153 154 // Read the HTML file 155 - const file = Bun.file(htmlPath); 156 - let content = await file.text(); 157 158 // Check if link tag already exists 159 const linkTag = `<link rel="site.standard.document" href="${atUri}">`; ··· 184 `${indent}${linkTag}\n${indent}` + 185 content.slice(headCloseIndex); 186 187 - await Bun.write(htmlPath, content); 188 log.success(` Injected into: ${relativePath}`); 189 injectedCount++; 190 }
··· 1 + import * as fs from "fs/promises"; 2 import { command, flag, option, optional, string } from "cmd-ts"; 3 import { log } from "@clack/prompts"; 4 import * as path from "path"; 5 + import { glob } from "glob"; 6 import { loadConfig, loadState, findConfig } from "../lib/config"; 7 8 export const injectCommand = command({ ··· 98 log.info(`Found ${pathToAtUri.size} published posts in state`); 99 100 // Scan for HTML files 101 + const htmlFiles = await glob("**/*.html", { 102 + cwd: resolvedOutputDir, 103 + absolute: false, 104 + }); 105 106 if (htmlFiles.length === 0) { 107 log.warn(`No HTML files found in ${resolvedOutputDir}`); ··· 114 let skippedCount = 0; 115 let alreadyHasCount = 0; 116 117 + for (const file of htmlFiles) { 118 + const htmlPath = path.join(resolvedOutputDir, file); 119 // Try to match this HTML file to a published post 120 + const relativePath = file; 121 const htmlDir = path.dirname(relativePath); 122 const htmlBasename = path.basename(relativePath, ".html"); 123 ··· 152 } 153 154 // Read the HTML file 155 + let content = await fs.readFile(htmlPath, "utf-8"); 156 157 // Check if link tag already exists 158 const linkTag = `<link rel="site.standard.document" href="${atUri}">`; ··· 183 `${indent}${linkTag}\n${indent}` + 184 content.slice(headCloseIndex); 185 186 + await fs.writeFile(htmlPath, content); 187 log.success(` Injected into: ${relativePath}`); 188 injectedCount++; 189 }
+3 -2
packages/cli/src/commands/publish.ts
··· 1 import { command, flag } from "cmd-ts"; 2 import { select, spinner, log } from "@clack/prompts"; 3 import * as path from "path"; ··· 164 // Handle cover image upload 165 let coverImage: BlobObject | undefined; 166 if (post.frontmatter.ogImage) { 167 - const imagePath = resolveImagePath( 168 post.frontmatter.ogImage, 169 imagesDir, 170 contentDir ··· 191 192 // Update frontmatter with atUri 193 const updatedContent = updateFrontmatterWithAtUri(post.rawContent, atUri); 194 - await Bun.write(post.filePath, updatedContent); 195 log.info(` Updated frontmatter in ${path.basename(post.filePath)}`); 196 197 // Use updated content (with atUri) for hash so next run sees matching hash
··· 1 + import * as fs from "fs/promises"; 2 import { command, flag } from "cmd-ts"; 3 import { select, spinner, log } from "@clack/prompts"; 4 import * as path from "path"; ··· 165 // Handle cover image upload 166 let coverImage: BlobObject | undefined; 167 if (post.frontmatter.ogImage) { 168 + const imagePath = await resolveImagePath( 169 post.frontmatter.ogImage, 170 imagesDir, 171 contentDir ··· 192 193 // Update frontmatter with atUri 194 const updatedContent = updateFrontmatterWithAtUri(post.rawContent, atUri); 195 + await fs.writeFile(post.filePath, updatedContent); 196 log.info(` Updated frontmatter in ${path.basename(post.filePath)}`); 197 198 // Use updated content (with atUri) for hash so next run sees matching hash
+3 -3
packages/cli/src/commands/sync.ts
··· 1 import { command, flag } from "cmd-ts"; 2 import { select, spinner, log } from "@clack/prompts"; 3 import * as path from "path"; ··· 169 if (frontmatterUpdates.length > 0) { 170 s.start(`Updating frontmatter in ${frontmatterUpdates.length} files...`); 171 for (const { filePath, atUri } of frontmatterUpdates) { 172 - const file = Bun.file(filePath); 173 - const content = await file.text(); 174 const updated = updateFrontmatterWithAtUri(content, atUri); 175 - await Bun.write(filePath, updated); 176 log.message(` Updated: ${path.basename(filePath)}`); 177 } 178 s.stop("Frontmatter updated");
··· 1 + import * as fs from "fs/promises"; 2 import { command, flag } from "cmd-ts"; 3 import { select, spinner, log } from "@clack/prompts"; 4 import * as path from "path"; ··· 170 if (frontmatterUpdates.length > 0) { 171 s.start(`Updating frontmatter in ${frontmatterUpdates.length} files...`); 172 for (const { filePath, atUri } of frontmatterUpdates) { 173 + const content = await fs.readFile(filePath, "utf-8"); 174 const updated = updateFrontmatterWithAtUri(content, atUri); 175 + await fs.writeFile(filePath, updated); 176 log.message(` Updated: ${path.basename(filePath)}`); 177 } 178 s.stop("Frontmatter updated");
+1 -1
packages/cli/src/index.ts
··· 1 - #!/usr/bin/env bun 2 3 import { run, subcommands } from "cmd-ts"; 4 import { authCommand } from "./commands/auth";
··· 1 + #!/usr/bin/env node 2 3 import { run, subcommands } from "cmd-ts"; 4 import { authCommand } from "./commands/auth";
+20 -15
packages/cli/src/lib/atproto.ts
··· 1 import { AtpAgent } from "@atproto/api"; 2 import * as path from "path"; 3 import type { Credentials, BlogPost, BlobObject, PublisherConfig } from "./types"; 4 import { stripMarkdownForText } from "./markdown"; 5 6 export async function resolveHandleToPDS(handle: string): Promise<string> { 7 // First, resolve the handle to a DID 8 let did: string; ··· 87 agent: AtpAgent, 88 imagePath: string 89 ): Promise<BlobObject | undefined> { 90 - const file = Bun.file(imagePath); 91 - 92 - if (!(await file.exists())) { 93 return undefined; 94 } 95 96 try { 97 - const imageBuffer = await file.arrayBuffer(); 98 - const mimeType = file.type || "application/octet-stream"; 99 100 const response = await agent.com.atproto.repo.uploadBlob( 101 new Uint8Array(imageBuffer), ··· 118 } 119 } 120 121 - export function resolveImagePath( 122 ogImage: string, 123 imagesDir: string | undefined, 124 contentDir: string 125 - ): string | null { 126 // Try multiple resolution strategies 127 const filename = path.basename(ogImage); 128 129 // 1. If imagesDir is specified, look there 130 if (imagesDir) { 131 const imagePath = path.join(imagesDir, filename); 132 - try { 133 - const stat = Bun.file(imagePath); 134 if (stat.size > 0) { 135 return imagePath; 136 } 137 - } catch { 138 - // File doesn't exist, continue 139 } 140 } 141 ··· 146 147 // 3. Try relative to content directory 148 const contentRelative = path.join(contentDir, ogImage); 149 - try { 150 - const stat = Bun.file(contentRelative); 151 if (stat.size > 0) { 152 return contentRelative; 153 } 154 - } catch { 155 - // File doesn't exist 156 } 157 158 return null;
··· 1 import { AtpAgent } from "@atproto/api"; 2 + import * as fs from "fs/promises"; 3 import * as path from "path"; 4 + import * as mimeTypes from "mime-types"; 5 import type { Credentials, BlogPost, BlobObject, PublisherConfig } from "./types"; 6 import { stripMarkdownForText } from "./markdown"; 7 8 + async function fileExists(filePath: string): Promise<boolean> { 9 + try { 10 + await fs.access(filePath); 11 + return true; 12 + } catch { 13 + return false; 14 + } 15 + } 16 + 17 export async function resolveHandleToPDS(handle: string): Promise<string> { 18 // First, resolve the handle to a DID 19 let did: string; ··· 98 agent: AtpAgent, 99 imagePath: string 100 ): Promise<BlobObject | undefined> { 101 + if (!(await fileExists(imagePath))) { 102 return undefined; 103 } 104 105 try { 106 + const imageBuffer = await fs.readFile(imagePath); 107 + const mimeType = mimeTypes.lookup(imagePath) || "application/octet-stream"; 108 109 const response = await agent.com.atproto.repo.uploadBlob( 110 new Uint8Array(imageBuffer), ··· 127 } 128 } 129 130 + export async function resolveImagePath( 131 ogImage: string, 132 imagesDir: string | undefined, 133 contentDir: string 134 + ): Promise<string | null> { 135 // Try multiple resolution strategies 136 const filename = path.basename(ogImage); 137 138 // 1. If imagesDir is specified, look there 139 if (imagesDir) { 140 const imagePath = path.join(imagesDir, filename); 141 + if (await fileExists(imagePath)) { 142 + const stat = await fs.stat(imagePath); 143 if (stat.size > 0) { 144 return imagePath; 145 } 146 } 147 } 148 ··· 153 154 // 3. Try relative to content directory 155 const contentRelative = path.join(contentDir, ogImage); 156 + if (await fileExists(contentRelative)) { 157 + const stat = await fs.stat(contentRelative); 158 if (stat.size > 0) { 159 return contentRelative; 160 } 161 } 162 163 return null;
+15 -8
packages/cli/src/lib/config.ts
··· 1 import * as path from "path"; 2 import type { PublisherConfig, PublisherState, FrontmatterMapping } from "./types"; 3 4 const CONFIG_FILENAME = "sequoia.json"; 5 const STATE_FILENAME = ".sequoia-state.json"; 6 7 export async function findConfig( 8 startDir: string = process.cwd(), 9 ): Promise<string | null> { ··· 11 12 while (true) { 13 const configPath = path.join(currentDir, CONFIG_FILENAME); 14 - const file = Bun.file(configPath); 15 16 - if (await file.exists()) { 17 return configPath; 18 } 19 ··· 38 } 39 40 try { 41 - const file = Bun.file(resolvedPath); 42 - const content = await file.text(); 43 const config = JSON.parse(content) as PublisherConfig; 44 45 // Validate required fields ··· 109 110 export async function loadState(configDir: string): Promise<PublisherState> { 111 const statePath = path.join(configDir, STATE_FILENAME); 112 - const file = Bun.file(statePath); 113 114 - if (!(await file.exists())) { 115 return { posts: {} }; 116 } 117 118 try { 119 - const content = await file.text(); 120 return JSON.parse(content) as PublisherState; 121 } catch { 122 return { posts: {} }; ··· 128 state: PublisherState, 129 ): Promise<void> { 130 const statePath = path.join(configDir, STATE_FILENAME); 131 - await Bun.write(statePath, JSON.stringify(state, null, 2)); 132 } 133 134 export function getStatePath(configDir: string): string {
··· 1 + import * as fs from "fs/promises"; 2 import * as path from "path"; 3 import type { PublisherConfig, PublisherState, FrontmatterMapping } from "./types"; 4 5 const CONFIG_FILENAME = "sequoia.json"; 6 const STATE_FILENAME = ".sequoia-state.json"; 7 8 + async function fileExists(filePath: string): Promise<boolean> { 9 + try { 10 + await fs.access(filePath); 11 + return true; 12 + } catch { 13 + return false; 14 + } 15 + } 16 + 17 export async function findConfig( 18 startDir: string = process.cwd(), 19 ): Promise<string | null> { ··· 21 22 while (true) { 23 const configPath = path.join(currentDir, CONFIG_FILENAME); 24 25 + if (await fileExists(configPath)) { 26 return configPath; 27 } 28 ··· 47 } 48 49 try { 50 + const content = await fs.readFile(resolvedPath, "utf-8"); 51 const config = JSON.parse(content) as PublisherConfig; 52 53 // Validate required fields ··· 117 118 export async function loadState(configDir: string): Promise<PublisherState> { 119 const statePath = path.join(configDir, STATE_FILENAME); 120 121 + if (!(await fileExists(statePath))) { 122 return { posts: {} }; 123 } 124 125 try { 126 + const content = await fs.readFile(statePath, "utf-8"); 127 return JSON.parse(content) as PublisherState; 128 } catch { 129 return { posts: {} }; ··· 135 state: PublisherState, 136 ): Promise<void> { 137 const statePath = path.join(configDir, STATE_FILENAME); 138 + await fs.writeFile(statePath, JSON.stringify(state, null, 2)); 139 } 140 141 export function getStatePath(configDir: string): string {
+15 -6
packages/cli/src/lib/credentials.ts
··· 1 import * as path from "path"; 2 import * as os from "os"; 3 import type { Credentials } from "./types"; ··· 8 // Stored credentials keyed by identifier 9 type CredentialsStore = Record<string, Credentials>; 10 11 /** 12 * Load all stored credentials 13 */ 14 async function loadCredentialsStore(): Promise<CredentialsStore> { 15 - const file = Bun.file(CREDENTIALS_FILE); 16 - if (!(await file.exists())) { 17 return {}; 18 } 19 20 try { 21 - const content = await file.text(); 22 const parsed = JSON.parse(content); 23 24 // Handle legacy single-credential format (migrate on read) ··· 37 * Save the entire credentials store 38 */ 39 async function saveCredentialsStore(store: CredentialsStore): Promise<void> { 40 - await Bun.$`mkdir -p ${CONFIG_DIR}`; 41 - await Bun.write(CREDENTIALS_FILE, JSON.stringify(store, null, 2)); 42 - await Bun.$`chmod 600 ${CREDENTIALS_FILE}`; 43 } 44 45 /**
··· 1 + import * as fs from "fs/promises"; 2 import * as path from "path"; 3 import * as os from "os"; 4 import type { Credentials } from "./types"; ··· 9 // Stored credentials keyed by identifier 10 type CredentialsStore = Record<string, Credentials>; 11 12 + async function fileExists(filePath: string): Promise<boolean> { 13 + try { 14 + await fs.access(filePath); 15 + return true; 16 + } catch { 17 + return false; 18 + } 19 + } 20 + 21 /** 22 * Load all stored credentials 23 */ 24 async function loadCredentialsStore(): Promise<CredentialsStore> { 25 + if (!(await fileExists(CREDENTIALS_FILE))) { 26 return {}; 27 } 28 29 try { 30 + const content = await fs.readFile(CREDENTIALS_FILE, "utf-8"); 31 const parsed = JSON.parse(content); 32 33 // Handle legacy single-credential format (migrate on read) ··· 46 * Save the entire credentials store 47 */ 48 async function saveCredentialsStore(store: CredentialsStore): Promise<void> { 49 + await fs.mkdir(CONFIG_DIR, { recursive: true }); 50 + await fs.writeFile(CREDENTIALS_FILE, JSON.stringify(store, null, 2)); 51 + await fs.chmod(CREDENTIALS_FILE, 0o600); 52 } 53 54 /**
+9 -9
packages/cli/src/lib/markdown.ts
··· 1 import * as path from "path"; 2 - import { Glob } from "bun"; 3 import type { PostFrontmatter, BlogPost, FrontmatterMapping } from "./types"; 4 5 export function parseFrontmatter(content: string, mapping?: FrontmatterMapping): { ··· 120 121 function shouldIgnore(relativePath: string, ignorePatterns: string[]): boolean { 122 for (const pattern of ignorePatterns) { 123 - const glob = new Glob(pattern); 124 - if (glob.match(relativePath)) { 125 return true; 126 } 127 } ··· 137 const posts: BlogPost[] = []; 138 139 for (const pattern of patterns) { 140 - const glob = new Glob(pattern); 141 - 142 - for await (const relativePath of glob.scan({ 143 cwd: contentDir, 144 absolute: false, 145 - })) { 146 // Skip files matching ignore patterns 147 if (shouldIgnore(relativePath, ignorePatterns)) { 148 continue; 149 } 150 151 const filePath = path.join(contentDir, relativePath); 152 - const file = Bun.file(filePath); 153 - const rawContent = await file.text(); 154 155 try { 156 const { frontmatter, body } = parseFrontmatter(rawContent, frontmatterMapping);
··· 1 + import * as fs from "fs/promises"; 2 import * as path from "path"; 3 + import { glob } from "glob"; 4 + import { minimatch } from "minimatch"; 5 import type { PostFrontmatter, BlogPost, FrontmatterMapping } from "./types"; 6 7 export function parseFrontmatter(content: string, mapping?: FrontmatterMapping): { ··· 122 123 function shouldIgnore(relativePath: string, ignorePatterns: string[]): boolean { 124 for (const pattern of ignorePatterns) { 125 + if (minimatch(relativePath, pattern)) { 126 return true; 127 } 128 } ··· 138 const posts: BlogPost[] = []; 139 140 for (const pattern of patterns) { 141 + const files = await glob(pattern, { 142 cwd: contentDir, 143 absolute: false, 144 + }); 145 + 146 + for (const relativePath of files) { 147 // Skip files matching ignore patterns 148 if (shouldIgnore(relativePath, ignorePatterns)) { 149 continue; 150 } 151 152 const filePath = path.join(contentDir, relativePath); 153 + const rawContent = await fs.readFile(filePath, "utf-8"); 154 155 try { 156 const { frontmatter, body } = parseFrontmatter(rawContent, frontmatterMapping);
+11 -20
packages/cli/tsconfig.json
··· 1 { 2 "compilerOptions": { 3 - // Environment setup & latest features 4 - "lib": ["ESNext"], 5 - "target": "ESNext", 6 - "module": "Preserve", 7 - "moduleDetection": "force", 8 - "jsx": "react-jsx", 9 - "allowJs": true, 10 - 11 - // Bundler mode 12 "moduleResolution": "bundler", 13 - "allowImportingTsExtensions": true, 14 - "verbatimModuleSyntax": true, 15 - "noEmit": true, 16 - 17 - // Best practices 18 "strict": true, 19 "skipLibCheck": true, 20 "noFallthroughCasesInSwitch": true, 21 "noUncheckedIndexedAccess": true, 22 - "noImplicitOverride": true, 23 - 24 - // Some stricter flags (disabled by default) 25 "noUnusedLocals": false, 26 - "noUnusedParameters": false, 27 - "noPropertyAccessFromIndexSignature": false, 28 - "composite": true 29 }, 30 "include": ["src"] 31 }
··· 1 { 2 "compilerOptions": { 3 + "lib": ["ES2022"], 4 + "target": "ES2022", 5 + "module": "ESNext", 6 "moduleResolution": "bundler", 7 + "outDir": "./dist", 8 + "rootDir": "./src", 9 + "declaration": true, 10 + "sourceMap": true, 11 "strict": true, 12 "skipLibCheck": true, 13 + "esModuleInterop": true, 14 + "resolveJsonModule": true, 15 + "forceConsistentCasingInFileNames": true, 16 "noFallthroughCasesInSwitch": true, 17 "noUncheckedIndexedAccess": true, 18 "noUnusedLocals": false, 19 + "noUnusedParameters": false 20 }, 21 "include": ["src"] 22 }

History

1 round 0 comments
sign up or login to add to the discussion
stevedylan.dev submitted #0
1 commit
expand
chore: refactored codebase to use node and fs instead of bun
expand 0 comments
pull request successfully merged