@swell/cli 2.1.0 → 2.2.1

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 (112) 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 +9 -9
  12. package/dist/commands/app/dev.js +163 -139
  13. package/dist/commands/app/frontend/dev.d.ts +1 -1
  14. package/dist/commands/app/frontend/dev.js +84 -78
  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/index.js +5 -1
  30. package/dist/commands/create/model.d.ts +4 -0
  31. package/dist/commands/create/model.js +120 -25
  32. package/dist/commands/create/tests.d.ts +15 -0
  33. package/dist/commands/create/tests.js +79 -0
  34. package/dist/commands/inspect/content.d.ts +25 -0
  35. package/dist/commands/inspect/content.js +159 -0
  36. package/dist/commands/inspect/index.d.ts +7 -0
  37. package/dist/commands/inspect/index.js +16 -0
  38. package/dist/commands/inspect/models.d.ts +27 -0
  39. package/dist/commands/inspect/models.js +197 -0
  40. package/dist/commands/schema.d.ts +27 -0
  41. package/dist/commands/schema.js +231 -0
  42. package/dist/commands/theme/dev.d.ts +1 -1
  43. package/dist/commands/theme/dev.js +2 -2
  44. package/dist/commands/theme/init.d.ts +1 -1
  45. package/dist/commands/theme/init.js +1 -1
  46. package/dist/commands/theme/pull.d.ts +2 -2
  47. package/dist/commands/theme/pull.js +2 -2
  48. package/dist/commands/theme/push.d.ts +1 -1
  49. package/dist/commands/theme/push.js +1 -1
  50. package/dist/create-app-command.d.ts +12 -2
  51. package/dist/create-app-command.js +103 -80
  52. package/dist/create-collection-command.js +4 -1
  53. package/dist/create-config-command.js +4 -0
  54. package/dist/env/local.d.ts +1 -0
  55. package/dist/env/local.js +1 -0
  56. package/dist/env/production.d.ts +1 -0
  57. package/dist/env/production.js +1 -0
  58. package/dist/env/review.d.ts +1 -0
  59. package/dist/env/review.js +1 -0
  60. package/dist/env/staging.d.ts +1 -0
  61. package/dist/env/staging.js +1 -0
  62. package/dist/lib/api.d.ts +16 -5
  63. package/dist/lib/api.js +67 -25
  64. package/dist/lib/app-config.js +1 -0
  65. package/dist/lib/apps/index.d.ts +2 -0
  66. package/dist/lib/apps/index.js +20 -14
  67. package/dist/lib/config.d.ts +1 -1
  68. package/dist/lib/constants.d.ts +1 -0
  69. package/dist/lib/constants.js +1 -0
  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/create/tests/templates/env-dts.d.ts +1 -0
  78. package/dist/lib/create/tests/templates/env-dts.js +14 -0
  79. package/dist/lib/create/tests/templates/index.d.ts +8 -0
  80. package/dist/lib/create/tests/templates/index.js +8 -0
  81. package/dist/lib/create/tests/templates/integration-test.d.ts +1 -0
  82. package/dist/lib/create/tests/templates/integration-test.js +19 -0
  83. package/dist/lib/create/tests/templates/mock-request.d.ts +2 -0
  84. package/dist/lib/create/tests/templates/mock-request.js +112 -0
  85. package/dist/lib/create/tests/templates/setup-globals.d.ts +1 -0
  86. package/dist/lib/create/tests/templates/setup-globals.js +23 -0
  87. package/dist/lib/create/tests/templates/swell-client.d.ts +1 -0
  88. package/dist/lib/create/tests/templates/swell-client.js +126 -0
  89. package/dist/lib/create/tests/templates/tsconfig.d.ts +1 -0
  90. package/dist/lib/create/tests/templates/tsconfig.js +15 -0
  91. package/dist/lib/create/tests/templates/unit-test.d.ts +1 -0
  92. package/dist/lib/create/tests/templates/unit-test.js +42 -0
  93. package/dist/lib/create/tests/templates/vitest-config.d.ts +2 -0
  94. package/dist/lib/create/tests/templates/vitest-config.js +121 -0
  95. package/dist/lib/create/tests/types.d.ts +21 -0
  96. package/dist/lib/create/tests/types.js +1 -0
  97. package/dist/lib/create/tests.d.ts +4 -0
  98. package/dist/lib/create/tests.js +113 -0
  99. package/dist/lib/sessions.js +4 -3
  100. package/dist/lib/style.js +0 -1
  101. package/dist/lib/theme-sync.d.ts +6 -6
  102. package/dist/lib/theme-sync.js +49 -39
  103. package/dist/push-app-command.d.ts +7 -7
  104. package/dist/push-app-command.js +38 -36
  105. package/dist/remote-app-command.d.ts +1 -1
  106. package/dist/remote-app-command.js +4 -2
  107. package/dist/swell-api-command.d.ts +13 -0
  108. package/dist/swell-api-command.js +75 -0
  109. package/dist/swell-command.d.ts +1 -1
  110. package/dist/swell-command.js +9 -9
  111. package/oclif.manifest.json +639 -46
  112. package/package.json +8 -1
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' },
@@ -125,6 +125,8 @@ export interface FrontendProjectType {
125
125
  slug: string;
126
126
  }
