@fias/arche-sdk 2.11.0 → 2.13.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.
Files changed (42) hide show
  1. package/dist/bridge.d.ts +27 -1
  2. package/dist/bridge.d.ts.map +1 -1
  3. package/dist/bridge.js +58 -0
  4. package/dist/bridge.js.map +1 -1
  5. package/dist/bridge.test.js +97 -4
  6. package/dist/bridge.test.js.map +1 -1
  7. package/dist/entity-ops.d.ts +24 -2
  8. package/dist/entity-ops.d.ts.map +1 -1
  9. package/dist/entity-ops.js +21 -1
  10. package/dist/entity-ops.js.map +1 -1
  11. package/dist/fias.d.ts +9 -4
  12. package/dist/fias.d.ts.map +1 -1
  13. package/dist/fias.js +15 -8
  14. package/dist/fias.js.map +1 -1
  15. package/dist/generated/permissions.d.ts +1 -1
  16. package/dist/generated/permissions.d.ts.map +1 -1
  17. package/dist/generated/permissions.js +6 -0
  18. package/dist/generated/permissions.js.map +1 -1
  19. package/dist/hooks.d.ts +58 -1
  20. package/dist/hooks.d.ts.map +1 -1
  21. package/dist/hooks.js +263 -3
  22. package/dist/hooks.js.map +1 -1
  23. package/dist/hooks.test.js +67 -0
  24. package/dist/hooks.test.js.map +1 -1
  25. package/dist/index.d.ts +4 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +29 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +719 -19
  30. package/dist/panels.d.ts +213 -0
  31. package/dist/panels.d.ts.map +1 -0
  32. package/dist/panels.js +422 -0
  33. package/dist/panels.js.map +1 -0
  34. package/dist/panels.test.d.ts +2 -0
  35. package/dist/panels.test.d.ts.map +1 -0
  36. package/dist/panels.test.js +127 -0
  37. package/dist/panels.test.js.map +1 -0
  38. package/dist/types.d.ts +311 -4
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/templates/default/AGENTS.md +186 -1
  42. package/templates/default/CLAUDE.md +186 -1
package/dist/index.mjs CHANGED
@@ -124,6 +124,12 @@ var FiasBridge = class {
124
124
  // useFiasPreviewState() callers don't collide. Providers produce a snapshot
125
125
  // on `preview_state_capture`; restore listeners receive it after the reload.
126
126
  this.previewStateProviders = /* @__PURE__ */ new Map();
127
+ /**
128
+ * The plugin's handler for host-dispatched Fias AI actions (one per plugin
129
+ * — `useFiasAIActions` owns registration). Throw/reject to report failure;
130
+ * the assistant awaits the result before telling the model it applied.
131
+ */
132
+ this.aiActionHandler = null;
127
133
  this.previewStateRestoreListeners = /* @__PURE__ */ new Map();
128
134
  // A restore payload that arrived before its hook registered (the host
129
135
  // restores on `ready`, but the React effect that registers runs later).
@@ -211,6 +217,30 @@ var FiasBridge = class {
211
217
  }
212
218
  return;
213
219
  }
