@swell/cli 2.2.0 → 2.3.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/app-command.js +1 -1
- package/dist/commands/api/delete.js +4 -1
- package/dist/commands/api/get.js +1 -0
- package/dist/commands/api/index.js +4 -1
- package/dist/commands/api/post.js +1 -0
- package/dist/commands/api/put.js +1 -0
- package/dist/commands/app/dev.js +5 -3
- package/dist/commands/app/init.d.ts +8 -7
- package/dist/commands/app/init.js +36 -18
- package/dist/commands/app/pull.js +2 -2
- package/dist/commands/app/push.js +2 -2
- package/dist/commands/app/version.d.ts +1 -0
- package/dist/commands/app/version.js +14 -4
- package/dist/commands/create/app.d.ts +8 -4
- package/dist/commands/create/app.js +68 -51
- package/dist/commands/create/content.d.ts +5 -6
- package/dist/commands/create/content.js +22 -38
- package/dist/commands/create/function.d.ts +6 -7
- package/dist/commands/create/function.js +94 -31
- package/dist/commands/create/index.js +13 -1
- package/dist/commands/create/model.d.ts +5 -6
- package/dist/commands/create/model.js +21 -33
- package/dist/commands/create/notification.d.ts +9 -9
- package/dist/commands/create/notification.js +117 -95
- package/dist/commands/create/setting.d.ts +21 -0
- package/dist/commands/create/setting.js +120 -0
- package/dist/commands/create/tests.d.ts +14 -0
- package/dist/commands/create/tests.js +76 -0
- package/dist/commands/create/webhook.d.ts +22 -0
- package/dist/commands/create/webhook.js +176 -0
- package/dist/commands/inspect/content.js +1 -1
- package/dist/commands/schema.d.ts +1 -0
- package/dist/commands/schema.js +51 -5
- package/dist/commands/theme/init.d.ts +8 -4
- package/dist/commands/theme/init.js +21 -8
- package/dist/create-app-command.d.ts +1 -0
- package/dist/create-app-command.js +15 -10
- package/dist/create-config-command.js +2 -2
- package/dist/help/custom-help.d.ts +89 -0
- package/dist/help/custom-help.js +337 -0
- package/dist/help/types.d.ts +75 -0
- package/dist/help/types.js +1 -0
- package/dist/lib/apps/app-config.js +2 -2
- package/dist/lib/apps/index.d.ts +2 -1
- package/dist/lib/apps/index.js +21 -4
- package/dist/lib/apps/paths.js +7 -6
- package/dist/lib/create/notification.d.ts +1 -0
- package/dist/lib/create/schemas.d.ts +1 -0
- package/dist/lib/create/schemas.js +1 -0
- package/dist/lib/create/setting.d.ts +15 -0
- package/dist/lib/create/setting.js +27 -0
- package/dist/lib/create/tests/templates/env-dts.d.ts +1 -0
- package/dist/lib/create/tests/templates/env-dts.js +15 -0
- package/dist/lib/create/tests/templates/index.d.ts +8 -0
- package/dist/lib/create/tests/templates/index.js +8 -0
- package/dist/lib/create/tests/templates/integration-test.d.ts +1 -0
- package/dist/lib/create/tests/templates/integration-test.js +19 -0
- package/dist/lib/create/tests/templates/mock-request.d.ts +2 -0
- package/dist/lib/create/tests/templates/mock-request.js +112 -0
- package/dist/lib/create/tests/templates/setup-globals.d.ts +1 -0
- package/dist/lib/create/tests/templates/setup-globals.js +23 -0
- package/dist/lib/create/tests/templates/swell-client.d.ts +1 -0
- package/dist/lib/create/tests/templates/swell-client.js +128 -0
- package/dist/lib/create/tests/templates/tsconfig.d.ts +1 -0
- package/dist/lib/create/tests/templates/tsconfig.js +17 -0
- package/dist/lib/create/tests/templates/unit-test.d.ts +1 -0
- package/dist/lib/create/tests/templates/unit-test.js +42 -0
- package/dist/lib/create/tests/templates/vitest-config.d.ts +2 -0
- package/dist/lib/create/tests/templates/vitest-config.js +122 -0
- package/dist/lib/create/tests/types.d.ts +21 -0
- package/dist/lib/create/tests/types.js +1 -0
- package/dist/lib/create/tests.d.ts +4 -0
- package/dist/lib/create/tests.js +113 -0
- package/dist/lib/create/webhook.d.ts +32 -0
- package/dist/lib/create/webhook.js +52 -0
- package/dist/swell-api-command.d.ts +14 -0
- package/dist/swell-api-command.js +113 -7
- package/oclif.manifest.json +2879 -0
- package/package.json +2 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CreateConfigCommand } from '../../create-config-command.js';
|
|
2
|
+
import { HelpMeta } from '../../help/types.js';
|
|
3
|
+
import { ConfigType } from '../../lib/apps/index.js';
|
|
4
|
+
export default class CreateSetting extends CreateConfigCommand {
|
|
5
|
+
static args: {
|
|
6
|
+
name: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static helpMeta: HelpMeta;
|
|
11
|
+
static flags: {
|
|
12
|
+
label: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
13
|
+
description: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
14
|
+
fields: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
15
|
+
overwrite: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
16
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
static summary: string;
|
|
19
|
+
createType: ConfigType;
|
|
20
|
+
run(): Promise<void>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { input } from '@inquirer/prompts';
|
|
2
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
|
+
import { CreateConfigCommand } from '../../create-config-command.js';
|
|
4
|
+
import { ConfigType } from '../../lib/apps/index.js';
|
|
5
|
+
import { toFileName } from '../../lib/create/index.js';
|
|
6
|
+
import { SCHEMAS } from '../../lib/create/schemas.js';
|
|
7
|
+
import { parseFields, toSettingLabel, } from '../../lib/create/setting.js';
|
|
8
|
+
export default class CreateSetting extends CreateConfigCommand {
|
|
9
|
+
static args = {
|
|
10
|
+
name: Args.string({
|
|
11
|
+
default: '',
|
|
12
|
+
description: 'Setting name (e.g., api-config)',
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
static description = 'Create a setting configuration in the settings folder.';
|
|
16
|
+
static examples = [
|
|
17
|
+
'$ swell create setting',
|
|
18
|
+
'$ swell create setting api-config -y',
|
|
19
|
+
'$ swell create setting api-config -f api_key:short_text,enabled:boolean -l "API Config" -y',
|
|
20
|
+
];
|
|
21
|
+
static helpMeta = {
|
|
22
|
+
usageDirect: '<name> [...] -y',
|
|
23
|
+
};
|
|
24
|
+
static flags = {
|
|
25
|
+
label: Flags.string({
|
|
26
|
+
char: 'l',
|
|
27
|
+
default: '',
|
|
28
|
+
description: 'Display label (default: titleized name)',
|
|
29
|
+
}),
|
|
30
|
+
description: Flags.string({
|
|
31
|
+
char: 'd',
|
|
32
|
+
default: '',
|
|
33
|
+
description: 'Description',
|
|
34
|
+
}),
|
|
35
|
+
fields: Flags.string({
|
|
36
|
+
char: 'f',
|
|
37
|
+
default: '',
|
|
38
|
+
description: 'Fields as id:type pairs (e.g., api_key:short_text,enabled:boolean)',
|
|
39
|
+
}),
|
|
40
|
+
overwrite: Flags.boolean({
|
|
41
|
+
default: false,
|
|
42
|
+
description: 'Overwrite existing file',
|
|
43
|
+
}),
|
|
44
|
+
yes: Flags.boolean({
|
|
45
|
+
char: 'y',
|
|
46
|
+
description: 'Skip prompts, require all arguments',
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
static summary = 'Create a setting configuration in the settings folder.';
|
|
50
|
+
createType = ConfigType.SETTING;
|
|
51
|
+
async run() {
|
|
52
|
+
const { args, flags } = await this.parse(CreateSetting);
|
|
53
|
+
const confirmYes = Boolean(flags.yes);
|
|
54
|
+
// NON-INTERACTIVE PATH
|
|
55
|
+
if (confirmYes) {
|
|
56
|
+
const argName = args.name;
|
|
57
|
+
if (!argName) {
|
|
58
|
+
this.error('Missing required argument for non-interactive mode: NAME\n\nExample: swell create setting api-config -y', { exit: 1 });
|
|
59
|
+
}
|
|
60
|
+
const name = toFileName(argName);
|
|
61
|
+
const label = flags.label || toSettingLabel(name);
|
|
62
|
+
const description = flags.description || '';
|
|
63
|
+
const fieldsRaw = (flags.fields || '');
|
|
64
|
+
const fieldPairs = fieldsRaw
|
|
65
|
+
? fieldsRaw
|
|
66
|
+
.split(',')
|
|
67
|
+
.map((v) => v.trim())
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
: [];
|
|
70
|
+
const fileName = toFileName(name);
|
|
71
|
+
const fileBody = {
|
|
72
|
+
$schema: SCHEMAS.SETTING,
|
|
73
|
+
description,
|
|
74
|
+
fields: parseFields(fieldPairs),
|
|
75
|
+
label,
|
|
76
|
+
};
|
|
77
|
+
await this.createFile({ fileBody, fileName }, flags.overwrite,
|
|
78
|
+
/* shouldConfirm */ false);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// INTERACTIVE PATH
|
|
82
|
+
let name = args.name;
|
|
83
|
+
if (!name) {
|
|
84
|
+
name = await input({
|
|
85
|
+
default: 'my-setting',
|
|
86
|
+
message: 'Setting name',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
name = toFileName(name);
|
|
90
|
+
let { label, description } = flags;
|
|
91
|
+
const { fields: fieldsFlag, overwrite } = flags;
|
|
92
|
+
if (!label) {
|
|
93
|
+
label = await input({
|
|
94
|
+
default: toSettingLabel(name),
|
|
95
|
+
message: 'Setting label',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (!description) {
|
|
99
|
+
description = await input({
|
|
100
|
+
default: '',
|
|
101
|
+
message: 'Describe what this setting is for',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const fieldsRaw = (fieldsFlag || '');
|
|
105
|
+
const fieldPairs = fieldsRaw
|
|
106
|
+
? fieldsRaw
|
|
107
|
+
.split(',')
|
|
108
|
+
.map((v) => v.trim())
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
: [];
|
|
111
|
+
const fileName = toFileName(name);
|
|
112
|
+
const fileBody = {
|
|
113
|
+
$schema: SCHEMAS.SETTING,
|
|
114
|
+
description,
|
|
115
|
+
fields: parseFields(fieldPairs),
|
|
116
|
+
label,
|
|
117
|
+
};
|
|
118
|
+
await this.createFile({ fileBody, fileName }, overwrite);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AppCommand } from '../../app-command.js';
|
|
2
|
+
import { HelpMeta } from '../../help/types.js';
|
|
3
|
+
export default class CreateTests extends AppCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static helpMeta: HelpMeta;
|
|
7
|
+
static flags: {
|
|
8
|
+
overwrite: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
9
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
static summary: string;
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private logScaffoldResult;
|
|
14
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { AppCommand } from '../../app-command.js';
|
|
3
|
+
import { toAppId } from '../../lib/create/index.js';
|
|
4
|
+
import { createTestsScaffold, } from '../../lib/create/tests.js';
|
|
5
|
+
export default class CreateTests extends AppCommand {
|
|
6
|
+
static description = 'Initialize a vitest + Cloudflare Workers test setup that reuses swell-cli authentication.';
|
|
7
|
+
static examples = [
|
|
8
|
+
'$ swell create tests',
|
|
9
|
+
'$ swell create tests --overwrite',
|
|
10
|
+
];
|
|
11
|
+
static helpMeta = {
|
|
12
|
+
usageDirect: '[-y]',
|
|
13
|
+
};
|
|
14
|
+
static flags = {
|
|
15
|
+
overwrite: Flags.boolean({
|
|
16
|
+
default: false,
|
|
17
|
+
description: 'Overwrite existing test files',
|
|
18
|
+
}),
|
|
19
|
+
yes: Flags.boolean({
|
|
20
|
+
char: 'y',
|
|
21
|
+
default: false,
|
|
22
|
+
description: 'Skip prompts, require all arguments',
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
static summary = 'Create tests scaffolding for your Swell app.';
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(CreateTests);
|
|
28
|
+
const appIdFromConfig = (this.swellConfig.get('id') ||
|
|
29
|
+
this.swellConfig.get('private_id') ||
|
|
30
|
+
this.swellConfig.get('public_id'));
|
|
31
|
+
const appId = appIdFromConfig ||
|
|
32
|
+
toAppId(this.swellConfig.get('name')) ||
|
|
33
|
+
'app';
|
|
34
|
+
const result = await createTestsScaffold({
|
|
35
|
+
appPath: this.appPath,
|
|
36
|
+
appId,
|
|
37
|
+
overwrite: Boolean(flags.overwrite),
|
|
38
|
+
});
|
|
39
|
+
this.logScaffoldResult(result);
|
|
40
|
+
}
|
|
41
|
+
logScaffoldResult(result) {
|
|
42
|
+
const hasChanges = result.createdFiles.length > 0 || result.packageJsonUpdated;
|
|
43
|
+
if (hasChanges) {
|
|
44
|
+
this.log('');
|
|
45
|
+
this.log('✓ Created test scaffolding');
|
|
46
|
+
}
|
|
47
|
+
if (result.createdFiles.length > 0) {
|
|
48
|
+
this.log('');
|
|
49
|
+
this.log('Created:');
|
|
50
|
+
for (const file of result.createdFiles) {
|
|
51
|
+
this.log(` ${file}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (result.skippedFiles.length > 0) {
|
|
55
|
+
this.log('');
|
|
56
|
+
this.log('Skipped (already exist):');
|
|
57
|
+
for (const file of result.skippedFiles) {
|
|
58
|
+
this.log(` ${file}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (result.packageJsonUpdated) {
|
|
62
|
+
this.log('');
|
|
63
|
+
this.log('Updated package.json with test scripts and devDependencies.');
|
|
64
|
+
}
|
|
65
|
+
for (const warning of result.warnings) {
|
|
66
|
+
this.warn(warning);
|
|
67
|
+
}
|
|
68
|
+
if (hasChanges) {
|
|
69
|
+
this.log('');
|
|
70
|
+
this.log('Next steps:');
|
|
71
|
+
this.log(' 1. npm install');
|
|
72
|
+
this.log(' 2. npm test');
|
|
73
|
+
}
|
|
74
|
+
this.log('');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CreateConfigCommand } from '../../create-config-command.js';
|
|
2
|
+
import { HelpMeta } from '../../help/types.js';
|
|
3
|
+
import { ConfigType } from '../../lib/apps/index.js';
|
|
4
|
+
export default class CreateWebhook extends CreateConfigCommand {
|
|
5
|
+
static args: {
|
|
6
|
+
name: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static helpMeta: HelpMeta;
|
|
11
|
+
static flags: {
|
|
12
|
+
url: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
13
|
+
events: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
14
|
+
description: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
15
|
+
enabled: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
16
|
+
overwrite: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
17
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
static summary: string;
|
|
20
|
+
createType: ConfigType;
|
|
21
|
+
run(): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { confirm, input } from '@inquirer/prompts';
|
|
2
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
|
+
import { CreateConfigCommand } from '../../create-config-command.js';
|
|
4
|
+
import { ConfigType } from '../../lib/apps/index.js';
|
|
5
|
+
import { toFileName } from '../../lib/create/index.js';
|
|
6
|
+
import { SCHEMAS } from '../../lib/create/schemas.js';
|
|
7
|
+
import { parseEvents, validateEventFormat, validateUrl, } from '../../lib/create/webhook.js';
|
|
8
|
+
export default class CreateWebhook extends CreateConfigCommand {
|
|
9
|
+
static args = {
|
|
10
|
+
name: Args.string({
|
|
11
|
+
default: '',
|
|
12
|
+
description: 'Webhook name (e.g., order-sync)',
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
static description = 'Create a webhook configuration in the webhooks folder.';
|
|
16
|
+
static examples = [
|
|
17
|
+
'$ swell create webhook',
|
|
18
|
+
'$ swell create webhook order-sync -u https://example.com/hook -e order.created -y',
|
|
19
|
+
'$ swell create webhook payment-handler -u https://api.example.com/payments -e payment.succeeded,payment.failed --enabled -y',
|
|
20
|
+
];
|
|
21
|
+
static helpMeta = {
|
|
22
|
+
usageDirect: '<name> -u <url> -e <events> [...] -y',
|
|
23
|
+
};
|
|
24
|
+
static flags = {
|
|
25
|
+
url: Flags.string({
|
|
26
|
+
char: 'u',
|
|
27
|
+
default: '',
|
|
28
|
+
description: 'Endpoint URL (required with -y)',
|
|
29
|
+
}),
|
|
30
|
+
events: Flags.string({
|
|
31
|
+
char: 'e',
|
|
32
|
+
default: '',
|
|
33
|
+
description: 'Events to listen for (e.g., order.created,payment.succeeded)',
|
|
34
|
+
}),
|
|
35
|
+
description: Flags.string({
|
|
36
|
+
char: 'd',
|
|
37
|
+
default: '',
|
|
38
|
+
description: 'Description',
|
|
39
|
+
}),
|
|
40
|
+
enabled: Flags.boolean({
|
|
41
|
+
default: false,
|
|
42
|
+
description: 'Enable webhook (default: false)',
|
|
43
|
+
}),
|
|
44
|
+
overwrite: Flags.boolean({
|
|
45
|
+
default: false,
|
|
46
|
+
description: 'Overwrite existing file',
|
|
47
|
+
}),
|
|
48
|
+
yes: Flags.boolean({
|
|
49
|
+
char: 'y',
|
|
50
|
+
description: 'Skip prompts, require all arguments',
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
static summary = 'Create a webhook configuration in the webhooks folder.';
|
|
54
|
+
createType = ConfigType.WEBHOOK;
|
|
55
|
+
async run() {
|
|
56
|
+
const { args, flags } = await this.parse(CreateWebhook);
|
|
57
|
+
const confirmYes = Boolean(flags.yes);
|
|
58
|
+
// NON-INTERACTIVE PATH
|
|
59
|
+
if (confirmYes) {
|
|
60
|
+
const argName = args.name;
|
|
61
|
+
if (!argName) {
|
|
62
|
+
this.error('Missing required argument for non-interactive mode: NAME\n\nExample: swell create webhook payment-handler -u https://example.com/hook -e order.created -y', { exit: 1 });
|
|
63
|
+
}
|
|
64
|
+
if (!flags.url) {
|
|
65
|
+
this.error('Missing required flag for non-interactive mode: --url\n\nExample: swell create webhook payment-handler -u https://example.com/hook -e order.created -y', { exit: 1 });
|
|
66
|
+
}
|
|
67
|
+
if (!flags.events) {
|
|
68
|
+
this.error('Missing required flag for non-interactive mode: --events\n\nExample: swell create webhook payment-handler -u https://example.com/hook -e order.created -y', { exit: 1 });
|
|
69
|
+
}
|
|
70
|
+
// Validate URL format
|
|
71
|
+
validateUrl(flags.url, this.error.bind(this));
|
|
72
|
+
// Parse and validate events
|
|
73
|
+
const events = parseEvents(flags.events);
|
|
74
|
+
if (events.length === 0) {
|
|
75
|
+
this.error('At least one event is required.\n\nExample: swell create webhook payment-handler -u https://example.com/hook -e order.created -y', { exit: 1 });
|
|
76
|
+
}
|
|
77
|
+
validateEventFormat(events, this.error.bind(this));
|
|
78
|
+
const name = toFileName(argName);
|
|
79
|
+
const fileName = toFileName(name);
|
|
80
|
+
const fileBody = {
|
|
81
|
+
$schema: SCHEMAS.WEBHOOK,
|
|
82
|
+
description: flags.description || '',
|
|
83
|
+
enabled: flags.enabled,
|
|
84
|
+
events,
|
|
85
|
+
url: flags.url,
|
|
86
|
+
};
|
|
87
|
+
await this.createFile({ fileBody, fileName }, flags.overwrite,
|
|
88
|
+
/* shouldConfirm */ false);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// INTERACTIVE PATH
|
|
92
|
+
let name = args.name;
|
|
93
|
+
if (!name) {
|
|
94
|
+
name = await input({
|
|
95
|
+
default: 'my-webhook',
|
|
96
|
+
message: 'Webhook name',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
name = toFileName(name);
|
|
100
|
+
// URL prompt (required)
|
|
101
|
+
let { url } = flags;
|
|
102
|
+
if (url) {
|
|
103
|
+
validateUrl(url, this.error.bind(this));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
url = await input({
|
|
107
|
+
message: 'Webhook endpoint URL',
|
|
108
|
+
validate(value) {
|
|
109
|
+
if (!value) {
|
|
110
|
+
return 'URL is required';
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const parsed = new URL(value);
|
|
114
|
+
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
115
|
+
return 'URL must use HTTP or HTTPS protocol';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return 'Invalid URL format';
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Events prompt (required)
|
|
126
|
+
let events;
|
|
127
|
+
if (flags.events) {
|
|
128
|
+
events = parseEvents(flags.events);
|
|
129
|
+
validateEventFormat(events, this.error.bind(this));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
const eventsInput = await input({
|
|
133
|
+
message: 'Events to subscribe (e.g., order.created,payment.succeeded)',
|
|
134
|
+
validate(value) {
|
|
135
|
+
if (!value) {
|
|
136
|
+
return 'At least one event is required';
|
|
137
|
+
}
|
|
138
|
+
const parsed = parseEvents(value);
|
|
139
|
+
for (const event of parsed) {
|
|
140
|
+
const parts = event.split('.');
|
|
141
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
142
|
+
return `Invalid event format: ${event}. Use model.action pattern.`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
events = parseEvents(eventsInput);
|
|
149
|
+
}
|
|
150
|
+
// Description prompt (optional)
|
|
151
|
+
let { description, enabled } = flags;
|
|
152
|
+
const { overwrite } = flags;
|
|
153
|
+
if (!description) {
|
|
154
|
+
description = await input({
|
|
155
|
+
default: '',
|
|
156
|
+
message: 'Describe what this webhook does',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// Enabled prompt (optional)
|
|
160
|
+
if (!enabled) {
|
|
161
|
+
enabled = await confirm({
|
|
162
|
+
default: false,
|
|
163
|
+
message: 'Enable webhook now?',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const fileName = toFileName(name);
|
|
167
|
+
const fileBody = {
|
|
168
|
+
$schema: SCHEMAS.WEBHOOK,
|
|
169
|
+
description,
|
|
170
|
+
enabled,
|
|
171
|
+
events,
|
|
172
|
+
url,
|
|
173
|
+
};
|
|
174
|
+
await this.createFile({ fileBody, fileName }, overwrite);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -132,7 +132,7 @@ With a content ID, it retrieves the configuration for that specific content view
|
|
|
132
132
|
return resolvedId; // custom IDs don't need reverse resolution
|
|
133
133
|
}
|
|
134
134
|
// Check if identifier is already an app ID (ObjectID format)
|
|
135
|
-
if (
|
|
135
|
+
if (/^[\da-f]{24}$/.test(identifier)) {
|
|
136
136
|
return resolvedId; // already unresolved format
|
|
137
137
|
}
|
|
138
138
|
// type === 'app': resolve slug to app ID
|
package/dist/commands/schema.js
CHANGED
|
@@ -5,6 +5,7 @@ import ajvErrors from 'ajv-errors';
|
|
|
5
5
|
import addFormats from 'ajv-formats';
|
|
6
6
|
import * as fs from 'node:fs';
|
|
7
7
|
import parseJson from 'parse-json';
|
|
8
|
+
import { bundleFunction } from '../lib/bundle.js';
|
|
8
9
|
import { SCHEMAS_BASE_URL } from '../lib/constants.js';
|
|
9
10
|
import { SwellCommand } from '../swell-command.js';
|
|
10
11
|
const SCHEMA_DEFINITIONS = Object.freeze({
|
|
@@ -23,9 +24,13 @@ const SCHEMA_DEFINITIONS = Object.freeze({
|
|
|
23
24
|
webhook: {
|
|
24
25
|
description: 'Webhook definitions (/webhooks/*.json)',
|
|
25
26
|
},
|
|
27
|
+
function: {
|
|
28
|
+
description: 'Function definitions (/functions/*.ts or *.js)',
|
|
29
|
+
hasJsonSchema: false,
|
|
30
|
+
},
|
|
26
31
|
});
|
|
27
32
|
export default class Schema extends SwellCommand {
|
|
28
|
-
static summary = 'View or validate Swell config
|
|
33
|
+
static summary = 'View or validate Swell config schemas.';
|
|
29
34
|
static examples = [
|
|
30
35
|
{
|
|
31
36
|
description: 'List available schema types',
|
|
@@ -44,22 +49,26 @@ export default class Schema extends SwellCommand {
|
|
|
44
49
|
command: '<%= config.bin %> <%= command.id %> content --format=dts',
|
|
45
50
|
},
|
|
46
51
|
{
|
|
47
|
-
description: 'Validate a file',
|
|
52
|
+
description: 'Validate a JSON config file',
|
|
48
53
|
command: '<%= config.bin %> <%= command.id %> content myfile.json',
|
|
49
54
|
},
|
|
50
55
|
{
|
|
51
56
|
description: 'Validate from stdin',
|
|
52
57
|
command: 'cat myfile.json | <%= config.bin %> <%= command.id %> content -',
|
|
53
58
|
},
|
|
59
|
+
{
|
|
60
|
+
description: 'Validate a function file',
|
|
61
|
+
command: '<%= config.bin %> <%= command.id %> function myfunction.ts',
|
|
62
|
+
},
|
|
54
63
|
];
|
|
55
64
|
static args = {
|
|
56
65
|
type: Args.string({
|
|
57
66
|
required: false,
|
|
58
|
-
description: 'Schema type (e.g. model, content, setting, notification, webhook)',
|
|
67
|
+
description: 'Schema type (e.g. model, content, setting, notification, webhook, function)',
|
|
59
68
|
}),
|
|
60
69
|
file: Args.string({
|
|
61
70
|
required: false,
|
|
62
|
-
description: 'Path to
|
|
71
|
+
description: 'Path to file to validate (or use "-" for stdin)',
|
|
63
72
|
}),
|
|
64
73
|
};
|
|
65
74
|
static flags = {
|
|
@@ -90,7 +99,9 @@ export default class Schema extends SwellCommand {
|
|
|
90
99
|
if (format) {
|
|
91
100
|
throw new Error('--format cannot be used with file validation');
|
|
92
101
|
}
|
|
93
|
-
await
|
|
102
|
+
await (type === 'function'
|
|
103
|
+
? this.validateFunction(file)
|
|
104
|
+
: this.validate(type, file));
|
|
94
105
|
return;
|
|
95
106
|
}
|
|
96
107
|
// Show schema info or output schema
|
|
@@ -119,6 +130,13 @@ export default class Schema extends SwellCommand {
|
|
|
119
130
|
this.log(`Run "swell schema --help" for more information.`);
|
|
120
131
|
}
|
|
121
132
|
async outputSchema(type, format) {
|
|
133
|
+
const definition = SCHEMA_DEFINITIONS[type];
|
|
134
|
+
// Guard against json-schema formats for types without JSON Schema
|
|
135
|
+
if ('hasJsonSchema' in definition &&
|
|
136
|
+
definition.hasJsonSchema === false &&
|
|
137
|
+
(format === 'json-schema' || format === 'json-schema-bundle')) {
|
|
138
|
+
throw new Error(`'${type}' does not have a JSON Schema. Use --format=dts for TypeScript declarations.`);
|
|
139
|
+
}
|
|
122
140
|
let output;
|
|
123
141
|
switch (format) {
|
|
124
142
|
case 'json-schema': {
|
|
@@ -202,6 +220,34 @@ export default class Schema extends SwellCommand {
|
|
|
202
220
|
}
|
|
203
221
|
this.log('Valid model definition');
|
|
204
222
|
}
|
|
223
|
+
async validateFunction(file) {
|
|
224
|
+
if (!fs.existsSync(file)) {
|
|
225
|
+
throw new Error(`File not found: ${file}`);
|
|
226
|
+
}
|
|
227
|
+
const filePath = fs.realpathSync(file);
|
|
228
|
+
// Attempt to bundle the function (validates syntax, imports, etc.)
|
|
229
|
+
let bundleResult;
|
|
230
|
+
try {
|
|
231
|
+
bundleResult = await bundleFunction(filePath);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
throw new Error(`Function compilation failed:\n• ${error.message}`);
|
|
235
|
+
}
|
|
236
|
+
const { config } = bundleResult;
|
|
237
|
+
// Validate config exists
|
|
238
|
+
if (!config) {
|
|
239
|
+
throw new Error('Invalid function:\n• Function must export a `config` object');
|
|
240
|
+
}
|
|
241
|
+
// Validate trigger exclusivity
|
|
242
|
+
const triggers = ['route', 'model', 'cron'].filter((t) => config[t]);
|
|
243
|
+
if (triggers.length === 0) {
|
|
244
|
+
throw new Error('Invalid function config:\n• Config must specify one of: route, model, cron');
|
|
245
|
+
}
|
|
246
|
+
if (triggers.length > 1) {
|
|
247
|
+
throw new Error(`Invalid function config:\n• Multiple triggers specified: ${triggers.join(', ')}. Use exactly one.`);
|
|
248
|
+
}
|
|
249
|
+
this.log('Valid function definition');
|
|
250
|
+
}
|
|
205
251
|
getFileInput(file) {
|
|
206
252
|
if (!fs.existsSync(file)) {
|
|
207
253
|
throw new Error(`File not found: ${file}`);
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { HelpMeta } from '../../help/types.js';
|
|
1
2
|
import InitApp from '../app/init.js';
|
|
2
3
|
export default class InitTheme extends InitApp {
|
|
4
|
+
protected commandExample: string;
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
3
8
|
static description: string;
|
|
4
|
-
static examples:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}[];
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static helpMeta: HelpMeta;
|
|
11
|
+
static flags: any;
|
|
8
12
|
appType: string;
|
|
9
13
|
}
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
1
2
|
import InitApp from '../app/init.js';
|
|
2
3
|
export default class InitTheme extends InitApp {
|
|
4
|
+
// Override command example for error messages
|
|
5
|
+
commandExample = 'swell theme init';
|
|
6
|
+
static args = {
|
|
7
|
+
id: Args.string({
|
|
8
|
+
default: '',
|
|
9
|
+
description: 'Theme identifier (defaults to current directory name)',
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
3
12
|
static description = 'Initialize theme swell.json in the current directory.';
|
|
4
13
|
static examples = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
command: 'swell theme init -y',
|
|
11
|
-
description: 'Initialize theme and accept all default values.',
|
|
12
|
-
},
|
|
14
|
+
'$ swell theme init',
|
|
15
|
+
'$ swell theme init my-theme',
|
|
16
|
+
'$ swell theme init -y',
|
|
17
|
+
'$ swell theme init my-theme -y',
|
|
18
|
+
'$ swell theme init my-theme --storefront-app proxima -y',
|
|
13
19
|
];
|
|
20
|
+
static helpMeta = {
|
|
21
|
+
usageDirect: '[id] --storefront-app <app> [...] -y',
|
|
22
|
+
};
|
|
23
|
+
// Inherit all flags from InitApp (which inherits from CreateApp)
|
|
24
|
+
static flags = {
|
|
25
|
+
...InitApp.flags,
|
|
26
|
+
};
|
|
14
27
|
appType = 'theme';
|
|
15
28
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SwellCommand } from './swell-command.js';
|
|
2
2
|
export declare abstract class CreateAppCommand extends SwellCommand {
|
|
3
|
+
protected commandExample: string;
|
|
3
4
|
static baseFlags: {
|
|
4
5
|
frontend: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
5
6
|
'storefront-app': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|