@swell/cli 2.0.1-alpha.16 → 2.0.1-alpha.18

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 (86) hide show
  1. package/dist/app-command.d.ts +1 -1
  2. package/dist/app-command.js +9 -9
  3. package/dist/commands/app/dev.d.ts +16 -0
  4. package/dist/commands/app/dev.js +28 -0
  5. package/dist/commands/app/frontend/deploy.d.ts +15 -0
  6. package/dist/commands/app/frontend/deploy.js +41 -0
  7. package/dist/commands/app/frontend/dev.d.ts +19 -0
  8. package/dist/commands/app/frontend/dev.js +108 -0
  9. package/dist/commands/app/info.js +14 -9
  10. package/dist/commands/app/init.d.ts +12 -0
  11. package/dist/commands/app/init.js +34 -0
  12. package/dist/commands/app/install.d.ts +0 -3
  13. package/dist/commands/app/install.js +12 -21
  14. package/dist/commands/app/pull.d.ts +15 -0
  15. package/dist/commands/app/pull.js +50 -0
  16. package/dist/commands/app/push.d.ts +9 -12
  17. package/dist/commands/app/push.js +57 -131
  18. package/dist/commands/app/release.d.ts +13 -0
  19. package/dist/commands/app/release.js +148 -0
  20. package/dist/commands/app/version.d.ts +5 -4
  21. package/dist/commands/app/version.js +60 -39
  22. package/dist/commands/app/watch.d.ts +2 -4
  23. package/dist/commands/app/watch.js +3 -83
  24. package/dist/commands/create/app.d.ts +35 -11
  25. package/dist/commands/create/app.js +131 -128
  26. package/dist/commands/create/frontend.d.ts +12 -0
  27. package/dist/commands/create/frontend.js +38 -0
  28. package/dist/commands/create/function.d.ts +2 -2
  29. package/dist/commands/create/function.js +2 -2
  30. package/dist/commands/create/notification.d.ts +2 -2
  31. package/dist/commands/create/notification.js +2 -2
  32. package/dist/commands/env.d.ts +12 -0
  33. package/dist/commands/env.js +31 -0
  34. package/dist/commands/login.js +7 -7
  35. package/dist/commands/logs.js +0 -3
  36. package/dist/commands/theme/dev.d.ts +21 -0
  37. package/dist/commands/theme/dev.js +164 -0
  38. package/dist/commands/theme/init.d.ts +9 -0
  39. package/dist/commands/theme/init.js +15 -0
  40. package/dist/commands/theme/pull.d.ts +20 -0
  41. package/dist/commands/theme/pull.js +65 -0
  42. package/dist/commands/theme/push.d.ts +19 -0
  43. package/dist/commands/theme/push.js +89 -0
  44. package/dist/create-app-command.d.ts +20 -0
  45. package/dist/create-app-command.js +253 -0
  46. package/dist/create-collection-command.d.ts +3 -3
  47. package/dist/create-collection-command.js +4 -4
  48. package/dist/{create-command.d.ts → create-config-command.d.ts} +1 -1
  49. package/dist/{create-command.js → create-config-command.js} +3 -5
  50. package/dist/env/local.d.ts +8 -0
  51. package/dist/env/local.js +7 -0
  52. package/dist/env/production.d.ts +8 -0
  53. package/dist/env/production.js +7 -0
  54. package/dist/env/review.d.ts +8 -0
  55. package/dist/env/review.js +7 -0
  56. package/dist/env/staging.d.ts +8 -0
  57. package/dist/env/staging.js +7 -0
  58. package/dist/lib/api.d.ts +1 -0
  59. package/dist/lib/api.js +39 -13
  60. package/dist/lib/app-config.d.ts +12 -5
  61. package/dist/lib/app-config.js +57 -21
  62. package/dist/lib/apps/app-config.d.ts +17 -3
  63. package/dist/lib/apps/app-config.js +194 -66
  64. package/dist/lib/apps/index.d.ts +92 -12
  65. package/dist/lib/apps/index.js +145 -47
  66. package/dist/lib/apps/paths.d.ts +14 -7
  67. package/dist/lib/apps/paths.js +73 -28
  68. package/dist/lib/config.d.ts +9 -2
  69. package/dist/lib/config.js +49 -1
  70. package/dist/lib/constants.d.ts +5 -0
  71. package/dist/lib/constants.js +23 -4
  72. package/dist/lib/create/content.d.ts +1 -1
  73. package/dist/lib/create/index.js +5 -1
  74. package/dist/lib/info.js +2 -2
  75. package/dist/lib/stores.d.ts +5 -3
  76. package/dist/lib/stores.js +7 -5
  77. package/dist/lib/style.d.ts +2 -0
  78. package/dist/lib/style.js +2 -0
  79. package/dist/push-app-command.d.ts +73 -0
  80. package/dist/push-app-command.js +754 -0
  81. package/dist/remote-app-command.d.ts +35 -9
  82. package/dist/remote-app-command.js +417 -159
  83. package/oclif.manifest.json +817 -71
  84. package/package.json +13 -1
  85. package/dist/commands/app/_pull.d.ts +0 -8
  86. package/dist/commands/app/_pull.js +0 -45
