@fias/arche-sdk 2.12.0 → 2.14.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/bridge.d.ts +27 -1
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +58 -0
- package/dist/bridge.js.map +1 -1
- package/dist/bridge.test.js +93 -0
- package/dist/bridge.test.js.map +1 -1
- package/dist/fias.d.ts +9 -4
- package/dist/fias.d.ts.map +1 -1
- package/dist/fias.js +15 -8
- package/dist/fias.js.map +1 -1
- package/dist/generated/permissions.d.ts +1 -1
- package/dist/generated/permissions.d.ts.map +1 -1
- package/dist/generated/permissions.js +2 -0
- package/dist/generated/permissions.js.map +1 -1
- package/dist/hooks.d.ts +56 -2
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +93 -4
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.test.js +93 -0
- package/dist/hooks.test.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +559 -20
- package/dist/panels.d.ts +213 -0
- package/dist/panels.d.ts.map +1 -0
- package/dist/panels.js +422 -0
- package/dist/panels.js.map +1 -0
- package/dist/panels.test.d.ts +2 -0
- package/dist/panels.test.d.ts.map +1 -0
- package/dist/panels.test.js +127 -0
- package/dist/panels.test.js.map +1 -0
- package/dist/types.d.ts +174 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/default/AGENTS.md +135 -1
- package/templates/default/CLAUDE.md +135 -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 = {
|
|
@@ -1191,7 +1247,8 @@ function useFiasDataStore() {
|
|
|
1191
1247
|
searchable: options?.searchable,
|
|
1192
1248
|
writeMinRole: options?.writeMinRole,
|
|
1193
1249
|
readMinRole: options?.readMinRole,
|
|
1194
|
-
writePolicy: options?.writePolicy
|
|
1250
|
+
writePolicy: options?.writePolicy,
|
|
1251
|
+
constraints: options?.constraints
|
|
1195
1252
|
}
|
|
1196
1253
|
);
|
|
1197
1254
|
return res.collection;
|
|
@@ -1214,12 +1271,18 @@ function useFiasDataStore() {
|
|
|
1214
1271
|
);
|
|
1215
1272
|
const put = useCallback(
|
|
1216
1273
|
async (collection, key, data, options) => {
|
|
1217
|
-
await invokeEntityOp(
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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 };
|
|
1223
1286
|
},
|
|
1224
1287
|
[bridge]
|
|
1225
1288
|
);
|
|
@@ -1251,6 +1314,7 @@ function useFiasDataStore() {
|
|
|
1251
1314
|
key: res.key ?? key,
|
|
1252
1315
|
data: res.data,
|
|
1253
1316
|
updatedAt: res.updatedAt ?? "",
|
|
1317
|
+
...res.version !== void 0 ? { version: res.version } : {},
|
|
1254
1318
|
...res.authoredByCollaborator !== void 0 ? { authoredByCollaborator: res.authoredByCollaborator } : {}
|
|
1255
1319
|
};
|
|
1256
1320
|
},
|
|
@@ -1294,8 +1358,17 @@ function useFiasDataStore() {
|
|
|
1294
1358
|
[bridge]
|
|
1295
1359
|
);
|
|
1296
1360
|
const batch = useCallback(
|
|
1297
|
-
async (operations) => {
|
|
1298
|
-
await invokeEntityOp(
|
|
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 };
|
|
1299
1372
|
},
|
|
1300
1373
|
[bridge]
|
|
1301
1374
|
);
|
|
@@ -1694,6 +1767,12 @@ function useVaultDocuments() {
|
|
|
1694
1767
|
]
|
|
1695
1768
|
);
|
|
1696
1769
|
}
|
|
1770
|
+
function base64ToBytes(base64) {
|
|
1771
|
+
const binary = atob(base64);
|
|
1772
|
+
const bytes = new Uint8Array(binary.length);
|
|
1773
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
1774
|
+
return bytes;
|
|
1775
|
+
}
|
|
1697
1776
|
function bytesToBase64(bytes) {
|
|
1698
1777
|
let binary = "";
|
|
1699
1778
|
const chunk = 32768;
|
|
@@ -1859,7 +1938,21 @@ function useVaultUserDocuments() {
|
|
|
1859
1938
|
},
|
|
1860
1939
|
[bridge]
|
|
1861
1940
|
);
|
|
1862
|
-
|
|
1941
|
+
const getBytes = useCallback(
|
|
1942
|
+
async (documentId) => {
|
|
1943
|
+
const res = await invokeEntityOp(bridge, VAULT_USER_DOCUMENTS_ENTITY_ID, "get_consented_document_bytes", { documentId });
|
|
1944
|
+
return {
|
|
1945
|
+
document: res.document,
|
|
1946
|
+
bytes: base64ToBytes(res.bytesBase64),
|
|
1947
|
+
contentType: res.contentType
|
|
1948
|
+
};
|
|
1949
|
+
},
|
|
1950
|
+
[bridge]
|
|
1951
|
+
);
|
|
1952
|
+
return useMemo2(
|
|
1953
|
+
() => ({ pick, list, get, getDownloadUrl, getBytes }),
|
|
1954
|
+
[pick, list, get, getDownloadUrl, getBytes]
|
|
1955
|
+
);
|
|
1863
1956
|
}
|
|
1864
1957
|
var COMMUNITY_ASSET_URL_REFRESH_BUFFER_MS = 3e4;
|
|
1865
1958
|
var communityAssetUrlCache = /* @__PURE__ */ new Map();
|
|
@@ -2034,6 +2127,48 @@ function useCommunityAssets() {
|
|
|
2034
2127
|
[publish, publishMany, unpublish, listMine, listCommunity, get, getUrl, getUrls, report]
|
|
2035
2128
|
);
|
|
2036
2129
|
}
|
|
2130
|
+
function useFiasAIActions(config) {
|
|
2131
|
+
const bridge = useBridge();
|
|
2132
|
+
const onActionRef = useRef(config?.onAction ?? null);
|
|
2133
|
+
onActionRef.current = config?.onAction ?? null;
|
|
2134
|
+
useEffect2(() => {
|
|
2135
|
+
bridge.setAiActionHandler((actionId, params) => {
|
|
2136
|
+
const handler = onActionRef.current;
|
|
2137
|
+
if (!handler) {
|
|
2138
|
+
throw new Error("no Fias AI action handler is currently registered");
|
|
2139
|
+
}
|
|
2140
|
+
return handler(actionId, params);
|
|
2141
|
+
});
|
|
2142
|
+
return () => bridge.setAiActionHandler(null);
|
|
2143
|
+
}, [bridge]);
|
|
2144
|
+
const declarationJson = config ? JSON.stringify({ description: config.description, actions: config.actions }) : null;
|
|
2145
|
+
useEffect2(() => {
|
|
2146
|
+
let cancelled = false;
|
|
2147
|
+
void bridge.waitForInit().then(() => {
|
|
2148
|
+
if (cancelled) return;
|
|
2149
|
+
bridge.declareAiActions(
|
|
2150
|
+
declarationJson ? JSON.parse(declarationJson) : null
|
|
2151
|
+
);
|
|
2152
|
+
});
|
|
2153
|
+
return () => {
|
|
2154
|
+
cancelled = true;
|
|
2155
|
+
};
|
|
2156
|
+
}, [bridge, declarationJson]);
|
|
2157
|
+
useEffect2(() => {
|
|
2158
|
+
return () => bridge.declareAiActions(null);
|
|
2159
|
+
}, [bridge]);
|
|
2160
|
+
const stateJson = config?.state ? JSON.stringify(config.state) : null;
|
|
2161
|
+
useEffect2(() => {
|
|
2162
|
+
if (stateJson === null) return;
|
|
2163
|
+
let cancelled = false;
|
|
2164
|
+
void bridge.waitForInit().then(() => {
|
|
2165
|
+
if (!cancelled) bridge.sendAiState(JSON.parse(stateJson));
|
|
2166
|
+
});
|
|
2167
|
+
return () => {
|
|
2168
|
+
cancelled = true;
|
|
2169
|
+
};
|
|
2170
|
+
}, [bridge, stateJson]);
|
|
2171
|
+
}
|
|
2037
2172
|
|
|
2038
2173
|
// src/generated/surfaces.ts
|
|
2039
2174
|
var AUDIO_AUDIO_ISOLATION_SURFACE_KEY = "audio.audio-isolation";
|
|
@@ -2101,6 +2236,7 @@ var useIntegrationWebSearch = () => useSurface(
|
|
|
2101
2236
|
|
|
2102
2237
|
// src/generated/permissions.ts
|
|
2103
2238
|
var VALID_PLUGIN_PERMISSIONS = [
|
|
2239
|
+
"ai:actions",
|
|
2104
2240
|
"assets:community:publish",
|
|
2105
2241
|
"assets:community:read",
|
|
2106
2242
|
"assets:read",
|
|
@@ -2127,6 +2263,7 @@ function isValidPluginPermission(perm) {
|
|
|
2127
2263
|
return VALID_PLUGIN_PERMISSIONS.includes(perm);
|
|
2128
2264
|
}
|
|
2129
2265
|
var PLUGIN_PERMISSION_DESCRIPTIONS = {
|
|
2266
|
+
"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.",
|
|
2130
2267
|
"assets:community:publish": "Share images you made here so other people using this app can see them, and manage what you have shared.",
|
|
2131
2268
|
"assets:community:read": "Show you images that other people using this app have shared. Everything shared is checked before anyone can see it.",
|
|
2132
2269
|
"assets:read": "Show images from this app's published asset library.",
|
|
@@ -2186,7 +2323,8 @@ var fias = {
|
|
|
2186
2323
|
name,
|
|
2187
2324
|
userScope: options?.userScope ?? "user",
|
|
2188
2325
|
searchable: options?.searchable,
|
|
2189
|
-
writePolicy: options?.writePolicy
|
|
2326
|
+
writePolicy: options?.writePolicy,
|
|
2327
|
+
constraints: options?.constraints
|
|
2190
2328
|
}
|
|
2191
2329
|
);
|
|
2192
2330
|
return res.collection;
|
|
@@ -2202,12 +2340,16 @@ var fias = {
|
|
|
2202
2340
|
async deleteCollection(name) {
|
|
2203
2341
|
await invokeEntityOp(getBridge(), PLUGIN_DATASTORE_ENTITY_ID, "delete_collection", { name });
|
|
2204
2342
|
},
|
|
2343
|
+
/** Write a document. Resolves with the document's post-write `version`
|
|
2344
|
+
* (ADR-014) so a follow-up CAS batch needs no read-back. */
|
|
2205
2345
|
async put(collection, key, data) {
|
|
2206
|
-
await invokeEntityOp(
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2346
|
+
const res = await invokeEntityOp(
|
|
2347
|
+
getBridge(),
|
|
2348
|
+
PLUGIN_DATASTORE_ENTITY_ID,
|
|
2349
|
+
"put",
|
|
2350
|
+
{ collection, key, data }
|
|
2351
|
+
);
|
|
2352
|
+
return { version: res?.version };
|
|
2211
2353
|
},
|
|
2212
2354
|
async get(collection, key) {
|
|
2213
2355
|
const res = await invokeEntityOp(
|
|
@@ -2227,6 +2369,7 @@ var fias = {
|
|
|
2227
2369
|
key: res.key ?? key,
|
|
2228
2370
|
data: res.data,
|
|
2229
2371
|
updatedAt: res.updatedAt ?? "",
|
|
2372
|
+
...res.version !== void 0 ? { version: res.version } : {},
|
|
2230
2373
|
...res.authoredByCollaborator !== void 0 ? { authoredByCollaborator: res.authoredByCollaborator } : {}
|
|
2231
2374
|
};
|
|
2232
2375
|
},
|
|
@@ -2250,9 +2393,20 @@ var fias = {
|
|
|
2250
2393
|
async delete(collection, key) {
|
|
2251
2394
|
await invokeEntityOp(getBridge(), PLUGIN_DATASTORE_ENTITY_ID, "delete", { collection, key });
|
|
2252
2395
|
},
|
|
2253
|
-
/** Apply up to 25 put/delete ops in one all-or-nothing transaction.
|
|
2254
|
-
|
|
2255
|
-
|
|
2396
|
+
/** Apply up to 25 put/delete ops in one all-or-nothing transaction.
|
|
2397
|
+
* Pass `options.preconditions` (ADR-014) to make the batch conditional;
|
|
2398
|
+
* the result carries each put's post-write version. */
|
|
2399
|
+
async batch(operations, options) {
|
|
2400
|
+
const res = await invokeEntityOp(
|
|
2401
|
+
getBridge(),
|
|
2402
|
+
PLUGIN_DATASTORE_ENTITY_ID,
|
|
2403
|
+
"batch",
|
|
2404
|
+
{
|
|
2405
|
+
operations,
|
|
2406
|
+
...options?.preconditions?.length ? { preconditions: options.preconditions } : {}
|
|
2407
|
+
}
|
|
2408
|
+
);
|
|
2409
|
+
return { written: res?.written };
|
|
2256
2410
|
}
|
|
2257
2411
|
}
|
|
2258
2412
|
};
|
|
@@ -4213,6 +4367,371 @@ function raceWithTimeout(promise, ms) {
|
|
|
4213
4367
|
function useFiasFonts() {
|
|
4214
4368
|
return useMemo3(() => ({ fonts: FONT_CATALOG, ensureFontLoaded }), []);
|
|
4215
4369
|
}
|
|
4370
|
+
|
|
4371
|
+
// src/panels.ts
|
|
4372
|
+
var PANEL_MIN_ELEMENTS = 1;
|
|
4373
|
+
var PANEL_MAX_ELEMENTS = 24;
|
|
4374
|
+
var PANEL_TITLE_MAX_LENGTH = 80;
|
|
4375
|
+
var PANEL_TEXT_MAX_LENGTH = 500;
|
|
4376
|
+
var PANEL_IMAGE_ALT_MAX_LENGTH = 120;
|
|
4377
|
+
var PANEL_IMAGE_KEY_MAX_LENGTH = 500;
|
|
4378
|
+
var PANEL_IMAGE_MIN_HEIGHT = 24;
|
|
4379
|
+
var PANEL_IMAGE_MAX_HEIGHT = 512;
|
|
4380
|
+
var PANEL_BUTTON_LABEL_MAX_LENGTH = 40;
|
|
4381
|
+
var PANEL_ACTION_MAX_LENGTH = 2048;
|
|
4382
|
+
var PANEL_EMIT_TOKEN_MAX_LENGTH = 64;
|
|
4383
|
+
var PANEL_ITEMS_EMPTY_TEXT_MAX_LENGTH = 120;
|
|
4384
|
+
var PANEL_ITEMS_SLOT_MAX_LENGTH = 64;
|
|
4385
|
+
var PANEL_SHOW_IF_MAX_LENGTH = 64;
|
|
4386
|
+
var PANEL_PRIORITY_ABS_MAX = 1e3;
|
|
4387
|
+
var PANEL_THEME_FONT_MAX_LENGTH = 200;
|
|
4388
|
+
var PANEL_THEME_AMOUNT_FORMAT_MAX_LENGTH = 24;
|
|
4389
|
+
var PANEL_THEME_Z_MAX = 1e6;
|
|
4390
|
+
var PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS = 4;
|
|
4391
|
+
var PANEL_IMAGE_ROOTS = ["dfe-icons", "dfe-avatars"];
|
|
4392
|
+
var PANEL_ELEMENT_KINDS = [
|
|
4393
|
+
"text",
|
|
4394
|
+
"conditionalText",
|
|
4395
|
+
"image",
|
|
4396
|
+
"assetImage",
|
|
4397
|
+
"items",
|
|
4398
|
+
"slottedItems",
|
|
4399
|
+
"button",
|
|
4400
|
+
"spacer"
|
|
4401
|
+
];
|
|
4402
|
+
var PANEL_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
|
|
4403
|
+
var SLOT_PATTERN = new RegExp(`^[a-z0-9][a-z0-9_-]{0,${PANEL_ITEMS_SLOT_MAX_LENGTH - 1}}$`);
|
|
4404
|
+
var EMIT_TOKEN_PATTERN = new RegExp(`^[a-z0-9][a-z0-9_-]{0,${PANEL_EMIT_TOKEN_MAX_LENGTH - 1}}$`);
|
|
4405
|
+
var SHOW_IF_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
4406
|
+
var ASSET_ID_PATTERN = /^as_[a-f0-9]{32}$/;
|
|
4407
|
+
var ARCHE_ID_PATTERN = /^arc_[a-f0-9]{32}$/;
|
|
4408
|
+
var IMAGE_KEY_PATTERN = new RegExp(
|
|
4409
|
+
`^(?:${PANEL_IMAGE_ROOTS.join("|")})/[A-Za-z0-9._/-]+\\.(?:png|jpe?g|webp|gif|svg)$`,
|
|
4410
|
+
"i"
|
|
4411
|
+
);
|
|
4412
|
+
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;
|
|
4413
|
+
var THEME_LENGTH_PATTERN = new RegExp(
|
|
4414
|
+
`^\\d{1,${PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS}}(?:\\.\\d{1,3})?(?:px|rem|em)$`
|
|
4415
|
+
);
|
|
4416
|
+
var THEME_FONT_PATTERN = /^[A-Za-z0-9 '",._-]+$/;
|
|
4417
|
+
var TEXT_STYLES = ["title", "body", "caption"];
|
|
4418
|
+
var TEXT_ALIGNS = ["left", "center", "right"];
|
|
4419
|
+
var SIZES = ["small", "medium", "large"];
|
|
4420
|
+
var BUTTON_VARIANTS = ["primary", "secondary"];
|
|
4421
|
+
function isRecord(value) {
|
|
4422
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4423
|
+
}
|
|
4424
|
+
function rejectUnknownKeys(value, allowed, path, issues) {
|
|
4425
|
+
for (const key of Object.keys(value)) {
|
|
4426
|
+
if (!allowed.includes(key)) issues.push(`${path}: unknown field "${key}"`);
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
function checkString(value, path, issues, opts) {
|
|
4430
|
+
if (typeof value !== "string") {
|
|
4431
|
+
issues.push(`${path}: expected a string`);
|
|
4432
|
+
return;
|
|
4433
|
+
}
|
|
4434
|
+
if (value.length < (opts.min ?? 1)) issues.push(`${path}: must not be empty`);
|
|
4435
|
+
if (value.length > opts.max) issues.push(`${path}: longer than ${opts.max} characters`);
|
|
4436
|
+
if (opts.pattern && !opts.pattern.test(value)) {
|
|
4437
|
+
issues.push(`${path}: ${opts.hint ?? "does not match the required format"}`);
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
function checkOptionalEnum(value, path, issues, allowed) {
|
|
4441
|
+
if (value === void 0) return;
|
|
4442
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
4443
|
+
issues.push(`${path}: must be one of ${allowed.join(" | ")}`);
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
function checkOptionalBoolean(value, path, issues) {
|
|
4447
|
+
if (value !== void 0 && typeof value !== "boolean") issues.push(`${path}: expected a boolean`);
|
|
4448
|
+
}
|
|
4449
|
+
function checkOptionalIntRange(value, path, issues, min, max) {
|
|
4450
|
+
if (value === void 0) return;
|
|
4451
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) {
|
|
4452
|
+
issues.push(`${path}: must be an integer between ${min} and ${max}`);
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
function validatePanelAction(action, path, issues) {
|
|
4456
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
4457
|
+
issues.push(`${path}: expected an action string`);
|
|
4458
|
+
return;
|
|
4459
|
+
}
|
|
4460
|
+
if (action.length > PANEL_ACTION_MAX_LENGTH) {
|
|
4461
|
+
issues.push(`${path}: longer than ${PANEL_ACTION_MAX_LENGTH} characters`);
|
|
4462
|
+
return;
|
|
4463
|
+
}
|
|
4464
|
+
if (action === "close") return;
|
|
4465
|
+
if (action.startsWith("emit:")) {
|
|
4466
|
+
if (!EMIT_TOKEN_PATTERN.test(action.slice("emit:".length))) {
|
|
4467
|
+
issues.push(
|
|
4468
|
+
`${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`
|
|
4469
|
+
);
|
|
4470
|
+
}
|
|
4471
|
+
return;
|
|
4472
|
+
}
|
|
4473
|
+
if (action.startsWith("open_arche:")) {
|
|
4474
|
+
if (!ARCHE_ID_PATTERN.test(action.slice("open_arche:".length))) {
|
|
4475
|
+
issues.push(`${path}: open_arche target must be an arche id (arc_ + 32 hex chars)`);
|
|
4476
|
+
}
|
|
4477
|
+
return;
|
|
4478
|
+
}
|
|
4479
|
+
if (action.startsWith("open_url:")) {
|
|
4480
|
+
if (/\s/.test(action)) {
|
|
4481
|
+
issues.push(`${path}: action must not contain whitespace`);
|
|
4482
|
+
return;
|
|
4483
|
+
}
|
|
4484
|
+
let parsed;
|
|
4485
|
+
try {
|
|
4486
|
+
parsed = new URL(action.slice("open_url:".length));
|
|
4487
|
+
} catch {
|
|
4488
|
+
issues.push(`${path}: open_url must be a valid absolute URL`);
|
|
4489
|
+
return;
|
|
4490
|
+
}
|
|
4491
|
+
if (parsed.protocol !== "https:") issues.push(`${path}: open_url scheme must be https`);
|
|
4492
|
+
return;
|
|
4493
|
+
}
|
|
4494
|
+
issues.push(
|
|
4495
|
+
`${path}: must be one of close | emit:<token> | open_arche:arc_<32 hex> | open_url:https://\u2026`
|
|
4496
|
+
);
|
|
4497
|
+
}
|
|
4498
|
+
function validateTextFields(element, path, issues) {
|
|
4499
|
+
checkString(element.text, `${path}.text`, issues, { max: PANEL_TEXT_MAX_LENGTH });
|
|
4500
|
+
checkOptionalEnum(element.style, `${path}.style`, issues, TEXT_STYLES);
|
|
4501
|
+
checkOptionalEnum(element.align, `${path}.align`, issues, TEXT_ALIGNS);
|
|
4502
|
+
checkOptionalBoolean(element.joinNext, `${path}.joinNext`, issues);
|
|
4503
|
+
}
|
|
4504
|
+
function validateItemsFields(element, path, issues) {
|
|
4505
|
+
checkOptionalBoolean(element.showIcons, `${path}.showIcons`, issues);
|
|
4506
|
+
checkOptionalEnum(element.iconSize, `${path}.iconSize`, issues, SIZES);
|
|
4507
|
+
if (element.emptyText !== void 0) {
|
|
4508
|
+
checkString(element.emptyText, `${path}.emptyText`, issues, {
|
|
4509
|
+
min: 0,
|
|
4510
|
+
max: PANEL_ITEMS_EMPTY_TEXT_MAX_LENGTH
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
function validateImageFields(element, path, issues) {
|
|
4515
|
+
if (element.alt !== void 0) {
|
|
4516
|
+
checkString(element.alt, `${path}.alt`, issues, { min: 0, max: PANEL_IMAGE_ALT_MAX_LENGTH });
|
|
4517
|
+
}
|
|
4518
|
+
checkOptionalIntRange(
|
|
4519
|
+
element.maxHeight,
|
|
4520
|
+
`${path}.maxHeight`,
|
|
4521
|
+
issues,
|
|
4522
|
+
PANEL_IMAGE_MIN_HEIGHT,
|
|
4523
|
+
PANEL_IMAGE_MAX_HEIGHT
|
|
4524
|
+
);
|
|
4525
|
+
}
|
|
4526
|
+
function validateElement(value, path, issues) {
|
|
4527
|
+
if (!isRecord(value)) {
|
|
4528
|
+
issues.push(`${path}: expected an object`);
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
const kind = value.kind;
|
|
4532
|
+
if (typeof kind !== "string" || !PANEL_ELEMENT_KINDS.includes(kind)) {
|
|
4533
|
+
issues.push(
|
|
4534
|
+
`${path}.kind: unknown element kind ${JSON.stringify(kind)} \u2014 expected one of ${PANEL_ELEMENT_KINDS.join(" | ")}`
|
|
4535
|
+
);
|
|
4536
|
+
return;
|
|
4537
|
+
}
|
|
4538
|
+
switch (kind) {
|
|
4539
|
+
case "text":
|
|
4540
|
+
rejectUnknownKeys(value, ["kind", "text", "style", "align", "joinNext"], path, issues);
|
|
4541
|
+
validateTextFields(value, path, issues);
|
|
4542
|
+
return;
|
|
4543
|
+
case "conditionalText":
|
|
4544
|
+
rejectUnknownKeys(
|
|
4545
|
+
value,
|
|
4546
|
+
["kind", "text", "style", "align", "joinNext", "showIf"],
|
|
4547
|
+
path,
|
|
4548
|
+
issues
|
|
4549
|
+
);
|
|
4550
|
+
validateTextFields(value, path, issues);
|
|
4551
|
+
checkString(value.showIf, `${path}.showIf`, issues, {
|
|
4552
|
+
max: PANEL_SHOW_IF_MAX_LENGTH,
|
|
4553
|
+
pattern: SHOW_IF_PATTERN,
|
|
4554
|
+
hint: "must be a value NAME (letters, digits, underscore; not starting with a digit) \u2014 expressions and operators are not supported"
|
|
4555
|
+
});
|
|
4556
|
+
return;
|
|
4557
|
+
case "image":
|
|
4558
|
+
rejectUnknownKeys(value, ["kind", "imageKey", "alt", "maxHeight"], path, issues);
|
|
4559
|
+
checkString(value.imageKey, `${path}.imageKey`, issues, {
|
|
4560
|
+
max: PANEL_IMAGE_KEY_MAX_LENGTH,
|
|
4561
|
+
pattern: IMAGE_KEY_PATTERN,
|
|
4562
|
+
hint: `must be a relative key under ${PANEL_IMAGE_ROOTS.join(" or ")}/ ending in an image extension (no URL, no leading slash)`
|
|
4563
|
+
});
|
|
4564
|
+
if (typeof value.imageKey === "string" && value.imageKey.includes("..")) {
|
|
4565
|
+
issues.push(`${path}.imageKey: must not contain ".."`);
|
|
4566
|
+
}
|
|
4567
|
+
validateImageFields(value, path, issues);
|
|
4568
|
+
return;
|
|
4569
|
+
case "assetImage":
|
|
4570
|
+
rejectUnknownKeys(value, ["kind", "assetId", "alt", "maxHeight"], path, issues);
|
|
4571
|
+
checkString(value.assetId, `${path}.assetId`, issues, {
|
|
4572
|
+
max: 64,
|
|
4573
|
+
pattern: ASSET_ID_PATTERN,
|
|
4574
|
+
hint: "must be an arche asset id (as_ + 32 hex chars)"
|
|
4575
|
+
});
|
|
4576
|
+
validateImageFields(value, path, issues);
|
|
4577
|
+
return;
|
|
4578
|
+
case "items":
|
|
4579
|
+
rejectUnknownKeys(value, ["kind", "showIcons", "iconSize", "emptyText"], path, issues);
|
|
4580
|
+
validateItemsFields(value, path, issues);
|
|
4581
|
+
return;
|
|
4582
|
+
case "slottedItems":
|
|
4583
|
+
rejectUnknownKeys(
|
|
4584
|
+
value,
|
|
4585
|
+
["kind", "slot", "showIcons", "iconSize", "emptyText"],
|
|
4586
|
+
path,
|
|
4587
|
+
issues
|
|
4588
|
+
);
|
|
4589
|
+
checkString(value.slot, `${path}.slot`, issues, {
|
|
4590
|
+
max: PANEL_ITEMS_SLOT_MAX_LENGTH,
|
|
4591
|
+
pattern: SLOT_PATTERN,
|
|
4592
|
+
hint: "must be lowercase letters, digits, hyphens and underscores, not starting with a separator"
|
|
4593
|
+
});
|
|
4594
|
+
validateItemsFields(value, path, issues);
|
|
4595
|
+
return;
|
|
4596
|
+
case "button":
|
|
4597
|
+
rejectUnknownKeys(value, ["kind", "label", "action", "variant"], path, issues);
|
|
4598
|
+
checkString(value.label, `${path}.label`, issues, { max: PANEL_BUTTON_LABEL_MAX_LENGTH });
|
|
4599
|
+
validatePanelAction(value.action, `${path}.action`, issues);
|
|
4600
|
+
checkOptionalEnum(value.variant, `${path}.variant`, issues, BUTTON_VARIANTS);
|
|
4601
|
+
return;
|
|
4602
|
+
case "spacer":
|
|
4603
|
+
rejectUnknownKeys(value, ["kind", "size"], path, issues);
|
|
4604
|
+
checkOptionalEnum(value.size, `${path}.size`, issues, SIZES);
|
|
4605
|
+
return;
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
var THEME_COLOR_FIELDS = ["bg", "fg", "border", "accent", "accentFg", "scrim"];
|
|
4609
|
+
var THEME_LENGTH_FIELDS = [
|
|
4610
|
+
"radius",
|
|
4611
|
+
"buttonRadius",
|
|
4612
|
+
"gap",
|
|
4613
|
+
"fontSize",
|
|
4614
|
+
"widthSmall",
|
|
4615
|
+
"widthMedium",
|
|
4616
|
+
"widthLarge"
|
|
4617
|
+
];
|
|
4618
|
+
function validatePanelTheme(value, path = "theme") {
|
|
4619
|
+
const issues = [];
|
|
4620
|
+
if (!isRecord(value)) return { ok: false, issues: [`${path}: expected an object`] };
|
|
4621
|
+
rejectUnknownKeys(
|
|
4622
|
+
value,
|
|
4623
|
+
[...THEME_COLOR_FIELDS, ...THEME_LENGTH_FIELDS, "font", "z", "amountFormat"],
|
|
4624
|
+
path,
|
|
4625
|
+
issues
|
|
4626
|
+
);
|
|
4627
|
+
for (const field of THEME_COLOR_FIELDS) {
|
|
4628
|
+
if (value[field] === void 0) continue;
|
|
4629
|
+
checkString(value[field], `${path}.${field}`, issues, {
|
|
4630
|
+
max: 64,
|
|
4631
|
+
pattern: THEME_COLOR_PATTERN,
|
|
4632
|
+
hint: "must be a hex colour (#rgb, #rgba, #rrggbb, #rrggbbaa) or rgb()/rgba() with numeric components"
|
|
4633
|
+
});
|
|
4634
|
+
}
|
|
4635
|
+
for (const field of THEME_LENGTH_FIELDS) {
|
|
4636
|
+
if (value[field] === void 0) continue;
|
|
4637
|
+
checkString(value[field], `${path}.${field}`, issues, {
|
|
4638
|
+
max: 16,
|
|
4639
|
+
pattern: THEME_LENGTH_PATTERN,
|
|
4640
|
+
hint: `must be a non-negative number (max ${PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS} digits before the point) followed by px, rem or em`
|
|
4641
|
+
});
|
|
4642
|
+
}
|
|
4643
|
+
if (value.font !== void 0) {
|
|
4644
|
+
checkString(value.font, `${path}.font`, issues, {
|
|
4645
|
+
max: PANEL_THEME_FONT_MAX_LENGTH,
|
|
4646
|
+
pattern: THEME_FONT_PATTERN,
|
|
4647
|
+
hint: "must be a font-family stack (names, commas, quotes) \u2014 no parentheses, so no url() or var()"
|
|
4648
|
+
});
|
|
4649
|
+
}
|
|
4650
|
+
checkOptionalIntRange(value.z, `${path}.z`, issues, 0, PANEL_THEME_Z_MAX);
|
|
4651
|
+
if (value.amountFormat !== void 0) {
|
|
4652
|
+
checkString(value.amountFormat, `${path}.amountFormat`, issues, {
|
|
4653
|
+
max: PANEL_THEME_AMOUNT_FORMAT_MAX_LENGTH
|
|
4654
|
+
});
|
|
4655
|
+
if (typeof value.amountFormat === "string" && !value.amountFormat.includes("{n}")) {
|
|
4656
|
+
issues.push(`${path}.amountFormat: must contain the placeholder {n}`);
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
|
|
4660
|
+
}
|
|
4661
|
+
function validatePanelDefinition(value, path = "panel") {
|
|
4662
|
+
const issues = [];
|
|
4663
|
+
if (!isRecord(value)) return { ok: false, issues: [`${path}: expected an object`] };
|
|
4664
|
+
rejectUnknownKeys(value, ["id", "title", "elements", "presentation"], path, issues);
|
|
4665
|
+
checkString(value.id, `${path}.id`, issues, {
|
|
4666
|
+
max: 64,
|
|
4667
|
+
pattern: PANEL_ID_PATTERN,
|
|
4668
|
+
hint: "must be 2\u201364 chars: lowercase letters, digits and hyphens, not starting with a hyphen"
|
|
4669
|
+
});
|
|
4670
|
+
checkString(value.title, `${path}.title`, issues, { max: PANEL_TITLE_MAX_LENGTH });
|
|
4671
|
+
if (!Array.isArray(value.elements)) {
|
|
4672
|
+
issues.push(`${path}.elements: expected an array`);
|
|
4673
|
+
} else {
|
|
4674
|
+
if (value.elements.length < PANEL_MIN_ELEMENTS) {
|
|
4675
|
+
issues.push(`${path}.elements: needs at least ${PANEL_MIN_ELEMENTS} element`);
|
|
4676
|
+
}
|
|
4677
|
+
if (value.elements.length > PANEL_MAX_ELEMENTS) {
|
|
4678
|
+
issues.push(`${path}.elements: more than ${PANEL_MAX_ELEMENTS} elements`);
|
|
4679
|
+
}
|
|
4680
|
+
value.elements.forEach(
|
|
4681
|
+
(element, index) => validateElement(element, `${path}.elements[${index}]`, issues)
|
|
4682
|
+
);
|
|
4683
|
+
}
|
|
4684
|
+
if (value.presentation !== void 0) {
|
|
4685
|
+
if (!isRecord(value.presentation)) {
|
|
4686
|
+
issues.push(`${path}.presentation: expected an object`);
|
|
4687
|
+
} else {
|
|
4688
|
+
rejectUnknownKeys(
|
|
4689
|
+
value.presentation,
|
|
4690
|
+
["size", "dismissible", "priority"],
|
|
4691
|
+
`${path}.presentation`,
|
|
4692
|
+
issues
|
|
4693
|
+
);
|
|
4694
|
+
checkOptionalEnum(value.presentation.size, `${path}.presentation.size`, issues, SIZES);
|
|
4695
|
+
checkOptionalBoolean(
|
|
4696
|
+
value.presentation.dismissible,
|
|
4697
|
+
`${path}.presentation.dismissible`,
|
|
4698
|
+
issues
|
|
4699
|
+
);
|
|
4700
|
+
checkOptionalIntRange(
|
|
4701
|
+
value.presentation.priority,
|
|
4702
|
+
`${path}.presentation.priority`,
|
|
4703
|
+
issues,
|
|
4704
|
+
-PANEL_PRIORITY_ABS_MAX,
|
|
4705
|
+
PANEL_PRIORITY_ABS_MAX
|
|
4706
|
+
);
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
|
|
4710
|
+
}
|
|
4711
|
+
function validatePanelCatalog(value) {
|
|
4712
|
+
const issues = [];
|
|
4713
|
+
if (!isRecord(value)) return { ok: false, issues: ["catalog: expected an object"] };
|
|
4714
|
+
rejectUnknownKeys(value, ["panels", "theme"], "catalog", issues);
|
|
4715
|
+
if (value.theme !== void 0) {
|
|
4716
|
+
const theme = validatePanelTheme(value.theme, "catalog.theme");
|
|
4717
|
+
if (!theme.ok) issues.push(...theme.issues);
|
|
4718
|
+
}
|
|
4719
|
+
if (!Array.isArray(value.panels)) {
|
|
4720
|
+
issues.push("catalog.panels: expected an array");
|
|
4721
|
+
return { ok: false, issues };
|
|
4722
|
+
}
|
|
4723
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4724
|
+
value.panels.forEach((panel, index) => {
|
|
4725
|
+
const path = `catalog.panels[${index}]`;
|
|
4726
|
+
const result = validatePanelDefinition(panel, path);
|
|
4727
|
+
if (!result.ok) issues.push(...result.issues);
|
|
4728
|
+
if (isRecord(panel) && typeof panel.id === "string") {
|
|
4729
|
+
if (seen.has(panel.id)) issues.push(`${path}.id: duplicate panel id "${panel.id}"`);
|
|
4730
|
+
seen.add(panel.id);
|
|
4731
|
+
}
|
|
4732
|
+
});
|
|
4733
|
+
return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
|
|
4734
|
+
}
|
|
4216
4735
|
export {
|
|
4217
4736
|
AUDIO_AUDIO_ISOLATION_SURFACE_KEY,
|
|
4218
4737
|
AUDIO_FORCED_ALIGNMENT_SURFACE_KEY,
|
|
@@ -4245,6 +4764,21 @@ export {
|
|
|
4245
4764
|
LIGHT_THEME,
|
|
4246
4765
|
NLLB_LANGUAGES,
|
|
4247
4766
|
NLLB_TRANSLATION_ENTITY_ID,
|
|
4767
|
+
PANEL_BUTTON_LABEL_MAX_LENGTH,
|
|
4768
|
+
PANEL_ELEMENT_KINDS,
|
|
4769
|
+
PANEL_EMIT_TOKEN_MAX_LENGTH,
|
|
4770
|
+
PANEL_IMAGE_ROOTS,
|
|
4771
|
+
PANEL_ITEMS_SLOT_MAX_LENGTH,
|
|
4772
|
+
PANEL_MAX_ELEMENTS,
|
|
4773
|
+
PANEL_MIN_ELEMENTS,
|
|
4774
|
+
PANEL_PRIORITY_ABS_MAX,
|
|
4775
|
+
PANEL_SHOW_IF_MAX_LENGTH,
|
|
4776
|
+
PANEL_TEXT_MAX_LENGTH,
|
|
4777
|
+
PANEL_THEME_AMOUNT_FORMAT_MAX_LENGTH,
|
|
4778
|
+
PANEL_THEME_FONT_MAX_LENGTH,
|
|
4779
|
+
PANEL_THEME_LENGTH_MAX_INTEGER_DIGITS,
|
|
4780
|
+
PANEL_THEME_Z_MAX,
|
|
4781
|
+
PANEL_TITLE_MAX_LENGTH,
|
|
4248
4782
|
PLUGIN_PERMISSION_DESCRIPTIONS,
|
|
4249
4783
|
SDK_BRIDGE_PROTOCOL_VERSION,
|
|
4250
4784
|
SDK_REQUIRES_HOST_PROTOCOL_VERSION,
|
|
@@ -4273,6 +4807,7 @@ export {
|
|
|
4273
4807
|
useElevenLabsVoiceChanger,
|
|
4274
4808
|
useElevenLabsVoiceDesign,
|
|
4275
4809
|
useEntityInvocation,
|
|
4810
|
+
useFiasAIActions,
|
|
4276
4811
|
useFiasDataStore,
|
|
4277
4812
|
useFiasFonts,
|
|
4278
4813
|
useFiasNavigation,
|
|
@@ -4302,5 +4837,9 @@ export {
|
|
|
4302
4837
|
useStepNavigation,
|
|
4303
4838
|
useSurface,
|
|
4304
4839
|
useVaultDocuments,
|
|
4305
|
-
useVaultUserDocuments
|
|
4840
|
+
useVaultUserDocuments,
|
|
4841
|
+
validatePanelAction,
|
|
4842
|
+
validatePanelCatalog,
|
|
4843
|
+
validatePanelDefinition,
|
|
4844
|
+
validatePanelTheme
|
|
4306
4845
|
};
|