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
@@ -43,15 +43,15 @@ describe('validate', () => {
43
43
  .expect(422)
44
44
  .expect(({ body }) =>
45
45
  expect(body).toMatchInlineSnapshot(`
46
- {
47
- "invalidFields": {
48
- "~union0/id": "Expected string, received null",
49
- "~union1/id": "Expected number, received null",
50
- "~union1/summary": "Required",
51
- },
52
- "message": "Input validation failed",
53
- }
54
- `),
46
+ {
47
+ "invalidFields": {
48
+ "~union0/id": "Invalid input: expected string, received null",
49
+ "~union1/id": "Invalid input: expected number, received null",
50
+ "~union1/summary": "Invalid input: expected string, received undefined",
51
+ },
52
+ "message": "Input validation failed",
53
+ }
54
+ `),
55
55
  );
56
56
  });
57
57
 
@@ -62,17 +62,17 @@ describe('validate', () => {
62
62
  .expect(422)
63
63
  .expect(({ body }) =>
64
64
  expect(body).toMatchInlineSnapshot(`
65
- {
66
- "invalidFields": {
67
- "~union0/description~union0": "Required",
68
- "~union0/description~union1": "Required",
69
- "~union0/id": "Required",
70
- "~union1/id": "Required",
71
- "~union1/summary": "Required",
72
- },
73
- "message": "Input validation failed",
74
- }
75
- `),
65
+ {
66
+ "invalidFields": {
67
+ "~union0/description~union0": "Invalid input: expected string, received undefined",
68
+ "~union0/description~union1": "Invalid input: expected object, received undefined",
69
+ "~union0/id": "Invalid input: expected string, received undefined",
70
+ "~union1/id": "Invalid input: expected number, received undefined",
71
+ "~union1/summary": "Invalid input: expected string, received undefined",
72
+ },
73
+ "message": "Input validation failed",
74
+ }
75
+ `),
76
76
  ));
77
77
 
