@xano/cli 0.0.95-beta.11 → 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.
Files changed (30) hide show
  1. package/dist/commands/sandbox/delete/index.d.ts +12 -0
  2. package/dist/commands/sandbox/delete/index.js +71 -0
  3. package/dist/commands/sandbox/env/delete/index.js +2 -0
  4. package/dist/commands/sandbox/env/get/index.js +2 -0
  5. package/dist/commands/sandbox/env/get_all/index.js +2 -0
  6. package/dist/commands/sandbox/env/list/index.js +2 -0
  7. package/dist/commands/sandbox/env/set/index.js +2 -0
  8. package/dist/commands/sandbox/env/set_all/index.js +2 -0
  9. package/dist/commands/sandbox/get/index.js +2 -0
  10. package/dist/commands/sandbox/license/get/index.js +2 -0
  11. package/dist/commands/sandbox/license/set/index.js +2 -0
  12. package/dist/commands/sandbox/pull/index.js +2 -0
  13. package/dist/commands/sandbox/push/index.d.ts +1 -0
  14. package/dist/commands/sandbox/push/index.js +21 -1
  15. package/dist/commands/sandbox/reset/index.js +2 -0
  16. package/dist/commands/sandbox/review/index.js +2 -0
  17. package/dist/commands/sandbox/unit_test/list/index.js +2 -0
  18. package/dist/commands/sandbox/unit_test/run/index.js +2 -0
  19. package/dist/commands/sandbox/unit_test/run_all/index.js +4 -0
  20. package/dist/commands/sandbox/workflow_test/delete/index.js +2 -0
  21. package/dist/commands/sandbox/workflow_test/get/index.js +2 -0
  22. package/dist/commands/sandbox/workflow_test/list/index.js +2 -0
  23. package/dist/commands/sandbox/workflow_test/run/index.js +2 -0
  24. package/dist/commands/sandbox/workflow_test/run_all/index.js +4 -0
  25. package/dist/commands/workspace/push/index.d.ts +1 -0
  26. package/dist/commands/workspace/push/index.js +19 -1
  27. package/dist/utils/reference-checker.d.ts +12 -0
  28. package/dist/utils/reference-checker.js +97 -2
  29. package/oclif.manifest.json +1981 -1927
  30. package/package.json +1 -1
@@ -0,0 +1,12 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class SandboxDelete extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ private confirm;
12
+ }
@@ -0,0 +1,71 @@
1
+ import { Flags } from '@oclif/core';
2
+ import BaseCommand from '../../../base-command.js';
3
+ export default class SandboxDelete extends BaseCommand {
4
+ static description = 'Delete your sandbox environment completely (debugging only — it will be re-created on next access)';
5
+ static examples = [
6
+ `$ xano sandbox delete
7
+ Are you sure you want to DELETE your sandbox environment? This destroys all data. (y/N) y
8
+ Sandbox environment deleted.
9
+ `,
10
+ `$ xano sandbox delete --force`,
11
+ ];
12
+ static flags = {
13
+ ...BaseCommand.baseFlags,
14
+ force: Flags.boolean({
15
+ char: 'f',
16
+ default: false,
17
+ description: 'Skip confirmation prompt',
18
+ required: false,
19
+ }),
20
+ };
21
+ async run() {
22
+ const { flags } = await this.parse(SandboxDelete);
23
+ const { profile } = this.resolveProfile(flags);
24
+ if (!flags.force) {
25
+ const confirmed = await this.confirm(`Are you sure you want to DELETE your sandbox environment? This destroys all data and the tenant will be re-created on next access.`);
26
+ if (!confirmed) {
27
+ this.log('Delete cancelled.');
28
+ return;
29
+ }
30
+ }
31
+ const apiUrl = `${profile.instance_origin}/api:meta/sandbox/me`;
32
+ try {
33
+ const response = await this.verboseFetch(apiUrl, {
34
+ headers: {
35
+ accept: 'application/json',
36
+ Authorization: `Bearer ${profile.access_token}`,
37
+ 'Content-Type': 'application/json',
38
+ },
39
+ method: 'DELETE',
40
+ }, flags.verbose, profile.access_token);
41
+ if (!response.ok) {
42
+ const message = await this.parseApiError(response, 'Failed to delete sandbox environment');
43
+ this.error(message);
44
+ }
45
+ this.log('Sandbox environment deleted.');
46
+ }
47
+ catch (error) {
48
+ if (error instanceof Error && 'oclif' in error)
49
+ throw error;
50
+ if (error instanceof Error) {
51
+ this.error(`Failed to delete sandbox environment: ${error.message}`);
52
+ }
53
+ else {
54
+ this.error(`Failed to delete sandbox environment: ${String(error)}`);
55
+ }
56
+ }
57
+ }
58
+ async confirm(message) {
59
+ const readline = await import('node:readline');
60
+ const rl = readline.createInterface({
61
+ input: process.stdin,
62
+ output: process.stdout,
63
+ });
64
+ return new Promise((resolve) => {
65
+ rl.question(`${message} (y/N) `, (answer) => {
66
+ rl.close();
67
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
68
+ });
69
+ });
70
+ }
71
+ }
@@ -63,6 +63,8 @@ Environment variable 'DATABASE_URL' deleted
63
63
  }
