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