@fias/arche-sdk 2.19.1 → 2.20.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
@@ -1008,7 +1008,10 @@ function useFiasStorage() {
1008
1008
  },
1009
1009
  [bridge]
1010
1010
  );
1011
- return { readFile, writeFile, listFiles, deleteFile };
1011
+ return useMemo2(
1012
+ () => ({ readFile, writeFile, listFiles, deleteFile }),
1013
+ [readFile, writeFile, listFiles, deleteFile]
1014
+ );
1012
1015
  }
1013
1016
  function useEntityInvocation() {
1014
1017
  const bridge = useBridge();
@@ -1243,21 +1246,30 @@ function useFiasNavigation() {
1243
1246
  bridge.request("open_arche", {
1244
1247
  archeId,
1245
1248
  newTab: opts?.newTab === true,
1246
- ...opts?.path ? { path: opts.path } : {}
1249
+ ...opts?.path ? { path: opts.path } : {},
1250
+ ...opts?.payload ? { payload: opts.payload } : {}
1247
1251
  });
1248
1252
  },
1249
1253
  [bridge]
1250
1254
  );
1251
- return { navigateTo, openArche, currentPath };
1255
+ return useMemo2(
1256
+ () => ({ navigateTo, openArche, currentPath }),
1257
+ [navigateTo, openArche, currentPath]
1258
+ );
1252
1259
  }
