@swell/cli 2.9.3 → 2.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/app-command.d.ts +2 -3
  2. package/dist/app-command.js +3 -6
  3. package/dist/commands/app/dev.d.ts +2 -2
  4. package/dist/commands/app/dev.js +49 -33
  5. package/dist/commands/app/frontend/dev.js +12 -6
  6. package/dist/commands/app/push.d.ts +1 -0
  7. package/dist/commands/app/push.js +3 -3
  8. package/dist/commands/app/release.js +2 -2
  9. package/dist/commands/create/app.d.ts +1 -1
  10. package/dist/commands/create/app.js +1 -1
  11. package/dist/commands/create/frontend.js +3 -3
  12. package/dist/commands/create/tests.js +10 -8
  13. package/dist/commands/inspect/extensions.js +4 -4
  14. package/dist/commands/schema.js +21 -18
  15. package/dist/commands/theme/dev.js +5 -5
  16. package/dist/commands/theme/push.d.ts +1 -0
  17. package/dist/commands/theme/push.js +3 -3
  18. package/dist/create-app-command.js +2 -2
  19. package/dist/create-config-command.d.ts +1 -1
  20. package/dist/create-config-command.js +3 -3
  21. package/dist/help/custom-help.d.ts +1 -1
  22. package/dist/help/types.d.ts +1 -1
  23. package/dist/lib/api.d.ts +1 -0
  24. package/dist/lib/api.js +1 -1
  25. package/dist/lib/app-config.d.ts +13 -8
  26. package/dist/lib/app-config.js +25 -23
  27. package/dist/lib/apps/app-config.d.ts +5 -5
  28. package/dist/lib/apps/app-config.js +53 -37
  29. package/dist/lib/apps/index.d.ts +15 -14
  30. package/dist/lib/apps/index.js +40 -23
  31. package/dist/lib/apps/paths.d.ts +1 -1
  32. package/dist/lib/apps/paths.js +3 -3
  33. package/dist/lib/bundle.js +26 -17
  34. package/dist/lib/config.d.ts +8 -8
  35. package/dist/lib/config.js +8 -10
  36. package/dist/lib/constants.d.ts +9 -9
  37. package/dist/lib/create/content.d.ts +6 -6
  38. package/dist/lib/create/content.js +7 -8
  39. package/dist/lib/create/function.d.ts +5 -6
  40. package/dist/lib/create/function.js +7 -8
  41. package/dist/lib/create/index.d.ts +8 -9
  42. package/dist/lib/create/index.js +7 -8
  43. package/dist/lib/create/model.d.ts +4 -4
  44. package/dist/lib/create/model.js +4 -5
  45. package/dist/lib/create/notification.d.ts +2 -3
  46. package/dist/lib/create/setting.d.ts +4 -4
  47. package/dist/lib/create/setting.js +3 -4
  48. package/dist/lib/create/tests/types.d.ts +3 -4
  49. package/dist/lib/create/tests.d.ts +1 -2
  50. package/dist/lib/create/tests.js +12 -13
  51. package/dist/lib/create/webhook.d.ts +6 -6
  52. package/dist/lib/create/webhook.js +15 -10
  53. package/dist/lib/function-source-analysis.js +3 -3
  54. package/dist/lib/info.d.ts +1 -1
  55. package/dist/lib/logs/index.d.ts +1 -0
  56. package/dist/lib/logs/line-output.d.ts +1 -0
  57. package/dist/lib/logs/line-output.js +12 -4
  58. package/dist/lib/logs/output.d.ts +1 -0
  59. package/dist/lib/logs/table-output.d.ts +1 -0
  60. package/dist/lib/logs/table-output.js +8 -5
  61. package/dist/lib/package-manager.d.ts +5 -5
  62. package/dist/lib/package-manager.js +9 -7
  63. package/dist/lib/proxy.js +4 -3
  64. package/dist/lib/sessions.d.ts +1 -2
  65. package/dist/lib/sessions.js +1 -2
  66. package/dist/lib/stores.d.ts +6 -6
  67. package/dist/lib/stores.js +5 -6
  68. package/dist/lib/swell-function-wrapper.d.ts +1 -193
  69. package/dist/lib/swell-function-wrapper.js +1 -1
  70. package/dist/lib/theme-sync.js +0 -5
  71. package/dist/push-app-command.d.ts +8 -6
  72. package/dist/push-app-command.js +45 -51
  73. package/dist/remote-app-command.d.ts +6 -4
  74. package/dist/remote-app-command.js +39 -25
  75. package/dist/swell-api-command.d.ts +1 -0
  76. package/dist/swell-api-command.js +3 -3
  77. package/dist/types/index.d.ts +94 -0
  78. package/dist/types/index.js +4 -0
  79. package/oclif.manifest.json +1 -1
  80. package/package.json +3 -3
@@ -1,4 +1,5 @@
1
- /// <reference types="node" />
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
2
3
  import type { GlobbyFilterFunction } from 'globby';
