@fias/arche-sdk 2.27.0 → 2.28.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
@@ -2240,7 +2240,8 @@ function useArcheAssets() {
2240
2240
  return bridge.request("arche_assets_list", {
2241
2241
  cursor: options?.cursor,
2242
2242
  limit: options?.limit,
2243
- tags: options?.tags
2243
+ tags: options?.tags,
2244
+ kinds: options?.kinds
2244
2245
  });
2245
2246
  },
2246
2247
  [bridge]
@@ -2249,7 +2250,8 @@ function useArcheAssets() {
2249
2250
  async (options) => {
2250
2251
  return bridge.request("arche_assets_index", {
2251
2252
  tags: options?.tags,
2252
- groupBy: options?.groupBy
2253
+ groupBy: options?.groupBy,
2254
+ kinds: options?.kinds
2253
2255
  });
2254
2256
  },
2255
2257
  [bridge]
@@ -2262,7 +2264,327 @@ function useArcheAssets() {
2262
2264
  );
2263
2265
  return useMemo2(() => ({ list, index, getUrl }), [list, index, getUrl]);
2264
2266
  }
2267
+ function useArcheAssetBytes() {
2268
+ const bridge = useBridge();
2269
+ const getBytes = useCallback(
2270
+ async (asset) => {
2271
+ let ref;
2272
+ if (typeof asset === "string") {
2273
+ try {
2274
+ ref = await bridge.request("arche_assets_get_url", { assetId: asset });
2275
+ } catch (err) {
2276
+ throw toArcheAssetError(err);
2277
+ }
2278
+ } else {
2279
+ ref = asset;
2280
+ }
2281
+ return enqueueAssetByteRead(bridge, ref);
2282
+ },
2283
+ [bridge]
2284
+ );
2285
+ const getMany = useCallback(
2286
+ async (assets) => {
2287
+ const read = await readAssetBytesBatch(bridge, assets);
2288
+ const out = {};
2289
+ for (const [assetId, result] of read) out[assetId] = result;
2290
+ return out;
2291
+ },
2292
+ [bridge]
2293
+ );
2294
+ return useMemo2(() => ({ getBytes, getMany }), [getBytes, getMany]);
2295
+ }
2296
+ var ARCHE_ASSET_BYTES_ENTITY_ID = "ent_intg_arche_asset_bytes_00000";
2297
+ var ArcheAssetError = class extends Error {
2298
+ constructor(message, code) {
2299
+ super(message);
2300
+ this.code = code;
2301
+ this.name = "ArcheAssetError";
2302
+ }
2303
+ };
2304
+ var ASSET_URL_MIN_REMAINING_SECONDS = 30;
2305
+ var ASSET_BYTES_MAX_URLS_PER_CALL = 100;
2306
+ var ASSET_URL_MAX_REFRESHES_PER_BATCH = 10;
2307
+ async function readSignedUrls(bridge, signedUrls) {
2308
+ let response;
2309
+ try {
2310
+ response = await bridge.request(
2311
+ "client_entity_invoke",
2312
+ { entityId: ARCHE_ASSET_BYTES_ENTITY_ID, input: { verb: "read", signedUrls } },
2313
+ CLIENT_ENTITY_INVOKE_TIMEOUT_MS
2314
+ );
2315
+ } catch (err) {
2316
+ throw toArcheAssetError(err);
2317
+ }
2318
+ if ("error" in response) {
2319
+ throw new ArcheAssetError(response.error.message, response.error.code);
2320
+ }
2321
+ return response.results;
2322
+ }
2323
+ function fileError(result) {
2324
+ return result && "error" in result ? new ArcheAssetError(result.error.message, result.error.code) : new ArcheAssetError("the asset could not be read", "ASSET_UNAVAILABLE");
2325
+ }
2326
+ async function readInBatches(bridge, items, out) {
2327
+ const lapsed = [];
2328
+ let queue = items;
2329
+ while (queue.length > 0) {
2330
+ const overBudget = [];
2331
+ let readCount = 0;
2332
+ for (let at = 0; at < queue.length; at += ASSET_BYTES_MAX_URLS_PER_CALL) {
2333
+ const chunk = queue.slice(at, at + ASSET_BYTES_MAX_URLS_PER_CALL);
2334
+ const results = await readSignedUrls(
2335
+ bridge,
2336
+ chunk.map((item) => item.signedUrl)
2337
+ );
2338
+ chunk.forEach((item, i) => {
2339
+ const result = results[i];
2340
+ if (result && !("error" in result)) {
2341
+ out.set(item.assetId, result);
2342
+ readCount += 1;
2343
+ } else if (result?.error.code === "ASSET_URL_EXPIRED" && !item.refreshed) {
2344
+ lapsed.push(item);
2345
+ } else if (result?.error.code === "ASSET_TOO_LARGE" && chunk.length > 1) {
2346
+ overBudget.push(item);
2347
+ } else {
2348
+ out.set(item.assetId, fileError(result));
2349
+ }
2350
+ });
2351
+ }
2352
+ if (readCount === 0) {
2353
+ for (const item of overBudget) {
2354
+ out.set(
2355
+ item.assetId,
2356
+ new ArcheAssetError("the asset is too large to read", "ASSET_TOO_LARGE")
2357
+ );
2358
+ }
2359
+ break;
2360
+ }
2361
+ queue = overBudget;
2362
+ }
2363
+ return lapsed;
2364
+ }
2365
+ async function readAssetBytesBatch(bridge, refs) {
2366
+ const out = /* @__PURE__ */ new Map();
2367
+ let refreshesLeft = ASSET_URL_MAX_REFRESHES_PER_BATCH;
2368
+ const refresh = async (assetIds) => {
2369
+ const fresh = [];
2370
+ for (const assetId of assetIds) {
2371
+ if (refreshesLeft <= 0) {
2372
+ out.set(
2373
+ assetId,
2374
+ new ArcheAssetError(
2375
+ "the asset URL has expired; list the assets again for fresh URLs",
2376
+ "ASSET_URL_EXPIRED"
2377
+ )
2378
+ );
2379
+ continue;
2380
+ }
2381
+ refreshesLeft -= 1;
2382
+ try {
2383
+ const asset = await bridge.request("arche_assets_get_url", { assetId });
2384
+ fresh.push({ assetId, signedUrl: asset.signedUrl, refreshed: true });
2385
+ } catch (err) {
2386
+ out.set(assetId, toArcheAssetError(err));
2387
+ }
2388
+ }
2389
+ return fresh;
2390
+ };
2391
+ const unique = /* @__PURE__ */ new Map();
2392
+ for (const ref of refs) if (!unique.has(ref.assetId)) unique.set(ref.assetId, ref);
2393
+ const nowSeconds = Date.now() / 1e3;
2394
+ const reusable = [];
2395
+ const stale = [];
2396
+ for (const ref of unique.values()) {
2397
+ if (ref.expiresAt - nowSeconds > ASSET_URL_MIN_REMAINING_SECONDS) {
2398
+ reusable.push({ assetId: ref.assetId, signedUrl: ref.signedUrl, refreshed: false });
2399
+ } else {
2400
+ stale.push(ref.assetId);
2401
+ }
2402
+ }
2403
+ const pending = [...reusable, ...await refresh(stale)];
2404
+ const lapsed = await readInBatches(bridge, pending, out);
2405
+ if (lapsed.length > 0) {
2406
+ await readInBatches(bridge, await refresh(lapsed.map((item) => item.assetId)), out);
2407
+ }
2408
+ return out;
2409
+ }
2410
+ var pendingAssetByteReads = /* @__PURE__ */ new WeakMap();
2411
+ function enqueueAssetByteRead(bridge, ref) {
2412
+ let batch = pendingAssetByteReads.get(bridge);
2413
+ if (!batch) {
2414
+ const created = { refs: [], waiters: [] };
2415
+ pendingAssetByteReads.set(bridge, created);
2416
+ batch = created;
2417
+ void Promise.resolve().then(() => flushAssetByteReads(bridge, created));
2418
+ }
2419
+ batch.refs.push(ref);
2420
+ const queued = batch;
2421
+ return new Promise((resolve, reject) => {
2422
+ queued.waiters.push({ assetId: ref.assetId, resolve, reject });
2423
+ });
2424
+ }
2425
+ async function flushAssetByteReads(bridge, batch) {
2426
+ pendingAssetByteReads.delete(bridge);
2427
+ let results;
2428
+ try {
2429
+ results = await readAssetBytesBatch(bridge, batch.refs);
2430
+ } catch (err) {
2431
+ const failure = toArcheAssetError(err);
2432
+ for (const waiter of batch.waiters) waiter.reject(failure);
2433
+ return;
2434
+ }
2435
+ const handedOut = /* @__PURE__ */ new Set();
2436
+ for (const waiter of batch.waiters) {
2437
+ const result = results.get(waiter.assetId) ?? new ArcheAssetError("the asset could not be read", "ASSET_UNAVAILABLE");
2438
+ if (result instanceof ArcheAssetError) {
2439
+ waiter.reject(result);
2440
+ } else if (handedOut.has(waiter.assetId)) {
2441
+ waiter.resolve({ ...result, bytes: result.bytes.slice(0) });
2442
+ } else {
2443
+ handedOut.add(waiter.assetId);
2444
+ waiter.resolve(result);
2445
+ }
2446
+ }
2447
+ }
2448
+ function toArcheAssetError(err) {
2449
+ if (err instanceof ArcheAssetError) return err;
2450
+ const message = err instanceof Error ? err.message : String(err);
2451
+ const code = err?.code;
2452
+ if (/permission denied/i.test(message)) {
2453
+ return new ArcheAssetError(
2454
+ `${message} \u2014 useArcheAssetBytes needs both "assets:read" and "entities:client_invoke" in fias-plugin.json`,
2455
+ "PERMISSION_DENIED"
2456
+ );
2457
+ }
2458
+ if (code === "RATE_LIMITED" || code === "RATE_LIMIT" || /rate limit/i.test(message)) {
2459
+ return new ArcheAssetError(message, "RATE_LIMIT");
2460
+ }
2461
+ if (/rapid-fire loop|runaway loop/i.test(message)) {
2462
+ return new ArcheAssetError(
2463
+ `${message} Load many assets with useArcheAssetBytes().getMany(entries) \u2014 one call per 100.`,
2464
+ "RATE_LIMIT"
2465
+ );
2466
+ }
2467
+ return new ArcheAssetError(message, "ASSET_UNAVAILABLE");
2468
+ }
2265
2469
  var PLUGIN_CONTENT_ENTITY_ID = "ent_intg_plugin_content_00000000";
