@scpxl/nodejs-framework 1.0.17 → 1.0.19

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 (192) hide show
  1. package/README.md +29 -0
  2. package/dist/api-requester/api-requester.d.ts +27 -6
  3. package/dist/api-requester/api-requester.d.ts.map +1 -1
  4. package/dist/api-requester/api-requester.js +188 -26
  5. package/dist/api-requester/api-requester.js.map +2 -2
  6. package/dist/api-requester/index.d.ts +1 -0
  7. package/dist/api-requester/index.d.ts.map +1 -1
  8. package/dist/api-requester/index.js.map +1 -1
  9. package/dist/application/base-application.d.ts +15 -22
  10. package/dist/application/base-application.d.ts.map +1 -1
  11. package/dist/application/base-application.js +145 -111
  12. package/dist/application/base-application.js.map +2 -2
  13. package/dist/application/command-application.d.ts.map +1 -1
  14. package/dist/application/command-application.js +3 -4
  15. package/dist/application/command-application.js.map +2 -2
  16. package/dist/application/web-application.d.ts.map +1 -1
  17. package/dist/application/web-application.js +9 -2
  18. package/dist/application/web-application.js.map +2 -2
  19. package/dist/cache/manager.d.ts +87 -6
  20. package/dist/cache/manager.d.ts.map +1 -1
  21. package/dist/cache/manager.js +77 -30
  22. package/dist/cache/manager.js.map +2 -2
  23. package/dist/cluster/cluster-manager.d.ts.map +1 -1
  24. package/dist/cluster/cluster-manager.js +5 -8
  25. package/dist/cluster/cluster-manager.js.map +2 -2
  26. package/dist/config/env.d.ts +11 -0
  27. package/dist/config/env.d.ts.map +1 -0
  28. package/dist/config/env.js +103 -0
  29. package/dist/config/env.js.map +7 -0
  30. package/dist/config/index.d.ts +3 -0
  31. package/dist/config/index.d.ts.map +1 -0
  32. package/dist/config/index.js +3 -0
  33. package/dist/config/index.js.map +7 -0
  34. package/dist/config/schema.d.ts +408 -0
  35. package/dist/config/schema.d.ts.map +1 -0
  36. package/dist/config/schema.js +218 -0
  37. package/dist/config/schema.js.map +7 -0
  38. package/dist/database/dynamic-entity.d.ts.map +1 -1
  39. package/dist/database/dynamic-entity.js +6 -2
  40. package/dist/database/dynamic-entity.js.map +2 -2
  41. package/dist/database/instance.d.ts.map +1 -1
  42. package/dist/database/instance.js +0 -8
  43. package/dist/database/instance.js.map +2 -2
  44. package/dist/database/manager.d.ts.map +1 -1
  45. package/dist/database/manager.js +71 -9
  46. package/dist/database/manager.js.map +2 -2
  47. package/dist/error/error-reporter.d.ts +96 -0
  48. package/dist/error/error-reporter.d.ts.map +1 -0
  49. package/dist/error/error-reporter.js +228 -0
  50. package/dist/error/error-reporter.js.map +7 -0
  51. package/dist/error/error.interface.d.ts +126 -0
  52. package/dist/error/error.interface.d.ts.map +1 -0
  53. package/dist/error/error.interface.js +45 -0
  54. package/dist/error/error.interface.js.map +7 -0
  55. package/dist/error/framework-errors.d.ts +113 -0
  56. package/dist/error/framework-errors.d.ts.map +1 -0
  57. package/dist/error/framework-errors.js +176 -0
  58. package/dist/error/framework-errors.js.map +7 -0
  59. package/dist/error/index.d.ts +6 -0
  60. package/dist/error/index.d.ts.map +1 -0
  61. package/dist/error/index.js +34 -0
  62. package/dist/error/index.js.map +7 -0
  63. package/dist/event/manager.d.ts.map +1 -1
  64. package/dist/event/manager.js +2 -9
  65. package/dist/event/manager.js.map +2 -2
  66. package/dist/index.d.ts +5 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +5 -1
  69. package/dist/index.js.map +2 -2
  70. package/dist/lifecycle/exit.d.ts +2 -1
  71. package/dist/lifecycle/exit.d.ts.map +1 -1
  72. package/dist/lifecycle/exit.js +6 -0
  73. package/dist/lifecycle/exit.js.map +2 -2
  74. package/dist/lifecycle/index.d.ts +7 -0
  75. package/dist/lifecycle/index.d.ts.map +1 -0
  76. package/dist/lifecycle/index.js +12 -0
  77. package/dist/lifecycle/index.js.map +7 -0
  78. package/dist/lifecycle/lifecycle-manager.d.ts +55 -2
  79. package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -1
  80. package/dist/lifecycle/lifecycle-manager.js +233 -7
  81. package/dist/lifecycle/lifecycle-manager.js.map +3 -3
  82. package/dist/lifecycle/shutdown-controller.d.ts +15 -0
  83. package/dist/lifecycle/shutdown-controller.d.ts.map +1 -0
  84. package/dist/lifecycle/shutdown-controller.js +38 -0
  85. package/dist/lifecycle/shutdown-controller.js.map +7 -0
  86. package/dist/lifecycle/types.d.ts +28 -0
  87. package/dist/lifecycle/types.d.ts.map +1 -0
  88. package/dist/lifecycle/types.js +13 -0
  89. package/dist/lifecycle/types.js.map +7 -0
  90. package/dist/logger/logger.d.ts +2 -1
  91. package/dist/logger/logger.d.ts.map +1 -1
  92. package/dist/logger/logger.js +35 -11
  93. package/dist/logger/logger.js.map +2 -2
  94. package/dist/performance/cache-performance.d.ts +6 -0
  95. package/dist/performance/cache-performance.d.ts.map +1 -1
  96. package/dist/performance/cache-performance.js +16 -0
  97. package/dist/performance/cache-performance.js.map +2 -2
  98. package/dist/performance/index.d.ts +1 -0
  99. package/dist/performance/index.d.ts.map +1 -1
  100. package/dist/performance/index.js +1 -0
  101. package/dist/performance/index.js.map +2 -2
  102. package/dist/performance/performance-monitor.d.ts.map +1 -1
  103. package/dist/performance/performance-monitor.js +47 -18
  104. package/dist/performance/performance-monitor.js.map +2 -2
  105. package/dist/performance/performance-monitor.plugin.d.ts +24 -0
  106. package/dist/performance/performance-monitor.plugin.d.ts.map +1 -0
  107. package/dist/performance/performance-monitor.plugin.js +89 -0
  108. package/dist/performance/performance-monitor.plugin.js.map +7 -0
  109. package/dist/performance/webserver-performance.js +1 -1
  110. package/dist/performance/webserver-performance.js.map +2 -2
  111. package/dist/queue/manager.d.ts.map +1 -1
  112. package/dist/queue/manager.js +3 -10
  113. package/dist/queue/manager.js.map +2 -2
  114. package/dist/queue/worker.d.ts.map +1 -1
  115. package/dist/queue/worker.js +2 -2
  116. package/dist/queue/worker.js.map +2 -2
  117. package/dist/redis/manager.d.ts.map +1 -1
  118. package/dist/redis/manager.js +228 -33
  119. package/dist/redis/manager.js.map +2 -2
  120. package/dist/request-context/index.d.ts +3 -0
  121. package/dist/request-context/index.d.ts.map +1 -0
  122. package/dist/request-context/index.js +25 -0
  123. package/dist/request-context/index.js.map +7 -0
  124. package/dist/request-context/request-context.d.ts +108 -0
  125. package/dist/request-context/request-context.d.ts.map +1 -0
  126. package/dist/request-context/request-context.interface.d.ts +46 -0
  127. package/dist/request-context/request-context.interface.d.ts.map +1 -0
  128. package/dist/request-context/request-context.interface.js +1 -0
  129. package/dist/request-context/request-context.interface.js.map +7 -0
  130. package/dist/request-context/request-context.js +79 -0
  131. package/dist/request-context/request-context.js.map +7 -0
  132. package/dist/services/aws/s3.js +4 -6
  133. package/dist/services/aws/s3.js.map +2 -2
  134. package/dist/util/file.d.ts +13 -0
  135. package/dist/util/file.d.ts.map +1 -1
  136. package/dist/util/file.js +46 -9
  137. package/dist/util/file.js.map +2 -2
  138. package/dist/util/helper.d.ts +16 -1
  139. package/dist/util/helper.d.ts.map +1 -1
  140. package/dist/util/helper.js +19 -43
  141. package/dist/util/helper.js.map +2 -2
  142. package/dist/util/index.d.ts +1 -0
  143. package/dist/util/index.d.ts.map +1 -1
  144. package/dist/util/index.js +18 -16
  145. package/dist/util/index.js.map +2 -2
  146. package/dist/util/loader.d.ts.map +1 -1
  147. package/dist/util/loader.js +13 -2
  148. package/dist/util/loader.js.map +2 -2
  149. package/dist/util/os.d.ts.map +1 -1
  150. package/dist/util/os.js +8 -14
  151. package/dist/util/os.js.map +2 -2
  152. package/dist/util/time.d.ts +8 -2
  153. package/dist/util/time.d.ts.map +1 -1
  154. package/dist/util/time.js +12 -7
  155. package/dist/util/time.js.map +2 -2
  156. package/dist/util/timing.d.ts +36 -0
  157. package/dist/util/timing.d.ts.map +1 -0
  158. package/dist/util/timing.interface.d.ts +47 -0
  159. package/dist/util/timing.interface.d.ts.map +1 -0
  160. package/dist/util/timing.interface.js +1 -0
  161. package/dist/util/timing.interface.js.map +7 -0
  162. package/dist/util/timing.js +98 -0
  163. package/dist/util/timing.js.map +7 -0
  164. package/dist/util/url.js +1 -1
  165. package/dist/util/url.js.map +2 -2
  166. package/dist/webserver/controller/base.d.ts +3 -1
  167. package/dist/webserver/controller/base.d.ts.map +1 -1
  168. package/dist/webserver/controller/base.interface.d.ts +2 -0
  169. package/dist/webserver/controller/base.interface.d.ts.map +1 -1
  170. package/dist/webserver/controller/base.js +4 -1
  171. package/dist/webserver/controller/base.js.map +2 -2
  172. package/dist/webserver/controller/health.d.ts +8 -1
  173. package/dist/webserver/controller/health.d.ts.map +1 -1
  174. package/dist/webserver/controller/health.js +36 -22
  175. package/dist/webserver/controller/health.js.map +2 -2
  176. package/dist/webserver/webserver.d.ts +16 -2
  177. package/dist/webserver/webserver.d.ts.map +1 -1
  178. package/dist/webserver/webserver.interface.d.ts +37 -0
  179. package/dist/webserver/webserver.interface.d.ts.map +1 -1
  180. package/dist/webserver/webserver.interface.js.map +2 -2
  181. package/dist/webserver/webserver.js +117 -20
  182. package/dist/webserver/webserver.js.map +2 -2
  183. package/dist/websocket/controllers/server/system.d.ts.map +1 -1
  184. package/dist/websocket/controllers/server/system.js.map +2 -2
  185. package/dist/websocket/websocket-base.d.ts.map +1 -1
  186. package/dist/websocket/websocket-base.js +2 -3
  187. package/dist/websocket/websocket-base.js.map +2 -2
  188. package/dist/websocket/websocket-server.d.ts +1 -1
  189. package/dist/websocket/websocket-server.d.ts.map +1 -1
  190. package/dist/websocket/websocket-server.js +7 -31
  191. package/dist/websocket/websocket-server.js.map +2 -2
  192. package/package.json +51 -10
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/redis/manager.ts"],
4
- "sourcesContent": ["import { Redis, type RedisOptions } from 'ioredis';\nimport type { RedisManagerConfig as RedisManagerOptions } from './manager.interface.js';\nimport RedisInstance from './instance.js';\nimport { Logger } from '../logger/index.js';\n\nexport default class RedisManager {\n private logger: typeof Logger = Logger;\n\n private options: RedisManagerOptions;\n\n public instances: RedisInstance[] = [];\n\n constructor(config: RedisManagerOptions) {\n this.options = config;\n }\n\n public connect(): Promise<RedisInstance> {\n return new Promise((resolve, reject) => {\n const redisOptions: RedisOptions = {\n host: this.options.host,\n port: this.options.port,\n password: this.options.password,\n maxRetriesPerRequest: null, // Needed for bullmq\n };\n\n const client = new Redis(redisOptions);\n const publisherClient = new Redis(redisOptions);\n const subscriberClient = new Redis(redisOptions);\n\n const handleConnect = (): void => {\n const redisInstance = new RedisInstance({\n redisManager: this,\n client,\n publisherClient,\n subscriberClient,\n });\n\n this.instances.push(redisInstance);\n\n if (this.options.applicationConfig.log?.startUp) {\n this.log('Connected', {\n Host: this.options.host,\n Port: this.options.port,\n });\n }\n\n resolve(redisInstance);\n };\n\n const handleError = (error: Error): void => {\n Logger.error({ error });\n\n reject(error);\n };\n\n client.on('connect', handleConnect);\n client.on('error', handleError);\n });\n }\n\n public async disconnect(): Promise<void> {\n await Promise.all(this.instances.map(instance => instance.disconnect()));\n }\n\n /**\n * Log Redis message\n */\n public log(message: string, meta?: Record<string, unknown>): void {\n this.logger.custom({ level: 'redis', message, meta });\n }\n}\n"],
5
- "mappings": ";;AAAA,SAAS,aAAgC;AAEzC,OAAO,mBAAmB;AAC1B,SAAS,cAAc;AAEvB,MAAO,aAA2B;AAAA,EALlC,OAKkC;AAAA;AAAA;AAAA,EACxB,SAAwB;AAAA,EAExB;AAAA,EAED,YAA6B,CAAC;AAAA,EAErC,YAAY,QAA6B;AACvC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEO,UAAkC;AACvC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,eAA6B;AAAA,QACjC,MAAM,KAAK,QAAQ;AAAA,QACnB,MAAM,KAAK,QAAQ;AAAA,QACnB,UAAU,KAAK,QAAQ;AAAA,QACvB,sBAAsB;AAAA;AAAA,MACxB;AAEA,YAAM,SAAS,IAAI,MAAM,YAAY;AACrC,YAAM,kBAAkB,IAAI,MAAM,YAAY;AAC9C,YAAM,mBAAmB,IAAI,MAAM,YAAY;AAE/C,YAAM,gBAAgB,6BAAY;AAChC,cAAM,gBAAgB,IAAI,cAAc;AAAA,UACtC,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAED,aAAK,UAAU,KAAK,aAAa;AAEjC,YAAI,KAAK,QAAQ,kBAAkB,KAAK,SAAS;AAC/C,eAAK,IAAI,aAAa;AAAA,YACpB,MAAM,KAAK,QAAQ;AAAA,YACnB,MAAM,KAAK,QAAQ;AAAA,UACrB,CAAC;AAAA,QACH;AAEA,gBAAQ,aAAa;AAAA,MACvB,GAlBsB;AAoBtB,YAAM,cAAc,wBAAC,UAAuB;AAC1C,eAAO,MAAM,EAAE,MAAM,CAAC;AAEtB,eAAO,KAAK;AAAA,MACd,GAJoB;AAMpB,aAAO,GAAG,WAAW,aAAa;AAClC,aAAO,GAAG,SAAS,WAAW;AAAA,IAChC,CAAC;AAAA,EACH;AAAA,EAEA,MAAa,aAA4B;AACvC,UAAM,QAAQ,IAAI,KAAK,UAAU,IAAI,cAAY,SAAS,WAAW,CAAC,CAAC;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,SAAiB,MAAsC;AAChE,SAAK,OAAO,OAAO,EAAE,OAAO,SAAS,SAAS,KAAK,CAAC;AAAA,EACtD;AACF;",
4
+ "sourcesContent": ["import { Redis, type RedisOptions } from 'ioredis';\nimport { EventEmitter } from 'node:events';\nimport type { RedisManagerConfig as RedisManagerOptions } from './manager.interface.js';\nimport RedisInstance from './instance.js';\nimport { Logger } from '../logger/index.js';\nimport { CachePerformanceWrapper } from '../performance/index.js';\n\nconst truthyPattern = /^(1|true|yes|on)$/i;\nconst scheduleMicrotask =\n typeof (globalThis as any).queueMicrotask === 'function'\n ? (globalThis as any).queueMicrotask.bind(globalThis)\n : (callback: () => void) => {\n void Promise.resolve().then(callback);\n };\n\ntype RedisCallback = (error: Error | null, result?: string) => void;\n\ninterface InMemoryRedisSharedState {\n store: Map<string, string | Buffer>;\n expirations: Map<string, NodeJS.Timeout>;\n subscriptions: Map<string, Set<InMemoryRedisClient>>;\n}\n\nclass InMemoryRedisClient extends EventEmitter {\n private shared: InMemoryRedisSharedState;\n\n constructor(shared: InMemoryRedisSharedState) {\n super();\n this.shared = shared;\n\n scheduleMicrotask(() => {\n this.emit('ready');\n });\n }\n\n private cleanupSubscriptions(): void {\n for (const subscribers of this.shared.subscriptions.values()) {\n subscribers.delete(this);\n }\n }\n\n private clearExpirationForKey(key: string): void {\n const timer = this.shared.expirations.get(key);\n if (timer) {\n clearTimeout(timer);\n this.shared.expirations.delete(key);\n }\n }\n\n public ping(callback?: RedisCallback): Promise<string> {\n if (callback) {\n callback(null, 'PONG');\n return Promise.resolve('PONG');\n }\n\n return Promise.resolve('PONG');\n }\n\n public async set(...args: any[]): Promise<'OK'> {\n const [key, value, mode, expiration] = args;\n const serializedValue: string | Buffer = value instanceof Buffer ? value : String(value);\n\n this.shared.store.set(key, serializedValue);\n this.clearExpirationForKey(key);\n\n if (typeof mode === 'string' && mode.toUpperCase() === 'EX' && typeof expiration === 'number') {\n const timer = setTimeout(() => {\n this.shared.store.delete(key);\n this.shared.expirations.delete(key);\n }, expiration * 1000);\n\n if (typeof timer.unref === 'function') {\n timer.unref();\n }\n\n this.shared.expirations.set(key, timer);\n }\n\n return 'OK';\n }\n\n public async get(key: string): Promise<string | null> {\n return (this.shared.store.get(key) as string | undefined) ?? null;\n }\n\n public async del(key: string): Promise<number> {\n const existed = this.shared.store.delete(key);\n this.clearExpirationForKey(key);\n return existed ? 1 : 0;\n }\n\n public async publish(channel: string, message: string): Promise<number> {\n const subscribers = this.shared.subscriptions.get(channel);\n\n if (!subscribers || subscribers.size === 0) {\n return 0;\n }\n\n for (const subscriber of subscribers) {\n scheduleMicrotask(() => {\n subscriber.emit('message', channel, message);\n });\n }\n\n return subscribers.size;\n }\n\n public async subscribe(channel: string): Promise<number> {\n let subscribers = this.shared.subscriptions.get(channel);\n if (!subscribers) {\n subscribers = new Set<InMemoryRedisClient>();\n this.shared.subscriptions.set(channel, subscribers);\n }\n subscribers.add(this);\n return subscribers.size;\n }\n\n public async unsubscribe(channel: string): Promise<number> {\n const subscribers = this.shared.subscriptions.get(channel);\n\n if (!subscribers) {\n return 0;\n }\n\n subscribers.delete(this);\n return subscribers.size;\n }\n\n public async quit(): Promise<'OK'> {\n this.cleanupSubscriptions();\n this.removeAllListeners();\n this.emit('end');\n return 'OK';\n }\n\n public disconnect(): void {\n this.cleanupSubscriptions();\n this.removeAllListeners();\n this.emit('end');\n }\n}\n\nexport default class RedisManager {\n private logger: typeof Logger = Logger;\n\n private options: RedisManagerOptions;\n\n public instances: RedisInstance[] = [];\n\n constructor(config: RedisManagerOptions) {\n this.options = config;\n }\n\n public async connect(): Promise<RedisInstance> {\n return CachePerformanceWrapper.monitorConnection(\n 'connect',\n async () => {\n const startTime = performance.now();\n\n const redisOptions: RedisOptions = {\n host: this.options.host,\n port: this.options.port,\n password: this.options.password,\n maxRetriesPerRequest: null, // Needed for bullmq\n };\n\n const useInMemoryRedis =\n truthyPattern.test(process.env.PXL_REDIS_IN_MEMORY ?? '') ||\n truthyPattern.test(process.env.REDIS_IN_MEMORY ?? '');\n\n let sharedState: InMemoryRedisSharedState | undefined;\n if (useInMemoryRedis) {\n sharedState = {\n store: new Map<string, string | Buffer>(),\n expirations: new Map<string, NodeJS.Timeout>(),\n subscriptions: new Map<string, Set<InMemoryRedisClient>>(),\n };\n }\n\n const createClient = (): Redis => {\n if (useInMemoryRedis) {\n if (!sharedState) {\n throw new Error('In-memory Redis shared state not initialized');\n }\n return new InMemoryRedisClient(sharedState) as unknown as Redis;\n }\n\n return new Redis(redisOptions);\n };\n\n const client = createClient();\n const publisherClient = createClient();\n const subscriberClient = createClient();\n\n try {\n // Wait for all three clients to be ready\n await Promise.all([\n new Promise<void>((resolve, reject) => {\n client.once('ready', () => resolve());\n client.once('error', (error: Error) => reject(error));\n }),\n new Promise<void>((resolve, reject) => {\n publisherClient.once('ready', () => resolve());\n publisherClient.once('error', (error: Error) => reject(error));\n }),\n new Promise<void>((resolve, reject) => {\n subscriberClient.once('ready', () => resolve());\n subscriberClient.once('error', (error: Error) => reject(error));\n }),\n ]);\n\n const redisInstance = new RedisInstance({\n redisManager: this,\n client,\n publisherClient,\n subscriberClient,\n });\n\n this.instances.push(redisInstance);\n\n const duration = performance.now() - startTime;\n const meta = {\n Host: this.options.host,\n Port: this.options.port,\n Duration: `${duration.toFixed(2)}ms`,\n Mode: useInMemoryRedis ? 'in-memory' : 'network',\n };\n\n if (this.options.applicationConfig.log?.startUp) {\n this.log('Connected', meta);\n } else {\n this.logger.debug({ message: 'Redis connected', meta });\n }\n\n if (useInMemoryRedis) {\n this.logger.debug({ message: 'Using in-memory Redis stub' });\n }\n\n return redisInstance;\n } catch (error) {\n const duration = performance.now() - startTime;\n\n // Clean up clients on error\n await Promise.allSettled([client.quit(), publisherClient.quit(), subscriberClient.quit()]);\n\n this.logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Redis connection failed',\n meta: {\n Host: this.options.host,\n Port: this.options.port,\n Duration: `${duration.toFixed(2)}ms`,\n Mode: useInMemoryRedis ? 'in-memory' : 'network',\n },\n });\n\n throw error;\n }\n },\n { host: this.options.host, port: this.options.port },\n );\n }\n\n public async disconnect(): Promise<void> {\n await CachePerformanceWrapper.monitorConnection(\n 'disconnect',\n async () => {\n const startTime = performance.now();\n const instanceCount = this.instances.length;\n\n try {\n await Promise.all(this.instances.map(instance => instance.disconnect()));\n\n const duration = performance.now() - startTime;\n\n if (instanceCount > 0) {\n const meta = {\n Instances: instanceCount,\n Host: this.options.host,\n Port: this.options.port,\n Duration: `${duration.toFixed(2)}ms`,\n };\n\n if (this.options.applicationConfig.log?.startUp) {\n this.log('Disconnected all Redis instances', meta);\n } else {\n this.logger.debug({ message: 'Redis instances disconnected', meta });\n }\n }\n\n this.instances = [];\n } catch (error) {\n const duration = performance.now() - startTime;\n\n this.logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Redis disconnection failed',\n meta: {\n Host: this.options.host,\n Port: this.options.port,\n Instances: instanceCount,\n Duration: `${duration.toFixed(2)}ms`,\n },\n });\n\n throw error;\n }\n },\n { host: this.options.host, port: this.options.port },\n );\n }\n\n /**\n * Log Redis message\n */\n public log(message: string, meta?: Record<string, unknown>): void {\n this.logger.custom({ level: 'redis', message, meta });\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,3 @@
1
+ export type { RequestContext, RunWithContextOptions } from './request-context.interface.js';
2
+ export { getRequestContext, getRequestId, getUserId, setUserId, getContextMetadata, setContextMetadata, runWithContext, runWithContextAsync, enterRequestContext, requestContextStorage, } from './request-context.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/request-context/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC5F,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import {
2
+ getRequestContext,
3
+ getRequestId,
4
+ getUserId,
5
+ setUserId,
6
+ getContextMetadata,
7
+ setContextMetadata,
8
+ runWithContext,
9
+ runWithContextAsync,
10
+ enterRequestContext,
11
+ requestContextStorage
12
+ } from "./request-context.js";
13
+ export {
14
+ enterRequestContext,
15
+ getContextMetadata,
16
+ getRequestContext,
17
+ getRequestId,
18
+ getUserId,
19
+ requestContextStorage,
20
+ runWithContext,
21
+ runWithContextAsync,
22
+ setContextMetadata,
23
+ setUserId
24
+ };
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/request-context/index.ts"],
4
+ "sourcesContent": ["export type { RequestContext, RunWithContextOptions } from './request-context.interface.js';\nexport {\n getRequestContext,\n getRequestId,\n getUserId,\n setUserId,\n getContextMetadata,\n setContextMetadata,\n runWithContext,\n runWithContextAsync,\n enterRequestContext,\n requestContextStorage,\n} from './request-context.js';\n"],
5
+ "mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;",
6
+ "names": []
7
+ }
@@ -0,0 +1,108 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import type { RequestContext, RunWithContextOptions } from './request-context.interface.js';
3
+ /**
4
+ * AsyncLocalStorage instance for request context
5
+ *
6
+ * This provides request-scoped storage that automatically propagates through
7
+ * async operations, enabling correlation IDs and request metadata to be
8
+ * accessible throughout the request lifecycle without explicit passing.
9
+ */
10
+ declare const requestContextStorage: AsyncLocalStorage<RequestContext>;
11
+ /**
12
+ * Get the current request context
13
+ *
14
+ * @returns The current request context, or undefined if not in a request context
15
+ *
16
+ * @example
17
+ * const context = getRequestContext();
18
+ * if (context) {
19
+ * console.log('Request ID:', context.requestId);
20
+ * }
21
+ */
22
+ export declare function getRequestContext(): RequestContext | undefined;
23
+ /**
24
+ * Get the current request ID
25
+ *
26
+ * @returns The current request ID, or undefined if not in a request context
27
+ *
28
+ * @example
29
+ * const requestId = getRequestId();
30
+ * logger.info({ message: 'Processing request', requestId });
31
+ */
32
+ export declare function getRequestId(): string | undefined;
33
+ /**
34
+ * Set metadata in the current request context
35
+ *
36
+ * @param key - Metadata key
37
+ * @param value - Metadata value
38
+ *
39
+ * @example
40
+ * setContextMetadata('operation', 'userLookup');
41
+ * setContextMetadata('cacheHit', true);
42
+ */
43
+ export declare function setContextMetadata(key: string, value: unknown): void;
44
+ /**
45
+ * Get metadata from the current request context
46
+ *
47
+ * @param key - Metadata key
48
+ * @returns Metadata value, or undefined if not found
49
+ */
50
+ export declare function getContextMetadata(key: string): unknown;
51
+ /**
52
+ * Run a function within a new request context
53
+ *
54
+ * @param options - Context options (requestId will be generated if not provided)
55
+ * @param fn - Function to run within the context
56
+ * @returns The result of the function
57
+ *
58
+ * @example
59
+ * await runWithContext({ requestId: 'custom-id' }, async () => {
60
+ * await processRequest();
61
+ * });
62
+ */
63
+ export declare function runWithContext<T>(options: RunWithContextOptions | undefined, fn: () => T): T;
64
+ /**
65
+ * Run a function within a new request context (async version)
66
+ *
67
+ * @param options - Context options (requestId will be generated if not provided)
68
+ * @param fn - Async function to run within the context
69
+ * @returns Promise resolving to the result of the function
70
+ *
71
+ * @example
72
+ * await runWithContextAsync({ requestId: 'custom-id' }, async () => {
73
+ * await processRequest();
74
+ * });
75
+ */
76
+ export declare function runWithContextAsync<T>(options: RunWithContextOptions | undefined, fn: () => Promise<T>): Promise<T>;
77
+ /**
78
+ * Update the user ID in the current request context
79
+ *
80
+ * @param userId - User ID to set
81
+ *
82
+ * @example
83
+ * // After authentication
84
+ * setUserId(authenticatedUser.id);
85
+ */
86
+ export declare function setUserId(userId: string): void;
87
+ /**
88
+ * Get the user ID from the current request context
89
+ *
90
+ * @returns The current user ID, or undefined if not set
91
+ */
92
+ export declare function getUserId(): string | undefined;
93
+ /**
94
+ * Enter a new request context (advanced usage for middleware)
95
+ *
96
+ * This sets the context for the current async execution context without
97
+ * wrapping in a callback. Use with caution - prefer runWithContext when possible.
98
+ *
99
+ * @param options - Context options
100
+ *
101
+ * @example
102
+ * // In Fastify middleware
103
+ * const requestId = req.headers['x-request-id'] || crypto.randomUUID();
104
+ * enterRequestContext({ requestId });
105
+ */
106
+ export declare function enterRequestContext(options: RunWithContextOptions): void;
107
+ export { requestContextStorage };
108
+ //# sourceMappingURL=request-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/request-context/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAE5F;;;;;;GAMG;AACH,QAAA,MAAM,qBAAqB,mCAA0C,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,SAAS,CAEjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAOpE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,qBAAqB,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAS5F;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,OAAO,EAAE,qBAAqB,GAAG,SAAS,EAC1C,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CASZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAK9C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CASxE;AAGD,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Request context interface
3
+ *
4
+ * This interface defines the shape of the context stored in AsyncLocalStorage
5
+ * for each HTTP request, providing correlation IDs and request metadata.
6
+ */
7
+ export interface RequestContext {
8
+ /**
9
+ * Unique request ID (UUID v4/v7) for tracing/correlation
10
+ */
11
+ requestId: string;
12
+ /**
13
+ * Request start time (performance.now() timestamp)
14
+ */
15
+ startTime?: number;
16
+ /**
17
+ * Authenticated user ID (if available)
18
+ */
19
+ userId?: string;
20
+ /**
21
+ * Additional custom metadata
22
+ */
23
+ metadata?: Record<string, unknown>;
24
+ }
25
+ /**
26
+ * Options for running code within a request context
27
+ */
28
+ export interface RunWithContextOptions {
29
+ /**
30
+ * Request ID to use (generated if not provided)
31
+ */
32
+ requestId?: string;
33
+ /**
34
+ * Request start time
35
+ */
36
+ startTime?: number;
37
+ /**
38
+ * User ID
39
+ */
40
+ userId?: string;
41
+ /**
42
+ * Additional metadata
43
+ */
44
+ metadata?: Record<string, unknown>;
45
+ }
46
+ //# sourceMappingURL=request-context.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.interface.d.ts","sourceRoot":"","sources":["../../src/request-context/request-context.interface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=request-context.interface.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,79 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { AsyncLocalStorage } from "node:async_hooks";
4
+ import crypto from "node:crypto";
5
+ const requestContextStorage = new AsyncLocalStorage();
6
+ function getRequestContext() {
7
+ return requestContextStorage.getStore();
8
+ }
9
+ __name(getRequestContext, "getRequestContext");
10
+ function getRequestId() {
11
+ return requestContextStorage.getStore()?.requestId;
12
+ }
13
+ __name(getRequestId, "getRequestId");
14
+ function setContextMetadata(key, value) {
15
+ const context = requestContextStorage.getStore();
16
+ if (context) {
17
+ context.metadata ??= {};
18
+ context.metadata[key] = value;
19
+ }
20
+ }
21
+ __name(setContextMetadata, "setContextMetadata");
22
+ function getContextMetadata(key) {
23
+ return requestContextStorage.getStore()?.metadata?.[key];
24
+ }
25
+ __name(getContextMetadata, "getContextMetadata");
26
+ function runWithContext(options, fn) {
27
+ const context = {
28
+ requestId: options?.requestId ?? crypto.randomUUID(),
29
+ startTime: options?.startTime,
30
+ userId: options?.userId,
31
+ metadata: options?.metadata
32
+ };
33
+ return requestContextStorage.run(context, fn);
34
+ }
35
+ __name(runWithContext, "runWithContext");
36
+ async function runWithContextAsync(options, fn) {
37
+ const context = {
38
+ requestId: options?.requestId ?? crypto.randomUUID(),
39
+ startTime: options?.startTime,
40
+ userId: options?.userId,
41
+ metadata: options?.metadata
42
+ };
43
+ return requestContextStorage.run(context, fn);
44
+ }
45
+ __name(runWithContextAsync, "runWithContextAsync");
46
+ function setUserId(userId) {
47
+ const context = requestContextStorage.getStore();
48
+ if (context) {
49
+ context.userId = userId;
50
+ }
51
+ }
52
+ __name(setUserId, "setUserId");
53
+ function getUserId() {
54
+ return requestContextStorage.getStore()?.userId;
55
+ }
56
+ __name(getUserId, "getUserId");
57
+ function enterRequestContext(options) {
58
+ const context = {
59
+ requestId: options?.requestId ?? crypto.randomUUID(),
60
+ startTime: options?.startTime,
61
+ userId: options?.userId,
62
+ metadata: options?.metadata
63
+ };
64
+ requestContextStorage.enterWith(context);
65
+ }
66
+ __name(enterRequestContext, "enterRequestContext");
67
+ export {
68
+ enterRequestContext,
69
+ getContextMetadata,
70
+ getRequestContext,
71
+ getRequestId,
72
+ getUserId,
73
+ requestContextStorage,
74
+ runWithContext,
75
+ runWithContextAsync,
76
+ setContextMetadata,
77
+ setUserId
78
+ };
79
+ //# sourceMappingURL=request-context.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/request-context/request-context.ts"],
4
+ "sourcesContent": ["import { AsyncLocalStorage } from 'node:async_hooks';\nimport crypto from 'node:crypto';\nimport type { RequestContext, RunWithContextOptions } from './request-context.interface.js';\n\n/**\n * AsyncLocalStorage instance for request context\n *\n * This provides request-scoped storage that automatically propagates through\n * async operations, enabling correlation IDs and request metadata to be\n * accessible throughout the request lifecycle without explicit passing.\n */\nconst requestContextStorage = new AsyncLocalStorage<RequestContext>();\n\n/**\n * Get the current request context\n *\n * @returns The current request context, or undefined if not in a request context\n *\n * @example\n * const context = getRequestContext();\n * if (context) {\n * console.log('Request ID:', context.requestId);\n * }\n */\nexport function getRequestContext(): RequestContext | undefined {\n return requestContextStorage.getStore();\n}\n\n/**\n * Get the current request ID\n *\n * @returns The current request ID, or undefined if not in a request context\n *\n * @example\n * const requestId = getRequestId();\n * logger.info({ message: 'Processing request', requestId });\n */\nexport function getRequestId(): string | undefined {\n return requestContextStorage.getStore()?.requestId;\n}\n\n/**\n * Set metadata in the current request context\n *\n * @param key - Metadata key\n * @param value - Metadata value\n *\n * @example\n * setContextMetadata('operation', 'userLookup');\n * setContextMetadata('cacheHit', true);\n */\nexport function setContextMetadata(key: string, value: unknown): void {\n const context = requestContextStorage.getStore();\n if (context) {\n context.metadata ??= {};\n // eslint-disable-next-line security/detect-object-injection\n context.metadata[key] = value;\n }\n}\n\n/**\n * Get metadata from the current request context\n *\n * @param key - Metadata key\n * @returns Metadata value, or undefined if not found\n */\nexport function getContextMetadata(key: string): unknown {\n // eslint-disable-next-line security/detect-object-injection\n return requestContextStorage.getStore()?.metadata?.[key];\n}\n\n/**\n * Run a function within a new request context\n *\n * @param options - Context options (requestId will be generated if not provided)\n * @param fn - Function to run within the context\n * @returns The result of the function\n *\n * @example\n * await runWithContext({ requestId: 'custom-id' }, async () => {\n * await processRequest();\n * });\n */\nexport function runWithContext<T>(options: RunWithContextOptions | undefined, fn: () => T): T {\n const context: RequestContext = {\n requestId: options?.requestId ?? crypto.randomUUID(),\n startTime: options?.startTime,\n userId: options?.userId,\n metadata: options?.metadata,\n };\n\n return requestContextStorage.run(context, fn);\n}\n\n/**\n * Run a function within a new request context (async version)\n *\n * @param options - Context options (requestId will be generated if not provided)\n * @param fn - Async function to run within the context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * await runWithContextAsync({ requestId: 'custom-id' }, async () => {\n * await processRequest();\n * });\n */\nexport async function runWithContextAsync<T>(\n options: RunWithContextOptions | undefined,\n fn: () => Promise<T>,\n): Promise<T> {\n const context: RequestContext = {\n requestId: options?.requestId ?? crypto.randomUUID(),\n startTime: options?.startTime,\n userId: options?.userId,\n metadata: options?.metadata,\n };\n\n return requestContextStorage.run(context, fn);\n}\n\n/**\n * Update the user ID in the current request context\n *\n * @param userId - User ID to set\n *\n * @example\n * // After authentication\n * setUserId(authenticatedUser.id);\n */\nexport function setUserId(userId: string): void {\n const context = requestContextStorage.getStore();\n if (context) {\n context.userId = userId;\n }\n}\n\n/**\n * Get the user ID from the current request context\n *\n * @returns The current user ID, or undefined if not set\n */\nexport function getUserId(): string | undefined {\n return requestContextStorage.getStore()?.userId;\n}\n\n/**\n * Enter a new request context (advanced usage for middleware)\n *\n * This sets the context for the current async execution context without\n * wrapping in a callback. Use with caution - prefer runWithContext when possible.\n *\n * @param options - Context options\n *\n * @example\n * // In Fastify middleware\n * const requestId = req.headers['x-request-id'] || crypto.randomUUID();\n * enterRequestContext({ requestId });\n */\nexport function enterRequestContext(options: RunWithContextOptions): void {\n const context: RequestContext = {\n requestId: options?.requestId ?? crypto.randomUUID(),\n startTime: options?.startTime,\n userId: options?.userId,\n metadata: options?.metadata,\n };\n\n requestContextStorage.enterWith(context);\n}\n\n// Export the storage instance for advanced usage (e.g., middleware)\nexport { requestContextStorage };\n"],
5
+ "mappings": ";;AAAA,SAAS,yBAAyB;AAClC,OAAO,YAAY;AAUnB,MAAM,wBAAwB,IAAI,kBAAkC;AAa7D,SAAS,oBAAgD;AAC9D,SAAO,sBAAsB,SAAS;AACxC;AAFgB;AAaT,SAAS,eAAmC;AACjD,SAAO,sBAAsB,SAAS,GAAG;AAC3C;AAFgB;AAcT,SAAS,mBAAmB,KAAa,OAAsB;AACpE,QAAM,UAAU,sBAAsB,SAAS;AAC/C,MAAI,SAAS;AACX,YAAQ,aAAa,CAAC;AAEtB,YAAQ,SAAS,GAAG,IAAI;AAAA,EAC1B;AACF;AAPgB;AAeT,SAAS,mBAAmB,KAAsB;AAEvD,SAAO,sBAAsB,SAAS,GAAG,WAAW,GAAG;AACzD;AAHgB;AAiBT,SAAS,eAAkB,SAA4C,IAAgB;AAC5F,QAAM,UAA0B;AAAA,IAC9B,WAAW,SAAS,aAAa,OAAO,WAAW;AAAA,IACnD,WAAW,SAAS;AAAA,IACpB,QAAQ,SAAS;AAAA,IACjB,UAAU,SAAS;AAAA,EACrB;AAEA,SAAO,sBAAsB,IAAI,SAAS,EAAE;AAC9C;AATgB;AAuBhB,eAAsB,oBACpB,SACA,IACY;AACZ,QAAM,UAA0B;AAAA,IAC9B,WAAW,SAAS,aAAa,OAAO,WAAW;AAAA,IACnD,WAAW,SAAS;AAAA,IACpB,QAAQ,SAAS;AAAA,IACjB,UAAU,SAAS;AAAA,EACrB;AAEA,SAAO,sBAAsB,IAAI,SAAS,EAAE;AAC9C;AAZsB;AAuBf,SAAS,UAAU,QAAsB;AAC9C,QAAM,UAAU,sBAAsB,SAAS;AAC/C,MAAI,SAAS;AACX,YAAQ,SAAS;AAAA,EACnB;AACF;AALgB;AAYT,SAAS,YAAgC;AAC9C,SAAO,sBAAsB,SAAS,GAAG;AAC3C;AAFgB;AAiBT,SAAS,oBAAoB,SAAsC;AACxE,QAAM,UAA0B;AAAA,IAC9B,WAAW,SAAS,aAAa,OAAO,WAAW;AAAA,IACnD,WAAW,SAAS;AAAA,IACpB,QAAQ,SAAS;AAAA,IACjB,UAAU,SAAS;AAAA,EACrB;AAEA,wBAAsB,UAAU,OAAO;AACzC;AATgB;",
6
+ "names": []
7
+ }
@@ -9,8 +9,8 @@ import {
9
9
  UploadPartCommand
10
10
  } from "@aws-sdk/client-s3";
