@knocklabs/cli 0.3.0 → 1.0.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.
Files changed (68) hide show
  1. package/README.md +505 -162
  2. package/dist/commands/branch/create.js +0 -2
  3. package/dist/commands/branch/delete.js +4 -3
  4. package/dist/commands/branch/exit.js +0 -2
  5. package/dist/commands/branch/list.js +0 -2
  6. package/dist/commands/branch/merge.js +82 -0
  7. package/dist/commands/branch/switch.js +0 -2
  8. package/dist/commands/channel/list.js +73 -0
  9. package/dist/commands/environment/list.js +73 -0
  10. package/dist/commands/guide/new.js +276 -0
  11. package/dist/commands/guide/pull.js +5 -6
  12. package/dist/commands/guide/push.js +1 -1
  13. package/dist/commands/guide/validate.js +1 -1
  14. package/dist/commands/init.js +108 -0
  15. package/dist/commands/layout/new.js +228 -0
  16. package/dist/commands/layout/pull.js +5 -6
  17. package/dist/commands/layout/push.js +1 -1
  18. package/dist/commands/layout/validate.js +1 -1
  19. package/dist/commands/message-type/new.js +228 -0
  20. package/dist/commands/message-type/pull.js +5 -6
  21. package/dist/commands/message-type/push.js +1 -1
  22. package/dist/commands/message-type/validate.js +1 -1
  23. package/dist/commands/partial/new.js +274 -0
  24. package/dist/commands/partial/pull.js +5 -6
  25. package/dist/commands/partial/push.js +1 -1
  26. package/dist/commands/partial/validate.js +1 -1
  27. package/dist/commands/pull.js +7 -2
  28. package/dist/commands/push.js +6 -4
  29. package/dist/commands/translation/pull.js +1 -1
  30. package/dist/commands/translation/push.js +1 -1
  31. package/dist/commands/translation/validate.js +1 -1
  32. package/dist/commands/workflow/new.js +179 -54
  33. package/dist/commands/workflow/pull.js +6 -8
  34. package/dist/commands/workflow/push.js +1 -1
  35. package/dist/commands/workflow/validate.js +1 -1
  36. package/dist/lib/api-v1.js +23 -2
  37. package/dist/lib/auth.js +1 -1
  38. package/dist/lib/base-command.js +18 -15
  39. package/dist/lib/helpers/flag.js +0 -2
  40. package/dist/lib/helpers/project-config.js +158 -0
  41. package/dist/lib/helpers/request.js +1 -2
  42. package/dist/lib/helpers/string.js +4 -4
  43. package/dist/lib/helpers/typegen.js +1 -1
  44. package/dist/lib/marshal/email-layout/generator.js +152 -0
  45. package/dist/lib/marshal/email-layout/helpers.js +6 -9
  46. package/dist/lib/marshal/email-layout/index.js +1 -0
  47. package/dist/lib/marshal/email-layout/writer.js +15 -3
  48. package/dist/lib/marshal/guide/generator.js +163 -0
  49. package/dist/lib/marshal/guide/helpers.js +6 -10
  50. package/dist/lib/marshal/guide/index.js +1 -0
  51. package/dist/lib/marshal/guide/writer.js +5 -9
  52. package/dist/lib/marshal/message-type/generator.js +139 -0
  53. package/dist/lib/marshal/message-type/helpers.js +6 -10
  54. package/dist/lib/marshal/message-type/index.js +1 -0
  55. package/dist/lib/marshal/message-type/writer.js +5 -1
  56. package/dist/lib/marshal/partial/generator.js +159 -0
  57. package/dist/lib/marshal/partial/helpers.js +6 -10
  58. package/dist/lib/marshal/partial/index.js +1 -0
  59. package/dist/lib/marshal/partial/writer.js +3 -0
  60. package/dist/lib/marshal/translation/helpers.js +6 -10
  61. package/dist/lib/marshal/translation/processor.isomorphic.js +4 -4
  62. package/dist/lib/marshal/translation/writer.js +2 -2
  63. package/dist/lib/marshal/workflow/generator.js +175 -19
  64. package/dist/lib/marshal/workflow/helpers.js +7 -10
  65. package/dist/lib/run-context/loader.js +5 -0
  66. package/dist/lib/templates.js +131 -0
  67. package/oclif.manifest.json +826 -266
  68. package/package.json +14 -9
