@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,6 +17,7 @@ const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
17
17
  const _lodash = require("lodash");
18
18
  const _error = require("../../helpers/error");
19
19
  const _json = require("../../helpers/json");
20
+ const _liquid = require("../../helpers/liquid");
20
21
  const _object = require("../../helpers/object");
21
22
  const _helpers = require("./helpers");
22
23
  const _types = require("./types");
@@ -74,25 +75,29 @@ const validateTemplateFilePathFormat = (relpath, workflowDirCtx)=>{
74
75
  };
75
76
  const readTemplateFile = async (relpath, workflowDirCtx)=>{
76
77
  const abspath = _nodePath.resolve(workflowDirCtx.abspath, relpath);
77
- switch(true){
78
- case abspath.toLowerCase().endsWith(".json"):
79
- {
80
- const [obj, errors] = await (0, _json.readJson)(abspath);
81
- const content = obj && JSON.stringify(obj);
82
- return [
83
- content,
84
- errors
85
- ];
86
- }
87
- default:
88
- {
89
- const content = await _fsExtra.readFile(abspath, "utf8");
90
- return [
91
- content,
92
- []
93
- ];
94
- }
95
- }
78
+ // First read all template files as text content and check for valid liquid
79
+ // syntax given it is supported across all message templates and formats.
80
+ const content = await _fsExtra.readFile(abspath, "utf8");
81
+ const liquidError = (0, _liquid.validateLiquidSyntax)(content);
82
+ if (liquidError) return [
83
+ undefined,
84
+ [
85
+ liquidError
86
+ ]
87
+ ];
88
+ // If not a json file, we can just return the file content.
89
+ const isJsonFile = abspath.toLowerCase().endsWith(".json");
90
+ if (!isJsonFile) return [
91
+ content,
92
+ []
93
+ ];
94
+ // If json, parse it as json and validate it as such.
95
+ const [obj, errors] = await (0, _json.readJson)(abspath);
96
+ const json = obj && JSON.stringify(obj);
97
+ return [
98
+ json,
99
+ errors
100
+ ];
96
101
  };
