{"_id":"@allodium/storage","_rev":"3-a5b3f548270191935d13f34d1786b806","name":"@allodium/storage","dist-tags":{"latest":"0.2.0"},"versions":{"0.0.1":{"name":"@allodium/storage","version":"0.0.1","license":"MIT","_id":"@allodium/storage@0.0.1","maintainers":[{"name":"glossydev","email":"adam@glossydev.com"}],"dist":{"shasum":"5d8d97c298a35034ef25dbcde1f16e22372c95d6","tarball":"https://registry.npmjs.org/@allodium/storage/-/storage-0.0.1.tgz","fileCount":5,"integrity":"sha512-TG8BZj4usEW3aSnBER8FkBbGFlOmpctt3u18KIRvnHw59vrwNFLxFJIqou4IC7SV7kpnoZdqhcfv97Qe5T2EMw==","signatures":[{"sig":"MEUCIHRssmeLyO881vAcicCXCgKZxxy6fI0b0FYBfFCaZjSxAiEA+CH5I7CxkqYrBAgaOuW5UOs1sLtztfUmQAhql0Ot0Oo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":12774},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"dcb271a2b5c97f5e238f9debb419b91837bd8763","scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"glossydev","email":"adam@glossydev.com"},"_npmVersion":"11.5.2","description":"Storage driver interface + local-disk driver + auth-aware asset serving for Allodium.","directories":{},"_nodeVersion":"22.18.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/storage_0.0.1_1785044983949_0.856731829607887","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@allodium/storage","version":"0.1.0","license":"MIT","_id":"@allodium/storage@0.1.0","maintainers":[{"name":"glossydev","email":"adam@glossydev.com"}],"dist":{"shasum":"ca370050fbc46cad054bb543540bc7e182e55b3e","tarball":"https://registry.npmjs.org/@allodium/storage/-/storage-0.1.0.tgz","fileCount":13,"integrity":"sha512-Fq9UbMRHO9n8X+Qkuy3CAYduaBQp7T1a/DzcdBxhuq64ax8RFdOetec5/XNN12F6HsB2UR0UklmrGXyuJ8ri7w==","signatures":[{"sig":"MEUCIAtDWQ5em0NxSreMgKg5n+jOVGRvjujlcm3HQ4w54rZGAiEAixWpsqg8J/TR2n/G9sSbq4lMhyon+jd6VT3QEL0284A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15231},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4c83d21ae5a1f00b641fb04562b8372d575c3ea1","scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"glossydev","email":"adam@glossydev.com"},"_npmVersion":"11.5.2","description":"Storage driver interface + local-disk driver + auth-aware asset serving for Allodium.","directories":{},"_nodeVersion":"22.18.0","_hasShrinkwrap":false,"devDependencies":{"@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/storage_0.1.0_1785210143153_0.5714726879859182","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@allodium/storage","version":"0.2.0","description":"Storage driver interface + local-disk driver + auth-aware asset serving for Allodium.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/glossydev/allodium.git","directory":"packages/storage"},"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit"},"engines":{"node":">=20"},"devDependencies":{"@types/node":"^22.0.0"},"gitHead":"8374294b8cb9f031760ef63f6a0fb82123c0ee1f","_id":"@allodium/storage@0.2.0","bugs":{"url":"https://github.com/glossydev/allodium/issues"},"homepage":"https://github.com/glossydev/allodium#readme","_nodeVersion":"22.23.2","_npmVersion":"12.0.2","dist":{"integrity":"sha512-WKbrBBkpl6179KirRgCE/VWKlfQ8jspDuTTrQxjM8kxhWad8QZhvQ9grTD1x+0Du2ho1Z2WKgFqsXsZXB6WRrA==","shasum":"00619999f063cc27d23391fccdd0d8b5d4ead81e","tarball":"https://registry.npmjs.org/@allodium/storage/-/storage-0.2.0.tgz","fileCount":15,"unpackedSize":19967,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allodium%2fstorage@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDcka9FLNBqGqVBXLLeiLTbDuWYQ0LC323cVR6TFk4QQgIgNktBiNY/zjWQMWXFieqap+lVjA/DDXnx260mQCWPjjw="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c246124f-ed87-4d15-9069-c3d701a0a300"}},"directories":{},"maintainers":[{"name":"glossydev","email":"adam@glossydev.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/storage_0.2.0_1789062472995_0.005101021533065575"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-26T05:49:43.775Z","modified":"2026-09-10T17:47:53.547Z","0.0.1":"2026-07-26T05:49:44.097Z","0.1.0":"2026-07-28T03:42:23.281Z","0.2.0":"2026-09-10T17:47:53.138Z"},"license":"MIT","description":"Storage driver interface + local-disk driver + auth-aware asset serving for Allodium.","maintainers":[{"name":"glossydev","email":"adam@glossydev.com"}],"readme":"# @allodium/storage\n\nA three-method byte store, and the headers that stop an upload from becoming an XSS.\n\n```bash\nnpm install @allodium/storage\n```\n\nRequires Node 20+. No peer dependencies — this one is self-contained.\n\n## The driver\n\n```ts\nimport { createLocalDiskDriver } from '@allodium/storage';\n\nexport const storage = createLocalDiskDriver({\n  root: () => process.env.UPLOADS_DIR!,   // thunk: read at call time\n});\n\nawait storage.put('a1b2c3.png', buffer);\nconst file = await storage.stream('a1b2c3.png');   // { stream, size } | null\nawait storage.delete('a1b2c3.png');                // idempotent\n```\n\nThree methods. An S3-compatible driver slots into the same interface later without\ntouching your application code.\n\n## Bytes only — metadata belongs in your database\n\nThe driver stores **bytes keyed by a disk name you generate**. It does not store the\noriginal filename, the MIME type, the uploader, or timestamps.\n\nThat is deliberate and it is the most important decision in the package. Metadata in\nsidecar files means:\n\n- no foreign keys — nothing can reference a file, so nothing can prevent deleting one\n  that is still in use\n- no queries — \"every file over 10MB uploaded last month\" needs a directory walk\n- two sources of truth that drift\n\nPut a `files` table in your database, and the disk name in a column. Then a file is just\na row, and every tool you already have works on it. An early version of this package did\nsidecar JSON; it was removed, and reintroducing it would be a regression.\n\n**Local disk on one box is a first-class production choice**, not a dev-mode fallback.\nThe layout is flat `{uuid}.{ext}`, which is what Directus's local driver used — so a\ncopied uploads volume serves unchanged after a migration.\n\n## Serving files safely\n\n```ts\nimport { assetContentHeaders, diskExtension } from '@allodium/storage';\n\nconst headers = assetContentHeaders({\n  type: file.mime_type,\n  size: found.size,\n  downloadName: file.filename,\n  forceDownload: url.searchParams.has('download'),\n  isProtected: true,           // auth-gated → private, no-cache\n});\nreturn new Response(stream, { headers });\n```\n\nWhat this gets right, all of which are easy to get wrong by hand:\n\n**SVG is never served inline.** An SVG is a document that can carry script, so serving a\nuser-uploaded one inline is stored XSS with extra steps. `INLINE_SAFE_TYPES` excludes it\ndeliberately — SVGs download instead. This is the single most common file-upload\nvulnerability and it is one line to avoid.\n\n**`X-Content-Type-Options: nosniff`** on everything, so a browser cannot decide your\n`text/plain` is really HTML.\n\n**RFC 5987 filenames.** `Content-Disposition` with both an ASCII fallback and a UTF-8\nencoding, so a file called `résumé.pdf` downloads with its name intact instead of\n`r_sum_.pdf` or a broken header.\n\n**Cache split by protection.** Public assets get a long cache; auth-gated ones get\n`private, no-cache`, so a shared proxy cannot hand one user's document to another.\n\n`diskExtension(mimeType, originalName)` derives a safe extension for the disk name,\npreferring the declared MIME type over a filename anyone can control.\n\n## Uploads happen first\n\nBecause a file row exists the moment the bytes land, content can reference it whenever.\nThe two-pass import dance — create the content to get ids, then a second pass to attach\nimages — is a symptom of a storage layer that requires an owner up front. This one\ndoesn't.\n\n## License\n\nMIT. No CLA.\n","readmeFilename":"README.md","homepage":"https://github.com/glossydev/allodium#readme","repository":{"type":"git","url":"git+https://github.com/glossydev/allodium.git","directory":"packages/storage"},"bugs":{"url":"https://github.com/glossydev/allodium/issues"}}