skuba 4.4.1 → 5.0.0-beta.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 (313) hide show
  1. package/config/tsconfig.json +1 -0
  2. package/jest/moduleNameMapper.js +50 -78
  3. package/jest/moduleNameMapper.test.ts +4 -14
  4. package/jest/transform.js +10 -1
  5. package/jest/tsConfig.js +26 -0
  6. package/lib/api/buildkite/annotate.js +46 -33
  7. package/lib/api/buildkite/annotate.js.map +7 -1
  8. package/lib/api/buildkite/index.js +31 -7
  9. package/lib/api/buildkite/index.js.map +7 -1
  10. package/lib/api/buildkite/md.js +31 -8
  11. package/lib/api/buildkite/md.js.map +7 -1
  12. package/lib/api/git/commit.js +45 -17
  13. package/lib/api/git/commit.js.map +7 -1
  14. package/lib/api/git/commitAllChanges.js +57 -27
  15. package/lib/api/git/commitAllChanges.js.map +7 -1
  16. package/lib/api/git/currentBranch.js +50 -28
  17. package/lib/api/git/currentBranch.js.map +7 -1
  18. package/lib/api/git/getChangedFiles.js +49 -27
  19. package/lib/api/git/getChangedFiles.js.map +7 -1
  20. package/lib/api/git/index.js +54 -22
  21. package/lib/api/git/index.js.map +7 -1
  22. package/lib/api/git/log.js +63 -42
  23. package/lib/api/git/log.js.map +7 -1
  24. package/lib/api/git/pull.js +62 -31
  25. package/lib/api/git/pull.js.map +7 -1
  26. package/lib/api/git/push.js +63 -31
  27. package/lib/api/git/push.js.map +7 -1
  28. package/lib/api/git/remote.js +59 -56
  29. package/lib/api/git/remote.js.map +7 -1
  30. package/lib/api/git/reset.js +55 -27
  31. package/lib/api/git/reset.js.map +7 -1
  32. package/lib/api/git/statusMatrix.js +46 -13
  33. package/lib/api/git/statusMatrix.js.map +7 -1
  34. package/lib/api/github/checkRun.js +70 -79
  35. package/lib/api/github/checkRun.js.map +7 -1
  36. package/lib/api/github/environment.js +40 -33
  37. package/lib/api/github/environment.js.map +7 -1
  38. package/lib/api/github/index.js +47 -17
  39. package/lib/api/github/index.js.map +7 -1
  40. package/lib/api/github/issueComment.js +73 -81
  41. package/lib/api/github/issueComment.js.map +7 -1
  42. package/lib/api/github/pullRequest.js +60 -61
  43. package/lib/api/github/pullRequest.js.map +7 -1
  44. package/lib/api/github/push.js +138 -133
  45. package/lib/api/github/push.js.map +7 -1
  46. package/lib/api/jest/index.d.ts +2 -1
  47. package/lib/api/jest/index.js +35 -14
  48. package/lib/api/jest/index.js.map +7 -1
  49. package/lib/api/net/compose.js +45 -17
  50. package/lib/api/net/compose.js.map +7 -1
  51. package/lib/api/net/index.js +28 -5
  52. package/lib/api/net/index.js.map +7 -1
  53. package/lib/api/net/socket.js +58 -36
  54. package/lib/api/net/socket.js.map +7 -1
  55. package/lib/api/net/waitFor.js +38 -18
  56. package/lib/api/net/waitFor.js.map +7 -1
  57. package/lib/cli/adapter/eslint.js +95 -72
  58. package/lib/cli/adapter/eslint.js.map +7 -1
  59. package/lib/cli/adapter/prettier.js +126 -124
  60. package/lib/cli/adapter/prettier.js.map +7 -1
  61. package/lib/cli/build/args.d.ts +7 -0
  62. package/lib/cli/build/args.js +69 -0
  63. package/lib/cli/build/args.js.map +7 -0
  64. package/lib/cli/build/esbuild.d.ts +5 -0
  65. package/lib/cli/build/esbuild.js +128 -0
  66. package/lib/cli/build/esbuild.js.map +7 -0
  67. package/lib/cli/build/index.d.ts +1 -1
  68. package/lib/cli/build/index.js +68 -5
  69. package/lib/cli/build/index.js.map +7 -1
  70. package/lib/cli/build/tsc.d.ts +1 -1
  71. package/lib/cli/build/tsc.js +34 -23
  72. package/lib/cli/build/tsc.js.map +7 -1
  73. package/lib/cli/buildPackage.js +53 -23
  74. package/lib/cli/buildPackage.js.map +7 -1
  75. package/lib/cli/configure/addEmptyExports.d.ts +5 -0
  76. package/lib/cli/configure/addEmptyExports.js +67 -0
  77. package/lib/cli/configure/addEmptyExports.js.map +7 -0
  78. package/lib/cli/configure/analyseConfiguration.js +61 -31
  79. package/lib/cli/configure/analyseConfiguration.js.map +7 -1
  80. package/lib/cli/configure/analyseDependencies.js +122 -113
  81. package/lib/cli/configure/analyseDependencies.js.map +7 -1
  82. package/lib/cli/configure/analysis/diff.js +37 -11
  83. package/lib/cli/configure/analysis/diff.js.map +7 -1
  84. package/lib/cli/configure/analysis/files.js +49 -22
  85. package/lib/cli/configure/analysis/files.js.map +7 -1
  86. package/lib/cli/configure/analysis/git.js +46 -16
  87. package/lib/cli/configure/analysis/git.js.map +7 -1
  88. package/lib/cli/configure/analysis/package.js +99 -52
  89. package/lib/cli/configure/analysis/package.js.map +7 -1
  90. package/lib/cli/configure/analysis/project.js +90 -54
  91. package/lib/cli/configure/analysis/project.js.map +7 -1
  92. package/lib/cli/configure/dependencies/index.js +40 -13
  93. package/lib/cli/configure/dependencies/index.js.map +7 -1
  94. package/lib/cli/configure/dependencies/seekDatadogCustomMetrics.js +57 -32
  95. package/lib/cli/configure/dependencies/seekDatadogCustomMetrics.js.map +7 -1
  96. package/lib/cli/configure/dependencies/seekKoala.js +53 -31
  97. package/lib/cli/configure/dependencies/seekKoala.js.map +7 -1
  98. package/lib/cli/configure/dependencies/skuba.js +49 -27
  99. package/lib/cli/configure/dependencies/skuba.js.map +7 -1
  100. package/lib/cli/configure/dependencies/skubaDeps.js +49 -28
  101. package/lib/cli/configure/dependencies/skubaDeps.js.map +7 -1
  102. package/lib/cli/configure/dependencies/skubaDive.js +65 -38
  103. package/lib/cli/configure/dependencies/skubaDive.js.map +7 -1
  104. package/lib/cli/configure/ensureTemplateCompletion.js +69 -41
  105. package/lib/cli/configure/ensureTemplateCompletion.js.map +7 -1
  106. package/lib/cli/configure/getEntryPoint.js +62 -32
  107. package/lib/cli/configure/getEntryPoint.js.map +7 -1
  108. package/lib/cli/configure/getProjectType.js +50 -28
  109. package/lib/cli/configure/getProjectType.js.map +7 -1
  110. package/lib/cli/configure/index.js +134 -109
  111. package/lib/cli/configure/index.js.map +7 -1
  112. package/lib/cli/configure/modules/eslint.js +57 -26
  113. package/lib/cli/configure/modules/eslint.js.map +7 -1
  114. package/lib/cli/configure/modules/ignore.js +37 -14
  115. package/lib/cli/configure/modules/ignore.js.map +7 -1
  116. package/lib/cli/configure/modules/index.js +53 -28
  117. package/lib/cli/configure/modules/index.js.map +7 -1
  118. package/lib/cli/configure/modules/jest.js +84 -47
  119. package/lib/cli/configure/modules/jest.js.map +7 -1
  120. package/lib/cli/configure/modules/nodemon.js +29 -6
  121. package/lib/cli/configure/modules/nodemon.js.map +7 -1
  122. package/lib/cli/configure/modules/package.js +113 -92
  123. package/lib/cli/configure/modules/package.js.map +7 -1
  124. package/lib/cli/configure/modules/prettier.js +48 -19
  125. package/lib/cli/configure/modules/prettier.js.map +7 -1
  126. package/lib/cli/configure/modules/renovate.js +52 -39
  127. package/lib/cli/configure/modules/renovate.js.map +7 -1
  128. package/lib/cli/configure/modules/serverless.js +33 -15
  129. package/lib/cli/configure/modules/serverless.js.map +7 -1
  130. package/lib/cli/configure/modules/skubaDive.js +63 -37
  131. package/lib/cli/configure/modules/skubaDive.js.map +7 -1
  132. package/lib/cli/configure/modules/tsconfig.js +79 -63
  133. package/lib/cli/configure/modules/tsconfig.js.map +7 -1
  134. package/lib/cli/configure/modules/tslint.js +29 -6
  135. package/lib/cli/configure/modules/tslint.js.map +7 -1
  136. package/lib/cli/configure/processing/deleteFiles.js +30 -8
  137. package/lib/cli/configure/processing/deleteFiles.js.map +7 -1
  138. package/lib/cli/configure/processing/ignoreFile.js +65 -59
  139. package/lib/cli/configure/processing/ignoreFile.js.map +7 -1
  140. package/lib/cli/configure/processing/javascript.js +35 -15
  141. package/lib/cli/configure/processing/javascript.js.map +7 -1
  142. package/lib/cli/configure/processing/json.js +51 -20
  143. package/lib/cli/configure/processing/json.js.map +7 -1
  144. package/lib/cli/configure/processing/loadFiles.js +30 -8
  145. package/lib/cli/configure/processing/loadFiles.js.map +7 -1
  146. package/lib/cli/configure/processing/module.js +37 -14
  147. package/lib/cli/configure/processing/module.js.map +7 -1
  148. package/lib/cli/configure/processing/package.js +73 -43
  149. package/lib/cli/configure/processing/package.js.map +7 -1
  150. package/lib/cli/configure/processing/prettier.js +37 -11
  151. package/lib/cli/configure/processing/prettier.js.map +7 -1
  152. package/lib/cli/configure/processing/record.js +54 -31
  153. package/lib/cli/configure/processing/record.js.map +7 -1
  154. package/lib/cli/configure/processing/typescript.js +176 -158
  155. package/lib/cli/configure/processing/typescript.js.map +7 -1
  156. package/lib/cli/configure/refreshIgnoreFiles.js +64 -40
  157. package/lib/cli/configure/refreshIgnoreFiles.js.map +7 -1
  158. package/lib/cli/configure/types.js +16 -2
  159. package/lib/cli/configure/types.js.map +7 -1
  160. package/lib/cli/format.js +58 -31
  161. package/lib/cli/format.js.map +7 -1
  162. package/lib/cli/help.js +31 -8
  163. package/lib/cli/help.js.map +7 -1
  164. package/lib/cli/init/getConfig.js +238 -207
  165. package/lib/cli/init/getConfig.js.map +7 -1
  166. package/lib/cli/init/git.js +70 -63
  167. package/lib/cli/init/git.js.map +7 -1
  168. package/lib/cli/init/index.js +133 -100
  169. package/lib/cli/init/index.js.map +7 -1
  170. package/lib/cli/init/prompts.js +84 -54
  171. package/lib/cli/init/prompts.js.map +7 -1
  172. package/lib/cli/init/types.js +52 -52
  173. package/lib/cli/init/types.js.map +7 -1
  174. package/lib/cli/init/validation.js +34 -12
  175. package/lib/cli/init/validation.js.map +7 -1
  176. package/lib/cli/init/writePackageJson.js +51 -22
  177. package/lib/cli/init/writePackageJson.js.map +7 -1
  178. package/lib/cli/lint/annotate/buildkite/eslint.js +34 -28
  179. package/lib/cli/lint/annotate/buildkite/eslint.js.map +7 -1
  180. package/lib/cli/lint/annotate/buildkite/index.js +50 -44
  181. package/lib/cli/lint/annotate/buildkite/index.js.map +7 -1
  182. package/lib/cli/lint/annotate/buildkite/prettier.js +41 -35
  183. package/lib/cli/lint/annotate/buildkite/prettier.js.map +7 -1
  184. package/lib/cli/lint/annotate/buildkite/tsc.js +39 -39
  185. package/lib/cli/lint/annotate/buildkite/tsc.js.map +7 -1
  186. package/lib/cli/lint/annotate/github/eslint.js +40 -16
  187. package/lib/cli/lint/annotate/github/eslint.js.map +7 -1
  188. package/lib/cli/lint/annotate/github/index.js +55 -51
  189. package/lib/cli/lint/annotate/github/index.js.map +7 -1
  190. package/lib/cli/lint/annotate/github/prettier.js +36 -13
  191. package/lib/cli/lint/annotate/github/prettier.js.map +7 -1
  192. package/lib/cli/lint/annotate/github/tsc.js +52 -52
  193. package/lib/cli/lint/annotate/github/tsc.js.map +7 -1
  194. package/lib/cli/lint/annotate/index.js +33 -10
  195. package/lib/cli/lint/annotate/index.js.map +7 -1
  196. package/lib/cli/lint/autofix.js +122 -117
  197. package/lib/cli/lint/autofix.js.map +7 -1
  198. package/lib/cli/lint/eslint.js +48 -18
  199. package/lib/cli/lint/eslint.js.map +7 -1
  200. package/lib/cli/lint/external.js +102 -81
  201. package/lib/cli/lint/external.js.map +7 -1
  202. package/lib/cli/lint/index.js +42 -18
  203. package/lib/cli/lint/index.js.map +7 -1
  204. package/lib/cli/lint/internal.js +54 -21
  205. package/lib/cli/lint/internal.js.map +7 -1
  206. package/lib/cli/lint/prettier.js +48 -18
  207. package/lib/cli/lint/prettier.js.map +7 -1
  208. package/lib/cli/lint/tsc.js +56 -32
  209. package/lib/cli/lint/tsc.js.map +7 -1
  210. package/lib/cli/lint/types.js +16 -2
  211. package/lib/cli/lint/types.js.map +7 -1
  212. package/lib/cli/node.js +65 -58
  213. package/lib/cli/node.js.map +7 -1
  214. package/lib/cli/release.js +29 -6
  215. package/lib/cli/release.js.map +7 -1
  216. package/lib/cli/start.js +61 -24
  217. package/lib/cli/start.js.map +7 -1
  218. package/lib/cli/test/index.js +35 -11
  219. package/lib/cli/test/index.js.map +7 -1
  220. package/lib/cli/test/reporters/github/annotations.js +89 -92
  221. package/lib/cli/test/reporters/github/annotations.js.map +7 -1
  222. package/lib/cli/test/reporters/github/index.js +68 -68
  223. package/lib/cli/test/reporters/github/index.js.map +7 -1
  224. package/lib/cli/version.js +31 -8
  225. package/lib/cli/version.js.map +7 -1
  226. package/lib/enquirer.d.js +2 -0
  227. package/lib/enquirer.d.js.map +7 -0
  228. package/lib/index.d.ts +6 -0
  229. package/lib/index.js +45 -41
  230. package/lib/index.js.map +7 -1
  231. package/lib/skuba.js +45 -38
  232. package/lib/skuba.js.map +7 -1
  233. package/lib/utils/args.js +97 -96
  234. package/lib/utils/args.js.map +7 -1
  235. package/lib/utils/command.js +64 -33
  236. package/lib/utils/command.js.map +7 -1
  237. package/lib/utils/copy.js +88 -51
  238. package/lib/utils/copy.js.map +7 -1
  239. package/lib/utils/dir.js +79 -67
  240. package/lib/utils/dir.js.map +7 -1
  241. package/lib/utils/env.js +27 -7
  242. package/lib/utils/env.js.map +7 -1
  243. package/lib/utils/error.js +58 -56
  244. package/lib/utils/error.js.map +7 -1
  245. package/lib/utils/exec.js +139 -110
  246. package/lib/utils/exec.js.map +7 -1
  247. package/lib/utils/help.js +31 -8
  248. package/lib/utils/help.js.map +7 -1
  249. package/lib/utils/logging.js +52 -24
  250. package/lib/utils/logging.js.map +7 -1
  251. package/lib/utils/logo.js +65 -27
  252. package/lib/utils/logo.js.map +7 -1
  253. package/lib/utils/manifest.d.ts +1 -0
  254. package/lib/utils/manifest.js +65 -51
  255. package/lib/utils/manifest.js.map +7 -1
  256. package/lib/utils/port.js +38 -17
  257. package/lib/utils/port.js.map +7 -1
  258. package/lib/utils/template.js +106 -87
  259. package/lib/utils/template.js.map +7 -1
  260. package/lib/utils/validation.js +43 -18
  261. package/lib/utils/validation.js.map +7 -1
  262. package/lib/utils/version.js +72 -82
  263. package/lib/utils/version.js.map +7 -1
  264. package/lib/utils/wait.js +52 -25
  265. package/lib/utils/wait.js.map +7 -1
  266. package/lib/utils/worker.js +59 -44
  267. package/lib/utils/worker.js.map +7 -1
  268. package/lib/wrapper/function-arguments.d.js +2 -0
  269. package/lib/wrapper/function-arguments.d.js.map +7 -0
  270. package/lib/wrapper/functionHandler.js +56 -29
  271. package/lib/wrapper/functionHandler.js.map +7 -1
  272. package/lib/wrapper/http.js +66 -56
  273. package/lib/wrapper/http.js.map +7 -1
  274. package/lib/wrapper/index.js +9 -21
  275. package/lib/wrapper/index.js.map +7 -1
  276. package/lib/wrapper/main.js +39 -20
  277. package/lib/wrapper/main.js.map +7 -1
  278. package/lib/wrapper/requestListener.js +50 -35
  279. package/lib/wrapper/requestListener.js.map +7 -1
  280. package/package.json +19 -13
  281. package/template/base/jest.config.ts +0 -6
  282. package/template/base/jest.setup.ts +2 -0
  283. package/template/express-rest-api/.buildkite/pipeline.yml +1 -1
  284. package/template/express-rest-api/package.json +1 -1
  285. package/template/greeter/.buildkite/pipeline.yml +1 -1
  286. package/template/greeter/package.json +1 -1
  287. package/template/greeter/src/app.test.ts +3 -1
  288. package/template/koa-rest-api/.buildkite/pipeline.yml +1 -1
  289. package/template/koa-rest-api/package.json +6 -7
  290. package/template/koa-rest-api/src/api/jobs/postJob.test.ts +3 -7
  291. package/template/koa-rest-api/src/api/jobs/postJob.ts +2 -2
  292. package/template/koa-rest-api/src/framework/validation.test.ts +17 -15
  293. package/template/koa-rest-api/src/framework/validation.ts +50 -9
  294. package/template/koa-rest-api/src/testing/types.ts +5 -10
  295. package/template/koa-rest-api/src/types/jobs.ts +5 -10
  296. package/template/lambda-sqs-worker/.buildkite/pipeline.yml +3 -3
  297. package/template/lambda-sqs-worker/package.json +2 -6
  298. package/template/lambda-sqs-worker/src/app.ts +2 -2
  299. package/template/lambda-sqs-worker/src/framework/validation.test.ts +37 -17
  300. package/template/lambda-sqs-worker/src/framework/validation.ts +4 -2
  301. package/template/lambda-sqs-worker/src/services/jobScorer.ts +2 -2
  302. package/template/lambda-sqs-worker/src/testing/types.ts +5 -10
  303. package/template/lambda-sqs-worker/src/types/jobScorer.ts +9 -16
  304. package/template/lambda-sqs-worker/src/types/pipelineEvents.ts +13 -20
  305. package/template/lambda-sqs-worker-cdk/.buildkite/pipeline.yml +3 -3
  306. package/template/lambda-sqs-worker-cdk/infra/__snapshots__/appStack.test.ts.snap +6 -0
  307. package/template/lambda-sqs-worker-cdk/infra/appStack.ts +3 -3
  308. package/template/lambda-sqs-worker-cdk/infra/index.ts +2 -2
  309. package/template/lambda-sqs-worker-cdk/package.json +2 -2
  310. package/template/lambda-sqs-worker-cdk/shared/context-types.ts +16 -25
  311. package/template/oss-npm-package/_package.json +1 -1
  312. package/template/private-npm-package/_package.json +1 -1
  313. package/jest/resolver.js +0 -24
