@swell/cli 2.7.1 → 2.9.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.
@@ -8,6 +8,7 @@ export default class AppDev extends PushAppCommand {
8
8
  'no-push': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
9
  port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
10
  'frontend-port': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
+ function: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
12
  'storefront-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
13
  'storefront-select': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
13
14
  yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
@@ -17,6 +18,7 @@ export default class AppDev extends PushAppCommand {
17
18
  };
18
19
  static summary: string;
19
20
  functionErrors: Map<string, string>;
21
+ functionFilter: string | null;
20
22
  functionPorts: Map<string, number>;
21
23
  functionProcesses: Map<string, ChildProcess>;
22
24
  tmpDir: string;
@@ -18,6 +18,7 @@ export default class AppDev extends PushAppCommand {
18
18
  'swell app dev --storefront-id <id>',
19
19
  'swell app dev --port 3000',
20
20
  'swell app dev --port 3000 --frontend-port 4000',
21
+ 'swell app dev --function my-function',
21
22
  ];
22
23
  static flags = {
23
24
  'no-push': Flags.boolean({
@@ -30,6 +31,9 @@ export default class AppDev extends PushAppCommand {
30
31
  'frontend-port': Flags.integer({
31
32
  description: 'specify the port for the frontend dev server when running with frontend',
32
33
  }),
34
+ function: Flags.string({
35
+ description: 'run only the named function',
36
+ }),
33
37
  'storefront-id': Flags.string({
34
38
  description: 'for storefront apps, identify a storefront to preview and push theme files to',
35
39
  }),
@@ -48,6 +52,8 @@ export default class AppDev extends PushAppCommand {
48
52
  };
49
53
  static summary = `Run an app in dev mode from your local machine.`;
50
54
  functionErrors = new Map();
55
+ // When set, only this function name is bundled, started, and watched
56
+ functionFilter = null;
51
57
  // All available functions
52
58
  functionPorts = new Map();
53
59
  // Wrangler child processes to kill on cleanup
@@ -62,6 +68,7 @@ export default class AppDev extends PushAppCommand {
62
68
  const { flags } = await this.parse(AppDev);
63
69
  const { port, 'frontend-port': frontendPort } = flags;
64
70
  const noPush = flags['no-push'];
71
+ this.functionFilter = flags.function ?? null;
65
72
  if (!(await this.ensureAppExists(undefined, false))) {
66
73
  return;
67
74
  }
@@ -213,12 +220,18 @@ ENVIRONMENT = "development"
213
220
  if (!config.isRootFunction()) {
214
221
  continue; // Skip if not a root function config
215
222
  }
223
+ if (this.functionFilter && config.name !== this.functionFilter) {
224
+ continue;
225
+ }
216
226
  functions.push(config);
217
227
  }
218
228
  }
219
229
  catch {
220
230
  // functions directory doesn't exist
221
231
  }
232
+ if (this.functionFilter && functions.length === 0) {
233
+ this.error(`Function '${this.functionFilter}' not found in ${style.path('functions/')}.`);
234
+ }
222
235
  return functions;
223
236
  }
224
237
  async logAllFunctions(functions) {
@@ -293,6 +306,9 @@ ENVIRONMENT = "development"
293
306
  if (appConfig?.type !== ConfigType.FUNCTION) {
294
307
  return;
295
308
  }
309
+ if (this.functionFilter && appConfig.name !== this.functionFilter) {
310
+ return;
311
+ }
296
312
  try {
297
313
  const fullPath = path.join(this.appPath, appConfig.filePath);
298
314
  // Re-bundle the function
@@ -10,6 +10,7 @@ export default class AppFrontendDev extends PushAppCommand {
10
10
  'no-push': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
11
  port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
12
  'frontend-port': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
+ function: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
14
  };
14
15
  static orientation: {
15
16
  env: string;
@@ -175,7 +175,7 @@ export default class Logs extends SwellCommand {
175
175
  type: Flags.string({
176
176
  description: 'filter logs by type',
177
177
  multiple: true,
178
- options: ['api', 'function', 'webhook'],
178
+ options: ['api', 'function', 'webhook', 'transaction'],
179
179
  }),
180
180
  };
181
181
  static summary = 'Output or stream store logs to the terminal.';
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { ConfigType } from './index.js';
3
4
  export declare class IgnoringFileError extends Error {
4
5
  constructor(message: string);
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { AppConfig } from './app-config.js';
3
4
  export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
4
5
  export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, } from './paths.js';
@@ -110,7 +110,7 @@ declare class SwellRequest {
110
110
  * @returns {object} existing app data merged with values
111
111
  * @throws {Error} if app id is missing or values is not a plain object
112
112
  */
113
- appValues(idOrValues: object | string, values?: object | undefined): object;
113
+ appValues(idOrValues: object | string, values: object | undefined): object;
114
114
  }
115
115
  /**
116
116
  * Class representing the Swell backend API.
@@ -129,7 +129,36 @@ declare class SwellAPI {
129
129
  post(url: any, data: any): Promise<any>;
130
130
  delete(url: any, data: any): Promise<any>;
131
131
  settings(id?: any): Promise<any>;
132
+ /**
133
+ * Atomic multi-op write. Throws SwellError with a stable `error.code`
134
+ * (transaction_conflict | transaction_timeout | transaction_throttled
135
+ * | transaction_op_failed | transaction_error). Retry is off by
136
+ * default — opt in with `{ retry: true }` or pass overrides.
137
+ *
138
+ * @param {Array<{method: string, url: string, data?: any}>} ops
139
+ * @param {{ retry?: true | { limit?: number, base?: number, max?: number, jitter?: boolean } }} [opts]
140
+ * @returns {Promise<any[]>}
141
+ */
142
+ transaction(ops: Array<{
143
+ method: string;
144
+ url: string;
145
+ data?: any;
146
+ }>, opts?: {
147
+ retry?: true | {
148
+ limit?: number | undefined;
149
+ base?: number | undefined;
150
+ max?: number | undefined;
151
+ jitter?: boolean | undefined;
152
+ } | undefined;
153
+ } | undefined): Promise<any[]>;
132
154
  }
155
+ declare const DEFAULT_RETRY: Readonly<{
156
+ limit: 3;
157
+ base: 100;
158
+ max: 5000;
159
+ jitter: true;
160
+ }>;
161
+ declare const RETRYABLE_CODES: Set<string>;
133
162
  /**
134
163
  * Class representing a Swell error.
135
164
  */
@@ -137,6 +166,8 @@ declare class SwellError extends Error {
137
166
  constructor(message: any, options?: {});
138
167
  status: any;
139
168
  body: any;
169
+ code: any;
170
+ get isRetryable(): boolean;
140
171
  }
141
172
  /**
142
173
  * Class representing a Swell response.
@@ -23,10 +23,10 @@ async function request(originalRequest, _env, context) {
23
23
  try {
24
24
  response = await executeModuleHandler(req, context);
25
25
  }
26
- catch (err) {
26
+ catch (error) {
27
27
  // Log the error for Swell
28
- console.error(err);
29
- response = new SwellResponse({ error: err.message }, { status: err.status || 500 });
28
+ console.error(error);
29
+ response = new SwellResponse({ error: error.message }, { status: error.status || 500 });
30
30
  }
31
31
  return SwellResponse._respond(req, response, context);
32
32
  }
@@ -71,11 +71,11 @@ async function executeModuleHandler(req, context) {
71
71
  if (moduleExports[method]) {
72
72
  return moduleExports[method](req, context);
73
73
  }
74
- else if (defaults) {
74
+ if (defaults) {
75
75
  if (typeof defaults === 'function') {
76
76
  return defaults(req, context);
77
77
  }
78
- else if (defaults[method]) {
78
+ if (defaults[method]) {
79
79
  return defaults[method](req, context);
80
80
  }
81
81
  }
@@ -120,9 +120,9 @@ class SwellRequest {
120
120
  this._logs = [];
121
121
  }
122
122
  assignRequestProps(req) {
123
- ['ur', 'method', 'headers', 'referrer', 'credentials'].forEach((prop) => {
123
+ for (const prop of ['ur', 'method', 'headers', 'referrer', 'credentials']) {
124
124
  this[prop] = req[prop];
125
- });
125
+ }
126
126
  }
127
127
  async initialize() {
128
128
  this.rawBody = await this.originalRequest.text();
@@ -131,15 +131,15 @@ class SwellRequest {
131
131
  this.data = JSON.parse(this.rawBody);
132
132
  this.body = { ...this.data };
133
133
  }
134
- catch (err) {
134
+ catch {
135
135
  this.data = {};
136
136
  }
137
137
  this.url = new URL(this.originalRequest.url);
138
138
  // Convert the query parameters to an object
139
- this.url.searchParams.forEach((value, key) => {
139
+ for (const [key, value] of this.url.searchParams.entries()) {
140
140
  this.query[key] = value;
141
141
  this.data[key] = value;
142
- });
142
+ }
143
143
  // Bind the console methods to the request
144
144
  console.log = this.log.bind(this, 'info');
145
145
  console.info = this.log.bind(this, 'info');
@@ -151,7 +151,7 @@ class SwellRequest {
151
151
  try {
152
152
  return JSON.parse(input);
153
153
  }
154
- catch (err) {
154
+ catch {
155
155
  return {};
156
156
  }
157
157
  }
@@ -160,7 +160,7 @@ class SwellRequest {
160
160
  this._logs.push({
161
161
  date: Date.now(),
162
162
  line: line.map((l) => (l instanceof Error ? l.stack : JSON.stringify(l))),
163
- ...(level !== 'info' ? { level } : {}),
163
+ ...(level === 'info' ? {} : { level }),
164
164
  });
165
165
  }
166
166
  getIngestableLogs(response) {
@@ -186,7 +186,7 @@ class SwellRequest {
186
186
  formatRequestData() {
187
187
  try {
188
188
  const stringData = JSON.stringify(this.body ?? null);
189
- return stringData.substring(0, 1024000);
189
+ return stringData.slice(0, 1024000);
190
190
  }
191
191
  catch {
192
192
  return '';
@@ -211,7 +211,7 @@ class SwellRequest {
211
211
  * @returns {object} existing app data merged with values
212
212
  * @throws {Error} if app id is missing or values is not a plain object
213
213
  */
214
- appValues(idOrValues, values = undefined) {
214
+ appValues(idOrValues, values) {
215
215
  const appId = typeof idOrValues === 'string' ? idOrValues : this.appId;
216
216
  const appValues = typeof idOrValues === 'string' ? values : idOrValues;
217
217
  if (!appId) {
@@ -283,7 +283,7 @@ class SwellAPI {
283
283
  throw new Error(`Error serializing data: ${data}`);
284
284
  }
285
285
  }
286
- const endpointUrl = String(url).startsWith('/') ? url.substring(1) : url;
286
+ const endpointUrl = String(url).startsWith('/') ? url.slice(1) : url;
287
287
  const response = await fetch(`${this.baseUrl}/${endpointUrl}${query}`, requestOptions);
288
288
  const responseText = await response.text();
289
289
  let result;
@@ -320,7 +320,54 @@ class SwellAPI {
320
320
  async settings(id = this.request.appId) {
321
321
  return this.makeRequest('GET', `/settings/${id}`);
322
322
  }
323
+ /**
324
+ * Atomic multi-op write. Throws SwellError with a stable `error.code`
325
+ * (transaction_conflict | transaction_timeout | transaction_throttled
326
+ * | transaction_op_failed | transaction_error). Retry is off by
327
+ * default — opt in with `{ retry: true }` or pass overrides.
328
+ *
329
+ * @param {Array<{method: string, url: string, data?: any}>} ops
330
+ * @param {{ retry?: true | { limit?: number, base?: number, max?: number, jitter?: boolean } }} [opts]
331
+ * @returns {Promise<any[]>}
332
+ */
333
+ async transaction(ops, opts = {}) {
334
+ if (!opts.retry) {
335
+ return this.makeRequest('POST', '/:transaction', ops);
336
+ }
337
+ const cfg = {
338
+ ...DEFAULT_RETRY,
339
+ ...(opts.retry === true ? {} : opts.retry),
340
+ };
341
+ let attempt = 0;
342
+ // eslint-disable-next-line no-constant-condition
343
+ while (true) {
344
+ try {
345
+ return await this.makeRequest('POST', '/:transaction', ops);
346
+ }
347
+ catch (error) {
348
+ if (!(error instanceof SwellError) ||
349
+ !error.isRetryable ||
350
+ attempt >= cfg.limit) {
351
+ throw error;
352
+ }
353
+ const delay = Math.min(cfg.base * 2 ** attempt, cfg.max);
354
+ const wait = cfg.jitter ? delay * (0.5 + Math.random() * 0.5) : delay;
355
+ await new Promise((resolve) => setTimeout(resolve, wait));
356
+ attempt++;
357
+ }
358
+ }
359
+ }
323
360
  }
361
+ const DEFAULT_RETRY = Object.freeze({
362
+ limit: 3,
363
+ base: 100,
364
+ max: 5000,
365
+ jitter: true,
366
+ });
367
+ const RETRYABLE_CODES = new Set([
368
+ 'transaction_conflict',
369
+ 'transaction_throttled',
370
+ ]);
324
371
  /**
325
372
  * Class representing a Swell error.
326
373
  */
@@ -331,6 +378,9 @@ class SwellError extends Error {
331
378
  if (typeof message === 'string') {
332
379
  formattedMessage = message;
333
380
  }
381
+ else if (typeof body?.error?.message === 'string') {
382
+ formattedMessage = body.error.message;
383
+ }
334
384
  else {
335
385
  formattedMessage = JSON.stringify(message, null, 2);
336
386
  }
@@ -341,6 +391,10 @@ class SwellError extends Error {
341
391
  this.name = 'SwellError';
342
392
  this.status = options.status || 500;
343
393
  this.body = body;
394
+ this.code = options.code || body?.error?.code;
395
+ }
396
+ get isRetryable() {
397
+ return RETRYABLE_CODES.has(this.code);
344
398
  }
345
399
  }
346
400
  /**
@@ -363,7 +417,7 @@ class SwellResponse extends Response {
363
417
  ...options,
364
418
  headers: {
365
419
  ...resultHeaders,
366
- ...(options.headers || {}),
420
+ ...options.headers,
367
421
  },
368
422
  });
369
423
  // Saved for future access
@@ -396,9 +450,9 @@ class SwellResponse extends Response {
396
450
  }
397
451
  static async _consumeNativeResponse(response) {
398
452
  const headers = {};
399
- response.headers.forEach((value, key) => {
453
+ for (const [key, value] of response.headers.entries()) {
400
454
  headers[key] = value;
401
- });
455
+ }
402
456
  try {
403
457
  const text = await response.text();
404
458
  let data;
@@ -413,8 +467,8 @@ class SwellResponse extends Response {
413
467
  headers,
414
468
  });
415
469
  }
416
- catch (err) {
417
- return new SwellResponse({ error: `Unable to read response body: ${err.message}` }, { status: 500 });
470
+ catch (error) {
471
+ return new SwellResponse({ error: `Unable to read response body: ${error.message}` }, { status: 500 });
418
472
  }
419
473
  }
420
474
  static _respondWithLogs(response, req) {
@@ -217,7 +217,8 @@
217
217
  "options": [
218
218
  "api",
219
219
  "function",
220
- "webhook"
220
+ "webhook",
221
+ "transaction"
221
222
  ],
222
223
  "type": "option"
223
224
  }
@@ -720,7 +721,8 @@
720
721
  "swell app dev",
721
722
  "swell app dev --storefront-id <id>",
722
723
  "swell app dev --port 3000",
723
- "swell app dev --port 3000 --frontend-port 4000"
724
+ "swell app dev --port 3000 --frontend-port 4000",
725
+ "swell app dev --function my-function"
724
726
  ],
725
727
  "flags": {
726
728
  "app-path": {
@@ -751,6 +753,13 @@
751
753
  "multiple": false,
752
754
  "type": "option"
753
755
  },
756
+ "function": {
757
+ "description": "run only the named function",
758
+ "name": "function",
759
+ "hasDynamicHelp": false,
760
+ "multiple": false,
761
+ "type": "option"
762
+ },
754
763
  "storefront-id": {
755
764
  "description": "for storefront apps, identify a storefront to preview and push theme files to",
756
765
  "name": "storefront-id",
@@ -800,7 +809,7 @@
800
809
  "name": "versions"
801
810
  }
802
811
  },
803
- "description": "The command shows the latest information about your app including the name,\ndescription, version, public ID, test store, and more. If the versions argument is passed,\nit will output all versions of the app instead.",
812
+ "description": "The command shows the latest information about your app including the name,\ndescription, version, public ID, test store, and more. If the \u001b[35m\u001b[1mversions\u001b[22m\u001b[39m argument is passed,\nit will output all versions of the app instead.",
804
813
  "examples": [
805
814
  {
806
815
  "command": "swell app info",
@@ -1099,7 +1108,7 @@
1099
1108
  "name": "targetPath"
1100
1109
  }
1101
1110
  },
1102
- "description": "Pull all app files, a specific file, or a specific configuration\ntype from an app in your store's test environment to your local machine.\n\nIf APPID is not specified, you will be prompted with a list of apps to choose from.\n\nApp file directories:\nassets/\ncontent/\nfrontend/\ncomponents/\nfunctions/\nmodels/\nnotifications/\nsettings/\ntheme/\nwebhooks/\nfrontend/",
1111
+ "description": "Pull all app files, a specific file, or a specific configuration\ntype from an app in your store's test environment to your local machine.\n\nIf APPID is not specified, you will be prompted with a list of apps to choose from.\n\nApp file directories:\n\u001b[4massets/\u001b[24m\n\u001b[4mcontent/\u001b[24m\n\u001b[4mfrontend/\u001b[24m\n\u001b[4mcomponents/\u001b[24m\n\u001b[4mfunctions/\u001b[24m\n\u001b[4mmodels/\u001b[24m\n\u001b[4mnotifications/\u001b[24m\n\u001b[4msettings/\u001b[24m\n\u001b[4mtheme/\u001b[24m\n\u001b[4mwebhooks/\u001b[24m\n\u001b[4mfrontend/\u001b[24m",
1103
1112
  "examples": [
1104
1113
  "swell app pull",
1105
1114
  "swell app pull example_app",
@@ -1148,7 +1157,7 @@
1148
1157
  "name": "file"
1149
1158
  }
1150
1159
  },
1151
- "description": "Push all app files, a specific file, or a specific configuration\ntype to an app in your store's test environment.\n\nIf the app does not exist, it will be created and its global ID saved to a .swellrc file.\n\n- If no file is specified, all configuration files will be pushed to the store.\n This includes the app icon (assets/icon.png) and swell.json.\n- If a file is specified, only that file will be pushed to the store.\n- If a directory is specified, only files in that directory will be pushed.\n\nApp file directories:\nassets/\ncontent/\nfrontend/\ncomponents/\nfunctions/\nmodels/\nnotifications/\nsettings/\ntheme/\nwebhooks/",
1160
+ "description": "Push all app files, a specific file, or a specific configuration\ntype to an app in your store's test environment.\n\nIf the app does not exist, it will be created and its global ID saved to a .swellrc file.\n\n- If no file is specified, all configuration files will be pushed to the store.\n This includes the app icon (assets/icon.png) and swell.json.\n- If a file is specified, only that file will be pushed to the store.\n- If a directory is specified, only files in that directory will be pushed.\n\nApp file directories:\n\u001b[4massets/\u001b[24m\n\u001b[4mcontent/\u001b[24m\n\u001b[4mfrontend/\u001b[24m\n\u001b[4mcomponents/\u001b[24m\n\u001b[4mfunctions/\u001b[24m\n\u001b[4mmodels/\u001b[24m\n\u001b[4mnotifications/\u001b[24m\n\u001b[4msettings/\u001b[24m\n\u001b[4mtheme/\u001b[24m\n\u001b[4mwebhooks/\u001b[24m",
1152
1161
  "examples": [
1153
1162
  "swell app push",
1154
1163
  "swell app push content",
@@ -3063,7 +3072,7 @@
3063
3072
  "name": "targetPath"
3064
3073
  }
3065
3074
  },
3066
- "description": "Pull all theme files, a specific file, or a specific configuration\ntype from a theme in your store's test environment to your local machine.\n\nIf APPID is not specified, you will be prompted with a list of themes to choose from.\n\nTheme file directories:\nassets/\ntheme/",
3075
+ "description": "Pull all theme files, a specific file, or a specific configuration\ntype from a theme in your store's test environment to your local machine.\n\nIf APPID is not specified, you will be prompted with a list of themes to choose from.\n\nTheme file directories:\n\u001b[4massets/\u001b[24m\n\u001b[4mtheme/\u001b[24m",
3067
3076
  "examples": [
3068
3077
  "swell theme pull",
3069
3078
  "swell theme pull mytheme",
@@ -3134,7 +3143,7 @@
3134
3143
  "name": "file"
3135
3144
  }
3136
3145
  },
3137
- "description": "Push all theme files, a specific file, or a specific configuration\ntype to an theme in your store's test environment.\n\nIf the theme does not exist, it will be created and its global ID saved to a .swellrc file.\n\n- If no file is specified, all configuration files will be pushed to the store.\n This includes the theme icon (assets/icon.png) and swell.json.\n- If a file is specified, only that file will be pushed to the store.\n- If a directory is specified, only files in that directory will be pushed.\n\nTheme file directories:\nassets/\ntheme/",
3146
+ "description": "Push all theme files, a specific file, or a specific configuration\ntype to an theme in your store's test environment.\n\nIf the theme does not exist, it will be created and its global ID saved to a .swellrc file.\n\n- If no file is specified, all configuration files will be pushed to the store.\n This includes the theme icon (assets/icon.png) and swell.json.\n- If a file is specified, only that file will be pushed to the store.\n- If a directory is specified, only files in that directory will be pushed.\n\nTheme file directories:\n\u001b[4massets/\u001b[24m\n\u001b[4mtheme/\u001b[24m",
3138
3147
  "examples": [
3139
3148
  "swell theme push",
3140
3149
  "swell theme push assets",
@@ -3307,6 +3316,13 @@
3307
3316
  "multiple": false,
3308
3317
  "type": "option"
3309
3318
  },
3319
+ "function": {
3320
+ "description": "run only the named function",
3321
+ "name": "function",
3322
+ "hasDynamicHelp": false,
3323
+ "multiple": false,
3324
+ "type": "option"
3325
+ },
3310
3326
  "storefront-id": {
3311
3327
  "description": "identify a storefront to preview with and push theme files to",
3312
3328
  "name": "storefront-id",
@@ -3362,5 +3378,5 @@
3362
3378
  ]
3363
3379
  }
3364
3380
  },
3365
- "version": "2.7.1"
3381
+ "version": "2.9.0"
3366
3382
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swell/cli",
3
- "version": "2.7.1",
3
+ "version": "2.9.0",
4
4
  "type": "module",
5
5
  "description": "Swell's command line interface/utility",
6
6
  "keywords": [
@@ -70,7 +70,7 @@
70
70
  "@types/inquirer": "9.0.6",
71
71
  "@types/localtunnel": "2.0.4",
72
72
  "@types/mocha": "10.0.3",
73
- "@types/node": "20.8.8",
73
+ "@types/node": "22.10.0",
74
74
  "@types/qs": "6.9.15",
75
75
  "@types/ws": "8.18.1",
76
76
  "@typescript-eslint/eslint-plugin": "6.9.0",
@@ -121,7 +121,7 @@
121
121
  "publish-alpha": "npm version prerelease --preid=alpha && npm publish --tag alpha"
122
122
  },
123
123
  "engines": {
124
- "node": ">= 18.16.1"
124
+ "node": ">= 22.0.0"
125
125
  },
126
126
  "types": "dist/index.d.ts"
127
127
  }