skuba 13.0.0-subpath-imports-20250722044721 → 13.0.0-subpath-imports-20251003044159

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 (268) hide show
  1. package/README.md +1 -2
  2. package/config/tsconfig.json +2 -1
  3. package/jest/moduleNameMapper.js +3 -11
  4. package/lib/cli/build/assets.js +4 -8
  5. package/lib/cli/build/assets.js.map +2 -2
  6. package/lib/cli/build/esbuild.d.ts +2 -1
  7. package/lib/cli/build/esbuild.js +5 -4
  8. package/lib/cli/build/esbuild.js.map +2 -2
  9. package/lib/cli/build/index.js +9 -5
  10. package/lib/cli/build/index.js.map +2 -2
  11. package/lib/cli/build/tsc.d.ts +9 -2
  12. package/lib/cli/build/tsc.js +76 -40
  13. package/lib/cli/build/tsc.js.map +3 -3
  14. package/lib/cli/configure/analyseDependencies.d.ts +2 -2
  15. package/lib/cli/configure/analyseDependencies.js.map +1 -1
  16. package/lib/cli/configure/analysis/package.d.ts +1 -1
  17. package/lib/cli/configure/analysis/package.js +1 -1
  18. package/lib/cli/configure/analysis/package.js.map +2 -2
  19. package/lib/cli/configure/ensureTemplateCompletion.d.ts +2 -2
  20. package/lib/cli/configure/ensureTemplateCompletion.js.map +1 -1
  21. package/lib/cli/configure/getEntryPoint.d.ts +2 -2
  22. package/lib/cli/configure/getEntryPoint.js.map +1 -1
  23. package/lib/cli/configure/getProjectType.d.ts +2 -2
  24. package/lib/cli/configure/getProjectType.js.map +1 -1
  25. package/lib/cli/configure/processing/configFile.d.ts +1 -1
  26. package/lib/cli/configure/processing/configFile.js +48 -6
  27. package/lib/cli/configure/processing/configFile.js.map +2 -2
  28. package/lib/cli/configure/processing/package.js +8 -2
  29. package/lib/cli/configure/processing/package.js.map +2 -2
  30. package/lib/cli/init/getConfig.js +1 -1
  31. package/lib/cli/init/getConfig.js.map +2 -2
  32. package/lib/cli/init/git.js +1 -1
  33. package/lib/cli/init/git.js.map +2 -2
  34. package/lib/cli/init/index.js +4 -4
  35. package/lib/cli/init/index.js.map +3 -3
  36. package/lib/cli/lint/annotate/buildkite/eslint.js +1 -1
  37. package/lib/cli/lint/annotate/buildkite/eslint.js.map +2 -2
  38. package/lib/cli/lint/annotate/buildkite/index.js +1 -1
  39. package/lib/cli/lint/annotate/buildkite/index.js.map +2 -2
  40. package/lib/cli/lint/annotate/buildkite/internal.js +1 -1
  41. package/lib/cli/lint/annotate/buildkite/internal.js.map +2 -2
  42. package/lib/cli/lint/annotate/buildkite/prettier.js +1 -1
  43. package/lib/cli/lint/annotate/buildkite/prettier.js.map +2 -2
  44. package/lib/cli/lint/annotate/buildkite/tsc.js +1 -1
  45. package/lib/cli/lint/annotate/buildkite/tsc.js.map +2 -2
  46. package/lib/cli/lint/annotate/github/eslint.d.ts +1 -1
  47. package/lib/cli/lint/annotate/github/eslint.js.map +2 -2
  48. package/lib/cli/lint/annotate/github/index.js +3 -4
  49. package/lib/cli/lint/annotate/github/index.js.map +2 -2
  50. package/lib/cli/lint/annotate/github/internal.d.ts +1 -1
  51. package/lib/cli/lint/annotate/github/internal.js.map +2 -2
  52. package/lib/cli/lint/annotate/github/prettier.d.ts +1 -1
  53. package/lib/cli/lint/annotate/github/prettier.js.map +2 -2
  54. package/lib/cli/lint/annotate/github/tsc.d.ts +1 -1
  55. package/lib/cli/lint/annotate/github/tsc.js.map +2 -2
  56. package/lib/cli/lint/autofix.d.ts +1 -1
  57. package/lib/cli/lint/autofix.js +3 -3
  58. package/lib/cli/lint/autofix.js.map +2 -2
  59. package/lib/cli/lint/internal.js +1 -1
  60. package/lib/cli/lint/internal.js.map +2 -2
  61. package/lib/cli/lint/internalLints/patchRenovateConfig.js +3 -3
  62. package/lib/cli/lint/internalLints/patchRenovateConfig.js.map +2 -2
  63. package/lib/cli/lint/internalLints/refreshConfigFiles.js +5 -3
  64. package/lib/cli/lint/internalLints/refreshConfigFiles.js.map +2 -2
  65. package/lib/cli/lint/internalLints/upgrade/index.d.ts +2 -2
  66. package/lib/cli/lint/internalLints/upgrade/index.js.map +1 -1
  67. package/lib/{api/buildkite/md.js → cli/lint/internalLints/upgrade/patches/12.0.2/index.js} +13 -11
  68. package/lib/cli/lint/internalLints/upgrade/patches/12.0.2/index.js.map +7 -0
  69. package/lib/cli/lint/internalLints/upgrade/patches/12.0.2/unhandledRejections.d.ts +4 -0
  70. package/lib/cli/lint/internalLints/upgrade/patches/12.0.2/unhandledRejections.js +162 -0
  71. package/lib/cli/lint/internalLints/upgrade/patches/12.0.2/unhandledRejections.js.map +7 -0
  72. package/lib/cli/lint/internalLints/upgrade/patches/12.1.1/index.d.ts +2 -0
  73. package/lib/{api/net → cli/lint/internalLints/upgrade/patches/12.1.1}/index.js +12 -6
  74. package/lib/cli/lint/internalLints/upgrade/patches/12.1.1/index.js.map +7 -0
  75. package/lib/cli/lint/internalLints/upgrade/patches/12.1.1/patchJestSnapshots.d.ts +3 -0
  76. package/lib/cli/lint/internalLints/upgrade/patches/12.1.1/patchJestSnapshots.js +105 -0
  77. package/lib/cli/lint/internalLints/upgrade/patches/12.1.1/patchJestSnapshots.js.map +7 -0
  78. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/index.d.ts +2 -0
  79. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/index.js +40 -0
  80. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/index.js.map +7 -0
  81. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/patchApiTokenFromEnvironment.d.ts +3 -0
  82. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/patchApiTokenFromEnvironment.js +100 -0
  83. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/patchApiTokenFromEnvironment.js.map +7 -0
  84. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/patchDockerfileSyntaxDirective.d.ts +3 -0
  85. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/patchDockerfileSyntaxDirective.js +99 -0
  86. package/lib/cli/lint/internalLints/upgrade/patches/12.3.0/patchDockerfileSyntaxDirective.js.map +7 -0
  87. package/lib/cli/lint/internalLints/upgrade/patches/12.4.0/index.d.ts +2 -0
  88. package/lib/{api/buildkite → cli/lint/internalLints/upgrade/patches/12.4.0}/index.js +12 -9
  89. package/lib/cli/lint/internalLints/upgrade/patches/12.4.0/index.js.map +7 -0
  90. package/lib/cli/lint/internalLints/upgrade/patches/12.4.0/patchDockerfileCIVariable.d.ts +3 -0
  91. package/lib/cli/lint/internalLints/upgrade/patches/12.4.0/patchDockerfileCIVariable.js +101 -0
  92. package/lib/cli/lint/internalLints/upgrade/patches/12.4.0/patchDockerfileCIVariable.js.map +7 -0
  93. package/lib/cli/lint/internalLints/upgrade/patches/13.0.0/index.d.ts +2 -0
  94. package/lib/cli/lint/internalLints/upgrade/patches/{12.2.0 → 13.0.0}/index.js.map +1 -1
  95. package/lib/cli/lint/internalLints/upgrade/patches/13.0.0/rewriteSrcImports.d.ts +13 -0
  96. package/lib/cli/lint/internalLints/upgrade/patches/13.0.0/rewriteSrcImports.js +221 -0
  97. package/lib/cli/lint/internalLints/upgrade/patches/13.0.0/rewriteSrcImports.js.map +7 -0
  98. package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/upgradeESLint.js +1 -1
  99. package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/upgradeESLint.js.map +2 -2
  100. package/lib/cli/node/index.js +8 -2
  101. package/lib/cli/node/index.js.map +2 -2
  102. package/lib/cli/start/index.js +8 -2
  103. package/lib/cli/start/index.js.map +2 -2
  104. package/lib/cli/test/index.d.ts +1 -1
  105. package/lib/cli/test/index.js +18 -4
  106. package/lib/cli/test/index.js.map +2 -2
  107. package/lib/cli/test/reporters/github/annotations.d.ts +1 -1
  108. package/lib/cli/test/reporters/github/annotations.js.map +1 -1
  109. package/lib/cli/test/reporters/github/index.js +3 -4
  110. package/lib/cli/test/reporters/github/index.js.map +2 -2
  111. package/lib/index.d.ts +4 -4
  112. package/lib/index.js +4 -4
  113. package/lib/index.js.map +2 -2
  114. package/lib/utils/args.d.ts +2 -0
  115. package/lib/utils/args.js +5 -0
  116. package/lib/utils/args.js.map +2 -2
  117. package/lib/utils/dir.js +2 -2
  118. package/lib/utils/dir.js.map +3 -3
  119. package/lib/utils/manifest.d.ts +6 -2
  120. package/lib/utils/manifest.js +17 -8
  121. package/lib/utils/manifest.js.map +2 -2
  122. package/lib/utils/template.d.ts +1 -1
  123. package/package.json +21 -24
  124. package/template/base/_.prettierrc.js +1 -1
  125. package/template/base/_eslint.config.js +1 -1
  126. package/template/base/_pnpm-workspace.yaml +14 -3
  127. package/template/base/jest.config.ts +7 -12
  128. package/template/base/jest.setup.ts +1 -1
  129. package/template/express-rest-api/.buildkite/pipeline.yml +6 -0
  130. package/template/express-rest-api/.env +1 -1
  131. package/template/express-rest-api/.gantry/dev.yml +5 -1
  132. package/template/express-rest-api/.gantry/prod.yml +5 -1
  133. package/template/express-rest-api/Dockerfile +2 -2
  134. package/template/express-rest-api/Dockerfile.dev-deps +0 -2
  135. package/template/express-rest-api/README.md +5 -5
  136. package/template/express-rest-api/gantry.apply.yml +17 -1
  137. package/template/express-rest-api/package.json +11 -5
  138. package/template/express-rest-api/src/api/healthCheck.ts +2 -2
  139. package/template/express-rest-api/src/config.ts +7 -7
  140. package/template/express-rest-api/src/framework/logging.ts +11 -7
  141. package/template/express-rest-api/src/framework/metrics.ts +1 -1
  142. package/template/express-rest-api/src/listen.ts +6 -0
  143. package/template/express-rest-api/src/tracing.ts +56 -0
  144. package/template/greeter/Dockerfile +0 -2
  145. package/template/greeter/README.md +2 -2
  146. package/template/greeter/package.json +2 -2
  147. package/template/koa-rest-api/.buildkite/pipeline.yml +6 -0
  148. package/template/koa-rest-api/.env +1 -1
  149. package/template/koa-rest-api/.gantry/dev.yml +3 -3
  150. package/template/koa-rest-api/.gantry/prod.yml +3 -3
  151. package/template/koa-rest-api/Dockerfile +1 -1
  152. package/template/koa-rest-api/Dockerfile.dev-deps +0 -2
  153. package/template/koa-rest-api/README.md +6 -6
  154. package/template/koa-rest-api/gantry.apply.yml +15 -3
  155. package/template/koa-rest-api/package.json +13 -14
  156. package/template/koa-rest-api/src/api/healthCheck.ts +2 -2
  157. package/template/koa-rest-api/src/config.ts +7 -7
  158. package/template/koa-rest-api/src/framework/logging.ts +12 -8
  159. package/template/koa-rest-api/src/framework/metrics.ts +1 -1
  160. package/template/koa-rest-api/src/framework/server.test.ts +7 -8
  161. package/template/koa-rest-api/src/framework/server.ts +1 -4
  162. package/template/koa-rest-api/src/listen.ts +6 -0
  163. package/template/lambda-sqs-worker-cdk/.buildkite/pipeline.yml +6 -2
  164. package/template/lambda-sqs-worker-cdk/.env +1 -1
  165. package/template/lambda-sqs-worker-cdk/Dockerfile +0 -2
  166. package/template/lambda-sqs-worker-cdk/README.md +8 -8
  167. package/template/lambda-sqs-worker-cdk/infra/__snapshots__/appStack.test.ts.snap +54 -26
  168. package/template/lambda-sqs-worker-cdk/infra/appStack.test.ts +5 -22
  169. package/template/lambda-sqs-worker-cdk/infra/appStack.ts +16 -9
  170. package/template/lambda-sqs-worker-cdk/infra/config.ts +34 -20
  171. package/template/lambda-sqs-worker-cdk/infra/index.ts +1 -1
  172. package/template/lambda-sqs-worker-cdk/package.json +7 -9
  173. package/template/lambda-sqs-worker-cdk/src/app.test.ts +91 -51
  174. package/template/lambda-sqs-worker-cdk/src/app.ts +10 -9
  175. package/template/lambda-sqs-worker-cdk/src/config.ts +11 -16
  176. package/template/lambda-sqs-worker-cdk/src/framework/handler.test.ts +10 -5
  177. package/template/lambda-sqs-worker-cdk/src/framework/handler.ts +48 -24
  178. package/template/lambda-sqs-worker-cdk/src/framework/logging.ts +23 -11
  179. package/template/lambda-sqs-worker-cdk/src/framework/metrics.ts +1 -4
  180. package/template/lambda-sqs-worker-cdk/src/testing/handler.ts +4 -1
  181. package/template/oss-npm-package/.github/workflows/release.yml +7 -8
  182. package/template/oss-npm-package/.github/workflows/validate.yml +6 -6
  183. package/template/oss-npm-package/README.md +1 -1
  184. package/template/oss-npm-package/_package.json +0 -3
  185. package/template/oss-npm-package/skuba.template.js +1 -1
  186. package/lib/api/buildkite/annotate.d.ts +0 -28
  187. package/lib/api/buildkite/annotate.js +0 -62
  188. package/lib/api/buildkite/annotate.js.map +0 -7
  189. package/lib/api/buildkite/index.d.ts +0 -3
  190. package/lib/api/buildkite/index.js.map +0 -7
  191. package/lib/api/buildkite/md.d.ts +0 -6
  192. package/lib/api/buildkite/md.js.map +0 -7
  193. package/lib/api/git/commit.d.ts +0 -15
  194. package/lib/api/git/commit.js +0 -52
  195. package/lib/api/git/commit.js.map +0 -7
  196. package/lib/api/git/commitAllChanges.d.ts +0 -19
  197. package/lib/api/git/commitAllChanges.js +0 -83
  198. package/lib/api/git/commitAllChanges.js.map +0 -7
  199. package/lib/api/git/currentBranch.d.ts +0 -10
  200. package/lib/api/git/currentBranch.js +0 -59
  201. package/lib/api/git/currentBranch.js.map +0 -7
  202. package/lib/api/git/findRoot.d.ts +0 -9
  203. package/lib/api/git/findRoot.js +0 -52
  204. package/lib/api/git/findRoot.js.map +0 -7
  205. package/lib/api/git/getChangedFiles.d.ts +0 -20
  206. package/lib/api/git/getChangedFiles.js +0 -81
  207. package/lib/api/git/getChangedFiles.js.map +0 -7
  208. package/lib/api/git/index.d.ts +0 -12
  209. package/lib/api/git/index.js +0 -61
  210. package/lib/api/git/index.js.map +0 -7
  211. package/lib/api/git/isFileGitIgnored.d.ts +0 -4
  212. package/lib/api/git/isFileGitIgnored.js +0 -49
  213. package/lib/api/git/isFileGitIgnored.js.map +0 -7
  214. package/lib/api/git/log.d.ts +0 -19
  215. package/lib/api/git/log.js +0 -71
  216. package/lib/api/git/log.js.map +0 -7
  217. package/lib/api/git/pull.d.ts +0 -35
  218. package/lib/api/git/pull.js +0 -69
  219. package/lib/api/git/pull.js.map +0 -7
  220. package/lib/api/git/push.d.ts +0 -52
  221. package/lib/api/git/push.js +0 -70
  222. package/lib/api/git/push.js.map +0 -7
  223. package/lib/api/git/remote.d.ts +0 -20
  224. package/lib/api/git/remote.js +0 -73
  225. package/lib/api/git/remote.js.map +0 -7
  226. package/lib/api/git/reset.d.ts +0 -12
  227. package/lib/api/git/reset.js +0 -62
  228. package/lib/api/git/reset.js.map +0 -7
  229. package/lib/api/git/statusMatrix.d.ts +0 -7
  230. package/lib/api/git/statusMatrix.js +0 -47
  231. package/lib/api/git/statusMatrix.js.map +0 -7
  232. package/lib/api/github/checkRun.d.ts +0 -44
  233. package/lib/api/github/checkRun.js +0 -81
  234. package/lib/api/github/checkRun.js.map +0 -7
  235. package/lib/api/github/environment.d.ts +0 -17
  236. package/lib/api/github/environment.js +0 -48
  237. package/lib/api/github/environment.js.map +0 -7
  238. package/lib/api/github/index.d.ts +0 -7
  239. package/lib/api/github/index.js +0 -48
  240. package/lib/api/github/index.js.map +0 -7
  241. package/lib/api/github/issueComment.d.ts +0 -61
  242. package/lib/api/github/issueComment.js +0 -99
  243. package/lib/api/github/issueComment.js.map +0 -7
  244. package/lib/api/github/octokit.d.ts +0 -6
  245. package/lib/api/github/octokit.js +0 -42
  246. package/lib/api/github/octokit.js.map +0 -7
  247. package/lib/api/github/pullRequest.d.ts +0 -21
  248. package/lib/api/github/pullRequest.js +0 -69
  249. package/lib/api/github/pullRequest.js.map +0 -7
  250. package/lib/api/github/push.d.ts +0 -85
  251. package/lib/api/github/push.js +0 -166
  252. package/lib/api/github/push.js.map +0 -7
  253. package/lib/api/net/compose.d.ts +0 -4
  254. package/lib/api/net/compose.js +0 -51
  255. package/lib/api/net/compose.js.map +0 -7
  256. package/lib/api/net/index.d.ts +0 -1
  257. package/lib/api/net/index.js.map +0 -7
  258. package/lib/api/net/socket.d.ts +0 -5
  259. package/lib/api/net/socket.js +0 -68
  260. package/lib/api/net/socket.js.map +0 -7
  261. package/lib/api/net/waitFor.d.ts +0 -22
  262. package/lib/api/net/waitFor.js +0 -40
  263. package/lib/api/net/waitFor.js.map +0 -7
  264. package/lib/cli/lint/internalLints/upgrade/patches/12.2.0/rewriteSrcImports.d.ts +0 -6
  265. package/lib/cli/lint/internalLints/upgrade/patches/12.2.0/rewriteSrcImports.js +0 -104
  266. package/lib/cli/lint/internalLints/upgrade/patches/12.2.0/rewriteSrcImports.js.map +0 -7
  267. /package/lib/cli/lint/internalLints/upgrade/patches/{12.2.0 → 12.0.2}/index.d.ts +0 -0
  268. /package/lib/cli/lint/internalLints/upgrade/patches/{12.2.0 → 13.0.0}/index.js +0 -0
