@proveanything/smartlinks 1.8.1 → 1.8.2

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/api/ai.js CHANGED
@@ -1,19 +1,6 @@
1
- var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
2
- var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
3
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
4
- var g = generator.apply(thisArg, _arguments || []), i, q = [];
5
- return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
6
- function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
7
- function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
8
- function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
9
- function fulfill(value) { resume("next", value); }
10
- function reject(value) { resume("throw", value); }
11
- function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
12
- };
13
1
  // src/api/ai.ts
14
2
  // AI endpoints: public and admin helpers
15
- import { post, request, del, getBaseURL, getApiHeaders } from "../http";
16
- import { SmartlinksApiError } from "../types/error";
3
+ import { post, request, del, requestStream } from "../http";
17
4
  function encodeQueryParams(params) {
18
5
  if (!params)
19
6
  return '';
@@ -25,89 +12,6 @@ function encodeQueryParams(params) {
25
12
  const search = query.toString();
26
13
  return search ? `?${search}` : '';
27
14
  }
28
- async function createSseStream(path, body) {
29
- var _a, _b, _c;
30
- const baseURL = getBaseURL();
31
- if (!baseURL) {
32
- throw new Error('HTTP client is not initialized. Call initializeApi(...) first.');
33
- }
34
- const url = `${baseURL}${path}`;
35
- const headers = Object.assign({ Accept: 'text/event-stream', 'Content-Type': 'application/json' }, getApiHeaders());
36
- const response = await fetch(url, {
37
- method: 'POST',
38
- headers,
39
- body: JSON.stringify(body),
40
- });
41
- if (!response.ok) {
42
- let responseBody;
43
- try {
44
- responseBody = await response.json();
45
- }
46
- catch (_d) {
47
- responseBody = null;
48
- }
49
- const code = response.status;
50
- const message = (responseBody === null || responseBody === void 0 ? void 0 : responseBody.message) || ((_a = responseBody === null || responseBody === void 0 ? void 0 : responseBody.error) === null || _a === void 0 ? void 0 : _a.message) || `Request failed with status ${code}`;
51
- throw new SmartlinksApiError(`Error ${code}: ${message}`, code, {
52
- code,
53
- errorCode: ((_b = responseBody === null || responseBody === void 0 ? void 0 : responseBody.error) === null || _b === void 0 ? void 0 : _b.code) || (responseBody === null || responseBody === void 0 ? void 0 : responseBody.errorCode),
54
- message,
55
- details: ((_c = responseBody === null || responseBody === void 0 ? void 0 : responseBody.error) === null || _c === void 0 ? void 0 : _c.details) || (responseBody === null || responseBody === void 0 ? void 0 : responseBody.details),
56
- }, url);
57
- }
58
- if (!response.body) {
59
- throw new Error('Streaming response body is unavailable in this environment');
60
- }
61
- return parseSseStream(response.body);
62
- }
63
- function parseSseStream(stream) {
64
- return __asyncGenerator(this, arguments, function* parseSseStream_1() {
65
- const reader = stream.getReader();
66
- const decoder = new TextDecoder();
67
- let buffer = '';
68
- let dataLines = [];
69
- while (true) {
70
- const { done, value } = yield __await(reader.read());
71
- if (done)
72
- break;
73
- buffer += decoder.decode(value, { stream: true });
74
- const lines = buffer.split(/\r?\n/);
75
- buffer = lines.pop() || '';
76
- for (const rawLine of lines) {
77
- const line = rawLine.trimEnd();
78
- if (!line) {
79
- if (!dataLines.length)
80
- continue;
81
- const payload = dataLines.join('\n');
82
- dataLines = [];
83
- if (payload === '[DONE]')
84
- return yield __await(void 0);
85
- try {
86
- yield yield __await(JSON.parse(payload));
87
- }
88
- catch (_a) {
89
- continue;
90
- }
91
- continue;
92
- }
93
- if (line.startsWith('data:')) {
94
- dataLines.push(line.slice(5).trimStart());
95
- }
96
- }
97
- }
98
- if (dataLines.length) {
99
- const payload = dataLines.join('\n');
100
- if (payload !== '[DONE]') {
101
- try {
102
- yield yield __await(JSON.parse(payload));
103
- }
104
- catch (_b) {
105
- return yield __await(void 0);
106
- }
107
- }
108
- }
109
- });
110
- }
111
15
  var aiInternal;
112
16
  (function (aiInternal) {
113
17
  // ============================================================================
@@ -126,7 +30,7 @@ var aiInternal;
126
30
  async function create(collectionId, request) {
127
31
  const path = `/admin/collection/${encodeURIComponent(collectionId)}/ai/v1/responses`;
128
32
  if (request.stream) {
129
- return createSseStream(path, request);
33
+ return requestStream(path, { method: 'POST', body: request });
130
34
  }
131
35
  return post(path, request);
132
36
  }
@@ -143,7 +47,7 @@ var aiInternal;
143
47
  async function create(collectionId, request) {
144
48
  const path = `/admin/collection/${encodeURIComponent(collectionId)}/ai/v1/chat/completions`;
145
49
  if (request.stream) {
146
- return createSseStream(path, request);
50
+ return requestStream(path, { method: 'POST', body: request });
147
51
  }
148
52
  return post(path, request);
149
53
  }
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.8.1 | Generated: 2026-03-14T09:21:59.178Z
3
+ Version: 1.8.2 | Generated: 2026-03-14T10:21:47.666Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -166,6 +166,14 @@ Internal helper that performs a PATCH request to `${baseURL}${path}`, injecting
166
166
  options: RequestInit) → `Promise<T>`
167
167
  Internal helper that performs a request to `${baseURL}${path}` with custom options, injecting headers for apiKey or bearerToken if present. Returns the parsed JSON as T, or throws an Error.
168
168
 
169
+ **requestStream**(path: string,
170
+ options?: {
171
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
172
+ body?: any
173
+ headers?: Record<string, string>
174
+ }) → `Promise<AsyncIterable<T>>`
175
+ Internal helper that performs a streaming request using the shared auth and proxy transport. The response is expected to be `text/event-stream` with JSON payloads in `data:` frames.
176
+
169
177
  **del**(path: string,
170
178
  extraHeaders?: Record<string, string>) → `Promise<T>`
171
179
  Internal helper that performs a DELETE request to `${baseURL}${path}`, injecting headers for apiKey or bearerToken if present. Returns the parsed JSON as T, or throws an Error.
@@ -4205,6 +4213,38 @@ interface ProxyResponse {
4205
4213
  }
4206
4214
  ```
4207
4215
 
4216
+ **ProxyStreamRequest** (interface)
4217
+ ```typescript
4218
+ interface ProxyStreamRequest {
4219
+ _smartlinksProxyStreamRequest: true;
4220
+ id: string;
4221
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
4222
+ path: string;
4223
+ body?: any;
4224
+ headers?: Record<string, string>;
4225
+ }
4226
+ ```
4227
+
4228
+ **ProxyStreamAbortMessage** (interface)
4229
+ ```typescript
4230
+ interface ProxyStreamAbortMessage {
4231
+ _smartlinksProxyStreamAbort: true;
4232
+ id: string;
4233
+ }
4234
+ ```
4235
+
4236
+ **ProxyStreamMessage** (interface)
4237
+ ```typescript
4238
+ interface ProxyStreamMessage {
4239
+ _smartlinksProxyStream: true;
4240
+ id: string;
4241
+ phase: 'open' | 'event' | 'end' | 'error';
4242
+ data?: any;
4243
+ error?: string;
4244
+ status?: number;
4245
+ }
4246
+ ```
4247
+
4208
4248
  **UploadStartMessage** (interface)
4209
4249
  ```typescript
4210
4250
  interface UploadStartMessage {
@@ -0,0 +1,308 @@
1
+ # Iframe Streaming Parent Changes
2
+
3
+ This note describes the parent-side changes needed to support AI streaming when an embedded SmartLinks app is running in iframe proxy mode.
4
+
5
+ If you are using the SDK `IframeResponder` directly, this is already implemented in the SDK changes. You only need this document if your parent application has its own iframe proxy handler and does not rely on `IframeResponder`.
6
+
7
+ ## Goal
8
+
9
+ Keep the existing architecture:
10
+
11
+ - local mode: child calls API directly
12
+ - iframe proxy mode: child never owns auth state and streams through the parent
13
+
14
+ This keeps user/session authority in the parent while making AI streaming behave like the rest of the SDK transport.
15
+
16
+ ## What changed
17
+
18
+ Previously, proxy mode only supported one-shot request/response messages:
19
+
20
+ - `_smartlinksProxyRequest`
21
+ - `_smartlinksProxyResponse`
22
+
23
+ Streaming now adds a second protocol for long-lived responses:
24
+
25
+ - `_smartlinksProxyStreamRequest`
26
+ - `_smartlinksProxyStream`
27
+ - `_smartlinksProxyStreamAbort`
28
+
29
+ ## New parent message handling
30
+
31
+ ### 1. Listen for stream requests
32
+
33
+ The iframe child may now send this message:
34
+
35
+ ```ts
36
+ {
37
+ _smartlinksProxyStreamRequest: true,
38
+ id: string,
39
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
40
+ path: string,
41
+ body?: any,
42
+ headers?: Record<string, string>
43
+ }
44
+ ```
45
+
46
+ Parent behavior:
47
+
48
+ - treat this like a proxied API request
49
+ - build the real API URL from your configured base URL plus `path`
50
+ - send the request using the parent's current auth/session context
51
+ - expect an SSE / streaming response body
52
+ - keep the request open until the stream ends or is aborted
53
+
54
+ ### 2. Forward stream lifecycle messages back to the child
55
+
56
+ The parent should send messages back to the iframe using this envelope:
57
+
58
+ ```ts
59
+ {
60
+ _smartlinksProxyStream: true,
61
+ id: string,
62
+ phase: 'open' | 'event' | 'end' | 'error',
63
+ data?: any,
64
+ error?: string,
65
+ status?: number
66
+ }
67
+ ```
68
+
69
+ Phases:
70
+
71
+ - `open`
72
+ - optional but recommended
73
+ - indicates the upstream streaming request was accepted and a body exists
74
+ - `event`
75
+ - contains one parsed JSON event from an SSE `data:` frame
76
+ - send one message per logical event payload
77
+ - `end`
78
+ - sent once when the stream finishes normally
79
+ - `error`
80
+ - sent if the upstream request fails before or during streaming
81
+
82
+ ### 3. Support abort from the child
83
+
84
+ The child may stop reading early and send:
85
+
86
+ ```ts
87
+ {
88
+ _smartlinksProxyStreamAbort: true,
89
+ id: string
90
+ }
91
+ ```
92
+
93
+ Parent behavior:
94
+
95
+ - look up the active stream by `id`
96
+ - abort the underlying fetch / reader
97
+ - clean up any local state for that stream
98
+ - do not keep streaming after abort
99
+
100
+ ## SSE forwarding rules
101
+
102
+ The upstream AI endpoints return SSE-like frames. The parent should:
103
+
104
+ - read the response body as a stream
105
+ - buffer text until line boundaries
106
+ - collect `data:` lines for a single event
107
+ - join multi-line `data:` payloads with `\n`
108
+ - ignore blank events
109
+ - stop on `data: [DONE]`
110
+ - JSON-parse each event payload
111
+ - forward parsed payloads to the iframe as `_smartlinksProxyStream` with `phase: 'event'`
112
+
113
+ Minimal parsing behavior:
114
+
115
+ 1. accumulate bytes into text
116
+ 2. split on `\r?\n`
117
+ 3. collect each `data:` line
118
+ 4. on blank line, finalize the event
119
+ 5. if payload is `[DONE]`, finish
120
+ 6. otherwise `JSON.parse(payload)` and forward
121
+
122
+ ## Auth and session expectations
123
+
124
+ The parent remains the source of truth for auth.
125
+
126
+ That means the parent stream handler should:
127
+
128
+ - use the same auth headers/token source as normal proxied requests
129
+ - not require the iframe to know the bearer token or API key
130
+ - naturally pick up the current logged-in user when the stream starts
131
+ - cancel active streams if your app invalidates session state on logout or account switch
132
+
133
+ In practice, the stream request should use the same header-building logic as your normal parent proxy transport.
134
+
135
+ ## Error handling expectations
136
+
137
+ If the upstream fetch returns a non-2xx status:
138
+
139
+ - try to read the JSON error body
140
+ - derive a useful message
141
+ - send one `_smartlinksProxyStream` message with `phase: 'error'`
142
+ - include `status` when available
143
+ - do not send `end` afterward
144
+
145
+ If the stream body is missing unexpectedly:
146
+
147
+ - send `phase: 'error'`
148
+
149
+ If JSON parsing fails for a single event chunk:
150
+
151
+ - safest behavior is to ignore that malformed chunk and continue
152
+
153
+ ## State the parent should keep
154
+
155
+ Track active streams in a map keyed by `id`:
156
+
157
+ ```ts
158
+ Map<string, AbortController>
159
+ ```
160
+
161
+ Recommended cleanup points:
162
+
163
+ - on normal stream end
164
+ - on error
165
+ - on child abort
166
+ - on iframe detach/unmount
167
+ - on parent auth reset/logout if you want all in-flight streams cancelled immediately
168
+
169
+ ## Parent implementation outline
170
+
171
+ ```ts
172
+ const activeStreams = new Map<string, AbortController>()
173
+
174
+ window.addEventListener('message', async (event) => {
175
+ const msg = event.data
176
+
177
+ if (msg?._smartlinksProxyStreamAbort && msg.id) {
178
+ activeStreams.get(msg.id)?.abort()
179
+ activeStreams.delete(msg.id)
180
+ return
181
+ }
182
+
183
+ if (msg?._smartlinksProxyStreamRequest && msg.id) {
184
+ const controller = new AbortController()
185
+ activeStreams.set(msg.id, controller)
186
+
187
+ try {
188
+ const response = await fetch(buildUrl(msg.path), {
189
+ method: msg.method,
190
+ headers: msg.headers,
191
+ body: msg.body ? JSON.stringify(msg.body) : undefined,
192
+ signal: controller.signal,
193
+ })
194
+
195
+ if (!response.ok || !response.body) {
196
+ postError(...)
197
+ return
198
+ }
199
+
200
+ postOpen(...)
201
+ await forwardSse(response.body, parsed => postEvent(...parsed))
202
+ postEnd(...)
203
+ } catch (err) {
204
+ if (err?.name !== 'AbortError') postError(...)
205
+ } finally {
206
+ activeStreams.delete(msg.id)
207
+ }
208
+ }
209
+ })
210
+ ```
211
+
212
+ ## Exact protocol summary
213
+
214
+ ### Child → parent
215
+
216
+ Standard stream request:
217
+
218
+ ```ts
219
+ {
220
+ _smartlinksProxyStreamRequest: true,
221
+ id,
222
+ method,
223
+ path,
224
+ body,
225
+ headers
226
+ }
227
+ ```
228
+
229
+ Abort request:
230
+
231
+ ```ts
232
+ {
233
+ _smartlinksProxyStreamAbort: true,
234
+ id
235
+ }
236
+ ```
237
+
238
+ ### Parent → child
239
+
240
+ Open:
241
+
242
+ ```ts
243
+ {
244
+ _smartlinksProxyStream: true,
245
+ id,
246
+ phase: 'open'
247
+ }
248
+ ```
249
+
250
+ Event:
251
+
252
+ ```ts
253
+ {
254
+ _smartlinksProxyStream: true,
255
+ id,
256
+ phase: 'event',
257
+ data: parsedJsonEvent
258
+ }
259
+ ```
260
+
261
+ End:
262
+
263
+ ```ts
264
+ {
265
+ _smartlinksProxyStream: true,
266
+ id,
267
+ phase: 'end'
268
+ }
269
+ ```
270
+
271
+ Error:
272
+
273
+ ```ts
274
+ {
275
+ _smartlinksProxyStream: true,
276
+ id,
277
+ phase: 'error',
278
+ error: 'message',
279
+ status?: number
280
+ }
281
+ ```
282
+
283
+ ## What does not change
284
+
285
+ These parts of the parent iframe integration stay the same:
286
+
287
+ - normal `_smartlinksProxyRequest` request/response flow
288
+ - upload proxy flow
289
+ - auth login/logout postMessage handling
290
+ - route/deep-link handling
291
+ - resize handling
292
+
293
+ This is an additive protocol, not a replacement.
294
+
295
+ ## Current SDK reference
296
+
297
+ The SDK implementation lives in:
298
+
299
+ - [src/http.ts](src/http.ts)
300
+ - [src/iframeResponder.ts](src/iframeResponder.ts)
301
+ - [src/types/iframeResponder.ts](src/types/iframeResponder.ts)
302
+ - [src/api/ai.ts](src/api/ai.ts)
303
+
304
+ ## Practical recommendation
305
+
306
+ If your parent already uses `IframeResponder`, prefer upgrading to the SDK version with these changes instead of re-implementing the protocol manually.
307
+
308
+ If your parent has a custom iframe bridge, implement exactly the three new message types above and reuse your existing auth/header logic from normal proxied requests.
package/dist/http.d.ts CHANGED
@@ -167,6 +167,15 @@ export declare function patch<T>(path: string, body: any, extraHeaders?: Record<
167
167
  * Returns the parsed JSON as T, or throws an Error.
168
168
  */
169
169
  export declare function requestWithOptions<T>(path: string, options: RequestInit): Promise<T>;
170
+ /**
171
+ * Internal helper that performs a streaming request using the shared auth and proxy transport.
172
+ * The response is expected to be `text/event-stream` with JSON payloads in `data:` frames.
173
+ */
174
+ export declare function requestStream<T>(path: string, options?: {
175
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
176
+ body?: any;
177
+ headers?: Record<string, string>;
178
+ }): Promise<AsyncIterable<T>>;
170
179
  /**
171
180
  * Internal helper that performs a DELETE request to `${baseURL}${path}`,
172
181
  * injecting headers for apiKey or bearerToken if present.