11
11
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
12
- import { Helper } from "../../util/index.js";
13
- import { createWriteStream, existsSync, mkdirSync } from "fs";
12
+ import { File, Helper } from "../../util/index.js";
13
+ import { createWriteStream } from "fs";
14
14
  import { Readable, pipeline } from "stream";
15
15
  import { promisify } from "node:util";
16
16
  import { Logger } from "../../logger/index.js";
@@ -173,9 +173,7 @@ class AwsS3 {
173
173
  onStart();
174
174
  }
175
175
  const directoryPath = dirname(destinationFilePath);
176
- if (!existsSync(directoryPath)) {
177
- mkdirSync(directoryPath, { recursive: true });
178
- }
176
+ await File.ensureDir(directoryPath);
179
177
  const fileStream = createWriteStream(destinationFilePath);
180
178
  const totalSize = parseInt(response.ContentLength?.toString() ?? "0", 10);
181
179
  let bytesRead = 0;
@@ -188,7 +186,7 @@ class AwsS3 {
188
186
  }
189
187
  });
190
188
  await asyncPipeline(response.Body, fileStream);
191
- if (!existsSync(destinationFilePath)) {
189
+ if (!await File.pathExists(destinationFilePath)) {
192
190
  throw new Error(`Could not find downloaded file at ${destinationFilePath}`);
193
191
  }
194
192
  Logger.info({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/services/aws/s3.ts"],
4
- "sourcesContent": ["import {\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n GetObjectCommand,\n PutObjectCommand,\n type PutObjectCommandInput,\n S3Client,\n type S3ClientConfig,\n UploadPartCommand,\n} from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport { Helper } from '../../util/index.js';\nimport type { AwsS3ConstructorOptions } from './s3.interface.js';\nimport { createWriteStream, existsSync, mkdirSync } from 'fs';\nimport { Readable, pipeline } from 'stream';\nimport { promisify } from 'node:util';\nimport { Logger } from '../../logger/index.js';\nimport { dirname } from 'path';\n\nconst asyncPipeline = promisify(pipeline);\n\ninterface DownloadFileOptions {\n bucketName: string;\n key: string;\n destinationFilePath: string;\n}\n\nexport default class AwsS3 {\n public client: S3Client;\n\n private options: AwsS3ConstructorOptions;\n\n constructor(options: Partial<AwsS3ConstructorOptions>) {\n // Define default options\n const defaultOptions: Partial<AwsS3ConstructorOptions> = {\n region: 'us-east-1',\n localstack: {\n enabled: false,\n port: 4566,\n },\n };\n\n this.options = Helper.defaultsDeep(options, defaultOptions);\n\n const s3ClientConfig: S3ClientConfig = {\n region: this.options.region,\n };\n\n if (this.options.localstack.enabled) {\n s3ClientConfig.forcePathStyle = true;\n\n if (!this.options.endpoint) {\n throw new Error('Endpoint is required when using LocalStack');\n }\n\n // s3ClientConfig.endpoint = `http://s3.localhost.localstack.cloud:${this.options.localstack.port}`; // Works when the Node.js API is calling from within the Docker container\n // s3ClientConfig.endpoint = `http://localhost:${this.options.localstack.port}`; // works out side of the container (media generator example)\n\n s3ClientConfig.endpoint = this.options.endpoint;\n\n s3ClientConfig.credentials = {\n accessKeyId: 'test',\n secretAccessKey: 'test',\n };\n } else {\n if (this.options.credentials?.accessKeyId && this.options.credentials?.secretAccessKey) {\n s3ClientConfig.credentials = {\n accessKeyId: this.options.credentials.accessKeyId,\n secretAccessKey: this.options.credentials.secretAccessKey,\n };\n }\n }\n\n this.client = new S3Client(s3ClientConfig);\n }\n\n private getBucketUrl({ bucketName, path }: { bucketName: string; path: string }) {\n let url;\n\n if (this.options.localstack.enabled) {\n url = `http://localhost:${this.options.localstack.port}/${bucketName}/${path}`;\n } else {\n url = `https://${bucketName}.s3.amazonaws.com/${path}`;\n }\n\n return url;\n }\n\n public async uploadFile({\n bucketName,\n path,\n body,\n contentType,\n forceDownload,\n publicRead,\n }: {\n bucketName: string;\n path: string;\n body: Buffer;\n contentType?: string;\n forceDownload?: boolean;\n publicRead?: boolean;\n }): Promise<string> {\n let contentDisposition = forceDownload ? 'attachment' : 'inline';\n contentDisposition += `; filename=\"${path.split('/').pop()}\"`;\n\n const putObjectOptions: PutObjectCommandInput = {\n Bucket: bucketName,\n Key: path,\n Body: body,\n ContentDisposition: contentDisposition,\n ACL: publicRead ? 'public-read' : 'private',\n };\n\n if (contentType) {\n putObjectOptions.ContentType = contentType;\n }\n\n const command = new PutObjectCommand(putObjectOptions);\n\n await this.client.send(command);\n\n return this.getBucketUrl({ bucketName, path });\n }\n\n public async startMultipartUpload({\n bucketName,\n path,\n publicRead,\n }: {\n bucketName: string;\n path: string;\n publicRead?: boolean;\n }) {\n const command = new CreateMultipartUploadCommand({\n Bucket: bucketName,\n Key: path,\n ACL: publicRead ? 'public-read' : 'private',\n });\n\n const response = await this.client.send(command);\n\n return response.UploadId;\n }\n\n public async uploadPart({\n bucketName,\n path,\n partNumber,\n uploadId,\n body,\n }: {\n bucketName: string;\n path: string;\n partNumber: number;\n uploadId: string;\n body: any;\n }): Promise<string | undefined> {\n const command = new UploadPartCommand({\n Bucket: bucketName,\n Key: path,\n PartNumber: partNumber,\n UploadId: uploadId,\n Body: body,\n });\n\n const response = await this.client.send(command);\n\n return response.ETag;\n }\n\n public async completeMultipartUpload({\n bucketName,\n path,\n uploadId,\n parts,\n }: {\n bucketName: string;\n path: string;\n uploadId: string;\n parts: { PartNumber: number; ETag: string }[];\n }) {\n // Sort parts by PartNumber\n parts.sort((a, b) => a.PartNumber - b.PartNumber);\n\n const command = new CompleteMultipartUploadCommand({\n Bucket: bucketName,\n Key: path,\n UploadId: uploadId,\n MultipartUpload: {\n Parts: parts,\n },\n });\n\n const response = await this.client.send(command);\n\n if (!response.Location) {\n throw new Error('Failed to complete multipart upload');\n }\n\n // return response.Location;\n return this.getBucketUrl({\n bucketName,\n path,\n });\n }\n\n async downloadFile({\n bucketName,\n key,\n destinationFilePath,\n onStart,\n onProgress,\n onComplete,\n onError,\n }: DownloadFileOptions & {\n onStart?: () => void;\n onProgress?: (progress: number) => void;\n onComplete?: () => void;\n onError?: (error: Error) => void;\n }): Promise<void> {\n const decodedKey = decodeURIComponent(key);\n const bucketKey = decodedKey;\n\n Logger.info({\n message: 'Downloading file from S3',\n meta: {\n bucketName,\n Key: bucketKey,\n },\n });\n\n const getObjectParams = {\n Bucket: bucketName,\n Key: bucketKey,\n };\n\n try {\n const command = new GetObjectCommand(getObjectParams);\n\n const response = await this.client.send(command);\n\n if (!response.Body || !(response.Body instanceof Readable)) {\n throw new Error('Expected Body to be a readable stream!');\n }\n\n if (onStart) {\n onStart();\n }\n\n const directoryPath = dirname(destinationFilePath);\n\n if (!existsSync(directoryPath)) {\n mkdirSync(directoryPath, { recursive: true });\n }\n\n const fileStream = createWriteStream(destinationFilePath);\n const totalSize = parseInt(response.ContentLength?.toString() ?? '0', 10);\n\n let bytesRead = 0;\n\n response.Body.on('data', (chunk: Buffer) => {\n bytesRead += chunk.length;\n\n if (onProgress && totalSize > 0) {\n const progress = Math.min((bytesRead / totalSize) * 100, 100);\n const formattedProgress = parseFloat(progress.toFixed(1));\n\n onProgress(formattedProgress);\n }\n });\n\n await asyncPipeline(response.Body, fileStream);\n\n if (!existsSync(destinationFilePath)) {\n throw new Error(`Could not find downloaded file at ${destinationFilePath}`);\n }\n\n Logger.info({\n message: 'File successfully downloaded',\n meta: {\n Path: destinationFilePath,\n },\n });\n\n if (onComplete) {\n onComplete();\n }\n } catch (error) {\n Logger.error({ error });\n\n if (onError) {\n onError(error as Error);\n }\n\n throw error as Error;\n }\n }\n\n public async generateSignedUrl({ bucket, key }: { bucket: string; key: string }): Promise<string> {\n try {\n const command = new GetObjectCommand({\n Bucket: bucket,\n Key: key,\n });\n\n // Set the expiration for the signed URL to 1 hour\n const signedUrl = await getSignedUrl(this.client, command, {\n expiresIn: 3600,\n });\n\n // Log the signed URL\n Logger.info({\n message: 'Generated signed URL',\n meta: {\n URL: signedUrl,\n },\n });\n\n return signedUrl;\n } catch (error) {\n Logger.error({ error });\n\n throw error;\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n GetObjectCommand,\n PutObjectCommand,\n type PutObjectCommandInput,\n S3Client,\n type S3ClientConfig,\n UploadPartCommand,\n} from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport { File, Helper } from '../../util/index.js';\nimport type { AwsS3ConstructorOptions } from './s3.interface.js';\nimport { createWriteStream } from 'fs';\nimport { Readable, pipeline } from 'stream';\nimport { promisify } from 'node:util';\nimport { Logger } from '../../logger/index.js';\nimport { dirname } from 'path';\n\nconst asyncPipeline = promisify(pipeline);\n\ninterface DownloadFileOptions {\n bucketName: string;\n key: string;\n destinationFilePath: string;\n}\n\nexport default class AwsS3 {\n public client: S3Client;\n\n private options: AwsS3ConstructorOptions;\n\n constructor(options: Partial<AwsS3ConstructorOptions>) {\n // Define default options\n const defaultOptions: Partial<AwsS3ConstructorOptions> = {\n region: 'us-east-1',\n localstack: {\n enabled: false,\n port: 4566,\n },\n };\n\n this.options = Helper.defaultsDeep(options, defaultOptions);\n\n const s3ClientConfig: S3ClientConfig = {\n region: this.options.region,\n };\n\n if (this.options.localstack.enabled) {\n s3ClientConfig.forcePathStyle = true;\n\n if (!this.options.endpoint) {\n throw new Error('Endpoint is required when using LocalStack');\n }\n\n // s3ClientConfig.endpoint = `http://s3.localhost.localstack.cloud:${this.options.localstack.port}`; // Works when the Node.js API is calling from within the Docker container\n // s3ClientConfig.endpoint = `http://localhost:${this.options.localstack.port}`; // works out side of the container (media generator example)\n\n s3ClientConfig.endpoint = this.options.endpoint;\n\n s3ClientConfig.credentials = {\n accessKeyId: 'test',\n secretAccessKey: 'test',\n };\n } else {\n if (this.options.credentials?.accessKeyId && this.options.credentials?.secretAccessKey) {\n s3ClientConfig.credentials = {\n accessKeyId: this.options.credentials.accessKeyId,\n secretAccessKey: this.options.credentials.secretAccessKey,\n };\n }\n }\n\n this.client = new S3Client(s3ClientConfig);\n }\n\n private getBucketUrl({ bucketName, path }: { bucketName: string; path: string }) {\n let url;\n\n if (this.options.localstack.enabled) {\n url = `http://localhost:${this.options.localstack.port}/${bucketName}/${path}`;\n } else {\n url = `https://${bucketName}.s3.amazonaws.com/${path}`;\n }\n\n return url;\n }\n\n public async uploadFile({\n bucketName,\n path,\n body,\n contentType,\n forceDownload,\n publicRead,\n }: {\n bucketName: string;\n path: string;\n body: Buffer;\n contentType?: string;\n forceDownload?: boolean;\n publicRead?: boolean;\n }): Promise<string> {\n let contentDisposition = forceDownload ? 'attachment' : 'inline';\n contentDisposition += `; filename=\"${path.split('/').pop()}\"`;\n\n const putObjectOptions: PutObjectCommandInput = {\n Bucket: bucketName,\n Key: path,\n Body: body,\n ContentDisposition: contentDisposition,\n ACL: publicRead ? 'public-read' : 'private',\n };\n\n if (contentType) {\n putObjectOptions.ContentType = contentType;\n }\n\n const command = new PutObjectCommand(putObjectOptions);\n\n await this.client.send(command);\n\n return this.getBucketUrl({ bucketName, path });\n }\n\n public async startMultipartUpload({\n bucketName,\n path,\n publicRead,\n }: {\n bucketName: string;\n path: string;\n publicRead?: boolean;\n }) {\n const command = new CreateMultipartUploadCommand({\n Bucket: bucketName,\n Key: path,\n ACL: publicRead ? 'public-read' : 'private',\n });\n\n const response = await this.client.send(command);\n\n return response.UploadId;\n }\n\n public async uploadPart({\n bucketName,\n path,\n partNumber,\n uploadId,\n body,\n }: {\n bucketName: string;\n path: string;\n partNumber: number;\n uploadId: string;\n body: any;\n }): Promise<string | undefined> {\n const command = new UploadPartCommand({\n Bucket: bucketName,\n Key: path,\n PartNumber: partNumber,\n UploadId: uploadId,\n Body: body,\n });\n\n const response = await this.client.send(command);\n\n return response.ETag;\n }\n\n public async completeMultipartUpload({\n bucketName,\n path,\n uploadId,\n parts,\n }: {\n bucketName: string;\n path: string;\n uploadId: string;\n parts: { PartNumber: number; ETag: string }[];\n }) {\n // Sort parts by PartNumber\n parts.sort((a, b) => a.PartNumber - b.PartNumber);\n\n const command = new CompleteMultipartUploadCommand({\n Bucket: bucketName,\n Key: path,\n UploadId: uploadId,\n MultipartUpload: {\n Parts: parts,\n },\n });\n\n const response = await this.client.send(command);\n\n if (!response.Location) {\n throw new Error('Failed to complete multipart upload');\n }\n\n // return response.Location;\n return this.getBucketUrl({\n bucketName,\n path,\n });\n }\n\n async downloadFile({\n bucketName,\n key,\n destinationFilePath,\n onStart,\n onProgress,\n onComplete,\n onError,\n }: DownloadFileOptions & {\n onStart?: () => void;\n onProgress?: (progress: number) => void;\n onComplete?: () => void;\n onError?: (error: Error) => void;\n }): Promise<void> {\n const decodedKey = decodeURIComponent(key);\n const bucketKey = decodedKey;\n\n Logger.info({\n message: 'Downloading file from S3',\n meta: {\n bucketName,\n Key: bucketKey,\n },\n });\n\n const getObjectParams = {\n Bucket: bucketName,\n Key: bucketKey,\n };\n\n try {\n const command = new GetObjectCommand(getObjectParams);\n\n const response = await this.client.send(command);\n\n if (!response.Body || !(response.Body instanceof Readable)) {\n throw new Error('Expected Body to be a readable stream!');\n }\n\n if (onStart) {\n onStart();\n }\n\n const directoryPath = dirname(destinationFilePath);\n\n // Ensure directory exists\n await File.ensureDir(directoryPath);\n\n const fileStream = createWriteStream(destinationFilePath);\n const totalSize = parseInt(response.ContentLength?.toString() ?? '0', 10);\n\n let bytesRead = 0;\n\n response.Body.on('data', (chunk: Buffer) => {\n bytesRead += chunk.length;\n\n if (onProgress && totalSize > 0) {\n const progress = Math.min((bytesRead / totalSize) * 100, 100);\n const formattedProgress = parseFloat(progress.toFixed(1));\n\n onProgress(formattedProgress);\n }\n });\n\n await asyncPipeline(response.Body, fileStream);\n\n // Verify file was written\n if (!(await File.pathExists(destinationFilePath))) {\n throw new Error(`Could not find downloaded file at ${destinationFilePath}`);\n }\n\n Logger.info({\n message: 'File successfully downloaded',\n meta: {\n Path: destinationFilePath,\n },\n });\n\n if (onComplete) {\n onComplete();\n }\n } catch (error) {\n Logger.error({ error });\n\n if (onError) {\n onError(error as Error);\n }\n\n throw error as Error;\n }\n }\n\n public async generateSignedUrl({ bucket, key }: { bucket: string; key: string }): Promise<string> {\n try {\n const command = new GetObjectCommand({\n Bucket: bucket,\n Key: key,\n });\n\n // Set the expiration for the signed URL to 1 hour\n const signedUrl = await getSignedUrl(this.client, command, {\n expiresIn: 3600,\n });\n\n // Log the signed URL\n Logger.info({\n message: 'Generated signed URL',\n meta: {\n URL: signedUrl,\n },\n });\n\n return signedUrl;\n } catch (error) {\n Logger.error({ error });\n\n throw error;\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Check if a file or directory exists asynchronously
3
+ * @param pathToCheck - Path to check
4
+ * @returns Promise<boolean> - true if exists, false otherwise
5
+ */
6
+ declare function pathExists(pathToCheck: string): Promise<boolean>;
7
+ /**
8
+ * Ensure directory exists, create if it doesn't
9
+ * @param dirPath - Directory path to ensure
10
+ */
11
+ declare function ensureDir(dirPath: string): Promise<void>;
1
12
  declare function convertFile({ inputFilePath, outputFilePath, format, }: {
2
13
  inputFilePath: string;
3
14
  outputFilePath: string;
@@ -40,6 +51,8 @@ declare const _default: {
40
51
  downloadFile: typeof downloadFile;
41
52
  formatFileSize: typeof formatFileSize;
42
53
  removeSync: typeof removeSync;
54
+ pathExists: typeof pathExists;
55
+ ensureDir: typeof ensureDir;
43
56
  };
44
57
  export default _default;
45
58
  //# sourceMappingURL=file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/util/file.ts"],"names":[],"mappings":"AASA,iBAAe,WAAW,CAAC,EACzB,aAAa,EACb,cAAc,EACd,MAAM,GACP,EAAE;IACD,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB;AAED;;;;;GAKG;AACH,iBAAS,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAwBjD;AAED;;;;;GAKG;AACH,iBAAe,YAAY,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B7G;AAED;;;;GAIG;AACH,iBAAS,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAQ5D;AAED;;;;GAIG;AACH,iBAAS,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAuBxC;;;;;;;;AAED,wBAME"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/util/file.ts"],"names":[],"mappings":"AAUA;;;;GAIG;AACH,iBAAe,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;;GAGG;AACH,iBAAe,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMvD;AAED,iBAAe,WAAW,CAAC,EACzB,aAAa,EACb,cAAc,EACd,MAAM,GACP,EAAE;IACD,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB;AAED;;;;;GAKG;AACH,iBAAS,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAmBjD;AAED;;;;;GAKG;AACH,iBAAe,YAAY,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B7G;AAED;;;;GAIG;AACH,iBAAS,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAyB5D;AAED;;;;GAIG;AACH,iBAAS,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAoBxC;;;;;;;;;;AAED,wBAQE"}
package/dist/util/file.js CHANGED
@@ -1,12 +1,30 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
  import * as fs from "fs";
4
+ import { access, mkdir } from "fs/promises";
4
5
  import * as path from "path";
5
6
  import * as https from "https";
6
7
  import { pipeline } from "stream";
7
8
  import { promisify } from "node:util";
8
9
  import ffmpeg from "fluent-ffmpeg";
9
10
  const pipelineAsync = promisify(pipeline);
11
+ async function pathExists(pathToCheck) {
12
+ try {
13
+ await access(pathToCheck);
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+ __name(pathExists, "pathExists");
20
+ async function ensureDir(dirPath) {
21
+ try {
22
+ await access(dirPath);
23
+ } catch {
24
+ await mkdir(dirPath, { recursive: true });
25
+ }
26
+ }
27
+ __name(ensureDir, "ensureDir");
10
28
  async function convertFile({
11
29
  inputFilePath,
12
30
  outputFilePath,
@@ -33,8 +51,7 @@ function copySync(src, dest) {
33
51
  if (!fs.existsSync(dest)) {
34
52
  fs.mkdirSync(dest);
35
53
  }
36
- const entries = fs.readdirSync(src);
37
- for (const entry of entries) {
54
+ for (const entry of fs.readdirSync(src)) {
38
55
  const srcPath = path.join(src, entry);
39
56
  const destPath = path.join(dest, entry);
40
57
  copySync(srcPath, destPath);
@@ -67,19 +84,37 @@ async function downloadFile({ url, destinationPath }) {
67
84
  }
68
85
  __name(downloadFile, "downloadFile");
69
86
  function formatFileSize({ bytes }) {
70
- const sizes = ["bytes", "kB", "MB", "GB", "TB"];
71
87
  if (bytes === 0) return "0 bytes";
72
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
73
- const fileSize = (bytes / Math.pow(1024, i)).toFixed(1);
74
- return `${fileSize} ${sizes[i]}`;
88
+ const units = ["bytes", "kB", "MB", "GB", "TB"];
89
+ let idx = Math.floor(Math.log(bytes) / Math.log(1024));
90
+ if (idx < 0) idx = 0;
91
+ if (idx >= units.length) idx = units.length - 1;
92
+ const fileSize = (bytes / Math.pow(1024, idx)).toFixed(1);
93
+ let unit;
94
+ switch (idx) {
95
+ case 0:
96
+ unit = "bytes";
97
+ break;
98
+ case 1:
99
+ unit = "kB";
100
+ break;
101
+ case 2:
102
+ unit = "MB";
103
+ break;
104
+ case 3:
105
+ unit = "GB";
106
+ break;
107
+ default:
108
+ unit = "TB";
109
+ }
110
+ return `${fileSize} ${unit}`;
75
111
  }
76
112
  __name(formatFileSize, "formatFileSize");
77
113
  function removeSync(target) {
78
114
  if (fs.existsSync(target)) {
79
115
  const stats = fs.statSync(target);
80
116
  if (stats.isDirectory()) {
81
- const entries = fs.readdirSync(target);
82
- for (const entry of entries) {
117
+ for (const entry of fs.readdirSync(target)) {
83
118
  const entryPath = path.join(target, entry);
84
119
  removeSync(entryPath);
85
120
  }
@@ -97,7 +132,9 @@ var file_default = {
97
132
  copySync,
98
133
  downloadFile,
99
134
  formatFileSize,
100
- removeSync
135
+ removeSync,
136
+ pathExists,
137
+ ensureDir
101
138
  };
102
139
  export {
103
140
  file_default as default