@xano/cli 1.2.0 → 1.3.0
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 +112 -3
- package/dist/base-command.d.ts +43 -36
- package/dist/base-command.js +223 -160
- package/dist/commands/auth/index.d.ts +1 -1
- package/dist/commands/auth/index.js +7 -7
- package/dist/commands/branch/create/index.d.ts +1 -1
- package/dist/commands/branch/create/index.js +1 -1
- package/dist/commands/ephemeral/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/create/index.js +103 -0
- package/dist/commands/ephemeral/delete/index.d.ts +18 -0
- package/dist/commands/ephemeral/delete/index.js +99 -0
- package/dist/commands/ephemeral/edit/index.d.ts +18 -0
- package/dist/commands/ephemeral/edit/index.js +109 -0
- package/dist/commands/ephemeral/get/index.d.ts +21 -0
- package/dist/commands/ephemeral/get/index.js +125 -0
- package/dist/commands/ephemeral/impersonate/index.d.ts +20 -0
- package/dist/commands/ephemeral/impersonate/index.js +120 -0
- package/dist/commands/ephemeral/list/index.d.ts +19 -0
- package/dist/commands/ephemeral/list/index.js +123 -0
- package/dist/commands/ephemeral/pull/index.d.ts +19 -0
- package/dist/commands/ephemeral/pull/index.js +163 -0
- package/dist/commands/ephemeral/push/index.d.ts +35 -0
- package/dist/commands/ephemeral/push/index.js +223 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
- package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/list/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/list/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
- package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
- package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/create/index.js +92 -0
- package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
- package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
- package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/get/index.js +101 -0
- package/dist/commands/ephemeral/static_host/list/index.d.ts +17 -0
- package/dist/commands/ephemeral/static_host/list/index.js +132 -0
- package/dist/commands/flatten/index.d.ts +34 -0
- package/dist/commands/flatten/index.js +140 -0
- package/dist/commands/function/create/index.js +1 -1
- package/dist/commands/function/edit/index.js +26 -32
- package/dist/commands/function/get/index.js +2 -3
- package/dist/commands/function/run/index.d.ts +1 -0
- package/dist/commands/function/run/index.js +19 -6
- package/dist/commands/profile/create/index.d.ts +1 -1
- package/dist/commands/profile/create/index.js +7 -7
- package/dist/commands/profile/me/index.js +1 -1
- package/dist/commands/profile/use/index.js +0 -4
- package/dist/commands/profile/wizard/index.d.ts +1 -1
- package/dist/commands/profile/wizard/index.js +12 -10
- package/dist/commands/profile/workspace/set/index.js +1 -1
- package/dist/commands/release/edit/index.js +2 -2
- package/dist/commands/release/export/index.js +1 -2
- package/dist/commands/release/import/index.js +1 -1
- package/dist/commands/release/pull/index.d.ts +0 -6
- package/dist/commands/release/pull/index.js +13 -99
- package/dist/commands/release/push/index.js +2 -2
- package/dist/commands/sandbox/env/get_all/index.js +1 -1
- package/dist/commands/sandbox/env/set_all/index.js +1 -1
- package/dist/commands/sandbox/get/index.js +1 -1
- package/dist/commands/sandbox/license/get/index.js +1 -1
- package/dist/commands/sandbox/license/set/index.js +1 -1
- package/dist/commands/sandbox/pull/index.d.ts +0 -1
- package/dist/commands/sandbox/pull/index.js +13 -81
- package/dist/commands/sandbox/push/index.d.ts +1 -1
- package/dist/commands/sandbox/push/index.js +5 -5
- package/dist/commands/static_host/build/create/index.d.ts +1 -1
- package/dist/commands/static_host/build/create/index.js +2 -2
- package/dist/commands/static_host/build/pull/index.js +1 -1
- package/dist/commands/static_host/build/push/index.js +1 -1
- package/dist/commands/static_host/edit/index.js +1 -1
- package/dist/commands/tenant/backup/export/index.js +1 -2
- package/dist/commands/tenant/backup/import/index.js +1 -1
- package/dist/commands/tenant/cluster/create/index.js +1 -1
- package/dist/commands/tenant/cluster/license/get/index.js +1 -1
- package/dist/commands/tenant/cluster/license/set/index.js +1 -1
- package/dist/commands/tenant/create/index.d.ts +1 -1
- package/dist/commands/tenant/create/index.js +6 -6
- package/dist/commands/tenant/deploy_platform/index.js +1 -1
- package/dist/commands/tenant/edit/index.js +7 -7
- package/dist/commands/tenant/env/get_all/index.js +1 -1
- package/dist/commands/tenant/env/set_all/index.js +1 -1
- package/dist/commands/tenant/impersonate/index.d.ts +1 -1
- package/dist/commands/tenant/impersonate/index.js +14 -14
- package/dist/commands/tenant/license/get/index.js +1 -1
- package/dist/commands/tenant/license/set/index.js +1 -1
- package/dist/commands/tenant/pull/index.d.ts +0 -6
- package/dist/commands/tenant/pull/index.js +14 -100
- package/dist/commands/workspace/git/pull/index.d.ts +4 -4
- package/dist/commands/workspace/git/pull/index.js +158 -97
- package/dist/commands/workspace/pull/index.d.ts +1 -7
- package/dist/commands/workspace/pull/index.js +17 -103
- package/dist/help.js +4 -2
- package/dist/update-check.d.ts +1 -1
- package/dist/update-check.js +1 -1
- package/dist/utils/document-parser.d.ts +116 -0
- package/dist/utils/document-parser.js +311 -4
- package/dist/utils/flatten.d.ts +36 -0
- package/dist/utils/flatten.js +68 -0
- package/dist/utils/knowledge-sync.d.ts +1 -0
- package/dist/utils/knowledge-sync.js +11 -6
- package/dist/utils/microservice-wait.d.ts +53 -0
- package/dist/utils/microservice-wait.js +84 -0
- package/dist/utils/multidoc-push.d.ts +88 -0
- package/dist/utils/multidoc-push.js +378 -15
- package/dist/utils/paging.d.ts +2 -1
- package/dist/utils/paging.js +0 -1
- package/dist/utils/reference-checker.js +14 -3
- package/oclif.manifest.json +5264 -3170
- package/package.json +10 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
2
|
import snakeCase from 'lodash.snakecase';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
5
|
import BaseCommand from '../../../base-command.js';
|
|
6
|
-
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
6
|
+
import { buildApiGroupFolderResolver, buildChannelServerResolver, parseDocument, resolveDocumentPath, } from '../../../utils/document-parser.js';
|
|
7
7
|
import { fetchKnowledge, writeKnowledge } from '../../../utils/knowledge-sync.js';
|
|
8
8
|
export default class Pull extends BaseCommand {
|
|
9
9
|
static description = 'Pull a workspace multidoc from the Xano Metadata API and split into individual files';
|
|
@@ -36,14 +36,14 @@ Pulled 58 documents
|
|
|
36
36
|
description: 'Output directory for pulled documents (defaults to current directory)',
|
|
37
37
|
required: false,
|
|
38
38
|
}),
|
|
39
|
-
|
|
39
|
+
draft: Flags.boolean({
|
|
40
40
|
default: false,
|
|
41
|
-
description: 'Include
|
|
41
|
+
description: 'Include draft versions',
|
|
42
42
|
required: false,
|
|
43
43
|
}),
|
|
44
|
-
|
|
44
|
+
env: Flags.boolean({
|
|
45
45
|
default: false,
|
|
46
|
-
description: 'Include
|
|
46
|
+
description: 'Include environment variables',
|
|
47
47
|
required: false,
|
|
48
48
|
}),
|
|
49
49
|
records: Flags.boolean({
|
|
@@ -134,96 +134,19 @@ Pulled 58 documents
|
|
|
134
134
|
// Resolve api_group names to unique folder names, disambiguating collisions
|
|
135
135
|
// where different names produce the same snakeCase (e.g., "Authentication" vs "authentication")
|
|
136
136
|
const getApiGroupFolder = buildApiGroupFolderResolver(documents, snakeCase);
|
|
137
|
+
// Resolve a realtime v2 channel path -> owning realtime_server name, so
|
|
138
|
+
// messages can nest under their channel's server (see resolver docs).
|
|
139
|
+
const getChannelServer = buildChannelServerResolver(documents);
|
|
137
140
|
// Track filenames per type to handle duplicates
|
|
138
141
|
const filenameCounters = new Map();
|
|
139
142
|
let writtenCount = 0;
|
|
140
143
|
for (const doc of documents) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
else if (doc.type === 'workspace_trigger') {
|
|
149
|
-
// workspace_trigger → workspace/trigger/{name}.xs
|
|
150
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
151
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
152
|
-
}
|
|
153
|
-
else if (doc.type === 'error_trigger') {
|
|
154
|
-
// error_trigger → workspace/trigger/{name}.xs (singleton, colocated with workspace triggers)
|
|
155
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
156
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
157
|
-
}
|
|
158
|
-
else if (doc.type === 'agent') {
|
|
159
|
-
// agent → ai/agent/{name}.xs
|
|
160
|
-
typeDir = path.join(outputDir, 'ai', 'agent');
|
|
161
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
162
|
-
}
|
|
163
|
-
else if (doc.type === 'mcp_server') {
|
|
164
|
-
// mcp_server → ai/mcp_server/{name}.xs
|
|
165
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server');
|
|
166
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
167
|
-
}
|
|
168
|
-
else if (doc.type === 'tool') {
|
|
169
|
-
// tool → ai/tool/{name}.xs
|
|
170
|
-
typeDir = path.join(outputDir, 'ai', 'tool');
|
|
171
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
172
|
-
}
|
|
173
|
-
else if (doc.type === 'agent_trigger') {
|
|
174
|
-
// agent_trigger → ai/agent/trigger/{name}.xs
|
|
175
|
-
typeDir = path.join(outputDir, 'ai', 'agent', 'trigger');
|
|
176
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
177
|
-
}
|
|
178
|
-
else if (doc.type === 'mcp_server_trigger') {
|
|
179
|
-
// mcp_server_trigger → ai/mcp_server/trigger/{name}.xs
|
|
180
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server', 'trigger');
|
|
181
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
182
|
-
}
|
|
183
|
-
else if (doc.type === 'table_trigger') {
|
|
184
|
-
// table_trigger → table/trigger/{name}.xs
|
|
185
|
-
typeDir = path.join(outputDir, 'table', 'trigger');
|
|
186
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
187
|
-
}
|
|
188
|
-
else if (doc.type === 'realtime_channel') {
|
|
189
|
-
// realtime_channel → realtime/channel/{name}.xs
|
|
190
|
-
typeDir = path.join(outputDir, 'realtime', 'channel');
|
|
191
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
192
|
-
}
|
|
193
|
-
else if (doc.type === 'realtime_trigger') {
|
|
194
|
-
// realtime_trigger → realtime/trigger/{name}.xs
|
|
195
|
-
typeDir = path.join(outputDir, 'realtime', 'trigger');
|
|
196
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
197
|
-
}
|
|
198
|
-
else if (doc.type === 'api_group') {
|
|
199
|
-
// api_group "test" → api/{resolved_folder}/{name}.xs
|
|
200
|
-
const groupFolder = getApiGroupFolder(doc.name);
|
|
201
|
-
typeDir = path.join(outputDir, 'api', groupFolder);
|
|
202
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
203
|
-
}
|
|
204
|
-
else if (doc.type === 'query' && doc.apiGroup) {
|
|
205
|
-
// query in group "test" → api/{resolved_folder}/{query_name}.xs
|
|
206
|
-
const groupFolder = getApiGroupFolder(doc.apiGroup);
|
|
207
|
-
const nameParts = doc.name.split('/');
|
|
208
|
-
const leafName = nameParts.pop();
|
|
209
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
210
|
-
typeDir = path.join(outputDir, 'api', groupFolder, ...folderParts);
|
|
211
|
-
baseName = this.sanitizeFilename(leafName);
|
|
212
|
-
if (doc.verb) {
|
|
213
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
// Default: split folder path from name
|
|
218
|
-
const nameParts = doc.name.split('/');
|
|
219
|
-
const leafName = nameParts.pop();
|
|
220
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
221
|
-
typeDir = path.join(outputDir, doc.type, ...folderParts);
|
|
222
|
-
baseName = this.sanitizeFilename(leafName);
|
|
223
|
-
if (doc.verb) {
|
|
224
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
144
|
+
const { baseName, typeDir } = resolveDocumentPath(doc, outputDir, {
|
|
145
|
+
getApiGroupFolder,
|
|
146
|
+
getChannelServer,
|
|
147
|
+
join: path.join,
|
|
148
|
+
snakeCase,
|
|
149
|
+
});
|
|
227
150
|
fs.mkdirSync(typeDir, { recursive: true });
|
|
228
151
|
// Track duplicates per directory
|
|
229
152
|
const dirKey = path.relative(outputDir, typeDir);
|
|
@@ -234,8 +157,7 @@ Pulled 58 documents
|
|
|
234
157
|
const count = typeCounters.get(baseName) || 0;
|
|
235
158
|
typeCounters.set(baseName, count + 1);
|
|
236
159
|
// Append numeric suffix for duplicates
|
|
237
|
-
|
|
238
|
-
filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
160
|
+
const filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
239
161
|
const filePath = path.join(typeDir, filename);
|
|
240
162
|
fs.writeFileSync(filePath, doc.content, 'utf8');
|
|
241
163
|
writtenCount++;
|
|
@@ -252,12 +174,4 @@ Pulled 58 documents
|
|
|
252
174
|
parts.push(`${knowledgeCount} knowledge file${knowledgeCount === 1 ? '' : 's'}`);
|
|
253
175
|
this.log(`Pulled ${parts.join(' + ')} to ${flags.directory}`);
|
|
254
176
|
}
|
|
255
|
-
/**
|
|
256
|
-
* Sanitize a document name for use as a filename.
|
|
257
|
-
* Strips quotes, replaces spaces with underscores, and removes
|
|
258
|
-
* characters that are unsafe in filenames.
|
|
259
|
-
*/
|
|
260
|
-
sanitizeFilename(name) {
|
|
261
|
-
return snakeCase(name.replaceAll('"', ''));
|
|
262
|
-
}
|
|
263
177
|
}
|
package/dist/help.js
CHANGED
|
@@ -45,7 +45,9 @@ export default class Help extends BaseHelp {
|
|
|
45
45
|
if (this.config.topicSeparator !== ':')
|
|
46
46
|
c.id = c.id.replaceAll(':', this.config.topicSeparator);
|
|
47
47
|
const summary = this.summary(c);
|
|
48
|
-
|
|
48
|
+
// Strip ANSI colour codes from the summary so the help table aligns.
|
|
49
|
+
// eslint-disable-next-line no-control-regex -- matching the escape char is the point
|
|
50
|
+
return [c.id, summary ? summary.replaceAll(/\u001B\[\d+m/g, '') : ''];
|
|
49
51
|
});
|
|
50
52
|
// Only add promoted commands at the root level, not within a specific topic
|
|
51
53
|
if (isRootHelp) {
|
|
@@ -59,6 +61,6 @@ export default class Help extends BaseHelp {
|
|
|
59
61
|
spacer: '\n',
|
|
60
62
|
stripAnsi: this.opts.stripAnsi,
|
|
61
63
|
});
|
|
62
|
-
return this.section('COMMANDS', body + `\n\n\
|
|
64
|
+
return this.section('COMMANDS', body + `\n\n\u001B[2mSee xano <topic> --help for all commands in a topic.\u001B[0m`);
|
|
63
65
|
}
|
|
64
66
|
}
|
package/dist/update-check.d.ts
CHANGED
|
@@ -5,4 +5,4 @@ export declare function clearUpdateCache(): void;
|
|
|
5
5
|
*
|
|
6
6
|
* The check hits npm at most once every 24 hours and caches the result.
|
|
7
7
|
*/
|
|
8
|
-
export declare function checkForUpdate(currentVersion: string, forceCheck?: boolean):
|
|
8
|
+
export declare function checkForUpdate(currentVersion: string, forceCheck?: boolean): null | string;
|
package/dist/update-check.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
3
|
import * as os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
5
|
const UPDATE_CHECK_FILE = path.join(os.homedir(), '.xano', 'update-check.json');
|
|
6
6
|
const CHECK_INTERVAL_MS = 8 * 60 * 60 * 1000; // 8 hours
|
|
7
7
|
function isBeta(version) {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export interface ParsedDocument {
|
|
2
2
|
apiGroup?: string;
|
|
3
3
|
canonical?: string;
|
|
4
|
+
/** Owning channel path for realtime v2 `message` documents. */
|
|
5
|
+
channel?: string;
|
|
4
6
|
content: string;
|
|
5
7
|
guid?: string;
|
|
6
8
|
name: string;
|
|
9
|
+
/** Owning realtime_server name for realtime v2 `channel` documents. */
|
|
10
|
+
server?: string;
|
|
7
11
|
type: string;
|
|
8
12
|
verb?: string;
|
|
9
13
|
}
|
|
@@ -12,6 +16,87 @@ export interface ParsedDocument {
|
|
|
12
16
|
* Skips leading comment lines (starting with //) to find the first meaningful line.
|
|
13
17
|
*/
|
|
14
18
|
export declare function parseDocument(content: string): null | ParsedDocument;
|
|
19
|
+
/**
|
|
20
|
+
* Map a realtime v2 channel path onto directory segments.
|
|
21
|
+
*
|
|
22
|
+
* A channel name IS a path template — "rooms/{room_id}" — so the directory tree
|
|
23
|
+
* mirrors it rather than flattening it. That is the whole reason messages nest
|
|
24
|
+
* under channels: the tree should show the structure, and
|
|
25
|
+
* "channel/rooms/[room_id]/post.xs" says immediately that `post` lives on a
|
|
26
|
+
* parameterized rooms channel.
|
|
27
|
+
*
|
|
28
|
+
* "rooms" -> ["rooms"]
|
|
29
|
+
* "rooms/{room_id}" -> ["rooms", "[room_id]"]
|
|
30
|
+
* "org/{org_id}/room/{id}" -> ["org", "[org_id]", "room", "[id]"]
|
|
31
|
+
*
|
|
32
|
+
* Braces become brackets because "{" and "}" are awkward in shells, while "["
|
|
33
|
+
* and "]" are legal on every filesystem. They are still glob character classes,
|
|
34
|
+
* so scripting against a checkout needs the path quoted.
|
|
35
|
+
*
|
|
36
|
+
* Escaping the braces rather than dropping them is what keeps a parameterized
|
|
37
|
+
* channel distinct from a literal one: without it, "rooms/{room_id}" and a
|
|
38
|
+
* literal channel named "rooms/room_id" collide on the same directory.
|
|
39
|
+
*
|
|
40
|
+
* Mirrors Vfs::channelSegment() on the server.
|
|
41
|
+
*/
|
|
42
|
+
export declare function channelPathSegments(name: string, snakeCaseFn: (s: string) => string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Build a map of realtime v2 channel path -> owning realtime_server name.
|
|
45
|
+
*
|
|
46
|
+
* This is the cross-document lookup the on-disk nesting needs: a `message`
|
|
47
|
+
* document names its channel (`channel = "..."`), while a `channel` document
|
|
48
|
+
* names its server (`realtime_server = "..."`). To place a message under
|
|
49
|
+
* `realtime/server/<server>/channel/<path>/...` we resolve the message's channel
|
|
50
|
+
* to that channel's server — which the channel document carries. (Messages now
|
|
51
|
+
* also carry `realtime_server` directly, but resolving via the channel keeps a
|
|
52
|
+
* single source of truth for the channel→server mapping.)
|
|
53
|
+
*
|
|
54
|
+
* Because every pull path parses the WHOLE multidoc into memory before writing,
|
|
55
|
+
* we can build this map from the `channel` docs in the same batch (a two-pass
|
|
56
|
+
* resolve), keyed by the channel's own name (its path). A channel with no
|
|
57
|
+
* `realtime_server` (e.g. a pre-server export) simply isn't in the map, and
|
|
58
|
+
* callers fall back accordingly.
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildChannelServerResolver(documents: ParsedDocument[]): (channelName: string) => string | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Sanitize a document name for use as a single on-disk filename segment.
|
|
63
|
+
* Strips quotes and snake_cases the rest. Mirrors the per-command
|
|
64
|
+
* `sanitizeFilename` the pull commands used to each define privately.
|
|
65
|
+
*/
|
|
66
|
+
export declare function sanitizeDocumentName(name: string, snakeCaseFn: (s: string) => string): string;
|
|
67
|
+
/**
|
|
68
|
+
* The directory + base filename (no extension, no duplicate suffix) a document
|
|
69
|
+
* is written to on disk. `resolveDocumentPath` returns this; callers append the
|
|
70
|
+
* `.xs` extension and any `_N` duplicate suffix.
|
|
71
|
+
*/
|
|
72
|
+
export interface DocumentPlacement {
|
|
73
|
+
baseName: string;
|
|
74
|
+
typeDir: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve where a parsed document lands on disk — the SINGLE source of truth for
|
|
78
|
+
* the pull layout, shared by every pull command (workspace, tenant, release,
|
|
79
|
+
* sandbox, ephemeral). Previously each command inlined a ~130-line copy of this
|
|
80
|
+
* decision; the copies drifted (the realtime v2 realtime_server/channel/message
|
|
81
|
+
* branches existed only in workspace/tenant), so an ephemeral pull dumped v2
|
|
82
|
+
* objects into flat top-level channel/, message/ and realtime_server/ folders —
|
|
83
|
+
* detaching messages from their channels and risking name collisions (message
|
|
84
|
+
* names are unique only WITHIN a channel).
|
|
85
|
+
*
|
|
86
|
+
* @param doc - the parsed document
|
|
87
|
+
* @param outputDir - the resolved pull output root
|
|
88
|
+
* @param deps - injected dependencies
|
|
89
|
+
* @param deps.getApiGroupFolder - resolves an api_group name to its unique folder
|
|
90
|
+
* @param deps.getChannelServer - resolves a v2 channel path to its owning realtime_server
|
|
91
|
+
* @param deps.join - the path.join implementation (injectable for testing)
|
|
92
|
+
* @param deps.snakeCase - the snakeCase implementation the caller uses
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolveDocumentPath(doc: ParsedDocument, outputDir: string, deps: {
|
|
95
|
+
getApiGroupFolder: (groupName: string) => string;
|
|
96
|
+
getChannelServer: (channelName: string) => string | undefined;
|
|
97
|
+
join: (...parts: string[]) => string;
|
|
98
|
+
snakeCase: (s: string) => string;
|
|
99
|
+
}): DocumentPlacement;
|
|
15
100
|
/**
|
|
16
101
|
* Build a unique key for a document based on its type, name, verb, and api_group.
|
|
17
102
|
* Used to match server GUID map entries back to local files.
|
|
@@ -37,3 +122,34 @@ export declare function findFilesWithGuid(entries: Array<{
|
|
|
37
122
|
content: string;
|
|
38
123
|
filePath: string;
|
|
39
124
|
}>, guid: string): string[];
|
|
125
|
+
/**
|
|
126
|
+
* Split a multi-document XanoScript blob (documents joined by `\n---\n`) into
|
|
127
|
+
* parsed documents, skipping empties and any fragment that fails to parse.
|
|
128
|
+
*
|
|
129
|
+
* This is the same split the pull commands apply to the server's multidoc
|
|
130
|
+
* response. A hand-authored bundle is one file of many `---`-separated
|
|
131
|
+
* documents; this turns it back into the per-document units every downstream
|
|
132
|
+
* step (placement, GUID writeback, partial-diff matching) assumes.
|
|
133
|
+
*/
|
|
134
|
+
export declare function splitMultidoc(blob: string): ParsedDocument[];
|
|
135
|
+
/** A document's resolved on-disk location relative to the output root, plus its content. */
|
|
136
|
+
export interface PlacedDocument {
|
|
137
|
+
content: string;
|
|
138
|
+
/** POSIX-style path relative to the output root, e.g. "api/pdf/documents_GET.xs". */
|
|
139
|
+
relPath: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Compute where each document lands on disk — a PURE function (no filesystem)
|
|
143
|
+
* mirroring the split-and-place loop the pull commands run. It resolves each
|
|
144
|
+
* document via `resolveDocumentPath`, appends `.xs`, and disambiguates
|
|
145
|
+
* collisions within a directory with a `_N` suffix (the second file named
|
|
146
|
+
* `say.xs` in a dir becomes `say_2.xs`), exactly as the pull writer does.
|
|
147
|
+
*
|
|
148
|
+
* Kept filesystem-free so it can be unit-tested and reused by both the pull
|
|
149
|
+
* commands and `flatten` without duplicating the placement rules.
|
|
150
|
+
*/
|
|
151
|
+
export declare function placeDocuments(documents: ParsedDocument[], deps: {
|
|
152
|
+
join: (...parts: string[]) => string;
|
|
153
|
+
relative: (from: string, to: string) => string;
|
|
154
|
+
snakeCase: (s: string) => string;
|
|
155
|
+
}): PlacedDocument[];
|