{"_id":"@12-apps/storage","_rev":"12-f678bd149390d7862f094db22af75522","name":"@12-apps/storage","dist-tags":{"latest":"2.8.0"},"versions":{"1.0.0":{"name":"@12-apps/storage","version":"1.0.0","license":"MIT","_id":"@12-apps/storage@1.0.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"c811a37472e5db3a8615a7dddc4e1f248fba7710","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-1.0.0.tgz","fileCount":35,"integrity":"sha512-ds6h0ZMoxq3P9o+tBfeNF5bhTUK1osXlv0sIoMxo/fd0O7tE884kyQYoCfRJdN4GwN9ZvMLs2VQFHZVR5BRFaA==","signatures":[{"sig":"MEUCICFRoz9i0jgQEys7mGHrT3RXs5a/+f1wUj61W4BgmTWyAiEAmcJtzOrW/gwrt9n8Hi4vR6qRQxq8dgbm8N+RFJogRdE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":178538},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./package.json":"./package.json"},"gitHead":"ffe62265a632670695040333d653369ed0bf728c","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"tigredonorte","email":"tigredonorte3@gmail.com"},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^4.0.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.20.0","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_1.0.0_1786608286187_0.26405038545314174","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@12-apps/storage","version":"2.0.0","license":"MIT","_id":"@12-apps/storage@2.0.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"fdaa84be22dec97ca3f63ba23571764bee276f1f","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.0.0.tgz","fileCount":35,"integrity":"sha512-tGbQNelQ4dIfkR2Nc9xbqBfEevvpRi3i6J3x7RD7hI5Ju7kIEowbd1nAEm22pDyp7hMfcEkzdcmljgxrtqcsfA==","signatures":[{"sig":"MEQCICFkBUQtaFco97uDu3Wge+gPjESuaYHY4VuSh845IwArAiBBACP9GznN3EWibzRiXeyrzSx4ldXh9Sp50pe3QiuBmQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":181488},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./package.json":"./package.json"},"gitHead":"d5bcea1f91ffc7959615b1a3569e1631507a732b","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6438e2dd-d817-46a0-a33a-88e8906fc871"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^4.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.20.0","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.0.0_1786756014877_0.6118992805197387","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@12-apps/storage","version":"2.0.1","license":"MIT","_id":"@12-apps/storage@2.0.1","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"89b90d75adbe2a3753379820caeeb5ac533daadc","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.0.1.tgz","fileCount":35,"integrity":"sha512-9J43vTwpfRAcZfTeQmsFEWhZPDf1HxPS8VAj+dcqSFJiaPySr3iNFe3SSqb2QYMGZjKzHVKj4efbtrN1r0M7/Q==","signatures":[{"sig":"MEUCIQDLGKhxJWKBPHep2jgzD9TqCWg4sipugSwoHd24WHvY7QIgX9Xrfu2L5J+IKX4/3axDFx3Zy4SgkOM+TqzDDUT0A2w=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":181488},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./package.json":"./package.json"},"gitHead":"3c4711049bf8351685612372531c3cae1370bbbb","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6438e2dd-d817-46a0-a33a-88e8906fc871"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.0.1_1787327318784_0.9056106706205167","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@12-apps/storage","version":"2.1.0","license":"MIT","_id":"@12-apps/storage@2.1.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"6f1261165b1be0a7276ff6e32a5c7095a4b81bb1","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.1.0.tgz","fileCount":37,"integrity":"sha512-EkRrqGmhCQezxYSVZMYvkFnuFMhGAyDwWXprYkfo/Rwfyv3+8rFVkUGdObLDb0Gfj/QNsEAaKYVj6eockGaAgw==","signatures":[{"sig":"MEQCIGOzkoi9nD9/tMTVAmjy/Im+tT/zfUUQQspPXgO0JLFZAiB1W5QqqlzeitrQzCdsFQ2NCjGMP2fKfJ7d/c5+CYgX+Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":183958},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./package.json":"./package.json"},"gitHead":"15c437065d0ec83f4e535ef7caa4d036327df460","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.2.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.1.0_1787352667434_0.4641321965323528","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@12-apps/storage","version":"2.2.0","license":"MIT","_id":"@12-apps/storage@2.2.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"9613f752c20152c5f6d886092914923242f57482","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.2.0.tgz","fileCount":39,"integrity":"sha512-IvVB1ONPdYktg1FXca6gWcxk7JUWlWfbwUP0F7ZlbTpI9xOEQUGyEZAP/w3N6ZFxTsdxguBXX/hK2/H69ZQFDw==","signatures":[{"sig":"MEYCIQC0S1UdA9D/27P6qlKh9XXJ13K714Xzpxi3dhaTHiqvOgIhAJBBQNVwmIZIbrELdt7L+WeNK7Gc7Y3P6lVOK4//7nGs","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":189273},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./manifest/server":"./src/manifest/server.ts"},"gitHead":"f976ac2b51207157a56e7e3faf148b5318807685","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.3.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@12-apps/wiring":"^1.10.0","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.2.0_1787438560102_0.7903711620753999","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"name":"@12-apps/storage","version":"2.3.0","license":"MIT","_id":"@12-apps/storage@2.3.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"2a126c4ede7ea0ec5f6b14929aee6041b5881be7","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.3.0.tgz","fileCount":39,"integrity":"sha512-FEHmzSaGR4dA/Jt1B1VN5lesA1wcALwV+pxWY2L8CESKulggwA/mxpg63D1q1C5P+AxT2OZCgAInFRQyN8q8AA==","signatures":[{"sig":"MEUCIHxp2/UNpvh4Hiw9pS93JMCGqSEu9eoQdMLXHz6K+9tyAiEAqx/yGYefFHPUhq85sETWLHcuhsajmQ9WacA//a6To3Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":189592},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./manifest/server":"./src/manifest/server.ts"},"gitHead":"cfd1c91f3801fb5da5c39021cd06817f95450e15","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.4.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@12-apps/wiring":"^1.10.0","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.3.0_1787473380229_0.05644163679243275","host":"s3://npm-registry-packages-npm-production"}},"2.4.0":{"name":"@12-apps/storage","version":"2.4.0","license":"MIT","_id":"@12-apps/storage@2.4.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"eada0390fde75c3f09799b57226f599a95e70e9e","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.4.0.tgz","fileCount":39,"integrity":"sha512-FZ5Ict1qFlAEJzGwvgEuRMOry64uNYAspk4rjgkmYXQiWzbSjiDY9dNBuDY76gO3kbdfDveDOOGjlYl8DBQwow==","signatures":[{"sig":"MEYCIQCbp6JAb9gBNeLZBPUyndomO9tBjWK8vVM/0gH9io/XNwIhAPuPtaNm9mzy6iXGUf2xNTo+OdS+oc+1q/zewwiwO+6Q","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":191461},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./manifest/server":"./src/manifest/server.ts"},"gitHead":"3bf9eef2568a872f949d9d26981c534210979a5b","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.8.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@12-apps/wiring":"^1.10.0","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.4.0_1787508513080_0.9460551485207764","host":"s3://npm-registry-packages-npm-production"}},"2.5.0":{"name":"@12-apps/storage","version":"2.5.0","license":"MIT","_id":"@12-apps/storage@2.5.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"fd0a0cec940976dc0f8a3ad4b91080f728190cf4","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.5.0.tgz","fileCount":43,"integrity":"sha512-+X649OTUCxlnj0MilTND8e0zJrMe6kaxNi6xV36gY006X5T6bxqCufwgKHA6S7a/ZduQfQb4TUb2IFoVBO93pg==","signatures":[{"sig":"MEUCIQCqiV04E9yHTypyGVTc684pl2yDp13nXyo/KtOhDIFWTQIgIqQRpddXPaLJ6mcplDVI1vizmwwxGSRS8TuXV6W53VI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":198162},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./manifest/server":"./src/manifest/server.ts"},"gitHead":"9aa6a7b4c316ceed44033000e57b1b02c316537b","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.9.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@12-apps/i18n":"^1.0.0","@12-apps/wiring":"^1.10.0","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.5.0_1787547304154_0.24262074690371338","host":"s3://npm-registry-packages-npm-production"}},"2.6.0":{"name":"@12-apps/storage","version":"2.6.0","license":"MIT","_id":"@12-apps/storage@2.6.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"14831eca1dae2ee4d553b180d2992082ed1d8719","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.6.0.tgz","fileCount":43,"integrity":"sha512-euZ4uwmyZ21ZftuMWH6c8EQNgAAKJ6hWCdghQto3cLs8jf3B0zntFXgE6CoZXubwiX4HemdczO116hFhNqWZLQ==","signatures":[{"sig":"MEUCIEe3ATGt5G0P/yzSQo7fuahdx4ivYbrGy1+8Cz6PrCiDAiEA6yamiLEbNCqbSucwMlN7uVZF260BHr9b2XyvHXdanrs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":199598},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./manifest/server":"./src/manifest/server.ts"},"gitHead":"7919b5bfd73132d3a8a78a6de99b0eb22575f35f","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.9.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@12-apps/i18n":"^1.1.0","@12-apps/wiring":"^1.11.0","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.6.0_1787584320522_0.681890129287698","host":"s3://npm-registry-packages-npm-production"}},"2.6.1":{"name":"@12-apps/storage","version":"2.6.1","license":"MIT","_id":"@12-apps/storage@2.6.1","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"8fa542854f517784cc85a4a3f359e7b102fc3469","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.6.1.tgz","fileCount":43,"integrity":"sha512-yLWNca24nUedDTJmJ3bMW8jeoyLM722MrrwzpmJKsaOl/0of+CLdJruyEcCxNnfq9ZlQ7mOrVVCyayTfkUPr2A==","signatures":[{"sig":"MEYCIQDR6PXdF1pMrjE59Qd4iDZ/Eo82t5Q/j6MOoLS/gY8MeAIhAIPLwaMGz/7j1QmwZ2OYca82P9iljdvnC4bhjOZ887cJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.6.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":200227},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./manifest/server":"./src/manifest/server.ts"},"gitHead":"7236c5b586f42f0ee17b408105e832eb3e570dc6","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.9.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@12-apps/i18n":"^1.1.0","@12-apps/wiring":"^1.12.1","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.6.1_1787591714013_0.12679371202431944","host":"s3://npm-registry-packages-npm-production"}},"2.7.0":{"name":"@12-apps/storage","version":"2.7.0","license":"MIT","_id":"@12-apps/storage@2.7.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"e979633c986c953914084d7c33d6fe36ff5144eb","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.7.0.tgz","fileCount":43,"integrity":"sha512-r15V/OTgd3iezRkSliFUHZwVh0ntWqkuBIbNKXi8fS7fwK9Y2Ba4iKCD0PPENpVv9MZstQIJ+0rNEeo+HDhVPg==","signatures":[{"sig":"MEYCIQD7+eFLHFtzPc0aW0RMLd3w0NJo3QNCpJ9vSDHNGKDO2AIhAJ1NEjOLVUnktytO3ea9MVH7kefo4H11qMJbKg+V4v8J","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":200282},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./s3":"./src/s3/index.ts","./hono":"./src/hono/index.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./manifest/server":"./src/manifest/server.ts"},"gitHead":"142fe64617cadbd2f852a257eafd0aaf95b73fbc","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/storage"},"_npmVersion":"12.0.2","description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.9.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","typescript":"^5.9.2","@types/react":"19.2.2","@12-apps/i18n":"^1.1.0","@12-apps/wiring":"^1.13.0","@types/react-dom":"19.2.2","@aws-sdk/client-s3":"^3.703.0","@12-apps/eslint-config":"^1.21.1","@testing-library/react":"^16.1.0","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0"},"peerDependenciesMeta":{"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"@aws-sdk/client-s3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storage_2.7.0_1787597474879_0.09772969754908867","host":"s3://npm-registry-packages-npm-production"}},"2.8.0":{"name":"@12-apps/storage","version":"2.8.0","type":"module","sideEffects":false,"description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","exports":{".":"./src/index.ts","./hono":"./src/hono/index.ts","./manifest":"./src/manifest/index.ts","./manifest/server":"./src/manifest/server.ts","./package.json":"./package.json","./react":"./src/react/index.ts","./s3":"./src/s3/index.ts","./server":"./src/server/index.ts"},"scripts":{"clean":"rm -rf node_modules coverage","test":"node ../../scripts/vitest-with-teardown.mjs run","test:watch":"vitest watch","lint":"eslint src --max-warnings 0","check-types":"tsc --noEmit","typecheck":"tsc --noEmit"},"peerDependencies":{"@12-apps/wiring":">=1.9.0","@aws-sdk/client-s3":">=3.700.0","hono":">=4.0.0","react":">=19.0.0","react-dom":">=19.0.0"},"peerDependenciesMeta":{"@aws-sdk/client-s3":{"optional":true},"hono":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true}},"dependencies":{"@12-apps/ui":"^6.10.0","zod":"^4.3.5"},"devDependencies":{"@12-apps/eslint-config":"^1.22.0","@12-apps/i18n":"^1.2.0","@12-apps/typescript-config":"^1.21.0","@12-apps/wiring":"^1.14.0","@aws-sdk/client-s3":"^3.703.0","@testing-library/react":"^16.1.0","@types/react":"19.2.2","@types/react-dom":"19.2.2","eslint":"^9.39.1","eslint-plugin-test-flakiness":"^1.4.0","hono":"^4.6.0","jsdom":"^25.0.1","react":"^19.2.0","react-dom":"^19.2.0","typescript":"^5.9.2","vitest":"^3.2.4"},"engines":{"node":">=22.0.0"},"license":"MIT","publishConfig":{"registry":"https://registry.npmjs.org","access":"public"},"repository":{"type":"git","url":"git+https://github.com/12-apps/shared-packages.git","directory":"packages/storage"},"gitHead":"4714bd10c901bcc51544843d5d6091cc1509e649","_id":"@12-apps/storage@2.8.0","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"homepage":"https://github.com/12-apps/shared-packages#readme","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-veREiKlHUvWQy+m7QwY/tKHe1Sg0mQTyzU/oKDn9qY6TC5Ka5B+kw4MFW1QEAh4C74ZRboL8eDAoX41h+eUdeA==","shasum":"0bcc3a902c79c9fa5e22f8e5d30ddd5e291dac80","tarball":"https://registry.npmjs.org/@12-apps/storage/-/storage-2.8.0.tgz","fileCount":43,"unpackedSize":200307,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fstorage@2.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDfSj+x6kugCmUAlVsi5v4Vie6jrl767RXcCf6KsbeizQIhAKvnbGU4nZy0KOJG1u40IYbwz64sXHm5F2CYTRvBJasv"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b51ae4f7-ac63-40cd-a3af-3c89a7ae1d2b"}},"directories":{},"maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/storage_2.8.0_1787617905022_0.5681157964387944"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-13T08:04:45.993Z","modified":"2026-08-25T00:31:45.601Z","1.0.0":"2026-08-13T08:04:46.365Z","2.0.0":"2026-08-15T01:06:55.065Z","2.0.1":"2026-08-21T15:48:38.950Z","2.1.0":"2026-08-21T22:51:07.572Z","2.2.0":"2026-08-22T22:42:40.241Z","2.3.0":"2026-08-23T08:23:00.396Z","2.4.0":"2026-08-23T18:08:33.221Z","2.5.0":"2026-08-24T04:55:04.307Z","2.6.0":"2026-08-24T15:12:00.661Z","2.6.1":"2026-08-24T17:15:14.164Z","2.7.0":"2026-08-24T18:51:15.093Z","2.8.0":"2026-08-25T00:31:45.188Z"},"bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"license":"MIT","homepage":"https://github.com/12-apps/shared-packages#readme","repository":{"type":"git","url":"git+https://github.com/12-apps/shared-packages.git","directory":"packages/storage"},"description":"Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the l","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"readme":"# @12-apps/storage\n\nImage uploads that land at **your own origin**, never at the bucket.\n\nOne backend factory, one frontend factory, a driver port with two\nimplementations, and an image-pipeline port so a native dependency stays the\nhost's choice.\n\n> **Adopting this?** This README is the API reference; **[`ADOPTING.md`](./ADOPTING.md)**\n> is the step-by-step wiring, including the five required config values and why\n> each of them has no default.\n\n## The rule the whole package is built around\n\n**The browser POSTs its bytes to the app server. There is no presigned-URL\nmode, and there must never be one.**\n\nA presigned bucket URL makes a working upload depend on the bucket's **CORS\nrules** — configuration that lives in neither this repository nor any deploy it\nruns. A `PUT` carrying a `Content-Type` is never a \"simple\" request, so the\nbrowser sends an `OPTIONS` preflight first; a freshly created bucket answers it\nwith `403` and no `Access-Control-Allow-*` headers, and the upload is never\nsent. What makes that failure mode particularly bad:\n\n- **The server sees nothing.** No request ever arrives — no log line, no error\n  rate, no 5xx.\n- **The browser will not say why.** A blocked cross-origin fetch is deliberately\n  indistinguishable from an offline network at the `fetch` layer.\n- **The person who sees it cannot fix it.** The message reaches a store owner,\n  about a bucket only an operator can configure.\n\nA second reason it cannot come back: a stored photo is a **set** of objects the\nserver derives from the bytes, so an upload the server never sees cannot produce\none.\n\n## Subpath exports\n\n| Import | Contents |\n| --- | --- |\n| `@12-apps/storage` | Isomorphic core: format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, URL builders, wire schemas. |\n| `@12-apps/storage/server` | `createApiStorage(config)`, the driver port, the local-disk driver, the image-pipeline port with both implementations, the reclaim. |\n| `@12-apps/storage/hono` | `storageRouter({ …config, resolveActor })` — a one-call mount. `hono` is an OPTIONAL peer. |\n| `@12-apps/storage/s3` | `createS3Driver(config)` for every S3-compatible vendor. `@aws-sdk/client-s3` is an OPTIONAL peer, resolved only here. |\n| `@12-apps/storage/react` | `createWebStorage({ apiBase })` — the bound picker, the preview, the upload hook, the browser-side re-encode. |\n\nThe exports map points at `./src` directly — no build step is required at\nruntime.\n\n## The two endpoints\n\n```\nPOST <mount>/uploads/image      raw bytes in, { data: { imageKey } } out\nGET  <mount>/uploads/local/…    the object back, or a redirect to where it lives\n```\n\nThe serve route has two behaviours and the **driver** decides which: a driver\nthat keeps its own bytes implements `read` and the route streams them; a\nbucket-backed driver does not, and the route redirects to `publicUrl`. That used\nto be an environment flag read inside the route — the same fact in a second\nplace, free to disagree with the driver actually in use.\n\n## What a stored photo is\n\n```\nproducts/<scope>/<uuid>/full.webp        the whole picture — the zoom\nproducts/<scope>/<uuid>/card-320.webp    4:3, a card on a phone\nproducts/<scope>/<uuid>/card-640.webp    4:3, a retina card / a phone hero\nproducts/<scope>/<uuid>/card-1280.webp   4:3, a retina hero on a desktop\nproducts/<scope>/<uuid>/thumb-128.webp   1:1, a 64px row thumbnail\nproducts/<scope>/<uuid>/thumb-256.webp   1:1, the same row at 2×\n```\n\nThe key saved on the row names the uncropped member, and **the key's shape is\nwhat says the crops exist**: `…/<uuid>/full.<ext>` has them, the flat\n`…/<uuid>.<ext>` does not. Nothing else records it — no column, and therefore\nnothing to keep in step across the draft, the change request, the recycle-bin\nsnapshot and the version history a row is copied into.\n\n`<scope>` is the tenant. It is what makes cross-tenant reclaim a parse rather\nthan a query: a reclaim refuses a key whose scope is not the caller's *before*\nconsulting any reference probe. A scheme without it can only answer \"may this\ntenant delete this object?\" by proving no row of **theirs** points at it — which\nis true of every other tenant's objects too.\n\n## The five values with no default\n\n`createApiStorage` requires `driver`, `maxBytes`, `imagePipeline`,\n`unscopedKeys` and `references`. Each safe value is host-specific, so a default\nwould be a decision made by whoever typed the first host and silently inherited\nby every one after it — and each of these fails **quietly**, which is the actual\nargument. A loud wrong default gets fixed on the first deploy; these do not:\n\n| Config | What a default would cost |\n| --- | --- |\n| `driver` | A container's ephemeral disk in production, reporting every upload as a success and losing the objects at the next deploy. |\n| `maxBytes` | Whatever number this file happens to carry, in a host whose tool schemas advertise something else. |\n| `imagePipeline` | Whether crops exist at all — discovered from the shape of a key in production. |\n| `unscopedKeys` | Whether one tenant's reclaim can reach another tenant's bytes. |\n| `references` | Whether a restored row still has its photo. `[]` is not neutral: it means *reclaim everything immediately*. |\n\n`logger` still defaults, and it is the one that legitimately can: a reclaim may\nonly report, never raise, so the worst a defaulted logger costs is silence.\n`references` defaulted too, until it didn't — because its default **destroys**.\n\n## Backend, in full\n\n**Note the absence of `??`.** Every value below either comes from the\nenvironment and must be there, or is written out. That is deliberate: a\n`process.env.UPLOADS_DIR ?? \\`${process.cwd()}/.uploads\\`` in this position is\nexactly the fail-open default the table above refuses to have. `UPLOADS_DIR`\nunset in production means uploads succeed, one container holds the object, the\nother 404s half the `<img>` requests, and the next deploy discards both — with\nnothing in any log, because `writeFile` succeeded every time.\n\n```ts\nimport { createApiStorage } from '@12-apps/storage/server';\nimport { createLocalDiskDriver, passthroughImagePipeline } from '@12-apps/storage/server';\nimport { DEFAULT_MAX_UPLOAD_BYTES, STORAGE_PATHS } from '@12-apps/storage';\n\n/** Fail at BOOT for a missing value, rather than at the next deploy. */\nfunction requireEnv(name: string): string {\n  const value = process.env[name];\n  if (!value) throw new Error(`${name} is required — it must be a MOUNTED VOLUME, not a container path.`);\n  return value;\n}\n\nconst storage = createApiStorage({\n  driver: createLocalDiskDriver({\n    root: requireEnv('UPLOADS_DIR'),\n    publicPathPrefix: `/api${STORAGE_PATHS.serve}`,\n  }),\n  maxBytes: DEFAULT_MAX_UPLOAD_BYTES,\n  imagePipeline: passthroughImagePipeline(),\n  // 'reject' because this host is fresh: every key it will ever mint is scoped, so\n  // a key without a scope can only be someone else's or nobody's. A host with rows\n  // that predate the scope segment passes 'accept' — see ADOPTING.md rule 2.\n  unscopedKeys: 'reject',\n  // Which of YOUR tables can still need an object after the row stopped pointing at\n  // it. `[]` says \"nothing here copies a key\" — write it only if you have checked.\n  references: [\n    { name: 'live-rows', referenced: (scope, key) => rowPointsAt(scope, key) },\n  ],\n});\n```\n\nFor **development** the `??` is fine, and only here, because a lost object on a\nlaptop costs nothing:\n\n```ts\n// DEV ONLY. Never in the block above.\nroot: process.env.UPLOADS_DIR ?? `${process.cwd()}/.uploads`,\n```\n\n`storage` carries everything else a host needs:\n\n| Member | Use |\n| --- | --- |\n| `routes` | Framework-neutral descriptors. `@12-apps/storage/hono` mounts them. |\n| `limits` | What this mount enforces — feed a tool schema from it, never from a constant of your own. |\n| `schemas.inlineImage` | A zod schema whose base64 ceiling **is** `maxBytes`. |\n| `schemas.objectKey` | The schema for an `imageKey` field. **Not `z.string()`** — see below. |\n| `storeImage` / `storeInlineImage` | The write path for a host write that carries bytes (an agent's only option). |\n| `urls.objectUrl` / `urls.imageSources` / `urls.versionedObjectUrl` | Key → URL, through the active driver. |\n| `reclaim.*` | Reclaiming a replaced object, a superseded set, or keys a failed write just minted. |\n\n### An `imageKey` field is not a string field\n\n`objectUrl` returns an already-absolute value **unchanged** — a deliberate\naffordance for a host migrating off a URL column. So a write body that accepts\n`imageKey: z.string()` accepts `https://tracker.example/p.png`, and every buyer\nloading that storefront page then sends their IP and user-agent to a third\nparty. No upload, no bucket and no driver is involved, so nothing on the storage\npath can catch it. Use the mount's own schema:\n\n```ts\nconst body = z.object({\n  imageKey: storage.schemas.objectKey.nullish(),\n  image: storage.schemas.inlineImage.optional(),\n}).superRefine(refineImageInput);\n```\n\n## Frontend, in full\n\n```ts\nimport { createWebStorage } from '@12-apps/storage/react';\n\nconst storage = createWebStorage({ apiBase: '/api' });\n// storage.page        a standalone upload surface\n// storage.ImageField  the field a form drops in — a key comes out\n// storage.useUpload   the primitive, for a bespoke affordance\n```\n\n## The image pipeline is a port\n\nImage processing means a native module, and a native module is build surface\nevery consumer would pay for on every deploy whether or not it wants crops. So\n`sharp` is neither a dependency nor a peer here: a host that has it passes the\nmodule in.\n\n```ts\nimport sharp from 'sharp';\nimport { createSharpImagePipeline } from '@12-apps/storage/server';\n\nimagePipeline: createSharpImagePipeline({ sharp });\n```\n\n`passthroughImagePipeline()` is the other choice: the bytes are stored as they\narrived, no crops, flat keys — which is exactly what every catalog image was\nbefore crops existed.\n\n**The magic-number check is not the pipeline's job**, and this README used to say\nit was. It belongs to the WRITER: `storeImage` verifies the bytes against the\ndeclared type as its first statement, before any pipeline sees them. That\nplacement is deliberate, because the two implementations cannot both be trusted\nwith it — `passthrough` verifies as part of doing nothing, while `sharp` cannot:\n\"did libvips decode this\" and \"is this the format that was declared\" are\ndifferent questions, and libvips decodes plenty of formats this allowlist\nexcludes. Putting the check in the writer means no path to storage goes around\nit, whichever pipeline a host injected and whether the bytes arrived at the\nendpoint, base64 inside a write, or through a direct `storeImage` call.\n\n## A second vendor is a config entry\n\n`createS3Driver` covers every S3-compatible store. AWS needs a `region`;\nDigitalOcean Spaces, MinIO, R2 and the rest need an `endpoint`, plus\n`forcePathStyle` for the ones that cannot do virtual-hosted addressing. No\nbranch in the upload path changes, and no host code is written.\n\nNothing in this package reads `process.env`.\n","readmeFilename":"README.md"}