@xanots/sdk 0.0.1
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/LICENSE +21 -0
- package/README.md +384 -0
- package/dist/agent-file-refresh-7XCACU34.js +95 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +184 -0
- package/dist/capture-HUV5BNTC.js +32 -0
- package/dist/chunk-26CNBZE2.js +963 -0
- package/dist/chunk-33ND6LKO.js +78 -0
- package/dist/chunk-3DUAGYGP.js +1550 -0
- package/dist/chunk-3IGNIP6R.js +89 -0
- package/dist/chunk-47WDWMBJ.js +14 -0
- package/dist/chunk-4BXJGVZ3.js +318 -0
- package/dist/chunk-4HT3BNZ7.js +675 -0
- package/dist/chunk-4YMD2OOZ.js +23 -0
- package/dist/chunk-5WGEURVI.js +156 -0
- package/dist/chunk-5YTBTF25.js +29 -0
- package/dist/chunk-6DHBYBTO.js +166 -0
- package/dist/chunk-76QBEIGO.js +66 -0
- package/dist/chunk-7DKX2SPN.js +100 -0
- package/dist/chunk-7REDODS2.js +468 -0
- package/dist/chunk-C56BC2FY.js +254 -0
- package/dist/chunk-DGSF2Q5H.js +60 -0
- package/dist/chunk-EMCAWMHC.js +3982 -0
- package/dist/chunk-EZG76F7R.js +159 -0
- package/dist/chunk-FJ6ZS5KB.js +3607 -0
- package/dist/chunk-GNPVYOPB.js +17 -0
- package/dist/chunk-HJPTWBLH.js +38 -0
- package/dist/chunk-IZW6MK3U.js +248 -0
- package/dist/chunk-LH6Z6KFV.js +247 -0
- package/dist/chunk-MZUI3JNX.js +93 -0
- package/dist/chunk-NDZFBZHC.js +167 -0
- package/dist/chunk-NOO35BQ2.js +37 -0
- package/dist/chunk-NUQCEOKA.js +13 -0
- package/dist/chunk-QTNO2WD6.js +56 -0
- package/dist/chunk-RVB7ITKN.js +49 -0
- package/dist/chunk-TRU5OHQ5.js +6904 -0
- package/dist/chunk-VKFQJEMW.js +814 -0
- package/dist/chunk-VTIL47DT.js +125 -0
- package/dist/chunk-WHOJWOSV.js +112 -0
- package/dist/chunk-WKP4D5MO.js +1692 -0
- package/dist/chunk-X4DVXBFY.js +38 -0
- package/dist/chunk-XQ22GLYS.js +118 -0
- package/dist/chunk-YDZYMZBE.js +670 -0
- package/dist/chunk-YGPIY3WT.js +1146 -0
- package/dist/chunk-YYRDLFSJ.js +602 -0
- package/dist/chunk-ZUTSMMAG.js +46 -0
- package/dist/cli.d.ts +331 -0
- package/dist/cli.js +33 -0
- package/dist/codegen-command-OOKK6AZK.js +40 -0
- package/dist/codegen.d.ts +114 -0
- package/dist/codegen.js +71 -0
- package/dist/completion-TIPPZN7H.js +298 -0
- package/dist/config-NL33PN4D.js +84 -0
- package/dist/deploy-command-4EQ7XX5G.js +451 -0
- package/dist/env-target-IFJ5QDHC.js +16 -0
- package/dist/ephemeral-command-3X3I4ULC.js +242 -0
- package/dist/index.d.ts +4477 -0
- package/dist/index.js +300 -0
- package/dist/init-command-R6H7ZXW5.js +27 -0
- package/dist/internal.d.ts +74 -0
- package/dist/internal.js +2065 -0
- package/dist/io-AMIKRLPC.js +11 -0
- package/dist/live-diff-Y3AKETHE.js +196 -0
- package/dist/lock-3CVKALKT.js +37 -0
- package/dist/lock-commands-XTR2YKLY.js +232 -0
- package/dist/login-command-ACJF6KWQ.js +167 -0
- package/dist/logout-command-MX3MJS5U.js +68 -0
- package/dist/loop-OM3P2Z6O.js +93 -0
- package/dist/marketplace-command-RATXTKXN.js +379 -0
- package/dist/meta-client-OW5WKWW7.js +170 -0
- package/dist/node.d.ts +59 -0
- package/dist/node.js +442 -0
- package/dist/profile-command-SWJ3SPKR.js +77 -0
- package/dist/release-command-VV5UVZKL.js +427 -0
- package/dist/response-CVAE2kMj.d.ts +1915 -0
- package/dist/routes-manifest-SP3ZXLMR.js +244 -0
- package/dist/runtime-V4C3AC3A.js +13 -0
- package/dist/sandbox-details-command-HJE5SPVG.js +18 -0
- package/dist/sandbox-export-command-QCJY4GMV.js +24 -0
- package/dist/static-host-D6KS7X45.js +138 -0
- package/dist/store-BQ97nKIF.d.ts +7760 -0
- package/dist/test-command-TSPBQHBM.js +383 -0
- package/dist/validate-command-4ERHJSCO.js +122 -0
- package/dist/verify-rollout-FAVQ6EJY.js +40 -0
- package/dist/workspace-K72NP7SX.js +49 -0
- package/dist/workspace-command-OF23DPNI.js +156 -0
- package/dist/workspace-export-AJMGN3CQ.js +10 -0
- package/llms/fields.md +104 -0
- package/llms/filters.md +183 -0
- package/llms/kinds-agent-mcp.md +11 -0
- package/llms/kinds-core.md +68 -0
- package/llms/kinds-realtime.md +76 -0
- package/llms/lambda.md +111 -0
- package/llms/legacy.md +43 -0
- package/llms/object-kinds.md +31 -0
- package/llms/statements-calls.md +23 -0
- package/llms/statements-catalog.md +309 -0
- package/llms/statements-data.md +58 -0
- package/llms/statements-runtime.md +48 -0
- package/llms/tests.md +58 -0
- package/llms/triggers.md +22 -0
- package/llms/values.md +29 -0
- package/llms-full.txt +1672 -0
- package/llms.txt +483 -0
- package/manifest.json +9592 -0
- package/package.json +110 -0
|
@@ -0,0 +1,963 @@
|
|
|
1
|
+
// src/validate/normalize.ts
|
|
2
|
+
var STRIP_KEYS = /* @__PURE__ */ new Set([
|
|
3
|
+
// server columns / persistence
|
|
4
|
+
"id",
|
|
5
|
+
"created_at",
|
|
6
|
+
"updated_at",
|
|
7
|
+
"deleted_at",
|
|
8
|
+
"guid",
|
|
9
|
+
"_draft",
|
|
10
|
+
// auto-generated xsids / runtime keys
|
|
11
|
+
"_xsid",
|
|
12
|
+
"@guid",
|
|
13
|
+
"@index",
|
|
14
|
+
"stack_id",
|
|
15
|
+
"index",
|
|
16
|
+
// deploy-target / market binding (deferred)
|
|
17
|
+
"workspace",
|
|
18
|
+
"branch",
|
|
19
|
+
"market_item",
|
|
20
|
+
// engine-stored source artifact (the raw XanoScript text), not authored data
|
|
21
|
+
"xanoscript",
|
|
22
|
+
// A workflow test's LAST RUN result — when it ran, how long it took, and the
|
|
23
|
+
// pass/fail of each statement. Instance state produced by executing the test,
|
|
24
|
+
// not workspace source, so the SDK neither authors nor emits it. A stored
|
|
25
|
+
// object always carries the key (`null` until the test has been run once), so
|
|
26
|
+
// without this the whole kind reads as a failed round trip.
|
|
27
|
+
"lastRun",
|
|
28
|
+
// storage-mode flag the golden table corpus predates (those fixtures were
|
|
29
|
+
// captured before `use_xdo` was serialized). The SDK always emits it; it
|
|
30
|
+
// doesn't change the authored schema, so drop it from both sides — same as
|
|
31
|
+
// the already-stripped `index`, whose gin entry `use_xdo` only gates.
|
|
32
|
+
"use_xdo",
|
|
33
|
+
// A dead key, dropped for the same reason as `iterator` (see
|
|
34
|
+
// {@link returnWithoutIterator}) and traced the same way.
|
|
35
|
+
//
|
|
36
|
+
// Xano's frontend writes `context: {dbo:{id}, allow_notfound: true}` from ONE
|
|
37
|
+
// place — the auth scaffold generator that builds a signup/login query. No
|
|
38
|
+
// engine class reads it: the `mvp` get-by class consults `dbo`, `output`, and
|
|
39
|
+
// its args and nothing else, and the older non-MVP class reads a differently
|
|
40
|
+
// named `allow_null`. So the member cannot change what a query does.
|
|
41
|
+
//
|
|
42
|
+
// It is deliberately NOT modelled — an authoring surface for a key the engine
|
|
43
|
+
// ignores is the mistake this file already refuses to make elsewhere. But the
|
|
44
|
+
// exclusion was only half-done: the encoder correctly declined to write it
|
|
45
|
+
// while the comparison still demanded it, so a correct decision read as a
|
|
46
|
+
// round-trip failure and sent every scaffolded auth query to `raw()`.
|
|
47
|
+
"allow_notfound"
|
|
48
|
+
]);
|
|
49
|
+
function isEmptyObject(v) {
|
|
50
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0;
|
|
51
|
+
}
|
|
52
|
+
function isEmptyCustomize(v) {
|
|
53
|
+
return v === "" || isEmptyObject(v) || isEmptyArray(v);
|
|
54
|
+
}
|
|
55
|
+
function isSavedTest(value) {
|
|
56
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
57
|
+
const rec = value;
|
|
58
|
+
return typeof rec.name === "string" && Array.isArray(rec.expect);
|
|
59
|
+
}
|
|
60
|
+
function isEmptyArray(v) {
|
|
61
|
+
return Array.isArray(v) && v.length === 0;
|
|
62
|
+
}
|
|
63
|
+
function isNumber(v, n) {
|
|
64
|
+
return v === n || typeof v === "string" && v !== "" && Number(v) === n;
|
|
65
|
+
}
|
|
66
|
+
function deepEqual(a, b) {
|
|
67
|
+
if (a === b) return true;
|
|
68
|
+
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
|
|
69
|
+
const aArr = Array.isArray(a);
|
|
70
|
+
if (aArr !== Array.isArray(b)) return false;
|
|
71
|
+
if (aArr) {
|
|
72
|
+
const x2 = a;
|
|
73
|
+
const y2 = b;
|
|
74
|
+
return x2.length === y2.length && x2.every((v, i) => deepEqual(v, y2[i]));
|
|
75
|
+
}
|
|
76
|
+
const x = a;
|
|
77
|
+
const y = b;
|
|
78
|
+
const keys = Object.keys(x);
|
|
79
|
+
return keys.length === Object.keys(y).length && keys.every((k) => deepEqual(x[k], y[k]));
|
|
80
|
+
}
|
|
81
|
+
var TIMESTAMP_RE = /^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2}:\d{2})(?:\.\d+)?(Z|z|[+-]\d{2}:?\d{2})?$/;
|
|
82
|
+
function canonicalizeTimestamp(s) {
|
|
83
|
+
const m = TIMESTAMP_RE.exec(s);
|
|
84
|
+
if (!m) return void 0;
|
|
85
|
+
let tz = m[3] ?? "Z";
|
|
86
|
+
if (tz === "z") tz = "Z";
|
|
87
|
+
if (/^[+-]\d{4}$/.test(tz)) tz = `${tz.slice(0, 3)}:${tz.slice(3)}`;
|
|
88
|
+
const d = /* @__PURE__ */ new Date(`${m[1]}T${m[2]}${tz}`);
|
|
89
|
+
return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
|
|
90
|
+
}
|
|
91
|
+
var DEFAULT_CONTEXT_RETURN = {
|
|
92
|
+
list: {
|
|
93
|
+
sort: [],
|
|
94
|
+
paging: { page: 1, offset: 0, totals: false, enabled: false, metadata: true, per_page: 25 },
|
|
95
|
+
distinct: "auto"
|
|
96
|
+
},
|
|
97
|
+
type: "list",
|
|
98
|
+
single: { sort: [] },
|
|
99
|
+
stream: { sort: [], paging: { page: 1, enabled: false, per_page: 25 }, distinct: "auto" },
|
|
100
|
+
aggregate: {
|
|
101
|
+
eval: [],
|
|
102
|
+
sort: [],
|
|
103
|
+
group: [],
|
|
104
|
+
index: [],
|
|
105
|
+
paging: { page: 1, enabled: false, metadata: true, per_page: 25 }
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var DEFAULT_RETURN_BLOCKS = {
|
|
109
|
+
list: DEFAULT_CONTEXT_RETURN.list,
|
|
110
|
+
single: DEFAULT_CONTEXT_RETURN.single,
|
|
111
|
+
stream: DEFAULT_CONTEXT_RETURN.stream,
|
|
112
|
+
aggregate: DEFAULT_CONTEXT_RETURN.aggregate
|
|
113
|
+
};
|
|
114
|
+
var MINIMAL_CONTEXT_RETURN = { type: "list" };
|
|
115
|
+
function hasNoListBounds(value) {
|
|
116
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
117
|
+
const entries = Object.entries(value);
|
|
118
|
+
if (entries.length === 0) return false;
|
|
119
|
+
return entries.every(
|
|
120
|
+
([member, bound]) => (member === "min" || member === "max") && (bound === "" || typeof bound === "object" && bound !== null && isEmptyObject(bound))
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
var PAGING_INT_KEYS = /* @__PURE__ */ new Set(["page", "per_page", "offset"]);
|
|
124
|
+
var UNREADABLE_INPUT = /* @__PURE__ */ new Set(["mvp:create_image"]);
|
|
125
|
+
function hasUnreadableInput(name) {
|
|
126
|
+
return typeof name === "string" && UNREADABLE_INPUT.has(name);
|
|
127
|
+
}
|
|
128
|
+
var RETIRED_INPUT = {
|
|
129
|
+
"mvp:datadog_log": ["ddtags"],
|
|
130
|
+
"mvp:datadog_metric": ["ddtags"]
|
|
131
|
+
};
|
|
132
|
+
function retiredInputs(name) {
|
|
133
|
+
return (typeof name === "string" ? RETIRED_INPUT[name] : void 0) ?? [];
|
|
134
|
+
}
|
|
135
|
+
var NAME_KEYED_INPUT = /* @__PURE__ */ new Set([
|
|
136
|
+
"mvp:create_auth",
|
|
137
|
+
"mvp:api_request",
|
|
138
|
+
"mvp:amazon_opensearch_document",
|
|
139
|
+
"mvp:elasticsearch_document"
|
|
140
|
+
]);
|
|
141
|
+
var EMPTY_SEARCH = { expression: [] };
|
|
142
|
+
function isBlankGroupStatement(v) {
|
|
143
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
|
|
144
|
+
const node = v;
|
|
145
|
+
if (node.op !== "=") return false;
|
|
146
|
+
const blankSide = (side) => side !== null && typeof side === "object" && side.tag === "const" && side.operand === "";
|
|
147
|
+
return blankSide(node.left) && blankSide(node.right);
|
|
148
|
+
}
|
|
149
|
+
var RETURN_BLOCKS = ["single", "list", "stream", "aggregate"];
|
|
150
|
+
var RETURN_TYPES = /* @__PURE__ */ new Set([...RETURN_BLOCKS, "count", "exists"]);
|
|
151
|
+
function liveReturnSection(value) {
|
|
152
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
153
|
+
const section = value;
|
|
154
|
+
const type = section.type;
|
|
155
|
+
if (typeof type !== "string" || !RETURN_TYPES.has(type)) return void 0;
|
|
156
|
+
const dead = RETURN_BLOCKS.filter((b) => b !== type && b in section);
|
|
157
|
+
if (dead.length === 0) return void 0;
|
|
158
|
+
const live = {};
|
|
159
|
+
for (const [k, v] of Object.entries(section)) {
|
|
160
|
+
if (!RETURN_BLOCKS.includes(k) || k === type) live[k] = v;
|
|
161
|
+
}
|
|
162
|
+
return live;
|
|
163
|
+
}
|
|
164
|
+
function unboundAuthTable(value) {
|
|
165
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
166
|
+
const block = value;
|
|
167
|
+
if (block.dbo_id !== 0 || !("row_id" in block)) return void 0;
|
|
168
|
+
const rest = { ...block };
|
|
169
|
+
delete rest["dbo_id"];
|
|
170
|
+
return rest;
|
|
171
|
+
}
|
|
172
|
+
function returnWithoutIterator(value) {
|
|
173
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
174
|
+
const section = value;
|
|
175
|
+
if (typeof section.type !== "string" || !RETURN_TYPES.has(section.type)) return void 0;
|
|
176
|
+
let found = false;
|
|
177
|
+
const out = {};
|
|
178
|
+
for (const [k, v] of Object.entries(section)) {
|
|
179
|
+
if (RETURN_BLOCKS.includes(k) && v !== null && typeof v === "object" && !Array.isArray(v) && "iterator" in v) {
|
|
180
|
+
found = true;
|
|
181
|
+
const rest = { ...v };
|
|
182
|
+
delete rest.iterator;
|
|
183
|
+
out[k] = rest;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
out[k] = v;
|
|
187
|
+
}
|
|
188
|
+
return found ? out : void 0;
|
|
189
|
+
}
|
|
190
|
+
var DEDICATED_RUNTIME_KEYS = ["cpu", "memory", "timeout", "max_retry"];
|
|
191
|
+
function liveAsyncRuntime(value) {
|
|
192
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
193
|
+
const block = value;
|
|
194
|
+
const mode = block.mode;
|
|
195
|
+
if (typeof mode !== "string") return void 0;
|
|
196
|
+
if (mode === "disabled") return {};
|
|
197
|
+
if (mode !== "async-shared") return void 0;
|
|
198
|
+
if (!DEDICATED_RUNTIME_KEYS.some((k) => k in block)) return void 0;
|
|
199
|
+
const live = {};
|
|
200
|
+
for (const [k, v] of Object.entries(block)) {
|
|
201
|
+
if (!DEDICATED_RUNTIME_KEYS.includes(k)) live[k] = v;
|
|
202
|
+
}
|
|
203
|
+
return live;
|
|
204
|
+
}
|
|
205
|
+
function isDeadResultItem(item, total) {
|
|
206
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) return false;
|
|
207
|
+
const entry = item;
|
|
208
|
+
if (entry.disabled !== void 0 && entry.disabled !== false) return true;
|
|
209
|
+
return entry.name === "" && total !== 1;
|
|
210
|
+
}
|
|
211
|
+
function isResultList(value) {
|
|
212
|
+
return Array.isArray(value) && value.length > 0 && value.every(
|
|
213
|
+
(e) => e !== null && typeof e === "object" && !Array.isArray(e) && "name" in e && "value" in e && "tag" in e && "filters" in e
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
function liveResultItems(value) {
|
|
217
|
+
if (!isResultList(value)) return void 0;
|
|
218
|
+
const live = value.filter((e) => !isDeadResultItem(e, value.length));
|
|
219
|
+
return live.length === value.length ? void 0 : live;
|
|
220
|
+
}
|
|
221
|
+
function isBlankAgentSettings(value) {
|
|
222
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
223
|
+
const settings = value;
|
|
224
|
+
if (settings.type !== "") return false;
|
|
225
|
+
if (settings.structuredOutputs === true) return false;
|
|
226
|
+
const schema = settings.structuredOutputsSchema;
|
|
227
|
+
if (Array.isArray(schema) && schema.length > 0) return false;
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
function configuredDeadReturnBlocks(value) {
|
|
231
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return [];
|
|
232
|
+
const section = value;
|
|
233
|
+
const type = section.type;
|
|
234
|
+
if (typeof type !== "string" || !RETURN_TYPES.has(type)) return [];
|
|
235
|
+
return RETURN_BLOCKS.filter((b) => b !== type && isConfiguredReturnBlock(section[b]));
|
|
236
|
+
}
|
|
237
|
+
function isConfiguredReturnBlock(block) {
|
|
238
|
+
if (block === null || typeof block !== "object" || Array.isArray(block)) return false;
|
|
239
|
+
const b = block;
|
|
240
|
+
for (const list of ["sort", "group", "eval", "index"]) {
|
|
241
|
+
if (Array.isArray(b[list]) && b[list].length > 0) return true;
|
|
242
|
+
}
|
|
243
|
+
const paging = b.paging;
|
|
244
|
+
return paging !== null && typeof paging === "object" && paging.enabled === true;
|
|
245
|
+
}
|
|
246
|
+
var DEFAULT_CONTEXT_EXTERNAL = {
|
|
247
|
+
tag: "input",
|
|
248
|
+
value: "",
|
|
249
|
+
permissions: { page: true, sort: true, search: true, per_page: false }
|
|
250
|
+
};
|
|
251
|
+
function bindsNoPagingFacet(value) {
|
|
252
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
253
|
+
const facets = Object.values(value);
|
|
254
|
+
if (facets.length === 0) return false;
|
|
255
|
+
return facets.every(
|
|
256
|
+
(facet) => facet !== null && typeof facet === "object" && !Array.isArray(facet) && facet.value === "" && (facet.filters ?? []).length === 0
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
var DEFAULT_CORS = {
|
|
260
|
+
mode: "default",
|
|
261
|
+
allowOrigins: [],
|
|
262
|
+
allowHeaders: [],
|
|
263
|
+
allowCredentials: false,
|
|
264
|
+
maxAge: 0,
|
|
265
|
+
allowMethods: { delete: false, get: false, head: false, patch: false, post: false, put: false }
|
|
266
|
+
};
|
|
267
|
+
var DEFAULT_DOCUMENTATION = { require_token: false, token: "" };
|
|
268
|
+
var DEFAULT_MIDDLEWARE = { pre: [], post: [], pre_customize: false, post_customize: false };
|
|
269
|
+
function isRawSqlContext(container) {
|
|
270
|
+
return container !== null && typeof container === "object" && typeof container.code === "string";
|
|
271
|
+
}
|
|
272
|
+
function withoutLeadingJoin(v) {
|
|
273
|
+
if (v === null || typeof v !== "object") return v;
|
|
274
|
+
const expression = v.expression;
|
|
275
|
+
if (!Array.isArray(expression)) return v;
|
|
276
|
+
const cleared = expression.map((entry, i) => {
|
|
277
|
+
if (entry === null || typeof entry !== "object") return entry;
|
|
278
|
+
const node = entry;
|
|
279
|
+
const group = "group" in node ? { group: withoutLeadingJoin(node.group) } : {};
|
|
280
|
+
if (i > 0) return { ...node, ...group };
|
|
281
|
+
const rest = Object.fromEntries(Object.entries(node).filter(([key]) => key !== "or"));
|
|
282
|
+
return { ...rest, ...group };
|
|
283
|
+
});
|
|
284
|
+
return { ...v, expression: cleared };
|
|
285
|
+
}
|
|
286
|
+
function isDefaultEnvelopeMember(key, v) {
|
|
287
|
+
switch (key) {
|
|
288
|
+
// An empty `mocks` arrives as `[]` from the engine and `{}` from the SDK — the
|
|
289
|
+
// empty-associative-collection artifact again. Both mean "no mocks".
|
|
290
|
+
case "mocks":
|
|
291
|
+
return isEmptyObject(v) || isEmptyArray(v);
|
|
292
|
+
// An UNSET runtime binding, in both stored spellings: the `null` the engine
|
|
293
|
+
// writes on one generation and the blank-member object it writes on another
|
|
294
|
+
// (`{id: "", mode: ""}` on 6 real `mvp:function` statements). A binding that
|
|
295
|
+
// names anything is preserved and still compares.
|
|
296
|
+
case "runtime":
|
|
297
|
+
return v === null || typeof v === "object" && v !== null && !Array.isArray(v) && Object.values(v).every((member) => member === "");
|
|
298
|
+
case "settings_registry":
|
|
299
|
+
return v === null || isEmptyArray(v);
|
|
300
|
+
case "addon":
|
|
301
|
+
return isEmptyArray(v);
|
|
302
|
+
case "disabled":
|
|
303
|
+
return v === false;
|
|
304
|
+
case "as":
|
|
305
|
+
case "description":
|
|
306
|
+
case "sql_name":
|
|
307
|
+
return v === "";
|
|
308
|
+
// The object-level members below are the same generational gap one level up
|
|
309
|
+
// — an object saved by an older engine generation omits them, while both the
|
|
310
|
+
// current engine and the SDK always write them at a fixed default. They were
|
|
311
|
+
// 1,716 of the 1,744 round-trip mismatches in a 187-workspace sweep, and
|
|
312
|
+
// each default is evidenced twice: the engine reads the absent key as this
|
|
313
|
+
// value, and real workspaces store present-at-default and absent side by
|
|
314
|
+
// side on one instance.
|
|
315
|
+
case "docs":
|
|
316
|
+
case "datasource":
|
|
317
|
+
case "view_alias":
|
|
318
|
+
return v === "";
|
|
319
|
+
// An MCP tool entry's optional metadata, and the agent provider-config
|
|
320
|
+
// members whose unset spelling is the empty string. Both store the empty
|
|
321
|
+
// form and the absent form side by side across the corpus.
|
|
322
|
+
case "tool_meta":
|
|
323
|
+
case "resource_uri":
|
|
324
|
+
case "alias":
|
|
325
|
+
case "baseURL":
|
|
326
|
+
case "safetySettings":
|
|
327
|
+
case "dynamicRetrievalConfig":
|
|
328
|
+
case "apiKey":
|
|
329
|
+
return v === "";
|
|
330
|
+
// `headers` is an empty STRING on an unset agent config and a string[] on an
|
|
331
|
+
// api_request statement — dropping only the string spelling leaves an empty
|
|
332
|
+
// header list comparing as the authored value it is.
|
|
333
|
+
case "headers":
|
|
334
|
+
return v === "";
|
|
335
|
+
// A tool entry's kind. Absent means `tool`; a `resource` or `prompt` entry
|
|
336
|
+
// says so and still compares. Value-distinct from the other `type`
|
|
337
|
+
// discriminators (an expression node, a column type), none of which is `tool`.
|
|
338
|
+
case "type":
|
|
339
|
+
return v === "tool";
|
|
340
|
+
// Four stored spellings of "no thinking budget" across 16 real configs: the
|
|
341
|
+
// empty string, absent, and the block with its budget as either `0` or `""`.
|
|
342
|
+
case "thinkingConfig":
|
|
343
|
+
return v === "" || typeof v === "object" && v !== null && !Array.isArray(v) && v["includeThoughts"] === false && (v["thinkingBudget"] === 0 || v["thinkingBudget"] === "");
|
|
344
|
+
// A query with no declared response type. Type- and value-distinct from the
|
|
345
|
+
// raw-SQL `response_type`, whose values are `list`/`single`/`count` and
|
|
346
|
+
// whose own default (`list`) is spelled differently.
|
|
347
|
+
case "response_type":
|
|
348
|
+
return v === "standard";
|
|
349
|
+
// Empty list members. `tag` is the load-bearing one: it is also the
|
|
350
|
+
// discriminator every tagged value carries, so the rule is restricted to the
|
|
351
|
+
// ARRAY spelling — a `tag: "const:str"` is a string and never matches.
|
|
352
|
+
case "tag":
|
|
353
|
+
case "views":
|
|
354
|
+
case "result":
|
|
355
|
+
return isEmptyArray(v);
|
|
356
|
+
// Two API-group gates with opposite defaults, each the value the engine
|
|
357
|
+
// falls back to when the key is absent: a group serves unless disabled,
|
|
358
|
+
// documentation is off unless turned on.
|
|
359
|
+
case "api_group_enabled":
|
|
360
|
+
return v === true;
|
|
361
|
+
case "swagger":
|
|
362
|
+
return v === false;
|
|
363
|
+
case "documentation":
|
|
364
|
+
return deepEqual(normalize(v), normalize(DEFAULT_DOCUMENTATION));
|
|
365
|
+
// A CORS block is applied ONLY when its `mode` is `"custom"` — the engine
|
|
366
|
+
// reads an absent mode as `""`, which is not custom — so a block that does
|
|
367
|
+
// not say `custom` configures nothing.
|
|
368
|
+
//
|
|
369
|
+
// Two spellings therefore mean the same default. The current one says
|
|
370
|
+
// `mode: "default"`; an older one predates `mode` entirely and carries
|
|
371
|
+
// `enabled: false`, a key that request path never reads.
|
|
372
|
+
case "cors": {
|
|
373
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
374
|
+
const block = { ...v };
|
|
375
|
+
if (block["enabled"] === false) delete block["enabled"];
|
|
376
|
+
block["mode"] ??= "default";
|
|
377
|
+
return deepEqual(normalize(block), normalize(DEFAULT_CORS));
|
|
378
|
+
}
|
|
379
|
+
// Both stored spellings of "no middleware": the engine hands an empty
|
|
380
|
+
// associative map back as a JSON array, the same artifact already absorbed
|
|
381
|
+
// for `mocks`, `customize` and an empty `context`. A phase explicitly
|
|
382
|
+
// customized to run nothing still compares — that is not inheriting.
|
|
383
|
+
case "middleware":
|
|
384
|
+
return isEmptyArray(v) || deepEqual(normalize(v), normalize(DEFAULT_MIDDLEWARE));
|
|
385
|
+
// Two unrelated `offset`s, both at a default. An addon's is the response path
|
|
386
|
+
// its rows are spliced into, `""` when spliced at the root. A list context's
|
|
387
|
+
// paging offset is declared to default to 0. Neither carries information at
|
|
388
|
+
// its default; any other value is preserved.
|
|
389
|
+
case "offset":
|
|
390
|
+
return v === "" || isNumber(v, 0);
|
|
391
|
+
// Return-block paging members, each at its declared default. Type-distinct
|
|
392
|
+
// from their same-named neighbours in a permissions block (`page: true`,
|
|
393
|
+
// `per_page: false`), which are booleans and so untouched.
|
|
394
|
+
case "page":
|
|
395
|
+
return isNumber(v, 1);
|
|
396
|
+
case "per_page":
|
|
397
|
+
return isNumber(v, 25);
|
|
398
|
+
case "metadata":
|
|
399
|
+
return v === true;
|
|
400
|
+
case "totals":
|
|
401
|
+
return v === false;
|
|
402
|
+
case "distinct":
|
|
403
|
+
return v === "auto";
|
|
404
|
+
// `s.setheader`'s duplicate-handling mode. The engine declares it
|
|
405
|
+
// `duplicates?=replace` and reads it as `($data["duplicates"] ?? "replace")`,
|
|
406
|
+
// so an absent key IS "replace"; 3 real statements omit it against 8 that
|
|
407
|
+
// store it present-at-default. The key appears on no other statement.
|
|
408
|
+
case "duplicates":
|
|
409
|
+
return v === "replace";
|
|
410
|
+
// A return sub-block whose every member sat at a default. These are checked
|
|
411
|
+
// against their NORMALIZED form because the member rules above are what empty
|
|
412
|
+
// them — without this the block survives as `{}` and the whole return envelope
|
|
413
|
+
// never collapses, which is the only reason it matters.
|
|
414
|
+
//
|
|
415
|
+
// `list` also names a foreach's iterated value; a tagged value never
|
|
416
|
+
// normalizes to empty, so that one is untouched.
|
|
417
|
+
//
|
|
418
|
+
// `enabled:false` is the one member no rule above empties, deliberately — it
|
|
419
|
+
// is meaningful on a history block, so it cannot be dropped by name. It IS
|
|
420
|
+
// droppable here, where the key says the block is paging: 7 real db.query
|
|
421
|
+
// statements omit `paging` outright against 149 that store it present at
|
|
422
|
+
// `enabled:false`, and the two spellings mean the same thing.
|
|
423
|
+
//
|
|
424
|
+
// Evidenced twice before being trusted. Every engine consumer reads the flag
|
|
425
|
+
// as `["paging"]["enabled"] ?? false`, so an absent block IS a disabled one,
|
|
426
|
+
// and the serializer returns before writing anything when it is off — a
|
|
427
|
+
// disabled block emits no source at all. The siblings are provably inert
|
|
428
|
+
// meanwhile: every read of `page`/`per_page`/`offset`/`metadata` sits behind
|
|
429
|
+
// that same gate.
|
|
430
|
+
//
|
|
431
|
+
// Still narrow: this fires only once every OTHER member has normalized away.
|
|
432
|
+
// A disabled block that customizes something (`{enabled:false, per_page:50}`)
|
|
433
|
+
// keeps both and compares as itself.
|
|
434
|
+
case "paging": {
|
|
435
|
+
const normalized = normalize(v);
|
|
436
|
+
return isEmptyObject(normalized) || deepEqual(normalized, { enabled: false });
|
|
437
|
+
}
|
|
438
|
+
// The same four blocks, plus the residue the member rules above cannot reach.
|
|
439
|
+
//
|
|
440
|
+
// The engine writes ALL FOUR result-shape blocks on every query; the SDK writes
|
|
441
|
+
// only the one its `returnType` selects. Emptiness alone does not collapse a
|
|
442
|
+
// default sibling, because two of its members have no rule that empties them —
|
|
443
|
+
// the paging `enabled:false` gate and an aggregate's empty `group`/`index`
|
|
444
|
+
// lists. So one customized `per_page` left every default sibling mismatching,
|
|
445
|
+
// and a paged query could never verify.
|
|
446
|
+
//
|
|
447
|
+
// Deep-equality against each block's own frozen default is what reaches those
|
|
448
|
+
// members WITHOUT a global rule on their generic names — `enabled:false` is
|
|
449
|
+
// meaningful on a history block, and an empty `group` is a condition default
|
|
450
|
+
// elsewhere. A block with anything authored inside it still compares.
|
|
451
|
+
case "list":
|
|
452
|
+
if (hasNoListBounds(v)) return true;
|
|
453
|
+
// falls through to the result-shape block of the same name
|
|
454
|
+
case "single":
|
|
455
|
+
case "stream":
|
|
456
|
+
case "aggregate":
|
|
457
|
+
return isEmptyObject(normalize(v)) || deepEqual(normalize(v), normalize(DEFAULT_RETURN_BLOCKS[key]));
|
|
458
|
+
// Field-envelope members the engine fills with a fixed default on save. A
|
|
459
|
+
// field saved by an older engine generation omits them entirely, while both
|
|
460
|
+
// the current engine and the SDK always write them — the same lean-vs-full
|
|
461
|
+
// generational gap as the members above, and by far the most common one in
|
|
462
|
+
// the wild: nearly every field in a workspace that has not been re-saved
|
|
463
|
+
// lacks `is_settings_registry`. Without these the decoder cannot prove ANY
|
|
464
|
+
// authored form reproduces such a field, so every one of them degrades to a
|
|
465
|
+
// descriptor literal or `rawField()` — including foreign keys, which lose
|
|
466
|
+
// their `f.tableRef(table)` form and the table import with it.
|
|
467
|
+
case "is_settings_registry":
|
|
468
|
+
case "sensitive":
|
|
469
|
+
return v === false;
|
|
470
|
+
case "mode":
|
|
471
|
+
case "format":
|
|
472
|
+
return v === "";
|
|
473
|
+
// A field's `vector.size` is only authored on a `vector` column; on every
|
|
474
|
+
// other type the engine writes this exact default. Dropping it at the
|
|
475
|
+
// default is symmetric, so a real `vector` column of size 3 still compares
|
|
476
|
+
// equal, and any other size is preserved and still compared.
|
|
477
|
+
case "vector":
|
|
478
|
+
return deepEqual(v, { size: 3 });
|
|
479
|
+
// Agent `agent_settings.model`: the engine persists a top-level empty
|
|
480
|
+
// `model:""` (the real model lives under `configs.<provider>.model`); the SDK
|
|
481
|
+
// omits the empty top-level field. Drop it on both sides when empty.
|
|
482
|
+
case "model":
|
|
483
|
+
return v === "";
|
|
484
|
+
// Statement input-entry members: the lean parser form omits these; the full
|
|
485
|
+
// persisted form carries them. Drop at their defaults (a meaningful
|
|
486
|
+
// `ignore:true` on a system column, or non-empty `children`, is kept).
|
|
487
|
+
case "ignore":
|
|
488
|
+
case "expand":
|
|
489
|
+
return v === false;
|
|
490
|
+
// Expression right-operand flag: the engine schema marks it `?=false` and
|
|
491
|
+
// drops it at the default on save, so the persisted form (and the SDK) omit
|
|
492
|
+
// it; older parser-generation fixtures still carry `ignore_empty:false`.
|
|
493
|
+
case "ignore_empty":
|
|
494
|
+
return v === false;
|
|
495
|
+
case "children":
|
|
496
|
+
return isEmptyArray(v);
|
|
497
|
+
// The two stored generations of an external database connection sit in
|
|
498
|
+
// SEPARATE fields, and a statement that uses one leaves the other at its
|
|
499
|
+
// unset value rather than dropping it: the engine's optional-schema pass
|
|
500
|
+
// gives the bare `connection_string` the text default `""` and leaves the
|
|
501
|
+
// nested `connection_string_flex` as nothing at all. Neither unset spelling
|
|
502
|
+
// says anything about the connection, so both drop on both sides — which is
|
|
503
|
+
// what lets a statement stored in either generation prove against the one
|
|
504
|
+
// field it actually uses (issue #138). A populated field of either
|
|
505
|
+
// generation is preserved and still compared.
|
|
506
|
+
case "connection_string":
|
|
507
|
+
return v === "";
|
|
508
|
+
case "connection_string_flex":
|
|
509
|
+
return v === null || v === "{}" || isEmptyObject(v);
|
|
510
|
+
// A value's filter chain: an empty `filters:[]` is identical to no filter
|
|
511
|
+
// chain. The engine always serializes it on a nested value (e.g. a
|
|
512
|
+
// `context`-nested `filename`); the SDK omits it there. Drop the empty form
|
|
513
|
+
// on both sides so the representational gap doesn't fail an otherwise-equal
|
|
514
|
+
// value. A non-empty chain is preserved and still compared.
|
|
515
|
+
case "filters":
|
|
516
|
+
return isEmptyArray(v);
|
|
517
|
+
// Statement/object input-entry array: the lean parser form omits an empty
|
|
518
|
+
// `input`; the full persisted form carries `input:[]`; and the engine writes
|
|
519
|
+
// `input:null` for a statement that takes no inputs at all. All three are the
|
|
520
|
+
// same "no inputs" state — the SDK emits the `[]` spelling, so without the
|
|
521
|
+
// null arm every input-less statement in a pulled workspace fails to prove
|
|
522
|
+
// and degrades to `raw()`. Drop all three on both sides; a populated `input`
|
|
523
|
+
// is preserved. Same two-spellings-of-empty shape as `settings_registry`.
|
|
524
|
+
case "input":
|
|
525
|
+
return v === null || isEmptyArray(v);
|
|
526
|
+
case "shared_workspace":
|
|
527
|
+
return v !== null && typeof v === "object" && v.is_shared === false;
|
|
528
|
+
// A trigger's `obj_id`: a table trigger's is the referenced table's GUID
|
|
529
|
+
// *string* (authored, derived by the SDK, kept and compared). A workspace /
|
|
530
|
+
// realtime / error trigger has no table, so the engine stores a *numeric*
|
|
531
|
+
// branch/workspace reference (`1`) while the SDK emits a `0` placeholder —
|
|
532
|
+
// deploy-target state, same rationale as the stripped `branch`. Drop the
|
|
533
|
+
// numeric form on both sides; the guid-string form is preserved.
|
|
534
|
+
case "obj_id":
|
|
535
|
+
return typeof v === "number";
|
|
536
|
+
// Toolset/query/function inherit-tier `history` block: when `inherit` is true
|
|
537
|
+
// nothing is customized (the limit/enabled members are the inherited
|
|
538
|
+
// defaults, not authored). The SDK omits it on a toolset; drop the inheriting
|
|
539
|
+
// form on both sides. A customized (`inherit:false`) history is preserved.
|
|
540
|
+
case "history":
|
|
541
|
+
return Array.isArray(v) || v !== null && typeof v === "object" && v.inherit === true;
|
|
542
|
+
// An MCP-server toolset persists `agent_settings:null` (only agents carry a
|
|
543
|
+
// real settings block); the SDK omits it. Drop the null form.
|
|
544
|
+
// `null`, and the engine's BLANK SCAFFOLD — an MCP toolset that configures no
|
|
545
|
+
// model still gets the whole block written, inert (see
|
|
546
|
+
// {@link isBlankAgentSettings}). The SDK spells both by omitting it.
|
|
547
|
+
case "agent_settings":
|
|
548
|
+
return v === null || isBlankAgentSettings(v);
|
|
549
|
+
// An agent's default `agent_settings.telemetry` (all providers off, empty
|
|
550
|
+
// keys): the SDK omits it. Drop when telemetry is disabled.
|
|
551
|
+
case "telemetry":
|
|
552
|
+
return v !== null && typeof v === "object" && v.enabled === false;
|
|
553
|
+
// A tool/query/function `test:[]` scaffold array — the SDK omits it on a tool;
|
|
554
|
+
// an empty test list is identical to none. Drop the empty form both sides.
|
|
555
|
+
case "test":
|
|
556
|
+
return isEmptyArray(v);
|
|
557
|
+
// A query's saved request/response sample at its empty spelling. The SDK
|
|
558
|
+
// writes `{}` whenever no sample is authored and the engine may store the
|
|
559
|
+
// key not at all, or with both halves null. All three mean "no sample".
|
|
560
|
+
// A POPULATED one is preserved and compared (and copied through opaquely —
|
|
561
|
+
// see the `example` branch in the recursion below).
|
|
562
|
+
case "example":
|
|
563
|
+
return v === null || isEmptyArray(v) || typeof v === "object" && Object.values(v).every((half) => half === null);
|
|
564
|
+
// A default `auth:false` (public / no auth table): the engine persists it on a
|
|
565
|
+
// tool where the SDK omits it. Drop the `false` form; `auth:true` and an
|
|
566
|
+
// auth-table id are preserved and still compared.
|
|
567
|
+
// `false` and `""` are both "no auth table"; a guid names one and compares.
|
|
568
|
+
case "auth":
|
|
569
|
+
return v === false || v === "";
|
|
570
|
+
// Default db-query `context` members the engine fills when an addon (or any
|
|
571
|
+
// db context) customizes nothing; the SDK omits them. Empty-collection /
|
|
572
|
+
// false members drop directly; the three structured members below match
|
|
573
|
+
// their exact engine default (a customized context is preserved).
|
|
574
|
+
case "bind":
|
|
575
|
+
case "eval":
|
|
576
|
+
case "sort":
|
|
577
|
+
return isEmptyArray(v);
|
|
578
|
+
case "future":
|
|
579
|
+
return v === false;
|
|
580
|
+
// Compare NORMALIZED against NORMALIZED default: normalize strips empty
|
|
581
|
+
// `sort`/`eval`/`index` members from the nested subtree, so the frozen
|
|
582
|
+
// default must pass through the same reduction to compare equal.
|
|
583
|
+
case "lock":
|
|
584
|
+
return deepEqual(normalize(v), normalize({ tag: "const:bool", value: "" }));
|
|
585
|
+
// A condition container that holds nothing, in either stored spelling: the
|
|
586
|
+
// `{expression: []}` the SDK writes and the empty associative-map form `[]`
|
|
587
|
+
// the engine hands back — the same artifact already absorbed for `mocks`,
|
|
588
|
+
// `customize` and an empty `context`. Both mean "no condition configured".
|
|
589
|
+
// A populated container is untouched and still compares node for node.
|
|
590
|
+
case "expr":
|
|
591
|
+
return isEmptyArray(v) || v !== null && typeof v === "object" && isEmptyArray(v.expression);
|
|
592
|
+
case "search":
|
|
593
|
+
return deepEqual(normalize(v), normalize({ expression: [] }));
|
|
594
|
+
// Two spellings of an all-defaults return block. The engine's declared shape
|
|
595
|
+
// defaults the result type to a list and leaves every sub-block optional, so
|
|
596
|
+
// a query that customizes nothing persists either the whole subtree (when the
|
|
597
|
+
// engine filled it on save) or nothing but the type — and the SDK omits it
|
|
598
|
+
// entirely. Accept both, and preserve any customized paging/sort/distinct.
|
|
599
|
+
case "return":
|
|
600
|
+
return deepEqual(normalize(v), normalize(DEFAULT_CONTEXT_RETURN)) || deepEqual(normalize(v), MINIMAL_CONTEXT_RETURN);
|
|
601
|
+
// A condition entry's nested group. The engine declares it optional with no
|
|
602
|
+
// default, so an entry that nests nothing omits the key while the SDK
|
|
603
|
+
// materializes an empty search. Same state; drop the empty form on both
|
|
604
|
+
// sides. A group that actually nests expressions is preserved and compared.
|
|
605
|
+
case "group":
|
|
606
|
+
return deepEqual(normalize(v), EMPTY_SEARCH);
|
|
607
|
+
// A condition entry's or-flag, declared to default false: the persisted form
|
|
608
|
+
// omits it at the default where the SDK writes it.
|
|
609
|
+
case "or":
|
|
610
|
+
return v === false;
|
|
611
|
+
// Generated-asset visibility, declared to default public wherever it appears.
|
|
612
|
+
case "access":
|
|
613
|
+
return v === "public";
|
|
614
|
+
// A precondition's error class, declared to default to the standard error.
|
|
615
|
+
case "error_type":
|
|
616
|
+
return v === "standard";
|
|
617
|
+
case "external":
|
|
618
|
+
return deepEqual(normalize(v), normalize(DEFAULT_CONTEXT_EXTERNAL));
|
|
619
|
+
case "simpleExternal":
|
|
620
|
+
return bindsNoPagingFacet(v);
|
|
621
|
+
default:
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
function isEmptyOutput(v) {
|
|
626
|
+
if (v === null) return true;
|
|
627
|
+
if (typeof v !== "object" || Array.isArray(v)) return false;
|
|
628
|
+
const o = v;
|
|
629
|
+
const noItems = o.items === void 0 || Array.isArray(o.items) && o.items.length === 0;
|
|
630
|
+
const noFilters = o.filters === void 0 || Array.isArray(o.filters) && o.filters.length === 0;
|
|
631
|
+
return noItems && noFilters && o.customize !== true;
|
|
632
|
+
}
|
|
633
|
+
function historyWithDefaultLimit(v) {
|
|
634
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return void 0;
|
|
635
|
+
const block = v;
|
|
636
|
+
if (block.inherit !== false) return void 0;
|
|
637
|
+
const enabledKey = Object.keys(block).find((key) => key === "enabled" || key.endsWith("_enabled"));
|
|
638
|
+
if (enabledKey === void 0) return void 0;
|
|
639
|
+
const limitKey = enabledKey === "enabled" ? "limit" : `${enabledKey.slice(0, -"_enabled".length)}_limit`;
|
|
640
|
+
if (Object.hasOwn(block, limitKey)) return void 0;
|
|
641
|
+
return { ...block, [limitKey]: ENGINE_HISTORY_LIMIT };
|
|
642
|
+
}
|
|
643
|
+
var ENGINE_HISTORY_LIMIT = 100;
|
|
644
|
+
var LOCAL_DBO_REF = /^dbo=\d+$/;
|
|
645
|
+
function clearLocalDboRefs(value, cleared) {
|
|
646
|
+
if (Array.isArray(value)) {
|
|
647
|
+
return value.map((item) => clearLocalDboRefs(item, cleared));
|
|
648
|
+
}
|
|
649
|
+
if (value === null || typeof value !== "object") return value;
|
|
650
|
+
const next = Object.fromEntries(
|
|
651
|
+
Object.entries(value).map(([key, member]) => [
|
|
652
|
+
key,
|
|
653
|
+
clearLocalDboRefs(member, cleared)
|
|
654
|
+
])
|
|
655
|
+
);
|
|
656
|
+
if (next.name === "@" && Array.isArray(next.arg)) {
|
|
657
|
+
const target = next.arg[0];
|
|
658
|
+
if (typeof target === "string" && LOCAL_DBO_REF.test(target)) {
|
|
659
|
+
cleared?.add(target);
|
|
660
|
+
return { ...next, arg: ["dbo=", ...next.arg.slice(1)] };
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return next;
|
|
664
|
+
}
|
|
665
|
+
var EMPTY_CONTEXT_FILL = /* @__PURE__ */ new Map([
|
|
666
|
+
// Its name rides the envelope `as`, so the context is the value alone.
|
|
667
|
+
["mvp:set_var", { tag: "const", named: false }],
|
|
668
|
+
// Standalone classes, each declaring its own default tag.
|
|
669
|
+
["mvp:sleep", { tag: "const:int", named: false }],
|
|
670
|
+
["mvp:die", { tag: "input", named: false }],
|
|
671
|
+
["mvp:setheader", { tag: "input", named: false }],
|
|
672
|
+
// `array_pop`/`array_shift` carry NO `tag`, because they take no operand:
|
|
673
|
+
// popping or shifting reads nothing, their specs route no spread field, and
|
|
674
|
+
// the encoder writes `name` alone. The fill must be exactly what the encoder
|
|
675
|
+
// writes — filling a value they cannot author invents members the comparison
|
|
676
|
+
// then demands forever, which is what shipped once when they were given the
|
|
677
|
+
// full `UpdateVarBase` treatment below.
|
|
678
|
+
//
|
|
679
|
+
// A name-only fill is a different claim from that one, and it is evidenced
|
|
680
|
+
// twice on the engine, both times on the SCALAR `name`:
|
|
681
|
+
// • the statement's own XanoScript schema declares `name?='': context.name`
|
|
682
|
+
// — the default for an absent name IS the empty string; and
|
|
683
|
+
// • the shared base class declares `name` as a plain `text` member and runs
|
|
684
|
+
// it through the same optional-schema pass that licenses every entry here.
|
|
685
|
+
//
|
|
686
|
+
// That second point is exactly where the loops failed (see
|
|
687
|
+
// {@link LOOP_EMPTY_ITERAND}): that pass materializes a SCALAR member but
|
|
688
|
+
// defaults a NESTED object to the literal string `"{}"`. `name` is a scalar,
|
|
689
|
+
// `list`/`cnt` are not, and the shape is what decides. The engine's own
|
|
690
|
+
// statement-transform corpus stores `{"context":{}}` for `mvp:array_shift`.
|
|
691
|
+
["mvp:array_pop", { named: true }],
|
|
692
|
+
["mvp:array_shift", { named: true }],
|
|
693
|
+
// The `UpdateVarBase` family: `{name, value, tag, filters}`, `tag` overridden
|
|
694
|
+
// per subclass by `getDefaultTag()`. `test/codegen/spec-inverse` asserts every
|
|
695
|
+
// entry's members against the spec catalog so the two cannot drift.
|
|
696
|
+
["mvp:update_var", { tag: "const", named: true }],
|
|
697
|
+
["mvp:array_merge", { tag: "const:array", named: true }],
|
|
698
|
+
["mvp:array_push", { tag: "const", named: true }],
|
|
699
|
+
["mvp:array_unshift", { tag: "const", named: true }],
|
|
700
|
+
["mvp:bitwise_and", { tag: "const:int", named: true }],
|
|
701
|
+
["mvp:bitwise_or", { tag: "const:int", named: true }],
|
|
702
|
+
["mvp:bitwise_xor", { tag: "const:int", named: true }],
|
|
703
|
+
["mvp:math_add", { tag: "const:decimal", named: true }],
|
|
704
|
+
["mvp:math_sub", { tag: "const:decimal", named: true }],
|
|
705
|
+
["mvp:math_mul", { tag: "const:decimal", named: true }],
|
|
706
|
+
["mvp:math_div", { tag: "const:decimal", named: true }],
|
|
707
|
+
["mvp:math_mod", { tag: "const:int", named: true }],
|
|
708
|
+
["mvp:text_append", { tag: "const", named: true }],
|
|
709
|
+
["mvp:text_prepend", { tag: "const", named: true }],
|
|
710
|
+
["mvp:text_trim", { tag: "const", named: true }],
|
|
711
|
+
["mvp:text_ltrim", { tag: "const", named: true }],
|
|
712
|
+
["mvp:text_rtrim", { tag: "const", named: true }],
|
|
713
|
+
["mvp:text_starts_with", { tag: "const", named: true }],
|
|
714
|
+
["mvp:text_istarts_with", { tag: "const", named: true }],
|
|
715
|
+
["mvp:text_ends_with", { tag: "const", named: true }],
|
|
716
|
+
["mvp:text_iends_with", { tag: "const", named: true }],
|
|
717
|
+
["mvp:text_contains", { tag: "const", named: true }],
|
|
718
|
+
["mvp:text_icontains", { tag: "const", named: true }],
|
|
719
|
+
// The file-resource family and `debug_log`: the context IS the value, like
|
|
720
|
+
// `set_var`, but the file resources declare a sibling SCALAR (`access`) that
|
|
721
|
+
// the same optional pass materializes alongside it — so the fill carries it.
|
|
722
|
+
// The nested `filename` is NOT filled: a nested object defaults to the literal
|
|
723
|
+
// string `"{}"` and materializes nothing.
|
|
724
|
+
//
|
|
725
|
+
// `extra` is exactly where the `array_pop` note above applies. `create_attachment`
|
|
726
|
+
// ALSO declares `include_meta?=false`, and filling it failed the round trip —
|
|
727
|
+
// the SDK's spec models no such field, so the encoder never writes it and the
|
|
728
|
+
// fill demanded a member the recovered record could not produce. The fill has
|
|
729
|
+
// to equal what the ENCODER writes, not everything the engine would supply.
|
|
730
|
+
// (A workspace that stores a real `include_meta` still degrades to `raw()`,
|
|
731
|
+
// which is the correct handling for an unmodelled member.)
|
|
732
|
+
//
|
|
733
|
+
// Read off each statement class's own schema, and the default tag is NOT
|
|
734
|
+
// uniform across them — the file resources declare `tag?=input` while
|
|
735
|
+
// `debug_log` declares `tag?=const`, the same split `die`/`setheader` have.
|
|
736
|
+
["mvp:create_image", { tag: "input", named: false, extra: { access: "public" } }],
|
|
737
|
+
["mvp:create_audio", { tag: "input", named: false, extra: { access: "public" } }],
|
|
738
|
+
["mvp:create_attachment", { tag: "input", named: false, extra: { access: "public" } }],
|
|
739
|
+
["mvp:debug_log", { tag: "const", named: false }]
|
|
740
|
+
]);
|
|
741
|
+
var LOOP_EMPTY_ITERAND = /* @__PURE__ */ new Map([
|
|
742
|
+
["mvp:foreach", { member: "list", value: "[]", tag: "const:array" }],
|
|
743
|
+
["mvp:for", { member: "cnt", value: "0", tag: "const:int" }]
|
|
744
|
+
]);
|
|
745
|
+
function blankValue(tag) {
|
|
746
|
+
return { value: "", tag, filters: [] };
|
|
747
|
+
}
|
|
748
|
+
function liveSwitchContext(stored) {
|
|
749
|
+
if (stored === null || typeof stored !== "object") return void 0;
|
|
750
|
+
const { name, context } = stored;
|
|
751
|
+
if (name !== "mvp:switch") return void 0;
|
|
752
|
+
if (context === null || typeof context !== "object" || Array.isArray(context)) return void 0;
|
|
753
|
+
const block = context;
|
|
754
|
+
const subject = block.value;
|
|
755
|
+
const nested = subject !== null && typeof subject === "object" && !Array.isArray(subject) && typeof subject.tag === "string";
|
|
756
|
+
if (!nested) return void 0;
|
|
757
|
+
const strays = ["tag", "filters"].filter((key) => key in block);
|
|
758
|
+
if (strays.length === 0) return void 0;
|
|
759
|
+
return Object.fromEntries(Object.entries(block).filter(([k]) => !strays.includes(k)));
|
|
760
|
+
}
|
|
761
|
+
function liveArrayMapContext(stored) {
|
|
762
|
+
if (stored === null || typeof stored !== "object") return void 0;
|
|
763
|
+
const { name, context } = stored;
|
|
764
|
+
if (name !== "mvp:array_map") return void 0;
|
|
765
|
+
if (context === null || typeof context !== "object" || Array.isArray(context)) return void 0;
|
|
766
|
+
const block = context;
|
|
767
|
+
const dead = (block.output_type ?? "value") === "object" ? "transform_value" : "transform_object";
|
|
768
|
+
if (!(dead in block)) return void 0;
|
|
769
|
+
return Object.fromEntries(Object.entries(block).filter(([k]) => k !== dead));
|
|
770
|
+
}
|
|
771
|
+
function filledContext(stored) {
|
|
772
|
+
if (stored === null || typeof stored !== "object") return null;
|
|
773
|
+
const { name, context } = stored;
|
|
774
|
+
if (typeof name !== "string") return null;
|
|
775
|
+
const whole = EMPTY_CONTEXT_FILL.get(name);
|
|
776
|
+
if (whole) {
|
|
777
|
+
const empty = context === null || Array.isArray(context) && context.length === 0 || typeof context === "object" && Object.keys(context).length === 0;
|
|
778
|
+
if (!empty) return null;
|
|
779
|
+
if (whole.tag === void 0) return { name: "" };
|
|
780
|
+
return {
|
|
781
|
+
...whole.named ? { name: "" } : {},
|
|
782
|
+
...blankValue(whole.tag),
|
|
783
|
+
...whole.extra ?? {}
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
const loop = LOOP_EMPTY_ITERAND.get(name);
|
|
787
|
+
if (loop) {
|
|
788
|
+
if (context === null || typeof context !== "object" || Array.isArray(context)) return null;
|
|
789
|
+
const block = context;
|
|
790
|
+
if (block[loop.member] !== void 0) return null;
|
|
791
|
+
return { ...block, [loop.member]: { value: loop.value, tag: loop.tag, filters: [] } };
|
|
792
|
+
}
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
var INERT_TRIGGER_META_GROUPS = {
|
|
796
|
+
database: {
|
|
797
|
+
datasource: [],
|
|
798
|
+
search: { expression: [] },
|
|
799
|
+
action: { delete: false, insert: false, truncate: false, update: false }
|
|
800
|
+
},
|
|
801
|
+
toolset: { action: { connection: false } },
|
|
802
|
+
workspace: { action: { branch_live: false, branch_merge: false, branch_new: false } },
|
|
803
|
+
workspace_realtime_channel: { action: { message: false, join: false } },
|
|
804
|
+
realtime_server: { action: { connect: false, disconnect: false } },
|
|
805
|
+
channel: { action: { join: false, leave: false, deliver: false } }
|
|
806
|
+
};
|
|
807
|
+
function withoutInertTriggerMetaGroups(value) {
|
|
808
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
809
|
+
const entries = Object.entries(value);
|
|
810
|
+
if (entries.length === 0) return void 0;
|
|
811
|
+
if (!entries.every(([k]) => Object.hasOwn(INERT_TRIGGER_META_GROUPS, k))) return void 0;
|
|
812
|
+
const kept = entries.filter(([k, v]) => !deepEqual(v, INERT_TRIGGER_META_GROUPS[k]));
|
|
813
|
+
return kept.length === entries.length ? void 0 : Object.fromEntries(kept);
|
|
814
|
+
}
|
|
815
|
+
function normalize(value) {
|
|
816
|
+
if (Array.isArray(value)) {
|
|
817
|
+
return value.map((v) => normalize(v));
|
|
818
|
+
}
|
|
819
|
+
if (value !== null && typeof value === "object") {
|
|
820
|
+
const out = {};
|
|
821
|
+
const blankObj = value.tag === "const:obj" && "value" in value && (value.value === "" || value.value === null);
|
|
822
|
+
const isTable = "schema" in value && !("context" in value);
|
|
823
|
+
const sortsInput = NAME_KEYED_INPUT.has(value.name);
|
|
824
|
+
const dropsInput = UNREADABLE_INPUT.has(value.name);
|
|
825
|
+
const retired = retiredInputs(value.name);
|
|
826
|
+
const contextFill = filledContext(value);
|
|
827
|
+
const arrayMapContext = liveArrayMapContext(value);
|
|
828
|
+
const switchContext = liveSwitchContext(value);
|
|
829
|
+
const isGroupNode = value.type === "group";
|
|
830
|
+
const isMiddlewareBlock = "pre_customize" in value || "post_customize" in value;
|
|
831
|
+
const isDescriptor = "type" in value && "required" in value;
|
|
832
|
+
const requiredEntry = value.required === true;
|
|
833
|
+
for (const [k, stored] of Object.entries(value)) {
|
|
834
|
+
const v = k === "expr" ? withoutLeadingJoin(stored) : k === "result" ? liveResultItems(stored) ?? stored : k === "return" ? (() => {
|
|
835
|
+
const live = liveReturnSection(stored) ?? stored;
|
|
836
|
+
return returnWithoutIterator(live) ?? live;
|
|
837
|
+
})() : k === "runtime" ? liveAsyncRuntime(stored) ?? stored : stored;
|
|
838
|
+
if (k === "token" && isSavedTest(value)) continue;
|
|
839
|
+
if (k === "id" && isSavedTest(value)) {
|
|
840
|
+
out[k] = v;
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
if (STRIP_KEYS.has(k)) continue;
|
|
844
|
+
if (isTable && k === "as") continue;
|
|
845
|
+
if (isGroupNode && k === "statement") continue;
|
|
846
|
+
if (isMiddlewareBlock && (k === "pre" || k === "post") && value[`${k}_customize`] !== true) {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
if (k === "arg" && isEmptyArray(v) && isRawSqlContext(value)) continue;
|
|
850
|
+
if (isDefaultEnvelopeMember(k, v)) continue;
|
|
851
|
+
if (isDescriptor && k === "default") {
|
|
852
|
+
if (requiredEntry || v === null || v === "") {
|
|
853
|
+
out[k] = null;
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (k === "output" && isEmptyOutput(v)) continue;
|
|
858
|
+
if (k === "history") {
|
|
859
|
+
const filled = historyWithDefaultLimit(v);
|
|
860
|
+
if (filled !== void 0) {
|
|
861
|
+
out[k] = normalize(filled);
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (k === "meta") {
|
|
866
|
+
const reduced = withoutInertTriggerMetaGroups(v);
|
|
867
|
+
if (reduced !== void 0) {
|
|
868
|
+
out[k] = normalize(reduced);
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
if (k === "customize" && isEmptyCustomize(v)) {
|
|
873
|
+
out[k] = {};
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (k === "customize") {
|
|
877
|
+
out[k] = normalize(clearLocalDboRefs(v));
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
if (k === "example") {
|
|
881
|
+
out[k] = Object.fromEntries(
|
|
882
|
+
Object.entries(v).filter(([, half]) => half !== null)
|
|
883
|
+
);
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (dropsInput && k === "input") continue;
|
|
887
|
+
if (k === "input" && Array.isArray(v) && retired.length > 0) {
|
|
888
|
+
out[k] = v.filter((entry) => !retired.includes(String(entry?.name ?? ""))).map((entry) => normalize(entry));
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
if (sortsInput && k === "input" && Array.isArray(v)) {
|
|
892
|
+
out[k] = [...v].sort(
|
|
893
|
+
(a, b) => String(a?.name ?? "").localeCompare(
|
|
894
|
+
String(b?.name ?? "")
|
|
895
|
+
)
|
|
896
|
+
).map((entry) => normalize(entry));
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
if (k === "auth") {
|
|
900
|
+
const unbound = unboundAuthTable(v);
|
|
901
|
+
if (unbound) {
|
|
902
|
+
out[k] = normalize(unbound);
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
if (k === "context" && arrayMapContext) {
|
|
907
|
+
out[k] = normalize(arrayMapContext);
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (k === "context" && switchContext) {
|
|
911
|
+
out[k] = normalize(switchContext);
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (k === "context" && contextFill) {
|
|
915
|
+
out[k] = normalize(contextFill);
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
if (k === "context" && (isEmptyArray(v) || v === null)) {
|
|
919
|
+
out[k] = {};
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
if (k === "arg" && Array.isArray(v)) {
|
|
923
|
+
out[k] = v.map((e) => typeof e === "number" ? String(e) : normalize(e));
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
if (PAGING_INT_KEYS.has(k) && typeof v === "string" && /^-?\d+$/.test(v)) {
|
|
927
|
+
out[k] = Number(v);
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (k === "value" && blankObj) {
|
|
931
|
+
out[k] = "";
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
out[k] = (k === "value" || k === "operand" || k === "temperature") && (typeof v === "number" || typeof v === "boolean") ? String(v) : normalize(v);
|
|
935
|
+
}
|
|
936
|
+
return out;
|
|
937
|
+
}
|
|
938
|
+
if (typeof value === "string") {
|
|
939
|
+
const ts = canonicalizeTimestamp(value);
|
|
940
|
+
if (ts !== void 0) return ts;
|
|
941
|
+
}
|
|
942
|
+
return value;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
export {
|
|
946
|
+
isEmptyCustomize,
|
|
947
|
+
deepEqual,
|
|
948
|
+
hasNoListBounds,
|
|
949
|
+
hasUnreadableInput,
|
|
950
|
+
retiredInputs,
|
|
951
|
+
isBlankGroupStatement,
|
|
952
|
+
isDeadResultItem,
|
|
953
|
+
isBlankAgentSettings,
|
|
954
|
+
configuredDeadReturnBlocks,
|
|
955
|
+
isDefaultEnvelopeMember,
|
|
956
|
+
isEmptyOutput,
|
|
957
|
+
ENGINE_HISTORY_LIMIT,
|
|
958
|
+
clearLocalDboRefs,
|
|
959
|
+
liveArrayMapContext,
|
|
960
|
+
filledContext,
|
|
961
|
+
normalize
|
|
962
|
+
};
|
|
963
|
+
//# sourceMappingURL=chunk-26CNBZE2.js.map
|