@knocklabs/cli 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +80 -84
  2. package/dist/commands/commit/list.js +11 -5
  3. package/dist/commands/guide/pull.js +6 -2
  4. package/dist/commands/guide/push.js +5 -6
  5. package/dist/commands/guide/validate.js +2 -5
  6. package/dist/commands/layout/pull.js +6 -2
  7. package/dist/commands/layout/push.js +3 -1
  8. package/dist/commands/message-type/pull.js +6 -2
  9. package/dist/commands/message-type/push.js +3 -1
  10. package/dist/commands/partial/pull.js +6 -2
  11. package/dist/commands/partial/push.js +3 -1
  12. package/dist/commands/translation/get.js +1 -0
  13. package/dist/commands/translation/list.js +1 -0
  14. package/dist/commands/translation/pull.js +1 -0
  15. package/dist/commands/translation/push.js +1 -0
  16. package/dist/commands/translation/validate.js +1 -0
  17. package/dist/commands/workflow/new.js +8 -9
  18. package/dist/commands/workflow/pull.js +6 -2
  19. package/dist/commands/workflow/push.js +5 -6
  20. package/dist/commands/workflow/validate.js +2 -5
  21. package/dist/lib/base-command.js +17 -1
  22. package/dist/lib/helpers/account-features.js +53 -0
  23. package/dist/lib/marshal/email-layout/processor.isomorphic.js +3 -2
  24. package/dist/lib/marshal/email-layout/writer.js +6 -4
  25. package/dist/lib/marshal/guide/processor.isomorphic.js +3 -2
  26. package/dist/lib/marshal/guide/writer.js +6 -4
  27. package/dist/lib/marshal/message-type/processor.isomorphic.js +3 -2
  28. package/dist/lib/marshal/message-type/writer.js +6 -4
  29. package/dist/lib/marshal/partial/processor.isomorphic.js +3 -2
  30. package/dist/lib/marshal/partial/writer.js +6 -4
  31. package/dist/lib/marshal/shared/helpers.isomorphic.js +7 -1
  32. package/dist/lib/marshal/workflow/processor.isomorphic.js +3 -2
  33. package/dist/lib/marshal/workflow/writer.js +6 -4
  34. package/oclif.manifest.json +20 -19
  35. package/package.json +7 -4
@@ -109,7 +109,9 @@ class EmailLayoutPull extends _basecommand.default {
109
109
  });
110
110
  return this.apiV1.getEmailLayout(props);
111
111
  });
112
- await _emaillayout.writeEmailLayoutDirFromData(dirContext, resp.data);
112
+ await _emaillayout.writeEmailLayoutDirFromData(dirContext, resp.data, {
113
+ withSchema: true
114
+ });
113
115
  const action = dirContext.exists ? "updated" : "created";
114
116
  const scope = (0, _command.formatCommandScope)(flags);
115
117
  this.log(`‣ Successfully ${action} \`${dirContext.key}\` at ${dirContext.abspath} using ${scope}`);
@@ -127,7 +129,9 @@ class EmailLayoutPull extends _basecommand.default {
127
129
  if (!input) return;
128
130
  _ux.spinner.start(`‣ Loading`);
129
131
  const emailLayouts = await this.listAllEmailLayouts();
130
- await _emaillayout.writeEmailLayoutIndexDir(targetDirCtx, emailLayouts);
132
+ await _emaillayout.writeEmailLayoutIndexDir(targetDirCtx, emailLayouts, {
133
+ withSchema: true
134
+ });
131
135
  _ux.spinner.stop();
132
136
  const action = targetDirCtx.exists ? "updated" : "created";
133
137
  const scope = (0, _command.formatCommandScope)(flags);
@@ -121,7 +121,9 @@ class EmailLayoutPush extends _basecommand.default {
121
121
  // Update the layout directory with the successfully pushed layout
122
122
  // payload from the server.
123
123
  // eslint-disable-next-line no-await-in-loop
124
- await _emaillayout.writeEmailLayoutDirFromData(layout, resp.data.email_layout);
124
+ await _emaillayout.writeEmailLayoutDirFromData(layout, resp.data.email_layout, {
125
+ withSchema: true
126
+ });
125
127
  continue;
126
128
  }
127
129
  const error = new _error.SourceError((0, _request.formatErrorRespMessage)(resp), _emaillayout.emailLayoutJsonPath(layout), "ApiError");
@@ -112,7 +112,9 @@ class MessageTypePull extends _basecommand.default {
112
112
  });
113
113
  return this.apiV1.getMessageType(props);
114
114
  });
