@swell/cli 2.5.8 → 2.6.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.
@@ -9,6 +9,7 @@ export default class ApiDelete extends SwellApiCommand {
9
9
  static flags: {
10
10
  live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
11
  api: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ header: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
13
  };
13
14
  static examples: string[];
14
15
  get method(): HttpMethod;
@@ -1,6 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { HttpMethod } from '../../lib/api.js';
3
- import { SwellApiCommand } from '../../swell-api-command.js';
3
+ import { SwellApiCommand, headerFlag } from '../../swell-api-command.js';
4
4
  export default class ApiDelete extends SwellApiCommand {
5
5
  static summary = 'Send a DELETE request to the Swell API.';
6
6
  static description = `Remove existing resources from the Swell API.
@@ -21,10 +21,12 @@ export default class ApiDelete extends SwellApiCommand {
21
21
  options: ['backend', 'frontend'],
22
22
  default: 'backend',
23
23
  }),
24
+ header: headerFlag,
24
25
  };
25
26
  static examples = [
26
27
  'swell api delete /products/abc123',
27
28
  'swell api delete /functions/my-app/clear-cache',
29
+ `swell api delete /functions/my-app/clear-cache -H 'Authorization: Bearer xxx'`,
28
30
  'swell api delete /products/abc123 --api frontend',
29
31
  ];
30
32
  get method() {
@@ -9,6 +9,7 @@ export default class ApiGet extends SwellApiCommand {
9
9
  static flags: {
10
10
  live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
11
  api: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ header: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
13
  };
13
14
  static examples: string[];
14
15
  get method(): HttpMethod;
@@ -1,6 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { HttpMethod } from '../../lib/api.js';
3
- import { SwellApiCommand } from '../../swell-api-command.js';
3
+ import { SwellApiCommand, headerFlag } from '../../swell-api-command.js';
4
4
  export default class ApiGet extends SwellApiCommand {
5
5
  static summary = 'Send a GET request to the Swell API.';
6
6
  static description = `Retrieve data from the Swell API.
@@ -22,12 +22,14 @@ export default class ApiGet extends SwellApiCommand {
22
22
  options: ['backend', 'frontend'],
23
23
  default: 'backend',
24
24
  }),
25
+ header: headerFlag,
25
26
  };
26
27
  static examples = [
27
28
  'swell api get /products',
28
29
  'swell api get "/products?limit=10&category=shoes"',
29
30
  'swell api get /products/abc123',
30
31
  'swell api get /functions/my-app/get-inventory',
32
+ `swell api get /functions/my-app/get-inventory -H 'Authorization: Bearer xxx'`,
31
33
  'swell api get /products --api frontend',
32
34
  ];
33
35
  get method() {
@@ -10,6 +10,7 @@ export default class ApiPost extends SwellApiCommand {
10
10
  body: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
11
  live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
12
12
  api: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
+ header: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
14
  };
14
15
  static examples: string[];
15
16
  get method(): HttpMethod;
@@ -1,6 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { HttpMethod } from '../../lib/api.js';
3
- import { SwellApiCommand } from '../../swell-api-command.js';
3
+ import { SwellApiCommand, headerFlag } from '../../swell-api-command.js';
4
4
  export default class ApiPost extends SwellApiCommand {
5
5
  static summary = 'Send a POST request to the Swell API.';
6
6
  static description = `Create new resources in the Swell API.
@@ -25,11 +25,13 @@ export default class ApiPost extends SwellApiCommand {
25
25
  options: ['backend', 'frontend'],
26
26
  default: 'backend',
27
27
  }),
28
+ header: headerFlag,
28
29
  };
29
30
  static examples = [
30
31
  'swell api post /products --body \'{"name": "Product", "price": 100}\'',
31
32
  'swell api post /products --body ./product.json',
32
33
  'swell api post /functions/my-app/create-order --body \'{"items":["sku-1"]}\'',
34
+ `swell api post /functions/my-app/webhook --body @./payload.json -H 'Stripe-Signature: t=123,v1=abc'`,
33
35
  `swell api post /products --body '{"name": "Product"}' --api frontend`,
34
36
  ];
35
37
  get method() {
@@ -10,6 +10,7 @@ export default class ApiPut extends SwellApiCommand {
10
10
  body: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
11
  live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
12
12
  api: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
+ header: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
14
  };
14
15
  static examples: string[];
15
16
  get method(): HttpMethod;
@@ -1,6 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { HttpMethod } from '../../lib/api.js';
3
- import { SwellApiCommand } from '../../swell-api-command.js';
3
+ import { SwellApiCommand, headerFlag } from '../../swell-api-command.js';
4
4
  export default class ApiPut extends SwellApiCommand {
5
5
  static summary = 'Send a PUT request to the Swell API.';
6
6
  static description = `Update existing resources in the Swell API.
@@ -25,11 +25,13 @@ export default class ApiPut extends SwellApiCommand {
25
25
  options: ['backend', 'frontend'],
26
26
  default: 'backend',
27
27
  }),
28
+ header: headerFlag,
28
29
  };
