@xanots/sdk 0.0.11 → 0.0.12
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 +16 -0
- package/README.md +11 -6
- package/dist/.build-fingerprint +1 -1
- package/dist/{agent-file-refresh-QNKN5RYD.js → agent-file-refresh-GQWAAOBV.js} +4 -4
- package/dist/bin.js +11 -11
- package/dist/{branch-commands-2BLOC2GR.js → branch-commands-5KMPAAZV.js} +7 -7
- package/dist/bundle.d.ts +2 -2
- package/dist/bundle.js +2 -2
- package/dist/{capture-4WVJY4DQ.js → capture-YLUVAITI.js} +2 -2
- package/dist/chunk-2VTJSI6X.js +192 -0
- package/dist/{chunk-5XZ744TS.js → chunk-2ZCUO2UG.js} +1 -1
- package/dist/{chunk-XEOX6AM7.js → chunk-3LQGF2WS.js} +2 -2
- package/dist/{chunk-WP4OZZV4.js → chunk-3VFCKHOB.js} +2 -2
- package/dist/{chunk-XHEXOES3.js → chunk-5YDINUAP.js} +1 -1
- package/dist/{chunk-LU7TRWMC.js → chunk-6VNRMKFJ.js} +2 -2
- package/dist/{chunk-CTD5ZCV6.js → chunk-7WRJPKGK.js} +2 -2
- package/dist/{chunk-3INK4Y4E.js → chunk-AE3PDSDS.js} +1 -1
- package/dist/{chunk-22TKBSDV.js → chunk-AG5GCZDD.js} +2 -2
- package/dist/{chunk-DBFU47BJ.js → chunk-AOFFKSJC.js} +2 -2
- package/dist/{chunk-TCFIPDB3.js → chunk-AVGDL6RB.js} +1 -1
- package/dist/{chunk-BC2C5GVI.js → chunk-DCMANKMX.js} +1 -1
- package/dist/{chunk-WHOJWOSV.js → chunk-EETVJZAZ.js} +1 -1
- package/dist/{chunk-4Q7ZOHH7.js → chunk-ELK7UALJ.js} +3 -3
- package/dist/{chunk-VNQM3V2C.js → chunk-EXENFOWE.js} +2 -2
- package/dist/{chunk-UOZMSF4C.js → chunk-GSP2BY4F.js} +5 -5
- package/dist/{chunk-AIZKXUNP.js → chunk-IMLYGQK6.js} +2 -2
- package/dist/{chunk-QK7ZQJLP.js → chunk-N74KDCBD.js} +39 -25
- package/dist/{chunk-3IGNIP6R.js → chunk-NDP7OUPS.js} +1 -1
- package/dist/{chunk-EQW3YT5U.js → chunk-P6PVBQL6.js} +2 -2
- package/dist/{chunk-HYBN4H3F.js → chunk-PLE5QQOZ.js} +27 -20
- package/dist/{chunk-BSK7ELHU.js → chunk-PR7OXHGZ.js} +1 -1
- package/dist/{chunk-OWGCOGKK.js → chunk-QYSQ3UDO.js} +8 -8
- package/dist/{chunk-ZQ2PKR6R.js → chunk-RQ3FXV4K.js} +2 -2
- package/dist/{chunk-Q77KNEUL.js → chunk-RQNMTDXD.js} +1703 -2
- package/dist/{chunk-QKM4U5UK.js → chunk-TJS2AF5Y.js} +2 -2
- package/dist/{chunk-DIA7CT7J.js → chunk-V5Y7D4LH.js} +11 -2
- package/dist/{chunk-KA6G2L7U.js → chunk-VK26K7AY.js} +3 -3
- package/dist/{chunk-4IF54NU5.js → chunk-VPAWRBK5.js} +12 -16
- package/dist/{chunk-QYMAZRAU.js → chunk-VRNZ2NVV.js} +5 -5
- package/dist/{chunk-RCT7UX7B.js → chunk-XT3XQ4PF.js} +32 -27
- package/dist/{chunk-VAF6A3YD.js → chunk-XWFRNJMQ.js} +1 -1
- package/dist/{chunk-7ZYW652H.js → chunk-YHS6VVLJ.js} +2 -2
- package/dist/{chunk-G4EJMQLD.js → chunk-YX22LKQE.js} +2 -2
- package/dist/{chunk-F6CYJ7TN.js → chunk-Z2ZIE5CO.js} +2 -2
- package/dist/cli.d.ts +13 -6
- package/dist/cli.js +10 -10
- package/dist/codegen-command-Y2SPMAUW.js +47 -0
- package/dist/codegen.d.ts +5 -5
- package/dist/codegen.js +2 -2
- package/dist/{completion-WF46272M.js → completion-HJU5QEFB.js} +2 -2
- package/dist/{deploy-command-IP7V7GT4.js → deploy-command-XHS5PKPV.js} +19 -19
- package/dist/{ephemeral-command-U4AQ3TXX.js → ephemeral-command-2NKPEXUU.js} +8 -8
- package/dist/index.d.ts +88 -104
- package/dist/index.js +9 -9
- package/dist/init-command-23FFNUFT.js +32 -0
- package/dist/internal.d.ts +10 -16
- package/dist/internal.js +26 -26
- package/dist/{io-P2H75UV2.js → io-UBDMMDH6.js} +3 -3
- package/dist/{live-diff-IXKBVG4K.js → live-diff-HCOTN5WC.js} +3 -3
- package/dist/{lock-46FWYE4D.js → lock-HQ4KARU2.js} +2 -2
- package/dist/{lock-commands-ZZKZ4LZJ.js → lock-commands-WCRC56ME.js} +11 -11
- package/dist/{login-command-Z6CHTA57.js → login-command-P7LXD5TE.js} +6 -6
- package/dist/{loop-D5NPL4VH.js → loop-IC5ISSYB.js} +3 -3
- package/dist/{marketplace-command-P4IPLJ6J.js → marketplace-command-T6JCHW7J.js} +4 -4
- package/dist/{meta-client-K2J4XH64.js → meta-client-LKRKR3L2.js} +4 -4
- package/dist/node.d.ts +6 -6
- package/dist/node.js +14 -14
- package/dist/{preflight-command-K346GPTY.js → preflight-command-TZWPHBXY.js} +16 -16
- package/dist/{profile-command-LJSBDV2L.js → profile-command-WBNSMQSI.js} +3 -3
- package/dist/{release-command-IMNTIVWK.js → release-command-WEFYRTCI.js} +26 -26
- package/dist/{response-BQVQ24l1.d.ts → response-D6xGLEIn.d.ts} +18 -23
- package/dist/{routes-manifest-PWZHDOI5.js → routes-manifest-5ZFKUQWA.js} +2 -2
- package/dist/{runtime-V4C3AC3A.js → runtime-LSLIDALK.js} +1 -1
- package/dist/scaffold.d.ts +16 -8
- package/dist/scaffold.js +7 -11
- package/dist/{static-host-3WMV7IZO.js → static-host-4JDQCHUW.js} +1 -1
- package/dist/{status-command-AL47VG7H.js → status-command-VARULAR5.js} +4 -4
- package/dist/{store-BLyNeQ8S.d.ts → store-DAnUIi1T.d.ts} +81 -86
- package/dist/{test-command-YAZLKLGQ.js → test-command-YCAR4O35.js} +7 -7
- package/dist/{upgrade-command-BN3EHAOI.js → upgrade-command-GYEIMJBG.js} +15 -16
- package/dist/{workspace-2COHDBM3.js → workspace-33KHFLX3.js} +2 -2
- package/dist/{workspace-command-MNK7Y7MQ.js → workspace-command-2ZTGT26W.js} +26 -28
- package/dist/{workspace-export-DURY5WYL.js → workspace-export-MLYTYZS5.js} +3 -3
- package/dist/{xdo-BjJj5W_E.d.ts → xdo-ODuJklk6.d.ts} +27 -23
- package/guides/authoring.md +11 -2
- package/guides/typed-frontend.md +4 -4
- package/llms/statements-data.md +3 -2
- package/llms/values.md +1 -1
- package/llms-full.txt +18 -18
- package/llms.txt +14 -15
- package/manifest.json +6 -2
- package/package.json +1 -1
- package/dist/chunk-ANUDXFEX.js +0 -881
- package/dist/chunk-JGCWTCA7.js +0 -95
- package/dist/chunk-YBC3IKMF.js +0 -845
- package/dist/codegen-command-FUT2KJB6.js +0 -49
- package/dist/init-command-NPVL32L6.js +0 -34
|
@@ -4,25 +4,25 @@ import {
|
|
|
4
4
|
} from "./chunk-NUQCEOKA.js";
|
|
5
5
|
import {
|
|
6
6
|
resolveMetaTarget
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RQ3FXV4K.js";
|
|
8
8
|
import "./chunk-K5IOND4K.js";
|
|
9
9
|
import {
|
|
10
10
|
getAccessToken
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-TJS2AF5Y.js";
|
|
12
12
|
import "./chunk-FE5I6S6N.js";
|
|
13
13
|
import {
|
|
14
14
|
fetchOrExplain,
|
|
15
15
|
httpFailure,
|
|
16
16
|
serverMessage
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-NDP7OUPS.js";
|
|
18
18
|
import {
|
|
19
19
|
parseEnvSelector,
|
|
20
20
|
unknownEnv
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-7WRJPKGK.js";
|
|
22
22
|
import {
|
|
23
23
|
UsageError,
|
|
24
24
|
unknownSubcommand
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-Z2ZIE5CO.js";
|
|
26
26
|
import "./chunk-GNPVYOPB.js";
|
|
27
27
|
import {
|
|
28
28
|
blank,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
success,
|
|
33
33
|
warn
|
|
34
34
|
} from "./chunk-EZG76F7R.js";
|
|
35
|
-
import "./chunk-
|
|
35
|
+
import "./chunk-V5Y7D4LH.js";
|
|
36
36
|
|
|
37
37
|
// src/deploy/tests.ts
|
|
38
38
|
var TIMEOUT_MS = 12e4;
|
|
@@ -397,4 +397,4 @@ export {
|
|
|
397
397
|
runSuite,
|
|
398
398
|
runTestCommand
|
|
399
399
|
};
|
|
400
|
-
//# sourceMappingURL=test-command-
|
|
400
|
+
//# sourceMappingURL=test-command-YCAR4O35.js.map
|
|
@@ -3,30 +3,29 @@ import {
|
|
|
3
3
|
} from "./chunk-ERQZFWIW.js";
|
|
4
4
|
import {
|
|
5
5
|
refreshAgentFiles
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2VTJSI6X.js";
|
|
7
7
|
import {
|
|
8
8
|
isMachineOutput,
|
|
9
9
|
writeJson
|
|
10
10
|
} from "./chunk-NUQCEOKA.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-VK26K7AY.js";
|
|
12
12
|
import {
|
|
13
13
|
runNpm
|
|
14
14
|
} from "./chunk-ZSYZTGJH.js";
|
|
15
15
|
import {
|
|
16
16
|
sdkDep
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-Q77KNEUL.js";
|
|
17
|
+
} from "./chunk-RQNMTDXD.js";
|
|
19
18
|
import {
|
|
20
19
|
checkForUpgrade,
|
|
21
20
|
detectInstallMode,
|
|
22
21
|
registryUrl,
|
|
23
22
|
suppressUpdateNotice,
|
|
24
23
|
upgradeCommand
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
29
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-AG5GCZDD.js";
|
|
25
|
+
import "./chunk-XT3XQ4PF.js";
|
|
26
|
+
import "./chunk-7WRJPKGK.js";
|
|
27
|
+
import "./chunk-3LQGF2WS.js";
|
|
28
|
+
import "./chunk-Z2ZIE5CO.js";
|
|
30
29
|
import "./chunk-GNPVYOPB.js";
|
|
31
30
|
import {
|
|
32
31
|
blank,
|
|
@@ -36,12 +35,12 @@ import {
|
|
|
36
35
|
success,
|
|
37
36
|
warn
|
|
38
37
|
} from "./chunk-EZG76F7R.js";
|
|
39
|
-
import "./chunk-
|
|
40
|
-
import "./chunk-
|
|
41
|
-
import "./chunk-
|
|
42
|
-
import "./chunk-
|
|
43
|
-
import "./chunk-
|
|
44
|
-
import "./chunk-
|
|
38
|
+
import "./chunk-QYSQ3UDO.js";
|
|
39
|
+
import "./chunk-V5Y7D4LH.js";
|
|
40
|
+
import "./chunk-N74KDCBD.js";
|
|
41
|
+
import "./chunk-EETVJZAZ.js";
|
|
42
|
+
import "./chunk-XWFRNJMQ.js";
|
|
43
|
+
import "./chunk-2ZCUO2UG.js";
|
|
45
44
|
import "./chunk-OHX6MIUZ.js";
|
|
46
45
|
|
|
47
46
|
// src/emit/upgrade-command.ts
|
|
@@ -176,4 +175,4 @@ function messageOf(err) {
|
|
|
176
175
|
export {
|
|
177
176
|
runUpgradeCommand
|
|
178
177
|
};
|
|
179
|
-
//# sourceMappingURL=upgrade-command-
|
|
178
|
+
//# sourceMappingURL=upgrade-command-GYEIMJBG.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
fetchReadOrExplain
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NDP7OUPS.js";
|
|
4
4
|
|
|
5
5
|
// src/deploy/workspace.ts
|
|
6
6
|
var RESOLVE_TIMEOUT_MS = 3e4;
|
|
@@ -46,4 +46,4 @@ ${text}`);
|
|
|
46
46
|
export {
|
|
47
47
|
resolveScopedWorkspaceId
|
|
48
48
|
};
|
|
49
|
-
//# sourceMappingURL=workspace-
|
|
49
|
+
//# sourceMappingURL=workspace-33KHFLX3.js.map
|
|
@@ -11,41 +11,39 @@ import {
|
|
|
11
11
|
} from "./chunk-NUQCEOKA.js";
|
|
12
12
|
import {
|
|
13
13
|
fetchWorkspaceBundle
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-PLE5QQOZ.js";
|
|
15
|
+
import "./chunk-RQ3FXV4K.js";
|
|
16
16
|
import "./chunk-K5IOND4K.js";
|
|
17
17
|
import {
|
|
18
18
|
ENV_WORKSPACE,
|
|
19
19
|
getAccessToken
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-TJS2AF5Y.js";
|
|
21
21
|
import "./chunk-FE5I6S6N.js";
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-YHS6VVLJ.js";
|
|
23
23
|
import "./chunk-ZUTSMMAG.js";
|
|
24
24
|
import "./chunk-X4DVXBFY.js";
|
|
25
25
|
import {
|
|
26
26
|
fetchOrExplain,
|
|
27
27
|
httpFailure
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-
|
|
28
|
+
} from "./chunk-NDP7OUPS.js";
|
|
29
|
+
import "./chunk-VPAWRBK5.js";
|
|
30
|
+
import "./chunk-VK26K7AY.js";
|
|
31
31
|
import "./chunk-ZSYZTGJH.js";
|
|
32
|
-
import "./chunk-
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
35
|
-
import "./chunk-
|
|
36
|
-
import "./chunk-
|
|
37
|
-
import "./chunk-BSK7ELHU.js";
|
|
38
|
-
import "./chunk-UOZMSF4C.js";
|
|
32
|
+
import "./chunk-YX22LKQE.js";
|
|
33
|
+
import "./chunk-6VNRMKFJ.js";
|
|
34
|
+
import "./chunk-RQNMTDXD.js";
|
|
35
|
+
import "./chunk-PR7OXHGZ.js";
|
|
36
|
+
import "./chunk-GSP2BY4F.js";
|
|
39
37
|
import "./chunk-EHP3WPEG.js";
|
|
40
|
-
import "./chunk-
|
|
41
|
-
import "./chunk-
|
|
42
|
-
import "./chunk-
|
|
43
|
-
import "./chunk-
|
|
44
|
-
import "./chunk-
|
|
38
|
+
import "./chunk-AE3PDSDS.js";
|
|
39
|
+
import "./chunk-DCMANKMX.js";
|
|
40
|
+
import "./chunk-XT3XQ4PF.js";
|
|
41
|
+
import "./chunk-7WRJPKGK.js";
|
|
42
|
+
import "./chunk-3LQGF2WS.js";
|
|
45
43
|
import {
|
|
46
44
|
removedSubcommand,
|
|
47
45
|
unknownSubcommand
|
|
48
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-Z2ZIE5CO.js";
|
|
49
47
|
import "./chunk-GNPVYOPB.js";
|
|
50
48
|
import {
|
|
51
49
|
formatFields,
|
|
@@ -53,12 +51,12 @@ import {
|
|
|
53
51
|
step,
|
|
54
52
|
success
|
|
55
53
|
} from "./chunk-EZG76F7R.js";
|
|
56
|
-
import "./chunk-
|
|
57
|
-
import "./chunk-
|
|
58
|
-
import "./chunk-
|
|
59
|
-
import "./chunk-
|
|
60
|
-
import "./chunk-
|
|
61
|
-
import "./chunk-
|
|
54
|
+
import "./chunk-QYSQ3UDO.js";
|
|
55
|
+
import "./chunk-V5Y7D4LH.js";
|
|
56
|
+
import "./chunk-N74KDCBD.js";
|
|
57
|
+
import "./chunk-EETVJZAZ.js";
|
|
58
|
+
import "./chunk-XWFRNJMQ.js";
|
|
59
|
+
import "./chunk-2ZCUO2UG.js";
|
|
62
60
|
import "./chunk-OHX6MIUZ.js";
|
|
63
61
|
|
|
64
62
|
// src/emit/workspace-command.ts
|
|
@@ -72,7 +70,7 @@ async function runWorkspaceCommand(args) {
|
|
|
72
70
|
case "export":
|
|
73
71
|
return runExport(args);
|
|
74
72
|
case "branch": {
|
|
75
|
-
const { runBranchCommand } = await import("./branch-commands-
|
|
73
|
+
const { runBranchCommand } = await import("./branch-commands-5KMPAAZV.js");
|
|
76
74
|
return runBranchCommand(args);
|
|
77
75
|
}
|
|
78
76
|
case "deploy":
|
|
@@ -162,4 +160,4 @@ async function runExport(args) {
|
|
|
162
160
|
export {
|
|
163
161
|
runWorkspaceCommand
|
|
164
162
|
};
|
|
165
|
-
//# sourceMappingURL=workspace-command-
|
|
163
|
+
//# sourceMappingURL=workspace-command-2ZTGT26W.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exportWorkspaceBundle
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YHS6VVLJ.js";
|
|
4
4
|
import "./chunk-ZUTSMMAG.js";
|
|
5
5
|
import "./chunk-X4DVXBFY.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-NDP7OUPS.js";
|
|
7
7
|
export {
|
|
8
8
|
exportWorkspaceBundle
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=workspace-export-
|
|
10
|
+
//# sourceMappingURL=workspace-export-MLYTYZS5.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The shared tagged-value primitive
|
|
2
|
+
* The shared tagged-value primitive. Every place a function references
|
|
3
3
|
* data — input bindings, statement context, response — uses this `{value, tag,
|
|
4
4
|
* filters}` shape. Built and tested once here, reused everywhere.
|
|
5
5
|
*/
|
|
@@ -10,7 +10,7 @@ type Value = TaggedValue;
|
|
|
10
10
|
* A {@link Value} that also carries, **at the type level only**, the name of the
|
|
11
11
|
* stack variable it references (`ref("user")` → `RefValue<"user">`). The `__ref`
|
|
12
12
|
* carrier is phantom — never present at runtime — and required (not optional) so
|
|
13
|
-
* `InferResponse`'s trace
|
|
13
|
+
* `InferResponse`'s trace matches only real refs, never a plain `Value`.
|
|
14
14
|
* Because it is a subtype of `Value`, every existing `ref(...)` use — filter
|
|
15
15
|
* args, `db.query` `where`, response fields — keeps type-checking unchanged.
|
|
16
16
|
*/
|
|
@@ -41,7 +41,7 @@ type FilteredValue<Base = unknown, Chain extends readonly unknown[] = readonly u
|
|
|
41
41
|
* an editor's hover distinguishes a pattern from the text beside it.
|
|
42
42
|
*
|
|
43
43
|
* It is deliberately NOT used to restrict `s.expect.to_match` or the `fl.regex_*`
|
|
44
|
-
* pattern slot
|
|
44
|
+
* pattern slot. A nominal restriction there
|
|
45
45
|
* would have to reject `Value`, and `inp()`, `env()`, `auth()` and `sys.*` all
|
|
46
46
|
* return exactly `Value` — the same type `c.text` does — so it would reject every
|
|
47
47
|
* DYNAMIC pattern along with the broken constant ones. The enforcement is instead
|
|
@@ -63,15 +63,15 @@ type FlattenFilters<Fs extends readonly unknown[]> = Fs extends readonly [
|
|
|
63
63
|
* *statically rejected* where it would silently fail at runtime: inside a
|
|
64
64
|
* `db.edit`/`db.add` `row`, `{tag:"col"}` does not resolve to the row's stored
|
|
65
65
|
* value — it evaluates to `null`, so a following `fl.add(1)` computes `null + 1`
|
|
66
|
-
* and the engine aborts ("Numbers are required for mathematical operations"
|
|
67
|
-
*
|
|
66
|
+
* and the engine aborts ("Numbers are required for mathematical operations").
|
|
67
|
+
* `col()` is only meaningful in a `db.query` `where`/view expression.
|
|
68
68
|
*/
|
|
69
69
|
type ColValue = Value & {
|
|
70
70
|
readonly __col: true;
|
|
71
71
|
};
|
|
72
72
|
/**
|
|
73
73
|
* The error branch surfaced when a tagged {@link Value} is nested inside a
|
|
74
|
-
* `c.obj`/`c.array` literal
|
|
74
|
+
* `c.obj`/`c.array` literal. The long message is the *property key*
|
|
75
75
|
* so TypeScript prints it verbatim in the "property … is missing" diagnostic; a
|
|
76
76
|
* `Value` has no such key, so intersecting it here makes the offending position
|
|
77
77
|
* fail to type-check. The runtime guard ({@link assertPlainJson}) carries the
|
|
@@ -107,7 +107,7 @@ type BlankTag = Exclude<Extract<Tag, `const${string}`>, "const" | "const:obj">;
|
|
|
107
107
|
/**
|
|
108
108
|
* Tags {@link c.null} can spell — the two the engine stores a `"null"` value
|
|
109
109
|
* under. `const:null` is the plain one; `const:obj` is the object-typed null a
|
|
110
|
-
* `db.*` statement's `@meta` slot carries
|
|
110
|
+
* `db.*` statement's `@meta` slot carries.
|
|
111
111
|
*
|
|
112
112
|
* Listed rather than derived: a `"null"` value under any OTHER constant tag is
|
|
113
113
|
* unobserved, and inventing spellings for it would offer an authoring form for
|
|
@@ -141,7 +141,7 @@ declare const c: {
|
|
|
141
141
|
* A `number` that is not an integer, or is past `Number.MAX_SAFE_INTEGER`,
|
|
142
142
|
* **throws**: by the time such a literal reaches here its precision is already
|
|
143
143
|
* gone, so encoding it would write a value the caller did not type. The throw
|
|
144
|
-
* names the string form.
|
|
144
|
+
* names the string form.
|
|
145
145
|
*/
|
|
146
146
|
int(n: number | bigint | string): Value;
|
|
147
147
|
/**
|
|
@@ -181,7 +181,7 @@ declare const c: {
|
|
|
181
181
|
* null** the engine writes into a `db.*` statement's `@meta` slot —
|
|
182
182
|
* `c.null("const:obj")` → `{tag:"const:obj", value:"null"}`, 16 of them across
|
|
183
183
|
* eight ordinary queries in the survey corpus, which had no spelling and came
|
|
184
|
-
* back as `rawValue
|
|
184
|
+
* back as `rawValue`.
|
|
185
185
|
*
|
|
186
186
|
* ⚠ Not the same bytes as `c.obj(null)`, which is the BLANK object
|
|
187
187
|
* (`value: ""`). Both evaluate to null — the engine JSON-decodes the stored
|
|
@@ -195,7 +195,7 @@ declare const c: {
|
|
|
195
195
|
* (`fl.regex_test`/`regex_match`/`regex_replace`/…). Xano runs PHP `preg_*`, so
|
|
196
196
|
* the pattern MUST be delimiter-wrapped — a bare `c.text("^…$")` is an invalid
|
|
197
197
|
* PCRE and the filter then matches *nothing* for every input, so a precondition
|
|
198
|
-
* built on it silently rejects all values
|
|
198
|
+
* built on it silently rejects all values. This wraps the raw
|
|
199
199
|
* pattern in `/…/`, escapes any interior `/`, and appends `flags`, so the result
|
|
200
200
|
* is always valid; `withFilters` rejects a bare `c.text` pattern and points here.
|
|
201
201
|
*
|
|
@@ -216,8 +216,13 @@ declare const c: {
|
|
|
216
216
|
* `text("now") |to_epoch_ms` chain; both evaluate to the same epoch-ms number
|
|
217
217
|
* on a live engine, and both persist verbatim, but the native tag is what the
|
|
218
218
|
* editor writes and needs no filter to get there. Chain math onto it as usual:
|
|
219
|
-
* `withFilters(c.now(), fl.epochms_add_ms(c.int(-maxAgeMs)))`.
|
|
220
|
-
*
|
|
219
|
+
* `withFilters(c.now(), fl.epochms_add_ms(c.int(-maxAgeMs)))`.
|
|
220
|
+
*
|
|
221
|
+
* Inside an `obj()` the tag is flattened into an expression string, where the
|
|
222
|
+
* identifier `now` alone is the STRING "now" — so `obj()` writes the
|
|
223
|
+
* `now|to_epochms` coercion that restores this tag's meaning. A `c.now()` used
|
|
224
|
+
* as a FILTER ARGUMENT there is refused: the coercion cannot be written in
|
|
225
|
+
* that position. Hoist it with `s.set_var` and `ref()` it.
|
|
221
226
|
*/
|
|
222
227
|
now(): Value;
|
|
223
228
|
/**
|
|
@@ -226,7 +231,6 @@ declare const c: {
|
|
|
226
231
|
* compile error and throws at runtime — it would serialize as internal
|
|
227
232
|
* representation the engine can't decode. For a computed/multi-key object
|
|
228
233
|
* response use a record of values (`response: { key: value }`), not `c.obj`.
|
|
229
|
-
* See issue #42.
|
|
230
234
|
*
|
|
231
235
|
* Called with no argument it is the **empty object**, `{}` — the same default
|
|
232
236
|
* the editor gives a new object variable, and the only empty form current
|
|
@@ -243,7 +247,7 @@ declare const c: {
|
|
|
243
247
|
* same bytes instead of being quietly re-pointed at `{}`.
|
|
244
248
|
*
|
|
245
249
|
* A **populated** object is stored the way the editor stores one: an empty
|
|
246
|
-
* `{}` base carrying one `set` filter per key
|
|
250
|
+
* `{}` base carrying one `set` filter per key. It is NOT a
|
|
247
251
|
* populated JSON string — that form fails the request at runtime, see
|
|
248
252
|
* {@link objSetFilters}.
|
|
249
253
|
*
|
|
@@ -257,7 +261,7 @@ declare const c: {
|
|
|
257
261
|
/**
|
|
258
262
|
* Array constant → JSON-string value with `tag:"const:array"`. Takes **plain
|
|
259
263
|
* JSON literals only** — like {@link obj}, a nested tagged value is a compile
|
|
260
|
-
* error and throws at runtime.
|
|
264
|
+
* error and throws at runtime.
|
|
261
265
|
*/
|
|
262
266
|
array<const T extends readonly unknown[]>(a: T & RejectValues<T>): Value;
|
|
263
267
|
/**
|
|
@@ -308,7 +312,7 @@ interface RefOptions {
|
|
|
308
312
|
* compiles to the raw var path `$owner.user_id`, which the engine resolves in
|
|
309
313
|
* a single lookup — so when the base var `owner` is null (e.g. a `db.get` that
|
|
310
314
|
* matched no row), it raises a runtime `ERROR_FATAL` "Unable to locate var"
|
|
311
|
-
* (HTTP 500) instead of yielding null
|
|
315
|
+
* (HTTP 500) instead of yielding null.
|
|
312
316
|
*
|
|
313
317
|
* With `safe: true` the path compiles through the `get` filter
|
|
314
318
|
* (`$owner|get:"user_id"`), which walks the remaining path and resolves to
|
|
@@ -324,7 +328,7 @@ interface RefOptions {
|
|
|
324
328
|
*
|
|
325
329
|
* Pass `{ safe: true }` to make a *nested* path null-safe: `ref("owner.user_id",
|
|
326
330
|
* { safe: true })` resolves to null instead of 500ing when the base `owner` is
|
|
327
|
-
* null
|
|
331
|
+
* null — the intent-revealing opt-in for drilling into a `db.get`
|
|
328
332
|
* result that may not exist.
|
|
329
333
|
*
|
|
330
334
|
* Picking a reference helper (these are easy to mix up):
|
|
@@ -339,7 +343,7 @@ declare function ref<const Name extends string>(name: Name, opts?: RefOptions):
|
|
|
339
343
|
/**
|
|
340
344
|
* The second parameter every non-`ref` reference creator declares, so that
|
|
341
345
|
* passing `{ safe: true }` to one is a COMPILE error whose expected type is the
|
|
342
|
-
* explanation
|
|
346
|
+
* explanation.
|
|
343
347
|
*
|
|
344
348
|
* `safe` was silently dropped before this: the creators took one parameter, so
|
|
345
349
|
* JavaScript discarded the extra argument and TypeScript never saw it. An
|
|
@@ -353,7 +357,7 @@ declare function inp(name: string, opts?: SafeIsRefOnly): Value;
|
|
|
353
357
|
* Reference a table **column**: `{tag:"col", value}` (used in `db.query` `where` +
|
|
354
358
|
* table views). See {@link ref} for the full picker. The return is branded
|
|
355
359
|
* {@link ColValue} so it is a *compile error* to pass `col()` into a `db.edit`/
|
|
356
|
-
* `db.add` `row` — where it would resolve to `null` at runtime
|
|
360
|
+
* `db.add` `row` — where it would resolve to `null` at runtime.
|
|
357
361
|
*/
|
|
358
362
|
declare function col(name: string, opts?: SafeIsRefOnly): ColValue;
|
|
359
363
|
/**
|
|
@@ -422,7 +426,7 @@ type ToolsetPath = "token" | "params" | `params.${string}`;
|
|
|
422
426
|
* - `toolset("params.tenant")` — one parameter out of that object.
|
|
423
427
|
*
|
|
424
428
|
* Only bound while a tool runs under its toolset; anywhere else it reads empty
|
|
425
|
-
|
|
429
|
+
*.
|
|
426
430
|
*/
|
|
427
431
|
declare function toolset(path: ToolsetPath, opts?: SafeIsRefOnly): Value;
|
|
428
432
|
/**
|
|
@@ -745,7 +749,7 @@ declare function test(def: TestDef): TestDef;
|
|
|
745
749
|
* The SDK emits only authored fields. Server/auto-generated keys (`id`,
|
|
746
750
|
* `created_at`, `_xsid`, `@guid`, `@index`, `guid`, `_draft`, `stack_id`, …)
|
|
747
751
|
* are generated by the engine on import and are NEVER emitted here; the test
|
|
748
|
-
* normalizer
|
|
752
|
+
* normalizer strips them from the fixture before comparison.
|
|
749
753
|
*/
|
|
750
754
|
/**
|
|
751
755
|
* Xano tagged-value tags (verified against the Xano engine). The runtime
|
|
@@ -779,7 +783,7 @@ interface FilterXdo<N extends string = string> {
|
|
|
779
783
|
* Required here until now, which made a pulled workspace fail its own
|
|
780
784
|
* type-check: codegen emits a stored filter verbatim when no `fl.*` form
|
|
781
785
|
* reproduces it, and a verbatim emit of a filter stored without the key does
|
|
782
|
-
* not have it
|
|
786
|
+
* not have it. Writing `disabled: false` in would have been the
|
|
783
787
|
* other repair, and it is the wrong one — it ADDS a key the engine did not
|
|
784
788
|
* store, so the re-encode would no longer match the stored bytes and the value
|
|
785
789
|
* would degrade further rather than round-trip.
|
|
@@ -810,7 +814,7 @@ interface MethodXdo {
|
|
|
810
814
|
}
|
|
811
815
|
/**
|
|
812
816
|
* A field-bearing entry — the shape shared by function/query inputs and table
|
|
813
|
-
* columns
|
|
817
|
+
* columns. In the persisted form the two are byte-identical: both use
|
|
814
818
|
* `customize:{}`, numeric `market_item` ids, an `_xsid` placeholder, and a
|
|
815
819
|
* present `description`. (The older parser generation emitted `customize:""` /
|
|
816
820
|
* string ids for inputs; the live `mvp_query`/`mvp_dbo` rows show they converge.)
|
package/guides/authoring.md
CHANGED
|
@@ -137,6 +137,13 @@ auto-assigned `id`/`created_at`, `s.db.del` binds `null`, and `edit`/`del` **thr
|
|
|
137
137
|
the engine reads the operand as a text literal and fails at runtime with a parse error
|
|
138
138
|
naming the *other* operand, so `db.query` rejects that spelling at export instead.
|
|
139
139
|
|
|
140
|
+
- **A join adds no columns to the returned row.** With or without a `bind`, a row is the
|
|
141
|
+
queried table's columns — which is what `InferResponse` types, so there is no `row.author`
|
|
142
|
+
to read. To bring a joined column back, project it with an `eval` whose `name` is the dotted
|
|
143
|
+
path: `eval: [{ name: "author.name", as: "author_name" }]` puts `author_name` on the row and
|
|
144
|
+
on the inferred type. A *bare* name there fails at runtime (it qualifies to the base table),
|
|
145
|
+
and a dotted joined column in `output` is dropped with no error.
|
|
146
|
+
|
|
140
147
|
- **Paging changes the response shape.** Supplying `paging` with metadata on (the default)
|
|
141
148
|
returns a **paging envelope** — `{ items, curPage, nextPage, prevPage, offset, perPage,
|
|
142
149
|
itemsReceived }`, plus totals when `totals: true` — instead of a bare `Row[]`, and
|
|
@@ -194,8 +201,10 @@ auto-assigned `id`/`created_at`, `s.db.del` binds `null`, and `edit`/`del` **thr
|
|
|
194
201
|
`lower`, and the `epochms_add_day` / `epochms_sub_month` family). The request-time timestamp
|
|
195
202
|
filters do not — `epochms_transform`, `epochms_add_ms`, `epochms_add_secs`, `epochms_date`,
|
|
196
203
|
`epochms_from_format` — and the engine doesn't degrade gracefully: the request dies with a
|
|
197
|
-
bare fatal naming nothing. For a relative cutoff, use the SQL-side spelling
|
|
198
|
-
|
|
204
|
+
bare fatal naming nothing. For a relative cutoff, use the SQL-side spelling — by raw name,
|
|
205
|
+
since `fl.*` carries no builder for that family: `filter("epochms_add_day", …)` or the raw
|
|
206
|
+
`{ name, arg }` form — or compute it in an earlier `s.set_var` and `ref()` that. `export()`
|
|
207
|
+
warns; a bare `c.now()` is always fine.
|
|
199
208
|
- **A `s.switch` case without `break: true` falls through.** The engine's default is
|
|
200
209
|
fallthrough, so a matched case also runs every *later* case body — and the `default` block
|
|
201
210
|
too. Whatever those bodies write gets written two or three times, at HTTP 200, with `tsc`
|
package/guides/typed-frontend.md
CHANGED
|
@@ -157,10 +157,10 @@ also pulls whatever its `stack` builds — the `s.*`/`c.*` factory *calls* run a
|
|
|
157
157
|
to construct the def, so they can't be tree-shaken out. Types are free (`InferInput`/
|
|
158
158
|
`InferRow` erase to nothing — use `import type`). That cost is a **floor**, not a function of
|
|
159
159
|
how lean the def is. Measured on a Vite lib build against the published package: one
|
|
160
|
-
`apiGroup` + one empty `query`, imported for a single `getPath()`, is **
|
|
161
|
-
(
|
|
160
|
+
`apiGroup` + one empty `query`, imported for a single `getPath()`, is **267 kB minified
|
|
161
|
+
(65 kB gzipped)** against 56 B for a hand-written path string. A realistic def — two
|
|
162
162
|
tables, a foreign key, a typed input, a `db.query` with a `where` and a sort, plus a
|
|
163
|
-
second endpoint — measures
|
|
163
|
+
second endpoint — measures 269 kB. That 2 kB spread is the point: the floor is the SDK
|
|
164
164
|
runtime itself, so splitting modules or simplifying a def does not move it, and the cost
|
|
165
165
|
is paid by importing any def at all.
|
|
166
166
|
|
|
@@ -172,7 +172,7 @@ xanots routes ./xano/index.ts --emit xano/routes.gen.ts
|
|
|
172
172
|
```
|
|
173
173
|
|
|
174
174
|
The emitted file is plain data plus one interpolator and imports nothing at all — the same
|
|
175
|
-
app builds to 1.3 kB, a
|
|
175
|
+
app builds to 1.3 kB, a ~200x saving, with no SDK code in the output. Route names and their `{param}` keys are still checked at compile
|
|
176
176
|
time, so a backend rename is a compile error rather than a 404:
|
|
177
177
|
|
|
178
178
|
```ts
|
package/llms/statements-data.md
CHANGED
|
@@ -34,13 +34,14 @@ primary key `id`):
|
|
|
34
34
|
- ⚠ `like`/`ilike` take the operand as the PATTERN, verbatim: a bare term matches only an exact whole-string equal, and the endpoint answers HTTP 200 with zero rows — nothing reports a problem, so a search box that matches nothing ships. For substring matching use `includes`/`not includes`, which wrap the operand in `%…%` themselves and match case-INSENSITIVELY. Prefer them over a hand-built `"%" + term + "%"`, which is non-empty even for an empty term and so defeats `ignoreEmpty`; `includes` composes with it. `contains`/`@>`/`overlaps` are JSON/array containment, not text — on a text column they 400 `ParseError: Invalid value for param`.
|
|
35
35
|
- Compose nested boolean logic with `and(...)` / `or(...)` groups (also available on `addon()` `where`).
|
|
36
36
|
- An operand may be a bare value (`col`/`inp`/`ref`/`auth`/`c.*`) OR a **filtered** value (`withFilters(...)`) inline — the engine compiles the string and arithmetic filters (`trim`, `concat`, `upper`, `lower`, …) into the SQL.
|
|
37
|
-
- ⚠ The REQUEST-TIME timestamp filters have no SQL form and kill the request with a bare fatal naming nothing: `epochms_transform`, `epochms_add_ms`, `epochms_add_secs`, `epochms_date`, `epochms_from_format`. For a relative cutoff use the SQL-side family instead (`epochms_add_day`, `epochms_sub_month`, `epochms_year`, …) or compute it in an earlier `s.set_var` and `ref()` that. `export --strict` reports it; a bare `c.now()` operand is always fine.
|
|
37
|
+
- ⚠ The REQUEST-TIME timestamp filters have no SQL form and kill the request with a bare fatal naming nothing: `epochms_transform`, `epochms_add_ms`, `epochms_add_secs`, `epochms_date`, `epochms_from_format`. For a relative cutoff use the SQL-side family instead (`epochms_add_day`, `epochms_sub_month`, `epochms_year`, …) — by raw name, since `fl.*` has no builder for them — or compute it in an earlier `s.set_var` and `ref()` that. `export --strict` reports it; a bare `c.now()` operand is always fine.
|
|
38
38
|
- `bind: [{ table, as?, join?, where? }]` — joins (`context.bind[]`). `join` defaults to `"inner"`. `as` defaults to the table name; two joins to the same table need distinct aliases.
|
|
39
39
|
- ⚠ In `where`/`sort`/`eval` a JOINED column takes a dotted path (`col("team_row.id")`); THIS query's own columns stay **bare** (`col("team")`). Qualifying your own by table name needs `tableAlias` (same rule as `aggregate`) — without it the engine reads the operand as text and 400s `ParseError: Invalid value for param` naming the OTHER operand, so it throws at export instead.
|
|
40
40
|
- `bind: [{ table: team, as: "team_row", join: "left", where: expr(col("team"), "=", col("team_row.id")) }]`
|
|
41
|
+
- ⚠ A join does NOT put the joined table's columns on the returned row — with or without a `bind`, a row is the QUERIED table's columns, which is what `InferResponse` types. There is no `row.team_row`. To read a joined column, PROJECT it with an `eval` whose `name` is the dotted path: `eval: [{ name: "team_row.name", as: "team_name" }]` puts `team_name` on the row and on the inferred type. A bare `name` there is `Unsupported parameter reference` at runtime (it qualifies to the base table), and a dotted joined column in `output` is dropped with no error.
|
|
41
42
|
- `returnType` — `"list"` (default) | `"single"` | `"count"` | `"exists"` | `"stream"` | `"aggregate"`. Drives `context.return.type` AND the `InferResponse` shape: `count`→`number`, `exists`→`boolean`, `single`→`Row|null`, `stream`→`Row[]` (pageable, no envelope), `list`→`Row[]`/envelope, `aggregate`→rows keyed by the `aggregate.group`/`eval` aliases. ⚠ A bare `count` of ZERO serializes as an EMPTY body, not `0` — a client parsing JSON gets a parse error on the one result it most needs to handle. Wrap it: `response: { count: ref("n") }`.
|
|
42
43
|
- `eval: [{ name, as, filters? }]` — computed columns (`context.eval[]`). Each `as` grafts onto the row as an `unknown` key in `InferResponse`; shadowing a real column throws. Write `name` **bare** (`"embedding"`) — it is alias-qualified on emit exactly like `aggregate` (a bare eval name is `Unsupported param format` at runtime), and the statement declares the alias it used. An `as` alias is `sort`able in the SAME query.
|
|
43
|
-
- An `eval`/`sort`/`where` filter pipeline compiles to **SQL**, so it resolves a DIFFERENT registry than `fl.*` (which runs in the request): the vector family, geo `distance`/`within`/`covers`, `search_rank`, the aggregators.
|
|
44
|
+
- An `eval`/`sort`/`where` filter pipeline compiles to **SQL**, so it resolves a DIFFERENT registry than `fl.*` (which runs in the request): the vector family, geo `distance`/`within`/`covers`, `search_rank`, the aggregators. ⚠ `fl.*` carries NO builder for these — reach them by raw name, `filter("epochms_add_day", …)` or the `{ name, arg }` form. The name lists are `QUERY_EXPRESSION_FILTERS`/`VECTOR_FILTERS` on `@xanots/sdk/internal`.
|
|
44
45
|
- **Vector similarity search** — the ONLY way to query an `f.vector` column (no `SearchOp` does distance). `eval: [{ name: "embedding", as: "distance", filters: [{ name: "vector_cos_distance", arg: [inp("q")] }] }]` + `sort: [{ sortBy: "distance", dir: "asc" }]` ranks in the DATABASE over the column's index. Match the filter to the index `op` (`vector_cos_distance`↔`vector_cosine_ops`, `vector_l2_distance`↔`vector_l2_ops`, `vector_l1_distance`↔`vector_l1_ops`, `vector_inner_product`↔`vector_ip_ops`); `vector_cos_similarity` is the inverse, so sort it `desc`. The same filter on a `where` operand cuts off BY distance instead of by row count.
|
|
45
46
|
- `aggregate: { group?, eval?, sort?, paging? }` (with `returnType:"aggregate"`) builds `context.return.aggregate`. `group`/`eval` are `{ name, as, filters? }`, an aggregator like `sum`/`count` riding `filters`. Some aggregators resolve ONLY here, not in a runtime value pipeline: `count_distinct`, `median`, `to_list`/`to_distinct_list` (each with `_asc`/`_desc`), and `vector_distance`.
|
|
46
47
|
- ⚠ Write each `name` as a **bare** column (`"status"`). It is alias-qualified to `"<alias>.status"` on emit — the engine rejects an unqualified column in an aggregate with `Unsupported param format`. An already-dotted `name` (a `bind`ed/joined column) passes through.
|
package/llms/values.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- `c.array(a: Json[]) => Value` — Array constant (JSON string) → tag "const:array". Plain JSON literals only — a nested tagged value is rejected, same as c.obj.
|
|
13
13
|
- `c.expression(source: string) => Value` — Xano Expression Engine source, passed through VERBATIM → tag "const:expr2". The string IS the expression: c.expression('"Hi, " ~ $input.name'), c.expression("$var.price * $var.qty"). ⚠️ NOT VALIDATED — never parsed or type-checked, invisible to InferResponse, and untouched by a rename that updates every typed ref(); a typo surfaces at runtime or as a wrong answer. Use it ONLY for syntax the typed surfaces cannot express (~ concatenation, inline arithmetic, conditionals) — prefer ref/inp/col, withFilters+fl.*, and obj() (which BUILDS a checked expression). Not the expr() condition builder.
|
|
14
14
|
- `c.now() => Value` — Current time as epoch-ms — the engine-native const:epochms constant (no filter). Valid inline as a where/cmp operand. For cutoff math (cutoff = now - max_age) either compare inline or, for reuse/readability, hoist it into an s.set_var and compare against the var.
|
|
15
|
-
- `obj(fields: Record<string, Value | nested>) => ObjValue<typeof fields>` — Dynamic object value → tag "const:expr2" (an object-literal expression string). The dynamic sibling of c.obj: members may be inp/ref/auth/col values, env()/setting()/sys.*, c.now(), c.* constants, nested records, or arrays — and each member may carry a FILTER CHAIN (withFilters + fl.*), which renders as the expression pipe `$var.row|get:"a.b"`. That matters most for the null-safe drill: db.get binds null on a miss, so ref(path, { safe: true }) inside an obj() is the normal shape, not a workaround — you do NOT need a preceding s.set_var to hoist it. The member record rides the return type, so InferResponse resolves each member the way it resolves a top-level response key — `response: { user: obj({ id: ref("row.id") }) }` derives `{ user: { id: Col | null } }`, and a raw nested object literal (`response: { user: { id: ref("row.id") } }`, auto-wrapped through this) derives the same. A constant record or list has two spellings that both work and render identically: bare (`{ a: 1 }`, `[]`) or `c.obj(...)`/`c.array([...])` —
|
|
15
|
+
- `obj(fields: Record<string, Value | nested>) => ObjValue<typeof fields>` — Dynamic object value → tag "const:expr2" (an object-literal expression string). The dynamic sibling of c.obj: members may be inp/ref/auth/col values, env()/setting()/sys.*, c.now(), c.* constants, nested records, or arrays — and each member may carry a FILTER CHAIN (withFilters + fl.*), which renders as the expression pipe `$var.row|get:"a.b"`. That matters most for the null-safe drill: db.get binds null on a miss, so ref(path, { safe: true }) inside an obj() is the normal shape, not a workaround — you do NOT need a preceding s.set_var to hoist it. The member record rides the return type, so InferResponse resolves each member the way it resolves a top-level response key — `response: { user: obj({ id: ref("row.id") }) }` derives `{ user: { id: Col | null } }`, and a raw nested object literal (`response: { user: { id: ref("row.id") } }`, auto-wrapped through this) derives the same. A constant record or list has two spellings that both work and render identically: bare (`{ a: 1 }`, `[]`) or `c.obj(...)`/`c.array([...])` — prefer the bare form. NEST WITH A RAW RECORD, not an inner obj() call: an inner call yields a const:expr2 value, which the expression serializer has no spelling for and THROWS. The legacy blank `c.obj(null)` is refused here (it evaluates to null, not {}) — write c.null() or c.obj(). Still rejected: a filter ARGUMENT that carries its own chain or is a c.now() (a trailing | binds to the whole value, not one argument, and c.now() needs one), a DISABLED filter (an expression string cannot record that), and the output/response/toolset/reg tags — build those in a prior step and ref() them. Use for e.g. s.ai.agent.run args.
|
|
16
16
|
- `ref(name: string, opts?: { safe?: boolean }) => Value` — Reference a stack variable → tag "var". Pass { safe: true } for null-safe nested access — a dotted ref("owner.user_id", { safe: true }) compiles through the get filter so it resolves to null instead of raising "Unable to locate var" when the base is null.
|
|
17
17
|
- `inp(name: string) => Value` — Reference a function/endpoint input → tag "input". Resolves ONLY against the `input` block of the def it sits in — a value produced earlier in the stack is `ref("var.field")`, not `inp("field")`. A name that is not declared here deploys clean and fails at runtime with ERROR_FATAL "Unable to locate input: <name>" on every branch that reads it; `export()` warns, and `--strict` fails the build. Sending the name in the request does NOT rescue it — an undeclared input is never bound, so the call fails identically with the value present. A dotted path drills INTO a declared input (`inp("action.amount")` needs a declared `action`).
|
|
18
18
|
- `col(name: string) => Value` — Reference a table column → tag "col".
|
package/llms-full.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# xanots v0.0.
|
|
1
|
+
# xanots v0.0.12
|
|
2
2
|
|
|
3
3
|
> TypeScript SDK that compiles a typed Xano workspace into the importable packageExport JSON bundle.
|
|
4
4
|
|
|
@@ -16,27 +16,26 @@ caller, `c.*` a constant — resolved at request time; JS operators over them do
|
|
|
16
16
|
compute (see Gotchas). Requests share no memory — state persists in tables or redis.
|
|
17
17
|
|
|
18
18
|
Coverage: object kinds 25/31, statement surfaces 214/214, filters 226 (226 typed).
|
|
19
|
-
Not authorable here: branch, market_item, realtime_channel, run.job, run.service, tablemap — these cannot be authored and do not survive a pull;
|
|
19
|
+
Not authorable here: branch, market_item, realtime_channel, run.job, run.service, tablemap — these cannot be authored and do not survive a pull; reasons in `coverage.objectKinds.unmodeled`.
|
|
20
20
|
|
|
21
21
|
This file is the whole always-read surface: the mental model, the deploy contract,
|
|
22
22
|
every gotcha, and control flow. Per-surface detail lives in the topic files listed
|
|
23
|
-
below — open the one whose condition matches the task,
|
|
24
|
-
exhaustive per-entry detail in NEITHER — a statement's
|
|
25
|
-
defaults, a filter's
|
|
26
|
-
|
|
27
|
-
`@xanots/sdk/manifest.json`,
|
|
28
|
-
|
|
29
|
-
it is ~65k tokens, so never read it whole). Its top-level keys are `name`, `version`, `description`, `coverage`, `values`, `objectKinds`, `fieldTypes`, `statements`, `filters`, `cli`, `cliGlobalFlags`. `statements` and `filters` are ARRAYS, not maps — SELECT, do not index:
|
|
23
|
+
below — open the one whose condition matches the task, skip the rest. For
|
|
24
|
+
exhaustive per-entry detail in NEITHER — a statement's field schema with engine
|
|
25
|
+
defaults, a filter's full argument list, the `storedName` mapping — do a TARGETED
|
|
26
|
+
lookup in the shipped `manifest.json` (a program imports it as
|
|
27
|
+
`@xanots/sdk/manifest.json`, needing `with { type: "json" }` in Node ESM;
|
|
28
|
+
it is ~60k tokens, so never read it whole). Its top-level keys are `name`, `version`, `description`, `coverage`, `values`, `objectKinds`, `fieldTypes`, `statements`, `filters`, `cli`, `cliGlobalFlags`. `statements` and `filters` are ARRAYS, not maps — SELECT, do not index:
|
|
30
29
|
jq '.statements[] | select(.sPath=="db.get")' manifest.json
|
|
31
30
|
jq '.filters[] | select(.name=="json_decode")' manifest.json
|
|
31
|
+
⚠ `fields` is null on the 65 `declarative: false` statements (`db.get`, …): typed wrappers whose arguments are the factory's `.d.ts` signature. Null ≠ missing.
|
|
32
32
|
Select a statement on `sPath` (the `s.*` path you write), NOT `surface` (the XanoScript term): 24 of 214 differ — `var`→`set_var`, `break`→`foreach_break`, `foreach.remove`→`foreach_remove`, and every `expect.*`.
|
|
33
33
|
|
|
34
34
|
## Topic files
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
`llms-full.txt` is everything concatenated — one fetch, for a reader that cannot open files.
|
|
36
|
+
Paths are relative to this file (`node_modules/@xanots/sdk/` once installed), so a
|
|
37
|
+
plain file read resolves them at the version you have.
|
|
38
|
+
`llms-full.txt` is everything concatenated — one fetch for a reader that cannot open files.
|
|
40
39
|
|
|
41
40
|
- [Object kinds](llms/object-kinds.md): Read when deciding WHAT to build — every authorable primitive (tables, endpoints, functions, tasks, agents, MCP, realtime, microservices, …), what each is, and which factory + register method build it. Read it too when a `register*` call will not typecheck because the array was built by `.flatMap()`/`.concat()` across modules.
|
|
42
41
|
- [Core def shapes](llms/kinds-core.md): Read when authoring a function, query, api group, task, workflow test, middleware, or tool — and for the `response` and `expr` shapes every one of them uses.
|
|
@@ -336,9 +335,9 @@ Non-obvious authoring rules:
|
|
|
336
335
|
no Node built-ins, so a bundler drops unused SDK exports. But importing a **def** for its
|
|
337
336
|
`getPath()`/`verb`/`getUrl()`/`getChannel()` also pulls whatever its `stack` references:
|
|
338
337
|
the `s.*`/`c.*` factory CALLS run at module load to BUILD it. Types are free.
|
|
339
|
-
⚠ A FLOOR — **~
|
|
338
|
+
⚠ A FLOOR — **~267 kB minified (~65 kB gzipped)** for the FIRST def; splitting modules
|
|
340
339
|
never removes it. The floor is the RUNTIME, not the def: a second def, or a much richer
|
|
341
|
-
one, adds ~
|
|
340
|
+
one, adds ~2 kB — so reducing what a def does will not reduce it.
|
|
342
341
|
Fix: `xanots routes <entry> --emit xano/routes.gen.ts` (`paths` is an accepted alias) — verbs, paths, and sockets as
|
|
343
342
|
plain data importing NOTHING, still compile-checked: `routePath("blog/{slug}", { slug })`,
|
|
344
343
|
`channelPath("rooms/{room_id}", { room_id })`, `socketUrl("chat", baseUrl)` (tenant base
|
|
@@ -815,13 +814,14 @@ primary key `id`):
|
|
|
815
814
|
- ⚠ `like`/`ilike` take the operand as the PATTERN, verbatim: a bare term matches only an exact whole-string equal, and the endpoint answers HTTP 200 with zero rows — nothing reports a problem, so a search box that matches nothing ships. For substring matching use `includes`/`not includes`, which wrap the operand in `%…%` themselves and match case-INSENSITIVELY. Prefer them over a hand-built `"%" + term + "%"`, which is non-empty even for an empty term and so defeats `ignoreEmpty`; `includes` composes with it. `contains`/`@>`/`overlaps` are JSON/array containment, not text — on a text column they 400 `ParseError: Invalid value for param`.
|
|
816
815
|
- Compose nested boolean logic with `and(...)` / `or(...)` groups (also available on `addon()` `where`).
|
|
817
816
|
- An operand may be a bare value (`col`/`inp`/`ref`/`auth`/`c.*`) OR a **filtered** value (`withFilters(...)`) inline — the engine compiles the string and arithmetic filters (`trim`, `concat`, `upper`, `lower`, …) into the SQL.
|
|
818
|
-
- ⚠ The REQUEST-TIME timestamp filters have no SQL form and kill the request with a bare fatal naming nothing: `epochms_transform`, `epochms_add_ms`, `epochms_add_secs`, `epochms_date`, `epochms_from_format`. For a relative cutoff use the SQL-side family instead (`epochms_add_day`, `epochms_sub_month`, `epochms_year`, …) or compute it in an earlier `s.set_var` and `ref()` that. `export --strict` reports it; a bare `c.now()` operand is always fine.
|
|
817
|
+
- ⚠ The REQUEST-TIME timestamp filters have no SQL form and kill the request with a bare fatal naming nothing: `epochms_transform`, `epochms_add_ms`, `epochms_add_secs`, `epochms_date`, `epochms_from_format`. For a relative cutoff use the SQL-side family instead (`epochms_add_day`, `epochms_sub_month`, `epochms_year`, …) — by raw name, since `fl.*` has no builder for them — or compute it in an earlier `s.set_var` and `ref()` that. `export --strict` reports it; a bare `c.now()` operand is always fine.
|
|
819
818
|
- `bind: [{ table, as?, join?, where? }]` — joins (`context.bind[]`). `join` defaults to `"inner"`. `as` defaults to the table name; two joins to the same table need distinct aliases.
|
|
820
819
|
- ⚠ In `where`/`sort`/`eval` a JOINED column takes a dotted path (`col("team_row.id")`); THIS query's own columns stay **bare** (`col("team")`). Qualifying your own by table name needs `tableAlias` (same rule as `aggregate`) — without it the engine reads the operand as text and 400s `ParseError: Invalid value for param` naming the OTHER operand, so it throws at export instead.
|
|
821
820
|
- `bind: [{ table: team, as: "team_row", join: "left", where: expr(col("team"), "=", col("team_row.id")) }]`
|
|
821
|
+
- ⚠ A join does NOT put the joined table's columns on the returned row — with or without a `bind`, a row is the QUERIED table's columns, which is what `InferResponse` types. There is no `row.team_row`. To read a joined column, PROJECT it with an `eval` whose `name` is the dotted path: `eval: [{ name: "team_row.name", as: "team_name" }]` puts `team_name` on the row and on the inferred type. A bare `name` there is `Unsupported parameter reference` at runtime (it qualifies to the base table), and a dotted joined column in `output` is dropped with no error.
|
|
822
822
|
- `returnType` — `"list"` (default) | `"single"` | `"count"` | `"exists"` | `"stream"` | `"aggregate"`. Drives `context.return.type` AND the `InferResponse` shape: `count`→`number`, `exists`→`boolean`, `single`→`Row|null`, `stream`→`Row[]` (pageable, no envelope), `list`→`Row[]`/envelope, `aggregate`→rows keyed by the `aggregate.group`/`eval` aliases. ⚠ A bare `count` of ZERO serializes as an EMPTY body, not `0` — a client parsing JSON gets a parse error on the one result it most needs to handle. Wrap it: `response: { count: ref("n") }`.
|
|
823
823
|
- `eval: [{ name, as, filters? }]` — computed columns (`context.eval[]`). Each `as` grafts onto the row as an `unknown` key in `InferResponse`; shadowing a real column throws. Write `name` **bare** (`"embedding"`) — it is alias-qualified on emit exactly like `aggregate` (a bare eval name is `Unsupported param format` at runtime), and the statement declares the alias it used. An `as` alias is `sort`able in the SAME query.
|
|
824
|
-
- An `eval`/`sort`/`where` filter pipeline compiles to **SQL**, so it resolves a DIFFERENT registry than `fl.*` (which runs in the request): the vector family, geo `distance`/`within`/`covers`, `search_rank`, the aggregators.
|
|
824
|
+
- An `eval`/`sort`/`where` filter pipeline compiles to **SQL**, so it resolves a DIFFERENT registry than `fl.*` (which runs in the request): the vector family, geo `distance`/`within`/`covers`, `search_rank`, the aggregators. ⚠ `fl.*` carries NO builder for these — reach them by raw name, `filter("epochms_add_day", …)` or the `{ name, arg }` form. The name lists are `QUERY_EXPRESSION_FILTERS`/`VECTOR_FILTERS` on `@xanots/sdk/internal`.
|
|
825
825
|
- **Vector similarity search** — the ONLY way to query an `f.vector` column (no `SearchOp` does distance). `eval: [{ name: "embedding", as: "distance", filters: [{ name: "vector_cos_distance", arg: [inp("q")] }] }]` + `sort: [{ sortBy: "distance", dir: "asc" }]` ranks in the DATABASE over the column's index. Match the filter to the index `op` (`vector_cos_distance`↔`vector_cosine_ops`, `vector_l2_distance`↔`vector_l2_ops`, `vector_l1_distance`↔`vector_l1_ops`, `vector_inner_product`↔`vector_ip_ops`); `vector_cos_similarity` is the inverse, so sort it `desc`. The same filter on a `where` operand cuts off BY distance instead of by row count.
|
|
826
826
|
- `aggregate: { group?, eval?, sort?, paging? }` (with `returnType:"aggregate"`) builds `context.return.aggregate`. `group`/`eval` are `{ name, as, filters? }`, an aggregator like `sum`/`count` riding `filters`. Some aggregators resolve ONLY here, not in a runtime value pipeline: `count_distinct`, `median`, `to_list`/`to_distinct_list` (each with `_asc`/`_desc`), and `vector_distance`.
|
|
827
827
|
- ⚠ Write each `name` as a **bare** column (`"status"`). It is alias-qualified to `"<alias>.status"` on emit — the engine rejects an unqualified column in an aggregate with `Unsupported param format`. An already-dotted `name` (a `bind`ed/joined column) passes through.
|
|
@@ -939,7 +939,7 @@ Microservices (the `microservice()` def and the statement that calls it):
|
|
|
939
939
|
- `c.array(a: Json[]) => Value` — Array constant (JSON string) → tag "const:array". Plain JSON literals only — a nested tagged value is rejected, same as c.obj.
|
|
940
940
|
- `c.expression(source: string) => Value` — Xano Expression Engine source, passed through VERBATIM → tag "const:expr2". The string IS the expression: c.expression('"Hi, " ~ $input.name'), c.expression("$var.price * $var.qty"). ⚠️ NOT VALIDATED — never parsed or type-checked, invisible to InferResponse, and untouched by a rename that updates every typed ref(); a typo surfaces at runtime or as a wrong answer. Use it ONLY for syntax the typed surfaces cannot express (~ concatenation, inline arithmetic, conditionals) — prefer ref/inp/col, withFilters+fl.*, and obj() (which BUILDS a checked expression). Not the expr() condition builder.
|
|
941
941
|
- `c.now() => Value` — Current time as epoch-ms — the engine-native const:epochms constant (no filter). Valid inline as a where/cmp operand. For cutoff math (cutoff = now - max_age) either compare inline or, for reuse/readability, hoist it into an s.set_var and compare against the var.
|
|
942
|
-
- `obj(fields: Record<string, Value | nested>) => ObjValue<typeof fields>` — Dynamic object value → tag "const:expr2" (an object-literal expression string). The dynamic sibling of c.obj: members may be inp/ref/auth/col values, env()/setting()/sys.*, c.now(), c.* constants, nested records, or arrays — and each member may carry a FILTER CHAIN (withFilters + fl.*), which renders as the expression pipe `$var.row|get:"a.b"`. That matters most for the null-safe drill: db.get binds null on a miss, so ref(path, { safe: true }) inside an obj() is the normal shape, not a workaround — you do NOT need a preceding s.set_var to hoist it. The member record rides the return type, so InferResponse resolves each member the way it resolves a top-level response key — `response: { user: obj({ id: ref("row.id") }) }` derives `{ user: { id: Col | null } }`, and a raw nested object literal (`response: { user: { id: ref("row.id") } }`, auto-wrapped through this) derives the same. A constant record or list has two spellings that both work and render identically: bare (`{ a: 1 }`, `[]`) or `c.obj(...)`/`c.array([...])` —
|
|
942
|
+
- `obj(fields: Record<string, Value | nested>) => ObjValue<typeof fields>` — Dynamic object value → tag "const:expr2" (an object-literal expression string). The dynamic sibling of c.obj: members may be inp/ref/auth/col values, env()/setting()/sys.*, c.now(), c.* constants, nested records, or arrays — and each member may carry a FILTER CHAIN (withFilters + fl.*), which renders as the expression pipe `$var.row|get:"a.b"`. That matters most for the null-safe drill: db.get binds null on a miss, so ref(path, { safe: true }) inside an obj() is the normal shape, not a workaround — you do NOT need a preceding s.set_var to hoist it. The member record rides the return type, so InferResponse resolves each member the way it resolves a top-level response key — `response: { user: obj({ id: ref("row.id") }) }` derives `{ user: { id: Col | null } }`, and a raw nested object literal (`response: { user: { id: ref("row.id") } }`, auto-wrapped through this) derives the same. A constant record or list has two spellings that both work and render identically: bare (`{ a: 1 }`, `[]`) or `c.obj(...)`/`c.array([...])` — prefer the bare form. NEST WITH A RAW RECORD, not an inner obj() call: an inner call yields a const:expr2 value, which the expression serializer has no spelling for and THROWS. The legacy blank `c.obj(null)` is refused here (it evaluates to null, not {}) — write c.null() or c.obj(). Still rejected: a filter ARGUMENT that carries its own chain or is a c.now() (a trailing | binds to the whole value, not one argument, and c.now() needs one), a DISABLED filter (an expression string cannot record that), and the output/response/toolset/reg tags — build those in a prior step and ref() them. Use for e.g. s.ai.agent.run args.
|
|
943
943
|
- `ref(name: string, opts?: { safe?: boolean }) => Value` — Reference a stack variable → tag "var". Pass { safe: true } for null-safe nested access — a dotted ref("owner.user_id", { safe: true }) compiles through the get filter so it resolves to null instead of raising "Unable to locate var" when the base is null.
|
|
944
944
|
- `inp(name: string) => Value` — Reference a function/endpoint input → tag "input". Resolves ONLY against the `input` block of the def it sits in — a value produced earlier in the stack is `ref("var.field")`, not `inp("field")`. A name that is not declared here deploys clean and fails at runtime with ERROR_FATAL "Unable to locate input: <name>" on every branch that reads it; `export()` warns, and `--strict` fails the build. Sending the name in the request does NOT rescue it — an undeclared input is never bound, so the call fails identically with the value present. A dotted path drills INTO a declared input (`inp("action.amount")` needs a declared `action`).
|
|
945
945
|
- `col(name: string) => Value` — Reference a table column → tag "col".
|