@dexcost/sdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/adapters/_netbytes.d.ts +31 -0
  4. package/dist/adapters/_netbytes.d.ts.map +1 -0
  5. package/dist/adapters/_netbytes.js +154 -0
  6. package/dist/adapters/_netbytes.js.map +1 -0
  7. package/dist/adapters/aws-lambda.d.ts +41 -0
  8. package/dist/adapters/aws-lambda.d.ts.map +1 -0
  9. package/dist/adapters/aws-lambda.js +65 -0
  10. package/dist/adapters/aws-lambda.js.map +1 -0
  11. package/dist/adapters/browser.d.ts +52 -0
  12. package/dist/adapters/browser.d.ts.map +1 -0
  13. package/dist/adapters/browser.js +127 -0
  14. package/dist/adapters/browser.js.map +1 -0
  15. package/dist/adapters/compute-wrap.d.ts +33 -0
  16. package/dist/adapters/compute-wrap.d.ts.map +1 -0
  17. package/dist/adapters/compute-wrap.js +188 -0
  18. package/dist/adapters/compute-wrap.js.map +1 -0
  19. package/dist/adapters/data/aws_lambda_pricing.json +61 -0
  20. package/dist/adapters/gpu-wrap.d.ts +31 -0
  21. package/dist/adapters/gpu-wrap.d.ts.map +1 -0
  22. package/dist/adapters/gpu-wrap.js +147 -0
  23. package/dist/adapters/gpu-wrap.js.map +1 -0
  24. package/dist/adapters/http.d.ts +58 -0
  25. package/dist/adapters/http.d.ts.map +1 -0
  26. package/dist/adapters/http.js +769 -0
  27. package/dist/adapters/http.js.map +1 -0
  28. package/dist/adapters/index.d.ts +11 -0
  29. package/dist/adapters/index.d.ts.map +1 -0
  30. package/dist/adapters/index.js +12 -0
  31. package/dist/adapters/index.js.map +1 -0
  32. package/dist/adapters/network-accountant.d.ts +63 -0
  33. package/dist/adapters/network-accountant.d.ts.map +1 -0
  34. package/dist/adapters/network-accountant.js +153 -0
  35. package/dist/adapters/network-accountant.js.map +1 -0
  36. package/dist/cli/index.d.ts +13 -0
  37. package/dist/cli/index.d.ts.map +1 -0
  38. package/dist/cli/index.js +225 -0
  39. package/dist/cli/index.js.map +1 -0
  40. package/dist/cli/scanner.d.ts +39 -0
  41. package/dist/cli/scanner.d.ts.map +1 -0
  42. package/dist/cli/scanner.js +480 -0
  43. package/dist/cli/scanner.js.map +1 -0
  44. package/dist/clients.d.ts +54 -0
  45. package/dist/clients.d.ts.map +1 -0
  46. package/dist/clients.js +240 -0
  47. package/dist/clients.js.map +1 -0
  48. package/dist/cloud-detect.d.ts +96 -0
  49. package/dist/cloud-detect.d.ts.map +1 -0
  50. package/dist/cloud-detect.js +545 -0
  51. package/dist/cloud-detect.js.map +1 -0
  52. package/dist/core/auto-task.d.ts +20 -0
  53. package/dist/core/auto-task.d.ts.map +1 -0
  54. package/dist/core/auto-task.js +34 -0
  55. package/dist/core/auto-task.js.map +1 -0
  56. package/dist/core/cgroup-reader.d.ts +45 -0
  57. package/dist/core/cgroup-reader.d.ts.map +1 -0
  58. package/dist/core/cgroup-reader.js +124 -0
  59. package/dist/core/cgroup-reader.js.map +1 -0
  60. package/dist/core/cgroup-walker.d.ts +60 -0
  61. package/dist/core/cgroup-walker.d.ts.map +1 -0
  62. package/dist/core/cgroup-walker.js +166 -0
  63. package/dist/core/cgroup-walker.js.map +1 -0
  64. package/dist/core/compute-accountant.d.ts +51 -0
  65. package/dist/core/compute-accountant.d.ts.map +1 -0
  66. package/dist/core/compute-accountant.js +179 -0
  67. package/dist/core/compute-accountant.js.map +1 -0
  68. package/dist/core/compute-runtime.d.ts +42 -0
  69. package/dist/core/compute-runtime.d.ts.map +1 -0
  70. package/dist/core/compute-runtime.js +80 -0
  71. package/dist/core/compute-runtime.js.map +1 -0
  72. package/dist/core/config.d.ts +44 -0
  73. package/dist/core/config.d.ts.map +1 -0
  74. package/dist/core/config.js +66 -0
  75. package/dist/core/config.js.map +1 -0
  76. package/dist/core/context.d.ts +76 -0
  77. package/dist/core/context.d.ts.map +1 -0
  78. package/dist/core/context.js +91 -0
  79. package/dist/core/context.js.map +1 -0
  80. package/dist/core/fargate-metadata.d.ts +27 -0
  81. package/dist/core/fargate-metadata.d.ts.map +1 -0
  82. package/dist/core/fargate-metadata.js +102 -0
  83. package/dist/core/fargate-metadata.js.map +1 -0
  84. package/dist/core/gpu-accountant.d.ts +104 -0
  85. package/dist/core/gpu-accountant.d.ts.map +1 -0
  86. package/dist/core/gpu-accountant.js +383 -0
  87. package/dist/core/gpu-accountant.js.map +1 -0
  88. package/dist/core/gpu-runtime.d.ts +58 -0
  89. package/dist/core/gpu-runtime.d.ts.map +1 -0
  90. package/dist/core/gpu-runtime.js +131 -0
  91. package/dist/core/gpu-runtime.js.map +1 -0
  92. package/dist/core/heuristics.d.ts +74 -0
  93. package/dist/core/heuristics.d.ts.map +1 -0
  94. package/dist/core/heuristics.js +182 -0
  95. package/dist/core/heuristics.js.map +1 -0
  96. package/dist/core/models.d.ts +149 -0
  97. package/dist/core/models.d.ts.map +1 -0
  98. package/dist/core/models.js +226 -0
  99. package/dist/core/models.js.map +1 -0
  100. package/dist/core/nvml-reader.d.ts +114 -0
  101. package/dist/core/nvml-reader.d.ts.map +1 -0
  102. package/dist/core/nvml-reader.js +323 -0
  103. package/dist/core/nvml-reader.js.map +1 -0
  104. package/dist/core/session.d.ts +48 -0
  105. package/dist/core/session.d.ts.map +1 -0
  106. package/dist/core/session.js +123 -0
  107. package/dist/core/session.js.map +1 -0
  108. package/dist/core/tracker.d.ts +364 -0
  109. package/dist/core/tracker.d.ts.map +1 -0
  110. package/dist/core/tracker.js +1073 -0
  111. package/dist/core/tracker.js.map +1 -0
  112. package/dist/data/compute_prices.json +180 -0
  113. package/dist/data/egress_prices.json +418 -0
  114. package/dist/data/gpu_prices.json +412 -0
  115. package/dist/data/service_prices.json +2595 -0
  116. package/dist/dev-console.d.ts +12 -0
  117. package/dist/dev-console.d.ts.map +1 -0
  118. package/dist/dev-console.js +60 -0
  119. package/dist/dev-console.js.map +1 -0
  120. package/dist/index.d.ts +52 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +61 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/instruments/anthropic.d.ts +26 -0
  125. package/dist/instruments/anthropic.d.ts.map +1 -0
  126. package/dist/instruments/anthropic.js +242 -0
  127. package/dist/instruments/anthropic.js.map +1 -0
  128. package/dist/instruments/bedrock.d.ts +29 -0
  129. package/dist/instruments/bedrock.d.ts.map +1 -0
  130. package/dist/instruments/bedrock.js +215 -0
  131. package/dist/instruments/bedrock.js.map +1 -0
  132. package/dist/instruments/cohere.d.ts +29 -0
  133. package/dist/instruments/cohere.d.ts.map +1 -0
  134. package/dist/instruments/cohere.js +237 -0
  135. package/dist/instruments/cohere.js.map +1 -0
  136. package/dist/instruments/gemini.d.ts +30 -0
  137. package/dist/instruments/gemini.d.ts.map +1 -0
  138. package/dist/instruments/gemini.js +247 -0
  139. package/dist/instruments/gemini.js.map +1 -0
  140. package/dist/instruments/index.d.ts +35 -0
  141. package/dist/instruments/index.d.ts.map +1 -0
  142. package/dist/instruments/index.js +54 -0
  143. package/dist/instruments/index.js.map +1 -0
  144. package/dist/instruments/mcp.d.ts +24 -0
  145. package/dist/instruments/mcp.d.ts.map +1 -0
  146. package/dist/instruments/mcp.js +459 -0
  147. package/dist/instruments/mcp.js.map +1 -0
  148. package/dist/instruments/openai.d.ts +26 -0
  149. package/dist/instruments/openai.d.ts.map +1 -0
  150. package/dist/instruments/openai.js +221 -0
  151. package/dist/instruments/openai.js.map +1 -0
  152. package/dist/instruments/vercel-ai.d.ts +28 -0
  153. package/dist/instruments/vercel-ai.d.ts.map +1 -0
  154. package/dist/instruments/vercel-ai.js +192 -0
  155. package/dist/instruments/vercel-ai.js.map +1 -0
  156. package/dist/integrations/langchain.d.ts +65 -0
  157. package/dist/integrations/langchain.d.ts.map +1 -0
  158. package/dist/integrations/langchain.js +165 -0
  159. package/dist/integrations/langchain.js.map +1 -0
  160. package/dist/middleware/express.d.ts +55 -0
  161. package/dist/middleware/express.d.ts.map +1 -0
  162. package/dist/middleware/express.js +101 -0
  163. package/dist/middleware/express.js.map +1 -0
  164. package/dist/middleware/index.d.ts +6 -0
  165. package/dist/middleware/index.d.ts.map +1 -0
  166. package/dist/middleware/index.js +5 -0
  167. package/dist/middleware/index.js.map +1 -0
  168. package/dist/pricing/compute-pricing.d.ts +57 -0
  169. package/dist/pricing/compute-pricing.d.ts.map +1 -0
  170. package/dist/pricing/compute-pricing.js +627 -0
  171. package/dist/pricing/compute-pricing.js.map +1 -0
  172. package/dist/pricing/cost_map.json +37665 -0
  173. package/dist/pricing/egress-pricing.d.ts +55 -0
  174. package/dist/pricing/egress-pricing.d.ts.map +1 -0
  175. package/dist/pricing/egress-pricing.js +226 -0
  176. package/dist/pricing/egress-pricing.js.map +1 -0
  177. package/dist/pricing/engine.d.ts +24 -0
  178. package/dist/pricing/engine.d.ts.map +1 -0
  179. package/dist/pricing/engine.js +148 -0
  180. package/dist/pricing/engine.js.map +1 -0
  181. package/dist/pricing/gpu-pricing.d.ts +63 -0
  182. package/dist/pricing/gpu-pricing.d.ts.map +1 -0
  183. package/dist/pricing/gpu-pricing.js +484 -0
  184. package/dist/pricing/gpu-pricing.js.map +1 -0
  185. package/dist/pricing/rates.d.ts +17 -0
  186. package/dist/pricing/rates.d.ts.map +1 -0
  187. package/dist/pricing/rates.js +102 -0
  188. package/dist/pricing/rates.js.map +1 -0
  189. package/dist/pricing/service-catalog.d.ts +87 -0
  190. package/dist/pricing/service-catalog.d.ts.map +1 -0
  191. package/dist/pricing/service-catalog.js +406 -0
  192. package/dist/pricing/service-catalog.js.map +1 -0
  193. package/dist/schema/dexcost-event.v1.json +111 -0
  194. package/dist/schema/dexcost-task.v1.json +160 -0
  195. package/dist/schema/validate.d.ts +15 -0
  196. package/dist/schema/validate.d.ts.map +1 -0
  197. package/dist/schema/validate.js +87 -0
  198. package/dist/schema/validate.js.map +1 -0
  199. package/dist/security/redaction.d.ts +55 -0
  200. package/dist/security/redaction.d.ts.map +1 -0
  201. package/dist/security/redaction.js +144 -0
  202. package/dist/security/redaction.js.map +1 -0
  203. package/dist/transport/buffer.d.ts +117 -0
  204. package/dist/transport/buffer.d.ts.map +1 -0
  205. package/dist/transport/buffer.js +759 -0
  206. package/dist/transport/buffer.js.map +1 -0
  207. package/dist/transport/pusher.d.ts +89 -0
  208. package/dist/transport/pusher.d.ts.map +1 -0
  209. package/dist/transport/pusher.js +323 -0
  210. package/dist/transport/pusher.js.map +1 -0
  211. package/package.json +93 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Browser cost adapter — automatic cost tracking for Playwright sessions.
