@openinary/core 1.1.0

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 (242) hide show
  1. package/LICENSE +661 -0
  2. package/dist/config/deps.d.ts +14 -0
  3. package/dist/config/deps.d.ts.map +1 -0
  4. package/dist/config/deps.js +2 -0
  5. package/dist/config/deps.js.map +1 -0
  6. package/dist/index.d.ts +27 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +28 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/routes/authenticated.d.ts +4 -0
  11. package/dist/routes/authenticated.d.ts.map +1 -0
  12. package/dist/routes/authenticated.js +130 -0
  13. package/dist/routes/authenticated.js.map +1 -0
  14. package/dist/routes/download-folder.d.ts +4 -0
  15. package/dist/routes/download-folder.d.ts.map +1 -0
  16. package/dist/routes/download-folder.js +101 -0
  17. package/dist/routes/download-folder.js.map +1 -0
  18. package/dist/routes/download-zip.d.ts +15 -0
  19. package/dist/routes/download-zip.d.ts.map +1 -0
  20. package/dist/routes/download-zip.js +147 -0
  21. package/dist/routes/download-zip.js.map +1 -0
  22. package/dist/routes/download.d.ts +4 -0
  23. package/dist/routes/download.d.ts.map +1 -0
  24. package/dist/routes/download.js +83 -0
  25. package/dist/routes/download.js.map +1 -0
  26. package/dist/routes/invalidate.d.ts +4 -0
  27. package/dist/routes/invalidate.d.ts.map +1 -0
  28. package/dist/routes/invalidate.js +114 -0
  29. package/dist/routes/invalidate.js.map +1 -0
  30. package/dist/routes/queue-events.d.ts +4 -0
  31. package/dist/routes/queue-events.d.ts.map +1 -0
  32. package/dist/routes/queue-events.js +125 -0
  33. package/dist/routes/queue-events.js.map +1 -0
  34. package/dist/routes/queue.d.ts +4 -0
  35. package/dist/routes/queue.d.ts.map +1 -0
  36. package/dist/routes/queue.js +130 -0
  37. package/dist/routes/queue.js.map +1 -0
  38. package/dist/routes/storage.d.ts +4 -0
  39. package/dist/routes/storage.d.ts.map +1 -0
  40. package/dist/routes/storage.js +694 -0
  41. package/dist/routes/storage.js.map +1 -0
  42. package/dist/routes/transform-helpers.d.ts +62 -0
  43. package/dist/routes/transform-helpers.d.ts.map +1 -0
  44. package/dist/routes/transform-helpers.js +372 -0
  45. package/dist/routes/transform-helpers.js.map +1 -0
  46. package/dist/routes/transform.d.ts +4 -0
  47. package/dist/routes/transform.d.ts.map +1 -0
  48. package/dist/routes/transform.js +65 -0
  49. package/dist/routes/transform.js.map +1 -0
  50. package/dist/routes/video-status.d.ts +4 -0
  51. package/dist/routes/video-status.d.ts.map +1 -0
  52. package/dist/routes/video-status.js +208 -0
  53. package/dist/routes/video-status.js.map +1 -0
  54. package/dist/services/transform.service.d.ts +71 -0
  55. package/dist/services/transform.service.d.ts.map +1 -0
  56. package/dist/services/transform.service.js +362 -0
  57. package/dist/services/transform.service.js.map +1 -0
  58. package/dist/types.d.ts +82 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/asset-deletion.d.ts +16 -0
  63. package/dist/utils/asset-deletion.d.ts.map +1 -0
  64. package/dist/utils/asset-deletion.js +131 -0
  65. package/dist/utils/asset-deletion.js.map +1 -0
  66. package/dist/utils/cache.d.ts +44 -0
  67. package/dist/utils/cache.d.ts.map +1 -0
  68. package/dist/utils/cache.js +248 -0
  69. package/dist/utils/cache.js.map +1 -0
  70. package/dist/utils/get-unique-file-path.d.ts +8 -0
  71. package/dist/utils/get-unique-file-path.d.ts.map +1 -0
  72. package/dist/utils/get-unique-file-path.js +26 -0
  73. package/dist/utils/get-unique-file-path.js.map +1 -0
  74. package/dist/utils/image/aspect-ratio.d.ts +7 -0
  75. package/dist/utils/image/aspect-ratio.d.ts.map +1 -0
  76. package/dist/utils/image/aspect-ratio.js +40 -0
  77. package/dist/utils/image/aspect-ratio.js.map +1 -0
  78. package/dist/utils/image/background.d.ts +6 -0
  79. package/dist/utils/image/background.d.ts.map +1 -0
  80. package/dist/utils/image/background.js +22 -0
  81. package/dist/utils/image/background.js.map +1 -0
  82. package/dist/utils/image/compression.d.ts +44 -0
  83. package/dist/utils/image/compression.d.ts.map +1 -0
  84. package/dist/utils/image/compression.js +345 -0
  85. package/dist/utils/image/compression.js.map +1 -0
  86. package/dist/utils/image/gravity.d.ts +6 -0
  87. package/dist/utils/image/gravity.d.ts.map +1 -0
  88. package/dist/utils/image/gravity.js +24 -0
  89. package/dist/utils/image/gravity.js.map +1 -0
  90. package/dist/utils/image/index.d.ts +8 -0
  91. package/dist/utils/image/index.d.ts.map +1 -0
  92. package/dist/utils/image/index.js +77 -0
  93. package/dist/utils/image/index.js.map +1 -0
  94. package/dist/utils/image/param-registry.d.ts +63 -0
  95. package/dist/utils/image/param-registry.d.ts.map +1 -0
  96. package/dist/utils/image/param-registry.js +196 -0
  97. package/dist/utils/image/param-registry.js.map +1 -0
  98. package/dist/utils/image/quality.d.ts +8 -0
  99. package/dist/utils/image/quality.d.ts.map +1 -0
  100. package/dist/utils/image/quality.js +9 -0
  101. package/dist/utils/image/quality.js.map +1 -0
  102. package/dist/utils/image/resize.d.ts +7 -0
  103. package/dist/utils/image/resize.d.ts.map +1 -0
  104. package/dist/utils/image/resize.js +62 -0
  105. package/dist/utils/image/resize.js.map +1 -0
  106. package/dist/utils/image/rotation.d.ts +6 -0
  107. package/dist/utils/image/rotation.d.ts.map +1 -0
  108. package/dist/utils/image/rotation.js +20 -0
  109. package/dist/utils/image/rotation.js.map +1 -0
  110. package/dist/utils/image/round-corners.d.ts +12 -0
  111. package/dist/utils/image/round-corners.d.ts.map +1 -0
  112. package/dist/utils/image/round-corners.js +102 -0
  113. package/dist/utils/image/round-corners.js.map +1 -0
  114. package/dist/utils/image/types.d.ts +2 -0
  115. package/dist/utils/image/types.d.ts.map +1 -0
  116. package/dist/utils/image/types.js +2 -0
  117. package/dist/utils/image/types.js.map +1 -0
  118. package/dist/utils/logger.d.ts +21 -0
  119. package/dist/utils/logger.d.ts.map +1 -0
  120. package/dist/utils/logger.js +82 -0
  121. package/dist/utils/logger.js.map +1 -0
  122. package/dist/utils/parser.d.ts +8 -0
  123. package/dist/utils/parser.d.ts.map +1 -0
  124. package/dist/utils/parser.js +226 -0
  125. package/dist/utils/parser.js.map +1 -0
  126. package/dist/utils/signature.d.ts +35 -0
  127. package/dist/utils/signature.d.ts.map +1 -0
  128. package/dist/utils/signature.js +100 -0
  129. package/dist/utils/signature.js.map +1 -0
  130. package/dist/utils/storage/cache.d.ts +41 -0
  131. package/dist/utils/storage/cache.d.ts.map +1 -0
  132. package/dist/utils/storage/cache.js +90 -0
  133. package/dist/utils/storage/cache.js.map +1 -0
  134. package/dist/utils/storage/cloud-storage.d.ts +188 -0
  135. package/dist/utils/storage/cloud-storage.d.ts.map +1 -0
  136. package/dist/utils/storage/cloud-storage.js +514 -0
  137. package/dist/utils/storage/cloud-storage.js.map +1 -0
  138. package/dist/utils/storage/factory.d.ts +10 -0
  139. package/dist/utils/storage/factory.d.ts.map +1 -0
  140. package/dist/utils/storage/factory.js +17 -0
  141. package/dist/utils/storage/factory.js.map +1 -0
  142. package/dist/utils/storage/index.d.ts +7 -0
  143. package/dist/utils/storage/index.d.ts.map +1 -0
  144. package/dist/utils/storage/index.js +8 -0
  145. package/dist/utils/storage/index.js.map +1 -0
  146. package/dist/utils/storage/key-generator.d.ts +11 -0
  147. package/dist/utils/storage/key-generator.d.ts.map +1 -0
  148. package/dist/utils/storage/key-generator.js +23 -0
  149. package/dist/utils/storage/key-generator.js.map +1 -0
  150. package/dist/utils/storage/listing-cache.d.ts +14 -0
  151. package/dist/utils/storage/listing-cache.d.ts.map +1 -0
  152. package/dist/utils/storage/listing-cache.js +29 -0
  153. package/dist/utils/storage/listing-cache.js.map +1 -0
  154. package/dist/utils/storage/s3-client.d.ts +97 -0
  155. package/dist/utils/storage/s3-client.d.ts.map +1 -0
  156. package/dist/utils/storage/s3-client.js +297 -0
  157. package/dist/utils/storage/s3-client.js.map +1 -0
  158. package/dist/utils/storage/stats-tracker.d.ts +45 -0
  159. package/dist/utils/storage/stats-tracker.d.ts.map +1 -0
  160. package/dist/utils/storage/stats-tracker.js +97 -0
  161. package/dist/utils/storage/stats-tracker.js.map +1 -0
  162. package/dist/utils/storage/types.d.ts +2 -0
  163. package/dist/utils/storage/types.d.ts.map +1 -0
  164. package/dist/utils/storage/types.js +2 -0
  165. package/dist/utils/storage/types.js.map +1 -0
  166. package/dist/utils/storage-level.d.ts +66 -0
  167. package/dist/utils/storage-level.d.ts.map +1 -0
  168. package/dist/utils/storage-level.js +106 -0
  169. package/dist/utils/storage-level.js.map +1 -0
  170. package/dist/utils/storage-tree.d.ts +30 -0
  171. package/dist/utils/storage-tree.d.ts.map +1 -0
  172. package/dist/utils/storage-tree.js +81 -0
  173. package/dist/utils/storage-tree.js.map +1 -0
  174. package/dist/utils/upload-signature.d.ts +25 -0
  175. package/dist/utils/upload-signature.d.ts.map +1 -0
  176. package/dist/utils/upload-signature.js +56 -0
  177. package/dist/utils/upload-signature.js.map +1 -0
  178. package/dist/utils/video/auto-downscale.d.ts +12 -0
  179. package/dist/utils/video/auto-downscale.d.ts.map +1 -0
  180. package/dist/utils/video/auto-downscale.js +29 -0
  181. package/dist/utils/video/auto-downscale.js.map +1 -0
  182. package/dist/utils/video/command-builder.d.ts +22 -0
  183. package/dist/utils/video/command-builder.d.ts.map +1 -0
  184. package/dist/utils/video/command-builder.js +79 -0
  185. package/dist/utils/video/command-builder.js.map +1 -0
  186. package/dist/utils/video/config.d.ts +51 -0
  187. package/dist/utils/video/config.d.ts.map +1 -0
  188. package/dist/utils/video/config.js +170 -0
  189. package/dist/utils/video/config.js.map +1 -0
  190. package/dist/utils/video/format.d.ts +22 -0
  191. package/dist/utils/video/format.d.ts.map +1 -0
  192. package/dist/utils/video/format.js +64 -0
  193. package/dist/utils/video/format.js.map +1 -0
  194. package/dist/utils/video/index.d.ts +25 -0
  195. package/dist/utils/video/index.d.ts.map +1 -0
  196. package/dist/utils/video/index.js +104 -0
  197. package/dist/utils/video/index.js.map +1 -0
  198. package/dist/utils/video/param-registry.d.ts +64 -0
  199. package/dist/utils/video/param-registry.d.ts.map +1 -0
  200. package/dist/utils/video/param-registry.js +201 -0
  201. package/dist/utils/video/param-registry.js.map +1 -0
  202. package/dist/utils/video/quality.d.ts +12 -0
  203. package/dist/utils/video/quality.d.ts.map +1 -0
  204. package/dist/utils/video/quality.js +45 -0
  205. package/dist/utils/video/quality.js.map +1 -0
  206. package/dist/utils/video/queue-store.d.ts +51 -0
  207. package/dist/utils/video/queue-store.d.ts.map +1 -0
  208. package/dist/utils/video/queue-store.js +2 -0
  209. package/dist/utils/video/queue-store.js.map +1 -0
  210. package/dist/utils/video/resize.d.ts +7 -0
  211. package/dist/utils/video/resize.d.ts.map +1 -0
  212. package/dist/utils/video/resize.js +40 -0
  213. package/dist/utils/video/resize.js.map +1 -0
  214. package/dist/utils/video/sqlite-video-job-store.d.ts +29 -0
  215. package/dist/utils/video/sqlite-video-job-store.d.ts.map +1 -0
  216. package/dist/utils/video/sqlite-video-job-store.js +320 -0
  217. package/dist/utils/video/sqlite-video-job-store.js.map +1 -0
  218. package/dist/utils/video/thumbnail.d.ts +7 -0
  219. package/dist/utils/video/thumbnail.d.ts.map +1 -0
  220. package/dist/utils/video/thumbnail.js +22 -0
  221. package/dist/utils/video/thumbnail.js.map +1 -0
  222. package/dist/utils/video/trim.d.ts +7 -0
  223. package/dist/utils/video/trim.d.ts.map +1 -0
  224. package/dist/utils/video/trim.js +36 -0
  225. package/dist/utils/video/trim.js.map +1 -0
  226. package/dist/utils/video/types.d.ts +19 -0
  227. package/dist/utils/video/types.d.ts.map +1 -0
  228. package/dist/utils/video/types.js +2 -0
  229. package/dist/utils/video/types.js.map +1 -0
  230. package/dist/utils/video/video-info.d.ts +25 -0
  231. package/dist/utils/video/video-info.d.ts.map +1 -0
  232. package/dist/utils/video/video-info.js +58 -0
  233. package/dist/utils/video/video-info.js.map +1 -0
  234. package/dist/utils/video/video-worker.d.ts +52 -0
  235. package/dist/utils/video/video-worker.d.ts.map +1 -0
  236. package/dist/utils/video/video-worker.js +192 -0
  237. package/dist/utils/video/video-worker.js.map +1 -0
  238. package/dist/utils/video-job-queue.d.ts +62 -0
  239. package/dist/utils/video-job-queue.d.ts.map +1 -0
  240. package/dist/utils/video-job-queue.js +122 -0
  241. package/dist/utils/video-job-queue.js.map +1 -0
  242. package/package.json +51 -0