97
102
  /*
98
103
  * Validates that a given value is a valid template file path and the file
@@ -100,7 +105,7 @@ const readTemplateFile = async (relpath, workflowDirCtx)=>{
100
105
  */ const maybeReadTemplateFile = async (val, workflowDirCtx, extractedFilePaths, pathToFieldStr)=>{
101
106
  // Validate the file path format, and that it is unique per workflow.
102
107
  if (!validateTemplateFilePathFormat(val, workflowDirCtx) || typeof val !== "string" || val in extractedFilePaths) {
103
- const error = new _error.JsonError("must be a relative path string to a unique file within the directory", pathToFieldStr);
108
+ const error = new _error.JsonDataError("must be a relative path string to a unique file within the directory", pathToFieldStr);
104
109
  return [
105
110
  undefined,
106
111
  error
@@ -112,7 +117,7 @@ const readTemplateFile = async (relpath, workflowDirCtx)=>{
112
117
  // Check a file actually exists at the given file path.
113
118
  const exists = await validateTemplateFileExists(val, workflowDirCtx);
114
119
  if (!exists) {
115
- const error = new _error.JsonError("must be a relative path string to a file that exists", pathToFieldStr);
120
+ const error = new _error.JsonDataError("must be a relative path string to a file that exists", pathToFieldStr);
116
121
  return [
117
122
  undefined,
118
123
  error
@@ -122,7 +127,9 @@ const readTemplateFile = async (relpath, workflowDirCtx)=>{
122
127
  // under the same field name but without the @ filepath marker.
123
128
  const [content, contentErrors] = await readTemplateFile(val, workflowDirCtx);
124
129
  if (contentErrors.length > 0) {
125
- const error = new _error.JsonError(`points to a file with invalid content (${val})\n\n` + (0, _error.formatErrors)(contentErrors), pathToFieldStr);
130
+ const error = new _error.JsonDataError(`points to a file with invalid content (${val})\n\n` + (0, _error.formatErrors)(contentErrors, {
131
+ indentBy: 2
132
+ }), pathToFieldStr);
126
133
  return [
127
134
  undefined,
128
135
  error
@@ -145,7 +152,7 @@ const compileTemplateFiles = async (workflowDirCtx, workflowJson)=>{
145
152
  ];
146
153
  }
147
154
  if (!Array.isArray(workflowJson.steps)) {
148
- errors.push(new _error.JsonError("must be an array of workflow steps", objPath.to("steps").str));
155
+ errors.push(new _error.JsonDataError("must be an array of workflow steps", objPath.to("steps").str));
149
156
  return [
150
157
  workflowJson,
151
158
  errors
@@ -157,11 +164,11 @@ const compileTemplateFiles = async (workflowDirCtx, workflowJson)=>{
157
164
  for (const [stepIdx, step] of steps.entries()){
158
165
  objPath.reset(pathToSteps).push(stepIdx);
159
166
  if (!(0, _lodash.isPlainObject)(step)) {
160
- errors.push(new _error.JsonError("must be a workflow step object", objPath.str));
167
+ errors.push(new _error.JsonDataError("must be a workflow step object", objPath.str));
161
168
  continue;
162
169
  }
163
170
  if (step.type === undefined) {
164
- errors.push(new _error.JsonError("must have a `type` field", objPath.str));
171
+ errors.push(new _error.JsonDataError("must have a `type` field", objPath.str));
165
172
  continue;
166
173
  }
167
174
  // Not a channel step, nothing more to do.
@@ -169,11 +176,11 @@ const compileTemplateFiles = async (workflowDirCtx, workflowJson)=>{
169
176
  continue;
170
177
  }
171
178
  if (step.template === undefined) {
172
- errors.push(new _error.JsonError("must have a `template` field containing a template object", objPath.str));
179
+ errors.push(new _error.JsonDataError("must have a `template` field containing a template object", objPath.str));
173
180
  continue;
174
181
  }
175
182
  if (!(0, _lodash.isPlainObject)(step.template)) {
176
- errors.push(new _error.JsonError("must be a template object", objPath.to("template").str));
183
+ errors.push(new _error.JsonDataError("must be a template object", objPath.to("template").str));
177
184
  continue;
178
185
  }
179
186
  // 3. For a given template, look for any extracted template content, read
@@ -207,7 +214,6 @@ const compileTemplateFiles = async (workflowDirCtx, workflowJson)=>{
207
214
  (0, _lodash.set)(workflowJson, inlinePathStr, content);
208
215
  }
209
216
  }
210
- // TODO: Consider validating content for liquid syntax too maybe?
211
217
  return [
212
218
  workflowJson,
213
219
  errors
@@ -9,14 +9,15 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
- writeWorkflowDir: ()=>writeWorkflowDir,
12
+ newTemplateFilePath: ()=>newTemplateFilePath,
13
+ writeWorkflowDirFromData: ()=>writeWorkflowDirFromData,
14
+ writeWorkflowDirFromBundle: ()=>writeWorkflowDirFromBundle,
13
15
  buildWorkflowDirBundle: ()=>buildWorkflowDirBundle,
14
16
  toWorkflowJson: ()=>toWorkflowJson
15
17
  });
16
18
  const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
17
19
  const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
18
20
  const _lodash = require("lodash");
19
- const _env = require("../../helpers/env");
20
21
  const _json = require("../../helpers/json");
21
22
  const _object = require("../../helpers/object");
22
23
  const _helpers = require("./helpers");
@@ -61,14 +62,7 @@ function _interopRequireWildcard(obj, nodeInterop) {
61
62
  }
62
63
  return newObj;
63
64
  }
64
- /*
65
- * For a given workflow step and a template field, return the template file path
66
- * we can extract out the content to.
67
- *
68
- * Note, this is a default "recommended" convention but the template file can
69
- * be located at any arbitrary path (as long as it is a relative path that is
70
- * inside the workflow directory and unique to the field)
71
- */ const newTemplateFilePath = (stepRef, fileName, fileExt)=>_nodePath.join(stepRef, `${fileName}.${fileExt}`).toLowerCase();
65
+ const newTemplateFilePath = (stepRef, fileName, fileExt)=>_nodePath.join(stepRef, `${fileName}.${fileExt}`).toLowerCase();
72
66
  /*
73
67
  * For a given workflow step and a template field, return the path of object
74
68
  * which we can use to check whether the field has been extracted (hence, with
@@ -151,7 +145,12 @@ function _interopRequireWildcard(obj, nodeInterop) {
151
145
  ...settingsExtractableFields
152
146
  };
153
147
  };
154
- const buildWorkflowDirBundle = (workflowDirCtx, remoteWorkflow, localWorkflow = {})=>{
148
+ /*
149
+ * Parse a given workflow payload, and extract out any template contents where
150
+ * necessary and mutate the workflow data accordingly so we end up with a
151
+ * mapping of file contents by its relative path (aka workflow dir bundle) that
152
+ * can be written into a file system as individual files.
153
+ */ const buildWorkflowDirBundle = (workflowDirCtx, remoteWorkflow, localWorkflow = {})=>{
155
154
  const bundle = {};
156
155
  const mutWorkflow = (0, _lodash.cloneDeep)(remoteWorkflow);
157
156
  const localWorkflowStepsByRef = (0, _lodash.keyBy)(localWorkflow.steps || [], "ref");
@@ -190,25 +189,30 @@ const buildWorkflowDirBundle = (workflowDirCtx, remoteWorkflow, localWorkflow =
190
189
  _helpers.WORKFLOW_JSON
191
190
  ], toWorkflowJson(mutWorkflow));
192
191
  };
193
- const writeWorkflowDir = async (remoteWorkflow, workflowDirCtx)=>{
192
+ const writeWorkflowDirFromData = async (workflowDirCtx, remoteWorkflow)=>{
194
193
  // If the workflow directory exists on the file system (i.e. previously
195
194
  // pulled before), then read the workflow file to use as a reference.
196
- const [localWorkflow] = workflowDirCtx.exists ? await (0, _reader.readWorkflowDir)(workflowDirCtx) : [];
195
+ // Note, we do not need to compile or validate template files for this.
196
+ const [localWorkflow] = workflowDirCtx.exists ? await (0, _reader.readWorkflowDir)(workflowDirCtx, {
197
+ withTemplateFiles: false
198
+ }) : [];
197
199
  const bundle = buildWorkflowDirBundle(workflowDirCtx, remoteWorkflow, localWorkflow);
198
- const workflowDirPath = _env.isTestEnv ? _nodePath.join(_env.sandboxDir, remoteWorkflow.key) : workflowDirCtx.abspath;
200
+ return writeWorkflowDirFromBundle(workflowDirCtx, bundle);
201
+ };
202
+ const writeWorkflowDirFromBundle = async (workflowDirCtx, workflowDirBundle)=>{
199
203
  try {
200
204
  // TODO(KNO-2794): Should rather clean up any orphaned template files
201
205
  // individually after successfully writing the workflow directory.
202
- await _fsExtra.remove(workflowDirPath);
203
- const promises = Object.entries(bundle).map(([relpath, fileContent])=>{
204
- const filePath = _nodePath.join(workflowDirPath, relpath);
206
+ await _fsExtra.remove(workflowDirCtx.abspath);
207
+ const promises = Object.entries(workflowDirBundle).map(([relpath, fileContent])=>{
208
+ const filePath = _nodePath.resolve(workflowDirCtx.abspath, relpath);
205
209
  return relpath === _helpers.WORKFLOW_JSON ? _fsExtra.outputJson(filePath, fileContent, {
206
210
  spaces: _json.DOUBLE_SPACES
207
211
  }) : _fsExtra.outputFile(filePath, fileContent);
208
212
  });
209
213
  await Promise.all(promises);
210
214
  } catch (error) {
211
- await _fsExtra.remove(workflowDirPath);
215
+ await _fsExtra.remove(workflowDirCtx.abspath);
212
216
  throw error;
213
217
  }
214
218
  };
@@ -11,7 +11,7 @@ Object.defineProperty(exports, "default", {
11
11
  const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
12
12
  const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
13
13
  const _yup = /*#__PURE__*/ _interopRequireWildcard(require("yup"));
14
- const _env = require("./helpers/env");
14
+ const _const = require("./helpers/const");
15
15
  function _getRequireWildcardCache(nodeInterop) {
16
16
  if (typeof WeakMap !== "function") return null;
17
17
  var cacheBabelInterop = new WeakMap();
@@ -58,8 +58,8 @@ const userConfigSchema = _yup.object({
58
58
  let USER_CONFIG;
59
59
  const maybeReadJsonConfig = async (configDir)=>{
60
60
  // Don't use a user config file in tests.
61
- if (_env.isTestEnv) return null;
62
- const pathToJsonConfig = _nodePath.join(configDir, "config.json");
61
+ if (_const.isTestEnv) return null;
62
+ const pathToJsonConfig = _nodePath.resolve(configDir, "config.json");
63
63
  const exists = await _fsExtra.pathExists(pathToJsonConfig);
64
64
  if (!exists) return null;
65
65
  return _fsExtra.readJSON(pathToJsonConfig);
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0-rc.0",
2
+ "version": "0.1.0-rc.1",
3
3
  "commands": {
4
4
  "ping": {
5
5
  "id": "ping",
@@ -16,7 +16,7 @@
16
16
  "service-token": {
17
17
  "name": "service-token",
18
18
  "type": "option",
19
- "summary": "service token to authenticate with",
19
+ "summary": "The service token to authenticate with",
20
20
  "required": true,
21
21
  "multiple": false
22
22
  },
@@ -30,6 +30,141 @@
30
30
  },
31
31
  "args": {}
32
32
  },
33
+ "commit": {
34
+ "id": "commit",
35
+ "strict": true,
36
+ "pluginName": "@knocklabs/cli",
37
+ "pluginAlias": "@knocklabs/cli",
38
+ "pluginType": "core",
39
+ "aliases": [],
40
+ "flags": {
41
+ "service-token": {
42
+ "name": "service-token",
43
+ "type": "option",
44
+ "summary": "The service token to authenticate with",
45
+ "required": true,
46
+ "multiple": false
47
+ },
48
+ "api-origin": {
49
+ "name": "api-origin",
50
+ "type": "option",
51
+ "hidden": true,
52
+ "required": false,
53
+ "multiple": false
54
+ },
55
+ "environment": {
56
+ "name": "environment",
57
+ "type": "option",
58
+ "summary": "Committing changes applies to the development environment only, use `commit promote` to promote changes to a later environment",
59
+ "multiple": false,
60
+ "options": [
61
+ "development"
62
+ ],
63
+ "default": "development"
64
+ },
65
+ "commit-message": {
66
+ "name": "commit-message",
67
+ "type": "option",
68
+ "char": "m",
69
+ "summary": "Use the given value as the commit message",
70
+ "multiple": false
71
+ },
72
+ "force": {
73
+ "name": "force",
74
+ "type": "boolean",
75
+ "allowNo": false
76
+ }
77
+ },
78
+ "args": {}
79
+ },
80
+ "commit:promote": {
81
+ "id": "commit:promote",
82
+ "strict": true,
83
+ "pluginName": "@knocklabs/cli",
84
+ "pluginAlias": "@knocklabs/cli",
85
+ "pluginType": "core",
86
+ "aliases": [],
87
+ "flags": {
88
+ "service-token": {
89
+ "name": "service-token",
90
+ "type": "option",
91
+ "summary": "The service token to authenticate with",
92
+ "required": true,
93
+ "multiple": false
94
+ },
95
+ "api-origin": {
96
+ "name": "api-origin",
97
+ "type": "option",
98
+ "hidden": true,
99
+ "required": false,
100
+ "multiple": false
101
+ },
102
+ "to": {
103
+ "name": "to",
104
+ "type": "option",
105
+ "summary": "The destination environment to promote changes from the preceding environment",
106
+ "required": true,
107
+ "multiple": false
108
+ },
109
+ "force": {
110
+ "name": "force",
111
+ "type": "boolean",
112
+ "allowNo": false
113
+ }
114
+ },
115
+ "args": {}
116
+ },
117
+ "workflow:activate": {
118
+ "id": "workflow:activate",
119
+ "strict": true,
120
+ "pluginName": "@knocklabs/cli",
121
+ "pluginAlias": "@knocklabs/cli",
122
+ "pluginType": "core",
123
+ "aliases": [],
124
+ "flags": {
125
+ "service-token": {
126
+ "name": "service-token",
127
+ "type": "option",
128
+ "summary": "The service token to authenticate with",
129
+ "required": true,
130
+ "multiple": false
131
+ },
132
+ "api-origin": {
133
+ "name": "api-origin",
134
+ "type": "option",
135
+ "hidden": true,
136
+ "required": false,
137
+ "multiple": false
138
+ },
139
+ "environment": {
140
+ "name": "environment",
141
+ "type": "option",
142
+ "required": true,
143
+ "multiple": false
144
+ },
145
+ "status": {
146
+ "name": "status",
147
+ "type": "option",
148
+ "multiple": false,
149
+ "options": [
150
+ "true",
151
+ "false"
152
+ ],
153
+ "default": true
154
+ },
155
+ "force": {
156
+ "name": "force",
157
+ "type": "boolean",
158
+ "allowNo": false
159
+ }
160
+ },
161
+ "args": {
162
+ "workflowKey": {
163
+ "name": "workflowKey",
164
+ "required": true
165
+ }
166
+ }
167
+ },
33
168
  "workflow:get": {
34
169
  "id": "workflow:get",
35
170
  "strict": true,
@@ -41,7 +176,7 @@
41
176
  "service-token": {
42
177
  "name": "service-token",
43
178
  "type": "option",
44
- "summary": "service token to authenticate with",
179
+ "summary": "The service token to authenticate with",
45
180
  "required": true,
46
181
  "multiple": false
47
182
  },
@@ -89,7 +224,7 @@
89
224
  "service-token": {
90
225
  "name": "service-token",
91
226
  "type": "option",
92
- "summary": "service token to authenticate with",
227
+ "summary": "The service token to authenticate with",
93
228
  "required": true,
94
229
  "multiple": false
95
230
  },
@@ -136,6 +271,50 @@
136
271
  },
137
272
  "args": {}
138
273
  },
274
+ "workflow:new": {
275
+ "id": "workflow:new",
276
+ "strict": true,
277
+ "pluginName": "@knocklabs/cli",
278
+ "pluginAlias": "@knocklabs/cli",
279
+ "pluginType": "core",
280
+ "hidden": true,
281
+ "aliases": [],
282
+ "flags": {
283
+ "service-token": {
284
+ "name": "service-token",
285
+ "type": "option",
286
+ "summary": "The service token to authenticate with",
287
+ "required": true,
288
+ "multiple": false
289
+ },
290
+ "api-origin": {
291
+ "name": "api-origin",
292
+ "type": "option",
293
+ "hidden": true,
294
+ "required": false,
295
+ "multiple": false
296
+ },
297
+ "steps": {
298
+ "name": "steps",
299
+ "type": "option",
300
+ "multiple": false,
301
+ "aliases": [
302
+ "step"
303
+ ]
304
+ },
305
+ "force": {
306
+ "name": "force",
307
+ "type": "boolean",
308
+ "allowNo": false
309
+ }
310
+ },
311
+ "args": {
312
+ "workflowKey": {
313
+ "name": "workflowKey",
314
+ "required": true
315
+ }
316
+ }
317
+ },
139
318
  "workflow:pull": {
140
319
  "id": "workflow:pull",
141
320
  "strict": true,
@@ -147,7 +326,7 @@
147
326
  "service-token": {
148
327
  "name": "service-token",
149
328
  "type": "option",
150
- "summary": "service token to authenticate with",
329
+ "summary": "The service token to authenticate with",
151
330
  "required": true,
152
331
  "multiple": false
153
332
  },
@@ -188,7 +367,63 @@
188
367
  "service-token": {
189
368
  "name": "service-token",
190
369
  "type": "option",
191
- "summary": "service token to authenticate with",
370
+ "summary": "The service token to authenticate with",
371
+ "required": true,
372
+ "multiple": false
373
+ },
374
+ "api-origin": {
375
+ "name": "api-origin",
376
+ "type": "option",
377
+ "hidden": true,
378
+ "required": false,
379
+ "multiple": false
380
+ },
381
+ "environment": {
382
+ "name": "environment",
383
+ "type": "option",
384
+ "summary": "Pushing a workflow is only allowed in the development environment",
385
+ "multiple": false,
386
+ "options": [
387
+ "development"
388
+ ],
389
+ "default": "development"
390
+ },
391
+ "commit": {
392
+ "name": "commit",
393
+ "type": "boolean",
394
+ "summary": "Push and commit the workflow(s) at the same time",
395
+ "allowNo": false
396
+ },
397
+ "commit-message": {
398
+ "name": "commit-message",
399
+ "type": "option",
400
+ "char": "m",
401
+ "summary": "Use the given value as the commit message",
402
+ "multiple": false,
403
+ "dependsOn": [
404
+ "commit"
405
+ ]
406
+ }
407
+ },
408
+ "args": {
409
+ "workflowKey": {
410
+ "name": "workflowKey",
411
+ "required": false
412
+ }
413
+ }
414
+ },
415
+ "workflow:validate": {
416
+ "id": "workflow:validate",
417
+ "strict": true,
418
+ "pluginName": "@knocklabs/cli",
419
+ "pluginAlias": "@knocklabs/cli",
420
+ "pluginType": "core",
421
+ "aliases": [],
422
+ "flags": {
423
+ "service-token": {
424
+ "name": "service-token",
425
+ "type": "option",
426
+ "summary": "The service token to authenticate with",
192
427
  "required": true,
193
428
  "multiple": false
194
429
  },
@@ -202,6 +437,7 @@
202
437
  "environment": {
203
438
  "name": "environment",
204
439
  "type": "option",
440
+ "summary": "Validating a workflow is only done in the development environment",
205
441
  "multiple": false,
206
442
  "options": [
207
443
  "development"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knocklabs/cli",
3
- "version": "0.1.0-rc.0",
3
+ "version": "0.1.0-rc.1",
4
4
  "description": "Knock CLI",
5
5
  "author": "@knocklabs",
6
6
  "bin": {
@@ -17,26 +17,27 @@
17
17
  "/oclif.manifest.json"
18
18
  ],
19
19
  "dependencies": {
20
- "@oclif/core": "^1.26.1",
20
+ "@oclif/core": "^1.26.2",
21
21
  "@oclif/plugin-help": "^5",
22
- "@oclif/plugin-plugins": "^2.3.0",
22
+ "@oclif/plugin-plugins": "^2.3.2",
23
23
  "@prantlf/jsonlint": "^11.7.0",
24
- "axios": "^1.3.2",
24
+ "axios": "^1.3.4",
25
25
  "date-fns": "^2.29.3",
26
26
  "enquirer": "^2.3.6",
27
27
  "fs-extra": "^10.1.0",
28
+ "liquidjs": "^10.5.0",
28
29
  "lodash": "^4.17.21",
29
30
  "yup": "^0.32.11"
30
31
  },
31
32
  "devDependencies": {
32
- "@oclif/test": "^2.3.0",
33
- "@swc/cli": "^0.1.61",
34
- "@swc/core": "^1.3.29",
33
+ "@oclif/test": "^2.3.6",
34
+ "@swc/cli": "^0.1.62",
35
+ "@swc/core": "^1.3.36",
35
36
  "@swc/helpers": "^0.4.14",
36
37
  "@types/chai": "^4",
37
38
  "@types/fs-extra": "^9.0.13",
38
39
  "@types/mocha": "^9.0.0",
39
- "@types/node": "^16.18.11",
40
+ "@types/node": "^16.18.12",
40
41
  "chai": "^4",
41
42
  "eslint": "^7.32.0",
42
43
  "eslint-config-oclif": "^4",
@@ -47,13 +48,13 @@
47
48
  "mocha": "^9",
48
49
  "nock": "^13.3.0",
49
50
  "oclif": "^3",
50
- "prettier": "2.8.3",
51
+ "prettier": "2.8.4",
51
52
  "shx": "^0.3.4",
52
53
  "sinon": "^15.0.1",
53
54
  "ts-node": "^10.9.1",
54
55
  "tsconfig-paths": "^4.1.2",
55
56
  "tslib": "^2.5.0",
56
- "typescript": "^4.9.4"
57
+ "typescript": "^4.9.5"
57
58
  },
58
59
  "oclif": {
59
60
  "bin": "knock",