@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,160 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://dexcost.io/schemas/dexcost-task.v1.json",
4
+ "title": "Dexcost Task v1",
5
+ "description": "Standard Event Schema v1 — Task payload. Represents a tracked business task with aggregated costs, tokens, and waste metrics.",
6
+ "type": "object",
7
+ "required": [
8
+ "task_id",
9
+ "task_type",
10
+ "status",
11
+ "started_at",
12
+ "schema_version",
13
+ "llm_cost_usd",
14
+ "external_cost_usd",
15
+ "compute_cost_usd",
16
+ "total_cost_usd",
17
+ "total_input_tokens",
18
+ "total_output_tokens",
19
+ "total_cached_tokens",
20
+ "retry_count",
21
+ "retry_cost_usd",
22
+ "failure_count"
23
+ ],
24
+ "additionalProperties": false,
25
+ "properties": {
26
+ "task_id": {
27
+ "type": "string",
28
+ "format": "uuid",
29
+ "description": "Unique identifier for the task."
30
+ },
31
+ "task_type": {
32
+ "type": "string",
33
+ "description": "Business task type (e.g. resolve_ticket, generate_report)."
34
+ },
35
+ "status": {
36
+ "type": "string",
37
+ "enum": ["pending", "success", "failed"],
38
+ "description": "Lifecycle status of the task."
39
+ },
40
+ "started_at": {
41
+ "type": "string",
42
+ "format": "date-time",
43
+ "description": "ISO-8601 timestamp when the task started."
44
+ },
45
+ "ended_at": {
46
+ "type": ["string", "null"],
47
+ "format": "date-time",
48
+ "description": "ISO-8601 timestamp when the task ended, or null if still running."
49
+ },
50
+ "metadata": {
51
+ "type": "object",
52
+ "description": "Caller-defined context (customer tier, ticket id, etc.)."
53
+ },
54
+ "customer_id": {
55
+ "type": ["string", "null"],
56
+ "description": "Denormalised customer identifier for fast queries."
57
+ },
58
+ "project_id": {
59
+ "type": ["string", "null"],
60
+ "description": "Denormalised project identifier for fast queries."
61
+ },
62
+ "parent_task_id": {
63
+ "type": ["string", "null"],
64
+ "format": "uuid",
65
+ "description": "UUID of the parent task, or null for top-level tasks."
66
+ },
67
+ "experiment_id": {
68
+ "type": ["string", "null"],
69
+ "description": "Groups tasks into an experiment for variant comparison."
70
+ },
71
+ "variant": {
72
+ "type": ["string", "null"],
73
+ "description": "Labels which variant this task run belongs to within an experiment."
74
+ },
75
+ "llm_cost_usd": {
76
+ "type": "string",
77
+ "pattern": "^-?\\d+(\\.\\d+)?$",
78
+ "description": "Aggregated LLM cost in USD (Decimal as string)."
79
+ },
80
+ "external_cost_usd": {
81
+ "type": "string",
82
+ "pattern": "^-?\\d+(\\.\\d+)?$",
83
+ "description": "Aggregated external service cost in USD (Decimal as string)."
84
+ },
85
+ "compute_cost_usd": {
86
+ "type": "string",
87
+ "pattern": "^-?\\d+(\\.\\d+)?$",
88
+ "description": "Aggregated compute cost in USD (Decimal as string)."
89
+ },
90
+ "network_cost_usd": {
91
+ "type": "string",
92
+ "pattern": "^-?\\d+(\\.\\d+)?$",
93
+ "description": "Aggregated cloud-egress cost in USD (Decimal as string). Distinct from external_cost_usd, which captures vendor API fees — see spec Decision #7."
94
+ },
95
+ "gpu_cost_usd": {
96
+ "type": "string",
97
+ "pattern": "^-?\\d+(\\.\\d+)?$",
98
+ "description": "Aggregated GPU cost in USD (Decimal as string). Computed at task finalize from per-PID NVML SM-time × catalog rate. Distinct from compute_cost_usd (CPU/memory billing dimension)."
99
+ },
100
+ "total_cost_usd": {
101
+ "type": "string",
102
+ "pattern": "^-?\\d+(\\.\\d+)?$",
103
+ "description": "Total aggregated cost in USD (Decimal as string)."
104
+ },
105
+ "total_input_tokens": {
106
+ "type": "integer",
107
+ "minimum": 0,
108
+ "description": "Total input tokens across all LLM events."
109
+ },
110
+ "total_output_tokens": {
111
+ "type": "integer",
112
+ "minimum": 0,
113
+ "description": "Total output tokens across all LLM events."
114
+ },
115
+ "total_cached_tokens": {
116
+ "type": "integer",
117
+ "minimum": 0,
118
+ "description": "Total cached tokens across all LLM events."
119
+ },
120
+ "retry_count": {
121
+ "type": "integer",
122
+ "minimum": 0,
123
+ "description": "Number of retried events in this task."
124
+ },
125
+ "retry_cost_usd": {
126
+ "type": "string",
127
+ "pattern": "^-?\\d+(\\.\\d+)?$",
128
+ "description": "Aggregated retry waste in USD (Decimal as string)."
129
+ },
130
+ "failure_count": {
131
+ "type": "integer",
132
+ "minimum": 0,
133
+ "description": "Number of failed events in this task."
134
+ },
135
+ "network_bytes_in": {
136
+ "type": "integer",
137
+ "minimum": 0,
138
+ "description": "Total bytes received across all HTTP responses for this task."
139
+ },
140
+ "network_bytes_out": {
141
+ "type": "integer",
142
+ "minimum": 0,
143
+ "description": "Total bytes sent across all HTTP requests for this task."
144
+ },
145
+ "network_call_count": {
146
+ "type": "integer",
147
+ "minimum": 0,
148
+ "description": "Total number of outbound HTTP calls recorded for this task."
149
+ },
150
+ "network_by_host": {
151
+ "type": "object",
152
+ "description": "Per-host breakdown of network usage, shape {\"hosts\": [...]} capped at 20 entries plus an _other overflow bucket."
153
+ },
154
+ "schema_version": {
155
+ "type": "string",
156
+ "const": "1",
157
+ "description": "Schema version — always '1' for v1 payloads."
158
+ }
159
+ }
160
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * JSON Schema validation for dexcost Standard Event Schema v1.
3
+ *
4
+ * Validates task and event payloads against bundled JSON Schema v1 files
5
+ * using Ajv. Mirrors the Python SDK's schema.py validate() function.
6
+ */
7
+ /**
8
+ * Validate a task or event payload against the bundled JSON Schema v1.
9
+ *
10
+ * @param payload - A Record produced by `taskToDict()` or `eventToDict()`.
11
+ * @returns An empty array when the payload is valid; otherwise an array of
12
+ * human-readable error strings in `"path: message"` format.
13
+ */
14
+ export declare function validate(payload: Record<string, unknown>): string[];
15
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/schema/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuDH;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAuBnE"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * JSON Schema validation for dexcost Standard Event Schema v1.
3
+ *
4
+ * Validates task and event payloads against bundled JSON Schema v1 files
5
+ * using Ajv. Mirrors the Python SDK's schema.py validate() function.
6
+ */
7
+ // Sprint 3 Theme E / §4.2.3 — runtime JSON load for Node 18 support.
8
+ // `import x from "./x.json" with { type: "json" }` is Node 22+ syntax;
9
+ // reading the file at runtime works on every supported Node version.
10
+ import { readFileSync } from "node:fs";
11
+ import { fileURLToPath } from "node:url";
12
+ import { dirname, join } from "node:path";
13
+ const _thisDir = dirname(fileURLToPath(import.meta.url));
14
+ const eventSchema = JSON.parse(readFileSync(join(_thisDir, "dexcost-event.v1.json"), "utf-8"));
15
+ const taskSchema = JSON.parse(readFileSync(join(_thisDir, "dexcost-task.v1.json"), "utf-8"));
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ let ajv;
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ let validateEvent;
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ let validateTask;
22
+ try {
23
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
24
+ const AjvModule = require("ajv");
25
+ const AjvClass = AjvModule.default ?? AjvModule;
26
+ // Build Ajv instance with allErrors so all problems are reported at once.
27
+ ajv = new AjvClass({ allErrors: true });
28
+ // Attempt to load ajv-formats for uuid and date-time format validation.
29
+ try {
30
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
31
+ const formats = require("ajv-formats");
32
+ const addFormats = formats.default ?? formats;
33
+ addFormats(ajv);
34
+ }
35
+ catch {
36
+ /* ajv-formats not available — format keywords are silently ignored */
37
+ }
38
+ validateEvent = ajv.compile(eventSchema);
39
+ validateTask = ajv.compile(taskSchema);
40
+ }
41
+ catch {
42
+ // ajv not installed — validators will be null, validate() returns empty errors
43
+ ajv = null;
44
+ validateEvent = null;
45
+ validateTask = null;
46
+ }
47
+ /**
48
+ * Validate a task or event payload against the bundled JSON Schema v1.
49
+ *
50
+ * @param payload - A Record produced by `taskToDict()` or `eventToDict()`.
51
+ * @returns An empty array when the payload is valid; otherwise an array of
52
+ * human-readable error strings in `"path: message"` format.
53
+ */
54
+ export function validate(payload) {
55
+ // If ajv failed to load, we can't validate — return empty (no errors)
56
+ if (ajv === null || validateEvent === null || validateTask === null) {
57
+ return [];
58
+ }
59
+ // Step 1: check schema_version
60
+ const sv = payload["schema_version"];
61
+ if (sv !== "1") {
62
+ return [`Unsupported schema_version: ${String(sv)}`];
63
+ }
64
+ // Step 2: route to the correct schema validator
65
+ if ("event_id" in payload) {
66
+ return runValidator(validateEvent, payload);
67
+ }
68
+ if ("task_id" in payload) {
69
+ return runValidator(validateTask, payload);
70
+ }
71
+ // Step 3: cannot determine type
72
+ return ["Cannot determine payload type: missing task_id or event_id"];
73
+ }
74
+ /** Run an Ajv compiled validator and format its errors. */
75
+ function runValidator(validator, payload) {
76
+ const valid = validator(payload);
77
+ if (valid) {
78
+ return [];
79
+ }
80
+ const errors = validator.errors ?? [];
81
+ return errors.map((err) => {
82
+ const path = err.instancePath || "(root)";
83
+ const message = err.message ?? "unknown error";
84
+ return `${path}: ${message}`;
85
+ });
86
+ }
87
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * PII redaction and metadata safety utilities.
3
+ *
4
+ * Provides field-level redaction, SHA-256 hashing, and metadata size
5
+ * enforcement to protect sensitive data before it leaves the SDK.
6
+ */
7
+ /**
8
+ * Recursively remove specified fields from a dictionary.
9
+ *
10
+ * Returns a new dict with any key found in `fields` stripped entirely at
11
+ * all nesting levels. Matching is case-sensitive. This mirrors the
12
+ * Python SDK's `redact_dict`, which DELETES matched keys rather than
13
+ * masking them, so redacted PII never leaves the process.
14
+ */
15
+ export declare function redactDict(data: Record<string, unknown>, fields: string[]): Record<string, unknown>;
16
+ /**
17
+ * Compute a SHA-256 hex digest of the given value.
18
+ *
19
+ * Uses the Node.js built-in `crypto` module.
20
+ */
21
+ export declare function hashValue(value: string): string;
22
+ /**
23
+ * Enforce a maximum serialised size on a metadata/details dictionary.
24
+ *
25
+ * If the JSON representation of `details` exceeds `maxBytes`, returns a
26
+ * deterministic stub `{ _truncated: true, _original_size_bytes: N }`
27
+ * rather than partially removing keys. This mirrors the Python SDK's
28
+ * `enforce_metadata_limit`. When `details` is unserialisable a
29
+ * `{ _truncated: true, _error: "unserializable" }` stub is returned.
30
+ *
31
+ * @param details - The metadata dictionary to enforce limits on.
32
+ * @param maxBytes - Maximum allowed byte size. Defaults to 10 KB.
33
+ * @returns The original dictionary when within the limit, otherwise a stub.
34
+ */
35
+ export declare function enforceMetadataLimit(details: Record<string, unknown>, maxBytes?: number): Record<string, unknown>;
36
+ /**
37
+ * Strip credentials from a URL before it is captured into an event.
38
+ *
39
+ * Removes:
40
+ * - userinfo (`user:pass@`) from the authority
41
+ * - query parameters whose name (case-insensitive) is in the canonical
42
+ * sensitive set OR ends with `-signature`, `-credential`, or
43
+ * `-security-token` (AWS SigV4 surface)
44
+ *
45
+ * Preserves scheme, host, port, path, non-sensitive query params, and
46
+ * fragment. The shape of every removed query parameter is preserved as
47
+ * `name=REDACTED` so downstream callers can still see which keys were
48
+ * present without leaking the values.
49
+ *
50
+ * Canonical algorithm — Python/Go/Rust SDK implementations must produce
51
+ * byte-identical output for the same input (enforced by
52
+ * /fixtures/expected_outputs/security/).
53
+ */
54
+ export declare function scrubUrl(url: string): string;
55
+ //# sourceMappingURL=redaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../src/security/redaction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,MAAM,EAAE,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,GAAE,MAA0B,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAezB;AAyBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA8B5C"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * PII redaction and metadata safety utilities.
3
+ *
4
+ * Provides field-level redaction, SHA-256 hashing, and metadata size
5
+ * enforcement to protect sensitive data before it leaves the SDK.
6
+ */
7
+ import { createHash } from "node:crypto";
8
+ /** Default maximum metadata size in bytes (10 KB). */
9
+ const DEFAULT_MAX_BYTES = 10_240;
10
+ /**
11
+ * Recursively remove specified fields from a dictionary.
12
+ *
13
+ * Returns a new dict with any key found in `fields` stripped entirely at
14
+ * all nesting levels. Matching is case-sensitive. This mirrors the
15
+ * Python SDK's `redact_dict`, which DELETES matched keys rather than
16
+ * masking them, so redacted PII never leaves the process.
17
+ */
18
+ export function redactDict(data, fields) {
19
+ const fieldSet = new Set(fields);
20
+ const result = {};
21
+ for (const [key, value] of Object.entries(data)) {
22
+ if (fieldSet.has(key)) {
23
+ continue;
24
+ }
25
+ if (value !== null &&
26
+ typeof value === "object" &&
27
+ !Array.isArray(value)) {
28
+ // Recursively redact nested objects
29
+ result[key] = redactDict(value, fields);
30
+ }
31
+ else {
32
+ result[key] = value;
33
+ }
34
+ }
35
+ return result;
36
+ }
37
+ /**
38
+ * Compute a SHA-256 hex digest of the given value.
39
+ *
40
+ * Uses the Node.js built-in `crypto` module.
41
+ */
42
+ export function hashValue(value) {
43
+ return createHash("sha256").update(value, "utf-8").digest("hex");
44
+ }
45
+ /**
46
+ * Enforce a maximum serialised size on a metadata/details dictionary.
47
+ *
48
+ * If the JSON representation of `details` exceeds `maxBytes`, returns a
49
+ * deterministic stub `{ _truncated: true, _original_size_bytes: N }`
50
+ * rather than partially removing keys. This mirrors the Python SDK's
51
+ * `enforce_metadata_limit`. When `details` is unserialisable a
52
+ * `{ _truncated: true, _error: "unserializable" }` stub is returned.
53
+ *
54
+ * @param details - The metadata dictionary to enforce limits on.
55
+ * @param maxBytes - Maximum allowed byte size. Defaults to 10 KB.
56
+ * @returns The original dictionary when within the limit, otherwise a stub.
57
+ */
58
+ export function enforceMetadataLimit(details, maxBytes = DEFAULT_MAX_BYTES) {
59
+ let serialized;
60
+ try {
61
+ serialized = JSON.stringify(details);
62
+ }
63
+ catch {
64
+ return { _truncated: true, _error: "unserializable" };
65
+ }
66
+ if (serialized === undefined) {
67
+ return { _truncated: true, _error: "unserializable" };
68
+ }
69
+ const byteSize = Buffer.byteLength(serialized, "utf-8");
70
+ if (byteSize <= maxBytes) {
71
+ return details;
72
+ }
73
+ return { _truncated: true, _original_size_bytes: byteSize };
74
+ }
75
+ /**
76
+ * Canonical set of query parameter names (case-insensitive) that
77
+ * {@link scrubUrl} strips. Must stay in sync with the same set in
78
+ * Python (dexcost/redaction.py), Go (security/redaction.go), and Rust
79
+ * (security/redaction.rs).
80
+ */
81
+ const SENSITIVE_QUERY_PARAMS = new Set([
82
+ "api_key",
83
+ "apikey",
84
+ "access_token",
85
+ "token",
86
+ "auth",
87
+ "password",
88
+ "secret",
89
+ "signature",
90
+ "x-amz-signature",
91
+ "x-amz-credential",
92
+ "x-amz-security-token",
93
+ "session",
94
+ ]);
95
+ const USERINFO_RE = /^(https?:\/\/)([^@/?#]+@)?(.+)$/;
96
+ /**
97
+ * Strip credentials from a URL before it is captured into an event.
98
+ *
99
+ * Removes:
100
+ * - userinfo (`user:pass@`) from the authority
101
+ * - query parameters whose name (case-insensitive) is in the canonical
102
+ * sensitive set OR ends with `-signature`, `-credential`, or
103
+ * `-security-token` (AWS SigV4 surface)
104
+ *
105
+ * Preserves scheme, host, port, path, non-sensitive query params, and
106
+ * fragment. The shape of every removed query parameter is preserved as
107
+ * `name=REDACTED` so downstream callers can still see which keys were
108
+ * present without leaking the values.
109
+ *
110
+ * Canonical algorithm — Python/Go/Rust SDK implementations must produce
111
+ * byte-identical output for the same input (enforced by
112
+ * /fixtures/expected_outputs/security/).
113
+ */
114
+ export function scrubUrl(url) {
115
+ if (!url)
116
+ return url;
117
+ const m = USERINFO_RE.exec(url);
118
+ if (m) {
119
+ url = m[1] + m[3];
120
+ }
121
+ let fragment = "";
122
+ const hashIdx = url.indexOf("#");
123
+ if (hashIdx >= 0) {
124
+ fragment = url.slice(hashIdx);
125
+ url = url.slice(0, hashIdx);
126
+ }
127
+ const qIdx = url.indexOf("?");
128
+ if (qIdx < 0)
129
+ return url + fragment;
130
+ const base = url.slice(0, qIdx);
131
+ const query = url.slice(qIdx + 1);
132
+ const parts = query.split("&").map((part) => {
133
+ const eqIdx = part.indexOf("=");
134
+ const name = eqIdx >= 0 ? part.slice(0, eqIdx) : part;
135
+ const lname = name.toLowerCase();
136
+ const sensitive = SENSITIVE_QUERY_PARAMS.has(lname) ||
137
+ lname.endsWith("-signature") ||
138
+ lname.endsWith("-credential") ||
139
+ lname.endsWith("-security-token");
140
+ return sensitive ? `${name}=REDACTED` : part;
141
+ });
142
+ return `${base}?${parts.join("&")}${fragment}`;
143
+ }
144
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../src/security/redaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * SQLite-backed event buffer for dexcost.
3
+ *
4
+ * Persists cost events and tasks to a local SQLite database using the exact
5
+ * same schema as the Python SDK. Data survives process restarts.
6
+ *
7
+ * Uses better-sqlite3 for synchronous, high-performance SQLite access.
8
+ */
9
+ import type { CostEvent, Task } from "../core/models.js";
10
+ /**
11
+ * SQLite-backed buffer that persists events and tasks across process restarts.
12
+ *
13
+ * Schema is identical to the Python SDK so both SDKs can share a database file.
14
+ * Costs are stored as TEXT strings to avoid floating-point precision loss.
15
+ */
16
+ export declare class EventBuffer {
17
+ private _db;
18
+ private _mem;
19
+ /**
20
+ * Test-only seam. When `true`, the constructor takes the no-binding
21
+ * fallback path without attempting the require — used by
22
+ * tests/runtime-fallback.test.ts to simulate Vercel Edge / Cloudflare
23
+ * Workers behaviour without touching the real native module. Do NOT
24
+ * set this in production code. Sprint 1 Theme B / §2.2.3 (B8).
25
+ */
26
+ static _forceFallbackForTest: boolean;
27
+ constructor(dbPath?: string);
28
+ /**
29
+ * Add `column` to `table` if it does not already exist.
30
+ *
31
+ * SQLite has no `ADD COLUMN IF NOT EXISTS`, so a duplicate-column error
32
+ * is the expected signal that the migration has already been applied and
33
+ * is swallowed. Any other failure is also tolerated so init never crashes.
34
+ */
35
+ private _migrateAddColumn;
36
+ /**
37
+ * Add a cost event to the buffer with sync_status = 'pending'.
38
+ */
39
+ addEvent(event: CostEvent): void;
40
+ /**
41
+ * Insert or replace a task in the buffer.
42
+ *
43
+ * The task is (re)marked `sync_status = 'pending'`: an upsert means the
44
+ * task's data changed (new cost rolled up, status flipped, etc.), so it
45
+ * must be re-sent on the next push. `markTasksSynced` flips it to
46
+ * `'synced'` after a successful POST so unchanged tasks are not re-sent.
47
+ */
48
+ upsertTask(task: Task): void;
49
+ /**
50
+ * Return up to `limit` pending events, ordered by timestamp ASC.
51
+ */
52
+ getPendingEvents(limit?: number): CostEvent[];
53
+ /**
54
+ * Mark the given event IDs as synced.
55
+ */
56
+ markSynced(eventIds: string[]): void;
57
+ /**
58
+ * Retrieve a task by ID, or undefined if not found.
59
+ */
60
+ getTask(taskId: string): Task | undefined;
61
+ /**
62
+ * Return all tasks in the buffer.
63
+ */
64
+ getAllTasks(): Task[];
65
+ /**
66
+ * Return all tasks awaiting sync (`sync_status = 'pending'`).
67
+ *
68
+ * The pusher sends only these so unchanged tasks are not re-POSTed on
69
+ * every push cycle.
70
+ */
71
+ getPendingTasks(): Task[];
72
+ /**
73
+ * Mark the given task IDs as synced.
74
+ *
75
+ * Called by the pusher after a successful POST so the tasks are excluded
76
+ * from subsequent pushes until they are upserted again.
77
+ */
78
+ markTasksSynced(taskIds: string[]): void;
79
+ /** The number of tasks awaiting sync (`sync_status = 'pending'`). */
80
+ get pendingTaskCount(): number;
81
+ /**
82
+ * Return all events in the buffer (including synced).
83
+ */
84
+ getAllEvents(): CostEvent[];
85
+ /**
86
+ * Return events for a specific task, ordered by timestamp DESC.
87
+ */
88
+ queryEvents(taskId: string): CostEvent[];
89
+ /**
90
+ * Update all columns of an existing event in-place.
91
+ */
92
+ updateEvent(event: CostEvent): void;
93
+ /**
94
+ * Return the number of pending (unsynced) events.
95
+ */
96
+ get pendingCount(): number;
97
+ /**
98
+ * Delete synced events older than `retentionHours` and VACUUM.
99
+ *
100
+ * Returns the number of deleted rows.
101
+ */
102
+ purgeSynced(retentionHours?: number): number;
103
+ /**
104
+ * Delete pending events older than `maxAgeDays` and VACUUM.
105
+ *
106
+ * Safety net for events that can never be synced (rejected API key,
107
+ * permanently-down endpoint, etc.) so the local buffer cannot grow
108
+ * unbounded. Mirrors the Python SDK's `purge_old_pending` (default 7
109
+ * days). Returns the number of deleted rows.
110
+ */
111
+ purgeOldPending(maxAgeDays?: number): number;
112
+ /**
113
+ * Close the underlying database connection.
114
+ */
115
+ close(): void;
116
+ }
117
+ //# sourceMappingURL=buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../../src/transport/buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAqZzD;;;;;GAKG;AACH,qBAAa,WAAW;IAGtB,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,IAAI,CAAkC;IAE9C;;;;;;OAMG;IACH,MAAM,CAAC,qBAAqB,UAAS;gBAEzB,MAAM,CAAC,EAAE,MAAM;IAiF3B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IASzB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IA4ChC;;;;;;;OAOG;IACH,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAkD5B;;OAEG;IACH,gBAAgB,CAAC,KAAK,GAAE,MAAY,GAAG,SAAS,EAAE;IAWlD;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAcpC;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IASzC;;OAEG;IACH,WAAW,IAAI,IAAI,EAAE;IAOrB;;;;;OAKG;IACH,eAAe,IAAI,IAAI,EAAE;IASzB;;;;;OAKG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAcxC,qEAAqE;IACrE,IAAI,gBAAgB,IAAI,MAAM,CAO7B;IAED;;OAEG;IACH,YAAY,IAAI,SAAS,EAAE;IAO3B;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IASxC;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAqDnC;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAOzB;IAED;;;;OAIG;IACH,WAAW,CAAC,cAAc,GAAE,MAAW,GAAG,MAAM;IAsBhD;;;;;;;OAOG;IACH,eAAe,CAAC,UAAU,GAAE,MAAU,GAAG,MAAM;IAoB/C;;OAEG;IACH,KAAK,IAAI,IAAI;CAKd"}