@nonoun/native-chat 0.3.0 → 0.4.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.
@@ -12,8 +12,21 @@ import { NativeElement } from '@nonoun/native-ui';
12
12
  * </n-chat-feed>
13
13
  * ```
14
14
  *
15
+ * ### Virtualization (opt-in)
16
+ *
17
+ * ```html
18
+ * <n-chat-feed virtual virtual-item-height="80" virtual-overscan="8">
19
+ * </n-chat-feed>
20
+ * ```
21
+ *
22
+ * Set `items` and `itemRenderer` properties for programmatic data-driven rendering.
23
+ *
15
24
  * @attr {boolean} auto-scroll - Enable auto-scroll to bottom on new content (default: true)
25
+ * @attr {boolean} virtual - Enable virtual scroll windowing
26
+ * @attr {string} virtual-item-height - Estimated item height in px (default: 80)
27
+ * @attr {string} virtual-overscan - Number of extra items to render outside viewport (default: 5)
16
28
  * @fires native:feed-scroll - Fired when scroll pinned state changes
29
+ * @fires native:range-change - Fired when virtual scroll range changes
17
30
  */
18
31
  export declare class NChatFeed extends NativeElement {
19
32
  #private;
@@ -22,6 +35,12 @@ export declare class NChatFeed extends NativeElement {
22
35
  get isPinned(): boolean;
23
36
  /** Scroll to the bottom of the feed. */
24
37
  scrollToBottom(smooth?: boolean): void;
38
+ /** Data items for virtual rendering. */
39
+ get items(): unknown[];
40
+ set items(val: unknown[]);
41
+ /** Callback that creates an HTMLElement from a data item. */
42
+ get itemRenderer(): ((item: unknown, index: number) => HTMLElement) | null;
43
+ set itemRenderer(fn: ((item: unknown, index: number) => HTMLElement) | null);
25
44
  attributeChangedCallback(name: string, old: string | null, val: string | null): void;
26
45
  setup(): void;
27
46
  teardown(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-feed-element.d.ts","sourceRoot":"","sources":["../../src/feed/chat-feed-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,mBAAmB,CAAC;AAI1D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,SAAU,SAAQ,aAAa;;IAC1C,MAAM,CAAC,kBAAkB,WAAiC;;IAc1D,IAAI,QAAQ,IAAI,OAAO,CAAiC;IAExD,wCAAwC;IACxC,cAAc,CAAC,MAAM,UAAO,GAAG,IAAI;IAUnC,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAUpF,KAAK,IAAI,IAAI;IA4Bb,QAAQ,IAAI,IAAI;CAuCjB"}
1
+ {"version":3,"file":"chat-feed-element.d.ts","sourceRoot":"","sources":["../../src/feed/chat-feed-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAmC,MAAM,mBAAmB,CAAC;AAInF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,SAAU,SAAQ,aAAa;;IAC1C,MAAM,CAAC,kBAAkB,WAAuF;;IAoBhH,IAAI,QAAQ,IAAI,OAAO,CAAiC;IAExD,wCAAwC;IACxC,cAAc,CAAC,MAAM,UAAO,GAAG,IAAI;IAQnC,wCAAwC;IACxC,IAAI,KAAK,IAAI,OAAO,EAAE,CAA8B;IACpD,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,EAKvB;IAED,6DAA6D;IAC7D,IAAI,YAAY,IAAI,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,WAAW,CAAC,GAAG,IAAI,CAA+B;IACzG,IAAI,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,WAAW,CAAC,GAAG,IAAI,EAE1E;IAID,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IA0BpF,KAAK,IAAI,IAAI;IAiCb,QAAQ,IAAI,IAAI;CA6HjB"}
package/dist/index.d.ts CHANGED
@@ -4,12 +4,14 @@ export { NChatPanel } from './chat-panel-element.ts';
4
4
  export { NChatFeed } from './feed/index.ts';
5
5
  export { NChatAvatar } from './avatar/index.ts';
6
6
  export { NChatMessage, NChatMessages, NChatMessageText, NChatMessageActivity, NChatMessageSeed, NChatMessageGenUI, NChatInputStructured, } from './message/index.ts';
7
+ export type { ChatMessageActionDef } from './message/chat-message-element.ts';
8
+ export { ACTION_REGISTRY, ROLE_DEFAULTS } from './message/chat-message-element.ts';
7
9
  export type { SeedOption } from './message/chat-message-seed-element.ts';
8
10
  export type { StructuredOption } from './message/chat-input-structured-element.ts';
9
11
  export type { GenUINode } from './message/chat-message-genui-element.ts';
10
12
  export { renderMarkdown, renderInline, sanitizeHtml } from './message/chat-message-text-element.ts';
11
- export type { ChatStreamChunk, StreamFormat, ChatTransportOptions, ChatStreamEvent, ChatTransport, } from './stream/index.ts';
12
- export { parseSSE, parseNDJSON, parseJSON, detectFormat, createChatStream, createChatTransport, } from './stream/index.ts';
13
+ export type { ChatStreamChunk, StreamFormat, ChatTransportOptions, ChatStreamEvent, ChatTransport, TransportState, TransportStatus, TransportStateCallback, RetryOptions, StreamEndReason, } from './stream/index.ts';
14
+ export { parseSSE, parseNDJSON, parseJSON, detectFormat, createChatStream, createChatTransport, classifyStreamEnd, classifyHttpError, backoffDelay, } from './stream/index.ts';
13
15
  export type { TelemetryCorrelation, TelemetryTiming, TelemetryRetry, TelemetryLevel, TelemetryEvent, TelemetryRedactor, } from './telemetry/index.ts';
14
16
  export { CHAT_EVENTS, SAFE_FIELDS, SENSITIVE_FIELDS, scrubPII, createDefaultRedactor, TelemetryEmitter, } from './telemetry/index.ts';
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAGvB,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,YAAY,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AACnF,YAAY,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAGpG,YAAY,EACV,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,QAAQ,EACR,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAGvB,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAGnF,YAAY,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,YAAY,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AACnF,YAAY,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAGpG,YAAY,EACV,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,QAAQ,EACR,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC"}
@@ -1,4 +1,10 @@
1
1
  import { NativeElement } from '@nonoun/native-ui';
2
+ export interface ChatMessageActionDef {
3
+ label: string;
4
+ icon: string;
5
+ }
6
+ export declare const ACTION_REGISTRY: Record<string, ChatMessageActionDef>;
7
+ export declare const ROLE_DEFAULTS: Record<string, string[]>;
2
8
  /**
3
9
  * Individual chat message bubble.
4
10
  *
@@ -8,8 +14,11 @@ import { NativeElement } from '@nonoun/native-ui';
8
14
  * @attr {string} role - `user` | `assistant` | `system`
9
15
  * @attr {string} message-id - Unique message identifier
10
16
  * @attr {string} timestamp - Epoch milliseconds
11
- * @attr {string} status - `sending` | `sent` | `error` | `streaming`
17
+ * @attr {string} status - `sending` | `sent` | `error` | `streaming` | `partial`
18
+ * @attr {string} actions - Comma-separated action list, or `"none"` to suppress
19
+ * @attr {string} actions-style - `"label"` (default) | `"icon"` | `"icon-label"`
12
20
  * @fires native:message-action - Fired when an action button is clicked
21
+ * @fires native:continue-request - Fired when continue is requested for a partial message
13
22
  */
14
23
  export declare class NChatMessage extends NativeElement {
15
24
  #private;
@@ -21,6 +30,10 @@ export declare class NChatMessage extends NativeElement {
21
30
  get timestamp(): number;
22
31
  get status(): string;
23
32
  set status(val: string);
33
+ get actions(): string | null;
34
+ set actions(val: string | null);
35
+ get actionsStyle(): string;
36
+ set actionsStyle(val: string);
24
37
  attributeChangedCallback(name: string, old: string | null, val: string | null): void;
25
38
  setup(): void;
26
39
  teardown(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-message-element.d.ts","sourceRoot":"","sources":["../../src/message/chat-message-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,SAAQ,aAAa;;IAC7C,MAAM,CAAC,kBAAkB,WAAiD;;IAc1E,IAAI,IAAI,IAAI,MAAM,CAA6B;IAC/C,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAGnB;IAED,IAAI,SAAS,IAAI,MAAM,CAAkD;IAEzE,IAAI,SAAS,IAAI,MAAM,CAAwD;IAE/E,IAAI,MAAM,IAAI,MAAM,CAA+B;IACnD,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,EAGrB;IAID,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAWpF,KAAK,IAAI,IAAI;IAeb,QAAQ,IAAI,IAAI;CAsDjB"}
1
+ {"version":3,"file":"chat-message-element.d.ts","sourceRoot":"","sources":["../../src/message/chat-message-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,mBAAmB,CAAC;AAI1D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAOhE,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAGlD,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAa,SAAQ,aAAa;;IAC7C,MAAM,CAAC,kBAAkB,WAA6E;;IAgBtG,IAAI,IAAI,IAAI,MAAM,CAA6B;IAC/C,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAGnB;IAED,IAAI,SAAS,IAAI,MAAM,CAAkD;IAEzE,IAAI,SAAS,IAAI,MAAM,CAAwD;IAE/E,IAAI,MAAM,IAAI,MAAM,CAA+B;IACnD,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,EAGrB;IAED,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAAgC;IAC5D,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAO7B;IAED,IAAI,YAAY,IAAI,MAAM,CAAqC;IAC/D,IAAI,YAAY,CAAC,GAAG,EAAE,MAAM,EAG3B;IAID,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAapF,KAAK,IAAI,IAAI;IAkBb,QAAQ,IAAI,IAAI;CAoFjB"}
@@ -150,6 +150,19 @@
150
150
  user-select: none;
151
151
  }
152
152
 
153
+ /* ── Virtual scroll spacers ── */
154
+
155
+ :where(n-chat-feed) > :where(.n-chat-feed-virtual-container) {
156
+ display: flex;
157
+ flex-direction: column;
158
+ gap: var(--n-chat-gap);
159
+ }
160
+
161
+ :where(n-chat-feed) :where(.n-virtual-spacer-top),
162
+ :where(n-chat-feed) :where(.n-virtual-spacer-bottom) {
163
+ flex-shrink: 0;
164
+ }
165
+
153
166
  :where(n-chat-feed[hidden]) { display: none; }
154
167
 
155
168
  /* ══════════════════════════════════════════════════
@@ -254,6 +267,11 @@
254
267
  border: 1px solid var(--n-border-danger);
255
268
  }
256
269
 
270
+ /* Partial status — stream ended without explicit completion */
271
+ [status="partial"]:where(n-chat-message) {
272
+ border-bottom: 2px dashed var(--n-border-muted);
273
+ }
274
+
257
275
  /* ── Message actions toolbar ── */
258
276
 
259
277
  :where(n-chat-message) > :where(.n-chat-message-actions) {
@@ -267,6 +285,11 @@
267
285
  opacity: 1;
268
286
  }
269
287
 
288
+ /* Partial status — always show actions (contains continue button) */
289
+ [status="partial"]:where(n-chat-message) > :where(.n-chat-message-actions) {
290
+ opacity: 1;
291
+ }
292
+
270
293
  :where(n-chat-message[hidden]) { display: none; }
271
294
 
272
295
  /* ══════════════════════════════════════════════════
@@ -1,11 +1,11 @@
1
- import { a as e, c as t, d as n, f as r, i, l as a, m as o, n as s, o as c, p as l, r as u, s as d, t as f, u as p } from "./chat-input-structured-element-Cpar3CeA.js";
1
+ import { a as e, c as t, d as n, f as r, g as i, h as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h } from "./chat-input-structured-element-C80qP8yg.js";
2
2
  /**
3
3
  * Parse a Server-Sent Events (SSE) response stream into ChatStreamChunk values.
4
4
  *
5
5
  * Reads `data:` lines from the response body, skips empty lines and comments,
6
6
  * handles the `[DONE]` sentinel, and accumulates the full message across chunks.
7
7
  */
8
- async function* m(e) {
8
+ async function* g(e) {
9
9
  let t = e.body.getReader(), n = new TextDecoder("utf-8"), r = "", i = "";
10
10
  try {
11
11
  for (;;) {
@@ -61,7 +61,8 @@ async function* m(e) {
61
61
  fullMessage: i,
62
62
  role: "assistant",
63
63
  datetime: Date.now(),
64
- done: !0
64
+ done: !0,
65
+ partial: !0
65
66
  };
66
67
  return;
67
68
  } catch {}
@@ -71,7 +72,8 @@ async function* m(e) {
71
72
  fullMessage: i,
72
73
  role: "assistant",
73
74
  datetime: Date.now(),
74
- done: !0
75
+ done: !0,
76
+ partial: !0
75
77
  });
76
78
  } finally {
77
79
  t.releaseLock();
@@ -83,7 +85,7 @@ async function* m(e) {
83
85
  * Each non-empty line is parsed as a standalone JSON object.
84
86
  * Accumulates the full message across chunks.
85
87
  */
86
- async function* h(e) {
88
+ async function* _(e) {
87
89
  let t = e.body.getReader(), n = new TextDecoder("utf-8"), r = "", i = "";
88
90
  try {
89
91
  for (;;) {
@@ -121,7 +123,8 @@ async function* h(e) {
121
123
  fullMessage: i,
122
124
  role: "assistant",
123
125
  datetime: Date.now(),
124
- done: !0
126
+ done: !0,
127
+ partial: !0
125
128
  };
126
129
  return;
127
130
  } catch {}
@@ -130,7 +133,8 @@ async function* h(e) {
130
133
  fullMessage: i,
131
134
  role: "assistant",
132
135
  datetime: Date.now(),
133
- done: !0
136
+ done: !0,
137
+ partial: !0
134
138
  });
135
139
  } finally {
136
140
  t.releaseLock();
@@ -142,7 +146,7 @@ async function* h(e) {
142
146
  * Reads the entire response body, parses as JSON, and yields one chunk
143
147
  * with `done: true`.
144
148
  */
145
- async function* g(e) {
149
+ async function* v(e) {
146
150
  let t = await e.json(), n = "";
147
151
  typeof t.content == "string" ? n = t.content : typeof t.message == "string" ? n = t.message : typeof t.delta == "string" ? n = t.delta : Array.isArray(t.choices) && typeof t.choices[0]?.message?.content == "string" && (n = t.choices[0].message.content), yield {
148
152
  delta: n,
@@ -153,9 +157,31 @@ async function* g(e) {
153
157
  };
154
158
  }
155
159
  /**
160
+ * Classify how a stream ended based on the final chunk and optional error.
161
+ * - `complete` — explicit done signal received (e.g., `[DONE]` sentinel, `finish_reason`)
162
+ * - `partial` — stream ended without explicit completion (truncated)
163
+ * - `error` — an error occurred during streaming
164
+ * - `stopped` — consumer aborted the stream
165
+ */
166
+ function y(e, t) {
167
+ return t ? t.name === "AbortError" ? "stopped" : "error" : e.partial ? "partial" : "complete";
168
+ }
169
+ /**
170
+ * Classify an HTTP status code into a transport state.
171
+ */
172
+ function b(e) {
173
+ return e === 401 || e === 403 ? "auth-required" : e === 429 ? "rate-limited" : "server-error";
174
+ }
175
+ /**
176
+ * Calculate exponential backoff delay with jitter.
177
+ */
178
+ function x(e, t = 1e3, n = 3e4) {
179
+ return Math.min(n, t * 2 ** e + Math.random() * 500);
180
+ }
181
+ /**
156
182
  * Detect the stream format from a Content-Type header value.
157
183
  */
158
- function _(e) {
184
+ function S(e) {
159
185
  let t = e.toLowerCase();
160
186
  return t.includes("text/event-stream") ? "sse" : t.includes("ndjson") || t.includes("x-ndjson") ? "ndjson" : "json";
161
187
  }
@@ -163,48 +189,125 @@ function _(e) {
163
189
  * Create an async generator that yields ChatStreamChunk values from a
164
190
  * Response, auto-detecting the format from Content-Type when not specified.
165
191
  */
166
- async function* v(e, t) {
167
- switch (t ?? _(e.headers.get("content-type") ?? "")) {
192
+ async function* C(e, t) {
193
+ switch (t ?? S(e.headers.get("content-type") ?? "")) {
168
194
  case "sse":
169
- yield* m(e);
195
+ yield* g(e);
170
196
  break;
171
197
  case "ndjson":
172
- yield* h(e);
198
+ yield* _(e);
173
199
  break;
174
200
  case "json":
175
- yield* g(e);
201
+ yield* v(e);
176
202
  break;
177
203
  }
178
204
  }
179
205
  /**
180
206
  * Create a reusable chat transport that sends requests and returns a
181
- * streaming async generator.
207
+ * streaming async generator. Supports retry/backoff and transport state callbacks.
182
208
  */
183
- function y(e) {
184
- return { async send(t) {
185
- let n = {
209
+ function w(e) {
210
+ let t = (t) => {
211
+ e.onStateChange?.(t);
212
+ }, n = e.retry?.maxAttempts ?? 1, r = e.retry?.baseDelayMs ?? 1e3, i = e.retry?.maxDelayMs ?? 3e4;
213
+ return { async send(a) {
214
+ let o = {
186
215
  "Content-Type": "application/json",
187
216
  ...e.headers
188
217
  };
189
- e.clientId && (n["X-Client-Id"] = e.clientId);
190
- let r = await fetch(e.baseUrl, {
191
- method: "POST",
192
- headers: n,
193
- body: JSON.stringify(t),
194
- signal: e.signal
195
- });
196
- if (!r.ok) throw Error(`Chat transport error: ${r.status} ${r.statusText}`);
197
- return v(r, e.format);
218
+ e.clientId && (o["X-Client-Id"] = e.clientId);
219
+ let s;
220
+ for (let c = 0; c < n; c++) {
221
+ t({
222
+ state: c === 0 ? "sending" : "retrying",
223
+ attempt: c,
224
+ maxAttempts: n
225
+ });
226
+ let l;
227
+ try {
228
+ l = await fetch(e.baseUrl, {
229
+ method: "POST",
230
+ headers: o,
231
+ body: JSON.stringify(a),
232
+ signal: e.signal
233
+ });
234
+ } catch (e) {
235
+ if (s = e instanceof Error ? e : Error(String(e)), t({
236
+ state: s.name === "TypeError" ? "offline" : "server-error",
237
+ error: s,
238
+ attempt: c,
239
+ maxAttempts: n
240
+ }), c + 1 < n) {
241
+ let e = x(c, r, i);
242
+ t({
243
+ state: "retrying",
244
+ retryInMs: e,
245
+ attempt: c + 1,
246
+ maxAttempts: n
247
+ }), await E(e);
248
+ continue;
249
+ }
250
+ break;
251
+ }
252
+ if (!l.ok) {
253
+ let e = b(l.status);
254
+ s = /* @__PURE__ */ Error(`Chat transport error: ${l.status} ${l.statusText}`);
255
+ let a;
256
+ if (l.status === 429) {
257
+ let e = l.headers.get("Retry-After");
258
+ if (e) {
259
+ let t = Number(e);
260
+ a = Number.isFinite(t) ? t * 1e3 : Date.parse(e) - Date.now();
261
+ }
262
+ }
263
+ if (t({
264
+ state: e,
265
+ statusCode: l.status,
266
+ retryInMs: a,
267
+ error: s,
268
+ attempt: c,
269
+ maxAttempts: n
270
+ }), (l.status === 429 || l.status >= 500) && c + 1 < n) {
271
+ let e = a ?? x(c, r, i);
272
+ t({
273
+ state: "retrying",
274
+ retryInMs: e,
275
+ attempt: c + 1,
276
+ maxAttempts: n
277
+ }), await E(e);
278
+ continue;
279
+ }
280
+ break;
281
+ }
282
+ return t({ state: "streaming" }), T(C(l, e.format), t);
283
+ }
284
+ throw s ?? /* @__PURE__ */ Error("Chat transport failed");
198
285
  } };
199
286
  }
200
- const b = {
287
+ /**
288
+ * Wraps an async generator to emit `ready` state when the stream finishes.
289
+ */
290
+ async function* T(e, t) {
291
+ try {
292
+ yield* e, t({ state: "ready" });
293
+ } catch (e) {
294
+ throw t({
295
+ state: "server-error",
296
+ error: e instanceof Error ? e : Error(String(e))
297
+ }), e;
298
+ }
299
+ }
300
+ function E(e) {
301
+ return new Promise((t) => setTimeout(t, e));
302
+ }
303
+ const D = {
201
304
  TRANSPORT: "native:chat-transport",
202
305
  GENERATION_START: "native:generation-start",
203
306
  GENERATION_COMPLETE: "native:generation-complete",
204
307
  GENERATION_STOP: "native:generation-stop",
205
308
  GENERATION_ERROR: "native:generation-error",
206
309
  WARNING: "native:chat-warning"
207
- }, x = [
310
+ }, O = [
208
311
  "requestId",
209
312
  "sessionId",
210
313
  "conversationId",
@@ -217,7 +320,7 @@ const b = {
217
320
  "attempt",
218
321
  "maxAttempts",
219
322
  "chunkIndex"
220
- ], S = [
323
+ ], k = [
221
324
  "query",
222
325
  "message",
223
326
  "delta",
@@ -227,17 +330,17 @@ const b = {
227
330
  "prompt",
228
331
  "response"
229
332
  ];
230
- var C = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, w = /\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{2,4}\)?[-.\s]?\d{3,4}[-.\s]?\d{3,4}\b/g, T = /\b(?:\d{4}[-\s]?){3}\d{4}\b/g;
333
+ var A = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, j = /\b(?:\+?\d{1,3}[-.\s]?)?\(?\d{2,4}\)?[-.\s]?\d{3,4}[-.\s]?\d{3,4}\b/g, M = /\b(?:\d{4}[-\s]?){3}\d{4}\b/g;
231
334
  /**
232
335
  * Scrub common PII patterns from a string.
233
336
  *
234
337
  * Replaces email addresses, phone-like numbers, and credit-card-like
235
338
  * number sequences with `[redacted]` placeholders.
236
339
  */
237
- function E(e) {
238
- return e.replace(C, "[redacted:email]").replace(T, "[redacted:card]").replace(w, "[redacted:phone]");
340
+ function N(e) {
341
+ return e.replace(A, "[redacted:email]").replace(M, "[redacted:card]").replace(j, "[redacted:phone]");
239
342
  }
240
- var D = new Set(S);
343
+ var P = new Set(k);
241
344
  /**
242
345
  * Create a default redactor for the given telemetry level.
243
346
  *
@@ -245,11 +348,11 @@ var D = new Set(S);
245
348
  * `[redacted:Nchars]` indicating the original length.
246
349
  * - **debug**: All fields pass through unmodified.
247
350
  */
248
- function O(e) {
351
+ function F(e) {
249
352
  return e === "debug" ? (e) => e : (e) => {
250
353
  if (!e.detail) return e;
251
354
  let t = {};
252
- for (let [n, r] of Object.entries(e.detail)) D.has(n) && typeof r == "string" ? t[n] = `[redacted:${r.length}chars]` : t[n] = r;
355
+ for (let [n, r] of Object.entries(e.detail)) P.has(n) && typeof r == "string" ? t[n] = `[redacted:${r.length}chars]` : t[n] = r;
253
356
  return {
254
357
  ...e,
255
358
  detail: t
@@ -260,15 +363,15 @@ function O(e) {
260
363
  * Dispatches telemetry events as `CustomEvent` instances on a given
261
364
  * `EventTarget`, applying a configurable redactor before emission.
262
365
  */
263
- var k = class {
366
+ var I = class {
264
367
  #e;
265
368
  #t;
266
369
  constructor(e, t = "minimal") {
267
- this.#e = e, this.#t = O(t);
370
+ this.#e = e, this.#t = F(t);
268
371
  }
269
372
  /** Change the telemetry level, replacing the redactor. */
270
373
  setLevel(e) {
271
- this.#t = O(e);
374
+ this.#t = F(e);
272
375
  }
273
376
  /** Replace the redactor with a custom implementation. */
274
377
  setRedactor(e) {
@@ -284,7 +387,7 @@ var k = class {
284
387
  }
285
388
  emitTransport(e, t, n) {
286
389
  this.emit({
287
- type: b.TRANSPORT,
390
+ type: D.TRANSPORT,
288
391
  correlation: e,
289
392
  detail: {
290
393
  mode: t,
@@ -294,14 +397,14 @@ var k = class {
294
397
  }
295
398
  emitGenerationStart(e) {
296
399
  this.emit({
297
- type: b.GENERATION_START,
400
+ type: D.GENERATION_START,
298
401
  correlation: e,
299
402
  timing: { startedAt: Date.now() }
300
403
  });
301
404
  }
302
405
  emitGenerationComplete(e, t) {
303
406
  this.emit({
304
- type: b.GENERATION_COMPLETE,
407
+ type: D.GENERATION_COMPLETE,
305
408
  correlation: e,
306
409
  timing: {
307
410
  completedAt: Date.now(),
@@ -311,7 +414,7 @@ var k = class {
311
414
  }
312
415
  emitWarning(e, t, n) {
313
416
  this.emit({
314
- type: b.WARNING,
417
+ type: D.WARNING,
315
418
  correlation: e,
316
419
  detail: {
317
420
  message: t,
@@ -320,4 +423,4 @@ var k = class {
320
423
  });
321
424
  }
322
425
  };
323
- export { b as CHAT_EVENTS, n as NChatAvatar, r as NChatFeed, o as NChatInput, f as NChatInputStructured, p as NChatMessage, i as NChatMessageActivity, s as NChatMessageGenUI, u as NChatMessageSeed, e as NChatMessageText, a as NChatMessages, l as NChatPanel, x as SAFE_FIELDS, S as SENSITIVE_FIELDS, k as TelemetryEmitter, v as createChatStream, y as createChatTransport, O as createDefaultRedactor, _ as detectFormat, g as parseJSON, h as parseNDJSON, m as parseSSE, c as renderInline, d as renderMarkdown, t as sanitizeHtml, E as scrubPII };
426
+ export { h as ACTION_REGISTRY, D as CHAT_EVENTS, d as NChatAvatar, c as NChatFeed, i as NChatInput, m as NChatInputStructured, n as NChatMessage, o as NChatMessageActivity, l as NChatMessageGenUI, f as NChatMessageSeed, e as NChatMessageText, s as NChatMessages, a as NChatPanel, r as ROLE_DEFAULTS, O as SAFE_FIELDS, k as SENSITIVE_FIELDS, I as TelemetryEmitter, x as backoffDelay, b as classifyHttpError, y as classifyStreamEnd, C as createChatStream, w as createChatTransport, F as createDefaultRedactor, S as detectFormat, v as parseJSON, _ as parseNDJSON, g as parseSSE, u as renderInline, p as renderMarkdown, t as sanitizeHtml, N as scrubPII };
package/dist/register.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, d as t, f as n, i as r, l as i, m as a, n as o, p as s, r as c, t as l, u } from "./chat-input-structured-element-Cpar3CeA.js";
1
+ import { a as e, d as t, g as n, h as r, i, l as a, m as o, n as s, p as c, r as l, t as u } from "./chat-input-structured-element-C80qP8yg.js";
2
2
  import { NButton as d, NCard as f, NDialog as p, NIcon as m, NTextarea as h, NToolbar as g, define as _, registerIcon as v } from "@nonoun/native-ui";
3
- _("n-chat-input", a), _("native-chat-panel", s), _("n-chat-feed", n), _("n-chat-avatar", t), _("n-chat-message", u), _("n-chat-messages", i), _("n-chat-message-text", e), _("n-chat-message-activity", r), _("n-chat-message-seed", c), _("n-chat-message-genui", o), _("n-chat-input-structured", l), _("n-textarea", h), _("n-button", d), _("n-icon", m), _("n-toolbar", g), _("n-dialog", p), _("n-card", f), v("chat-dots", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M116,128a12,12,0,1,1,12,12A12,12,0,0,1,116,128ZM84,140a12,12,0,1,0-12-12A12,12,0,0,0,84,140Zm88,0a12,12,0,1,0-12-12A12,12,0,0,0,172,140Zm60-76V192a16,16,0,0,1-16,16H83l-32.6,28.16-.09.07A15.89,15.89,0,0,1,40,240a16.13,16.13,0,0,1-6.8-1.52A15.85,15.85,0,0,1,24,224V64A16,16,0,0,1,40,48H216A16,16,0,0,1,232,64ZM40,224h0ZM216,64H40V224l34.77-30A8,8,0,0,1,80,192H216Z\"/></svg>"), v("user", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M230.92,212c-15.23-26.33-38.7-45.21-66.09-54.16a72,72,0,1,0-73.66,0C63.78,166.78,40.31,185.66,25.08,212a8,8,0,1,0,13.85,8C55.71,194.74,89.05,176,128,176s72.29,18.74,89.07,44a8,8,0,0,0,13.85-8ZM72,96a56,56,0,1,1,56,56A56.06,56.06,0,0,1,72,96Z\"/></svg>"), v("stop", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,160H56V56H200V200Z\"/></svg>"), v("arrow-counter-clockwise", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M224,128a96,96,0,0,1-94.71,96H128A95.38,95.38,0,0,1,62.1,197.8a8,8,0,0,1,11-11.63A80,80,0,1,0,71.43,71.39a3.07,3.07,0,0,1-.26.25L44.59,96H72a8,8,0,0,1,0,16H24a8,8,0,0,1-8-8V56a8,8,0,0,1,16,0V85.8L60.25,60A96,96,0,0,1,224,128Z\"/></svg>");
4
- export { t as NChatAvatar, n as NChatFeed, a as NChatInput, l as NChatInputStructured, u as NChatMessage, r as NChatMessageActivity, o as NChatMessageGenUI, c as NChatMessageSeed, e as NChatMessageText, i as NChatMessages, s as NChatPanel };
3
+ _("n-chat-input", n), _("native-chat-panel", r), _("n-chat-feed", o), _("n-chat-avatar", c), _("n-chat-message", t), _("n-chat-messages", a), _("n-chat-message-text", e), _("n-chat-message-activity", i), _("n-chat-message-seed", l), _("n-chat-message-genui", s), _("n-chat-input-structured", u), _("n-textarea", h), _("n-button", d), _("n-icon", m), _("n-toolbar", g), _("n-dialog", p), _("n-card", f), v("chat-dots", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M116,128a12,12,0,1,1,12,12A12,12,0,0,1,116,128ZM84,140a12,12,0,1,0-12-12A12,12,0,0,0,84,140Zm88,0a12,12,0,1,0-12-12A12,12,0,0,0,172,140Zm60-76V192a16,16,0,0,1-16,16H83l-32.6,28.16-.09.07A15.89,15.89,0,0,1,40,240a16.13,16.13,0,0,1-6.8-1.52A15.85,15.85,0,0,1,24,224V64A16,16,0,0,1,40,48H216A16,16,0,0,1,232,64ZM40,224h0ZM216,64H40V224l34.77-30A8,8,0,0,1,80,192H216Z\"/></svg>"), v("user", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M230.92,212c-15.23-26.33-38.7-45.21-66.09-54.16a72,72,0,1,0-73.66,0C63.78,166.78,40.31,185.66,25.08,212a8,8,0,1,0,13.85,8C55.71,194.74,89.05,176,128,176s72.29,18.74,89.07,44a8,8,0,0,0,13.85-8ZM72,96a56,56,0,1,1,56,56A56.06,56.06,0,0,1,72,96Z\"/></svg>"), v("stop", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,160H56V56H200V200Z\"/></svg>"), v("arrow-counter-clockwise", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M224,128a96,96,0,0,1-94.71,96H128A95.38,95.38,0,0,1,62.1,197.8a8,8,0,0,1,11-11.63A80,80,0,1,0,71.43,71.39a3.07,3.07,0,0,1-.26.25L44.59,96H72a8,8,0,0,1,0,16H24a8,8,0,0,1-8-8V56a8,8,0,0,1,16,0V85.8L60.25,60A96,96,0,0,1,224,128Z\"/></svg>");
4
+ export { c as NChatAvatar, o as NChatFeed, n as NChatInput, u as NChatInputStructured, t as NChatMessage, i as NChatMessageActivity, s as NChatMessageGenUI, l as NChatMessageSeed, e as NChatMessageText, a as NChatMessages, r as NChatPanel };
@@ -0,0 +1,19 @@
1
+ import type { ChatStreamChunk, TransportState } from './types.ts';
2
+ export type StreamEndReason = 'complete' | 'partial' | 'error' | 'stopped';
3
+ /**
4
+ * Classify how a stream ended based on the final chunk and optional error.
5
+ * - `complete` — explicit done signal received (e.g., `[DONE]` sentinel, `finish_reason`)
6
+ * - `partial` — stream ended without explicit completion (truncated)
7
+ * - `error` — an error occurred during streaming
8
+ * - `stopped` — consumer aborted the stream
9
+ */
10
+ export declare function classifyStreamEnd(chunk: ChatStreamChunk, error?: Error): StreamEndReason;
11
+ /**
12
+ * Classify an HTTP status code into a transport state.
13
+ */
14
+ export declare function classifyHttpError(status: number): TransportState;
15
+ /**
16
+ * Calculate exponential backoff delay with jitter.
17
+ */
18
+ export declare function backoffDelay(attempt: number, base?: number, max?: number): number;
19
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/stream/classify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElE,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,eAAe,CAOxF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAKhE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAO,EAAE,GAAG,SAAQ,GAAG,MAAM,CAE9E"}
@@ -13,7 +13,7 @@ export interface ChatTransport {
13
13
  }
14
14
  /**
15
15
  * Create a reusable chat transport that sends requests and returns a
16
- * streaming async generator.
16
+ * streaming async generator. Supports retry/backoff and transport state callbacks.
17
17
  */
18
18
  export declare function createChatTransport(options: ChatTransportOptions): ChatTransport;
19
19
  //# sourceMappingURL=create-transport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-transport.d.ts","sourceRoot":"","sources":["../../src/stream/create-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKtF;;GAEG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAK9D;AAED;;;GAGG;AACH,wBAAuB,gBAAgB,CACrC,QAAQ,EAAE,QAAQ,EAClB,MAAM,CAAC,EAAE,YAAY,GACpB,cAAc,CAAC,eAAe,CAAC,CAejC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC;CAC/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CA2Bf"}
1
+ {"version":3,"file":"create-transport.d.ts","sourceRoot":"","sources":["../../src/stream/create-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAmB,MAAM,YAAY,CAAC;AAMvG;;GAEG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAK9D;AAED;;;GAGG;AACH,wBAAuB,gBAAgB,CACrC,QAAQ,EAAE,QAAQ,EAClB,MAAM,CAAC,EAAE,YAAY,GACpB,cAAc,CAAC,eAAe,CAAC,CAejC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC;CAC/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAsGf"}
@@ -1,7 +1,9 @@
1
- export type { ChatStreamChunk, StreamFormat, ChatTransportOptions, ChatStreamEvent, } from './types.ts';
1
+ export type { ChatStreamChunk, StreamFormat, ChatTransportOptions, ChatStreamEvent, TransportState, TransportStatus, TransportStateCallback, RetryOptions, } from './types.ts';
2
2
  export { parseSSE } from './parse-sse.ts';
3
3
  export { parseNDJSON } from './parse-ndjson.ts';
4
4
  export { parseJSON } from './parse-json.ts';
5
5
  export type { ChatTransport } from './create-transport.ts';
6
6
  export { detectFormat, createChatStream, createChatTransport, } from './create-transport.ts';
7
+ export type { StreamEndReason } from './classify.ts';
8
+ export { classifyStreamEnd, classifyHttpError, backoffDelay } from './classify.ts';
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"parse-ndjson.d.ts","sourceRoot":"","sources":["../../src/stream/parse-ndjson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,wBAAuB,WAAW,CAChC,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,eAAe,CAAC,CAyFjC"}
1
+ {"version":3,"file":"parse-ndjson.d.ts","sourceRoot":"","sources":["../../src/stream/parse-ndjson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,wBAAuB,WAAW,CAChC,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,eAAe,CAAC,CA2FjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"parse-sse.d.ts","sourceRoot":"","sources":["../../src/stream/parse-sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,wBAAuB,QAAQ,CAC7B,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,eAAe,CAAC,CAiIjC"}
1
+ {"version":3,"file":"parse-sse.d.ts","sourceRoot":"","sources":["../../src/stream/parse-sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,wBAAuB,QAAQ,CAC7B,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,eAAe,CAAC,CAmIjC"}
@@ -4,17 +4,36 @@ export interface ChatStreamChunk {
4
4
  role: 'assistant' | 'user';
5
5
  datetime: number;
6
6
  done: boolean;
7
+ /** True when stream ended without an explicit completion signal (e.g., no `[DONE]` sentinel). */
8
+ partial?: boolean;
7
9
  }
8
10
  export type StreamFormat = 'sse' | 'ndjson' | 'json';
11
+ export type TransportState = 'idle' | 'sending' | 'streaming' | 'retrying' | 'rate-limited' | 'auth-required' | 'server-error' | 'offline' | 'ready';
12
+ export interface TransportStatus {
13
+ state: TransportState;
14
+ statusCode?: number;
15
+ retryInMs?: number;
16
+ attempt?: number;
17
+ maxAttempts?: number;
18
+ error?: Error;
19
+ }
20
+ export type TransportStateCallback = (status: TransportStatus) => void;
21
+ export interface RetryOptions {
22
+ maxAttempts?: number;
23
+ baseDelayMs?: number;
24
+ maxDelayMs?: number;
25
+ }
9
26
  export interface ChatTransportOptions {
10
27
  baseUrl: string;
11
28
  clientId?: string;
12
29
  format?: StreamFormat;
13
30
  headers?: Record<string, string>;
14
31
  signal?: AbortSignal;
32
+ onStateChange?: TransportStateCallback;
33
+ retry?: RetryOptions;
15
34
  }
16
35
  export interface ChatStreamEvent {
17
- type: 'start' | 'chunk' | 'complete' | 'error' | 'stop';
36
+ type: 'start' | 'chunk' | 'complete' | 'partial' | 'error' | 'stop';
18
37
  chunk?: ChatStreamChunk;
19
38
  error?: Error;
20
39
  durationMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/stream/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IACxD,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/stream/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,iGAAiG;IACjG,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAIrD,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,SAAS,GACT,WAAW,GACX,UAAU,GACV,cAAc,GACd,eAAe,GACf,cAAc,GACd,SAAS,GACT,OAAO,CAAC;AAEZ,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACpE,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nonoun/native-chat",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Chat input and message layout components for @nonoun/native-ui",
5
5
  "license": "MIT",
6
6
  "type": "module",