29
30
  static examples = [
30
31
  'swell api put /products/{id} --body \'{"id": "abc123", "name": "Updated"}\'',
31
32
  'swell api put /products/abc123 --body ./product.json',
32
33
  'swell api put /functions/my-app/update-stock --body \'{"sku":"abc","qty":10}\'',
34
+ `swell api put /functions/my-app/update-stock --body '{"sku":"abc"}' -H 'X-Idempotency-Key: 42'`,
33
35
  `swell api put /products/abc123 --body '{"name": "Updated"}' --api frontend`,
34
36
  ];
35
37
  get method() {
@@ -257,11 +257,8 @@ ENVIRONMENT = "development"
257
257
  .map((m) => String(m).toUpperCase())
258
258
  .join(', '))}`);
259
259
  }
260
- if (config.route.headers) {
261
- const headers = Object.entries(config.route.headers).map(([key, value]) => `${key}: ${value}`);
262
- this.log(` Headers:`);
263
- for (const header of headers)
264
- this.log(` ${style.dim(header)}`);
260
+ if (config.route.headers?.length) {
261
+ this.log(` Headers: ${style.dim(config.route.headers.join(', '))}`);
265
262
  }
266
263
  if (config.route.cache?.timeout) {
267
264
  this.log(` Cache timeout: ${style.dim(config.route.cache.timeout)}`);
@@ -97,7 +97,18 @@ export function createMockRequest(options: MockRequestOptions = {}): SwellReques
97
97
  const targetAppId =
98
98
  typeof idOrValues === "string" ? idOrValues : resolvedAppId;
99
99
  const appValues = typeof idOrValues === "string" ? values : idOrValues;
100
- if (!targetAppId || !appValues) return undefined;
100
+ if (!targetAppId) {
101
+ throw new Error("appValues: missing app id (req.appId is empty)");
102
+ }
103
+ if (
104
+ typeof appValues !== "object" ||
105
+ appValues === null ||
106
+ Object.getPrototypeOf(appValues) !== Object.prototype
107
+ ) {
108
+ throw new Error(
109
+ "appValues: values must be a plain object (arrays, class instances, null, and primitives are not allowed)"
110
+ );
111
+ }
101
112
  return {
102
113
  $app: {
103
114
  [targetAppId]: appValues,
@@ -2,6 +2,7 @@ export function setupGlobalsTemplate() {
2
2
  return `\
