@thebes/cadmus 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -0
  3. package/dist/astro/index.cjs +149 -0
  4. package/dist/astro/index.cjs.map +1 -0
  5. package/dist/astro/index.d.cts +101 -0
  6. package/dist/astro/index.d.cts.map +1 -0
  7. package/dist/astro/index.d.ts +101 -0
  8. package/dist/astro/index.d.ts.map +1 -0
  9. package/dist/astro/index.js +146 -0
  10. package/dist/astro/index.js.map +1 -0
  11. package/dist/auth/index.cjs +59 -0
  12. package/dist/auth/index.cjs.map +1 -0
  13. package/dist/auth/index.d.cts +14 -0
  14. package/dist/auth/index.d.cts.map +1 -0
  15. package/dist/auth/index.d.ts +14 -0
  16. package/dist/auth/index.d.ts.map +1 -0
  17. package/dist/auth/index.js +54 -0
  18. package/dist/auth/index.js.map +1 -0
  19. package/dist/cache/index.cjs +18 -0
  20. package/dist/cache/index.cjs.map +1 -0
  21. package/dist/cache/index.d.cts +10 -0
  22. package/dist/cache/index.d.cts.map +1 -0
  23. package/dist/cache/index.d.ts +10 -0
  24. package/dist/cache/index.d.ts.map +1 -0
  25. package/dist/cache/index.js +17 -0
  26. package/dist/cache/index.js.map +1 -0
  27. package/dist/cms/index.cjs +763 -0
  28. package/dist/cms/index.cjs.map +1 -0
  29. package/dist/cms/index.d.cts +2 -0
  30. package/dist/cms/index.d.ts +2 -0
  31. package/dist/cms/index.js +743 -0
  32. package/dist/cms/index.js.map +1 -0
  33. package/dist/db/index.cjs +10 -0
  34. package/dist/db/index.cjs.map +1 -0
  35. package/dist/db/index.d.cts +7 -0
  36. package/dist/db/index.d.cts.map +1 -0
  37. package/dist/db/index.d.ts +7 -0
  38. package/dist/db/index.d.ts.map +1 -0
  39. package/dist/db/index.js +9 -0
  40. package/dist/db/index.js.map +1 -0
  41. package/dist/email/index.cjs +25 -0
  42. package/dist/email/index.cjs.map +1 -0
  43. package/dist/email/index.d.cts +12 -0
  44. package/dist/email/index.d.cts.map +1 -0
  45. package/dist/email/index.d.ts +12 -0
  46. package/dist/email/index.d.ts.map +1 -0
  47. package/dist/email/index.js +24 -0
  48. package/dist/email/index.js.map +1 -0
  49. package/dist/errors-CW6Lz0AQ.cjs +196 -0
  50. package/dist/errors-CW6Lz0AQ.cjs.map +1 -0
  51. package/dist/errors-mZIqZJO4.js +125 -0
  52. package/dist/errors-mZIqZJO4.js.map +1 -0
  53. package/dist/hono/index.cjs +132 -0
  54. package/dist/hono/index.cjs.map +1 -0
  55. package/dist/hono/index.d.cts +59 -0
  56. package/dist/hono/index.d.cts.map +1 -0
  57. package/dist/hono/index.d.ts +59 -0
  58. package/dist/hono/index.d.ts.map +1 -0
  59. package/dist/hono/index.js +130 -0
  60. package/dist/hono/index.js.map +1 -0
  61. package/dist/index-BUrCSGVb.d.cts +616 -0
  62. package/dist/index-BUrCSGVb.d.cts.map +1 -0
  63. package/dist/index-BUrCSGVb.d.ts +616 -0
  64. package/dist/index-BUrCSGVb.d.ts.map +1 -0
  65. package/dist/index.cjs +60 -0
  66. package/dist/index.d.cts +107 -0
  67. package/dist/index.d.cts.map +1 -0
  68. package/dist/index.d.ts +107 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +11 -0
  71. package/dist/queues/index.cjs +31 -0
  72. package/dist/queues/index.cjs.map +1 -0
  73. package/dist/queues/index.d.cts +22 -0
  74. package/dist/queues/index.d.cts.map +1 -0
  75. package/dist/queues/index.d.ts +22 -0
  76. package/dist/queues/index.d.ts.map +1 -0
  77. package/dist/queues/index.js +29 -0
  78. package/dist/queues/index.js.map +1 -0
  79. package/dist/rate-limit/index.cjs +38 -0
  80. package/dist/rate-limit/index.cjs.map +1 -0
  81. package/dist/rate-limit/index.d.cts +14 -0
  82. package/dist/rate-limit/index.d.cts.map +1 -0
  83. package/dist/rate-limit/index.d.ts +14 -0
  84. package/dist/rate-limit/index.d.ts.map +1 -0
  85. package/dist/rate-limit/index.js +37 -0
  86. package/dist/rate-limit/index.js.map +1 -0
  87. package/dist/session/index.cjs +48 -0
  88. package/dist/session/index.cjs.map +1 -0
  89. package/dist/session/index.d.cts +14 -0
  90. package/dist/session/index.d.cts.map +1 -0
  91. package/dist/session/index.d.ts +14 -0
  92. package/dist/session/index.d.ts.map +1 -0
  93. package/dist/session/index.js +45 -0
  94. package/dist/session/index.js.map +1 -0
  95. package/dist/storage/index.cjs +29 -0
  96. package/dist/storage/index.cjs.map +1 -0
  97. package/dist/storage/index.d.cts +38 -0
  98. package/dist/storage/index.d.cts.map +1 -0
  99. package/dist/storage/index.d.ts +38 -0
  100. package/dist/storage/index.d.ts.map +1 -0
  101. package/dist/storage/index.js +26 -0
  102. package/dist/storage/index.js.map +1 -0
  103. package/package.json +115 -0
