@knocklabs/cli 0.1.0-rc.0 → 0.1.0-rc.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.
@@ -17,7 +17,7 @@ function _interopRequireDefault(obj) {
17
17
  const DEFAULT_ORIGIN = "https://control.knock.app";
18
18
  const API_VERSION = "v1";
19
19
  class ApiV1 {
20
- // By resources:
20
+ // By resources: Workflows
21
21
  async ping() {
22
22
  return this.get("/ping");
23
23
  }
@@ -45,7 +45,9 @@ class ApiV1 {
45
45
  async upsertWorkflow({ args , flags }, workflow) {
46
46
  const params = (0, _object.prune)({
47
47
  environment: flags.environment,
48
- annotate: flags.annotate
48
+ annotate: flags.annotate,
49
+ commit: flags.commit,
50
+ commit_message: flags["commit-message"]
49
51
  });
50
52
  const data = {
51
53
  workflow
@@ -54,6 +56,44 @@ class ApiV1 {
54
56
  params
55
57
  });
56
58
  }
59
+ async validateWorkflow({ args , flags }, workflow) {
60
+ const params = (0, _object.prune)({
61
+ environment: flags.environment
62
+ });
63
+ const data = {
64
+ workflow
65
+ };
66
+ return this.put(`/workflows/${args.workflowKey}/validate`, data, {
67
+ params
68
+ });
69
+ }
70
+ async activateWorkflow({ args , flags }) {
71
+ const params = (0, _object.prune)({
72
+ environment: flags.environment,
73
+ status: flags.status
74
+ });
75
+ return this.put(`/workflows/${args.workflowKey}/activate`, {}, {
76
+ params
77
+ });
78
+ }
79
+ // By resources: Commits
80
+ async commitAllChanges({ flags }) {
81
+ const params = (0, _object.prune)({
82
+ environment: flags.environment,
83
+ commit_message: flags.commit_message
84
+ });
85
+ return this.put(`/commits`, {}, {
86
+ params
87
+ });
88
+ }
89
+ async promoteAllChanges({ flags }) {
90
+ const params = (0, _object.prune)({
91
+ to_environment: flags.to
92
+ });
93
+ return this.put(`/commits/promote`, {}, {
94
+ params
95
+ });
96
+ }
57
97
  // By methods:
58
98
  async get(subpath, config) {
59
99
  return this.client.get(`/${API_VERSION}` + subpath, config);
@@ -74,7 +74,7 @@ BaseCommand.globalFlags = {
74
74
  // - if not provided, fall back to env variable
75
75
  // - if not available, fall back to user config
76
76
  "service-token": _core.Flags.string({
77
- summary: "service token to authenticate with",
77
+ summary: "The service token to authenticate with",
78
78
  required: true,
79
79
  multiple: false,
80
80
  env: "KNOCK_SERVICE_TOKEN",
@@ -9,6 +9,7 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
+ KnockEnv: ()=>KnockEnv,
12
13
  isTestEnv: ()=>isTestEnv,
13
14
  sandboxDir: ()=>sandboxDir
14
15
  });
@@ -59,5 +60,10 @@ function _interopRequireWildcard(obj, nodeInterop) {
59
60
  }
60
61
  return newObj;
61
62
  }
63
+ var KnockEnv;
64
+ (function(KnockEnv) {
65
+ KnockEnv["Development"] = "development";
66
+ KnockEnv["Production"] = "production";
67
+ })(KnockEnv || (KnockEnv = {}));
62
68
  const isTestEnv = process.env.NODE_ENV === "test";
63
69
  const sandboxDir = _nodePath.resolve(_fsExtra.realpathSync(_nodeOs.default.tmpdir()), ".knock");
@@ -8,18 +8,12 @@ Object.defineProperty(exports, "ensureResourceDirForTarget", {
8
8
  });
9
9
  const _core = require("@oclif/core");
10
10
  const ensureResourceDirForTarget = (resourceDirCtx, target)=>{
11
- switch(target.commandId){
12
- case "workflow:pull":
13
- break;
14
- default:
15
- throw new Error(`Unhandled commandId: ${target.commandId}`);
16
- }
17
11
  // If the target resource is a different type than the current resource dir
18
12
  // type, error out.
19
13
  if (resourceDirCtx.type !== target.type) {
20
14
  return _core.CliUx.ux.error(`Cannot run ${target.commandId} inside a ${resourceDirCtx.type} directory`);
21
15
  }
22
- // If the resource key was not procided with the command, then infer from the
16
+ // If the resource key was not provided with the command, then infer from the
23
17
  // current resource directory context.
24
18
  if (!target.key) {
25
19
  return resourceDirCtx;
@@ -10,7 +10,9 @@ function _export(target, all) {
10
10
  }
11
11
  _export(exports, {
12
12
  ApiError: ()=>ApiError,
13
- JsonError: ()=>JsonError,
13
+ JsonSyntaxError: ()=>JsonSyntaxError,
14
+ JsonDataError: ()=>JsonDataError,
15
+ LiquidParseError: ()=>LiquidParseError,
14
16
  formatErrors: ()=>formatErrors
15
17
  });
16
18
  const _string = require("./string");
@@ -28,10 +30,18 @@ class CustomError extends Error {
28
30
  }
29
31
  class ApiError extends CustomError {
30
32
  }
31
- class JsonError extends CustomError {
32
- constructor(message, path){
33
+ class JsonSyntaxError extends CustomError {
34
+ }
35
+ class JsonDataError extends CustomError {
36
+ constructor(message, objPath){
37
+ super(message);
38
+ this.objPath = objPath;
39
+ }
40
+ }
41
+ class LiquidParseError extends CustomError {
42
+ constructor(message, context){
33
43
  super(message);
34
- this.path = path;
44
+ this.context = context;
35
45
  }
36
46
  }
37
47
  /*
@@ -42,12 +52,17 @@ class JsonError extends CustomError {
42
52
  */ const formatError = (error)=>{
43
53
  switch(true){
44
54
  case error instanceof ApiError:
45
- case error instanceof SyntaxError:
55
+ case error instanceof JsonSyntaxError:
46
56
  return `${error.name}: ${error.message}`;
47
- case error instanceof JsonError:
57
+ case error instanceof JsonDataError:
58
+ {
59
+ const e = error;
60
+ return e.objPath ? `${e.name}: data at "${e.objPath}" ${e.message}` : `${e.name}: ${e.message}`;
61
+ }
62
+ case error instanceof LiquidParseError:
48
63
  {
49
64
  const e = error;
50
- return e.path === "" ? `${e.name}: ${e.message}` : `${e.name}: data at "${e.path}" ${e.message}`;
65
+ return `${e.name}: ${e.message + "\n" + e.context}`;
51
66
  }
52
67
  default:
53
68
  throw new Error(`Unhandled error type: ${error}`);
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "booleanStr", {
6
+ enumerable: true,
7
+ get: ()=>booleanStr
8
+ });
9
+ const _core = require("@oclif/core");
10
+ const booleanStr = _core.Flags.custom({
11
+ options: [
12
+ "true",
13
+ "false"
14
+ ],
15
+ parse: async (input)=>input === "true"
16
+ });
@@ -14,6 +14,7 @@ _export(exports, {
14
14
  });
15
15
  const _jsonlint = /*#__PURE__*/ _interopRequireWildcard(require("@prantlf/jsonlint"));
16
16
  const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
17
+ const _error = require("./error");
17
18
  function _getRequireWildcardCache(nodeInterop) {
18
19
  if (typeof WeakMap !== "function") return null;
19
20
  var cacheBabelInterop = new WeakMap();
@@ -63,7 +64,7 @@ const readJson = async (filePath)=>{
63
64
  } catch (error) {
64
65
  // https://github.com/prantlf/jsonlint#error-handling
65
66
  if (!(error instanceof SyntaxError)) throw error;
66
- errors.push(error);
67
+ errors.push(new _error.JsonSyntaxError(error.message));
67
68
  }
68
69
  return [
69
70
  payload,
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "validateLiquidSyntax", {
6
+ enumerable: true,
7
+ get: ()=>validateLiquidSyntax
8
+ });
9
+ const _liquidjs = require("liquidjs");
10
+ const _error = require("./error");
11
+ const engine = new _liquidjs.Liquid({
12
+ timezoneOffset: 0
13
+ });
14
+ // Turn off a couple tags we do not support.
15
+ const disabled = {
16
+ parse (token) {
17
+ throw new Error(`"${token.name}" tag is not supported in Knock`);
18
+ }
19
+ };
20
+ engine.registerTag("render", disabled);
21
+ engine.registerTag("include", disabled);
22
+ function validateLiquidSyntax(input) {
23
+ try {
24
+ engine.parse(input);
25
+ return undefined;
26
+ } catch (error) {
27
+ // All liquidjs errors should be extended from the LiquidError class.
28
+ // Reference: https://liquidjs.com/api/classes/util_error_.liquiderror.html
29
+ if (!(error instanceof _liquidjs.LiquidError)) throw error;
30
+ // Here we only care about syntactical errors specifically.
31
+ switch(true){
32
+ case error instanceof _liquidjs.TokenizationError:
33
+ case error instanceof _liquidjs.ParseError:
34
+ return new _error.LiquidParseError(error.message, error.context);
35
+ default:
36
+ return undefined;
37
+ }
38
+ }
39
+ }
@@ -7,8 +7,47 @@ Object.defineProperty(exports, "withSpinner", {
7
7
  get: ()=>withSpinner
8
8
  });
9
9
  const _core = require("@oclif/core");
10
- const _env = require("./env");
11
10
  const _error = require("./error");
11
+ const _spinner = /*#__PURE__*/ _interopRequireWildcard(require("./spinner"));
12
+ function _getRequireWildcardCache(nodeInterop) {
13
+ if (typeof WeakMap !== "function") return null;
14
+ var cacheBabelInterop = new WeakMap();
15
+ var cacheNodeInterop = new WeakMap();
16
+ return (_getRequireWildcardCache = function(nodeInterop) {
17
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
18
+ })(nodeInterop);
19
+ }
20
+ function _interopRequireWildcard(obj, nodeInterop) {
21
+ if (!nodeInterop && obj && obj.__esModule) {
22
+ return obj;
23
+ }
24
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
25
+ return {
26
+ default: obj
27
+ };
28
+ }
29
+ var cache = _getRequireWildcardCache(nodeInterop);
30
+ if (cache && cache.has(obj)) {
31
+ return cache.get(obj);
32
+ }
33
+ var newObj = {};
34
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
35
+ for(var key in obj){
36
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
37
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
38
+ if (desc && (desc.get || desc.set)) {
39
+ Object.defineProperty(newObj, key, desc);
40
+ } else {
41
+ newObj[key] = obj[key];
42
+ }
43
+ }
44
+ }
45
+ newObj.default = obj;
46
+ if (cache) {
47
+ cache.set(obj, newObj);
48
+ }
49
+ return newObj;
50
+ }
12
51
  const isSuccessResp = (resp)=>resp.status >= 200 && resp.status < 300;
13
52
  /*
14
53
  * Returns a formatted error message from an error response based on status code.
@@ -18,7 +57,7 @@ const isSuccessResp = (resp)=>resp.status >= 200 && resp.status < 300;
18
57
  }
19
58
  const { message , errors =[] } = data;
20
59
  if (status >= 400) {
21
- const errs = errors.map((e)=>new _error.JsonError(e.message, e.field));
60
+ const errs = errors.map((e)=>new _error.JsonDataError(e.message, e.field));
22
61
  return errs.length === 0 ? message : message + "\n\n" + (0, _error.formatErrors)(errs);
23
62
  }
24
63
  return message;
@@ -26,13 +65,13 @@ const isSuccessResp = (resp)=>resp.status >= 200 && resp.status < 300;
26
65
  const withSpinner = async (requestFn, opts = {})=>{
27
66
  const { action ="‣ Loading" , ensureSuccess =true } = opts;
28
67
  // Suppress printing the spinner in tests, oclif doesn't for some reasons.
29
- if (!_env.isTestEnv) _core.CliUx.ux.action.start(action);
68
+ _spinner.start(action);
30
69
  const resp = await requestFn();
31
70
  // Error out before the action stop so the spinner can update accordingly.
32
71
  if (ensureSuccess && !isSuccessResp(resp)) {
33
72
  const message = formatErrorRespMessage(resp);
34
73
  _core.CliUx.ux.error(new _error.ApiError(message));
35
74
  }
36
- _core.CliUx.ux.action.stop();
75
+ _spinner.stop();
37
76
  return resp;
38
77
  };
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ start: ()=>start,
13
+ stop: ()=>stop
14
+ });
15
+ const _core = require("@oclif/core");
16
+ const _const = require("./const");
17
+ const start = (action)=>{
18
+ if (!_const.isTestEnv) _core.CliUx.ux.action.start(action);
19
+ };
20
+ const stop = ()=>_core.CliUx.ux.action.stop();
@@ -1,17 +1,25 @@
1
1
  /*
2
- * Indent each line in a string, useful when printing out nested errors.
3
- *
4
- * NOTE: Copied over from https://github.com/sindresorhus/indent-string for now,
5
- * because getting an "[ERR_REQUIRE_ESM]: Must use import to load ES Module"
6
- * error when pulling in the package.
2
+ * Checks if a given string is in a slugified format.
7
3
  */ "use strict";
8
4
  Object.defineProperty(exports, "__esModule", {
9
5
  value: true
10
6
  });
11
- Object.defineProperty(exports, "indentString", {
12
- enumerable: true,
13
- get: ()=>indentString
7
+ function _export(target, all) {
8
+ for(var name in all)Object.defineProperty(target, name, {
9
+ enumerable: true,
10
+ get: all[name]
11
+ });
12
+ }
13
+ _export(exports, {
14
+ checkSlugifiedFormat: ()=>checkSlugifiedFormat,
15
+ indentString: ()=>indentString
14
16
  });
17
+ const SLUG_FORMAT_RE = /^[\w-]+$/;
18
+ const SLUG_LOWERCASE_FORMAT_RE = /^[\d_a-z-]+$/;
19
+ const checkSlugifiedFormat = (input, opts = {})=>{
20
+ const { onlyLowerCase =true } = opts;
21
+ return onlyLowerCase ? SLUG_LOWERCASE_FORMAT_RE.test(input) : SLUG_FORMAT_RE.test(input);
22
+ };
15
23
  const indentString = (string, count = 0, options = {})=>{
16
24
  const { indent =" " , includeEmptyLines =false } = options;
17
25
  if (typeof string !== "string") {
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ parseStepsInput: ()=>parseStepsInput,
13
+ generateWorkflowDir: ()=>generateWorkflowDir,
14
+ scaffoldWorkflowDirBundle: ()=>scaffoldWorkflowDirBundle
15
+ });
16
+ const _lodash = require("lodash");
17
+ const _helpers = require("./helpers");
18
+ const _types = require("./types");
19
+ const _writer = require("./writer");
20
+ const scaffoldDelayStep = (refSuffix)=>{
21
+ const scaffoldedStep = {
22
+ ref: `${_types.StepType.Delay}_${refSuffix}`,
23
+ type: _types.StepType.Delay,
24
+ settings: {
25
+ delay_for: {
26
+ unit: "seconds",
27
+ value: 30
28
+ }
29
+ }
30
+ };
31
+ return [
32
+ scaffoldedStep,
33
+ {}
34
+ ];
35
+ };
36
+ const scaffoldBatchStep = (refSuffix)=>{
37
+ const scaffoldedStep = {
38
+ ref: `${_types.StepType.Batch}_${refSuffix}`,
39
+ type: _types.StepType.Batch,
40
+ settings: {
41
+ batch_order: "asc",
42
+ batch_window: {
43
+ unit: "seconds",
44
+ value: 30
45
+ }
46
+ }
47
+ };
48
+ return [
49
+ scaffoldedStep,
50
+ {}
51
+ ];
52
+ };
53
+ const scaffoldHttpFetchStep = (refSuffix)=>{
54
+ const scaffoldedStep = {
55
+ ref: `${_types.StepType.HttpFetch}_${refSuffix}`,
56
+ type: _types.StepType.HttpFetch,
57
+ settings: {
58
+ method: "get",
59
+ url: "https://example.com"
60
+ }
61
+ };
62
+ return [
63
+ scaffoldedStep,
64
+ {}
65
+ ];
66
+ };
67
+ const scaffoldEmailChannelStep = (refSuffix)=>{
68
+ const stepRef = `email_${refSuffix}`;
69
+ const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "html_body", "html");
70
+ const scaffoldedStep = {
71
+ ref: stepRef,
72
+ type: _types.StepType.Channel,
73
+ channel_key: "<EMAIL CHANNEL KEY>",
74
+ template: {
75
+ settings: {
76
+ layout_key: "default"
77
+ },
78
+ subject: "You've got mail!",
79
+ ["html_body" + _helpers.FILEPATH_MARKER]: templateFilePath
80
+ }
81
+ };
82
+ const bundleFragment = {
83
+ [templateFilePath]: "<p>Hello, <strong>{{ recipient.name }}</strong>!</p>"
84
+ };
85
+ return [
86
+ scaffoldedStep,
87
+ bundleFragment
88
+ ];
89
+ };
90
+ const scaffoldInAppFeedChannelStep = (refSuffix)=>{
91
+ const stepRef = `in_app_feed_${refSuffix}`;
92
+ const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "markdown_body", "md");
93
+ const scaffoldedStep = {
94
+ ref: stepRef,
95
+ type: _types.StepType.Channel,
96
+ channel_key: "<IN-APP-FEED CHANNEL KEY>",
97
+ template: {
98
+ action_url: "{{ vars.app_url }}",
99
+ ["markdown_body" + _helpers.FILEPATH_MARKER]: templateFilePath
100
+ }
101
+ };
102
+ const bundleFragment = {
103
+ [templateFilePath]: "Hello, **{{ recipient.name }}**!"
104
+ };
105
+ return [
106
+ scaffoldedStep,
107
+ bundleFragment
108
+ ];
109
+ };
110
+ const scaffoldSmsChannelStep = (refSuffix)=>{
111
+ const stepRef = `sms_${refSuffix}`;
112
+ const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "text_body", "txt");
113
+ const scaffoldedStep = {
114
+ ref: stepRef,
115
+ type: _types.StepType.Channel,
116
+ channel_key: "<SMS CHANNEL KEY>",
117
+ template: {
118
+ ["text_body" + _helpers.FILEPATH_MARKER]: templateFilePath
119
+ }
120
+ };
121
+ const bundleFragment = {
122
+ [templateFilePath]: "Hello, {{ recipient.name }}!"
123
+ };
124
+ return [
125
+ scaffoldedStep,
126
+ bundleFragment
127
+ ];
128
+ };
129
+ const scaffoldPushChannelStep = (refSuffix)=>{
130
+ const stepRef = `push_${refSuffix}`;
131
+ const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "text_body", "txt");
132
+ const scaffoldedStep = {
133
+ ref: stepRef,
134
+ type: _types.StepType.Channel,
135
+ channel_key: "<PUSH CHANNEL KEY>",
136
+ template: {
137
+ settings: {
138
+ delivery_type: "content"
139
+ },
140
+ ["text_body" + _helpers.FILEPATH_MARKER]: templateFilePath
141
+ }
142
+ };
143
+ const bundleFragment = {
144
+ [templateFilePath]: "Hello, {{ recipient.name }}!"
145
+ };
146
+ return [
147
+ scaffoldedStep,
148
+ bundleFragment
149
+ ];
150
+ };
151
+ const scaffoldChatChannelStep = (refSuffix)=>{
152
+ const stepRef = `chat_${refSuffix}`;
153
+ const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "markdown_body", "md");
154
+ const scaffoldedStep = {
155
+ ref: stepRef,
156
+ type: _types.StepType.Channel,
157
+ channel_key: "<CHAT CHANNEL KEY>",
158
+ template: {
159
+ ["markdown_body" + _helpers.FILEPATH_MARKER]: templateFilePath
160
+ }
161
+ };
162
+ const bundleFragment = {
163
+ [templateFilePath]: "Hello, **{{ recipient.name }}**!"
164
+ };
165
+ return [
166
+ scaffoldedStep,
167
+ bundleFragment
168
+ ];
169
+ };
170
+ const STEP_TAGS = [
171
+ "delay",
172
+ "batch",
173
+ "fetch",
174
+ "email",
175
+ "in-app",
176
+ "sms",
177
+ "push",
178
+ "chat"
179
+ ];
180
+ const stepScaffoldFuncs = {
181
+ // Function steps
182
+ delay: scaffoldDelayStep,
183
+ batch: scaffoldBatchStep,
184
+ fetch: scaffoldHttpFetchStep,
185
+ // Channel steps
186
+ email: scaffoldEmailChannelStep,
187
+ "in-app": scaffoldInAppFeedChannelStep,
188
+ sms: scaffoldSmsChannelStep,
189
+ push: scaffoldPushChannelStep,
190
+ chat: scaffoldChatChannelStep
191
+ };
192
+ const parseStepsInput = (input)=>{
193
+ const tags = input.split(",").filter((x)=>x);
194
+ const invalidTags = tags.filter((tag)=>!(0, _lodash.get)(stepScaffoldFuncs, tag));
195
+ if (invalidTags.length > 0) {
196
+ return [
197
+ undefined,
198
+ "must be a comma-separated string of the following values: " + STEP_TAGS.join(", ")
199
+ ];
200
+ }
201
+ return [
202
+ tags,
203
+ undefined
204
+ ];
205
+ };
206
+ const scaffoldWorkflowDirBundle = (attrs)=>{
207
+ // A map of 1-based counters to track the number of each step tag seen, used
208
+ // for formatting step refs.
209
+ const stepCountByTag = (0, _lodash.assign)({}, ...STEP_TAGS.map((tag)=>({
210
+ [tag]: 0
211
+ })));
212
+ // Scaffold workflow steps data based on given step tags.
213
+ const [scaffoldedSteps = [], bundleFragments = []] = (0, _lodash.zip)(...(attrs.steps || []).map((tag)=>{
214
+ const stepCount = ++stepCountByTag[tag];
215
+ return stepScaffoldFuncs[tag](stepCount);
216
+ }));
217
+ // Build a workflow directory bundle with scaffolded steps data, plus other
218
+ // workflow attributes.
219
+ const workflowJson = {
220
+ ...attrs,
221
+ steps: scaffoldedSteps
222
+ };
223
+ return (0, _lodash.assign)({
224
+ [_helpers.WORKFLOW_JSON]: workflowJson
225
+ }, ...bundleFragments);
226
+ };
227
+ const generateWorkflowDir = async (workflowDirCtx, attrs)=>{
228
+ const bundle = scaffoldWorkflowDirBundle(attrs);
229
+ return (0, _writer.writeWorkflowDirFromBundle)(workflowDirCtx, bundle);
230
+ };
@@ -12,6 +12,7 @@ _export(exports, {
12
12
  WORKFLOW_JSON: ()=>WORKFLOW_JSON,
13
13
  FILEPATH_MARKER: ()=>FILEPATH_MARKER,
14
14
  FILEPATH_MARKED_RE: ()=>FILEPATH_MARKED_RE,
15
+ validateWorkflowKey: ()=>validateWorkflowKey,
15
16
  lsWorkflowJson: ()=>lsWorkflowJson,
16
17
  isWorkflowDir: ()=>isWorkflowDir,
17
18
  formatCategories: ()=>formatCategories,
@@ -21,6 +22,7 @@ _export(exports, {
21
22
  const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
22
23
  const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
23
24
  const _lodash = require("lodash");
25
+ const _string = require("../../helpers/string");
24
26
  const _types = require("./types");
25
27
  function _getRequireWildcardCache(nodeInterop) {
26
28
  if (typeof WeakMap !== "function") return null;
@@ -64,6 +66,14 @@ function _interopRequireWildcard(obj, nodeInterop) {
64
66
  const WORKFLOW_JSON = "workflow.json";
65
67
  const FILEPATH_MARKER = "@";
66
68
  const FILEPATH_MARKED_RE = new RegExp(`${FILEPATH_MARKER}$`);
69
+ const validateWorkflowKey = (input)=>{
70
+ if (!(0, _string.checkSlugifiedFormat)(input, {
71
+ onlyLowerCase: true
72
+ })) {
73
+ return "must include only lowercase alphanumeric, dash, or underscore characters";
74
+ }
75
+ return undefined;
76
+ };
67
77
  const lsWorkflowJson = async (dirPath)=>{
68
78
  const workflowJsonPath = _nodePath.join(dirPath, WORKFLOW_JSON);
69
79
  const exists = await _fsExtra.pathExists(workflowJsonPath);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
+ _exportStar(require("./generator"), exports);
5
6
  _exportStar(require("./helpers"), exports);
6
7
  _exportStar(require("./reader"), exports);
7
8
  _exportStar(require("./types"), exports);