@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.
- package/dist/abort-controller.d.ts +19 -0
- package/dist/abort-controller.d.ts.map +1 -0
- package/dist/abort-controller.js +53 -0
- package/dist/activity-tracker.d.ts +48 -0
- package/dist/activity-tracker.d.ts.map +1 -0
- package/dist/activity-tracker.js +80 -0
- package/dist/analytics.d.ts +49 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +88 -0
- package/dist/array.d.ts +12 -0
- package/dist/array.d.ts.map +1 -0
- package/dist/array.js +20 -0
- package/dist/async-context.d.ts +20 -0
- package/dist/async-context.d.ts.map +1 -0
- package/dist/async-context.js +25 -0
- package/dist/binary-check.d.ts +16 -0
- package/dist/binary-check.d.ts.map +1 -0
- package/dist/binary-check.js +43 -0
- package/dist/buffered-writer.d.ts +30 -0
- package/dist/buffered-writer.d.ts.map +1 -0
- package/dist/buffered-writer.js +87 -0
- package/dist/circular-buffer.d.ts +28 -0
- package/dist/circular-buffer.d.ts.map +1 -0
- package/dist/circular-buffer.js +61 -0
- package/dist/cleanup-registry.d.ts +23 -0
- package/dist/cleanup-registry.d.ts.map +1 -0
- package/dist/cleanup-registry.js +34 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +32 -10
- package/dist/combined-abort-signal.d.ts +25 -0
- package/dist/combined-abort-signal.d.ts.map +1 -0
- package/dist/combined-abort-signal.js +47 -0
- package/dist/cron-lock.d.ts +29 -0
- package/dist/cron-lock.d.ts.map +1 -0
- package/dist/cron-lock.js +127 -0
- package/dist/cron-scheduler.d.ts +41 -0
- package/dist/cron-scheduler.d.ts.map +1 -0
- package/dist/cron-scheduler.js +189 -0
- package/dist/cron-tasks.d.ts +86 -0
- package/dist/cron-tasks.d.ts.map +1 -0
- package/dist/cron-tasks.js +205 -0
- package/dist/cron.d.ts +35 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +215 -0
- package/dist/env.d.ts +26 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +50 -0
- package/dist/errors.d.ts +99 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +214 -0
- package/dist/format.d.ts +21 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +48 -0
- package/dist/fps-tracker.d.ts +22 -0
- package/dist/fps-tracker.d.ts.map +1 -0
- package/dist/fps-tracker.js +44 -0
- package/dist/graceful-shutdown.d.ts +35 -0
- package/dist/graceful-shutdown.d.ts.map +1 -0
- package/dist/graceful-shutdown.js +89 -0
- package/dist/hash.d.ts +21 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +31 -0
- package/dist/heap-diagnostics.d.ts +68 -0
- package/dist/heap-diagnostics.d.ts.map +1 -0
- package/dist/heap-diagnostics.js +110 -0
- package/dist/idle-timeout.d.ts +21 -0
- package/dist/idle-timeout.d.ts.map +1 -0
- package/dist/idle-timeout.js +42 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/intl.d.ts +18 -0
- package/dist/intl.d.ts.map +1 -0
- package/dist/intl.js +75 -0
- package/dist/jsonl.d.ts +16 -0
- package/dist/jsonl.d.ts.map +1 -0
- package/dist/jsonl.js +60 -0
- package/dist/lazy-schema.d.ts +6 -0
- package/dist/lazy-schema.d.ts.map +1 -0
- package/dist/lazy-schema.js +8 -0
- package/dist/memo.d.ts +64 -0
- package/dist/memo.d.ts.map +1 -0
- package/dist/memo.js +162 -0
- package/dist/pkce.d.ts +13 -0
- package/dist/pkce.d.ts.map +1 -0
- package/dist/pkce.js +28 -0
- package/dist/priority-queue.d.ts +36 -0
- package/dist/priority-queue.d.ts.map +1 -0
- package/dist/priority-queue.js +97 -0
- package/dist/process-utils.d.ts +20 -0
- package/dist/process-utils.d.ts.map +1 -0
- package/dist/process-utils.js +54 -0
- package/dist/query-guard.d.ts +34 -0
- package/dist/query-guard.d.ts.map +1 -0
- package/dist/query-guard.js +74 -0
- package/dist/retry.d.ts +60 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +89 -0
- package/dist/schemas.d.ts +6 -6
- package/dist/secrets.d.ts +44 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +115 -0
- package/dist/semantic-types.d.ts +39 -0
- package/dist/semantic-types.d.ts.map +1 -0
- package/dist/semantic-types.js +49 -0
- package/dist/sequential.d.ts +21 -0
- package/dist/sequential.d.ts.map +1 -0
- package/dist/sequential.js +49 -0
- package/dist/signal.d.ts +29 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +39 -0
- package/dist/sleep.d.ts +21 -0
- package/dist/sleep.d.ts.map +1 -0
- package/dist/sleep.js +58 -0
- package/dist/slow-ops.d.ts +41 -0
- package/dist/slow-ops.d.ts.map +1 -0
- package/dist/slow-ops.js +133 -0
- package/dist/store.d.ts +20 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +34 -0
- package/dist/stream.d.ts +29 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +92 -0
- package/dist/string-utils.d.ts +46 -0
- package/dist/string-utils.d.ts.map +1 -0
- package/dist/string-utils.js +69 -0
- package/dist/strip-bom.d.ts +8 -0
- package/dist/strip-bom.d.ts.map +1 -0
- package/dist/strip-bom.js +10 -0
- package/dist/subprocess-env.d.ts +25 -0
- package/dist/subprocess-env.d.ts.map +1 -0
- package/dist/subprocess-env.js +55 -0
- package/dist/temp-file.d.ts +18 -0
- package/dist/temp-file.d.ts.map +1 -0
- package/dist/temp-file.js +26 -0
- package/dist/tool-contract.d.ts +85 -0
- package/dist/tool-contract.d.ts.map +1 -0
- package/dist/tool-contract.js +101 -0
- package/dist/tools/read.d.ts +2 -10
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +662 -537
- package/dist/tools/write.d.ts +3 -2
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +329 -177
- package/dist/uuid.d.ts +20 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +28 -0
- package/dist/validation.d.ts +64 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +236 -0
- package/dist/with-resolvers.d.ts +12 -0
- package/dist/with-resolvers.d.ts.map +1 -0
- package/dist/with-resolvers.js +14 -0
- package/dist/xml-escape.d.ts +12 -0
- package/dist/xml-escape.d.ts.map +1 -0
- package/dist/xml-escape.js +15 -0
- package/package.json +1 -1
package/dist/jsonl.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL parsing and safe JSON utilities.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Claude Code's `src/utils/json.ts` (generic parts only).
|
|
5
|
+
*/
|
|
6
|
+
import { createLRU } from "./memo.js";
|
|
7
|
+
const parseCache = createLRU(50);
|
|
8
|
+
/**
|
|
9
|
+
* Parse JSON safely, returning `null` on failure.
|
|
10
|
+
* Results are LRU-memoized (50 entries max, keys ≤ 8KB).
|
|
11
|
+
*/
|
|
12
|
+
export function safeParseJSON(json) {
|
|
13
|
+
if (!json)
|
|
14
|
+
return null;
|
|
15
|
+
// Skip cache for large strings
|
|
16
|
+
if (json.length > 8192) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(json);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const cached = parseCache.peek(json);
|
|
25
|
+
if (cached) {
|
|
26
|
+
return cached.ok ? cached.value : null;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const value = JSON.parse(json);
|
|
30
|
+
parseCache.set(json, { ok: true, value });
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
parseCache.set(json, { ok: false });
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ─── JSONL ──────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Parse a JSONL string into an array of typed values.
|
|
41
|
+
* Skips blank lines and silently drops malformed lines.
|
|
42
|
+
*/
|
|
43
|
+
export function parseJSONL(data) {
|
|
44
|
+
if (!data)
|
|
45
|
+
return [];
|
|
46
|
+
const results = [];
|
|
47
|
+
const lines = data.split("\n");
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
const trimmed = line.trim();
|
|
50
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
51
|
+
continue;
|
|
52
|
+
try {
|
|
53
|
+
results.push(JSON.parse(trimmed));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Skip malformed lines
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a memoized factory function that constructs the value on first call.
|
|
3
|
+
* Used to defer Zod schema construction from module init time to first access.
|
|
4
|
+
*/
|
|
5
|
+
export declare function lazySchema<T>(factory: () => T): () => T;
|
|
6
|
+
//# sourceMappingURL=lazy-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-schema.d.ts","sourceRoot":"","sources":["../src/lazy-schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAGvD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a memoized factory function that constructs the value on first call.
|
|
3
|
+
* Used to defer Zod schema construction from module init time to first access.
|
|
4
|
+
*/
|
|
5
|
+
export function lazySchema(factory) {
|
|
6
|
+
let cached;
|
|
7
|
+
return () => (cached ??= factory());
|
|
8
|
+
}
|
package/dist/memo.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoization toolkit — zero external dependencies.
|
|
3
|
+
*
|
|
4
|
+
* Three strategies:
|
|
5
|
+
* 1. memoizeWithTTL — synchronous TTL cache with stale-while-revalidate
|
|
6
|
+
* 2. memoizeAsync — async TTL cache with in-flight deduplication
|
|
7
|
+
* 3. createLRU — bounded LRU cache backed by Map insertion order
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Creates a synchronous TTL-cached wrapper around `fn`.
|
|
11
|
+
*
|
|
12
|
+
* - First call computes and caches the value.
|
|
13
|
+
* - Subsequent calls within `ttlMs` return the cached value.
|
|
14
|
+
* - After TTL expires the stale value is returned immediately while a
|
|
15
|
+
* background refresh is scheduled via `queueMicrotask`.
|
|
16
|
+
* - `clear()` invalidates the cache and increments a generation counter so
|
|
17
|
+
* any in-flight refresh from a previous generation is silently discarded.
|
|
18
|
+
*/
|
|
19
|
+
export declare function memoizeWithTTL<T>(fn: () => T, ttlMs: number): {
|
|
20
|
+
get(): T;
|
|
21
|
+
clear(): void;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Creates an async TTL-cached wrapper around `fn`.
|
|
25
|
+
*
|
|
26
|
+
* - Concurrent callers with the same cache key share a single in-flight
|
|
27
|
+
* Promise (deduplication).
|
|
28
|
+
* - On error the cache entry **and** in-flight entry are removed so the next
|
|
29
|
+
* call retries (self-correcting).
|
|
30
|
+
* - `keyFn` defaults to `JSON.stringify(args)`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function memoizeAsync<Args extends unknown[], T>(fn: (...args: Args) => Promise<T>, options: {
|
|
33
|
+
ttlMs: number;
|
|
34
|
+
keyFn?: (...args: Args) => string;
|
|
35
|
+
/** When set, uses a bounded LRU cache instead of an unbounded Map. */
|
|
36
|
+
maxEntries?: number;
|
|
37
|
+
}): (...args: Args) => Promise<T>;
|
|
38
|
+
/** Bounded least-recently-used cache. */
|
|
39
|
+
export interface LRUCache<K, V> {
|
|
40
|
+
/** Get value and promote key to most-recently-used. */
|
|
41
|
+
get(key: K): V | undefined;
|
|
42
|
+
/** Insert or update a key-value pair. Evicts LRU entry on overflow. */
|
|
43
|
+
set(key: K, value: V): void;
|
|
44
|
+
/** Get value without promoting the key. */
|
|
45
|
+
peek(key: K): V | undefined;
|
|
46
|
+
/** Check existence without promoting. */
|
|
47
|
+
has(key: K): boolean;
|
|
48
|
+
/** Delete a single entry. Returns `true` if the key existed. */
|
|
49
|
+
delete(key: K): boolean;
|
|
50
|
+
/** Remove all entries. */
|
|
51
|
+
clear(): void;
|
|
52
|
+
/** Current number of entries. */
|
|
53
|
+
readonly size: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Creates a bounded LRU cache.
|
|
57
|
+
*
|
|
58
|
+
* Uses a `Map` whose insertion order is preserved by the JS spec.
|
|
59
|
+
* On access (`get` / `set`) the entry is deleted and re-inserted to promote
|
|
60
|
+
* it to the most-recently-used position. When capacity is exceeded the first
|
|
61
|
+
* key (least recently used) is evicted.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createLRU<K, V>(capacity: number): LRUCache<K, V>;
|
|
64
|
+
//# sourceMappingURL=memo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memo.d.ts","sourceRoot":"","sources":["../src/memo.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,EAAE,EAAE,MAAM,CAAC,EACX,KAAK,EAAE,MAAM,GACZ;IAAE,GAAG,IAAI,CAAC,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAkD7B;AAWD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,CAAC,EACpD,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EACjC,OAAO,EAAE;IACP,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IAClC,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CA0C/B;AAMD,yCAAyC;AACzC,MAAM,WAAW,QAAQ,CAAC,CAAC,EAAE,CAAC;IAC5B,uDAAuD;IACvD,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC3B,uEAAuE;IACvE,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAC5B,2CAA2C;IAC3C,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC5B,yCAAyC;IACzC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACrB,gEAAgE;IAChE,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACxB,0BAA0B;IAC1B,KAAK,IAAI,IAAI,CAAC;IACd,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAmDhE"}
|
package/dist/memo.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoization toolkit — zero external dependencies.
|
|
3
|
+
*
|
|
4
|
+
* Three strategies:
|
|
5
|
+
* 1. memoizeWithTTL — synchronous TTL cache with stale-while-revalidate
|
|
6
|
+
* 2. memoizeAsync — async TTL cache with in-flight deduplication
|
|
7
|
+
* 3. createLRU — bounded LRU cache backed by Map insertion order
|
|
8
|
+
*/
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// 1. Synchronous TTL with stale-while-revalidate
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Creates a synchronous TTL-cached wrapper around `fn`.
|
|
14
|
+
*
|
|
15
|
+
* - First call computes and caches the value.
|
|
16
|
+
* - Subsequent calls within `ttlMs` return the cached value.
|
|
17
|
+
* - After TTL expires the stale value is returned immediately while a
|
|
18
|
+
* background refresh is scheduled via `queueMicrotask`.
|
|
19
|
+
* - `clear()` invalidates the cache and increments a generation counter so
|
|
20
|
+
* any in-flight refresh from a previous generation is silently discarded.
|
|
21
|
+
*/
|
|
22
|
+
export function memoizeWithTTL(fn, ttlMs) {
|
|
23
|
+
let cached;
|
|
24
|
+
let cachedAt = 0;
|
|
25
|
+
let hasValue = false;
|
|
26
|
+
let generation = 0;
|
|
27
|
+
let refreshScheduled = false;
|
|
28
|
+
function refresh(gen) {
|
|
29
|
+
refreshScheduled = true;
|
|
30
|
+
queueMicrotask(() => {
|
|
31
|
+
if (gen !== generation) {
|
|
32
|
+
refreshScheduled = false;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
cached = fn();
|
|
37
|
+
cachedAt = Date.now();
|
|
38
|
+
hasValue = true;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
refreshScheduled = false;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
get() {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
if (!hasValue) {
|
|
49
|
+
cached = fn();
|
|
50
|
+
cachedAt = now;
|
|
51
|
+
hasValue = true;
|
|
52
|
+
return cached;
|
|
53
|
+
}
|
|
54
|
+
if (now - cachedAt > ttlMs && !refreshScheduled) {
|
|
55
|
+
refresh(generation);
|
|
56
|
+
}
|
|
57
|
+
return cached;
|
|
58
|
+
},
|
|
59
|
+
clear() {
|
|
60
|
+
generation++;
|
|
61
|
+
cached = undefined;
|
|
62
|
+
cachedAt = 0;
|
|
63
|
+
hasValue = false;
|
|
64
|
+
refreshScheduled = false;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates an async TTL-cached wrapper around `fn`.
|
|
70
|
+
*
|
|
71
|
+
* - Concurrent callers with the same cache key share a single in-flight
|
|
72
|
+
* Promise (deduplication).
|
|
73
|
+
* - On error the cache entry **and** in-flight entry are removed so the next
|
|
74
|
+
* call retries (self-correcting).
|
|
75
|
+
* - `keyFn` defaults to `JSON.stringify(args)`.
|
|
76
|
+
*/
|
|
77
|
+
export function memoizeAsync(fn, options) {
|
|
78
|
+
const { ttlMs, keyFn = (...args) => JSON.stringify(args) } = options;
|
|
79
|
+
const cache = options.maxEntries
|
|
80
|
+
? createLRU(options.maxEntries)
|
|
81
|
+
: new Map();
|
|
82
|
+
const inFlight = new Map();
|
|
83
|
+
return (...args) => {
|
|
84
|
+
const key = keyFn(...args);
|
|
85
|
+
// Return cached if fresh
|
|
86
|
+
const entry = cache.get(key);
|
|
87
|
+
if (entry && Date.now() < entry.expiresAt) {
|
|
88
|
+
return Promise.resolve(entry.value);
|
|
89
|
+
}
|
|
90
|
+
// Deduplicate concurrent calls
|
|
91
|
+
const existing = inFlight.get(key);
|
|
92
|
+
if (existing) {
|
|
93
|
+
return existing;
|
|
94
|
+
}
|
|
95
|
+
const promise = fn(...args)
|
|
96
|
+
.then((value) => {
|
|
97
|
+
cache.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
98
|
+
inFlight.delete(key);
|
|
99
|
+
return value;
|
|
100
|
+
})
|
|
101
|
+
.catch((err) => {
|
|
102
|
+
cache.delete(key);
|
|
103
|
+
inFlight.delete(key);
|
|
104
|
+
throw err;
|
|
105
|
+
});
|
|
106
|
+
inFlight.set(key, promise);
|
|
107
|
+
return promise;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Creates a bounded LRU cache.
|
|
112
|
+
*
|
|
113
|
+
* Uses a `Map` whose insertion order is preserved by the JS spec.
|
|
114
|
+
* On access (`get` / `set`) the entry is deleted and re-inserted to promote
|
|
115
|
+
* it to the most-recently-used position. When capacity is exceeded the first
|
|
116
|
+
* key (least recently used) is evicted.
|
|
117
|
+
*/
|
|
118
|
+
export function createLRU(capacity) {
|
|
119
|
+
if (capacity < 1) {
|
|
120
|
+
throw new RangeError("LRU capacity must be >= 1");
|
|
121
|
+
}
|
|
122
|
+
const map = new Map();
|
|
123
|
+
return {
|
|
124
|
+
get(key) {
|
|
125
|
+
const value = map.get(key);
|
|
126
|
+
if (value === undefined && !map.has(key)) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
// Promote: delete + re-insert
|
|
130
|
+
map.delete(key);
|
|
131
|
+
map.set(key, value);
|
|
132
|
+
return value;
|
|
133
|
+
},
|
|
134
|
+
set(key, value) {
|
|
135
|
+
// If key exists, delete first so re-insert promotes it
|
|
136
|
+
if (map.has(key)) {
|
|
137
|
+
map.delete(key);
|
|
138
|
+
}
|
|
139
|
+
else if (map.size >= capacity) {
|
|
140
|
+
// Evict LRU (first key)
|
|
141
|
+
const first = map.keys().next().value;
|
|
142
|
+
map.delete(first);
|
|
143
|
+
}
|
|
144
|
+
map.set(key, value);
|
|
145
|
+
},
|
|
146
|
+
peek(key) {
|
|
147
|
+
return map.get(key);
|
|
148
|
+
},
|
|
149
|
+
has(key) {
|
|
150
|
+
return map.has(key);
|
|
151
|
+
},
|
|
152
|
+
delete(key) {
|
|
153
|
+
return map.delete(key);
|
|
154
|
+
},
|
|
155
|
+
clear() {
|
|
156
|
+
map.clear();
|
|
157
|
+
},
|
|
158
|
+
get size() {
|
|
159
|
+
return map.size;
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
package/dist/pkce.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) primitives for OAuth 2.0 flows.
|
|
3
|
+
*
|
|
4
|
+
* Generates cryptographically secure code verifiers, challenges (S256),
|
|
5
|
+
* and state tokens using base64url encoding per RFC 7636.
|
|
6
|
+
*/
|
|
7
|
+
/** Generate a 32-byte random code verifier (base64url). */
|
|
8
|
+
export declare function generateCodeVerifier(): string;
|
|
9
|
+
/** Compute S256 code challenge from a verifier. */
|
|
10
|
+
export declare function generateCodeChallenge(verifier: string): string;
|
|
11
|
+
/** Generate a 32-byte random state token (base64url). */
|
|
12
|
+
export declare function generateState(): string;
|
|
13
|
+
//# sourceMappingURL=pkce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,2DAA2D;AAC3D,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,mDAAmD;AACnD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED,yDAAyD;AACzD,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
package/dist/pkce.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) primitives for OAuth 2.0 flows.
|
|
3
|
+
*
|
|
4
|
+
* Generates cryptographically secure code verifiers, challenges (S256),
|
|
5
|
+
* and state tokens using base64url encoding per RFC 7636.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
8
|
+
function base64URLEncode(buffer) {
|
|
9
|
+
return buffer
|
|
10
|
+
.toString("base64")
|
|
11
|
+
.replace(/\+/g, "-")
|
|
12
|
+
.replace(/\//g, "_")
|
|
13
|
+
.replace(/=/g, "");
|
|
14
|
+
}
|
|
15
|
+
/** Generate a 32-byte random code verifier (base64url). */
|
|
16
|
+
export function generateCodeVerifier() {
|
|
17
|
+
return base64URLEncode(randomBytes(32));
|
|
18
|
+
}
|
|
19
|
+
/** Compute S256 code challenge from a verifier. */
|
|
20
|
+
export function generateCodeChallenge(verifier) {
|
|
21
|
+
const hash = createHash("sha256");
|
|
22
|
+
hash.update(verifier);
|
|
23
|
+
return base64URLEncode(hash.digest());
|
|
24
|
+
}
|
|
25
|
+
/** Generate a 32-byte random state token (base64url). */
|
|
26
|
+
export function generateState() {
|
|
27
|
+
return base64URLEncode(randomBytes(32));
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic priority queue with filtered dequeue — depends on signal.ts.
|
|
3
|
+
*
|
|
4
|
+
* Lower priority number = higher priority (dequeued first).
|
|
5
|
+
* Subscribe/getSnapshot for external store compatibility.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by Claude Code's `src/utils/messageQueueManager.ts` (generified).
|
|
8
|
+
*/
|
|
9
|
+
export declare class PriorityQueue<T> {
|
|
10
|
+
private entries;
|
|
11
|
+
private snapshot;
|
|
12
|
+
private changed;
|
|
13
|
+
private updateSnapshot;
|
|
14
|
+
/** Enqueue an item with a given priority (lower = higher priority). */
|
|
15
|
+
enqueue(item: T, priority: number): void;
|
|
16
|
+
/**
|
|
17
|
+
* Dequeue the highest-priority item (lowest priority number).
|
|
18
|
+
* Optionally filter to only consider items matching the predicate.
|
|
19
|
+
* Returns undefined if queue is empty or no match.
|
|
20
|
+
*/
|
|
21
|
+
dequeue(filter?: (item: T) => boolean): T | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Peek at the highest-priority item without removing it.
|
|
24
|
+
* Optionally filter to only consider matching items.
|
|
25
|
+
*/
|
|
26
|
+
peek(filter?: (item: T) => boolean): T | undefined;
|
|
27
|
+
/** Remove and return all items matching the predicate. */
|
|
28
|
+
dequeueAllMatching(predicate: (item: T) => boolean): T[];
|
|
29
|
+
/** Remove all items. */
|
|
30
|
+
clear(): void;
|
|
31
|
+
/** Current queue size. */
|
|
32
|
+
get size(): number;
|
|
33
|
+
subscribe: (listener: () => void) => (() => void);
|
|
34
|
+
getSnapshot: () => readonly T[];
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=priority-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"priority-queue.d.ts","sourceRoot":"","sources":["../src/priority-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,qBAAa,aAAa,CAAC,CAAC;IAC1B,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,OAAO,CAAkB;IAEjC,OAAO,CAAC,cAAc;IAKtB,uEAAuE;IACvE,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKxC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,CAAC,GAAG,SAAS;IAsBrD;;;OAGG;IACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,CAAC,GAAG,SAAS;IAalD,0DAA0D;IAC1D,kBAAkB,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,CAAC,EAAE;IAgBxD,wBAAwB;IACxB,KAAK,IAAI,IAAI;IAMb,0BAA0B;IAC1B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAID,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAE9C;IAEF,WAAW,QAAO,SAAS,CAAC,EAAE,CAE5B;CACH"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic priority queue with filtered dequeue — depends on signal.ts.
|
|
3
|
+
*
|
|
4
|
+
* Lower priority number = higher priority (dequeued first).
|
|
5
|
+
* Subscribe/getSnapshot for external store compatibility.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by Claude Code's `src/utils/messageQueueManager.ts` (generified).
|
|
8
|
+
*/
|
|
9
|
+
import { createSignal } from "./signal.js";
|
|
10
|
+
// ─── Class ──────────────────────────────────────────────────────────
|
|
11
|
+
export class PriorityQueue {
|
|
12
|
+
entries = [];
|
|
13
|
+
snapshot = [];
|
|
14
|
+
changed = createSignal();
|
|
15
|
+
updateSnapshot() {
|
|
16
|
+
this.snapshot = Object.freeze(this.entries.map((e) => e.item));
|
|
17
|
+
this.changed.emit();
|
|
18
|
+
}
|
|
19
|
+
/** Enqueue an item with a given priority (lower = higher priority). */
|
|
20
|
+
enqueue(item, priority) {
|
|
21
|
+
this.entries.push({ item, priority });
|
|
22
|
+
this.updateSnapshot();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Dequeue the highest-priority item (lowest priority number).
|
|
26
|
+
* Optionally filter to only consider items matching the predicate.
|
|
27
|
+
* Returns undefined if queue is empty or no match.
|
|
28
|
+
*/
|
|
29
|
+
dequeue(filter) {
|
|
30
|
+
if (this.entries.length === 0)
|
|
31
|
+
return undefined;
|
|
32
|
+
let bestIdx = -1;
|
|
33
|
+
let bestPriority = Infinity;
|
|
34
|
+
for (let i = 0; i < this.entries.length; i++) {
|
|
35
|
+
const entry = this.entries[i];
|
|
36
|
+
if (filter && !filter(entry.item))
|
|
37
|
+
continue;
|
|
38
|
+
if (entry.priority < bestPriority) {
|
|
39
|
+
bestPriority = entry.priority;
|
|
40
|
+
bestIdx = i;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (bestIdx === -1)
|
|
44
|
+
return undefined;
|
|
45
|
+
const removed = this.entries.splice(bestIdx, 1)[0];
|
|
46
|
+
this.updateSnapshot();
|
|
47
|
+
return removed.item;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Peek at the highest-priority item without removing it.
|
|
51
|
+
* Optionally filter to only consider matching items.
|
|
52
|
+
*/
|
|
53
|
+
peek(filter) {
|
|
54
|
+
let best;
|
|
55
|
+
for (const entry of this.entries) {
|
|
56
|
+
if (filter && !filter(entry.item))
|
|
57
|
+
continue;
|
|
58
|
+
if (!best || entry.priority < best.priority) {
|
|
59
|
+
best = entry;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return best?.item;
|
|
63
|
+
}
|
|
64
|
+
/** Remove and return all items matching the predicate. */
|
|
65
|
+
dequeueAllMatching(predicate) {
|
|
66
|
+
const matched = [];
|
|
67
|
+
this.entries = this.entries.filter((entry) => {
|
|
68
|
+
if (predicate(entry.item)) {
|
|
69
|
+
matched.push(entry.item);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
});
|
|
74
|
+
if (matched.length > 0) {
|
|
75
|
+
this.updateSnapshot();
|
|
76
|
+
}
|
|
77
|
+
return matched;
|
|
78
|
+
}
|
|
79
|
+
/** Remove all items. */
|
|
80
|
+
clear() {
|
|
81
|
+
if (this.entries.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
this.entries = [];
|
|
84
|
+
this.updateSnapshot();
|
|
85
|
+
}
|
|
86
|
+
/** Current queue size. */
|
|
87
|
+
get size() {
|
|
88
|
+
return this.entries.length;
|
|
89
|
+
}
|
|
90
|
+
// ─── useSyncExternalStore compatibility ───────────────────────
|
|
91
|
+
subscribe = (listener) => {
|
|
92
|
+
return this.changed.subscribe(listener);
|
|
93
|
+
};
|
|
94
|
+
getSnapshot = () => {
|
|
95
|
+
return this.snapshot;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process I/O utilities — EPIPE handling, guarded writes, stdin detection.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Claude Code's `src/utils/process.ts`.
|
|
5
|
+
*/
|
|
6
|
+
/** Register EPIPE handlers on stdout/stderr to prevent memory leaks on broken pipes. */
|
|
7
|
+
export declare function registerProcessOutputErrorHandlers(): void;
|
|
8
|
+
/** Write to stdout, skipping if stream is destroyed. */
|
|
9
|
+
export declare function writeToStdout(data: string): void;
|
|
10
|
+
/** Write to stderr, skipping if stream is destroyed. */
|
|
11
|
+
export declare function writeToStderr(data: string): void;
|
|
12
|
+
/** Write error to stderr and exit with code 1. */
|
|
13
|
+
export declare function exitWithError(message: string): never;
|
|
14
|
+
/**
|
|
15
|
+
* Peek for incoming data on a stdin-like stream.
|
|
16
|
+
* Returns `true` on timeout (no data), `false` if stream ends.
|
|
17
|
+
* First data chunk cancels the timeout — after that, waits for end.
|
|
18
|
+
*/
|
|
19
|
+
export declare function peekForStdinData(stream: NodeJS.EventEmitter, ms: number): Promise<boolean>;
|
|
20
|
+
//# sourceMappingURL=process-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-utils.d.ts","sourceRoot":"","sources":["../src/process-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,wFAAwF;AACxF,wBAAgB,kCAAkC,IAAI,IAAI,CAGzD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIhD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIhD;AAED,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAGpD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,CAAC,YAAY,EAC3B,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CAclB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process I/O utilities — EPIPE handling, guarded writes, stdin detection.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Claude Code's `src/utils/process.ts`.
|
|
5
|
+
*/
|
|
6
|
+
function handleEPIPE(stream) {
|
|
7
|
+
return (err) => {
|
|
8
|
+
if (err.code === "EPIPE") {
|
|
9
|
+
stream.destroy();
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Register EPIPE handlers on stdout/stderr to prevent memory leaks on broken pipes. */
|
|
14
|
+
export function registerProcessOutputErrorHandlers() {
|
|
15
|
+
process.stdout.on("error", handleEPIPE(process.stdout));
|
|
16
|
+
process.stderr.on("error", handleEPIPE(process.stderr));
|
|
17
|
+
}
|
|
18
|
+
/** Write to stdout, skipping if stream is destroyed. */
|
|
19
|
+
export function writeToStdout(data) {
|
|
20
|
+
if (!process.stdout.destroyed) {
|
|
21
|
+
process.stdout.write(data);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Write to stderr, skipping if stream is destroyed. */
|
|
25
|
+
export function writeToStderr(data) {
|
|
26
|
+
if (!process.stderr.destroyed) {
|
|
27
|
+
process.stderr.write(data);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Write error to stderr and exit with code 1. */
|
|
31
|
+
export function exitWithError(message) {
|
|
32
|
+
console.error(message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Peek for incoming data on a stdin-like stream.
|
|
37
|
+
* Returns `true` on timeout (no data), `false` if stream ends.
|
|
38
|
+
* First data chunk cancels the timeout — after that, waits for end.
|
|
39
|
+
*/
|
|
40
|
+
export function peekForStdinData(stream, ms) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const done = (timedOut) => {
|
|
43
|
+
clearTimeout(peek);
|
|
44
|
+
stream.off("end", onEnd);
|
|
45
|
+
stream.off("data", onFirstData);
|
|
46
|
+
resolve(timedOut);
|
|
47
|
+
};
|
|
48
|
+
const onEnd = () => done(false);
|
|
49
|
+
const onFirstData = () => clearTimeout(peek);
|
|
50
|
+
const peek = setTimeout(done, ms, true);
|
|
51
|
+
stream.once("end", onEnd);
|
|
52
|
+
stream.once("data", onFirstData);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency state machine — prevents re-entrant async operations.
|
|
3
|
+
*
|
|
4
|
+
* Tracks a 3-state lifecycle: idle → dispatching → running → idle.
|
|
5
|
+
* A generation counter ensures stale finally-blocks from cancelled
|
|
6
|
+
* queries don't corrupt cleanup.
|
|
7
|
+
*
|
|
8
|
+
* Inspired by Claude Code's `src/utils/QueryGuard.ts`.
|
|
9
|
+
*/
|
|
10
|
+
export declare class QueryGuard {
|
|
11
|
+
private state;
|
|
12
|
+
private _generation;
|
|
13
|
+
private _changed;
|
|
14
|
+
/** Try to reserve the guard for a new operation. Returns false if busy. */
|
|
15
|
+
reserve(): boolean;
|
|
16
|
+
/** Cancel a reservation (go back to idle). */
|
|
17
|
+
cancelReservation(): void;
|
|
18
|
+
/** Transition from dispatching to running. Returns generation or null if not dispatching. */
|
|
19
|
+
tryStart(): number | null;
|
|
20
|
+
/**
|
|
21
|
+
* End a running operation. Only succeeds if the generation matches
|
|
22
|
+
* (prevents stale cleanups). Returns true if the guard was released.
|
|
23
|
+
*/
|
|
24
|
+
end(generation: number): boolean;
|
|
25
|
+
/** Force the guard back to idle regardless of state. */
|
|
26
|
+
forceEnd(): void;
|
|
27
|
+
/** Whether the guard is in dispatching or running state. */
|
|
28
|
+
get isActive(): boolean;
|
|
29
|
+
/** Current generation counter. */
|
|
30
|
+
get generation(): number;
|
|
31
|
+
subscribe: (listener: () => void) => (() => void);
|
|
32
|
+
getSnapshot: () => boolean;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=query-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-guard.d.ts","sourceRoot":"","sources":["../src/query-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAkB;IAElC,2EAA2E;IAC3E,OAAO,IAAI,OAAO;IAOlB,8CAA8C;IAC9C,iBAAiB,IAAI,IAAI;IAMzB,6FAA6F;IAC7F,QAAQ,IAAI,MAAM,GAAG,IAAI;IAQzB;;;OAGG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAQhC,wDAAwD;IACxD,QAAQ,IAAI,IAAI;IAMhB,4DAA4D;IAC5D,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,kCAAkC;IAClC,IAAI,UAAU,IAAI,MAAM,CAEvB;IAID,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAE9C;IAEF,WAAW,QAAO,OAAO,CAEvB;CACH"}
|