@swell/cli 2.0.20 → 2.2.0

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 (94) hide show
  1. package/dist/commands/api/delete.d.ts +15 -0
  2. package/dist/commands/api/delete.js +26 -0
  3. package/dist/commands/api/get.d.ts +15 -0
  4. package/dist/commands/api/get.js +31 -0
  5. package/dist/commands/api/index.d.ts +7 -0
  6. package/dist/commands/api/index.js +16 -0
  7. package/dist/commands/api/post.d.ts +16 -0
  8. package/dist/commands/api/post.js +33 -0
  9. package/dist/commands/api/put.d.ts +16 -0
  10. package/dist/commands/api/put.js +33 -0
  11. package/dist/commands/app/dev.d.ts +18 -4
  12. package/dist/commands/app/dev.js +383 -5
  13. package/dist/commands/app/frontend/dev.d.ts +6 -5
  14. package/dist/commands/app/frontend/dev.js +127 -58
  15. package/dist/commands/app/install.js +3 -3
  16. package/dist/commands/app/pull.d.ts +1 -1
  17. package/dist/commands/app/pull.js +6 -6
  18. package/dist/commands/app/push.d.ts +3 -3
  19. package/dist/commands/app/push.js +8 -6
  20. package/dist/commands/app/release.d.ts +1 -1
  21. package/dist/commands/app/release.js +1 -1
  22. package/dist/commands/app/version.d.ts +1 -1
  23. package/dist/commands/app/version.js +5 -3
  24. package/dist/commands/create/app.d.ts +10 -2
  25. package/dist/commands/create/app.js +226 -57
  26. package/dist/commands/create/content.d.ts +4 -0
  27. package/dist/commands/create/content.js +83 -15
  28. package/dist/commands/create/function.js +27 -5
  29. package/dist/commands/create/model.d.ts +4 -0
  30. package/dist/commands/create/model.js +120 -25
  31. package/dist/commands/inspect/content.d.ts +25 -0
  32. package/dist/commands/inspect/content.js +159 -0
  33. package/dist/commands/inspect/index.d.ts +7 -0
  34. package/dist/commands/inspect/index.js +16 -0
  35. package/dist/commands/inspect/models.d.ts +27 -0
  36. package/dist/commands/inspect/models.js +197 -0
  37. package/dist/commands/schema.d.ts +27 -0
  38. package/dist/commands/schema.js +231 -0
  39. package/dist/commands/theme/dev.d.ts +1 -1
  40. package/dist/commands/theme/dev.js +2 -2
  41. package/dist/commands/theme/init.d.ts +1 -1
  42. package/dist/commands/theme/init.js +1 -1
  43. package/dist/commands/theme/pull.d.ts +2 -2
  44. package/dist/commands/theme/pull.js +2 -2
  45. package/dist/commands/theme/push.d.ts +1 -1
  46. package/dist/commands/theme/push.js +1 -1
  47. package/dist/create-app-command.d.ts +14 -0
  48. package/dist/create-app-command.js +198 -15
  49. package/dist/create-collection-command.js +4 -1
  50. package/dist/create-config-command.js +4 -0
  51. package/dist/env/local.d.ts +2 -0
  52. package/dist/env/local.js +2 -0
  53. package/dist/env/production.d.ts +2 -0
  54. package/dist/env/production.js +2 -0
  55. package/dist/env/review.d.ts +2 -0
  56. package/dist/env/review.js +2 -0
  57. package/dist/env/staging.d.ts +2 -0
  58. package/dist/env/staging.js +2 -0
  59. package/dist/lib/api.d.ts +16 -5
  60. package/dist/lib/api.js +67 -25
  61. package/dist/lib/app-config.js +1 -0
  62. package/dist/lib/apps/app-config.d.ts +49 -0
  63. package/dist/lib/apps/app-config.js +21 -14
  64. package/dist/lib/apps/index.d.ts +5 -4
  65. package/dist/lib/apps/index.js +109 -21
  66. package/dist/lib/bundle.js +2 -1
  67. package/dist/lib/config.d.ts +1 -1
  68. package/dist/lib/constants.d.ts +2 -4
  69. package/dist/lib/constants.js +6 -4
  70. package/dist/lib/create/content.d.ts +5 -1
  71. package/dist/lib/create/content.js +8 -1
  72. package/dist/lib/create/function.d.ts +2 -1
  73. package/dist/lib/create/function.js +7 -3
  74. package/dist/lib/create/model.d.ts +1 -0
  75. package/dist/lib/create/schemas.d.ts +6 -0
  76. package/dist/lib/create/schemas.js +6 -0
  77. package/dist/lib/proxy.d.ts +1 -0
  78. package/dist/lib/proxy.js +29 -1
  79. package/dist/lib/sessions.js +4 -3
  80. package/dist/lib/style.js +0 -1
  81. package/dist/lib/swell-function-wrapper.d.ts +23 -4
  82. package/dist/lib/swell-function-wrapper.js +11 -4
  83. package/dist/lib/theme-sync.d.ts +6 -6
  84. package/dist/lib/theme-sync.js +49 -39
  85. package/dist/push-app-command.d.ts +12 -8
  86. package/dist/push-app-command.js +108 -29
  87. package/dist/remote-app-command.d.ts +1 -1
  88. package/dist/remote-app-command.js +4 -2
  89. package/dist/swell-api-command.d.ts +13 -0
  90. package/dist/swell-api-command.js +75 -0
  91. package/dist/swell-command.d.ts +1 -1
  92. package/dist/swell-command.js +9 -9
  93. package/package.json +10 -1
  94. package/oclif.manifest.json +0 -1895