@@ -0,0 +1,14 @@
1
+ //#region src/session/index.d.ts
2
+ /** Stores `value` under `key` in KV, JSON-serialized, with a TTL in seconds. */
3
+ declare function createSession<T>(kv: KVNamespace, key: string, value: T, ttlSeconds: number): Promise<void>;
4
+ /**
5
+ * Reads and JSON-parses the session at `key`. Retries on a miss (KV's
6
+ * eventual consistency can otherwise produce a false negative right after
7
+ * a write) before returning null.
8
+ */
9
+ declare function getSession<T>(kv: KVNamespace, key: string): Promise<T | null>;
10
+ /** Deletes the session at `key`. */
11
+ declare function deleteSession(kv: KVNamespace, key: string): Promise<void>;
12
+ //#endregion
13
+ export { createSession, deleteSession, getSession };
14
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/session/index.ts"],"mappings":";;iBAsBsB,aAAA,IACpB,EAAA,EAAI,WAAA,EACJ,GAAA,UACA,KAAA,EAAO,CAAA,EACP,UAAA,WACC,OAAA;;;;;;iBAamB,UAAA,IACpB,EAAA,EAAI,WAAA,EACJ,GAAA,WACC,OAAA,CAAQ,CAAA;;iBAeW,aAAA,CACpB,EAAA,EAAI,WAAA,EACJ,GAAA,WACC,OAAO"}
@@ -0,0 +1,14 @@
1
+ //#region src/session/index.d.ts
2
+ /** Stores `value` under `key` in KV, JSON-serialized, with a TTL in seconds. */
3
+ declare function createSession<T>(kv: KVNamespace, key: string, value: T, ttlSeconds: number): Promise<void>;
4
+ /**
5
+ * Reads and JSON-parses the session at `key`. Retries on a miss (KV's
6
+ * eventual consistency can otherwise produce a false negative right after
7
+ * a write) before returning null.
8
+ */
9
+ declare function getSession<T>(kv: KVNamespace, key: string): Promise<T | null>;
10
+ /** Deletes the session at `key`. */
11
+ declare function deleteSession(kv: KVNamespace, key: string): Promise<void>;
12
+ //#endregion
13
+ export { createSession, deleteSession, getSession };
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/session/index.ts"],"mappings":";;iBAsBsB,aAAA,IACpB,EAAA,EAAI,WAAA,EACJ,GAAA,UACA,KAAA,EAAO,CAAA,EACP,UAAA,WACC,OAAA;;;;;;iBAamB,UAAA,IACpB,EAAA,EAAI,WAAA,EACJ,GAAA,WACC,OAAA,CAAQ,CAAA;;iBAeW,aAAA,CACpB,EAAA,EAAI,WAAA,EACJ,GAAA,WACC,OAAO"}
@@ -0,0 +1,45 @@
1
+ import { d as CadmusSessionError } from "../errors-mZIqZJO4.js";
2
+ //#region src/session/index.ts
3
+ const RETRY_ATTEMPTS = 2;
4
+ const RETRY_DELAY_MS = 100;
5
+ function sleep(ms) {
6
+ return new Promise((resolve) => setTimeout(resolve, ms));
7
+ }
8
+ /** Stores `value` under `key` in KV, JSON-serialized, with a TTL in seconds. */
9
+ async function createSession(kv, key, value, ttlSeconds) {
10
+ try {
11
+ await kv.put(key, JSON.stringify(value), { expirationTtl: ttlSeconds });
12
+ } catch (cause) {
13
+ throw new CadmusSessionError(`Failed to create session "${key}"`, cause);
14
+ }
15
+ }
16
+ /**
17
+ * Reads and JSON-parses the session at `key`. Retries on a miss (KV's
18
+ * eventual consistency can otherwise produce a false negative right after
19
+ * a write) before returning null.
20
+ */
21
+ async function getSession(kv, key) {
22
+ for (let attempt = 0; attempt <= RETRY_ATTEMPTS; attempt++) {
23
+ let raw;
24
+ try {
25
+ raw = await kv.get(key);
26
+ } catch (cause) {
27
+ throw new CadmusSessionError(`Failed to read session "${key}"`, cause);
28
+ }
29
+ if (raw !== null) return JSON.parse(raw);
30
+ if (attempt < RETRY_ATTEMPTS) await sleep(RETRY_DELAY_MS);
31
+ }
32
+ return null;
33
+ }
34
+ /** Deletes the session at `key`. */
35
+ async function deleteSession(kv, key) {
36
+ try {
37
+ await kv.delete(key);
38
+ } catch (cause) {
39
+ throw new CadmusSessionError(`Failed to delete session "${key}"`, cause);
40
+ }
41
+ }
42
+ //#endregion
43
+ export { createSession, deleteSession, getSession };
44
+
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/session/index.ts"],"sourcesContent":["// Copyright (c) 2026 BowenLabs. All rights reserved.\n// Cadmus is MIT licensed. See LICENSE in the repo root.\n//\n// @thebes/cadmus/session\n//\n// Thin JSON-over-KV session store. Takes a raw KVNamespace and a caller-\n// chosen key — no \"session:\" prefix or namespace convention baked in,\n// since that's an app-level choice, not a framework one. KV is eventually\n// consistent, so getSession() retries on a miss (a write immediately\n// followed by a read on a different edge location can otherwise see a\n// false negative) before concluding the session genuinely doesn't exist.\n\nimport { CadmusSessionError } from \"../errors.js\";\n\nconst RETRY_ATTEMPTS = 2;\nconst RETRY_DELAY_MS = 100;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Stores `value` under `key` in KV, JSON-serialized, with a TTL in seconds. */\nexport async function createSession<T>(\n kv: KVNamespace,\n key: string,\n value: T,\n ttlSeconds: number,\n): Promise<void> {\n try {\n await kv.put(key, JSON.stringify(value), { expirationTtl: ttlSeconds });\n } catch (cause) {\n throw new CadmusSessionError(`Failed to create session \"${key}\"`, cause);\n }\n}\n\n/**\n * Reads and JSON-parses the session at `key`. Retries on a miss (KV's\n * eventual consistency can otherwise produce a false negative right after\n * a write) before returning null.\n */\nexport async function getSession<T>(\n kv: KVNamespace,\n key: string,\n): Promise<T | null> {\n for (let attempt = 0; attempt <= RETRY_ATTEMPTS; attempt++) {\n let raw: string | null;\n try {\n raw = await kv.get(key);\n } catch (cause) {\n throw new CadmusSessionError(`Failed to read session \"${key}\"`, cause);\n }\n if (raw !== null) return JSON.parse(raw) as T;\n if (attempt < RETRY_ATTEMPTS) await sleep(RETRY_DELAY_MS);\n }\n return null;\n}\n\n/** Deletes the session at `key`. */\nexport async function deleteSession(\n kv: KVNamespace,\n key: string,\n): Promise<void> {\n try {\n await kv.delete(key);\n } catch (cause) {\n throw new CadmusSessionError(`Failed to delete session \"${key}\"`, cause);\n }\n}\n"],"mappings":";;AAcA,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AAEvB,SAAS,MAAM,IAA2B;CACxC,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;;AAGA,eAAsB,cACpB,IACA,KACA,OACA,YACe;CACf,IAAI;EACF,MAAM,GAAG,IAAI,KAAK,KAAK,UAAU,KAAK,GAAG,EAAE,eAAe,WAAW,CAAC;CACxE,SAAS,OAAO;EACd,MAAM,IAAI,mBAAmB,6BAA6B,IAAI,IAAI,KAAK;CACzE;AACF;;;;;;AAOA,eAAsB,WACpB,IACA,KACmB;CACnB,KAAK,IAAI,UAAU,GAAG,WAAW,gBAAgB,WAAW;EAC1D,IAAI;EACJ,IAAI;GACF,MAAM,MAAM,GAAG,IAAI,GAAG;EACxB,SAAS,OAAO;GACd,MAAM,IAAI,mBAAmB,2BAA2B,IAAI,IAAI,KAAK;EACvE;EACA,IAAI,QAAQ,MAAM,OAAO,KAAK,MAAM,GAAG;EACvC,IAAI,UAAU,gBAAgB,MAAM,MAAM,cAAc;CAC1D;CACA,OAAO;AACT;;AAGA,eAAsB,cACpB,IACA,KACe;CACf,IAAI;EACF,MAAM,GAAG,OAAO,GAAG;CACrB,SAAS,OAAO;EACd,MAAM,IAAI,mBAAmB,6BAA6B,IAAI,IAAI,KAAK;CACzE;AACF"}
@@ -0,0 +1,29 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_errors = require("../errors-CW6Lz0AQ.cjs");
3
+ //#region src/storage/index.ts
4
+ /** MIME types `validateImageFile` accepts — never trust a client-sent
5
+ * MIME type beyond this whitelist check; it's still just `file.type`,
6
+ * not a real content sniff, but rules out the obviously-wrong cases. */
7
+ const IMAGE_MIME_WHITELIST = [
8
+ "image/jpeg",
9
+ "image/png",
10
+ "image/webp",
11
+ "image/gif"
12
+ ];
13
+ const MAX_IMAGE_UPLOAD_BYTES = 5 * 1024 * 1024;
14
+ /**
15
+ * Validates a file against the image MIME whitelist and 5MB size cap
16
+ * before it ever reaches `ImageService.upload()`/R2. Throws
17
+ * `CadmusStorageError` — callers map that to the right HTTP status
18
+ * (400 for an invalid type, 413 for oversize).
19
+ */
20
+ function validateImageFile(file) {
21
+ if (!IMAGE_MIME_WHITELIST.includes(file.type)) throw new require_errors.CadmusStorageError(`Unsupported file type: ${file.type || "unknown"}`);
22
+ if (file.size > 5242880) throw new require_errors.CadmusStorageError(`File exceeds the ${MAX_IMAGE_UPLOAD_BYTES / (1024 * 1024)}MB limit`);
23
+ }
24
+ //#endregion
25
+ exports.IMAGE_MIME_WHITELIST = IMAGE_MIME_WHITELIST;
26
+ exports.MAX_IMAGE_UPLOAD_BYTES = MAX_IMAGE_UPLOAD_BYTES;
27
+ exports.validateImageFile = validateImageFile;
28
+
29
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["CadmusStorageError"],"sources":["../../src/storage/index.ts"],"sourcesContent":["// Copyright (c) 2026 BowenLabs. All rights reserved.\n// Cadmus is MIT licensed. See LICENSE in the repo root.\n//\n// @thebes/cadmus/storage\n//\n// Defines the ImageService contract only — R2 upload/serve and any\n// alternate implementation (e.g. a Cloudflare Images extension) live\n// outside this primitive. Cadmus ships the interface so apps can swap\n// implementations without touching any component, renderer, or block\n// data; it has no opinion on which implementation is active.\nimport { CadmusStorageError } from \"../errors.js\";\n\n/** A rendered `<img>`-ready description of a stored image. */\nexport interface RenderedImage {\n src: string;\n srcset?: string;\n sizes?: string;\n}\n\n/**\n * Resolved once per app and imported everywhere images are read or\n * written. Never construct storage URLs or `cdn-cgi/image/...` paths\n * inline — always go through an `ImageService`.\n */\nexport interface ImageService {\n upload: (file: File) => Promise<{ url: string }>;\n render: (image: {\n url: string;\n width?: number;\n height?: number;\n alt: string;\n }) => RenderedImage;\n}\n\n/** MIME types `validateImageFile` accepts — never trust a client-sent\n * MIME type beyond this whitelist check; it's still just `file.type`,\n * not a real content sniff, but rules out the obviously-wrong cases. */\nexport const IMAGE_MIME_WHITELIST = [\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n \"image/gif\",\n] as const;\n\nexport const MAX_IMAGE_UPLOAD_BYTES = 5 * 1024 * 1024; // 5MB\n\n/**\n * Validates a file against the image MIME whitelist and 5MB size cap\n * before it ever reaches `ImageService.upload()`/R2. Throws\n * `CadmusStorageError` — callers map that to the right HTTP status\n * (400 for an invalid type, 413 for oversize).\n */\nexport function validateImageFile(file: File): void {\n if (\n !IMAGE_MIME_WHITELIST.includes(\n file.type as (typeof IMAGE_MIME_WHITELIST)[number],\n )\n ) {\n throw new CadmusStorageError(\n `Unsupported file type: ${file.type || \"unknown\"}`,\n );\n }\n if (file.size > MAX_IMAGE_UPLOAD_BYTES) {\n throw new CadmusStorageError(\n `File exceeds the ${MAX_IMAGE_UPLOAD_BYTES / (1024 * 1024)}MB limit`,\n );\n }\n}\n"],"mappings":";;;;;;AAqCA,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;AACF;AAEA,MAAa,yBAAyB,IAAI,OAAO;;;;;;;AAQjD,SAAgB,kBAAkB,MAAkB;CAClD,IACE,CAAC,qBAAqB,SACpB,KAAK,IACP,GAEA,MAAM,IAAIA,eAAAA,mBACR,0BAA0B,KAAK,QAAQ,WACzC;CAEF,IAAI,KAAK,OAAA,SACP,MAAM,IAAIA,eAAAA,mBACR,oBAAoB,0BAA0B,OAAO,MAAM,SAC7D;AAEJ"}
@@ -0,0 +1,38 @@
1
+ //#region src/storage/index.d.ts
2
+ /** A rendered `<img>`-ready description of a stored image. */
3
+ interface RenderedImage {
4
+ src: string;
5
+ srcset?: string;
6
+ sizes?: string;
7
+ }
8
+ /**
9
+ * Resolved once per app and imported everywhere images are read or
10
+ * written. Never construct storage URLs or `cdn-cgi/image/...` paths
11
+ * inline — always go through an `ImageService`.
12
+ */
13
+ interface ImageService {
14
+ upload: (file: File) => Promise<{
15
+ url: string;
16
+ }>;
17
+ render: (image: {
18
+ url: string;
19
+ width?: number;
20
+ height?: number;
21
+ alt: string;
22
+ }) => RenderedImage;
23
+ }
24
+ /** MIME types `validateImageFile` accepts — never trust a client-sent
25
+ * MIME type beyond this whitelist check; it's still just `file.type`,
26
+ * not a real content sniff, but rules out the obviously-wrong cases. */
27
+ declare const IMAGE_MIME_WHITELIST: readonly ["image/jpeg", "image/png", "image/webp", "image/gif"];
28
+ declare const MAX_IMAGE_UPLOAD_BYTES: number;
29
+ /**
30
+ * Validates a file against the image MIME whitelist and 5MB size cap
31
+ * before it ever reaches `ImageService.upload()`/R2. Throws
32
+ * `CadmusStorageError` — callers map that to the right HTTP status
33
+ * (400 for an invalid type, 413 for oversize).
34
+ */
35
+ declare function validateImageFile(file: File): void;
36
+ //#endregion
37
+ export { IMAGE_MIME_WHITELIST, ImageService, MAX_IMAGE_UPLOAD_BYTES, RenderedImage, validateImageFile };
38
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/storage/index.ts"],"mappings":";;UAaiB,aAAA;EACf,GAAA;EACA,MAAA;EACA,KAAA;AAAA;;;;AAAK;AAQP;UAAiB,YAAA;EACf,MAAA,GAAS,IAAA,EAAM,IAAA,KAAS,OAAA;IAAU,GAAA;EAAA;EAClC,MAAA,GAAS,KAAA;IACP,GAAA;IACA,KAAA;IACA,MAAA;IACA,GAAA;EAAA,MACI,aAAA;AAAA;;;;cAMK,oBAAA;AAAA,cAOA,sBAAA;;;;;AAbQ;AAMrB;iBAegB,iBAAA,CAAkB,IAAU,EAAJ,IAAI"}
@@ -0,0 +1,38 @@
1
+ //#region src/storage/index.d.ts
2
+ /** A rendered `<img>`-ready description of a stored image. */
3
+ interface RenderedImage {
4
+ src: string;
5
+ srcset?: string;
6
+ sizes?: string;
7
+ }
8
+ /**
9
+ * Resolved once per app and imported everywhere images are read or
10
+ * written. Never construct storage URLs or `cdn-cgi/image/...` paths
11
+ * inline — always go through an `ImageService`.
12
+ */
13
+ interface ImageService {
14
+ upload: (file: File) => Promise<{
15
+ url: string;
16
+ }>;
17
+ render: (image: {
18
+ url: string;
19
+ width?: number;
20
+ height?: number;
21
+ alt: string;
22
+ }) => RenderedImage;
23
+ }
24
+ /** MIME types `validateImageFile` accepts — never trust a client-sent
25
+ * MIME type beyond this whitelist check; it's still just `file.type`,
26
+ * not a real content sniff, but rules out the obviously-wrong cases. */
27
+ declare const IMAGE_MIME_WHITELIST: readonly ["image/jpeg", "image/png", "image/webp", "image/gif"];
28
+ declare const MAX_IMAGE_UPLOAD_BYTES: number;
29
+ /**
30
+ * Validates a file against the image MIME whitelist and 5MB size cap
31
+ * before it ever reaches `ImageService.upload()`/R2. Throws
32
+ * `CadmusStorageError` — callers map that to the right HTTP status
33
+ * (400 for an invalid type, 413 for oversize).
34
+ */
35
+ declare function validateImageFile(file: File): void;
36
+ //#endregion
37
+ export { IMAGE_MIME_WHITELIST, ImageService, MAX_IMAGE_UPLOAD_BYTES, RenderedImage, validateImageFile };
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/storage/index.ts"],"mappings":";;UAaiB,aAAA;EACf,GAAA;EACA,MAAA;EACA,KAAA;AAAA;;;;AAAK;AAQP;UAAiB,YAAA;EACf,MAAA,GAAS,IAAA,EAAM,IAAA,KAAS,OAAA;IAAU,GAAA;EAAA;EAClC,MAAA,GAAS,KAAA;IACP,GAAA;IACA,KAAA;IACA,MAAA;IACA,GAAA;EAAA,MACI,aAAA;AAAA;;;;cAMK,oBAAA;AAAA,cAOA,sBAAA;;;;;AAbQ;AAMrB;iBAegB,iBAAA,CAAkB,IAAU,EAAJ,IAAI"}
@@ -0,0 +1,26 @@
1
+ import { f as CadmusStorageError } from "../errors-mZIqZJO4.js";
2
+ //#region src/storage/index.ts
3
+ /** MIME types `validateImageFile` accepts — never trust a client-sent
4
+ * MIME type beyond this whitelist check; it's still just `file.type`,
5
+ * not a real content sniff, but rules out the obviously-wrong cases. */
6
+ const IMAGE_MIME_WHITELIST = [
7
+ "image/jpeg",
8
+ "image/png",
9
+ "image/webp",
10
+ "image/gif"
11
+ ];
12
+ const MAX_IMAGE_UPLOAD_BYTES = 5 * 1024 * 1024;
13
+ /**
14
+ * Validates a file against the image MIME whitelist and 5MB size cap
15
+ * before it ever reaches `ImageService.upload()`/R2. Throws
16
+ * `CadmusStorageError` — callers map that to the right HTTP status
17
+ * (400 for an invalid type, 413 for oversize).
18
+ */
19
+ function validateImageFile(file) {
20
+ if (!IMAGE_MIME_WHITELIST.includes(file.type)) throw new CadmusStorageError(`Unsupported file type: ${file.type || "unknown"}`);
21
+ if (file.size > 5242880) throw new CadmusStorageError(`File exceeds the ${MAX_IMAGE_UPLOAD_BYTES / (1024 * 1024)}MB limit`);
22
+ }
23
+ //#endregion
24
+ export { IMAGE_MIME_WHITELIST, MAX_IMAGE_UPLOAD_BYTES, validateImageFile };
25
+
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/storage/index.ts"],"sourcesContent":["// Copyright (c) 2026 BowenLabs. All rights reserved.\n// Cadmus is MIT licensed. See LICENSE in the repo root.\n//\n// @thebes/cadmus/storage\n//\n// Defines the ImageService contract only — R2 upload/serve and any\n// alternate implementation (e.g. a Cloudflare Images extension) live\n// outside this primitive. Cadmus ships the interface so apps can swap\n// implementations without touching any component, renderer, or block\n// data; it has no opinion on which implementation is active.\nimport { CadmusStorageError } from \"../errors.js\";\n\n/** A rendered `<img>`-ready description of a stored image. */\nexport interface RenderedImage {\n src: string;\n srcset?: string;\n sizes?: string;\n}\n\n/**\n * Resolved once per app and imported everywhere images are read or\n * written. Never construct storage URLs or `cdn-cgi/image/...` paths\n * inline — always go through an `ImageService`.\n */\nexport interface ImageService {\n upload: (file: File) => Promise<{ url: string }>;\n render: (image: {\n url: string;\n width?: number;\n height?: number;\n alt: string;\n }) => RenderedImage;\n}\n\n/** MIME types `validateImageFile` accepts — never trust a client-sent\n * MIME type beyond this whitelist check; it's still just `file.type`,\n * not a real content sniff, but rules out the obviously-wrong cases. */\nexport const IMAGE_MIME_WHITELIST = [\n \"image/jpeg\",\n \"image/png\",\n \"image/webp\",\n \"image/gif\",\n] as const;\n\nexport const MAX_IMAGE_UPLOAD_BYTES = 5 * 1024 * 1024; // 5MB\n\n/**\n * Validates a file against the image MIME whitelist and 5MB size cap\n * before it ever reaches `ImageService.upload()`/R2. Throws\n * `CadmusStorageError` — callers map that to the right HTTP status\n * (400 for an invalid type, 413 for oversize).\n */\nexport function validateImageFile(file: File): void {\n if (\n !IMAGE_MIME_WHITELIST.includes(\n file.type as (typeof IMAGE_MIME_WHITELIST)[number],\n )\n ) {\n throw new CadmusStorageError(\n `Unsupported file type: ${file.type || \"unknown\"}`,\n );\n }\n if (file.size > MAX_IMAGE_UPLOAD_BYTES) {\n throw new CadmusStorageError(\n `File exceeds the ${MAX_IMAGE_UPLOAD_BYTES / (1024 * 1024)}MB limit`,\n );\n }\n}\n"],"mappings":";;;;;AAqCA,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;AACF;AAEA,MAAa,yBAAyB,IAAI,OAAO;;;;;;;AAQjD,SAAgB,kBAAkB,MAAkB;CAClD,IACE,CAAC,qBAAqB,SACpB,KAAK,IACP,GAEA,MAAM,IAAI,mBACR,0BAA0B,KAAK,QAAQ,WACzC;CAEF,IAAI,KAAK,OAAA,SACP,MAAM,IAAI,mBACR,oBAAoB,0BAA0B,OAAO,MAAM,SAC7D;AAEJ"}
package/package.json ADDED
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "@thebes/cadmus",
3
+ "version": "0.2.1",
4
+ "description": "V8-first, Cloudflare-native full-stack framework primitives",
5
+ "author": "BowenLabs <hello@bowenlabs.io>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/bowenlabs/project-thebes",
10
+ "directory": "packages/cadmus"
11
+ },
12
+ "homepage": "https://github.com/bowenlabs/project-thebes/tree/main/packages/cadmus#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/bowenlabs/project-thebes/issues"
15
+ },
16
+ "keywords": [
17
+ "cloudflare",
18
+ "cloudflare-workers",
19
+ "v8",
20
+ "framework",
21
+ "d1",
22
+ "kv",
23
+ "r2",
24
+ "hono",
25
+ "astro",
26
+ "tanstack"
27
+ ],
28
+ "type": "module",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./auth": {
35
+ "types": "./dist/auth/index.d.ts",
36
+ "default": "./dist/auth/index.js"
37
+ },
38
+ "./db": {
39
+ "types": "./dist/db/index.d.ts",
40
+ "default": "./dist/db/index.js"
41
+ },
42
+ "./storage": {
43
+ "types": "./dist/storage/index.d.ts",
44
+ "default": "./dist/storage/index.js"
45
+ },
46
+ "./cache": {
47
+ "types": "./dist/cache/index.d.ts",
48
+ "default": "./dist/cache/index.js"
49
+ },
50
+ "./email": {
51
+ "types": "./dist/email/index.d.ts",
52
+ "default": "./dist/email/index.js"
53
+ },
54
+ "./rate-limit": {
55
+ "types": "./dist/rate-limit/index.d.ts",
56
+ "default": "./dist/rate-limit/index.js"
57
+ },
58
+ "./session": {
59
+ "types": "./dist/session/index.d.ts",
60
+ "default": "./dist/session/index.js"
61
+ },
62
+ "./queues": {
63
+ "types": "./dist/queues/index.d.ts",
64
+ "default": "./dist/queues/index.js"
65
+ },
66
+ "./hono": {
67
+ "types": "./dist/hono/index.d.ts",
68
+ "default": "./dist/hono/index.js"
69
+ },
70
+ "./cms": {
71
+ "types": "./dist/cms/index.d.ts",
72
+ "default": "./dist/cms/index.js"
73
+ },
74
+ "./astro": {
75
+ "types": "./dist/astro/index.d.ts",
76
+ "default": "./dist/astro/index.js"
77
+ }
78
+ },
79
+ "peerDependencies": {
80
+ "astro": ">=5.0.0",
81
+ "hono": ">=4.0.0"
82
+ },
83
+ "peerDependenciesMeta": {
84
+ "astro": {
85
+ "optional": true
86
+ },
87
+ "hono": {
88
+ "optional": true
89
+ }
90
+ },
91
+ "devDependencies": {
92
+ "@cloudflare/vitest-pool-workers": "latest",
93
+ "@cloudflare/workers-types": "latest",
94
+ "astro": "latest",
95
+ "hono": "latest",
96
+ "typescript": "latest",
97
+ "vite-plus": "latest",
98
+ "vitest": "latest"
99
+ },
100
+ "files": [
101
+ "dist",
102
+ "README.md",
103
+ "LICENSE"
104
+ ],
105
+ "dependencies": {
106
+ "drizzle-orm": "^0.45.2",
107
+ "mimetext": "^3.0.28"
108
+ },
109
+ "scripts": {
110
+ "build": "vp pack",
111
+ "dev": "vp pack --watch",
112
+ "test": "vitest run",
113
+ "test:watch": "vitest"
114
+ }
115
+ }