@oka-core/reason 0.2.15 → 0.2.16

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 (158) hide show
  1. package/dist/abort-controller.d.ts +19 -0
  2. package/dist/abort-controller.d.ts.map +1 -0
  3. package/dist/abort-controller.js +53 -0
  4. package/dist/activity-tracker.d.ts +48 -0
  5. package/dist/activity-tracker.d.ts.map +1 -0
  6. package/dist/activity-tracker.js +80 -0
  7. package/dist/analytics.d.ts +49 -0
  8. package/dist/analytics.d.ts.map +1 -0
  9. package/dist/analytics.js +88 -0
  10. package/dist/array.d.ts +12 -0
  11. package/dist/array.d.ts.map +1 -0
  12. package/dist/array.js +20 -0
  13. package/dist/async-context.d.ts +20 -0
  14. package/dist/async-context.d.ts.map +1 -0
  15. package/dist/async-context.js +25 -0
  16. package/dist/binary-check.d.ts +16 -0
  17. package/dist/binary-check.d.ts.map +1 -0
  18. package/dist/binary-check.js +43 -0
  19. package/dist/buffered-writer.d.ts +30 -0
  20. package/dist/buffered-writer.d.ts.map +1 -0
  21. package/dist/buffered-writer.js +87 -0
  22. package/dist/circular-buffer.d.ts +28 -0
  23. package/dist/circular-buffer.d.ts.map +1 -0
  24. package/dist/circular-buffer.js +61 -0
  25. package/dist/cleanup-registry.d.ts +23 -0
  26. package/dist/cleanup-registry.d.ts.map +1 -0
  27. package/dist/cleanup-registry.js +34 -0
  28. package/dist/client.d.ts +4 -0
  29. package/dist/client.d.ts.map +1 -1
  30. package/dist/client.js +32 -10
  31. package/dist/combined-abort-signal.d.ts +25 -0
  32. package/dist/combined-abort-signal.d.ts.map +1 -0
  33. package/dist/combined-abort-signal.js +47 -0
  34. package/dist/cron-lock.d.ts +29 -0
  35. package/dist/cron-lock.d.ts.map +1 -0
  36. package/dist/cron-lock.js +127 -0
  37. package/dist/cron-scheduler.d.ts +41 -0
  38. package/dist/cron-scheduler.d.ts.map +1 -0
  39. package/dist/cron-scheduler.js +189 -0
  40. package/dist/cron-tasks.d.ts +86 -0
  41. package/dist/cron-tasks.d.ts.map +1 -0
  42. package/dist/cron-tasks.js +205 -0
  43. package/dist/cron.d.ts +35 -0
  44. package/dist/cron.d.ts.map +1 -0
  45. package/dist/cron.js +215 -0
  46. package/dist/env.d.ts +26 -0
  47. package/dist/env.d.ts.map +1 -0
  48. package/dist/env.js +50 -0
  49. package/dist/errors.d.ts +99 -0
  50. package/dist/errors.d.ts.map +1 -0
  51. package/dist/errors.js +214 -0
  52. package/dist/format.d.ts +21 -0
  53. package/dist/format.d.ts.map +1 -0
  54. package/dist/format.js +48 -0
  55. package/dist/fps-tracker.d.ts +22 -0
  56. package/dist/fps-tracker.d.ts.map +1 -0
  57. package/dist/fps-tracker.js +44 -0
  58. package/dist/graceful-shutdown.d.ts +35 -0
  59. package/dist/graceful-shutdown.d.ts.map +1 -0
  60. package/dist/graceful-shutdown.js +89 -0
  61. package/dist/hash.d.ts +21 -0
  62. package/dist/hash.d.ts.map +1 -0
  63. package/dist/hash.js +31 -0
  64. package/dist/heap-diagnostics.d.ts +68 -0
  65. package/dist/heap-diagnostics.d.ts.map +1 -0
  66. package/dist/heap-diagnostics.js +110 -0
  67. package/dist/idle-timeout.d.ts +21 -0
  68. package/dist/idle-timeout.d.ts.map +1 -0
  69. package/dist/idle-timeout.js +42 -0
  70. package/dist/index.d.ts +2 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +5 -0
  73. package/dist/intl.d.ts +18 -0
  74. package/dist/intl.d.ts.map +1 -0
  75. package/dist/intl.js +75 -0
  76. package/dist/jsonl.d.ts +16 -0
  77. package/dist/jsonl.d.ts.map +1 -0
  78. package/dist/jsonl.js +60 -0
  79. package/dist/lazy-schema.d.ts +6 -0
  80. package/dist/lazy-schema.d.ts.map +1 -0
  81. package/dist/lazy-schema.js +8 -0
  82. package/dist/memo.d.ts +64 -0
  83. package/dist/memo.d.ts.map +1 -0
  84. package/dist/memo.js +162 -0
  85. package/dist/pkce.d.ts +13 -0
  86. package/dist/pkce.d.ts.map +1 -0
  87. package/dist/pkce.js +28 -0
  88. package/dist/priority-queue.d.ts +36 -0
  89. package/dist/priority-queue.d.ts.map +1 -0
  90. package/dist/priority-queue.js +97 -0
  91. package/dist/process-utils.d.ts +20 -0
  92. package/dist/process-utils.d.ts.map +1 -0
  93. package/dist/process-utils.js +54 -0
  94. package/dist/query-guard.d.ts +34 -0
  95. package/dist/query-guard.d.ts.map +1 -0
  96. package/dist/query-guard.js +74 -0
  97. package/dist/retry.d.ts +60 -0
  98. package/dist/retry.d.ts.map +1 -0
  99. package/dist/retry.js +89 -0
  100. package/dist/schemas.d.ts +6 -6
  101. package/dist/secrets.d.ts +44 -0
  102. package/dist/secrets.d.ts.map +1 -0
  103. package/dist/secrets.js +115 -0
  104. package/dist/semantic-types.d.ts +39 -0
  105. package/dist/semantic-types.d.ts.map +1 -0
  106. package/dist/semantic-types.js +49 -0
  107. package/dist/sequential.d.ts +21 -0
  108. package/dist/sequential.d.ts.map +1 -0
  109. package/dist/sequential.js +49 -0
  110. package/dist/signal.d.ts +29 -0
  111. package/dist/signal.d.ts.map +1 -0
  112. package/dist/signal.js +39 -0
  113. package/dist/sleep.d.ts +21 -0
  114. package/dist/sleep.d.ts.map +1 -0
  115. package/dist/sleep.js +58 -0
  116. package/dist/slow-ops.d.ts +41 -0
  117. package/dist/slow-ops.d.ts.map +1 -0
  118. package/dist/slow-ops.js +133 -0
  119. package/dist/store.d.ts +20 -0
  120. package/dist/store.d.ts.map +1 -0
  121. package/dist/store.js +34 -0
  122. package/dist/stream.d.ts +29 -0
  123. package/dist/stream.d.ts.map +1 -0
  124. package/dist/stream.js +92 -0
  125. package/dist/string-utils.d.ts +46 -0
  126. package/dist/string-utils.d.ts.map +1 -0
  127. package/dist/string-utils.js +69 -0
  128. package/dist/strip-bom.d.ts +8 -0
  129. package/dist/strip-bom.d.ts.map +1 -0
  130. package/dist/strip-bom.js +10 -0
  131. package/dist/subprocess-env.d.ts +25 -0
  132. package/dist/subprocess-env.d.ts.map +1 -0
  133. package/dist/subprocess-env.js +55 -0
  134. package/dist/temp-file.d.ts +18 -0
  135. package/dist/temp-file.d.ts.map +1 -0
  136. package/dist/temp-file.js +26 -0
  137. package/dist/tool-contract.d.ts +85 -0
  138. package/dist/tool-contract.d.ts.map +1 -0
  139. package/dist/tool-contract.js +101 -0
  140. package/dist/tools/read.d.ts +2 -10
  141. package/dist/tools/read.d.ts.map +1 -1
  142. package/dist/tools/read.js +662 -537
  143. package/dist/tools/write.d.ts +3 -2
  144. package/dist/tools/write.d.ts.map +1 -1
  145. package/dist/tools/write.js +329 -177
  146. package/dist/uuid.d.ts +20 -0
  147. package/dist/uuid.d.ts.map +1 -0
  148. package/dist/uuid.js +28 -0
  149. package/dist/validation.d.ts +64 -0
  150. package/dist/validation.d.ts.map +1 -0
  151. package/dist/validation.js +236 -0
  152. package/dist/with-resolvers.d.ts +12 -0
  153. package/dist/with-resolvers.d.ts.map +1 -0
  154. package/dist/with-resolvers.js +14 -0
  155. package/dist/xml-escape.d.ts +12 -0
  156. package/dist/xml-escape.d.ts.map +1 -0
  157. package/dist/xml-escape.js +15 -0
  158. package/package.json +1 -1