115
- await _messagetype.writeMessageTypeDirFromData(dirContext, resp.data);
115
+ await _messagetype.writeMessageTypeDirFromData(dirContext, resp.data, {
116
+ withSchema: true
117
+ });
116
118
  const action = dirContext.exists ? "updated" : "created";
117
119
  const scope = (0, _command.formatCommandScope)(flags);
118
120
  this.log(`‣ Successfully ${action} \`${dirContext.key}\` at ${dirContext.abspath} using ${scope}`);
@@ -161,7 +163,9 @@ class MessageTypePull extends _basecommand.default {
161
163
  if (!input) return;
162
164
  _ux.spinner.start(`‣ Loading`);
163
165
  const messageTypes = await this.listAllMessageTypes();
164
- await _messagetype.writeMessageTypesIndexDir(targetDirCtx, messageTypes);
166
+ await _messagetype.writeMessageTypesIndexDir(targetDirCtx, messageTypes, {
167
+ withSchema: true
168
+ });
165
169
  _ux.spinner.stop();
166
170
  const action = targetDirCtx.exists ? "updated" : "created";
167
171
  const scope = (0, _command.formatCommandScope)(flags);
@@ -121,7 +121,9 @@ class MessageTypePush extends _basecommand.default {
121
121
  // Update the message type directory with the successfully pushed message
122
122
  // type payload from the server.
123
123
  // eslint-disable-next-line no-await-in-loop
124
- await _messagetype.writeMessageTypeDirFromData(messageType, resp.data.message_type);
124
+ await _messagetype.writeMessageTypeDirFromData(messageType, resp.data.message_type, {
125
+ withSchema: true
126
+ });
125
127
  continue;
126
128
  }
127
129
  const error = new _error.SourceError((0, _request.formatErrorRespMessage)(resp), _messagetype.messageTypeJsonPath(messageType), "ApiError");
@@ -109,7 +109,9 @@ class PartialPull extends _basecommand.default {
109
109
  });
110
110
  return this.apiV1.getPartial(props);
111
111
  });
112
- await _partial.writePartialDirFromData(dirContext, resp.data);
112
+ await _partial.writePartialDirFromData(dirContext, resp.data, {
113
+ withSchema: true
114
+ });
113
115
  const action = dirContext.exists ? "updated" : "created";
114
116
  const scope = (0, _command.formatCommandScope)(flags);
115
117
  this.log(`‣ Successfully ${action} \`${dirContext.key}\` at ${dirContext.abspath} using ${scope}`);
@@ -127,7 +129,9 @@ class PartialPull extends _basecommand.default {
127
129
  if (!input) return;
128
130
  _ux.spinner.start(`‣ Loading`);
129
131
  const partials = await this.listAllPartials();
130
- await _partial.writePartialsIndexDir(targetDirCtx, partials);
132
+ await _partial.writePartialsIndexDir(targetDirCtx, partials, {
133
+ withSchema: true
134
+ });
131
135
  _ux.spinner.stop();
132
136
  const action = targetDirCtx.exists ? "updated" : "created";
133
137
  const scope = (0, _command.formatCommandScope)(flags);
@@ -121,7 +121,9 @@ class PartialPush extends _basecommand.default {
121
121
  // Update the partial directory with the successfully pushed partial
122
122
  // payload from the server.
123
123
  // eslint-disable-next-line no-await-in-loop
124
- await _partial.writePartialDirFromData(partial, resp.data.partial);
124
+ await _partial.writePartialDirFromData(partial, resp.data.partial, {
125
+ withSchema: true
126
+ });
125
127
  continue;
126
128
  }
127
129
  const error = new _error.SourceError((0, _request.formatErrorRespMessage)(resp), _partial.partialJsonPath(partial), "ApiError");
@@ -134,6 +134,7 @@ class TranslationGet extends _basecommand.default {
134
134
  }
135
135
  }
136
136
  _define_property(TranslationGet, "summary", "Display a single translation from an environment.");
