@ai4b-team/fsaos-gateway-sdk 3.34.2 → 3.35.1
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 +224 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -2
- package/dist/index.d.ts +89 -2
- package/dist/index.js +224 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/iife/ui.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* @fsaos/ui v2.0.0 — FSAOS UI Primitives — 2026-10-
|
|
1
|
+
/* @fsaos/ui v2.0.0 — FSAOS UI Primitives — 2026-10-07T03:44:34.236Z */
|
|
2
2
|
"use strict";var __FSAOS_UI__=(()=>{var m=Object.create;var r=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var f=Object.getPrototypeOf,h=Object.prototype.hasOwnProperty;var y=(e,i)=>()=>(i||e((i={exports:{}}).exports,i),i.exports),N=(e,i)=>{for(var a in i)r(e,a,{get:i[a],enumerable:!0})},s=(e,i,a,l)=>{if(i&&typeof i=="object"||typeof i=="function")for(let t of u(i))!h.call(e,t)&&t!==a&&r(e,t,{get:()=>i[t],enumerable:!(l=g(i,t))||l.enumerable});return e};var x=(e,i,a)=>(a=e!=null?m(f(e)):{},s(i||!e||!e.__esModule?r(a,"default",{value:e,enumerable:!0}):a,e)),b=e=>s(r({},"__esModule",{value:!0}),e);var d=y((V,c)=>{c.exports=React});var P={};N(P,{Badge:()=>z,Breadcrumbs:()=>A,Button:()=>D,Card:()=>w,DataView:()=>R,Detail:()=>_,Empty:()=>I,FilePreview:()=>O,Form:()=>F,Grid:()=>k,Image:()=>G,Link:()=>E,Page:()=>v,Router:()=>T,Sidebar:()=>S,Spinner:()=>B,Stack:()=>C,Table:()=>j,Tabs:()=>W,Video:()=>H});var n=x(d(),1);function o(){return(typeof window<"u"&&window.__FSAOS_CONFIG__||{}).edgeBaseUrl||""}function v(e){return(0,n.createElement)("div",{style:{minHeight:"100vh",display:"flex",flexDirection:"column"},className:e.className||""},e.children)}function C(e){let i=e.direction||"vertical";return(0,n.createElement)("div",{style:{display:"flex",flexDirection:i==="horizontal"?"row":"column",gap:e.gap||"0.5rem",padding:e.padding||"0",alignItems:e.align||"stretch",justifyContent:e.justify||"flex-start",flex:e.flex||"initial"},className:e.className||""},e.children)}function k(e){return(0,n.createElement)("div",{style:{display:"grid",gridTemplateColumns:"repeat("+(e.columns||3)+", 1fr)",gap:e.gap||"1rem",padding:e.padding||"0"},className:e.className||""},e.children)}function S(e){return(0,n.createElement)("div",{style:{display:"flex",flexDirection:"row",minHeight:"100vh"},className:e.className||""},e.children)}function w(e){return(0,n.createElement)("div",{style:{border:"1px solid var(--color-border, #e5e7eb)",borderRadius:"var(--border-radius, 0.5rem)",padding:e.padding||"1rem",background:"var(--color-surface, #fff)"},className:e.className||"",onClick:e.onClick},e.children)}function z(e){return(0,n.createElement)("span",{style:{display:"inline-flex",alignItems:"center",padding:"0.125rem 0.5rem",fontSize:"0.75rem",fontWeight:"500",borderRadius:"9999px",background:e.color||"#e5e7eb",color:e.textColor||"#374151"},className:e.className||""},e.children||e.label)}function B(e){let i=e.size||"1.5rem";return(0,n.createElement)("div",{style:{width:i,height:i,border:"2px solid #e5e7eb",borderTopColor:"var(--color-primary, #3b82f6)",borderRadius:"50%",animation:"fsaos-spin 0.6s linear infinite"}})}function D(e){return(0,n.createElement)("button",{style:{display:"inline-flex",alignItems:"center",justifyContent:"center",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:"500",borderRadius:"var(--border-radius, 0.375rem)",border:e.variant==="outline"?"1px solid var(--color-border, #d1d5db)":"none",background:e.variant==="outline"||e.variant==="ghost"?"transparent":"var(--color-primary, #3b82f6)",color:e.variant==="outline"||e.variant==="ghost"?"var(--color-text, #1a1a1a)":"#fff",cursor:e.disabled?"not-allowed":"pointer",opacity:e.disabled?"0.5":"1",gap:"0.5rem"},className:e.className||"",onClick:e.onClick,disabled:e.disabled},e.children||e.label)}function I(e){return(0,n.createElement)("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"3rem",color:"var(--color-text-secondary, #6b7280)",textAlign:"center"}},e.icon?(0,n.createElement)("div",{style:{fontSize:"2rem",marginBottom:"0.5rem"}},e.icon):null,(0,n.createElement)("div",{style:{fontWeight:500,marginBottom:"0.25rem"}},e.title||e.message||e.children||"No items"),e.description?(0,n.createElement)("div",{style:{fontSize:"0.875rem",opacity:.7}},e.description):null)}function R(e){return(0,n.createElement)("div",{className:e.className||""},e.children)}function j(e){return(0,n.createElement)("table",{className:e.className||""},e.children)}function F(e){return(0,n.createElement)("form",{className:e.className||"",onSubmit:e.onSubmit},e.children)}function _(e){return(0,n.createElement)("div",{className:e.className||""},e.children)}function T(e){return(0,n.createElement)("div",null,e.children)}function E(e){return(0,n.createElement)("a",{href:e.href||e.to,className:e.className||"",onClick:e.onClick},e.children)}function W(e){return(0,n.createElement)("div",{className:e.className||""},e.children)}function A(e){return(0,n.createElement)("nav",{className:e.className||""},e.children)}function G(e){return(0,n.createElement)("img",{src:e.src||(e.path?o()+e.path:""),alt:e.alt||"",className:e.className||"",style:e.style})}function H(e){return(0,n.createElement)("video",{src:e.src||(e.path?o()+e.path:""),controls:!0,className:e.className||""})}function O(e){return(0,n.createElement)("div",{className:e.className||""},e.children||"File preview")}return b(P);})();
|
|
3
3
|
//# sourceMappingURL=ui.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -782,6 +782,10 @@ function listingTypes(filter) {
|
|
|
782
782
|
}
|
|
783
783
|
function affectsListing(query, filter, event) {
|
|
784
784
|
if (containsChangedItem(query, event)) return true;
|
|
785
|
+
if (filter.spaceIds !== void 0) {
|
|
786
|
+
if (!filter.spaceIds.length) return false;
|
|
787
|
+
if (event.space_item_id !== void 0 && !filter.spaceIds.includes(event.space_item_id) && !filter.spaceIds.includes(event.old_space_item_id)) return false;
|
|
788
|
+
}
|
|
785
789
|
const scope = filter.scope;
|
|
786
790
|
const scoped = typeof scope === "string" && scope !== "__unscoped__";
|
|
787
791
|
const hasPath = typeof event.path === "string" || typeof event.old_path === "string";
|
|
@@ -825,6 +829,29 @@ function clearInfiniteItemsRefreshes(client2) {
|
|
|
825
829
|
pending2.delete(client2);
|
|
826
830
|
}
|
|
827
831
|
|
|
832
|
+
// src/items-selection.ts
|
|
833
|
+
var SelectedSpaceContractError = class extends Error {
|
|
834
|
+
constructor() {
|
|
835
|
+
super("The server did not confirm the selected-Space listing contract");
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
function assertItemsSelection(requested, returned) {
|
|
839
|
+
if (!Array.isArray(returned) || returned.some((id) => typeof id !== "string") || JSON.stringify([...new Set(returned)].sort()) !== JSON.stringify(requested)) {
|
|
840
|
+
throw new SelectedSpaceContractError();
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
function normalizeItemsSelection(filter) {
|
|
844
|
+
if (!filter || filter.spaceIds === void 0) return filter;
|
|
845
|
+
return {
|
|
846
|
+
...filter,
|
|
847
|
+
spaceIds: [...new Set(filter.spaceIds.map((id) => id.toLowerCase()))].sort(),
|
|
848
|
+
...filter.limit !== void 0 ? { limit: Math.max(1, Math.min(filter.limit, 500)) } : {}
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function unavailableItemsSelection(error) {
|
|
852
|
+
return error instanceof SelectedSpaceContractError || error instanceof EnforcementDeniedError || error instanceof GatewayRequestError && (error.payload?.error === "SPACE_SELECTION_UNAVAILABLE" || error.payload?.error === "QUERY_PARAMS_INVALID" || error.status === 401 || error.status === 403);
|
|
853
|
+
}
|
|
854
|
+
|
|
828
855
|
// src/vfs-keys.ts
|
|
829
856
|
function sk() {
|
|
830
857
|
return getScope() ?? "__unscoped__";
|
|
@@ -847,7 +874,7 @@ var vfsKeys = {
|
|
|
847
874
|
/** Search results (scope-aware — search is scoped). */
|
|
848
875
|
search: (query, types) => ["vfs", sk(), "search", query, ...types ?? []],
|
|
849
876
|
/** Items query (type-based listing with filters, scope-aware). */
|
|
850
|
-
items: (type, filters) => ["vfs", sk(), "items", type, JSON.stringify(filters ?? {})],
|
|
877
|
+
items: (type, filters) => ["vfs", sk(), "items", type, JSON.stringify(normalizeItemsSelection(filters ?? {}))],
|
|
851
878
|
/** All items queries for current scope (for broad invalidation). */
|
|
852
879
|
allItems: () => ["vfs", sk(), "items"],
|
|
853
880
|
/** Recursive tree at a path + depth (scope-aware). */
|
|
@@ -1737,12 +1764,15 @@ function itemsFilterFromQueryFilter(typeData, extra = {}) {
|
|
|
1737
1764
|
return { ...filter, ...extra };
|
|
1738
1765
|
}
|
|
1739
1766
|
async function fetchItems(filter, options = {}) {
|
|
1767
|
+
filter = normalizeItemsSelection(filter);
|
|
1768
|
+
if (filter.spaceIds?.length === 0) return { items: [], total_hint: 0 };
|
|
1740
1769
|
const params = {};
|
|
1741
1770
|
if (filter.type) params.item_type = filter.type;
|
|
1742
1771
|
if (filter.scope) {
|
|
1743
1772
|
params.scope_path = filter.scope;
|
|
1744
1773
|
}
|
|
1745
1774
|
const filters = {};
|
|
1775
|
+
if (filter.spaceIds !== void 0) filters.space_ids = filter.spaceIds;
|
|
1746
1776
|
if (filter.name) filters.name = filter.name;
|
|
1747
1777
|
if (filter.tag) filters.tag = filter.tag;
|
|
1748
1778
|
if (filter.parent_id) filters.parent_id = filter.parent_id;
|
|
@@ -1769,6 +1799,9 @@ async function fetchItems(filter, options = {}) {
|
|
|
1769
1799
|
}
|
|
1770
1800
|
try {
|
|
1771
1801
|
const response = await gatewayCall("query", params);
|
|
1802
|
+
if (filter.spaceIds !== void 0) {
|
|
1803
|
+
assertItemsSelection(filter.spaceIds, response.selected_space_ids ?? response.content?.selected_space_ids);
|
|
1804
|
+
}
|
|
1772
1805
|
const items = extractItems(response);
|
|
1773
1806
|
for (const item of items) {
|
|
1774
1807
|
getQueryClient().setQueryData(vfsKeys.item(item.path), item);
|
|
@@ -1776,7 +1809,7 @@ async function fetchItems(filter, options = {}) {
|
|
|
1776
1809
|
return { items, total_hint: response.total ?? response.content?.total ?? items.length };
|
|
1777
1810
|
} catch (err) {
|
|
1778
1811
|
console.error(`[gateway] Failed to fetch items of type ${filter.type ?? "(any_of)"}:`, err);
|
|
1779
|
-
if (options.throwOnError) throw err;
|
|
1812
|
+
if (options.throwOnError || filter.spaceIds !== void 0) throw err;
|
|
1780
1813
|
return { items: [], total_hint: 0 };
|
|
1781
1814
|
}
|
|
1782
1815
|
}
|
|
@@ -2109,7 +2142,7 @@ function handleCcmChange(data) {
|
|
|
2109
2142
|
const sourceId = data.source_item_id || data.source_id;
|
|
2110
2143
|
const targetId = data.target_item_id || data.target_id;
|
|
2111
2144
|
const looksEdgeRelated = data.edge_type != null || sourceId != null || targetId != null;
|
|
2112
|
-
if (!looksEdgeRelated) return;
|
|
2145
|
+
if (!looksEdgeRelated || !sourceId && !targetId || data.item_type === "message") return;
|
|
2113
2146
|
const debounceKey = `ccm:${sourceId ?? ""}:${targetId ?? ""}`;
|
|
2114
2147
|
const existing = debounceTimers.get(debounceKey);
|
|
2115
2148
|
if (existing) clearTimeout(existing);
|
|
@@ -2121,11 +2154,6 @@ function handleCcmChange(data) {
|
|
|
2121
2154
|
if (targetId) {
|
|
2122
2155
|
getQueryClient().invalidateQueries({ queryKey: vfsKeys.edges(targetId) });
|
|
2123
2156
|
}
|
|
2124
|
-
if (!sourceId && !targetId) {
|
|
2125
|
-
getQueryClient().invalidateQueries({
|
|
2126
|
-
predicate: (query) => query.queryKey[0] === "vfs" && query.queryKey[1] === "edges"
|
|
2127
|
-
});
|
|
2128
|
-
}
|
|
2129
2157
|
}, DEBOUNCE_MS);
|
|
2130
2158
|
debounceTimers.set(debounceKey, timer);
|
|
2131
2159
|
}
|
|
@@ -2307,6 +2335,148 @@ function replaceMounts(refs, anchor) {
|
|
|
2307
2335
|
});
|
|
2308
2336
|
}
|
|
2309
2337
|
|
|
2338
|
+
// src/mounted-reader-events.ts
|
|
2339
|
+
function inMountedReadScope(event) {
|
|
2340
|
+
if (typeof event.path !== "string") return false;
|
|
2341
|
+
const roots = [...getMounts().map((mount3) => mount3.path), getAnchorRef()?.path].filter((path) => !!path);
|
|
2342
|
+
if (!roots.length && getScope()) roots.push(getScope());
|
|
2343
|
+
return roots.some((root) => isChannelPath(event.path, root));
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
// src/channel-message-events.ts
|
|
2347
|
+
function channelMessageChange(event, channelPath, parentId, messages) {
|
|
2348
|
+
if (!["message", "card"].includes(event?.item_type) || !isChannelPath(event.path, channelPath)) return false;
|
|
2349
|
+
if (!parentId) return true;
|
|
2350
|
+
if (event.item_id === parentId || event.parent_id === parentId) return true;
|
|
2351
|
+
return messages.some((message) => {
|
|
2352
|
+
if (event.item_id === message.id || event.parent_id === message.id) return true;
|
|
2353
|
+
const root = message.id === parentId ? message.path : message.parent_message_id === parentId ? message.path.slice(0, message.path.lastIndexOf("/")) : void 0;
|
|
2354
|
+
return !!root && isChannelPath(event.path, root);
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// src/channel-realtime.ts
|
|
2359
|
+
var watches = /* @__PURE__ */ new WeakMap();
|
|
2360
|
+
function watchChannels(client2, key, normalize) {
|
|
2361
|
+
let entries = watches.get(client2);
|
|
2362
|
+
if (!entries) {
|
|
2363
|
+
entries = /* @__PURE__ */ new Map();
|
|
2364
|
+
watches.set(client2, entries);
|
|
2365
|
+
}
|
|
2366
|
+
const hash = JSON.stringify(key);
|
|
2367
|
+
let watch = entries.get(hash);
|
|
2368
|
+
if (!watch) {
|
|
2369
|
+
let stopped = false;
|
|
2370
|
+
let timer;
|
|
2371
|
+
let running = false;
|
|
2372
|
+
let inventory = false;
|
|
2373
|
+
const channels = /* @__PURE__ */ new Set();
|
|
2374
|
+
const renames = /* @__PURE__ */ new Set();
|
|
2375
|
+
const current = () => client2.getQueryData(key) ?? [];
|
|
2376
|
+
const schedule2 = () => {
|
|
2377
|
+
if (stopped || running || timer) return;
|
|
2378
|
+
timer = setTimeout(() => {
|
|
2379
|
+
timer = void 0;
|
|
2380
|
+
void flush();
|
|
2381
|
+
}, 250);
|
|
2382
|
+
};
|
|
2383
|
+
const flush = async () => {
|
|
2384
|
+
if (stopped) return;
|
|
2385
|
+
running = true;
|
|
2386
|
+
const reload = inventory;
|
|
2387
|
+
inventory = false;
|
|
2388
|
+
const pending3 = [...channels];
|
|
2389
|
+
const checkRename = new Set(renames);
|
|
2390
|
+
renames.clear();
|
|
2391
|
+
channels.clear();
|
|
2392
|
+
try {
|
|
2393
|
+
if (reload) {
|
|
2394
|
+
await refreshQuery(client2, key, { exact: true });
|
|
2395
|
+
} else {
|
|
2396
|
+
const results = await Promise.allSettled(pending3.map(async (id) => {
|
|
2397
|
+
const owner = client2.getQueryCache().find({ queryKey: key, exact: true });
|
|
2398
|
+
if (!owner) return;
|
|
2399
|
+
const result = await gatewayCall("read", { id });
|
|
2400
|
+
if (result.success === false) throw new Error("Channel refresh failed");
|
|
2401
|
+
if (stopped || !owner || client2.getQueryCache().find({ queryKey: key, exact: true }) !== owner) return;
|
|
2402
|
+
const raw = normalizeItem(result.item ?? result);
|
|
2403
|
+
if (raw.id !== id || raw.item_type !== "channel") return;
|
|
2404
|
+
const previous = current().find((item2) => item2.id === id);
|
|
2405
|
+
if (!previous) return;
|
|
2406
|
+
const item = normalize({ ...raw });
|
|
2407
|
+
const next = {
|
|
2408
|
+
...previous,
|
|
2409
|
+
path: item.path,
|
|
2410
|
+
name: item.name,
|
|
2411
|
+
displayName: item.displayName,
|
|
2412
|
+
messageCount: item.messageCount,
|
|
2413
|
+
aiMode: item.aiMode,
|
|
2414
|
+
isPrivate: item.isPrivate,
|
|
2415
|
+
isDm: item.isDm,
|
|
2416
|
+
autoPilot: item.autoPilot,
|
|
2417
|
+
updatedAt: item.updatedAt
|
|
2418
|
+
};
|
|
2419
|
+
if (checkRename.has(id) && (previous.name !== next.name || previous.displayName !== next.displayName || previous.path !== next.path)) {
|
|
2420
|
+
inventory = true;
|
|
2421
|
+
}
|
|
2422
|
+
client2.setQueryData(key, (list) => list?.map((item2) => item2.id === id ? next : item2));
|
|
2423
|
+
}));
|
|
2424
|
+
const failed = results.find((result) => result.status === "rejected");
|
|
2425
|
+
if (failed?.status === "rejected") throw failed.reason;
|
|
2426
|
+
}
|
|
2427
|
+
} catch (error) {
|
|
2428
|
+
console.warn("[channels] refresh failed", error);
|
|
2429
|
+
} finally {
|
|
2430
|
+
running = false;
|
|
2431
|
+
if (inventory || channels.size) schedule2();
|
|
2432
|
+
}
|
|
2433
|
+
};
|
|
2434
|
+
const inScope = (data) => {
|
|
2435
|
+
if (current().some((c) => c.id === data.item_id || c.path === data.path)) return true;
|
|
2436
|
+
return inMountedReadScope(data);
|
|
2437
|
+
};
|
|
2438
|
+
const onChannel = (data) => {
|
|
2439
|
+
if (!inScope(data)) return;
|
|
2440
|
+
const action = String(data.action ?? "").toLowerCase();
|
|
2441
|
+
if (["insert", "delete", "rename", "move"].includes(action)) inventory = true;
|
|
2442
|
+
else if (action === "update") {
|
|
2443
|
+
const channel = current().find((c) => c.id === data.item_id || c.path === data.path);
|
|
2444
|
+
if (channel) {
|
|
2445
|
+
channels.add(channel.id);
|
|
2446
|
+
renames.add(channel.id);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
if (inventory || channels.size) schedule2();
|
|
2450
|
+
};
|
|
2451
|
+
const onMessage = (data) => {
|
|
2452
|
+
const channel = current().find((c) => c.id === data.parent_id);
|
|
2453
|
+
if (!channel) return;
|
|
2454
|
+
channels.add(channel.id);
|
|
2455
|
+
schedule2();
|
|
2456
|
+
};
|
|
2457
|
+
const stops = ["vfs_change", "ccm_change"].flatMap((type) => [
|
|
2458
|
+
subscribeToEvents(type, onChannel, (data) => data.item_type === "channel"),
|
|
2459
|
+
subscribeToEvents(type, onMessage, (data) => data.item_type === "message")
|
|
2460
|
+
]);
|
|
2461
|
+
watch = { users: 0, stop: () => {
|
|
2462
|
+
stopped = true;
|
|
2463
|
+
if (timer) clearTimeout(timer);
|
|
2464
|
+
stops.forEach((stop) => stop());
|
|
2465
|
+
} };
|
|
2466
|
+
entries.set(hash, watch);
|
|
2467
|
+
}
|
|
2468
|
+
watch.users++;
|
|
2469
|
+
let released = false;
|
|
2470
|
+
return () => {
|
|
2471
|
+
if (released) return;
|
|
2472
|
+
released = true;
|
|
2473
|
+
if (--watch.users === 0) {
|
|
2474
|
+
watch.stop();
|
|
2475
|
+
entries.delete(hash);
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2310
2480
|
// src/hooks.ts
|
|
2311
2481
|
function useScopeKey() {
|
|
2312
2482
|
return React.useSyncExternalStore(subscribeScope, getScopeVersion, getScopeVersion);
|
|
@@ -2390,22 +2560,28 @@ function useResolve(params) {
|
|
|
2390
2560
|
}
|
|
2391
2561
|
function useItems(filter) {
|
|
2392
2562
|
useScopeKey();
|
|
2393
|
-
const
|
|
2394
|
-
|
|
2563
|
+
const selected = normalizeItemsSelection(filter);
|
|
2564
|
+
const stableFilter = React.useMemo(() => selected, [JSON.stringify(selected)]);
|
|
2565
|
+
const query = reactQuery.useQuery({
|
|
2395
2566
|
queryKey: vfsKeys.items(
|
|
2396
2567
|
stableFilter?.type ?? "",
|
|
2397
2568
|
stableFilter
|
|
2398
2569
|
),
|
|
2399
2570
|
queryFn: () => fetchItems(stableFilter),
|
|
2400
2571
|
enabled: isListableItemsFilter(stableFilter),
|
|
2572
|
+
...stableFilter?.spaceIds !== void 0 ? {
|
|
2573
|
+
retry: (attempt, error) => !unavailableItemsSelection(error) && attempt < 1
|
|
2574
|
+
} : {},
|
|
2401
2575
|
select: (result) => result.items
|
|
2402
2576
|
});
|
|
2577
|
+
return stableFilter?.spaceIds !== void 0 && unavailableItemsSelection(query.error) ? { ...query, data: void 0 } : query;
|
|
2403
2578
|
}
|
|
2404
2579
|
function useInfiniteItems(filter, options = {}) {
|
|
2405
2580
|
const scopeKey = useScopeKey();
|
|
2406
2581
|
const { realtime = false } = options;
|
|
2407
|
-
const
|
|
2408
|
-
const
|
|
2582
|
+
const selected = normalizeItemsSelection(filter);
|
|
2583
|
+
const PAGE_SIZE = selected?.limit ?? 20;
|
|
2584
|
+
const stableFilter = React.useMemo(() => selected, [JSON.stringify(selected)]);
|
|
2409
2585
|
const stableKey = React.useMemo(() => ["vfs", getScope() ?? "__unscoped__", "infinite-items", stableFilter?.type ?? "", JSON.stringify(stableFilter ?? {})], [scopeKey, stableFilter]);
|
|
2410
2586
|
const query = reactQuery.useInfiniteQuery({
|
|
2411
2587
|
queryKey: stableKey,
|
|
@@ -2418,16 +2594,21 @@ function useInfiniteItems(filter, options = {}) {
|
|
|
2418
2594
|
},
|
|
2419
2595
|
initialPageParam: 0,
|
|
2420
2596
|
getNextPageParam: (lastPage, _allPages, lastPageParam) => {
|
|
2597
|
+
if (stableFilter?.spaceIds !== void 0 && lastPage.total_hint !== void 0 && lastPageParam + lastPage.items.length >= lastPage.total_hint) return void 0;
|
|
2421
2598
|
if (lastPage.items.length < PAGE_SIZE) return void 0;
|
|
2422
2599
|
return lastPageParam + lastPage.items.length;
|
|
2423
2600
|
},
|
|
2424
2601
|
enabled: isListableItemsFilter(stableFilter),
|
|
2602
|
+
...stableFilter?.spaceIds !== void 0 ? {
|
|
2603
|
+
retry: (attempt, error) => !unavailableItemsSelection(error) && attempt < 1
|
|
2604
|
+
} : {},
|
|
2425
2605
|
// Realtime marks the cache stale only on relevant changes or reconnect.
|
|
2426
2606
|
// Expanding a channel or handing it to another observer reuses its pages.
|
|
2427
2607
|
staleTime: realtime ? Infinity : void 0,
|
|
2428
2608
|
meta: realtime && stableFilter ? { infiniteItemsRealtime: { filter: stableFilter } } : {}
|
|
2429
2609
|
});
|
|
2430
2610
|
const allItems = React.useMemo(() => {
|
|
2611
|
+
if (stableFilter?.spaceIds !== void 0 && unavailableItemsSelection(query.error)) return [];
|
|
2431
2612
|
if (!query.data?.pages) return [];
|
|
2432
2613
|
const seen = /* @__PURE__ */ new Set();
|
|
2433
2614
|
const flat = [];
|
|
@@ -2440,17 +2621,18 @@ function useInfiniteItems(filter, options = {}) {
|
|
|
2440
2621
|
}
|
|
2441
2622
|
}
|
|
2442
2623
|
return flat;
|
|
2443
|
-
}, [query.data?.pages]);
|
|
2624
|
+
}, [query.data?.pages, query.error, stableFilter]);
|
|
2625
|
+
const unavailable = stableFilter?.spaceIds !== void 0 && unavailableItemsSelection(query.error);
|
|
2444
2626
|
return {
|
|
2445
2627
|
allItems,
|
|
2446
2628
|
refetch: query.refetch,
|
|
2447
2629
|
fetchNextPage: query.fetchNextPage,
|
|
2448
2630
|
isFetchingNextPage: query.isFetchingNextPage,
|
|
2449
|
-
isExhausted: !query.hasNextPage,
|
|
2631
|
+
isExhausted: unavailable || !query.hasNextPage,
|
|
2450
2632
|
isLoading: query.isLoading,
|
|
2451
2633
|
isFetching: query.isFetching,
|
|
2452
2634
|
error: query.error,
|
|
2453
|
-
totalHint: query.data?.pages?.[0]?.total_hint
|
|
2635
|
+
totalHint: unavailable ? void 0 : query.data?.pages?.[0]?.total_hint
|
|
2454
2636
|
};
|
|
2455
2637
|
}
|
|
2456
2638
|
function useInfiniteChildren(path) {
|
|
@@ -2758,10 +2940,15 @@ function useMounts() {
|
|
|
2758
2940
|
useScopeKey();
|
|
2759
2941
|
const version = React.useSyncExternalStore(subscribeMounts, getMountVersion, getMountVersion);
|
|
2760
2942
|
const mounts = React.useMemo(() => getMounts(), [version]);
|
|
2943
|
+
const anchor = getAnchorRef();
|
|
2944
|
+
const selectedSpaces = React.useMemo(() => mounts.filter((ref) => ref.kind === "space" && (anchor?.kind === "account" || ref.id === anchor?.id)), [mounts, anchor]);
|
|
2945
|
+
const readSpaceIds = React.useMemo(() => selectedSpaces.map((ref) => ref.id).sort(), [selectedSpaces]);
|
|
2761
2946
|
return {
|
|
2762
2947
|
mounts,
|
|
2948
|
+
selectedSpaces,
|
|
2949
|
+
readSpaceIds,
|
|
2763
2950
|
homeAnchor: getHomeAnchor(),
|
|
2764
|
-
anchor
|
|
2951
|
+
anchor,
|
|
2765
2952
|
setAnchor: selectMount,
|
|
2766
2953
|
replaceMounts,
|
|
2767
2954
|
mount,
|
|
@@ -3267,15 +3454,18 @@ function useChannelMessages(options) {
|
|
|
3267
3454
|
void refreshQuery(qc, stableKey, { exact: true });
|
|
3268
3455
|
}, 500);
|
|
3269
3456
|
},
|
|
3270
|
-
(data) =>
|
|
3271
|
-
|
|
3272
|
-
|
|
3457
|
+
(data) => channelMessageChange(
|
|
3458
|
+
data,
|
|
3459
|
+
channelPath,
|
|
3460
|
+
parentMessageId,
|
|
3461
|
+
qc.getQueryData(stableKey)?.messages ?? []
|
|
3462
|
+
)
|
|
3273
3463
|
);
|
|
3274
3464
|
return () => {
|
|
3275
3465
|
unsub();
|
|
3276
3466
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
3277
3467
|
};
|
|
3278
|
-
}, [realtime, isEnabled, channelPath, stableKey, qc]);
|
|
3468
|
+
}, [realtime, isEnabled, channelPath, parentMessageId, stableKey, qc]);
|
|
3279
3469
|
const response = query.data;
|
|
3280
3470
|
const messages = response?.messages ?? [];
|
|
3281
3471
|
const messageCards = response?.messageCards ?? /* @__PURE__ */ new Map();
|
|
@@ -3462,9 +3652,12 @@ function useInfiniteChannelMessages(options) {
|
|
|
3462
3652
|
void tail();
|
|
3463
3653
|
}, 500);
|
|
3464
3654
|
},
|
|
3465
|
-
(data) =>
|
|
3466
|
-
|
|
3467
|
-
|
|
3655
|
+
(data) => channelMessageChange(
|
|
3656
|
+
data,
|
|
3657
|
+
channelPath,
|
|
3658
|
+
parentMessageId,
|
|
3659
|
+
qc.getQueryData(stableKey)?.pages.flatMap((page) => page.messages) ?? []
|
|
3660
|
+
)
|
|
3468
3661
|
);
|
|
3469
3662
|
return () => {
|
|
3470
3663
|
disposed = true;
|
|
@@ -3532,7 +3725,6 @@ function useAllChannels(options = {}) {
|
|
|
3532
3725
|
const scopeKey = useScopeKey();
|
|
3533
3726
|
const { realtime = true, enabled: enabled2 = true } = options;
|
|
3534
3727
|
const qc = reactQuery.useQueryClient();
|
|
3535
|
-
const debounceRef = React.useRef(null);
|
|
3536
3728
|
const stableKey = React.useMemo(() => vfsKeys.channels("all"), [scopeKey]);
|
|
3537
3729
|
const query = reactQuery.useQuery({
|
|
3538
3730
|
queryKey: stableKey,
|
|
@@ -3549,31 +3741,7 @@ function useAllChannels(options = {}) {
|
|
|
3549
3741
|
});
|
|
3550
3742
|
React.useEffect(() => {
|
|
3551
3743
|
if (!realtime || !enabled2) return;
|
|
3552
|
-
|
|
3553
|
-
"vfs_change",
|
|
3554
|
-
() => {
|
|
3555
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
3556
|
-
debounceRef.current = setTimeout(() => {
|
|
3557
|
-
qc.invalidateQueries({ queryKey: stableKey });
|
|
3558
|
-
}, 1e3);
|
|
3559
|
-
},
|
|
3560
|
-
(data) => data.item_type === "channel" || data.item_type === "message"
|
|
3561
|
-
);
|
|
3562
|
-
const unsub2 = subscribeToEvents(
|
|
3563
|
-
"ccm_change",
|
|
3564
|
-
() => {
|
|
3565
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
3566
|
-
debounceRef.current = setTimeout(() => {
|
|
3567
|
-
qc.invalidateQueries({ queryKey: stableKey });
|
|
3568
|
-
}, 1e3);
|
|
3569
|
-
},
|
|
3570
|
-
(data) => data.item_type === "channel" || data.item_type === "message"
|
|
3571
|
-
);
|
|
3572
|
-
return () => {
|
|
3573
|
-
unsub();
|
|
3574
|
-
unsub2();
|
|
3575
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
3576
|
-
};
|
|
3744
|
+
return watchChannels(qc, stableKey, normalizeChannel);
|
|
3577
3745
|
}, [realtime, enabled2, stableKey, qc]);
|
|
3578
3746
|
const channels = query.data ?? [];
|
|
3579
3747
|
return {
|
|
@@ -3632,7 +3800,7 @@ function useNotifications(options = {}) {
|
|
|
3632
3800
|
qc.invalidateQueries({ queryKey: stableKey });
|
|
3633
3801
|
}, 2e3);
|
|
3634
3802
|
},
|
|
3635
|
-
(data) => data.item_type === "notification"
|
|
3803
|
+
(data) => data.item_type === "notification" && inMountedReadScope(data)
|
|
3636
3804
|
)
|
|
3637
3805
|
);
|
|
3638
3806
|
return () => {
|
|
@@ -3859,6 +4027,8 @@ function useEventStream(handlerOrEventType, handlerOrOptions, filter) {
|
|
|
3859
4027
|
function useRealtimeQuery(options) {
|
|
3860
4028
|
const { queryKey, queryFn, eventType, filter, enabled: enabled2 = true, debounceMs = 500 } = options;
|
|
3861
4029
|
const qc = reactQuery.useQueryClient();
|
|
4030
|
+
const filterRef = React.useRef(filter);
|
|
4031
|
+
filterRef.current = filter;
|
|
3862
4032
|
const debounceRef = React.useRef(null);
|
|
3863
4033
|
const query = reactQuery.useQuery({
|
|
3864
4034
|
queryKey,
|
|
@@ -3872,10 +4042,10 @@ function useRealtimeQuery(options) {
|
|
|
3872
4042
|
() => {
|
|
3873
4043
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
3874
4044
|
debounceRef.current = setTimeout(() => {
|
|
3875
|
-
qc
|
|
4045
|
+
void refreshQuery(qc, queryKey, { exact: true });
|
|
3876
4046
|
}, debounceMs);
|
|
3877
4047
|
},
|
|
3878
|
-
|
|
4048
|
+
(data) => filterRef.current?.(data) ?? true
|
|
3879
4049
|
);
|
|
3880
4050
|
return () => {
|
|
3881
4051
|
unsub();
|
|
@@ -5487,11 +5657,11 @@ function useTimelineHistory(channelPath, options) {
|
|
|
5487
5657
|
qc.invalidateQueries({ queryKey });
|
|
5488
5658
|
}, debounceMs);
|
|
5489
5659
|
},
|
|
5490
|
-
//
|
|
5660
|
+
// Rows are written to the dataset item, not descendant items.
|
|
5491
5661
|
(data) => {
|
|
5492
5662
|
const eventPath = data?.path;
|
|
5493
5663
|
if (!eventPath) return false;
|
|
5494
|
-
return eventPath === datasetPath
|
|
5664
|
+
return eventPath === datasetPath && data.item_type === "dataset";
|
|
5495
5665
|
}
|
|
5496
5666
|
);
|
|
5497
5667
|
return () => {
|