64
64
  }
65
65
  catch (error) {
66
+ if (error instanceof Error && 'oclif' in error)
67
+ throw error;
66
68
  if (error instanceof Error) {
67
69
  this.error(`Failed to delete sandbox environment variable: ${error.message}`);
68
70
  }
@@ -52,6 +52,8 @@ postgres://localhost:5432/mydb
52
52
  }
53
53
  }
54
54
  catch (error) {
55
+ if (error instanceof Error && 'oclif' in error)
56
+ throw error;
55
57
  if (error instanceof Error) {
56
58
  this.error(`Failed to get sandbox environment variable: ${error.message}`);
57
59
  }
@@ -65,6 +65,8 @@ Environment variables saved to env_<tenant>.yaml
65
65
  }
66
66
  }
67
67
  catch (error) {
68
+ if (error instanceof Error && 'oclif' in error)
69
+ throw error;
68
70
  if (error instanceof Error) {
69
71
  this.error(`Failed to get sandbox environment variables: ${error.message}`);
70
72
  }
@@ -54,6 +54,8 @@ Environment variables for sandbox environment:
54
54
  }
55
55
  }
56
56
  catch (error) {
57
+ if (error instanceof Error && 'oclif' in error)
58
+ throw error;
57
59
  if (error instanceof Error) {
58
60
  this.error(`Failed to list sandbox environment variables: ${error.message}`);
59
61
  }
@@ -61,6 +61,8 @@ Environment variable 'DATABASE_URL' set
61
61
  }
62
62
  }
63
63
  catch (error) {
64
+ if (error instanceof Error && 'oclif' in error)
65
+ throw error;
64
66
  if (error instanceof Error) {
65
67
  this.error(`Failed to set sandbox environment variable: ${error.message}`);
66
68
  }
@@ -73,6 +73,8 @@ Reads from env_<tenant>.yaml
73
73
  }
74
74
  }
75
75
  catch (error) {
76
+ if (error instanceof Error && 'oclif' in error)
77
+ throw error;
76
78
  if (error instanceof Error) {
77
79
  this.error(`Failed to set sandbox environment variables: ${error.message}`);
78
80
  }
@@ -50,6 +50,8 @@ Sandbox Environment: (tc24-abcd-x1y2)
50
50
  }
51
51
  }
52
52
  catch (error) {
53
+ if (error instanceof Error && 'oclif' in error)
54
+ throw error;
53
55
  if (error instanceof Error) {
54
56
  this.error(`Failed to get sandbox environment: ${error.message}`);
55
57
  }
@@ -65,6 +65,8 @@ License saved to license_<tenant>.yaml
65
65
  }
66
66
  }
67
67
  catch (error) {
68
+ if (error instanceof Error && 'oclif' in error)
69
+ throw error;
68
70
  if (error instanceof Error) {
69
71
  this.error(`Failed to get sandbox environment license: ${error.message}`);
70
72
  }
@@ -82,6 +82,8 @@ Reads from license_<tenant>.yaml
82
82
  }
