@swell/cli 2.0.1-alpha.11 → 2.0.1-alpha.13

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.
@@ -50,7 +50,10 @@ ${Object.values(ConfigPaths)
50
50
  const file = fileArg || fileFlag;
51
51
  const currentStore = swellConfig.getDefaultStore();
52
52
  this.api.setStoreEnv(currentStore, 'test');
53
- this.app = await this.getAppWithConfig('', false);
53
+ this.app = await this.getAppWithConfig('').catch((_err) => {
54
+ // Ignore so we can continue to create it
55
+ return {};
56
+ });
54
57
  const confirmExistingApp = await this.confirmRemoveInstalledApp(currentStore);
55
58
  if (confirmExistingApp === false) {
56
59
  return;
@@ -60,9 +63,7 @@ ${Object.values(ConfigPaths)
60
63
  if (!confirmExistingApp?.id) {
61
64
  const otherAppName = this.app.name || this.appConfig.get('name');
62
65
  const confirmPushNew = await confirm({
63
- message: `${style.appConfigValue(otherAppName)} has a dev version in another ${this.app.client_id
64
- ? `store ${style.dim(`(${this.app.client_id})`)}`
65
- : 'environment'}. Do you want to create a new development instance in ${style.storeId(currentStore)}?`,
66
+ message: `${style.appConfigValue(otherAppName)} has a dev version in another environment. Do you want to create a new development instance in ${style.storeId(currentStore)}?`,
66
67
  default: false,
67
68
  });
68
69
  if (!confirmPushNew) {
@@ -39,21 +39,14 @@ have available for logging in to.
39
39
  // noop
40
40
  }
41
41
  }
42
- if (!user) {
43
- const [sessionId, storeId] = await getSessionIdOrLoginInBrowser(store);
44
- this.api.setStoreEnv(storeId);
45
- user = await this.api.get({ adminPath: 'user' }, { sessionId });
46
- await this.getAndSetStoreConfig({ sessionId, storeId });
47
- await modifyDefaultStore({ currentStoreId, storeId });
48
- config.setUser(user);
49
- this.log(`You are now logged in to ${style.storeId(storeId)} as ${style.currentUser(user.username)}.`);
50
- process.exit(0);
51
- }
52
- this.log(`You are already logged in as ${style.currentUser(user.username)}.`);
53
- this.log([
54
- `Use ${style.command('swell switch')}`,
55
- 'to login to another account.',
56
- ].join(' '));
42
+ const [sessionId, storeId] = await getSessionIdOrLoginInBrowser(store);
43
+ this.api.setStoreEnv(storeId);
44
+ user = await this.api.get({ adminPath: 'user' }, { sessionId });
45
+ await this.getAndSetStoreConfig({ sessionId, storeId });
46
+ await modifyDefaultStore({ currentStoreId, storeId });
47
+ config.setUser(user);
48
+ this.log(`You are now logged in to ${style.storeId(storeId)} as ${style.currentUser(user.username)}.`);
49
+ process.env.NODE_ENV !== 'test' && process.exit(0);
57
50
  }
58
51
  /**
59
52
  * Use the /user/switch API request to either get the session id for the
@@ -28,7 +28,7 @@ export async function bundleFunction(filePath) {
28
28
  // eslint-disable-next-line no-new-func
29
29
  const evalFn = new Function(`${code}\nreturn { ...moduleExports }`);
30
30
  const { config, ...moduleExports } = evalFn();
31
- code += getSwellFunctionWrapper();
31
+ code = await minifyCode(code + getSwellFunctionWrapper());
32
32
  return { code, config, dependencies, moduleExports };
33
33
  }
34
34
  catch (error) {
@@ -41,3 +41,16 @@ function getSwellFunctionWrapper() {
41
41
  const __dirname = path.dirname(__filename);
42
42
  return fs.readFileSync(path.join(__dirname, './swell-function-wrapper.js'), 'utf8');
43
43
  }
44
+ async function minifyCode(code) {
45
+ try {
46
+ const result = await esbuild.transform(code, {
47
+ minifyWhitespace: true,
48
+ minifyIdentifiers: false,
49
+ minifySyntax: false,
50
+ });
51
+ return result.code;
52
+ }
53
+ catch (err) {
54
+ throw new Error('Compilation error:', err.message);
55
+ }
56
+ }
@@ -83,6 +83,6 @@ export class LoggedItem {
83
83
  }
84
84
  })
85
85
  .join('\n');
86
- return `Function ${event ? `${event.type} >` : ''} ${style.logMethod(method.toUpperCase())} /${name} ${logLines ? `\n${logLines}` : ''}`;
86
+ return `Function ${event ? `${event.hook ? `${event.hook}:` : ''}${event.type} >` : ''} ${style.logMethod(method.toUpperCase())} /${name} ${logLines ? `\n${logLines}` : ''}`;
87
87
  }
88
88
  }
@@ -1,4 +1,11 @@
1
- declare function handleRequest(originalRequest: any, _env: any, context: any): Promise<Response>;
1
+ /**
2
+ * Handle a function request.
3
+ * @param {Request} originalRequest from cloudflare
4
+ * @param {*} _env cloudflare environment vars
5
+ * @param {*} context cloudflare conttext
6
+ * @returns {Promise<SwellResponse>}
7
+ */
8
+ declare function request(originalRequest: Request, _env: any, context: any): Promise<SwellResponse>;
2
9
  /**
3
10
  * Invokes one of the function types that are available.
4
11
  *
@@ -35,10 +42,19 @@ declare function handleRequest(originalRequest: any, _env: any, context: any): P
35
42
  * @param {Event} context
36
43
  */
37
44
  declare function executeModuleHandler(req: SwellRequest, context: Event): Promise<any>;
45
+ /**
46
+ * Helper to determine if a value is an ordinary object.
47
+ * @param {any} obj
48
+ * @returns {boolean}
49
+ */
50
+ declare function isOrdinaryObject(val: any): boolean;
38
51
  declare const origialConsoleLog: {
39
52
  (...data: any[]): void;
40
53
  (message?: any, ...optionalParams: any[]): void;
41
54
  };
55
+ /**
56
+ * Class representing a Swell request.
57
+ */
42
58
  declare class SwellRequest {
43
59
  constructor(req: any, context: any);
44
60
  originalRequest: any;
@@ -62,8 +78,21 @@ declare class SwellRequest {
62
78
  url: URL | undefined;
63
79
  parseJson(input: any): any;
64
80
  log(level: any, ...line: any[]): void;
81
+ getIngestableLogs(response: any): {
82
+ params: any;
83
+ } | undefined;
65
84
  ingestLogs(response: any): Promise<void>;
85
+ /**
86
+ * Merge values into app data for the current request.
87
+ * @param {object|string|undefined} idOrValues string to indicate app ID, or values to merge
88
+ * @param {object|undefined} values values to merge into app data
89
+ * @returns {object|undefined} existing app data merged with values if passed
90
+ */
91
+ appValues(idOrValues?: object | string | undefined, values?: object | undefined): object | undefined;
66
92
  }
93
+ /**
94
+ * Class representing the Swell backend API.
95
+ */
67
96
  declare class SwellAPI {
68
97
  constructor(req: any, context: any);
69
98
  request: any;
@@ -79,10 +108,20 @@ declare class SwellAPI {
79
108
  delete(url: any, data: any): Promise<any>;
80
109
  settings(id?: any): Promise<any>;
81
110
  }
111
+ /**
112
+ * Class representing a Swell error.
113
+ */
82
114
  declare class SwellError extends Error {
83
115
  constructor(message: any, options?: {});
84
116
  status: any;
85
117
  }
118
+ /**
119
+ * Class representing a Swell response.
120
+ */
86
121
  declare class SwellResponse extends Response {
122
+ static _respond(req: any, response: any, context: any): any;
123
+ static _respondWithLogs(response: any, req: any): SwellResponse;
87
124
  constructor(data: any, options?: {});
125
+ _swellData: any;
126
+ _swellOptions: {};
88
127
  }
@@ -1,9 +1,16 @@
1
1
  "use strict";
2
2
  const origialConsoleLog = console.log;
3
3
  addEventListener('fetch', (event) => {
4
- event.respondWith(handleRequest(event.request, event.env, event));
4
+ event.respondWith(request(event.request, event.env, event));
5
5
  });
6
- async function handleRequest(originalRequest, _env, context) {
6
+ /**
7
+ * Handle a function request.
8
+ * @param {Request} originalRequest from cloudflare
9
+ * @param {*} _env cloudflare environment vars
10
+ * @param {*} context cloudflare conttext
11
+ * @returns {Promise<SwellResponse>}
12
+ */
13
+ async function request(originalRequest, _env, context) {
7
14
  const req = new SwellRequest(originalRequest, context);
8
15
  await req.initialize();
9
16
  let response;
@@ -12,16 +19,10 @@ async function handleRequest(originalRequest, _env, context) {
12
19
  }
13
20
  catch (err) {
14
21
  // Log the error for Swell
15
- console.error(err.stack);
22
+ console.error(err);
16
23
  response = new SwellResponse({ error: err.message }, { status: err.status || 500 });
17
24
  }
18
- // Create a response by default if one is not returned
19
- if (!(response instanceof Response)) {
20
- response = new SwellResponse(response);
21
- }
22
- // Send logs back to Swell
23
- context.waitUntil(req.ingestLogs(response));
24
- return response;
25
+ return SwellResponse._respond(req, response, context);
25
26
  }
26
27
  /**
27
28
  * Invokes one of the function types that are available.
@@ -74,6 +75,9 @@ async function executeModuleHandler(req, context) {
74
75
  }
75
76
  throw new Error(`Function does not export a method to handle ${method.toUpperCase()} requests`);
76
77
  }
78
+ /**
79
+ * Class representing a Swell request.
80
+ */
77
81
  class SwellRequest {
78
82
  constructor(req, context) {
79
83
  this.originalRequest = req;
@@ -141,11 +145,11 @@ class SwellRequest {
141
145
  origialConsoleLog(...line);
142
146
  this._logs.push({
143
147
  date: Date.now(),
144
- line: line.map((l) => JSON.stringify(l)),
148
+ line: line.map((l) => (l instanceof Error ? l.stack : JSON.stringify(l))),
145
149
  ...(level !== 'info' ? { level } : {}),
146
150
  });
147
151
  }
148
- async ingestLogs(response) {
152
+ getIngestableLogs(response) {
149
153
  if (!this.logParams) {
150
154
  return;
151
155
  }
@@ -153,23 +157,51 @@ class SwellRequest {
153
157
  this.logParams.time = Date.now() - this.logParams.$start;
154
158
  delete this.logParams.$start;
155
159
  }
156
- const result = await this.swell.post('/:logs', {
157
- $ingest_function_logs: {
158
- params: {
159
- ...this.logParams,
160
- message: {
161
- ...this.logParams?.message,
162
- logs: this._logs,
163
- status: response.status,
164
- },
160
+ return {
161
+ params: {
162
+ ...this.logParams,
163
+ message: {
164
+ ...this.logParams?.message,
165
+ logs: this._logs,
166
+ status: response.status,
165
167
  },
166
168
  },
169
+ };
170
+ }
171
+ async ingestLogs(response) {
172
+ const ingestableLogs = this.getIngestableLogs(response);
173
+ if (!ingestableLogs) {
174
+ return;
175
+ }
176
+ const result = await this.swell.post('/:logs', {
177
+ $ingest_function_logs: ingestableLogs,
167
178
  });
168
179
  if (!result?.success) {
169
180
  console.error('Error ingesting logs', result);
170
181
  }
171
182
  }
183
+ /**
184
+ * Merge values into app data for the current request.
185
+ * @param {object|string|undefined} idOrValues string to indicate app ID, or values to merge
186
+ * @param {object|undefined} values values to merge into app data
187
+ * @returns {object|undefined} existing app data merged with values if passed
188
+ */
189
+ appValues(idOrValues = undefined, values = undefined) {
190
+ const appId = typeof idOrValues === 'string' ? appIdOrValues : this.appId;
191
+ const appValues = typeof idOrValues === 'string' ? values : idOrValues;
192
+ if (!appId || !isOrdinaryObject(appValues)) {
193
+ return undefined;
194
+ }
195
+ return {
196
+ $app: {
197
+ [appId]: appValues,
198
+ },
199
+ };
200
+ }
172
201
  }
202
+ /**
203
+ * Class representing the Swell backend API.
204
+ */
173
205
  class SwellAPI {
174
206
  constructor(req, context) {
175
207
  this.request = req;
@@ -261,6 +293,9 @@ class SwellAPI {
261
293
  return this.makeRequest('GET', `/settings/${id}`);
262
294
  }
263
295
  }
296
+ /**
297
+ * Class representing a Swell error.
298
+ */
264
299
  class SwellError extends Error {
265
300
  constructor(message, options = {}) {
266
301
  let formattedMessage;
@@ -278,10 +313,13 @@ class SwellError extends Error {
278
313
  this.status = options.status || 500;
279
314
  }
280
315
  }
316
+ /**
317
+ * Class representing a Swell response.
318
+ */
281
319
  class SwellResponse extends Response {
282
320
  constructor(data, options = {}) {
283
321
  const resultHeaders = {};
284
- let result;
322
+ let result = '';
285
323
  if (typeof data === 'string') {
286
324
  result = data;
287
325
  resultHeaders['Content-Type'] = 'text/plain;charset=UTF-8';
@@ -298,5 +336,52 @@ class SwellResponse extends Response {
298
336
  ...(options.headers || {}),
299
337
  },
300
338
  });
339
+ // Saved for future access
340
+ this._swellData = data;
341
+ this._swellOptions = options || {};
301
342
  }
343
+ static _respond(req, response, context) {
344
+ let finalResponse = response;
345
+ // Convert a plain Response instance to SwellResponse
346
+ if (finalResponse instanceof Response &&
347
+ !(finalResponse instanceof SwellResponse)) {
348
+ finalResponse = new SwellResponse(response.body, {
349
+ status: response.status,
350
+ headers: response.headers,
351
+ });
352
+ }
353
+ else if (!(finalResponse instanceof SwellResponse)) {
354
+ finalResponse = new SwellResponse(response);
355
+ }
356
+ // Send logs back with the response for event hooks
357
+ if (req.data?.$event?.hook) {
358
+ return SwellResponse._respondWithLogs(finalResponse, req);
359
+ }
360
+ // Ingest logs in the background
361
+ context.waitUntil(req.ingestLogs(finalResponse));
362
+ return finalResponse;
363
+ }
364
+ static _respondWithLogs(response, req) {
365
+ const ingestableLogs = req.getIngestableLogs(response);
366
+ // Rebuild response with logs
367
+ const responseData = response instanceof SwellResponse ? response?._swellData : response;
368
+ const resultData = ingestableLogs
369
+ ? {
370
+ $logs: ingestableLogs,
371
+ $data: responseData,
372
+ }
373
+ : responseData;
374
+ return new SwellResponse(resultData, response?._swellOptions);
375
+ }
376
+ }
377
+ /**
378
+ * Helper to determine if a value is an ordinary object.
379
+ * @param {any} obj
380
+ * @returns {boolean}
381
+ */
382
+ function isOrdinaryObject(val) {
383
+ return (typeof val === 'object' &&
384
+ val !== null &&
385
+ Object.getPrototypeOf(val) === Object.prototype);
302
386
  }
387
+ ;
@@ -12,7 +12,7 @@ export declare abstract class RemoteAppCommand extends AppCommand {
12
12
  }, storeId?: string): string;
13
13
  protected deleteConfig(configId: string): Promise<any>;
14
14
  protected getApp(id?: string): Promise<App>;
15
- protected getAppWithConfig(id?: string, errorNotFound?: boolean): Promise<App>;
15
+ protected getAppWithConfig(id?: string): Promise<App>;
16
16
  protected getCreateUpdateApp(updateApp?: App): Promise<App>;
17
17
  protected handleRequestErrors(request: () => any, spinnerError?: () => void): Promise<any>;
18
18
  init(): Promise<void>;
@@ -51,7 +51,7 @@ export class RemoteAppCommand extends AppCommand {
51
51
  this.debugJson('getApp: remote app', app);
52
52
  return app;
53
53
  }
54
- async getAppWithConfig(id = '', errorNotFound = true) {
54
+ async getAppWithConfig(id = '') {
55
55
  const swellId = this.app?.id;
56
56
  const appConfigId = this.appConfig.get('id');
57
57
  if (!appConfigId) {
@@ -90,7 +90,7 @@ export class RemoteAppCommand extends AppCommand {
90
90
  // we don't create a loop.
91
91
  //
92
92
  // we don't throw an error ever sometimes, like when pushing an app
93
- const shouldThrow = !errorNotFound && (errorJson.error.message !== 'App not found' || id);
93
+ const shouldThrow = errorJson.error.message !== 'App not found' || id;
94
94
  if (shouldThrow) {
95
95
  const errorMessage = typeof errorJson.error === 'string'
96
96
  ? errorJson.error
@@ -201,6 +201,10 @@ export class RemoteAppCommand extends AppCommand {
201
201
  this.showErrors(jsonError, spinnerError);
202
202
  this.error('API request failed');
203
203
  }
204
+ else if (typeof error.message === 'string') {
205
+ this.showErrors(error, spinnerError);
206
+ this.error(error.message);
207
+ }
204
208
  throw error;
205
209
  }
206
210
  }
@@ -1086,5 +1086,5 @@
1086
1086
  ]
1087
1087
  }
1088
1088
  },
1089
- "version": "2.0.1-alpha.11"
1089
+ "version": "2.0.1-alpha.13"
1090
1090
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swell/cli",
3
- "version": "2.0.1-alpha.11",
3
+ "version": "2.0.1-alpha.13",
4
4
  "type": "module",
5
5
  "description": "Swell's command line interface/utility",
6
6
  "keywords": [