137
+ _define_property(TranslationGet, "verifyFeatureEnabled", "translations");
137
138
  _define_property(TranslationGet, "flags", {
138
139
  environment: _core.Flags.string({
139
140
  default: "development",
@@ -137,6 +137,7 @@ class TranslationList extends _basecommand.default {
137
137
  }
138
138
  }
139
139
  _define_property(TranslationList, "summary", "Display all translations for an environment.");
140
+ _define_property(TranslationList, "verifyFeatureEnabled", "translations");
140
141
  _define_property(TranslationList, "flags", {
141
142
  environment: _core.Flags.string({
142
143
  default: "development",
@@ -173,6 +173,7 @@ class TranslationPull extends _basecommand.default {
173
173
  }
174
174
  }
175
175
  _define_property(TranslationPull, "summary", "Pull one or more translations from an environment into a local file system.");
176
+ _define_property(TranslationPull, "verifyFeatureEnabled", "translations");
176
177
  _define_property(TranslationPull, "flags", {
177
178
  environment: _core.Flags.string({
178
179
  default: "development",
@@ -125,6 +125,7 @@ class TranslationPush extends _basecommand.default {
125
125
  }
126
126
  }
127
127
  _define_property(TranslationPush, "summary", "Push one or more translations from a local file system to Knock.");
128
+ _define_property(TranslationPush, "verifyFeatureEnabled", "translations");
128
129
  _define_property(TranslationPush, "flags", {
129
130
  environment: _core.Flags.string({
130
131
  summary: "Pushing a translation is only allowed in the development environment",
@@ -120,6 +120,7 @@ class TranslationValidate extends _basecommand.default {
120
120
  }
121
121
  }
122
122
  _define_property(TranslationValidate, "summary", "Validate one or more translations from a local file system.");
123
+ _define_property(TranslationValidate, "verifyFeatureEnabled", "translations");
123
124
  _define_property(TranslationValidate, "flags", {
124
125
  environment: _core.Flags.string({
125
126
  summary: "Validating a translation is only done in the development environment",
@@ -12,7 +12,7 @@ const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
12
12
  const _core = require("@oclif/core");
13
13
  const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
14
14
  const _basecommand = /*#__PURE__*/ _interop_require_default(require("../../lib/base-command"));
15
- const _objectisomorphic = require("../../lib/helpers/object.isomorphic");
15
+ const _const = require("../../lib/helpers/const");
16
16
  const _ux = require("../../lib/helpers/ux");
17
17
  const _workflow = /*#__PURE__*/ _interop_require_wildcard(require("../../lib/marshal/workflow"));
18
18
  function _define_property(obj, key, value) {
@@ -119,17 +119,12 @@ class WorkflowNew extends _basecommand.default {
119
119
  const isExistingWorkflow = await this.checkExistingWorkflow();
120
120
  if (isExistingWorkflow) {
121
121
  this.log("");
122
- this.warn(`Workflow \`${args.workflowKey}\` already exists in \`development\` environment`);
122
+ this.warn(`Workflow \`${args.workflowKey}\` already exists in \`${flags.environment}\` environment`);
123
123
  }
124
124
  }
125
125
  async checkExistingWorkflow() {
126
- const props = (0, _objectisomorphic.merge)(this.props, {
127
- flags: {
128
- environment: "development"
129
- }
130
- });
131
126
  try {
132
- const resp = await this.apiV1.getWorkflow(props);
127
+ const resp = await this.apiV1.getWorkflow(this.props);
133
128
  return resp.status === 200;
134
129
  } catch {}
135
130
  }
@@ -140,7 +135,11 @@ _define_property(WorkflowNew, "flags", {
140
135
  "step"
141
136
  ]
142
137
  }),
143
- force: _core.Flags.boolean()
138
+ force: _core.Flags.boolean(),
139
+ environment: _core.Flags.string({
140
+ hidden: true,
141
+ default: _const.KnockEnv.Development
142
+ })
144
143
  });
145
144
  _define_property(WorkflowNew, "args", {
146
145
  workflowKey: _core.Args.string({
@@ -113,7 +113,9 @@ class WorkflowPull extends _basecommand.default {
113
113
  return this.apiV1.getWorkflow(props);
114
114
  });
115
115
  // 3. Write the fetched workflow to create or update the workflow directory.
116
- await _workflow.writeWorkflowDirFromData(dirContext, resp.data);
116
+ await _workflow.writeWorkflowDirFromData(dirContext, resp.data, {
117
+ withSchema: true
118
+ });
117
119
  const action = dirContext.exists ? "updated" : "created";
118
120
  const scope = (0, _command.formatCommandScope)(flags);
119
121
  this.log(`‣ Successfully ${action} \`${dirContext.key}\` at ${dirContext.abspath} using ${scope}`);
@@ -162,7 +164,9 @@ class WorkflowPull extends _basecommand.default {
162
164
  // Fetch all workflows then write them to the local file system.
163
165
  _ux.spinner.start(`‣ Loading`);
164
166
  const workflows = await this.listAllWorkflows();
165
- await _workflow.writeWorkflowsIndexDir(targetDirCtx, workflows);
167
+ await _workflow.writeWorkflowsIndexDir(targetDirCtx, workflows, {
168
+ withSchema: true
169
+ });
166
170
  _ux.spinner.stop();
167
171
  const action = targetDirCtx.exists ? "updated" : "created";
168
172
  const scope = (0, _command.formatCommandScope)(flags);
@@ -121,7 +121,9 @@ class WorkflowPush extends _basecommand.default {
121
121
  // Update the workflow directory with the successfully pushed workflow
122
122
  // payload from the server.
123
123
  // eslint-disable-next-line no-await-in-loop
124
- await _workflow.writeWorkflowDirFromData(workflow, resp.data.workflow);
124
+ await _workflow.writeWorkflowDirFromData(workflow, resp.data.workflow, {
125
+ withSchema: true
126
+ });
125
127
  continue;
126
128
  }
127
129
  const error = new _error.SourceError((0, _request.formatErrorRespMessage)(resp), _workflow.workflowJsonPath(workflow), "ApiError");
@@ -138,11 +140,8 @@ class WorkflowPush extends _basecommand.default {
138
140
  _define_property(WorkflowPush, "summary", "Push one or more workflows from a local file system to Knock.");
139
141
  _define_property(WorkflowPush, "flags", {
140
142
  environment: _core.Flags.string({
141
- summary: "Pushing a workflow is only allowed in the development environment",
142
- default: _const.KnockEnv.Development,
143
- options: [
144
- _const.KnockEnv.Development
145
- ]
143
+ summary: "The environment to push the workflow to. Defaults to development.",
144
+ default: _const.KnockEnv.Development
146
145
  }),
147
146
  branch: _flag.branch,
148
147
  all: _core.Flags.boolean({
@@ -125,11 +125,8 @@ class WorkflowValidate extends _basecommand.default {
125
125
  _define_property(WorkflowValidate, "summary", "Validate one or more workflows from a local file system.");
126
126
  _define_property(WorkflowValidate, "flags", {
127
127
  environment: _core.Flags.string({
128
- summary: "Validating a workflow is only done in the development environment",
129
- default: _const.KnockEnv.Development,
130
- options: [
131
- _const.KnockEnv.Development
132
- ]
128
+ summary: "The environment to validate the workflow in. Defaults to development.",
129
+ default: _const.KnockEnv.Development
133
130
  }),
134
131
  branch: _flag.branch,
135
132
  all: _core.Flags.boolean({
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "default", {
11
11
  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
+ const _accountfeatures = require("./helpers/account-features");
14
15
  const _runcontext = /*#__PURE__*/ _interop_require_wildcard(require("./run-context"));
15
16
  const _urls = require("./urls");
16
17
  const _userconfig = require("./user-config");
@@ -106,7 +107,12 @@ class BaseCommand extends _core.Command {
106
107
  }
107
108
  // 6. Instantiate a knock api client.
108
109
  this.apiV1 = new _apiv1.default(this.sessionContext, this.config);
109
- // 7. Load the run context of the invoked command.
110
+ // 7. Verify that required features are enabled for the account.
111
+ const ctor = this.ctor;
112
+ if (ctor.verifyFeatureEnabled) {
113
+ await this.verifyFeatureEnabled(ctor.verifyFeatureEnabled);
114
+ }
115
+ // 8. Load the run context of the invoked command.
110
116
  this.runContext = await _runcontext.load(this.id);
111
117
  }
112
118
  buildSessionContext() {
@@ -138,6 +144,15 @@ class BaseCommand extends _core.Command {
138
144
  this.error("No token found. Refusing to run command.");
139
145
  }
140
146
  }
147
+ async verifyFeatureEnabled(feature) {
148
+ if (feature === "translations") {
149
+ const featureCheck = await (0, _accountfeatures.checkTranslationsFeature)(this.apiV1);
150
+ if (!featureCheck.enabled) {
151
+ this.log(featureCheck.message);
152
+ this.exit(0);
153
+ }
154
+ }
155
+ }
141
156
  async refreshAccessTokenForSession() {
142
157
  // Maybe refresh the access token?
143
158
  try {
@@ -166,6 +181,7 @@ class BaseCommand extends _core.Command {
166
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);
167
182
  }
168
183
  }
184
+ _define_property(BaseCommand, "verifyFeatureEnabled", void 0);
169
185
  // Base flags are inherited by any command that extends BaseCommand.
170
186
  _define_property(BaseCommand, "baseFlags", {
171
187
  // Evaluated in the following precedence:
@@ -0,0 +1,53 @@
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: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get checkAccountFeature () {
13
+ return checkAccountFeature;
14
+ },
15
+ get checkTranslationsFeature () {
16
+ return checkTranslationsFeature;
17
+ }
18
+ });
19
+ const _request = require("./request");
20
+ async function checkAccountFeature(apiV1, featureName, options = {}) {
21
+ try {
22
+ const whoamiResp = await apiV1.whoami();
23
+ if (!(0, _request.isSuccessResp)(whoamiResp)) {
24
+ return {
25
+ enabled: false,
26
+ message: `Unable to retrieve account information: ${whoamiResp.status} ${whoamiResp.statusText}`
27
+ };
28
+ }
29
+ const accountFeatures = whoamiResp.data.account_features;
30
+ const isFeatureEnabled = (accountFeatures === null || accountFeatures === void 0 ? void 0 : accountFeatures[featureName]) === true;
31
+ if (!isFeatureEnabled) {
32
+ const defaultMessage = `The ${featureName} feature is not enabled for your account. Please contact support to enable this feature.`;
33
+ return {
34
+ enabled: false,
35
+ message: options.errorMessage || defaultMessage
36
+ };
37
+ }
38
+ return {
39
+ enabled: true
40
+ };
41
+ } catch (error) {
42
+ return {
43
+ enabled: false,
44
+ message: error instanceof Error ? error.message : "Unknown error occurred while checking translations feature"
45
+ };
46
+ }
47
+ }
48
+ async function checkTranslationsFeature(apiV1, options = {}) {
49
+ return checkAccountFeature(apiV1, "translations_allowed", {
50
+ errorMessage: options.errorMessage || "Translations are not enabled for your account. Please contact support to enable the translations feature.",
51
+ ...options
52
+ });
53
+ }
@@ -38,8 +38,9 @@ const compileExtractionSettings = (emailLayout)=>{
38
38
  }
39
39
  return map;
40
40
  };
41
- const buildEmailLayoutDirBundle = (remoteEmailLayout, localEmailLayout = {})=>{
41
+ const buildEmailLayoutDirBundle = (remoteEmailLayout, localEmailLayout, $schema)=>{
42
42
  const bundle = {};
43
+ localEmailLayout = localEmailLayout || {};
43
44
  const mutRemoteEmailLayout = (0, _lodash.cloneDeep)(remoteEmailLayout);
44
45
  // A map of extraction settings of every field in the email layout
45
46
  const compiledExtractionSettings = compileExtractionSettings(mutRemoteEmailLayout);
@@ -75,5 +76,5 @@ const buildEmailLayoutDirBundle = (remoteEmailLayout, localEmailLayout = {})=>{
75
76
  // the layout JSON realtive path + the file content.
76
77
  return (0, _lodash.set)(bundle, [
77
78
  LAYOUT_JSON
78
- ], (0, _helpersisomorphic.prepareResourceJson)(mutRemoteEmailLayout));
79
+ ], (0, _helpersisomorphic.prepareResourceJson)(mutRemoteEmailLayout, $schema));
79
80
  };
@@ -73,13 +73,15 @@ function _interop_require_wildcard(obj, nodeInterop) {
73
73
  }
74
74
  return newObj;
75
75
  }
76
- const writeEmailLayoutDirFromData = async (emailLayoutDirCtx, remoteEmailLayout)=>{
76
+ const EMAIL_LAYOUT_SCHEMA = "https://schemas.knock.app/cli/email-layout.json";
77
+ const writeEmailLayoutDirFromData = async (emailLayoutDirCtx, remoteEmailLayout, options)=>{
78
+ const { withSchema = false } = options || {};
77
79
  // If the layout directory exists on the file system (i.e. previously
78
80
  // pulled before), then read the layout file to use as a reference.
79
81
  const [localEmailLayout] = emailLayoutDirCtx.exists ? await (0, _reader.readEmailLayoutDir)(emailLayoutDirCtx, {
80
82
  withExtractedFiles: true
81
83
  }) : [];
82
- const bundle = (0, _processorisomorphic.buildEmailLayoutDirBundle)(remoteEmailLayout, localEmailLayout);
84
+ const bundle = (0, _processorisomorphic.buildEmailLayoutDirBundle)(remoteEmailLayout, localEmailLayout, withSchema ? EMAIL_LAYOUT_SCHEMA : undefined);
83
85
  const backupDirPath = _nodepath.default.resolve(_const.sandboxDir, (0, _lodash.uniqueId)("backup"));
84
86
  try {
85
87
  // We store a backup in case there's an error.
@@ -109,7 +111,7 @@ const writeEmailLayoutDirFromData = async (emailLayoutDirCtx, remoteEmailLayout)
109
111
  await _fsextra.remove(backupDirPath);
110
112
  }
111
113
  };
112
- const writeEmailLayoutIndexDir = async (indexDirCtx, remoteEmailLayouts)=>{
114
+ const writeEmailLayoutIndexDir = async (indexDirCtx, remoteEmailLayouts, options)=>{
113
115
  const backupDirPath = _nodepath.default.resolve(_const.sandboxDir, (0, _lodash.uniqueId)("backup"));
114
116
  try {
115
117
  if (indexDirCtx.exists) {
@@ -124,7 +126,7 @@ const writeEmailLayoutIndexDir = async (indexDirCtx, remoteEmailLayouts)=>{
124
126
  abspath: emailLayoutDirPath,
125
127
  exists: indexDirCtx.exists ? await (0, _helpers.isEmailLayoutDir)(emailLayoutDirPath) : false
126
128
  };
127
- return writeEmailLayoutDirFromData(emailLayoutDirCtx, remoteEmailLayout);
129
+ return writeEmailLayoutDirFromData(emailLayoutDirCtx, remoteEmailLayout, options);
128
130
  });
129
131
  await Promise.all(writeEmailLayoutDirPromises);
130
132
  } catch (error) {
@@ -45,8 +45,9 @@ const compileExtractionSettings = (guide)=>{
45
45
  }
46
46
  return map;
47
47
  };
48
- const buildGuideDirBundle = (remoteGuide, localGuide = {})=>{
48
+ const buildGuideDirBundle = (remoteGuide, localGuide, $schema)=>{
49
49
  const bundle = {};
50
+ localGuide = localGuide || {};
50
51
  const mutRemoteGuide = (0, _lodash.cloneDeep)(remoteGuide);
51
52
  // A map of extraction settings of every field in the guide.
52
53
  const compiledExtractionSettings = compileExtractionSettings(mutRemoteGuide);
@@ -81,5 +82,5 @@ const buildGuideDirBundle = (remoteGuide, localGuide = {})=>{
81
82
  // the guide JSON relative path + the file content.
82
83
  return (0, _lodash.set)(bundle, [
83
84
  GUIDE_JSON
84
- ], (0, _helpersisomorphic.prepareResourceJson)(mutRemoteGuide));
85
+ ], (0, _helpersisomorphic.prepareResourceJson)(mutRemoteGuide, $schema));
85
86
  };
@@ -68,13 +68,15 @@ function _interop_require_wildcard(obj, nodeInterop) {
68
68
  }
69
69
  return newObj;
70
70
  }
71
- const writeGuideDirFromData = async (guideDirCtx, remoteGuide)=>{
71
+ const GUIDE_SCHEMA = "https://schemas.knock.app/cli/guide.json";
72
+ const writeGuideDirFromData = async (guideDirCtx, remoteGuide, options)=>{
73
+ const { withSchema = false } = options || {};
72
74
  // If the guide directory exists on the file system (i.e. previously
73
75
  // pulled before), then read the guide file to use as a reference.
74
76
  const [localGuide] = guideDirCtx.exists ? await (0, _reader.readGuideDir)(guideDirCtx, {
75
77
  withExtractedFiles: true
76
78
  }) : [];
77
- const bundle = (0, _processorisomorphic.buildGuideDirBundle)(remoteGuide, localGuide);
79
+ const bundle = (0, _processorisomorphic.buildGuideDirBundle)(remoteGuide, localGuide, withSchema ? GUIDE_SCHEMA : undefined);
78
80
  return writeGuideDirFromBundle(guideDirCtx, bundle);
79
81
  };
80
82
  /*
@@ -136,7 +138,7 @@ const writeGuideDirFromData = async (guideDirCtx, remoteGuide)=>{
136
138
  });
137
139
  await Promise.all(promises);
138
140
  };
139
- const writeGuidesIndexDir = async (indexDirCtx, remoteGuides)=>{
141
+ const writeGuidesIndexDir = async (indexDirCtx, remoteGuides, options)=>{
140
142
  const backupDirPath = _nodepath.resolve(_const.sandboxDir, (0, _lodash.uniqueId)("backup"));
141
143
  try {
142
144
  // If the index directory already exists, back it up in the temp sandbox
@@ -154,7 +156,7 @@ const writeGuidesIndexDir = async (indexDirCtx, remoteGuides)=>{
154
156
  abspath: guideDirPath,
155
157
  exists: indexDirCtx.exists ? await (0, _helpers.isGuideDir)(guideDirPath) : false
156
158
  };
157
- return writeGuideDirFromData(guideDirCtx, guide);
159
+ return writeGuideDirFromData(guideDirCtx, guide, options);
158
160
  });
159
161
  await Promise.all(writeGuideDirPromises);
160
162
  } catch (error) {
@@ -38,9 +38,10 @@ const compileExtractionSettings = (messageType)=>{
38
38
  }
39
39
  return map;
40
40
  };
41
- const buildMessageTypeDirBundle = (remoteMessageType, localMessageType = {})=>{
41
+ const buildMessageTypeDirBundle = (remoteMessageType, localMessageType, $schema)=>{
42
42
  const bundle = {};
43
43
  const mutRemoteMessageType = (0, _lodash.cloneDeep)(remoteMessageType);
44
+ localMessageType = localMessageType || {};
44
45
  // A map of extraction settings of every field in the message type
45
46
  const compiledExtractionSettings = compileExtractionSettings(mutRemoteMessageType);
46
47
  // Iterate through each extractable field, determine whether we need to
@@ -74,5 +75,5 @@ const buildMessageTypeDirBundle = (remoteMessageType, localMessageType = {})=>{
74
75
  // the message type JSON relative path + the file content.
75
76
  return (0, _lodash.set)(bundle, [
76
77
  MESSAGE_TYPE_JSON
77
- ], (0, _helpersisomorphic.prepareResourceJson)(mutRemoteMessageType));
78
+ ], (0, _helpersisomorphic.prepareResourceJson)(mutRemoteMessageType, $schema));
78
79
  };
@@ -68,13 +68,15 @@ function _interop_require_wildcard(obj, nodeInterop) {
68
68
  }
69
69
  return newObj;
70
70
  }
71
- const writeMessageTypeDirFromData = async (messageTypeDirCtx, remoteMessageType)=>{
71
+ const MESSAGE_TYPE_SCHEMA = "https://schemas.knock.app/cli/message-type.json";
72
+ const writeMessageTypeDirFromData = async (messageTypeDirCtx, remoteMessageType, options)=>{
73
+ const { withSchema = false } = options || {};
72
74
  // If the message type directory exists on the file system (i.e. previously
73
75
  // pulled before), then read the message type file to use as a reference.
74
76
  const [localMessageType] = messageTypeDirCtx.exists ? await (0, _reader.readMessageTypeDir)(messageTypeDirCtx, {
75
77
  withExtractedFiles: true
76
78
  }) : [];
77
- const bundle = (0, _processorisomorphic.buildMessageTypeDirBundle)(remoteMessageType, localMessageType);
79
+ const bundle = (0, _processorisomorphic.buildMessageTypeDirBundle)(remoteMessageType, localMessageType, withSchema ? MESSAGE_TYPE_SCHEMA : undefined);
78
80
  return writeMessageTypeDirFromBundle(messageTypeDirCtx, bundle);
79
81
  };
80
82
  /*
@@ -136,7 +138,7 @@ const writeMessageTypeDirFromData = async (messageTypeDirCtx, remoteMessageType)
136
138
  });
137
139
  await Promise.all(promises);
138
140
  };
139
- const writeMessageTypesIndexDir = async (indexDirCtx, remoteMessageTypes)=>{
141
+ const writeMessageTypesIndexDir = async (indexDirCtx, remoteMessageTypes, options)=>{
140
142
  const backupDirPath = _nodepath.resolve(_const.sandboxDir, (0, _lodash.uniqueId)("backup"));
141
143
  try {
142
144
  // If the index directory already exists, back it up in the temp sandbox
@@ -154,7 +156,7 @@ const writeMessageTypesIndexDir = async (indexDirCtx, remoteMessageTypes)=>{
154
156
  abspath: messageTypeDirPath,
155
157
  exists: indexDirCtx.exists ? await (0, _helpers.isMessageTypeDir)(messageTypeDirPath) : false
156
158
  };
157
- return writeMessageTypeDirFromData(messageTypeDirCtx, messageType);
159
+ return writeMessageTypeDirFromData(messageTypeDirCtx, messageType, options);
158
160
  });
159
161
  await Promise.all(promises);
160
162
  } catch (error) {
@@ -61,8 +61,9 @@ const compileExtractionSettings = (partial)=>{
61
61
  }
62
62
  return map;
63
63
  };
64
- const buildPartialDirBundle = (remotePartial, localPartial = {})=>{
64
+ const buildPartialDirBundle = (remotePartial, localPartial, $schema)=>{
65
65
  const bundle = {};
66
+ localPartial = localPartial || {};
66
67
  const mutRemotePartial = (0, _lodash.cloneDeep)(remotePartial);
67
68
  // A map of extraction settings of every field in the partial
68
69
  const compiledExtractionSettings = compileExtractionSettings(mutRemotePartial);
@@ -97,5 +98,5 @@ const buildPartialDirBundle = (remotePartial, localPartial = {})=>{
97
98
  // the partial JSON relative path + the file content.
98
99
  return (0, _lodash.set)(bundle, [
99
100
  PARTIAL_JSON
100
- ], (0, _helpersisomorphic.prepareResourceJson)(mutRemotePartial));
101
+ ], (0, _helpersisomorphic.prepareResourceJson)(mutRemotePartial, $schema));
101
102
  };
@@ -68,13 +68,15 @@ function _interop_require_wildcard(obj, nodeInterop) {
68
68
  }
69
69
  return newObj;
70
70
  }
71
- const writePartialDirFromData = async (partialDirCtx, remotePartial)=>{
71
+ const PARTIAL_SCHEMA = "https://schemas.knock.app/cli/partial.json";
72
+ const writePartialDirFromData = async (partialDirCtx, remotePartial, options)=>{
73
+ const { withSchema = false } = options || {};
72
74
  // If the partial directory exists on the file system (i.e. previously
73
75
  // pulled before), then read the partial file to use as a reference.
74
76
  const [localPartial] = partialDirCtx.exists ? await (0, _reader.readPartialDir)(partialDirCtx, {
75
77
  withExtractedFiles: true
76
78
  }) : [];
77
- const bundle = (0, _processorisomorphic.buildPartialDirBundle)(remotePartial, localPartial);
79
+ const bundle = (0, _processorisomorphic.buildPartialDirBundle)(remotePartial, localPartial, withSchema ? PARTIAL_SCHEMA : undefined);
78
80
  return writePartialDirFromBundle(partialDirCtx, bundle);
79
81
  };
80
82
  /*
@@ -136,7 +138,7 @@ const writePartialDirFromData = async (partialDirCtx, remotePartial)=>{
136
138
  });
137
139
  await Promise.all(promises);
138
140
  };
139
- const writePartialsIndexDir = async (indexDirCtx, remotePartials)=>{
141
+ const writePartialsIndexDir = async (indexDirCtx, remotePartials, options)=>{
140
142
  const backupDirPath = _nodepath.resolve(_const.sandboxDir, (0, _lodash.uniqueId)("backup"));
141
143
  try {
142
144
  // If the index directory already exists, back it up in the temp sandbox
@@ -154,7 +156,7 @@ const writePartialsIndexDir = async (indexDirCtx, remotePartials)=>{
154
156
  abspath: partialDirPath,
155
157
  exists: indexDirCtx.exists ? await (0, _helpers.isPartialDir)(partialDirPath) : false
156
158
  };
157
- return writePartialDirFromData(partialDirCtx, partial);
159
+ return writePartialDirFromData(partialDirCtx, partial, options);
158
160
  });
159
161
  await Promise.all(writePartialDirPromises);
160
162
  } catch (error) {
@@ -15,7 +15,7 @@ const REMOVED_READONLY_FIELDS = [
15
15
  "sha",
16
16
  "updated_at"
17
17
  ];
18
- const prepareResourceJson = (resource)=>{
18
+ const prepareResourceJson = (resource, $schema)=>{
19
19
  var _resource___annotation;
20
20
  const readonlyFields = ((_resource___annotation = resource.__annotation) === null || _resource___annotation === void 0 ? void 0 : _resource___annotation.readonly_fields) || [];
21
21
  const [readonly, remainder] = (0, _objectisomorphic.split)(resource, readonlyFields);
@@ -25,6 +25,12 @@ const prepareResourceJson = (resource)=>{
25
25
  ...remainder,
26
26
  __readonly: filteredReadonlyFields
27
27
  };
28
+ // Append the $schema property to the resource JSON if it is provided.
29
+ if ($schema) {
30
+ Object.assign(resourceJson, {
31
+ $schema
32
+ });
33
+ }
28
34
  // Strip out all schema annotations, so not to expose them to end users.
29
35
  return (0, _objectisomorphic.omitDeep)(resourceJson, [
30
36
  "__annotation"
@@ -250,8 +250,9 @@ const recursivelyBuildWorkflowDirBundle = (bundle, steps, localWorkflowStepsByRe
250
250
  }
251
251
  }
252
252
  };
253
- const buildWorkflowDirBundle = (remoteWorkflow, localWorkflow = {})=>{
253
+ const buildWorkflowDirBundle = (remoteWorkflow, localWorkflow, $schema)=>{
254
254
  const bundle = {};
255
+ localWorkflow = localWorkflow || {};
255
256
  const mutWorkflow = (0, _lodash.cloneDeep)(remoteWorkflow);
256
257
  const localWorkflowStepsByRef = keyLocalWorkflowStepsByRef(localWorkflow.steps);
257
258
  // Recursively traverse the workflow step tree, mutating it and the bundle
@@ -260,5 +261,5 @@ const buildWorkflowDirBundle = (remoteWorkflow, localWorkflow = {})=>{
260
261
  // Then, prepare the workflow data to be written into a workflow json file.
261
262
  return (0, _lodash.set)(bundle, [
262
263
  WORKFLOW_JSON
263
- ], (0, _helpersisomorphic.prepareResourceJson)(mutWorkflow));
264
+ ], (0, _helpersisomorphic.prepareResourceJson)(mutWorkflow, $schema));
264
265
  };