@xano/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +788 -0
- package/LICENSE +21 -0
- package/README.md +541 -0
- package/dist/.build-fingerprint +1 -0
- package/dist/agent-file-refresh-ZEMK33AX.js +38 -0
- package/dist/answer-shape-K67TAAPY.js +33 -0
- package/dist/backend-dir-2U53CIAL.js +39 -0
- package/dist/backend-slot-7QXW2QYC.js +28 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +255 -0
- package/dist/branch-NCRNWQBQ.js +61 -0
- package/dist/branch-commands-ACIRCEQJ.js +79 -0
- package/dist/bundle.d.ts +223 -0
- package/dist/bundle.js +153 -0
- package/dist/capture-WAXPD7DR.js +41 -0
- package/dist/chunk-27GWNP2I.js +23 -0
- package/dist/chunk-2CMJZDCL.js +177 -0
- package/dist/chunk-2EZ3ZJZZ.js +302 -0
- package/dist/chunk-2GNFDZA2.js +390 -0
- package/dist/chunk-2MNLRIOX.js +11 -0
- package/dist/chunk-2TFRR6JV.js +104 -0
- package/dist/chunk-2ZUEE4EK.js +408 -0
- package/dist/chunk-36JUQZVQ.js +22 -0
- package/dist/chunk-3DRXWLJ5.js +83 -0
- package/dist/chunk-3JDU4VRL.js +137 -0
- package/dist/chunk-3KZCLN4Z.js +241 -0
- package/dist/chunk-3LDULPRE.js +168 -0
- package/dist/chunk-3M4LFYNQ.js +111 -0
- package/dist/chunk-3XI5CTUE.js +22 -0
- package/dist/chunk-3YW23UFH.js +50 -0
- package/dist/chunk-45UIT5T2.js +862 -0
- package/dist/chunk-4BW2SG2N.js +46 -0
- package/dist/chunk-4KHEMC32.js +79 -0
- package/dist/chunk-4POWHT2K.js +321 -0
- package/dist/chunk-4Q2GN7NW.js +192 -0
- package/dist/chunk-5A2PLKZB.js +70 -0
- package/dist/chunk-5FBYXK5P.js +159 -0
- package/dist/chunk-5HVXPTU4.js +323 -0
- package/dist/chunk-5QU2P5CA.js +304 -0
- package/dist/chunk-5V5XU32D.js +191 -0
- package/dist/chunk-5ZSO4T2G.js +10 -0
- package/dist/chunk-622HUCZT.js +10 -0
- package/dist/chunk-65HWOMGV.js +177 -0
- package/dist/chunk-6ELFRN3P.js +365 -0
- package/dist/chunk-6HO3NCSJ.js +1055 -0
- package/dist/chunk-6IUMKYWI.js +457 -0
- package/dist/chunk-6KEB3C3U.js +65 -0
- package/dist/chunk-6MR5TI5V.js +987 -0
- package/dist/chunk-6NNTNXN7.js +157 -0
- package/dist/chunk-6PBR37LN.js +97 -0
- package/dist/chunk-6QXLMMT3.js +155 -0
- package/dist/chunk-6ZGJCBZS.js +224 -0
- package/dist/chunk-73IVBKMA.js +250 -0
- package/dist/chunk-7G3CTU7I.js +286 -0
- package/dist/chunk-A3UDOZB6.js +222 -0
- package/dist/chunk-A6E77GDW.js +85 -0
- package/dist/chunk-AHJWCYQO.js +660 -0
- package/dist/chunk-AKNCFVKZ.js +36 -0
- package/dist/chunk-AM4FKL4J.js +237 -0
- package/dist/chunk-AN6BL47T.js +26 -0
- package/dist/chunk-AOSYVMUS.js +147 -0
- package/dist/chunk-APWXY5SL.js +182 -0
- package/dist/chunk-AWEIIR2U.js +218 -0
- package/dist/chunk-AWQ7BQTQ.js +73 -0
- package/dist/chunk-BAUZOWGX.js +393 -0
- package/dist/chunk-BHACRS6R.js +940 -0
- package/dist/chunk-BWPNT55T.js +680 -0
- package/dist/chunk-C6RIMRU4.js +187 -0
- package/dist/chunk-CGVQZXYG.js +263 -0
- package/dist/chunk-CIMQZRRE.js +162 -0
- package/dist/chunk-CJHQKJU4.js +113 -0
- package/dist/chunk-CTMO46PK.js +1897 -0
- package/dist/chunk-D53J55C5.js +106 -0
- package/dist/chunk-D7VGG3WS.js +90 -0
- package/dist/chunk-DAD3ONIS.js +245 -0
- package/dist/chunk-DGWHAVRG.js +162 -0
- package/dist/chunk-DX4QR4B2.js +483 -0
- package/dist/chunk-EHP3WPEG.js +21 -0
- package/dist/chunk-ELP2HMXC.js +432 -0
- package/dist/chunk-ERVIASGL.js +21 -0
- package/dist/chunk-ET4Y4XCB.js +865 -0
- package/dist/chunk-ETO6LGLD.js +4750 -0
- package/dist/chunk-EUUUMY7J.js +24 -0
- package/dist/chunk-FQJ2TTHP.js +218 -0
- package/dist/chunk-FQOF7E2E.js +133 -0
- package/dist/chunk-FWD7AL6N.js +21 -0
- package/dist/chunk-FXFV3KU6.js +138 -0
- package/dist/chunk-FXRQOWTE.js +171 -0
- package/dist/chunk-G554NZNI.js +210 -0
- package/dist/chunk-GSGDWJB5.js +13 -0
- package/dist/chunk-H2V3N23D.js +66 -0
- package/dist/chunk-H5C7MBCR.js +159 -0
- package/dist/chunk-HHN734BU.js +371 -0
- package/dist/chunk-HRVBLCVJ.js +243 -0
- package/dist/chunk-HVRUQXGD.js +1264 -0
- package/dist/chunk-I4F5TCJH.js +130 -0
- package/dist/chunk-IDOTCMDV.js +57 -0
- package/dist/chunk-IGOLHKWF.js +427 -0
- package/dist/chunk-JGE7CAWB.js +148 -0
- package/dist/chunk-JTUKLCXS.js +92 -0
- package/dist/chunk-JZGVVJSI.js +66 -0
- package/dist/chunk-K4WX6UUU.js +923 -0
- package/dist/chunk-K5BBGTYZ.js +217 -0
- package/dist/chunk-K6RXBGUB.js +427 -0
- package/dist/chunk-KTSDSD36.js +165 -0
- package/dist/chunk-L5JYN3NM.js +1038 -0
- package/dist/chunk-LIEOKXKD.js +354 -0
- package/dist/chunk-LLNZGEFK.js +51 -0
- package/dist/chunk-LPECSJ2J.js +55 -0
- package/dist/chunk-LQCMKAJ7.js +413 -0
- package/dist/chunk-LRN4ZJCQ.js +1145 -0
- package/dist/chunk-MJ4ZVHKN.js +17 -0
- package/dist/chunk-MXHSQOC4.js +3061 -0
- package/dist/chunk-N5GKCWE5.js +367 -0
- package/dist/chunk-NA3PXMEF.js +10 -0
- package/dist/chunk-NAQDMEHV.js +76 -0
- package/dist/chunk-NWKXM5RD.js +109 -0
- package/dist/chunk-NZOWWHU5.js +196 -0
- package/dist/chunk-OHX6MIUZ.js +184 -0
- package/dist/chunk-OOXWYAA4.js +15 -0
- package/dist/chunk-OPHB5ORL.js +1990 -0
- package/dist/chunk-OXA6PSZT.js +1725 -0
- package/dist/chunk-P2HAGF6H.js +73 -0
- package/dist/chunk-PTHVMXZI.js +3036 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q325JKYB.js +189 -0
- package/dist/chunk-Q7DGTQ5G.js +255 -0
- package/dist/chunk-QCNYHBL7.js +601 -0
- package/dist/chunk-QO6BVGS5.js +145 -0
- package/dist/chunk-QXTKSOFI.js +76 -0
- package/dist/chunk-ROY4YAEQ.js +66 -0
- package/dist/chunk-RTJRZFFY.js +447 -0
- package/dist/chunk-SAVJUS2X.js +128 -0
- package/dist/chunk-SFLHVCKX.js +173 -0
- package/dist/chunk-STSFNCY4.js +127 -0
- package/dist/chunk-SWBF2LTK.js +170 -0
- package/dist/chunk-T5KELPAR.js +109 -0
- package/dist/chunk-TFFGOFVZ.js +1481 -0
- package/dist/chunk-TLHIFK4M.js +256 -0
- package/dist/chunk-TLQE7ZN3.js +156 -0
- package/dist/chunk-TPHWJ7UG.js +1981 -0
- package/dist/chunk-TRJCALW3.js +291 -0
- package/dist/chunk-TTEIYD5P.js +133 -0
- package/dist/chunk-U64O2MXY.js +407 -0
- package/dist/chunk-UCQ2VUTG.js +630 -0
- package/dist/chunk-UTI5G6OJ.js +44 -0
- package/dist/chunk-UVHDMMIP.js +188 -0
- package/dist/chunk-UZ6QQWJT.js +120 -0
- package/dist/chunk-VAX3UZOY.js +686 -0
- package/dist/chunk-W3HBUKFO.js +57 -0
- package/dist/chunk-WKQLGKCL.js +2823 -0
- package/dist/chunk-WQFNASK4.js +560 -0
- package/dist/chunk-WRBKLYSJ.js +224 -0
- package/dist/chunk-WTPZRR4I.js +17 -0
- package/dist/chunk-WZDJ2LLX.js +2385 -0
- package/dist/chunk-X2NL4KUO.js +14 -0
- package/dist/chunk-X4DVXBFY.js +38 -0
- package/dist/chunk-X677DMZM.js +7050 -0
- package/dist/chunk-XBKMWY7F.js +612 -0
- package/dist/chunk-XNCP4KYO.js +78 -0
- package/dist/chunk-XVBGLISP.js +35 -0
- package/dist/chunk-XW6KISBB.js +1192 -0
- package/dist/chunk-YGZHHAAB.js +8156 -0
- package/dist/chunk-YHEFQUQB.js +186 -0
- package/dist/chunk-YLDALTWZ.js +653 -0
- package/dist/chunk-YU4HSQW7.js +229 -0
- package/dist/chunk-ZDPP6RKB.js +27 -0
- package/dist/chunk-ZFDZKNGG.js +194 -0
- package/dist/chunk-ZIY7RNUT.js +49 -0
- package/dist/chunk-ZYJP6BOO.js +30 -0
- package/dist/cli.d.ts +1111 -0
- package/dist/cli.js +165 -0
- package/dist/codegen-command-VR2GWR6C.js +173 -0
- package/dist/codegen.d.ts +115 -0
- package/dist/codegen.js +19 -0
- package/dist/commands-GJMJ4XYZ.js +76 -0
- package/dist/completion-ZQLO6PB6.js +738 -0
- package/dist/config-MBYGCAFK.js +136 -0
- package/dist/deploy-command-JVZPWHMK.js +180 -0
- package/dist/deploy-source-3W45CXMP.js +81 -0
- package/dist/dev-env-sync-HN44K4D5.js +188 -0
- package/dist/diagnostics-UEAAHWVF.js +30 -0
- package/dist/display-name-hint-ABBPVQIR.js +46 -0
- package/dist/emit-CyhBDxli.d.ts +7007 -0
- package/dist/env-example-refresh-ET5HMMDW.js +126 -0
- package/dist/env-pull-command-TNMNB3CV.js +323 -0
- package/dist/env-target-O4HRKTGZ.js +40 -0
- package/dist/env-var-command-ZIRDCAZA.js +427 -0
- package/dist/ephemeral-P7F3PMAD.js +54 -0
- package/dist/ephemeral-command-CLJJKOFT.js +592 -0
- package/dist/ephemeral-state-PYCBSOGX.js +61 -0
- package/dist/file-lock-PT74GWYW.js +10 -0
- package/dist/generate-command-AYEOLA26.js +510 -0
- package/dist/gitignore-3SJ3WNRG.js +51 -0
- package/dist/guards-4EBNCZDC.js +162 -0
- package/dist/hosted-file-PSHJB3HX.js +28 -0
- package/dist/impersonate-command-5JAS3WSV.js +152 -0
- package/dist/import-EQSFU6JC.js +19 -0
- package/dist/index.d.ts +5470 -0
- package/dist/index.js +371 -0
- package/dist/init-command-X2FR4UAK.js +125 -0
- package/dist/init-modules-RY7UHAOL.js +50 -0
- package/dist/init-web-M4YZSZK6.js +42 -0
- package/dist/internal.d.ts +179 -0
- package/dist/internal.js +2634 -0
- package/dist/io-RC56QSN5.js +26 -0
- package/dist/keep-data-merge-7MPLVAVI.js +1119 -0
- package/dist/lambda-config-reconcile-OUZ4OYAB.js +116 -0
- package/dist/landed-753YLJ5H.js +40 -0
- package/dist/landing-record-SU52SKXU.js +61 -0
- package/dist/live-diff-YONW2VI3.js +65 -0
- package/dist/local-engine-choice-CH5X3W7E.js +79 -0
- package/dist/local-engine-command-Z3OL7VOD.js +766 -0
- package/dist/local-engine-config-5U3SC434.js +39 -0
- package/dist/local-engine-deploy-PCQ4SNPB.js +39 -0
- package/dist/local-engine-process-EHTE5LLU.js +63 -0
- package/dist/local-engine-state-A4FTRFAF.js +32 -0
- package/dist/local-engine-update-check-6L645VBL.js +162 -0
- package/dist/lock-MRRHW5GS.js +87 -0
- package/dist/lock-commands-2QG25HL7.js +963 -0
- package/dist/login-command-QPYRWOXZ.js +538 -0
- package/dist/logout-command-WOIQLDP6.js +56 -0
- package/dist/loop-ALIZLOSX.js +191 -0
- package/dist/marketplace-command-K5YL42NO.js +977 -0
- package/dist/marketplace-resolve-LY32JES5.js +40 -0
- package/dist/meta-client-YXGPW53C.js +244 -0
- package/dist/module-manifest-JFGHU2TC.js +38 -0
- package/dist/near-key.d.ts +13 -0
- package/dist/near-key.js +8 -0
- package/dist/node.d.ts +95 -0
- package/dist/node.js +969 -0
- package/dist/operation-outcome-TZSESNC7.js +51 -0
- package/dist/output-target-AL3I44GG.js +47 -0
- package/dist/package-manager-PRAHUSCN.js +34 -0
- package/dist/plan-disclosure-6G4MLZKD.js +114 -0
- package/dist/plugin-contributions-WRNY5ABG.js +28 -0
- package/dist/plugin-prompt-4VA3B4FF.js +20 -0
- package/dist/plugin-questions-JTSYA74Q.js +24 -0
- package/dist/plugin.d.ts +396 -0
- package/dist/plugin.js +1 -0
- package/dist/preflight-command-2T5Z36MI.js +321 -0
- package/dist/profile-command-AH3PIM4X.js +800 -0
- package/dist/profile-pointer-GRWJU3YD.js +31 -0
- package/dist/profile-select-74TRDEPD.js +23 -0
- package/dist/project-dependency-KSH63ODT.js +35 -0
- package/dist/project-sdk-REPPOIBD.js +24 -0
- package/dist/promote-command-PCWT5N6S.js +140 -0
- package/dist/prompt-II3F3LND.js +27 -0
- package/dist/publish-command-TTDUUJI5.js +414 -0
- package/dist/pull-command-VBO7C4S3.js +161 -0
- package/dist/pull-superseded-GJWLG7XO.js +20 -0
- package/dist/release-command-N4ZPKMHR.js +146 -0
- package/dist/release-ns-command-3KFDMZ6G.js +1745 -0
- package/dist/response-HlXahv4K.d.ts +925 -0
- package/dist/retry-command-AS5EVREX.js +34 -0
- package/dist/routes-manifest-SNWUVBT6.js +43 -0
- package/dist/runtime-XETHVF33.js +14 -0
- package/dist/scaffold.d.ts +1911 -0
- package/dist/scaffold.js +107 -0
- package/dist/secrets-file-LVZD6IIM.js +56 -0
- package/dist/secrets-fill-command-TTCPSVXN.js +218 -0
- package/dist/seed-VU7C3QYS.js +46 -0
- package/dist/sent-writes-N7X4OVXT.js +22 -0
- package/dist/source-resolve-S2XA3XZF.js +83 -0
- package/dist/static-host-B2Y6AG3W.js +352 -0
- package/dist/status-command-RUDXTF7I.js +554 -0
- package/dist/store-6VNCD4D7.js +20 -0
- package/dist/store-ARM6VMO5.js +98 -0
- package/dist/store-DuYfzKlW.d.ts +3144 -0
- package/dist/suggest-6FOOYKHG.js +12 -0
- package/dist/sveltekit.d.ts +154 -0
- package/dist/sveltekit.js +109 -0
- package/dist/sync-contributions-UCEROO26.js +415 -0
- package/dist/table-ENAVMITR.js +42 -0
- package/dist/tables-command-GUPXXSB5.js +156 -0
- package/dist/tenant-IIHPOHGP.js +42 -0
- package/dist/tenant-command-D5HGI2X4.js +965 -0
- package/dist/test-command-TIKZMDPZ.js +476 -0
- package/dist/tests-BGGIOK3L.js +26 -0
- package/dist/token-NICJUTMT.js +72 -0
- package/dist/toolchain-hooks-GHKLUJOH.js +96 -0
- package/dist/toolchain-modules-5EAZUONB.js +24 -0
- package/dist/tracked-backend-EIBUMDZA.js +80 -0
- package/dist/update-check-2W2NLDTC.js +136 -0
- package/dist/upgrade-command-QHNFWM7O.js +531 -0
- package/dist/verify-rollout-DRCK5TDD.js +61 -0
- package/dist/whoami-command-STGINTXH.js +51 -0
- package/dist/workspace-2CLHU2LK.js +93 -0
- package/dist/workspace-binding-56PIIPCP.js +54 -0
- package/dist/workspace-command-JEXQETJD.js +535 -0
- package/dist/workspace-env-F2RFMSC5.js +70 -0
- package/dist/workspace-export-OJSH2HIW.js +21 -0
- package/dist/writable-VA7OISSK.js +25 -0
- package/dist/xano-ICFUSPFR.js +42 -0
- package/dist/xanosdk-import-GN42TO2B.js +56 -0
- package/dist/xanosdk-project-S6DXJONQ.js +41 -0
- package/dist/xdo-CxDr2Q1C.d.ts +1371 -0
- package/guides/README.md +33 -0
- package/guides/authoring.md +921 -0
- package/guides/cli.md +401 -0
- package/guides/codegen.md +177 -0
- package/guides/codes.md +404 -0
- package/guides/coverage.md +74 -0
- package/guides/deploying.md +1525 -0
- package/guides/environment.md +400 -0
- package/guides/marketplace.md +415 -0
- package/guides/module-contract.md +558 -0
- package/guides/object-kinds.md +433 -0
- package/guides/project-structure.md +43 -0
- package/guides/scaffold.md +351 -0
- package/guides/typed-frontend.md +242 -0
- package/llms/client.md +63 -0
- package/llms/errors.md +56 -0
- package/llms/fields.md +176 -0
- package/llms/filters.md +183 -0
- package/llms/kinds-agent-mcp.md +30 -0
- package/llms/kinds-core.md +100 -0
- package/llms/kinds-knowledge.md +20 -0
- package/llms/kinds-realtime.md +76 -0
- package/llms/lambda.md +94 -0
- package/llms/legacy.md +44 -0
- package/llms/lock.md +40 -0
- package/llms/object-kinds.md +34 -0
- package/llms/statements-calls.md +62 -0
- package/llms/statements-catalog.md +320 -0
- package/llms/statements-data.md +66 -0
- package/llms/statements-runtime.md +68 -0
- package/llms/tests.md +59 -0
- package/llms/toolchain.md +16 -0
- package/llms/triggers.md +36 -0
- package/llms/values.md +29 -0
- package/llms-full.txt +2085 -0
- package/llms.txt +493 -0
- package/manifest.json +11224 -0
- package/package.json +162 -0
- package/types/lambda-globals.d.ts +129 -0
|
@@ -0,0 +1,1897 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveAuthRef
|
|
3
|
+
} from "./chunk-ZDPP6RKB.js";
|
|
4
|
+
import {
|
|
5
|
+
obj
|
|
6
|
+
} from "./chunk-K5BBGTYZ.js";
|
|
7
|
+
import {
|
|
8
|
+
buildBundle
|
|
9
|
+
} from "./chunk-LQCMKAJ7.js";
|
|
10
|
+
import {
|
|
11
|
+
checkAddonAttachments,
|
|
12
|
+
checkAddonOutput,
|
|
13
|
+
checkAgentApiKeys,
|
|
14
|
+
checkAgentArgPlaceholders,
|
|
15
|
+
checkApiGroupDocsExposure,
|
|
16
|
+
checkAssertionsOutsideTests,
|
|
17
|
+
checkAuthNoCaller,
|
|
18
|
+
checkCacheTtl,
|
|
19
|
+
checkDecodeOnlyStatements,
|
|
20
|
+
checkDocumentationTokens,
|
|
21
|
+
checkExpressionOperands,
|
|
22
|
+
checkGuardRoleColumns,
|
|
23
|
+
checkIntRange,
|
|
24
|
+
checkInterpolatedValues,
|
|
25
|
+
checkKnowledge,
|
|
26
|
+
checkLiveDatasourceTests,
|
|
27
|
+
checkLoopControl,
|
|
28
|
+
checkMcpResourceUris,
|
|
29
|
+
checkMcpStackHazards,
|
|
30
|
+
checkMicroserviceBlocks,
|
|
31
|
+
checkMicroserviceReferences,
|
|
32
|
+
checkMicroserviceSecrets,
|
|
33
|
+
checkNonJsonValues,
|
|
34
|
+
checkNonNullVectorColumns,
|
|
35
|
+
checkNullMatchArgs,
|
|
36
|
+
checkOmittedCallInputs,
|
|
37
|
+
checkPasswordInputHashing,
|
|
38
|
+
checkPathSegmentCandidates,
|
|
39
|
+
checkPrimitiveAuthIdentity,
|
|
40
|
+
checkRatelimitWithoutError,
|
|
41
|
+
checkRealtimeDeliver,
|
|
42
|
+
checkRealtimeGates,
|
|
43
|
+
checkRealtimeJoinDeliver,
|
|
44
|
+
checkRealtimePublish,
|
|
45
|
+
checkRealtimeSilentShapes,
|
|
46
|
+
checkReferences,
|
|
47
|
+
checkRegexOperandOrder,
|
|
48
|
+
checkReservedColumnNames,
|
|
49
|
+
checkReservedInputNames,
|
|
50
|
+
checkRouteShadowing,
|
|
51
|
+
checkRunFamilyHosts,
|
|
52
|
+
checkSafeRefMatchArgs,
|
|
53
|
+
checkSameNameSiblings,
|
|
54
|
+
checkSearchOperandFilters,
|
|
55
|
+
checkSeed,
|
|
56
|
+
checkStackAuthRatelimit,
|
|
57
|
+
checkStacks,
|
|
58
|
+
checkSwitchFallthrough,
|
|
59
|
+
checkTimezoneArgs,
|
|
60
|
+
checkToThrowScope,
|
|
61
|
+
checkToolAuthIdentity,
|
|
62
|
+
checkToolOutputSchema,
|
|
63
|
+
checkTriggerBindings,
|
|
64
|
+
checkUnboundVarRefs,
|
|
65
|
+
checkUnitTests,
|
|
66
|
+
checkUnknownCallInputs,
|
|
67
|
+
checkUnusableColumnNames,
|
|
68
|
+
checkWriteBeforeElicit,
|
|
69
|
+
checkZeroBasedObjects,
|
|
70
|
+
checkZipPasswordKeys,
|
|
71
|
+
postEnvelopeMisreads,
|
|
72
|
+
unnestedEnvelopeReads,
|
|
73
|
+
walkNodes
|
|
74
|
+
} from "./chunk-YGZHHAAB.js";
|
|
75
|
+
import {
|
|
76
|
+
WORKSPACE_HISTORY_TYPES,
|
|
77
|
+
assertNameAsStored,
|
|
78
|
+
middlewareEntryGuid,
|
|
79
|
+
stackReferencesAuth
|
|
80
|
+
} from "./chunk-5V5XU32D.js";
|
|
81
|
+
import {
|
|
82
|
+
defKindOf
|
|
83
|
+
} from "./chunk-ERVIASGL.js";
|
|
84
|
+
import {
|
|
85
|
+
AUTHOR_KIND_NAME,
|
|
86
|
+
assertDefShape,
|
|
87
|
+
assertMockKeysResolved,
|
|
88
|
+
assertNoProtoKeys,
|
|
89
|
+
assertTestIds,
|
|
90
|
+
encodeObject,
|
|
91
|
+
getKind
|
|
92
|
+
} from "./chunk-4POWHT2K.js";
|
|
93
|
+
import {
|
|
94
|
+
documentationTokenDeclarations,
|
|
95
|
+
resolveDocumentationTokens
|
|
96
|
+
} from "./chunk-3JDU4VRL.js";
|
|
97
|
+
import {
|
|
98
|
+
nearestKey
|
|
99
|
+
} from "./chunk-WTPZRR4I.js";
|
|
100
|
+
import {
|
|
101
|
+
DiagnosticBag,
|
|
102
|
+
allowableWarnings,
|
|
103
|
+
setDiagnosticSink
|
|
104
|
+
} from "./chunk-2EZ3ZJZZ.js";
|
|
105
|
+
import {
|
|
106
|
+
CANONICAL_PAYLOAD_KEYS,
|
|
107
|
+
LOCK_PAYLOAD_KEYS,
|
|
108
|
+
WORKSPACE_KEY,
|
|
109
|
+
identityNamesByGuid,
|
|
110
|
+
lockKey,
|
|
111
|
+
lockNameForObject,
|
|
112
|
+
mintCanonical,
|
|
113
|
+
recordObserved,
|
|
114
|
+
sdkKindName
|
|
115
|
+
} from "./chunk-6MR5TI5V.js";
|
|
116
|
+
import {
|
|
117
|
+
HOSTED_ICON_KINDS,
|
|
118
|
+
pendingHostedFile
|
|
119
|
+
} from "./chunk-XNCP4KYO.js";
|
|
120
|
+
import {
|
|
121
|
+
isTaggedValue
|
|
122
|
+
} from "./chunk-OXA6PSZT.js";
|
|
123
|
+
import {
|
|
124
|
+
REFERENCEABLE_KINDS,
|
|
125
|
+
deriveGuid,
|
|
126
|
+
refSpelling,
|
|
127
|
+
resolveRef
|
|
128
|
+
} from "./chunk-NZOWWHU5.js";
|
|
129
|
+
import {
|
|
130
|
+
getLockedCanonical
|
|
131
|
+
} from "./chunk-4BW2SG2N.js";
|
|
132
|
+
import {
|
|
133
|
+
describeEntry
|
|
134
|
+
} from "./chunk-CIMQZRRE.js";
|
|
135
|
+
import {
|
|
136
|
+
withArticle
|
|
137
|
+
} from "./chunk-36JUQZVQ.js";
|
|
138
|
+
|
|
139
|
+
// src/kinds/def-keys.ts
|
|
140
|
+
var keys = (roster, ...extra) => [...Object.keys(roster), ...extra];
|
|
141
|
+
var COMMON = { __kind: 1, name: 1, guid: 1, description: 1, tags: 1 };
|
|
142
|
+
var STACK = { input: 1, stack: 1, response: 1, responseShape: 1, history: 1, middleware: 1 };
|
|
143
|
+
var TOOLSET = { ...COMMON, docs: 1, history: 1, output: 1, enabled: 1, canonical: 1, tools: 1, llm: 1, diagnostics: 1 };
|
|
144
|
+
var DEF_KEYS = {
|
|
145
|
+
query: keys(
|
|
146
|
+
{
|
|
147
|
+
...COMMON,
|
|
148
|
+
...STACK,
|
|
149
|
+
verb: 1,
|
|
150
|
+
apiGroup: 1,
|
|
151
|
+
apiGroupId: 1,
|
|
152
|
+
diagnostics: 1,
|
|
153
|
+
auth: 1,
|
|
154
|
+
docs: 1,
|
|
155
|
+
responseType: 1,
|
|
156
|
+
apiEnabled: 1,
|
|
157
|
+
disabled: 1,
|
|
158
|
+
cache: 1,
|
|
159
|
+
tests: 1,
|
|
160
|
+
example: 1
|
|
161
|
+
},
|
|
162
|
+
"getPath",
|
|
163
|
+
"toSearchParams"
|
|
164
|
+
),
|
|
165
|
+
function: keys(
|
|
166
|
+
{ ...COMMON, ...STACK, docs: 1, cache: 1, tests: 1, workspace: 1, diagnostics: 1 }
|
|
167
|
+
),
|
|
168
|
+
task: keys(
|
|
169
|
+
{ ...COMMON, docs: 1, middleware: 1, history: 1, stack: 1, datasource: 1, active: 1, schedule: 1, diagnostics: 1 }
|
|
170
|
+
),
|
|
171
|
+
table: keys(
|
|
172
|
+
{
|
|
173
|
+
...COMMON,
|
|
174
|
+
auth: 1,
|
|
175
|
+
docs: 1,
|
|
176
|
+
__cols: 1,
|
|
177
|
+
__row: 1,
|
|
178
|
+
__internal: 1,
|
|
179
|
+
install: 1,
|
|
180
|
+
schema: 1,
|
|
181
|
+
system: 1,
|
|
182
|
+
idType: 1,
|
|
183
|
+
useXdo: 1,
|
|
184
|
+
index: 1,
|
|
185
|
+
autocomplete: 1,
|
|
186
|
+
external: 1,
|
|
187
|
+
views: 1,
|
|
188
|
+
seed: 1,
|
|
189
|
+
publicSeed: 1,
|
|
190
|
+
diagnostics: 1
|
|
191
|
+
}
|
|
192
|
+
),
|
|
193
|
+
middleware: keys(
|
|
194
|
+
{ ...COMMON, ...STACK, docs: 1, tests: 1, resultStrategy: 1, exceptionPolicy: 1, diagnostics: 1 }
|
|
195
|
+
),
|
|
196
|
+
addon: keys(
|
|
197
|
+
{
|
|
198
|
+
...COMMON,
|
|
199
|
+
input: 1,
|
|
200
|
+
table: 1,
|
|
201
|
+
tableAlias: 1,
|
|
202
|
+
where: 1,
|
|
203
|
+
sort: 1,
|
|
204
|
+
context: 1,
|
|
205
|
+
output: 1,
|
|
206
|
+
cardinality: 1,
|
|
207
|
+
group: 1,
|
|
208
|
+
eval: 1,
|
|
209
|
+
__graft: 1,
|
|
210
|
+
diagnostics: 1
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
agent: keys(TOOLSET, "getCanonical"),
|
|
214
|
+
mcp_server: keys(
|
|
215
|
+
{ ...TOOLSET, instructions: 1, spec: 1, prompts: 1, resources: 1, oauth: 1 },
|
|
216
|
+
"getCanonical",
|
|
217
|
+
"getPath",
|
|
218
|
+
"getUrl"
|
|
219
|
+
),
|
|
220
|
+
tool: keys(
|
|
221
|
+
{
|
|
222
|
+
...COMMON,
|
|
223
|
+
...STACK,
|
|
224
|
+
docs: 1,
|
|
225
|
+
enabled: 1,
|
|
226
|
+
instructions: 1,
|
|
227
|
+
toolsetId: 1,
|
|
228
|
+
diagnostics: 1,
|
|
229
|
+
title: 1,
|
|
230
|
+
annotations: 1,
|
|
231
|
+
icons: 1,
|
|
232
|
+
output: 1
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
prompt: keys(
|
|
236
|
+
{ ...COMMON, ...STACK, docs: 1, title: 1, icons: 1, diagnostics: 1 }
|
|
237
|
+
),
|
|
238
|
+
resource: keys(
|
|
239
|
+
{ ...COMMON, ...STACK, docs: 1, uri: 1, mimeType: 1, title: 1, icons: 1, annotations: 1, diagnostics: 1 }
|
|
240
|
+
),
|
|
241
|
+
realtime_server: keys(
|
|
242
|
+
{ ...COMMON, diagnostics: 1, middleware: 1, history: 1, enabled: 1, canonical: 1 },
|
|
243
|
+
"getCanonical",
|
|
244
|
+
"getPath",
|
|
245
|
+
"getUrl"
|
|
246
|
+
),
|
|
247
|
+
channel: keys(
|
|
248
|
+
{
|
|
249
|
+
...COMMON,
|
|
250
|
+
middleware: 1,
|
|
251
|
+
history: 1,
|
|
252
|
+
input: 1,
|
|
253
|
+
active: 1,
|
|
254
|
+
server: 1,
|
|
255
|
+
anonymousClients: 1,
|
|
256
|
+
presence: 1,
|
|
257
|
+
publish: 1,
|
|
258
|
+
conversation: 1,
|
|
259
|
+
delivery: 1,
|
|
260
|
+
rateLimit: 1,
|
|
261
|
+
diagnostics: 1
|
|
262
|
+
},
|
|
263
|
+
"getChannel"
|
|
264
|
+
),
|
|
265
|
+
message: keys(
|
|
266
|
+
{ ...COMMON, ...STACK, auth: 1, disabled: 1, active: 1, server: 1, channel: 1, deliverTo: 1, diagnostics: 1 }
|
|
267
|
+
),
|
|
268
|
+
knowledge: keys({ ...COMMON, enabled: 1, type: 1, mode: 1, body: 1, refs: 1, diagnostics: 1 }),
|
|
269
|
+
workflow_test: keys(
|
|
270
|
+
{ ...COMMON, docs: 1, stack: 1, datasource: 1, active: 1, diagnostics: 1 }
|
|
271
|
+
),
|
|
272
|
+
api_group: keys(
|
|
273
|
+
{
|
|
274
|
+
...COMMON,
|
|
275
|
+
docs: 1,
|
|
276
|
+
middleware: 1,
|
|
277
|
+
history: 1,
|
|
278
|
+
canonical: 1,
|
|
279
|
+
documentation: 1,
|
|
280
|
+
swagger: 1,
|
|
281
|
+
apiGroupEnabled: 1,
|
|
282
|
+
cors: 1,
|
|
283
|
+
diagnostics: 1
|
|
284
|
+
}
|
|
285
|
+
),
|
|
286
|
+
microservice: keys(
|
|
287
|
+
{
|
|
288
|
+
...COMMON,
|
|
289
|
+
kind: 1,
|
|
290
|
+
tenantDeploy: 1,
|
|
291
|
+
deployment: 1,
|
|
292
|
+
ingresses: 1,
|
|
293
|
+
configs: 1,
|
|
294
|
+
volumes: 1,
|
|
295
|
+
chart: 1,
|
|
296
|
+
registryAuth: 1,
|
|
297
|
+
diagnostics: 1
|
|
298
|
+
}
|
|
299
|
+
),
|
|
300
|
+
trigger: keys(
|
|
301
|
+
{
|
|
302
|
+
...COMMON,
|
|
303
|
+
history: 1,
|
|
304
|
+
stack: 1,
|
|
305
|
+
response: 1,
|
|
306
|
+
responseShape: 1,
|
|
307
|
+
active: 1,
|
|
308
|
+
objType: 1,
|
|
309
|
+
toolsetType: 1,
|
|
310
|
+
objId: 1,
|
|
311
|
+
hasResult: 1,
|
|
312
|
+
meta: 1,
|
|
313
|
+
diagnostics: 1
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
workspace: keys(
|
|
317
|
+
{
|
|
318
|
+
__kind: 1,
|
|
319
|
+
name: 1,
|
|
320
|
+
diagnostics: 1,
|
|
321
|
+
description: 1,
|
|
322
|
+
middleware: 1,
|
|
323
|
+
history: 1,
|
|
324
|
+
canonical: 1,
|
|
325
|
+
use_xdo: 1,
|
|
326
|
+
preferences: 1,
|
|
327
|
+
realtime: 1,
|
|
328
|
+
documentation: 1,
|
|
329
|
+
swagger: 1,
|
|
330
|
+
env: 1,
|
|
331
|
+
settings: 1,
|
|
332
|
+
use_custom_names: 1,
|
|
333
|
+
defaults: 1,
|
|
334
|
+
datasources: 1,
|
|
335
|
+
datasource_live: 1
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
};
|
|
339
|
+
var MIDDLEWARE = keys({ pre: 1, post: 1 });
|
|
340
|
+
var CACHE = keys({ active: 1, ttl: 1, input: 1, auth: 1, datasource: 1, ip: 1, headers: 1, env: 1 });
|
|
341
|
+
var NESTED_DEF_KEYS = {
|
|
342
|
+
query: { cache: CACHE },
|
|
343
|
+
function: { cache: CACHE },
|
|
344
|
+
table: {
|
|
345
|
+
"views[]": keys({ name: 1, id: 1, alias: 1, hide: 1, q: 1, where: 1, sort: 1 })
|
|
346
|
+
},
|
|
347
|
+
task: {
|
|
348
|
+
"schedule[]": keys(
|
|
349
|
+
{ startsOn: 1, freq: 1, repeatEnabled: 1, endsOn: 1, endsEnabled: 1 }
|
|
350
|
+
)
|
|
351
|
+
},
|
|
352
|
+
microservice: {
|
|
353
|
+
deployment: keys({ replicas: 1, strategy: 1, docker: 1, containers: 1 }),
|
|
354
|
+
"deployment.containers[]": keys(
|
|
355
|
+
{ name: 1, image: 1, pullSecret: 1, type: 1, command: 1, args: 1, env: 1, ports: 1, resources: 1, volumes: 1 }
|
|
356
|
+
),
|
|
357
|
+
"deployment.containers[].env[]": keys({ name: 1, value: 1, fromEnv: 1 }),
|
|
358
|
+
"deployment.containers[].ports[]": keys({ servicePort: 1, containerPort: 1 }),
|
|
359
|
+
"deployment.containers[].resources": keys({ cpu: 1, ram: 1 }),
|
|
360
|
+
"deployment.containers[].volumes[]": keys(
|
|
361
|
+
{ name: 1, type: 1, persistent: 1, emptyDir: 1, config: 1 }
|
|
362
|
+
),
|
|
363
|
+
"ingresses[]": keys({ name: 1, domain: 1, paths: 1 }),
|
|
364
|
+
"ingresses[].paths[]": keys({ service: 1, path: 1 }),
|
|
365
|
+
"configs[]": keys({ name: 1, type: 1, value: 1 }),
|
|
366
|
+
"volumes[]": keys({ name: 1, size: 1, class: 1 }),
|
|
367
|
+
chart: keys({ ref: 1, values: 1, version: 1 }),
|
|
368
|
+
registryAuth: keys({ type: 1, server: 1, dockerconfigjson: 1 })
|
|
369
|
+
},
|
|
370
|
+
api_group: {
|
|
371
|
+
cors: keys(
|
|
372
|
+
{ mode: 1, allowOrigins: 1, allowHeaders: 1, allowCredentials: 1, maxAge: 1, allowMethods: 1 }
|
|
373
|
+
),
|
|
374
|
+
"cors.allowMethods": keys(
|
|
375
|
+
{ delete: 1, get: 1, head: 1, patch: 1, post: 1, put: 1 }
|
|
376
|
+
),
|
|
377
|
+
documentation: keys({ require_token: 1, token: 1 })
|
|
378
|
+
},
|
|
379
|
+
channel: {
|
|
380
|
+
publish: keys({ who: 1, direct: 1 }),
|
|
381
|
+
conversation: keys({ enabled: 1, limit: 1, ttl: 1 }),
|
|
382
|
+
delivery: keys({ guarantee: 1, perRecipient: 1 }),
|
|
383
|
+
rateLimit: keys({ messagesPerMinute: 1 })
|
|
384
|
+
},
|
|
385
|
+
workspace: {
|
|
386
|
+
preferences: keys({ allow_push: 1, track_performance: 1, use_internal_docs: 1 }),
|
|
387
|
+
// The workspace's own middleware is per host tier, each a `{ pre, post }`.
|
|
388
|
+
middleware: keys({ query: 1, function: 1, task: 1, tool: 1 }),
|
|
389
|
+
"middleware.query": MIDDLEWARE,
|
|
390
|
+
"middleware.function": MIDDLEWARE,
|
|
391
|
+
"middleware.task": MIDDLEWARE,
|
|
392
|
+
"middleware.tool": MIDDLEWARE,
|
|
393
|
+
// WHOLESALE maps: a key nothing reads falls back to its default silently.
|
|
394
|
+
history: WORKSPACE_HISTORY_TYPES,
|
|
395
|
+
defaults: keys({ db_primary_key: 1 })
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
var NEAR_MISS_DEF_KEYS = {
|
|
399
|
+
workspace: {
|
|
400
|
+
realtime: ["hash", "mode", "enabled", "channels"],
|
|
401
|
+
documentation: ["require_token", "token", "whitelist"]
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
function recordsAt(def, path) {
|
|
405
|
+
let at = [["", def]];
|
|
406
|
+
for (const part of path.split(".")) {
|
|
407
|
+
const list = part.endsWith("[]");
|
|
408
|
+
const field = list ? part.slice(0, -2) : part;
|
|
409
|
+
at = at.flatMap(([where, v]) => {
|
|
410
|
+
if (typeof v !== "object" || v === null) return [];
|
|
411
|
+
const next = v[field];
|
|
412
|
+
const label = where ? `${where}.${field}` : field;
|
|
413
|
+
if (!list) return [[label, next]];
|
|
414
|
+
return Array.isArray(next) ? next.map((e, i) => [`${label}[${i}]`, e]) : [];
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
return at.filter(([, v]) => typeof v === "object" && v !== null && !Array.isArray(v));
|
|
418
|
+
}
|
|
419
|
+
function assertDefKeys(owner, def, known, kind) {
|
|
420
|
+
if (known === void 0 || typeof def !== "object" || def === null) return;
|
|
421
|
+
refuseUnknown(owner, def, known);
|
|
422
|
+
const nested = { middleware: MIDDLEWARE, ...kind ? NESTED_DEF_KEYS[kind] : {} };
|
|
423
|
+
for (const [path, roster] of Object.entries(nested)) {
|
|
424
|
+
for (const [where, record] of recordsAt(def, path)) refuseUnknown(`${owner} \`${where}\``, record, roster);
|
|
425
|
+
}
|
|
426
|
+
for (const [path, roster] of Object.entries(kind && NEAR_MISS_DEF_KEYS[kind] || {})) {
|
|
427
|
+
for (const [where, record] of recordsAt(def, path)) {
|
|
428
|
+
const typos = Object.keys(record).filter((k) => !roster.includes(k) && nearestKey(k, roster) !== void 0);
|
|
429
|
+
if (typos.length > 0) refuseUnknown(`${owner} \`${where}\``, Object.fromEntries(typos.map((k) => [k, 1])), roster);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function refuseUnknown(owner, def, known) {
|
|
434
|
+
for (const key of Object.keys(def)) {
|
|
435
|
+
if (known.includes(key)) continue;
|
|
436
|
+
const near = nearestKey(key, known) ?? known.find((k) => k.length > 3 && (key.startsWith(k) || k.startsWith(key)));
|
|
437
|
+
throw new Error(
|
|
438
|
+
`${owner}: unknown key "${key}"${near ? ` \u2014 did you mean "${near}"?` : "."} Nothing reads it, so it would not do what it says. Keys this def takes: ${known.filter((k) => !k.startsWith("__") && !/^get[A-Z]/.test(k) && k !== "token").join(", ")}.` + // A literal token is refused at export: it is a secret, never source.
|
|
439
|
+
(known.includes("token") ? " A token is not written here \u2014 it comes from `xano/.secrets.json`." : "")
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/kinds/mcp-oauth.ts
|
|
445
|
+
var MCP_OAUTH_PRESETS = ["auth0", "clerk", "workos", "entra", "okta", "cognito", "generic"];
|
|
446
|
+
var MCP_OAUTH_PRESETS_WITHOUT_AUD = ["clerk"];
|
|
447
|
+
var MCP_OAUTH_STABLE_CLAIMS = ["sub", "oid", "email"];
|
|
448
|
+
var HOSTED_KEYS = ["mode", "authTable", "loginUrl"];
|
|
449
|
+
var EXTERNAL_KEYS = [
|
|
450
|
+
"mode",
|
|
451
|
+
"authTable",
|
|
452
|
+
"issuer",
|
|
453
|
+
"column",
|
|
454
|
+
"audience",
|
|
455
|
+
"audienceAck",
|
|
456
|
+
"claim",
|
|
457
|
+
"claimAck",
|
|
458
|
+
"preset",
|
|
459
|
+
"allowedClientIds"
|
|
460
|
+
];
|
|
461
|
+
var REMOVED_KEYS = ["consent", "branding", "trustedClients"];
|
|
462
|
+
var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
463
|
+
var MCP_OAUTH_ENV_REF = /^\$\{env\.([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
464
|
+
function envString(owner, field, v) {
|
|
465
|
+
if (typeof v === "string") return v;
|
|
466
|
+
if (isTaggedValue(v)) {
|
|
467
|
+
const name = v.value;
|
|
468
|
+
if (v.tag === "setting" && v.filters.length === 0 && ENV_NAME.test(name)) return `\${env.${name}}`;
|
|
469
|
+
throw new Error(
|
|
470
|
+
`${owner} \`oauth.${field}\`: only \`env("NAME")\` (a workspace environment variable) is accepted here, not ${JSON.stringify({ tag: v.tag, value: name })}. Built-in settings (\`$remote_ip\` and the like) and filtered values are not resolved in this setting \u2014 pass a string or \`env("NAME")\`.`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
throw new Error(`${owner} \`oauth.${field}\`: expected a string or \`env("NAME")\` (got ${v === null ? "null" : typeof v}).`);
|
|
474
|
+
}
|
|
475
|
+
function strings(owner, field, v, envAllowed) {
|
|
476
|
+
if (v === void 0) return [];
|
|
477
|
+
if (!Array.isArray(v)) throw new Error(`${owner} \`oauth.${field}\`: expected a list of strings.`);
|
|
478
|
+
return v.map((item, i) => {
|
|
479
|
+
if (envAllowed) return envString(owner, `${field}[${i}]`, item);
|
|
480
|
+
if (typeof item !== "string") throw new Error(`${owner} \`oauth.${field}[${i}]\`: expected a string.`);
|
|
481
|
+
return item;
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
function oneOf(owner, field, v, allowed) {
|
|
485
|
+
if (v === void 0) return;
|
|
486
|
+
if (typeof v !== "string" || !allowed.includes(v)) {
|
|
487
|
+
throw new Error(
|
|
488
|
+
`${owner} \`oauth.${field}\` must be ${allowed.map((a) => JSON.stringify(a)).join(", ")} \u2014 got ${JSON.stringify(v)}. The platform refuses any other spelling rather than reading it as a different sign-in flow.`
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function encodeMcpOauth(owner, oauth) {
|
|
493
|
+
if (oauth === null || typeof oauth !== "object" || Array.isArray(oauth)) {
|
|
494
|
+
throw new Error(`${owner} \`oauth\`: expected { mode: "hosted" | "external", authTable, \u2026 }.`);
|
|
495
|
+
}
|
|
496
|
+
const raw = oauth;
|
|
497
|
+
oneOf(owner, "mode", raw.mode, ["hosted", "external"]);
|
|
498
|
+
if (raw.mode === void 0) throw new Error(`${owner} \`oauth.mode\` is required: "hosted" or "external".`);
|
|
499
|
+
for (const key of REMOVED_KEYS) {
|
|
500
|
+
if (key in raw) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
`${owner} \`oauth.${key}\` no longer exists: the platform dropped \`consent\`, \`branding\` and \`trustedClients\`. Your \`loginUrl\` page is the consent screen \u2014 show what \`s.mcp.oauth.request\` returns (client name, logo, callback host, whether the client is verified) and pass the user's choice to \`s.mcp.oauth.complete\` as \`decision\`. Remove the key.`
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
refuseUnknown(`${owner} \`oauth\` (mode "${String(raw.mode)}")`, raw, raw.mode === "hosted" ? HOSTED_KEYS : EXTERNAL_KEYS);
|
|
507
|
+
const table = raw.authTable;
|
|
508
|
+
if (table === void 0 || table === null || table === false || table === "") {
|
|
509
|
+
throw new Error(
|
|
510
|
+
`${owner} \`oauth.authTable\` is required: the auth table (a \`table({ auth: true })\` handle or its name) whose row a sign-in resolves to.`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
const authTable = resolveAuthRef("mcpServer oauth", owner, table);
|
|
514
|
+
const out = {
|
|
515
|
+
mode: raw.mode,
|
|
516
|
+
auth_table: authTable,
|
|
517
|
+
login_url: "",
|
|
518
|
+
issuer: "",
|
|
519
|
+
audience: [],
|
|
520
|
+
audience_ack: false,
|
|
521
|
+
claim: "sub",
|
|
522
|
+
claim_ack: false,
|
|
523
|
+
column: "",
|
|
524
|
+
preset: "",
|
|
525
|
+
allowed_client_ids: []
|
|
526
|
+
};
|
|
527
|
+
if (oauth.mode === "hosted") {
|
|
528
|
+
out.login_url = envString(owner, "loginUrl", oauth.loginUrl ?? "");
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
oneOf(owner, "preset", oauth.preset, MCP_OAUTH_PRESETS);
|
|
532
|
+
out.issuer = envString(owner, "issuer", oauth.issuer ?? "");
|
|
533
|
+
out.audience = strings(owner, "audience", oauth.audience, true);
|
|
534
|
+
out.audience_ack = oauth.audienceAck === true;
|
|
535
|
+
if (oauth.claim !== void 0 && typeof oauth.claim !== "string") throw new Error(`${owner} \`oauth.claim\`: expected a string.`);
|
|
536
|
+
out.claim = oauth.claim || "sub";
|
|
537
|
+
out.claim_ack = oauth.claimAck === true;
|
|
538
|
+
if (oauth.column !== void 0 && typeof oauth.column !== "string") throw new Error(`${owner} \`oauth.column\`: expected a string.`);
|
|
539
|
+
out.column = oauth.column ?? "";
|
|
540
|
+
out.preset = oauth.preset ?? "";
|
|
541
|
+
out.allowed_client_ids = strings(owner, "allowedClientIds", oauth.allowedClientIds, false);
|
|
542
|
+
return out;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/workspace/guards-mcp-oauth.ts
|
|
546
|
+
function checkMcpOauth(sections, bag) {
|
|
547
|
+
for (const set of sections.toolset ?? []) {
|
|
548
|
+
if (!set || typeof set !== "object") continue;
|
|
549
|
+
const { name, oauth } = set;
|
|
550
|
+
if (!oauth || typeof oauth !== "object" || Array.isArray(oauth)) continue;
|
|
551
|
+
const o = oauth;
|
|
552
|
+
const server = `mcpServer "${String(name ?? "?")}"`;
|
|
553
|
+
const text = (k) => typeof o[k] === "string" ? o[k].trim() : "";
|
|
554
|
+
if (o.mode === "hosted" && text("login_url") === "") {
|
|
555
|
+
bag.error(
|
|
556
|
+
"mcp.oauth-hosted-incomplete",
|
|
557
|
+
`${server}: \`oauth\` is hosted mode with no \`loginUrl\`. A client signing in is sent to that page, so the platform refuses the push without one. Set \`loginUrl\` to your login page (https), or \`env("NAME")\`.`,
|
|
558
|
+
set
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
if (o.mode !== "external") continue;
|
|
562
|
+
const missing = ["issuer", "column"].filter((k) => text(k) === "");
|
|
563
|
+
if (missing.length > 0) {
|
|
564
|
+
bag.error(
|
|
565
|
+
"mcp.oauth-external-incomplete",
|
|
566
|
+
`${server}: \`oauth\` is external mode with no ${missing.map((k) => `\`${k}\``).join(" or ")}. The \`issuer\` is what a token is verified against and the \`column\` is the auth-table column its claim names a row by; the platform refuses the push without both.`,
|
|
567
|
+
set
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const preset = text("preset");
|
|
571
|
+
const clientIds = Array.isArray(o.allowed_client_ids) ? o.allowed_client_ids : [];
|
|
572
|
+
if (MCP_OAUTH_PRESETS_WITHOUT_AUD.includes(preset) && clientIds.length === 0) {
|
|
573
|
+
bag.error(
|
|
574
|
+
"mcp.oauth-preset-client-ids",
|
|
575
|
+
`${server}: \`oauth.preset\` "${preset}" issues access tokens without an audience, so nothing ties a token to this server. Set \`allowedClientIds\` to the client ids allowed to call it; the platform refuses the push without them.`,
|
|
576
|
+
set
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
const claim = typeof o.claim === "string" && o.claim !== "" ? o.claim : "sub";
|
|
580
|
+
if (!MCP_OAUTH_STABLE_CLAIMS.includes(claim) && o.claim_ack !== true) {
|
|
581
|
+
bag.error(
|
|
582
|
+
"mcp.oauth-claim-unacked",
|
|
583
|
+
`${server}: \`oauth.claim\` "${claim}" can be edited by users at many identity providers, which would let a user choose the row they sign in as. Use "sub", "oid" or "email", or set \`claimAck: true\` to accept it; the platform refuses the push otherwise.`,
|
|
584
|
+
set
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function isEmptyRevokeTarget(input) {
|
|
590
|
+
if (typeof input?.tag !== "string" || !input.tag.startsWith("const")) return false;
|
|
591
|
+
const value = String(input.value ?? "");
|
|
592
|
+
if (input.name === "user_id") return value === "";
|
|
593
|
+
if (input.name === "grant_id") return value === "" || Number(value) <= 0;
|
|
594
|
+
if (input.name === "all") return value === "" || value === "false" || value === "0";
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
function checkMcpOauthRevoke(sections, bag) {
|
|
598
|
+
for (const [payloadKey, arr] of Object.entries(sections)) {
|
|
599
|
+
for (const obj2 of arr ?? []) {
|
|
600
|
+
if (!obj2 || typeof obj2 !== "object") continue;
|
|
601
|
+
walkNodes(obj2, (node) => {
|
|
602
|
+
if (node.name !== "mvp:mcp_oauth_revoke" || !Array.isArray(node.input)) return;
|
|
603
|
+
const targets = node.input.filter((i) => !isEmptyRevokeTarget(i)).map((i) => i?.name).filter((n) => n === "user_id" || n === "grant_id" || n === "all");
|
|
604
|
+
if (targets.length === 1) return;
|
|
605
|
+
const name = obj2.name;
|
|
606
|
+
const owner = `${sdkKindName(payloadKey, obj2)} "${typeof name === "string" ? name : "?"}"`;
|
|
607
|
+
bag.error(
|
|
608
|
+
"mcp.oauth-revoke-target",
|
|
609
|
+
`${owner}: \`s.mcp.oauth.revoke\` needs exactly one of \`user_id\`, \`grant_id\` or \`all\`, but it names ${targets.length === 0 ? "no target" : targets.map((t) => `\`${t}\``).join(" and ")} \u2014 the platform refuses any other combination every time the statement runs.`,
|
|
610
|
+
obj2
|
|
611
|
+
);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// src/workspace/filter-operands.ts
|
|
618
|
+
var SCALAR_COLUMN_TYPES = /* @__PURE__ */ new Set([
|
|
619
|
+
"int",
|
|
620
|
+
"decimal",
|
|
621
|
+
"text",
|
|
622
|
+
"bool",
|
|
623
|
+
"epochms",
|
|
624
|
+
"timestamp",
|
|
625
|
+
"date",
|
|
626
|
+
"email",
|
|
627
|
+
"password",
|
|
628
|
+
"enum",
|
|
629
|
+
"uuid"
|
|
630
|
+
]);
|
|
631
|
+
var SCALAR_TAGS = /* @__PURE__ */ new Set(["const", "const:encoded", "const:int", "const:decimal", "const:bool", "const:null"]);
|
|
632
|
+
var NUMERIC_TEXT = {
|
|
633
|
+
int: /^\s*[+-]?\d+\s*$/,
|
|
634
|
+
decimal: /^\s*(?:[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?|nan|[+-]?inf(?:inity)?)\s*$/i
|
|
635
|
+
};
|
|
636
|
+
function outsideInt64(v) {
|
|
637
|
+
const text = typeof v === "number" && Number.isInteger(v) ? BigInt(v).toString() : typeof v === "string" ? v.trim() : void 0;
|
|
638
|
+
if (text === void 0 || !/^[+-]?\d+$/.test(text)) return false;
|
|
639
|
+
const n = BigInt(text);
|
|
640
|
+
return n > 2n ** 63n - 1n || n < -(2n ** 63n);
|
|
641
|
+
}
|
|
642
|
+
function isScalar(v) {
|
|
643
|
+
return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
|
|
644
|
+
}
|
|
645
|
+
var COMPARISON_OPS = /* @__PURE__ */ new Set(["=", "==", "!=", "===", "!==", "<", "<=", ">", ">="]);
|
|
646
|
+
var PATTERN_OPS = /* @__PURE__ */ new Set(["like", "not like", "ilike", "not ilike", "~", "!~", "includes", "not includes", "search"]);
|
|
647
|
+
var CONTAINMENT_OPS = /* @__PURE__ */ new Set(["contains", "not contains", "overlaps", "not overlaps"]);
|
|
648
|
+
function unfiltered(o) {
|
|
649
|
+
return !Array.isArray(o.filters) || o.filters.length === 0;
|
|
650
|
+
}
|
|
651
|
+
function literalShape(o) {
|
|
652
|
+
if (typeof o.tag !== "string" || !unfiltered(o)) return void 0;
|
|
653
|
+
const text = typeof o.operand === "string" ? o.operand : void 0;
|
|
654
|
+
if (text?.startsWith("(")) return void 0;
|
|
655
|
+
if (SCALAR_TAGS.has(o.tag)) return { kind: "scalar" };
|
|
656
|
+
if (o.tag !== "const:array" && o.tag !== "const:obj") return void 0;
|
|
657
|
+
let value;
|
|
658
|
+
try {
|
|
659
|
+
value = JSON.parse(text ?? "");
|
|
660
|
+
} catch {
|
|
661
|
+
return void 0;
|
|
662
|
+
}
|
|
663
|
+
if (Array.isArray(value)) return { kind: "list", length: value.length, items: value };
|
|
664
|
+
if (!value || typeof value !== "object") return void 0;
|
|
665
|
+
const keys2 = Object.keys(value);
|
|
666
|
+
if (keys2.every((k, i) => k === String(i))) return void 0;
|
|
667
|
+
return { kind: "object" };
|
|
668
|
+
}
|
|
669
|
+
function objectLiteral(o) {
|
|
670
|
+
return o.tag === "const:obj" && o.operand === "{}" && Array.isArray(o.filters) && o.filters.length > 0 && o.filters.every((x) => x?.name === "set");
|
|
671
|
+
}
|
|
672
|
+
function emptyLiteral(o) {
|
|
673
|
+
if (o.tag === "const:null") return true;
|
|
674
|
+
return typeof o.operand !== "string" || ["", "0", "[]", "{}", "false", "null"].includes(o.operand);
|
|
675
|
+
}
|
|
676
|
+
function tableShapes(sections) {
|
|
677
|
+
const out = /* @__PURE__ */ new Map();
|
|
678
|
+
for (const raw of sections.dbo ?? []) {
|
|
679
|
+
const t = raw;
|
|
680
|
+
if (typeof t?.guid !== "string" || !Array.isArray(t.schema)) continue;
|
|
681
|
+
const cols = /* @__PURE__ */ new Map();
|
|
682
|
+
for (const c of t.schema) {
|
|
683
|
+
if (typeof c?.name !== "string" || typeof c.type !== "string") continue;
|
|
684
|
+
cols.set(c.name, { type: c.type, list: c.style?.type === "list" });
|
|
685
|
+
}
|
|
686
|
+
out.set(t.guid, cols);
|
|
687
|
+
}
|
|
688
|
+
return out;
|
|
689
|
+
}
|
|
690
|
+
function describeColumn(name, c) {
|
|
691
|
+
return `\`${name}\` (${c.type}${c.list ? " list" : ""})`;
|
|
692
|
+
}
|
|
693
|
+
function describeLiteral(s) {
|
|
694
|
+
if (s.kind === "list") return `a ${s.length}-element list`;
|
|
695
|
+
return s.kind === "object" ? "an object" : "a scalar";
|
|
696
|
+
}
|
|
697
|
+
function problem(op, left, right, columns) {
|
|
698
|
+
if (right.ignore_empty === true && emptyLiteral(right)) return void 0;
|
|
699
|
+
const lit = literalShape(right);
|
|
700
|
+
const leftName = left.tag === "col" && typeof left.operand === "string" && unfiltered(left) ? left.operand : void 0;
|
|
701
|
+
const column = leftName !== void 0 && !leftName.includes(".") ? columns?.get(leftName) : void 0;
|
|
702
|
+
if (op === "between" || op === "not between") {
|
|
703
|
+
if (left.tag !== "col") return void 0;
|
|
704
|
+
if (right.tag === "col") return `\`${op}\` takes a literal two-value list on the right, not a column`;
|
|
705
|
+
if (lit && !(lit.kind === "list" && lit.length === 2)) {
|
|
706
|
+
return `\`${op}\` needs exactly a two-value list (\`c.array([low, high])\`) on the right; it has ${describeLiteral(lit)}`;
|
|
707
|
+
}
|
|
708
|
+
return void 0;
|
|
709
|
+
}
|
|
710
|
+
if (!column) return void 0;
|
|
711
|
+
const col = describeColumn(leftName, column);
|
|
712
|
+
if (CONTAINMENT_OPS.has(op) && !column.list && SCALAR_COLUMN_TYPES.has(column.type)) {
|
|
713
|
+
return `\`${op}\` is json/list containment and ${col} is neither \u2014 for a substring match use \`includes\``;
|
|
714
|
+
}
|
|
715
|
+
if (CONTAINMENT_OPS.has(op) && column.type === "json" && !column.list && right.tag !== "col") {
|
|
716
|
+
return `\`${op}\` on a json column has no working operand: the database cannot compare ${col} with the list it builds, whatever the value. Store the values in a list column (\`f.text({ array: true })\`), where \`${op}\` works, or match a json array of objects with \`@>\` (\`c.array([{ \u2026 }])\`)`;
|
|
717
|
+
}
|
|
718
|
+
if (op === "@>" && SCALAR_COLUMN_TYPES.has(column.type)) {
|
|
719
|
+
return `\`@>\` needs a json column on the left, and ${col} is not one`;
|
|
720
|
+
}
|
|
721
|
+
if (op === "@>" && !column.list && objectLiteral(right)) {
|
|
722
|
+
return `\`@>\` with an object literal on the right fails with "Invalid expression" \u2014 bind the object first (\`s.set_var("match", c.obj({ \u2026 }))\`, then \`ref("match")\`) or take it as a json input; a json array of objects matches with \`c.array([{ \u2026 }])\``;
|
|
723
|
+
}
|
|
724
|
+
if ((op === "includes" || op === "not includes") && column.type === "bool") {
|
|
725
|
+
return `\`${op}\` does not take a bool column (${col})`;
|
|
726
|
+
}
|
|
727
|
+
if (!lit) return void 0;
|
|
728
|
+
if (op === "in" || op === "not in") {
|
|
729
|
+
if (!column.list && lit.kind !== "list") return `\`${op}\` on ${col} needs a list on the right; it has ${describeLiteral(lit)}`;
|
|
730
|
+
if (column.list && lit.kind === "list") return `\`${op}\` needs a scalar on one side, and ${col} and the right side are both lists`;
|
|
731
|
+
return void 0;
|
|
732
|
+
}
|
|
733
|
+
if (op === "@>" && column.type === "json") {
|
|
734
|
+
if (lit.kind === "scalar" || lit.kind === "list" && isScalar(lit.items[0])) {
|
|
735
|
+
return `\`@>\` needs a json value on the right, and a ${lit.kind === "scalar" ? "scalar" : "list of scalars"} is not one \u2014 the engine reads a list by its first element. A list of objects works (\`c.array([{ \u2026 }])\`); matching scalar elements needs a list column (\`f.text({ array: true })\`) with \`contains\``;
|
|
736
|
+
}
|
|
737
|
+
return void 0;
|
|
738
|
+
}
|
|
739
|
+
const numeric = NUMERIC_TEXT[column.type];
|
|
740
|
+
if (CONTAINMENT_OPS.has(op) && column.list && numeric !== void 0 && lit.kind === "list") {
|
|
741
|
+
const bad = lit.items.find((v) => typeof v === "string" && !numeric.test(v));
|
|
742
|
+
if (bad !== void 0) {
|
|
743
|
+
return `\`${op}\` on ${col} casts every element to ${column.type === "int" ? "a whole number" : "a number"}, and ${JSON.stringify(bad)} is not one`;
|
|
744
|
+
}
|
|
745
|
+
const big = column.type === "int" ? lit.items.find(outsideInt64) : void 0;
|
|
746
|
+
if (big !== void 0) {
|
|
747
|
+
return `\`${op}\` on ${col} casts every element to a 64-bit whole number, and ${JSON.stringify(big)} is out of its range`;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
if (CONTAINMENT_OPS.has(op)) {
|
|
751
|
+
if (lit.kind === "scalar") return `\`${op}\` needs json or a list on the right; it has a scalar \u2014 wrap it: \`c.array([value])\``;
|
|
752
|
+
return void 0;
|
|
753
|
+
}
|
|
754
|
+
if (COMPARISON_OPS.has(op)) {
|
|
755
|
+
if (lit.kind === "list") return `\`${op}\` does not compare lists; the right side is ${describeLiteral(lit)} \u2014 use \`in\``;
|
|
756
|
+
if (column.list && right.tag !== "const:null") return `\`${op}\` does not compare lists, and ${col} is one`;
|
|
757
|
+
return void 0;
|
|
758
|
+
}
|
|
759
|
+
if (PATTERN_OPS.has(op)) {
|
|
760
|
+
if (lit.kind === "list") return `\`${op}\` does not take a list on the right; it has ${describeLiteral(lit)}`;
|
|
761
|
+
}
|
|
762
|
+
return void 0;
|
|
763
|
+
}
|
|
764
|
+
function walk(node, visit) {
|
|
765
|
+
const stack = [node];
|
|
766
|
+
while (stack.length > 0) {
|
|
767
|
+
const n = stack.pop();
|
|
768
|
+
if (Array.isArray(n)) {
|
|
769
|
+
for (let i = n.length - 1; i >= 0; i--) stack.push(n[i]);
|
|
770
|
+
} else if (n && typeof n === "object") {
|
|
771
|
+
const record = n;
|
|
772
|
+
visit(record);
|
|
773
|
+
const values = Object.values(record);
|
|
774
|
+
for (let i = values.length - 1; i >= 0; i--) stack.push(values[i]);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function checkFilterOperands(sections, bag) {
|
|
779
|
+
const tables = tableShapes(sections);
|
|
780
|
+
for (const [payloadKey, arr] of Object.entries(sections)) {
|
|
781
|
+
for (const obj2 of arr ?? []) {
|
|
782
|
+
if (!obj2 || typeof obj2 !== "object") continue;
|
|
783
|
+
const found = [];
|
|
784
|
+
walk(obj2, (record) => {
|
|
785
|
+
const context = record.context;
|
|
786
|
+
if (!context || typeof context.search !== "object" || context.search === null) return;
|
|
787
|
+
const id = context.dbo?.id;
|
|
788
|
+
const columns = typeof id === "string" ? tables.get(id) : void 0;
|
|
789
|
+
walk(context.search, (node) => {
|
|
790
|
+
const s = node.statement;
|
|
791
|
+
if (!s || typeof s !== "object" || typeof s.op !== "string") return;
|
|
792
|
+
const why = problem(s.op, s.left ?? {}, s.right ?? {}, columns);
|
|
793
|
+
if (why !== void 0) found.push(why);
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
if (found.length === 0) continue;
|
|
797
|
+
const name = obj2.name;
|
|
798
|
+
const owner = `${sdkKindName(payloadKey, obj2)} "${typeof name === "string" ? name : "?"}"`;
|
|
799
|
+
for (const why of [...new Set(found)]) {
|
|
800
|
+
bag.warn(
|
|
801
|
+
"db.filter-operand-invalid",
|
|
802
|
+
`${owner}: a \`where\` clause fails every request that reaches it, whatever the data \u2014 ${why}.`,
|
|
803
|
+
obj2
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// src/workspace/xano.ts
|
|
811
|
+
var MIDDLEWARE_HOST_KINDS = ["query", "task", "function", "tool", "prompt", "resource"];
|
|
812
|
+
var XANO_BRAND = /* @__PURE__ */ Symbol.for("xanosdk.Xano");
|
|
813
|
+
var XANO_ORIGIN = /* @__PURE__ */ Symbol.for("xanosdk.Xano.origin");
|
|
814
|
+
function nameResponseRecordError(err, kindName, def) {
|
|
815
|
+
if (!(err instanceof Error)) return err;
|
|
816
|
+
const response = def?.response;
|
|
817
|
+
if (response === null || typeof response !== "object" || isTaggedValue(response)) return err;
|
|
818
|
+
if (err.message.startsWith("`response.")) {
|
|
819
|
+
err.message = `${authorKindName(kindName)} "${String(def.name)}": ${err.message}`;
|
|
820
|
+
return err;
|
|
821
|
+
}
|
|
822
|
+
if (err.message.startsWith("obj() takes a { key: value } record")) {
|
|
823
|
+
const bad = Object.entries(response).find(([, m]) => typeof m !== "object" || m === null || Array.isArray(m));
|
|
824
|
+
if (bad !== void 0) {
|
|
825
|
+
const [key, member] = bad;
|
|
826
|
+
err.message = `${authorKindName(kindName)} "${String(def.name)}": \`response.${key}\` is ${describeEntry(member)} \u2014 a response key takes a value (\`ref("x")\`, \`c.*\`) or a { key: value } record.` + (Array.isArray(member) ? ` A list of values goes inside a record member (\`${key}: { items: [ \u2026 ] }\`), or bind it first (\`s.set_var\`) and use \`ref()\`; a list of constants is \`c.array([ \u2026 ])\`.` : "");
|
|
827
|
+
}
|
|
828
|
+
return err;
|
|
829
|
+
}
|
|
830
|
+
if (!err.message.startsWith("obj(): `")) return err;
|
|
831
|
+
for (const [key, member] of Object.entries(response)) {
|
|
832
|
+
if (member === null || typeof member !== "object" || Array.isArray(member) || isTaggedValue(member)) continue;
|
|
833
|
+
try {
|
|
834
|
+
obj(member);
|
|
835
|
+
} catch {
|
|
836
|
+
err.message = `${authorKindName(kindName)} "${String(def.name)}": ` + err.message.replace("obj(): `", `\`response.${key}.`) + ` A nested response record is one object expression; a top-level response key can carry this value as it is.`;
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return err;
|
|
841
|
+
}
|
|
842
|
+
function assertDefEntry(via, kindName, def, index) {
|
|
843
|
+
if (typeof def === "object" && def !== null && !Array.isArray(def)) return;
|
|
844
|
+
const at = index === void 0 ? "its argument" : `entry [${index}]`;
|
|
845
|
+
const hint = def === false || def === void 0 ? " A `cond && def` entry leaves false/undefined behind \u2014 spread it instead: `...(cond ? [def] : [])`." : typeof def === "function" ? " A factory passed uncalled is a function \u2014 call it: `table({ \u2026 })`, not `table`." : Array.isArray(def) && index === void 0 ? ` It takes one def, not a list \u2014 \`${via}(def)\`.` : "";
|
|
846
|
+
throw new Error(`${via}: ${at} must be ${withArticle(authorKindName(kindName))} def \u2014 got ${describeEntry(def)}.${hint}`);
|
|
847
|
+
}
|
|
848
|
+
function authorKindName(kindName) {
|
|
849
|
+
return AUTHOR_KIND_NAME[kindName] ?? (kindName === "workspace" ? "workspaceConfig" : kindName);
|
|
850
|
+
}
|
|
851
|
+
var REGISTER_METHOD = {
|
|
852
|
+
workspace: "registerWorkspace",
|
|
853
|
+
table: "registerTables",
|
|
854
|
+
function: "registerFunctions",
|
|
855
|
+
query: "registerQueries",
|
|
856
|
+
api_group: "registerApiGroups",
|
|
857
|
+
task: "registerTasks",
|
|
858
|
+
middleware: "registerMiddleware",
|
|
859
|
+
addon: "registerAddons",
|
|
860
|
+
tool: "registerTools",
|
|
861
|
+
prompt: "registerPrompts",
|
|
862
|
+
resource: "registerResources",
|
|
863
|
+
mcp_server: "registerMcpServers",
|
|
864
|
+
agent: "registerAgents",
|
|
865
|
+
trigger: "registerTriggers",
|
|
866
|
+
knowledge: "registerKnowledge",
|
|
867
|
+
microservice: "registerMicroservices",
|
|
868
|
+
realtime_server: "registerRealtimeServers",
|
|
869
|
+
channel: "registerRealtimeChannels",
|
|
870
|
+
message: "registerRealtimeMessages",
|
|
871
|
+
workflow_test: "registerWorkflowTests"
|
|
872
|
+
};
|
|
873
|
+
function structuralKind(def, kindName) {
|
|
874
|
+
if (typeof def !== "object" || def === null) return void 0;
|
|
875
|
+
const d = def;
|
|
876
|
+
if (typeof d.verb === "string") return "query";
|
|
877
|
+
if (typeof d.objType === "string") return "trigger";
|
|
878
|
+
if (d.cors !== void 0 || d.swagger !== void 0 && kindName !== "workspace") return "api_group";
|
|
879
|
+
if (d.canonical !== void 0 && !CANONICAL_KINDS.has(kindName)) return "api_group";
|
|
880
|
+
return void 0;
|
|
881
|
+
}
|
|
882
|
+
function standaloneDefKind(def) {
|
|
883
|
+
const branded = defKindOf(def);
|
|
884
|
+
if (branded !== void 0) return branded;
|
|
885
|
+
const structural = structuralKind(def, "");
|
|
886
|
+
if (structural !== void 0) return structural;
|
|
887
|
+
if (typeof def !== "object" || def === null) return void 0;
|
|
888
|
+
if (def.schema !== void 0) return "table";
|
|
889
|
+
const keys2 = Object.keys(def);
|
|
890
|
+
if (keys2.includes("name") && keys2.every((k) => API_GROUP_KEYS.has(k))) return "api_group";
|
|
891
|
+
return void 0;
|
|
892
|
+
}
|
|
893
|
+
var API_GROUP_KEYS = new Set(DEF_KEYS["api_group"]);
|
|
894
|
+
var CANONICAL_KINDS = /* @__PURE__ */ new Set(["api_group", "mcp_server", "agent", "realtime_server", "workspace"]);
|
|
895
|
+
function assertDefKind(via, kindName, def, index) {
|
|
896
|
+
const made = defKindOf(def) ?? structuralKind(def, kindName);
|
|
897
|
+
if (made === void 0 || made === kindName) return;
|
|
898
|
+
const at = index === void 0 ? "its argument" : `entry [${index}]`;
|
|
899
|
+
const method = REGISTER_METHOD[made];
|
|
900
|
+
throw new Error(
|
|
901
|
+
`${via}: ${at} is ${withArticle(authorKindName(made))} def, not ${withArticle(authorKindName(kindName))} def` + (method === void 0 ? "." : ` \u2014 use ${method}.`)
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
function acceptedWarnings(kindName, def) {
|
|
905
|
+
const diagnostics = def?.diagnostics;
|
|
906
|
+
if (diagnostics === void 0 || diagnostics === null) return void 0;
|
|
907
|
+
const offered = allowableWarnings(kindName);
|
|
908
|
+
const name = def.name;
|
|
909
|
+
const owner = `${authorKindName(kindName)}${typeof name === "string" ? ` "${name}"` : ""}`;
|
|
910
|
+
const codes = offered.map((c) => `"${c}"`).join(", ");
|
|
911
|
+
if (offered.length === 0) throw new Error(`${owner}: \`diagnostics\` \u2014 this def raises no warning that can be accepted.`);
|
|
912
|
+
const allow = diagnostics.allow;
|
|
913
|
+
const extra = typeof diagnostics === "object" ? Object.keys(diagnostics).find((k) => k !== "allow") : void 0;
|
|
914
|
+
if (typeof diagnostics !== "object" || !Array.isArray(allow) || extra !== void 0) {
|
|
915
|
+
throw new Error(
|
|
916
|
+
`${owner}: \`diagnostics\` must be { allow: [...] } \u2014 got ${describeEntry(extra ?? allow ?? diagnostics)}${extra === void 0 ? "" : " as a key"}. Codes this def can accept: ${codes}.`
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
for (const code of allow) {
|
|
920
|
+
if (typeof code === "string" && offered.includes(code)) continue;
|
|
921
|
+
const near = typeof code === "string" ? nearestCode(code, offered) : void 0;
|
|
922
|
+
throw new Error(
|
|
923
|
+
`${owner}: \`diagnostics.allow\` names ${typeof code === "string" ? `unknown code "${code}"` : describeEntry(code)} for ${withArticle(authorKindName(kindName))}${near === void 0 ? "." : ` \u2014 did you mean "${near}"?`} Codes this def can accept: ${codes}.`
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
return new Set(allow);
|
|
927
|
+
}
|
|
928
|
+
function asAuthoringRefusal(err) {
|
|
929
|
+
if (err instanceof Error && Object.getPrototypeOf(err) === Error.prototype && err.code === void 0) {
|
|
930
|
+
Object.defineProperty(err, "name", { value: "UsageError", configurable: true, writable: true });
|
|
931
|
+
}
|
|
932
|
+
return err;
|
|
933
|
+
}
|
|
934
|
+
function nearestCode(code, offered) {
|
|
935
|
+
return nearestKey(code, offered) ?? offered.find((c) => c.includes(code) || code.includes(c));
|
|
936
|
+
}
|
|
937
|
+
var Xano = class {
|
|
938
|
+
/** @internal cross-realm identity brand — see {@link Xano.isXano}. */
|
|
939
|
+
[XANO_BRAND] = true;
|
|
940
|
+
constructor() {
|
|
941
|
+
Object.defineProperty(this, XANO_ORIGIN, { value: new Error("registry created"), enumerable: false });
|
|
942
|
+
}
|
|
943
|
+
/** Encoded objects keyed by `packageExport` payload key. */
|
|
944
|
+
sections = /* @__PURE__ */ new Map();
|
|
945
|
+
/** Table defs, encoded lazily at {@link export} so each can inherit the workspace `use_xdo`. */
|
|
946
|
+
tableDefs = [];
|
|
947
|
+
knowledgeDefs = [];
|
|
948
|
+
workspaceConfig = {};
|
|
949
|
+
/**
|
|
950
|
+
* Whether the attached config named `env` at all — `env: {}` included. The
|
|
951
|
+
* encoded config always carries an `env` list, so it cannot tell a config that
|
|
952
|
+
* declares none on purpose from one that was never attached; this can.
|
|
953
|
+
*/
|
|
954
|
+
envAuthored = false;
|
|
955
|
+
/**
|
|
956
|
+
* Whether a config was passed to {@link registerWorkspace} by the author, as
|
|
957
|
+
* opposed to the `{ name }` the {@link workspace} shorthand registers. Lets the
|
|
958
|
+
* env guard tell "no config attached" from "the attached config has no `env`".
|
|
959
|
+
*/
|
|
960
|
+
configAttached = false;
|
|
961
|
+
bundleType = "workspace";
|
|
962
|
+
/**
|
|
963
|
+
* Encoded objects whose `canonical` a locked {@link export} filled in (from
|
|
964
|
+
* the lock or freshly minted) rather than the author writing it.
|
|
965
|
+
*
|
|
966
|
+
* The fill mutates the registered object, so a second `export()` off the same
|
|
967
|
+
* registry can no longer see that the slug started empty. Without this the
|
|
968
|
+
* re-export would classify a minted token as a pinned one and a later release
|
|
969
|
+
* would refuse over a URL nobody promised.
|
|
970
|
+
*/
|
|
971
|
+
filledCanonicals = /* @__PURE__ */ new WeakSet();
|
|
972
|
+
/** The filled canonicals whose lock entry was adopted from a live backend (`canonical_source: "adopted"`). */
|
|
973
|
+
adoptedCanonicals = /* @__PURE__ */ new WeakSet();
|
|
974
|
+
/** Every guid a def pins with its own `guid:` — recorded in the lock as `guid_source: "code"`. */
|
|
975
|
+
codeGuids = /* @__PURE__ */ new Set();
|
|
976
|
+
/** True for any `Xano` registry, even one created by a different module instance. */
|
|
977
|
+
static isXano(value) {
|
|
978
|
+
return typeof value === "object" && value !== null && value[XANO_BRAND] === true;
|
|
979
|
+
}
|
|
980
|
+
/** Encode one def and stamp its deterministic guid (the engine's sync/reference anchor). */
|
|
981
|
+
encodeOne(kind, def) {
|
|
982
|
+
let encoded;
|
|
983
|
+
const raised = [];
|
|
984
|
+
const previous = setDiagnosticSink((d) => raised.push(d));
|
|
985
|
+
try {
|
|
986
|
+
encoded = kind.encode(def);
|
|
987
|
+
} catch (err) {
|
|
988
|
+
throw asAuthoringRefusal(nameResponseRecordError(err, kind.name, def));
|
|
989
|
+
} finally {
|
|
990
|
+
setDiagnosticSink(previous);
|
|
991
|
+
}
|
|
992
|
+
if (raised.length > 0 && encoded !== null && typeof encoded === "object") this.encodeWarnings.set(encoded, raised);
|
|
993
|
+
assertTestIds(encoded, kind.name);
|
|
994
|
+
assertMockKeysResolved(encoded, kind.name);
|
|
995
|
+
if (REFERENCEABLE_KINDS.has(kind.name) && encoded && typeof encoded === "object") {
|
|
996
|
+
const obj2 = encoded;
|
|
997
|
+
const explicit = def.guid;
|
|
998
|
+
if (obj2.guid === void 0 && typeof obj2.name === "string") {
|
|
999
|
+
obj2.guid = explicit ?? kind.guidOf?.(def) ?? deriveGuid(kind.payloadKey, obj2.name);
|
|
1000
|
+
}
|
|
1001
|
+
if (typeof explicit === "string" && explicit !== "" && obj2.guid === explicit) this.codeGuids.add(explicit);
|
|
1002
|
+
}
|
|
1003
|
+
return encoded;
|
|
1004
|
+
}
|
|
1005
|
+
/** Register one or more authoring defs of a given kind. */
|
|
1006
|
+
register(kindName, defOrDefs, via = `register(${JSON.stringify(kindName)})`) {
|
|
1007
|
+
const kind = getKind(kindName);
|
|
1008
|
+
const defs = Array.isArray(defOrDefs) ? defOrDefs : [defOrDefs];
|
|
1009
|
+
defs.forEach((def, i) => {
|
|
1010
|
+
assertDefEntry(via, kind.name, def, Array.isArray(defOrDefs) ? i : void 0);
|
|
1011
|
+
assertDefKind(via, kind.name, def, Array.isArray(defOrDefs) ? i : void 0);
|
|
1012
|
+
const name = def.name;
|
|
1013
|
+
assertDefKeys(`${authorKindName(kind.name)}${typeof name === "string" ? ` "${name}"` : ""}`, def, DEF_KEYS[kind.name], kind.name);
|
|
1014
|
+
assertDefShape(kind.name, def);
|
|
1015
|
+
assertNameAsStored(authorKindName(kind.name), name);
|
|
1016
|
+
});
|
|
1017
|
+
const allows = defs.map((def) => acceptedWarnings(kind.name, def));
|
|
1018
|
+
for (const def of defs) this.assertNotAlreadyRegistered(kind.name, def);
|
|
1019
|
+
if (kind.name === "table") {
|
|
1020
|
+
defs.forEach((def, i) => {
|
|
1021
|
+
this.tableDefs.push(def);
|
|
1022
|
+
if (allows[i]) this.accepted.set(def, allows[i]);
|
|
1023
|
+
});
|
|
1024
|
+
return this;
|
|
1025
|
+
}
|
|
1026
|
+
if (kind.name === "knowledge") {
|
|
1027
|
+
for (const def of defs) this.knowledgeDefs.push(def);
|
|
1028
|
+
}
|
|
1029
|
+
const bucket = this.sections.get(kind.payloadKey) ?? [];
|
|
1030
|
+
defs.forEach((def, i) => {
|
|
1031
|
+
const encoded = this.encodeOne(kind, def);
|
|
1032
|
+
const allow = allows[i];
|
|
1033
|
+
if (allow && encoded && typeof encoded === "object") this.accepted.set(encoded, allow);
|
|
1034
|
+
bucket.push(encoded);
|
|
1035
|
+
});
|
|
1036
|
+
this.sections.set(kind.payloadKey, bucket);
|
|
1037
|
+
return this;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* A table's bundle entry, encoded now that the workspace `use_xdo` is known: a
|
|
1041
|
+
* table without an explicit `useXdo` inherits the workspace default.
|
|
1042
|
+
*/
|
|
1043
|
+
encodeTableEntry(def) {
|
|
1044
|
+
const wsUseXdo = this.workspaceConfig.use_xdo === true;
|
|
1045
|
+
const encoded = this.encodeOne(
|
|
1046
|
+
getKind("table"),
|
|
1047
|
+
def.useXdo === void 0 ? { ...def, useXdo: wsUseXdo } : def
|
|
1048
|
+
);
|
|
1049
|
+
const dbo = { ...encoded, import: { mode: "standard" } };
|
|
1050
|
+
const allow = this.accepted.get(def);
|
|
1051
|
+
if (allow) this.accepted.set(dbo, allow);
|
|
1052
|
+
const raised = this.encodeWarnings.get(encoded);
|
|
1053
|
+
if (raised) this.encodeWarnings.set(dbo, raised);
|
|
1054
|
+
return dbo;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* @internal The entry `def` becomes in the bundle a registry holding only it
|
|
1058
|
+
* exports — what `emit` and `xanosdk compile` print. Registered first, so the
|
|
1059
|
+
* def is validated exactly as a `register*` call validates it; the encoder's
|
|
1060
|
+
* warnings are returned rather than printed, for the caller's checks to raise.
|
|
1061
|
+
* The cross-object fills a lone def can take are applied as export applies
|
|
1062
|
+
* them: an empty canonical from the seeded lock, hosted-file icons from
|
|
1063
|
+
* `opts.hostedFiles`, a group's documentation token from
|
|
1064
|
+
* `opts.documentationTokens`. Knowledge bodies are export's.
|
|
1065
|
+
*/
|
|
1066
|
+
encodeEntry(kindName, def, opts = {}) {
|
|
1067
|
+
const kind = getKind(kindName);
|
|
1068
|
+
if (kind.name === "workspace") {
|
|
1069
|
+
this.registerWorkspace(def);
|
|
1070
|
+
return { payloadKey: kind.payloadKey, entry: this.export().payload.workspace, warnings: [] };
|
|
1071
|
+
}
|
|
1072
|
+
this.register(kind.name, def);
|
|
1073
|
+
const encoded = kind.name === "table" ? this.encodeTableEntry(def) : this.sections.get(kind.payloadKey).at(-1);
|
|
1074
|
+
const warnings = this.encodeWarnings.get(encoded) ?? [];
|
|
1075
|
+
let entry = encoded;
|
|
1076
|
+
if (CANONICAL_PAYLOAD_KEYS.has(kind.payloadKey) && entry.canonical === "" && typeof entry.name === "string") {
|
|
1077
|
+
const locked = getLockedCanonical(lockKey(kind.payloadKey, entry.name));
|
|
1078
|
+
if (locked !== void 0) entry = { ...entry, canonical: locked };
|
|
1079
|
+
}
|
|
1080
|
+
if (opts.hostedFiles !== void 0 && HOSTED_ICON_KINDS.includes(kind.payloadKey)) {
|
|
1081
|
+
const sections = { [kind.payloadKey]: [entry] };
|
|
1082
|
+
this.fillHostedFiles(sections, opts.hostedFiles);
|
|
1083
|
+
entry = sections[kind.payloadKey][0];
|
|
1084
|
+
}
|
|
1085
|
+
if (kind.name === "api_group") {
|
|
1086
|
+
entry = resolveDocumentationTokens({
|
|
1087
|
+
workspace: {},
|
|
1088
|
+
apiGroups: [entry],
|
|
1089
|
+
values: opts.documentationTokens ?? {}
|
|
1090
|
+
}).apiGroups[0];
|
|
1091
|
+
}
|
|
1092
|
+
return { payloadKey: kind.payloadKey, entry, warnings };
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Every def object handed to {@link register}, by identity, so the SAME one
|
|
1096
|
+
* registered twice is caught at the call that did it.
|
|
1097
|
+
*
|
|
1098
|
+
* Identity, not structure. Two separately constructed defs sharing a name are
|
|
1099
|
+
* a genuine collision, and the guid check at export already diagnoses that
|
|
1100
|
+
* correctly — it explains that identity derives from `(type, name)` and names
|
|
1101
|
+
* both objects. This guard must not shadow it.
|
|
1102
|
+
*/
|
|
1103
|
+
registered = /* @__PURE__ */ new Set();
|
|
1104
|
+
/** The warnings each encoded object's encoder raised, raised again about it by every export. */
|
|
1105
|
+
encodeWarnings = /* @__PURE__ */ new WeakMap();
|
|
1106
|
+
/** Each encoded object's accepted warning codes, from its def's `diagnostics.allow`. */
|
|
1107
|
+
accepted = /* @__PURE__ */ new WeakMap();
|
|
1108
|
+
/** The workspace config's accepted warning codes — they hold for every object. */
|
|
1109
|
+
workspaceAccepted;
|
|
1110
|
+
/**
|
|
1111
|
+
* Refuse the same def object twice.
|
|
1112
|
+
*
|
|
1113
|
+
* The failure this front-runs: a helper registers a def on your behalf and
|
|
1114
|
+
* you then register it again yourself — `registerAuth(ws).registerTables([userTable])`
|
|
1115
|
+
* is the reported shape. Nothing objected at the second call; it surfaced much
|
|
1116
|
+
* later at `emitBundle` as `Duplicate object guid (efb55…) shared by "dbo/user"
|
|
1117
|
+
* and "dbo/user"`, a message that names a guid and the same label twice and
|
|
1118
|
+
* points at neither line.
|
|
1119
|
+
*/
|
|
1120
|
+
assertNotAlreadyRegistered(kindName, def) {
|
|
1121
|
+
if (def === null || typeof def !== "object") return;
|
|
1122
|
+
if (!this.registered.has(def)) {
|
|
1123
|
+
this.registered.add(def);
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
const name = def.name;
|
|
1127
|
+
const label = typeof name === "string" ? `"${name}"` : "it";
|
|
1128
|
+
throw new Error(
|
|
1129
|
+
`register: the same ${kindName} def ${label} was registered twice on this workspace. The second registration is always a mistake \u2014 the bundle would carry the object twice under one guid, which fails the import. A helper that registers on your behalf is the usual cause (\`registerAuth(ws)\` registers its own tables, so a following \`registerTables([userTable])\` is the duplicate): drop the manual call. Two DIFFERENT defs that share a name are a separate problem, and export reports that one by guid.`
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Register the workspace settings object (singleton).
|
|
1134
|
+
*
|
|
1135
|
+
* A config with no `name` inherits the one already on the registry — which is
|
|
1136
|
+
* what `workspace("my-app")` set — so the natural chain
|
|
1137
|
+
* `workspace("my-app").registerWorkspace(workspaceConfig({ history }))` no
|
|
1138
|
+
* longer makes an author restate a name this registry has held since its
|
|
1139
|
+
* first call. An explicit `name` still wins, and a rename this way is
|
|
1140
|
+
* a rename of the workspace.
|
|
1141
|
+
*/
|
|
1142
|
+
registerWorkspace(def) {
|
|
1143
|
+
if (def !== void 0 && def !== null) {
|
|
1144
|
+
assertDefEntry("registerWorkspace", "workspace", def, void 0);
|
|
1145
|
+
assertDefKind("registerWorkspace", "workspace", def, void 0);
|
|
1146
|
+
assertNoProtoKeys("registerWorkspace", def);
|
|
1147
|
+
assertDefKeys("workspaceConfig", def, DEF_KEYS.workspace, "workspace");
|
|
1148
|
+
assertDefShape("workspace", def);
|
|
1149
|
+
}
|
|
1150
|
+
const authored = def ?? {};
|
|
1151
|
+
if (typeof authored.name === "string" && authored.name.trim() === "") {
|
|
1152
|
+
throw new Error(
|
|
1153
|
+
`registerWorkspace: the workspace name must not be blank \u2014 got ${describeEntry(authored.name)}. Name it, or leave \`name\` out to keep the one \`workspace("\u2026")\` gave.`
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
const inherited = this.workspaceConfig.name;
|
|
1157
|
+
const withName = authored.name === void 0 && typeof inherited === "string" ? { ...authored, name: inherited } : authored;
|
|
1158
|
+
this.workspaceConfig = encodeObject("workspace", withName);
|
|
1159
|
+
this.workspaceAccepted = acceptedWarnings("workspace", authored);
|
|
1160
|
+
this.envAuthored = authored.env !== void 0;
|
|
1161
|
+
this.configAttached = SHORTHAND_CONFIGS.has(def) ? this.configAttached : true;
|
|
1162
|
+
return this;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Fill each encoded knowledge item's `content` and emit its reference files as
|
|
1166
|
+
* the sibling `knowledge_file` section.
|
|
1167
|
+
*
|
|
1168
|
+
* Reference rows name their parent by GUID rather than by row id: the id does
|
|
1169
|
+
* not exist until the import inserts the parent, and the importer resolves the
|
|
1170
|
+
* guid back to it. `references` stays empty — the platform derives that list
|
|
1171
|
+
* from the stored files itself, so anything written here is overwritten.
|
|
1172
|
+
*/
|
|
1173
|
+
fillKnowledge(sections, resolved) {
|
|
1174
|
+
const byName = new Map(resolved.map((r) => [r.name, r]));
|
|
1175
|
+
const files = [];
|
|
1176
|
+
const warnings = [];
|
|
1177
|
+
const items = (sections.knowledge ?? []).map((source) => {
|
|
1178
|
+
if (!byName.has(source.name)) return source;
|
|
1179
|
+
const copy = { ...source };
|
|
1180
|
+
const allow = this.accepted.get(source);
|
|
1181
|
+
if (allow) this.accepted.set(copy, allow);
|
|
1182
|
+
const raised = this.encodeWarnings.get(source);
|
|
1183
|
+
if (raised) this.encodeWarnings.set(copy, raised);
|
|
1184
|
+
return copy;
|
|
1185
|
+
});
|
|
1186
|
+
if (sections.knowledge !== void 0) sections.knowledge = items;
|
|
1187
|
+
for (const item of items) {
|
|
1188
|
+
const match = byName.get(item.name);
|
|
1189
|
+
if (match === void 0) continue;
|
|
1190
|
+
item.content = match.content;
|
|
1191
|
+
for (const w of match.warnings ?? []) warnings.push({ item, ...w });
|
|
1192
|
+
for (const file of match.files) {
|
|
1193
|
+
files.push({
|
|
1194
|
+
knowledge: { id: item.guid },
|
|
1195
|
+
path: file.path,
|
|
1196
|
+
content: file.content,
|
|
1197
|
+
size: file.size,
|
|
1198
|
+
guid: deriveGuid("knowledge_file", `${item.name}/${file.path}`)
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (files.length > 0) sections.knowledge_file = files;
|
|
1203
|
+
return warnings;
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Replace every pending hosted-file placeholder with the resolver's value and
|
|
1207
|
+
* list the files it read as the `vault` section.
|
|
1208
|
+
*
|
|
1209
|
+
* The encoded MCP objects are the registry's own, shared by every export, so
|
|
1210
|
+
* each is copied rather than edited: an unresolved `export()` after a
|
|
1211
|
+
* resolved one still sees the inert placeholder.
|
|
1212
|
+
*/
|
|
1213
|
+
fillHostedFiles(sections, resolver) {
|
|
1214
|
+
for (const key of HOSTED_ICON_KINDS) {
|
|
1215
|
+
const items = sections[key];
|
|
1216
|
+
if (items === void 0) continue;
|
|
1217
|
+
sections[key] = items.map((item) => {
|
|
1218
|
+
const icons = item.icons;
|
|
1219
|
+
if (!Array.isArray(icons) || !icons.some((icon) => pendingHostedFile(icon) !== void 0)) return item;
|
|
1220
|
+
const copy = {
|
|
1221
|
+
...item,
|
|
1222
|
+
icons: icons.map((icon) => {
|
|
1223
|
+
const hosted = pendingHostedFile(icon);
|
|
1224
|
+
return hosted === void 0 ? icon : { ...icon, src: resolver.resolve(hosted.ref, hosted.rules) };
|
|
1225
|
+
})
|
|
1226
|
+
};
|
|
1227
|
+
const allow = this.accepted.get(item);
|
|
1228
|
+
if (allow) this.accepted.set(copy, allow);
|
|
1229
|
+
return copy;
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
const rows = resolver.libraryRows();
|
|
1233
|
+
if (rows.length > 0) sections.vault = rows;
|
|
1234
|
+
}
|
|
1235
|
+
/** Set the bundle `type` (defaults to "workspace"). */
|
|
1236
|
+
setBundleType(type) {
|
|
1237
|
+
this.bundleType = type;
|
|
1238
|
+
return this;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* The registered table defs (with their authored `seed`), for the Node deploy
|
|
1242
|
+
* path to build `content/` seed entries. Deliberately NOT reached from
|
|
1243
|
+
* `export()` — seed values are resolved only in the deploy pipeline, so they
|
|
1244
|
+
* never enter the browser-safe bundle. See {@link import("./seed.js")}.
|
|
1245
|
+
*/
|
|
1246
|
+
tables() {
|
|
1247
|
+
return [...this.tableDefs];
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* The ENCODED workspace config, read-only.
|
|
1251
|
+
*
|
|
1252
|
+
* For the deploy path, which needs to know which env variable names the
|
|
1253
|
+
* workspace declares before the bundle exists — so an `--env-var` naming
|
|
1254
|
+
* something undeclared can be reported as an addition rather than silently
|
|
1255
|
+
* appended, and so a declared name with no value anywhere can refuse a deploy
|
|
1256
|
+
* rather than clear the live one. A shallow copy: nothing outside
|
|
1257
|
+
* `registerWorkspace` may write it.
|
|
1258
|
+
*/
|
|
1259
|
+
workspaceSettings() {
|
|
1260
|
+
return { ...this.workspaceConfig };
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Every scope in this workspace that declares a documentation gate, with the
|
|
1264
|
+
* sidecar key its token is stored under.
|
|
1265
|
+
*
|
|
1266
|
+
* For the deploy path, which has to know them before the bundle exists — so a
|
|
1267
|
+
* gate with no token anywhere can refuse a deploy rather than ship a workspace
|
|
1268
|
+
* whose doc site silently does not match its source. Read off the ENCODED
|
|
1269
|
+
* shapes, so a decoded workspace is covered exactly like a hand-authored one,
|
|
1270
|
+
* and so the guid each key is built from is the one already stamped at
|
|
1271
|
+
* registration.
|
|
1272
|
+
*/
|
|
1273
|
+
documentationTokenNames() {
|
|
1274
|
+
return documentationTokenDeclarations({
|
|
1275
|
+
workspace: this.workspaceConfig,
|
|
1276
|
+
apiGroups: this.sections.get("app")
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* The registered knowledge defs (with their authored `body`/`refs` path
|
|
1281
|
+
* markers), for the Node compile path to read markdown off disk. Deliberately
|
|
1282
|
+
* NOT reached from `export()` — a file read has no place in the browser-safe
|
|
1283
|
+
* bundle, so the encoder emits an empty `content` and the compile path fills
|
|
1284
|
+
* it. See {@link import("./knowledge.js")}.
|
|
1285
|
+
*/
|
|
1286
|
+
knowledge() {
|
|
1287
|
+
return [...this.knowledgeDefs];
|
|
1288
|
+
}
|
|
1289
|
+
registerFunctions(defs) {
|
|
1290
|
+
return this.register("function", defs, "registerFunctions");
|
|
1291
|
+
}
|
|
1292
|
+
/** Register knowledge items — the markdown a workspace's AI agents read. */
|
|
1293
|
+
registerKnowledge(defs) {
|
|
1294
|
+
return this.register("knowledge", defs, "registerKnowledge");
|
|
1295
|
+
}
|
|
1296
|
+
registerTriggers(defs) {
|
|
1297
|
+
return this.register("trigger", defs, "registerTriggers");
|
|
1298
|
+
}
|
|
1299
|
+
registerTools(defs) {
|
|
1300
|
+
return this.register("tool", defs, "registerTools");
|
|
1301
|
+
}
|
|
1302
|
+
/** Register MCP prompts. Expose one by listing it in an `mcpServer({ prompts })`. */
|
|
1303
|
+
registerPrompts(defs) {
|
|
1304
|
+
return this.register("prompt", defs, "registerPrompts");
|
|
1305
|
+
}
|
|
1306
|
+
/** Register MCP resources. Expose one by listing it in an `mcpServer({ resources })`. */
|
|
1307
|
+
registerResources(defs) {
|
|
1308
|
+
return this.register("resource", defs, "registerResources");
|
|
1309
|
+
}
|
|
1310
|
+
registerMcpServers(defs) {
|
|
1311
|
+
return this.register("mcp_server", defs, "registerMcpServers");
|
|
1312
|
+
}
|
|
1313
|
+
registerAgents(defs) {
|
|
1314
|
+
return this.register("agent", defs, "registerAgents");
|
|
1315
|
+
}
|
|
1316
|
+
registerTables(defs) {
|
|
1317
|
+
return this.register("table", defs, "registerTables");
|
|
1318
|
+
}
|
|
1319
|
+
registerQueries(defs) {
|
|
1320
|
+
return this.register("query", defs, "registerQueries");
|
|
1321
|
+
}
|
|
1322
|
+
registerApiGroups(defs) {
|
|
1323
|
+
return this.register("api_group", defs, "registerApiGroups");
|
|
1324
|
+
}
|
|
1325
|
+
registerTasks(defs) {
|
|
1326
|
+
return this.register("task", defs, "registerTasks");
|
|
1327
|
+
}
|
|
1328
|
+
registerMiddleware(defs) {
|
|
1329
|
+
return this.register("middleware", defs, "registerMiddleware");
|
|
1330
|
+
}
|
|
1331
|
+
registerAddons(defs) {
|
|
1332
|
+
return this.register("addon", defs, "registerAddons");
|
|
1333
|
+
}
|
|
1334
|
+
registerMicroservices(defs) {
|
|
1335
|
+
return this.register("microservice", defs, "registerMicroservices");
|
|
1336
|
+
}
|
|
1337
|
+
registerRealtimeServers(defs) {
|
|
1338
|
+
return this.register("realtime_server", defs, "registerRealtimeServers");
|
|
1339
|
+
}
|
|
1340
|
+
registerRealtimeChannels(defs) {
|
|
1341
|
+
return this.register("channel", defs, "registerRealtimeChannels");
|
|
1342
|
+
}
|
|
1343
|
+
registerRealtimeMessages(defs) {
|
|
1344
|
+
return this.register("message", defs, "registerRealtimeMessages");
|
|
1345
|
+
}
|
|
1346
|
+
registerWorkflowTests(defs) {
|
|
1347
|
+
return this.register("workflow_test", defs, "registerWorkflowTests");
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Assemble the signed aggregate `packageExport` bundle.
|
|
1351
|
+
*
|
|
1352
|
+
* With `options.lock` (a {@link LockExportContext}), the export participates
|
|
1353
|
+
* in identity locking: empty api-group/toolset canonicals are filled from the
|
|
1354
|
+
* lock — minted fresh on first sight — and every identity the bundle emits is
|
|
1355
|
+
* reported back through `ctx.observed` (MUTATING the passed context), which
|
|
1356
|
+
* the caller merges into the lock file. All lock work happens before the
|
|
1357
|
+
* bundle is signed. Without options the output is byte-identical to before.
|
|
1358
|
+
*
|
|
1359
|
+
* With `options.strict`, every build WARNING fails the export instead of
|
|
1360
|
+
* printing — the shapes that deploy clean and then lose data or return the
|
|
1361
|
+
* wrong rows. Nothing about the emitted bundle changes; it either exports or
|
|
1362
|
+
* it does not.
|
|
1363
|
+
*/
|
|
1364
|
+
export(options = {}) {
|
|
1365
|
+
try {
|
|
1366
|
+
return this.assemble(options);
|
|
1367
|
+
} catch (err) {
|
|
1368
|
+
throw asAuthoringRefusal(err);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
/** {@link export}'s work, its refusals not yet classed. */
|
|
1372
|
+
assemble(options) {
|
|
1373
|
+
const lockCtx = options.lock;
|
|
1374
|
+
const sections = {};
|
|
1375
|
+
for (const [key, arr] of this.sections) {
|
|
1376
|
+
sections[key] = arr;
|
|
1377
|
+
}
|
|
1378
|
+
if (this.tableDefs.length > 0) {
|
|
1379
|
+
sections[getKind("table").payloadKey] = this.tableDefs.map((def) => this.encodeTableEntry(def));
|
|
1380
|
+
}
|
|
1381
|
+
const knowledgeWarnings = options.knowledge !== void 0 && options.knowledge.length > 0 ? this.fillKnowledge(sections, options.knowledge) : [];
|
|
1382
|
+
if (options.hostedFiles !== void 0) {
|
|
1383
|
+
this.fillHostedFiles(sections, options.hostedFiles);
|
|
1384
|
+
}
|
|
1385
|
+
const resolveDocs = () => resolveDocumentationTokens({
|
|
1386
|
+
workspace: this.workspaceConfig,
|
|
1387
|
+
apiGroups: sections["app"],
|
|
1388
|
+
values: options.documentationTokens ?? {},
|
|
1389
|
+
...options.allowEmptyDocToken ? { allowEmpty: options.allowEmptyDocToken } : {}
|
|
1390
|
+
});
|
|
1391
|
+
const docsView = resolveDocs();
|
|
1392
|
+
docsView.apiGroups?.forEach((copy, i) => {
|
|
1393
|
+
const allow = this.accepted.get(sections["app"]?.[i]);
|
|
1394
|
+
if (allow && copy !== null && typeof copy === "object") this.accepted.set(copy, allow);
|
|
1395
|
+
});
|
|
1396
|
+
const bag = new DiagnosticBag(options.strict === true);
|
|
1397
|
+
const used = /* @__PURE__ */ new Map();
|
|
1398
|
+
const use = (allow, code) => {
|
|
1399
|
+
if (allow?.has(code) !== true) return false;
|
|
1400
|
+
used.set(allow, (used.get(allow) ?? /* @__PURE__ */ new Set()).add(code));
|
|
1401
|
+
return true;
|
|
1402
|
+
};
|
|
1403
|
+
bag.accepts = (code, subject) => {
|
|
1404
|
+
const own = use(subject === void 0 ? void 0 : this.accepted.get(subject), code);
|
|
1405
|
+
return use(this.workspaceAccepted, code) || own;
|
|
1406
|
+
};
|
|
1407
|
+
let subjectMap;
|
|
1408
|
+
const subjects = () => subjectMap ??= this.diagnosticSubjects(sections, docsView.apiGroups);
|
|
1409
|
+
bag.describe = (subject) => subjects().get(subject);
|
|
1410
|
+
for (const arr of Object.values(sections)) {
|
|
1411
|
+
for (const obj2 of arr ?? []) {
|
|
1412
|
+
if (obj2 === null || typeof obj2 !== "object") continue;
|
|
1413
|
+
for (const d of this.encodeWarnings.get(obj2) ?? []) bag.warn(d.code, d.message, obj2);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
for (const { item, code, message } of knowledgeWarnings) bag.warn(code, message, item);
|
|
1417
|
+
checkDocumentationTokens(this.workspaceConfig, sections, bag, options.secretsRemedy);
|
|
1418
|
+
const documentedSections = docsView.apiGroups === void 0 ? sections : { ...sections, app: docsView.apiGroups };
|
|
1419
|
+
checkApiGroupDocsExposure(documentedSections, bag, new Set(docsView.unresolvedGroups), options.secretsRemedy);
|
|
1420
|
+
this.validateQueryAuth(sections, bag);
|
|
1421
|
+
this.validateMiddlewareAuth(sections, bag);
|
|
1422
|
+
this.validatePostMiddlewareEnvelope(sections, bag);
|
|
1423
|
+
checkMicroserviceSecrets(sections, bag);
|
|
1424
|
+
checkMicroserviceBlocks(sections, bag);
|
|
1425
|
+
checkNonJsonValues(sections, bag, this.workspaceConfig);
|
|
1426
|
+
checkLiveDatasourceTests(sections, bag);
|
|
1427
|
+
checkStacks(
|
|
1428
|
+
this.tableDefs,
|
|
1429
|
+
sections,
|
|
1430
|
+
bag,
|
|
1431
|
+
this.workspaceConfig,
|
|
1432
|
+
this.envAuthored ? "authored" : this.configAttached ? "no-env" : "unattached"
|
|
1433
|
+
);
|
|
1434
|
+
checkExpressionOperands(sections, bag);
|
|
1435
|
+
checkInterpolatedValues(sections, bag);
|
|
1436
|
+
checkZeroBasedObjects(sections, bag);
|
|
1437
|
+
checkIntRange(sections, bag);
|
|
1438
|
+
checkRegexOperandOrder(sections, bag);
|
|
1439
|
+
checkTimezoneArgs(sections, bag);
|
|
1440
|
+
checkReferences(this.bundleType, sections, this.workspaceConfig.guid, bag);
|
|
1441
|
+
checkMicroserviceReferences(this.bundleType, sections, bag);
|
|
1442
|
+
checkDecodeOnlyStatements(sections, bag);
|
|
1443
|
+
checkRunFamilyHosts(sections, bag);
|
|
1444
|
+
checkLoopControl(sections, bag);
|
|
1445
|
+
checkSwitchFallthrough(sections, bag);
|
|
1446
|
+
checkSearchOperandFilters(sections, bag);
|
|
1447
|
+
checkFilterOperands(sections, bag);
|
|
1448
|
+
checkToolAuthIdentity(sections, bag);
|
|
1449
|
+
checkPrimitiveAuthIdentity(sections, bag);
|
|
1450
|
+
checkWriteBeforeElicit(sections, bag);
|
|
1451
|
+
checkMcpStackHazards(sections, bag);
|
|
1452
|
+
checkMcpResourceUris(sections, bag);
|
|
1453
|
+
checkMcpOauth(sections, bag);
|
|
1454
|
+
checkMcpOauthRevoke(sections, bag);
|
|
1455
|
+
checkStackAuthRatelimit(sections, bag);
|
|
1456
|
+
checkRatelimitWithoutError(sections, bag);
|
|
1457
|
+
checkAuthNoCaller(sections, bag);
|
|
1458
|
+
checkRealtimeGates(sections, bag);
|
|
1459
|
+
checkRealtimeSilentShapes(sections, bag);
|
|
1460
|
+
checkNonNullVectorColumns(sections, bag);
|
|
1461
|
+
checkRealtimeDeliver(this.bundleType, sections, bag);
|
|
1462
|
+
checkRealtimeJoinDeliver(sections, bag);
|
|
1463
|
+
checkTriggerBindings(sections, bag);
|
|
1464
|
+
checkUnboundVarRefs(sections, bag);
|
|
1465
|
+
checkToThrowScope(sections, bag);
|
|
1466
|
+
checkPasswordInputHashing(sections, bag);
|
|
1467
|
+
checkZipPasswordKeys(sections, bag);
|
|
1468
|
+
checkSafeRefMatchArgs(sections, bag);
|
|
1469
|
+
checkNullMatchArgs(sections, bag);
|
|
1470
|
+
checkPathSegmentCandidates(sections, bag);
|
|
1471
|
+
checkRouteShadowing(sections, bag);
|
|
1472
|
+
checkSameNameSiblings(sections, bag);
|
|
1473
|
+
checkSeed(this.tableDefs, bag, this.workspaceConfig.use_xdo === true);
|
|
1474
|
+
checkReservedColumnNames(this.tableDefs, bag);
|
|
1475
|
+
checkUnusableColumnNames(this.tableDefs, bag);
|
|
1476
|
+
checkCacheTtl(sections, bag);
|
|
1477
|
+
checkReservedInputNames(sections, bag);
|
|
1478
|
+
checkOmittedCallInputs(sections, bag);
|
|
1479
|
+
checkUnknownCallInputs(sections, bag);
|
|
1480
|
+
checkAddonAttachments(sections, bag);
|
|
1481
|
+
checkUnitTests(sections, bag);
|
|
1482
|
+
checkAddonOutput(sections, bag);
|
|
1483
|
+
checkGuardRoleColumns(sections, this.tableDefs, bag);
|
|
1484
|
+
checkToolOutputSchema(sections, bag);
|
|
1485
|
+
checkAgentArgPlaceholders(sections, bag);
|
|
1486
|
+
checkAssertionsOutsideTests(sections, bag);
|
|
1487
|
+
checkAgentApiKeys(sections, bag);
|
|
1488
|
+
checkKnowledge(sections, bag, { resolved: options.knowledge !== void 0 });
|
|
1489
|
+
checkRealtimePublish(this.bundleType, sections, bag);
|
|
1490
|
+
this.checkAllowsUsed(bag, subjects, used);
|
|
1491
|
+
options.accepted?.push(...bag.accepted());
|
|
1492
|
+
bag.flush();
|
|
1493
|
+
if (lockCtx) this.applyLock(lockCtx, sections);
|
|
1494
|
+
if (this.workspaceConfig.guid === void 0) {
|
|
1495
|
+
const wsName = typeof this.workspaceConfig.name === "string" ? this.workspaceConfig.name : "workspace";
|
|
1496
|
+
this.workspaceConfig.guid = deriveGuid("workspace", wsName);
|
|
1497
|
+
}
|
|
1498
|
+
const shipped = resolveDocs();
|
|
1499
|
+
if (shipped.apiGroups !== void 0) sections["app"] = shipped.apiGroups;
|
|
1500
|
+
return buildBundle({
|
|
1501
|
+
type: this.bundleType,
|
|
1502
|
+
workspace: shipped.workspace,
|
|
1503
|
+
sections,
|
|
1504
|
+
lock: lockCtx?.lock,
|
|
1505
|
+
...options.envOverrides ? { envOverrides: options.envOverrides } : {},
|
|
1506
|
+
...options.knowledge !== void 0 ? { knowledgeRead: true } : {}
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* The def each object this export checks was registered from — the encoded
|
|
1511
|
+
* objects, the table defs whose columns are read directly, and the copies the
|
|
1512
|
+
* docs view took — for a finding to name its subject.
|
|
1513
|
+
*/
|
|
1514
|
+
diagnosticSubjects(sections, docsCopies) {
|
|
1515
|
+
const subjects = /* @__PURE__ */ new Map();
|
|
1516
|
+
const add = (obj2, kind) => {
|
|
1517
|
+
if (obj2 === null || typeof obj2 !== "object") return;
|
|
1518
|
+
const name = obj2.name;
|
|
1519
|
+
if (typeof name === "string") subjects.set(obj2, { kind, name });
|
|
1520
|
+
};
|
|
1521
|
+
add(this.workspaceConfig, "workspace");
|
|
1522
|
+
for (const [key, items] of Object.entries(sections)) {
|
|
1523
|
+
for (const obj2 of items ?? []) add(obj2, sdkKindName(key, obj2));
|
|
1524
|
+
}
|
|
1525
|
+
for (const def of this.tableDefs) add(def, "table");
|
|
1526
|
+
docsCopies?.forEach((copy, i) => {
|
|
1527
|
+
const source = subjects.get(sections["app"]?.[i]);
|
|
1528
|
+
if (source && copy !== null && typeof copy === "object") subjects.set(copy, source);
|
|
1529
|
+
});
|
|
1530
|
+
return subjects;
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Warn on each `diagnostics.allow` code that accepted nothing in this export.
|
|
1534
|
+
* The shape it named is gone, or the check stopped finding it — either way
|
|
1535
|
+
* the source still reads as "this def has a known hazard". Raised directly
|
|
1536
|
+
* rather than through `warn`: it is about the allow itself, so no allow can
|
|
1537
|
+
* accept it. A code whose check did not run here had nothing to accept.
|
|
1538
|
+
*/
|
|
1539
|
+
checkAllowsUsed(bag, subjects, used) {
|
|
1540
|
+
const owners = /* @__PURE__ */ new Map();
|
|
1541
|
+
for (const [obj2, subject] of subjects()) {
|
|
1542
|
+
const allow = obj2 === this.workspaceConfig ? this.workspaceAccepted : this.accepted.get(obj2);
|
|
1543
|
+
if (allow && !owners.has(allow)) owners.set(allow, subject);
|
|
1544
|
+
}
|
|
1545
|
+
for (const [allow, subject] of owners) {
|
|
1546
|
+
const unused = [...allow].filter((code) => used.get(allow)?.has(code) !== true && !bag.wasSkipped(code));
|
|
1547
|
+
if (unused.length === 0) continue;
|
|
1548
|
+
const one = unused.length === 1;
|
|
1549
|
+
bag.add({
|
|
1550
|
+
severity: "warning",
|
|
1551
|
+
code: "diagnostics.allow-unused",
|
|
1552
|
+
message: `${subject.kind} "${subject.name}": \`diagnostics.allow\` accepts ${unused.map((c) => `"${c}"`).join(", ")}, but this export raised no such warning about ${allow === this.workspaceAccepted ? "any def" : "it"} \u2014 the shape ${one ? "it" : "they"} accepted is gone, or the check no longer finds it. Remove ${one ? "it" : "them"} from \`allow\`.`,
|
|
1553
|
+
subject
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Cross-check every query's resolved `auth` against the registered auth tables.
|
|
1559
|
+
*
|
|
1560
|
+
* `resolveAuth` (in `encodeQuery`) turns a table ref into a guid with no
|
|
1561
|
+
* registry visibility, so a bare-name typo produces a valid-looking guid that
|
|
1562
|
+
* only fails at deploy with an opaque engine error. Here we have the registry,
|
|
1563
|
+
* so we catch it at export and name the offending query.
|
|
1564
|
+
*
|
|
1565
|
+
* A registered table that is not `table({ auth: true })` only WARNS: the
|
|
1566
|
+
* engine reads that flag nowhere at request time (it compares the token's
|
|
1567
|
+
* `dbo` to the endpoint's by name, and mints tokens for any table by name), so
|
|
1568
|
+
* refusing the combination blocked a real workspace's round trip. A numeric
|
|
1569
|
+
* `auth` (raw `dbo.id` escape hatch) references a table by id xanosdk never
|
|
1570
|
+
* sees, so it's left as-is; `false` is a public endpoint. A table that pins an
|
|
1571
|
+
* explicit `guid` referenced by bare name lands in the "not registered" branch
|
|
1572
|
+
* (its name-derived guid diverges from the pinned one) — pass the def instead.
|
|
1573
|
+
*/
|
|
1574
|
+
validateQueryAuth(sections, bag) {
|
|
1575
|
+
const queries = sections["query"];
|
|
1576
|
+
if (!Array.isArray(queries) || queries.length === 0) return;
|
|
1577
|
+
const tableNameByGuid = /* @__PURE__ */ new Map();
|
|
1578
|
+
const authGuids = /* @__PURE__ */ new Set();
|
|
1579
|
+
for (const def of this.tableDefs) {
|
|
1580
|
+
const guid = resolveRef("dbo", def);
|
|
1581
|
+
tableNameByGuid.set(guid, def.name);
|
|
1582
|
+
if (def.auth === true) authGuids.add(guid);
|
|
1583
|
+
}
|
|
1584
|
+
for (const q of queries) {
|
|
1585
|
+
if (!q || typeof q !== "object") continue;
|
|
1586
|
+
const auth = q.auth;
|
|
1587
|
+
if (typeof auth !== "string" || authGuids.has(auth)) continue;
|
|
1588
|
+
const name = String(q.name ?? "?");
|
|
1589
|
+
const known = tableNameByGuid.get(auth);
|
|
1590
|
+
if (known) {
|
|
1591
|
+
bag.warn(
|
|
1592
|
+
"query.auth-table-unflagged",
|
|
1593
|
+
`query "${name}" requires auth against table "${known}", which is not marked \`table({ auth: true })\`. The engine allows it \u2014 a token minted for that table is accepted \u2014 but the editor will not offer the table as an auth source. Mark it if that is what you meant.`,
|
|
1594
|
+
q
|
|
1595
|
+
);
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
if (this.bundleType === "workspace") continue;
|
|
1599
|
+
bag.error(
|
|
1600
|
+
"query.auth-table-unregistered",
|
|
1601
|
+
refSpelling(auth) === "handle" ? `query "${name}": \`auth\` is a table def that isn't registered on this workspace \u2014 add it with \`registerTables([\u2026])\`.` : `query "${name}": \`auth\` references a table that isn't registered on this workspace. Check the name for a typo, or register the auth table with \`registerTables([\u2026])\`; if it pins an explicit \`guid\`, pass the table def rather than its name.`
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Warn about an `auth()`-keyed middleware **directly attached** to a host where
|
|
1607
|
+
* there is no caller identity for `auth()` to resolve.
|
|
1608
|
+
*
|
|
1609
|
+
* The footgun: a rate limiter keyed by `auth("id")` is the canonical middleware,
|
|
1610
|
+
* but attach it to a host with no authenticated caller and `auth()` cannot
|
|
1611
|
+
* resolve — every request 403s before the host runs, with no signal at author
|
|
1612
|
+
* or export time. This surfaces it at export, where the
|
|
1613
|
+
* middleware registry is known (an attachment entry only carries the target's
|
|
1614
|
+
* guid; we resolve it back to the encoded `run` to inspect for `auth()`).
|
|
1615
|
+
*
|
|
1616
|
+
* It **warns, never throws** — a bare `auth()` reference is not proof the
|
|
1617
|
+
* request will fail: the statement holding it may sit on a branch that never
|
|
1618
|
+
* runs on this host, and the same middleware object is often attached to
|
|
1619
|
+
* authenticated hosts too. Blocking the export would produce false positives
|
|
1620
|
+
* on legitimate use. The warning names the host and reason so
|
|
1621
|
+
* the author can confirm intent, key off `sys.remoteIp()`, or move to an
|
|
1622
|
+
* authenticated host.
|
|
1623
|
+
*
|
|
1624
|
+
* Scope: the host's EFFECTIVE chain, across all three tiers. Seeing only a
|
|
1625
|
+
* host's own `middleware.pre`/`post` would let an author who DRY'd a per-user
|
|
1626
|
+
* limiter up to `apiGroup({ middleware })` or to the workspace tier silently
|
|
1627
|
+
* reintroduce the failure — the same unresolvable key, attached one level up,
|
|
1628
|
+
* with the warning switched off. The limiter is most naturally written once at
|
|
1629
|
+
* the group, so that is the tier that most needs the warning.
|
|
1630
|
+
*
|
|
1631
|
+
* The cascade is the engine's own: a phase whose `_customize` flag is set is
|
|
1632
|
+
* answered by that tier (including `clear()`, which customizes with an empty
|
|
1633
|
+
* list and therefore stops inheritance); an un-customized phase falls through
|
|
1634
|
+
* to the API group, then to the workspace's `{objType}_{phase}` map. A query
|
|
1635
|
+
* that clears a phase is NOT warned about a group middleware it does not run.
|
|
1636
|
+
*
|
|
1637
|
+
* An authenticated `query` (its own `auth` table set) resolves an identity, so
|
|
1638
|
+
* it is skipped at every tier.
|
|
1639
|
+
*/
|
|
1640
|
+
validateMiddlewareAuth(sections, bag) {
|
|
1641
|
+
const authCache = /* @__PURE__ */ new Map();
|
|
1642
|
+
const referencesAuth = (guid, mw) => {
|
|
1643
|
+
let hit = authCache.get(guid);
|
|
1644
|
+
if (hit === void 0) {
|
|
1645
|
+
const run = mw?.run;
|
|
1646
|
+
hit = stackReferencesAuth(Array.isArray(run) ? run : void 0);
|
|
1647
|
+
authCache.set(guid, hit);
|
|
1648
|
+
}
|
|
1649
|
+
return hit;
|
|
1650
|
+
};
|
|
1651
|
+
const reasons = {
|
|
1652
|
+
query: "this endpoint has no auth table",
|
|
1653
|
+
task: "a task is scheduled/background and never has a request identity",
|
|
1654
|
+
function: "there is no caller identity unless an authenticated caller invokes it",
|
|
1655
|
+
tool: "there is no caller identity unless an authenticated caller invokes it",
|
|
1656
|
+
prompt: "there is no caller identity unless an authenticated caller invokes it",
|
|
1657
|
+
resource: "there is no caller identity unless an authenticated caller invokes it"
|
|
1658
|
+
};
|
|
1659
|
+
for (const { label, host, hostName, tier, guid, middleware } of this.attachedMiddleware(sections)) {
|
|
1660
|
+
if (label === "query" && host.auth) continue;
|
|
1661
|
+
if (!referencesAuth(guid, middleware)) continue;
|
|
1662
|
+
const mwName = String(middleware?.name ?? guid);
|
|
1663
|
+
bag.warn(
|
|
1664
|
+
"middleware.auth-null-host",
|
|
1665
|
+
`middleware "${mwName}" references auth() and is attached to ${label} "${hostName}"${tier}, where ${reasons[label]}. auth() cannot resolve there and the request FAILS (403) rather than degrading to a shared key \u2014 attach it to an authenticated host, key off sys.remoteIp(), or remove auth().`,
|
|
1666
|
+
middleware
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Warn about a middleware in a host's effective **`post`** chain that reads a
|
|
1672
|
+
* request field out of `s.util.get_all_input` — see `postEnvelopeMisreads`.
|
|
1673
|
+
*
|
|
1674
|
+
* In `post` that envelope holds the host's outcome (`{ status, result }`), not
|
|
1675
|
+
* the request, so the read cannot resolve. Under the default `rethrow` policy
|
|
1676
|
+
* it fails the request with `Unable to locate var` after the host's stack has
|
|
1677
|
+
* run and its writes have landed. Checked on the effective chain for the same
|
|
1678
|
+
* reason as {@link validateMiddlewareAuth}: a post middleware is most often
|
|
1679
|
+
* attached once, at the group or the workspace tier.
|
|
1680
|
+
*
|
|
1681
|
+
* Warns rather than throws: the same middleware is valid in a `pre` chain, and
|
|
1682
|
+
* a read on a branch the post path never takes fails nothing.
|
|
1683
|
+
*/
|
|
1684
|
+
validatePostMiddlewareEnvelope(sections, bag) {
|
|
1685
|
+
for (const middleware of sections["middleware"] ?? []) {
|
|
1686
|
+
const unnested = unnestedEnvelopeReads(middleware);
|
|
1687
|
+
if (unnested.length === 0) continue;
|
|
1688
|
+
const hard = unnested.filter((r) => !r.safe);
|
|
1689
|
+
const soft = unnested.filter((r) => r.safe);
|
|
1690
|
+
const list = (rs) => rs.map((r) => `\`${r.path}\``).join(", ");
|
|
1691
|
+
const fixed = unnested.map((r) => `\`${r.path.replace(".", ".vars.")}\``).join(", ");
|
|
1692
|
+
const outcome = [
|
|
1693
|
+
hard.length > 0 ? `${list(hard)} fails with \`Unable to locate var\` (a 500)` : "",
|
|
1694
|
+
soft.length > 0 ? `${list(soft)} (a safe/\`get\` read) is always null, silently` : ""
|
|
1695
|
+
].filter(Boolean);
|
|
1696
|
+
bag.warn(
|
|
1697
|
+
"middleware.envelope-unnested",
|
|
1698
|
+
`middleware "${String(middleware.name ?? "?")}" reads ${list(unnested)} straight off \`s.util.get_all_input\`, which binds a \`{ type, vars }\` envelope \u2014 the request is under \`vars\`, so ${outcome.join("; ")}. In \`pre\` read ${fixed} (or unwrap once: \`asFilters: [fl.get(c.text("vars"))]\`); in \`post\`, \`vars\` is \`{ status, result }\` and the request body is \`s.util.get_raw_input\`.`,
|
|
1699
|
+
middleware
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1702
|
+
const misreadCache = /* @__PURE__ */ new Map();
|
|
1703
|
+
for (const { label, hostName, phase, tier, guid, middleware } of this.attachedMiddleware(sections)) {
|
|
1704
|
+
if (phase !== "post") continue;
|
|
1705
|
+
let misreads = misreadCache.get(guid);
|
|
1706
|
+
if (misreads === void 0) {
|
|
1707
|
+
misreads = postEnvelopeMisreads(middleware);
|
|
1708
|
+
misreadCache.set(guid, misreads);
|
|
1709
|
+
}
|
|
1710
|
+
if (misreads.length === 0) continue;
|
|
1711
|
+
const mwName = String(middleware?.name ?? guid);
|
|
1712
|
+
const results = [...new Set(misreads.map((p) => p.split(".")[0]))].map((b) => `\`${b}.vars.result.<field>\``).join(" / ");
|
|
1713
|
+
bag.warn(
|
|
1714
|
+
"middleware.post-reads-request",
|
|
1715
|
+
`middleware "${mwName}" is in the post chain of ${label} "${hostName}"${tier} and reads ${misreads.map((p) => `\`${p}\``).join(", ")} from \`s.util.get_all_input\`. In \`post\` that envelope is \`{ type, vars: { status, result } }\` (plus \`payload\` on an error) \u2014 the host's outcome, not the request \u2014 so the read fails with \`Unable to locate var\` after the host already ran (under \`rethrow\`, a failed response for committed work). Read the response at ${results}, or the request body with \`s.util.get_raw_input\`.`,
|
|
1716
|
+
middleware
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Every enabled middleware attachment in each leaf host's EFFECTIVE chain,
|
|
1722
|
+
* resolved across all three tiers — one row per host, phase and entry.
|
|
1723
|
+
*
|
|
1724
|
+
* The cascade is the engine's own: a phase whose `_customize` flag is set is
|
|
1725
|
+
* answered by that tier (including `clear()`, which customizes with an empty
|
|
1726
|
+
* list and therefore stops inheritance); an un-customized phase falls through
|
|
1727
|
+
* to the API group (queries only), then to the workspace's
|
|
1728
|
+
* `{objType}_{phase}` map. The apiGroup and workspace tiers are not hosts —
|
|
1729
|
+
* they are resolved INTO each host's chain. A disabled attachment does not run
|
|
1730
|
+
* and is not yielded.
|
|
1731
|
+
*/
|
|
1732
|
+
*attachedMiddleware(sections) {
|
|
1733
|
+
const middlewares = sections["middleware"];
|
|
1734
|
+
if (!Array.isArray(middlewares) || middlewares.length === 0) return;
|
|
1735
|
+
const byGuid = /* @__PURE__ */ new Map();
|
|
1736
|
+
for (const mw of middlewares) {
|
|
1737
|
+
if (!mw || typeof mw !== "object") continue;
|
|
1738
|
+
const guid = mw.guid;
|
|
1739
|
+
if (typeof guid === "string") byGuid.set(guid, mw);
|
|
1740
|
+
}
|
|
1741
|
+
const groupByGuid = /* @__PURE__ */ new Map();
|
|
1742
|
+
for (const group of sections["app"] ?? []) {
|
|
1743
|
+
if (!group || typeof group !== "object") continue;
|
|
1744
|
+
const guid = group.guid;
|
|
1745
|
+
if (typeof guid === "string") groupByGuid.set(guid, group);
|
|
1746
|
+
}
|
|
1747
|
+
const workspaceTier = this.workspaceConfig.middleware ?? {};
|
|
1748
|
+
const list = (x) => Array.isArray(x) ? x : [];
|
|
1749
|
+
const customizes = (block, phase) => !!block && typeof block === "object" && block[`${phase}_customize`] === true;
|
|
1750
|
+
for (const label of MIDDLEWARE_HOST_KINDS) {
|
|
1751
|
+
const hosts = sections[label];
|
|
1752
|
+
if (!Array.isArray(hosts)) continue;
|
|
1753
|
+
for (const host of hosts) {
|
|
1754
|
+
if (!host || typeof host !== "object") continue;
|
|
1755
|
+
const block = host.middleware;
|
|
1756
|
+
const hostName = String(host.name ?? "?");
|
|
1757
|
+
const groupId = label === "query" ? host.app?.id : void 0;
|
|
1758
|
+
const group = typeof groupId === "string" ? groupByGuid.get(groupId) : void 0;
|
|
1759
|
+
for (const phase of ["pre", "post"]) {
|
|
1760
|
+
let entries;
|
|
1761
|
+
let tier;
|
|
1762
|
+
if (customizes(block, phase)) {
|
|
1763
|
+
entries = list(block[phase]);
|
|
1764
|
+
tier = "";
|
|
1765
|
+
} else if (group && customizes(group.middleware, phase)) {
|
|
1766
|
+
entries = list(group.middleware[phase]);
|
|
1767
|
+
tier = ` (inherited from apiGroup "${String(group.name ?? "?")}")`;
|
|
1768
|
+
} else {
|
|
1769
|
+
entries = list(workspaceTier[`${label}_${phase}`]);
|
|
1770
|
+
tier = " (inherited from the workspace tier)";
|
|
1771
|
+
}
|
|
1772
|
+
for (const entry of entries) {
|
|
1773
|
+
if (entry?.disabled === true) continue;
|
|
1774
|
+
const guid = middlewareEntryGuid(entry);
|
|
1775
|
+
if (!guid) continue;
|
|
1776
|
+
yield {
|
|
1777
|
+
label,
|
|
1778
|
+
host,
|
|
1779
|
+
hostName,
|
|
1780
|
+
phase,
|
|
1781
|
+
tier,
|
|
1782
|
+
guid,
|
|
1783
|
+
middleware: byGuid.get(guid)
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1791
|
+
* Lock participation (see {@link export}): canonical fill + identity report.
|
|
1792
|
+
* Runs after all sections are assembled and before signing.
|
|
1793
|
+
*/
|
|
1794
|
+
applyLock(ctx, sections) {
|
|
1795
|
+
const usedCanonicals = /* @__PURE__ */ new Set();
|
|
1796
|
+
for (const entry of Object.values(ctx.lock.objects)) {
|
|
1797
|
+
if (entry.canonical) usedCanonicals.add(entry.canonical);
|
|
1798
|
+
}
|
|
1799
|
+
for (const arr of Object.values(sections)) {
|
|
1800
|
+
for (const obj2 of arr ?? []) {
|
|
1801
|
+
const canonical = obj2.canonical;
|
|
1802
|
+
if (typeof canonical === "string" && canonical !== "") usedCanonicals.add(canonical);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
const mintUnique = () => {
|
|
1806
|
+
let token = mintCanonical();
|
|
1807
|
+
while (usedCanonicals.has(token)) token = mintCanonical();
|
|
1808
|
+
usedCanonicals.add(token);
|
|
1809
|
+
return token;
|
|
1810
|
+
};
|
|
1811
|
+
const canonicalByGuid = (guid) => {
|
|
1812
|
+
if (typeof guid !== "string") return void 0;
|
|
1813
|
+
for (const entry of Object.values(ctx.lock.objects)) {
|
|
1814
|
+
if (entry.guid === guid && entry.canonical !== void 0) return entry.canonical;
|
|
1815
|
+
}
|
|
1816
|
+
return void 0;
|
|
1817
|
+
};
|
|
1818
|
+
const sourceByKey = /* @__PURE__ */ new Map();
|
|
1819
|
+
for (const key of CANONICAL_PAYLOAD_KEYS) {
|
|
1820
|
+
for (const obj2 of sections[key] ?? []) {
|
|
1821
|
+
if (!obj2 || typeof obj2 !== "object") continue;
|
|
1822
|
+
const o = obj2;
|
|
1823
|
+
if (typeof o.name !== "string") continue;
|
|
1824
|
+
if (o.canonical === "") {
|
|
1825
|
+
const locked = ctx.lock.objects[lockKey(key, o.name)];
|
|
1826
|
+
o.canonical = locked?.canonical ?? canonicalByGuid(o.guid) ?? mintUnique();
|
|
1827
|
+
if (locked?.canonical !== void 0 && locked.canonical_source === "adopted") this.adoptedCanonicals.add(obj2);
|
|
1828
|
+
this.filledCanonicals.add(obj2);
|
|
1829
|
+
}
|
|
1830
|
+
sourceByKey.set(
|
|
1831
|
+
lockKey(key, o.name),
|
|
1832
|
+
this.adoptedCanonicals.has(obj2) ? "adopted" : this.filledCanonicals.has(obj2) ? "minted" : "code"
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
const ws = this.workspaceConfig;
|
|
1837
|
+
if (ws.canonical === "") {
|
|
1838
|
+
ws.canonical = ctx.lock.objects[WORKSPACE_KEY]?.canonical ?? "";
|
|
1839
|
+
}
|
|
1840
|
+
if (typeof ws.canonical === "string" && ws.canonical !== "") {
|
|
1841
|
+
recordObserved(ctx, WORKSPACE_KEY, { canonical: ws.canonical });
|
|
1842
|
+
}
|
|
1843
|
+
const appNames = identityNamesByGuid(sections);
|
|
1844
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
1845
|
+
for (const [payloadKey, arr] of Object.entries(sections)) {
|
|
1846
|
+
if (!LOCK_PAYLOAD_KEYS.has(payloadKey)) continue;
|
|
1847
|
+
for (const obj2 of arr ?? []) {
|
|
1848
|
+
if (!obj2 || typeof obj2 !== "object") continue;
|
|
1849
|
+
const o = obj2;
|
|
1850
|
+
if (typeof o.name !== "string" || typeof o.guid !== "string") continue;
|
|
1851
|
+
const identity = { guid: o.guid };
|
|
1852
|
+
if (this.codeGuids.has(o.guid)) identity.guid_source = "code";
|
|
1853
|
+
const lockName = lockNameForObject(payloadKey, o, appNames);
|
|
1854
|
+
const key = lockKey(payloadKey, lockName);
|
|
1855
|
+
const label = `${sdkKindName(payloadKey, o)} "${o.name}"`;
|
|
1856
|
+
const prior = claimed.get(key);
|
|
1857
|
+
if (prior !== void 0 && prior.guid !== o.guid) {
|
|
1858
|
+
const why = payloadKey === "toolset" ? "Agents and MCP servers share ONE name space." : `Two objects of one kind sharing a name behind explicit guids.`;
|
|
1859
|
+
throw new Error(
|
|
1860
|
+
`Two exported objects collapse onto lock key "${sdkKindName(payloadKey, o)}:${lockName}" \u2014 ${prior.label} and ${label}, with different guids (${prior.guid} vs ${o.guid}). ${why} A lock entry holds ONE guid per name, so an explicit \`guid\` cannot separate them: rename one.`
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
claimed.set(key, { label, guid: o.guid });
|
|
1864
|
+
if (typeof o.canonical === "string" && o.canonical !== "") {
|
|
1865
|
+
identity.canonical = o.canonical;
|
|
1866
|
+
const source = sourceByKey.get(key);
|
|
1867
|
+
if (source !== void 0) identity.canonical_source = source;
|
|
1868
|
+
}
|
|
1869
|
+
if (payloadKey === "toolset" && o.type === "agent") identity.type = "agent";
|
|
1870
|
+
recordObserved(ctx, key, identity);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
function workspace(name) {
|
|
1876
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
1877
|
+
throw new Error(
|
|
1878
|
+
`workspace(name): the workspace name must be a non-empty string \u2014 got ${describeEntry(name)}. Name it: \`workspace("my-app")\`.`
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
const shorthand = { name };
|
|
1882
|
+
SHORTHAND_CONFIGS.add(shorthand);
|
|
1883
|
+
return new Xano().registerWorkspace(shorthand);
|
|
1884
|
+
}
|
|
1885
|
+
var SHORTHAND_CONFIGS = /* @__PURE__ */ new WeakSet();
|
|
1886
|
+
|
|
1887
|
+
export {
|
|
1888
|
+
refuseUnknown,
|
|
1889
|
+
MCP_OAUTH_ENV_REF,
|
|
1890
|
+
encodeMcpOauth,
|
|
1891
|
+
checkFilterOperands,
|
|
1892
|
+
XANO_ORIGIN,
|
|
1893
|
+
standaloneDefKind,
|
|
1894
|
+
Xano,
|
|
1895
|
+
workspace
|
|
1896
|
+
};
|
|
1897
|
+
//# sourceMappingURL=chunk-CTMO46PK.js.map
|