3
4
  import * as fs from 'node:fs';
4
5
  import { App, AppConfig, ConfigType, FrontendProjectType } from './lib/apps/index.js';
@@ -26,7 +27,7 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
26
27
  deployAppFrontend(force?: boolean, log?: boolean): Promise<void>;
27
28
  deployFrontend(projectType: FrontendProjectType): Promise<string>;
28
29
  ensureAppExists(file?: string, shouldCreate?: boolean): Promise<boolean>;
29
- ensureLoggedIn(): Promise<true | undefined>;
30
+ ensureLoggedIn(): Promise<void>;
30
31
  exec(command: string, cwd?: string, onOutput?: (string: string) => any | false): Promise<void>;
31
32
  execFrontend(command: string, onOutput?: (string: string) => any | false): Promise<void>;
32
33
  getAllAppStorefronts(params?: {
@@ -46,8 +47,8 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
46
47
  targetPath?: string;
47
48
  }, shouldChoose?: boolean): Promise<App | undefined>;
48
49
  getExistingApp(appId: string): Promise<App>;
49
- getFrontendDeploymentHash(): string | undefined;
50
- getFrontendProjectType(required?: boolean): FrontendProjectType | null;
50
+ getFrontendDeploymentHash(): Promise<string | undefined>;
51
+ getFrontendProjectType(required?: boolean): Promise<FrontendProjectType | null>;
51
52
  getStorefrontToPull(args?: {
52
53
  appId?: string;
53
54
  targetPath?: string;
@@ -66,6 +67,7 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
66
67
  env?: string;
67
68
  'storefront-id'?: string;
68
69
  'storefront-select'?: boolean;
70
+ yes?: boolean;
69
71
  }, defaultStorefront?: any): Promise<void>;
70
72
  handleWatchFileChange(configFile: string, configType: ConfigType, event: WatchFileEvent): Promise<void>;
71
73
  logAppFrontendUrl(): void;
@@ -76,7 +78,7 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
76
78
  logStorefrontFrontendUrl(storefront?: any, branchId?: string): void;
77
79
  pushFile(relativePath: string): Promise<void>;
78
80
  pushFilePath(relativePath: string, force?: boolean): Promise<void>;
79
- resolveAppPath(appId?: string, targetPath?: string): string;
81
+ resolveAppPath(appId?: string, targetPath?: string): Promise<string>;
80
82
  runWatchChangeQueue(): Promise<void>;
81
83
  saveCurrentStorefront(): void;
82
84
  logLocalPreviewUrl(): void;
@@ -88,7 +90,7 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
88
90
  logChanges?: boolean;
89
91
  onChange?: (appConfig?: AppConfig, result?: any) => void;
90
92
  syncAll?: boolean;
91
- }): Promise<void>;
93
+ }): Promise<fs.FSWatcher>;
92
94
  wranglerDeployFrontend(_projectType: FrontendProjectType): Promise<string>;
93
95
  private confirmRemoveInstalledApp;
94
96
  }
@@ -6,7 +6,7 @@ import * as path from 'node:path';
6
6
  import Stream from 'node:stream';
7
7
  import ora from 'ora';
8
8
  import { default as swellConfig } from './lib/app-config.js';
9
- import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, CUSTOM_FRAMEWORK_SLUG, appConfigFromFile, filePathExists, filePathExistsAsync, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getProjectCommands, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
9
+ import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, CUSTOM_FRAMEWORK_SLUG, appConfigFromFile, filePathExistsAsync, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getProjectCommands, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
10
10
  import { getGlobIgnorePathsChecker } from './lib/apps/paths.js';
11
11
  import { slugFromApp } from './lib/apps/slug.js';
12
12
  import { default as localConfig } from './lib/config.js';
@@ -121,7 +121,7 @@ export class PushAppCommand extends RemoteAppCommand {
121
121
  return;
122
122
  }
123
123
  // Get commands transformed for the detected package manager
