@ai4b-team/fsaos-gateway-sdk 3.34.0 → 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/README.md +6 -0
- 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 +343 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -67
- package/dist/index.d.ts +89 -67
- package/dist/index.js +340 -142
- 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
|
};
|
|
@@ -3397,6 +3510,7 @@ function normalizeChannel(raw) {
|
|
|
3397
3510
|
isPrivate: td.is_private || false,
|
|
3398
3511
|
isDm: td.is_dm || false,
|
|
3399
3512
|
messageCount: td.message_count || 0,
|
|
3513
|
+
createdAt: raw.created_at || "",
|
|
3400
3514
|
updatedAt: raw.updated_at || "",
|
|
3401
3515
|
chatScopePath: lineage.context_path || "",
|
|
3402
3516
|
contextId: raw.scope_item_id || "",
|
|
@@ -4003,6 +4117,15 @@ function usePersonalItemOrder(key, items) {
|
|
|
4003
4117
|
const scopePath = getScope();
|
|
4004
4118
|
const supported = anchor?.kind === "space";
|
|
4005
4119
|
const query = useArrangement(supported && scopePath ? { key, scope_id: anchor.id } : null);
|
|
4120
|
+
const orderedItems = React.useMemo(() => {
|
|
4121
|
+
const arranged = query.applyTo([...items]);
|
|
4122
|
+
if (key !== "channel-order") return arranged;
|
|
4123
|
+
const savedIds = new Set(query.order ?? []);
|
|
4124
|
+
const unplaced = arranged.filter((item) => !savedIds.has(item.id));
|
|
4125
|
+
const created = (item) => Date.parse(item.createdAt ?? "") || 0;
|
|
4126
|
+
unplaced.sort((a, b) => created(b) - created(a));
|
|
4127
|
+
return [...unplaced, ...arranged.filter((item) => savedIds.has(item.id))];
|
|
4128
|
+
}, [key, items, query.applyTo, query.order]);
|
|
4006
4129
|
const client2 = reactQuery.useQueryClient();
|
|
4007
4130
|
const pending3 = React.useRef(false);
|
|
4008
4131
|
const ready = Boolean(supported && !query.error && query.arrangement?.item_id && query.resolutionLevel === "member" && query.arrangement.write_target_id === query.arrangement.item_id);
|
|
@@ -4029,7 +4152,7 @@ function usePersonalItemOrder(key, items) {
|
|
|
4029
4152
|
if (cached?.item_id !== query.arrangement.item_id || JSON.stringify(cached?.type_data?.order ?? []) !== JSON.stringify(query.order ?? [])) {
|
|
4030
4153
|
throw new Error("The saved order changed. Try dragging again");
|
|
4031
4154
|
}
|
|
4032
|
-
const current =
|
|
4155
|
+
const current = orderedItems.map((item) => item.id);
|
|
4033
4156
|
if (ids.every((id, index2) => current[index2] === id)) return;
|
|
4034
4157
|
const visible = new Set(visibleIds);
|
|
4035
4158
|
let index = 0;
|
|
@@ -4048,7 +4171,7 @@ function usePersonalItemOrder(key, items) {
|
|
|
4048
4171
|
}
|
|
4049
4172
|
}
|
|
4050
4173
|
return {
|
|
4051
|
-
items:
|
|
4174
|
+
items: orderedItems,
|
|
4052
4175
|
ready,
|
|
4053
4176
|
supported,
|
|
4054
4177
|
contextKey: `${generation}:${scopePath}:${key}`,
|
|
@@ -4336,34 +4459,109 @@ function startTracking(options = {}) {
|
|
|
4336
4459
|
return trackingCleanup;
|
|
4337
4460
|
}
|
|
4338
4461
|
|
|
4339
|
-
// src/
|
|
4462
|
+
// src/workspace.ts
|
|
4340
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
|
+
});
|
|
4341
4520
|
function initializeFsaos(options = {}) {
|
|
4342
|
-
if (
|
|
4343
|
-
|
|
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 () => {
|
|
4344
4530
|
const tracking = options.tracking ?? true;
|
|
4345
4531
|
const stopTracking = tracking === false ? () => {
|
|
4346
4532
|
} : startTracking(typeof tracking === "object" ? tracking : {});
|
|
4533
|
+
stopActiveTracking = stopTracking;
|
|
4347
4534
|
try {
|
|
4348
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");
|
|
4349
4538
|
let disposed = false;
|
|
4350
4539
|
const runtime = {
|
|
4351
4540
|
session,
|
|
4541
|
+
workspace,
|
|
4352
4542
|
dispose() {
|
|
4353
4543
|
if (disposed) return;
|
|
4354
4544
|
disposed = true;
|
|
4355
4545
|
stopTracking();
|
|
4356
|
-
|
|
4546
|
+
if (generation === getSessionGeneration()) {
|
|
4547
|
+
initialization2 = void 0;
|
|
4548
|
+
workspaceAccount = void 0;
|
|
4549
|
+
stopActiveTracking = void 0;
|
|
4550
|
+
}
|
|
4357
4551
|
}
|
|
4358
4552
|
};
|
|
4359
4553
|
return runtime;
|
|
4360
4554
|
} catch (error) {
|
|
4361
4555
|
stopTracking();
|
|
4362
|
-
|
|
4556
|
+
if (generation === getSessionGeneration()) {
|
|
4557
|
+
initialization2 = void 0;
|
|
4558
|
+
workspaceAccount = void 0;
|
|
4559
|
+
stopActiveTracking = void 0;
|
|
4560
|
+
}
|
|
4363
4561
|
throw error;
|
|
4364
4562
|
}
|
|
4365
4563
|
})();
|
|
4366
|
-
return
|
|
4564
|
+
return initialization2;
|
|
4367
4565
|
}
|
|
4368
4566
|
|
|
4369
4567
|
// src/execution-control.ts
|
|
@@ -5458,6 +5656,7 @@ exports.hasServerClock = hasServerClock;
|
|
|
5458
5656
|
exports.initSession = initSession;
|
|
5459
5657
|
exports.initVfsRealtimeWs = initVfsRealtimeWs;
|
|
5460
5658
|
exports.initializeFsaos = initializeFsaos;
|
|
5659
|
+
exports.initializeWorkspace = initializeWorkspace;
|
|
5461
5660
|
exports.interpretSchema = interpretSchema;
|
|
5462
5661
|
exports.invalidateAllVfs = invalidateAllVfs;
|
|
5463
5662
|
exports.invalidateChannelMessages = invalidateChannelMessages;
|
|
@@ -5473,6 +5672,7 @@ exports.isListableItemsFilter = isListableItemsFilter;
|
|
|
5473
5672
|
exports.isScopeReady = isScopeReady;
|
|
5474
5673
|
exports.itemsFilterFromQueryFilter = itemsFilterFromQueryFilter;
|
|
5475
5674
|
exports.listChildren = listChildren;
|
|
5675
|
+
exports.loadMounts = loadMounts;
|
|
5476
5676
|
exports.mount = mount2;
|
|
5477
5677
|
exports.mountRealtimeScope = mountRealtimeScope;
|
|
5478
5678
|
exports.normalizeItem = normalizeItem;
|
|
@@ -5484,9 +5684,11 @@ exports.queryClient = queryClient;
|
|
|
5484
5684
|
exports.readItem = readItem;
|
|
5485
5685
|
exports.refreshQuery = refreshQuery;
|
|
5486
5686
|
exports.registerCleanup = registerCleanup;
|
|
5687
|
+
exports.replaceMounts = replaceMounts;
|
|
5487
5688
|
exports.resetAllSdkState = resetAllSdkState;
|
|
5488
5689
|
exports.resolvePrincipalId = resolvePrincipalId;
|
|
5489
5690
|
exports.resubscribePath = resubscribePath;
|
|
5691
|
+
exports.selectMount = selectMount;
|
|
5490
5692
|
exports.serverDeadlinePassed = serverDeadlinePassed;
|
|
5491
5693
|
exports.serverMsUntil = serverMsUntil;
|
|
5492
5694
|
exports.serverNow = serverNow;
|