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

Compare changes

Choose any two refs to compare.

+154 -101
+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 }