@@ -0,0 +1,82 @@
1
+ import pino from "pino";
2
+ /**
3
+ * Get log level from environment variable
4
+ * Defaults to 'info' in production, 'debug' in development
5
+ */
6
+ function getLogLevel() {
7
+ const envLevel = process.env.LOG_LEVEL?.toLowerCase();
8
+ const nodeEnv = process.env.NODE_ENV?.toLowerCase() || "development";
9
+ if (envLevel) {
10
+ return envLevel;
11
+ }
12
+ // Default levels by environment
13
+ if (nodeEnv === "production") {
14
+ return "info";
15
+ }
16
+ return "debug";
17
+ }
18
+ /**
19
+ * Serialize any error value into a plain object that survives JSON.stringify.
20
+ *
21
+ * Problem: Error.message / Error.stack / Error.name are non-enumerable, so
22
+ * JSON.stringify(new Error('x')) === '{}'. pino's transport worker thread
23
+ * uses structuredClone / JSON serialization internally, which drops those
24
+ * properties even when stdSerializers are configured.
25
+ *
26
+ * Solution: always call serializeError() before passing an error to pino so
27
+ * the resulting object only contains plain enumerable string fields.
28
+ */
29
+ export function serializeError(error) {
30
+ if (error === null || error === undefined) {
31
+ return { message: String(error) };
32
+ }
33
+ if (error instanceof Error) {
34
+ const obj = {
35
+ type: error.constructor?.name ?? "Error",
36
+ message: error.message,
37
+ stack: error.stack,
38
+ };
39
+ // Capture any extra enumerable properties (e.g. `code` on fs errors)
40
+ for (const key of Object.keys(error)) {
41
+ if (!(key in obj)) {
42
+ obj[key] = error[key];
43
+ }
44
+ }
45
+ return obj;
46
+ }
47
+ if (typeof error === "object") {
48
+ return { ...error };
49
+ }
50
+ return { message: String(error) };
51
+ }
52
+ /**
53
+ * Create a structured logger instance
54
+ * - In development: pretty printed logs with colors
55
+ * - In production: JSON structured logs
56
+ */
57
+ const logger = pino({
58
+ level: getLogLevel(),
59
+ // stdSerializers covers the JSON transport (production).
60
+ // In development (pino-pretty worker thread) we also call serializeError()
61
+ // explicitly at every log site to guarantee visibility.
62
+ serializers: {
63
+ error: pino.stdSerializers.err,
64
+ err: pino.stdSerializers.err,
65
+ },
66
+ transport: process.env.NODE_ENV === "production"
67
+ ? undefined // Use default JSON output in production
68
+ : {
69
+ target: "pino-pretty",
70
+ options: {
71
+ colorize: true,
72
+ translateTime: "HH:MM:ss Z",
73
+ ignore: "pid,hostname",
74
+ singleLine: false,
75
+ },
76
+ },
77
+ base: {
78
+ env: process.env.NODE_ENV || "development",
79
+ },
80
+ });
81
+ export default logger;
82
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;GAGG;AACH,SAAS,WAAW;IAClB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,aAAa,CAAC;IAErE,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,gCAAgC;IAChC,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACpC,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,GAAG,GAA4B;YACnC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,IAAI,IAAI,OAAO;YACxC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC;QACF,qEAAqE;QACrE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;gBAClB,GAAG,CAAC,GAAG,CAAC,GAAI,KAAa,CAAC,GAAG,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,GAAI,KAAgB,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,WAAW,EAAE;IACpB,yDAAyD;IACzD,2EAA2E;IAC3E,wDAAwD;IACxD,WAAW,EAAE;QACX,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG;QAC9B,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG;KAC7B;IACD,SAAS,EACP,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QACnC,CAAC,CAAC,SAAS,CAAC,wCAAwC;QACpD,CAAC,CAAC;YACE,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE;gBACP,QAAQ,EAAE,IAAI;gBACd,aAAa,EAAE,YAAY;gBAC3B,MAAM,EAAE,cAAc;gBACtB,UAAU,EAAE,KAAK;aAClB;SACF;IACP,IAAI,EAAE;QACJ,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;KAC3C;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const parseParams: (path: string) => Record<string, string>;
2
+ /**
3
+ * Check if a path segment is a transformation definition (e.g. "w_300,h_300,c_fill").
4
+ * Every comma-separated part must be a valid key_value pair to avoid false positives
5
+ * on folder names like "w_photos", "f_family", or "bg_images".
6
+ */
7
+ export declare const isTransformSegment: (segment: string) => boolean;
8
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/utils/parser.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,2BAevC,CAAC;AAkCF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,OAIpD,CAAC"}
@@ -0,0 +1,226 @@
1
+ export const parseParams = (path) => {
2
+ const segments = path.split("/");
3
+ const params = {};
4
+ const tIndex = segments.indexOf("t");
5
+ if (tIndex !== -1 && segments.length > tIndex + 1) {
6
+ const transformSegment = segments[tIndex + 1];
7
+ if (isTransformSegment(transformSegment)) {
8
+ const transformParams = parseTransform(transformSegment);
9
+ Object.assign(params, transformParams);
10
+ }
11
+ }
12
+ return params;
13
+ };
14
+ /**
15
+ * Valid values for each transformation key.
16
+ * Used by isTransformSegment to reject folder names that happen to start with a known key.
17
+ */
18
+ const TRANSFORM_VALUE_PATTERNS = {
19
+ w: /^\d+$|^auto$/,
20
+ h: /^\d+$|^auto$/,
21
+ c: /^(fill|lfill|fill_pad|fit|limit|mfit|scale|crop|thumb|pad|lpad)$/,
22
+ g: /^(center|c|north(?:_center)?|n|south(?:_center)?|s|east|e|west|w|faces?(?:_center)?|auto)$/,
23
+ q: /^\d+$|^auto$/,
24
+ f: /^(webp|jpe?g|png|avif|gif|psd|mp4|webm|mov|avi|mp3|wav|ogg|pdf|auto)$/,
25
+ a: /^-?\d+$|^auto$/,
26
+ ar: /^\d+:\d+$|^\d+(?:\.\d+)?$/,
27
+ b: /^(transparent|white|black|rgb:[0-9a-fA-F]{3,8}|#?[0-9a-fA-F]{3,8})$/,
28
+ bg: /^(transparent|white|black|rgb:[0-9a-fA-F]{3,8}|#?[0-9a-fA-F]{3,8})$/,
29
+ so: /^\d+(?:\.\d+)?$/,
30
+ eo: /^\d+(?:\.\d+)?$/,
31
+ t: /^(true|1|\d+)$/,
32
+ tt: /^\d+(?:\.\d+)?$/,
33
+ r: /^max$|^\d+(?::\d+){0,3}$/,
34
+ };
35
+ const isValidTransformPair = (part) => {
36
+ const underscoreIndex = part.indexOf("_");
37
+ if (underscoreIndex === -1)
38
+ return false;
39
+ const key = part.substring(0, underscoreIndex);
40
+ const value = part.substring(underscoreIndex + 1);
41
+ if (!value)
42
+ return false;
43
+ const pattern = TRANSFORM_VALUE_PATTERNS[key];
44
+ return pattern !== undefined && pattern.test(value);
45
+ };
46
+ /**
47
+ * Check if a path segment is a transformation definition (e.g. "w_300,h_300,c_fill").
48
+ * Every comma-separated part must be a valid key_value pair to avoid false positives
49
+ * on folder names like "w_photos", "f_family", or "bg_images".
50
+ */
51
+ export const isTransformSegment = (segment) => {
52
+ if (!segment || segment.includes("."))
53
+ return false;
54
+ const parts = segment.split(",").filter(Boolean);
55
+ return parts.length > 0 && parts.every(isValidTransformPair);
56
+ };
57
+ /**
58
+ * Parse a single transformation segment into our
59
+ * internal parameter map.
60
+ */
61
+ const parseTransform = (segment) => {
62
+ const params = {};
63
+ const parts = segment.split(",");
64
+ let width;
65
+ let height;
66
+ let cropMode;
67
+ let startOffset;
68
+ let endOffset;
69
+ for (const part of parts) {
70
+ if (!part)
71
+ continue;
72
+ const underscoreIndex = part.indexOf("_");
73
+ if (underscoreIndex === -1)
74
+ continue;
75
+ const key = part.substring(0, underscoreIndex);
76
+ const value = part.substring(underscoreIndex + 1);
77
+ switch (key) {
78
+ case "w":
79
+ width = value;
80
+ break;
81
+ case "h":
82
+ height = value;
83
+ break;
84
+ case "c":
85
+ cropMode = mapCropMode(value);
86
+ break;
87
+ case "g":
88
+ params.gravity = mapGravity(value);
89
+ break;
90
+ case "q":
91
+ // Quality (e.g. q_80, q_auto)
92
+ params.quality = value;
93
+ break;
94
+ case "f":
95
+ // Map directly, validation happens downstream
96
+ params.format = value;
97
+ break;
98
+ case "a":
99
+ // Angle of rotation (e.g. a_90, a_auto)
100
+ params.rotate = value;
101
+ break;
102
+ case "ar":
103
+ // Aspect ratio, usually like "16:9" already
104
+ params.aspect = value;
105
+ break;
106
+ case "b":
107
+ case "bg":
108
+ params.background = mapBackground(value);
109
+ break;
110
+ case "so":
111
+ // Start offset (in seconds) for video/audio
112
+ startOffset = value;
113
+ break;
114
+ case "eo":
115
+ // End offset (in seconds) for video/audio
116
+ endOffset = value;
117
+ break;
118
+ case "t":
119
+ // FIX H12: Parse thumbnail parameter
120
+ params.thumbnail = value;
121
+ break;
122
+ case "tt":
123
+ // FIX H12: Parse thumbnail time parameter
124
+ params.thumbnailTime = value;
125
+ break;
126
+ case "r":
127
+ params.radius = value;
128
+ break;
129
+ default:
130
+ // Ignore unsupported/unknown directives for now
131
+ break;
132
+ }
133
+ }
134
+ // Set width and/or height independently
135
+ if (width) {
136
+ params.width = width;
137
+ }
138
+ if (height) {
139
+ params.height = height;
140
+ }
141
+ // If both are present, also set resize for backwards compatibility
142
+ if (width && height) {
143
+ params.resize = `${width}x${height}`;
144
+ }
145
+ if (cropMode) {
146
+ params.crop = cropMode;
147
+ }
148
+ if (startOffset) {
149
+ params.startOffset = startOffset;
150
+ }
151
+ if (endOffset) {
152
+ params.endOffset = endOffset;
153
+ }
154
+ return params;
155
+ };
156
+ const mapCropMode = (value) => {
157
+ switch (value) {
158
+ case "fill":
159
+ case "lfill":
160
+ case "fill_pad":
161
+ return "fill";
162
+ case "fit":
163
+ case "limit":
164
+ case "mfit":
165
+ return "fit";
166
+ case "scale":
167
+ return "scale";
168
+ case "crop":
169
+ case "thumb":
170
+ return "crop";
171
+ case "pad":
172
+ case "lpad":
173
+ return "pad";
174
+ default:
175
+ return "fill";
176
+ }
177
+ };
178
+ const mapGravity = (value) => {
179
+ switch (value) {
180
+ case "center":
181
+ case "c":
182
+ return "center";
183
+ case "north":
184
+ case "north_center":
185
+ case "n":
186
+ return "north";
187
+ case "south":
188
+ case "south_center":
189
+ case "s":
190
+ return "south";
191
+ case "east":
192
+ case "e":
193
+ return "east";
194
+ case "west":
195
+ case "w":
196
+ return "west";
197
+ case "face":
198
+ case "faces":
199
+ case "face_center":
200
+ return "face";
201
+ case "auto":
202
+ return "auto";
203
+ default:
204
+ return "center";
205
+ }
206
+ };
207
+ const mapBackground = (value) => {
208
+ if (value.startsWith("rgb:")) {
209
+ const hex = value.substring("rgb:".length);
210
+ return `#${hex}`;
211
+ }
212
+ switch (value) {
213
+ case "transparent":
214
+ return "transparent";
215
+ case "white":
216
+ return "#ffffff";
217
+ case "black":
218
+ return "#000000";
219
+ default:
220
+ // Assume it's already a hex-like string
221
+ if (value.startsWith("#"))
222
+ return value;
223
+ return `#${value}`;
224
+ }
225
+ };
226
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/utils/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Signature utility for authenticated delivery URLs
3
+ * Uses HMAC-SHA256 for cryptographically secure signatures
4
+ */
5
+ export declare const SIGNATURE_LENGTH = 16;
6
+ /**
7
+ * Sanitizes a file path to prevent path traversal attacks
8
+ * @param filePath - The file path to sanitize
9
+ * @returns Sanitized file path
10
+ */
11
+ export declare function sanitizeFilePath(filePath: string): string;
12
+ /**
13
+ * Generates a cryptographically secure signature for authenticated URLs
14
+ * @param transformations - The transformation string (e.g., "c_fill,w_300,h_250")
15
+ * @param filePath - The file path
16
+ * @param secret - The API secret key
17
+ * @returns The signature (first SIGNATURE_LENGTH characters of HMAC-SHA256)
18
+ */
19
+ export declare function generateSignature(transformations: string, filePath: string, secret: string): string;
20
+ /**
21
+ * Verifies a signature using timing-safe comparison
22
+ * @param providedSignature - The signature provided in the URL
23
+ * @param transformations - The transformation string
24
+ * @param filePath - The file path
25
+ * @param secret - The API secret key
26
+ * @returns True if the signature is valid, false otherwise
27
+ */
28
+ export declare function verifySignature(providedSignature: string, transformations: string, filePath: string, secret: string): boolean;
29
+ /**
30
+ * Validates that the API_SECRET is properly configured
31
+ * @param secret - The API secret to validate
32
+ * @throws Error if the secret is not configured or is too weak
33
+ */
34
+ export declare function validateApiSecret(secret: string | undefined): void;
35
+ //# sourceMappingURL=signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/utils/signature.ts"],"names":[],"mappings":"AAIA;;;GAGG;AAEH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAazD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,CAmBR;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAiCT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAiBlE"}
@@ -0,0 +1,100 @@
1
+ import crypto from 'crypto';
2
+ import path from 'path';
3
+ import logger, { serializeError } from './logger.js';
4
+ /**
5
+ * Signature utility for authenticated delivery URLs
6
+ * Uses HMAC-SHA256 for cryptographically secure signatures
7
+ */
8
+ export const SIGNATURE_LENGTH = 16; // 64 bits of entropy
9
+ /**
10
+ * Sanitizes a file path to prevent path traversal attacks
11
+ * @param filePath - The file path to sanitize
12
+ * @returns Sanitized file path
13
+ */
14
+ export function sanitizeFilePath(filePath) {
15
+ // Normalize the path to resolve any '..' or '.' segments
16
+ const normalized = path.normalize(filePath);
17
+ // Remove any leading path traversal sequences
18
+ const sanitized = normalized.replace(/^(\.\.[/\\])+/, '');
19
+ // Additional check: reject paths that still contain '..'
20
+ if (sanitized.includes('..')) {
21
+ throw new Error('Invalid file path: path traversal detected');
22
+ }
23
+ return sanitized;
24
+ }
25
+ /**
26
+ * Generates a cryptographically secure signature for authenticated URLs
27
+ * @param transformations - The transformation string (e.g., "c_fill,w_300,h_250")
28
+ * @param filePath - The file path
29
+ * @param secret - The API secret key
30
+ * @returns The signature (first SIGNATURE_LENGTH characters of HMAC-SHA256)
31
+ */
32
+ export function generateSignature(transformations, filePath, secret) {
33
+ if (!secret) {
34
+ throw new Error('API_SECRET is required for signature generation');
35
+ }
36
+ // Sanitize the file path to prevent path traversal
37
+ const sanitizedPath = sanitizeFilePath(filePath);
38
+ // Create the string to sign
39
+ const stringToSign = transformations
40
+ ? `${transformations}/${sanitizedPath}`
41
+ : sanitizedPath;
42
+ // Generate HMAC-SHA256 signature
43
+ const hmac = crypto.createHmac('sha256', secret);
44
+ hmac.update(stringToSign);
45
+ const signature = hmac.digest('hex').substring(0, SIGNATURE_LENGTH);
46
+ return signature;
47
+ }
48
+ /**
49
+ * Verifies a signature using timing-safe comparison
50
+ * @param providedSignature - The signature provided in the URL
51
+ * @param transformations - The transformation string
52
+ * @param filePath - The file path
53
+ * @param secret - The API secret key
54
+ * @returns True if the signature is valid, false otherwise
55
+ */
56
+ export function verifySignature(providedSignature, transformations, filePath, secret) {
57
+ if (!secret) {
58
+ logger.error('API_SECRET is not configured for signature verification');
59
+ return false;
60
+ }
61
+ // Validate signature length
62
+ if (providedSignature.length !== SIGNATURE_LENGTH) {
63
+ logger.warn({ providedLength: providedSignature.length, expectedLength: SIGNATURE_LENGTH }, 'Invalid signature length');
64
+ return false;
65
+ }
66
+ try {
67
+ // Generate the expected signature
68
+ const expectedSignature = generateSignature(transformations, filePath, secret);
69
+ // Use timing-safe comparison to prevent timing attacks
70
+ const providedBuffer = Buffer.from(providedSignature, 'utf8');
71
+ const expectedBuffer = Buffer.from(expectedSignature, 'utf8');
72
+ // Both buffers must have the same length for timingSafeEqual
73
+ if (providedBuffer.length !== expectedBuffer.length) {
74
+ return false;
75
+ }
76
+ return crypto.timingSafeEqual(providedBuffer, expectedBuffer);
77
+ }
78
+ catch (error) {
79
+ logger.error({ error: serializeError(error) }, 'Error during signature verification');
80
+ return false;
81
+ }
82
+ }
83
+ /**
84
+ * Validates that the API_SECRET is properly configured
85
+ * @param secret - The API secret to validate
86
+ * @throws Error if the secret is not configured or is too weak
87
+ */
88
+ export function validateApiSecret(secret) {
89
+ if (!secret) {
90
+ throw new Error('API_SECRET environment variable is required for authenticated delivery URLs. ' +
91
+ 'Please set a strong secret key in your environment configuration.');
92
+ }
93
+ // Ensure the secret is reasonably strong (at least 16 characters)
94
+ if (secret.length < 16) {
95
+ throw new Error('API_SECRET must be at least 16 characters long for adequate security. ' +
96
+ `Current length: ${secret.length}`);
97
+ }
98
+ logger.info('API_SECRET validation passed');
99
+ }
100
+ //# sourceMappingURL=signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/utils/signature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { CacheEntry } from './types';
2
+ export declare class StorageCache {
3
+ private existsCache;
4
+ private readonly CACHE_TTL;
5
+ private readonly NEGATIVE_CACHE_TTL;
6
+ private readonly MAX_CACHE_SIZE;
7
+ constructor();
8
+ /**
9
+ * Gets a cache entry
10
+ */
11
+ get(key: string): CacheEntry | undefined;
12
+ /**
13
+ * Sets a cache entry with size management
14
+ */
15
+ set(key: string, entry: CacheEntry): void;
16
+ /**
17
+ * Deletes a cache entry
18
+ */
19
+ delete(key: string): void;
20
+ /**
21
+ * Checks if cache hit is valid
22
+ */
23
+ isValidHit(key: string): boolean;
24
+ /**
25
+ * Gets cache size
26
+ */
27
+ get size(): number;
28
+ /**
29
+ * Gets all cache keys
30
+ */
31
+ getAllKeys(): string[];
32
+ /**
33
+ * Clears all cache entries
34
+ */
35
+ clear(): void;
36
+ /**
37
+ * Automatic cache cleanup (LRU + TTL)
38
+ */
39
+ private cleanup;
40
+ }
41
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/utils/storage/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAa;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;;IAOxC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAkBxC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IASzC;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB;;OAEG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKhC;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,UAAU,IAAI,MAAM,EAAE;IAItB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,CAAC,OAAO;CAuBhB"}
@@ -0,0 +1,90 @@
1
+ export class StorageCache {
2
+ existsCache = new Map();
3
+ CACHE_TTL = 60 * 1000; // 1 minute (reduced from 5 minutes to detect deletions faster)
4
+ NEGATIVE_CACHE_TTL = 30 * 1000; // 30 seconds for "not found"
5
+ MAX_CACHE_SIZE = 10000; // Maximum 10k entries
6
+ constructor() {
7
+ // Automatic cache cleanup every 10 minutes
8
+ setInterval(() => this.cleanup(), 10 * 60 * 1000);
9
+ }
10
+ /**
11
+ * Gets a cache entry
12
+ */
13
+ get(key) {
14
+ const cached = this.existsCache.get(key);
15
+ if (cached) {
16
+ const age = Date.now() - cached.timestamp;
17
+ const ttl = cached.exists ? this.CACHE_TTL : this.NEGATIVE_CACHE_TTL;
18
+ if (age < ttl) {
19
+ return cached;
20
+ }
21
+ // Remove expired entry
22
+ this.existsCache.delete(key);
23
+ }
24
+ return undefined;
25
+ }
26
+ /**
27
+ * Sets a cache entry with size management
28
+ */
29
+ set(key, entry) {
30
+ // Cleanup if cache becomes too large
31
+ if (this.existsCache.size >= this.MAX_CACHE_SIZE) {
32
+ this.cleanup();
33
+ }
34
+ this.existsCache.set(key, entry);
35
+ }
36
+ /**
37
+ * Deletes a cache entry
38
+ */
39
+ delete(key) {
40
+ this.existsCache.delete(key);
41
+ }
42
+ /**
43
+ * Checks if cache hit is valid
44
+ */
45
+ isValidHit(key) {
46
+ const cached = this.get(key);
47
+ return cached !== undefined;
48
+ }
49
+ /**
50
+ * Gets cache size
51
+ */
52
+ get size() {
53
+ return this.existsCache.size;
54
+ }
55
+ /**
56
+ * Gets all cache keys
57
+ */
58
+ getAllKeys() {
59
+ return Array.from(this.existsCache.keys());
60
+ }
61
+ /**
62
+ * Clears all cache entries
63
+ */
64
+ clear() {
65
+ this.existsCache.clear();
66
+ }
67
+ /**
68
+ * Automatic cache cleanup (LRU + TTL)
69
+ */
70
+ cleanup() {
71
+ const now = Date.now();
72
+ for (const [key, value] of this.existsCache.entries()) {
73
+ const age = now - value.timestamp;
74
+ const ttl = value.exists ? this.CACHE_TTL : this.NEGATIVE_CACHE_TTL;
75
+ if (age > ttl) {
76
+ this.existsCache.delete(key);
77
+ }
78
+ }
79
+ // If still too large, remove the oldest entries
80
+ if (this.existsCache.size > this.MAX_CACHE_SIZE * 0.8) {
81
+ const entries = Array.from(this.existsCache.entries())
82
+ .sort((a, b) => a[1].timestamp - b[1].timestamp);
83
+ const toRemove = entries.slice(0, Math.floor(entries.length * 0.2));
84
+ toRemove.forEach(([key]) => {
85
+ this.existsCache.delete(key);
86
+ });
87
+ }
88
+ }
89
+ }
90
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/utils/storage/cache.ts"],"names":[],"mappings":"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"}