@@ -1,7 +1,7 @@
1
1
  import { agentFromMiddleware } from 'src/testing/server';
2
2
  import {
3
+ IdDescriptionSchema,
3
4
  chance,
4
- filterIdDescription,
5
5
  mockIdDescription,
6
6
  } from 'src/testing/types';
7
7
 
@@ -12,7 +12,7 @@ const agent = agentFromMiddleware(jsonBodyParser, (ctx) => {
12
12
  const result = validate({
13
13
  ctx,
14
14
  input: ctx.request.body,
15
- filter: filterIdDescription,
15
+ schema: IdDescriptionSchema,
16
16
  });
17
17
 
18
18
  ctx.body = result;
@@ -41,13 +41,14 @@ describe('validate', () => {
41
41
  .post('/')
42
42
  .send({ ...idDescription, id: null })
43
43
  .expect(422)
44
- .expect(({ text }) =>
45
- expect(text).toMatchInlineSnapshot(`
46
- "Validation failed:
44
+ .expect(({ body }) =>
45
+ expect(body).toMatchInlineSnapshot(`
47
46
  {
48
- "id": "Expected string, but was null"
49
- }.
50
- Object should match { id: string; description: string; }"
47
+ "invalidFields": {
48
+ "/id": "Expected string, received null",
49
+ },
50
+ "message": "Input validation failed",
51
+ }
51
52
  `),
52
53
  );
53
54
  });
@@ -57,14 +58,15 @@ describe('validate', () => {
57
58
  .post('/')
58
59
  .send({})
59
60
  .expect(422)
60
- .expect(({ text }) =>
61
- expect(text).toMatchInlineSnapshot(`
62
- "Validation failed:
61
+ .expect(({ body }) =>
62
+ expect(body).toMatchInlineSnapshot(`
63
63
  {
64
- "id": "Expected string, but was missing",
65
- "description": "Expected string, but was missing"
66
- }.
67
- Object should match { id: string; description: string; }"
64
+ "invalidFields": {
65
+ "/description": "Required",
66
+ "/id": "Required",
67
+ },
68
+ "message": "Input validation failed",
69
+ }
68
70
  `),
69
71
  ));
70
72
  });
@@ -1,23 +1,64 @@
1
+ import { ErrorMiddleware } from 'seek-koala';
2
+ import { z } from 'zod';
3
+
1
4
  import { Context } from 'src/types/koa';
2
5
 
6
+ /**
7
+ * Converts a `ZodError` into an `invalidFields` object
8
+ *
9
+ * For example, the `ZodError`:
10
+ *
11
+ * ```json
12
+ * {
13
+ * "issues": [
14
+ * {
15
+ * "code": "invalid_type",
16
+ * "expected": "string",
17
+ * "received": "undefined",
18
+ * "path": ["advertiserId"],
19
+ * "message": "advertiserId is required in the URL"
20
+ * }
21
+ * ],
22
+ * "name": "ZodError"
23
+ * }
24
+ * ```
25
+ *
26
+ * Returns:
27
+ *
28
+ * ```json
29
+ * { "/advertiserId": "advertiserId is required in the URL" }
30
+ * ```
31
+ */
32
+ const parseInvalidFieldsFromError = ({
33
+ errors,
34
+ }: z.ZodError): Record<string, string> =>
35
+ Object.fromEntries(
36
+ errors.map((err) => [`/${err.path.join('/')}`, err.message]),
37
+ );
38
+
3
39
  export const validate = <T>({
4
40
  ctx,
5
41
  input,
6
- filter,
42
+ schema,
7
43
  }: {
8
44
  ctx: Context;
9
45
  input: unknown;
10
- filter: (data: unknown) => T;
46
+ schema: z.ZodSchema<T>;
11
47
  }) => {
12
- try {
13
- return filter(input);
14
- } catch (err) {
15
- // TODO: consider providing structured error messages for your consumers.
16
- return ctx.throw(422, err instanceof Error ? err.message : String(err));
48
+ const parseResult = schema.safeParse(input);
49
+ if (parseResult.success === false) {
50
+ return ctx.throw(
51
+ 422,
52
+ new ErrorMiddleware.JsonResponse('Input validation failed', {
53
+ message: 'Input validation failed',
54
+ invalidFields: parseInvalidFieldsFromError(parseResult.error),
55
+ }),
56
+ );
17
57
  }
58
+ return parseResult.data;
18
59
  };
19
60
 
20
61
  export const validateRequestBody = <T>(
21
62
  ctx: Context,
22
- filter: (input: unknown) => T,
23
- ): T => validate({ ctx, input: ctx.request.body as unknown, filter });
63
+ schema: z.ZodSchema<T>,
64
+ ): T => validate<T>({ ctx, input: ctx.request.body as unknown, schema });
@@ -1,20 +1,15 @@
1
- /* eslint-disable new-cap */
2
-
3
1
  import { Chance } from 'chance';
4
- import * as t from 'runtypes';
5
- import checkFilter from 'runtypes-filter';
2
+ import { z } from 'zod';
6
3
 
7
4
  import { JobInput } from 'src/types/jobs';
8
5
 
9
- export type IdDescription = t.Static<typeof IdDescription>;
6
+ export type IdDescription = z.infer<typeof IdDescriptionSchema>;
10
7
 
11
- const IdDescription = t.Record({
12
- id: t.String,
13
- description: t.String,
8
+ export const IdDescriptionSchema = z.object({
9
+ id: z.string(),
10
+ description: z.string(),
14
11
  });
15
12
 
16
- export const filterIdDescription = checkFilter(IdDescription);
17
-
18
13
  export const chance = new Chance();
19
14
 
20
15
  export const mockIdDescription = (): IdDescription => ({
@@ -1,7 +1,4 @@
1
- /* eslint-disable new-cap */
2
-
3
- import * as t from 'runtypes';
4
- import checkFilter from 'runtypes-filter';
1
+ import { z } from 'zod';
5
2
 
6
3
  export interface Job {
7
4
  id: string;
@@ -11,12 +8,10 @@ export interface Job {
11
8
  };
12
9
  }
13
10
 
14
- export type JobInput = t.Static<typeof JobInput>;
11
+ export type JobInput = z.infer<typeof JobInputSchema>;
15
12
 
16
- const JobInput = t.Record({
17
- hirer: t.Record({
18
- id: t.String,
13
+ export const JobInputSchema = z.object({
14
+ hirer: z.object({
15
+ id: z.string(),
19
16
  }),
20
17
  });
21
-
22
- export const filterJobInput = checkFilter(JobInput);
@@ -26,13 +26,13 @@ configs:
26
26
  - yarn deploy
27
27
  concurrency: 1
28
28
  plugins:
29
- - artifacts#v1.5.0:
29
+ - artifacts#v1.7.0:
30
30
  build: ${BUILDKITE_BUILD_ID}
31
31
  download: lib/*
32
32
  - *aws-sm
33
33
  - *private-npm
34
34
  - *docker-ecr-cache
35
- - docker-compose#v3.10.0:
35
+ - docker-compose#v4.5.0:
36
36
  dependencies: false
37
37
  run: app
38
38
  retry:
@@ -60,7 +60,7 @@ steps:
60
60
  - *aws-sm
61
61
  - *private-npm
62
62
  - *docker-ecr-cache
63
- - docker-compose#v3.10.0:
63
+ - docker-compose#v4.5.0:
64
64
  run: app
65
65
  timeout_in_minutes: 10
66
66
 
@@ -4,13 +4,12 @@
4
4
  "aws-sdk": "^2.1011.0",
5
5
  "datadog-lambda-js": "^6.83.0",
6
6
  "skuba-dive": "^2.0.0",
7
- "runtypes": "^6.4.1",
8
- "runtypes-filter": "^0.6.0"
7
+ "zod": "^3.19.1"
9
8
  },
10
9
  "devDependencies": {
11
10
  "@types/aws-lambda": "^8.10.84",
12
11
  "@types/chance": "^1.1.3",
13
- "@types/node": "^16.0.0",
12
+ "@types/node": "16.11.64",
14
13
  "chance": "^1.1.8",
15
14
  "pino-pretty": "^9.0.0",
16
15
  "serverless": "^3.17.0",
@@ -24,9 +23,6 @@
24
23
  },
25
24
  "license": "UNLICENSED",
26
25
  "private": true,
27
- "resolutions": {
28
- "@types/responselike": "1.0.0"
29
- },
30
26
  "scripts": {
31
27
  "build": "skuba build",
32
28
  "deploy": "serverless deploy --force --verbose",
@@ -8,7 +8,7 @@ import { metricsClient } from 'src/framework/metrics';
8
8
  import { validateJson } from 'src/framework/validation';
9
9
  import { scoreJobPublishedEvent, scoringService } from 'src/services/jobScorer';
10
10
  import { sendPipelineEvent } from 'src/services/pipelineEventSender';
11
- import { filterJobPublishedEvent } from 'src/types/pipelineEvents';
11
+ import { JobPublishedEventSchema } from 'src/types/pipelineEvents';
12
12
 
13
13
  /**
14
14
  * Tests connectivity to ensure appropriate access and network configuration.
@@ -40,7 +40,7 @@ export const handler = createHandler<SQSEvent>(async (event) => {
40
40
  // the event and eventually send it to your dead-letter queue. If you don't
41
41
  // trust your source to provide consistently well-formed input, consider
42
42
  // catching and handling this error in code.
43
- const publishedJob = validateJson(record.body, filterJobPublishedEvent);
43
+ const publishedJob = validateJson(record.body, JobPublishedEventSchema);
44
44
 
45
45
  const scoredJob = await scoreJobPublishedEvent(publishedJob);
46
46
 
@@ -1,6 +1,6 @@
1
1
  import {
2
+ IdDescriptionSchema,
2
3
  chance,
3
- filterIdDescription,
4
4
  mockIdDescription,
5
5
  } from 'src/testing/types';
6
6
 
@@ -12,7 +12,7 @@ describe('validateJson', () => {
12
12
  it('permits valid input', () => {
13
13
  const input = JSON.stringify(idDescription);
14
14
 
15
- expect(validateJson(input, filterIdDescription)).toStrictEqual(
15
+ expect(validateJson(input, IdDescriptionSchema)).toStrictEqual(
16
16
  idDescription,
17
17
  );
18
18
  });
@@ -20,7 +20,7 @@ describe('validateJson', () => {
20
20
  it('filters additional properties', () => {
21
21
  const input = JSON.stringify({ ...idDescription, hacker: chance.name() });
22
22
 
23
- expect(validateJson(input, filterIdDescription)).toStrictEqual(
23
+ expect(validateJson(input, IdDescriptionSchema)).toStrictEqual(
24
24
  idDescription,
25
25
  );
26
26
  });
@@ -28,27 +28,47 @@ describe('validateJson', () => {
28
28
  it('blocks mistyped prop', () => {
29
29
  const input = JSON.stringify({ ...idDescription, id: null });
30
30
 
31
- expect(() => validateJson(input, filterIdDescription))
31
+ expect(() => validateJson(input, IdDescriptionSchema))
32
32
  .toThrowErrorMatchingInlineSnapshot(`
33
- "Validation failed:
34
- {
35
- "id": "Expected string, but was null"
36
- }.
37
- Object should match { id: string; description: string; }"
33
+ "[
34
+ {
35
+ "code": "invalid_type",
36
+ "expected": "string",
37
+ "received": "null",
38
+ "path": [
39
+ "id"
40
+ ],
41
+ "message": "Expected string, received null"
42
+ }
43
+ ]"
38
44
  `);
39
45
  });
40
46
 
41
47
  it('blocks missing prop', () => {
42
48
  const input = '{}';
43
49
 
44
- expect(() => validateJson(input, filterIdDescription))
50
+ expect(() => validateJson(input, IdDescriptionSchema))
45
51
  .toThrowErrorMatchingInlineSnapshot(`
46
- "Validation failed:
47
- {
48
- "id": "Expected string, but was missing",
49
- "description": "Expected string, but was missing"
50
- }.
51
- Object should match { id: string; description: string; }"
52
+ "[
53
+ {
54
+ "code": "invalid_type",
55
+ "expected": "string",
56
+ "received": "undefined",
57
+ "path": [
58
+ "id"
59
+ ],
60
+ "message": "Required"
61
+ },
62
+ {
63
+ "code": "invalid_type",
64
+ "expected": "string",
65
+ "received": "undefined",
66
+ "path": [
67
+ "description"
68
+ ],
69
+ "message": "Required"
70
+ }
71
+ ]"
52
72
  `);
53
73
  });
54
74
 
@@ -56,7 +76,7 @@ describe('validateJson', () => {
56
76
  const input = '}';
57
77
 
58
78
  expect(() =>
59
- validateJson(input, filterIdDescription),
79
+ validateJson(input, IdDescriptionSchema),
60
80
  ).toThrowErrorMatchingInlineSnapshot(
61
81
  `"Unexpected token } in JSON at position 0"`,
62
82
  );
@@ -1,2 +1,4 @@
1
- export const validateJson = <T>(input: string, filter: (data: unknown) => T) =>
2
- filter(JSON.parse(input));
1
+ import { z } from 'zod';
2
+
3
+ export const validateJson = <T>(input: string, schema: z.ZodSchema<T>) =>
4
+ schema.parse(JSON.parse(input));
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  JobScorerInput,
7
7
  JobScorerOutput,
8
- filterJobScorerOutput,
8
+ JobScorerOutputSchema,
9
9
  } from 'src/types/jobScorer';
10
10
  import { JobPublishedEvent, JobScoredEvent } from 'src/types/pipelineEvents';
11
11
 
@@ -39,7 +39,7 @@ const scoreJob = async ({
39
39
  }: JobScorerInput): Promise<JobScorerOutput> => {
40
40
  const score = await scoringService.request(details);
41
41
 
42
- return filterJobScorerOutput({
42
+ return JobScorerOutputSchema.parse({
43
43
  id,
44
44
  score,
45
45
  });
@@ -1,20 +1,15 @@
1
- /* eslint-disable new-cap */
2
-
3
1
  import { Chance } from 'chance';
4
- import * as t from 'runtypes';
5
- import checkFilter from 'runtypes-filter';
2
+ import { z } from 'zod';
6
3
 
7
4
  import { JobPublishedEvent } from 'src/types/pipelineEvents';
8
5
 
9
- export type IdDescription = t.Static<typeof IdDescription>;
6
+ export type IdDescription = z.infer<typeof IdDescriptionSchema>;
10
7
 
11
- const IdDescription = t.Record({
12
- id: t.String,
13
- description: t.String,
8
+ export const IdDescriptionSchema = z.object({
9
+ id: z.string(),
10
+ description: z.string(),
14
11
  });
15
12
 
16
- export const filterIdDescription = checkFilter(IdDescription);
17
-
18
13
  export const chance = new Chance();
19
14
 
20
15
  export const mockIdDescription = (): IdDescription => ({
@@ -1,22 +1,15 @@
1
- /* eslint-disable new-cap */
1
+ import { z } from 'zod';
2
2
 
3
- import * as t from 'runtypes';
4
- import checkFilter from 'runtypes-filter';
3
+ export type JobScorerInput = z.infer<typeof JobScorerInputSchema>;
5
4
 
6
- export type JobScorerInput = t.Static<typeof JobScorerInput>;
7
-
8
- const JobScorerInput = t.Record({
9
- id: t.String,
10
- details: t.String,
5
+ const JobScorerInputSchema = z.object({
6
+ id: z.string(),
7
+ details: z.string(),
11
8
  });
12
9
 
13
- export const filterJobScorerInput = checkFilter(JobScorerInput);
14
-
15
- export type JobScorerOutput = t.Static<typeof JobScorerOutput>;
10
+ export type JobScorerOutput = z.infer<typeof JobScorerOutputSchema>;
16
11
 
17
- const JobScorerOutput = t.Record({
18
- id: t.String,
19
- score: t.Number,
12
+ export const JobScorerOutputSchema = z.object({
13
+ id: z.string(),
14
+ score: z.number(),
20
15
  });
21
-
22
- export const filterJobScorerOutput = checkFilter(JobScorerOutput);
@@ -1,28 +1,21 @@
1
- /* eslint-disable new-cap */
1
+ import { z } from 'zod';
2
2
 
3
- import * as t from 'runtypes';
4
- import checkFilter from 'runtypes-filter';
3
+ export type JobPublishedEvent = z.infer<typeof JobPublishedEventSchema>;
5
4
 
6
- export type JobPublishedEvent = t.Static<typeof JobPublishedEvent>;
7
-
8
- const JobPublishedEvent = t.Record({
9
- data: t.Record({
10
- details: t.String,
5
+ export const JobPublishedEventSchema = z.object({
6
+ data: z.object({
7
+ details: z.string(),
11
8
  }),
12
- entityId: t.String,
13
- eventType: t.Literal('JobPublished'),
9
+ entityId: z.string(),
10
+ eventType: z.literal('JobPublished'),
14
11
  });
15
12
 
16
- export const filterJobPublishedEvent = checkFilter(JobPublishedEvent);
17
-
18
- export type JobScoredEvent = t.Static<typeof JobScoredEvent>;
13
+ export type JobScoredEvent = z.infer<typeof JobScoredEventSchema>;
19
14
 
20
- const JobScoredEvent = t.Record({
21
- data: t.Record({
22
- score: t.Number,
15
+ export const JobScoredEventSchema = z.object({
16
+ data: z.object({
17
+ score: z.number(),
23
18
  }),
24
- entityId: t.String,
25
- eventType: t.Literal('JobScored'),
19
+ entityId: z.string(),
20
+ eventType: z.literal('JobScored'),
26
21
  });
27
-
28
- export const filterJobScoredEvent = checkFilter(JobScoredEvent);
@@ -26,13 +26,13 @@ configs:
26
26
  - yarn deploy
27
27
  concurrency: 1
28
28
  plugins:
29
- - artifacts#v1.5.0:
29
+ - artifacts#v1.7.0:
30
30
  build: ${BUILDKITE_BUILD_ID}
31
31
  download: lib/*
32
32
  - *aws-sm
33
33
  - *private-npm
34
34
  - *docker-ecr-cache
35
- - docker-compose#v3.10.0:
35
+ - docker-compose#v4.5.0:
36
36
  dependencies: false
37
37
  run: app
38
38
  retry:
@@ -57,7 +57,7 @@ steps:
57
57
  - *aws-sm
58
58
  - *private-npm
59
59
  - *docker-ecr-cache
60
- - docker-compose#v3.10.0:
60
+ - docker-compose#v4.5.0:
61
61
  run: app
62
62
  timeout_in_minutes: 10
63
63
 
@@ -327,6 +327,9 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
327
327
  "Type": "AWS::SQS::QueuePolicy",
328
328
  },
329
329
  "workerqueueappStacktopic0CA45134AFB31FF4": {
330
+ "DependsOn": [
331
+ "workerqueuePolicy97054CB4",
332
+ ],
330
333
  "Properties": {
331
334
  "Endpoint": {
332
335
  "Fn::GetAtt": [
@@ -686,6 +689,9 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
686
689
  "Type": "AWS::SQS::QueuePolicy",
687
690
  },
688
691
  "workerqueueappStacktopic0CA45134AFB31FF4": {
692
+ "DependsOn": [
693
+ "workerqueuePolicy97054CB4",
694
+ ],
689
695
  "Properties": {
690
696
  "Endpoint": {
691
697
  "Fn::GetAtt": [
@@ -11,14 +11,14 @@ import {
11
11
  } from 'aws-cdk-lib';
12
12
  import type { Construct } from 'constructs';
13
13
 
14
- import { envContext, stageContext } from '../shared/context-types';
14
+ import { EnvContextSchema, StageContextSchema } from '../shared/context-types';
15
15
 
16
16
  export class AppStack extends Stack {
17
17
  constructor(scope: Construct, id: string, props?: StackProps) {
18
18
  super(scope, id, props);
19
19
 
20
- const stage = stageContext.check(this.node.tryGetContext('stage'));
21
- const context = envContext.check(this.node.tryGetContext(stage));
20
+ const stage = StageContextSchema.parse(this.node.tryGetContext('stage'));
21
+ const context = EnvContextSchema.parse(this.node.tryGetContext(stage));
22
22
 
23
23
  const accountPrincipal = new aws_iam.AccountPrincipal(this.account);
24
24
 
@@ -1,12 +1,12 @@
1
1
  import { App } from 'aws-cdk-lib';
2
2
 
3
- import { globalContext } from '../shared/context-types';
3
+ import { GlobalContextSchema } from '../shared/context-types';
4
4
 
5
5
  import { AppStack } from './appStack';
6
6
 
7
7
  const app = new App();
8
8
 
9
- const context = globalContext.check(app.node.tryGetContext('global'));
9
+ const context = GlobalContextSchema.parse(app.node.tryGetContext('global'));
10
10
 
11
11
  // eslint-disable-next-line no-new
12
12
  new AppStack(app, 'appStack', {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "@seek/logger": "^5.0.1",
4
- "runtypes": "^6.3.2"
4
+ "zod": "^3.19.1"
5
5
  },
6
6
  "devDependencies": {
7
7
  "@aws-cdk/assert": "^2.24.0",
8
8
  "@types/aws-lambda": "^8.10.82",
9
- "@types/node": "^16.0.0",
9
+ "@types/node": "16.11.64",
10
10
  "aws-cdk": "^2.24.0",
11
11
  "aws-cdk-lib": "^2.24.0",
12
12
  "constructs": "^10.0.17",
@@ -1,30 +1,21 @@
1
- /* eslint-disable new-cap */
2
- import * as t from 'runtypes';
1
+ import { z } from 'zod';
3
2
 
4
- export const stageContext = t.Union(t.Literal('dev'), t.Literal('prod'));
5
- export type StageContext = t.Static<typeof stageContext>;
3
+ export const StageContextSchema = z.enum(['dev', 'prod']);
4
+ export type StageContext = z.infer<typeof StageContextSchema>;
6
5
 
7
- export const envContext = t
8
- .Record({
9
- workerLambda: t
10
- .Record({
11
- reservedConcurrency: t.Number,
12
- environment: t
13
- .Record({
14
- SOMETHING: t.String,
15
- })
16
- .asReadonly(),
17
- })
18
- .asReadonly(),
19
- })
20
- .asReadonly();
6
+ export const EnvContextSchema = z.object({
7
+ workerLambda: z.object({
8
+ reservedConcurrency: z.number(),
9
+ environment: z.object({
10
+ SOMETHING: z.string(),
11
+ }),
12
+ }),
13
+ });
21
14
 
22
- export type EnvContext = t.Static<typeof envContext>;
15
+ export type EnvContext = z.infer<typeof EnvContextSchema>;
23
16
 
24
- export const globalContext = t
25
- .Record({
26
- appName: t.String,
27
- })
28
- .asReadonly();
17
+ export const GlobalContextSchema = z.object({
18
+ appName: z.string(),
19
+ });
29
20
 
30
- export type GlobalContext = t.Static<typeof globalContext>;
21
+ export type GlobalContext = z.infer<typeof GlobalContextSchema>;
@@ -2,7 +2,7 @@
2
2
  "dependencies": {},
3
3
  "description": "<%- description %>",
4
4
  "devDependencies": {
5
- "@types/node": "^16.0.0",
5
+ "@types/node": "16.11.64",
6
6
  "commitizen": "^4.2.4",
7
7
  "skuba": "*"
8
8
  },
@@ -2,7 +2,7 @@
2
2
  "dependencies": {},
3
3
  "description": "<%- description %>",
4
4
  "devDependencies": {
5
- "@types/node": "^16.0.0",
5
+ "@types/node": "16.11.64",
6
6
  "commitizen": "^4.2.4",
7
7
  "skuba": "*"
8
8
  },