@swell/cli 2.1.0 → 2.2.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.
- package/dist/commands/api/delete.d.ts +15 -0
- package/dist/commands/api/delete.js +26 -0
- package/dist/commands/api/get.d.ts +15 -0
- package/dist/commands/api/get.js +31 -0
- package/dist/commands/api/index.d.ts +7 -0
- package/dist/commands/api/index.js +16 -0
- package/dist/commands/api/post.d.ts +16 -0
- package/dist/commands/api/post.js +33 -0
- package/dist/commands/api/put.d.ts +16 -0
- package/dist/commands/api/put.js +33 -0
- package/dist/commands/app/dev.d.ts +9 -9
- package/dist/commands/app/dev.js +158 -136
- package/dist/commands/app/frontend/dev.d.ts +1 -1
- package/dist/commands/app/frontend/dev.js +84 -78
- package/dist/commands/app/install.js +3 -3
- package/dist/commands/app/pull.d.ts +1 -1
- package/dist/commands/app/pull.js +6 -6
- package/dist/commands/app/push.d.ts +3 -3
- package/dist/commands/app/push.js +8 -6
- package/dist/commands/app/release.d.ts +1 -1
- package/dist/commands/app/release.js +1 -1
- package/dist/commands/app/version.d.ts +1 -1
- package/dist/commands/app/version.js +5 -3
- package/dist/commands/create/app.d.ts +10 -2
- package/dist/commands/create/app.js +226 -57
- package/dist/commands/create/content.d.ts +4 -0
- package/dist/commands/create/content.js +83 -15
- package/dist/commands/create/function.js +27 -5
- package/dist/commands/create/model.d.ts +4 -0
- package/dist/commands/create/model.js +120 -25
- package/dist/commands/inspect/content.d.ts +25 -0
- package/dist/commands/inspect/content.js +159 -0
- package/dist/commands/inspect/index.d.ts +7 -0
- package/dist/commands/inspect/index.js +16 -0
- package/dist/commands/inspect/models.d.ts +27 -0
- package/dist/commands/inspect/models.js +197 -0
- package/dist/commands/schema.d.ts +27 -0
- package/dist/commands/schema.js +231 -0
- package/dist/commands/theme/dev.d.ts +1 -1
- package/dist/commands/theme/dev.js +2 -2
- package/dist/commands/theme/init.d.ts +1 -1
- package/dist/commands/theme/init.js +1 -1
- package/dist/commands/theme/pull.d.ts +2 -2
- package/dist/commands/theme/pull.js +2 -2
- package/dist/commands/theme/push.d.ts +1 -1
- package/dist/commands/theme/push.js +1 -1
- package/dist/create-app-command.d.ts +12 -2
- package/dist/create-app-command.js +103 -80
- package/dist/create-collection-command.js +4 -1
- package/dist/create-config-command.js +4 -0
- package/dist/env/local.d.ts +1 -0
- package/dist/env/local.js +1 -0
- package/dist/env/production.d.ts +1 -0
- package/dist/env/production.js +1 -0
- package/dist/env/review.d.ts +1 -0
- package/dist/env/review.js +1 -0
- package/dist/env/staging.d.ts +1 -0
- package/dist/env/staging.js +1 -0
- package/dist/lib/api.d.ts +16 -5
- package/dist/lib/api.js +67 -25
- package/dist/lib/app-config.js +1 -0
- package/dist/lib/apps/index.d.ts +2 -0
- package/dist/lib/apps/index.js +20 -15
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.js +1 -0
- package/dist/lib/create/content.d.ts +5 -1
- package/dist/lib/create/content.js +8 -1
- package/dist/lib/create/function.d.ts +2 -1
- package/dist/lib/create/function.js +7 -3
- package/dist/lib/create/model.d.ts +1 -0
- package/dist/lib/create/schemas.d.ts +6 -0
- package/dist/lib/create/schemas.js +6 -0
- package/dist/lib/sessions.js +4 -3
- package/dist/lib/style.js +0 -1
- package/dist/lib/theme-sync.d.ts +6 -6
- package/dist/lib/theme-sync.js +49 -39
- package/dist/push-app-command.d.ts +7 -7
- package/dist/push-app-command.js +38 -36
- package/dist/remote-app-command.d.ts +1 -1
- package/dist/remote-app-command.js +4 -2
- package/dist/swell-api-command.d.ts +13 -0
- package/dist/swell-api-command.js +75 -0
- package/dist/swell-command.d.ts +1 -1
- package/dist/swell-command.js +9 -9
- package/package.json +8 -1
- package/oclif.manifest.json +0 -1936
package/dist/lib/apps/index.js
CHANGED
|
@@ -46,7 +46,7 @@ const ConfigTypeBatchOrder = [
|
|
|
46
46
|
ConfigType.FRONTEND,
|
|
47
47
|
ConfigType.FILE,
|
|
48
48
|
];
|
|
49
|
-
// Configs available for
|
|
49
|
+
// Configs available for admin/integration app types
|
|
50
50
|
const StandardConfigTypes = [
|
|
51
51
|
'MODEL',
|
|
52
52
|
'CONTENT',
|
|
@@ -56,7 +56,6 @@ const StandardConfigTypes = [
|
|
|
56
56
|
'NOTIFICATION',
|
|
57
57
|
'SETTING',
|
|
58
58
|
'WEBHOOK',
|
|
59
|
-
'THEME',
|
|
60
59
|
];
|
|
61
60
|
export const StorefrontConfigTypes = [
|
|
62
61
|
'FRONTEND',
|
|
@@ -140,7 +139,7 @@ export const FrontendProjectTypes = [
|
|
|
140
139
|
},
|
|
141
140
|
{
|
|
142
141
|
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}',
|
|
142
|
+
// devCommand: 'npm exec --workspace=frontend -- opennextjs-cloudflare build && npm exec --workspace=frontend -- opennextjs-cloudflare preview --port=${PORT}',
|
|
144
143
|
devCommand: 'npm exec --workspace=frontend -- next dev --turbopack --port ${PORT}',
|
|
145
144
|
installCommand: 'npm create cloudflare@latest -- frontend --framework=next --deploy=false --git=false -- --typescript --use-npm --src-dir --app --eslint --import-alias "@/*" --tailwind --turbopack',
|
|
146
145
|
mainPackage: 'next',
|
|
@@ -148,6 +147,19 @@ export const FrontendProjectTypes = [
|
|
|
148
147
|
slug: 'nextjs',
|
|
149
148
|
},
|
|
150
149
|
];
|
|
150
|
+
export function getFrontendProjectSlugs(withNone = true, withLegacy = true) {
|
|
151
|
+
const types = withLegacy
|
|
152
|
+
? FrontendProjectTypes
|
|
153
|
+
: FrontendProjectTypes.filter((projectType) => projectType.installCommand);
|
|
154
|
+
const slugs = types.map((projectType) => projectType.slug);
|
|
155
|
+
if (withNone) {
|
|
156
|
+
slugs.push('none');
|
|
157
|
+
}
|
|
158
|
+
return slugs;
|
|
159
|
+
}
|
|
160
|
+
export function getFrontendProjectValidValues(withNone = true, withLegacy = true) {
|
|
161
|
+
return getFrontendProjectSlugs(withNone, withLegacy).join(', ');
|
|
162
|
+
}
|
|
151
163
|
export function getAppSlugId(app) {
|
|
152
164
|
return toAppId(app.private_id) || app.public_id || app.id;
|
|
153
165
|
}
|
|
@@ -158,7 +170,7 @@ function hasWorkspaceStructure(appPath) {
|
|
|
158
170
|
return false;
|
|
159
171
|
}
|
|
160
172
|
try {
|
|
161
|
-
const content = fs.readFileSync(rootPkgPath, '
|
|
173
|
+
const content = fs.readFileSync(rootPkgPath, 'utf8');
|
|
162
174
|
const pkg = JSON.parse(content);
|
|
163
175
|
return Array.isArray(pkg.workspaces) && pkg.workspaces.includes('frontend');
|
|
164
176
|
}
|
|
@@ -182,7 +194,7 @@ export function getFrontendProjectType(appPath) {
|
|
|
182
194
|
continue;
|
|
183
195
|
}
|
|
184
196
|
try {
|
|
185
|
-
const content = fs.readFileSync(pkgPath, '
|
|
197
|
+
const content = fs.readFileSync(pkgPath, 'utf8');
|
|
186
198
|
const pkg = JSON.parse(content);
|
|
187
199
|
for (const projectType of projectTypes) {
|
|
188
200
|
if (pkg.dependencies?.[projectType.mainPackage] ||
|
|
@@ -190,16 +202,9 @@ export function getFrontendProjectType(appPath) {
|
|
|
190
202
|
// Create a copy to avoid mutating the original
|
|
191
203
|
const detectedType = { ...projectType };
|
|
192
204
|
// If buildCommand not explicitly set in framework definition, detect it
|
|
193
|
-
|
|
194
|
-
// Check if package.json has a "build" script
|
|
195
|
-
|
|
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
|
-
}
|
|
205
|
+
detectedType.buildCommand ||=
|
|
206
|
+
// Check if package.json has a "build" script, if not set to empty string (no build needed)
|
|
207
|
+
pkg.scripts?.build ? 'npm run build' : '';
|
|
203
208
|
return detectedType;
|
|
204
209
|
}
|
|
205
210
|
}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/constants.js
CHANGED
|
@@ -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,
|
|
36
|
+
const { swell, store, data } = req;
|
|
33
37
|
}
|
|
34
38
|
`;
|
|
35
39
|
};
|
|
@@ -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
|
+
};
|
package/dist/lib/sessions.js
CHANGED
|
@@ -2,6 +2,7 @@ import { ux } from '@oclif/core';
|
|
|
2
2
|
import * as http from 'node:http';
|
|
3
3
|
import open from 'open';
|
|
4
4
|
import { getLoginHost } from './constants.js';
|
|
5
|
+
import { getProxyUrl } from './proxy.js';
|
|
5
6
|
import style from './style.js';
|
|
6
7
|
/**
|
|
7
8
|
* Get the session id for the passed in store. If the session is not found,
|
|
@@ -90,10 +91,10 @@ function serverForLogin({ onPost, onStart, storeId, }) {
|
|
|
90
91
|
res.end('Not Found');
|
|
91
92
|
}
|
|
92
93
|
});
|
|
93
|
-
server.listen(0,
|
|
94
|
+
server.listen(0, async () => {
|
|
94
95
|
const serverAddr = server.address();
|
|
95
|
-
|
|
96
|
-
const localServerUrl = Buffer.from(
|
|
96
|
+
const proxyUrl = await getProxyUrl(serverAddr.port);
|
|
97
|
+
const localServerUrl = Buffer.from(proxyUrl).toString('base64');
|
|
97
98
|
const loginUrl = `${getLoginHost(storeId)}/admin?cli=${localServerUrl}`;
|
|
98
99
|
onStart(loginUrl);
|
|
99
100
|
});
|
package/dist/lib/style.js
CHANGED
package/dist/lib/theme-sync.d.ts
CHANGED
|
@@ -3,17 +3,17 @@ interface ThemeSyncOptions {
|
|
|
3
3
|
targetUrl: string;
|
|
4
4
|
}
|
|
5
5
|
export declare class ThemeSync {
|
|
6
|
-
private readonly
|
|
6
|
+
private readonly options;
|
|
7
7
|
private readonly proxy;
|
|
8
|
+
private readonly server;
|
|
8
9
|
private readonly wss;
|
|
9
|
-
private readonly options;
|
|
10
|
-
private selectPort;
|
|
11
10
|
constructor(options: ThemeSyncOptions);
|
|
12
|
-
start(): Promise<number>;
|
|
13
11
|
reload(): void;
|
|
14
|
-
|
|
12
|
+
start(): Promise<number>;
|
|
15
13
|
private decodeRequestBody;
|
|
16
|
-
private shouldInjectScript;
|
|
17
14
|
private injectScript;
|
|
15
|
+
private parseRequestBody;
|
|
16
|
+
private selectPort;
|
|
17
|
+
private shouldInjectScript;
|
|
18
18
|
}
|
|
19
19
|
export {};
|
package/dist/lib/theme-sync.js
CHANGED
|
@@ -1,29 +1,37 @@
|
|
|
1
|
-
import http from 'http';
|
|
2
1
|
import getPort, { portNumbers } from 'get-port';
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
3
|
+
// @ts-ignore - http-proxy has incomplete TypeScript definitions
|
|
4
|
+
import httpProxy from 'http-proxy';
|
|
5
|
+
import http from 'node:http';
|
|
3
6
|
import util from 'node:util';
|
|
4
7
|
import zlib from 'node:zlib';
|
|
5
|
-
// @ts-
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
9
|
+
// @ts-ignore - ws library uses named exports differently
|
|
10
|
+
// eslint-disable-next-line import/no-named-as-default
|
|
8
11
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
9
12
|
const gunzipAsync = util.promisify(zlib.gunzip);
|
|
10
13
|
const inflateAsync = util.promisify(zlib.inflate);
|
|
11
14
|
const brotliDecompressAsync = util.promisify(zlib.brotliDecompress);
|
|
12
15
|
const FALLBACK_PORT = 3000;
|
|
13
16
|
export class ThemeSync {
|
|
14
|
-
|
|
17
|
+
options;
|
|
15
18
|
proxy;
|
|
19
|
+
server;
|
|
16
20
|
wss;
|
|
17
|
-
options;
|
|
18
|
-
async selectPort() {
|
|
19
|
-
return (await getPort({ port: portNumbers(3000, 3100) })) || FALLBACK_PORT;
|
|
20
|
-
}
|
|
21
21
|
constructor(options) {
|
|
22
22
|
this.options = options;
|
|
23
23
|
this.server = http.createServer();
|
|
24
24
|
this.proxy = httpProxy.createProxyServer({});
|
|
25
25
|
this.wss = new WebSocketServer({ server: this.server });
|
|
26
26
|
}
|
|
27
|
+
reload() {
|
|
28
|
+
// Get all connected WebSocket clients
|
|
29
|
+
const clients = [...this.wss.clients].filter((client) => client.readyState === WebSocket.OPEN);
|
|
30
|
+
// Notify clients to reload
|
|
31
|
+
for (const client of clients) {
|
|
32
|
+
client.send('reload');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
27
35
|
async start() {
|
|
28
36
|
// Select port if not specified
|
|
29
37
|
if (!this.options.port) {
|
|
@@ -85,38 +93,52 @@ export class ThemeSync {
|
|
|
85
93
|
});
|
|
86
94
|
});
|
|
87
95
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
decodeRequestBody(body, encoding) {
|
|
97
|
+
switch (encoding) {
|
|
98
|
+
case 'gzip': {
|
|
99
|
+
return gunzipAsync(body);
|
|
100
|
+
}
|
|
101
|
+
case 'deflate': {
|
|
102
|
+
return inflateAsync(body);
|
|
103
|
+
}
|
|
104
|
+
case 'br': {
|
|
105
|
+
return brotliDecompressAsync(body);
|
|
106
|
+
}
|
|
107
|
+
default: {
|
|
108
|
+
return Promise.reject(new Error(`Unsupported content encoding: ${encoding}`));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Inject WebSocket client script into proxied HTML
|
|
113
|
+
injectScript(html, host) {
|
|
114
|
+
const script = `
|
|
115
|
+
<script>
|
|
116
|
+
const ws = new WebSocket('ws://${host}/ws');
|
|
117
|
+
ws.onmessage = () => {
|
|
118
|
+
window.location.reload();
|
|
119
|
+
};
|
|
120
|
+
</script>
|
|
121
|
+
`;
|
|
122
|
+
return html.replace('</body>', `${script}</body>`);
|
|
95
123
|
}
|
|
96
124
|
async parseRequestBody(proxyRes, body) {
|
|
97
125
|
// May contain multiple encodings
|
|
98
126
|
const contentEncoding = String(proxyRes.headers['content-encoding'] ?? '')
|
|
99
127
|
.split(',')
|
|
100
128
|
.reverse();
|
|
129
|
+
// Sequential decoding of content-encoding layers is required
|
|
130
|
+
/* eslint-disable no-await-in-loop */
|
|
101
131
|
for (const value of contentEncoding) {
|
|
102
132
|
const encoding = value.trim().toLowerCase();
|
|
103
133
|
if (encoding) {
|
|
104
134
|
body = await this.decodeRequestBody(body, encoding);
|
|
105
135
|
}
|
|
106
136
|
}
|
|
137
|
+
/* eslint-enable no-await-in-loop */
|
|
107
138
|
return body.toString('utf8');
|
|
108
139
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
case 'gzip':
|
|
112
|
-
return gunzipAsync(body);
|
|
113
|
-
case 'deflate':
|
|
114
|
-
return inflateAsync(body);
|
|
115
|
-
case 'br':
|
|
116
|
-
return brotliDecompressAsync(body);
|
|
117
|
-
default:
|
|
118
|
-
return Promise.reject(new Error(`Unsupported content encoding: ${encoding}`));
|
|
119
|
-
}
|
|
140
|
+
async selectPort() {
|
|
141
|
+
return (await getPort({ port: portNumbers(3000, 3100) })) || FALLBACK_PORT;
|
|
120
142
|
}
|
|
121
143
|
shouldInjectScript(proxyRes, html) {
|
|
122
144
|
// Check if this is an HTML response
|
|
@@ -129,16 +151,4 @@ export class ThemeSync {
|
|
|
129
151
|
}
|
|
130
152
|
return true;
|
|
131
153
|
}
|
|
132
|
-
// Inject WebSocket client script into proxied HTML
|
|
133
|
-
injectScript(html, host) {
|
|
134
|
-
const script = `
|
|
135
|
-
<script>
|
|
136
|
-
const ws = new WebSocket('ws://${host}/ws');
|
|
137
|
-
ws.onmessage = () => {
|
|
138
|
-
window.location.reload();
|
|
139
|
-
};
|
|
140
|
-
</script>
|
|
141
|
-
`;
|
|
142
|
-
return html.replace('</body>', `${script}</body>`);
|
|
143
|
-
}
|
|
144
154
|
}
|
|
@@ -26,8 +26,8 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
|
|
|
26
26
|
deployFrontend(projectType: FrontendProjectType): Promise<string>;
|
|
27
27
|
ensureAppExists(file?: string, shouldCreate?: boolean): Promise<boolean>;
|
|
28
28
|
ensureLoggedIn(): Promise<true | undefined>;
|
|
29
|
-
execFrontend(command: string, onOutput?: (string: string) => any | false): Promise<void>;
|
|
30
29
|
exec(command: string, cwd?: string, onOutput?: (string: string) => any | false): Promise<void>;
|
|
30
|
+
execFrontend(command: string, onOutput?: (string: string) => any | false): Promise<void>;
|
|
31
31
|
getAllAppStorefronts(params?: {
|
|
32
32
|
type?: string;
|
|
33
33
|
}): Promise<any>;
|
|
@@ -51,19 +51,19 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
|
|
|
51
51
|
appId?: string;
|
|
52
52
|
targetPath?: string;
|
|
53
53
|
}, flags?: {
|
|
54
|
+
env?: string;
|
|
54
55
|
'storefront-id'?: string;
|
|
55
56
|
'storefront-select'?: boolean;
|
|
56
|
-
env?: string;
|
|
57
57
|
}): Promise<any>;
|
|
58
58
|
getStorefrontToPush(flags?: {
|
|
59
|
+
env?: string;
|
|
59
60
|
'storefront-id'?: string;
|
|
60
61
|
'storefront-select'?: boolean;
|
|
61
|
-
env?: string;
|
|
62
62
|
}): Promise<any>;
|
|
63
63
|
setStorefrontEnv(flags: {
|
|
64
|
+
env?: string;
|
|
64
65
|
'storefront-id'?: string;
|
|
65
66
|
'storefront-select'?: boolean;
|
|
66
|
-
env?: string;
|
|
67
67
|
}, defaultStorefront?: any): Promise<void>;
|
|
68
68
|
handleWatchFileChange(configFile: string, configType: ConfigType, event: WatchFileEvent): Promise<void>;
|
|
69
69
|
logAppFrontendUrl(): void;
|
|
@@ -78,15 +78,15 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
|
|
|
78
78
|
runWatchChangeQueue(): Promise<void>;
|
|
79
79
|
saveCurrentStorefront(): void;
|
|
80
80
|
setWatchingFiles(): Promise<void>;
|
|
81
|
+
startProxyServer(port?: number): Promise<number>;
|
|
81
82
|
updateFrontendDeployment(currentStore: string, projectType: FrontendProjectType, deploymentUrl: string, deploymentHash: string): Promise<void>;
|
|
83
|
+
updateLocalProxy(proxyUrl: string, storefrontId?: string): Promise<void>;
|
|
82
84
|
watchForChanges({ logChanges, onChange, syncAll, }?: {
|
|
83
85
|
logChanges?: boolean;
|
|
84
86
|
onChange?: (appConfig?: AppConfig, result?: any) => void;
|
|
85
87
|
syncAll?: boolean;
|
|
86
88
|
}): Promise<void>;
|
|
87
|
-
|
|
88
|
-
updateLocalProxy(proxyUrl: string, storefrontId?: string): Promise<void>;
|
|
89
|
-
wranglerDeployFrontend(projectType: FrontendProjectType): Promise<string>;
|
|
89
|
+
wranglerDeployFrontend(_projectType: FrontendProjectType): Promise<string>;
|
|
90
90
|
private confirmRemoveInstalledApp;
|
|
91
91
|
}
|
|
92
92
|
export {};
|
package/dist/push-app-command.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { confirm, input, select } from '@inquirer/prompts';
|
|
2
2
|
import { $ } from 'execa';
|
|
3
|
+
import getPort, { portNumbers } from 'get-port';
|
|
3
4
|
import * as fs from 'node:fs';
|
|
4
5
|
import * as path from 'node:path';
|
|
5
6
|
import Stream from 'node:stream';
|
|
6
|
-
import getPort, { portNumbers } from 'get-port';
|
|
7
7
|
import ora from 'ora';
|
|
8
8
|
import { default as swellConfig } from './lib/app-config.js';
|
|
9
|
-
import { ConfigType,
|
|
10
|
-
import { selectEnvironmentId } from './lib/stores.js';
|
|
9
|
+
import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, appConfigFromFile, filePathExists, filePathExistsAsync, findAppConfig, getAppSlugId, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
|
|
11
10
|
import { getGlobIgnorePathsChecker } from './lib/apps/paths.js';
|
|
12
11
|
import { default as localConfig } from './lib/config.js';
|
|
13
12
|
import { toAppId } from './lib/create/index.js';
|
|
13
|
+
import { getProxyUrl } from './lib/proxy.js';
|
|
14
|
+
import { selectEnvironmentId } from './lib/stores.js';
|
|
14
15
|
import style from './lib/style.js';
|
|
15
16
|
import { RemoteAppCommand } from './remote-app-command.js';
|
|
16
|
-
import { getProxyUrl } from './lib/proxy.js';
|
|
17
17
|
const PUSH_CONCURRENCY = 3;
|
|
18
18
|
const WATCH_WINDOW_MS = 100;
|
|
19
19
|
const DEV_SERVER_FALLBACK_PORT = 3000;
|
|
@@ -307,9 +307,6 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
307
307
|
return true;
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
|
-
async execFrontend(command, onOutput) {
|
|
311
|
-
return this.exec(command, this.frontendPath, onOutput);
|
|
312
|
-
}
|
|
313
310
|
async exec(command, cwd, onOutput) {
|
|
314
311
|
const $$ = $({
|
|
315
312
|
cwd: cwd || this.appPath,
|
|
@@ -334,6 +331,9 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
334
331
|
await $$ `${command}`;
|
|
335
332
|
}
|
|
336
333
|
}
|
|
334
|
+
async execFrontend(command, onOutput) {
|
|
335
|
+
return this.exec(command, this.frontendPath, onOutput);
|
|
336
|
+
}
|
|
337
337
|
async getAllAppStorefronts(params) {
|
|
338
338
|
const query = Object.entries(params || {})
|
|
339
339
|
.map(([key, value]) => `${key}=${value}`)
|
|
@@ -435,7 +435,7 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
435
435
|
this.showFrontendMigrationError();
|
|
436
436
|
}
|
|
437
437
|
// Frontend directory doesn't exist at all
|
|
438
|
-
this.error(`No valid frontend app found in ${this.frontendPath}. Supported frameworks include: ${
|
|
438
|
+
this.error(`No valid frontend app found in ${this.frontendPath}. Supported frameworks include: ${getFrontendProjectValidValues(false)}.`);
|
|
439
439
|
}
|
|
440
440
|
return projectType;
|
|
441
441
|
}
|
|
@@ -561,8 +561,8 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
561
561
|
await this.api.setStoreEnv(currentStore, defaultStorefront?.env);
|
|
562
562
|
}
|
|
563
563
|
}
|
|
564
|
-
else if (flags
|
|
565
|
-
await this.api.setStoreEnv(currentStore, flags
|
|
564
|
+
else if (flags.env) {
|
|
565
|
+
await this.api.setStoreEnv(currentStore, flags.env);
|
|
566
566
|
}
|
|
567
567
|
else {
|
|
568
568
|
const hasTestEnv = await this.api.isTestEnvEnabled();
|
|
@@ -703,8 +703,9 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
703
703
|
}
|
|
704
704
|
const queue = [...this.watchingChangeQueue.values()];
|
|
705
705
|
this.watchingChangeQueue.clear();
|
|
706
|
+
// Process queue in batches with concurrency control
|
|
707
|
+
/* eslint-disable no-await-in-loop */
|
|
706
708
|
while (queue.length > 0) {
|
|
707
|
-
// eslint-disable-next-line no-await-in-loop
|
|
708
709
|
try {
|
|
709
710
|
await Promise.all(queue
|
|
710
711
|
.splice(0, PUSH_CONCURRENCY)
|
|
@@ -715,10 +716,11 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
715
716
|
this.onWatchChange?.(appConfig, action, result);
|
|
716
717
|
}));
|
|
717
718
|
}
|
|
718
|
-
catch
|
|
719
|
-
//noop
|
|
719
|
+
catch {
|
|
720
|
+
// noop
|
|
720
721
|
}
|
|
721
722
|
}
|
|
723
|
+
/* eslint-enable no-await-in-loop */
|
|
722
724
|
// refetch app to get updated configs
|
|
723
725
|
this.app = await this.getAppWithConfig(this.app.id);
|
|
724
726
|
// Process next accumulated changes
|
|
@@ -733,6 +735,16 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
733
735
|
async setWatchingFiles() {
|
|
734
736
|
this.watchingFiles = new Set(await globAllFilesByPath(this.appPath));
|
|
735
737
|
}
|
|
738
|
+
async startProxyServer(port) {
|
|
739
|
+
// Find an open port starting at 3000
|
|
740
|
+
const freePort = port ||
|
|
741
|
+
(await getPort({ port: portNumbers(3000, 3100) })) ||
|
|
742
|
+
DEV_SERVER_FALLBACK_PORT;
|
|
743
|
+
// Start proxy
|
|
744
|
+
const proxyUrl = await getProxyUrl(freePort);
|
|
745
|
+
await this.updateLocalProxy(proxyUrl, this.storefront?.id);
|
|
746
|
+
return freePort;
|
|
747
|
+
}
|
|
736
748
|
async updateFrontendDeployment(currentStore, projectType, deploymentUrl, deploymentHash) {
|
|
737
749
|
const spinner = ora();
|
|
738
750
|
spinner.start('Updating frontend deployment...');
|
|
@@ -750,6 +762,17 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
750
762
|
}), () => spinner.fail('Error updating app deployment'));
|
|
751
763
|
spinner.succeed('Updated frontend deployment.\n');
|
|
752
764
|
}
|
|
765
|
+
async updateLocalProxy(proxyUrl, storefrontId) {
|
|
766
|
+
const storefront = this.app.type === 'storefront' &&
|
|
767
|
+
(await this.getAppStorefront({ storefront_id: storefrontId }));
|
|
768
|
+
await this.handleRequestErrors(async () => this.api.put({ adminPath: `/client/apps/${this.app.id}/local-proxy` }, {
|
|
769
|
+
body: {
|
|
770
|
+
proxy_url: proxyUrl,
|
|
771
|
+
storefront_id: storefront?.id || null,
|
|
772
|
+
storefront_slug: storefront?.slug || null,
|
|
773
|
+
},
|
|
774
|
+
}));
|
|
775
|
+
}
|
|
753
776
|
async watchForChanges({ logChanges, onChange, syncAll, } = {}) {
|
|
754
777
|
this.watchingIgnoreFilter = await getGlobIgnorePathsChecker(this.appPath);
|
|
755
778
|
this.logWatchChanges = logChanges ?? true;
|
|
@@ -765,28 +788,7 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
765
788
|
}
|
|
766
789
|
fs.watch(this.appPath, { recursive: true }, this.watchListener);
|
|
767
790
|
}
|
|
768
|
-
async
|
|
769
|
-
// Find an open port starting at 3000
|
|
770
|
-
const freePort = port ||
|
|
771
|
-
(await getPort({ port: portNumbers(3000, 3100) })) ||
|
|
772
|
-
DEV_SERVER_FALLBACK_PORT;
|
|
773
|
-
// Start proxy
|
|
774
|
-
const proxyUrl = await getProxyUrl(freePort);
|
|
775
|
-
await this.updateLocalProxy(proxyUrl, this.storefront?.id);
|
|
776
|
-
return freePort;
|
|
777
|
-
}
|
|
778
|
-
async updateLocalProxy(proxyUrl, storefrontId) {
|
|
779
|
-
const storefront = this.app.type === 'storefront' &&
|
|
780
|
-
(await this.getAppStorefront({ storefront_id: storefrontId }));
|
|
781
|
-
await this.handleRequestErrors(async () => this.api.put({ adminPath: `/client/apps/${this.app.id}/local-proxy` }, {
|
|
782
|
-
body: {
|
|
783
|
-
proxy_url: proxyUrl,
|
|
784
|
-
storefront_id: storefront?.id || null,
|
|
785
|
-
storefront_slug: storefront?.slug || null,
|
|
786
|
-
},
|
|
787
|
-
}));
|
|
788
|
-
}
|
|
789
|
-
async wranglerDeployFrontend(projectType) {
|
|
791
|
+
async wranglerDeployFrontend(_projectType) {
|
|
790
792
|
let deploymentUrl;
|
|
791
793
|
let interactiveError = false;
|
|
792
794
|
let pagesProjectError = false;
|
|
@@ -795,7 +797,7 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
795
797
|
await this.execFrontend(`npx wrangler deploy`, (string) => {
|
|
796
798
|
// Dependent on wrangler output
|
|
797
799
|
// Parse the deployment URL from the wrangler output.
|
|
798
|
-
const match = string.match(/^\s*(https:\/\/\S+\.workers\.dev)\s*[\s
|
|
800
|
+
const match = string.match(/^\s*(https:\/\/\S+\.workers\.dev)\s*[\S\s]*?Current Version ID: [\w-]+/m);
|
|
799
801
|
if (match && match.length > 0) {
|
|
800
802
|
deploymentUrl = match[1];
|
|
801
803
|
return false;
|
|
@@ -5,11 +5,11 @@ import { App, AppConfig, AppVersion } from './lib/apps/index.js';
|
|
|
5
5
|
* configurations.
|
|
6
6
|
*/
|
|
7
7
|
export declare abstract class RemoteAppCommand extends AppCommand {
|
|
8
|
-
appType: string;
|
|
9
8
|
static delayOrientation: boolean;
|
|
10
9
|
static orientation: AppCommandOrientation;
|
|
11
10
|
app: App;
|
|
12
11
|
appCreated: boolean;
|
|
12
|
+
appType: string;
|
|
13
13
|
storefront?: any;
|
|
14
14
|
storefrontLocalUrl?: string;
|
|
15
15
|
themeSyncApp?: boolean;
|
|
@@ -15,7 +15,6 @@ import style from './lib/style.js';
|
|
|
15
15
|
* configurations.
|
|
16
16
|
*/
|
|
17
17
|
export class RemoteAppCommand extends AppCommand {
|
|
18
|
-
appType = 'any';
|
|
19
18
|
// allows child commands to set orientation
|
|
20
19
|
static delayOrientation = false;
|
|
21
20
|
// most of the times this will be the target environment
|
|
@@ -24,6 +23,7 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
24
23
|
app = {};
|
|
25
24
|
// Indicates the app was created during the command execution
|
|
26
25
|
appCreated = false;
|
|
26
|
+
appType = 'any';
|
|
27
27
|
// API instance of the target storefront, if applicable
|
|
28
28
|
storefront = null;
|
|
29
29
|
// Indicates we are syncing with a local app frontend via proxy
|
|
@@ -638,7 +638,9 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
638
638
|
const { env, store } = flags;
|
|
639
639
|
if (this.app.name) {
|
|
640
640
|
// Show theme version if not syncing app
|
|
641
|
-
const version = !this.themeSyncApp &&
|
|
641
|
+
const version = !this.themeSyncApp &&
|
|
642
|
+
this.app.type === 'theme' &&
|
|
643
|
+
this.storefront?.theme_version
|
|
642
644
|
? this.storefront?.theme_version
|
|
643
645
|
: this.app.version;
|
|
644
646
|
orientationOutput.push(`[${this.app.type === 'theme' ? 'theme' : 'app'}] ${style.appName(this.app.name)}${version ? ` v${version}` : ''}`);
|