3
3
  class SwellErrorImpl extends Error {
4
4
  status: number;
5
+ body?: unknown;
5
6
 
6
7
  constructor(message: string | object, options: { status?: number } = {}) {
7
8
  const text =
@@ -12,6 +13,7 @@ class SwellErrorImpl extends Error {
12
13
  super(text);
13
14
  this.name = "SwellError";
14
15
  this.status = options.status ?? 500;
16
+ this.body = typeof message === "string" ? undefined : message;
15
17
  }
16
18
  }
17
19
 
@@ -88,6 +88,7 @@ declare class SwellRequest {
88
88
  id: any;
89
89
  isLocalDev: boolean;
90
90
  swell: SwellAPI;
91
+ rawBody: string;
91
92
  body: {};
92
93
  query: {};
93
94
  data: {};
@@ -106,9 +107,10 @@ declare class SwellRequest {
106
107
  * Merge values into app data for the current request.
107
108
  * @param {object|string} idOrValues string to indicate app ID, or values to merge
108
109
  * @param {object|undefined} values values to merge into app data
109
- * @returns {object|undefined} existing app data merged with values if passed
110
+ * @returns {object} existing app data merged with values
111
+ * @throws {Error} if app id is missing or values is not a plain object
110
112
  */
111
- appValues(idOrValues: object | string, values?: object | undefined): object | undefined;
113
+ appValues(idOrValues: object | string, values?: object | undefined): object;
112
114
  }
113
115
  /**
114
116
  * Class representing the Swell backend API.
@@ -134,12 +136,14 @@ declare class SwellAPI {
134
136
  declare class SwellError extends Error {
135
137
  constructor(message: any, options?: {});
136
138
  status: any;
139
+ body: any;
137
140
  }
138
141
  /**
139
142
  * Class representing a Swell response.
140
143
  */
141
144
  declare class SwellResponse extends Response {
142
- static _respond(req: any, response: any, context: any): any;
145
+ static _respond(req: any, response: any, context: any): Promise<any>;
146
+ static _consumeNativeResponse(response: any): Promise<SwellResponse>;
143
147
  static _respondWithLogs(response: any, req: any): SwellResponse;
144
148
  constructor(data: any, options?: {});
145
149
  _swellData: any;
@@ -90,6 +90,8 @@ class SwellRequest {
90
90
  this.assignRequestProps(req);
91
91
  // Set environment specific variables
92
92
  this.context = context;
93
+ // Slug-form app identifier (e.g. 'klaviyo') matching keys in record.$app[...].
94
+ // Derived from the app's private_id with the leading underscore stripped.
93
95
  this.appId = req.headers.get('Swell-App-Id');
94
96
  this.storeId = req.headers.get('Swell-Store-Id');
95
97
  this.accessToken = req.headers.get('Swell-Access-Token');
@@ -105,11 +107,14 @@ class SwellRequest {
105
107
  this.swell = new SwellAPI(this, context);
106
108
  // URL of the original request
107
109
  this.url;
108
- // Original body of the request, JSON if applicable
110
+ // Raw request body text, untouched by parsing.
111
+ // Use on route triggers for HMAC/webhook signature verification.
112
+ this.rawBody = '';
113
+ // Parsed JSON body as object, or raw text string when body isn't JSON.
109
114
  this.body = {};
110
115
  // URL query parameters as an object
111
116
  this.query = {};
112
- // Combined object of body and query parameters
117
+ // Combined object of body and query parameters (query keys overwrite body keys)
113
118
  this.data = {};
114
119
  // Internal logs
115
120
  this._logs = [];
@@ -120,9 +125,10 @@ class SwellRequest {
120
125
  });
121
126
  }
122
127
  async initialize() {
123
- this.body = await this.originalRequest.text();
128
+ this.rawBody = await this.originalRequest.text();
129
+ this.body = this.rawBody;
124
130
  try {
125
- this.data = JSON.parse(this.body);
131
+ this.data = JSON.parse(this.rawBody);
126
132
  this.body = { ...this.data };
127
133
  }
128
134
  catch (err) {
@@ -202,13 +208,17 @@ class SwellRequest {
202
208
  * Merge values into app data for the current request.
203
209
  * @param {object|string} idOrValues string to indicate app ID, or values to merge
204
210
  * @param {object|undefined} values values to merge into app data
205
- * @returns {object|undefined} existing app data merged with values if passed
211
+ * @returns {object} existing app data merged with values
212
+ * @throws {Error} if app id is missing or values is not a plain object
206
213
  */
207
214
  appValues(idOrValues, values = undefined) {
208
- const appId = typeof idOrValues === 'string' ? appIdOrValues : this.appId;
215
+ const appId = typeof idOrValues === 'string' ? idOrValues : this.appId;
209
216
  const appValues = typeof idOrValues === 'string' ? values : idOrValues;
210
- if (!appId || !isOrdinaryObject(appValues)) {
211
- return undefined;
217
+ if (!appId) {
218
+ throw new Error('appValues: missing app id (req.appId is empty)');
219
+ }
220
+ if (!isOrdinaryObject(appValues)) {
221
+ throw new Error('appValues: values must be a plain object (arrays, class instances, null, and primitives are not allowed)');
212
222
  }
213
223
  return {
214
224
  $app: {
@@ -316,6 +326,7 @@ class SwellAPI {
316
326
  */
317
327
  class SwellError extends Error {
318
328
  constructor(message, options = {}) {
329
+ const body = typeof message === 'string' ? undefined : message;
319
330
  let formattedMessage;
320
331
  if (typeof message === 'string') {
321
332
  formattedMessage = message;
@@ -329,6 +340,7 @@ class SwellError extends Error {
329
340
  super(formattedMessage);
330
341
  this.name = 'SwellError';
331
342
  this.status = options.status || 500;
343
+ this.body = body;
332
344
  }
333
345
  }
334
346
  /**
@@ -358,27 +370,53 @@ class SwellResponse extends Response {
358
370
  this._swellData = data;
359
371
  this._swellOptions = options || {};
360
372
  }
361
- static _respond(req, response, context) {
373
+ static async _respond(req, response, context) {
362
374
  let finalResponse = response;
363
- // Convert a plain Response instance to SwellResponse
375
+ const isHook = Boolean(req.data?.$event?.hook);
364
376
  if (finalResponse instanceof Response &&
365
377
  !(finalResponse instanceof SwellResponse)) {
366
- finalResponse = new SwellResponse(response.body, {
367
- status: response.status,
368
- headers: response.headers,
369
- });
378
+ // Non-hook responses pass through unchanged so the handler's body,
379
+ // status, and headers reach the caller intact. Hooks need the parsed
380
+ // body so $logs can be merged into the payload below.
381
+ if (isHook) {
382
+ finalResponse =
383
+ await SwellResponse._consumeNativeResponse(finalResponse);
384
+ }
370
385
  }
371
386
  else if (!(finalResponse instanceof SwellResponse)) {
372
387
  finalResponse = new SwellResponse(response);
373
388
  }
374
389
  // Send logs back with the response for event hooks
375
- if (req.data?.$event?.hook) {
390
+ if (isHook) {
376
391
  return SwellResponse._respondWithLogs(finalResponse, req);
377
392
  }
378
393
  // Ingest logs in the background
379
394
  context.waitUntil(req.ingestLogs(finalResponse));
380
395
  return finalResponse;
381
396
  }
397
+ static async _consumeNativeResponse(response) {
398
+ const headers = {};
399
+ response.headers.forEach((value, key) => {
400
+ headers[key] = value;
401
+ });
402
+ try {
403
+ const text = await response.text();
404
+ let data;
405
+ try {
406
+ data = JSON.parse(text);
407
+ }
408
+ catch {
409
+ data = text;
410
+ }
411
+ return new SwellResponse(data, {
412
+ status: response.status,
413
+ headers,
414
+ });
415
+ }
416
+ catch (err) {
417
+ return new SwellResponse({ error: `Unable to read response body: ${err.message}` }, { status: 500 });
418
+ }
419
+ }
382
420
  static _respondWithLogs(response, req) {
383
421
  const ingestableLogs = req.getIngestableLogs(response);
384
422
  // Rebuild response with logs
@@ -1,5 +1,6 @@
1
1
  import { HttpMethod } from './lib/api.js';
2
2
  import { SwellCommand } from './swell-command.js';
3
+ export declare const headerFlag: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
3
4
  export declare abstract class SwellApiCommand extends SwellCommand {
4
5
  protected abstract get method(): HttpMethod;
5
6
  protected request(command: typeof SwellApiCommand, requestOptions?: Api.RequestOptions): Promise<void>;
@@ -18,6 +19,7 @@ export declare abstract class SwellApiCommand extends SwellCommand {
18
19
  * Build a function invocation request via the admin /:functions endpoint.
19
20
  */
20
21
  private buildFunctionCallRequest;
22
+ private parseHeaders;
21
23
  private parseQueryString;
22
24
  private processBody;
23
25
  private isFilePath;
@@ -1,3 +1,4 @@
1
+ import { Flags } from '@oclif/core';
1
2
  import * as fs from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { FetchError } from 'node-fetch';
@@ -7,6 +8,13 @@ import { SwellCommand } from './swell-command.js';
7
8
  const FUNCTION_PATH_REGEX = /^\/functions\/([^/]+)\/([^/?]+)(\?.*)?$/;
8
9
  // Pattern to match /functions/{functionId} with optional query string
9
10
  const FUNCTION_DIRECT_REGEX = /^\/functions\/([^/?]+)(\?.*)?$/;
11
+ // Shared --header / -H flag for function-call paths. Pass once per header.
12
+ // Only forwarded as $call.headers on /functions/* paths; ignored elsewhere.
13
+ export const headerFlag = Flags.string({
14
+ char: 'H',
15
+ description: "HTTP header to forward to a function (format: 'Name: value'). Repeat for multiple. Only applies to /functions/* paths.",
16
+ multiple: true,
17
+ });
10
18
  export class SwellApiCommand extends SwellCommand {
11
19
  async request(command, requestOptions = {}) {
12
20
  const { paths, options, methodOverride } = await this.parseCommand(command);
@@ -29,7 +37,7 @@ export class SwellApiCommand extends SwellCommand {
29
37
  const parsedInput = await super.parse(options, argv);
30
38
  const { args, flags } = parsedInput;
31
39
  const { path: requestPath } = args;
32
- const { live, api, body } = flags;
40
+ const { live, api, body, header } = flags;
33
41
  const isFrontendAPI = api === 'frontend';
34
42
  if (!live) {
35
43
  await this.api.setEnv('test');
@@ -41,21 +49,30 @@ export class SwellApiCommand extends SwellCommand {
41
49
  throw new Error('Path must start with a forward slash (/)');
42
50
  }
43
51
  const processedBody = await this.processBody(body);
52
+ const callHeaders = this.parseHeaders(header);
44
53
  // Check if this is a function call by name: /functions/{appId}/{functionName}
45
54
  // Must check this first (more specific pattern)
46
55
  const functionMatch = requestPath.match(FUNCTION_PATH_REGEX);
47
56
  if (functionMatch) {
48
57
  const [, appId, functionName, queryString] = functionMatch;
49
58
  const functionId = await this.resolveFunctionId(appId, functionName);
50
- const queryParams = this.parseQueryString(queryString);
51
- return this.buildFunctionCallRequest(parsedInput, functionId, processedBody, queryParams);
59
+ return this.buildFunctionCallRequest(parsedInput, {
60
+ functionId,
61
+ body: processedBody,
62
+ query: this.parseQueryString(queryString),
63
+ headers: callHeaders,
64
+ });
52
65
  }
53
66
  // Check if this is a direct function call: /functions/{functionId}
54
67
  const directMatch = requestPath.match(FUNCTION_DIRECT_REGEX);
55
68
  if (directMatch) {
56
69
  const [, functionId, queryString] = directMatch;
57
- const queryParams = this.parseQueryString(queryString);
58
- return this.buildFunctionCallRequest(parsedInput, functionId, processedBody, queryParams);
70
+ return this.buildFunctionCallRequest(parsedInput, {
71
+ functionId,
72
+ body: processedBody,
73
+ query: this.parseQueryString(queryString),
74
+ headers: callHeaders,
75
+ });
59
76
  }
60
77
  const paths = isFrontendAPI
61
78
  ? { frontendPath: requestPath }
@@ -108,27 +125,49 @@ export class SwellApiCommand extends SwellCommand {
108
125
  /**
109
126
  * Build a function invocation request via the admin /:functions endpoint.
110
127
  */
111
- buildFunctionCallRequest(parsedInput, functionId, bodyData, queryParams) {
128
+ buildFunctionCallRequest(parsedInput, call) {
112
129
  // Merge query params with body data (body takes precedence)
113
- // Only merge if bodyData is a plain object; otherwise use bodyData or query params alone
114
- const mergedData = this.isPlainObject(bodyData)
115
- ? { ...queryParams, ...bodyData }
116
- : bodyData ?? queryParams;
117
- const callBody = {
118
- $call: {
119
- data: mergedData,
120
- method: this.method,
121
- },
130
+ // Only merge if body is a plain object; otherwise use body or query alone
131
+ const mergedData = this.isPlainObject(call.body)
132
+ ? { ...call.query, ...call.body }
133
+ : call.body ?? call.query;
134
+ const $call = {
135
+ data: mergedData,
136
+ method: this.method,
122
137
  };
138
+ if (Object.keys(call.headers).length > 0) {
139
+ $call.headers = call.headers;
140
+ }
123
141
  return {
124
142
  ...parsedInput,
125
- paths: { adminPath: `/data/:functions/${functionId}` },
143
+ paths: { adminPath: `/data/:functions/${call.functionId}` },
126
144
  options: {
127
- body: callBody,
145
+ body: { $call },
128
146
  },
129
147
  methodOverride: HttpMethod.PUT,
130
148
  };
131
149
  }
150
+ // Parse repeated `-H "Name: value"` flag entries into a map. Splits on first colon
151
+ // so values containing colons (e.g. `Authorization: Bearer x:y`) survive intact.
152
+ parseHeaders(headerArgs) {
153
+ if (!headerArgs?.length) {
154
+ return {};
155
+ }
156
+ const result = {};
157
+ for (const entry of headerArgs) {
158
+ const colonIndex = entry.indexOf(':');
159
+ if (colonIndex < 1) {
160
+ throw new Error(`Invalid header '${entry}'. Expected format: 'Name: value'.`);
161
+ }
162
+ const name = entry.slice(0, colonIndex).trim();
163
+ const value = entry.slice(colonIndex + 1).trim();
164
+ if (!name) {
165
+ throw new Error(`Invalid header '${entry}'. Header name cannot be empty.`);
166
+ }
167
+ result[name] = value;
168
+ }
169
+ return result;
170
+ }
132
171
  parseQueryString(queryString) {
133
172
  if (!queryString) {
134
173
  return {};
@@ -385,6 +385,7 @@
385
385
  "examples": [
386
386
  "swell api delete /products/abc123",
387
387
  "swell api delete /functions/my-app/clear-cache",
388
+ "swell api delete /functions/my-app/clear-cache -H 'Authorization: Bearer xxx'",
388
389
  "swell api delete /products/abc123 --api frontend"
389
390
  ],
390
391
  "flags": {
@@ -405,6 +406,14 @@
405
406
  "frontend"
406
407
  ],
407
408
  "type": "option"
409
+ },
410
+ "header": {
411
+ "char": "H",
412
+ "description": "HTTP header to forward to a function (format: 'Name: value'). Repeat for multiple. Only applies to /functions/* paths.",
413
+ "name": "header",
414
+ "hasDynamicHelp": false,
415
+ "multiple": true,
416
+ "type": "option"
408
417
  }
409
418
  },
410
419
  "hasDynamicHelp": false,
@@ -438,6 +447,7 @@
438
447
  "swell api get \"/products?limit=10&category=shoes\"",
439
448
  "swell api get /products/abc123",
440
449
  "swell api get /functions/my-app/get-inventory",
450
+ "swell api get /functions/my-app/get-inventory -H 'Authorization: Bearer xxx'",
441
451
  "swell api get /products --api frontend"
442
452
  ],
443
453
  "flags": {
@@ -458,6 +468,14 @@
458
468
  "frontend"
459
469
  ],
460
470
  "type": "option"
471
+ },
472
+ "header": {
473
+ "char": "H",
474
+ "description": "HTTP header to forward to a function (format: 'Name: value'). Repeat for multiple. Only applies to /functions/* paths.",
475
+ "name": "header",
476
+ "hasDynamicHelp": false,
477
+ "multiple": true,
478
+ "type": "option"
461
479
  }
462
480
  },
463
481
  "hasDynamicHelp": false,
@@ -521,6 +539,7 @@
521
539
  "swell api post /products --body '{\"name\": \"Product\", \"price\": 100}'",
522
540
  "swell api post /products --body ./product.json",
523
541
  "swell api post /functions/my-app/create-order --body '{\"items\":[\"sku-1\"]}'",
542
+ "swell api post /functions/my-app/webhook --body @./payload.json -H 'Stripe-Signature: t=123,v1=abc'",
524
543
  "swell api post /products --body '{\"name\": \"Product\"}' --api frontend"
525
544
  ],
526
545
  "flags": {
@@ -548,6 +567,14 @@
548
567
  "frontend"
549
568
  ],
550
569
  "type": "option"
570
+ },
571
+ "header": {
572
+ "char": "H",
573
+ "description": "HTTP header to forward to a function (format: 'Name: value'). Repeat for multiple. Only applies to /functions/* paths.",
574
+ "name": "header",
575
+ "hasDynamicHelp": false,
576
+ "multiple": true,
577
+ "type": "option"
551
578
  }
552
579
  },
553
580
  "hasDynamicHelp": false,
@@ -580,6 +607,7 @@
580
607
  "swell api put /products/{id} --body '{\"id\": \"abc123\", \"name\": \"Updated\"}'",
581
608
  "swell api put /products/abc123 --body ./product.json",
582
609
  "swell api put /functions/my-app/update-stock --body '{\"sku\":\"abc\",\"qty\":10}'",
610
+ "swell api put /functions/my-app/update-stock --body '{\"sku\":\"abc\"}' -H 'X-Idempotency-Key: 42'",
583
611
  "swell api put /products/abc123 --body '{\"name\": \"Updated\"}' --api frontend"
584
612
  ],
585
613
  "flags": {
@@ -607,6 +635,14 @@
607
635
  "frontend"
608
636
  ],
609
637
  "type": "option"
638
+ },
639
+ "header": {
640
+ "char": "H",
641
+ "description": "HTTP header to forward to a function (format: 'Name: value'). Repeat for multiple. Only applies to /functions/* paths.",
642
+ "name": "header",
643
+ "hasDynamicHelp": false,
644
+ "multiple": true,
645
+ "type": "option"
610
646
  }
611
647
  },
612
648
  "hasDynamicHelp": false,
@@ -2969,5 +3005,5 @@
2969
3005
  ]
2970
3006
  }
2971
3007
  },
2972
- "version": "2.5.8"
3008
+ "version": "2.6.0"
2973
3009
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swell/cli",
3
- "version": "2.5.8",
3
+ "version": "2.6.0",
4
4
  "type": "module",
5
5
  "description": "Swell's command line interface/utility",
6
6
  "keywords": [