@ai4b-team/fsaos-gateway-sdk 3.34.1 → 3.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/iife/gateway.js +5 -5
- package/dist/iife/gateway.js.map +4 -4
- package/dist/iife/ui.js +1 -1
- package/dist/index.cjs +331 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -67
- package/dist/index.d.ts +85 -67
- package/dist/index.js +328 -140
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -101,6 +101,7 @@ __export(src_exports, {
|
|
|
101
101
|
initSession: () => initSession,
|
|
102
102
|
initVfsRealtimeWs: () => initVfsRealtimeWs,
|
|
103
103
|
initializeFsaos: () => initializeFsaos,
|
|
104
|
+
initializeWorkspace: () => initializeWorkspace,
|
|
104
105
|
interpretSchema: () => interpretSchema,
|
|
105
106
|
invalidateAllVfs: () => invalidateAllVfs,
|
|
106
107
|
invalidateChannelMessages: () => invalidateChannelMessages,
|
|
@@ -116,6 +117,7 @@ __export(src_exports, {
|
|
|
116
117
|
isScopeReady: () => isScopeReady,
|
|
117
118
|
itemsFilterFromQueryFilter: () => itemsFilterFromQueryFilter,
|
|
118
119
|
listChildren: () => listChildren,
|
|
120
|
+
loadMounts: () => loadMounts,
|
|
119
121
|
mount: () => mount2,
|
|
120
122
|
mountRealtimeScope: () => mountRealtimeScope,
|
|
121
123
|
normalizeItem: () => normalizeItem,
|
|
@@ -127,9 +129,11 @@ __export(src_exports, {
|
|
|
127
129
|
readItem: () => readItem,
|
|
128
130
|
refreshQuery: () => refreshQuery,
|
|
129
131
|
registerCleanup: () => registerCleanup,
|
|
132
|
+
replaceMounts: () => replaceMounts,
|
|
130
133
|
resetAllSdkState: () => resetAllSdkState,
|
|
131
134
|
resolvePrincipalId: () => resolvePrincipalId,
|
|
132
135
|
resubscribePath: () => resubscribePath,
|
|
136
|
+
selectMount: () => selectMount,
|
|
133
137
|
serverDeadlinePassed: () => serverDeadlinePassed,
|
|
134
138
|
serverMsUntil: () => serverMsUntil,
|
|
135
139
|
serverNow: () => serverNow,
|
|
@@ -335,7 +339,7 @@ function notifyAnchorListeners() {
|
|
|
335
339
|
}
|
|
336
340
|
function setAnchor(ref) {
|
|
337
341
|
const path = typeof ref === "string" ? ref : ref.path;
|
|
338
|
-
const changed = currentAnchorPath !== path;
|
|
342
|
+
const changed = currentAnchorPath !== path || currentAnchorRef?.id !== (typeof ref === "string" ? void 0 : ref.id);
|
|
339
343
|
currentAnchorPath = path;
|
|
340
344
|
currentAnchorRef = typeof ref === "string" ? null : ref;
|
|
341
345
|
if (typeof ref !== "string" && !mountSet.some((m) => m.id === ref.id)) {
|
|
@@ -372,13 +376,13 @@ function isAnchorReady() {
|
|
|
372
376
|
return anchorResolved;
|
|
373
377
|
}
|
|
374
378
|
var homeAnchor = null;
|
|
375
|
-
function electHomeAnchor(ref) {
|
|
379
|
+
function electHomeAnchor(ref, options = {}) {
|
|
376
380
|
if (homeAnchor) {
|
|
377
381
|
console.warn("[SDK] electHomeAnchor called twice; ignoring (logout to re-elect).");
|
|
378
382
|
return;
|
|
379
383
|
}
|
|
380
384
|
homeAnchor = ref;
|
|
381
|
-
setAnchor(ref);
|
|
385
|
+
if (options.activate !== false) setAnchor(ref);
|
|
382
386
|
}
|
|
383
387
|
function getHomeAnchor() {
|
|
384
388
|
return homeAnchor;
|
|
@@ -408,32 +412,33 @@ function subscribeMounts(listener) {
|
|
|
408
412
|
mountListeners.delete(listener);
|
|
409
413
|
};
|
|
410
414
|
}
|
|
411
|
-
function
|
|
415
|
+
function commitMountSelection(mounts, anchor) {
|
|
416
|
+
const mountsChanged = JSON.stringify(mountSet) !== JSON.stringify(mounts);
|
|
417
|
+
const anchorChanged = currentAnchorPath !== anchor.path || currentAnchorRef?.id !== anchor.id;
|
|
412
418
|
mountSet = mounts;
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
notifyMountListeners();
|
|
419
|
-
}
|
|
420
|
-
function removeMountState(id) {
|
|
421
|
-
const removed = mountSet.find((m) => m.id === id);
|
|
422
|
-
if (!removed) return;
|
|
423
|
-
mountSet = mountSet.filter((m) => m.id !== id);
|
|
424
|
-
notifyMountListeners();
|
|
425
|
-
if (currentAnchorRef?.id === id && homeAnchor) {
|
|
426
|
-
setAnchor(homeAnchor);
|
|
419
|
+
currentAnchorPath = anchor.path;
|
|
420
|
+
currentAnchorRef = anchor;
|
|
421
|
+
if (!anchorResolved) {
|
|
422
|
+
anchorResolved = true;
|
|
423
|
+
resolveAnchorReady();
|
|
427
424
|
}
|
|
425
|
+
if (mountsChanged) notifyMountListeners();
|
|
426
|
+
if (anchorChanged) notifyAnchorListeners();
|
|
428
427
|
}
|
|
429
428
|
var sessionEntry = null;
|
|
430
429
|
var sessionPromise = null;
|
|
430
|
+
var sessionGeneration = 0;
|
|
431
|
+
function getSessionGeneration() {
|
|
432
|
+
return sessionGeneration;
|
|
433
|
+
}
|
|
431
434
|
async function initSession() {
|
|
432
435
|
if (sessionEntry) return sessionEntry;
|
|
433
436
|
if (sessionPromise) return sessionPromise;
|
|
437
|
+
const generation = sessionGeneration;
|
|
434
438
|
sessionPromise = (async () => {
|
|
435
439
|
const hostname = getHostname();
|
|
436
440
|
const token = await getAccessToken();
|
|
441
|
+
if (generation !== sessionGeneration) throw new Error("Session changed during initialization");
|
|
437
442
|
const headers = {};
|
|
438
443
|
if (token) {
|
|
439
444
|
headers["Authorization"] = `Bearer ${token}`;
|
|
@@ -446,6 +451,7 @@ async function initSession() {
|
|
|
446
451
|
throw new Error(`Session init failed: ${response.status} ${response.statusText}`);
|
|
447
452
|
}
|
|
448
453
|
const json = await response.json();
|
|
454
|
+
if (generation !== sessionGeneration) throw new Error("Session changed during initialization");
|
|
449
455
|
const entry = json.entry || json;
|
|
450
456
|
sessionEntry = {
|
|
451
457
|
scope_id: entry.scope_id,
|
|
@@ -459,13 +465,17 @@ async function initSession() {
|
|
|
459
465
|
principal_id: entry.principal_id || null
|
|
460
466
|
};
|
|
461
467
|
return sessionEntry;
|
|
462
|
-
})()
|
|
468
|
+
})().catch((error) => {
|
|
469
|
+
if (generation === sessionGeneration) sessionPromise = null;
|
|
470
|
+
throw error;
|
|
471
|
+
});
|
|
463
472
|
return sessionPromise;
|
|
464
473
|
}
|
|
465
474
|
function getSessionEntry() {
|
|
466
475
|
return sessionEntry;
|
|
467
476
|
}
|
|
468
477
|
function clearSession() {
|
|
478
|
+
sessionGeneration++;
|
|
469
479
|
sessionEntry = null;
|
|
470
480
|
sessionPromise = null;
|
|
471
481
|
currentAnchorPath = null;
|
|
@@ -480,6 +490,7 @@ function registerCleanup(fn) {
|
|
|
480
490
|
function resetAllSdkState() {
|
|
481
491
|
clearSession();
|
|
482
492
|
clearCachedToken();
|
|
493
|
+
resolveAnchorReady();
|
|
483
494
|
anchorResolved = false;
|
|
484
495
|
anchorReady = new Promise((resolve) => {
|
|
485
496
|
resolveAnchorReady = resolve;
|
|
@@ -572,24 +583,28 @@ var GatewayRequestError = class extends Error {
|
|
|
572
583
|
}
|
|
573
584
|
};
|
|
574
585
|
async function gatewayCall(method, params = {}) {
|
|
586
|
+
const generation = getSessionGeneration();
|
|
587
|
+
const requestedAnchor = getAnchor();
|
|
575
588
|
const hostname = getHostname();
|
|
576
589
|
const url = `${GATEWAY_URL}/d/${hostname}`;
|
|
577
590
|
const headers = {
|
|
578
591
|
"Content-Type": "application/json"
|
|
579
592
|
};
|
|
580
593
|
const token = await getAccessToken();
|
|
594
|
+
if (generation !== getSessionGeneration()) throw new Error("Session changed before gateway request");
|
|
581
595
|
if (token) {
|
|
582
596
|
headers["Authorization"] = `Bearer ${token}`;
|
|
583
597
|
}
|
|
584
598
|
if (!ANCHOR_INDEPENDENT_METHODS.has(method) && !params._scope_path) {
|
|
585
599
|
await awaitAnchorReady();
|
|
586
600
|
}
|
|
601
|
+
if (generation !== getSessionGeneration()) throw new Error("Session changed before gateway request");
|
|
587
602
|
const enrichedParams = { ...params };
|
|
588
603
|
const session = getSessionEntry();
|
|
589
604
|
if (!enrichedParams.domain_scope_id && session?.scope_id) {
|
|
590
605
|
enrichedParams.domain_scope_id = session.scope_id;
|
|
591
606
|
}
|
|
592
|
-
const anchorPath = getAnchor();
|
|
607
|
+
const anchorPath = requestedAnchor ?? getAnchor();
|
|
593
608
|
if (!enrichedParams._scope_path && anchorPath) {
|
|
594
609
|
enrichedParams._scope_path = anchorPath;
|
|
595
610
|
}
|
|
@@ -599,6 +614,7 @@ async function gatewayCall(method, params = {}) {
|
|
|
599
614
|
body: JSON.stringify({ method, params: enrichedParams })
|
|
600
615
|
});
|
|
601
616
|
const responseBody = await response.text();
|
|
617
|
+
if (generation !== getSessionGeneration()) throw new Error("Session changed during gateway request");
|
|
602
618
|
let json = null;
|
|
603
619
|
if (responseBody) {
|
|
604
620
|
try {
|
|
@@ -705,6 +721,11 @@ function clearArrangementRefreshes(client2) {
|
|
|
705
721
|
pending.delete(client2);
|
|
706
722
|
}
|
|
707
723
|
var client = null;
|
|
724
|
+
registerCleanup(() => {
|
|
725
|
+
if (!client) return;
|
|
726
|
+
void client.cancelQueries({ queryKey: ["vfs"] });
|
|
727
|
+
client.removeQueries({ queryKey: ["vfs"] });
|
|
728
|
+
});
|
|
708
729
|
function setQueryClient(custom) {
|
|
709
730
|
if (client && client !== custom) {
|
|
710
731
|
throw new Error(
|
|
@@ -1982,60 +2003,40 @@ function invalidateOpenEnvelope(path, strategy) {
|
|
|
1982
2003
|
}
|
|
1983
2004
|
}
|
|
1984
2005
|
|
|
1985
|
-
// src/
|
|
1986
|
-
function
|
|
1987
|
-
return
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
const ids = res?.item?.type_data?.mounts ?? [];
|
|
1995
|
-
const refs = ids.map(resolveRef).filter((r) => r !== null);
|
|
1996
|
-
const home = getHomeAnchor();
|
|
1997
|
-
if (home && !refs.some((r) => r.id === home.id)) {
|
|
1998
|
-
refs.unshift(home);
|
|
1999
|
-
}
|
|
2000
|
-
setMountsState(refs);
|
|
2001
|
-
return refs;
|
|
2002
|
-
} catch {
|
|
2003
|
-
const home = getHomeAnchor();
|
|
2004
|
-
const refs = home ? [home] : [];
|
|
2005
|
-
setMountsState(refs);
|
|
2006
|
-
return refs;
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
async function persist() {
|
|
2010
|
-
if (!declItemPath) return;
|
|
2011
|
-
const ids = getMounts().map((m) => m.id);
|
|
2012
|
-
const lastAnchor = getAnchorRef()?.id ?? null;
|
|
2013
|
-
try {
|
|
2014
|
-
await gatewayCall("update", {
|
|
2015
|
-
path: declItemPath,
|
|
2016
|
-
type_data: { mounts: ids, last_anchor: lastAnchor }
|
|
2017
|
-
});
|
|
2018
|
-
} catch (e) {
|
|
2019
|
-
try {
|
|
2020
|
-
const parent = declItemPath.split("/").slice(0, -1).join("/");
|
|
2021
|
-
await gatewayCall("create", {
|
|
2022
|
-
parent_path: parent,
|
|
2023
|
-
name: "mounts",
|
|
2024
|
-
item_type: "file",
|
|
2025
|
-
type_data: { mounts: ids, last_anchor: lastAnchor }
|
|
2026
|
-
});
|
|
2027
|
-
} catch (e2) {
|
|
2028
|
-
console.warn("[SDK] mount declaration persist failed:", e2);
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
async function mount(ref) {
|
|
2033
|
-
addMountState(ref);
|
|
2034
|
-
await persist();
|
|
2006
|
+
// src/memberships.ts
|
|
2007
|
+
function membershipToMountRef(m) {
|
|
2008
|
+
return {
|
|
2009
|
+
id: m.accountId,
|
|
2010
|
+
path: m.path,
|
|
2011
|
+
kind: m.isSpace ? "space" : "account",
|
|
2012
|
+
name: m.name,
|
|
2013
|
+
grantedUnderAccountId: m.grantedUnderAccountId ?? void 0
|
|
2014
|
+
};
|
|
2035
2015
|
}
|
|
2036
|
-
async function
|
|
2037
|
-
|
|
2038
|
-
|
|
2016
|
+
async function fetchMemberships() {
|
|
2017
|
+
const result = await gatewayCall("list-memberships", {});
|
|
2018
|
+
const memberships = result.memberships || [];
|
|
2019
|
+
return memberships.map((m) => ({
|
|
2020
|
+
accountId: m.account_id,
|
|
2021
|
+
path: m.account_path,
|
|
2022
|
+
name: m.account_name,
|
|
2023
|
+
role: m.role || "member",
|
|
2024
|
+
typeData: m.account_type_data || {},
|
|
2025
|
+
joinedAt: m.joined_at,
|
|
2026
|
+
via: m.via,
|
|
2027
|
+
placement: m.placement,
|
|
2028
|
+
subordinateTo: m.subordinate_to ?? null,
|
|
2029
|
+
cascadeAnchorId: m.cascade_anchor_id ?? null,
|
|
2030
|
+
isAccount: m.is_account ?? void 0,
|
|
2031
|
+
isSpace: m.is_space ?? void 0,
|
|
2032
|
+
isOwner: m.is_owner ?? void 0,
|
|
2033
|
+
billingResponsible: m.billing_responsible ?? void 0,
|
|
2034
|
+
grantedVia: m.granted_via ?? null,
|
|
2035
|
+
grantedUnderAccountId: m.granted_under_account_id ?? null,
|
|
2036
|
+
membershipPath: m.membership_path ?? null,
|
|
2037
|
+
personalSpaceId: m.personal_space_id ?? null,
|
|
2038
|
+
personalSpacePath: m.personal_space_path ?? null
|
|
2039
|
+
}));
|
|
2039
2040
|
}
|
|
2040
2041
|
|
|
2041
2042
|
// src/vfs-realtime-ws.ts
|
|
@@ -2045,7 +2046,10 @@ var unsubCcmEvents = null;
|
|
|
2045
2046
|
var debounceTimers = /* @__PURE__ */ new Map();
|
|
2046
2047
|
var DEBOUNCE_MS = 250;
|
|
2047
2048
|
function initVfsRealtimeWs(accountId) {
|
|
2048
|
-
|
|
2049
|
+
if (unsubEvents) {
|
|
2050
|
+
mountRealtimeScope(accountId);
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2049
2053
|
mountRealtimeScope(accountId);
|
|
2050
2054
|
unsubEvents = subscribeToEvents(
|
|
2051
2055
|
"vfs_change",
|
|
@@ -2162,6 +2166,147 @@ function applyInvalidation(action, path, data, parentPath) {
|
|
|
2162
2166
|
}
|
|
2163
2167
|
}
|
|
2164
2168
|
|
|
2169
|
+
// src/mounts.ts
|
|
2170
|
+
var declaration = null;
|
|
2171
|
+
registerCleanup(() => {
|
|
2172
|
+
declaration = null;
|
|
2173
|
+
});
|
|
2174
|
+
function assertCurrent(owner) {
|
|
2175
|
+
if (owner !== declaration || owner.generation !== getSessionGeneration()) throw new Error("Workspace session changed");
|
|
2176
|
+
}
|
|
2177
|
+
function missing(error) {
|
|
2178
|
+
return error instanceof GatewayRequestError && error.payload?.error === "item_not_found";
|
|
2179
|
+
}
|
|
2180
|
+
function canonical(owner, ref) {
|
|
2181
|
+
const current = owner.resolve(ref.id);
|
|
2182
|
+
if (!current || current.path !== ref.path || current.kind !== ref.kind) {
|
|
2183
|
+
throw new Error("Mount is not an available membership");
|
|
2184
|
+
}
|
|
2185
|
+
return current;
|
|
2186
|
+
}
|
|
2187
|
+
function withHome(refs) {
|
|
2188
|
+
const home = getHomeAnchor();
|
|
2189
|
+
if (!home) throw new Error("Elect a home account before loading mounts");
|
|
2190
|
+
return [home, ...refs.filter((ref, index) => ref.id !== home.id && refs.findIndex((r) => r.id === ref.id) === index)];
|
|
2191
|
+
}
|
|
2192
|
+
function signature(refs, anchor) {
|
|
2193
|
+
return JSON.stringify({ mounts: refs.map((ref) => ref.id), last_anchor: anchor.id });
|
|
2194
|
+
}
|
|
2195
|
+
async function save(owner, refs, anchor) {
|
|
2196
|
+
assertCurrent(owner);
|
|
2197
|
+
const next = signature(refs, anchor);
|
|
2198
|
+
if (owner.saved === next) return;
|
|
2199
|
+
const type_data = JSON.parse(next);
|
|
2200
|
+
await gatewayCall(owner.exists ? "update" : "create", owner.exists ? {
|
|
2201
|
+
_scope_path: owner.scope,
|
|
2202
|
+
path: owner.path,
|
|
2203
|
+
type_data
|
|
2204
|
+
} : {
|
|
2205
|
+
_scope_path: owner.scope,
|
|
2206
|
+
parent_path: owner.path.slice(0, owner.path.lastIndexOf("/")),
|
|
2207
|
+
name: "mounts",
|
|
2208
|
+
item_type: "file",
|
|
2209
|
+
type_data
|
|
2210
|
+
});
|
|
2211
|
+
assertCurrent(owner);
|
|
2212
|
+
owner.exists = true;
|
|
2213
|
+
owner.saved = next;
|
|
2214
|
+
}
|
|
2215
|
+
function publish(refs, anchor) {
|
|
2216
|
+
const previous = getMounts();
|
|
2217
|
+
initVfsRealtimeWs(getHomeAnchor().id);
|
|
2218
|
+
for (const ref of previous) if (!refs.some((r) => r.id === ref.id)) unmountRealtimeScope(ref.id);
|
|
2219
|
+
for (const ref of refs) mountRealtimeScope(ref.id);
|
|
2220
|
+
commitMountSelection(refs, anchor);
|
|
2221
|
+
}
|
|
2222
|
+
async function loadMounts(memberSpacePath, resolveRef, options = {}) {
|
|
2223
|
+
const home = getHomeAnchor();
|
|
2224
|
+
if (!home) throw new Error("Elect a home account before loading mounts");
|
|
2225
|
+
const path = `${memberSpacePath}/settings/mounts`;
|
|
2226
|
+
if (declaration && declaration.path !== path) throw new Error("Reset the session before changing the mount declaration owner");
|
|
2227
|
+
const owner = declaration ??= {
|
|
2228
|
+
generation: getSessionGeneration(),
|
|
2229
|
+
path,
|
|
2230
|
+
scope: home.path,
|
|
2231
|
+
resolve: resolveRef,
|
|
2232
|
+
exists: false,
|
|
2233
|
+
ready: false,
|
|
2234
|
+
saved: null,
|
|
2235
|
+
queue: Promise.resolve()
|
|
2236
|
+
};
|
|
2237
|
+
assertCurrent(owner);
|
|
2238
|
+
if (owner.loading) return owner.loading;
|
|
2239
|
+
if (owner.ready) return getMounts();
|
|
2240
|
+
owner.resolve = resolveRef;
|
|
2241
|
+
owner.loading = (async () => {
|
|
2242
|
+
let data = null;
|
|
2243
|
+
try {
|
|
2244
|
+
const response = await gatewayCall("read", { path, _scope_path: owner.scope });
|
|
2245
|
+
assertCurrent(owner);
|
|
2246
|
+
data = response.type_data ?? response.item?.type_data;
|
|
2247
|
+
if (!data || !Array.isArray(data.mounts) || !data.mounts.every((id) => typeof id === "string")) {
|
|
2248
|
+
throw new Error("Invalid saved mount declaration");
|
|
2249
|
+
}
|
|
2250
|
+
owner.exists = true;
|
|
2251
|
+
} catch (error) {
|
|
2252
|
+
assertCurrent(owner);
|
|
2253
|
+
if (!missing(error)) throw error;
|
|
2254
|
+
owner.exists = false;
|
|
2255
|
+
}
|
|
2256
|
+
const initial = options.initialAnchor ? canonical(owner, options.initialAnchor) : home;
|
|
2257
|
+
const refs = withHome(data ? data.mounts.map(resolveRef).filter((ref) => ref !== null) : [initial]);
|
|
2258
|
+
const anchor = data ? refs.find((ref) => ref.id === data.last_anchor) ?? refs.find((ref) => ref.kind === "space") ?? home : initial;
|
|
2259
|
+
if (data) owner.saved = signature(refs, anchor);
|
|
2260
|
+
else if (options.initialAnchor) await save(owner, refs, anchor);
|
|
2261
|
+
assertCurrent(owner);
|
|
2262
|
+
owner.ready = true;
|
|
2263
|
+
publish(refs, anchor);
|
|
2264
|
+
return refs;
|
|
2265
|
+
})().finally(() => {
|
|
2266
|
+
owner.loading = void 0;
|
|
2267
|
+
});
|
|
2268
|
+
return owner.loading;
|
|
2269
|
+
}
|
|
2270
|
+
function mutate(change) {
|
|
2271
|
+
const owner = declaration;
|
|
2272
|
+
if (!owner?.ready) return Promise.reject(new Error("Workspace mounts are not loaded"));
|
|
2273
|
+
const task = owner.queue.then(async () => {
|
|
2274
|
+
assertCurrent(owner);
|
|
2275
|
+
const { refs, anchor } = change(owner);
|
|
2276
|
+
await save(owner, refs, anchor);
|
|
2277
|
+
assertCurrent(owner);
|
|
2278
|
+
publish(refs, anchor);
|
|
2279
|
+
});
|
|
2280
|
+
owner.queue = task.catch(() => {
|
|
2281
|
+
});
|
|
2282
|
+
return task;
|
|
2283
|
+
}
|
|
2284
|
+
function mount(ref) {
|
|
2285
|
+
return mutate((owner) => ({ refs: withHome([...getMounts(), canonical(owner, ref)]), anchor: getAnchorRef() }));
|
|
2286
|
+
}
|
|
2287
|
+
function unmount(id) {
|
|
2288
|
+
return mutate(() => {
|
|
2289
|
+
if (id === getHomeAnchor().id) throw new Error("The home account cannot be unmounted");
|
|
2290
|
+
const refs = withHome(getMounts().filter((ref) => ref.id !== id));
|
|
2291
|
+
const anchor = refs.find((ref) => ref.id === getAnchorRef()?.id) ?? refs.find((ref) => ref.kind === "space") ?? getHomeAnchor();
|
|
2292
|
+
return { refs, anchor };
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
function selectMount(ref) {
|
|
2296
|
+
return mutate((owner) => {
|
|
2297
|
+
const anchor = canonical(owner, ref);
|
|
2298
|
+
return { refs: withHome([...getMounts(), anchor]), anchor };
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
function replaceMounts(refs, anchor) {
|
|
2302
|
+
return mutate((owner) => {
|
|
2303
|
+
const next = withHome(refs.map((ref) => canonical(owner, ref)));
|
|
2304
|
+
const selected = canonical(owner, anchor);
|
|
2305
|
+
if (!next.some((ref) => ref.id === selected.id)) throw new Error("Selected mount must be in the working set");
|
|
2306
|
+
return { refs: next, anchor: selected };
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2165
2310
|
// src/hooks.ts
|
|
2166
2311
|
function useScopeKey() {
|
|
2167
2312
|
return React.useSyncExternalStore(subscribeScope, getScopeVersion, getScopeVersion);
|
|
@@ -2528,44 +2673,13 @@ function useScope() {
|
|
|
2528
2673
|
return { data, loading, error };
|
|
2529
2674
|
}
|
|
2530
2675
|
var EMPTY_ACCOUNTS = [];
|
|
2531
|
-
function membershipToMountRef(m) {
|
|
2532
|
-
return {
|
|
2533
|
-
id: m.accountId,
|
|
2534
|
-
path: m.path,
|
|
2535
|
-
kind: m.isSpace ? "space" : "account",
|
|
2536
|
-
name: m.name,
|
|
2537
|
-
grantedUnderAccountId: m.grantedUnderAccountId ?? void 0
|
|
2538
|
-
};
|
|
2539
|
-
}
|
|
2540
2676
|
function useAccounts() {
|
|
2541
2677
|
const { user } = useAuth();
|
|
2542
|
-
const autoSelectedRef = React.useRef(
|
|
2678
|
+
const autoSelectedRef = React.useRef(null);
|
|
2679
|
+
const [mountError, setMountError] = React.useState(null);
|
|
2543
2680
|
const query = reactQuery.useQuery({
|
|
2544
2681
|
queryKey: vfsKeys.memberships(user?.id ?? "anonymous"),
|
|
2545
|
-
queryFn:
|
|
2546
|
-
const result = await gatewayCall("list-memberships", {});
|
|
2547
|
-
const memberships = result.memberships || [];
|
|
2548
|
-
return memberships.map((m) => ({
|
|
2549
|
-
accountId: m.account_id,
|
|
2550
|
-
path: m.account_path,
|
|
2551
|
-
name: m.account_name,
|
|
2552
|
-
role: m.role || "member",
|
|
2553
|
-
typeData: m.account_type_data || {},
|
|
2554
|
-
joinedAt: m.joined_at,
|
|
2555
|
-
via: m.via,
|
|
2556
|
-
placement: m.placement,
|
|
2557
|
-
subordinateTo: m.subordinate_to ?? null,
|
|
2558
|
-
cascadeAnchorId: m.cascade_anchor_id ?? null,
|
|
2559
|
-
isAccount: m.is_account ?? void 0,
|
|
2560
|
-
isSpace: m.is_space ?? void 0,
|
|
2561
|
-
isOwner: m.is_owner ?? void 0,
|
|
2562
|
-
billingResponsible: m.billing_responsible ?? void 0,
|
|
2563
|
-
grantedVia: m.granted_via ?? null,
|
|
2564
|
-
grantedUnderAccountId: m.granted_under_account_id ?? null,
|
|
2565
|
-
personalSpaceId: m.personal_space_id ?? null,
|
|
2566
|
-
personalSpacePath: m.personal_space_path ?? null
|
|
2567
|
-
}));
|
|
2568
|
-
},
|
|
2682
|
+
queryFn: fetchMemberships,
|
|
2569
2683
|
enabled: !!user?.id,
|
|
2570
2684
|
staleTime: 1e3 * 60 * 2
|
|
2571
2685
|
});
|
|
@@ -2587,50 +2701,47 @@ function useAccounts() {
|
|
|
2587
2701
|
}, [anchorVersion2, allMemberships]);
|
|
2588
2702
|
React.useEffect(() => {
|
|
2589
2703
|
if (!user?.id) {
|
|
2590
|
-
autoSelectedRef.current =
|
|
2704
|
+
autoSelectedRef.current = null;
|
|
2591
2705
|
return;
|
|
2592
2706
|
}
|
|
2593
|
-
if (autoSelectedRef.current) return;
|
|
2707
|
+
if (autoSelectedRef.current === user.id) return;
|
|
2708
|
+
setMountError(null);
|
|
2594
2709
|
if (electionCandidates.length === 1) {
|
|
2595
2710
|
const only = electionCandidates[0];
|
|
2596
|
-
electHomeAnchor(membershipToMountRef(only));
|
|
2711
|
+
if (!getHomeAnchor()) electHomeAnchor(membershipToMountRef(only));
|
|
2597
2712
|
initVfsRealtimeWs(only.accountId);
|
|
2598
2713
|
if (only.personalSpacePath) {
|
|
2599
2714
|
loadMounts(only.personalSpacePath, (id) => {
|
|
2600
2715
|
const m = allMemberships.find((x) => x.accountId === id);
|
|
2601
2716
|
return m ? membershipToMountRef(m) : null;
|
|
2602
|
-
}).catch(
|
|
2603
|
-
});
|
|
2717
|
+
}).catch(setMountError);
|
|
2604
2718
|
}
|
|
2605
|
-
autoSelectedRef.current =
|
|
2719
|
+
autoSelectedRef.current = user.id;
|
|
2606
2720
|
} else if (electionCandidates.length > 1) {
|
|
2607
|
-
autoSelectedRef.current =
|
|
2721
|
+
autoSelectedRef.current = user.id;
|
|
2608
2722
|
}
|
|
2609
2723
|
}, [user?.id, electionCandidates, allMemberships]);
|
|
2610
|
-
const switchAccount = React.useCallback((accountPath) => {
|
|
2724
|
+
const switchAccount = React.useCallback(async (accountPath) => {
|
|
2611
2725
|
const m = allMemberships.find((a) => a.path === accountPath);
|
|
2612
|
-
if (!m)
|
|
2726
|
+
if (!m) throw new Error("Account membership is unavailable");
|
|
2613
2727
|
const ref = membershipToMountRef(m);
|
|
2614
2728
|
if (!getHomeAnchor()) {
|
|
2615
2729
|
electHomeAnchor(ref);
|
|
2616
|
-
if (m.personalSpacePath) {
|
|
2617
|
-
loadMounts(m.personalSpacePath, (id) => {
|
|
2618
|
-
const mm = allMemberships.find((x) => x.accountId === id);
|
|
2619
|
-
return mm ? membershipToMountRef(mm) : null;
|
|
2620
|
-
}).catch(() => {
|
|
2621
|
-
});
|
|
2622
|
-
}
|
|
2623
|
-
} else {
|
|
2624
|
-
setAnchor(ref);
|
|
2625
2730
|
}
|
|
2731
|
+
const home = allMemberships.find((a) => a.accountId === getHomeAnchor()?.id);
|
|
2732
|
+
if (!home?.personalSpacePath) throw new Error("The home membership has no mount declaration location");
|
|
2733
|
+
await loadMounts(home.personalSpacePath, (id) => {
|
|
2734
|
+
const member = allMemberships.find((a) => a.accountId === id);
|
|
2735
|
+
return member ? membershipToMountRef(member) : null;
|
|
2736
|
+
});
|
|
2737
|
+
await selectMount(ref);
|
|
2738
|
+
setMountError(null);
|
|
2626
2739
|
initVfsRealtimeWs(m.accountId);
|
|
2627
2740
|
}, [allMemberships]);
|
|
2628
|
-
const mountScope = React.useCallback((scopePath) => {
|
|
2741
|
+
const mountScope = React.useCallback(async (scopePath) => {
|
|
2629
2742
|
const m = allMemberships.find((a) => a.path === scopePath);
|
|
2630
|
-
if (!m)
|
|
2631
|
-
mount(membershipToMountRef(m))
|
|
2632
|
-
});
|
|
2633
|
-
mountRealtimeScope(scopePath);
|
|
2743
|
+
if (!m) throw new Error("Scope membership is unavailable");
|
|
2744
|
+
await mount(membershipToMountRef(m));
|
|
2634
2745
|
}, [allMemberships]);
|
|
2635
2746
|
return {
|
|
2636
2747
|
accounts: electionCandidates,
|
|
@@ -2640,17 +2751,19 @@ function useAccounts() {
|
|
|
2640
2751
|
switchAccount,
|
|
2641
2752
|
mountScope,
|
|
2642
2753
|
loading: query.isLoading,
|
|
2643
|
-
error: query.error
|
|
2754
|
+
error: query.error ?? mountError
|
|
2644
2755
|
};
|
|
2645
2756
|
}
|
|
2646
2757
|
function useMounts() {
|
|
2758
|
+
useScopeKey();
|
|
2647
2759
|
const version = React.useSyncExternalStore(subscribeMounts, getMountVersion, getMountVersion);
|
|
2648
2760
|
const mounts = React.useMemo(() => getMounts(), [version]);
|
|
2649
2761
|
return {
|
|
2650
2762
|
mounts,
|
|
2651
2763
|
homeAnchor: getHomeAnchor(),
|
|
2652
2764
|
anchor: getAnchorRef(),
|
|
2653
|
-
setAnchor,
|
|
2765
|
+
setAnchor: selectMount,
|
|
2766
|
+
replaceMounts,
|
|
2654
2767
|
mount,
|
|
2655
2768
|
unmount
|
|
2656
2769
|
};
|
|
@@ -4346,34 +4459,109 @@ function startTracking(options = {}) {
|
|
|
4346
4459
|
return trackingCleanup;
|
|
4347
4460
|
}
|
|
4348
4461
|
|
|
4349
|
-
// src/
|
|
4462
|
+
// src/workspace.ts
|
|
4350
4463
|
var initialization;
|
|
4464
|
+
registerCleanup(() => {
|
|
4465
|
+
initialization = void 0;
|
|
4466
|
+
});
|
|
4467
|
+
function initializeWorkspace(options) {
|
|
4468
|
+
if (initialization) {
|
|
4469
|
+
if (initialization.accountPath !== options.accountPath) return Promise.reject(new Error("Reset the session before changing accounts"));
|
|
4470
|
+
return initialization.promise;
|
|
4471
|
+
}
|
|
4472
|
+
const generation = getSessionGeneration();
|
|
4473
|
+
const check = () => {
|
|
4474
|
+
if (generation !== getSessionGeneration()) throw new Error("Workspace session changed");
|
|
4475
|
+
};
|
|
4476
|
+
const promise = (async () => {
|
|
4477
|
+
const entry = await initSession();
|
|
4478
|
+
check();
|
|
4479
|
+
if (!entry.principal_id) throw new Error("Workspace initialization requires an authenticated principal");
|
|
4480
|
+
const memberships = await getQueryClient().fetchQuery({
|
|
4481
|
+
queryKey: vfsKeys.memberships(entry.principal_id),
|
|
4482
|
+
queryFn: fetchMemberships,
|
|
4483
|
+
staleTime: 12e4,
|
|
4484
|
+
retry: false
|
|
4485
|
+
});
|
|
4486
|
+
check();
|
|
4487
|
+
const account = memberships.find((m) => m.path === options.accountPath && m.isAccount && !m.isSpace);
|
|
4488
|
+
if (!account) throw new Error("Authenticated account membership is unavailable");
|
|
4489
|
+
const personal = memberships.find((m) => m.isSpace && m.accountId === account.personalSpaceId && m.path === account.personalSpacePath);
|
|
4490
|
+
if (!personal || !account.personalSpacePath) throw new Error("A provisioned default Space membership is required");
|
|
4491
|
+
const home = membershipToMountRef(account);
|
|
4492
|
+
const defaultSpace = membershipToMountRef(personal);
|
|
4493
|
+
const elected = getHomeAnchor();
|
|
4494
|
+
if (elected && elected.id !== home.id) throw new Error("Reset the session before changing accounts");
|
|
4495
|
+
if (!elected) electHomeAnchor(home, { activate: false });
|
|
4496
|
+
await loadMounts(account.personalSpacePath, (id) => {
|
|
4497
|
+
const member = memberships.find((m) => m.accountId === id && (m.isAccount || m.isSpace));
|
|
4498
|
+
return member ? membershipToMountRef(member) : null;
|
|
4499
|
+
}, { initialAnchor: defaultSpace });
|
|
4500
|
+
check();
|
|
4501
|
+
return { home, defaultSpace, memberships };
|
|
4502
|
+
})().catch((error) => {
|
|
4503
|
+
if (generation === getSessionGeneration()) initialization = void 0;
|
|
4504
|
+
throw error;
|
|
4505
|
+
});
|
|
4506
|
+
initialization = { accountPath: options.accountPath, promise };
|
|
4507
|
+
return promise;
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
// src/initialize.ts
|
|
4511
|
+
var initialization2;
|
|
4512
|
+
var stopActiveTracking;
|
|
4513
|
+
var workspaceAccount;
|
|
4514
|
+
registerCleanup(() => {
|
|
4515
|
+
stopActiveTracking?.();
|
|
4516
|
+
stopActiveTracking = void 0;
|
|
4517
|
+
initialization2 = void 0;
|
|
4518
|
+
workspaceAccount = void 0;
|
|
4519
|
+
});
|
|
4351
4520
|
function initializeFsaos(options = {}) {
|
|
4352
|
-
if (
|
|
4353
|
-
|
|
4521
|
+
if (initialization2) {
|
|
4522
|
+
if (options.workspace && options.workspace.accountPath !== workspaceAccount) {
|
|
4523
|
+
return Promise.reject(new Error("Initialize workspace through initializeWorkspace, or dispose the existing runtime first"));
|
|
4524
|
+
}
|
|
4525
|
+
return initialization2;
|
|
4526
|
+
}
|
|
4527
|
+
workspaceAccount = options.workspace?.accountPath;
|
|
4528
|
+
const generation = getSessionGeneration();
|
|
4529
|
+
initialization2 = (async () => {
|
|
4354
4530
|
const tracking = options.tracking ?? true;
|
|
4355
4531
|
const stopTracking = tracking === false ? () => {
|
|
4356
4532
|
} : startTracking(typeof tracking === "object" ? tracking : {});
|
|
4533
|
+
stopActiveTracking = stopTracking;
|
|
4357
4534
|
try {
|
|
4358
4535
|
const session = options.session === false ? null : await initSession();
|
|
4536
|
+
const workspace = options.workspace ? await initializeWorkspace(options.workspace) : void 0;
|
|
4537
|
+
if (generation !== getSessionGeneration()) throw new Error("Session changed during initialization");
|
|
4359
4538
|
let disposed = false;
|
|
4360
4539
|
const runtime = {
|
|
4361
4540
|
session,
|
|
4541
|
+
workspace,
|
|
4362
4542
|
dispose() {
|
|
4363
4543
|
if (disposed) return;
|
|
4364
4544
|
disposed = true;
|
|
4365
4545
|
stopTracking();
|
|
4366
|
-
|
|
4546
|
+
if (generation === getSessionGeneration()) {
|
|
4547
|
+
initialization2 = void 0;
|
|
4548
|
+
workspaceAccount = void 0;
|
|
4549
|
+
stopActiveTracking = void 0;
|
|
4550
|
+
}
|
|
4367
4551
|
}
|
|
4368
4552
|
};
|
|
4369
4553
|
return runtime;
|
|
4370
4554
|
} catch (error) {
|
|
4371
4555
|
stopTracking();
|
|
4372
|
-
|
|
4556
|
+
if (generation === getSessionGeneration()) {
|
|
4557
|
+
initialization2 = void 0;
|
|
4558
|
+
workspaceAccount = void 0;
|
|
4559
|
+
stopActiveTracking = void 0;
|
|
4560
|
+
}
|
|
4373
4561
|
throw error;
|
|
4374
4562
|
}
|
|
4375
4563
|
})();
|
|
4376
|
-
return
|
|
4564
|
+
return initialization2;
|
|
4377
4565
|
}
|
|
4378
4566
|
|
|
4379
4567
|
// src/execution-control.ts
|
|
@@ -5468,6 +5656,7 @@ exports.hasServerClock = hasServerClock;
|
|
|
5468
5656
|
exports.initSession = initSession;
|
|
5469
5657
|
exports.initVfsRealtimeWs = initVfsRealtimeWs;
|
|
5470
5658
|
exports.initializeFsaos = initializeFsaos;
|
|
5659
|
+
exports.initializeWorkspace = initializeWorkspace;
|
|
5471
5660
|
exports.interpretSchema = interpretSchema;
|
|
5472
5661
|
exports.invalidateAllVfs = invalidateAllVfs;
|
|
5473
5662
|
exports.invalidateChannelMessages = invalidateChannelMessages;
|
|
@@ -5483,6 +5672,7 @@ exports.isListableItemsFilter = isListableItemsFilter;
|
|
|
5483
5672
|
exports.isScopeReady = isScopeReady;
|
|
5484
5673
|
exports.itemsFilterFromQueryFilter = itemsFilterFromQueryFilter;
|
|
5485
5674
|
exports.listChildren = listChildren;
|
|
5675
|
+
exports.loadMounts = loadMounts;
|
|
5486
5676
|
exports.mount = mount2;
|
|
5487
5677
|
exports.mountRealtimeScope = mountRealtimeScope;
|
|
5488
5678
|
exports.normalizeItem = normalizeItem;
|
|
@@ -5494,9 +5684,11 @@ exports.queryClient = queryClient;
|
|
|
5494
5684
|
exports.readItem = readItem;
|
|
5495
5685
|
exports.refreshQuery = refreshQuery;
|
|
5496
5686
|
exports.registerCleanup = registerCleanup;
|
|
5687
|
+
exports.replaceMounts = replaceMounts;
|
|
5497
5688
|
exports.resetAllSdkState = resetAllSdkState;
|
|
5498
5689
|
exports.resolvePrincipalId = resolvePrincipalId;
|
|
5499
5690
|
exports.resubscribePath = resubscribePath;
|
|
5691
|
+
exports.selectMount = selectMount;
|
|
5500
5692
|
exports.serverDeadlinePassed = serverDeadlinePassed;
|
|
5501
5693
|
exports.serverMsUntil = serverMsUntil;
|
|
5502
5694
|
exports.serverNow = serverNow;
|