@qaecy/cue-sdk 0.0.4 → 0.0.6
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/{cue-Cl66hB4E.js → cue-BA1pOKTu.js} +58 -47
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/api.d.ts +2 -1
- package/lib/models.d.ts +47 -2
- package/lib/sync.d.ts +28 -5
- package/node.js +318 -169
- package/package.json +1 -4
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { initializeApp as D } from "firebase/app";
|
|
2
|
-
import { getAuth as S, connectAuthEmulator as C, signInWithEmailAndPassword as
|
|
3
|
-
import { getFirestore as b, connectFirestoreEmulator as A, doc as _, collection as
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Q {
|
|
2
|
+
import { getAuth as S, connectAuthEmulator as C, signInWithEmailAndPassword as T, GoogleAuthProvider as P, OAuthProvider as U, signInWithPopup as O, signInWithCustomToken as j, signOut as v, onAuthStateChanged as N, fetchSignInMethodsForEmail as K, linkWithPopup as R, unlink as $, reauthenticateWithCredential as f, EmailAuthProvider as m, updatePassword as q, linkWithCredential as x, verifyBeforeUpdateEmail as H, sendEmailVerification as L } from "firebase/auth";
|
|
3
|
+
import { getFirestore as b, connectFirestoreEmulator as A, doc as _, collection as n, getDoc as y, setDoc as g, getDocs as h, query as l, where as d, serverTimestamp as F, increment as W, addDoc as z, deleteDoc as B, limit as J } from "firebase/firestore";
|
|
4
|
+
const M = "microsoft.com";
|
|
5
|
+
class V {
|
|
7
6
|
_auth;
|
|
8
7
|
_endpoints;
|
|
9
8
|
constructor(t, e = !1, r) {
|
|
@@ -15,14 +14,14 @@ class Q {
|
|
|
15
14
|
if (t === "password") {
|
|
16
15
|
if (!e)
|
|
17
16
|
throw new Error("credentials are required for password sign-in");
|
|
18
|
-
return (await
|
|
17
|
+
return (await T(
|
|
19
18
|
this._auth,
|
|
20
19
|
e.email,
|
|
21
20
|
e.password
|
|
22
21
|
)).user;
|
|
23
22
|
}
|
|
24
|
-
const r = t === "google" ? new P() : new U(
|
|
25
|
-
return (await
|
|
23
|
+
const r = t === "google" ? new P() : new U(M);
|
|
24
|
+
return (await O(this._auth, r)).user;
|
|
26
25
|
}
|
|
27
26
|
/** Sign in with a Cue API key */
|
|
28
27
|
async signInWithApiKey(t, e) {
|
|
@@ -36,11 +35,11 @@ class Q {
|
|
|
36
35
|
if (!r.ok)
|
|
37
36
|
throw new Error(`Failed to fetch custom token: ${r.statusText}`);
|
|
38
37
|
const { token: s } = await r.json();
|
|
39
|
-
return (await
|
|
38
|
+
return (await j(this._auth, s)).user;
|
|
40
39
|
}
|
|
41
40
|
/** Sign out the current user */
|
|
42
41
|
async signOut() {
|
|
43
|
-
await
|
|
42
|
+
await v(this._auth);
|
|
44
43
|
}
|
|
45
44
|
/** Currently signed-in user, or null if not authenticated */
|
|
46
45
|
get currentUser() {
|
|
@@ -48,7 +47,7 @@ class Q {
|
|
|
48
47
|
}
|
|
49
48
|
/** Subscribe to authentication state changes. Returns an unsubscribe function. */
|
|
50
49
|
onAuthStateChanged(t) {
|
|
51
|
-
return
|
|
50
|
+
return N(this._auth, t);
|
|
52
51
|
}
|
|
53
52
|
/** Get the Firebase ID token for the current user, or null if not authenticated */
|
|
54
53
|
async getToken(t = !1) {
|
|
@@ -60,7 +59,7 @@ class Q {
|
|
|
60
59
|
return this._auth;
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
|
-
const
|
|
62
|
+
const G = "/assistant/search", Q = "/triplestore/query", Y = "/data-views/admin/consumption";
|
|
64
63
|
class X {
|
|
65
64
|
constructor(t, e, r, s) {
|
|
66
65
|
this._auth = t, this._gatewayUrl = e, this.projects = r, this.sync = s;
|
|
@@ -89,7 +88,7 @@ class X {
|
|
|
89
88
|
* The user must be authenticated before calling this.
|
|
90
89
|
*/
|
|
91
90
|
async search(t) {
|
|
92
|
-
const e = await this._authHeaders(), r = await fetch(`${this._gatewayUrl}${
|
|
91
|
+
const e = await this._authHeaders(), r = await fetch(`${this._gatewayUrl}${G}`, {
|
|
93
92
|
method: "POST",
|
|
94
93
|
headers: e,
|
|
95
94
|
body: JSON.stringify({
|
|
@@ -107,7 +106,7 @@ class X {
|
|
|
107
106
|
* The user must be authenticated before calling this.
|
|
108
107
|
*/
|
|
109
108
|
async sparql(t, e) {
|
|
110
|
-
const r = await this._authHeaders(), s = await fetch(`${this._gatewayUrl}${
|
|
109
|
+
const r = await this._authHeaders(), s = await fetch(`${this._gatewayUrl}${Q}`, {
|
|
111
110
|
method: "POST",
|
|
112
111
|
headers: r,
|
|
113
112
|
body: JSON.stringify({ query: t, projectId: e })
|
|
@@ -116,6 +115,18 @@ class X {
|
|
|
116
115
|
throw new Error(`SPARQL query failed: ${s.status} ${s.statusText}`);
|
|
117
116
|
return s.json();
|
|
118
117
|
}
|
|
118
|
+
async getConsumption(t, e) {
|
|
119
|
+
const r = await this._authHeaders(), s = await fetch(`${this._gatewayUrl}${Y}`, {
|
|
120
|
+
headers: {
|
|
121
|
+
...r,
|
|
122
|
+
"x-project-id": t,
|
|
123
|
+
...e ? { "x-tier": e } : {}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
if (!s.ok)
|
|
127
|
+
throw new Error(`Failed to fetch consumption: ${s.status} ${s.statusText}`);
|
|
128
|
+
return s.json();
|
|
129
|
+
}
|
|
119
130
|
}
|
|
120
131
|
const w = "projects";
|
|
121
132
|
class Z {
|
|
@@ -137,10 +148,10 @@ class Z {
|
|
|
137
148
|
* Throws if a project with the given ID already exists.
|
|
138
149
|
*/
|
|
139
150
|
async createProject(t) {
|
|
140
|
-
const e = this._requireUser(), r = t.id ?? crypto.randomUUID(), s = (/* @__PURE__ */ new Date()).toISOString(), a = { views: [], chatDisabled: !1 }, i = _(
|
|
141
|
-
if ((await
|
|
151
|
+
const e = this._requireUser(), r = t.id ?? crypto.randomUUID(), s = (/* @__PURE__ */ new Date()).toISOString(), a = { views: [], chatDisabled: !1 }, i = _(n(this._db, w), r);
|
|
152
|
+
if ((await y(i)).exists())
|
|
142
153
|
throw new Error(`Project with ID "${r}" already exists.`);
|
|
143
|
-
const
|
|
154
|
+
const u = {
|
|
144
155
|
id: r,
|
|
145
156
|
name: t.name,
|
|
146
157
|
organizationID: t.organizationID,
|
|
@@ -153,28 +164,28 @@ class Z {
|
|
|
153
164
|
alternativeIDs: [r],
|
|
154
165
|
projectSettings: a
|
|
155
166
|
};
|
|
156
|
-
return await
|
|
167
|
+
return await g(i, u), u;
|
|
157
168
|
}
|
|
158
169
|
/**
|
|
159
170
|
* List all projects where the authenticated user appears in the members, syncers, or admins array.
|
|
160
171
|
* Runs three parallel Firestore queries and deduplicates by project ID.
|
|
161
172
|
*/
|
|
162
173
|
async listProjects() {
|
|
163
|
-
const t = this._requireUser(), e =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
]), i = /* @__PURE__ */ new Set(),
|
|
168
|
-
for (const
|
|
169
|
-
for (const k of
|
|
174
|
+
const t = this._requireUser(), e = n(this._db, w), [r, s, a] = await Promise.all([
|
|
175
|
+
h(l(e, d("members", "array-contains", t))),
|
|
176
|
+
h(l(e, d("syncers", "array-contains", t))),
|
|
177
|
+
h(l(e, d("admins", "array-contains", t)))
|
|
178
|
+
]), i = /* @__PURE__ */ new Set(), o = [];
|
|
179
|
+
for (const u of [r, s, a])
|
|
180
|
+
for (const k of u.docs) {
|
|
170
181
|
const p = k.data();
|
|
171
|
-
i.has(p.id) || (i.add(p.id),
|
|
182
|
+
i.has(p.id) || (i.add(p.id), o.push(p));
|
|
172
183
|
}
|
|
173
|
-
return
|
|
184
|
+
return o;
|
|
174
185
|
}
|
|
175
186
|
/** Fetch a single project by ID. Returns null if not found. */
|
|
176
187
|
async getProject(t) {
|
|
177
|
-
const e = _(
|
|
188
|
+
const e = _(n(this._db, w), t), r = await y(e);
|
|
178
189
|
return r.exists() ? r.data() : null;
|
|
179
190
|
}
|
|
180
191
|
/**
|
|
@@ -184,14 +195,14 @@ class Z {
|
|
|
184
195
|
async incrementUnitsConsumed(t, e, r) {
|
|
185
196
|
if (e <= 0) return;
|
|
186
197
|
const s = _(this._db, "clientSync", t);
|
|
187
|
-
await
|
|
188
|
-
unitsConsumed:
|
|
189
|
-
lastUpdated:
|
|
198
|
+
await g(s, {
|
|
199
|
+
unitsConsumed: W(e),
|
|
200
|
+
lastUpdated: F(),
|
|
190
201
|
lastUserId: r
|
|
191
202
|
}, { merge: !0 });
|
|
192
203
|
}
|
|
193
204
|
}
|
|
194
|
-
const
|
|
205
|
+
const E = "apiKeys";
|
|
195
206
|
class tt {
|
|
196
207
|
constructor(t, e, r, s, a) {
|
|
197
208
|
this._auth = t, this._db = b(e), r && A(this._db, s, a);
|
|
@@ -208,7 +219,7 @@ class tt {
|
|
|
208
219
|
async getSignInMethods() {
|
|
209
220
|
const t = this._auth.currentUser;
|
|
210
221
|
if (!t?.email) throw new Error("User has no e-mail");
|
|
211
|
-
return
|
|
222
|
+
return K(this._auth.firebaseAuth, t.email);
|
|
212
223
|
}
|
|
213
224
|
/** Builds a human-readable label from a Firebase UserInfo provider entry. */
|
|
214
225
|
buildProviderLabel(t) {
|
|
@@ -228,38 +239,38 @@ class tt {
|
|
|
228
239
|
}
|
|
229
240
|
/** Unlinks a provider from the current account. */
|
|
230
241
|
async unlinkProvider(t) {
|
|
231
|
-
await
|
|
242
|
+
await $(this._requireUser(), t);
|
|
232
243
|
}
|
|
233
244
|
/** Changes the password. Reauthenticates first. */
|
|
234
245
|
async updatePassword(t, e) {
|
|
235
246
|
const r = this._requireUser();
|
|
236
247
|
if (!r.email) throw new Error("User has no e-mail");
|
|
237
|
-
await
|
|
248
|
+
await f(
|
|
238
249
|
r,
|
|
239
250
|
m.credential(r.email, t)
|
|
240
|
-
), await
|
|
251
|
+
), await q(r, e);
|
|
241
252
|
}
|
|
242
253
|
/** Adds (sets) a password for an account that currently only uses SSO. */
|
|
243
254
|
async addPassword(t) {
|
|
244
255
|
const e = this._requireUser();
|
|
245
256
|
if (!e.email) throw new Error("User has no e-mail");
|
|
246
|
-
await
|
|
257
|
+
await x(e, m.credential(e.email, t));
|
|
247
258
|
}
|
|
248
259
|
/** Requests an e-mail change. Sends a verification e-mail to the new address. */
|
|
249
260
|
async updateEmail(t, e) {
|
|
250
261
|
const r = this._requireUser();
|
|
251
262
|
if (!r.email) throw new Error("User e-mail not available");
|
|
252
|
-
await
|
|
263
|
+
await f(
|
|
253
264
|
r,
|
|
254
265
|
m.credential(r.email, e)
|
|
255
|
-
), await
|
|
266
|
+
), await H(r, t), await L(r);
|
|
256
267
|
}
|
|
257
268
|
/** Creates a new API key for the current user. */
|
|
258
269
|
async createAPIKey(t) {
|
|
259
270
|
const e = this._auth.currentUser?.uid;
|
|
260
271
|
if (!e) throw new Error("User not authenticated");
|
|
261
|
-
const r = { key: `cue-${
|
|
262
|
-
return this._apiKeyDocRef = await z(
|
|
272
|
+
const r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", a = { key: `cue-${Array.from(globalThis.crypto.getRandomValues(new Uint8Array(150)), (o) => r[o & 63]).join("")}`, uid: e, expiration: t }, i = n(this._db, E);
|
|
273
|
+
return this._apiKeyDocRef = await z(i, a), a;
|
|
263
274
|
}
|
|
264
275
|
/** Revokes the current user's API key. */
|
|
265
276
|
async revokeAPIKey() {
|
|
@@ -269,11 +280,11 @@ class tt {
|
|
|
269
280
|
/** Fetches the current user's existing API key. */
|
|
270
281
|
async requestAPIKey() {
|
|
271
282
|
if (!this._apiKeyDocRef) throw new Error("No API key document reference");
|
|
272
|
-
const t = (await
|
|
283
|
+
const t = (await y(this._apiKeyDocRef)).data();
|
|
273
284
|
return { key: t.key, expiration: t.expiration };
|
|
274
285
|
}
|
|
275
286
|
async _checkIfUserHasAPIKey(t) {
|
|
276
|
-
const e =
|
|
287
|
+
const e = n(this._db, E), r = l(e, d("uid", "==", t), J(1)), s = await h(r);
|
|
277
288
|
return s.empty || (this._apiKeyDocRef = s.docs[0].ref), !s.empty;
|
|
278
289
|
}
|
|
279
290
|
_requireUser() {
|
|
@@ -302,7 +313,7 @@ const I = "qaecy-mvp-406413", et = "734737865998", rt = {
|
|
|
302
313
|
firestoreEmulatorPort: 8080
|
|
303
314
|
}
|
|
304
315
|
};
|
|
305
|
-
class
|
|
316
|
+
class ot {
|
|
306
317
|
auth;
|
|
307
318
|
api;
|
|
308
319
|
profile;
|
|
@@ -318,7 +329,7 @@ class nt {
|
|
|
318
329
|
authDomain: `${I}.firebaseapp.com`,
|
|
319
330
|
projectId: I,
|
|
320
331
|
messagingSenderId: et
|
|
321
|
-
}), this.auth = new
|
|
332
|
+
}), this.auth = new V(this._app, this._isEmulator, this._endpoints);
|
|
322
333
|
const r = new Z(this.auth, this._app, this._isEmulator, this._endpoints);
|
|
323
334
|
this.api = this._buildApi(r), this.profile = new tt(
|
|
324
335
|
this.auth,
|
|
@@ -338,9 +349,9 @@ class nt {
|
|
|
338
349
|
}
|
|
339
350
|
}
|
|
340
351
|
export {
|
|
341
|
-
|
|
352
|
+
ot as C,
|
|
342
353
|
X as a,
|
|
343
|
-
|
|
354
|
+
V as b,
|
|
344
355
|
tt as c,
|
|
345
356
|
Z as d
|
|
346
357
|
};
|
package/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export { CueAuth } from './lib/auth';
|
|
|
3
3
|
export { CueApi } from './lib/api';
|
|
4
4
|
export { CueProjects } from './lib/project';
|
|
5
5
|
export { CueProfile } from './lib/profile';
|
|
6
|
-
export type { CueSdkConfig, SsoProvider, PasswordCredentials, SearchRequest, SearchResponse, SearchSource, ProjectData, ProjectSettings, CreateProjectOptions, SyncOptions, SyncResult, ScanOutputRecord, ProfileSSOAccount, APIKeyInfo, APIKeyDoc, } from './lib/models';
|
|
6
|
+
export type { CueSdkConfig, SsoProvider, PasswordCredentials, SearchRequest, SearchResponse, SearchSource, ProjectData, ProjectSettings, CreateProjectOptions, SyncOptions, SyncResult, SyncPreview, ScanOutputRecord, UnitsConsumedDto, ProfileSSOAccount, APIKeyInfo, APIKeyDoc, } from './lib/models';
|
|
7
7
|
export type { AuthStateListener, Unsubscribe } from './lib/auth';
|
package/index.js
CHANGED
package/lib/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CueAuth } from './auth';
|
|
2
|
-
import { SearchRequest, SearchResponse } from './models';
|
|
2
|
+
import { SearchRequest, SearchResponse, UnitsConsumedDto } from './models';
|
|
3
3
|
import { CueProjects } from './project';
|
|
4
4
|
import { CueSyncApi } from './sync';
|
|
5
5
|
export declare class CueApi {
|
|
@@ -24,4 +24,5 @@ export declare class CueApi {
|
|
|
24
24
|
* The user must be authenticated before calling this.
|
|
25
25
|
*/
|
|
26
26
|
sparql(query: string, projectId: string): Promise<unknown>;
|
|
27
|
+
getConsumption(projectId: string, tier?: string): Promise<UnitsConsumedDto>;
|
|
27
28
|
}
|
package/lib/models.d.ts
CHANGED
|
@@ -63,6 +63,8 @@ export interface ProjectSettings {
|
|
|
63
63
|
type: string;
|
|
64
64
|
uri: string;
|
|
65
65
|
};
|
|
66
|
+
/** Processing tier determining credit costs. Defaults to "l" if not set. */
|
|
67
|
+
tier?: 's' | 'm' | 'l';
|
|
66
68
|
}
|
|
67
69
|
export interface ProjectData {
|
|
68
70
|
id: string;
|
|
@@ -85,6 +87,13 @@ export interface CreateProjectOptions {
|
|
|
85
87
|
/** Explicit project ID. Defaults to a new UUID. */
|
|
86
88
|
id?: string;
|
|
87
89
|
}
|
|
90
|
+
export interface SyncProgress {
|
|
91
|
+
percent: number;
|
|
92
|
+
syncCount: number;
|
|
93
|
+
totalCount: number;
|
|
94
|
+
syncSize: number;
|
|
95
|
+
totalSize: number;
|
|
96
|
+
}
|
|
88
97
|
export interface SyncOptions {
|
|
89
98
|
/** The project/space ID to sync into */
|
|
90
99
|
spaceId: string;
|
|
@@ -94,7 +103,14 @@ export interface SyncOptions {
|
|
|
94
103
|
userId: string;
|
|
95
104
|
/** Enable verbose logging */
|
|
96
105
|
verbose?: boolean;
|
|
106
|
+
/** Called whenever upload progress changes */
|
|
107
|
+
onProgress?: (progress: SyncProgress) => void;
|
|
97
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Credit cost table fetched from the public bucket (`unit-credit.json`).
|
|
111
|
+
* Maps file extension → tier → credits per unit.
|
|
112
|
+
*/
|
|
113
|
+
export type UnitCreditMap = Partial<Record<'s' | 'm' | 'l', Record<string, number>>>;
|
|
98
114
|
/** Per-extension cost breakdown returned by {@link CueSyncApi.scanCost}. */
|
|
99
115
|
export interface ScanOutputRecord {
|
|
100
116
|
/** File extension (without leading dot), e.g. `"pdf"`, `"ifc"`. */
|
|
@@ -109,6 +125,35 @@ export interface ScanOutputRecord {
|
|
|
109
125
|
units: number;
|
|
110
126
|
/** Total size of files with this extension in megabytes. */
|
|
111
127
|
sizeMb: number;
|
|
128
|
+
/** Credits to consume for this extension (units × credit-per-unit for the project tier). */
|
|
129
|
+
credits?: number;
|
|
130
|
+
}
|
|
131
|
+
export interface UnitsConsumedDto {
|
|
132
|
+
creditsAvailable: number;
|
|
133
|
+
creditsConsumed: number;
|
|
134
|
+
filesProcessed: number;
|
|
135
|
+
unitsAvailable: number;
|
|
136
|
+
unitsConsumed: number;
|
|
137
|
+
}
|
|
138
|
+
export interface SyncPreview {
|
|
139
|
+
/** Per-extension cost breakdown for files not yet synced */
|
|
140
|
+
costRecords: ScanOutputRecord[];
|
|
141
|
+
/** Project tier used for cost calculation */
|
|
142
|
+
tier: 's' | 'm' | 'l';
|
|
143
|
+
/** Human-readable tier name from tier-names.json */
|
|
144
|
+
tierName: string;
|
|
145
|
+
/** Total units required for the new files */
|
|
146
|
+
unitsToConsume: number;
|
|
147
|
+
/** Total credits to consume (derived from unit-credit.json + project tier) */
|
|
148
|
+
creditsToConsume: number;
|
|
149
|
+
/** Credits currently available for this project */
|
|
150
|
+
creditsAvailable: number;
|
|
151
|
+
/** Units still available for this project */
|
|
152
|
+
unitsAvailable: number;
|
|
153
|
+
/** Number of new files that would be uploaded */
|
|
154
|
+
filesToUpload: number;
|
|
155
|
+
/** Total local files scanned */
|
|
156
|
+
totalLocalFiles: number;
|
|
112
157
|
}
|
|
113
158
|
export interface SyncResult {
|
|
114
159
|
/** Number of files successfully synced in this run */
|
|
@@ -123,8 +168,8 @@ export interface SyncResult {
|
|
|
123
168
|
totalSize: number;
|
|
124
169
|
/** Whether any RDF metadata was written */
|
|
125
170
|
rdfWritten: boolean;
|
|
126
|
-
/**
|
|
127
|
-
|
|
171
|
+
/** Updated credit balance after sync, fetched from the consumption endpoint */
|
|
172
|
+
creditsAvailable: number;
|
|
128
173
|
}
|
|
129
174
|
export interface ProfileSSOAccount {
|
|
130
175
|
id: string;
|
package/lib/sync.d.ts
CHANGED
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
import { CueBlobStorage } from 'js-databases';
|
|
2
2
|
import { LocalFile } from 'js-sync-tools';
|
|
3
3
|
import { CueAuth } from './auth';
|
|
4
|
+
import { CueApi } from './api';
|
|
4
5
|
import { CueProjects } from './project';
|
|
5
|
-
import { ScanOutputRecord, SyncOptions, SyncResult } from './models';
|
|
6
|
+
import { ScanOutputRecord, SyncOptions, SyncPreview, SyncResult } from './models';
|
|
6
7
|
export declare class CueSyncApi {
|
|
7
8
|
private readonly _auth;
|
|
8
9
|
private readonly _projects;
|
|
9
10
|
private readonly _blob;
|
|
10
11
|
private readonly _gatewayUrl;
|
|
11
|
-
private readonly _serviceId;
|
|
12
|
-
private readonly _rdfBase;
|
|
13
12
|
private readonly _graphMap;
|
|
14
|
-
|
|
13
|
+
private _api?;
|
|
14
|
+
private _pendingItems;
|
|
15
|
+
private _pendingSpaceId;
|
|
16
|
+
private _flushTimer;
|
|
17
|
+
constructor(_auth: CueAuth, _projects: CueProjects, _blob: CueBlobStorage, _gatewayUrl: string);
|
|
18
|
+
/** @internal Injected by CueApi after construction to avoid circular dependency. */
|
|
19
|
+
_bindApi(api: CueApi): void;
|
|
20
|
+
/**
|
|
21
|
+
* Flushes any pending metadata items from a previous interrupted sync.
|
|
22
|
+
* Safe to call even when there is nothing new to upload.
|
|
23
|
+
*/
|
|
24
|
+
flushPendingMetadata(spaceId: string, verbose?: boolean): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Returns a preview of what would be synced: cost breakdown for new files only,
|
|
27
|
+
* units required, and units still available. Use this before calling {@link sync}
|
|
28
|
+
* to show the user an accurate cost estimate.
|
|
29
|
+
*/
|
|
30
|
+
previewSync(localFiles: LocalFile[], options: SyncOptions): Promise<SyncPreview>;
|
|
15
31
|
sync(localFiles: LocalFile[], options: SyncOptions): Promise<SyncResult>;
|
|
16
32
|
private _getOrCreateGraph;
|
|
17
33
|
private _listRemoteFiles;
|
|
18
34
|
private _getGraphFiles;
|
|
19
|
-
private
|
|
35
|
+
private _initPendingBatch;
|
|
36
|
+
private _queueFileLocation;
|
|
37
|
+
private _drainPending;
|
|
38
|
+
private _flushBatch;
|
|
39
|
+
private _postFssBatch;
|
|
40
|
+
private _stopFlushTimer;
|
|
20
41
|
/**
|
|
21
42
|
* Scans `localFiles` and returns a per-extension cost breakdown.
|
|
22
43
|
*
|
|
@@ -25,5 +46,7 @@ export declare class CueSyncApi {
|
|
|
25
46
|
* shown to the user before or after calling {@link sync}.
|
|
26
47
|
*/
|
|
27
48
|
scanCost(localFiles: LocalFile[]): Promise<ScanOutputRecord[]>;
|
|
49
|
+
private _fetchTierNames;
|
|
50
|
+
private _fetchUnitCreditMap;
|
|
28
51
|
private _logProgress;
|
|
29
52
|
}
|
package/node.js
CHANGED
|
@@ -1,161 +1,234 @@
|
|
|
1
|
-
import { C as
|
|
2
|
-
import { b as
|
|
3
|
-
import { getStorage as
|
|
4
|
-
import { CueGraphDatabase as
|
|
5
|
-
import { readFile as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { C as z, a as D } from "./cue-BA1pOKTu.js";
|
|
2
|
+
import { b as yt, c as Pt, d as Et } from "./cue-BA1pOKTu.js";
|
|
3
|
+
import { getStorage as U, connectStorageEmulator as A } from "firebase/storage";
|
|
4
|
+
import { CueGraphDatabase as B, CueBlobStorage as x } from "js-databases";
|
|
5
|
+
import { readFile as b, writeFile as L, unlink as k } from "fs/promises";
|
|
6
|
+
import { join as S } from "path";
|
|
7
|
+
import { pathToFileURL as H } from "url";
|
|
8
|
+
import { tmpdir as G } from "os";
|
|
8
9
|
import { compareLocalRemote as j } from "js-sync-tools";
|
|
9
|
-
import { uploadedFileMetadata as
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/* @vite-ignore */
|
|
16
|
-
t
|
|
17
|
-
);
|
|
18
|
-
await e.default({ module_or_path: o }), q = e.scan, A = e.scan_file_map;
|
|
10
|
+
import { uploadedFileMetadata as J } from "js-file-metadata-helpers";
|
|
11
|
+
import { qaecyPrefixes as K } from "js/prefixes";
|
|
12
|
+
let N = null, I = null;
|
|
13
|
+
async function W() {
|
|
14
|
+
const u = S(__dirname, "assets", "wasm"), t = await b(S(u, "dir_scanner_wasm_bg.wasm")), e = await import(H(S(u, "dir_scanner_wasm.mjs")).href);
|
|
15
|
+
await e.default({ module_or_path: t }), N = e.scan;
|
|
19
16
|
}
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
const Y = "fuseki", Q = "/triplestore/query", V = "/sparql/query", X = "/triplestore/update", Z = "/sparql/update", tt = "/commands/file-system-structure/batch", O = 1e3, F = "cue:pending:";
|
|
18
|
+
function v(u) {
|
|
19
|
+
return S(G(), `cue-sync-pending-${u}.json`);
|
|
20
|
+
}
|
|
21
|
+
async function R(u) {
|
|
22
|
+
if (typeof window < "u") {
|
|
23
|
+
const t = window.localStorage.getItem(`${F}${u}`);
|
|
24
|
+
return t ? JSON.parse(t) : null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const t = await b(v(u), "utf-8");
|
|
28
|
+
return JSON.parse(t);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function q(u) {
|
|
34
|
+
const t = JSON.stringify(u);
|
|
35
|
+
if (typeof window < "u") {
|
|
36
|
+
window.localStorage.setItem(`${F}${u.spaceId}`, t);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
await L(v(u.spaceId), t, "utf-8");
|
|
40
|
+
}
|
|
41
|
+
async function et(u) {
|
|
42
|
+
if (typeof window < "u") {
|
|
43
|
+
window.localStorage.removeItem(`${F}${u}`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await k(v(u));
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
class it {
|
|
52
|
+
constructor(t, o, e, n) {
|
|
53
|
+
this._auth = t, this._projects = o, this._blob = e, this._gatewayUrl = n;
|
|
24
54
|
}
|
|
25
55
|
_auth;
|
|
26
56
|
_projects;
|
|
27
57
|
_blob;
|
|
28
58
|
_gatewayUrl;
|
|
29
|
-
_serviceId;
|
|
30
|
-
_rdfBase;
|
|
31
59
|
_graphMap = /* @__PURE__ */ new Map();
|
|
60
|
+
_api;
|
|
61
|
+
_pendingItems = [];
|
|
62
|
+
_pendingSpaceId = null;
|
|
63
|
+
_flushTimer = null;
|
|
64
|
+
/** @internal Injected by CueApi after construction to avoid circular dependency. */
|
|
65
|
+
_bindApi(t) {
|
|
66
|
+
this._api = t;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Flushes any pending metadata items from a previous interrupted sync.
|
|
70
|
+
* Safe to call even when there is nothing new to upload.
|
|
71
|
+
*/
|
|
72
|
+
async flushPendingMetadata(t, o) {
|
|
73
|
+
const e = await this._auth.getToken();
|
|
74
|
+
if (!e) throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
75
|
+
const n = await R(t);
|
|
76
|
+
if (!(!n || n.items.length === 0)) {
|
|
77
|
+
console.info(`Trying to upload metadata (${n.items.length} item(s))...`), o && console.info(`Flushing ${n.items.length} pending file location(s) from previous sync ⏳`);
|
|
78
|
+
try {
|
|
79
|
+
this._pendingSpaceId = t, this._pendingItems = [], await this._flushBatch(n.items, t, e, o), console.info("Metadata uploaded ✅");
|
|
80
|
+
} catch (s) {
|
|
81
|
+
throw new Error(`METADATA_SYNC_FAILED: ${s instanceof Error ? s.message : String(s)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns a preview of what would be synced: cost breakdown for new files only,
|
|
87
|
+
* units required, and units still available. Use this before calling {@link sync}
|
|
88
|
+
* to show the user an accurate cost estimate.
|
|
89
|
+
*/
|
|
90
|
+
async previewSync(t, o) {
|
|
91
|
+
const { spaceId: e, providerId: n, verbose: s } = o, i = await this._auth.getToken();
|
|
92
|
+
if (!i) throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
93
|
+
const r = await this._getOrCreateGraph(e, i), c = (await this._projects.getProject(e))?.projectSettings?.tier ?? "l", [p, g, m, C] = await Promise.all([
|
|
94
|
+
this._listRemoteFiles(r, e, n, s),
|
|
95
|
+
this._api?.getConsumption(e, c) ?? Promise.reject(new Error("CueSyncApi is not bound to a CueApi instance")),
|
|
96
|
+
this._fetchUnitCreditMap(s),
|
|
97
|
+
this._fetchTierNames()
|
|
98
|
+
]), l = (await j(t, p)).localNotOnRemote ?? [], _ = l.length > 0 ? await this.scanCost(l) : [];
|
|
99
|
+
let w = 0, y = 0;
|
|
100
|
+
for (const f of _) {
|
|
101
|
+
w += f.units;
|
|
102
|
+
const E = m[c], h = E?.[f.ext] ?? 1;
|
|
103
|
+
s && E && !(f.ext in E) && console.info(` Unknown format: .${f.ext} (using default rate of 1 credit/unit)`);
|
|
104
|
+
const d = f.units * h;
|
|
105
|
+
y += d, f.credits = Math.round(d);
|
|
106
|
+
}
|
|
107
|
+
const P = C[c] ?? c;
|
|
108
|
+
return {
|
|
109
|
+
costRecords: _,
|
|
110
|
+
tier: c,
|
|
111
|
+
tierName: P,
|
|
112
|
+
unitsToConsume: w,
|
|
113
|
+
creditsToConsume: Math.round(y),
|
|
114
|
+
creditsAvailable: g.creditsAvailable,
|
|
115
|
+
unitsAvailable: g.unitsAvailable,
|
|
116
|
+
filesToUpload: l.length,
|
|
117
|
+
totalLocalFiles: t.length
|
|
118
|
+
};
|
|
119
|
+
}
|
|
32
120
|
async sync(t, o) {
|
|
33
|
-
const { spaceId: e, providerId:
|
|
34
|
-
if (!
|
|
35
|
-
const c = await this._getOrCreateGraph(e,
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
121
|
+
const { spaceId: e, providerId: n, userId: s, verbose: i, onProgress: r } = o, a = await this._auth.getToken();
|
|
122
|
+
if (!a) throw new Error("Not authenticated. Call cue.auth.signIn() first.");
|
|
123
|
+
const c = await this._getOrCreateGraph(e, a);
|
|
124
|
+
i && console.info("Listing remote files ⏳");
|
|
125
|
+
const g = (await this._projects.getProject(e))?.projectSettings?.tier ?? "l", [m, C] = await Promise.all([
|
|
126
|
+
this._listRemoteFiles(c, e, n, i),
|
|
127
|
+
this._api?.getConsumption(e, g) ?? Promise.reject(new Error("CueSyncApi is not bound to a CueApi instance"))
|
|
128
|
+
]), { unitsAvailable: T } = C, l = await j(t, m);
|
|
129
|
+
i && (console.info(`Total local files: ${t.length}`), console.info(`Total remote files: ${m.length}`), console.info(
|
|
130
|
+
`Total files to sync: ${(l.localNotOnRemote?.length ?? 0) + l.localNotOnRemotePathOnly.length}`
|
|
40
131
|
));
|
|
41
|
-
let
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
P = A(r);
|
|
51
|
-
} catch (r) {
|
|
52
|
-
console.warn("[CueSyncApi] Unit map failed, proceeding without unit tracking:", r);
|
|
132
|
+
let _ = l.syncCount, w = l.syncSize, y = 0, P = !1;
|
|
133
|
+
const f = l.localNotOnRemote ?? [];
|
|
134
|
+
if (f.length > 0) {
|
|
135
|
+
const d = (await this.scanCost(f)).reduce(($, M) => $ + M.units, 0);
|
|
136
|
+
if (d > T)
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Insufficient units: ${d} units required but only ${T} available.`
|
|
139
|
+
);
|
|
53
140
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
for (const r of E) {
|
|
57
|
-
let h = !1;
|
|
141
|
+
await this._initPendingBatch(e, a, i), i && f.length && console.info("Syncing missing files ⏳");
|
|
142
|
+
for (const h of f)
|
|
58
143
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
144
|
+
const d = J(
|
|
145
|
+
h.relativePath,
|
|
61
146
|
e,
|
|
62
147
|
s,
|
|
63
|
-
|
|
64
|
-
|
|
148
|
+
h.md5,
|
|
149
|
+
n
|
|
65
150
|
);
|
|
66
|
-
if (!
|
|
67
|
-
const
|
|
151
|
+
if (!d.blob_name) throw new Error(`blob_name missing for ${h.relativePath}`);
|
|
152
|
+
const $ = await b(h.fullPath);
|
|
68
153
|
await this._blob.uploadRaw(
|
|
69
|
-
|
|
70
|
-
new Uint8Array(
|
|
71
|
-
|
|
72
|
-
), await this.
|
|
73
|
-
|
|
74
|
-
|
|
154
|
+
d.blob_name,
|
|
155
|
+
new Uint8Array($),
|
|
156
|
+
d
|
|
157
|
+
), await this._queueFileLocation({
|
|
158
|
+
relativePath: h.relativePath,
|
|
159
|
+
md5: h.md5,
|
|
160
|
+
size: h.size,
|
|
161
|
+
providerId: n,
|
|
162
|
+
fileContentExists: !1
|
|
163
|
+
}), P = !0, _ += 1, w += h.size || 0, this._logProgress(_, l.totalCount, w, l.totalSize, r);
|
|
164
|
+
} catch (d) {
|
|
165
|
+
y += 1, console.error(`[CueSyncApi] Failed to upload file: ${h.fullPath}`), i && console.error("[CueSyncApi] Upload error details:", d);
|
|
75
166
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
a && n.localNotOnRemotePathOnly.length && console.info(`Syncing missing file locations (on provider "${i}") ⏳`);
|
|
89
|
-
for (const r of n.localNotOnRemotePathOnly) {
|
|
90
|
-
const h = S(
|
|
91
|
-
r.relativePath,
|
|
92
|
-
e,
|
|
93
|
-
s,
|
|
94
|
-
r.md5,
|
|
95
|
-
i
|
|
96
|
-
);
|
|
97
|
-
if (!h.blob_name) throw new Error(`blob_name missing for ${r.relativePath}`);
|
|
98
|
-
await this._uploadRdfMetadata(r, h, a) && (g = !0), u += 1, m += r.size || 0;
|
|
99
|
-
const d = P[r.relativePath] ?? 0;
|
|
100
|
-
if (d > 0) {
|
|
101
|
-
y += d;
|
|
102
|
-
try {
|
|
103
|
-
await this._projects.incrementUnitsConsumed(e, d, s);
|
|
104
|
-
} catch ($) {
|
|
105
|
-
console.error(`[CueSyncApi] Failed to write clientSync entry for ${r.relativePath}:`, $);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
this._logProgress(u, n.totalCount, m, n.totalSize, a);
|
|
109
|
-
}
|
|
167
|
+
i && l.localNotOnRemotePathOnly.length && console.info(`Syncing missing file locations (on provider "${n}") ⏳`);
|
|
168
|
+
for (const h of l.localNotOnRemotePathOnly)
|
|
169
|
+
await this._queueFileLocation({
|
|
170
|
+
relativePath: h.relativePath,
|
|
171
|
+
md5: h.md5,
|
|
172
|
+
size: h.size,
|
|
173
|
+
providerId: n,
|
|
174
|
+
fileContentExists: !0
|
|
175
|
+
}), P = !0, _ += 1, w += h.size || 0, this._logProgress(_, l.totalCount, w, l.totalSize, r);
|
|
176
|
+
await this._drainPending(i), this._stopFlushTimer();
|
|
177
|
+
const E = await (this._api?.getConsumption(e, g) ?? Promise.resolve({ creditsAvailable: 0 }));
|
|
110
178
|
return {
|
|
111
|
-
syncCount:
|
|
112
|
-
syncSize:
|
|
113
|
-
failedUploads:
|
|
114
|
-
totalCount:
|
|
115
|
-
totalSize:
|
|
116
|
-
rdfWritten:
|
|
117
|
-
|
|
179
|
+
syncCount: _,
|
|
180
|
+
syncSize: w,
|
|
181
|
+
failedUploads: y,
|
|
182
|
+
totalCount: l.totalCount,
|
|
183
|
+
totalSize: l.totalSize,
|
|
184
|
+
rdfWritten: P,
|
|
185
|
+
creditsAvailable: E.creditsAvailable
|
|
118
186
|
};
|
|
119
187
|
}
|
|
120
188
|
async _getOrCreateGraph(t, o) {
|
|
121
189
|
const e = this._graphMap.get(t);
|
|
122
190
|
if (e) return e;
|
|
123
|
-
const s = (await this._projects.getProject(t))?.projectSettings?.graph?.type ??
|
|
191
|
+
const s = (await this._projects.getProject(t))?.projectSettings?.graph?.type ?? Y, i = s === "qlever" ? `${this._gatewayUrl}${V}` : `${this._gatewayUrl}${Q}`, r = s === "qlever" ? `${this._gatewayUrl}${Z}` : `${this._gatewayUrl}${X}`, a = new B({
|
|
124
192
|
graphType: s,
|
|
125
|
-
queryEndpoint:
|
|
126
|
-
updateEndpoint:
|
|
193
|
+
queryEndpoint: i,
|
|
194
|
+
updateEndpoint: r,
|
|
127
195
|
originalHeaders: {
|
|
128
196
|
"x-project-id": t,
|
|
129
197
|
authorization: `Bearer ${o}`
|
|
130
198
|
}
|
|
131
199
|
});
|
|
132
|
-
return this._graphMap.set(t,
|
|
200
|
+
return this._graphMap.set(t, a), a;
|
|
133
201
|
}
|
|
134
|
-
async _listRemoteFiles(t, o, e,
|
|
135
|
-
|
|
136
|
-
const
|
|
202
|
+
async _listRemoteFiles(t, o, e, n) {
|
|
203
|
+
n && console.info(`Listing files in raw space: ${o}`);
|
|
204
|
+
const s = Promise.race([
|
|
205
|
+
this._getGraphFiles(t, e),
|
|
206
|
+
new Promise(
|
|
207
|
+
(c, p) => setTimeout(() => p(new Error("GRAPH_TIMEOUT: Knowledge graph query timed out")), 15e3)
|
|
208
|
+
)
|
|
209
|
+
]), [i, r] = await Promise.all([
|
|
137
210
|
this._blob.listRaw(o),
|
|
138
|
-
|
|
211
|
+
s
|
|
139
212
|
]);
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
for (const c of
|
|
143
|
-
const p = c.substring(0, 36),
|
|
144
|
-
if (
|
|
145
|
-
|
|
213
|
+
n && console.info(`Found ${i.length} files in raw store for space: ${o}`);
|
|
214
|
+
const a = [];
|
|
215
|
+
for (const c of i) {
|
|
216
|
+
const p = c.substring(0, 36), g = r[p] ?? [];
|
|
217
|
+
if (g.length === 0)
|
|
218
|
+
n && console.warn(`No location data found for contentUUID: ${p}`), a.push({ contentUUID: p });
|
|
146
219
|
else
|
|
147
|
-
for (const
|
|
148
|
-
|
|
220
|
+
for (const m of g)
|
|
221
|
+
a.push({
|
|
149
222
|
contentUUID: p,
|
|
150
|
-
locationUUID:
|
|
151
|
-
created:
|
|
152
|
-
size:
|
|
223
|
+
locationUUID: m.locationUUID,
|
|
224
|
+
created: m.created,
|
|
225
|
+
size: m.size
|
|
153
226
|
});
|
|
154
227
|
}
|
|
155
|
-
return
|
|
228
|
+
return a;
|
|
156
229
|
}
|
|
157
230
|
async _getGraphFiles(t, o) {
|
|
158
|
-
const e = `PREFIX qcy: <${
|
|
231
|
+
const e = `PREFIX qcy: <${K.qcy}>
|
|
159
232
|
SELECT ?fc ?loc ?created ?fp ?size
|
|
160
233
|
WHERE {
|
|
161
234
|
?fc a qcy:FileContent ;
|
|
@@ -164,30 +237,79 @@ WHERE {
|
|
|
164
237
|
?loc qcy:remoteProviderId "${o}" ;
|
|
165
238
|
qcy:dateCreated ?created ;
|
|
166
239
|
qcy:filePath ?fp .
|
|
167
|
-
}`,
|
|
168
|
-
if (typeof
|
|
169
|
-
for (const
|
|
170
|
-
const
|
|
171
|
-
s[
|
|
240
|
+
}`, n = await t.query(e, "application/sparql-results+json"), s = {};
|
|
241
|
+
if (typeof n == "string") return s;
|
|
242
|
+
for (const i of n.results.bindings) {
|
|
243
|
+
const r = i.loc.value.split("/").at(-1) ?? "", a = i.fc.value.split("/").at(-1) ?? "", c = i.created.value, p = i.size.value;
|
|
244
|
+
s[a] || (s[a] = []), s[a].push({ locationUUID: r, created: c, size: p });
|
|
172
245
|
}
|
|
173
246
|
return s;
|
|
174
247
|
}
|
|
175
|
-
async
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
248
|
+
async _initPendingBatch(t, o, e) {
|
|
249
|
+
this._pendingSpaceId = t, this._pendingItems = [];
|
|
250
|
+
const n = await R(t);
|
|
251
|
+
if (n && n.items.length > 0) {
|
|
252
|
+
console.info(`Trying to upload metadata from interrupted sync (${n.items.length} item(s))...`), e && console.info(`Flushing ${n.items.length} pending file location(s) from previous sync ⏳`);
|
|
253
|
+
try {
|
|
254
|
+
await this._flushBatch(n.items, t, o, e), console.info("Metadata uploaded ✅");
|
|
255
|
+
} catch (i) {
|
|
256
|
+
throw new Error(`METADATA_SYNC_FAILED: ${i instanceof Error ? i.message : String(i)}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const s = setInterval(() => {
|
|
260
|
+
this._drainPending(e).catch(
|
|
261
|
+
(i) => console.error("[CueSyncApi] Periodic flush failed:", i)
|
|
262
|
+
);
|
|
263
|
+
}, 6e4);
|
|
264
|
+
typeof s == "object" && typeof s.unref == "function" && s.unref(), this._flushTimer = s;
|
|
265
|
+
}
|
|
266
|
+
async _queueFileLocation(t) {
|
|
267
|
+
this._pendingItems.push(t), this._pendingSpaceId && await q({ spaceId: this._pendingSpaceId, items: this._pendingItems });
|
|
268
|
+
}
|
|
269
|
+
async _drainPending(t) {
|
|
270
|
+
if (!this._pendingSpaceId || this._pendingItems.length === 0) return;
|
|
271
|
+
const o = await this._auth.getToken();
|
|
272
|
+
o && await this._flushBatch(this._pendingItems, this._pendingSpaceId, o, t);
|
|
273
|
+
}
|
|
274
|
+
async _flushBatch(t, o, e, n) {
|
|
275
|
+
const s = [...t];
|
|
276
|
+
this._pendingSpaceId === o && (this._pendingItems = []), await et(o);
|
|
277
|
+
try {
|
|
278
|
+
for (let i = 0; i < s.length; i += O)
|
|
279
|
+
await this._postFssBatch(s.slice(i, i + O), o, e);
|
|
280
|
+
n && console.info(`Wrote ${s.length} file location(s) to commands API ✅`);
|
|
281
|
+
} catch (i) {
|
|
282
|
+
const r = [...s, ...this._pendingItems];
|
|
283
|
+
throw this._pendingItems = r, await q({ spaceId: o, items: r }), i;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async _postFssBatch(t, o, e) {
|
|
287
|
+
const n = new AbortController(), s = setTimeout(() => n.abort(), 15e3);
|
|
288
|
+
let i;
|
|
289
|
+
try {
|
|
290
|
+
i = await fetch(`${this._gatewayUrl}${tt}`, {
|
|
291
|
+
method: "POST",
|
|
292
|
+
headers: {
|
|
293
|
+
Authorization: `Bearer ${e}`,
|
|
294
|
+
"Content-Type": "application/json",
|
|
295
|
+
"x-project-id": o
|
|
296
|
+
},
|
|
297
|
+
body: JSON.stringify({ items: t }),
|
|
298
|
+
signal: n.signal
|
|
299
|
+
});
|
|
300
|
+
} catch (r) {
|
|
301
|
+
const a = r instanceof Error && r.name === "AbortError";
|
|
302
|
+
throw new Error(`File structure batch POST failed: ${a ? "request timed out" : r instanceof Error ? r.message : String(r)}`);
|
|
303
|
+
} finally {
|
|
304
|
+
clearTimeout(s);
|
|
305
|
+
}
|
|
306
|
+
if (!i.ok) {
|
|
307
|
+
const r = await i.text().catch(() => "");
|
|
308
|
+
throw new Error(`File structure batch POST failed: ${i.status} ${i.statusText}${r ? ` — ${r}` : ""}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
_stopFlushTimer() {
|
|
312
|
+
this._flushTimer !== null && (clearInterval(this._flushTimer), this._flushTimer = null);
|
|
191
313
|
}
|
|
192
314
|
/**
|
|
193
315
|
* Scans `localFiles` and returns a per-extension cost breakdown.
|
|
@@ -197,44 +319,71 @@ WHERE {
|
|
|
197
319
|
* shown to the user before or after calling {@link sync}.
|
|
198
320
|
*/
|
|
199
321
|
async scanCost(t) {
|
|
200
|
-
|
|
201
|
-
const o =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
);
|
|
322
|
+
if (I || (I = W()), await I, !N) throw new Error("WASM scan function not initialised");
|
|
323
|
+
const o = 200, e = /* @__PURE__ */ new Map();
|
|
324
|
+
for (let n = 0; n < t.length; n += o) {
|
|
325
|
+
const s = t.slice(n, n + o), i = await Promise.all(
|
|
326
|
+
s.map(async (a) => ({
|
|
327
|
+
originalPath: a.relativePath,
|
|
328
|
+
data: new Uint8Array(await b(a.fullPath))
|
|
329
|
+
}))
|
|
330
|
+
), r = N(i);
|
|
331
|
+
for (const a of r) {
|
|
332
|
+
const c = e.get(a.ext);
|
|
333
|
+
c ? (c.count += a.count, c.units += a.units, c.sizeMb += a.sizeMb) : e.set(a.ext, { ...a });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return Array.from(e.values());
|
|
337
|
+
}
|
|
338
|
+
async _fetchTierNames() {
|
|
339
|
+
try {
|
|
340
|
+
const t = await this._blob.downloadPublic("tier-names.json");
|
|
341
|
+
return JSON.parse(t);
|
|
342
|
+
} catch {
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async _fetchUnitCreditMap(t) {
|
|
347
|
+
let o;
|
|
348
|
+
try {
|
|
349
|
+
o = await this._blob.downloadPublic("unit-credit.json");
|
|
350
|
+
} catch (e) {
|
|
351
|
+
throw new Error(`Couldn't fetch credits table: ${e instanceof Error ? e.message : String(e)}`);
|
|
352
|
+
}
|
|
353
|
+
t && console.info(`Price file: ${o.length} bytes`);
|
|
354
|
+
try {
|
|
355
|
+
return JSON.parse(o);
|
|
356
|
+
} catch {
|
|
357
|
+
throw new Error(`Credits table is not valid JSON (${o.length} bytes)`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
_logProgress(t, o, e, n, s) {
|
|
361
|
+
if (!s || o === 0) return;
|
|
362
|
+
const i = Math.floor(t / o * 100);
|
|
363
|
+
s({ percent: i, syncCount: t, totalCount: o, syncSize: e, totalSize: n });
|
|
215
364
|
}
|
|
216
365
|
}
|
|
217
|
-
const
|
|
218
|
-
class
|
|
366
|
+
const ot = "spaces_raw_eu_west6", nt = "spaces_processed_eu_west6", st = "cue_public_eu_west6";
|
|
367
|
+
class mt extends z {
|
|
219
368
|
constructor(t) {
|
|
220
369
|
super(t);
|
|
221
370
|
}
|
|
222
371
|
_buildApi(t) {
|
|
223
|
-
const o =
|
|
372
|
+
const o = U(this._app, ot), e = U(this._app, nt), n = U(this._app, st);
|
|
224
373
|
if (this._isEmulator) {
|
|
225
|
-
const a = this._endpoints.storageEmulatorHost,
|
|
226
|
-
|
|
374
|
+
const a = this._endpoints.storageEmulatorHost, c = this._endpoints.storageEmulatorPort;
|
|
375
|
+
A(o, a, c), A(e, a, c), A(n, a, c);
|
|
227
376
|
}
|
|
228
|
-
const
|
|
229
|
-
return
|
|
377
|
+
const s = new x({ storageRaw: o, storageProcessed: e, storagePublic: n }), i = new it(this.auth, t, s, this._endpoints.gatewayUrl), r = new D(this.auth, this._endpoints.gatewayUrl, t, i);
|
|
378
|
+
return i._bindApi(r), r;
|
|
230
379
|
}
|
|
231
380
|
}
|
|
232
381
|
export {
|
|
233
|
-
|
|
382
|
+
z as Cue,
|
|
234
383
|
D as CueApi,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
384
|
+
yt as CueAuth,
|
|
385
|
+
mt as CueNode,
|
|
386
|
+
Pt as CueProfile,
|
|
387
|
+
Et as CueProjects,
|
|
388
|
+
it as CueSyncApi
|
|
240
389
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qaecy/cue-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -14,9 +14,6 @@
|
|
|
14
14
|
"types": "./node.d.ts"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
|
-
"dependencies": {
|
|
18
|
-
"dir-scanner-wasm": "file:../../../data/dir-scanner-wasm-0.1.0.tgz"
|
|
19
|
-
},
|
|
20
17
|
"peerDependencies": {
|
|
21
18
|
"firebase": ">=10.0.0"
|
|
22
19
|
}
|