@appsemble/cli 0.26.0 → 0.27.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/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.26.0/config/assets/logo.svg) Appsemble CLI
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.27.1/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.26.0/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.26.0)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.27.1/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.27.1)
7
7
  [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io)
8
8
 
9
9
  ## Table of Contents
@@ -305,5 +305,5 @@ appsemble run-cronjobs --interval 30
305
305
 
306
306
  ## License
307
307
 
308
- [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.26.0/LICENSE.md) ©
308
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.27.1/LICENSE.md) ©
309
309
  [Appsemble](https://appsemble.com)
@@ -40,26 +40,25 @@ export function builder(yargs) {
40
40
  .option('demo-mode', {
41
41
  describe: 'Whether the app should be used in demo mode.',
42
42
  type: 'boolean',
43
- })
44
- .option('seed', {
45
- describe: 'Whether to enable seeding for an app or not.',
46
- implies: ['demo-mode'],
47
43
  })
48
44
  .option('dry-run', {
49
45
  describe: 'Whether the API should be called to run without actually creating the app.',
50
46
  type: 'boolean',
51
47
  })
52
48
  .option('resources', {
53
- describe: 'Whether the resources from the `resources` directory should be created after publishing the app. The names of subdirectories are used as the name of the resource, otherwise the names of top level resource .json files are used instead.',
49
+ describe: 'Whether the resources from the `resources` directory should be created after publishing the app. The names of sub-directories are used as the name of the resource, otherwise the names of top level resource .json files are used instead.',
54
50
  type: 'boolean',
55
51
  })
56
52
  .option('assets', {
57
53
  describe: 'Whether the assets from the `assets` directory should be created after publishing the app.',
58
54
  type: 'boolean',
55
+ default: false,
59
56
  })
60
57
  .option('assets-clonable', {
61
58
  describe: 'Whether published assets should be clonable. Ignored if assets equals false.',
62
59
  type: 'boolean',
60
+ default: false,
61
+ implies: 'assets',
63
62
  })
64
63
  .option('modify-context', {
65
64
  describe: 'If the app context is specified, modify it for the current context to include the id of the published app.',
@@ -13,7 +13,9 @@ interface UpdateAppArguments extends BaseArguments {
13
13
  id: number;
14
14
  template: boolean;
15
15
  demoMode: boolean;
16
- seed: boolean;
16
+ resources: boolean;
17
+ assets: boolean;
18
+ assetsClonable: boolean;
17
19
  force: boolean;
18
20
  visibility: AppVisibility;
19
21
  sentryDsn: string;
@@ -1,4 +1,4 @@
1
- import { AppsembleError, authenticate, logger } from '@appsemble/node-utils';
1
+ import { AppsembleError, logger } from '@appsemble/node-utils';
2
2
  import fg from 'fast-glob';
3
3
  import normalizePath from 'normalize-path';
4
4
  import { updateApp } from '../../lib/app.js';
@@ -44,9 +44,20 @@ export function builder(yargs) {
44
44
  describe: 'Whether the app should be used in demo mode.',
45
45
  type: 'boolean',
46
46
  })
47
- .option('seed', {
48
- describe: 'Whether to enable seeding for an app or not.',
49
- implies: ['demo-mode'],
47
+ .option('resources', {
48
+ describe: 'Whether the resources from the `resources` directory should replace the seed resources of the app being updated. The names of sub-directories are used as the name of the resource, otherwise the names of top level resource .json files are used instead.',
49
+ type: 'boolean',
50
+ })
51
+ .option('assets', {
52
+ describe: 'Whether the assets from the `assets` directory should replace the seed assets of the app being updated.',
53
+ type: 'boolean',
54
+ default: false,
55
+ })
56
+ .option('assets-clonable', {
57
+ describe: 'Whether published assets should be clonable. Ignored if assets equals false.',
58
+ type: 'boolean',
59
+ default: false,
60
+ implies: 'assets',
50
61
  })
51
62
  .option('force', {
52
63
  describe: 'Whether the lock property should be ignored.',
@@ -70,7 +81,6 @@ export async function handler({ clientCredentials, id, paths, remote, ...args })
70
81
  }
71
82
  const normalizedPaths = paths.map((path) => normalizePath(path));
72
83
  const directories = await fg(normalizedPaths, { absolute: true, onlyDirectories: true });
73
- await authenticate(remote, 'apps:write', clientCredentials);
74
84
  logger.info(`Updating ${directories.length} apps`);
75
85
  for (const dir of directories) {
76
86
  await updateApp({
@@ -8,9 +8,10 @@ interface PublishAssetArguments extends BaseArguments {
8
8
  context: string;
9
9
  app: string;
10
10
  clonable: boolean;
11
+ seed: boolean;
11
12
  }
12
13
  export declare const command = "publish <paths...>";
13
14
  export declare const description = "Publish assets for an existing app.";
14
15
  export declare function builder(yargs: Argv): Argv<any>;
15
- export declare function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, useFileName, }: PublishAssetArguments): Promise<void>;
16
+ export declare function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, seed, useFileName, }: PublishAssetArguments): Promise<void>;
16
17
  export {};
@@ -35,9 +35,13 @@ export function builder(yargs) {
35
35
  type: 'boolean',
36
36
  default: false,
37
37
  describe: 'If true, all published assets will be clonable',
38
+ })
39
+ .option('seed', {
40
+ describe: 'If true, published resources will be used as seed',
41
+ default: false,
38
42
  });
39
43
  }
40
- export async function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, useFileName, }) {
44
+ export async function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, seed, useFileName, }) {
41
45
  const [resolvedAppId, resolvedRemote] = await resolveAppIdAndRemote(app, context, remote, appId);
42
46
  await authenticate(resolvedRemote, 'assets:write', clientCredentials);
43
47
  const normalizedPaths = paths.map((path) => normalizePath(path));
@@ -52,6 +56,7 @@ export async function handler({ app, appId, clientCredentials, clonable, context
52
56
  name: name || (useFileName ? parse(path).name : null),
53
57
  appId: resolvedAppId,
54
58
  path,
59
+ seed,
55
60
  remote: resolvedRemote,
56
61
  clonable,
57
62
  });
@@ -6,9 +6,10 @@ interface PublishResourceArguments extends BaseArguments {
6
6
  appId: number;
7
7
  context: string;
8
8
  app: string;
9
+ seed: boolean;
9
10
  }
10
11
  export declare const command = "publish <resource-name> <paths...>";
11
12
  export declare const description = "Publish resources based on a specified JSON file or directory.";
12
13
  export declare function builder(yargs: Argv): Argv<any>;
13
- export declare function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, }: PublishResourceArguments): Promise<void>;
14
+ export declare function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, seed, }: PublishResourceArguments): Promise<void>;
14
15
  export {};
