@qitqode/qitqore 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -2
  3. package/dist/device-authorization.d.ts +67 -0
  4. package/dist/device-authorization.d.ts.map +1 -0
  5. package/dist/device-authorization.js +122 -0
  6. package/dist/device-authorization.js.map +1 -0
  7. package/dist/errors.d.ts +77 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/errors.js +254 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/index.d.ts +13 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +13 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/recovery-capabilities.d.ts +26 -0
  16. package/dist/recovery-capabilities.d.ts.map +1 -0
  17. package/dist/recovery-capabilities.js +113 -0
  18. package/dist/recovery-capabilities.js.map +1 -0
  19. package/dist/recovery-directive.d.ts +20 -0
  20. package/dist/recovery-directive.d.ts.map +1 -0
  21. package/dist/recovery-directive.js +116 -0
  22. package/dist/recovery-directive.js.map +1 -0
  23. package/dist/recovery-lease.d.ts +80 -0
  24. package/dist/recovery-lease.d.ts.map +1 -0
  25. package/dist/recovery-lease.js +257 -0
  26. package/dist/recovery-lease.js.map +1 -0
  27. package/dist/recovery-status.d.ts +22 -0
  28. package/dist/recovery-status.d.ts.map +1 -0
  29. package/dist/recovery-status.js +113 -0
  30. package/dist/recovery-status.js.map +1 -0
  31. package/dist/recovery.d.ts +48 -0
  32. package/dist/recovery.d.ts.map +1 -0
  33. package/dist/recovery.js +145 -0
  34. package/dist/recovery.js.map +1 -0
  35. package/dist/redaction.d.ts +64 -0
  36. package/dist/redaction.d.ts.map +1 -0
  37. package/dist/redaction.js +154 -0
  38. package/dist/redaction.js.map +1 -0
  39. package/dist/route-contract.d.ts +36 -0
  40. package/dist/route-contract.d.ts.map +1 -0
  41. package/dist/route-contract.js +89 -0
  42. package/dist/route-contract.js.map +1 -0
  43. package/dist/sse.d.ts +22 -0
  44. package/dist/sse.d.ts.map +1 -0
  45. package/dist/sse.js +156 -0
  46. package/dist/sse.js.map +1 -0
  47. package/dist/stream-payload.d.ts +39 -0
  48. package/dist/stream-payload.d.ts.map +1 -0
  49. package/dist/stream-payload.js +86 -0
  50. package/dist/stream-payload.js.map +1 -0
  51. package/dist/transport.d.ts +44 -0
  52. package/dist/transport.d.ts.map +1 -0
  53. package/dist/transport.js +260 -0
  54. package/dist/transport.js.map +1 -0
  55. package/package.json +38 -4
