@semiont/api-client 0.4.13 → 0.4.15
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/README.md +56 -128
- package/dist/index.d.ts +736 -627
- package/dist/index.js +804 -1218
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
|
-
import ky from 'ky';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import ky, { HTTPError } from 'ky';
|
|
2
|
+
import { BehaviorSubject, Observable, merge } from 'rxjs';
|
|
3
|
+
import { map, distinctUntilChanged, filter, takeUntil } from 'rxjs/operators';
|
|
4
|
+
import { annotationId, resourceId, email, googleCredential, refreshToken } from '@semiont/core';
|
|
4
5
|
export { getFragmentSelector, getSvgSelector, getTextPositionSelector, validateSvgMarkup } from '@semiont/core';
|
|
5
|
-
import { map, distinctUntilChanged, filter, take, timeout } from 'rxjs/operators';
|
|
6
6
|
|
|
7
7
|
// src/client.ts
|
|
8
8
|
|
|
9
9
|
// src/sse/stream.ts
|
|
10
|
-
var DOMAIN_EVENT_TO_UI_CHANNEL = {
|
|
11
|
-
"annotation.added": "mark:added",
|
|
12
|
-
"annotation.removed": "mark:removed",
|
|
13
|
-
"annotation.body.updated": "mark:body-updated",
|
|
14
|
-
"resource.archived": "mark:archived",
|
|
15
|
-
"resource.unarchived": "mark:unarchived",
|
|
16
|
-
"entitytag.added": "mark:entity-tag-added",
|
|
17
|
-
"entitytag.removed": "mark:entity-tag-removed"
|
|
18
|
-
};
|
|
19
10
|
function createSSEStream(url, fetchOptions, config, logger) {
|
|
20
|
-
|
|
11
|
+
let abortController = new AbortController();
|
|
21
12
|
let closed = false;
|
|
13
|
+
let lastEventId = null;
|
|
14
|
+
const RECONNECT_INITIAL_MS = 1e3;
|
|
15
|
+
const RECONNECT_MAX_MS = 3e4;
|
|
16
|
+
let reconnectDelayMs = RECONNECT_INITIAL_MS;
|
|
22
17
|
const connect = async () => {
|
|
23
18
|
try {
|
|
24
19
|
logger?.debug("SSE Stream Request", {
|
|
@@ -27,13 +22,17 @@ function createSSEStream(url, fetchOptions, config, logger) {
|
|
|
27
22
|
method: fetchOptions.method || "GET",
|
|
28
23
|
timestamp: Date.now()
|
|
29
24
|
});
|
|
25
|
+
const headers = {
|
|
26
|
+
...fetchOptions.headers,
|
|
27
|
+
"Accept": "text/event-stream"
|
|
28
|
+
};
|
|
29
|
+
if (lastEventId !== null) {
|
|
30
|
+
headers["Last-Event-ID"] = lastEventId;
|
|
31
|
+
}
|
|
30
32
|
const response = await fetch(url, {
|
|
31
33
|
...fetchOptions,
|
|
32
34
|
signal: abortController.signal,
|
|
33
|
-
headers
|
|
34
|
-
...fetchOptions.headers,
|
|
35
|
-
"Accept": "text/event-stream"
|
|
36
|
-
}
|
|
35
|
+
headers
|
|
37
36
|
});
|
|
38
37
|
if (!response.ok) {
|
|
39
38
|
const errorData = await response.json().catch(() => ({}));
|
|
@@ -117,10 +116,13 @@ function createSSEStream(url, fetchOptions, config, logger) {
|
|
|
117
116
|
}
|
|
118
117
|
}
|
|
119
118
|
};
|
|
120
|
-
const handleEvent = (eventType, data,
|
|
119
|
+
const handleEvent = (eventType, data, id) => {
|
|
121
120
|
if (data.startsWith(":")) {
|
|
122
121
|
return;
|
|
123
122
|
}
|
|
123
|
+
if (id) {
|
|
124
|
+
lastEventId = id;
|
|
125
|
+
}
|
|
124
126
|
try {
|
|
125
127
|
const parsed = JSON.parse(data);
|
|
126
128
|
logger?.debug("SSE Event Received", {
|
|
@@ -129,13 +131,8 @@ function createSSEStream(url, fetchOptions, config, logger) {
|
|
|
129
131
|
event: eventType || "message",
|
|
130
132
|
hasData: !!data
|
|
131
133
|
});
|
|
132
|
-
if (typeof parsed === "object" && parsed !== null && "
|
|
134
|
+
if (typeof parsed === "object" && parsed !== null && "metadata" in parsed) {
|
|
133
135
|
config.eventBus.get(eventType).next(parsed);
|
|
134
|
-
const uiChannel = DOMAIN_EVENT_TO_UI_CHANNEL[parsed.type];
|
|
135
|
-
if (uiChannel) {
|
|
136
|
-
config.eventBus.get(uiChannel).next(parsed);
|
|
137
|
-
}
|
|
138
|
-
config.eventBus.get("make-meaning:event").next(parsed);
|
|
139
136
|
return;
|
|
140
137
|
}
|
|
141
138
|
config.eventBus.get(eventType).next(parsed);
|
|
@@ -150,9 +147,33 @@ function createSSEStream(url, fetchOptions, config, logger) {
|
|
|
150
147
|
logger?.error("SSE Failed to parse event data", { error, eventType, data });
|
|
151
148
|
}
|
|
152
149
|
};
|
|
153
|
-
|
|
150
|
+
const runConnect = async () => {
|
|
151
|
+
if (!config.reconnect) {
|
|
152
|
+
return connect();
|
|
153
|
+
}
|
|
154
|
+
while (!closed) {
|
|
155
|
+
abortController = new AbortController();
|
|
156
|
+
try {
|
|
157
|
+
await connect();
|
|
158
|
+
if (closed) return;
|
|
159
|
+
logger?.info("SSE Stream ended cleanly; reconnecting", { url });
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (closed) return;
|
|
162
|
+
if (error instanceof Error && error.name === "AbortError") return;
|
|
163
|
+
logger?.warn("SSE Stream errored; reconnecting", {
|
|
164
|
+
url,
|
|
165
|
+
error: error instanceof Error ? error.message : String(error),
|
|
166
|
+
delayMs: reconnectDelayMs
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
await new Promise((resolve) => setTimeout(resolve, reconnectDelayMs));
|
|
170
|
+
reconnectDelayMs = Math.min(reconnectDelayMs * 2, RECONNECT_MAX_MS);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
void runConnect();
|
|
154
174
|
return {
|
|
155
175
|
close() {
|
|
176
|
+
closed = true;
|
|
156
177
|
abortController.abort();
|
|
157
178
|
}
|
|
158
179
|
};
|
|
@@ -179,459 +200,6 @@ var SSEClient = class {
|
|
|
179
200
|
}
|
|
180
201
|
return headers;
|
|
181
202
|
}
|
|
182
|
-
/**
|
|
183
|
-
* Detect annotations in a resource (streaming)
|
|
184
|
-
*
|
|
185
|
-
* Streams entity detection progress via Server-Sent Events.
|
|
186
|
-
*
|
|
187
|
-
* @param resourceId - Resource URI or ID
|
|
188
|
-
* @param request - Detection configuration (entity types to detect)
|
|
189
|
-
* @param options - Request options (auth token)
|
|
190
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
191
|
-
*
|
|
192
|
-
* @example
|
|
193
|
-
* ```typescript
|
|
194
|
-
* const stream = sseClient.markReferences(
|
|
195
|
-
* 'http://localhost:4000/resources/doc-123',
|
|
196
|
-
* { entityTypes: ['Person', 'Organization'] },
|
|
197
|
-
* { auth: 'your-token' }
|
|
198
|
-
* );
|
|
199
|
-
*
|
|
200
|
-
* stream.onProgress((progress) => {
|
|
201
|
-
* console.log(`Scanning: ${progress.currentEntityType}`);
|
|
202
|
-
* console.log(`Progress: ${progress.processedEntityTypes}/${progress.totalEntityTypes}`);
|
|
203
|
-
* });
|
|
204
|
-
*
|
|
205
|
-
* stream.onComplete((result) => {
|
|
206
|
-
* console.log(`Detection complete! Found ${result.foundCount} entities`);
|
|
207
|
-
* });
|
|
208
|
-
*
|
|
209
|
-
* stream.onError((error) => {
|
|
210
|
-
* console.error('Detection failed:', error.message);
|
|
211
|
-
* });
|
|
212
|
-
*
|
|
213
|
-
* // Cleanup when done
|
|
214
|
-
* stream.close();
|
|
215
|
-
* ```
|
|
216
|
-
*/
|
|
217
|
-
markReferences(resourceId, request, options) {
|
|
218
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotate-references-stream`;
|
|
219
|
-
return createSSEStream(
|
|
220
|
-
url,
|
|
221
|
-
{
|
|
222
|
-
method: "POST",
|
|
223
|
-
headers: this.getHeaders(options.auth),
|
|
224
|
-
body: JSON.stringify(request)
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
progressEvents: ["mark:progress"],
|
|
228
|
-
completeEvent: "mark:assist-finished",
|
|
229
|
-
errorEvent: "mark:assist-failed",
|
|
230
|
-
eventBus: options.eventBus,
|
|
231
|
-
eventPrefix: void 0
|
|
232
|
-
},
|
|
233
|
-
this.logger
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Generate resource from annotation (streaming)
|
|
238
|
-
*
|
|
239
|
-
* Streams resource generation progress via Server-Sent Events.
|
|
240
|
-
*
|
|
241
|
-
* @param resourceId - Source resource URI or ID
|
|
242
|
-
* @param annotationId - Annotation URI or ID to use as generation source
|
|
243
|
-
* @param request - Generation options (title, prompt, language)
|
|
244
|
-
* @param options - Request options (auth token)
|
|
245
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
246
|
-
*
|
|
247
|
-
* @example
|
|
248
|
-
* ```typescript
|
|
249
|
-
* const stream = sseClient.yieldResource(
|
|
250
|
-
* 'http://localhost:4000/resources/doc-123',
|
|
251
|
-
* 'http://localhost:4000/annotations/ann-456',
|
|
252
|
-
* { language: 'es', title: 'Spanish Summary' },
|
|
253
|
-
* { auth: 'your-token' }
|
|
254
|
-
* );
|
|
255
|
-
*
|
|
256
|
-
* stream.onProgress((progress) => {
|
|
257
|
-
* console.log(`${progress.status}: ${progress.percentage}%`);
|
|
258
|
-
* console.log(progress.message);
|
|
259
|
-
* });
|
|
260
|
-
*
|
|
261
|
-
* stream.onComplete((result) => {
|
|
262
|
-
* console.log(`Yielded resource: ${result.resourceId}`);
|
|
263
|
-
* });
|
|
264
|
-
*
|
|
265
|
-
* stream.onError((error) => {
|
|
266
|
-
* console.error('Yield failed:', error.message);
|
|
267
|
-
* });
|
|
268
|
-
*
|
|
269
|
-
* // Cleanup when done
|
|
270
|
-
* stream.close();
|
|
271
|
-
* ```
|
|
272
|
-
*/
|
|
273
|
-
yieldResource(resourceId, annotationId, request, options) {
|
|
274
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/yield-resource-stream`;
|
|
275
|
-
return createSSEStream(
|
|
276
|
-
url,
|
|
277
|
-
{
|
|
278
|
-
method: "POST",
|
|
279
|
-
headers: this.getHeaders(options.auth),
|
|
280
|
-
body: JSON.stringify(request)
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
progressEvents: ["yield:progress"],
|
|
284
|
-
completeEvent: "yield:finished",
|
|
285
|
-
errorEvent: "yield:failed",
|
|
286
|
-
eventBus: options.eventBus,
|
|
287
|
-
eventPrefix: void 0
|
|
288
|
-
},
|
|
289
|
-
this.logger
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Detect highlights in a resource (streaming)
|
|
294
|
-
*
|
|
295
|
-
* Streams highlight annotation progress via Server-Sent Events.
|
|
296
|
-
*
|
|
297
|
-
* @param resourceId - Resource URI or ID
|
|
298
|
-
* @param request - Detection configuration (optional instructions)
|
|
299
|
-
* @param options - Request options (auth token)
|
|
300
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
301
|
-
*
|
|
302
|
-
* @example
|
|
303
|
-
* ```typescript
|
|
304
|
-
* const stream = sseClient.markHighlights(
|
|
305
|
-
* 'http://localhost:4000/resources/doc-123',
|
|
306
|
-
* { instructions: 'Focus on key technical points' },
|
|
307
|
-
* { auth: 'your-token' }
|
|
308
|
-
* );
|
|
309
|
-
*
|
|
310
|
-
* stream.onProgress((progress) => {
|
|
311
|
-
* console.log(`${progress.status}: ${progress.percentage}%`);
|
|
312
|
-
* console.log(progress.message);
|
|
313
|
-
* });
|
|
314
|
-
*
|
|
315
|
-
* stream.onComplete((result) => {
|
|
316
|
-
* console.log(`Detection complete! Created ${result.createdCount} highlights`);
|
|
317
|
-
* });
|
|
318
|
-
*
|
|
319
|
-
* stream.onError((error) => {
|
|
320
|
-
* console.error('Detection failed:', error.message);
|
|
321
|
-
* });
|
|
322
|
-
*
|
|
323
|
-
* // Cleanup when done
|
|
324
|
-
* stream.close();
|
|
325
|
-
* ```
|
|
326
|
-
*/
|
|
327
|
-
markHighlights(resourceId, request = {}, options) {
|
|
328
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotate-highlights-stream`;
|
|
329
|
-
return createSSEStream(
|
|
330
|
-
url,
|
|
331
|
-
{
|
|
332
|
-
method: "POST",
|
|
333
|
-
headers: this.getHeaders(options.auth),
|
|
334
|
-
body: JSON.stringify(request)
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
progressEvents: ["mark:progress"],
|
|
338
|
-
completeEvent: "mark:assist-finished",
|
|
339
|
-
errorEvent: "mark:assist-failed",
|
|
340
|
-
eventBus: options.eventBus,
|
|
341
|
-
eventPrefix: void 0
|
|
342
|
-
},
|
|
343
|
-
this.logger
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Detect assessments in a resource (streaming)
|
|
348
|
-
*
|
|
349
|
-
* Streams assessment annotation progress via Server-Sent Events.
|
|
350
|
-
*
|
|
351
|
-
* @param resourceId - Resource URI or ID
|
|
352
|
-
* @param request - Detection configuration (optional instructions)
|
|
353
|
-
* @param options - Request options (auth token)
|
|
354
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
355
|
-
*
|
|
356
|
-
* @example
|
|
357
|
-
* ```typescript
|
|
358
|
-
* const stream = sseClient.markAssessments(
|
|
359
|
-
* 'http://localhost:4000/resources/doc-123',
|
|
360
|
-
* { instructions: 'Evaluate claims for accuracy' },
|
|
361
|
-
* { auth: 'your-token' }
|
|
362
|
-
* );
|
|
363
|
-
*
|
|
364
|
-
* stream.onProgress((progress) => {
|
|
365
|
-
* console.log(`${progress.status}: ${progress.percentage}%`);
|
|
366
|
-
* console.log(progress.message);
|
|
367
|
-
* });
|
|
368
|
-
*
|
|
369
|
-
* stream.onComplete((result) => {
|
|
370
|
-
* console.log(`Detection complete! Created ${result.createdCount} assessments`);
|
|
371
|
-
* });
|
|
372
|
-
*
|
|
373
|
-
* stream.onError((error) => {
|
|
374
|
-
* console.error('Detection failed:', error.message);
|
|
375
|
-
* });
|
|
376
|
-
*
|
|
377
|
-
* // Cleanup when done
|
|
378
|
-
* stream.close();
|
|
379
|
-
* ```
|
|
380
|
-
*/
|
|
381
|
-
markAssessments(resourceId, request = {}, options) {
|
|
382
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotate-assessments-stream`;
|
|
383
|
-
return createSSEStream(
|
|
384
|
-
url,
|
|
385
|
-
{
|
|
386
|
-
method: "POST",
|
|
387
|
-
headers: this.getHeaders(options.auth),
|
|
388
|
-
body: JSON.stringify(request)
|
|
389
|
-
},
|
|
390
|
-
{
|
|
391
|
-
progressEvents: ["mark:progress"],
|
|
392
|
-
completeEvent: "mark:assist-finished",
|
|
393
|
-
errorEvent: "mark:assist-failed",
|
|
394
|
-
eventBus: options.eventBus,
|
|
395
|
-
eventPrefix: void 0
|
|
396
|
-
},
|
|
397
|
-
this.logger
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Detect comments in a resource (streaming)
|
|
402
|
-
*
|
|
403
|
-
* Streams comment annotation progress via Server-Sent Events.
|
|
404
|
-
* Uses AI to identify passages that would benefit from explanatory comments
|
|
405
|
-
* and creates comment annotations with contextual information.
|
|
406
|
-
*
|
|
407
|
-
* @param resourceId - Resource URI or ID
|
|
408
|
-
* @param request - Detection configuration (optional instructions and tone)
|
|
409
|
-
* @param options - Request options (auth token)
|
|
410
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
411
|
-
*
|
|
412
|
-
* @example
|
|
413
|
-
* ```typescript
|
|
414
|
-
* const stream = sseClient.markComments(
|
|
415
|
-
* 'http://localhost:4000/resources/doc-123',
|
|
416
|
-
* {
|
|
417
|
-
* instructions: 'Focus on technical terminology',
|
|
418
|
-
* tone: 'scholarly'
|
|
419
|
-
* },
|
|
420
|
-
* { auth: 'your-token' }
|
|
421
|
-
* );
|
|
422
|
-
*
|
|
423
|
-
* stream.onProgress((progress) => {
|
|
424
|
-
* console.log(`${progress.status}: ${progress.percentage}%`);
|
|
425
|
-
* });
|
|
426
|
-
*
|
|
427
|
-
* stream.onComplete((result) => {
|
|
428
|
-
* console.log(`Detection complete! Created ${result.createdCount} comments`);
|
|
429
|
-
* });
|
|
430
|
-
*
|
|
431
|
-
* stream.onError((error) => {
|
|
432
|
-
* console.error('Detection failed:', error.message);
|
|
433
|
-
* });
|
|
434
|
-
*
|
|
435
|
-
* // Cleanup when done
|
|
436
|
-
* stream.close();
|
|
437
|
-
* ```
|
|
438
|
-
*/
|
|
439
|
-
markComments(resourceId, request = {}, options) {
|
|
440
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotate-comments-stream`;
|
|
441
|
-
return createSSEStream(
|
|
442
|
-
url,
|
|
443
|
-
{
|
|
444
|
-
method: "POST",
|
|
445
|
-
headers: this.getHeaders(options.auth),
|
|
446
|
-
body: JSON.stringify(request)
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
progressEvents: ["mark:progress"],
|
|
450
|
-
completeEvent: "mark:assist-finished",
|
|
451
|
-
errorEvent: "mark:assist-failed",
|
|
452
|
-
eventBus: options.eventBus,
|
|
453
|
-
eventPrefix: void 0
|
|
454
|
-
},
|
|
455
|
-
this.logger
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Detect tags in a resource (streaming)
|
|
460
|
-
*
|
|
461
|
-
* Streams tag annotation progress via Server-Sent Events.
|
|
462
|
-
* Uses AI to identify passages serving specific structural roles
|
|
463
|
-
* (e.g., IRAC, IMRAD, Toulmin) and creates tag annotations with dual-body structure.
|
|
464
|
-
*
|
|
465
|
-
* @param resourceId - Resource URI or ID
|
|
466
|
-
* @param request - Detection configuration (schema and categories to detect)
|
|
467
|
-
* @param options - Request options (auth token)
|
|
468
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
469
|
-
*
|
|
470
|
-
* @example
|
|
471
|
-
* ```typescript
|
|
472
|
-
* const stream = sseClient.markTags(
|
|
473
|
-
* 'http://localhost:4000/resources/doc-123',
|
|
474
|
-
* {
|
|
475
|
-
* schemaId: 'legal-irac',
|
|
476
|
-
* categories: ['Issue', 'Rule', 'Application', 'Conclusion']
|
|
477
|
-
* },
|
|
478
|
-
* { auth: 'your-token' }
|
|
479
|
-
* );
|
|
480
|
-
*
|
|
481
|
-
* stream.onProgress((progress) => {
|
|
482
|
-
* console.log(`${progress.status}: ${progress.percentage}%`);
|
|
483
|
-
* console.log(`Processing ${progress.currentCategory}...`);
|
|
484
|
-
* });
|
|
485
|
-
*
|
|
486
|
-
* stream.onComplete((result) => {
|
|
487
|
-
* console.log(`Detection complete! Created ${result.tagsCreated} tags`);
|
|
488
|
-
* });
|
|
489
|
-
*
|
|
490
|
-
* stream.onError((error) => {
|
|
491
|
-
* console.error('Detection failed:', error.message);
|
|
492
|
-
* });
|
|
493
|
-
*
|
|
494
|
-
* // Cleanup when done
|
|
495
|
-
* stream.close();
|
|
496
|
-
* ```
|
|
497
|
-
*/
|
|
498
|
-
markTags(resourceId, request, options) {
|
|
499
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotate-tags-stream`;
|
|
500
|
-
return createSSEStream(
|
|
501
|
-
url,
|
|
502
|
-
{
|
|
503
|
-
method: "POST",
|
|
504
|
-
headers: this.getHeaders(options.auth),
|
|
505
|
-
body: JSON.stringify(request)
|
|
506
|
-
},
|
|
507
|
-
{
|
|
508
|
-
progressEvents: ["mark:progress"],
|
|
509
|
-
completeEvent: "mark:assist-finished",
|
|
510
|
-
errorEvent: "mark:assist-failed",
|
|
511
|
-
eventBus: options.eventBus,
|
|
512
|
-
eventPrefix: void 0
|
|
513
|
-
},
|
|
514
|
-
this.logger
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Gather LLM context for a resource (streaming)
|
|
519
|
-
*
|
|
520
|
-
* Streams resource LLM context gathering progress via Server-Sent Events.
|
|
521
|
-
*
|
|
522
|
-
* @param resourceId - Resource URI or ID
|
|
523
|
-
* @param request - Gather configuration (depth, maxResources, includeContent, includeSummary)
|
|
524
|
-
* @param options - Request options (auth token, eventBus)
|
|
525
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
526
|
-
*/
|
|
527
|
-
gatherResource(resourceId, request, options) {
|
|
528
|
-
const url = `${this.baseUrl}/resources/${resourceId}/gather-resource-stream`;
|
|
529
|
-
return createSSEStream(
|
|
530
|
-
url,
|
|
531
|
-
{
|
|
532
|
-
method: "POST",
|
|
533
|
-
headers: this.getHeaders(options.auth),
|
|
534
|
-
body: JSON.stringify(request)
|
|
535
|
-
},
|
|
536
|
-
{
|
|
537
|
-
progressEvents: ["gather:progress"],
|
|
538
|
-
completeEvent: "gather:finished",
|
|
539
|
-
errorEvent: "gather:failed",
|
|
540
|
-
eventBus: options.eventBus,
|
|
541
|
-
eventPrefix: void 0
|
|
542
|
-
},
|
|
543
|
-
this.logger
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Gather LLM context for an annotation (streaming)
|
|
548
|
-
*
|
|
549
|
-
* Streams annotation LLM context gathering progress via Server-Sent Events.
|
|
550
|
-
*
|
|
551
|
-
* @param resourceId - Resource URI or ID
|
|
552
|
-
* @param annotationId - Annotation URI or ID
|
|
553
|
-
* @param request - Gather configuration (contextWindow)
|
|
554
|
-
* @param options - Request options (auth token, eventBus)
|
|
555
|
-
* @returns SSE stream controller with progress/complete/error callbacks
|
|
556
|
-
*/
|
|
557
|
-
gatherAnnotation(resourceId, annotationId, request, options) {
|
|
558
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/gather-annotation-stream`;
|
|
559
|
-
return createSSEStream(
|
|
560
|
-
url,
|
|
561
|
-
{
|
|
562
|
-
method: "POST",
|
|
563
|
-
headers: this.getHeaders(options.auth),
|
|
564
|
-
body: JSON.stringify(request)
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
progressEvents: ["gather:annotation-progress"],
|
|
568
|
-
completeEvent: "gather:annotation-finished",
|
|
569
|
-
errorEvent: "gather:failed",
|
|
570
|
-
eventBus: options.eventBus,
|
|
571
|
-
eventPrefix: void 0
|
|
572
|
-
},
|
|
573
|
-
this.logger
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Bind annotation body (streaming)
|
|
578
|
-
*
|
|
579
|
-
* Applies annotation body operations and streams completion via Server-Sent Events.
|
|
580
|
-
*
|
|
581
|
-
* @param resourceId - Resource URI or ID
|
|
582
|
-
* @param annotationId - Annotation URI or ID
|
|
583
|
-
* @param request - Bind operations (resourceId + operations array)
|
|
584
|
-
* @param options - Request options (auth token, eventBus)
|
|
585
|
-
* @returns SSE stream controller with complete/error callbacks
|
|
586
|
-
*/
|
|
587
|
-
bindAnnotation(resourceId, annotationId, request, options) {
|
|
588
|
-
const url = `${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/bind-stream`;
|
|
589
|
-
return createSSEStream(
|
|
590
|
-
url,
|
|
591
|
-
{
|
|
592
|
-
method: "POST",
|
|
593
|
-
headers: this.getHeaders(options.auth),
|
|
594
|
-
body: JSON.stringify(request)
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
progressEvents: [],
|
|
598
|
-
completeEvent: "bind:finished",
|
|
599
|
-
errorEvent: "bind:failed",
|
|
600
|
-
eventBus: options.eventBus,
|
|
601
|
-
eventPrefix: void 0
|
|
602
|
-
},
|
|
603
|
-
this.logger
|
|
604
|
-
);
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Search for binding candidates (streaming)
|
|
608
|
-
*
|
|
609
|
-
* Bridges match:search-requested to the backend Matcher actor via SSE.
|
|
610
|
-
* Results emit as match:search-results on the browser EventBus.
|
|
611
|
-
*
|
|
612
|
-
* @param resourceId - Resource the annotation belongs to
|
|
613
|
-
* @param request - Search configuration (referenceId, context, limit)
|
|
614
|
-
* @param options - Request options (auth token, eventBus)
|
|
615
|
-
* @returns SSE stream controller
|
|
616
|
-
*/
|
|
617
|
-
matchSearch(resourceId, request, options) {
|
|
618
|
-
const url = `${this.baseUrl}/resources/${resourceId}/match-search-stream`;
|
|
619
|
-
return createSSEStream(
|
|
620
|
-
url,
|
|
621
|
-
{
|
|
622
|
-
method: "POST",
|
|
623
|
-
headers: this.getHeaders(options.auth),
|
|
624
|
-
body: JSON.stringify(request)
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
progressEvents: [],
|
|
628
|
-
completeEvent: "match:search-results",
|
|
629
|
-
errorEvent: "match:search-failed",
|
|
630
|
-
eventBus: options.eventBus
|
|
631
|
-
},
|
|
632
|
-
this.logger
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
203
|
/**
|
|
636
204
|
* Subscribe to resource events (long-lived stream)
|
|
637
205
|
*
|
|
@@ -681,7 +249,8 @@ var SSEClient = class {
|
|
|
681
249
|
// Never completes (long-lived)
|
|
682
250
|
errorEvent: null,
|
|
683
251
|
// No error event (errors throw)
|
|
684
|
-
eventBus: options.eventBus
|
|
252
|
+
eventBus: options.eventBus,
|
|
253
|
+
reconnect: true
|
|
685
254
|
},
|
|
686
255
|
this.logger
|
|
687
256
|
);
|
|
@@ -706,11 +275,9 @@ var SSEClient = class {
|
|
|
706
275
|
* ```typescript
|
|
707
276
|
* const stream = sseClient.globalEvents({ auth: 'your-token', eventBus });
|
|
708
277
|
*
|
|
709
|
-
* // Events auto-emit to EventBus — subscribe there
|
|
710
|
-
* eventBus.get('
|
|
711
|
-
*
|
|
712
|
-
* // Invalidate entity types query
|
|
713
|
-
* }
|
|
278
|
+
* // Events auto-emit to EventBus typed channels — subscribe there
|
|
279
|
+
* eventBus.get('mark:entity-type-added').subscribe((stored) => {
|
|
280
|
+
* // Invalidate entity types query
|
|
714
281
|
* });
|
|
715
282
|
*
|
|
716
283
|
* // Close when no longer needed
|
|
@@ -729,7 +296,8 @@ var SSEClient = class {
|
|
|
729
296
|
progressEvents: ["*"],
|
|
730
297
|
completeEvent: null,
|
|
731
298
|
errorEvent: null,
|
|
732
|
-
eventBus: options.eventBus
|
|
299
|
+
eventBus: options.eventBus,
|
|
300
|
+
reconnect: true
|
|
733
301
|
},
|
|
734
302
|
this.logger
|
|
735
303
|
);
|
|
@@ -765,7 +333,8 @@ var SSEClient = class {
|
|
|
765
333
|
progressEvents: ["*"],
|
|
766
334
|
completeEvent: null,
|
|
767
335
|
errorEvent: null,
|
|
768
|
-
eventBus: options.eventBus
|
|
336
|
+
eventBus: options.eventBus,
|
|
337
|
+
reconnect: true
|
|
769
338
|
},
|
|
770
339
|
this.logger
|
|
771
340
|
);
|
|
@@ -778,497 +347,693 @@ var SSEClient = class {
|
|
|
778
347
|
return stream;
|
|
779
348
|
}
|
|
780
349
|
};
|
|
781
|
-
var
|
|
782
|
-
constructor(
|
|
783
|
-
this.eventBus = eventBus;
|
|
784
|
-
this.sse = sse;
|
|
350
|
+
var BrowseNamespace = class {
|
|
351
|
+
constructor(http, eventBus, getToken) {
|
|
785
352
|
this.http = http;
|
|
353
|
+
this.eventBus = eventBus;
|
|
354
|
+
this.getToken = getToken;
|
|
355
|
+
this.subscribeToEvents();
|
|
356
|
+
}
|
|
357
|
+
// ── Annotation list cache ───────────────────────────────────────────────
|
|
358
|
+
annotationList$ = new BehaviorSubject(/* @__PURE__ */ new Map());
|
|
359
|
+
fetchingAnnotationList = /* @__PURE__ */ new Set();
|
|
360
|
+
annotationListObs$ = /* @__PURE__ */ new Map();
|
|
361
|
+
// ── Annotation detail cache ─────────────────────────────────────────────
|
|
362
|
+
annotationDetail$ = new BehaviorSubject(/* @__PURE__ */ new Map());
|
|
363
|
+
fetchingAnnotationDetail = /* @__PURE__ */ new Set();
|
|
364
|
+
annotationDetailObs$ = /* @__PURE__ */ new Map();
|
|
365
|
+
// ── Resource detail cache ───────────────────────────────────────────────
|
|
366
|
+
resourceDetail$ = new BehaviorSubject(/* @__PURE__ */ new Map());
|
|
367
|
+
fetchingResourceDetail = /* @__PURE__ */ new Set();
|
|
368
|
+
resourceDetailObs$ = /* @__PURE__ */ new Map();
|
|
369
|
+
// ── Resource list cache ─────────────────────────────────────────────────
|
|
370
|
+
resourceList$ = new BehaviorSubject(/* @__PURE__ */ new Map());
|
|
371
|
+
fetchingResourceList = /* @__PURE__ */ new Set();
|
|
372
|
+
resourceListObs$ = /* @__PURE__ */ new Map();
|
|
373
|
+
// ── Entity types cache ──────────────────────────────────────────────────
|
|
374
|
+
entityTypes$ = new BehaviorSubject(void 0);
|
|
375
|
+
fetchingEntityTypes = false;
|
|
376
|
+
// ── Referenced-by cache ─────────────────────────────────────────────────
|
|
377
|
+
referencedBy$ = new BehaviorSubject(/* @__PURE__ */ new Map());
|
|
378
|
+
fetchingReferencedBy = /* @__PURE__ */ new Set();
|
|
379
|
+
referencedByObs$ = /* @__PURE__ */ new Map();
|
|
380
|
+
getToken;
|
|
381
|
+
// ── Live queries ────────────────────────────────────────────────────────
|
|
382
|
+
resource(resourceId) {
|
|
383
|
+
if (!this.resourceDetail$.value.has(resourceId) && !this.fetchingResourceDetail.has(resourceId)) {
|
|
384
|
+
this.fetchResourceDetail(resourceId);
|
|
385
|
+
}
|
|
386
|
+
let obs = this.resourceDetailObs$.get(resourceId);
|
|
387
|
+
if (!obs) {
|
|
388
|
+
obs = this.resourceDetail$.pipe(map((m) => m.get(resourceId)), distinctUntilChanged());
|
|
389
|
+
this.resourceDetailObs$.set(resourceId, obs);
|
|
390
|
+
}
|
|
391
|
+
return obs;
|
|
786
392
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
this.eventBus.get("bind:update-body").subscribe((event) => {
|
|
799
|
-
const annotationId = event.annotationId;
|
|
800
|
-
const finishedSub = this.eventBus.get("bind:finished").subscribe((finishedEvent) => {
|
|
801
|
-
if (finishedEvent.annotationId !== annotationId) return;
|
|
802
|
-
finishedSub.unsubscribe();
|
|
803
|
-
failedSub.unsubscribe();
|
|
804
|
-
this.eventBus.get("bind:body-updated").next({ annotationId });
|
|
805
|
-
});
|
|
806
|
-
const failedSub = this.eventBus.get("bind:failed").subscribe(() => {
|
|
807
|
-
finishedSub.unsubscribe();
|
|
808
|
-
failedSub.unsubscribe();
|
|
809
|
-
this.eventBus.get("bind:body-update-failed").next({ error: new Error("Bind failed") });
|
|
810
|
-
});
|
|
811
|
-
this.sse.bindAnnotation(
|
|
812
|
-
rUri,
|
|
813
|
-
annotationId,
|
|
814
|
-
{ resourceId: event.resourceId, operations: event.operations },
|
|
815
|
-
{ auth: getToken(), eventBus: this.eventBus }
|
|
816
|
-
);
|
|
817
|
-
})
|
|
818
|
-
);
|
|
819
|
-
sub.add(
|
|
820
|
-
this.eventBus.get("match:search-requested").subscribe((event) => {
|
|
821
|
-
this.sse.matchSearch(rUri, {
|
|
822
|
-
correlationId: event.correlationId,
|
|
823
|
-
referenceId: event.referenceId,
|
|
824
|
-
context: event.context,
|
|
825
|
-
limit: event.limit,
|
|
826
|
-
useSemanticScoring: event.useSemanticScoring
|
|
827
|
-
}, { auth: getToken(), eventBus: this.eventBus });
|
|
828
|
-
})
|
|
829
|
-
);
|
|
830
|
-
return sub;
|
|
393
|
+
resources(filters) {
|
|
394
|
+
const key = JSON.stringify(filters ?? {});
|
|
395
|
+
if (!this.resourceList$.value.has(key) && !this.fetchingResourceList.has(key)) {
|
|
396
|
+
this.fetchResourceList(key, filters);
|
|
397
|
+
}
|
|
398
|
+
let obs = this.resourceListObs$.get(key);
|
|
399
|
+
if (!obs) {
|
|
400
|
+
obs = this.resourceList$.pipe(map((m) => m.get(key)), distinctUntilChanged());
|
|
401
|
+
this.resourceListObs$.set(key, obs);
|
|
402
|
+
}
|
|
403
|
+
return obs;
|
|
831
404
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
abortController?.abort();
|
|
846
|
-
abortController = new AbortController();
|
|
847
|
-
this.sse.yieldResource(
|
|
848
|
-
event.resourceId,
|
|
849
|
-
event.annotationId,
|
|
850
|
-
event.options,
|
|
851
|
-
{ auth: getToken(), eventBus: this.eventBus }
|
|
852
|
-
);
|
|
853
|
-
})
|
|
854
|
-
);
|
|
855
|
-
sub.add(
|
|
856
|
-
this.eventBus.get("yield:finished").subscribe((event) => {
|
|
857
|
-
if (!event.resourceId || !event.referenceId || !event.sourceResourceId) return;
|
|
858
|
-
this.eventBus.get("bind:update-body").next({
|
|
859
|
-
annotationId: annotationId(event.referenceId),
|
|
860
|
-
resourceId: resourceId(event.sourceResourceId),
|
|
861
|
-
operations: [{ op: "add", item: { type: "SpecificResource", source: event.resourceId } }]
|
|
862
|
-
});
|
|
863
|
-
})
|
|
864
|
-
);
|
|
865
|
-
sub.add(
|
|
866
|
-
this.eventBus.get("job:cancel-requested").subscribe((event) => {
|
|
867
|
-
if (event.jobType === "generation") {
|
|
868
|
-
abortController?.abort();
|
|
869
|
-
abortController = null;
|
|
870
|
-
}
|
|
871
|
-
})
|
|
872
|
-
);
|
|
873
|
-
sub.add(new Subscription(() => {
|
|
874
|
-
abortController?.abort();
|
|
875
|
-
}));
|
|
876
|
-
return sub;
|
|
405
|
+
annotations(resourceId) {
|
|
406
|
+
if (!this.annotationList$.value.has(resourceId) && !this.fetchingAnnotationList.has(resourceId)) {
|
|
407
|
+
this.fetchAnnotationList(resourceId);
|
|
408
|
+
}
|
|
409
|
+
let obs = this.annotationListObs$.get(resourceId);
|
|
410
|
+
if (!obs) {
|
|
411
|
+
obs = this.annotationList$.pipe(
|
|
412
|
+
map((m) => m.get(resourceId)?.annotations),
|
|
413
|
+
distinctUntilChanged()
|
|
414
|
+
);
|
|
415
|
+
this.annotationListObs$.set(resourceId, obs);
|
|
416
|
+
}
|
|
417
|
+
return obs;
|
|
877
418
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
mark(rUri, getToken) {
|
|
889
|
-
const sub = new Subscription();
|
|
890
|
-
let assistAbort = null;
|
|
891
|
-
sub.add(
|
|
892
|
-
this.eventBus.get("mark:submit").subscribe(async (event) => {
|
|
893
|
-
try {
|
|
894
|
-
const result = await this.http.markAnnotation(rUri, {
|
|
895
|
-
motivation: event.motivation,
|
|
896
|
-
target: { source: rUri, selector: event.selector },
|
|
897
|
-
body: event.body
|
|
898
|
-
}, { auth: getToken() });
|
|
899
|
-
this.eventBus.get("mark:created").next({ annotationId: annotationId(result.annotationId) });
|
|
900
|
-
} catch (error) {
|
|
901
|
-
this.eventBus.get("mark:create-failed").next({ error });
|
|
902
|
-
}
|
|
903
|
-
})
|
|
904
|
-
);
|
|
905
|
-
sub.add(
|
|
906
|
-
this.eventBus.get("mark:delete").subscribe(async (event) => {
|
|
907
|
-
try {
|
|
908
|
-
await this.http.deleteAnnotation(rUri, event.annotationId, { auth: getToken() });
|
|
909
|
-
this.eventBus.get("mark:deleted").next({ annotationId: event.annotationId });
|
|
910
|
-
} catch (error) {
|
|
911
|
-
this.eventBus.get("mark:delete-failed").next({ error });
|
|
912
|
-
}
|
|
913
|
-
})
|
|
914
|
-
);
|
|
915
|
-
sub.add(
|
|
916
|
-
this.eventBus.get("mark:assist-request").subscribe(async (event) => {
|
|
917
|
-
assistAbort?.abort();
|
|
918
|
-
assistAbort = new AbortController();
|
|
919
|
-
const opts = { auth: getToken(), eventBus: this.eventBus };
|
|
920
|
-
const { motivation, options } = event;
|
|
921
|
-
try {
|
|
922
|
-
if (motivation === "tagging") {
|
|
923
|
-
const { schemaId, categories } = options;
|
|
924
|
-
if (!schemaId || !categories?.length) throw new Error("Tag assist requires schemaId and categories");
|
|
925
|
-
this.sse.markTags(rUri, { schemaId, categories }, opts);
|
|
926
|
-
} else if (motivation === "linking") {
|
|
927
|
-
const { entityTypes, includeDescriptiveReferences } = options;
|
|
928
|
-
if (!entityTypes?.length) throw new Error("Reference assist requires entityTypes");
|
|
929
|
-
this.sse.markReferences(rUri, {
|
|
930
|
-
entityTypes,
|
|
931
|
-
includeDescriptiveReferences: includeDescriptiveReferences ?? false
|
|
932
|
-
}, opts);
|
|
933
|
-
} else if (motivation === "highlighting") {
|
|
934
|
-
this.sse.markHighlights(rUri, { instructions: options.instructions, density: options.density }, opts);
|
|
935
|
-
} else if (motivation === "assessing") {
|
|
936
|
-
this.sse.markAssessments(rUri, {
|
|
937
|
-
instructions: options.instructions,
|
|
938
|
-
tone: options.tone,
|
|
939
|
-
density: options.density,
|
|
940
|
-
language: options.language
|
|
941
|
-
}, opts);
|
|
942
|
-
} else if (motivation === "commenting") {
|
|
943
|
-
this.sse.markComments(rUri, {
|
|
944
|
-
instructions: options.instructions,
|
|
945
|
-
tone: options.tone,
|
|
946
|
-
density: options.density,
|
|
947
|
-
language: options.language
|
|
948
|
-
}, opts);
|
|
949
|
-
}
|
|
950
|
-
} catch (error) {
|
|
951
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
952
|
-
this.eventBus.get("mark:assist-cancelled").next(void 0);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
})
|
|
956
|
-
);
|
|
957
|
-
sub.add(
|
|
958
|
-
this.eventBus.get("job:cancel-requested").subscribe((event) => {
|
|
959
|
-
if (event.jobType === "annotation") {
|
|
960
|
-
assistAbort?.abort();
|
|
961
|
-
assistAbort = null;
|
|
962
|
-
}
|
|
963
|
-
})
|
|
964
|
-
);
|
|
965
|
-
sub.add(new Subscription(() => {
|
|
966
|
-
assistAbort?.abort();
|
|
967
|
-
}));
|
|
968
|
-
return sub;
|
|
419
|
+
annotation(resourceId, annotationId) {
|
|
420
|
+
if (!this.annotationDetail$.value.has(annotationId) && !this.fetchingAnnotationDetail.has(annotationId)) {
|
|
421
|
+
this.fetchAnnotationDetail(resourceId, annotationId);
|
|
422
|
+
}
|
|
423
|
+
let obs = this.annotationDetailObs$.get(annotationId);
|
|
424
|
+
if (!obs) {
|
|
425
|
+
obs = this.annotationDetail$.pipe(map((m) => m.get(annotationId)), distinctUntilChanged());
|
|
426
|
+
this.annotationDetailObs$.set(annotationId, obs);
|
|
427
|
+
}
|
|
428
|
+
return obs;
|
|
969
429
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
if (failedEvent.correlationId !== correlationId) return;
|
|
993
|
-
finishedSub.unsubscribe();
|
|
994
|
-
failedSub.unsubscribe();
|
|
995
|
-
});
|
|
996
|
-
this.sse.gatherAnnotation(
|
|
997
|
-
rUri,
|
|
998
|
-
event.annotationId,
|
|
999
|
-
{ contextWindow, correlationId },
|
|
1000
|
-
{ auth: getToken(), eventBus: this.eventBus }
|
|
1001
|
-
);
|
|
430
|
+
entityTypes() {
|
|
431
|
+
if (this.entityTypes$.value === void 0 && !this.fetchingEntityTypes) {
|
|
432
|
+
this.fetchEntityTypes();
|
|
433
|
+
}
|
|
434
|
+
return this.entityTypes$.asObservable();
|
|
435
|
+
}
|
|
436
|
+
referencedBy(resourceId) {
|
|
437
|
+
if (!this.referencedBy$.value.has(resourceId) && !this.fetchingReferencedBy.has(resourceId)) {
|
|
438
|
+
this.fetchReferencedBy(resourceId);
|
|
439
|
+
}
|
|
440
|
+
let obs = this.referencedByObs$.get(resourceId);
|
|
441
|
+
if (!obs) {
|
|
442
|
+
obs = this.referencedBy$.pipe(map((m) => m.get(resourceId)), distinctUntilChanged());
|
|
443
|
+
this.referencedByObs$.set(resourceId, obs);
|
|
444
|
+
}
|
|
445
|
+
return obs;
|
|
446
|
+
}
|
|
447
|
+
// ── One-shot reads ──────────────────────────────────────────────────────
|
|
448
|
+
async resourceContent(resourceId) {
|
|
449
|
+
const result = await this.http.getResourceRepresentation(resourceId, {
|
|
450
|
+
accept: "text/plain",
|
|
451
|
+
auth: this.getToken()
|
|
1002
452
|
});
|
|
453
|
+
const decoder = new TextDecoder();
|
|
454
|
+
return decoder.decode(result.data);
|
|
1003
455
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
*/
|
|
1009
|
-
resourceEvents(rUri, getToken) {
|
|
1010
|
-
const stream = this.sse.resourceEvents(rUri, {
|
|
1011
|
-
auth: getToken(),
|
|
1012
|
-
eventBus: this.eventBus
|
|
456
|
+
async resourceRepresentation(resourceId, options) {
|
|
457
|
+
return this.http.getResourceRepresentation(resourceId, {
|
|
458
|
+
accept: options?.accept,
|
|
459
|
+
auth: this.getToken()
|
|
1013
460
|
});
|
|
1014
|
-
return new Subscription(() => stream.close());
|
|
1015
461
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
*/
|
|
1021
|
-
attentionStream(getToken) {
|
|
1022
|
-
const stream = this.sse.attentionStream({
|
|
1023
|
-
auth: getToken(),
|
|
1024
|
-
eventBus: this.eventBus
|
|
462
|
+
async resourceRepresentationStream(resourceId, options) {
|
|
463
|
+
return this.http.getResourceRepresentationStream(resourceId, {
|
|
464
|
+
accept: options?.accept,
|
|
465
|
+
auth: this.getToken()
|
|
1025
466
|
});
|
|
1026
|
-
return new Subscription(() => stream.close());
|
|
1027
467
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
468
|
+
async resourceEvents(resourceId) {
|
|
469
|
+
const result = await this.http.getResourceEvents(resourceId, { auth: this.getToken() });
|
|
470
|
+
return result.events;
|
|
471
|
+
}
|
|
472
|
+
async annotationHistory(resourceId, annotationId) {
|
|
473
|
+
return this.http.getAnnotationHistory(resourceId, annotationId, { auth: this.getToken() });
|
|
474
|
+
}
|
|
475
|
+
async connections(_resourceId) {
|
|
476
|
+
throw new Error("Not implemented: connections endpoint does not exist yet");
|
|
477
|
+
}
|
|
478
|
+
async backlinks(_resourceId) {
|
|
479
|
+
throw new Error("Not implemented: backlinks endpoint does not exist yet");
|
|
480
|
+
}
|
|
481
|
+
async resourcesByName(_query, _limit) {
|
|
482
|
+
throw new Error("Not implemented: resourcesByName endpoint does not exist yet");
|
|
483
|
+
}
|
|
484
|
+
async files(dirPath, sort) {
|
|
485
|
+
return this.http.browseFiles(dirPath, sort, { auth: this.getToken() });
|
|
486
|
+
}
|
|
487
|
+
// ── Invalidation (exposed for other namespaces) ─────────────────────────
|
|
488
|
+
invalidateAnnotationList(resourceId) {
|
|
489
|
+
const next = new Map(this.annotationList$.value);
|
|
490
|
+
next.delete(resourceId);
|
|
491
|
+
this.annotationList$.next(next);
|
|
492
|
+
this.fetchAnnotationList(resourceId);
|
|
493
|
+
}
|
|
494
|
+
invalidateAnnotationDetail(annotationId) {
|
|
495
|
+
const next = new Map(this.annotationDetail$.value);
|
|
496
|
+
next.delete(annotationId);
|
|
497
|
+
this.annotationDetail$.next(next);
|
|
498
|
+
}
|
|
499
|
+
invalidateResourceDetail(id) {
|
|
500
|
+
const next = new Map(this.resourceDetail$.value);
|
|
501
|
+
next.delete(id);
|
|
502
|
+
this.resourceDetail$.next(next);
|
|
503
|
+
this.fetchResourceDetail(id);
|
|
504
|
+
}
|
|
505
|
+
invalidateResourceLists() {
|
|
506
|
+
this.resourceList$.next(/* @__PURE__ */ new Map());
|
|
507
|
+
}
|
|
508
|
+
invalidateEntityTypes() {
|
|
509
|
+
this.entityTypes$.next(void 0);
|
|
510
|
+
this.fetchEntityTypes();
|
|
511
|
+
}
|
|
512
|
+
invalidateReferencedBy(resourceId) {
|
|
513
|
+
const next = new Map(this.referencedBy$.value);
|
|
514
|
+
next.delete(resourceId);
|
|
515
|
+
this.referencedBy$.next(next);
|
|
516
|
+
this.fetchReferencedBy(resourceId);
|
|
517
|
+
}
|
|
518
|
+
updateAnnotationInPlace(resourceId, annotation) {
|
|
519
|
+
const currentList = this.annotationList$.value.get(resourceId);
|
|
520
|
+
if (!currentList) return;
|
|
521
|
+
const existingIdx = currentList.annotations.findIndex((a) => a.id === annotation.id);
|
|
522
|
+
const nextAnnotations = existingIdx >= 0 ? currentList.annotations.map((a, i) => i === existingIdx ? annotation : a) : [...currentList.annotations, annotation];
|
|
523
|
+
const nextList = { ...currentList, annotations: nextAnnotations };
|
|
524
|
+
const nextMap = new Map(this.annotationList$.value);
|
|
525
|
+
nextMap.set(resourceId, nextList);
|
|
526
|
+
this.annotationList$.next(nextMap);
|
|
527
|
+
}
|
|
528
|
+
// ── EventBus subscriptions ──────────────────────────────────────────────
|
|
529
|
+
subscribeToEvents() {
|
|
530
|
+
const bus = this.eventBus;
|
|
531
|
+
bus.get("mark:delete-ok").subscribe((event) => {
|
|
532
|
+
this.invalidateAnnotationDetail(annotationId(event.annotationId));
|
|
1035
533
|
});
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
534
|
+
bus.get("mark:added").subscribe((stored) => {
|
|
535
|
+
if (stored.resourceId) {
|
|
536
|
+
this.invalidateAnnotationList(stored.resourceId);
|
|
537
|
+
}
|
|
1039
538
|
});
|
|
1040
|
-
|
|
1041
|
-
if (
|
|
1042
|
-
this.
|
|
1043
|
-
|
|
539
|
+
bus.get("mark:removed").subscribe((stored) => {
|
|
540
|
+
if (stored.resourceId) {
|
|
541
|
+
this.invalidateAnnotationList(stored.resourceId);
|
|
542
|
+
}
|
|
543
|
+
this.invalidateAnnotationDetail(annotationId(stored.payload.annotationId));
|
|
544
|
+
});
|
|
545
|
+
bus.get("mark:body-updated").subscribe((event) => {
|
|
546
|
+
const enriched = event;
|
|
547
|
+
if (!enriched.resourceId || !enriched.annotation) return;
|
|
548
|
+
this.updateAnnotationInPlace(enriched.resourceId, enriched.annotation);
|
|
549
|
+
this.invalidateAnnotationDetail(annotationId(enriched.annotation.id));
|
|
550
|
+
});
|
|
551
|
+
bus.get("mark:entity-tag-added").subscribe((stored) => {
|
|
552
|
+
if (stored.resourceId) {
|
|
553
|
+
this.invalidateAnnotationList(stored.resourceId);
|
|
554
|
+
this.invalidateResourceDetail(stored.resourceId);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
bus.get("mark:entity-tag-removed").subscribe((stored) => {
|
|
558
|
+
if (stored.resourceId) {
|
|
559
|
+
this.invalidateAnnotationList(stored.resourceId);
|
|
560
|
+
this.invalidateResourceDetail(stored.resourceId);
|
|
1044
561
|
}
|
|
1045
562
|
});
|
|
1046
|
-
|
|
563
|
+
bus.get("replay-window-exceeded").subscribe((event) => {
|
|
1047
564
|
if (event.resourceId) {
|
|
1048
|
-
this.
|
|
1049
|
-
this.invalidateLists();
|
|
565
|
+
this.invalidateAnnotationList(event.resourceId);
|
|
1050
566
|
}
|
|
1051
567
|
});
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
568
|
+
bus.get("yield:create-ok").subscribe((event) => {
|
|
569
|
+
this.fetchResourceDetail(resourceId(event.resourceId));
|
|
570
|
+
this.invalidateResourceLists();
|
|
571
|
+
});
|
|
572
|
+
bus.get("yield:update-ok").subscribe((event) => {
|
|
573
|
+
this.invalidateResourceDetail(resourceId(event.resourceId));
|
|
574
|
+
this.invalidateResourceLists();
|
|
575
|
+
});
|
|
576
|
+
bus.get("mark:archived").subscribe((stored) => {
|
|
577
|
+
if (stored.resourceId) {
|
|
578
|
+
this.invalidateResourceDetail(stored.resourceId);
|
|
579
|
+
this.invalidateResourceLists();
|
|
1055
580
|
}
|
|
1056
581
|
});
|
|
1057
|
-
|
|
1058
|
-
if (
|
|
1059
|
-
this.
|
|
582
|
+
bus.get("mark:unarchived").subscribe((stored) => {
|
|
583
|
+
if (stored.resourceId) {
|
|
584
|
+
this.invalidateResourceDetail(stored.resourceId);
|
|
585
|
+
this.invalidateResourceLists();
|
|
1060
586
|
}
|
|
1061
587
|
});
|
|
588
|
+
bus.get("mark:entity-type-added").subscribe(() => {
|
|
589
|
+
this.invalidateEntityTypes();
|
|
590
|
+
});
|
|
1062
591
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
/** Update the token getter (called from React when auth token changes) */
|
|
1076
|
-
setTokenGetter(getter) {
|
|
1077
|
-
this.getToken = getter;
|
|
1078
|
-
}
|
|
1079
|
-
/**
|
|
1080
|
-
* Get a single resource by ID as an Observable.
|
|
1081
|
-
* Triggers a fetch if not cached.
|
|
1082
|
-
*/
|
|
1083
|
-
get(id) {
|
|
1084
|
-
if (!this.detail$.value.has(id) && !this.fetchingDetail.has(id)) {
|
|
1085
|
-
this.fetchDetail(id);
|
|
1086
|
-
}
|
|
1087
|
-
let obs = this.detailObs$.get(id);
|
|
1088
|
-
if (!obs) {
|
|
1089
|
-
obs = this.detail$.pipe(map((m) => m.get(id)), distinctUntilChanged());
|
|
1090
|
-
this.detailObs$.set(id, obs);
|
|
592
|
+
// ── Fetch helpers ───────────────────────────────────────────────────────
|
|
593
|
+
async fetchAnnotationList(resourceId) {
|
|
594
|
+
if (this.fetchingAnnotationList.has(resourceId)) return;
|
|
595
|
+
this.fetchingAnnotationList.add(resourceId);
|
|
596
|
+
try {
|
|
597
|
+
const result = await this.http.browseAnnotations(resourceId, void 0, { auth: this.getToken() });
|
|
598
|
+
const next = new Map(this.annotationList$.value);
|
|
599
|
+
next.set(resourceId, result);
|
|
600
|
+
this.annotationList$.next(next);
|
|
601
|
+
} catch {
|
|
602
|
+
} finally {
|
|
603
|
+
this.fetchingAnnotationList.delete(resourceId);
|
|
1091
604
|
}
|
|
1092
|
-
return obs;
|
|
1093
605
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
this.
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
obs = this.list$.pipe(map((m) => m.get(key)), distinctUntilChanged());
|
|
1106
|
-
this.listObs$.set(key, obs);
|
|
606
|
+
async fetchAnnotationDetail(resourceId, annotationId) {
|
|
607
|
+
if (this.fetchingAnnotationDetail.has(annotationId)) return;
|
|
608
|
+
this.fetchingAnnotationDetail.add(annotationId);
|
|
609
|
+
try {
|
|
610
|
+
const result = await this.http.browseAnnotation(resourceId, annotationId, { auth: this.getToken() });
|
|
611
|
+
const next = new Map(this.annotationDetail$.value);
|
|
612
|
+
next.set(annotationId, result.annotation);
|
|
613
|
+
this.annotationDetail$.next(next);
|
|
614
|
+
} catch {
|
|
615
|
+
} finally {
|
|
616
|
+
this.fetchingAnnotationDetail.delete(annotationId);
|
|
1107
617
|
}
|
|
1108
|
-
return obs;
|
|
1109
618
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
619
|
+
async fetchResourceDetail(id) {
|
|
620
|
+
if (this.fetchingResourceDetail.has(id)) return;
|
|
621
|
+
this.fetchingResourceDetail.add(id);
|
|
622
|
+
try {
|
|
623
|
+
const result = await this.http.browseResource(id, { auth: this.getToken() });
|
|
624
|
+
const next = new Map(this.resourceDetail$.value);
|
|
625
|
+
next.set(id, result.resource);
|
|
626
|
+
this.resourceDetail$.next(next);
|
|
627
|
+
} catch {
|
|
628
|
+
} finally {
|
|
629
|
+
this.fetchingResourceDetail.delete(id);
|
|
630
|
+
}
|
|
1116
631
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
this.
|
|
632
|
+
async fetchResourceList(key, filters) {
|
|
633
|
+
if (this.fetchingResourceList.has(key)) return;
|
|
634
|
+
this.fetchingResourceList.add(key);
|
|
635
|
+
try {
|
|
636
|
+
const result = await this.http.browseResources(filters?.limit, filters?.archived, void 0, { auth: this.getToken() });
|
|
637
|
+
const next = new Map(this.resourceList$.value);
|
|
638
|
+
next.set(key, result.resources);
|
|
639
|
+
this.resourceList$.next(next);
|
|
640
|
+
} catch {
|
|
641
|
+
} finally {
|
|
642
|
+
this.fetchingResourceList.delete(key);
|
|
643
|
+
}
|
|
1120
644
|
}
|
|
1121
|
-
async
|
|
1122
|
-
if (this.
|
|
1123
|
-
this.
|
|
645
|
+
async fetchEntityTypes() {
|
|
646
|
+
if (this.fetchingEntityTypes) return;
|
|
647
|
+
this.fetchingEntityTypes = true;
|
|
1124
648
|
try {
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
next.set(id, resource);
|
|
1128
|
-
this.detail$.next(next);
|
|
649
|
+
const result = await this.http.listEntityTypes({ auth: this.getToken() });
|
|
650
|
+
this.entityTypes$.next(result.entityTypes);
|
|
1129
651
|
} catch {
|
|
1130
652
|
} finally {
|
|
1131
|
-
this.
|
|
653
|
+
this.fetchingEntityTypes = false;
|
|
1132
654
|
}
|
|
1133
655
|
}
|
|
1134
|
-
async
|
|
1135
|
-
if (this.
|
|
1136
|
-
this.
|
|
656
|
+
async fetchReferencedBy(resourceId) {
|
|
657
|
+
if (this.fetchingReferencedBy.has(resourceId)) return;
|
|
658
|
+
this.fetchingReferencedBy.add(resourceId);
|
|
1137
659
|
try {
|
|
1138
|
-
const result = await this.http.
|
|
1139
|
-
const next = new Map(this.
|
|
1140
|
-
next.set(
|
|
1141
|
-
this.
|
|
660
|
+
const result = await this.http.browseReferences(resourceId, { auth: this.getToken() });
|
|
661
|
+
const next = new Map(this.referencedBy$.value);
|
|
662
|
+
next.set(resourceId, result.referencedBy);
|
|
663
|
+
this.referencedBy$.next(next);
|
|
1142
664
|
} catch {
|
|
1143
665
|
} finally {
|
|
1144
|
-
this.
|
|
666
|
+
this.fetchingReferencedBy.delete(resourceId);
|
|
1145
667
|
}
|
|
1146
668
|
}
|
|
1147
669
|
};
|
|
1148
|
-
var
|
|
1149
|
-
constructor(http, eventBus) {
|
|
670
|
+
var MarkNamespace = class {
|
|
671
|
+
constructor(http, eventBus, getToken) {
|
|
1150
672
|
this.http = http;
|
|
1151
|
-
eventBus
|
|
1152
|
-
|
|
673
|
+
this.eventBus = eventBus;
|
|
674
|
+
this.getToken = getToken;
|
|
675
|
+
}
|
|
676
|
+
async annotation(resourceId, input) {
|
|
677
|
+
return this.http.markAnnotation(resourceId, input, { auth: this.getToken() });
|
|
678
|
+
}
|
|
679
|
+
async delete(resourceId, annotationId) {
|
|
680
|
+
return this.http.deleteAnnotation(resourceId, annotationId, { auth: this.getToken() });
|
|
681
|
+
}
|
|
682
|
+
async entityType(type) {
|
|
683
|
+
return this.http.addEntityType(type, { auth: this.getToken() });
|
|
684
|
+
}
|
|
685
|
+
async entityTypes(types) {
|
|
686
|
+
return this.http.addEntityTypesBulk(types, { auth: this.getToken() });
|
|
687
|
+
}
|
|
688
|
+
async updateResource(resourceId, data) {
|
|
689
|
+
return this.http.updateResource(resourceId, data, { auth: this.getToken() });
|
|
690
|
+
}
|
|
691
|
+
async archive(resourceId) {
|
|
692
|
+
return this.http.updateResource(resourceId, { archived: true }, { auth: this.getToken() });
|
|
693
|
+
}
|
|
694
|
+
async unarchive(resourceId) {
|
|
695
|
+
return this.http.updateResource(resourceId, { archived: false }, { auth: this.getToken() });
|
|
696
|
+
}
|
|
697
|
+
assist(resourceId, motivation, options) {
|
|
698
|
+
return new Observable((subscriber) => {
|
|
699
|
+
const progress$ = this.eventBus.get("mark:progress").pipe(
|
|
700
|
+
filter((e) => e.resourceId === resourceId)
|
|
701
|
+
);
|
|
702
|
+
const finished$ = this.eventBus.get("mark:assist-finished").pipe(
|
|
703
|
+
filter((e) => e.resourceId === resourceId && e.motivation === motivation)
|
|
704
|
+
);
|
|
705
|
+
const failed$ = this.eventBus.get("mark:assist-failed").pipe(
|
|
706
|
+
filter((e) => e.resourceId === resourceId)
|
|
707
|
+
);
|
|
708
|
+
const progressSub = progress$.pipe(takeUntil(merge(finished$, failed$))).subscribe((e) => subscriber.next(e));
|
|
709
|
+
const finishedSub = finished$.subscribe((e) => {
|
|
710
|
+
subscriber.next(e);
|
|
711
|
+
subscriber.complete();
|
|
712
|
+
});
|
|
713
|
+
const failedSub = failed$.subscribe((e) => {
|
|
714
|
+
subscriber.error(new Error(e.message));
|
|
715
|
+
});
|
|
716
|
+
const auth = this.getToken();
|
|
717
|
+
const postPromise = this.dispatchAssist(resourceId, motivation, options, auth);
|
|
718
|
+
postPromise.catch((error) => {
|
|
719
|
+
subscriber.error(error);
|
|
720
|
+
});
|
|
721
|
+
return () => {
|
|
722
|
+
progressSub.unsubscribe();
|
|
723
|
+
finishedSub.unsubscribe();
|
|
724
|
+
failedSub.unsubscribe();
|
|
725
|
+
};
|
|
1153
726
|
});
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}
|
|
727
|
+
}
|
|
728
|
+
async dispatchAssist(resourceId, motivation, options, auth) {
|
|
729
|
+
if (motivation === "tagging") {
|
|
730
|
+
const { schemaId, categories } = options;
|
|
731
|
+
if (!schemaId || !categories?.length) throw new Error("Tag assist requires schemaId and categories");
|
|
732
|
+
await this.http.annotateTags(resourceId, { schemaId, categories }, { auth });
|
|
733
|
+
} else if (motivation === "linking") {
|
|
734
|
+
const { entityTypes, includeDescriptiveReferences } = options;
|
|
735
|
+
if (!entityTypes?.length) throw new Error("Reference assist requires entityTypes");
|
|
736
|
+
await this.http.annotateReferences(resourceId, {
|
|
737
|
+
entityTypes,
|
|
738
|
+
includeDescriptiveReferences: includeDescriptiveReferences ?? false
|
|
739
|
+
}, { auth });
|
|
740
|
+
} else if (motivation === "highlighting") {
|
|
741
|
+
await this.http.annotateHighlights(resourceId, {
|
|
742
|
+
instructions: options.instructions,
|
|
743
|
+
density: options.density
|
|
744
|
+
}, { auth });
|
|
745
|
+
} else if (motivation === "assessing") {
|
|
746
|
+
await this.http.annotateAssessments(resourceId, {
|
|
747
|
+
instructions: options.instructions,
|
|
748
|
+
tone: options.tone,
|
|
749
|
+
density: options.density,
|
|
750
|
+
language: options.language
|
|
751
|
+
}, { auth });
|
|
752
|
+
} else if (motivation === "commenting") {
|
|
753
|
+
await this.http.annotateComments(resourceId, {
|
|
754
|
+
instructions: options.instructions,
|
|
755
|
+
tone: options.tone,
|
|
756
|
+
density: options.density,
|
|
757
|
+
language: options.language
|
|
758
|
+
}, { auth });
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
// src/namespaces/bind.ts
|
|
764
|
+
var BindNamespace = class {
|
|
765
|
+
constructor(http, getToken) {
|
|
766
|
+
this.http = http;
|
|
767
|
+
this.getToken = getToken;
|
|
768
|
+
}
|
|
769
|
+
async body(resourceId, annotationId, operations) {
|
|
770
|
+
await this.http.bindAnnotation(resourceId, annotationId, { operations }, { auth: this.getToken() });
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
var GatherNamespace = class {
|
|
774
|
+
constructor(http, eventBus, getToken) {
|
|
775
|
+
this.http = http;
|
|
776
|
+
this.eventBus = eventBus;
|
|
777
|
+
this.getToken = getToken;
|
|
778
|
+
}
|
|
779
|
+
annotation(annotationId$1, resourceId, options) {
|
|
780
|
+
return new Observable((subscriber) => {
|
|
781
|
+
const correlationId = crypto.randomUUID();
|
|
782
|
+
const complete$ = this.eventBus.get("gather:complete").pipe(
|
|
783
|
+
filter((e) => e.correlationId === correlationId)
|
|
784
|
+
);
|
|
785
|
+
const failed$ = this.eventBus.get("gather:failed").pipe(
|
|
786
|
+
filter((e) => e.correlationId === correlationId)
|
|
787
|
+
);
|
|
788
|
+
const sub = merge(
|
|
789
|
+
this.eventBus.get("gather:annotation-progress").pipe(
|
|
790
|
+
// Progress events don't carry correlationId, so match by annotationId
|
|
791
|
+
filter((e) => e.annotationId === annotationId$1),
|
|
792
|
+
map((e) => e)
|
|
793
|
+
),
|
|
794
|
+
complete$.pipe(map((e) => e))
|
|
795
|
+
).pipe(takeUntil(merge(complete$, failed$))).subscribe({
|
|
796
|
+
next: (v) => subscriber.next(v),
|
|
797
|
+
error: (e) => subscriber.error(e)
|
|
798
|
+
});
|
|
799
|
+
const completeSub = complete$.subscribe((e) => {
|
|
800
|
+
subscriber.next(e);
|
|
801
|
+
subscriber.complete();
|
|
802
|
+
});
|
|
803
|
+
const failedSub = failed$.subscribe((e) => {
|
|
804
|
+
subscriber.error(new Error(e.message));
|
|
805
|
+
});
|
|
806
|
+
this.http.gatherAnnotationContext(
|
|
807
|
+
resourceId,
|
|
808
|
+
annotationId(annotationId$1),
|
|
809
|
+
{ correlationId, contextWindow: options?.contextWindow ?? 2e3 },
|
|
810
|
+
{ auth: this.getToken() }
|
|
811
|
+
).catch((error) => {
|
|
812
|
+
subscriber.error(error);
|
|
813
|
+
});
|
|
814
|
+
return () => {
|
|
815
|
+
sub.unsubscribe();
|
|
816
|
+
completeSub.unsubscribe();
|
|
817
|
+
failedSub.unsubscribe();
|
|
818
|
+
};
|
|
1158
819
|
});
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
820
|
+
}
|
|
821
|
+
resource(_resourceId, _options) {
|
|
822
|
+
throw new Error("Not implemented: gather.resource() \u2014 no backend route yet");
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
var MatchNamespace = class {
|
|
826
|
+
constructor(http, eventBus, getToken) {
|
|
827
|
+
this.http = http;
|
|
828
|
+
this.eventBus = eventBus;
|
|
829
|
+
this.getToken = getToken;
|
|
830
|
+
}
|
|
831
|
+
search(resourceId, referenceId, context, options) {
|
|
832
|
+
return new Observable((subscriber) => {
|
|
833
|
+
const correlationId = crypto.randomUUID();
|
|
834
|
+
const result$ = this.eventBus.get("match:search-results").pipe(
|
|
835
|
+
filter((e) => e.correlationId === correlationId)
|
|
836
|
+
);
|
|
837
|
+
const failed$ = this.eventBus.get("match:search-failed").pipe(
|
|
838
|
+
filter((e) => e.correlationId === correlationId)
|
|
839
|
+
);
|
|
840
|
+
const resultSub = result$.subscribe((e) => {
|
|
841
|
+
subscriber.next(e);
|
|
842
|
+
subscriber.complete();
|
|
843
|
+
});
|
|
844
|
+
const failedSub = failed$.subscribe((e) => {
|
|
845
|
+
subscriber.error(new Error(e.error));
|
|
846
|
+
});
|
|
847
|
+
this.http.matchSearch(
|
|
848
|
+
resourceId,
|
|
849
|
+
{
|
|
850
|
+
correlationId,
|
|
851
|
+
referenceId,
|
|
852
|
+
context,
|
|
853
|
+
limit: options?.limit,
|
|
854
|
+
useSemanticScoring: options?.useSemanticScoring
|
|
855
|
+
},
|
|
856
|
+
{ auth: this.getToken() }
|
|
857
|
+
).catch((error) => {
|
|
858
|
+
subscriber.error(error);
|
|
859
|
+
});
|
|
860
|
+
return () => {
|
|
861
|
+
resultSub.unsubscribe();
|
|
862
|
+
failedSub.unsubscribe();
|
|
863
|
+
};
|
|
1164
864
|
});
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
var YieldNamespace = class {
|
|
868
|
+
constructor(http, eventBus, getToken) {
|
|
869
|
+
this.http = http;
|
|
870
|
+
this.eventBus = eventBus;
|
|
871
|
+
this.getToken = getToken;
|
|
872
|
+
}
|
|
873
|
+
async resource(data) {
|
|
874
|
+
return this.http.yieldResource(data, { auth: this.getToken() });
|
|
875
|
+
}
|
|
876
|
+
fromAnnotation(resourceId$1, annotationId$1, options) {
|
|
877
|
+
return new Observable((subscriber) => {
|
|
878
|
+
const progress$ = this.eventBus.get("yield:progress").pipe(
|
|
879
|
+
filter((e) => e.referenceId === annotationId$1)
|
|
880
|
+
);
|
|
881
|
+
const finished$ = this.eventBus.get("yield:finished").pipe(
|
|
882
|
+
filter((e) => e.referenceId === annotationId$1)
|
|
883
|
+
);
|
|
884
|
+
const failed$ = this.eventBus.get("yield:failed").pipe(
|
|
885
|
+
filter((e) => e.referenceId === annotationId$1)
|
|
886
|
+
);
|
|
887
|
+
const progressSub = progress$.pipe(takeUntil(merge(finished$, failed$))).subscribe((e) => subscriber.next(e));
|
|
888
|
+
const finishedSub = finished$.subscribe((event) => {
|
|
889
|
+
subscriber.next(event);
|
|
890
|
+
subscriber.complete();
|
|
891
|
+
if (event.resourceId && event.referenceId && event.sourceResourceId) {
|
|
892
|
+
this.eventBus.get("bind:update-body").next({
|
|
893
|
+
correlationId: crypto.randomUUID(),
|
|
894
|
+
annotationId: annotationId(event.referenceId),
|
|
895
|
+
resourceId: resourceId(event.sourceResourceId),
|
|
896
|
+
operations: [{ op: "add", item: { type: "SpecificResource", source: event.resourceId } }]
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
const failedSub = failed$.subscribe((e) => {
|
|
901
|
+
subscriber.error(new Error(e.error ?? e.message ?? "Generation failed"));
|
|
902
|
+
});
|
|
903
|
+
this.http.yieldResourceFromAnnotation(
|
|
904
|
+
resourceId$1,
|
|
905
|
+
annotationId$1,
|
|
906
|
+
options,
|
|
907
|
+
{ auth: this.getToken() }
|
|
908
|
+
).catch((error) => {
|
|
909
|
+
subscriber.error(error);
|
|
910
|
+
});
|
|
911
|
+
return () => {
|
|
912
|
+
progressSub.unsubscribe();
|
|
913
|
+
finishedSub.unsubscribe();
|
|
914
|
+
failedSub.unsubscribe();
|
|
915
|
+
};
|
|
1170
916
|
});
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
917
|
+
}
|
|
918
|
+
async cloneToken(resourceId) {
|
|
919
|
+
const result = await this.http.generateCloneToken(resourceId, { auth: this.getToken() });
|
|
920
|
+
return result;
|
|
921
|
+
}
|
|
922
|
+
async fromToken(token) {
|
|
923
|
+
const result = await this.http.getResourceByToken(token, { auth: this.getToken() });
|
|
924
|
+
return result.sourceResource;
|
|
925
|
+
}
|
|
926
|
+
async createFromToken(options) {
|
|
927
|
+
return this.http.createResourceFromToken(options, { auth: this.getToken() });
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
// src/namespaces/beckon.ts
|
|
932
|
+
var BeckonNamespace = class {
|
|
933
|
+
constructor(http, getToken) {
|
|
934
|
+
this.http = http;
|
|
935
|
+
this.getToken = getToken;
|
|
936
|
+
}
|
|
937
|
+
attention(annotationId, resourceId) {
|
|
938
|
+
this.http.beckonAttention(
|
|
939
|
+
"me",
|
|
940
|
+
// participantId — always 'me' for self-identification
|
|
941
|
+
{ annotationId, resourceId },
|
|
942
|
+
{ auth: this.getToken() }
|
|
943
|
+
).catch(() => {
|
|
1175
944
|
});
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
// src/namespaces/job.ts
|
|
949
|
+
var JobNamespace = class {
|
|
950
|
+
constructor(http, getToken) {
|
|
951
|
+
this.http = http;
|
|
952
|
+
this.getToken = getToken;
|
|
953
|
+
}
|
|
954
|
+
async status(jobId) {
|
|
955
|
+
return this.http.getJobStatus(jobId, { auth: this.getToken() });
|
|
956
|
+
}
|
|
957
|
+
async pollUntilComplete(jobId, options) {
|
|
958
|
+
return this.http.pollJobUntilComplete(jobId, {
|
|
959
|
+
interval: options?.interval,
|
|
960
|
+
timeout: options?.timeout,
|
|
961
|
+
onProgress: options?.onProgress,
|
|
962
|
+
auth: this.getToken()
|
|
1180
963
|
});
|
|
1181
964
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
/** Individual annotation details keyed by AnnotationId */
|
|
1185
|
-
detail$ = new BehaviorSubject(/* @__PURE__ */ new Map());
|
|
1186
|
-
/** Track in-flight fetches */
|
|
1187
|
-
fetchingList = /* @__PURE__ */ new Set();
|
|
1188
|
-
fetchingDetail = /* @__PURE__ */ new Set();
|
|
1189
|
-
/** Memoized Observables — same instance returned for the same key */
|
|
1190
|
-
listObs$ = /* @__PURE__ */ new Map();
|
|
1191
|
-
detailObs$ = /* @__PURE__ */ new Map();
|
|
1192
|
-
/** Mutable token getter — updated from the React layer when auth changes */
|
|
1193
|
-
getToken = () => void 0;
|
|
1194
|
-
/** Update the token getter (called from React when auth token changes) */
|
|
1195
|
-
setTokenGetter(getter) {
|
|
1196
|
-
this.getToken = getter;
|
|
965
|
+
async cancel(jobId, type) {
|
|
966
|
+
throw new Error(`Not implemented: job.cancel(${jobId}, ${type}) \u2014 needs EventBus wiring`);
|
|
1197
967
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
if (!this.list$.value.has(resourceId) && !this.fetchingList.has(resourceId)) {
|
|
1204
|
-
this.fetchList(resourceId);
|
|
1205
|
-
}
|
|
1206
|
-
let obs = this.listObs$.get(resourceId);
|
|
1207
|
-
if (!obs) {
|
|
1208
|
-
obs = this.list$.pipe(map((m) => m.get(resourceId)), distinctUntilChanged());
|
|
1209
|
-
this.listObs$.set(resourceId, obs);
|
|
1210
|
-
}
|
|
1211
|
-
return obs;
|
|
968
|
+
};
|
|
969
|
+
var AuthNamespace = class {
|
|
970
|
+
constructor(http, getToken) {
|
|
971
|
+
this.http = http;
|
|
972
|
+
this.getToken = getToken;
|
|
1212
973
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
* Triggers a fetch if not cached.
|
|
1216
|
-
*/
|
|
1217
|
-
get(resourceId, annotationId) {
|
|
1218
|
-
if (!this.detail$.value.has(annotationId) && !this.fetchingDetail.has(annotationId)) {
|
|
1219
|
-
this.fetchDetail(resourceId, annotationId);
|
|
1220
|
-
}
|
|
1221
|
-
let obs = this.detailObs$.get(annotationId);
|
|
1222
|
-
if (!obs) {
|
|
1223
|
-
obs = this.detail$.pipe(map((m) => m.get(annotationId)), distinctUntilChanged());
|
|
1224
|
-
this.detailObs$.set(annotationId, obs);
|
|
1225
|
-
}
|
|
1226
|
-
return obs;
|
|
974
|
+
async password(emailStr, passwordStr) {
|
|
975
|
+
return this.http.authenticatePassword(email(emailStr), passwordStr);
|
|
1227
976
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
const next = new Map(this.list$.value);
|
|
1231
|
-
next.delete(resourceId);
|
|
1232
|
-
this.list$.next(next);
|
|
1233
|
-
this.fetchList(resourceId);
|
|
977
|
+
async google(credential) {
|
|
978
|
+
return this.http.authenticateGoogle(googleCredential(credential));
|
|
1234
979
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
const next = new Map(this.detail$.value);
|
|
1238
|
-
next.delete(annotationId);
|
|
1239
|
-
this.detail$.next(next);
|
|
980
|
+
async refresh(token) {
|
|
981
|
+
return this.http.refreshToken(refreshToken(token));
|
|
1240
982
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
const next = new Map(this.detail$.value);
|
|
1244
|
-
next.delete(annotationId);
|
|
1245
|
-
this.detail$.next(next);
|
|
983
|
+
async logout() {
|
|
984
|
+
await this.http.logout({ auth: this.getToken() });
|
|
1246
985
|
}
|
|
1247
|
-
async
|
|
1248
|
-
|
|
1249
|
-
this.fetchingList.add(resourceId);
|
|
1250
|
-
try {
|
|
1251
|
-
const result = await this.http.browseAnnotations(resourceId, void 0, { auth: this.getToken() });
|
|
1252
|
-
const next = new Map(this.list$.value);
|
|
1253
|
-
next.set(resourceId, result);
|
|
1254
|
-
this.list$.next(next);
|
|
1255
|
-
} catch {
|
|
1256
|
-
} finally {
|
|
1257
|
-
this.fetchingList.delete(resourceId);
|
|
1258
|
-
}
|
|
986
|
+
async me() {
|
|
987
|
+
return this.http.getMe({ auth: this.getToken() });
|
|
1259
988
|
}
|
|
1260
|
-
async
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
989
|
+
async acceptTerms() {
|
|
990
|
+
await this.http.acceptTerms({ auth: this.getToken() });
|
|
991
|
+
}
|
|
992
|
+
async mcpToken() {
|
|
993
|
+
return this.http.generateMCPToken({ auth: this.getToken() });
|
|
994
|
+
}
|
|
995
|
+
async mediaToken(resourceId) {
|
|
996
|
+
return this.http.getMediaToken(resourceId, { auth: this.getToken() });
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
// src/namespaces/admin.ts
|
|
1001
|
+
var AdminNamespace = class {
|
|
1002
|
+
constructor(http, getToken) {
|
|
1003
|
+
this.http = http;
|
|
1004
|
+
this.getToken = getToken;
|
|
1005
|
+
}
|
|
1006
|
+
async users() {
|
|
1007
|
+
const result = await this.http.listUsers({ auth: this.getToken() });
|
|
1008
|
+
return result.users;
|
|
1009
|
+
}
|
|
1010
|
+
async userStats() {
|
|
1011
|
+
return this.http.getUserStats({ auth: this.getToken() });
|
|
1012
|
+
}
|
|
1013
|
+
async updateUser(userId, data) {
|
|
1014
|
+
const result = await this.http.updateUser(userId, data, { auth: this.getToken() });
|
|
1015
|
+
return result.user;
|
|
1016
|
+
}
|
|
1017
|
+
async oauthConfig() {
|
|
1018
|
+
return this.http.getOAuthConfig({ auth: this.getToken() });
|
|
1019
|
+
}
|
|
1020
|
+
async healthCheck() {
|
|
1021
|
+
return this.http.healthCheck({ auth: this.getToken() });
|
|
1022
|
+
}
|
|
1023
|
+
async status() {
|
|
1024
|
+
return this.http.getStatus({ auth: this.getToken() });
|
|
1025
|
+
}
|
|
1026
|
+
async backup() {
|
|
1027
|
+
return this.http.backupKnowledgeBase({ auth: this.getToken() });
|
|
1028
|
+
}
|
|
1029
|
+
async restore(file, onProgress) {
|
|
1030
|
+
return this.http.restoreKnowledgeBase(file, { auth: this.getToken(), onProgress });
|
|
1031
|
+
}
|
|
1032
|
+
async exportKnowledgeBase(params) {
|
|
1033
|
+
return this.http.exportKnowledgeBase(params, { auth: this.getToken() });
|
|
1034
|
+
}
|
|
1035
|
+
async importKnowledgeBase(file, onProgress) {
|
|
1036
|
+
return this.http.importKnowledgeBase(file, { auth: this.getToken(), onProgress });
|
|
1272
1037
|
}
|
|
1273
1038
|
};
|
|
1274
1039
|
|
|
@@ -1288,6 +1053,11 @@ var SemiontApiClient = class {
|
|
|
1288
1053
|
/** The workspace-scoped EventBus this client was constructed with. */
|
|
1289
1054
|
eventBus;
|
|
1290
1055
|
logger;
|
|
1056
|
+
/**
|
|
1057
|
+
* Shared mutable token getter. All verb namespaces read from this.
|
|
1058
|
+
* Updated via setTokenGetter() from the React auth layer.
|
|
1059
|
+
*/
|
|
1060
|
+
_getToken = () => void 0;
|
|
1291
1061
|
/**
|
|
1292
1062
|
* SSE streaming client for real-time operations
|
|
1293
1063
|
*
|
|
@@ -1295,25 +1065,30 @@ var SemiontApiClient = class {
|
|
|
1295
1065
|
* Uses native fetch() instead of ky for SSE support.
|
|
1296
1066
|
*/
|
|
1297
1067
|
sse;
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1068
|
+
// ── Verb-oriented namespace API ──────────────────────────────────────────
|
|
1069
|
+
browse;
|
|
1070
|
+
mark;
|
|
1071
|
+
bind;
|
|
1072
|
+
gather;
|
|
1073
|
+
match;
|
|
1074
|
+
yield;
|
|
1075
|
+
beckon;
|
|
1076
|
+
job;
|
|
1077
|
+
auth;
|
|
1078
|
+
admin;
|
|
1309
1079
|
constructor(config) {
|
|
1310
|
-
const { baseUrl, eventBus, timeout
|
|
1080
|
+
const { baseUrl, eventBus, timeout = 3e4, retry = 2, logger, tokenRefresher } = config;
|
|
1311
1081
|
this.eventBus = eventBus;
|
|
1312
1082
|
this.logger = logger;
|
|
1313
1083
|
this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
1084
|
+
const retryConfig = tokenRefresher ? {
|
|
1085
|
+
limit: 1,
|
|
1086
|
+
methods: ["get", "post", "put", "patch", "delete", "head", "options"],
|
|
1087
|
+
statusCodes: [401, 408, 413, 429, 500, 502, 503, 504]
|
|
1088
|
+
} : retry;
|
|
1314
1089
|
this.http = ky.create({
|
|
1315
|
-
timeout
|
|
1316
|
-
retry,
|
|
1090
|
+
timeout,
|
|
1091
|
+
retry: retryConfig,
|
|
1317
1092
|
credentials: "include",
|
|
1318
1093
|
hooks: {
|
|
1319
1094
|
beforeRequest: [
|
|
@@ -1329,6 +1104,21 @@ var SemiontApiClient = class {
|
|
|
1329
1104
|
}
|
|
1330
1105
|
}
|
|
1331
1106
|
],
|
|
1107
|
+
beforeRetry: tokenRefresher ? [
|
|
1108
|
+
async ({ request, error }) => {
|
|
1109
|
+
if (!(error instanceof HTTPError) || error.response.status !== 401) {
|
|
1110
|
+
return void 0;
|
|
1111
|
+
}
|
|
1112
|
+
try {
|
|
1113
|
+
const newToken = await tokenRefresher();
|
|
1114
|
+
if (!newToken) return ky.stop;
|
|
1115
|
+
request.headers.set("Authorization", `Bearer ${newToken}`);
|
|
1116
|
+
return void 0;
|
|
1117
|
+
} catch {
|
|
1118
|
+
return ky.stop;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
] : [],
|
|
1332
1122
|
afterResponse: [
|
|
1333
1123
|
(request, _options, response) => {
|
|
1334
1124
|
if (this.logger) {
|
|
@@ -1374,11 +1164,26 @@ var SemiontApiClient = class {
|
|
|
1374
1164
|
baseUrl: this.baseUrl,
|
|
1375
1165
|
logger: this.logger
|
|
1376
1166
|
});
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1167
|
+
if (config.getToken) this._getToken = config.getToken;
|
|
1168
|
+
const getToken = () => this._getToken();
|
|
1169
|
+
this.browse = new BrowseNamespace(this, this.eventBus, getToken);
|
|
1170
|
+
this.mark = new MarkNamespace(this, this.eventBus, getToken);
|
|
1171
|
+
this.bind = new BindNamespace(this, getToken);
|
|
1172
|
+
this.gather = new GatherNamespace(this, this.eventBus, getToken);
|
|
1173
|
+
this.match = new MatchNamespace(this, this.eventBus, getToken);
|
|
1174
|
+
this.yield = new YieldNamespace(this, this.eventBus, getToken);
|
|
1175
|
+
this.beckon = new BeckonNamespace(this, getToken);
|
|
1176
|
+
this.job = new JobNamespace(this, getToken);
|
|
1177
|
+
this.auth = new AuthNamespace(this, getToken);
|
|
1178
|
+
this.admin = new AdminNamespace(this, getToken);
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Update the token getter for all verb namespaces.
|
|
1182
|
+
* Called from the React auth layer when the token changes.
|
|
1183
|
+
* All namespaces share this getter via closure — no per-namespace sync needed.
|
|
1184
|
+
*/
|
|
1185
|
+
setTokenGetter(getter) {
|
|
1186
|
+
this._getToken = getter;
|
|
1382
1187
|
}
|
|
1383
1188
|
authHeaders(options) {
|
|
1384
1189
|
return options?.auth ? { Authorization: `Bearer ${options.auth}` } : {};
|
|
@@ -1645,16 +1450,64 @@ var SemiontApiClient = class {
|
|
|
1645
1450
|
});
|
|
1646
1451
|
}
|
|
1647
1452
|
async bindAnnotation(resourceId, annotationId, data, options) {
|
|
1648
|
-
|
|
1649
|
-
json: data,
|
|
1453
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/bind`, {
|
|
1454
|
+
json: { resourceId, ...data },
|
|
1650
1455
|
headers: this.authHeaders(options)
|
|
1651
|
-
});
|
|
1456
|
+
}).json();
|
|
1652
1457
|
}
|
|
1653
1458
|
async getAnnotationHistory(resourceId, annotationId, options) {
|
|
1654
1459
|
return this.http.get(`${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/history`, {
|
|
1655
1460
|
headers: this.authHeaders(options)
|
|
1656
1461
|
}).json();
|
|
1657
1462
|
}
|
|
1463
|
+
async annotateReferences(resourceId, data, options) {
|
|
1464
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotate-references`, {
|
|
1465
|
+
json: data,
|
|
1466
|
+
headers: this.authHeaders(options)
|
|
1467
|
+
}).json();
|
|
1468
|
+
}
|
|
1469
|
+
async annotateHighlights(resourceId, data, options) {
|
|
1470
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotate-highlights`, {
|
|
1471
|
+
json: data,
|
|
1472
|
+
headers: this.authHeaders(options)
|
|
1473
|
+
}).json();
|
|
1474
|
+
}
|
|
1475
|
+
async annotateAssessments(resourceId, data, options) {
|
|
1476
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotate-assessments`, {
|
|
1477
|
+
json: data,
|
|
1478
|
+
headers: this.authHeaders(options)
|
|
1479
|
+
}).json();
|
|
1480
|
+
}
|
|
1481
|
+
async annotateComments(resourceId, data, options) {
|
|
1482
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotate-comments`, {
|
|
1483
|
+
json: data,
|
|
1484
|
+
headers: this.authHeaders(options)
|
|
1485
|
+
}).json();
|
|
1486
|
+
}
|
|
1487
|
+
async annotateTags(resourceId, data, options) {
|
|
1488
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotate-tags`, {
|
|
1489
|
+
json: data,
|
|
1490
|
+
headers: this.authHeaders(options)
|
|
1491
|
+
}).json();
|
|
1492
|
+
}
|
|
1493
|
+
async yieldResourceFromAnnotation(resourceId, annotationId, data, options) {
|
|
1494
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/yield-resource`, {
|
|
1495
|
+
json: data,
|
|
1496
|
+
headers: this.authHeaders(options)
|
|
1497
|
+
}).json();
|
|
1498
|
+
}
|
|
1499
|
+
async gatherAnnotationContext(resourceId, annotationId, data, options) {
|
|
1500
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/gather`, {
|
|
1501
|
+
json: data,
|
|
1502
|
+
headers: this.authHeaders(options)
|
|
1503
|
+
}).json();
|
|
1504
|
+
}
|
|
1505
|
+
async matchSearch(resourceId, data, options) {
|
|
1506
|
+
return this.http.post(`${this.baseUrl}/resources/${resourceId}/match-search`, {
|
|
1507
|
+
json: { resourceId, ...data },
|
|
1508
|
+
headers: this.authHeaders(options)
|
|
1509
|
+
}).json();
|
|
1510
|
+
}
|
|
1658
1511
|
// ============================================================================
|
|
1659
1512
|
// ENTITY TYPES
|
|
1660
1513
|
// ============================================================================
|
|
@@ -1801,7 +1654,7 @@ var SemiontApiClient = class {
|
|
|
1801
1654
|
*/
|
|
1802
1655
|
async pollJobUntilComplete(id, options) {
|
|
1803
1656
|
const interval = options?.interval ?? 1e3;
|
|
1804
|
-
const
|
|
1657
|
+
const timeout = options?.timeout ?? 6e4;
|
|
1805
1658
|
const startTime = Date.now();
|
|
1806
1659
|
while (true) {
|
|
1807
1660
|
const status = await this.getJobStatus(id, { auth: options?.auth });
|
|
@@ -1811,8 +1664,8 @@ var SemiontApiClient = class {
|
|
|
1811
1664
|
if (status.status === "complete" || status.status === "failed" || status.status === "cancelled") {
|
|
1812
1665
|
return status;
|
|
1813
1666
|
}
|
|
1814
|
-
if (Date.now() - startTime >
|
|
1815
|
-
throw new Error(`Job polling timeout after ${
|
|
1667
|
+
if (Date.now() - startTime > timeout) {
|
|
1668
|
+
throw new Error(`Job polling timeout after ${timeout}ms`);
|
|
1816
1669
|
}
|
|
1817
1670
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
1818
1671
|
}
|
|
@@ -1840,273 +1693,6 @@ var SemiontApiClient = class {
|
|
|
1840
1693
|
}).json();
|
|
1841
1694
|
}
|
|
1842
1695
|
};
|
|
1843
|
-
async function eventBusRequest(eventBus, requestEvent, payload, successEvent, failureEvent, timeoutMs = 3e4) {
|
|
1844
|
-
const { correlationId } = payload;
|
|
1845
|
-
const result$ = merge(
|
|
1846
|
-
eventBus.get(successEvent).pipe(
|
|
1847
|
-
filter((e) => e.correlationId === correlationId),
|
|
1848
|
-
map((e) => ({ ok: true, response: e.response }))
|
|
1849
|
-
),
|
|
1850
|
-
eventBus.get(failureEvent).pipe(
|
|
1851
|
-
filter((e) => e.correlationId === correlationId),
|
|
1852
|
-
map((e) => ({ ok: false, error: e.error }))
|
|
1853
|
-
)
|
|
1854
|
-
).pipe(take(1), timeout(timeoutMs));
|
|
1855
|
-
const resultPromise = firstValueFrom(result$);
|
|
1856
|
-
eventBus.get(requestEvent).next(payload);
|
|
1857
|
-
const result = await resultPromise;
|
|
1858
|
-
if (!result.ok) {
|
|
1859
|
-
throw result.error;
|
|
1860
|
-
}
|
|
1861
|
-
return result.response;
|
|
1862
|
-
}
|
|
1863
|
-
var EventBusClient = class {
|
|
1864
|
-
constructor(eventBus, timeoutMs = 3e4) {
|
|
1865
|
-
this.eventBus = eventBus;
|
|
1866
|
-
this.timeoutMs = timeoutMs;
|
|
1867
|
-
}
|
|
1868
|
-
// ========================================================================
|
|
1869
|
-
// Browse Flow — Resource reads
|
|
1870
|
-
// ========================================================================
|
|
1871
|
-
async browseResource(resourceId) {
|
|
1872
|
-
return eventBusRequest(
|
|
1873
|
-
this.eventBus,
|
|
1874
|
-
"browse:resource-requested",
|
|
1875
|
-
{ correlationId: crypto.randomUUID(), resourceId },
|
|
1876
|
-
"browse:resource-result",
|
|
1877
|
-
"browse:resource-failed",
|
|
1878
|
-
this.timeoutMs
|
|
1879
|
-
);
|
|
1880
|
-
}
|
|
1881
|
-
async browseResources(options) {
|
|
1882
|
-
return eventBusRequest(
|
|
1883
|
-
this.eventBus,
|
|
1884
|
-
"browse:resources-requested",
|
|
1885
|
-
{ correlationId: crypto.randomUUID(), ...options },
|
|
1886
|
-
"browse:resources-result",
|
|
1887
|
-
"browse:resources-failed",
|
|
1888
|
-
this.timeoutMs
|
|
1889
|
-
);
|
|
1890
|
-
}
|
|
1891
|
-
// ========================================================================
|
|
1892
|
-
// Browse Flow — Annotation reads
|
|
1893
|
-
// ========================================================================
|
|
1894
|
-
async getAnnotations(resourceId) {
|
|
1895
|
-
return eventBusRequest(
|
|
1896
|
-
this.eventBus,
|
|
1897
|
-
"browse:annotations-requested",
|
|
1898
|
-
{ correlationId: crypto.randomUUID(), resourceId },
|
|
1899
|
-
"browse:annotations-result",
|
|
1900
|
-
"browse:annotations-failed",
|
|
1901
|
-
this.timeoutMs
|
|
1902
|
-
);
|
|
1903
|
-
}
|
|
1904
|
-
async getAnnotation(resourceId, annotationId) {
|
|
1905
|
-
return eventBusRequest(
|
|
1906
|
-
this.eventBus,
|
|
1907
|
-
"browse:annotation-requested",
|
|
1908
|
-
{ correlationId: crypto.randomUUID(), resourceId, annotationId },
|
|
1909
|
-
"browse:annotation-result",
|
|
1910
|
-
"browse:annotation-failed",
|
|
1911
|
-
this.timeoutMs
|
|
1912
|
-
);
|
|
1913
|
-
}
|
|
1914
|
-
// ========================================================================
|
|
1915
|
-
// Browse Flow — Event history
|
|
1916
|
-
// ========================================================================
|
|
1917
|
-
async getEvents(resourceId, options) {
|
|
1918
|
-
return eventBusRequest(
|
|
1919
|
-
this.eventBus,
|
|
1920
|
-
"browse:events-requested",
|
|
1921
|
-
{ correlationId: crypto.randomUUID(), resourceId, ...options },
|
|
1922
|
-
"browse:events-result",
|
|
1923
|
-
"browse:events-failed",
|
|
1924
|
-
this.timeoutMs
|
|
1925
|
-
);
|
|
1926
|
-
}
|
|
1927
|
-
async getAnnotationHistory(resourceId, annotationId) {
|
|
1928
|
-
return eventBusRequest(
|
|
1929
|
-
this.eventBus,
|
|
1930
|
-
"browse:annotation-history-requested",
|
|
1931
|
-
{ correlationId: crypto.randomUUID(), resourceId, annotationId },
|
|
1932
|
-
"browse:annotation-history-result",
|
|
1933
|
-
"browse:annotation-history-failed",
|
|
1934
|
-
this.timeoutMs
|
|
1935
|
-
);
|
|
1936
|
-
}
|
|
1937
|
-
// ========================================================================
|
|
1938
|
-
// Bind Flow — Graph queries
|
|
1939
|
-
// ========================================================================
|
|
1940
|
-
async getReferencedBy(resourceId, motivation) {
|
|
1941
|
-
return eventBusRequest(
|
|
1942
|
-
this.eventBus,
|
|
1943
|
-
"browse:referenced-by-requested",
|
|
1944
|
-
{ correlationId: crypto.randomUUID(), resourceId, motivation },
|
|
1945
|
-
"browse:referenced-by-result",
|
|
1946
|
-
"browse:referenced-by-failed",
|
|
1947
|
-
this.timeoutMs
|
|
1948
|
-
);
|
|
1949
|
-
}
|
|
1950
|
-
// ========================================================================
|
|
1951
|
-
// Mark Flow — Entity types
|
|
1952
|
-
// ========================================================================
|
|
1953
|
-
async listEntityTypes() {
|
|
1954
|
-
return eventBusRequest(
|
|
1955
|
-
this.eventBus,
|
|
1956
|
-
"browse:entity-types-requested",
|
|
1957
|
-
{ correlationId: crypto.randomUUID() },
|
|
1958
|
-
"browse:entity-types-result",
|
|
1959
|
-
"browse:entity-types-failed",
|
|
1960
|
-
this.timeoutMs
|
|
1961
|
-
);
|
|
1962
|
-
}
|
|
1963
|
-
addEntityType(tag, userId) {
|
|
1964
|
-
this.eventBus.get("mark:add-entity-type").next({ tag, userId });
|
|
1965
|
-
}
|
|
1966
|
-
// ========================================================================
|
|
1967
|
-
// Yield Flow — Clone tokens
|
|
1968
|
-
// ========================================================================
|
|
1969
|
-
async generateCloneToken(resourceId) {
|
|
1970
|
-
return eventBusRequest(
|
|
1971
|
-
this.eventBus,
|
|
1972
|
-
"yield:clone-token-requested",
|
|
1973
|
-
{ correlationId: crypto.randomUUID(), resourceId },
|
|
1974
|
-
"yield:clone-token-generated",
|
|
1975
|
-
"yield:clone-token-failed",
|
|
1976
|
-
this.timeoutMs
|
|
1977
|
-
);
|
|
1978
|
-
}
|
|
1979
|
-
async getResourceByToken(token) {
|
|
1980
|
-
return eventBusRequest(
|
|
1981
|
-
this.eventBus,
|
|
1982
|
-
"yield:clone-resource-requested",
|
|
1983
|
-
{ correlationId: crypto.randomUUID(), token },
|
|
1984
|
-
"yield:clone-resource-result",
|
|
1985
|
-
"yield:clone-resource-failed",
|
|
1986
|
-
this.timeoutMs
|
|
1987
|
-
);
|
|
1988
|
-
}
|
|
1989
|
-
async createResourceFromToken(options) {
|
|
1990
|
-
return eventBusRequest(
|
|
1991
|
-
this.eventBus,
|
|
1992
|
-
"yield:clone-create",
|
|
1993
|
-
{ correlationId: crypto.randomUUID(), ...options },
|
|
1994
|
-
"yield:clone-created",
|
|
1995
|
-
"yield:clone-create-failed",
|
|
1996
|
-
this.timeoutMs
|
|
1997
|
-
);
|
|
1998
|
-
}
|
|
1999
|
-
// ========================================================================
|
|
2000
|
-
// Job Control
|
|
2001
|
-
// ========================================================================
|
|
2002
|
-
async getJobStatus(jobId) {
|
|
2003
|
-
return eventBusRequest(
|
|
2004
|
-
this.eventBus,
|
|
2005
|
-
"job:status-requested",
|
|
2006
|
-
{ correlationId: crypto.randomUUID(), jobId },
|
|
2007
|
-
"job:status-result",
|
|
2008
|
-
"job:status-failed",
|
|
2009
|
-
this.timeoutMs
|
|
2010
|
-
);
|
|
2011
|
-
}
|
|
2012
|
-
// ========================================================================
|
|
2013
|
-
// Gather Flow — LLM context
|
|
2014
|
-
// ========================================================================
|
|
2015
|
-
async gatherAnnotation(annotationId, resourceId, options) {
|
|
2016
|
-
const correlationId = crypto.randomUUID();
|
|
2017
|
-
const result$ = merge(
|
|
2018
|
-
this.eventBus.get("gather:complete").pipe(
|
|
2019
|
-
filter((e) => e.correlationId === correlationId),
|
|
2020
|
-
map((e) => ({ ok: true, response: e.response }))
|
|
2021
|
-
),
|
|
2022
|
-
this.eventBus.get("gather:failed").pipe(
|
|
2023
|
-
filter((e) => e.correlationId === correlationId),
|
|
2024
|
-
map((e) => ({ ok: false, error: e.error }))
|
|
2025
|
-
)
|
|
2026
|
-
).pipe(take(1), timeout(this.timeoutMs));
|
|
2027
|
-
const resultPromise = firstValueFrom(result$);
|
|
2028
|
-
this.eventBus.get("gather:requested").next({
|
|
2029
|
-
correlationId,
|
|
2030
|
-
annotationId,
|
|
2031
|
-
resourceId,
|
|
2032
|
-
options
|
|
2033
|
-
});
|
|
2034
|
-
const result = await resultPromise;
|
|
2035
|
-
if (!result.ok) {
|
|
2036
|
-
throw result.error;
|
|
2037
|
-
}
|
|
2038
|
-
return result.response;
|
|
2039
|
-
}
|
|
2040
|
-
async gatherResource(resourceId, options) {
|
|
2041
|
-
const correlationId = crypto.randomUUID();
|
|
2042
|
-
const result$ = merge(
|
|
2043
|
-
this.eventBus.get("gather:resource-complete").pipe(
|
|
2044
|
-
filter((e) => e.correlationId === correlationId),
|
|
2045
|
-
map((e) => ({ ok: true, response: e.response }))
|
|
2046
|
-
),
|
|
2047
|
-
this.eventBus.get("gather:resource-failed").pipe(
|
|
2048
|
-
filter((e) => e.correlationId === correlationId),
|
|
2049
|
-
map((e) => ({ ok: false, error: e.error }))
|
|
2050
|
-
)
|
|
2051
|
-
).pipe(take(1), timeout(this.timeoutMs));
|
|
2052
|
-
const resultPromise = firstValueFrom(result$);
|
|
2053
|
-
this.eventBus.get("gather:resource-requested").next({
|
|
2054
|
-
correlationId,
|
|
2055
|
-
resourceId,
|
|
2056
|
-
options
|
|
2057
|
-
});
|
|
2058
|
-
const result = await resultPromise;
|
|
2059
|
-
if (!result.ok) {
|
|
2060
|
-
throw result.error;
|
|
2061
|
-
}
|
|
2062
|
-
return result.response;
|
|
2063
|
-
}
|
|
2064
|
-
// ========================================================================
|
|
2065
|
-
// Bind Flow — Search
|
|
2066
|
-
// ========================================================================
|
|
2067
|
-
async searchResources(searchTerm) {
|
|
2068
|
-
const correlationId = crypto.randomUUID();
|
|
2069
|
-
const referenceId = correlationId;
|
|
2070
|
-
const result$ = merge(
|
|
2071
|
-
this.eventBus.get("match:search-results").pipe(
|
|
2072
|
-
filter((e) => e.correlationId === correlationId),
|
|
2073
|
-
map((e) => ({ ok: true, response: e.response }))
|
|
2074
|
-
),
|
|
2075
|
-
this.eventBus.get("match:search-failed").pipe(
|
|
2076
|
-
filter((e) => e.correlationId === correlationId),
|
|
2077
|
-
map((e) => ({ ok: false, error: new Error(e.error) }))
|
|
2078
|
-
)
|
|
2079
|
-
).pipe(take(1), timeout(this.timeoutMs));
|
|
2080
|
-
const resultPromise = firstValueFrom(result$);
|
|
2081
|
-
this.eventBus.get("match:search-requested").next({
|
|
2082
|
-
correlationId,
|
|
2083
|
-
referenceId,
|
|
2084
|
-
context: {
|
|
2085
|
-
annotation: {
|
|
2086
|
-
"@context": "http://www.w3.org/ns/anno.jsonld",
|
|
2087
|
-
type: "Annotation",
|
|
2088
|
-
id: referenceId,
|
|
2089
|
-
motivation: "linking",
|
|
2090
|
-
target: referenceId,
|
|
2091
|
-
body: []
|
|
2092
|
-
},
|
|
2093
|
-
sourceResource: {
|
|
2094
|
-
"@context": "https://schema.org",
|
|
2095
|
-
"@id": referenceId,
|
|
2096
|
-
name: searchTerm,
|
|
2097
|
-
format: "text/plain",
|
|
2098
|
-
representations: []
|
|
2099
|
-
},
|
|
2100
|
-
sourceContext: { selected: searchTerm }
|
|
2101
|
-
}
|
|
2102
|
-
});
|
|
2103
|
-
const result = await resultPromise;
|
|
2104
|
-
if (!result.ok) {
|
|
2105
|
-
throw result.error;
|
|
2106
|
-
}
|
|
2107
|
-
return result.response;
|
|
2108
|
-
}
|
|
2109
|
-
};
|
|
2110
1696
|
function getBodySource(body) {
|
|
2111
1697
|
if (Array.isArray(body)) {
|
|
2112
1698
|
for (const item of body) {
|
|
@@ -2749,14 +2335,14 @@ function isValidEmail(email) {
|
|
|
2749
2335
|
|
|
2750
2336
|
// src/mime-utils.ts
|
|
2751
2337
|
function getExtensionForMimeType(mimeType) {
|
|
2752
|
-
const
|
|
2338
|
+
const map3 = {
|
|
2753
2339
|
"text/plain": "txt",
|
|
2754
2340
|
"text/markdown": "md",
|
|
2755
2341
|
"image/png": "png",
|
|
2756
2342
|
"image/jpeg": "jpg",
|
|
2757
2343
|
"application/pdf": "pdf"
|
|
2758
2344
|
};
|
|
2759
|
-
return
|
|
2345
|
+
return map3[mimeType] || "dat";
|
|
2760
2346
|
}
|
|
2761
2347
|
function isImageMimeType(mimeType) {
|
|
2762
2348
|
return mimeType === "image/png" || mimeType === "image/jpeg";
|
|
@@ -2777,6 +2363,6 @@ function getMimeCategory(mimeType) {
|
|
|
2777
2363
|
return "unsupported";
|
|
2778
2364
|
}
|
|
2779
2365
|
|
|
2780
|
-
export { APIError,
|
|
2366
|
+
export { APIError, AdminNamespace, AuthNamespace, BeckonNamespace, BindNamespace, BrowseNamespace, GatherNamespace, JWTTokenSchema, JobNamespace, LOCALES, MarkNamespace, MatchNamespace, SSEClient, SSE_STREAM_CONNECTED, SemiontApiClient, YieldNamespace, buildContentCache, createCircleSvg, createPolygonSvg, createRectangleSvg, decodeRepresentation, decodeWithCharset, extractBoundingBox, extractCharset, extractContext, findBestTextMatch, findTextWithContext, formatLocaleDisplay, getAllLocaleCodes, getAnnotationExactText, getBodySource, getBodyType, getChecksum, getCommentText, getCreator, getDerivedFrom, getExactText, getExtensionForMimeType, getLanguage, getLocaleEnglishName, getLocaleInfo, getLocaleNativeName, getMimeCategory, getNodeEncoding, getPrimaryMediaType, getPrimaryRepresentation, getPrimarySelector, getResourceEntityTypes, getResourceId, getStorageUri, getTargetSelector, getTargetSource, getTextQuoteSelector, hasTargetSelector, isArchived, isAssessment, isBodyResolved, isComment, isDraft, isHighlight, isImageMimeType, isPdfMimeType, isReference, isResolvedReference, isStubReference, isTag, isTextMimeType, isValidEmail, normalizeCoordinates, normalizeText, parseSvgSelector, scaleSvgToNative, validateAndCorrectOffsets, validateData, verifyPosition };
|
|
2781
2367
|
//# sourceMappingURL=index.js.map
|
|
2782
2368
|
//# sourceMappingURL=index.js.map
|