@swell/cli 2.9.23 → 2.9.25

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,7 +9,7 @@ import * as path from 'node:path';
9
9
  import ora from 'ora';
10
10
  import { ConfigType, allConfigFilesInDir, appConfigFromFile, } from '../../lib/apps/index.js';
11
11
  import { bundleFunction } from '../../lib/bundle.js';
12
- import { getLocalFrontendHost } from '../../lib/constants.js';
12
+ import { FUNCTION_COMPATIBILITY_DATE, getLocalFrontendHost, } from '../../lib/constants.js';
13
13
  import { analyzeFunctionSource, hasKindDiagnostic, } from '../../lib/function-source-analysis.js';
14
14
  import { detectPackageManager, getExecSpawnArgs, } from '../../lib/package-manager.js';
15
15
  import style from '../../lib/style.js';
@@ -255,7 +255,7 @@ export default class AppDev extends PushAppCommand {
255
255
  return `
256
256
  name = "${functionName}"
257
257
  main = "${bundledPath}"
258
- compatibility_date = "2023-05-18"
258
+ compatibility_date = "${FUNCTION_COMPATIBILITY_DATE}"
259
259
 
260
260
  [vars]
261
261
  ENVIRONMENT = "development"
@@ -1,5 +1,5 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import { Ajv } from 'ajv';
2
+ import { Ajv2020 } from 'ajv/dist/2020.js';
3
3
  import ajvErrors from 'ajv-errors';
4
4
  import addFormats from 'ajv-formats';
5
5
  import * as fs from 'node:fs/promises';
@@ -200,7 +200,8 @@ export default class Schema extends SwellCommand {
200
200
  throw new Error(`Failed to fetch bundled schema: ${response.statusText}`);
201
201
  }
202
202
  const schema = await response.json();
203
- const ajv = new Ajv({
203
+ // The schemas declare draft 2020-12, which the default Ajv class rejects
204
+ const ajv = new Ajv2020({
204
205
  allErrors: true,
205
206
  strict: false,
206
207
  });
@@ -211,7 +212,7 @@ export default class Schema extends SwellCommand {
211
212
  if (!valid) {
212
213
  const { errors } = validate;
213
214
  const errorLength = errors?.length;
214
- let errorTitle = 'Invalid model definition';
215
+ let errorTitle = `Invalid ${type} definition`;
215
216
  if (typeof errorLength === 'number' && errorLength > 1) {
216
217
  errorTitle += ` (${errorLength} errors)`;
217
218
  }
@@ -222,7 +223,7 @@ export default class Schema extends SwellCommand {
222
223
  }
223
224
  throw new Error(`${errorTitle}\n${errorMessage}`);
224
225
  }
225
- this.log('Valid model definition');
226
+ this.log(`Valid ${type} definition`);
226
227
  }
227
228
  async validateFunction(file) {
228
229
  const accessError = await fs.access(file, fs.constants.F_OK).then(() => null, (error) => error);
@@ -1,6 +1,7 @@
1
1
  export declare const API_BASE_URL: string;
2
2
  export declare const LOCAL_PROXY_PROVIDER: string;
3
3
  export declare const SCHEMAS_BASE_URL: string;
4
+ export declare const FUNCTION_COMPATIBILITY_DATE = "2023-05-18";
4
5
  export declare function getAdminApiBaseUrl(storeId: string): string;
5
6
  export declare function getFrontendApiBaseUrl(storeId: string): string;
6
7
  export declare function getLoginHost(storeId?: string): string;
@@ -17,6 +17,9 @@ const STOREFRONT_FRONTEND_HOST = env.get('STOREFRONT_FRONTEND_HOST') ||
17
17
  defaultEnv.STOREFRONT_FRONTEND_HOST;
18
18
  export const LOCAL_PROXY_PROVIDER = env.get('LOCAL_PROXY_PROVIDER') || defaultEnv.LOCAL_PROXY_PROVIDER;
19
19
  export const SCHEMAS_BASE_URL = env.get('SCHEMAS_BASE_URL') || defaultEnv.SCHEMAS_BASE_URL;
20
+ // Workers compatibility date for running app functions locally, in swell app
21
+ // dev and in tests
22
+ export const FUNCTION_COMPATIBILITY_DATE = '2023-05-18';
20
23
  // helpers to get constants with replacements
21
24
  export function getAdminApiBaseUrl(storeId) {
22
25
  return ADMIN_API_BASE_URL.replace('${STORE_ID}', storeId);
@@ -46,7 +46,12 @@ export function createMockRequest(options: MockRequestOptions = {}): SwellReques
46
46
  } = options;
47
47
 
48
48
  const requestHeaders = new Headers(headers);
49
- const requestQuery = { ...Object.fromEntries(new URL(url).searchParams), ...query };
49
+ // The app tsconfig's webworker lib does not type URLSearchParams as iterable
50
+ const urlQuery: Record<string, string> = {};
51
+ new URL(url).searchParams.forEach((value, key) => {
52
+ urlQuery[key] = value;
53
+ });
54
+ const requestQuery = { ...urlQuery, ...query };
50
55
  const originalRequest = new Request(url, {
51
56
  method,
52
57
  headers: requestHeaders,
@@ -35,10 +35,37 @@ class SwellRejectionImpl extends Error {
35
35
  }
36
36
  }
37
37
 
38
+ // Builds the body and content type like the deployed function runtime
39
+ class SwellResponseImpl extends Response {
40
+ constructor(
41
+ data?: string | object,
42
+ options: ResponseInit & { headers?: Record<string, string> } = {},
43
+ ) {
44
+ const headers: Record<string, string> = {};
45
+ let body = "";
46
+
47
+ if (typeof data === "string") {
48
+ body = data;
49
+ headers["Content-Type"] = "text/plain;charset=UTF-8";
50
+ } else if (data !== undefined) {
51
+ body = JSON.stringify(data, null, 2);
52
+ headers["Content-Type"] = "application/json;charset=UTF-8";
53
+ }
54
+
55
+ super(body, {
56
+ status: 200,
57
+ ...options,
58
+ headers: { ...headers, ...options.headers },
59
+ });
60
+ }
61
+ }
62
+
38
63
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
64
  (globalThis as any).SwellError = SwellError;
40
65
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
66
  (globalThis as any).SwellRejection = SwellRejectionImpl;
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ (globalThis as any).SwellResponse = SwellResponseImpl;
42
69
 
43
70
  export {};
44
71
  `;
@@ -1,3 +1,4 @@
1
+ import { FUNCTION_COMPATIBILITY_DATE } from '../../../constants.js';
1
2
  export function vitestConfigTemplate(options) {
2
3
  return `\
3
4
  import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
@@ -106,6 +107,7 @@ export default defineWorkersConfig({
106
107
  singleWorker: true,
107
108
  isolatedStorage: true,
108
109
  miniflare: {
110
+ compatibilityDate: "${FUNCTION_COMPATIBILITY_DATE}",
109
111
  bindings: {
110
112
  SWELL_STORE_ID: sdkAuth.storeId,
111
113
  SWELL_SESSION_ID: sdkAuth.sessionId,
@@ -82,6 +82,7 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
82
82
  logDashboardUrl(flags: {
83
83
  [flag: string]: any;
84
84
  }): void;
85
+ logDeployedStorefrontUrl(): Promise<void>;
85
86
  logStorefrontConnected(): void;
86
87
  logStorefrontFrontendUrl(storefront?: any, branchId?: string): void;
87
88
  logReleaseDetailsHint(error: any): void;
@@ -286,6 +286,9 @@ export class PushAppCommand extends RemoteAppCommand {
286
286
  }
287
287
  await this.handleRequestErrors(async () => this.api.post({ adminPath: `/apps/${this.app.id}/frontend-package` }, { body: { package: serialized } }));
288
288
  this.log(`Deployed managed frontend package ${pkg.digest}.`);
289
+ if (log !== false) {
290
+ await this.logDeployedStorefrontUrl();
291
+ }
289
292
  return;
290
293
  }
291
294
  if (this.app.frontend?.hosting === 'managed' &&
@@ -306,10 +309,10 @@ export class PushAppCommand extends RemoteAppCommand {
306
309
  await this.updateFrontendDeployment(currentStore, projectType, deploymentUrl, deploymentHash);
307
310
  }
308
311
  if (log !== false) {
309
- if (this.app.type === 'storefront' && this.storefront) {
310
- this.logStorefrontFrontendUrl(this.storefront);
312
+ if (this.app.type === 'storefront') {
313
+ await this.logDeployedStorefrontUrl();
311
314
  }
312
- else if (this.app.type !== 'storefront') {
315
+ else {
313
316
  // Get updated app with `installed` property
314
317
  this.app = await this.getApp(this.app.id);
315
318
  this.logAppFrontendUrl();
@@ -737,6 +740,21 @@ export class PushAppCommand extends RemoteAppCommand {
737
740
  const currentStore = localConfig.getDefaultStore();
738
741
  this.log(`View the ${style.appConfigValue(this.app.name)} app in your dashboard at ${style.link(this.dashboardUrl(flags, currentStore))}.`);
739
742
  }
743
+ // `swell app push` deploys without choosing a storefront, so fall back to
744
+ // the app's latest one in this environment
745
+ async logDeployedStorefrontUrl() {
746
+ if (this.app.type !== 'storefront') {
747
+ return;
748
+ }
749
+ // The frontend is already deployed, so a failed lookup only skips the link
750
+ const storefront = this.storefront ||
751
+ (await this.api
752
+ .get({ adminPath: `/apps/${this.app.id}/storefront` })
753
+ .catch(() => null));
754
+ if (storefront) {
755
+ this.logStorefrontFrontendUrl(storefront);
756
+ }
757
+ }
740
758
  logStorefrontConnected() {
741
759
  if (!this.storefront) {
742
760
  this.error('Storefront not connected');
@@ -3606,5 +3606,5 @@
3606
3606
  ]
3607
3607
  }
3608
3608
  },
3609
- "version": "2.9.23"
3609
+ "version": "2.9.25"
3610
3610
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swell/cli",
3
- "version": "2.9.23",
3
+ "version": "2.9.25",
4
4
  "type": "module",
5
5
  "description": "Swell's command line interface/utility",
6
6
  "keywords": [
@@ -117,7 +117,8 @@
117
117
  "test": "mocha --forbid-only --ignore \"**/node_modules/**\" \"test/**/*.test.ts\"",
118
118
  "publish-alpha": "npm version prerelease --preid=alpha && npm publish --tag alpha",
119
119
  "test:frontend-packages": "npm run build && node test/fixtures/managed-frontend/verify.mjs && node test/fixtures/managed-frontend/verify-react-worker.mjs",
120
- "test:frontend-scaffolds": "npm run build && node test/fixtures/managed-frontend/verify-scaffolds.mjs"
120
+ "test:frontend-scaffolds": "npm run build && node test/fixtures/managed-frontend/verify-scaffolds.mjs",
121
+ "test:tests-scaffold": "npm run build && node test/fixtures/tests-scaffold/verify.mjs"
121
122
  },
122
123
  "engines": {
123
124
  "node": ">= 22.0.0"