78
78
  it('blocks invalid nested union prop', () => {
@@ -89,17 +89,17 @@ describe('validate', () => {
89
89
  .expect(422)
90
90
  .expect(({ body }) =>
91
91
  expect(body).toMatchInlineSnapshot(`
92
- {
93
- "invalidFields": {
94
- "~union0/description~union0": "Expected string, received object",
95
- "~union0/description~union1/content": "Required",
96
- "~union0/id": "Expected string, received null",
97
- "~union1/id": "Expected number, received null",
98
- "~union1/summary": "Required",
99
- },
100
- "message": "Input validation failed",
101
- }
102
- `),
92
+ {
93
+ "invalidFields": {
94
+ "~union0/description~union0": "Invalid input: expected string, received object",
95
+ "~union0/description~union1/content": "Invalid input: expected string, received undefined",
96
+ "~union0/id": "Invalid input: expected string, received null",
97
+ "~union1/id": "Invalid input: expected number, received null",
98
+ "~union1/summary": "Invalid input: expected string, received undefined",
99
+ },
100
+ "message": "Input validation failed",
101
+ }
102
+ `),
103
103
  );
104
104
  });
105
105
  });
@@ -1,5 +1,5 @@
1
1
  import { ErrorMiddleware } from 'seek-koala';
2
- import { ZodIssueCode, type z } from 'zod';
2
+ import type { core, z } from 'zod/v4';
3
3
 
4
4
  import type { Context } from 'src/types/koa';
5
5
 
@@ -35,41 +35,38 @@ type InvalidFields = Record<string, string>;
35
35
  * @see [union error example](./validation.test.ts)
36
36
  */
37
37
  const parseInvalidFieldsFromError = (err: z.ZodError): InvalidFields =>
38
- Object.fromEntries(parseTuples(err, {}));
38
+ Object.fromEntries(parseTuples(err.issues));
39
39
 
40
40
  const parseTuples = (
41
- { errors }: z.ZodError,
42
- unions: Record<number, number[]>,
41
+ errors: core.$ZodIssue[],
42
+ basePath: Array<string | number | symbol> = [],
43
+ unions: Record<number, number[]> = {},
43
44
  ): Array<readonly [string, string]> =>
44
45
  errors.flatMap((issue) => {
45
- if (issue.code === ZodIssueCode.invalid_union) {
46
- return issue.unionErrors.flatMap((err, idx) =>
47
- parseTuples(err, {
46
+ if (issue.code === 'invalid_union') {
47
+ return issue.errors.flatMap((err, idx) =>
48
+ parseTuples(err, issue.path, {
48
49
  ...unions,
49
50
  [issue.path.length]: [...(unions[issue.path.length] ?? []), idx],
50
51
  }),
51
52
  );
52
53
  }
53
54
 
54
- const path = ['', ...issue.path]
55
+ const path = ['', ...basePath, ...issue.path]
55
56
  .map((prop, idx) => [prop, ...(unions[idx] ?? [])].join('~union'))
56
57
  .join('/');
57
58
 
58
59
  return [[path, issue.message]] as const;
59
60
  });
60
61
 
61
- export const validate = <
62
- Output,
63
- Def extends z.ZodTypeDef = z.ZodTypeDef,
64
- Input = Output,
65
- >({
62
+ export const validate = <Output, Input = Output>({
66
63
  ctx,
67
64
  input,
68
65
  schema,
69
66
  }: {
70
67
  ctx: Context;
71
68
  input: unknown;
72
- schema: z.ZodSchema<Output, Def, Input>;
69
+ schema: z.ZodSchema<Output, Input>;
73
70
  }): Output => {
74
71
  const parseResult = schema.safeParse(input);
75
72
  if (parseResult.success === false) {
@@ -85,15 +82,11 @@ export const validate = <
85
82
  return parseResult.data;
86
83
  };
87
84
 
88
- export const validateRequestBody = <
89
- Output,
90
- Def extends z.ZodTypeDef = z.ZodTypeDef,
91
- Input = Output,
92
- >(
85
+ export const validateRequestBody = <Output, Input = Output>(
93
86
  ctx: Context,
94
- schema: z.ZodSchema<Output, Def, Input>,
87
+ schema: z.ZodSchema<Output, Input>,
95
88
  ): Output =>
96
- validate<Output, Def, Input>({
89
+ validate<Output, Input>({
97
90
  ctx,
98
91
  input: ctx.request.body as unknown,
99
92
  schema,
@@ -1,5 +1,5 @@
1
1
  import { Chance } from 'chance';
2
- import { z } from 'zod';
2
+ import { z } from 'zod/v4';
3
3
 
4
4
  import type { JobInput } from 'src/types/jobs';
5
5
 
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { z } from 'zod/v4';
2
2
 
3
3
  export interface Job {
4
4
  id: string;
@@ -23,7 +23,7 @@ configs:
23
23
  concurrency: 1
24
24
  plugins:
25
25
  - *docker-ecr-cache
26
- - docker-compose#v5.9.0:
26
+ - docker-compose#v5.10.0:
27
27
  dependencies: false
28
28
  run: app
29
29
  environment:
@@ -52,7 +52,7 @@ steps:
52
52
  GET_NPM_TOKEN: please
53
53
  plugins:
54
54
  - *docker-ecr-cache
55
- - docker-compose#v5.9.0:
55
+ - docker-compose#v5.10.0:
56
56
  run: app
57
57
  environment:
58
58
  - 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
 
@@ -369,6 +369,9 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
369
369
  "Type": "AWS::Lambda::EventSourceMapping",
370
370
  },
371
371
  "workerCurrentVersionxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": {
372
+ "Metadata": {
373
+ "aws:cdk:do-not-refactor": true,
374
+ },
372
375
  "Properties": {
373
376
  "FunctionName": {
374
377
  "Ref": "worker28EA3E30",
@@ -1099,6 +1102,9 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
1099
1102
  "Type": "AWS::Lambda::EventSourceMapping",
1100
1103
  },
1101
1104
  "workerCurrentVersionxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx": {
1105
+ "Metadata": {
1106
+ "aws:cdk:do-not-refactor": true,
1107
+ },
1102
1108
  "Properties": {
1103
1109
  "FunctionName": {
1104
1110
  "Ref": "worker28EA3E30",
@@ -17,15 +17,15 @@
17
17
  "@aws-sdk/client-codedeploy": "^3.363.0",
18
18
  "@aws-sdk/client-lambda": "^3.363.0",
19
19
  "@aws-sdk/client-sns": "^3.363.0",
20
- "@seek/aws-codedeploy-hooks": "^1.2.0",
20
+ "@seek/aws-codedeploy-hooks": "^2.0.0",
21
21
  "@seek/logger": "^10.0.0",
22
22
  "datadog-lambda-js": "^10.0.0",
23
23
  "dd-trace": "^5.0.0",
24
24
  "skuba-dive": "^2.0.0",
25
- "zod": "^3.19.1"
25
+ "zod": "^3.25.67"
26
26
  },
27
27
  "devDependencies": {
28
- "@seek/aws-codedeploy-infra": "^2.1.0",
28
+ "@seek/aws-codedeploy-infra": "^3.0.0",
29
29
  "@types/aws-lambda": "^8.10.82",
30
30
  "@types/chance": "^1.1.3",
31
31
  "@types/node": "^22.13.10",
@@ -37,9 +37,9 @@
37
37
  "constructs": "^10.0.17",
38
38
  "datadog-cdk-constructs-v2": "^2.0.0",
39
39
  "pino-pretty": "^13.0.0",
40
- "skuba": "11.1.0-jest30-20250620003740"
40
+ "skuba": "*"
41
41
  },
42
- "packageManager": "pnpm@10.12.1",
42
+ "packageManager": "pnpm@10.12.4",
43
43
  "engines": {
44
44
  "node": ">=22"
45
45
  }
@@ -2,11 +2,11 @@ import { PublishCommand } from '@aws-sdk/client-sns';
2
2
 
3
3
  import { metricsClient } from 'src/framework/metrics';
4
4
  import { createCtx, createSqsEvent } from 'src/testing/handler';
5
- import { logger } from 'src/testing/logging';
6
5
  import { scoringService, sns } from 'src/testing/services';
7
6
  import { chance, mockJobPublishedEvent } from 'src/testing/types';
8
7
 
9
8
  import * as app from './app';
9
+ import { stdoutMock } from './framework/logging';
10
10
 
11
11
  describe('app', () => {
12
12
  it('exports a handler', () => expect(app).toHaveProperty('handler'));
@@ -23,7 +23,6 @@ describe('handler', () => {
23
23
  .spyOn(metricsClient, 'distribution')
24
24
  .mockReturnValue();
25
25
 
26
- beforeAll(logger.spy);
27
26
  beforeAll(scoringService.spy);
28
27
 
29
28
  beforeEach(() => {
@@ -32,7 +31,7 @@ describe('handler', () => {
32
31
  });
33
32
 
34
33
  afterEach(() => {
35
- logger.clear();
34
+ stdoutMock.clear();
36
35
  distribution.mockClear();
37
36
  scoringService.clear();
38
37
  sns.clear();
@@ -45,12 +44,24 @@ describe('handler', () => {
45
44
 
46
45
  expect(scoringService.request).toHaveBeenCalledTimes(1);
47
46
 
48
- expect(logger.error).not.toHaveBeenCalled();
49
-
50
- expect(logger.debug.mock.calls).toEqual([
51
- [{ count: 1 }, 'Received jobs'],
52
- [{ snsMessageId: expect.any(String) }, 'Scored job'],
53
- ['Function succeeded'],
47
+ expect(stdoutMock.calls).toMatchObject([
48
+ {
49
+ awsRequestId: '-',
50
+ count: 1,
51
+ level: 20,
52
+ msg: 'Received jobs',
53
+ },
54
+ {
55
+ awsRequestId: '-',
56
+ level: 20,
57
+ msg: 'Scored job',
58
+ snsMessageId: expect.any(String),
59
+ },
60
+ {
61
+ awsRequestId: '-',
62
+ level: 20,
63
+ msg: 'Function succeeded',
64
+ },
54
65
  ]);
55
66
 
56
67
  expect(distribution.mock.calls).toEqual([
@@ -76,7 +87,23 @@ describe('handler', () => {
76
87
 
77
88
  await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
78
89
 
79
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
90
+ expect(stdoutMock.calls).toMatchObject([
91
+ {
92
+ awsRequestId: '-',
93
+ count: 1,
94
+ level: 20,
95
+ msg: 'Received jobs',
96
+ },
97
+ {
98
+ awsRequestId: '-',
99
+ err: {
100
+ message: err.message,
101
+ type: 'Error',
102
+ },
103
+ level: 50,
104
+ msg: 'Function failed',
105
+ },
106
+ ]);
80
107
  });
81
108
 
82
109
  it('bubbles up SNS error', async () => {
@@ -88,22 +115,44 @@ describe('handler', () => {
88
115
 
89
116
  await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
90
117
 
91
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
118
+ expect(stdoutMock.calls).toMatchObject([
119
+ {
120
+ awsRequestId: '-',
121
+ count: 1,
122
+ level: 20,
123
+ msg: 'Received jobs',
124
+ },
125
+ {
126
+ awsRequestId: '-',
127
+ err: {
128
+ message: err.message,
129
+ type: 'Error',
130
+ },
131
+ level: 50,
132
+ msg: 'Function failed',
133
+ },
134
+ ]);
92
135
  });
93
136
 
94
137
  it('throws on zero records', async () => {
95
- const err = new Error('Received 0 records');
96
-
97
138
  const event = createSqsEvent([]);
98
139
 
99
140
  await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
100
141
 
101
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
142
+ expect(stdoutMock.calls).toMatchObject([
143
+ {
144
+ awsRequestId: '-',
145
+ err: {
146
+ message: 'Received 0 records',
147
+ type: 'Error',
148
+ },
149
+ level: 50,
150
+ msg: 'Function failed',
151
+ },
152
+ ]);
102
153
  });
103
154
 
104
155
  it('throws on multiple records', async () => {
105
- const err = new Error('Received 2 records');
106
-
107
156
  const event = createSqsEvent([
108
157
  JSON.stringify(jobPublished),
109
158
  JSON.stringify(jobPublished),
@@ -111,6 +160,16 @@ describe('handler', () => {
111
160
 
112
161
  await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
113
162
 
114
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
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
+ ]);
115
174
  });
116
175
  });
@@ -29,7 +29,7 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
29
29
  }),
30
30
 
31
31
  test: () => ({
32
- logLevel: Env.string('LOG_LEVEL', { default: 'silent' }),
32
+ logLevel: Env.string('LOG_LEVEL', { default: 'debug' }),
33
33
  metrics: false,
34
34
  name: '<%- serviceName %>',
35
35
  version: 'test',
@@ -1,16 +1,14 @@
1
1
  import { createCtx } from 'src/testing/handler';
2
- import { logger } from 'src/testing/logging';
3
2
  import { chance } from 'src/testing/types';
4
3
 
5
4
  import { createHandler } from './handler';
5
+ import { logger, stdoutMock } from './logging';
6
6
 
7
7
  describe('createHandler', () => {
8
8
  const ctx = createCtx();
9
9
  const input = chance.paragraph();
10
10
 
11
- beforeAll(logger.spy);
12
-
13
- afterEach(logger.clear);
11
+ afterEach(stdoutMock.clear);
14
12
 
15
13
  it('handles happy path', async () => {
16
14
  const output = chance.paragraph();
@@ -25,11 +23,17 @@ describe('createHandler', () => {
25
23
 
26
24
  await expect(handler(input, ctx)).resolves.toBe(output);
27
25
 
28
- expect(logger.error).not.toHaveBeenCalled();
29
-
30
- expect(logger.debug.mock.calls).toEqual([
31
- ['Handler invoked'],
32
- ['Function succeeded'],
26
+ expect(stdoutMock.calls).toMatchObject([
27
+ {
28
+ awsRequestId: '-',
29
+ level: 20,
30
+ msg: 'Handler invoked',
31
+ },
32
+ {
33
+ awsRequestId: '-',
34
+ level: 20,
35
+ msg: 'Function succeeded',
36
+ },
33
37
  ]);
34
38
  });
35
39
 
@@ -40,9 +44,17 @@ describe('createHandler', () => {
40
44
 
41
45
  await expect(handler(input, ctx)).rejects.toThrow('Function failed');
42
46
 
43
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
44
-
45
- expect(logger.debug).not.toHaveBeenCalled();
47
+ expect(stdoutMock.calls).toMatchObject([
48
+ {
49
+ awsRequestId: '-',
50
+ err: {
51
+ message: err.message,
52
+ type: 'Error',
53
+ },
54
+ level: 50,
55
+ msg: 'Function failed',
56
+ },
57
+ ]);
46
58
  });
47
59
 
48
60
  it('handles sync error', async () => {
@@ -54,8 +66,16 @@ describe('createHandler', () => {
54
66
 
55
67
  await expect(handler(input, ctx)).rejects.toThrow('Function failed');
56
68
 
57
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
58
-
59
- expect(logger.debug).not.toHaveBeenCalled();
69
+ expect(stdoutMock.calls).toMatchObject([
70
+ {
71
+ awsRequestId: '-',
72
+ err: {
73
+ message: err.message,
74
+ type: 'Error',
75
+ },
76
+ level: 50,
77
+ msg: 'Function failed',
78
+ },
79
+ ]);
60
80
  });
61
81
  });
@@ -1,6 +1,6 @@
1
1
  import { AsyncLocalStorage } from 'async_hooks';
2
2
 
3
- import createLogger from '@seek/logger';
3
+ import createLogger, { createDestination } from '@seek/logger';
4
4
 
5
5
  import { config } from 'src/config';
6
6
 
@@ -10,18 +10,29 @@ interface LoggerContext {
10
10
 
11
11
  export const loggerContext = new AsyncLocalStorage<LoggerContext>();
12
12
 
13
- export const logger = createLogger({
14
- base: {
15
- environment: config.environment,
16
- version: config.version,
13
+ const { destination, stdoutMock } = createDestination({
14
+ mock: config.environment === 'test' && {
15
+ redact: ['awsRequestId'],
17
16
  },
17
+ });
18
18
 
19
- level: config.logLevel,
19
+ export { stdoutMock };
20
20
 
21
- mixin: () => ({ ...loggerContext.getStore() }),
21
+ export const logger = createLogger(
22
+ {
23
+ base: {
24
+ environment: config.environment,
25
+ version: config.version,
26
+ },
22
27
 
23
- name: config.name,
28
+ level: config.logLevel,
24
29
 
25
- transport:
26
- config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
27
- });
30
+ mixin: () => ({ ...loggerContext.getStore() }),
31
+
32
+ name: config.name,
33
+
34
+ transport:
35
+ config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
36
+ },
37
+ destination,
38
+ );
@@ -32,13 +32,12 @@ describe('validateJson', () => {
32
32
  .toThrowErrorMatchingInlineSnapshot(`
33
33
  "[
34
34
  {
35
- "code": "invalid_type",
36
35
  "expected": "string",
37
- "received": "null",
36
+ "code": "invalid_type",
38
37
  "path": [
39
38
  "id"
40
39
  ],
41
- "message": "Expected string, received null"
40
+ "message": "Invalid input: expected string, received null"
42
41
  }
43
42
  ]"
44
43
  `);
@@ -51,22 +50,20 @@ describe('validateJson', () => {
51
50
  .toThrowErrorMatchingInlineSnapshot(`
52
51
  "[
53
52
  {
54
- "code": "invalid_type",
55
53
  "expected": "string",
56
- "received": "undefined",
54
+ "code": "invalid_type",
57
55
  "path": [
58
56
  "id"
59
57
  ],
60
- "message": "Required"
58
+ "message": "Invalid input: expected string, received undefined"
61
59
  },
62
60
  {
63
- "code": "invalid_type",
64
61
  "expected": "string",
65
- "received": "undefined",
62
+ "code": "invalid_type",
66
63
  "path": [
67
64
  "description"
68
65
  ],
69
- "message": "Required"
66
+ "message": "Invalid input: expected string, received undefined"
70
67
  }
71
68
  ]"
72
69
  `);
@@ -1,10 +1,6 @@
1
- import type { z } from 'zod';
1
+ import type { z } from 'zod/v4';
2
2
 
3
- export const validateJson = <
4
- Output,
5
- Def extends z.ZodTypeDef = z.ZodTypeDef,
6
- Input = Output,
7
- >(
3
+ export const validateJson = <Output, Input>(
8
4
  input: string,
9
- schema: z.ZodSchema<Output, Def, Input>,
5
+ schema: z.ZodSchema<Output, Input>,
10
6
  ): Output => schema.parse(JSON.parse(input));
@@ -1,5 +1,5 @@
1
1
  import { Chance } from 'chance';
2
- import { z } from 'zod';
2
+ import { z } from 'zod/v4';
3
3
 
4
4
  import type { JobPublishedEvent } from 'src/types/pipelineEvents';
5
5
 
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { z } from 'zod/v4';
2
2
 
3
3
  export type JobScorerInput = z.infer<typeof JobScorerInputSchema>;
4
4
 
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { z } from 'zod/v4';
2
2
 
3
3
  export type JobPublishedEvent = z.infer<typeof JobPublishedEventSchema>;
4
4
 
@@ -1,16 +0,0 @@
1
- import * as logging from 'src/framework/logging';
2
-
3
- export const logger = {
4
- error: jest.fn(),
5
- info: jest.fn(),
6
-
7
- clear: () => {
8
- logger.error.mockClear();
9
- logger.info.mockClear();
10
- },
11
-
12
- spy: () => {
13
- jest.spyOn(logging.logger, 'error').mockImplementation(logger.error);
14
- jest.spyOn(logging.logger, 'info').mockImplementation(logger.info);
15
- },
16
- };
@@ -1,19 +0,0 @@
1
- import * as logging from 'src/framework/logging';
2
-
3
- export const logger = {
4
- error: jest.fn(),
5
- info: jest.fn(),
6
- debug: jest.fn(),
7
-
8
- clear: () => {
9
- logger.error.mockClear();
10
- logger.info.mockClear();
11
- logger.debug.mockClear();
12
- },
13
-
14
- spy: () => {
15
- jest.spyOn(logging.logger, 'error').mockImplementation(logger.error);
16
- jest.spyOn(logging.logger, 'info').mockImplementation(logger.info);
17
- jest.spyOn(logging.logger, 'debug').mockImplementation(logger.debug);
18
- },
19
- };