@qredence/fleet 0.5.1 → 0.5.7
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/CHANGELOG.md +19 -0
- package/bin/fleet-prime.mjs +6 -0
- package/dist/web/client/assets/action-swap-roll-BB8DFMuf.js +1 -0
- package/dist/web/client/assets/agent-activity-CDcDNVC6.js +1 -0
- package/dist/web/client/assets/agent-disclosure-B8SnhQXL.js +1 -0
- package/dist/web/client/assets/analytics-stub-B8MIkiDB.js +1 -0
- package/dist/web/client/assets/{artifacts-panel-CX62LQ13.js → artifacts-panel-CmHYXfoo.js} +1 -1
- package/dist/web/client/assets/button-BV9NafmI.js +1 -0
- package/dist/web/client/assets/{chat-CsepBQwy.js → chat-W-yOMyG_.js} +1 -1
- package/dist/web/client/assets/chat-command-palette-DiG-i3uJ.js +1 -0
- package/dist/web/client/assets/check-JjHKAZas.js +1 -0
- package/dist/web/client/assets/chevron-up-CnSvySGH.js +1 -0
- package/dist/web/client/assets/circle-alert-CONlFReH.js +1 -0
- package/dist/web/client/assets/circle-x-CkKCB__4.js +1 -0
- package/dist/web/client/assets/citations-CpTFUvx7.js +2 -0
- package/dist/web/client/assets/collapsible-BoXtZZWN.js +1 -0
- package/dist/web/client/assets/command-Cx-UYeQ9.js +1 -0
- package/dist/web/client/assets/createBaseUIEventDetails-DV0dQrBX.js +1 -0
- package/dist/web/client/assets/dialog-Dh7WpGIu.js +1 -0
- package/dist/web/client/assets/dist-BPaFisIZ.js +41 -0
- package/dist/web/client/assets/earth-DGZXAN5r.js +1 -0
- package/dist/web/client/assets/ease-u56MhKYS.js +1 -0
- package/dist/web/client/assets/file-text-T-NcAlZs.js +1 -0
- package/dist/web/client/assets/fleet-message-queue-B6zAK-hX.js +1 -0
- package/dist/web/client/assets/{fleet-pi-tool-renderer-BD_ZAjlG.js → fleet-pi-tool-renderer-Btq9mDUe.js} +29 -29
- package/dist/web/client/assets/fleet-reasoning-panel-MtbSdoCv.js +1 -0
- package/dist/web/client/assets/fleet-subagent-list-iszvrvy_.js +1 -0
- package/dist/web/client/assets/fleet-tool-timeline-COzPrwr8.js +1 -0
- package/dist/web/client/assets/{fork-picker-dialog-C2cVJUbf.js → fork-picker-dialog-CAhWx0G5.js} +1 -1
- package/dist/web/client/assets/gauge-DNpstbBn.js +1 -0
- package/dist/web/client/assets/generative-text-renderer-C1JrmYfx.js +6 -0
- package/dist/web/client/assets/globals-Bfr0jrPq.css +2 -0
- package/dist/web/client/assets/horizontal-resize-Bl3AyBJg.js +1 -0
- package/dist/web/client/assets/image-BbPmlw-n.js +1 -0
- package/dist/web/client/assets/index-QgUwd4m8.js +12 -0
- package/dist/web/client/assets/input-Bol3TP4v.js +1 -0
- package/dist/web/client/assets/input-group-D5gi9zBK.js +1 -0
- package/dist/web/client/assets/{ipython-tool-BfeIa0nK.js → ipython-tool-BrMePqD-.js} +1 -1
- package/dist/web/client/assets/{markdown-DG7pUdsX.js → markdown-RD_ydkyD.js} +2 -2
- package/dist/web/client/assets/{markdown-code-BgMzpy7O.js → markdown-code-ETqXqY8E.js} +1 -1
- package/dist/web/client/assets/message-square-hbDao-A1.js +1 -0
- package/dist/web/client/assets/model-selector-list-CJY9pyt2.js +1 -0
- package/dist/web/client/assets/notify-DKTqXChG.js +1 -0
- package/dist/web/client/assets/{openui-renderer-DlUo6ehT.js → openui-renderer-BZMghkzQ.js} +8 -8
- package/dist/web/client/assets/{prime-agent-artifacts-CHGb5V_j.js → prime-agent-artifacts-C-g1VPiW.js} +1 -1
- package/dist/web/client/assets/prompt-suggestions-CXM6knMK.js +1 -0
- package/dist/web/client/assets/provider-catalog-CHKFGwHr.js +1 -0
- package/dist/web/client/assets/provider-label-KkqVX1CI.js +1 -0
- package/dist/web/client/assets/range-BWOHiLNe.js +1 -0
- package/dist/web/client/assets/refresh-cw-IThTJ3j-.js +1 -0
- package/dist/web/client/assets/repl-panel-CCh4ly7J.js +1 -0
- package/dist/web/client/assets/{resource-helpers-ULlyU8qK.js → resource-helpers-Q2T0YCMO.js} +1 -1
- package/dist/web/client/assets/resources-panel-Bo8dfUdm.js +1 -0
- package/dist/web/client/assets/right-panel-context-B2VAZ2p4.js +1 -0
- package/dist/web/client/assets/right-panel-registry-C5fQcBKw.js +2 -0
- package/dist/web/client/assets/routes-C1hRkRkv.js +16 -0
- package/dist/web/client/assets/safe-external-url-D3PJJIcM.js +1 -0
- package/dist/web/client/assets/session-insights-panel-CQdT90Op.js +1 -0
- package/dist/web/client/assets/settings-dialog-CvvM6T0m.js +1 -0
- package/dist/web/client/assets/{sparkles-C5H3Jtgf.js → shield-check-CkA2viVW.js} +1 -1
- package/dist/web/client/assets/{skeleton-loaders-fStjEeRP.js → skeleton-loaders-DjGv9LLA.js} +1 -1
- package/dist/web/client/assets/sparkles-DuclzqlE.js +1 -0
- package/dist/web/client/assets/subagent-chat-panel-tGCg6gcn.js +1 -0
- package/dist/web/client/assets/subagent-transcript-NO0PR8RU.js +1 -0
- package/dist/web/client/assets/subagent-utils-CQbaJ1g-.js +1 -0
- package/dist/web/client/assets/subagents-panel-BkFLsQQY.js +1 -0
- package/dist/web/client/assets/surfaces-cW-gp56p.js +1 -0
- package/dist/web/client/assets/switch-CcOFiQBK.js +1 -0
- package/dist/web/client/assets/terminal-DOS1s1gm.js +1 -0
- package/dist/web/client/assets/toast-ATq44uAL.js +1 -0
- package/dist/web/client/assets/tool-result-pklnrxSW.js +4 -0
- package/dist/web/client/assets/use-reduced-motion-eORd7fqW.js +9 -0
- package/dist/web/client/assets/useBaseUiId-CHqXB2UQ.js +1 -0
- package/dist/web/client/assets/useControlled-CJJ9BOQB.js +1 -0
- package/dist/web/client/assets/workspace-panel-BmMW7h7b.js +1 -0
- package/dist/web/client/assets/wrench-BJNNe07k.js +1 -0
- package/dist/web/client/assets/x-CsRC_tCI.js +1 -0
- package/dist/web/client/bundle-budget.json +10 -0
- package/dist/web/server/_tanstack-start-manifest_v-B4BnRTa_-DR7WQTT5.js +90 -0
- package/dist/web/server/agent-activity-Dx6AlDhN-JXOL2EXK.js +514 -0
- package/dist/web/server/{artifacts-panel-D9w1DHAN-FGRDCYU2.js → artifacts-panel-CloS3-ij-XWYXKL6A.js} +30 -22
- package/dist/web/server/{chat-command-palette-BaenPkCN-BHSOPGU4.js → chat-command-palette-Cc0uGvNm-GKENKTO7.js} +48 -22
- package/dist/web/server/{chunk-KPNGZR2R.js → chunk-3B6M63AO.js} +206 -209
- package/dist/web/server/chunk-3D4Z32XA.js +22 -0
- package/dist/web/server/chunk-3SSHBMDS.js +196 -0
- package/dist/web/server/chunk-4PQ6OD6J.js +22 -0
- package/dist/web/server/{chunk-5AVQZ4AZ.js → chunk-4UBOIY2S.js} +2 -1
- package/dist/web/server/chunk-56SEDI5N.js +19 -0
- package/dist/web/server/chunk-6QNW66XR.js +19 -0
- package/dist/web/server/chunk-744IIIDL.js +19 -0
- package/dist/web/server/{chunk-52SJQXO4.js → chunk-7PQTPSEE.js} +568 -568
- package/dist/web/server/{chunk-XYYNYFFD.js → chunk-7SR6WDM2.js} +2 -2
- package/dist/web/server/chunk-ANL4B775.js +60 -0
- package/dist/web/server/chunk-BNQWMQHD.js +35 -0
- package/dist/web/server/{chunk-X2TUFLTP.js → chunk-C5X6G26O.js} +436 -454
- package/dist/web/server/chunk-C66BSNA6.js +42 -0
- package/dist/web/server/{chunk-3GSFXWL5.js → chunk-DEUBJJZN.js} +4 -59
- package/dist/web/server/chunk-E365EOXJ.js +35 -0
- package/dist/web/server/{chunk-5WZFFNFY.js → chunk-EFAGCVZ3.js} +2 -23
- package/dist/web/server/{chunk-Y6LJ4WBI.js → chunk-EGQ6ZX4D.js} +563 -830
- package/dist/web/server/{chunk-DO3TVNGA.js → chunk-EKBKGAWX.js} +12 -10
- package/dist/web/server/{chunk-RPROZ2JC.js → chunk-F6MGAW3L.js} +133 -133
- package/dist/web/server/chunk-GABXCOY3.js +37 -0
- package/dist/web/server/chunk-GV7DJIHY.js +36 -0
- package/dist/web/server/{chunk-PWK2HJIV.js → chunk-HSJMLO36.js} +5 -252
- package/dist/web/server/{chunk-46VQY7SG.js → chunk-HU4AIJV3.js} +4 -4
- package/dist/web/server/{chunk-H64JLONZ.js → chunk-HZY3LBGP.js} +146 -2
- package/dist/web/server/{chunk-3IKSPTYI.js → chunk-J6MRJXUH.js} +22 -15
- package/dist/web/server/{chunk-FE4KN6T2.js → chunk-JOSTE7WK.js} +7 -5
- package/dist/web/server/{chunk-QJBEWT4E.js → chunk-K3BXQG3N.js} +27 -292
- package/dist/web/server/chunk-K4ESBQ3R.js +20 -0
- package/dist/web/server/chunk-KOIPYBJF.js +3773 -0
- package/dist/web/server/{chunk-44IVTHDV.js → chunk-KZSM3BHO.js} +28 -119
- package/dist/web/server/chunk-L46H6IDX.js +13 -0
- package/dist/web/server/chunk-LNGWHEE6.js +206 -0
- package/dist/web/server/{chunk-2U7PWSJX.js → chunk-MOSVTKH5.js} +262 -3661
- package/dist/web/server/{chunk-MDYA4NXK.js → chunk-NJRYX4A2.js} +4 -4
- package/dist/web/server/chunk-NNMZQN7Q.js +296 -0
- package/dist/web/server/{chunk-VHCAD46L.js → chunk-NPPG2GTW.js} +7 -7
- package/dist/web/server/{chunk-AZZHUVJR.js → chunk-PJNYH5SR.js} +7 -303
- package/dist/web/server/{chunk-7APYEQIO.js → chunk-QAE5SXDK.js} +1 -24
- package/dist/web/server/chunk-QTFCJGOY.js +189 -0
- package/dist/web/server/{chunk-FBOO3G67.js → chunk-RANJ6OJ3.js} +9 -9
- package/dist/web/server/chunk-RPBSDXJ4.js +62 -0
- package/dist/web/server/{chunk-PCJ5EKFE.js → chunk-RYHIJMFD.js} +63 -34
- package/dist/web/server/{chunk-NLIUTTKL.js → chunk-S3PQ6HKH.js} +1 -16
- package/dist/web/server/chunk-SG4W4SPQ.js +31 -0
- package/dist/web/server/{chunk-LESNF5O4.js → chunk-T23OO4SC.js} +2 -11
- package/dist/web/server/{chunk-FVWDJNXR.js → chunk-UNZJQ7CY.js} +6 -4
- package/dist/web/server/chunk-V3S6BVXO.js +22 -0
- package/dist/web/server/{chunk-M2FGGPNA.js → chunk-VGDP4YDU.js} +70 -45
- package/dist/web/server/{chunk-WK7CQS2N.js → chunk-W5GRNHV5.js} +51 -49
- package/dist/web/server/{chunk-HBZ6YKRB.js → chunk-WN77JNYY.js} +2 -2
- package/dist/web/server/{chunk-B2F6MLVZ.js → chunk-XFVL5ITL.js} +2246 -2228
- package/dist/web/server/{chunk-EIVNRYP3.js → chunk-XKYEXPLQ.js} +2 -17
- package/dist/web/server/{chunk-6GMKRR3C.js → chunk-XNYQSCB2.js} +2 -2
- package/dist/web/server/chunk-YHXAXJ6I.js +22 -0
- package/dist/web/server/chunk-YLMXYMLG.js +86 -0
- package/dist/web/server/chunk-YNGDUWCF.js +56 -0
- package/dist/web/server/chunk-ZJFA4F5X.js +280 -0
- package/dist/web/server/fleet-message-queue-Bn0_3Efe-G4BZR2EU.js +74 -0
- package/dist/web/server/fleet-pi-tool-renderer-C52SZmPx-CIAH3IV7.js +51 -0
- package/dist/web/server/fleet-reasoning-panel-C_e7wzVm-SDHEWDQT.js +199 -0
- package/dist/web/server/fleet-subagent-list-CQltKswG-EKCMQHDI.js +18 -0
- package/dist/web/server/fleet-tool-timeline-KENErMKg-HTN7SW3D.js +16 -0
- package/dist/web/server/{fork-picker-dialog-BZzfxdQd-Y725VJ7G.js → fork-picker-dialog-DK9YGQuB-4QEFWRU4.js} +6 -5
- package/dist/web/server/{markdown-Boni3lHk-AZGKDNAR.js → markdown-QcSUaLyd-6DJUK5NC.js} +1 -1
- package/dist/web/server/{markdown-code-BrWukbg3-D2GN4Y77.js → markdown-code-Dyo1_13a-52ZQZSPE.js} +2 -2
- package/dist/web/server/model-selector-list-CEfWjRc--GBW2GUO6.js +289 -0
- package/dist/web/server/{openui-renderer-Cg4RSE9k-7BTJIYZJ.js → openui-renderer-Dk0Ak9cW-MUJQHDAS.js} +19 -16
- package/dist/web/server/prompt-suggestions-BOmHi4-J-C563CHFU.js +30 -0
- package/dist/web/server/{repl-panel-BEQrC2ef-V3YEZ7EO.js → repl-panel-D9E8nkQy-L45ZNG2V.js} +4 -3
- package/dist/web/server/{resources-panel-CwKh4U9f-D3VDEMDW.js → resources-panel-B03M8bEp-SZG726N5.js} +7 -6
- package/dist/web/server/{router-CEahIUpn-SRG7XKIH.js → router-BE9HcX2W-JMQ3JWE5.js} +1756 -1374
- package/dist/web/server/{routes-BznemByz-F4DJAYIM.js → routes-CJTRGB1Z-WJKXJFFT.js} +3748 -3955
- package/dist/web/server/server.js +3 -3
- package/dist/web/server/{session-insights-panel-JfeTHE0d-3HZB3ZWI.js → session-insights-panel-BHZyKDTo-LRMAMA3O.js} +4 -2
- package/dist/web/server/{settings-dialog-DgeBMORv-7SRSENPM.js → settings-dialog-yDibpbBH-TCLJ7NRJ.js} +172 -161
- package/dist/web/server/{start-CLAD7RcD-56S4BWMM.js → start-BYVnok_H-IA6TWGA3.js} +1 -1
- package/dist/web/server/subagent-chat-panel-B1YoZIKO-DFRUDQJ2.js +82 -0
- package/dist/web/server/subagents-panel-dJJwpsEL-FTWWCHND.js +200 -0
- package/dist/web/server/{workspace-panel-y54P79_B-VPIYUXF6.js → workspace-panel-BC_H8Jri-5MHAEQQS.js} +22 -18
- package/package.json +5 -5
- package/dist/web/client/assets/analytics-stub-Jh3lkb8P.js +0 -1
- package/dist/web/client/assets/button-0At5xSyU.js +0 -1
- package/dist/web/client/assets/chat-command-palette-C4sSf8Kg.js +0 -1
- package/dist/web/client/assets/collapsible-DmyYcezk.js +0 -1
- package/dist/web/client/assets/command-CB6emP8m.js +0 -2
- package/dist/web/client/assets/conversation-turns-ByFoWPXe.js +0 -6
- package/dist/web/client/assets/dialog-DZ44tBtH.js +0 -1
- package/dist/web/client/assets/dist-MNgIFxqY.js +0 -41
- package/dist/web/client/assets/ease-vFBiNFNK.js +0 -9
- package/dist/web/client/assets/file-text-BhQ0ZnoU.js +0 -1
- package/dist/web/client/assets/globals-CqqQdk2S.css +0 -2
- package/dist/web/client/assets/horizontal-resize-C1TpRLNC.js +0 -1
- package/dist/web/client/assets/index-DweoFRMs.js +0 -12
- package/dist/web/client/assets/input-CqC82Agz.js +0 -1
- package/dist/web/client/assets/input-group-H0TG-mkU.js +0 -1
- package/dist/web/client/assets/notify-Duj26eeZ.js +0 -1
- package/dist/web/client/assets/provider-catalog-BZhZuKSz.js +0 -1
- package/dist/web/client/assets/provider-label-jKm8v18u.js +0 -1
- package/dist/web/client/assets/range-vSJ7Ux_5.js +0 -2
- package/dist/web/client/assets/repl-panel-DiGS6uCw.js +0 -1
- package/dist/web/client/assets/resources-panel-DSU7VGX2.js +0 -1
- package/dist/web/client/assets/routes-CxlNCi_v.js +0 -16
- package/dist/web/client/assets/safe-external-url-Bl_78dpZ.js +0 -1
- package/dist/web/client/assets/session-insights-panel-DwmwiikS.js +0 -1
- package/dist/web/client/assets/settings-dialog-CkfpTKfM.js +0 -1
- package/dist/web/client/assets/subagents-panel-DGnsK6Nx.js +0 -1
- package/dist/web/client/assets/switch-BFEBpUEU.js +0 -1
- package/dist/web/client/assets/toast-C4s7FYYa.js +0 -1
- package/dist/web/client/assets/tool-result-CBzwwUE9.js +0 -4
- package/dist/web/client/assets/useBaseUiId-B9f7fokB.js +0 -1
- package/dist/web/client/assets/useControlled-CoY1jSuL.js +0 -1
- package/dist/web/client/assets/workspace-panel-CjdXlEvd.js +0 -1
- package/dist/web/client/assets/x-BgciQ7_C.js +0 -1
- package/dist/web/server/_tanstack-start-manifest_v-bDUlyKY0-KFW35JRO.js +0 -82
- package/dist/web/server/chunk-ZEVZFLIY.js +0 -334
- package/dist/web/server/fleet-pi-tool-renderer-lJvZWmBJ-4M5YI6VS.js +0 -35
- package/dist/web/server/subagents-panel-CzZe3co7-2VZ5JTWF.js +0 -332
|
@@ -0,0 +1,3773 @@
|
|
|
1
|
+
import {
|
|
2
|
+
object,
|
|
3
|
+
registry,
|
|
4
|
+
toJSONSchema
|
|
5
|
+
} from "./chunk-XFVL5ITL.js";
|
|
6
|
+
import {
|
|
7
|
+
cn
|
|
8
|
+
} from "./chunk-DJMHPJBP.js";
|
|
9
|
+
|
|
10
|
+
// web/app/dist/server/assets/src-CItS1pK4.js
|
|
11
|
+
import "react";
|
|
12
|
+
import { jsx } from "react/jsx-runtime";
|
|
13
|
+
function Card({ className, size = "default", ...props }) {
|
|
14
|
+
return /* @__PURE__ */ jsx("div", {
|
|
15
|
+
"data-slot": "card",
|
|
16
|
+
"data-size": size,
|
|
17
|
+
className: cn("group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", className),
|
|
18
|
+
...props
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function CardHeader({ className, ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsx("div", {
|
|
23
|
+
"data-slot": "card-header",
|
|
24
|
+
className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3", className),
|
|
25
|
+
...props
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function CardTitle({ className, ...props }) {
|
|
29
|
+
return /* @__PURE__ */ jsx("div", {
|
|
30
|
+
"data-slot": "card-title",
|
|
31
|
+
className: cn("font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className),
|
|
32
|
+
...props
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function CardDescription({ className, ...props }) {
|
|
36
|
+
return /* @__PURE__ */ jsx("div", {
|
|
37
|
+
"data-slot": "card-description",
|
|
38
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
39
|
+
...props
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function CardContent({ className, ...props }) {
|
|
43
|
+
return /* @__PURE__ */ jsx("div", {
|
|
44
|
+
"data-slot": "card-content",
|
|
45
|
+
className: cn("px-4 group-data-[size=sm]/card:px-3", className),
|
|
46
|
+
...props
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
var CSP_META = `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; font-src 'none'; connect-src 'none'; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; media-src data:; navigate-to 'none';">`;
|
|
50
|
+
var MAX_TITLE_BYTES = 240;
|
|
51
|
+
var FORBIDDEN_ELEMENTS = /<(?:link|iframe|object|embed|base|form|portal|frameset)\b/i;
|
|
52
|
+
var META_TAG = /<meta\b[^>]*>/gi;
|
|
53
|
+
var REFRESH_META = /\bhttp-equiv\s*=[\s"']*refresh\b/i;
|
|
54
|
+
var CSP_META_REFERENCE = /\bcontent-security-policy\b/i;
|
|
55
|
+
var EVENT_HANDLER_ATTRIBUTE = /\son[a-z][\w:-]*\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i;
|
|
56
|
+
var SCRIPT_SOURCE_ATTRIBUTE = /<script\b[^>]*\bsrc\s*=/i;
|
|
57
|
+
var CSS_IMPORT = /@import\b/i;
|
|
58
|
+
var NETWORK_API = /\b(?:fetch|XMLHttpRequest|WebSocket|EventSource|sendBeacon|showModalDialog)\s*\(|\bwindow\s*\.\s*open\s*\(|\b(?:window|globalThis|document|location)\s*\.\s*(?:location|assign|replace|reload|href|pathname|host|hostname|port|protocol|search|hash)\s*=|\b(?:window|globalThis|document|location)\s*\.\s*(?:location\s*\.\s*)?(?:assign|replace|reload)\s*\(/i;
|
|
59
|
+
var RESOURCE_ATTRIBUTE = /\b(?:src|href|action|formaction|poster|cite|background|xlink:href)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
60
|
+
var UNSAFE_SCHEME = /^(?:javascript|vbscript|file|blob|filesystem|about|data:text\/html|data:application\/javascript):/i;
|
|
61
|
+
function byteLength(value) {
|
|
62
|
+
return new TextEncoder().encode(value).byteLength;
|
|
63
|
+
}
|
|
64
|
+
function safeResourceUrl(value) {
|
|
65
|
+
const normalized = value.trim();
|
|
66
|
+
if (!normalized || UNSAFE_SCHEME.test(normalized)) return false;
|
|
67
|
+
if (normalized.startsWith("#")) return true;
|
|
68
|
+
return /^(?:data:image\/(?:gif|jpeg|jpg|png|webp|svg\+xml);|data:(?:audio|video)\/)/i.test(normalized);
|
|
69
|
+
}
|
|
70
|
+
function scanHtmlTags(document) {
|
|
71
|
+
const tags = [];
|
|
72
|
+
let index = 0;
|
|
73
|
+
let rawTextElement;
|
|
74
|
+
while (index < document.length) {
|
|
75
|
+
if (rawTextElement) {
|
|
76
|
+
const closingTag = new RegExp(`<\\/\\s*${rawTextElement}\\b[^>]*>`, "ig");
|
|
77
|
+
closingTag.lastIndex = index;
|
|
78
|
+
const closingMatch = closingTag.exec(document);
|
|
79
|
+
if (!closingMatch) break;
|
|
80
|
+
index = closingMatch.index;
|
|
81
|
+
rawTextElement = void 0;
|
|
82
|
+
}
|
|
83
|
+
const start = document.indexOf("<", index);
|
|
84
|
+
if (start < 0) break;
|
|
85
|
+
if (document.startsWith("<!--", start)) {
|
|
86
|
+
const commentEnd = document.indexOf("-->", start + 4);
|
|
87
|
+
index = commentEnd < 0 ? document.length : commentEnd + 3;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
let end = start + 1;
|
|
91
|
+
let quote;
|
|
92
|
+
for (; end < document.length; end += 1) {
|
|
93
|
+
const character = document[end];
|
|
94
|
+
if (quote) {
|
|
95
|
+
if (character === quote) quote = void 0;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (character === '"' || character === "'") {
|
|
99
|
+
quote = character;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (character === ">") break;
|
|
103
|
+
}
|
|
104
|
+
if (end >= document.length) break;
|
|
105
|
+
const raw = document.slice(start, end + 1);
|
|
106
|
+
const tagMatch = /^<\s*(\/?)\s*([A-Za-z][\w:-]*)\b/i.exec(raw);
|
|
107
|
+
if (!tagMatch) {
|
|
108
|
+
index = end + 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const closing = Boolean(tagMatch[1]);
|
|
112
|
+
const name = tagMatch[2].toLowerCase();
|
|
113
|
+
tags.push({
|
|
114
|
+
raw,
|
|
115
|
+
name,
|
|
116
|
+
closing,
|
|
117
|
+
start,
|
|
118
|
+
end: end + 1
|
|
119
|
+
});
|
|
120
|
+
if (!closing && (name === "script" || name === "style") && !/\/\s*>$/.test(raw)) rawTextElement = name;
|
|
121
|
+
index = end + 1;
|
|
122
|
+
}
|
|
123
|
+
return tags;
|
|
124
|
+
}
|
|
125
|
+
function hasHtmlAttribute(document, expectedAttribute) {
|
|
126
|
+
let inTag = false;
|
|
127
|
+
let quote;
|
|
128
|
+
let tagStart = -1;
|
|
129
|
+
for (let index = 0; index < document.length; index += 1) {
|
|
130
|
+
const character = document[index];
|
|
131
|
+
if (!inTag) {
|
|
132
|
+
if (character === "<") {
|
|
133
|
+
inTag = true;
|
|
134
|
+
tagStart = index;
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (quote) {
|
|
139
|
+
if (character === quote) quote = void 0;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (character === '"' || character === "'") {
|
|
143
|
+
quote = character;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (character !== ">") continue;
|
|
147
|
+
const tag = document.slice(tagStart, index + 1);
|
|
148
|
+
inTag = false;
|
|
149
|
+
if (/^<\s*\//.test(tag) || /^<\s*[!/?]/.test(tag)) continue;
|
|
150
|
+
const tagName = /^<\s*[A-Za-z][\w:-]*/.exec(tag);
|
|
151
|
+
if (!tagName) continue;
|
|
152
|
+
const attributes = tag.slice(tagName[0].length, -1);
|
|
153
|
+
let attributeIndex = 0;
|
|
154
|
+
while (attributeIndex < attributes.length) {
|
|
155
|
+
while (/\s/.test(attributes[attributeIndex] ?? "")) attributeIndex += 1;
|
|
156
|
+
if (attributeIndex >= attributes.length || attributes[attributeIndex] === "/") break;
|
|
157
|
+
const nameStart = attributeIndex;
|
|
158
|
+
while (!/[\s=/>]/.test(attributes[attributeIndex] ?? "")) attributeIndex += 1;
|
|
159
|
+
if (attributes.slice(nameStart, attributeIndex).toLowerCase() === expectedAttribute.toLowerCase()) return true;
|
|
160
|
+
while (/\s/.test(attributes[attributeIndex] ?? "")) attributeIndex += 1;
|
|
161
|
+
if (attributes[attributeIndex] !== "=") continue;
|
|
162
|
+
attributeIndex += 1;
|
|
163
|
+
while (/\s/.test(attributes[attributeIndex] ?? "")) attributeIndex += 1;
|
|
164
|
+
const valueQuote = attributes[attributeIndex];
|
|
165
|
+
if (valueQuote === '"' || valueQuote === "'") {
|
|
166
|
+
attributeIndex += 1;
|
|
167
|
+
while (attributeIndex < attributes.length && attributes[attributeIndex] !== valueQuote) attributeIndex += 1;
|
|
168
|
+
attributeIndex += 1;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
while (attributeIndex < attributes.length && !/[\s>]/.test(attributes[attributeIndex] ?? "")) attributeIndex += 1;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
function hasUnsafeResourceAttribute(document) {
|
|
177
|
+
RESOURCE_ATTRIBUTE.lastIndex = 0;
|
|
178
|
+
for (const match of document.matchAll(RESOURCE_ATTRIBUTE)) if (!safeResourceUrl(match[1] ?? match[2] ?? match[3] ?? "")) return true;
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
function isCssWhitespace(character) {
|
|
182
|
+
return character !== void 0 && /\s/.test(character);
|
|
183
|
+
}
|
|
184
|
+
function extractCssUrlValues(document) {
|
|
185
|
+
const values = [];
|
|
186
|
+
const lower = document.toLowerCase();
|
|
187
|
+
let searchFrom = 0;
|
|
188
|
+
while (searchFrom < document.length) {
|
|
189
|
+
const keywordStart = lower.indexOf("url", searchFrom);
|
|
190
|
+
if (keywordStart === -1) break;
|
|
191
|
+
let cursor = keywordStart + 3;
|
|
192
|
+
while (isCssWhitespace(document[cursor])) cursor += 1;
|
|
193
|
+
if (document[cursor] !== "(") {
|
|
194
|
+
searchFrom = keywordStart + 3;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
cursor += 1;
|
|
198
|
+
while (isCssWhitespace(document[cursor])) cursor += 1;
|
|
199
|
+
const contentStart = cursor;
|
|
200
|
+
const quote = document[contentStart];
|
|
201
|
+
if (quote === '"' || quote === "'") {
|
|
202
|
+
const closingQuote = document.indexOf(quote, contentStart + 1);
|
|
203
|
+
if (closingQuote !== -1) {
|
|
204
|
+
let afterQuote = closingQuote + 1;
|
|
205
|
+
while (isCssWhitespace(document[afterQuote])) afterQuote += 1;
|
|
206
|
+
if (document[afterQuote] === ")") {
|
|
207
|
+
values.push(document.slice(contentStart, closingQuote + 1));
|
|
208
|
+
searchFrom = afterQuote + 1;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const closingParen = document.indexOf(")", contentStart);
|
|
214
|
+
if (closingParen === -1) break;
|
|
215
|
+
values.push(document.slice(contentStart, closingParen));
|
|
216
|
+
searchFrom = closingParen + 1;
|
|
217
|
+
}
|
|
218
|
+
return values;
|
|
219
|
+
}
|
|
220
|
+
function hasUnsafeCssUrl(document) {
|
|
221
|
+
for (const rawValue of extractCssUrlValues(document)) {
|
|
222
|
+
let value = rawValue;
|
|
223
|
+
if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) value = value.slice(1, -1);
|
|
224
|
+
else value = value.trim();
|
|
225
|
+
if (!safeResourceUrl(value)) return true;
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
function hasForbiddenMeta(document) {
|
|
230
|
+
META_TAG.lastIndex = 0;
|
|
231
|
+
for (const match of document.matchAll(META_TAG)) {
|
|
232
|
+
const tag = match[0] ?? "";
|
|
233
|
+
if (REFRESH_META.test(tag)) return true;
|
|
234
|
+
if (CSP_META_REFERENCE.test(tag) && tag !== CSP_META) return true;
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
function hasCanonicalCspMetaInHead(document) {
|
|
239
|
+
let headDepth = 0;
|
|
240
|
+
for (const tag of scanHtmlTags(document)) {
|
|
241
|
+
if (tag.name === "head") {
|
|
242
|
+
if (tag.closing) headDepth = Math.max(0, headDepth - 1);
|
|
243
|
+
else headDepth += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (headDepth > 0 && tag.name === "meta" && !tag.closing && tag.raw === CSP_META) return true;
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
function normalizeDocument(document) {
|
|
251
|
+
const trimmed = document.trim();
|
|
252
|
+
if (hasCanonicalCspMetaInHead(trimmed)) return trimmed;
|
|
253
|
+
const tags = scanHtmlTags(trimmed);
|
|
254
|
+
const htmlTag = tags.find((tag) => tag.name === "html" && !tag.closing);
|
|
255
|
+
if (!htmlTag) return `<!doctype html><html><head>${CSP_META}</head><body>${trimmed}</body></html>`;
|
|
256
|
+
const headTag = tags.find((tag) => tag.name === "head" && !tag.closing);
|
|
257
|
+
if (headTag) return `${trimmed.slice(0, headTag.end)}${CSP_META}${trimmed.slice(headTag.end)}`;
|
|
258
|
+
return `${trimmed.slice(0, htmlTag.end)}<head>${CSP_META}</head>${trimmed.slice(htmlTag.end)}`;
|
|
259
|
+
}
|
|
260
|
+
function validateAndNormalizeOpenUIHtmlArtifact(input) {
|
|
261
|
+
if (typeof input !== "object" || input === null) return {
|
|
262
|
+
ok: false,
|
|
263
|
+
reason: "OpenUI artifact must be an object."
|
|
264
|
+
};
|
|
265
|
+
const source = input;
|
|
266
|
+
if (typeof source.title !== "string" || !source.title.trim()) return {
|
|
267
|
+
ok: false,
|
|
268
|
+
reason: "OpenUI artifact title is required."
|
|
269
|
+
};
|
|
270
|
+
if (typeof source.document !== "string" || !source.document.trim()) return {
|
|
271
|
+
ok: false,
|
|
272
|
+
reason: "OpenUI artifact document is required."
|
|
273
|
+
};
|
|
274
|
+
const title = source.title.trim();
|
|
275
|
+
const document = source.document.trim();
|
|
276
|
+
if (byteLength(title) > MAX_TITLE_BYTES) return {
|
|
277
|
+
ok: false,
|
|
278
|
+
reason: "OpenUI artifact title is too large."
|
|
279
|
+
};
|
|
280
|
+
if (byteLength(document) > 1048576) return {
|
|
281
|
+
ok: false,
|
|
282
|
+
reason: "OpenUI artifact exceeds the 1 MiB limit.",
|
|
283
|
+
status: 413
|
|
284
|
+
};
|
|
285
|
+
if (FORBIDDEN_ELEMENTS.test(document)) return {
|
|
286
|
+
ok: false,
|
|
287
|
+
reason: "External HTML resources and embedded frames are not allowed."
|
|
288
|
+
};
|
|
289
|
+
if (hasForbiddenMeta(document)) return {
|
|
290
|
+
ok: false,
|
|
291
|
+
reason: "Custom refresh and CSP metadata are not allowed."
|
|
292
|
+
};
|
|
293
|
+
if (EVENT_HANDLER_ATTRIBUTE.test(document)) return {
|
|
294
|
+
ok: false,
|
|
295
|
+
reason: "Inline event-handler attributes are not allowed."
|
|
296
|
+
};
|
|
297
|
+
if (SCRIPT_SOURCE_ATTRIBUTE.test(document)) return {
|
|
298
|
+
ok: false,
|
|
299
|
+
reason: "External scripts are not allowed."
|
|
300
|
+
};
|
|
301
|
+
if (CSS_IMPORT.test(document) || hasUnsafeCssUrl(document)) return {
|
|
302
|
+
ok: false,
|
|
303
|
+
reason: "External styles and CSS resources are not allowed."
|
|
304
|
+
};
|
|
305
|
+
if (hasUnsafeResourceAttribute(document)) return {
|
|
306
|
+
ok: false,
|
|
307
|
+
reason: "External or unsafe resource URLs are not allowed."
|
|
308
|
+
};
|
|
309
|
+
if (NETWORK_API.test(document)) return {
|
|
310
|
+
ok: false,
|
|
311
|
+
reason: "Network access and pop-up APIs are not allowed."
|
|
312
|
+
};
|
|
313
|
+
if (hasHtmlAttribute(document, "download") || hasHtmlAttribute(document, "target")) return {
|
|
314
|
+
ok: false,
|
|
315
|
+
reason: "Downloads and new-window navigation are not allowed."
|
|
316
|
+
};
|
|
317
|
+
const normalizedDocument = normalizeDocument(document);
|
|
318
|
+
if (byteLength(normalizedDocument) > 1048576) return {
|
|
319
|
+
ok: false,
|
|
320
|
+
reason: "Normalized OpenUI artifact exceeds the 1 MiB limit.",
|
|
321
|
+
status: 413
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
ok: true,
|
|
325
|
+
value: {
|
|
326
|
+
title,
|
|
327
|
+
document: normalizedDocument
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function resolveField(obj, path) {
|
|
332
|
+
if (!path || obj == null) return void 0;
|
|
333
|
+
if (!path.includes(".")) return obj[path];
|
|
334
|
+
let cur = obj;
|
|
335
|
+
for (const p of path.split(".")) {
|
|
336
|
+
if (cur == null) return void 0;
|
|
337
|
+
cur = cur[p];
|
|
338
|
+
}
|
|
339
|
+
return cur;
|
|
340
|
+
}
|
|
341
|
+
function toNumber(val) {
|
|
342
|
+
if (typeof val === "number") return val;
|
|
343
|
+
if (typeof val === "string") {
|
|
344
|
+
const n = Number(val);
|
|
345
|
+
return isNaN(n) ? 0 : n;
|
|
346
|
+
}
|
|
347
|
+
if (typeof val === "boolean") return val ? 1 : 0;
|
|
348
|
+
return 0;
|
|
349
|
+
}
|
|
350
|
+
var BUILTINS = {
|
|
351
|
+
Count: {
|
|
352
|
+
name: "Count",
|
|
353
|
+
signature: "Count(array) \u2192 number",
|
|
354
|
+
description: "Returns array length",
|
|
355
|
+
fn: (arr) => Array.isArray(arr) ? arr.length : 0
|
|
356
|
+
},
|
|
357
|
+
First: {
|
|
358
|
+
name: "First",
|
|
359
|
+
signature: "First(array) \u2192 element",
|
|
360
|
+
description: "Returns first element of array",
|
|
361
|
+
fn: (arr) => Array.isArray(arr) ? arr[0] ?? null : null
|
|
362
|
+
},
|
|
363
|
+
Last: {
|
|
364
|
+
name: "Last",
|
|
365
|
+
signature: "Last(array) \u2192 element",
|
|
366
|
+
description: "Returns last element of array",
|
|
367
|
+
fn: (arr) => Array.isArray(arr) ? arr[arr.length - 1] ?? null : null
|
|
368
|
+
},
|
|
369
|
+
Sum: {
|
|
370
|
+
name: "Sum",
|
|
371
|
+
signature: "Sum(numbers[]) \u2192 number",
|
|
372
|
+
description: "Sum of numeric array",
|
|
373
|
+
fn: (arr) => Array.isArray(arr) ? arr.reduce((a, b) => a + toNumber(b), 0) : 0
|
|
374
|
+
},
|
|
375
|
+
Avg: {
|
|
376
|
+
name: "Avg",
|
|
377
|
+
signature: "Avg(numbers[]) \u2192 number",
|
|
378
|
+
description: "Average of numeric array",
|
|
379
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((a, b) => a + toNumber(b), 0) / arr.length : 0
|
|
380
|
+
},
|
|
381
|
+
Min: {
|
|
382
|
+
name: "Min",
|
|
383
|
+
signature: "Min(numbers[]) \u2192 number",
|
|
384
|
+
description: "Minimum value in array",
|
|
385
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.min(acc, toNumber(b)), toNumber(arr[0])) : 0
|
|
386
|
+
},
|
|
387
|
+
Max: {
|
|
388
|
+
name: "Max",
|
|
389
|
+
signature: "Max(numbers[]) \u2192 number",
|
|
390
|
+
description: "Maximum value in array",
|
|
391
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.max(acc, toNumber(b)), toNumber(arr[0])) : 0
|
|
392
|
+
},
|
|
393
|
+
Sort: {
|
|
394
|
+
name: "Sort",
|
|
395
|
+
signature: "Sort(array, field, direction?) \u2192 sorted array",
|
|
396
|
+
description: 'Sort array by field. Direction: "asc" (default) or "desc"',
|
|
397
|
+
fn: (arr, field, dir) => {
|
|
398
|
+
if (!Array.isArray(arr)) return arr;
|
|
399
|
+
const f = String(field ?? "");
|
|
400
|
+
const desc = String(dir ?? "asc") === "desc";
|
|
401
|
+
return [...arr].sort((a, b) => {
|
|
402
|
+
const av = f ? resolveField(a, f) : a;
|
|
403
|
+
const bv = f ? resolveField(b, f) : b;
|
|
404
|
+
const aIsNumeric = typeof av === "number" || typeof av === "string" && !isNaN(Number(av)) && av !== "";
|
|
405
|
+
const bIsNumeric = typeof bv === "number" || typeof bv === "string" && !isNaN(Number(bv)) && bv !== "";
|
|
406
|
+
if (aIsNumeric && bIsNumeric) {
|
|
407
|
+
const diff = toNumber(av) - toNumber(bv);
|
|
408
|
+
return desc ? -diff : diff;
|
|
409
|
+
}
|
|
410
|
+
const cmp = String(av ?? "").localeCompare(String(bv ?? ""));
|
|
411
|
+
return desc ? -cmp : cmp;
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
Filter: {
|
|
416
|
+
name: "Filter",
|
|
417
|
+
signature: 'Filter(array, field, operator: "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains", value) \u2192 filtered array',
|
|
418
|
+
description: "Filter array by field value",
|
|
419
|
+
fn: (arr, field, op, value) => {
|
|
420
|
+
if (!Array.isArray(arr)) return [];
|
|
421
|
+
const f = String(field ?? "");
|
|
422
|
+
const o = String(op ?? "==");
|
|
423
|
+
return arr.filter((item) => {
|
|
424
|
+
const v = f ? resolveField(item, f) : item;
|
|
425
|
+
switch (o) {
|
|
426
|
+
case "==":
|
|
427
|
+
return v == value;
|
|
428
|
+
case "!=":
|
|
429
|
+
return v != value;
|
|
430
|
+
case ">":
|
|
431
|
+
return toNumber(v) > toNumber(value);
|
|
432
|
+
case "<":
|
|
433
|
+
return toNumber(v) < toNumber(value);
|
|
434
|
+
case ">=":
|
|
435
|
+
return toNumber(v) >= toNumber(value);
|
|
436
|
+
case "<=":
|
|
437
|
+
return toNumber(v) <= toNumber(value);
|
|
438
|
+
case "contains":
|
|
439
|
+
return String(v ?? "").includes(String(value ?? ""));
|
|
440
|
+
default:
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
Round: {
|
|
447
|
+
name: "Round",
|
|
448
|
+
signature: "Round(number, decimals?) \u2192 number",
|
|
449
|
+
description: "Round to N decimal places (default 0)",
|
|
450
|
+
fn: (n, decimals) => {
|
|
451
|
+
const num = toNumber(n);
|
|
452
|
+
const d = decimals != null ? toNumber(decimals) : 0;
|
|
453
|
+
const factor = Math.pow(10, d);
|
|
454
|
+
return Math.round(num * factor) / factor;
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
Abs: {
|
|
458
|
+
name: "Abs",
|
|
459
|
+
signature: "Abs(number) \u2192 number",
|
|
460
|
+
description: "Absolute value",
|
|
461
|
+
fn: (n) => Math.abs(toNumber(n))
|
|
462
|
+
},
|
|
463
|
+
Floor: {
|
|
464
|
+
name: "Floor",
|
|
465
|
+
signature: "Floor(number) \u2192 number",
|
|
466
|
+
description: "Round down to nearest integer",
|
|
467
|
+
fn: (n) => Math.floor(toNumber(n))
|
|
468
|
+
},
|
|
469
|
+
Ceil: {
|
|
470
|
+
name: "Ceil",
|
|
471
|
+
signature: "Ceil(number) \u2192 number",
|
|
472
|
+
description: "Round up to nearest integer",
|
|
473
|
+
fn: (n) => Math.ceil(toNumber(n))
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
var LAZY_BUILTINS = /* @__PURE__ */ new Set(["Each"]);
|
|
477
|
+
var LAZY_BUILTIN_DEFS = { Each: {
|
|
478
|
+
signature: "Each(array, varName, template)",
|
|
479
|
+
description: "Evaluate template for each element. varName is the loop variable \u2014 use it ONLY inside the template expression (inline). Do NOT create a separate statement for the template."
|
|
480
|
+
} };
|
|
481
|
+
var ACTION_STEPS = {
|
|
482
|
+
Run: "run",
|
|
483
|
+
ToAssistant: "continue_conversation",
|
|
484
|
+
OpenUrl: "open_url",
|
|
485
|
+
Set: "set",
|
|
486
|
+
Reset: "reset"
|
|
487
|
+
};
|
|
488
|
+
var ACTION_NAMES = /* @__PURE__ */ new Set(["Action", ...Object.keys(ACTION_STEPS)]);
|
|
489
|
+
var BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
490
|
+
...Object.keys(BUILTINS),
|
|
491
|
+
...LAZY_BUILTINS,
|
|
492
|
+
...ACTION_NAMES
|
|
493
|
+
]);
|
|
494
|
+
function isBuiltin(name) {
|
|
495
|
+
return BUILTIN_NAMES.has(name);
|
|
496
|
+
}
|
|
497
|
+
var RESERVED_CALLS = {
|
|
498
|
+
Query: "Query",
|
|
499
|
+
Mutation: "Mutation"
|
|
500
|
+
};
|
|
501
|
+
function isReservedCall(name) {
|
|
502
|
+
return name in RESERVED_CALLS;
|
|
503
|
+
}
|
|
504
|
+
function jsonSchemaTypeStr(schema) {
|
|
505
|
+
const type = schema.type;
|
|
506
|
+
if (type === "string") {
|
|
507
|
+
const enumVals = schema.enum;
|
|
508
|
+
if (enumVals) return enumVals.map((v) => `"${v}"`).join(" | ");
|
|
509
|
+
return "string";
|
|
510
|
+
}
|
|
511
|
+
if (type === "number" || type === "integer") return "number";
|
|
512
|
+
if (type === "boolean") return "boolean";
|
|
513
|
+
if (type === "array") {
|
|
514
|
+
const items = schema.items;
|
|
515
|
+
if (items) return `${jsonSchemaTypeStr(items)}[]`;
|
|
516
|
+
return "any[]";
|
|
517
|
+
}
|
|
518
|
+
if (type === "object") {
|
|
519
|
+
const props = schema.properties;
|
|
520
|
+
if (props && Object.keys(props).length > 0) {
|
|
521
|
+
const required = schema.required ?? [];
|
|
522
|
+
return `{${Object.entries(props).map(([k, v]) => {
|
|
523
|
+
return `${k}${required.includes(k) ? "" : "?"}: ${jsonSchemaTypeStr(v)}`;
|
|
524
|
+
}).join(", ")}}`;
|
|
525
|
+
}
|
|
526
|
+
return "object";
|
|
527
|
+
}
|
|
528
|
+
return "any";
|
|
529
|
+
}
|
|
530
|
+
function defaultForSchema(schema) {
|
|
531
|
+
const type = schema.type;
|
|
532
|
+
if (type === "string") return "";
|
|
533
|
+
if (type === "number" || type === "integer") return 0;
|
|
534
|
+
if (type === "boolean") return false;
|
|
535
|
+
if (type === "array") return [];
|
|
536
|
+
if (type === "object") {
|
|
537
|
+
const props = schema.properties;
|
|
538
|
+
if (props && Object.keys(props).length > 0) {
|
|
539
|
+
const result = {};
|
|
540
|
+
for (const [k, v] of Object.entries(props)) result[k] = defaultForSchema(v);
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
return {};
|
|
544
|
+
}
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
var PREAMBLE = `You are an AI assistant that responds using openui-lang, a declarative UI language. Your ENTIRE response must be valid openui-lang code \u2014 no markdown, no explanations, just openui-lang.`;
|
|
548
|
+
function syntaxRules(rootName, flags) {
|
|
549
|
+
const lines = [
|
|
550
|
+
"## Syntax Rules",
|
|
551
|
+
"",
|
|
552
|
+
"1. Each statement is on its own line: `identifier = Expression`",
|
|
553
|
+
`2. \`root\` is the entry point \u2014 every program must define \`root = ${rootName}(...)\``,
|
|
554
|
+
'3. Expressions are: strings ("..."), numbers, booleans (true/false), null, arrays ([...]), objects ({...}), or component calls TypeName(arg1, arg2, ...)',
|
|
555
|
+
"4. Use references for readability: define `name = ...` on one line, then use `name` later",
|
|
556
|
+
"5. EVERY variable (except root) MUST be referenced by at least one other variable. Unreferenced variables are silently dropped and will NOT render. Always include defined variables in their parent's children/items array.",
|
|
557
|
+
'6. Arguments are POSITIONAL (order matters, not names). Write `Stack([children], "row", "l")` NOT `Stack([children], direction: "row", gap: "l")` \u2014 colon syntax is NOT supported and silently breaks',
|
|
558
|
+
"7. Optional arguments can be omitted from the end"
|
|
559
|
+
];
|
|
560
|
+
let ruleNum = 8;
|
|
561
|
+
if (flags.bindings) lines.push(`${ruleNum++}. Declare mutable state with \`$varName = defaultValue\`. Components marked with \`$binding\` can read/write these. Undeclared $variables are auto-created with null default.`);
|
|
562
|
+
if (flags.supportsExpressions) lines.push(`${ruleNum++}. String concatenation: \`"text" + $var + "more"\``, `${ruleNum++}. Dot member access: \`query.field\` reads a field; on arrays it extracts that field from every element`, `${ruleNum++}. Index access: \`arr[0]\`, \`data[index]\``, `${ruleNum++}. Arithmetic operators: +, -, *, /, % (work on numbers; + is string concat when either side is a string)`, `${ruleNum++}. Comparison: ==, !=, >, <, >=, <=`, `${ruleNum++}. Logical: &&, ||, ! (prefix)`, `${ruleNum++}. Ternary: \`condition ? valueIfTrue : valueIfFalse\``, `${ruleNum++}. Parentheses for grouping: \`(a + b) * c\``);
|
|
563
|
+
lines.push("- Strings use double quotes with backslash escaping");
|
|
564
|
+
return lines.join("\n");
|
|
565
|
+
}
|
|
566
|
+
function builtinFunctionsSection() {
|
|
567
|
+
const builtinLines = Object.values(BUILTINS).map((b) => `@${b.signature} \u2014 ${b.description}`);
|
|
568
|
+
const lazyLines = Object.values(LAZY_BUILTIN_DEFS).map((b) => `@${b.signature} \u2014 ${b.description}`);
|
|
569
|
+
return `## Built-in Functions
|
|
570
|
+
|
|
571
|
+
Data functions prefixed with \`@\` to distinguish from components. These are the ONLY functions available \u2014 do NOT invent new ones.
|
|
572
|
+
Use @-prefixed built-in functions (@Count, @Sum, @Avg, @Min, @Max, @Round) on Query results \u2014 do NOT hardcode computed values.
|
|
573
|
+
|
|
574
|
+
${[...builtinLines, ...lazyLines].join("\n")}
|
|
575
|
+
|
|
576
|
+
Builtins compose \u2014 output of one is input to the next:
|
|
577
|
+
\`@Count(@Filter(data.rows, "field", "==", "val"))\` for KPIs/chart values, \`@Round(@Avg(data.rows.score), 1)\`, \`@Each(data.rows, "item", Comp(item.field))\` for per-item rendering.
|
|
578
|
+
Array pluck: \`data.rows.field\` extracts a field from every row \u2192 use with @Sum, @Avg, charts, tables.
|
|
579
|
+
|
|
580
|
+
IMPORTANT @Each rule: The loop variable (e.g. "item") is ONLY available inside the @Each template expression. Always inline the template \u2014 do NOT extract it to a separate statement.
|
|
581
|
+
CORRECT: \`Col("Actions", @Each(rows, "t", Button("Edit", Action([@Set($id, t.id)]))))\`
|
|
582
|
+
WRONG: \`myBtn = Button("Edit", Action([@Set($id, t.id)]))\` then \`Col("Actions", @Each(rows, "t", myBtn))\` \u2014 t is undefined in myBtn.`;
|
|
583
|
+
}
|
|
584
|
+
function querySection() {
|
|
585
|
+
return `## Query \u2014 Live Data Fetching
|
|
586
|
+
|
|
587
|
+
Fetch data from available tools. Returns defaults instantly, swaps in real data when it arrives.
|
|
588
|
+
|
|
589
|
+
\`\`\`
|
|
590
|
+
metrics = Query("tool_name", {arg1: value, arg2: $binding}, {defaultField: 0, defaultData: []}, refreshInterval?)
|
|
591
|
+
\`\`\`
|
|
592
|
+
|
|
593
|
+
- First arg: tool name (string)
|
|
594
|
+
- Second arg: arguments object (may reference $bindings \u2014 re-fetches automatically on change)
|
|
595
|
+
- Third arg: default data (rendered immediately before fetch resolves)
|
|
596
|
+
- Fourth arg (optional): refresh interval in seconds (e.g. 30 for auto-refresh every 30s)
|
|
597
|
+
- Use dot access on results: metrics.totalEvents, metrics.data.day (array pluck)
|
|
598
|
+
- Query results must use regular identifiers: \`metrics = Query(...)\`, NOT \`$metrics = Query(...)\`
|
|
599
|
+
- Manual refresh: \`Button("Refresh", Action([@Run(query1), @Run(query2)]), "secondary")\` \u2014 re-fetches the listed queries
|
|
600
|
+
- Refresh all queries: create Action with @Run for each query`;
|
|
601
|
+
}
|
|
602
|
+
function mutationSection() {
|
|
603
|
+
return `## Mutation \u2014 Write Operations
|
|
604
|
+
|
|
605
|
+
Execute state-changing tool calls (create, update, delete). Unlike Query (auto-fetches on render), Mutation fires only on button click via Action.
|
|
606
|
+
|
|
607
|
+
\`\`\`
|
|
608
|
+
result = Mutation("tool_name", {arg1: $binding, arg2: "value"})
|
|
609
|
+
\`\`\`
|
|
610
|
+
|
|
611
|
+
- First arg: tool name (string)
|
|
612
|
+
- Second arg: arguments object (evaluated with current $binding values at click time)
|
|
613
|
+
- result.status: "idle" | "loading" | "success" | "error"
|
|
614
|
+
- result.data: tool response on success
|
|
615
|
+
- result.error: error message on failure
|
|
616
|
+
- Mutation results use regular identifiers: \`result = Mutation(...)\`, NOT \`$result\`
|
|
617
|
+
- Show loading state: \`result.status == "loading" ? TextContent("Saving...") : null\``;
|
|
618
|
+
}
|
|
619
|
+
function actionSection(flags) {
|
|
620
|
+
const steps = ['- @ToAssistant("message") \u2014 Send a message to the assistant (for conversational buttons like "Tell me more", "Explain this")', '- @OpenUrl("https://...") \u2014 Navigate to a URL'];
|
|
621
|
+
if (flags.bindings) steps.push("- @Set($variable, value) \u2014 Set a $variable to a specific value", '- @Reset($var1, $var2, ...) \u2014 Reset $variables to their declared defaults (e.g. @Reset($title, $priority) restores $title="" and $priority="medium")');
|
|
622
|
+
if (flags.toolCalls) steps.unshift("- @Run(queryOrMutationRef) \u2014 Execute a Mutation or re-fetch a Query (ref must be a declared Query/Mutation)");
|
|
623
|
+
const examples = [];
|
|
624
|
+
if (flags.toolCalls) examples.push(`Example \u2014 mutation + refresh + reset (PREFERRED pattern):
|
|
625
|
+
\`\`\`
|
|
626
|
+
$binding = "default"
|
|
627
|
+
result = Mutation("tool_name", {field: $binding})
|
|
628
|
+
data = Query("tool_name", {}, {rows: []})
|
|
629
|
+
onSubmit = Action([@Run(result), @Run(data), @Reset($binding)])
|
|
630
|
+
\`\`\``);
|
|
631
|
+
examples.push(`Example \u2014 simple nav:
|
|
632
|
+
\`\`\`
|
|
633
|
+
viewBtn = Button("View", Action([@OpenUrl("https://example.com")]))
|
|
634
|
+
\`\`\``);
|
|
635
|
+
const rules = ['- Action can be assigned to a variable or inlined: Button("Go", onSubmit) and Button("Go", Action([...])) both work'];
|
|
636
|
+
if (flags.toolCalls) rules.push("- If a @Run(mutation) step fails, remaining steps are skipped (halt on failure)", "- @Run(queryRef) re-fetches the query (fire-and-forget, cannot fail)");
|
|
637
|
+
return `## Action \u2014 Button Behavior
|
|
638
|
+
|
|
639
|
+
Action([@steps...]) wires button clicks to operations. Steps are @-prefixed built-in actions. Steps execute in order.
|
|
640
|
+
Buttons without an explicit Action prop automatically send their label to the assistant (equivalent to Action([@ToAssistant(label)])).
|
|
641
|
+
|
|
642
|
+
Available steps:
|
|
643
|
+
${steps.join("\n")}
|
|
644
|
+
|
|
645
|
+
${examples.join("\n\n")}
|
|
646
|
+
|
|
647
|
+
${rules.join("\n")}`;
|
|
648
|
+
}
|
|
649
|
+
function interactiveFiltersSection() {
|
|
650
|
+
return `## Interactive Filters
|
|
651
|
+
|
|
652
|
+
To let the user filter data with a dropdown:
|
|
653
|
+
1. Declare a $variable with a default: \`$dateRange = "14"\`
|
|
654
|
+
2. Create a Select with name, items, and binding: \`Select("dateRange", [SelectItem("7", "Last 7 days"), ...], null, null, $dateRange)\`
|
|
655
|
+
3. Wrap in FormControl for a label: \`FormControl("Date Range", Select(...))\`
|
|
656
|
+
4. Pass $dateRange in Query args: \`Query("tool", {dateRange: $dateRange}, {defaults})\`
|
|
657
|
+
5. When the user changes the Select, $dateRange updates and the Query automatically re-fetches
|
|
658
|
+
|
|
659
|
+
FILTER WIRING RULE: If a $binding filter is visible in the UI, EVERY relevant Query MUST reference that $binding in its args. Never show a filter dropdown while hardcoding the query args.
|
|
660
|
+
|
|
661
|
+
Rules for $variables:
|
|
662
|
+
- $variables hold simple values (strings or numbers), NOT arrays or objects
|
|
663
|
+
- $variables must be bound to a Select/Input component via the value argument (last positional arg) to be interactive
|
|
664
|
+
- Queries must use regular identifiers (NOT $variables): \`metrics = Query(...)\` not \`$metrics = Query(...)\`
|
|
665
|
+
- **Auto-declare**: You do NOT need to explicitly declare $variables. If you use \`$foo\` without declaring it, the parser auto-creates \`$foo = null\`. You can still declare explicitly to set a default: \`$days = "14"\`
|
|
666
|
+
|
|
667
|
+
## Forms
|
|
668
|
+
|
|
669
|
+
Simple form \u2014 no $bindings needed. Field values are managed internally by the Form via the name prop:
|
|
670
|
+
\`\`\`
|
|
671
|
+
contactForm = Form("contact", submitBtn, [nameField, emailField])
|
|
672
|
+
nameField = FormControl("Name", Input("name", "Your name", "text", {required: true}))
|
|
673
|
+
emailField = FormControl("Email", Input("email", "your@email.com", "email", {required: true, email: true}))
|
|
674
|
+
submitBtn = Button("Submit")
|
|
675
|
+
\`\`\`
|
|
676
|
+
|
|
677
|
+
Use $bindings when you need to read field values elsewhere (in Action context, Query args, or conditionals). They are auto-declared:
|
|
678
|
+
\`\`\`
|
|
679
|
+
$role = "engineer"
|
|
680
|
+
contactForm = Form("contact", submitBtn, [nameField, emailField, roleField])
|
|
681
|
+
nameField = FormControl("Name", Input("name", "Enter your name", "text", {required: true}, $name))
|
|
682
|
+
emailField = FormControl("Email", Input("email", "Enter your email", "email", {required: true, email: true}, $email))
|
|
683
|
+
roleField = FormControl("Role", Select("role", [SelectItem("engineer", "Engineer"), SelectItem("designer", "Designer"), SelectItem("pm", "PM")], null, {required: true}, $role))
|
|
684
|
+
submitBtn = Button("Submit")
|
|
685
|
+
\`\`\`
|
|
686
|
+
|
|
687
|
+
For form + mutation patterns (create, refresh, reset), see the Action section example above.
|
|
688
|
+
|
|
689
|
+
IMPORTANT: Always add validation rules to form fields used with Mutations. Use OBJECT syntax: {required: true, email: true, minLength: 8}. The renderer shows error messages automatically and blocks submit when validation fails.`;
|
|
690
|
+
}
|
|
691
|
+
function editModeSection() {
|
|
692
|
+
return `## Edit Mode
|
|
693
|
+
|
|
694
|
+
The runtime merges by statement name: same name = replace, new name = append.
|
|
695
|
+
Output ONLY statements that changed or are new. Everything else is kept automatically.
|
|
696
|
+
|
|
697
|
+
### Delete
|
|
698
|
+
To remove a component, update the parent to exclude it from its children array. Orphaned statements are automatically garbage-collected.
|
|
699
|
+
Example \u2014 remove chart: \`root = Stack([header, kpiRow, table])\` \u2014 chart is no longer in the children list, so it and any statements only it referenced are auto-deleted.
|
|
700
|
+
|
|
701
|
+
### Patch size guide
|
|
702
|
+
- Changing a title or label: 1 statement
|
|
703
|
+
- Adding a component: 2-3 statements (the new component + parent update)
|
|
704
|
+
- Removing a component: 1 statement (re-declare parent without the removed child)
|
|
705
|
+
- Adding a filter + wiring to query: 3-5 statements
|
|
706
|
+
- Restructuring into tabs: 5-10 statements
|
|
707
|
+
|
|
708
|
+
### Rules
|
|
709
|
+
- Reuse existing statement names exactly \u2014 do not rename
|
|
710
|
+
- Do NOT re-emit unchanged statements \u2014 the runtime keeps them
|
|
711
|
+
- A typical edit patch is 1-10 statements, not 20+
|
|
712
|
+
- If the existing code already satisfies the request, output only the root statement
|
|
713
|
+
- NEVER output the entire program as a patch. Only output what actually changes
|
|
714
|
+
- If you are about to output more than 10 statements, reconsider \u2014 most edits need fewer`;
|
|
715
|
+
}
|
|
716
|
+
function streamingRules(rootName, flags) {
|
|
717
|
+
const steps = [`1. \`root = ${rootName}(...)\` \u2014 UI shell appears immediately`];
|
|
718
|
+
if (flags.supportsExpressions) {
|
|
719
|
+
steps.push("2. $variable declarations \u2014 state ready for bindings");
|
|
720
|
+
steps.push("3. Query statements \u2014 defaults resolve immediately so components render with data");
|
|
721
|
+
steps.push("4. Component definitions \u2014 fill in with data already available");
|
|
722
|
+
steps.push("5. Data values \u2014 leaf content last");
|
|
723
|
+
} else {
|
|
724
|
+
steps.push("2. Component definitions \u2014 fill in as they stream");
|
|
725
|
+
steps.push("3. Data values \u2014 leaf content last");
|
|
726
|
+
}
|
|
727
|
+
return `## Hoisting & Streaming (CRITICAL)
|
|
728
|
+
|
|
729
|
+
openui-lang supports hoisting: a reference can be used BEFORE it is defined. The parser resolves all references after the full input is parsed.
|
|
730
|
+
|
|
731
|
+
During streaming, the output is re-parsed on every chunk. Undefined references are temporarily unresolved and appear once their definitions stream in. This creates a progressive top-down reveal \u2014 structure first, then data fills in.
|
|
732
|
+
|
|
733
|
+
**Recommended statement order for optimal streaming:**
|
|
734
|
+
${steps.join("\n")}
|
|
735
|
+
|
|
736
|
+
Always write the root = ${rootName}(...) statement first so the UI shell appears immediately, even before child data has streamed in.`;
|
|
737
|
+
}
|
|
738
|
+
function inlineModeSection() {
|
|
739
|
+
return `## Inline Mode
|
|
740
|
+
|
|
741
|
+
You are in inline mode. You can respond in two ways:
|
|
742
|
+
|
|
743
|
+
### 1. Code response (when the user wants to CREATE or CHANGE the UI)
|
|
744
|
+
Wrap openui-lang code in triple-backtick fences. You can include explanatory text before/after:
|
|
745
|
+
|
|
746
|
+
Here's your dashboard:
|
|
747
|
+
|
|
748
|
+
\`\`\`openui-lang
|
|
749
|
+
root = RootComp([header, content])
|
|
750
|
+
header = SomeHeader("Title")
|
|
751
|
+
content = SomeContent("Hello world")
|
|
752
|
+
\`\`\`
|
|
753
|
+
|
|
754
|
+
I created a simple layout with a header.
|
|
755
|
+
|
|
756
|
+
### 2. Text-only response (when the user asks a QUESTION)
|
|
757
|
+
If the user asks "what is this?", "explain the chart", "how does this work", etc. \u2014 respond with plain text. Do NOT output any openui-lang code. The existing dashboard stays unchanged.
|
|
758
|
+
|
|
759
|
+
### Rules
|
|
760
|
+
- When the user asks for changes, output ONLY the changed/new statements in a fenced block
|
|
761
|
+
- When the user asks a question, respond with text only \u2014 NO code. The dashboard stays unchanged.
|
|
762
|
+
- The parser extracts code from fences automatically. Text outside fences is shown as chat.`;
|
|
763
|
+
}
|
|
764
|
+
function toolWorkflowSection() {
|
|
765
|
+
return `## Data Workflow
|
|
766
|
+
|
|
767
|
+
When tools are available, follow this workflow:
|
|
768
|
+
1. FIRST: Call the most relevant tool to inspect the real data shape before generating code
|
|
769
|
+
2. Use Query() for READ operations (data that should stay live) \u2014 NEVER hardcode tool results as literal arrays or objects
|
|
770
|
+
3. Use Mutation() for WRITE operations (create, update, delete) \u2014 triggered by button clicks via Action([@Run(mutationRef)])
|
|
771
|
+
4. Use the real data from step 1 as condensed Query defaults (3-5 rows) so the UI renders immediately
|
|
772
|
+
5. Use @-prefixed builtins (@Count, @Filter, @Sort, @Sum) on Query results for KPIs and aggregations \u2014 the runtime evaluates these live on every refresh
|
|
773
|
+
6. Hardcoded arrays are ONLY for static display data (labels, options) where no tool exists
|
|
774
|
+
|
|
775
|
+
WRONG \u2014 you called a tool and got data back, but you inlined the results:
|
|
776
|
+
\`\`\`
|
|
777
|
+
openCount = 2
|
|
778
|
+
item1 = SomeComp("first item title")
|
|
779
|
+
item2 = SomeComp("second item title")
|
|
780
|
+
list = Stack([item1, item2])
|
|
781
|
+
chart = SomeChart(["A", "B"], [12, 8])
|
|
782
|
+
\`\`\`
|
|
783
|
+
This is static \u2014 it shows stale data and won't update. Creating item1, item2, item3... manually is ALWAYS wrong when a tool exists.
|
|
784
|
+
|
|
785
|
+
RIGHT \u2014 use Query() for live data, Mutation() for writes, @builtins to derive values:
|
|
786
|
+
\`\`\`
|
|
787
|
+
data = Query("tool_name", {}, {rows: []})
|
|
788
|
+
openCount = @Count(@Filter(data.rows, "field", "==", "value"))
|
|
789
|
+
list = @Each(data.rows, "item", SomeComp(item.title, item.field))
|
|
790
|
+
createResult = Mutation("create_tool", {title: $title})
|
|
791
|
+
submitBtn = Button("Create", Action([@Run(createResult), @Run(data), @Reset($title)]))
|
|
792
|
+
\`\`\`
|
|
793
|
+
Everything derives from the Query \u2014 when data refreshes, the entire dashboard updates automatically.`;
|
|
794
|
+
}
|
|
795
|
+
function importantRules(rootName, flags) {
|
|
796
|
+
const verifyLines = [`1. root = ${rootName}(...) is the FIRST line (for optimal streaming).`, "2. Every referenced name is defined. Every defined name (other than root) is reachable from root."];
|
|
797
|
+
if (flags.toolCalls) verifyLines.push("3. Every Query result is referenced by at least one component.");
|
|
798
|
+
if (flags.bindings) verifyLines.push(`${flags.toolCalls ? "4" : "3"}. Every $binding appears in at least one component or expression.`);
|
|
799
|
+
if (flags.toolCalls && flags.bindings) verifyLines.push("5. Every visible filter $binding appears in at least one Query args object.");
|
|
800
|
+
return `## Important Rules
|
|
801
|
+
- When asked about data, generate realistic/plausible data
|
|
802
|
+
- Choose components that best represent the content (tables for comparisons, charts for trends, forms for input, etc.)
|
|
803
|
+
|
|
804
|
+
## Final Verification
|
|
805
|
+
Before finishing, walk your output and verify:
|
|
806
|
+
${verifyLines.join("\n")}`;
|
|
807
|
+
}
|
|
808
|
+
function renderToolSignature(tool) {
|
|
809
|
+
let args = "";
|
|
810
|
+
if (tool.inputSchema) {
|
|
811
|
+
const props = tool.inputSchema.properties;
|
|
812
|
+
const required = tool.inputSchema.required ?? [];
|
|
813
|
+
if (props && Object.keys(props).length > 0) args = Object.entries(props).map(([k, v]) => {
|
|
814
|
+
return `${k}${required.includes(k) ? "" : "?"}: ${jsonSchemaTypeStr(v)}`;
|
|
815
|
+
}).join(", ");
|
|
816
|
+
}
|
|
817
|
+
let returnType = "";
|
|
818
|
+
if (tool.outputSchema) returnType = ` \u2192 ${jsonSchemaTypeStr(tool.outputSchema)}`;
|
|
819
|
+
let line = `- ${tool.name}(${args})${returnType}`;
|
|
820
|
+
if (tool.description) line += `
|
|
821
|
+
${tool.description}`;
|
|
822
|
+
return line;
|
|
823
|
+
}
|
|
824
|
+
function renderToolsSection(tools) {
|
|
825
|
+
const lines = [];
|
|
826
|
+
const stringTools = [];
|
|
827
|
+
const specTools = [];
|
|
828
|
+
for (const tool of tools) if (typeof tool === "string") stringTools.push(tool);
|
|
829
|
+
else specTools.push(tool);
|
|
830
|
+
lines.push("## Available Tools");
|
|
831
|
+
lines.push("");
|
|
832
|
+
lines.push("Use these with Query() for read operations or Mutation() for write operations. The LLM decides which is appropriate based on the tool's purpose.");
|
|
833
|
+
lines.push("");
|
|
834
|
+
for (const t of stringTools) lines.push(`- ${t}`);
|
|
835
|
+
for (const t of specTools) lines.push(renderToolSignature(t));
|
|
836
|
+
const toolsWithOutput = specTools.filter((t) => t.outputSchema);
|
|
837
|
+
if (toolsWithOutput.length > 0) {
|
|
838
|
+
lines.push("");
|
|
839
|
+
lines.push("### Default values for Query results");
|
|
840
|
+
lines.push("");
|
|
841
|
+
lines.push("Use these shapes as minimal Query defaults:");
|
|
842
|
+
for (const t of toolsWithOutput) {
|
|
843
|
+
const defaults = defaultForSchema(t.outputSchema);
|
|
844
|
+
lines.push(`- ${t.name}: \`${JSON.stringify(defaults)}\``);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
lines.push("");
|
|
848
|
+
lines.push("CRITICAL: Use ONLY the tools listed above in Query() and Mutation() calls. Do NOT invent or guess tool names. If the user asks for functionality that doesn't match any available tool, use realistic mock data instead of fabricating a tool call.");
|
|
849
|
+
return lines.join("\n");
|
|
850
|
+
}
|
|
851
|
+
function generateComponentSignatures(spec, flags) {
|
|
852
|
+
const lines = [
|
|
853
|
+
"## Component Signatures",
|
|
854
|
+
"",
|
|
855
|
+
"Arguments marked with ? are optional. Sub-components can be inline or referenced; prefer references for better streaming."
|
|
856
|
+
];
|
|
857
|
+
if (flags.usesActionExpression) {
|
|
858
|
+
const allSteps = [
|
|
859
|
+
flags.toolCalls ? "@Run" : "",
|
|
860
|
+
"@ToAssistant",
|
|
861
|
+
"@OpenUrl",
|
|
862
|
+
flags.bindings ? "@Set" : "",
|
|
863
|
+
flags.bindings ? "@Reset" : ""
|
|
864
|
+
].filter(Boolean);
|
|
865
|
+
lines.push(`Props typed \`ActionExpression\` accept an Action([@steps...]) expression. See the Action section for available steps (${allSteps.join(", ")}).`);
|
|
866
|
+
}
|
|
867
|
+
if (flags.bindings || Object.values(spec.components).some((c) => c.signature?.includes("$binding"))) lines.push("Props marked `$binding<type>` accept a `$variable` reference for two-way binding.");
|
|
868
|
+
const formatSig = (comp) => comp.description ? `${comp.signature} \u2014 ${comp.description}` : comp.signature;
|
|
869
|
+
if (spec.componentGroups?.length) {
|
|
870
|
+
const grouped = /* @__PURE__ */ new Set();
|
|
871
|
+
for (const group of spec.componentGroups) {
|
|
872
|
+
lines.push("", `### ${group.name}`);
|
|
873
|
+
for (const name of group.components) {
|
|
874
|
+
if (grouped.has(name)) continue;
|
|
875
|
+
const comp = spec.components[name];
|
|
876
|
+
if (!comp) continue;
|
|
877
|
+
grouped.add(name);
|
|
878
|
+
lines.push(formatSig(comp));
|
|
879
|
+
}
|
|
880
|
+
if (group.notes?.length) for (const note of group.notes) lines.push(note);
|
|
881
|
+
}
|
|
882
|
+
const ungrouped = Object.keys(spec.components).filter((n) => !grouped.has(n));
|
|
883
|
+
if (ungrouped.length) {
|
|
884
|
+
lines.push("", "### Other");
|
|
885
|
+
for (const name of ungrouped) {
|
|
886
|
+
const comp = spec.components[name];
|
|
887
|
+
lines.push(formatSig(comp));
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
} else {
|
|
891
|
+
lines.push("");
|
|
892
|
+
for (const [, comp] of Object.entries(spec.components)) lines.push(formatSig(comp));
|
|
893
|
+
}
|
|
894
|
+
return lines.join("\n");
|
|
895
|
+
}
|
|
896
|
+
function generatePrompt(spec) {
|
|
897
|
+
const rootName = spec.root ?? "Root";
|
|
898
|
+
const hasTools = !!spec.tools?.length;
|
|
899
|
+
const toolCalls = spec.toolCalls ?? hasTools;
|
|
900
|
+
const bindings = spec.bindings ?? toolCalls;
|
|
901
|
+
const supportsExpressions = toolCalls || bindings;
|
|
902
|
+
const usesActionExpression = Object.values(spec.components).some((c) => c.signature?.includes("ActionExpression"));
|
|
903
|
+
const parts = [];
|
|
904
|
+
parts.push(spec.preamble ?? PREAMBLE);
|
|
905
|
+
parts.push("");
|
|
906
|
+
parts.push(syntaxRules(rootName, {
|
|
907
|
+
supportsExpressions,
|
|
908
|
+
bindings
|
|
909
|
+
}));
|
|
910
|
+
parts.push("");
|
|
911
|
+
parts.push(generateComponentSignatures(spec, {
|
|
912
|
+
toolCalls,
|
|
913
|
+
bindings,
|
|
914
|
+
usesActionExpression
|
|
915
|
+
}));
|
|
916
|
+
if (supportsExpressions) {
|
|
917
|
+
parts.push("");
|
|
918
|
+
parts.push(builtinFunctionsSection());
|
|
919
|
+
}
|
|
920
|
+
if (toolCalls) {
|
|
921
|
+
parts.push("");
|
|
922
|
+
parts.push(querySection());
|
|
923
|
+
parts.push("");
|
|
924
|
+
parts.push(mutationSection());
|
|
925
|
+
}
|
|
926
|
+
if (usesActionExpression) {
|
|
927
|
+
parts.push("");
|
|
928
|
+
parts.push(actionSection({
|
|
929
|
+
toolCalls,
|
|
930
|
+
bindings
|
|
931
|
+
}));
|
|
932
|
+
}
|
|
933
|
+
if (toolCalls && bindings) {
|
|
934
|
+
parts.push("");
|
|
935
|
+
parts.push(interactiveFiltersSection());
|
|
936
|
+
}
|
|
937
|
+
if (toolCalls) {
|
|
938
|
+
parts.push("");
|
|
939
|
+
parts.push(toolWorkflowSection());
|
|
940
|
+
}
|
|
941
|
+
if (spec.tools?.length) {
|
|
942
|
+
parts.push("");
|
|
943
|
+
parts.push(renderToolsSection(spec.tools));
|
|
944
|
+
}
|
|
945
|
+
parts.push("");
|
|
946
|
+
parts.push(streamingRules(rootName, { supportsExpressions }));
|
|
947
|
+
const allExamples = [...spec.examples ?? [], ...spec.toolExamples ?? []];
|
|
948
|
+
if (allExamples.length) {
|
|
949
|
+
parts.push("");
|
|
950
|
+
parts.push("## Examples");
|
|
951
|
+
parts.push("");
|
|
952
|
+
for (const ex of allExamples) {
|
|
953
|
+
parts.push(ex);
|
|
954
|
+
parts.push("");
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (spec.editMode) {
|
|
958
|
+
parts.push("");
|
|
959
|
+
parts.push(editModeSection());
|
|
960
|
+
}
|
|
961
|
+
if (spec.inlineMode) {
|
|
962
|
+
parts.push("");
|
|
963
|
+
parts.push(inlineModeSection());
|
|
964
|
+
}
|
|
965
|
+
parts.push(importantRules(rootName, {
|
|
966
|
+
toolCalls,
|
|
967
|
+
bindings
|
|
968
|
+
}));
|
|
969
|
+
if (spec.additionalRules?.length) {
|
|
970
|
+
parts.push("");
|
|
971
|
+
for (const rule of spec.additionalRules) parts.push(`- ${rule}`);
|
|
972
|
+
}
|
|
973
|
+
return parts.join("\n");
|
|
974
|
+
}
|
|
975
|
+
var reactiveSchemas = /* @__PURE__ */ new WeakSet();
|
|
976
|
+
function markReactive(schema) {
|
|
977
|
+
reactiveSchemas.add(schema);
|
|
978
|
+
}
|
|
979
|
+
function isReactiveSchema(schema) {
|
|
980
|
+
return typeof schema === "object" && schema !== null && reactiveSchemas.has(schema);
|
|
981
|
+
}
|
|
982
|
+
var schemaIdTags = /* @__PURE__ */ new WeakMap();
|
|
983
|
+
function tagSchemaId(schema, id) {
|
|
984
|
+
schemaIdTags.set(schema, id);
|
|
985
|
+
}
|
|
986
|
+
function assertV4Schema(schema, componentName) {
|
|
987
|
+
if (schema != null && typeof schema === "object" && "_def" in schema && !("_zod" in schema)) throw new Error(`[OpenUI] Component "${componentName}" was defined with a Zod 3 schema. OpenUI requires Zod 4 schemas. If you're on zod@3.25+, import from "zod/v4" instead of "zod". See: https://zod.dev/v4/versioning`);
|
|
988
|
+
}
|
|
989
|
+
function defineComponent(config) {
|
|
990
|
+
assertV4Schema(config.props, config.name);
|
|
991
|
+
schemaIdTags.set(config.props, config.name);
|
|
992
|
+
return {
|
|
993
|
+
...config,
|
|
994
|
+
ref: config.props
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
function getZodDef(schema) {
|
|
998
|
+
return schema?._zod?.def;
|
|
999
|
+
}
|
|
1000
|
+
function getZodType(schema) {
|
|
1001
|
+
return getZodDef(schema)?.type;
|
|
1002
|
+
}
|
|
1003
|
+
function isOptionalType(schema) {
|
|
1004
|
+
const type = getZodType(schema);
|
|
1005
|
+
return type === "optional" || type === "default" || type === "nullable";
|
|
1006
|
+
}
|
|
1007
|
+
function unwrap(schema) {
|
|
1008
|
+
let s = schema;
|
|
1009
|
+
let def = getZodDef(s);
|
|
1010
|
+
while (def?.type === "optional" || def?.type === "default" || def?.type === "nullable") {
|
|
1011
|
+
s = def.innerType;
|
|
1012
|
+
def = getZodDef(s);
|
|
1013
|
+
}
|
|
1014
|
+
return s;
|
|
1015
|
+
}
|
|
1016
|
+
function isArrayType(schema) {
|
|
1017
|
+
return getZodType(unwrap(schema)) === "array";
|
|
1018
|
+
}
|
|
1019
|
+
function getArrayInnerType(schema) {
|
|
1020
|
+
const def = getZodDef(unwrap(schema));
|
|
1021
|
+
if (def?.type === "array") return def.element ?? def.innerType;
|
|
1022
|
+
}
|
|
1023
|
+
function getEnumValues(schema) {
|
|
1024
|
+
const def = getZodDef(unwrap(schema));
|
|
1025
|
+
if (def?.type !== "enum") return void 0;
|
|
1026
|
+
if (Array.isArray(def.values)) return def.values;
|
|
1027
|
+
if (def.entries && typeof def.entries === "object") return Object.keys(def.entries);
|
|
1028
|
+
}
|
|
1029
|
+
function getSchemaId(schema, reg) {
|
|
1030
|
+
try {
|
|
1031
|
+
const meta = reg.get(schema);
|
|
1032
|
+
if (meta?.id) return meta.id;
|
|
1033
|
+
} catch {
|
|
1034
|
+
}
|
|
1035
|
+
if (typeof schema === "object" && schema !== null) return schemaIdTags.get(schema);
|
|
1036
|
+
}
|
|
1037
|
+
function getUnionOptions(schema) {
|
|
1038
|
+
const def = getZodDef(schema);
|
|
1039
|
+
if (def?.type === "union" && Array.isArray(def.options)) return def.options;
|
|
1040
|
+
}
|
|
1041
|
+
function getObjectShape(schema) {
|
|
1042
|
+
const def = getZodDef(schema);
|
|
1043
|
+
if (def?.type === "object" && def.shape && typeof def.shape === "object") return def.shape;
|
|
1044
|
+
}
|
|
1045
|
+
function resolveTypeAnnotation(schema, reg) {
|
|
1046
|
+
const isReactive = isReactiveSchema(schema);
|
|
1047
|
+
const baseType = resolveBaseType(unwrap(schema), reg);
|
|
1048
|
+
if (!baseType) return void 0;
|
|
1049
|
+
return isReactive ? `$binding<${baseType}>` : baseType;
|
|
1050
|
+
}
|
|
1051
|
+
function resolveBaseType(inner, reg) {
|
|
1052
|
+
const directId = getSchemaId(inner, reg);
|
|
1053
|
+
if (directId) return directId;
|
|
1054
|
+
const unionOpts = getUnionOptions(inner);
|
|
1055
|
+
if (unionOpts) {
|
|
1056
|
+
const names = unionOpts.map((o) => resolveTypeAnnotation(o, reg)).filter(Boolean);
|
|
1057
|
+
if (names.length > 0) return names.join(" | ");
|
|
1058
|
+
}
|
|
1059
|
+
if (isArrayType(inner)) {
|
|
1060
|
+
const arrayInner = getArrayInnerType(inner);
|
|
1061
|
+
if (!arrayInner) return void 0;
|
|
1062
|
+
const innerType = resolveTypeAnnotation(arrayInner, reg);
|
|
1063
|
+
if (innerType) return getUnionOptions(unwrap(arrayInner)) !== void 0 ? `(${innerType})[]` : `${innerType}[]`;
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const zodType = getZodType(inner);
|
|
1067
|
+
if (zodType === "string") return "string";
|
|
1068
|
+
if (zodType === "number") return "number";
|
|
1069
|
+
if (zodType === "boolean") return "boolean";
|
|
1070
|
+
if (zodType === "any") return "any";
|
|
1071
|
+
if (zodType === "record") {
|
|
1072
|
+
const def = getZodDef(inner);
|
|
1073
|
+
return `Record<${resolveTypeAnnotation(def?.keyType, reg) ?? "string"}, ${resolveTypeAnnotation(def?.valueType, reg) ?? "any"}>`;
|
|
1074
|
+
}
|
|
1075
|
+
const enumVals = getEnumValues(inner);
|
|
1076
|
+
if (enumVals) return enumVals.map((v) => `"${v}"`).join(" | ");
|
|
1077
|
+
if (zodType === "literal") {
|
|
1078
|
+
const vals = getZodDef(inner)?.values;
|
|
1079
|
+
if (Array.isArray(vals) && vals.length === 1) {
|
|
1080
|
+
const v = vals[0];
|
|
1081
|
+
return typeof v === "string" ? `"${v}"` : String(v);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
const shape = getObjectShape(inner);
|
|
1085
|
+
if (shape) return `{${Object.entries(shape).map(([name, fieldSchema]) => {
|
|
1086
|
+
const opt = isOptionalType(fieldSchema) ? "?" : "";
|
|
1087
|
+
const fieldType = resolveTypeAnnotation(fieldSchema, reg);
|
|
1088
|
+
return fieldType ? `${name}${opt}: ${fieldType}` : `${name}${opt}`;
|
|
1089
|
+
}).join(", ")}}`;
|
|
1090
|
+
return "any";
|
|
1091
|
+
}
|
|
1092
|
+
function analyzeFields(shape, reg) {
|
|
1093
|
+
return Object.entries(shape).map(([name, schema]) => ({
|
|
1094
|
+
name,
|
|
1095
|
+
isOptional: isOptionalType(schema),
|
|
1096
|
+
isArray: isArrayType(schema),
|
|
1097
|
+
typeAnnotation: resolveTypeAnnotation(schema, reg)
|
|
1098
|
+
}));
|
|
1099
|
+
}
|
|
1100
|
+
function buildSignature(componentName, fields) {
|
|
1101
|
+
return `${componentName}(${fields.map((f) => {
|
|
1102
|
+
if (f.typeAnnotation) return f.isOptional ? `${f.name}?: ${f.typeAnnotation}` : `${f.name}: ${f.typeAnnotation}`;
|
|
1103
|
+
if (f.isArray) return f.isOptional ? `[${f.name}]?` : `[${f.name}]`;
|
|
1104
|
+
return f.isOptional ? `${f.name}?` : f.name;
|
|
1105
|
+
}).join(", ")})`;
|
|
1106
|
+
}
|
|
1107
|
+
function buildComponentSpecs(components, reg) {
|
|
1108
|
+
const specs = {};
|
|
1109
|
+
for (const [name, def] of Object.entries(components)) specs[name] = {
|
|
1110
|
+
signature: buildSignature(name, analyzeFields(def.props.shape, reg)),
|
|
1111
|
+
description: def.description
|
|
1112
|
+
};
|
|
1113
|
+
return specs;
|
|
1114
|
+
}
|
|
1115
|
+
var fallbackLibraryId = 0;
|
|
1116
|
+
function createLibraryId() {
|
|
1117
|
+
if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
|
|
1118
|
+
fallbackLibraryId += 1;
|
|
1119
|
+
return `openui-lib-${Date.now().toString(36)}-${fallbackLibraryId.toString(36)}`;
|
|
1120
|
+
}
|
|
1121
|
+
function createLibrary(input) {
|
|
1122
|
+
const componentsRecord = {};
|
|
1123
|
+
const reg = registry();
|
|
1124
|
+
const __libraryId = createLibraryId();
|
|
1125
|
+
for (const comp of input.components) {
|
|
1126
|
+
reg.add(comp.props, { id: comp.name });
|
|
1127
|
+
componentsRecord[comp.name] = comp;
|
|
1128
|
+
}
|
|
1129
|
+
if (input.root && !componentsRecord[input.root]) {
|
|
1130
|
+
const available = Object.keys(componentsRecord).join(", ");
|
|
1131
|
+
throw new Error(`[createLibrary] Root component "${input.root}" was not found in components. Available components: ${available}`);
|
|
1132
|
+
}
|
|
1133
|
+
const library = {
|
|
1134
|
+
components: componentsRecord,
|
|
1135
|
+
componentGroups: input.componentGroups,
|
|
1136
|
+
root: input.root,
|
|
1137
|
+
id: input.id,
|
|
1138
|
+
__libraryId,
|
|
1139
|
+
prompt(options) {
|
|
1140
|
+
return generatePrompt({
|
|
1141
|
+
root: input.root,
|
|
1142
|
+
components: buildComponentSpecs(componentsRecord, reg),
|
|
1143
|
+
componentGroups: input.componentGroups,
|
|
1144
|
+
...options
|
|
1145
|
+
});
|
|
1146
|
+
},
|
|
1147
|
+
toSpec() {
|
|
1148
|
+
return {
|
|
1149
|
+
root: input.root,
|
|
1150
|
+
components: buildComponentSpecs(componentsRecord, reg),
|
|
1151
|
+
componentGroups: input.componentGroups,
|
|
1152
|
+
schema: buildJSONSchema(),
|
|
1153
|
+
...input.id !== void 0 ? { id: input.id } : {}
|
|
1154
|
+
};
|
|
1155
|
+
},
|
|
1156
|
+
toJSONSchema() {
|
|
1157
|
+
return buildJSONSchema();
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
function buildJSONSchema() {
|
|
1161
|
+
const combinedSchema = object(Object.fromEntries(Object.entries(componentsRecord).map(([k, v]) => [k, v.props])));
|
|
1162
|
+
const schema = toJSONSchema(combinedSchema, { metadata: reg });
|
|
1163
|
+
for (const [name, comp] of Object.entries(componentsRecord)) {
|
|
1164
|
+
const def = schema.$defs?.[name];
|
|
1165
|
+
if (def && comp.description) def.description = comp.description;
|
|
1166
|
+
}
|
|
1167
|
+
return schema;
|
|
1168
|
+
}
|
|
1169
|
+
return library;
|
|
1170
|
+
}
|
|
1171
|
+
function isElementNode(value) {
|
|
1172
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1173
|
+
const node = value;
|
|
1174
|
+
return node.type === "element" && typeof node.typeName === "string" && typeof node.props === "object" && node.props !== null && typeof node.partial === "boolean";
|
|
1175
|
+
}
|
|
1176
|
+
var BuiltinActionType = /* @__PURE__ */ (function(BuiltinActionType2) {
|
|
1177
|
+
BuiltinActionType2["ContinueConversation"] = "continue_conversation";
|
|
1178
|
+
BuiltinActionType2["OpenUrl"] = "open_url";
|
|
1179
|
+
return BuiltinActionType2;
|
|
1180
|
+
})({});
|
|
1181
|
+
function isRuntimeExpr(node) {
|
|
1182
|
+
switch (node.k) {
|
|
1183
|
+
case "StateRef":
|
|
1184
|
+
case "RuntimeRef":
|
|
1185
|
+
case "BinOp":
|
|
1186
|
+
case "UnaryOp":
|
|
1187
|
+
case "Ternary":
|
|
1188
|
+
case "Member":
|
|
1189
|
+
case "Index":
|
|
1190
|
+
case "Assign":
|
|
1191
|
+
return true;
|
|
1192
|
+
default:
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
var AST_KINDS = /* @__PURE__ */ new Set([
|
|
1197
|
+
"Comp",
|
|
1198
|
+
"Ref",
|
|
1199
|
+
"StateRef",
|
|
1200
|
+
"RuntimeRef",
|
|
1201
|
+
"BinOp",
|
|
1202
|
+
"UnaryOp",
|
|
1203
|
+
"Ternary",
|
|
1204
|
+
"Member",
|
|
1205
|
+
"Index",
|
|
1206
|
+
"Assign",
|
|
1207
|
+
"Str",
|
|
1208
|
+
"Num",
|
|
1209
|
+
"Bool",
|
|
1210
|
+
"Null",
|
|
1211
|
+
"Arr",
|
|
1212
|
+
"Obj",
|
|
1213
|
+
"Ph"
|
|
1214
|
+
]);
|
|
1215
|
+
function isASTNode(value) {
|
|
1216
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1217
|
+
return AST_KINDS.has(value.k);
|
|
1218
|
+
}
|
|
1219
|
+
function walkAST(node, visit) {
|
|
1220
|
+
const walk = (current) => {
|
|
1221
|
+
visit(current);
|
|
1222
|
+
switch (current.k) {
|
|
1223
|
+
case "Comp":
|
|
1224
|
+
current.args.forEach(walk);
|
|
1225
|
+
Object.values(current.mappedProps ?? {}).forEach(walk);
|
|
1226
|
+
break;
|
|
1227
|
+
case "Arr":
|
|
1228
|
+
current.els.forEach(walk);
|
|
1229
|
+
break;
|
|
1230
|
+
case "Obj":
|
|
1231
|
+
current.entries.forEach(([, value]) => walk(value));
|
|
1232
|
+
break;
|
|
1233
|
+
case "BinOp":
|
|
1234
|
+
walk(current.left);
|
|
1235
|
+
walk(current.right);
|
|
1236
|
+
break;
|
|
1237
|
+
case "UnaryOp":
|
|
1238
|
+
walk(current.operand);
|
|
1239
|
+
break;
|
|
1240
|
+
case "Ternary":
|
|
1241
|
+
walk(current.cond);
|
|
1242
|
+
walk(current.then);
|
|
1243
|
+
walk(current.else);
|
|
1244
|
+
break;
|
|
1245
|
+
case "Member":
|
|
1246
|
+
walk(current.obj);
|
|
1247
|
+
break;
|
|
1248
|
+
case "Index":
|
|
1249
|
+
walk(current.obj);
|
|
1250
|
+
walk(current.index);
|
|
1251
|
+
break;
|
|
1252
|
+
case "Assign":
|
|
1253
|
+
walk(current.value);
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
walk(node);
|
|
1257
|
+
}
|
|
1258
|
+
var PREC_TERNARY = 1;
|
|
1259
|
+
var PREC_OR = 2;
|
|
1260
|
+
var PREC_AND = 3;
|
|
1261
|
+
var PREC_EQ = 4;
|
|
1262
|
+
var PREC_CMP = 5;
|
|
1263
|
+
var PREC_ADD = 6;
|
|
1264
|
+
var PREC_MUL = 7;
|
|
1265
|
+
var PREC_UNARY = 8;
|
|
1266
|
+
var PREC_MEMBER = 9;
|
|
1267
|
+
function parseExpression(tokens) {
|
|
1268
|
+
let pos = 0;
|
|
1269
|
+
const cur = () => tokens[pos] ?? { t: 13 };
|
|
1270
|
+
const adv = () => {
|
|
1271
|
+
const tok = cur();
|
|
1272
|
+
pos++;
|
|
1273
|
+
return tok;
|
|
1274
|
+
};
|
|
1275
|
+
const eat = (kind) => {
|
|
1276
|
+
if (cur().t === kind) pos++;
|
|
1277
|
+
};
|
|
1278
|
+
function getInfixPrec(tok) {
|
|
1279
|
+
switch (tok.t) {
|
|
1280
|
+
case 34:
|
|
1281
|
+
return PREC_TERNARY;
|
|
1282
|
+
case 32:
|
|
1283
|
+
return PREC_OR;
|
|
1284
|
+
case 31:
|
|
1285
|
+
return PREC_AND;
|
|
1286
|
+
case 25:
|
|
1287
|
+
case 26:
|
|
1288
|
+
return PREC_EQ;
|
|
1289
|
+
case 27:
|
|
1290
|
+
case 28:
|
|
1291
|
+
case 29:
|
|
1292
|
+
case 30:
|
|
1293
|
+
return PREC_CMP;
|
|
1294
|
+
case 20:
|
|
1295
|
+
case 21:
|
|
1296
|
+
return PREC_ADD;
|
|
1297
|
+
case 22:
|
|
1298
|
+
case 23:
|
|
1299
|
+
case 24:
|
|
1300
|
+
return PREC_MUL;
|
|
1301
|
+
case 19:
|
|
1302
|
+
case 3:
|
|
1303
|
+
return PREC_MEMBER;
|
|
1304
|
+
default:
|
|
1305
|
+
return 0;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
function parseExpr(minPrec = 0) {
|
|
1309
|
+
let left = parsePrefix();
|
|
1310
|
+
while (getInfixPrec(cur()) > minPrec) left = parseInfix(left);
|
|
1311
|
+
return left;
|
|
1312
|
+
}
|
|
1313
|
+
function parsePrefix() {
|
|
1314
|
+
const tok = cur();
|
|
1315
|
+
if (tok.t === 14) {
|
|
1316
|
+
adv();
|
|
1317
|
+
return {
|
|
1318
|
+
k: "Str",
|
|
1319
|
+
v: tok.v
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
if (tok.t === 15) {
|
|
1323
|
+
adv();
|
|
1324
|
+
return {
|
|
1325
|
+
k: "Num",
|
|
1326
|
+
v: tok.v
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
if (tok.t === 10) {
|
|
1330
|
+
adv();
|
|
1331
|
+
return {
|
|
1332
|
+
k: "Bool",
|
|
1333
|
+
v: true
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
if (tok.t === 11) {
|
|
1337
|
+
adv();
|
|
1338
|
+
return {
|
|
1339
|
+
k: "Bool",
|
|
1340
|
+
v: false
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
if (tok.t === 12) {
|
|
1344
|
+
adv();
|
|
1345
|
+
return { k: "Null" };
|
|
1346
|
+
}
|
|
1347
|
+
if (tok.t === 3) return parseArr();
|
|
1348
|
+
if (tok.t === 5) return parseObj();
|
|
1349
|
+
if (tok.t === 18) {
|
|
1350
|
+
const name = tok.v;
|
|
1351
|
+
adv();
|
|
1352
|
+
if (cur().t === 9) {
|
|
1353
|
+
adv();
|
|
1354
|
+
return {
|
|
1355
|
+
k: "Assign",
|
|
1356
|
+
target: name,
|
|
1357
|
+
value: parseExpr(0)
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
k: "StateRef",
|
|
1362
|
+
n: name
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
if (tok.t === 17) {
|
|
1366
|
+
const name = tok.v;
|
|
1367
|
+
if (tokens[pos + 1]?.t === 1 && (!isBuiltin(name) || name === "Action")) return parseComp();
|
|
1368
|
+
adv();
|
|
1369
|
+
return {
|
|
1370
|
+
k: "Ref",
|
|
1371
|
+
n: name
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
if (tok.t === 35) {
|
|
1375
|
+
if (tokens[pos + 1]?.t === 1) return parseComp();
|
|
1376
|
+
adv();
|
|
1377
|
+
return {
|
|
1378
|
+
k: "Ref",
|
|
1379
|
+
n: tok.v
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
if (tok.t === 16) {
|
|
1383
|
+
adv();
|
|
1384
|
+
return {
|
|
1385
|
+
k: "Ref",
|
|
1386
|
+
n: tok.v
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
if (tok.t === 33) {
|
|
1390
|
+
adv();
|
|
1391
|
+
return {
|
|
1392
|
+
k: "UnaryOp",
|
|
1393
|
+
op: "!",
|
|
1394
|
+
operand: parseExpr(PREC_UNARY)
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
if (tok.t === 21) {
|
|
1398
|
+
adv();
|
|
1399
|
+
return {
|
|
1400
|
+
k: "UnaryOp",
|
|
1401
|
+
op: "-",
|
|
1402
|
+
operand: parseExpr(PREC_UNARY)
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
if (tok.t === 1) {
|
|
1406
|
+
adv();
|
|
1407
|
+
const inner = parseExpr(0);
|
|
1408
|
+
eat(2);
|
|
1409
|
+
return inner;
|
|
1410
|
+
}
|
|
1411
|
+
adv();
|
|
1412
|
+
return { k: "Null" };
|
|
1413
|
+
}
|
|
1414
|
+
function parseInfix(left) {
|
|
1415
|
+
const tok = cur();
|
|
1416
|
+
if (tok.t === 20) {
|
|
1417
|
+
adv();
|
|
1418
|
+
return {
|
|
1419
|
+
k: "BinOp",
|
|
1420
|
+
op: "+",
|
|
1421
|
+
left,
|
|
1422
|
+
right: parseExpr(PREC_ADD)
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
if (tok.t === 21) {
|
|
1426
|
+
adv();
|
|
1427
|
+
return {
|
|
1428
|
+
k: "BinOp",
|
|
1429
|
+
op: "-",
|
|
1430
|
+
left,
|
|
1431
|
+
right: parseExpr(PREC_ADD)
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
if (tok.t === 22) {
|
|
1435
|
+
adv();
|
|
1436
|
+
return {
|
|
1437
|
+
k: "BinOp",
|
|
1438
|
+
op: "*",
|
|
1439
|
+
left,
|
|
1440
|
+
right: parseExpr(PREC_MUL)
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
if (tok.t === 23) {
|
|
1444
|
+
adv();
|
|
1445
|
+
return {
|
|
1446
|
+
k: "BinOp",
|
|
1447
|
+
op: "/",
|
|
1448
|
+
left,
|
|
1449
|
+
right: parseExpr(PREC_MUL)
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
if (tok.t === 24) {
|
|
1453
|
+
adv();
|
|
1454
|
+
return {
|
|
1455
|
+
k: "BinOp",
|
|
1456
|
+
op: "%",
|
|
1457
|
+
left,
|
|
1458
|
+
right: parseExpr(PREC_MUL)
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
if (tok.t === 25) {
|
|
1462
|
+
adv();
|
|
1463
|
+
return {
|
|
1464
|
+
k: "BinOp",
|
|
1465
|
+
op: "==",
|
|
1466
|
+
left,
|
|
1467
|
+
right: parseExpr(PREC_EQ)
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
if (tok.t === 26) {
|
|
1471
|
+
adv();
|
|
1472
|
+
return {
|
|
1473
|
+
k: "BinOp",
|
|
1474
|
+
op: "!=",
|
|
1475
|
+
left,
|
|
1476
|
+
right: parseExpr(PREC_EQ)
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
if (tok.t === 27) {
|
|
1480
|
+
adv();
|
|
1481
|
+
return {
|
|
1482
|
+
k: "BinOp",
|
|
1483
|
+
op: ">",
|
|
1484
|
+
left,
|
|
1485
|
+
right: parseExpr(PREC_CMP)
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
if (tok.t === 28) {
|
|
1489
|
+
adv();
|
|
1490
|
+
return {
|
|
1491
|
+
k: "BinOp",
|
|
1492
|
+
op: "<",
|
|
1493
|
+
left,
|
|
1494
|
+
right: parseExpr(PREC_CMP)
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
if (tok.t === 29) {
|
|
1498
|
+
adv();
|
|
1499
|
+
return {
|
|
1500
|
+
k: "BinOp",
|
|
1501
|
+
op: ">=",
|
|
1502
|
+
left,
|
|
1503
|
+
right: parseExpr(PREC_CMP)
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
if (tok.t === 30) {
|
|
1507
|
+
adv();
|
|
1508
|
+
return {
|
|
1509
|
+
k: "BinOp",
|
|
1510
|
+
op: "<=",
|
|
1511
|
+
left,
|
|
1512
|
+
right: parseExpr(PREC_CMP)
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
if (tok.t === 31) {
|
|
1516
|
+
adv();
|
|
1517
|
+
return {
|
|
1518
|
+
k: "BinOp",
|
|
1519
|
+
op: "&&",
|
|
1520
|
+
left,
|
|
1521
|
+
right: parseExpr(PREC_AND)
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
if (tok.t === 32) {
|
|
1525
|
+
adv();
|
|
1526
|
+
return {
|
|
1527
|
+
k: "BinOp",
|
|
1528
|
+
op: "||",
|
|
1529
|
+
left,
|
|
1530
|
+
right: parseExpr(PREC_OR)
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
if (tok.t === 34) {
|
|
1534
|
+
adv();
|
|
1535
|
+
const then = parseExpr(0);
|
|
1536
|
+
eat(8);
|
|
1537
|
+
return {
|
|
1538
|
+
k: "Ternary",
|
|
1539
|
+
cond: left,
|
|
1540
|
+
then,
|
|
1541
|
+
else: parseExpr(0)
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
if (tok.t === 19) {
|
|
1545
|
+
adv();
|
|
1546
|
+
const fieldTok = cur();
|
|
1547
|
+
return {
|
|
1548
|
+
k: "Member",
|
|
1549
|
+
obj: left,
|
|
1550
|
+
field: fieldTok.t === 16 || fieldTok.t === 17 || fieldTok.t === 14 || fieldTok.t === 15 ? (adv(), String(fieldTok.v)) : fieldTok.t === 18 ? (adv(), fieldTok.v.replace(/^\$/, "")) : (adv(), "?")
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
if (tok.t === 3) {
|
|
1554
|
+
adv();
|
|
1555
|
+
const index = parseExpr(0);
|
|
1556
|
+
eat(4);
|
|
1557
|
+
return {
|
|
1558
|
+
k: "Index",
|
|
1559
|
+
obj: left,
|
|
1560
|
+
index
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
return left;
|
|
1564
|
+
}
|
|
1565
|
+
function parseComp() {
|
|
1566
|
+
const name = cur().v;
|
|
1567
|
+
adv();
|
|
1568
|
+
eat(1);
|
|
1569
|
+
const args = [];
|
|
1570
|
+
while (cur().t !== 2 && cur().t !== 13) {
|
|
1571
|
+
args.push(parseExpr(0));
|
|
1572
|
+
if (cur().t === 7) adv();
|
|
1573
|
+
}
|
|
1574
|
+
eat(2);
|
|
1575
|
+
return {
|
|
1576
|
+
k: "Comp",
|
|
1577
|
+
name,
|
|
1578
|
+
args
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
function parseArr() {
|
|
1582
|
+
adv();
|
|
1583
|
+
const els = [];
|
|
1584
|
+
while (cur().t !== 4 && cur().t !== 13) {
|
|
1585
|
+
els.push(parseExpr(0));
|
|
1586
|
+
if (cur().t === 7) adv();
|
|
1587
|
+
}
|
|
1588
|
+
eat(4);
|
|
1589
|
+
return {
|
|
1590
|
+
k: "Arr",
|
|
1591
|
+
els
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
function parseObj() {
|
|
1595
|
+
adv();
|
|
1596
|
+
const entries = [];
|
|
1597
|
+
while (cur().t !== 6 && cur().t !== 13) {
|
|
1598
|
+
const kt = cur();
|
|
1599
|
+
const key = kt.t === 16 || kt.t === 14 || kt.t === 17 || kt.t === 15 ? (adv(), String(kt.v)) : kt.t === 18 ? (adv(), kt.v.replace(/^\$/, "")) : (adv(), "?");
|
|
1600
|
+
eat(8);
|
|
1601
|
+
entries.push([key, parseExpr(0)]);
|
|
1602
|
+
if (cur().t === 7) adv();
|
|
1603
|
+
}
|
|
1604
|
+
eat(6);
|
|
1605
|
+
return {
|
|
1606
|
+
k: "Obj",
|
|
1607
|
+
entries
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
return parseExpr(0);
|
|
1611
|
+
}
|
|
1612
|
+
function tokenize(src) {
|
|
1613
|
+
const tokens = [];
|
|
1614
|
+
let i = 0;
|
|
1615
|
+
const n = src.length;
|
|
1616
|
+
while (i < n) {
|
|
1617
|
+
while (i < n && (src[i] === " " || src[i] === " " || src[i] === "\r")) i++;
|
|
1618
|
+
if (i >= n) break;
|
|
1619
|
+
const c = src[i];
|
|
1620
|
+
if (c === "\n") {
|
|
1621
|
+
tokens.push({ t: 0 });
|
|
1622
|
+
i++;
|
|
1623
|
+
continue;
|
|
1624
|
+
}
|
|
1625
|
+
if (c === "(") {
|
|
1626
|
+
tokens.push({ t: 1 });
|
|
1627
|
+
i++;
|
|
1628
|
+
continue;
|
|
1629
|
+
}
|
|
1630
|
+
if (c === ")") {
|
|
1631
|
+
tokens.push({ t: 2 });
|
|
1632
|
+
i++;
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (c === "[") {
|
|
1636
|
+
tokens.push({ t: 3 });
|
|
1637
|
+
i++;
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
if (c === "]") {
|
|
1641
|
+
tokens.push({ t: 4 });
|
|
1642
|
+
i++;
|
|
1643
|
+
continue;
|
|
1644
|
+
}
|
|
1645
|
+
if (c === "{") {
|
|
1646
|
+
tokens.push({ t: 5 });
|
|
1647
|
+
i++;
|
|
1648
|
+
continue;
|
|
1649
|
+
}
|
|
1650
|
+
if (c === "}") {
|
|
1651
|
+
tokens.push({ t: 6 });
|
|
1652
|
+
i++;
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
if (c === ",") {
|
|
1656
|
+
tokens.push({ t: 7 });
|
|
1657
|
+
i++;
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
if (c === ":") {
|
|
1661
|
+
tokens.push({ t: 8 });
|
|
1662
|
+
i++;
|
|
1663
|
+
continue;
|
|
1664
|
+
}
|
|
1665
|
+
if (c === "=") {
|
|
1666
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
1667
|
+
tokens.push({ t: 25 });
|
|
1668
|
+
i += 2;
|
|
1669
|
+
} else {
|
|
1670
|
+
tokens.push({ t: 9 });
|
|
1671
|
+
i++;
|
|
1672
|
+
}
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
if (c === "!") {
|
|
1676
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
1677
|
+
tokens.push({ t: 26 });
|
|
1678
|
+
i += 2;
|
|
1679
|
+
} else {
|
|
1680
|
+
tokens.push({ t: 33 });
|
|
1681
|
+
i++;
|
|
1682
|
+
}
|
|
1683
|
+
continue;
|
|
1684
|
+
}
|
|
1685
|
+
if (c === ">") {
|
|
1686
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
1687
|
+
tokens.push({ t: 29 });
|
|
1688
|
+
i += 2;
|
|
1689
|
+
} else {
|
|
1690
|
+
tokens.push({ t: 27 });
|
|
1691
|
+
i++;
|
|
1692
|
+
}
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
if (c === "<") {
|
|
1696
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
1697
|
+
tokens.push({ t: 30 });
|
|
1698
|
+
i += 2;
|
|
1699
|
+
} else {
|
|
1700
|
+
tokens.push({ t: 28 });
|
|
1701
|
+
i++;
|
|
1702
|
+
}
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
if (c === "&") {
|
|
1706
|
+
if (i + 1 < n && src[i + 1] === "&") {
|
|
1707
|
+
tokens.push({ t: 31 });
|
|
1708
|
+
i += 2;
|
|
1709
|
+
} else {
|
|
1710
|
+
tokens.push({ t: 31 });
|
|
1711
|
+
i++;
|
|
1712
|
+
}
|
|
1713
|
+
continue;
|
|
1714
|
+
}
|
|
1715
|
+
if (c === "|") {
|
|
1716
|
+
if (i + 1 < n && src[i + 1] === "|") {
|
|
1717
|
+
tokens.push({ t: 32 });
|
|
1718
|
+
i += 2;
|
|
1719
|
+
} else {
|
|
1720
|
+
tokens.push({ t: 32 });
|
|
1721
|
+
i++;
|
|
1722
|
+
}
|
|
1723
|
+
continue;
|
|
1724
|
+
}
|
|
1725
|
+
if (c === ".") {
|
|
1726
|
+
tokens.push({ t: 19 });
|
|
1727
|
+
i++;
|
|
1728
|
+
continue;
|
|
1729
|
+
}
|
|
1730
|
+
if (c === "?") {
|
|
1731
|
+
tokens.push({ t: 34 });
|
|
1732
|
+
i++;
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
if (c === "+") {
|
|
1736
|
+
tokens.push({ t: 20 });
|
|
1737
|
+
i++;
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
if (c === "*") {
|
|
1741
|
+
tokens.push({ t: 22 });
|
|
1742
|
+
i++;
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
if (c === "/") {
|
|
1746
|
+
tokens.push({ t: 23 });
|
|
1747
|
+
i++;
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
if (c === "%") {
|
|
1751
|
+
tokens.push({ t: 24 });
|
|
1752
|
+
i++;
|
|
1753
|
+
continue;
|
|
1754
|
+
}
|
|
1755
|
+
if (c === '"') {
|
|
1756
|
+
const start = i;
|
|
1757
|
+
i++;
|
|
1758
|
+
let isClosed = false;
|
|
1759
|
+
while (i < n) if (src[i] === "\\") i += 2;
|
|
1760
|
+
else if (src[i] === '"') {
|
|
1761
|
+
i++;
|
|
1762
|
+
isClosed = true;
|
|
1763
|
+
break;
|
|
1764
|
+
} else i++;
|
|
1765
|
+
const rawString = src.slice(start, i);
|
|
1766
|
+
try {
|
|
1767
|
+
const validJsonString = isClosed ? rawString : rawString + '"';
|
|
1768
|
+
tokens.push({
|
|
1769
|
+
t: 14,
|
|
1770
|
+
v: JSON.parse(validJsonString)
|
|
1771
|
+
});
|
|
1772
|
+
} catch {
|
|
1773
|
+
const stripped = rawString.replace(/^"|"$/g, "");
|
|
1774
|
+
tokens.push({
|
|
1775
|
+
t: 14,
|
|
1776
|
+
v: stripped
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
continue;
|
|
1780
|
+
}
|
|
1781
|
+
if (c === "'") {
|
|
1782
|
+
i++;
|
|
1783
|
+
let result = "";
|
|
1784
|
+
while (i < n) if (src[i] === "\\") {
|
|
1785
|
+
i++;
|
|
1786
|
+
if (i < n) {
|
|
1787
|
+
const esc = src[i];
|
|
1788
|
+
if (esc === "'") result += "'";
|
|
1789
|
+
else if (esc === "\\") result += "\\";
|
|
1790
|
+
else if (esc === "n") result += "\n";
|
|
1791
|
+
else if (esc === "t") result += " ";
|
|
1792
|
+
else result += esc;
|
|
1793
|
+
i++;
|
|
1794
|
+
}
|
|
1795
|
+
} else if (src[i] === "'") {
|
|
1796
|
+
i++;
|
|
1797
|
+
break;
|
|
1798
|
+
} else {
|
|
1799
|
+
result += src[i];
|
|
1800
|
+
i++;
|
|
1801
|
+
}
|
|
1802
|
+
tokens.push({
|
|
1803
|
+
t: 14,
|
|
1804
|
+
v: result
|
|
1805
|
+
});
|
|
1806
|
+
continue;
|
|
1807
|
+
}
|
|
1808
|
+
if (c === "-") {
|
|
1809
|
+
const prev = tokens.length > 0 ? tokens[tokens.length - 1] : null;
|
|
1810
|
+
if (!(prev != null && (prev.t === 15 || prev.t === 14 || prev.t === 16 || prev.t === 17 || prev.t === 2 || prev.t === 4 || prev.t === 10 || prev.t === 11 || prev.t === 12 || prev.t === 18 || prev.t === 35)) && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") {
|
|
1811
|
+
} else {
|
|
1812
|
+
tokens.push({ t: 21 });
|
|
1813
|
+
i++;
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
const isDigit = c >= "0" && c <= "9";
|
|
1818
|
+
const isNegDigit = c === "-" && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9";
|
|
1819
|
+
if (isDigit || isNegDigit) {
|
|
1820
|
+
const start = i;
|
|
1821
|
+
if (src[i] === "-") i++;
|
|
1822
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
1823
|
+
if (i < n && src[i] === "." && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") {
|
|
1824
|
+
i++;
|
|
1825
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
1826
|
+
}
|
|
1827
|
+
if (i < n && (src[i] === "e" || src[i] === "E")) {
|
|
1828
|
+
i++;
|
|
1829
|
+
if (i < n && (src[i] === "+" || src[i] === "-")) i++;
|
|
1830
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
1831
|
+
}
|
|
1832
|
+
tokens.push({
|
|
1833
|
+
t: 15,
|
|
1834
|
+
v: +src.slice(start, i)
|
|
1835
|
+
});
|
|
1836
|
+
continue;
|
|
1837
|
+
}
|
|
1838
|
+
if (c === "$" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
|
|
1839
|
+
const start = i;
|
|
1840
|
+
i++;
|
|
1841
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_")) i++;
|
|
1842
|
+
tokens.push({
|
|
1843
|
+
t: 18,
|
|
1844
|
+
v: src.slice(start, i)
|
|
1845
|
+
});
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
if (c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_") {
|
|
1849
|
+
const start = i;
|
|
1850
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_")) i++;
|
|
1851
|
+
const word = src.slice(start, i);
|
|
1852
|
+
if (word === "true") {
|
|
1853
|
+
tokens.push({ t: 10 });
|
|
1854
|
+
continue;
|
|
1855
|
+
}
|
|
1856
|
+
if (word === "false") {
|
|
1857
|
+
tokens.push({ t: 11 });
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
if (word === "null") {
|
|
1861
|
+
tokens.push({ t: 12 });
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
const kind = c >= "A" && c <= "Z" ? 17 : 16;
|
|
1865
|
+
tokens.push({
|
|
1866
|
+
t: kind,
|
|
1867
|
+
v: word
|
|
1868
|
+
});
|
|
1869
|
+
continue;
|
|
1870
|
+
}
|
|
1871
|
+
if (c === "@" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
|
|
1872
|
+
i++;
|
|
1873
|
+
const start = i;
|
|
1874
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_")) i++;
|
|
1875
|
+
tokens.push({
|
|
1876
|
+
t: 35,
|
|
1877
|
+
v: src.slice(start, i)
|
|
1878
|
+
});
|
|
1879
|
+
continue;
|
|
1880
|
+
}
|
|
1881
|
+
i++;
|
|
1882
|
+
}
|
|
1883
|
+
tokens.push({ t: 13 });
|
|
1884
|
+
return tokens;
|
|
1885
|
+
}
|
|
1886
|
+
var SCALAR_TYPEOFS = [
|
|
1887
|
+
"string",
|
|
1888
|
+
"number",
|
|
1889
|
+
"boolean"
|
|
1890
|
+
];
|
|
1891
|
+
var NO_PROPS = {};
|
|
1892
|
+
var NO_REQUIRED = [];
|
|
1893
|
+
function jsType(value) {
|
|
1894
|
+
return Array.isArray(value) ? "array" : typeof value;
|
|
1895
|
+
}
|
|
1896
|
+
function isCompositeSchema(s) {
|
|
1897
|
+
return "$ref" in s || "anyOf" in s || "oneOf" in s || "allOf" in s;
|
|
1898
|
+
}
|
|
1899
|
+
function getScalarTypeInfo(s) {
|
|
1900
|
+
if (Array.isArray(s["enum"])) return { enumValues: s["enum"] };
|
|
1901
|
+
if ("const" in s) return { enumValues: [s["const"]] };
|
|
1902
|
+
switch (s["type"]) {
|
|
1903
|
+
case "string":
|
|
1904
|
+
return { expectedType: "string" };
|
|
1905
|
+
case "number":
|
|
1906
|
+
case "integer":
|
|
1907
|
+
return { expectedType: "number" };
|
|
1908
|
+
case "boolean":
|
|
1909
|
+
return { expectedType: "boolean" };
|
|
1910
|
+
default:
|
|
1911
|
+
return {};
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
function getSchemaDefaultValue(property) {
|
|
1915
|
+
if (!property || typeof property !== "object" || Array.isArray(property)) return;
|
|
1916
|
+
return property.default;
|
|
1917
|
+
}
|
|
1918
|
+
function getTypeFromSchema(property) {
|
|
1919
|
+
if (!property || typeof property !== "object" || Array.isArray(property)) return;
|
|
1920
|
+
const p = property;
|
|
1921
|
+
if (typeof p.$ref === "string") return p.$ref.split("/").pop();
|
|
1922
|
+
const leaf = getScalarTypeInfo(p);
|
|
1923
|
+
if (leaf.enumValues) return leaf.enumValues.map((v) => JSON.stringify(v)).join("|");
|
|
1924
|
+
if (leaf.expectedType) return leaf.expectedType;
|
|
1925
|
+
return typeof p.type === "string" ? p.type : void 0;
|
|
1926
|
+
}
|
|
1927
|
+
function buildParamsSignature(component, params) {
|
|
1928
|
+
return `${component}(${params.map((p) => {
|
|
1929
|
+
const type = getTypeFromSchema(p.schema);
|
|
1930
|
+
const marked = p.required ? `${p.name}*` : p.name;
|
|
1931
|
+
return type ? `${marked}: ${type}` : marked;
|
|
1932
|
+
}).join(", ")})`;
|
|
1933
|
+
}
|
|
1934
|
+
function checkTypeMismatch(value, info) {
|
|
1935
|
+
const actual = typeof value;
|
|
1936
|
+
if (info.enumValues) {
|
|
1937
|
+
if (!SCALAR_TYPEOFS.includes(actual)) return null;
|
|
1938
|
+
if (info.enumValues.includes(value)) return null;
|
|
1939
|
+
return {
|
|
1940
|
+
expected: `one of [${info.enumValues.map((v) => JSON.stringify(v)).join(", ")}]`,
|
|
1941
|
+
actual: JSON.stringify(value)
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
if (info.expectedType && info.expectedType !== actual) return {
|
|
1945
|
+
expected: info.expectedType,
|
|
1946
|
+
actual: jsType(value)
|
|
1947
|
+
};
|
|
1948
|
+
return null;
|
|
1949
|
+
}
|
|
1950
|
+
function validationMessage(component, path, issue) {
|
|
1951
|
+
switch (issue.code) {
|
|
1952
|
+
case "type-mismatch":
|
|
1953
|
+
return `field "${path}" expects ${issue.expected} but got ${issue.actual}`;
|
|
1954
|
+
case "missing-required":
|
|
1955
|
+
return `missing required field "${path}"${issue.signature ? ` \u2014 signature: ${issue.signature}` : ""}`;
|
|
1956
|
+
case "null-required":
|
|
1957
|
+
return `required field "${path}" cannot be null${issue.signature ? ` \u2014 signature: ${issue.signature}` : ""}`;
|
|
1958
|
+
case "unknown-component":
|
|
1959
|
+
return `Unknown component "${component}" \u2014 not found in catalog or builtins${issue.available?.length ? `. Available components: ${issue.available.join(", ")}` : ""}`;
|
|
1960
|
+
case "inline-reserved":
|
|
1961
|
+
return `${component}() must be declared as a top-level statement, not used inline as a value`;
|
|
1962
|
+
case "excess-args":
|
|
1963
|
+
return `${component} takes ${issue.declared} arg(s), got ${issue.got} (${issue.got - issue.declared} excess dropped)`;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
function pushValidationIssue(ctx, component, path, issue) {
|
|
1967
|
+
ctx.errors.push({
|
|
1968
|
+
code: issue.code,
|
|
1969
|
+
component,
|
|
1970
|
+
path,
|
|
1971
|
+
message: validationMessage(component, path, issue),
|
|
1972
|
+
statementId: ctx.currentStatementId
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
function resolveInvalidValue(container, key, required, defaultValue) {
|
|
1976
|
+
if (defaultValue !== void 0) {
|
|
1977
|
+
container[key] = defaultValue;
|
|
1978
|
+
return false;
|
|
1979
|
+
}
|
|
1980
|
+
if (required) return true;
|
|
1981
|
+
delete container[key];
|
|
1982
|
+
return false;
|
|
1983
|
+
}
|
|
1984
|
+
function validateElementPosition(element, s, component, path, ctx) {
|
|
1985
|
+
if (isCompositeSchema(s)) return false;
|
|
1986
|
+
if (typeof s["type"] === "string" || Array.isArray(s["enum"]) || "const" in s) {
|
|
1987
|
+
pushValidationIssue(ctx, component, path, {
|
|
1988
|
+
code: "type-mismatch",
|
|
1989
|
+
expected: typeof s["type"] === "string" ? s["type"] : "a literal value",
|
|
1990
|
+
actual: `component "${element.typeName}"`
|
|
1991
|
+
});
|
|
1992
|
+
return true;
|
|
1993
|
+
}
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1996
|
+
function validateObjectValue(value, s, component, path, ctx) {
|
|
1997
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
1998
|
+
pushValidationIssue(ctx, component, path, {
|
|
1999
|
+
code: "type-mismatch",
|
|
2000
|
+
expected: "object",
|
|
2001
|
+
actual: jsType(value)
|
|
2002
|
+
});
|
|
2003
|
+
return true;
|
|
2004
|
+
}
|
|
2005
|
+
const obj = value;
|
|
2006
|
+
const props = s["properties"] && typeof s["properties"] === "object" ? s["properties"] : NO_PROPS;
|
|
2007
|
+
const required = Array.isArray(s["required"]) ? s["required"] : NO_REQUIRED;
|
|
2008
|
+
let invalid = false;
|
|
2009
|
+
if (!ctx.partial) for (const key of required) {
|
|
2010
|
+
const present = key in obj;
|
|
2011
|
+
if (!present || obj[key] == null) {
|
|
2012
|
+
const fallback = getSchemaDefaultValue(props[key]);
|
|
2013
|
+
if (fallback !== void 0) {
|
|
2014
|
+
obj[key] = fallback;
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
pushValidationIssue(ctx, component, `${path}/${key}`, { code: present ? "null-required" : "missing-required" });
|
|
2018
|
+
invalid = true;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
for (const key of Object.keys(props)) if (key in obj) {
|
|
2022
|
+
const sub = props[key];
|
|
2023
|
+
if (validateSchemaValue(obj[key], sub, component, `${path}/${key}`, ctx)) {
|
|
2024
|
+
if (resolveInvalidValue(obj, key, required.includes(key), getSchemaDefaultValue(sub))) invalid = true;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return invalid;
|
|
2028
|
+
}
|
|
2029
|
+
function validateArrayValue(value, s, component, path, ctx) {
|
|
2030
|
+
if (!Array.isArray(value)) {
|
|
2031
|
+
pushValidationIssue(ctx, component, path, {
|
|
2032
|
+
code: "type-mismatch",
|
|
2033
|
+
expected: "array",
|
|
2034
|
+
actual: jsType(value)
|
|
2035
|
+
});
|
|
2036
|
+
return true;
|
|
2037
|
+
}
|
|
2038
|
+
const items = s["items"];
|
|
2039
|
+
if (!items || typeof items !== "object" || Array.isArray(items)) return false;
|
|
2040
|
+
let invalid;
|
|
2041
|
+
for (let i = 0; i < value.length; i++) if (validateSchemaValue(value[i], items, component, `${path}/${i}`, ctx)) (invalid ??= []).push(i);
|
|
2042
|
+
if (invalid) for (let i = invalid.length - 1; i >= 0; i--) value.splice(invalid[i], 1);
|
|
2043
|
+
return false;
|
|
2044
|
+
}
|
|
2045
|
+
function validateLeafValue(value, s, component, path, ctx) {
|
|
2046
|
+
const leaf = getScalarTypeInfo(s);
|
|
2047
|
+
if (leaf.expectedType == null && leaf.enumValues == null) return false;
|
|
2048
|
+
if (leaf.enumValues && ctx.partial) return false;
|
|
2049
|
+
const mismatch = checkTypeMismatch(value, leaf);
|
|
2050
|
+
if (mismatch) {
|
|
2051
|
+
pushValidationIssue(ctx, component, path, {
|
|
2052
|
+
code: "type-mismatch",
|
|
2053
|
+
...mismatch
|
|
2054
|
+
});
|
|
2055
|
+
return true;
|
|
2056
|
+
}
|
|
2057
|
+
return false;
|
|
2058
|
+
}
|
|
2059
|
+
function validateSchemaValue(value, schema, component, path, ctx) {
|
|
2060
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return false;
|
|
2061
|
+
const s = schema;
|
|
2062
|
+
if (value == null) return false;
|
|
2063
|
+
if (isASTNode(value)) return false;
|
|
2064
|
+
if (isElementNode(value)) return validateElementPosition(value, s, component, path, ctx);
|
|
2065
|
+
if (isCompositeSchema(s)) return false;
|
|
2066
|
+
const type = s["type"];
|
|
2067
|
+
if (type === "object") return validateObjectValue(value, s, component, path, ctx);
|
|
2068
|
+
if (type === "array") return validateArrayValue(value, s, component, path, ctx);
|
|
2069
|
+
return validateLeafValue(value, s, component, path, ctx);
|
|
2070
|
+
}
|
|
2071
|
+
function containsDynamicValue(v) {
|
|
2072
|
+
if (v == null || typeof v !== "object") return false;
|
|
2073
|
+
if (isASTNode(v)) return true;
|
|
2074
|
+
if (Array.isArray(v)) return v.some(containsDynamicValue);
|
|
2075
|
+
if (isElementNode(v)) return Object.values(v.props).some(containsDynamicValue);
|
|
2076
|
+
return Object.values(v).some(containsDynamicValue);
|
|
2077
|
+
}
|
|
2078
|
+
function resolveRef(name, ctx, mode) {
|
|
2079
|
+
if (ctx.visited.has(name)) {
|
|
2080
|
+
ctx.unres.push(name);
|
|
2081
|
+
return mode === "expr" ? {
|
|
2082
|
+
k: "Ph",
|
|
2083
|
+
n: name
|
|
2084
|
+
} : null;
|
|
2085
|
+
}
|
|
2086
|
+
if (!ctx.syms.has(name)) {
|
|
2087
|
+
ctx.unres.push(name);
|
|
2088
|
+
return mode === "expr" ? {
|
|
2089
|
+
k: "Ph",
|
|
2090
|
+
n: name
|
|
2091
|
+
} : null;
|
|
2092
|
+
}
|
|
2093
|
+
const target = ctx.syms.get(name);
|
|
2094
|
+
ctx.unreached?.delete(name);
|
|
2095
|
+
if (target.k === "Comp" && isReservedCall(target.name)) return {
|
|
2096
|
+
k: "RuntimeRef",
|
|
2097
|
+
n: name,
|
|
2098
|
+
refType: target.name === RESERVED_CALLS.Mutation ? "mutation" : "query"
|
|
2099
|
+
};
|
|
2100
|
+
ctx.visited.add(name);
|
|
2101
|
+
const prevStatementId = ctx.currentStatementId;
|
|
2102
|
+
ctx.currentStatementId = name;
|
|
2103
|
+
try {
|
|
2104
|
+
const result = mode === "value" ? materializeValue(target, ctx) : materializeExpr(target, ctx);
|
|
2105
|
+
if (mode === "value" && isElementNode(result)) result.statementId = name;
|
|
2106
|
+
return result;
|
|
2107
|
+
} finally {
|
|
2108
|
+
ctx.currentStatementId = prevStatementId;
|
|
2109
|
+
ctx.visited.delete(name);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
function materializeLazyBuiltin(node, ctx, scopedRefs) {
|
|
2113
|
+
if (!LAZY_BUILTINS.has(node.name) || node.args.length < 3) return null;
|
|
2114
|
+
const varArg = node.args[1];
|
|
2115
|
+
const varName = varArg.k === "Ref" ? varArg.n : varArg.k === "Str" ? varArg.v : null;
|
|
2116
|
+
if (!varName) return null;
|
|
2117
|
+
const nextScopedRefs = new Set(scopedRefs);
|
|
2118
|
+
nextScopedRefs.add(varName);
|
|
2119
|
+
const recursedArgs = node.args.map((a, i) => i === 1 ? a : materializeExprInternal(a, ctx, nextScopedRefs));
|
|
2120
|
+
return {
|
|
2121
|
+
...node,
|
|
2122
|
+
args: recursedArgs
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function materializeExprInternal(node, ctx, scopedRefs) {
|
|
2126
|
+
switch (node.k) {
|
|
2127
|
+
case "Ref":
|
|
2128
|
+
return scopedRefs.has(node.n) ? node : resolveRef(node.n, ctx, "expr");
|
|
2129
|
+
case "Ph":
|
|
2130
|
+
return node;
|
|
2131
|
+
case "Comp": {
|
|
2132
|
+
const lazy = materializeLazyBuiltin(node, ctx, scopedRefs);
|
|
2133
|
+
if (lazy) return lazy;
|
|
2134
|
+
const recursedArgs = node.args.map((a) => materializeExprInternal(a, ctx, scopedRefs));
|
|
2135
|
+
if (isBuiltin(node.name) || isReservedCall(node.name)) return {
|
|
2136
|
+
...node,
|
|
2137
|
+
args: recursedArgs
|
|
2138
|
+
};
|
|
2139
|
+
const def = ctx.cat?.get(node.name);
|
|
2140
|
+
if (def) {
|
|
2141
|
+
const mappedProps = {};
|
|
2142
|
+
for (let i = 0; i < def.params.length && i < recursedArgs.length; i++) mappedProps[def.params[i].name] = recursedArgs[i];
|
|
2143
|
+
return {
|
|
2144
|
+
...node,
|
|
2145
|
+
args: recursedArgs,
|
|
2146
|
+
mappedProps
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
pushValidationIssue(ctx, node.name, "", {
|
|
2150
|
+
code: "unknown-component",
|
|
2151
|
+
available: ctx.cat && [...ctx.cat.keys()]
|
|
2152
|
+
});
|
|
2153
|
+
return {
|
|
2154
|
+
...node,
|
|
2155
|
+
args: recursedArgs
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
case "Arr":
|
|
2159
|
+
return {
|
|
2160
|
+
...node,
|
|
2161
|
+
els: node.els.map((e) => materializeExprInternal(e, ctx, scopedRefs))
|
|
2162
|
+
};
|
|
2163
|
+
case "Obj":
|
|
2164
|
+
return {
|
|
2165
|
+
...node,
|
|
2166
|
+
entries: node.entries.map(([k, v]) => [k, materializeExprInternal(v, ctx, scopedRefs)])
|
|
2167
|
+
};
|
|
2168
|
+
case "BinOp":
|
|
2169
|
+
return {
|
|
2170
|
+
...node,
|
|
2171
|
+
left: materializeExprInternal(node.left, ctx, scopedRefs),
|
|
2172
|
+
right: materializeExprInternal(node.right, ctx, scopedRefs)
|
|
2173
|
+
};
|
|
2174
|
+
case "UnaryOp":
|
|
2175
|
+
return {
|
|
2176
|
+
...node,
|
|
2177
|
+
operand: materializeExprInternal(node.operand, ctx, scopedRefs)
|
|
2178
|
+
};
|
|
2179
|
+
case "Ternary":
|
|
2180
|
+
return {
|
|
2181
|
+
...node,
|
|
2182
|
+
cond: materializeExprInternal(node.cond, ctx, scopedRefs),
|
|
2183
|
+
then: materializeExprInternal(node.then, ctx, scopedRefs),
|
|
2184
|
+
else: materializeExprInternal(node.else, ctx, scopedRefs)
|
|
2185
|
+
};
|
|
2186
|
+
case "Member":
|
|
2187
|
+
return {
|
|
2188
|
+
...node,
|
|
2189
|
+
obj: materializeExprInternal(node.obj, ctx, scopedRefs)
|
|
2190
|
+
};
|
|
2191
|
+
case "Index":
|
|
2192
|
+
return {
|
|
2193
|
+
...node,
|
|
2194
|
+
obj: materializeExprInternal(node.obj, ctx, scopedRefs),
|
|
2195
|
+
index: materializeExprInternal(node.index, ctx, scopedRefs)
|
|
2196
|
+
};
|
|
2197
|
+
case "Assign":
|
|
2198
|
+
return {
|
|
2199
|
+
...node,
|
|
2200
|
+
value: materializeExprInternal(node.value, ctx, scopedRefs)
|
|
2201
|
+
};
|
|
2202
|
+
default:
|
|
2203
|
+
return node;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
function materializeExpr(node, ctx) {
|
|
2207
|
+
return materializeExprInternal(node, ctx, /* @__PURE__ */ new Set());
|
|
2208
|
+
}
|
|
2209
|
+
function materializeValue(node, ctx) {
|
|
2210
|
+
switch (node.k) {
|
|
2211
|
+
case "Ref":
|
|
2212
|
+
return resolveRef(node.n, ctx, "value");
|
|
2213
|
+
case "Str":
|
|
2214
|
+
return node.v;
|
|
2215
|
+
case "Num":
|
|
2216
|
+
return node.v;
|
|
2217
|
+
case "Bool":
|
|
2218
|
+
return node.v;
|
|
2219
|
+
case "Null":
|
|
2220
|
+
return null;
|
|
2221
|
+
case "Ph":
|
|
2222
|
+
return null;
|
|
2223
|
+
case "Arr": {
|
|
2224
|
+
const items = [];
|
|
2225
|
+
for (const e of node.els) {
|
|
2226
|
+
if (e.k === "Ph") continue;
|
|
2227
|
+
const value = materializeValue(e, ctx);
|
|
2228
|
+
if (value === null && (e.k === "Comp" || e.k === "Ref")) continue;
|
|
2229
|
+
items.push(value);
|
|
2230
|
+
}
|
|
2231
|
+
return items;
|
|
2232
|
+
}
|
|
2233
|
+
case "Obj": {
|
|
2234
|
+
const o = {};
|
|
2235
|
+
for (const [k, v] of node.entries) o[k] = materializeValue(v, ctx);
|
|
2236
|
+
return o;
|
|
2237
|
+
}
|
|
2238
|
+
case "Comp": {
|
|
2239
|
+
const { name, args } = node;
|
|
2240
|
+
if (isBuiltin(name)) {
|
|
2241
|
+
const lazy = materializeLazyBuiltin(node, ctx, /* @__PURE__ */ new Set());
|
|
2242
|
+
if (lazy) return lazy;
|
|
2243
|
+
return {
|
|
2244
|
+
...node,
|
|
2245
|
+
args: args.map((a) => materializeExpr(a, ctx))
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
if (isReservedCall(name)) {
|
|
2249
|
+
pushValidationIssue(ctx, name, "", { code: "inline-reserved" });
|
|
2250
|
+
return null;
|
|
2251
|
+
}
|
|
2252
|
+
const def = ctx.cat?.get(name);
|
|
2253
|
+
const props = {};
|
|
2254
|
+
if (def) {
|
|
2255
|
+
let dropComponent = false;
|
|
2256
|
+
for (let i = 0; i < def.params.length && i < args.length; i++) {
|
|
2257
|
+
const param = def.params[i];
|
|
2258
|
+
const value = materializeValue(args[i], ctx);
|
|
2259
|
+
props[param.name] = value;
|
|
2260
|
+
if (param.schema !== void 0 && validateSchemaValue(value, param.schema, name, `/${param.name}`, ctx)) {
|
|
2261
|
+
if (resolveInvalidValue(props, param.name, param.required, param.defaultValue)) dropComponent = true;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
if (args.length > def.params.length) pushValidationIssue(ctx, name, "", {
|
|
2265
|
+
code: "excess-args",
|
|
2266
|
+
declared: def.params.length,
|
|
2267
|
+
got: args.length
|
|
2268
|
+
});
|
|
2269
|
+
const missingRequired = def.params.filter((p) => p.required && (!(p.name in props) || props[p.name] === null));
|
|
2270
|
+
if (missingRequired.length) {
|
|
2271
|
+
const stillInvalid = missingRequired.filter((p) => {
|
|
2272
|
+
if (p.defaultValue !== void 0) {
|
|
2273
|
+
props[p.name] = p.defaultValue;
|
|
2274
|
+
return false;
|
|
2275
|
+
}
|
|
2276
|
+
return true;
|
|
2277
|
+
});
|
|
2278
|
+
if (stillInvalid.length) {
|
|
2279
|
+
for (const p of stillInvalid) pushValidationIssue(ctx, name, `/${p.name}`, {
|
|
2280
|
+
code: p.name in props ? "null-required" : "missing-required",
|
|
2281
|
+
signature: buildParamsSignature(name, def.params)
|
|
2282
|
+
});
|
|
2283
|
+
return null;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
if (dropComponent) return null;
|
|
2287
|
+
} else if (!isBuiltin(name) && !isReservedCall(name)) {
|
|
2288
|
+
pushValidationIssue(ctx, name, "", {
|
|
2289
|
+
code: "unknown-component",
|
|
2290
|
+
available: ctx.cat && [...ctx.cat.keys()]
|
|
2291
|
+
});
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2294
|
+
const hasDynamicProps = Object.values(props).some((v) => containsDynamicValue(v));
|
|
2295
|
+
return {
|
|
2296
|
+
type: "element",
|
|
2297
|
+
typeName: name,
|
|
2298
|
+
props,
|
|
2299
|
+
partial: ctx.partial,
|
|
2300
|
+
hasDynamicProps
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
default:
|
|
2304
|
+
if (isRuntimeExpr(node)) return materializeExpr(node, ctx);
|
|
2305
|
+
return node;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
function autoClose(input) {
|
|
2309
|
+
const stack = [];
|
|
2310
|
+
let inStr = false;
|
|
2311
|
+
let esc = false;
|
|
2312
|
+
for (let i = 0; i < input.length; i++) {
|
|
2313
|
+
const c = input[i];
|
|
2314
|
+
if (esc) {
|
|
2315
|
+
esc = false;
|
|
2316
|
+
continue;
|
|
2317
|
+
}
|
|
2318
|
+
if (c === "\\" && inStr) {
|
|
2319
|
+
esc = true;
|
|
2320
|
+
continue;
|
|
2321
|
+
}
|
|
2322
|
+
if (inStr) {
|
|
2323
|
+
if (c === inStr) inStr = false;
|
|
2324
|
+
continue;
|
|
2325
|
+
}
|
|
2326
|
+
if (c === '"' || c === "'") {
|
|
2327
|
+
inStr = c;
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
if (c === "(" || c === "[" || c === "{") stack.push(c);
|
|
2331
|
+
else if (c === ")" && stack[stack.length - 1] === "(") stack.pop();
|
|
2332
|
+
else if (c === "]" && stack[stack.length - 1] === "[") stack.pop();
|
|
2333
|
+
else if (c === "}" && stack[stack.length - 1] === "{") stack.pop();
|
|
2334
|
+
}
|
|
2335
|
+
if (!(!!inStr || stack.length > 0)) return {
|
|
2336
|
+
text: input,
|
|
2337
|
+
wasIncomplete: false
|
|
2338
|
+
};
|
|
2339
|
+
let out = input;
|
|
2340
|
+
if (inStr) {
|
|
2341
|
+
if (esc) out += "\\";
|
|
2342
|
+
out += inStr;
|
|
2343
|
+
}
|
|
2344
|
+
for (let j = stack.length - 1; j >= 0; j--) out += stack[j] === "(" ? ")" : stack[j] === "[" ? "]" : "}";
|
|
2345
|
+
return {
|
|
2346
|
+
text: out,
|
|
2347
|
+
wasIncomplete: true
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
function split(tokens) {
|
|
2351
|
+
const stmts = [];
|
|
2352
|
+
let pos = 0;
|
|
2353
|
+
while (pos < tokens.length) {
|
|
2354
|
+
while (pos < tokens.length && tokens[pos].t === 0) pos++;
|
|
2355
|
+
if (pos >= tokens.length || tokens[pos].t === 13) break;
|
|
2356
|
+
const tok = tokens[pos];
|
|
2357
|
+
if (tok.t !== 16 && tok.t !== 17 && tok.t !== 18) {
|
|
2358
|
+
while (pos < tokens.length && tokens[pos].t !== 0 && tokens[pos].t !== 13) pos++;
|
|
2359
|
+
continue;
|
|
2360
|
+
}
|
|
2361
|
+
const id = tok.v;
|
|
2362
|
+
const idTokenType = tok.t;
|
|
2363
|
+
pos++;
|
|
2364
|
+
if (pos >= tokens.length || tokens[pos].t !== 9) {
|
|
2365
|
+
while (pos < tokens.length && tokens[pos].t !== 0 && tokens[pos].t !== 13) pos++;
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
pos++;
|
|
2369
|
+
const expr = [];
|
|
2370
|
+
let depth = 0;
|
|
2371
|
+
let ternaryDepth = 0;
|
|
2372
|
+
while (pos < tokens.length && tokens[pos].t !== 13) {
|
|
2373
|
+
const tt = tokens[pos].t;
|
|
2374
|
+
if (tt === 0 && depth <= 0 && ternaryDepth <= 0) {
|
|
2375
|
+
let peek = pos + 1;
|
|
2376
|
+
while (peek < tokens.length && tokens[peek].t === 0) peek++;
|
|
2377
|
+
const nextT = peek < tokens.length ? tokens[peek].t : 13;
|
|
2378
|
+
if (nextT === 34 || nextT === 8 && ternaryDepth > 0) {
|
|
2379
|
+
pos++;
|
|
2380
|
+
continue;
|
|
2381
|
+
}
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
if (tt === 0) {
|
|
2385
|
+
pos++;
|
|
2386
|
+
continue;
|
|
2387
|
+
}
|
|
2388
|
+
if (tt === 1 || tt === 3 || tt === 5) depth++;
|
|
2389
|
+
else if ((tt === 2 || tt === 4 || tt === 6) && depth > 0) depth--;
|
|
2390
|
+
else if (tt === 34 && depth === 0) ternaryDepth++;
|
|
2391
|
+
else if (tt === 8 && depth === 0 && ternaryDepth > 0) ternaryDepth--;
|
|
2392
|
+
expr.push(tokens[pos++]);
|
|
2393
|
+
}
|
|
2394
|
+
if (expr.length) stmts.push({
|
|
2395
|
+
id,
|
|
2396
|
+
idTokenType,
|
|
2397
|
+
tokens: expr
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2400
|
+
return stmts;
|
|
2401
|
+
}
|
|
2402
|
+
function emptyResult(incomplete = true) {
|
|
2403
|
+
return {
|
|
2404
|
+
root: null,
|
|
2405
|
+
meta: {
|
|
2406
|
+
incomplete,
|
|
2407
|
+
unresolved: [],
|
|
2408
|
+
orphaned: [],
|
|
2409
|
+
statementCount: 0,
|
|
2410
|
+
errors: []
|
|
2411
|
+
},
|
|
2412
|
+
stateDeclarations: {},
|
|
2413
|
+
queryStatements: [],
|
|
2414
|
+
mutationStatements: []
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
function collectQueryDeps(node) {
|
|
2418
|
+
if (!isASTNode(node)) return [];
|
|
2419
|
+
const refs = /* @__PURE__ */ new Set();
|
|
2420
|
+
walkAST(node, (current) => {
|
|
2421
|
+
if (current.k === "StateRef") refs.add(current.n);
|
|
2422
|
+
});
|
|
2423
|
+
return [...refs];
|
|
2424
|
+
}
|
|
2425
|
+
function classifyStatement(raw, expr) {
|
|
2426
|
+
if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Query) {
|
|
2427
|
+
const deps = collectQueryDeps(expr.args[1]);
|
|
2428
|
+
return {
|
|
2429
|
+
kind: "query",
|
|
2430
|
+
id: raw.id,
|
|
2431
|
+
call: {
|
|
2432
|
+
callee: RESERVED_CALLS.Query,
|
|
2433
|
+
args: expr.args
|
|
2434
|
+
},
|
|
2435
|
+
expr,
|
|
2436
|
+
deps: deps.length > 0 ? deps : void 0
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Mutation) return {
|
|
2440
|
+
kind: "mutation",
|
|
2441
|
+
id: raw.id,
|
|
2442
|
+
call: {
|
|
2443
|
+
callee: RESERVED_CALLS.Mutation,
|
|
2444
|
+
args: expr.args
|
|
2445
|
+
},
|
|
2446
|
+
expr
|
|
2447
|
+
};
|
|
2448
|
+
if (raw.idTokenType === 18) return {
|
|
2449
|
+
kind: "state",
|
|
2450
|
+
id: raw.id,
|
|
2451
|
+
init: expr
|
|
2452
|
+
};
|
|
2453
|
+
return {
|
|
2454
|
+
kind: "value",
|
|
2455
|
+
id: raw.id,
|
|
2456
|
+
expr
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
function extractStatements(stmts, ctx) {
|
|
2460
|
+
const stateDeclarations = {};
|
|
2461
|
+
const queryStatements = [];
|
|
2462
|
+
const mutationStatements = [];
|
|
2463
|
+
for (const stmt of stmts) switch (stmt.kind) {
|
|
2464
|
+
case "state":
|
|
2465
|
+
stateDeclarations[stmt.id] = materializeValue(stmt.init, ctx);
|
|
2466
|
+
break;
|
|
2467
|
+
case "query":
|
|
2468
|
+
queryStatements.push({
|
|
2469
|
+
statementId: stmt.id,
|
|
2470
|
+
toolAST: stmt.call.args[0] ?? null,
|
|
2471
|
+
argsAST: stmt.call.args[1] ?? null,
|
|
2472
|
+
defaultsAST: stmt.call.args[2] ?? null,
|
|
2473
|
+
refreshAST: stmt.call.args[3] ?? null,
|
|
2474
|
+
deps: stmt.deps,
|
|
2475
|
+
complete: true
|
|
2476
|
+
});
|
|
2477
|
+
break;
|
|
2478
|
+
case "mutation":
|
|
2479
|
+
mutationStatements.push({
|
|
2480
|
+
statementId: stmt.id,
|
|
2481
|
+
toolAST: stmt.call.args[0] ?? null,
|
|
2482
|
+
argsAST: stmt.call.args[1] ?? null
|
|
2483
|
+
});
|
|
2484
|
+
break;
|
|
2485
|
+
}
|
|
2486
|
+
for (const stmt of stmts) {
|
|
2487
|
+
const nodes = stmt.kind === "state" ? [stmt.init] : stmt.kind === "value" ? [stmt.expr] : stmt.kind === "query" || stmt.kind === "mutation" ? stmt.call.args : [];
|
|
2488
|
+
for (const node of nodes) for (const dep of collectQueryDeps(node)) if (!(dep in stateDeclarations)) stateDeclarations[dep] = null;
|
|
2489
|
+
}
|
|
2490
|
+
return {
|
|
2491
|
+
stateDeclarations,
|
|
2492
|
+
queryStatements,
|
|
2493
|
+
mutationStatements
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
var DEFAULT_ROOT_STATEMENT_ID = "root";
|
|
2497
|
+
function isComponentStatement(stmt) {
|
|
2498
|
+
return stmt.kind === "value" && stmt.expr.k === "Comp" && !isBuiltin(stmt.expr.name) && stmt.expr.name !== RESERVED_CALLS.Query && stmt.expr.name !== RESERVED_CALLS.Mutation;
|
|
2499
|
+
}
|
|
2500
|
+
function pickEntryId(stmtMap, typedStmts, firstId, rootName) {
|
|
2501
|
+
if (stmtMap.has(DEFAULT_ROOT_STATEMENT_ID)) return DEFAULT_ROOT_STATEMENT_ID;
|
|
2502
|
+
if (rootName && stmtMap.has(rootName)) return rootName;
|
|
2503
|
+
const preferredComponent = rootName ? typedStmts.find((stmt) => isComponentStatement(stmt) && stmt.expr.name === rootName) : void 0;
|
|
2504
|
+
if (preferredComponent) return preferredComponent.id;
|
|
2505
|
+
return typedStmts.find(isComponentStatement)?.id ?? firstId;
|
|
2506
|
+
}
|
|
2507
|
+
function buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtCount, cat, rootName) {
|
|
2508
|
+
const entryId = pickEntryId(stmtMap, typedStmts, firstId, rootName);
|
|
2509
|
+
if (!stmtMap.has(entryId)) return emptyResult(wasIncomplete);
|
|
2510
|
+
const syms = /* @__PURE__ */ new Map();
|
|
2511
|
+
for (const [id, stmt] of stmtMap) syms.set(id, stmt.kind === "state" ? stmt.init : stmt.expr);
|
|
2512
|
+
const unres = [];
|
|
2513
|
+
const errors = [];
|
|
2514
|
+
const unreached = /* @__PURE__ */ new Set();
|
|
2515
|
+
for (const [id, stmt] of stmtMap) {
|
|
2516
|
+
if (id === entryId) continue;
|
|
2517
|
+
if (stmt.kind === "state" || stmt.kind === "query" || stmt.kind === "mutation") continue;
|
|
2518
|
+
unreached.add(id);
|
|
2519
|
+
}
|
|
2520
|
+
const ctx = {
|
|
2521
|
+
syms,
|
|
2522
|
+
cat,
|
|
2523
|
+
errors,
|
|
2524
|
+
unres,
|
|
2525
|
+
visited: /* @__PURE__ */ new Set(),
|
|
2526
|
+
partial: wasIncomplete,
|
|
2527
|
+
currentStatementId: entryId,
|
|
2528
|
+
unreached
|
|
2529
|
+
};
|
|
2530
|
+
const materialized = materializeValue(syms.get(entryId), ctx);
|
|
2531
|
+
const root = isElementNode(materialized) ? materialized : null;
|
|
2532
|
+
if (root) root.statementId = entryId;
|
|
2533
|
+
const { stateDeclarations, queryStatements, mutationStatements } = extractStatements(typedStmts, ctx);
|
|
2534
|
+
return {
|
|
2535
|
+
root,
|
|
2536
|
+
meta: {
|
|
2537
|
+
incomplete: wasIncomplete,
|
|
2538
|
+
unresolved: unres,
|
|
2539
|
+
orphaned: [...unreached],
|
|
2540
|
+
statementCount: stmtCount,
|
|
2541
|
+
errors
|
|
2542
|
+
},
|
|
2543
|
+
stateDeclarations,
|
|
2544
|
+
queryStatements,
|
|
2545
|
+
mutationStatements
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
function skipString(input, start) {
|
|
2549
|
+
if (input[start] !== '"') return start;
|
|
2550
|
+
let i = start + 1;
|
|
2551
|
+
while (i < input.length) {
|
|
2552
|
+
const c = input[i];
|
|
2553
|
+
if (c === "\\") i += 2;
|
|
2554
|
+
else if (c === '"') return i + 1;
|
|
2555
|
+
else i++;
|
|
2556
|
+
}
|
|
2557
|
+
return i;
|
|
2558
|
+
}
|
|
2559
|
+
function stripFences(input) {
|
|
2560
|
+
const blocks = [];
|
|
2561
|
+
let i = 0;
|
|
2562
|
+
while (i < input.length) {
|
|
2563
|
+
let fenceStart = -1;
|
|
2564
|
+
while (i < input.length) {
|
|
2565
|
+
const nextI = skipString(input, i);
|
|
2566
|
+
if (nextI > i) {
|
|
2567
|
+
i = nextI;
|
|
2568
|
+
continue;
|
|
2569
|
+
}
|
|
2570
|
+
if (input[i] === "`" && i + 1 < input.length && input[i + 1] === "`" && i + 2 < input.length && input[i + 2] === "`") {
|
|
2571
|
+
fenceStart = i;
|
|
2572
|
+
break;
|
|
2573
|
+
}
|
|
2574
|
+
i++;
|
|
2575
|
+
}
|
|
2576
|
+
if (fenceStart === -1) break;
|
|
2577
|
+
let j = fenceStart + 3;
|
|
2578
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
2579
|
+
if (j >= input.length) {
|
|
2580
|
+
blocks.push(input.slice(fenceStart + 3).replace(/^[^\n]*\n?/, ""));
|
|
2581
|
+
i = input.length;
|
|
2582
|
+
break;
|
|
2583
|
+
}
|
|
2584
|
+
j++;
|
|
2585
|
+
let closePos = -1;
|
|
2586
|
+
let k = j;
|
|
2587
|
+
while (k < input.length) {
|
|
2588
|
+
const nextK = skipString(input, k);
|
|
2589
|
+
if (nextK > k) {
|
|
2590
|
+
k = nextK;
|
|
2591
|
+
continue;
|
|
2592
|
+
}
|
|
2593
|
+
if (input[k] === "`" && k + 1 < input.length && input[k + 1] === "`" && k + 2 < input.length && input[k + 2] === "`") {
|
|
2594
|
+
closePos = k;
|
|
2595
|
+
break;
|
|
2596
|
+
}
|
|
2597
|
+
k++;
|
|
2598
|
+
}
|
|
2599
|
+
if (closePos !== -1) {
|
|
2600
|
+
blocks.push(input.slice(j, closePos));
|
|
2601
|
+
i = closePos + 3;
|
|
2602
|
+
} else {
|
|
2603
|
+
blocks.push(input.slice(j));
|
|
2604
|
+
i = input.length;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
if (blocks.length > 0) return blocks.join("\n");
|
|
2608
|
+
if (input.startsWith("```")) {
|
|
2609
|
+
let j = 3;
|
|
2610
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
2611
|
+
const start = j < input.length ? j + 1 : 3;
|
|
2612
|
+
const body = input.slice(start);
|
|
2613
|
+
const trailingFence = body.lastIndexOf("```");
|
|
2614
|
+
if (trailingFence !== -1) return body.slice(0, trailingFence);
|
|
2615
|
+
return body;
|
|
2616
|
+
}
|
|
2617
|
+
return input;
|
|
2618
|
+
}
|
|
2619
|
+
function stripComments(input) {
|
|
2620
|
+
let inStr = false;
|
|
2621
|
+
return input.split("\n").map((line) => {
|
|
2622
|
+
for (let i = 0; i < line.length; i++) {
|
|
2623
|
+
const c = line[i];
|
|
2624
|
+
if (inStr) {
|
|
2625
|
+
if (c === "\\" && i + 1 < line.length) {
|
|
2626
|
+
i++;
|
|
2627
|
+
continue;
|
|
2628
|
+
}
|
|
2629
|
+
if (c === inStr) inStr = false;
|
|
2630
|
+
continue;
|
|
2631
|
+
}
|
|
2632
|
+
if (c === '"' || c === "'") {
|
|
2633
|
+
inStr = c;
|
|
2634
|
+
continue;
|
|
2635
|
+
}
|
|
2636
|
+
if (c === "/" && line[i + 1] === "/") return line.substring(0, i).trimEnd();
|
|
2637
|
+
if (c === "#") return line.substring(0, i).trimEnd();
|
|
2638
|
+
}
|
|
2639
|
+
return line;
|
|
2640
|
+
}).join("\n");
|
|
2641
|
+
}
|
|
2642
|
+
function preprocess(input) {
|
|
2643
|
+
return stripComments(stripFences(input.trim())).trim();
|
|
2644
|
+
}
|
|
2645
|
+
function createStreamParser(cat, rootName) {
|
|
2646
|
+
let buf = "";
|
|
2647
|
+
let cleaned = "";
|
|
2648
|
+
let completedEnd = 0;
|
|
2649
|
+
const completedStmtMap = /* @__PURE__ */ new Map();
|
|
2650
|
+
let completedCount = 0;
|
|
2651
|
+
let firstId = "";
|
|
2652
|
+
function addStmt(text) {
|
|
2653
|
+
const t = text.trim();
|
|
2654
|
+
if (!t) return;
|
|
2655
|
+
for (const s of split(tokenize(t))) {
|
|
2656
|
+
const stmt = classifyStatement(s, parseExpression(s.tokens));
|
|
2657
|
+
completedStmtMap.set(s.id, stmt);
|
|
2658
|
+
completedCount++;
|
|
2659
|
+
if (!firstId) firstId = s.id;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
function refreshCleaned() {
|
|
2663
|
+
const next = preprocess(buf);
|
|
2664
|
+
if (!next.startsWith(cleaned.slice(0, completedEnd))) {
|
|
2665
|
+
completedEnd = 0;
|
|
2666
|
+
completedStmtMap.clear();
|
|
2667
|
+
completedCount = 0;
|
|
2668
|
+
firstId = "";
|
|
2669
|
+
}
|
|
2670
|
+
cleaned = next;
|
|
2671
|
+
}
|
|
2672
|
+
function scanNewCompleted() {
|
|
2673
|
+
let depth = 0, ternaryDepth = 0, inStr = false, esc = false;
|
|
2674
|
+
let stmtStart = completedEnd;
|
|
2675
|
+
for (let i = completedEnd; i < cleaned.length; i++) {
|
|
2676
|
+
const c = cleaned[i];
|
|
2677
|
+
if (esc) {
|
|
2678
|
+
esc = false;
|
|
2679
|
+
continue;
|
|
2680
|
+
}
|
|
2681
|
+
if (c === "\\" && inStr) {
|
|
2682
|
+
esc = true;
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
2685
|
+
if (inStr) {
|
|
2686
|
+
if (c === inStr) inStr = false;
|
|
2687
|
+
continue;
|
|
2688
|
+
}
|
|
2689
|
+
if (c === '"' || c === "'") {
|
|
2690
|
+
inStr = c;
|
|
2691
|
+
continue;
|
|
2692
|
+
}
|
|
2693
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
2694
|
+
else if (c === ")" || c === "]" || c === "}") depth = Math.max(0, depth - 1);
|
|
2695
|
+
else if (c === "?" && depth === 0) ternaryDepth++;
|
|
2696
|
+
else if (c === ":" && depth === 0 && ternaryDepth > 0) ternaryDepth--;
|
|
2697
|
+
else if (c === "\n" && depth <= 0 && ternaryDepth <= 0) {
|
|
2698
|
+
let peek = i + 1;
|
|
2699
|
+
while (peek < cleaned.length && (cleaned[peek] === " " || cleaned[peek] === " " || cleaned[peek] === "\r" || cleaned[peek] === "\n")) peek++;
|
|
2700
|
+
if (peek < cleaned.length && (cleaned[peek] === "?" || cleaned[peek] === ":" && ternaryDepth > 0)) continue;
|
|
2701
|
+
const t = cleaned.slice(stmtStart, i).trim();
|
|
2702
|
+
if (t) addStmt(t);
|
|
2703
|
+
stmtStart = i + 1;
|
|
2704
|
+
completedEnd = i + 1;
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
return stmtStart;
|
|
2708
|
+
}
|
|
2709
|
+
function currentResult() {
|
|
2710
|
+
refreshCleaned();
|
|
2711
|
+
const pendingStart = scanNewCompleted();
|
|
2712
|
+
const pendingText = cleaned.slice(pendingStart).trim();
|
|
2713
|
+
if (!pendingText) {
|
|
2714
|
+
if (completedCount === 0) return emptyResult();
|
|
2715
|
+
return buildResult(completedStmtMap, [...completedStmtMap.values()], firstId, false, completedCount, cat, rootName);
|
|
2716
|
+
}
|
|
2717
|
+
const { text: closed, wasIncomplete } = autoClose(pendingText);
|
|
2718
|
+
const stmts = split(tokenize(closed));
|
|
2719
|
+
if (!stmts.length) {
|
|
2720
|
+
if (completedCount === 0) return emptyResult(wasIncomplete);
|
|
2721
|
+
return buildResult(completedStmtMap, [...completedStmtMap.values()], firstId, wasIncomplete, completedCount, cat, rootName);
|
|
2722
|
+
}
|
|
2723
|
+
const allStmtMap = new Map(completedStmtMap);
|
|
2724
|
+
for (const s of stmts) {
|
|
2725
|
+
if (completedStmtMap.has(s.id)) continue;
|
|
2726
|
+
const stmt = classifyStatement(s, parseExpression(s.tokens));
|
|
2727
|
+
allStmtMap.set(s.id, stmt);
|
|
2728
|
+
}
|
|
2729
|
+
return buildResult(allStmtMap, [...allStmtMap.values()], firstId || stmts[0].id, wasIncomplete, completedCount + stmts.length, cat, rootName);
|
|
2730
|
+
}
|
|
2731
|
+
function reset() {
|
|
2732
|
+
buf = "";
|
|
2733
|
+
cleaned = "";
|
|
2734
|
+
completedEnd = 0;
|
|
2735
|
+
completedStmtMap.clear();
|
|
2736
|
+
completedCount = 0;
|
|
2737
|
+
firstId = "";
|
|
2738
|
+
}
|
|
2739
|
+
return {
|
|
2740
|
+
push(chunk) {
|
|
2741
|
+
buf += chunk;
|
|
2742
|
+
return currentResult();
|
|
2743
|
+
},
|
|
2744
|
+
set(fullText) {
|
|
2745
|
+
if (fullText.length < buf.length || !fullText.startsWith(buf)) reset();
|
|
2746
|
+
const delta = fullText.slice(buf.length);
|
|
2747
|
+
if (delta) buf += delta;
|
|
2748
|
+
return currentResult();
|
|
2749
|
+
},
|
|
2750
|
+
getResult: currentResult
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
function compileSchema(schema) {
|
|
2754
|
+
const map = /* @__PURE__ */ new Map();
|
|
2755
|
+
const defs = schema.$defs ?? {};
|
|
2756
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
2757
|
+
const properties = def.properties ?? {};
|
|
2758
|
+
const required = def.required ?? [];
|
|
2759
|
+
const params = Object.keys(properties).map((key) => ({
|
|
2760
|
+
name: key,
|
|
2761
|
+
required: required.includes(key),
|
|
2762
|
+
defaultValue: getSchemaDefaultValue(properties[key]),
|
|
2763
|
+
schema: properties[key]
|
|
2764
|
+
}));
|
|
2765
|
+
map.set(name, { params });
|
|
2766
|
+
}
|
|
2767
|
+
return map;
|
|
2768
|
+
}
|
|
2769
|
+
function createStreamingParser(schema, rootName) {
|
|
2770
|
+
return createStreamParser(compileSchema(schema), rootName);
|
|
2771
|
+
}
|
|
2772
|
+
function buildSignatureHint(componentName, schema) {
|
|
2773
|
+
if (!schema?.properties) return void 0;
|
|
2774
|
+
const required = new Set(schema.required ?? []);
|
|
2775
|
+
return `Signature: ${componentName}(${Object.keys(schema.properties).map((k) => required.has(k) ? `${k}*` : k).join(", ")}) \u2014 * marks required`;
|
|
2776
|
+
}
|
|
2777
|
+
function enrichErrors(validationErrors, schema, componentNames) {
|
|
2778
|
+
return validationErrors.map((ve) => {
|
|
2779
|
+
const error = {
|
|
2780
|
+
source: "parser",
|
|
2781
|
+
code: ve.code,
|
|
2782
|
+
message: ve.message,
|
|
2783
|
+
component: ve.component,
|
|
2784
|
+
path: ve.path || void 0,
|
|
2785
|
+
statementId: ve.statementId
|
|
2786
|
+
};
|
|
2787
|
+
if (ve.code === "unknown-component" && componentNames.length) error.hint = `Available components: ${componentNames.join(", ")}`;
|
|
2788
|
+
else if (ve.code === "missing-required" || ve.code === "null-required") error.hint = buildSignatureHint(ve.component, schema.$defs?.[ve.component]);
|
|
2789
|
+
else if (ve.code === "inline-reserved") error.hint = `Declare as a top-level statement: myVar = ${ve.component}(...)`;
|
|
2790
|
+
return error;
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
function isReactiveAssign(value) {
|
|
2794
|
+
return typeof value === "object" && value !== null && value.__reactive === "assign";
|
|
2795
|
+
}
|
|
2796
|
+
function evaluate(node, context, schemaCtx) {
|
|
2797
|
+
switch (node.k) {
|
|
2798
|
+
case "Str":
|
|
2799
|
+
return node.v;
|
|
2800
|
+
case "Num":
|
|
2801
|
+
return node.v;
|
|
2802
|
+
case "Bool":
|
|
2803
|
+
return node.v;
|
|
2804
|
+
case "Null":
|
|
2805
|
+
return null;
|
|
2806
|
+
case "Ph":
|
|
2807
|
+
return null;
|
|
2808
|
+
case "StateRef":
|
|
2809
|
+
return context.extraScope?.[node.n] ?? context.getState(node.n);
|
|
2810
|
+
case "Ref":
|
|
2811
|
+
case "RuntimeRef":
|
|
2812
|
+
return context.resolveRef(node.n);
|
|
2813
|
+
case "Arr":
|
|
2814
|
+
return node.els.map((el) => evaluate(el, context));
|
|
2815
|
+
case "Obj":
|
|
2816
|
+
return Object.fromEntries(node.entries.map(([k, v]) => [k, evaluate(v, context)]));
|
|
2817
|
+
case "Comp": {
|
|
2818
|
+
if (LAZY_BUILTINS.has(node.name)) return evaluateLazyBuiltin(node.name, node.args, context, schemaCtx);
|
|
2819
|
+
const builtin = BUILTINS[node.name];
|
|
2820
|
+
if (builtin) {
|
|
2821
|
+
const args = node.args.map((a) => evaluate(a, context));
|
|
2822
|
+
return builtin.fn(...args);
|
|
2823
|
+
}
|
|
2824
|
+
if (ACTION_NAMES.has(node.name)) return evaluateActionCall(node.name, node.args, context);
|
|
2825
|
+
if (node.mappedProps) {
|
|
2826
|
+
const def = schemaCtx?.library.components[node.name];
|
|
2827
|
+
const props = {};
|
|
2828
|
+
for (const [key, val] of Object.entries(node.mappedProps)) {
|
|
2829
|
+
const propSchema = def?.props?.shape?.[key];
|
|
2830
|
+
if (val.k === "StateRef" && propSchema && isReactiveSchema(propSchema)) props[key] = {
|
|
2831
|
+
__reactive: "assign",
|
|
2832
|
+
target: val.n,
|
|
2833
|
+
expr: {
|
|
2834
|
+
k: "StateRef",
|
|
2835
|
+
n: "$value"
|
|
2836
|
+
}
|
|
2837
|
+
};
|
|
2838
|
+
else if (val.k === "StateRef") props[key] = schemaCtx ? context.getState(val.n) : val;
|
|
2839
|
+
else props[key] = evaluate(val, context, schemaCtx);
|
|
2840
|
+
}
|
|
2841
|
+
const result = {
|
|
2842
|
+
type: "element",
|
|
2843
|
+
typeName: node.name,
|
|
2844
|
+
props,
|
|
2845
|
+
partial: false,
|
|
2846
|
+
hasDynamicProps: true
|
|
2847
|
+
};
|
|
2848
|
+
if (schemaCtx) {
|
|
2849
|
+
for (const [key, val] of Object.entries(props)) if (isElementNode(val)) props[key] = evaluateElementInline(val, context, schemaCtx);
|
|
2850
|
+
else if (Array.isArray(val)) props[key] = val.map((item) => isElementNode(item) ? evaluateElementInline(item, context, schemaCtx) : item);
|
|
2851
|
+
}
|
|
2852
|
+
return result;
|
|
2853
|
+
}
|
|
2854
|
+
console.warn(`[openui] Unexpected unmapped Comp node: ${node.name}`);
|
|
2855
|
+
return null;
|
|
2856
|
+
}
|
|
2857
|
+
case "BinOp": {
|
|
2858
|
+
if (node.op === "&&") {
|
|
2859
|
+
const left2 = evaluate(node.left, context);
|
|
2860
|
+
return left2 ? evaluate(node.right, context) : left2;
|
|
2861
|
+
}
|
|
2862
|
+
if (node.op === "||") {
|
|
2863
|
+
const left2 = evaluate(node.left, context);
|
|
2864
|
+
return left2 ? left2 : evaluate(node.right, context);
|
|
2865
|
+
}
|
|
2866
|
+
const left = evaluate(node.left, context);
|
|
2867
|
+
const right = evaluate(node.right, context);
|
|
2868
|
+
switch (node.op) {
|
|
2869
|
+
case "+":
|
|
2870
|
+
if (typeof left === "string" || typeof right === "string") return String(left ?? "") + String(right ?? "");
|
|
2871
|
+
return toNumber(left) + toNumber(right);
|
|
2872
|
+
case "-":
|
|
2873
|
+
return toNumber(left) - toNumber(right);
|
|
2874
|
+
case "*":
|
|
2875
|
+
return toNumber(left) * toNumber(right);
|
|
2876
|
+
case "/":
|
|
2877
|
+
return toNumber(right) === 0 ? 0 : toNumber(left) / toNumber(right);
|
|
2878
|
+
case "%":
|
|
2879
|
+
return toNumber(right) === 0 ? 0 : toNumber(left) % toNumber(right);
|
|
2880
|
+
case "==":
|
|
2881
|
+
return left == right;
|
|
2882
|
+
case "!=":
|
|
2883
|
+
return left != right;
|
|
2884
|
+
case ">":
|
|
2885
|
+
return toNumber(left) > toNumber(right);
|
|
2886
|
+
case "<":
|
|
2887
|
+
return toNumber(left) < toNumber(right);
|
|
2888
|
+
case ">=":
|
|
2889
|
+
return toNumber(left) >= toNumber(right);
|
|
2890
|
+
case "<=":
|
|
2891
|
+
return toNumber(left) <= toNumber(right);
|
|
2892
|
+
default:
|
|
2893
|
+
return null;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
case "UnaryOp":
|
|
2897
|
+
if (node.op === "!") return !evaluate(node.operand, context);
|
|
2898
|
+
if (node.op === "-") return -toNumber(evaluate(node.operand, context));
|
|
2899
|
+
return null;
|
|
2900
|
+
case "Ternary":
|
|
2901
|
+
return evaluate(node.cond, context) ? evaluate(node.then, context) : evaluate(node.else, context);
|
|
2902
|
+
case "Member": {
|
|
2903
|
+
const obj = evaluate(node.obj, context);
|
|
2904
|
+
if (obj == null) return null;
|
|
2905
|
+
if (Array.isArray(obj)) {
|
|
2906
|
+
if (node.field === "length") return obj.length;
|
|
2907
|
+
return obj.map((item) => item?.[node.field] ?? null);
|
|
2908
|
+
}
|
|
2909
|
+
return obj[node.field];
|
|
2910
|
+
}
|
|
2911
|
+
case "Index": {
|
|
2912
|
+
const obj = evaluate(node.obj, context);
|
|
2913
|
+
const idx = evaluate(node.index, context);
|
|
2914
|
+
if (obj == null || idx == null) return null;
|
|
2915
|
+
if (Array.isArray(obj)) return obj[toNumber(idx)];
|
|
2916
|
+
return obj[String(idx)];
|
|
2917
|
+
}
|
|
2918
|
+
case "Assign":
|
|
2919
|
+
return {
|
|
2920
|
+
__reactive: "assign",
|
|
2921
|
+
target: node.target,
|
|
2922
|
+
expr: node.value
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
function evaluateElementInline(el, context, schemaCtx) {
|
|
2927
|
+
if (el.hasDynamicProps === false) return el;
|
|
2928
|
+
const def = schemaCtx.library.components[el.typeName];
|
|
2929
|
+
const evaluated = {};
|
|
2930
|
+
for (const [key, value] of Object.entries(el.props)) {
|
|
2931
|
+
const propSchema = def?.props?.shape?.[key];
|
|
2932
|
+
evaluated[key] = evaluatePropInline(value, context, schemaCtx, propSchema);
|
|
2933
|
+
}
|
|
2934
|
+
return {
|
|
2935
|
+
...el,
|
|
2936
|
+
props: evaluated
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
function evaluatePropInline(value, context, schemaCtx, reactiveSchema) {
|
|
2940
|
+
return evaluatePropCore(value, context, schemaCtx, reactiveSchema, {
|
|
2941
|
+
recurseElement: (el) => evaluateElementInline(el, context, schemaCtx),
|
|
2942
|
+
recurse: (v, rs) => evaluatePropInline(v, context, schemaCtx, rs)
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
function toLiteralAST(value) {
|
|
2946
|
+
if (value === null || value === void 0) return { k: "Null" };
|
|
2947
|
+
if (typeof value === "string") return {
|
|
2948
|
+
k: "Str",
|
|
2949
|
+
v: value
|
|
2950
|
+
};
|
|
2951
|
+
if (typeof value === "number") return {
|
|
2952
|
+
k: "Num",
|
|
2953
|
+
v: value
|
|
2954
|
+
};
|
|
2955
|
+
if (typeof value === "boolean") return {
|
|
2956
|
+
k: "Bool",
|
|
2957
|
+
v: value
|
|
2958
|
+
};
|
|
2959
|
+
if (Array.isArray(value)) return {
|
|
2960
|
+
k: "Arr",
|
|
2961
|
+
els: value.map(toLiteralAST)
|
|
2962
|
+
};
|
|
2963
|
+
if (typeof value === "object") return {
|
|
2964
|
+
k: "Obj",
|
|
2965
|
+
entries: Object.entries(value).map(([k, v]) => [k, toLiteralAST(v)])
|
|
2966
|
+
};
|
|
2967
|
+
return { k: "Null" };
|
|
2968
|
+
}
|
|
2969
|
+
function evaluateActionCall(name, args, context) {
|
|
2970
|
+
switch (name) {
|
|
2971
|
+
case "Action": {
|
|
2972
|
+
const stepsArg = args.length > 0 ? evaluate(args[0], context) : [];
|
|
2973
|
+
return { steps: (Array.isArray(stepsArg) ? stepsArg : []).filter((s) => s != null && typeof s === "object" && "type" in s) };
|
|
2974
|
+
}
|
|
2975
|
+
case "Run": {
|
|
2976
|
+
if (args.length === 0) return null;
|
|
2977
|
+
const refNode = args[0];
|
|
2978
|
+
if (refNode.k === "RuntimeRef") return {
|
|
2979
|
+
type: ACTION_STEPS.Run,
|
|
2980
|
+
statementId: refNode.n,
|
|
2981
|
+
refType: refNode.refType
|
|
2982
|
+
};
|
|
2983
|
+
return null;
|
|
2984
|
+
}
|
|
2985
|
+
case "ToAssistant": {
|
|
2986
|
+
const message = args.length > 0 ? String(evaluate(args[0], context) ?? "") : "";
|
|
2987
|
+
const ctx = args.length > 1 ? String(evaluate(args[1], context) ?? "") : void 0;
|
|
2988
|
+
return {
|
|
2989
|
+
type: ACTION_STEPS.ToAssistant,
|
|
2990
|
+
message,
|
|
2991
|
+
context: ctx
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
case "OpenUrl": {
|
|
2995
|
+
const url = args.length > 0 ? String(evaluate(args[0], context) ?? "") : "";
|
|
2996
|
+
return {
|
|
2997
|
+
type: ACTION_STEPS.OpenUrl,
|
|
2998
|
+
url
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
case "Set": {
|
|
3002
|
+
if (args.length < 2) return null;
|
|
3003
|
+
const targetNode = args[0];
|
|
3004
|
+
if (targetNode.k !== "StateRef") return null;
|
|
3005
|
+
return {
|
|
3006
|
+
type: ACTION_STEPS.Set,
|
|
3007
|
+
target: targetNode.n,
|
|
3008
|
+
valueAST: args[1]
|
|
3009
|
+
};
|
|
3010
|
+
}
|
|
3011
|
+
case "Reset": {
|
|
3012
|
+
const targets = args.filter((a) => a.k === "StateRef").map((a) => a.n);
|
|
3013
|
+
if (targets.length === 0) return null;
|
|
3014
|
+
return {
|
|
3015
|
+
type: ACTION_STEPS.Reset,
|
|
3016
|
+
targets
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
default:
|
|
3020
|
+
return null;
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
function substituteRef(node, varName, value) {
|
|
3024
|
+
switch (node.k) {
|
|
3025
|
+
case "Ref":
|
|
3026
|
+
return node.n === varName ? toLiteralAST(value) : node;
|
|
3027
|
+
case "Member":
|
|
3028
|
+
if (isASTNode(node.obj)) {
|
|
3029
|
+
const subObj = substituteRef(node.obj, varName, value);
|
|
3030
|
+
if (subObj.k === "Obj") {
|
|
3031
|
+
const entry = subObj.entries.find(([k]) => k === node.field);
|
|
3032
|
+
if (entry) return entry[1];
|
|
3033
|
+
}
|
|
3034
|
+
return {
|
|
3035
|
+
...node,
|
|
3036
|
+
obj: subObj
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
return node;
|
|
3040
|
+
case "Index":
|
|
3041
|
+
return {
|
|
3042
|
+
...node,
|
|
3043
|
+
obj: isASTNode(node.obj) ? substituteRef(node.obj, varName, value) : node.obj,
|
|
3044
|
+
index: isASTNode(node.index) ? substituteRef(node.index, varName, value) : node.index
|
|
3045
|
+
};
|
|
3046
|
+
case "BinOp":
|
|
3047
|
+
return {
|
|
3048
|
+
...node,
|
|
3049
|
+
left: substituteRef(node.left, varName, value),
|
|
3050
|
+
right: substituteRef(node.right, varName, value)
|
|
3051
|
+
};
|
|
3052
|
+
case "UnaryOp":
|
|
3053
|
+
return {
|
|
3054
|
+
...node,
|
|
3055
|
+
operand: substituteRef(node.operand, varName, value)
|
|
3056
|
+
};
|
|
3057
|
+
case "Ternary":
|
|
3058
|
+
return {
|
|
3059
|
+
...node,
|
|
3060
|
+
cond: substituteRef(node.cond, varName, value),
|
|
3061
|
+
then: substituteRef(node.then, varName, value),
|
|
3062
|
+
else: substituteRef(node.else, varName, value)
|
|
3063
|
+
};
|
|
3064
|
+
case "Arr":
|
|
3065
|
+
return {
|
|
3066
|
+
...node,
|
|
3067
|
+
els: node.els.map((e) => substituteRef(e, varName, value))
|
|
3068
|
+
};
|
|
3069
|
+
case "Obj":
|
|
3070
|
+
return {
|
|
3071
|
+
...node,
|
|
3072
|
+
entries: node.entries.map(([k, v]) => [k, substituteRef(v, varName, value)])
|
|
3073
|
+
};
|
|
3074
|
+
case "Comp": {
|
|
3075
|
+
const result = {
|
|
3076
|
+
...node,
|
|
3077
|
+
args: node.args.map((a) => substituteRef(a, varName, value))
|
|
3078
|
+
};
|
|
3079
|
+
if (node.mappedProps) {
|
|
3080
|
+
const subProps = {};
|
|
3081
|
+
for (const [k, v] of Object.entries(node.mappedProps)) subProps[k] = substituteRef(v, varName, value);
|
|
3082
|
+
result.mappedProps = subProps;
|
|
3083
|
+
}
|
|
3084
|
+
return result;
|
|
3085
|
+
}
|
|
3086
|
+
case "Assign":
|
|
3087
|
+
return {
|
|
3088
|
+
...node,
|
|
3089
|
+
value: substituteRef(node.value, varName, value)
|
|
3090
|
+
};
|
|
3091
|
+
default:
|
|
3092
|
+
return node;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
function evaluateLazyBuiltin(name, args, context, schemaCtx) {
|
|
3096
|
+
if (name === "Each") {
|
|
3097
|
+
if (args.length < 3) return [];
|
|
3098
|
+
const arr = evaluate(args[0], context);
|
|
3099
|
+
if (!Array.isArray(arr)) return [];
|
|
3100
|
+
const varName = args[1].k === "Ref" ? args[1].n : args[1].k === "Str" ? args[1].v : null;
|
|
3101
|
+
if (!varName) return [];
|
|
3102
|
+
const template = args[2];
|
|
3103
|
+
return arr.map((item, _idx) => {
|
|
3104
|
+
const substituted = substituteRef(template, varName, item);
|
|
3105
|
+
const childCtx = {
|
|
3106
|
+
...context,
|
|
3107
|
+
resolveRef: (refName) => {
|
|
3108
|
+
if (refName === varName) return item;
|
|
3109
|
+
return context.resolveRef(refName);
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3112
|
+
const result = evaluate(substituted, childCtx, schemaCtx);
|
|
3113
|
+
if (schemaCtx && isElementNode(result)) return evaluateElementInline(result, childCtx, schemaCtx);
|
|
3114
|
+
return result;
|
|
3115
|
+
});
|
|
3116
|
+
}
|
|
3117
|
+
return null;
|
|
3118
|
+
}
|
|
3119
|
+
function evaluatePropCore(value, context, schemaCtx, reactiveSchema, callbacks) {
|
|
3120
|
+
if (value == null) return value;
|
|
3121
|
+
if (typeof value !== "object") return value;
|
|
3122
|
+
if (isASTNode(value)) {
|
|
3123
|
+
if (value.k === "StateRef" && reactiveSchema && isReactiveSchema(reactiveSchema)) return {
|
|
3124
|
+
__reactive: "assign",
|
|
3125
|
+
target: value.n,
|
|
3126
|
+
expr: {
|
|
3127
|
+
k: "StateRef",
|
|
3128
|
+
n: "$value"
|
|
3129
|
+
}
|
|
3130
|
+
};
|
|
3131
|
+
const result = evaluate(value, context, schemaCtx);
|
|
3132
|
+
if (isElementNode(result)) return callbacks.recurseElement(result);
|
|
3133
|
+
if (Array.isArray(result)) return result.map((item) => isElementNode(item) ? callbacks.recurseElement(item) : item);
|
|
3134
|
+
if (isReactiveAssign(result) && !(reactiveSchema && isReactiveSchema(reactiveSchema))) return context.getState(result.target) ?? null;
|
|
3135
|
+
return result;
|
|
3136
|
+
}
|
|
3137
|
+
if (typeof value === "string" && reactiveSchema && isReactiveSchema(reactiveSchema)) return value;
|
|
3138
|
+
if (Array.isArray(value)) return value.map((v) => callbacks.recurse(v, reactiveSchema));
|
|
3139
|
+
if (isElementNode(value)) return callbacks.recurseElement(value);
|
|
3140
|
+
const obj = value;
|
|
3141
|
+
if ("steps" in obj && Array.isArray(obj.steps)) return value;
|
|
3142
|
+
if ("type" in obj && "valueAST" in obj) return value;
|
|
3143
|
+
let needsEval = false;
|
|
3144
|
+
for (const val of Object.values(obj)) if (typeof val === "object" && val !== null) {
|
|
3145
|
+
needsEval = true;
|
|
3146
|
+
break;
|
|
3147
|
+
}
|
|
3148
|
+
if (needsEval) {
|
|
3149
|
+
const result = {};
|
|
3150
|
+
for (const [k, v] of Object.entries(obj)) result[k] = callbacks.recurse(v, reactiveSchema);
|
|
3151
|
+
return result;
|
|
3152
|
+
}
|
|
3153
|
+
return value;
|
|
3154
|
+
}
|
|
3155
|
+
function evaluateElementProps(el, evalCtx) {
|
|
3156
|
+
if (el.hasDynamicProps === false) return el;
|
|
3157
|
+
const schemaCtx = { library: evalCtx.library };
|
|
3158
|
+
const def = evalCtx.library.components[el.typeName];
|
|
3159
|
+
const evaluated = {};
|
|
3160
|
+
for (const [key, value] of Object.entries(el.props)) {
|
|
3161
|
+
const propSchema = def?.props?.shape?.[key];
|
|
3162
|
+
try {
|
|
3163
|
+
evaluated[key] = evaluatePropValue(value, evalCtx, schemaCtx, propSchema);
|
|
3164
|
+
} catch (e) {
|
|
3165
|
+
evaluated[key] = value;
|
|
3166
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3167
|
+
evalCtx.errors?.push({
|
|
3168
|
+
source: "runtime",
|
|
3169
|
+
code: "runtime-error",
|
|
3170
|
+
component: el.typeName,
|
|
3171
|
+
statementId: el.statementId,
|
|
3172
|
+
message: `Evaluating prop "${key}" on ${el.typeName} failed: ${msg}`,
|
|
3173
|
+
hint: `Check the expression used for prop "${key}"`
|
|
3174
|
+
});
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
return {
|
|
3178
|
+
...el,
|
|
3179
|
+
props: evaluated
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
function evaluatePropValue(value, evalCtx, schemaCtx, reactiveSchema) {
|
|
3183
|
+
return evaluatePropCore(value, evalCtx.ctx, schemaCtx, reactiveSchema, {
|
|
3184
|
+
recurseElement: (el) => evaluateElementProps(el, evalCtx),
|
|
3185
|
+
recurse: (v, rs) => evaluatePropValue(v, evalCtx, schemaCtx, rs)
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
var McpToolError = class extends Error {
|
|
3189
|
+
toolErrorText;
|
|
3190
|
+
constructor(errorText) {
|
|
3191
|
+
super(`MCP tool error: ${errorText || "Unknown error"}`);
|
|
3192
|
+
this.name = "McpToolError";
|
|
3193
|
+
this.toolErrorText = errorText;
|
|
3194
|
+
}
|
|
3195
|
+
};
|
|
3196
|
+
function extractToolResult(result) {
|
|
3197
|
+
if (result.isError) {
|
|
3198
|
+
const errorText = result.content?.filter((c) => c.type === "text").map((c) => c.text).join("\n");
|
|
3199
|
+
throw new McpToolError(errorText || "Unknown error");
|
|
3200
|
+
}
|
|
3201
|
+
if (result.structuredContent != null) return result.structuredContent;
|
|
3202
|
+
const textParts = result.content?.filter((c) => c.type === "text").map((c) => c.text ?? "");
|
|
3203
|
+
if (textParts?.length) {
|
|
3204
|
+
const text = textParts.join("");
|
|
3205
|
+
try {
|
|
3206
|
+
return JSON.parse(text);
|
|
3207
|
+
} catch {
|
|
3208
|
+
return text;
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
return null;
|
|
3212
|
+
}
|
|
3213
|
+
var ToolNotFoundError = class extends Error {
|
|
3214
|
+
toolName;
|
|
3215
|
+
availableTools;
|
|
3216
|
+
constructor(toolName, availableTools = []) {
|
|
3217
|
+
super(`[openui] No handler for tool "${toolName}". Available: ${availableTools.join(", ") || "(none)"}`);
|
|
3218
|
+
this.name = "ToolNotFoundError";
|
|
3219
|
+
this.toolName = toolName;
|
|
3220
|
+
this.availableTools = availableTools;
|
|
3221
|
+
}
|
|
3222
|
+
};
|
|
3223
|
+
function stableStringify(value) {
|
|
3224
|
+
return JSON.stringify(value, (_key, val) => {
|
|
3225
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
3226
|
+
const sorted = {};
|
|
3227
|
+
for (const k of Object.keys(val).sort()) sorted[k] = val[k];
|
|
3228
|
+
return sorted;
|
|
3229
|
+
}
|
|
3230
|
+
if (val === void 0) return "__undefined__";
|
|
3231
|
+
if (typeof val === "number") {
|
|
3232
|
+
if (Number.isNaN(val)) return "__NaN__";
|
|
3233
|
+
if (val === Infinity) return "__Inf__";
|
|
3234
|
+
if (val === -Infinity) return "__-Inf__";
|
|
3235
|
+
}
|
|
3236
|
+
return val;
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
function buildCacheKey(toolName, args, deps) {
|
|
3240
|
+
const depsKey = deps != null ? "::" + stableStringify(deps) : "";
|
|
3241
|
+
return toolName + "::" + stableStringify(args) + depsKey;
|
|
3242
|
+
}
|
|
3243
|
+
function createQueryManager(toolProvider) {
|
|
3244
|
+
const queries = /* @__PURE__ */ new Map();
|
|
3245
|
+
const mutations = /* @__PURE__ */ new Map();
|
|
3246
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3247
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3248
|
+
let snapshot = {
|
|
3249
|
+
__openui_loading: [],
|
|
3250
|
+
__openui_refetching: [],
|
|
3251
|
+
__openui_errors: []
|
|
3252
|
+
};
|
|
3253
|
+
let snapshotJson = JSON.stringify(snapshot);
|
|
3254
|
+
let disposed = false;
|
|
3255
|
+
let generation = 0;
|
|
3256
|
+
function rebuildSnapshot() {
|
|
3257
|
+
const out = {
|
|
3258
|
+
__openui_loading: [],
|
|
3259
|
+
__openui_refetching: [],
|
|
3260
|
+
__openui_errors: []
|
|
3261
|
+
};
|
|
3262
|
+
for (const [sid, q] of queries) {
|
|
3263
|
+
const entry = cache.get(q.cacheKey);
|
|
3264
|
+
if (entry && entry.data !== void 0) out[sid] = entry.data;
|
|
3265
|
+
else if (q.prevCacheKey) {
|
|
3266
|
+
const prev = cache.get(q.prevCacheKey);
|
|
3267
|
+
if (prev && prev.data !== void 0) out[sid] = prev.data;
|
|
3268
|
+
else out[sid] = q.defaults;
|
|
3269
|
+
} else out[sid] = q.defaults;
|
|
3270
|
+
if (q.loading) {
|
|
3271
|
+
out.__openui_loading.push(sid);
|
|
3272
|
+
if (q.everFetched) out.__openui_refetching.push(sid);
|
|
3273
|
+
}
|
|
3274
|
+
if (q.error) out.__openui_errors.push(q.error);
|
|
3275
|
+
}
|
|
3276
|
+
for (const [sid, m] of mutations) {
|
|
3277
|
+
out[sid] = m.result;
|
|
3278
|
+
if (m.error) out.__openui_errors.push(m.error);
|
|
3279
|
+
}
|
|
3280
|
+
try {
|
|
3281
|
+
const outJson = JSON.stringify(out);
|
|
3282
|
+
if (outJson === snapshotJson) return false;
|
|
3283
|
+
snapshot = out;
|
|
3284
|
+
snapshotJson = outJson;
|
|
3285
|
+
} catch {
|
|
3286
|
+
snapshot = out;
|
|
3287
|
+
snapshotJson = "";
|
|
3288
|
+
}
|
|
3289
|
+
return true;
|
|
3290
|
+
}
|
|
3291
|
+
function notify() {
|
|
3292
|
+
for (const listener of [...listeners]) listener();
|
|
3293
|
+
}
|
|
3294
|
+
async function executeFetch(cacheKey, statementId) {
|
|
3295
|
+
if (!toolProvider) return;
|
|
3296
|
+
const q = queries.get(statementId);
|
|
3297
|
+
if (!q) return;
|
|
3298
|
+
const fetchKey = cacheKey;
|
|
3299
|
+
const toolName = q.toolName;
|
|
3300
|
+
const args = q.args;
|
|
3301
|
+
let entry = cache.get(fetchKey);
|
|
3302
|
+
if (!entry) {
|
|
3303
|
+
entry = {
|
|
3304
|
+
data: void 0,
|
|
3305
|
+
inFlight: true
|
|
3306
|
+
};
|
|
3307
|
+
cache.set(fetchKey, entry);
|
|
3308
|
+
} else entry.inFlight = true;
|
|
3309
|
+
q.loading = true;
|
|
3310
|
+
rebuildSnapshot();
|
|
3311
|
+
notify();
|
|
3312
|
+
try {
|
|
3313
|
+
const data = await toolProvider.callTool(toolName, args ?? {});
|
|
3314
|
+
if (disposed) return;
|
|
3315
|
+
const current = queries.get(statementId);
|
|
3316
|
+
if (!current || current.cacheKey !== fetchKey) {
|
|
3317
|
+
entry.inFlight = false;
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
entry.data = data ?? null;
|
|
3321
|
+
current.everFetched = true;
|
|
3322
|
+
current.error = void 0;
|
|
3323
|
+
if (current.prevCacheKey && current.prevCacheKey !== fetchKey) {
|
|
3324
|
+
const prevKey = current.prevCacheKey;
|
|
3325
|
+
current.prevCacheKey = void 0;
|
|
3326
|
+
cleanupCacheEntry(prevKey);
|
|
3327
|
+
}
|
|
3328
|
+
} catch (err) {
|
|
3329
|
+
const current = queries.get(statementId);
|
|
3330
|
+
if (current && current.cacheKey === fetchKey) if (err instanceof ToolNotFoundError) current.error = {
|
|
3331
|
+
source: "query",
|
|
3332
|
+
code: "tool-not-found",
|
|
3333
|
+
message: `Query tool "${toolName}" not found`,
|
|
3334
|
+
statementId,
|
|
3335
|
+
component: "Query",
|
|
3336
|
+
toolName,
|
|
3337
|
+
hint: err.availableTools.length ? `Available tools: ${err.availableTools.join(", ")}` : void 0
|
|
3338
|
+
};
|
|
3339
|
+
else if (err instanceof McpToolError) current.error = {
|
|
3340
|
+
source: "query",
|
|
3341
|
+
code: "mcp-error",
|
|
3342
|
+
message: `Query "${toolName}" returned an error: ${err.toolErrorText}`,
|
|
3343
|
+
statementId,
|
|
3344
|
+
component: "Query",
|
|
3345
|
+
toolName
|
|
3346
|
+
};
|
|
3347
|
+
else current.error = {
|
|
3348
|
+
source: "query",
|
|
3349
|
+
code: "tool-error",
|
|
3350
|
+
message: `Query "${toolName}" failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
3351
|
+
statementId,
|
|
3352
|
+
component: "Query",
|
|
3353
|
+
toolName
|
|
3354
|
+
};
|
|
3355
|
+
console.error(`Query "${toolName}" failed:`, err);
|
|
3356
|
+
} finally {
|
|
3357
|
+
entry.inFlight = false;
|
|
3358
|
+
const current = queries.get(statementId);
|
|
3359
|
+
if (current && current.cacheKey === fetchKey) {
|
|
3360
|
+
current.loading = false;
|
|
3361
|
+
if (rebuildSnapshot()) notify();
|
|
3362
|
+
if (current.needsRefetch) {
|
|
3363
|
+
current.needsRefetch = false;
|
|
3364
|
+
executeFetch(current.cacheKey, statementId);
|
|
3365
|
+
}
|
|
3366
|
+
} else if (rebuildSnapshot()) notify();
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
function cleanupCacheEntry(cacheKey) {
|
|
3370
|
+
for (const q of queries.values()) if (q.cacheKey === cacheKey || q.prevCacheKey === cacheKey) return;
|
|
3371
|
+
cache.delete(cacheKey);
|
|
3372
|
+
}
|
|
3373
|
+
function evaluateQueries(queryNodes) {
|
|
3374
|
+
if (disposed) return;
|
|
3375
|
+
const activeIds = new Set(queryNodes.map((n) => n.statementId));
|
|
3376
|
+
for (const [sid, q] of queries) if (!activeIds.has(sid)) {
|
|
3377
|
+
if (q.timer) clearInterval(q.timer);
|
|
3378
|
+
queries.delete(sid);
|
|
3379
|
+
cleanupCacheEntry(q.cacheKey);
|
|
3380
|
+
if (q.prevCacheKey) cleanupCacheEntry(q.prevCacheKey);
|
|
3381
|
+
}
|
|
3382
|
+
for (const node of queryNodes) {
|
|
3383
|
+
if (!node.complete) continue;
|
|
3384
|
+
const cacheKey = buildCacheKey(node.toolName, node.args, node.deps);
|
|
3385
|
+
const existing = queries.get(node.statementId);
|
|
3386
|
+
if (existing) {
|
|
3387
|
+
if (existing.cacheKey !== cacheKey) existing.prevCacheKey = existing.cacheKey;
|
|
3388
|
+
existing.toolName = node.toolName;
|
|
3389
|
+
existing.args = node.args;
|
|
3390
|
+
existing.defaults = node.defaults;
|
|
3391
|
+
existing.cacheKey = cacheKey;
|
|
3392
|
+
} else queries.set(node.statementId, {
|
|
3393
|
+
toolName: node.toolName,
|
|
3394
|
+
args: node.args,
|
|
3395
|
+
defaults: node.defaults,
|
|
3396
|
+
cacheKey,
|
|
3397
|
+
loading: false,
|
|
3398
|
+
everFetched: false,
|
|
3399
|
+
refreshInterval: 0,
|
|
3400
|
+
needsRefetch: false
|
|
3401
|
+
});
|
|
3402
|
+
const q = queries.get(node.statementId);
|
|
3403
|
+
const entry = cache.get(cacheKey);
|
|
3404
|
+
const hasSettledData = entry && entry.data !== void 0 && !entry.inFlight;
|
|
3405
|
+
if (toolProvider && !hasSettledData && !entry?.inFlight) executeFetch(cacheKey, node.statementId);
|
|
3406
|
+
const newInterval = node.refreshInterval ?? 0;
|
|
3407
|
+
if (newInterval !== q.refreshInterval) {
|
|
3408
|
+
if (q.timer) {
|
|
3409
|
+
clearInterval(q.timer);
|
|
3410
|
+
q.timer = void 0;
|
|
3411
|
+
}
|
|
3412
|
+
if (newInterval > 0) q.timer = setInterval(() => {
|
|
3413
|
+
if (disposed || !toolProvider) return;
|
|
3414
|
+
if (!cache.get(q.cacheKey)?.inFlight) executeFetch(q.cacheKey, node.statementId);
|
|
3415
|
+
}, newInterval * 1e3);
|
|
3416
|
+
q.refreshInterval = newInterval;
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
if (rebuildSnapshot()) notify();
|
|
3420
|
+
}
|
|
3421
|
+
function getResult(statementId) {
|
|
3422
|
+
const q = queries.get(statementId);
|
|
3423
|
+
if (!q) return null;
|
|
3424
|
+
const entry = cache.get(q.cacheKey);
|
|
3425
|
+
if (entry && entry.data !== void 0) return entry.data;
|
|
3426
|
+
if (q.prevCacheKey) {
|
|
3427
|
+
const prev = cache.get(q.prevCacheKey);
|
|
3428
|
+
if (prev && prev.data !== void 0) return prev.data;
|
|
3429
|
+
}
|
|
3430
|
+
return q.defaults;
|
|
3431
|
+
}
|
|
3432
|
+
function isLoading(statementId) {
|
|
3433
|
+
return queries.get(statementId)?.loading ?? false;
|
|
3434
|
+
}
|
|
3435
|
+
function isAnyLoading() {
|
|
3436
|
+
for (const q of queries.values()) if (q.loading) return true;
|
|
3437
|
+
return false;
|
|
3438
|
+
}
|
|
3439
|
+
function invalidate(statementIds) {
|
|
3440
|
+
if (disposed || !toolProvider) return;
|
|
3441
|
+
const targets = statementIds?.length ? statementIds.filter((sid) => queries.has(sid)) : [...queries.keys()];
|
|
3442
|
+
for (const sid of targets) {
|
|
3443
|
+
const q = queries.get(sid);
|
|
3444
|
+
if (!q) continue;
|
|
3445
|
+
if (cache.get(q.cacheKey)?.inFlight) q.needsRefetch = true;
|
|
3446
|
+
else executeFetch(q.cacheKey, sid);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
function registerMutations(nodes) {
|
|
3450
|
+
const activeIds = new Set(nodes.map((n) => n.statementId));
|
|
3451
|
+
for (const sid of mutations.keys()) if (!activeIds.has(sid)) mutations.delete(sid);
|
|
3452
|
+
for (const node of nodes) {
|
|
3453
|
+
const existing = mutations.get(node.statementId);
|
|
3454
|
+
if (existing) {
|
|
3455
|
+
if (existing.toolName !== node.toolName) {
|
|
3456
|
+
existing.toolName = node.toolName;
|
|
3457
|
+
existing.result = {
|
|
3458
|
+
status: "idle",
|
|
3459
|
+
data: null,
|
|
3460
|
+
error: null
|
|
3461
|
+
};
|
|
3462
|
+
existing.error = void 0;
|
|
3463
|
+
}
|
|
3464
|
+
} else mutations.set(node.statementId, {
|
|
3465
|
+
toolName: node.toolName,
|
|
3466
|
+
result: { status: "idle" }
|
|
3467
|
+
});
|
|
3468
|
+
}
|
|
3469
|
+
if (rebuildSnapshot()) notify();
|
|
3470
|
+
}
|
|
3471
|
+
async function fireMutation(statementId, evaluatedArgs, refreshQueryIds) {
|
|
3472
|
+
if (disposed || !toolProvider) return false;
|
|
3473
|
+
const m = mutations.get(statementId);
|
|
3474
|
+
if (!m) return false;
|
|
3475
|
+
if (m.result.status === "loading") return false;
|
|
3476
|
+
const gen = generation;
|
|
3477
|
+
m.result = { status: "loading" };
|
|
3478
|
+
rebuildSnapshot();
|
|
3479
|
+
notify();
|
|
3480
|
+
let success = false;
|
|
3481
|
+
try {
|
|
3482
|
+
const data = await toolProvider.callTool(m.toolName, evaluatedArgs);
|
|
3483
|
+
if (disposed || gen !== generation) return false;
|
|
3484
|
+
m.result = {
|
|
3485
|
+
status: "success",
|
|
3486
|
+
data
|
|
3487
|
+
};
|
|
3488
|
+
m.error = void 0;
|
|
3489
|
+
success = true;
|
|
3490
|
+
} catch (err) {
|
|
3491
|
+
if (disposed || gen !== generation) return false;
|
|
3492
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3493
|
+
m.result = {
|
|
3494
|
+
status: "error",
|
|
3495
|
+
error: msg
|
|
3496
|
+
};
|
|
3497
|
+
if (err instanceof ToolNotFoundError) m.error = {
|
|
3498
|
+
source: "mutation",
|
|
3499
|
+
code: "tool-not-found",
|
|
3500
|
+
message: `Mutation tool "${m.toolName}" not found`,
|
|
3501
|
+
statementId,
|
|
3502
|
+
component: "Mutation",
|
|
3503
|
+
toolName: m.toolName,
|
|
3504
|
+
hint: err.availableTools.length ? `Available tools: ${err.availableTools.join(", ")}` : void 0
|
|
3505
|
+
};
|
|
3506
|
+
else if (err instanceof McpToolError) m.error = {
|
|
3507
|
+
source: "mutation",
|
|
3508
|
+
code: "mcp-error",
|
|
3509
|
+
message: `Mutation "${m.toolName}" returned an error: ${err.toolErrorText}`,
|
|
3510
|
+
statementId,
|
|
3511
|
+
component: "Mutation",
|
|
3512
|
+
toolName: m.toolName
|
|
3513
|
+
};
|
|
3514
|
+
else m.error = {
|
|
3515
|
+
source: "mutation",
|
|
3516
|
+
code: "tool-error",
|
|
3517
|
+
message: `Mutation "${m.toolName}" failed: ${msg}`,
|
|
3518
|
+
statementId,
|
|
3519
|
+
component: "Mutation",
|
|
3520
|
+
toolName: m.toolName
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
rebuildSnapshot();
|
|
3524
|
+
notify();
|
|
3525
|
+
if (success && refreshQueryIds?.length) invalidate(refreshQueryIds);
|
|
3526
|
+
return success;
|
|
3527
|
+
}
|
|
3528
|
+
function getMutationResult(statementId) {
|
|
3529
|
+
return mutations.get(statementId)?.result ?? null;
|
|
3530
|
+
}
|
|
3531
|
+
function subscribe(listener) {
|
|
3532
|
+
listeners.add(listener);
|
|
3533
|
+
return () => listeners.delete(listener);
|
|
3534
|
+
}
|
|
3535
|
+
function getSnapshot() {
|
|
3536
|
+
return snapshot;
|
|
3537
|
+
}
|
|
3538
|
+
function activate() {
|
|
3539
|
+
disposed = false;
|
|
3540
|
+
}
|
|
3541
|
+
function dispose() {
|
|
3542
|
+
disposed = true;
|
|
3543
|
+
generation++;
|
|
3544
|
+
listeners.clear();
|
|
3545
|
+
for (const q of queries.values()) {
|
|
3546
|
+
if (q.timer) {
|
|
3547
|
+
clearInterval(q.timer);
|
|
3548
|
+
q.timer = void 0;
|
|
3549
|
+
}
|
|
3550
|
+
q.refreshInterval = 0;
|
|
3551
|
+
q.loading = false;
|
|
3552
|
+
q.needsRefetch = false;
|
|
3553
|
+
}
|
|
3554
|
+
mutations.clear();
|
|
3555
|
+
}
|
|
3556
|
+
return {
|
|
3557
|
+
evaluateQueries,
|
|
3558
|
+
getResult,
|
|
3559
|
+
isLoading,
|
|
3560
|
+
isAnyLoading,
|
|
3561
|
+
invalidate,
|
|
3562
|
+
registerMutations,
|
|
3563
|
+
fireMutation,
|
|
3564
|
+
getMutationResult,
|
|
3565
|
+
subscribe,
|
|
3566
|
+
getSnapshot,
|
|
3567
|
+
activate,
|
|
3568
|
+
dispose
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
function resolveStateField(name, bindingValue, store, evaluationContext, fieldGetter, fieldSetter) {
|
|
3572
|
+
if (isReactiveAssign(bindingValue) && store && evaluationContext) {
|
|
3573
|
+
const { target, expr } = bindingValue;
|
|
3574
|
+
return {
|
|
3575
|
+
name,
|
|
3576
|
+
value: store.get(target),
|
|
3577
|
+
setValue: (value) => {
|
|
3578
|
+
const extraScope = { $value: value };
|
|
3579
|
+
const nextValue = evaluate(expr, {
|
|
3580
|
+
...evaluationContext,
|
|
3581
|
+
extraScope
|
|
3582
|
+
});
|
|
3583
|
+
store.set(target, nextValue);
|
|
3584
|
+
},
|
|
3585
|
+
isReactive: true
|
|
3586
|
+
};
|
|
3587
|
+
}
|
|
3588
|
+
return {
|
|
3589
|
+
name,
|
|
3590
|
+
value: fieldGetter(name) ?? bindingValue,
|
|
3591
|
+
setValue: (value) => fieldSetter(name, value),
|
|
3592
|
+
isReactive: false
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3595
|
+
function createStore() {
|
|
3596
|
+
const state = /* @__PURE__ */ new Map();
|
|
3597
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3598
|
+
let snapshot = {};
|
|
3599
|
+
function notify() {
|
|
3600
|
+
const currentListeners = [...listeners];
|
|
3601
|
+
for (const listener of currentListeners) listener();
|
|
3602
|
+
}
|
|
3603
|
+
function rebuildSnapshot() {
|
|
3604
|
+
snapshot = Object.fromEntries(state);
|
|
3605
|
+
}
|
|
3606
|
+
function get(name) {
|
|
3607
|
+
return state.get(name);
|
|
3608
|
+
}
|
|
3609
|
+
function set(name, value) {
|
|
3610
|
+
const existing = state.get(name);
|
|
3611
|
+
if (Object.is(existing, value)) return;
|
|
3612
|
+
if (value && existing && typeof value === "object" && typeof existing === "object" && !Array.isArray(value) && !Array.isArray(existing)) {
|
|
3613
|
+
const nk = Object.keys(value);
|
|
3614
|
+
const ok = Object.keys(existing);
|
|
3615
|
+
if (nk.length === ok.length && nk.every((k) => Object.is(value[k], existing[k]))) return;
|
|
3616
|
+
}
|
|
3617
|
+
state.set(name, value);
|
|
3618
|
+
rebuildSnapshot();
|
|
3619
|
+
notify();
|
|
3620
|
+
}
|
|
3621
|
+
function subscribe(listener) {
|
|
3622
|
+
listeners.add(listener);
|
|
3623
|
+
return () => {
|
|
3624
|
+
listeners.delete(listener);
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
function getSnapshot() {
|
|
3628
|
+
return snapshot;
|
|
3629
|
+
}
|
|
3630
|
+
function initialize(defaults, persisted) {
|
|
3631
|
+
for (const key of Object.keys(persisted)) state.set(key, persisted[key]);
|
|
3632
|
+
for (const key of Object.keys(defaults)) if (!state.has(key)) state.set(key, defaults[key]);
|
|
3633
|
+
rebuildSnapshot();
|
|
3634
|
+
notify();
|
|
3635
|
+
}
|
|
3636
|
+
function dispose() {
|
|
3637
|
+
state.clear();
|
|
3638
|
+
listeners.clear();
|
|
3639
|
+
snapshot = {};
|
|
3640
|
+
}
|
|
3641
|
+
return {
|
|
3642
|
+
get,
|
|
3643
|
+
set,
|
|
3644
|
+
subscribe,
|
|
3645
|
+
getSnapshot,
|
|
3646
|
+
initialize,
|
|
3647
|
+
dispose
|
|
3648
|
+
};
|
|
3649
|
+
}
|
|
3650
|
+
function isEmpty(value) {
|
|
3651
|
+
if (value === null || value === void 0 || value === "") return true;
|
|
3652
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
3653
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "value" in value) return isEmpty(value.value);
|
|
3654
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 0) return true;
|
|
3655
|
+
return false;
|
|
3656
|
+
}
|
|
3657
|
+
var builtInValidators = {
|
|
3658
|
+
required: (value) => {
|
|
3659
|
+
if (isEmpty(value)) return "This field is required";
|
|
3660
|
+
if (typeof value === "object" && !Array.isArray(value) && value !== null) {
|
|
3661
|
+
const vals = Object.values(value);
|
|
3662
|
+
if (vals.length > 0 && vals.every((v) => typeof v === "boolean") && !vals.some(Boolean)) return "At least one option is required";
|
|
3663
|
+
}
|
|
3664
|
+
},
|
|
3665
|
+
email: (value) => {
|
|
3666
|
+
if (isEmpty(value)) return void 0;
|
|
3667
|
+
if (typeof value !== "string") return "Please enter a valid email";
|
|
3668
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? void 0 : "Please enter a valid email";
|
|
3669
|
+
},
|
|
3670
|
+
url: (value) => {
|
|
3671
|
+
if (isEmpty(value)) return void 0;
|
|
3672
|
+
if (typeof value !== "string") return "Please enter a valid URL";
|
|
3673
|
+
try {
|
|
3674
|
+
new URL(value);
|
|
3675
|
+
return;
|
|
3676
|
+
} catch {
|
|
3677
|
+
return "Please enter a valid URL";
|
|
3678
|
+
}
|
|
3679
|
+
},
|
|
3680
|
+
numeric: (value) => {
|
|
3681
|
+
if (isEmpty(value)) return void 0;
|
|
3682
|
+
if (typeof value === "number" && !isNaN(value)) return void 0;
|
|
3683
|
+
if (typeof value === "string" && !isNaN(parseFloat(value)) && value.trim() !== "") return void 0;
|
|
3684
|
+
return "Must be a number";
|
|
3685
|
+
},
|
|
3686
|
+
min: (value, arg) => {
|
|
3687
|
+
if (isEmpty(value)) return void 0;
|
|
3688
|
+
const n = typeof value === "number" ? value : parseFloat(String(value));
|
|
3689
|
+
if (isNaN(n)) return void 0;
|
|
3690
|
+
const min = Number(arg);
|
|
3691
|
+
return n >= min ? void 0 : `Must be at least ${min}`;
|
|
3692
|
+
},
|
|
3693
|
+
max: (value, arg) => {
|
|
3694
|
+
if (isEmpty(value)) return void 0;
|
|
3695
|
+
const n = typeof value === "number" ? value : parseFloat(String(value));
|
|
3696
|
+
if (isNaN(n)) return void 0;
|
|
3697
|
+
const max = Number(arg);
|
|
3698
|
+
return n <= max ? void 0 : `Must be no more than ${max}`;
|
|
3699
|
+
},
|
|
3700
|
+
minLength: (value, arg) => {
|
|
3701
|
+
if (isEmpty(value)) return void 0;
|
|
3702
|
+
if (typeof value !== "string") return void 0;
|
|
3703
|
+
const min = Number(arg);
|
|
3704
|
+
return value.length >= min ? void 0 : `Must be at least ${min} characters`;
|
|
3705
|
+
},
|
|
3706
|
+
maxLength: (value, arg) => {
|
|
3707
|
+
if (isEmpty(value)) return void 0;
|
|
3708
|
+
if (typeof value !== "string") return void 0;
|
|
3709
|
+
const max = Number(arg);
|
|
3710
|
+
return value.length <= max ? void 0 : `Must be no more than ${max} characters`;
|
|
3711
|
+
},
|
|
3712
|
+
pattern: (value, arg) => {
|
|
3713
|
+
if (isEmpty(value)) return void 0;
|
|
3714
|
+
if (typeof value !== "string" || typeof arg !== "string") return void 0;
|
|
3715
|
+
try {
|
|
3716
|
+
return new RegExp(arg).test(value) ? void 0 : "Invalid format";
|
|
3717
|
+
} catch {
|
|
3718
|
+
return;
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
};
|
|
3722
|
+
function validate(value, rules, customValidators) {
|
|
3723
|
+
for (const rule of rules) {
|
|
3724
|
+
const validator = customValidators?.[rule.type] ?? builtInValidators[rule.type];
|
|
3725
|
+
if (!validator) {
|
|
3726
|
+
console.warn(`[openui] Unknown validation rule type: "${rule.type}"`);
|
|
3727
|
+
continue;
|
|
3728
|
+
}
|
|
3729
|
+
const error = validator(value, rule.arg);
|
|
3730
|
+
if (error) return error;
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
function parseStructuredRules(rules) {
|
|
3734
|
+
if (!rules || typeof rules !== "object" || Array.isArray(rules)) return [];
|
|
3735
|
+
const obj = rules;
|
|
3736
|
+
const result = [];
|
|
3737
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
3738
|
+
if (val === false || val === void 0 || val === null) continue;
|
|
3739
|
+
if (val === true) result.push({ type: key });
|
|
3740
|
+
else result.push({
|
|
3741
|
+
type: key,
|
|
3742
|
+
arg: val
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
return result;
|
|
3746
|
+
}
|
|
3747
|
+
|
|
3748
|
+
export {
|
|
3749
|
+
Card,
|
|
3750
|
+
CardHeader,
|
|
3751
|
+
CardTitle,
|
|
3752
|
+
CardDescription,
|
|
3753
|
+
CardContent,
|
|
3754
|
+
validateAndNormalizeOpenUIHtmlArtifact,
|
|
3755
|
+
ACTION_STEPS,
|
|
3756
|
+
generatePrompt,
|
|
3757
|
+
markReactive,
|
|
3758
|
+
tagSchemaId,
|
|
3759
|
+
defineComponent,
|
|
3760
|
+
createLibrary,
|
|
3761
|
+
BuiltinActionType,
|
|
3762
|
+
createStreamingParser,
|
|
3763
|
+
enrichErrors,
|
|
3764
|
+
evaluate,
|
|
3765
|
+
evaluateElementProps,
|
|
3766
|
+
extractToolResult,
|
|
3767
|
+
ToolNotFoundError,
|
|
3768
|
+
createQueryManager,
|
|
3769
|
+
resolveStateField,
|
|
3770
|
+
createStore,
|
|
3771
|
+
validate,
|
|
3772
|
+
parseStructuredRules
|
|
3773
|
+
};
|