@@ -0,0 +1,253 @@
1
+ import { confirm, select } from '@inquirer/prompts';
2
+ import { Flags } from '@oclif/core';
3
+ import { $ } from 'execa';
4
+ import { exec } from 'node:child_process';
5
+ import path from 'node:path';
6
+ import Stream from 'node:stream';
7
+ import { promisify } from 'node:util';
8
+ import ora from 'ora';
9
+ import Api from './lib/api.js';
10
+ import { FrontendProjectTypes, getAllConfigPaths, writeFile, writeJsonFile, } from './lib/apps/index.js';
11
+ import style from './lib/style.js';
12
+ import { SwellCommand } from './swell-command.js';
13
+ const execAsync = promisify(exec);
14
+ export class CreateAppCommand extends SwellCommand {
15
+ static baseFlags = {
16
+ frontend: Flags.string({
17
+ description: `create a starter framework for a hosted frontend`,
18
+ options: FrontendProjectTypes.map(({ slug }) => slug),
19
+ }),
20
+ 'storefront-app': Flags.string({
21
+ description: `id of an installed storefront app to create a theme for, if applicable`,
22
+ }),
23
+ yes: Flags.boolean({
24
+ char: 'y',
25
+ description: `accept all default values`,
26
+ }),
27
+ };
28
+ devApi;
29
+ constructor(argv, config) {
30
+ super(argv, config);
31
+ this.devApi = new Api(undefined, 'test');
32
+ }
33
+ async createAppConfigFolders(swellConfig) {
34
+ for (const type of getAllConfigPaths(swellConfig.get('type'))) {
35
+ // Create a config folder
36
+ // eslint-disable-next-line no-await-in-loop
37
+ await execAsync(`mkdir -p ${type}`, {
38
+ cwd: path.dirname(swellConfig.path),
39
+ });
40
+ }
41
+ }
42
+ async createFrontendApp(swellConfig, flags, directCreate, kind) {
43
+ const spinner = ora();
44
+ const configPath = path.dirname(swellConfig.path);
45
+ const frameworkType = flags.frontend ||
46
+ (await select({
47
+ choices: [
48
+ ...FrontendProjectTypes.map(({ name, slug }) => ({
49
+ name,
50
+ value: slug,
51
+ })),
52
+ ...(directCreate
53
+ ? []
54
+ : [
55
+ {
56
+ name: 'Do not install a starter framework',
57
+ value: null,
58
+ },
59
+ ]),
60
+ ],
61
+ message: directCreate
62
+ ? `Choose a framework for your hosted${kind ? ` ${kind} ` : ' '}app frontend`
63
+ : `Install a framework for your hosted${kind ? ` ${kind} ` : ' '}app frontend?`,
64
+ }));
65
+ if (frameworkType) {
66
+ const projectType = FrontendProjectTypes.find((type) => type.slug === frameworkType);
67
+ if (!projectType) {
68
+ this.error(`Could not find project type: ${frameworkType}`);
69
+ }
70
+ this.log();
71
+ spinner.start(`Creating ${projectType?.name} frontend app (this may take a while)...`);
72
+ try {
73
+ await execAsync(`mkdir -p frontend`, {
74
+ cwd: configPath,
75
+ });
76
+ await execAsync(projectType.installCommand, {
77
+ cwd: configPath,
78
+ });
79
+ // Use this command to debug output, i.e.e when command becomes non-responsive
80
+ /* await this.execWithStdio(
81
+ configPath,
82
+ projectType.installCommand,
83
+ ); */
84
+ }
85
+ catch (error) {
86
+ spinner.fail(`Error creating ${projectType?.name} app:\n`);
87
+ this.log(error.stdout);
88
+ this.log();
89
+ return false;
90
+ }
91
+ spinner.succeed(`${projectType?.name} initialized app in ${configPath}/frontend/`);
92
+ if (directCreate) {
93
+ this.log();
94
+ }
95
+ return true;
96
+ }
97
+ return false;
98
+ }
99
+ async createStorefrontApp(swellConfig, flags, directCreate) {
100
+ return this.createFrontendApp(swellConfig, flags, directCreate, 'storefront');
101
+ }
102
+ async createThemeApp(config, flags, installedStorefrontApp) {
103
+ const spinner = ora();
104
+ const configPath = path.dirname(config.path);
105
+ const defaultThemeConfigs = await this.devApi.get({
106
+ adminPath: `/client/apps/${installedStorefrontApp.app_id}/theme-template-configs`,
107
+ });
108
+ if (defaultThemeConfigs.results?.length > 0) {
109
+ const { name: appName, version: appVersion } = installedStorefrontApp.app;
110
+ const shouldCreate = flags.yes ||
111
+ (await confirm({
112
+ message: `Create a theme template for ${style.appConfigValue(appName)} ${appVersion ? `v${appVersion}` : ''}?`,
113
+ }));
114
+ if (shouldCreate) {
115
+ this.log();
116
+ spinner.start(`Creating theme template...`);
117
+ try {
118
+ await execAsync(`mkdir -p theme`, {
119
+ cwd: configPath,
120
+ });
121
+ for (const themeConfig of defaultThemeConfigs.results) {
122
+ const filePath = themeConfig.file_path.replace(/^frontend\/theme-template\//, '');
123
+ const fileContent = themeConfig.file_data;
124
+ const isJson = filePath.endsWith('.json');
125
+ // eslint-disable-next-line no-await-in-loop
126
+ await execAsync(`mkdir -p ${path.dirname(filePath)}`, {
127
+ cwd: configPath,
128
+ });
129
+ // eslint-disable-next-line no-await-in-loop
130
+ await (isJson
131
+ ? writeJsonFile(path.join(configPath, filePath), fileContent)
132
+ : writeFile(path.join(configPath, filePath), fileContent));
133
+ }
134
+ }
135
+ catch (error) {
136
+ spinner.fail(`Error creating theme template:\n`);
137
+ this.log(error.stdout);
138
+ this.log();
139
+ return false;
140
+ }
141
+ spinner.succeed(`Theme template initialized in ${configPath}/theme/`);
142
+ }
143
+ }
144
+ return true;
145
+ }
146
+ async doesPackageManagerExist(packageManager) {
147
+ try {
148
+ await execAsync(`${packageManager} --version`);
149
+ return true;
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ }
155
+ async execWithStdio(cwd, command, onOutput) {
156
+ const $$ = $({
157
+ cwd,
158
+ shell: true,
159
+ stderr: 'inherit',
160
+ stdin: 'inherit',
161
+ stdout: 'pipe',
162
+ });
163
+ const outStream = new Stream.Writable();
164
+ outStream._write = (chunk, _encoding, next) => {
165
+ const string = chunk.toString();
166
+ const out = onOutput?.(string);
167
+ if (out !== false) {
168
+ console.log(string);
169
+ }
170
+ next();
171
+ };
172
+ try {
173
+ await $$ `${command}`.pipeStdout?.(outStream);
174
+ }
175
+ catch (error) {
176
+ console.log(error);
177
+ }
178
+ }
179
+ async findPackageManager(pkg = '') {
180
+ const packageManager = pkg;
181
+ // Determine if system has yarn or npm install
182
+ if (packageManager) {
183
+ if (await this.doesPackageManagerExist(packageManager)) {
184
+ return packageManager;
185
+ }
186
+ }
187
+ else {
188
+ if (await this.doesPackageManagerExist('npm')) {
189
+ return 'npm';
190
+ }
191
+ if (await this.doesPackageManagerExist('yarn')) {
192
+ return 'yarn';
193
+ }
194
+ }
195
+ }
196
+ async getInstalledStorefrontApps() {
197
+ const spinner = ora();
198
+ spinner.start('Retrieving installed storefront apps...');
199
+ const installedApps = await this.devApi.get({
200
+ adminPath: `/client/apps`,
201
+ });
202
+ const storefrontInstalledApps = installedApps.results.filter((installedApp) => installedApp.app?.type === 'storefront');
203
+ if (storefrontInstalledApps.length === 0) {
204
+ spinner.fail(`You must have at least one storefront app installed in your ${style.appEnv('test')} environment before creating a theme.`);
205
+ return false;
206
+ }
207
+ spinner.stop();
208
+ return storefrontInstalledApps;
209
+ }
210
+ async setupPackage(name, config, pkg) {
211
+ const execAsync = promisify(exec);
212
+ const configPath = path.dirname(config.path);
213
+ const packageJson = {
214
+ description: config.get('description'),
215
+ devDependencies: {
216
+ '@swell/app-types': '^1.0.5',
217
+ },
218
+ name,
219
+ scripts: {},
220
+ version: config.get('version'),
221
+ };
222
+ const tsConfig = {
223
+ compilerOptions: {
224
+ lib: ['esnext', 'webworker'],
225
+ module: 'esnext',
226
+ target: 'esnext',
227
+ types: ['@swell/app-types'],
228
+ },
229
+ exclude: ['node_modules'],
230
+ include: ['**/*.ts'],
231
+ };
232
+ await writeJsonFile(path.join(configPath, 'package.json'), packageJson);
233
+ await writeJsonFile(path.join(configPath, 'tsconfig.json'), tsConfig);
234
+ await writeFile(path.join(configPath, '.gitignore'), `node_modules`);
235
+ try {
236
+ const packageManager = await this.findPackageManager(pkg);
237
+ await execAsync(`${packageManager} install`, {
238
+ cwd: configPath,
239
+ });
240
+ }
241
+ catch (error) {
242
+ this.error(error.message);
243
+ }
244
+ }
245
+ async tryPackageSetup(name, config, pkg) {
246
+ try {
247
+ await this.setupPackage(name, config, pkg);
248
+ }
249
+ catch (error) {
250
+ this.log(`There was an error setting up the package manager: ${error.message}`);
251
+ }
252
+ }
253
+ }
@@ -1,12 +1,12 @@
1
- import { CreateCommand } from './create-command.js';
1
+ import { CreateConfigCommand } from './create-config-command.js';
2
2
  /**
3
3
  * A base class for Swell CLI Create [content|models] command input.
4
4
  *
5
- * This class extends the `CreateCommand` class and adds:
5
+ * This class extends the `CreateConfigCommand` class and adds:
6
6
  *
7
7
  * - promptCollection: Get collection name and description
8
8
  */
9
- export declare abstract class CreateCollectionCommand extends CreateCommand {
9
+ export declare abstract class CreateCollectionCommand extends CreateConfigCommand {
10
10
  static args: {
11
11
  collection: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
12
12
  };
@@ -1,15 +1,15 @@
1
1
  import { Separator, input, select } from '@inquirer/prompts';
2
2
  import { Args, Flags } from '@oclif/core';
3
- import { CreateCommand } from './create-command.js';
3
+ import { CreateConfigCommand } from './create-config-command.js';
4
4
  import { toCollectionLabel, toCollectionName } from './lib/create/index.js';
5
5
  /**
6
6
  * A base class for Swell CLI Create [content|models] command input.
7
7
  *
8
- * This class extends the `CreateCommand` class and adds:
8
+ * This class extends the `CreateConfigCommand` class and adds:
9
9
  *
10
10
  * - promptCollection: Get collection name and description
11
11
  */
12
- export class CreateCollectionCommand extends CreateCommand {
12
+ export class CreateCollectionCommand extends CreateConfigCommand {
13
13
  // base flags for all AppCommands
14
14
  static args = {
15
15
  collection: Args.string({
@@ -20,7 +20,7 @@ export class CreateCollectionCommand extends CreateCommand {
20
20
  // needs to be in a ES2021 format: https://github.com/oclif/oclif/issues/1100
21
21
  static baseFlags = {
22
22
  'app-path': Flags.string({
23
- description: 'path to the app directory',
23
+ description: 'path to your app directory',
24
24
  }),
25
25
  };
26
26
  static flags = {
@@ -9,7 +9,7 @@ import { CreateFileParams } from './lib/create/index.js';
9
9
  * - createType: Type of Config to generate file path
10
10
  * - createFile: File creation, including general prompts
11
11
  */
12
- export declare abstract class CreateCommand extends AppCommand {
12
+ export declare abstract class CreateConfigCommand extends AppCommand {
13
13
  protected createType: '' | ConfigType;
14
14
  protected createFile({ extension, fileBody, fileName }: CreateFileParams, overwrite: boolean, shouldConfirm?: boolean): Promise<boolean | undefined>;
15
15
  protected getCollectionOptions(): Promise<any>;
@@ -1,7 +1,7 @@
1
1
  import { confirm } from '@inquirer/prompts';
2
2
  import * as path from 'node:path';
3
3
  import { AppCommand } from './app-command.js';
4
- import { ConfigPaths, fileExists, writeFile, writeJsonFile, } from './lib/apps/index.js';
4
+ import { ConfigPaths, filePathExists, writeFile, writeJsonFile, } from './lib/apps/index.js';
5
5
  /**
6
6
  * A base class for Swell CLI Create commands for file and input handling.
7
7
  *
@@ -10,7 +10,7 @@ import { ConfigPaths, fileExists, writeFile, writeJsonFile, } from './lib/apps/i
10
10
  * - createType: Type of Config to generate file path
11
11
  * - createFile: File creation, including general prompts
12
12
  */
13
- export class CreateCommand extends AppCommand {
13
+ export class CreateConfigCommand extends AppCommand {
14
14
  createType = '';
15
15
  async createFile({ extension = 'json', fileBody, fileName }, overwrite, shouldConfirm = true) {
16
16
  const isJson = extension === 'json';
@@ -18,7 +18,7 @@ export class CreateCommand extends AppCommand {
18
18
  if (shouldConfirm) {
19
19
  this.log(`\nCreating app ${this.createType} in ${filePath}`);
20
20
  this.log(`\n${isJson ? JSON.stringify(fileBody, null, 4) : fileBody}\n`);
21
- if (fileExists(filePath) && !overwrite) {
21
+ if (filePathExists(filePath) && !overwrite) {
22
22
  const continueWrite = await confirm({
23
23
  message: `This will overwrite an existing ${this.createType} file. Ok to continue?`,
24
24
  });
@@ -45,9 +45,7 @@ export class CreateCommand extends AppCommand {
45
45
  const models = await this.api.post({ adminPath: `/data/$get/:models` }, {
46
46
  body: {
47
47
  abstract: { $ne: true },
48
- // eslint-disable-next-line camelcase
49
48
  app_id: null,
50
- // eslint-disable-next-line camelcase
51
49
  client_id: null,
52
50
  deprecated: { $ne: true },
53
51
  fields: 'name, namespace',
@@ -0,0 +1,8 @@
1
+ declare namespace _default {
2
+ let API_BASE_URL: string;
3
+ let ADMIN_API_BASE_URL: string;
4
+ let LOGIN_HOST: string;
5
+ let APP_PAGES_HOST: string;
6
+ let STOREFRONT_FRONTEND_HOST: string;
7
+ }
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ export default {
2
+ API_BASE_URL: 'https://localapi.schema.io:4443',
3
+ ADMIN_API_BASE_URL: 'http://${STORE_ID}.swell.test:4001/admin/api',
4
+ LOGIN_HOST: 'http://${STORE_ID}.swell.test:3000',
5
+ APP_PAGES_HOST: 'http://${STORE_ID}--${APP_ID}--${ENV}.swell.test:4001',
6
+ STOREFRONT_FRONTEND_HOST: 'http://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.test:4001',
7
+ };
@@ -0,0 +1,8 @@
1
+ declare namespace _default {
2
+ let API_BASE_URL: string;
3
+ let ADMIN_API_BASE_URL: string;
4
+ let LOGIN_HOST: string;
5
+ let APP_PAGES_HOST: string;
6
+ let STOREFRONT_FRONTEND_HOST: string;
7
+ }
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ export default {
2
+ API_BASE_URL: 'https://api.swell.store',
3
+ ADMIN_API_BASE_URL: 'https://${STORE_ID}.swell.store/admin/api',
4
+ LOGIN_HOST: 'https://${STORE_ID}.swell.store',
5
+ APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.swell.store',
6
+ STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.store',
7
+ };
@@ -0,0 +1,8 @@
1
+ declare namespace _default {
2
+ let API_BASE_URL: string;
3
+ let ADMIN_API_BASE_URL: string;
4
+ let LOGIN_HOST: string;
5
+ let APP_PAGES_HOST: string;
6
+ let STOREFRONT_FRONTEND_HOST: string;
7
+ }
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ export default {
2
+ API_BASE_URL: 'https://${REVIEW_DOMAIN}.rav2.swell.store',
3
+ ADMIN_API_BASE_URL: 'https://${STORE_ID}.${REVIEW_DOMAIN}.rav2.swell.store/admin/api',
4
+ LOGIN_HOST: 'https://${REVIEW_DOMAIN}.rav2.swell.store',
5
+ APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.${REVIEW_DOMAIN}.rav2.swell.store',
6
+ STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.${REVIEW_DOMAIN}.rav2.swell.store',
7
+ };
@@ -0,0 +1,8 @@
1
+ declare namespace _default {
2
+ let API_BASE_URL: string;
3
+ let ADMIN_API_BASE_URL: string;
4
+ let LOGIN_HOST: string;
5
+ let APP_PAGES_HOST: string;
6
+ let STOREFRONT_FRONTEND_HOST: string;
7
+ }
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ export default {
2
+ API_BASE_URL: 'https://staging.swell.store',
3
+ ADMIN_API_BASE_URL: 'https://${STORE_ID}.staging.swell.store/admin/api',
4
+ LOGIN_HOST: 'https://staging.swell.store',
5
+ APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.staging.swell.store',
6
+ STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.staging.swell.store',
7
+ };
package/dist/lib/api.d.ts CHANGED
@@ -11,4 +11,5 @@ export default class Api {
11
11
  put(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
12
12
  setStoreEnv(storeId: string, envId?: string): void;
13
13
  truncatePath(path: string | undefined): string | undefined;
14
+ waitForAsyncResponse(response: any, pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
14
15
  }
package/dist/lib/api.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import fetch from 'node-fetch';
2
+ import { stringify } from 'qs';
2
3
  import config from './config.js';
3
4
  import { API_BASE_URL, getAdminApiBaseUrl } from './constants.js';
4
5
  const defaultHeaders = (opts) => ({
@@ -50,35 +51,35 @@ export default class Api {
50
51
  ? `${getAdminApiBaseUrl(storeId)}/${this.truncatePath(pathOpts.adminPath)}`
51
52
  : `${API_BASE_URL}/${this.truncatePath(pathOpts.backendPath)}`;
52
53
  if (options.query) {
53
- const query = new URLSearchParams(options.query);
54
- path += `?${query.toString()}`;
54
+ path += `?${stringify(options.query)}`;
55
55
  }
56
56
  path = path
57
- // eslint-disable-next-line no-template-curly-in-string
58
57
  .replace('${STORE_ID}', storeId) // replace store id in template string
59
58
  .replaceAll(/([^:]\/)\/+/g, '$1'); // remove duplicate slashes
60
59
  if (!path)
61
60
  throw new Error('Operation not supported.');
62
- const resp = await fetch(path, fetchOptions);
63
- const respData = await resp.text();
64
- if (!resp.ok) {
61
+ const res = await fetch(path, fetchOptions);
62
+ const resData = await res.text();
63
+ if (!res.ok) {
65
64
  // Try to parse error message
66
65
  let errorJson;
67
66
  try {
68
- errorJson = JSON.parse(respData);
67
+ errorJson = JSON.parse(resData);
69
68
  }
70
69
  catch {
71
70
  // ignore
72
71
  }
73
- throw new Error(errorJson?.error?.message || errorJson?.error || respData);
72
+ const error = new Error(errorJson?.error?.message || errorJson?.error || resData);
73
+ error.status = res.status;
74
+ throw error;
74
75
  }
75
76
  try {
76
- return JSON.parse(respData);
77
+ return JSON.parse(resData);
77
78
  }
78
79
  catch (error) {
79
80
  // respData could be "{}" (length: >=2) but it shouldn't be "{", " " (whitespace - length: 1) or
80
81
  // "" (empty response - length: 0) while logging the error.
81
- if (respData.length >= 2)
82
+ if (resData.length >= 2)
82
83
  console.log(error);
83
84
  return null;
84
85
  }
@@ -87,10 +88,11 @@ export default class Api {
87
88
  // ensure post body is stringified
88
89
  if (options.body)
89
90
  options.body = JSON.stringify(options.body);
90
- return this.api(pathOpts, {
91
+ const response = await this.api(pathOpts, {
91
92
  method: 'DELETE',
92
93
  ...options,
93
94
  });
95
+ return this.waitForAsyncResponse(response, pathOpts, options);
94
96
  }
95
97
  async get(pathOpts, options = {}) {
96
98
  return this.api(pathOpts, {
@@ -105,19 +107,21 @@ export default class Api {
105
107
  // ensure post body is stringified
106
108
  if (options.body)
107
109
  options.body = JSON.stringify(options.body);
108
- return this.api(pathOpts, {
110
+ const response = await this.api(pathOpts, {
109
111
  method: 'POST',
110
112
  ...options,
111
113
  });
114
+ return this.waitForAsyncResponse(response, pathOpts, options);
112
115
  }
113
116
  async put(pathOpts, options = {}) {
114
117
  // ensure post body is stringified
115
118
  if (options.body)
116
119
  options.body = JSON.stringify(options.body);
117
- return this.api(pathOpts, {
120
+ const response = await this.api(pathOpts, {
118
121
  method: 'PUT',
119
122
  ...options,
120
123
  });
124
+ return this.waitForAsyncResponse(response, pathOpts, options);
121
125
  }
122
126
  setStoreEnv(storeId, envId) {
123
127
  this.storeId = storeId;
@@ -126,4 +130,26 @@ export default class Api {
126
130
  truncatePath(path) {
127
131
  return path?.startsWith('/') ? path.slice(1) : path;
128
132
  }
133
+ async waitForAsyncResponse(response, pathOpts, options = {}) {
134
+ const { onAsyncGetPath, spinner } = options;
135
+ if (!response?.async_progressing || !onAsyncGetPath) {
136
+ return response;
137
+ }
138
+ /* eslint-disable-next-line no-promise-executor-return */
139
+ await new Promise((resolve) => setTimeout(resolve, 1000));
140
+ const asyncResponse = await this.api(onAsyncGetPath(response), {
141
+ method: 'GET',
142
+ onAsyncGetPath,
143
+ });
144
+ if (asyncResponse?.async_error) {
145
+ throw new Error(asyncResponse.async_error);
146
+ }
147
+ if (!asyncResponse?.async_progressing && spinner) {
148
+ spinner.suffixText = '';
149
+ }
150
+ else if (asyncResponse?.async_progress > 0 && spinner) {
151
+ spinner.suffixText = `${asyncResponse.async_progress}%`;
152
+ }
153
+ return this.waitForAsyncResponse(asyncResponse, pathOpts, options);
154
+ }
129
155
  }
@@ -1,25 +1,25 @@
1
1
  import { App } from './apps';
2
2
  /**
3
- * Checks if the app config file exists.
3
+ * Checks if the swell config file exists at the given path, default to cwd.
4
4
  *
5
5
  * @param configFilePath - the path to the app config file
6
6
  * @returns boolean
7
7
  * @throws Error
8
8
  */
9
- declare function appConfigFileExists(configFilePath?: string): boolean;
9
+ declare function swellConfigFileExists(configFilePath?: string): boolean;
10
10
  declare function writeRcFile(path: string, data: any): Promise<void>;
11
11
  declare function readRcFile(path: string): App;
12
12
  /**
13
13
  * Returns a new configuration instance in the current working directory or
14
14
  * a parent directory.
15
15
  *
16
- * @param configDirPath - the path to the app config directory. If not provided,
16
+ * @param configDir - the path to the app config directory. If not provided,
17
17
  * the config file will be searched for in the current working directory or
18
18
  * a parent directory.
19
19
  *
20
20
  * @returns Conf
21
21
  */
22
- declare function config(configDirPath?: string): LocalApp;
22
+ declare function config(configDir?: string): LocalApp;
23
23
  /**
24
24
  * Returns a new configuration instance in the current working directory only.
25
25
  *
@@ -27,4 +27,11 @@ declare function config(configDirPath?: string): LocalApp;
27
27
  * @returns Conf
28
28
  */
29
29
  declare function newConfig(path?: string): any;
30
- export { appConfigFileExists, config as default, newConfig, readRcFile, writeRcFile, };
30
+ /**
31
+ * Returns a new configuration instance at the specified path.
32
+ *
33
+ * @param appPath Path of the current working directory
34
+ * @returns Conf
35
+ */
36
+ declare function newConfigAtPath(appPath?: string): any;
37
+ export { config as default, newConfig, newConfigAtPath, readRcFile, swellConfigFileExists, writeRcFile, };