83
83
  }
84
84
  catch (error) {
85
+ if (error instanceof Error && 'oclif' in error)
86
+ throw error;
85
87
  if (error instanceof Error) {
86
88
  this.error(`Failed to set sandbox environment license: ${error.message}`);
87
89
  }
@@ -61,6 +61,8 @@ Pulled 42 documents from sandbox environment to ./my-sandbox
61
61
  responseText = await response.text();
62
62
  }
63
63
  catch (error) {
64
+ if (error instanceof Error && 'oclif' in error)
65
+ throw error;
64
66
  if (error instanceof Error) {
65
67
  this.error(`Failed to fetch multidoc: ${error.message}`);
66
68
  }
@@ -15,5 +15,6 @@ export default class SandboxPush extends BaseCommand {
15
15
  };
16
16
  run(): Promise<void>;
17
17
  private collectFiles;
18
+ private renderBadIndexes;
18
19
  private renderBadReferences;
19
20
  }
@@ -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(),
@@ -124,6 +129,8 @@ Pushed 42 documents to sandbox environment from ./my-workspace
124
129
  }
125
130
  }
126
131
  catch (error) {
132
+ if (error instanceof Error && 'oclif' in error)
133
+ throw error;
127
134
  if (error instanceof Error) {
128
135
  this.error(`Failed to push multidoc: ${error.message}`);
129
136
  }
@@ -148,6 +155,19 @@ Pushed 42 documents to sandbox environment from ./my-workspace
148
155
  }
149
156
  return files.sort();
150
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
+ }
151
171
  renderBadReferences(badRefs) {
152
172
  this.log('');
153
173
  this.log(ux.colorize('yellow', ux.colorize('bold', '=== Unresolved References ===')));
@@ -45,6 +45,8 @@ Sandbox environment has been reset.
45
45
  this.log('Sandbox environment has been reset.');
46
46
  }
47
47
  catch (error) {
48
+ if (error instanceof Error && 'oclif' in error)
49
+ throw error;
48
50
  if (error instanceof Error) {
49
51
  this.error(`Failed to reset sandbox environment: ${error.message}`);
50
52
  }
@@ -68,6 +68,8 @@ Review session started!
68
68
  process.exit(0);
69
69
  }
70
70
  catch (error) {
71
+ if (error instanceof Error && 'oclif' in error)
72
+ throw error;
71
73
  if (error instanceof Error) {
72
74
  this.error(`Failed to open sandbox review: ${error.message}`);
73
75
  }
@@ -78,6 +78,8 @@ Unit tests:
78
78
  }
79
79
  }
80
80
  catch (error) {
81
+ if (error instanceof Error && 'oclif' in error)
82
+ throw error;
81
83
  if (error instanceof Error) {
82
84
  this.error(`Failed to list unit tests: ${error.message}`);
83
85
  }
@@ -66,6 +66,8 @@ Result: PASS
66
66
  }
67
67
  }
68
68
  catch (error) {
69
+ if (error instanceof Error && 'oclif' in error)
70
+ throw error;
69
71
  if (error instanceof Error) {
70
72
  this.error(`Failed to run unit test: ${error.message}`);
71
73
  }
@@ -128,6 +128,8 @@ Results: 4 passed, 1 failed
128
128
  }
129
129
  }
130
130
  catch (error) {
131
+ if (error instanceof Error && 'oclif' in error)
132
+ throw error;
131
133
  const message = error instanceof Error ? error.message : String(error);
132
134
  results.push({
133
135
  message,
@@ -156,6 +158,8 @@ Results: 4 passed, 1 failed
156
158
  }
157
159
  }
158
160
  catch (error) {
161
+ if (error instanceof Error && 'oclif' in error)
162
+ throw error;
159
163
  if (error instanceof Error) {
160
164
  this.error(`Failed to run unit tests: ${error.message}`);
161
165
  }
@@ -48,6 +48,8 @@ Deleted workflow test 42
48
48
  }
49
49
  }
