stratal 0.0.14 → 0.0.15

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 (136) hide show
  1. package/dist/application-Du0d8O_e.d.mts +116 -0
  2. package/dist/application-Du0d8O_e.d.mts.map +1 -0
  3. package/dist/{base-email.provider-bzdAYp8Z.mjs → base-email.provider-CNwsPbwm.mjs} +1 -1
  4. package/dist/{base-email.provider-bzdAYp8Z.mjs.map → base-email.provider-CNwsPbwm.mjs.map} +1 -1
  5. package/dist/bin/cloudflare-workers-loader.mjs +34 -0
  6. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -0
  7. package/dist/bin/quarry.mjs +164 -0
  8. package/dist/bin/quarry.mjs.map +1 -0
  9. package/dist/cache/index.d.mts +2 -2
  10. package/dist/cache/index.mjs +10 -6
  11. package/dist/cache/index.mjs.map +1 -1
  12. package/dist/command-DG_u5ob2.mjs +192 -0
  13. package/dist/command-DG_u5ob2.mjs.map +1 -0
  14. package/dist/command-DcebcSrL.d.mts +120 -0
  15. package/dist/command-DcebcSrL.d.mts.map +1 -0
  16. package/dist/config/index.d.mts +2 -2
  17. package/dist/config/index.mjs +10 -6
  18. package/dist/config/index.mjs.map +1 -1
  19. package/dist/cron/index.d.mts +1 -1
  20. package/dist/cron/index.mjs +4 -3
  21. package/dist/{cron-manager-CpS_hrDD.mjs → cron-manager-BRh86QCS.mjs} +3 -3
  22. package/dist/{cron-manager-CpS_hrDD.mjs.map → cron-manager-BRh86QCS.mjs.map} +1 -1
  23. package/dist/decorate-D5j-d9_z.mjs +171 -0
  24. package/dist/decorate-D5j-d9_z.mjs.map +1 -0
  25. package/dist/di/index.d.mts +1 -1
  26. package/dist/di/index.mjs +3 -2
  27. package/dist/email/index.d.mts +3 -3
  28. package/dist/email/index.mjs +15 -11
  29. package/dist/email/index.mjs.map +1 -1
  30. package/dist/{en-C9U5-ETs.mjs → en-uVIaxFXR.mjs} +3 -1
  31. package/dist/en-uVIaxFXR.mjs.map +1 -0
  32. package/dist/errors/index.d.mts +1 -1
  33. package/dist/errors/index.mjs +3 -2
  34. package/dist/{errors-BRJgVd5-.mjs → errors-CtCi1wn6.mjs} +6 -3
  35. package/dist/errors-CtCi1wn6.mjs.map +1 -0
  36. package/dist/events/index.d.mts +2 -2
  37. package/dist/events/index.mjs +3 -2
  38. package/dist/{events-CQyvSyrQ.mjs → events-CXl-o1Ad.mjs} +3 -2
  39. package/dist/{events-CQyvSyrQ.mjs.map → events-CXl-o1Ad.mjs.map} +1 -1
  40. package/dist/{gateway-context-D7TFPLi5.mjs → gateway-context-90CQEQDR.mjs} +4 -4
  41. package/dist/{gateway-context-D7TFPLi5.mjs.map → gateway-context-90CQEQDR.mjs.map} +1 -1
  42. package/dist/guards/index.d.mts +3 -3
  43. package/dist/guards/index.mjs +1 -1
  44. package/dist/{guards-B5o618bL.mjs → guards-DMbsAxSX.mjs} +1 -1
  45. package/dist/{guards-B5o618bL.mjs.map → guards-DMbsAxSX.mjs.map} +1 -1
  46. package/dist/i18n/index.d.mts +2 -2
  47. package/dist/i18n/index.mjs +14 -10
  48. package/dist/i18n/messages/en/index.d.mts +1 -1
  49. package/dist/i18n/messages/en/index.mjs +1 -1
  50. package/dist/i18n/validation/index.d.mts +1 -1
  51. package/dist/i18n/validation/index.mjs +1 -1
  52. package/dist/{i18n.module-C9wQr_2k.mjs → i18n.module-qNrpIVts.mjs} +10 -11
  53. package/dist/i18n.module-qNrpIVts.mjs.map +1 -0
  54. package/dist/{index-BWEwA_XK.d.mts → index-CSuHOJc3.d.mts} +3 -1
  55. package/dist/{index-BWEwA_XK.d.mts.map → index-CSuHOJc3.d.mts.map} +1 -1
  56. package/dist/{index-3TtGtYlJ.d.mts → index-Cfkie8JM.d.mts} +1 -1
  57. package/dist/{index-3TtGtYlJ.d.mts.map → index-Cfkie8JM.d.mts.map} +1 -1
  58. package/dist/{index-Dl4RvzNp.d.mts → index-CpAN9ENH.d.mts} +2 -2
  59. package/dist/{index-Dl4RvzNp.d.mts.map → index-CpAN9ENH.d.mts.map} +1 -1
  60. package/dist/{index-C9bIk5tt.d.mts → index-D69rxo8H.d.mts} +9 -6
  61. package/dist/index-D69rxo8H.d.mts.map +1 -0
  62. package/dist/{index-zKURVFOC.d.mts → index-H-Su81aK.d.mts} +3 -3
  63. package/dist/{index-zKURVFOC.d.mts.map → index-H-Su81aK.d.mts.map} +1 -1
  64. package/dist/index.d.mts +3 -106
  65. package/dist/index.d.mts.map +1 -1
  66. package/dist/index.mjs +19 -13
  67. package/dist/is-command-MZDCH-0T.mjs +14 -0
  68. package/dist/is-command-MZDCH-0T.mjs.map +1 -0
  69. package/dist/is-seeder-BN9Ej1r7.mjs +28 -0
  70. package/dist/is-seeder-BN9Ej1r7.mjs.map +1 -0
  71. package/dist/logger/index.d.mts +1 -1
  72. package/dist/logger/index.mjs +2 -1
  73. package/dist/{logger-Bg-CuidS.mjs → logger-BR1-s1Um.mjs} +4 -169
  74. package/dist/logger-BR1-s1Um.mjs.map +1 -0
  75. package/dist/middleware/index.d.mts +1 -1
  76. package/dist/middleware/index.mjs +5 -4
  77. package/dist/{middleware-B3tx1u1K.mjs → middleware-iRhNjsPH.mjs} +3 -3
  78. package/dist/{middleware-B3tx1u1K.mjs.map → middleware-iRhNjsPH.mjs.map} +1 -1
  79. package/dist/module/index.d.mts +21 -3
  80. package/dist/module/index.d.mts.map +1 -1
  81. package/dist/module/index.mjs +10 -6
  82. package/dist/{module-Dvzm4dhS.mjs → module-BH7t7BGG.mjs} +44 -5
  83. package/dist/module-BH7t7BGG.mjs.map +1 -0
  84. package/dist/openapi/index.d.mts +3 -3
  85. package/dist/openapi/index.mjs +14 -10
  86. package/dist/quarry/index.d.mts +112 -0
  87. package/dist/quarry/index.d.mts.map +1 -0
  88. package/dist/quarry/index.mjs +6 -0
  89. package/dist/quarry-registry-BPmKVjhG.mjs +302 -0
  90. package/dist/quarry-registry-BPmKVjhG.mjs.map +1 -0
  91. package/dist/queue/index.d.mts +1 -1
  92. package/dist/queue/index.mjs +11 -7
  93. package/dist/queue/index.mjs.map +1 -1
  94. package/dist/{queue.module-ZqaZ2iY0.mjs → queue.module-BdXWUvIM.mjs} +4 -4
  95. package/dist/{queue.module-ZqaZ2iY0.mjs.map → queue.module-BdXWUvIM.mjs.map} +1 -1
  96. package/dist/{resend.provider-BFGt6fS4.mjs → resend.provider-CQT5be5E.mjs} +5 -4
  97. package/dist/{resend.provider-BFGt6fS4.mjs.map → resend.provider-CQT5be5E.mjs.map} +1 -1
  98. package/dist/router/index.d.mts +1 -1
  99. package/dist/router/index.mjs +14 -10
  100. package/dist/{router-context-DlTxpJUG.mjs → router-context-BLn4PrRG.mjs} +2 -2
  101. package/dist/{router-context-DlTxpJUG.mjs.map → router-context-BLn4PrRG.mjs.map} +1 -1
  102. package/dist/seeder/index.d.mts +77 -0
  103. package/dist/seeder/index.d.mts.map +1 -0
  104. package/dist/seeder/index.mjs +7 -0
  105. package/dist/seeder-DatfjJvU.mjs +132 -0
  106. package/dist/seeder-DatfjJvU.mjs.map +1 -0
  107. package/dist/{smtp.provider-BYY-AdmU.mjs → smtp.provider-Cj7BUFbJ.mjs} +5 -4
  108. package/dist/{smtp.provider-BYY-AdmU.mjs.map → smtp.provider-Cj7BUFbJ.mjs.map} +1 -1
  109. package/dist/storage/index.d.mts +2 -2
  110. package/dist/storage/index.mjs +12 -8
  111. package/dist/{storage-dgi7MG6z.mjs → storage-BtcfgibD.mjs} +5 -5
  112. package/dist/{storage-dgi7MG6z.mjs.map → storage-BtcfgibD.mjs.map} +1 -1
  113. package/dist/{stratal-D4MS_7pI.mjs → stratal-Cm0Yy8v4.mjs} +44 -9
  114. package/dist/stratal-Cm0Yy8v4.mjs.map +1 -0
  115. package/dist/{types-JUIHSW_a.d.mts → types-Cu4jkeiH.d.mts} +1 -1
  116. package/dist/types-Cu4jkeiH.d.mts.map +1 -0
  117. package/dist/types-N84Ak6YT.d.mts +64 -0
  118. package/dist/types-N84Ak6YT.d.mts.map +1 -0
  119. package/dist/usage-generator-BTZDk5zx.mjs +75 -0
  120. package/dist/usage-generator-BTZDk5zx.mjs.map +1 -0
  121. package/dist/{validation-DA5nptIp.mjs → validation-Dbt-snjx.mjs} +1 -1
  122. package/dist/{validation-DA5nptIp.mjs.map → validation-Dbt-snjx.mjs.map} +1 -1
  123. package/dist/websocket/index.d.mts +2 -2
  124. package/dist/websocket/index.mjs +5 -4
  125. package/dist/workers/index.d.mts +1 -1
  126. package/dist/workers/index.mjs +19 -13
  127. package/dist/workers/index.mjs.map +1 -1
  128. package/package.json +15 -1
  129. package/dist/en-C9U5-ETs.mjs.map +0 -1
  130. package/dist/errors-BRJgVd5-.mjs.map +0 -1
  131. package/dist/i18n.module-C9wQr_2k.mjs.map +0 -1
  132. package/dist/index-C9bIk5tt.d.mts.map +0 -1
  133. package/dist/logger-Bg-CuidS.mjs.map +0 -1
  134. package/dist/module-Dvzm4dhS.mjs.map +0 -1
  135. package/dist/stratal-D4MS_7pI.mjs.map +0 -1
  136. package/dist/types-JUIHSW_a.d.mts.map +0 -1
