skuba 11.1.0-jest30-20250620003740 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/lib/cli/migrate/nodeVersion/index.js +18 -18
  2. package/lib/cli/migrate/nodeVersion/index.js.map +2 -2
  3. package/package.json +15 -15
  4. package/template/express-rest-api/.buildkite/pipeline.yml +1 -1
  5. package/template/express-rest-api/Dockerfile.dev-deps +1 -1
  6. package/template/express-rest-api/package.json +1 -1
  7. package/template/greeter/.buildkite/pipeline.yml +1 -1
  8. package/template/greeter/Dockerfile +1 -1
  9. package/template/greeter/package.json +2 -2
  10. package/template/koa-rest-api/.buildkite/pipeline.yml +1 -1
  11. package/template/koa-rest-api/Dockerfile.dev-deps +1 -1
  12. package/template/koa-rest-api/package.json +7 -7
  13. package/template/koa-rest-api/src/api/jobs/postJob.test.ts +1 -1
  14. package/template/koa-rest-api/src/config.ts +1 -1
  15. package/template/koa-rest-api/src/framework/logging.ts +21 -12
  16. package/template/koa-rest-api/src/framework/server.test.ts +91 -60
  17. package/template/koa-rest-api/src/framework/validation.test.ts +31 -31
  18. package/template/koa-rest-api/src/framework/validation.ts +14 -21
  19. package/template/koa-rest-api/src/testing/types.ts +1 -1
  20. package/template/koa-rest-api/src/types/jobs.ts +1 -1
  21. package/template/lambda-sqs-worker-cdk/.buildkite/pipeline.yml +2 -2
  22. package/template/lambda-sqs-worker-cdk/Dockerfile +1 -1
  23. package/template/lambda-sqs-worker-cdk/infra/__snapshots__/appStack.test.ts.snap +6 -0
  24. package/template/lambda-sqs-worker-cdk/package.json +5 -5
  25. package/template/lambda-sqs-worker-cdk/src/app.test.ts +76 -17
  26. package/template/lambda-sqs-worker-cdk/src/config.ts +1 -1
  27. package/template/lambda-sqs-worker-cdk/src/framework/handler.test.ts +35 -15
  28. package/template/lambda-sqs-worker-cdk/src/framework/logging.ts +22 -11
  29. package/template/lambda-sqs-worker-cdk/src/framework/validation.test.ts +6 -9
  30. package/template/lambda-sqs-worker-cdk/src/framework/validation.ts +3 -7
  31. package/template/lambda-sqs-worker-cdk/src/testing/types.ts +1 -1
  32. package/template/lambda-sqs-worker-cdk/src/types/jobScorer.ts +1 -1
  33. package/template/lambda-sqs-worker-cdk/src/types/pipelineEvents.ts +1 -1
  34. package/template/koa-rest-api/src/testing/logging.ts +0 -16
  35. package/template/lambda-sqs-worker-cdk/src/testing/logging.ts +0 -19
