@semiont/api-client 0.2.2-build.15 → 0.2.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.
Files changed (73) hide show
  1. package/dist/{types.d.ts → index-DHh0ToZB.d.ts} +460 -6
  2. package/dist/index.d.ts +909 -13
  3. package/dist/index.js +1598 -60
  4. package/dist/index.js.map +1 -1
  5. package/dist/utils/index.d.ts +1 -12
  6. package/dist/utils/index.js +597 -26
  7. package/dist/utils/index.js.map +1 -1
  8. package/package.json +11 -8
  9. package/dist/__tests__/client.test.d.ts +0 -28
  10. package/dist/__tests__/client.test.d.ts.map +0 -1
  11. package/dist/__tests__/client.test.js +0 -567
  12. package/dist/__tests__/client.test.js.map +0 -1
  13. package/dist/__tests__/sse-client.test.d.ts +0 -7
  14. package/dist/__tests__/sse-client.test.d.ts.map +0 -1
  15. package/dist/__tests__/sse-client.test.js +0 -421
  16. package/dist/__tests__/sse-client.test.js.map +0 -1
  17. package/dist/__tests__/sse-stream.test.d.ts +0 -7
  18. package/dist/__tests__/sse-stream.test.d.ts.map +0 -1
  19. package/dist/__tests__/sse-stream.test.js +0 -394
  20. package/dist/__tests__/sse-stream.test.js.map +0 -1
  21. package/dist/__tests__/svg-selectors.test.d.ts +0 -5
  22. package/dist/__tests__/svg-selectors.test.d.ts.map +0 -1
  23. package/dist/__tests__/svg-selectors.test.js +0 -124
  24. package/dist/__tests__/svg-selectors.test.js.map +0 -1
  25. package/dist/branded-types.d.ts +0 -70
  26. package/dist/branded-types.d.ts.map +0 -1
  27. package/dist/branded-types.js +0 -62
  28. package/dist/branded-types.js.map +0 -1
  29. package/dist/client.d.ts +0 -243
  30. package/dist/client.d.ts.map +0 -1
  31. package/dist/client.js +0 -460
  32. package/dist/client.js.map +0 -1
  33. package/dist/index.d.ts.map +0 -1
  34. package/dist/mime-utils.d.ts +0 -27
  35. package/dist/mime-utils.d.ts.map +0 -1
  36. package/dist/mime-utils.js +0 -49
  37. package/dist/mime-utils.js.map +0 -1
  38. package/dist/sse/index.d.ts +0 -343
  39. package/dist/sse/index.d.ts.map +0 -1
  40. package/dist/sse/index.js +0 -404
  41. package/dist/sse/index.js.map +0 -1
  42. package/dist/sse/stream.d.ts +0 -58
  43. package/dist/sse/stream.d.ts.map +0 -1
  44. package/dist/sse/stream.js +0 -187
  45. package/dist/sse/stream.js.map +0 -1
  46. package/dist/sse/types.d.ts +0 -295
  47. package/dist/sse/types.d.ts.map +0 -1
  48. package/dist/sse/types.js +0 -10
  49. package/dist/sse/types.js.map +0 -1
  50. package/dist/types.d.ts.map +0 -1
  51. package/dist/types.js +0 -7
  52. package/dist/types.js.map +0 -1
  53. package/dist/utils/annotations.d.ts +0 -191
  54. package/dist/utils/annotations.d.ts.map +0 -1
  55. package/dist/utils/annotations.js +0 -404
  56. package/dist/utils/annotations.js.map +0 -1
  57. package/dist/utils/events.d.ts +0 -74
  58. package/dist/utils/events.d.ts.map +0 -1
  59. package/dist/utils/events.js +0 -329
  60. package/dist/utils/events.js.map +0 -1
  61. package/dist/utils/index.d.ts.map +0 -1
  62. package/dist/utils/locales.d.ts +0 -31
  63. package/dist/utils/locales.d.ts.map +0 -1
  64. package/dist/utils/locales.js +0 -83
  65. package/dist/utils/locales.js.map +0 -1
  66. package/dist/utils/resources.d.ts +0 -34
  67. package/dist/utils/resources.d.ts.map +0 -1
  68. package/dist/utils/resources.js +0 -63
  69. package/dist/utils/resources.js.map +0 -1
  70. package/dist/utils/validation.d.ts +0 -57
  71. package/dist/utils/validation.d.ts.map +0 -1
  72. package/dist/utils/validation.js +0 -89
  73. package/dist/utils/validation.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,62 +1,1600 @@
