@tracon/client 1.0.0-preview.1

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/schema.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * This file was auto-generated by openapi-typescript.
3
+ * Do not make direct changes to the file.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/dist/sse.d.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Server-Sent Events framing for Tracon's streaming endpoints.
3
+ *
4
+ * `openapi-fetch` can already hand back the raw stream (`parseAs: 'stream'`,
5
+ * or the `response` every call returns), so streaming needs no special client
6
+ * method — but every consumer would then have to write this decoder again.
7
+ * It ships here instead: the Tracon UI is one consumer of it, not its
8
+ * owner (Phase 159).
9
+ */
10
+ /** Options for {@link readSse}. */
11
+ export interface ReadSseOptions {
12
+ /**
13
+ * Ends the stream with an {@link SseIdleTimeoutError} when no bytes at all
14
+ * arrive for this long. Omitted means wait forever.
15
+ *
16
+ * 🚨 Measure BYTES, not frames. Tracon sends `: waiting` comments while a run
17
+ * is still going and {@link SseDecoder} drops comments, so a healthy but
18
+ * quiet run yields no frames for as long as the run is quiet — a
19
+ * frame-based timeout would cut it off. Bytes tell the two apart: a run that
20
+ * is still going keeps sending them, a finished one closes the stream, and a
21
+ * connection that died without either sends nothing.
22
+ *
23
+ * Set it above the server's `RunEventPollInterval` (250 ms by default), with
24
+ * room for a slow link: that interval is how often the keep-alive is sent,
25
+ * and a threshold under it would fire between two healthy keep-alives.
26
+ */
27
+ idleTimeoutMs?: number;
28
+ }
29
+ /**
30
+ * Thrown by {@link readSse} when a stream goes completely quiet for longer
31
+ * than {@link ReadSseOptions.idleTimeoutMs}.
32
+ *
33
+ * A connection that drops without a close frame — a laptop going to sleep, a
34
+ * proxy timing out, a network that goes away mid-body — leaves the reader
35
+ * waiting on a chunk that will never come and throws nothing at all. Without
36
+ * this, a consumer cannot tell that from a run that is simply still working:
37
+ * both look like silence forever.
38
+ */
39
+ export declare class SseIdleTimeoutError extends Error {
40
+ constructor(idleTimeoutMs: number);
41
+ /** The threshold that was exceeded, in milliseconds. */
42
+ readonly idleTimeoutMs: number;
43
+ }
44
+ /** One Server-Sent Events frame. */
45
+ export interface SseFrame {
46
+ /** Value of the `id:` field, when present. */
47
+ id: string | null;
48
+ /** Value of the `event:` field. Defaults to `message`, as the spec requires. */
49
+ event: string;
50
+ /** Joined `data:` lines. */
51
+ data: string;
52
+ }
53
+ /**
54
+ * Incremental SSE decoder.
55
+ *
56
+ * Kept as a pure class with no I/O so the framing rules can be unit tested:
57
+ * chunk boundaries fall in arbitrary places and a parser that assumes whole
58
+ * frames per chunk works right up until a slow network proves it wrong.
59
+ *
60
+ * Comment lines (`: waiting`) are skipped. Tracon sends them as keep-alives
61
+ * while a run is still in progress.
62
+ */
63
+ export declare class SseDecoder {
64
+ #private;
65
+ /** Feeds a chunk of text and returns every frame it completed. */
66
+ push(chunk: string): SseFrame[];
67
+ }
68
+ /**
69
+ * Reads a `text/event-stream` response body as frames.
70
+ *
71
+ * `EventSource` is not used: it cannot send an `Authorization` header and it
72
+ * cannot issue a POST, and Tracon needs both.
73
+ */
74
+ export declare function readSse(response: Response, options?: ReadSseOptions): AsyncGenerator<SseFrame>;
75
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,aAAa,EAAE,MAAM;IAOjC,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,oCAAoC;AACpC,MAAM,WAAW,QAAQ;IACvB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,qBAAa,UAAU;;IAMrB,kEAAkE;IAClE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE;CAiGhC;AAED;;;;;GAKG;AACH,wBAAuB,OAAO,CAC5B,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,cAAc,CAAC,QAAQ,CAAC,CA0B1B"}
package/dist/sse.js ADDED
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Thrown by {@link readSse} when a stream goes completely quiet for longer
3
+ * than {@link ReadSseOptions.idleTimeoutMs}.
4
+ *
5
+ * A connection that drops without a close frame — a laptop going to sleep, a
6
+ * proxy timing out, a network that goes away mid-body — leaves the reader
7
+ * waiting on a chunk that will never come and throws nothing at all. Without
8
+ * this, a consumer cannot tell that from a run that is simply still working:
9
+ * both look like silence forever.
10
+ */
11
+ export class SseIdleTimeoutError extends Error {
12
+ constructor(idleTimeoutMs) {
13
+ super(`The event stream sent nothing for ${idleTimeoutMs}ms and was closed.`);
14
+ this.name = 'SseIdleTimeoutError';
15
+ this.idleTimeoutMs = idleTimeoutMs;
16
+ }
17
+ /** The threshold that was exceeded, in milliseconds. */
18
+ idleTimeoutMs;
19
+ }
20
+ /**
21
+ * Incremental SSE decoder.
22
+ *
23
+ * Kept as a pure class with no I/O so the framing rules can be unit tested:
24
+ * chunk boundaries fall in arbitrary places and a parser that assumes whole
25
+ * frames per chunk works right up until a slow network proves it wrong.
26
+ *
27
+ * Comment lines (`: waiting`) are skipped. Tracon sends them as keep-alives
28
+ * while a run is still in progress.
29
+ */
30
+ export class SseDecoder {
31
+ #buffer = '';
32
+ #data = [];
33
+ #event = null;
34
+ #id = null;
35
+ /** Feeds a chunk of text and returns every frame it completed. */
36
+ push(chunk) {
37
+ this.#buffer += chunk;
38
+ const frames = [];
39
+ let newline = this.#nextLineBreak();
40
+ while (newline !== null) {
41
+ const line = this.#buffer.slice(0, newline.index);
42
+ this.#buffer = this.#buffer.slice(newline.index + newline.length);
43
+ const frame = this.#consume(line);
44
+ if (frame !== null) {
45
+ frames.push(frame);
46
+ }
47
+ newline = this.#nextLineBreak();
48
+ }
49
+ return frames;
50
+ }
51
+ #nextLineBreak() {
52
+ for (let index = 0; index < this.#buffer.length; index++) {
53
+ const char = this.#buffer[index];
54
+ if (char === '\n') {
55
+ return { index, length: 1 };
56
+ }
57
+ if (char === '\r') {
58
+ // A trailing lone "\r" may be the first half of "\r\n"; wait for more.
59
+ if (index === this.#buffer.length - 1) {
60
+ return null;
61
+ }
62
+ return this.#buffer[index + 1] === '\n'
63
+ ? { index, length: 2 }
64
+ : { index, length: 1 };
65
+ }
66
+ }
67
+ return null;
68
+ }
69
+ #consume(line) {
70
+ if (line.length === 0) {
71
+ return this.#flush();
72
+ }
73
+ if (line.startsWith(':')) {
74
+ return null;
75
+ }
76
+ const colon = line.indexOf(':');
77
+ const field = colon === -1 ? line : line.slice(0, colon);
78
+ let value = colon === -1 ? '' : line.slice(colon + 1);
79
+ if (value.startsWith(' ')) {
80
+ value = value.slice(1);
81
+ }
82
+ switch (field) {
83
+ case 'data':
84
+ this.#data.push(value);
85
+ break;
86
+ case 'event':
87
+ this.#event = value;
88
+ break;
89
+ case 'id':
90
+ this.#id = value;
91
+ break;
92
+ default:
93
+ break;
94
+ }
95
+ return null;
96
+ }
97
+ #flush() {
98
+ if (this.#data.length === 0 && this.#event === null) {
99
+ return null;
100
+ }
101
+ const frame = {
102
+ id: this.#id,
103
+ event: this.#event ?? 'message',
104
+ data: this.#data.join('\n'),
105
+ };
106
+ this.#data = [];
107
+ this.#event = null;
108
+ this.#id = null;
109
+ return frame;
110
+ }
111
+ }
112
+ /**
113
+ * Reads a `text/event-stream` response body as frames.
114
+ *
115
+ * `EventSource` is not used: it cannot send an `Authorization` header and it
116
+ * cannot issue a POST, and Tracon needs both.
117
+ */
118
+ export async function* readSse(response, options) {
119
+ if (response.body === null) {
120
+ return;
121
+ }
122
+ const reader = response.body.getReader();
123
+ const utf8 = new TextDecoder();
124
+ const decoder = new SseDecoder();
125
+ const idleTimeoutMs = options?.idleTimeoutMs;
126
+ try {
127
+ for (;;) {
128
+ const { done, value } = idleTimeoutMs === undefined ? await reader.read() : await readWithin(reader, idleTimeoutMs);
129
+ if (done) {
130
+ break;
131
+ }
132
+ for (const frame of decoder.push(utf8.decode(value, { stream: true }))) {
133
+ yield frame;
134
+ }
135
+ }
136
+ }
137
+ finally {
138
+ reader.releaseLock();
139
+ }
140
+ }
141
+ /**
142
+ * Reads the next chunk, or throws {@link SseIdleTimeoutError} when nothing at
143
+ * all arrives within `idleTimeoutMs`.
144
+ *
145
+ * The reader is cancelled on the way out so the underlying connection is
146
+ * released rather than left half-open behind a generator nobody is pulling.
147
+ */
148
+ async function readWithin(reader, idleTimeoutMs) {
149
+ let timer;
150
+ const idle = new Promise((_resolve, reject) => {
151
+ timer = setTimeout(() => reject(new SseIdleTimeoutError(idleTimeoutMs)), idleTimeoutMs);
152
+ });
153
+ try {
154
+ return await Promise.race([reader.read(), idle]);
155
+ }
156
+ catch (error) {
157
+ if (error instanceof SseIdleTimeoutError) {
158
+ await reader.cancel().catch(() => undefined);
159
+ }
160
+ throw error;
161
+ }
162
+ finally {
163
+ clearTimeout(timer);
164
+ }
165
+ }
166
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@tracon/client",
3
+ "version": "1.0.0-preview.1",
4
+ "description": "Typed TypeScript client for the Tracon management API, generated from its OpenAPI document.",
5
+ "license": "PolyForm-Small-Business-1.0.0",
6
+ "type": "module",
7
+ "homepage": "https://tracon.dev",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "LICENSE.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "scripts": {
27
+ "generate": "node scripts/generate.mjs",
28
+ "build": "tsc",
29
+ "prepublishOnly": "npm run build",
30
+ "test": "vitest run",
31
+ "typecheck": "tsc --noEmit"
32
+ },
33
+ "dependencies": {
34
+ "openapi-fetch": "^0.17.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^26.2.0",
38
+ "openapi-typescript": "^7.13.0",
39
+ "typescript": "^5.9.3",
40
+ "vitest": "^3.2.4"
41
+ }
42
+ }