@@ -0,0 +1,116 @@
1
+ import { $t as Container, Ht as StratalEnv, It as VersioningOptions, Tt as RouterContext, U as HonoApp, ct as ModuleClass, it as DynamicModule } from "./index-D69rxo8H.mjs";
2
+ import { l as LogLevel } from "./index-Cfkie8JM.mjs";
3
+ import { r as CommandResult, t as CommandInput } from "./types-N84Ak6YT.mjs";
4
+
5
+ //#region src/execution-context.d.ts
6
+ interface StratalExecutionContext {
7
+ waitUntil(promise: Promise<unknown>): void;
8
+ }
9
+ //#endregion
10
+ //#region src/application.d.ts
11
+ interface ApplicationConfig {
12
+ /** Root application module */
13
+ module: ModuleClass | DynamicModule;
14
+ /** Logging configuration. Defaults: level=INFO, formatter='json' */
15
+ logging?: {
16
+ level?: LogLevel;
17
+ formatter?: 'json' | 'pretty';
18
+ };
19
+ /**
20
+ * API versioning configuration.
21
+ * When provided, enables URI-based versioning for controllers.
22
+ */
23
+ versioning?: VersioningOptions;
24
+ }
25
+ interface ApplicationOptions extends ApplicationConfig {
26
+ env: StratalEnv;
27
+ ctx: StratalExecutionContext;
28
+ }
29
+ /**
30
+ * Application
31
+ *
32
+ * Main application class managing the two-tier container hierarchy:
33
+ * - Global Container: All services (singletons via tsyringe native)
34
+ * - Request Container: Child of global, context-enriched instances per request
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const app = new Application({ module: AppModule, env, ctx })
39
+ * await app.initialize()
40
+ *
41
+ * // Access container via getter
42
+ * const service = app.container.resolve(MY_TOKEN)
43
+ *
44
+ * // Handle HTTP request (via HonoApp)
45
+ * // Handle queue batch
46
+ * await app.handleQueue(batch, 'my-queue')
47
+ * ```
48
+ */
49
+ declare class Application {
50
+ /**
51
+ * Unified Container - manages all DI operations
52
+ */
53
+ private _container;
54
+ private honoApp;
55
+ private moduleRegistry;
56
+ private consumerRegistry;
57
+ private cronManager;
58
+ private quarry;
59
+ private initialized;
60
+ readonly env: StratalEnv;
61
+ private readonly appConfig;
62
+ constructor({
63
+ env,
64
+ ctx,
65
+ ...config
66
+ }: ApplicationOptions);
67
+ /**
68
+ * Get the Container instance
69
+ */
70
+ get container(): Container;
71
+ /**
72
+ * Get the HonoApp instance
73
+ */
74
+ get hono(): HonoApp;
75
+ initialize(): Promise<void>;
76
+ /**
77
+ * Resolve a service from the container
78
+ */
79
+ resolve<T>(token: symbol): T;
80
+ /**
81
+ * Handle queue batch processing
82
+ */
83
+ handleQueue(batch: MessageBatch, queueName: string): Promise<void>;
84
+ /**
85
+ * Handle scheduled cron trigger
86
+ */
87
+ handleScheduled(controller: ScheduledController): Promise<void>;
88
+ /**
89
+ * Create mock RouterContext for queue/cron/seeder processing
90
+ */
91
+ createMockRouterContext(locale?: string): RouterContext;
92
+ shutdown(): Promise<void>;
93
+ /**
94
+ * Execute a command by name in a request-scoped container.
95
+ */
96
+ handleCommand(name: string, input?: CommandInput): Promise<CommandResult>;
97
+ private registerCommands;
98
+ private registerSeeders;
99
+ private registerQueueConsumers;
100
+ private registerCronJobs;
101
+ /**
102
+ * Auto-wire `@Listener()` classes with the EventRegistry.
103
+ */
104
+ private registerEventListeners;
105
+ /**
106
+ * Register LoggerService and dependencies
107
+ */
108
+ private registerLoggerService;
109
+ /**
110
+ * Register core services with explicit scope
111
+ */
112
+ private registerCoreServices;
113
+ }
114
+ //#endregion
115
+ export { StratalExecutionContext as i, ApplicationConfig as n, ApplicationOptions as r, Application as t };
116
+ //# sourceMappingURL=application-Du0d8O_e.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-Du0d8O_e.d.mts","names":[],"sources":["../src/execution-context.ts","../src/application.ts"],"mappings":";;;;;UAAiB,uBAAA;EACf,SAAA,CAAU,OAAA,EAAS,OAAA;AAAA;;;UC6BJ,iBAAA;ED7BI;EC+BnB,MAAA,EAAQ,WAAA,GAAc,aAAA;ED/Ba;ECiCnC,OAAA;IACE,KAAA,GAAQ,QAAA;IACR,SAAA;EAAA;EANa;;;;EAYf,UAAA,GAAa,iBAAA;AAAA;AAAA,UAGE,kBAAA,SAA2B,iBAAA;EAC1C,GAAA,EAAK,UAAA;EACL,GAAA,EAAK,uBAAA;AAAA;;;;;;;;;;;AAFP;;;;;;;;;;cAyBa,WAAA;EAvBX;;;EAAA,QA2BQ,UAAA;EAAA,QAEA,OAAA;EAAA,QACA,cAAA;EAAA,QACA,gBAAA;EAAA,QACA,WAAA;EAAA,QACA,MAAA;EAAA,QACA,WAAA;EAAA,SAEC,GAAA,EAAK,UAAA;EAAA,iBACG,SAAA;;IAEH,GAAA;IAAK,GAAA;IAAA,GAAQ;EAAA,GAAU,kBAAA;EA0CjB;;;EAAA,IAXhB,SAAA,CAAA,GAAa,SAAA;EAwFiB;;;EAAA,IAjF9B,IAAA,CAAA,GAAQ,OAAA;EAIN,UAAA,CAAA,GAAc,OAAA;EAqH6C;;;EAzEjE,OAAA,GAAA,CAAW,KAAA,WAAgB,CAAA;EAlGnB;;;EA+GF,WAAA,CAAY,KAAA,EAAO,YAAA,EAAc,SAAA,WAAoB,OAAA;EA1GnD;;;EA8HF,eAAA,CAAgB,UAAA,EAAY,mBAAA,GAAsB,OAAA;EA1H1C;;;EA2Id,uBAAA,CAAwB,MAAA,YAAgB,aAAA;EAQlC,QAAA,CAAA,GAAY,OAAA;EAhJC;;;EA+Jb,aAAA,CAAc,IAAA,UAAc,KAAA,GAAQ,YAAA,GAAe,OAAA,CAAQ,aAAA;EAAA,QAOzD,gBAAA;EAAA,QAoBA,eAAA;EAAA,QASA,sBAAA;EAAA,QAOA,gBAAA;EAhKF;;;EAAA,QA0KE,sBAAA;EA9HG;;;EAAA,QAmJH,qBAAA;EAtIU;;;EAAA,QA0JV,oBAAA;AAAA"}
@@ -39,4 +39,4 @@ var BaseEmailProvider = class {
39
39
  //#endregion
40
40
  export { BaseEmailProvider as t };
41
41
 
42
- //# sourceMappingURL=base-email.provider-bzdAYp8Z.mjs.map
42
+ //# sourceMappingURL=base-email.provider-CNwsPbwm.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-email.provider-bzdAYp8Z.mjs","names":[],"sources":["../src/email/providers/base-email.provider.ts"],"sourcesContent":["import type { ResolvedEmailMessage } from '../contracts'\nimport type { EmailBatchSendResult, EmailSendResult, IEmailProvider } from './email-provider.interface'\n\n/**\n * Base Email Provider\n *\n * Abstract base class for email providers.\n * Provides shared implementation of sendBatch() to reduce code duplication.\n */\nexport abstract class BaseEmailProvider implements IEmailProvider {\n /**\n * Send a single email - must be implemented by concrete providers\n */\n abstract send(message: ResolvedEmailMessage): Promise<EmailSendResult>\n\n /**\n * Send multiple emails in a batch\n *\n * Default implementation sends emails sequentially.\n * Concrete providers can override for optimized batch sending.\n */\n async sendBatch(messages: ResolvedEmailMessage[]): Promise<EmailBatchSendResult> {\n const results: EmailSendResult[] = []\n let successful = 0\n let failed = 0\n\n for (const message of messages) {\n try {\n const result = await this.send(message)\n results.push(result)\n successful++\n }\n catch (error) {\n results.push({\n messageId: '',\n accepted: false,\n metadata: {\n error: error instanceof Error ? error.message : 'Unknown error',\n },\n })\n failed++\n }\n }\n\n return {\n total: messages.length,\n successful,\n failed,\n results,\n }\n }\n}\n"],"mappings":";;;;;;;AASA,IAAsB,oBAAtB,MAAkE;;;;;;;CAYhE,MAAM,UAAU,UAAiE;EAC/E,MAAM,UAA6B,EAAE;EACrC,IAAI,aAAa;EACjB,IAAI,SAAS;AAEb,OAAK,MAAM,WAAW,SACpB,KAAI;GACF,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ;AACvC,WAAQ,KAAK,OAAO;AACpB;WAEK,OAAO;AACZ,WAAQ,KAAK;IACX,WAAW;IACX,UAAU;IACV,UAAU,EACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,iBACjD;IACF,CAAC;AACF;;AAIJ,SAAO;GACL,OAAO,SAAS;GAChB;GACA;GACA;GACD"}
1
+ {"version":3,"file":"base-email.provider-CNwsPbwm.mjs","names":[],"sources":["../src/email/providers/base-email.provider.ts"],"sourcesContent":["import type { ResolvedEmailMessage } from '../contracts'\nimport type { EmailBatchSendResult, EmailSendResult, IEmailProvider } from './email-provider.interface'\n\n/**\n * Base Email Provider\n *\n * Abstract base class for email providers.\n * Provides shared implementation of sendBatch() to reduce code duplication.\n */\nexport abstract class BaseEmailProvider implements IEmailProvider {\n /**\n * Send a single email - must be implemented by concrete providers\n */\n abstract send(message: ResolvedEmailMessage): Promise<EmailSendResult>\n\n /**\n * Send multiple emails in a batch\n *\n * Default implementation sends emails sequentially.\n * Concrete providers can override for optimized batch sending.\n */\n async sendBatch(messages: ResolvedEmailMessage[]): Promise<EmailBatchSendResult> {\n const results: EmailSendResult[] = []\n let successful = 0\n let failed = 0\n\n for (const message of messages) {\n try {\n const result = await this.send(message)\n results.push(result)\n successful++\n }\n catch (error) {\n results.push({\n messageId: '',\n accepted: false,\n metadata: {\n error: error instanceof Error ? error.message : 'Unknown error',\n },\n })\n failed++\n }\n }\n\n return {\n total: messages.length,\n successful,\n failed,\n results,\n }\n }\n}\n"],"mappings":";;;;;;;AASA,IAAsB,oBAAtB,MAAkE;;;;;;;CAYhE,MAAM,UAAU,UAAiE;EAC/E,MAAM,UAA6B,EAAE;EACrC,IAAI,aAAa;EACjB,IAAI,SAAS;AAEb,OAAK,MAAM,WAAW,SACpB,KAAI;GACF,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ;AACvC,WAAQ,KAAK,OAAO;AACpB;WAEK,OAAO;AACZ,WAAQ,KAAK;IACX,WAAW;IACX,UAAU;IACV,UAAU,EACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,iBACjD;IACF,CAAC;AACF;;AAIJ,SAAO;GACL,OAAO,SAAS;GAChB;GACA;GACA;GACD"}
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env -S node --no-warnings
2
+ //#region src/bin/cloudflare-workers-loader.ts
3
+ /**
4
+ * ESM loader hook that provides a virtual `cloudflare:workers` module.
5
+ *
6
+ * When registered via `node --import` or `register()`, this intercepts
7
+ * `import('cloudflare:workers')` and returns a module that reads `env`
8
+ * and `waitUntil` from `globalThis.__stratalPlatformProxy`.
9
+ */
10
+ const VIRTUAL_URL = "cloudflare-workers:virtual";
11
+ async function resolve(specifier, context, nextResolve) {
12
+ if (specifier === "cloudflare:workers") return {
13
+ url: VIRTUAL_URL,
14
+ shortCircuit: true
15
+ };
16
+ return nextResolve(specifier, context);
17
+ }
18
+ async function load(url, context, nextLoad) {
19
+ if (url === VIRTUAL_URL) return {
20
+ format: "module",
21
+ shortCircuit: true,
22
+ source: `
23
+ const proxy = globalThis.__stratalPlatformProxy;
24
+ if (!proxy) throw new Error('globalThis.__stratalPlatformProxy not set — Quarry CLI must initialize it before importing the app entry.');
25
+ export const env = proxy.env;
26
+ export const waitUntil = proxy.waitUntil;
27
+ `
28
+ };
29
+ return nextLoad(url, context);
30
+ }
31
+ //#endregion
32
+ export { load, resolve };
33
+
34
+ //# sourceMappingURL=cloudflare-workers-loader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-workers-loader.mjs","names":[],"sources":["../../src/bin/cloudflare-workers-loader.ts"],"sourcesContent":["/**\n * ESM loader hook that provides a virtual `cloudflare:workers` module.\n *\n * When registered via `node --import` or `register()`, this intercepts\n * `import('cloudflare:workers')` and returns a module that reads `env`\n * and `waitUntil` from `globalThis.__stratalPlatformProxy`.\n */\n\nconst VIRTUAL_URL = 'cloudflare-workers:virtual'\n\ninterface ResolveContext {\n parentURL?: string\n conditions: string[]\n}\n\ninterface ResolveResult {\n url: string\n shortCircuit?: boolean\n}\n\ntype NextResolve = (specifier: string, context: ResolveContext) => Promise<ResolveResult>\n\nexport async function resolve(\n specifier: string,\n context: ResolveContext,\n nextResolve: NextResolve,\n): Promise<ResolveResult> {\n if (specifier === 'cloudflare:workers') {\n return { url: VIRTUAL_URL, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n}\n\ninterface LoadContext {\n format?: string\n conditions: string[]\n}\n\ninterface LoadResult {\n format: string\n source: string\n shortCircuit?: boolean\n}\n\ntype NextLoad = (url: string, context: LoadContext) => Promise<LoadResult>\n\nexport async function load(\n url: string,\n context: LoadContext,\n nextLoad: NextLoad,\n): Promise<LoadResult> {\n if (url === VIRTUAL_URL) {\n return {\n format: 'module',\n shortCircuit: true,\n source: `\nconst proxy = globalThis.__stratalPlatformProxy;\nif (!proxy) throw new Error('globalThis.__stratalPlatformProxy not set — Quarry CLI must initialize it before importing the app entry.');\nexport const env = proxy.env;\nexport const waitUntil = proxy.waitUntil;\n`,\n }\n }\n return nextLoad(url, context)\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,cAAc;AAcpB,eAAsB,QACpB,WACA,SACA,aACwB;AACxB,KAAI,cAAc,qBAChB,QAAO;EAAE,KAAK;EAAa,cAAc;EAAM;AAEjD,QAAO,YAAY,WAAW,QAAQ;;AAgBxC,eAAsB,KACpB,KACA,SACA,UACqB;AACrB,KAAI,QAAQ,YACV,QAAO;EACL,QAAQ;EACR,cAAc;EACd,QAAQ;;;;;;EAMT;AAEH,QAAO,SAAS,KAAK,QAAQ"}
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env -S node --no-warnings
2
+ import { createRequire, register } from "node:module";
3
+ import "reflect-metadata";
4
+ import { existsSync } from "node:fs";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { pathToFileURL } from "node:url";
7
+ import { Command, Option } from "clipanion";
8
+ //#region src/bin/commands/dynamic-command.ts
9
+ function createDynamicCommands(quarry, parseSignature, app) {
10
+ const commands = [];
11
+ for (const entry of quarry.list()) {
12
+ const commandClass = quarry.get(entry.name);
13
+ const signature = parseSignature(commandClass.command);
14
+ const paths = [entry.name.split(" ")];
15
+ if (commandClass.aliases) for (const alias of commandClass.aliases) paths.push(alias.split(" "));
16
+ class DynCmd extends Command {
17
+ static paths = paths;
18
+ static usage = commandClass.description ? Command.Usage({ description: commandClass.description }) : void 0;
19
+ async execute() {
20
+ const input = {};
21
+ for (const arg of signature.arguments) {
22
+ const value = this[arg.name];
23
+ if (value !== void 0) input[arg.name] = value;
24
+ }
25
+ for (const opt of signature.options) {
26
+ const value = this[opt.name];
27
+ if (value !== void 0) input[opt.name] = value;
28
+ }
29
+ const result = await app.handleCommand(entry.name, input);
30
+ for (const line of result.output) this.context.stdout.write(line + "\n");
31
+ for (const err of result.errors) this.context.stderr.write(err + "\n");
32
+ return result.exitCode;
33
+ }
34
+ }
35
+ const proto = DynCmd.prototype;
36
+ for (const arg of signature.arguments) if (arg.isArray) proto[arg.name] = Option.Rest({
37
+ name: arg.name,
38
+ required: arg.required ? 1 : 0
39
+ });
40
+ else proto[arg.name] = Option.String({
41
+ name: arg.name,
42
+ required: arg.required
43
+ });
44
+ for (const opt of signature.options) {
45
+ const optName = opt.alias ? `-${opt.alias},--${opt.name}` : `--${opt.name}`;
46
+ if (opt.isFlag) proto[opt.name] = Option.Boolean(optName, { description: opt.description });
47
+ else if (opt.isArray) proto[opt.name] = Option.Array(optName, { description: opt.description });
48
+ else proto[opt.name] = Option.String(optName, { description: opt.description });
49
+ }
50
+ commands.push(DynCmd);
51
+ }
52
+ return commands;
53
+ }
54
+ //#endregion
55
+ //#region src/bin/commands/help-command.ts
56
+ function createHelpCommand() {
57
+ class HelpCommand extends Command {
58
+ static paths = [["help"]];
59
+ static usage = Command.Usage({ description: "Show help for a command" });
60
+ commandPath = Option.Rest();
61
+ execute() {
62
+ const commandName = this.commandPath.join(" ");
63
+ if (this.help || !commandName) {
64
+ this.context.stdout.write(this.cli.usage());
65
+ return Promise.resolve(0);
66
+ }
67
+ try {
68
+ const command = this.cli.process(this.commandPath);
69
+ this.context.stdout.write(this.cli.usage(command, { detailed: true }));
70
+ return Promise.resolve(0);
71
+ } catch {
72
+ this.context.stderr.write(`Unknown command: ${commandName}\n`);
73
+ return Promise.resolve(1);
74
+ }
75
+ }
76
+ }
77
+ return HelpCommand;
78
+ }
79
+ //#endregion
80
+ //#region src/bin/commands/list-command.ts
81
+ function createListCommand(quarry) {
82
+ class ListCommand extends Command {
83
+ static paths = [["list"]];
84
+ static usage = Command.Usage({ description: "List all registered commands" });
85
+ execute() {
86
+ const commands = quarry.list();
87
+ if (commands.length === 0) {
88
+ this.context.stdout.write("No registered commands.\n");
89
+ return Promise.resolve(0);
90
+ }
91
+ this.context.stdout.write("\nRegistered commands:\n\n");
92
+ const maxName = Math.max(...commands.map((c) => c.name.length));
93
+ for (const cmd of commands) {
94
+ const aliasStr = cmd.aliases.length > 0 ? ` (${cmd.aliases.join(", ")})` : "";
95
+ const desc = cmd.description ?? "";
96
+ this.context.stdout.write(` ${cmd.name.padEnd(maxName + 4)}${desc}${aliasStr}\n`);
97
+ }
98
+ this.context.stdout.write("\n");
99
+ return Promise.resolve(0);
100
+ }
101
+ }
102
+ return ListCommand;
103
+ }
104
+ //#endregion
105
+ //#region src/bin/quarry.ts
106
+ const require = createRequire(import.meta.url);
107
+ register(pathToFileURL(join(dirname(require.resolve("@swc-node/register")), "esm/esm.mjs")), pathToFileURL("./"));
108
+ register(new URL("./cloudflare-workers-loader.mjs", import.meta.url), pathToFileURL("./"));
109
+ const DEFAULT_ENTRY = "./src/index.ts";
110
+ const firstArg = process.argv[2];
111
+ let entryFile = DEFAULT_ENTRY;
112
+ if (firstArg && (firstArg.includes("/") || firstArg.includes("\\") || /\.(ts|js|mts|mjs)$/.test(firstArg))) {
113
+ entryFile = firstArg;
114
+ process.argv.splice(2, 1);
115
+ }
116
+ const entryPath = resolve(process.cwd(), entryFile);
117
+ if (!existsSync(entryPath)) {
118
+ console.error(`Error: Entry file not found: ${entryFile}`);
119
+ console.error("");
120
+ console.error("Create src/index.ts with a default Stratal export, or specify a custom path:");
121
+ console.error(" npx quarry ./path/to/entry.ts <command> [options]");
122
+ process.exit(1);
123
+ }
124
+ async function main() {
125
+ const { getPlatformProxy } = await import(createRequire(join(process.cwd(), "package.json")).resolve("wrangler"));
126
+ const { env, ctx, dispose } = await getPlatformProxy();
127
+ let app;
128
+ try {
129
+ globalThis.__stratalPlatformProxy = {
130
+ env,
131
+ waitUntil: ctx.waitUntil.bind(ctx)
132
+ };
133
+ await import(pathToFileURL(entryPath).href);
134
+ const [{ Stratal }, { DI_TOKENS }, { parseSignature }] = await Promise.all([
135
+ import("stratal"),
136
+ import("stratal/di"),
137
+ import("stratal/quarry")
138
+ ]);
139
+ app = await Stratal.resolveApplication();
140
+ const quarry = app.container.resolve(DI_TOKENS.Quarry);
141
+ const { Builtins, Cli } = await import("clipanion");
142
+ const cli = new Cli({
143
+ binaryName: "quarry",
144
+ binaryLabel: "Quarry CLI",
145
+ binaryVersion: require("../../package.json").version
146
+ });
147
+ cli.register(Builtins.HelpCommand);
148
+ cli.register(createListCommand(quarry));
149
+ cli.register(createHelpCommand());
150
+ for (const cmd of createDynamicCommands(quarry, parseSignature, app)) cli.register(cmd);
151
+ await cli.runExit(process.argv.slice(2), { ...Cli.defaultContext });
152
+ } finally {
153
+ await app?.shutdown();
154
+ await dispose();
155
+ }
156
+ }
157
+ main().catch((error) => {
158
+ console.error("Fatal error:", error instanceof Error ? error.message : String(error));
159
+ process.exit(1);
160
+ });
161
+ //#endregion
162
+ export {};
163
+
164
+ //# sourceMappingURL=quarry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quarry.mjs","names":[],"sources":["../../src/bin/commands/dynamic-command.ts","../../src/bin/commands/help-command.ts","../../src/bin/commands/list-command.ts","../../src/bin/quarry.ts"],"sourcesContent":["import { Command, type CommandClass, Option, type Usage } from 'clipanion'\n\nimport type { Application } from 'stratal'\nimport type { QuarryRegistry } from 'stratal/quarry'\nimport type { ParsedSignature } from 'stratal/quarry'\n\nexport function createDynamicCommands(\n quarry: QuarryRegistry,\n parseSignature: (command: string) => ParsedSignature,\n app: Application,\n) {\n const commands: CommandClass[] = []\n\n for (const entry of quarry.list()) {\n const commandClass = quarry.get(entry.name)! as unknown as { command: string; description?: string; aliases?: string[] }\n const signature = parseSignature(commandClass.command)\n\n const paths: string[][] = [entry.name.split(' ')]\n if (commandClass.aliases) {\n for (const alias of commandClass.aliases) {\n paths.push(alias.split(' '))\n }\n }\n\n class DynCmd extends Command {\n static override paths = paths\n static override usage: Usage | undefined = commandClass.description\n ? Command.Usage({ description: commandClass.description })\n : undefined\n\n async execute(): Promise<number> {\n const input: Record<string, unknown> = {}\n\n for (const arg of signature.arguments) {\n const value = (this as Record<string, unknown>)[arg.name]\n if (value !== undefined) input[arg.name] = value\n }\n\n for (const opt of signature.options) {\n const value = (this as Record<string, unknown>)[opt.name]\n if (value !== undefined) input[opt.name] = value\n }\n\n const result = await app.handleCommand(entry.name, input)\n\n for (const line of result.output) {\n this.context.stdout.write(line + '\\n')\n }\n\n for (const err of result.errors) {\n this.context.stderr.write(err + '\\n')\n }\n\n return result.exitCode\n }\n }\n\n // Define Clipanion options/arguments as class property defaults\n const proto = DynCmd.prototype as unknown as Record<string, unknown>\n for (const arg of signature.arguments) {\n if (arg.isArray) {\n proto[arg.name] = Option.Rest({ name: arg.name, required: arg.required ? 1 : 0 })\n } else {\n proto[arg.name] = Option.String({ name: arg.name, required: arg.required })\n }\n }\n\n for (const opt of signature.options) {\n const optName = opt.alias ? `-${opt.alias},--${opt.name}` : `--${opt.name}`\n\n if (opt.isFlag) {\n proto[opt.name] = Option.Boolean(optName, { description: opt.description })\n } else if (opt.isArray) {\n proto[opt.name] = Option.Array(optName, { description: opt.description })\n } else {\n proto[opt.name] = Option.String(optName, { description: opt.description })\n }\n }\n\n commands.push(DynCmd)\n }\n\n return commands\n}\n","import { Command, Option, type Usage } from 'clipanion'\n\nexport function createHelpCommand() {\n class HelpCommand extends Command {\n static override paths = [['help']]\n static override usage: Usage = Command.Usage({ description: 'Show help for a command' })\n\n commandPath = Option.Rest()\n\n execute(): Promise<number> {\n const commandName = this.commandPath.join(' ')\n\n if (this.help || !commandName) {\n this.context.stdout.write(this.cli.usage())\n return Promise.resolve(0)\n }\n\n try {\n const command = this.cli.process(this.commandPath)\n this.context.stdout.write(this.cli.usage(command, { detailed: true }))\n return Promise.resolve(0)\n } catch {\n this.context.stderr.write(`Unknown command: ${commandName}\\n`)\n return Promise.resolve(1)\n }\n }\n }\n\n return HelpCommand\n}\n","import { Command, type Usage } from 'clipanion'\n\nimport type { QuarryRegistry } from 'stratal/quarry'\n\nexport function createListCommand(quarry: QuarryRegistry) {\n class ListCommand extends Command {\n static override paths = [['list']]\n static override usage: Usage = Command.Usage({ description: 'List all registered commands' })\n\n execute(): Promise<number> {\n const commands = quarry.list()\n\n if (commands.length === 0) {\n this.context.stdout.write('No registered commands.\\n')\n return Promise.resolve(0)\n }\n\n this.context.stdout.write('\\nRegistered commands:\\n\\n')\n\n const maxName = Math.max(...commands.map((c) => c.name.length))\n for (const cmd of commands) {\n const aliasStr = cmd.aliases.length > 0 ? ` (${cmd.aliases.join(', ')})` : ''\n const desc = cmd.description ?? ''\n this.context.stdout.write(` ${cmd.name.padEnd(maxName + 4)}${desc}${aliasStr}\\n`)\n }\n\n this.context.stdout.write('\\n')\n return Promise.resolve(0)\n }\n }\n\n return ListCommand\n}\n","import 'reflect-metadata'\n\nimport { existsSync } from 'node:fs'\nimport { createRequire, register } from 'node:module'\nimport { dirname, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { QuarryRegistry } from 'stratal/quarry'\n\nimport { type Application } from '../application'\nimport { createDynamicCommands } from './commands/dynamic-command'\nimport { createHelpCommand } from './commands/help-command'\nimport { createListCommand } from './commands/list-command'\n\nconst require = createRequire(import.meta.url)\n\n// Register @swc-node/register for TypeScript + decorator support\nconst swcRegisterPath = join(dirname(require.resolve('@swc-node/register')), 'esm/esm.mjs')\nregister(pathToFileURL(swcRegisterPath), pathToFileURL('./'))\n\n// Register cloudflare:workers virtual module loader\nregister(new URL('./cloudflare-workers-loader.mjs', import.meta.url), pathToFileURL('./'))\n\nconst DEFAULT_ENTRY = './src/index.ts'\n\n// Determine entry file: if first arg looks like a file path, use it; otherwise use default\nconst firstArg = process.argv[2]\nlet entryFile = DEFAULT_ENTRY\n\nif (firstArg && (firstArg.includes('/') || firstArg.includes('\\\\') || /\\.(ts|js|mts|mjs)$/.test(firstArg))) {\n entryFile = firstArg\n // Remove the entry file from argv so Clipanion sees: [node, script, command, ...options]\n process.argv.splice(2, 1)\n}\n\n// Resolve and validate the entry file\nconst entryPath = resolve(process.cwd(), entryFile)\n\nif (!existsSync(entryPath)) {\n console.error(`Error: Entry file not found: ${entryFile}`)\n console.error('')\n console.error('Create src/index.ts with a default Stratal export, or specify a custom path:')\n console.error(' npx quarry ./path/to/entry.ts <command> [options]')\n process.exit(1)\n}\n\nasync function main(): Promise<void> {\n const cwdRequire = createRequire(join(process.cwd(), 'package.json'))\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n const { getPlatformProxy } = await import(cwdRequire.resolve('wrangler')) as typeof import('wrangler')\n const { env, ctx, dispose } = await getPlatformProxy()\n\n let app: Application | undefined\n try {\n // Store platform proxy on globalThis so the cloudflare:workers virtual module can read it\n (globalThis as Record<string, unknown>).__stratalPlatformProxy = {\n env,\n waitUntil: ctx.waitUntil.bind(ctx),\n }\n\n // Import user's entry file — triggers `new Stratal(...)` + full Application init\n await import(pathToFileURL(entryPath).href)\n\n // Parallel import of stratal modules\n const [\n { Stratal },\n { DI_TOKENS },\n { parseSignature },\n ] = await Promise.all([\n import('stratal'),\n import('stratal/di'),\n import('stratal/quarry'),\n ])\n\n app = await Stratal.resolveApplication()\n const quarry = app.container.resolve<QuarryRegistry>(DI_TOKENS.Quarry)\n\n // Build Clipanion CLI\n const { Builtins, Cli } = await import('clipanion')\n const pkg = require('../../package.json') as { version: string }\n\n const cli = new Cli({\n binaryName: 'quarry',\n binaryLabel: 'Quarry CLI',\n binaryVersion: pkg.version,\n })\n\n cli.register(Builtins.HelpCommand)\n cli.register(createListCommand(quarry))\n cli.register(createHelpCommand())\n\n for (const cmd of createDynamicCommands(quarry, parseSignature, app)) {\n cli.register(cmd)\n }\n\n await cli.runExit(process.argv.slice(2), { ...Cli.defaultContext })\n } finally {\n await app?.shutdown()\n await dispose()\n }\n}\n\nmain().catch((error: unknown) => {\n console.error('Fatal error:', error instanceof Error ? error.message : String(error))\n process.exit(1)\n})\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { Ht as StratalEnv, s as ApplicationError } from "../index-C9bIk5tt.mjs";
2
- import { i as LoggerService } from "../index-3TtGtYlJ.mjs";
1
+ import { Ht as StratalEnv, s as ApplicationError } from "../index-D69rxo8H.mjs";
2
+ import { i as LoggerService } from "../index-Cfkie8JM.mjs";
3
3
 
4
4
  //#region src/cache/cache.module.d.ts
5
5
  /**
@@ -1,9 +1,13 @@
1
- import { S as ApplicationError, b as ERROR_CODES } from "../errors-BRJgVd5-.mjs";
2
- import { a as __decorate, d as Transient, g as DI_TOKENS, o as __decorateParam, s as __decorateMetadata, u as LOGGER_TOKENS } from "../logger-Bg-CuidS.mjs";
3
- import { r as Module } from "../module-Dvzm4dhS.mjs";
4
- import "../events-CQyvSyrQ.mjs";
5
- import "../middleware-B3tx1u1K.mjs";
6
- import "../router-context-DlTxpJUG.mjs";
1
+ import { S as ApplicationError, b as ERROR_CODES } from "../errors-CtCi1wn6.mjs";
2
+ import { i as Transient, l as DI_TOKENS, n as __decorateParam, r as __decorateMetadata, t as __decorate } from "../decorate-D5j-d9_z.mjs";
3
+ import { s as LOGGER_TOKENS } from "../logger-BR1-s1Um.mjs";
4
+ import { r as Module } from "../module-BH7t7BGG.mjs";
5
+ import "../events-CXl-o1Ad.mjs";
6
+ import "../command-DG_u5ob2.mjs";
7
+ import "../is-command-MZDCH-0T.mjs";
8
+ import "../is-seeder-BN9Ej1r7.mjs";
9
+ import "../middleware-iRhNjsPH.mjs";
10
+ import "../router-context-BLn4PrRG.mjs";
7
11
  import { inject } from "tsyringe";
8
12
  //#region src/cache/cache.tokens.ts
9
13
  const CACHE_TOKENS = { CacheService: Symbol.for("stratal:cache:service") };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cache/cache.tokens.ts","../../src/cache/errors/cache-get.error.ts","../../src/cache/errors/cache-put.error.ts","../../src/cache/errors/cache-delete.error.ts","../../src/cache/errors/cache-list.error.ts","../../src/cache/services/cache.service.ts","../../src/cache/cache.module.ts"],"sourcesContent":["export const CACHE_TOKENS = {\n CacheService: Symbol.for('stratal:cache:service'),\n} as const\n\nexport type CacheToken = (typeof CACHE_TOKENS)[keyof typeof CACHE_TOKENS]\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache get operation fails\n *\n * Raw error details are logged via LoggerService for security.\n * Only the key is included in the user-facing error message.\n */\nexport class CacheGetError extends ApplicationError {\n constructor(key: string) {\n super('errors.cache.getFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { key })\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache put operation fails\n *\n * Raw error details are logged via LoggerService for security.\n * Only the key is included in the user-facing error message.\n */\nexport class CachePutError extends ApplicationError {\n constructor(key: string) {\n super('errors.cache.putFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { key })\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache delete operation fails\n *\n * Raw error details are logged via LoggerService for security.\n * Only the key is included in the user-facing error message.\n */\nexport class CacheDeleteError extends ApplicationError {\n constructor(key: string) {\n super('errors.cache.deleteFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { key })\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache list operation fails\n *\n * Raw error details are logged via LoggerService for security.\n */\nexport class CacheListError extends ApplicationError {\n constructor() {\n super('errors.cache.listFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, {})\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type StratalEnv } from '../../env'\nimport { LOGGER_TOKENS, type LoggerService } from '../../logger'\nimport { CACHE_TOKENS } from '../cache.tokens'\nimport {\n CacheDeleteError,\n CacheGetError,\n CacheListError,\n CachePutError,\n} from '../errors'\n\n/**\n * Cache Service\n *\n * Type-safe wrapper around Cloudflare KV namespaces for caching operations.\n *\n * **Features:**\n * - Mirrors all KVNamespace methods with full type safety\n * - Supports multiple KV bindings via `withBinding()`\n * - Automatic error handling with logging\n * - Security: Raw errors are logged, not exposed to users\n *\n * **Usage:**\n * ```typescript\n * class MyService {\n * private readonly uploadsCache: CacheService\n *\n * constructor(\n * @inject(CACHE_TOKENS.CacheService) private readonly cache: CacheService,\n * @inject(DI_TOKENS.CloudflareEnv) private readonly env: Env\n * ) {\n * // Initialize specialized caches in constructor\n * this.uploadsCache = this.cache.withBinding(this.env.UPLOADS_CACHE)\n * }\n *\n * async cacheData(key: string, value: string) {\n * await this.cache.put(key, value, { expirationTtl: 3600 })\n * await this.uploadsCache.put(`upload:${key}`, value)\n * }\n * }\n * ```\n *\n * @see https://developers.cloudflare.com/kv/api/\n */\n@Transient(CACHE_TOKENS.CacheService)\nexport class CacheService {\n private kv: KVNamespace\n\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(LOGGER_TOKENS.LoggerService) private readonly logger: LoggerService\n ) {\n this.kv = env.CACHE\n }\n\n /**\n * Set the KV namespace binding\n *\n * Used internally by `withBinding()` to configure different KV instances.\n *\n * @param kv - KV namespace to use\n */\n setKV(kv: KVNamespace): void {\n this.kv = kv\n }\n\n /**\n * Create a new CacheService instance with a different KV binding\n *\n * **Pattern:** Returns a new instance (immutable)\n *\n * **Best Practice:** Initialize specialized caches as class properties in constructor\n *\n * @example\n * ```typescript\n * class MyService {\n * private readonly uploadsCache: CacheService\n * private readonly systemCache: CacheService\n *\n * constructor(\n * @inject(CACHE_TOKENS.CacheService) private readonly cache: CacheService,\n * @inject(DI_TOKENS.CloudflareEnv) private readonly env: Env\n * ) {\n * this.uploadsCache = this.cache.withBinding(this.env.UPLOADS_CACHE)\n * this.systemCache = this.cache.withBinding(this.env.SYSTEM_CONFIG_KV)\n * }\n * }\n * ```\n *\n * @param kv - KV namespace to use\n * @returns New CacheService instance with the specified binding\n */\n withBinding(kv: KVNamespace): CacheService {\n const instance = new CacheService(this.env, this.logger)\n instance.setKV(kv)\n return instance\n }\n\n // ==================== GET METHODS ====================\n\n /**\n * Get a value from cache\n *\n * @param key - Cache key\n * @param typeOrOptions - Type string or options object (defaults to 'text')\n * @returns Value in specified type, or null if not found\n * @throws {CacheGetError} If operation fails\n */\n async get(key: string, typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>): Promise<string | null>\n async get<ExpectedValue = unknown>(key: string, typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>): Promise<ExpectedValue | null>\n async get(key: string, typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>): Promise<ArrayBuffer | null>\n async get(key: string, typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>): Promise<ReadableStream | null>\n\n async get<ExpectedValue = unknown>(\n key: string,\n typeOrOptions?: string | KVNamespaceGetOptions<'text' | 'json' | 'arrayBuffer' | 'stream'>\n ): Promise<string | ExpectedValue | ArrayBuffer | ReadableStream | null> {\n try {\n if (typeof typeOrOptions === 'string') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.get(key, typeOrOptions as any)\n }\n\n if (typeOrOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.get(key, typeOrOptions as any)\n }\n\n return await this.kv.get(key)\n } catch (error) {\n this.logger.error('Cache get operation failed', { key, error })\n throw new CacheGetError(key)\n }\n }\n\n // ==================== GET WITH METADATA METHODS ====================\n\n /**\n * Get a value with metadata from cache\n *\n * @param key - Cache key\n * @param typeOrOptions - Type string or options object (defaults to 'text')\n * @returns Object with value, metadata, and cacheStatus\n * @throws {CacheGetError} If operation fails\n */\n async getWithMetadata<Metadata = unknown>(\n key: string,\n typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>\n ): Promise<KVNamespaceGetWithMetadataResult<string, Metadata>>\n async getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(\n key: string,\n typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>\n ): Promise<KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>\n async getWithMetadata<Metadata = unknown>(\n key: string,\n typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>\n ): Promise<KVNamespaceGetWithMetadataResult<ArrayBuffer, Metadata>>\n async getWithMetadata<Metadata = unknown>(\n key: string,\n typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>\n ): Promise<KVNamespaceGetWithMetadataResult<ReadableStream, Metadata>>\n\n async getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(\n key: string,\n typeOrOptions?: string | KVNamespaceGetOptions<'text' | 'json' | 'arrayBuffer' | 'stream'>\n ): Promise<\n KVNamespaceGetWithMetadataResult<\n string | ExpectedValue | ArrayBuffer | ReadableStream,\n Metadata\n >\n > {\n try {\n if (typeof typeOrOptions === 'string') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.getWithMetadata(key, typeOrOptions as any)\n }\n\n if (typeOrOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.getWithMetadata(key, typeOrOptions as any)\n }\n\n return await this.kv.getWithMetadata(key)\n } catch (error) {\n this.logger.error('Cache getWithMetadata operation failed', { key, error })\n throw new CacheGetError(key)\n }\n }\n\n // ==================== PUT METHOD ====================\n\n /**\n * Store a value in cache\n *\n * @param key - Cache key\n * @param value - Value to store (string, ArrayBuffer, ArrayBufferView, or ReadableStream)\n * @param options - Put options (expiration, expirationTtl, metadata)\n * @throws {CachePutError} If operation fails\n *\n * @example\n * ```typescript\n * // Simple put\n * await cache.put('key', 'value')\n *\n * // With TTL\n * await cache.put('key', 'value', { expirationTtl: 3600 })\n *\n * // With metadata\n * await cache.put('key', 'value', { metadata: { created: Date.now() } })\n * ```\n */\n async put(\n key: string,\n value: string | ArrayBuffer | ArrayBufferView | ReadableStream,\n options?: KVNamespacePutOptions\n ): Promise<void> {\n try {\n await this.kv.put(key, value as string, options)\n } catch (error) {\n this.logger.error('Cache put operation failed', { key, error })\n throw new CachePutError(key)\n }\n }\n\n // ==================== DELETE METHODS ====================\n\n /**\n * Delete a value from cache\n *\n * @param key - Cache key to delete\n * @throws {CacheDeleteError} If operation fails\n */\n async delete(key: string): Promise<void> {\n try {\n await this.kv.delete(key)\n } catch (error) {\n this.logger.error('Cache delete operation failed', { key, error })\n throw new CacheDeleteError(key)\n }\n }\n\n\n // ==================== LIST METHOD ====================\n\n /**\n * List keys in cache\n *\n * @param options - List options (limit, prefix, cursor)\n * @returns List result with keys and pagination info\n * @throws {CacheListError} If operation fails\n *\n * @example\n * ```typescript\n * // List all keys\n * const result = await cache.list()\n *\n * // List with prefix\n * const result = await cache.list({ prefix: 'user:' })\n *\n * // Paginated list\n * const result = await cache.list({ limit: 100 })\n * if (!result.list_complete) {\n * const nextPage = await cache.list({ cursor: result.cursor })\n * }\n * ```\n */\n async list<Metadata = unknown>(\n options?: KVNamespaceListOptions\n ): Promise<KVNamespaceListResult<Metadata>> {\n try {\n return await this.kv.list<Metadata>(options)\n } catch (error) {\n this.logger.error('Cache list operation failed', { options, error })\n throw new CacheListError()\n }\n }\n}\n","/**\n * Cache Module\n *\n * Provides key-value caching capabilities using Cloudflare KV namespaces.\n *\n * **Features:**\n * - Type-safe KV wrapper with full method coverage\n * - Multiple KV binding support via `withBinding()`\n * - Automatic error handling with security-focused logging\n * - Singleton service for optimal performance\n */\n\nimport { Module } from '../module'\nimport { CACHE_TOKENS } from './cache.tokens'\nimport { CacheService } from './services'\n\n@Module({\n providers: [\n // Singleton - CacheService has no request dependencies\n { provide: CACHE_TOKENS.CacheService, useClass: CacheService },\n ],\n})\nexport class CacheModule {}\n"],"mappings":";;;;;;;;AAAA,MAAa,eAAe,EAC1B,cAAc,OAAO,IAAI,wBAAwB,EAClD;;;;;;;;;ACOD,IAAa,gBAAb,cAAmC,iBAAiB;CAClD,YAAY,KAAa;AACvB,QAAM,0BAA0B,YAAY,OAAO,sBAAsB,EAAE,KAAK,CAAC;;;;;;;;;;;ACFrF,IAAa,gBAAb,cAAmC,iBAAiB;CAClD,YAAY,KAAa;AACvB,QAAM,0BAA0B,YAAY,OAAO,sBAAsB,EAAE,KAAK,CAAC;;;;;;;;;;;ACFrF,IAAa,mBAAb,cAAsC,iBAAiB;CACrD,YAAY,KAAa;AACvB,QAAM,6BAA6B,YAAY,OAAO,sBAAsB,EAAE,KAAK,CAAC;;;;;;;;;;ACHxF,IAAa,iBAAb,cAAoC,iBAAiB;CACnD,cAAc;AACZ,QAAM,2BAA2B,YAAY,OAAO,sBAAsB,EAAE,CAAC;;;;;;ACqC1E,IAAA,eAAA,gBAAA,MAAM,aAAa;CACxB;CAEA,YACE,KACA,QACA;AAFkD,OAAA,MAAA;AACI,OAAA,SAAA;AAEtD,OAAK,KAAK,IAAI;;;;;;;;;CAUhB,MAAM,IAAuB;AAC3B,OAAK,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BZ,YAAY,IAA+B;EACzC,MAAM,WAAW,IAAA,cAAiB,KAAK,KAAK,KAAK,OAAO;AACxD,WAAS,MAAM,GAAG;AAClB,SAAO;;CAkBT,MAAM,IACJ,KACA,eACuE;AACvE,MAAI;AACF,OAAI,OAAO,kBAAkB,SAE3B,QAAO,MAAM,KAAK,GAAG,IAAI,KAAK,cAAqB;AAGrD,OAAI,cAEF,QAAO,MAAM,KAAK,GAAG,IAAI,KAAK,cAAqB;AAGrD,UAAO,MAAM,KAAK,GAAG,IAAI,IAAI;WACtB,OAAO;AACd,QAAK,OAAO,MAAM,8BAA8B;IAAE;IAAK;IAAO,CAAC;AAC/D,SAAM,IAAI,cAAc,IAAI;;;CA+BhC,MAAM,gBACJ,KACA,eAMA;AACA,MAAI;AACF,OAAI,OAAO,kBAAkB,SAE3B,QAAO,MAAM,KAAK,GAAG,gBAAgB,KAAK,cAAqB;AAGjE,OAAI,cAEF,QAAO,MAAM,KAAK,GAAG,gBAAgB,KAAK,cAAqB;AAGjE,UAAO,MAAM,KAAK,GAAG,gBAAgB,IAAI;WAClC,OAAO;AACd,QAAK,OAAO,MAAM,0CAA0C;IAAE;IAAK;IAAO,CAAC;AAC3E,SAAM,IAAI,cAAc,IAAI;;;;;;;;;;;;;;;;;;;;;;;CA0BhC,MAAM,IACJ,KACA,OACA,SACe;AACf,MAAI;AACF,SAAM,KAAK,GAAG,IAAI,KAAK,OAAiB,QAAQ;WACzC,OAAO;AACd,QAAK,OAAO,MAAM,8BAA8B;IAAE;IAAK;IAAO,CAAC;AAC/D,SAAM,IAAI,cAAc,IAAI;;;;;;;;;CAYhC,MAAM,OAAO,KAA4B;AACvC,MAAI;AACF,SAAM,KAAK,GAAG,OAAO,IAAI;WAClB,OAAO;AACd,QAAK,OAAO,MAAM,iCAAiC;IAAE;IAAK;IAAO,CAAC;AAClE,SAAM,IAAI,iBAAiB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;CA6BnC,MAAM,KACJ,SAC0C;AAC1C,MAAI;AACF,UAAO,MAAM,KAAK,GAAG,KAAe,QAAQ;WACrC,OAAO;AACd,QAAK,OAAO,MAAM,+BAA+B;IAAE;IAAS;IAAO,CAAC;AACpE,SAAM,IAAI,gBAAgB;;;;;CArO/B,UAAU,aAAa,aAAa;oBAKhC,OAAO,UAAU,cAAc,CAAA;oBAC/B,OAAO,cAAc,cAAc,CAAA;;;;;;;;;;;;;;;;AC9BjC,IAAA,cAAA,MAAM,YAAY;0BANxB,OAAO,EACN,WAAW,CAET;CAAE,SAAS,aAAa;CAAc,UAAU;CAAc,CAC/D,EACF,CAAC,CAAA,EAAA,YAAA"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/cache/cache.tokens.ts","../../src/cache/errors/cache-get.error.ts","../../src/cache/errors/cache-put.error.ts","../../src/cache/errors/cache-delete.error.ts","../../src/cache/errors/cache-list.error.ts","../../src/cache/services/cache.service.ts","../../src/cache/cache.module.ts"],"sourcesContent":["export const CACHE_TOKENS = {\n CacheService: Symbol.for('stratal:cache:service'),\n} as const\n\nexport type CacheToken = (typeof CACHE_TOKENS)[keyof typeof CACHE_TOKENS]\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache get operation fails\n *\n * Raw error details are logged via LoggerService for security.\n * Only the key is included in the user-facing error message.\n */\nexport class CacheGetError extends ApplicationError {\n constructor(key: string) {\n super('errors.cache.getFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { key })\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache put operation fails\n *\n * Raw error details are logged via LoggerService for security.\n * Only the key is included in the user-facing error message.\n */\nexport class CachePutError extends ApplicationError {\n constructor(key: string) {\n super('errors.cache.putFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { key })\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache delete operation fails\n *\n * Raw error details are logged via LoggerService for security.\n * Only the key is included in the user-facing error message.\n */\nexport class CacheDeleteError extends ApplicationError {\n constructor(key: string) {\n super('errors.cache.deleteFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { key })\n }\n}\n","import { ERROR_CODES } from '../../errors'\nimport { ApplicationError } from '../../errors'\n\n/**\n * Error thrown when a cache list operation fails\n *\n * Raw error details are logged via LoggerService for security.\n */\nexport class CacheListError extends ApplicationError {\n constructor() {\n super('errors.cache.listFailed', ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, {})\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { DI_TOKENS } from '../../di/tokens'\nimport { type StratalEnv } from '../../env'\nimport { LOGGER_TOKENS, type LoggerService } from '../../logger'\nimport { CACHE_TOKENS } from '../cache.tokens'\nimport {\n CacheDeleteError,\n CacheGetError,\n CacheListError,\n CachePutError,\n} from '../errors'\n\n/**\n * Cache Service\n *\n * Type-safe wrapper around Cloudflare KV namespaces for caching operations.\n *\n * **Features:**\n * - Mirrors all KVNamespace methods with full type safety\n * - Supports multiple KV bindings via `withBinding()`\n * - Automatic error handling with logging\n * - Security: Raw errors are logged, not exposed to users\n *\n * **Usage:**\n * ```typescript\n * class MyService {\n * private readonly uploadsCache: CacheService\n *\n * constructor(\n * @inject(CACHE_TOKENS.CacheService) private readonly cache: CacheService,\n * @inject(DI_TOKENS.CloudflareEnv) private readonly env: Env\n * ) {\n * // Initialize specialized caches in constructor\n * this.uploadsCache = this.cache.withBinding(this.env.UPLOADS_CACHE)\n * }\n *\n * async cacheData(key: string, value: string) {\n * await this.cache.put(key, value, { expirationTtl: 3600 })\n * await this.uploadsCache.put(`upload:${key}`, value)\n * }\n * }\n * ```\n *\n * @see https://developers.cloudflare.com/kv/api/\n */\n@Transient(CACHE_TOKENS.CacheService)\nexport class CacheService {\n private kv: KVNamespace\n\n constructor(\n @inject(DI_TOKENS.CloudflareEnv) private readonly env: StratalEnv,\n @inject(LOGGER_TOKENS.LoggerService) private readonly logger: LoggerService\n ) {\n this.kv = env.CACHE\n }\n\n /**\n * Set the KV namespace binding\n *\n * Used internally by `withBinding()` to configure different KV instances.\n *\n * @param kv - KV namespace to use\n */\n setKV(kv: KVNamespace): void {\n this.kv = kv\n }\n\n /**\n * Create a new CacheService instance with a different KV binding\n *\n * **Pattern:** Returns a new instance (immutable)\n *\n * **Best Practice:** Initialize specialized caches as class properties in constructor\n *\n * @example\n * ```typescript\n * class MyService {\n * private readonly uploadsCache: CacheService\n * private readonly systemCache: CacheService\n *\n * constructor(\n * @inject(CACHE_TOKENS.CacheService) private readonly cache: CacheService,\n * @inject(DI_TOKENS.CloudflareEnv) private readonly env: Env\n * ) {\n * this.uploadsCache = this.cache.withBinding(this.env.UPLOADS_CACHE)\n * this.systemCache = this.cache.withBinding(this.env.SYSTEM_CONFIG_KV)\n * }\n * }\n * ```\n *\n * @param kv - KV namespace to use\n * @returns New CacheService instance with the specified binding\n */\n withBinding(kv: KVNamespace): CacheService {\n const instance = new CacheService(this.env, this.logger)\n instance.setKV(kv)\n return instance\n }\n\n // ==================== GET METHODS ====================\n\n /**\n * Get a value from cache\n *\n * @param key - Cache key\n * @param typeOrOptions - Type string or options object (defaults to 'text')\n * @returns Value in specified type, or null if not found\n * @throws {CacheGetError} If operation fails\n */\n async get(key: string, typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>): Promise<string | null>\n async get<ExpectedValue = unknown>(key: string, typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>): Promise<ExpectedValue | null>\n async get(key: string, typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>): Promise<ArrayBuffer | null>\n async get(key: string, typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>): Promise<ReadableStream | null>\n\n async get<ExpectedValue = unknown>(\n key: string,\n typeOrOptions?: string | KVNamespaceGetOptions<'text' | 'json' | 'arrayBuffer' | 'stream'>\n ): Promise<string | ExpectedValue | ArrayBuffer | ReadableStream | null> {\n try {\n if (typeof typeOrOptions === 'string') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.get(key, typeOrOptions as any)\n }\n\n if (typeOrOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.get(key, typeOrOptions as any)\n }\n\n return await this.kv.get(key)\n } catch (error) {\n this.logger.error('Cache get operation failed', { key, error })\n throw new CacheGetError(key)\n }\n }\n\n // ==================== GET WITH METADATA METHODS ====================\n\n /**\n * Get a value with metadata from cache\n *\n * @param key - Cache key\n * @param typeOrOptions - Type string or options object (defaults to 'text')\n * @returns Object with value, metadata, and cacheStatus\n * @throws {CacheGetError} If operation fails\n */\n async getWithMetadata<Metadata = unknown>(\n key: string,\n typeOrOptions?: 'text' | KVNamespaceGetOptions<'text'>\n ): Promise<KVNamespaceGetWithMetadataResult<string, Metadata>>\n async getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(\n key: string,\n typeOrOptions: 'json' | KVNamespaceGetOptions<'json'>\n ): Promise<KVNamespaceGetWithMetadataResult<ExpectedValue, Metadata>>\n async getWithMetadata<Metadata = unknown>(\n key: string,\n typeOrOptions: 'arrayBuffer' | KVNamespaceGetOptions<'arrayBuffer'>\n ): Promise<KVNamespaceGetWithMetadataResult<ArrayBuffer, Metadata>>\n async getWithMetadata<Metadata = unknown>(\n key: string,\n typeOrOptions: 'stream' | KVNamespaceGetOptions<'stream'>\n ): Promise<KVNamespaceGetWithMetadataResult<ReadableStream, Metadata>>\n\n async getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(\n key: string,\n typeOrOptions?: string | KVNamespaceGetOptions<'text' | 'json' | 'arrayBuffer' | 'stream'>\n ): Promise<\n KVNamespaceGetWithMetadataResult<\n string | ExpectedValue | ArrayBuffer | ReadableStream,\n Metadata\n >\n > {\n try {\n if (typeof typeOrOptions === 'string') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.getWithMetadata(key, typeOrOptions as any)\n }\n\n if (typeOrOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any -- bridging KV overloaded API\n return await this.kv.getWithMetadata(key, typeOrOptions as any)\n }\n\n return await this.kv.getWithMetadata(key)\n } catch (error) {\n this.logger.error('Cache getWithMetadata operation failed', { key, error })\n throw new CacheGetError(key)\n }\n }\n\n // ==================== PUT METHOD ====================\n\n /**\n * Store a value in cache\n *\n * @param key - Cache key\n * @param value - Value to store (string, ArrayBuffer, ArrayBufferView, or ReadableStream)\n * @param options - Put options (expiration, expirationTtl, metadata)\n * @throws {CachePutError} If operation fails\n *\n * @example\n * ```typescript\n * // Simple put\n * await cache.put('key', 'value')\n *\n * // With TTL\n * await cache.put('key', 'value', { expirationTtl: 3600 })\n *\n * // With metadata\n * await cache.put('key', 'value', { metadata: { created: Date.now() } })\n * ```\n */\n async put(\n key: string,\n value: string | ArrayBuffer | ArrayBufferView | ReadableStream,\n options?: KVNamespacePutOptions\n ): Promise<void> {\n try {\n await this.kv.put(key, value as string, options)\n } catch (error) {\n this.logger.error('Cache put operation failed', { key, error })\n throw new CachePutError(key)\n }\n }\n\n // ==================== DELETE METHODS ====================\n\n /**\n * Delete a value from cache\n *\n * @param key - Cache key to delete\n * @throws {CacheDeleteError} If operation fails\n */\n async delete(key: string): Promise<void> {\n try {\n await this.kv.delete(key)\n } catch (error) {\n this.logger.error('Cache delete operation failed', { key, error })\n throw new CacheDeleteError(key)\n }\n }\n\n\n // ==================== LIST METHOD ====================\n\n /**\n * List keys in cache\n *\n * @param options - List options (limit, prefix, cursor)\n * @returns List result with keys and pagination info\n * @throws {CacheListError} If operation fails\n *\n * @example\n * ```typescript\n * // List all keys\n * const result = await cache.list()\n *\n * // List with prefix\n * const result = await cache.list({ prefix: 'user:' })\n *\n * // Paginated list\n * const result = await cache.list({ limit: 100 })\n * if (!result.list_complete) {\n * const nextPage = await cache.list({ cursor: result.cursor })\n * }\n * ```\n */\n async list<Metadata = unknown>(\n options?: KVNamespaceListOptions\n ): Promise<KVNamespaceListResult<Metadata>> {\n try {\n return await this.kv.list<Metadata>(options)\n } catch (error) {\n this.logger.error('Cache list operation failed', { options, error })\n throw new CacheListError()\n }\n }\n}\n","/**\n * Cache Module\n *\n * Provides key-value caching capabilities using Cloudflare KV namespaces.\n *\n * **Features:**\n * - Type-safe KV wrapper with full method coverage\n * - Multiple KV binding support via `withBinding()`\n * - Automatic error handling with security-focused logging\n * - Singleton service for optimal performance\n */\n\nimport { Module } from '../module'\nimport { CACHE_TOKENS } from './cache.tokens'\nimport { CacheService } from './services'\n\n@Module({\n providers: [\n // Singleton - CacheService has no request dependencies\n { provide: CACHE_TOKENS.CacheService, useClass: CacheService },\n ],\n})\nexport class CacheModule {}\n"],"mappings":"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"}