@@ -10,11 +10,17 @@ Object.defineProperty(exports, "default", {
10
10
  });
11
11
  const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
12
12
  const _core = require("@oclif/core");
13
- const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
13
+ const _enquirer = require("enquirer");
14
14
  const _basecommand = /*#__PURE__*/ _interop_require_default(require("../../lib/base-command"));
15
15
  const _const = require("../../lib/helpers/const");
16
+ const _flag = /*#__PURE__*/ _interop_require_wildcard(require("../../lib/helpers/flag"));
17
+ const _projectconfig = require("../../lib/helpers/project-config");
18
+ const _string = require("../../lib/helpers/string");
16
19
  const _ux = require("../../lib/helpers/ux");
17
20
  const _workflow = /*#__PURE__*/ _interop_require_wildcard(require("../../lib/marshal/workflow"));
21
+ const _generator = require("../../lib/marshal/workflow/generator");
22
+ const _runcontext = require("../../lib/run-context");
23
+ const _push = /*#__PURE__*/ _interop_require_default(require("./push"));
18
24
  function _define_property(obj, key, value) {
19
25
  if (key in obj) {
20
26
  Object.defineProperty(obj, key, {
@@ -76,75 +82,194 @@ function _interop_require_wildcard(obj, nodeInterop) {
76
82
  }
77
83
  class WorkflowNew extends _basecommand.default {
78
84
  async run() {
79
- const { args, flags } = this.props;
80
- const { cwd, resourceDir } = this.runContext;
81
- _ux.spinner.start("‣ Validating");
85
+ const { flags } = this.props;
86
+ const { resourceDir } = this.runContext;
82
87
  // 1. Ensure we aren't in any existing resource directory already.
83
- // TODO: In the future, maybe check for the project context and if we are in
84
- // /workflows directory.
85
88
  if (resourceDir) {
86
- return this.error(`Cannot generate inside an existing ${resourceDir.type} directory`);
89
+ return this.error(`Cannot create a new workflow inside an existing ${resourceDir.type} directory`);
87
90
  }
88
- // 2. Ensure the workflow key is in the valid format.
89
- const workflowKeyError = _workflow.validateWorkflowKey(args.workflowKey);
91
+ // 2. Prompt for name and key if not provided
92
+ let name = flags.name;
93
+ let key = flags.key;
94
+ if (!name) {
95
+ const nameResponse = await (0, _enquirer.prompt)({
96
+ type: "input",
97
+ name: "name",
98
+ message: "Workflow name",
99
+ validate: (value)=>{
100
+ if (!value || value.trim().length === 0) {
101
+ return "Workflow name is required";
102
+ }
103
+ return true;
104
+ }
105
+ });
106
+ name = nameResponse.name;
107
+ }
108
+ if (!key) {
109
+ const keyResponse = await (0, _enquirer.prompt)({
110
+ type: "input",
111
+ name: "key",
112
+ message: "Workflow key (immutable slug)",
113
+ initial: (0, _string.slugify)(name),
114
+ validate: (value)=>{
115
+ if (!value || value.trim().length === 0) {
116
+ return "Workflow key is required";
117
+ }
118
+ const keyError = _workflow.validateWorkflowKey(value);
119
+ if (keyError) {
120
+ return `Invalid workflow key: ${keyError}`;
121
+ }
122
+ return true;
123
+ }
124
+ });
125
+ key = keyResponse.key;
126
+ }
127
+ // Validate the workflow key
128
+ const workflowKeyError = _workflow.validateWorkflowKey(key);
90
129
  if (workflowKeyError) {
91
- return this.error(`Invalid workflow key \`${args.workflowKey}\` (${workflowKeyError})`);
130
+ return this.error(`Invalid workflow key \`${key}\` (${workflowKeyError})`);
92
131
  }
93
- // 3. Parse and validate the steps flag, if given.
94
- const [steps, stepsError] = _workflow.parseStepsInput(flags.steps || "");
95
- if (stepsError) {
96
- return this.error(`Invalid --steps \`${flags.steps}\` (${stepsError})`);
132
+ const workflowDirCtx = await this.getWorkflowDirContext(key);
133
+ const promptMessage = workflowDirCtx.exists ? `Found \`${workflowDirCtx.key}\` at ${workflowDirCtx.abspath}, overwrite?` : `Create a new workflow directory \`${workflowDirCtx.key}\` at ${workflowDirCtx.abspath}?`;
134
+ // Check if the workflow directory already exists, and prompt to confirm if not.
135
+ const input = flags.force || await (0, _ux.promptToConfirm)(promptMessage);
136
+ if (!input) return;
137
+ // Generate the workflow either from a template or from scratch
138
+ await (flags.template ? this.fromTemplate(workflowDirCtx, name, flags.template) : this.fromSteps(workflowDirCtx, name, flags.steps));
139
+ if (flags.push) {
140
+ _ux.spinner.start("‣ Pushing workflow to Knock");
141
+ try {
142
+ await _push.default.run([
143
+ key
144
+ ]);
145
+ } catch (error) {
146
+ this.error(`Failed to push workflow to Knock: ${error}`);
147
+ } finally{
148
+ _ux.spinner.stop();
149
+ }
97
150
  }
98
- // 4. Ensure not to overwrite any existing path accidentally.
99
- const newWorkflowDirPath = _nodepath.resolve(cwd, args.workflowKey);
100
- const pathExists = await _fsextra.pathExists(newWorkflowDirPath);
101
- if (pathExists && !flags.force) {
102
- return this.error(`Cannot overwrite an existing path at ${newWorkflowDirPath}` + " (use --force to overwrite)");
151
+ this.log(`‣ Successfully created workflow \`${key}\``);
152
+ }
153
+ async fromTemplate(workflowDirCtx, name, templateString) {
154
+ // When being called from the template string, we want to try and generate
155
+ // the workflow from the provided template.
156
+ const channelsByType = await this.listAllChannelsByType();
157
+ _ux.spinner.start(`‣ Generating workflow from template \`${templateString}\``);
158
+ try {
159
+ await _workflow.generateWorkflowFromTemplate(workflowDirCtx, templateString, {
160
+ name
161
+ }, channelsByType);
162
+ } catch (error) {
163
+ this.error(`Failed to generate workflow from template: ${error}`);
164
+ } finally{
165
+ _ux.spinner.stop();
103
166
  }
104
- _ux.spinner.stop();
105
- // 5-A. We are good to generate a new workflow directory.
106
- const dirContext = {
107
- type: "workflow",
108
- key: args.workflowKey,
109
- abspath: newWorkflowDirPath,
110
- exists: await _workflow.isWorkflowDir(newWorkflowDirPath)
111
- };
112
- const attrs = {
113
- name: args.workflowKey,
167
+ }
168
+ async fromSteps(workflowDirCtx, name, stepStr) {
169
+ const channelsByType = await this.listAllChannelsByType();
170
+ const channelTypes = Object.keys(channelsByType);
171
+ const availableStepTypes = _workflow.getAvailableStepTypes(channelTypes);
172
+ let steps = [];
173
+ // Stuff with steps
174
+ if (stepStr) {
175
+ // Parse steps from flag
176
+ const [parsedSteps, stepsError] = (0, _generator.parseStepsInput)(stepStr, availableStepTypes);
177
+ if (stepsError) {
178
+ return this.error(`Invalid --steps \`${stepStr}\` (${stepsError})`);
179
+ }
180
+ steps = parsedSteps || [];
181
+ } else {
182
+ // Prompt for steps with multiselect
183
+ const stepChoices = availableStepTypes.map((stepType)=>({
184
+ name: stepType,
185
+ message: _generator.StepTagChoices[stepType]
186
+ }));
187
+ const stepsResponse = await (0, _enquirer.prompt)({
188
+ type: "multiselect",
189
+ name: "steps",
190
+ message: "(optional) Select step types to bootstrap the workflow with",
191
+ choices: stepChoices
192
+ });
193
+ steps = (stepsResponse === null || stepsResponse === void 0 ? void 0 : stepsResponse.steps) || [];
194
+ }
195
+ // 6. Generate the workflow directory with scaffolded steps
196
+ await _workflow.generateWorkflowDir(workflowDirCtx, {
197
+ name,
114
198
  steps
115
- };
116
- await _workflow.generateWorkflowDir(dirContext, attrs);
117
- this.log(`‣ Successfully generated a workflow directory at ${dirContext.abspath}`);
118
- // 5-B. Lastly warn if this workflow already exists in Knock.
119
- const isExistingWorkflow = await this.checkExistingWorkflow();
120
- if (isExistingWorkflow) {
121
- this.log("");
122
- this.warn(`Workflow \`${args.workflowKey}\` already exists in \`${flags.environment}\` environment`);
199
+ }, channelsByType);
200
+ }
201
+ async getWorkflowDirContext(workflowKey) {
202
+ const { resourceDir, cwd: runCwd } = this.runContext;
203
+ // Inside an existing resource dir, use it if valid for the target workflow.
204
+ if (resourceDir) {
205
+ const target = {
206
+ commandId: _basecommand.default.id,
207
+ type: "workflow",
208
+ key: workflowKey
209
+ };
210
+ return (0, _runcontext.ensureResourceDirForTarget)(resourceDir, target);
211
+ }
212
+ // Default to knock project config first if present, otherwise cwd.
213
+ const dirCtx = await (0, _projectconfig.resolveResourceDir)(this.projectConfig, "workflow", runCwd);
214
+ // Not inside any existing workflow directory, which means either create a
215
+ // new worfklow directory in the cwd, or update it if there is one already.
216
+ if (workflowKey) {
217
+ const dirPath = _nodepath.resolve(dirCtx.abspath, workflowKey);
218
+ const exists = await _workflow.isWorkflowDir(dirPath);
219
+ return {
220
+ type: "workflow",
221
+ key: workflowKey,
222
+ abspath: dirPath,
223
+ exists
224
+ };
123
225
  }
226
+ // Not in any workflow directory, nor a workflow key arg was given so error.
227
+ return this.error("Missing 1 required arg:\nworkflowKey");
124
228
  }
125
- async checkExistingWorkflow() {
126
- try {
127
- const resp = await this.apiV1.getWorkflow(this.props);
128
- return resp.status === 200;
129
- } catch {}
229
+ async listAllChannelsByType() {
230
+ const channels = await this.apiV1.listAllChannels();
231
+ // Group channels by type
232
+ // eslint-disable-next-line unicorn/no-array-reduce
233
+ const channelsByType = channels.reduce((acc, channel)=>({
234
+ ...acc,
235
+ [channel.type]: [
236
+ ...acc[channel.type] || [],
237
+ channel
238
+ ]
239
+ }), {});
240
+ return channelsByType;
130
241
  }
131
242
  }
243
+ _define_property(WorkflowNew, "summary", "Create a new workflow with a minimal configuration.");
132
244
  _define_property(WorkflowNew, "flags", {
245
+ name: _core.Flags.string({
246
+ summary: "The name of the workflow",
247
+ char: "n"
248
+ }),
249
+ key: _core.Flags.string({
250
+ summary: "The key of the workflow",
251
+ char: "k"
252
+ }),
133
253
  steps: _core.Flags.string({
134
- aliases: [
135
- "step"
136
- ]
254
+ summary: "Comma-separated list of step types to include in the workflow",
255
+ char: "s"
137
256
  }),
138
- force: _core.Flags.boolean(),
139
257
  environment: _core.Flags.string({
140
- hidden: true,
258
+ summary: "The environment to create the workflow in. Defaults to development.",
141
259
  default: _const.KnockEnv.Development
260
+ }),
261
+ branch: _flag.branch,
262
+ force: _core.Flags.boolean({
263
+ summary: "Force the creation of the workflow directory without confirmation."
264
+ }),
265
+ push: _core.Flags.boolean({
266
+ summary: "Whether or not to push the workflow to Knock after creation.",
267
+ default: false,
268
+ char: "p"
269
+ }),
270
+ template: _core.Flags.string({
271
+ summary: "The template repository to use for the workflow. Should be `workflows/{type}`. You cannot use this flag with --steps.",
272
+ char: "t"
142
273
  })
143
274
  });
144
- _define_property(WorkflowNew, "args", {
145
- workflowKey: _core.Args.string({
146
- required: true
147
- })
148
- });
149
- // TODO(KNO-3072): Unhide after we move the generator logic to the backend.
150
- _define_property(WorkflowNew, "hidden", true);
275
+ _define_property(WorkflowNew, "args", {});
@@ -16,6 +16,7 @@ const _error = require("../../lib/helpers/error");
16
16
  const _flag = /*#__PURE__*/ _interop_require_wildcard(require("../../lib/helpers/flag"));
17
17
  const _objectisomorphic = require("../../lib/helpers/object.isomorphic");
18
18
  const _page = require("../../lib/helpers/page");
19
+ const _projectconfig = require("../../lib/helpers/project-config");
19
20
  const _request = require("../../lib/helpers/request");
20
21
  const _ux = require("../../lib/helpers/ux");
21
22
  const _workflow = /*#__PURE__*/ _interop_require_wildcard(require("../../lib/marshal/workflow"));
@@ -132,10 +133,12 @@ class WorkflowPull extends _basecommand.default {
132
133
  };
133
134
  return (0, _runcontext.ensureResourceDirForTarget)(resourceDir, target);
134
135
  }
136
+ // Default to knock project config first if present, otherwise cwd.
137
+ const dirCtx = await (0, _projectconfig.resolveResourceDir)(this.projectConfig, "workflow", runCwd);
135
138
  // Not inside any existing workflow directory, which means either create a
136
139
  // new worfklow directory in the cwd, or update it if there is one already.
137
140
  if (workflowKey) {
138
- const dirPath = _nodepath.resolve(runCwd, workflowKey);
141
+ const dirPath = _nodepath.resolve(dirCtx.abspath, workflowKey);
139
142
  const exists = await _workflow.isWorkflowDir(dirPath);
140
143
  return {
141
144
  type: "workflow",
@@ -151,13 +154,8 @@ class WorkflowPull extends _basecommand.default {
151
154
  * Pull all workflows
152
155
  */ async pullAllWorkflows() {
153
156
  const { flags } = this.props;
154
- // TODO: In the future we should default to the knock project config first
155
- // if present, before defaulting to the cwd.
156
- const defaultToCwd = {
157
- abspath: this.runContext.cwd,
158
- exists: true
159
- };
160
- const targetDirCtx = flags["workflows-dir"] || defaultToCwd;
157
+ const workflowsIndexDirCtx = await (0, _projectconfig.resolveResourceDir)(this.projectConfig, "workflow", this.runContext.cwd);
158
+ const targetDirCtx = flags["workflows-dir"] || workflowsIndexDirCtx;
161
159
  const prompt = targetDirCtx.exists ? `Pull latest workflows into ${targetDirCtx.abspath}?\n This will overwrite the contents of this directory.` : `Create a new workflows directory at ${targetDirCtx.abspath}?`;
162
160
  const input = flags.force || await (0, _ux.promptToConfirm)(prompt);
163
161
  if (!input) return;
@@ -83,7 +83,7 @@ class WorkflowPush extends _basecommand.default {
83
83
  async run() {
84
84
  const { flags } = this.props;
85
85
  // 1. First read all workflow directories found for the given command.
86
- const target = await _workflow.ensureValidCommandTarget(this.props, this.runContext);
86
+ const target = await _workflow.ensureValidCommandTarget(this.props, this.runContext, this.projectConfig);
87
87
  const [workflows, readErrors] = await _workflow.readAllForCommandTarget(target, {
88
88
  withExtractedFiles: true
89
89
  });
@@ -80,7 +80,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
80
80
  class WorkflowValidate extends _basecommand.default {
81
81
  async run() {
82
82
  // 1. Read all workflow directories found for the given command.
83
- const target = await _workflow.ensureValidCommandTarget(this.props, this.runContext);
83
+ const target = await _workflow.ensureValidCommandTarget(this.props, this.runContext, this.projectConfig);
84
84
  const [workflows, readErrors] = await _workflow.readAllForCommandTarget(target, {
85
85
  withExtractedFiles: true
86
86
  });
@@ -323,6 +323,13 @@ class ApiV1 {
323
323
  params
324
324
  });
325
325
  }
326
+ async listAllMessageTypes(params) {
327
+ const messageTypes = [];
328
+ for await (const messageType of this.mgmtClient.messageTypes.list(params)){
329
+ messageTypes.push(messageType);
330
+ }
331
+ return messageTypes;
332
+ }
326
333
  async getMessageType({ args, flags }) {
327
334
  const params = (0, _objectisomorphic.prune)({
328
335
  environment: flags.environment,
@@ -425,6 +432,20 @@ class ApiV1 {
425
432
  params
426
433
  });
427
434
  }
435
+ async listAllChannels() {
436
+ const channels = [];
437
+ for await (const channel of this.mgmtClient.channels.list()){
438
+ channels.push(channel);
439
+ }
440
+ return channels;
441
+ }
442
+ async listAllEnvironments() {
443
+ const environments = [];
444
+ for await (const environment of this.mgmtClient.environments.list()){
445
+ environments.push(environment);
446
+ }
447
+ return environments;
448
+ }
428
449
  // By methods:
429
450
  async get(subpath, config) {
430
451
  return this.client.get(`/${API_VERSION}` + subpath, config);
@@ -433,18 +454,18 @@ class ApiV1 {
433
454
  return this.client.put(`/${API_VERSION}` + subpath, data, config);
434
455
  }
435
456
  constructor(sessionContext, config){
457
+ var _ref;
436
458
  var _sessionContext_session;
437
459
  _define_property(this, "client", void 0);
438
460
  _define_property(this, "mgmtClient", void 0);
439
461
  const baseURL = sessionContext.apiOrigin;
440
462
  const token = this.getToken(sessionContext);
441
- var _sessionContext_session_clientId;
442
463
  const headers = {
443
464
  // Used to authenticate the request to the API.
444
465
  Authorization: `Bearer ${token}`,
445
466
  // Used in conjunction with the JWT access token, to allow the OAuth server to
446
467
  // verify the client ID of the OAuth client that issued the access token.
447
- "x-knock-client-id": (_sessionContext_session_clientId = (_sessionContext_session = sessionContext.session) === null || _sessionContext_session === void 0 ? void 0 : _sessionContext_session.clientId) !== null && _sessionContext_session_clientId !== void 0 ? _sessionContext_session_clientId : undefined,
468
+ "x-knock-client-id": (_ref = (_sessionContext_session = sessionContext.session) === null || _sessionContext_session === void 0 ? void 0 : _sessionContext_session.clientId) !== null && _ref !== void 0 ? _ref : undefined,
448
469
  "User-Agent": `${config.userAgent}`
449
470
  };
450
471
  this.client = _axios.default.create({
package/dist/lib/auth.js CHANGED
@@ -198,12 +198,12 @@ async function waitForAccessToken(dashboardUrl, authUrl) {
198
198
  };
199
199
  const browserUrl = `${authorizationEndpoint}?${new URLSearchParams(params).toString()}`;
200
200
  server.on("request", async (req, res)=>{
201
+ var _req_url;
201
202
  if (!clientId || !redirectUri) {
202
203
  res.writeHead(500).end("Something went wrong");
203
204
  cleanupAndReject("something went wrong");
204
205
  return;
205
206
  }
206
- var _req_url;
207
207
  const url = new URL((_req_url = req.url) !== null && _req_url !== void 0 ? _req_url : "/", "http://127.0.0.1");
208
208
  if (url.pathname !== callbackPath) {
209
209
  res.writeHead(404).end("Invalid path");
@@ -12,6 +12,7 @@ const _core = require("@oclif/core");
12
12
  const _apiv1 = /*#__PURE__*/ _interop_require_default(require("./api-v1"));
13
13
  const _auth = /*#__PURE__*/ _interop_require_default(require("./auth"));
14
14
  const _accountfeatures = require("./helpers/account-features");
15
+ const _projectconfig = require("./helpers/project-config");
15
16
  const _runcontext = /*#__PURE__*/ _interop_require_wildcard(require("./run-context"));
16
17
  const _urls = require("./urls");
17
18
  const _userconfig = require("./user-config");
@@ -89,52 +90,54 @@ class BaseCommand extends _core.Command {
89
90
  this.configStore = new _userconfig.UserConfigStore(this.config.configDir);
90
91
  // 1. Load user's config from the config dir, as available.
91
92
  await this.configStore.load();
92
- // 2. Parse flags and args, must come after the user config load.
93
+ // 2. Load project config (knock.json) if available.
94
+ this.projectConfig = await (0, _projectconfig.findAndReadProjectConfig)();
95
+ // 3. Parse flags and args, must come after the user config load.
93
96
  const { args, flags } = await this.parse(this.ctor);
94
97
  this.props = {
95
98
  args: args,
96
99
  flags: flags
97
100
  };
98
- // 3. Build the initial session context.
101
+ // 4. Build the initial session context.
99
102
  this.sessionContext = this.buildSessionContext();
100
- // 4. If the command requires authentication, ensure the session is authenticated.
103
+ // 5. If the command requires authentication, ensure the session is authenticated.
101
104
  if (this.requiresAuth) {
102
105
  this.ensureAuthenticated();
103
106
  }
104
- // 5. If the session context is an OAuth session, refresh the access token.
107
+ // 6. If the session context is an OAuth session, refresh the access token.
105
108
  if (this.sessionContext.type === "oauth") {
106
109
  await this.refreshAccessTokenForSession();
107
110
  }
108
- // 6. Instantiate a knock api client.
111
+ // 7. Instantiate a knock api client.
109
112
  this.apiV1 = new _apiv1.default(this.sessionContext, this.config);
110
- // 7. Verify that required features are enabled for the account.
113
+ // 8. Verify that required features are enabled for the account.
111
114
  const ctor = this.ctor;
112
115
  if (ctor.verifyFeatureEnabled) {
113
116
  await this.verifyFeatureEnabled(ctor.verifyFeatureEnabled);
114
117
  }
115
- // 8. Load the run context of the invoked command.
118
+ // 9. Load the run context of the invoked command.
116
119
  this.runContext = await _runcontext.load(this.id);
117
120
  }
118
121
  buildSessionContext() {
122
+ var _this_props_flags_servicetoken, _this_props_flags_apiorigin;
119
123
  const userConfig = this.configStore.get();
120
124
  const session = userConfig.userSession;
121
125
  // If the user has a session and a service token is not provided, use the session.
122
126
  if (session && !this.props.flags["service-token"]) {
123
- var _this_props_flags_apiorigin;
127
+ var _this_props_flags_apiorigin1;
124
128
  return sessionWithDefaultOrigins({
125
129
  type: "oauth",
126
130
  session,
127
- apiOrigin: (_this_props_flags_apiorigin = this.props.flags["api-origin"]) !== null && _this_props_flags_apiorigin !== void 0 ? _this_props_flags_apiorigin : userConfig.apiOrigin,
131
+ apiOrigin: (_this_props_flags_apiorigin1 = this.props.flags["api-origin"]) !== null && _this_props_flags_apiorigin1 !== void 0 ? _this_props_flags_apiorigin1 : userConfig.apiOrigin,
128
132
  dashboardOrigin: userConfig.dashboardOrigin,
129
133
  authOrigin: userConfig.authOrigin
130
134
  });
131
135
  }
132
- var _this_props_flags_servicetoken, _this_props_flags_apiorigin1;
133
136
  // Otherwise, default to this being a service token session.
134
137
  return sessionWithDefaultOrigins({
135
138
  type: "service",
136
139
  token: (_this_props_flags_servicetoken = this.props.flags["service-token"]) !== null && _this_props_flags_servicetoken !== void 0 ? _this_props_flags_servicetoken : userConfig.serviceToken,
137
- apiOrigin: (_this_props_flags_apiorigin1 = this.props.flags["api-origin"]) !== null && _this_props_flags_apiorigin1 !== void 0 ? _this_props_flags_apiorigin1 : userConfig.apiOrigin,
140
+ apiOrigin: (_this_props_flags_apiorigin = this.props.flags["api-origin"]) !== null && _this_props_flags_apiorigin !== void 0 ? _this_props_flags_apiorigin : userConfig.apiOrigin,
138
141
  dashboardOrigin: userConfig.dashboardOrigin,
139
142
  authOrigin: userConfig.authOrigin
140
143
  });
@@ -156,12 +159,12 @@ class BaseCommand extends _core.Command {
156
159
  async refreshAccessTokenForSession() {
157
160
  // Maybe refresh the access token?
158
161
  try {
162
+ var _ref, _ref1;
159
163
  var _this_sessionContext_session, _this_sessionContext_session1;
160
- var _this_sessionContext_session_clientId, _this_sessionContext_session_refreshToken;
161
164
  const refreshedSession = await _auth.default.refreshAccessToken({
162
165
  authUrl: this.sessionContext.authOrigin,
163
- clientId: (_this_sessionContext_session_clientId = (_this_sessionContext_session = this.sessionContext.session) === null || _this_sessionContext_session === void 0 ? void 0 : _this_sessionContext_session.clientId) !== null && _this_sessionContext_session_clientId !== void 0 ? _this_sessionContext_session_clientId : "",
164
- refreshToken: (_this_sessionContext_session_refreshToken = (_this_sessionContext_session1 = this.sessionContext.session) === null || _this_sessionContext_session1 === void 0 ? void 0 : _this_sessionContext_session1.refreshToken) !== null && _this_sessionContext_session_refreshToken !== void 0 ? _this_sessionContext_session_refreshToken : ""
166
+ clientId: (_ref = (_this_sessionContext_session = this.sessionContext.session) === null || _this_sessionContext_session === void 0 ? void 0 : _this_sessionContext_session.clientId) !== null && _ref !== void 0 ? _ref : "",
167
+ refreshToken: (_ref1 = (_this_sessionContext_session1 = this.sessionContext.session) === null || _this_sessionContext_session1 === void 0 ? void 0 : _this_sessionContext_session1.refreshToken) !== null && _ref1 !== void 0 ? _ref1 : ""
165
168
  });
166
169
  this.debug("Successfully refreshed access token.");
167
170
  // Update the user config to use the new session.
@@ -178,7 +181,7 @@ class BaseCommand extends _core.Command {
178
181
  }
179
182
  }
180
183
  constructor(...args){
181
- super(...args), _define_property(this, "props", void 0), _define_property(this, "apiV1", void 0), _define_property(this, "runContext", void 0), _define_property(this, "sessionContext", void 0), _define_property(this, "configStore", void 0), _define_property(this, "requiresAuth", true);
184
+ super(...args), _define_property(this, "props", void 0), _define_property(this, "apiV1", void 0), _define_property(this, "runContext", void 0), _define_property(this, "sessionContext", void 0), _define_property(this, "configStore", void 0), _define_property(this, "projectConfig", void 0), _define_property(this, "requiresAuth", true);
182
185
  }
183
186
  }
184
187
  _define_property(BaseCommand, "verifyFeatureEnabled", void 0);
@@ -148,8 +148,6 @@ const slug = _core.Flags.custom({
148
148
  });
149
149
  const branch = slug({
150
150
  summary: "The slug of the branch to use.",
151
- // TODO Hide until branching is released in GA
152
- hidden: true,
153
151
  // Memoize this flag's default. oclif runs this default function even when the flag is unused.
154
152
  // Using lodash's once avoids unnecessarily reading the branch file multiple times.
155
153
  default: (0, _lodash.once)(_branch.readSlugFromBranchFile)