1
- "use strict";
2
- /**
3
- * @semiont/api-client
4
- *
5
- * Complete SDK for Semiont - types, client, and utilities
6
- *
7
- * This package provides:
8
- * - TypeScript types generated from the OpenAPI specification
9
- * - A SemiontApiClient class for making API requests
10
- * - Utilities for working with annotations, events, and text
11
- *
12
- * Example:
13
- * ```typescript
14
- * import { SemiontApiClient, isReference, getExactText } from '@semiont/api-client';
15
- *
16
- * const client = new SemiontApiClient({ baseUrl: 'http://localhost:4000' });
17
- * await client.authenticateLocal('user@example.com', '123456');
18
- *
19
- * const doc = await client.createResource({
20
- * name: 'My Resource',
21
- * content: 'Hello World',
22
- * format: 'text/plain',
23
- * entityTypes: ['example']
24
- * });
25
- *
26
- * // Use utilities
27
- * if (isReference(annotation)) {
28
- * const text = getExactText(annotation.target.selector);
29
- * }
30
- * ```
31
- */
32
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
33
- if (k2 === undefined) k2 = k;
34
- var desc = Object.getOwnPropertyDescriptor(m, k);
35
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
36
- desc = { enumerable: true, get: function() { return m[k]; } };
37
- }
38
- Object.defineProperty(o, k2, desc);
39
- }) : (function(o, m, k, k2) {
40
- if (k2 === undefined) k2 = k;
41
- o[k2] = m[k];
42
- }));
43
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
44
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
1
+ import ky from 'ky';
2
+
3
+ // src/client.ts
4
+
5
+ // src/sse/stream.ts
6
+ function createSSEStream(url, fetchOptions, config) {
7
+ const abortController = new AbortController();
8
+ let progressCallback = null;
9
+ let completeCallback = null;
10
+ let errorCallback = null;
11
+ const customHandlers = /* @__PURE__ */ new Map();
12
+ let closed = false;
13
+ const connect = async () => {
14
+ try {
15
+ const response = await fetch(url, {
16
+ ...fetchOptions,
17
+ signal: abortController.signal,
18
+ headers: {
19
+ ...fetchOptions.headers,
20
+ "Accept": "text/event-stream"
21
+ }
22
+ });
23
+ if (!response.ok) {
24
+ const errorData = await response.json().catch(() => ({}));
25
+ throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
26
+ }
27
+ if (!response.body) {
28
+ throw new Error("Response body is null - server did not return a stream");
29
+ }
30
+ const reader = response.body.getReader();
31
+ const decoder = new TextDecoder();
32
+ let buffer = "";
33
+ let eventType = "";
34
+ let eventData = "";
35
+ let eventId = "";
36
+ while (true) {
37
+ const { done, value } = await reader.read();
38
+ if (done || closed) break;
39
+ buffer += decoder.decode(value, { stream: true });
40
+ const lines = buffer.split("\n");
41
+ buffer = lines.pop() || "";
42
+ for (const line of lines) {
43
+ if (line.startsWith("event:")) {
44
+ eventType = line.slice(6).trim();
45
+ } else if (line.startsWith("data:")) {
46
+ eventData = line.slice(5).trim();
47
+ } else if (line.startsWith("id:")) {
48
+ eventId = line.slice(3).trim();
49
+ } else if (line === "") {
50
+ if (eventData && !closed) {
51
+ handleEvent(eventType, eventData, eventId);
52
+ if (closed) break;
53
+ eventType = "";
54
+ eventData = "";
55
+ eventId = "";
56
+ }
57
+ }
58
+ }
59
+ if (closed) break;
60
+ }
61
+ } catch (error) {
62
+ if (error instanceof Error && error.name !== "AbortError") {
63
+ errorCallback?.(error);
64
+ }
65
+ }
66
+ };
67
+ const handleEvent = (eventType, data, _id) => {
68
+ if (data.startsWith(":")) {
69
+ return;
70
+ }
71
+ try {
72
+ const parsed = JSON.parse(data);
73
+ if (config.customEventHandler) {
74
+ const handler = customHandlers.get(eventType);
75
+ if (handler) {
76
+ handler(parsed);
77
+ return;
78
+ }
79
+ progressCallback?.(parsed);
80
+ return;
81
+ }
82
+ if (config.progressEvents.includes(eventType)) {
83
+ progressCallback?.(parsed);
84
+ }
85
+ if (config.completeEvent && eventType === config.completeEvent) {
86
+ completeCallback?.(parsed);
87
+ closed = true;
88
+ abortController.abort();
89
+ }
90
+ if (config.errorEvent && eventType === config.errorEvent) {
91
+ errorCallback?.(new Error(parsed.message || "Stream error"));
92
+ closed = true;
93
+ abortController.abort();
94
+ }
95
+ } catch (error) {
96
+ console.error("[SSE] Failed to parse event data:", error);
97
+ console.error("[SSE] Event type:", eventType);
98
+ console.error("[SSE] Data:", data);
99
+ }
100
+ };
101
+ connect();
102
+ return {
103
+ onProgress(callback) {
104
+ progressCallback = callback;
105
+ },
106
+ onComplete(callback) {
107
+ completeCallback = callback;
108
+ },
109
+ onError(callback) {
110
+ errorCallback = callback;
111
+ },
112
+ close() {
113
+ abortController.abort();
114
+ },
115
+ // Internal method for custom event handlers (used by resourceEvents)
116
+ on(event, callback) {
117
+ customHandlers.set(event, callback);
118
+ }
119
+ };
120
+ }
121
+
122
+ // src/sse/index.ts
123
+ var SSEClient = class {
124
+ baseUrl;
125
+ accessToken = null;
126
+ constructor(config) {
127
+ this.baseUrl = config.baseUrl.endsWith("/") ? config.baseUrl.slice(0, -1) : config.baseUrl;
128
+ this.accessToken = config.accessToken || null;
129
+ }
130
+ /**
131
+ * Set the access token for authenticated requests
132
+ */
133
+ setAccessToken(token) {
134
+ this.accessToken = token;
135
+ }
136
+ /**
137
+ * Clear the access token
138
+ */
139
+ clearAccessToken() {
140
+ this.accessToken = null;
141
+ }
142
+ /**
143
+ * Get common headers for SSE requests
144
+ */
145
+ getHeaders() {
146
+ const headers = {
147
+ "Content-Type": "application/json"
148
+ };
149
+ if (this.accessToken) {
150
+ headers["Authorization"] = `Bearer ${this.accessToken}`;
151
+ }
152
+ return headers;
153
+ }
154
+ /**
155
+ * Extract resource ID from URI
156
+ *
157
+ * Handles both full URIs and plain IDs:
158
+ * - 'http://localhost:4000/resources/doc-123' -> 'doc-123'
159
+ * - 'doc-123' -> 'doc-123'
160
+ */
161
+ extractId(uri) {
162
+ const parts = uri.split("/");
163
+ return parts[parts.length - 1];
164
+ }
165
+ /**
166
+ * Detect annotations in a resource (streaming)
167
+ *
168
+ * Streams entity detection progress via Server-Sent Events.
169
+ *
170
+ * @param resourceId - Resource URI or ID
171
+ * @param request - Detection configuration (entity types to detect)
172
+ * @returns SSE stream controller with progress/complete/error callbacks
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const stream = sseClient.detectAnnotations(
177
+ * 'http://localhost:4000/resources/doc-123',
178
+ * { entityTypes: ['Person', 'Organization'] }
179
+ * );
180
+ *
181
+ * stream.onProgress((progress) => {
182
+ * console.log(`Scanning: ${progress.currentEntityType}`);
183
+ * console.log(`Progress: ${progress.processedEntityTypes}/${progress.totalEntityTypes}`);
184
+ * });
185
+ *
186
+ * stream.onComplete((result) => {
187
+ * console.log(`Detection complete! Found ${result.foundCount} entities`);
188
+ * });
189
+ *
190
+ * stream.onError((error) => {
191
+ * console.error('Detection failed:', error.message);
192
+ * });
193
+ *
194
+ * // Cleanup when done
195
+ * stream.close();
196
+ * ```
197
+ */
198
+ detectAnnotations(resourceId, request) {
199
+ const id = this.extractId(resourceId);
200
+ const url = `${this.baseUrl}/resources/${id}/detect-annotations-stream`;
201
+ return createSSEStream(
202
+ url,
203
+ {
204
+ method: "POST",
205
+ headers: this.getHeaders(),
206
+ body: JSON.stringify(request)
207
+ },
208
+ {
209
+ progressEvents: ["detection-started", "detection-progress"],
210
+ completeEvent: "detection-complete",
211
+ errorEvent: "detection-error"
212
+ }
213
+ );
214
+ }
215
+ /**
216
+ * Generate resource from annotation (streaming)
217
+ *
218
+ * Streams resource generation progress via Server-Sent Events.
219
+ *
220
+ * @param resourceId - Source resource URI or ID
221
+ * @param annotationId - Annotation URI or ID to use as generation source
222
+ * @param request - Generation options (title, prompt, language)
223
+ * @returns SSE stream controller with progress/complete/error callbacks
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * const stream = sseClient.generateResourceFromAnnotation(
228
+ * 'http://localhost:4000/resources/doc-123',
229
+ * 'http://localhost:4000/annotations/ann-456',
230
+ * { language: 'es', title: 'Spanish Summary' }
231
+ * );
232
+ *
233
+ * stream.onProgress((progress) => {
234
+ * console.log(`${progress.status}: ${progress.percentage}%`);
235
+ * console.log(progress.message);
236
+ * });
237
+ *
238
+ * stream.onComplete((result) => {
239
+ * console.log(`Generated resource: ${result.resourceId}`);
240
+ * });
241
+ *
242
+ * stream.onError((error) => {
243
+ * console.error('Generation failed:', error.message);
244
+ * });
245
+ *
246
+ * // Cleanup when done
247
+ * stream.close();
248
+ * ```
249
+ */
250
+ generateResourceFromAnnotation(resourceId, annotationId, request) {
251
+ const resId = this.extractId(resourceId);
252
+ const annId = this.extractId(annotationId);
253
+ const url = `${this.baseUrl}/resources/${resId}/annotations/${annId}/generate-resource-stream`;
254
+ return createSSEStream(
255
+ url,
256
+ {
257
+ method: "POST",
258
+ headers: this.getHeaders(),
259
+ body: JSON.stringify(request)
260
+ },
261
+ {
262
+ progressEvents: ["generation-started", "generation-progress"],
263
+ completeEvent: "generation-complete",
264
+ errorEvent: "generation-error"
265
+ }
266
+ );
267
+ }
268
+ /**
269
+ * Detect highlights in a resource (streaming)
270
+ *
271
+ * Streams highlight detection progress via Server-Sent Events.
272
+ *
273
+ * @param resourceId - Resource URI or ID
274
+ * @param request - Detection configuration (optional instructions)
275
+ * @returns SSE stream controller with progress/complete/error callbacks
276
+ *
277
+ * @example
278
+ * ```typescript
279
+ * const stream = sseClient.detectHighlights(
280
+ * 'http://localhost:4000/resources/doc-123',
281
+ * { instructions: 'Focus on key technical points' }
282
+ * );
283
+ *
284
+ * stream.onProgress((progress) => {
285
+ * console.log(`${progress.status}: ${progress.percentage}%`);
286
+ * console.log(progress.message);
287
+ * });
288
+ *
289
+ * stream.onComplete((result) => {
290
+ * console.log(`Detection complete! Created ${result.createdCount} highlights`);
291
+ * });
292
+ *
293
+ * stream.onError((error) => {
294
+ * console.error('Detection failed:', error.message);
295
+ * });
296
+ *
297
+ * // Cleanup when done
298
+ * stream.close();
299
+ * ```
300
+ */
301
+ detectHighlights(resourceId, request = {}) {
302
+ const id = this.extractId(resourceId);
303
+ const url = `${this.baseUrl}/resources/${id}/detect-highlights-stream`;
304
+ return createSSEStream(
305
+ url,
306
+ {
307
+ method: "POST",
308
+ headers: this.getHeaders(),
309
+ body: JSON.stringify(request)
310
+ },
311
+ {
312
+ progressEvents: ["highlight-detection-started", "highlight-detection-progress"],
313
+ completeEvent: "highlight-detection-complete",
314
+ errorEvent: "highlight-detection-error"
315
+ }
316
+ );
317
+ }
318
+ /**
319
+ * Detect assessments in a resource (streaming)
320
+ *
321
+ * Streams assessment detection progress via Server-Sent Events.
322
+ *
323
+ * @param resourceId - Resource URI or ID
324
+ * @param request - Detection configuration (optional instructions)
325
+ * @returns SSE stream controller with progress/complete/error callbacks
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * const stream = sseClient.detectAssessments(
330
+ * 'http://localhost:4000/resources/doc-123',
331
+ * { instructions: 'Evaluate claims for accuracy' }
332
+ * );
333
+ *
334
+ * stream.onProgress((progress) => {
335
+ * console.log(`${progress.status}: ${progress.percentage}%`);
336
+ * console.log(progress.message);
337
+ * });
338
+ *
339
+ * stream.onComplete((result) => {
340
+ * console.log(`Detection complete! Created ${result.createdCount} assessments`);
341
+ * });
342
+ *
343
+ * stream.onError((error) => {
344
+ * console.error('Detection failed:', error.message);
345
+ * });
346
+ *
347
+ * // Cleanup when done
348
+ * stream.close();
349
+ * ```
350
+ */
351
+ detectAssessments(resourceId, request = {}) {
352
+ const id = this.extractId(resourceId);
353
+ const url = `${this.baseUrl}/resources/${id}/detect-assessments-stream`;
354
+ return createSSEStream(
355
+ url,
356
+ {
357
+ method: "POST",
358
+ headers: this.getHeaders(),
359
+ body: JSON.stringify(request)
360
+ },
361
+ {
362
+ progressEvents: ["assessment-detection-started", "assessment-detection-progress"],
363
+ completeEvent: "assessment-detection-complete",
364
+ errorEvent: "assessment-detection-error"
365
+ }
366
+ );
367
+ }
368
+ /**
369
+ * Detect comments in a resource (streaming)
370
+ *
371
+ * Streams comment detection progress via Server-Sent Events.
372
+ * Uses AI to identify passages that would benefit from explanatory comments
373
+ * and creates comment annotations with contextual information.
374
+ *
375
+ * @param resourceId - Resource URI or ID
376
+ * @param request - Detection configuration (optional instructions and tone)
377
+ * @returns SSE stream controller with progress/complete/error callbacks
378
+ *
379
+ * @example
380
+ * ```typescript
381
+ * const stream = sseClient.detectComments('http://localhost:4000/resources/doc-123', {
382
+ * instructions: 'Focus on technical terminology',
383
+ * tone: 'scholarly'
384
+ * });
385
+ *
386
+ * stream.onProgress((progress) => {
387
+ * console.log(`${progress.status}: ${progress.percentage}%`);
388
+ * });
389
+ *
390
+ * stream.onComplete((result) => {
391
+ * console.log(`Detection complete! Created ${result.createdCount} comments`);
392
+ * });
393
+ *
394
+ * stream.onError((error) => {
395
+ * console.error('Detection failed:', error.message);
396
+ * });
397
+ *
398
+ * // Cleanup when done
399
+ * stream.close();
400
+ * ```
401
+ */
402
+ detectComments(resourceId, request = {}) {
403
+ const id = this.extractId(resourceId);
404
+ const url = `${this.baseUrl}/resources/${id}/detect-comments-stream`;
405
+ return createSSEStream(
406
+ url,
407
+ {
408
+ method: "POST",
409
+ headers: this.getHeaders(),
410
+ body: JSON.stringify(request)
411
+ },
412
+ {
413
+ progressEvents: ["comment-detection-started", "comment-detection-progress"],
414
+ completeEvent: "comment-detection-complete",
415
+ errorEvent: "comment-detection-error"
416
+ }
417
+ );
418
+ }
419
+ /**
420
+ * Detect tags in a resource (streaming)
421
+ *
422
+ * Streams tag detection progress via Server-Sent Events.
423
+ * Uses AI to identify passages serving specific structural roles
424
+ * (e.g., IRAC, IMRAD, Toulmin) and creates tag annotations with dual-body structure.
425
+ *
426
+ * @param resourceId - Resource URI or ID
427
+ * @param request - Detection configuration (schema and categories to detect)
428
+ * @returns SSE stream controller with progress/complete/error callbacks
429
+ *
430
+ * @example
431
+ * ```typescript
432
+ * const stream = sseClient.detectTags('http://localhost:4000/resources/doc-123', {
433
+ * schemaId: 'legal-irac',
434
+ * categories: ['Issue', 'Rule', 'Application', 'Conclusion']
435
+ * });
436
+ *
437
+ * stream.onProgress((progress) => {
438
+ * console.log(`${progress.status}: ${progress.percentage}%`);
439
+ * console.log(`Processing ${progress.currentCategory}...`);
440
+ * });
441
+ *
442
+ * stream.onComplete((result) => {
443
+ * console.log(`Detection complete! Created ${result.tagsCreated} tags`);
444
+ * });
445
+ *
446
+ * stream.onError((error) => {
447
+ * console.error('Detection failed:', error.message);
448
+ * });
449
+ *
450
+ * // Cleanup when done
451
+ * stream.close();
452
+ * ```
453
+ */
454
+ detectTags(resourceId, request) {
455
+ const id = this.extractId(resourceId);
456
+ const url = `${this.baseUrl}/resources/${id}/detect-tags-stream`;
457
+ return createSSEStream(
458
+ url,
459
+ {
460
+ method: "POST",
461
+ headers: this.getHeaders(),
462
+ body: JSON.stringify(request)
463
+ },
464
+ {
465
+ progressEvents: ["tag-detection-started", "tag-detection-progress"],
466
+ completeEvent: "tag-detection-complete",
467
+ errorEvent: "tag-detection-error"
468
+ }
469
+ );
470
+ }
471
+ /**
472
+ * Subscribe to resource events (long-lived stream)
473
+ *
474
+ * Opens a long-lived SSE connection to receive real-time events for a resource.
475
+ * Used for collaborative editing - see events from other users as they happen.
476
+ *
477
+ * This stream does NOT have a complete event - it stays open until explicitly closed.
478
+ *
479
+ * @param resourceId - Resource URI or ID to subscribe to
480
+ * @returns SSE stream controller with event callback
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * const stream = sseClient.resourceEvents('http://localhost:4000/resources/doc-123');
485
+ *
486
+ * stream.onProgress((event) => {
487
+ * console.log(`Event: ${event.type}`);
488
+ * console.log(`User: ${event.userId}`);
489
+ * console.log(`Sequence: ${event.metadata.sequenceNumber}`);
490
+ * console.log(`Payload:`, event.payload);
491
+ * });
492
+ *
493
+ * stream.onError((error) => {
494
+ * console.error('Stream error:', error.message);
495
+ * });
496
+ *
497
+ * // Close when no longer needed (e.g., component unmount)
498
+ * stream.close();
499
+ * ```
500
+ */
501
+ resourceEvents(resourceId) {
502
+ const id = this.extractId(resourceId);
503
+ const url = `${this.baseUrl}/resources/${id}/events/stream`;
504
+ return createSSEStream(
505
+ url,
506
+ {
507
+ method: "GET",
508
+ headers: this.getHeaders()
509
+ },
510
+ {
511
+ progressEvents: ["*"],
512
+ // Accept all event types
513
+ completeEvent: null,
514
+ // Long-lived stream - no completion
515
+ errorEvent: "error",
516
+ // Generic error event
517
+ customEventHandler: true
518
+ // Use custom event handling
519
+ }
520
+ );
521
+ }
522
+ };
523
+
524
+ // src/client.ts
525
+ var APIError = class extends Error {
526
+ constructor(message, status, statusText, details) {
527
+ super(message);
528
+ this.status = status;
529
+ this.statusText = statusText;
530
+ this.details = details;
531
+ this.name = "APIError";
532
+ }
533
+ };
534
+ var SemiontApiClient = class {
535
+ http;
536
+ baseUrl;
537
+ accessToken = null;
538
+ /**
539
+ * SSE streaming client for real-time operations
540
+ *
541
+ * Separate from the main HTTP client to clearly mark streaming endpoints.
542
+ * Uses native fetch() instead of ky for SSE support.
543
+ *
544
+ * @example
545
+ * ```typescript
546
+ * const stream = client.sse.detectAnnotations(
547
+ * resourceId,
548
+ * { entityTypes: ['Person', 'Organization'] }
549
+ * );
550
+ *
551
+ * stream.onProgress((p) => console.log(p.message));
552
+ * stream.onComplete((r) => console.log(`Found ${r.foundCount} entities`));
553
+ * stream.close();
554
+ * ```
555
+ */
556
+ sse;
557
+ constructor(config) {
558
+ const { baseUrl: baseUrl2, accessToken: accessToken2, timeout = 3e4, retry = 2 } = config;
559
+ this.baseUrl = baseUrl2.endsWith("/") ? baseUrl2.slice(0, -1) : baseUrl2;
560
+ this.http = ky.create({
561
+ timeout,
562
+ retry,
563
+ hooks: {
564
+ beforeRequest: [
565
+ (request) => {
566
+ if (this.accessToken) {
567
+ request.headers.set("Authorization", `Bearer ${this.accessToken}`);
568
+ }
569
+ }
570
+ ],
571
+ beforeError: [
572
+ async (error) => {
573
+ const { response } = error;
574
+ if (response) {
575
+ const body = await response.json().catch(() => ({}));
576
+ throw new APIError(
577
+ body.message || `HTTP ${response.status}: ${response.statusText}`,
578
+ response.status,
579
+ response.statusText,
580
+ body
581
+ );
582
+ }
583
+ return error;
584
+ }
585
+ ]
586
+ }
587
+ });
588
+ if (accessToken2) {
589
+ this.accessToken = accessToken2;
590
+ }
591
+ this.sse = new SSEClient({
592
+ baseUrl: this.baseUrl,
593
+ accessToken: this.accessToken || void 0
594
+ });
595
+ }
596
+ /**
597
+ * Set the access token for authenticated requests
598
+ */
599
+ setAccessToken(token) {
600
+ this.accessToken = token;
601
+ this.sse.setAccessToken(token);
602
+ }
603
+ /**
604
+ * Clear the access token
605
+ */
606
+ clearAccessToken() {
607
+ this.accessToken = null;
608
+ this.sse.clearAccessToken();
609
+ }
610
+ // ============================================================================
611
+ // AUTHENTICATION
612
+ // ============================================================================
613
+ async authenticatePassword(email2, password) {
614
+ const response = await this.http.post(`${this.baseUrl}/api/tokens/password`, { json: { email: email2, password } }).json();
615
+ if (response.token) {
616
+ this.setAccessToken(response.token);
617
+ }
618
+ return response;
619
+ }
620
+ async refreshToken(token) {
621
+ const response = await this.http.post(`${this.baseUrl}/api/tokens/refresh`, { json: { refreshToken: token } }).json();
622
+ if (response.access_token) {
623
+ this.setAccessToken(response.access_token);
624
+ }
625
+ return response;
626
+ }
627
+ async authenticateGoogle(credential) {
628
+ const response = await this.http.post(`${this.baseUrl}/api/tokens/google`, { json: { credential } }).json();
629
+ if (response.token) {
630
+ this.setAccessToken(response.token);
631
+ }
632
+ return response;
633
+ }
634
+ async generateMCPToken() {
635
+ return this.http.post(`${this.baseUrl}/api/tokens/mcp-generate`).json();
636
+ }
637
+ // ============================================================================
638
+ // USERS
639
+ // ============================================================================
640
+ async getMe() {
641
+ return this.http.get(`${this.baseUrl}/api/users/me`).json();
642
+ }
643
+ async acceptTerms() {
644
+ return this.http.post(`${this.baseUrl}/api/users/accept-terms`).json();
645
+ }
646
+ async logout() {
647
+ return this.http.post(`${this.baseUrl}/api/users/logout`).json();
648
+ }
649
+ // ============================================================================
650
+ // RESOURCES
651
+ // ============================================================================
652
+ /**
653
+ * Create a new resource with binary content support
654
+ *
655
+ * @param data - Resource creation data
656
+ * @param data.name - Resource name
657
+ * @param data.file - File object or Buffer with binary content
658
+ * @param data.format - MIME type (e.g., 'text/markdown', 'image/png')
659
+ * @param data.entityTypes - Optional array of entity types
660
+ * @param data.language - Optional ISO 639-1 language code
661
+ * @param data.creationMethod - Optional creation method
662
+ * @param data.sourceAnnotationId - Optional source annotation ID
663
+ * @param data.sourceResourceId - Optional source resource ID
664
+ */
665
+ async createResource(data) {
666
+ const formData = new FormData();
667
+ formData.append("name", data.name);
668
+ formData.append("format", data.format);
669
+ if (data.file instanceof File) {
670
+ formData.append("file", data.file);
671
+ } else if (Buffer.isBuffer(data.file)) {
672
+ const blob = new Blob([data.file], { type: data.format });
673
+ formData.append("file", blob, data.name);
674
+ } else {
675
+ throw new Error("file must be a File or Buffer");
676
+ }
677
+ if (data.entityTypes && data.entityTypes.length > 0) {
678
+ formData.append("entityTypes", JSON.stringify(data.entityTypes));
679
+ }
680
+ if (data.language) {
681
+ formData.append("language", data.language);
682
+ }
683
+ if (data.creationMethod) {
684
+ formData.append("creationMethod", data.creationMethod);
685
+ }
686
+ if (data.sourceAnnotationId) {
687
+ formData.append("sourceAnnotationId", data.sourceAnnotationId);
688
+ }
689
+ if (data.sourceResourceId) {
690
+ formData.append("sourceResourceId", data.sourceResourceId);
691
+ }
692
+ return this.http.post(`${this.baseUrl}/resources`, { body: formData }).json();
693
+ }
694
+ async getResource(resourceUri2) {
695
+ return this.http.get(resourceUri2).json();
696
+ }
697
+ /**
698
+ * Get resource representation using W3C content negotiation
699
+ * Returns raw binary content (images, PDFs, text, etc.) with content type
700
+ *
701
+ * @param resourceUri - Full resource URI
702
+ * @param options - Options including Accept header for content negotiation
703
+ * @returns Object with data (ArrayBuffer) and contentType (string)
704
+ *
705
+ * @example
706
+ * ```typescript
707
+ * // Get markdown representation
708
+ * const { data, contentType } = await client.getResourceRepresentation(rUri, { accept: 'text/markdown' });
709
+ * const markdown = new TextDecoder().decode(data);
710
+ *
711
+ * // Get image representation
712
+ * const { data, contentType } = await client.getResourceRepresentation(rUri, { accept: 'image/png' });
713
+ * const blob = new Blob([data], { type: contentType });
714
+ *
715
+ * // Get PDF representation
716
+ * const { data, contentType } = await client.getResourceRepresentation(rUri, { accept: 'application/pdf' });
717
+ * ```
718
+ */
719
+ async getResourceRepresentation(resourceUri2, options) {
720
+ const response = await this.http.get(resourceUri2, {
721
+ headers: {
722
+ Accept: options?.accept || "text/plain"
723
+ }
724
+ });
725
+ const contentType = response.headers.get("content-type") || "application/octet-stream";
726
+ const data = await response.arrayBuffer();
727
+ return { data, contentType };
728
+ }
729
+ /**
730
+ * Get resource representation as a stream using W3C content negotiation
731
+ * Returns streaming binary content (for large files: videos, large PDFs, etc.)
732
+ *
733
+ * Use this for large files to avoid loading entire content into memory.
734
+ * The stream is consumed incrementally and the backend connection stays open
735
+ * until the stream is fully consumed or closed.
736
+ *
737
+ * @param resourceUri - Full resource URI
738
+ * @param options - Options including Accept header for content negotiation
739
+ * @returns Object with stream (ReadableStream) and contentType (string)
740
+ *
741
+ * @example
742
+ * ```typescript
743
+ * // Stream large file
744
+ * const { stream, contentType } = await client.getResourceRepresentationStream(rUri, {
745
+ * accept: 'video/mp4'
746
+ * });
747
+ *
748
+ * // Consume stream chunk by chunk (never loads entire file into memory)
749
+ * for await (const chunk of stream) {
750
+ * // Process chunk
751
+ * console.log(`Received ${chunk.length} bytes`);
752
+ * }
753
+ *
754
+ * // Or pipe to a file in Node.js
755
+ * const fileStream = fs.createWriteStream('output.mp4');
756
+ * const reader = stream.getReader();
757
+ * while (true) {
758
+ * const { done, value } = await reader.read();
759
+ * if (done) break;
760
+ * fileStream.write(value);
761
+ * }
762
+ * ```
763
+ */
764
+ async getResourceRepresentationStream(resourceUri2, options) {
765
+ const response = await this.http.get(resourceUri2, {
766
+ headers: {
767
+ Accept: options?.accept || "text/plain"
768
+ }
769
+ });
770
+ const contentType = response.headers.get("content-type") || "application/octet-stream";
771
+ if (!response.body) {
772
+ throw new Error("Response body is null - cannot create stream");
773
+ }
774
+ return { stream: response.body, contentType };
775
+ }
776
+ async listResources(limit, archived, query) {
777
+ const searchParams = new URLSearchParams();
778
+ if (limit) searchParams.append("limit", limit.toString());
779
+ if (archived !== void 0) searchParams.append("archived", archived.toString());
780
+ if (query) searchParams.append("q", query);
781
+ return this.http.get(`${this.baseUrl}/resources`, { searchParams }).json();
782
+ }
783
+ async updateResource(resourceUri2, data) {
784
+ return this.http.patch(resourceUri2, { json: data }).json();
785
+ }
786
+ async getResourceEvents(resourceUri2) {
787
+ return this.http.get(`${resourceUri2}/events`).json();
788
+ }
789
+ async getResourceAnnotations(resourceUri2) {
790
+ return this.http.get(`${resourceUri2}/annotations`).json();
791
+ }
792
+ async getAnnotationLLMContext(resourceUri2, annotationId, options) {
793
+ const searchParams = new URLSearchParams();
794
+ if (options?.contextWindow) {
795
+ searchParams.append("contextWindow", options.contextWindow.toString());
796
+ }
797
+ return this.http.get(
798
+ `${resourceUri2}/annotations/${annotationId}/llm-context`,
799
+ { searchParams }
800
+ ).json();
801
+ }
802
+ async getResourceReferencedBy(resourceUri2) {
803
+ return this.http.get(`${resourceUri2}/referenced-by`).json();
804
+ }
805
+ async generateCloneToken(resourceUri2) {
806
+ return this.http.post(`${resourceUri2}/clone-with-token`).json();
807
+ }
808
+ async getResourceByToken(token) {
809
+ return this.http.get(`${this.baseUrl}/api/resources/token/${token}`).json();
810
+ }
811
+ async createResourceFromToken(data) {
812
+ return this.http.post(`${this.baseUrl}/api/resources/create-from-token`, { json: data }).json();
813
+ }
814
+ // ============================================================================
815
+ // ANNOTATIONS
816
+ // ============================================================================
817
+ async createAnnotation(resourceUri2, data) {
818
+ return this.http.post(`${resourceUri2}/annotations`, { json: data }).json();
819
+ }
820
+ async getAnnotation(annotationUri2) {
821
+ return this.http.get(annotationUri2).json();
822
+ }
823
+ async getResourceAnnotation(annotationUri2) {
824
+ return this.http.get(annotationUri2).json();
825
+ }
826
+ async listAnnotations(resourceUri2, motivation) {
827
+ const searchParams = new URLSearchParams();
828
+ if (motivation) searchParams.append("motivation", motivation);
829
+ return this.http.get(`${resourceUri2}/annotations`, { searchParams }).json();
830
+ }
831
+ async deleteAnnotation(annotationUri2) {
832
+ await this.http.delete(annotationUri2);
833
+ }
834
+ async updateAnnotationBody(annotationUri2, data) {
835
+ return this.http.put(`${annotationUri2}/body`, {
836
+ json: data
837
+ }).json();
838
+ }
839
+ async getAnnotationHistory(annotationUri2) {
840
+ return this.http.get(`${annotationUri2}/history`).json();
841
+ }
842
+ // ============================================================================
843
+ // ENTITY TYPES
844
+ // ============================================================================
845
+ async addEntityType(type) {
846
+ return this.http.post(`${this.baseUrl}/api/entity-types`, { json: { type } }).json();
847
+ }
848
+ async addEntityTypesBulk(types) {
849
+ return this.http.post(`${this.baseUrl}/api/entity-types/bulk`, { json: { tags: types } }).json();
850
+ }
851
+ async listEntityTypes() {
852
+ return this.http.get(`${this.baseUrl}/api/entity-types`).json();
853
+ }
854
+ // ============================================================================
855
+ // ADMIN
856
+ // ============================================================================
857
+ async listUsers() {
858
+ return this.http.get(`${this.baseUrl}/api/admin/users`).json();
859
+ }
860
+ async getUserStats() {
861
+ return this.http.get(`${this.baseUrl}/api/admin/users/stats`).json();
862
+ }
863
+ /**
864
+ * Update a user by ID
865
+ * Note: Users use DID identifiers (did:web:domain:users:id), not HTTP URIs.
866
+ */
867
+ async updateUser(id, data) {
868
+ return this.http.patch(`${this.baseUrl}/api/admin/users/${id}`, { json: data }).json();
869
+ }
870
+ async getOAuthConfig() {
871
+ return this.http.get(`${this.baseUrl}/api/admin/oauth/config`).json();
872
+ }
873
+ // ============================================================================
874
+ // JOB STATUS
875
+ // ============================================================================
876
+ async getJobStatus(id) {
877
+ return this.http.get(`${this.baseUrl}/api/jobs/${id}`).json();
878
+ }
879
+ /**
880
+ * Poll a job until it completes or fails
881
+ * @param id - The job ID to poll
882
+ * @param options - Polling options
883
+ * @returns The final job status
884
+ */
885
+ async pollJobUntilComplete(id, options) {
886
+ const interval = options?.interval ?? 1e3;
887
+ const timeout = options?.timeout ?? 6e4;
888
+ const startTime = Date.now();
889
+ while (true) {
890
+ const status = await this.getJobStatus(id);
891
+ if (options?.onProgress) {
892
+ options.onProgress(status);
893
+ }
894
+ if (status.status === "complete" || status.status === "failed" || status.status === "cancelled") {
895
+ return status;
896
+ }
897
+ if (Date.now() - startTime > timeout) {
898
+ throw new Error(`Job polling timeout after ${timeout}ms`);
899
+ }
900
+ await new Promise((resolve) => setTimeout(resolve, interval));
901
+ }
902
+ }
903
+ // ============================================================================
904
+ // LLM CONTEXT
905
+ // ============================================================================
906
+ async getResourceLLMContext(resourceUri2, options) {
907
+ const searchParams = new URLSearchParams();
908
+ if (options?.depth !== void 0) searchParams.append("depth", options.depth.toString());
909
+ if (options?.maxResources !== void 0) searchParams.append("maxResources", options.maxResources.toString());
910
+ if (options?.includeContent !== void 0) searchParams.append("includeContent", options.includeContent.toString());
911
+ if (options?.includeSummary !== void 0) searchParams.append("includeSummary", options.includeSummary.toString());
912
+ return this.http.get(`${resourceUri2}/llm-context`, { searchParams }).json();
913
+ }
914
+ // ============================================================================
915
+ // SYSTEM STATUS
916
+ // ============================================================================
917
+ async healthCheck() {
918
+ return this.http.get(`${this.baseUrl}/api/health`).json();
919
+ }
920
+ async getStatus() {
921
+ return this.http.get(`${this.baseUrl}/api/status`).json();
922
+ }
923
+ };
924
+
925
+ // src/branded-types.ts
926
+ function email(value) {
927
+ return value;
928
+ }
929
+ function authCode(value) {
930
+ return value;
931
+ }
932
+ function googleCredential(value) {
933
+ return value;
934
+ }
935
+ function accessToken(value) {
936
+ return value;
937
+ }
938
+ function refreshToken(value) {
939
+ return value;
940
+ }
941
+ function mcpToken(value) {
942
+ return value;
943
+ }
944
+ function cloneToken(value) {
945
+ return value;
946
+ }
947
+ function jobId(value) {
948
+ return value;
949
+ }
950
+ function userDID(value) {
951
+ return value;
952
+ }
953
+ function entityType(value) {
954
+ return value;
955
+ }
956
+ function searchQuery(value) {
957
+ return value;
958
+ }
959
+ function baseUrl(value) {
960
+ return value;
961
+ }
962
+ function resourceUri(uri) {
963
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
964
+ throw new TypeError(`Expected ResourceUri, got: ${uri}`);
965
+ }
966
+ return uri;
967
+ }
968
+ function annotationUri(uri) {
969
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
970
+ throw new TypeError(`Expected AnnotationUri, got: ${uri}`);
971
+ }
972
+ return uri;
973
+ }
974
+ function resourceAnnotationUri(uri) {
975
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
976
+ throw new TypeError(`Expected ResourceAnnotationUri, got: ${uri}`);
977
+ }
978
+ if (!uri.includes("/resources/") || !uri.includes("/annotations/")) {
979
+ throw new TypeError(`Expected nested ResourceAnnotationUri format, got: ${uri}`);
980
+ }
981
+ return uri;
982
+ }
983
+
984
+ // src/utils/annotations.ts
985
+ function getBodySource(body) {
986
+ if (Array.isArray(body)) {
987
+ for (const item of body) {
988
+ if (typeof item === "object" && item !== null && "type" in item && "source" in item) {
989
+ const itemType = item.type;
990
+ const itemSource = item.source;
991
+ if (itemType === "SpecificResource" && typeof itemSource === "string") {
992
+ return resourceUri(itemSource);
993
+ }
994
+ }
995
+ }
996
+ return null;
997
+ }
998
+ if (typeof body === "object" && body !== null && "type" in body && "source" in body) {
999
+ const bodyType = body.type;
1000
+ const bodySource = body.source;
1001
+ if (bodyType === "SpecificResource" && typeof bodySource === "string") {
1002
+ return resourceUri(bodySource);
1003
+ }
1004
+ }
1005
+ return null;
1006
+ }
1007
+ function getBodyType(body) {
1008
+ if (Array.isArray(body)) {
1009
+ if (body.length === 0) {
1010
+ return null;
1011
+ }
1012
+ if (typeof body[0] === "object" && body[0] !== null && "type" in body[0]) {
1013
+ const firstType = body[0].type;
1014
+ if (firstType === "TextualBody" || firstType === "SpecificResource") {
1015
+ return firstType;
1016
+ }
1017
+ }
1018
+ return null;
1019
+ }
1020
+ if (typeof body === "object" && body !== null && "type" in body) {
1021
+ const bodyType = body.type;
1022
+ if (bodyType === "TextualBody" || bodyType === "SpecificResource") {
1023
+ return bodyType;
1024
+ }
1025
+ }
1026
+ return null;
1027
+ }
1028
+ function isBodyResolved(body) {
1029
+ return getBodySource(body) !== null;
1030
+ }
1031
+ function getTargetSource(target) {
1032
+ if (typeof target === "string") {
1033
+ return resourceUri(target);
1034
+ }
1035
+ return resourceUri(target.source);
1036
+ }
1037
+ function getTargetSelector(target) {
1038
+ if (typeof target === "string") {
1039
+ return void 0;
1040
+ }
1041
+ return target.selector;
1042
+ }
1043
+ function hasTargetSelector(target) {
1044
+ return typeof target !== "string" && target.selector !== void 0;
1045
+ }
1046
+ function getEntityTypes(annotation) {
1047
+ if (Array.isArray(annotation.body)) {
1048
+ const entityTags = [];
1049
+ for (const item of annotation.body) {
1050
+ if (typeof item === "object" && item !== null && "type" in item && "value" in item && "purpose" in item) {
1051
+ const itemType = item.type;
1052
+ const itemValue = item.value;
1053
+ const itemPurpose = item.purpose;
1054
+ if (itemType === "TextualBody" && itemPurpose === "tagging" && typeof itemValue === "string" && itemValue.length > 0) {
1055
+ entityTags.push(itemValue);
1056
+ }
1057
+ }
1058
+ }
1059
+ return entityTags;
1060
+ }
1061
+ return [];
1062
+ }
1063
+ function isHighlight(annotation) {
1064
+ return annotation.motivation === "highlighting";
1065
+ }
1066
+ function isReference(annotation) {
1067
+ return annotation.motivation === "linking";
1068
+ }
1069
+ function isAssessment(annotation) {
1070
+ return annotation.motivation === "assessing";
1071
+ }
1072
+ function isComment(annotation) {
1073
+ return annotation.motivation === "commenting";
1074
+ }
1075
+ function isTag(annotation) {
1076
+ return annotation.motivation === "tagging";
1077
+ }
1078
+ function getCommentText(annotation) {
1079
+ if (!isComment(annotation)) return void 0;
1080
+ const body = Array.isArray(annotation.body) ? annotation.body[0] : annotation.body;
1081
+ if (body && "value" in body) {
1082
+ return body.value;
1083
+ }
1084
+ return void 0;
1085
+ }
1086
+ function getTagCategory(annotation) {
1087
+ if (!isTag(annotation)) return void 0;
1088
+ const bodies = Array.isArray(annotation.body) ? annotation.body : [annotation.body];
1089
+ const taggingBody = bodies.find((b) => b && "purpose" in b && b.purpose === "tagging");
1090
+ if (taggingBody && "value" in taggingBody) {
1091
+ return taggingBody.value;
1092
+ }
1093
+ return void 0;
1094
+ }
1095
+ function getTagSchemaId(annotation) {
1096
+ if (!isTag(annotation)) return void 0;
1097
+ const bodies = Array.isArray(annotation.body) ? annotation.body : [annotation.body];
1098
+ const classifyingBody = bodies.find((b) => b && "purpose" in b && b.purpose === "classifying");
1099
+ if (classifyingBody && "value" in classifyingBody) {
1100
+ return classifyingBody.value;
1101
+ }
1102
+ return void 0;
1103
+ }
1104
+ function isStubReference(annotation) {
1105
+ return isReference(annotation) && !isBodyResolved(annotation.body);
1106
+ }
1107
+ function isResolvedReference(annotation) {
1108
+ return isReference(annotation) && isBodyResolved(annotation.body);
1109
+ }
1110
+ function getExactText(selector) {
1111
+ if (!selector) {
1112
+ return "";
1113
+ }
1114
+ const selectors = Array.isArray(selector) ? selector : [selector];
1115
+ const quoteSelector = selectors.find((s) => s.type === "TextQuoteSelector");
1116
+ if (quoteSelector) {
1117
+ return quoteSelector.exact;
1118
+ }
1119
+ return "";
1120
+ }
1121
+ function getAnnotationExactText(annotation) {
1122
+ const selector = getTargetSelector(annotation.target);
1123
+ return getExactText(selector);
1124
+ }
1125
+ function getPrimarySelector(selector) {
1126
+ if (Array.isArray(selector)) {
1127
+ if (selector.length === 0) {
1128
+ throw new Error("Empty selector array");
1129
+ }
1130
+ const first = selector[0];
1131
+ if (!first) {
1132
+ throw new Error("Invalid selector array");
1133
+ }
1134
+ return first;
1135
+ }
1136
+ return selector;
1137
+ }
1138
+ function getTextPositionSelector(selector) {
1139
+ if (!selector) return null;
1140
+ const selectors = Array.isArray(selector) ? selector : [selector];
1141
+ const found = selectors.find((s) => s.type === "TextPositionSelector");
1142
+ if (!found) return null;
1143
+ return found.type === "TextPositionSelector" ? found : null;
1144
+ }
1145
+ function getTextQuoteSelector(selector) {
1146
+ const selectors = Array.isArray(selector) ? selector : [selector];
1147
+ const found = selectors.find((s) => s.type === "TextQuoteSelector");
1148
+ if (!found) return null;
1149
+ return found.type === "TextQuoteSelector" ? found : null;
1150
+ }
1151
+ function getSvgSelector(selector) {
1152
+ if (!selector) return null;
1153
+ const selectors = Array.isArray(selector) ? selector : [selector];
1154
+ const found = selectors.find((s) => s.type === "SvgSelector");
1155
+ if (!found) return null;
1156
+ return found.type === "SvgSelector" ? found : null;
1157
+ }
1158
+ function validateSvgMarkup(svg) {
1159
+ if (!svg.includes('xmlns="http://www.w3.org/2000/svg"')) {
1160
+ return 'SVG must include xmlns="http://www.w3.org/2000/svg" attribute';
1161
+ }
1162
+ if (!svg.includes("<svg") || !svg.includes("</svg>")) {
1163
+ return "SVG must have opening and closing tags";
1164
+ }
1165
+ const shapeElements = ["rect", "circle", "ellipse", "polygon", "polyline", "path", "line"];
1166
+ const hasShape = shapeElements.some(
1167
+ (shape) => svg.includes(`<${shape}`) || svg.includes(`<${shape} `)
1168
+ );
1169
+ if (!hasShape) {
1170
+ return "SVG must contain at least one shape element (rect, circle, ellipse, polygon, polyline, path, or line)";
1171
+ }
1172
+ return null;
1173
+ }
1174
+ function extractBoundingBox(svg) {
1175
+ const viewBoxMatch = svg.match(/<svg[^>]*viewBox="([^"]+)"/);
1176
+ if (viewBoxMatch) {
1177
+ const values = viewBoxMatch[1].split(/\s+/).map(parseFloat);
1178
+ if (values.length === 4 && values.every((v) => !isNaN(v))) {
1179
+ return {
1180
+ x: values[0],
1181
+ y: values[1],
1182
+ width: values[2],
1183
+ height: values[3]
1184
+ };
1185
+ }
1186
+ }
1187
+ const svgTagMatch = svg.match(/<svg[^>]*>/);
1188
+ if (svgTagMatch) {
1189
+ const svgTag = svgTagMatch[0];
1190
+ const widthMatch = svgTag.match(/width="([^"]+)"/);
1191
+ const heightMatch = svgTag.match(/height="([^"]+)"/);
1192
+ if (widthMatch && heightMatch) {
1193
+ const width = parseFloat(widthMatch[1]);
1194
+ const height = parseFloat(heightMatch[1]);
1195
+ if (!isNaN(width) && !isNaN(height)) {
1196
+ return { x: 0, y: 0, width, height };
1197
+ }
1198
+ }
1199
+ }
1200
+ return null;
1201
+ }
1202
+
1203
+ // src/utils/events.ts
1204
+ function getAnnotationUriFromEvent(event) {
1205
+ const eventData = event.event;
1206
+ const payload = eventData.payload;
1207
+ if (!payload) {
1208
+ return null;
1209
+ }
1210
+ switch (eventData.type) {
1211
+ case "annotation.added":
1212
+ return payload.annotation?.id || null;
1213
+ case "annotation.removed":
1214
+ case "annotation.body.updated":
1215
+ if (payload.annotationId && eventData.resourceId) {
1216
+ try {
1217
+ const resourceUri2 = eventData.resourceId;
1218
+ const baseUrl2 = resourceUri2.substring(0, resourceUri2.lastIndexOf("/resources/"));
1219
+ return `${baseUrl2}/annotations/${payload.annotationId}`;
1220
+ } catch (e) {
1221
+ return null;
1222
+ }
1223
+ }
1224
+ return null;
1225
+ default:
1226
+ return null;
1227
+ }
1228
+ }
1229
+ function isEventRelatedToAnnotation(event, annotationUri2) {
1230
+ const eventAnnotationUri = getAnnotationUriFromEvent(event);
1231
+ return eventAnnotationUri === annotationUri2;
1232
+ }
1233
+ function isResourceEvent(event) {
1234
+ return event && typeof event.event === "object" && typeof event.event.id === "string" && typeof event.event.timestamp === "string" && typeof event.event.resourceId === "string" && typeof event.event.type === "string" && typeof event.metadata === "object" && typeof event.metadata.sequenceNumber === "number";
1235
+ }
1236
+ function formatEventType(type, t, payload) {
1237
+ switch (type) {
1238
+ case "resource.created":
1239
+ return t("resourceCreated");
1240
+ case "resource.cloned":
1241
+ return t("resourceCloned");
1242
+ case "resource.archived":
1243
+ return t("resourceArchived");
1244
+ case "resource.unarchived":
1245
+ return t("resourceUnarchived");
1246
+ case "annotation.added": {
1247
+ const motivation = payload?.annotation?.motivation;
1248
+ if (motivation === "highlighting") return t("highlightAdded");
1249
+ if (motivation === "linking") return t("referenceCreated");
1250
+ if (motivation === "assessing") return t("assessmentAdded");
1251
+ return t("annotationAdded");
1252
+ }
1253
+ case "annotation.removed": {
1254
+ return t("annotationRemoved");
1255
+ }
1256
+ case "annotation.body.updated": {
1257
+ return t("annotationBodyUpdated");
1258
+ }
1259
+ case "entitytag.added":
1260
+ return t("entitytagAdded");
1261
+ case "entitytag.removed":
1262
+ return t("entitytagRemoved");
1263
+ case "entitytype.added":
1264
+ return t("entitytypeAdded");
1265
+ case "job.completed":
1266
+ case "job.started":
1267
+ case "job.progress":
1268
+ case "job.failed":
1269
+ return t("jobEvent");
1270
+ default:
1271
+ const _exhaustiveCheck = type;
1272
+ return _exhaustiveCheck;
1273
+ }
1274
+ }
1275
+ function getEventEmoji(type, payload) {
1276
+ switch (type) {
1277
+ case "resource.created":
1278
+ case "resource.cloned":
1279
+ case "resource.archived":
1280
+ case "resource.unarchived":
1281
+ return "\u{1F4C4}";
1282
+ case "annotation.added": {
1283
+ const motivation = payload?.annotation?.motivation;
1284
+ if (motivation === "highlighting") return "\u{1F7E1}";
1285
+ if (motivation === "linking") return "\u{1F535}";
1286
+ if (motivation === "assessing") return "\u{1F534}";
1287
+ return "\u{1F4DD}";
1288
+ }
1289
+ case "annotation.removed": {
1290
+ return "\u{1F5D1}\uFE0F";
1291
+ }
1292
+ case "annotation.body.updated": {
1293
+ return "\u270F\uFE0F";
1294
+ }
1295
+ case "entitytag.added":
1296
+ case "entitytag.removed":
1297
+ return "\u{1F3F7}\uFE0F";
1298
+ case "entitytype.added":
1299
+ return "\u{1F3F7}\uFE0F";
1300
+ // Same emoji as entitytag (global entity type collection)
1301
+ case "job.completed":
1302
+ return "\u{1F517}";
1303
+ // Link emoji for linked document creation
1304
+ case "job.started":
1305
+ case "job.progress":
1306
+ return "\u2699\uFE0F";
1307
+ // Gear for job processing
1308
+ case "job.failed":
1309
+ return "\u274C";
1310
+ // X mark for failed jobs
1311
+ default:
1312
+ const _exhaustiveCheck = type;
1313
+ return _exhaustiveCheck;
1314
+ }
1315
+ }
1316
+ function formatRelativeTime(timestamp, t) {
1317
+ const date = new Date(timestamp);
1318
+ const now = /* @__PURE__ */ new Date();
1319
+ const diffMs = now.getTime() - date.getTime();
1320
+ const diffMins = Math.floor(diffMs / 6e4);
1321
+ const diffHours = Math.floor(diffMs / 36e5);
1322
+ const diffDays = Math.floor(diffMs / 864e5);
1323
+ if (diffMins < 1) return t("justNow");
1324
+ if (diffMins < 60) return t("minutesAgo", { count: diffMins });
1325
+ if (diffHours < 24) return t("hoursAgo", { count: diffHours });
1326
+ if (diffDays < 7) return t("daysAgo", { count: diffDays });
1327
+ return date.toLocaleDateString();
1328
+ }
1329
+ function truncateText(text, maxLength = 50) {
1330
+ const trimmed = text.trim();
1331
+ return trimmed.length > maxLength ? trimmed.substring(0, maxLength) + "..." : trimmed;
1332
+ }
1333
+ function getEventDisplayContent(event, annotations, allEvents) {
1334
+ const eventData = event.event;
1335
+ const payload = eventData.payload;
1336
+ switch (eventData.type) {
1337
+ case "resource.created":
1338
+ case "resource.cloned": {
1339
+ return { exact: payload.name, isQuoted: false, isTag: false };
1340
+ }
1341
+ // Unified annotation events
1342
+ case "annotation.body.updated": {
1343
+ const annotation = annotations.find(
1344
+ (a) => a.id.endsWith(`/annotations/${payload.annotationId}`)
1345
+ );
1346
+ if (annotation?.target) {
1347
+ try {
1348
+ const targetSelector = getTargetSelector(annotation.target);
1349
+ const exact = getExactText(targetSelector);
1350
+ if (exact) {
1351
+ return { exact: truncateText(exact), isQuoted: true, isTag: false };
1352
+ }
1353
+ } catch {
1354
+ }
1355
+ }
1356
+ return null;
1357
+ }
1358
+ case "annotation.removed": {
1359
+ const addedEvent = allEvents.find(
1360
+ (e) => e.event.type === "annotation.added" && e.event.payload.annotation?.id?.endsWith(`/annotations/${payload.annotationId}`)
1361
+ );
1362
+ if (addedEvent) {
1363
+ const addedPayload = addedEvent.event.payload;
1364
+ try {
1365
+ const exact = getExactText(addedPayload.annotation.target.selector);
1366
+ if (exact) {
1367
+ return { exact: truncateText(exact), isQuoted: true, isTag: false };
1368
+ }
1369
+ } catch {
1370
+ }
1371
+ }
1372
+ return null;
1373
+ }
1374
+ case "annotation.added": {
1375
+ try {
1376
+ const exact = getExactText(payload.annotation.target.selector);
1377
+ if (exact) {
1378
+ return { exact: truncateText(exact), isQuoted: true, isTag: false };
1379
+ }
1380
+ } catch {
1381
+ }
1382
+ return null;
1383
+ }
1384
+ case "entitytag.added":
1385
+ case "entitytag.removed": {
1386
+ return { exact: payload.entityType, isQuoted: false, isTag: true };
1387
+ }
1388
+ case "job.completed": {
1389
+ if (payload.annotationUri) {
1390
+ const annotation = annotations.find(
1391
+ (a) => a.id === payload.annotationUri
1392
+ );
1393
+ if (annotation?.target) {
1394
+ try {
1395
+ const targetSelector = getTargetSelector(annotation.target);
1396
+ const exact = getExactText(targetSelector);
1397
+ if (exact) {
1398
+ return { exact: truncateText(exact), isQuoted: true, isTag: false };
1399
+ }
1400
+ } catch {
1401
+ }
1402
+ }
1403
+ }
1404
+ return null;
1405
+ }
1406
+ default:
1407
+ return null;
1408
+ }
1409
+ }
1410
+ function getEventEntityTypes(event) {
1411
+ const eventData = event.event;
1412
+ if (eventData.type === "annotation.added") {
1413
+ const payload = eventData.payload;
1414
+ const motivation = payload?.annotation?.motivation;
1415
+ if (motivation === "linking") {
1416
+ return payload.annotation?.body?.entityTypes ?? [];
1417
+ }
1418
+ }
1419
+ return [];
1420
+ }
1421
+ function getResourceCreationDetails(event) {
1422
+ const eventData = event.event;
1423
+ const payload = eventData.payload;
1424
+ if (eventData.type === "resource.created") {
1425
+ return {
1426
+ type: "created",
1427
+ method: payload.creationMethod || "unknown",
1428
+ userId: eventData.userId,
1429
+ metadata: payload.metadata
1430
+ };
1431
+ }
1432
+ if (eventData.type === "resource.cloned") {
1433
+ return {
1434
+ type: "cloned",
1435
+ method: payload.creationMethod || "clone",
1436
+ userId: eventData.userId,
1437
+ sourceDocId: payload.parentResourceId,
1438
+ parentResourceId: payload.parentResourceId,
1439
+ metadata: payload.metadata
1440
+ };
1441
+ }
1442
+ return null;
1443
+ }
1444
+
1445
+ // src/utils/locales.ts
1446
+ var LOCALES = [
1447
+ { code: "ar", nativeName: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629", englishName: "Arabic" },
1448
+ { code: "bn", nativeName: "\u09AC\u09BE\u0982\u09B2\u09BE", englishName: "Bengali" },
1449
+ { code: "cs", nativeName: "\u010Ce\u0161tina", englishName: "Czech" },
1450
+ { code: "da", nativeName: "Dansk", englishName: "Danish" },
1451
+ { code: "de", nativeName: "Deutsch", englishName: "German" },
1452
+ { code: "el", nativeName: "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC", englishName: "Greek" },
1453
+ { code: "en", nativeName: "English", englishName: "English" },
1454
+ { code: "es", nativeName: "Espa\xF1ol", englishName: "Spanish" },
1455
+ { code: "fa", nativeName: "\u0641\u0627\u0631\u0633\u06CC", englishName: "Persian" },
1456
+ { code: "fi", nativeName: "Suomi", englishName: "Finnish" },
1457
+ { code: "fr", nativeName: "Fran\xE7ais", englishName: "French" },
1458
+ { code: "he", nativeName: "\u05E2\u05D1\u05E8\u05D9\u05EA", englishName: "Hebrew" },
1459
+ { code: "hi", nativeName: "\u0939\u093F\u0928\u094D\u0926\u0940", englishName: "Hindi" },
1460
+ { code: "id", nativeName: "Bahasa Indonesia", englishName: "Indonesian" },
1461
+ { code: "it", nativeName: "Italiano", englishName: "Italian" },
1462
+ { code: "ja", nativeName: "\u65E5\u672C\u8A9E", englishName: "Japanese" },
1463
+ { code: "ko", nativeName: "\uD55C\uAD6D\uC5B4", englishName: "Korean" },
1464
+ { code: "ms", nativeName: "Bahasa Melayu", englishName: "Malay" },
1465
+ { code: "nl", nativeName: "Nederlands", englishName: "Dutch" },
1466
+ { code: "no", nativeName: "Norsk", englishName: "Norwegian" },
1467
+ { code: "pl", nativeName: "Polski", englishName: "Polish" },
1468
+ { code: "pt", nativeName: "Portugu\xEAs", englishName: "Portuguese" },
1469
+ { code: "ro", nativeName: "Rom\xE2n\u0103", englishName: "Romanian" },
1470
+ { code: "sv", nativeName: "Svenska", englishName: "Swedish" },
1471
+ { code: "th", nativeName: "\u0E44\u0E17\u0E22", englishName: "Thai" },
1472
+ { code: "tr", nativeName: "T\xFCrk\xE7e", englishName: "Turkish" },
1473
+ { code: "uk", nativeName: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430", englishName: "Ukrainian" },
1474
+ { code: "vi", nativeName: "Ti\u1EBFng Vi\u1EC7t", englishName: "Vietnamese" },
1475
+ { code: "zh", nativeName: "\u4E2D\u6587", englishName: "Chinese" }
1476
+ ];
1477
+ var localeByCode = new Map(
1478
+ LOCALES.map((locale) => [locale.code.toLowerCase(), locale])
1479
+ );
1480
+ function getLocaleInfo(code) {
1481
+ if (!code) return void 0;
1482
+ return localeByCode.get(code.toLowerCase());
1483
+ }
1484
+ function getLocaleNativeName(code) {
1485
+ return getLocaleInfo(code)?.nativeName;
1486
+ }
1487
+ function getLocaleEnglishName(code) {
1488
+ return getLocaleInfo(code)?.englishName;
1489
+ }
1490
+ function formatLocaleDisplay(code) {
1491
+ if (!code) return void 0;
1492
+ const info = getLocaleInfo(code);
1493
+ if (!info) return code;
1494
+ return `${info.nativeName} (${code.toLowerCase()})`;
1495
+ }
1496
+ function getAllLocaleCodes() {
1497
+ return LOCALES.map((l) => l.code);
1498
+ }
1499
+
1500
+ // src/utils/resources.ts
1501
+ function getResourceId(resource) {
1502
+ if (!resource) return void 0;
1503
+ const fullId = resource["@id"];
1504
+ if (fullId.includes("/resources/")) {
1505
+ const parts = fullId.split("/resources/");
1506
+ const lastPart = parts[parts.length - 1];
1507
+ return lastPart || void 0;
1508
+ }
1509
+ return void 0;
1510
+ }
1511
+ function getPrimaryRepresentation(resource) {
1512
+ if (!resource?.representations) return void 0;
1513
+ const reps = Array.isArray(resource.representations) ? resource.representations : [resource.representations];
1514
+ return reps[0];
1515
+ }
1516
+ function getPrimaryMediaType(resource) {
1517
+ return getPrimaryRepresentation(resource)?.mediaType;
1518
+ }
1519
+ function getChecksum(resource) {
1520
+ return getPrimaryRepresentation(resource)?.checksum;
1521
+ }
1522
+ function getLanguage(resource) {
1523
+ return getPrimaryRepresentation(resource)?.language;
1524
+ }
1525
+
1526
+ // src/utils/validation.ts
1527
+ var JWTTokenSchema = {
1528
+ parse(token) {
1529
+ if (typeof token !== "string") {
1530
+ throw new Error("Token must be a string");
1531
+ }
1532
+ if (!token || token.length === 0) {
1533
+ throw new Error("Token is required");
1534
+ }
1535
+ const jwtRegex = /^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]*$/;
1536
+ if (!jwtRegex.test(token)) {
1537
+ throw new Error("Invalid JWT token format");
1538
+ }
1539
+ return token;
1540
+ },
1541
+ safeParse(token) {
1542
+ try {
1543
+ const validated = this.parse(token);
1544
+ return { success: true, data: validated };
1545
+ } catch (error) {
1546
+ return {
1547
+ success: false,
1548
+ error: error instanceof Error ? error.message : "Invalid JWT token"
1549
+ };
1550
+ }
1551
+ }
45
1552
  };
46
- Object.defineProperty(exports, "__esModule", { value: true });
47
- exports.getMimeCategory = exports.isTextMimeType = exports.isImageMimeType = exports.getExtensionForMimeType = exports.SSEClient = void 0;
48
- // Generated OpenAPI types and client
49
- __exportStar(require("./types"), exports);
50
- __exportStar(require("./client"), exports);
51
- var sse_1 = require("./sse");
52
- Object.defineProperty(exports, "SSEClient", { enumerable: true, get: function () { return sse_1.SSEClient; } });
53
- // Handwritten utilities
54
- __exportStar(require("./utils"), exports);
55
- var mime_utils_1 = require("./mime-utils");
56
- Object.defineProperty(exports, "getExtensionForMimeType", { enumerable: true, get: function () { return mime_utils_1.getExtensionForMimeType; } });
57
- Object.defineProperty(exports, "isImageMimeType", { enumerable: true, get: function () { return mime_utils_1.isImageMimeType; } });
58
- Object.defineProperty(exports, "isTextMimeType", { enumerable: true, get: function () { return mime_utils_1.isTextMimeType; } });
59
- Object.defineProperty(exports, "getMimeCategory", { enumerable: true, get: function () { return mime_utils_1.getMimeCategory; } });
60
- // All branded types (URIs, tokens, identifiers, etc.)
61
- __exportStar(require("./branded-types"), exports);
1553
+ function validateData(schema, data) {
1554
+ try {
1555
+ const validated = schema.parse(data);
1556
+ return { success: true, data: validated };
1557
+ } catch (error) {
1558
+ return {
1559
+ success: false,
1560
+ error: error instanceof Error ? error.message : "Validation failed"
1561
+ };
1562
+ }
1563
+ }
1564
+ function isValidEmail(email2) {
1565
+ if (email2.length < 1 || email2.length > 255) {
1566
+ return false;
1567
+ }
1568
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1569
+ return emailRegex.test(email2);
1570
+ }
1571
+
1572
+ // src/mime-utils.ts
1573
+ function getExtensionForMimeType(mimeType) {
1574
+ const map = {
1575
+ "text/plain": "txt",
1576
+ "text/markdown": "md",
1577
+ "image/png": "png",
1578
+ "image/jpeg": "jpg"
1579
+ };
1580
+ return map[mimeType] || "dat";
1581
+ }
1582
+ function isImageMimeType(mimeType) {
1583
+ return mimeType === "image/png" || mimeType === "image/jpeg";
1584
+ }
1585
+ function isTextMimeType(mimeType) {
1586
+ return mimeType === "text/plain" || mimeType === "text/markdown";
1587
+ }
1588
+ function getMimeCategory(mimeType) {
1589
+ if (isTextMimeType(mimeType)) {
1590
+ return "text";
1591
+ }
1592
+ if (isImageMimeType(mimeType)) {
1593
+ return "image";
1594
+ }
1595
+ return "unsupported";
1596
+ }
1597
+
1598
+ export { APIError, JWTTokenSchema, LOCALES, SSEClient, SemiontApiClient, accessToken, annotationUri, authCode, baseUrl, cloneToken, email, entityType, extractBoundingBox, formatEventType, formatLocaleDisplay, formatRelativeTime, getAllLocaleCodes, getAnnotationExactText, getAnnotationUriFromEvent, getBodySource, getBodyType, getChecksum, getCommentText, getEntityTypes, getEventDisplayContent, getEventEmoji, getEventEntityTypes, getExactText, getExtensionForMimeType, getLanguage, getLocaleEnglishName, getLocaleInfo, getLocaleNativeName, getMimeCategory, getPrimaryMediaType, getPrimaryRepresentation, getPrimarySelector, getResourceCreationDetails, getResourceId, getSvgSelector, getTagCategory, getTagSchemaId, getTargetSelector, getTargetSource, getTextPositionSelector, getTextQuoteSelector, googleCredential, hasTargetSelector, isAssessment, isBodyResolved, isComment, isEventRelatedToAnnotation, isHighlight, isImageMimeType, isReference, isResolvedReference, isResourceEvent, isStubReference, isTag, isTextMimeType, isValidEmail, jobId, mcpToken, refreshToken, resourceAnnotationUri, resourceUri, searchQuery, userDID, validateData, validateSvgMarkup };
1599
+ //# sourceMappingURL=index.js.map
62
1600
  //# sourceMappingURL=index.js.map