2470
+ var PLUGIN_LOCAL_STORE_ENTITY_ID = "ent_intg_plugin_local_store_0000";
2471
+ var FiasLocalStoreError = class extends Error {
2472
+ constructor(message, code) {
2473
+ super(message);
2474
+ this.code = code;
2475
+ this.name = "FiasLocalStoreError";
2476
+ }
2477
+ };
2478
+ function toLocalStoreError(err) {
2479
+ if (err instanceof FiasLocalStoreError) return err;
2480
+ const message = err instanceof Error ? err.message : String(err);
2481
+ const raw = err?.code;
2482
+ const code = raw === "PERMISSION_DENIED" || /permission denied/i.test(message) ? "PERMISSION_DENIED" : raw === "RATE_LIMIT" || raw === "RATE_LIMITED" || /rate limit/i.test(message) ? "RATE_LIMIT" : "LOCAL_STORE_UNAVAILABLE";
2483
+ return new FiasLocalStoreError(message, code);
2484
+ }
2485
+ function useLocalStore() {
2486
+ const bridge = useBridge();
2487
+ const call = useCallback(
2488
+ async (input) => {
2489
+ let response;
2490
+ try {
2491
+ response = await bridge.request(
2492
+ "client_entity_invoke",
2493
+ { entityId: PLUGIN_LOCAL_STORE_ENTITY_ID, input },
2494
+ CLIENT_ENTITY_INVOKE_TIMEOUT_MS
2495
+ );
2496
+ } catch (err) {
2497
+ throw toLocalStoreError(err);
2498
+ }
2499
+ if (response?.error) {
2500
+ throw new FiasLocalStoreError(response.error.message, response.error.code);
2501
+ }
2502
+ return response ?? {};
2503
+ },
2504
+ [bridge]
2505
+ );
2506
+ const get = useCallback(
2507
+ async (key) => (await call({ verb: "get", key })).entry ?? null,
2508
+ [call]
2509
+ );
2510
+ const getText = useCallback(
2511
+ async (key) => {
2512
+ const entry = await get(key);
2513
+ if (!entry) return null;
2514
+ if (typeof entry.value !== "string") {
2515
+ throw new FiasLocalStoreError(`"${key}" does not hold text`, "LOCAL_STORE_WRONG_KIND");
2516
+ }
2517
+ return entry.value;
2518
+ },
2519
+ [get]
2520
+ );
2521
+ const getBytes = useCallback(
2522
+ async (key) => {
2523
+ const entry = await get(key);
2524
+ if (!entry) return null;
2525
+ if (!(entry.value instanceof ArrayBuffer)) {
2526
+ throw new FiasLocalStoreError(`"${key}" does not hold bytes`, "LOCAL_STORE_WRONG_KIND");
2527
+ }
2528
+ return entry.value;
2529
+ },
2530
+ [get]
2531
+ );
2532
+ const getJson = useCallback(
2533
+ async (key) => {
2534
+ const entry = await get(key);
2535
+ if (!entry) return null;
2536
+ if (entry.value instanceof ArrayBuffer) {
2537
+ throw new FiasLocalStoreError(`"${key}" holds bytes, not JSON`, "LOCAL_STORE_WRONG_KIND");
2538
+ }
2539
+ if (typeof entry.value === "string") {
2540
+ try {
2541
+ return JSON.parse(entry.value);
2542
+ } catch {
2543
+ throw new FiasLocalStoreError(
2544
+ `"${key}" holds text that is not JSON`,
2545
+ "LOCAL_STORE_WRONG_KIND"
2546
+ );
2547
+ }
2548
+ }
2549
+ return entry.value;
2550
+ },
2551
+ [get]
2552
+ );
2553
+ const getMany = useCallback(
2554
+ async (keys) => (await call({ verb: "get_many", keys })).entries ?? [],
2555
+ [call]
2556
+ );
2557
+ const put = useCallback(
2558
+ async (key, value) => ({
2559
+ sizeBytes: (await call({ verb: "put", key, value })).sizeBytes ?? 0
2560
+ }),
2561
+ [call]
2562
+ );
2563
+ const del = useCallback(
2564
+ async (key) => {
2565
+ await call({ verb: "delete", key });
2566
+ },
2567
+ [call]
2568
+ );
2569
+ const list = useCallback(
2570
+ async (prefix) => (await call({ verb: "list", ...prefix !== void 0 ? { prefix } : {} })).entries ?? [],
2571
+ [call]
2572
+ );
2573
+ const clear = useCallback(
2574
+ async () => ({
2575
+ deleted: (await call({ verb: "clear" })).deleted ?? 0
2576
+ }),
2577
+ [call]
2578
+ );
2579
+ const estimate = useCallback(async () => {
2580
+ const res = await call({ verb: "estimate" });
2581
+ return { usedBytes: res.usedBytes ?? 0, budgetBytes: res.budgetBytes ?? 0 };
2582
+ }, [call]);
2583
+ return useMemo2(
2584
+ () => ({ get, getMany, getText, getBytes, getJson, put, delete: del, list, clear, estimate }),
2585
+ [get, getMany, getText, getBytes, getJson, put, del, list, clear, estimate]
2586
+ );
2587
+ }
2266
2588
  var FiasContentError = class extends Error {
2267
2589
  constructor(message, code, path) {
2268
2590
  super(message);
@@ -2962,6 +3284,7 @@ var VALID_PLUGIN_PERMISSIONS = [
2962
3284
  "entities:web_search",
2963
3285
  "navigation:open_arche",
2964
3286
  "sandbox:vendored-libraries",
3287
+ "storage:local",
2965
3288
  "storage:sandbox",
2966
3289
  "store:purchase",
2967
3290
  "theme:read",
@@ -2981,7 +3304,7 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
2981
3304
  "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.",
2982
3305
  "assets:community:publish": "Share images you made here so other people using this arche can see them, and manage what you have shared.",
2983
3306
  "assets:community:read": "Show you images that other people using this arche have shared. Everything shared is checked before anyone can see it.",
2984
- "assets:read": "Show images from this arche's published asset library.",
3307
+ "assets:read": "Show images and play sounds from this arche's published asset library.",
2985
3308
  "data:agents": "Let Fias AI, and assistants you connect with an API key and allow here, read this arche's data \u2014 and change it where it says so \u2014 even while it is closed.",
2986
3309
  "data:search": "Search this arche's own data semantically \u2014 costs credits per search.",
2987
3310
  "data:store": "Save structured data in this arche's own private database.",
@@ -2996,6 +3319,7 @@ var PLUGIN_PERMISSION_DESCRIPTIONS = {
2996
3319
  "entities:web_search": "Search the live web on your behalf \u2014 costs credits.",
2997
3320
  "navigation:open_arche": "Open other arches for you (navigation only).",
2998
3321
  "sandbox:vendored-libraries": "Load platform-hosted libraries (like the PDF renderer) from the trusted CDN.",
3322
+ "storage:local": "Keep its working files on this device, so a refresh or crash does not lose your work. Nothing leaves your device; cleared when you sign out or when someone else signs in here.",
2999
3323
  "storage:sandbox": "Save files in this arche's own private storage.",
3000
3324
  "store:purchase": "Offer purchases inside the arche (you approve each purchase).",
3001
3325
  "theme:read": "Match your platform theme.",
@@ -5485,6 +5809,7 @@ function validatePanelCatalog(value) {
5485
5809
  return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
5486
5810
  }
5487
5811
  export {
5812
+ ARCHE_ASSET_BYTES_ENTITY_ID,
5488
5813
  AUDIO_AUDIO_ISOLATION_SURFACE_KEY,
5489
5814
  AUDIO_FORCED_ALIGNMENT_SURFACE_KEY,
5490
5815
  AUDIO_GEMINI_TRANSCRIBE_SURFACE_KEY,
@@ -5496,6 +5821,7 @@ export {
5496
5821
  AUDIO_TTS_SURFACE_KEY,
5497
5822
  AUDIO_VOICE_CHANGER_SURFACE_KEY,
5498
5823
  AUDIO_VOICE_DESIGN_SURFACE_KEY,
5824
+ ArcheAssetError,
5499
5825
  DARK_THEME,
5500
5826
  ENTITY_INVOCATION_HISTORY_LIMITS,
5501
5827
  ENTITY_INVOCATION_REQUEST_LIMITS,
@@ -5504,6 +5830,7 @@ export {
5504
5830
  FiasBridge,
5505
5831
  FiasBridgeError,
5506
5832
  FiasContentError,
5833
+ FiasLocalStoreError,
5507
5834
  FiasProvider,
5508
5835
  GRAMMAR_CHECK_ENTITY_ID,
5509
5836
  IMAGE_SMART_CONTROL_SURFACE_KEY,
@@ -5537,6 +5864,7 @@ export {
5537
5864
  PANEL_THEME_Z_MAX,
5538
5865
  PANEL_TITLE_MAX_LENGTH,
5539
5866
  PLUGIN_CONTENT_ENTITY_ID,
5867
+ PLUGIN_LOCAL_STORE_ENTITY_ID,
5540
5868
  PLUGIN_PERMISSION_DESCRIPTIONS,
5541
5869
  REASONING_EFFORTS,
5542
5870
  SDK_BRIDGE_PROTOCOL_VERSION,
@@ -5557,6 +5885,7 @@ export {
5557
5885
  invocationPayloadByteLimit,
5558
5886
  isValidPluginPermission,
5559
5887
  resetBridge,
5888
+ useArcheAssetBytes,
5560
5889
  useArcheAssets,
5561
5890
  useArcheHandoff,
5562
5891
  useAudioGeneration,
@@ -5591,6 +5920,7 @@ export {
5591
5920
  useImageEntities,
5592
5921
  useImageGeneration,
5593
5922
  useIntegrationWebSearch,
5923
+ useLocalStore,
5594
5924
  useLyriaMusic,
5595
5925
  usePersistentState,
5596
5926
  useSmartControl,