@appsemble/cli 0.27.7 → 0.27.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.27.7/config/assets/logo.svg) Appsemble CLI
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.27.9/config/assets/logo.svg) Appsemble CLI
2
2
 
3
3
  > Manage apps and blocks from the command line.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@appsemble/cli)](https://www.npmjs.com/package/@appsemble/cli)
6
- [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.27.7/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.27.7)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.27.9/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.27.9)
7
7
  [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io)
8
8
 
9
9
  ## Table of Contents
@@ -157,6 +157,24 @@ Similarly, to update an app using an app directory, run the following command:
157
157
  appsemble app update --id <app-id> <path/to/updated app>.
158
158
  ```
159
159
 
160
+ If you want to update settings for an app, run the following command:
161
+
162
+ ```sh
163
+ appsemble app patch --id <app-id>
164
+ ```
165
+
166
+ Use `--help` flag with the above command to view the supported arguments.
167
+
168
+ ---
169
+
170
+ **🛈NOTE**
171
+
172
+ > The `app patch` command supports updating the `locked` property of an app, locked property is
173
+ > updated before other properties. If you set the locked property to `fullLock`, any other changes
174
+ > won’t be applied unless you are using `--force` flag.
175
+
176
+ ---
177
+
160
178
  To delete an app using the CLI use the following command:
161
179
 
162
180
  ```sh
@@ -169,10 +187,10 @@ To export an app as a zip file, run the following:
169
187
  appsemble app export --id <app-id>