@@ -0,0 +1,145 @@
1
+ function abortReason(signal) {
2
+ return signal.reason ?? Object.assign(new Error("The operation was aborted"), { name: "AbortError" });
3
+ }
4
+ export function abortableDelay(ms, signal) {
5
+ if (!Number.isFinite(ms) || ms < 0) {
6
+ return Promise.reject(new RangeError("delay must be a non-negative finite number"));
7
+ }
8
+ if (signal?.aborted)
9
+ return Promise.reject(abortReason(signal));
10
+ if (ms === 0)
11
+ return Promise.resolve();
12
+ return new Promise((resolve, reject) => {
13
+ let settled = false;
14
+ const cleanup = () => {
15
+ clearTimeout(timer);
16
+ signal?.removeEventListener("abort", onAbort);
17
+ };
18
+ const finish = (callback) => {
19
+ if (settled)
20
+ return;
21
+ settled = true;
22
+ cleanup();
23
+ callback();
24
+ };
25
+ const onAbort = () => finish(() => reject(signal ? abortReason(signal) : undefined));
26
+ const timer = setTimeout(() => finish(resolve), ms);
27
+ signal?.addEventListener("abort", onAbort, { once: true });
28
+ if (signal?.aborted)
29
+ onAbort();
30
+ });
31
+ }
32
+ function snapshot(outcome) {
33
+ return Object.freeze({ ...outcome });
34
+ }
35
+ export async function runWithRecovery(options) {
36
+ if (typeof options.operationId !== "string" ||
37
+ !options.operationId.trim() ||
38
+ options.operationId.length > 256) {
39
+ throw new TypeError("operationId must contain between 1 and 256 characters");
40
+ }
41
+ if (!Number.isInteger(options.maxAttempts) || options.maxAttempts < 1 || options.maxAttempts > 5) {
42
+ throw new RangeError("maxAttempts must be an integer between 1 and 5");
43
+ }
44
+ if (options.deadlineAt !== undefined && (!Number.isFinite(options.deadlineAt) || options.deadlineAt <= 0)) {
45
+ throw new RangeError("deadlineAt must be a positive finite epoch timestamp");
46
+ }
47
+ if (options.deadlineAt !== undefined && options.deadlineAt <= Date.now()) {
48
+ throw new Error("The recovery operation deadline expired");
49
+ }
50
+ const deadlineController = options.deadlineAt === undefined ? undefined : new AbortController();
51
+ const deadlineTimer = deadlineController
52
+ ? setTimeout(() => deadlineController.abort(new Error("The recovery operation deadline expired")), Math.max(0, options.deadlineAt - Date.now()))
53
+ : undefined;
54
+ const signal = deadlineController
55
+ ? AbortSignal.any([...(options.signal ? [options.signal] : []), deadlineController.signal])
56
+ : options.signal;
57
+ try {
58
+ for (let attempt = 1; attempt <= options.maxAttempts; attempt += 1) {
59
+ if (signal?.aborted)
60
+ throw abortReason(signal);
61
+ const outcome = {
62
+ acceptance: "unknown",
63
+ partialOutput: false,
64
+ sideEffectStarted: false,
65
+ };
66
+ const context = {
67
+ operationId: options.operationId,
68
+ attempt,
69
+ ...(signal ? { signal } : {}),
70
+ markNotAccepted() {
71
+ if (outcome.acceptance !== "accepted")
72
+ outcome.acceptance = "not-accepted";
73
+ },
74
+ markAccepted() {
75
+ outcome.acceptance = "accepted";
76
+ },
77
+ markPartialOutput() {
78
+ outcome.partialOutput = true;
79
+ },
80
+ markSideEffectStarted() {
81
+ outcome.sideEffectStarted = true;
82
+ },
83
+ };
84
+ try {
85
+ if (options.onAttemptStart)
86
+ await options.onAttemptStart(context);
87
+ const result = await options.execute(context);
88
+ if (options.onAttemptOutcome) {
89
+ await options.onAttemptOutcome(Object.freeze({
90
+ operationId: options.operationId,
91
+ attempt,
92
+ outcome: snapshot(outcome),
93
+ ...(signal ? { signal } : {}),
94
+ }));
95
+ }
96
+ return result;
97
+ }
98
+ catch (error) {
99
+ if (options.onAttemptOutcome) {
100
+ await options.onAttemptOutcome(Object.freeze({
101
+ operationId: options.operationId,
102
+ attempt,
103
+ outcome: snapshot(outcome),
104
+ ...(signal ? { signal } : {}),
105
+ error,
106
+ }));
107
+ }
108
+ if (signal?.aborted)
109
+ throw abortReason(signal);
110
+ if (attempt >= options.maxAttempts)
111
+ throw error;
112
+ const replayContext = Object.freeze({
113
+ operationId: options.operationId,
114
+ attempt,
115
+ nextAttempt: attempt + 1,
116
+ error,
117
+ outcome: snapshot(outcome),
118
+ ...(signal ? { signal } : {}),
119
+ });
120
+ if (options.shouldRetry(error, replayContext) !== true)
121
+ throw error;
122
+ if (outcome.acceptance === "accepted" ||
123
+ outcome.partialOutput ||
124
+ outcome.sideEffectStarted) {
125
+ throw error;
126
+ }
127
+ if ((await options.authorizeReplay(replayContext)) !== true)
128
+ throw error;
129
+ if (signal?.aborted)
130
+ throw abortReason(signal);
131
+ const configuredDelay = typeof options.backoffMs === "function"
132
+ ? options.backoffMs(attempt + 1, error)
133
+ : options.backoffMs ?? 0;
134
+ const delay = Math.min(30_000, Math.max(0, configuredDelay));
135
+ await abortableDelay(delay, signal);
136
+ }
137
+ }
138
+ }
139
+ finally {
140
+ if (deadlineTimer !== undefined)
141
+ clearTimeout(deadlineTimer);
142
+ }
143
+ throw new Error("Unreachable recovery state");
144
+ }
145
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Pure, opt-in secret redaction. The caller supplies every secret value and
3
+ * sensitive key name; QitQore never reads the environment, a credential store,
4
+ * or any global registry, and it keeps no state between calls except the
5
+ * configuration captured by `createSecretRedactor`.
6
+ */
7
+ export declare const DEFAULT_REDACTION_MARKER = "[redacted]";
8
+ export declare const TRUNCATION_MARKER = "[truncated]";
9
+ /**
10
+ * Replace credential shapes in free text: authorization headers, bearer or basic
11
+ * tokens, secret-looking `key=value` pairs, query-string secrets, and JWT-shaped
12
+ * values. These are heuristics, so `createSecretRedactor` can disable them for
13
+ * model output where an ordinary word such as `token: 5` must survive.
14
+ */
15
+ export declare function redactCredentialShapes(text: string): string;
16
+ /**
17
+ * Bounded, log-safe text: credential shapes are redacted, control characters
18
+ * are replaced, surrounding whitespace is trimmed, and the result is capped.
19
+ */
20
+ export declare function redactSensitiveText(text: string, maxLength: number): string;
21
+ /**
22
+ * Encodings in which a secret can legitimately appear in text: the raw value,
23
+ * URL-encoded, JSON-escaped, and base64. Hosts use this to count registrations
24
+ * against their own limits, so the encoding rule has one implementation.
25
+ */
26
+ export declare function secretEncodings(value: string): string[];
27
+ export interface SecretRedactorOptions {
28
+ /** Exact secret values supplied by the host. Shorter values are ignored. */
29
+ secrets?: readonly string[];
30
+ /** Shortest configured value that is redacted. Defaults to 8 characters. */
31
+ minSecretLength?: number;
32
+ /** Key names whose values are replaced wholesale. Compared after normalization. */
33
+ sensitiveKeys?: readonly string[];
34
+ /** Additional key test over the normalized key, for host-specific naming rules. */
35
+ isSensitiveKey?: (normalized: string) => boolean;
36
+ /** Replacement text for redacted values. Must be deterministic for the caller. */
37
+ marker?: string;
38
+ /**
39
+ * Apply credential-shape heuristics in addition to exact values. Defaults to
40
+ * true. Disable it for model output, where a shape match can alter ordinary prose.
41
+ */
42
+ patterns?: boolean;
43
+ }
44
+ export interface SecretRedactionStream {
45
+ /** Redact a chunk. Text that could still be the start of a secret is held back. */
46
+ push(chunk: string): string;
47
+ /** Release everything still held back. Call once the stream has ended. */
48
+ finish(): string;
49
+ }
50
+ export interface SecretRedactor {
51
+ /** Replace configured values and their encodings, then common credential shapes. */
52
+ redact(text: string): string;
53
+ /** Create a stateful, chunk-safe redactor for one streamed output. */
54
+ stream(): SecretRedactionStream;
55
+ /** Recursively redact a structured value with bounded depth, entries, and string size. */
56
+ redactStructure(value: unknown): unknown;
57
+ }
58
+ /**
59
+ * Build a redactor from explicit configuration. Each call returns an independent
60
+ * object, so hosts that rotate secrets create a new redactor rather than mutating
61
+ * a shared one.
62
+ */
63
+ export declare function createSecretRedactor(options?: SecretRedactorOptions): SecretRedactor;
64
+ //# sourceMappingURL=redaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,eAAO,MAAM,wBAAwB,eAAe,CAAC;AACrD,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAS/C;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO3D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3E;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvD;AAUD,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,mFAAmF;IACnF,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,sEAAsE;IACtE,MAAM,IAAI,qBAAqB,CAAC;IAChC,0FAA0F;IAC1F,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CA0FxF"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Pure, opt-in secret redaction. The caller supplies every secret value and
3
+ * sensitive key name; QitQore never reads the environment, a credential store,
4
+ * or any global registry, and it keeps no state between calls except the
5
+ * configuration captured by `createSecretRedactor`.
6
+ */
7
+ const AUTH_HEADER = /\b(authorization)\s*[:=]\s*(?:bearer|basic)\s+\S+/gi;
8
+ const SECRET_VALUE = /\b(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|token|password|secret)\s*[:=]\s*["']?[^\s,;"'&]+/gi;
9
+ const BEARER_TOKEN = /\b(bearer|basic)\s+[A-Za-z0-9._~+/-]+=*/gi;
10
+ const URL_SECRET = /([?&](?:api[-_]?key|access[-_]?token|refresh[-_]?token|token|secret)=)[^&\s]+/gi;
11
+ const JWT = /\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g;
12
+ const CONTROL_CHARACTERS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
13
+ export const DEFAULT_REDACTION_MARKER = "[redacted]";
14
+ export const TRUNCATION_MARKER = "[truncated]";
15
+ const DEFAULT_MIN_SECRET_LENGTH = 8;
16
+ const MAX_STRUCTURE_DEPTH = 8;
17
+ const MAX_STRUCTURE_ENTRIES = 256;
18
+ const MAX_STRING_LENGTH = 16_384;
19
+ /** Longest non-whitespace run a stream holds back, so a split token is never emitted early. */
20
+ const MAX_STREAM_TOKEN_HOLD = 4_096;
21
+ /**
22
+ * Replace credential shapes in free text: authorization headers, bearer or basic
23
+ * tokens, secret-looking `key=value` pairs, query-string secrets, and JWT-shaped
24
+ * values. These are heuristics, so `createSecretRedactor` can disable them for
25
+ * model output where an ordinary word such as `token: 5` must survive.
26
+ */
27
+ export function redactCredentialShapes(text) {
28
+ return text
29
+ .replace(AUTH_HEADER, "$1: [redacted]")
30
+ .replace(SECRET_VALUE, "$1=[redacted]")
31
+ .replace(BEARER_TOKEN, "$1 [redacted]")
32
+ .replace(URL_SECRET, "$1[redacted]")
33
+ .replace(JWT, "[redacted-token]");
34
+ }
35
+ /**
36
+ * Bounded, log-safe text: credential shapes are redacted, control characters
37
+ * are replaced, surrounding whitespace is trimmed, and the result is capped.
38
+ */
39
+ export function redactSensitiveText(text, maxLength) {
40
+ return redactCredentialShapes(text).replace(CONTROL_CHARACTERS, " ").trim().slice(0, maxLength);
41
+ }
42
+ function utf8Base64(value) {
43
+ let binary = "";
44
+ for (const byte of new TextEncoder().encode(value))
45
+ binary += String.fromCharCode(byte);
46
+ return btoa(binary);
47
+ }
48
+ /**
49
+ * Encodings in which a secret can legitimately appear in text: the raw value,
50
+ * URL-encoded, JSON-escaped, and base64. Hosts use this to count registrations
51
+ * against their own limits, so the encoding rule has one implementation.
52
+ */
53
+ export function secretEncodings(value) {
54
+ const variants = [
55
+ value,
56
+ encodeURIComponent(value),
57
+ JSON.stringify(value).slice(1, -1),
58
+ utf8Base64(value),
59
+ ];
60
+ return [...new Set(variants)].filter((item) => item.length > 0);
61
+ }
62
+ /** Normalize a key so `accessToken`, `access-token`, and `access_token` compare equal. */
63
+ function normalizedKey(key) {
64
+ return key
65
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
66
+ .replace(/-/g, "_")
67
+ .toLowerCase();
68
+ }
69
+ /**
70
+ * Build a redactor from explicit configuration. Each call returns an independent
71
+ * object, so hosts that rotate secrets create a new redactor rather than mutating
72
+ * a shared one.
73
+ */
74
+ export function createSecretRedactor(options = {}) {
75
+ const minSecretLength = options.minSecretLength ?? DEFAULT_MIN_SECRET_LENGTH;
76
+ const marker = options.marker ?? DEFAULT_REDACTION_MARKER;
77
+ const variants = [
78
+ ...new Set((options.secrets ?? [])
79
+ .filter((value) => typeof value === "string" && value.length >= minSecretLength)
80
+ .flatMap(secretEncodings)),
81
+ ].sort((a, b) => b.length - a.length);
82
+ const longest = variants.length === 0 ? 0 : variants[0].length;
83
+ const sensitiveKeys = new Set((options.sensitiveKeys ?? []).map(normalizedKey));
84
+ const isSensitiveKey = (key) => sensitiveKeys.has(normalizedKey(key)) || (options.isSensitiveKey?.(normalizedKey(key)) ?? false);
85
+ const patterns = options.patterns ?? true;
86
+ // Exact values come first so a secret is never partially rewritten by a shape rule.
87
+ // Whitespace and control characters are preserved: streamed text must reassemble unchanged.
88
+ const redact = (text) => {
89
+ const exact = variants.length === 0 || text.length === 0
90
+ ? text
91
+ : variants.reduce((current, item) => current.split(item).join(marker), text);
92
+ return patterns ? redactCredentialShapes(exact) : exact;
93
+ };
94
+ /**
95
+ * Earliest index where a still-incomplete secret could begin. Everything from
96
+ * that index on is held, so a value split across chunks is never emitted in part.
97
+ */
98
+ const partialSecretStart = (text) => {
99
+ for (let start = Math.max(0, text.length - longest + 1); start < text.length; start += 1) {
100
+ const suffix = text.slice(start);
101
+ if (variants.some((variant) => variant.length > suffix.length && variant.startsWith(suffix)))
102
+ return start;
103
+ }
104
+ return text.length;
105
+ };
106
+ /** Credential shapes span non-whitespace runs, so the trailing run is held as well. */
107
+ const credentialTailStart = (text) => {
108
+ const tail = /\S*$/.exec(text)?.[0].length ?? 0;
109
+ return text.length - Math.min(tail, MAX_STREAM_TOKEN_HOLD);
110
+ };
111
+ const stream = () => {
112
+ let pending = "";
113
+ return {
114
+ push(chunk) {
115
+ pending += chunk;
116
+ let boundary = Math.min(partialSecretStart(pending), patterns ? credentialTailStart(pending) : pending.length);
117
+ if (boundary > 0 && boundary < pending.length) {
118
+ const before = pending.charCodeAt(boundary - 1);
119
+ const after = pending.charCodeAt(boundary);
120
+ if (before >= 0xd800 && before <= 0xdbff && after >= 0xdc00 && after <= 0xdfff)
121
+ boundary -= 1;
122
+ }
123
+ const safe = pending.slice(0, boundary);
124
+ pending = pending.slice(boundary);
125
+ return redact(safe);
126
+ },
127
+ finish() {
128
+ const final = redact(pending);
129
+ pending = "";
130
+ return final;
131
+ },
132
+ };
133
+ };
134
+ const redactStructure = (value, depth = 0) => {
135
+ if (typeof value === "string") {
136
+ const scrubbed = redact(value);
137
+ return scrubbed.length > MAX_STRING_LENGTH
138
+ ? `${scrubbed.slice(0, MAX_STRING_LENGTH)}${TRUNCATION_MARKER}`
139
+ : scrubbed;
140
+ }
141
+ if (value === null || typeof value !== "object")
142
+ return value;
143
+ if (depth >= MAX_STRUCTURE_DEPTH)
144
+ return TRUNCATION_MARKER;
145
+ if (Array.isArray(value)) {
146
+ return value.slice(0, MAX_STRUCTURE_ENTRIES).map((item) => redactStructure(item, depth + 1));
147
+ }
148
+ return Object.fromEntries(Object.entries(value)
149
+ .slice(0, MAX_STRUCTURE_ENTRIES)
150
+ .map(([key, item]) => [key, isSensitiveKey(key) ? marker : redactStructure(item, depth + 1)]));
151
+ };
152
+ return { redact, stream, redactStructure };
153
+ }
154
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure helpers for Qortex route identity, module endpoints, and request headers.
3
+ * Every module name, route, and identifier comes from the caller. QitQore does
4
+ * not choose between the canonical `qortex` and historical `@bijikyu/omniquery`
5
+ * module paths, and it never creates request or idempotency identities.
6
+ */
7
+ /**
8
+ * Build the route identity Qortex reports for a recovery status. The module is
9
+ * the public path segment as the caller addresses it, and the path is normalized
10
+ * to begin with a single slash.
11
+ */
12
+ export declare function recoveryRouteID(input: {
13
+ module: string;
14
+ method: string;
15
+ path: string;
16
+ }): string;
17
+ /**
18
+ * Build a module function URL, `<base>/api/v1/<module>/<function>`. The module
19
+ * path is kept exactly as supplied, including scoped names such as
20
+ * `@bijikyu/omniquery`, because percent-encoding it would not match the route.
21
+ */
22
+ export declare function qortexModuleEndpoint(input: {
23
+ baseUrl: string | URL;
24
+ module: string;
25
+ functionName: string;
26
+ }): string;
27
+ /**
28
+ * Bounded request-identity headers. A host supplies both identities; QitQore
29
+ * validates them and never invents one. The idempotency key is sent with the
30
+ * request ID only when the host has an approved stable key.
31
+ */
32
+ export declare function qortexRequestHeaders(input: {
33
+ requestId?: string;
34
+ idempotencyKey?: string;
35
+ }): Record<string, string>;
36
+ //# sourceMappingURL=route-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-contract.d.ts","sourceRoot":"","sources":["../src/route-contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAgB/F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAgBT;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAezB"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Pure helpers for Qortex route identity, module endpoints, and request headers.
3
+ * Every module name, route, and identifier comes from the caller. QitQore does
4
+ * not choose between the canonical `qortex` and historical `@bijikyu/omniquery`
5
+ * module paths, and it never creates request or idempotency identities.
6
+ */
7
+ const MODULE_PATH = /^[A-Za-z0-9._~@-]{1,128}(?:\/[A-Za-z0-9._~@-]{1,128})*$/;
8
+ const FUNCTION_NAME = /^[A-Za-z][A-Za-z0-9_]{0,127}$/;
9
+ const METHOD = /^[A-Za-z]{1,16}$/;
10
+ const ROUTE_PATH = /^\/[A-Za-z0-9_./~-]{0,255}$/;
11
+ const REQUEST_ID = /^[A-Za-z0-9_.:-]{1,160}$/;
12
+ const IDEMPOTENCY_KEY = /^[A-Za-z0-9_.:-]{1,256}$/;
13
+ const MAX_ROUTE_ID_LENGTH = 256;
14
+ /** Dot-only segments would let a module or path climb out of its route prefix. */
15
+ function hasDotSegment(value) {
16
+ return value.split("/").some((segment) => segment === "." || segment === "..");
17
+ }
18
+ function isModulePath(value) {
19
+ return typeof value === "string" && MODULE_PATH.test(value) && !hasDotSegment(value);
20
+ }
21
+ function isRoutePath(value) {
22
+ return ROUTE_PATH.test(value) && !hasDotSegment(value);
23
+ }
24
+ /**
25
+ * Build the route identity Qortex reports for a recovery status. The module is
26
+ * the public path segment as the caller addresses it, and the path is normalized
27
+ * to begin with a single slash.
28
+ */
29
+ export function recoveryRouteID(input) {
30
+ if (!isModulePath(input.module)) {
31
+ throw new TypeError("Qortex route module is invalid");
32
+ }
33
+ if (typeof input.method !== "string" || !METHOD.test(input.method)) {
34
+ throw new TypeError("Qortex route method is invalid");
35
+ }
36
+ const path = `/${input.path.replace(/^\/+/, "")}`;
37
+ if (!isRoutePath(path)) {
38
+ throw new TypeError("Qortex route path is invalid");
39
+ }
40
+ const routeID = `${input.module}:${input.method.toUpperCase()}:${path}`;
41
+ if (routeID.length > MAX_ROUTE_ID_LENGTH) {
42
+ throw new TypeError("Qortex route identity is too long");
43
+ }
44
+ return routeID;
45
+ }
46
+ /**
47
+ * Build a module function URL, `<base>/api/v1/<module>/<function>`. The module
48
+ * path is kept exactly as supplied, including scoped names such as
49
+ * `@bijikyu/omniquery`, because percent-encoding it would not match the route.
50
+ */
51
+ export function qortexModuleEndpoint(input) {
52
+ if (!isModulePath(input.module)) {
53
+ throw new TypeError("Qortex module name is invalid");
54
+ }
55
+ if (typeof input.functionName !== "string" || !FUNCTION_NAME.test(input.functionName)) {
56
+ throw new TypeError("Qortex function name is invalid");
57
+ }
58
+ const base = new URL(input.baseUrl);
59
+ if (base.protocol !== "https:" && base.protocol !== "http:") {
60
+ throw new TypeError("Qortex base URL must use HTTP or HTTPS");
61
+ }
62
+ if (base.search !== "" || base.hash !== "") {
63
+ throw new TypeError("Qortex base URL must not include a query or fragment");
64
+ }
65
+ const basePath = base.pathname.replace(/\/+$/, "");
66
+ return `${base.origin}${basePath}/api/v1/${input.module}/${input.functionName}`;
67
+ }
68
+ /**
69
+ * Bounded request-identity headers. A host supplies both identities; QitQore
70
+ * validates them and never invents one. The idempotency key is sent with the
71
+ * request ID only when the host has an approved stable key.
72
+ */
73
+ export function qortexRequestHeaders(input) {
74
+ const headers = {};
75
+ if (input.requestId !== undefined) {
76
+ if (typeof input.requestId !== "string" || !REQUEST_ID.test(input.requestId)) {
77
+ throw new TypeError("Qortex request ID is invalid");
78
+ }
79
+ headers["x-request-id"] = input.requestId;
80
+ }
81
+ if (input.idempotencyKey !== undefined) {
82
+ if (typeof input.idempotencyKey !== "string" || !IDEMPOTENCY_KEY.test(input.idempotencyKey)) {
83
+ throw new TypeError("Qortex idempotency key is invalid");
84
+ }
85
+ headers["x-idempotency-key"] = input.idempotencyKey;
86
+ }
87
+ return headers;
88
+ }
89
+ //# sourceMappingURL=route-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-contract.js","sourceRoot":"","sources":["../src/route-contract.ts"],"names":[],"mappings":"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"}
package/dist/sse.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ export interface QortexSseEvent {
2
+ data: string;
3
+ event?: string;
4
+ id?: string;
5
+ retry?: number;
6
+ }
7
+ export declare class SseDecoder {
8
+ private readonly decoder;
9
+ private buffer;
10
+ private data;
11
+ private eventName;
12
+ private eventId;
13
+ private lastEventId;
14
+ private retry;
15
+ push(chunk: Uint8Array | string): QortexSseEvent[];
16
+ finish(): QortexSseEvent[];
17
+ private consume;
18
+ private processLine;
19
+ private resetFrame;
20
+ }
21
+ export declare function iterateSse(stream: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<QortexSseEvent>;
22
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,KAAK,CAAqB;IAElC,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,cAAc,EAAE;IAQlD,MAAM,IAAI,cAAc,EAAE;IAS1B,OAAO,CAAC,OAAO;IAgCf,OAAO,CAAC,WAAW;IA4CnB,OAAO,CAAC,UAAU;CAMnB;AAED,wBAAuB,UAAU,CAC/B,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,cAAc,CAAC,CA0ChC"}