@revoengine/cli 1.0.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.
Files changed (46) hide show
  1. package/README.md +211 -0
  2. package/dist/bin/revo.d.ts +2 -0
  3. package/dist/bin/revo.js +19 -0
  4. package/dist/src/cli.d.ts +3 -0
  5. package/dist/src/cli.js +213 -0
  6. package/dist/src/client.d.ts +72 -0
  7. package/dist/src/client.js +315 -0
  8. package/dist/src/commands/auth.d.ts +2 -0
  9. package/dist/src/commands/auth.js +131 -0
  10. package/dist/src/commands/component.d.ts +2 -0
  11. package/dist/src/commands/component.js +905 -0
  12. package/dist/src/commands/endpoints.d.ts +2 -0
  13. package/dist/src/commands/endpoints.js +4 -0
  14. package/dist/src/commands/index.d.ts +7 -0
  15. package/dist/src/commands/index.js +7 -0
  16. package/dist/src/commands/info.d.ts +2 -0
  17. package/dist/src/commands/info.js +6 -0
  18. package/dist/src/commands/project.d.ts +2 -0
  19. package/dist/src/commands/project.js +80 -0
  20. package/dist/src/commands/request.d.ts +2 -0
  21. package/dist/src/commands/request.js +59 -0
  22. package/dist/src/commands/search.d.ts +2 -0
  23. package/dist/src/commands/search.js +22 -0
  24. package/dist/src/config.d.ts +54 -0
  25. package/dist/src/config.js +356 -0
  26. package/dist/src/index.d.ts +4 -0
  27. package/dist/src/index.js +4 -0
  28. package/dist/src/legacy.d.ts +8 -0
  29. package/dist/src/legacy.js +88 -0
  30. package/dist/src/project.d.ts +102 -0
  31. package/dist/src/project.js +475 -0
  32. package/dist/src/prompt.d.ts +4 -0
  33. package/dist/src/prompt.js +64 -0
  34. package/dist/src/runtime-view.d.ts +17 -0
  35. package/dist/src/runtime-view.js +80 -0
  36. package/dist/src/spinner.d.ts +14 -0
  37. package/dist/src/spinner.js +46 -0
  38. package/dist/src/types.d.ts +36 -0
  39. package/dist/src/types.js +1 -0
  40. package/dist/src/ui.d.ts +26 -0
  41. package/dist/src/ui.js +182 -0
  42. package/dist/src/utils.d.ts +10 -0
  43. package/dist/src/utils.js +86 -0
  44. package/package.json +32 -0
  45. package/tsconfig.build.json +15 -0
  46. package/tsconfig.json +19 -0
