@xano/cli 0.0.64 → 0.0.66
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/README.md +25 -0
- package/dist/base-command.d.ts +25 -0
- package/dist/base-command.js +53 -11
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +23 -16
- package/dist/commands/function/edit/index.js +17 -18
- package/dist/commands/function/get/index.js +11 -11
- package/dist/commands/profile/create/index.d.ts +1 -0
- package/dist/commands/profile/create/index.js +10 -0
- package/dist/commands/profile/edit/index.d.ts +2 -0
- package/dist/commands/profile/edit/index.js +23 -1
- package/dist/commands/profile/list/index.js +3 -0
- package/dist/commands/profile/wizard/index.d.ts +2 -0
- package/dist/commands/profile/wizard/index.js +23 -12
- package/dist/commands/release/export/index.js +14 -13
- package/dist/commands/release/pull/index.d.ts +0 -6
- package/dist/commands/release/pull/index.js +15 -62
- package/dist/commands/release/push/index.js +16 -6
- package/dist/commands/tenant/backup/export/index.js +4 -2
- package/dist/commands/tenant/create/index.js +3 -0
- package/dist/commands/tenant/deploy_platform/index.js +1 -0
- package/dist/commands/tenant/deploy_release/index.js +1 -0
- package/dist/commands/tenant/pull/index.d.ts +0 -6
- package/dist/commands/tenant/pull/index.js +9 -56
- package/dist/commands/tenant/push/index.js +16 -6
- package/dist/commands/workspace/git/pull/index.js +9 -8
- package/dist/commands/workspace/pull/index.js +9 -6
- package/dist/commands/workspace/push/index.js +10 -1
- package/dist/utils/document-parser.d.ts +22 -0
- package/dist/utils/document-parser.js +54 -1
- package/oclif.manifest.json +992 -952
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import * as os from 'node:os';
|
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import snakeCase from 'lodash.snakecase';
|
|
7
7
|
import BaseCommand from '../../../base-command.js';
|
|
8
|
-
import { parseDocument } from '../../../utils/document-parser.js';
|
|
8
|
+
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
9
9
|
export default class Pull extends BaseCommand {
|
|
10
10
|
static args = {
|
|
11
11
|
directory: Args.string({
|
|
@@ -149,6 +149,9 @@ Pulled 42 documents to ./my-workspace
|
|
|
149
149
|
const outputDir = path.resolve(args.directory);
|
|
150
150
|
// Create the output directory if it doesn't exist
|
|
151
151
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
152
|
+
// Resolve api_group names to unique folder names, disambiguating collisions
|
|
153
|
+
// where different names produce the same snakeCase (e.g., "Authentication" vs "authentication")
|
|
154
|
+
const getApiGroupFolder = buildApiGroupFolderResolver(documents, snakeCase);
|
|
152
155
|
// Track filenames per type to handle duplicates
|
|
153
156
|
const filenameCounters = new Map();
|
|
154
157
|
let writtenCount = 0;
|
|
@@ -206,14 +209,14 @@ Pulled 42 documents to ./my-workspace
|
|
|
206
209
|
baseName = this.sanitizeFilename(doc.name);
|
|
207
210
|
}
|
|
208
211
|
else if (doc.type === 'api_group') {
|
|
209
|
-
// api_group "test" → api/
|
|
210
|
-
const groupFolder =
|
|
212
|
+
// api_group "test" → api/{resolved_folder}/{name}.xs
|
|
213
|
+
const groupFolder = getApiGroupFolder(doc.name);
|
|
211
214
|
typeDir = path.join(outputDir, 'api', groupFolder);
|
|
212
|
-
baseName =
|
|
215
|
+
baseName = this.sanitizeFilename(doc.name);
|
|
213
216
|
}
|
|
214
217
|
else if (doc.type === 'query' && doc.apiGroup) {
|
|
215
|
-
// query in group "test" → api/
|
|
216
|
-
const groupFolder =
|
|
218
|
+
// query in group "test" → api/{resolved_folder}/{query_name}.xs
|
|
219
|
+
const groupFolder = getApiGroupFolder(doc.apiGroup);
|
|
217
220
|
const nameParts = doc.name.split('/');
|
|
218
221
|
const leafName = nameParts.pop();
|
|
219
222
|
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
@@ -4,7 +4,7 @@ import * as fs from 'node:fs';
|
|
|
4
4
|
import * as os from 'node:os';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import BaseCommand from '../../../base-command.js';
|
|
7
|
-
import { buildDocumentKey, parseDocument } from '../../../utils/document-parser.js';
|
|
7
|
+
import { buildDocumentKey, findFilesWithGuid, parseDocument } from '../../../utils/document-parser.js';
|
|
8
8
|
export default class Push extends BaseCommand {
|
|
9
9
|
static args = {
|
|
10
10
|
directory: Args.string({
|
|
@@ -201,6 +201,15 @@ Push schema only, skip records and environment variables
|
|
|
201
201
|
catch {
|
|
202
202
|
errorMessage += `\n${errorText}`;
|
|
203
203
|
}
|
|
204
|
+
// Surface local files involved in duplicate GUID errors
|
|
205
|
+
const guidMatch = errorMessage.match(/Duplicate \w+ guid: (\S+)/);
|
|
206
|
+
if (guidMatch) {
|
|
207
|
+
const dupeFiles = findFilesWithGuid(documentEntries, guidMatch[1]);
|
|
208
|
+
if (dupeFiles.length > 0) {
|
|
209
|
+
const relPaths = dupeFiles.map((f) => path.relative(inputDir, f));
|
|
210
|
+
errorMessage += `\n Local files with this GUID:\n${relPaths.map((f) => ` ${f}`).join('\n')}`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
204
213
|
this.error(errorMessage);
|
|
205
214
|
}
|
|
206
215
|
// Parse the response for GUID map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export interface ParsedDocument {
|
|
2
2
|
apiGroup?: string;
|
|
3
|
+
canonical?: string;
|
|
3
4
|
content: string;
|
|
5
|
+
guid?: string;
|
|
4
6
|
name: string;
|
|
5
7
|
type: string;
|
|
6
8
|
verb?: string;
|
|
@@ -15,3 +17,23 @@ export declare function parseDocument(content: string): null | ParsedDocument;
|
|
|
15
17
|
* Used to match server GUID map entries back to local files.
|
|
16
18
|
*/
|
|
17
19
|
export declare function buildDocumentKey(type: string, name: string, verb?: string, apiGroup?: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Build a map of api_group name → unique folder name for a set of documents.
|
|
22
|
+
*
|
|
23
|
+
* When two api_groups produce the same snakeCase folder (e.g., "Authentication" and
|
|
24
|
+
* "authentication" both → "authentication"), the first group keeps the base name
|
|
25
|
+
* and subsequent groups get a numeric suffix (authentication_2, authentication_3, etc.).
|
|
26
|
+
*
|
|
27
|
+
* @param documents - Parsed documents (only api_group type docs are considered)
|
|
28
|
+
* @param snakeCaseFn - The snakeCase function to use for folder name generation
|
|
29
|
+
* @returns A function that resolves an api_group name to its unique folder name
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildApiGroupFolderResolver(documents: ParsedDocument[], snakeCaseFn: (s: string) => string): (groupName: string) => string;
|
|
32
|
+
/**
|
|
33
|
+
* Find local .xs files that contain a specific GUID.
|
|
34
|
+
* Used to surface which files are involved when the server reports a duplicate GUID error.
|
|
35
|
+
*/
|
|
36
|
+
export declare function findFilesWithGuid(entries: Array<{
|
|
37
|
+
content: string;
|
|
38
|
+
filePath: string;
|
|
39
|
+
}>, guid: string): string[];
|
|
@@ -45,7 +45,19 @@ export function parseDocument(content) {
|
|
|
45
45
|
if (apiGroupMatch) {
|
|
46
46
|
apiGroup = apiGroupMatch[1];
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
// Extract canonical if present (e.g., canonical = "abc123")
|
|
49
|
+
let canonical;
|
|
50
|
+
const canonicalMatch = content.match(/canonical\s*=\s*"([^"]*)"/);
|
|
51
|
+
if (canonicalMatch) {
|
|
52
|
+
canonical = canonicalMatch[1];
|
|
53
|
+
}
|
|
54
|
+
// Extract guid if present (e.g., guid = "abc123")
|
|
55
|
+
let guid;
|
|
56
|
+
const guidMatch = content.match(/guid\s*=\s*"([^"]*)"/);
|
|
57
|
+
if (guidMatch) {
|
|
58
|
+
guid = guidMatch[1];
|
|
59
|
+
}
|
|
60
|
+
return { apiGroup, canonical, content, guid, name, type, verb };
|
|
49
61
|
}
|
|
50
62
|
/**
|
|
51
63
|
* Build a unique key for a document based on its type, name, verb, and api_group.
|
|
@@ -59,3 +71,44 @@ export function buildDocumentKey(type, name, verb, apiGroup) {
|
|
|
59
71
|
parts.push(apiGroup);
|
|
60
72
|
return parts.join(':');
|
|
61
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Build a map of api_group name → unique folder name for a set of documents.
|
|
76
|
+
*
|
|
77
|
+
* When two api_groups produce the same snakeCase folder (e.g., "Authentication" and
|
|
78
|
+
* "authentication" both → "authentication"), the first group keeps the base name
|
|
79
|
+
* and subsequent groups get a numeric suffix (authentication_2, authentication_3, etc.).
|
|
80
|
+
*
|
|
81
|
+
* @param documents - Parsed documents (only api_group type docs are considered)
|
|
82
|
+
* @param snakeCaseFn - The snakeCase function to use for folder name generation
|
|
83
|
+
* @returns A function that resolves an api_group name to its unique folder name
|
|
84
|
+
*/
|
|
85
|
+
export function buildApiGroupFolderResolver(documents, snakeCaseFn) {
|
|
86
|
+
const apiGroupFolderMap = new Map();
|
|
87
|
+
const folderClaims = new Map();
|
|
88
|
+
for (const doc of documents) {
|
|
89
|
+
if (doc.type !== 'api_group')
|
|
90
|
+
continue;
|
|
91
|
+
const folder = snakeCaseFn(doc.name);
|
|
92
|
+
const names = folderClaims.get(folder) ?? [];
|
|
93
|
+
if (!names.includes(doc.name)) {
|
|
94
|
+
names.push(doc.name);
|
|
95
|
+
}
|
|
96
|
+
folderClaims.set(folder, names);
|
|
97
|
+
}
|
|
98
|
+
for (const [folder, names] of folderClaims) {
|
|
99
|
+
apiGroupFolderMap.set(names[0], folder);
|
|
100
|
+
for (let i = 1; i < names.length; i++) {
|
|
101
|
+
apiGroupFolderMap.set(names[i], `${folder}_${i + 1}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return (groupName) => {
|
|
105
|
+
return apiGroupFolderMap.get(groupName) ?? snakeCaseFn(groupName);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Find local .xs files that contain a specific GUID.
|
|
110
|
+
* Used to surface which files are involved when the server reports a duplicate GUID error.
|
|
111
|
+
*/
|
|
112
|
+
export function findFilesWithGuid(entries, guid) {
|
|
113
|
+
return entries.filter((e) => e.content.includes(guid)).map((e) => e.filePath);
|
|
114
|
+
}
|