@swell/cli 2.3.4 → 2.4.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.
- package/dist/commands/api/delete.d.ts +1 -0
- package/dist/commands/api/delete.js +8 -1
- package/dist/commands/api/get.d.ts +1 -0
- package/dist/commands/api/get.js +9 -2
- package/dist/commands/api/index.js +4 -2
- package/dist/commands/api/post.d.ts +1 -0
- package/dist/commands/api/post.js +9 -2
- package/dist/commands/api/put.d.ts +1 -0
- package/dist/commands/api/put.js +9 -2
- package/dist/commands/app/dev.js +6 -2
- package/dist/commands/app/frontend/dev.js +6 -3
- package/dist/commands/create/app.js +2 -2
- package/dist/commands/create/frontend.js +2 -2
- package/dist/commands/create/tests.js +5 -2
- package/dist/commands/inspect/models.d.ts +0 -1
- package/dist/commands/inspect/models.js +2 -15
- package/dist/commands/theme/dev.js +10 -7
- package/dist/create-app-command.d.ts +8 -2
- package/dist/create-app-command.js +137 -79
- 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 +5 -2
- package/dist/lib/api.js +60 -9
- package/dist/lib/apps/index.d.ts +11 -0
- package/dist/lib/apps/index.js +45 -46
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.js +5 -0
- package/dist/lib/create/tests.js +1 -1
- package/dist/lib/package-manager.d.ts +86 -0
- package/dist/lib/package-manager.js +186 -0
- package/dist/push-app-command.js +13 -6
- package/dist/swell-api-command.js +8 -2
- package/oclif.manifest.json +76 -15
- package/package.json +1 -1
|
@@ -9,9 +9,39 @@ import { promisify } from 'node:util';
|
|
|
9
9
|
import ora from 'ora';
|
|
10
10
|
import Api from './lib/api.js';
|
|
11
11
|
import { FrontendProjectTypes, getFrontendProjectValidValues, getAllConfigPaths, getFrontendProjectSlugs, writeFile, writeJsonFile, } from './lib/apps/index.js';
|
|
12
|
+
import { getPackageManagerCommands, transformCreateCommand, } from './lib/package-manager.js';
|
|
12
13
|
import style from './lib/style.js';
|
|
13
14
|
import { SwellCommand } from './swell-command.js';
|
|
14
15
|
const execAsync = promisify(exec);
|
|
16
|
+
/** Allowed hosts for tunnel providers used in local development */
|
|
17
|
+
const TUNNEL_ALLOWED_HOSTS = ['.ngrok.app', '.loca.lt', '.trycloudflare.com'];
|
|
18
|
+
/**
|
|
19
|
+
* Find the closing brace position for a config function call (e.g., defineConfig, defineNuxtConfig).
|
|
20
|
+
* Uses brace counting to find the matching `}` for the opening `{`.
|
|
21
|
+
* @returns Position of the closing `}`, or -1 if not found
|
|
22
|
+
*/
|
|
23
|
+
function findConfigClosingBrace(content, functionName) {
|
|
24
|
+
const pattern = new RegExp(`${functionName}\\s*\\(\\s*\\{`);
|
|
25
|
+
const match = content.match(pattern);
|
|
26
|
+
if (!match || match.index === undefined) {
|
|
27
|
+
return -1;
|
|
28
|
+
}
|
|
29
|
+
const startPos = match.index + match[0].length;
|
|
30
|
+
let depth = 1;
|
|
31
|
+
for (let i = startPos; i < content.length && depth > 0; i++) {
|
|
32
|
+
const char = content[i];
|
|
33
|
+
if (char === '{') {
|
|
34
|
+
depth++;
|
|
35
|
+
}
|
|
36
|
+
else if (char === '}') {
|
|
37
|
+
depth--;
|
|
38
|
+
if (depth === 0) {
|
|
39
|
+
return i;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
15
45
|
export class CreateAppCommand extends SwellCommand {
|
|
16
46
|
// Command name used in error message examples; override in subclasses
|
|
17
47
|
commandExample = 'swell create app';
|
|
@@ -76,51 +106,24 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
76
106
|
if (project.architect.serve.options.allowedHosts) {
|
|
77
107
|
return;
|
|
78
108
|
}
|
|
79
|
-
project.architect.serve.options.allowedHosts =
|
|
109
|
+
project.architect.serve.options.allowedHosts = TUNNEL_ALLOWED_HOSTS;
|
|
80
110
|
await fs.writeFile(angularJsonPath, JSON.stringify(config, null, 2), 'utf8');
|
|
81
111
|
}
|
|
82
112
|
async addAllowedHostsToAstro(configPath) {
|
|
83
|
-
|
|
84
|
-
let content;
|
|
85
|
-
try {
|
|
86
|
-
content = await fs.readFile(astroConfigPath, 'utf8');
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (content.includes('allowedHosts')) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const lines = content.split('\n');
|
|
95
|
-
let insertIndex = -1;
|
|
96
|
-
for (const [i, line] of lines.entries()) {
|
|
97
|
-
if (line.includes('defineConfig({')) {
|
|
98
|
-
insertIndex = i + 1;
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (insertIndex === -1) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
// Detect indentation from next line or use default
|
|
106
|
-
const nextLine = lines[insertIndex];
|
|
107
|
-
const indentMatch = nextLine?.match(/^(\s+)/);
|
|
108
|
-
const baseIndent = indentMatch ? indentMatch[1] : ' ';
|
|
109
|
-
const viteConfig = [
|
|
110
|
-
`${baseIndent}vite: {`,
|
|
111
|
-
`${baseIndent} server: {`,
|
|
112
|
-
`${baseIndent} allowedHosts: ['.ngrok.app', '.loca.lt'],`,
|
|
113
|
-
`${baseIndent} },`,
|
|
114
|
-
`${baseIndent}},`,
|
|
115
|
-
];
|
|
116
|
-
lines.splice(insertIndex, 0, ...viteConfig);
|
|
117
|
-
await fs.writeFile(astroConfigPath, lines.join('\n'), 'utf8');
|
|
113
|
+
return this.addViteAllowedHosts(configPath, 'astro.config.mjs', 'defineConfig');
|
|
118
114
|
}
|
|
119
115
|
async addAllowedHostsToNuxt(configPath) {
|
|
120
|
-
|
|
116
|
+
return this.addViteAllowedHosts(configPath, 'nuxt.config.ts', 'defineNuxtConfig');
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Add vite allowedHosts configuration to a framework config file.
|
|
120
|
+
* Supports config files that use a defineX({}) pattern (Astro, Nuxt, etc.)
|
|
121
|
+
*/
|
|
122
|
+
async addViteAllowedHosts(configPath, configFile, functionName) {
|
|
123
|
+
const configFilePath = path.join(configPath, 'frontend', configFile);
|
|
121
124
|
let content;
|
|
122
125
|
try {
|
|
123
|
-
content = await fs.readFile(
|
|
126
|
+
content = await fs.readFile(configFilePath, 'utf8');
|
|
124
127
|
}
|
|
125
128
|
catch {
|
|
126
129
|
return;
|
|
@@ -128,29 +131,30 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
128
131
|
if (content.includes('allowedHosts')) {
|
|
129
132
|
return;
|
|
130
133
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
const viteConfig = ` vite: {
|
|
135
|
+
server: {
|
|
136
|
+
allowedHosts: ${JSON.stringify(TUNNEL_ALLOWED_HOSTS)},
|
|
137
|
+
},
|
|
138
|
+
},`;
|
|
139
|
+
// Handle empty config: functionName({})
|
|
140
|
+
const emptyConfig = `${functionName}({})`;
|
|
141
|
+
if (content.includes(emptyConfig)) {
|
|
142
|
+
content = content.replace(emptyConfig, `${functionName}({\n${viteConfig}\n})`);
|
|
143
|
+
await fs.writeFile(configFilePath, content, 'utf8');
|
|
144
|
+
return;
|
|
138
145
|
}
|
|
139
|
-
|
|
146
|
+
// Find the closing brace of the config function using brace counting
|
|
147
|
+
const closingPos = findConfigClosingBrace(content, functionName);
|
|
148
|
+
if (closingPos === -1) {
|
|
140
149
|
return;
|
|
141
150
|
}
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`${baseIndent} },`,
|
|
150
|
-
`${baseIndent}},`,
|
|
151
|
-
];
|
|
152
|
-
lines.splice(insertIndex, 0, ...viteConfig);
|
|
153
|
-
await fs.writeFile(nuxtConfigPath, lines.join('\n'), 'utf8');
|
|
151
|
+
const before = content.slice(0, closingPos);
|
|
152
|
+
const after = content.slice(closingPos);
|
|
153
|
+
// Ensure proper comma before vite config
|
|
154
|
+
const needsComma = before.trimEnd().slice(-1) !== ',';
|
|
155
|
+
const separator = needsComma ? ',\n' : '\n';
|
|
156
|
+
content = before.trimEnd() + separator + viteConfig + '\n' + after;
|
|
157
|
+
await fs.writeFile(configFilePath, content, 'utf8');
|
|
154
158
|
}
|
|
155
159
|
async createAppConfigFolders(swellConfig) {
|
|
156
160
|
for (const type of getAllConfigPaths(swellConfig.get('type'))) {
|
|
@@ -188,19 +192,63 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
188
192
|
}));
|
|
189
193
|
if (frameworkType && frameworkType !== 'none') {
|
|
190
194
|
const projectType = this.getProjectType(frameworkType);
|
|
195
|
+
// Determine package manager - 'none' is not valid for frontend scaffolding
|
|
196
|
+
let pkg = (flags.pkg || 'npm');
|
|
197
|
+
if (flags.pkg === 'none') {
|
|
198
|
+
this.log(`\n${style.dim('Note: --pkg none is not available for frontend scaffolding, using npm.')}`);
|
|
199
|
+
pkg = 'npm';
|
|
200
|
+
// Ensure root package.json exists (skipped when --pkg none)
|
|
201
|
+
const rootPkgPath = path.join(configPath, 'package.json');
|
|
202
|
+
try {
|
|
203
|
+
await fs.access(rootPkgPath);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Root package.json doesn't exist, create it
|
|
207
|
+
await this.setupPackage(swellConfig.get('id'), swellConfig, pkg);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
191
210
|
this.log();
|
|
211
|
+
// Check for placeholder frontend/package.json and remove it before C3 scaffolding
|
|
212
|
+
const frontendPath = path.join(configPath, 'frontend');
|
|
213
|
+
const frontendPkgPath = path.join(frontendPath, 'package.json');
|
|
214
|
+
try {
|
|
215
|
+
const content = await fs.readFile(frontendPkgPath, 'utf8');
|
|
216
|
+
const frontendPkg = JSON.parse(content);
|
|
217
|
+
// Only remove if it matches our placeholder signature
|
|
218
|
+
if (frontendPkg.version === '0.0.0' &&
|
|
219
|
+
frontendPkg.name === 'frontend' &&
|
|
220
|
+
frontendPkg.private === true) {
|
|
221
|
+
await fs.unlink(frontendPkgPath);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// No package.json or can't read, that's fine
|
|
226
|
+
}
|
|
227
|
+
// Check if frontend folder exists and has files (user-created content)
|
|
228
|
+
try {
|
|
229
|
+
const files = await fs.readdir(frontendPath);
|
|
230
|
+
if (files.length > 0) {
|
|
231
|
+
spinner.fail('frontend/ folder is not empty. Please remove existing files before scaffolding.');
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Folder doesn't exist, that's fine - C3 will create it
|
|
237
|
+
}
|
|
192
238
|
spinner.start(`Creating ${projectType?.name} frontend app (this may take a while)...`);
|
|
193
239
|
try {
|
|
194
240
|
await execAsync(`mkdir -p frontend`, {
|
|
195
241
|
cwd: configPath,
|
|
196
242
|
});
|
|
197
|
-
|
|
243
|
+
// Transform install command for the selected package manager
|
|
244
|
+
const installCommand = transformCreateCommand(projectType.installCommand, pkg);
|
|
245
|
+
await execAsync(installCommand, {
|
|
198
246
|
cwd: configPath,
|
|
199
247
|
});
|
|
200
248
|
// Use this command to debug output, i.e.e when command becomes non-responsive
|
|
201
249
|
/* await this.execWithStdio(
|
|
202
250
|
configPath,
|
|
203
|
-
|
|
251
|
+
installCommand,
|
|
204
252
|
); */
|
|
205
253
|
}
|
|
206
254
|
catch (error) {
|
|
@@ -210,25 +258,23 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
210
258
|
return false;
|
|
211
259
|
}
|
|
212
260
|
// Ensure frontend package.json has correct name for workspace
|
|
213
|
-
const frontendPkgPath = path.join(configPath, 'frontend', 'package.json');
|
|
214
261
|
try {
|
|
215
262
|
const pkgContent = await fs.readFile(frontendPkgPath, 'utf8');
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
await fs.writeFile(frontendPkgPath, JSON.stringify(
|
|
263
|
+
const frontendPkgJson = JSON.parse(pkgContent);
|
|
264
|
+
if (frontendPkgJson.name !== 'frontend') {
|
|
265
|
+
frontendPkgJson.name = 'frontend';
|
|
266
|
+
await fs.writeFile(frontendPkgPath, JSON.stringify(frontendPkgJson, null, 2), 'utf8');
|
|
220
267
|
}
|
|
221
268
|
}
|
|
222
269
|
catch {
|
|
223
270
|
// Ignore if package.json doesn't exist or can't be read
|
|
224
271
|
}
|
|
225
272
|
await this.addFrontendAllowedHosts(projectType, configPath);
|
|
226
|
-
//
|
|
227
|
-
// (removes frontend/package-lock.json, hoists dependencies, creates root lock file)
|
|
273
|
+
// Run install at root to hoist dependencies to workspace
|
|
228
274
|
spinner.start('Initializing workspace...');
|
|
229
275
|
try {
|
|
230
|
-
const
|
|
231
|
-
await execAsync(
|
|
276
|
+
const { install } = getPackageManagerCommands(pkg);
|
|
277
|
+
await execAsync(install, { cwd: configPath });
|
|
232
278
|
spinner.succeed('Workspace initialized');
|
|
233
279
|
}
|
|
234
280
|
catch {
|
|
@@ -375,19 +421,22 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
375
421
|
const configPath = path.dirname(config.path);
|
|
376
422
|
const packageJson = {
|
|
377
423
|
description: config.get('description'),
|
|
378
|
-
// Include workspace even if frontend doesn't exist yet
|
|
379
|
-
// npm tolerates missing workspace directories without errors
|
|
380
|
-
workspaces: ['frontend'],
|
|
381
424
|
devDependencies: {
|
|
382
425
|
'@swell/app-types': '^1.0.5',
|
|
383
426
|
typescript: '^5.9.3',
|
|
384
427
|
},
|
|
385
428
|
name,
|
|
429
|
+
// Required for yarn workspaces, good practice for all package managers
|
|
430
|
+
private: true,
|
|
386
431
|
scripts: {
|
|
387
432
|
typecheck: '([ -z "$(find functions -name \'*.ts\' 2>/dev/null | head -1)" ] || tsc --noEmit) && ([ ! -f test/tsconfig.json ] || tsc --noEmit -p test) && ([ ! -f frontend/tsconfig.json ] || tsc --noEmit -p frontend)',
|
|
388
433
|
},
|
|
389
434
|
version: config.get('version'),
|
|
390
435
|
};
|
|
436
|
+
// pnpm uses pnpm-workspace.yaml instead of workspaces field in package.json
|
|
437
|
+
if (pkg !== 'pnpm') {
|
|
438
|
+
packageJson.workspaces = ['frontend'];
|
|
439
|
+
}
|
|
391
440
|
const tsConfig = {
|
|
392
441
|
compilerOptions: {
|
|
393
442
|
lib: ['esnext', 'webworker'],
|
|
@@ -401,15 +450,24 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
401
450
|
await writeJsonFile(path.join(configPath, 'package.json'), packageJson);
|
|
402
451
|
await writeJsonFile(path.join(configPath, 'tsconfig.json'), tsConfig);
|
|
403
452
|
await writeFile(path.join(configPath, '.gitignore'), `node_modules`);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
await
|
|
407
|
-
|
|
453
|
+
// Create pnpm-workspace.yaml for pnpm (required for workspace support)
|
|
454
|
+
if (pkg === 'pnpm') {
|
|
455
|
+
await writeFile(path.join(configPath, 'pnpm-workspace.yaml'), 'packages:\n - frontend\n');
|
|
456
|
+
}
|
|
457
|
+
// Create placeholder frontend/package.json for bun and yarn
|
|
458
|
+
// These package managers require workspace directories to exist with a package.json
|
|
459
|
+
if (pkg === 'bun' || pkg === 'yarn') {
|
|
460
|
+
const frontendPath = path.join(configPath, 'frontend');
|
|
461
|
+
await fs.mkdir(frontendPath, { recursive: true });
|
|
462
|
+
await writeJsonFile(path.join(frontendPath, 'package.json'), {
|
|
463
|
+
name: 'frontend',
|
|
464
|
+
private: true,
|
|
465
|
+
version: '0.0.0',
|
|
408
466
|
});
|
|
409
467
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}
|
|
468
|
+
// Install root dependencies using selected package manager
|
|
469
|
+
const { install } = getPackageManagerCommands(pkg);
|
|
470
|
+
await execAsync(install, { cwd: configPath });
|
|
413
471
|
}
|
|
414
472
|
async tryPackageSetup(name, config, pkg) {
|
|
415
473
|
try {
|
package/dist/env/local.d.ts
CHANGED
package/dist/env/local.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
API_BASE_URL: 'https://localapi.schema.io:4443',
|
|
3
3
|
ADMIN_API_BASE_URL: 'http://${STORE_ID}.swell.test:4001/admin/api',
|
|
4
|
+
FRONTEND_API_BASE_URL: 'http://${STORE_ID}.swell.test:4001/api',
|
|
4
5
|
LOGIN_HOST: 'http://${STORE_ID}.swell.test:3000',
|
|
5
6
|
APP_PAGES_HOST: 'http://${STORE_ID}--${APP_ID}--${ENV}.swell.test:4001',
|
|
6
7
|
STOREFRONT_FRONTEND_HOST: 'http://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.test:4001',
|
package/dist/env/production.d.ts
CHANGED
package/dist/env/production.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
API_BASE_URL: 'https://api.swell.store',
|
|
3
3
|
ADMIN_API_BASE_URL: 'https://${STORE_ID}.swell.store/admin/api',
|
|
4
|
+
FRONTEND_API_BASE_URL: 'https://${STORE_ID}.swell.store/api',
|
|
4
5
|
LOGIN_HOST: 'https://${STORE_ID}.swell.store',
|
|
5
6
|
APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.swell.store',
|
|
6
7
|
STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.store',
|
package/dist/env/review.d.ts
CHANGED
package/dist/env/review.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
API_BASE_URL: 'https://${REVIEW_DOMAIN}.rav2.swell.store',
|
|
3
3
|
ADMIN_API_BASE_URL: 'https://${STORE_ID}.${REVIEW_DOMAIN}.rav2.swell.store/admin/api',
|
|
4
|
+
FRONTEND_API_BASE_URL: 'https://${STORE_ID}.${REVIEW_DOMAIN}.rav2.swell.store/api',
|
|
4
5
|
LOGIN_HOST: 'https://${REVIEW_DOMAIN}.rav2.swell.store',
|
|
5
6
|
APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.${REVIEW_DOMAIN}.rav2.swell.store',
|
|
6
7
|
STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.${REVIEW_DOMAIN}.rav2.swell.store',
|
package/dist/env/staging.d.ts
CHANGED
package/dist/env/staging.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
API_BASE_URL: 'https://staging.swell.store',
|
|
3
3
|
ADMIN_API_BASE_URL: 'https://${STORE_ID}.staging.swell.store/admin/api',
|
|
4
|
+
FRONTEND_API_BASE_URL: 'https://${STORE_ID}.staging.swell.store/api',
|
|
4
5
|
LOGIN_HOST: 'https://staging.swell.store',
|
|
5
6
|
APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.staging.swell.store',
|
|
6
7
|
STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.staging.swell.store',
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -5,10 +5,11 @@ export declare enum HttpMethod {
|
|
|
5
5
|
DELETE = "delete"
|
|
6
6
|
}
|
|
7
7
|
export default class Api {
|
|
8
|
+
storeId: string | undefined;
|
|
8
9
|
envId: string | undefined;
|
|
9
10
|
secretKey: string | undefined;
|
|
10
|
-
|
|
11
|
-
constructor(storeId?: string, envId?: string, secretKey?: string);
|
|
11
|
+
publicKey: string | undefined;
|
|
12
|
+
constructor(storeId?: string, envId?: string, publicKey?: string, secretKey?: string);
|
|
12
13
|
api(pathOpts: Api.Paths, options: Api.RequestOptions): Promise<any>;
|
|
13
14
|
get(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
|
|
14
15
|
getAll(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<{
|
|
@@ -21,6 +22,8 @@ export default class Api {
|
|
|
21
22
|
isTestEnvEnabled(): Promise<boolean>;
|
|
22
23
|
setEnv(envId: string): Promise<void>;
|
|
23
24
|
setStoreEnv(storeId: string, envId?: string): Promise<void>;
|
|
25
|
+
setPublicKey(publicKey?: string): Promise<void>;
|
|
26
|
+
private isFrontend;
|
|
24
27
|
private isAdmin;
|
|
25
28
|
private truncatePath;
|
|
26
29
|
private waitForAsyncResponse;
|
package/dist/lib/api.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fetch from 'node-fetch';
|
|
2
2
|
import { stringify } from 'qs';
|
|
3
|
+
import { getCurrentAppSlugId, hasAppContext } from './apps/index.js';
|
|
3
4
|
import config from './config.js';
|
|
4
|
-
import { API_BASE_URL, getAdminApiBaseUrl } from './constants.js';
|
|
5
|
+
import { API_BASE_URL, getAdminApiBaseUrl, getFrontendApiBaseUrl, } from './constants.js';
|
|
6
|
+
import style from './style.js';
|
|
5
7
|
export var HttpMethod;
|
|
6
8
|
(function (HttpMethod) {
|
|
7
9
|
HttpMethod["GET"] = "get";
|
|
@@ -15,7 +17,7 @@ const defaultHeaders = (opts) => ({
|
|
|
15
17
|
...opts,
|
|
16
18
|
});
|
|
17
19
|
const authenticatedHeaders = (storeId, opts = {}) => {
|
|
18
|
-
let { secretKey, sessionId, ...moreOpts } = opts;
|
|
20
|
+
let { publicKey, secretKey, sessionId, ...moreOpts } = opts;
|
|
19
21
|
sessionId ??= config.getSessionId(storeId);
|
|
20
22
|
if (secretKey) {
|
|
21
23
|
return defaultHeaders({
|
|
@@ -23,6 +25,12 @@ const authenticatedHeaders = (storeId, opts = {}) => {
|
|
|
23
25
|
...moreOpts,
|
|
24
26
|
});
|
|
25
27
|
}
|
|
28
|
+
if (publicKey) {
|
|
29
|
+
return defaultHeaders({
|
|
30
|
+
Authorization: `Basic ${Buffer.from(publicKey).toString('base64')}`,
|
|
31
|
+
...moreOpts,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
26
34
|
if (sessionId) {
|
|
27
35
|
return defaultHeaders({
|
|
28
36
|
'X-Session': sessionId,
|
|
@@ -32,12 +40,14 @@ const authenticatedHeaders = (storeId, opts = {}) => {
|
|
|
32
40
|
return defaultHeaders(moreOpts);
|
|
33
41
|
};
|
|
34
42
|
export default class Api {
|
|
43
|
+
storeId;
|
|
35
44
|
envId;
|
|
36
45
|
secretKey;
|
|
37
|
-
|
|
38
|
-
constructor(storeId, envId, secretKey) {
|
|
46
|
+
publicKey;
|
|
47
|
+
constructor(storeId, envId, publicKey, secretKey) {
|
|
39
48
|
this.storeId = storeId;
|
|
40
49
|
this.envId = envId;
|
|
50
|
+
this.publicKey = publicKey;
|
|
41
51
|
this.secretKey = secretKey;
|
|
42
52
|
}
|
|
43
53
|
async api(pathOpts, options) {
|
|
@@ -50,14 +60,17 @@ export default class Api {
|
|
|
50
60
|
...options?.headers,
|
|
51
61
|
...(this.envId ? { 'Swell-Env': this.envId } : undefined),
|
|
52
62
|
...authenticatedHeaders(storeId, {
|
|
63
|
+
publicKey: this.publicKey,
|
|
53
64
|
secretKey: this.secretKey,
|
|
54
65
|
sessionId: options.sessionId,
|
|
55
66
|
}),
|
|
56
67
|
},
|
|
57
68
|
};
|
|
58
|
-
path = this.
|
|
59
|
-
? `${
|
|
60
|
-
:
|
|
69
|
+
path = this.isFrontend(pathOpts)
|
|
70
|
+
? `${getFrontendApiBaseUrl(storeId)}/${this.truncatePath(pathOpts.frontendPath)}`
|
|
71
|
+
: this.isAdmin(pathOpts)
|
|
72
|
+
? `${getAdminApiBaseUrl(storeId)}/${this.truncatePath(pathOpts.adminPath)}`
|
|
73
|
+
: `${API_BASE_URL}/${this.truncatePath(pathOpts.backendPath)}`;
|
|
61
74
|
if (options.query) {
|
|
62
75
|
path += `?${stringify(options.query)}`;
|
|
63
76
|
}
|
|
@@ -179,8 +192,46 @@ export default class Api {
|
|
|
179
192
|
this.storeId = storeId;
|
|
180
193
|
this.envId = envId;
|
|
181
194
|
}
|
|
182
|
-
|
|
183
|
-
|
|
195
|
+
async setPublicKey(publicKey) {
|
|
196
|
+
if (publicKey) {
|
|
197
|
+
this.publicKey = publicKey;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const appSlugId = await getCurrentAppSlugId();
|
|
201
|
+
const response = await this.post({ adminPath: '/data/:batch' }, {
|
|
202
|
+
query: [
|
|
203
|
+
{
|
|
204
|
+
method: 'get',
|
|
205
|
+
url: '/:clients/:self/keys/:last',
|
|
206
|
+
data: {
|
|
207
|
+
scope: 'user',
|
|
208
|
+
public: { $exists: true },
|
|
209
|
+
revoked: { $ne: true },
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
appSlugId && {
|
|
213
|
+
method: 'get',
|
|
214
|
+
url: '/:clients/:self/apps/:last',
|
|
215
|
+
data: {
|
|
216
|
+
app_private_id: `_${appSlugId}`,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
});
|
|
221
|
+
const [userKey, installedApp] = Object.values(response);
|
|
222
|
+
this.publicKey = installedApp?.public_key || userKey?.public;
|
|
223
|
+
if (!this.publicKey) {
|
|
224
|
+
throw new Error('No API public key available. Run inside a deployed app or create a user-scoped public key.');
|
|
225
|
+
}
|
|
226
|
+
else if (hasAppContext() && !installedApp?.public_key) {
|
|
227
|
+
console.warn(style.funcWarn('⚠ No app context found. Running in user scope.'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
isFrontend(pathOpts) {
|
|
231
|
+
return Boolean(pathOpts.frontendPath) && Boolean(this.publicKey);
|
|
232
|
+
}
|
|
233
|
+
isAdmin(pathOpts) {
|
|
234
|
+
return Boolean(pathOpts.adminPath) && !this.secretKey;
|
|
184
235
|
}
|
|
185
236
|
truncatePath(path) {
|
|
186
237
|
return path?.startsWith('/') ? path.slice(1) : path;
|
package/dist/lib/apps/index.d.ts
CHANGED
|
@@ -131,6 +131,15 @@ export declare function getFrontendProjectSlugs(withNone?: boolean, withLegacy?:
|
|
|
131
131
|
export declare function getFrontendProjectValidValues(withNone?: boolean, withLegacy?: boolean): string;
|
|
132
132
|
export declare function getAppSlugId(app: App): string | undefined;
|
|
133
133
|
export declare function getFrontendProjectType(appPath: string): FrontendProjectType | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Get project commands transformed for the detected package manager.
|
|
136
|
+
* Detects the package manager from lock files in appPath and transforms
|
|
137
|
+
* npm-style commands to the equivalent for that package manager.
|
|
138
|
+
*/
|
|
139
|
+
export declare function getProjectCommands(appPath: string, projectType: FrontendProjectType): {
|
|
140
|
+
buildCommand?: string;
|
|
141
|
+
devCommand: string;
|
|
142
|
+
};
|
|
134
143
|
export declare function getConfigTypeFromPath(path: string): ConfigType | undefined;
|
|
135
144
|
export declare function getConfigTypeKeyFromValue(value: string): string | undefined;
|
|
136
145
|
export declare function filePathExists(filePath: string): boolean;
|
|
@@ -156,3 +165,5 @@ interface BatchAppFiles {
|
|
|
156
165
|
* @returns {BatchAppFiles[]} Batches of files to push
|
|
157
166
|
*/
|
|
158
167
|
export declare function batchAppFilesByType(configs: AppConfig[]): BatchAppFiles[];
|
|
168
|
+
export declare function hasAppContext(): boolean;
|
|
169
|
+
export declare function getCurrentAppSlugId(): Promise<string | undefined>;
|