@swell/cli 2.6.0 → 2.7.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/app/frontend/dev.js +18 -0
- package/dist/commands/inspect/content.d.ts +25 -14
- package/dist/commands/inspect/content.js +34 -144
- package/dist/commands/inspect/extensions.d.ts +49 -0
- package/dist/commands/inspect/extensions.js +424 -0
- package/dist/commands/inspect/functions.d.ts +48 -0
- package/dist/commands/inspect/functions.js +83 -0
- package/dist/commands/inspect/index.js +8 -7
- package/dist/commands/inspect/models.d.ts +17 -2
- package/dist/commands/inspect/models.js +114 -47
- package/dist/commands/inspect/notifications.d.ts +31 -0
- package/dist/commands/inspect/notifications.js +125 -0
- package/dist/commands/inspect/settings.d.ts +28 -0
- package/dist/commands/inspect/settings.js +82 -0
- package/dist/commands/inspect/webhooks.d.ts +34 -0
- package/dist/commands/inspect/webhooks.js +61 -0
- package/dist/create-app-command.d.ts +7 -0
- package/dist/create-app-command.js +80 -9
- package/dist/inspect-resource-command.d.ts +91 -0
- package/dist/inspect-resource-command.js +232 -0
- package/dist/lib/apps/index.d.ts +2 -1
- package/dist/lib/apps/index.js +43 -6
- package/dist/lib/apps/inspect-scope.d.ts +58 -0
- package/dist/lib/apps/inspect-scope.js +60 -0
- package/dist/lib/apps/object-id.d.ts +7 -0
- package/dist/lib/apps/object-id.js +9 -0
- package/dist/lib/apps/paths.js +8 -1
- package/dist/lib/apps/resolve.d.ts +16 -0
- package/dist/lib/apps/resolve.js +39 -0
- package/dist/lib/apps/slug.d.ts +29 -0
- package/dist/lib/apps/slug.js +12 -0
- package/dist/lib/inspect/content.d.ts +39 -0
- package/dist/lib/inspect/content.js +76 -0
- package/dist/lib/inspect/extensions.d.ts +267 -0
- package/dist/lib/inspect/extensions.js +690 -0
- package/dist/lib/inspect/notifications.d.ts +115 -0
- package/dist/lib/inspect/notifications.js +173 -0
- package/dist/lib/inspect/settings.d.ts +61 -0
- package/dist/lib/inspect/settings.js +56 -0
- package/dist/lib/inspect/table.d.ts +29 -0
- package/dist/lib/inspect/table.js +61 -0
- package/dist/push-app-command.js +3 -2
- package/dist/swell-api-command.d.ts +0 -4
- package/dist/swell-api-command.js +2 -18
- package/oclif.manifest.json +392 -35
- package/package.json +1 -1
|
@@ -182,6 +182,24 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
182
182
|
}
|
|
183
183
|
break;
|
|
184
184
|
}
|
|
185
|
+
case 'react':
|
|
186
|
+
case 'react-storefront': {
|
|
187
|
+
// Vite emits the Local URL as its ready signal — no separate
|
|
188
|
+
// detector needed.
|
|
189
|
+
if (/➜\s+local:\s+http:\/\//i.test(string) ||
|
|
190
|
+
/➜\s+network:/i.test(string)) {
|
|
191
|
+
if (!serverReadyDetected) {
|
|
192
|
+
serverReadyDetected = true;
|
|
193
|
+
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
194
|
+
setTimeout(() => this.logLocalPreviewUrl(), 100);
|
|
195
|
+
if (!isAppDev) {
|
|
196
|
+
this.watchForChanges();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
185
203
|
case 'nuxt': {
|
|
186
204
|
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
|
|
187
205
|
if (/➜ local:\s+http:\/\//i.test(string) ||
|
|
@@ -1,25 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { InspectResourceCommand, InspectResourceCommandParsed } from '../../inspect-resource-command.js';
|
|
2
|
+
import { InspectScope } from '../../lib/apps/inspect-scope.js';
|
|
3
|
+
import { GroupInfo } from '../../lib/inspect/table.js';
|
|
4
|
+
interface ContentView {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
app_id?: string;
|
|
8
|
+
collection?: string;
|
|
9
|
+
fields?: any[];
|
|
10
|
+
source_id?: string;
|
|
11
|
+
source_type?: string;
|
|
12
|
+
views?: any[];
|
|
13
|
+
}
|
|
14
|
+
export default class Content extends InspectResourceCommand {
|
|
3
15
|
static summary: string;
|
|
4
16
|
static description: string;
|
|
5
17
|
static args: {
|
|
6
|
-
|
|
18
|
+
identifier: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
7
19
|
};
|
|
8
20
|
static flags: {
|
|
21
|
+
app: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
22
|
live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
23
|
+
json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
24
|
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
25
|
};
|
|
12
26
|
static examples: string[];
|
|
27
|
+
protected resourceLabel: string;
|
|
28
|
+
protected resourceLabelSingular: string;
|
|
29
|
+
protected adminPath: string;
|
|
30
|
+
protected commandName: string;
|
|
13
31
|
run(): Promise<void>;
|
|
14
|
-
protected
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
private showContentView;
|
|
18
|
-
private resolveContentId;
|
|
19
|
-
private showContentDetail;
|
|
20
|
-
private reverseResolveContentId;
|
|
21
|
-
private getAppBySlug;
|
|
22
|
-
private getAppSlugId;
|
|
23
|
-
private throwContentNotFound;
|
|
24
|
-
private showContent;
|
|
32
|
+
protected keyFor(record: ContentView, appSlugById: Record<string, string>): string;
|
|
33
|
+
protected groupForRecord(record: ContentView, appSlugById: Record<string, string>): GroupInfo;
|
|
34
|
+
protected showDetail(identifier: string, scope: InspectScope, flags: InspectResourceCommandParsed['flags']): Promise<void>;
|
|
25
35
|
}
|
|
36
|
+
export {};
|
|
@@ -1,159 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
static
|
|
7
|
-
static description = `
|
|
8
|
-
View content view extensions available in your Swell store.
|
|
1
|
+
import { InspectResourceCommand, inspectResourceBaseArgs, inspectResourceBaseFlags, } from '../../inspect-resource-command.js';
|
|
2
|
+
import { resolveAppId } from '../../lib/apps/resolve.js';
|
|
3
|
+
import { groupContentRecord, resolveContentKey, reverseResolveContentId, } from '../../lib/inspect/content.js';
|
|
4
|
+
export default class Content extends InspectResourceCommand {
|
|
5
|
+
static summary = 'App and platform content views.';
|
|
6
|
+
static description = `Lists content view extensions across all apps plus platform 'custom' sources, grouped by owner. Pass --app=<slug> or --app=. (current swell.json) to scope.
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
Pass an identifier to view a single record as JSON.
|
|
9
|
+
|
|
10
|
+
Identifier forms:
|
|
11
|
+
app.<app>.<name> — app-owned view (list column 1)
|
|
12
|
+
custom.<source>.<name> — platform custom view (e.g. custom.admin.products)
|
|
13
|
+
<name> — requires --app= scope
|
|
12
14
|
`;
|
|
13
|
-
static args = {
|
|
14
|
-
|
|
15
|
-
description: 'Optional content view ID. Example: custom.admin.products, app.myapp.products',
|
|
16
|
-
required: false,
|
|
17
|
-
}),
|
|
18
|
-
};
|
|
19
|
-
static flags = {
|
|
20
|
-
live: Flags.boolean({
|
|
21
|
-
description: 'Use the live environment instead of test (default: test).',
|
|
22
|
-
default: false,
|
|
23
|
-
}),
|
|
24
|
-
yes: Flags.boolean({
|
|
25
|
-
char: 'y',
|
|
26
|
-
description: 'Accept all prompts automatically (for agent compatibility; no functional effect).',
|
|
27
|
-
default: false,
|
|
28
|
-
}),
|
|
29
|
-
};
|
|
15
|
+
static args = { ...inspectResourceBaseArgs };
|
|
16
|
+
static flags = { ...inspectResourceBaseFlags };
|
|
30
17
|
static examples = [
|
|
31
18
|
'swell inspect content',
|
|
32
|
-
'swell inspect content
|
|
19
|
+
'swell inspect content --app=my-app',
|
|
33
20
|
'swell inspect content app.honest_reviews.reviews',
|
|
34
|
-
'swell inspect content
|
|
21
|
+
'swell inspect content custom.admin.products',
|
|
22
|
+
'swell inspect content reviews --app=honest_reviews',
|
|
23
|
+
'swell inspect content --live',
|
|
35
24
|
];
|
|
25
|
+
resourceLabel = 'Content views';
|
|
26
|
+
resourceLabelSingular = 'content view';
|
|
27
|
+
adminPath = '/data/:content';
|
|
28
|
+
commandName = 'content';
|
|
36
29
|
async run() {
|
|
37
30
|
const { args, flags } = await this.parse(Content);
|
|
38
|
-
|
|
39
|
-
const { live } = flags;
|
|
40
|
-
if (!live) {
|
|
41
|
-
await this.api.setEnv('test');
|
|
42
|
-
}
|
|
43
|
-
// Detail mode: show configuration for a specific content view
|
|
44
|
-
if (contentId) {
|
|
45
|
-
await this.showContentDetail(contentId);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const store = localConfig.getDefaultStore();
|
|
49
|
-
// List mode: show all content views
|
|
50
|
-
await this.listContent(store, live);
|
|
51
|
-
}
|
|
52
|
-
async catch(error) {
|
|
53
|
-
if (error instanceof FetchError) {
|
|
54
|
-
const message = `Could not connect to Swell API. Please try again later: ${error.message}`;
|
|
55
|
-
return this.error(message, { exit: 2, code: error.code });
|
|
56
|
-
}
|
|
57
|
-
return this.error(error.message, { exit: 1 });
|
|
58
|
-
}
|
|
59
|
-
async listContent(store, live) {
|
|
60
|
-
const { results: apps } = await this.api.get({
|
|
61
|
-
adminPath: '/apps',
|
|
62
|
-
});
|
|
63
|
-
const appsById = Object.fromEntries(apps.map((app) => [app.id, app]));
|
|
64
|
-
const { results: contentViews } = await this.api.getAll({
|
|
65
|
-
adminPath: '/data/:content',
|
|
66
|
-
});
|
|
67
|
-
// Sort by resolved ID in reverse order (custom.* first)
|
|
68
|
-
const sortedContentViews = [...contentViews].sort((a, b) => {
|
|
69
|
-
const resolvedA = this.resolveContentId(a, appsById);
|
|
70
|
-
const resolvedB = this.resolveContentId(b, appsById);
|
|
71
|
-
return resolvedB.localeCompare(resolvedA);
|
|
72
|
-
});
|
|
73
|
-
this.log(`Content views extensions available in '${store}' ${live ? '[live]' : '[test]'}:`);
|
|
74
|
-
this.log();
|
|
75
|
-
this.showContentViews(sortedContentViews, appsById);
|
|
76
|
-
// Show next step hints
|
|
77
|
-
this.log();
|
|
78
|
-
this.log('Examples:');
|
|
79
|
-
this.log(' swell inspect content custom.admin.products');
|
|
80
|
-
this.log(' swell inspect content app.honest_reviews.accounts');
|
|
81
|
-
}
|
|
82
|
-
showContentViews(contentViews, appsById) {
|
|
83
|
-
const maxNameWidth = Math.max(...contentViews.map((cv) => cv.name.length), 15);
|
|
84
|
-
for (const cv of contentViews) {
|
|
85
|
-
this.showContentView(cv, maxNameWidth, appsById);
|
|
86
|
-
}
|
|
31
|
+
await this.runInspect({ args, flags });
|
|
87
32
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const paddedName = contentView.name.padEnd(nameWidth + 2);
|
|
91
|
-
this.log(`${paddedName}${resolvedId}`);
|
|
33
|
+
keyFor(record, appSlugById) {
|
|
34
|
+
return resolveContentKey(record.id, appSlugById);
|
|
92
35
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Parse ID: app.{appId}.{name} or custom.{source}.{name}
|
|
96
|
-
const match = id.match(/^(app|custom)\.([^.]+)\.(.+)$/);
|
|
97
|
-
if (!match)
|
|
98
|
-
return id; // fallback to original
|
|
99
|
-
const [, type, identifier] = match;
|
|
100
|
-
if (type === 'custom') {
|
|
101
|
-
return id; // custom IDs are already resolved
|
|
102
|
-
}
|
|
103
|
-
// type === 'app': resolve app ID to slug
|
|
104
|
-
const appId = identifier;
|
|
105
|
-
const app = appsById[appId];
|
|
106
|
-
if (!app) {
|
|
107
|
-
return id; // fallback if app not found
|
|
108
|
-
}
|
|
109
|
-
const appSlug = this.getAppSlugId(app);
|
|
110
|
-
return `app.${appSlug}.${name}`;
|
|
36
|
+
groupForRecord(record, appSlugById) {
|
|
37
|
+
return groupContentRecord(record, appSlugById);
|
|
111
38
|
}
|
|
112
|
-
async
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const content = await this.api.get({
|
|
117
|
-
adminPath: `/data/:content/${unresolvedId}`,
|
|
39
|
+
async showDetail(identifier, scope, flags) {
|
|
40
|
+
const unresolved = await reverseResolveContentId(identifier, scope, (slug) => resolveAppId(this.api, slug));
|
|
41
|
+
const record = await this.api.get({
|
|
42
|
+
adminPath: `${this.adminPath}/${unresolved}`,
|
|
118
43
|
});
|
|
119
|
-
if (!
|
|
120
|
-
this.
|
|
121
|
-
}
|
|
122
|
-
this.showContent(content);
|
|
123
|
-
}
|
|
124
|
-
async reverseResolveContentId(resolvedId) {
|
|
125
|
-
// Parse ID: app.{slug}.{name} or custom.{source}.{name}
|
|
126
|
-
const match = resolvedId.match(/^(app|custom)\.([^.]+)\.(.+)$/);
|
|
127
|
-
if (!match) {
|
|
128
|
-
throw new Error(`Invalid content ID format: ${resolvedId}`);
|
|
129
|
-
}
|
|
130
|
-
const [, type, identifier, name] = match;
|
|
131
|
-
if (type === 'custom') {
|
|
132
|
-
return resolvedId; // custom IDs don't need reverse resolution
|
|
44
|
+
if (!record) {
|
|
45
|
+
this.throwNotFound(identifier);
|
|
133
46
|
}
|
|
134
|
-
|
|
135
|
-
if (/^[\da-f]{24}$/.test(identifier)) {
|
|
136
|
-
return resolvedId; // already unresolved format
|
|
137
|
-
}
|
|
138
|
-
// type === 'app': resolve slug to app ID
|
|
139
|
-
const appSlug = identifier;
|
|
140
|
-
const app = await this.getAppBySlug(appSlug);
|
|
141
|
-
return `app.${app.id}.${name}`;
|
|
142
|
-
}
|
|
143
|
-
async getAppBySlug(slug) {
|
|
144
|
-
const app = await this.api.get({ adminPath: `/apps/${slug}` });
|
|
145
|
-
if (!app) {
|
|
146
|
-
throw new Error(`App '${slug}' not found.\nList available content: swell inspect content`);
|
|
147
|
-
}
|
|
148
|
-
return app;
|
|
149
|
-
}
|
|
150
|
-
getAppSlugId(app) {
|
|
151
|
-
return app.public_id || app.private_id.replace(/^_/, '');
|
|
152
|
-
}
|
|
153
|
-
throwContentNotFound(id) {
|
|
154
|
-
throw new Error(`No content view found for '${id}'.\nList available content: swell inspect content`);
|
|
155
|
-
}
|
|
156
|
-
showContent(content) {
|
|
157
|
-
this.log(JSON.stringify(content, null, 2));
|
|
47
|
+
this.emitDetail(record, flags);
|
|
158
48
|
}
|
|
159
49
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SwellCommand } from '../../swell-command.js';
|
|
2
|
+
export default class Extensions extends SwellCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
identifier: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
app: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
|
+
live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
|
+
json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
12
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
static examples: string[];
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
protected catch(error: Error): Promise<any>;
|
|
17
|
+
/**
|
|
18
|
+
* List mode is bounded to two parallel round-trip phases regardless of app
|
|
19
|
+
* count: phase 1 fans `/client/apps`, the three settings singletons, and a
|
|
20
|
+
* single `/data/:functions` filter; phase 2 fans `/apps/<id>/configs?type=component`
|
|
21
|
+
* only for apps with a non-empty `extensions[]`.
|
|
22
|
+
*
|
|
23
|
+
* `include` / `aggregate` are NOT used here — the CLI's `node-fetch`
|
|
24
|
+
* transport cannot transmit GET-with-body, and URL-form transmission of
|
|
25
|
+
* those payloads is silently dropped server-side. Verified empirically
|
|
26
|
+
* against the live admin API. Don't reach for those primitives.
|
|
27
|
+
*/
|
|
28
|
+
private showList;
|
|
29
|
+
/**
|
|
30
|
+
* Detail mode is bounded to one round-trip's worth of latency: the install
|
|
31
|
+
* record (or `/apps/<id>` fallback), the relevant settings singleton(s),
|
|
32
|
+
* `/data/:functions?where[app_id]=<id>&where[extension]=<extId>`, and
|
|
33
|
+
* `/apps/<id>/configs?type=component` — all in parallel.
|
|
34
|
+
*/
|
|
35
|
+
private showDetail;
|
|
36
|
+
/**
|
|
37
|
+
* `extensions` emits a synthesized envelope rather than a raw API record —
|
|
38
|
+
* other inspect subcommands print one upstream record, but here the JSON
|
|
39
|
+
* nests `manifest`, `native_bindings[].record`, `bound.functions[]`, and
|
|
40
|
+
* `bound.components[]` under shared status fields.
|
|
41
|
+
*
|
|
42
|
+
* The `Next steps:` footer mixes runnable shell commands and merchant-UI
|
|
43
|
+
* lines (prefixed `(merchant)`). The structured `action` and `action_owner`
|
|
44
|
+
* fields above give an agent a parseable signal independent of the footer.
|
|
45
|
+
*/
|
|
46
|
+
private emitDetail;
|
|
47
|
+
private fetchSafe;
|
|
48
|
+
private printPreamble;
|
|
49
|
+
}
|