@schafevormfenster/rest-commons 0.1.9 → 0.1.11
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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-check.log +129 -115
- package/.turbo/turbo-lint.log +4 -0
- package/dist/api-schemas/health.schema.js +2 -2
- package/dist/api-schemas/paginated-results.schema.js +1 -1
- package/dist/api-schemas/partial-results.schema.js +1 -1
- package/dist/api-schemas/result.schema.js +1 -1
- package/dist/api-schemas/results.schema.js +1 -1
- package/dist/helpers/correlation/get-correlation-id.js +1 -1
- package/dist/helpers/detect-mime-type.js +4 -4
- package/dist/helpers/parameter-validation.js +1 -1
- package/dist/helpers/response-headers/build-api-unauthorized-headers.js +1 -1
- package/dist/helpers/response-headers/resolve-environment.d.ts +1 -1
- package/dist/helpers/response-headers/resolve-environment.d.ts.map +1 -1
- package/dist/index.d.ts +33 -33
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -33
- package/dist/primitives/geo-point.schema.js +2 -2
- package/dist/primitives/latitude.schema.js +1 -1
- package/dist/primitives/location.schema.js +1 -1
- package/dist/primitives/longitude.schema.js +1 -1
- package/dist/primitives/slug.schema.js +2 -2
- package/dist/time/bounded-time.schema.d.ts.map +1 -1
- package/dist/time/bounded-time.schema.js +19 -15
- package/dist/time/flexible-time-parser.d.ts +1 -1
- package/dist/time/flexible-time-parser.d.ts.map +1 -1
- package/dist/time/flexible-time-parser.js +2 -2
- package/dist/time/flexible-time.schema.js +3 -3
- package/dist/time/relative-time.schema.js +1 -1
- package/dist/time/time-helpers.js +2 -2
- package/dist/time/time-schemas.js +1 -1
- package/package.json +8 -8
- package/src/api-schemas/error.schema.test.ts +20 -14
- package/src/api-schemas/health.schema.test.ts +116 -112
- package/src/api-schemas/health.schema.ts +2 -2
- package/src/api-schemas/okay.schema.test.ts +10 -6
- package/src/api-schemas/paginated-results.schema.ts +1 -1
- package/src/api-schemas/partial-results.schema.ts +1 -1
- package/src/api-schemas/result.schema.test.ts +13 -9
- package/src/api-schemas/result.schema.ts +1 -1
- package/src/api-schemas/results.schema.test.ts +27 -7
- package/src/api-schemas/results.schema.ts +1 -1
- package/src/helpers/correlation/get-correlation-id.test.ts +1 -1
- package/src/helpers/correlation/get-correlation-id.ts +1 -1
- package/src/helpers/correlation/get-header.test.ts +2 -2
- package/src/helpers/detect-mime-type.test.ts +85 -81
- package/src/helpers/detect-mime-type.ts +4 -4
- package/src/helpers/detect-suspicious-patterns.test.ts +34 -28
- package/src/helpers/eventify-constants.test.ts +54 -34
- package/src/helpers/eventify-constants.types.test.ts +54 -34
- package/src/helpers/hash-binary.test.ts +50 -46
- package/src/helpers/mime-types/detect-image-mime-type.test.ts +59 -55
- package/src/helpers/mime-types/detect-ole-mime-type.test.ts +73 -69
- package/src/helpers/mime-types/detect-pdf-mime-type.test.ts +30 -26
- package/src/helpers/mime-types/detect-zip-mime-type.test.ts +76 -72
- package/src/helpers/parameter-validation.test.ts +27 -23
- package/src/helpers/parameter-validation.ts +1 -1
- package/src/helpers/process-eventify-request.ts +2 -2
- package/src/helpers/response-headers/build-api-unauthorized-headers.ts +1 -1
- package/src/helpers/response-headers/resolve-environment.ts +1 -1
- package/src/helpers/slugify.test.ts +62 -58
- package/src/index.ts +33 -33
- package/src/normalization/normalize-list.test.ts +36 -32
- package/src/normalization/normalize-location.test.ts +1 -1
- package/src/primitives/coordinate-precision.test.ts +38 -32
- package/src/primitives/geo-point.schema.test.ts +60 -56
- package/src/primitives/geo-point.schema.ts +2 -2
- package/src/primitives/geoname-id.schema.test.ts +50 -46
- package/src/primitives/international-zip.schema.test.ts +175 -121
- package/src/primitives/latitude.schema.test.ts +67 -61
- package/src/primitives/latitude.schema.ts +1 -1
- package/src/primitives/location.schema.test.ts +15 -11
- package/src/primitives/location.schema.ts +1 -1
- package/src/primitives/longitude.schema.test.ts +67 -61
- package/src/primitives/longitude.schema.ts +1 -1
- package/src/primitives/numeric-id.schema.test.ts +24 -20
- package/src/primitives/slug.schema.test.ts +93 -85
- package/src/primitives/slug.schema.ts +2 -2
- package/src/primitives/uuid.schema.test.ts +37 -33
- package/src/primitives/wikidata-id.schema.test.ts +48 -44
- package/src/time/boundary-enforcement.test.ts +1 -1
- package/src/time/bounded-time.schema.test.ts +2 -2
- package/src/time/bounded-time.schema.ts +19 -15
- package/src/time/flexible-time-parser.test.ts +3 -3
- package/src/time/flexible-time-parser.ts +2 -2
- package/src/time/flexible-time.schema.test.ts +6 -6
- package/src/time/flexible-time.schema.ts +3 -3
- package/src/time/is-relative-time.test.ts +18 -14
- package/src/time/iso8601.types.test.ts +4 -4
- package/src/time/parse-relative-time.test.ts +37 -33
- package/src/time/relative-time.schema.test.ts +18 -14
- package/src/time/relative-time.schema.ts +1 -1
- package/src/time/since-parameter.schema.test.ts +46 -42
- package/src/time/time-helpers.test.ts +2 -2
- package/src/time/time-helpers.ts +2 -2
- package/src/time/time-schemas.test.ts +5 -5
- package/src/time/time-schemas.ts +1 -1
- package/src/time/time.schema.test.ts +19 -15
- package/src/time/timezone-independence.test.ts +2 -2
- package/src/time/timezone.types.test.ts +49 -43
package/dist/index.js
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
// Schemas
|
|
2
|
-
export * from "./api-schemas/error.schema";
|
|
3
|
-
export * from "./api-schemas/health.schema";
|
|
4
|
-
export * from "./api-schemas/okay.schema";
|
|
5
|
-
export * from "./api-schemas/result.schema";
|
|
6
|
-
export * from "./api-schemas/results.schema";
|
|
2
|
+
export * from "./api-schemas/error.schema.js";
|
|
3
|
+
export * from "./api-schemas/health.schema.js";
|
|
4
|
+
export * from "./api-schemas/okay.schema.js";
|
|
5
|
+
export * from "./api-schemas/result.schema.js";
|
|
6
|
+
export * from "./api-schemas/results.schema.js";
|
|
7
7
|
// Primitive schemas
|
|
8
|
-
export * from "./primitives/location.schema";
|
|
9
|
-
export * from "./primitives/slug.schema";
|
|
10
|
-
export * from "./primitives/numeric-id.schema";
|
|
11
|
-
export * from "./primitives/uuid.schema";
|
|
12
|
-
export * from "./primitives/geoname-id.schema";
|
|
13
|
-
export * from "./primitives/wikidata-id.schema";
|
|
14
|
-
export * from "./primitives/international-zip.schema";
|
|
8
|
+
export * from "./primitives/location.schema.js";
|
|
9
|
+
export * from "./primitives/slug.schema.js";
|
|
10
|
+
export * from "./primitives/numeric-id.schema.js";
|
|
11
|
+
export * from "./primitives/uuid.schema.js";
|
|
12
|
+
export * from "./primitives/geoname-id.schema.js";
|
|
13
|
+
export * from "./primitives/wikidata-id.schema.js";
|
|
14
|
+
export * from "./primitives/international-zip.schema.js";
|
|
15
15
|
// Time utilities
|
|
16
|
-
export * from "./time/timezone.types";
|
|
17
|
-
export * from "./time/iso8601.schema"; // ISO8601 schemas
|
|
18
|
-
export * from "./time/relative-time.schema"; // Relative time schemas
|
|
19
|
-
export * from "./time/flexible-time.schema"; // Flexible time schemas
|
|
20
|
-
export * from "./time/bounded-time.schema"; // Bounded time schemas
|
|
21
|
-
export * from "./time/time-helpers"; // Helper functions
|
|
22
|
-
export * from "./time/is-relative-time";
|
|
23
|
-
export { parseFlexibleTime as parseFlexibleTimeAdvanced } from "./time/flexible-time-parser";
|
|
24
|
-
export * from "./time/boundary-enforcement";
|
|
16
|
+
export * from "./time/timezone.types.js";
|
|
17
|
+
export * from "./time/iso8601.schema.js"; // ISO8601 schemas
|
|
18
|
+
export * from "./time/relative-time.schema.js"; // Relative time schemas
|
|
19
|
+
export * from "./time/flexible-time.schema.js"; // Flexible time schemas
|
|
20
|
+
export * from "./time/bounded-time.schema.js"; // Bounded time schemas
|
|
21
|
+
export * from "./time/time-helpers.js"; // Helper functions
|
|
22
|
+
export * from "./time/is-relative-time.js";
|
|
23
|
+
export { parseFlexibleTime as parseFlexibleTimeAdvanced } from "./time/flexible-time-parser.js";
|
|
24
|
+
export * from "./time/boundary-enforcement.js";
|
|
25
25
|
// Helpers
|
|
26
|
-
export * from "./helpers/correlation/get-correlation-id";
|
|
27
|
-
export * from "./helpers/correlation/get-header";
|
|
28
|
-
export * from "./helpers/detect-mime-type";
|
|
29
|
-
export * from "./helpers/detect-suspicious-patterns";
|
|
30
|
-
export * from "./helpers/hash-binary";
|
|
31
|
-
export * from "./helpers/parameter-validation";
|
|
32
|
-
export * from "./helpers/slugify";
|
|
33
|
-
export * from "./helpers/response-headers/build-api-unauthorized-headers";
|
|
34
|
-
export * from "./helpers/response-headers/environment.types";
|
|
35
|
-
export * from "./helpers/response-headers/resolve-environment";
|
|
26
|
+
export * from "./helpers/correlation/get-correlation-id.js";
|
|
27
|
+
export * from "./helpers/correlation/get-header.js";
|
|
28
|
+
export * from "./helpers/detect-mime-type.js";
|
|
29
|
+
export * from "./helpers/detect-suspicious-patterns.js";
|
|
30
|
+
export * from "./helpers/hash-binary.js";
|
|
31
|
+
export * from "./helpers/parameter-validation.js";
|
|
32
|
+
export * from "./helpers/slugify.js";
|
|
33
|
+
export * from "./helpers/response-headers/build-api-unauthorized-headers.js";
|
|
34
|
+
export * from "./helpers/response-headers/environment.types.js";
|
|
35
|
+
export * from "./helpers/response-headers/resolve-environment.js";
|
|
36
36
|
// Utilities
|
|
37
|
-
export * from "./normalization/normalize-list";
|
|
38
|
-
export * from "./normalization/normalize-location";
|
|
37
|
+
export * from "./normalization/normalize-list.js";
|
|
38
|
+
export * from "./normalization/normalize-location.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { LatitudeSchema } from "./latitude.schema";
|
|
3
|
-
import { LongitudeSchema } from "./longitude.schema";
|
|
2
|
+
import { LatitudeSchema } from "./latitude.schema.js";
|
|
3
|
+
import { LongitudeSchema } from "./longitude.schema.js";
|
|
4
4
|
/**
|
|
5
5
|
* Zod schema for validating geographic coordinates as a tuple.
|
|
6
6
|
* Geographic coordinates as [latitude, longitude].
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { hasValidDecimalPrecision } from "./coordinate-precision";
|
|
2
|
+
import { hasValidDecimalPrecision } from "./coordinate-precision.js";
|
|
3
3
|
/**
|
|
4
4
|
* Zod schema for validating latitude coordinates.
|
|
5
5
|
* Latitude must be a number between -90 and 90 degrees inclusive.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { validateNoSuspiciousPatterns } from "../helpers/parameter-validation";
|
|
2
|
+
import { validateNoSuspiciousPatterns } from "../helpers/parameter-validation.js";
|
|
3
3
|
/**
|
|
4
4
|
* Zod schema for validating location parameter in findbyaddress endpoint.
|
|
5
5
|
* Checks for minimum length and suspicious patterns.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { hasValidDecimalPrecision } from "./coordinate-precision";
|
|
2
|
+
import { hasValidDecimalPrecision } from "./coordinate-precision.js";
|
|
3
3
|
/**
|
|
4
4
|
* Zod schema for validating longitude coordinates.
|
|
5
5
|
* Longitude must be a number between -180 and 180 degrees inclusive.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { validateNoSuspiciousPatterns } from "../helpers/parameter-validation";
|
|
3
|
-
import { slugify } from "../helpers/slugify";
|
|
2
|
+
import { validateNoSuspiciousPatterns } from "../helpers/parameter-validation.js";
|
|
3
|
+
import { slugify } from "../helpers/slugify.js";
|
|
4
4
|
/**
|
|
5
5
|
* Zod schema for validating slug parameter in community slug endpoint.
|
|
6
6
|
* Checks for length between 2-150 characters and suspicious patterns.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-time.schema.d.ts","sourceRoot":"","sources":["../../src/time/bounded-time.schema.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,eAAoB,
|
|
1
|
+
{"version":3,"file":"bounded-time.schema.d.ts","sourceRoot":"","sources":["../../src/time/bounded-time.schema.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,eAAoB,OAwC5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,eAAoB,OAuC5E"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FlexibleTimePastSchema } from "./flexible-time.schema";
|
|
2
|
-
import { RelativeTimePastSchema } from "./relative-time.schema";
|
|
3
|
-
import { parseRelativeTime } from "./time-helpers";
|
|
1
|
+
import { FlexibleTimePastSchema } from "./flexible-time.schema.js";
|
|
2
|
+
import { RelativeTimePastSchema } from "./relative-time.schema.js";
|
|
3
|
+
import { parseRelativeTime } from "./time-helpers.js";
|
|
4
4
|
/**
|
|
5
5
|
* Creates a flexible time schema with min/max boundaries
|
|
6
6
|
* @param options - Boundary configuration
|
|
@@ -16,24 +16,26 @@ export function createBoundedFlexibleTimeSchema(options = {}) {
|
|
|
16
16
|
if (options.min) {
|
|
17
17
|
// Min is the oldest allowed time (furthest in the past)
|
|
18
18
|
// For relative times going backwards, larger values are older
|
|
19
|
-
const
|
|
20
|
-
const minMs = new Date(minTime).getTime();
|
|
19
|
+
const minBoundary = options.min;
|
|
21
20
|
schema = schema.refine((value) => {
|
|
21
|
+
const minTime = parseRelativeTime(minBoundary, new Date(), "past");
|
|
22
|
+
const minMs = new Date(minTime).getTime();
|
|
22
23
|
const valueMs = new Date(value).getTime();
|
|
23
24
|
return valueMs >= minMs;
|
|
24
25
|
}, {
|
|
25
|
-
message: `Time must be more recent than ${options.min} ago
|
|
26
|
+
message: `Time must be more recent than ${options.min} ago`,
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
if (options.max) {
|
|
29
30
|
// Max is the newest allowed time (can be in future for flexible time)
|
|
30
|
-
const
|
|
31
|
-
const maxMs = new Date(maxTime).getTime();
|
|
31
|
+
const maxBoundary = options.max;
|
|
32
32
|
schema = schema.refine((value) => {
|
|
33
|
+
const maxTime = parseRelativeTime(maxBoundary, new Date(), "future");
|
|
34
|
+
const maxMs = new Date(maxTime).getTime();
|
|
33
35
|
const valueMs = new Date(value).getTime();
|
|
34
36
|
return valueMs <= maxMs;
|
|
35
37
|
}, {
|
|
36
|
-
message: `Time must be before ${options.max} in the future
|
|
38
|
+
message: `Time must be before ${options.max} in the future`,
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
return schema;
|
|
@@ -53,24 +55,26 @@ export function createBoundedRelativeTimeSchema(options = {}) {
|
|
|
53
55
|
let schema = RelativeTimePastSchema;
|
|
54
56
|
if (options.min) {
|
|
55
57
|
// Min is the most recent allowed (closest to now)
|
|
56
|
-
const
|
|
57
|
-
const minMs = new Date(minTime).getTime();
|
|
58
|
+
const minBoundary = options.min;
|
|
58
59
|
schema = schema.refine((value) => {
|
|
60
|
+
const minTime = parseRelativeTime(minBoundary, new Date(), "past");
|
|
61
|
+
const minMs = new Date(minTime).getTime();
|
|
59
62
|
const valueMs = new Date(value).getTime();
|
|
60
63
|
return valueMs <= minMs; // Value must be older (earlier) than min
|
|
61
64
|
}, {
|
|
62
|
-
message: `Time must be at least ${options.min} ago
|
|
65
|
+
message: `Time must be at least ${options.min} ago`,
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
if (options.max) {
|
|
66
69
|
// Max is the oldest allowed (furthest from now)
|
|
67
|
-
const
|
|
68
|
-
const maxMs = new Date(maxTime).getTime();
|
|
70
|
+
const maxBoundary = options.max;
|
|
69
71
|
schema = schema.refine((value) => {
|
|
72
|
+
const maxTime = parseRelativeTime(maxBoundary, new Date(), "past");
|
|
73
|
+
const maxMs = new Date(maxTime).getTime();
|
|
70
74
|
const valueMs = new Date(value).getTime();
|
|
71
75
|
return valueMs >= maxMs; // Value must be newer (later) than max
|
|
72
76
|
}, {
|
|
73
|
-
message: `Time cannot be more than ${options.max} ago
|
|
77
|
+
message: `Time cannot be more than ${options.max} ago`,
|
|
74
78
|
});
|
|
75
79
|
}
|
|
76
80
|
return schema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flexible-time-parser.d.ts","sourceRoot":"","sources":["../../src/time/flexible-time-parser.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,YAAY,EACb,MAAM,
|
|
1
|
+
{"version":3,"file":"flexible-time-parser.d.ts","sourceRoot":"","sources":["../../src/time/flexible-time-parser.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,YAAY,EACb,MAAM,mBAAmB,CAAC;AAK3B;;;;GAIG;AAEH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,YAAY,EACnB,QAAQ,GAAE,IAAiB,GAC1B,MAAM,CA2FR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,IAAI,IAC7C,OAAO,YAAY,YAC5B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
2
|
import isoWeek from "dayjs/plugin/isoWeek.js";
|
|
3
3
|
import utc from "dayjs/plugin/utc.js";
|
|
4
|
-
import { ISO8601Schema } from "./iso8601.types";
|
|
5
|
-
import { RelativeTimeSchema, FlexibleTimeSchema, } from "./time-schemas";
|
|
4
|
+
import { ISO8601Schema } from "./iso8601.types.js";
|
|
5
|
+
import { RelativeTimeSchema, FlexibleTimeSchema, } from "./time-schemas.js";
|
|
6
6
|
dayjs.extend(isoWeek);
|
|
7
7
|
dayjs.extend(utc);
|
|
8
8
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ISO8601Schema } from "./iso8601.schema";
|
|
3
|
-
import { RelativeTimeSchema } from "./relative-time.schema";
|
|
4
|
-
import { parseFlexibleTime } from "./time-helpers";
|
|
2
|
+
import { ISO8601Schema } from "./iso8601.schema.js";
|
|
3
|
+
import { RelativeTimeSchema } from "./relative-time.schema.js";
|
|
4
|
+
import { parseFlexibleTime } from "./time-helpers.js";
|
|
5
5
|
/**
|
|
6
6
|
* Core validation schema for flexible time input
|
|
7
7
|
* Accepts either ISO8601 datetime or relative time expression
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { parseRelativeTime } from "./time-helpers";
|
|
2
|
+
import { parseRelativeTime } from "./time-helpers.js";
|
|
3
3
|
/**
|
|
4
4
|
* Core validation schema for relative time expressions
|
|
5
5
|
* Supports: "1m" (minute), "2h" (hour), "3d" (day), "1w" (week), "2M" (month)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
2
|
import utc from "dayjs/plugin/utc.js";
|
|
3
|
-
import { ISO8601Schema } from "./iso8601.schema";
|
|
4
|
-
import { RelativeTimeSchema } from "./relative-time.schema";
|
|
3
|
+
import { ISO8601Schema } from "./iso8601.schema.js";
|
|
4
|
+
import { RelativeTimeSchema } from "./relative-time.schema.js";
|
|
5
5
|
dayjs.extend(utc);
|
|
6
6
|
/**
|
|
7
7
|
* Helper function to parse relative time and calculate the datetime
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schafevormfenster/rest-commons",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Centralized authority for REST standards and schemas - XSD schemas, parsing functions, and coding instructions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,20 +20,20 @@
|
|
|
20
20
|
"node": ">=24"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^22.19.
|
|
24
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
25
|
-
"@typescript-eslint/parser": "^8.
|
|
23
|
+
"@types/node": "^22.19.10",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
25
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
26
26
|
"eslint": "^9.39.2",
|
|
27
27
|
"typescript": "^5.9.3",
|
|
28
|
-
"vitest": "^4.0.
|
|
29
|
-
"@schafevormfenster/eslint-config": "0.0.
|
|
28
|
+
"vitest": "^4.0.18",
|
|
29
|
+
"@schafevormfenster/eslint-config": "0.0.24"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"dayjs": "^1.11.19",
|
|
33
33
|
"slugify": "^1.6.6",
|
|
34
34
|
"zod": "^3.25.76",
|
|
35
|
-
"@schafevormfenster/logging": "0.1.
|
|
36
|
-
"@schafevormfenster/security": "0.1.
|
|
35
|
+
"@schafevormfenster/logging": "0.1.8",
|
|
36
|
+
"@schafevormfenster/security": "0.1.5"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"typecheck": "tsc --noEmit",
|
|
@@ -3,25 +3,31 @@ import { describe, it, expect } from "vitest";
|
|
|
3
3
|
import { ApiErrorSchema, ApiErrorConstructor } from "./error.schema";
|
|
4
4
|
|
|
5
5
|
describe("ApiErrorSchema", () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
describe("Happy Path", () => {
|
|
7
|
+
it("validates error shape for 4xx/5xx", () => {
|
|
8
|
+
const parsed = ApiErrorSchema.parse({ status: 404, error: "Not Found" });
|
|
9
|
+
expect(parsed.status).toBe(404);
|
|
10
|
+
expect(parsed.error).toMatch(/not found/i);
|
|
11
|
+
});
|
|
10
12
|
});
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
describe("Error Handling", () => {
|
|
15
|
+
it("rejects non-error status codes", () => {
|
|
16
|
+
expect(() => ApiErrorSchema.parse({ status: 200, error: "ok" })).toThrow();
|
|
17
|
+
expect(() =>
|
|
18
|
+
ApiErrorSchema.parse({ status: 600, error: "invalid" })
|
|
19
|
+
).toThrow();
|
|
20
|
+
});
|
|
17
21
|
});
|
|
18
22
|
});
|
|
19
23
|
|
|
20
24
|
describe("ApiError class", () => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
describe("Happy Path", () => {
|
|
26
|
+
it("sets status and name", () => {
|
|
27
|
+
const error = new ApiErrorConstructor(418, "I'm a teapot");
|
|
28
|
+
expect(error.status).toBe(418);
|
|
29
|
+
expect(error.message).toMatch(/i'm a teapot/i);
|
|
30
|
+
expect(error.name).toBe("ApiError");
|
|
31
|
+
});
|
|
26
32
|
});
|
|
27
33
|
});
|
|
@@ -13,136 +13,140 @@ import {
|
|
|
13
13
|
} from "./health.schema";
|
|
14
14
|
|
|
15
15
|
describe("health.schema", () => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
describe("Happy Path", () => {
|
|
17
|
+
it("parses healthy service info", () => {
|
|
18
|
+
const service = HealthyServiceInfoSchema.parse({
|
|
19
|
+
name: "Example",
|
|
20
|
+
status: 200,
|
|
21
|
+
message: "healthy",
|
|
22
|
+
version: "1.0.0",
|
|
23
|
+
});
|
|
24
|
+
expect(service.name).toBe("Example");
|
|
25
|
+
expect(service.status).toBe(200);
|
|
26
|
+
expect(service.message).toMatch(/healthy/i);
|
|
22
27
|
});
|
|
23
|
-
expect(service.name).toBe("Example");
|
|
24
|
-
expect(service.status).toBe(200);
|
|
25
|
-
expect(service.message).toMatch(/healthy/i);
|
|
26
|
-
});
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
it("parses unhealthy service info", () => {
|
|
30
|
+
const service = UnhealthyServiceInfoSchema.parse({
|
|
31
|
+
name: "Example",
|
|
32
|
+
status: 503,
|
|
33
|
+
error: "Service unavailable",
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
});
|
|
36
|
+
expect(service.status).toBe(503);
|
|
37
|
+
expect(service.error).toMatch(/service unavailable/i);
|
|
34
38
|
});
|
|
35
|
-
expect(service.status).toBe(503);
|
|
36
|
-
expect(service.error).toMatch(/service unavailable/i);
|
|
37
|
-
});
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
it("parses degraded service info", () => {
|
|
41
|
+
const service = DegradedServiceInfoSchema.parse({
|
|
42
|
+
name: "Example",
|
|
43
|
+
status: 202,
|
|
44
|
+
message: "degraded",
|
|
45
|
+
version: "1.0.0",
|
|
46
|
+
});
|
|
47
|
+
expect(service.name).toBe("Example");
|
|
48
|
+
expect(service.status).toBe(202);
|
|
49
|
+
expect(service.message).toMatch(/degraded/i);
|
|
45
50
|
});
|
|
46
|
-
expect(service.name).toBe("Example");
|
|
47
|
-
expect(service.status).toBe(202);
|
|
48
|
-
expect(service.message).toMatch(/degraded/i);
|
|
49
|
-
});
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
it("parses healthy API status", () => {
|
|
53
|
+
const status = HealthyApiStatusSchema.parse({
|
|
54
|
+
status: 200,
|
|
55
|
+
name: "api",
|
|
56
|
+
version: "1.0.0",
|
|
57
|
+
description: "ok",
|
|
58
|
+
services: [
|
|
59
|
+
{ name: "svc", status: 200, message: "healthy" },
|
|
60
|
+
{ name: "svc2", status: 500, error: "fail" },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
expect(status.status).toBe(200);
|
|
64
|
+
expect(status.services).toHaveLength(2);
|
|
61
65
|
});
|
|
62
|
-
expect(status.status).toBe(200);
|
|
63
|
-
expect(status.services).toHaveLength(2);
|
|
64
|
-
});
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
it("parses unhealthy API status", () => {
|
|
68
|
+
const status = UnhealthyApiStatusSchema.parse({
|
|
69
|
+
status: 500,
|
|
70
|
+
error: "bad",
|
|
71
|
+
name: "api",
|
|
72
|
+
version: "1.0.0",
|
|
73
|
+
services: [{ name: "svc", status: 500, error: "boom" }],
|
|
74
|
+
});
|
|
75
|
+
expect(status.error).toMatch(/bad/i);
|
|
73
76
|
});
|
|
74
|
-
expect(status.error).toMatch(/bad/i);
|
|
75
|
-
});
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
it("parses degraded API status", () => {
|
|
79
|
+
const status = DegradedApiStatusSchema.parse({
|
|
80
|
+
status: 202,
|
|
81
|
+
name: "api",
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
description: "partially operational",
|
|
84
|
+
services: [
|
|
85
|
+
{ name: "svc1", status: 200, message: "healthy" },
|
|
86
|
+
{ name: "svc2", status: 202, message: "degraded" },
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
expect(status.status).toBe(202);
|
|
90
|
+
expect(status.services).toHaveLength(2);
|
|
87
91
|
});
|
|
88
|
-
expect(status.status).toBe(202);
|
|
89
|
-
expect(status.services).toHaveLength(2);
|
|
90
|
-
});
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
it("ApiInfoSchema validates basic api info", () => {
|
|
94
|
+
const info = ApiInfoSchema.parse({
|
|
95
|
+
name: "api",
|
|
96
|
+
version: "1.2.3",
|
|
97
|
+
description: "desc",
|
|
98
|
+
services: [{ name: "svc", status: 200, message: "healthy" }],
|
|
99
|
+
});
|
|
100
|
+
expect(info.name).toBe("api");
|
|
98
101
|
});
|
|
99
|
-
expect(info.name).toBe("api");
|
|
100
102
|
});
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
describe("Edge Cases", () => {
|
|
105
|
+
it("ApiStatusSchema accepts healthy, degraded, and unhealthy variants", () => {
|
|
106
|
+
const healthy = ApiStatusSchema.parse({
|
|
107
|
+
status: 200,
|
|
108
|
+
name: "api",
|
|
109
|
+
version: "1.0.0",
|
|
110
|
+
services: [{ name: "svc", status: 200, message: "healthy" }],
|
|
111
|
+
});
|
|
112
|
+
expect(healthy.status).toBe(200);
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
const degraded = ApiStatusSchema.parse({
|
|
115
|
+
status: 202,
|
|
116
|
+
name: "api",
|
|
117
|
+
version: "1.0.0",
|
|
118
|
+
services: [{ name: "svc", status: 202, message: "degraded" }],
|
|
119
|
+
});
|
|
120
|
+
expect(degraded.status).toBe(202);
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
const unhealthy = ApiStatusSchema.parse({
|
|
123
|
+
status: 503,
|
|
124
|
+
error: "down",
|
|
125
|
+
name: "api",
|
|
126
|
+
version: "1.0.0",
|
|
127
|
+
services: [{ name: "svc", status: 503, error: "down" }],
|
|
128
|
+
});
|
|
129
|
+
expect(unhealthy.status).toBe(503);
|
|
125
130
|
});
|
|
126
|
-
expect(unhealthy.status).toBe(503);
|
|
127
|
-
});
|
|
128
131
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
132
|
+
it("ServiceStatusSchema union validates all three variants", () => {
|
|
133
|
+
expect(
|
|
134
|
+
ServiceStatusSchema.parse({
|
|
135
|
+
name: "svc",
|
|
136
|
+
status: 200,
|
|
137
|
+
message: "healthy",
|
|
138
|
+
})
|
|
139
|
+
).toBeTruthy();
|
|
140
|
+
expect(
|
|
141
|
+
ServiceStatusSchema.parse({
|
|
142
|
+
name: "svc",
|
|
143
|
+
status: 202,
|
|
144
|
+
message: "degraded",
|
|
145
|
+
})
|
|
146
|
+
).toBeTruthy();
|
|
147
|
+
expect(
|
|
148
|
+
ServiceStatusSchema.parse({ name: "svc", status: 500, error: "err" })
|
|
149
|
+
).toBeTruthy();
|
|
150
|
+
});
|
|
147
151
|
});
|
|
148
152
|
});
|
|
@@ -3,13 +3,17 @@ import { describe, it, expect } from "vitest";
|
|
|
3
3
|
import { OkaySchema } from "./okay.schema";
|
|
4
4
|
|
|
5
5
|
describe("OkaySchema", () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
describe("Happy Path", () => {
|
|
7
|
+
it("accepts 2xx status codes", () => {
|
|
8
|
+
expect(OkaySchema.parse({ status: 200 })).toEqual({ status: 200 });
|
|
9
|
+
expect(OkaySchema.parse({ status: 299 })).toEqual({ status: 299 });
|
|
10
|
+
});
|
|
9
11
|
});
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
describe("Error Handling", () => {
|
|
14
|
+
it("rejects invalid input with non-2xx status codes", () => {
|
|
15
|
+
expect(() => OkaySchema.parse({ status: 199 })).toThrow();
|
|
16
|
+
expect(() => OkaySchema.parse({ status: 300 })).toThrow();
|
|
17
|
+
});
|
|
14
18
|
});
|
|
15
19
|
});
|