@xano/cli 0.0.25 → 0.0.27

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.
@@ -3,6 +3,7 @@ import * as yaml from 'js-yaml';
3
3
  import * as fs from 'node:fs';
4
4
  import * as os from 'node:os';
5
5
  import * as path from 'node:path';
6
+ import snakeCase from 'lodash.snakecase';
6
7
  import BaseCommand from '../../../base-command.js';
7
8
  export default class Pull extends BaseCommand {
8
9
  static args = {
@@ -130,19 +131,69 @@ Pulled 58 documents to ./backup
130
131
  const filenameCounters = new Map();
131
132
  let writtenCount = 0;
132
133
  for (const doc of documents) {
133
- // Create the type subdirectory
134
- const typeDir = path.join(outputDir, doc.type);
135
- fs.mkdirSync(typeDir, { recursive: true });
136
- // Build the base filename
137
- let baseName = this.sanitizeFilename(doc.name);
138
- if (doc.verb) {
139
- baseName = `${baseName}_${doc.verb}`;
134
+ let typeDir;
135
+ let baseName;
136
+ if (doc.type === 'workspace') {
137
+ // workspace workspace/{name}.xs
138
+ typeDir = path.join(outputDir, 'workspace');
139
+ baseName = this.sanitizeFilename(doc.name);
140
+ }
141
+ else if (doc.type === 'workspace_trigger') {
142
+ // workspace_trigger → workspace/trigger/{name}.xs
143
+ typeDir = path.join(outputDir, 'workspace', 'trigger');
144
+ baseName = this.sanitizeFilename(doc.name);
145
+ }
146
+ else if (doc.type === 'agent_trigger') {
147
+ // agent_trigger → agent/trigger/{name}.xs
148
+ typeDir = path.join(outputDir, 'agent', 'trigger');
149
+ baseName = this.sanitizeFilename(doc.name);
150
+ }
151
+ else if (doc.type === 'mcp_server_trigger') {
152
+ // mcp_server_trigger → mcp_server/trigger/{name}.xs
153
+ typeDir = path.join(outputDir, 'mcp_server', 'trigger');
154
+ baseName = this.sanitizeFilename(doc.name);
155
+ }
156
+ else if (doc.type === 'table_trigger') {
157
+ // table_trigger → table/trigger/{name}.xs
158
+ typeDir = path.join(outputDir, 'table', 'trigger');
159
+ baseName = this.sanitizeFilename(doc.name);
160
+ }
161
+ else if (doc.type === 'api_group') {
162
+ // api_group "test" → api/test/api_group.xs
163
+ const groupFolder = snakeCase(doc.name);
164
+ typeDir = path.join(outputDir, 'api', groupFolder);
165
+ baseName = 'api_group';
140
166
  }
141
- // Track duplicates per type directory
142
- if (!filenameCounters.has(doc.type)) {
143
- filenameCounters.set(doc.type, new Map());
167
+ else if (doc.type === 'query' && doc.apiGroup) {
168
+ // query in group "test" → api/test/{query_name}.xs
169
+ const groupFolder = snakeCase(doc.apiGroup);
170
+ const nameParts = doc.name.split('/');
171
+ const leafName = nameParts.pop();
172
+ const folderParts = nameParts.map((part) => snakeCase(part));
173
+ typeDir = path.join(outputDir, 'api', groupFolder, ...folderParts);
174
+ baseName = this.sanitizeFilename(leafName);
175
+ if (doc.verb) {
176
+ baseName = `${baseName}_${doc.verb}`;
177
+ }
144
178
  }
145
- const typeCounters = filenameCounters.get(doc.type);
179
+ else {
180
+ // Default: split folder path from name
181
+ const nameParts = doc.name.split('/');
182
+ const leafName = nameParts.pop();
183
+ const folderParts = nameParts.map((part) => snakeCase(part));
184
+ typeDir = path.join(outputDir, doc.type, ...folderParts);
185
+ baseName = this.sanitizeFilename(leafName);
186
+ if (doc.verb) {
187
+ baseName = `${baseName}_${doc.verb}`;
188
+ }
189
+ }
190
+ fs.mkdirSync(typeDir, { recursive: true });
191
+ // Track duplicates per directory
192
+ const dirKey = path.relative(outputDir, typeDir);
193
+ if (!filenameCounters.has(dirKey)) {
194
+ filenameCounters.set(dirKey, new Map());
195
+ }
196
+ const typeCounters = filenameCounters.get(dirKey);
146
197
  const count = typeCounters.get(baseName) || 0;
147
198
  typeCounters.set(baseName, count + 1);
148
199
  // Append numeric suffix for duplicates
@@ -217,7 +268,13 @@ Pulled 58 documents to ./backup
217
268
  if (verbMatch) {
218
269
  verb = verbMatch[1];
219
270
  }
220
- return { content, name, type, verb };
271
+ // Extract api_group if present (e.g., api_group = "test")
272
+ let apiGroup;
273
+ const apiGroupMatch = content.match(/api_group\s*=\s*"([^"]*)"/);
274
+ if (apiGroupMatch) {
275
+ apiGroup = apiGroupMatch[1];
276
+ }
277
+ return { apiGroup, content, name, type, verb };
221
278
  }
222
279
  /**
223
280
  * Sanitize a document name for use as a filename.
@@ -225,9 +282,6 @@ Pulled 58 documents to ./backup
225
282
  * characters that are unsafe in filenames.
226
283
  */
227
284
  sanitizeFilename(name) {
228
- return name
229
- .replaceAll('"', '')
230
- .replaceAll(/\s+/g, '_')
231
- .replaceAll(/[<>:"/\\|?*]/g, '_');
285
+ return snakeCase(name.replaceAll('"', ''));
232
286
  }
233
287
  }
@@ -107,7 +107,7 @@ Pushed 58 documents from ./backup
107
107
  }
108
108
  // Log the response if any
109
109
  const responseText = await response.text();
110
- if (responseText) {
110
+ if (responseText && responseText !== 'null') {
111
111
  this.log(responseText);
112
112
  }
113
113
  }