3
+ *
4
+ * `trackBrowser` wraps a block of Playwright work, measures wall-clock
5
+ * time, and records a `compute_cost` event proportional to session
6
+ * duration. Mirrors the Python SDK's `adapters/browser.py`.
7
+ *
8
+ * `playwright` is an optional peer dependency — this adapter is duck-typed
9
+ * and never imports it, so it works with any object exposing a `.url`.
10
+ *
11
+ * Usage:
12
+ *
13
+ * import { trackBrowser } from "dexcost";
14
+ *
15
+ * await tracker.track({ taskType: "scrape" }, async () => {
16
+ * await trackBrowser(page, async () => {
17
+ * await page.goto("https://example.com");
18
+ * }, { ratePerMinute: 0.01 });
19
+ * });
20
+ */
21
+ import { randomUUID } from "node:crypto";
22
+ import { getCurrentTask } from "../core/context.js";
23
+ import { createCostEvent } from "../core/models.js";
24
+ import { scrubUrl } from "../security/redaction.js";
25
+ /* eslint-disable @typescript-eslint/no-explicit-any */
26
+ /** Default browser usage rate in USD per minute. */
27
+ const DEFAULT_RATE_PER_MINUTE = 0.01;
28
+ /** Events recorded by the browser adapter. */
29
+ /** Events recorded by the browser adapter.
30
+ *
31
+ * Sprint 4 §5.2 (A3) — hard FIFO cap matching Python (c1d87a7) and
32
+ * the TS http adapter. Long-running Playwright sessions otherwise
33
+ * leak ~250 bytes per recording.
34
+ */
35
+ const _RECORDED_EVENTS_CAP = 10_000;
36
+ const _recordedEvents = [];
37
+ /**
38
+ * Durable storage buffer wired by {@link setBrowserBuffer}. When set, recorded
39
+ * browser cost events are persisted (and shipped by the EventPusher) instead
40
+ * of only being kept in the in-memory `_recordedEvents` list.
41
+ */
42
+ let _buffer = null;
43
+ /**
44
+ * Wire the browser adapter to a storage buffer. The CostTracker calls this on
45
+ * construction so `trackBrowser()` cost events reach SQLite and the sync
46
+ * pusher. Pass `null` to detach (events then stay in-memory only).
47
+ */
48
+ export function setBrowserBuffer(buffer) {
49
+ _buffer = buffer;
50
+ }
51
+ /** Return all events recorded by the browser adapter since the last clear. */
52
+ export function getBrowserEvents() {
53
+ return [..._recordedEvents];
54
+ }
55
+ /** Clear the browser adapter's recorded events list. */
56
+ export function clearBrowserEvents() {
57
+ _recordedEvents.length = 0;
58
+ }
59
+ /**
60
+ * Run `fn` while measuring browser session wall-clock time, then record a
61
+ * `compute_cost` event with `costUsd = elapsedMinutes * ratePerMinute`.
62
+ *
63
+ * The event is only recorded when there is an active task in the context
64
+ * (via `getCurrentTask()`). When no task is active, the timing wrapper is
65
+ * a silent pass-through. The cost is always recorded even if `fn` throws.
66
+ *
67
+ * @param page - A Playwright `Page` (or any object with a `.url`). Not
68
+ * type-checked, to avoid requiring `playwright` as a hard dependency.
69
+ * @param fn - The browser work to run and time.
70
+ * @param options - Optional `ratePerMinute` override.
71
+ */
72
+ export async function trackBrowser(page, fn, options = {}) {
73
+ const ratePerMinute = options.ratePerMinute ?? DEFAULT_RATE_PER_MINUTE;
74
+ const start = performance.now();
75
+ try {
76
+ return await fn();
77
+ }
78
+ finally {
79
+ const elapsedSeconds = (performance.now() - start) / 1000;
80
+ _recordBrowserEvent(page, elapsedSeconds, ratePerMinute);
81
+ }
82
+ }
83
+ /** Record a compute_cost event for a browser session. No-op without a task. */
84
+ function _recordBrowserEvent(page, elapsedSeconds, ratePerMinute) {
85
+ const task = getCurrentTask();
86
+ if (task === undefined) {
87
+ return;
88
+ }
89
+ const elapsedMinutes = elapsedSeconds / 60;
90
+ const costUsd = elapsedMinutes * ratePerMinute;
91
+ let pageUrl = "";
92
+ try {
93
+ const url = page?.url;
94
+ const raw = typeof url === "function" ? String(url.call(page)) : String(url ?? "");
95
+ pageUrl = scrubUrl(raw);
96
+ }
97
+ catch {
98
+ // page may have closed — ignore
99
+ }
100
+ const event = createCostEvent({
101
+ eventId: randomUUID(),
102
+ taskId: task.taskId,
103
+ eventType: "compute_cost",
104
+ costUsd,
105
+ costConfidence: "computed",
106
+ pricingSource: "rate_registry",
107
+ serviceName: "playwright_browser",
108
+ isRetry: false,
109
+ details: {
110
+ wall_clock_seconds: Number(elapsedSeconds.toFixed(6)),
111
+ rate_per_minute: ratePerMinute,
112
+ page_url: pageUrl,
113
+ },
114
+ });
115
+ task.computeCostUsd += costUsd;
116
+ task.totalCostUsd += costUsd;
117
+ _recordedEvents.push(event);
118
+ if (_recordedEvents.length > _RECORDED_EVENTS_CAP) {
119
+ _recordedEvents.splice(0, _RECORDED_EVENTS_CAP / 10);
120
+ }
121
+ // Persist durably so the EventPusher ships the browser cost; the in-memory
122
+ // list above is kept for tests and lightweight setups.
123
+ if (_buffer) {
124
+ _buffer.addEvent(event);
125
+ }
126
+ }
127
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/adapters/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Serverless handler wraps for compute capture (node-only, browser-safe).
3
+ *
4
+ * Each wrap is a thin decorator that:
5
+ * 1. Reads runtime-specific env vars (memory limit, init type, region).
6
+ * 2. Creates a ComputeAccountant and attaches it to the active task.
7
+ * 3. Times the handler with performance.now().
8
+ * 4. Reads cgroup memory.peak at exit.
9
+ * 5. Builds the per-invocation compute_cost event with cost_pending=true
10
+ * and persists it via the global tracker's buffer.
11
+ * 6. TrackedTask.end()'s _finalizeCompute back-fills cost_usd at finalize.
12
+ *
13
+ * When no dexcost task is in context the wrap is a transparent pass-through —
14
+ * anonymous compute never creates orphan events (capture spec §6 case 2).
15
+ *
16
+ * Mirrors python/src/dexcost/compute_wrap.py.
17
+ */
18
+ /**
19
+ * Wrap an AWS Lambda handler — emits one compute_cost event per invocation
20
+ * with the env-declared memory limit, the wall-clock duration, the
21
+ * architecture detected at runtime, and the initialization type.
22
+ */
23
+ export declare function wrapLambdaHandler<E, C, R>(handler: (event: E, context: C) => Promise<R> | R): (event: E, context: C) => Promise<R>;
24
+ /**
25
+ * Wrap a Cloud Run HTTP handler. Default billing model is request-based
26
+ * (estimated confidence); override via init(computeBillingOverrides=
27
+ * {cloud_run: 'instance'}) for instance-based billing customers.
28
+ */
29
+ export declare function wrapCloudRunHandler<A extends any[], R>(handler: (...args: A) => Promise<R> | R): (...args: A) => Promise<R>;
30
+ export declare function wrapCloudFunctionsHandler<A extends any[], R>(handler: (...args: A) => Promise<R> | R): (...args: A) => Promise<R>;
31
+ export declare function wrapAzureFunctionsHandler<A extends any[], R>(handler: (...args: A) => Promise<R> | R): (...args: A) => Promise<R>;
32
+ export declare function wrapVercelHandler<A extends any[], R>(handler: (...args: A) => Promise<R> | R): (...args: A) => Promise<R>;
33
+ //# sourceMappingURL=compute-wrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-wrap.d.ts","sourceRoot":"","sources":["../../src/adapters/compute-wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA0EH;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACvC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAChD,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CA2BtC;AAID;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EACpD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAY5B;AAID,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EAC1D,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAY5B;AAID,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EAC1D,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAY5B;AAID,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EAClD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAY5B"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Serverless handler wraps for compute capture (node-only, browser-safe).
3
+ *
4
+ * Each wrap is a thin decorator that:
5
+ * 1. Reads runtime-specific env vars (memory limit, init type, region).
6
+ * 2. Creates a ComputeAccountant and attaches it to the active task.
7
+ * 3. Times the handler with performance.now().
8
+ * 4. Reads cgroup memory.peak at exit.
9
+ * 5. Builds the per-invocation compute_cost event with cost_pending=true
10
+ * and persists it via the global tracker's buffer.
11
+ * 6. TrackedTask.end()'s _finalizeCompute back-fills cost_usd at finalize.
12
+ *
13
+ * When no dexcost task is in context the wrap is a transparent pass-through —
14
+ * anonymous compute never creates orphan events (capture spec §6 case 2).
15
+ *
16
+ * Mirrors python/src/dexcost/compute_wrap.py.
17
+ */
18
+ import { randomUUID } from "node:crypto";
19
+ import { ComputeAccountant } from "../core/compute-accountant.js";
20
+ import { RuntimeKind } from "../core/compute-runtime.js";
21
+ import { getCurrentTask } from "../core/context.js";
22
+ import { getCloudEnv } from "../cloud-detect.js";
23
+ import { readMemoryPeak } from "../core/cgroup-reader.js";
24
+ import { getTracker } from "../core/tracker.js";
25
+ import { createCostEvent } from "../core/models.js";
26
+ function _isNode() {
27
+ return typeof process !== "undefined" && !!process.versions?.node;
28
+ }
29
+ /** Insert the compute_cost event with cost_pending=true via the global tracker. */
30
+ function _persistComputeEvent(task, details) {
31
+ let tracker;
32
+ try {
33
+ tracker = getTracker();
34
+ }
35
+ catch {
36
+ // No global tracker → nothing to persist into.
37
+ return;
38
+ }
39
+ const ev = createCostEvent({
40
+ eventId: randomUUID(),
41
+ taskId: task.taskId,
42
+ eventType: "compute_cost",
43
+ costUsd: 0,
44
+ costConfidence: "unknown",
45
+ isRetry: false,
46
+ details,
47
+ });
48
+ try {
49
+ tracker.buffer.addEvent(ev);
50
+ }
51
+ catch {
52
+ // Fail-silent per convention §9.
53
+ // eslint-disable-next-line no-console
54
+ console.warn("compute_wrap failed to persist event");
55
+ }
56
+ }
57
+ /**
58
+ * Run `handler` while measuring duration_ms and peak memory; persist a
59
+ * serverless compute_cost event on exit. Exceptions from the handler are
60
+ * re-raised after the event is persisted (the cost is still incurred —
61
+ * capture spec §6 case 7).
62
+ */
63
+ async function _timeAndCapture(accountant, handler) {
64
+ const t0 = performance.now();
65
+ try {
66
+ return await handler();
67
+ }
68
+ finally {
69
+ const durationMs = Math.trunc(performance.now() - t0);
70
+ const peak = readMemoryPeak() ?? 0;
71
+ try {
72
+ const details = accountant.buildServerlessEvent(durationMs, peak);
73
+ const task = getCurrentTask();
74
+ if (details !== null && task !== undefined) {
75
+ _persistComputeEvent(task, details);
76
+ }
77
+ }
78
+ catch {
79
+ // eslint-disable-next-line no-console
80
+ console.warn("compute_wrap event-build failed");
81
+ }
82
+ }
83
+ }
84
+ // ─── Lambda ──────────────────────────────────────────────────────────────────
85
+ /**
86
+ * Wrap an AWS Lambda handler — emits one compute_cost event per invocation
87
+ * with the env-declared memory limit, the wall-clock duration, the
88
+ * architecture detected at runtime, and the initialization type.
89
+ */
90
+ export function wrapLambdaHandler(handler) {
91
+ return async (event, context) => {
92
+ const task = getCurrentTask();
93
+ if (!task) {
94
+ // No active dexcost task → pass-through (capture spec §6 case 2).
95
+ return await handler(event, context);
96
+ }
97
+ if (!_isNode()) {
98
+ return await handler(event, context);
99
+ }
100
+ let memMb = 128;
101
+ const raw = process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE;
102
+ if (raw !== undefined) {
103
+ const parsed = Number(raw);
104
+ if (Number.isFinite(parsed) && parsed > 0)
105
+ memMb = Math.trunc(parsed);
106
+ }
107
+ const initType = process.env.AWS_LAMBDA_INITIALIZATION_TYPE || "on-demand";
108
+ const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || undefined;
109
+ const accountant = new ComputeAccountant({
110
+ runtime: RuntimeKind.Lambda,
111
+ lambdaMemoryMb: memMb,
112
+ initializationType: initType,
113
+ region,
114
+ });
115
+ task._compute = accountant;
116
+ return await _timeAndCapture(accountant, () => handler(event, context));
117
+ };
118
+ }
119
+ // ─── Cloud Run ───────────────────────────────────────────────────────────────
120
+ /**
121
+ * Wrap a Cloud Run HTTP handler. Default billing model is request-based
122
+ * (estimated confidence); override via init(computeBillingOverrides=
123
+ * {cloud_run: 'instance'}) for instance-based billing customers.
124
+ */
125
+ export function wrapCloudRunHandler(handler) {
126
+ return async (...args) => {
127
+ const task = getCurrentTask();
128
+ if (!task)
129
+ return await handler(...args);
130
+ if (!_isNode())
131
+ return await handler(...args);
132
+ const accountant = new ComputeAccountant({
133
+ runtime: RuntimeKind.CloudRun,
134
+ region: getCloudEnv().region ?? undefined,
135
+ });
136
+ task._compute = accountant;
137
+ return await _timeAndCapture(accountant, () => handler(...args));
138
+ };
139
+ }
140
+ // ─── Cloud Functions Gen2 ────────────────────────────────────────────────────
141
+ export function wrapCloudFunctionsHandler(handler) {
142
+ return async (...args) => {
143
+ const task = getCurrentTask();
144
+ if (!task)
145
+ return await handler(...args);
146
+ if (!_isNode())
147
+ return await handler(...args);
148
+ const accountant = new ComputeAccountant({
149
+ runtime: RuntimeKind.CloudFunctions,
150
+ region: getCloudEnv().region ?? undefined,
151
+ });
152
+ task._compute = accountant;
153
+ return await _timeAndCapture(accountant, () => handler(...args));
154
+ };
155
+ }
156
+ // ─── Azure Functions ─────────────────────────────────────────────────────────
157
+ export function wrapAzureFunctionsHandler(handler) {
158
+ return async (...args) => {
159
+ const task = getCurrentTask();
160
+ if (!task)
161
+ return await handler(...args);
162
+ if (!_isNode())
163
+ return await handler(...args);
164
+ const accountant = new ComputeAccountant({
165
+ runtime: RuntimeKind.AzureFunctions,
166
+ region: process.env.REGION_NAME || undefined,
167
+ });
168
+ task._compute = accountant;
169
+ return await _timeAndCapture(accountant, () => handler(...args));
170
+ };
171
+ }
172
+ // ─── Vercel Fluid ────────────────────────────────────────────────────────────
173
+ export function wrapVercelHandler(handler) {
174
+ return async (...args) => {
175
+ const task = getCurrentTask();
176
+ if (!task)
177
+ return await handler(...args);
178
+ if (!_isNode())
179
+ return await handler(...args);
180
+ const accountant = new ComputeAccountant({
181
+ runtime: RuntimeKind.Vercel,
182
+ region: process.env.VERCEL_REGION || undefined,
183
+ });
184
+ task._compute = accountant;
185
+ return await _timeAndCapture(accountant, () => handler(...args));
186
+ };
187
+ }
188
+ //# sourceMappingURL=compute-wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-wrap.js","sourceRoot":"","sources":["../../src/adapters/compute-wrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ {
2
+ "_meta": {
3
+ "source": "https://aws.amazon.com/lambda/pricing/",
4
+ "updated": "2026-02-26",
5
+ "notes": "Prices in USD. duration_per_gb_second is the cost per 1 GB-second. request_per_invocation is the per-request charge."
6
+ },
7
+ "regions": {
8
+ "us-east-1": {
9
+ "duration_per_gb_second": "0.0000166667",
10
+ "request_per_invocation": "0.0000002"
11
+ },
12
+ "us-east-2": {
13
+ "duration_per_gb_second": "0.0000166667",
14
+ "request_per_invocation": "0.0000002"
15
+ },
16
+ "us-west-1": {
17
+ "duration_per_gb_second": "0.0000166667",
18
+ "request_per_invocation": "0.0000002"
19
+ },
20
+ "us-west-2": {
21
+ "duration_per_gb_second": "0.0000166667",
22
+ "request_per_invocation": "0.0000002"
23
+ },
24
+ "eu-west-1": {
25
+ "duration_per_gb_second": "0.0000166667",
26
+ "request_per_invocation": "0.0000002"
27
+ },
28
+ "eu-west-2": {
29
+ "duration_per_gb_second": "0.0000175000",
30
+ "request_per_invocation": "0.0000002"
31
+ },
32
+ "eu-central-1": {
33
+ "duration_per_gb_second": "0.0000175000",
34
+ "request_per_invocation": "0.0000002"
35
+ },
36
+ "ap-southeast-1": {
37
+ "duration_per_gb_second": "0.0000166667",
38
+ "request_per_invocation": "0.0000002"
39
+ },
40
+ "ap-southeast-2": {
41
+ "duration_per_gb_second": "0.0000166667",
42
+ "request_per_invocation": "0.0000002"
43
+ },
44
+ "ap-northeast-1": {
45
+ "duration_per_gb_second": "0.0000166667",
46
+ "request_per_invocation": "0.0000002"
47
+ },
48
+ "ap-south-1": {
49
+ "duration_per_gb_second": "0.0000133334",
50
+ "request_per_invocation": "0.0000002"
51
+ },
52
+ "sa-east-1": {
53
+ "duration_per_gb_second": "0.0000216667",
54
+ "request_per_invocation": "0.0000002"
55
+ },
56
+ "ca-central-1": {
57
+ "duration_per_gb_second": "0.0000166667",
58
+ "request_per_invocation": "0.0000002"
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Serverless GPU handler wraps — Modal / RunPod / Replicate.
3
+ *
4
+ * Phase 2 GPU foundation Task 7. Mirrors python/src/dexcost/gpu_wrap.py +
5
+ * compute-wrap.ts's shape.
6
+ *
7
+ * Each wrap is a thin decorator that:
8
+ * 1. Creates a GpuAccountant and attaches it to the active task as `_gpu`.
9
+ * 2. Times the handler with performance.now().
10
+ * 3. Calls accountant.snapshotEndAndBuild(durationMs) at exit.
11
+ * 4. Persists the dual events (1 gpu_cost with cost_pending=true + N
12
+ * gpu_utilization_signal) via the global tracker's buffer.
13
+ * 5. Handler exceptions are re-raised AFTER events are persisted (the
14
+ * GPU-seconds were consumed and Modal/RunPod/Replicate bill failed
15
+ * invocations identically to successful ones — capture spec §6 case 7).
16
+ *
17
+ * When no dexcost task is in context the wrap is transparent (capture spec
18
+ * §6 case 2 — anonymous compute never creates orphan events).
19
+ *
20
+ * Browser-safe: short-circuits the accountant work off-Node.
21
+ */
22
+ import { type GpuAccountantHooks } from "../core/gpu-accountant.js";
23
+ /**
24
+ * Test-only — pre-set the hooks every wrap-created GpuAccountant should
25
+ * receive (used to stub NVML / cgroup primitives). Pass null to restore.
26
+ */
27
+ export declare function _setGpuAccountantHooksForTests(hooks: Partial<GpuAccountantHooks> | null): void;
28
+ export declare function wrapModalHandler<A extends any[], R>(handler: (...args: A) => Promise<R> | R): (...args: A) => Promise<R>;
29
+ export declare function wrapRunpodHandler<A extends any[], R>(handler: (...args: A) => Promise<R> | R): (...args: A) => Promise<R>;
30
+ export declare function wrapReplicateHandler<A extends any[], R>(handler: (...args: A) => Promise<R> | R): (...args: A) => Promise<R>;
31
+ //# sourceMappingURL=gpu-wrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-wrap.d.ts","sourceRoot":"","sources":["../../src/adapters/gpu-wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAgBnF;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,GACxC,IAAI,CAEN;AAkFD,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EACjD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAS5B;AAID,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EAClD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAS5B;AAID,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC,EACrD,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACtC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAS5B"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Serverless GPU handler wraps — Modal / RunPod / Replicate.
3
+ *
4
+ * Phase 2 GPU foundation Task 7. Mirrors python/src/dexcost/gpu_wrap.py +
5
+ * compute-wrap.ts's shape.
6
+ *
7
+ * Each wrap is a thin decorator that:
8
+ * 1. Creates a GpuAccountant and attaches it to the active task as `_gpu`.
9
+ * 2. Times the handler with performance.now().
10
+ * 3. Calls accountant.snapshotEndAndBuild(durationMs) at exit.
11
+ * 4. Persists the dual events (1 gpu_cost with cost_pending=true + N
12
+ * gpu_utilization_signal) via the global tracker's buffer.
13
+ * 5. Handler exceptions are re-raised AFTER events are persisted (the
14
+ * GPU-seconds were consumed and Modal/RunPod/Replicate bill failed
15
+ * invocations identically to successful ones — capture spec §6 case 7).
16
+ *
17
+ * When no dexcost task is in context the wrap is transparent (capture spec
18
+ * §6 case 2 — anonymous compute never creates orphan events).
19
+ *
20
+ * Browser-safe: short-circuits the accountant work off-Node.
21
+ */
22
+ import { randomUUID } from "node:crypto";
23
+ import { GpuAccountant } from "../core/gpu-accountant.js";
24
+ import { GpuRuntimeKind } from "../core/gpu-runtime.js";
25
+ import { getCurrentTask } from "../core/context.js";
26
+ import { getCloudEnv } from "../cloud-detect.js";
27
+ import { getTracker } from "../core/tracker.js";
28
+ import { createCostEvent } from "../core/models.js";
29
+ function _isNode() {
30
+ return typeof process !== "undefined" && !!process.versions?.node;
31
+ }
32
+ // ─── Test hook: override the GpuAccountant hooks injected by the wrap ───────
33
+ let _hooksOverride = null;
34
+ /**
35
+ * Test-only — pre-set the hooks every wrap-created GpuAccountant should
36
+ * receive (used to stub NVML / cgroup primitives). Pass null to restore.
37
+ */
38
+ export function _setGpuAccountantHooksForTests(hooks) {
39
+ _hooksOverride = hooks;
40
+ }
41
+ // ─── Persist helpers ────────────────────────────────────────────────────────
42
+ function _persistGpuEvents(task, costDetails, signalEvents) {
43
+ let tracker;
44
+ try {
45
+ tracker = getTracker();
46
+ }
47
+ catch {
48
+ return; // No global tracker → nothing to persist
49
+ }
50
+ try {
51
+ if (costDetails !== null) {
52
+ const ev = createCostEvent({
53
+ eventId: randomUUID(),
54
+ taskId: task.taskId,
55
+ eventType: "gpu_cost",
56
+ costUsd: 0, // back-filled at task finalize
57
+ costConfidence: "unknown",
58
+ isRetry: false,
59
+ details: costDetails,
60
+ });
61
+ tracker.buffer.addEvent(ev);
62
+ }
63
+ if (signalEvents) {
64
+ for (const sig of signalEvents) {
65
+ const ev = createCostEvent({
66
+ eventId: randomUUID(),
67
+ taskId: task.taskId,
68
+ eventType: "gpu_utilization_signal",
69
+ costUsd: 0, // Decision #3 observability-only
70
+ costConfidence: "unknown",
71
+ isRetry: false,
72
+ details: sig,
73
+ });
74
+ tracker.buffer.addEvent(ev);
75
+ }
76
+ }
77
+ }
78
+ catch {
79
+ // Fail-silent per convention §9.
80
+ // eslint-disable-next-line no-console
81
+ console.warn("gpu_wrap failed to persist events");
82
+ }
83
+ }
84
+ async function _timeAndCapture(accountant, handler) {
85
+ accountant.snapshotStart();
86
+ const t0 = performance.now();
87
+ try {
88
+ return await handler();
89
+ }
90
+ finally {
91
+ const durationMs = Math.trunc(performance.now() - t0);
92
+ try {
93
+ const { costDetails, signalEvents } = accountant.snapshotEndAndBuild(durationMs);
94
+ const task = getCurrentTask();
95
+ if (task !== undefined) {
96
+ _persistGpuEvents(task, costDetails, signalEvents);
97
+ }
98
+ }
99
+ catch {
100
+ // eslint-disable-next-line no-console
101
+ console.warn("gpu_wrap event-build failed");
102
+ }
103
+ }
104
+ }
105
+ function _makeAccountant(runtime) {
106
+ return new GpuAccountant(runtime, getCloudEnv(), _hooksOverride ?? undefined);
107
+ }
108
+ // ─── Modal ──────────────────────────────────────────────────────────────────
109
+ export function wrapModalHandler(handler) {
110
+ return async (...args) => {
111
+ const task = getCurrentTask();
112
+ if (!task)
113
+ return await handler(...args);
114
+ if (!_isNode())
115
+ return await handler(...args);
116
+ const accountant = _makeAccountant(GpuRuntimeKind.Modal);
117
+ task._gpu = accountant;
118
+ return await _timeAndCapture(accountant, () => handler(...args));
119
+ };
120
+ }
121
+ // ─── RunPod ─────────────────────────────────────────────────────────────────
122
+ export function wrapRunpodHandler(handler) {
123
+ return async (...args) => {
124
+ const task = getCurrentTask();
125
+ if (!task)
126
+ return await handler(...args);
127
+ if (!_isNode())
128
+ return await handler(...args);
129
+ const accountant = _makeAccountant(GpuRuntimeKind.RunPod);
130
+ task._gpu = accountant;
131
+ return await _timeAndCapture(accountant, () => handler(...args));
132
+ };
133
+ }
134
+ // ─── Replicate ──────────────────────────────────────────────────────────────
135
+ export function wrapReplicateHandler(handler) {
136
+ return async (...args) => {
137
+ const task = getCurrentTask();
138
+ if (!task)
139
+ return await handler(...args);
140
+ if (!_isNode())
141
+ return await handler(...args);
142
+ const accountant = _makeAccountant(GpuRuntimeKind.Replicate);
143
+ task._gpu = accountant;
144
+ return await _timeAndCapture(accountant, () => handler(...args));
145
+ };
146
+ }
147
+ //# sourceMappingURL=gpu-wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-wrap.js","sourceRoot":"","sources":["../../src/adapters/gpu-wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAA2B,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,SAAS,OAAO;IACd,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;AACpE,CAAC;AAED,+EAA+E;AAE/E,IAAI,cAAc,GAAuC,IAAI,CAAC;AAE9D;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAC5C,KAAyC;IAEzC,cAAc,GAAG,KAAK,CAAC;AACzB,CAAC;AAED,+EAA+E;AAE/E,SAAS,iBAAiB,CACxB,IAAU,EACV,WAAuC,EACvC,YAA+C;IAE/C,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,UAAU,EAAE,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,yCAAyC;IACnD,CAAC;IACD,IAAI,CAAC;QACH,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,EAAE,GAAG,eAAe,CAAC;gBACzB,OAAO,EAAE,UAAU,EAAE;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,SAAS,EAAE,UAAU;gBACrB,OAAO,EAAE,CAAC,EAAE,+BAA+B;gBAC3C,cAAc,EAAE,SAAS;gBACzB,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,WAAW;aACrB,CAAC,CAAC;YACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;gBAC/B,MAAM,EAAE,GAAG,eAAe,CAAC;oBACzB,OAAO,EAAE,UAAU,EAAE;oBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,SAAS,EAAE,wBAAwB;oBACnC,OAAO,EAAE,CAAC,EAAE,iCAAiC;oBAC7C,cAAc,EAAE,SAAS;oBACzB,OAAO,EAAE,KAAK;oBACd,OAAO,EAAE,GAAG;iBACb,CAAC,CAAC;gBACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;QACjC,sCAAsC;QACtC,OAAO,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,UAAyB,EACzB,OAA6B;IAE7B,UAAU,CAAC,aAAa,EAAE,CAAC;IAC3B,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,EAAE,CAAC;IACzB,CAAC;YAAS,CAAC;QACT,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,UAAU,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;YACjF,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;YAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAAuB;IAC9C,OAAO,IAAI,aAAa,CACtB,OAAO,EACP,WAAW,EAAE,EACb,cAAc,IAAI,SAAS,CAC5B,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,gBAAgB,CAC9B,OAAuC;IAEvC,OAAO,KAAK,EAAE,GAAG,IAAO,EAAc,EAAE;QACtC,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,eAAe,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACxD,IAAY,CAAC,IAAI,GAAG,UAAU,CAAC;QAChC,OAAO,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,iBAAiB,CAC/B,OAAuC;IAEvC,OAAO,KAAK,EAAE,GAAG,IAAO,EAAc,EAAE;QACtC,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACzD,IAAY,CAAC,IAAI,GAAG,UAAU,CAAC;QAChC,OAAO,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,oBAAoB,CAClC,OAAuC;IAEvC,OAAO,KAAK,EAAE,GAAG,IAAO,EAAc,EAAE;QACtC,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,eAAe,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAC5D,IAAY,CAAC,IAAI,GAAG,UAAU,CAAC;QAChC,OAAO,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC;AACJ,CAAC"}