@semiont/api-client 0.2.46 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import ky from 'ky';
2
2
  import { merge, firstValueFrom } from 'rxjs';
3
3
  import { filter, map, take, timeout } from 'rxjs/operators';
4
- import { resourceUri } from '@semiont/core';
5
4
  export { getFragmentSelector, getSvgSelector, getTextPositionSelector, validateSvgMarkup } from '@semiont/core';
6
5
 
7
6
  // src/client.ts
@@ -168,17 +167,6 @@ var SSEClient = class {
168
167
  }
169
168
  return headers;
170
169
  }
171
- /**
172
- * Extract resource ID from URI
173
- *
174
- * Handles both full URIs and plain IDs:
175
- * - 'http://localhost:4000/resources/doc-123' -> 'doc-123'
176
- * - 'doc-123' -> 'doc-123'
177
- */
178
- extractId(uri) {
179
- const parts = uri.split("/");
180
- return parts[parts.length - 1];
181
- }
182
170
  /**
183
171
  * Detect annotations in a resource (streaming)
184
172
  *
@@ -215,8 +203,7 @@ var SSEClient = class {
215
203
  * ```
216
204
  */
217
205
  annotateReferences(resourceId, request, options) {
218
- const id = this.extractId(resourceId);
219
- const url = `${this.baseUrl}/resources/${id}/annotate-references-stream`;
206
+ const url = `${this.baseUrl}/resources/${resourceId}/annotate-references-stream`;
220
207
  return createSSEStream(
221
208
  url,
222
209
  {
@@ -272,9 +259,7 @@ var SSEClient = class {
272
259
  * ```
273
260
  */
274
261
  yieldResourceFromAnnotation(resourceId, annotationId, request, options) {
275
- const resId = this.extractId(resourceId);
276
- const annId = this.extractId(annotationId);
277
- const url = `${this.baseUrl}/resources/${resId}/annotations/${annId}/yield-resource-stream`;
262
+ const url = `${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/yield-resource-stream`;
278
263
  return createSSEStream(
279
264
  url,
280
265
  {
@@ -328,8 +313,7 @@ var SSEClient = class {
328
313
  * ```
329
314
  */
330
315
  annotateHighlights(resourceId, request = {}, options) {
331
- const id = this.extractId(resourceId);
332
- const url = `${this.baseUrl}/resources/${id}/annotate-highlights-stream`;
316
+ const url = `${this.baseUrl}/resources/${resourceId}/annotate-highlights-stream`;
333
317
  return createSSEStream(
334
318
  url,
335
319
  {
@@ -383,8 +367,7 @@ var SSEClient = class {
383
367
  * ```
384
368
  */
385
369
  annotateAssessments(resourceId, request = {}, options) {
386
- const id = this.extractId(resourceId);
387
- const url = `${this.baseUrl}/resources/${id}/annotate-assessments-stream`;
370
+ const url = `${this.baseUrl}/resources/${resourceId}/annotate-assessments-stream`;
388
371
  return createSSEStream(
389
372
  url,
390
373
  {
@@ -442,8 +425,7 @@ var SSEClient = class {
442
425
  * ```
443
426
  */
444
427
  annotateComments(resourceId, request = {}, options) {
445
- const id = this.extractId(resourceId);
446
- const url = `${this.baseUrl}/resources/${id}/annotate-comments-stream`;
428
+ const url = `${this.baseUrl}/resources/${resourceId}/annotate-comments-stream`;
447
429
  return createSSEStream(
448
430
  url,
449
431
  {
@@ -502,8 +484,7 @@ var SSEClient = class {
502
484
  * ```
503
485
  */
504
486
  annotateTags(resourceId, request, options) {
505
- const id = this.extractId(resourceId);
506
- const url = `${this.baseUrl}/resources/${id}/annotate-tags-stream`;
487
+ const url = `${this.baseUrl}/resources/${resourceId}/annotate-tags-stream`;
507
488
  return createSSEStream(
508
489
  url,
509
490
  {
@@ -521,6 +502,35 @@ var SSEClient = class {
521
502
  this.logger
522
503
  );
523
504
  }
505
+ /**
506
+ * Search for binding candidates (streaming)
507
+ *
508
+ * Bridges bind:search-requested to the backend Binder actor via SSE.
509
+ * Results emit as bind:search-results on the browser EventBus.
510
+ *
511
+ * @param resourceId - Resource the annotation belongs to
512
+ * @param request - Search configuration (referenceId, context, limit)
513
+ * @param options - Request options (auth token, eventBus)
514
+ * @returns SSE stream controller
515
+ */
516
+ bindSearch(resourceId, request, options) {
517
+ const url = `${this.baseUrl}/resources/${resourceId}/bind-search-stream`;
518
+ return createSSEStream(
519
+ url,
520
+ {
521
+ method: "POST",
522
+ headers: this.getHeaders(options.auth),
523
+ body: JSON.stringify(request)
524
+ },
525
+ {
526
+ progressEvents: [],
527
+ completeEvent: "bind:search-results",
528
+ errorEvent: "bind:search-failed",
529
+ eventBus: options.eventBus
530
+ },
531
+ this.logger
532
+ );
533
+ }
524
534
  /**
525
535
  * Subscribe to resource events (long-lived stream)
526
536
  *
@@ -556,8 +566,7 @@ var SSEClient = class {
556
566
  * ```
557
567
  */
558
568
  resourceEvents(resourceId, options) {
559
- const id = this.extractId(resourceId);
560
- const url = `${this.baseUrl}/resources/${id}/events/stream`;
569
+ const url = `${this.baseUrl}/resources/${resourceId}/events/stream`;
561
570
  const stream = createSSEStream(
562
571
  url,
563
572
  {
@@ -839,8 +848,8 @@ var SemiontApiClient = class {
839
848
  auth: options?.auth
840
849
  }).json();
841
850
  }
842
- async getResource(resourceUri2, options) {
843
- return this.http.get(resourceUri2, {
851
+ async getResource(id, options) {
852
+ return this.http.get(`${this.baseUrl}/resources/${id}`, {
844
853
  ...options,
845
854
  auth: options?.auth
846
855
  }).json();
@@ -867,8 +876,8 @@ var SemiontApiClient = class {
867
876
  * const { data, contentType } = await client.getResourceRepresentation(rUri, { accept: 'application/pdf', auth: token });
868
877
  * ```
869
878
  */
870
- async getResourceRepresentation(resourceUri2, options) {
871
- const response = await this.http.get(resourceUri2, {
879
+ async getResourceRepresentation(id, options) {
880
+ const response = await this.http.get(`${this.baseUrl}/resources/${id}`, {
872
881
  headers: {
873
882
  Accept: options?.accept || "text/plain"
874
883
  },
@@ -914,8 +923,8 @@ var SemiontApiClient = class {
914
923
  * }
915
924
  * ```
916
925
  */
917
- async getResourceRepresentationStream(resourceUri2, options) {
918
- const response = await this.http.get(resourceUri2, {
926
+ async getResourceRepresentationStream(id, options) {
927
+ const response = await this.http.get(`${this.baseUrl}/resources/${id}`, {
919
928
  headers: {
920
929
  Accept: options?.accept || "text/plain"
921
930
  },
@@ -938,46 +947,45 @@ var SemiontApiClient = class {
938
947
  auth: options?.auth
939
948
  }).json();
940
949
  }
941
- async updateResource(resourceUri2, data, options) {
942
- await this.http.patch(resourceUri2, {
950
+ async updateResource(id, data, options) {
951
+ await this.http.patch(`${this.baseUrl}/resources/${id}`, {
943
952
  json: data,
944
953
  ...options,
945
954
  auth: options?.auth
946
955
  }).text();
947
956
  }
948
- async getResourceEvents(resourceUri2, options) {
949
- return this.http.get(`${resourceUri2}/events`, {
957
+ async getResourceEvents(id, options) {
958
+ return this.http.get(`${this.baseUrl}/resources/${id}/events`, {
950
959
  ...options,
951
960
  auth: options?.auth
952
961
  }).json();
953
962
  }
954
- async getResourceAnnotations(resourceUri2, options) {
955
- return this.http.get(`${resourceUri2}/annotations`, {
963
+ async getResourceAnnotations(id, options) {
964
+ return this.http.get(`${this.baseUrl}/resources/${id}/annotations`, {
956
965
  ...options,
957
966
  auth: options?.auth
958
967
  }).json();
959
968
  }
960
- async getAnnotationLLMContext(resourceUri2, annotationId, options) {
969
+ async getAnnotationLLMContext(resourceId, annotationId, options) {
961
970
  const searchParams = new URLSearchParams();
962
971
  if (options?.contextWindow) {
963
972
  searchParams.append("contextWindow", options.contextWindow.toString());
964
973
  }
965
974
  return this.http.get(
966
- `${resourceUri2}/annotations/${annotationId}/llm-context`,
975
+ `${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/llm-context`,
967
976
  {
968
977
  searchParams,
969
978
  auth: options?.auth
970
979
  }
971
980
  ).json();
972
981
  }
973
- async getResourceReferencedBy(resourceUri2, options) {
974
- return this.http.get(`${resourceUri2}/referenced-by`, {
982
+ async getResourceReferencedBy(id, options) {
983
+ return this.http.get(`${this.baseUrl}/resources/${id}/referenced-by`, {
975
984
  ...options,
976
985
  auth: options?.auth
977
986
  }).json();
978
987
  }
979
- async generateCloneToken(resourceUri2, options) {
980
- const id = resourceUri2.split("/").pop();
988
+ async generateCloneToken(id, options) {
981
989
  return this.http.post(`${this.baseUrl}/resources/${id}/clone-with-token`, {
982
990
  ...options,
983
991
  auth: options?.auth
@@ -999,52 +1007,53 @@ var SemiontApiClient = class {
999
1007
  // ============================================================================
1000
1008
  // ANNOTATIONS
1001
1009
  // ============================================================================
1002
- async createAnnotation(resourceUri2, data, options) {
1003
- return this.http.post(`${resourceUri2}/annotations`, {
1010
+ async createAnnotation(id, data, options) {
1011
+ return this.http.post(`${this.baseUrl}/resources/${id}/annotations`, {
1004
1012
  json: data,
1005
1013
  ...options,
1006
1014
  auth: options?.auth
1007
1015
  }).json();
1008
1016
  }
1009
- async getAnnotation(annotationUri, options) {
1010
- return this.http.get(annotationUri, {
1017
+ async getAnnotation(id, options) {
1018
+ return this.http.get(`${this.baseUrl}/annotations/${id}`, {
1011
1019
  ...options,
1012
1020
  auth: options?.auth
1013
1021
  }).json();
1014
1022
  }
1015
- async getResourceAnnotation(annotationUri, options) {
1016
- return this.http.get(annotationUri, {
1023
+ async getResourceAnnotation(resourceId, annotationId, options) {
1024
+ return this.http.get(`${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}`, {
1017
1025
  ...options,
1018
1026
  auth: options?.auth
1019
1027
  }).json();
1020
1028
  }
1021
- async listAnnotations(resourceUri2, motivation, options) {
1029
+ async listAnnotations(id, motivation, options) {
1022
1030
  const searchParams = new URLSearchParams();
1023
1031
  if (motivation) searchParams.append("motivation", motivation);
1024
- return this.http.get(`${resourceUri2}/annotations`, {
1032
+ return this.http.get(`${this.baseUrl}/resources/${id}/annotations`, {
1025
1033
  searchParams,
1026
1034
  ...options,
1027
1035
  auth: options?.auth
1028
1036
  }).json();
1029
1037
  }
1030
- async deleteAnnotation(annotationUri, options) {
1031
- await this.http.delete(annotationUri, {
1038
+ async deleteAnnotation(resourceId, annotationId, options) {
1039
+ await this.http.delete(`${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}`, {
1032
1040
  ...options,
1033
1041
  auth: options?.auth
1034
1042
  });
1035
1043
  }
1036
- async updateAnnotationBody(annotationUri, data, options) {
1037
- await this.http.put(`${annotationUri}/body`, {
1044
+ async updateAnnotationBody(resourceId, annotationId, data, options) {
1045
+ await this.http.put(`${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/body`, {
1038
1046
  json: data,
1039
1047
  ...options,
1040
1048
  auth: options?.auth
1041
1049
  });
1042
1050
  }
1043
- async getAnnotationHistory(annotationUri, options) {
1051
+ async getAnnotationHistory(resourceId, annotationId, options) {
1052
+ const url = `${this.baseUrl}/resources/${resourceId}/annotations/${annotationId}/history`;
1044
1053
  if (options) {
1045
- return this.http.get(`${annotationUri}/history`, options).json();
1054
+ return this.http.get(url, options).json();
1046
1055
  }
1047
- return this.http.get(`${annotationUri}/history`).json();
1056
+ return this.http.get(url).json();
1048
1057
  }
1049
1058
  // ============================================================================
1050
1059
  // ENTITY TYPES
@@ -1102,6 +1111,113 @@ var SemiontApiClient = class {
1102
1111
  }).json();
1103
1112
  }
1104
1113
  // ============================================================================
1114
+ // ADMIN — EXCHANGE (Backup/Restore)
1115
+ // ============================================================================
1116
+ /**
1117
+ * Create a backup of the knowledge base. Returns raw Response for streaming download.
1118
+ * Caller should use response.blob() to trigger a file download.
1119
+ */
1120
+ async backupKnowledgeBase(options) {
1121
+ return fetch(`${this.baseUrl}/api/admin/exchange/backup`, {
1122
+ method: "POST",
1123
+ headers: {
1124
+ "Content-Type": "application/json",
1125
+ ...options?.auth ? { Authorization: `Bearer ${options.auth}` } : {}
1126
+ }
1127
+ });
1128
+ }
1129
+ /**
1130
+ * Restore knowledge base from a backup file. Parses SSE progress events and calls onProgress.
1131
+ * Returns the final SSE event (phase: 'complete' or 'error').
1132
+ */
1133
+ async restoreKnowledgeBase(file, options) {
1134
+ const formData = new FormData();
1135
+ formData.append("file", file);
1136
+ const response = await fetch(`${this.baseUrl}/api/admin/exchange/restore`, {
1137
+ method: "POST",
1138
+ headers: {
1139
+ ...options?.auth ? { Authorization: `Bearer ${options.auth}` } : {}
1140
+ },
1141
+ body: formData
1142
+ });
1143
+ if (!response.ok) {
1144
+ throw new Error(`Restore failed: ${response.status} ${response.statusText}`);
1145
+ }
1146
+ const reader = response.body.getReader();
1147
+ const decoder = new TextDecoder();
1148
+ let buffer = "";
1149
+ let finalResult = { phase: "unknown" };
1150
+ while (true) {
1151
+ const { done, value } = await reader.read();
1152
+ if (done) break;
1153
+ buffer += decoder.decode(value, { stream: true });
1154
+ const lines = buffer.split("\n");
1155
+ buffer = lines.pop();
1156
+ for (const line of lines) {
1157
+ if (line.startsWith("data: ")) {
1158
+ const event = JSON.parse(line.slice(6));
1159
+ options?.onProgress?.(event);
1160
+ finalResult = event;
1161
+ }
1162
+ }
1163
+ }
1164
+ return finalResult;
1165
+ }
1166
+ // ============================================================================
1167
+ // ADMIN — EXCHANGE (Linked Data Export/Import)
1168
+ // ============================================================================
1169
+ /**
1170
+ * Export the knowledge base as a JSON-LD Linked Data archive. Returns raw Response for streaming download.
1171
+ * Caller should use response.blob() to trigger a file download.
1172
+ */
1173
+ async exportKnowledgeBase(params, options) {
1174
+ const query = params?.includeArchived ? "?includeArchived=true" : "";
1175
+ return fetch(`${this.baseUrl}/api/moderate/exchange/export${query}`, {
1176
+ method: "POST",
1177
+ headers: {
1178
+ "Content-Type": "application/json",
1179
+ ...options?.auth ? { Authorization: `Bearer ${options.auth}` } : {}
1180
+ }
1181
+ });
1182
+ }
1183
+ /**
1184
+ * Import a JSON-LD Linked Data archive into the knowledge base. Parses SSE progress events and calls onProgress.
1185
+ * Returns the final SSE event (phase: 'complete' or 'error').
1186
+ */
1187
+ async importKnowledgeBase(file, options) {
1188
+ const formData = new FormData();
1189
+ formData.append("file", file);
1190
+ const response = await fetch(`${this.baseUrl}/api/moderate/exchange/import`, {
1191
+ method: "POST",
1192
+ headers: {
1193
+ ...options?.auth ? { Authorization: `Bearer ${options.auth}` } : {}
1194
+ },
1195
+ body: formData
1196
+ });
1197
+ if (!response.ok) {
1198
+ throw new Error(`Import failed: ${response.status} ${response.statusText}`);
1199
+ }
1200
+ const reader = response.body.getReader();
1201
+ const decoder = new TextDecoder();
1202
+ let buffer = "";
1203
+ let finalResult = { phase: "unknown" };
1204
+ while (true) {
1205
+ const { done, value } = await reader.read();
1206
+ if (done) break;
1207
+ buffer += decoder.decode(value, { stream: true });
1208
+ const lines = buffer.split("\n");
1209
+ buffer = lines.pop();
1210
+ for (const line of lines) {
1211
+ if (line.startsWith("data: ")) {
1212
+ const event = JSON.parse(line.slice(6));
1213
+ options?.onProgress?.(event);
1214
+ finalResult = event;
1215
+ }
1216
+ }
1217
+ }
1218
+ return finalResult;
1219
+ }
1220
+ // ============================================================================
1105
1221
  // JOB STATUS
1106
1222
  // ============================================================================
1107
1223
  async getJobStatus(id, options) {
@@ -1137,13 +1253,13 @@ var SemiontApiClient = class {
1137
1253
  // ============================================================================
1138
1254
  // LLM CONTEXT
1139
1255
  // ============================================================================
1140
- async getResourceLLMContext(resourceUri2, options) {
1256
+ async getResourceLLMContext(id, options) {
1141
1257
  const searchParams = new URLSearchParams();
1142
1258
  if (options?.depth !== void 0) searchParams.append("depth", options.depth.toString());
1143
1259
  if (options?.maxResources !== void 0) searchParams.append("maxResources", options.maxResources.toString());
1144
1260
  if (options?.includeContent !== void 0) searchParams.append("includeContent", options.includeContent.toString());
1145
1261
  if (options?.includeSummary !== void 0) searchParams.append("includeSummary", options.includeSummary.toString());
1146
- return this.http.get(`${resourceUri2}/llm-context`, {
1262
+ return this.http.get(`${this.baseUrl}/resources/${id}/llm-context`, {
1147
1263
  searchParams,
1148
1264
  auth: options?.auth
1149
1265
  }).json();
@@ -1336,7 +1452,7 @@ var EventBusClient = class {
1336
1452
  // ========================================================================
1337
1453
  // Gather Flow — LLM context
1338
1454
  // ========================================================================
1339
- async getAnnotationLLMContext(annotationUri, resourceUri2, options) {
1455
+ async getAnnotationLLMContext(annotationId, resourceId, options) {
1340
1456
  const correlationId = crypto.randomUUID();
1341
1457
  const result$ = merge(
1342
1458
  this.eventBus.get("gather:complete").pipe(
@@ -1351,8 +1467,8 @@ var EventBusClient = class {
1351
1467
  const resultPromise = firstValueFrom(result$);
1352
1468
  this.eventBus.get("gather:requested").next({
1353
1469
  correlationId,
1354
- annotationUri,
1355
- resourceUri: resourceUri2,
1470
+ annotationId,
1471
+ resourceId,
1356
1472
  options
1357
1473
  });
1358
1474
  const result = await resultPromise;
@@ -1361,7 +1477,7 @@ var EventBusClient = class {
1361
1477
  }
1362
1478
  return result.response;
1363
1479
  }
1364
- async getResourceLLMContext(resourceUri2, options) {
1480
+ async getResourceLLMContext(resourceId, options) {
1365
1481
  const correlationId = crypto.randomUUID();
1366
1482
  const result$ = merge(
1367
1483
  this.eventBus.get("gather:resource-complete").pipe(
@@ -1376,7 +1492,7 @@ var EventBusClient = class {
1376
1492
  const resultPromise = firstValueFrom(result$);
1377
1493
  this.eventBus.get("gather:resource-requested").next({
1378
1494
  correlationId,
1379
- resourceUri: resourceUri2,
1495
+ resourceId,
1380
1496
  options
1381
1497
  });
1382
1498
  const result = await resultPromise;
@@ -1405,7 +1521,24 @@ var EventBusClient = class {
1405
1521
  this.eventBus.get("bind:search-requested").next({
1406
1522
  correlationId,
1407
1523
  referenceId,
1408
- searchTerm
1524
+ context: {
1525
+ annotation: {
1526
+ "@context": "http://www.w3.org/ns/anno.jsonld",
1527
+ type: "Annotation",
1528
+ id: referenceId,
1529
+ motivation: "linking",
1530
+ target: referenceId,
1531
+ body: []
1532
+ },
1533
+ sourceResource: {
1534
+ "@context": "https://schema.org",
1535
+ "@id": referenceId,
1536
+ name: searchTerm,
1537
+ format: "text/plain",
1538
+ representations: []
1539
+ },
1540
+ sourceContext: { selected: searchTerm }
1541
+ }
1409
1542
  });
1410
1543
  const result = await resultPromise;
1411
1544
  if (!result.ok) {
@@ -1421,7 +1554,7 @@ function getBodySource(body) {
1421
1554
  const itemType = item.type;
1422
1555
  const itemSource = item.source;
1423
1556
  if (itemType === "SpecificResource" && typeof itemSource === "string") {
1424
- return resourceUri(itemSource);
1557
+ return itemSource;
1425
1558
  }
1426
1559
  }
1427
1560
  }
@@ -1431,7 +1564,7 @@ function getBodySource(body) {
1431
1564
  const bodyType = body.type;
1432
1565
  const bodySource = body.source;
1433
1566
  if (bodyType === "SpecificResource" && typeof bodySource === "string") {
1434
- return resourceUri(bodySource);
1567
+ return bodySource;
1435
1568
  }
1436
1569
  }
1437
1570
  return null;
@@ -1462,9 +1595,9 @@ function isBodyResolved(body) {
1462
1595
  }
1463
1596
  function getTargetSource(target) {
1464
1597
  if (typeof target === "string") {
1465
- return resourceUri(target);
1598
+ return target;
1466
1599
  }
1467
- return resourceUri(target.source);
1600
+ return target.source;
1468
1601
  }
1469
1602
  function getTargetSelector(target) {
1470
1603
  if (typeof target === "string") {
@@ -1771,13 +1904,7 @@ function getAllLocaleCodes() {
1771
1904
  // src/utils/resources.ts
1772
1905
  function getResourceId(resource) {
1773
1906
  if (!resource) return void 0;
1774
- const fullId = resource["@id"];
1775
- if (fullId.includes("/resources/")) {
1776
- const parts = fullId.split("/resources/");
1777
- const lastPart = parts[parts.length - 1];
1778
- return lastPart || void 0;
1779
- }
1780
- return void 0;
1907
+ return resource["@id"] || void 0;
1781
1908
  }
1782
1909
  function getPrimaryRepresentation(resource) {
1783
1910
  if (!resource?.representations) return void 0;