@qaecy/cue-sdk 0.0.14 → 0.0.17
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 +151 -0
- package/cue-yUoyoy61.js +8701 -0
- package/index.d.ts +6 -0
- package/index.js +17 -14
- package/lib/api.d.ts +22 -1
- package/lib/cue.d.ts +63 -0
- package/lib/documents.d.ts +45 -2
- package/lib/entities.d.ts +37 -1
- package/lib/gis.d.ts +89 -0
- package/lib/models.d.ts +44 -0
- package/lib/project-view.d.ts +5 -0
- package/lib/schema.d.ts +6 -1
- package/lib/storage.d.ts +26 -0
- package/lib/tables.d.ts +14 -0
- package/node.js +41 -38
- package/package.json +1 -1
- package/variables.d.ts +2 -0
- package/cue-DjbCgB69.js +0 -3614
package/cue-DjbCgB69.js
DELETED
|
@@ -1,3614 +0,0 @@
|
|
|
1
|
-
import { getApps as Lt, initializeApp as jt } from "firebase/app";
|
|
2
|
-
import { ref as w, getDownloadURL as k, getMetadata as M, getBlob as xt, updateMetadata as qt, uploadBytesResumable as et, uploadBytes as z, StringFormat as Nt, uploadString as kt, listAll as $, getStorage as P, connectStorageEmulator as Bt } from "firebase/storage";
|
|
3
|
-
import { Writer as Ft, DataFactory as U } from "n3";
|
|
4
|
-
import { getFirestore as Ht, connectFirestoreEmulator as zt, collection as st, getDocs as Gt, query as Wt, where as Kt, limit as Qt, doc as it, getDoc as Vt, setDoc as Xt, serverTimestamp as Yt, increment as Jt } from "firebase/firestore";
|
|
5
|
-
import { getAuth as Zt, connectAuthEmulator as te, onIdTokenChanged as ee, getIdTokenResult as G, signInWithEmailAndPassword as se, GoogleAuthProvider as W, OAuthProvider as K, signInWithPopup as ie, signInWithRedirect as ne, getRedirectResult as ae, signInWithCustomToken as oe, signOut as re, onAuthStateChanged as ce, fetchSignInMethodsForEmail as le, linkWithPopup as ue, unlink as pe, reauthenticateWithCredential as nt, EmailAuthProvider as B, updatePassword as he, linkWithCredential as de, verifyBeforeUpdateEmail as fe, sendEmailVerification as me } from "firebase/auth";
|
|
6
|
-
import { v5 as ye } from "uuid";
|
|
7
|
-
import "spark-md5";
|
|
8
|
-
import { getFunctions as vt, connectFunctionsEmulator as bt, httpsCallable as j } from "firebase/functions";
|
|
9
|
-
class V {
|
|
10
|
-
queryEndpoint;
|
|
11
|
-
updateEndpoint;
|
|
12
|
-
baseHeaders;
|
|
13
|
-
static RELEVANT_HEADER_KEYS = [
|
|
14
|
-
"authorization",
|
|
15
|
-
"Authorization",
|
|
16
|
-
"x-project-id"
|
|
17
|
-
];
|
|
18
|
-
constructor(t) {
|
|
19
|
-
this.queryEndpoint = t.queryEndpoint, this.updateEndpoint = t.updateEndpoint, this.baseHeaders = Object.fromEntries(
|
|
20
|
-
Object.entries(t.originalHeaders || {}).filter(
|
|
21
|
-
([e]) => V.RELEVANT_HEADER_KEYS.includes(e)
|
|
22
|
-
)
|
|
23
|
-
), t.authHeader !== void 0 && (this.baseHeaders.Authorization = t.authHeader);
|
|
24
|
-
}
|
|
25
|
-
async ping() {
|
|
26
|
-
return (await this.query("ASK { }")).boolean;
|
|
27
|
-
}
|
|
28
|
-
async query(t, e = "application/sparql-results+json") {
|
|
29
|
-
let s;
|
|
30
|
-
try {
|
|
31
|
-
s = await fetch(this.queryEndpoint, {
|
|
32
|
-
headers: {
|
|
33
|
-
...this.baseHeaders,
|
|
34
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
35
|
-
Accept: e
|
|
36
|
-
},
|
|
37
|
-
method: "POST",
|
|
38
|
-
body: new URLSearchParams({ query: t })
|
|
39
|
-
});
|
|
40
|
-
} catch (i) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
`Fuseki is not accessible at ${this.queryEndpoint}: ${i instanceof Error ? i.message : String(i)}`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
if (!s.ok) {
|
|
46
|
-
const i = await s.text();
|
|
47
|
-
throw new Error(`Fuseki query failed (HTTP ${s.status}): ${i}`);
|
|
48
|
-
}
|
|
49
|
-
return await s.json();
|
|
50
|
-
}
|
|
51
|
-
async subset(t, e = "text/turtle") {
|
|
52
|
-
const s = await fetch(this.queryEndpoint, {
|
|
53
|
-
headers: {
|
|
54
|
-
...this.baseHeaders,
|
|
55
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
56
|
-
Authorization: this.baseHeaders.Authorization || "",
|
|
57
|
-
Accept: e
|
|
58
|
-
},
|
|
59
|
-
method: "POST",
|
|
60
|
-
body: new URLSearchParams({ query: t })
|
|
61
|
-
});
|
|
62
|
-
return e === "application/ld+json" ? await s.json() : await s.text();
|
|
63
|
-
}
|
|
64
|
-
async update(t) {
|
|
65
|
-
const e = await fetch(this.updateEndpoint, {
|
|
66
|
-
headers: {
|
|
67
|
-
...this.baseHeaders,
|
|
68
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
69
|
-
},
|
|
70
|
-
method: "POST",
|
|
71
|
-
body: new URLSearchParams({ update: t })
|
|
72
|
-
});
|
|
73
|
-
if (!e.ok) {
|
|
74
|
-
const i = await e.text();
|
|
75
|
-
throw new Error(`SPARQL update failed (HTTP ${e.status}): ${i}`);
|
|
76
|
-
}
|
|
77
|
-
return (e.headers.get("content-type") ?? "").includes("application/json") ? await e.json() : {
|
|
78
|
-
ok: e.ok,
|
|
79
|
-
status: e.status,
|
|
80
|
-
message: await e.text()
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
class F extends Error {
|
|
85
|
-
constructor(t) {
|
|
86
|
-
super(`QLever is locked (rebuild in progress): ${t}`), this.name = "QLeverLockedError";
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
class x {
|
|
90
|
-
queryEndpoint;
|
|
91
|
-
updateEndpoint;
|
|
92
|
-
dataEndpoint;
|
|
93
|
-
baseHeaders;
|
|
94
|
-
static RELEVANT_HEADER_KEYS = [
|
|
95
|
-
"authorization",
|
|
96
|
-
"Authorization",
|
|
97
|
-
"x-project-id",
|
|
98
|
-
"x-user-roles"
|
|
99
|
-
// add more if needed
|
|
100
|
-
];
|
|
101
|
-
/** Max retries on 423 Locked (rebuild in progress). */
|
|
102
|
-
static LOCKED_MAX_RETRIES = parseInt(
|
|
103
|
-
(typeof process < "u" ? process.env.QLEVER_LOCKED_MAX_RETRIES : void 0) ?? "10",
|
|
104
|
-
10
|
|
105
|
-
);
|
|
106
|
-
/** Base delay (ms) for exponential backoff on 423. */
|
|
107
|
-
static LOCKED_BASE_DELAY_MS = parseInt(
|
|
108
|
-
(typeof process < "u" ? process.env.QLEVER_LOCKED_BASE_DELAY_MS : void 0) ?? "2000",
|
|
109
|
-
10
|
|
110
|
-
);
|
|
111
|
-
/**
|
|
112
|
-
* Retry an async write operation on 423 Locked with exponential backoff + jitter.
|
|
113
|
-
* 423 means qlever accessor has an ongoing rebuild; we should wait and retry.
|
|
114
|
-
*/
|
|
115
|
-
static async _retryOnLocked(t) {
|
|
116
|
-
const e = x.LOCKED_MAX_RETRIES, s = x.LOCKED_BASE_DELAY_MS;
|
|
117
|
-
let i;
|
|
118
|
-
for (let n = 0; n <= e; n++)
|
|
119
|
-
try {
|
|
120
|
-
return await t();
|
|
121
|
-
} catch (a) {
|
|
122
|
-
if (i = a, a instanceof F && n < e) {
|
|
123
|
-
const c = 0.5 + Math.random(), r = s * Math.pow(2, n) * c;
|
|
124
|
-
await new Promise((u) => setTimeout(u, r));
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
throw a;
|
|
128
|
-
}
|
|
129
|
-
throw i;
|
|
130
|
-
}
|
|
131
|
-
constructor(t) {
|
|
132
|
-
this.queryEndpoint = t.queryEndpoint, this.updateEndpoint = t.updateEndpoint, this.dataEndpoint = this.updateEndpoint.replace(/\/update$/, "/data"), this.baseHeaders = Object.fromEntries(
|
|
133
|
-
Object.entries(t.originalHeaders || {}).filter(
|
|
134
|
-
([e]) => x.RELEVANT_HEADER_KEYS.includes(e)
|
|
135
|
-
)
|
|
136
|
-
), this.baseHeaders["x-user-roles"] = "admin";
|
|
137
|
-
}
|
|
138
|
-
async ping() {
|
|
139
|
-
return (await this.query("ASK { }")).boolean;
|
|
140
|
-
}
|
|
141
|
-
async query(t, e = "application/sparql-results+json") {
|
|
142
|
-
let s;
|
|
143
|
-
try {
|
|
144
|
-
s = await fetch(this.queryEndpoint, {
|
|
145
|
-
headers: {
|
|
146
|
-
...this.baseHeaders,
|
|
147
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
148
|
-
Accept: e
|
|
149
|
-
},
|
|
150
|
-
method: "POST",
|
|
151
|
-
body: new URLSearchParams({ query: t })
|
|
152
|
-
});
|
|
153
|
-
} catch (i) {
|
|
154
|
-
throw new Error(
|
|
155
|
-
`QLever is not accessible at ${this.queryEndpoint}: ${i instanceof Error ? i.message : String(i)}`
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
if (!s.ok) {
|
|
159
|
-
const i = await s.text();
|
|
160
|
-
throw new Error(`QLever query failed (HTTP ${s.status}): ${i}`);
|
|
161
|
-
}
|
|
162
|
-
return await s.json();
|
|
163
|
-
}
|
|
164
|
-
async subset(t, e = "text/turtle") {
|
|
165
|
-
return await (await fetch(this.queryEndpoint, {
|
|
166
|
-
headers: {
|
|
167
|
-
...this.baseHeaders,
|
|
168
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
169
|
-
Accept: e
|
|
170
|
-
},
|
|
171
|
-
method: "POST",
|
|
172
|
-
body: new URLSearchParams({ query: t })
|
|
173
|
-
})).text();
|
|
174
|
-
}
|
|
175
|
-
async update(t) {
|
|
176
|
-
return x._retryOnLocked(async () => {
|
|
177
|
-
const e = await fetch(this.updateEndpoint, {
|
|
178
|
-
headers: {
|
|
179
|
-
...this.baseHeaders,
|
|
180
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
181
|
-
},
|
|
182
|
-
method: "POST",
|
|
183
|
-
body: new URLSearchParams({ update: t })
|
|
184
|
-
});
|
|
185
|
-
if (!e.ok) {
|
|
186
|
-
const s = await e.text();
|
|
187
|
-
throw e.status === 423 ? new F(s) : new Error(`SPARQL update failed (HTTP ${e.status}): ${s}`);
|
|
188
|
-
}
|
|
189
|
-
return await e.json();
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Insert quads via the /data endpoint, grouped by named graph.
|
|
194
|
-
* This is preferred over SPARQL INSERT DATA for QLever because
|
|
195
|
-
* the /data endpoint correctly registers named graphs in the index.
|
|
196
|
-
*/
|
|
197
|
-
async insertData(t) {
|
|
198
|
-
await this._postToDataEndpoint(t, this.dataEndpoint);
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Delete quads via the /data/delete endpoint, grouped by named graph.
|
|
202
|
-
*/
|
|
203
|
-
async deleteData(t) {
|
|
204
|
-
await this._postToDataEndpoint(t, `${this.dataEndpoint}/delete`);
|
|
205
|
-
}
|
|
206
|
-
async _postToDataEndpoint(t, e) {
|
|
207
|
-
const s = await this._quadsToNQuads(t), i = await ge(Buffer.from(s, "utf-8"));
|
|
208
|
-
await x._retryOnLocked(async () => {
|
|
209
|
-
const n = await fetch(e, {
|
|
210
|
-
method: "POST",
|
|
211
|
-
headers: {
|
|
212
|
-
...this.baseHeaders,
|
|
213
|
-
"Content-Type": "application/n-quads",
|
|
214
|
-
"Content-Encoding": "gzip"
|
|
215
|
-
},
|
|
216
|
-
body: i
|
|
217
|
-
});
|
|
218
|
-
if (!n.ok) {
|
|
219
|
-
const a = await n.text();
|
|
220
|
-
throw n.status === 423 ? new F(a) : new Error(`QLever data POST failed (HTTP ${n.status}): ${a}`);
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
_quadsToNQuads(t) {
|
|
225
|
-
return new Promise((e, s) => {
|
|
226
|
-
const i = new Ft({ format: "application/n-quads" });
|
|
227
|
-
i.addQuads(t), i.end((n, a) => n ? s(n) : e(a));
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
async function ge(l) {
|
|
232
|
-
const t = new CompressionStream("gzip"), e = t.writable.getWriter();
|
|
233
|
-
e.write(new Uint8Array(l)), e.close();
|
|
234
|
-
const s = [], i = t.readable.getReader();
|
|
235
|
-
for (; ; ) {
|
|
236
|
-
const { done: r, value: u } = await i.read();
|
|
237
|
-
if (r) break;
|
|
238
|
-
s.push(u);
|
|
239
|
-
}
|
|
240
|
-
const n = s.reduce((r, u) => r + u.byteLength, 0), a = new Uint8Array(n);
|
|
241
|
-
let c = 0;
|
|
242
|
-
for (const r of s)
|
|
243
|
-
a.set(r, c), c += r.byteLength;
|
|
244
|
-
return a.buffer;
|
|
245
|
-
}
|
|
246
|
-
class _e {
|
|
247
|
-
constructor(t) {
|
|
248
|
-
switch (this.options = t, this.options.graphType) {
|
|
249
|
-
case "qlever":
|
|
250
|
-
this._db = new x(this.options);
|
|
251
|
-
break;
|
|
252
|
-
case "fuseki":
|
|
253
|
-
this._db = new V(this.options);
|
|
254
|
-
break;
|
|
255
|
-
default:
|
|
256
|
-
throw new Error(`Unsupported graph type: ${this.options.graphType}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
options;
|
|
260
|
-
_db;
|
|
261
|
-
ping() {
|
|
262
|
-
return this._db.ping();
|
|
263
|
-
}
|
|
264
|
-
query(t, e) {
|
|
265
|
-
return this._db.query(t, e);
|
|
266
|
-
}
|
|
267
|
-
subset(t, e) {
|
|
268
|
-
return this.options.graphType === "qlever" ? e && e !== "text/turtle" ? Promise.reject(
|
|
269
|
-
new Error(
|
|
270
|
-
"QLever only supports 'text/turtle' for CONSTRUCT/DESCRIBE queries."
|
|
271
|
-
)
|
|
272
|
-
) : this._db.subset(
|
|
273
|
-
t,
|
|
274
|
-
e
|
|
275
|
-
) : this._db.subset(t, e);
|
|
276
|
-
}
|
|
277
|
-
update(t) {
|
|
278
|
-
return this._db.update(t);
|
|
279
|
-
}
|
|
280
|
-
/** Returns true if this backend supports the /data bulk-insert endpoint (QLever only). */
|
|
281
|
-
supportsDataEndpoint() {
|
|
282
|
-
return this.options.graphType === "qlever";
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Insert quads using the backend's preferred bulk-insert mechanism.
|
|
286
|
-
* For QLever: uses the /data endpoint (correctly registers named graphs).
|
|
287
|
-
* For Fuseki: falls back to SPARQL INSERT DATA.
|
|
288
|
-
*/
|
|
289
|
-
insertData(t) {
|
|
290
|
-
return this.options.graphType === "qlever" ? this._db.insertData(t) : Promise.reject(new Error("insertData not supported for Fuseki — use update() with SPARQL INSERT DATA"));
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Delete quads using the backend's preferred bulk-delete mechanism.
|
|
294
|
-
* For QLever: uses the /data/delete endpoint.
|
|
295
|
-
* For Fuseki: falls back to SPARQL DELETE DATA.
|
|
296
|
-
*/
|
|
297
|
-
deleteData(t) {
|
|
298
|
-
return this.options.graphType === "qlever" ? this._db.deleteData(t) : Promise.reject(new Error("deleteData not supported for Fuseki — use update() with SPARQL DELETE DATA"));
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
class Ee {
|
|
302
|
-
constructor(t) {
|
|
303
|
-
this.options = t;
|
|
304
|
-
}
|
|
305
|
-
options;
|
|
306
|
-
/** Paths known to be absent, keyed as `bucket:path`. Avoids repeat 404 requests. */
|
|
307
|
-
_knownMissing = /* @__PURE__ */ new Set();
|
|
308
|
-
// ─── Storage bucket resolution ────────────────────────────────────────────
|
|
309
|
-
_bucket(t) {
|
|
310
|
-
switch (t) {
|
|
311
|
-
case "raw":
|
|
312
|
-
return this.options.storageRaw;
|
|
313
|
-
case "processed":
|
|
314
|
-
return this.options.storageProcessed;
|
|
315
|
-
case "logs":
|
|
316
|
-
return this.options.storageLogs;
|
|
317
|
-
case "chatSessions":
|
|
318
|
-
return this.options.storageChatSessions;
|
|
319
|
-
case "public":
|
|
320
|
-
return this.options.storagePublic;
|
|
321
|
-
case "persistence":
|
|
322
|
-
return this.options.storagePersistence;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
// ─── Downloads ────────────────────────────────────────────────────────────
|
|
326
|
-
/** Get an authenticated download URL. Returns undefined if the file does not exist. */
|
|
327
|
-
async getDownloadURL(t, e) {
|
|
328
|
-
const s = w(this._bucket(t), e);
|
|
329
|
-
try {
|
|
330
|
-
return await k(s);
|
|
331
|
-
} catch (i) {
|
|
332
|
-
if (i?.code === "storage/object-not-found") return;
|
|
333
|
-
throw i;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Download URL with a `?t=<updated>` suffix, bypassing caches.
|
|
338
|
-
* Returns undefined if the file does not exist.
|
|
339
|
-
*/
|
|
340
|
-
async getCacheBustedUrl(t, e) {
|
|
341
|
-
const s = `${t}:${e}`;
|
|
342
|
-
if (this._knownMissing.has(s)) return;
|
|
343
|
-
const i = w(this._bucket(t), e);
|
|
344
|
-
try {
|
|
345
|
-
const n = await M(i);
|
|
346
|
-
return `${await k(i)}&t=${encodeURIComponent(n.updated)}`;
|
|
347
|
-
} catch (n) {
|
|
348
|
-
if (n?.code === "storage/object-not-found" || n?.status === 404) {
|
|
349
|
-
this._knownMissing.add(s), console.debug(`[CueBlobStorage] ${e} not found (404 OK — optional cache file)`);
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
throw n;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
/** Download a file as a Blob. Returns undefined if the file does not exist. */
|
|
356
|
-
async getFile(t, e) {
|
|
357
|
-
const s = w(this._bucket(t), e);
|
|
358
|
-
try {
|
|
359
|
-
return await xt(s);
|
|
360
|
-
} catch (i) {
|
|
361
|
-
if (i?.code === "storage/object-not-found") return;
|
|
362
|
-
throw i;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
/** Returns true if the file exists. */
|
|
366
|
-
async fileExists(t, e) {
|
|
367
|
-
return await this.getDownloadURL(t, e) !== void 0;
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Download a file from the public bucket and return its contents as a string.
|
|
371
|
-
* Uses a cache-busted URL to ensure the latest version is fetched.
|
|
372
|
-
*/
|
|
373
|
-
async downloadPublic(t) {
|
|
374
|
-
const e = w(this.options.storagePublic, t), s = new AbortController(), i = setTimeout(() => s.abort(), 1e4), n = (a) => Promise.race([
|
|
375
|
-
a,
|
|
376
|
-
new Promise((c, r) => {
|
|
377
|
-
s.signal.addEventListener(
|
|
378
|
-
"abort",
|
|
379
|
-
() => r(new DOMException(`Download timed out: ${t}`, "AbortError"))
|
|
380
|
-
);
|
|
381
|
-
})
|
|
382
|
-
]);
|
|
383
|
-
try {
|
|
384
|
-
const [a, c] = await Promise.all([
|
|
385
|
-
n(k(e)),
|
|
386
|
-
n(M(e))
|
|
387
|
-
]), r = `${a}&t=${encodeURIComponent(c.updated)}`, u = await fetch(r, { signal: s.signal });
|
|
388
|
-
if (!u.ok) throw new Error(`HTTP ${u.status}`);
|
|
389
|
-
return u.text();
|
|
390
|
-
} catch (a) {
|
|
391
|
-
const c = a instanceof Error && a.name === "AbortError";
|
|
392
|
-
throw new Error(c ? `Download timed out: ${t}` : a instanceof Error ? a.message : String(a));
|
|
393
|
-
} finally {
|
|
394
|
-
clearTimeout(i);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
// ─── Metadata ─────────────────────────────────────────────────────────────
|
|
398
|
-
/** Read file metadata. Returns undefined if the file does not exist. */
|
|
399
|
-
async getMetadata(t, e) {
|
|
400
|
-
const s = w(this._bucket(t), e);
|
|
401
|
-
try {
|
|
402
|
-
const i = await M(s);
|
|
403
|
-
return {
|
|
404
|
-
updated: i.updated,
|
|
405
|
-
contentType: i.contentType,
|
|
406
|
-
size: i.size,
|
|
407
|
-
customMetadata: i.customMetadata
|
|
408
|
-
};
|
|
409
|
-
} catch (i) {
|
|
410
|
-
if (i?.code === "storage/object-not-found") return;
|
|
411
|
-
throw i;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
/** Update custom metadata on an existing file. */
|
|
415
|
-
async setMetadata(t, e, s) {
|
|
416
|
-
const i = w(this._bucket(t), e);
|
|
417
|
-
await qt(i, { customMetadata: s });
|
|
418
|
-
}
|
|
419
|
-
// ─── Uploads ──────────────────────────────────────────────────────────────
|
|
420
|
-
/**
|
|
421
|
-
* Resumable upload. Returns an {@link UploadHandle} with progress callbacks
|
|
422
|
-
* and pause/resume/cancel controls. Use for large files or files where
|
|
423
|
-
* upload progress needs to be surfaced in the UI.
|
|
424
|
-
*/
|
|
425
|
-
uploadResumable(t, e, s, i) {
|
|
426
|
-
const n = w(this._bucket(t), e), a = et(n, s, i ? { customMetadata: i } : void 0);
|
|
427
|
-
return {
|
|
428
|
-
complete: () => new Promise((c, r) => a.then(() => c(), r)),
|
|
429
|
-
pause: () => a.pause(),
|
|
430
|
-
resume: () => a.resume(),
|
|
431
|
-
cancel: () => a.cancel(),
|
|
432
|
-
onProgress: (c) => {
|
|
433
|
-
const r = a.on("state_changed", (u) => {
|
|
434
|
-
c(u.bytesTransferred, u.totalBytes);
|
|
435
|
-
});
|
|
436
|
-
return () => r();
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Simple one-shot upload. Waits for completion before resolving.
|
|
442
|
-
* Use for small files where progress feedback is not needed.
|
|
443
|
-
*/
|
|
444
|
-
async uploadBytes(t, e, s, i) {
|
|
445
|
-
const n = w(this._bucket(t), e);
|
|
446
|
-
await z(n, s, i ? { customMetadata: i } : void 0);
|
|
447
|
-
}
|
|
448
|
-
/** Upload a string or base64-encoded value. */
|
|
449
|
-
async uploadString(t, e, s, i = Nt.RAW, n) {
|
|
450
|
-
const a = w(this._bucket(t), e);
|
|
451
|
-
await kt(a, s, i, n ? { customMetadata: n } : void 0);
|
|
452
|
-
}
|
|
453
|
-
// ─── Listing ──────────────────────────────────────────────────────────────
|
|
454
|
-
/** List all item names directly under `prefix` in the given bucket. */
|
|
455
|
-
async listFiles(t, e) {
|
|
456
|
-
const s = w(this._bucket(t), e);
|
|
457
|
-
return (await $(s)).items.map((n) => n.name);
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Recursively list all file full-paths under `prefix` in the given bucket.
|
|
461
|
-
* Returns full storage paths (not just names).
|
|
462
|
-
*/
|
|
463
|
-
async listFilesRecursive(t, e) {
|
|
464
|
-
const s = this._bucket(t), i = async (n) => {
|
|
465
|
-
const a = await $(w(s, n));
|
|
466
|
-
let c = a.items.map((r) => r.fullPath);
|
|
467
|
-
for (const r of a.prefixes)
|
|
468
|
-
c = c.concat(await i(r.fullPath));
|
|
469
|
-
return c;
|
|
470
|
-
};
|
|
471
|
-
return i(e);
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Delete all files under `prefix` recursively.
|
|
475
|
-
* Returns the number of files deleted.
|
|
476
|
-
*/
|
|
477
|
-
async deleteDirectory(t, e) {
|
|
478
|
-
const { deleteObject: s } = await import("firebase/storage"), i = this._bucket(t);
|
|
479
|
-
let n = 0;
|
|
480
|
-
const a = async (c) => {
|
|
481
|
-
const r = await $(w(i, c));
|
|
482
|
-
for (const u of r.items)
|
|
483
|
-
await s(u), n++;
|
|
484
|
-
for (const u of r.prefixes)
|
|
485
|
-
await a(u.fullPath);
|
|
486
|
-
};
|
|
487
|
-
return await a(e), n;
|
|
488
|
-
}
|
|
489
|
-
// ─── Legacy helpers kept for CueBlobStorage consumers ────────────────────
|
|
490
|
-
// (used by loaders / processors that were already depending on CueBlobStorage)
|
|
491
|
-
/** Upload binary data to the raw bucket with retry logic. */
|
|
492
|
-
async uploadRaw(t, e, s, i = 3, n, a) {
|
|
493
|
-
const c = w(this.options.storageRaw, t);
|
|
494
|
-
let r = 0, u;
|
|
495
|
-
for (; r < i; )
|
|
496
|
-
try {
|
|
497
|
-
await new Promise((p, m) => {
|
|
498
|
-
if (n?.aborted) {
|
|
499
|
-
m(new DOMException("Upload cancelled", "AbortError"));
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
const h = et(c, e, { customMetadata: s }), g = () => {
|
|
503
|
-
h.cancel(), m(new DOMException("Upload cancelled", "AbortError"));
|
|
504
|
-
};
|
|
505
|
-
n?.addEventListener("abort", g, { once: !0 }), h.on(
|
|
506
|
-
"state_changed",
|
|
507
|
-
(f) => {
|
|
508
|
-
if (a) {
|
|
509
|
-
const y = Math.round(f.bytesTransferred / f.totalBytes * 100);
|
|
510
|
-
a(y);
|
|
511
|
-
}
|
|
512
|
-
},
|
|
513
|
-
(f) => {
|
|
514
|
-
n?.removeEventListener("abort", g), m(f);
|
|
515
|
-
},
|
|
516
|
-
() => {
|
|
517
|
-
n?.removeEventListener("abort", g), p();
|
|
518
|
-
}
|
|
519
|
-
);
|
|
520
|
-
});
|
|
521
|
-
return;
|
|
522
|
-
} catch (p) {
|
|
523
|
-
if (n?.aborted || p instanceof DOMException && p.name === "AbortError")
|
|
524
|
-
throw p;
|
|
525
|
-
u = p, r++, r < i && await new Promise((m) => setTimeout(m, 1e3 * r));
|
|
526
|
-
}
|
|
527
|
-
throw u;
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Upload data to the processed bucket.
|
|
531
|
-
* Skips upload and returns `false` if the blob already exists.
|
|
532
|
-
*/
|
|
533
|
-
async uploadProcessed(t, e, s) {
|
|
534
|
-
const i = w(this.options.storageProcessed, t);
|
|
535
|
-
return await M(i).catch(() => null) ? !1 : (await z(i, e, { customMetadata: s }), !0);
|
|
536
|
-
}
|
|
537
|
-
/** List all blob names directly under `prefix` in the raw bucket. */
|
|
538
|
-
async listRaw(t) {
|
|
539
|
-
const e = w(this.options.storageRaw, t);
|
|
540
|
-
return (await $(e)).items.map((i) => i.name);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
const at = {
|
|
544
|
-
ä: "ae",
|
|
545
|
-
ä: "ae",
|
|
546
|
-
Ä: "AE",
|
|
547
|
-
ö: "oe",
|
|
548
|
-
Ö: "OE",
|
|
549
|
-
ü: "ue",
|
|
550
|
-
ü: "ue",
|
|
551
|
-
Ü: "UE",
|
|
552
|
-
Ü: "UE",
|
|
553
|
-
ß: "ss",
|
|
554
|
-
æ: "ae",
|
|
555
|
-
Æ: "AE",
|
|
556
|
-
ø: "oe",
|
|
557
|
-
Ø: "OE",
|
|
558
|
-
å: "aa",
|
|
559
|
-
Å: "AA",
|
|
560
|
-
á: "a",
|
|
561
|
-
Á: "A",
|
|
562
|
-
ð: "d",
|
|
563
|
-
Ð: "D",
|
|
564
|
-
é: "e",
|
|
565
|
-
É: "E",
|
|
566
|
-
í: "i",
|
|
567
|
-
Í: "I",
|
|
568
|
-
ó: "o",
|
|
569
|
-
Ó: "O",
|
|
570
|
-
ú: "u",
|
|
571
|
-
Ú: "U",
|
|
572
|
-
ý: "y",
|
|
573
|
-
Ý: "Y",
|
|
574
|
-
þ: "th",
|
|
575
|
-
Þ: "Th"
|
|
576
|
-
};
|
|
577
|
-
function b(l, t = !1) {
|
|
578
|
-
const e = "daca0510-72b5-48ba-9091-b918ca18136b";
|
|
579
|
-
return l = we(l, t), ye(l, e);
|
|
580
|
-
}
|
|
581
|
-
function we(l, t = !1) {
|
|
582
|
-
let e = l;
|
|
583
|
-
for (const s in at)
|
|
584
|
-
e = e.replace(new RegExp(s, "g"), at[s]);
|
|
585
|
-
return t && e !== l && console.info(`${l} -> ${e}`), e;
|
|
586
|
-
}
|
|
587
|
-
function Ie(l, t = "") {
|
|
588
|
-
return b(`${t}${l}`);
|
|
589
|
-
}
|
|
590
|
-
class I {
|
|
591
|
-
_value;
|
|
592
|
-
_listeners = /* @__PURE__ */ new Set();
|
|
593
|
-
constructor(t) {
|
|
594
|
-
this._value = t;
|
|
595
|
-
}
|
|
596
|
-
get() {
|
|
597
|
-
return this._value;
|
|
598
|
-
}
|
|
599
|
-
set(t) {
|
|
600
|
-
this._value = t;
|
|
601
|
-
for (const e of this._listeners) e();
|
|
602
|
-
}
|
|
603
|
-
subscribe(t) {
|
|
604
|
-
return this._listeners.add(t), () => this._listeners.delete(t);
|
|
605
|
-
}
|
|
606
|
-
/** Returns a read-only view of this signal. */
|
|
607
|
-
asReadonly() {
|
|
608
|
-
return this;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
function X(l, t) {
|
|
612
|
-
const e = /* @__PURE__ */ new Set();
|
|
613
|
-
let s = t(), i = !1;
|
|
614
|
-
const n = [], a = () => {
|
|
615
|
-
i = !0;
|
|
616
|
-
for (const c of e) c();
|
|
617
|
-
};
|
|
618
|
-
for (const c of l)
|
|
619
|
-
n.push(c.subscribe(a));
|
|
620
|
-
return {
|
|
621
|
-
get() {
|
|
622
|
-
return i && (s = t(), i = !1), s;
|
|
623
|
-
},
|
|
624
|
-
subscribe(c) {
|
|
625
|
-
return e.add(c), () => e.delete(c);
|
|
626
|
-
},
|
|
627
|
-
destroy() {
|
|
628
|
-
for (const c of n) c();
|
|
629
|
-
e.clear();
|
|
630
|
-
}
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
async function Y(l, t, e, s) {
|
|
634
|
-
const i = b(l);
|
|
635
|
-
let n;
|
|
636
|
-
if (s) {
|
|
637
|
-
const c = await s.get(i);
|
|
638
|
-
c !== void 0 && (e(c, !0), n = b(JSON.stringify(c)));
|
|
639
|
-
}
|
|
640
|
-
const a = await t();
|
|
641
|
-
return e(a, !1), s && b(JSON.stringify(a)) !== n && s.set(i, a).catch(
|
|
642
|
-
(r) => console.error("[staleWhileRevalidate] Cache write failed:", r)
|
|
643
|
-
), a;
|
|
644
|
-
}
|
|
645
|
-
const H = {
|
|
646
|
-
apiKey: "AIzaSyAiW42QBx9HS4Khu88pCW7MV66IhBAQul0",
|
|
647
|
-
appId: "1:151132927589:web:d2ffdb377dfadfd23ab88c",
|
|
648
|
-
measurementId: "G-YT4PK6HGZD"
|
|
649
|
-
}, ot = "qaecy-mvp-406413", Ce = "734737865998", Dt = "europe-west6", rt = "projects", Te = "spaces_chats_eu_west6", Ae = "spaces_raw_eu_west6", Se = "spaces_processed_eu_west6", Re = "spaces_logs_eu_west6", Pe = "cue_public_eu_west6", ct = "db_persistence_eu_west6", xe = "/data-views/admin/consumption", ve = "/data-views/admin/profile/organizations", lt = "/data-views/admin/profile/api-keys", ut = "/commands/admin/profile/api-keys", be = "/commands/admin/profile/terms", De = (l) => `/data-views/admin/organizations/${l}/members`, Ue = "/commands/admin/project", Oe = "/assistant/search", Ut = "/triplestore/query", Me = "/triplestore/update", Ot = "/qlever-server/qlever/query", $e = "/qlever-server/qlever/update", pt = "/commands/file-system-structure/batch", ht = "microsoft.com", dt = "superadmin", J = "https://cue.qaecy.com/r/";
|
|
650
|
-
class ft {
|
|
651
|
-
_auth;
|
|
652
|
-
_endpoints;
|
|
653
|
-
_userSignal = new I(null);
|
|
654
|
-
_tokenSignal = new I(null);
|
|
655
|
-
_isSuperAdminSignal = new I(!1);
|
|
656
|
-
_userIdsSignal;
|
|
657
|
-
_stopTokenListener;
|
|
658
|
-
/** Reactive auth state — emits the signed-in `User`, or `null` when signed out. */
|
|
659
|
-
user;
|
|
660
|
-
/** Reactive Firebase ID token — refreshes automatically; `null` when signed out. */
|
|
661
|
-
token;
|
|
662
|
-
/** `true` when the current user has the `superadmin` custom claim. */
|
|
663
|
-
isSuperAdmin;
|
|
664
|
-
/** All unique UIDs for the current user (Firebase UID + linked provider UIDs). */
|
|
665
|
-
userIds;
|
|
666
|
-
constructor(t, e = !1, s) {
|
|
667
|
-
this._auth = Zt(t), this._endpoints = s, e && te(this._auth, s.authEmulatorUrl, {
|
|
668
|
-
disableWarnings: !0
|
|
669
|
-
}), this.user = this._userSignal.asReadonly(), this.token = this._tokenSignal.asReadonly(), this.isSuperAdmin = this._isSuperAdminSignal.asReadonly(), this._userIdsSignal = X([this._userSignal], () => {
|
|
670
|
-
const i = this._userSignal.get();
|
|
671
|
-
if (!i) return [];
|
|
672
|
-
const n = /* @__PURE__ */ new Set([i.uid]);
|
|
673
|
-
return i.providerData.forEach((a) => n.add(a.uid)), Array.from(n);
|
|
674
|
-
}), this.userIds = this._userIdsSignal, this._stopTokenListener = ee(this._auth, async (i) => {
|
|
675
|
-
if (this._userSignal.set(i), i) {
|
|
676
|
-
const n = await i.getIdToken();
|
|
677
|
-
this._tokenSignal.set(n);
|
|
678
|
-
const a = await G(i);
|
|
679
|
-
this._isSuperAdminSignal.set(a.claims.role === dt);
|
|
680
|
-
} else
|
|
681
|
-
this._tokenSignal.set(null), this._isSuperAdminSignal.set(!1);
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
/** Stop all internal Firebase listeners. Call when the `Cue` instance is no longer needed. */
|
|
685
|
-
destroy() {
|
|
686
|
-
this._stopTokenListener(), this._userIdsSignal.destroy?.();
|
|
687
|
-
}
|
|
688
|
-
async signIn(t, e) {
|
|
689
|
-
if (t === "password") {
|
|
690
|
-
if (!e)
|
|
691
|
-
throw new Error("credentials are required for password sign-in");
|
|
692
|
-
return (await se(
|
|
693
|
-
this._auth,
|
|
694
|
-
e.email,
|
|
695
|
-
e.password
|
|
696
|
-
)).user;
|
|
697
|
-
}
|
|
698
|
-
const s = t === "google" ? new W() : new K(ht);
|
|
699
|
-
return (await ie(this._auth, s)).user;
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Initiate a redirect-based sign-in (mobile / iframe contexts where popups
|
|
703
|
-
* are blocked). Call `checkRedirectResult()` on the next page load to
|
|
704
|
-
* retrieve the result.
|
|
705
|
-
*/
|
|
706
|
-
async signInWithRedirect(t) {
|
|
707
|
-
const e = t === "google" ? new W() : new K(ht);
|
|
708
|
-
await ne(this._auth, e);
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Retrieve the result of a redirect sign-in. Returns the signed-in `User`
|
|
712
|
-
* or `null` if there is no pending redirect result.
|
|
713
|
-
* Call this once on app startup before showing a sign-in UI.
|
|
714
|
-
*/
|
|
715
|
-
async checkRedirectResult() {
|
|
716
|
-
return (await ae(this._auth))?.user ?? null;
|
|
717
|
-
}
|
|
718
|
-
/**
|
|
719
|
-
* One-shot async check — returns `true` if the current user has the
|
|
720
|
-
* `superadmin` custom claim. For reactive use, read `cue.auth.isSuperAdmin`
|
|
721
|
-
* (the signal) instead.
|
|
722
|
-
*/
|
|
723
|
-
async checkSuperAdmin() {
|
|
724
|
-
const t = this._auth.currentUser;
|
|
725
|
-
return t ? (await G(t)).claims.role === dt : !1;
|
|
726
|
-
}
|
|
727
|
-
/** Sign in with a Cue API key. `projectId` is optional — omit it when no project context is available (e.g. admin flows). */
|
|
728
|
-
async signInWithApiKey(t, e) {
|
|
729
|
-
const s = { "cue-api-key": t };
|
|
730
|
-
e && (s["x-project-id"] = e);
|
|
731
|
-
const i = await fetch(this._endpoints.tokenUrl, {
|
|
732
|
-
method: "GET",
|
|
733
|
-
headers: s
|
|
734
|
-
});
|
|
735
|
-
if (!i.ok)
|
|
736
|
-
throw new Error(`Failed to fetch custom token: ${i.statusText}`);
|
|
737
|
-
const { token: n } = await i.json();
|
|
738
|
-
return (await oe(this._auth, n)).user;
|
|
739
|
-
}
|
|
740
|
-
/** Sign out the current user */
|
|
741
|
-
async signOut() {
|
|
742
|
-
await re(this._auth);
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Register a new user by name and email.
|
|
746
|
-
* The backend validates that the email domain belongs to an existing organisation,
|
|
747
|
-
* creates the Firebase Auth account, assigns org membership, and dispatches a
|
|
748
|
-
* "set your password" email to the address provided.
|
|
749
|
-
* Returns the new user's UID and the organisation name on success.
|
|
750
|
-
*/
|
|
751
|
-
async signUp(t, e) {
|
|
752
|
-
const s = await fetch(
|
|
753
|
-
`${this._endpoints.gatewayUrl}/commands/admin/user/signup`,
|
|
754
|
-
{
|
|
755
|
-
method: "POST",
|
|
756
|
-
headers: { "Content-Type": "application/json" },
|
|
757
|
-
body: JSON.stringify({ name: t, email: e })
|
|
758
|
-
}
|
|
759
|
-
);
|
|
760
|
-
if (!s.ok) {
|
|
761
|
-
const i = await s.json().catch(() => ({}));
|
|
762
|
-
throw new Error(i?.message ?? `Sign-up failed (${s.status})`);
|
|
763
|
-
}
|
|
764
|
-
return s.json();
|
|
765
|
-
}
|
|
766
|
-
/** Currently signed-in user, or null if not authenticated */
|
|
767
|
-
get currentUser() {
|
|
768
|
-
return this._auth.currentUser;
|
|
769
|
-
}
|
|
770
|
-
/** Subscribe to authentication state changes. Returns an unsubscribe function. */
|
|
771
|
-
onAuthStateChanged(t) {
|
|
772
|
-
return ce(this._auth, t);
|
|
773
|
-
}
|
|
774
|
-
/** Get the Firebase ID token for the current user, or null if not authenticated */
|
|
775
|
-
async getToken(t = !1) {
|
|
776
|
-
const e = this._auth.currentUser;
|
|
777
|
-
return e ? e.getIdToken(t) : null;
|
|
778
|
-
}
|
|
779
|
-
/**
|
|
780
|
-
* Executes a fetch with a Bearer token. On a 401 response the token is
|
|
781
|
-
* force-refreshed and the request is retried once before throwing.
|
|
782
|
-
*/
|
|
783
|
-
async authenticatedFetch(t, e = {}) {
|
|
784
|
-
const s = async (n) => {
|
|
785
|
-
const a = await this.getToken(n);
|
|
786
|
-
if (!a) throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
787
|
-
const { headers: c, ...r } = e;
|
|
788
|
-
return fetch(t, {
|
|
789
|
-
...r,
|
|
790
|
-
headers: {
|
|
791
|
-
...c,
|
|
792
|
-
Authorization: `Bearer ${a}`
|
|
793
|
-
}
|
|
794
|
-
});
|
|
795
|
-
};
|
|
796
|
-
let i = await s(!1);
|
|
797
|
-
return i.status === 401 && (i = await s(!0)), i;
|
|
798
|
-
}
|
|
799
|
-
/** Raw Firebase Auth instance, for advanced use cases */
|
|
800
|
-
get firebaseAuth() {
|
|
801
|
-
return this._auth;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
class mt {
|
|
805
|
-
constructor(t, e, s, i) {
|
|
806
|
-
this._auth = t, this._gatewayUrl = e, this.projects = s, this.sync = i;
|
|
807
|
-
}
|
|
808
|
-
_auth;
|
|
809
|
-
_gatewayUrl;
|
|
810
|
-
projects;
|
|
811
|
-
sync;
|
|
812
|
-
/**
|
|
813
|
-
* Returns standard authentication headers for the current user.
|
|
814
|
-
* Useful when calling Cue-backed services directly (e.g. the GIS proxy).
|
|
815
|
-
*/
|
|
816
|
-
async getAuthHeaders() {
|
|
817
|
-
const t = await this._auth.getToken();
|
|
818
|
-
if (!t)
|
|
819
|
-
throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
820
|
-
return {
|
|
821
|
-
Authorization: `Bearer ${t}`,
|
|
822
|
-
"Content-Type": "application/json"
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* Search project documents using natural language.
|
|
827
|
-
* The user must be authenticated before calling this.
|
|
828
|
-
*/
|
|
829
|
-
async search(t) {
|
|
830
|
-
const e = await this._auth.authenticatedFetch(
|
|
831
|
-
`${this._gatewayUrl}${Oe}`,
|
|
832
|
-
{
|
|
833
|
-
method: "POST",
|
|
834
|
-
headers: { "Content-Type": "application/json", "cue-project-id": t.projectId },
|
|
835
|
-
body: JSON.stringify({
|
|
836
|
-
term: t.term,
|
|
837
|
-
projectId: t.projectId,
|
|
838
|
-
categories: t.categories ?? []
|
|
839
|
-
})
|
|
840
|
-
}
|
|
841
|
-
);
|
|
842
|
-
if (!e.ok)
|
|
843
|
-
throw new Error(
|
|
844
|
-
`Search request failed: ${e.status} ${e.statusText}`
|
|
845
|
-
);
|
|
846
|
-
return e.json();
|
|
847
|
-
}
|
|
848
|
-
/**
|
|
849
|
-
* Execute a SPARQL query against the project's triplestore.
|
|
850
|
-
* The user must be authenticated before calling this.
|
|
851
|
-
*/
|
|
852
|
-
async sparql(t, e, s) {
|
|
853
|
-
s || (s = (await this.projects.getProject(e))?.projectSettings?.graph?.type ?? "qlever");
|
|
854
|
-
const i = s === "fuseki" ? Ut : Ot;
|
|
855
|
-
console.log(`Executing SPARQL query against ${i} for project ${e} with graph type ${s}`);
|
|
856
|
-
const n = new URLSearchParams();
|
|
857
|
-
n.append("query", t);
|
|
858
|
-
const a = await this._auth.authenticatedFetch(
|
|
859
|
-
`${this._gatewayUrl}${i}`,
|
|
860
|
-
{
|
|
861
|
-
method: "POST",
|
|
862
|
-
headers: {
|
|
863
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
864
|
-
Accept: "application/sparql-results+json",
|
|
865
|
-
"x-project-id": e,
|
|
866
|
-
"cue-project-id": e
|
|
867
|
-
},
|
|
868
|
-
body: n
|
|
869
|
-
}
|
|
870
|
-
);
|
|
871
|
-
if (!a.ok)
|
|
872
|
-
throw new Error(
|
|
873
|
-
`SPARQL query failed: ${a.status} ${a.statusText}`
|
|
874
|
-
);
|
|
875
|
-
return a.json();
|
|
876
|
-
}
|
|
877
|
-
async getConsumption(t) {
|
|
878
|
-
const e = await this._auth.authenticatedFetch(
|
|
879
|
-
`${this._gatewayUrl}${xe}`,
|
|
880
|
-
{
|
|
881
|
-
headers: {
|
|
882
|
-
"Content-Type": "application/json",
|
|
883
|
-
"x-project-id": t,
|
|
884
|
-
"cue-project-id": t
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
);
|
|
888
|
-
if (!e.ok)
|
|
889
|
-
throw new Error(
|
|
890
|
-
`Failed to fetch consumption: ${e.status} ${e.statusText}`
|
|
891
|
-
);
|
|
892
|
-
return e.json();
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
class yt {
|
|
896
|
-
constructor(t, e, s = !1, i) {
|
|
897
|
-
if (this._auth = t, this._db = Ht(e), this._functions = vt(e, Dt), this._gatewayUrl = i?.gatewayUrl ?? "", s) {
|
|
898
|
-
const n = i?.firestoreEmulatorHost ?? "localhost", a = i?.firestoreEmulatorPort ?? 8080;
|
|
899
|
-
zt(this._db, n, a), bt(this._functions, "localhost", 5001);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
_auth;
|
|
903
|
-
_db;
|
|
904
|
-
_functions;
|
|
905
|
-
_gatewayUrl;
|
|
906
|
-
_requireUser() {
|
|
907
|
-
const t = this._auth.currentUser;
|
|
908
|
-
if (!t) throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
909
|
-
return t.uid;
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* Create a new project. The authenticated user is automatically set as admin, syncer, and member.
|
|
913
|
-
* Throws if a project with the given ID already exists.
|
|
914
|
-
*/
|
|
915
|
-
async createProject(t) {
|
|
916
|
-
const e = await this._auth.authenticatedFetch(`${this._gatewayUrl}${Ue}`, {
|
|
917
|
-
method: "POST",
|
|
918
|
-
headers: { "Content-Type": "application/json" },
|
|
919
|
-
body: JSON.stringify(t)
|
|
920
|
-
});
|
|
921
|
-
if (!e.ok) {
|
|
922
|
-
const s = await e.text().catch(() => "");
|
|
923
|
-
throw new Error(`Failed to create project: ${e.status} ${e.statusText}${s ? ` — ${s}` : ""}`);
|
|
924
|
-
}
|
|
925
|
-
return e.json();
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* List all projects where the authenticated user appears in the members array.
|
|
929
|
-
* Access is gated by Firestore rules which check membership.
|
|
930
|
-
*/
|
|
931
|
-
async listProjects() {
|
|
932
|
-
const t = this._requireUser(), e = st(this._db, rt);
|
|
933
|
-
return (await Gt(Wt(e, Kt("members", "array-contains", t), Qt(100)))).docs.map((i) => i.data());
|
|
934
|
-
}
|
|
935
|
-
/** Fetch a single project by ID. Returns null if not found. */
|
|
936
|
-
async getProject(t) {
|
|
937
|
-
const e = it(st(this._db, rt), t), s = await Vt(e);
|
|
938
|
-
return s.exists() ? s.data() : null;
|
|
939
|
-
}
|
|
940
|
-
/**
|
|
941
|
-
* Atomically increments `unitsConsumed` on the top-level `clientSync/{projectId}`
|
|
942
|
-
* document, creating it if it doesn't exist. Intended for pre-flight checks.
|
|
943
|
-
*/
|
|
944
|
-
async incrementUnitsConsumed(t, e, s) {
|
|
945
|
-
if (e <= 0) return;
|
|
946
|
-
const i = it(this._db, "clientSync", t);
|
|
947
|
-
await Xt(i, {
|
|
948
|
-
unitsConsumed: Jt(e),
|
|
949
|
-
lastUpdated: Yt(),
|
|
950
|
-
lastUserId: s
|
|
951
|
-
}, { merge: !0 });
|
|
952
|
-
}
|
|
953
|
-
/**
|
|
954
|
-
* Invite a user to a project by email. Returns the invited user's uid and display name.
|
|
955
|
-
*/
|
|
956
|
-
async inviteUserToProject(t, e, s) {
|
|
957
|
-
return (await j(this._functions, "inviteUserToProject")({ email: t, spaceId: e, role: s })).data;
|
|
958
|
-
}
|
|
959
|
-
/** Change an existing member's role on a project. */
|
|
960
|
-
async changeUserRoleOnProject(t, e, s) {
|
|
961
|
-
await j(this._functions, "changeUserRoleOnProject")({ uid: t, spaceId: e, role: s });
|
|
962
|
-
}
|
|
963
|
-
/** Remove a member from a project. */
|
|
964
|
-
async removeUserFromProject(t, e) {
|
|
965
|
-
await j(this._functions, "removeUserFromProject")({ uid: t, spaceId: e });
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
class gt {
|
|
969
|
-
constructor(t, e, s, i) {
|
|
970
|
-
this._auth = t, this._gatewayUrl = i, this._functions = vt(e, Dt), s && bt(this._functions, "localhost", 5001);
|
|
971
|
-
}
|
|
972
|
-
_auth;
|
|
973
|
-
_gatewayUrl;
|
|
974
|
-
_functions;
|
|
975
|
-
_url(t) {
|
|
976
|
-
return `${this._gatewayUrl}${t}`;
|
|
977
|
-
}
|
|
978
|
-
async _fetch(t, e) {
|
|
979
|
-
const s = await this._auth.authenticatedFetch(this._url(t), e);
|
|
980
|
-
if (!s.ok)
|
|
981
|
-
throw new Error(`Profile API error ${s.status}: ${s.statusText}`);
|
|
982
|
-
const i = await s.text();
|
|
983
|
-
return i ? JSON.parse(i) : null;
|
|
984
|
-
}
|
|
985
|
-
/** Whether the current user has an active API key. */
|
|
986
|
-
async hasAPIKey() {
|
|
987
|
-
return await this._fetch(lt) !== null;
|
|
988
|
-
}
|
|
989
|
-
/** Returns the sign-in methods registered for the current user's email. */
|
|
990
|
-
async getSignInMethods() {
|
|
991
|
-
const t = this._auth.currentUser;
|
|
992
|
-
if (!t?.email) throw new Error("User has no e-mail");
|
|
993
|
-
return le(this._auth.firebaseAuth, t.email);
|
|
994
|
-
}
|
|
995
|
-
/** Builds a human-readable label from a Firebase UserInfo provider entry. */
|
|
996
|
-
buildProviderLabel(t) {
|
|
997
|
-
return t.displayName && t.email ? `${t.displayName} (${t.email})` : t.displayName ?? t.email ?? "-";
|
|
998
|
-
}
|
|
999
|
-
/** Returns SSO accounts linked to the current user (excludes password). */
|
|
1000
|
-
getSSOAccounts() {
|
|
1001
|
-
const t = this._auth.currentUser;
|
|
1002
|
-
return t ? t.providerData.filter((e) => e.providerId !== "password").map((e) => ({ id: e.providerId, label: this.buildProviderLabel(e) })) : [];
|
|
1003
|
-
}
|
|
1004
|
-
/** Links a Google or Microsoft provider to the current account via popup. Returns the new provider label. */
|
|
1005
|
-
async linkProvider(t) {
|
|
1006
|
-
const e = this._requireUser(), s = t === "google.com" ? new W() : new K(t), n = (await ue(e, s)).user.providerData.find(
|
|
1007
|
-
(a) => a.providerId === t
|
|
1008
|
-
);
|
|
1009
|
-
return n ? this.buildProviderLabel(n) : "-";
|
|
1010
|
-
}
|
|
1011
|
-
/** Unlinks a provider from the current account. */
|
|
1012
|
-
async unlinkProvider(t) {
|
|
1013
|
-
await pe(this._requireUser(), t);
|
|
1014
|
-
}
|
|
1015
|
-
/** Changes the password. Reauthenticates first. */
|
|
1016
|
-
async updatePassword(t, e) {
|
|
1017
|
-
const s = this._requireUser();
|
|
1018
|
-
if (!s.email) throw new Error("User has no e-mail");
|
|
1019
|
-
await nt(
|
|
1020
|
-
s,
|
|
1021
|
-
B.credential(s.email, t)
|
|
1022
|
-
), await he(s, e);
|
|
1023
|
-
}
|
|
1024
|
-
/** Adds (sets) a password for an account that currently only uses SSO. */
|
|
1025
|
-
async addPassword(t) {
|
|
1026
|
-
const e = this._requireUser();
|
|
1027
|
-
if (!e.email) throw new Error("User has no e-mail");
|
|
1028
|
-
await de(e, B.credential(e.email, t));
|
|
1029
|
-
}
|
|
1030
|
-
/** Requests an e-mail change. Sends a verification e-mail to the new address. */
|
|
1031
|
-
async updateEmail(t, e) {
|
|
1032
|
-
const s = this._requireUser();
|
|
1033
|
-
if (!s.email) throw new Error("User e-mail not available");
|
|
1034
|
-
await nt(
|
|
1035
|
-
s,
|
|
1036
|
-
B.credential(s.email, e)
|
|
1037
|
-
), await fe(s, t), await me(s);
|
|
1038
|
-
}
|
|
1039
|
-
/** Creates a new API key for the current user. */
|
|
1040
|
-
async createAPIKey(t) {
|
|
1041
|
-
return this._fetch(ut, {
|
|
1042
|
-
method: "POST",
|
|
1043
|
-
headers: { "Content-Type": "application/json" },
|
|
1044
|
-
body: JSON.stringify({ expiration: t })
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
/** Revokes the current user's API key. */
|
|
1048
|
-
async revokeAPIKey() {
|
|
1049
|
-
const t = await this._auth.authenticatedFetch(
|
|
1050
|
-
this._url(ut),
|
|
1051
|
-
{ method: "DELETE" }
|
|
1052
|
-
);
|
|
1053
|
-
if (!t.ok)
|
|
1054
|
-
throw new Error(`Revoke API key error ${t.status}: ${t.statusText}`);
|
|
1055
|
-
}
|
|
1056
|
-
/** Fetches the current user's existing API key. */
|
|
1057
|
-
async requestAPIKey() {
|
|
1058
|
-
const t = await this._fetch(lt);
|
|
1059
|
-
if (!t) throw new Error("No API key found");
|
|
1060
|
-
return t;
|
|
1061
|
-
}
|
|
1062
|
-
/** Returns organizations the current user is a member of. */
|
|
1063
|
-
async listOrganizations() {
|
|
1064
|
-
return this._fetch(ve);
|
|
1065
|
-
}
|
|
1066
|
-
/** Returns all members of the given organisation. Caller must be an org admin or superadmin. */
|
|
1067
|
-
async getOrgMembers(t) {
|
|
1068
|
-
return this._fetch(De(t));
|
|
1069
|
-
}
|
|
1070
|
-
_requireUser() {
|
|
1071
|
-
const t = this._auth.currentUser;
|
|
1072
|
-
if (!t) throw new Error("Not authenticated");
|
|
1073
|
-
return t;
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Fetch display name and email for a list of user UIDs.
|
|
1077
|
-
* Uses the `getUserInfo` Firebase callable function.
|
|
1078
|
-
*/
|
|
1079
|
-
async getUserInfo(t) {
|
|
1080
|
-
return (await j(this._functions, "getUserInfo")({ uids: t })).data;
|
|
1081
|
-
}
|
|
1082
|
-
/** Record that the current user has accepted the terms of service. Sets a `terms` custom claim on the token. */
|
|
1083
|
-
async acceptTerms(t) {
|
|
1084
|
-
await this._fetch(be, {
|
|
1085
|
-
method: "POST",
|
|
1086
|
-
headers: { "Content-Type": "application/json" },
|
|
1087
|
-
body: JSON.stringify({ version: t })
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Returns the terms version the current user has accepted (e.g. `"v1"`),
|
|
1092
|
-
* or `null` if they have not accepted any version yet.
|
|
1093
|
-
* Reads from the cached ID token — call after `acceptTerms()` with a
|
|
1094
|
-
* force-refreshed token to see the updated value.
|
|
1095
|
-
*/
|
|
1096
|
-
async latestTermsAccepted() {
|
|
1097
|
-
const t = this._auth.currentUser;
|
|
1098
|
-
return t ? (await G(t)).claims.terms ?? null : null;
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
const _t = ["superadmin", "admin", "syncer", "member"], Et = {
|
|
1102
|
-
createEntities: "admin",
|
|
1103
|
-
createProvider: "superadmin",
|
|
1104
|
-
changeContentCategories: "syncer",
|
|
1105
|
-
deleteDocuments: "superadmin",
|
|
1106
|
-
deleteUserFromProject: "admin",
|
|
1107
|
-
downloadDocuments: "member",
|
|
1108
|
-
editContentCategories: "syncer",
|
|
1109
|
-
editPublicReposAvailableToAgent: "admin",
|
|
1110
|
-
editTier: "admin",
|
|
1111
|
-
inviteUserToProject: "member",
|
|
1112
|
-
renameDocuments: "superadmin",
|
|
1113
|
-
uploadDocuments: "syncer",
|
|
1114
|
-
viewEntities: "member"
|
|
1115
|
-
};
|
|
1116
|
-
function Le() {
|
|
1117
|
-
return {
|
|
1118
|
-
changeContentCategories: !1,
|
|
1119
|
-
createEntities: !1,
|
|
1120
|
-
createProvider: !1,
|
|
1121
|
-
deleteDocuments: !1,
|
|
1122
|
-
deleteUserFromProject: !1,
|
|
1123
|
-
downloadDocuments: !1,
|
|
1124
|
-
editContentCategories: !1,
|
|
1125
|
-
editPublicReposAvailableToAgent: !1,
|
|
1126
|
-
editTier: !1,
|
|
1127
|
-
inviteUserToProject: !1,
|
|
1128
|
-
renameDocuments: !1,
|
|
1129
|
-
uploadDocuments: !1,
|
|
1130
|
-
viewEntities: !1
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
class wt {
|
|
1134
|
-
constructor(t) {
|
|
1135
|
-
this._isSuperAdmin = t, this._projectRoles = new I([]), this.privileges = X(
|
|
1136
|
-
[this._projectRoles, t],
|
|
1137
|
-
() => this._compute()
|
|
1138
|
-
);
|
|
1139
|
-
}
|
|
1140
|
-
_isSuperAdmin;
|
|
1141
|
-
_projectRoles;
|
|
1142
|
-
/**
|
|
1143
|
-
* Reactive signal — current user's privileges for the selected project.
|
|
1144
|
-
* Recomputes automatically when `setProjectRoles()` is called or when
|
|
1145
|
-
* the `isSuperAdmin` signal changes.
|
|
1146
|
-
*/
|
|
1147
|
-
privileges;
|
|
1148
|
-
/**
|
|
1149
|
-
* Set the user's roles for the currently selected project.
|
|
1150
|
-
*
|
|
1151
|
-
* Roles are expanded along the hierarchy: `admin` automatically includes
|
|
1152
|
-
* `syncer` and `member`; `syncer` includes `member`. Pass an empty array
|
|
1153
|
-
* to reset to the lowest privilege level.
|
|
1154
|
-
*/
|
|
1155
|
-
setProjectRoles(t) {
|
|
1156
|
-
const e = this._expand(t), s = this._projectRoles.get();
|
|
1157
|
-
s.length === e.length && s.every((n, a) => n === e[a]) || this._projectRoles.set(e);
|
|
1158
|
-
}
|
|
1159
|
-
_expand(t) {
|
|
1160
|
-
if (this._isSuperAdmin.get())
|
|
1161
|
-
return ["superadmin", "admin", "syncer", "member"];
|
|
1162
|
-
const e = _t.findIndex((s) => t.includes(s));
|
|
1163
|
-
return e === -1 ? [] : Array.from(_t.slice(e));
|
|
1164
|
-
}
|
|
1165
|
-
_compute() {
|
|
1166
|
-
const t = this._projectRoles.get(), e = Le();
|
|
1167
|
-
for (const s of Object.keys(Et))
|
|
1168
|
-
e[s] = t.includes(Et[s]);
|
|
1169
|
-
return e;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
class It {
|
|
1173
|
-
constructor(t) {
|
|
1174
|
-
this._storage = t;
|
|
1175
|
-
}
|
|
1176
|
-
_storage;
|
|
1177
|
-
// ── Query cache ────────────────────────────────────────────────────────────
|
|
1178
|
-
async getQueryCache(t, e) {
|
|
1179
|
-
return this._get(`portal/projects/${t}/queries`, e);
|
|
1180
|
-
}
|
|
1181
|
-
async setQueryCache(t, e, s) {
|
|
1182
|
-
return this._set(`portal/projects/${t}/queries`, e, s);
|
|
1183
|
-
}
|
|
1184
|
-
// ── Agent session cache ────────────────────────────────────────────────────
|
|
1185
|
-
async getAgentSessionCache(t, e) {
|
|
1186
|
-
return this._get(`portal/projects/${t}/agent`, e);
|
|
1187
|
-
}
|
|
1188
|
-
async setAgentSessionCache(t, e, s) {
|
|
1189
|
-
return this._set(`portal/projects/${t}/agent`, e, s);
|
|
1190
|
-
}
|
|
1191
|
-
// ── User-project cache ─────────────────────────────────────────────────────
|
|
1192
|
-
async getUserProjectCache(t, e, s) {
|
|
1193
|
-
return this._get(`portal/projects/${t}/users/${e}`, s);
|
|
1194
|
-
}
|
|
1195
|
-
async setUserProjectCache(t, e, s, i) {
|
|
1196
|
-
return this._set(
|
|
1197
|
-
`portal/projects/${t}/users/${e}`,
|
|
1198
|
-
s,
|
|
1199
|
-
i
|
|
1200
|
-
);
|
|
1201
|
-
}
|
|
1202
|
-
// ── Internal helpers ───────────────────────────────────────────────────────
|
|
1203
|
-
async _get(t, e) {
|
|
1204
|
-
try {
|
|
1205
|
-
const i = await (await xt(w(this._storage, `${t}/${e}.json.gz`))).arrayBuffer(), n = new DecompressionStream("gzip"), a = n.writable.getWriter(), c = n.readable.getReader();
|
|
1206
|
-
a.write(new Uint8Array(i)), a.close();
|
|
1207
|
-
const r = [];
|
|
1208
|
-
let u = await c.read();
|
|
1209
|
-
for (; !u.done; )
|
|
1210
|
-
r.push(u.value), u = await c.read();
|
|
1211
|
-
const p = r.reduce((g, f) => g + f.length, 0), m = new Uint8Array(p);
|
|
1212
|
-
let h = 0;
|
|
1213
|
-
for (const g of r)
|
|
1214
|
-
m.set(g, h), h += g.length;
|
|
1215
|
-
return JSON.parse(new TextDecoder().decode(m));
|
|
1216
|
-
} catch (s) {
|
|
1217
|
-
if (je(s)) return;
|
|
1218
|
-
throw s;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
async _set(t, e, s) {
|
|
1222
|
-
const i = new TextEncoder().encode(JSON.stringify(s)), n = new CompressionStream("gzip"), a = n.writable.getWriter(), c = n.readable.getReader();
|
|
1223
|
-
a.write(new Uint8Array(i)), a.close();
|
|
1224
|
-
const r = [];
|
|
1225
|
-
let u = await c.read();
|
|
1226
|
-
for (; !u.done; )
|
|
1227
|
-
r.push(u.value), u = await c.read();
|
|
1228
|
-
const p = r.reduce((g, f) => g + f.length, 0), m = new Uint8Array(p);
|
|
1229
|
-
let h = 0;
|
|
1230
|
-
for (const g of r)
|
|
1231
|
-
m.set(g, h), h += g.length;
|
|
1232
|
-
await z(
|
|
1233
|
-
w(this._storage, `${t}/${e}.json.gz`),
|
|
1234
|
-
m,
|
|
1235
|
-
{ contentType: "application/gzip" }
|
|
1236
|
-
);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
function je(l) {
|
|
1240
|
-
return typeof l == "object" && l !== null && "code" in l && l.code === "storage/object-not-found";
|
|
1241
|
-
}
|
|
1242
|
-
const S = {
|
|
1243
|
-
qcy: "https://dev.qaecy.com/ont#"
|
|
1244
|
-
}, Q = {
|
|
1245
|
-
rdfs: "http://www.w3.org/2000/01/rdf-schema#",
|
|
1246
|
-
skos: "http://www.w3.org/2004/02/skos/core#",
|
|
1247
|
-
geo: "http://www.opengis.net/ont/geosparql#"
|
|
1248
|
-
};
|
|
1249
|
-
class qe {
|
|
1250
|
-
constructor(t, e, s, i, n) {
|
|
1251
|
-
this._api = t, this._projectId = e, this._queryCache = i, this._graphType = n, this._language = new I(s), this._contentCategories = new I([]), this._entityCategories = new I([]), this._relationships = new I([]), this.availableContentCategories = this._contentCategories.asReadonly(), this.availableEntityCategories = this._entityCategories.asReadonly(), this.availableEntityRelationships = this._relationships.asReadonly(), this._load(s).catch(
|
|
1252
|
-
(a) => console.error("[CueProjectSchema] Initial load failed:", a)
|
|
1253
|
-
);
|
|
1254
|
-
}
|
|
1255
|
-
_api;
|
|
1256
|
-
_projectId;
|
|
1257
|
-
_queryCache;
|
|
1258
|
-
_graphType;
|
|
1259
|
-
_cache = /* @__PURE__ */ new Map();
|
|
1260
|
-
_language;
|
|
1261
|
-
_contentCategories;
|
|
1262
|
-
_entityCategories;
|
|
1263
|
-
_relationships;
|
|
1264
|
-
/** Currently active content categories for the selected language. */
|
|
1265
|
-
availableContentCategories;
|
|
1266
|
-
/** Currently active entity categories for the selected language. */
|
|
1267
|
-
availableEntityCategories;
|
|
1268
|
-
/** Currently active entity relationship types for the selected language. */
|
|
1269
|
-
availableEntityRelationships;
|
|
1270
|
-
/** Returns the currently active language. */
|
|
1271
|
-
get language() {
|
|
1272
|
-
return this._language.get();
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Switch the active language. If the data for this language has already been
|
|
1276
|
-
* fetched it is applied immediately from cache; otherwise a new SPARQL fetch
|
|
1277
|
-
* is triggered.
|
|
1278
|
-
*/
|
|
1279
|
-
setLanguage(t) {
|
|
1280
|
-
this._language.get() !== t && (this._language.set(t), this._load(t).catch(
|
|
1281
|
-
(e) => console.error("[CueProjectSchema] Language switch failed:", e)
|
|
1282
|
-
));
|
|
1283
|
-
}
|
|
1284
|
-
/**
|
|
1285
|
-
* Force a re-fetch for the current language, bypassing the cache.
|
|
1286
|
-
* Useful when the triplestore data has changed.
|
|
1287
|
-
*/
|
|
1288
|
-
async refresh() {
|
|
1289
|
-
const t = this._language.get();
|
|
1290
|
-
this._cache.delete(t), await this._load(t);
|
|
1291
|
-
}
|
|
1292
|
-
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
1293
|
-
async _load(t) {
|
|
1294
|
-
const e = this._cache.get(t);
|
|
1295
|
-
if (e) {
|
|
1296
|
-
this._apply(e);
|
|
1297
|
-
return;
|
|
1298
|
-
}
|
|
1299
|
-
const s = this._buildCategoriesQuery(t, "ContentCategory"), i = this._buildCategoriesQuery(t, "EntityCategory"), n = this._buildRelationshipsQuery(t);
|
|
1300
|
-
await Y(
|
|
1301
|
-
s + i + n,
|
|
1302
|
-
async () => {
|
|
1303
|
-
const [a, c, r] = await Promise.all([
|
|
1304
|
-
this._runCategoriesQuery(s),
|
|
1305
|
-
this._runCategoriesQuery(i),
|
|
1306
|
-
this._runRelationshipsQuery(n)
|
|
1307
|
-
]);
|
|
1308
|
-
return { contentCategories: a, entityCategories: c, relationships: r };
|
|
1309
|
-
},
|
|
1310
|
-
(a) => {
|
|
1311
|
-
this._cache.set(t, a), this._language.get() === t && this._apply(a);
|
|
1312
|
-
},
|
|
1313
|
-
this._queryCache
|
|
1314
|
-
);
|
|
1315
|
-
}
|
|
1316
|
-
_apply(t) {
|
|
1317
|
-
this._contentCategories.set(t.contentCategories), this._entityCategories.set(t.entityCategories), this._relationships.set(t.relationships);
|
|
1318
|
-
}
|
|
1319
|
-
async _fetchCategories(t, e) {
|
|
1320
|
-
return this._runCategoriesQuery(this._buildCategoriesQuery(t, e));
|
|
1321
|
-
}
|
|
1322
|
-
_buildCategoriesQuery(t, e) {
|
|
1323
|
-
return `PREFIX qcy: <${S.qcy}>
|
|
1324
|
-
PREFIX skos: <${Q.skos}>
|
|
1325
|
-
SELECT ?iri ?parent (SAMPLE(?l) AS ?label)
|
|
1326
|
-
WHERE {
|
|
1327
|
-
?iri a qcy:${e} .
|
|
1328
|
-
OPTIONAL { ?iri skos:prefLabel ?lang_label FILTER(LANG(?lang_label) = "${t}") }
|
|
1329
|
-
OPTIONAL { ?iri skos:prefLabel ?no_lang_label }
|
|
1330
|
-
BIND(COALESCE(?lang_label, ?no_lang_label) AS ?l)
|
|
1331
|
-
OPTIONAL { ?iri skos:broader ?parent }
|
|
1332
|
-
}
|
|
1333
|
-
GROUP BY ?iri ?parent`;
|
|
1334
|
-
}
|
|
1335
|
-
async _runCategoriesQuery(t) {
|
|
1336
|
-
return (await this._api.sparql(t, this._projectId, this._graphType)).results.bindings.filter((s) => s.iri !== void 0).map((s) => {
|
|
1337
|
-
const i = s.iri.value;
|
|
1338
|
-
return {
|
|
1339
|
-
iri: i,
|
|
1340
|
-
label: s.label?.value ?? i.split("#").at(-1) ?? i,
|
|
1341
|
-
parent: s.parent?.value
|
|
1342
|
-
};
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
async _fetchRelationships(t) {
|
|
1346
|
-
return this._runRelationshipsQuery(this._buildRelationshipsQuery(t));
|
|
1347
|
-
}
|
|
1348
|
-
_buildRelationshipsQuery(t) {
|
|
1349
|
-
return `PREFIX qcy: <${S.qcy}>
|
|
1350
|
-
PREFIX rdfs: <${Q.rdfs}>
|
|
1351
|
-
SELECT ?iri ?parent (SAMPLE(?l) AS ?label)
|
|
1352
|
-
WHERE {
|
|
1353
|
-
?x qcy:relatedEntity ?y ;
|
|
1354
|
-
?iri ?y .
|
|
1355
|
-
OPTIONAL { ?iri rdfs:label ?lang_label FILTER(LANG(?lang_label) = "${t}") }
|
|
1356
|
-
OPTIONAL { ?iri rdfs:label ?no_lang_label }
|
|
1357
|
-
BIND(COALESCE(?lang_label, ?no_lang_label) AS ?l)
|
|
1358
|
-
OPTIONAL { ?iri rdfs:subPropertyOf ?parent }
|
|
1359
|
-
}
|
|
1360
|
-
GROUP BY ?iri ?parent`;
|
|
1361
|
-
}
|
|
1362
|
-
async _runRelationshipsQuery(t) {
|
|
1363
|
-
return (await this._api.sparql(t, this._projectId, this._graphType)).results.bindings.filter((s) => s.iri !== void 0).map((s) => {
|
|
1364
|
-
const i = s.iri.value;
|
|
1365
|
-
return {
|
|
1366
|
-
iri: i,
|
|
1367
|
-
label: s.label?.value ?? i.split("#").at(-1) ?? i,
|
|
1368
|
-
parent: s.parent?.value
|
|
1369
|
-
};
|
|
1370
|
-
});
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
const Ne = "https://qlever.dev/api/osm-planet";
|
|
1374
|
-
class ke {
|
|
1375
|
-
constructor(t, e, s = J, i, n) {
|
|
1376
|
-
this._api = t, this._projectId = e, this._queryCache = i, this._graphType = n, this.baseURL = `${s}${e}/`, this.entityInfoMap = this._entityInfoMapComputed, this.entityGraph = this._entityGraph.asReadonly(), this._entityOSMMap.subscribe(() => this._checkPendingOSMFetches()), this._fetchEntityGraph().catch(
|
|
1377
|
-
(a) => console.error("[CueProjectEntities] Entity graph fetch failed:", a)
|
|
1378
|
-
);
|
|
1379
|
-
}
|
|
1380
|
-
_api;
|
|
1381
|
-
_projectId;
|
|
1382
|
-
_queryCache;
|
|
1383
|
-
_graphType;
|
|
1384
|
-
/** Full RDF base URL for this project, e.g. `https://cue.qaecy.com/r/{pid}/` */
|
|
1385
|
-
baseURL;
|
|
1386
|
-
// ── Internal writable slices ───────────────────────────────────────────────
|
|
1387
|
-
_entityDetails = new I({});
|
|
1388
|
-
_entityDocuments = new I({});
|
|
1389
|
-
_entityRelationships = new I({});
|
|
1390
|
-
_entityOSMMap = new I({});
|
|
1391
|
-
_osmWKTMap = new I({});
|
|
1392
|
-
_fetchingOSMIds = /* @__PURE__ */ new Set();
|
|
1393
|
-
_entityGraph = new I(void 0);
|
|
1394
|
-
// ── Derived signals ────────────────────────────────────────────────────────
|
|
1395
|
-
_entityInfoMapComputed = X(
|
|
1396
|
-
[
|
|
1397
|
-
this._entityDetails,
|
|
1398
|
-
this._entityDocuments,
|
|
1399
|
-
this._entityRelationships,
|
|
1400
|
-
this._entityOSMMap,
|
|
1401
|
-
this._osmWKTMap
|
|
1402
|
-
],
|
|
1403
|
-
() => this._computeEntityInfoMap()
|
|
1404
|
-
);
|
|
1405
|
-
/** Merged per-entity detail map. Updated reactively as data arrives. */
|
|
1406
|
-
entityInfoMap;
|
|
1407
|
-
/** Project-level category graph (fetched once per project). */
|
|
1408
|
-
entityGraph;
|
|
1409
|
-
// ── Public helpers ─────────────────────────────────────────────────────────
|
|
1410
|
-
/**
|
|
1411
|
-
* Constructs the full RDF IRI for the given entity UUID.
|
|
1412
|
-
* Use this to bridge the UUID-based batch APIs and the IRI-based per-entity APIs.
|
|
1413
|
-
*/
|
|
1414
|
-
entityIri(t) {
|
|
1415
|
-
return `${this.baseURL}${t}`;
|
|
1416
|
-
}
|
|
1417
|
-
/**
|
|
1418
|
-
* Resets all entity state and re-fetches the entity graph.
|
|
1419
|
-
* Call when the active project changes.
|
|
1420
|
-
*/
|
|
1421
|
-
reset() {
|
|
1422
|
-
this._entityDetails.set({}), this._entityDocuments.set({}), this._entityRelationships.set({}), this._entityOSMMap.set({}), this._osmWKTMap.set({}), this._entityGraph.set(void 0), this._fetchingOSMIds.clear(), this._fetchEntityGraph().catch(
|
|
1423
|
-
(t) => console.error("[CueProjectEntities] Entity graph fetch failed after reset:", t)
|
|
1424
|
-
);
|
|
1425
|
-
}
|
|
1426
|
-
// ── Public imperative API ──────────────────────────────────────────────────
|
|
1427
|
-
/**
|
|
1428
|
-
* Lazily batch-fetches core data (label + categories) for the given entity
|
|
1429
|
-
* UUIDs. Already-fetched UUIDs are skipped.
|
|
1430
|
-
*
|
|
1431
|
-
* Data is merged into `entityInfoMap` once the SPARQL response arrives.
|
|
1432
|
-
*/
|
|
1433
|
-
requestEntityData(t, e = !1) {
|
|
1434
|
-
const s = t.filter((a) => this._entityDetails.get()[a] === void 0);
|
|
1435
|
-
if (s.length === 0) return;
|
|
1436
|
-
const i = s.map((a) => `r:${a}`).join(" "), n = `PREFIX qcy: <${S.qcy}>
|
|
1437
|
-
PREFIX r: <${this.baseURL}>
|
|
1438
|
-
SELECT ?id (SAMPLE(?val) AS ?value) (GROUP_CONCAT(DISTINCT STR(?cat); SEPARATOR=";") AS ?categories) ?mentionCount
|
|
1439
|
-
WHERE {
|
|
1440
|
-
VALUES ?iri { ${i} }
|
|
1441
|
-
?iri qcy:hasEntityCategory ?cat ;
|
|
1442
|
-
qcy:value ?val
|
|
1443
|
-
BIND(REPLACE(STR(?iri), "^.*/([^/]*)$", "$1") AS ?id)
|
|
1444
|
-
${e ? `{SELECT ?iri (COUNT(?mention) AS ?mentionCount)
|
|
1445
|
-
WHERE { ?mention qcy:resolvesTo ?iri } GROUP BY ?iri}` : ""}
|
|
1446
|
-
}
|
|
1447
|
-
GROUP BY ?id ?mentionCount`;
|
|
1448
|
-
this._api.sparql(n, this._projectId, this._graphType).then((a) => {
|
|
1449
|
-
const c = a, r = { ...this._entityDetails.get() };
|
|
1450
|
-
c.results.bindings.forEach((u) => {
|
|
1451
|
-
if (!u.id) return;
|
|
1452
|
-
const p = u.id.value;
|
|
1453
|
-
r[p] = {
|
|
1454
|
-
value: u.value?.value ?? "",
|
|
1455
|
-
categories: u.categories?.value?.split(";").filter(Boolean) ?? [],
|
|
1456
|
-
mentionCount: u.mentionCount ? parseInt(u.mentionCount.value, 10) : void 0
|
|
1457
|
-
};
|
|
1458
|
-
}), this._entityDetails.set(r);
|
|
1459
|
-
}).catch(
|
|
1460
|
-
(a) => console.error("[CueProjectEntities] requestEntityData failed:", a)
|
|
1461
|
-
);
|
|
1462
|
-
}
|
|
1463
|
-
/**
|
|
1464
|
-
* Lazily fetches OSM location data for the given entity UUIDs.
|
|
1465
|
-
* Already-fetched UUIDs are skipped.
|
|
1466
|
-
*
|
|
1467
|
-
* OSM WKT geometry is fetched in a second pass via a federated SPARQL SERVICE
|
|
1468
|
-
* query and merged into `entityInfoMap` reactively once it arrives.
|
|
1469
|
-
*/
|
|
1470
|
-
async requestEntityLocations(t) {
|
|
1471
|
-
const e = t.filter((r) => this._entityOSMMap.get()[r] === void 0);
|
|
1472
|
-
if (e.length === 0) return;
|
|
1473
|
-
const s = { ...this._entityOSMMap.get() };
|
|
1474
|
-
for (const r of e) s[r] = { direct: [], indirect: [] };
|
|
1475
|
-
this._entityOSMMap.set(s);
|
|
1476
|
-
const i = e.map((r) => `r:${r}`).join(" "), n = `PREFIX qcy: <${S.qcy}>
|
|
1477
|
-
PREFIX r: <${this.baseURL}>
|
|
1478
|
-
SELECT ?id ?osm ?direct ?rels ?entityUUID
|
|
1479
|
-
WHERE {
|
|
1480
|
-
VALUES ?iri { ${i} }
|
|
1481
|
-
{
|
|
1482
|
-
SELECT ?iri ?osm ?direct
|
|
1483
|
-
WHERE {
|
|
1484
|
-
?iri qcy:similarTo ?osm .
|
|
1485
|
-
FILTER STRSTARTS(STR(?osm), "https://www.openstreetmap.org/")
|
|
1486
|
-
BIND(true AS ?direct)
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
UNION
|
|
1490
|
-
{
|
|
1491
|
-
SELECT ?iri ?osm ?direct ?entityUUID (GROUP_CONCAT(STR(?rel); SEPARATOR=";") AS ?rels)
|
|
1492
|
-
WHERE {
|
|
1493
|
-
?iri qcy:relatedEntity ?loc .
|
|
1494
|
-
?iri ?rel ?loc .
|
|
1495
|
-
?loc qcy:similarTo ?osm .
|
|
1496
|
-
FILTER STRSTARTS(STR(?osm), "https://www.openstreetmap.org/")
|
|
1497
|
-
FILTER(?rel != qcy:relatedEntity)
|
|
1498
|
-
BIND(false AS ?direct)
|
|
1499
|
-
BIND(REPLACE(STR(?loc), "^.*/([^/]*)$", "$1") AS ?entityUUID)
|
|
1500
|
-
} GROUP BY ?iri ?osm ?direct ?entityUUID
|
|
1501
|
-
}
|
|
1502
|
-
BIND(REPLACE(STR(?iri), "^.*/([^/]*)$", "$1") AS ?id)
|
|
1503
|
-
}`, a = await this._api.sparql(n, this._projectId, this._graphType), c = { ...this._entityOSMMap.get() };
|
|
1504
|
-
a.results.bindings.forEach((r) => {
|
|
1505
|
-
if (!r.id || !r.osm) return;
|
|
1506
|
-
const u = r.id.value, p = r.osm.value, m = r.direct?.value === "true", h = r.rels?.value?.split(";").filter(Boolean) ?? [], g = r.entityUUID?.value, f = c[u] ?? { direct: [], indirect: [] };
|
|
1507
|
-
m ? f.direct.push(p) : g && f.indirect.push({ osm: p, viaRels: h, entityUUID: g }), c[u] = f;
|
|
1508
|
-
}), this._entityOSMMap.set(c);
|
|
1509
|
-
}
|
|
1510
|
-
/**
|
|
1511
|
-
* Fetches incoming and outgoing relationships for a single entity IRI.
|
|
1512
|
-
* Neighbouring entity core data is opportunistically populated into
|
|
1513
|
-
* `entityInfoMap` as a side-effect.
|
|
1514
|
-
*
|
|
1515
|
-
* The result is stored in `entityInfoMap[uuid].relationshipData` and also
|
|
1516
|
-
* returned directly for callers that need an immediate value.
|
|
1517
|
-
*/
|
|
1518
|
-
async fetchEntityRelationships(t) {
|
|
1519
|
-
const e = t.replace(/^.*\/([^/]*)$/, "$1");
|
|
1520
|
-
this._entityRelationships.set({
|
|
1521
|
-
...this._entityRelationships.get(),
|
|
1522
|
-
[e]: { incoming: [], outgoing: [] }
|
|
1523
|
-
});
|
|
1524
|
-
const [s, i] = await Promise.all([
|
|
1525
|
-
this._fetchOutgoingRelationships(t),
|
|
1526
|
-
this._fetchIncomingRelationships(t)
|
|
1527
|
-
]), n = { outgoing: s, incoming: i };
|
|
1528
|
-
return this._entityRelationships.set({ ...this._entityRelationships.get(), [e]: n }), n;
|
|
1529
|
-
}
|
|
1530
|
-
/**
|
|
1531
|
-
* Fetches UUIDs of documents that reference the given entity IRI.
|
|
1532
|
-
* Also triggers a core-data fetch for the entity itself if not yet loaded.
|
|
1533
|
-
*
|
|
1534
|
-
* Returns document UUIDs. Full document data will be populated by the
|
|
1535
|
-
* document service (future `CueProjectDocuments`).
|
|
1536
|
-
*/
|
|
1537
|
-
async fetchEntityDocuments(t) {
|
|
1538
|
-
const e = t.replace(/^.*\/([^/]*)$/, "$1");
|
|
1539
|
-
this._entityDocuments.set({ ...this._entityDocuments.get(), [e]: [] }), this._entityDetails.get()[e] === void 0 && this.requestEntityData([e]);
|
|
1540
|
-
const s = `PREFIX qcy: <${S.qcy}>
|
|
1541
|
-
SELECT DISTINCT ?id
|
|
1542
|
-
WHERE {
|
|
1543
|
-
BIND(<${t}> AS ?iri)
|
|
1544
|
-
?doc a qcy:FileContent ;
|
|
1545
|
-
qcy:about ?iri .
|
|
1546
|
-
BIND(REPLACE(STR(?doc), "^.*/([^/]*)$", "$1") AS ?id)
|
|
1547
|
-
}`, n = (await this._api.sparql(s, this._projectId, this._graphType)).results.bindings.filter((a) => a.id !== void 0).map((a) => a.id.value);
|
|
1548
|
-
return this._entityDocuments.set({ ...this._entityDocuments.get(), [e]: n }), n;
|
|
1549
|
-
}
|
|
1550
|
-
// ── Private helpers ────────────────────────────────────────────────────────
|
|
1551
|
-
_computeEntityInfoMap() {
|
|
1552
|
-
const t = this._entityDetails.get(), e = this._entityDocuments.get(), s = this._entityRelationships.get(), i = this._entityOSMMap.get(), n = this._osmWKTMap.get(), a = /* @__PURE__ */ new Set([
|
|
1553
|
-
...Object.keys(t),
|
|
1554
|
-
...Object.keys(i).filter((r) => {
|
|
1555
|
-
const u = i[r];
|
|
1556
|
-
return u.direct.length > 0 || u.indirect.length > 0;
|
|
1557
|
-
})
|
|
1558
|
-
]), c = {};
|
|
1559
|
-
for (const r of a) {
|
|
1560
|
-
const u = t[r], p = i[r];
|
|
1561
|
-
let m, h;
|
|
1562
|
-
if (p) {
|
|
1563
|
-
const g = /* @__PURE__ */ new Set();
|
|
1564
|
-
m = p.direct.filter(
|
|
1565
|
-
(y) => n[y] !== void 0 && !g.has(y) && !!g.add(y)
|
|
1566
|
-
).map((y) => ({ osmIRI: y, wkt: n[y] }));
|
|
1567
|
-
const f = /* @__PURE__ */ new Map();
|
|
1568
|
-
for (const { osm: y, viaRels: C, entityUUID: d } of p.indirect) {
|
|
1569
|
-
const T = n[y];
|
|
1570
|
-
if (T)
|
|
1571
|
-
for (const A of C) {
|
|
1572
|
-
const E = `${A}:${d}`, _ = f.get(E) ?? { geometries: [], entityUUID: d };
|
|
1573
|
-
_.geometries.some((R) => R.osmIRI === y) || (_.geometries.push({ osmIRI: y, wkt: T }), f.set(E, _));
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
f.size > 0 && (h = Array.from(f.entries()).map(
|
|
1577
|
-
([y, { geometries: C, entityUUID: d }]) => ({
|
|
1578
|
-
rel: y.split(":")[0],
|
|
1579
|
-
geometries: C,
|
|
1580
|
-
entityUUID: d
|
|
1581
|
-
})
|
|
1582
|
-
));
|
|
1583
|
-
}
|
|
1584
|
-
c[r] = {
|
|
1585
|
-
value: u?.value ?? "",
|
|
1586
|
-
categories: u?.categories ?? [],
|
|
1587
|
-
mentionCount: u?.mentionCount,
|
|
1588
|
-
documentRefs: e[r],
|
|
1589
|
-
relationshipData: s[r],
|
|
1590
|
-
directMapGeometries: m,
|
|
1591
|
-
indirectMapGeometries: h
|
|
1592
|
-
};
|
|
1593
|
-
}
|
|
1594
|
-
return c;
|
|
1595
|
-
}
|
|
1596
|
-
async _fetchOutgoingRelationships(t) {
|
|
1597
|
-
const e = `PREFIX qcy: <${S.qcy}>
|
|
1598
|
-
SELECT ?rel ?related (SAMPLE(?val) AS ?nodeValue) (GROUP_CONCAT(STR(?cat); SEPARATOR=";") AS ?categories)
|
|
1599
|
-
WHERE {
|
|
1600
|
-
BIND(<${t}> AS ?s)
|
|
1601
|
-
?s qcy:relatedEntity ?related ;
|
|
1602
|
-
?rel ?related .
|
|
1603
|
-
?related qcy:hasEntityCategory ?cat ;
|
|
1604
|
-
qcy:value ?val
|
|
1605
|
-
FILTER(?rel != qcy:relatedEntity)
|
|
1606
|
-
}
|
|
1607
|
-
GROUP BY ?rel ?related`, s = await this._api.sparql(e, this._projectId, this._graphType), i = [], n = { ...this._entityDetails.get() };
|
|
1608
|
-
return s.results.bindings.forEach((a) => {
|
|
1609
|
-
if (!a.rel || !a.related) return;
|
|
1610
|
-
const c = a.categories?.value?.split(";").filter(Boolean) ?? [];
|
|
1611
|
-
i.push({
|
|
1612
|
-
relIRI: a.rel.value,
|
|
1613
|
-
nodeIRI: a.related.value,
|
|
1614
|
-
nodeValue: a.nodeValue?.value ?? "",
|
|
1615
|
-
nodeCategories: c
|
|
1616
|
-
});
|
|
1617
|
-
const r = a.related.value.replace(/^.*\/([^/]*)$/, "$1");
|
|
1618
|
-
n[r] = {
|
|
1619
|
-
value: a.nodeValue?.value ?? "",
|
|
1620
|
-
categories: c
|
|
1621
|
-
};
|
|
1622
|
-
}), this._entityDetails.set(n), i;
|
|
1623
|
-
}
|
|
1624
|
-
async _fetchIncomingRelationships(t) {
|
|
1625
|
-
const e = `PREFIX qcy: <${S.qcy}>
|
|
1626
|
-
SELECT ?rel ?relating (SAMPLE(?val) AS ?nodeValue) (GROUP_CONCAT(STR(?cat); SEPARATOR=";") AS ?categories)
|
|
1627
|
-
WHERE {
|
|
1628
|
-
BIND(<${t}> AS ?s)
|
|
1629
|
-
?relating qcy:relatedEntity ?s ;
|
|
1630
|
-
?rel ?s .
|
|
1631
|
-
?relating qcy:hasEntityCategory ?cat ;
|
|
1632
|
-
qcy:value ?val
|
|
1633
|
-
FILTER(?rel != qcy:relatedEntity)
|
|
1634
|
-
}
|
|
1635
|
-
GROUP BY ?rel ?relating`, s = await this._api.sparql(e, this._projectId, this._graphType), i = [], n = { ...this._entityDetails.get() };
|
|
1636
|
-
return s.results.bindings.forEach((a) => {
|
|
1637
|
-
if (!a.rel || !a.relating) return;
|
|
1638
|
-
const c = a.categories?.value?.split(";").filter(Boolean) ?? [];
|
|
1639
|
-
i.push({
|
|
1640
|
-
relIRI: a.rel.value,
|
|
1641
|
-
nodeIRI: a.relating.value,
|
|
1642
|
-
nodeValue: a.nodeValue?.value ?? "",
|
|
1643
|
-
nodeCategories: c
|
|
1644
|
-
});
|
|
1645
|
-
const r = a.relating.value.replace(/^.*\/([^/]*)$/, "$1");
|
|
1646
|
-
n[r] = {
|
|
1647
|
-
value: a.nodeValue?.value ?? "",
|
|
1648
|
-
categories: c
|
|
1649
|
-
};
|
|
1650
|
-
}), this._entityDetails.set(n), i;
|
|
1651
|
-
}
|
|
1652
|
-
async _fetchEntityGraph() {
|
|
1653
|
-
const t = `PREFIX qcy: <${S.qcy}>
|
|
1654
|
-
SELECT ?e1Cat ?e2Cat (COUNT(?e1) AS ?e1Count) (COUNT(?e2) AS ?e2Count)
|
|
1655
|
-
WHERE {
|
|
1656
|
-
?e1 a qcy:CanonicalEntity ;
|
|
1657
|
-
qcy:hasEntityCategory ?e1Cat ;
|
|
1658
|
-
qcy:relatedEntity ?e2 .
|
|
1659
|
-
?e2 qcy:hasEntityCategory ?e2Cat
|
|
1660
|
-
}
|
|
1661
|
-
GROUP BY ?e1Cat ?e2Cat`;
|
|
1662
|
-
await Y(
|
|
1663
|
-
t,
|
|
1664
|
-
async () => {
|
|
1665
|
-
const e = await this._api.sparql(t, this._projectId, this._graphType), s = [], i = [];
|
|
1666
|
-
return e.results.bindings.forEach((n) => {
|
|
1667
|
-
if (!n.e1Cat || !n.e2Cat) return;
|
|
1668
|
-
const a = n.e1Cat.value, c = n.e2Cat.value, r = n.e1Count && parseInt(n.e1Count.value, 10) || 20, u = n.e2Count && parseInt(n.e2Count.value, 10) || 20;
|
|
1669
|
-
s.some((p) => p.iri === a) || s.push({ iri: a, size: r }), s.some((p) => p.iri === c) || s.push({ iri: c, size: u }), i.push({ sourceID: a, targetID: c });
|
|
1670
|
-
}), { entities: s, relations: i };
|
|
1671
|
-
},
|
|
1672
|
-
(e) => this._entityGraph.set(e),
|
|
1673
|
-
this._queryCache
|
|
1674
|
-
);
|
|
1675
|
-
}
|
|
1676
|
-
/** Detects new OSM IRIs in the OSM map that don't yet have WKT and fetches them. */
|
|
1677
|
-
_checkPendingOSMFetches() {
|
|
1678
|
-
const t = this._entityOSMMap.get(), e = this._osmWKTMap.get(), s = [];
|
|
1679
|
-
for (const i of Object.values(t)) {
|
|
1680
|
-
for (const n of i.direct)
|
|
1681
|
-
!e[n] && !this._fetchingOSMIds.has(n) && s.push(n);
|
|
1682
|
-
for (const { osm: n } of i.indirect)
|
|
1683
|
-
!e[n] && !this._fetchingOSMIds.has(n) && s.push(n);
|
|
1684
|
-
}
|
|
1685
|
-
if (s.length !== 0) {
|
|
1686
|
-
for (const i of s) this._fetchingOSMIds.add(i);
|
|
1687
|
-
this._fetchOSMLocations(s).catch(
|
|
1688
|
-
(i) => console.error("[CueProjectEntities] OSM WKT fetch failed:", i)
|
|
1689
|
-
);
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
async _fetchOSMLocations(t) {
|
|
1693
|
-
const e = t.filter((c) => !c.includes("/relation/"));
|
|
1694
|
-
if (e.length === 0) return;
|
|
1695
|
-
const s = e.map((c) => `<${c}>`).join(" "), i = `PREFIX geo: <${Q.geo}>
|
|
1696
|
-
SELECT * WHERE {
|
|
1697
|
-
VALUES ?s { ${s} }
|
|
1698
|
-
SERVICE <${Ne}> {
|
|
1699
|
-
?s geo:hasGeometry/geo:asWKT ?wkt
|
|
1700
|
-
}
|
|
1701
|
-
}`, n = await this._api.sparql(i, this._projectId, this._graphType), a = { ...this._osmWKTMap.get() };
|
|
1702
|
-
n.results.bindings.forEach((c) => {
|
|
1703
|
-
!c.s || !c.wkt || (a[c.s.value] = c.wkt.value, this._fetchingOSMIds.delete(c.s.value));
|
|
1704
|
-
}), this._osmWKTMap.set(a);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
class Be {
|
|
1708
|
-
constructor(t, e, s, i = J, n, a) {
|
|
1709
|
-
this._api = t, this._projectId = e, this._queryCache = n, this._graphType = a, this.baseURL = `${i}${e}/`, this._language = s, this.documentInfoMap = this._documentInfoMap.asReadonly(), this.projectDocumentsData = this._projectDocumentsData.asReadonly();
|
|
1710
|
-
}
|
|
1711
|
-
_api;
|
|
1712
|
-
_projectId;
|
|
1713
|
-
_queryCache;
|
|
1714
|
-
_graphType;
|
|
1715
|
-
/** Full RDF base URL for this project, e.g. `https://cue.qaecy.com/r/{pid}/` */
|
|
1716
|
-
baseURL;
|
|
1717
|
-
_language;
|
|
1718
|
-
_documentInfoMap = new I({});
|
|
1719
|
-
_projectDocumentsData = new I({
|
|
1720
|
-
duplicateCount: 0,
|
|
1721
|
-
documentsBySuffix: {},
|
|
1722
|
-
documentsByContentCategory: {}
|
|
1723
|
-
});
|
|
1724
|
-
/** Lazily populated per-document detail map. */
|
|
1725
|
-
documentInfoMap;
|
|
1726
|
-
/** Project-level document overview (grouped counts + sizes). */
|
|
1727
|
-
projectDocumentsData;
|
|
1728
|
-
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
1729
|
-
/**
|
|
1730
|
-
* Resets all document state. Call when the active project changes.
|
|
1731
|
-
* Follow with `fetchOverview()` once the triplestore is ready.
|
|
1732
|
-
*/
|
|
1733
|
-
reset() {
|
|
1734
|
-
this._documentInfoMap.set({}), this._projectDocumentsData.set({
|
|
1735
|
-
duplicateCount: 0,
|
|
1736
|
-
documentsBySuffix: {},
|
|
1737
|
-
documentsByContentCategory: {}
|
|
1738
|
-
});
|
|
1739
|
-
}
|
|
1740
|
-
/**
|
|
1741
|
-
* Updates the active language and clears the document info map so that
|
|
1742
|
-
* language-sensitive fields (subject, summary) are re-fetched on the next
|
|
1743
|
-
* `requestDocumentData()` call.
|
|
1744
|
-
*/
|
|
1745
|
-
setLanguage(t) {
|
|
1746
|
-
this._language !== t && (this._language = t, this._documentInfoMap.set({}));
|
|
1747
|
-
}
|
|
1748
|
-
// ── Public API ─────────────────────────────────────────────────────────────
|
|
1749
|
-
/**
|
|
1750
|
-
* Fetches the three-part project overview (by suffix, by content category,
|
|
1751
|
-
* duplicate count) in parallel and writes them as a single atomic update to
|
|
1752
|
-
* `projectDocumentsData`. Safe to call again to refresh.
|
|
1753
|
-
*/
|
|
1754
|
-
async fetchOverview() {
|
|
1755
|
-
this._projectDocumentsData.set({
|
|
1756
|
-
duplicateCount: 0,
|
|
1757
|
-
documentsBySuffix: {},
|
|
1758
|
-
documentsByContentCategory: {}
|
|
1759
|
-
});
|
|
1760
|
-
const t = this._buildDocumentsBySuffixQuery(), e = this._buildDocumentsByContentCategoryQuery(), s = this._buildDuplicateCountQuery();
|
|
1761
|
-
await Y(
|
|
1762
|
-
t + e + s,
|
|
1763
|
-
async () => {
|
|
1764
|
-
const [i, n, a] = await Promise.all([
|
|
1765
|
-
this._runDocumentsBySuffixQuery(t),
|
|
1766
|
-
this._runDocumentsByContentCategoryQuery(e),
|
|
1767
|
-
this._runDuplicateCountQuery(s)
|
|
1768
|
-
]);
|
|
1769
|
-
return { duplicateCount: a, documentsBySuffix: i, documentsByContentCategory: n };
|
|
1770
|
-
},
|
|
1771
|
-
(i) => this._projectDocumentsData.set(i),
|
|
1772
|
-
this._queryCache
|
|
1773
|
-
);
|
|
1774
|
-
}
|
|
1775
|
-
/**
|
|
1776
|
-
* Lazily batch-fetches core metadata for the given document UUIDs.
|
|
1777
|
-
* Already-cached UUIDs are skipped. Data is merged into `documentInfoMap`
|
|
1778
|
-
* once the SPARQL response arrives.
|
|
1779
|
-
*/
|
|
1780
|
-
requestDocumentData(t) {
|
|
1781
|
-
const e = t.filter((a) => this._documentInfoMap.get()[a] === void 0);
|
|
1782
|
-
if (e.length === 0) return;
|
|
1783
|
-
const s = e.map((a) => `r:${a}`).join(" "), i = this._language, n = `PREFIX qcy: <${S.qcy}>
|
|
1784
|
-
PREFIX r: <${this.baseURL}>
|
|
1785
|
-
SELECT ?id ?contentIRI ?suffix ?size ?subject ?summary
|
|
1786
|
-
(SAMPLE(?fp) AS ?path)
|
|
1787
|
-
(GROUP_CONCAT(DISTINCT ?tag; SEPARATOR=";") AS ?tags)
|
|
1788
|
-
(GROUP_CONCAT(DISTINCT STR(?cat); SEPARATOR=";") AS ?categories)
|
|
1789
|
-
WHERE {
|
|
1790
|
-
VALUES ?contentIRI { ${s} }
|
|
1791
|
-
?contentIRI qcy:sizeBytes ?size ;
|
|
1792
|
-
qcy:hasFileLocation ?loc .
|
|
1793
|
-
?loc qcy:filePath ?fp ;
|
|
1794
|
-
qcy:suffix ?suffix .
|
|
1795
|
-
OPTIONAL { ?contentIRI qcy:hasContentCategory ?cat }
|
|
1796
|
-
OPTIONAL { ?contentIRI qcy:tag ?tag }
|
|
1797
|
-
OPTIONAL { ?loc qcy:remoteProviderId ?pid }
|
|
1798
|
-
|
|
1799
|
-
OPTIONAL { ?contentIRI qcy:subject ?lang_subj FILTER(LANG(?lang_subj) = "${i}") }
|
|
1800
|
-
OPTIONAL { ?contentIRI qcy:subject ?no_lang_subj }
|
|
1801
|
-
BIND(COALESCE(?lang_subj, ?no_lang_subj) AS ?subject)
|
|
1802
|
-
|
|
1803
|
-
OPTIONAL { ?contentIRI qcy:textSummary ?lang_summary FILTER(LANG(?lang_summary) = "${i}") }
|
|
1804
|
-
OPTIONAL { ?contentIRI qcy:textSummary ?no_lang_summary }
|
|
1805
|
-
BIND(COALESCE(?lang_summary, ?no_lang_summary) AS ?summary)
|
|
1806
|
-
|
|
1807
|
-
BIND(REPLACE(STR(?contentIRI), "^.*/([^/]*)$", "$1") AS ?id)
|
|
1808
|
-
}
|
|
1809
|
-
GROUP BY ?id ?contentIRI ?suffix ?size ?subject ?summary`;
|
|
1810
|
-
this._api.sparql(n, this._projectId).then((a) => {
|
|
1811
|
-
const c = a, r = { ...this._documentInfoMap.get() };
|
|
1812
|
-
c.results.bindings.forEach((u) => {
|
|
1813
|
-
if (!u.id || !u.contentIRI) return;
|
|
1814
|
-
const p = u.id.value;
|
|
1815
|
-
r[p] = {
|
|
1816
|
-
id: p,
|
|
1817
|
-
contentIRI: u.contentIRI.value,
|
|
1818
|
-
path: u.path?.value ?? "",
|
|
1819
|
-
suffix: u.suffix?.value ?? "",
|
|
1820
|
-
size: u.size ? parseInt(u.size.value, 10) : 0,
|
|
1821
|
-
tags: u.tags?.value?.split(";").filter(Boolean) ?? [],
|
|
1822
|
-
categories: u.categories?.value?.split(";").filter(Boolean) ?? [],
|
|
1823
|
-
subject: u.subject?.value,
|
|
1824
|
-
summary: u.summary?.value,
|
|
1825
|
-
providerId: u.pid?.value
|
|
1826
|
-
};
|
|
1827
|
-
}), this._documentInfoMap.set(r);
|
|
1828
|
-
}).catch(
|
|
1829
|
-
(a) => console.error("[CueProjectDocuments] requestDocumentData failed:", a)
|
|
1830
|
-
);
|
|
1831
|
-
}
|
|
1832
|
-
// ── Private helpers ────────────────────────────────────────────────────────
|
|
1833
|
-
async _fetchDocumentsBySuffix() {
|
|
1834
|
-
return this._runDocumentsBySuffixQuery(this._buildDocumentsBySuffixQuery());
|
|
1835
|
-
}
|
|
1836
|
-
_buildDocumentsBySuffixQuery() {
|
|
1837
|
-
return `PREFIX qcy: <${S.qcy}>
|
|
1838
|
-
SELECT ?ext (SUM(?size) AS ?totalSize) (COUNT(*) AS ?docCount)
|
|
1839
|
-
WHERE {
|
|
1840
|
-
{
|
|
1841
|
-
SELECT DISTINCT ?fc ?ext ?size
|
|
1842
|
-
WHERE {
|
|
1843
|
-
?fc a qcy:FileContent ;
|
|
1844
|
-
qcy:sizeBytes ?size ;
|
|
1845
|
-
qcy:hasFileLocation/qcy:suffix ?ext .
|
|
1846
|
-
FILTER NOT EXISTS { ?x qcy:alternativeRepresentation ?fc }
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
}
|
|
1850
|
-
GROUP BY ?ext
|
|
1851
|
-
ORDER BY DESC(?docCount)`;
|
|
1852
|
-
}
|
|
1853
|
-
async _runDocumentsBySuffixQuery(t) {
|
|
1854
|
-
const e = await this._api.sparql(t, this._projectId, this._graphType), s = {};
|
|
1855
|
-
return e.results.bindings.forEach((i) => {
|
|
1856
|
-
i.ext && (s[i.ext.value] = {
|
|
1857
|
-
size: i.totalSize ? parseInt(i.totalSize.value, 10) : 0,
|
|
1858
|
-
count: i.docCount ? parseInt(i.docCount.value, 10) : 0
|
|
1859
|
-
});
|
|
1860
|
-
}), s;
|
|
1861
|
-
}
|
|
1862
|
-
async _fetchDocumentsByContentCategory() {
|
|
1863
|
-
return this._runDocumentsByContentCategoryQuery(this._buildDocumentsByContentCategoryQuery());
|
|
1864
|
-
}
|
|
1865
|
-
_buildDocumentsByContentCategoryQuery() {
|
|
1866
|
-
return `PREFIX qcy: <${S.qcy}>
|
|
1867
|
-
SELECT ?cat (SUM(?size) AS ?totalSize) (COUNT(*) AS ?docCount)
|
|
1868
|
-
WHERE {
|
|
1869
|
-
{
|
|
1870
|
-
SELECT DISTINCT ?fc ?cat ?size
|
|
1871
|
-
WHERE {
|
|
1872
|
-
?fc a qcy:FileContent ;
|
|
1873
|
-
qcy:hasContentCategory ?cat ;
|
|
1874
|
-
qcy:sizeBytes ?size .
|
|
1875
|
-
FILTER NOT EXISTS { ?x qcy:alternativeRepresentation ?fc }
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
GROUP BY ?cat
|
|
1880
|
-
ORDER BY DESC(?docCount)`;
|
|
1881
|
-
}
|
|
1882
|
-
async _runDocumentsByContentCategoryQuery(t) {
|
|
1883
|
-
const e = await this._api.sparql(t, this._projectId, this._graphType), s = {};
|
|
1884
|
-
return e.results.bindings.forEach((i) => {
|
|
1885
|
-
i.cat && (s[i.cat.value] = {
|
|
1886
|
-
size: i.totalSize ? parseInt(i.totalSize.value, 10) : 0,
|
|
1887
|
-
count: i.docCount ? parseInt(i.docCount.value, 10) : 0
|
|
1888
|
-
});
|
|
1889
|
-
}), s;
|
|
1890
|
-
}
|
|
1891
|
-
async _fetchDuplicateCount() {
|
|
1892
|
-
return this._runDuplicateCountQuery(this._buildDuplicateCountQuery());
|
|
1893
|
-
}
|
|
1894
|
-
_buildDuplicateCountQuery() {
|
|
1895
|
-
return `PREFIX qcy: <${S.qcy}>
|
|
1896
|
-
SELECT (COUNT(*) AS ?count)
|
|
1897
|
-
WHERE {
|
|
1898
|
-
SELECT ?fc
|
|
1899
|
-
WHERE {
|
|
1900
|
-
?fc a qcy:FileContent ;
|
|
1901
|
-
qcy:hasFileLocation ?fl .
|
|
1902
|
-
FILTER NOT EXISTS { ?x qcy:alternativeRepresentation ?fc }
|
|
1903
|
-
}
|
|
1904
|
-
GROUP BY ?fc
|
|
1905
|
-
HAVING (COUNT(?fl) > 1)
|
|
1906
|
-
}`;
|
|
1907
|
-
}
|
|
1908
|
-
async _runDuplicateCountQuery(t) {
|
|
1909
|
-
const s = (await this._api.sparql(t, this._projectId, this._graphType)).results.bindings[0];
|
|
1910
|
-
return s?.count ? parseInt(s.count.value, 10) : 0;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
class Fe {
|
|
1914
|
-
constructor(t, e, { language: s, queryCache: i, rdfBase: n = J, graphType: a }) {
|
|
1915
|
-
this._api = t, this._projectId = e, this.schema = new qe(t, e, s, i, a), this.entities = new ke(t, e, n, i, a), this.documents = new Be(t, e, s, n, i, a), this.availableContentCategories = this.schema.availableContentCategories, this.availableEntityCategories = this.schema.availableEntityCategories, this.availableEntityRelationships = this.schema.availableEntityRelationships, this.entityInfoMap = this.entities.entityInfoMap, this.entityGraph = this.entities.entityGraph, this.documentInfoMap = this.documents.documentInfoMap, this.projectDocumentsData = this.documents.projectDocumentsData, this.searchResults = this._searchResults.asReadonly(), this.documents.fetchOverview().catch((c) => console.error("[CueProjectView] fetchOverview failed:", c));
|
|
1916
|
-
}
|
|
1917
|
-
_api;
|
|
1918
|
-
_projectId;
|
|
1919
|
-
/** Direct access to the schema data class (available categories / relationships). */
|
|
1920
|
-
schema;
|
|
1921
|
-
/** Direct access to the entity data class. */
|
|
1922
|
-
entities;
|
|
1923
|
-
/** Direct access to the document data class. */
|
|
1924
|
-
documents;
|
|
1925
|
-
// ── Proxied signals ────────────────────────────────────────────────────────
|
|
1926
|
-
/** Available content category definitions for this project. Auto-fetched on init. */
|
|
1927
|
-
availableContentCategories;
|
|
1928
|
-
/** Available entity category definitions for this project. Auto-fetched on init. */
|
|
1929
|
-
availableEntityCategories;
|
|
1930
|
-
/** Available entity relationship types. Auto-fetched on init. */
|
|
1931
|
-
availableEntityRelationships;
|
|
1932
|
-
/** Merged per-entity detail map. Populated lazily via `requestEntityData()` etc. */
|
|
1933
|
-
entityInfoMap;
|
|
1934
|
-
/** Project-level entity co-occurrence graph. Fetched once on init. */
|
|
1935
|
-
entityGraph;
|
|
1936
|
-
/** Per-document info map. Populated lazily via `requestDocumentData()`. */
|
|
1937
|
-
documentInfoMap;
|
|
1938
|
-
/** Project document overview (counts by suffix and category). Fetched on init. */
|
|
1939
|
-
projectDocumentsData;
|
|
1940
|
-
// ── Search state ───────────────────────────────────────────────────────────
|
|
1941
|
-
_searchResults = new I(void 0);
|
|
1942
|
-
/** The result of the most recent `search()` call. `undefined` before first search. */
|
|
1943
|
-
searchResults;
|
|
1944
|
-
_destroyed = !1;
|
|
1945
|
-
// ── Entity methods ─────────────────────────────────────────────────────────
|
|
1946
|
-
/**
|
|
1947
|
-
* Lazily batch-fetch core data (label + categories) for the given entity UUIDs.
|
|
1948
|
-
* Already-fetched UUIDs are skipped. Populates `entityInfoMap`.
|
|
1949
|
-
*/
|
|
1950
|
-
requestEntityData(t, e = !1) {
|
|
1951
|
-
this._destroyed || this.entities.requestEntityData(t, e);
|
|
1952
|
-
}
|
|
1953
|
-
/**
|
|
1954
|
-
* Lazily fetch OSM location data for the given entity UUIDs.
|
|
1955
|
-
* Already-fetched UUIDs are skipped. Populates `entityInfoMap` geometry fields.
|
|
1956
|
-
*/
|
|
1957
|
-
async requestEntityLocations(t) {
|
|
1958
|
-
if (!this._destroyed)
|
|
1959
|
-
return this.entities.requestEntityLocations(t);
|
|
1960
|
-
}
|
|
1961
|
-
/**
|
|
1962
|
-
* Fetch incoming and outgoing relationships for a single entity IRI.
|
|
1963
|
-
* Result is stored in `entityInfoMap[uuid].relationshipData`.
|
|
1964
|
-
*/
|
|
1965
|
-
async fetchEntityRelationships(t) {
|
|
1966
|
-
if (this._destroyed) throw new Error("CueProjectView is destroyed");
|
|
1967
|
-
return this.entities.fetchEntityRelationships(t);
|
|
1968
|
-
}
|
|
1969
|
-
/**
|
|
1970
|
-
* Fetch UUIDs of documents that reference the given entity IRI.
|
|
1971
|
-
* Result is stored in `entityInfoMap[uuid].documentRefs`.
|
|
1972
|
-
*/
|
|
1973
|
-
async fetchEntityDocuments(t) {
|
|
1974
|
-
if (this._destroyed) throw new Error("CueProjectView is destroyed");
|
|
1975
|
-
return this.entities.fetchEntityDocuments(t);
|
|
1976
|
-
}
|
|
1977
|
-
/** Constructs the full RDF IRI for an entity UUID. */
|
|
1978
|
-
entityIri(t) {
|
|
1979
|
-
return this.entities.entityIri(t);
|
|
1980
|
-
}
|
|
1981
|
-
// ── Document methods ───────────────────────────────────────────────────────
|
|
1982
|
-
/**
|
|
1983
|
-
* Lazily batch-fetch document info for the given UUIDs.
|
|
1984
|
-
* Already-fetched UUIDs are skipped. Populates `documentInfoMap`.
|
|
1985
|
-
*/
|
|
1986
|
-
requestDocumentData(t) {
|
|
1987
|
-
this._destroyed || this.documents.requestDocumentData(t);
|
|
1988
|
-
}
|
|
1989
|
-
// ── Search ─────────────────────────────────────────────────────────────────
|
|
1990
|
-
/**
|
|
1991
|
-
* Run a natural-language search against the project.
|
|
1992
|
-
* The result is stored in `searchResults` and replaces any previous result.
|
|
1993
|
-
*/
|
|
1994
|
-
async search(t, e) {
|
|
1995
|
-
if (this._destroyed) return;
|
|
1996
|
-
const s = await this._api.search({
|
|
1997
|
-
term: t,
|
|
1998
|
-
projectId: this._projectId,
|
|
1999
|
-
categories: e?.categories
|
|
2000
|
-
});
|
|
2001
|
-
this._destroyed || this._searchResults.set(s);
|
|
2002
|
-
}
|
|
2003
|
-
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
2004
|
-
/**
|
|
2005
|
-
* Switch the active language for schema labels and document text fields.
|
|
2006
|
-
* Schema responses are cached per language (instant if previously loaded).
|
|
2007
|
-
* The document info map is cleared and lazily re-populated on next access.
|
|
2008
|
-
*/
|
|
2009
|
-
setLanguage(t) {
|
|
2010
|
-
this._destroyed || (this.schema.setLanguage(t), this.documents.setLanguage(t));
|
|
2011
|
-
}
|
|
2012
|
-
/**
|
|
2013
|
-
* Reset all entity and document state and re-fetch the project overview.
|
|
2014
|
-
* Prefer creating a fresh `CueProjectView` when switching projects.
|
|
2015
|
-
* Use `reset()` only when the same project's data needs to be invalidated.
|
|
2016
|
-
*/
|
|
2017
|
-
reset() {
|
|
2018
|
-
this._destroyed || (this.entities.reset(), this.documents.reset(), this._searchResults.set(void 0), this.documents.fetchOverview().catch((t) => console.error("[CueProjectView] fetchOverview failed after reset:", t)));
|
|
2019
|
-
}
|
|
2020
|
-
/**
|
|
2021
|
-
* Tear down this view instance. Clears all reactive state and blocks further
|
|
2022
|
-
* updates. Call from the Angular adapter's `ngOnDestroy` or equivalent.
|
|
2023
|
-
*/
|
|
2024
|
-
destroy() {
|
|
2025
|
-
this._destroyed = !0, this._searchResults.set(void 0);
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
function Ct(l, t) {
|
|
2029
|
-
return new Promise((e) => {
|
|
2030
|
-
const s = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
|
|
2031
|
-
for (const d of l)
|
|
2032
|
-
s.has(d.contentUUID) || s.set(d.contentUUID, []), s.get(d.contentUUID)?.push(d), i.set(d.locationUUID, d);
|
|
2033
|
-
const n = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
|
|
2034
|
-
for (const d of t)
|
|
2035
|
-
n.has(d.contentUUID) || n.set(d.contentUUID, []), n.get(d.contentUUID)?.push(d), d.locationUUID && a.set(d.locationUUID, d);
|
|
2036
|
-
const c = [], r = [];
|
|
2037
|
-
for (const d of l)
|
|
2038
|
-
n.has(d.contentUUID) ? (n.get(d.contentUUID) || []).some((E) => E.locationUUID === d.locationUUID) || r.push(d) : c.push(d);
|
|
2039
|
-
const u = [], p = [];
|
|
2040
|
-
for (const d of t) {
|
|
2041
|
-
const T = s.get(d.contentUUID);
|
|
2042
|
-
T ? T.some((E) => E.locationUUID === d.locationUUID) || p.push(d) : u.push(d);
|
|
2043
|
-
}
|
|
2044
|
-
const m = l.length - c.length - r.length, h = l.length, g = h > 0 ? m / h : 1;
|
|
2045
|
-
let f = 0, y = 0;
|
|
2046
|
-
for (const d of l)
|
|
2047
|
-
y += d.size || 0, (n.get(d.contentUUID) || []).some((E) => E.locationUUID === d.locationUUID) && (f += d.size || 0);
|
|
2048
|
-
const C = y > 0 ? f / y : 1;
|
|
2049
|
-
e({
|
|
2050
|
-
localNotOnRemote: c,
|
|
2051
|
-
localNotOnRemotePathOnly: r,
|
|
2052
|
-
remoteNotOnLocal: u,
|
|
2053
|
-
remoteNotOnLocalPathOnly: p,
|
|
2054
|
-
syncCount: m,
|
|
2055
|
-
totalCount: h,
|
|
2056
|
-
syncSize: f,
|
|
2057
|
-
totalSize: y,
|
|
2058
|
-
synctPctSize: C,
|
|
2059
|
-
synctPctCount: g
|
|
2060
|
-
});
|
|
2061
|
-
});
|
|
2062
|
-
}
|
|
2063
|
-
var o = /* @__PURE__ */ ((l) => (l.AUTOMATION = "automation", l.ARCHIVE = "archive", l.AUDIO = "audio", l.BACKUP = "backup", l.BINARY = "binary", l.BIM = "bim", l.CAD = "cad", l.DATA = "data", l.EMAIL = "email", l.FONT = "font", l.GEOSPATIAL = "geospatial", l.IMAGE = "image", l.INSTALLER = "installer", l.MARKUP = "markup", l.MULTIMEDIA = "multimedia", l.PLANNING = "planning", l.PRESENTATION = "presentation", l.SCRIPT = "script", l.SPREADSHEET = "spreadsheet", l.TEXT = "text", l.THREE_D = "3d", l.UNKNOWN = "unknown", l.VIDEO = "video", l))(o || {});
|
|
2064
|
-
const He = {
|
|
2065
|
-
".aac": {
|
|
2066
|
-
type: o.AUDIO,
|
|
2067
|
-
open: !0,
|
|
2068
|
-
suffix: ".aac",
|
|
2069
|
-
mime: "audio/aac"
|
|
2070
|
-
},
|
|
2071
|
-
".abw": {
|
|
2072
|
-
type: o.TEXT,
|
|
2073
|
-
open: !0,
|
|
2074
|
-
suffix: ".abw",
|
|
2075
|
-
mime: "application/x-abiword"
|
|
2076
|
-
},
|
|
2077
|
-
".arc": {
|
|
2078
|
-
type: o.ARCHIVE,
|
|
2079
|
-
open: !1,
|
|
2080
|
-
suffix: ".arc",
|
|
2081
|
-
mime: "application/x-freearc"
|
|
2082
|
-
},
|
|
2083
|
-
".avif": {
|
|
2084
|
-
type: o.IMAGE,
|
|
2085
|
-
open: !0,
|
|
2086
|
-
suffix: ".avif",
|
|
2087
|
-
mime: "image/avif"
|
|
2088
|
-
},
|
|
2089
|
-
".avi": {
|
|
2090
|
-
type: o.VIDEO,
|
|
2091
|
-
open: !0,
|
|
2092
|
-
suffix: ".avi",
|
|
2093
|
-
mime: "video/x-msvideo"
|
|
2094
|
-
},
|
|
2095
|
-
".apng": {
|
|
2096
|
-
type: o.IMAGE,
|
|
2097
|
-
open: !0,
|
|
2098
|
-
suffix: ".apng",
|
|
2099
|
-
mime: "image/apng"
|
|
2100
|
-
},
|
|
2101
|
-
".bak": {
|
|
2102
|
-
type: o.BACKUP,
|
|
2103
|
-
open: !1,
|
|
2104
|
-
suffix: ".bak",
|
|
2105
|
-
mime: "application/octet-stream"
|
|
2106
|
-
},
|
|
2107
|
-
".bin": {
|
|
2108
|
-
type: o.BINARY,
|
|
2109
|
-
open: !1,
|
|
2110
|
-
suffix: ".bin",
|
|
2111
|
-
mime: "application/octet-stream"
|
|
2112
|
-
},
|
|
2113
|
-
".bmc": {
|
|
2114
|
-
type: o.BIM,
|
|
2115
|
-
open: !1,
|
|
2116
|
-
suffix: ".bmc",
|
|
2117
|
-
mime: "application/octet-stream"
|
|
2118
|
-
},
|
|
2119
|
-
".bmp": {
|
|
2120
|
-
type: o.IMAGE,
|
|
2121
|
-
open: !0,
|
|
2122
|
-
suffix: ".bmp",
|
|
2123
|
-
mime: "image/bmp"
|
|
2124
|
-
},
|
|
2125
|
-
".bz": {
|
|
2126
|
-
type: o.ARCHIVE,
|
|
2127
|
-
open: !0,
|
|
2128
|
-
suffix: ".bz",
|
|
2129
|
-
mime: "application/x-bzip"
|
|
2130
|
-
},
|
|
2131
|
-
".bz2": {
|
|
2132
|
-
type: o.ARCHIVE,
|
|
2133
|
-
open: !0,
|
|
2134
|
-
suffix: ".bz2",
|
|
2135
|
-
mime: "application/x-bzip2"
|
|
2136
|
-
},
|
|
2137
|
-
".c3d": {
|
|
2138
|
-
type: o.THREE_D,
|
|
2139
|
-
open: !0,
|
|
2140
|
-
suffix: ".c3d",
|
|
2141
|
-
mime: "application/octet-stream"
|
|
2142
|
-
},
|
|
2143
|
-
".cpg": {
|
|
2144
|
-
type: o.GEOSPATIAL,
|
|
2145
|
-
open: !0,
|
|
2146
|
-
suffix: ".cpg",
|
|
2147
|
-
mime: "text/plain"
|
|
2148
|
-
},
|
|
2149
|
-
".css": {
|
|
2150
|
-
type: o.MARKUP,
|
|
2151
|
-
open: !0,
|
|
2152
|
-
suffix: ".css",
|
|
2153
|
-
mime: "text/css"
|
|
2154
|
-
},
|
|
2155
|
-
".csv": {
|
|
2156
|
-
type: o.DATA,
|
|
2157
|
-
open: !0,
|
|
2158
|
-
suffix: ".csv",
|
|
2159
|
-
mime: "text/csv"
|
|
2160
|
-
},
|
|
2161
|
-
".dat": {
|
|
2162
|
-
type: o.DATA,
|
|
2163
|
-
open: !1,
|
|
2164
|
-
suffix: ".dat",
|
|
2165
|
-
mime: "application/octet-stream"
|
|
2166
|
-
},
|
|
2167
|
-
".dbf": {
|
|
2168
|
-
type: o.DATA,
|
|
2169
|
-
open: !0,
|
|
2170
|
-
suffix: ".dbf",
|
|
2171
|
-
mime: "application/x-dbf"
|
|
2172
|
-
},
|
|
2173
|
-
".doc": {
|
|
2174
|
-
type: o.TEXT,
|
|
2175
|
-
open: !1,
|
|
2176
|
-
suffix: ".doc",
|
|
2177
|
-
mime: "application/msword"
|
|
2178
|
-
},
|
|
2179
|
-
".docx": {
|
|
2180
|
-
type: o.TEXT,
|
|
2181
|
-
open: !0,
|
|
2182
|
-
suffix: ".docx",
|
|
2183
|
-
mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
2184
|
-
},
|
|
2185
|
-
".e57": {
|
|
2186
|
-
type: o.THREE_D,
|
|
2187
|
-
open: !0,
|
|
2188
|
-
suffix: ".e57",
|
|
2189
|
-
mime: "application/octet-stream"
|
|
2190
|
-
},
|
|
2191
|
-
".epub": {
|
|
2192
|
-
type: o.TEXT,
|
|
2193
|
-
open: !0,
|
|
2194
|
-
suffix: ".epub",
|
|
2195
|
-
mime: "application/epub+zip"
|
|
2196
|
-
},
|
|
2197
|
-
".exe": {
|
|
2198
|
-
type: o.INSTALLER,
|
|
2199
|
-
open: !1,
|
|
2200
|
-
suffix: ".exe",
|
|
2201
|
-
mime: "application/x-msdownload"
|
|
2202
|
-
},
|
|
2203
|
-
".ffs": {
|
|
2204
|
-
type: o.AUTOMATION,
|
|
2205
|
-
open: !1,
|
|
2206
|
-
suffix: ".ffs",
|
|
2207
|
-
mime: "application/octet-stream"
|
|
2208
|
-
},
|
|
2209
|
-
".fme": {
|
|
2210
|
-
type: o.AUTOMATION,
|
|
2211
|
-
open: !1,
|
|
2212
|
-
suffix: ".fme",
|
|
2213
|
-
mime: "application/octet-stream"
|
|
2214
|
-
},
|
|
2215
|
-
".fmw": {
|
|
2216
|
-
type: o.AUTOMATION,
|
|
2217
|
-
open: !1,
|
|
2218
|
-
suffix: ".fmw",
|
|
2219
|
-
mime: "application/octet-stream"
|
|
2220
|
-
},
|
|
2221
|
-
".gh": {
|
|
2222
|
-
type: o.THREE_D,
|
|
2223
|
-
open: !1,
|
|
2224
|
-
suffix: ".gh",
|
|
2225
|
-
mime: "application/octet-stream"
|
|
2226
|
-
},
|
|
2227
|
-
".gz": {
|
|
2228
|
-
type: o.ARCHIVE,
|
|
2229
|
-
open: !0,
|
|
2230
|
-
suffix: ".gz",
|
|
2231
|
-
mime: "application/gzip"
|
|
2232
|
-
},
|
|
2233
|
-
".gif": {
|
|
2234
|
-
type: o.IMAGE,
|
|
2235
|
-
open: !0,
|
|
2236
|
-
suffix: ".gif",
|
|
2237
|
-
mime: "image/gif"
|
|
2238
|
-
},
|
|
2239
|
-
".html": {
|
|
2240
|
-
type: o.MARKUP,
|
|
2241
|
-
open: !0,
|
|
2242
|
-
suffix: ".html",
|
|
2243
|
-
mime: "text/html"
|
|
2244
|
-
},
|
|
2245
|
-
".htm": {
|
|
2246
|
-
type: o.MARKUP,
|
|
2247
|
-
open: !0,
|
|
2248
|
-
suffix: ".htm",
|
|
2249
|
-
mime: "text/html"
|
|
2250
|
-
},
|
|
2251
|
-
".ico": {
|
|
2252
|
-
type: o.IMAGE,
|
|
2253
|
-
open: !1,
|
|
2254
|
-
suffix: ".ico",
|
|
2255
|
-
mime: "image/x-icon"
|
|
2256
|
-
},
|
|
2257
|
-
".ics": {
|
|
2258
|
-
type: o.DATA,
|
|
2259
|
-
open: !0,
|
|
2260
|
-
suffix: ".ics",
|
|
2261
|
-
mime: "text/calendar"
|
|
2262
|
-
},
|
|
2263
|
-
".jar": {
|
|
2264
|
-
type: o.ARCHIVE,
|
|
2265
|
-
open: !0,
|
|
2266
|
-
suffix: ".jar",
|
|
2267
|
-
mime: "application/java-archive"
|
|
2268
|
-
},
|
|
2269
|
-
".jpg": {
|
|
2270
|
-
type: o.IMAGE,
|
|
2271
|
-
open: !0,
|
|
2272
|
-
suffix: ".jpg",
|
|
2273
|
-
mime: "image/jpeg"
|
|
2274
|
-
},
|
|
2275
|
-
".jpeg": {
|
|
2276
|
-
type: o.IMAGE,
|
|
2277
|
-
open: !0,
|
|
2278
|
-
suffix: ".jpeg",
|
|
2279
|
-
mime: "image/jpeg"
|
|
2280
|
-
},
|
|
2281
|
-
".json": {
|
|
2282
|
-
type: o.DATA,
|
|
2283
|
-
open: !0,
|
|
2284
|
-
suffix: ".json",
|
|
2285
|
-
mime: "application/json"
|
|
2286
|
-
},
|
|
2287
|
-
".jsonld": {
|
|
2288
|
-
type: o.DATA,
|
|
2289
|
-
open: !0,
|
|
2290
|
-
suffix: ".jsonld",
|
|
2291
|
-
mime: "application/ld+json"
|
|
2292
|
-
},
|
|
2293
|
-
".js": {
|
|
2294
|
-
type: o.SCRIPT,
|
|
2295
|
-
open: !0,
|
|
2296
|
-
suffix: ".js",
|
|
2297
|
-
mime: "application/javascript"
|
|
2298
|
-
},
|
|
2299
|
-
".msi": {
|
|
2300
|
-
type: o.INSTALLER,
|
|
2301
|
-
open: !1,
|
|
2302
|
-
suffix: ".msi",
|
|
2303
|
-
mime: "application/x-msdownload"
|
|
2304
|
-
},
|
|
2305
|
-
".mov": {
|
|
2306
|
-
type: o.VIDEO,
|
|
2307
|
-
open: !0,
|
|
2308
|
-
suffix: ".mov",
|
|
2309
|
-
mime: "video/quicktime"
|
|
2310
|
-
},
|
|
2311
|
-
".mp3": {
|
|
2312
|
-
type: o.AUDIO,
|
|
2313
|
-
open: !0,
|
|
2314
|
-
suffix: ".mp3",
|
|
2315
|
-
mime: "audio/mpeg"
|
|
2316
|
-
},
|
|
2317
|
-
".mp4": {
|
|
2318
|
-
type: o.VIDEO,
|
|
2319
|
-
open: !0,
|
|
2320
|
-
suffix: ".mp4",
|
|
2321
|
-
mime: "video/mp4"
|
|
2322
|
-
},
|
|
2323
|
-
".mpeg": {
|
|
2324
|
-
type: o.VIDEO,
|
|
2325
|
-
open: !0,
|
|
2326
|
-
suffix: ".mpeg",
|
|
2327
|
-
mime: "video/mpeg"
|
|
2328
|
-
},
|
|
2329
|
-
".mtl": {
|
|
2330
|
-
type: o.THREE_D,
|
|
2331
|
-
open: !0,
|
|
2332
|
-
suffix: ".mtl",
|
|
2333
|
-
mime: "text/plain"
|
|
2334
|
-
},
|
|
2335
|
-
".odp": {
|
|
2336
|
-
type: o.PRESENTATION,
|
|
2337
|
-
open: !0,
|
|
2338
|
-
suffix: ".odp",
|
|
2339
|
-
mime: "application/vnd.oasis.opendocument.presentation"
|
|
2340
|
-
},
|
|
2341
|
-
".ods": {
|
|
2342
|
-
type: o.SPREADSHEET,
|
|
2343
|
-
open: !0,
|
|
2344
|
-
suffix: ".ods",
|
|
2345
|
-
mime: "application/vnd.oasis.opendocument.spreadsheet"
|
|
2346
|
-
},
|
|
2347
|
-
".odt": {
|
|
2348
|
-
type: o.TEXT,
|
|
2349
|
-
open: !0,
|
|
2350
|
-
suffix: ".odt",
|
|
2351
|
-
mime: "application/vnd.oasis.opendocument.text"
|
|
2352
|
-
},
|
|
2353
|
-
".oga": {
|
|
2354
|
-
type: o.AUDIO,
|
|
2355
|
-
open: !0,
|
|
2356
|
-
suffix: ".oga",
|
|
2357
|
-
mime: "audio/ogg"
|
|
2358
|
-
},
|
|
2359
|
-
".ogg": {
|
|
2360
|
-
type: o.AUDIO,
|
|
2361
|
-
open: !0,
|
|
2362
|
-
suffix: ".ogg",
|
|
2363
|
-
mime: "audio/ogg"
|
|
2364
|
-
},
|
|
2365
|
-
".ogv": {
|
|
2366
|
-
type: o.VIDEO,
|
|
2367
|
-
open: !0,
|
|
2368
|
-
suffix: ".ogv",
|
|
2369
|
-
mime: "video/ogg"
|
|
2370
|
-
},
|
|
2371
|
-
".ogx": {
|
|
2372
|
-
type: o.VIDEO,
|
|
2373
|
-
open: !0,
|
|
2374
|
-
suffix: ".ogx",
|
|
2375
|
-
mime: "application/octet-stream"
|
|
2376
|
-
},
|
|
2377
|
-
".otf": {
|
|
2378
|
-
type: o.FONT,
|
|
2379
|
-
open: !0,
|
|
2380
|
-
suffix: ".otf",
|
|
2381
|
-
mime: "font/otf"
|
|
2382
|
-
},
|
|
2383
|
-
".png": {
|
|
2384
|
-
type: o.IMAGE,
|
|
2385
|
-
open: !0,
|
|
2386
|
-
suffix: ".png",
|
|
2387
|
-
mime: "image/png"
|
|
2388
|
-
},
|
|
2389
|
-
".pdf": {
|
|
2390
|
-
type: o.TEXT,
|
|
2391
|
-
open: !0,
|
|
2392
|
-
suffix: ".pdf",
|
|
2393
|
-
mime: "application/pdf"
|
|
2394
|
-
},
|
|
2395
|
-
".php": {
|
|
2396
|
-
type: o.SCRIPT,
|
|
2397
|
-
open: !0,
|
|
2398
|
-
suffix: ".php",
|
|
2399
|
-
mime: "application/x-httpd-php"
|
|
2400
|
-
},
|
|
2401
|
-
".pkg": {
|
|
2402
|
-
type: o.INSTALLER,
|
|
2403
|
-
open: !1,
|
|
2404
|
-
suffix: ".pkg",
|
|
2405
|
-
mime: "application/octet-stream"
|
|
2406
|
-
},
|
|
2407
|
-
".ppt": {
|
|
2408
|
-
type: o.PRESENTATION,
|
|
2409
|
-
open: !1,
|
|
2410
|
-
suffix: ".ppt",
|
|
2411
|
-
mime: "application/vnd.ms-powerpoint"
|
|
2412
|
-
},
|
|
2413
|
-
".pptx": {
|
|
2414
|
-
type: o.PRESENTATION,
|
|
2415
|
-
open: !0,
|
|
2416
|
-
suffix: ".pptx",
|
|
2417
|
-
mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
2418
|
-
},
|
|
2419
|
-
".ps": {
|
|
2420
|
-
type: o.IMAGE,
|
|
2421
|
-
open: !1,
|
|
2422
|
-
suffix: ".ps",
|
|
2423
|
-
mime: "application/postscript"
|
|
2424
|
-
},
|
|
2425
|
-
".rar": {
|
|
2426
|
-
type: o.ARCHIVE,
|
|
2427
|
-
open: !1,
|
|
2428
|
-
suffix: ".rar",
|
|
2429
|
-
mime: "application/vnd.rar"
|
|
2430
|
-
},
|
|
2431
|
-
".rcp": {
|
|
2432
|
-
type: o.THREE_D,
|
|
2433
|
-
open: !1,
|
|
2434
|
-
suffix: ".rcp",
|
|
2435
|
-
mime: "application/octet-stream"
|
|
2436
|
-
},
|
|
2437
|
-
".rtf": {
|
|
2438
|
-
type: o.TEXT,
|
|
2439
|
-
open: !0,
|
|
2440
|
-
suffix: ".rtf",
|
|
2441
|
-
mime: "application/rtf"
|
|
2442
|
-
},
|
|
2443
|
-
".sbn": {
|
|
2444
|
-
type: o.GEOSPATIAL,
|
|
2445
|
-
open: !1,
|
|
2446
|
-
suffix: ".sbn",
|
|
2447
|
-
mime: "application/octet-stream"
|
|
2448
|
-
},
|
|
2449
|
-
".sbx": {
|
|
2450
|
-
type: o.GEOSPATIAL,
|
|
2451
|
-
open: !1,
|
|
2452
|
-
suffix: ".sbx",
|
|
2453
|
-
mime: "application/octet-stream"
|
|
2454
|
-
},
|
|
2455
|
-
".shx": {
|
|
2456
|
-
type: o.GEOSPATIAL,
|
|
2457
|
-
open: !0,
|
|
2458
|
-
suffix: ".shx",
|
|
2459
|
-
mime: "application/octet-stream"
|
|
2460
|
-
},
|
|
2461
|
-
".sh": {
|
|
2462
|
-
type: o.SCRIPT,
|
|
2463
|
-
open: !0,
|
|
2464
|
-
suffix: ".sh",
|
|
2465
|
-
mime: "application/x-sh"
|
|
2466
|
-
},
|
|
2467
|
-
".slk": {
|
|
2468
|
-
type: o.DATA,
|
|
2469
|
-
open: !0,
|
|
2470
|
-
suffix: ".slk",
|
|
2471
|
-
mime: "application/vnd.ms-excel"
|
|
2472
|
-
},
|
|
2473
|
-
".svg": {
|
|
2474
|
-
type: o.IMAGE,
|
|
2475
|
-
open: !0,
|
|
2476
|
-
suffix: ".svg",
|
|
2477
|
-
mime: "image/svg+xml"
|
|
2478
|
-
},
|
|
2479
|
-
".tar": {
|
|
2480
|
-
type: o.ARCHIVE,
|
|
2481
|
-
open: !0,
|
|
2482
|
-
suffix: ".tar",
|
|
2483
|
-
mime: "application/x-tar"
|
|
2484
|
-
},
|
|
2485
|
-
".tiff": {
|
|
2486
|
-
type: o.IMAGE,
|
|
2487
|
-
open: !0,
|
|
2488
|
-
suffix: ".tiff",
|
|
2489
|
-
mime: "image/tiff"
|
|
2490
|
-
},
|
|
2491
|
-
".tif": {
|
|
2492
|
-
type: o.IMAGE,
|
|
2493
|
-
open: !0,
|
|
2494
|
-
suffix: ".tif",
|
|
2495
|
-
mime: "image/tiff"
|
|
2496
|
-
},
|
|
2497
|
-
".ttf": {
|
|
2498
|
-
type: o.FONT,
|
|
2499
|
-
open: !0,
|
|
2500
|
-
suffix: ".ttf",
|
|
2501
|
-
mime: "font/ttf"
|
|
2502
|
-
},
|
|
2503
|
-
".txt": {
|
|
2504
|
-
type: o.TEXT,
|
|
2505
|
-
open: !0,
|
|
2506
|
-
suffix: ".txt",
|
|
2507
|
-
mime: "text/plain"
|
|
2508
|
-
},
|
|
2509
|
-
".vgl": {
|
|
2510
|
-
type: o.THREE_D,
|
|
2511
|
-
open: !1,
|
|
2512
|
-
suffix: ".vgl",
|
|
2513
|
-
mime: "application/octet-stream"
|
|
2514
|
-
},
|
|
2515
|
-
".vwx": {
|
|
2516
|
-
type: o.CAD,
|
|
2517
|
-
open: !1,
|
|
2518
|
-
suffix: ".vwx",
|
|
2519
|
-
mime: "application/octet-stream"
|
|
2520
|
-
},
|
|
2521
|
-
".wav": {
|
|
2522
|
-
type: o.AUDIO,
|
|
2523
|
-
open: !0,
|
|
2524
|
-
suffix: ".wav",
|
|
2525
|
-
mime: "audio/wav"
|
|
2526
|
-
},
|
|
2527
|
-
".webm": {
|
|
2528
|
-
type: o.VIDEO,
|
|
2529
|
-
open: !0,
|
|
2530
|
-
suffix: ".webm",
|
|
2531
|
-
mime: "video/webm"
|
|
2532
|
-
},
|
|
2533
|
-
".webp": {
|
|
2534
|
-
type: o.IMAGE,
|
|
2535
|
-
open: !0,
|
|
2536
|
-
suffix: ".webp",
|
|
2537
|
-
mime: "image/webp"
|
|
2538
|
-
},
|
|
2539
|
-
".wmv": {
|
|
2540
|
-
type: o.VIDEO,
|
|
2541
|
-
open: !0,
|
|
2542
|
-
suffix: ".wmv",
|
|
2543
|
-
mime: "video/x-ms-wmv"
|
|
2544
|
-
},
|
|
2545
|
-
".woff": {
|
|
2546
|
-
type: o.FONT,
|
|
2547
|
-
open: !0,
|
|
2548
|
-
suffix: ".woff",
|
|
2549
|
-
mime: "font/woff"
|
|
2550
|
-
},
|
|
2551
|
-
".woff2": {
|
|
2552
|
-
type: o.FONT,
|
|
2553
|
-
open: !0,
|
|
2554
|
-
suffix: ".woff2",
|
|
2555
|
-
mime: "font/woff2"
|
|
2556
|
-
},
|
|
2557
|
-
".xls": {
|
|
2558
|
-
type: o.SPREADSHEET,
|
|
2559
|
-
open: !1,
|
|
2560
|
-
suffix: ".xls",
|
|
2561
|
-
mime: "application/vnd.ms-excel"
|
|
2562
|
-
},
|
|
2563
|
-
".xlsx": {
|
|
2564
|
-
type: o.SPREADSHEET,
|
|
2565
|
-
open: !0,
|
|
2566
|
-
suffix: ".xlsx",
|
|
2567
|
-
mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
2568
|
-
},
|
|
2569
|
-
".xhtml": {
|
|
2570
|
-
type: o.MARKUP,
|
|
2571
|
-
open: !0,
|
|
2572
|
-
suffix: ".xhtml",
|
|
2573
|
-
mime: "application/xhtml+xml"
|
|
2574
|
-
},
|
|
2575
|
-
".xml": {
|
|
2576
|
-
type: o.DATA,
|
|
2577
|
-
open: !0,
|
|
2578
|
-
suffix: ".xml",
|
|
2579
|
-
mime: "application/xml"
|
|
2580
|
-
},
|
|
2581
|
-
".xyz": {
|
|
2582
|
-
type: o.THREE_D,
|
|
2583
|
-
open: !0,
|
|
2584
|
-
suffix: ".xyz",
|
|
2585
|
-
mime: "chemical/x-xyz"
|
|
2586
|
-
},
|
|
2587
|
-
".zip": {
|
|
2588
|
-
type: o.ARCHIVE,
|
|
2589
|
-
open: !0,
|
|
2590
|
-
suffix: ".zip",
|
|
2591
|
-
mime: "application/zip"
|
|
2592
|
-
},
|
|
2593
|
-
".7z": {
|
|
2594
|
-
type: o.ARCHIVE,
|
|
2595
|
-
open: !0,
|
|
2596
|
-
suffix: ".7z",
|
|
2597
|
-
mime: "application/x-7z-compressed"
|
|
2598
|
-
},
|
|
2599
|
-
".eml": {
|
|
2600
|
-
type: o.EMAIL,
|
|
2601
|
-
open: !0,
|
|
2602
|
-
suffix: ".eml",
|
|
2603
|
-
mime: "message/rfc822"
|
|
2604
|
-
},
|
|
2605
|
-
".emlx": {
|
|
2606
|
-
type: o.EMAIL,
|
|
2607
|
-
open: !0,
|
|
2608
|
-
suffix: ".emlx",
|
|
2609
|
-
mime: "application/octet-stream"
|
|
2610
|
-
},
|
|
2611
|
-
".mbx": {
|
|
2612
|
-
type: o.EMAIL,
|
|
2613
|
-
open: !0,
|
|
2614
|
-
suffix: ".mbx",
|
|
2615
|
-
mime: "application/mbox"
|
|
2616
|
-
},
|
|
2617
|
-
".md": {
|
|
2618
|
-
type: o.TEXT,
|
|
2619
|
-
open: !0,
|
|
2620
|
-
suffix: ".md",
|
|
2621
|
-
mime: "text/markdown"
|
|
2622
|
-
},
|
|
2623
|
-
".msg": {
|
|
2624
|
-
type: o.EMAIL,
|
|
2625
|
-
open: !1,
|
|
2626
|
-
suffix: ".msg",
|
|
2627
|
-
mime: "application/vnd.ms-outlook"
|
|
2628
|
-
},
|
|
2629
|
-
".rst": {
|
|
2630
|
-
type: o.TEXT,
|
|
2631
|
-
open: !0,
|
|
2632
|
-
suffix: ".rst",
|
|
2633
|
-
mime: "text/x-rst"
|
|
2634
|
-
},
|
|
2635
|
-
".heic": {
|
|
2636
|
-
type: o.IMAGE,
|
|
2637
|
-
open: !1,
|
|
2638
|
-
suffix: ".heic",
|
|
2639
|
-
mime: "image/heic"
|
|
2640
|
-
},
|
|
2641
|
-
".mpp": {
|
|
2642
|
-
type: o.PLANNING,
|
|
2643
|
-
open: !1,
|
|
2644
|
-
suffix: ".mpp",
|
|
2645
|
-
mime: "application/vnd.ms-project"
|
|
2646
|
-
},
|
|
2647
|
-
".tsv": {
|
|
2648
|
-
type: o.DATA,
|
|
2649
|
-
open: !0,
|
|
2650
|
-
suffix: ".tsv",
|
|
2651
|
-
mime: "text/tab-separated-values"
|
|
2652
|
-
},
|
|
2653
|
-
".b3dm": {
|
|
2654
|
-
type: o.THREE_D,
|
|
2655
|
-
open: !0,
|
|
2656
|
-
suffix: ".b3dm",
|
|
2657
|
-
mime: "application/octet-stream"
|
|
2658
|
-
},
|
|
2659
|
-
".i3dm": {
|
|
2660
|
-
type: o.THREE_D,
|
|
2661
|
-
open: !0,
|
|
2662
|
-
suffix: ".i3dm",
|
|
2663
|
-
mime: "application/octet-stream"
|
|
2664
|
-
},
|
|
2665
|
-
".pnts": {
|
|
2666
|
-
type: o.THREE_D,
|
|
2667
|
-
open: !1,
|
|
2668
|
-
suffix: ".pnts",
|
|
2669
|
-
mime: "application/octet-stream"
|
|
2670
|
-
},
|
|
2671
|
-
".cmpt": {
|
|
2672
|
-
type: o.THREE_D,
|
|
2673
|
-
open: !1,
|
|
2674
|
-
suffix: ".cmpt",
|
|
2675
|
-
mime: "application/octet-stream"
|
|
2676
|
-
},
|
|
2677
|
-
".gltf": {
|
|
2678
|
-
type: o.THREE_D,
|
|
2679
|
-
open: !0,
|
|
2680
|
-
suffix: ".gltf",
|
|
2681
|
-
mime: "model/gltf+json"
|
|
2682
|
-
},
|
|
2683
|
-
".glb": {
|
|
2684
|
-
type: o.THREE_D,
|
|
2685
|
-
open: !0,
|
|
2686
|
-
suffix: ".glb",
|
|
2687
|
-
mime: "model/gltf-binary"
|
|
2688
|
-
},
|
|
2689
|
-
".ifc": {
|
|
2690
|
-
type: o.BIM,
|
|
2691
|
-
open: !0,
|
|
2692
|
-
suffix: ".ifc",
|
|
2693
|
-
mime: "application/x-step"
|
|
2694
|
-
},
|
|
2695
|
-
".ifczip": {
|
|
2696
|
-
type: o.BIM,
|
|
2697
|
-
open: !0,
|
|
2698
|
-
suffix: ".ifczip",
|
|
2699
|
-
mime: "application/zip"
|
|
2700
|
-
},
|
|
2701
|
-
".ifcxml": {
|
|
2702
|
-
type: o.BIM,
|
|
2703
|
-
open: !0,
|
|
2704
|
-
suffix: ".ifcxml",
|
|
2705
|
-
mime: "application/xml"
|
|
2706
|
-
},
|
|
2707
|
-
".3dm": {
|
|
2708
|
-
type: o.CAD,
|
|
2709
|
-
open: !1,
|
|
2710
|
-
suffix: ".3dm",
|
|
2711
|
-
mime: "application/vnd.x-rhino3dm"
|
|
2712
|
-
},
|
|
2713
|
-
".3ds": {
|
|
2714
|
-
type: o.THREE_D,
|
|
2715
|
-
open: !1,
|
|
2716
|
-
suffix: ".3ds",
|
|
2717
|
-
mime: "image/x-3ds"
|
|
2718
|
-
},
|
|
2719
|
-
".ghp": {
|
|
2720
|
-
type: o.CAD,
|
|
2721
|
-
open: !1,
|
|
2722
|
-
suffix: ".ghp",
|
|
2723
|
-
mime: "application/octet-stream"
|
|
2724
|
-
},
|
|
2725
|
-
".rvt": {
|
|
2726
|
-
type: o.BIM,
|
|
2727
|
-
open: !1,
|
|
2728
|
-
suffix: ".rvt",
|
|
2729
|
-
mime: "application/octet-stream"
|
|
2730
|
-
},
|
|
2731
|
-
".rfa": {
|
|
2732
|
-
type: o.BIM,
|
|
2733
|
-
open: !1,
|
|
2734
|
-
suffix: ".rfa",
|
|
2735
|
-
mime: "application/octet-stream"
|
|
2736
|
-
},
|
|
2737
|
-
".rft": {
|
|
2738
|
-
type: o.BIM,
|
|
2739
|
-
open: !1,
|
|
2740
|
-
suffix: ".rft",
|
|
2741
|
-
mime: "application/octet-stream"
|
|
2742
|
-
},
|
|
2743
|
-
".rte": {
|
|
2744
|
-
type: o.BIM,
|
|
2745
|
-
open: !1,
|
|
2746
|
-
suffix: ".rte",
|
|
2747
|
-
mime: "application/octet-stream"
|
|
2748
|
-
},
|
|
2749
|
-
".pla": {
|
|
2750
|
-
type: o.BIM,
|
|
2751
|
-
open: !1,
|
|
2752
|
-
suffix: ".pla",
|
|
2753
|
-
mime: "application/octet-stream"
|
|
2754
|
-
},
|
|
2755
|
-
".pln": {
|
|
2756
|
-
type: o.BIM,
|
|
2757
|
-
open: !1,
|
|
2758
|
-
suffix: ".pln",
|
|
2759
|
-
mime: "application/octet-stream"
|
|
2760
|
-
},
|
|
2761
|
-
".gsm": {
|
|
2762
|
-
type: o.BIM,
|
|
2763
|
-
open: !1,
|
|
2764
|
-
suffix: ".gsm",
|
|
2765
|
-
mime: "application/octet-stream"
|
|
2766
|
-
},
|
|
2767
|
-
".smc": {
|
|
2768
|
-
type: o.BIM,
|
|
2769
|
-
open: !1,
|
|
2770
|
-
suffix: ".smc",
|
|
2771
|
-
mime: "application/octet-stream"
|
|
2772
|
-
},
|
|
2773
|
-
".bcf": {
|
|
2774
|
-
type: o.BIM,
|
|
2775
|
-
open: !0,
|
|
2776
|
-
suffix: ".bcf",
|
|
2777
|
-
mime: "application/octet-stream"
|
|
2778
|
-
},
|
|
2779
|
-
".dwg": {
|
|
2780
|
-
type: o.CAD,
|
|
2781
|
-
open: !1,
|
|
2782
|
-
suffix: ".dwg",
|
|
2783
|
-
mime: "application/acad"
|
|
2784
|
-
},
|
|
2785
|
-
".dwl": {
|
|
2786
|
-
type: o.CAD,
|
|
2787
|
-
open: !1,
|
|
2788
|
-
suffix: ".dwl",
|
|
2789
|
-
mime: "application/octet-stream"
|
|
2790
|
-
},
|
|
2791
|
-
".dxf": {
|
|
2792
|
-
type: o.CAD,
|
|
2793
|
-
open: !0,
|
|
2794
|
-
suffix: ".dxf",
|
|
2795
|
-
mime: "image/vnd.dxf"
|
|
2796
|
-
},
|
|
2797
|
-
".dgn": {
|
|
2798
|
-
type: o.CAD,
|
|
2799
|
-
open: !1,
|
|
2800
|
-
suffix: ".dgn",
|
|
2801
|
-
mime: "application/octet-stream"
|
|
2802
|
-
},
|
|
2803
|
-
".nwd": {
|
|
2804
|
-
type: o.BIM,
|
|
2805
|
-
open: !1,
|
|
2806
|
-
suffix: ".nwd",
|
|
2807
|
-
mime: "application/octet-stream"
|
|
2808
|
-
},
|
|
2809
|
-
".obj": {
|
|
2810
|
-
type: o.THREE_D,
|
|
2811
|
-
open: !0,
|
|
2812
|
-
suffix: ".obj",
|
|
2813
|
-
mime: "text/plain"
|
|
2814
|
-
},
|
|
2815
|
-
".c4d": {
|
|
2816
|
-
type: o.THREE_D,
|
|
2817
|
-
open: !1,
|
|
2818
|
-
suffix: ".c4d",
|
|
2819
|
-
mime: "application/octet-stream"
|
|
2820
|
-
},
|
|
2821
|
-
".teklastructures": {
|
|
2822
|
-
type: o.BIM,
|
|
2823
|
-
open: !1,
|
|
2824
|
-
suffix: ".teklastructures",
|
|
2825
|
-
mime: "application/octet-stream"
|
|
2826
|
-
},
|
|
2827
|
-
".pts": {
|
|
2828
|
-
type: o.THREE_D,
|
|
2829
|
-
open: !1,
|
|
2830
|
-
suffix: ".pts",
|
|
2831
|
-
mime: "application/octet-stream"
|
|
2832
|
-
},
|
|
2833
|
-
".las": {
|
|
2834
|
-
type: o.THREE_D,
|
|
2835
|
-
open: !0,
|
|
2836
|
-
suffix: ".las",
|
|
2837
|
-
mime: "application/octet-stream"
|
|
2838
|
-
},
|
|
2839
|
-
".laz": {
|
|
2840
|
-
type: o.THREE_D,
|
|
2841
|
-
open: !0,
|
|
2842
|
-
suffix: ".laz",
|
|
2843
|
-
mime: "application/octet-stream"
|
|
2844
|
-
},
|
|
2845
|
-
".usdz": {
|
|
2846
|
-
type: o.THREE_D,
|
|
2847
|
-
open: !0,
|
|
2848
|
-
suffix: ".usdz",
|
|
2849
|
-
mime: "model/vnd.usdz+zip"
|
|
2850
|
-
},
|
|
2851
|
-
".usda": {
|
|
2852
|
-
type: o.THREE_D,
|
|
2853
|
-
open: !0,
|
|
2854
|
-
suffix: ".usda",
|
|
2855
|
-
mime: "application/octet-stream"
|
|
2856
|
-
},
|
|
2857
|
-
".usdc": {
|
|
2858
|
-
type: o.THREE_D,
|
|
2859
|
-
open: !0,
|
|
2860
|
-
suffix: ".usdc",
|
|
2861
|
-
mime: "application/octet-stream"
|
|
2862
|
-
},
|
|
2863
|
-
".usd": {
|
|
2864
|
-
type: o.THREE_D,
|
|
2865
|
-
open: !0,
|
|
2866
|
-
suffix: ".usd",
|
|
2867
|
-
mime: "application/octet-stream"
|
|
2868
|
-
},
|
|
2869
|
-
".psd": {
|
|
2870
|
-
type: o.IMAGE,
|
|
2871
|
-
open: !1,
|
|
2872
|
-
suffix: ".psd",
|
|
2873
|
-
mime: "image/vnd.adobe.photoshop"
|
|
2874
|
-
},
|
|
2875
|
-
".psp": {
|
|
2876
|
-
type: o.IMAGE,
|
|
2877
|
-
open: !1,
|
|
2878
|
-
suffix: ".psp",
|
|
2879
|
-
mime: "image/x-paintshoppro"
|
|
2880
|
-
},
|
|
2881
|
-
".ai": {
|
|
2882
|
-
type: o.IMAGE,
|
|
2883
|
-
open: !1,
|
|
2884
|
-
suffix: ".ai",
|
|
2885
|
-
mime: "application/postscript"
|
|
2886
|
-
},
|
|
2887
|
-
".skp": {
|
|
2888
|
-
type: o.THREE_D,
|
|
2889
|
-
open: !1,
|
|
2890
|
-
suffix: ".skp",
|
|
2891
|
-
mime: "application/vnd.sketchup.skp"
|
|
2892
|
-
},
|
|
2893
|
-
".stl": {
|
|
2894
|
-
type: o.THREE_D,
|
|
2895
|
-
open: !0,
|
|
2896
|
-
suffix: ".stl",
|
|
2897
|
-
mime: "model/stl"
|
|
2898
|
-
},
|
|
2899
|
-
".fbx": {
|
|
2900
|
-
type: o.THREE_D,
|
|
2901
|
-
open: !0,
|
|
2902
|
-
suffix: ".fbx",
|
|
2903
|
-
mime: "application/octet-stream"
|
|
2904
|
-
},
|
|
2905
|
-
".kml": {
|
|
2906
|
-
type: o.GEOSPATIAL,
|
|
2907
|
-
open: !0,
|
|
2908
|
-
suffix: ".kml",
|
|
2909
|
-
mime: "application/vnd.google-earth.kml+xml"
|
|
2910
|
-
},
|
|
2911
|
-
".shp": {
|
|
2912
|
-
type: o.GEOSPATIAL,
|
|
2913
|
-
open: !0,
|
|
2914
|
-
suffix: ".shp",
|
|
2915
|
-
mime: "application/octet-stream"
|
|
2916
|
-
},
|
|
2917
|
-
".fragments": {
|
|
2918
|
-
type: o.BIM,
|
|
2919
|
-
open: !0,
|
|
2920
|
-
suffix: ".fragments",
|
|
2921
|
-
mime: "application/octet-stream"
|
|
2922
|
-
}
|
|
2923
|
-
};
|
|
2924
|
-
o.AUDIO + "", o.VIDEO + "", o.IMAGE + "", o.TEXT + "", o.MARKUP + "", o.SCRIPT + "", o.DATA + "", o.ARCHIVE + "", o.INSTALLER + "", o.BINARY + "", o.BACKUP + "", o.AUTOMATION + "", o.PRESENTATION + "", o.SPREADSHEET + "", o.FONT + "", o.GEOSPATIAL + "", o.THREE_D + "", o.CAD + "", o.BIM + "", o.PLANNING + "", o.EMAIL + "", o.MULTIMEDIA + "", o.UNKNOWN + "";
|
|
2925
|
-
const { namedNode: ze, literal: rs } = U;
|
|
2926
|
-
ze("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
|
|
2927
|
-
const { namedNode: cs } = U, { namedNode: Ge, literal: ls } = U;
|
|
2928
|
-
Ge("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
|
|
2929
|
-
function We(l) {
|
|
2930
|
-
const t = l.split(".");
|
|
2931
|
-
return t.length > 1 ? "." + t.pop()?.toLowerCase() : "";
|
|
2932
|
-
}
|
|
2933
|
-
const { namedNode: Ke, literal: us } = U;
|
|
2934
|
-
Ke("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
|
|
2935
|
-
const { namedNode: Qe, literal: ps } = U;
|
|
2936
|
-
Qe("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
|
|
2937
|
-
function Tt(l, t, e, s, i, n, a, c = !1, r = !1) {
|
|
2938
|
-
const u = We(l), p = b(s), m = `${t}/${p}${u}`;
|
|
2939
|
-
a === void 0 && (a = He[u]?.mime ?? "application/octet-stream");
|
|
2940
|
-
const h = Ie(l, i);
|
|
2941
|
-
return {
|
|
2942
|
-
name: l,
|
|
2943
|
-
blob_name: m,
|
|
2944
|
-
uploaded_by: e,
|
|
2945
|
-
suffix: u,
|
|
2946
|
-
md5_hash: s,
|
|
2947
|
-
file_id: p,
|
|
2948
|
-
skip_loading: c ? "True" : "False",
|
|
2949
|
-
skip_processing: r ? "True" : "False",
|
|
2950
|
-
space_id: t,
|
|
2951
|
-
provider_id: i ?? "",
|
|
2952
|
-
mime: a,
|
|
2953
|
-
additional_metadata: "",
|
|
2954
|
-
last_seen_as: JSON.stringify({
|
|
2955
|
-
hostId: i ?? "",
|
|
2956
|
-
fileUUID: h
|
|
2957
|
-
})
|
|
2958
|
-
};
|
|
2959
|
-
}
|
|
2960
|
-
async function O() {
|
|
2961
|
-
return import(
|
|
2962
|
-
/* webpackIgnore: true */
|
|
2963
|
-
"fs/promises"
|
|
2964
|
-
);
|
|
2965
|
-
}
|
|
2966
|
-
async function At(l) {
|
|
2967
|
-
if (typeof window < "u")
|
|
2968
|
-
throw new Error(
|
|
2969
|
-
`Cannot read file from path "${l}" in a browser environment. Provide file.data (Uint8Array) instead.`
|
|
2970
|
-
);
|
|
2971
|
-
const { readFile: t } = await O();
|
|
2972
|
-
return t(l);
|
|
2973
|
-
}
|
|
2974
|
-
let D = null, q = null, N = null;
|
|
2975
|
-
function hs(l) {
|
|
2976
|
-
N = l, q = null, D = null;
|
|
2977
|
-
}
|
|
2978
|
-
async function Ve() {
|
|
2979
|
-
if (typeof window < "u") {
|
|
2980
|
-
if (!N)
|
|
2981
|
-
throw new Error(
|
|
2982
|
-
"WASM scanner is not configured for browser use. Call configureScanWasm(baseUrl) during app initialisation."
|
|
2983
|
-
);
|
|
2984
|
-
const l = await fetch(`${N}/dir_scanner_wasm_bg.wasm`);
|
|
2985
|
-
if (!l.ok)
|
|
2986
|
-
throw new Error(`Failed to fetch WASM binary: ${l.status} ${l.statusText}`);
|
|
2987
|
-
const t = new Uint8Array(await l.arrayBuffer()), s = await import(`${N}/dir_scanner_wasm.mjs`);
|
|
2988
|
-
await s.default({ module_or_path: t }), D = s.scan;
|
|
2989
|
-
} else {
|
|
2990
|
-
const { readFile: l } = await O(), { join: t } = await import(
|
|
2991
|
-
/* webpackIgnore: true */
|
|
2992
|
-
"path"
|
|
2993
|
-
), { pathToFileURL: e } = await import(
|
|
2994
|
-
/* webpackIgnore: true */
|
|
2995
|
-
"url"
|
|
2996
|
-
), s = t(__dirname, "assets", "wasm"), i = await l(t(s, "dir_scanner_wasm_bg.wasm")), a = await import(e(t(s, "dir_scanner_wasm.js")).href);
|
|
2997
|
-
await a.default({ module_or_path: i }), D = a.scan;
|
|
2998
|
-
}
|
|
2999
|
-
}
|
|
3000
|
-
const Xe = "qlever", L = 1e3, Z = "cue:pending:";
|
|
3001
|
-
async function tt(l) {
|
|
3002
|
-
const { tmpdir: t } = await import(
|
|
3003
|
-
/* webpackIgnore: true */
|
|
3004
|
-
"os"
|
|
3005
|
-
), { join: e } = await import(
|
|
3006
|
-
/* webpackIgnore: true */
|
|
3007
|
-
"path"
|
|
3008
|
-
);
|
|
3009
|
-
return e(t(), `cue-sync-pending-${l}.json`);
|
|
3010
|
-
}
|
|
3011
|
-
async function St(l) {
|
|
3012
|
-
if (typeof window < "u") {
|
|
3013
|
-
const t = window.localStorage.getItem(`${Z}${l}`);
|
|
3014
|
-
return t ? JSON.parse(t) : null;
|
|
3015
|
-
}
|
|
3016
|
-
try {
|
|
3017
|
-
const t = await (await O()).readFile(await tt(l), "utf-8");
|
|
3018
|
-
return JSON.parse(t);
|
|
3019
|
-
} catch {
|
|
3020
|
-
return null;
|
|
3021
|
-
}
|
|
3022
|
-
}
|
|
3023
|
-
async function Rt(l) {
|
|
3024
|
-
const t = JSON.stringify(l);
|
|
3025
|
-
if (typeof window < "u") {
|
|
3026
|
-
window.localStorage.setItem(`${Z}${l.spaceId}`, t);
|
|
3027
|
-
return;
|
|
3028
|
-
}
|
|
3029
|
-
await (await O()).writeFile(await tt(l.spaceId), t, "utf-8");
|
|
3030
|
-
}
|
|
3031
|
-
async function Ye(l) {
|
|
3032
|
-
if (typeof window < "u") {
|
|
3033
|
-
window.localStorage.removeItem(`${Z}${l}`);
|
|
3034
|
-
return;
|
|
3035
|
-
}
|
|
3036
|
-
try {
|
|
3037
|
-
await (await O()).unlink(await tt(l));
|
|
3038
|
-
} catch {
|
|
3039
|
-
}
|
|
3040
|
-
}
|
|
3041
|
-
class Je {
|
|
3042
|
-
constructor(t, e, s, i) {
|
|
3043
|
-
this._auth = t, this._projects = e, this._blob = s, this._gatewayUrl = i;
|
|
3044
|
-
}
|
|
3045
|
-
_auth;
|
|
3046
|
-
_projects;
|
|
3047
|
-
_blob;
|
|
3048
|
-
_gatewayUrl;
|
|
3049
|
-
_graphMap = /* @__PURE__ */ new Map();
|
|
3050
|
-
_api;
|
|
3051
|
-
_pendingItems = [];
|
|
3052
|
-
_pendingSpaceId = null;
|
|
3053
|
-
_flushTimer = null;
|
|
3054
|
-
_legacy = !1;
|
|
3055
|
-
/** @internal Injected by CueApi after construction to avoid circular dependency. */
|
|
3056
|
-
_bindApi(t) {
|
|
3057
|
-
this._api = t;
|
|
3058
|
-
}
|
|
3059
|
-
/**
|
|
3060
|
-
* Initialises browser-mode sync for a project space.
|
|
3061
|
-
* - Flushes any metadata items that were queued but not sent in a previous session
|
|
3062
|
-
* (persisted in `localStorage`).
|
|
3063
|
-
* - Starts the 60-second periodic flush timer.
|
|
3064
|
-
*
|
|
3065
|
-
* Call this once when the file manager component is created (or when the active
|
|
3066
|
-
* project changes) so that interrupted uploads are recovered immediately.
|
|
3067
|
-
*/
|
|
3068
|
-
async initBrowserSync(t) {
|
|
3069
|
-
await this._initPendingBatch(t);
|
|
3070
|
-
}
|
|
3071
|
-
/**
|
|
3072
|
-
* Pushes filesystem-structure metadata for all provided files directly to the
|
|
3073
|
-
* commands API, without checking what is already on the remote or accounting for
|
|
3074
|
-
* credits. Use this when you want to force-write metadata for every file in a
|
|
3075
|
-
* local path (e.g. to repair missing graph data after a migration).
|
|
3076
|
-
*/
|
|
3077
|
-
async pushAllMetadata(t, e) {
|
|
3078
|
-
this._legacy = e.legacy ?? !1;
|
|
3079
|
-
const s = t.map((i) => ({
|
|
3080
|
-
relativePath: i.relativePath,
|
|
3081
|
-
md5: i.md5,
|
|
3082
|
-
size: i.size,
|
|
3083
|
-
providerId: e.providerId,
|
|
3084
|
-
fileContentExists: !1
|
|
3085
|
-
}));
|
|
3086
|
-
for (let i = 0; i < s.length; i += L)
|
|
3087
|
-
await this._postFssBatch(s.slice(i, i + L), e.spaceId);
|
|
3088
|
-
e.verbose && console.info(`Pushed metadata for ${s.length} file(s) ✅`);
|
|
3089
|
-
}
|
|
3090
|
-
/**
|
|
3091
|
-
* Flushes any pending file-location metadata from a previously interrupted sync.
|
|
3092
|
-
* Safe to call even when there are no new files to upload (e.g. when the process
|
|
3093
|
-
* died after uploading to blob storage but before the commands-API batch POST).
|
|
3094
|
-
*/
|
|
3095
|
-
async flushPendingMetadata(t, e, s) {
|
|
3096
|
-
this._legacy = s ?? !1;
|
|
3097
|
-
const i = await St(t);
|
|
3098
|
-
if (!(!i || i.items.length === 0)) {
|
|
3099
|
-
console.info(`Trying to upload metadata (${i.items.length} item(s))...`), e && console.info(`Flushing ${i.items.length} pending file location(s) from previous sync ⏳`);
|
|
3100
|
-
try {
|
|
3101
|
-
this._pendingSpaceId = t, this._pendingItems = [], await this._flushBatch(i.items, t, e), console.info("Metadata uploaded ✅");
|
|
3102
|
-
} catch (n) {
|
|
3103
|
-
throw new Error(`METADATA_SYNC_FAILED: ${n instanceof Error ? n.message : String(n)}`);
|
|
3104
|
-
}
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
/**
|
|
3108
|
-
* Returns a preview of what would be synced: cost breakdown for new files only,
|
|
3109
|
-
* units required, and units still available. Use this before calling {@link sync}
|
|
3110
|
-
* to show the user an accurate cost estimate.
|
|
3111
|
-
*/
|
|
3112
|
-
async previewSync(t, e) {
|
|
3113
|
-
const { spaceId: s, providerId: i, verbose: n } = e, a = await this._auth.getToken();
|
|
3114
|
-
if (!a) throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
3115
|
-
const c = await this._getOrCreateGraph(s, a), u = (await this._projects.getProject(s))?.projectSettings?.tier ?? "l", [p, m, h, g] = await Promise.all([
|
|
3116
|
-
this._listRemoteFiles(c, s, i, n),
|
|
3117
|
-
this._api?.getConsumption(s) ?? Promise.reject(new Error("CueSyncApi is not bound to a CueApi instance")),
|
|
3118
|
-
this._fetchUnitCreditMap(n),
|
|
3119
|
-
this._fetchTierNames()
|
|
3120
|
-
]), y = (await Ct(t, p)).localNotOnRemote ?? [], C = y.length > 0 ? await this.scanCost(y) : [];
|
|
3121
|
-
let d = 0, T = 0;
|
|
3122
|
-
for (const E of C) {
|
|
3123
|
-
d += E.units;
|
|
3124
|
-
const _ = h[u], R = _?.[E.ext] ?? 1;
|
|
3125
|
-
n && _ && !(E.ext in _) && console.info(` Unknown format: .${E.ext} (using default rate of 1 credit/unit)`);
|
|
3126
|
-
const v = E.units * R;
|
|
3127
|
-
T += v, E.credits = Math.round(v);
|
|
3128
|
-
}
|
|
3129
|
-
const A = g[u] ?? u;
|
|
3130
|
-
return {
|
|
3131
|
-
costRecords: C,
|
|
3132
|
-
tier: u,
|
|
3133
|
-
tierName: A,
|
|
3134
|
-
unitsToConsume: d,
|
|
3135
|
-
creditsToConsume: Math.round(T),
|
|
3136
|
-
creditsAvailable: m.creditsAvailable,
|
|
3137
|
-
unitsAvailable: m.unitsAvailable,
|
|
3138
|
-
filesToUpload: y.length,
|
|
3139
|
-
totalLocalFiles: t.length
|
|
3140
|
-
};
|
|
3141
|
-
}
|
|
3142
|
-
async sync(t, e) {
|
|
3143
|
-
const { spaceId: s, providerId: i, userId: n, verbose: a, onProgress: c, legacy: r } = e;
|
|
3144
|
-
this._legacy = r ?? !1;
|
|
3145
|
-
const u = await this._auth.getToken();
|
|
3146
|
-
if (!u) throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
3147
|
-
const p = await this._getOrCreateGraph(s, u);
|
|
3148
|
-
a && console.info("Listing remote files ⏳");
|
|
3149
|
-
const [m, h] = await Promise.all([
|
|
3150
|
-
this._listRemoteFiles(p, s, i, a),
|
|
3151
|
-
this._api?.getConsumption(s) ?? Promise.reject(new Error("CueSyncApi is not bound to a CueApi instance"))
|
|
3152
|
-
]), { unitsAvailable: g } = h, f = await Ct(t, m);
|
|
3153
|
-
a && (console.info(`Total local files: ${t.length}`), console.info(`Total remote files: ${m.length}`), console.info(
|
|
3154
|
-
`Total files to sync: ${(f.localNotOnRemote?.length ?? 0) + f.localNotOnRemotePathOnly.length}`
|
|
3155
|
-
));
|
|
3156
|
-
let y = f.syncCount, C = f.syncSize, d = 0, T = !1;
|
|
3157
|
-
const A = f.localNotOnRemote ?? [];
|
|
3158
|
-
if (A.length > 0) {
|
|
3159
|
-
const R = (await this.scanCost(A)).reduce((v, $t) => v + $t.units, 0);
|
|
3160
|
-
if (R > g)
|
|
3161
|
-
throw new Error(
|
|
3162
|
-
`Insufficient units: ${R} units required but only ${g} available.`
|
|
3163
|
-
);
|
|
3164
|
-
}
|
|
3165
|
-
await this._initPendingBatch(s, a), a && A.length && console.info("Syncing missing files ⏳");
|
|
3166
|
-
for (const _ of A)
|
|
3167
|
-
try {
|
|
3168
|
-
const R = Tt(
|
|
3169
|
-
_.relativePath,
|
|
3170
|
-
s,
|
|
3171
|
-
n,
|
|
3172
|
-
_.md5,
|
|
3173
|
-
i
|
|
3174
|
-
);
|
|
3175
|
-
if (!R.blob_name) throw new Error(`blob_name missing for ${_.relativePath}`);
|
|
3176
|
-
const v = _.data ?? new Uint8Array(await At(_.fullPath));
|
|
3177
|
-
await this._blob.uploadRaw(
|
|
3178
|
-
R.blob_name,
|
|
3179
|
-
v,
|
|
3180
|
-
R
|
|
3181
|
-
), await this._queueFileLocation({
|
|
3182
|
-
relativePath: _.relativePath,
|
|
3183
|
-
md5: _.md5,
|
|
3184
|
-
size: _.size,
|
|
3185
|
-
providerId: i,
|
|
3186
|
-
fileContentExists: !1
|
|
3187
|
-
}), T = !0, y += 1, C += _.size || 0, this._logProgress(y, f.totalCount, C, f.totalSize, c);
|
|
3188
|
-
} catch (R) {
|
|
3189
|
-
d += 1, console.error(`[CueSyncApi] Failed to upload file: ${_.fullPath}`), a && console.error("[CueSyncApi] Upload error details:", R);
|
|
3190
|
-
}
|
|
3191
|
-
a && f.localNotOnRemotePathOnly.length && console.info(`Syncing missing file locations (on provider "${i}") ⏳`);
|
|
3192
|
-
for (const _ of f.localNotOnRemotePathOnly)
|
|
3193
|
-
await this._queueFileLocation({
|
|
3194
|
-
relativePath: _.relativePath,
|
|
3195
|
-
md5: _.md5,
|
|
3196
|
-
size: _.size,
|
|
3197
|
-
providerId: i,
|
|
3198
|
-
fileContentExists: !0
|
|
3199
|
-
}), T = !0, y += 1, C += _.size || 0, this._logProgress(y, f.totalCount, C, f.totalSize, c);
|
|
3200
|
-
await this._drainPending(a), this._stopFlushTimer();
|
|
3201
|
-
const E = await (this._api?.getConsumption(s) ?? Promise.resolve({ creditsAvailable: 0 }));
|
|
3202
|
-
return {
|
|
3203
|
-
syncCount: y,
|
|
3204
|
-
syncSize: C,
|
|
3205
|
-
failedUploads: d,
|
|
3206
|
-
totalCount: f.totalCount,
|
|
3207
|
-
totalSize: f.totalSize,
|
|
3208
|
-
rdfWritten: T,
|
|
3209
|
-
creditsAvailable: E.creditsAvailable
|
|
3210
|
-
};
|
|
3211
|
-
}
|
|
3212
|
-
async _getOrCreateGraph(t, e) {
|
|
3213
|
-
const s = this._graphMap.get(t);
|
|
3214
|
-
if (s) return s;
|
|
3215
|
-
const n = (await this._projects.getProject(t))?.projectSettings?.graph?.type ?? Xe, a = n === "qlever" ? `${this._gatewayUrl}${Ot}` : `${this._gatewayUrl}${Ut}`, c = n === "qlever" ? `${this._gatewayUrl}${$e}` : `${this._gatewayUrl}${Me}`, r = new _e({
|
|
3216
|
-
graphType: n,
|
|
3217
|
-
queryEndpoint: a,
|
|
3218
|
-
updateEndpoint: c,
|
|
3219
|
-
originalHeaders: {
|
|
3220
|
-
"x-project-id": t,
|
|
3221
|
-
authorization: `Bearer ${e}`
|
|
3222
|
-
}
|
|
3223
|
-
});
|
|
3224
|
-
return this._graphMap.set(t, r), r;
|
|
3225
|
-
}
|
|
3226
|
-
async _listRemoteFiles(t, e, s, i) {
|
|
3227
|
-
i && console.info(`Listing files in raw space: ${e}`);
|
|
3228
|
-
const n = Promise.race([
|
|
3229
|
-
this._getGraphFiles(t, s),
|
|
3230
|
-
new Promise(
|
|
3231
|
-
(u, p) => setTimeout(() => p(new Error("GRAPH_TIMEOUT: Knowledge graph query timed out")), 15e3)
|
|
3232
|
-
)
|
|
3233
|
-
]), [a, c] = await Promise.all([
|
|
3234
|
-
this._blob.listRaw(e),
|
|
3235
|
-
n
|
|
3236
|
-
]);
|
|
3237
|
-
i && console.info(`Found ${a.length} files in raw store for space: ${e}`);
|
|
3238
|
-
const r = [];
|
|
3239
|
-
for (const u of a) {
|
|
3240
|
-
const p = u.substring(0, 36), m = c[p] ?? [];
|
|
3241
|
-
if (m.length === 0)
|
|
3242
|
-
i && console.warn(`No location data found for contentUUID: ${p}`), r.push({ contentUUID: p });
|
|
3243
|
-
else
|
|
3244
|
-
for (const h of m)
|
|
3245
|
-
r.push({
|
|
3246
|
-
contentUUID: p,
|
|
3247
|
-
locationUUID: h.locationUUID,
|
|
3248
|
-
created: h.created,
|
|
3249
|
-
size: h.size
|
|
3250
|
-
});
|
|
3251
|
-
}
|
|
3252
|
-
return r;
|
|
3253
|
-
}
|
|
3254
|
-
async _getGraphFiles(t, e) {
|
|
3255
|
-
const s = `PREFIX qcy: <${S.qcy}>
|
|
3256
|
-
SELECT ?fc ?loc ?created ?fp ?size
|
|
3257
|
-
WHERE {
|
|
3258
|
-
?fc a qcy:FileContent ;
|
|
3259
|
-
qcy:sizeBytes ?size ;
|
|
3260
|
-
qcy:hasFileLocation ?loc .
|
|
3261
|
-
?loc qcy:remoteProviderId "${e}" ;
|
|
3262
|
-
qcy:dateCreated ?created ;
|
|
3263
|
-
qcy:filePath ?fp .
|
|
3264
|
-
}`;
|
|
3265
|
-
let i;
|
|
3266
|
-
try {
|
|
3267
|
-
i = await t.query(s, "application/sparql-results+json");
|
|
3268
|
-
} catch (a) {
|
|
3269
|
-
throw (a instanceof Error ? a.message : String(a)).includes("HTTP 500") ? new Error("GRAPH_UNAVAILABLE: The knowledge graph for this project has no database configured. Contact your administrator or check the project setup.") : a;
|
|
3270
|
-
}
|
|
3271
|
-
const n = {};
|
|
3272
|
-
if (typeof i == "string") return n;
|
|
3273
|
-
for (const a of i.results.bindings) {
|
|
3274
|
-
const c = a.loc.value.split("/").at(-1) ?? "", r = a.fc.value.split("/").at(-1) ?? "", u = a.created.value, p = a.size.value;
|
|
3275
|
-
n[r] || (n[r] = []), n[r].push({ locationUUID: c, created: u, size: p });
|
|
3276
|
-
}
|
|
3277
|
-
return n;
|
|
3278
|
-
}
|
|
3279
|
-
async _initPendingBatch(t, e) {
|
|
3280
|
-
this._flushTimer !== null && (clearInterval(this._flushTimer), this._flushTimer = null), this._pendingSpaceId = t, this._pendingItems = [];
|
|
3281
|
-
const s = await St(t);
|
|
3282
|
-
if (s && s.items.length > 0) {
|
|
3283
|
-
console.info(`Trying to upload metadata from interrupted sync (${s.items.length} item(s))...`), e && console.info(`Flushing ${s.items.length} pending file location(s) from previous sync ⏳`);
|
|
3284
|
-
try {
|
|
3285
|
-
await this._flushBatch(s.items, t, e), console.info("Metadata uploaded ✅");
|
|
3286
|
-
} catch (n) {
|
|
3287
|
-
throw new Error(`METADATA_SYNC_FAILED: ${n instanceof Error ? n.message : String(n)}`);
|
|
3288
|
-
}
|
|
3289
|
-
}
|
|
3290
|
-
const i = setInterval(() => {
|
|
3291
|
-
this._drainPending(e).catch((n) => {
|
|
3292
|
-
const a = n instanceof Error ? n.message : String(n);
|
|
3293
|
-
console.warn(
|
|
3294
|
-
`⚠️ Metadata flush failed (will retry on next tick): ${a}
|
|
3295
|
-
Pending items are saved locally and will be re-sent automatically.`
|
|
3296
|
-
);
|
|
3297
|
-
});
|
|
3298
|
-
}, 6e4);
|
|
3299
|
-
typeof i == "object" && typeof i.unref == "function" && i.unref(), this._flushTimer = i;
|
|
3300
|
-
}
|
|
3301
|
-
async _queueFileLocation(t) {
|
|
3302
|
-
this._pendingItems.push(t), this._pendingSpaceId && await Rt({ spaceId: this._pendingSpaceId, items: this._pendingItems });
|
|
3303
|
-
}
|
|
3304
|
-
/**
|
|
3305
|
-
* Flush all queued file-location items to the commands API in a single batch.
|
|
3306
|
-
* Call this once after a group of `syncBrowserFile` calls completes so that
|
|
3307
|
-
* all items are sent together rather than one POST per file.
|
|
3308
|
-
*/
|
|
3309
|
-
async drainPending() {
|
|
3310
|
-
await this._drainPending();
|
|
3311
|
-
}
|
|
3312
|
-
async _drainPending(t) {
|
|
3313
|
-
!this._pendingSpaceId || this._pendingItems.length === 0 || this._auth.currentUser && await this._flushBatch(this._pendingItems, this._pendingSpaceId, t);
|
|
3314
|
-
}
|
|
3315
|
-
async _flushBatch(t, e, s) {
|
|
3316
|
-
const i = [...t];
|
|
3317
|
-
this._pendingSpaceId === e && (this._pendingItems = []), await Ye(e);
|
|
3318
|
-
try {
|
|
3319
|
-
for (let n = 0; n < i.length; n += L)
|
|
3320
|
-
await this._postFssBatch(i.slice(n, n + L), e);
|
|
3321
|
-
s && console.info(`Wrote ${i.length} file location(s) to commands API ✅`);
|
|
3322
|
-
} catch (n) {
|
|
3323
|
-
const a = [...i, ...this._pendingItems];
|
|
3324
|
-
throw this._pendingItems = a, await Rt({ spaceId: e, items: a }), n;
|
|
3325
|
-
}
|
|
3326
|
-
}
|
|
3327
|
-
async _postFssBatch(t, e) {
|
|
3328
|
-
const s = this._legacy ? `${this._gatewayUrl}${pt}?blob=true` : `${this._gatewayUrl}${pt}`;
|
|
3329
|
-
let i;
|
|
3330
|
-
try {
|
|
3331
|
-
i = await this._auth.authenticatedFetch(s, {
|
|
3332
|
-
method: "POST",
|
|
3333
|
-
headers: {
|
|
3334
|
-
"Content-Type": "application/json",
|
|
3335
|
-
"x-project-id": e
|
|
3336
|
-
},
|
|
3337
|
-
body: JSON.stringify({ items: t })
|
|
3338
|
-
});
|
|
3339
|
-
} catch (n) {
|
|
3340
|
-
throw new Error(`File structure batch POST failed: ${n instanceof Error ? n.message : String(n)}`);
|
|
3341
|
-
}
|
|
3342
|
-
if (!i.ok) {
|
|
3343
|
-
const n = await i.text().catch(() => "");
|
|
3344
|
-
throw new Error(`File structure batch POST failed: ${i.status} ${i.statusText}${n ? ` — ${n}` : ""}`);
|
|
3345
|
-
}
|
|
3346
|
-
}
|
|
3347
|
-
_stopFlushTimer() {
|
|
3348
|
-
this._flushTimer !== null && (clearInterval(this._flushTimer), this._flushTimer = null);
|
|
3349
|
-
}
|
|
3350
|
-
/**
|
|
3351
|
-
* Scans `localFiles` and returns a per-extension cost breakdown.
|
|
3352
|
-
*
|
|
3353
|
-
* Each {@link ScanOutputRecord} contains `units` — the billable metric for
|
|
3354
|
-
* that extension (e.g. pages for PDFs, rows for spreadsheets) — which can be
|
|
3355
|
-
* shown to the user before or after calling {@link sync}.
|
|
3356
|
-
*/
|
|
3357
|
-
async scanCost(t) {
|
|
3358
|
-
if (q || (q = Ve()), await q, !D) throw new Error("WASM scan function not initialised");
|
|
3359
|
-
const e = 200, s = /* @__PURE__ */ new Map();
|
|
3360
|
-
for (let i = 0; i < t.length; i += e) {
|
|
3361
|
-
const n = t.slice(i, i + e), a = await Promise.all(
|
|
3362
|
-
n.map(async (r) => ({
|
|
3363
|
-
originalPath: r.relativePath,
|
|
3364
|
-
// Use pre-loaded data if available (browser), otherwise read from disk (Node.js).
|
|
3365
|
-
data: r.data ?? new Uint8Array(await At(r.fullPath))
|
|
3366
|
-
}))
|
|
3367
|
-
), c = D(a);
|
|
3368
|
-
for (const r of c) {
|
|
3369
|
-
const u = s.get(r.ext);
|
|
3370
|
-
u ? (u.count += r.count, u.units += r.units, u.sizeMb += r.sizeMb) : s.set(r.ext, { ...r });
|
|
3371
|
-
}
|
|
3372
|
-
}
|
|
3373
|
-
return Array.from(s.values());
|
|
3374
|
-
}
|
|
3375
|
-
/**
|
|
3376
|
-
* Compute the credit cost for a set of local files without uploading anything.
|
|
3377
|
-
* Intended for browser use where the full {@link previewSync} (which requires a
|
|
3378
|
-
* remote file listing) would be too heavy for a quick estimate.
|
|
3379
|
-
*
|
|
3380
|
-
* @param localFiles - Files to analyse. Each entry must carry `data` when
|
|
3381
|
-
* called from a browser context.
|
|
3382
|
-
* @param spaceId - Project/space identifier used to fetch the tier settings.
|
|
3383
|
-
* @returns Per-extension cost breakdown and the number of credits currently
|
|
3384
|
-
* available in the project.
|
|
3385
|
-
*/
|
|
3386
|
-
async computeCredits(t, e) {
|
|
3387
|
-
const i = (await this._projects.getProject(e))?.projectSettings?.tier ?? "l";
|
|
3388
|
-
console.info(`Computing credit cost for ${t.length} file(s) using tier "${i}"...`);
|
|
3389
|
-
const n = (this._api?.getConsumption(e) ?? Promise.resolve({ creditsAvailable: 0, unitsAvailable: 0 })).then((h) => (console.info("[computeCredits] getConsumption resolved:", h), h)).catch((h) => (console.warn("[computeCredits] getConsumption failed, defaulting to 0:", h?.message ?? h), { creditsAvailable: 0, unitsAvailable: 0 })), a = this._fetchUnitCreditMap().then((h) => (console.info("[computeCredits] creditMap resolved, keys:", Object.keys(h)), h)).catch((h) => (console.warn("[computeCredits] fetchUnitCreditMap failed, using default rates:", h?.message ?? h), {})), c = t.length > 0 ? (console.info(`[computeCredits] starting WASM scan of ${t.length} file(s)...`), this.scanCost(t).then((h) => (console.info(`[computeCredits] WASM scan done: ${h.length} ext(s)`), h))) : Promise.resolve([]), [r, u, p] = await Promise.all([
|
|
3390
|
-
c,
|
|
3391
|
-
a,
|
|
3392
|
-
n
|
|
3393
|
-
]);
|
|
3394
|
-
console.info(`[computeCredits] all resolved — ${r.length} ext(s), creditsAvailable: ${p.creditsAvailable}`);
|
|
3395
|
-
let m = 0;
|
|
3396
|
-
for (const h of r) {
|
|
3397
|
-
const f = u[i]?.[h.ext] ?? 1, y = h.units * f;
|
|
3398
|
-
m += y, h.credits = Math.round(y);
|
|
3399
|
-
}
|
|
3400
|
-
return {
|
|
3401
|
-
costRecords: r,
|
|
3402
|
-
creditsToConsume: Math.round(m),
|
|
3403
|
-
creditsAvailable: p.creditsAvailable
|
|
3404
|
-
};
|
|
3405
|
-
}
|
|
3406
|
-
/**
|
|
3407
|
-
* Upload a single browser-supplied file and write its metadata to the knowledge graph.
|
|
3408
|
-
*
|
|
3409
|
-
* Unlike {@link sync} (which performs a full remote comparison), this method is
|
|
3410
|
-
* designed for the web file-manager flow where the user has already confirmed the
|
|
3411
|
-
* upload via the credit modal. The file's binary data must be provided in
|
|
3412
|
-
* `file.data`; the `file.fullPath` field is ignored.
|
|
3413
|
-
*
|
|
3414
|
-
* Cancellation is supported via `options.signal`. Aborting the signal cancels
|
|
3415
|
-
* the Firebase Storage upload; metadata is never written for a cancelled upload.
|
|
3416
|
-
*
|
|
3417
|
-
* @param file - `LocalFile` with `data` populated (e.g. from `File.arrayBuffer()`).
|
|
3418
|
-
* @param options - Upload options including project/provider/user context and an
|
|
3419
|
-
* optional `AbortSignal` for cancellation and `onProgress` for tracking.
|
|
3420
|
-
*/
|
|
3421
|
-
async syncBrowserFile(t, e) {
|
|
3422
|
-
const { spaceId: s, providerId: i, userId: n, signal: a, onProgress: c } = e;
|
|
3423
|
-
if (!t.data)
|
|
3424
|
-
throw new Error("syncBrowserFile requires file.data (Uint8Array). Read the file with File.arrayBuffer() first.");
|
|
3425
|
-
const r = Tt(t.relativePath, s, n, t.md5, i);
|
|
3426
|
-
if (!r.blob_name) throw new Error(`blob_name missing for ${t.relativePath}`);
|
|
3427
|
-
await this._blob.uploadRaw(
|
|
3428
|
-
r.blob_name,
|
|
3429
|
-
t.data,
|
|
3430
|
-
r,
|
|
3431
|
-
3,
|
|
3432
|
-
a,
|
|
3433
|
-
c
|
|
3434
|
-
), await this._queueFileLocation(
|
|
3435
|
-
{ relativePath: t.relativePath, md5: t.md5, size: t.size, providerId: i, fileContentExists: !1 }
|
|
3436
|
-
);
|
|
3437
|
-
}
|
|
3438
|
-
async getTierNames() {
|
|
3439
|
-
return this._fetchTierNames();
|
|
3440
|
-
}
|
|
3441
|
-
async _fetchTierNames() {
|
|
3442
|
-
try {
|
|
3443
|
-
const t = await this._blob.downloadPublic("tier-names.json");
|
|
3444
|
-
return JSON.parse(t);
|
|
3445
|
-
} catch {
|
|
3446
|
-
return {};
|
|
3447
|
-
}
|
|
3448
|
-
}
|
|
3449
|
-
async _fetchUnitCreditMap(t) {
|
|
3450
|
-
let e;
|
|
3451
|
-
try {
|
|
3452
|
-
e = await this._blob.downloadPublic("unit-credit.json");
|
|
3453
|
-
} catch (s) {
|
|
3454
|
-
throw new Error(`Couldn't fetch credits table: ${s instanceof Error ? s.message : String(s)}`);
|
|
3455
|
-
}
|
|
3456
|
-
t && console.info(`Price file: ${e.length} bytes`);
|
|
3457
|
-
try {
|
|
3458
|
-
return JSON.parse(e);
|
|
3459
|
-
} catch {
|
|
3460
|
-
throw new Error(`Credits table is not valid JSON (${e.length} bytes)`);
|
|
3461
|
-
}
|
|
3462
|
-
}
|
|
3463
|
-
_logProgress(t, e, s, i, n) {
|
|
3464
|
-
if (!n || e === 0) return;
|
|
3465
|
-
const a = Math.floor(t / e * 100);
|
|
3466
|
-
n({ percent: a, syncCount: t, totalCount: e, syncSize: s, totalSize: i });
|
|
3467
|
-
}
|
|
3468
|
-
}
|
|
3469
|
-
const Pt = {
|
|
3470
|
-
production: {
|
|
3471
|
-
gatewayUrl: "https://accessors-api-gateway-ueyeemwf2a-oa.a.run.app",
|
|
3472
|
-
tokenUrl: "https://accessors-api-gateway-ueyeemwf2a-oa.a.run.app/token",
|
|
3473
|
-
authEmulatorUrl: "http://localhost:9099",
|
|
3474
|
-
storageEmulatorHost: "localhost",
|
|
3475
|
-
storageEmulatorPort: 9199,
|
|
3476
|
-
firestoreEmulatorHost: "localhost",
|
|
3477
|
-
firestoreEmulatorPort: 8080
|
|
3478
|
-
},
|
|
3479
|
-
emulator: {
|
|
3480
|
-
gatewayUrl: "http://localhost:18093",
|
|
3481
|
-
tokenUrl: "http://localhost:18093/token",
|
|
3482
|
-
authEmulatorUrl: "http://localhost:9099",
|
|
3483
|
-
storageEmulatorHost: "localhost",
|
|
3484
|
-
storageEmulatorPort: 9199,
|
|
3485
|
-
firestoreEmulatorHost: "localhost",
|
|
3486
|
-
firestoreEmulatorPort: 8080
|
|
3487
|
-
}
|
|
3488
|
-
};
|
|
3489
|
-
class Mt {
|
|
3490
|
-
auth;
|
|
3491
|
-
api;
|
|
3492
|
-
projects;
|
|
3493
|
-
profile;
|
|
3494
|
-
privileges;
|
|
3495
|
-
cache;
|
|
3496
|
-
_app;
|
|
3497
|
-
_endpoints;
|
|
3498
|
-
_isEmulator;
|
|
3499
|
-
constructor(t = {}) {
|
|
3500
|
-
!t.apiKey && !t.appId && !t.measurementId && console.warn(
|
|
3501
|
-
"Using default SDK app settings. Contact QAECY for your own configuration for any production code."
|
|
3502
|
-
);
|
|
3503
|
-
const s = t.apiKey ?? H.apiKey, i = t.appId ?? H.appId, n = t.measurementId ?? H.measurementId, a = t.environment ?? "production";
|
|
3504
|
-
this._endpoints = { ...Pt[a], ...t.endpoints }, this._isEmulator = a === "emulator", this._app = Lt().find((r) => r.name === "[DEFAULT]") ?? jt({
|
|
3505
|
-
apiKey: s,
|
|
3506
|
-
appId: i,
|
|
3507
|
-
measurementId: n,
|
|
3508
|
-
authDomain: `${ot}.firebaseapp.com`,
|
|
3509
|
-
projectId: ot,
|
|
3510
|
-
messagingSenderId: Ce
|
|
3511
|
-
}), this.auth = new ft(this._app, this._isEmulator, this._endpoints), this.projects = new yt(this.auth, this._app, this._isEmulator, this._endpoints), this.api = this._buildApi(this.projects), this.profile = new gt(
|
|
3512
|
-
this.auth,
|
|
3513
|
-
this._app,
|
|
3514
|
-
this._isEmulator,
|
|
3515
|
-
this._endpoints.gatewayUrl
|
|
3516
|
-
), this.privileges = new wt(this.auth.isSuperAdmin);
|
|
3517
|
-
const c = P(this._app, ct);
|
|
3518
|
-
this._isEmulator && Bt(c, this._endpoints.storageEmulatorHost, this._endpoints.storageEmulatorPort), this.cache = new It(c);
|
|
3519
|
-
}
|
|
3520
|
-
/**
|
|
3521
|
-
* Create a `Cue` instance from an already-initialized Firebase app.
|
|
3522
|
-
*
|
|
3523
|
-
* Use this when the host application (e.g. cue-portal via `CueFirebase`) has
|
|
3524
|
-
* already called `initializeApp()`. Reusing the same `FirebaseApp` means the
|
|
3525
|
-
* SDK shares the same auth state and Firestore instance — no second login is
|
|
3526
|
-
* needed and `getToken()` returns the portal user's token directly.
|
|
3527
|
-
*
|
|
3528
|
-
* Emulator connections are skipped because the host app already set them up.
|
|
3529
|
-
*
|
|
3530
|
-
* @example
|
|
3531
|
-
* // In an Angular service after CueFirebase is ready:
|
|
3532
|
-
* const cue = Cue.fromApp(CueFirebase.getInstance().app!, {
|
|
3533
|
-
* environment: 'emulator',
|
|
3534
|
-
* });
|
|
3535
|
-
*/
|
|
3536
|
-
static fromApp(t, e = {}) {
|
|
3537
|
-
const s = e.environment ?? "production", i = { ...Pt[s], ...e.endpoints }, n = new ft(t, !1, i), a = new yt(n, t, !1, i), c = P(t, Ae), r = P(t, Se), u = P(t, Pe), p = P(t, Re), m = P(t, Te), h = P(t, ct), g = new Ee({
|
|
3538
|
-
storageRaw: c,
|
|
3539
|
-
storageProcessed: r,
|
|
3540
|
-
storagePublic: u,
|
|
3541
|
-
storageLogs: p,
|
|
3542
|
-
storageChatSessions: m,
|
|
3543
|
-
storagePersistence: h
|
|
3544
|
-
}), f = new Je(n, a, g, i.gatewayUrl), y = new mt(n, i.gatewayUrl, a, f);
|
|
3545
|
-
f._bindApi(y);
|
|
3546
|
-
const C = new gt(n, t, !1, i.gatewayUrl), d = Object.create(Mt.prototype), T = new wt(n.isSuperAdmin), A = new It(h);
|
|
3547
|
-
return Object.assign(d, {
|
|
3548
|
-
_app: t,
|
|
3549
|
-
_endpoints: i,
|
|
3550
|
-
_isEmulator: s === "emulator",
|
|
3551
|
-
auth: n,
|
|
3552
|
-
api: y,
|
|
3553
|
-
projects: a,
|
|
3554
|
-
profile: C,
|
|
3555
|
-
privileges: T,
|
|
3556
|
-
cache: A
|
|
3557
|
-
}), d;
|
|
3558
|
-
}
|
|
3559
|
-
/** Override in subclasses to provide a custom CueApi (e.g. with sync). */
|
|
3560
|
-
_buildApi(t) {
|
|
3561
|
-
return new mt(this.auth, this._endpoints.gatewayUrl, t);
|
|
3562
|
-
}
|
|
3563
|
-
/** Convenience: get the current user's Firebase ID token */
|
|
3564
|
-
getToken(t = !1) {
|
|
3565
|
-
return this.auth.getToken(t);
|
|
3566
|
-
}
|
|
3567
|
-
/**
|
|
3568
|
-
* Create a `CueProjectView` for the given project, wiring the SDK's query
|
|
3569
|
-
* cache automatically.
|
|
3570
|
-
*
|
|
3571
|
-
* The view auto-fetches the document overview and entity graph on construction
|
|
3572
|
-
* and exposes reactive signals for all project knowledge-graph state.
|
|
3573
|
-
*
|
|
3574
|
-
* @example
|
|
3575
|
-
* ```ts
|
|
3576
|
-
* const view = cue.createProjectView('my-project', { language: 'en' });
|
|
3577
|
-
* view.requestEntityData(['uuid1']);
|
|
3578
|
-
* const info = view.entityInfoMap.get()['uuid1'];
|
|
3579
|
-
* ```
|
|
3580
|
-
*/
|
|
3581
|
-
createProjectView(t, e) {
|
|
3582
|
-
const s = e.queryCache ?? {
|
|
3583
|
-
get: (i) => this.cache.getQueryCache(t, i).then((n) => n?.results),
|
|
3584
|
-
set: (i, n) => this.cache.setQueryCache(t, i, { query: i, results: n })
|
|
3585
|
-
};
|
|
3586
|
-
return new Fe(this.api, t, { ...e, queryCache: s });
|
|
3587
|
-
}
|
|
3588
|
-
}
|
|
3589
|
-
export {
|
|
3590
|
-
Te as B,
|
|
3591
|
-
Mt as C,
|
|
3592
|
-
Et as R,
|
|
3593
|
-
mt as a,
|
|
3594
|
-
ft as b,
|
|
3595
|
-
It as c,
|
|
3596
|
-
wt as d,
|
|
3597
|
-
gt as e,
|
|
3598
|
-
Be as f,
|
|
3599
|
-
ke as g,
|
|
3600
|
-
qe as h,
|
|
3601
|
-
Fe as i,
|
|
3602
|
-
yt as j,
|
|
3603
|
-
I as k,
|
|
3604
|
-
Je as l,
|
|
3605
|
-
hs as m,
|
|
3606
|
-
X as n,
|
|
3607
|
-
Re as o,
|
|
3608
|
-
Ae as p,
|
|
3609
|
-
ct as q,
|
|
3610
|
-
Se as r,
|
|
3611
|
-
Y as s,
|
|
3612
|
-
Pe as t,
|
|
3613
|
-
Ee as u
|
|
3614
|
-
};
|