50
50
  catch (error) {
51
+ if (error instanceof Error && 'oclif' in error)
52
+ throw error;
51
53
  if (error instanceof Error) {
52
54
  this.error(`Failed to delete workflow test: ${error.message}`);
53
55
  }
@@ -47,6 +47,8 @@ export default class SandboxWorkflowTestGet extends BaseCommand {
47
47
  }
48
48
  }
49
49
  catch (error) {
50
+ if (error instanceof Error && 'oclif' in error)
51
+ throw error;
50
52
  if (error instanceof Error) {
51
53
  this.error(`Failed to get workflow test: ${error.message}`);
52
54
  }
@@ -71,6 +71,8 @@ Workflow tests:
71
71
  }
72
72
  }
73
73
  catch (error) {
74
+ if (error instanceof Error && 'oclif' in error)
75
+ throw error;
74
76
  if (error instanceof Error) {
75
77
  this.error(`Failed to list workflow tests: ${error.message}`);
76
78
  }
@@ -64,6 +64,8 @@ Result: PASS (0.25s)
64
64
  }
65
65
  }
66
66
  catch (error) {
67
+ if (error instanceof Error && 'oclif' in error)
68
+ throw error;
67
69
  if (error instanceof Error) {
68
70
  this.error(`Failed to run workflow test: ${error.message}`);
69
71
  }
@@ -116,6 +116,8 @@ Results: 2 passed, 1 failed
116
116
  }
117
117
  }
118
118
  catch (error) {
119
+ if (error instanceof Error && 'oclif' in error)
120
+ throw error;
119
121
  const message = error instanceof Error ? error.message : String(error);
120
122
  results.push({
121
123
  message,
@@ -142,6 +144,8 @@ Results: 2 passed, 1 failed
142
144
  }
143
145
  }
144
146
  catch (error) {
147
+ if (error instanceof Error && 'oclif' in error)
148
+ throw error;
145
149
  if (error instanceof Error) {
146
150
  this.error(`Failed to run workflow tests: ${error.message}`);
147
151
  }
@@ -30,6 +30,7 @@ export default class Push extends BaseCommand {
30
30
  * type subdirectory name then filename for deterministic ordering.
31
31
  */
32
32
  private collectFiles;
33
+ private renderBadIndexes;
33
34
  private renderBadReferences;
34
35
  private loadCredentials;
35
36
  }
@@ -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[];
@@ -20,6 +20,14 @@ const REFERENCE_PATTERNS = [
20
20
  regex: /^\s*workflow_test\.call\s+("(?:[^"\\]|\\.)*"|[^\s{]+)/gm,
21
21
  targetType: 'workflow_test',
22
22
  },
23
+ // db.* statements reference tables: db.get, db.query, db.add, db.edit, db.add_or_edit, db.delete, db.bulk_add, db.bulk_delete, db.count
24
+ {
25
+ keyword: 'db.*',
26
+ regex: /^\s*db\.(?:get|query|add|edit|add_or_edit|delete|bulk_add|bulk_delete|count)\s+("(?:[^"\\]|\\.)*"|[^\s{]+)/gm,
27
+ targetType: 'table',
28
+ },
29
+ // Schema foreign key references: table = "name" inside field definitions
30
+ { keyword: 'table (FK)', regex: /\btable\s*=\s*"([^"]*)"/gm, targetType: 'table' },
23
31
  ];
24
32
  /**
25
33
  * Strip surrounding quotes from a name if present.
@@ -116,8 +124,8 @@ export function checkReferences(documents, serverOperations) {
116
124
  let match;
117
125
  while ((match = pattern.regex.exec(doc.content)) !== null) {
118
126
  const rawName = stripQuotes(match[1]);
119
- // Skip empty names (e.g., action.call "" is valid for integration actions)
120
- if (!rawName)
127
+ // Skip empty names only for action.call (valid for integration actions)
128
+ if (!rawName && pattern.keyword === 'action.call')
121
129
  continue;
122
130
  const { targetType } = pattern;
123
131
  const knownNames = registry.get(targetType);
@@ -135,3 +143,90 @@ export function checkReferences(documents, serverOperations) {
135
143
  }
136
144
  return badRefs;
137
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
+ }