124
- const { buildCommand } = getProjectCommands(this.appPath, projectType);
124
+ const { buildCommand } = await getProjectCommands(this.appPath, projectType);
125
125
  if (!buildCommand) {
126
126
  // No build command needed for this framework
127
127
  return;
@@ -251,11 +251,11 @@ export class PushAppCommand extends RemoteAppCommand {
251
251
  }
252
252
  async deployAppFrontend(force, log) {
253
253
  const currentStore = localConfig.getDefaultStore();
254
- const projectType = this.getFrontendProjectType(false);
254
+ const projectType = await this.getFrontendProjectType(false);
255
255
  if (!projectType) {
256
256
  return;
257
257
  }
258
- const deploymentHash = this.getFrontendDeploymentHash();
258
+ const deploymentHash = await this.getFrontendDeploymentHash();
259
259
  // Build and deploy only if there are files to push
260
260
  if (deploymentHash &&
261
261
  (force || deploymentHash !== this.app.frontend?.deployment_hash)) {
@@ -335,7 +335,6 @@ export class PushAppCommand extends RemoteAppCommand {
335
335
  this.log('Logging in...\n');
336
336
  await this.config.runCommand('login');
337
337
  this.log();
338
- return true;
339
338
  }
340
339
  }
341
340
  async exec(command, cwd, onOutput) {
@@ -386,25 +385,20 @@ export class PushAppCommand extends RemoteAppCommand {
386
385
  const query = Object.entries(params || {})
387
386
  .map(([key, value]) => `${key}=${value}`)
388
387
  .join('&');
389
- try {
390
- return this.handleRequestErrors(async () => this.api.get({
391
- adminPath: `/apps/${this.app.id}/storefront?${query}`,
392
- }));
393
- }
394
- catch {
395
- // noop
396
- }
388
+ return this.handleRequestErrors(async () => this.api.get({
389
+ adminPath: `/apps/${this.app.id}/storefront?${query}`,
390
+ }));
397
391
  }
398
392
  async getAppToPull(args, shouldChoose = true) {
399
393
  const { appId, targetPath } = args;
400
394
  const klass = this.ctor;
401
395
  const currentStore = localConfig.getDefaultStore();
402
396
  await this.api.setStoreEnv(currentStore, klass.orientation?.env);
403
- this.resolveAppPath(appId, targetPath);
404
- if (isPathDirectory(this.appPath)) {
405
- this.swellConfig = swellConfig(this.appPath);
406
- const localAppId = this.swellConfig.get('id');
407
- const localAppType = this.swellConfig.get('type');
397
+ await this.resolveAppPath(appId, targetPath);
398
+ if (await isPathDirectory(this.appPath)) {
399
+ this.swellConfig = await swellConfig(this.appPath);
400
+ const localAppId = String(this.swellConfig.get('id'));
401
+ const localAppType = String(this.swellConfig.get('type'));
408
402
  if (localAppId) {
409
403
  if (appId && appId !== localAppId) {
410
404
  this.error(`${localAppType === 'theme' ? 'A theme' : 'An app'} already exists in this path with a different ID: ${style.appConfigValue(localAppId)}`);
@@ -418,7 +412,7 @@ export class PushAppCommand extends RemoteAppCommand {
418
412
  ? await this.chooseAppToPull()
419
413
  : undefined;
420
414
  if (shouldChoose) {
421
- this.swellConfig = swellConfig(this.appPath);
415
+ this.swellConfig = await swellConfig(this.appPath);
422
416
  }
423
417
  return app;
424
418
  }
@@ -436,8 +430,8 @@ export class PushAppCommand extends RemoteAppCommand {
436
430
  }
437
431
  }
438
432
  }
439
- getFrontendDeploymentHash() {
440
- const localConfigs = this.getLocalAppConfigs();
433
+ async getFrontendDeploymentHash() {
434
+ const localConfigs = await this.getLocalAppConfigs();
441
435
  let frontendHashes = localConfigs
442
436
  .filter((config) => config.filePath?.startsWith(`frontend${path.sep}`))
443
437
  .map((config) => config.hash)
@@ -449,13 +443,13 @@ export class PushAppCommand extends RemoteAppCommand {
449
443
  }
450
444
  return frontendHashes.length > 0 ? hashString(frontendHashes) : undefined;
451
445
  }
452
- getFrontendProjectType(required = true) {
446
+ async getFrontendProjectType(required = true) {
453
447
  this.frontendPath = path.join(this.appPath, 'frontend');
454
- const frontendExists = filePathExists(this.frontendPath);
448
+ const frontendExists = await filePathExistsAsync(this.frontendPath);
455
449
  if (!required && !frontendExists) {
456
450
  return null;
457
451
  }
458
- const projectType = getFrontendProjectType(this.appPath);
452
+ const projectType = await getFrontendProjectType(this.appPath);
459
453
  if (!projectType) {
460
454
  // If frontend not required, just return null (don't error)
461
455
  if (!required) {
@@ -475,10 +469,10 @@ export class PushAppCommand extends RemoteAppCommand {
475
469
  const { appId, targetPath } = args;
476
470
  const targetStorefront = flags['storefront-id'];
477
471
  const targetApp = await this.getAppToPull(args, false);
478
- this.resolveAppPath(appId, targetPath);
472
+ await this.resolveAppPath(appId, targetPath);
479
473
  // Only use default if path exists
480
474
  let defaultStorefront;
481
- if (isPathDirectory(this.appPath)) {
475
+ if (await isPathDirectory(this.appPath)) {
482
476
  defaultStorefront = localConfig.getDefaultStorefront(this.appPath);
483
477
  }
484
478
  await this.setStorefrontEnv(flags, defaultStorefront);
@@ -501,7 +495,7 @@ export class PushAppCommand extends RemoteAppCommand {
501
495
  const appId = this.appType === 'theme'
502
496
  ? this.storefront.theme_app_id
503
497
  : this.storefront.app_id;
504
- this.resolveAppPath(this.storefront.name, targetPath);
498
+ await this.resolveAppPath(this.storefront.name, targetPath);
505
499
  this.app = await this.getApp(appId);
506
500
  }
507
501
  else {
@@ -509,7 +503,7 @@ export class PushAppCommand extends RemoteAppCommand {
509
503
  // However it's a good fallback in case the storefront was deleted
510
504
  // Choosing a theme app will proceed to pull app files
511
505
  this.app = await this.chooseAppToPull();
512
- this.resolveAppPath(this.app.name, targetPath);
506
+ await this.resolveAppPath(this.app.name, targetPath);
513
507
  }
514
508
  }
515
509
  else {
@@ -528,7 +522,7 @@ export class PushAppCommand extends RemoteAppCommand {
528
522
  : this.storefront.app_id;
529
523
  this.app = await this.getApp(appId);
530
524
  }
531
- this.swellConfig = swellConfig(this.appPath);
525
+ this.swellConfig = await swellConfig(this.appPath);
532
526
  return this.storefront;
533
527
  }
534
528
  async getStorefrontToPush(flags = {}) {
@@ -600,6 +594,7 @@ export class PushAppCommand extends RemoteAppCommand {
600
594
  async setStorefrontEnv(flags, defaultStorefront) {
601
595
  const currentStore = localConfig.getDefaultStore();
602
596
  const targetStorefront = flags['storefront-id'];
597
+ const nonInteractive = flags.yes || false;
603
598
  if (defaultStorefront &&
604
599
  !targetStorefront &&
605
600
  !flags['storefront-id'] &&
@@ -615,7 +610,7 @@ export class PushAppCommand extends RemoteAppCommand {
615
610
  else {
616
611
  const hasTestEnv = await this.api.isTestEnvEnabled();
617
612
  if (hasTestEnv) {
618
- const env = await selectEnvironmentId();
613
+ const env = nonInteractive ? 'test' : await selectEnvironmentId();
619
614
  await this.api.setStoreEnv(currentStore, env);
620
615
  }
621
616
  }
@@ -636,7 +631,7 @@ export class PushAppCommand extends RemoteAppCommand {
636
631
  break;
637
632
  }
638
633
  default: {
639
- config = appConfigFromFile(configFile, configType, this.appPath);
634
+ config = await appConfigFromFile(configFile, configType, this.appPath);
640
635
  this.watchingChangeQueue.set(configFile, {
641
636
  action: 'push',
642
637
  appConfig: config,
@@ -682,19 +677,21 @@ export class PushAppCommand extends RemoteAppCommand {
682
677
  async pushFile(relativePath) {
683
678
  const basePath = relativePath.split(path.sep)[0];
684
679
  const configType = getConfigTypeFromPath(basePath) || ConfigType.FILE;
685
- const config = appConfigFromFile(relativePath, configType, this.appPath);
680
+ const config = await appConfigFromFile(relativePath, configType, this.appPath);
686
681
  await this.pushRemoteFile(config);
687
682
  }
688
683
  async pushFilePath(relativePath, force) {
689
684
  const topDir = relativePath.split(path.sep)[0];
690
- const configType = (getConfigTypeFromPath(topDir) || 'file');
691
- const configTypeKey = getConfigTypeKeyFromValue(configType) || 'file';
685
+ const configType = getConfigTypeFromPath(topDir) || ConfigType.FILE;
686
+ const configTypeKey = getConfigTypeKeyFromValue(configType) || ConfigType.FILE;
692
687
  const exAppConfigs = (this.app?.configs || []).filter((config) => config.filePath?.startsWith(`${relativePath}/`));
693
- const allFiles = [];
694
- const pushFiles = [];
688
+ const promises = [];
695
689
  for (const { configFile } of allConfigFilesInDir(this.appPath, relativePath, configTypeKey)) {
696
- const config = appConfigFromFile(configFile, configType, this.appPath);
697
- allFiles.push(config);
690
+ promises.push(appConfigFromFile(configFile, configType, this.appPath));
691
+ }
692
+ const allFiles = await Promise.all(promises);
693
+ const pushFiles = [];
694
+ for (const config of allFiles) {
698
695
  const exConfig = exAppConfigs.find((exConfig) => config.filePath === exConfig.filePath);
699
696
  if (!force && exConfig && exConfig.hash === config.hash) {
700
697
  continue;
@@ -709,18 +706,16 @@ export class PushAppCommand extends RemoteAppCommand {
709
706
  // eslint-disable-next-line no-await-in-loop
710
707
  await Promise.all(pushFiles
711
708
  .splice(0, pushConcurrency)
712
- .map(async (appConfig) => {
713
- await this.pushRemoteFile(appConfig);
714
- }));
709
+ .map((appConfig) => this.pushRemoteFile(appConfig)));
715
710
  }
716
711
  }
717
712
  // Remove any deleted files
718
713
  if (filesToRemove.length > 0) {
719
714
  while (filesToRemove.length > 0) {
720
715
  // eslint-disable-next-line no-await-in-loop
721
- await Promise.all(filesToRemove.splice(0, PUSH_CONCURRENCY).map(async (appConfig) => {
722
- await this.removeRemoteFile(appConfig);
723
- }));
716
+ await Promise.all(filesToRemove
717
+ .splice(0, PUSH_CONCURRENCY)
718
+ .map((appConfig) => this.removeRemoteFile(appConfig)));
724
719
  }
725
720
  }
726
721
  if (!hasChanges && !force) {
@@ -731,12 +726,12 @@ export class PushAppCommand extends RemoteAppCommand {
731
726
  this.log(`${style.success('✔')} Pushed ${style.path(relativePath)} to ${style.appConfigValue(this.app.name)}.`);
732
727
  }
733
728
  }
734
- resolveAppPath(appId, targetPath) {
729
+ async resolveAppPath(appId, targetPath) {
735
730
  const resolvedAppId = appId || '';
736
731
  let resolvedPath = targetPath || resolvedAppId;
737
732
  // If app_id matches directory, use it as target path
738
733
  if (!targetPath &&
739
- isPathDirectory(path.resolve(process.cwd(), resolvedAppId))) {
734
+ (await isPathDirectory(path.resolve(process.cwd(), resolvedAppId)))) {
740
735
  resolvedPath = resolvedAppId;
741
736
  }
742
737
  // Reset app path if appId is provided
@@ -765,7 +760,7 @@ export class PushAppCommand extends RemoteAppCommand {
765
760
  }));
766
761
  }
767
762
  catch {
768
- // noop
763
+ // TODO: Ignore only some errors
769
764
  }
770
765
  }
771
766
  /* eslint-enable no-await-in-loop */
@@ -848,13 +843,12 @@ export class PushAppCommand extends RemoteAppCommand {
848
843
  await this.setWatchingFiles();
849
844
  if (syncAll) {
850
845
  await this.pushAppConfigs();
851
- fs.watch(this.appPath, { recursive: true }, async (_eventType, _filename) => {
846
+ return fs.watch(this.appPath, { recursive: true }, async (_eventType, _filename) => {
852
847
  await this.pushAppConfigs();
853
848
  await this.setWatchingFiles();
854
849
  });
855
- return;
856
850
  }
857
- fs.watch(this.appPath, { recursive: true }, this.watchListener);
851
+ return fs.watch(this.appPath, { recursive: true }, this.watchListener);
858
852
  }
859
853
  async wranglerDeployFrontend(_projectType) {
860
854
  let deploymentUrl;
@@ -863,7 +857,7 @@ export class PushAppCommand extends RemoteAppCommand {
863
857
  let outputBuffer = '';
864
858
  this.log(`\nDeploying to Cloudflare...\n`);
865
859
  // Get the wrangler deploy command for the detected package manager
866
- const pm = detectPackageManager(this.appPath);
860
+ const pm = await detectPackageManager(this.appPath);
867
861
  const deployCommand = transformCommand('npx wrangler deploy', pm);
868
862
  try {
869
863
  await this.execFrontend(deployCommand, (string) => {
@@ -1,3 +1,5 @@
1
+ import { type Ora } from 'ora';
2
+ import type { AppCommandOrientation } from './types/index.js';
1
3
  import { AppCommand } from './app-command.js';
2
4
  import { App, AppConfig, AppVersion } from './lib/apps/index.js';
3
5
  /**
@@ -24,12 +26,12 @@ export declare abstract class RemoteAppCommand extends AppCommand {
24
26
  getConfigData(configId: string): Promise<any>;
25
27
  protected getCreateUpdateApp(updateApp?: App | null): Promise<App>;
26
28
  protected getInstalledApp(): Promise<any>;
27
- protected getLocalAppConfigs(): AppConfig[];
29
+ protected getLocalAppConfigs(): Promise<AppConfig[]>;
28
30
  protected getVersion(version?: null | string | undefined): Promise<AppVersion>;
29
31
  protected getVersions(version?: null | string | undefined): Promise<AppVersion[]>;
30
32
  protected handleRequestErrors(request: () => any, spinnerError?: () => void): Promise<any>;
31
33
  init(): Promise<void>;
32
- protected installApp(version: string, spinner?: any): Promise<any>;
34
+ protected installApp(version: string, spinner?: Ora): Promise<any>;
33
35
  isConfigDeprecated(config: AppConfig): boolean;
34
36
  localFrontendUrl(storeId: string, localId: string): string;
35
37
  logOrientation(orientation?: AppCommandOrientation): Promise<void>;
@@ -51,8 +53,8 @@ export declare abstract class RemoteAppCommand extends AppCommand {
51
53
  storefrontFrontendUrl(storeId: string, storefront?: any, branchId?: string, preview?: boolean): string;
52
54
  protected swellRcPath(): string;
53
55
  protected timestampStyled(): string;
54
- protected updateInstalledApp(version: string, spinner?: any): Promise<void>;
55
- protected updateVersion(version: string, body: any, spinner?: any): Promise<void>;
56
+ protected updateInstalledApp(version: string, spinner?: Ora): Promise<void>;
57
+ protected updateVersion(version: string, body: any, spinner?: Ora): Promise<void>;
56
58
  /**
57
59
  * Reads the .swellrc file and sets up the remote app configuration.
58
60
  *
@@ -5,7 +5,7 @@ import * as path from 'node:path';
5
5
  import { default as ora } from 'ora';
6
6
  import { AppCommand } from './app-command.js';
7
7
  import { readRcFile, writeRcFile } from './lib/app-config.js';
8
- import { AppConfig, FunctionProcessingError, IgnoringFileError, SwellJsonFields, allBaseFilesInDir, allConfigFilesPaths, appConfigFromFile, batchAppFilesByType, deleteFile, hashFile, writeFile, writeJsonFile, PUSH_CONCURRENCY, } from './lib/apps/index.js';
8
+ import { AppConfig, ConfigType, FunctionProcessingError, IgnoringFileError, SwellJsonFields, allBaseFilesInDir, allConfigFilesPaths, appConfigFromFile, batchAppFilesByType, deleteFile, hashFile, writeFile, writeJsonFile, PUSH_CONCURRENCY, } from './lib/apps/index.js';
9
9
  import { default as localConfig } from './lib/config.js';
10
10
  import { getAppFrontendHost, getLocalFrontendHost, getLoginHost, getStorefrontFrontendHost, } from './lib/constants.js';
11
11
  import { toAppId } from './lib/create/index.js';
@@ -78,20 +78,21 @@ export class RemoteAppCommand extends AppCommand {
78
78
  },
79
79
  });
80
80
  // Instantiate AppConfig for each object
81
- app.configs = (configs.results || []).map((config) => AppConfig.create({
81
+ app.configs = await Promise.all((configs.results || []).map((config) => AppConfig.create({
82
82
  ...config,
83
83
  appPath: this.appPath,
84
- }));
84
+ })));
85
85
  // Pull app configs to compare with theme for push/pull
86
86
  if (themeId && this.themeSyncApp) {
87
87
  const appConfigs = await this.api.get({ adminPath: `/apps/${id}/configs` }, { query: { limit: 1000 } });
88
+ const promises = [];
88
89
  for (const appConfig of appConfigs.results || []) {
89
90
  const themeConfig = app.configs.find((config) => config.filePath === appConfig.file_path);
90
91
  if (themeConfig && themeConfig.fileHash !== appConfig.hash) {
91
92
  themeConfig.hash = appConfig.hash;
92
93
  }
93
94
  else if (!themeConfig && appConfig) {
94
- app.configs.push(AppConfig.create({
95
+ promises.push(AppConfig.create({
95
96
  ...appConfig,
96
97
  appConfigId: appConfig.id,
97
98
  appPath: this.appPath,
@@ -99,6 +100,10 @@ export class RemoteAppCommand extends AppCommand {
99
100
  }));
100
101
  }
101
102
  }
103
+ if (promises.length > 0) {
104
+ const list = await Promise.all(promises);
105
+ app.configs.push(...list);
106
+ }
102
107
  for (const themeConfig of app.configs) {
103
108
  const appConfig = appConfigs.results.find((config) => themeConfig.filePath === config.file_path);
104
109
  if (!appConfig) {
@@ -178,7 +183,7 @@ export class RemoteAppCommand extends AppCommand {
178
183
  throw error;
179
184
  }
180
185
  }
181
- if (!this.swellConfig.store.id) {
186
+ if (!this.swellConfig.store?.id) {
182
187
  throw new Error('App swell.json is missing or `id` is not defined.');
183
188
  }
184
189
  const body = {
@@ -244,17 +249,15 @@ export class RemoteAppCommand extends AppCommand {
244
249
  // check if app is already installed on store
245
250
  return this.api.get({ adminPath: `/client/apps/${this.app.id}` }, {});
246
251
  }
247
- getLocalAppConfigs() {
248
- const localConfigs = [];
252
+ async getLocalAppConfigs() {
253
+ const promises = [];
249
254
  for (const { configFile, configType } of allConfigFilesPaths(this.appPath)) {
250
- const config = appConfigFromFile(configFile, configType, this.appPath);
251
- localConfigs.push(config);
255
+ promises.push(appConfigFromFile(configFile, configType, this.appPath));
252
256
  }
253
257
  for (const filePath of allBaseFilesInDir(this.appPath)) {
254
- const config = appConfigFromFile(filePath, 'file', this.appPath);
255
- localConfigs.push(config);
258
+ promises.push(appConfigFromFile(filePath, ConfigType.FILE, this.appPath));
256
259
  }
257
- return localConfigs;
260
+ return Promise.all(promises);
258
261
  }
259
262
  async getVersion(version) {
260
263
  const versionsResponse = await this.api.get({ adminPath: `/apps/${this.app.id}/versions` }, {
@@ -289,8 +292,11 @@ export class RemoteAppCommand extends AppCommand {
289
292
  try {
290
293
  jsonError = JSON.parse(error.message);
291
294
  }
292
- catch {
293
- // noop, likely JSON parse error
295
+ catch (error) {
296
+ // Skip JSON parse error
297
+ if (!(error instanceof SyntaxError)) {
298
+ throw error;
299
+ }
294
300
  }
295
301
  if (!showedErrors) {
296
302
  if (jsonError && jsonError.error) {
@@ -312,8 +318,8 @@ export class RemoteAppCommand extends AppCommand {
312
318
  await this.logOrientation();
313
319
  }
314
320
  }
315
- async installApp(version, spinner) {
316
- return this.api.post({ adminPath: `/client/apps` }, {
321
+ installApp(version, spinner) {
322
+ return this.api.post({ adminPath: '/client/apps' }, {
317
323
  body: { app_id: this.app.id, version },
318
324
  onAsyncGetPath: (response) => ({
319
325
  adminPath: `/client/app-async-status/${response.app_id}`,
@@ -352,8 +358,10 @@ export class RemoteAppCommand extends AppCommand {
352
358
  async pullAppConfigs(force, confirmRemove = true) {
353
359
  const pulled = [];
354
360
  const removed = [];
355
- const localConfigs = this.getLocalAppConfigs();
356
- const remoteConfigs = await this.cleanRemoteConfigs(await this.pullAppJsonAndRemoteConfigs(), false);
361
+ const [localConfigs, remoteConfigs] = await Promise.all([
362
+ this.getLocalAppConfigs(),
363
+ this.pullAppJsonAndRemoteConfigs().then((remoteConfigs) => this.cleanRemoteConfigs(remoteConfigs, false)),
364
+ ]);
357
365
  const batches = batchAppFilesByType(remoteConfigs);
358
366
  for (const batch of batches) {
359
367
  const remoteBatchConfigs = remoteConfigs.filter((config) => config.type === batch.type);
@@ -364,13 +372,15 @@ export class RemoteAppCommand extends AppCommand {
364
372
  try {
365
373
  const localConfig = localBatchConfigs.find((config) => config.filePath === batchConfig.filePath);
366
374
  if (localConfig) {
367
- const hash = hashFile(batchConfig.appPath, undefined, batchConfig.filePath);
375
+ const hash = hashFile(batchConfig.appPath, localConfig.fileData, batchConfig.filePath);
368
376
  if (hash === batchConfig.hash) {
369
377
  return false;
370
378
  }
371
379
  }
372
380
  }
373
- catch { }
381
+ catch {
382
+ // TODO: Ignore only some errors
383
+ }
374
384
  return true;
375
385
  });
376
386
  const filesToRemove = localBatchConfigs.filter((config) => !batch.configs.some((batchConfig) => batchConfig.filePath === config.filePath));
@@ -439,7 +449,7 @@ export class RemoteAppCommand extends AppCommand {
439
449
  swellConfigJson.id = toAppId(this.app.private_id);
440
450
  await writeJsonFile(path.join(this.appPath, 'swell.json'), swellConfigJson);
441
451
  }
442
- const swellConfig = appConfigFromFile('swell.json', 'file', this.appPath);
452
+ const swellConfig = await appConfigFromFile('swell.json', ConfigType.FILE, this.appPath);
443
453
  swellConfig.id = swellJson?.id;
444
454
  return [swellConfig, ...allOtherConfigs];
445
455
  }
@@ -460,8 +470,10 @@ export class RemoteAppCommand extends AppCommand {
460
470
  async pushAppConfigs(force) {
461
471
  const pushed = [];
462
472
  const removed = [];
463
- const localConfigs = this.getLocalAppConfigs();
464
- const remoteConfigs = await this.cleanRemoteConfigs(this.app?.configs || []);
473
+ const [localConfigs, remoteConfigs] = await Promise.all([
474
+ this.getLocalAppConfigs(),
475
+ this.cleanRemoteConfigs(this.app?.configs || []),
476
+ ]);
465
477
  // Do not allow more than 10K files
466
478
  if (localConfigs.length > 10000) {
467
479
  this.error(`Apps only support up to 10,000 files. Found ${localConfigs.length} files.`);
@@ -470,8 +482,10 @@ export class RemoteAppCommand extends AppCommand {
470
482
  for (const batch of batches) {
471
483
  const remoteBatchConfigs = remoteConfigs.filter((config) => config.type === batch.type);
472
484
  const pushFiles = batch.configs.filter((batchConfig) => {
485
+ if (force)
486
+ return true;
473
487
  const remoteConfig = remoteBatchConfigs.find((config) => batchConfig.filePath === config.filePath);
474
- return Boolean(force || !remoteConfig || remoteConfig.hash !== batchConfig.hash);
488
+ return Boolean(!remoteConfig || remoteConfig.hash !== batchConfig.hash);
475
489
  });
476
490
  const filesToRemove = remoteBatchConfigs.filter((config) => !batch.configs.some((batchConfig) => batchConfig.filePath === config.filePath));
477
491
  // Skip if there are no config changes
@@ -538,7 +552,7 @@ export class RemoteAppCommand extends AppCommand {
538
552
  }
539
553
  if (error instanceof FunctionProcessingError) {
540
554
  log &&
541
- spinner.fail(`${error.message} ${error.originalError?.message?.toLowerCase()}`);
555
+ spinner.fail(`${error.message} ${error.cause?.message?.toLowerCase()}`);
542
556
  return;
543
557
  }
544
558
  log && spinner.fail(error.message);
@@ -1,3 +1,4 @@
1
+ import type Api from './types/index.js';
1
2
  import { HttpMethod } from './lib/api.js';
2
3
  import { SwellCommand } from './swell-command.js';
3
4
  export declare const headerFlag: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
@@ -1,6 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
- import * as fs from 'node:fs';
3
- import path from 'node:path';
2
+ import * as fs from 'node:fs/promises';
3
+ import * as path from 'node:path';
4
4
  import { FetchError } from 'node-fetch';
5
5
  import { HttpMethod } from './lib/api.js';
6
6
  import { resolveAppId } from './lib/apps/resolve.js';
@@ -168,7 +168,7 @@ export class SwellApiCommand extends SwellCommand {
168
168
  return;
169
169
  }
170
170
  const bodyData = this.isFilePath(body)
171
- ? await fs.promises.readFile(path.resolve(body), 'utf8')
171
+ ? await fs.readFile(path.resolve(body), 'utf8')
172
172
  : body;
173
173
  return JSON.parse(bodyData);
174
174
  }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @fileoverview Type definitions for the application.
3
+ */
4
+ import type { default as Conf } from 'conf';
5
+ declare namespace Api {
6
+ export interface AuthenticationHeaders {
7
+ publicKey?: string;
8
+ secretKey?: string;
9
+ sessionId?: string;
10
+ }
11
+ /**
12
+ * The paths to the API endpoints. Typically a Command will define the
13
+ * path to the API endpoint it needs.
14
+ */
15
+ export interface Paths {
16
+ adminPath?: string;
17
+ backendPath?: string;
18
+ frontendPath?: string;
19
+ }
20
+ type RequestInit = import('node-fetch').RequestInit;
21
+ /**
22
+ * The options to pass to our generic API request function. This extends
23
+ * the built-in fetch RequestInit interface.
24
+ */
25
+ export interface RequestOptions extends RequestInit {
26
+ body?: any;
27
+ headers?: Record<string, string>;
28
+ onAsyncGetPath?: (response: any) => Paths;
29
+ query?: any;
30
+ sessionId?: string;
31
+ spinner?: any;
32
+ rawResponse?: boolean;
33
+ }
34
+ export {};
35
+ }
36
+ export default Api;
37
+ /**
38
+ * The configuration of an app as defined by the developer.
39
+ */
40
+ export interface LocalApp extends Conf<App> {
41
+ swDirPath: string;
42
+ }
43
+ export interface AppCommandOrientation {
44
+ env?: string;
45
+ storeId?: string;
46
+ }
47
+ /**
48
+ * A log item as defined by the Swell API.
49
+ */
50
+ export interface LogItem {
51
+ app: string;
52
+ data: string;
53
+ date: string;
54
+ env: string;
55
+ ip: string;
56
+ req: string;
57
+ request: string;
58
+ status: string;
59
+ time: string;
60
+ type: string;
61
+ }
62
+ export interface Store {
63
+ name: string;
64
+ email: string;
65
+ sessionId: string;
66
+ storeId: string;
67
+ }
68
+ export interface User {
69
+ email: string;
70
+ name: string;
71
+ }
72
+ export interface UserAuthorization {
73
+ client_id: string;
74
+ client_name: string;
75
+ }
76
+ export interface App {
77
+ id: string;
78
+ name: string;
79
+ type: string;
80
+ private_id: string;
81
+ public_id?: string;
82
+ }
83
+ export interface Model {
84
+ name: string;
85
+ namespace?: string;
86
+ app_id?: string;
87
+ deprecated?: boolean;
88
+ fields: {
89
+ [key: string]: {
90
+ name: string;
91
+ type: string;
92
+ };
93
+ };
94
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @fileoverview Type definitions for the application.
3
+ */
4
+ export {};
@@ -3556,5 +3556,5 @@
3556
3556
  ]
3557
3557
  }
3558
3558
  },
3559
- "version": "2.9.3"
3559
+ "version": "2.9.5"
3560
3560
  }