package/dist/cron.js ADDED
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Minimal 5-field cron expression parser and next-run calculator.
3
+ *
4
+ * Field syntax: wildcard (*), step ( * /N), range (N-M), range-step (N-M/S),
5
+ * list (N,M,...), plain value (N). Sunday alias: 7 → 0.
6
+ *
7
+ * All times are local timezone. DST-safe: spring-forward gaps are skipped,
8
+ * fall-back duplicates fire once (vixie-cron semantics).
9
+ */
10
+ const FIELD_RANGES = [
11
+ { min: 0, max: 59 }, // minute
12
+ { min: 0, max: 23 }, // hour
13
+ { min: 1, max: 31 }, // dayOfMonth
14
+ { min: 1, max: 12 }, // month
15
+ { min: 0, max: 6 }, // dayOfWeek (0=Sunday; 7 accepted as alias)
16
+ ];
17
+ function expandField(field, range) {
18
+ const { min, max } = range;
19
+ const out = new Set();
20
+ for (const part of field.split(",")) {
21
+ // wildcard or */N
22
+ const stepMatch = part.match(/^\*(?:\/(\d+))?$/);
23
+ if (stepMatch) {
24
+ const step = stepMatch[1] ? parseInt(stepMatch[1], 10) : 1;
25
+ if (step < 1)
26
+ return null;
27
+ for (let i = min; i <= max; i += step)
28
+ out.add(i);
29
+ continue;
30
+ }
31
+ // N-M or N-M/S
32
+ const rangeMatch = part.match(/^(\d+)-(\d+)(?:\/(\d+))?$/);
33
+ if (rangeMatch) {
34
+ const lo = parseInt(rangeMatch[1], 10);
35
+ const hi = parseInt(rangeMatch[2], 10);
36
+ const step = rangeMatch[3] ? parseInt(rangeMatch[3], 10) : 1;
37
+ const isDow = min === 0 && max === 6;
38
+ const effMax = isDow ? 7 : max;
39
+ if (lo > hi || step < 1 || lo < min || hi > effMax)
40
+ return null;
41
+ for (let i = lo; i <= hi; i += step) {
42
+ out.add(isDow && i === 7 ? 0 : i);
43
+ }
44
+ continue;
45
+ }
46
+ // plain N
47
+ const singleMatch = part.match(/^\d+$/);
48
+ if (singleMatch) {
49
+ let n = parseInt(part, 10);
50
+ if (min === 0 && max === 6 && n === 7)
51
+ n = 0;
52
+ if (n < min || n > max)
53
+ return null;
54
+ out.add(n);
55
+ continue;
56
+ }
57
+ return null;
58
+ }
59
+ if (out.size === 0)
60
+ return null;
61
+ return Array.from(out).sort((a, b) => a - b);
62
+ }
63
+ /**
64
+ * Parse a 5-field cron expression into expanded number arrays.
65
+ * Returns null if invalid or unsupported syntax.
66
+ */
67
+ export function parseCronExpression(expr) {
68
+ const parts = expr.trim().split(/\s+/);
69
+ if (parts.length !== 5)
70
+ return null;
71
+ const expanded = [];
72
+ for (let i = 0; i < 5; i++) {
73
+ const result = expandField(parts[i], FIELD_RANGES[i]);
74
+ if (!result)
75
+ return null;
76
+ expanded.push(result);
77
+ }
78
+ return {
79
+ minute: expanded[0],
80
+ hour: expanded[1],
81
+ dayOfMonth: expanded[2],
82
+ month: expanded[3],
83
+ dayOfWeek: expanded[4],
84
+ };
85
+ }
86
+ /**
87
+ * Compute the next Date strictly after `from` that matches the cron fields.
88
+ * Uses local timezone. Walks forward minute-by-minute, bounded at 366 days.
89
+ *
90
+ * Standard cron: when both dayOfMonth and dayOfWeek are constrained
91
+ * (neither is the full range), a date matches if EITHER matches.
92
+ */
93
+ export function computeNextCronRun(fields, from) {
94
+ const minuteSet = new Set(fields.minute);
95
+ const hourSet = new Set(fields.hour);
96
+ const domSet = new Set(fields.dayOfMonth);
97
+ const monthSet = new Set(fields.month);
98
+ const dowSet = new Set(fields.dayOfWeek);
99
+ const domWild = fields.dayOfMonth.length === 31;
100
+ const dowWild = fields.dayOfWeek.length === 7;
101
+ // Round up to the next whole minute (strictly after `from`)
102
+ const t = new Date(from.getTime());
103
+ t.setSeconds(0, 0);
104
+ t.setMinutes(t.getMinutes() + 1);
105
+ const maxIter = 366 * 24 * 60;
106
+ for (let i = 0; i < maxIter; i++) {
107
+ const month = t.getMonth() + 1;
108
+ if (!monthSet.has(month)) {
109
+ t.setMonth(t.getMonth() + 1, 1);
110
+ t.setHours(0, 0, 0, 0);
111
+ continue;
112
+ }
113
+ const dom = t.getDate();
114
+ const dow = t.getDay();
115
+ const dayMatches = domWild && dowWild
116
+ ? true
117
+ : domWild
118
+ ? dowSet.has(dow)
119
+ : dowWild
120
+ ? domSet.has(dom)
121
+ : domSet.has(dom) || dowSet.has(dow);
122
+ if (!dayMatches) {
123
+ t.setDate(t.getDate() + 1);
124
+ t.setHours(0, 0, 0, 0);
125
+ continue;
126
+ }
127
+ if (!hourSet.has(t.getHours())) {
128
+ t.setHours(t.getHours() + 1, 0, 0, 0);
129
+ continue;
130
+ }
131
+ if (!minuteSet.has(t.getMinutes())) {
132
+ t.setMinutes(t.getMinutes() + 1);
133
+ continue;
134
+ }
135
+ return t;
136
+ }
137
+ return null;
138
+ }
139
+ // --- cronToHuman ---
140
+ const DAY_NAMES = [
141
+ "Sunday",
142
+ "Monday",
143
+ "Tuesday",
144
+ "Wednesday",
145
+ "Thursday",
146
+ "Friday",
147
+ "Saturday",
148
+ ];
149
+ function formatLocalTime(minute, hour) {
150
+ const d = new Date(2000, 0, 1, hour, minute);
151
+ return d.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
152
+ }
153
+ /**
154
+ * Convert a cron expression to a human-readable description.
155
+ * Covers common patterns; falls through to the raw string for complex ones.
156
+ */
157
+ export function cronToHuman(cron) {
158
+ const parts = cron.trim().split(/\s+/);
159
+ if (parts.length !== 5)
160
+ return cron;
161
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
162
+ // Every N minutes: */N * * * *
163
+ const everyMinMatch = minute.match(/^\*\/(\d+)$/);
164
+ if (everyMinMatch &&
165
+ hour === "*" &&
166
+ dayOfMonth === "*" &&
167
+ month === "*" &&
168
+ dayOfWeek === "*") {
169
+ const n = parseInt(everyMinMatch[1], 10);
170
+ return n === 1 ? "Every minute" : `Every ${n} minutes`;
171
+ }
172
+ // Every hour: N * * * *
173
+ if (minute.match(/^\d+$/) &&
174
+ hour === "*" &&
175
+ dayOfMonth === "*" &&
176
+ month === "*" &&
177
+ dayOfWeek === "*") {
178
+ const m = parseInt(minute, 10);
179
+ if (m === 0)
180
+ return "Every hour";
181
+ return `Every hour at :${m.toString().padStart(2, "0")}`;
182
+ }
183
+ // Every N hours: M */N * * *
184
+ const everyHourMatch = hour.match(/^\*\/(\d+)$/);
185
+ if (minute.match(/^\d+$/) &&
186
+ everyHourMatch &&
187
+ dayOfMonth === "*" &&
188
+ month === "*" &&
189
+ dayOfWeek === "*") {
190
+ const n = parseInt(everyHourMatch[1], 10);
191
+ const m = parseInt(minute, 10);
192
+ const suffix = m === 0 ? "" : ` at :${m.toString().padStart(2, "0")}`;
193
+ return n === 1 ? `Every hour${suffix}` : `Every ${n} hours${suffix}`;
194
+ }
195
+ if (!minute.match(/^\d+$/) || !hour.match(/^\d+$/))
196
+ return cron;
197
+ const m = parseInt(minute, 10);
198
+ const h = parseInt(hour, 10);
199
+ // Daily: M H * * *
200
+ if (dayOfMonth === "*" && month === "*" && dayOfWeek === "*") {
201
+ return `Every day at ${formatLocalTime(m, h)}`;
202
+ }
203
+ // Specific day of week: M H * * D
204
+ if (dayOfMonth === "*" && month === "*" && dayOfWeek.match(/^\d$/)) {
205
+ const dayIndex = parseInt(dayOfWeek, 10) % 7;
206
+ const dayName = DAY_NAMES[dayIndex];
207
+ if (dayName)
208
+ return `Every ${dayName} at ${formatLocalTime(m, h)}`;
209
+ }
210
+ // Weekdays: M H * * 1-5
211
+ if (dayOfMonth === "*" && month === "*" && dayOfWeek === "1-5") {
212
+ return `Weekdays at ${formatLocalTime(m, h)}`;
213
+ }
214
+ return cron;
215
+ }
package/dist/env.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Environment variable utilities — zero external dependencies.
3
+ *
4
+ * Provides consistent truthy/falsy parsing for env vars and a KEY=VALUE
5
+ * argument parser. Handles the three-state problem (true/false/undefined).
6
+ *
7
+ * Inspired by Claude Code's `src/utils/envUtils.ts` (generic parts only).
8
+ */
9
+ /**
10
+ * Check if an environment variable is truthy.
11
+ * Recognizes: "1", "true", "yes", "on" (case-insensitive).
12
+ * Returns `false` for undefined/empty.
13
+ */
14
+ export declare function isEnvTruthy(val: string | boolean | undefined): boolean;
15
+ /**
16
+ * Check if an environment variable is explicitly set to a falsy value.
17
+ * Recognizes: "0", "false", "no", "off" (case-insensitive).
18
+ * Returns `false` for undefined (distinguishes "not set" from "set to false").
19
+ */
20
+ export declare function isEnvDefinedFalsy(val: string | boolean | undefined): boolean;
21
+ /**
22
+ * Parse an array of `KEY=VALUE` strings into a record.
23
+ * Ignores entries without `=`.
24
+ */
25
+ export declare function parseEnvVars(rawArgs: string[] | undefined): Record<string, string>;
26
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAItE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAI5E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxB"}
package/dist/env.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Environment variable utilities — zero external dependencies.
3
+ *
4
+ * Provides consistent truthy/falsy parsing for env vars and a KEY=VALUE
5
+ * argument parser. Handles the three-state problem (true/false/undefined).
6
+ *
7
+ * Inspired by Claude Code's `src/utils/envUtils.ts` (generic parts only).
8
+ */
9
+ const TRUTHY = new Set(["1", "true", "yes", "on"]);
10
+ const FALSY = new Set(["0", "false", "no", "off"]);
11
+ /**
12
+ * Check if an environment variable is truthy.
13
+ * Recognizes: "1", "true", "yes", "on" (case-insensitive).
14
+ * Returns `false` for undefined/empty.
15
+ */
16
+ export function isEnvTruthy(val) {
17
+ if (typeof val === "boolean")
18
+ return val;
19
+ if (!val)
20
+ return false;
21
+ return TRUTHY.has(val.toLowerCase());
22
+ }
23
+ /**
24
+ * Check if an environment variable is explicitly set to a falsy value.
25
+ * Recognizes: "0", "false", "no", "off" (case-insensitive).
26
+ * Returns `false` for undefined (distinguishes "not set" from "set to false").
27
+ */
28
+ export function isEnvDefinedFalsy(val) {
29
+ if (typeof val === "boolean")
30
+ return !val;
31
+ if (!val)
32
+ return false;
33
+ return FALSY.has(val.toLowerCase());
34
+ }
35
+ /**
36
+ * Parse an array of `KEY=VALUE` strings into a record.
37
+ * Ignores entries without `=`.
38
+ */
39
+ export function parseEnvVars(rawArgs) {
40
+ if (!rawArgs)
41
+ return {};
42
+ const result = {};
43
+ for (const arg of rawArgs) {
44
+ const idx = arg.indexOf("=");
45
+ if (idx > 0) {
46
+ result[arg.slice(0, idx)] = arg.slice(idx + 1);
47
+ }
48
+ }
49
+ return result;
50
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Typed error hierarchy for the Oka Reason platform.
3
+ *
4
+ * Zero external dependencies — pure domain layer.
5
+ * (Imports only from sibling domain modules.)
6
+ *
7
+ * Inspired by Claude Code's error taxonomy:
8
+ * - Every error has a machine-readable `code` and human-readable `message`
9
+ * - `telemetryMessage` is PII-safe (never includes tokens, paths, user IDs)
10
+ * - `isRetryable` flag drives retry logic in the client
11
+ * - `category` enables grouping in dashboards
12
+ */
13
+ export type ErrorCategory = "authentication" | "authorization" | "validation" | "network" | "security" | "resource" | "internal";
14
+ export declare class OkaError extends Error {
15
+ /** Machine-readable error code (e.g., "NETWORK_ERROR", "PATH_TRAVERSAL"). */
16
+ readonly code: string;
17
+ /** Grouping category for dashboards. */
18
+ readonly category: ErrorCategory;
19
+ /** PII-safe message suitable for telemetry — never includes tokens, paths, or user IDs. */
20
+ readonly telemetryMessage: string;
21
+ /** Whether this error is transient and the operation can be retried. */
22
+ readonly isRetryable: boolean;
23
+ constructor(message: string, opts: {
24
+ code: string;
25
+ category: ErrorCategory;
26
+ telemetryMessage?: string;
27
+ isRetryable?: boolean;
28
+ cause?: unknown;
29
+ });
30
+ }
31
+ export declare class AuthenticationError extends OkaError {
32
+ constructor(message: string, opts?: {
33
+ cause?: unknown;
34
+ });
35
+ }
36
+ export declare class AuthorizationError extends OkaError {
37
+ constructor(message: string, opts?: {
38
+ cause?: unknown;
39
+ });
40
+ }
41
+ export declare class ValidationError extends OkaError {
42
+ constructor(message: string, opts?: {
43
+ cause?: unknown;
44
+ });
45
+ }
46
+ export declare class PathSecurityError extends OkaError {
47
+ constructor(message: string, opts?: {
48
+ cause?: unknown;
49
+ });
50
+ }
51
+ export declare class ContentTooLargeError extends OkaError {
52
+ readonly sizeBytes: number;
53
+ readonly maxBytes: number;
54
+ constructor(sizeBytes: number, maxBytes: number, opts?: {
55
+ cause?: unknown;
56
+ });
57
+ }
58
+ export declare class SecretDetectedError extends OkaError {
59
+ readonly patternName: string;
60
+ constructor(patternName: string, opts?: {
61
+ cause?: unknown;
62
+ });
63
+ }
64
+ export declare class NetworkError extends OkaError {
65
+ readonly statusCode?: number;
66
+ constructor(message: string, opts?: {
67
+ statusCode?: number;
68
+ isRetryable?: boolean;
69
+ cause?: unknown;
70
+ });
71
+ }
72
+ export declare class AbortError extends OkaError {
73
+ constructor(message?: string, opts?: {
74
+ cause?: unknown;
75
+ });
76
+ }
77
+ /**
78
+ * Check if an error is any kind of abort (our AbortError, DOMException, SDK abort).
79
+ * Mirrors CC's isAbortError() pattern.
80
+ */
81
+ export declare function isAbortError(err: unknown): boolean;
82
+ /** Check if an error is retryable (transient network/capacity issue). */
83
+ export declare function isRetryableError(err: unknown): boolean;
84
+ /**
85
+ * Extract a PII-safe message for telemetry from any error.
86
+ * Never includes tokens, file paths, user IDs, or stack traces.
87
+ */
88
+ export declare function toSafeTelemetryMessage(err: unknown): string;
89
+ /**
90
+ * Normalize any thrown value into an Error instance.
91
+ * Mirrors CC's toError() utility.
92
+ */
93
+ export declare function toError(err: unknown): Error;
94
+ /**
95
+ * Extract a user-facing error message suitable for MCP tool responses.
96
+ * More informative than telemetry messages but still avoids raw stack traces.
97
+ */
98
+ export declare function toUserMessage(err: unknown): string;
99
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,eAAe,GACf,YAAY,GACZ,SAAS,GACT,UAAU,GACV,UAAU,GACV,UAAU,CAAC;AAIf,qBAAa,QAAS,SAAQ,KAAK;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,2FAA2F;IAC3F,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;gBAG5B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,aAAa,CAAC;QACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB;CASJ;AAID,qBAAa,mBAAoB,SAAQ,QAAQ;gBACnC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAUxD;AAED,qBAAa,kBAAmB,SAAQ,QAAQ;gBAClC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAUxD;AAID,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAUxD;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;gBACjC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAUxD;AAED,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAe5E;AAID,qBAAa,mBAAoB,SAAQ,QAAQ;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAW5D;AAID,qBAAa,YAAa,SAAQ,QAAQ;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAgBzE;AAID,qBAAa,UAAW,SAAQ,QAAQ;gBAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAUzD;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAIlD;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAKtD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAS3D;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CAI3C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAclD"}
package/dist/errors.js ADDED
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Typed error hierarchy for the Oka Reason platform.
3
+ *
4
+ * Zero external dependencies — pure domain layer.
5
+ * (Imports only from sibling domain modules.)
6
+ *
7
+ * Inspired by Claude Code's error taxonomy:
8
+ * - Every error has a machine-readable `code` and human-readable `message`
9
+ * - `telemetryMessage` is PII-safe (never includes tokens, paths, user IDs)
10
+ * - `isRetryable` flag drives retry logic in the client
11
+ * - `category` enables grouping in dashboards
12
+ */
13
+ import { humanReadableSize } from "./format.js";
14
+ // ─── Base Error ──────────────────────────────────────────────────────
15
+ export class OkaError extends Error {
16
+ /** Machine-readable error code (e.g., "NETWORK_ERROR", "PATH_TRAVERSAL"). */
17
+ code;
18
+ /** Grouping category for dashboards. */
19
+ category;
20
+ /** PII-safe message suitable for telemetry — never includes tokens, paths, or user IDs. */
21
+ telemetryMessage;
22
+ /** Whether this error is transient and the operation can be retried. */
23
+ isRetryable;
24
+ constructor(message, opts) {
25
+ super(message, { cause: opts.cause });
26
+ this.name = "OkaError";
27
+ this.code = opts.code;
28
+ this.category = opts.category;
29
+ this.telemetryMessage = opts.telemetryMessage ?? opts.code;
30
+ this.isRetryable = opts.isRetryable ?? false;
31
+ }
32
+ }
33
+ // ─── Authentication / Authorization ──────────────────────────────────
34
+ export class AuthenticationError extends OkaError {
35
+ constructor(message, opts) {
36
+ super(message, {
37
+ code: "AUTHENTICATION_FAILED",
38
+ category: "authentication",
39
+ telemetryMessage: "authentication failed",
40
+ isRetryable: false,
41
+ cause: opts?.cause,
42
+ });
43
+ this.name = "AuthenticationError";
44
+ }
45
+ }
46
+ export class AuthorizationError extends OkaError {
47
+ constructor(message, opts) {
48
+ super(message, {
49
+ code: "FORBIDDEN",
50
+ category: "authorization",
51
+ telemetryMessage: "forbidden",
52
+ isRetryable: false,
53
+ cause: opts?.cause,
54
+ });
55
+ this.name = "AuthorizationError";
56
+ }
57
+ }
58
+ // ─── Validation ──────────────────────────────────────────────────────
59
+ export class ValidationError extends OkaError {
60
+ constructor(message, opts) {
61
+ super(message, {
62
+ code: "VALIDATION_ERROR",
63
+ category: "validation",
64
+ telemetryMessage: "input validation failed",
65
+ isRetryable: false,
66
+ cause: opts?.cause,
67
+ });
68
+ this.name = "ValidationError";
69
+ }
70
+ }
71
+ export class PathSecurityError extends OkaError {
72
+ constructor(message, opts) {
73
+ super(message, {
74
+ code: "PATH_TRAVERSAL",
75
+ category: "security",
76
+ telemetryMessage: "path traversal detected",
77
+ isRetryable: false,
78
+ cause: opts?.cause,
79
+ });
80
+ this.name = "PathSecurityError";
81
+ }
82
+ }
83
+ export class ContentTooLargeError extends OkaError {
84
+ sizeBytes;
85
+ maxBytes;
86
+ constructor(sizeBytes, maxBytes, opts) {
87
+ super(`Content too large: ${humanReadableSize(sizeBytes)} (max ${humanReadableSize(maxBytes)})`, {
88
+ code: "CONTENT_TOO_LARGE",
89
+ category: "resource",
90
+ telemetryMessage: `content too large: ${sizeBytes}B > ${maxBytes}B`,
91
+ isRetryable: false,
92
+ cause: opts?.cause,
93
+ });
94
+ this.name = "ContentTooLargeError";
95
+ this.sizeBytes = sizeBytes;
96
+ this.maxBytes = maxBytes;
97
+ }
98
+ }
99
+ // ─── Security ────────────────────────────────────────────────────────
100
+ export class SecretDetectedError extends OkaError {
101
+ patternName;
102
+ constructor(patternName, opts) {
103
+ super(`Secret detected in content: ${patternName}`, {
104
+ code: "SECRET_DETECTED",
105
+ category: "security",
106
+ telemetryMessage: `secret detected: ${patternName}`,
107
+ isRetryable: false,
108
+ cause: opts?.cause,
109
+ });
110
+ this.name = "SecretDetectedError";
111
+ this.patternName = patternName;
112
+ }
113
+ }
114
+ // ─── Network ─────────────────────────────────────────────────────────
115
+ export class NetworkError extends OkaError {
116
+ statusCode;
117
+ constructor(message, opts) {
118
+ const status = opts?.statusCode;
119
+ // 5xx and network failures are retryable; 4xx are not
120
+ const retryable = opts?.isRetryable ?? (status === undefined || status >= 500);
121
+ super(message, {
122
+ code: "NETWORK_ERROR",
123
+ category: "network",
124
+ telemetryMessage: status ? `HTTP ${status}` : "network error",
125
+ isRetryable: retryable,
126
+ cause: opts?.cause,
127
+ });
128
+ this.name = "NetworkError";
129
+ this.statusCode = status;
130
+ }
131
+ }
132
+ // ─── Abort ───────────────────────────────────────────────────────────
133
+ export class AbortError extends OkaError {
134
+ constructor(message, opts) {
135
+ super(message ?? "Operation aborted", {
136
+ code: "ABORTED",
137
+ category: "internal",
138
+ telemetryMessage: "operation aborted",
139
+ isRetryable: false,
140
+ cause: opts?.cause,
141
+ });
142
+ this.name = "AbortError";
143
+ }
144
+ }
145
+ // ─── Helpers ─────────────────────────────────────────────────────────
146
+ /**
147
+ * Check if an error is any kind of abort (our AbortError, DOMException, SDK abort).
148
+ * Mirrors CC's isAbortError() pattern.
149
+ */
150
+ export function isAbortError(err) {
151
+ if (err instanceof AbortError)
152
+ return true;
153
+ if (err instanceof Error && err.name === "AbortError")
154
+ return true;
155
+ return false;
156
+ }
157
+ /** Check if an error is retryable (transient network/capacity issue). */
158
+ export function isRetryableError(err) {
159
+ if (err instanceof OkaError)
160
+ return err.isRetryable;
161
+ // Treat generic network errors as retryable
162
+ if (err instanceof TypeError && err.message.includes("fetch"))
163
+ return true;
164
+ return false;
165
+ }
166
+ /**
167
+ * Extract a PII-safe message for telemetry from any error.
168
+ * Never includes tokens, file paths, user IDs, or stack traces.
169
+ */
170
+ export function toSafeTelemetryMessage(err) {
171
+ if (err instanceof OkaError)
172
+ return err.telemetryMessage;
173
+ if (err instanceof Error) {
174
+ // Strip anything that looks like a path, token, or email
175
+ const msg = err.message;
176
+ if (msg.length > 200)
177
+ return `${err.name}: [message too long]`;
178
+ return err.name;
179
+ }
180
+ return "unknown error";
181
+ }
182
+ /**
183
+ * Normalize any thrown value into an Error instance.
184
+ * Mirrors CC's toError() utility.
185
+ */
186
+ export function toError(err) {
187
+ if (err instanceof Error)
188
+ return err;
189
+ if (typeof err === "string")
190
+ return new Error(err);
191
+ return new Error(String(err));
192
+ }
193
+ /**
194
+ * Extract a user-facing error message suitable for MCP tool responses.
195
+ * More informative than telemetry messages but still avoids raw stack traces.
196
+ */
197
+ export function toUserMessage(err) {
198
+ if (err instanceof AuthenticationError) {
199
+ return "Authentication failed — run mcp__oka__login to re-authenticate.";
200
+ }
201
+ if (err instanceof NetworkError) {
202
+ const status = err.statusCode;
203
+ if (status === 429)
204
+ return "Rate limited — please try again shortly.";
205
+ if (status && status >= 500)
206
+ return `Server error (HTTP ${status}) — please try again.`;
207
+ return err.message;
208
+ }
209
+ if (err instanceof OkaError)
210
+ return err.message;
211
+ if (err instanceof Error)
212
+ return err.message;
213
+ return String(err);
214
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Format utilities — human-readable sizes and durations.
3
+ *
4
+ * ZERO external dependencies — pure domain layer.
5
+ */
6
+ /**
7
+ * Format a byte count as a human-readable string.
8
+ *
9
+ * @example humanReadableSize(1536) // "1.5 KB"
10
+ * @example humanReadableSize(0) // "0 B"
11
+ */
12
+ export declare function humanReadableSize(bytes: number): string;
13
+ /**
14
+ * Format a duration in milliseconds as a human-readable string.
15
+ *
16
+ * @example humanReadableDuration(350) // "350ms"
17
+ * @example humanReadableDuration(1200) // "1.2s"
18
+ * @example humanReadableDuration(135000) // "2m 15s"
19
+ */
20
+ export declare function humanReadableDuration(ms: number): string;
21
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAevD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAcxD"}
package/dist/format.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Format utilities — human-readable sizes and durations.
3
+ *
4
+ * ZERO external dependencies — pure domain layer.
5
+ */
6
+ const SIZE_UNITS = ["B", "KB", "MB", "GB", "TB"];
7
+ /**
8
+ * Format a byte count as a human-readable string.
9
+ *
10
+ * @example humanReadableSize(1536) // "1.5 KB"
11
+ * @example humanReadableSize(0) // "0 B"
12
+ */
13
+ export function humanReadableSize(bytes) {
14
+ if (bytes < 0)
15
+ return `-${humanReadableSize(-bytes)}`;
16
+ if (bytes === 0)
17
+ return "0 B";
18
+ let value = bytes;
19
+ let unitIndex = 0;
20
+ while (value >= 1024 && unitIndex < SIZE_UNITS.length - 1) {
21
+ value /= 1024;
22
+ unitIndex++;
23
+ }
24
+ const formatted = unitIndex === 0 ? String(value) : value.toFixed(1).replace(/\.0$/, "");
25
+ return `${formatted} ${SIZE_UNITS[unitIndex]}`;
26
+ }
27
+ /**
28
+ * Format a duration in milliseconds as a human-readable string.
29
+ *
30
+ * @example humanReadableDuration(350) // "350ms"
31
+ * @example humanReadableDuration(1200) // "1.2s"
32
+ * @example humanReadableDuration(135000) // "2m 15s"
33
+ */
34
+ export function humanReadableDuration(ms) {
35
+ if (ms < 0)
36
+ return `-${humanReadableDuration(-ms)}`;
37
+ if (ms < 1000)
38
+ return `${Math.round(ms)}ms`;
39
+ const seconds = ms / 1000;
40
+ if (seconds < 60) {
41
+ return `${seconds.toFixed(1).replace(/\.0$/, "")}s`;
42
+ }
43
+ const minutes = Math.floor(seconds / 60);
44
+ const remainingSeconds = Math.round(seconds % 60);
45
+ if (remainingSeconds === 0)
46
+ return `${minutes}m`;
47
+ return `${minutes}m ${remainingSeconds}s`;
48
+ }