@appsemble/cli 0.27.9 → 0.27.11

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/lib/app.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { cpSync, createReadStream, createWriteStream, existsSync } from 'node:fs';
2
- import { mkdir, readdir, readFile, rm, stat } from 'node:fs/promises';
2
+ import { mkdir, readdir, rm, stat } from 'node:fs/promises';
3
3
  import { basename, dirname, join, parse, relative, resolve } from 'node:path';
4
4
  import { inspect } from 'node:util';
5
5
  import { applyAppVariant, AppsembleError, authenticate, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
@@ -20,12 +20,16 @@ import { publishResourcesRecursively } from './resource.js';
20
20
  * Traverses an app directory and appends the files it finds to the given FormData object.
21
21
  *
22
22
  * @param path The path of the app directory to traverse.
23
+ * @param context The Context to use from `.appsemblerc.yaml`.
23
24
  * @param formData The FormData object to append the results into..
24
25
  * @returns The context from `.appsemblerc.yaml` if a match was found.
25
26
  */
26
- export async function traverseAppDirectory(path, formData) {
27
+ export async function traverseAppDirectory(path, context, formData) {
27
28
  let rc;
29
+ let discoveredContext;
28
30
  let yaml;
31
+ let iconPath;
32
+ let maskableIconPath;
29
33
  let controllerPath;
30
34
  let controllerBuildConfig;
31
35
  let controllerBuildResult;
@@ -44,6 +48,14 @@ export async function traverseAppDirectory(path, formData) {
44
48
  };
45
49
  logger.info(`Traversing directory for App files in ${path} šŸ•µ`);
46
50
  await opendirSafe(path, async (filepath, filestat) => {
51
+ if (filestat.isFile() && filestat.name.toLowerCase().startsWith('readme')) {
52
+ logger.info(`Adding ${filepath}`);
53
+ formData.append('readmes', createReadStream(filepath));
54
+ if (filestat.name.toLowerCase().startsWith('readme.md')) {
55
+ gatheredData.readmeUrl = basename(filepath);
56
+ }
57
+ return;
58
+ }
47
59
  switch (filestat.name.toLowerCase()) {
48
60
  case '.appsemblerc.yaml':
49
61
  logger.info(`Reading app settings from ${filepath}`);
@@ -52,6 +64,10 @@ export async function traverseAppDirectory(path, formData) {
52
64
  formData.append('iconBackground', rc.iconBackground);
53
65
  gatheredData.iconBackground = rc.iconBackground;
54
66
  }
67
+ if (context && has(rc === null || rc === void 0 ? void 0 : rc.context, context)) {
68
+ discoveredContext = rc.context[context];
69
+ logger.verbose(`Using context: ${inspect(discoveredContext, { colors: true })}`);
70
+ }
55
71
  break;
56
72
  case 'app-definition.yaml': {
57
73
  logger.info(`Using app definition from ${filepath}`);
@@ -66,15 +82,12 @@ export async function traverseAppDirectory(path, formData) {
66
82
  }
67
83
  case 'icon.png':
68
84
  case 'icon.svg':
85
+ iconPath = filepath;
69
86
  gatheredData.iconUrl = filepath;
70
87
  return;
71
88
  case 'maskable-icon.png':
72
- return;
73
- case 'readme.md':
74
- logger.info(`Including longDescription from ${filepath}`);
75
- formData.append('longDescription', await readFile(filepath, 'utf8'));
76
- gatheredData.longDescription = await readFile(filepath, 'utf8');
77
- return;
89
+ maskableIconPath = filepath;
90
+ break;
78
91
  case 'screenshots':
79
92
  return opendirSafe(filepath, (screenshotPath, screenshotStat) => {
80
93
  if (screenshotStat.isDirectory() &&
@@ -124,13 +137,20 @@ export async function traverseAppDirectory(path, formData) {
124
137
  gatheredData.controllerImplementations = controllerImplementations;
125
138
  break;
126
139
  default:
127
- logger.warn(`Found unused file ${filepath}`);
140
+ break;
128
141
  }
129
142
  });
130
143
  if (yaml === undefined) {
131
144
  throw new AppsembleError('No app definition found');
132
145
  }
133
- return [rc, yaml, gatheredData];
146
+ discoveredContext || (discoveredContext = {});
147
+ discoveredContext.icon = discoveredContext.icon
148
+ ? resolve(path, discoveredContext.icon)
149
+ : iconPath;
150
+ discoveredContext.maskableIcon = discoveredContext.maskableIcon
151
+ ? resolve(path, discoveredContext.maskableIcon)
152
+ : maskableIconPath;
153
+ return [discoveredContext, rc, yaml, gatheredData];
134
154
  }
135
155
  async function retrieveContext(path, context) {
136
156
  let rc;
@@ -449,15 +469,45 @@ This block version is not used in the app`);
449
469
  }
450
470
  }
451
471
  /**
452
- * Update an existing app.
472
+ * Resolve the app’s ID and remote based on the app context.
453
473
  *
454
- * @param argv The command line options used for updating the app.
474
+ * @param appPath The path to the app.
475
+ * @param name Which context to use in the AppsembleRC file.
476
+ * @param defaultRemote The remote to fall back to.
477
+ * @param defaultAppId The app ID to fall back to.
478
+ * @returns The resolved app ID and remote.
455
479
  */
456
- export async function updateApp({ clientCredentials, context, force, path, ...options }) {
457
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
480
+ export async function resolveAppIdAndRemote(appPath, name, defaultRemote, defaultAppId) {
481
+ var _a;
482
+ let id = defaultAppId;
483
+ let resolvedRemote = defaultRemote;
484
+ if (appPath) {
485
+ const rcPath = join(appPath, '.appsemblerc.yaml');
486
+ const [rc] = await readData(rcPath);
487
+ const context = (_a = rc.context) === null || _a === void 0 ? void 0 : _a[name];
488
+ if (!defaultAppId) {
489
+ id = context === null || context === void 0 ? void 0 : context.id;
490
+ }
491
+ if (context.remote) {
492
+ resolvedRemote = context.remote;
493
+ }
494
+ }
495
+ if (id == null) {
496
+ throw new AppsembleError(`App ID was not found in context.${name}.id nor in --app-id`);
497
+ }
498
+ return [id, coerceRemote(resolvedRemote)];
499
+ }
500
+ /**
501
+ * Publish a new app.
502
+ *
503
+ * @param options The options to use for publishing an app.
504
+ */
505
+ export async function publishApp({ clientCredentials, context, dryRun, modifyContext, path, ...options }) {
506
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
458
507
  const file = await stat(path);
459
508
  const formData = new FormData();
460
509
  const appsembleContext = await retrieveContext(path, context);
510
+ let rc;
461
511
  let yaml;
462
512
  let filename = relative(process.cwd(), path);
463
513
  const variant = (_b = (_a = appsembleContext.variant) !== null && _a !== void 0 ? _a : options.variant) !== null && _b !== void 0 ? _b : context;
@@ -470,16 +520,24 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
470
520
  logger.warn(`App variant ${variant} is not defined in ${join(path, 'variants')}. Using default app.`);
471
521
  }
472
522
  if (file.isFile()) {
523
+ // Assuming file is App YAML
473
524
  const [, data] = await readData(appVariantPath);
474
525
  yaml = data;
475
526
  formData.append('yaml', data);
476
527
  }
477
528
  else {
478
- [, yaml] = await traverseAppDirectory(appVariantPath, formData);
529
+ let appsembleVariantContext;
530
+ [appsembleVariantContext, rc, yaml] = await traverseAppDirectory(appVariantPath, context, formData);
531
+ if (appsembleVariantContext.icon) {
532
+ appsembleContext.icon = appsembleVariantContext.icon;
533
+ }
534
+ if (appsembleVariantContext.maskableIcon) {
535
+ appsembleContext.maskableIcon = appsembleVariantContext.maskableIcon;
536
+ }
479
537
  filename = join(filename, 'app-definition.yaml');
480
538
  }
481
539
  const remote = (_c = appsembleContext.remote) !== null && _c !== void 0 ? _c : options.remote;
482
- const id = (_d = appsembleContext.id) !== null && _d !== void 0 ? _d : options.id;
540
+ const organizationId = (_d = appsembleContext.organization) !== null && _d !== void 0 ? _d : options.organization;
483
541
  const template = (_f = (_e = appsembleContext.template) !== null && _e !== void 0 ? _e : options.template) !== null && _f !== void 0 ? _f : false;
484
542
  const assetsClonable = (_h = (_g = appsembleContext.assetsClonable) !== null && _g !== void 0 ? _g : options.assetsClonable) !== null && _h !== void 0 ? _h : false;
485
543
  const demoMode = (_k = (_j = appsembleContext.demoMode) !== null && _j !== void 0 ? _j : options.demoMode) !== null && _k !== void 0 ? _k : false;
@@ -490,23 +548,23 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
490
548
  const sentryDsn = (_q = appsembleContext.sentryDsn) !== null && _q !== void 0 ? _q : options.sentryDsn;
491
549
  const sentryEnvironment = (_r = appsembleContext.sentryEnvironment) !== null && _r !== void 0 ? _r : options.sentryEnvironment;
492
550
  const googleAnalyticsId = (_s = appsembleContext.googleAnalyticsId) !== null && _s !== void 0 ? _s : options.googleAnalyticsId;
493
- const resources = (_t = appsembleContext.resources) !== null && _t !== void 0 ? _t : options.resources;
494
- const assets = (_u = appsembleContext.assets) !== null && _u !== void 0 ? _u : options.assets;
495
- const { appLock } = appsembleContext;
496
- logger.info(`App id: ${id}`);
551
+ const assets = (_t = appsembleContext.assets) !== null && _t !== void 0 ? _t : options.assets;
552
+ const resources = (_u = appsembleContext.resources) !== null && _u !== void 0 ? _u : options.resources;
553
+ const appLock = appsembleContext.appLock || 'unlocked';
497
554
  logger.verbose(`App remote: ${remote}`);
555
+ logger.verbose(`App organization: ${organizationId}`);
498
556
  logger.verbose(`App is template: ${inspect(template, { colors: true })}`);
499
557
  logger.verbose(`App visibility: ${visibility}`);
500
558
  logger.verbose(`Icon background: ${iconBackground}`);
501
- logger.verbose(`Force update: ${inspect(force, { colors: true })}`);
502
- if (!id) {
503
- throw new AppsembleError('The app id must be passed as a command line flag or in the context');
559
+ if (!organizationId) {
560
+ throw new AppsembleError('An organization id must be passed as a command line flag or in the context');
504
561
  }
505
- formData.append('force', String(force));
562
+ formData.append('OrganizationId', organizationId);
506
563
  formData.append('template', String(template));
507
564
  formData.append('demoMode', String(demoMode));
508
565
  formData.append('visibility', visibility);
509
566
  formData.append('iconBackground', iconBackground);
567
+ formData.append('locked', appLock);
510
568
  if (icon) {
511
569
  const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
512
570
  logger.info(`Using icon from ${(_v = realIcon.path) !== null && _v !== void 0 ? _v : 'stdin'}`);
@@ -533,21 +591,12 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
533
591
  authScope += ' resources:write';
534
592
  }
535
593
  await authenticate(remote, authScope, clientCredentials);
536
- if (appLock) {
537
- logger.info(`Setting AppLock to ${appLock}`);
538
- try {
539
- await axios.post(`/api/apps/${id}/lock`, {
540
- locked: appLock,
541
- });
542
- }
543
- catch (error) {
544
- logger.error(error);
545
- process.exit(1);
546
- }
547
- }
548
594
  let data;
549
595
  try {
550
- ({ data } = await axios.patch(`/api/apps/${id}`, formData, { baseURL: remote }));
596
+ ({ data } = await axios.post('/api/apps', formData, {
597
+ baseURL: remote,
598
+ params: { dryRun: dryRun || undefined },
599
+ }));
551
600
  }
552
601
  catch (error) {
553
602
  if (!axios.isAxiosError(error)) {
@@ -558,38 +607,64 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
558
607
  }
559
608
  throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
560
609
  }
561
- if (file.isDirectory()) {
562
- // After uploading the app, upload block styles and messages if they are available
563
- await traverseBlockThemes(appVariantPath, data.id, remote, force);
564
- await uploadMessages(appVariantPath, data.id, remote, force);
565
- // After updating the app, publish seed resources and assets if they are available
566
- if (assets && (data.locked !== 'fullLock' || force)) {
610
+ if (dryRun) {
611
+ logger.info('Skipped uploading block themes and app messages.');
612
+ logger.info('Successfully ran dry run. The app should be valid when creating it.');
613
+ return;
614
+ }
615
+ if (file.isDirectory() && !dryRun) {
616
+ // After uploading the app, upload block styles, messages if they are available
617
+ await traverseBlockThemes(appVariantPath, data.id, remote, false);
618
+ await uploadMessages(appVariantPath, data.id, remote, false);
619
+ // After uploading the app, publish seed resources and assets if they are available
620
+ if (assets) {
567
621
  await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
568
622
  }
569
- if (resources && (data.locked !== 'fullLock' || force)) {
623
+ if (resources) {
570
624
  await publishSeedResources(appVariantPath, data, remote);
571
625
  }
572
626
  }
627
+ if (modifyContext && appsembleContext && context && !dryRun) {
628
+ rc.context[context].id = data.id;
629
+ await writeData(join(appVariantPath, '.appsemblerc.yaml'), rc, { sort: false });
630
+ logger.info(`Updated .appsemblerc: Set context.${context}.id to ${data.id}`);
631
+ }
573
632
  const { host, protocol } = new URL(remote);
574
- logger.info(`Successfully updated app ${data.definition.name}! šŸ™Œ`);
633
+ logger.info(`Successfully published app ${data.definition.name}! šŸ™Œ`);
575
634
  logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
576
635
  logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
577
- if (appVariantPath !== path) {
636
+ const rcCollections = (_0 = (_z = rc === null || rc === void 0 ? void 0 : rc.context) === null || _z === void 0 ? void 0 : _z[context]) === null || _0 === void 0 ? void 0 : _0.collections;
637
+ if (Array.isArray(rcCollections)) {
638
+ for (const collectionId of rcCollections) {
639
+ try {
640
+ await axios.post(`/api/appCollections/${collectionId}/apps`, { AppId: data.id }, {
641
+ baseURL: remote,
642
+ });
643
+ logger.info(`Successfully added app to collection ${collectionId}`);
644
+ }
645
+ catch (error) {
646
+ logger.error(error);
647
+ }
648
+ }
649
+ }
650
+ if (appVariantPath !== path && modifyContext && !dryRun) {
651
+ const [defaultAppRc] = await readData(join(path, '.appsemblerc.yaml'));
652
+ defaultAppRc.context[context] = rc.context[context];
653
+ await writeData(join(path, '.appsemblerc.yaml'), defaultAppRc, { sort: false });
578
654
  await rm(appVariantPath, { recursive: true });
579
655
  logger.info(`Removed ${appVariantPath}`);
580
656
  }
581
657
  }
582
658
  /**
583
- * Publish a new app.
659
+ * Update an existing app.
584
660
  *
585
- * @param options The options to use for publishing an app.
661
+ * @param argv The command line options used for updating the app.
586
662
  */
587
- export async function publishApp({ clientCredentials, context, dryRun, modifyContext, path, ...options }) {
588
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
663
+ export async function updateApp({ clientCredentials, context, force, path, ...options }) {
664
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
589
665
  const file = await stat(path);
590
666
  const formData = new FormData();
591
667
  const appsembleContext = await retrieveContext(path, context);
592
- let rc;
593
668
  let yaml;
594
669
  let filename = relative(process.cwd(), path);
595
670
  const variant = (_b = (_a = appsembleContext.variant) !== null && _a !== void 0 ? _a : options.variant) !== null && _b !== void 0 ? _b : context;
@@ -602,17 +677,23 @@ export async function publishApp({ clientCredentials, context, dryRun, modifyCon
602
677
  logger.warn(`App variant ${variant} is not defined in ${join(path, 'variants')}. Using default app.`);
603
678
  }
604
679
  if (file.isFile()) {
605
- // Assuming file is App YAML
606
680
  const [, data] = await readData(appVariantPath);
607
681
  yaml = data;
608
682
  formData.append('yaml', data);
609
683
  }
610
684
  else {
611
- [rc, yaml] = await traverseAppDirectory(appVariantPath, formData);
685
+ let appsembleVariantContext;
686
+ [appsembleVariantContext, , yaml] = await traverseAppDirectory(appVariantPath, context, formData);
687
+ if (appsembleVariantContext.icon) {
688
+ appsembleContext.icon = appsembleVariantContext.icon;
689
+ }
690
+ if (appsembleVariantContext.maskableIcon) {
691
+ appsembleContext.maskableIcon = appsembleVariantContext.maskableIcon;
692
+ }
612
693
  filename = join(filename, 'app-definition.yaml');
613
694
  }
614
695
  const remote = (_c = appsembleContext.remote) !== null && _c !== void 0 ? _c : options.remote;
615
- const organizationId = (_d = appsembleContext.organization) !== null && _d !== void 0 ? _d : options.organization;
696
+ const id = (_d = appsembleContext.id) !== null && _d !== void 0 ? _d : options.id;
616
697
  const template = (_f = (_e = appsembleContext.template) !== null && _e !== void 0 ? _e : options.template) !== null && _f !== void 0 ? _f : false;
617
698
  const assetsClonable = (_h = (_g = appsembleContext.assetsClonable) !== null && _g !== void 0 ? _g : options.assetsClonable) !== null && _h !== void 0 ? _h : false;
618
699
  const demoMode = (_k = (_j = appsembleContext.demoMode) !== null && _j !== void 0 ? _j : options.demoMode) !== null && _k !== void 0 ? _k : false;
@@ -623,23 +704,23 @@ export async function publishApp({ clientCredentials, context, dryRun, modifyCon
623
704
  const sentryDsn = (_q = appsembleContext.sentryDsn) !== null && _q !== void 0 ? _q : options.sentryDsn;
624
705
  const sentryEnvironment = (_r = appsembleContext.sentryEnvironment) !== null && _r !== void 0 ? _r : options.sentryEnvironment;
625
706
  const googleAnalyticsId = (_s = appsembleContext.googleAnalyticsId) !== null && _s !== void 0 ? _s : options.googleAnalyticsId;
626
- const assets = (_t = appsembleContext.assets) !== null && _t !== void 0 ? _t : options.assets;
627
- const resources = (_u = appsembleContext.resources) !== null && _u !== void 0 ? _u : options.resources;
628
- const appLock = appsembleContext.appLock || 'unlocked';
707
+ const resources = (_t = appsembleContext.resources) !== null && _t !== void 0 ? _t : options.resources;
708
+ const assets = (_u = appsembleContext.assets) !== null && _u !== void 0 ? _u : options.assets;
709
+ const { appLock } = appsembleContext;
710
+ logger.info(`App id: ${id}`);
629
711
  logger.verbose(`App remote: ${remote}`);
630
- logger.verbose(`App organization: ${organizationId}`);
631
712
  logger.verbose(`App is template: ${inspect(template, { colors: true })}`);
632
713
  logger.verbose(`App visibility: ${visibility}`);
633
714
  logger.verbose(`Icon background: ${iconBackground}`);
634
- if (!organizationId) {
635
- throw new AppsembleError('An organization id must be passed as a command line flag or in the context');
715
+ logger.verbose(`Force update: ${inspect(force, { colors: true })}`);
716
+ if (!id) {
717
+ throw new AppsembleError('The app id must be passed as a command line flag or in the context');
636
718
  }
637
- formData.append('OrganizationId', organizationId);
719
+ formData.append('force', String(force));
638
720
  formData.append('template', String(template));
639
721
  formData.append('demoMode', String(demoMode));
640
722
  formData.append('visibility', visibility);
641
723
  formData.append('iconBackground', iconBackground);
642
- formData.append('locked', appLock);
643
724
  if (icon) {
644
725
  const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
645
726
  logger.info(`Using icon from ${(_v = realIcon.path) !== null && _v !== void 0 ? _v : 'stdin'}`);
@@ -666,12 +747,21 @@ export async function publishApp({ clientCredentials, context, dryRun, modifyCon
666
747
  authScope += ' resources:write';
667
748
  }
668
749
  await authenticate(remote, authScope, clientCredentials);
750
+ if (appLock) {
751
+ logger.info(`Setting AppLock to ${appLock}`);
752
+ try {
753
+ await axios.post(`/api/apps/${id}/lock`, {
754
+ locked: appLock,
755
+ });
756
+ }
757
+ catch (error) {
758
+ logger.error(error);
759
+ process.exit(1);
760
+ }
761
+ }
669
762
  let data;
670
763
  try {
671
- ({ data } = await axios.post('/api/apps', formData, {
672
- baseURL: remote,
673
- params: { dryRun: dryRun || undefined },
674
- }));
764
+ ({ data } = await axios.patch(`/api/apps/${id}`, formData, { baseURL: remote }));
675
765
  }
676
766
  catch (error) {
677
767
  if (!axios.isAxiosError(error)) {
@@ -682,81 +772,89 @@ export async function publishApp({ clientCredentials, context, dryRun, modifyCon
682
772
  }
683
773
  throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
684
774
  }
685
- if (dryRun) {
686
- logger.info('Skipped uploading block themes and app messages.');
687
- logger.info('Successfully ran dry run. The app should be valid when creating it.');
688
- return;
689
- }
690
- if (file.isDirectory() && !dryRun) {
691
- // After uploading the app, upload block styles, messages if they are available
692
- await traverseBlockThemes(appVariantPath, data.id, remote, false);
693
- await uploadMessages(appVariantPath, data.id, remote, false);
694
- // After uploading the app, publish seed resources and assets if they are available
695
- if (assets) {
775
+ if (file.isDirectory()) {
776
+ // After uploading the app, upload block styles and messages if they are available
777
+ await traverseBlockThemes(appVariantPath, data.id, remote, force);
778
+ await uploadMessages(appVariantPath, data.id, remote, force);
779
+ // After updating the app, publish seed resources and assets if they are available
780
+ if (assets && (data.locked !== 'fullLock' || force)) {
696
781
  await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
697
782
  }
698
- if (resources) {
783
+ if (resources && (data.locked !== 'fullLock' || force)) {
699
784
  await publishSeedResources(appVariantPath, data, remote);
700
785
  }
701
786
  }
702
- if (modifyContext && appsembleContext && context && !dryRun) {
703
- rc.context[context].id = data.id;
704
- await writeData(join(appVariantPath, '.appsemblerc.yaml'), rc, { sort: false });
705
- logger.info(`Updated .appsemblerc: Set context.${context}.id to ${data.id}`);
706
- }
707
787
  const { host, protocol } = new URL(remote);
708
- logger.info(`Successfully published app ${data.definition.name}! šŸ™Œ`);
788
+ logger.info(`Successfully updated app ${data.definition.name}! šŸ™Œ`);
709
789
  logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
710
790
  logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
711
- const rcCollections = (_z = rc.context[context]) === null || _z === void 0 ? void 0 : _z.collections;
712
- if (Array.isArray(rcCollections)) {
713
- for (const collectionId of rcCollections) {
714
- try {
715
- await axios.post(`/api/appCollections/${collectionId}/apps`, { AppId: data.id }, {
716
- baseURL: remote,
717
- });
718
- logger.info(`Successfully added app to collection ${collectionId}`);
719
- }
720
- catch (error) {
721
- logger.error(error);
722
- }
723
- }
724
- }
725
791
  if (appVariantPath !== path) {
726
- const [defaultAppRc] = await readData(join(path, '.appsemblerc.yaml'));
727
- defaultAppRc.context[context] = rc.context[context];
728
- await writeData(join(path, '.appsemblerc.yaml'), defaultAppRc, { sort: false });
729
792
  await rm(appVariantPath, { recursive: true });
730
793
  logger.info(`Removed ${appVariantPath}`);
731
794
  }
732
795
  }
733
- /**
734
- * Resolve the app’s ID and remote based on the app context.
735
- *
736
- * @param appPath The path to the app.
737
- * @param name Which context to use in the AppsembleRC file.
738
- * @param defaultRemote The remote to fall back to.
739
- * @param defaultAppId The app ID to fall back to.
740
- * @returns The resolved app ID and remote.
741
- */
742
- export async function resolveAppIdAndRemote(appPath, name, defaultRemote, defaultAppId) {
743
- var _a;
744
- let id = defaultAppId;
745
- let resolvedRemote = defaultRemote;
746
- if (appPath) {
747
- const rcPath = join(appPath, '.appsemblerc.yaml');
748
- const [rc] = await readData(rcPath);
749
- const context = (_a = rc.context) === null || _a === void 0 ? void 0 : _a[name];
750
- if (!defaultAppId) {
751
- id = context === null || context === void 0 ? void 0 : context.id;
796
+ export async function patchApp({ id, remote, ...options }) {
797
+ const formData = new FormData();
798
+ if (options.path !== undefined) {
799
+ logger.info(`Setting app path to ${options.path}`);
800
+ formData.append('path', options.path);
801
+ }
802
+ if (options.force !== undefined) {
803
+ formData.append('force', String(options.force));
804
+ }
805
+ if (options.demoMode !== undefined) {
806
+ logger.info(`Setting app demo mode to ${options.demoMode}`);
807
+ formData.append('demoMode', String(options.demoMode));
808
+ }
809
+ if (options.template !== undefined) {
810
+ logger.info(`Setting template to ${options.template}`);
811
+ formData.append('template', String(options.template));
812
+ }
813
+ if (options.visibility !== undefined) {
814
+ logger.info(`Setting app visibility to ${options.visibility}`);
815
+ formData.append('visibility', options.visibility);
816
+ }
817
+ if (options.iconBackground !== undefined) {
818
+ logger.info(`Setting app icon background to ${options.iconBackground}`);
819
+ formData.append('iconBackground', options.iconBackground);
820
+ }
821
+ if (options.showAppDefinition !== undefined) {
822
+ logger.info(`Setting showAppDefinition to ${options.showAppDefinition}`);
823
+ formData.append('showAppDefinition', String(options.showAppDefinition));
824
+ }
825
+ if (options.showAppsembleLogin !== undefined) {
826
+ logger.info(`Setting showAppsembleLogin to ${options.showAppsembleLogin}`);
827
+ formData.append('showAppsembleLogin', String(options.showAppsembleLogin));
828
+ }
829
+ if (options.showAppsembleOAuth2Login !== undefined) {
830
+ logger.info(`Setting showAppsembleOAuth2Login to ${options.showAppsembleOAuth2Login}`);
831
+ formData.append('showAppsembleOAuth2Login', String(options.showAppsembleOAuth2Login));
832
+ }
833
+ if (options.enableSelfRegistration !== undefined) {
834
+ logger.info(`Setting enableSelfRegistration to ${options.enableSelfRegistration}`);
835
+ formData.append('enableSelfRegistration', String(options.enableSelfRegistration));
836
+ }
837
+ try {
838
+ if (!formData.getLengthSync() && options.locked === undefined) {
839
+ logger.warn('Nothing to patch. Need at least one additional argument to patch app.');
840
+ return;
752
841
  }
753
- if (context.remote) {
754
- resolvedRemote = context.remote;
842
+ if (formData.getLengthSync()) {
843
+ const { data } = await axios.patch(`/api/apps/${id}`, formData);
844
+ logger.info(`Successfully updated app with id ${id}`);
845
+ const { host, protocol } = new URL(remote);
846
+ logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
847
+ }
848
+ if (options.locked !== undefined) {
849
+ await axios.post(`/api/apps/${id}/lock`, {
850
+ locked: options.locked,
851
+ });
852
+ logger.info(`Successfully set app lock value for app with id ${id} to ${options.locked}`);
755
853
  }
854
+ logger.info(`App store page: ${new URL(`/apps/${id}`, remote)}`);
756
855
  }
757
- if (id == null) {
758
- throw new AppsembleError(`App ID was not found in context.${name}.id nor in --app-id`);
856
+ catch (error) {
857
+ logger.error(error);
759
858
  }
760
- return [id, coerceRemote(resolvedRemote)];
761
859
  }
762
860
  //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ export {};