@xano/cli 0.0.95-beta.12 → 0.0.95-beta.13
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/dist/commands/sandbox/push/index.d.ts +1 -0
- package/dist/commands/sandbox/push/index.js +19 -1
- package/dist/commands/workspace/push/index.d.ts +1 -0
- package/dist/commands/workspace/push/index.js +19 -1
- package/dist/utils/reference-checker.d.ts +12 -0
- package/dist/utils/reference-checker.js +87 -0
- package/oclif.manifest.json +1427 -1427
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../base-command.js';
|
|
5
5
|
import { findFilesWithGuid } from '../../../utils/document-parser.js';
|
|
6
|
-
import { checkReferences } from '../../../utils/reference-checker.js';
|
|
6
|
+
import { checkReferences, checkTableIndexes } from '../../../utils/reference-checker.js';
|
|
7
7
|
export default class SandboxPush extends BaseCommand {
|
|
8
8
|
static args = {
|
|
9
9
|
directory: Args.string({
|
|
@@ -72,6 +72,11 @@ Pushed 42 documents to sandbox environment from ./my-workspace
|
|
|
72
72
|
if (badRefs.length > 0) {
|
|
73
73
|
this.renderBadReferences(badRefs);
|
|
74
74
|
}
|
|
75
|
+
// Check for indexes referencing non-existent schema fields
|
|
76
|
+
const badIndexes = checkTableIndexes(documentEntries);
|
|
77
|
+
if (badIndexes.length > 0) {
|
|
78
|
+
this.renderBadIndexes(badIndexes);
|
|
79
|
+
}
|
|
75
80
|
const multidoc = documentEntries.map((d) => d.content).join('\n---\n');
|
|
76
81
|
const queryParams = new URLSearchParams({
|
|
77
82
|
env: flags.env.toString(),
|
|
@@ -150,6 +155,19 @@ Pushed 42 documents to sandbox environment from ./my-workspace
|
|
|
150
155
|
}
|
|
151
156
|
return files.sort();
|
|
152
157
|
}
|
|
158
|
+
renderBadIndexes(badIndexes) {
|
|
159
|
+
this.log('');
|
|
160
|
+
this.log(ux.colorize('red', ux.colorize('bold', '=== CRITICAL: Invalid Indexes ===')));
|
|
161
|
+
this.log('');
|
|
162
|
+
this.log(ux.colorize('red', 'The following tables have indexes referencing fields that do not exist in the schema.'));
|
|
163
|
+
this.log(ux.colorize('red', 'These will cause the import to fail.'));
|
|
164
|
+
this.log('');
|
|
165
|
+
for (const idx of badIndexes) {
|
|
166
|
+
this.log(` ${ux.colorize('red', 'CRITICAL'.padEnd(16))} ${'table'.padEnd(18)} ${idx.table}`);
|
|
167
|
+
this.log(` ${' '.repeat(16)} ${' '.repeat(18)} ${ux.colorize('dim', `${idx.indexType} index → field "${idx.field}" does not exist in schema`)}`);
|
|
168
|
+
}
|
|
169
|
+
this.log('');
|
|
170
|
+
}
|
|
153
171
|
renderBadReferences(badRefs) {
|
|
154
172
|
this.log('');
|
|
155
173
|
this.log(ux.colorize('yellow', ux.colorize('bold', '=== Unresolved References ===')));
|
|
@@ -6,7 +6,7 @@ import * as os from 'node:os';
|
|
|
6
6
|
import * as path from 'node:path';
|
|
7
7
|
import BaseCommand from '../../../base-command.js';
|
|
8
8
|
import { buildDocumentKey, findFilesWithGuid, parseDocument } from '../../../utils/document-parser.js';
|
|
9
|
-
import { checkReferences } from '../../../utils/reference-checker.js';
|
|
9
|
+
import { checkReferences, checkTableIndexes } from '../../../utils/reference-checker.js';
|
|
10
10
|
export default class Push extends BaseCommand {
|
|
11
11
|
static args = {
|
|
12
12
|
directory: Args.string({
|
|
@@ -331,6 +331,11 @@ Push functions but exclude test files
|
|
|
331
331
|
if (badRefs.length > 0) {
|
|
332
332
|
this.renderBadReferences(badRefs);
|
|
333
333
|
}
|
|
334
|
+
// Check for indexes referencing non-existent schema fields
|
|
335
|
+
const badIndexes = checkTableIndexes(documentEntries);
|
|
336
|
+
if (badIndexes.length > 0) {
|
|
337
|
+
this.renderBadIndexes(badIndexes);
|
|
338
|
+
}
|
|
334
339
|
// Check for critical errors that must block the push
|
|
335
340
|
const criticalOps = preview.operations.filter((op) => op.details?.includes('exception:') || op.details?.includes('mvp:placeholder'));
|
|
336
341
|
if (criticalOps.length > 0) {
|
|
@@ -751,6 +756,19 @@ Push functions but exclude test files
|
|
|
751
756
|
}
|
|
752
757
|
return files.sort();
|
|
753
758
|
}
|
|
759
|
+
renderBadIndexes(badIndexes) {
|
|
760
|
+
this.log('');
|
|
761
|
+
this.log(ux.colorize('red', ux.colorize('bold', '=== CRITICAL: Invalid Indexes ===')));
|
|
762
|
+
this.log('');
|
|
763
|
+
this.log(ux.colorize('red', 'The following tables have indexes referencing fields that do not exist in the schema.'));
|
|
764
|
+
this.log(ux.colorize('red', 'These will cause the import to fail.'));
|
|
765
|
+
this.log('');
|
|
766
|
+
for (const idx of badIndexes) {
|
|
767
|
+
this.log(` ${ux.colorize('red', 'CRITICAL'.padEnd(16))} ${'table'.padEnd(18)} ${idx.table}`);
|
|
768
|
+
this.log(` ${' '.repeat(16)} ${' '.repeat(18)} ${ux.colorize('dim', `${idx.indexType} index → field "${idx.field}" does not exist in schema`)}`);
|
|
769
|
+
}
|
|
770
|
+
this.log('');
|
|
771
|
+
}
|
|
754
772
|
renderBadReferences(badRefs) {
|
|
755
773
|
this.log(ux.colorize('yellow', ux.colorize('bold', '=== Unresolved References ===')));
|
|
756
774
|
this.log('');
|
|
@@ -43,3 +43,15 @@ export declare function checkReferences(documents: Array<{
|
|
|
43
43
|
name: string;
|
|
44
44
|
type: string;
|
|
45
45
|
}>): BadReference[];
|
|
46
|
+
export interface BadIndex {
|
|
47
|
+
field: string;
|
|
48
|
+
indexType: string;
|
|
49
|
+
table: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check table documents for indexes that reference fields not in the schema.
|
|
53
|
+
* Parses the XanoScript table format to extract schema field names and index field names.
|
|
54
|
+
*/
|
|
55
|
+
export declare function checkTableIndexes(documents: Array<{
|
|
56
|
+
content: string;
|
|
57
|
+
}>): BadIndex[];
|
|
@@ -143,3 +143,90 @@ export function checkReferences(documents, serverOperations) {
|
|
|
143
143
|
}
|
|
144
144
|
return badRefs;
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Check table documents for indexes that reference fields not in the schema.
|
|
148
|
+
* Parses the XanoScript table format to extract schema field names and index field names.
|
|
149
|
+
*/
|
|
150
|
+
export function checkTableIndexes(documents) {
|
|
151
|
+
const badIndexes = [];
|
|
152
|
+
for (const doc of documents) {
|
|
153
|
+
const parsed = parseDocument(doc.content);
|
|
154
|
+
if (!parsed || parsed.type !== 'table')
|
|
155
|
+
continue;
|
|
156
|
+
const schemaFields = extractSchemaFields(doc.content);
|
|
157
|
+
const indexes = extractIndexes(doc.content);
|
|
158
|
+
for (const idx of indexes) {
|
|
159
|
+
for (const field of idx.fields) {
|
|
160
|
+
if (!schemaFields.has(field)) {
|
|
161
|
+
badIndexes.push({
|
|
162
|
+
field,
|
|
163
|
+
indexType: idx.type,
|
|
164
|
+
table: parsed.name,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return badIndexes;
|
|
171
|
+
}
|
|
172
|
+
function extractSchemaFields(content) {
|
|
173
|
+
// id and created_at are auto-added during import
|
|
174
|
+
const fields = new Set(['id', 'created_at']);
|
|
175
|
+
// Find the schema block by matching braces
|
|
176
|
+
const schemaStart = content.match(/\bschema\s*\{/);
|
|
177
|
+
if (!schemaStart || schemaStart.index === undefined)
|
|
178
|
+
return fields;
|
|
179
|
+
let depth = 0;
|
|
180
|
+
let blockStart = -1;
|
|
181
|
+
let blockEnd = -1;
|
|
182
|
+
for (let i = schemaStart.index; i < content.length; i++) {
|
|
183
|
+
if (content[i] === '{') {
|
|
184
|
+
if (depth === 0)
|
|
185
|
+
blockStart = i + 1;
|
|
186
|
+
depth++;
|
|
187
|
+
}
|
|
188
|
+
else if (content[i] === '}') {
|
|
189
|
+
depth--;
|
|
190
|
+
if (depth === 0) {
|
|
191
|
+
blockEnd = i;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (blockStart < 0 || blockEnd < 0)
|
|
197
|
+
return fields;
|
|
198
|
+
const schemaBlock = content.slice(blockStart, blockEnd);
|
|
199
|
+
// Match field declarations: "type name" or "type name?" or "type name?=default"
|
|
200
|
+
const fieldRegex = /^\s*\w+\s+(\w+)[?\s{]/gm;
|
|
201
|
+
let match;
|
|
202
|
+
while ((match = fieldRegex.exec(schemaBlock)) !== null) {
|
|
203
|
+
fields.add(match[1]);
|
|
204
|
+
}
|
|
205
|
+
return fields;
|
|
206
|
+
}
|
|
207
|
+
function extractIndexes(content) {
|
|
208
|
+
const indexes = [];
|
|
209
|
+
// Match the index array: index = [ ... ]
|
|
210
|
+
const indexMatch = content.match(/\bindex\s*=\s*\[([\s\S]*?)\n\s*\]/);
|
|
211
|
+
if (!indexMatch)
|
|
212
|
+
return indexes;
|
|
213
|
+
const indexBlock = indexMatch[1];
|
|
214
|
+
// Match each index object: {type: "btree", field: [{name: "col", op: "desc"}]}
|
|
215
|
+
const entryRegex = /\{([^}]+)\}/g;
|
|
216
|
+
let match;
|
|
217
|
+
while ((match = entryRegex.exec(indexBlock)) !== null) {
|
|
218
|
+
const entry = match[1];
|
|
219
|
+
const typeMatch = entry.match(/type:\s*"(\w+)"/);
|
|
220
|
+
const type = typeMatch ? typeMatch[1] : 'unknown';
|
|
221
|
+
const fieldNames = [];
|
|
222
|
+
const nameRegex = /name:\s*"(\w*)"/g;
|
|
223
|
+
let nameMatch;
|
|
224
|
+
while ((nameMatch = nameRegex.exec(entry)) !== null) {
|
|
225
|
+
fieldNames.push(nameMatch[1]);
|
|
226
|
+
}
|
|
227
|
+
if (fieldNames.length > 0) {
|
|
228
|
+
indexes.push({ fields: fieldNames, type });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return indexes;
|
|
232
|
+
}
|