@knocklabs/cli 0.3.1 → 1.0.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/README.md +343 -55
- package/dist/commands/branch/delete.js +4 -1
- package/dist/commands/branch/merge.js +82 -0
- package/dist/commands/channel/list.js +73 -0
- package/dist/commands/environment/list.js +73 -0
- package/dist/commands/guide/new.js +276 -0
- package/dist/commands/guide/open.js +106 -0
- package/dist/commands/guide/pull.js +5 -6
- package/dist/commands/guide/push.js +1 -1
- package/dist/commands/guide/validate.js +1 -1
- package/dist/commands/init.js +108 -0
- package/dist/commands/layout/new.js +228 -0
- package/dist/commands/layout/open.js +106 -0
- package/dist/commands/layout/pull.js +5 -6
- package/dist/commands/layout/push.js +1 -1
- package/dist/commands/layout/validate.js +1 -1
- package/dist/commands/message-type/new.js +228 -0
- package/dist/commands/message-type/open.js +106 -0
- package/dist/commands/message-type/pull.js +5 -6
- package/dist/commands/message-type/push.js +1 -1
- package/dist/commands/message-type/validate.js +1 -1
- package/dist/commands/partial/new.js +274 -0
- package/dist/commands/partial/open.js +106 -0
- package/dist/commands/partial/pull.js +5 -6
- package/dist/commands/partial/push.js +1 -1
- package/dist/commands/partial/validate.js +1 -1
- package/dist/commands/pull.js +7 -2
- package/dist/commands/push.js +6 -4
- package/dist/commands/translation/pull.js +1 -1
- package/dist/commands/translation/push.js +1 -1
- package/dist/commands/translation/validate.js +1 -1
- package/dist/commands/workflow/new.js +179 -54
- package/dist/commands/workflow/open.js +106 -0
- package/dist/commands/workflow/pull.js +6 -8
- package/dist/commands/workflow/push.js +1 -1
- package/dist/commands/workflow/validate.js +1 -1
- package/dist/lib/api-v1.js +23 -2
- package/dist/lib/auth.js +1 -1
- package/dist/lib/base-command.js +18 -15
- package/dist/lib/helpers/project-config.js +158 -0
- package/dist/lib/helpers/request.js +1 -2
- package/dist/lib/helpers/string.js +4 -4
- package/dist/lib/helpers/typegen.js +1 -1
- package/dist/lib/marshal/email-layout/generator.js +152 -0
- package/dist/lib/marshal/email-layout/helpers.js +6 -9
- package/dist/lib/marshal/email-layout/index.js +1 -0
- package/dist/lib/marshal/email-layout/writer.js +15 -3
- package/dist/lib/marshal/guide/generator.js +163 -0
- package/dist/lib/marshal/guide/helpers.js +6 -10
- package/dist/lib/marshal/guide/index.js +1 -0
- package/dist/lib/marshal/guide/writer.js +5 -9
- package/dist/lib/marshal/message-type/generator.js +139 -0
- package/dist/lib/marshal/message-type/helpers.js +6 -10
- package/dist/lib/marshal/message-type/index.js +1 -0
- package/dist/lib/marshal/message-type/writer.js +5 -1
- package/dist/lib/marshal/partial/generator.js +159 -0
- package/dist/lib/marshal/partial/helpers.js +6 -10
- package/dist/lib/marshal/partial/index.js +1 -0
- package/dist/lib/marshal/partial/writer.js +3 -0
- package/dist/lib/marshal/translation/helpers.js +6 -10
- package/dist/lib/marshal/translation/processor.isomorphic.js +4 -4
- package/dist/lib/marshal/translation/writer.js +2 -2
- package/dist/lib/marshal/workflow/generator.js +175 -19
- package/dist/lib/marshal/workflow/helpers.js +7 -10
- package/dist/lib/run-context/loader.js +5 -0
- package/dist/lib/templates.js +131 -0
- package/dist/lib/urls.js +16 -0
- package/oclif.manifest.json +1446 -547
- package/package.json +11 -9
|
@@ -25,6 +25,7 @@ _export(exports, {
|
|
|
25
25
|
const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
|
|
26
26
|
const _core = require("@oclif/core");
|
|
27
27
|
const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
|
|
28
|
+
const _projectconfig = require("../../helpers/project-config");
|
|
28
29
|
const _processorisomorphic = require("./processor.isomorphic");
|
|
29
30
|
function _getRequireWildcardCache(nodeInterop) {
|
|
30
31
|
if (typeof WeakMap !== "function") return null;
|
|
@@ -74,7 +75,7 @@ const lsPartialJson = async (dirPath)=>{
|
|
|
74
75
|
return exists ? partialJsonPath : undefined;
|
|
75
76
|
};
|
|
76
77
|
const isPartialDir = async (dirPath)=>Boolean(await lsPartialJson(dirPath));
|
|
77
|
-
const ensureValidCommandTarget = async (props, runContext)=>{
|
|
78
|
+
const ensureValidCommandTarget = async (props, runContext, projectConfig)=>{
|
|
78
79
|
const { args, flags } = props;
|
|
79
80
|
const { commandId, resourceDir: resourceDirCtx, cwd: runCwd } = runContext;
|
|
80
81
|
// If the target resource is a different type than the current resource dir
|
|
@@ -86,6 +87,8 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
86
87
|
if (flags.all && args.partialKey) {
|
|
87
88
|
return _core.ux.error(`partialKey arg \`${args.partialKey}\` cannot also be provided when using --all`);
|
|
88
89
|
}
|
|
90
|
+
// Default to knock project config first if present, otherwise cwd.
|
|
91
|
+
const partialsIndexDirCtx = await (0, _projectconfig.resolveResourceDir)(projectConfig, "partial", runCwd);
|
|
89
92
|
// --all flag is given, which means no partial key arg.
|
|
90
93
|
if (flags.all) {
|
|
91
94
|
// If --all flag used inside a partial directory, then require a partials
|
|
@@ -93,16 +96,9 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
93
96
|
if (resourceDirCtx && !flags["partials-dir"]) {
|
|
94
97
|
return _core.ux.error("Missing required flag partials-dir");
|
|
95
98
|
}
|
|
96
|
-
// Targeting all partial dirs in the partials index dir.
|
|
97
|
-
// TODO: Default to the knock project config first if present before cwd.
|
|
98
|
-
const defaultToCwd = {
|
|
99
|
-
abspath: runCwd,
|
|
100
|
-
exists: true
|
|
101
|
-
};
|
|
102
|
-
const indexDirCtx = flags["partials-dir"] || defaultToCwd;
|
|
103
99
|
return {
|
|
104
100
|
type: "partialsIndexDir",
|
|
105
|
-
context:
|
|
101
|
+
context: flags["partials-dir"] || partialsIndexDirCtx
|
|
106
102
|
};
|
|
107
103
|
}
|
|
108
104
|
// Partial key arg is given, which means no --all flag.
|
|
@@ -110,7 +106,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
110
106
|
if (resourceDirCtx && resourceDirCtx.key !== args.partialKey) {
|
|
111
107
|
return _core.ux.error(`Cannot run ${commandId} \`${args.partialKey}\` inside another partial directory:\n${resourceDirCtx.key}`);
|
|
112
108
|
}
|
|
113
|
-
const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(
|
|
109
|
+
const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(partialsIndexDirCtx.abspath, args.partialKey);
|
|
114
110
|
const partialDirCtx = {
|
|
115
111
|
type: "partial",
|
|
116
112
|
key: args.partialKey,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
+
_export_star(require("./generator"), exports);
|
|
5
6
|
_export_star(require("./helpers"), exports);
|
|
6
7
|
_export_star(require("./processor.isomorphic"), exports);
|
|
7
8
|
_export_star(require("./reader"), exports);
|
|
@@ -42,6 +42,7 @@ const _core = require("@oclif/core");
|
|
|
42
42
|
const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
|
|
43
43
|
const _localecodes = /*#__PURE__*/ _interop_require_default(require("locale-codes"));
|
|
44
44
|
const _fs = require("../../helpers/fs");
|
|
45
|
+
const _projectconfig = require("../../helpers/project-config");
|
|
45
46
|
const _processorisomorphic = require("./processor.isomorphic");
|
|
46
47
|
function _interop_require_default(obj) {
|
|
47
48
|
return obj && obj.__esModule ? obj : {
|
|
@@ -136,7 +137,7 @@ const parseTranslationRef = (reference)=>{
|
|
|
136
137
|
// Invalid pattern.
|
|
137
138
|
return undefined;
|
|
138
139
|
};
|
|
139
|
-
const ensureValidCommandTarget = async (props, runContext)=>{
|
|
140
|
+
const ensureValidCommandTarget = async (props, runContext, projectConfig)=>{
|
|
140
141
|
const { flags, args } = props;
|
|
141
142
|
const { commandId, resourceDir: resourceDirCtx, cwd: runCwd } = runContext;
|
|
142
143
|
// Error, trying to run the command not in a translation directory.
|
|
@@ -147,6 +148,8 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
147
148
|
if (!args.translationRef && !flags.all) {
|
|
148
149
|
_core.ux.error("At least one of translation ref arg or --all flag must be given");
|
|
149
150
|
}
|
|
151
|
+
// Default to knock project config first if present, otherwise cwd.
|
|
152
|
+
const translationsIndexDirCtx = await (0, _projectconfig.resolveResourceDir)(projectConfig, "translation", runCwd);
|
|
150
153
|
// No translationRef arg, which means --all flag is used.
|
|
151
154
|
if (!args.translationRef) {
|
|
152
155
|
// Targeting all translation files in the current locale directory.
|
|
@@ -156,16 +159,9 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
156
159
|
context: resourceDirCtx
|
|
157
160
|
};
|
|
158
161
|
}
|
|
159
|
-
// Targeting all translation files in the translations index dir.
|
|
160
|
-
// TODO: Default to the knock project config first if present before cwd.
|
|
161
|
-
const defaultToCwd = {
|
|
162
|
-
abspath: runCwd,
|
|
163
|
-
exists: true
|
|
164
|
-
};
|
|
165
|
-
const indexDirCtx = flags["translations-dir"] || defaultToCwd;
|
|
166
162
|
return {
|
|
167
163
|
type: "translationsIndexDir",
|
|
168
|
-
context:
|
|
164
|
+
context: flags["translations-dir"] || translationsIndexDirCtx
|
|
169
165
|
};
|
|
170
166
|
}
|
|
171
167
|
// From this point on, we have translationRef so parse and validate the format.
|
|
@@ -178,7 +174,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
178
174
|
if (resourceDirCtx && resourceDirCtx.key !== localeCode) {
|
|
179
175
|
return _core.ux.error(`Cannot run ${commandId} with \`${args.translationRef}\` inside a ${resourceDirCtx.key} directory`);
|
|
180
176
|
}
|
|
181
|
-
const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : flags["translations-dir"] ? _nodepath.resolve(flags["translations-dir"].abspath, localeCode) : _nodepath.resolve(
|
|
177
|
+
const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : flags["translations-dir"] ? _nodepath.resolve(flags["translations-dir"].abspath, localeCode) : _nodepath.resolve(translationsIndexDirCtx.abspath, localeCode);
|
|
182
178
|
// Got translationRef arg but no --all flag, which means target only a single
|
|
183
179
|
// translation file.
|
|
184
180
|
if (!flags.all) {
|
|
@@ -32,14 +32,14 @@ const SUPPORTED_TRANSLATION_FORMATS = [
|
|
|
32
32
|
const DEFAULT_TRANSLATION_FORMAT = "json";
|
|
33
33
|
const formatRef = (localeCode, namespace)=>namespace ? `${namespace}.${localeCode}` : localeCode;
|
|
34
34
|
const formatFileName = (input, options)=>{
|
|
35
|
-
var
|
|
36
|
-
const extension = (
|
|
35
|
+
var _ref;
|
|
36
|
+
const extension = (_ref = options === null || options === void 0 ? void 0 : options.format) !== null && _ref !== void 0 ? _ref : DEFAULT_TRANSLATION_FORMAT;
|
|
37
37
|
const ref = typeof input === "string" ? input : formatRef(input.locale_code, input.namespace);
|
|
38
38
|
return `${ref}.${extension}`;
|
|
39
39
|
};
|
|
40
40
|
const buildTranslationDirBundle = (input, options)=>{
|
|
41
|
-
var
|
|
42
|
-
const format = (
|
|
41
|
+
var _ref;
|
|
42
|
+
const format = (_ref = options === null || options === void 0 ? void 0 : options.format) !== null && _ref !== void 0 ? _ref : DEFAULT_TRANSLATION_FORMAT;
|
|
43
43
|
if (Array.isArray(input)) {
|
|
44
44
|
const translations = input;
|
|
45
45
|
return Object.fromEntries(translations.map((translation)=>[
|
|
@@ -65,8 +65,8 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
65
65
|
return newObj;
|
|
66
66
|
}
|
|
67
67
|
const writeTranslationFile = async (translationFileCtx, translation, options)=>{
|
|
68
|
-
var
|
|
69
|
-
const format = (
|
|
68
|
+
var _ref;
|
|
69
|
+
const format = (_ref = options === null || options === void 0 ? void 0 : options.format) !== null && _ref !== void 0 ? _ref : _processorisomorphic.DEFAULT_TRANSLATION_FORMAT;
|
|
70
70
|
switch(format){
|
|
71
71
|
case "json":
|
|
72
72
|
return _fsextra.outputJson(translationFileCtx.abspath, JSON.parse(translation.content), {
|
|
@@ -9,9 +9,18 @@ function _export(target, all) {
|
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
|
+
get StepTagChoices () {
|
|
13
|
+
return StepTagChoices;
|
|
14
|
+
},
|
|
12
15
|
get generateWorkflowDir () {
|
|
13
16
|
return generateWorkflowDir;
|
|
14
17
|
},
|
|
18
|
+
get generateWorkflowFromTemplate () {
|
|
19
|
+
return generateWorkflowFromTemplate;
|
|
20
|
+
},
|
|
21
|
+
get getAvailableStepTypes () {
|
|
22
|
+
return getAvailableStepTypes;
|
|
23
|
+
},
|
|
15
24
|
get parseStepsInput () {
|
|
16
25
|
return parseStepsInput;
|
|
17
26
|
},
|
|
@@ -22,7 +31,9 @@ _export(exports, {
|
|
|
22
31
|
const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
|
|
23
32
|
const _lodash = require("lodash");
|
|
24
33
|
const _constisomorphic = require("../shared/const.isomorphic");
|
|
34
|
+
const _templates = /*#__PURE__*/ _interop_require_wildcard(require("../../templates"));
|
|
25
35
|
const _processorisomorphic = require("./processor.isomorphic");
|
|
36
|
+
const _reader = require("./reader");
|
|
26
37
|
const _types = require("./types");
|
|
27
38
|
const _writer = require("./writer");
|
|
28
39
|
function _getRequireWildcardCache(nodeInterop) {
|
|
@@ -115,13 +126,14 @@ const scaffoldHttpFetchStep = (refSuffix)=>{
|
|
|
115
126
|
{}
|
|
116
127
|
];
|
|
117
128
|
};
|
|
118
|
-
const scaffoldEmailChannelStep = (refSuffix)=>{
|
|
129
|
+
const scaffoldEmailChannelStep = (refSuffix, channels)=>{
|
|
119
130
|
const stepRef = `email_${refSuffix}`;
|
|
120
131
|
const templateFilePath = newTemplateFilePath(stepRef, "html_body", "html");
|
|
132
|
+
const firstChannel = channels[0];
|
|
121
133
|
const scaffoldedStep = {
|
|
122
134
|
ref: stepRef,
|
|
123
135
|
type: _types.StepType.Channel,
|
|
124
|
-
channel_key: "<EMAIL CHANNEL KEY>",
|
|
136
|
+
channel_key: firstChannel ? firstChannel.key : "<EMAIL CHANNEL KEY>",
|
|
125
137
|
template: {
|
|
126
138
|
settings: {
|
|
127
139
|
layout_key: "default"
|
|
@@ -138,13 +150,14 @@ const scaffoldEmailChannelStep = (refSuffix)=>{
|
|
|
138
150
|
bundleFragment
|
|
139
151
|
];
|
|
140
152
|
};
|
|
141
|
-
const scaffoldInAppFeedChannelStep = (refSuffix)=>{
|
|
153
|
+
const scaffoldInAppFeedChannelStep = (refSuffix, channels)=>{
|
|
142
154
|
const stepRef = `in_app_feed_${refSuffix}`;
|
|
143
155
|
const templateFilePath = newTemplateFilePath(stepRef, "markdown_body", "md");
|
|
156
|
+
const firstChannel = channels[0];
|
|
144
157
|
const scaffoldedStep = {
|
|
145
158
|
ref: stepRef,
|
|
146
159
|
type: _types.StepType.Channel,
|
|
147
|
-
channel_key: "<IN-APP-FEED CHANNEL KEY>",
|
|
160
|
+
channel_key: firstChannel ? firstChannel.key : "<IN-APP-FEED CHANNEL KEY>",
|
|
148
161
|
template: {
|
|
149
162
|
action_url: "{{ vars.app_url }}",
|
|
150
163
|
["markdown_body" + _constisomorphic.FILEPATH_MARKER]: templateFilePath
|
|
@@ -158,13 +171,14 @@ const scaffoldInAppFeedChannelStep = (refSuffix)=>{
|
|
|
158
171
|
bundleFragment
|
|
159
172
|
];
|
|
160
173
|
};
|
|
161
|
-
const scaffoldSmsChannelStep = (refSuffix)=>{
|
|
174
|
+
const scaffoldSmsChannelStep = (refSuffix, channels)=>{
|
|
162
175
|
const stepRef = `sms_${refSuffix}`;
|
|
163
176
|
const templateFilePath = newTemplateFilePath(stepRef, "text_body", "txt");
|
|
177
|
+
const firstChannel = channels[0];
|
|
164
178
|
const scaffoldedStep = {
|
|
165
179
|
ref: stepRef,
|
|
166
180
|
type: _types.StepType.Channel,
|
|
167
|
-
channel_key: "<SMS CHANNEL KEY>",
|
|
181
|
+
channel_key: firstChannel ? firstChannel.key : "<SMS CHANNEL KEY>",
|
|
168
182
|
template: {
|
|
169
183
|
["text_body" + _constisomorphic.FILEPATH_MARKER]: templateFilePath
|
|
170
184
|
}
|
|
@@ -177,13 +191,14 @@ const scaffoldSmsChannelStep = (refSuffix)=>{
|
|
|
177
191
|
bundleFragment
|
|
178
192
|
];
|
|
179
193
|
};
|
|
180
|
-
const scaffoldPushChannelStep = (refSuffix)=>{
|
|
194
|
+
const scaffoldPushChannelStep = (refSuffix, channels)=>{
|
|
181
195
|
const stepRef = `push_${refSuffix}`;
|
|
182
196
|
const templateFilePath = newTemplateFilePath(stepRef, "text_body", "txt");
|
|
197
|
+
const firstChannel = channels[0];
|
|
183
198
|
const scaffoldedStep = {
|
|
184
199
|
ref: stepRef,
|
|
185
200
|
type: _types.StepType.Channel,
|
|
186
|
-
channel_key: "<PUSH CHANNEL KEY>",
|
|
201
|
+
channel_key: firstChannel ? firstChannel.key : "<PUSH CHANNEL KEY>",
|
|
187
202
|
template: {
|
|
188
203
|
settings: {
|
|
189
204
|
delivery_type: "content"
|
|
@@ -199,13 +214,14 @@ const scaffoldPushChannelStep = (refSuffix)=>{
|
|
|
199
214
|
bundleFragment
|
|
200
215
|
];
|
|
201
216
|
};
|
|
202
|
-
const scaffoldChatChannelStep = (refSuffix)=>{
|
|
217
|
+
const scaffoldChatChannelStep = (refSuffix, channels)=>{
|
|
203
218
|
const stepRef = `chat_${refSuffix}`;
|
|
204
219
|
const templateFilePath = newTemplateFilePath(stepRef, "markdown_body", "md");
|
|
220
|
+
const firstChannel = channels[0];
|
|
205
221
|
const scaffoldedStep = {
|
|
206
222
|
ref: stepRef,
|
|
207
223
|
type: _types.StepType.Channel,
|
|
208
|
-
channel_key: "<CHAT CHANNEL KEY>",
|
|
224
|
+
channel_key: firstChannel ? firstChannel.key : "<CHAT CHANNEL KEY>",
|
|
209
225
|
template: {
|
|
210
226
|
["markdown_body" + _constisomorphic.FILEPATH_MARKER]: templateFilePath
|
|
211
227
|
}
|
|
@@ -223,11 +239,21 @@ const STEP_TAGS = [
|
|
|
223
239
|
"batch",
|
|
224
240
|
"fetch",
|
|
225
241
|
"email",
|
|
226
|
-
"in-app",
|
|
242
|
+
"in-app-feed",
|
|
227
243
|
"sms",
|
|
228
244
|
"push",
|
|
229
245
|
"chat"
|
|
230
246
|
];
|
|
247
|
+
const StepTagChoices = {
|
|
248
|
+
delay: "Delay step",
|
|
249
|
+
batch: "Batch step",
|
|
250
|
+
fetch: "HTTP Fetch step",
|
|
251
|
+
email: "Email channel step",
|
|
252
|
+
"in-app-feed": "In-app feed channel step",
|
|
253
|
+
sms: "SMS channel step",
|
|
254
|
+
push: "Push channel step",
|
|
255
|
+
chat: "Chat channel step"
|
|
256
|
+
};
|
|
231
257
|
const stepScaffoldFuncs = {
|
|
232
258
|
// Function steps
|
|
233
259
|
delay: scaffoldDelayStep,
|
|
@@ -235,18 +261,18 @@ const stepScaffoldFuncs = {
|
|
|
235
261
|
fetch: scaffoldHttpFetchStep,
|
|
236
262
|
// Channel steps
|
|
237
263
|
email: scaffoldEmailChannelStep,
|
|
238
|
-
"in-app": scaffoldInAppFeedChannelStep,
|
|
264
|
+
"in-app-feed": scaffoldInAppFeedChannelStep,
|
|
239
265
|
sms: scaffoldSmsChannelStep,
|
|
240
266
|
push: scaffoldPushChannelStep,
|
|
241
267
|
chat: scaffoldChatChannelStep
|
|
242
268
|
};
|
|
243
|
-
const parseStepsInput = (input)=>{
|
|
269
|
+
const parseStepsInput = (input, availableStepTypes)=>{
|
|
244
270
|
const tags = input.split(",").filter((x)=>x);
|
|
245
|
-
const invalidTags = tags.filter((tag)=>!
|
|
271
|
+
const invalidTags = tags.filter((tag)=>!availableStepTypes.includes(tag));
|
|
246
272
|
if (invalidTags.length > 0) {
|
|
247
273
|
return [
|
|
248
274
|
undefined,
|
|
249
|
-
|
|
275
|
+
`Invalid step type: ${invalidTags.join(", ")}`
|
|
250
276
|
];
|
|
251
277
|
}
|
|
252
278
|
return [
|
|
@@ -254,7 +280,26 @@ const parseStepsInput = (input)=>{
|
|
|
254
280
|
undefined
|
|
255
281
|
];
|
|
256
282
|
};
|
|
257
|
-
const
|
|
283
|
+
const getAvailableStepTypes = (channelTypes)=>{
|
|
284
|
+
// Return a list of step
|
|
285
|
+
return STEP_TAGS.filter((stepTag)=>{
|
|
286
|
+
switch(stepTag){
|
|
287
|
+
case "email":
|
|
288
|
+
return channelTypes.includes("email");
|
|
289
|
+
case "in-app-feed":
|
|
290
|
+
return channelTypes.includes("in_app_feed");
|
|
291
|
+
case "sms":
|
|
292
|
+
return channelTypes.includes("sms");
|
|
293
|
+
case "push":
|
|
294
|
+
return channelTypes.includes("push");
|
|
295
|
+
case "chat":
|
|
296
|
+
return channelTypes.includes("chat");
|
|
297
|
+
default:
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
};
|
|
302
|
+
const scaffoldWorkflowDirBundle = (attrs, channelsByType)=>{
|
|
258
303
|
// A map of 1-based counters to track the number of each step tag seen, used
|
|
259
304
|
// for formatting step refs.
|
|
260
305
|
const stepCountByTag = (0, _lodash.assign)({}, ...STEP_TAGS.map((tag)=>({
|
|
@@ -263,7 +308,17 @@ const scaffoldWorkflowDirBundle = (attrs)=>{
|
|
|
263
308
|
// Scaffold workflow steps data based on given step tags.
|
|
264
309
|
const [scaffoldedSteps = [], bundleFragments = []] = (0, _lodash.zip)(...(attrs.steps || []).map((tag)=>{
|
|
265
310
|
const stepCount = ++stepCountByTag[tag];
|
|
266
|
-
|
|
311
|
+
switch(tag){
|
|
312
|
+
case "email":
|
|
313
|
+
case "sms":
|
|
314
|
+
case "push":
|
|
315
|
+
case "chat":
|
|
316
|
+
return stepScaffoldFuncs[tag](stepCount, (0, _lodash.get)(channelsByType, tag, []));
|
|
317
|
+
case "in-app-feed":
|
|
318
|
+
return stepScaffoldFuncs[tag](stepCount, (0, _lodash.get)(channelsByType, "in_app_feed", []));
|
|
319
|
+
default:
|
|
320
|
+
return stepScaffoldFuncs[tag](stepCount);
|
|
321
|
+
}
|
|
267
322
|
}));
|
|
268
323
|
// Build a workflow directory bundle with scaffolded steps data, plus other
|
|
269
324
|
// workflow attributes.
|
|
@@ -275,7 +330,108 @@ const scaffoldWorkflowDirBundle = (attrs)=>{
|
|
|
275
330
|
[_processorisomorphic.WORKFLOW_JSON]: workflowJson
|
|
276
331
|
}, ...bundleFragments);
|
|
277
332
|
};
|
|
278
|
-
const generateWorkflowDir = async (workflowDirCtx, attrs)=>{
|
|
279
|
-
const bundle = scaffoldWorkflowDirBundle(attrs);
|
|
333
|
+
const generateWorkflowDir = async (workflowDirCtx, attrs, channelsByType)=>{
|
|
334
|
+
const bundle = scaffoldWorkflowDirBundle(attrs, channelsByType);
|
|
280
335
|
return (0, _writer.writeWorkflowDirFromBundle)(workflowDirCtx, bundle);
|
|
281
336
|
};
|
|
337
|
+
function swapChannelReferences(step, channelsByType) {
|
|
338
|
+
// Temporary until we start shipping the `channel_type` field on channel steps
|
|
339
|
+
// so that we can then branch by that value.
|
|
340
|
+
switch(step.channel_key){
|
|
341
|
+
case "postmark":
|
|
342
|
+
case "sendgrid":
|
|
343
|
+
case "resend":
|
|
344
|
+
case "email":
|
|
345
|
+
case "knock-email":
|
|
346
|
+
var _channelsByType_email_;
|
|
347
|
+
return {
|
|
348
|
+
...step,
|
|
349
|
+
channel_key: (_channelsByType_email_ = channelsByType.email[0]) === null || _channelsByType_email_ === void 0 ? void 0 : _channelsByType_email_.key
|
|
350
|
+
};
|
|
351
|
+
case "in-app-feed":
|
|
352
|
+
case "knock-in-app-feed":
|
|
353
|
+
case "knock-feed":
|
|
354
|
+
var _channelsByType_in_app_feed_;
|
|
355
|
+
return {
|
|
356
|
+
...step,
|
|
357
|
+
channel_key: (_channelsByType_in_app_feed_ = channelsByType.in_app_feed[0]) === null || _channelsByType_in_app_feed_ === void 0 ? void 0 : _channelsByType_in_app_feed_.key
|
|
358
|
+
};
|
|
359
|
+
case "sms":
|
|
360
|
+
case "knock-sms":
|
|
361
|
+
case "twilio":
|
|
362
|
+
var _channelsByType_sms_;
|
|
363
|
+
return {
|
|
364
|
+
...step,
|
|
365
|
+
channel_key: (_channelsByType_sms_ = channelsByType.sms[0]) === null || _channelsByType_sms_ === void 0 ? void 0 : _channelsByType_sms_.key
|
|
366
|
+
};
|
|
367
|
+
case "push":
|
|
368
|
+
case "apns":
|
|
369
|
+
case "fcm":
|
|
370
|
+
var _channelsByType_push_;
|
|
371
|
+
return {
|
|
372
|
+
...step,
|
|
373
|
+
channel_key: (_channelsByType_push_ = channelsByType.push[0]) === null || _channelsByType_push_ === void 0 ? void 0 : _channelsByType_push_.key
|
|
374
|
+
};
|
|
375
|
+
case "chat":
|
|
376
|
+
var _channelsByType_chat_;
|
|
377
|
+
return {
|
|
378
|
+
...step,
|
|
379
|
+
channel_key: (_channelsByType_chat_ = channelsByType.chat[0]) === null || _channelsByType_chat_ === void 0 ? void 0 : _channelsByType_chat_.key
|
|
380
|
+
};
|
|
381
|
+
default:
|
|
382
|
+
throw new Error(`Unknown channel type: ${step.channel_key}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function walkWorkflowSteps(steps, channelsByType) {
|
|
386
|
+
return steps.map((step)=>{
|
|
387
|
+
switch(step.type){
|
|
388
|
+
case _types.StepType.Channel:
|
|
389
|
+
return swapChannelReferences(step, channelsByType);
|
|
390
|
+
case _types.StepType.Branch:
|
|
391
|
+
var _step_branches;
|
|
392
|
+
return {
|
|
393
|
+
...step,
|
|
394
|
+
branches: ((_step_branches = step.branches) !== null && _step_branches !== void 0 ? _step_branches : []).map((branch)=>{
|
|
395
|
+
var _branch_steps;
|
|
396
|
+
return {
|
|
397
|
+
...branch,
|
|
398
|
+
steps: walkWorkflowSteps((_branch_steps = branch.steps) !== null && _branch_steps !== void 0 ? _branch_steps : [], channelsByType)
|
|
399
|
+
};
|
|
400
|
+
})
|
|
401
|
+
};
|
|
402
|
+
default:
|
|
403
|
+
return step;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
const generateWorkflowFromTemplate = async (workflowDirCtx, templateString, attrs, channelsByType)=>{
|
|
408
|
+
let tempDir;
|
|
409
|
+
try {
|
|
410
|
+
var _workflow_steps;
|
|
411
|
+
// Download the template directory into a temp directory
|
|
412
|
+
tempDir = await _templates.downloadTemplate(templateString);
|
|
413
|
+
// Create a workflow directory context for the temp directory
|
|
414
|
+
const tempWorkflowDirCtx = {
|
|
415
|
+
type: "workflow",
|
|
416
|
+
key: "temp",
|
|
417
|
+
abspath: tempDir,
|
|
418
|
+
exists: true
|
|
419
|
+
};
|
|
420
|
+
// Read the workflow.json from the temp directory we downloaded
|
|
421
|
+
const [workflow, errors] = await (0, _reader.readWorkflowDir)(tempWorkflowDirCtx, {
|
|
422
|
+
withExtractedFiles: true
|
|
423
|
+
});
|
|
424
|
+
if (errors.length > 0 || !workflow) {
|
|
425
|
+
throw new Error(`Invalid workflow template: ${errors.join(", ")}`);
|
|
426
|
+
}
|
|
427
|
+
// Modify the workflow data with the new attributes
|
|
428
|
+
workflow.name = attrs.name;
|
|
429
|
+
// Parse the workflow steps, and replace any channel steps with the correct `channel_key`
|
|
430
|
+
workflow.steps = walkWorkflowSteps((_workflow_steps = workflow.steps) !== null && _workflow_steps !== void 0 ? _workflow_steps : [], channelsByType);
|
|
431
|
+
// Finally, we write the workflow into the target workflow directory
|
|
432
|
+
(0, _writer.writeWorkflowDirFromData)(workflowDirCtx, workflow);
|
|
433
|
+
return;
|
|
434
|
+
} finally{
|
|
435
|
+
await _templates.cleanupTempDir(tempDir);
|
|
436
|
+
}
|
|
437
|
+
};
|
|
@@ -45,6 +45,7 @@ const _core = require("@oclif/core");
|
|
|
45
45
|
const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
|
|
46
46
|
const _lodash = require("lodash");
|
|
47
47
|
const _quicktypecore = require("quicktype-core");
|
|
48
|
+
const _projectconfig = require("../../helpers/project-config");
|
|
48
49
|
const _string = require("../../helpers/string");
|
|
49
50
|
const _typegen = require("../../helpers/typegen");
|
|
50
51
|
const _processorisomorphic = require("./processor.isomorphic");
|
|
@@ -197,7 +198,7 @@ const formatStepSummary = (step)=>{
|
|
|
197
198
|
const formatStatus = (workflow)=>{
|
|
198
199
|
return workflow.active ? "active" : "inactive";
|
|
199
200
|
};
|
|
200
|
-
const ensureValidCommandTarget = async (props, runContext)=>{
|
|
201
|
+
const ensureValidCommandTarget = async (props, runContext, projectConfig)=>{
|
|
201
202
|
const { args, flags } = props;
|
|
202
203
|
const { commandId, resourceDir: resourceDirCtx, cwd: runCwd } = runContext;
|
|
203
204
|
// If the target resource is a different type than the current resource dir
|
|
@@ -209,6 +210,9 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
209
210
|
if (flags.all && args.workflowKey) {
|
|
210
211
|
return _core.ux.error(`workflowKey arg \`${args.workflowKey}\` cannot also be provided when using --all`);
|
|
211
212
|
}
|
|
213
|
+
// Targeting all workflow dirs in the workflows index dir.
|
|
214
|
+
// Default to knock project config first if present, otherwise cwd.
|
|
215
|
+
const workflowsIndexDirCtx = await (0, _projectconfig.resolveResourceDir)(projectConfig, "workflow", runCwd);
|
|
212
216
|
// --all flag is given, which means no workflow key arg.
|
|
213
217
|
if (flags.all) {
|
|
214
218
|
// If --all flag used inside a workflow directory, then require a workflows
|
|
@@ -216,16 +220,9 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
216
220
|
if (resourceDirCtx && !flags["workflows-dir"]) {
|
|
217
221
|
return _core.ux.error("Missing required flag workflows-dir");
|
|
218
222
|
}
|
|
219
|
-
// Targeting all workflow dirs in the workflows index dir.
|
|
220
|
-
// TODO: Default to the knock project config first if present before cwd.
|
|
221
|
-
const defaultToCwd = {
|
|
222
|
-
abspath: runCwd,
|
|
223
|
-
exists: true
|
|
224
|
-
};
|
|
225
|
-
const indexDirCtx = flags["workflows-dir"] || defaultToCwd;
|
|
226
223
|
return {
|
|
227
224
|
type: "workflowsIndexDir",
|
|
228
|
-
context:
|
|
225
|
+
context: flags["workflows-dir"] || workflowsIndexDirCtx
|
|
229
226
|
};
|
|
230
227
|
}
|
|
231
228
|
// Workflow key arg is given, which means no --all flag.
|
|
@@ -233,7 +230,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
|
|
|
233
230
|
if (resourceDirCtx && resourceDirCtx.key !== args.workflowKey) {
|
|
234
231
|
return _core.ux.error(`Cannot run ${commandId} \`${args.workflowKey}\` inside another workflow directory:\n${resourceDirCtx.key}`);
|
|
235
232
|
}
|
|
236
|
-
const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(
|
|
233
|
+
const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(workflowsIndexDirCtx.abspath, args.workflowKey);
|
|
237
234
|
const workflowDirCtx = {
|
|
238
235
|
type: "workflow",
|
|
239
236
|
key: args.workflowKey,
|
|
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "load", {
|
|
|
14
14
|
});
|
|
15
15
|
const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
|
|
16
16
|
const _emaillayout = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/email-layout"));
|
|
17
|
+
const _guide = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/guide"));
|
|
17
18
|
const _messagetype = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/message-type"));
|
|
18
19
|
const _partial = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/partial"));
|
|
19
20
|
const _translation = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/translation"));
|
|
@@ -74,6 +75,10 @@ const evaluateRecursively = async (ctx, currDir)=>{
|
|
|
74
75
|
if (isWorkflowDir) {
|
|
75
76
|
ctx.resourceDir = buildResourceDirContext("workflow", currDir);
|
|
76
77
|
}
|
|
78
|
+
const isGuideDir = await _guide.isGuideDir(currDir);
|
|
79
|
+
if (isGuideDir) {
|
|
80
|
+
ctx.resourceDir = buildResourceDirContext("guide", currDir);
|
|
81
|
+
}
|
|
77
82
|
const isEmailLayoutDir = await _emaillayout.isEmailLayoutDir(currDir);
|
|
78
83
|
if (isEmailLayoutDir) {
|
|
79
84
|
ctx.resourceDir = buildResourceDirContext("email_layout", currDir);
|