@@ -2,27 +2,35 @@ import { confirm, select } from '@inquirer/prompts';
2
2
  import { Flags } from '@oclif/core';
3
3
  import { $ } from 'execa';
4
4
  import { exec } from 'node:child_process';
5
+ import fs from 'node:fs/promises';
5
6
  import path from 'node:path';
6
7
  import Stream from 'node:stream';
7
8
  import { promisify } from 'node:util';
8
9
  import ora from 'ora';
9
10
  import Api from './lib/api.js';
10
- import { FrontendProjectTypes, getAllConfigPaths, writeFile, writeJsonFile, } from './lib/apps/index.js';
11
+ import { FrontendProjectTypes, getFrontendProjectValidValues, getAllConfigPaths, getFrontendProjectSlugs, writeFile, writeJsonFile, } from './lib/apps/index.js';
11
12
  import style from './lib/style.js';
12
13
  import { SwellCommand } from './swell-command.js';
13
14
  const execAsync = promisify(exec);
14
15
  export class CreateAppCommand extends SwellCommand {
15
16
  static baseFlags = {
16
17
  frontend: Flags.string({
17
- description: `create a starter framework for a hosted frontend`,
18
- options: FrontendProjectTypes.map(({ slug }) => slug),
18
+ description: `create a starter framework for a hosted frontend (default in -y: none)`,
19
+ options: getFrontendProjectSlugs(true, false),
19
20
  }),
20
21
  'storefront-app': Flags.string({
21
22
  description: `id of an installed storefront app to create a theme for, if applicable`,
22
23
  }),
24
+ 'integration-type': Flags.string({
25
+ description: `integration type for integration app, if applicable`,
26
+ options: ['generic', 'payment', 'shipping', 'tax'],
27
+ }),
28
+ 'integration-id': Flags.string({
29
+ description: `unique identifier for the integration service (required for payment/shipping/tax; use 'card' for credit cards)`,
30
+ }),
23
31
  yes: Flags.boolean({
24
32
  char: 'y',
25
- description: `accept all default values`,
33
+ description: `accept all default values; non-interactive, never prompts; errors if required values are missing`,
26
34
  }),
27
35
  };
28
36
  devApi;
@@ -30,6 +38,118 @@ export class CreateAppCommand extends SwellCommand {
30
38
  super(argv, config);
31
39
  this.devApi = new Api(undefined, 'test');
32
40
  }
41
+ async addAllowedHostsToAngular(configPath) {
42
+ const angularJsonPath = path.join(configPath, 'frontend', 'angular.json');
43
+ let content;
44
+ try {
45
+ content = await fs.readFile(angularJsonPath, 'utf8');
46
+ }
47
+ catch {
48
+ return;
49
+ }
50
+ let config;
51
+ try {
52
+ config = JSON.parse(content);
53
+ }
54
+ catch {
55
+ return;
56
+ }
57
+ const { projects } = config;
58
+ if (!projects || typeof projects !== 'object') {
59
+ return;
60
+ }
61
+ const projectNames = Object.keys(projects);
62
+ if (projectNames.length === 0) {
63
+ return;
64
+ }
65
+ // Use first project (typically only one exists in new apps)
66
+ const projectName = projectNames[0];
67
+ const project = projects[projectName];
68
+ if (!project?.architect?.serve) {
69
+ return;
70
+ }
71
+ if (!project.architect.serve.options) {
72
+ project.architect.serve.options = {};
73
+ }
74
+ if (project.architect.serve.options.allowedHosts) {
75
+ return;
76
+ }
77
+ project.architect.serve.options.allowedHosts = ['.ngrok.app', '.loca.lt'];
78
+ await fs.writeFile(angularJsonPath, JSON.stringify(config, null, 2), 'utf8');
79
+ }
80
+ async addAllowedHostsToAstro(configPath) {
81
+ const astroConfigPath = path.join(configPath, 'frontend', 'astro.config.mjs');
82
+ let content;
83
+ try {
84
+ content = await fs.readFile(astroConfigPath, 'utf8');
85
+ }
86
+ catch {
87
+ return;
88
+ }
89
+ if (content.includes('allowedHosts')) {
90
+ return;
91
+ }
92
+ const lines = content.split('\n');
93
+ let insertIndex = -1;
94
+ for (const [i, line] of lines.entries()) {
95
+ if (line.includes('defineConfig({')) {
96
+ insertIndex = i + 1;
97
+ break;
98
+ }
99
+ }
100
+ if (insertIndex === -1) {
101
+ return;
102
+ }
103
+ // Detect indentation from next line or use default
104
+ const nextLine = lines[insertIndex];
105
+ const indentMatch = nextLine?.match(/^(\s+)/);
106
+ const baseIndent = indentMatch ? indentMatch[1] : ' ';
107
+ const viteConfig = [
108
+ `${baseIndent}vite: {`,
109
+ `${baseIndent} server: {`,
110
+ `${baseIndent} allowedHosts: ['.ngrok.app', '.loca.lt'],`,
111
+ `${baseIndent} },`,
112
+ `${baseIndent}},`,
113
+ ];
114
+ lines.splice(insertIndex, 0, ...viteConfig);
115
+ await fs.writeFile(astroConfigPath, lines.join('\n'), 'utf8');
116
+ }
117
+ async addAllowedHostsToNuxt(configPath) {
118
+ const nuxtConfigPath = path.join(configPath, 'frontend', 'nuxt.config.ts');
119
+ let content;
120
+ try {
121
+ content = await fs.readFile(nuxtConfigPath, 'utf8');
122
+ }
123
+ catch {
124
+ return;
125
+ }
126
+ if (content.includes('allowedHosts')) {
127
+ return;
128
+ }
129
+ const lines = content.split('\n');
130
+ let insertIndex = -1;
131
+ for (const [i, line] of lines.entries()) {
132
+ if (line.includes('defineNuxtConfig({')) {
133
+ insertIndex = i + 1;
134
+ break;
135
+ }
136
+ }
137
+ if (insertIndex === -1) {
138
+ return;
139
+ }
140
+ const nextLine = lines[insertIndex];
141
+ const indentMatch = nextLine?.match(/^(\s+)/);
142
+ const baseIndent = indentMatch ? indentMatch[1] : ' ';
143
+ const viteConfig = [
144
+ `${baseIndent}vite: {`,
145
+ `${baseIndent} server: {`,
146
+ `${baseIndent} allowedHosts: ['.ngrok.app', '.loca.lt'],`,
147
+ `${baseIndent} },`,
148
+ `${baseIndent}},`,
149
+ ];
150
+ lines.splice(insertIndex, 0, ...viteConfig);
151
+ await fs.writeFile(nuxtConfigPath, lines.join('\n'), 'utf8');
152
+ }
33
153
  async createAppConfigFolders(swellConfig) {
34
154
  for (const type of getAllConfigPaths(swellConfig.get('type'))) {
35
155
  // Create a config folder
@@ -42,13 +162,11 @@ export class CreateAppCommand extends SwellCommand {
42
162
  async createFrontendApp(swellConfig, flags, directCreate, kind) {
43
163
  const spinner = ora();
44
164
  const configPath = path.dirname(swellConfig.path);
45
- const frameworkType = flags.frontend ||
165
+ // use 'none' by default for non-interactive mode
166
+ const inputFrontend = flags.yes ? flags.frontend || 'none' : flags.frontend;
167
+ const frameworkType = inputFrontend ||
46
168
  (await select({
47
169
  choices: [
48
- ...FrontendProjectTypes.map(({ name, slug }) => ({
49
- name,
50
- value: slug,
51
- })),
52
170
  ...(directCreate
53
171
  ? []
54
172
  : [
@@ -57,16 +175,17 @@ export class CreateAppCommand extends SwellCommand {
57
175
  value: null,
58
176
  },
59
177
  ]),
178
+ ...FrontendProjectTypes.map(({ name, slug }) => ({
179
+ name,
180
+ value: slug,
181
+ })),
60
182
  ],
61
183
  message: directCreate
62
184
  ? `Choose framework for your hosted${kind ? ` ${kind} ` : ' '}app frontend`
63
185
  : `Install framework for a hosted${kind ? ` ${kind} ` : ' '}app frontend?`,
64
186
  }));
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
- }
187
+ if (frameworkType && frameworkType !== 'none') {
188
+ const projectType = this.getProjectType(frameworkType);
70
189
  this.log();
71
190
  spinner.start(`Creating ${projectType?.name} frontend app (this may take a while)...`);
72
191
  try {
@@ -88,6 +207,31 @@ export class CreateAppCommand extends SwellCommand {
88
207
  this.log();
89
208
  return false;
90
209
  }
210
+ // Ensure frontend package.json has correct name for workspace
211
+ const frontendPkgPath = path.join(configPath, 'frontend', 'package.json');
212
+ try {
213
+ const pkgContent = await fs.readFile(frontendPkgPath, 'utf8');
214
+ const pkg = JSON.parse(pkgContent);
215
+ if (pkg.name !== 'frontend') {
216
+ pkg.name = 'frontend';
217
+ await fs.writeFile(frontendPkgPath, JSON.stringify(pkg, null, 2), 'utf8');
218
+ }
219
+ }
220
+ catch {
221
+ // Ignore if package.json doesn't exist or can't be read
222
+ }
223
+ await this.addFrontendAllowedHosts(projectType, configPath);
224
+ // Re-run npm install at root to properly initialize workspace structure
225
+ // (removes frontend/package-lock.json, hoists dependencies, creates root lock file)
226
+ spinner.start('Initializing workspace...');
227
+ try {
228
+ const execAsync = promisify(exec);
229
+ await execAsync('npm install', { cwd: configPath });
230
+ spinner.succeed('Workspace initialized');
231
+ }
232
+ catch {
233
+ spinner.warn('Failed to initialize workspace');
234
+ }
91
235
  spinner.succeed(`${projectType?.name} initialized app in ${configPath}/frontend/`);
92
236
  if (directCreate) {
93
237
  this.log();
@@ -207,11 +351,31 @@ export class CreateAppCommand extends SwellCommand {
207
351
  spinner.stop();
208
352
  return storefrontInstalledApps;
209
353
  }
354
+ getProjectType(frameworkType) {
355
+ const projectType = FrontendProjectTypes.find((type) => type.slug === frameworkType);
356
+ if (!projectType) {
357
+ this.error(`Could not find project type: ${frameworkType}\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample: swell create app reviews --type admin --frontend astro -y`, {
358
+ exit: 1,
359
+ });
360
+ }
361
+ if (!projectType.installCommand) {
362
+ this.error(`Project type ${projectType.name} cannot be installed (legacy type)\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample: swell create app reviews --type admin --frontend astro -y`, {
363
+ exit: 1,
364
+ });
365
+ }
366
+ return {
367
+ ...projectType,
368
+ installCommand: projectType.installCommand || '',
369
+ };
370
+ }
210
371
  async setupPackage(name, config, pkg) {
211
372
  const execAsync = promisify(exec);
212
373
  const configPath = path.dirname(config.path);
213
374
  const packageJson = {
214
375
  description: config.get('description'),
376
+ // Include workspace even if frontend doesn't exist yet
377
+ // npm tolerates missing workspace directories without errors
378
+ workspaces: ['frontend'],
215
379
  devDependencies: {
216
380
  '@swell/app-types': '^1.0.5',
217
381
  },
@@ -226,7 +390,7 @@ export class CreateAppCommand extends SwellCommand {
226
390
  target: 'esnext',
227
391
  types: ['@swell/app-types'],
228
392
  },
229
- exclude: ['node_modules'],
393
+ exclude: ['node_modules', 'frontend'],
230
394
  include: ['**/*.ts'],
231
395
  };
232
396
  await writeJsonFile(path.join(configPath, 'package.json'), packageJson);
@@ -250,4 +414,23 @@ export class CreateAppCommand extends SwellCommand {
250
414
  this.log(`There was an error setting up the package manager: ${error.message}`);
251
415
  }
252
416
  }
417
+ async addFrontendAllowedHosts(projectType, configPath) {
418
+ // Configure allowedHosts for frameworks using Vite dev server
419
+ // This allows tunneling services (ngrok, localtunnel) to proxy requests to the local dev environment
420
+ switch (projectType.slug) {
421
+ case 'astro': {
422
+ await this.addAllowedHostsToAstro(configPath);
423
+ break;
424
+ }
425
+ case 'angular': {
426
+ await this.addAllowedHostsToAngular(configPath);
427
+ break;
428
+ }
429
+ case 'nuxt': {
430
+ await this.addAllowedHostsToNuxt(configPath);
431
+ break;
432
+ }
433
+ // Other frameworks don't require allowedHosts configuration
434
+ }
435
+ }
253
436
  }
@@ -41,7 +41,10 @@ export class CreateCollectionCommand extends CreateConfigCommand {
41
41
  }),
42
42
  };
43
43
  async promptCollection() {
44
- const { args, flags } = await this.parse(CreateCollectionCommand);
44
+ // Parse using the concrete command class so child-command flags
45
+ // (e.g., --events, --fields) are recognized during interactive flows.
46
+ const klass = super.ctor;
47
+ const { args, flags } = await this.parse(klass);
45
48
  let { description, label } = flags;
46
49
  let { collection } = args;
47
50
  let isStandardCollection = false;
@@ -35,6 +35,10 @@ export class CreateConfigCommand extends AppCommand {
35
35
  }
36
36
  }
37
37
  }
38
+ else if (filePathExists(filePath) && !overwrite) {
39
+ // Non-interactive mode: never prompt; fail fast on potential overwrite
40
+ this.error(`A ${this.createType} file already exists at ${filePath}.\n\nUse --overwrite to replace the existing file in non-interactive mode.`, { exit: 1 });
41
+ }
38
42
  typeof fileBody === 'string'
39
43
  ? await writeFile(filePath, fileBody)
40
44
  : await writeJsonFile(filePath, fileBody);
@@ -4,5 +4,7 @@ declare namespace _default {
4
4
  let LOGIN_HOST: string;
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
+ let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
7
9
  }
8
10
  export default _default;
package/dist/env/local.js CHANGED
@@ -4,4 +4,6 @@ export default {
4
4
  LOGIN_HOST: 'http://${STORE_ID}.swell.test:3000',
5
5
  APP_PAGES_HOST: 'http://${STORE_ID}--${APP_ID}--${ENV}.swell.test:4001',
6
6
  STOREFRONT_FRONTEND_HOST: 'http://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.test:4001',
7
+ LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://pub-3672563ccb924eeba4d45be1296a0aee.r2.dev',
7
9
  };
@@ -4,5 +4,7 @@ declare namespace _default {
4
4
  let LOGIN_HOST: string;
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
+ let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
7
9
  }
8
10
  export default _default;
@@ -4,4 +4,6 @@ export default {
4
4
  LOGIN_HOST: 'https://${STORE_ID}.swell.store',
5
5
  APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.swell.store',
6
6
  STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.store',
7
+ LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://json.swell.store',
7
9
  };
@@ -4,5 +4,7 @@ declare namespace _default {
4
4
  let LOGIN_HOST: string;
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
+ let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
7
9
  }
8
10
  export default _default;
@@ -4,4 +4,6 @@ export default {
4
4
  LOGIN_HOST: 'https://${REVIEW_DOMAIN}.rav2.swell.store',
5
5
  APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.${REVIEW_DOMAIN}.rav2.swell.store',
6
6
  STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.${REVIEW_DOMAIN}.rav2.swell.store',
7
+ LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://pub-3672563ccb924eeba4d45be1296a0aee.r2.dev',
7
9
  };
@@ -4,5 +4,7 @@ declare namespace _default {
4
4
  let LOGIN_HOST: string;
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
+ let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
7
9
  }
8
10
  export default _default;
@@ -4,4 +4,6 @@ export default {
4
4
  LOGIN_HOST: 'https://staging.swell.store',
5
5
  APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.staging.swell.store',
6
6
  STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.staging.swell.store',
7
+ LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://pub-3672563ccb924eeba4d45be1296a0aee.r2.dev',
7
9
  };
package/dist/lib/api.d.ts CHANGED
@@ -1,16 +1,27 @@
1
+ export declare enum HttpMethod {
2
+ GET = "get",
3
+ POST = "post",
4
+ PUT = "put",
5
+ DELETE = "delete"
6
+ }
1
7
  export default class Api {
2
8
  envId: string | undefined;
3
9
  secretKey: string | undefined;
4
10
  storeId: string | undefined;
5
11
  constructor(storeId?: string, envId?: string, secretKey?: string);
6
12
  api(pathOpts: Api.Paths, options: Api.RequestOptions): Promise<any>;
7
- delete(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
8
13
  get(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
9
- isAdmin(): boolean;
14
+ getAll(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<{
15
+ count: number;
16
+ results: any[];
17
+ }>;
10
18
  post(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
11
19
  put(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
12
- setStoreEnv(storeId: string, envId?: string): Promise<void>;
20
+ delete(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
13
21
  isTestEnvEnabled(): Promise<boolean>;
14
- truncatePath(path: string | undefined): string | undefined;
15
- waitForAsyncResponse(response: any, pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
22
+ setEnv(envId: string): Promise<void>;
23
+ setStoreEnv(storeId: string, envId?: string): Promise<void>;
24
+ private isAdmin;
25
+ private truncatePath;
26
+ private waitForAsyncResponse;
16
27
  }
package/dist/lib/api.js CHANGED
@@ -2,6 +2,14 @@ import fetch from 'node-fetch';
2
2
  import { stringify } from 'qs';
3
3
  import config from './config.js';
4
4
  import { API_BASE_URL, getAdminApiBaseUrl } from './constants.js';
5
+ export var HttpMethod;
6
+ (function (HttpMethod) {
7
+ HttpMethod["GET"] = "get";
8
+ HttpMethod["POST"] = "post";
9
+ HttpMethod["PUT"] = "put";
10
+ HttpMethod["DELETE"] = "delete";
11
+ })(HttpMethod || (HttpMethod = {}));
12
+ const GET_ALL_LIMIT = 1000;
5
13
  const defaultHeaders = (opts) => ({
6
14
  'Content-Type': 'application/json',
7
15
  ...opts,
@@ -59,6 +67,9 @@ export default class Api {
59
67
  if (!path)
60
68
  throw new Error('Operation not supported.');
61
69
  const res = await fetch(path, fetchOptions);
70
+ if (options.rawResponse) {
71
+ return res;
72
+ }
62
73
  const resData = await res.text();
63
74
  if (!res.ok) {
64
75
  // Try to parse error message
@@ -84,31 +95,46 @@ export default class Api {
84
95
  return null;
85
96
  }
86
97
  }
87
- async delete(pathOpts, options = {}) {
88
- // ensure post body is stringified
89
- if (options.body)
90
- options.body = JSON.stringify(options.body);
91
- const response = await this.api(pathOpts, {
92
- method: 'DELETE',
93
- ...options,
94
- });
95
- return this.waitForAsyncResponse(response, pathOpts, options);
96
- }
97
98
  async get(pathOpts, options = {}) {
98
99
  return this.api(pathOpts, {
99
- method: 'GET',
100
+ method: HttpMethod.GET,
100
101
  ...options,
101
102
  });
102
103
  }
103
- isAdmin() {
104
- return !this.secretKey;
104
+ async getAll(pathOpts, options = {}) {
105
+ const result = {
106
+ results: [],
107
+ count: 0,
108
+ };
109
+ let page = 1;
110
+ do {
111
+ // eslint-disable-next-line no-await-in-loop
112
+ const pageResult = await this.api(pathOpts, {
113
+ method: HttpMethod.GET,
114
+ ...options,
115
+ query: {
116
+ limit: GET_ALL_LIMIT,
117
+ ...options.query,
118
+ page,
119
+ },
120
+ });
121
+ if (pageResult && pageResult.results) {
122
+ result.results = [...result.results, ...pageResult.results];
123
+ result.count = pageResult.count;
124
+ page++;
125
+ }
126
+ else {
127
+ break;
128
+ }
129
+ } while (result.count > result.results.length && result.results.length > 0);
130
+ return result;
105
131
  }
106
132
  async post(pathOpts, options = {}) {
107
133
  // ensure post body is stringified
108
134
  if (options.body)
109
135
  options.body = JSON.stringify(options.body);
110
136
  const response = await this.api(pathOpts, {
111
- method: 'POST',
137
+ method: HttpMethod.POST,
112
138
  ...options,
113
139
  });
114
140
  return this.waitForAsyncResponse(response, pathOpts, options);
@@ -118,20 +144,20 @@ export default class Api {
118
144
  if (options.body)
119
145
  options.body = JSON.stringify(options.body);
120
146
  const response = await this.api(pathOpts, {
121
- method: 'PUT',
147
+ method: HttpMethod.PUT,
122
148
  ...options,
123
149
  });
124
150
  return this.waitForAsyncResponse(response, pathOpts, options);
125
151
  }
126
- async setStoreEnv(storeId, envId) {
127
- if (envId) {
128
- const hasTestEnv = await this.isTestEnvEnabled();
129
- if (!hasTestEnv) {
130
- throw new Error('Test environment is not enabled for this store.');
131
- }
132
- }
133
- this.storeId = storeId;
134
- this.envId = envId;
152
+ async delete(pathOpts, options = {}) {
153
+ // ensure post body is stringified
154
+ if (options.body)
155
+ options.body = JSON.stringify(options.body);
156
+ const response = await this.api(pathOpts, {
157
+ method: HttpMethod.DELETE,
158
+ ...options,
159
+ });
160
+ return this.waitForAsyncResponse(response, pathOpts, options);
135
161
  }
136
162
  async isTestEnvEnabled() {
137
163
  const clientRecord = await this.get({
@@ -139,6 +165,22 @@ export default class Api {
139
165
  });
140
166
  return clientRecord.test_enabled !== false;
141
167
  }
168
+ async setEnv(envId) {
169
+ const hasTestEnv = await this.isTestEnvEnabled();
170
+ if (!hasTestEnv) {
171
+ throw new Error('Test environment is not enabled for this store.');
172
+ }
173
+ this.envId = envId;
174
+ }
175
+ async setStoreEnv(storeId, envId) {
176
+ if (envId) {
177
+ await this.setEnv(envId);
178
+ }
179
+ this.storeId = storeId;
180
+ }
181
+ isAdmin() {
182
+ return !this.secretKey;
183
+ }
142
184
  truncatePath(path) {
143
185
  return path?.startsWith('/') ? path.slice(1) : path;
144
186
  }
@@ -151,7 +193,7 @@ export default class Api {
151
193
  setTimeout(resolve, 1000);
152
194
  });
153
195
  const asyncResponse = await this.api(onAsyncGetPath(response), {
154
- method: 'GET',
196
+ method: HttpMethod.GET,
155
197
  onAsyncGetPath,
156
198
  });
157
199
  if (asyncResponse?.async_error) {
@@ -7,6 +7,7 @@ import * as path from 'node:path';
7
7
  const schema = {
8
8
  compatibilities: { type: 'object' },
9
9
  description: { type: 'string' },
10
+ extensions: { type: 'array' },
10
11
  id: { type: 'string' },
11
12
  integrations: { type: 'array' },
12
13
  name: { type: 'string' },
@@ -53,4 +53,53 @@ export declare abstract class AppConfig {
53
53
  protected abstract preparePostData(postData: any): Promise<any>;
54
54
  abstract type: ConfigType;
55
55
  }
56
+ export declare class AppConfigDefault extends AppConfig {
57
+ hasValues: boolean;
58
+ type: ConfigType;
59
+ preparePostData(postData: any): Promise<any>;
60
+ }
61
+ export declare class AppConfigModel extends AppConfig {
62
+ hasValues: boolean;
63
+ type: ConfigType;
64
+ preparePostData(postData: any): Promise<any>;
65
+ }
66
+ export declare class AppConfigContent extends AppConfig {
67
+ hasValues: boolean;
68
+ type: ConfigType;
69
+ preparePostData(postData: any): Promise<any>;
70
+ }
71
+ export declare class AppConfigNotification extends AppConfig {
72
+ hasValues: boolean;
73
+ type: ConfigType;
74
+ preparePostData(postData: any): Promise<any>;
75
+ }
76
+ export declare class AppConfigSetting extends AppConfig {
77
+ hasValues: boolean;
78
+ type: ConfigType;
79
+ preparePostData(postData: any): Promise<any>;
80
+ }
81
+ export declare class AppConfigWebhook extends AppConfig {
82
+ hasValues: boolean;
83
+ type: ConfigType;
84
+ preparePostData(postData: any): Promise<any>;
85
+ }
86
+ export declare class AppConfigFunction extends AppConfig {
87
+ hasValues: boolean;
88
+ type: ConfigType;
89
+ isRootFunction(): boolean;
90
+ preparePostData(postData: any): Promise<any>;
91
+ }
92
+ export declare class AppConfigAsset extends AppConfigDefault {
93
+ hasValues: boolean;
94
+ type: ConfigType;
95
+ }
96
+ export declare class AppConfigFrontend extends AppConfigDefault {
97
+ hasValues: boolean;
98
+ type: ConfigType;
99
+ }
100
+ export declare class AppConfigTheme extends AppConfigDefault {
101
+ hasValues: boolean;
102
+ type: ConfigType;
103
+ preparePostData(postData: any): Promise<any>;
104
+ }
56
105
  export {};