@fias/arche-sdk 2.29.0 → 2.30.0

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/index.mjs CHANGED
@@ -276,8 +276,14 @@ var FiasBridge = class {
276
276
  this.holder = null;
277
277
  this.holderListeners = /* @__PURE__ */ new Set();
278
278
  this.currentPath = "";
279
+ // Host-chrome state (useFiasChrome). `null` until init, and from a host
280
+ // older than the chrome lane — `hasChrome` tells the two apart.
281
+ this.chrome = null;
282
+ this.hasChrome = false;
283
+ this.chromeListeners = /* @__PURE__ */ new Set();
279
284
  this.hostOrigin = "";
280
285
  this.themeListeners = /* @__PURE__ */ new Set();
286
+ this.accessListeners = /* @__PURE__ */ new Set();
281
287
  this.navigationListeners = /* @__PURE__ */ new Set();
282
288
  this.stepNavigationListeners = /* @__PURE__ */ new Set();
283
289
  // Preview-state preservation (preview mode only). Keyed so multiple
@@ -331,6 +337,8 @@ var FiasBridge = class {
331
337
  this.textModels = initMsg.payload.textModels ?? null;
332
338
  this.holder = initMsg.payload.holder ?? null;
333
339
  this.currentPath = initMsg.payload.currentPath;
340
+ this.hasChrome = "chrome" in initMsg.payload;
341
+ this.chrome = initMsg.payload.chrome ?? null;
334
342
  this.initialized = true;
335
343
  this.initResolve?.();
336
344
  for (const listener of this.navigationListeners) {
@@ -339,6 +347,9 @@ var FiasBridge = class {
339
347
  for (const listener of this.holderListeners) {
340
348
  listener(this.holder);
341
349
  }
350
+ for (const listener of this.chromeListeners) {
351
+ listener(this.chrome);
352
+ }
342
353
  return;
343
354
  }
344
355
  if (this.hostOrigin && event.origin !== this.hostOrigin) return;
@@ -349,6 +360,10 @@ var FiasBridge = class {
349
360
  }
350
361
  return;
351
362
  }
363
+ if (data.type === "access_update") {
364
+ for (const listener of this.accessListeners) listener();
365
+ return;
366
+ }
352
367
  if (data.type === "text_models_update") {
353
368
  if (Array.isArray(data.payload)) {
354
369
  this.textModels = data.payload;
@@ -365,6 +380,16 @@ var FiasBridge = class {
365
380
  }
366
381
  return;
367
382
  }
383
+ if (data.type === "chrome_update") {
384
+ if (data.payload && typeof data.payload === "object") {
385
+ this.hasChrome = true;
386
+ this.chrome = data.payload;
387
+ for (const listener of this.chromeListeners) {
388
+ listener(this.chrome);
389
+ }
390
+ }
391
+ return;
392
+ }
368
393
  if (data.type === "step_navigate") {
369
394
  const { nodeId } = data.payload;
370
395
  if (typeof nodeId === "string") {
@@ -592,6 +617,37 @@ var FiasBridge = class {
592
617
  getCurrentPath() {
593
618
  return this.currentPath;
594
619
  }
620
+ /**
621
+ * The host-chrome state, or null before init / from a host without the
622
+ * chrome lane (`hasChromeSupport` tells the two apart). See `useFiasChrome`.
623
+ */
624
+ getChrome() {
625
+ return this.chrome;
626
+ }
627
+ /** True once a host has delivered chrome state (init or chrome_update). */
628
+ hasChromeSupport() {
629
+ return this.hasChrome;
630
+ }
631
+ /** Called on init and on every chrome_update. */
632
+ onChromeUpdate(listener) {
633
+ this.chromeListeners.add(listener);
634
+ return () => this.chromeListeners.delete(listener);
635
+ }
636
+ /**
637
+ * Set host-chrome state for this arche (fire-and-forget; the host echoes the
638
+ * result as chrome_update). Every present key must be a boolean — the host
639
+ * drops the whole message otherwise. See `useFiasChrome`.
640
+ */
641
+ setChrome(patch) {
642
+ this.send("chrome_set", patch);
643
+ }
644
+ /**
645
+ * Leave the arche for a host destination — a closed enum, never a URL.
646
+ * See `useFiasChrome`.
647
+ */
648
+ navigateHost(target) {
649
+ this.send("chrome_navigate", { target });
650
+ }
595
651
  /**
596
652
  * The live text-model catalog, or null until the host has delivered it (or
597
653
  * when the plugin lacks `entities:invoke`). See `useTextModels`.
@@ -620,6 +676,14 @@ var FiasBridge = class {
620
676
  this.themeListeners.add(listener);
621
677
  return () => this.themeListeners.delete(listener);
622
678
  }
679
+ /**
680
+ * The host says the person's own access may have changed (ADR-034) — after
681
+ * its access sheet changed it. Carries nothing: ask `my_access` again.
682
+ */
683
+ onAccessUpdate(listener) {
684
+ this.accessListeners.add(listener);
685
+ return () => this.accessListeners.delete(listener);
686
+ }
623
687
  onNavigationUpdate(listener) {
624
688
  this.navigationListeners.add(listener);
625
689
  return () => this.navigationListeners.delete(listener);
@@ -743,6 +807,7 @@ var FiasBridge = class {
743
807
  }
744
808
  this.pendingRequests.clear();
745
809
  this.themeListeners.clear();
810
+ this.accessListeners.clear();
746
811
  this.textModelListeners.clear();
747
812
  this.holderListeners.clear();
748
813
  this.navigationListeners.clear();
@@ -777,7 +842,7 @@ var FiasBridge = class {
777
842
  /**
778
843
  * Listens for the terminal ending of a logical realtime subscription.
779
844
  * Reasons are a closed union — `authorization_revoked` and
780
- * `collection_deleted` are definitive; the rest may be worth a fresh
845
+ * `collection_deleted` (the resource is gone) are definitive; the rest may be worth a fresh
781
846
  * subscribe once conditions change.
782
847
  */
783
848
  onSubscriptionEnded(subscriptionId, listener) {
@@ -1088,6 +1153,7 @@ var PLUGIN_WORKSPACES_ENTITY_ID = "ent_intg_plugin_workspaces_00000";
1088
1153
  var PLUGIN_STORE_ENTITY_ID = "ent_intg_plugin_store_0000000000";
1089
1154
  var VAULT_USER_DOCUMENTS_ENTITY_ID = "ent_intg_vault_documents_0000000";
1090
1155
  var VAULT_ARCHE_DOCUMENTS_ENTITY_ID = "ent_intg_vault_arche_docs_000000";
1156
+ var VAULT_CREATIVE_ENTITY_ID = "ent_intg_vault_creative_00000000";
1091
1157
  var COMMUNITY_ASSETS_ENTITY_ID = "ent_intg_community_assets_000000";
1092
1158
  var DOCUMENT_SIGN_ENTITY_ID = "ent_intg_document_sign_000000000";
1093
1159
  var COMPANY_EMAIL_SEND_ENTITY_ID = "ent_intg_company_email_send_0000";
@@ -1096,6 +1162,8 @@ var CONNECTIONS_CONNECT_ENTITY_ID = "ent_intg_connections_connect_000";
1096
1162
  var X_ENTITY_ID = "ent_intg_twitter_000000000000000";
1097
1163
  var LINKEDIN_ENTITY_ID = "ent_intg_linkedin_00000000000000";
1098
1164
  var ACTION_CONFIRMATION_ENTITY_ID = "ent_intg_action_confirmation_000";
1165
+ var APP_ACCESS_DIALOG_ENTITY_ID = "ent_intg_app_access_dialog_00000";
1166
+ var CREATIVE_PICKER_ENTITY_ID = "ent_intg_creative_picker_0000000";
1099
1167
  var SCHEDULED_INVOCATIONS_ENTITY_ID = "ent_intg_scheduled_invocations00";
1100
1168
  var CONFIRM_TIMEOUT_MS = 10 * 60 * 1e3;
1101
1169
  var APPROVED_CALL_TIMEOUT_MS = 2 * 60 * 1e3;
@@ -1573,6 +1641,72 @@ function usePersistentState(key, initialValue) {
1573
1641
  );
1574
1642
  return [value, setValue];
1575
1643
  }
1644
+ var INERT_CHROME = {
1645
+ focusMode: false,
1646
+ focusModeAvailable: false,
1647
+ sidebarAvailable: false,
1648
+ sidebarCollapsed: false,
1649
+ hostExpandedBar: true,
1650
+ hostExpandButton: true
1651
+ };
1652
+ function useFiasChrome() {
1653
+ const bridge = useBridge();
1654
+ const [chrome, setChrome] = useState2(bridge.getChrome());
1655
+ const [supported, setSupported] = useState2(bridge.hasChromeSupport());
1656
+ useEffect2(() => {
1657
+ let cancelled = false;
1658
+ bridge.waitForInit().then(() => {
1659
+ if (cancelled) return;
1660
+ setChrome(bridge.getChrome());
1661
+ setSupported(bridge.hasChromeSupport());
1662
+ });
1663
+ const unsubscribe = bridge.onChromeUpdate((next) => {
1664
+ setChrome(next);
1665
+ setSupported(bridge.hasChromeSupport());
1666
+ });
1667
+ return () => {
1668
+ cancelled = true;
1669
+ unsubscribe();
1670
+ };
1671
+ }, [bridge]);
1672
+ useEffect2(() => {
1673
+ bridge.setChrome({ hostExpandButton: false });
1674
+ }, [bridge]);
1675
+ const setFocusMode = useCallback((on) => bridge.setChrome({ focusMode: on }), [bridge]);
1676
+ const setSidebarCollapsed = useCallback(
1677
+ (collapsed) => bridge.setChrome({ sidebarCollapsed: collapsed }),
1678
+ [bridge]
1679
+ );
1680
+ const setHostExpandedBar = useCallback(
1681
+ (shown) => bridge.setChrome({ hostExpandedBar: shown }),
1682
+ [bridge]
1683
+ );
1684
+ const goToFiasHome = useCallback(() => bridge.navigateHost("home"), [bridge]);
1685
+ const goToWorktop = useCallback(() => bridge.navigateHost("worktop"), [bridge]);
1686
+ const openAssistant = useCallback(() => bridge.openAssistant(), [bridge]);
1687
+ return useMemo2(
1688
+ () => ({
1689
+ ...chrome ?? INERT_CHROME,
1690
+ supported: supported && chrome !== null,
1691
+ setFocusMode,
1692
+ setSidebarCollapsed,
1693
+ setHostExpandedBar,
1694
+ goToFiasHome,
1695
+ goToWorktop,
1696
+ openAssistant
1697
+ }),
1698
+ [
1699
+ chrome,
1700
+ supported,
1701
+ setFocusMode,
1702
+ setSidebarCollapsed,
1703
+ setHostExpandedBar,
1704
+ goToFiasHome,
1705
+ goToWorktop,
1706
+ openAssistant
1707
+ ]
1708
+ );
1709
+ }
1576
1710
  function useStepNavigation(initialStep, options) {
1577
1711
  const bridge = useBridge();
1578
1712
  const persistKey = options?.persistKey;
@@ -1674,15 +1808,34 @@ function useFiasDataStore() {
1674
1808
  "put",
1675
1809
  {
1676
1810
  collection,
1677
- key,
1811
+ // No key: the server makes one (collections with audiences, ADR-034).
1812
+ ...key !== null ? { key } : {},
1678
1813
  data,
1679
- workspaceId: options?.workspaceId
1814
+ workspaceId: options?.workspaceId,
1815
+ ...options?.audience !== void 0 ? { audience: options.audience } : {}
1680
1816
  }
1681
1817
  );
1682
- return { version: res?.version };
1818
+ return {
1819
+ version: res?.version,
1820
+ ...res?.key !== void 0 ? { key: res.key } : {},
1821
+ ...res?.audience !== void 0 ? { audience: res.audience } : {}
1822
+ };
1683
1823
  },
1684
1824
  [bridge]
1685
1825
  );
1826
+ const create = useCallback(
1827
+ async (collection, data, options) => {
1828
+ const result = await put(collection, null, data, options);
1829
+ if (result.key === void 0) {
1830
+ throw new FiasBridgeError(
1831
+ "This Fias does not support creating documents in audiences yet",
1832
+ "AUDIENCES_UNAVAILABLE"
1833
+ );
1834
+ }
1835
+ return { ...result, key: result.key };
1836
+ },
1837
+ [put]
1838
+ );
1686
1839
  const get = useCallback(
1687
1840
  async (collection, key, options) => {
1688
1841
  const res = await invokeEntityOp(
@@ -1692,7 +1845,8 @@ function useFiasDataStore() {
1692
1845
  {
1693
1846
  collection,
1694
1847
  key,
1695
- workspaceId: options?.workspaceId
1848
+ workspaceId: options?.workspaceId,
1849
+ ...options?.audience !== void 0 ? { audience: options.audience } : {}
1696
1850
  }
1697
1851
  );
1698
1852
  return res.data;
@@ -1704,7 +1858,8 @@ function useFiasDataStore() {
1704
1858
  const res = await invokeEntityOp(bridge, PLUGIN_DATASTORE_ENTITY_ID, "get", {
1705
1859
  collection,
1706
1860
  key,
1707
- workspaceId: options?.workspaceId
1861
+ workspaceId: options?.workspaceId,
1862
+ ...options?.audience !== void 0 ? { audience: options.audience } : {}
1708
1863
  });
1709
1864
  if (res.data === null || res.data === void 0) return null;
1710
1865
  return {
@@ -1712,7 +1867,8 @@ function useFiasDataStore() {
1712
1867
  data: res.data,
1713
1868
  updatedAt: res.updatedAt ?? "",
1714
1869
  ...res.version !== void 0 ? { version: res.version } : {},
1715
- ...res.authoredByCollaborator !== void 0 ? { authoredByCollaborator: res.authoredByCollaborator } : {}
1870
+ ...res.authoredByCollaborator !== void 0 ? { authoredByCollaborator: res.authoredByCollaborator } : {},
1871
+ ...res.audience !== void 0 ? { audience: res.audience } : {}
1716
1872
  };
1717
1873
  },
1718
1874
  [bridge]
@@ -1729,7 +1885,8 @@ function useFiasDataStore() {
1729
1885
  // answered WORKSPACE_REQUIRED, pointing the author at the collection
1730
1886
  // rather than at the call. Take the scope arg when it carries one and
1731
1887
  // otherwise leave whatever the spread put there.
1732
- ...scope?.workspaceId !== void 0 ? { workspaceId: scope.workspaceId } : {}
1888
+ ...scope?.workspaceId !== void 0 ? { workspaceId: scope.workspaceId } : {},
1889
+ ...scope?.audience !== void 0 ? { audience: scope.audience } : {}
1733
1890
  });
1734
1891
  },
1735
1892
  [bridge]
@@ -1745,7 +1902,8 @@ function useFiasDataStore() {
1745
1902
  query: queryText,
1746
1903
  ...options,
1747
1904
  // See `query` above — never blank a caller-supplied workspaceId.
1748
- ...scope?.workspaceId !== void 0 ? { workspaceId: scope.workspaceId } : {}
1905
+ ...scope?.workspaceId !== void 0 ? { workspaceId: scope.workspaceId } : {},
1906
+ ...scope?.audience !== void 0 ? { audience: scope.audience } : {}
1749
1907
  }
1750
1908
  );
1751
1909
  return res.matches;
@@ -1783,6 +1941,7 @@ function useFiasDataStore() {
1783
1941
  listCollections,
1784
1942
  deleteCollection,
1785
1943
  put,
1944
+ create,
1786
1945
  get,
1787
1946
  getDocument,
1788
1947
  query,
@@ -1795,6 +1954,7 @@ function useFiasDataStore() {
1795
1954
  listCollections,
1796
1955
  deleteCollection,
1797
1956
  put,
1957
+ create,
1798
1958
  get,
1799
1959
  getDocument,
1800
1960
  query,
@@ -1928,6 +2088,213 @@ function useFiasWorkspaces() {
1928
2088
  ]
1929
2089
  );
1930
2090
  }
2091
+ var NO_WORKSPACE_ACCESS_CODES = /* @__PURE__ */ new Set([
2092
+ "NOT_A_MEMBER",
2093
+ "WORKSPACE_NOT_FOUND"
2094
+ ]);
2095
+ function useFiasAccess(workspaceId) {
2096
+ const bridge = useBridge();
2097
+ const known = workspaceId ? peekSharedAccess(bridge, workspaceId) : void 0;
2098
+ const [settled, setSettled] = useState2(known?.settled ?? null);
2099
+ const [fetching, setFetching] = useState2(known ? known.fetching : !!workspaceId);
2100
+ useEffect2(() => {
2101
+ if (!workspaceId) return;
2102
+ const shared = sharedAccessFor(bridge, workspaceId);
2103
+ const sync = () => {
2104
+ setSettled(shared.settled);
2105
+ setFetching(shared.fetching);
2106
+ };
2107
+ const unsubscribe = subscribeSharedAccess(shared, sync);
2108
+ askSharedAccess(shared, "ensure");
2109
+ sync();
2110
+ return unsubscribe;
2111
+ }, [bridge, workspaceId]);
2112
+ const refetch = useCallback(() => {
2113
+ if (workspaceId) askSharedAccess(sharedAccessFor(bridge, workspaceId), "again");
2114
+ }, [bridge, workspaceId]);
2115
+ const openAccessDialog = useCallback(async () => {
2116
+ let answer;
2117
+ try {
2118
+ answer = await bridge.request(
2119
+ "client_entity_invoke",
2120
+ { entityId: APP_ACCESS_DIALOG_ENTITY_ID, input: {} },
2121
+ ACCESS_SHEET_TIMEOUT_MS
2122
+ );
2123
+ } catch (err) {
2124
+ if (err instanceof FiasBridgeError && err.code === "BRIDGE_ERROR" && err.message.includes("no client-side executor")) {
2125
+ throw new FiasBridgeError("Managing access is not available here", "ACCESS_UNAVAILABLE");
2126
+ }
2127
+ throw err;
2128
+ }
2129
+ if (answer && "error" in answer) {
2130
+ throw new FiasBridgeError(answer.error.message, answer.error.code);
2131
+ }
2132
+ const changed = answer?.changed === true;
2133
+ if (changed) refetch();
2134
+ return { changed };
2135
+ }, [bridge, refetch]);
2136
+ const own = workspaceId && settled?.workspaceId === workspaceId ? settled : null;
2137
+ const current = known ? known.settled : own;
2138
+ const access = current?.access ?? null;
2139
+ const error = current?.error ?? null;
2140
+ const isLoading = !!workspaceId && (current === null || (known ? known.fetching : fetching));
2141
+ const can = useCallback(
2142
+ (permission) => access?.mode === "roles" && access.permissions.includes(permission),
2143
+ [access]
2144
+ );
2145
+ const canReadAudience = useCallback(
2146
+ (audience) => access?.mode === "roles" && (access.audiences?.readable.includes(audience) ?? false),
2147
+ [access]
2148
+ );
2149
+ const canDiscloseTo = useCallback(
2150
+ (audience) => access?.mode === "roles" && (access.audiences?.disclosable.includes(audience) ?? false),
2151
+ [access]
2152
+ );
2153
+ return useMemo2(
2154
+ () => ({
2155
+ access,
2156
+ isLoading,
2157
+ error,
2158
+ refetch,
2159
+ can,
2160
+ canReadAudience,
2161
+ canDiscloseTo,
2162
+ openAccessDialog
2163
+ }),
2164
+ [access, isLoading, error, refetch, can, canReadAudience, canDiscloseTo, openAccessDialog]
2165
+ );
2166
+ }
2167
+ var lastAccessAnswers = /* @__PURE__ */ new Map();
2168
+ var FOCUS_REFETCH_MIN_MS = 3e4;
2169
+ var ACCESS_SHEET_TIMEOUT_MS = 30 * 60 * 1e3;
2170
+ var sharedAccessByBridge = /* @__PURE__ */ new WeakMap();
2171
+ function peekSharedAccess(bridge, workspaceId) {
2172
+ return sharedAccessByBridge.get(bridge)?.get(workspaceId);
2173
+ }
2174
+ function sharedAccessFor(bridge, workspaceId) {
2175
+ let byWorkspace = sharedAccessByBridge.get(bridge);
2176
+ if (!byWorkspace) {
2177
+ byWorkspace = /* @__PURE__ */ new Map();
2178
+ sharedAccessByBridge.set(bridge, byWorkspace);
2179
+ }
2180
+ let shared = byWorkspace.get(workspaceId);
2181
+ if (!shared) {
2182
+ shared = {
2183
+ bridge,
2184
+ workspaceId,
2185
+ settled: null,
2186
+ fetching: false,
2187
+ answered: false,
2188
+ answeredAt: 0,
2189
+ inFlight: null,
2190
+ scheduled: false,
2191
+ again: false,
2192
+ listeners: /* @__PURE__ */ new Set(),
2193
+ stopListening: null
2194
+ };
2195
+ byWorkspace.set(workspaceId, shared);
2196
+ }
2197
+ return shared;
2198
+ }
2199
+ function releaseSharedAccess(shared) {
2200
+ if (shared.listeners.size > 0 || shared.inFlight || shared.scheduled) return;
2201
+ const byWorkspace = sharedAccessByBridge.get(shared.bridge);
2202
+ if (byWorkspace?.get(shared.workspaceId) === shared) byWorkspace.delete(shared.workspaceId);
2203
+ }
2204
+ function notifySharedAccess(shared) {
2205
+ for (const listener of Array.from(shared.listeners)) listener();
2206
+ }
2207
+ function subscribeSharedAccess(shared, listener) {
2208
+ shared.listeners.add(listener);
2209
+ if (!shared.stopListening) {
2210
+ const unsubscribe = shared.bridge.onAccessUpdate(() => askSharedAccess(shared, "again"));
2211
+ const onFocus = () => {
2212
+ if (Date.now() - shared.answeredAt >= FOCUS_REFETCH_MIN_MS) {
2213
+ askSharedAccess(shared, "join");
2214
+ }
2215
+ };
2216
+ const win = typeof window === "undefined" ? null : window;
2217
+ win?.addEventListener("focus", onFocus);
2218
+ shared.stopListening = () => {
2219
+ unsubscribe();
2220
+ win?.removeEventListener("focus", onFocus);
2221
+ };
2222
+ }
2223
+ return () => {
2224
+ shared.listeners.delete(listener);
2225
+ if (shared.listeners.size === 0 && shared.stopListening) {
2226
+ shared.stopListening();
2227
+ shared.stopListening = null;
2228
+ }
2229
+ releaseSharedAccess(shared);
2230
+ };
2231
+ }
2232
+ function askSharedAccess(shared, demand) {
2233
+ if (shared.inFlight) {
2234
+ if (demand === "again") shared.again = true;
2235
+ return;
2236
+ }
2237
+ if (shared.scheduled) return;
2238
+ if (demand === "ensure" && shared.answered) return;
2239
+ shared.scheduled = true;
2240
+ void Promise.resolve().then(() => {
2241
+ shared.scheduled = false;
2242
+ startSharedAccess(shared);
2243
+ });
2244
+ }
2245
+ function settleSharedAccess(shared, access) {
2246
+ const answer = JSON.stringify(access);
2247
+ const before = lastAccessAnswers.get(shared.workspaceId);
2248
+ const noAccess = access.mode === "tier" && access.tier === null;
2249
+ if (noAccess || before !== void 0 && before !== answer) clearSdkReadCaches();
2250
+ lastAccessAnswers.set(shared.workspaceId, answer);
2251
+ shared.answered = true;
2252
+ shared.answeredAt = Date.now();
2253
+ shared.settled = { workspaceId: shared.workspaceId, access, error: null };
2254
+ }
2255
+ function startSharedAccess(shared) {
2256
+ if (shared.listeners.size === 0) {
2257
+ releaseSharedAccess(shared);
2258
+ return;
2259
+ }
2260
+ const { bridge, workspaceId } = shared;
2261
+ const background = shared.answered;
2262
+ if (!background) {
2263
+ shared.fetching = true;
2264
+ notifySharedAccess(shared);
2265
+ }
2266
+ shared.inFlight = (async () => {
2267
+ try {
2268
+ const res = await invokeEntityOp(
2269
+ bridge,
2270
+ PLUGIN_WORKSPACES_ENTITY_ID,
2271
+ "my_access",
2272
+ { workspaceId }
2273
+ );
2274
+ if (shared.listeners.size > 0) settleSharedAccess(shared, res.access);
2275
+ } catch (err) {
2276
+ if (shared.listeners.size === 0) {
2277
+ } else if (err instanceof FiasBridgeError && NO_WORKSPACE_ACCESS_CODES.has(err.code)) {
2278
+ settleSharedAccess(shared, { mode: "tier", workspaceId, tier: null, expiresAt: null });
2279
+ } else if (!background) {
2280
+ shared.settled = {
2281
+ workspaceId,
2282
+ access: null,
2283
+ error: err instanceof Error ? err : new Error(String(err))
2284
+ };
2285
+ }
2286
+ } finally {
2287
+ shared.fetching = false;
2288
+ shared.inFlight = null;
2289
+ if (shared.again) {
2290
+ shared.again = false;
2291
+ askSharedAccess(shared, "again");
2292
+ }
2293
+ notifySharedAccess(shared);
2294
+ releaseSharedAccess(shared);
2295
+ }
2296
+ })();
2297
+ }
1931
2298
  function useFiasStore() {
1932
2299
  const bridge = useBridge();
1933
2300
  const [products, setProducts] = useState2([]);
@@ -2013,6 +2380,12 @@ function useFiasStore() {
2013
2380
  var DOWNLOAD_URL_REFRESH_BUFFER_MS = 6e4;
2014
2381
  var downloadUrlCache = /* @__PURE__ */ new Map();
2015
2382
  var downloadUrlInflight = /* @__PURE__ */ new Map();
2383
+ function clearSdkReadCaches() {
2384
+ downloadUrlGeneration += 1;
2385
+ downloadUrlCache.clear();
2386
+ downloadUrlInflight.clear();
2387
+ }
2388
+ var downloadUrlGeneration = 0;
2016
2389
  async function fetchVaultDocumentDownloadUrl(documentId) {
2017
2390
  const now = Date.now();
2018
2391
  const cached = downloadUrlCache.get(documentId);
@@ -2022,6 +2395,7 @@ async function fetchVaultDocumentDownloadUrl(documentId) {
2022
2395
  const inflight = downloadUrlInflight.get(documentId);
2023
2396
  if (inflight) return inflight;
2024
2397
  const bridge = getBridge();
2398
+ const generation = downloadUrlGeneration;
2025
2399
  const promise = bridge.request(
2026
2400
  "vault_documents_download_url",
2027
2401
  { documentId }
@@ -2031,14 +2405,16 @@ async function fetchVaultDocumentDownloadUrl(documentId) {
2031
2405
  expiresAt: new Date(result.expiresAt).getTime(),
2032
2406
  contentType: result.contentType
2033
2407
  };
2034
- downloadUrlCache.set(documentId, {
2035
- url: normalized.url,
2036
- expiresAt: normalized.expiresAt,
2037
- contentType: normalized.contentType
2038
- });
2408
+ if (generation === downloadUrlGeneration) {
2409
+ downloadUrlCache.set(documentId, {
2410
+ url: normalized.url,
2411
+ expiresAt: normalized.expiresAt,
2412
+ contentType: normalized.contentType
2413
+ });
2414
+ }
2039
2415
  return normalized;
2040
2416
  }).finally(() => {
2041
- downloadUrlInflight.delete(documentId);
2417
+ if (downloadUrlInflight.get(documentId) === promise) downloadUrlInflight.delete(documentId);
2042
2418
  });
2043
2419
  downloadUrlInflight.set(documentId, promise);
2044
2420
  return promise;
@@ -2799,13 +3175,22 @@ function useFiasPreviewState(key, getState, onRestore) {
2799
3175
  );
2800
3176
  }, [bridge, key]);
2801
3177
  }
2802
- function useDataSubscription(collection, onInvalidate, options) {
3178
+ function stableStringify(value) {
3179
+ return JSON.stringify(
3180
+ value,
3181
+ (_key, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(
3182
+ Object.keys(v).sort().map((k) => [k, v[k]])
3183
+ ) : v
3184
+ );
3185
+ }
3186
+ function useEntitySubscription(entityId, payload, onInvalidate, options) {
2803
3187
  const bridge = useBridge();
2804
3188
  const [state, setState] = useState2({ status: "subscribing" });
2805
3189
  const onInvalidateRef = useRef(onInvalidate);
2806
3190
  onInvalidateRef.current = onInvalidate;
2807
3191
  const enabled = options?.enabled ?? true;
2808
- const workspaceId = options?.workspaceId;
3192
+ const minRefetchIntervalMs = Math.max(0, options?.minRefetchIntervalMs ?? 0);
3193
+ const payloadKey = stableStringify(payload);
2809
3194
  useEffect2(() => {
2810
3195
  if (!enabled) {
2811
3196
  setState({ status: "ended", endedReason: "unsubscribed" });
@@ -2817,12 +3202,27 @@ function useDataSubscription(collection, onInvalidate, options) {
2817
3202
  let unsubscribeEnded = null;
2818
3203
  let refetching = false;
2819
3204
  let trailing = false;
3205
+ let lastRunAt = -Infinity;
3206
+ let intervalTimer = null;
2820
3207
  const runInvalidate = async () => {
2821
- if (refetching) {
3208
+ if (refetching || intervalTimer) {
2822
3209
  trailing = true;
2823
3210
  return;
2824
3211
  }
3212
+ const wait = lastRunAt + minRefetchIntervalMs - Date.now();
3213
+ if (wait > 0) {
3214
+ trailing = true;
3215
+ intervalTimer = setTimeout(() => {
3216
+ intervalTimer = null;
3217
+ if (trailing && !cancelled) {
3218
+ trailing = false;
3219
+ void runInvalidate();
3220
+ }
3221
+ }, wait);
3222
+ return;
3223
+ }
2825
3224
  refetching = true;
3225
+ lastRunAt = Date.now();
2826
3226
  try {
2827
3227
  await onInvalidateRef.current();
2828
3228
  } catch {
@@ -2837,8 +3237,8 @@ function useDataSubscription(collection, onInvalidate, options) {
2837
3237
  void (async () => {
2838
3238
  try {
2839
3239
  const res = await bridge.request("entity_invoke", {
2840
- entityId: PLUGIN_DATASTORE_ENTITY_ID,
2841
- payload: { op: "subscribe", collection, workspaceId }
3240
+ entityId,
3241
+ payload: JSON.parse(payloadKey)
2842
3242
  });
2843
3243
  const subscription = res?.output;
2844
3244
  if (!subscription || subscription.kind !== "subscription" || typeof subscription.subscriptionId !== "string") {
@@ -2862,13 +3262,28 @@ function useDataSubscription(collection, onInvalidate, options) {
2862
3262
  })();
2863
3263
  return () => {
2864
3264
  cancelled = true;
3265
+ if (intervalTimer) clearTimeout(intervalTimer);
2865
3266
  unsubscribeEvent?.();
2866
3267
  unsubscribeEnded?.();
2867
3268
  if (subscriptionId) bridge.releaseSubscription(subscriptionId);
2868
3269
  };
2869
- }, [bridge, collection, workspaceId, enabled]);
3270
+ }, [bridge, entityId, payloadKey, enabled, minRefetchIntervalMs]);
2870
3271
  return state;
2871
3272
  }
3273
+ function useDataSubscription(collection, onInvalidate, options) {
3274
+ const audience = options?.audience;
3275
+ return useEntitySubscription(
3276
+ PLUGIN_DATASTORE_ENTITY_ID,
3277
+ {
3278
+ op: "subscribe",
3279
+ collection,
3280
+ workspaceId: options?.workspaceId,
3281
+ ...audience !== void 0 ? { audience } : {}
3282
+ },
3283
+ onInvalidate,
3284
+ { enabled: options?.enabled }
3285
+ );
3286
+ }
2872
3287
  function useVaultUserDocuments() {
2873
3288
  const bridge = useBridge();
2874
3289
  const pick = useCallback(
@@ -3068,6 +3483,51 @@ async function fetchCommunityAssetUrls(bridge, assetIds) {
3068
3483
  }
3069
3484
  return hits;
3070
3485
  }
3486
+ var CREATIVE_PICKER_TIMEOUT_MS = 30 * 60 * 1e3;
3487
+ function useCreativeLibrary() {
3488
+ const bridge = useBridge();
3489
+ return useMemo2(() => {
3490
+ const item = async (op, args) => (await invokeEntityOp(bridge, VAULT_CREATIVE_ENTITY_ID, op, args)).item;
3491
+ return {
3492
+ list: async (options = {}) => (await invokeEntityOp(
3493
+ bridge,
3494
+ VAULT_CREATIVE_ENTITY_ID,
3495
+ "list_items",
3496
+ { ...options }
3497
+ )).items,
3498
+ get: (itemId, version) => item("get_item", { itemId, ...version !== void 0 ? { version } : {} }),
3499
+ create: (fields) => item("create_item", { referenceDocumentIds: [], ...fields }),
3500
+ update: (itemId, fields) => item("update_item", { itemId, ...fields }),
3501
+ remove: (itemId) => item("delete_item", { itemId }),
3502
+ restore: (itemId) => item("restore_item", { itemId }),
3503
+ pick: async (options = {}) => {
3504
+ let answer;
3505
+ try {
3506
+ answer = await bridge.request(
3507
+ "client_entity_invoke",
3508
+ {
3509
+ entityId: CREATIVE_PICKER_ENTITY_ID,
3510
+ input: options.kind ? { kind: options.kind } : {}
3511
+ },
3512
+ CREATIVE_PICKER_TIMEOUT_MS
3513
+ );
3514
+ } catch (err) {
3515
+ if (err instanceof FiasBridgeError && err.code === "BRIDGE_ERROR" && err.message.includes("no client-side executor")) {
3516
+ throw new FiasBridgeError(
3517
+ "The Creative Library picker is not available here",
3518
+ "PICKER_UNAVAILABLE"
3519
+ );
3520
+ }
3521
+ throw err;
3522
+ }
3523
+ if (answer && "error" in answer) {
3524
+ throw new FiasBridgeError(answer.error.message, answer.error.code);
3525
+ }
3526
+ return answer;
3527
+ }
3528
+ };
3529
+ }, [bridge]);
3530
+ }
3071
3531
  var CONNECT_TIMEOUT_MS = 10 * 60 * 1e3;
3072
3532
  function useFiasConnections() {
3073
3533
  const bridge = useBridge();
@@ -3422,6 +3882,8 @@ var AUDIO_VOICE_DESIGN_SURFACE_KEY = "audio.voice-design";
3422
3882
  var useElevenLabsVoiceDesign = () => useSurface(
3423
3883
  AUDIO_VOICE_DESIGN_SURFACE_KEY
3424
3884
  );
3885
+ var IMAGE_CREATE_ASSET_SURFACE_KEY = "image.create-asset";
3886
+ var useCreateAsset = () => useSurface(IMAGE_CREATE_ASSET_SURFACE_KEY);
3425
3887
  var IMAGE_SMART_CONTROL_SURFACE_KEY = "image.smart-control";
3426
3888
  var useSmartControl = () => useSurface(IMAGE_SMART_CONTROL_SURFACE_KEY);
3427
3889
  var IMAGE_SMART_ERASE_SURFACE_KEY = "image.smart-erase";
@@ -3478,6 +3940,7 @@ var VALID_PLUGIN_PERMISSIONS = [
3478
3940
  "entities:image_generate",
3479
3941
  "entities:invoke",
3480
3942
  "entities:web_search",
3943
+ "litcraft:hub",
3481
3944
  "navigation:open_arche",
3482
3945
  "sandbox:vendored-libraries",
3483
3946
  "schedule:invoke",
@@ -3487,6 +3950,8 @@ var VALID_PLUGIN_PERMISSIONS = [
3487
3950
  "theme:read",
3488
3951
  "user:profile:read",
3489
3952
  "vault:arche-saves:write",
3953
+ "vault:creative:read",
3954
+ "vault:creative:write",
3490
3955
  "vault:documents:read",
3491
3956
  "vault:documents:write",
3492
3957
  "vault:user-documents:edit",
@@ -3515,6 +3980,7 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
3515
3980
  "entities:image_generate": "Generate images with platform AI \u2014 costs credits.",
3516
3981
  "entities:invoke": "Use platform AI (text) on your behalf \u2014 costs credits.",
3517
3982
  "entities:web_search": "Search the live web on your behalf \u2014 costs credits.",
3983
+ "litcraft:hub": "See your LitCraft profile, stats and inventory, and the LitCraft community leaderboards, play time and activity. It can hide your name on leaderboards, but only you can show it again.",
3518
3984
  "navigation:open_arche": "Open other arches for you (navigation only).",
3519
3985
  "sandbox:vendored-libraries": "Load platform-hosted libraries (like the PDF renderer) from the trusted CDN.",
3520
3986
  "schedule:invoke": "Schedule an action for later \u2014 like a post at 9 AM tomorrow. You approve exactly what will be sent and when, and can see or cancel what it scheduled.",
@@ -3524,6 +3990,8 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
3524
3990
  "theme:read": "Match your platform theme.",
3525
3991
  "user:profile:read": "See your display name and avatar.",
3526
3992
  "vault:arche-saves:write": "Saves files to your Arche Saves automatically.",
3993
+ "vault:creative:read": "See and use your styles and characters, including their reference pictures \u2014 in a team workspace only after its owner or admin approves it there.",
3994
+ "vault:creative:write": "Create and change your styles and characters. Earlier versions are kept.",
3527
3995
  "vault:documents:read": "Read files this arche itself created, in its own folders of your Vault.",
3528
3996
  "vault:documents:write": "Create and manage its own files, in its own folder of your Vault.",
3529
3997
  "vault:user-documents:edit": "Save changes to files of yours that you let it edit \u2014 you choose each file, earlier versions are kept unless you turned version history off, and you can remove access any time in Vault \u2192 Arche Access.",
@@ -6092,6 +6560,7 @@ export {
6092
6560
  FiasLocalStoreError,
6093
6561
  FiasProvider,
6094
6562
  GRAMMAR_CHECK_ENTITY_ID,
6563
+ IMAGE_CREATE_ASSET_SURFACE_KEY,
6095
6564
  IMAGE_SMART_CONTROL_SURFACE_KEY,
6096
6565
  IMAGE_SMART_ERASE_SURFACE_KEY,
6097
6566
  IMAGE_SMART_IMAGE_TO_IMAGE_SURFACE_KEY,
@@ -6156,6 +6625,8 @@ export {
6156
6625
  useClientEntity,
6157
6626
  useCommunityAssets,
6158
6627
  useCompanyEmail,
6628
+ useCreateAsset,
6629
+ useCreativeLibrary,
6159
6630
  useDataSubscription,
6160
6631
  useDocumentSign,
6161
6632
  useElevenLabsAudioIsolation,
@@ -6167,7 +6638,10 @@ export {
6167
6638
  useElevenLabsVoiceChanger,
6168
6639
  useElevenLabsVoiceDesign,
6169
6640
  useEntityInvocation,
6641
+ useEntitySubscription,
6170
6642
  useFiasAIActions,
6643
+ useFiasAccess,
6644
+ useFiasChrome,
6171
6645
  useFiasConnections,
6172
6646
  useFiasContent,
6173
6647
  useFiasDataStore,