ultracode-shared 1.3.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/constants.d.ts +33 -0
  4. package/dist/constants.d.ts.map +1 -0
  5. package/dist/constants.js +37 -0
  6. package/dist/constants.js.map +1 -0
  7. package/dist/errors.d.ts +113 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/errors.js +155 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/events/EventBus.d.ts +36 -0
  12. package/dist/events/EventBus.d.ts.map +1 -0
  13. package/dist/events/EventBus.js +83 -0
  14. package/dist/events/EventBus.js.map +1 -0
  15. package/dist/index.d.ts +30 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +34 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/logger.d.ts +65 -0
  20. package/dist/logger.d.ts.map +1 -0
  21. package/dist/logger.js +74 -0
  22. package/dist/logger.js.map +1 -0
  23. package/dist/types/agent.d.ts +68 -0
  24. package/dist/types/agent.d.ts.map +1 -0
  25. package/dist/types/agent.js +9 -0
  26. package/dist/types/agent.js.map +1 -0
  27. package/dist/types/config.d.ts +265 -0
  28. package/dist/types/config.d.ts.map +1 -0
  29. package/dist/types/config.js +103 -0
  30. package/dist/types/config.js.map +1 -0
  31. package/dist/types/events.d.ts +78 -0
  32. package/dist/types/events.d.ts.map +1 -0
  33. package/dist/types/events.js +10 -0
  34. package/dist/types/events.js.map +1 -0
  35. package/dist/types/memory.d.ts +53 -0
  36. package/dist/types/memory.d.ts.map +1 -0
  37. package/dist/types/memory.js +9 -0
  38. package/dist/types/memory.js.map +1 -0
  39. package/dist/types/provider.d.ts +129 -0
  40. package/dist/types/provider.d.ts.map +1 -0
  41. package/dist/types/provider.js +10 -0
  42. package/dist/types/provider.js.map +1 -0
  43. package/dist/types/tool.d.ts +122 -0
  44. package/dist/types/tool.d.ts.map +1 -0
  45. package/dist/types/tool.js +11 -0
  46. package/dist/types/tool.js.map +1 -0
  47. package/dist/types/versioning.d.ts +141 -0
  48. package/dist/types/versioning.d.ts.map +1 -0
  49. package/dist/types/versioning.js +8 -0
  50. package/dist/types/versioning.js.map +1 -0
  51. package/dist/utils/async.d.ts +17 -0
  52. package/dist/utils/async.d.ts.map +1 -0
  53. package/dist/utils/async.js +45 -0
  54. package/dist/utils/async.js.map +1 -0
  55. package/dist/utils/id.d.ts +13 -0
  56. package/dist/utils/id.d.ts.map +1 -0
  57. package/dist/utils/id.js +17 -0
  58. package/dist/utils/id.js.map +1 -0
  59. package/dist/utils/index.d.ts +11 -0
  60. package/dist/utils/index.d.ts.map +1 -0
  61. package/dist/utils/index.js +10 -0
  62. package/dist/utils/index.js.map +1 -0
  63. package/dist/utils/math.d.ts +15 -0
  64. package/dist/utils/math.d.ts.map +1 -0
  65. package/dist/utils/math.js +25 -0
  66. package/dist/utils/math.js.map +1 -0
  67. package/dist/utils/object.d.ts +36 -0
  68. package/dist/utils/object.d.ts.map +1 -0
  69. package/dist/utils/object.js +122 -0
  70. package/dist/utils/object.js.map +1 -0
  71. package/dist/utils/serializeError.d.ts +15 -0
  72. package/dist/utils/serializeError.d.ts.map +1 -0
  73. package/dist/utils/serializeError.js +69 -0
  74. package/dist/utils/serializeError.js.map +1 -0
  75. package/dist/utils/truncate.d.ts +29 -0
  76. package/dist/utils/truncate.d.ts.map +1 -0
  77. package/dist/utils/truncate.js +42 -0
  78. package/dist/utils/truncate.js.map +1 -0
  79. package/package.json +39 -0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Async helpers: cancellable {@link sleep}.