@@ -45,83 +45,83 @@ const subPatches = ({
45
45
  ` },
46
46
  {
47
47
  files: "**/Dockerfile*",
48
- regex: /^FROM(.*) (public.ecr.aws\/docker\/library\/)?node:([0-9]+(?:\.[0-9]+(?:\.[0-9]+)?)?)(-[a-z0-9]+)?(@sha256:[a-f0-9]{64})?( .*)?$/gm,
48
+ regex: () => /^FROM(.*) (public.ecr.aws\/docker\/library\/)?node:([0-9]+(?:\.[0-9]+(?:\.[0-9]+)?)?)(-[a-z0-9]+)?(@sha256:[a-f0-9]{64})?( .*)?$/gm,
49
49
  replace: `FROM$1 $2node:${nodeVersion}$4$6`
50
50
  },
51
51
  {
52
52
  files: "**/Dockerfile*",
53
- regex: /^FROM(.*) gcr.io\/distroless\/nodejs\d+-debian(\d+)(@sha256:[a-f0-9]{64})?(\.[^- \n]+)?(-[^ \n]+)?( .+|)$/gm,
53
+ regex: () => /^FROM(.*) gcr.io\/distroless\/nodejs\d+-debian(\d+)(@sha256:[a-f0-9]{64})?(\.[^- \n]+)?(-[^ \n]+)?( .+|)$/gm,
54
54
  replace: `FROM$1 gcr.io/distroless/nodejs${nodeVersion}-debian$2$4$5$6`
55
55
  },
56
56
  {
57
57
  files: "**/serverless*.y*ml",
58
- regex: /\bnodejs\d+.x\b/gm,
58
+ regex: () => /\bnodejs\d+.x\b/gm,
59
59
  tests: [import_checks.isPatchableServerlessVersion],
60
60
  replace: `nodejs${nodeVersion}.x`
61
61
  },
62
62
  {
63
63
  files: "**/serverless*.y*ml",
64
- regex: /\bnode\d+\b/gm,
64
+ regex: () => /\bnode\d+\b/gm,
65
65
  tests: [import_checks.isPatchableServerlessVersion],
66
66
  replace: `node${nodeVersion}`
67
67
  },
68
68
  {
69
69
  files: "**/infra/**/*.ts",
70
- regex: /NODEJS_\d+_X/g,
70
+ regex: () => /NODEJS_\d+_X/g,
71
71
  replace: `NODEJS_${nodeVersion}_X`
72
72
  },
73
73
  {
74
74
  files: "**/infra/**/*.ts",
75
- regex: /(target:\s*'node)(\d+)(.+)$/gm,
75
+ regex: () => /(target:\s*'node)(\d+)(.+)$/gm,
76
76
  replace: `$1${nodeVersion}$3`
77
77
  },
78
78
  {
79
79
  files: "**/.buildkite/*",
80
- regex: /(image: )(public.ecr.aws\/docker\/library\/)?(node:)[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?$/gm,
80
+ regex: () => /(image: )(public.ecr.aws\/docker\/library\/)?(node:)[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?$/gm,
81
81
  replace: `$1$2$3${nodeVersion}$5`
82
82
  },
83
83
  {
84
84
  files: ".node-version*",
85
- regex: /(\d+(?:\.\d+)*)/g,
85
+ regex: () => /(\d+(?:\.\d+)*)/g,
86
86
  replace: `${nodeVersion}`
87
87
  },
88
88
  {
89
89
  files: "**/package.json",
90
- regex: /(["']engines["']:\s*{[\s\S]*?["']node["']:\s*["']>=)(\d+(?:\.\d+)*)(['"]\s*})/gm,
90
+ regex: () => /(["']engines["']:\s*{[\s\S]*?["']node["']:\s*["']>=)(\d+(?:\.\d+)*)(['"]\s*})/gm,
91
91
  tests: [import_checks.isPatchableServerlessVersion, import_checks.isPatchableSkubaType],
92
92
  replace: `$1${nodeVersion}$3`
93
93
  },
94
94
  {
95
95
  files: "**/tsconfig*.json",
96
- regex: /("target":\s*")(ES\d+)"/gim,
96
+ regex: () => /("target":\s*")(ES\d+)"/gim,
97
97
  tests: [import_checks.isPatchableServerlessVersion, import_checks.isPatchableSkubaType],
98
98
  replace: `$1${ECMAScriptVersion}"`
99
99
  },
100
100
  {
101
101
  files: "**/tsconfig*.json",
102
- regex: /("lib":\s*\[)([\S\s]*?)(ES\d+)([\S\s]*?)(\])/gim,
102
+ regex: () => /("lib":\s*\[)([\S\s]*?)(ES\d+)([\S\s]*?)(\])/gim,
103
103
  tests: [import_checks.isPatchableServerlessVersion, import_checks.isPatchableSkubaType],
104
104
  replace: `$1$2${ECMAScriptVersion}$4$5`
105
105
  },
106
106
  {
107
107
  files: "**/docker-compose*.y*ml",
108
- regex: /(image: )(public.ecr.aws\/docker\/library\/)?(node:)[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?$/gm,
108
+ regex: () => /(image: )(public.ecr.aws\/docker\/library\/)?(node:)[0-9.]+(\.[^- \n]+)?(-[^ \n]+)?$/gm,
109
109
  replace: `$1$2$3${nodeVersion}$5`
110
110
  }
111
111
  ];
112
112
  const runSubPatch = async (dir, patch) => {
113
113
  const readFile = (0, import_project.createDestinationFileReader)(dir);
114
- const paths = patch.file ? [patch.file] : await (0, import_fast_glob.glob)(patch.files ?? [], { cwd: dir });
114
+ const paths = patch.file ? [patch.file] : await (0, import_fast_glob.glob)(patch.files ?? [], {
115
+ cwd: dir,
116
+ ignore: ["**/node_modules/**"]
117
+ });
115
118
  await Promise.all(
116
119
  paths.map(async (path) => {
117
- if (path.includes("node_modules")) {
118
- return;
119
- }
120
120
  const contents = await readFile(path);
121
121
  if (!contents) {
122
122
  return;
123
123
  }
124
- if (patch.regex && !patch.regex.test(contents)) {
124
+ if (patch.regex && !patch.regex().test(contents)) {
125
125
  return;
126
126
  }
127
127
  if (patch.tests) {
@@ -148,7 +148,7 @@ const writePatchedContents = async ({
148
148
  regex
149
149
  }) => await import_fs_extra.default.promises.writeFile(
150
150
  path,
151
- regex ? contents.replaceAll(regex, templated) : templated
151
+ regex ? contents.replaceAll(regex(), templated) : templated
152
152
  );
153
153
  const upgrade = async (versions, dir) => {
154
154
  for (const subPatch of subPatches(versions)) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/cli/migrate/nodeVersion/index.ts"],
4
- "sourcesContent": ["import { inspect } from 'util';\n\nimport { glob } from 'fast-glob';\nimport fs from 'fs-extra';\n\nimport { log } from '../../../utils/logging';\nimport { createDestinationFileReader } from '../../configure/analysis/project';\n\nimport {\n isPatchableNodeVersion,\n isPatchableServerlessVersion,\n isPatchableSkubaType,\n} from './checks';\n\ntype FileSelector =\n | { files: string; file?: never }\n | { file: string; files?: never };\n\ntype SubPatch = FileSelector & {\n tests?: Array<(path: string) => Promise<boolean>>;\n regex?: RegExp;\n replace: string;\n};\n\nconst subPatches = ({\n nodeVersion,\n ECMAScriptVersion,\n}: Versions): SubPatch[] => [\n { file: '.nvmrc', replace: `${nodeVersion}\\n` },\n {\n files: '**/Dockerfile*',\n\n regex:\n /^FROM(.*) (public.ecr.aws\\/docker\\/library\\/)?node:([0-9]+(?:\\.[0-9]+(?:\\.[0-9]+)?)?)(-[a-z0-9]+)?(@sha256:[a-f0-9]{64})?( .*)?$/gm,\n replace: `FROM$1 $2node:${nodeVersion}$4$6`,\n },\n {\n files: '**/Dockerfile*',\n regex:\n /^FROM(.*) gcr.io\\/distroless\\/nodejs\\d+-debian(\\d+)(@sha256:[a-f0-9]{64})?(\\.[^- \\n]+)?(-[^ \\n]+)?( .+|)$/gm,\n replace: `FROM$1 gcr.io/distroless/nodejs${nodeVersion}-debian$2$4$5$6`,\n },\n\n {\n files: '**/serverless*.y*ml',\n regex: /\\bnodejs\\d+.x\\b/gm,\n tests: [isPatchableServerlessVersion],\n replace: `nodejs${nodeVersion}.x`,\n },\n {\n files: '**/serverless*.y*ml',\n regex: /\\bnode\\d+\\b/gm,\n tests: [isPatchableServerlessVersion],\n replace: `node${nodeVersion}`,\n },\n\n {\n files: '**/infra/**/*.ts',\n regex: /NODEJS_\\d+_X/g,\n replace: `NODEJS_${nodeVersion}_X`,\n },\n {\n files: '**/infra/**/*.ts',\n regex: /(target:\\s*'node)(\\d+)(.+)$/gm,\n replace: `$1${nodeVersion}$3`,\n },\n\n {\n files: '**/.buildkite/*',\n regex:\n /(image: )(public.ecr.aws\\/docker\\/library\\/)?(node:)[0-9.]+(\\.[^- \\n]+)?(-[^ \\n]+)?$/gm,\n replace: `$1$2$3${nodeVersion}$5`,\n },\n {\n files: '.node-version*',\n regex: /(\\d+(?:\\.\\d+)*)/g,\n replace: `${nodeVersion}`,\n },\n\n {\n files: '**/package.json',\n regex:\n /([\"']engines[\"']:\\s*{[\\s\\S]*?[\"']node[\"']:\\s*[\"']>=)(\\d+(?:\\.\\d+)*)(['\"]\\s*})/gm,\n tests: [isPatchableServerlessVersion, isPatchableSkubaType],\n replace: `$1${nodeVersion}$3`,\n },\n\n {\n files: '**/tsconfig*.json',\n regex: /(\"target\":\\s*\")(ES\\d+)\"/gim,\n tests: [isPatchableServerlessVersion, isPatchableSkubaType],\n replace: `$1${ECMAScriptVersion}\"`,\n },\n {\n files: '**/tsconfig*.json',\n regex: /(\"lib\":\\s*\\[)([\\S\\s]*?)(ES\\d+)([\\S\\s]*?)(\\])/gim,\n tests: [isPatchableServerlessVersion, isPatchableSkubaType],\n replace: `$1$2${ECMAScriptVersion}$4$5`,\n },\n\n {\n files: '**/docker-compose*.y*ml',\n regex:\n /(image: )(public.ecr.aws\\/docker\\/library\\/)?(node:)[0-9.]+(\\.[^- \\n]+)?(-[^ \\n]+)?$/gm,\n\n replace: `$1$2$3${nodeVersion}$5`,\n },\n];\n\ntype Versions = {\n nodeVersion: number;\n ECMAScriptVersion: string;\n};\n\nconst runSubPatch = async (dir: string, patch: SubPatch) => {\n const readFile = createDestinationFileReader(dir);\n const paths = patch.file\n ? [patch.file]\n : await glob(patch.files ?? [], { cwd: dir });\n\n await Promise.all(\n paths.map(async (path) => {\n if (path.includes('node_modules')) {\n return;\n }\n const contents = await readFile(path);\n if (!contents) {\n return;\n }\n\n if (patch.regex && !patch.regex.test(contents)) {\n return;\n }\n\n if (patch.tests) {\n const results = await Promise.all(\n patch.tests.map((test) => test(path)),\n );\n if (!results.every(Boolean)) {\n return;\n }\n }\n\n await writePatchedContents({\n path,\n contents,\n templated: patch.replace,\n regex: patch.regex,\n });\n }),\n );\n};\n\nconst writePatchedContents = async ({\n path,\n contents,\n templated,\n regex,\n}: {\n path: string;\n contents: string;\n templated: string;\n regex?: RegExp;\n}) =>\n await fs.promises.writeFile(\n path,\n regex ? contents.replaceAll(regex, templated) : templated,\n );\n\nconst upgrade = async (versions: Versions, dir: string) => {\n for (const subPatch of subPatches(versions)) {\n await runSubPatch(dir, subPatch);\n }\n};\n\nexport const nodeVersionMigration = async (\n {\n nodeVersion,\n ECMAScriptVersion,\n }: {\n nodeVersion: number;\n ECMAScriptVersion: string;\n },\n dir = process.cwd(),\n) => {\n log.ok(`Upgrading to Node.js ${nodeVersion}`);\n try {\n if (!(await isPatchableNodeVersion(nodeVersion, dir))) {\n throw new Error('Node.js version is not patchable');\n }\n\n await upgrade({ nodeVersion, ECMAScriptVersion }, dir);\n\n log.ok('Upgraded to Node.js', nodeVersion);\n } catch (error) {\n log.err('Failed to upgrade');\n log.subtle(inspect(error));\n process.exitCode = 1;\n }\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { inspect } from 'util';\n\nimport { glob } from 'fast-glob';\nimport fs from 'fs-extra';\n\nimport { log } from '../../../utils/logging';\nimport { createDestinationFileReader } from '../../configure/analysis/project';\n\nimport {\n isPatchableNodeVersion,\n isPatchableServerlessVersion,\n isPatchableSkubaType,\n} from './checks';\n\ntype FileSelector =\n | { files: string; file?: never }\n | { file: string; files?: never };\n\ntype SubPatch = FileSelector & {\n tests?: Array<(path: string) => Promise<boolean>>;\n regex?: () => RegExp;\n replace: string;\n};\n\nconst subPatches = ({\n nodeVersion,\n ECMAScriptVersion,\n}: Versions): SubPatch[] => [\n { file: '.nvmrc', replace: `${nodeVersion}\\n` },\n {\n files: '**/Dockerfile*',\n\n regex: () =>\n /^FROM(.*) (public.ecr.aws\\/docker\\/library\\/)?node:([0-9]+(?:\\.[0-9]+(?:\\.[0-9]+)?)?)(-[a-z0-9]+)?(@sha256:[a-f0-9]{64})?( .*)?$/gm,\n replace: `FROM$1 $2node:${nodeVersion}$4$6`,\n },\n {\n files: '**/Dockerfile*',\n regex: () =>\n /^FROM(.*) gcr.io\\/distroless\\/nodejs\\d+-debian(\\d+)(@sha256:[a-f0-9]{64})?(\\.[^- \\n]+)?(-[^ \\n]+)?( .+|)$/gm,\n replace: `FROM$1 gcr.io/distroless/nodejs${nodeVersion}-debian$2$4$5$6`,\n },\n\n {\n files: '**/serverless*.y*ml',\n regex: () => /\\bnodejs\\d+.x\\b/gm,\n tests: [isPatchableServerlessVersion],\n replace: `nodejs${nodeVersion}.x`,\n },\n {\n files: '**/serverless*.y*ml',\n regex: () => /\\bnode\\d+\\b/gm,\n tests: [isPatchableServerlessVersion],\n replace: `node${nodeVersion}`,\n },\n\n {\n files: '**/infra/**/*.ts',\n regex: () => /NODEJS_\\d+_X/g,\n replace: `NODEJS_${nodeVersion}_X`,\n },\n {\n files: '**/infra/**/*.ts',\n regex: () => /(target:\\s*'node)(\\d+)(.+)$/gm,\n replace: `$1${nodeVersion}$3`,\n },\n\n {\n files: '**/.buildkite/*',\n regex: () =>\n /(image: )(public.ecr.aws\\/docker\\/library\\/)?(node:)[0-9.]+(\\.[^- \\n]+)?(-[^ \\n]+)?$/gm,\n replace: `$1$2$3${nodeVersion}$5`,\n },\n {\n files: '.node-version*',\n regex: () => /(\\d+(?:\\.\\d+)*)/g,\n replace: `${nodeVersion}`,\n },\n\n {\n files: '**/package.json',\n regex: () =>\n /([\"']engines[\"']:\\s*{[\\s\\S]*?[\"']node[\"']:\\s*[\"']>=)(\\d+(?:\\.\\d+)*)(['\"]\\s*})/gm,\n tests: [isPatchableServerlessVersion, isPatchableSkubaType],\n replace: `$1${nodeVersion}$3`,\n },\n\n {\n files: '**/tsconfig*.json',\n regex: () => /(\"target\":\\s*\")(ES\\d+)\"/gim,\n tests: [isPatchableServerlessVersion, isPatchableSkubaType],\n replace: `$1${ECMAScriptVersion}\"`,\n },\n {\n files: '**/tsconfig*.json',\n regex: () => /(\"lib\":\\s*\\[)([\\S\\s]*?)(ES\\d+)([\\S\\s]*?)(\\])/gim,\n tests: [isPatchableServerlessVersion, isPatchableSkubaType],\n replace: `$1$2${ECMAScriptVersion}$4$5`,\n },\n\n {\n files: '**/docker-compose*.y*ml',\n regex: () =>\n /(image: )(public.ecr.aws\\/docker\\/library\\/)?(node:)[0-9.]+(\\.[^- \\n]+)?(-[^ \\n]+)?$/gm,\n\n replace: `$1$2$3${nodeVersion}$5`,\n },\n];\n\ntype Versions = {\n nodeVersion: number;\n ECMAScriptVersion: string;\n};\n\nconst runSubPatch = async (dir: string, patch: SubPatch) => {\n const readFile = createDestinationFileReader(dir);\n const paths = patch.file\n ? [patch.file]\n : await glob(patch.files ?? [], {\n cwd: dir,\n ignore: ['**/node_modules/**'],\n });\n\n await Promise.all(\n paths.map(async (path) => {\n const contents = await readFile(path);\n if (!contents) {\n return;\n }\n\n if (patch.regex && !patch.regex().test(contents)) {\n return;\n }\n\n if (patch.tests) {\n const results = await Promise.all(\n patch.tests.map((test) => test(path)),\n );\n if (!results.every(Boolean)) {\n return;\n }\n }\n\n await writePatchedContents({\n path,\n contents,\n templated: patch.replace,\n regex: patch.regex,\n });\n }),\n );\n};\n\nconst writePatchedContents = async ({\n path,\n contents,\n templated,\n regex,\n}: {\n path: string;\n contents: string;\n templated: string;\n regex?: () => RegExp;\n}) =>\n await fs.promises.writeFile(\n path,\n regex ? contents.replaceAll(regex(), templated) : templated,\n );\n\nconst upgrade = async (versions: Versions, dir: string) => {\n for (const subPatch of subPatches(versions)) {\n await runSubPatch(dir, subPatch);\n }\n};\n\nexport const nodeVersionMigration = async (\n {\n nodeVersion,\n ECMAScriptVersion,\n }: {\n nodeVersion: number;\n ECMAScriptVersion: string;\n },\n dir = process.cwd(),\n) => {\n log.ok(`Upgrading to Node.js ${nodeVersion}`);\n try {\n if (!(await isPatchableNodeVersion(nodeVersion, dir))) {\n throw new Error('Node.js version is not patchable');\n }\n\n await upgrade({ nodeVersion, ECMAScriptVersion }, dir);\n\n log.ok('Upgraded to Node.js', nodeVersion);\n } catch (error) {\n log.err('Failed to upgrade');\n log.subtle(inspect(error));\n process.exitCode = 1;\n }\n};\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAwB;AAExB,uBAAqB;AACrB,sBAAe;AAEf,qBAAoB;AACpB,qBAA4C;AAE5C,oBAIO;AAYP,MAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AACF,MAA4B;AAAA,EAC1B,EAAE,MAAM,UAAU,SAAS,GAAG,WAAW;AAAA,EAAK;AAAA,EAC9C;AAAA,IACE,OAAO;AAAA,IAEP,OAAO,MACL;AAAA,IACF,SAAS,iBAAiB,WAAW;AAAA,EACvC;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MACL;AAAA,IACF,SAAS,kCAAkC,WAAW;AAAA,EACxD;AAAA,EAEA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MAAM;AAAA,IACb,OAAO,CAAC,0CAA4B;AAAA,IACpC,SAAS,SAAS,WAAW;AAAA,EAC/B;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MAAM;AAAA,IACb,OAAO,CAAC,0CAA4B;AAAA,IACpC,SAAS,OAAO,WAAW;AAAA,EAC7B;AAAA,EAEA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MAAM;AAAA,IACb,SAAS,UAAU,WAAW;AAAA,EAChC;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MAAM;AAAA,IACb,SAAS,KAAK,WAAW;AAAA,EAC3B;AAAA,EAEA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MACL;AAAA,IACF,SAAS,SAAS,WAAW;AAAA,EAC/B;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MAAM;AAAA,IACb,SAAS,GAAG,WAAW;AAAA,EACzB;AAAA,EAEA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MACL;AAAA,IACF,OAAO,CAAC,4CAA8B,kCAAoB;AAAA,IAC1D,SAAS,KAAK,WAAW;AAAA,EAC3B;AAAA,EAEA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MAAM;AAAA,IACb,OAAO,CAAC,4CAA8B,kCAAoB;AAAA,IAC1D,SAAS,KAAK,iBAAiB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MAAM;AAAA,IACb,OAAO,CAAC,4CAA8B,kCAAoB;AAAA,IAC1D,SAAS,OAAO,iBAAiB;AAAA,EACnC;AAAA,EAEA;AAAA,IACE,OAAO;AAAA,IACP,OAAO,MACL;AAAA,IAEF,SAAS,SAAS,WAAW;AAAA,EAC/B;AACF;AAOA,MAAM,cAAc,OAAO,KAAa,UAAoB;AAC1D,QAAM,eAAW,4CAA4B,GAAG;AAChD,QAAM,QAAQ,MAAM,OAChB,CAAC,MAAM,IAAI,IACX,UAAM,uBAAK,MAAM,SAAS,CAAC,GAAG;AAAA,IAC5B,KAAK;AAAA,IACL,QAAQ,CAAC,oBAAoB;AAAA,EAC/B,CAAC;AAEL,QAAM,QAAQ;AAAA,IACZ,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,WAAW,MAAM,SAAS,IAAI;AACpC,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AAEA,UAAI,MAAM,SAAS,CAAC,MAAM,MAAM,EAAE,KAAK,QAAQ,GAAG;AAChD;AAAA,MACF;AAEA,UAAI,MAAM,OAAO;AACf,cAAM,UAAU,MAAM,QAAQ;AAAA,UAC5B,MAAM,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;AAAA,QACtC;AACA,YAAI,CAAC,QAAQ,MAAM,OAAO,GAAG;AAC3B;AAAA,QACF;AAAA,MACF;AAEA,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA;AAAA,QACA,WAAW,MAAM;AAAA,QACjB,OAAO,MAAM;AAAA,MACf,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;AAEA,MAAM,uBAAuB,OAAO;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAME,MAAM,gBAAAA,QAAG,SAAS;AAAA,EAChB;AAAA,EACA,QAAQ,SAAS,WAAW,MAAM,GAAG,SAAS,IAAI;AACpD;AAEF,MAAM,UAAU,OAAO,UAAoB,QAAgB;AACzD,aAAW,YAAY,WAAW,QAAQ,GAAG;AAC3C,UAAM,YAAY,KAAK,QAAQ;AAAA,EACjC;AACF;AAEO,MAAM,uBAAuB,OAClC;AAAA,EACE;AAAA,EACA;AACF,GAIA,MAAM,QAAQ,IAAI,MACf;AACH,qBAAI,GAAG,wBAAwB,WAAW,EAAE;AAC5C,MAAI;AACF,QAAI,CAAE,UAAM,sCAAuB,aAAa,GAAG,GAAI;AACrD,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAEA,UAAM,QAAQ,EAAE,aAAa,kBAAkB,GAAG,GAAG;AAErD,uBAAI,GAAG,uBAAuB,WAAW;AAAA,EAC3C,SAAS,OAAO;AACd,uBAAI,IAAI,mBAAmB;AAC3B,uBAAI,WAAO,qBAAQ,KAAK,CAAC;AACzB,YAAQ,WAAW;AAAA,EACrB;AACF;",
6
6
  "names": ["fs"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skuba",
3
- "version": "11.1.0-jest30-20250620003740",
3
+ "version": "11.1.0",
4
4
  "private": false,
5
5
  "description": "SEEK development toolkit for backend applications and packages",
6
6
  "homepage": "https://github.com/seek-oss/skuba#readme",
@@ -55,7 +55,7 @@
55
55
  "@jest/types": "^30.0.0",
56
56
  "@octokit/graphql": "^9.0.0",
57
57
  "@octokit/graphql-schema": "^15.3.0",
58
- "@octokit/rest": "^21.0.0",
58
+ "@octokit/rest": "^22.0.0",
59
59
  "@octokit/types": "^14.0.0",
60
60
  "@types/jest": "^30.0.0",
61
61
  "@types/node": "^22.0.0",
@@ -85,7 +85,7 @@
85
85
  "npm-run-path": "^4.0.1",
86
86
  "npm-which": "^3.0.1",
87
87
  "picomatch": "^4.0.0",
88
- "prettier": "~3.5.0",
88
+ "prettier": "~3.6.0",
89
89
  "prettier-plugin-packagejson": "^2.4.10",
90
90
  "read-pkg-up": "^7.0.1",
91
91
  "semantic-release": "^24.2.3",
@@ -97,37 +97,37 @@
97
97
  "tsconfig-seek": "2.0.0",
98
98
  "tsx": "^4.16.2",
99
99
  "typescript": "~5.8.0",
100
- "zod": "^3.22.4",
101
- "eslint-config-skuba": "6.1.0"
100
+ "zod": "^3.25.67",
101
+ "eslint-config-skuba": "6.1.1"
102
102
  },
103
103
  "devDependencies": {
104
- "@changesets/cli": "2.29.3",
104
+ "@changesets/cli": "2.29.5",
105
105
  "@changesets/get-github-info": "0.6.0",
106
106
  "@jest/reporters": "30.0.2",
107
107
  "@jest/test-result": "30.0.2",
108
108
  "@types/ejs": "3.1.5",
109
- "@types/express": "5.0.1",
109
+ "@types/express": "5.0.3",
110
110
  "@types/fs-extra": "11.0.4",
111
111
  "@types/koa": "2.15.0",
112
112
  "@types/lodash.mergewith": "4.6.9",
113
113
  "@types/minimist": "1.2.5",
114
114
  "@types/module-alias": "2.0.4",
115
- "@types/npm-registry-fetch": "8.0.7",
116
- "@types/npm-which": "3.0.3",
115
+ "@types/npm-registry-fetch": "8.0.8",
116
+ "@types/npm-which": "3.0.4",
117
117
  "@types/picomatch": "4.0.0",
118
118
  "@types/semver": "7.7.0",
119
119
  "@types/supertest": "6.0.3",
120
- "enhanced-resolve": "5.18.1",
120
+ "enhanced-resolve": "5.18.2",
121
121
  "express": "5.1.0",
122
- "fastify": "5.3.2",
123
- "jest-diff": "30.0.2",
122
+ "fastify": "5.4.0",
123
+ "jest-diff": "30.0.3",
124
124
  "jsonfile": "6.1.0",
125
125
  "koa": "3.0.0",
126
- "memfs": "4.17.1",
126
+ "memfs": "4.17.2",
127
127
  "remark-cli": "12.0.1",
128
128
  "remark-preset-lint-recommended": "7.0.1",
129
- "semver": "7.7.1",
130
- "supertest": "7.1.0",
129
+ "semver": "7.7.2",
130
+ "supertest": "7.1.1",
131
131
  "type-fest": "2.19.0"
132
132
  },
133
133
  "peerDependencies": {
@@ -48,7 +48,7 @@ steps:
48
48
  GET_NPM_TOKEN: please
49
49
  plugins:
50
50
  - *docker-ecr-cache
51
- - docker-compose#v5.9.0:
51
+ - docker-compose#v5.10.0:
52
52
  run: app
53
53
  environment:
54
54
  - GITHUB_API_TOKEN
@@ -1,4 +1,4 @@
1
- # syntax=docker/dockerfile:1.16
1
+ # syntax=docker/dockerfile:1.17
2
2
 
3
3
  FROM public.ecr.aws/docker/library/node:22-alpine AS dev-deps
4
4
 
@@ -28,7 +28,7 @@
28
28
  "skuba": "*",
29
29
  "supertest": "^7.0.0"
30
30
  },
31
- "packageManager": "pnpm@10.12.1",
31
+ "packageManager": "pnpm@10.12.4",
32
32
  "engines": {
33
33
  "node": ">=22"
34
34
  }
@@ -29,7 +29,7 @@ steps:
29
29
  GET_NPM_TOKEN: please
30
30
  plugins:
31
31
  - *docker-ecr-cache
32
- - docker-compose#v5.9.0:
32
+ - docker-compose#v5.10.0:
33
33
  run: app
34
34
  environment:
35
35
  - GITHUB_API_TOKEN
@@ -1,4 +1,4 @@
1
- # syntax=docker/dockerfile:1.16
1
+ # syntax=docker/dockerfile:1.17
2
2
 
3
3
  FROM public.ecr.aws/docker/library/node:22-alpine AS dev-deps
4
4
 
@@ -17,9 +17,9 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/node": "^22.13.10",
20
- "skuba": "11.1.0-jest30-20250620003740"
20
+ "skuba": "*"
21
21
  },
22
- "packageManager": "pnpm@10.12.1",
22
+ "packageManager": "pnpm@10.12.4",
23
23
  "engines": {
24
24
  "node": ">=22"
25
25
  }
@@ -48,7 +48,7 @@ steps:
48
48
  GET_NPM_TOKEN: please
49
49
  plugins:
50
50
  - *docker-ecr-cache
51
- - docker-compose#v5.9.0:
51
+ - docker-compose#v5.10.0:
52
52
  run: app
53
53
  environment:
54
54
  - GITHUB_API_TOKEN
@@ -1,4 +1,4 @@
1
- # syntax=docker/dockerfile:1.16
1
+ # syntax=docker/dockerfile:1.17
2
2
 
3
3
  FROM public.ecr.aws/docker/library/node:22-alpine AS dev-deps
4
4
 
@@ -13,15 +13,15 @@
13
13
  "test:watch": "skuba test --watch"
14
14
  },
15
15
  "dependencies": {
16
- "@koa/bodyparser": "^5.1.1",
16
+ "@koa/bodyparser": "^6.0.0",
17
17
  "@koa/router": "^13.0.0",
18
18
  "@opentelemetry/api": "^1.9.0",
19
19
  "@opentelemetry/core": "^2.0.0",
20
- "@opentelemetry/exporter-trace-otlp-grpc": "^0.201.0",
21
- "@opentelemetry/instrumentation-aws-sdk": "^0.53.0",
22
- "@opentelemetry/instrumentation-http": "^0.201.0",
20
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.202.0",
21
+ "@opentelemetry/instrumentation-aws-sdk": "^0.54.0",
22
+ "@opentelemetry/instrumentation-http": "^0.202.0",
23
23
  "@opentelemetry/propagator-b3": "^2.0.0",
24
- "@opentelemetry/sdk-node": "^0.201.0",
24
+ "@opentelemetry/sdk-node": "^0.202.0",
25
25
  "@seek/logger": "^10.0.0",
26
26
  "hot-shots": "^10.0.0",
27
27
  "koa": "^2.16.1",
@@ -29,7 +29,7 @@
29
29
  "seek-datadog-custom-metrics": "^4.6.3",
30
30
  "seek-koala": "^7.0.0",
31
31
  "skuba-dive": "^2.0.0",
32
- "zod": "^3.19.1"
32
+ "zod": "^3.25.67"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/chance": "^1.1.3",
@@ -44,7 +44,7 @@
44
44
  "skuba": "*",
45
45
  "supertest": "^7.0.0"
46
46
  },
47
- "packageManager": "pnpm@10.12.1",
47
+ "packageManager": "pnpm@10.12.4",
48
48
  "engines": {
49
49
  "node": ">=22"
50
50
  }
@@ -27,7 +27,7 @@ describe('postJobHandler', () => {
27
27
  .expect(422)
28
28
  .expect(({ text }) =>
29
29
  expect(text).toMatchInlineSnapshot(
30
- `"{"message":"Input validation failed","invalidFields":{"/hirer":"Required"}}"`,
30
+ `"{"message":"Input validation failed","invalidFields":{"/hirer":"Invalid input: expected object, received undefined"}}"`,
31
31
  ),
32
32
  );
33
33
  });
@@ -32,7 +32,7 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
32
32
  }),
33
33
 
34
34
  test: () => ({
35
- logLevel: Env.string('LOG_LEVEL', { default: 'silent' }),
35
+ logLevel: 'debug',
36
36
  name: '<%- serviceName %>',
37
37
  version: 'test',
38
38
 
@@ -1,4 +1,4 @@
1
- import createLogger from '@seek/logger';
1
+ import createLogger, { createDestination } from '@seek/logger';
2
2
  import { RequestLogging } from 'seek-koala';
3
3
 
4
4
  import { config } from 'src/config';
@@ -8,18 +8,27 @@ const { createContextMiddleware, mixin } =
8
8
 
9
9
  export const contextMiddleware = createContextMiddleware();
10
10
 
11
- export const logger = createLogger({
12
- base: {
13
- environment: config.environment,
14
- version: config.version,
15
- },
11
+ const { destination, stdoutMock } = createDestination({
12
+ mock: config.environment === 'test',
13
+ });
16
14
 
17
- mixin,
15
+ export { stdoutMock };
18
16
 
19
- level: config.logLevel,
17
+ export const logger = createLogger(
18
+ {
19
+ base: {
20
+ environment: config.environment,
21
+ version: config.version,
22
+ },
20
23
 
21
- name: config.name,
24
+ mixin,
22
25
 
23
- transport:
24
- config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
25
- });
26
+ level: config.logLevel,
27
+
28
+ name: config.name,
29
+
30
+ transport:
31
+ config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
32
+ },
33
+ destination,
34
+ );
@@ -1,11 +1,12 @@
1
1
  import Router from '@koa/router';
2
2
 
3
- import { logger } from 'src/testing/logging';
4
3
  import { metricsClient } from 'src/testing/metrics';
5
4
  import { agentFromRouter } from 'src/testing/server';
6
5
  import { chance } from 'src/testing/types';
7
6
  import type { Middleware } from 'src/types/koa';
8
7
 
8
+ import { stdoutMock } from './logging';
9
+
9
10
  const middleware = jest.fn<void, Parameters<Middleware>>();
10
11
 
11
12
  const router = new Router()
@@ -15,10 +16,10 @@ const router = new Router()
15
16
  const agent = agentFromRouter(router);
16
17
 
17
18
  describe('createApp', () => {
18
- beforeAll(logger.spy);
19
-
20
- afterEach(metricsClient.clear);
21
- afterEach(logger.clear);
19
+ afterEach(() => {
20
+ metricsClient.clear();
21
+ stdoutMock.clear();
22
+ });
22
23
 
23
24
  it('handles root route', async () => {
24
25
  middleware.mockImplementation((ctx) => (ctx.body = ''));
@@ -29,9 +30,7 @@ describe('createApp', () => {
29
30
  .expect('server', /.+/)
30
31
  .expect('x-api-version', /.+/);
31
32
 
32
- expect(logger.error).not.toHaveBeenCalled();
33
-
34
- expect(logger.info).not.toHaveBeenCalled();
33
+ expect(stdoutMock.calls).toHaveLength(0);
35
34
 
36
35
  metricsClient.expectTagSubset(['env:test', 'version:test']);
37
36
  metricsClient.expectTagSubset([
@@ -51,9 +50,7 @@ describe('createApp', () => {
51
50
  .expect('server', /.+/)
52
51
  .expect('x-api-version', /.+/);
53
52
 
54
- expect(logger.error).not.toHaveBeenCalled();
55
-
56
- expect(logger.info).not.toHaveBeenCalled();
53
+ expect(stdoutMock.calls).toHaveLength(0);
57
54
 
58
55
  metricsClient.expectTagSubset([
59
56
  'http_method:put',
@@ -72,13 +69,15 @@ describe('createApp', () => {
72
69
  .expect('server', /.+/)
73
70
  .expect('x-api-version', /.+/);
74
71
 
75
- expect(logger.error).not.toHaveBeenCalled();
76
-
77
- expect(logger.info).toHaveBeenNthCalledWith(
78
- 1,
79
- expect.objectContaining({ status: 404 }),
80
- 'Client error',
81
- );
72
+ expect(stdoutMock.calls).toMatchObject([
73
+ {
74
+ level: 30,
75
+ method: 'GET',
76
+ msg: 'Client error',
77
+ status: 404,
78
+ url: '/unknown',
79
+ },
80
+ ]);
82
81
 
83
82
  metricsClient.expectTagSubset([
84
83
  'http_method:get',
@@ -102,13 +101,16 @@ describe('createApp', () => {
102
101
  .expect('server', /.+/)
103
102
  .expect('x-api-version', /.+/);
104
103
 
105
- expect(logger.error).not.toHaveBeenCalled();
106
-
107
- expect(logger.info).toHaveBeenNthCalledWith(
108
- 1,
109
- expect.objectContaining({ status: 400 }),
110
- 'Client error',
111
- );
104
+ expect(stdoutMock.calls).toMatchObject([
105
+ {
106
+ level: 30,
107
+ method: 'GET',
108
+ msg: 'Client error',
109
+ route: '/',
110
+ status: 400,
111
+ url: '/',
112
+ },
113
+ ]);
112
114
 
113
115
  metricsClient.expectTagSubset([
114
116
  'http_method:get',
@@ -129,13 +131,20 @@ describe('createApp', () => {
129
131
  .expect('server', /.+/)
130
132
  .expect('x-api-version', /.+/);
131
133
 
132
- expect(logger.error).not.toHaveBeenCalled();
133
-
134
- expect(logger.info).toHaveBeenNthCalledWith(
135
- 1,
136
- expect.objectContaining({ err: expect.any(Error), status: 400 }),
137
- 'Client error',
138
- );
134
+ expect(stdoutMock.calls).toMatchObject([
135
+ {
136
+ err: {
137
+ statusCode: 400,
138
+ type: 'BadRequestError',
139
+ },
140
+ level: 30,
141
+ method: 'GET',
142
+ msg: 'Client error',
143
+ route: '/',
144
+ status: 400,
145
+ url: '/',
146
+ },
147
+ ]);
139
148
 
140
149
  metricsClient.expectTagSubset([
141
150
  'http_method:get',
@@ -156,13 +165,20 @@ describe('createApp', () => {
156
165
  .expect('server', /.+/)
157
166
  .expect('x-api-version', /.+/);
158
167
 
159
- expect(logger.error).toHaveBeenNthCalledWith(
160
- 1,
161
- expect.objectContaining({ err: expect.any(Error), status: 500 }),
162
- 'Server error',
163
- );
164
-
165
- expect(logger.info).not.toHaveBeenCalled();
168
+ expect(stdoutMock.calls).toMatchObject([
169
+ {
170
+ err: {
171
+ statusCode: 500,
172
+ type: 'InternalServerError',
173
+ },
174
+ level: 50,
175
+ method: 'GET',
176
+ msg: 'Server error',
177
+ route: '/',
178
+ status: 500,
179
+ url: '/',
180
+ },
181
+ ]);
166
182
 
167
183
  metricsClient.expectTagSubset([
168
184
  'http_method:get',
@@ -185,13 +201,20 @@ describe('createApp', () => {
185
201
  .expect('server', /.+/)
186
202
  .expect('x-api-version', /.+/);
187
203
 
188
- expect(logger.error).toHaveBeenNthCalledWith(
189
- 1,
190
- expect.objectContaining({ err, status: 500 }),
191
- 'Server error',
192
- );
193
-
194
- expect(logger.info).not.toHaveBeenCalled();
204
+ expect(stdoutMock.calls).toMatchObject([
205
+ {
206
+ err: {
207
+ message: err.message,
208
+ type: 'Error',
209
+ },
210
+ level: 50,
211
+ method: 'GET',
212
+ msg: 'Server error',
213
+ route: '/',
214
+ status: 500,
215
+ url: '/',
216
+ },
217
+ ]);
195
218
 
196
219
  metricsClient.expectTagSubset([
197
220
  'http_method:get',
@@ -212,13 +235,17 @@ describe('createApp', () => {
212
235
  .expect('server', /.+/)
213
236
  .expect('x-api-version', /.+/);
214
237
 
215
- expect(logger.error).toHaveBeenNthCalledWith(
216
- 1,
217
- expect.objectContaining({ err: null, status: 500 }),
218
- 'Server error',
219
- );
220
-
221
- expect(logger.info).not.toHaveBeenCalled();
238
+ expect(stdoutMock.calls).toMatchObject([
239
+ {
240
+ err: null,
241
+ level: 50,
242
+ method: 'GET',
243
+ msg: 'Server error',
244
+ route: '/',
245
+ status: 500,
246
+ url: '/',
247
+ },
248
+ ]);
222
249
 
223
250
  metricsClient.expectTagSubset([
224
251
  'http_method:get',
@@ -241,13 +268,17 @@ describe('createApp', () => {
241
268
  .expect('server', /.+/)
242
269
  .expect('x-api-version', /.+/);
243
270
 
244
- expect(logger.error).toHaveBeenNthCalledWith(
245
- 1,
246
- expect.objectContaining({ err, status: 500 }),
247
- 'Server error',
248
- );
249
-
250
- expect(logger.info).not.toHaveBeenCalled();
271
+ expect(stdoutMock.calls).toMatchObject([
272
+ {
273
+ err,
274
+ level: 50,
275
+ method: 'GET',
276
+ msg: 'Server error',
277
+ route: '/',
278
+ status: 500,
279
+ url: '/',
280
+ },
281
+ ]);
251
282
 
252
283
  metricsClient.expectTagSubset([
253
284
  'http_method:get',