170
188
  ```
171
189
 
172
- Supported arguments for this app are `resources`, `assets` and `path`. Resources is used to specify
173
- whether to include the resources in the exported file, the same can be applied to assets and path is
174
- the path of the folder where you want to put your downloaded file. The default path is `./apps`, and
175
- resources and assets are by default `false`.
190
+ Supported arguments for this command are `resources`, `assets` and `path`. Resources is used to
191
+ specify whether to include the resources in the exported file, the same can be applied to assets and
192
+ path is the path of the folder where you want to put your downloaded file. The default path is
193
+ `./apps`, and resources and assets are by default `false`.
176
194
 
177
195
  To import an app from a zip file, use the following command:
178
196
 
@@ -305,5 +323,5 @@ appsemble run-cronjobs --interval 30
305
323
 
306
324
  ## License
307
325
 
308
- [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.27.7/LICENSE.md) ©
326
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.27.9/LICENSE.md) ©
309
327
  [Appsemble](https://appsemble.com)
@@ -3,6 +3,7 @@ import * as remove from './delete.js';
3
3
  import * as exportApp from './export.js';
4
4
  import * as extractMessages from './extract-messages.js';
5
5
  import * as importApp from './import.js';
6
+ import * as patch from './patch.js';
6
7
  import * as publish from './publish.js';
7
8
  import * as update from './update.js';
8
9
  export { noop as handler } from '@appsemble/utils';
@@ -16,6 +17,7 @@ export function builder(yargs) {
16
17
  .command(exportApp)
17
18
  .command(extractMessages)
18
19
  .command(importApp)
20
+ .command(patch)
19
21
  .command(update)
20
22
  .demandCommand(1);
21
23
  }
@@ -0,0 +1,22 @@
1
+ import { type AppLock, type AppVisibility } from '@appsemble/types';
2
+ import { type Argv } from 'yargs';
3
+ import { type BaseArguments } from '../../types.js';
4
+ interface PatchAppArguments extends BaseArguments {
5
+ id: number;
6
+ iconBackground?: string;
7
+ force?: boolean;
8
+ path?: string;
9
+ visibility?: AppVisibility;
10
+ showAppDefinition?: boolean;
11
+ template?: boolean;
12
+ demoMode?: boolean;
13
+ locked?: AppLock;
14
+ showAppsembleOAuth2Login?: boolean;
15
+ showAppsembleLogin?: boolean;
16
+ enableSelfRegistration?: boolean;
17
+ }
18
+ export declare const command = "patch";
19
+ export declare const description = "Change the settings of an app without having the entire source.";
20
+ export declare function builder(yargs: Argv): Argv<any>;
21
+ export declare function handler({ clientCredentials, id, remote, ...args }: PatchAppArguments): Promise<void>;
22
+ export {};
@@ -0,0 +1,115 @@
1
+ import { authenticate, logger } from '@appsemble/node-utils';
2
+ import axios from 'axios';
3
+ import FormData from 'form-data';
4
+ export const command = 'patch';
5
+ export const description = 'Change the settings of an app without having the entire source.';
6
+ export function builder(yargs) {
7
+ return yargs
8
+ .option('id', {
9
+ description: 'Id of the app to patch.',
10
+ demandOption: true,
11
+ })
12
+ .option('iconBackground', {
13
+ description: 'The background color to use for the icon in opaque contexts.',
14
+ })
15
+ .option('path', {
16
+ description: 'Path to be used to determine the URL path of the app',
17
+ })
18
+ .option('visibility', {
19
+ description: 'Visibility of the app in the public app store.',
20
+ choices: ['public', 'unlisted', 'private'],
21
+ })
22
+ .option('showAppDefinition', {
23
+ description: 'Whether to expose the app definition in Appsemble studio.',
24
+ type: 'boolean',
25
+ })
26
+ .option('template', {
27
+ description: 'Whether to mark the app as template',
28
+ type: 'boolean',
29
+ })
30
+ .option('demoMode', {
31
+ description: 'Whether the app should be used in demo mode',
32
+ type: 'boolean',
33
+ })
34
+ .option('locked', {
35
+ description: 'Change the value of AppLock for your app.',
36
+ type: 'string',
37
+ choices: ['fullLock', 'studioLock', 'unlocked'],
38
+ })
39
+ .option('showAppsembleOAuth2Login', {
40
+ description: 'Whether the Appsemble OAuth2 login method should be shown',
41
+ type: 'boolean',
42
+ })
43
+ .option('showAppsembleLogin', {
44
+ description: 'Whether the Appsemble password login method should be shown',
45
+ type: 'boolean',
46
+ })
47
+ .option('enableSelfRegistration', {
48
+ description: 'Whether new users should be able to register themselves',
49
+ type: 'boolean',
50
+ })
51
+ .option('force', {
52
+ description: 'Whether the lock property should be ignored',
53
+ type: 'boolean',
54
+ });
55
+ }
56
+ export async function handler({ clientCredentials, id, remote, ...args }) {
57
+ await authenticate(remote, 'apps:write', clientCredentials);
58
+ const formData = new FormData();
59
+ if (args.path) {
60
+ logger.info(`Setting app path to ${args.path}`);
61
+ formData.append('path', args.path);
62
+ }
63
+ if (args.force) {
64
+ formData.append('force', String(args.force));
65
+ }
66
+ if (args.demoMode) {
67
+ logger.info(`Setting app demo mode to ${args.demoMode}`);
68
+ formData.append('demoMode', String(args.demoMode));
69
+ }
70
+ if (args.template) {
71
+ logger.info(`Setting template to ${args.template}`);
72
+ formData.append('template', String(args.template));
73
+ }
74
+ if (args.visibility) {
75
+ logger.info(`Setting app visibility to ${args.visibility}`);
76
+ formData.append('visibility', args.visibility);
77
+ }
78
+ if (args.iconBackground) {
79
+ logger.info(`Setting app icon background to ${args.iconBackground}`);
80
+ formData.append('iconBackground', args.iconBackground);
81
+ }
82
+ if (args.showAppDefinition) {
83
+ logger.info(`Setting showAppDefinition to ${args.showAppDefinition}`);
84
+ formData.append('showAppDefinition', String(args.showAppDefinition));
85
+ }
86
+ if (args.showAppsembleLogin) {
87
+ logger.info(`Setting showAppsembleLogin to ${args.showAppsembleLogin}`);
88
+ formData.append('showAppsembleLogin', String(args.showAppsembleLogin));
89
+ }
90
+ if (args.showAppsembleOAuth2Login) {
91
+ logger.info(`Setting showAppsembleOAuth2Login to ${args.showAppsembleOAuth2Login}`);
92
+ formData.append('showAppsembleOAuth2Login', String(args.showAppsembleOAuth2Login));
93
+ }
94
+ if (args.enableSelfRegistration) {
95
+ logger.info(`Setting enableSelfRegistration to ${args.enableSelfRegistration}`);
96
+ formData.append('enableSelfRegistration', String(args.enableSelfRegistration));
97
+ }
98
+ try {
99
+ if (args.locked) {
100
+ await axios.post(`api/apps/${id}/lock`, {
101
+ locked: args.locked,
102
+ });
103
+ logger.info(`Successfully set app lock value for app with id ${id} to ${args.locked}`);
104
+ if (!formData.getLengthSync()) {
105
+ return;
106
+ }
107
+ }
108
+ await axios.patch(`/api/apps/${id}`, formData);
109
+ logger.info(`Successfully updated app with id ${id}`);
110
+ }
111
+ catch (error) {
112
+ logger.error(error);
113
+ }
114
+ }
115
+ //# sourceMappingURL=patch.js.map
@@ -14,7 +14,7 @@ export function builder(yargs) {
14
14
  describe: 'If specified, use the specified context from .appsemblerc.yaml',
15
15
  })
16
16
  .option('organization', {
17
- describe: 'The organization ID the app should be published for.',
17
+ describe: 'The organization ID the app should be published under.',
18
18
  })
19
19
  .option('icon', {
20
20
  describe: 'The icon to upload. By default "icon.png" in the app directory is used.',
@@ -27,6 +27,9 @@ export function builder(yargs) {
27
27
  .option('maskable-icon', {
28
28
  describe: 'The maskable icon to upload. By default "maskable-icon.png" in the app directory is used.',
29
29
  coerce: coerceFile,
30
+ })
31
+ .option('variant', {
32
+ describe: 'The shared variant to use instead.',
30
33
  })
31
34
  .option('visibility', {
32
35
  describe: 'Visibility of the app in the public app store.',
@@ -29,6 +29,9 @@ export function builder(yargs) {
29
29
  .option('maskable-icon', {
30
30
  describe: 'The maskable icon to upload. By default "maskable-icon.png" in the app directory is used.',
31
31
  coerce: coerceFile,
32
+ })
33
+ .option('variant', {
34
+ describe: 'The shared variant to use instead.',
32
35
  })
33
36
  .option('visibility', {
34
37
  describe: 'Visibility of the app in the public app store.',
package/commands/serve.js CHANGED
@@ -56,7 +56,7 @@ export function builder(yargs) {
56
56
  export async function handler(argv) {
57
57
  setArgv(argv);
58
58
  const appPath = join(process.cwd(), argv.path);
59
- const [, , , appsembleApp] = await traverseAppDirectory(appPath, 'development', new FormData());
59
+ const [, , appsembleApp] = await traverseAppDirectory(appPath, new FormData());
60
60
  const appRoles = appsembleApp.definition.roles;
61
61
  const passedUserRole = argv['user-role'];
62
62
  if (passedUserRole && !(appRoles === null || appRoles === void 0 ? void 0 : appRoles.includes(passedUserRole))) {
package/lib/app.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  import { type ReadStream } from 'node:fs';
4
- import { type App, type AppsembleContext, type AppsembleRC, type AppVisibility } from '@appsemble/types';
4
+ import { type App, type AppsembleRC, type AppVisibility } from '@appsemble/types';
5
5
  import FormData from 'form-data';
6
6
  interface PublishAppParams {
7
7
  /**
@@ -20,6 +20,10 @@ interface PublishAppParams {
20
20
  * The path in which the App YAML is located.
21
21
  */
22
22
  path: string;
23
+ /**
24
+ * The shared variant to use instead.
25
+ */
26
+ variant?: string;
23
27
  /**
24
28
  * Visibility of the app in the public app store.
25
29
  */
@@ -103,6 +107,10 @@ interface UpdateAppParams {
103
107
  * The path in which the App YAML is located.
104
108
  */
105
109
  path: string;
110
+ /**
111
+ * The shared variant to use instead.
112
+ */
113
+ variant?: string;
106
114
  /**
107
115
  * Visibility of the app in the public app store.
108
116
  */
@@ -164,11 +172,10 @@ interface UpdateAppParams {
164
172
  * Traverses an app directory and appends the files it finds to the given FormData object.
165
173
  *
166
174
  * @param path The path of the app directory to traverse.
167
- * @param context The Context to use from `.appsemblerc.yaml`.
168
175
  * @param formData The FormData object to append the results into..
169
176
  * @returns The context from `.appsemblerc.yaml` if a match was found.
170
177
  */
171
- export declare function traverseAppDirectory(path: string, context: string, formData: FormData): Promise<[AppsembleContext, AppsembleRC, string, App]>;
178
+ export declare function traverseAppDirectory(path: string, formData: FormData): Promise<[AppsembleRC, string, App]>;
172
179
  /**
173
180
  * Export an app as a zip file.
174
181
  *
@@ -203,13 +210,13 @@ export declare function writeAppMessages(path: string, languages: string[], veri
203
210
  *
204
211
  * @param argv The command line options used for updating the app.
205
212
  */
206
- export declare function updateApp({ assets, clientCredentials, context, force, path, resources, ...options }: UpdateAppParams): Promise<void>;
213
+ export declare function updateApp({ clientCredentials, context, force, path, ...options }: UpdateAppParams): Promise<void>;
207
214
  /**
208
215
  * Publish a new app.
209
216
  *
210
217
  * @param options The options to use for publishing an app.
211
218
  */
212
- export declare function publishApp({ assets, clientCredentials, context, dryRun, modifyContext, path, resources, ...options }: PublishAppParams): Promise<void>;
219
+ export declare function publishApp({ clientCredentials, context, dryRun, modifyContext, path, ...options }: PublishAppParams): Promise<void>;
213
220
  /**
214
221
  * Resolve the app’s ID and remote based on the app context.
215
222
  *
package/lib/app.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createReadStream, createWriteStream, existsSync } from 'node:fs';
1
+ import { cpSync, createReadStream, createWriteStream, existsSync } from 'node:fs';
2
2
  import { mkdir, readdir, readFile, 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';
@@ -20,21 +20,25 @@ 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`.
24
23
  * @param formData The FormData object to append the results into..