@@ -27,9 +27,13 @@ export function builder(yargs) {
27
27
  .option('context', {
28
28
  describe: 'If specified, use the specified context from .appsemblerc.yaml',
29
29
  demandOption: 'app',
30
+ })
31
+ .option('seed', {
32
+ describe: 'If true, published resources will be used as seed',
33
+ default: false,
30
34
  });
31
35
  }
32
- export async function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, }) {
36
+ export async function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, seed, }) {
33
37
  const [resolvedAppId, resolvedRemote] = await resolveAppIdAndRemote(app, context, remote, appId);
34
38
  await authenticate(resolvedRemote, 'resources:write', clientCredentials);
35
39
  const normalizedPaths = paths.map((path) => normalizePath(path));
@@ -43,8 +47,9 @@ export async function handler({ app, appId, clientCredentials, context, paths, r
43
47
  await publishResource({
44
48
  type: resourceName,
45
49
  appId: resolvedAppId,
46
- path,
47
50
  remote: resolvedRemote,
51
+ path,
52
+ seed,
48
53
  });
49
54
  }
50
55
  }
package/lib/app.d.ts CHANGED
@@ -120,9 +120,17 @@ interface UpdateAppParams {
120
120
  */
121
121
  demoMode: boolean;
122
122
  /**
123
- * Whether the App should seed resources and assets in demo mode.
123
+ * Whether resources from the `resources` directory should be published after publishing the app.
124
124
  */
125
- seed: boolean;
125
+ resources: boolean;
126
+ /**
127
+ * Whether assets from the `assets` directory should be published after publishing the app.
128
+ */
129
+ assets: boolean;
130
+ /**
131
+ * Whether published assets should be clonable. Ignored if `assets` equals false.
132
+ */
133
+ assetsClonable: boolean;
126
134
  /**
127
135
  * Whether the locked property should be ignored.
128
136
  */
@@ -180,6 +188,8 @@ export declare function exportAppAsZip(appId: number, assets: boolean, resources
180
188
  * @param force Whether or not to force the update for locked apps.
181
189
  */
182
190
  export declare function uploadMessages(path: string, appId: number, remote: string, force: boolean): Promise<void>;
191
+ export declare function publishSeedResources(path: string, app: App, remote: string): Promise<void>;
192
+ export declare function publishSeedAssets(path: string, app: App, remote: string, assetsClonable: boolean): Promise<void>;
183
193
  /**
184
194
  * @param path The path to the app directory.
185
195
  * @param languages A list of languages for which translations should be added in addition to the
@@ -193,13 +203,13 @@ export declare function writeAppMessages(path: string, languages: string[], veri
193
203
  *
194
204
  * @param argv The command line options used for updating the app.
195
205
  */
196
- export declare function updateApp({ clientCredentials, context, force, path, ...options }: UpdateAppParams): Promise<void>;
206
+ export declare function updateApp({ assets, clientCredentials, context, force, path, resources, ...options }: UpdateAppParams): Promise<void>;
197
207
  /**
198
208
  * Publish a new app.
199
209
  *
200
210
  * @param options The options to use for publishing an app.
201
211
  */
202
- export declare function publishApp({ assets, assetsClonable, clientCredentials, context, dryRun, modifyContext, path, resources, ...options }: PublishAppParams): Promise<void>;
212
+ export declare function publishApp({ assets, clientCredentials, context, dryRun, modifyContext, path, resources, ...options }: PublishAppParams): Promise<void>;
203
213
  /**
204
214
  * Resolve the app’s ID and remote based on the app context.
205
215
  *
package/lib/app.js CHANGED
@@ -1,14 +1,13 @@
1
1
  import { createReadStream, createWriteStream, existsSync } from 'node:fs';
2
- import { mkdir, readdir, readFile, stat } from 'node:fs/promises';
3
- import { basename, join, parse, relative, resolve } from 'node:path';
2
+ import { mkdir, readdir, readFile, rm, stat } from 'node:fs/promises';
3
+ import { basename, dirname, join, parse, relative, resolve } from 'node:path';
4
4
  import { inspect } from 'node:util';
5
- import { AppsembleError, authenticate, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
5
+ import { applyAppVariant, AppsembleError, authenticate, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
6
6
  import { extractAppMessages, has, normalizeBlockName } from '@appsemble/utils';
7
7
  import axios from 'axios';
8
8
  import fg from 'fast-glob';
9
9
  import FormData from 'form-data';
10
10
  import normalizePath from 'normalize-path';
11
- import { parse as parseYaml } from 'yaml';
12
11
  import { publishAsset } from './asset.js';
13
12
  import { traverseBlockThemes } from './block.js';
14
13
  import { coerceRemote } from './coercers.js';
@@ -176,6 +175,88 @@ export async function uploadMessages(path, appId, remote, force) {
176
175
  logger.info(`Successfully uploaded messages for language “${language.language}” 🎉`);
177
176
  }
178
177
  }
178
+ export async function publishSeedResources(path, app, remote) {
179
+ var _a, _b;
180
+ const resourcesPath = join(path, 'resources');
181
+ logger.info(`Publishing seed resources from ${resourcesPath}`);
182
+ if (existsSync(resourcesPath)) {
183
+ logger.info(`Deleting existing seed resources from app ${app.id}`);
184
+ await axios.delete(`/api/apps/${app.id}/seed-resources`);
185
+ try {
186
+ const resourceFiles = await readdir(resourcesPath, { withFileTypes: true });
187
+ const resourcesToPublish = [];
188
+ const publishedResourcesIds = {};
189
+ for (const resource of resourceFiles) {
190
+ if (resource.isFile()) {
191
+ const { name } = parse(resource.name);
192
+ resourcesToPublish.push({
193
+ appId: app.id,
194
+ path: join(resourcesPath, resource.name),
195
+ definition: (_a = app.definition.resources) === null || _a === void 0 ? void 0 : _a[name],
196
+ type: name,
197
+ });
198
+ }
199
+ else if (resource.isDirectory()) {
200
+ const subDirectoryResources = await readdir(join(resourcesPath, resource.name), {
201
+ withFileTypes: true,
202
+ });
203
+ for (const subResource of subDirectoryResources.filter((s) => s.isFile())) {
204
+ resourcesToPublish.push({
205
+ appId: app.id,
206
+ path: join(resourcesPath, resource.name, subResource.name),
207
+ definition: (_b = app.definition.resources) === null || _b === void 0 ? void 0 : _b[resource.name],
208
+ type: resource.name,
209
+ });
210
+ }
211
+ }
212
+ }
213
+ await publishResourcesRecursively({
214
+ seed: true,
215
+ remote,
216
+ resourcesToPublish,
217
+ publishedResourcesIds,
218
+ });
219
+ }
220
+ catch (error) {
221
+ logger.error('Something went wrong when publishing seed resources:');
222
+ logger.error(error);
223
+ }
224
+ }
225
+ else {
226
+ logger.warn(`Missing resources directory in ${path}. Skipping...`);
227
+ }
228
+ }
229
+ export async function publishSeedAssets(path, app, remote, assetsClonable) {
230
+ const assetsPath = join(path, 'assets');
231
+ logger.info(`Publishing seed assets from ${assetsPath}`);
232
+ if (existsSync(assetsPath)) {
233
+ logger.info(`Deleting existing seed assets from app ${app.id}`);
234
+ await axios.delete(`/api/apps/${app.id}/seed-assets`);
235
+ const assetFiles = await readdir(assetsPath);
236
+ const normalizedPaths = assetFiles.map((assetFile) => normalizePath(join(assetsPath, assetFile)));
237
+ const files = await fg(normalizedPaths, { absolute: true, onlyFiles: true });
238
+ try {
239
+ logger.info(`Publishing ${files.length} asset(s)`);
240
+ for (const assetFilePath of files) {
241
+ await publishAsset({
242
+ name: parse(assetFilePath).name,
243
+ appId: app.id,
244
+ path: assetFilePath,
245
+ remote,
246
+ seed: true,
247
+ clonable: assetsClonable,
248
+ });
249
+ }
250
+ }
251
+ catch (error) {
252
+ logger.error('Something went wrong when creating assets:');
253
+ logger.error(error);
254
+ }
255
+ }
256
+ else {
257
+ logger.warn(`Missing assets directory in ${path}. Skipping...`);
258
+ }
259
+ }
179
260
  /**
180
261
  * @param path The path to the app directory.
181
262
  * @param languages A list of languages for which translations should be added in addition to the
@@ -331,32 +412,38 @@ This block version is not used in the app`);
331
412
  *
332
413
  * @param argv The command line options used for updating the app.
333
414
  */
334
- export async function updateApp({ clientCredentials, context, force, path, ...options }) {
335
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
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;
336
417
  const file = await stat(path);
337
418
  const formData = new FormData();
338
419
  let appsembleContext;
420
+ let appVariantPath = path;
421
+ if (existsSync(join(path, 'variants', context))) {
422
+ await applyAppVariant(path, context);
423
+ appVariantPath = join(dirname(path), `${basename(path)}-${context}`);
424
+ }
425
+ else {
426
+ logger.warn(`App variant ${context} is not defined. Using default app.`);
427
+ }
339
428
  if (file.isFile()) {
340
- const [, data] = await readData(path);
429
+ const [, data] = await readData(appVariantPath);
341
430
  formData.append('yaml', data);
342
431
  }
343
432
  else {
344
- [appsembleContext] = await traverseAppDirectory(path, context, formData);
433
+ [appsembleContext] = await traverseAppDirectory(appVariantPath, context, formData);
345
434
  }
346
435
  const remote = (_a = appsembleContext.remote) !== null && _a !== void 0 ? _a : options.remote;
347
436
  const id = (_b = appsembleContext.id) !== null && _b !== void 0 ? _b : options.id;
348
437
  const template = (_d = (_c = appsembleContext.template) !== null && _c !== void 0 ? _c : options.template) !== null && _d !== void 0 ? _d : false;
349
- const demoMode = (_f = (_e = appsembleContext.demoMode) !== null && _e !== void 0 ? _e : options.demoMode) !== null && _f !== void 0 ? _f : false;
350
- // TODO: seed depends on demoMode, this is caused by bad database normalization.
351
- // Consider using a property called `type` on the App model instead.
352
- const seed = (_j = (_h = (_g = appsembleContext.seed) !== null && _g !== void 0 ? _g : options.seed) !== null && _h !== void 0 ? _h : options.demoMode) !== null && _j !== void 0 ? _j : false;
353
- const visibility = (_k = appsembleContext.visibility) !== null && _k !== void 0 ? _k : options.visibility;
354
- const iconBackground = (_l = appsembleContext.iconBackground) !== null && _l !== void 0 ? _l : options.iconBackground;
355
- const icon = (_m = options.icon) !== null && _m !== void 0 ? _m : appsembleContext.icon;
356
- const maskableIcon = (_o = options.maskableIcon) !== null && _o !== void 0 ? _o : appsembleContext.maskableIcon;
357
- const sentryDsn = (_p = appsembleContext.sentryDsn) !== null && _p !== void 0 ? _p : options.sentryDsn;
358
- const sentryEnvironment = (_q = appsembleContext.sentryEnvironment) !== null && _q !== void 0 ? _q : options.sentryEnvironment;
359
- const googleAnalyticsId = (_r = appsembleContext.googleAnalyticsId) !== null && _r !== void 0 ? _r : options.googleAnalyticsId;
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;
360
447
  const { appLock } = appsembleContext;
361
448
  logger.info(`App id: ${id}`);
362
449
  logger.verbose(`App remote: ${remote}`);
@@ -370,17 +457,16 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
370
457
  formData.append('force', String(force));
371
458
  formData.append('template', String(template));
372
459
  formData.append('demoMode', String(demoMode));
373
- formData.append('seed', String(seed));
374
460
  formData.append('visibility', visibility);
375
461
  formData.append('iconBackground', iconBackground);
376
462
  if (icon) {
377
463
  const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
378
- logger.info(`Using icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
464
+ logger.info(`Using icon from ${(_r = realIcon.path) !== null && _r !== void 0 ? _r : 'stdin'}`);
379
465
  formData.append('icon', realIcon);
380
466
  }
381
467
  if (maskableIcon) {
382
468
  const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
383
- logger.info(`Using maskable icon from ${(_t = realIcon.path) !== null && _t !== void 0 ? _t : 'stdin'}`);
469
+ logger.info(`Using maskable icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
384
470
  formData.append('maskableIcon', realIcon);
385
471
  }
386
472
  if (sentryDsn) {
@@ -394,7 +480,11 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
394
480
  logger.info('Using Google Analytics');
395
481
  formData.append('googleAnalyticsID', googleAnalyticsId);
396
482
  }
397
- await authenticate(remote, 'apps:write', clientCredentials);
483
+ let authScope = 'apps:write';
484
+ if (resources) {
485
+ authScope += ' resources:write';
486
+ }
487
+ await authenticate(remote, authScope, clientCredentials);
398
488
  if (appLock) {
399
489
  logger.info(`Setting AppLock to ${appLock}`);
400
490
  try {
@@ -417,7 +507,7 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
417
507
  if (!axios.isAxiosError(error)) {
418
508
  throw error;
419
509
  }
420
- if (((_v = (_u = error.response) === null || _u === void 0 ? void 0 : _u.data) === null || _v === void 0 ? void 0 : _v.message) !== 'App validation failed') {
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') {
421
511
  throw error;
422
512
  }
423
513
  logger.error(error.response.data);
@@ -425,51 +515,68 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
425
515
  }
426
516
  if (file.isDirectory()) {
427
517
  // After uploading the app, upload block styles and messages if they are available
428
- await traverseBlockThemes(path, data.id, remote, force);
429
- await uploadMessages(path, data.id, remote, force);
518
+ await traverseBlockThemes(appVariantPath, data.id, remote, force);
519
+ await uploadMessages(appVariantPath, data.id, remote, force);
520
+ // After updating the app, publish seed resources and assets if they are available
521
+ if (assets && (data.locked !== 'fullLock' || force)) {
522
+ await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
523
+ }
524
+ if (resources && (data.locked !== 'fullLock' || force)) {
525
+ await publishSeedResources(appVariantPath, data, remote);
526
+ }
430
527
  }
431
528
  const { host, protocol } = new URL(remote);
432
529
  logger.info(`Successfully updated app ${data.definition.name}! 🙌`);
433
530
  logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
434
531
  logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
532
+ if (appVariantPath !== path) {
533
+ await rm(appVariantPath, { recursive: true });
534
+ logger.info(`Removed ${appVariantPath}`);
535
+ }
435
536
  }
436
537
  /**
437
538
  * Publish a new app.
438
539
  *
439
540
  * @param options The options to use for publishing an app.
440
541
  */
441
- export async function publishApp({ assets, assetsClonable, clientCredentials, context, dryRun, modifyContext, path, resources, ...options }) {
442
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
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;
443
544
  const file = await stat(path);
444
545
  const formData = new FormData();
445
546
  let appsembleContext;
446
547
  let rc;
447
548
  let yaml;
448
549
  let filename = relative(process.cwd(), path);
550
+ let appVariantPath = path;
551
+ if (existsSync(join(path, 'variants', context))) {
552
+ await applyAppVariant(path, context);
553
+ appVariantPath = join(dirname(path), `${basename(path)}-${context}`);
554
+ }
555
+ else {
556
+ logger.warn(`App variant ${context} is not defined. Using default app.`);
557
+ }
449
558
  if (file.isFile()) {
450
559
  // Assuming file is App YAML
451
- const [, data] = await readData(path);
560
+ const [, data] = await readData(appVariantPath);
452
561
  yaml = data;
453
562
  formData.append('yaml', data);
454
563
  }
455
564
  else {
456
- [appsembleContext, rc, yaml] = await traverseAppDirectory(path, context, formData);
565
+ [appsembleContext, rc, yaml] = await traverseAppDirectory(appVariantPath, context, formData);
457
566
  filename = join(filename, 'app-definition.yaml');
458
567
  }
459
568
  const remote = (_a = appsembleContext.remote) !== null && _a !== void 0 ? _a : options.remote;
460
569
  const organizationId = (_b = appsembleContext.organization) !== null && _b !== void 0 ? _b : options.organization;
461
570
  const template = (_d = (_c = appsembleContext.template) !== null && _c !== void 0 ? _c : options.template) !== null && _d !== void 0 ? _d : false;
462
- const demoMode = (_f = (_e = appsembleContext.demoMode) !== null && _e !== void 0 ? _e : options.demoMode) !== null && _f !== void 0 ? _f : false;
463
- // TODO: seed depends on demoMode, this is caused by bad database normalization.
464
- // Consider using a property called `type` on the App model instead.
465
- const seed = (_j = (_h = (_g = appsembleContext.seed) !== null && _g !== void 0 ? _g : options.seed) !== null && _h !== void 0 ? _h : options.demoMode) !== null && _j !== void 0 ? _j : false;
466
- const visibility = (_k = appsembleContext.visibility) !== null && _k !== void 0 ? _k : options.visibility;
467
- const iconBackground = (_l = appsembleContext.iconBackground) !== null && _l !== void 0 ? _l : options.iconBackground;
468
- const icon = (_m = options.icon) !== null && _m !== void 0 ? _m : appsembleContext.icon;
469
- const maskableIcon = (_o = options.maskableIcon) !== null && _o !== void 0 ? _o : appsembleContext.maskableIcon;
470
- const sentryDsn = (_p = appsembleContext.sentryDsn) !== null && _p !== void 0 ? _p : options.sentryDsn;
471
- const sentryEnvironment = (_q = appsembleContext.sentryEnvironment) !== null && _q !== void 0 ? _q : options.sentryEnvironment;
472
- const googleAnalyticsId = (_r = appsembleContext.googleAnalyticsId) !== null && _r !== void 0 ? _r : options.googleAnalyticsId;
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;
473
580
  const appLock = appsembleContext.appLock || 'unlocked';
474
581
  logger.verbose(`App remote: ${remote}`);
475
582
  logger.verbose(`App organization: ${organizationId}`);
@@ -482,18 +589,17 @@ export async function publishApp({ assets, assetsClonable, clientCredentials, co
482
589
  formData.append('OrganizationId', organizationId);
483
590
  formData.append('template', String(template));
484
591
  formData.append('demoMode', String(demoMode));
485
- formData.append('seed', String(seed));
486
592
  formData.append('visibility', visibility);
487
593
  formData.append('iconBackground', iconBackground);
488
594
  formData.append('locked', appLock);
489
595
  if (icon) {
490
596
  const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
491
- logger.info(`Using icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
597
+ logger.info(`Using icon from ${(_r = realIcon.path) !== null && _r !== void 0 ? _r : 'stdin'}`);
492
598
  formData.append('icon', realIcon);
493
599
  }
494
600
  if (maskableIcon) {
495
601
  const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
496
- logger.info(`Using maskable icon from ${(_t = realIcon.path) !== null && _t !== void 0 ? _t : 'stdin'}`);
602
+ logger.info(`Using maskable icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
497
603
  formData.append('maskableIcon', realIcon);
498
604
  }
499
605
  if (sentryDsn) {
@@ -523,7 +629,7 @@ export async function publishApp({ assets, assetsClonable, clientCredentials, co
523
629
  if (!axios.isAxiosError(error)) {
524
630
  throw error;
525
631
  }
526
- if (((_v = (_u = error.response) === null || _u === void 0 ? void 0 : _u.data) === null || _v === void 0 ? void 0 : _v.message) !== 'App validation failed') {
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') {
527
633
  throw error;
528
634
  }
529
635
  throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
@@ -534,83 +640,47 @@ export async function publishApp({ assets, assetsClonable, clientCredentials, co
534
640
  return;
535
641
  }
536
642
  if (file.isDirectory() && !dryRun) {
537
- // After uploading the app, upload block styles and messages if they are available
538
- await traverseBlockThemes(path, data.id, remote, false);
539
- await uploadMessages(path, data.id, remote, false);
540
- if (assets && existsSync(join(path, 'assets'))) {
541
- const assetsPath = join(path, 'assets');
542
- const assetFiles = await readdir(assetsPath);
543
- const normalizedPaths = assetFiles.map((assetFile) => normalizePath(join(assetsPath, assetFile)));
544
- const files = await fg(normalizedPaths, { absolute: true, onlyFiles: true });
545
- try {
546
- logger.info(`Publishing ${files.length} asset(s)`);
547
- for (const assetFilePath of files) {
548
- await publishAsset({
549
- name: parse(assetFilePath).name,
550
- appId: data.id,
551
- path: assetFilePath,
552
- remote,
553
- clonable: assetsClonable,
554
- });
555
- }
556
- }
557
- catch (error) {
558
- logger.error('Something went wrong when creating assets:');
559
- logger.error(error);
560
- }
643
+ // After uploading the app, upload block styles, messages if they are available
644
+ await traverseBlockThemes(appVariantPath, data.id, remote, false);
645
+ await uploadMessages(appVariantPath, data.id, remote, false);
646
+ // After uploading the app, publish seed resources and assets if they are available
647
+ if (assets) {
648
+ await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
561
649
  }
562
- if (resources && existsSync(join(path, 'resources'))) {
563
- const appDefinition = parseYaml(yaml, { maxAliasCount: 10000 });
564
- const resourcePath = join(path, 'resources');
565
- try {
566
- const resourceFiles = await readdir(resourcePath, { withFileTypes: true });
567
- const resourcesToPublish = [];
568
- const publishedResourcesIds = {};
569
- for (const resource of resourceFiles) {
570
- if (resource.isFile()) {
571
- const { name } = parse(resource.name);
572
- resourcesToPublish.push({
573
- appId: data.id,
574
- path: join(resourcePath, resource.name),
575
- definition: appDefinition.resources[name],
576
- type: name,
577
- });
578
- }
579
- else if (resource.isDirectory()) {
580
- const subDirectoryResources = await readdir(join(resourcePath, resource.name), {
581
- withFileTypes: true,
582
- });
583
- for (const subResource of subDirectoryResources.filter((s) => s.isFile())) {
584
- resourcesToPublish.push({
585
- appId: data.id,
586
- path: join(resourcePath, resource.name, subResource.name),
587
- definition: appDefinition.resources[resource.name],
588
- type: resource.name,
589
- });
590
- }
591
- }
592
- }
593
- await publishResourcesRecursively({
594
- remote,
595
- resourcesToPublish,
596
- publishedResourcesIds,
597
- });
598
- }
599
- catch (error) {
600
- logger.error('Something went wrong when creating resources:');
601
- logger.error(error);
602
- }
650
+ if (resources) {
651
+ await publishSeedResources(appVariantPath, data, remote);
603
652
  }
604
653
  }
605
654
  if (modifyContext && appsembleContext && context && !dryRun) {
606
655
  rc.context[context].id = data.id;
607
- await writeData(join(path, '.appsemblerc.yaml'), rc, { sort: false });
656
+ await writeData(join(appVariantPath, '.appsemblerc.yaml'), rc, { sort: false });
608
657
  logger.info(`Updated .appsemblerc: Set context.${context}.id to ${data.id}`);
609
658
  }
610
659
  const { host, protocol } = new URL(remote);
611
660
  logger.info(`Successfully published app ${data.definition.name}! 🙌`);
612
661
  logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
613
662
  logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
663
+ const rcCollections = rc.context[context].collections;
664
+ if (Array.isArray(rcCollections)) {
665
+ for (const collectionId of rcCollections) {
666
+ try {
667
+ await axios.post(`/api/appCollections/${collectionId}/apps`, { AppId: data.id }, {
668
+ baseURL: remote,
669
+ });
670
+ logger.info(`Successfully added app to collection ${collectionId}`);
671
+ }
672
+ catch (error) {
673
+ logger.error(error);
674
+ }
675
+ }
676
+ }
677
+ if (appVariantPath !== path) {
678
+ const [defaultAppRc] = await readData(join(path, '.appsemblerc.yaml'));
679
+ defaultAppRc.context[context] = rc.context[context];
680
+ await writeData(join(path, '.appsemblerc.yaml'), defaultAppRc, { sort: false });
681
+ await rm(appVariantPath, { recursive: true });
682
+ logger.info(`Removed ${appVariantPath}`);
683
+ }
614
684
  }
615
685
  /**
616
686
  * Resolve the app’s ID and remote based on the app context.
package/lib/asset.d.ts CHANGED
@@ -16,10 +16,14 @@ interface PublishAssetParams {
16
16
  * The remote server to publish the asset on.
17
17
  */
18
18
  remote: string;
19
+ /**
20
+ * Whether the published asset should be used as seed.
21
+ */
22
+ seed: boolean;
19
23
  /**
20
24
  * Whether the asset should be clonable.
21
25
  */
22
26
  clonable: boolean;
23
27
  }
24
- export declare function publishAsset({ appId, clonable, name, path, remote, }: PublishAssetParams): Promise<void>;
28
+ export declare function publishAsset({ appId, clonable, name, path, remote, seed, }: PublishAssetParams): Promise<void>;
25
29
  export {};
package/lib/asset.js CHANGED
@@ -3,7 +3,7 @@ import { logger } from '@appsemble/node-utils';
3
3
  import { normalize } from '@appsemble/utils';
4
4
  import axios from 'axios';
5
5
  import FormData from 'form-data';
6
- export async function publishAsset({ appId, clonable, name, path, remote, }) {
6
+ export async function publishAsset({ appId, clonable, name, path, remote, seed, }) {
7
7
  const formData = new FormData();
8
8
  const file = createReadStream(path);
9
9
  formData.append('file', file);
@@ -11,7 +11,8 @@ export async function publishAsset({ appId, clonable, name, path, remote, }) {
11
11
  if (name) {
12
12
  formData.append('name', normalize(name));
13
13
  }
14
- const { data: { id }, } = await axios.post(`/api/apps/${appId}/assets`, formData, { baseURL: remote });
14
+ const endpoint = `/api/apps/${appId}/${seed ? 'seed-' : ''}assets`;
15
+ const { data: { id }, } = await axios.post(endpoint, formData, { baseURL: remote });
15
16
  logger.info(`Published asset ${id}${name ? ` with name ${normalize(name)}` : ''}`);
16
17
  }
17
18
  //# sourceMappingURL=asset.js.map
package/lib/resource.d.ts CHANGED
@@ -34,6 +34,10 @@ interface PublishResourceParams {
34
34
  * The remote server to publish the resource on.
35
35
  */
36
36
  remote: string;
37
+ /**
38
+ * Whether the published resource should be used as seed.
39
+ */
40
+ seed: boolean;
37
41
  /**
38
42
  * The definition of the resource.
39
43
  */
@@ -54,6 +58,10 @@ interface PublishResourcesRecursivelyParams {
54
58
  * The remote server to publish the resource on.
55
59
  */
56
60
  remote: string;
61
+ /**
62
+ * Whether the published resources should be used as seed.
63
+ */
64
+ seed: boolean;
57
65
  /**
58
66
  * All resources that need to be published.
59
67
  */
@@ -63,7 +71,7 @@ interface PublishResourcesRecursivelyParams {
63
71
  */
64
72
  publishedResourcesIds: Record<string, number[]>;
65
73
  }
66
- export declare function publishResource({ appId, definition, path, publishedResourcesIds, remote, type, }: PublishResourceParams): Promise<number[]>;
67
- export declare function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, }: PublishResourcesRecursivelyParams): Promise<Record<string, number[]>>;
74
+ export declare function publishResource({ appId, definition, path, publishedResourcesIds, remote, seed, type, }: PublishResourceParams): Promise<number[]>;
75
+ export declare function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, seed, }: PublishResourcesRecursivelyParams): Promise<Record<string, number[]>>;
68
76
  export declare function updateResource({ appId, path, remote, resourceName, }: UpdateResourceParams): Promise<void>;
69
77
  export {};
package/lib/resource.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { AppsembleError, logger, readData } from '@appsemble/node-utils';
3
3
  import axios from 'axios';
4
- export async function publishResource({ appId, definition, path, publishedResourcesIds = {}, remote, type, }) {
4
+ export async function publishResource({ appId, definition, path, publishedResourcesIds = {}, remote, seed, type, }) {
5
5
  const csv = path.endsWith('.csv');
6
6
  let resources;
7
7
  if (csv) {
@@ -16,15 +16,16 @@ export async function publishResource({ appId, definition, path, publishedResour
16
16
  }
17
17
  logger.info(`Publishing resource(s) from ${path}`);
18
18
  let data;
19
+ const endpoint = `/api/apps/${appId}/${seed ? 'seed-' : ''}resources/${type}`;
19
20
  if (csv) {
20
- const response = await axios.post(`/api/apps/${appId}/resources/${type}`, resources, {
21
+ const response = await axios.post(endpoint, resources, {
21
22
  baseURL: remote,
22
23
  headers: { 'content-type': 'text/csv' },
23
24
  });
24
25
  data = response.data;
25
26
  }
26
27
  else {
27
- const response = await axios.post(`/api/apps/${appId}/resources/${type}`, resources.map((resource) => {
28
+ const response = await axios.post(endpoint, resources.map((resource) => {
28
29
  var _a, _b, _c;
29
30
  const resourceWithReferences = { ...resource };
30
31
  for (const [referencedProperty, resourceReference] of Object.entries((_a = definition === null || definition === void 0 ? void 0 : definition.references) !== null && _a !== void 0 ? _a : {})) {
@@ -45,7 +46,7 @@ export async function publishResource({ appId, definition, path, publishedResour
45
46
  .join('\n')}`);
46
47
  return ids;
47
48
  }
48
- export async function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, }) {
49
+ export async function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, seed, }) {
49
50
  let updatedPublishedResourcesIds = { ...publishedResourcesIds };
50
51
  for (const resourceToPublish of resourcesToPublish) {
51
52
  const resourceReferences = resourceToPublish.definition.references;
@@ -56,9 +57,10 @@ export async function publishResourcesRecursively({ publishedResourcesIds, remot
56
57
  if (referencedResourcesToPublish.length &&
57
58
  !updatedPublishedResourcesIds[referencedResourceType]) {
58
59
  const referencedPublishedResourcesIds = await publishResourcesRecursively({
59
- remote,
60
60
  resourcesToPublish: referencedResourcesToPublish,
61
61
  publishedResourcesIds: updatedPublishedResourcesIds,
62
+ remote,
63
+ seed,
62
64
  });
63
65
  updatedPublishedResourcesIds = {
64
66
  ...updatedPublishedResourcesIds,
@@ -72,6 +74,7 @@ export async function publishResourcesRecursively({ publishedResourcesIds, remot
72
74
  ...resourceToPublish,
73
75
  publishedResourcesIds: updatedPublishedResourcesIds,
74
76
  remote,
77
+ seed,
75
78
  });
76
79
  }
77
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsemble/cli",
3
- "version": "0.26.0",
3
+ "version": "0.27.1",
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.26.0",
46
- "@appsemble/types": "0.26.0",
47
- "@appsemble/utils": "0.26.0",
45
+ "@appsemble/node-utils": "0.27.1",
46
+ "@appsemble/types": "0.27.1",
47
+ "@appsemble/utils": "0.27.1",
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.26.0",
90
+ "@appsemble/types": "0.27.1",
91
91
  "@types/concat-stream": "^2.0.0",
92
92
  "@types/inquirer": "^9.0.0",
93
93
  "@types/normalize-path": "^3.0.0",
@@ -33,4 +33,3 @@ export * from './applyAppServiceSecrets.js';
33
33
  export * from './getAppMembers.js';
34
34
  export * from './getAppTeams.js';
35
35
  export * from './getAppUserInfo.js';
36
- export * from './checkSeededResources.js';
@@ -33,5 +33,4 @@ export * from './applyAppServiceSecrets.js';
33
33
  export * from './getAppMembers.js';
34
34
  export * from './getAppTeams.js';
35
35
  export * from './getAppUserInfo.js';
36
- export * from './checkSeededResources.js';
37
36
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,7 @@
1
- import { applyAppServiceSecrets, checkRole, checkSeededResources, createAppAsset, createAppResourcesWithAssets, createSettings, createTheme, deleteAppAsset, deleteAppResource, email, getApp, getAppAssets, getAppBlockStyles, getAppDetails, getAppIcon, getAppMembers, getAppMessages, getAppResource, getAppResources, getAppScreenshots, getAppStyles, getAppTeams, getAppUrl, getAppUserInfo, getBlockAsset, getBlockMessages, getBlocksAssetsPaths, getCsp, getDbUpdated, getHost, getTheme, parseQuery, reloadUser, sendNotifications, updateAppResource, verifyResourceActionPermission, } from './index.js';
1
+ import { applyAppServiceSecrets, checkRole, createAppAsset, createAppResourcesWithAssets, createSettings, createTheme, deleteAppAsset, deleteAppResource, email, getApp, getAppAssets, getAppBlockStyles, getAppDetails, getAppIcon, getAppMembers, getAppMessages, getAppResource, getAppResources, getAppScreenshots, getAppStyles, getAppTeams, getAppUrl, getAppUserInfo, getBlockAsset, getBlockMessages, getBlocksAssetsPaths, getCsp, getDbUpdated, getHost, getTheme, parseQuery, reloadUser, sendNotifications, updateAppResource, verifyResourceActionPermission, } from './index.js';
2
2
  export const options = {
3
3
  applyAppServiceSecrets,
4
4
  checkRole,
5
- checkSeededResources,
6
5
  createAppAsset,
7
6
  createAppResourcesWithAssets,
8
7
  createSettings,
@@ -6,7 +6,7 @@ pages:
6
6
  - name: Example Page A
7
7
  blocks:
8
8
  - type: action-button
9
- version: 0.26.0
9
+ version: 0.27.1
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.26.0
20
+ version: 0.27.1
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.26.0",
5
+ "@appsemble/sdk": "0.27.1",
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.26.0",
6
- "@appsemble/sdk": "0.26.0",
5
+ "@appsemble/preact": "0.27.1",
6
+ "@appsemble/sdk": "0.27.1",
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.26.0"
5
+ "@appsemble/sdk": "0.27.1"
6
6
  }
7
7
  }
@@ -1 +0,0 @@
1
- export declare function checkSeededResources(): Promise<boolean>;
@@ -1,4 +0,0 @@
1
- export function checkSeededResources() {
2
- return Promise.resolve(false);
3
- }
4
- //# sourceMappingURL=checkSeededResources.js.map