3
+ */
4
+ /**
5
+ * Resolve after `ms` milliseconds, with optional cancellation.
6
+ *
7
+ * - If `signal` is already aborted, rejects immediately with the signal's
8
+ * `reason` (or an `AbortError` if none).
9
+ * - If `signal` aborts before the timer fires, the timer is cleared and the
10
+ * promise rejects with the abort reason.
11
+ * - Negative or non-finite `ms` is clamped to `0`.
12
+ *
13
+ * @param ms - delay in milliseconds.
14
+ * @param signal - optional cancellation signal.
15
+ */
16
+ export function sleep(ms, signal) {
17
+ const delay = Number.isFinite(ms) && ms > 0 ? ms : 0;
18
+ return new Promise((resolve, reject) => {
19
+ if (signal?.aborted) {
20
+ reject(abortReason(signal));
21
+ return;
22
+ }
23
+ let onAbort;
24
+ const timer = setTimeout(() => {
25
+ if (signal !== undefined && onAbort !== undefined) {
26
+ signal.removeEventListener("abort", onAbort);
27
+ }
28
+ resolve();
29
+ }, delay);
30
+ if (signal !== undefined) {
31
+ onAbort = () => {
32
+ clearTimeout(timer);
33
+ reject(abortReason(signal));
34
+ };
35
+ signal.addEventListener("abort", onAbort, { once: true });
36
+ }
37
+ });
38
+ }
39
+ /** Extract a rejection reason from an aborted signal. */
40
+ function abortReason(signal) {
41
+ if (signal.reason !== undefined)
42
+ return signal.reason;
43
+ return new DOMException("The operation was aborted.", "AbortError");
44
+ }
45
+ //# sourceMappingURL=async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/utils/async.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAErD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,IAAI,OAAiC,CAAC;QAEtC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAClD,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC/C,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,KAAK,CAAC,CAAC;QAEV,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,GAAG,GAAS,EAAE;gBACnB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YAC9B,CAAC,CAAC;YACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,yDAAyD;AACzD,SAAS,WAAW,CAAC,MAAmB;IACtC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC;IACtD,OAAO,IAAI,YAAY,CAAC,4BAA4B,EAAE,YAAY,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Identifier generation.
3
+ */
4
+ /**
5
+ * Generate a unique identifier backed by `crypto.randomUUID()`.
6
+ *
7
+ * - Without a prefix: returns the bare UUID (e.g. `"f47ac10b-58cc-…"`).
8
+ * - With a prefix: returns `"<prefix>_<uuid>"` (e.g. `"sess_f47ac10b-…"`).
9
+ *
10
+ * @param prefix - optional namespace prefix (no trailing separator needed).
11
+ */
12
+ export declare function generateId(prefix?: string): string;
13
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlD"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Identifier generation.
3
+ */
4
+ import { randomUUID } from "node:crypto";
5
+ /**
6
+ * Generate a unique identifier backed by `crypto.randomUUID()`.
7
+ *
8
+ * - Without a prefix: returns the bare UUID (e.g. `"f47ac10b-58cc-…"`).
9
+ * - With a prefix: returns `"<prefix>_<uuid>"` (e.g. `"sess_f47ac10b-…"`).
10
+ *
11
+ * @param prefix - optional namespace prefix (no trailing separator needed).
12
+ */
13
+ export function generateId(prefix) {
14
+ const uuid = randomUUID();
15
+ return prefix !== undefined && prefix.length > 0 ? `${prefix}_${uuid}` : uuid;
16
+ }
17
+ //# sourceMappingURL=id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe;IACxC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChF,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Barrel for the shared utility functions.
3
+ */
4
+ export { serializeError } from "./serializeError.js";
5
+ export { truncate, DEFAULT_TRUNCATE_SUFFIX } from "./truncate.js";
6
+ export type { TruncateOptions } from "./truncate.js";
7
+ export { isPlainObject, deepMerge } from "./object.js";
8
+ export { sleep } from "./async.js";
9
+ export { clamp } from "./math.js";
10
+ export { generateId } from "./id.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Barrel for the shared utility functions.
3
+ */
4
+ export { serializeError } from "./serializeError.js";
5
+ export { truncate, DEFAULT_TRUNCATE_SUFFIX } from "./truncate.js";
6
+ export { isPlainObject, deepMerge } from "./object.js";
7
+ export { sleep } from "./async.js";
8
+ export { clamp } from "./math.js";
9
+ export { generateId } from "./id.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Numeric helpers.
3
+ */
4
+ /**
5
+ * Constrain `value` to the inclusive range `[min, max]`.
6
+ *
7
+ * - `NaN` input returns `min` (defensive default).
8
+ * - If `min > max`, the bounds are swapped so the function is total.
9
+ *
10
+ * @param value - the value to clamp.
11
+ * @param min - lower bound.
12
+ * @param max - upper bound.
13
+ */
14
+ export declare function clamp(value: number, min: number, max: number): number;
15
+ //# sourceMappingURL=math.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/utils/math.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAOrE"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Numeric helpers.
3
+ */
4
+ /**
5
+ * Constrain `value` to the inclusive range `[min, max]`.
6
+ *
7
+ * - `NaN` input returns `min` (defensive default).
8
+ * - If `min > max`, the bounds are swapped so the function is total.
9
+ *
10
+ * @param value - the value to clamp.
11
+ * @param min - lower bound.
12
+ * @param max - upper bound.
13
+ */
14
+ export function clamp(value, min, max) {
15
+ const lo = Math.min(min, max);
16
+ const hi = Math.max(min, max);
17
+ if (Number.isNaN(value))
18
+ return lo;
19
+ if (value < lo)
20
+ return lo;
21
+ if (value > hi)
22
+ return hi;
23
+ return value;
24
+ }
25
+ //# sourceMappingURL=math.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/utils/math.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC3D,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,EAAE,CAAC;IAC1B,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Object utilities: plain-object detection and a prototype-pollution-safe deep
3
+ * merge used to layer configuration (project over user over defaults).
4
+ */
5
+ /**
6
+ * Maximum nesting depth `deepMerge`/`clonePlain` will recurse before failing
7
+ * fast with a {@link ValidationError}. Guards against stack-overflow crashes
8
+ * from pathologically deep (or cyclic) override trees.
9
+ */
10
+ export declare const MAX_MERGE_DEPTH = 100;
11
+ /**
12
+ * Whether `value` is a "plain" object: a non-null object whose prototype is
13
+ * `Object.prototype` or `null`. Arrays, class instances, `Date`, `Map`, etc.
14
+ * are rejected.
15
+ */
16
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
17
+ /**
18
+ * Recursively merge `override` onto `base`, returning a new object.
19
+ *
20
+ * Semantics:
21
+ * - Plain objects are merged key-by-key, recursively.
22
+ * - **Arrays are replaced wholesale**, never concatenated or element-merged.
23
+ * - `undefined` values in `override` are skipped (they do not erase `base`).
24
+ * `null` values *do* overwrite (an explicit "set to null").
25
+ * - Non-plain objects (class instances, `Date`, `Map`, …) in `override` replace
26
+ * the corresponding `base` value wholesale.
27
+ * - **Prototype pollution is prevented**: keys `__proto__`, `constructor` and
28
+ * `prototype` are never copied, and only own-enumerable string keys are read.
29
+ *
30
+ * Neither input is mutated. The result is a fresh, null-prototype-free object
31
+ * tree for the merged portions.
32
+ *
33
+ * @typeParam T - the (resolved) shape of the base object.
34
+ */
35
+ export declare function deepMerge<T>(base: T, override: unknown): T;
36
+ //# sourceMappingURL=object.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/utils/object.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;;;GAIG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAK9E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,GAAG,CAAC,CAE1D"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Object utilities: plain-object detection and a prototype-pollution-safe deep
3
+ * merge used to layer configuration (project over user over defaults).
4
+ */
5
+ import { ValidationError } from "../errors.js";
6
+ /** Keys that must never be assigned during a merge (prototype-pollution guard). */
7
+ const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
8
+ /**
9
+ * Maximum nesting depth `deepMerge`/`clonePlain` will recurse before failing
10
+ * fast with a {@link ValidationError}. Guards against stack-overflow crashes
11
+ * from pathologically deep (or cyclic) override trees.
12
+ */
13
+ export const MAX_MERGE_DEPTH = 100;
14
+ /**
15
+ * Whether `value` is a "plain" object: a non-null object whose prototype is
16
+ * `Object.prototype` or `null`. Arrays, class instances, `Date`, `Map`, etc.
17
+ * are rejected.
18
+ */
19
+ export function isPlainObject(value) {
20
+ if (typeof value !== "object" || value === null)
21
+ return false;
22
+ if (Array.isArray(value))
23
+ return false;
24
+ const proto = Object.getPrototypeOf(value);
25
+ return proto === Object.prototype || proto === null;
26
+ }
27
+ /**
28
+ * Recursively merge `override` onto `base`, returning a new object.
29
+ *
30
+ * Semantics:
31
+ * - Plain objects are merged key-by-key, recursively.
32
+ * - **Arrays are replaced wholesale**, never concatenated or element-merged.
33
+ * - `undefined` values in `override` are skipped (they do not erase `base`).
34
+ * `null` values *do* overwrite (an explicit "set to null").
35
+ * - Non-plain objects (class instances, `Date`, `Map`, …) in `override` replace
36
+ * the corresponding `base` value wholesale.
37
+ * - **Prototype pollution is prevented**: keys `__proto__`, `constructor` and
38
+ * `prototype` are never copied, and only own-enumerable string keys are read.
39
+ *
40
+ * Neither input is mutated. The result is a fresh, null-prototype-free object
41
+ * tree for the merged portions.
42
+ *
43
+ * @typeParam T - the (resolved) shape of the base object.
44
+ */
45
+ export function deepMerge(base, override) {
46
+ return deepMergeAt(base, override, 0);
47
+ }
48
+ /** Recursive worker for {@link deepMerge}; tracks nesting `depth`. */
49
+ function deepMergeAt(base, override, depth) {
50
+ if (depth > MAX_MERGE_DEPTH) {
51
+ throw new ValidationError(`deepMerge exceeded maximum nesting depth of ${MAX_MERGE_DEPTH} ` +
52
+ `(cyclic or pathologically deep override)`, { details: { maxDepth: MAX_MERGE_DEPTH } });
53
+ }
54
+ // Non-mergeable override (primitive, array, class instance) wins outright,
55
+ // unless it is `undefined` (treated as "no override").
56
+ if (override === undefined)
57
+ return clonePlainAt(base, depth, new WeakSet());
58
+ if (!isPlainObject(base) || !isPlainObject(override)) {
59
+ return clonePlainAt(override, depth, new WeakSet());
60
+ }
61
+ const out = clonePlainAt(base, depth, new WeakSet());
62
+ for (const key of Object.keys(override)) {
63
+ if (FORBIDDEN_KEYS.has(key))
64
+ continue;
65
+ if (!Object.prototype.hasOwnProperty.call(override, key))
66
+ continue;
67
+ const overrideVal = override[key];
68
+ if (overrideVal === undefined)
69
+ continue; // skip, keep base
70
+ const baseVal = out[key];
71
+ if (isPlainObject(baseVal) && isPlainObject(overrideVal)) {
72
+ out[key] = deepMergeAt(baseVal, overrideVal, depth + 1);
73
+ }
74
+ else {
75
+ out[key] = clonePlainAt(overrideVal, depth + 1, new WeakSet());
76
+ }
77
+ }
78
+ return out;
79
+ }
80
+ /**
81
+ * Shallow-recursive clone that copies plain objects and arrays while leaving
82
+ * other values (primitives, class instances) by reference. Drops forbidden keys.
83
+ * Tracks nesting `depth` and the chain of `seen` containers (cycle guard).
84
+ *
85
+ * Cyclic references and pathologically deep trees are rejected with a
86
+ * {@link ValidationError} rather than crashing the stack.
87
+ */
88
+ function clonePlainAt(value, depth, seen) {
89
+ if (depth > MAX_MERGE_DEPTH) {
90
+ throw new ValidationError(`deepMerge exceeded maximum nesting depth of ${MAX_MERGE_DEPTH} ` +
91
+ `(cyclic or pathologically deep override)`, { details: { maxDepth: MAX_MERGE_DEPTH } });
92
+ }
93
+ if (Array.isArray(value)) {
94
+ if (seen.has(value)) {
95
+ throw new ValidationError("deepMerge detected a circular reference", {
96
+ details: { kind: "array" },
97
+ });
98
+ }
99
+ seen.add(value);
100
+ const cloned = value.map((item) => clonePlainAt(item, depth + 1, seen));
101
+ seen.delete(value);
102
+ return cloned;
103
+ }
104
+ if (isPlainObject(value)) {
105
+ if (seen.has(value)) {
106
+ throw new ValidationError("deepMerge detected a circular reference", {
107
+ details: { kind: "object" },
108
+ });
109
+ }
110
+ seen.add(value);
111
+ const out = {};
112
+ for (const key of Object.keys(value)) {
113
+ if (FORBIDDEN_KEYS.has(key))
114
+ continue;
115
+ out[key] = clonePlainAt(value[key], depth + 1, seen);
116
+ }
117
+ seen.delete(value);
118
+ return out;
119
+ }
120
+ return value;
121
+ }
122
+ //# sourceMappingURL=object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object.js","sourceRoot":"","sources":["../../src/utils/object.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Reduce an arbitrary thrown value to a stable, JSON-safe {@link SerializedError}.
3
+ *
4
+ * Handles {@link UltraCodeError} (preserving its `code`), plain `Error`s,
5
+ * string/primitive throws, and structurally error-shaped objects. Never throws.
6
+ */
7
+ import type { SerializedError } from "../errors.js";
8
+ /**
9
+ * Convert any caught value into a {@link SerializedError}.
10
+ *
11
+ * @param err - the thrown value (may be anything).
12
+ * @returns a JSON-safe error descriptor with at least `name`, `message`, `code`.
13
+ */
14
+ export declare function serializeError(err: unknown): SerializedError;
15
+ //# sourceMappingURL=serializeError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializeError.d.ts","sourceRoot":"","sources":["../../src/utils/serializeError.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAQpD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,CAwC5D"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Reduce an arbitrary thrown value to a stable, JSON-safe {@link SerializedError}.
3
+ *
4
+ * Handles {@link UltraCodeError} (preserving its `code`), plain `Error`s,
5
+ * string/primitive throws, and structurally error-shaped objects. Never throws.
6
+ */
7
+ import { UltraCodeError } from "../errors.js";
8
+ /** Type guard for objects that look like an `Error`. */
9
+ function isErrorLike(value) {
10
+ return typeof value === "object" && value !== null;
11
+ }
12
+ /**
13
+ * Convert any caught value into a {@link SerializedError}.
14
+ *
15
+ * @param err - the thrown value (may be anything).
16
+ * @returns a JSON-safe error descriptor with at least `name`, `message`, `code`.
17
+ */
18
+ export function serializeError(err) {
19
+ // UltraCodeError already knows how to serialise itself.
20
+ if (err instanceof UltraCodeError) {
21
+ return err.toJSON();
22
+ }
23
+ if (err instanceof Error) {
24
+ const out = {
25
+ name: err.name || "Error",
26
+ message: err.message,
27
+ code: typeof err.code === "string"
28
+ ? err.code
29
+ : "ERROR",
30
+ };
31
+ if (typeof err.stack === "string")
32
+ out.stack = err.stack;
33
+ return out;
34
+ }
35
+ // String throws: `throw "boom"`.
36
+ if (typeof err === "string") {
37
+ return { name: "Error", message: err, code: "ERROR" };
38
+ }
39
+ // Error-shaped plain objects (e.g. structured-clone survivors).
40
+ if (isErrorLike(err)) {
41
+ const name = typeof err.name === "string" ? err.name : "Error";
42
+ const message = typeof err.message === "string" ? err.message : safeStringify(err);
43
+ const out = {
44
+ name,
45
+ message,
46
+ code: typeof err.code === "string" ? err.code : "ERROR",
47
+ };
48
+ if (typeof err.stack === "string")
49
+ out.stack = err.stack;
50
+ return out;
51
+ }
52
+ // Fallback for numbers, booleans, null, undefined, symbols, bigints.
53
+ return { name: "Error", message: safeStringify(err), code: "ERROR" };
54
+ }
55
+ /** `JSON.stringify` that degrades gracefully and never throws. */
56
+ function safeStringify(value) {
57
+ if (value === undefined)
58
+ return "undefined";
59
+ if (value === null)
60
+ return "null";
61
+ try {
62
+ const s = JSON.stringify(value);
63
+ return s ?? String(value);
64
+ }
65
+ catch {
66
+ return String(value);
67
+ }
68
+ }
69
+ //# sourceMappingURL=serializeError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializeError.js","sourceRoot":"","sources":["../../src/utils/serializeError.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,wDAAwD;AACxD,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,wDAAwD;IACxD,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;QAClC,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC;IACtB,CAAC;IAED,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAoB;YAC3B,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,OAAO;YACzB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,IAAI,EACF,OAAQ,GAAqC,CAAC,IAAI,KAAK,QAAQ;gBAC7D,CAAC,CAAE,GAAmC,CAAC,IAAI;gBAC3C,CAAC,CAAC,OAAO;SACd,CAAC;QACF,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;YAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;QACzD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,iCAAiC;IACjC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACxD,CAAC;IAED,gEAAgE;IAChE,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;QAC/D,MAAM,OAAO,GACX,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACrE,MAAM,GAAG,GAAoB;YAC3B,IAAI;YACJ,OAAO;YACP,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO;SACxD,CAAC;QACF,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;YAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;QACzD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,qEAAqE;IACrE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACvE,CAAC;AAED,kEAAkE;AAClE,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IAC5C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Unicode-safe string truncation.
3
+ *
4
+ * Truncation respects Unicode code points (so a surrogate pair / emoji is never
5
+ * split in half) and appends an ellipsis suffix that is included in the budget.
6
+ */
7
+ /** Default suffix appended when a string is truncated. */
8
+ export declare const DEFAULT_TRUNCATE_SUFFIX = "\u2026";
9
+ /** Options for {@link truncate}. */
10
+ export interface TruncateOptions {
11
+ /** Suffix appended on truncation. Defaults to `"…"`. */
12
+ suffix?: string;
13
+ }
14
+ /**
15
+ * Truncate `s` so the result is at most `max` code points long.
16
+ *
17
+ * - If `s` already fits, it is returned unchanged.
18
+ * - If `max <= 0`, the empty string is returned.
19
+ * - The `suffix` counts toward `max`; if the suffix alone exceeds `max`, the
20
+ * suffix itself is code-point-truncated to fit.
21
+ * - Counting and slicing operate on code points (via the string iterator), so
22
+ * emoji and other astral-plane characters are never cut mid-pair.
23
+ *
24
+ * @param s - input string.
25
+ * @param max - maximum length in Unicode code points.
26
+ * @param options - truncation options.
27
+ */
28
+ export declare function truncate(s: string, max: number, options?: TruncateOptions): string;
29
+ //# sourceMappingURL=truncate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../src/utils/truncate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,0DAA0D;AAC1D,eAAO,MAAM,uBAAuB,WAAM,CAAC;AAE3C,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAmBtF"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Unicode-safe string truncation.
3
+ *
4
+ * Truncation respects Unicode code points (so a surrogate pair / emoji is never
5
+ * split in half) and appends an ellipsis suffix that is included in the budget.
6
+ */
7
+ /** Default suffix appended when a string is truncated. */
8
+ export const DEFAULT_TRUNCATE_SUFFIX = "…";
9
+ /**
10
+ * Truncate `s` so the result is at most `max` code points long.
11
+ *
12
+ * - If `s` already fits, it is returned unchanged.
13
+ * - If `max <= 0`, the empty string is returned.
14
+ * - The `suffix` counts toward `max`; if the suffix alone exceeds `max`, the
15
+ * suffix itself is code-point-truncated to fit.
16
+ * - Counting and slicing operate on code points (via the string iterator), so
17
+ * emoji and other astral-plane characters are never cut mid-pair.
18
+ *
19
+ * @param s - input string.
20
+ * @param max - maximum length in Unicode code points.
21
+ * @param options - truncation options.
22
+ */
23
+ export function truncate(s, max, options = {}) {
24
+ if (max <= 0)
25
+ return "";
26
+ // Fast path: byte/unit length is an upper bound on code-point count, so if it
27
+ // already fits there is no possible truncation. Avoids iterating huge strings.
28
+ if (s.length <= max)
29
+ return s;
30
+ const suffix = options.suffix ?? DEFAULT_TRUNCATE_SUFFIX;
31
+ const codePoints = Array.from(s);
32
+ if (codePoints.length <= max)
33
+ return s;
34
+ const suffixPoints = Array.from(suffix);
35
+ // If the suffix cannot fit, truncate the suffix itself.
36
+ if (suffixPoints.length >= max) {
37
+ return suffixPoints.slice(0, max).join("");
38
+ }
39
+ const keep = max - suffixPoints.length;
40
+ return codePoints.slice(0, keep).join("") + suffix;
41
+ }
42
+ //# sourceMappingURL=truncate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncate.js","sourceRoot":"","sources":["../../src/utils/truncate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAQ3C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,QAAQ,CAAC,CAAS,EAAE,GAAW,EAAE,UAA2B,EAAE;IAC5E,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAExB,8EAA8E;IAC9E,+EAA+E;IAC/E,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,uBAAuB,CAAC;IACzD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,UAAU,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,CAAC,CAAC;IAEvC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,IAAI,YAAY,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QAC/B,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC;IACvC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;AACrD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "ultracode-shared",
3
+ "version": "1.3.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "description": "Shared types, contracts, errors and utilities for UltraCode",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/AndreasSchwan-SIMO/UltraCode.git",
26
+ "directory": "packages/shared"
27
+ },
28
+ "homepage": "https://github.com/AndreasSchwan-SIMO/UltraCode#readme",
29
+ "bugs": {
30
+ "url": "https://github.com/AndreasSchwan-SIMO/UltraCode/issues"
31
+ },
32
+ "engines": {
33
+ "node": ">=22.13.0"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc -b",
37
+ "test": "vitest run"
38
+ }
39
+ }