@xano/cli 1.0.4-beta.7 → 1.0.4-beta.9

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.
@@ -0,0 +1,148 @@
1
+ import * as fs from 'node:fs';
2
+ /**
3
+ * Split a `--data` token into its key, operator and value.
4
+ * Recognizes, in priority order: `:=` (raw json), `@` (file), `=` (string).
5
+ * The operator is the FIRST occurrence scanned left-to-right, but `:=` is
6
+ * detected before a bare `=` at the same position so `k:=1` is raw-json, not
7
+ * a string key `k:` set to `=1`.
8
+ */
9
+ export function splitDataToken(token) {
10
+ // Find the earliest `=` and `@`; `:=` is an `=` preceded by `:`.
11
+ const eq = token.indexOf('=');
12
+ const at = token.indexOf('@');
13
+ // Neither operator present -> invalid.
14
+ if (eq === -1 && at === -1) {
15
+ throw new Error(`Invalid --data '${token}'. Expected key=value, key:=json, or key@file.`);
16
+ }
17
+ // Choose whichever operator appears first. On a tie an '=' can't tie with '@'
18
+ // at the same index, so a simple "smallest index wins" is unambiguous.
19
+ const useAt = at !== -1 && (eq === -1 || at < eq);
20
+ if (useAt) {
21
+ const key = token.slice(0, at);
22
+ if (!key)
23
+ throw new Error(`Invalid --data '${token}': missing key before '@'.`);
24
+ return { key, op: '@', value: token.slice(at + 1) };
25
+ }
26
+ // `=` path — check for the `:=` raw-json form (a ':' immediately before '=').
27
+ if (eq > 0 && token[eq - 1] === ':') {
28
+ const key = token.slice(0, eq - 1);
29
+ if (!key)
30
+ throw new Error(`Invalid --data '${token}': missing key before ':='.`);
31
+ return { key, op: ':=', value: token.slice(eq + 1) };
32
+ }
33
+ const key = token.slice(0, eq);
34
+ if (!key)
35
+ throw new Error(`Invalid --data '${token}': missing key before '='.`);
36
+ return { key, op: '=', value: token.slice(eq + 1) };
37
+ }
38
+ /**
39
+ * Parse `--data` tokens into an object, applying httpie-style typing.
40
+ * Later tokens override earlier ones with the same key.
41
+ */
42
+ export function parseDataPairs(pairs, readFile = defaultReadFile) {
43
+ const out = {};
44
+ for (const token of pairs) {
45
+ const { key, op, value } = splitDataToken(token);
46
+ switch (op) {
47
+ case ':=': {
48
+ try {
49
+ out[key] = JSON.parse(value);
50
+ }
51
+ catch (error) {
52
+ throw new Error(`Invalid JSON for --data '${key}:=': ${error.message}`);
53
+ }
54
+ break;
55
+ }
56
+ case '@': {
57
+ out[key] = readFile(value);
58
+ break;
59
+ }
60
+ default: {
61
+ out[key] = value;
62
+ }
63
+ }
64
+ }
65
+ return out;
66
+ }
67
+ /**
68
+ * Load the `--json` source: inline JSON, `@file`, or `-` (stdin, supplied by
69
+ * the caller via `stdin`). Must parse to a plain object; arrays/scalars are
70
+ * rejected because a function's input is always a keyed object.
71
+ */
72
+ export function loadJsonSource(source, opts = {}) {
73
+ if (source === undefined)
74
+ return undefined;
75
+ const readFile = opts.readFile ?? defaultReadFile;
76
+ let raw;
77
+ if (source === '-') {
78
+ if (!opts.stdin)
79
+ throw new Error('stdin reader not available for --json -');
80
+ raw = opts.stdin();
81
+ }
82
+ else if (source.startsWith('@')) {
83
+ raw = readFile(source.slice(1));
84
+ }
85
+ else {
86
+ raw = source;
87
+ }
88
+ if (raw.trim() === '')
89
+ return undefined;
90
+ let parsed;
91
+ try {
92
+ parsed = JSON.parse(raw);
93
+ }
94
+ catch (error) {
95
+ throw new Error(`Invalid JSON input: ${error.message}`);
96
+ }
97
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
98
+ throw new Error('Function input must be a JSON object (e.g. {"name":"..."}), not an array or scalar.');
99
+ }
100
+ return parsed;
101
+ }
102
+ /**
103
+ * Merge a JSON base with --data overrides into the final input object.
104
+ * Precedence: base first, then dataPairs override (shallow).
105
+ */
106
+ export function assembleInput(opts) {
107
+ return { ...opts.jsonBase, ...opts.dataPairs };
108
+ }
109
+ /**
110
+ * Validate a supplied input object against a function's declared input schema.
111
+ * Non-destructive: returns lists the caller can act on (prompt for missing
112
+ * required params on a TTY, warn on enum mismatches, etc.). The server remains
113
+ * the source of truth, so type mismatches are warnings, not hard failures.
114
+ */
115
+ export function validateAgainstSchema(input, schema) {
116
+ const missingRequired = [];
117
+ const warnings = [];
118
+ if (!schema)
119
+ return { missingRequired, warnings };
120
+ for (const param of schema) {
121
+ const present = Object.hasOwn(input, param.name);
122
+ if (!present) {
123
+ // A required param with no default must be supplied.
124
+ if (param.required && param.default === undefined) {
125
+ missingRequired.push(param);
126
+ }
127
+ continue;
128
+ }
129
+ const value = input[param.name];
130
+ if (value === null && param.nullable === false) {
131
+ warnings.push(`Input '${param.name}' is null but the parameter is not nullable.`);
132
+ }
133
+ // Enum membership check (only for non-null scalar values).
134
+ if (Array.isArray(param.values) && param.values.length > 0 && value !== null && !param.values.includes(value)) {
135
+ warnings.push(`Input '${param.name}' = ${JSON.stringify(value)} is not one of the allowed values: ` +
136
+ param.values.map((v) => JSON.stringify(v)).join(', '));
137
+ }
138
+ }
139
+ return { missingRequired, warnings };
140
+ }
141
+ function defaultReadFile(p) {
142
+ try {
143
+ return fs.readFileSync(p, 'utf8');
144
+ }
145
+ catch (error) {
146
+ throw new Error(`Failed to read file '${p}': ${error.message}`);
147
+ }
148
+ }