127
127
  export declare const FrontendProjectTypes: FrontendProjectType[];
128
+ export declare function getFrontendProjectSlugs(withNone?: boolean, withLegacy?: boolean): string[];
129
+ export declare function getFrontendProjectValidValues(withNone?: boolean, withLegacy?: boolean): string;
128
130
  export declare function getAppSlugId(app: App): string | undefined;
129
131
  export declare function getFrontendProjectType(appPath: string): FrontendProjectType | undefined;
130
132
  export declare function getConfigTypeFromPath(path: string): ConfigType | undefined;
@@ -46,7 +46,7 @@ const ConfigTypeBatchOrder = [
46
46
  ConfigType.FRONTEND,
47
47
  ConfigType.FILE,
48
48
  ];
49
- // Configs available for different app types
49
+ // Configs available for admin/integration app types
50
50
  const StandardConfigTypes = [
51
51
  'MODEL',
52
52
  'CONTENT',
@@ -140,7 +140,7 @@ export const FrontendProjectTypes = [
140
140
  },
141
141
  {
142
142
  buildCommand: 'npm exec --workspace=frontend -- opennextjs-cloudflare build',
143
- //devCommand: 'npm exec --workspace=frontend -- opennextjs-cloudflare build && npm exec --workspace=frontend -- opennextjs-cloudflare preview --port=${PORT}',
143
+ // devCommand: 'npm exec --workspace=frontend -- opennextjs-cloudflare build && npm exec --workspace=frontend -- opennextjs-cloudflare preview --port=${PORT}',
144
144
  devCommand: 'npm exec --workspace=frontend -- next dev --turbopack --port ${PORT}',
145
145
  installCommand: 'npm create cloudflare@latest -- frontend --framework=next --deploy=false --git=false -- --typescript --use-npm --src-dir --app --eslint --import-alias "@/*" --tailwind --turbopack',
146
146
  mainPackage: 'next',
@@ -148,6 +148,19 @@ export const FrontendProjectTypes = [
148
148
  slug: 'nextjs',
149
149
  },
150
150
  ];
151
+ export function getFrontendProjectSlugs(withNone = true, withLegacy = true) {
152
+ const types = withLegacy
153
+ ? FrontendProjectTypes
154
+ : FrontendProjectTypes.filter((projectType) => projectType.installCommand);
155
+ const slugs = types.map((projectType) => projectType.slug);
156
+ if (withNone) {
157
+ slugs.push('none');
158
+ }
159
+ return slugs;
160
+ }
161
+ export function getFrontendProjectValidValues(withNone = true, withLegacy = true) {
162
+ return getFrontendProjectSlugs(withNone, withLegacy).join(', ');
163
+ }
151
164
  export function getAppSlugId(app) {
152
165
  return toAppId(app.private_id) || app.public_id || app.id;
153
166
  }
@@ -158,7 +171,7 @@ function hasWorkspaceStructure(appPath) {
158
171
  return false;
159
172
  }
160
173
  try {
161
- const content = fs.readFileSync(rootPkgPath, 'utf-8');
174
+ const content = fs.readFileSync(rootPkgPath, 'utf8');
162
175
  const pkg = JSON.parse(content);
163
176
  return Array.isArray(pkg.workspaces) && pkg.workspaces.includes('frontend');
164
177
  }
@@ -182,7 +195,7 @@ export function getFrontendProjectType(appPath) {
182
195
  continue;
183
196
  }
184
197
  try {
185
- const content = fs.readFileSync(pkgPath, 'utf-8');
198
+ const content = fs.readFileSync(pkgPath, 'utf8');
186
199
  const pkg = JSON.parse(content);
187
200
  for (const projectType of projectTypes) {
188
201
  if (pkg.dependencies?.[projectType.mainPackage] ||
@@ -190,16 +203,9 @@ export function getFrontendProjectType(appPath) {
190
203
  // Create a copy to avoid mutating the original
191
204
  const detectedType = { ...projectType };
192
205
  // If buildCommand not explicitly set in framework definition, detect it
193
- if (detectedType.buildCommand === undefined) {
194
- // Check if package.json has a "build" script
195
- if (pkg.scripts?.build) {
196
- detectedType.buildCommand = 'npm run build';
197
- }
198
- else {
199
- // No build script found, set to empty string (no build needed)
200
- detectedType.buildCommand = '';
201
- }
202
- }
206
+ detectedType.buildCommand ||=
207
+ // Check if package.json has a "build" script, if not set to empty string (no build needed)
208
+ pkg.scripts?.build ? 'npm run build' : '';
203
209
  return detectedType;
204
210
  }
205
211
  }
@@ -8,8 +8,8 @@ declare function getAll(): any;
8
8
  declare function getDefaultStore(): string;
9
9
  declare function setDefaultStore(storeId: string): void;
10
10
  declare function getDefaultStorefront(appPath: string): {
11
- id: string;
12
11
  env?: string;
12
+ id: string;
13
13
  };
14
14
  declare function setDefaultStorefront(appPath: string, storefrontId: string, env?: string): void;
15
15
  declare function getSessionId(storeId: string): any;
@@ -1,5 +1,6 @@
1
1
  export declare const API_BASE_URL: any;
2
2
  export declare const LOCAL_PROXY_PROVIDER: any;
3
+ export declare const SCHEMAS_BASE_URL: any;
3
4
  export declare function getAdminApiBaseUrl(storeId: string): any;
4
5
  export declare function getLoginHost(storeId?: string): any;
5
6
  export declare function getAppFrontendHost(storeId: string, installedAppId: string): any;
@@ -14,6 +14,7 @@ const STOREFRONT_FRONTEND_HOST = env.get('STOREFRONT_FRONTEND_HOST') ||
14
14
  env.get('STOREFRONT_PAGES_HOST') || // Deprecated
15
15
  defaultEnv.STOREFRONT_FRONTEND_HOST;
16
16
  export const LOCAL_PROXY_PROVIDER = env.get('LOCAL_PROXY_PROVIDER') || defaultEnv.LOCAL_PROXY_PROVIDER;
17
+ export const SCHEMAS_BASE_URL = env.get('SCHEMAS_BASE_URL') || defaultEnv.SCHEMAS_BASE_URL;
17
18
  // helpers to get constants with replacements
18
19
  export function getAdminApiBaseUrl(storeId) {
19
20
  return ADMIN_API_BASE_URL.replace('${STORE_ID}', storeId);
@@ -1,4 +1,5 @@
1
1
  interface CreateContentFileBody {
2
+ $schema: string;
2
3
  collection: string;
3
4
  description?: string;
4
5
  fields?: ContentFieldProps[];
@@ -18,6 +19,9 @@ interface ContentFieldProps {
18
19
  label: string;
19
20
  type?: ContentType;
20
21
  }
22
+ declare const validateViews: (values: string[], onError: (msg: string, options: {
23
+ exit: number;
24
+ }) => never) => void;
21
25
  declare const parseViews: (views: string[]) => ContentViewProps[];
22
26
  declare const parseFields: (fields: string[]) => ContentFieldProps[];
23
- export { CreateContentFileBody, parseFields, parseViews };
27
+ export { CreateContentFileBody, parseFields, parseViews, validateViews };
@@ -1,4 +1,11 @@
1
1
  import { titleize } from 'inflection';
2
+ const validContentViewValues = ['list', 'edit', 'new'];
3
+ const validateViews = (values, onError) => {
4
+ const invalid = values.filter((v) => !validContentViewValues.includes(v));
5
+ if (invalid.length > 0) {
6
+ onError(`Invalid view(s): ${invalid.join(', ')}\n\nValid views: ${validContentViewValues.join(', ')}\n\nExample: swell create content visitors -v list,edit,new -y`, { exit: 1 });
7
+ }
8
+ };
2
9
  const parseViews = (views) => {
3
10
  if (views.length === 0) {
4
11
  return [];
@@ -37,4 +44,4 @@ const parseFields = (fields) => {
37
44
  }
38
45
  return fieldResponse;
39
46
  };
40
- export { parseFields, parseViews };
47
+ export { parseFields, parseViews, validateViews };
@@ -7,11 +7,12 @@ interface CreateFunctionArgs {
7
7
  interface WriteFunctionFileParams {
8
8
  description: string;
9
9
  events: string;
10
+ extension?: string;
10
11
  functionName: string;
11
12
  language: FunctionLanguage;
12
13
  route: string;
13
14
  schedule: string;
14
15
  trigger: FunctionType;
15
16
  }
16
- declare const getFunctionTemplate: ({ description, events, language, route, schedule, trigger, }: WriteFunctionFileParams) => string;
17
+ declare const getFunctionTemplate: ({ description, events, extension, language, route, schedule, trigger, }: WriteFunctionFileParams) => string;
17
18
  export { CreateFunctionArgs, FunctionLanguage, FunctionType, WriteFunctionFileParams, getFunctionTemplate, };
@@ -1,4 +1,4 @@
1
- const getFunctionTemplate = ({ description, events, language, route, schedule, trigger, }) => {
1
+ const getFunctionTemplate = ({ description, events, extension, language, route, schedule, trigger, }) => {
2
2
  let typeAppend = ``;
3
3
  if (trigger === 'model') {
4
4
  const eventsList = events
@@ -24,12 +24,16 @@ const getFunctionTemplate = ({ description, events, language, route, schedule, t
24
24
  schedule: '${schedule}',
25
25
  },`;
26
26
  }
27
+ let extensionAppend = '';
28
+ if (extension) {
29
+ extensionAppend = `extension: '${extension}',\n `;
30
+ }
27
31
  return `export const config${language === 'ts' ? ': SwellConfig' : ''} = {
28
- description: '${description}'${typeAppend}
32
+ ${extensionAppend}description: '${description}'${typeAppend}
29
33
  };
30
34
 
31
35
  export default async function (req${language === 'ts' ? ': SwellRequest' : ''}) {
32
- const { swell, store, settings, data${trigger === 'route' ? ', session' : ''} } = req;
36
+ const { swell, store, data } = req;
33
37
  }
34
38
  `;
35
39
  };
@@ -1,4 +1,5 @@
1
1
  interface CreateModelFileBody {
2
+ $schema: string;
2
3
  collection?: string;
3
4
  description?: string;
4
5
  events?: ModelEventObject;
@@ -0,0 +1,6 @@
1
+ export declare const SCHEMAS: {
2
+ readonly CONTENT: "https://json.swell.store/content.json";
3
+ readonly MODEL: "https://json.swell.store/model.json";
4
+ readonly NOTIFICATION: "https://json.swell.store/notification.json";
5
+ readonly SETTING: "https://json.swell.store/setting.json";
6
+ };
@@ -0,0 +1,6 @@
1
+ export const SCHEMAS = {
2
+ CONTENT: 'https://json.swell.store/content.json',
3
+ MODEL: 'https://json.swell.store/model.json',
4
+ NOTIFICATION: 'https://json.swell.store/notification.json',
5
+ SETTING: 'https://json.swell.store/setting.json',
6
+ };
@@ -0,0 +1 @@
1
+ export declare function envDtsTemplate(): string;
@@ -0,0 +1,14 @@
1
+ export function envDtsTemplate() {
2
+ return `\
3
+ declare module "cloudflare:test" {
4
+ interface ProvidedEnv {
5
+ SWELL_STORE_ID: string;
6
+ SWELL_SESSION_ID: string;
7
+ SWELL_API_BASE_URL: string;
8
+ SWELL_APP_ID: string;
9
+ }
10
+ }
11
+
12
+ export {};
13
+ `;
14
+ }
@@ -0,0 +1,8 @@
1
+ export { envDtsTemplate } from './env-dts.js';
2
+ export { integrationTestTemplate } from './integration-test.js';
3
+ export { mockRequestTemplate } from './mock-request.js';
4
+ export { setupGlobalsTemplate } from './setup-globals.js';
5
+ export { swellClientTemplate } from './swell-client.js';
6
+ export { tsconfigTemplate } from './tsconfig.js';
7
+ export { unitTestTemplate } from './unit-test.js';
8
+ export { vitestConfigTemplate } from './vitest-config.js';
@@ -0,0 +1,8 @@
1
+ export { envDtsTemplate } from './env-dts.js';
2
+ export { integrationTestTemplate } from './integration-test.js';
3
+ export { mockRequestTemplate } from './mock-request.js';
4
+ export { setupGlobalsTemplate } from './setup-globals.js';
5
+ export { swellClientTemplate } from './swell-client.js';
6
+ export { tsconfigTemplate } from './tsconfig.js';
7
+ export { unitTestTemplate } from './unit-test.js';
8
+ export { vitestConfigTemplate } from './vitest-config.js';
@@ -0,0 +1 @@
1
+ export declare function integrationTestTemplate(): string;
@@ -0,0 +1,19 @@
1
+ export function integrationTestTemplate() {
2
+ return `\
3
+ import { describe, it, expect } from "vitest";
4
+ import { createSwellClient } from "../helpers/swell-client";
5
+
6
+ describe("Store settings (read-only)", () => {
7
+ it("loads general store settings using CLI auth", async () => {
8
+ const swell = createSwellClient();
9
+
10
+ const general = await swell.get("/settings/general");
11
+
12
+ expect(general).toBeDefined();
13
+ expect(general.id).toBe("general");
14
+ expect(general.features).toBeDefined();
15
+ expect(typeof general.features.carts).toBe("boolean");
16
+ });
17
+ });
18
+ `;
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { TestTemplateOptions } from '../types.js';
2
+ export declare function mockRequestTemplate(options: TestTemplateOptions): string;
@@ -0,0 +1,112 @@
1
+ export function mockRequestTemplate(options) {
2
+ return `\
3
+ import { env } from "cloudflare:test";
4
+ import { createSwellClient } from "./swell-client";
5
+
6
+ export interface MockRequestOptions {
7
+ data?: SwellData;
8
+ query?: { [key: string]: string };
9
+ session?: { [key: string]: any };
10
+ headers?: Record<string, string>;
11
+ method?: string;
12
+ url?: string;
13
+ swell?: Partial<SwellAPI>;
14
+ store?: Partial<SwellStore>;
15
+ appId?: string;
16
+ useRealSwell?: boolean;
17
+ }
18
+
19
+ function createDefaultSwellMock(): Partial<SwellAPI> {
20
+ const notMocked = (method: string) => () => {
21
+ throw new Error(
22
+ \`swell.\${method}() not mocked. Pass { swell: { \${method}: vi.fn() } } to createMockRequest().\`
23
+ );
24
+ };
25
+ return {
26
+ get: notMocked("get"),
27
+ post: notMocked("post"),
28
+ put: notMocked("put"),
29
+ delete: notMocked("delete"),
30
+ settings: notMocked("settings"),
31
+ };
32
+ }
33
+
34
+ export function createMockRequest(options: MockRequestOptions = {}): SwellRequest {
35
+ const {
36
+ data = {},
37
+ query = {},
38
+ session = {},
39
+ headers = {},
40
+ method = "POST",
41
+ url = "https://example.com/test",
42
+ swell,
43
+ store,
44
+ appId,
45
+ useRealSwell = false,
46
+ } = options;
47
+
48
+ const requestHeaders = new Headers(headers);
49
+ const originalRequest = new Request(url, {
50
+ method,
51
+ headers: requestHeaders,
52
+ body: method === "GET" ? undefined : JSON.stringify(data),
53
+ });
54
+
55
+ const storeId = store?.id || env.SWELL_STORE_ID || "test-store";
56
+
57
+ const resolvedStore = {
58
+ id: storeId,
59
+ url: store?.url || "",
60
+ admin_url: store?.admin_url || "",
61
+ };
62
+
63
+ const context = {
64
+ waitUntil: async (promise: Promise<unknown>) => {
65
+ await promise;
66
+ },
67
+ };
68
+
69
+ const swellClient =
70
+ swell || (useRealSwell ? createSwellClient() : createDefaultSwellMock());
71
+
72
+ const resolvedAppId = appId || env.SWELL_APP_ID || "${options.appId}";
73
+
74
+ const req: Partial<SwellRequest> & { appId: string; storeId: string } = {
75
+ originalRequest,
76
+ context,
77
+ url,
78
+ method,
79
+ headers: requestHeaders,
80
+ referrer: originalRequest.referrer,
81
+ credentials: "include",
82
+ appId: resolvedAppId,
83
+ storeId,
84
+ accessToken: null,
85
+ publicKey: null,
86
+ store: resolvedStore,
87
+ session,
88
+ apiHost: env.SWELL_API_BASE_URL || "",
89
+ logParams: undefined,
90
+ swell: swellClient as SwellAPI,
91
+ body: data,
92
+ data,
93
+ query,
94
+ initialize: async () => {},
95
+ parseJson: (input: string) => JSON.parse(input),
96
+ appValues: (idOrValues: string | SwellData, values?: SwellData) => {
97
+ const targetAppId =
98
+ typeof idOrValues === "string" ? idOrValues : resolvedAppId;
99
+ const appValues = typeof idOrValues === "string" ? values : idOrValues;
100
+ if (!targetAppId || !appValues) return undefined;
101
+ return {
102
+ $app: {
103
+ [targetAppId]: appValues,
104
+ },
105
+ };
106
+ },
107
+ };
108
+
109
+ return req as SwellRequest;
110
+ }
111
+ `;
112
+ }
@@ -0,0 +1 @@
1
+ export declare function setupGlobalsTemplate(): string;
@@ -0,0 +1,23 @@
1
+ export function setupGlobalsTemplate() {
2
+ return `\
3
+ class SwellErrorImpl extends Error {
4
+ status: number;
5
+
6
+ constructor(message: string | object, options: { status?: number } = {}) {
7
+ const text =
8
+ typeof message === "string"
9
+ ? message
10
+ : JSON.stringify(message, null, 2);
11
+
12
+ super(text);
13
+ this.name = "SwellError";
14
+ this.status = options.status ?? 500;
15
+ }
16
+ }
17
+
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ (globalThis as any).SwellError = SwellErrorImpl;
20
+
21
+ export {};
22
+ `;
23
+ }
@@ -0,0 +1 @@
1
+ export declare function swellClientTemplate(): string;