1253
1260
  function usePersistentState(key, initialValue) {
1254
1261
  const bridge = useBridge();
1255
1262
  const [value, setValueInternal] = useState2(initialValue);
1256
1263
  const initializedRef = useRef(false);
1257
1264
  const valueRef = useRef(initialValue);
1265
+ const setByPluginRef = useRef(false);
1258
1266
  const writer = useMemo2(() => createDebouncedWriter(bridge), [bridge]);
1259
1267
  useEffect2(() => {
1260
1268
  invokeEntityOp(bridge, PLUGIN_STORAGE_ENTITY_ID, "read", { path: `__state/${key}` }).then((result) => {
1269
+ if (setByPluginRef.current) {
1270
+ initializedRef.current = true;
1271
+ return;
1272
+ }
1261
1273
  if (result.exists && result.content !== null) {
1262
1274
  try {
1263
1275
  const parsed = JSON.parse(result.content);
@@ -1277,6 +1289,7 @@ function usePersistentState(key, initialValue) {
1277
1289
  const setValue = useCallback(
1278
1290
  (next) => {
1279
1291
  const resolved = typeof next === "function" ? next(valueRef.current) : next;
1292
+ setByPluginRef.current = true;
1280
1293
  valueRef.current = resolved;
1281
1294
  setValueInternal(resolved);
1282
1295
  writer.schedule(`__state/${key}`, JSON.stringify(resolved));
@@ -1740,9 +1753,10 @@ function useVaultDocuments() {
1740
1753
  );
1741
1754
  const read = useCallback(
1742
1755
  async (documentId) => {
1743
- return bridge.request("vault_documents_read", {
1744
- documentId
1745
- });
1756
+ return bridge.request(
1757
+ "vault_documents_read",
1758
+ { documentId }
1759
+ );
1746
1760
  },
1747
1761
  [bridge]
1748
1762
  );
@@ -1767,16 +1781,29 @@ function useVaultDocuments() {
1767
1781
  );
1768
1782
  const saveContent = useCallback(
1769
1783
  async (params) => {
1770
- const res = await invokeEntityOp(
1771
- bridge,
1772
- VAULT_ARCHE_DOCUMENTS_ENTITY_ID,
1773
- "save_own_document_content",
1774
- {
1775
- documentId: params.documentId,
1776
- content: params.content,
1777
- contentType: params.contentType
1778
- }
1779
- );
1784
+ const routed = await routeSaveContent(params.content);
1785
+ let res;
1786
+ if (routed.lane === "body") {
1787
+ res = await invokeEntityOp(
1788
+ bridge,
1789
+ VAULT_ARCHE_DOCUMENTS_ENTITY_ID,
1790
+ "save_own_document_content",
1791
+ {
1792
+ documentId: params.documentId,
1793
+ content: routed.text,
1794
+ contentType: params.contentType,
1795
+ expectedRevision: params.expectedRevision
1796
+ }
1797
+ );
1798
+ } else {
1799
+ res = await bridge.request("vault_documents_upload", {
1800
+ saveToDocumentId: params.documentId,
1801
+ bytes: bytesToBase64(routed.bytes),
1802
+ mimeType: params.contentType,
1803
+ contentKind: routed.contentKind,
1804
+ expectedRevision: params.expectedRevision
1805
+ });
1806
+ }
1780
1807
  downloadUrlCache.delete(params.documentId);
1781
1808
  return res;
1782
1809
  },
@@ -1784,6 +1811,12 @@ function useVaultDocuments() {
1784
1811
  );
1785
1812
  const write = useCallback(
1786
1813
  async (params) => {
1814
+ if (params.content.length > SAVE_CONTENT_BODY_LANE_MAX_BYTES || !fitsBridgeBodyAsJsonText(params.content)) {
1815
+ throw new FiasBridgeError(
1816
+ "write() takes small text only: at most 200,000 characters, and small enough to travel in one bridge request once JSON-escaped (quotes, backslashes and newlines double). Use upload() for larger documents.",
1817
+ "INVALID_PARAMS"
1818
+ );
1819
+ }
1787
1820
  return bridge.request("vault_documents_write", {
1788
1821
  name: params.name,
1789
1822
  content: params.content,
@@ -1877,15 +1910,25 @@ function useVaultDocuments() {
1877
1910
  sensitivity: params.sensitivity,
1878
1911
  folderPath: params.folderPath,
1879
1912
  replacesDocumentId: params.replacesDocumentId,
1880
- // Opt-in user-visible save. The host turns this into a confirmation
1881
- // the user sees and the iframe cannot script; the server independently
1882
- // requires `vault:user-documents:write` plus a registered folder for
1883
- // this arche. Omitted (the common case) keeps the arche-private default.
1913
+ // A save into the user's Documents. The host turns this into its Save
1914
+ // dialog, which the iframe cannot see or script, and performs the
1915
+ // write itself from the user's session. Omitted (the common case)
1916
+ // keeps the arche-private default.
1917
+ destination: params.destination,
1884
1918
  userVisible: params.userVisible
1885
1919
  });
1886
1920
  },
1887
1921
  [bridge]
1888
1922
  );
1923
+ const saveCopy = useCallback(
1924
+ async (documentId, options) => {
1925
+ return bridge.request("vault_documents_update", {
1926
+ documentId,
1927
+ saveCopy: { ...options?.suggestedName ? { suggestedName: options.suggestedName } : {} }
1928
+ });
1929
+ },
1930
+ [bridge]
1931
+ );
1889
1932
  return useMemo2(
1890
1933
  () => ({
1891
1934
  list,
@@ -1900,6 +1943,7 @@ function useVaultDocuments() {
1900
1943
  attach,
1901
1944
  detach,
1902
1945
  upload,
1946
+ saveCopy,
1903
1947
  uploadInit,
1904
1948
  uploadFinalize
1905
1949
  }),
@@ -1916,11 +1960,45 @@ function useVaultDocuments() {
1916
1960
  attach,
1917
1961
  detach,
1918
1962
  upload,
1963
+ saveCopy,
1919
1964
  uploadInit,
1920
1965
  uploadFinalize
1921
1966
  ]
1922
1967
  );
1923
1968
  }
1969
+ var SAVE_CONTENT_BODY_LANE_MAX_BYTES = 2e5;
1970
+ var BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES = 256 * 1024 - 16 * 1024;
1971
+ function fitsBridgeBodyAsJsonText(text, utf8Bytes) {
1972
+ if (text.length * 6 <= BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES) return true;
1973
+ const escapeOverhead = JSON.stringify(text).length - text.length;
1974
+ const bytes = utf8Bytes ?? new TextEncoder().encode(text).length;
1975
+ return bytes + escapeOverhead <= BRIDGE_BODY_TEXT_MAX_SERIALIZED_BYTES;
1976
+ }
1977
+ var SAVE_CONTENT_MAX_BYTES = 15 * 1024 * 1024;
1978
+ async function routeSaveContent(content) {
1979
+ let routed;
1980
+ if (typeof content === "string") {
1981
+ if (content.length * 6 <= SAVE_CONTENT_BODY_LANE_MAX_BYTES) {
1982
+ return { lane: "body", text: content };
1983
+ }
1984
+ const bytes = new TextEncoder().encode(content);
1985
+ routed = bytes.length <= SAVE_CONTENT_BODY_LANE_MAX_BYTES && fitsBridgeBodyAsJsonText(content, bytes.length) ? { lane: "body", text: content } : { lane: "host", bytes, contentKind: "text" };
1986
+ } else {
1987
+ routed = { lane: "host", bytes: await toUint8Array(content), contentKind: "binary" };
1988
+ }
1989
+ if (routed.lane === "host" && routed.bytes.length > SAVE_CONTENT_MAX_BYTES) {
1990
+ throw new FiasBridgeError(
1991
+ `Content is ${routed.bytes.length} bytes; saveContent accepts at most ${SAVE_CONTENT_MAX_BYTES} bytes (15 MB).`,
1992
+ "OWN_DOCUMENT_SAVE_TOO_LARGE"
1993
+ );
1994
+ }
1995
+ return routed;
1996
+ }
1997
+ async function toUint8Array(content) {
1998
+ if (content instanceof Uint8Array) return content;
1999
+ if (content instanceof ArrayBuffer) return new Uint8Array(content);
2000
+ return new Uint8Array(await content.arrayBuffer());
2001
+ }
1924
2002
  function base64ToBytes(base64) {
1925
2003
  const binary = atob(base64);
1926
2004
  const bytes = new Uint8Array(binary.length);
@@ -1964,6 +2042,123 @@ function useArcheAssets() {
1964
2042
  );
1965
2043
  return useMemo2(() => ({ list, index, getUrl }), [list, index, getUrl]);
1966
2044
  }
2045
+ var PLUGIN_CONTENT_ENTITY_ID = "ent_intg_plugin_content_00000000";
2046
+ var FiasContentError = class extends Error {
2047
+ constructor(message, code, path) {
2048
+ super(message);
2049
+ this.code = code;
2050
+ this.path = path;
2051
+ this.name = "FiasContentError";
2052
+ }
2053
+ };
2054
+ function toContentError(err) {
2055
+ if (err instanceof FiasContentError) return err;
2056
+ const message = err instanceof Error ? err.message : String(err);
2057
+ const code = err?.code;
2058
+ if (/permission denied/i.test(message)) return new FiasContentError(message, "PERMISSION_DENIED");
2059
+ if (code === "RATE_LIMITED" || /rate limit/i.test(message)) {
2060
+ return new FiasContentError(message, "RATE_LIMIT");
2061
+ }
2062
+ return new FiasContentError(message, "CONTENT_UNAVAILABLE");
2063
+ }
2064
+ function fileResult(results, path) {
2065
+ const result = results?.[path];
2066
+ if (!result) {
2067
+ throw new FiasContentError(`No result for content/${path}`, "CONTENT_UNAVAILABLE", path);
2068
+ }
2069
+ if ("error" in result) throw new FiasContentError(result.error.message, result.error.code, path);
2070
+ return result;
2071
+ }
2072
+ var contentObjectUrls = /* @__PURE__ */ new Map();
2073
+ function useFiasContent() {
2074
+ const bridge = useBridge();
2075
+ const call = useCallback(
2076
+ async (input) => {
2077
+ let response;
2078
+ try {
2079
+ response = await bridge.request(
2080
+ "client_entity_invoke",
2081
+ { entityId: PLUGIN_CONTENT_ENTITY_ID, input },
2082
+ CLIENT_ENTITY_INVOKE_TIMEOUT_MS
2083
+ );
2084
+ } catch (err) {
2085
+ throw toContentError(err);
2086
+ }
2087
+ if (response?.error) {
2088
+ throw new FiasContentError(response.error.message, response.error.code);
2089
+ }
2090
+ return response ?? {};
2091
+ },
2092
+ [bridge]
2093
+ );
2094
+ const list = useCallback(
2095
+ async (prefix) => (await call({ verb: "list", ...prefix !== void 0 ? { prefix } : {} })).files ?? [],
2096
+ [call]
2097
+ );
2098
+ const getText = useCallback(
2099
+ async (path) => {
2100
+ const { results } = await call({ verb: "read", paths: [path], as: "text" });
2101
+ return fileResult(results, path).text;
2102
+ },
2103
+ [call]
2104
+ );
2105
+ const getJson = useCallback(
2106
+ async (path) => JSON.parse(await getText(path)),
2107
+ [getText]
2108
+ );
2109
+ const readBytes = useCallback(
2110
+ async (path) => {
2111
+ const { results } = await call({ verb: "read", paths: [path], as: "bytes" });
2112
+ return fileResult(results, path);
2113
+ },
2114
+ [call]
2115
+ );
2116
+ const getBytes = useCallback(
2117
+ async (path) => (await readBytes(path)).bytes,
2118
+ [readBytes]
2119
+ );
2120
+ const getMany = useCallback(
2121
+ async (paths, as) => {
2122
+ const { results } = await call({ verb: "read", paths, as });
2123
+ const out = {};
2124
+ for (const path of paths) {
2125
+ try {
2126
+ const result = fileResult(results, path);
2127
+ out[path] = as === "text" ? result.text : result.bytes;
2128
+ } catch (err) {
2129
+ out[path] = toContentError(err);
2130
+ }
2131
+ }
2132
+ return out;
2133
+ },
2134
+ [call]
2135
+ );
2136
+ const getObjectUrl = useCallback(
2137
+ async (path) => {
2138
+ const cached = contentObjectUrls.get(path);
2139
+ if (cached) return cached;
2140
+ const pending = readBytes(path).then(
2141
+ ({ bytes, mimeType }) => URL.createObjectURL(new Blob([bytes], { type: mimeType }))
2142
+ );
2143
+ contentObjectUrls.set(path, pending);
2144
+ pending.catch(() => contentObjectUrls.delete(path));
2145
+ return pending;
2146
+ },
2147
+ [readBytes]
2148
+ );
2149
+ const getUrl = useCallback(
2150
+ async (path) => {
2151
+ const { results } = await call({ verb: "url", paths: [path] });
2152
+ const { url, expiresAt } = fileResult(results, path);
2153
+ return { url, expiresAt };
2154
+ },
2155
+ [call]
2156
+ );
2157
+ return useMemo2(
2158
+ () => ({ list, getText, getJson, getBytes, getMany, getObjectUrl, getUrl }),
2159
+ [list, getText, getJson, getBytes, getMany, getObjectUrl, getUrl]
2160
+ );
2161
+ }
1967
2162
  function useFiasPreviewState(key, getState, onRestore) {
1968
2163
  const bridge = useBridge();
1969
2164
  const getStateRef = useRef(getState);
@@ -2053,7 +2248,44 @@ function useVaultUserDocuments() {
2053
2248
  const pick = useCallback(
2054
2249
  async (options) => {
2055
2250
  return bridge.request("vault_documents_pick", {
2056
- maxDocuments: options?.maxDocuments
2251
+ maxDocuments: options?.maxDocuments,
2252
+ access: options?.access,
2253
+ ...options?.accept ? { accept: [...options.accept] } : {}
2254
+ });
2255
+ },
2256
+ [bridge]
2257
+ );
2258
+ const requestEditAccess = useCallback(
2259
+ async (documentId) => {
2260
+ return bridge.request("vault_documents_pick", {
2261
+ maxDocuments: 1,
2262
+ access: "write",
2263
+ documentId
2264
+ });
2265
+ },
2266
+ [bridge]
2267
+ );
2268
+ const saveContent = useCallback(
2269
+ async (params) => {
2270
+ const routed = await routeSaveContent(params.content);
2271
+ if (routed.lane === "body") {
2272
+ return invokeEntityOp(
2273
+ bridge,
2274
+ VAULT_USER_DOCUMENTS_ENTITY_ID,
2275
+ "save_consented_document_content",
2276
+ {
2277
+ documentId: params.documentId,
2278
+ content: routed.text,
2279
+ expectedRevision: params.expectedRevision
2280
+ }
2281
+ );
2282
+ }
2283
+ return bridge.request("vault_documents_upload", {
2284
+ saveToDocumentId: params.documentId,
2285
+ saveTarget: "user",
2286
+ bytes: bytesToBase64(routed.bytes),
2287
+ contentKind: routed.contentKind,
2288
+ expectedRevision: params.expectedRevision
2057
2289
  });
2058
2290
  },
2059
2291
  [bridge]
@@ -2104,8 +2336,8 @@ function useVaultUserDocuments() {
2104
2336
  [bridge]
2105
2337
  );
2106
2338
  return useMemo2(
2107
- () => ({ pick, list, get, getDownloadUrl, getBytes }),
2108
- [pick, list, get, getDownloadUrl, getBytes]
2339
+ () => ({ pick, requestEditAccess, saveContent, list, get, getDownloadUrl, getBytes }),
2340
+ [pick, requestEditAccess, saveContent, list, get, getDownloadUrl, getBytes]
2109
2341
  );
2110
2342
  }
2111
2343
  var COMMUNITY_ASSET_URL_REFRESH_BUFFER_MS = 3e4;
@@ -2421,8 +2653,10 @@ var VALID_PLUGIN_PERMISSIONS = [
2421
2653
  "store:purchase",
2422
2654
  "theme:read",
2423
2655
  "user:profile:read",
2656
+ "vault:arche-saves:write",
2424
2657
  "vault:documents:read",
2425
2658
  "vault:documents:write",
2659
+ "vault:user-documents:edit",
2426
2660
  "vault:user-documents:read",
2427
2661
  "vault:user-documents:write"
2428
2662
  ];
@@ -2430,12 +2664,12 @@ function isValidPluginPermission(perm) {
2430
2664
  return VALID_PLUGIN_PERMISSIONS.includes(perm);
2431
2665
  }
2432
2666
  var PLUGIN_PERMISSION_DESCRIPTIONS = {
2433
- "ai:actions": "Let Fias AI operate this app for you \u2014 only actions you could do yourself in its interface, and risky ones ask you first.",
2434
- "assets:community:publish": "Share images you made here so other people using this app can see them, and manage what you have shared.",
2435
- "assets:community:read": "Show you images that other people using this app have shared. Everything shared is checked before anyone can see it.",
2436
- "assets:read": "Show images from this app's published asset library.",
2437
- "data:search": "Search this app's own data semantically \u2014 costs credits per search.",
2438
- "data:store": "Save structured data in this app's own private database.",
2667
+ "ai:actions": "Let Fias AI operate this arche for you \u2014 only actions you could do yourself in its interface, and risky ones ask you first.",
2668
+ "assets:community:publish": "Share images you made here so other people using this arche can see them, and manage what you have shared.",
2669
+ "assets:community:read": "Show you images that other people using this arche have shared. Everything shared is checked before anyone can see it.",
2670
+ "assets:read": "Show images from this arche's published asset library.",
2671
+ "data:search": "Search this arche's own data semantically \u2014 costs credits per search.",
2672
+ "data:store": "Save structured data in this arche's own private database.",
2439
2673
  "data:workspace": "Create team workspaces and manage their members.",
2440
2674
  "entities:audio_generate": "Generate audio with platform AI \u2014 costs credits.",
2441
2675
  "entities:client_invoke": "Use free on-device tools (grammar check, translation) in your browser.",
@@ -2445,14 +2679,16 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
2445
2679
  "entities:web_search": "Search the live web on your behalf \u2014 costs credits.",
2446
2680
  "navigation:open_arche": "Open other arches for you (navigation only).",
2447
2681
  "sandbox:vendored-libraries": "Load platform-hosted libraries (like the PDF renderer) from the trusted CDN.",
2448
- "storage:sandbox": "Save files in this app's own private storage.",
2449
- "store:purchase": "Offer in-app purchases (you approve each purchase).",
2682
+ "storage:sandbox": "Save files in this arche's own private storage.",
2683
+ "store:purchase": "Offer purchases inside the arche (you approve each purchase).",
2450
2684
  "theme:read": "Match your platform theme.",
2451
2685
  "user:profile:read": "See your display name and avatar.",
2452
- "vault:documents:read": "Read documents this app itself created in your Vault.",
2453
- "vault:documents:write": "Create and manage its own documents in your Vault.",
2454
- "vault:user-documents:read": "Read documents YOU choose to share with it from your Vault \u2014 you pick each document, and you can remove access any time in Vault \u2192 App Access.",
2455
- "vault:user-documents:write": "Save its work to your Fias files, where you can find and reuse it. You confirm each save."
2686
+ "vault:arche-saves:write": "Saves files to your Arche Saves automatically.",
2687
+ "vault:documents:read": "Read files this arche itself created, in its own folders of your Vault.",
2688
+ "vault:documents:write": "Create and manage its own files, in its own folder of your Vault.",
2689
+ "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.",
2690
+ "vault:user-documents:read": "Read documents YOU choose to share with it from your Vault \u2014 you pick each document, and you can remove access any time in Vault \u2192 Arche Access.",
2691
+ "vault:user-documents:write": "Save its work to your Documents \u2014 you choose the folder and the name each time."
2456
2692
  };
2457
2693
 
2458
2694
  // src/fias.ts
@@ -4939,6 +5175,7 @@ export {
4939
5175
  FONT_PAIRINGS,
4940
5176
  FiasBridge,
4941
5177
  FiasBridgeError,
5178
+ FiasContentError,
4942
5179
  FiasProvider,
4943
5180
  GRAMMAR_CHECK_ENTITY_ID,
4944
5181
  IMAGE_SMART_CONTROL_SURFACE_KEY,
@@ -4971,6 +5208,7 @@ export {
4971
5208
  PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS,
4972
5209
  PANEL_THEME_Z_MAX,
4973
5210
  PANEL_TITLE_MAX_LENGTH,
5211
+ PLUGIN_CONTENT_ENTITY_ID,
4974
5212
  PLUGIN_PERMISSION_DESCRIPTIONS,
4975
5213
  SDK_BRIDGE_PROTOCOL_VERSION,
4976
5214
  SDK_REQUIRES_HOST_PROTOCOL_VERSION,
@@ -5001,6 +5239,7 @@ export {
5001
5239
  useElevenLabsVoiceDesign,
5002
5240
  useEntityInvocation,
5003
5241
  useFiasAIActions,
5242
+ useFiasContent,
5004
5243
  useFiasDataStore,
5005
5244
  useFiasFonts,
5006
5245
  useFiasNavigation,