@@ -1,4 +1,5 @@
1
1
  import { PublishCommand } from '@aws-sdk/client-sns';
2
+ import type { SQSBatchResponse } from 'aws-lambda';
2
3
 
3
4
  import * as app from './app.js';
4
5
  import { stdoutMock } from './framework/logging.js';
@@ -40,42 +41,100 @@ describe('handler', () => {
40
41
  it('handles one record', async () => {
41
42
  const event = createSqsEvent([JSON.stringify(jobPublished)]);
42
43
 
43
- await expect(app.handler(event, ctx)).resolves.toBeUndefined();
44
+ await expect(app.handler(event, ctx)).resolves.toEqual<SQSBatchResponse>({
45
+ batchItemFailures: [],
46
+ });
44
47
 
45
48
  expect(scoringService.request).toHaveBeenCalledTimes(1);
46
49
 
47
50
  expect(stdoutMock.calls).toMatchObject([
51
+ { count: 1, level: 20, msg: 'Received jobs' },
48
52
  {
49
- awsRequestId: '-',
50
- count: 1,
51
53
  level: 20,
52
- msg: 'Received jobs',
54
+ msg: 'Scored job',
55
+ snsMessageId: expect.any(String),
56
+ sqsMessageId: event.Records[0]!.messageId,
53
57
  },
58
+ { level: 20, msg: 'Function completed' },
59
+ ]);
60
+
61
+ expect(distribution.mock.calls).toEqual([
62
+ ['job.received', 1],
63
+ ['job.scored', 1],
64
+ ]);
65
+
66
+ expect(sns.client).toReceiveCommandTimes(PublishCommand, 1);
67
+ });
68
+
69
+ it('handles multiple records', async () => {
70
+ const event = createSqsEvent([
71
+ JSON.stringify(jobPublished),
72
+ JSON.stringify(jobPublished),
73
+ ]);
74
+
75
+ await expect(app.handler(event, ctx)).resolves.toEqual<SQSBatchResponse>({
76
+ batchItemFailures: [],
77
+ });
78
+
79
+ expect(stdoutMock.calls).toMatchObject([
80
+ { count: 2, level: 20, msg: 'Received jobs' },
54
81
  {
55
- awsRequestId: '-',
56
82
  level: 20,
57
83
  msg: 'Scored job',
58
84
  snsMessageId: expect.any(String),
85
+ sqsMessageId: event.Records[0]!.messageId,
59
86
  },
60
87
  {
61
- awsRequestId: '-',
62
88
  level: 20,
63
- msg: 'Function succeeded',
89
+ msg: 'Scored job',
90
+ snsMessageId: expect.any(String),
91
+ sqsMessageId: event.Records[1]!.messageId,
64
92
  },
93
+ { level: 20, msg: 'Function completed' },
65
94
  ]);
95
+ });
66
96
 
67
- expect(distribution.mock.calls).toEqual([
68
- ['job.received', 1],
69
- ['job.scored', 1],
97
+ it('handles partial batch failure', async () => {
98
+ const event = createSqsEvent([
99
+ JSON.stringify('}'),
100
+ JSON.stringify(jobPublished),
70
101
  ]);
71
102
 
72
- expect(sns.client).toReceiveCommandTimes(PublishCommand, 1);
103
+ await expect(app.handler(event, ctx)).resolves.toEqual<SQSBatchResponse>({
104
+ batchItemFailures: [{ itemIdentifier: event.Records[0]!.messageId }],
105
+ });
106
+
107
+ expect(stdoutMock.calls).toMatchObject([
108
+ { count: 2, level: 20, msg: 'Received jobs' },
109
+ {
110
+ error: {
111
+ name: 'ZodError',
112
+ type: 'ZodError',
113
+ },
114
+ level: 50,
115
+ msg: 'Processing record failed',
116
+ sqsMessageId: event.Records[0]!.messageId,
117
+ },
118
+ {
119
+ level: 20,
120
+ msg: 'Scored job',
121
+ snsMessageId: expect.any(String),
122
+ sqsMessageId: event.Records[1]!.messageId,
123
+ },
124
+ { level: 20, msg: 'Function completed' },
125
+ ]);
73
126
  });
74
127
 
75
- it('throws on invalid input', () => {
128
+ it('returns a batchItemFailure on invalid input', () => {
76
129
  const event = createSqsEvent(['}']);
77
130
 
78
- return expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
131
+ return expect(app.handler(event, ctx)).resolves.toEqual<SQSBatchResponse>({
132
+ batchItemFailures: [
133
+ {
134
+ itemIdentifier: event.Records[0]!.messageId,
135
+ },
136
+ ],
137
+ });
79
138
  });
80
139
 
81
140
  it('bubbles up scoring service error', async () => {
@@ -85,24 +144,22 @@ describe('handler', () => {
85
144
 
86
145
  const event = createSqsEvent([JSON.stringify(jobPublished)]);
87
146
 
88
- await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
147
+ await expect(app.handler(event, ctx)).resolves.toEqual<SQSBatchResponse>({
148
+ batchItemFailures: [{ itemIdentifier: event.Records[0]!.messageId }],
149
+ });
89
150
 
90
151
  expect(stdoutMock.calls).toMatchObject([
152
+ { count: 1, level: 20, msg: 'Received jobs' },
91
153
  {
92
- awsRequestId: '-',
93
- count: 1,
94
- level: 20,
95
- msg: 'Received jobs',
96
- },
97
- {
98
- awsRequestId: '-',
99
- err: {
154
+ error: {
100
155
  message: err.message,
101
156
  type: 'Error',
102
157
  },
103
158
  level: 50,
104
- msg: 'Function failed',
159
+ msg: 'Processing record failed',
160
+ sqsMessageId: event.Records[0]!.messageId,
105
161
  },
162
+ { level: 20, msg: 'Function completed' },
106
163
  ]);
107
164
  });
108
165
 
@@ -113,23 +170,28 @@ describe('handler', () => {
113
170
 
114
171
  const event = createSqsEvent([JSON.stringify(jobPublished)]);
115
172
 
116
- await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
173
+ await expect(app.handler(event, ctx)).resolves.toEqual<SQSBatchResponse>({
174
+ batchItemFailures: [{ itemIdentifier: event.Records[0]!.messageId }],
175
+ });
117
176
 
118
177
  expect(stdoutMock.calls).toMatchObject([
119
178
  {
120
- awsRequestId: '-',
121
179
  count: 1,
122
180
  level: 20,
123
181
  msg: 'Received jobs',
124
182
  },
125
183
  {
126
- awsRequestId: '-',
127
- err: {
184
+ error: {
128
185
  message: err.message,
129
186
  type: 'Error',
130
187
  },
131
188
  level: 50,
132
- msg: 'Function failed',
189
+ msg: 'Processing record failed',
190
+ sqsMessageId: event.Records[0]!.messageId,
191
+ },
192
+ {
193
+ level: 20,
194
+ msg: 'Function completed',
133
195
  },
134
196
  ]);
135
197
  });
@@ -141,8 +203,7 @@ describe('handler', () => {
141
203
 
142
204
  expect(stdoutMock.calls).toMatchObject([
143
205
  {
144
- awsRequestId: '-',
145
- err: {
206
+ error: {
146
207
  message: 'Received 0 records',
147
208
  type: 'Error',
148
209
  },
@@ -151,25 +212,4 @@ describe('handler', () => {
151
212
  },
152
213
  ]);
153
214
  });
154
-
155
- it('throws on multiple records', async () => {
156
- const event = createSqsEvent([
157
- JSON.stringify(jobPublished),
158
- JSON.stringify(jobPublished),
159
- ]);
160
-
161
- await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
162
-
163
- expect(stdoutMock.calls).toMatchObject([
164
- {
165
- awsRequestId: '-',
166
- err: {
167
- message: 'Received 2 records',
168
- type: 'Error',
169
- },
170
- level: 50,
171
- msg: 'Function failed',
172
- },
173
- ]);
174
- });
175
215
  });
@@ -1,7 +1,10 @@
1
1
  import { isLambdaHook } from '@seek/aws-codedeploy-hooks';
2
2
  import type { SQSEvent } from 'aws-lambda';
3
3
 
4
- import { createHandler } from '#src/framework/handler.js';
4
+ import {
5
+ createBatchSQSHandler,
6
+ createHandler,
7
+ } from '#src/framework/handler.js';
5
8
  import { logger } from '#src/framework/logging.js';
6
9
  import { metricsClient } from '#src/framework/metrics.js';
7
10
  import { validateJson } from '#src/framework/validation.js';
@@ -34,19 +37,17 @@ export const handler = createHandler<SQSEvent>(async (event, ctx) => {
34
37
 
35
38
  const count = event.Records.length;
36
39
 
37
- if (count !== 1) {
38
- throw Error(`Received ${count} records`);
40
+ if (!count) {
41
+ throw Error('Received 0 records');
39
42
  }
40
-
41
43
  logger.debug({ count }, 'Received jobs');
42
44
 
43
- metricsClient.distribution('job.received', event.Records.length);
45
+ metricsClient.distribution('job.received', count);
44
46
 
45
- const record = event.Records[0];
46
- if (!record) {
47
- throw new Error('Malformed SQS event with no records');
48
- }
47
+ return recordHandler(event, ctx);
48
+ });
49
49
 
50
+ const recordHandler = createBatchSQSHandler(async (record, _ctx) => {
50
51
  const { body } = record;
51
52
 
52
53
  // TODO: this throws an error, which will cause the Lambda function to retry
@@ -1,27 +1,25 @@
1
1
  import { Env } from 'skuba-dive';
2
2
 
3
3
  interface Config {
4
- environment: Environment;
4
+ deployment: Deployment;
5
5
 
6
6
  logLevel: string;
7
- metrics: boolean;
8
7
  name: string;
9
8
  version: string;
10
9
 
11
10
  destinationSnsTopicArn: string;
12
11
  }
13
12
 
14
- type Environment = (typeof environments)[number];
13
+ type Deployment = (typeof deployments)[number];
15
14
 
16
- const environments = ['local', 'test', 'dev', 'prod'] as const;
15
+ const deployments = ['local', 'test', 'dev', 'prod'] as const;
17
16
 
18
- const environment = Env.oneOf(environments)('ENVIRONMENT');
17
+ const deployment = Env.oneOf(deployments)('DEPLOYMENT');
19
18
 
20
19
  /* istanbul ignore next: config verification makes more sense in a smoke test */
21
- const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
20
+ const configs: Record<Deployment, () => Omit<Config, 'deployment'>> = {
22
21
  local: () => ({
23
22
  logLevel: 'debug',
24
- metrics: false,
25
23
  name: '<%- serviceName %>',
26
24
  version: 'local',
27
25
 
@@ -30,7 +28,6 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
30
28
 
31
29
  test: () => ({
32
30
  logLevel: Env.string('LOG_LEVEL', { default: 'debug' }),
33
- metrics: false,
34
31
  name: '<%- serviceName %>',
35
32
  version: 'test',
36
33
 
@@ -39,24 +36,22 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
39
36
 
40
37
  dev: () => ({
41
38
  logLevel: 'debug',
42
- metrics: true,
43
- name: Env.string('SERVICE'),
44
- version: Env.string('VERSION'),
39
+ name: Env.string('DD_SERVICE'),
40
+ version: Env.string('DD_VERSION'),
45
41
 
46
42
  destinationSnsTopicArn: Env.string('DESTINATION_SNS_TOPIC_ARN'),
47
43
  }),
48
44
 
49
45
  prod: () => ({
50
46
  logLevel: 'info',
51
- metrics: true,
52
- name: Env.string('SERVICE'),
53
- version: Env.string('VERSION'),
47
+ name: Env.string('DD_SERVICE'),
48
+ version: Env.string('DD_VERSION'),
54
49
 
55
50
  destinationSnsTopicArn: Env.string('DESTINATION_SNS_TOPIC_ARN'),
56
51
  }),
57
52
  };
58
53
 
59
54
  export const config: Config = {
60
- ...configs[environment](),
61
- environment,
55
+ ...configs[deployment](),
56
+ deployment,
62
57
  };
@@ -1,3 +1,5 @@
1
+ import type { SQSEvent } from 'aws-lambda';
2
+
1
3
  import { createHandler } from './handler.js';
2
4
  import { logger, stdoutMock } from './logging.js';
3
5
 
@@ -6,12 +8,14 @@ import { chance } from '#src/testing/types.js';
6
8
 
7
9
  describe('createHandler', () => {
8
10
  const ctx = createCtx();
9
- const input = chance.paragraph();
11
+ const input: SQSEvent = {
12
+ Records: [],
13
+ };
10
14
 
11
15
  afterEach(stdoutMock.clear);
12
16
 
13
17
  it('handles happy path', async () => {
14
- const output = chance.paragraph();
18
+ const output = chance.sentence();
15
19
 
16
20
  const handler = createHandler((event) => {
17
21
  expect(event).toBe(input);
@@ -32,7 +36,8 @@ describe('createHandler', () => {
32
36
  {
33
37
  awsRequestId: '-',
34
38
  level: 20,
35
- msg: 'Function succeeded',
39
+ output,
40
+ msg: 'Function completed',
36
41
  },
37
42
  ]);
38
43
  });
@@ -47,7 +52,7 @@ describe('createHandler', () => {
47
52
  expect(stdoutMock.calls).toMatchObject([
48
53
  {
49
54
  awsRequestId: '-',
50
- err: {
55
+ error: {
51
56
  message: err.message,
52
57
  type: 'Error',
53
58
  },
@@ -69,7 +74,7 @@ describe('createHandler', () => {
69
74
  expect(stdoutMock.calls).toMatchObject([
70
75
  {
71
76
  awsRequestId: '-',
72
- err: {
77
+ error: {
73
78
  message: err.message,
74
79
  type: 'Error',
75
80
  },
@@ -1,40 +1,64 @@
1
- import type { Context as LambdaContext } from 'aws-lambda';
2
- import { datadog } from 'datadog-lambda-js';
1
+ import type {
2
+ Context as LambdaContext,
3
+ SQSBatchItemFailure,
4
+ SQSBatchResponse,
5
+ SQSEvent,
6
+ SQSRecord,
7
+ } from 'aws-lambda';
3
8
 
4
- import { config } from '#src/config.js';
5
- import { logger, loggerContext } from '#src/framework/logging.js';
9
+ import {
10
+ lambdaContext,
11
+ logger,
12
+ recordContext,
13
+ } from '#src/framework/logging.js';
6
14
 
7
15
  type Handler<Event, Output> = (
8
16
  event: Event,
9
17
  ctx: LambdaContext,
10
18
  ) => Promise<Output>;
11
19
 
12
- /**
13
- * Conditionally applies the Datadog wrapper to a Lambda handler.
14
- *
15
- * This also "fixes" its broken type definitions.
16
- */
17
- const withDatadog = <Event, Output = unknown>(
18
- fn: Handler<Event, Output>,
19
- ): Handler<Event, Output> =>
20
- // istanbul ignore next
21
- config.metrics ? (datadog(fn) as Handler<Event, Output>) : fn;
22
-
23
- export const createHandler = <Event, Output = unknown>(
24
- fn: (event: Event, ctx: LambdaContext) => Promise<Output>,
25
- ) =>
26
- withDatadog<Event>((event, ctx) =>
27
- loggerContext.run({ awsRequestId: ctx.awsRequestId }, async () => {
20
+ export const createHandler =
21
+ <Event extends SQSEvent, Output = unknown>(
22
+ fn: (event: Event, ctx: LambdaContext) => Promise<Output>,
23
+ ): Handler<Event, Output> =>
24
+ async (event, ctx) =>
25
+ lambdaContext.run({ awsRequestId: ctx.awsRequestId }, async () => {
28
26
  try {
29
27
  const output = await fn(event, ctx);
30
28
 
31
- logger.debug('Function succeeded');
29
+ logger.debug({ output }, 'Function completed');
32
30
 
33
31
  return output;
34
32
  } catch (err) {
35
- logger.error({ err }, 'Function failed');
33
+ logger.error(err, 'Function failed');
36
34
 
37
35
  throw new Error('Function failed');
38
36
  }
39
- }),
40
- );
37
+ });
38
+
39
+ export const createBatchSQSHandler =
40
+ (
41
+ fn: (record: SQSRecord, ctx: LambdaContext) => Promise<unknown>,
42
+ ): Handler<SQSEvent, SQSBatchResponse> =>
43
+ async (event, ctx) => {
44
+ const processRecord = (
45
+ record: SQSRecord,
46
+ ): Promise<SQSBatchItemFailure | undefined> =>
47
+ recordContext.run({ sqsMessageId: record.messageId }, async () => {
48
+ try {
49
+ await fn(record, ctx);
50
+ return;
51
+ } catch (err) {
52
+ logger.error(err, 'Processing record failed');
53
+ return {
54
+ itemIdentifier: record.messageId,
55
+ };
56
+ }
57
+ });
58
+
59
+ const results = await Promise.all(event.Records.map(processRecord));
60
+
61
+ return {
62
+ batchItemFailures: results.filter((item) => item !== undefined),
63
+ };
64
+ };
@@ -1,17 +1,22 @@
1
1
  import { AsyncLocalStorage } from 'async_hooks';
2
2
 
3
- import createLogger, { createDestination } from '@seek/logger';
3
+ import { createDestination, createLogger } from '@seek/logger';
4
4
 
5
5
  import { config } from '#src/config.js';
6
6
 
7
- interface LoggerContext {
7
+ interface LambdaContext {
8
8
  awsRequestId: string;
9
9
  }
10
10
 
11
- export const loggerContext = new AsyncLocalStorage<LoggerContext>();
11
+ interface RecordContext {
12
+ sqsMessageId: string;
13
+ }
14
+
15
+ export const lambdaContext = new AsyncLocalStorage<LambdaContext>();
16
+ export const recordContext = new AsyncLocalStorage<RecordContext>();
12
17
 
13
18
  const { destination, stdoutMock } = createDestination({
14
- mock: config.environment === 'test' && {
19
+ mock: config.deployment === 'test' && {
15
20
  redact: ['awsRequestId'],
16
21
  },
17
22
  });
@@ -20,19 +25,26 @@ export { stdoutMock };
20
25
 
21
26
  export const logger = createLogger(
22
27
  {
23
- base: {
24
- environment: config.environment,
25
- version: config.version,
28
+ eeeoh: {
29
+ /**
30
+ * TODO: choose an appropriate Datadog log tier.
31
+ *
32
+ * https://github.com/seek-oss/logger/blob/master/docs/eeeoh.md#datadog-log-tiers
33
+ */
34
+ datadog: 'tin',
35
+ team: '<%- teamName %>',
36
+ use: 'environment',
26
37
  },
27
38
 
28
39
  level: config.logLevel,
29
40
 
30
- mixin: () => ({ ...loggerContext.getStore() }),
31
-
32
- name: config.name,
41
+ mixin: () => ({
42
+ ...lambdaContext.getStore(),
43
+ ...recordContext.getStore(),
44
+ }),
33
45
 
34
46
  transport:
35
- config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
47
+ config.deployment === 'local' ? { target: 'pino-pretty' } : undefined,
36
48
  },
37
49
  destination,
38
50
  );
@@ -7,8 +7,5 @@ const prefix = `${config.name}.`;
7
7
  export const metricsClient = {
8
8
  distribution: (
9
9
  ...[name, ...rest]: Parameters<typeof sendDistributionMetric>
10
- ) =>
11
- config.metrics
12
- ? sendDistributionMetric(`${prefix}${name}`, ...rest)
13
- : undefined,
10
+ ) => sendDistributionMetric(`${prefix}${name}`, ...rest),
14
11
  };
@@ -9,5 +9,8 @@ export const createCtx = () =>
9
9
 
10
10
  export const createSqsEvent = (bodies: string[]) =>
11
11
  ({
12
- Records: bodies.map((body) => ({ body })),
12
+ Records: bodies.map((body) => ({
13
+ body,
14
+ messageId: chance.guid({ version: 4 }),
15
+ })),
13
16
  }) as SQSEvent;
@@ -3,7 +3,7 @@ name: Release
3
3
  on:
4
4
  push:
5
5
  branches:
6
- - $default-branch
6
+ - main
7
7
  - beta
8
8
 
9
9
  permissions: {}
@@ -18,17 +18,17 @@ jobs:
18
18
  timeout-minutes: 20
19
19
  steps:
20
20
  - name: Check out repo
21
- uses: actions/checkout@v4
21
+ uses: actions/checkout@v5
22
22
  with:
23
23
  fetch-depth: 0
24
24
 
25
+ - name: Set up pnpm
26
+ uses: pnpm/action-setup@v4
27
+
25
28
  - name: Set up Node.js
26
- uses: actions/setup-node@v4
29
+ uses: actions/setup-node@v5
27
30
  with:
28
- node-version: ^22.14
29
-
30
- - name: Set up pnpm
31
- run: corepack enable pnpm && corepack install
31
+ node-version-file: .nvmrc
32
32
 
33
33
  - name: Install dependencies
34
34
  run: pnpm install --frozen-lockfile
@@ -37,4 +37,3 @@ jobs:
37
37
  run: pnpm release
38
38
  env:
39
39
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -16,15 +16,15 @@ jobs:
16
16
  timeout-minutes: 20
17
17
  steps:
18
18
  - name: Check out repo
19
- uses: actions/checkout@v4
19
+ uses: actions/checkout@v5
20
+
21
+ - name: Set up pnpm
22
+ uses: pnpm/action-setup@v4
20
23
 
21
24
  - name: Set up Node.js
22
- uses: actions/setup-node@v4
25
+ uses: actions/setup-node@v5
23
26
  with:
24
- node-version: ^22.14
25
-
26
- - name: Set up pnpm
27
- run: corepack enable pnpm && corepack install
27
+ node-version-file: .nvmrc
28
28
 
29
29
  - name: Install dependencies
30
30
  run: pnpm install --frozen-lockfile
@@ -82,7 +82,7 @@ on:
82
82
  push:
83
83
  branches:
84
84
  # add others as necessary
85
- - $default-branch
85
+ - main
86
86
  - beta
87
87
  # - alpha
88
88
  ```
@@ -47,8 +47,5 @@
47
47
  "packageManager": "pnpm@10.7.0",
48
48
  "engines": {
49
49
  "node": ">=20.9.0"
50
- },
51
- "publishConfig": {
52
- "provenance": true
53
50
  }
54
51
  }
@@ -2,7 +2,7 @@
2
2
  * Run `skuba configure` to finish templating and remove this file.
3
3
  */
4
4
 
5
- export default {
5
+ module.exports = {
6
6
  entryPoint: 'src/index.ts',
7
7
  fields: [
8
8
  {
@@ -1,28 +0,0 @@
1
- export type AnnotationStyle = 'success' | 'info' | 'warning' | 'error';
2
- interface AnnotationOptions {
3
- context?: string;
4
- /**
5
- * Scopes an annotation's context to the Buildkite step ID.
6
- *
7
- * This lets you emit distinct annotations per step, and only takes effect if
8
- * the `BUILDKITE_STEP_ID` environment variable is present.
9
- */
10
- scopeContextToStep?: boolean;
11
- style?: AnnotationStyle;
12
- }
13
- export declare const MAX_SIZE: number;
14
- export declare const TRUNCATION_WARNING = "... [Truncated due to size limit]";
15
- /**
16
- * Asynchronously uploads a Buildkite annotation.
17
- *
18
- * If the following environment variables are not present,
19
- * the function will silently return without attempting to annotate:
20
- *
21
- * - `BUILDKITE`
22
- * - `BUILDKITE_AGENT_ACCESS_TOKEN`
23
- * - `BUILDKITE_JOB_ID`
24
- *
25
- * The `buildkite-agent` binary must also be on your `PATH`.
26
- */
27
- export declare const annotate: (markdown: string, opts?: AnnotationOptions) => Promise<void>;
28
- export {};