instant-cli 0.22.99-experimental.add-user-perm-rules.20792844601.1 → 0.22.99-experimental.drewh-perms-map.20797152806.1
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/.turbo/turbo-build.log +1 -1
- package/dist/index.js +1041 -1146
- package/dist/index.js.map +1 -1
- package/dist/rename.js +58 -69
- package/dist/rename.js.map +1 -1
- package/dist/renderSchemaPlan.js +10 -22
- package/dist/renderSchemaPlan.js.map +1 -1
- package/dist/ui/index.js +115 -102
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/lib.js +29 -30
- package/dist/ui/lib.js.map +1 -1
- package/dist/util/fs.js +17 -30
- package/dist/util/fs.js.map +1 -1
- package/dist/util/isHeadlessEnvironment.js +1 -1
- package/dist/util/isHeadlessEnvironment.js.map +1 -1
- package/dist/util/loadConfig.js +32 -32
- package/dist/util/loadConfig.js.map +1 -1
- package/dist/util/packageManager.js +26 -37
- package/dist/util/packageManager.js.map +1 -1
- package/dist/util/projectDir.js +16 -27
- package/dist/util/projectDir.js.map +1 -1
- package/dist/util/promptOk.js +14 -21
- package/dist/util/promptOk.js.map +1 -1
- package/dist/util/renamePrompt.js +4 -2
- package/dist/util/renamePrompt.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
// @ts-check
|
|
11
2
|
import { generatePermsTypescriptFile, apiSchemaToInstantSchemaDef, generateSchemaTypescriptFile, diffSchemas, convertTxSteps, validateSchema, SchemaValidationError, PlatformApi, } from '@instantdb/platform';
|
|
12
3
|
import version from './version.js';
|
|
@@ -65,30 +56,27 @@ const potentialAdminTokenEnvs = {
|
|
|
65
56
|
default: 'INSTANT_APP_ADMIN_TOKEN',
|
|
66
57
|
short: 'INSTANT_ADMIN_TOKEN',
|
|
67
58
|
};
|
|
68
|
-
function detectEnvType(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const packageJSON = yield getPackageJson(pkgDir);
|
|
72
|
-
if (!packageJSON) {
|
|
73
|
-
return 'catchall';
|
|
74
|
-
}
|
|
75
|
-
if ((_b = packageJSON.dependencies) === null || _b === void 0 ? void 0 : _b.next) {
|
|
76
|
-
return 'next';
|
|
77
|
-
}
|
|
78
|
-
if ((_c = packageJSON.devDependencies) === null || _c === void 0 ? void 0 : _c.svelte) {
|
|
79
|
-
return 'svelte';
|
|
80
|
-
}
|
|
81
|
-
if ((_d = packageJSON.devDependencies) === null || _d === void 0 ? void 0 : _d.vite) {
|
|
82
|
-
return 'vite';
|
|
83
|
-
}
|
|
84
|
-
if ((_e = packageJSON.dependencies) === null || _e === void 0 ? void 0 : _e.expo) {
|
|
85
|
-
return 'expo';
|
|
86
|
-
}
|
|
87
|
-
if ((_f = packageJSON.dependencies) === null || _f === void 0 ? void 0 : _f.nuxt) {
|
|
88
|
-
return 'nuxt';
|
|
89
|
-
}
|
|
59
|
+
async function detectEnvType({ pkgDir }) {
|
|
60
|
+
const packageJSON = await getPackageJson(pkgDir);
|
|
61
|
+
if (!packageJSON) {
|
|
90
62
|
return 'catchall';
|
|
91
|
-
}
|
|
63
|
+
}
|
|
64
|
+
if (packageJSON.dependencies?.next) {
|
|
65
|
+
return 'next';
|
|
66
|
+
}
|
|
67
|
+
if (packageJSON.devDependencies?.svelte) {
|
|
68
|
+
return 'svelte';
|
|
69
|
+
}
|
|
70
|
+
if (packageJSON.devDependencies?.vite) {
|
|
71
|
+
return 'vite';
|
|
72
|
+
}
|
|
73
|
+
if (packageJSON.dependencies?.expo) {
|
|
74
|
+
return 'expo';
|
|
75
|
+
}
|
|
76
|
+
if (packageJSON.dependencies?.nuxt) {
|
|
77
|
+
return 'nuxt';
|
|
78
|
+
}
|
|
79
|
+
return 'catchall';
|
|
92
80
|
}
|
|
93
81
|
const instantDashOrigin = dev
|
|
94
82
|
? 'http://localhost:3000'
|
|
@@ -114,15 +102,13 @@ function convertPushPullToCurrentFormat(arg, opts) {
|
|
|
114
102
|
return { ok: false };
|
|
115
103
|
return { ok: true, bag, opts };
|
|
116
104
|
}
|
|
117
|
-
function packageDirectoryWithErrorLogging() {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return projectInfo;
|
|
125
|
-
});
|
|
105
|
+
async function packageDirectoryWithErrorLogging() {
|
|
106
|
+
const projectInfo = await findProjectDir();
|
|
107
|
+
if (!projectInfo) {
|
|
108
|
+
error("Couldn't find your root directory. Is there a package.json or deno.json file?");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
return projectInfo;
|
|
126
112
|
}
|
|
127
113
|
// cli
|
|
128
114
|
// Header -- this shows up in every command
|
|
@@ -258,16 +244,16 @@ program
|
|
|
258
244
|
.description('Log into your account')
|
|
259
245
|
.option('-p --print', 'Prints the auth token into the console.')
|
|
260
246
|
.option('--headless', 'Print the login URL instead of trying to open the browser')
|
|
261
|
-
.action((opts) =>
|
|
247
|
+
.action(async (opts) => {
|
|
262
248
|
console.log("Let's log you in!");
|
|
263
|
-
|
|
264
|
-
})
|
|
249
|
+
await login(opts);
|
|
250
|
+
});
|
|
265
251
|
program
|
|
266
252
|
.command('logout')
|
|
267
253
|
.description('Log out of your Instant account')
|
|
268
|
-
.action(() =>
|
|
269
|
-
|
|
270
|
-
})
|
|
254
|
+
.action(async () => {
|
|
255
|
+
await logout();
|
|
256
|
+
});
|
|
271
257
|
program
|
|
272
258
|
.command('init')
|
|
273
259
|
.description('Set up a new project.')
|
|
@@ -290,10 +276,10 @@ program
|
|
|
290
276
|
.argument('[app-id]')
|
|
291
277
|
.description('Push schema to production.')
|
|
292
278
|
.option('--skip-check-types', "Don't check types on the server when pushing schema")
|
|
293
|
-
.action((appIdOrName, opts) =>
|
|
279
|
+
.action(async (appIdOrName, opts) => {
|
|
294
280
|
warnDeprecation('push-schema', 'push schema');
|
|
295
|
-
|
|
296
|
-
})
|
|
281
|
+
await handlePush('schema', { app: appIdOrName, ...opts });
|
|
282
|
+
});
|
|
297
283
|
// Note: Nov 20, 2024
|
|
298
284
|
// We can eventually delete this,
|
|
299
285
|
// once we know most people use the new pull and push commands
|
|
@@ -301,10 +287,10 @@ program
|
|
|
301
287
|
.command('push-perms', { hidden: true })
|
|
302
288
|
.argument('[app-id]')
|
|
303
289
|
.description('Push perms to production.')
|
|
304
|
-
.action((appIdOrName) =>
|
|
290
|
+
.action(async (appIdOrName) => {
|
|
305
291
|
warnDeprecation('push-perms', 'push perms');
|
|
306
|
-
|
|
307
|
-
})
|
|
292
|
+
await handlePush('perms', { app: appIdOrName });
|
|
293
|
+
});
|
|
308
294
|
program
|
|
309
295
|
.command('push')
|
|
310
296
|
.argument('[schema|perms|all]', 'Which configuration to push. Defaults to `all`')
|
|
@@ -318,14 +304,12 @@ Environment Variables:
|
|
|
318
304
|
INSTANT_SCHEMA_FILE_PATH Override schema file location (default: instant.schema.ts)
|
|
319
305
|
INSTANT_PERMS_FILE_PATH Override perms file location (default: instant.perms.ts)
|
|
320
306
|
`)
|
|
321
|
-
.action(function (arg, inputOpts) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
yield handlePush(bag, opts);
|
|
328
|
-
});
|
|
307
|
+
.action(async function (arg, inputOpts) {
|
|
308
|
+
const ret = convertPushPullToCurrentFormat(arg, inputOpts);
|
|
309
|
+
if (!ret.ok)
|
|
310
|
+
return process.exit(1);
|
|
311
|
+
const { bag, opts } = ret;
|
|
312
|
+
await handlePush(bag, opts);
|
|
329
313
|
});
|
|
330
314
|
// Note: Nov 20, 2024
|
|
331
315
|
// We can eventually delete this,
|
|
@@ -334,10 +318,10 @@ program
|
|
|
334
318
|
.command('pull-schema', { hidden: true })
|
|
335
319
|
.argument('[app-id]')
|
|
336
320
|
.description('Generate instant.schema.ts from production')
|
|
337
|
-
.action((appIdOrName) =>
|
|
321
|
+
.action(async (appIdOrName) => {
|
|
338
322
|
warnDeprecation('pull-schema', 'pull schema');
|
|
339
|
-
|
|
340
|
-
})
|
|
323
|
+
await handlePull('schema', { app: appIdOrName });
|
|
324
|
+
});
|
|
341
325
|
// Note: Nov 20, 2024
|
|
342
326
|
// We can eventually delete this,
|
|
343
327
|
// once we know most people use the new pull and push commands
|
|
@@ -345,10 +329,10 @@ program
|
|
|
345
329
|
.command('pull-perms', { hidden: true })
|
|
346
330
|
.argument('[app-id]')
|
|
347
331
|
.description('Generate instant.perms.ts from production.')
|
|
348
|
-
.action((appIdOrName) =>
|
|
332
|
+
.action(async (appIdOrName) => {
|
|
349
333
|
warnDeprecation('pull-perms', 'pull perms');
|
|
350
|
-
|
|
351
|
-
})
|
|
334
|
+
await handlePull('perms', { app: appIdOrName });
|
|
335
|
+
});
|
|
352
336
|
program
|
|
353
337
|
.command('pull')
|
|
354
338
|
.argument('[schema|perms|all]', 'Which configuration to push. Defaults to `all`')
|
|
@@ -361,173 +345,159 @@ Environment Variables:
|
|
|
361
345
|
INSTANT_SCHEMA_FILE_PATH Override schema file location (default: instant.schema.ts)
|
|
362
346
|
INSTANT_PERMS_FILE_PATH Override perms file location (default: instant.perms.ts)
|
|
363
347
|
`)
|
|
364
|
-
.action(function (arg, inputOpts) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
yield handlePull(bag, opts);
|
|
371
|
-
});
|
|
348
|
+
.action(async function (arg, inputOpts) {
|
|
349
|
+
const ret = convertPushPullToCurrentFormat(arg, inputOpts);
|
|
350
|
+
if (!ret.ok)
|
|
351
|
+
return process.exit(1);
|
|
352
|
+
const { bag, opts } = ret;
|
|
353
|
+
await handlePull(bag, opts);
|
|
372
354
|
});
|
|
373
355
|
program
|
|
374
356
|
.command('claim')
|
|
375
357
|
.description('Transfer a tempoary app into your Instant account')
|
|
376
|
-
.action(function () {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
358
|
+
.action(async function () {
|
|
359
|
+
const token = await readConfigAuthToken(false);
|
|
360
|
+
if (!token) {
|
|
361
|
+
console.error(`Please log in first with ${chalk.bgGray.white('instant-cli login')} to claim an app`);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
const envResult = detectAppIdAndAdminTokenFromEnvWithErrorLogging();
|
|
365
|
+
if (!envResult.ok)
|
|
366
|
+
return process.exit(1);
|
|
367
|
+
if (!envResult.appId) {
|
|
368
|
+
error('No app ID found in environment variables.');
|
|
369
|
+
return process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
if (!envResult.adminToken) {
|
|
372
|
+
error('No admin token found in environment variables.');
|
|
373
|
+
return process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
const appId = envResult.appId.value;
|
|
376
|
+
const adminToken = envResult.adminToken.value;
|
|
377
|
+
console.log(`Found ${chalk.green(envResult.appId.envName)}: ${appId}`);
|
|
378
|
+
await claimEphemeralApp(appId, adminToken);
|
|
379
|
+
});
|
|
380
|
+
program.parse(process.argv);
|
|
381
|
+
async function handleInit(opts) {
|
|
382
|
+
const pkgAndAuthInfo = await getOrPromptPackageAndAuthInfoWithErrorLogging(opts);
|
|
383
|
+
if (!pkgAndAuthInfo)
|
|
384
|
+
return process.exit(1);
|
|
385
|
+
const { ok, appId } = await getOrCreateAppAndWriteToEnv(pkgAndAuthInfo, opts);
|
|
386
|
+
if (!ok) {
|
|
387
|
+
return process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
// Create schema file if it doesn't exist
|
|
390
|
+
// or ask to push if local schema exists
|
|
391
|
+
const localSchemaExists = await readLocalSchemaFile();
|
|
392
|
+
if (!localSchemaExists) {
|
|
393
|
+
await pull('schema', appId, pkgAndAuthInfo);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
const doSchemaPush = await promptOk({
|
|
397
|
+
promptText: 'Found local schema. Push it to the new app?',
|
|
398
|
+
inline: true,
|
|
399
|
+
}, program.opts());
|
|
400
|
+
if (doSchemaPush) {
|
|
401
|
+
await push('schema', appId, opts);
|
|
382
402
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
403
|
+
}
|
|
404
|
+
// Create perms file if it doesn't exist
|
|
405
|
+
// or ask to push if local perms exists
|
|
406
|
+
const localPermsExists = await readLocalPermsFile();
|
|
407
|
+
if (!localPermsExists) {
|
|
408
|
+
await pull('perms', appId, pkgAndAuthInfo);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
const doPermsPush = await promptOk({
|
|
412
|
+
promptText: 'Found local perms. Push it to the new app?',
|
|
413
|
+
inline: true,
|
|
414
|
+
}, program.opts());
|
|
415
|
+
if (doPermsPush) {
|
|
416
|
+
await push('perms', appId, opts);
|
|
389
417
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async function handleInitWithoutFiles(opts) {
|
|
421
|
+
try {
|
|
422
|
+
const authToken = await readConfigAuthToken(false);
|
|
423
|
+
if (!authToken) {
|
|
424
|
+
throw new Error(`Please log in first with 'instant-cli login' before running this command.`);
|
|
393
425
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
console.log(`Found ${chalk.green(envResult.appId.envName)}: ${appId}`);
|
|
397
|
-
yield claimEphemeralApp(appId, adminToken);
|
|
398
|
-
});
|
|
399
|
-
});
|
|
400
|
-
program.parse(process.argv);
|
|
401
|
-
function handleInit(opts) {
|
|
402
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
403
|
-
const pkgAndAuthInfo = yield getOrPromptPackageAndAuthInfoWithErrorLogging(opts);
|
|
404
|
-
if (!pkgAndAuthInfo)
|
|
405
|
-
return process.exit(1);
|
|
406
|
-
const { ok, appId } = yield getOrCreateAppAndWriteToEnv(pkgAndAuthInfo, opts);
|
|
407
|
-
if (!ok) {
|
|
408
|
-
return process.exit(1);
|
|
426
|
+
if (!opts?.title) {
|
|
427
|
+
throw new Error('Title is required for creating a new app without local files.');
|
|
409
428
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const localSchemaExists = yield readLocalSchemaFile();
|
|
413
|
-
if (!localSchemaExists) {
|
|
414
|
-
yield pull('schema', appId, pkgAndAuthInfo);
|
|
429
|
+
if (opts.title.startsWith('-')) {
|
|
430
|
+
throw new Error(`Invalid title: "${opts.title}". Title cannot be a flag.`);
|
|
415
431
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
promptText: 'Found local schema. Push it to the new app?',
|
|
419
|
-
inline: true,
|
|
420
|
-
}, program.opts());
|
|
421
|
-
if (doSchemaPush) {
|
|
422
|
-
yield push('schema', appId, opts);
|
|
423
|
-
}
|
|
432
|
+
if (opts?.temp && opts?.orgId) {
|
|
433
|
+
throw new Error('Cannot use --temp and --org-id flags together.');
|
|
424
434
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (!localPermsExists) {
|
|
429
|
-
yield pull('perms', appId, pkgAndAuthInfo);
|
|
435
|
+
let result;
|
|
436
|
+
if (opts?.temp) {
|
|
437
|
+
result = await createEphemeralApp(opts.title);
|
|
430
438
|
}
|
|
431
439
|
else {
|
|
432
|
-
|
|
433
|
-
promptText: 'Found local perms. Push it to the new app?',
|
|
434
|
-
inline: true,
|
|
435
|
-
}, program.opts());
|
|
436
|
-
if (doPermsPush) {
|
|
437
|
-
yield push('perms', appId, opts);
|
|
438
|
-
}
|
|
440
|
+
result = await createApp(opts.title, opts.orgId);
|
|
439
441
|
}
|
|
440
|
-
|
|
442
|
+
console.error(`${chalk.green('Successfully created new app!')}\n`);
|
|
443
|
+
console.log(toJson({
|
|
444
|
+
app: result,
|
|
445
|
+
error: null,
|
|
446
|
+
}));
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
console.error(`${chalk.red('Failed to create app.')}\n`);
|
|
450
|
+
console.log(toJson({
|
|
451
|
+
app: null,
|
|
452
|
+
error: { message: error.message },
|
|
453
|
+
}));
|
|
454
|
+
process.exit(1);
|
|
455
|
+
}
|
|
441
456
|
}
|
|
442
|
-
function
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
error: null,
|
|
469
|
-
}));
|
|
470
|
-
}
|
|
471
|
-
catch (error) {
|
|
472
|
-
console.error(`${chalk.red('Failed to create app.')}\n`);
|
|
473
|
-
console.log(toJson({
|
|
474
|
-
app: null,
|
|
475
|
-
error: { message: error.message },
|
|
476
|
-
}));
|
|
477
|
-
process.exit(1);
|
|
478
|
-
}
|
|
479
|
-
});
|
|
457
|
+
async function handlePush(bag, opts) {
|
|
458
|
+
const pkgAndAuthInfo = await enforcePackageAndAuthInfoWithErrorLogging(opts);
|
|
459
|
+
if (!pkgAndAuthInfo)
|
|
460
|
+
return process.exit(1);
|
|
461
|
+
const { ok, appId } = await detectAppWithErrorLogging(opts);
|
|
462
|
+
if (!ok)
|
|
463
|
+
return process.exit(1);
|
|
464
|
+
if (!appId) {
|
|
465
|
+
error('No app ID detected. Please specify one with --app or set up with `instant-cli init`');
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
await push(bag, appId, opts);
|
|
469
|
+
}
|
|
470
|
+
async function handlePull(bag, opts) {
|
|
471
|
+
const pkgAndAuthInfo = await enforcePackageAndAuthInfoWithErrorLogging(opts);
|
|
472
|
+
if (!pkgAndAuthInfo)
|
|
473
|
+
return process.exit(1);
|
|
474
|
+
const { ok, appId } = await detectAppWithErrorLogging(opts);
|
|
475
|
+
if (!ok) {
|
|
476
|
+
return process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
if (!appId) {
|
|
479
|
+
error('No app ID detected. Please specify one with --app or set up with `instant-cli init`');
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
await pull(bag, appId, { ...pkgAndAuthInfo, ...opts });
|
|
480
483
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
if (!pkgAndAuthInfo)
|
|
485
|
-
return process.exit(1);
|
|
486
|
-
const { ok, appId } = yield detectAppWithErrorLogging(opts);
|
|
484
|
+
async function push(bag, appId, opts) {
|
|
485
|
+
if (bag === 'schema' || bag === 'all') {
|
|
486
|
+
const { ok } = await pushSchema(appId, opts);
|
|
487
487
|
if (!ok)
|
|
488
488
|
return process.exit(1);
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
yield push(bag, appId, opts);
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
function handlePull(bag, opts) {
|
|
497
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
498
|
-
const pkgAndAuthInfo = yield enforcePackageAndAuthInfoWithErrorLogging(opts);
|
|
499
|
-
if (!pkgAndAuthInfo)
|
|
500
|
-
return process.exit(1);
|
|
501
|
-
const { ok, appId } = yield detectAppWithErrorLogging(opts);
|
|
502
|
-
if (!ok) {
|
|
489
|
+
}
|
|
490
|
+
if (bag === 'perms' || bag === 'all') {
|
|
491
|
+
const { ok } = await pushPerms(appId);
|
|
492
|
+
if (!ok)
|
|
503
493
|
return process.exit(1);
|
|
504
|
-
|
|
505
|
-
if (!appId) {
|
|
506
|
-
error('No app ID detected. Please specify one with --app or set up with `instant-cli init`');
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
yield pull(bag, appId, Object.assign(Object.assign({}, pkgAndAuthInfo), opts));
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
function push(bag, appId, opts) {
|
|
513
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
514
|
-
if (bag === 'schema' || bag === 'all') {
|
|
515
|
-
const { ok } = yield pushSchema(appId, opts);
|
|
516
|
-
if (!ok)
|
|
517
|
-
return process.exit(1);
|
|
518
|
-
}
|
|
519
|
-
if (bag === 'perms' || bag === 'all') {
|
|
520
|
-
const { ok } = yield pushPerms(appId);
|
|
521
|
-
if (!ok)
|
|
522
|
-
return process.exit(1);
|
|
523
|
-
}
|
|
524
|
-
});
|
|
494
|
+
}
|
|
525
495
|
}
|
|
526
496
|
function printDotEnvInfo(envType, appId) {
|
|
527
497
|
console.log(`\nPicked app ${chalk.green(appId)}!\n`);
|
|
528
498
|
console.log(`To use this app automatically from now on, update your ${chalk.green('`.env`')} file:`);
|
|
529
499
|
const picked = potentialEnvs[envType];
|
|
530
|
-
const rest =
|
|
500
|
+
const rest = { ...potentialEnvs };
|
|
531
501
|
delete rest[envType];
|
|
532
502
|
console.log(` ${chalk.green(picked)}=${appId}`);
|
|
533
503
|
const otherEnvs = Object.values(rest);
|
|
@@ -536,132 +506,121 @@ function printDotEnvInfo(envType, appId) {
|
|
|
536
506
|
console.log(`Alternative names: \n${otherEnvStr} \n`);
|
|
537
507
|
console.log(terminalLink('Dashboard:', appDashUrl(appId)) + '\n');
|
|
538
508
|
}
|
|
539
|
-
function handleEnvFile(
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
console.log(`Created ${chalk.green('.env')} file!`);
|
|
575
|
-
}
|
|
576
|
-
});
|
|
509
|
+
async function handleEnvFile(pkgAndAuthInfo, { appId, appToken }) {
|
|
510
|
+
const { pkgDir } = pkgAndAuthInfo;
|
|
511
|
+
const envType = await detectEnvType(pkgAndAuthInfo);
|
|
512
|
+
const envName = potentialEnvs[envType];
|
|
513
|
+
const envFile = program.optsWithGlobals().env ?? '.env';
|
|
514
|
+
const hasEnvFile = await pathExists(join(pkgDir, envFile));
|
|
515
|
+
if (hasEnvFile) {
|
|
516
|
+
printDotEnvInfo(envType, appId);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
console.log(`\nLooks like you don't have a ${chalk.green(`\`${envFile}\``)} file yet.`);
|
|
520
|
+
console.log(`If we set ${chalk.green(envName)} & ${chalk.green('INSTANT_APP_ADMIN_TOKEN')}, we can remember the app that you chose for all future commands.`);
|
|
521
|
+
const saveExtraInfo = envFile !== '.env' ? chalk.green(' (will create `' + envFile + '`)') : '';
|
|
522
|
+
const ok = await promptOk({
|
|
523
|
+
inline: true,
|
|
524
|
+
promptText: 'Want us to create this env file for you?' + saveExtraInfo,
|
|
525
|
+
modifyOutput: (a) => a,
|
|
526
|
+
}, program.opts(), true);
|
|
527
|
+
if (!ok) {
|
|
528
|
+
console.log(`No .env file created. You can always set ${chalk.green('`' + envName + '`')} later. \n`);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const content = [
|
|
532
|
+
[envName, appId],
|
|
533
|
+
['INSTANT_APP_ADMIN_TOKEN', appToken],
|
|
534
|
+
]
|
|
535
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
536
|
+
.join('\n') + '\n';
|
|
537
|
+
await writeFile(join(pkgDir, envFile), content, 'utf-8');
|
|
538
|
+
if (envFile !== '.env') {
|
|
539
|
+
console.log(`Created ${chalk.green(envFile)}!`);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
console.log(`Created ${chalk.green('.env')} file!`);
|
|
543
|
+
}
|
|
577
544
|
}
|
|
578
|
-
function getOrCreateAppAndWriteToEnv(pkgAndAuthInfo, opts) {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
if (!ret.ok)
|
|
582
|
-
return ret;
|
|
583
|
-
const { appId, appToken, source } = ret;
|
|
584
|
-
if (source === 'created' || source === 'imported') {
|
|
585
|
-
yield handleEnvFile(pkgAndAuthInfo, { appId, appToken });
|
|
586
|
-
}
|
|
545
|
+
async function getOrCreateAppAndWriteToEnv(pkgAndAuthInfo, opts) {
|
|
546
|
+
const ret = await detectOrCreateAppWithErrorLogging(opts);
|
|
547
|
+
if (!ret.ok)
|
|
587
548
|
return ret;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const { ok } = yield pullSchema(appId, pkgAndAuthInfo);
|
|
594
|
-
if (!ok)
|
|
595
|
-
return process.exit(1);
|
|
596
|
-
}
|
|
597
|
-
if (bag === 'perms' || bag === 'all') {
|
|
598
|
-
const { ok } = yield pullPerms(appId, pkgAndAuthInfo);
|
|
599
|
-
if (!ok)
|
|
600
|
-
return process.exit(1);
|
|
601
|
-
}
|
|
602
|
-
});
|
|
549
|
+
const { appId, appToken, source } = ret;
|
|
550
|
+
if (source === 'created' || source === 'imported') {
|
|
551
|
+
await handleEnvFile(pkgAndAuthInfo, { appId, appToken });
|
|
552
|
+
}
|
|
553
|
+
return ret;
|
|
603
554
|
}
|
|
604
|
-
function
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
path: '/dash/cli/auth/register',
|
|
609
|
-
debugName: 'Login register',
|
|
610
|
-
errorMessage: 'Failed to register login.',
|
|
611
|
-
noAuth: true,
|
|
612
|
-
command: 'login',
|
|
613
|
-
});
|
|
614
|
-
if (!registerRes.ok) {
|
|
555
|
+
async function pull(bag, appId, pkgAndAuthInfo) {
|
|
556
|
+
if (bag === 'schema' || bag === 'all') {
|
|
557
|
+
const { ok } = await pullSchema(appId, pkgAndAuthInfo);
|
|
558
|
+
if (!ok)
|
|
615
559
|
return process.exit(1);
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
if (
|
|
620
|
-
console.log(`Open this URL in a browser to log in:\n ${instantDashOrigin}/dash?ticket=${ticket}\n`);
|
|
621
|
-
}
|
|
622
|
-
else {
|
|
623
|
-
const ok = yield promptOk({
|
|
624
|
-
promptText: `This will open instantdb.com in your browser, OK to proceed?`,
|
|
625
|
-
}, program.opts(),
|
|
626
|
-
/*defaultAnswer=*/ true);
|
|
627
|
-
if (!ok)
|
|
628
|
-
return;
|
|
629
|
-
openInBrowser(`${instantDashOrigin}/dash?ticket=${ticket}`);
|
|
630
|
-
}
|
|
631
|
-
console.log('Waiting for authentication...');
|
|
632
|
-
const authTokenRes = yield waitForAuthToken({ secret });
|
|
633
|
-
if (!authTokenRes) {
|
|
560
|
+
}
|
|
561
|
+
if (bag === 'perms' || bag === 'all') {
|
|
562
|
+
const { ok } = await pullPerms(appId, pkgAndAuthInfo);
|
|
563
|
+
if (!ok)
|
|
634
564
|
return process.exit(1);
|
|
635
|
-
|
|
636
|
-
const { token, email } = authTokenRes;
|
|
637
|
-
if (options.print) {
|
|
638
|
-
console.log(chalk.red('[Do not share] Your Instant auth token:', token));
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
yield saveConfigAuthToken(token);
|
|
642
|
-
console.log(chalk.green(`Successfully logged in as ${email}!`));
|
|
643
|
-
}
|
|
644
|
-
return token;
|
|
645
|
-
});
|
|
565
|
+
}
|
|
646
566
|
}
|
|
647
|
-
function
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
567
|
+
async function login(options) {
|
|
568
|
+
const registerRes = await fetchJson({
|
|
569
|
+
method: 'POST',
|
|
570
|
+
path: '/dash/cli/auth/register',
|
|
571
|
+
debugName: 'Login register',
|
|
572
|
+
errorMessage: 'Failed to register login.',
|
|
573
|
+
noAuth: true,
|
|
574
|
+
command: 'login',
|
|
575
|
+
});
|
|
576
|
+
if (!registerRes.ok) {
|
|
577
|
+
return process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
const { secret, ticket } = registerRes.data;
|
|
580
|
+
console.log();
|
|
581
|
+
if (isHeadlessEnvironment(options)) {
|
|
582
|
+
console.log(`Open this URL in a browser to log in:\n ${instantDashOrigin}/dash?ticket=${ticket}\n`);
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
const ok = await promptOk({
|
|
586
|
+
promptText: `This will open instantdb.com in your browser, OK to proceed?`,
|
|
587
|
+
}, program.opts(),
|
|
588
|
+
/*defaultAnswer=*/ true);
|
|
589
|
+
if (!ok)
|
|
590
|
+
return;
|
|
591
|
+
openInBrowser(`${instantDashOrigin}/dash?ticket=${ticket}`);
|
|
592
|
+
}
|
|
593
|
+
console.log('Waiting for authentication...');
|
|
594
|
+
const authTokenRes = await waitForAuthToken({ secret });
|
|
595
|
+
if (!authTokenRes) {
|
|
596
|
+
return process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
const { token, email } = authTokenRes;
|
|
599
|
+
if (options.print) {
|
|
600
|
+
console.log(chalk.red('[Do not share] Your Instant auth token:', token));
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
await saveConfigAuthToken(token);
|
|
604
|
+
console.log(chalk.green(`Successfully logged in as ${email}!`));
|
|
605
|
+
}
|
|
606
|
+
return token;
|
|
607
|
+
}
|
|
608
|
+
async function logout() {
|
|
609
|
+
const { authConfigFilePath } = getAuthPaths();
|
|
610
|
+
try {
|
|
611
|
+
await unlink(authConfigFilePath);
|
|
612
|
+
console.log(chalk.green('Successfully logged out from Instant!'));
|
|
613
|
+
return true;
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
if (error.code === 'ENOENT') {
|
|
617
|
+
console.log(chalk.green('You were already logged out!'));
|
|
654
618
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
console.log(chalk.green('You were already logged out!'));
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
error('Failed to logout: ' + error.message);
|
|
661
|
-
}
|
|
662
|
-
return false;
|
|
619
|
+
else {
|
|
620
|
+
error('Failed to logout: ' + error.message);
|
|
663
621
|
}
|
|
664
|
-
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
665
624
|
}
|
|
666
625
|
const packageAliasAndFullNames = {
|
|
667
626
|
react: '@instantdb/react',
|
|
@@ -669,404 +628,376 @@ const packageAliasAndFullNames = {
|
|
|
669
628
|
core: '@instantdb/core',
|
|
670
629
|
admin: '@instantdb/admin',
|
|
671
630
|
};
|
|
672
|
-
function getOrInstallInstantModuleWithErrorLogging(pkgDir, opts) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
console.error('--yes was provided without a package specificaion and no Instant SDK was found');
|
|
693
|
-
process.exit(1);
|
|
694
|
-
}
|
|
695
|
-
moduleName = yield renderUnwrap(new UI.Select({
|
|
696
|
-
promptText: 'Which package would you like to use?',
|
|
697
|
-
options: [
|
|
698
|
-
{ label: '@instantdb/react', value: '@instantdb/react' },
|
|
699
|
-
{
|
|
700
|
-
label: '@instantdb/react-native',
|
|
701
|
-
value: '@instantdb/react-native',
|
|
702
|
-
},
|
|
703
|
-
{ label: '@instantdb/core', value: '@instantdb/core' },
|
|
704
|
-
{ label: '@instantdb/admin', value: '@instantdb/admin' },
|
|
705
|
-
],
|
|
706
|
-
}));
|
|
707
|
-
}
|
|
708
|
-
const packageManager = yield detectPackageManager(pkgDir);
|
|
709
|
-
const packagesToInstall = [moduleName];
|
|
710
|
-
if (moduleName === '@instantdb/react-native') {
|
|
711
|
-
packagesToInstall.push('react-native-get-random-values', '@react-native-async-storage/async-storage');
|
|
631
|
+
async function getOrInstallInstantModuleWithErrorLogging(pkgDir, opts) {
|
|
632
|
+
const pkgJson = await getPackageJSONWithErrorLogging(pkgDir);
|
|
633
|
+
if (!pkgJson) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
console.log('Checking for an Instant SDK...');
|
|
637
|
+
const instantModuleName = await getInstantModuleName(pkgJson);
|
|
638
|
+
if (instantModuleName) {
|
|
639
|
+
console.log(`Found ${chalk.green(instantModuleName)} in your package.json.`);
|
|
640
|
+
return instantModuleName;
|
|
641
|
+
}
|
|
642
|
+
console.log("Couldn't find an Instant SDK in your package.json, let's install one!");
|
|
643
|
+
let moduleName;
|
|
644
|
+
if (opts.package) {
|
|
645
|
+
moduleName = packageAliasAndFullNames[opts.package];
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
if (program.optsWithGlobals()?.yes) {
|
|
649
|
+
console.error('--yes was provided without a package specificaion and no Instant SDK was found');
|
|
650
|
+
process.exit(1);
|
|
712
651
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
652
|
+
moduleName = await renderUnwrap(new UI.Select({
|
|
653
|
+
promptText: 'Which package would you like to use?',
|
|
654
|
+
options: [
|
|
655
|
+
{ label: '@instantdb/react', value: '@instantdb/react' },
|
|
656
|
+
{
|
|
657
|
+
label: '@instantdb/react-native',
|
|
658
|
+
value: '@instantdb/react-native',
|
|
659
|
+
},
|
|
660
|
+
{ label: '@instantdb/core', value: '@instantdb/core' },
|
|
661
|
+
{ label: '@instantdb/admin', value: '@instantdb/admin' },
|
|
662
|
+
],
|
|
718
663
|
}));
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
const choice = yield renderUnwrap(new UI.Select({
|
|
759
|
-
promptText: 'Would you like to create the app in an organization?',
|
|
760
|
-
options: choices,
|
|
761
|
-
}));
|
|
762
|
-
if (choice) {
|
|
763
|
-
org_id = choice;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
const app = { id, title, admin_token: token, org_id };
|
|
767
|
-
const appRes = yield fetchJson({
|
|
768
|
-
method: 'POST',
|
|
769
|
-
path: '/dash/apps',
|
|
770
|
-
debugName: 'App create',
|
|
771
|
-
errorMessage: 'Failed to create app.',
|
|
772
|
-
body: app,
|
|
773
|
-
command: 'init',
|
|
774
|
-
});
|
|
775
|
-
if (!appRes.ok)
|
|
776
|
-
return { ok: false };
|
|
777
|
-
return {
|
|
778
|
-
ok: true,
|
|
779
|
-
appId: id,
|
|
780
|
-
appTitle: title,
|
|
781
|
-
appToken: token,
|
|
782
|
-
source: 'created',
|
|
783
|
-
};
|
|
664
|
+
}
|
|
665
|
+
const packageManager = await detectPackageManager(pkgDir);
|
|
666
|
+
const packagesToInstall = [moduleName];
|
|
667
|
+
if (moduleName === '@instantdb/react-native') {
|
|
668
|
+
packagesToInstall.push('react-native-get-random-values', '@react-native-async-storage/async-storage');
|
|
669
|
+
}
|
|
670
|
+
const installCommand = getInstallCommand(packageManager, packagesToInstall.join(' '));
|
|
671
|
+
await renderUnwrap(new UI.Spinner({
|
|
672
|
+
promise: execAsync(installCommand, pkgDir),
|
|
673
|
+
workingText: `Installing ${packagesToInstall.join(', ')} using ${packageManager}...`,
|
|
674
|
+
doneText: `Installed ${packagesToInstall.join(', ')} using ${packageManager}.`,
|
|
675
|
+
}));
|
|
676
|
+
return moduleName;
|
|
677
|
+
}
|
|
678
|
+
async function promptCreateApp(opts) {
|
|
679
|
+
const id = randomUUID();
|
|
680
|
+
const token = randomUUID();
|
|
681
|
+
let _title;
|
|
682
|
+
if (opts?.title) {
|
|
683
|
+
_title = opts.title;
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
_title = await renderUnwrap(new UI.TextInput({
|
|
687
|
+
prompt: 'What would you like to call it?',
|
|
688
|
+
placeholder: 'My cool app',
|
|
689
|
+
})).catch(() => null);
|
|
690
|
+
}
|
|
691
|
+
const title = _title?.trim();
|
|
692
|
+
if (!title) {
|
|
693
|
+
error('No name provided.');
|
|
694
|
+
return { ok: false };
|
|
695
|
+
}
|
|
696
|
+
const res = await fetchJson({
|
|
697
|
+
debugName: 'Fetching orgs',
|
|
698
|
+
method: 'GET',
|
|
699
|
+
path: '/dash',
|
|
700
|
+
errorMessage: 'Failed to fetch apps.',
|
|
701
|
+
command: 'init',
|
|
784
702
|
});
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const result = yield renderUnwrap(new UI.AppSelector({
|
|
799
|
-
allowEphemeral: true,
|
|
800
|
-
allowCreate: true,
|
|
801
|
-
startingMenuIndex: 2,
|
|
802
|
-
api: {
|
|
803
|
-
getDash: () => res.data,
|
|
804
|
-
createEphemeralApp,
|
|
805
|
-
getAppsForOrg: (orgId) => __awaiter(this, void 0, void 0, function* () {
|
|
806
|
-
const orgsRes = yield fetchJson({
|
|
807
|
-
debugName: 'Fetching org apps',
|
|
808
|
-
method: 'GET',
|
|
809
|
-
path: `/dash/orgs/${orgId}`,
|
|
810
|
-
errorMessage: 'Failed to fetch apps.',
|
|
811
|
-
command: 'init',
|
|
812
|
-
});
|
|
813
|
-
if (!orgsRes.ok) {
|
|
814
|
-
throw new Error('Failed to fetch org apps');
|
|
815
|
-
}
|
|
816
|
-
return { apps: orgsRes.data.apps };
|
|
817
|
-
}),
|
|
818
|
-
createApp,
|
|
819
|
-
},
|
|
703
|
+
if (!res.ok) {
|
|
704
|
+
return { ok: false };
|
|
705
|
+
}
|
|
706
|
+
const allowedOrgs = res.data.orgs.filter((org) => org.role !== 'app-member');
|
|
707
|
+
let org_id = opts.org;
|
|
708
|
+
if (!org_id && allowedOrgs.length) {
|
|
709
|
+
const choices = [{ label: '(No organization)', value: null }];
|
|
710
|
+
for (const org of allowedOrgs) {
|
|
711
|
+
choices.push({ label: org.title, value: org.id });
|
|
712
|
+
}
|
|
713
|
+
const choice = await renderUnwrap(new UI.Select({
|
|
714
|
+
promptText: 'Would you like to create the app in an organization?',
|
|
715
|
+
options: choices,
|
|
820
716
|
}));
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
appId: result.appId,
|
|
824
|
-
appToken: result.adminToken,
|
|
825
|
-
source: result.approach === 'import' ? 'imported' : 'created',
|
|
826
|
-
};
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
function createApp(title, orgId) {
|
|
830
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
831
|
-
const id = randomUUID();
|
|
832
|
-
const token = randomUUID();
|
|
833
|
-
const app = { id, title, admin_token: token, org_id: orgId };
|
|
834
|
-
const appRes = yield fetchJson({
|
|
835
|
-
method: 'POST',
|
|
836
|
-
path: '/dash/apps',
|
|
837
|
-
debugName: 'App create',
|
|
838
|
-
errorMessage: 'Failed to create app.',
|
|
839
|
-
body: app,
|
|
840
|
-
command: 'init',
|
|
841
|
-
});
|
|
842
|
-
if (!appRes.ok)
|
|
843
|
-
throw new Error('Failed to create app');
|
|
844
|
-
return { appId: id, adminToken: token };
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
function createEphemeralApp(title) {
|
|
848
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
849
|
-
const api = new PlatformApi({ apiURI: instantBackendOrigin });
|
|
850
|
-
const { app } = yield api.createTemporaryApp({ title });
|
|
851
|
-
return { appId: app.id, adminToken: app.adminToken };
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
function detectAppWithErrorLogging(opts) {
|
|
855
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
856
|
-
const fromOpts = yield detectAppIdFromOptsWithErrorLogging(opts);
|
|
857
|
-
if (!fromOpts.ok)
|
|
858
|
-
return fromOpts;
|
|
859
|
-
if (fromOpts.appId) {
|
|
860
|
-
return { ok: true, appId: fromOpts.appId, source: 'opts' };
|
|
717
|
+
if (choice) {
|
|
718
|
+
org_id = choice;
|
|
861
719
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
function detectOrCreateAppWithErrorLogging(opts) {
|
|
874
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
875
|
-
const detected = yield detectAppWithErrorLogging(opts);
|
|
876
|
-
if (!detected.ok)
|
|
877
|
-
return detected;
|
|
878
|
-
if (detected.appId) {
|
|
879
|
-
return detected;
|
|
880
|
-
}
|
|
881
|
-
let action;
|
|
882
|
-
if (program.optsWithGlobals().yes) {
|
|
883
|
-
action = 'create';
|
|
884
|
-
if (!(opts === null || opts === void 0 ? void 0 : opts.title)) {
|
|
885
|
-
console.error(chalk.red(`Title is required when using --yes and no app is linked`));
|
|
886
|
-
process.exit(1);
|
|
887
|
-
}
|
|
888
|
-
const app = yield createApp(opts.title);
|
|
889
|
-
return { ok: true, appId: app.appId, source: 'created' };
|
|
890
|
-
}
|
|
891
|
-
else {
|
|
892
|
-
console.log();
|
|
893
|
-
return yield promptImportAppOrCreateApp();
|
|
894
|
-
}
|
|
895
|
-
});
|
|
896
|
-
}
|
|
897
|
-
function writeTypescript(path, content, encoding) {
|
|
898
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
899
|
-
const prettierConfig = yield prettier.resolveConfig(path);
|
|
900
|
-
const formattedCode = yield prettier.format(content, Object.assign(Object.assign({}, prettierConfig), { parser: 'typescript' }));
|
|
901
|
-
return yield writeFile(path, formattedCode, encoding);
|
|
720
|
+
}
|
|
721
|
+
const app = { id, title, admin_token: token, org_id };
|
|
722
|
+
const appRes = await fetchJson({
|
|
723
|
+
method: 'POST',
|
|
724
|
+
path: '/dash/apps',
|
|
725
|
+
debugName: 'App create',
|
|
726
|
+
errorMessage: 'Failed to create app.',
|
|
727
|
+
body: app,
|
|
728
|
+
command: 'init',
|
|
902
729
|
});
|
|
730
|
+
if (!appRes.ok)
|
|
731
|
+
return { ok: false };
|
|
732
|
+
return {
|
|
733
|
+
ok: true,
|
|
734
|
+
appId: id,
|
|
735
|
+
appTitle: title,
|
|
736
|
+
appToken: token,
|
|
737
|
+
source: 'created',
|
|
738
|
+
};
|
|
903
739
|
}
|
|
904
|
-
function
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
'@instantdb/core',
|
|
912
|
-
'@instantdb/admin',
|
|
913
|
-
].find((name) => deps[name] || devDeps[name]);
|
|
914
|
-
return instantModuleName;
|
|
740
|
+
async function promptImportAppOrCreateApp() {
|
|
741
|
+
const res = await fetchJson({
|
|
742
|
+
debugName: 'Fetching apps',
|
|
743
|
+
method: 'GET',
|
|
744
|
+
path: '/dash',
|
|
745
|
+
errorMessage: 'Failed to fetch apps.',
|
|
746
|
+
command: 'init',
|
|
915
747
|
});
|
|
748
|
+
if (!res.ok) {
|
|
749
|
+
return { ok: false };
|
|
750
|
+
}
|
|
751
|
+
const result = await renderUnwrap(new UI.AppSelector({
|
|
752
|
+
allowEphemeral: true,
|
|
753
|
+
allowCreate: true,
|
|
754
|
+
startingMenuIndex: 2,
|
|
755
|
+
api: {
|
|
756
|
+
getDash: () => res.data,
|
|
757
|
+
createEphemeralApp,
|
|
758
|
+
getAppsForOrg: async (orgId) => {
|
|
759
|
+
const orgsRes = await fetchJson({
|
|
760
|
+
debugName: 'Fetching org apps',
|
|
761
|
+
method: 'GET',
|
|
762
|
+
path: `/dash/orgs/${orgId}`,
|
|
763
|
+
errorMessage: 'Failed to fetch apps.',
|
|
764
|
+
command: 'init',
|
|
765
|
+
});
|
|
766
|
+
if (!orgsRes.ok) {
|
|
767
|
+
throw new Error('Failed to fetch org apps');
|
|
768
|
+
}
|
|
769
|
+
return { apps: orgsRes.data.apps };
|
|
770
|
+
},
|
|
771
|
+
createApp,
|
|
772
|
+
},
|
|
773
|
+
}));
|
|
774
|
+
return {
|
|
775
|
+
ok: true,
|
|
776
|
+
appId: result.appId,
|
|
777
|
+
appToken: result.adminToken,
|
|
778
|
+
source: result.approach === 'import' ? 'imported' : 'created',
|
|
779
|
+
};
|
|
916
780
|
}
|
|
917
|
-
function
|
|
918
|
-
|
|
919
|
-
|
|
781
|
+
async function createApp(title, orgId) {
|
|
782
|
+
const id = randomUUID();
|
|
783
|
+
const token = randomUUID();
|
|
784
|
+
const app = { id, title, admin_token: token, org_id: orgId };
|
|
785
|
+
const appRes = await fetchJson({
|
|
786
|
+
method: 'POST',
|
|
787
|
+
path: '/dash/apps',
|
|
788
|
+
debugName: 'App create',
|
|
789
|
+
errorMessage: 'Failed to create app.',
|
|
790
|
+
body: app,
|
|
791
|
+
command: 'init',
|
|
920
792
|
});
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
return
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
793
|
+
if (!appRes.ok)
|
|
794
|
+
throw new Error('Failed to create app');
|
|
795
|
+
return { appId: id, adminToken: token };
|
|
796
|
+
}
|
|
797
|
+
async function createEphemeralApp(title) {
|
|
798
|
+
const api = new PlatformApi({ apiURI: instantBackendOrigin });
|
|
799
|
+
const { app } = await api.createTemporaryApp({ title });
|
|
800
|
+
return { appId: app.id, adminToken: app.adminToken };
|
|
801
|
+
}
|
|
802
|
+
async function detectAppWithErrorLogging(opts) {
|
|
803
|
+
const fromOpts = await detectAppIdFromOptsWithErrorLogging(opts);
|
|
804
|
+
if (!fromOpts.ok)
|
|
805
|
+
return fromOpts;
|
|
806
|
+
if (fromOpts.appId) {
|
|
807
|
+
return { ok: true, appId: fromOpts.appId, source: 'opts' };
|
|
808
|
+
}
|
|
809
|
+
const fromEnv = detectAppIdFromEnvWithErrorLogging();
|
|
810
|
+
if (!fromEnv.ok)
|
|
811
|
+
return fromEnv;
|
|
812
|
+
if (fromEnv.found) {
|
|
813
|
+
const { envName, value } = fromEnv.found;
|
|
814
|
+
console.log(`Found ${chalk.green(envName)}: ${value}`);
|
|
815
|
+
return { ok: true, appId: value, source: 'env' };
|
|
816
|
+
}
|
|
817
|
+
return { ok: true };
|
|
818
|
+
}
|
|
819
|
+
async function detectOrCreateAppWithErrorLogging(opts) {
|
|
820
|
+
const detected = await detectAppWithErrorLogging(opts);
|
|
821
|
+
if (!detected.ok)
|
|
822
|
+
return detected;
|
|
823
|
+
if (detected.appId) {
|
|
824
|
+
return detected;
|
|
825
|
+
}
|
|
826
|
+
let action;
|
|
827
|
+
if (program.optsWithGlobals().yes) {
|
|
828
|
+
action = 'create';
|
|
829
|
+
if (!opts?.title) {
|
|
830
|
+
console.error(chalk.red(`Title is required when using --yes and no app is linked`));
|
|
831
|
+
process.exit(1);
|
|
928
832
|
}
|
|
929
|
-
|
|
833
|
+
const app = await createApp(opts.title);
|
|
834
|
+
return { ok: true, appId: app.appId, source: 'created' };
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
console.log();
|
|
838
|
+
return await promptImportAppOrCreateApp();
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
async function writeTypescript(path, content, encoding) {
|
|
842
|
+
const prettierConfig = await prettier.resolveConfig(path);
|
|
843
|
+
const formattedCode = await prettier.format(content, {
|
|
844
|
+
...prettierConfig,
|
|
845
|
+
parser: 'typescript',
|
|
930
846
|
});
|
|
847
|
+
return await writeFile(path, formattedCode, encoding);
|
|
848
|
+
}
|
|
849
|
+
async function getInstantModuleName(pkgJson) {
|
|
850
|
+
const deps = pkgJson.dependencies || {};
|
|
851
|
+
const devDeps = pkgJson.devDependencies || {};
|
|
852
|
+
const instantModuleName = [
|
|
853
|
+
'@instantdb/react',
|
|
854
|
+
'@instantdb/react-native',
|
|
855
|
+
'@instantdb/core',
|
|
856
|
+
'@instantdb/admin',
|
|
857
|
+
].find((name) => deps[name] || devDeps[name]);
|
|
858
|
+
return instantModuleName;
|
|
859
|
+
}
|
|
860
|
+
async function getPackageJson(pkgDir) {
|
|
861
|
+
return await readJsonFile(join(pkgDir, 'package.json'));
|
|
862
|
+
}
|
|
863
|
+
async function getPackageJSONWithErrorLogging(pkgDir) {
|
|
864
|
+
const pkgJson = await getPackageJson(pkgDir);
|
|
865
|
+
if (!pkgJson) {
|
|
866
|
+
error(`Couldn't find a packge.json file in: ${pkgDir}. Please add one.`);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
return pkgJson;
|
|
931
870
|
}
|
|
932
|
-
function enforcePackageAndAuthInfoWithErrorLogging(_opts) {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
const authToken = yield readConfigAuthTokenWithErrorLogging();
|
|
942
|
-
if (!authToken) {
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
return {
|
|
946
|
-
pkgDir,
|
|
947
|
-
projectType,
|
|
948
|
-
instantModuleName: '@instantdb/core',
|
|
949
|
-
authToken,
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
const pkgJson = yield getPackageJSONWithErrorLogging(pkgDir);
|
|
953
|
-
if (!pkgJson) {
|
|
954
|
-
return;
|
|
955
|
-
}
|
|
956
|
-
const instantModuleName = yield getInstantModuleName(pkgJson);
|
|
957
|
-
if (!instantModuleName) {
|
|
958
|
-
error("We couldn't find an Instant SDK. Install one, or run `init`");
|
|
959
|
-
}
|
|
960
|
-
const authToken = yield readConfigAuthTokenWithErrorLogging();
|
|
871
|
+
async function enforcePackageAndAuthInfoWithErrorLogging(_opts) {
|
|
872
|
+
const projectInfo = await packageDirectoryWithErrorLogging();
|
|
873
|
+
if (!projectInfo) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const { dir: pkgDir, type: projectType } = projectInfo;
|
|
877
|
+
// Deno projects don't have package.json or node_modules
|
|
878
|
+
if (projectType === 'deno') {
|
|
879
|
+
const authToken = await readConfigAuthTokenWithErrorLogging();
|
|
961
880
|
if (!authToken) {
|
|
962
881
|
return;
|
|
963
882
|
}
|
|
964
|
-
return {
|
|
965
|
-
|
|
883
|
+
return {
|
|
884
|
+
pkgDir,
|
|
885
|
+
projectType,
|
|
886
|
+
instantModuleName: '@instantdb/core',
|
|
887
|
+
authToken,
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
const pkgJson = await getPackageJSONWithErrorLogging(pkgDir);
|
|
891
|
+
if (!pkgJson) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const instantModuleName = await getInstantModuleName(pkgJson);
|
|
895
|
+
if (!instantModuleName) {
|
|
896
|
+
error("We couldn't find an Instant SDK. Install one, or run `init`");
|
|
897
|
+
}
|
|
898
|
+
const authToken = await readConfigAuthTokenWithErrorLogging();
|
|
899
|
+
if (!authToken) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
return { pkgDir, projectType, instantModuleName, authToken };
|
|
966
903
|
}
|
|
967
|
-
function getOrPromptPackageAndAuthInfoWithErrorLogging(opts) {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const authToken = yield readAuthTokenOrLoginWithErrorLogging();
|
|
977
|
-
if (!authToken) {
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
return {
|
|
981
|
-
pkgDir,
|
|
982
|
-
projectType,
|
|
983
|
-
instantModuleName: '@instantdb/core',
|
|
984
|
-
authToken,
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
const instantModuleName = yield getOrInstallInstantModuleWithErrorLogging(pkgDir, opts);
|
|
988
|
-
if (!instantModuleName) {
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
991
|
-
const authToken = yield readAuthTokenOrLoginWithErrorLogging();
|
|
904
|
+
async function getOrPromptPackageAndAuthInfoWithErrorLogging(opts) {
|
|
905
|
+
const projectInfo = await packageDirectoryWithErrorLogging();
|
|
906
|
+
if (!projectInfo) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
const { dir: pkgDir, type: projectType } = projectInfo;
|
|
910
|
+
// Deno projects don't have package.json or node_modules
|
|
911
|
+
if (projectType === 'deno') {
|
|
912
|
+
const authToken = await readAuthTokenOrLoginWithErrorLogging();
|
|
992
913
|
if (!authToken) {
|
|
993
914
|
return;
|
|
994
915
|
}
|
|
995
|
-
return {
|
|
916
|
+
return {
|
|
917
|
+
pkgDir,
|
|
918
|
+
projectType,
|
|
919
|
+
instantModuleName: '@instantdb/core',
|
|
920
|
+
authToken,
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
const instantModuleName = await getOrInstallInstantModuleWithErrorLogging(pkgDir, opts);
|
|
924
|
+
if (!instantModuleName) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const authToken = await readAuthTokenOrLoginWithErrorLogging();
|
|
928
|
+
if (!authToken) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
return { pkgDir, projectType, instantModuleName, authToken };
|
|
932
|
+
}
|
|
933
|
+
async function pullSchema(appId, { pkgDir, instantModuleName, experimentalTypePreservation }) {
|
|
934
|
+
console.log('Pulling schema...');
|
|
935
|
+
const pullRes = await fetchJson({
|
|
936
|
+
path: `/dash/apps/${appId}/schema/pull`,
|
|
937
|
+
debugName: 'Schema pull',
|
|
938
|
+
errorMessage: 'Failed to pull schema.',
|
|
939
|
+
command: 'pull',
|
|
996
940
|
});
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
941
|
+
if (!pullRes.ok)
|
|
942
|
+
return pullRes;
|
|
943
|
+
if (!countEntities(pullRes.data.schema.refs) &&
|
|
944
|
+
!countEntities(pullRes.data.schema.blobs)) {
|
|
945
|
+
console.log('Schema is empty. Skipping.');
|
|
946
|
+
return { ok: true };
|
|
947
|
+
}
|
|
948
|
+
const prev = await readLocalSchemaFile();
|
|
949
|
+
if (prev) {
|
|
950
|
+
const shouldContinue = await promptOk({
|
|
951
|
+
promptText: 'This will overwrite your local instant.schema.ts file, OK to proceed?',
|
|
952
|
+
modifyOutput: UI.modifiers.yPadding,
|
|
953
|
+
inline: true,
|
|
954
|
+
}, program.opts());
|
|
955
|
+
console.log();
|
|
956
|
+
if (!shouldContinue)
|
|
1012
957
|
return { ok: true };
|
|
958
|
+
}
|
|
959
|
+
const shortSchemaPath = getSchemaPathToWrite(prev?.path);
|
|
960
|
+
const schemaPath = join(pkgDir, shortSchemaPath);
|
|
961
|
+
let newSchemaContent = generateSchemaTypescriptFile(prev?.schema, apiSchemaToInstantSchemaDef(pullRes.data.schema), instantModuleName);
|
|
962
|
+
if (prev && experimentalTypePreservation) {
|
|
963
|
+
try {
|
|
964
|
+
const oldSchemaContent = await readFile(prev.path, 'utf-8');
|
|
965
|
+
newSchemaContent = mergeSchema(oldSchemaContent, newSchemaContent);
|
|
1013
966
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
const shouldContinue = yield promptOk({
|
|
1017
|
-
promptText: 'This will overwrite your local instant.schema.ts file, OK to proceed?',
|
|
1018
|
-
modifyOutput: UI.modifiers.yPadding,
|
|
1019
|
-
inline: true,
|
|
1020
|
-
}, program.opts());
|
|
1021
|
-
console.log();
|
|
1022
|
-
if (!shouldContinue)
|
|
1023
|
-
return { ok: true };
|
|
1024
|
-
}
|
|
1025
|
-
const shortSchemaPath = getSchemaPathToWrite(prev === null || prev === void 0 ? void 0 : prev.path);
|
|
1026
|
-
const schemaPath = join(pkgDir, shortSchemaPath);
|
|
1027
|
-
let newSchemaContent = generateSchemaTypescriptFile(prev === null || prev === void 0 ? void 0 : prev.schema, apiSchemaToInstantSchemaDef(pullRes.data.schema), instantModuleName);
|
|
1028
|
-
if (prev && experimentalTypePreservation) {
|
|
1029
|
-
try {
|
|
1030
|
-
const oldSchemaContent = yield readFile(prev.path, 'utf-8');
|
|
1031
|
-
newSchemaContent = mergeSchema(oldSchemaContent, newSchemaContent);
|
|
1032
|
-
}
|
|
1033
|
-
catch (e) {
|
|
1034
|
-
warn('Failed to merge schema with existing file. Overwriting instead.', e);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
yield writeTypescript(schemaPath, newSchemaContent, 'utf-8');
|
|
1038
|
-
console.log('✅ Wrote schema to ' + shortSchemaPath);
|
|
1039
|
-
return { ok: true };
|
|
1040
|
-
});
|
|
1041
|
-
}
|
|
1042
|
-
function pullPerms(appId_1, _a) {
|
|
1043
|
-
return __awaiter(this, arguments, void 0, function* (appId, { pkgDir, instantModuleName }) {
|
|
1044
|
-
console.log('Pulling perms...');
|
|
1045
|
-
const pullRes = yield fetchJson({
|
|
1046
|
-
path: `/dash/apps/${appId}/perms/pull`,
|
|
1047
|
-
debugName: 'Perms pull',
|
|
1048
|
-
errorMessage: 'Failed to pull perms.',
|
|
1049
|
-
command: 'pull',
|
|
1050
|
-
});
|
|
1051
|
-
if (!pullRes.ok)
|
|
1052
|
-
return pullRes;
|
|
1053
|
-
const prev = yield readLocalPermsFile();
|
|
1054
|
-
if (prev) {
|
|
1055
|
-
const shouldContinue = yield promptOk({
|
|
1056
|
-
promptText: 'This will overwrite your local instant.perms.ts file, OK to proceed?',
|
|
1057
|
-
modifyOutput: UI.modifiers.yPadding,
|
|
1058
|
-
inline: true,
|
|
1059
|
-
}, program.opts());
|
|
1060
|
-
console.log();
|
|
1061
|
-
if (!shouldContinue)
|
|
1062
|
-
return { ok: true };
|
|
967
|
+
catch (e) {
|
|
968
|
+
warn('Failed to merge schema with existing file. Overwriting instead.', e);
|
|
1063
969
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
970
|
+
}
|
|
971
|
+
await writeTypescript(schemaPath, newSchemaContent, 'utf-8');
|
|
972
|
+
console.log('✅ Wrote schema to ' + shortSchemaPath);
|
|
973
|
+
return { ok: true };
|
|
974
|
+
}
|
|
975
|
+
async function pullPerms(appId, { pkgDir, instantModuleName }) {
|
|
976
|
+
console.log('Pulling perms...');
|
|
977
|
+
const pullRes = await fetchJson({
|
|
978
|
+
path: `/dash/apps/${appId}/perms/pull`,
|
|
979
|
+
debugName: 'Perms pull',
|
|
980
|
+
errorMessage: 'Failed to pull perms.',
|
|
981
|
+
command: 'pull',
|
|
1069
982
|
});
|
|
983
|
+
if (!pullRes.ok)
|
|
984
|
+
return pullRes;
|
|
985
|
+
const prev = await readLocalPermsFile();
|
|
986
|
+
if (prev) {
|
|
987
|
+
const shouldContinue = await promptOk({
|
|
988
|
+
promptText: 'This will overwrite your local instant.perms.ts file, OK to proceed?',
|
|
989
|
+
modifyOutput: UI.modifiers.yPadding,
|
|
990
|
+
inline: true,
|
|
991
|
+
}, program.opts());
|
|
992
|
+
console.log();
|
|
993
|
+
if (!shouldContinue)
|
|
994
|
+
return { ok: true };
|
|
995
|
+
}
|
|
996
|
+
const shortPermsPath = getPermsPathToWrite(prev?.path);
|
|
997
|
+
const permsPath = join(pkgDir, shortPermsPath);
|
|
998
|
+
await writeTypescript(permsPath, generatePermsTypescriptFile(pullRes.data.perms || {}, instantModuleName), 'utf-8');
|
|
999
|
+
console.log('✅ Wrote permissions to ' + shortPermsPath);
|
|
1000
|
+
return { ok: true };
|
|
1070
1001
|
}
|
|
1071
1002
|
function indexingJobCompletedActionMessage(job) {
|
|
1072
1003
|
if (job.job_type === 'check-data-type') {
|
|
@@ -1103,7 +1034,7 @@ function truncate(s, maxLen) {
|
|
|
1103
1034
|
}
|
|
1104
1035
|
function formatSamples(triples_samples) {
|
|
1105
1036
|
return triples_samples.slice(0, 3).map((t) => {
|
|
1106
|
-
return
|
|
1037
|
+
return { ...t, value: truncate(JSON.stringify(t.value), 32) };
|
|
1107
1038
|
});
|
|
1108
1039
|
}
|
|
1109
1040
|
function createUrl(triple, job) {
|
|
@@ -1124,7 +1055,6 @@ function padCell(value, width) {
|
|
|
1124
1055
|
return trimmed + ' '.repeat(width - trimmed.length);
|
|
1125
1056
|
}
|
|
1126
1057
|
function indexingJobCompletedMessage(job) {
|
|
1127
|
-
var _a;
|
|
1128
1058
|
const actionMessage = indexingJobCompletedActionMessage(job);
|
|
1129
1059
|
if (job.job_status === 'canceled') {
|
|
1130
1060
|
return `Canceled ${actionMessage} before it could finish.`;
|
|
@@ -1133,7 +1063,7 @@ function indexingJobCompletedMessage(job) {
|
|
|
1133
1063
|
return `Finished ${actionMessage}.`;
|
|
1134
1064
|
}
|
|
1135
1065
|
if (job.job_status === 'errored') {
|
|
1136
|
-
if (
|
|
1066
|
+
if (job.invalid_triples_sample?.length) {
|
|
1137
1067
|
const [etype, label] = job.attr_name.split('.');
|
|
1138
1068
|
const samples = formatSamples(job.invalid_triples_sample);
|
|
1139
1069
|
const longestValue = samples.reduce((acc, { value }) => Math.max(acc, value.length), label.length);
|
|
@@ -1201,88 +1131,85 @@ function jobGroupDescription(jobs) {
|
|
|
1201
1131
|
}
|
|
1202
1132
|
return joinInSentence([...actions].sort()) || 'updating schema';
|
|
1203
1133
|
}
|
|
1204
|
-
function waitForIndexingJobsToFinish(appId, data) {
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
else {
|
|
1245
|
-
spinner.addMessage(msg);
|
|
1246
|
-
}
|
|
1134
|
+
async function waitForIndexingJobsToFinish(appId, data) {
|
|
1135
|
+
const spinnerDefferedPromise = deferred();
|
|
1136
|
+
const spinner = new UI.Spinner({
|
|
1137
|
+
promise: spinnerDefferedPromise.promise,
|
|
1138
|
+
});
|
|
1139
|
+
const spinnerRenderPromise = renderUnwrap(spinner);
|
|
1140
|
+
const groupId = data['group-id'];
|
|
1141
|
+
let jobs = data.jobs;
|
|
1142
|
+
let waitMs = 20;
|
|
1143
|
+
let lastUpdatedAt = new Date(0);
|
|
1144
|
+
const completedIds = new Set();
|
|
1145
|
+
const errorMessages = [];
|
|
1146
|
+
while (true) {
|
|
1147
|
+
let stillRunning = false;
|
|
1148
|
+
let updated = false;
|
|
1149
|
+
let workEstimateTotal = 0;
|
|
1150
|
+
let workCompletedTotal = 0;
|
|
1151
|
+
for (const job of jobs) {
|
|
1152
|
+
const updatedAt = new Date(job.updated_at);
|
|
1153
|
+
if (updatedAt > lastUpdatedAt) {
|
|
1154
|
+
updated = true;
|
|
1155
|
+
lastUpdatedAt = updatedAt;
|
|
1156
|
+
}
|
|
1157
|
+
if (job.job_status === 'waiting' || job.job_status === 'processing') {
|
|
1158
|
+
stillRunning = true;
|
|
1159
|
+
// Default estimate to high value to prevent % from jumping around
|
|
1160
|
+
workEstimateTotal += job.work_estimate ?? 50000;
|
|
1161
|
+
workCompletedTotal += job.work_completed ?? 0;
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
if (!completedIds.has(job.id)) {
|
|
1165
|
+
completedIds.add(job.id);
|
|
1166
|
+
const msg = indexingJobCompletedMessage(job);
|
|
1167
|
+
if (msg) {
|
|
1168
|
+
if (job.job_status === 'errored') {
|
|
1169
|
+
spinner.addMessage(msg);
|
|
1170
|
+
errorMessages.push(msg);
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
spinner.addMessage(msg);
|
|
1247
1174
|
}
|
|
1248
1175
|
}
|
|
1249
1176
|
}
|
|
1250
1177
|
}
|
|
1251
|
-
if (!stillRunning) {
|
|
1252
|
-
break;
|
|
1253
|
-
}
|
|
1254
|
-
if (workEstimateTotal) {
|
|
1255
|
-
const percent = Math.floor((workCompletedTotal / workEstimateTotal) * 100);
|
|
1256
|
-
spinner.updateText(`${jobGroupDescription(jobs)} ${percent}%`);
|
|
1257
|
-
}
|
|
1258
|
-
waitMs = updated ? 1 : Math.min(10000, waitMs * 2);
|
|
1259
|
-
yield sleep(waitMs);
|
|
1260
|
-
const res = yield fetchJson({
|
|
1261
|
-
debugName: 'Check indexing status',
|
|
1262
|
-
method: 'GET',
|
|
1263
|
-
path: `/dash/apps/${appId}/indexing-jobs/group/${groupId}`,
|
|
1264
|
-
errorMessage: 'Failed to check indexing status.',
|
|
1265
|
-
command: 'push',
|
|
1266
|
-
});
|
|
1267
|
-
if (!res.ok) {
|
|
1268
|
-
break;
|
|
1269
|
-
}
|
|
1270
|
-
jobs = res.data.jobs;
|
|
1271
1178
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
// Log errors at the end so that they're easier to see.
|
|
1275
|
-
if (errorMessages.length) {
|
|
1276
|
-
for (const msg of errorMessages) {
|
|
1277
|
-
console.log(msg);
|
|
1278
|
-
}
|
|
1279
|
-
console.log(chalk.red('Some steps failed while updating schema.'));
|
|
1280
|
-
process.exit(1);
|
|
1179
|
+
if (!stillRunning) {
|
|
1180
|
+
break;
|
|
1281
1181
|
}
|
|
1282
|
-
|
|
1182
|
+
if (workEstimateTotal) {
|
|
1183
|
+
const percent = Math.floor((workCompletedTotal / workEstimateTotal) * 100);
|
|
1184
|
+
spinner.updateText(`${jobGroupDescription(jobs)} ${percent}%`);
|
|
1185
|
+
}
|
|
1186
|
+
waitMs = updated ? 1 : Math.min(10000, waitMs * 2);
|
|
1187
|
+
await sleep(waitMs);
|
|
1188
|
+
const res = await fetchJson({
|
|
1189
|
+
debugName: 'Check indexing status',
|
|
1190
|
+
method: 'GET',
|
|
1191
|
+
path: `/dash/apps/${appId}/indexing-jobs/group/${groupId}`,
|
|
1192
|
+
errorMessage: 'Failed to check indexing status.',
|
|
1193
|
+
command: 'push',
|
|
1194
|
+
});
|
|
1195
|
+
if (!res.ok) {
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
jobs = res.data.jobs;
|
|
1199
|
+
}
|
|
1200
|
+
spinnerDefferedPromise.resolve(null);
|
|
1201
|
+
await spinnerRenderPromise;
|
|
1202
|
+
// Log errors at the end so that they're easier to see.
|
|
1203
|
+
if (errorMessages.length) {
|
|
1204
|
+
for (const msg of errorMessages) {
|
|
1205
|
+
console.log(msg);
|
|
1206
|
+
}
|
|
1207
|
+
console.log(chalk.red('Some steps failed while updating schema.'));
|
|
1208
|
+
process.exit(1);
|
|
1209
|
+
}
|
|
1283
1210
|
}
|
|
1284
|
-
const resolveRenames = (created, promptData, extraInfo) =>
|
|
1285
|
-
const answer =
|
|
1211
|
+
const resolveRenames = async (created, promptData, extraInfo) => {
|
|
1212
|
+
const answer = await renderUnwrap(new ResolveRenamePrompt(created, promptData, extraInfo, UI.modifiers.piped([
|
|
1286
1213
|
(out) => boxen(out, {
|
|
1287
1214
|
dimBorder: true,
|
|
1288
1215
|
padding: {
|
|
@@ -1293,7 +1220,7 @@ const resolveRenames = (created, promptData, extraInfo) => __awaiter(void 0, voi
|
|
|
1293
1220
|
UI.modifiers.vanishOnComplete,
|
|
1294
1221
|
])));
|
|
1295
1222
|
return answer;
|
|
1296
|
-
}
|
|
1223
|
+
};
|
|
1297
1224
|
function collectSystemCatalogIdentNames(currentAttrs) {
|
|
1298
1225
|
const allSystemIdents = currentAttrs
|
|
1299
1226
|
.filter((attr) => attr.catalog === 'system')
|
|
@@ -1306,151 +1233,64 @@ function collectSystemCatalogIdentNames(currentAttrs) {
|
|
|
1306
1233
|
}
|
|
1307
1234
|
return res;
|
|
1308
1235
|
}
|
|
1309
|
-
function pushSchema(appId, opts) {
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
console.error(chalk.red('Invalid schema:', error.message));
|
|
1336
|
-
}
|
|
1337
|
-
else {
|
|
1338
|
-
console.error('Unexpected error:', error);
|
|
1339
|
-
}
|
|
1340
|
-
return { ok: false };
|
|
1341
|
-
}
|
|
1342
|
-
const renameSelector = program.optsWithGlobals().yes
|
|
1343
|
-
? buildAutoRenameSelector(opts)
|
|
1344
|
-
: resolveRenames;
|
|
1345
|
-
const diffResult = yield diffSchemas(oldSchema, schema, renameSelector, systemCatalogIdentNames);
|
|
1346
|
-
if (currentAttrs === undefined) {
|
|
1347
|
-
throw new Error("Couldn't get current schema from server");
|
|
1348
|
-
}
|
|
1349
|
-
const txSteps = convertTxSteps(diffResult, currentAttrs);
|
|
1350
|
-
if (txSteps.length === 0) {
|
|
1351
|
-
console.log(chalk.bgGray('No schema changes to apply!'));
|
|
1352
|
-
return { ok: true };
|
|
1353
|
-
}
|
|
1354
|
-
let wantsToPush = false;
|
|
1355
|
-
try {
|
|
1356
|
-
const groupedSteps = groupSteps(diffResult);
|
|
1357
|
-
const lines = renderSchemaPlan(groupedSteps, currentAttrs);
|
|
1358
|
-
if (program.optsWithGlobals().yes) {
|
|
1359
|
-
console.log('Applying schema changes...');
|
|
1360
|
-
console.log(lines.join('\n'));
|
|
1361
|
-
}
|
|
1362
|
-
wantsToPush = yield promptOk({
|
|
1363
|
-
promptText: 'Push these changes?',
|
|
1364
|
-
yesText: 'Push',
|
|
1365
|
-
noText: 'Cancel',
|
|
1366
|
-
modifyOutput: (output) => {
|
|
1367
|
-
let both = lines.join('\n') + '\n\n' + output;
|
|
1368
|
-
return boxen(both, {
|
|
1369
|
-
dimBorder: true,
|
|
1370
|
-
padding: {
|
|
1371
|
-
left: 1,
|
|
1372
|
-
right: 1,
|
|
1373
|
-
},
|
|
1374
|
-
});
|
|
1375
|
-
},
|
|
1376
|
-
}, program.opts());
|
|
1377
|
-
}
|
|
1378
|
-
catch (error) {
|
|
1379
|
-
if (error instanceof CancelSchemaError) {
|
|
1380
|
-
console.info('Schema migration cancelled!');
|
|
1381
|
-
}
|
|
1382
|
-
return { ok: false };
|
|
1383
|
-
}
|
|
1384
|
-
if (verbose) {
|
|
1385
|
-
console.log(txSteps);
|
|
1386
|
-
}
|
|
1387
|
-
if (wantsToPush) {
|
|
1388
|
-
const applyRes = yield fetchJson({
|
|
1389
|
-
method: 'POST',
|
|
1390
|
-
path: `/dash/apps/${appId}/schema/steps/apply`,
|
|
1391
|
-
debugName: 'Schema apply',
|
|
1392
|
-
errorMessage: 'Failed to update schema.',
|
|
1393
|
-
body: {
|
|
1394
|
-
steps: txSteps,
|
|
1395
|
-
},
|
|
1396
|
-
command: 'push',
|
|
1397
|
-
});
|
|
1398
|
-
console.log(chalk.green('Schema updated!'));
|
|
1399
|
-
if (!applyRes.ok)
|
|
1400
|
-
return applyRes;
|
|
1401
|
-
if (applyRes.data['indexing-jobs']) {
|
|
1402
|
-
yield waitForIndexingJobsToFinish(appId, applyRes.data['indexing-jobs']);
|
|
1403
|
-
}
|
|
1236
|
+
async function pushSchema(appId, opts) {
|
|
1237
|
+
const res = await readLocalSchemaFileWithErrorLogging();
|
|
1238
|
+
if (!res)
|
|
1239
|
+
return { ok: false };
|
|
1240
|
+
const { schema } = res;
|
|
1241
|
+
const pulledSchemaResponse = await fetchJson({
|
|
1242
|
+
method: 'GET',
|
|
1243
|
+
path: `/dash/apps/${appId}/schema/pull`,
|
|
1244
|
+
debugName: 'Schema plan',
|
|
1245
|
+
errorMessage: 'Failed to get old schema.',
|
|
1246
|
+
command: 'push',
|
|
1247
|
+
});
|
|
1248
|
+
if (!pulledSchemaResponse.ok)
|
|
1249
|
+
return pulledSchemaResponse;
|
|
1250
|
+
const currentAttrs = pulledSchemaResponse.data['attrs'];
|
|
1251
|
+
const currentApiSchema = pulledSchemaResponse.data['schema'];
|
|
1252
|
+
const oldSchema = apiSchemaToInstantSchemaDef(currentApiSchema, {
|
|
1253
|
+
disableTypeInference: true,
|
|
1254
|
+
});
|
|
1255
|
+
const systemCatalogIdentNames = collectSystemCatalogIdentNames(currentAttrs);
|
|
1256
|
+
try {
|
|
1257
|
+
validateSchema(schema, systemCatalogIdentNames);
|
|
1258
|
+
}
|
|
1259
|
+
catch (error) {
|
|
1260
|
+
if (error instanceof SchemaValidationError) {
|
|
1261
|
+
console.error(chalk.red('Invalid schema:', error.message));
|
|
1404
1262
|
}
|
|
1405
1263
|
else {
|
|
1406
|
-
console.
|
|
1264
|
+
console.error('Unexpected error:', error);
|
|
1407
1265
|
}
|
|
1266
|
+
return { ok: false };
|
|
1267
|
+
}
|
|
1268
|
+
const renameSelector = program.optsWithGlobals().yes
|
|
1269
|
+
? buildAutoRenameSelector(opts)
|
|
1270
|
+
: resolveRenames;
|
|
1271
|
+
const diffResult = await diffSchemas(oldSchema, schema, renameSelector, systemCatalogIdentNames);
|
|
1272
|
+
if (currentAttrs === undefined) {
|
|
1273
|
+
throw new Error("Couldn't get current schema from server");
|
|
1274
|
+
}
|
|
1275
|
+
const txSteps = convertTxSteps(diffResult, currentAttrs);
|
|
1276
|
+
if (txSteps.length === 0) {
|
|
1277
|
+
console.log(chalk.bgGray('No schema changes to apply!'));
|
|
1408
1278
|
return { ok: true };
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
token: adminToken,
|
|
1418
|
-
},
|
|
1419
|
-
path: `/dash/apps/ephemeral/${appId}/claim`,
|
|
1420
|
-
debugName: 'Claim ephemeral app',
|
|
1421
|
-
errorMessage: 'Failed to claim ephemeral app.',
|
|
1422
|
-
command: 'claim',
|
|
1423
|
-
});
|
|
1424
|
-
if (!res.ok)
|
|
1425
|
-
return res;
|
|
1426
|
-
console.log(chalk.green('App claimed!'));
|
|
1427
|
-
return { ok: true };
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1430
|
-
function pushPerms(appId) {
|
|
1431
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1432
|
-
const res = yield readLocalPermsFileWithErrorLogging();
|
|
1433
|
-
if (!res) {
|
|
1434
|
-
return { ok: true };
|
|
1435
|
-
}
|
|
1436
|
-
console.log('Planning perms...');
|
|
1437
|
-
const prodPerms = yield fetchJson({
|
|
1438
|
-
path: `/dash/apps/${appId}/perms/pull`,
|
|
1439
|
-
debugName: 'Perms pull',
|
|
1440
|
-
errorMessage: 'Failed to pull perms.',
|
|
1441
|
-
command: 'push',
|
|
1442
|
-
});
|
|
1443
|
-
if (!prodPerms.ok)
|
|
1444
|
-
return prodPerms;
|
|
1445
|
-
const diffedStr = jsonDiff.diffString(prodPerms.data.perms || {}, res.perms || {});
|
|
1446
|
-
if (!diffedStr.length) {
|
|
1447
|
-
console.log('No perms changes detected. Skipping.');
|
|
1448
|
-
return { ok: true };
|
|
1279
|
+
}
|
|
1280
|
+
let wantsToPush = false;
|
|
1281
|
+
try {
|
|
1282
|
+
const groupedSteps = groupSteps(diffResult);
|
|
1283
|
+
const lines = renderSchemaPlan(groupedSteps, currentAttrs);
|
|
1284
|
+
if (program.optsWithGlobals().yes) {
|
|
1285
|
+
console.log('Applying schema changes...');
|
|
1286
|
+
console.log(lines.join('\n'));
|
|
1449
1287
|
}
|
|
1450
|
-
|
|
1451
|
-
promptText: 'Push these changes
|
|
1288
|
+
wantsToPush = await promptOk({
|
|
1289
|
+
promptText: 'Push these changes?',
|
|
1290
|
+
yesText: 'Push',
|
|
1291
|
+
noText: 'Cancel',
|
|
1452
1292
|
modifyOutput: (output) => {
|
|
1453
|
-
let both =
|
|
1293
|
+
let both = lines.join('\n') + '\n\n' + output;
|
|
1454
1294
|
return boxen(both, {
|
|
1455
1295
|
dimBorder: true,
|
|
1456
1296
|
padding: {
|
|
@@ -1460,52 +1300,130 @@ function pushPerms(appId) {
|
|
|
1460
1300
|
});
|
|
1461
1301
|
},
|
|
1462
1302
|
}, program.opts());
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1303
|
+
}
|
|
1304
|
+
catch (error) {
|
|
1305
|
+
if (error instanceof CancelSchemaError) {
|
|
1306
|
+
console.info('Schema migration cancelled!');
|
|
1307
|
+
}
|
|
1308
|
+
return { ok: false };
|
|
1309
|
+
}
|
|
1310
|
+
if (verbose) {
|
|
1311
|
+
console.log(txSteps);
|
|
1312
|
+
}
|
|
1313
|
+
if (wantsToPush) {
|
|
1314
|
+
const applyRes = await fetchJson({
|
|
1466
1315
|
method: 'POST',
|
|
1467
|
-
path: `/dash/apps/${appId}/
|
|
1316
|
+
path: `/dash/apps/${appId}/schema/steps/apply`,
|
|
1468
1317
|
debugName: 'Schema apply',
|
|
1469
1318
|
errorMessage: 'Failed to update schema.',
|
|
1470
1319
|
body: {
|
|
1471
|
-
|
|
1320
|
+
steps: txSteps,
|
|
1472
1321
|
},
|
|
1473
1322
|
command: 'push',
|
|
1474
1323
|
});
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1324
|
+
console.log(chalk.green('Schema updated!'));
|
|
1325
|
+
if (!applyRes.ok)
|
|
1326
|
+
return applyRes;
|
|
1327
|
+
if (applyRes.data['indexing-jobs']) {
|
|
1328
|
+
await waitForIndexingJobsToFinish(appId, applyRes.data['indexing-jobs']);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
else {
|
|
1332
|
+
console.info('Schema migration cancelled!');
|
|
1333
|
+
}
|
|
1334
|
+
return { ok: true };
|
|
1335
|
+
}
|
|
1336
|
+
async function claimEphemeralApp(appId, adminToken) {
|
|
1337
|
+
const res = await fetchJson({
|
|
1338
|
+
method: 'POST',
|
|
1339
|
+
body: {
|
|
1340
|
+
app_id: appId,
|
|
1341
|
+
token: adminToken,
|
|
1342
|
+
},
|
|
1343
|
+
path: `/dash/apps/ephemeral/${appId}/claim`,
|
|
1344
|
+
debugName: 'Claim ephemeral app',
|
|
1345
|
+
errorMessage: 'Failed to claim ephemeral app.',
|
|
1346
|
+
command: 'claim',
|
|
1479
1347
|
});
|
|
1348
|
+
if (!res.ok)
|
|
1349
|
+
return res;
|
|
1350
|
+
console.log(chalk.green('App claimed!'));
|
|
1351
|
+
return { ok: true };
|
|
1480
1352
|
}
|
|
1481
|
-
function
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1353
|
+
async function pushPerms(appId) {
|
|
1354
|
+
const res = await readLocalPermsFileWithErrorLogging();
|
|
1355
|
+
if (!res) {
|
|
1356
|
+
return { ok: true };
|
|
1357
|
+
}
|
|
1358
|
+
console.log('Planning perms...');
|
|
1359
|
+
const prodPerms = await fetchJson({
|
|
1360
|
+
path: `/dash/apps/${appId}/perms/pull`,
|
|
1361
|
+
debugName: 'Perms pull',
|
|
1362
|
+
errorMessage: 'Failed to pull perms.',
|
|
1363
|
+
command: 'push',
|
|
1364
|
+
});
|
|
1365
|
+
if (!prodPerms.ok)
|
|
1366
|
+
return prodPerms;
|
|
1367
|
+
const diffedStr = jsonDiff.diffString(prodPerms.data.perms || {}, res.perms || {});
|
|
1368
|
+
if (!diffedStr.length) {
|
|
1369
|
+
console.log('No perms changes detected. Skipping.');
|
|
1370
|
+
return { ok: true };
|
|
1371
|
+
}
|
|
1372
|
+
const okPush = await promptOk({
|
|
1373
|
+
promptText: 'Push these changes to your perms?',
|
|
1374
|
+
modifyOutput: (output) => {
|
|
1375
|
+
let both = diffedStr + '\n' + output;
|
|
1376
|
+
return boxen(both, {
|
|
1377
|
+
dimBorder: true,
|
|
1378
|
+
padding: {
|
|
1379
|
+
left: 1,
|
|
1380
|
+
right: 1,
|
|
1381
|
+
},
|
|
1495
1382
|
});
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1383
|
+
},
|
|
1384
|
+
}, program.opts());
|
|
1385
|
+
if (!okPush)
|
|
1386
|
+
return { ok: true };
|
|
1387
|
+
const permsRes = await fetchJson({
|
|
1388
|
+
method: 'POST',
|
|
1389
|
+
path: `/dash/apps/${appId}/rules`,
|
|
1390
|
+
debugName: 'Schema apply',
|
|
1391
|
+
errorMessage: 'Failed to update schema.',
|
|
1392
|
+
body: {
|
|
1393
|
+
code: res.perms,
|
|
1394
|
+
},
|
|
1395
|
+
command: 'push',
|
|
1508
1396
|
});
|
|
1397
|
+
if (!permsRes.ok)
|
|
1398
|
+
return permsRes;
|
|
1399
|
+
console.log(chalk.green('Permissions updated!'));
|
|
1400
|
+
return { ok: true };
|
|
1401
|
+
}
|
|
1402
|
+
async function waitForAuthToken({ secret }) {
|
|
1403
|
+
for (let i = 1; i <= 120; i++) {
|
|
1404
|
+
await sleep(1000);
|
|
1405
|
+
const authCheckRes = await fetchJson({
|
|
1406
|
+
method: 'POST',
|
|
1407
|
+
debugName: 'Auth check',
|
|
1408
|
+
errorMessage: 'Failed to check auth status.',
|
|
1409
|
+
path: '/dash/cli/auth/check',
|
|
1410
|
+
body: { secret },
|
|
1411
|
+
noAuth: true,
|
|
1412
|
+
noLogError: true,
|
|
1413
|
+
command: 'login',
|
|
1414
|
+
});
|
|
1415
|
+
if (authCheckRes.ok) {
|
|
1416
|
+
return authCheckRes.data;
|
|
1417
|
+
}
|
|
1418
|
+
if (authCheckRes.data?.hint.errors?.[0]?.issue === 'waiting-for-user') {
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
error('Failed to authenticate ');
|
|
1422
|
+
prettyPrintJSONErr(authCheckRes.data);
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
error('Timed out waiting for authentication');
|
|
1426
|
+
return null;
|
|
1509
1427
|
}
|
|
1510
1428
|
// resources
|
|
1511
1429
|
/**
|
|
@@ -1522,66 +1440,69 @@ function waitForAuthToken(_a) {
|
|
|
1522
1440
|
* @param {string} [options.command] - The CLI command being executed (e.g., 'push', 'pull', 'login')
|
|
1523
1441
|
* @returns {Promise<{ ok: boolean; data: any }>}
|
|
1524
1442
|
*/
|
|
1525
|
-
function fetchJson(
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1443
|
+
async function fetchJson({ debugName, errorMessage, path, body, method, noAuth, noLogError, command, }) {
|
|
1444
|
+
const withAuth = !noAuth;
|
|
1445
|
+
const withErrorLogging = !noLogError;
|
|
1446
|
+
let authToken = null;
|
|
1447
|
+
if (withAuth) {
|
|
1448
|
+
authToken = await readConfigAuthTokenWithErrorLogging();
|
|
1449
|
+
if (!authToken) {
|
|
1450
|
+
return { ok: false, data: undefined };
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
const timeoutMs = 1000 * 60 * 5; // 5 minutes
|
|
1454
|
+
try {
|
|
1455
|
+
const res = await fetch(`${instantBackendOrigin}${path}`, {
|
|
1456
|
+
method: method ?? 'GET',
|
|
1457
|
+
headers: {
|
|
1458
|
+
...(withAuth ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
1459
|
+
'Content-Type': 'application/json',
|
|
1460
|
+
'X-Instant-Source': 'instant-cli',
|
|
1461
|
+
'X-Instant-Version': version,
|
|
1462
|
+
...(command ? { 'X-Instant-Command': command } : {}),
|
|
1463
|
+
},
|
|
1464
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
1465
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1466
|
+
});
|
|
1467
|
+
if (verbose) {
|
|
1468
|
+
console.log(debugName, 'response:', res.status, res.statusText);
|
|
1535
1469
|
}
|
|
1536
|
-
|
|
1470
|
+
let data;
|
|
1537
1471
|
try {
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
1543
|
-
});
|
|
1544
|
-
if (verbose) {
|
|
1545
|
-
console.log(debugName, 'response:', res.status, res.statusText);
|
|
1546
|
-
}
|
|
1547
|
-
let data;
|
|
1548
|
-
try {
|
|
1549
|
-
data = yield res.json();
|
|
1550
|
-
}
|
|
1551
|
-
catch (_b) {
|
|
1552
|
-
data = null;
|
|
1553
|
-
}
|
|
1554
|
-
if (verbose && data) {
|
|
1555
|
-
console.log(debugName, 'json:', JSON.stringify(data, null, 2));
|
|
1556
|
-
}
|
|
1557
|
-
if (!res.ok) {
|
|
1558
|
-
if (withErrorLogging) {
|
|
1559
|
-
error(errorMessage);
|
|
1560
|
-
prettyPrintJSONErr(data);
|
|
1561
|
-
}
|
|
1562
|
-
return { ok: false, data };
|
|
1563
|
-
}
|
|
1564
|
-
return { ok: true, data };
|
|
1472
|
+
data = await res.json();
|
|
1473
|
+
}
|
|
1474
|
+
catch {
|
|
1475
|
+
data = null;
|
|
1565
1476
|
}
|
|
1566
|
-
|
|
1477
|
+
if (verbose && data) {
|
|
1478
|
+
console.log(debugName, 'json:', JSON.stringify(data, null, 2));
|
|
1479
|
+
}
|
|
1480
|
+
if (!res.ok) {
|
|
1567
1481
|
if (withErrorLogging) {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
}
|
|
1571
|
-
else {
|
|
1572
|
-
error(`Error: type: ${err.name}, message: ${err.message}`);
|
|
1573
|
-
}
|
|
1482
|
+
error(errorMessage);
|
|
1483
|
+
prettyPrintJSONErr(data);
|
|
1574
1484
|
}
|
|
1575
|
-
return { ok: false, data
|
|
1485
|
+
return { ok: false, data };
|
|
1576
1486
|
}
|
|
1577
|
-
|
|
1487
|
+
return { ok: true, data };
|
|
1488
|
+
}
|
|
1489
|
+
catch (err) {
|
|
1490
|
+
if (withErrorLogging) {
|
|
1491
|
+
if (err.name === 'AbortError') {
|
|
1492
|
+
error(`Timeout: It took more than ${timeoutMs / 60000} minutes to get the result.`);
|
|
1493
|
+
}
|
|
1494
|
+
else {
|
|
1495
|
+
error(`Error: type: ${err.name}, message: ${err.message}`);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
return { ok: false, data: null };
|
|
1499
|
+
}
|
|
1578
1500
|
}
|
|
1579
1501
|
function prettyPrintJSONErr(data) {
|
|
1580
|
-
|
|
1581
|
-
if (data === null || data === void 0 ? void 0 : data.message) {
|
|
1502
|
+
if (data?.message) {
|
|
1582
1503
|
error(data.message);
|
|
1583
1504
|
}
|
|
1584
|
-
if (Array.isArray(
|
|
1505
|
+
if (Array.isArray(data?.hint?.errors)) {
|
|
1585
1506
|
for (const err of data.hint.errors) {
|
|
1586
1507
|
error(`${err.in ? err.in.join('->') + ': ' : ''}${err.message}`);
|
|
1587
1508
|
}
|
|
@@ -1590,128 +1511,109 @@ function prettyPrintJSONErr(data) {
|
|
|
1590
1511
|
error('Failed to parse error response');
|
|
1591
1512
|
}
|
|
1592
1513
|
}
|
|
1593
|
-
function readLocalPermsFile() {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
merge: false,
|
|
1599
|
-
});
|
|
1600
|
-
if (!res.config)
|
|
1601
|
-
return;
|
|
1602
|
-
const relativePath = path.relative(process.cwd(), res.sources[0]);
|
|
1603
|
-
return { path: relativePath, perms: res.config };
|
|
1604
|
-
});
|
|
1605
|
-
}
|
|
1606
|
-
function readLocalPermsFileWithErrorLogging() {
|
|
1607
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1608
|
-
const res = yield readLocalPermsFile();
|
|
1609
|
-
if (!res) {
|
|
1610
|
-
error(`We couldn't find your ${chalk.yellow('`instant.perms.ts`')} file. Make sure it's in the root directory. (Hint: You can use an INSTANT_PERMS_FILE_PATH environment variable to specify it.)`);
|
|
1611
|
-
}
|
|
1612
|
-
return res;
|
|
1613
|
-
});
|
|
1614
|
-
}
|
|
1615
|
-
function readLocalSchemaFile() {
|
|
1616
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1617
|
-
const readCandidates = getSchemaReadCandidates();
|
|
1618
|
-
const res = yield loadConfig({
|
|
1619
|
-
sources: readCandidates,
|
|
1620
|
-
merge: false,
|
|
1621
|
-
});
|
|
1622
|
-
if (!res.config)
|
|
1623
|
-
return;
|
|
1624
|
-
const relativePath = path.relative(process.cwd(), res.sources[0]);
|
|
1625
|
-
return { path: relativePath, schema: res.config };
|
|
1626
|
-
});
|
|
1627
|
-
}
|
|
1628
|
-
function readInstantConfigFile() {
|
|
1629
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1630
|
-
return (yield loadConfig({
|
|
1631
|
-
sources: [
|
|
1632
|
-
// load from `instant.config.xx`
|
|
1633
|
-
{
|
|
1634
|
-
files: 'instant.config',
|
|
1635
|
-
extensions: ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs', 'json'],
|
|
1636
|
-
},
|
|
1637
|
-
],
|
|
1638
|
-
// if false, the only the first matched will be loaded
|
|
1639
|
-
// if true, all matched will be loaded and deep merged
|
|
1640
|
-
merge: false,
|
|
1641
|
-
})).config;
|
|
1514
|
+
async function readLocalPermsFile() {
|
|
1515
|
+
const readCandidates = getPermsReadCandidates();
|
|
1516
|
+
const res = await loadConfig({
|
|
1517
|
+
sources: readCandidates,
|
|
1518
|
+
merge: false,
|
|
1642
1519
|
});
|
|
1520
|
+
if (!res.config)
|
|
1521
|
+
return;
|
|
1522
|
+
const relativePath = path.relative(process.cwd(), res.sources[0]);
|
|
1523
|
+
return { path: relativePath, perms: res.config };
|
|
1524
|
+
}
|
|
1525
|
+
async function readLocalPermsFileWithErrorLogging() {
|
|
1526
|
+
const res = await readLocalPermsFile();
|
|
1527
|
+
if (!res) {
|
|
1528
|
+
error(`We couldn't find your ${chalk.yellow('`instant.perms.ts`')} file. Make sure it's in the root directory. (Hint: You can use an INSTANT_PERMS_FILE_PATH environment variable to specify it.)`);
|
|
1529
|
+
}
|
|
1530
|
+
return res;
|
|
1643
1531
|
}
|
|
1644
|
-
function
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
error(`We couldn't find your ${chalk.yellow('`instant.schema.ts`')} file. Make sure it's in the root directory. (Hint: You can use an INSTANT_SCHEMA_FILE_PATH environment variable to specify it.)`);
|
|
1650
|
-
return;
|
|
1651
|
-
}
|
|
1652
|
-
if (((_b = (_a = res.schema) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name) !== 'InstantSchemaDef') {
|
|
1653
|
-
error("We couldn't find your schema export.");
|
|
1654
|
-
error('In your ' +
|
|
1655
|
-
chalk.green('`instant.schema.ts`') +
|
|
1656
|
-
' file, make sure you ' +
|
|
1657
|
-
chalk.green('`export default schema`'));
|
|
1658
|
-
return;
|
|
1659
|
-
}
|
|
1660
|
-
return res;
|
|
1532
|
+
async function readLocalSchemaFile() {
|
|
1533
|
+
const readCandidates = getSchemaReadCandidates();
|
|
1534
|
+
const res = await loadConfig({
|
|
1535
|
+
sources: readCandidates,
|
|
1536
|
+
merge: false,
|
|
1661
1537
|
});
|
|
1538
|
+
if (!res.config)
|
|
1539
|
+
return;
|
|
1540
|
+
const relativePath = path.relative(process.cwd(), res.sources[0]);
|
|
1541
|
+
return { path: relativePath, schema: res.config };
|
|
1542
|
+
}
|
|
1543
|
+
async function readInstantConfigFile() {
|
|
1544
|
+
return (await loadConfig({
|
|
1545
|
+
sources: [
|
|
1546
|
+
// load from `instant.config.xx`
|
|
1547
|
+
{
|
|
1548
|
+
files: 'instant.config',
|
|
1549
|
+
extensions: ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs', 'json'],
|
|
1550
|
+
},
|
|
1551
|
+
],
|
|
1552
|
+
// if false, the only the first matched will be loaded
|
|
1553
|
+
// if true, all matched will be loaded and deep merged
|
|
1554
|
+
merge: false,
|
|
1555
|
+
})).config;
|
|
1556
|
+
}
|
|
1557
|
+
async function readLocalSchemaFileWithErrorLogging() {
|
|
1558
|
+
const res = await readLocalSchemaFile();
|
|
1559
|
+
if (!res) {
|
|
1560
|
+
error(`We couldn't find your ${chalk.yellow('`instant.schema.ts`')} file. Make sure it's in the root directory. (Hint: You can use an INSTANT_SCHEMA_FILE_PATH environment variable to specify it.)`);
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
if (res.schema?.constructor?.name !== 'InstantSchemaDef') {
|
|
1564
|
+
error("We couldn't find your schema export.");
|
|
1565
|
+
error('In your ' +
|
|
1566
|
+
chalk.green('`instant.schema.ts`') +
|
|
1567
|
+
' file, make sure you ' +
|
|
1568
|
+
chalk.green('`export default schema`'));
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
return res;
|
|
1662
1572
|
}
|
|
1663
|
-
function readConfigAuthToken() {
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
return token;
|
|
1678
|
-
}
|
|
1573
|
+
async function readConfigAuthToken(allowAdminToken = true) {
|
|
1574
|
+
const options = program.opts();
|
|
1575
|
+
if (typeof options.token === 'string') {
|
|
1576
|
+
return options.token;
|
|
1577
|
+
}
|
|
1578
|
+
if (process.env.INSTANT_CLI_AUTH_TOKEN) {
|
|
1579
|
+
return process.env.INSTANT_CLI_AUTH_TOKEN;
|
|
1580
|
+
}
|
|
1581
|
+
if (allowAdminToken) {
|
|
1582
|
+
const adminTokenNames = Object.values(potentialAdminTokenEnvs);
|
|
1583
|
+
for (const envName of adminTokenNames) {
|
|
1584
|
+
const token = process.env[envName];
|
|
1585
|
+
if (token) {
|
|
1586
|
+
return token;
|
|
1679
1587
|
}
|
|
1680
1588
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
}
|
|
1688
|
-
export function readConfigAuthTokenWithErrorLogging() {
|
|
1689
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1690
|
-
const token = yield readConfigAuthToken();
|
|
1691
|
-
if (!token) {
|
|
1692
|
-
error(`Looks like you are not logged in. Please log in with ${chalk.green('`instant-cli login`')}`);
|
|
1693
|
-
}
|
|
1694
|
-
return token;
|
|
1695
|
-
});
|
|
1589
|
+
}
|
|
1590
|
+
const authToken = await readFile(getAuthPaths().authConfigFilePath, 'utf-8').catch(() => null);
|
|
1591
|
+
if (authToken) {
|
|
1592
|
+
return authToken;
|
|
1593
|
+
}
|
|
1594
|
+
return null;
|
|
1696
1595
|
}
|
|
1697
|
-
function
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
console.log(`Let's log in!`);
|
|
1704
|
-
return yield login({});
|
|
1705
|
-
});
|
|
1596
|
+
export async function readConfigAuthTokenWithErrorLogging() {
|
|
1597
|
+
const token = await readConfigAuthToken();
|
|
1598
|
+
if (!token) {
|
|
1599
|
+
error(`Looks like you are not logged in. Please log in with ${chalk.green('`instant-cli login`')}`);
|
|
1600
|
+
}
|
|
1601
|
+
return token;
|
|
1706
1602
|
}
|
|
1707
|
-
function
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1603
|
+
async function readAuthTokenOrLoginWithErrorLogging() {
|
|
1604
|
+
const token = await readConfigAuthToken();
|
|
1605
|
+
if (token)
|
|
1606
|
+
return token;
|
|
1607
|
+
console.log(`Looks like you are not logged in...`);
|
|
1608
|
+
console.log(`Let's log in!`);
|
|
1609
|
+
return await login({});
|
|
1610
|
+
}
|
|
1611
|
+
async function saveConfigAuthToken(authToken) {
|
|
1612
|
+
const authPaths = getAuthPaths();
|
|
1613
|
+
await mkdir(authPaths.appConfigDirPath, {
|
|
1614
|
+
recursive: true,
|
|
1714
1615
|
});
|
|
1616
|
+
return writeFile(authPaths.authConfigFilePath, authToken, 'utf-8');
|
|
1715
1617
|
}
|
|
1716
1618
|
// utils
|
|
1717
1619
|
function sleep(ms) {
|
|
@@ -1737,20 +1639,16 @@ function identName(ident) {
|
|
|
1737
1639
|
return `${identEtype(ident)}.${identLabel(ident)}`;
|
|
1738
1640
|
}
|
|
1739
1641
|
function attrFwdLabel(attr) {
|
|
1740
|
-
|
|
1741
|
-
return (_a = attr['forward-identity']) === null || _a === void 0 ? void 0 : _a[2];
|
|
1642
|
+
return attr['forward-identity']?.[2];
|
|
1742
1643
|
}
|
|
1743
1644
|
function attrFwdEtype(attr) {
|
|
1744
|
-
|
|
1745
|
-
return (_a = attr['forward-identity']) === null || _a === void 0 ? void 0 : _a[1];
|
|
1645
|
+
return attr['forward-identity']?.[1];
|
|
1746
1646
|
}
|
|
1747
1647
|
function attrRevLabel(attr) {
|
|
1748
|
-
|
|
1749
|
-
return (_a = attr['reverse-identity']) === null || _a === void 0 ? void 0 : _a[2];
|
|
1648
|
+
return attr['reverse-identity']?.[2];
|
|
1750
1649
|
}
|
|
1751
1650
|
function attrRevEtype(attr) {
|
|
1752
|
-
|
|
1753
|
-
return (_a = attr['reverse-identity']) === null || _a === void 0 ? void 0 : _a[1];
|
|
1651
|
+
return attr['reverse-identity']?.[1];
|
|
1754
1652
|
}
|
|
1755
1653
|
function attrFwdName(attr) {
|
|
1756
1654
|
return `${attrFwdEtype(attr)}.${attrFwdLabel(attr)}`;
|
|
@@ -1771,26 +1669,23 @@ function isUUID(uuid) {
|
|
|
1771
1669
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1772
1670
|
return uuidRegex.test(uuid);
|
|
1773
1671
|
}
|
|
1774
|
-
function detectAppIdFromOptsWithErrorLogging(opts) {
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
}
|
|
1792
|
-
return { ok: true, appId: namedAppId || uuidAppId };
|
|
1793
|
-
});
|
|
1672
|
+
async function detectAppIdFromOptsWithErrorLogging(opts) {
|
|
1673
|
+
if (!opts.app)
|
|
1674
|
+
return { ok: true };
|
|
1675
|
+
const appId = opts.app;
|
|
1676
|
+
const config = await readInstantConfigFile();
|
|
1677
|
+
const nameMatch = config?.apps?.[appId];
|
|
1678
|
+
const namedAppId = nameMatch?.id && isUUID(nameMatch.id) ? nameMatch : null;
|
|
1679
|
+
const uuidAppId = appId && isUUID(appId) ? appId : null;
|
|
1680
|
+
if (nameMatch && !namedAppId) {
|
|
1681
|
+
error(`Expected \`${appId}\` to point to a UUID, but got ${nameMatch.id}.`);
|
|
1682
|
+
return { ok: false };
|
|
1683
|
+
}
|
|
1684
|
+
if (!namedAppId && !uuidAppId) {
|
|
1685
|
+
error(`Expected App ID to be a UUID, but got: ${chalk.red(appId)}`);
|
|
1686
|
+
return { ok: false };
|
|
1687
|
+
}
|
|
1688
|
+
return { ok: true, appId: namedAppId || uuidAppId };
|
|
1794
1689
|
}
|
|
1795
1690
|
function detectAppIdFromEnvWithErrorLogging() {
|
|
1796
1691
|
const found = Object.keys(potentialEnvs)
|