@semiont/sdk 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -22
- package/dist/index.d.ts +450 -581
- package/dist/index.js +353 -1020
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { SemiontError, annotationId, resourceId, email, googleCredential, refreshToken, EventBus, baseUrl, accessToken,
|
|
1
|
+
import { SemiontError, annotationId, resourceId, email, googleCredential, refreshToken, EventBus, baseUrl, accessToken, searchQuery } from '@semiont/core';
|
|
2
2
|
export { SemiontError, accessToken, annotationId, baseUrl, entityType, refreshToken, resourceId, userId } from '@semiont/core';
|
|
3
|
-
import { Observable, lastValueFrom, firstValueFrom, merge, TimeoutError, throwError,
|
|
3
|
+
import { Observable, lastValueFrom, firstValueFrom, merge, TimeoutError, throwError, map as map$1, BehaviorSubject, Subject, Subscription, of, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs';
|
|
4
4
|
export { firstValueFrom, lastValueFrom } from 'rxjs';
|
|
5
|
-
import { filter, map, take, timeout, catchError, takeUntil, startWith, debounceTime, distinctUntilChanged
|
|
6
|
-
import { HttpTransport, HttpContentTransport,
|
|
7
|
-
export { APIError,
|
|
5
|
+
import { filter, map, take, timeout, catchError, takeUntil, startWith, debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
|
|
6
|
+
import { HttpTransport, HttpContentTransport, APIError } from '@semiont/api-client';
|
|
7
|
+
export { APIError, HttpContentTransport, HttpTransport } from '@semiont/api-client';
|
|
8
8
|
|
|
9
9
|
// src/client.ts
|
|
10
10
|
var StreamObservable = class _StreamObservable extends Observable {
|
|
@@ -28,7 +28,7 @@ var CacheObservable = class _CacheObservable extends Observable {
|
|
|
28
28
|
* Observable per key (its B4 contract), so this preserves that contract
|
|
29
29
|
* through the awaitable wrapping. Without the memo, every public-method
|
|
30
30
|
* call would produce a fresh wrapper and break referential-equality
|
|
31
|
-
* guarantees that
|
|
31
|
+
* guarantees that hook-style reactive consumers depend on.
|
|
32
32
|
*
|
|
33
33
|
* Backed by a `WeakMap`, so wrappers are GC'd when their source is.
|
|
34
34
|
*/
|
|
@@ -42,6 +42,17 @@ var CacheObservable = class _CacheObservable extends Observable {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
var wrapperCache = /* @__PURE__ */ new WeakMap();
|
|
45
|
+
var UploadObservable = class extends Observable {
|
|
46
|
+
then(onfulfilled, onrejected) {
|
|
47
|
+
return lastValueFrom(this).then((v) => {
|
|
48
|
+
if (v.phase !== "finished") {
|
|
49
|
+
throw new Error(`UploadObservable resolved on a non-finished event: ${v.phase}`);
|
|
50
|
+
}
|
|
51
|
+
const result = { resourceId: v.resourceId };
|
|
52
|
+
return onfulfilled ? onfulfilled(result) : result;
|
|
53
|
+
}, onrejected);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
45
56
|
var BusRequestError = class extends SemiontError {
|
|
46
57
|
constructor(message, code, details) {
|
|
47
58
|
super(message, code, details);
|
|
@@ -117,7 +128,7 @@ function createCache(fetchFn) {
|
|
|
117
128
|
if (!obs) {
|
|
118
129
|
obs = store$.pipe(
|
|
119
130
|
map$1((m) => m.get(key)),
|
|
120
|
-
distinctUntilChanged()
|
|
131
|
+
distinctUntilChanged$1()
|
|
121
132
|
);
|
|
122
133
|
obsCache.set(key, obs);
|
|
123
134
|
}
|
|
@@ -499,7 +510,7 @@ var BrowseNamespace = class {
|
|
|
499
510
|
this.on("yield:update-ok", this.onYieldResourceMutated);
|
|
500
511
|
this.on("mark:archived", this.onArchiveToggled);
|
|
501
512
|
this.on("mark:unarchived", this.onArchiveToggled);
|
|
502
|
-
this.on("
|
|
513
|
+
this.on("frame:entity-type-added", () => this.invalidateEntityTypes());
|
|
503
514
|
}
|
|
504
515
|
};
|
|
505
516
|
var MarkNamespace = class {
|
|
@@ -507,7 +518,8 @@ var MarkNamespace = class {
|
|
|
507
518
|
this.transport = transport;
|
|
508
519
|
this.bus = bus;
|
|
509
520
|
}
|
|
510
|
-
async annotation(
|
|
521
|
+
async annotation(input) {
|
|
522
|
+
const resourceId2 = resourceId(input.target.source);
|
|
511
523
|
const result = await busRequest(
|
|
512
524
|
this.transport,
|
|
513
525
|
"mark:create-request",
|
|
@@ -520,14 +532,6 @@ var MarkNamespace = class {
|
|
|
520
532
|
async delete(resourceId2, annotationId2) {
|
|
521
533
|
await this.transport.emit("mark:delete", { annotationId: annotationId2, resourceId: resourceId2 });
|
|
522
534
|
}
|
|
523
|
-
async entityType(type) {
|
|
524
|
-
await this.transport.emit("mark:add-entity-type", { tag: type });
|
|
525
|
-
}
|
|
526
|
-
async entityTypes(types) {
|
|
527
|
-
for (const tag of types) {
|
|
528
|
-
await this.transport.emit("mark:add-entity-type", { tag });
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
535
|
async archive(resourceId2) {
|
|
532
536
|
await this.transport.emit("mark:archive", { resourceId: resourceId2 });
|
|
533
537
|
}
|
|
@@ -682,6 +686,7 @@ var MarkNamespace = class {
|
|
|
682
686
|
if (options.density !== void 0) params.density = options.density;
|
|
683
687
|
if (options.tone !== void 0) params.tone = options.tone;
|
|
684
688
|
if (options.language !== void 0) params.language = options.language;
|
|
689
|
+
if (options.sourceLanguage !== void 0) params.sourceLanguage = options.sourceLanguage;
|
|
685
690
|
if (options.schemaId !== void 0) params.schemaId = options.schemaId;
|
|
686
691
|
if (options.categories !== void 0) params.categories = options.categories;
|
|
687
692
|
return busRequest(
|
|
@@ -717,7 +722,7 @@ var GatherNamespace = class {
|
|
|
717
722
|
this.transport = transport;
|
|
718
723
|
this.bus = bus;
|
|
719
724
|
}
|
|
720
|
-
annotation(
|
|
725
|
+
annotation(resourceId2, annotationId2, options) {
|
|
721
726
|
return new StreamObservable((subscriber) => {
|
|
722
727
|
const correlationId = crypto.randomUUID();
|
|
723
728
|
const complete$ = this.bus.get("gather:complete").pipe(
|
|
@@ -809,22 +814,53 @@ var YieldNamespace = class {
|
|
|
809
814
|
this.bus = bus;
|
|
810
815
|
this.content = content;
|
|
811
816
|
}
|
|
812
|
-
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
817
|
+
resource(data) {
|
|
818
|
+
const totalBytes = typeof Buffer !== "undefined" && data.file instanceof Buffer ? data.file.length : data.file.size;
|
|
819
|
+
return new UploadObservable((subscriber) => {
|
|
820
|
+
subscriber.next({ phase: "started", totalBytes });
|
|
821
|
+
let cancelled = false;
|
|
822
|
+
const abortController = new AbortController();
|
|
823
|
+
this.content.putBinary(
|
|
824
|
+
{
|
|
825
|
+
name: data.name,
|
|
826
|
+
file: data.file,
|
|
827
|
+
format: data.format,
|
|
828
|
+
storageUri: data.storageUri,
|
|
829
|
+
...data.entityTypes ? { entityTypes: data.entityTypes } : {},
|
|
830
|
+
...data.language ? { language: data.language } : {},
|
|
831
|
+
...data.creationMethod ? { creationMethod: data.creationMethod } : {},
|
|
832
|
+
...data.sourceAnnotationId ? { sourceAnnotationId: data.sourceAnnotationId } : {},
|
|
833
|
+
...data.sourceResourceId ? { sourceResourceId: data.sourceResourceId } : {},
|
|
834
|
+
...data.generationPrompt ? { generationPrompt: data.generationPrompt } : {},
|
|
835
|
+
...data.generator ? { generator: data.generator } : {},
|
|
836
|
+
...data.isDraft !== void 0 ? { isDraft: data.isDraft } : {}
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
// Byte-progress hook. Honored by `HttpContentTransport`'s XHR
|
|
840
|
+
// path; ignored by ky-path uploads (no `onProgress` consumer)
|
|
841
|
+
// and by `LocalContentTransport` (no wire to observe).
|
|
842
|
+
onProgress: ({ bytesUploaded, totalBytes: txTotal }) => {
|
|
843
|
+
if (cancelled) return;
|
|
844
|
+
const total = txTotal > 0 ? txTotal : totalBytes;
|
|
845
|
+
subscriber.next({ phase: "progress", bytesUploaded, totalBytes: total });
|
|
846
|
+
},
|
|
847
|
+
signal: abortController.signal
|
|
848
|
+
}
|
|
849
|
+
).then((result) => {
|
|
850
|
+
if (cancelled) return;
|
|
851
|
+
subscriber.next({
|
|
852
|
+
phase: "finished",
|
|
853
|
+
resourceId: resourceId(result.resourceId)
|
|
854
|
+
});
|
|
855
|
+
subscriber.complete();
|
|
856
|
+
}).catch((err) => {
|
|
857
|
+
if (!cancelled) subscriber.error(err);
|
|
858
|
+
});
|
|
859
|
+
return () => {
|
|
860
|
+
cancelled = true;
|
|
861
|
+
abortController.abort();
|
|
862
|
+
};
|
|
826
863
|
});
|
|
827
|
-
return { resourceId: result.resourceId };
|
|
828
864
|
}
|
|
829
865
|
fromAnnotation(resourceId2, annotationId2, options) {
|
|
830
866
|
return new StreamObservable((subscriber) => {
|
|
@@ -915,6 +951,7 @@ var YieldNamespace = class {
|
|
|
915
951
|
title: options.title,
|
|
916
952
|
prompt: options.prompt,
|
|
917
953
|
language: options.language,
|
|
954
|
+
sourceLanguage: options.sourceLanguage,
|
|
918
955
|
temperature: options.temperature,
|
|
919
956
|
maxTokens: options.maxTokens,
|
|
920
957
|
storageUri: options.storageUri,
|
|
@@ -960,13 +997,14 @@ var YieldNamespace = class {
|
|
|
960
997
|
return result.sourceResource;
|
|
961
998
|
}
|
|
962
999
|
async createFromToken(options) {
|
|
963
|
-
|
|
1000
|
+
const result = await busRequest(
|
|
964
1001
|
this.transport,
|
|
965
1002
|
"yield:clone-create",
|
|
966
1003
|
options,
|
|
967
1004
|
"yield:clone-created",
|
|
968
1005
|
"yield:clone-create-failed"
|
|
969
1006
|
);
|
|
1007
|
+
return { resourceId: resourceId(result.resourceId) };
|
|
970
1008
|
}
|
|
971
1009
|
clone() {
|
|
972
1010
|
this.bus.get("yield:clone").next(void 0);
|
|
@@ -979,7 +1017,7 @@ var BeckonNamespace = class {
|
|
|
979
1017
|
this.transport = transport;
|
|
980
1018
|
this.bus = bus;
|
|
981
1019
|
}
|
|
982
|
-
attention(
|
|
1020
|
+
attention(resourceId2, annotationId2) {
|
|
983
1021
|
void this.transport.emit("beckon:focus", { annotationId: annotationId2, resourceId: resourceId2 });
|
|
984
1022
|
}
|
|
985
1023
|
hover(annotationId2) {
|
|
@@ -990,6 +1028,21 @@ var BeckonNamespace = class {
|
|
|
990
1028
|
}
|
|
991
1029
|
};
|
|
992
1030
|
|
|
1031
|
+
// src/namespaces/frame.ts
|
|
1032
|
+
var FrameNamespace = class {
|
|
1033
|
+
constructor(transport) {
|
|
1034
|
+
this.transport = transport;
|
|
1035
|
+
}
|
|
1036
|
+
async addEntityType(type) {
|
|
1037
|
+
await this.transport.emit("frame:add-entity-type", { tag: type });
|
|
1038
|
+
}
|
|
1039
|
+
async addEntityTypes(types) {
|
|
1040
|
+
for (const tag of types) {
|
|
1041
|
+
await this.transport.emit("frame:add-entity-type", { tag });
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
|
|
993
1046
|
// src/namespaces/job.ts
|
|
994
1047
|
var JobNamespace = class {
|
|
995
1048
|
constructor(transport, bus) {
|
|
@@ -1027,7 +1080,7 @@ var JobNamespace = class {
|
|
|
1027
1080
|
}
|
|
1028
1081
|
async pollUntilComplete(jobId, options) {
|
|
1029
1082
|
const interval = options?.interval ?? 1e3;
|
|
1030
|
-
const
|
|
1083
|
+
const timeout6 = options?.timeout ?? 6e4;
|
|
1031
1084
|
const startTime = Date.now();
|
|
1032
1085
|
while (true) {
|
|
1033
1086
|
const status = await this.status(jobId);
|
|
@@ -1035,89 +1088,97 @@ var JobNamespace = class {
|
|
|
1035
1088
|
if (status.status === "complete" || status.status === "failed" || status.status === "cancelled") {
|
|
1036
1089
|
return status;
|
|
1037
1090
|
}
|
|
1038
|
-
if (Date.now() - startTime >
|
|
1039
|
-
throw new Error(`Job polling timeout after ${
|
|
1091
|
+
if (Date.now() - startTime > timeout6) {
|
|
1092
|
+
throw new Error(`Job polling timeout after ${timeout6}ms`);
|
|
1040
1093
|
}
|
|
1041
1094
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
1042
1095
|
}
|
|
1043
1096
|
}
|
|
1044
|
-
async
|
|
1045
|
-
await this.transport.emit("job:cancel-requested", {
|
|
1046
|
-
jobType: type === "generation" ? "generation" : "annotation"
|
|
1047
|
-
});
|
|
1097
|
+
async cancelByType(jobType) {
|
|
1098
|
+
await this.transport.emit("job:cancel-requested", { jobType });
|
|
1048
1099
|
}
|
|
1049
1100
|
cancelRequest(jobType) {
|
|
1050
1101
|
this.bus.get("job:cancel-requested").next({ jobType });
|
|
1051
1102
|
}
|
|
1052
1103
|
};
|
|
1053
1104
|
var AuthNamespace = class {
|
|
1054
|
-
constructor(
|
|
1055
|
-
this.
|
|
1105
|
+
constructor(backend) {
|
|
1106
|
+
this.backend = backend;
|
|
1056
1107
|
}
|
|
1057
1108
|
async password(emailStr, passwordStr) {
|
|
1058
|
-
return this.
|
|
1109
|
+
return this.backend.authenticatePassword(email(emailStr), passwordStr);
|
|
1059
1110
|
}
|
|
1060
1111
|
async google(credential) {
|
|
1061
|
-
return this.
|
|
1112
|
+
return this.backend.authenticateGoogle(googleCredential(credential));
|
|
1062
1113
|
}
|
|
1063
1114
|
async refresh(token) {
|
|
1064
|
-
return this.
|
|
1115
|
+
return this.backend.refreshAccessToken(refreshToken(token));
|
|
1065
1116
|
}
|
|
1066
1117
|
async logout() {
|
|
1067
|
-
await this.
|
|
1118
|
+
await this.backend.logout();
|
|
1068
1119
|
}
|
|
1069
1120
|
async me() {
|
|
1070
|
-
return this.
|
|
1121
|
+
return this.backend.getCurrentUser();
|
|
1071
1122
|
}
|
|
1072
1123
|
async acceptTerms() {
|
|
1073
|
-
await this.
|
|
1124
|
+
await this.backend.acceptTerms();
|
|
1074
1125
|
}
|
|
1075
1126
|
async mcpToken() {
|
|
1076
|
-
return this.
|
|
1127
|
+
return this.backend.generateMcpToken();
|
|
1077
1128
|
}
|
|
1078
1129
|
async mediaToken(resourceId2) {
|
|
1079
|
-
return this.
|
|
1130
|
+
return this.backend.getMediaToken(resourceId2);
|
|
1080
1131
|
}
|
|
1081
1132
|
};
|
|
1082
1133
|
|
|
1083
1134
|
// src/namespaces/admin.ts
|
|
1084
1135
|
var AdminNamespace = class {
|
|
1085
|
-
constructor(
|
|
1086
|
-
this.
|
|
1136
|
+
constructor(backend) {
|
|
1137
|
+
this.backend = backend;
|
|
1087
1138
|
}
|
|
1088
1139
|
async users() {
|
|
1089
|
-
const result = await this.
|
|
1140
|
+
const result = await this.backend.listUsers();
|
|
1090
1141
|
return result.users;
|
|
1091
1142
|
}
|
|
1092
1143
|
async userStats() {
|
|
1093
|
-
return this.
|
|
1144
|
+
return this.backend.getUserStats();
|
|
1094
1145
|
}
|
|
1095
1146
|
async updateUser(userId2, data) {
|
|
1096
|
-
const result = await this.
|
|
1147
|
+
const result = await this.backend.updateUser(userId2, data);
|
|
1097
1148
|
return result.user;
|
|
1098
1149
|
}
|
|
1099
1150
|
async oauthConfig() {
|
|
1100
|
-
return this.
|
|
1151
|
+
return this.backend.getOAuthConfig();
|
|
1101
1152
|
}
|
|
1102
1153
|
async healthCheck() {
|
|
1103
|
-
return this.
|
|
1154
|
+
return this.backend.healthCheck();
|
|
1104
1155
|
}
|
|
1105
1156
|
async status() {
|
|
1106
|
-
return this.
|
|
1157
|
+
return this.backend.getStatus();
|
|
1107
1158
|
}
|
|
1108
1159
|
async backup() {
|
|
1109
|
-
return this.
|
|
1160
|
+
return this.backend.backupKnowledgeBase();
|
|
1110
1161
|
}
|
|
1111
|
-
|
|
1112
|
-
return this.
|
|
1162
|
+
restore(file) {
|
|
1163
|
+
return wrapAsStream(this.backend.restoreKnowledgeBase(file));
|
|
1113
1164
|
}
|
|
1114
1165
|
async exportKnowledgeBase(params) {
|
|
1115
|
-
return this.
|
|
1166
|
+
return this.backend.exportKnowledgeBase(params);
|
|
1116
1167
|
}
|
|
1117
|
-
|
|
1118
|
-
return this.
|
|
1168
|
+
importKnowledgeBase(file) {
|
|
1169
|
+
return wrapAsStream(this.backend.importKnowledgeBase(file));
|
|
1119
1170
|
}
|
|
1120
1171
|
};
|
|
1172
|
+
function wrapAsStream(source) {
|
|
1173
|
+
return new StreamObservable((subscriber) => {
|
|
1174
|
+
const sub = source.subscribe({
|
|
1175
|
+
next: (v) => subscriber.next(v),
|
|
1176
|
+
error: (e) => subscriber.error(e),
|
|
1177
|
+
complete: () => subscriber.complete()
|
|
1178
|
+
});
|
|
1179
|
+
return () => sub.unsubscribe();
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1121
1182
|
var SemiontClient = class _SemiontClient {
|
|
1122
1183
|
/**
|
|
1123
1184
|
* The wire-facing transport. Owns bus actor, HTTP, auth, admin, exchange,
|
|
@@ -1137,6 +1198,15 @@ var SemiontClient = class _SemiontClient {
|
|
|
1137
1198
|
bus;
|
|
1138
1199
|
baseUrl;
|
|
1139
1200
|
// ── Verb-oriented namespace API ──────────────────────────────────────────
|
|
1201
|
+
//
|
|
1202
|
+
// The first nine namespaces are bus-driven and always present. `frame`
|
|
1203
|
+
// is the schema-layer flow's surface (eighth flow); the other eight are
|
|
1204
|
+
// content-layer flows plus `job`. `auth` and `admin` are backend-ops
|
|
1205
|
+
// namespaces — they're only constructed when the caller passes an
|
|
1206
|
+
// `IBackendOperations` instance to the constructor. A `SemiontClient`
|
|
1207
|
+
// over a transport-only setup (e.g. `LocalTransport`) has
|
|
1208
|
+
// `auth === undefined` / `admin === undefined`.
|
|
1209
|
+
frame;
|
|
1140
1210
|
browse;
|
|
1141
1211
|
mark;
|
|
1142
1212
|
bind;
|
|
@@ -1159,13 +1229,20 @@ var SemiontClient = class _SemiontClient {
|
|
|
1159
1229
|
* Callers do not pass a bus in. If they need to interact with the bus
|
|
1160
1230
|
* (e.g. for tests or to subscribe to arbitrary channels), they read it
|
|
1161
1231
|
* back via `client.bus`.
|
|
1232
|
+
*
|
|
1233
|
+
* `backend` is optional. When provided, the `auth` and `admin`
|
|
1234
|
+
* namespaces are constructed against it; when omitted, they're
|
|
1235
|
+
* `undefined`. For HTTP setups this is conventionally the same
|
|
1236
|
+
* `HttpTransport` instance that's also passed as `transport` (HTTP
|
|
1237
|
+
* implements both `ITransport` and `IBackendOperations`).
|
|
1162
1238
|
*/
|
|
1163
|
-
constructor(transport, content) {
|
|
1239
|
+
constructor(transport, content, backend) {
|
|
1164
1240
|
this.transport = transport;
|
|
1165
1241
|
this.content = content;
|
|
1166
1242
|
this.baseUrl = transport.baseUrl;
|
|
1167
1243
|
this.bus = new EventBus();
|
|
1168
1244
|
this.transport.bridgeInto(this.bus);
|
|
1245
|
+
this.frame = new FrameNamespace(this.transport);
|
|
1169
1246
|
this.browse = new BrowseNamespace(this.transport, this.bus, this.content);
|
|
1170
1247
|
this.mark = new MarkNamespace(this.transport, this.bus);
|
|
1171
1248
|
this.bind = new BindNamespace(this.transport, this.bus);
|
|
@@ -1174,8 +1251,8 @@ var SemiontClient = class _SemiontClient {
|
|
|
1174
1251
|
this.yield = new YieldNamespace(this.transport, this.bus, this.content);
|
|
1175
1252
|
this.beckon = new BeckonNamespace(this.transport, this.bus);
|
|
1176
1253
|
this.job = new JobNamespace(this.transport, this.bus);
|
|
1177
|
-
this.auth = new AuthNamespace(
|
|
1178
|
-
this.admin = new AdminNamespace(
|
|
1254
|
+
this.auth = backend ? new AuthNamespace(backend) : void 0;
|
|
1255
|
+
this.admin = backend ? new AdminNamespace(backend) : void 0;
|
|
1179
1256
|
}
|
|
1180
1257
|
/** Transport-level connection state. HTTP reflects SSE health; local is always 'connected'. */
|
|
1181
1258
|
get state$() {
|
|
@@ -1197,9 +1274,10 @@ var SemiontClient = class _SemiontClient {
|
|
|
1197
1274
|
* Use this for one-shot scripts, CLI commands, or any consumer that
|
|
1198
1275
|
* doesn't need to drive the token from outside (no manual refresh,
|
|
1199
1276
|
* no cross-tab sync). For long-running scripts that need refresh,
|
|
1200
|
-
* use `SemiontSession.fromHttp(...)`
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
1277
|
+
* use `SemiontSession.fromHttp(...)` (with a token already on hand)
|
|
1278
|
+
* or `SemiontSession.signInHttp(...)` (credentials-first) instead —
|
|
1279
|
+
* either owns the same transport/client wiring plus the
|
|
1280
|
+
* proactive-refresh + storage machinery.
|
|
1203
1281
|
*
|
|
1204
1282
|
* Strings are accepted for `baseUrl` and `token`; they are branded
|
|
1205
1283
|
* via `baseUrl()` / `accessToken()` from `@semiont/core` automatically.
|
|
@@ -1213,13 +1291,13 @@ var SemiontClient = class _SemiontClient {
|
|
|
1213
1291
|
const token$ = new BehaviorSubject(tok);
|
|
1214
1292
|
const transport = new HttpTransport({ baseUrl: url, token$ });
|
|
1215
1293
|
const content = new HttpContentTransport(transport);
|
|
1216
|
-
return new _SemiontClient(transport, content);
|
|
1294
|
+
return new _SemiontClient(transport, content, transport);
|
|
1217
1295
|
}
|
|
1218
1296
|
/**
|
|
1219
1297
|
* Async factory for the credentials-first script case. Builds a
|
|
1220
|
-
* transient transport, calls `auth.password(email, password)`
|
|
1221
|
-
* acquire an access token, and returns the wired client with
|
|
1222
|
-
* token populated.
|
|
1298
|
+
* transient HTTP transport, calls `auth.password(email, password)`
|
|
1299
|
+
* to acquire an access token, and returns the wired client with
|
|
1300
|
+
* the token populated.
|
|
1223
1301
|
*
|
|
1224
1302
|
* This is the right entry point for skills, CLI scripts, and any
|
|
1225
1303
|
* consumer that starts with email + password rather than a JWT
|
|
@@ -1228,18 +1306,24 @@ var SemiontClient = class _SemiontClient {
|
|
|
1228
1306
|
* `fromHttp({ baseUrl, token })` instead.
|
|
1229
1307
|
*
|
|
1230
1308
|
* For long-running scripts that need refresh, use
|
|
1231
|
-
* `SemiontSession.
|
|
1232
|
-
* session machinery for proactive refresh and persistence.
|
|
1309
|
+
* `SemiontSession.signInHttp(...)` — same credentials shape, plus
|
|
1310
|
+
* the session machinery for proactive refresh and persistence.
|
|
1311
|
+
*
|
|
1312
|
+
* Named `signInHttp` because email+password authentication is
|
|
1313
|
+
* inherently an HTTP-shaped operation in the current backend; an
|
|
1314
|
+
* in-process `LocalTransport` doesn't have a credentials login
|
|
1315
|
+
* path. Non-HTTP transports construct the client directly from
|
|
1316
|
+
* their package's transport instance.
|
|
1233
1317
|
*
|
|
1234
1318
|
* Throws if authentication fails. The transient client is disposed
|
|
1235
1319
|
* before the throw, so no resources leak on failure.
|
|
1236
1320
|
*/
|
|
1237
|
-
static async
|
|
1321
|
+
static async signInHttp(opts) {
|
|
1238
1322
|
const url = typeof opts.baseUrl === "string" ? baseUrl(opts.baseUrl) : opts.baseUrl;
|
|
1239
1323
|
const token$ = new BehaviorSubject(null);
|
|
1240
1324
|
const transport = new HttpTransport({ baseUrl: url, token$ });
|
|
1241
1325
|
const content = new HttpContentTransport(transport);
|
|
1242
|
-
const client = new _SemiontClient(transport, content);
|
|
1326
|
+
const client = new _SemiontClient(transport, content, transport);
|
|
1243
1327
|
try {
|
|
1244
1328
|
const auth = await client.auth.password(opts.email, opts.password);
|
|
1245
1329
|
token$.next(accessToken(auth.token));
|
|
@@ -1293,35 +1377,28 @@ function isJwtExpired(token) {
|
|
|
1293
1377
|
if (!expiry) return true;
|
|
1294
1378
|
return expiry.getTime() < Date.now();
|
|
1295
1379
|
}
|
|
1296
|
-
function
|
|
1297
|
-
if (entry
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
return
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
return {
|
|
1310
|
-
id: entry.id,
|
|
1311
|
-
label: entry.label || "Unknown",
|
|
1312
|
-
host: "localhost",
|
|
1313
|
-
port: 4e3,
|
|
1314
|
-
protocol: "http",
|
|
1315
|
-
email: ""
|
|
1316
|
-
};
|
|
1380
|
+
function isKnowledgeBase(entry) {
|
|
1381
|
+
if (!entry || typeof entry !== "object") return false;
|
|
1382
|
+
const e = entry;
|
|
1383
|
+
if (typeof e.id !== "string" || typeof e.label !== "string" || typeof e.email !== "string") {
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
1386
|
+
const ep = e.endpoint;
|
|
1387
|
+
if (!ep || typeof ep !== "object") return false;
|
|
1388
|
+
if (ep.kind === "http") {
|
|
1389
|
+
return typeof ep.host === "string" && typeof ep.port === "number" && (ep.protocol === "http" || ep.protocol === "https");
|
|
1390
|
+
}
|
|
1391
|
+
if (ep.kind === "local") {
|
|
1392
|
+
return typeof ep.kbId === "string";
|
|
1317
1393
|
}
|
|
1394
|
+
return false;
|
|
1318
1395
|
}
|
|
1319
1396
|
function loadKnowledgeBases(storage) {
|
|
1320
1397
|
try {
|
|
1321
1398
|
const raw = storage.get(STORAGE_KEY);
|
|
1322
1399
|
if (!raw) return [];
|
|
1323
1400
|
const entries = JSON.parse(raw);
|
|
1324
|
-
return entries.
|
|
1401
|
+
return entries.filter(isKnowledgeBase);
|
|
1325
1402
|
} catch {
|
|
1326
1403
|
return [];
|
|
1327
1404
|
}
|
|
@@ -1336,15 +1413,15 @@ var HOSTNAME_RE = /^(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9]
|
|
|
1336
1413
|
function isValidHostname(host) {
|
|
1337
1414
|
return HOSTNAME_RE.test(host);
|
|
1338
1415
|
}
|
|
1339
|
-
function kbBackendUrl(
|
|
1340
|
-
if (!isValidHostname(
|
|
1341
|
-
throw new Error(`Invalid KB hostname: "${
|
|
1416
|
+
function kbBackendUrl(endpoint) {
|
|
1417
|
+
if (!isValidHostname(endpoint.host)) {
|
|
1418
|
+
throw new Error(`Invalid KB hostname: "${endpoint.host}"`);
|
|
1342
1419
|
}
|
|
1343
1420
|
const url = new URL("http://x");
|
|
1344
|
-
url.protocol =
|
|
1345
|
-
url.hostname =
|
|
1346
|
-
url.port = String(
|
|
1347
|
-
return `${
|
|
1421
|
+
url.protocol = endpoint.protocol + ":";
|
|
1422
|
+
url.hostname = endpoint.host;
|
|
1423
|
+
url.port = String(endpoint.port);
|
|
1424
|
+
return `${endpoint.protocol}://${url.hostname}:${endpoint.port}`;
|
|
1348
1425
|
}
|
|
1349
1426
|
function generateKbId() {
|
|
1350
1427
|
return crypto.randomUUID();
|
|
@@ -1365,6 +1442,18 @@ var SemiontSession = class _SemiontSession {
|
|
|
1365
1442
|
token$;
|
|
1366
1443
|
user$;
|
|
1367
1444
|
streamState$;
|
|
1445
|
+
/**
|
|
1446
|
+
* Stream of `SemiontError` instances surfaced by the underlying transport
|
|
1447
|
+
* just before they're thrown to the caller. For `HttpTransport` this is
|
|
1448
|
+
* an `APIError` (status-coded); other transports emit their own subclass.
|
|
1449
|
+
* Surfaced here so a host layer (e.g. `SemiontBrowser`) can route by
|
|
1450
|
+
* `err.code` to global notifications without every call site handling
|
|
1451
|
+
* errors itself. Headless consumers can subscribe for logging.
|
|
1452
|
+
*
|
|
1453
|
+
* Re-published from `client.transport.errors$` per the `ITransport`
|
|
1454
|
+
* contract — the session is purely a passthrough.
|
|
1455
|
+
*/
|
|
1456
|
+
errors$;
|
|
1368
1457
|
/** Resolves after the initial validation round-trip completes (success or failure). */
|
|
1369
1458
|
ready;
|
|
1370
1459
|
storage;
|
|
@@ -1387,6 +1476,7 @@ var SemiontSession = class _SemiontSession {
|
|
|
1387
1476
|
this.client = config.client;
|
|
1388
1477
|
this.token$ = config.token$;
|
|
1389
1478
|
this.user$ = new BehaviorSubject(null);
|
|
1479
|
+
this.errors$ = this.client.transport.errors$;
|
|
1390
1480
|
const stored = getStoredSession(this.storage, this.kb.id);
|
|
1391
1481
|
if (stored && !isJwtExpired(stored.access) && this.token$.getValue() === null) {
|
|
1392
1482
|
this.token$.next(accessToken(stored.access));
|
|
@@ -1571,7 +1661,7 @@ var SemiontSession = class _SemiontSession {
|
|
|
1571
1661
|
const token$ = new BehaviorSubject(tok);
|
|
1572
1662
|
const transport = new HttpTransport({ baseUrl: url, token$ });
|
|
1573
1663
|
const content = new HttpContentTransport(transport);
|
|
1574
|
-
const client = new SemiontClient(transport, content);
|
|
1664
|
+
const client = new SemiontClient(transport, content, transport);
|
|
1575
1665
|
const config = { kb: opts.kb, storage: opts.storage, client, token$ };
|
|
1576
1666
|
if (opts.refresh) config.refresh = opts.refresh;
|
|
1577
1667
|
if (opts.validate) config.validate = opts.validate;
|
|
@@ -1581,11 +1671,11 @@ var SemiontSession = class _SemiontSession {
|
|
|
1581
1671
|
}
|
|
1582
1672
|
/**
|
|
1583
1673
|
* Async factory for the credentials-first long-running script case.
|
|
1584
|
-
* Builds the transport stack, calls `auth.password(email,
|
|
1585
|
-
* to acquire access + refresh tokens, persists them via
|
|
1586
|
-
* adapter, wires a default `refresh` callback that
|
|
1587
|
-
* refresh token via `auth.refresh(...)`, and returns
|
|
1588
|
-
* session.
|
|
1674
|
+
* Builds the HTTP transport stack, calls `auth.password(email,
|
|
1675
|
+
* password)` to acquire access + refresh tokens, persists them via
|
|
1676
|
+
* the storage adapter, wires a default `refresh` callback that
|
|
1677
|
+
* exchanges the refresh token via `auth.refresh(...)`, and returns
|
|
1678
|
+
* the ready session.
|
|
1589
1679
|
*
|
|
1590
1680
|
* The consumer-supplied `refresh` callback becomes optional — only
|
|
1591
1681
|
* needed for non-standard refresh flows (worker-pool shared secret,
|
|
@@ -1598,15 +1688,21 @@ var SemiontSession = class _SemiontSession {
|
|
|
1598
1688
|
* trampling each other's tokens. The factory does not synthesize a
|
|
1599
1689
|
* default; the consumer makes the choice.
|
|
1600
1690
|
*
|
|
1691
|
+
* Named `signInHttp` because email+password authentication is
|
|
1692
|
+
* inherently an HTTP-shaped operation in the current backend; an
|
|
1693
|
+
* in-process `LocalTransport` doesn't have a credentials login
|
|
1694
|
+
* path. Non-HTTP transports construct the session directly from
|
|
1695
|
+
* their package's transport instance.
|
|
1696
|
+
*
|
|
1601
1697
|
* Throws on auth failure with no resources leaked. On success, the
|
|
1602
1698
|
* returned session's `ready` promise has already resolved.
|
|
1603
1699
|
*/
|
|
1604
|
-
static async
|
|
1700
|
+
static async signInHttp(opts) {
|
|
1605
1701
|
const url = typeof opts.baseUrl === "string" ? baseUrl(opts.baseUrl) : opts.baseUrl;
|
|
1606
1702
|
const token$ = new BehaviorSubject(null);
|
|
1607
1703
|
const transport = new HttpTransport({ baseUrl: url, token$ });
|
|
1608
1704
|
const content = new HttpContentTransport(transport);
|
|
1609
|
-
const client = new SemiontClient(transport, content);
|
|
1705
|
+
const client = new SemiontClient(transport, content, transport);
|
|
1610
1706
|
let auth;
|
|
1611
1707
|
try {
|
|
1612
1708
|
auth = await client.auth.password(opts.email, opts.password);
|
|
@@ -1641,25 +1737,7 @@ var SemiontSession = class _SemiontSession {
|
|
|
1641
1737
|
return session;
|
|
1642
1738
|
}
|
|
1643
1739
|
};
|
|
1644
|
-
|
|
1645
|
-
// src/session/notify.ts
|
|
1646
|
-
var activeOnSessionExpired = null;
|
|
1647
|
-
var activeOnPermissionDenied = null;
|
|
1648
|
-
function notifySessionExpired(message) {
|
|
1649
|
-
activeOnSessionExpired?.(message);
|
|
1650
|
-
}
|
|
1651
|
-
function notifyPermissionDenied(message) {
|
|
1652
|
-
activeOnPermissionDenied?.(message);
|
|
1653
|
-
}
|
|
1654
|
-
function registerAuthNotifyHandlers(handlers) {
|
|
1655
|
-
activeOnSessionExpired = handlers.onSessionExpired;
|
|
1656
|
-
activeOnPermissionDenied = handlers.onPermissionDenied;
|
|
1657
|
-
return () => {
|
|
1658
|
-
activeOnSessionExpired = null;
|
|
1659
|
-
activeOnPermissionDenied = null;
|
|
1660
|
-
};
|
|
1661
|
-
}
|
|
1662
|
-
var FrontendSessionSignals = class {
|
|
1740
|
+
var SessionSignals = class {
|
|
1663
1741
|
sessionExpiredAt$;
|
|
1664
1742
|
sessionExpiredMessage$;
|
|
1665
1743
|
permissionDeniedAt$;
|
|
@@ -1724,7 +1802,7 @@ var SemiontBrowser = class {
|
|
|
1724
1802
|
* non-null when `activeSession$` is non-null, always null when it
|
|
1725
1803
|
* is. Extracted from the session itself so headless sessions
|
|
1726
1804
|
* (workers, CLIs, tests) don't carry dead modal observables.
|
|
1727
|
-
* See [
|
|
1805
|
+
* See [SessionSignals](./session-signals.ts).
|
|
1728
1806
|
*/
|
|
1729
1807
|
activeSignals$;
|
|
1730
1808
|
/**
|
|
@@ -1740,6 +1818,7 @@ var SemiontBrowser = class {
|
|
|
1740
1818
|
error$;
|
|
1741
1819
|
identityToken$;
|
|
1742
1820
|
storage;
|
|
1821
|
+
sessionFactory;
|
|
1743
1822
|
/**
|
|
1744
1823
|
* App-scoped EventBus. Hosts UI-shell events that must work regardless
|
|
1745
1824
|
* of whether a KB session is active: panel toggles, sidebar state,
|
|
@@ -1748,20 +1827,12 @@ var SemiontBrowser = class {
|
|
|
1748
1827
|
* (mark:*, beckon:*, gather:*, match:*, bind:*, yield:*, browse:click).
|
|
1749
1828
|
*/
|
|
1750
1829
|
eventBus = new EventBus();
|
|
1751
|
-
unregisterNotify = null;
|
|
1752
1830
|
unsubscribeStorage = null;
|
|
1753
1831
|
disposed = false;
|
|
1754
1832
|
activating = null;
|
|
1755
|
-
/**
|
|
1756
|
-
* Per-KB in-flight refresh dedup. Simultaneous 401s for the same
|
|
1757
|
-
* KB converge on a single `/api/tokens/refresh` network call.
|
|
1758
|
-
* Was previously module-scoped in `refresh.ts`; moved here when
|
|
1759
|
-
* that file was deleted — SemiontBrowser is a singleton so the
|
|
1760
|
-
* scoping is equivalent.
|
|
1761
|
-
*/
|
|
1762
|
-
inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
1763
1833
|
constructor(config) {
|
|
1764
1834
|
this.storage = config.storage;
|
|
1835
|
+
this.sessionFactory = config.sessionFactory;
|
|
1765
1836
|
const kbs = loadKnowledgeBases(this.storage);
|
|
1766
1837
|
const storedActive = this.storage.get(ACTIVE_KEY);
|
|
1767
1838
|
const initialActive = storedActive && kbs.some((kb) => kb.id === storedActive) ? storedActive : kbs[0]?.id ?? null;
|
|
@@ -1788,14 +1859,6 @@ var SemiontBrowser = class {
|
|
|
1788
1859
|
} catch {
|
|
1789
1860
|
}
|
|
1790
1861
|
}) ?? null;
|
|
1791
|
-
this.unregisterNotify = registerAuthNotifyHandlers({
|
|
1792
|
-
onSessionExpired: (message) => {
|
|
1793
|
-
this.activeSignals$.getValue()?.notifySessionExpired(message ?? null);
|
|
1794
|
-
},
|
|
1795
|
-
onPermissionDenied: (message) => {
|
|
1796
|
-
this.activeSignals$.getValue()?.notifyPermissionDenied(message ?? null);
|
|
1797
|
-
}
|
|
1798
|
-
});
|
|
1799
1862
|
if (initialActive) {
|
|
1800
1863
|
void this.setActiveKb(initialActive);
|
|
1801
1864
|
}
|
|
@@ -1817,9 +1880,10 @@ var SemiontBrowser = class {
|
|
|
1817
1880
|
}
|
|
1818
1881
|
// ── Identity token (NextAuth bridge; D1) ──────────────────────────────
|
|
1819
1882
|
/**
|
|
1820
|
-
* Set the app-level identity token
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1883
|
+
* Set the app-level identity token. Sourced from whatever the host
|
|
1884
|
+
* environment uses for OAuth sessions (e.g. NextAuth in a browser app).
|
|
1885
|
+
* Should be called once from the host's startup-and-on-change site;
|
|
1886
|
+
* no other code should write to this slot.
|
|
1823
1887
|
*/
|
|
1824
1888
|
setIdentityToken(token) {
|
|
1825
1889
|
if (this.disposed) return;
|
|
@@ -1841,6 +1905,12 @@ var SemiontBrowser = class {
|
|
|
1841
1905
|
void this.setActiveKb(next[0]?.id ?? null);
|
|
1842
1906
|
}
|
|
1843
1907
|
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Patch a KB in the list. Restricted to the common, endpoint-agnostic
|
|
1910
|
+
* fields (`label`, `email`, `gitBranch`) — the `endpoint` shape isn't
|
|
1911
|
+
* editable in place; remove and re-add to change the connection
|
|
1912
|
+
* target.
|
|
1913
|
+
*/
|
|
1844
1914
|
updateKb(id, updates) {
|
|
1845
1915
|
this.kbs$.next(
|
|
1846
1916
|
this.kbs$.getValue().map((kb) => kb.id === id ? { ...kb, ...updates } : kb)
|
|
@@ -1896,25 +1966,32 @@ var SemiontBrowser = class {
|
|
|
1896
1966
|
if (!id) return;
|
|
1897
1967
|
const kb = this.kbs$.getValue().find((k) => k.id === id);
|
|
1898
1968
|
if (!kb) return;
|
|
1899
|
-
const signals = new
|
|
1900
|
-
const token$ = new BehaviorSubject(null);
|
|
1969
|
+
const signals = new SessionSignals();
|
|
1901
1970
|
let session;
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1971
|
+
try {
|
|
1972
|
+
session = this.sessionFactory({
|
|
1973
|
+
kb,
|
|
1974
|
+
storage: this.storage,
|
|
1975
|
+
signals,
|
|
1976
|
+
onError: (err) => this.error$.next(err)
|
|
1977
|
+
});
|
|
1978
|
+
} catch (err) {
|
|
1979
|
+
this.error$.next(
|
|
1980
|
+
err instanceof SemiontSessionError ? err : new SemiontSessionError(
|
|
1981
|
+
"session.construct-failed",
|
|
1982
|
+
err instanceof Error ? err.message : String(err),
|
|
1983
|
+
id
|
|
1984
|
+
)
|
|
1985
|
+
);
|
|
1986
|
+
signals.dispose();
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
session.errors$.subscribe((err) => {
|
|
1990
|
+
if (err.code === "unauthorized") {
|
|
1991
|
+
signals.notifySessionExpired(err.message);
|
|
1992
|
+
} else if (err.code === "forbidden") {
|
|
1993
|
+
signals.notifyPermissionDenied(err.message);
|
|
1994
|
+
}
|
|
1918
1995
|
});
|
|
1919
1996
|
try {
|
|
1920
1997
|
await session.ready;
|
|
@@ -2030,80 +2107,10 @@ var SemiontBrowser = class {
|
|
|
2030
2107
|
if (moved) list.splice(newIndex, 0, moved);
|
|
2031
2108
|
this.openResources$.next(list);
|
|
2032
2109
|
}
|
|
2033
|
-
// ── Auth callbacks bound per session ──────────────────────────────────
|
|
2034
|
-
//
|
|
2035
|
-
// These closures back the `refresh` and `validate` callbacks passed
|
|
2036
|
-
// to `SemiontSession` in `setActiveKb`. Factored out as methods
|
|
2037
|
-
// (rather than inline in the activation closure) so test-doubles
|
|
2038
|
-
// can override them cleanly, and so the in-flight dedup map
|
|
2039
|
-
// survives across activations of the same KB.
|
|
2040
|
-
/**
|
|
2041
|
-
* Refresh the active KB's access token. Returns the new token on
|
|
2042
|
-
* success, null on failure. Concurrent calls for the same KB
|
|
2043
|
-
* dedupe through `inFlightRefreshes`, so simultaneous 401s trigger
|
|
2044
|
-
* only one `/api/tokens/refresh` round trip.
|
|
2045
|
-
*
|
|
2046
|
-
* Uses a throwaway `SemiontClient` with no `tokenRefresher` —
|
|
2047
|
-
* a refresh call returning 401 would otherwise re-enter this
|
|
2048
|
-
* function infinitely.
|
|
2049
|
-
*/
|
|
2050
|
-
async performRefresh(kb) {
|
|
2051
|
-
const existing = this.inFlightRefreshes.get(kb.id);
|
|
2052
|
-
if (existing) return existing;
|
|
2053
|
-
const promise = (async () => {
|
|
2054
|
-
const stored = getStoredSession(this.storage, kb.id);
|
|
2055
|
-
if (!stored) return null;
|
|
2056
|
-
const throwawayTransport = new HttpTransport({ baseUrl: baseUrl(kbBackendUrl(kb)) });
|
|
2057
|
-
const throwaway = new SemiontClient(throwawayTransport, new HttpContentTransport(throwawayTransport));
|
|
2058
|
-
try {
|
|
2059
|
-
const response = await throwaway.auth.refresh(stored.refresh);
|
|
2060
|
-
const newAccess = response.access_token;
|
|
2061
|
-
if (!newAccess) return null;
|
|
2062
|
-
setStoredSession(this.storage, kb.id, { access: newAccess, refresh: stored.refresh });
|
|
2063
|
-
return newAccess;
|
|
2064
|
-
} catch {
|
|
2065
|
-
return null;
|
|
2066
|
-
} finally {
|
|
2067
|
-
throwaway.dispose();
|
|
2068
|
-
}
|
|
2069
|
-
})();
|
|
2070
|
-
this.inFlightRefreshes.set(kb.id, promise);
|
|
2071
|
-
try {
|
|
2072
|
-
return await promise;
|
|
2073
|
-
} finally {
|
|
2074
|
-
this.inFlightRefreshes.delete(kb.id);
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2077
|
-
/**
|
|
2078
|
-
* Validate an access token by calling `auth.me` on a throwaway
|
|
2079
|
-
* client. The session uses this once at startup to populate
|
|
2080
|
-
* `user$`; 401 triggers a refresh-then-retry inside the session.
|
|
2081
|
-
*
|
|
2082
|
-
* The throwaway transport is seeded with the specific token to
|
|
2083
|
-
* validate so the request actually carries it (HttpTransport
|
|
2084
|
-
* sources `Authorization` from its `token$`).
|
|
2085
|
-
*/
|
|
2086
|
-
async performValidate(kb, token) {
|
|
2087
|
-
const tokenSubject = new BehaviorSubject(token);
|
|
2088
|
-
const throwawayTransport = new HttpTransport({
|
|
2089
|
-
baseUrl: baseUrl(kbBackendUrl(kb)),
|
|
2090
|
-
token$: tokenSubject
|
|
2091
|
-
});
|
|
2092
|
-
const throwaway = new SemiontClient(throwawayTransport, new HttpContentTransport(throwawayTransport));
|
|
2093
|
-
try {
|
|
2094
|
-
const data = await throwaway.auth.me();
|
|
2095
|
-
return data;
|
|
2096
|
-
} finally {
|
|
2097
|
-
throwaway.dispose();
|
|
2098
|
-
tokenSubject.complete();
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
2110
|
// ── Lifecycle ─────────────────────────────────────────────────────────
|
|
2102
2111
|
async dispose() {
|
|
2103
2112
|
if (this.disposed) return;
|
|
2104
2113
|
this.disposed = true;
|
|
2105
|
-
this.unregisterNotify?.();
|
|
2106
|
-
this.unregisterNotify = null;
|
|
2107
2114
|
if (this.unsubscribeStorage) {
|
|
2108
2115
|
this.unsubscribeStorage();
|
|
2109
2116
|
this.unsubscribeStorage = null;
|
|
@@ -2124,12 +2131,91 @@ var SemiontBrowser = class {
|
|
|
2124
2131
|
this.eventBus.destroy();
|
|
2125
2132
|
}
|
|
2126
2133
|
};
|
|
2134
|
+
function createHttpSessionFactory() {
|
|
2135
|
+
const inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
2136
|
+
return (opts) => {
|
|
2137
|
+
const { kb, storage, signals, onError } = opts;
|
|
2138
|
+
if (kb.endpoint.kind !== "http") {
|
|
2139
|
+
throw new SemiontSessionError(
|
|
2140
|
+
"session.construct-failed",
|
|
2141
|
+
`HTTP session factory cannot construct a session for endpoint kind "${kb.endpoint.kind}"`,
|
|
2142
|
+
kb.id
|
|
2143
|
+
);
|
|
2144
|
+
}
|
|
2145
|
+
const endpoint = kb.endpoint;
|
|
2146
|
+
const performRefresh = async () => {
|
|
2147
|
+
const existing = inFlightRefreshes.get(kb.id);
|
|
2148
|
+
if (existing) return existing;
|
|
2149
|
+
const promise = (async () => {
|
|
2150
|
+
const stored = getStoredSession(storage, kb.id);
|
|
2151
|
+
if (!stored) return null;
|
|
2152
|
+
const throwawayTransport = new HttpTransport({ baseUrl: baseUrl(kbBackendUrl(endpoint)) });
|
|
2153
|
+
const throwaway = new SemiontClient(throwawayTransport, new HttpContentTransport(throwawayTransport), throwawayTransport);
|
|
2154
|
+
try {
|
|
2155
|
+
const response = await throwaway.auth.refresh(stored.refresh);
|
|
2156
|
+
const newAccess = response.access_token;
|
|
2157
|
+
if (!newAccess) return null;
|
|
2158
|
+
setStoredSession(storage, kb.id, { access: newAccess, refresh: stored.refresh });
|
|
2159
|
+
return newAccess;
|
|
2160
|
+
} catch {
|
|
2161
|
+
return null;
|
|
2162
|
+
} finally {
|
|
2163
|
+
throwaway.dispose();
|
|
2164
|
+
}
|
|
2165
|
+
})();
|
|
2166
|
+
inFlightRefreshes.set(kb.id, promise);
|
|
2167
|
+
try {
|
|
2168
|
+
return await promise;
|
|
2169
|
+
} finally {
|
|
2170
|
+
inFlightRefreshes.delete(kb.id);
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
const performValidate = async (token) => {
|
|
2174
|
+
const tokenSubject = new BehaviorSubject(token);
|
|
2175
|
+
const throwawayTransport = new HttpTransport({
|
|
2176
|
+
baseUrl: baseUrl(kbBackendUrl(endpoint)),
|
|
2177
|
+
token$: tokenSubject
|
|
2178
|
+
});
|
|
2179
|
+
const throwaway = new SemiontClient(throwawayTransport, new HttpContentTransport(throwawayTransport), throwawayTransport);
|
|
2180
|
+
try {
|
|
2181
|
+
const data = await throwaway.auth.me();
|
|
2182
|
+
return data;
|
|
2183
|
+
} finally {
|
|
2184
|
+
throwaway.dispose();
|
|
2185
|
+
tokenSubject.complete();
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
const token$ = new BehaviorSubject(null);
|
|
2189
|
+
let session;
|
|
2190
|
+
const transport = new HttpTransport({
|
|
2191
|
+
baseUrl: baseUrl(kbBackendUrl(endpoint)),
|
|
2192
|
+
token$,
|
|
2193
|
+
tokenRefresher: () => session.refresh().then((t) => t ?? null)
|
|
2194
|
+
});
|
|
2195
|
+
const content = new HttpContentTransport(transport);
|
|
2196
|
+
const client = new SemiontClient(transport, content, transport);
|
|
2197
|
+
session = new SemiontSession({
|
|
2198
|
+
kb,
|
|
2199
|
+
storage,
|
|
2200
|
+
client,
|
|
2201
|
+
token$,
|
|
2202
|
+
refresh: performRefresh,
|
|
2203
|
+
validate: performValidate,
|
|
2204
|
+
onAuthFailed: (msg) => signals.notifySessionExpired(msg),
|
|
2205
|
+
onError
|
|
2206
|
+
});
|
|
2207
|
+
return session;
|
|
2208
|
+
};
|
|
2209
|
+
}
|
|
2127
2210
|
|
|
2128
2211
|
// src/session/registry.ts
|
|
2129
2212
|
var instance = null;
|
|
2130
2213
|
function getBrowser(options) {
|
|
2131
2214
|
if (!instance) {
|
|
2132
|
-
instance = new SemiontBrowser({
|
|
2215
|
+
instance = new SemiontBrowser({
|
|
2216
|
+
storage: options.storage,
|
|
2217
|
+
sessionFactory: options.sessionFactory
|
|
2218
|
+
});
|
|
2133
2219
|
}
|
|
2134
2220
|
return instance;
|
|
2135
2221
|
}
|
|
@@ -2147,6 +2233,18 @@ var InMemorySessionStorage = class {
|
|
|
2147
2233
|
this.map.delete(key);
|
|
2148
2234
|
}
|
|
2149
2235
|
};
|
|
2236
|
+
|
|
2237
|
+
// src/session/knowledge-base.ts
|
|
2238
|
+
function httpKb(opts) {
|
|
2239
|
+
const { id, label, email, host, port, protocol, gitBranch } = opts;
|
|
2240
|
+
return {
|
|
2241
|
+
id,
|
|
2242
|
+
label,
|
|
2243
|
+
email,
|
|
2244
|
+
...gitBranch !== void 0 ? { gitBranch } : {},
|
|
2245
|
+
endpoint: { kind: "http", host, port, protocol }
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2150
2248
|
function createDisposer() {
|
|
2151
2249
|
const sub = new Subscription();
|
|
2152
2250
|
return {
|
|
@@ -2162,7 +2260,7 @@ function createSearchPipeline(fetch, options = {}) {
|
|
|
2162
2260
|
const state$ = input$.pipe(
|
|
2163
2261
|
startWith(initial),
|
|
2164
2262
|
debounceTime(debounceMs),
|
|
2165
|
-
distinctUntilChanged
|
|
2263
|
+
distinctUntilChanged(),
|
|
2166
2264
|
switchMap((q) => {
|
|
2167
2265
|
const trimmed = q.trim();
|
|
2168
2266
|
if (!trimmed) {
|
|
@@ -2184,7 +2282,7 @@ function createSearchPipeline(fetch, options = {}) {
|
|
|
2184
2282
|
dispose: () => input$.complete()
|
|
2185
2283
|
};
|
|
2186
2284
|
}
|
|
2187
|
-
function
|
|
2285
|
+
function createBeckonStateUnit(client) {
|
|
2188
2286
|
const subs = [];
|
|
2189
2287
|
const hovered$ = new BehaviorSubject(null);
|
|
2190
2288
|
subs.push(client.bus.get("beckon:hover").subscribe(({ annotationId: annotationId2 }) => {
|
|
@@ -2235,59 +2333,7 @@ function createHoverHandlers(emit, delayMs) {
|
|
|
2235
2333
|
};
|
|
2236
2334
|
return { handleMouseEnter, handleMouseLeave, cleanup: cancelTimer };
|
|
2237
2335
|
}
|
|
2238
|
-
|
|
2239
|
-
var RESOURCE_PANELS = ["history", "info", "annotations", "collaboration", "jsonld"];
|
|
2240
|
-
var MOTIVATION_TO_TAB = {
|
|
2241
|
-
"linking": "reference",
|
|
2242
|
-
"commenting": "comment",
|
|
2243
|
-
"tagging": "tag",
|
|
2244
|
-
"highlighting": "highlight",
|
|
2245
|
-
"assessing": "assessment"
|
|
2246
|
-
};
|
|
2247
|
-
var tabGenerationCounter = 0;
|
|
2248
|
-
function createShellVM(browser, options) {
|
|
2249
|
-
const subs = [];
|
|
2250
|
-
const activePanel$ = new BehaviorSubject(options?.initialPanel ?? null);
|
|
2251
|
-
const scrollToAnnotationId$ = new BehaviorSubject(null);
|
|
2252
|
-
const panelInitialTab$ = new BehaviorSubject(null);
|
|
2253
|
-
if (options?.onPanelChange) {
|
|
2254
|
-
const cb = options.onPanelChange;
|
|
2255
|
-
subs.push(activePanel$.subscribe(cb));
|
|
2256
|
-
}
|
|
2257
|
-
subs.push(browser.stream("panel:toggle").subscribe(({ panel }) => {
|
|
2258
|
-
const current = activePanel$.getValue();
|
|
2259
|
-
activePanel$.next(current === panel ? null : panel);
|
|
2260
|
-
}));
|
|
2261
|
-
subs.push(browser.stream("panel:open").subscribe(({ panel, scrollToAnnotationId, motivation }) => {
|
|
2262
|
-
if (scrollToAnnotationId) {
|
|
2263
|
-
scrollToAnnotationId$.next(scrollToAnnotationId);
|
|
2264
|
-
}
|
|
2265
|
-
if (motivation) {
|
|
2266
|
-
const tab = MOTIVATION_TO_TAB[motivation] || "highlight";
|
|
2267
|
-
panelInitialTab$.next({ tab, generation: ++tabGenerationCounter });
|
|
2268
|
-
}
|
|
2269
|
-
activePanel$.next(panel);
|
|
2270
|
-
}));
|
|
2271
|
-
subs.push(browser.stream("panel:close").subscribe(() => {
|
|
2272
|
-
activePanel$.next(null);
|
|
2273
|
-
}));
|
|
2274
|
-
return {
|
|
2275
|
-
activePanel$: activePanel$.asObservable(),
|
|
2276
|
-
scrollToAnnotationId$: scrollToAnnotationId$.asObservable(),
|
|
2277
|
-
panelInitialTab$: panelInitialTab$.asObservable(),
|
|
2278
|
-
openPanel: (panel) => browser.emit("panel:open", { panel }),
|
|
2279
|
-
closePanel: () => browser.emit("panel:close", void 0),
|
|
2280
|
-
togglePanel: (panel) => browser.emit("panel:toggle", { panel }),
|
|
2281
|
-
onScrollCompleted: () => scrollToAnnotationId$.next(null),
|
|
2282
|
-
dispose() {
|
|
2283
|
-
subs.forEach((s) => s.unsubscribe());
|
|
2284
|
-
activePanel$.complete();
|
|
2285
|
-
scrollToAnnotationId$.complete();
|
|
2286
|
-
panelInitialTab$.complete();
|
|
2287
|
-
}
|
|
2288
|
-
};
|
|
2289
|
-
}
|
|
2290
|
-
function createGatherVM(client, resourceId2) {
|
|
2336
|
+
function createGatherStateUnit(client, resourceId2) {
|
|
2291
2337
|
const subs = [];
|
|
2292
2338
|
const context$ = new BehaviorSubject(null);
|
|
2293
2339
|
const loading$ = new BehaviorSubject(false);
|
|
@@ -2299,8 +2345,8 @@ function createGatherVM(client, resourceId2) {
|
|
|
2299
2345
|
context$.next(null);
|
|
2300
2346
|
annotationId$.next(annotationId(event.annotationId));
|
|
2301
2347
|
const gatherSub = client.gather.annotation(
|
|
2302
|
-
annotationId(event.annotationId),
|
|
2303
2348
|
resourceId2,
|
|
2349
|
+
annotationId(event.annotationId),
|
|
2304
2350
|
{ contextWindow: event.options?.contextWindow ?? 2e3 }
|
|
2305
2351
|
).pipe(
|
|
2306
2352
|
timeout(6e4)
|
|
@@ -2337,7 +2383,7 @@ function createGatherVM(client, resourceId2) {
|
|
|
2337
2383
|
}
|
|
2338
2384
|
};
|
|
2339
2385
|
}
|
|
2340
|
-
function
|
|
2386
|
+
function createMatchStateUnit(client, _resourceId) {
|
|
2341
2387
|
const subs = [];
|
|
2342
2388
|
subs.push(client.bus.get("match:search-requested").subscribe((event) => {
|
|
2343
2389
|
const searchSub = client.match.search(
|
|
@@ -2363,7 +2409,7 @@ function createMatchVM(client, _resourceId) {
|
|
|
2363
2409
|
}
|
|
2364
2410
|
};
|
|
2365
2411
|
}
|
|
2366
|
-
function
|
|
2412
|
+
function createYieldStateUnit(client, resourceId2, locale) {
|
|
2367
2413
|
const subs = [];
|
|
2368
2414
|
const isGenerating$ = new BehaviorSubject(false);
|
|
2369
2415
|
const progress$ = new BehaviorSubject(null);
|
|
@@ -2418,7 +2464,7 @@ function selectionToSelector(selection) {
|
|
|
2418
2464
|
}
|
|
2419
2465
|
return { type: "TextQuoteSelector", exact: selection.exact, ...selection.prefix && { prefix: selection.prefix }, ...selection.suffix && { suffix: selection.suffix } };
|
|
2420
2466
|
}
|
|
2421
|
-
function
|
|
2467
|
+
function createMarkStateUnit(client, resourceId2) {
|
|
2422
2468
|
const subs = [];
|
|
2423
2469
|
const pendingAnnotation$ = new BehaviorSubject(null);
|
|
2424
2470
|
const assistingMotivation$ = new BehaviorSubject(null);
|
|
@@ -2442,7 +2488,7 @@ function createMarkVM(client, resourceId2) {
|
|
|
2442
2488
|
subs.push(client.bus.get("mark:create-ok").subscribe(() => pendingAnnotation$.next(null)));
|
|
2443
2489
|
subs.push(client.bus.get("mark:submit").subscribe(async (event) => {
|
|
2444
2490
|
try {
|
|
2445
|
-
const result = await client.mark.annotation(
|
|
2491
|
+
const result = await client.mark.annotation({
|
|
2446
2492
|
motivation: event.motivation,
|
|
2447
2493
|
target: { source: resourceId2, selector: event.selector },
|
|
2448
2494
|
body: event.body
|
|
@@ -2503,720 +2549,7 @@ function createMarkVM(client, resourceId2) {
|
|
|
2503
2549
|
}
|
|
2504
2550
|
};
|
|
2505
2551
|
}
|
|
2506
|
-
var RECENT_LIMIT = 10;
|
|
2507
|
-
var SEARCH_LIMIT = 20;
|
|
2508
|
-
function createDiscoverVM(client, browse) {
|
|
2509
|
-
const disposer = createDisposer();
|
|
2510
|
-
const search = createSearchPipeline(
|
|
2511
|
-
(q) => client.browse.resources({ search: q, limit: SEARCH_LIMIT })
|
|
2512
|
-
);
|
|
2513
|
-
disposer.add(search);
|
|
2514
|
-
disposer.add(browse);
|
|
2515
|
-
const recent$ = client.browse.resources({ limit: RECENT_LIMIT, archived: false });
|
|
2516
|
-
const recentResources$ = recent$.pipe(
|
|
2517
|
-
map$1((r) => r ?? [])
|
|
2518
|
-
);
|
|
2519
|
-
const isLoadingRecent$ = recent$.pipe(
|
|
2520
|
-
map$1((r) => r === void 0)
|
|
2521
|
-
);
|
|
2522
|
-
const entityTypes$ = client.browse.entityTypes().pipe(
|
|
2523
|
-
map$1((e) => e ?? [])
|
|
2524
|
-
);
|
|
2525
|
-
return {
|
|
2526
|
-
browse,
|
|
2527
|
-
search,
|
|
2528
|
-
recentResources$,
|
|
2529
|
-
entityTypes$,
|
|
2530
|
-
isLoadingRecent$,
|
|
2531
|
-
dispose: () => disposer.dispose()
|
|
2532
|
-
};
|
|
2533
|
-
}
|
|
2534
|
-
function createEntityTagsVM(client, browse) {
|
|
2535
|
-
const disposer = createDisposer();
|
|
2536
|
-
disposer.add(browse);
|
|
2537
|
-
const newTag$ = new BehaviorSubject("");
|
|
2538
|
-
const error$ = new BehaviorSubject("");
|
|
2539
|
-
const isAdding$ = new BehaviorSubject(false);
|
|
2540
|
-
const raw$ = client.browse.entityTypes();
|
|
2541
|
-
const entityTypes$ = raw$.pipe(map$1((e) => e ?? []));
|
|
2542
|
-
const isLoading$ = raw$.pipe(map$1((e) => e === void 0));
|
|
2543
|
-
const addTag = async () => {
|
|
2544
|
-
const tag = newTag$.getValue().trim();
|
|
2545
|
-
if (!tag) return;
|
|
2546
|
-
error$.next("");
|
|
2547
|
-
isAdding$.next(true);
|
|
2548
|
-
try {
|
|
2549
|
-
await client.mark.entityType(tag);
|
|
2550
|
-
newTag$.next("");
|
|
2551
|
-
} catch (err) {
|
|
2552
|
-
error$.next(err instanceof Error ? err.message : "Failed to add entity type");
|
|
2553
|
-
} finally {
|
|
2554
|
-
isAdding$.next(false);
|
|
2555
|
-
}
|
|
2556
|
-
};
|
|
2557
|
-
return {
|
|
2558
|
-
browse,
|
|
2559
|
-
entityTypes$,
|
|
2560
|
-
isLoading$,
|
|
2561
|
-
newTag$: newTag$.asObservable(),
|
|
2562
|
-
error$: error$.asObservable(),
|
|
2563
|
-
isAdding$: isAdding$.asObservable(),
|
|
2564
|
-
setNewTag: (v) => newTag$.next(v),
|
|
2565
|
-
addTag,
|
|
2566
|
-
dispose: () => {
|
|
2567
|
-
newTag$.complete();
|
|
2568
|
-
error$.complete();
|
|
2569
|
-
isAdding$.complete();
|
|
2570
|
-
disposer.dispose();
|
|
2571
|
-
}
|
|
2572
|
-
};
|
|
2573
|
-
}
|
|
2574
|
-
function createExchangeVM(browse, exportFn, importFn) {
|
|
2575
|
-
const disposer = createDisposer();
|
|
2576
|
-
disposer.add(browse);
|
|
2577
|
-
const selectedFile$ = new BehaviorSubject(null);
|
|
2578
|
-
const preview$ = new BehaviorSubject(null);
|
|
2579
|
-
const importPhase$ = new BehaviorSubject(null);
|
|
2580
|
-
const importMessage$ = new BehaviorSubject(void 0);
|
|
2581
|
-
const importResult$ = new BehaviorSubject(void 0);
|
|
2582
|
-
const isExporting$ = new BehaviorSubject(false);
|
|
2583
|
-
const isImporting$ = new BehaviorSubject(false);
|
|
2584
|
-
const selectFile = (file) => {
|
|
2585
|
-
selectedFile$.next(file);
|
|
2586
|
-
importPhase$.next(null);
|
|
2587
|
-
importMessage$.next(void 0);
|
|
2588
|
-
importResult$.next(void 0);
|
|
2589
|
-
preview$.next({
|
|
2590
|
-
format: file.name.endsWith(".tar.gz") || file.name.endsWith(".gz") ? "semiont-linked-data" : "unknown",
|
|
2591
|
-
version: 1,
|
|
2592
|
-
sourceUrl: "",
|
|
2593
|
-
stats: {}
|
|
2594
|
-
});
|
|
2595
|
-
};
|
|
2596
|
-
const cancelImport = () => {
|
|
2597
|
-
selectedFile$.next(null);
|
|
2598
|
-
preview$.next(null);
|
|
2599
|
-
importPhase$.next(null);
|
|
2600
|
-
importMessage$.next(void 0);
|
|
2601
|
-
importResult$.next(void 0);
|
|
2602
|
-
};
|
|
2603
|
-
const doExport = async () => {
|
|
2604
|
-
isExporting$.next(true);
|
|
2605
|
-
try {
|
|
2606
|
-
const response = await exportFn();
|
|
2607
|
-
if (!response.ok) throw new Error(`Export failed: ${response.status} ${response.statusText}`);
|
|
2608
|
-
const blob = await response.blob();
|
|
2609
|
-
const contentDisposition = response.headers.get("Content-Disposition");
|
|
2610
|
-
const filename = contentDisposition?.match(/filename="(.+?)"/)?.[1] ?? `semiont-export-${Date.now()}.tar.gz`;
|
|
2611
|
-
return { blob, filename };
|
|
2612
|
-
} finally {
|
|
2613
|
-
isExporting$.next(false);
|
|
2614
|
-
}
|
|
2615
|
-
};
|
|
2616
|
-
const doImport = async () => {
|
|
2617
|
-
const file = selectedFile$.getValue();
|
|
2618
|
-
if (!file) return;
|
|
2619
|
-
isImporting$.next(true);
|
|
2620
|
-
importPhase$.next("started");
|
|
2621
|
-
importMessage$.next(void 0);
|
|
2622
|
-
importResult$.next(void 0);
|
|
2623
|
-
try {
|
|
2624
|
-
await importFn(file, {
|
|
2625
|
-
onProgress: (event) => {
|
|
2626
|
-
importPhase$.next(event.phase);
|
|
2627
|
-
importMessage$.next(event.message);
|
|
2628
|
-
if (event.result) importResult$.next(event.result);
|
|
2629
|
-
}
|
|
2630
|
-
});
|
|
2631
|
-
} finally {
|
|
2632
|
-
isImporting$.next(false);
|
|
2633
|
-
}
|
|
2634
|
-
};
|
|
2635
|
-
return {
|
|
2636
|
-
browse,
|
|
2637
|
-
selectedFile$: selectedFile$.asObservable(),
|
|
2638
|
-
preview$: preview$.asObservable(),
|
|
2639
|
-
importPhase$: importPhase$.asObservable(),
|
|
2640
|
-
importMessage$: importMessage$.asObservable(),
|
|
2641
|
-
importResult$: importResult$.asObservable(),
|
|
2642
|
-
isExporting$: isExporting$.asObservable(),
|
|
2643
|
-
isImporting$: isImporting$.asObservable(),
|
|
2644
|
-
selectFile,
|
|
2645
|
-
cancelImport,
|
|
2646
|
-
doExport,
|
|
2647
|
-
doImport,
|
|
2648
|
-
dispose: () => {
|
|
2649
|
-
selectedFile$.complete();
|
|
2650
|
-
preview$.complete();
|
|
2651
|
-
importPhase$.complete();
|
|
2652
|
-
importMessage$.complete();
|
|
2653
|
-
importResult$.complete();
|
|
2654
|
-
isExporting$.complete();
|
|
2655
|
-
isImporting$.complete();
|
|
2656
|
-
disposer.dispose();
|
|
2657
|
-
}
|
|
2658
|
-
};
|
|
2659
|
-
}
|
|
2660
|
-
function createAdminUsersVM(client, browse) {
|
|
2661
|
-
const disposer = createDisposer();
|
|
2662
|
-
disposer.add(browse);
|
|
2663
|
-
const users$ = new BehaviorSubject([]);
|
|
2664
|
-
const stats$ = new BehaviorSubject(null);
|
|
2665
|
-
const usersLoading$ = new BehaviorSubject(true);
|
|
2666
|
-
const statsLoading$ = new BehaviorSubject(true);
|
|
2667
|
-
const fetchUsers = () => {
|
|
2668
|
-
usersLoading$.next(true);
|
|
2669
|
-
client.admin.users().then((data) => {
|
|
2670
|
-
users$.next(data.users ?? []);
|
|
2671
|
-
usersLoading$.next(false);
|
|
2672
|
-
}).catch(() => usersLoading$.next(false));
|
|
2673
|
-
};
|
|
2674
|
-
const fetchStats = () => {
|
|
2675
|
-
statsLoading$.next(true);
|
|
2676
|
-
client.admin.userStats().then((data) => {
|
|
2677
|
-
stats$.next(data.stats ?? null);
|
|
2678
|
-
statsLoading$.next(false);
|
|
2679
|
-
}).catch(() => statsLoading$.next(false));
|
|
2680
|
-
};
|
|
2681
|
-
fetchUsers();
|
|
2682
|
-
fetchStats();
|
|
2683
|
-
const updateUser = async (id, data) => {
|
|
2684
|
-
await client.admin.updateUser(userDID(id), data);
|
|
2685
|
-
fetchUsers();
|
|
2686
|
-
fetchStats();
|
|
2687
|
-
};
|
|
2688
|
-
return {
|
|
2689
|
-
browse,
|
|
2690
|
-
users$: users$.asObservable(),
|
|
2691
|
-
stats$: stats$.asObservable(),
|
|
2692
|
-
usersLoading$: usersLoading$.asObservable(),
|
|
2693
|
-
statsLoading$: statsLoading$.asObservable(),
|
|
2694
|
-
updateUser,
|
|
2695
|
-
dispose: () => {
|
|
2696
|
-
users$.complete();
|
|
2697
|
-
stats$.complete();
|
|
2698
|
-
usersLoading$.complete();
|
|
2699
|
-
statsLoading$.complete();
|
|
2700
|
-
disposer.dispose();
|
|
2701
|
-
}
|
|
2702
|
-
};
|
|
2703
|
-
}
|
|
2704
|
-
function createAdminSecurityVM(client, browse) {
|
|
2705
|
-
const disposer = createDisposer();
|
|
2706
|
-
disposer.add(browse);
|
|
2707
|
-
const providers$ = new BehaviorSubject([]);
|
|
2708
|
-
const allowedDomains$ = new BehaviorSubject([]);
|
|
2709
|
-
const isLoading$ = new BehaviorSubject(true);
|
|
2710
|
-
client.admin.oauthConfig().then((data) => {
|
|
2711
|
-
const config = data;
|
|
2712
|
-
providers$.next(config.providers ?? []);
|
|
2713
|
-
allowedDomains$.next(config.allowedDomains ?? []);
|
|
2714
|
-
isLoading$.next(false);
|
|
2715
|
-
}).catch(() => isLoading$.next(false));
|
|
2716
|
-
return {
|
|
2717
|
-
browse,
|
|
2718
|
-
providers$: providers$.asObservable(),
|
|
2719
|
-
allowedDomains$: allowedDomains$.asObservable(),
|
|
2720
|
-
isLoading$: isLoading$.asObservable(),
|
|
2721
|
-
dispose: () => {
|
|
2722
|
-
providers$.complete();
|
|
2723
|
-
allowedDomains$.complete();
|
|
2724
|
-
isLoading$.complete();
|
|
2725
|
-
disposer.dispose();
|
|
2726
|
-
}
|
|
2727
|
-
};
|
|
2728
|
-
}
|
|
2729
|
-
function createWelcomeVM(client) {
|
|
2730
|
-
const disposer = createDisposer();
|
|
2731
|
-
const userData$ = new BehaviorSubject(null);
|
|
2732
|
-
const isProcessing$ = new BehaviorSubject(false);
|
|
2733
|
-
client.auth.me().then((data) => userData$.next(data)).catch(() => {
|
|
2734
|
-
});
|
|
2735
|
-
const acceptTerms = async () => {
|
|
2736
|
-
isProcessing$.next(true);
|
|
2737
|
-
try {
|
|
2738
|
-
await client.auth.acceptTerms();
|
|
2739
|
-
userData$.next({ ...userData$.getValue(), termsAcceptedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2740
|
-
} finally {
|
|
2741
|
-
isProcessing$.next(false);
|
|
2742
|
-
}
|
|
2743
|
-
};
|
|
2744
|
-
return {
|
|
2745
|
-
userData$: userData$.asObservable(),
|
|
2746
|
-
isProcessing$: isProcessing$.asObservable(),
|
|
2747
|
-
acceptTerms,
|
|
2748
|
-
dispose: () => {
|
|
2749
|
-
userData$.complete();
|
|
2750
|
-
isProcessing$.complete();
|
|
2751
|
-
disposer.dispose();
|
|
2752
|
-
}
|
|
2753
|
-
};
|
|
2754
|
-
}
|
|
2755
|
-
function createResourceLoaderVM(client, resourceId2) {
|
|
2756
|
-
const raw$ = client.browse.resource(resourceId2);
|
|
2757
|
-
const resource$ = raw$;
|
|
2758
|
-
const isLoading$ = raw$.pipe(map$1((r) => r === void 0));
|
|
2759
|
-
return {
|
|
2760
|
-
resource$,
|
|
2761
|
-
isLoading$,
|
|
2762
|
-
invalidate: () => client.browse.invalidateResourceDetail(resourceId2),
|
|
2763
|
-
dispose: () => {
|
|
2764
|
-
}
|
|
2765
|
-
};
|
|
2766
|
-
}
|
|
2767
|
-
function createSessionVM(client) {
|
|
2768
|
-
const isLoggingOut$ = new BehaviorSubject(false);
|
|
2769
|
-
const logout = async () => {
|
|
2770
|
-
isLoggingOut$.next(true);
|
|
2771
|
-
try {
|
|
2772
|
-
await client.auth.logout();
|
|
2773
|
-
} catch {
|
|
2774
|
-
} finally {
|
|
2775
|
-
isLoggingOut$.next(false);
|
|
2776
|
-
}
|
|
2777
|
-
};
|
|
2778
|
-
return {
|
|
2779
|
-
isLoggingOut$: isLoggingOut$.asObservable(),
|
|
2780
|
-
logout,
|
|
2781
|
-
dispose: () => {
|
|
2782
|
-
isLoggingOut$.complete();
|
|
2783
|
-
}
|
|
2784
|
-
};
|
|
2785
|
-
}
|
|
2786
|
-
var SMELTER_CHANNELS = [
|
|
2787
|
-
"yield:created",
|
|
2788
|
-
"yield:updated",
|
|
2789
|
-
"yield:representation-added",
|
|
2790
|
-
"mark:archived",
|
|
2791
|
-
"mark:added",
|
|
2792
|
-
"mark:removed"
|
|
2793
|
-
];
|
|
2794
|
-
function createSmelterActorVM(options) {
|
|
2795
|
-
const actor = createActorVM({
|
|
2796
|
-
baseUrl: options.baseUrl,
|
|
2797
|
-
token: options.token,
|
|
2798
|
-
channels: [...SMELTER_CHANNELS],
|
|
2799
|
-
reconnectMs: options.reconnectMs
|
|
2800
|
-
});
|
|
2801
|
-
const events$ = merge(
|
|
2802
|
-
...SMELTER_CHANNELS.map(
|
|
2803
|
-
(channel) => actor.on$(channel).pipe(
|
|
2804
|
-
map((payload) => ({
|
|
2805
|
-
type: channel,
|
|
2806
|
-
resourceId: payload.resourceId,
|
|
2807
|
-
payload
|
|
2808
|
-
}))
|
|
2809
|
-
)
|
|
2810
|
-
)
|
|
2811
|
-
);
|
|
2812
|
-
return {
|
|
2813
|
-
events$,
|
|
2814
|
-
state$: actor.state$,
|
|
2815
|
-
emit: (channel, payload) => actor.emit(channel, payload),
|
|
2816
|
-
start: () => actor.start(),
|
|
2817
|
-
stop: () => actor.stop(),
|
|
2818
|
-
dispose: () => actor.dispose()
|
|
2819
|
-
};
|
|
2820
|
-
}
|
|
2821
|
-
function createJobClaimAdapter(options) {
|
|
2822
|
-
const { actor, jobTypes } = options;
|
|
2823
|
-
const activeJob$ = new BehaviorSubject(null);
|
|
2824
|
-
const isProcessing$ = new BehaviorSubject(false);
|
|
2825
|
-
const jobsCompleted$ = new BehaviorSubject(0);
|
|
2826
|
-
const errors$ = new Subject();
|
|
2827
|
-
let jobSubscription = null;
|
|
2828
|
-
let started = false;
|
|
2829
|
-
const claimJob = async (assignment) => {
|
|
2830
|
-
try {
|
|
2831
|
-
const correlationId = crypto.randomUUID();
|
|
2832
|
-
const result$ = merge(
|
|
2833
|
-
actor.on$("job:claimed").pipe(
|
|
2834
|
-
filter$1((e) => e.correlationId === correlationId),
|
|
2835
|
-
map$1((e) => ({ ok: true, response: e.response }))
|
|
2836
|
-
),
|
|
2837
|
-
actor.on$("job:claim-failed").pipe(
|
|
2838
|
-
filter$1((e) => e.correlationId === correlationId),
|
|
2839
|
-
map$1(() => ({ ok: false }))
|
|
2840
|
-
)
|
|
2841
|
-
).pipe(take$1(1), timeout$1(1e4));
|
|
2842
|
-
const resultPromise = firstValueFrom(result$);
|
|
2843
|
-
await actor.emit("job:claim", { correlationId, jobId: assignment.jobId });
|
|
2844
|
-
const result = await resultPromise;
|
|
2845
|
-
if (!result.ok) return null;
|
|
2846
|
-
const job = result.response;
|
|
2847
|
-
return {
|
|
2848
|
-
jobId: assignment.jobId,
|
|
2849
|
-
type: assignment.type,
|
|
2850
|
-
resourceId: assignment.resourceId,
|
|
2851
|
-
userId: job.metadata?.userId ?? "",
|
|
2852
|
-
params: job.params ?? {}
|
|
2853
|
-
};
|
|
2854
|
-
} catch {
|
|
2855
|
-
return null;
|
|
2856
|
-
}
|
|
2857
|
-
};
|
|
2858
|
-
return {
|
|
2859
|
-
activeJob$: activeJob$.asObservable(),
|
|
2860
|
-
isProcessing$: isProcessing$.asObservable(),
|
|
2861
|
-
jobsCompleted$: jobsCompleted$.asObservable(),
|
|
2862
|
-
errors$: errors$.asObservable(),
|
|
2863
|
-
start: () => {
|
|
2864
|
-
if (started) return;
|
|
2865
|
-
started = true;
|
|
2866
|
-
actor.addChannels(["job:queued"]);
|
|
2867
|
-
jobSubscription = actor.on$("job:queued").subscribe((event) => {
|
|
2868
|
-
const jobType = event.jobType;
|
|
2869
|
-
if (jobTypes.length > 0 && !jobTypes.includes(jobType)) return;
|
|
2870
|
-
if (isProcessing$.getValue()) return;
|
|
2871
|
-
isProcessing$.next(true);
|
|
2872
|
-
claimJob({ jobId: event.jobId, type: jobType, resourceId: event.resourceId }).then((job) => {
|
|
2873
|
-
if (job) {
|
|
2874
|
-
activeJob$.next(job);
|
|
2875
|
-
} else {
|
|
2876
|
-
isProcessing$.next(false);
|
|
2877
|
-
}
|
|
2878
|
-
}).catch(() => {
|
|
2879
|
-
isProcessing$.next(false);
|
|
2880
|
-
});
|
|
2881
|
-
});
|
|
2882
|
-
},
|
|
2883
|
-
stop: () => {
|
|
2884
|
-
jobSubscription?.unsubscribe();
|
|
2885
|
-
jobSubscription = null;
|
|
2886
|
-
started = false;
|
|
2887
|
-
},
|
|
2888
|
-
completeJob: () => {
|
|
2889
|
-
activeJob$.next(null);
|
|
2890
|
-
isProcessing$.next(false);
|
|
2891
|
-
jobsCompleted$.next(jobsCompleted$.getValue() + 1);
|
|
2892
|
-
},
|
|
2893
|
-
failJob: (jid, error) => {
|
|
2894
|
-
activeJob$.next(null);
|
|
2895
|
-
isProcessing$.next(false);
|
|
2896
|
-
errors$.next({ jobId: jid, error });
|
|
2897
|
-
},
|
|
2898
|
-
dispose: () => {
|
|
2899
|
-
jobSubscription?.unsubscribe();
|
|
2900
|
-
jobSubscription = null;
|
|
2901
|
-
started = false;
|
|
2902
|
-
activeJob$.complete();
|
|
2903
|
-
isProcessing$.complete();
|
|
2904
|
-
jobsCompleted$.complete();
|
|
2905
|
-
errors$.complete();
|
|
2906
|
-
}
|
|
2907
|
-
};
|
|
2908
|
-
}
|
|
2909
|
-
function createJobQueueVM(client) {
|
|
2910
|
-
const jobs$ = new BehaviorSubject([]);
|
|
2911
|
-
const jobCreated$ = new Subject();
|
|
2912
|
-
const jobCompleted$ = new Subject();
|
|
2913
|
-
const jobFailed$ = new Subject();
|
|
2914
|
-
const pendingByType$ = jobs$.pipe(
|
|
2915
|
-
map$1((all) => {
|
|
2916
|
-
const counts = /* @__PURE__ */ new Map();
|
|
2917
|
-
for (const j of all) {
|
|
2918
|
-
if (j.status === "pending") {
|
|
2919
|
-
counts.set(j.type, (counts.get(j.type) ?? 0) + 1);
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
return counts;
|
|
2923
|
-
})
|
|
2924
|
-
);
|
|
2925
|
-
const runningJobs$ = jobs$.pipe(
|
|
2926
|
-
map$1((all) => all.filter((j) => j.status === "running"))
|
|
2927
|
-
);
|
|
2928
|
-
const addOrUpdate = (job) => {
|
|
2929
|
-
const current = jobs$.getValue();
|
|
2930
|
-
const idx = current.findIndex((j) => j.jobId === job.jobId);
|
|
2931
|
-
if (idx >= 0) {
|
|
2932
|
-
const next = [...current];
|
|
2933
|
-
next[idx] = job;
|
|
2934
|
-
jobs$.next(next);
|
|
2935
|
-
} else {
|
|
2936
|
-
jobs$.next([...current, job]);
|
|
2937
|
-
}
|
|
2938
|
-
};
|
|
2939
|
-
const subs = [
|
|
2940
|
-
client.bus.get("job:queued").subscribe((event) => {
|
|
2941
|
-
const job = {
|
|
2942
|
-
jobId: event.jobId,
|
|
2943
|
-
type: event.jobType,
|
|
2944
|
-
status: "pending",
|
|
2945
|
-
resourceId: event.resourceId,
|
|
2946
|
-
userId: event.userId,
|
|
2947
|
-
created: (/* @__PURE__ */ new Date()).toISOString()
|
|
2948
|
-
};
|
|
2949
|
-
addOrUpdate(job);
|
|
2950
|
-
jobCreated$.next(job);
|
|
2951
|
-
}),
|
|
2952
|
-
client.bus.get("job:complete").subscribe((event) => {
|
|
2953
|
-
if (!event._userId) {
|
|
2954
|
-
throw new Error("job:complete missing _userId (gateway injection)");
|
|
2955
|
-
}
|
|
2956
|
-
const job = {
|
|
2957
|
-
jobId: event.jobId,
|
|
2958
|
-
type: event.jobType,
|
|
2959
|
-
status: "complete",
|
|
2960
|
-
resourceId: event.resourceId,
|
|
2961
|
-
userId: event._userId,
|
|
2962
|
-
created: "",
|
|
2963
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2964
|
-
result: event.result
|
|
2965
|
-
};
|
|
2966
|
-
addOrUpdate(job);
|
|
2967
|
-
jobCompleted$.next(job);
|
|
2968
|
-
}),
|
|
2969
|
-
client.bus.get("job:fail").subscribe((event) => {
|
|
2970
|
-
if (!event._userId) {
|
|
2971
|
-
throw new Error("job:fail missing _userId (gateway injection)");
|
|
2972
|
-
}
|
|
2973
|
-
const job = {
|
|
2974
|
-
jobId: event.jobId,
|
|
2975
|
-
type: event.jobType,
|
|
2976
|
-
status: "failed",
|
|
2977
|
-
resourceId: event.resourceId,
|
|
2978
|
-
userId: event._userId,
|
|
2979
|
-
created: "",
|
|
2980
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2981
|
-
error: event.error
|
|
2982
|
-
};
|
|
2983
|
-
addOrUpdate(job);
|
|
2984
|
-
jobFailed$.next(job);
|
|
2985
|
-
})
|
|
2986
|
-
];
|
|
2987
|
-
return {
|
|
2988
|
-
jobs$: jobs$.asObservable(),
|
|
2989
|
-
pendingByType$,
|
|
2990
|
-
runningJobs$,
|
|
2991
|
-
jobCreated$: jobCreated$.asObservable(),
|
|
2992
|
-
jobCompleted$: jobCompleted$.asObservable(),
|
|
2993
|
-
jobFailed$: jobFailed$.asObservable(),
|
|
2994
|
-
dispose: () => {
|
|
2995
|
-
subs.forEach((s) => s.unsubscribe());
|
|
2996
|
-
jobs$.complete();
|
|
2997
|
-
jobCreated$.complete();
|
|
2998
|
-
jobCompleted$.complete();
|
|
2999
|
-
jobFailed$.complete();
|
|
3000
|
-
}
|
|
3001
|
-
};
|
|
3002
|
-
}
|
|
3003
|
-
var WIZARD_CLOSED = {
|
|
3004
|
-
open: false,
|
|
3005
|
-
annotationId: null,
|
|
3006
|
-
resourceId: null,
|
|
3007
|
-
defaultTitle: "",
|
|
3008
|
-
entityTypes: []
|
|
3009
|
-
};
|
|
3010
|
-
function createResourceViewerPageVM(client, resourceId2, locale, browse, options) {
|
|
3011
|
-
const disposer = createDisposer();
|
|
3012
|
-
const beckon = createBeckonVM(client);
|
|
3013
|
-
const mark = createMarkVM(client, resourceId2);
|
|
3014
|
-
const gather = createGatherVM(client, resourceId2);
|
|
3015
|
-
const matchVM = createMatchVM(client);
|
|
3016
|
-
const yieldVM = createYieldVM(client, resourceId2, locale);
|
|
3017
|
-
disposer.add(beckon);
|
|
3018
|
-
disposer.add(browse);
|
|
3019
|
-
disposer.add(mark);
|
|
3020
|
-
disposer.add(gather);
|
|
3021
|
-
disposer.add(matchVM);
|
|
3022
|
-
disposer.add(yieldVM);
|
|
3023
|
-
const annotations$ = client.browse.annotations(resourceId2).pipe(
|
|
3024
|
-
map$1((a) => a ?? [])
|
|
3025
|
-
);
|
|
3026
|
-
const annotationGroups$ = annotations$.pipe(
|
|
3027
|
-
map$1((anns) => {
|
|
3028
|
-
const groups = { highlights: [], comments: [], assessments: [], references: [], tags: [] };
|
|
3029
|
-
for (const ann of anns) {
|
|
3030
|
-
if (isHighlight(ann)) groups.highlights.push(ann);
|
|
3031
|
-
else if (isComment(ann)) groups.comments.push(ann);
|
|
3032
|
-
else if (isAssessment(ann)) groups.assessments.push(ann);
|
|
3033
|
-
else if (isReference(ann)) groups.references.push(ann);
|
|
3034
|
-
else if (isTag(ann)) groups.tags.push(ann);
|
|
3035
|
-
}
|
|
3036
|
-
return groups;
|
|
3037
|
-
})
|
|
3038
|
-
);
|
|
3039
|
-
const entityTypes$ = client.browse.entityTypes().pipe(
|
|
3040
|
-
map$1((e) => e ?? [])
|
|
3041
|
-
);
|
|
3042
|
-
const events$ = client.browse.events(resourceId2).pipe(
|
|
3043
|
-
map$1((e) => e ?? [])
|
|
3044
|
-
);
|
|
3045
|
-
const referencedBy$ = client.browse.referencedBy(resourceId2).pipe(
|
|
3046
|
-
map$1((r) => r ?? [])
|
|
3047
|
-
);
|
|
3048
|
-
const content$ = new BehaviorSubject("");
|
|
3049
|
-
const contentLoading$ = new BehaviorSubject(false);
|
|
3050
|
-
const mediaToken$ = new BehaviorSubject(null);
|
|
3051
|
-
const mediaType = options?.mediaType || "text/plain";
|
|
3052
|
-
const isBinaryType = mediaType.startsWith("image/") || mediaType === "application/pdf";
|
|
3053
|
-
if (!isBinaryType && mediaType) {
|
|
3054
|
-
contentLoading$.next(true);
|
|
3055
|
-
client.browse.resourceRepresentation(resourceId2, { accept: mediaType }).then(({ data }) => {
|
|
3056
|
-
content$.next(decodeWithCharset(data, mediaType));
|
|
3057
|
-
contentLoading$.next(false);
|
|
3058
|
-
}).catch(() => {
|
|
3059
|
-
contentLoading$.next(false);
|
|
3060
|
-
});
|
|
3061
|
-
}
|
|
3062
|
-
if (isBinaryType) {
|
|
3063
|
-
client.auth.mediaToken(resourceId2).then(({ token }) => mediaToken$.next(token)).catch(() => {
|
|
3064
|
-
});
|
|
3065
|
-
}
|
|
3066
|
-
const wizard$ = new BehaviorSubject(WIZARD_CLOSED);
|
|
3067
|
-
const unsubscribeResource = client.subscribeToResource(resourceId2);
|
|
3068
|
-
disposer.add(unsubscribeResource);
|
|
3069
|
-
const bindInitiateSub = client.bus.get("bind:initiate").subscribe((event) => {
|
|
3070
|
-
wizard$.next({
|
|
3071
|
-
open: true,
|
|
3072
|
-
annotationId: event.annotationId,
|
|
3073
|
-
resourceId: event.resourceId,
|
|
3074
|
-
defaultTitle: event.defaultTitle,
|
|
3075
|
-
entityTypes: event.entityTypes
|
|
3076
|
-
});
|
|
3077
|
-
client.bus.get("gather:requested").next({
|
|
3078
|
-
correlationId: crypto.randomUUID(),
|
|
3079
|
-
annotationId: event.annotationId,
|
|
3080
|
-
resourceId: event.resourceId,
|
|
3081
|
-
options: { contextWindow: 2e3 }
|
|
3082
|
-
});
|
|
3083
|
-
});
|
|
3084
|
-
disposer.add(() => bindInitiateSub.unsubscribe());
|
|
3085
|
-
return {
|
|
3086
|
-
beckon,
|
|
3087
|
-
browse,
|
|
3088
|
-
mark,
|
|
3089
|
-
gather,
|
|
3090
|
-
yield: yieldVM,
|
|
3091
|
-
annotations$,
|
|
3092
|
-
annotationGroups$,
|
|
3093
|
-
entityTypes$,
|
|
3094
|
-
events$,
|
|
3095
|
-
referencedBy$,
|
|
3096
|
-
content$: content$.asObservable(),
|
|
3097
|
-
contentLoading$: contentLoading$.asObservable(),
|
|
3098
|
-
mediaToken$: mediaToken$.asObservable(),
|
|
3099
|
-
wizard$: wizard$.asObservable(),
|
|
3100
|
-
closeWizard: () => wizard$.next(WIZARD_CLOSED),
|
|
3101
|
-
dispose: () => {
|
|
3102
|
-
wizard$.complete();
|
|
3103
|
-
content$.complete();
|
|
3104
|
-
contentLoading$.complete();
|
|
3105
|
-
mediaToken$.complete();
|
|
3106
|
-
disposer.dispose();
|
|
3107
|
-
}
|
|
3108
|
-
};
|
|
3109
|
-
}
|
|
3110
|
-
function createComposePageVM(client, browse, params, auth) {
|
|
3111
|
-
const disposer = createDisposer();
|
|
3112
|
-
disposer.add(browse);
|
|
3113
|
-
const isReferenceMode = Boolean(params.annotationUri && params.sourceDocumentId && params.name);
|
|
3114
|
-
const isCloneMode = params.mode === "clone" && Boolean(params.token);
|
|
3115
|
-
const pageMode = isCloneMode ? "clone" : isReferenceMode ? "reference" : "new";
|
|
3116
|
-
const mode$ = new BehaviorSubject(pageMode);
|
|
3117
|
-
const loading$ = new BehaviorSubject(true);
|
|
3118
|
-
const cloneData$ = new BehaviorSubject(null);
|
|
3119
|
-
const referenceData$ = new BehaviorSubject(null);
|
|
3120
|
-
const gatheredContext$ = new BehaviorSubject(null);
|
|
3121
|
-
const entityTypes$ = client.browse.entityTypes().pipe(
|
|
3122
|
-
map$1((e) => e ?? [])
|
|
3123
|
-
);
|
|
3124
|
-
if (isReferenceMode) {
|
|
3125
|
-
const entityTypes = params.entityTypes ? params.entityTypes.split(",") : [];
|
|
3126
|
-
referenceData$.next({
|
|
3127
|
-
annotationUri: params.annotationUri,
|
|
3128
|
-
sourceDocumentId: params.sourceDocumentId,
|
|
3129
|
-
name: params.name,
|
|
3130
|
-
entityTypes
|
|
3131
|
-
});
|
|
3132
|
-
if (params.storedContext) {
|
|
3133
|
-
try {
|
|
3134
|
-
gatheredContext$.next(JSON.parse(params.storedContext));
|
|
3135
|
-
} catch {
|
|
3136
|
-
}
|
|
3137
|
-
}
|
|
3138
|
-
loading$.next(false);
|
|
3139
|
-
} else if (isCloneMode) {
|
|
3140
|
-
void (async () => {
|
|
3141
|
-
try {
|
|
3142
|
-
const tokenResult = await client.yield.fromToken(params.token);
|
|
3143
|
-
if (tokenResult && auth) {
|
|
3144
|
-
const rId = resourceId(tokenResult["@id"]);
|
|
3145
|
-
const mediaType = getPrimaryMediaType(tokenResult) || "text/plain";
|
|
3146
|
-
const { data } = await client.browse.resourceRepresentation(rId, {
|
|
3147
|
-
accept: mediaType
|
|
3148
|
-
});
|
|
3149
|
-
const content = decodeWithCharset(data, mediaType);
|
|
3150
|
-
cloneData$.next({ sourceResource: tokenResult, sourceContent: content });
|
|
3151
|
-
}
|
|
3152
|
-
} catch {
|
|
3153
|
-
}
|
|
3154
|
-
loading$.next(false);
|
|
3155
|
-
})();
|
|
3156
|
-
} else {
|
|
3157
|
-
loading$.next(false);
|
|
3158
|
-
}
|
|
3159
|
-
const save = async (saveParams) => {
|
|
3160
|
-
if (saveParams.mode === "clone") {
|
|
3161
|
-
const response2 = await client.yield.createFromToken({
|
|
3162
|
-
token: params.token,
|
|
3163
|
-
name: saveParams.name,
|
|
3164
|
-
content: saveParams.content,
|
|
3165
|
-
archiveOriginal: saveParams.archiveOriginal ?? true
|
|
3166
|
-
});
|
|
3167
|
-
return response2.resourceId;
|
|
3168
|
-
}
|
|
3169
|
-
let fileToUpload;
|
|
3170
|
-
let mimeType;
|
|
3171
|
-
if (saveParams.file) {
|
|
3172
|
-
fileToUpload = saveParams.file;
|
|
3173
|
-
mimeType = saveParams.format ?? "application/octet-stream";
|
|
3174
|
-
} else {
|
|
3175
|
-
const blob = new Blob([saveParams.content || ""], { type: saveParams.format ?? "application/octet-stream" });
|
|
3176
|
-
const extension = saveParams.format === "text/plain" ? ".txt" : saveParams.format === "text/html" ? ".html" : ".md";
|
|
3177
|
-
fileToUpload = new File([blob], saveParams.name + extension, { type: saveParams.format ?? "application/octet-stream" });
|
|
3178
|
-
mimeType = saveParams.format ?? "application/octet-stream";
|
|
3179
|
-
}
|
|
3180
|
-
const format = saveParams.charset && !saveParams.file ? `${mimeType}; charset=${saveParams.charset}` : mimeType;
|
|
3181
|
-
const response = await client.yield.resource({
|
|
3182
|
-
name: saveParams.name,
|
|
3183
|
-
file: fileToUpload,
|
|
3184
|
-
format,
|
|
3185
|
-
entityTypes: saveParams.entityTypes || [],
|
|
3186
|
-
language: saveParams.language,
|
|
3187
|
-
creationMethod: "ui",
|
|
3188
|
-
storageUri: saveParams.storageUri
|
|
3189
|
-
});
|
|
3190
|
-
const newResourceId = response.resourceId;
|
|
3191
|
-
if (saveParams.mode === "reference" && saveParams.annotationUri && saveParams.sourceDocumentId) {
|
|
3192
|
-
await client.bind.body(
|
|
3193
|
-
resourceId(saveParams.sourceDocumentId),
|
|
3194
|
-
annotationId(saveParams.annotationUri),
|
|
3195
|
-
[{ op: "add", item: { type: "SpecificResource", source: newResourceId, purpose: "linking" } }]
|
|
3196
|
-
);
|
|
3197
|
-
}
|
|
3198
|
-
return newResourceId;
|
|
3199
|
-
};
|
|
3200
|
-
return {
|
|
3201
|
-
browse,
|
|
3202
|
-
mode$: mode$.asObservable(),
|
|
3203
|
-
loading$: loading$.asObservable(),
|
|
3204
|
-
cloneData$: cloneData$.asObservable(),
|
|
3205
|
-
referenceData$: referenceData$.asObservable(),
|
|
3206
|
-
gatheredContext$: gatheredContext$.asObservable(),
|
|
3207
|
-
entityTypes$,
|
|
3208
|
-
save,
|
|
3209
|
-
dispose: () => {
|
|
3210
|
-
mode$.complete();
|
|
3211
|
-
loading$.complete();
|
|
3212
|
-
cloneData$.complete();
|
|
3213
|
-
referenceData$.complete();
|
|
3214
|
-
gatheredContext$.complete();
|
|
3215
|
-
disposer.dispose();
|
|
3216
|
-
}
|
|
3217
|
-
};
|
|
3218
|
-
}
|
|
3219
2552
|
|
|
3220
|
-
export { AdminNamespace, AuthNamespace, BeckonNamespace, BindNamespace, BrowseNamespace, BusRequestError,
|
|
2553
|
+
export { AdminNamespace, AuthNamespace, BeckonNamespace, BindNamespace, BrowseNamespace, BusRequestError, CacheObservable, FrameNamespace, GatherNamespace, HOVER_DELAY_MS, InMemorySessionStorage, JobNamespace, MarkNamespace, MatchNamespace, SemiontBrowser, SemiontClient, SemiontSession, SemiontSessionError, SessionSignals, StreamObservable, UploadObservable, YieldNamespace, busRequest, createBeckonStateUnit, createDisposer, createGatherStateUnit, createHoverHandlers, createHttpSessionFactory, createMarkStateUnit, createMatchStateUnit, createSearchPipeline, createYieldStateUnit, defaultProtocol, getBrowser, httpKb, isValidHostname, kbBackendUrl, setStoredSession };
|
|
3221
2554
|
//# sourceMappingURL=index.js.map
|
|
3222
2555
|
//# sourceMappingURL=index.js.map
|