@@ -0,0 +1,88 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { DEFAULT_BASE_URL } from "./config.js";
4
+ import { readJsonFile } from "./utils.js";
5
+ function isLegacyAccountRecord(value) {
6
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
7
+ }
8
+ function resolveLegacyEntry(configFile, preferredInstance) {
9
+ if (preferredInstance && isLegacyAccountRecord(configFile[preferredInstance])) {
10
+ return [preferredInstance, configFile[preferredInstance]];
11
+ }
12
+ for (const [key, value] of Object.entries(configFile)) {
13
+ if (isLegacyAccountRecord(value) && value.default) {
14
+ return [key, value];
15
+ }
16
+ }
17
+ for (const [key, value] of Object.entries(configFile)) {
18
+ if (isLegacyAccountRecord(value)) {
19
+ return [key, value];
20
+ }
21
+ }
22
+ return undefined;
23
+ }
24
+ function normalizeLegacyRecord(record, fallbackInstance = '') {
25
+ const baseUrl = record.url || record.baseUrl || DEFAULT_BASE_URL;
26
+ const instance = record.instance || fallbackInstance;
27
+ const token = record.token || record.key || '';
28
+ return {
29
+ baseUrl,
30
+ instance,
31
+ token,
32
+ source: 'legacy-config',
33
+ };
34
+ }
35
+ export function readLegacyImportFromFile(filePath, preferredInstance) {
36
+ if (!fs.existsSync(filePath)) {
37
+ return null;
38
+ }
39
+ let parsed;
40
+ try {
41
+ parsed = readJsonFile(filePath);
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ if (!parsed || typeof parsed !== 'object') {
47
+ return null;
48
+ }
49
+ if (isLegacyAccountRecord(parsed)) {
50
+ return normalizeLegacyRecord(parsed, preferredInstance || parsed.instance || '');
51
+ }
52
+ const selected = resolveLegacyEntry(parsed, preferredInstance);
53
+ if (!selected) {
54
+ return null;
55
+ }
56
+ const [instanceKey, record] = selected;
57
+ return normalizeLegacyRecord(record, record.instance || instanceKey);
58
+ }
59
+ export function readLegacyImportFromProject(cwd = process.cwd()) {
60
+ const projectStatePath = path.join(cwd, '.revongcli');
61
+ if (!fs.existsSync(projectStatePath)) {
62
+ return null;
63
+ }
64
+ const projectState = readJsonFile(projectStatePath);
65
+ const credentials = projectState?.credentials || '';
66
+ if (credentials === 'ENVIRONMENTAL_VARIABLES') {
67
+ const envConfig = process.env.REVO_CONFIG;
68
+ if (!envConfig) {
69
+ return null;
70
+ }
71
+ try {
72
+ const parsed = JSON.parse(envConfig);
73
+ const selected = resolveLegacyEntry(parsed, projectState.instance || undefined);
74
+ if (!selected) {
75
+ return null;
76
+ }
77
+ const [instanceKey, record] = selected;
78
+ return normalizeLegacyRecord(record, projectState.instance || record.instance || instanceKey);
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ if (!credentials) {
85
+ return null;
86
+ }
87
+ return readLegacyImportFromFile(credentials, projectState.instance || undefined);
88
+ }
@@ -0,0 +1,102 @@
1
+ import type { ParsedArgs } from './types.ts';
2
+ export declare const REVO_PROJECT_DIR = ".revoengine";
3
+ export declare const REVO_TYPES_DIR: string;
4
+ export declare const REVO_TYPES_FILE: string;
5
+ export declare const REVO_METADATA_FILE: string;
6
+ export declare const REVO_TYPES_GITIGNORE_ENTRY = ".revoengine/types/";
7
+ export declare const REVO_DEBUG_JS_FILE: string;
8
+ export declare const REVO_DEBUG_TS_FILE: string;
9
+ export declare const REVO_DEBUG_INPUT_FILE: string;
10
+ export declare const REVO_DEBUG_CONFIG_FILE: string;
11
+ export declare const REVO_PLAYGROUND_JS_FILE: string;
12
+ export declare const REVO_PLAYGROUND_TS_FILE: string;
13
+ export type ProjectInvocation = {
14
+ action: 'init' | 'switch' | 'update';
15
+ targetArg?: string;
16
+ extraArgs: string[];
17
+ };
18
+ export type EditorTypesBundle = {
19
+ code: string;
20
+ endpoint?: string;
21
+ instanceId?: string;
22
+ apiVersion?: string;
23
+ libVersion?: string;
24
+ hash?: string;
25
+ lastSyncAt?: string;
26
+ };
27
+ export type RevoProjectMetadata = {
28
+ schemaVersion: 1;
29
+ endpoint: string;
30
+ instanceId: string;
31
+ authProfile: string;
32
+ apiVersion: string;
33
+ libVersion: string;
34
+ hash: string;
35
+ lastSyncAt: string;
36
+ };
37
+ export type ProjectSyncInput = {
38
+ endpoint: string;
39
+ instanceId: string;
40
+ code: string;
41
+ apiVersion: string;
42
+ libVersion: string;
43
+ hash: string;
44
+ lastSyncAt: string;
45
+ };
46
+ export type ProjectConfigResult = {
47
+ filePath: string;
48
+ action: 'created' | 'patched';
49
+ };
50
+ export type GitignorePatchResult = {
51
+ filePath: string;
52
+ action: 'created' | 'patched' | 'unchanged';
53
+ };
54
+ export declare function resolveProjectInvocation(args: ParsedArgs): ProjectInvocation;
55
+ export declare function resolveProjectTarget(cwd: string, targetArg?: string): string;
56
+ export declare function extractEditorTypesBundle(payload: unknown): EditorTypesBundle;
57
+ export declare function buildEditorEndpoint(baseUrl: string): string;
58
+ export declare function buildEditorTypesUrl(endpoint: string): string;
59
+ export declare function buildSandboxDebugUrl(endpoint: string): string;
60
+ export declare function extractSandboxEndpoint(profile: unknown): string | null;
61
+ export declare function buildProjectMetadata(input: {
62
+ baseUrl: string;
63
+ instance: string;
64
+ bundle: EditorTypesBundle;
65
+ now?: Date;
66
+ }): {
67
+ schemaVersion: 1;
68
+ endpoint: string;
69
+ instanceId: string;
70
+ authProfile: string;
71
+ apiVersion: string;
72
+ libVersion: string;
73
+ hash: string;
74
+ lastSyncAt: string;
75
+ };
76
+ export declare function findProjectMetadataFile(startDir?: string): string;
77
+ export declare function readProjectMetadata(startDir?: string): RevoProjectMetadata | null;
78
+ export declare function resolveProjectRoot(startDir?: string): string;
79
+ export declare function buildProjectSyncState(input: {
80
+ fallbackEndpoint: string;
81
+ fallbackInstanceId: string;
82
+ bundle: EditorTypesBundle;
83
+ now?: Date;
84
+ }): {
85
+ endpoint: string;
86
+ instanceId: string;
87
+ code: string;
88
+ apiVersion: string;
89
+ libVersion: string;
90
+ hash: string;
91
+ lastSyncAt: string;
92
+ };
93
+ export declare function syncProjectFiles(targetDir: string, input: ProjectSyncInput): {
94
+ projectDir: string;
95
+ typesFile: string;
96
+ metadataFile: string;
97
+ configResult: ProjectConfigResult;
98
+ gitignoreResult: GitignorePatchResult;
99
+ };
100
+ export declare function ensureProjectDebugScaffolding(rootDir: string): void;
101
+ export declare function ensureProjectConfig(rootDir: string): ProjectConfigResult;
102
+ export declare function ensureProjectGitignore(rootDir: string): GitignorePatchResult;
@@ -0,0 +1,475 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { readJsonFile } from "./utils.js";
4
+ export const REVO_PROJECT_DIR = '.revoengine';
5
+ export const REVO_TYPES_DIR = path.join(REVO_PROJECT_DIR, 'types');
6
+ export const REVO_TYPES_FILE = path.join(REVO_TYPES_DIR, 'revo.editor.d.ts');
7
+ export const REVO_METADATA_FILE = path.join(REVO_PROJECT_DIR, 'revo.json');
8
+ export const REVO_TYPES_GITIGNORE_ENTRY = '.revoengine/types/';
9
+ export const REVO_DEBUG_JS_FILE = path.join(REVO_PROJECT_DIR, 'debug_code.js');
10
+ export const REVO_DEBUG_TS_FILE = path.join(REVO_PROJECT_DIR, 'debug_code.ts');
11
+ export const REVO_DEBUG_INPUT_FILE = path.join(REVO_PROJECT_DIR, 'debug_input.json');
12
+ export const REVO_DEBUG_CONFIG_FILE = path.join(REVO_PROJECT_DIR, 'debug.json');
13
+ export const REVO_PLAYGROUND_JS_FILE = path.join(REVO_PROJECT_DIR, 'playground_code.js');
14
+ export const REVO_PLAYGROUND_TS_FILE = path.join(REVO_PROJECT_DIR, 'playground_code.ts');
15
+ const DEFAULT_PROJECT_INCLUDE = [
16
+ '**/*.ts',
17
+ '**/*.tsx',
18
+ '**/*.js',
19
+ '**/*.jsx',
20
+ REVO_TYPES_FILE,
21
+ ];
22
+ const DEFAULT_PROJECT_EXCLUDE = [
23
+ 'node_modules',
24
+ 'dist',
25
+ 'build',
26
+ ];
27
+ function isRecord(value) {
28
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
29
+ }
30
+ function readString(value) {
31
+ return typeof value === 'string' ? value : undefined;
32
+ }
33
+ function getNestedRecord(record, key) {
34
+ const candidate = record[key];
35
+ return isRecord(candidate) ? candidate : undefined;
36
+ }
37
+ function getRequiredMetadataValue(bundle, key) {
38
+ const value = bundle[key];
39
+ if (!value) {
40
+ throw new Error(`Editor types response did not include \`${key}\`.`);
41
+ }
42
+ return value;
43
+ }
44
+ function normalizeEndpoint(value) {
45
+ return value.replace(/\/+$/, '');
46
+ }
47
+ function stripJsonComments(input) {
48
+ let output = '';
49
+ let inString = false;
50
+ let inLineComment = false;
51
+ let inBlockComment = false;
52
+ let escaped = false;
53
+ for (let index = 0; index < input.length; index += 1) {
54
+ const char = input[index];
55
+ const next = input[index + 1];
56
+ if (inLineComment) {
57
+ if (char === '\n' || char === '\r') {
58
+ inLineComment = false;
59
+ output += char;
60
+ }
61
+ continue;
62
+ }
63
+ if (inBlockComment) {
64
+ if (char === '*' && next === '/') {
65
+ inBlockComment = false;
66
+ index += 1;
67
+ continue;
68
+ }
69
+ if (char === '\n' || char === '\r') {
70
+ output += char;
71
+ }
72
+ continue;
73
+ }
74
+ if (inString) {
75
+ output += char;
76
+ if (escaped) {
77
+ escaped = false;
78
+ }
79
+ else if (char === '\\') {
80
+ escaped = true;
81
+ }
82
+ else if (char === '"') {
83
+ inString = false;
84
+ }
85
+ continue;
86
+ }
87
+ if (char === '"') {
88
+ inString = true;
89
+ output += char;
90
+ continue;
91
+ }
92
+ if (char === '/' && next === '/') {
93
+ inLineComment = true;
94
+ index += 1;
95
+ continue;
96
+ }
97
+ if (char === '/' && next === '*') {
98
+ inBlockComment = true;
99
+ index += 1;
100
+ continue;
101
+ }
102
+ output += char;
103
+ }
104
+ return output;
105
+ }
106
+ function stripTrailingCommas(input) {
107
+ let output = '';
108
+ let inString = false;
109
+ let escaped = false;
110
+ for (let index = 0; index < input.length; index += 1) {
111
+ const char = input[index];
112
+ if (inString) {
113
+ output += char;
114
+ if (escaped) {
115
+ escaped = false;
116
+ }
117
+ else if (char === '\\') {
118
+ escaped = true;
119
+ }
120
+ else if (char === '"') {
121
+ inString = false;
122
+ }
123
+ continue;
124
+ }
125
+ if (char === '"') {
126
+ inString = true;
127
+ output += char;
128
+ continue;
129
+ }
130
+ if (char === ',') {
131
+ let lookahead = index + 1;
132
+ while (lookahead < input.length && /\s/.test(input[lookahead])) {
133
+ lookahead += 1;
134
+ }
135
+ if (input[lookahead] === '}' || input[lookahead] === ']') {
136
+ continue;
137
+ }
138
+ }
139
+ output += char;
140
+ }
141
+ return output;
142
+ }
143
+ function parseConfigFile(filePath) {
144
+ const raw = fs.readFileSync(filePath, 'utf8');
145
+ if (!raw.trim()) {
146
+ return {};
147
+ }
148
+ const normalized = stripTrailingCommas(stripJsonComments(raw));
149
+ const parsed = JSON.parse(normalized);
150
+ if (!isRecord(parsed)) {
151
+ throw new Error(`${path.basename(filePath)} must contain a JSON object.`);
152
+ }
153
+ return parsed;
154
+ }
155
+ function ensureObjectField(target, key, filePath) {
156
+ const current = target[key];
157
+ if (current === undefined) {
158
+ const next = {};
159
+ target[key] = next;
160
+ return next;
161
+ }
162
+ if (!isRecord(current)) {
163
+ throw new Error(`${path.basename(filePath)} field \`${key}\` must be an object.`);
164
+ }
165
+ return current;
166
+ }
167
+ function ensureStringArray(value, field, filePath) {
168
+ if (value === undefined) {
169
+ return undefined;
170
+ }
171
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string')) {
172
+ throw new Error(`${path.basename(filePath)} field \`${field}\` must be an array of strings.`);
173
+ }
174
+ return [...value];
175
+ }
176
+ function appendUnique(list, value) {
177
+ if (!list.includes(value)) {
178
+ list.push(value);
179
+ }
180
+ return list;
181
+ }
182
+ function writeJsonFile(filePath, value) {
183
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
184
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
185
+ }
186
+ export function resolveProjectInvocation(args) {
187
+ const projectArgs = args._.slice(1);
188
+ const [first, second, ...rest] = projectArgs;
189
+ if (!first) {
190
+ return {
191
+ action: 'init',
192
+ extraArgs: [],
193
+ };
194
+ }
195
+ if (first === 'init' || first === 'switch' || first === 'update') {
196
+ return {
197
+ action: first,
198
+ targetArg: second,
199
+ extraArgs: rest,
200
+ };
201
+ }
202
+ return {
203
+ action: 'init',
204
+ targetArg: first,
205
+ extraArgs: second ? [second, ...rest] : rest,
206
+ };
207
+ }
208
+ export function resolveProjectTarget(cwd, targetArg) {
209
+ return path.resolve(cwd, targetArg || '.');
210
+ }
211
+ export function extractEditorTypesBundle(payload) {
212
+ const root = isRecord(payload) && isRecord(payload.data) ? payload.data : payload;
213
+ if (!isRecord(root)) {
214
+ throw new Error('Editor types response was not a JSON object.');
215
+ }
216
+ const meta = getNestedRecord(root, 'meta');
217
+ const code = readString(root.code);
218
+ if (!code) {
219
+ throw new Error('Editor types response did not include a `code` field.');
220
+ }
221
+ return {
222
+ code,
223
+ endpoint: readString(root.endpoint) || readString(meta?.endpoint),
224
+ instanceId: readString(root.instanceId) || readString(root.instance) || readString(meta?.instanceId) || readString(meta?.instance),
225
+ apiVersion: readString(root.apiVersion) || readString(meta?.apiVersion),
226
+ libVersion: readString(root.libVersion) || readString(meta?.libVersion),
227
+ hash: readString(root.hash) || readString(meta?.hash),
228
+ lastSyncAt: readString(root.lastSyncAt) || readString(meta?.lastSyncAt),
229
+ };
230
+ }
231
+ export function buildEditorEndpoint(baseUrl) {
232
+ const url = new URL(baseUrl);
233
+ const basePath = url.pathname.replace(/\/+$/, '');
234
+ url.pathname = (/\/v\d+$/i.test(basePath) ? basePath : `${basePath || ''}/v1`).replace(/\/{2,}/g, '/');
235
+ url.search = '';
236
+ url.hash = '';
237
+ return normalizeEndpoint(url.toString());
238
+ }
239
+ export function buildEditorTypesUrl(endpoint) {
240
+ const url = new URL(endpoint);
241
+ const basePath = normalizeEndpoint(url.pathname);
242
+ const versionedPath = /\/v\d+$/i.test(basePath) ? basePath : `${basePath || ''}/v1`;
243
+ url.pathname = `${versionedPath}/editor/types`.replace(/\/{2,}/g, '/');
244
+ url.search = '';
245
+ url.hash = '';
246
+ return url.toString();
247
+ }
248
+ export function buildSandboxDebugUrl(endpoint) {
249
+ const url = new URL(endpoint);
250
+ const basePath = normalizeEndpoint(url.pathname);
251
+ const versionedPath = /\/v\d+$/i.test(basePath) ? basePath : `${basePath || ''}/v1`;
252
+ url.pathname = `${versionedPath}/debug`.replace(/\/{2,}/g, '/');
253
+ url.search = '';
254
+ url.hash = '';
255
+ return url.toString();
256
+ }
257
+ export function extractSandboxEndpoint(profile) {
258
+ if (!profile || typeof profile !== 'object') {
259
+ return null;
260
+ }
261
+ const candidate = profile;
262
+ const roots = [candidate];
263
+ const nestedProfile = candidate.profile;
264
+ if (nestedProfile && typeof nestedProfile === 'object' && !Array.isArray(nestedProfile)) {
265
+ roots.push(nestedProfile);
266
+ }
267
+ for (const root of roots) {
268
+ const instance = root.instance;
269
+ if (!instance || typeof instance !== 'object' || Array.isArray(instance)) {
270
+ continue;
271
+ }
272
+ const endpoints = instance.endpoints;
273
+ if (!endpoints || typeof endpoints !== 'object' || Array.isArray(endpoints)) {
274
+ continue;
275
+ }
276
+ const sandbox = endpoints.sandbox;
277
+ if (typeof sandbox === 'string' && sandbox) {
278
+ return normalizeEndpoint(sandbox);
279
+ }
280
+ }
281
+ return null;
282
+ }
283
+ export function buildProjectMetadata(input) {
284
+ const { baseUrl, bundle, instance, now = new Date() } = input;
285
+ return {
286
+ schemaVersion: 1,
287
+ endpoint: bundle.endpoint || buildEditorEndpoint(baseUrl),
288
+ instanceId: bundle.instanceId || instance,
289
+ authProfile: 'default',
290
+ apiVersion: getRequiredMetadataValue(bundle, 'apiVersion'),
291
+ libVersion: getRequiredMetadataValue(bundle, 'libVersion'),
292
+ hash: getRequiredMetadataValue(bundle, 'hash'),
293
+ lastSyncAt: bundle.lastSyncAt || now.toISOString(),
294
+ };
295
+ }
296
+ export function findProjectMetadataFile(startDir = process.cwd()) {
297
+ if (!startDir) {
298
+ return '';
299
+ }
300
+ let currentDir = path.resolve(startDir);
301
+ const { root } = path.parse(currentDir);
302
+ while (true) {
303
+ const candidate = path.join(currentDir, REVO_METADATA_FILE);
304
+ if (fs.existsSync(candidate)) {
305
+ return candidate;
306
+ }
307
+ if (currentDir === root) {
308
+ return '';
309
+ }
310
+ currentDir = path.dirname(currentDir);
311
+ }
312
+ }
313
+ export function readProjectMetadata(startDir = process.cwd()) {
314
+ const metadataFile = findProjectMetadataFile(startDir);
315
+ if (!metadataFile) {
316
+ return null;
317
+ }
318
+ const raw = readJsonFile(metadataFile);
319
+ if (!isRecord(raw)) {
320
+ return null;
321
+ }
322
+ if (typeof raw.instanceId !== 'string'
323
+ || typeof raw.endpoint !== 'string'
324
+ || typeof raw.authProfile !== 'string'
325
+ || typeof raw.apiVersion !== 'string'
326
+ || typeof raw.libVersion !== 'string'
327
+ || typeof raw.hash !== 'string'
328
+ || typeof raw.lastSyncAt !== 'string') {
329
+ return null;
330
+ }
331
+ return raw;
332
+ }
333
+ export function resolveProjectRoot(startDir = process.cwd()) {
334
+ const metadataFile = findProjectMetadataFile(startDir);
335
+ return metadataFile ? path.dirname(path.dirname(metadataFile)) : '';
336
+ }
337
+ export function buildProjectSyncState(input) {
338
+ const metadata = buildProjectMetadata({
339
+ baseUrl: input.fallbackEndpoint,
340
+ instance: input.fallbackInstanceId,
341
+ bundle: input.bundle,
342
+ now: input.now,
343
+ });
344
+ return {
345
+ endpoint: metadata.endpoint,
346
+ instanceId: metadata.instanceId,
347
+ code: input.bundle.code,
348
+ apiVersion: metadata.apiVersion,
349
+ libVersion: metadata.libVersion,
350
+ hash: metadata.hash,
351
+ lastSyncAt: metadata.lastSyncAt,
352
+ };
353
+ }
354
+ export function syncProjectFiles(targetDir, input) {
355
+ const projectDir = path.join(targetDir, REVO_PROJECT_DIR);
356
+ const typesDir = path.join(targetDir, REVO_TYPES_DIR);
357
+ fs.mkdirSync(projectDir, { recursive: true });
358
+ fs.mkdirSync(typesDir, { recursive: true });
359
+ const typesFile = path.join(targetDir, REVO_TYPES_FILE);
360
+ const metadataFile = path.join(targetDir, REVO_METADATA_FILE);
361
+ fs.writeFileSync(typesFile, input.code);
362
+ writeJsonFile(metadataFile, {
363
+ schemaVersion: 1,
364
+ endpoint: input.endpoint,
365
+ instanceId: input.instanceId,
366
+ authProfile: 'default',
367
+ apiVersion: input.apiVersion,
368
+ libVersion: input.libVersion,
369
+ hash: input.hash,
370
+ lastSyncAt: input.lastSyncAt,
371
+ });
372
+ const configResult = ensureProjectConfig(targetDir);
373
+ const gitignoreResult = ensureProjectGitignore(targetDir);
374
+ ensureProjectDebugScaffolding(targetDir);
375
+ return {
376
+ projectDir,
377
+ typesFile,
378
+ metadataFile,
379
+ configResult,
380
+ gitignoreResult,
381
+ };
382
+ }
383
+ export function ensureProjectDebugScaffolding(rootDir) {
384
+ const files = [
385
+ [path.join(rootDir, REVO_DEBUG_JS_FILE), ''],
386
+ [path.join(rootDir, REVO_DEBUG_TS_FILE), ''],
387
+ [path.join(rootDir, REVO_DEBUG_INPUT_FILE), '{}\n'],
388
+ [path.join(rootDir, REVO_DEBUG_CONFIG_FILE), `${JSON.stringify({
389
+ timeout: 10,
390
+ memory: 128,
391
+ production: false,
392
+ }, null, 2)}\n`],
393
+ [path.join(rootDir, REVO_PLAYGROUND_JS_FILE), ''],
394
+ [path.join(rootDir, REVO_PLAYGROUND_TS_FILE), ''],
395
+ ];
396
+ for (const [filePath, contents] of files) {
397
+ if (fs.existsSync(filePath)) {
398
+ continue;
399
+ }
400
+ fs.writeFileSync(filePath, contents);
401
+ }
402
+ }
403
+ export function ensureProjectConfig(rootDir) {
404
+ const tsconfigPath = path.join(rootDir, 'tsconfig.json');
405
+ const jsconfigPath = path.join(rootDir, 'jsconfig.json');
406
+ if (!fs.existsSync(tsconfigPath) && !fs.existsSync(jsconfigPath)) {
407
+ writeJsonFile(tsconfigPath, {
408
+ compilerOptions: {
409
+ allowJs: true,
410
+ checkJs: false,
411
+ noEmit: true,
412
+ skipLibCheck: true,
413
+ },
414
+ include: DEFAULT_PROJECT_INCLUDE,
415
+ exclude: DEFAULT_PROJECT_EXCLUDE,
416
+ });
417
+ return {
418
+ filePath: tsconfigPath,
419
+ action: 'created',
420
+ };
421
+ }
422
+ const filePath = fs.existsSync(tsconfigPath) ? tsconfigPath : jsconfigPath;
423
+ const parsed = parseConfigFile(filePath);
424
+ const compilerOptions = ensureObjectField(parsed, 'compilerOptions', filePath);
425
+ if (compilerOptions.allowJs === undefined) {
426
+ compilerOptions.allowJs = true;
427
+ }
428
+ if (compilerOptions.noEmit === undefined) {
429
+ compilerOptions.noEmit = true;
430
+ }
431
+ if (compilerOptions.skipLibCheck === undefined) {
432
+ compilerOptions.skipLibCheck = true;
433
+ }
434
+ const files = ensureStringArray(parsed.files, 'files', filePath);
435
+ const include = ensureStringArray(parsed.include, 'include', filePath);
436
+ if (files) {
437
+ parsed.files = appendUnique(files, REVO_TYPES_FILE);
438
+ }
439
+ else if (include) {
440
+ parsed.include = appendUnique(include, REVO_TYPES_FILE);
441
+ }
442
+ else {
443
+ parsed.include = DEFAULT_PROJECT_INCLUDE;
444
+ }
445
+ writeJsonFile(filePath, parsed);
446
+ return {
447
+ filePath,
448
+ action: 'patched',
449
+ };
450
+ }
451
+ export function ensureProjectGitignore(rootDir) {
452
+ const filePath = path.join(rootDir, '.gitignore');
453
+ if (!fs.existsSync(filePath)) {
454
+ fs.writeFileSync(filePath, `${REVO_TYPES_GITIGNORE_ENTRY}\n`);
455
+ return {
456
+ filePath,
457
+ action: 'created',
458
+ };
459
+ }
460
+ const raw = fs.readFileSync(filePath, 'utf8');
461
+ const lines = raw.split(/\r?\n/);
462
+ if (lines.some((line) => line.trim() === REVO_TYPES_GITIGNORE_ENTRY)) {
463
+ return {
464
+ filePath,
465
+ action: 'unchanged',
466
+ };
467
+ }
468
+ const trimmed = raw.replace(/\s*$/, '');
469
+ const next = trimmed ? `${trimmed}\n${REVO_TYPES_GITIGNORE_ENTRY}\n` : `${REVO_TYPES_GITIGNORE_ENTRY}\n`;
470
+ fs.writeFileSync(filePath, next);
471
+ return {
472
+ filePath,
473
+ action: 'patched',
474
+ };
475
+ }
@@ -0,0 +1,4 @@
1
+ export declare function isInteractiveTerminal(): boolean;
2
+ export declare function promptText(message: string, defaultValue?: string): Promise<string>;
3
+ export declare function promptSecret(message: string): Promise<string>;
4
+ export declare function promptConfirm(message: string, defaultValue?: boolean): Promise<boolean>;