@knocklabs/cli 0.1.0-rc.3 → 0.1.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.
@@ -6,15 +6,17 @@ Object.defineProperty(exports, "default", {
6
6
  enumerable: true,
7
7
  get: ()=>WorkflowPush
8
8
  });
9
- const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
10
9
  const _core = require("@oclif/core");
11
10
  const _baseCommand = /*#__PURE__*/ _interopRequireDefault(require("../../lib/base-command"));
12
11
  const _const = require("../../lib/helpers/const");
13
12
  const _error = require("../../lib/helpers/error");
13
+ const _flag = /*#__PURE__*/ _interopRequireWildcard(require("../../lib/helpers/flag"));
14
14
  const _object = require("../../lib/helpers/object");
15
15
  const _request = require("../../lib/helpers/request");
16
+ const _string = require("../../lib/helpers/string");
17
+ const _ux = require("../../lib/helpers/ux");
16
18
  const _workflow = /*#__PURE__*/ _interopRequireWildcard(require("../../lib/marshal/workflow"));
17
- const _runContext = require("../../lib/run-context");
19
+ const _validate = /*#__PURE__*/ _interopRequireDefault(require("./validate"));
18
20
  function _interopRequireDefault(obj) {
19
21
  return obj && obj.__esModule ? obj : {
20
22
  default: obj
@@ -61,55 +63,57 @@ function _interopRequireWildcard(obj, nodeInterop) {
61
63
  }
62
64
  class WorkflowPush extends _baseCommand.default {
63
65
  async run() {
64
- // 1. Retrieve the target workflow directory context.
65
- const dirContext = await this.getWorkflowDirContext();
66
- this.log(`‣ Reading \`${dirContext.key}\` at ${dirContext.abspath}`);
67
- // 2. Read the workflow.json with its template files.
68
- const [workflow, errors] = await _workflow.readWorkflowDir(dirContext, {
66
+ const { flags } = this.props;
67
+ // 1. First read all workflow directories found for the given command.
68
+ const target = await _workflow.ensureValidCommandTarget(this.props, this.runContext);
69
+ const [workflows, readErrors] = await _workflow.readAllForCommandTarget(target, {
69
70
  withExtractedFiles: true
70
71
  });
71
- if (errors.length > 0) {
72
- this.error(`Found the following errors in \`${dirContext.key}\` ${_workflow.WORKFLOW_JSON}\n\n` + (0, _error.formatErrors)(errors));
72
+ if (readErrors.length > 0) {
73
+ this.error((0, _error.formatErrors)(readErrors, {
74
+ prependBy: "\n\n"
75
+ }));
73
76
  }
74
- // 3. Push up the compiled workflow data.
75
- const resp = await (0, _request.withSpinner)(()=>{
77
+ if (workflows.length === 0) {
78
+ this.error(`No workflow directories found in ${target.context.abspath}`);
79
+ }
80
+ // 2. Then validate them all ahead of pushing them.
81
+ _ux.spinner.start(`‣ Validating`);
82
+ const apiErrors = await _validate.default.validateAll(this.apiV1, this.props, workflows);
83
+ if (apiErrors.length > 0) {
84
+ this.error((0, _error.formatErrors)(apiErrors, {
85
+ prependBy: "\n\n"
86
+ }));
87
+ }
88
+ _ux.spinner.stop();
89
+ // 3. Finally push up each workflow, abort on the first error.
90
+ _ux.spinner.start(`‣ Pushing`);
91
+ for (const workflow of workflows){
76
92
  const props = (0, _object.merge)(this.props, {
77
- args: {
78
- workflowKey: dirContext.key
79
- },
80
93
  flags: {
81
94
  annotate: true
82
95
  }
83
96
  });
84
- return this.apiV1.upsertWorkflow(props, workflow);
85
- });
86
- // 4. Update the workflow directory with the successfully pushed workflow
87
- // payload from the server.
88
- await _workflow.writeWorkflowDirFromData(dirContext, resp.data.workflow);
89
- this.log(`‣ Successfully pushed \`${dirContext.key}\`, and updated ${dirContext.abspath}`);
90
- }
91
- async getWorkflowDirContext() {
92
- const { workflowKey } = this.props.args;
93
- const { resourceDir , cwd: runCwd } = this.runContext;
94
- if (resourceDir) {
95
- const target = {
96
- commandId: _baseCommand.default.id,
97
- type: "workflow",
98
- key: workflowKey
99
- };
100
- return (0, _runContext.ensureResourceDirForTarget)(resourceDir, target);
101
- }
102
- if (workflowKey) {
103
- const dirPath = _nodePath.resolve(runCwd, workflowKey);
104
- const exists = await _workflow.isWorkflowDir(dirPath);
105
- return exists ? {
106
- type: "workflow",
107
- key: workflowKey,
108
- abspath: dirPath,
109
- exists
110
- } : this.error(`Cannot locate a workflow directory for \`${workflowKey}\``);
97
+ // eslint-disable-next-line no-await-in-loop
98
+ const resp = await this.apiV1.upsertWorkflow(props, {
99
+ ...workflow.content,
100
+ key: workflow.key
101
+ });
102
+ if ((0, _request.isSuccessResp)(resp)) {
103
+ // Update the workflow directory with the successfully pushed workflow
104
+ // payload from the server.
105
+ // eslint-disable-next-line no-await-in-loop
106
+ await _workflow.writeWorkflowDirFromData(workflow, resp.data.workflow);
107
+ continue;
108
+ }
109
+ const error = new _error.SourceError((0, _request.formatErrorRespMessage)(resp), _workflow.workflowJsonPath(workflow), "ApiError");
110
+ this.error((0, _error.formatError)(error));
111
111
  }
112
- return this.error("Missing 1 required arg:\nworkflowKey");
112
+ _ux.spinner.stop();
113
+ // 4. Display a success message.
114
+ const workflowKeys = workflows.map((w)=>w.key);
115
+ const actioned = flags.commit ? "pushed and committed" : "pushed";
116
+ this.log(`‣ Successfully ${actioned} ${workflows.length} workflow(s):\n` + (0, _string.indentString)(workflowKeys.join("\n"), 4));
113
117
  }
114
118
  }
115
119
  WorkflowPush.flags = {
@@ -120,6 +124,12 @@ WorkflowPush.flags = {
120
124
  _const.KnockEnv.Development
121
125
  ]
122
126
  }),
127
+ all: _core.Flags.boolean(),
128
+ "workflows-dir": _flag.dirPath({
129
+ dependsOn: [
130
+ "all"
131
+ ]
132
+ }),
123
133
  commit: _core.Flags.boolean({
124
134
  summary: "Push and commit the workflow(s) at the same time"
125
135
  }),
@@ -6,15 +6,15 @@ Object.defineProperty(exports, "default", {
6
6
  enumerable: true,
7
7
  get: ()=>WorkflowValidate
8
8
  });
9
- const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
10
9
  const _core = require("@oclif/core");
11
10
  const _baseCommand = /*#__PURE__*/ _interopRequireDefault(require("../../lib/base-command"));
12
11
  const _const = require("../../lib/helpers/const");
13
12
  const _error = require("../../lib/helpers/error");
14
- const _object = require("../../lib/helpers/object");
13
+ const _flag = /*#__PURE__*/ _interopRequireWildcard(require("../../lib/helpers/flag"));
15
14
  const _request = require("../../lib/helpers/request");
15
+ const _string = require("../../lib/helpers/string");
16
+ const _ux = require("../../lib/helpers/ux");
16
17
  const _workflow = /*#__PURE__*/ _interopRequireWildcard(require("../../lib/marshal/workflow"));
17
- const _runContext = require("../../lib/run-context");
18
18
  function _interopRequireDefault(obj) {
19
19
  return obj && obj.__esModule ? obj : {
20
20
  default: obj
@@ -61,51 +61,46 @@ function _interopRequireWildcard(obj, nodeInterop) {
61
61
  }
62
62
  class WorkflowValidate extends _baseCommand.default {
63
63
  async run() {
64
- // 1. Retrieve the target workflow directory context.
65
- const dirContext = await this.getWorkflowDirContext();
66
- this.log(`‣ Reading \`${dirContext.key}\` at ${dirContext.abspath}`);
67
- // 2. Read the workflow.json with its template files.
68
- const [workflow, errors] = await _workflow.readWorkflowDir(dirContext, {
64
+ // 1. Read all workflow directories found for the given command.
65
+ const target = await _workflow.ensureValidCommandTarget(this.props, this.runContext);
66
+ const [workflows, readErrors] = await _workflow.readAllForCommandTarget(target, {
69
67
  withExtractedFiles: true
70
68
  });
71
- if (errors.length > 0) {
72
- this.error(`Found the following errors in \`${dirContext.key}\` ${_workflow.WORKFLOW_JSON}\n\n` + (0, _error.formatErrors)(errors));
69
+ if (readErrors.length > 0) {
70
+ this.error((0, _error.formatErrors)(readErrors, {
71
+ prependBy: "\n\n"
72
+ }));
73
73
  }
74
- // 3. Validate the compiled workflow data.
75
- await (0, _request.withSpinner)(()=>{
76
- const props = (0, _object.merge)(this.props, {
77
- args: {
78
- workflowKey: dirContext.key
79
- }
80
- });
81
- return this.apiV1.validateWorkflow(props, workflow);
82
- }, {
83
- action: "‣ Validating"
84
- });
85
- this.log(`‣ Successfully validated \`${dirContext.key}\``);
86
- }
87
- async getWorkflowDirContext() {
88
- const { workflowKey } = this.props.args;
89
- const { resourceDir , cwd: runCwd } = this.runContext;
90
- if (resourceDir) {
91
- const target = {
92
- commandId: _baseCommand.default.id,
93
- type: "workflow",
94
- key: workflowKey
95
- };
96
- return (0, _runContext.ensureResourceDirForTarget)(resourceDir, target);
74
+ if (workflows.length === 0) {
75
+ this.error(`No workflow directories found in ${target.context.abspath}`);
97
76
  }
98
- if (workflowKey) {
99
- const dirPath = _nodePath.resolve(runCwd, workflowKey);
100
- const exists = await _workflow.isWorkflowDir(dirPath);
101
- return exists ? {
102
- type: "workflow",
103
- key: workflowKey,
104
- abspath: dirPath,
105
- exists
106
- } : this.error(`Cannot locate a workflow directory for \`${workflowKey}\``);
77
+ // 2. Validate each workflow data.
78
+ _ux.spinner.start(`‣ Validating`);
79
+ const apiErrors = await WorkflowValidate.validateAll(this.apiV1, this.props, workflows);
80
+ if (apiErrors.length > 0) {
81
+ this.error((0, _error.formatErrors)(apiErrors, {
82
+ prependBy: "\n\n"
83
+ }));
107
84
  }
108
- return this.error("Missing 1 required arg:\nworkflowKey");
85
+ _ux.spinner.stop();
86
+ // 3. Display a success message.
87
+ const workflowKeys = workflows.map((w)=>w.key);
88
+ this.log(`‣ Successfully validated ${workflows.length} workflow(s):\n` + (0, _string.indentString)(workflowKeys.join("\n"), 4));
89
+ }
90
+ static async validateAll(api, props, workflows) {
91
+ // TODO: Throw an error if a non validation error (e.g. authentication error)
92
+ // instead of printing out same error messages repeatedly.
93
+ const errorPromises = workflows.map(async (workflow)=>{
94
+ const resp = await api.validateWorkflow(props, {
95
+ ...workflow.content,
96
+ key: workflow.key
97
+ });
98
+ if ((0, _request.isSuccessResp)(resp)) return;
99
+ const error = new _error.SourceError((0, _request.formatErrorRespMessage)(resp), _workflow.workflowJsonPath(workflow), "ApiError");
100
+ return error;
101
+ });
102
+ const errors = (await Promise.all(errorPromises)).filter((e)=>Boolean(e));
103
+ return errors;
109
104
  }
110
105
  }
111
106
  WorkflowValidate.flags = {
@@ -115,6 +110,12 @@ WorkflowValidate.flags = {
115
110
  options: [
116
111
  _const.KnockEnv.Development
117
112
  ]
113
+ }),
114
+ all: _core.Flags.boolean(),
115
+ "workflows-dir": _flag.dirPath({
116
+ dependsOn: [
117
+ "all"
118
+ ]
118
119
  })
119
120
  };
120
121
  WorkflowValidate.args = [
@@ -20,6 +20,9 @@ class ApiV1 {
20
20
  async ping() {
21
21
  return this.get("/ping");
22
22
  }
23
+ async whoami() {
24
+ return this.get("/whoami");
25
+ }
23
26
  // By resources: Workflows
24
27
  async listWorkflows({ flags }) {
25
28
  const params = (0, _object.prune)({
@@ -42,7 +45,7 @@ class ApiV1 {
42
45
  params
43
46
  });
44
47
  }
45
- async upsertWorkflow({ args , flags }, workflow) {
48
+ async upsertWorkflow({ flags }, workflow) {
46
49
  const params = (0, _object.prune)({
47
50
  environment: flags.environment,
48
51
  annotate: flags.annotate,
@@ -52,18 +55,18 @@ class ApiV1 {
52
55
  const data = {
53
56
  workflow
54
57
  };
55
- return this.put(`/workflows/${args.workflowKey}`, data, {
58
+ return this.put(`/workflows/${workflow.key}`, data, {
56
59
  params
57
60
  });
58
61
  }
59
- async validateWorkflow({ args , flags }, workflow) {
62
+ async validateWorkflow({ flags }, workflow) {
60
63
  const params = (0, _object.prune)({
61
64
  environment: flags.environment
62
65
  });
63
66
  const data = {
64
67
  workflow
65
68
  };
66
- return this.put(`/workflows/${args.workflowKey}/validate`, data, {
69
+ return this.put(`/workflows/${workflow.key}/validate`, data, {
67
70
  params
68
71
  });
69
72
  }
@@ -95,16 +98,28 @@ class ApiV1 {
95
98
  });
96
99
  }
97
100
  // By resources: Translations
98
- async listTranslations({ flags }) {
101
+ async listTranslations({ flags }, filters = {}) {
99
102
  const params = (0, _object.prune)({
100
103
  environment: flags.environment,
101
104
  hide_uncommitted_changes: flags["hide-uncommitted-changes"],
105
+ locale_code: filters.localeCode,
106
+ namespace: filters.namespace,
102
107
  ...(0, _page.toPageParams)(flags)
103
108
  });
104
109
  return this.get("/translations", {
105
110
  params
106
111
  });
107
112
  }
113
+ async getTranslation({ flags }, translation) {
114
+ const params = (0, _object.prune)({
115
+ environment: flags.environment,
116
+ hide_uncommitted_changes: flags["hide-uncommitted-changes"],
117
+ namespace: translation.namespace
118
+ });
119
+ return this.get(`/translations/${translation.localeCode}`, {
120
+ params
121
+ });
122
+ }
108
123
  async upsertTranslation({ flags }, translation) {
109
124
  const params = (0, _object.prune)({
110
125
  environment: flags.environment,
@@ -9,6 +9,8 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
+ formatLanguage: ()=>formatLanguage,
13
+ formatRef: ()=>formatRef,
12
14
  isValidLocale: ()=>isValidLocale,
13
15
  isTranslationDir: ()=>isTranslationDir,
14
16
  buildTranslationFileCtx: ()=>buildTranslationFileCtx,
@@ -65,6 +67,11 @@ function _interopRequireWildcard(obj, nodeInterop) {
65
67
  }
66
68
  return newObj;
67
69
  }
70
+ const formatLanguage = (translation)=>{
71
+ const lang = _localeCodes.default.getByTag(translation.locale_code);
72
+ return lang.location ? `${lang.name}, ${lang.location}` : lang.name;
73
+ };
74
+ const formatRef = ({ locale_code , namespace })=>namespace ? `${namespace}.${locale_code}` : locale_code;
68
75
  const isValidLocale = (localeCode)=>Boolean(_localeCodes.default.getByTag(localeCode));
69
76
  const isTranslationDir = (dirPath)=>{
70
77
  const locale = _nodePath.basename(dirPath);
@@ -87,7 +94,8 @@ const parseTranslationRef = (reference)=>{
87
94
  // Locale code only (e.g. `en`)
88
95
  if (strings.length === 1) {
89
96
  return {
90
- localeCode: strings[0]
97
+ localeCode: strings[0],
98
+ namespace: undefined
91
99
  };
92
100
  }
93
101
  // Locale code prefixed with a namespace (e.g. `admin.en`)
@@ -114,7 +122,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
114
122
  // No translationRef arg, which means --all flag is used.
115
123
  if (!args.translationRef) {
116
124
  // Targeting all translation files in the current locale directory.
117
- if (resourceDirCtx) {
125
+ if (resourceDirCtx && !flags["translations-dir"]) {
118
126
  return {
119
127
  type: "translationDir",
120
128
  context: resourceDirCtx
@@ -135,7 +143,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
135
143
  // From this point on, we have translationRef so parse and validate the format.
136
144
  const parsedRef = parseTranslationRef(args.translationRef);
137
145
  if (!parsedRef) {
138
- return _core.CliUx.ux.error(`Invalid translationRef arg \`${args.translationRef}\`, use valid <locale> or <namespace>.<locale> for namespaced translations`);
146
+ return _core.CliUx.ux.error(`Invalid translation ref \`${args.translationRef}\`, use valid <locale> or <namespace>.<locale> for namespaced translations`);
139
147
  }
140
148
  const { localeCode , namespace } = parsedRef;
141
149
  // If we are in the translation dir, make sure the locale matches.
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "readTranslationFilesForCommandTarget", {
5
+ Object.defineProperty(exports, "readAllForCommandTarget", {
6
6
  enumerable: true,
7
- get: ()=>readTranslationFilesForCommandTarget
7
+ get: ()=>readAllForCommandTarget
8
8
  });
9
9
  const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
10
10
  const _core = require("@oclif/core");
@@ -56,6 +56,9 @@ function _interopRequireWildcard(obj, nodeInterop) {
56
56
  * translation file data.
57
57
  *
58
58
  * Note, it assumes they are valid file paths to translation files.
59
+ *
60
+ * TODO: Refactor to take translation file contexts instead of raw file paths,
61
+ * to keep things consistent with the workflow reader module.
59
62
  */ const readTranslationFiles = async (filePaths)=>{
60
63
  const translations = [];
61
64
  const errors = [];
@@ -69,27 +72,27 @@ function _interopRequireWildcard(obj, nodeInterop) {
69
72
  if (readJsonErrors.length > 0) {
70
73
  const e = new _error.SourceError((0, _error.formatErrors)(readJsonErrors), abspath);
71
74
  errors.push(e);
75
+ continue;
72
76
  }
73
- if (content) {
74
- translations.push({
75
- ref: translationRef,
76
- localeCode,
77
- namespace,
78
- abspath,
79
- exists: true,
80
- content: JSON.stringify(content)
81
- });
82
- }
77
+ translations.push({
78
+ ref: translationRef,
79
+ localeCode,
80
+ namespace,
81
+ abspath,
82
+ exists: true,
83
+ content: JSON.stringify(content)
84
+ });
83
85
  }
84
86
  return [
85
87
  translations,
86
88
  errors
87
89
  ];
88
90
  };
89
- const readTranslationFilesForCommandTarget = async (target)=>{
91
+ const readAllForCommandTarget = async (target)=>{
90
92
  const { type: targetType , context: targetCtx } = target;
91
93
  if (!targetCtx.exists) {
92
- return _core.CliUx.ux.error(`Cannot locate translation file(s) at \`${targetCtx.abspath}\``);
94
+ const subject = targetType === "translationFile" ? "a translation file at" : "translation files in";
95
+ return _core.CliUx.ux.error(`Cannot locate ${subject} \`${targetCtx.abspath}\``);
93
96
  }
94
97
  switch(targetType){
95
98
  case "translationFile":
@@ -1,3 +1,4 @@
1
+ // Translation payload data from the API.
1
2
  "use strict";
2
3
  Object.defineProperty(exports, "__esModule", {
3
4
  value: true
@@ -2,9 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "writeTranslationsIndexDir", {
6
- enumerable: true,
7
- get: ()=>writeTranslationsIndexDir
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
+ writeTranslationFile: ()=>writeTranslationFile,
13
+ writeTranslationFiles: ()=>writeTranslationFiles
8
14
  });
9
15
  const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
10
16
  const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
@@ -51,30 +57,36 @@ function _interopRequireWildcard(obj, nodeInterop) {
51
57
  }
52
58
  return newObj;
53
59
  }
54
- const writeTranslationsIndexDir = async (indexDirCtx, translations)=>{
60
+ const writeTranslationFile = async (translationFileCtx, translation)=>_fsExtra.outputJson(translationFileCtx.abspath, JSON.parse(translation.content), {
61
+ spaces: _json.DOUBLE_SPACES
62
+ });
63
+ const writeTranslationFiles = async (targetDirCtx, translations)=>{
55
64
  const backupDirPath = _nodePath.resolve(_const.sandboxDir, (0, _lodash.uniqueId)("backup"));
56
65
  try {
57
66
  // If the index directory already exists, back it up in the temp sandbox
58
67
  // before wiping it clean.
59
- if (indexDirCtx.exists) {
60
- await _fsExtra.copy(indexDirCtx.abspath, backupDirPath);
61
- await _fsExtra.remove(indexDirCtx.abspath);
68
+ if (targetDirCtx.exists) {
69
+ await _fsExtra.copy(targetDirCtx.abspath, backupDirPath);
70
+ await _fsExtra.emptyDir(targetDirCtx.abspath);
62
71
  }
63
72
  // Write given remote translations into the given translations directory path.
64
73
  const writeTranslationDirPromises = translations.map(async (translation)=>{
65
- const translationDirPath = _nodePath.resolve(indexDirCtx.abspath, translation.locale_code);
66
- const translationFileCtx = await (0, _helpers.buildTranslationFileCtx)(translationDirPath, translation.locale_code, translation.namespace);
67
- return _fsExtra.outputJson(translationFileCtx.abspath, JSON.parse(translation.content), {
68
- spaces: _json.DOUBLE_SPACES
69
- });
74
+ // If TranslationDirContext, then that is the locale directory we want
75
+ // to write translation files in; otherwise for translations index
76
+ // directory, we want to nest translation files under each locale dir.
77
+ const localeDirPath = "key" in targetDirCtx ? targetDirCtx.abspath : _nodePath.resolve(targetDirCtx.abspath, translation.locale_code);
78
+ const translationFileCtx = await (0, _helpers.buildTranslationFileCtx)(localeDirPath, translation.locale_code, translation.namespace);
79
+ return writeTranslationFile(translationFileCtx, translation);
70
80
  });
71
81
  await Promise.all(writeTranslationDirPromises);
72
82
  } catch (error) {
73
83
  // In case of any error, wipe the index directory that is likely in a bad
74
84
  // state then restore the backup if one existed before.
75
- await _fsExtra.remove(indexDirCtx.abspath);
76
- if (indexDirCtx.exists) {
77
- await _fsExtra.copy(backupDirPath, indexDirCtx.abspath);
85
+ if (targetDirCtx.exists) {
86
+ await _fsExtra.emptyDir(targetDirCtx.abspath);
87
+ await _fsExtra.copy(backupDirPath, targetDirCtx.abspath);
88
+ } else {
89
+ await _fsExtra.remove(targetDirCtx.abspath);
78
90
  }
79
91
  throw error;
80
92
  } finally{
@@ -11,6 +11,7 @@ function _export(target, all) {
11
11
  _export(exports, {
12
12
  WORKFLOW_JSON: ()=>WORKFLOW_JSON,
13
13
  VISUAL_BLOCKS_JSON: ()=>VISUAL_BLOCKS_JSON,
14
+ workflowJsonPath: ()=>workflowJsonPath,
14
15
  FILEPATH_MARKER: ()=>FILEPATH_MARKER,
15
16
  FILEPATH_MARKED_RE: ()=>FILEPATH_MARKED_RE,
16
17
  validateWorkflowKey: ()=>validateWorkflowKey,
@@ -18,9 +19,11 @@ _export(exports, {
18
19
  isWorkflowDir: ()=>isWorkflowDir,
19
20
  formatCategories: ()=>formatCategories,
20
21
  formatStepSummary: ()=>formatStepSummary,
21
- formatStatus: ()=>formatStatus
22
+ formatStatus: ()=>formatStatus,
23
+ ensureValidCommandTarget: ()=>ensureValidCommandTarget
22
24
  });
23
25
  const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
26
+ const _core = require("@oclif/core");
24
27
  const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
25
28
  const _lodash = require("lodash");
26
29
  const _string = require("../../helpers/string");
@@ -66,6 +69,7 @@ function _interopRequireWildcard(obj, nodeInterop) {
66
69
  }
67
70
  const WORKFLOW_JSON = "workflow.json";
68
71
  const VISUAL_BLOCKS_JSON = "visual_blocks.json";
72
+ const workflowJsonPath = (workflowDirCtx)=>_nodePath.resolve(workflowDirCtx.abspath, WORKFLOW_JSON);
69
73
  const FILEPATH_MARKER = "@";
70
74
  const FILEPATH_MARKED_RE = new RegExp(`${FILEPATH_MARKER}$`);
71
75
  const validateWorkflowKey = (input)=>{
@@ -77,7 +81,7 @@ const validateWorkflowKey = (input)=>{
77
81
  return undefined;
78
82
  };
79
83
  const lsWorkflowJson = async (dirPath)=>{
80
- const workflowJsonPath = _nodePath.join(dirPath, WORKFLOW_JSON);
84
+ const workflowJsonPath = _nodePath.resolve(dirPath, WORKFLOW_JSON);
81
85
  const exists = await _fsExtra.pathExists(workflowJsonPath);
82
86
  return exists ? workflowJsonPath : undefined;
83
87
  };
@@ -149,3 +153,61 @@ const formatStepSummary = (step)=>{
149
153
  const formatStatus = (workflow)=>{
150
154
  return workflow.active ? "active" : "inactive";
151
155
  };
156
+ const ensureValidCommandTarget = async (props, runContext)=>{
157
+ const { args , flags } = props;
158
+ const { commandId , resourceDir: resourceDirCtx , cwd: runCwd } = runContext;
159
+ // If the target resource is a different type than the current resource dir
160
+ // type, error out.
161
+ if (resourceDirCtx && resourceDirCtx.type !== "workflow") {
162
+ return _core.CliUx.ux.error(`Cannot run ${commandId} inside a ${resourceDirCtx.type} directory`);
163
+ }
164
+ // Cannot accept both workflow key arg and --all flag.
165
+ if (flags.all && args.workflowKey) {
166
+ return _core.CliUx.ux.error(`workflowKey arg \`${args.workflowKey}\` cannot also be provided when using --all`);
167
+ }
168
+ // --all flag is given, which means no workflow key arg.
169
+ if (flags.all) {
170
+ // If --all flag used inside a workflow directory, then require a workflows
171
+ // dir path.
172
+ if (resourceDirCtx && !flags["workflows-dir"]) {
173
+ return _core.CliUx.ux.error("Missing required flag workflows-dir");
174
+ }
175
+ // Targeting all workflow dirs in the workflows index dir.
176
+ // TODO: Default to the knock project config first if present before cwd.
177
+ const defaultToCwd = {
178
+ abspath: runCwd,
179
+ exists: true
180
+ };
181
+ const indexDirCtx = flags["workflows-dir"] || defaultToCwd;
182
+ return {
183
+ type: "workflowsIndexDir",
184
+ context: indexDirCtx
185
+ };
186
+ }
187
+ // Workflow key arg is given, which means no --all flag.
188
+ if (args.workflowKey) {
189
+ if (resourceDirCtx && resourceDirCtx.key !== args.workflowKey) {
190
+ return _core.CliUx.ux.error(`Cannot run ${commandId} \`${args.workflowKey}\` inside another workflow directory:\n${resourceDirCtx.key}`);
191
+ }
192
+ const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodePath.resolve(runCwd, args.workflowKey);
193
+ const workflowDirCtx = {
194
+ type: "workflow",
195
+ key: args.workflowKey,
196
+ abspath: targetDirPath,
197
+ exists: await isWorkflowDir(targetDirPath)
198
+ };
199
+ return {
200
+ type: "workflowDir",
201
+ context: workflowDirCtx
202
+ };
203
+ }
204
+ // From this point on, we have neither a workflow key arg nor --all flag.
205
+ // If running inside a workflow directory, then use that workflow directory.
206
+ if (resourceDirCtx) {
207
+ return {
208
+ type: "workflowDir",
209
+ context: resourceDirCtx
210
+ };
211
+ }
212
+ return _core.CliUx.ux.error("Missing 1 required arg:\nworkflowKey");
213
+ };