25
24
  * @returns The context from `.appsemblerc.yaml` if a match was found.
26
25
  */
27
- export async function traverseAppDirectory(path, context, formData) {
28
- let discoveredContext;
26
+ export async function traverseAppDirectory(path, formData) {
29
27
  let rc;
30
- let iconPath;
31
- let maskableIconPath;
32
28
  let yaml;
33
29
  let controllerPath;
34
30
  let controllerBuildConfig;
35
31
  let controllerBuildResult;
36
32
  let controllerCode;
37
33
  let controllerImplementations;
34
+ let supportedLanguages = new Set();
35
+ try {
36
+ const languageFiles = await readdir(join(path, 'i18n'));
37
+ supportedLanguages = new Set(languageFiles.map((lang) => lang.split('.json')[0].toLowerCase()));
38
+ }
39
+ catch {
40
+ logger.warn(`Could not read ${join(path, 'i18n')}. No supported languages found.`);
41
+ }
38
42
  const gatheredData = {
39
43
  screenshotUrls: [],
40
44
  };
@@ -48,10 +52,6 @@ export async function traverseAppDirectory(path, context, formData) {
48
52
  formData.append('iconBackground', rc.iconBackground);
49
53
  gatheredData.iconBackground = rc.iconBackground;
50
54
  }
51
- if (context && has(rc === null || rc === void 0 ? void 0 : rc.context, context)) {
52
- discoveredContext = rc.context[context];
53
- logger.verbose(`Using context: ${inspect(discoveredContext, { colors: true })}`);
54
- }
55
55
  break;
56
56
  case 'app-definition.yaml': {
57
57
  logger.info(`Using app definition from ${filepath}`);
@@ -66,11 +66,9 @@ export async function traverseAppDirectory(path, context, formData) {
66
66
  }
67
67
  case 'icon.png':
68
68
  case 'icon.svg':
69
- iconPath = filepath;
70
69
  gatheredData.iconUrl = filepath;
71
70
  return;
72
71
  case 'maskable-icon.png':
73
- maskableIconPath = filepath;
74
72
  return;
75
73
  case 'readme.md':
76
74
  logger.info(`Including longDescription from ${filepath}`);
@@ -79,13 +77,28 @@ export async function traverseAppDirectory(path, context, formData) {
79
77
  return;
80
78
  case 'screenshots':
81
79
  return opendirSafe(filepath, (screenshotPath, screenshotStat) => {
82
- logger.info(`Adding screenshot ${screenshotPath} 🖼️`);
83
- if (!screenshotStat.isFile()) {
84
- throw new AppsembleError(`Expected ${screenshotPath} to be an image file`);
80
+ if (screenshotStat.isDirectory() &&
81
+ !['screenshots', ...supportedLanguages].includes(screenshotStat.name)) {
82
+ logger.warn(`Unsupported directory name at ${screenshotPath}. Skipping...`);
85
83
  }
86
- formData.append('screenshots', createReadStream(screenshotPath));
87
- gatheredData.screenshotUrls.push(basename(screenshotPath));
88
- }, { allowMissing: true });
84
+ if (screenshotStat.isFile()) {
85
+ const screenshotDirectoryPath = dirname(screenshotPath);
86
+ const screenshotDirectoryName = basename(screenshotDirectoryPath);
87
+ if (!['screenshots', ...supportedLanguages].includes(screenshotDirectoryName)) {
88
+ return;
89
+ }
90
+ const screenshotName = basename(screenshotPath);
91
+ const language = supportedLanguages.has(screenshotDirectoryName)
92
+ ? screenshotDirectoryName
93
+ : 'unspecified';
94
+ const tmpFilePath = join(screenshotDirectoryPath, `${language}-${screenshotName}`);
95
+ cpSync(screenshotPath, tmpFilePath);
96
+ logger.info(`Adding screenshot ${tmpFilePath} 🖼️`);
97
+ formData.append('screenshots', createReadStream(tmpFilePath));
98
+ gatheredData.screenshotUrls.push(basename(screenshotPath));
99
+ rm(tmpFilePath);
100
+ }
101
+ }, { allowMissing: true, recursive: true });
89
102
  case 'theme':
90
103
  return opendirSafe(filepath, async (themeDir, themeStat) => {
91
104
  const name = themeStat.name.toLowerCase();
@@ -117,6 +130,34 @@ export async function traverseAppDirectory(path, context, formData) {
117
130
  if (yaml === undefined) {
118
131
  throw new AppsembleError('No app definition found');
119
132
  }
133
+ return [rc, yaml, gatheredData];
134
+ }
135
+ async function retrieveContext(path, context) {
136
+ let rc;
137
+ let discoveredContext;
138
+ let iconPath;
139
+ let maskableIconPath;
140
+ await opendirSafe(path, async (filepath, filestat) => {
141
+ switch (filestat.name.toLowerCase()) {
142
+ case '.appsemblerc.yaml':
143
+ logger.info(`Reading app settings from ${filepath}`);
144
+ [rc] = await readData(filepath);
145
+ if (context && has(rc === null || rc === void 0 ? void 0 : rc.context, context)) {
146
+ discoveredContext = rc.context[context];
147
+ logger.verbose(`Using context: ${inspect(discoveredContext, { colors: true })}`);
148
+ }
149
+ break;
150
+ case 'icon.png':
151
+ case 'icon.svg':
152
+ iconPath = filepath;
153
+ return;
154
+ case 'maskable-icon.png':
155
+ maskableIconPath = filepath;
156
+ break;
157
+ default:
158
+ break;
159
+ }
160
+ });
120
161
  discoveredContext || (discoveredContext = {});
121
162
  discoveredContext.icon = discoveredContext.icon
122
163
  ? resolve(path, discoveredContext.icon)
@@ -124,7 +165,7 @@ export async function traverseAppDirectory(path, context, formData) {
124
165
  discoveredContext.maskableIcon = discoveredContext.maskableIcon
125
166
  ? resolve(path, discoveredContext.maskableIcon)
126
167
  : maskableIconPath;
127
- return [discoveredContext, rc, yaml, gatheredData];
168
+ return discoveredContext;
128
169
  }
129
170
  /**
130
171
  * Export an app as a zip file.
@@ -412,38 +453,45 @@ This block version is not used in the app`);
412
453
  *
413
454
  * @param argv The command line options used for updating the app.
414
455
  */
415
- export async function updateApp({ assets, clientCredentials, context, force, path, resources, ...options }) {
416
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
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;
417
458
  const file = await stat(path);
418
459
  const formData = new FormData();
419
- let appsembleContext;
460
+ const appsembleContext = await retrieveContext(path, context);
461
+ let yaml;
462
+ let filename = relative(process.cwd(), path);
463
+ const variant = (_b = (_a = appsembleContext.variant) !== null && _a !== void 0 ? _a : options.variant) !== null && _b !== void 0 ? _b : context;
420
464
  let appVariantPath = path;
421
- if (existsSync(join(path, 'variants', context))) {
422
- await applyAppVariant(path, context);
423
- appVariantPath = join(dirname(path), `${basename(path)}-${context}`);
465
+ if (variant && existsSync(join(path, 'variants', variant))) {
466
+ await applyAppVariant(path, variant);
467
+ appVariantPath = join(dirname(path), `${basename(path)}-${variant}`);
424
468
  }
425
469
  else {
426
- logger.warn(`App variant ${context} is not defined. Using default app.`);
470
+ logger.warn(`App variant ${variant} is not defined in ${join(path, 'variants')}. Using default app.`);
427
471
  }
428
472
  if (file.isFile()) {
429
473
  const [, data] = await readData(appVariantPath);
474
+ yaml = data;
430
475
  formData.append('yaml', data);
431
476
  }
432
477
  else {
433
- [appsembleContext] = await traverseAppDirectory(appVariantPath, context, formData);
434
- }
435
- const remote = (_a = appsembleContext.remote) !== null && _a !== void 0 ? _a : options.remote;
436
- const id = (_b = appsembleContext.id) !== null && _b !== void 0 ? _b : options.id;
437
- const template = (_d = (_c = appsembleContext.template) !== null && _c !== void 0 ? _c : options.template) !== null && _d !== void 0 ? _d : false;
438
- const assetsClonable = (_f = (_e = appsembleContext.assetsClonable) !== null && _e !== void 0 ? _e : options.assetsClonable) !== null && _f !== void 0 ? _f : false;
439
- const demoMode = (_h = (_g = appsembleContext.demoMode) !== null && _g !== void 0 ? _g : options.demoMode) !== null && _h !== void 0 ? _h : false;
440
- const visibility = (_j = appsembleContext.visibility) !== null && _j !== void 0 ? _j : options.visibility;
441
- const iconBackground = (_k = appsembleContext.iconBackground) !== null && _k !== void 0 ? _k : options.iconBackground;
442
- const icon = (_l = options.icon) !== null && _l !== void 0 ? _l : appsembleContext.icon;
443
- const maskableIcon = (_m = options.maskableIcon) !== null && _m !== void 0 ? _m : appsembleContext.maskableIcon;
444
- const sentryDsn = (_o = appsembleContext.sentryDsn) !== null && _o !== void 0 ? _o : options.sentryDsn;
445
- const sentryEnvironment = (_p = appsembleContext.sentryEnvironment) !== null && _p !== void 0 ? _p : options.sentryEnvironment;
446
- const googleAnalyticsId = (_q = appsembleContext.googleAnalyticsId) !== null && _q !== void 0 ? _q : options.googleAnalyticsId;
478
+ [, yaml] = await traverseAppDirectory(appVariantPath, formData);
479
+ filename = join(filename, 'app-definition.yaml');
480
+ }
481
+ 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;
483
+ const template = (_f = (_e = appsembleContext.template) !== null && _e !== void 0 ? _e : options.template) !== null && _f !== void 0 ? _f : false;
484
+ const assetsClonable = (_h = (_g = appsembleContext.assetsClonable) !== null && _g !== void 0 ? _g : options.assetsClonable) !== null && _h !== void 0 ? _h : false;
485
+ const demoMode = (_k = (_j = appsembleContext.demoMode) !== null && _j !== void 0 ? _j : options.demoMode) !== null && _k !== void 0 ? _k : false;
486
+ const visibility = (_l = appsembleContext.visibility) !== null && _l !== void 0 ? _l : options.visibility;
487
+ const iconBackground = (_m = appsembleContext.iconBackground) !== null && _m !== void 0 ? _m : options.iconBackground;
488
+ const icon = (_o = options.icon) !== null && _o !== void 0 ? _o : appsembleContext.icon;
489
+ const maskableIcon = (_p = options.maskableIcon) !== null && _p !== void 0 ? _p : appsembleContext.maskableIcon;
490
+ const sentryDsn = (_q = appsembleContext.sentryDsn) !== null && _q !== void 0 ? _q : options.sentryDsn;
491
+ const sentryEnvironment = (_r = appsembleContext.sentryEnvironment) !== null && _r !== void 0 ? _r : options.sentryEnvironment;
492
+ 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;
447
495
  const { appLock } = appsembleContext;
448
496
  logger.info(`App id: ${id}`);
449
497
  logger.verbose(`App remote: ${remote}`);
@@ -461,12 +509,12 @@ export async function updateApp({ assets, clientCredentials, context, force, pat
461
509
  formData.append('iconBackground', iconBackground);
462
510
  if (icon) {
463
511
  const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
464
- logger.info(`Using icon from ${(_r = realIcon.path) !== null && _r !== void 0 ? _r : 'stdin'}`);
512
+ logger.info(`Using icon from ${(_v = realIcon.path) !== null && _v !== void 0 ? _v : 'stdin'}`);
465
513
  formData.append('icon', realIcon);
466
514
  }
467
515
  if (maskableIcon) {
468
516
  const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
469
- logger.info(`Using maskable icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
517
+ logger.info(`Using maskable icon from ${(_w = realIcon.path) !== null && _w !== void 0 ? _w : 'stdin'}`);
470
518
  formData.append('maskableIcon', realIcon);
471
519
  }
472
520
  if (sentryDsn) {
@@ -499,19 +547,16 @@ export async function updateApp({ assets, clientCredentials, context, force, pat
499
547
  }
500
548
  let data;
501
549
  try {
502
- data = await axios
503
- .patch(`/api/apps/${id}`, formData, { baseURL: remote })
504
- .then((r) => r.data);
550
+ ({ data } = await axios.patch(`/api/apps/${id}`, formData, { baseURL: remote }));
505
551
  }
506
552
  catch (error) {
507
553
  if (!axios.isAxiosError(error)) {
508
554
  throw error;
509
555
  }
510
- if (((_u = (_t = error.response) === null || _t === void 0 ? void 0 : _t.data) === null || _u === void 0 ? void 0 : _u.message) !== 'App validation failed') {
556
+ if (((_y = (_x = error.response) === null || _x === void 0 ? void 0 : _x.data) === null || _y === void 0 ? void 0 : _y.message) !== 'App validation failed') {
511
557
  throw error;
512
558
  }
513
- logger.error(error.response.data);
514
- process.exit(1);
559
+ throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
515
560
  }
516
561
  if (file.isDirectory()) {
517
562
  // After uploading the app, upload block styles and messages if they are available
@@ -539,21 +584,22 @@ export async function updateApp({ assets, clientCredentials, context, force, pat
539
584
  *
540
585
  * @param options The options to use for publishing an app.
541
586
  */
542
- export async function publishApp({ assets, clientCredentials, context, dryRun, modifyContext, path, resources, ...options }) {
543
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
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;
544
589
  const file = await stat(path);
545
590
  const formData = new FormData();
546
- let appsembleContext;
591
+ const appsembleContext = await retrieveContext(path, context);
547
592
  let rc;
548
593
  let yaml;
549
594
  let filename = relative(process.cwd(), path);
595
+ const variant = (_b = (_a = appsembleContext.variant) !== null && _a !== void 0 ? _a : options.variant) !== null && _b !== void 0 ? _b : context;
550
596
  let appVariantPath = path;
551
- if (existsSync(join(path, 'variants', context))) {
552
- await applyAppVariant(path, context);
553
- appVariantPath = join(dirname(path), `${basename(path)}-${context}`);
597
+ if (variant && existsSync(join(path, 'variants', variant))) {
598
+ await applyAppVariant(path, variant);
599
+ appVariantPath = join(dirname(path), `${basename(path)}-${variant}`);
554
600
  }
555
601
  else {
556
- logger.warn(`App variant ${context} is not defined. Using default app.`);
602
+ logger.warn(`App variant ${variant} is not defined in ${join(path, 'variants')}. Using default app.`);
557
603
  }
558
604
  if (file.isFile()) {
559
605
  // Assuming file is App YAML
@@ -562,21 +608,23 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
562
608
  formData.append('yaml', data);
563
609
  }
564
610
  else {
565
- [appsembleContext, rc, yaml] = await traverseAppDirectory(appVariantPath, context, formData);
611
+ [rc, yaml] = await traverseAppDirectory(appVariantPath, formData);
566
612
  filename = join(filename, 'app-definition.yaml');
567
613
  }
568
- const remote = (_a = appsembleContext.remote) !== null && _a !== void 0 ? _a : options.remote;
569
- const organizationId = (_b = appsembleContext.organization) !== null && _b !== void 0 ? _b : options.organization;
570
- const template = (_d = (_c = appsembleContext.template) !== null && _c !== void 0 ? _c : options.template) !== null && _d !== void 0 ? _d : false;
571
- const assetsClonable = (_f = (_e = appsembleContext.assetsClonable) !== null && _e !== void 0 ? _e : options.assetsClonable) !== null && _f !== void 0 ? _f : false;
572
- const demoMode = (_h = (_g = appsembleContext.demoMode) !== null && _g !== void 0 ? _g : options.demoMode) !== null && _h !== void 0 ? _h : false;
573
- const visibility = (_j = appsembleContext.visibility) !== null && _j !== void 0 ? _j : options.visibility;
574
- const iconBackground = (_k = appsembleContext.iconBackground) !== null && _k !== void 0 ? _k : options.iconBackground;
575
- const icon = (_l = options.icon) !== null && _l !== void 0 ? _l : appsembleContext.icon;
576
- const maskableIcon = (_m = options.maskableIcon) !== null && _m !== void 0 ? _m : appsembleContext.maskableIcon;
577
- const sentryDsn = (_o = appsembleContext.sentryDsn) !== null && _o !== void 0 ? _o : options.sentryDsn;
578
- const sentryEnvironment = (_p = appsembleContext.sentryEnvironment) !== null && _p !== void 0 ? _p : options.sentryEnvironment;
579
- const googleAnalyticsId = (_q = appsembleContext.googleAnalyticsId) !== null && _q !== void 0 ? _q : options.googleAnalyticsId;
614
+ 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;
616
+ const template = (_f = (_e = appsembleContext.template) !== null && _e !== void 0 ? _e : options.template) !== null && _f !== void 0 ? _f : false;
617
+ const assetsClonable = (_h = (_g = appsembleContext.assetsClonable) !== null && _g !== void 0 ? _g : options.assetsClonable) !== null && _h !== void 0 ? _h : false;
618
+ const demoMode = (_k = (_j = appsembleContext.demoMode) !== null && _j !== void 0 ? _j : options.demoMode) !== null && _k !== void 0 ? _k : false;
619
+ const visibility = (_l = appsembleContext.visibility) !== null && _l !== void 0 ? _l : options.visibility;
620
+ const iconBackground = (_m = appsembleContext.iconBackground) !== null && _m !== void 0 ? _m : options.iconBackground;
621
+ const icon = (_o = options.icon) !== null && _o !== void 0 ? _o : appsembleContext.icon;
622
+ const maskableIcon = (_p = options.maskableIcon) !== null && _p !== void 0 ? _p : appsembleContext.maskableIcon;
623
+ const sentryDsn = (_q = appsembleContext.sentryDsn) !== null && _q !== void 0 ? _q : options.sentryDsn;
624
+ const sentryEnvironment = (_r = appsembleContext.sentryEnvironment) !== null && _r !== void 0 ? _r : options.sentryEnvironment;
625
+ 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;
580
628
  const appLock = appsembleContext.appLock || 'unlocked';
581
629
  logger.verbose(`App remote: ${remote}`);
582
630
  logger.verbose(`App organization: ${organizationId}`);
@@ -594,12 +642,12 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
594
642
  formData.append('locked', appLock);
595
643
  if (icon) {
596
644
  const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
597
- logger.info(`Using icon from ${(_r = realIcon.path) !== null && _r !== void 0 ? _r : 'stdin'}`);
645
+ logger.info(`Using icon from ${(_v = realIcon.path) !== null && _v !== void 0 ? _v : 'stdin'}`);
598
646
  formData.append('icon', realIcon);
599
647
  }
600
648
  if (maskableIcon) {
601
649
  const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
602
- logger.info(`Using maskable icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
650
+ logger.info(`Using maskable icon from ${(_w = realIcon.path) !== null && _w !== void 0 ? _w : 'stdin'}`);
603
651
  formData.append('maskableIcon', realIcon);
604
652
  }
605
653
  if (sentryDsn) {
@@ -629,7 +677,7 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
629
677
  if (!axios.isAxiosError(error)) {
630
678
  throw error;
631
679
  }
632
- if (((_u = (_t = error.response) === null || _t === void 0 ? void 0 : _t.data) === null || _u === void 0 ? void 0 : _u.message) !== 'App validation failed') {
680
+ if (((_y = (_x = error.response) === null || _x === void 0 ? void 0 : _x.data) === null || _y === void 0 ? void 0 : _y.message) !== 'App validation failed') {
633
681
  throw error;
634
682
  }
635
683
  throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
@@ -660,7 +708,7 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
660
708
  logger.info(`Successfully published app ${data.definition.name}! 🙌`);
661
709
  logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
662
710
  logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
663
- const rcCollections = rc.context[context].collections;
711
+ const rcCollections = (_z = rc.context[context]) === null || _z === void 0 ? void 0 : _z.collections;
664
712
  if (Array.isArray(rcCollections)) {
665
713
  for (const collectionId of rcCollections) {
666
714
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsemble/cli",
3
- "version": "0.27.7",
3
+ "version": "0.27.9",
4
4
  "description": "The CLI for developing with Appsemble apps and blocks",
5
5
  "keywords": [
6
6
  "app",
@@ -42,9 +42,9 @@
42
42
  "test": "vitest"
43
43
  },
44
44
  "dependencies": {
45
- "@appsemble/node-utils": "0.27.7",
46
- "@appsemble/types": "0.27.7",
47
- "@appsemble/utils": "0.27.7",
45
+ "@appsemble/node-utils": "0.27.9",
46
+ "@appsemble/types": "0.27.9",
47
+ "@appsemble/utils": "0.27.9",
48
48
  "@fortawesome/fontawesome-common-types": "^6.0.0",
49
49
  "@koa/cors": "^4.0.0",
50
50
  "@odata/parser": "^0.2.0",
@@ -87,7 +87,7 @@
87
87
  "yargs": "^17.0.0"
88
88
  },
89
89
  "devDependencies": {
90
- "@appsemble/types": "0.27.7",
90
+ "@appsemble/types": "0.27.9",
91
91
  "@types/concat-stream": "^2.0.0",
92
92
  "@types/inquirer": "^9.0.0",
93
93
  "@types/normalize-path": "^3.0.0",
@@ -6,7 +6,7 @@ pages:
6
6
  - name: Example Page A
7
7
  blocks:
8
8
  - type: action-button
9
- version: 0.27.7
9
+ version: 0.27.9
10
10
  parameters:
11
11
  icon: arrow-right
12
12
  actions:
@@ -17,7 +17,7 @@ pages:
17
17
  - name: Example Page B
18
18
  blocks:
19
19
  - type: action-button
20
- version: 0.27.7
20
+ version: 0.27.9
21
21
  parameters:
22
22
  icon: arrow-left
23
23
  actions:
@@ -2,7 +2,7 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@appsemble/sdk": "0.27.7",
5
+ "@appsemble/sdk": "0.27.9",
6
6
  "mini-jsx": "^4.0.0"
7
7
  }
8
8
  }
@@ -2,8 +2,8 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@appsemble/preact": "0.27.7",
6
- "@appsemble/sdk": "0.27.7",
5
+ "@appsemble/preact": "0.27.9",
6
+ "@appsemble/sdk": "0.27.9",
7
7
  "preact": "^10.0.0"
8
8
  }
9
9
  }
@@ -2,6 +2,6 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@appsemble/sdk": "0.27.7"
5
+ "@appsemble/sdk": "0.27.9"
6
6
  }
7
7
  }