220
+ if (data.type === "ai_action_dispatch") {
221
+ const { messageId } = data;
222
+ const payload = data.payload;
223
+ const handler = this.aiActionHandler;
224
+ void (async () => {
225
+ try {
226
+ if (typeof payload?.actionId !== "string") {
227
+ throw new Error("malformed dispatch: missing actionId");
228
+ }
229
+ if (!handler) {
230
+ throw new Error("the plugin has no Fias AI action handler registered");
231
+ }
232
+ await handler(payload.actionId, payload.params ?? {});
233
+ this.send("ai_action_result", { ok: true }, messageId);
234
+ } catch (err) {
235
+ this.send(
236
+ "ai_action_result",
237
+ { ok: false, error: err instanceof Error ? err.message : String(err) },
238
+ messageId
239
+ );
240
+ }
241
+ })();
242
+ return;
243
+ }
214
244
  if (data.type === "subscription_event") {
215
245
  const payload = data.payload;
216
246
  if (typeof payload?.subscriptionId === "string" && payload.event) {
@@ -491,6 +521,32 @@ var FiasBridge = class {
491
521
  this.subscriptionEndedListeners.delete(subscriptionId);
492
522
  this.send("subscription_unsubscribe", { subscriptionId });
493
523
  }
524
+ /**
525
+ * Register (or clear, with null) THE handler for host-dispatched Fias AI
526
+ * actions. One per plugin — `useFiasAIActions` owns this; last write wins.
527
+ */
528
+ setAiActionHandler(handler) {
529
+ this.aiActionHandler = handler;
530
+ }
531
+ /**
532
+ * Declare (or clear, with null) the plugin's runtime Fias AI actions.
533
+ * Fire-and-forget; the host validates, bounds, and throttles (~1/s), and
534
+ * honours the runtime lane only for TRUSTED arches — non-trusted plugins
535
+ * declare statically via the manifest `fiasAI` block. Requires the
536
+ * `ai:actions` permission.
537
+ */
538
+ declareAiActions(declaration) {
539
+ this.send("ai_actions_declare", declaration);
540
+ }
541
+ /**
542
+ * Push the state snapshot the assistant sees (and `availableWhen` filters
543
+ * against). Keep it SMALL and flag-shaped ({documentOpen: true, …}): the
544
+ * host drops snapshots over its byte cap, and accepted ones are embedded
545
+ * in the assistant's system prompt every turn.
546
+ */
547
+ sendAiState(state) {
548
+ this.send("ai_state", state);
549
+ }
494
550
  send(type, payload, messageId) {
495
551
  if (typeof window === "undefined") return;
496
552
  const message = {
@@ -714,6 +770,9 @@ var PLUGIN_DATASTORE_ENTITY_ID = "ent_intg_plugin_datastore_000000";
714
770
  var PLUGIN_WORKSPACES_ENTITY_ID = "ent_intg_plugin_workspaces_00000";
715
771
  var PLUGIN_STORE_ENTITY_ID = "ent_intg_plugin_store_0000000000";
716
772
  var VAULT_USER_DOCUMENTS_ENTITY_ID = "ent_intg_vault_documents_0000000";
773
+ var COMMUNITY_ASSETS_ENTITY_ID = "ent_intg_community_assets_000000";
774
+ var COMMUNITY_ASSET_MAX_URL_BATCH = 50;
775
+ var COMMUNITY_ASSET_MAX_PUBLISH_BATCH = 20;
717
776
  async function invokeEntityOp(bridge, entityId, op, args = {}) {
718
777
  const res = await bridge.request("entity_invoke", {
719
778
  entityId,
@@ -1188,7 +1247,8 @@ function useFiasDataStore() {
1188
1247
  searchable: options?.searchable,
1189
1248
  writeMinRole: options?.writeMinRole,
1190
1249
  readMinRole: options?.readMinRole,
1191
- writePolicy: options?.writePolicy
1250
+ writePolicy: options?.writePolicy,
1251
+ constraints: options?.constraints
1192
1252
  }
1193
1253
  );
1194
1254
  return res.collection;
@@ -1211,12 +1271,18 @@ function useFiasDataStore() {
1211
1271
  );
1212
1272
  const put = useCallback(
1213
1273
  async (collection, key, data, options) => {
1214
- await invokeEntityOp(bridge, PLUGIN_DATASTORE_ENTITY_ID, "put", {
1215
- collection,
1216
- key,
1217
- data,
1218
- workspaceId: options?.workspaceId
1219
- });
1274
+ const res = await invokeEntityOp(
1275
+ bridge,
1276
+ PLUGIN_DATASTORE_ENTITY_ID,
1277
+ "put",
1278
+ {
1279
+ collection,
1280
+ key,
1281
+ data,
1282
+ workspaceId: options?.workspaceId
1283
+ }
1284
+ );
1285
+ return { version: res?.version };
1220
1286
  },
1221
1287
  [bridge]
1222
1288
  );
@@ -1248,6 +1314,7 @@ function useFiasDataStore() {
1248
1314
  key: res.key ?? key,
1249
1315
  data: res.data,
1250
1316
  updatedAt: res.updatedAt ?? "",
1317
+ ...res.version !== void 0 ? { version: res.version } : {},
1251
1318
  ...res.authoredByCollaborator !== void 0 ? { authoredByCollaborator: res.authoredByCollaborator } : {}
1252
1319
  };
1253
1320
  },
@@ -1291,8 +1358,17 @@ function useFiasDataStore() {
1291
1358
  [bridge]
1292
1359
  );
1293
1360
  const batch = useCallback(
1294
- async (operations) => {
1295
- await invokeEntityOp(bridge, PLUGIN_DATASTORE_ENTITY_ID, "batch", { operations });
1361
+ async (operations, options) => {
1362
+ const res = await invokeEntityOp(
1363
+ bridge,
1364
+ PLUGIN_DATASTORE_ENTITY_ID,
1365
+ "batch",
1366
+ {
1367
+ operations,
1368
+ ...options?.preconditions?.length ? { preconditions: options.preconditions } : {}
1369
+ }
1370
+ );
1371
+ return { written: res?.written };
1296
1372
  },
1297
1373
  [bridge]
1298
1374
  );
@@ -1858,6 +1934,221 @@ function useVaultUserDocuments() {
1858
1934
  );
1859
1935
  return useMemo2(() => ({ pick, list, get, getDownloadUrl }), [pick, list, get, getDownloadUrl]);
1860
1936
  }
1937
+ var COMMUNITY_ASSET_URL_REFRESH_BUFFER_MS = 3e4;
1938
+ var communityAssetUrlCache = /* @__PURE__ */ new Map();
1939
+ var communityAssetUrlInflight = /* @__PURE__ */ new Map();
1940
+ async function fetchCommunityAssetUrls(bridge, assetIds) {
1941
+ const now = Date.now();
1942
+ const hits = [];
1943
+ const misses = [];
1944
+ for (const assetId of assetIds) {
1945
+ const cached = communityAssetUrlCache.get(assetId);
1946
+ if (cached && now < cached.expiresAt - COMMUNITY_ASSET_URL_REFRESH_BUFFER_MS) {
1947
+ hits.push(cached);
1948
+ } else {
1949
+ misses.push(assetId);
1950
+ }
1951
+ }
1952
+ if (misses.length === 0) return hits;
1953
+ const pending = [];
1954
+ const toRequest = [];
1955
+ for (const assetId of misses) {
1956
+ const inflight = communityAssetUrlInflight.get(assetId);
1957
+ if (inflight) pending.push(inflight);
1958
+ else toRequest.push(assetId);
1959
+ }
1960
+ for (let i = 0; i < toRequest.length; i += COMMUNITY_ASSET_MAX_URL_BATCH) {
1961
+ const chunk = toRequest.slice(i, i + COMMUNITY_ASSET_MAX_URL_BATCH);
1962
+ const promise = invokeEntityOp(
1963
+ bridge,
1964
+ COMMUNITY_ASSETS_ENTITY_ID,
1965
+ "get_urls",
1966
+ { assetIds: chunk }
1967
+ ).then((res) => {
1968
+ for (const url of res.urls) {
1969
+ communityAssetUrlCache.set(url.assetId, url);
1970
+ }
1971
+ return res.urls;
1972
+ }).finally(() => {
1973
+ for (const assetId of chunk) {
1974
+ communityAssetUrlInflight.delete(assetId);
1975
+ }
1976
+ });
1977
+ for (const assetId of chunk) {
1978
+ communityAssetUrlInflight.set(assetId, promise);
1979
+ }
1980
+ pending.push(promise);
1981
+ }
1982
+ const settled = await Promise.all(pending);
1983
+ const requested = new Set(assetIds);
1984
+ const seen = new Set(hits.map((h) => h.assetId));
1985
+ for (const batch of settled) {
1986
+ for (const url of batch) {
1987
+ if (!requested.has(url.assetId) || seen.has(url.assetId)) continue;
1988
+ seen.add(url.assetId);
1989
+ hits.push(url);
1990
+ }
1991
+ }
1992
+ return hits;
1993
+ }
1994
+ function useCommunityAssets() {
1995
+ const bridge = useBridge();
1996
+ const publish = useCallback(
1997
+ async (input) => {
1998
+ const res = await invokeEntityOp(
1999
+ bridge,
2000
+ COMMUNITY_ASSETS_ENTITY_ID,
2001
+ "publish",
2002
+ {
2003
+ fileId: input.fileId,
2004
+ title: input.title,
2005
+ caption: input.caption,
2006
+ tags: input.tags,
2007
+ clientRef: input.clientRef,
2008
+ attributionName: input.attributionName
2009
+ }
2010
+ );
2011
+ return res.asset;
2012
+ },
2013
+ [bridge]
2014
+ );
2015
+ const publishMany = useCallback(
2016
+ async (items) => {
2017
+ const results = [];
2018
+ for (let i = 0; i < items.length; i += COMMUNITY_ASSET_MAX_PUBLISH_BATCH) {
2019
+ const res = await invokeEntityOp(
2020
+ bridge,
2021
+ COMMUNITY_ASSETS_ENTITY_ID,
2022
+ "publish_many",
2023
+ { items: items.slice(i, i + COMMUNITY_ASSET_MAX_PUBLISH_BATCH) }
2024
+ );
2025
+ results.push(...res.results);
2026
+ }
2027
+ return results;
2028
+ },
2029
+ [bridge]
2030
+ );
2031
+ const unpublish = useCallback(
2032
+ async (assetId) => {
2033
+ const res = await invokeEntityOp(
2034
+ bridge,
2035
+ COMMUNITY_ASSETS_ENTITY_ID,
2036
+ "unpublish",
2037
+ { assetId }
2038
+ );
2039
+ communityAssetUrlCache.delete(assetId);
2040
+ return res.asset;
2041
+ },
2042
+ [bridge]
2043
+ );
2044
+ const listMine = useCallback(
2045
+ async (options) => invokeEntityOp(bridge, COMMUNITY_ASSETS_ENTITY_ID, "list_mine", {
2046
+ limit: options?.limit,
2047
+ cursor: options?.cursor
2048
+ }),
2049
+ [bridge]
2050
+ );
2051
+ const listCommunity = useCallback(
2052
+ async (options) => invokeEntityOp(
2053
+ bridge,
2054
+ COMMUNITY_ASSETS_ENTITY_ID,
2055
+ "list_community",
2056
+ { limit: options?.limit, cursor: options?.cursor, tag: options?.tag }
2057
+ ),
2058
+ [bridge]
2059
+ );
2060
+ const get = useCallback(
2061
+ async (assetId) => {
2062
+ const res = await invokeEntityOp(
2063
+ bridge,
2064
+ COMMUNITY_ASSETS_ENTITY_ID,
2065
+ "get",
2066
+ { assetId }
2067
+ );
2068
+ return res.asset;
2069
+ },
2070
+ [bridge]
2071
+ );
2072
+ const getUrls = useCallback(
2073
+ (assetIds) => fetchCommunityAssetUrls(bridge, assetIds),
2074
+ [bridge]
2075
+ );
2076
+ const getUrl = useCallback(
2077
+ async (assetId) => {
2078
+ const [url] = await fetchCommunityAssetUrls(bridge, [assetId]);
2079
+ if (!url) {
2080
+ throw new Error(`Community asset ${assetId} is not available`);
2081
+ }
2082
+ return url;
2083
+ },
2084
+ [bridge]
2085
+ );
2086
+ const report = useCallback(
2087
+ async (assetId, reason, details) => invokeEntityOp(
2088
+ bridge,
2089
+ COMMUNITY_ASSETS_ENTITY_ID,
2090
+ "report",
2091
+ { assetId, reason, details }
2092
+ ),
2093
+ [bridge]
2094
+ );
2095
+ return useMemo2(
2096
+ () => ({
2097
+ publish,
2098
+ publishMany,
2099
+ unpublish,
2100
+ listMine,
2101
+ listCommunity,
2102
+ get,
2103
+ getUrl,
2104
+ getUrls,
2105
+ report
2106
+ }),
2107
+ [publish, publishMany, unpublish, listMine, listCommunity, get, getUrl, getUrls, report]
2108
+ );
2109
+ }
2110
+ function useFiasAIActions(config) {
2111
+ const bridge = useBridge();
2112
+ const onActionRef = useRef(config?.onAction ?? null);
2113
+ onActionRef.current = config?.onAction ?? null;
2114
+ useEffect2(() => {
2115
+ bridge.setAiActionHandler((actionId, params) => {
2116
+ const handler = onActionRef.current;
2117
+ if (!handler) {
2118
+ throw new Error("no Fias AI action handler is currently registered");
2119
+ }
2120
+ return handler(actionId, params);
2121
+ });
2122
+ return () => bridge.setAiActionHandler(null);
2123
+ }, [bridge]);
2124
+ const declarationJson = config ? JSON.stringify({ description: config.description, actions: config.actions }) : null;
2125
+ useEffect2(() => {
2126
+ let cancelled = false;
2127
+ void bridge.waitForInit().then(() => {
2128
+ if (cancelled) return;
2129
+ bridge.declareAiActions(
2130
+ declarationJson ? JSON.parse(declarationJson) : null
2131
+ );
2132
+ });
2133
+ return () => {
2134
+ cancelled = true;
2135
+ };
2136
+ }, [bridge, declarationJson]);
2137
+ useEffect2(() => {
2138
+ return () => bridge.declareAiActions(null);
2139
+ }, [bridge]);
2140
+ const stateJson = config?.state ? JSON.stringify(config.state) : null;
2141
+ useEffect2(() => {
2142
+ if (stateJson === null) return;
2143
+ let cancelled = false;
2144
+ void bridge.waitForInit().then(() => {
2145
+ if (!cancelled) bridge.sendAiState(JSON.parse(stateJson));
2146
+ });
2147
+ return () => {
2148
+ cancelled = true;
2149
+ };
2150
+ }, [bridge, stateJson]);
2151
+ }
1861
2152
 
1862
2153
  // src/generated/surfaces.ts
1863
2154
  var AUDIO_AUDIO_ISOLATION_SURFACE_KEY = "audio.audio-isolation";
@@ -1925,6 +2216,9 @@ var useIntegrationWebSearch = () => useSurface(
1925
2216
 
1926
2217
  // src/generated/permissions.ts
1927
2218
  var VALID_PLUGIN_PERMISSIONS = [
2219
+ "ai:actions",
2220
+ "assets:community:publish",
2221
+ "assets:community:read",
1928
2222
  "assets:read",
1929
2223
  "data:search",
1930
2224
  "data:store",
@@ -1949,6 +2243,9 @@ function isValidPluginPermission(perm) {
1949
2243
  return VALID_PLUGIN_PERMISSIONS.includes(perm);
1950
2244
  }
1951
2245
  var PLUGIN_PERMISSION_DESCRIPTIONS = {
2246
+ "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.",
2247
+ "assets:community:publish": "Share images you made here so other people using this app can see them, and manage what you have shared.",
2248
+ "assets:community:read": "Show you images that other people using this app have shared. Everything shared is checked before anyone can see it.",
1952
2249
  "assets:read": "Show images from this app's published asset library.",
1953
2250
  "data:search": "Search this app's own data semantically \u2014 costs credits per search.",
1954
2251
  "data:store": "Save structured data in this app's own private database.",
@@ -2006,7 +2303,8 @@ var fias = {
2006
2303
  name,
2007
2304
  userScope: options?.userScope ?? "user",
2008
2305
  searchable: options?.searchable,
2009
- writePolicy: options?.writePolicy
2306
+ writePolicy: options?.writePolicy,
2307
+ constraints: options?.constraints
2010
2308
  }
2011
2309
  );
2012
2310
  return res.collection;
@@ -2022,12 +2320,16 @@ var fias = {
2022
2320
  async deleteCollection(name) {
2023
2321
  await invokeEntityOp(getBridge(), PLUGIN_DATASTORE_ENTITY_ID, "delete_collection", { name });
2024
2322
  },
2323
+ /** Write a document. Resolves with the document's post-write `version`
2324
+ * (ADR-014) so a follow-up CAS batch needs no read-back. */
2025
2325
  async put(collection, key, data) {
2026
- await invokeEntityOp(getBridge(), PLUGIN_DATASTORE_ENTITY_ID, "put", {
2027
- collection,
2028
- key,
2029
- data
2030
- });
2326
+ const res = await invokeEntityOp(
2327
+ getBridge(),
2328
+ PLUGIN_DATASTORE_ENTITY_ID,
2329
+ "put",
2330
+ { collection, key, data }
2331
+ );
2332
+ return { version: res?.version };
2031
2333
  },
2032
2334
  async get(collection, key) {
2033
2335
  const res = await invokeEntityOp(
@@ -2047,6 +2349,7 @@ var fias = {
2047
2349
  key: res.key ?? key,
2048
2350
  data: res.data,
2049
2351
  updatedAt: res.updatedAt ?? "",
2352
+ ...res.version !== void 0 ? { version: res.version } : {},
2050
2353
  ...res.authoredByCollaborator !== void 0 ? { authoredByCollaborator: res.authoredByCollaborator } : {}
2051
2354
  };
2052
2355
  },
@@ -2070,9 +2373,20 @@ var fias = {
2070
2373
  async delete(collection, key) {
2071
2374
  await invokeEntityOp(getBridge(), PLUGIN_DATASTORE_ENTITY_ID, "delete", { collection, key });
2072
2375
  },
2073
- /** Apply up to 25 put/delete ops in one all-or-nothing transaction. */
2074
- async batch(operations) {
2075
- await invokeEntityOp(getBridge(), PLUGIN_DATASTORE_ENTITY_ID, "batch", { operations });
2376
+ /** Apply up to 25 put/delete ops in one all-or-nothing transaction.
2377
+ * Pass `options.preconditions` (ADR-014) to make the batch conditional;
2378
+ * the result carries each put's post-write version. */
2379
+ async batch(operations, options) {
2380
+ const res = await invokeEntityOp(
2381
+ getBridge(),
2382
+ PLUGIN_DATASTORE_ENTITY_ID,
2383
+ "batch",
2384
+ {
2385
+ operations,
2386
+ ...options?.preconditions?.length ? { preconditions: options.preconditions } : {}
2387
+ }
2388
+ );
2389
+ return { written: res?.written };
2076
2390
  }
2077
2391
  }
2078
2392
  };
@@ -4033,6 +4347,371 @@ function raceWithTimeout(promise, ms) {
4033
4347
  function useFiasFonts() {
4034
4348
  return useMemo3(() => ({ fonts: FONT_CATALOG, ensureFontLoaded }), []);
4035
4349
  }
4350
+
4351
+ // src/panels.ts
4352
+ var PANEL_MIN_ELEMENTS = 1;
4353
+ var PANEL_MAX_ELEMENTS = 24;
4354
+ var PANEL_TITLE_MAX_LENGTH = 80;
4355
+ var PANEL_TEXT_MAX_LENGTH = 500;
4356
+ var PANEL_IMAGE_ALT_MAX_LENGTH = 120;
4357
+ var PANEL_IMAGE_KEY_MAX_LENGTH = 500;
4358
+ var PANEL_IMAGE_MIN_HEIGHT = 24;
4359
+ var PANEL_IMAGE_MAX_HEIGHT = 512;
4360
+ var PANEL_BUTTON_LABEL_MAX_LENGTH = 40;
4361
+ var PANEL_ACTION_MAX_LENGTH = 2048;
4362
+ var PANEL_EMIT_TOKEN_MAX_LENGTH = 64;
4363
+ var PANEL_ITEMS_EMPTY_TEXT_MAX_LENGTH = 120;
4364
+ var PANEL_ITEMS_SLOT_MAX_LENGTH = 64;
4365
+ var PANEL_SHOW_IF_MAX_LENGTH = 64;
4366
+ var PANEL_PRIORITY_ABS_MAX = 1e3;
4367
+ var PANEL_THEME_FONT_MAX_LENGTH = 200;
4368
+ var PANEL_THEME_AMOUNT_FORMAT_MAX_LENGTH = 24;
4369
+ var PANEL_THEME_Z_MAX = 1e6;
4370
+ var PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS = 4;
4371
+ var PANEL_IMAGE_ROOTS = ["dfe-icons", "dfe-avatars"];
4372
+ var PANEL_ELEMENT_KINDS = [
4373
+ "text",
4374
+ "conditionalText",
4375
+ "image",
4376
+ "assetImage",
4377
+ "items",
4378
+ "slottedItems",
4379
+ "button",
4380
+ "spacer"
4381
+ ];
4382
+ var PANEL_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
4383
+ var SLOT_PATTERN = new RegExp(`^[a-z0-9][a-z0-9_-]{0,${PANEL_ITEMS_SLOT_MAX_LENGTH - 1}}$`);
4384
+ var EMIT_TOKEN_PATTERN = new RegExp(`^[a-z0-9][a-z0-9_-]{0,${PANEL_EMIT_TOKEN_MAX_LENGTH - 1}}$`);
4385
+ var SHOW_IF_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
4386
+ var ASSET_ID_PATTERN = /^as_[a-f0-9]{32}$/;
4387
+ var ARCHE_ID_PATTERN = /^arc_[a-f0-9]{32}$/;
4388
+ var IMAGE_KEY_PATTERN = new RegExp(
4389
+ `^(?:${PANEL_IMAGE_ROOTS.join("|")})/[A-Za-z0-9._/-]+\\.(?:png|jpe?g|webp|gif|svg)$`,
4390
+ "i"
4391
+ );
4392
+ var THEME_COLOR_PATTERN = /^(?:#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})|rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*(?:,\s*(?:0|1|0?\.\d{1,3})\s*)?\))$/i;
4393
+ var THEME_LENGTH_PATTERN = new RegExp(
4394
+ `^\\d{1,${PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS}}(?:\\.\\d{1,3})?(?:px|rem|em)$`
4395
+ );
4396
+ var THEME_FONT_PATTERN = /^[A-Za-z0-9 '",._-]+$/;
4397
+ var TEXT_STYLES = ["title", "body", "caption"];
4398
+ var TEXT_ALIGNS = ["left", "center", "right"];
4399
+ var SIZES = ["small", "medium", "large"];
4400
+ var BUTTON_VARIANTS = ["primary", "secondary"];
4401
+ function isRecord(value) {
4402
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4403
+ }
4404
+ function rejectUnknownKeys(value, allowed, path, issues) {
4405
+ for (const key of Object.keys(value)) {
4406
+ if (!allowed.includes(key)) issues.push(`${path}: unknown field "${key}"`);
4407
+ }
4408
+ }
4409
+ function checkString(value, path, issues, opts) {
4410
+ if (typeof value !== "string") {
4411
+ issues.push(`${path}: expected a string`);
4412
+ return;
4413
+ }
4414
+ if (value.length < (opts.min ?? 1)) issues.push(`${path}: must not be empty`);
4415
+ if (value.length > opts.max) issues.push(`${path}: longer than ${opts.max} characters`);
4416
+ if (opts.pattern && !opts.pattern.test(value)) {
4417
+ issues.push(`${path}: ${opts.hint ?? "does not match the required format"}`);
4418
+ }
4419
+ }
4420
+ function checkOptionalEnum(value, path, issues, allowed) {
4421
+ if (value === void 0) return;
4422
+ if (typeof value !== "string" || !allowed.includes(value)) {
4423
+ issues.push(`${path}: must be one of ${allowed.join(" | ")}`);
4424
+ }
4425
+ }
4426
+ function checkOptionalBoolean(value, path, issues) {
4427
+ if (value !== void 0 && typeof value !== "boolean") issues.push(`${path}: expected a boolean`);
4428
+ }
4429
+ function checkOptionalIntRange(value, path, issues, min, max) {
4430
+ if (value === void 0) return;
4431
+ if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) {
4432
+ issues.push(`${path}: must be an integer between ${min} and ${max}`);
4433
+ }
4434
+ }
4435
+ function validatePanelAction(action, path, issues) {
4436
+ if (typeof action !== "string" || action.length === 0) {
4437
+ issues.push(`${path}: expected an action string`);
4438
+ return;
4439
+ }
4440
+ if (action.length > PANEL_ACTION_MAX_LENGTH) {
4441
+ issues.push(`${path}: longer than ${PANEL_ACTION_MAX_LENGTH} characters`);
4442
+ return;
4443
+ }
4444
+ if (action === "close") return;
4445
+ if (action.startsWith("emit:")) {
4446
+ if (!EMIT_TOKEN_PATTERN.test(action.slice("emit:".length))) {
4447
+ issues.push(
4448
+ `${path}: emit token must be 1\u2013${PANEL_EMIT_TOKEN_MAX_LENGTH} chars \u2014 lowercase letters, digits, hyphens and underscores, not starting with a separator`
4449
+ );
4450
+ }
4451
+ return;
4452
+ }
4453
+ if (action.startsWith("open_arche:")) {
4454
+ if (!ARCHE_ID_PATTERN.test(action.slice("open_arche:".length))) {
4455
+ issues.push(`${path}: open_arche target must be an arche id (arc_ + 32 hex chars)`);
4456
+ }
4457
+ return;
4458
+ }
4459
+ if (action.startsWith("open_url:")) {
4460
+ if (/\s/.test(action)) {
4461
+ issues.push(`${path}: action must not contain whitespace`);
4462
+ return;
4463
+ }
4464
+ let parsed;
4465
+ try {
4466
+ parsed = new URL(action.slice("open_url:".length));
4467
+ } catch {
4468
+ issues.push(`${path}: open_url must be a valid absolute URL`);
4469
+ return;
4470
+ }
4471
+ if (parsed.protocol !== "https:") issues.push(`${path}: open_url scheme must be https`);
4472
+ return;
4473
+ }
4474
+ issues.push(
4475
+ `${path}: must be one of close | emit:<token> | open_arche:arc_<32 hex> | open_url:https://\u2026`
4476
+ );
4477
+ }
4478
+ function validateTextFields(element, path, issues) {
4479
+ checkString(element.text, `${path}.text`, issues, { max: PANEL_TEXT_MAX_LENGTH });
4480
+ checkOptionalEnum(element.style, `${path}.style`, issues, TEXT_STYLES);
4481
+ checkOptionalEnum(element.align, `${path}.align`, issues, TEXT_ALIGNS);
4482
+ checkOptionalBoolean(element.joinNext, `${path}.joinNext`, issues);
4483
+ }
4484
+ function validateItemsFields(element, path, issues) {
4485
+ checkOptionalBoolean(element.showIcons, `${path}.showIcons`, issues);
4486
+ checkOptionalEnum(element.iconSize, `${path}.iconSize`, issues, SIZES);
4487
+ if (element.emptyText !== void 0) {
4488
+ checkString(element.emptyText, `${path}.emptyText`, issues, {
4489
+ min: 0,
4490
+ max: PANEL_ITEMS_EMPTY_TEXT_MAX_LENGTH
4491
+ });
4492
+ }
4493
+ }
4494
+ function validateImageFields(element, path, issues) {
4495
+ if (element.alt !== void 0) {
4496
+ checkString(element.alt, `${path}.alt`, issues, { min: 0, max: PANEL_IMAGE_ALT_MAX_LENGTH });
4497
+ }
4498
+ checkOptionalIntRange(
4499
+ element.maxHeight,
4500
+ `${path}.maxHeight`,
4501
+ issues,
4502
+ PANEL_IMAGE_MIN_HEIGHT,
4503
+ PANEL_IMAGE_MAX_HEIGHT
4504
+ );
4505
+ }
4506
+ function validateElement(value, path, issues) {
4507
+ if (!isRecord(value)) {
4508
+ issues.push(`${path}: expected an object`);
4509
+ return;
4510
+ }
4511
+ const kind = value.kind;
4512
+ if (typeof kind !== "string" || !PANEL_ELEMENT_KINDS.includes(kind)) {
4513
+ issues.push(
4514
+ `${path}.kind: unknown element kind ${JSON.stringify(kind)} \u2014 expected one of ${PANEL_ELEMENT_KINDS.join(" | ")}`
4515
+ );
4516
+ return;
4517
+ }
4518
+ switch (kind) {
4519
+ case "text":
4520
+ rejectUnknownKeys(value, ["kind", "text", "style", "align", "joinNext"], path, issues);
4521
+ validateTextFields(value, path, issues);
4522
+ return;
4523
+ case "conditionalText":
4524
+ rejectUnknownKeys(
4525
+ value,
4526
+ ["kind", "text", "style", "align", "joinNext", "showIf"],
4527
+ path,
4528
+ issues
4529
+ );
4530
+ validateTextFields(value, path, issues);
4531
+ checkString(value.showIf, `${path}.showIf`, issues, {
4532
+ max: PANEL_SHOW_IF_MAX_LENGTH,
4533
+ pattern: SHOW_IF_PATTERN,
4534
+ hint: "must be a value NAME (letters, digits, underscore; not starting with a digit) \u2014 expressions and operators are not supported"
4535
+ });
4536
+ return;
4537
+ case "image":
4538
+ rejectUnknownKeys(value, ["kind", "imageKey", "alt", "maxHeight"], path, issues);
4539
+ checkString(value.imageKey, `${path}.imageKey`, issues, {
4540
+ max: PANEL_IMAGE_KEY_MAX_LENGTH,
4541
+ pattern: IMAGE_KEY_PATTERN,
4542
+ hint: `must be a relative key under ${PANEL_IMAGE_ROOTS.join(" or ")}/ ending in an image extension (no URL, no leading slash)`
4543
+ });
4544
+ if (typeof value.imageKey === "string" && value.imageKey.includes("..")) {
4545
+ issues.push(`${path}.imageKey: must not contain ".."`);
4546
+ }
4547
+ validateImageFields(value, path, issues);
4548
+ return;
4549
+ case "assetImage":
4550
+ rejectUnknownKeys(value, ["kind", "assetId", "alt", "maxHeight"], path, issues);
4551
+ checkString(value.assetId, `${path}.assetId`, issues, {
4552
+ max: 64,
4553
+ pattern: ASSET_ID_PATTERN,
4554
+ hint: "must be an arche asset id (as_ + 32 hex chars)"
4555
+ });
4556
+ validateImageFields(value, path, issues);
4557
+ return;
4558
+ case "items":
4559
+ rejectUnknownKeys(value, ["kind", "showIcons", "iconSize", "emptyText"], path, issues);
4560
+ validateItemsFields(value, path, issues);
4561
+ return;
4562
+ case "slottedItems":
4563
+ rejectUnknownKeys(
4564
+ value,
4565
+ ["kind", "slot", "showIcons", "iconSize", "emptyText"],
4566
+ path,
4567
+ issues
4568
+ );
4569
+ checkString(value.slot, `${path}.slot`, issues, {
4570
+ max: PANEL_ITEMS_SLOT_MAX_LENGTH,
4571
+ pattern: SLOT_PATTERN,
4572
+ hint: "must be lowercase letters, digits, hyphens and underscores, not starting with a separator"
4573
+ });
4574
+ validateItemsFields(value, path, issues);
4575
+ return;
4576
+ case "button":
4577
+ rejectUnknownKeys(value, ["kind", "label", "action", "variant"], path, issues);
4578
+ checkString(value.label, `${path}.label`, issues, { max: PANEL_BUTTON_LABEL_MAX_LENGTH });
4579
+ validatePanelAction(value.action, `${path}.action`, issues);
4580
+ checkOptionalEnum(value.variant, `${path}.variant`, issues, BUTTON_VARIANTS);
4581
+ return;
4582
+ case "spacer":
4583
+ rejectUnknownKeys(value, ["kind", "size"], path, issues);
4584
+ checkOptionalEnum(value.size, `${path}.size`, issues, SIZES);
4585
+ return;
4586
+ }
4587
+ }
4588
+ var THEME_COLOR_FIELDS = ["bg", "fg", "border", "accent", "accentFg", "scrim"];
4589
+ var THEME_LENGTH_FIELDS = [
4590
+ "radius",
4591
+ "buttonRadius",
4592
+ "gap",
4593
+ "fontSize",
4594
+ "widthSmall",
4595
+ "widthMedium",
4596
+ "widthLarge"
4597
+ ];
4598
+ function validatePanelTheme(value, path = "theme") {
4599
+ const issues = [];
4600
+ if (!isRecord(value)) return { ok: false, issues: [`${path}: expected an object`] };
4601
+ rejectUnknownKeys(
4602
+ value,
4603
+ [...THEME_COLOR_FIELDS, ...THEME_LENGTH_FIELDS, "font", "z", "amountFormat"],
4604
+ path,
4605
+ issues
4606
+ );
4607
+ for (const field of THEME_COLOR_FIELDS) {
4608
+ if (value[field] === void 0) continue;
4609
+ checkString(value[field], `${path}.${field}`, issues, {
4610
+ max: 64,
4611
+ pattern: THEME_COLOR_PATTERN,
4612
+ hint: "must be a hex colour (#rgb, #rgba, #rrggbb, #rrggbbaa) or rgb()/rgba() with numeric components"
4613
+ });
4614
+ }
4615
+ for (const field of THEME_LENGTH_FIELDS) {
4616
+ if (value[field] === void 0) continue;
4617
+ checkString(value[field], `${path}.${field}`, issues, {
4618
+ max: 16,
4619
+ pattern: THEME_LENGTH_PATTERN,
4620
+ hint: `must be a non-negative number (max ${PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS} digits before the point) followed by px, rem or em`
4621
+ });
4622
+ }
4623
+ if (value.font !== void 0) {
4624
+ checkString(value.font, `${path}.font`, issues, {
4625
+ max: PANEL_THEME_FONT_MAX_LENGTH,
4626
+ pattern: THEME_FONT_PATTERN,
4627
+ hint: "must be a font-family stack (names, commas, quotes) \u2014 no parentheses, so no url() or var()"
4628
+ });
4629
+ }
4630
+ checkOptionalIntRange(value.z, `${path}.z`, issues, 0, PANEL_THEME_Z_MAX);
4631
+ if (value.amountFormat !== void 0) {
4632
+ checkString(value.amountFormat, `${path}.amountFormat`, issues, {
4633
+ max: PANEL_THEME_AMOUNT_FORMAT_MAX_LENGTH
4634
+ });
4635
+ if (typeof value.amountFormat === "string" && !value.amountFormat.includes("{n}")) {
4636
+ issues.push(`${path}.amountFormat: must contain the placeholder {n}`);
4637
+ }
4638
+ }
4639
+ return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
4640
+ }
4641
+ function validatePanelDefinition(value, path = "panel") {
4642
+ const issues = [];
4643
+ if (!isRecord(value)) return { ok: false, issues: [`${path}: expected an object`] };
4644
+ rejectUnknownKeys(value, ["id", "title", "elements", "presentation"], path, issues);
4645
+ checkString(value.id, `${path}.id`, issues, {
4646
+ max: 64,
4647
+ pattern: PANEL_ID_PATTERN,
4648
+ hint: "must be 2\u201364 chars: lowercase letters, digits and hyphens, not starting with a hyphen"
4649
+ });
4650
+ checkString(value.title, `${path}.title`, issues, { max: PANEL_TITLE_MAX_LENGTH });
4651
+ if (!Array.isArray(value.elements)) {
4652
+ issues.push(`${path}.elements: expected an array`);
4653
+ } else {
4654
+ if (value.elements.length < PANEL_MIN_ELEMENTS) {
4655
+ issues.push(`${path}.elements: needs at least ${PANEL_MIN_ELEMENTS} element`);
4656
+ }
4657
+ if (value.elements.length > PANEL_MAX_ELEMENTS) {
4658
+ issues.push(`${path}.elements: more than ${PANEL_MAX_ELEMENTS} elements`);
4659
+ }
4660
+ value.elements.forEach(
4661
+ (element, index) => validateElement(element, `${path}.elements[${index}]`, issues)
4662
+ );
4663
+ }
4664
+ if (value.presentation !== void 0) {
4665
+ if (!isRecord(value.presentation)) {
4666
+ issues.push(`${path}.presentation: expected an object`);
4667
+ } else {
4668
+ rejectUnknownKeys(
4669
+ value.presentation,
4670
+ ["size", "dismissible", "priority"],
4671
+ `${path}.presentation`,
4672
+ issues
4673
+ );
4674
+ checkOptionalEnum(value.presentation.size, `${path}.presentation.size`, issues, SIZES);
4675
+ checkOptionalBoolean(
4676
+ value.presentation.dismissible,
4677
+ `${path}.presentation.dismissible`,
4678
+ issues
4679
+ );
4680
+ checkOptionalIntRange(
4681
+ value.presentation.priority,
4682
+ `${path}.presentation.priority`,
4683
+ issues,
4684
+ -PANEL_PRIORITY_ABS_MAX,
4685
+ PANEL_PRIORITY_ABS_MAX
4686
+ );
4687
+ }
4688
+ }
4689
+ return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
4690
+ }
4691
+ function validatePanelCatalog(value) {
4692
+ const issues = [];
4693
+ if (!isRecord(value)) return { ok: false, issues: ["catalog: expected an object"] };
4694
+ rejectUnknownKeys(value, ["panels", "theme"], "catalog", issues);
4695
+ if (value.theme !== void 0) {
4696
+ const theme = validatePanelTheme(value.theme, "catalog.theme");
4697
+ if (!theme.ok) issues.push(...theme.issues);
4698
+ }
4699
+ if (!Array.isArray(value.panels)) {
4700
+ issues.push("catalog.panels: expected an array");
4701
+ return { ok: false, issues };
4702
+ }
4703
+ const seen = /* @__PURE__ */ new Set();
4704
+ value.panels.forEach((panel, index) => {
4705
+ const path = `catalog.panels[${index}]`;
4706
+ const result = validatePanelDefinition(panel, path);
4707
+ if (!result.ok) issues.push(...result.issues);
4708
+ if (isRecord(panel) && typeof panel.id === "string") {
4709
+ if (seen.has(panel.id)) issues.push(`${path}.id: duplicate panel id "${panel.id}"`);
4710
+ seen.add(panel.id);
4711
+ }
4712
+ });
4713
+ return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
4714
+ }
4036
4715
  export {
4037
4716
  AUDIO_AUDIO_ISOLATION_SURFACE_KEY,
4038
4717
  AUDIO_FORCED_ALIGNMENT_SURFACE_KEY,
@@ -4065,6 +4744,21 @@ export {
4065
4744
  LIGHT_THEME,
4066
4745
  NLLB_LANGUAGES,
4067
4746
  NLLB_TRANSLATION_ENTITY_ID,
4747
+ PANEL_BUTTON_LABEL_MAX_LENGTH,
4748
+ PANEL_ELEMENT_KINDS,
4749
+ PANEL_EMIT_TOKEN_MAX_LENGTH,
4750
+ PANEL_IMAGE_ROOTS,
4751
+ PANEL_ITEMS_SLOT_MAX_LENGTH,
4752
+ PANEL_MAX_ELEMENTS,
4753
+ PANEL_MIN_ELEMENTS,
4754
+ PANEL_PRIORITY_ABS_MAX,
4755
+ PANEL_SHOW_IF_MAX_LENGTH,
4756
+ PANEL_TEXT_MAX_LENGTH,
4757
+ PANEL_THEME_AMOUNT_FORMAT_MAX_LENGTH,
4758
+ PANEL_THEME_FONT_MAX_LENGTH,
4759
+ PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS,
4760
+ PANEL_THEME_Z_MAX,
4761
+ PANEL_TITLE_MAX_LENGTH,
4068
4762
  PLUGIN_PERMISSION_DESCRIPTIONS,
4069
4763
  SDK_BRIDGE_PROTOCOL_VERSION,
4070
4764
  SDK_REQUIRES_HOST_PROTOCOL_VERSION,
@@ -4082,6 +4776,7 @@ export {
4082
4776
  useArcheAssets,
4083
4777
  useAudioGeneration,
4084
4778
  useClientEntity,
4779
+ useCommunityAssets,
4085
4780
  useDataSubscription,
4086
4781
  useElevenLabsAudioIsolation,
4087
4782
  useElevenLabsForcedAlignment,
@@ -4092,6 +4787,7 @@ export {
4092
4787
  useElevenLabsVoiceChanger,
4093
4788
  useElevenLabsVoiceDesign,
4094
4789
  useEntityInvocation,
4790
+ useFiasAIActions,
4095
4791
  useFiasDataStore,
4096
4792
  useFiasFonts,
4097
4793
  useFiasNavigation,
@@ -4121,5 +4817,9 @@ export {
4121
4817
  useStepNavigation,
4122
4818
  useSurface,
4123
4819
  useVaultDocuments,
4124
- useVaultUserDocuments
4820
+ useVaultUserDocuments,
4821
+ validatePanelAction,
4822
+ validatePanelCatalog,
4823
+ validatePanelDefinition,
4824
+ validatePanelTheme
4125
4825
  };