@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.
Files changed (100) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-check.log +129 -115
  3. package/.turbo/turbo-lint.log +4 -0
  4. package/dist/api-schemas/health.schema.js +2 -2
  5. package/dist/api-schemas/paginated-results.schema.js +1 -1
  6. package/dist/api-schemas/partial-results.schema.js +1 -1
  7. package/dist/api-schemas/result.schema.js +1 -1
  8. package/dist/api-schemas/results.schema.js +1 -1
  9. package/dist/helpers/correlation/get-correlation-id.js +1 -1
  10. package/dist/helpers/detect-mime-type.js +4 -4
  11. package/dist/helpers/parameter-validation.js +1 -1
  12. package/dist/helpers/response-headers/build-api-unauthorized-headers.js +1 -1
  13. package/dist/helpers/response-headers/resolve-environment.d.ts +1 -1
  14. package/dist/helpers/response-headers/resolve-environment.d.ts.map +1 -1
  15. package/dist/index.d.ts +33 -33
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +33 -33
  18. package/dist/primitives/geo-point.schema.js +2 -2
  19. package/dist/primitives/latitude.schema.js +1 -1
  20. package/dist/primitives/location.schema.js +1 -1
  21. package/dist/primitives/longitude.schema.js +1 -1
  22. package/dist/primitives/slug.schema.js +2 -2
  23. package/dist/time/bounded-time.schema.d.ts.map +1 -1
  24. package/dist/time/bounded-time.schema.js +19 -15
  25. package/dist/time/flexible-time-parser.d.ts +1 -1
  26. package/dist/time/flexible-time-parser.d.ts.map +1 -1
  27. package/dist/time/flexible-time-parser.js +2 -2
  28. package/dist/time/flexible-time.schema.js +3 -3
  29. package/dist/time/relative-time.schema.js +1 -1
  30. package/dist/time/time-helpers.js +2 -2
  31. package/dist/time/time-schemas.js +1 -1
  32. package/package.json +8 -8
  33. package/src/api-schemas/error.schema.test.ts +20 -14
  34. package/src/api-schemas/health.schema.test.ts +116 -112
  35. package/src/api-schemas/health.schema.ts +2 -2
  36. package/src/api-schemas/okay.schema.test.ts +10 -6
  37. package/src/api-schemas/paginated-results.schema.ts +1 -1
  38. package/src/api-schemas/partial-results.schema.ts +1 -1
  39. package/src/api-schemas/result.schema.test.ts +13 -9
  40. package/src/api-schemas/result.schema.ts +1 -1
  41. package/src/api-schemas/results.schema.test.ts +27 -7
  42. package/src/api-schemas/results.schema.ts +1 -1
  43. package/src/helpers/correlation/get-correlation-id.test.ts +1 -1
  44. package/src/helpers/correlation/get-correlation-id.ts +1 -1
  45. package/src/helpers/correlation/get-header.test.ts +2 -2
  46. package/src/helpers/detect-mime-type.test.ts +85 -81
  47. package/src/helpers/detect-mime-type.ts +4 -4
  48. package/src/helpers/detect-suspicious-patterns.test.ts +34 -28
  49. package/src/helpers/eventify-constants.test.ts +54 -34
  50. package/src/helpers/eventify-constants.types.test.ts +54 -34
  51. package/src/helpers/hash-binary.test.ts +50 -46
  52. package/src/helpers/mime-types/detect-image-mime-type.test.ts +59 -55
  53. package/src/helpers/mime-types/detect-ole-mime-type.test.ts +73 -69
  54. package/src/helpers/mime-types/detect-pdf-mime-type.test.ts +30 -26
  55. package/src/helpers/mime-types/detect-zip-mime-type.test.ts +76 -72
  56. package/src/helpers/parameter-validation.test.ts +27 -23
  57. package/src/helpers/parameter-validation.ts +1 -1
  58. package/src/helpers/process-eventify-request.ts +2 -2
  59. package/src/helpers/response-headers/build-api-unauthorized-headers.ts +1 -1
  60. package/src/helpers/response-headers/resolve-environment.ts +1 -1
  61. package/src/helpers/slugify.test.ts +62 -58
  62. package/src/index.ts +33 -33
  63. package/src/normalization/normalize-list.test.ts +36 -32
  64. package/src/normalization/normalize-location.test.ts +1 -1
  65. package/src/primitives/coordinate-precision.test.ts +38 -32
  66. package/src/primitives/geo-point.schema.test.ts +60 -56
  67. package/src/primitives/geo-point.schema.ts +2 -2
  68. package/src/primitives/geoname-id.schema.test.ts +50 -46
  69. package/src/primitives/international-zip.schema.test.ts +175 -121
  70. package/src/primitives/latitude.schema.test.ts +67 -61
  71. package/src/primitives/latitude.schema.ts +1 -1
  72. package/src/primitives/location.schema.test.ts +15 -11
  73. package/src/primitives/location.schema.ts +1 -1
  74. package/src/primitives/longitude.schema.test.ts +67 -61
  75. package/src/primitives/longitude.schema.ts +1 -1
  76. package/src/primitives/numeric-id.schema.test.ts +24 -20
  77. package/src/primitives/slug.schema.test.ts +93 -85
  78. package/src/primitives/slug.schema.ts +2 -2
  79. package/src/primitives/uuid.schema.test.ts +37 -33
  80. package/src/primitives/wikidata-id.schema.test.ts +48 -44
  81. package/src/time/boundary-enforcement.test.ts +1 -1
  82. package/src/time/bounded-time.schema.test.ts +2 -2
  83. package/src/time/bounded-time.schema.ts +19 -15
  84. package/src/time/flexible-time-parser.test.ts +3 -3
  85. package/src/time/flexible-time-parser.ts +2 -2
  86. package/src/time/flexible-time.schema.test.ts +6 -6
  87. package/src/time/flexible-time.schema.ts +3 -3
  88. package/src/time/is-relative-time.test.ts +18 -14
  89. package/src/time/iso8601.types.test.ts +4 -4
  90. package/src/time/parse-relative-time.test.ts +37 -33
  91. package/src/time/relative-time.schema.test.ts +18 -14
  92. package/src/time/relative-time.schema.ts +1 -1
  93. package/src/time/since-parameter.schema.test.ts +46 -42
  94. package/src/time/time-helpers.test.ts +2 -2
  95. package/src/time/time-helpers.ts +2 -2
  96. package/src/time/time-schemas.test.ts +5 -5
  97. package/src/time/time-schemas.ts +1 -1
  98. package/src/time/time.schema.test.ts +19 -15
  99. package/src/time/timezone-independence.test.ts +2 -2
  100. 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,OAsC5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,eAAoB,OAqC5E"}
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 minTime = parseRelativeTime(options.min, new Date(), "past");
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 (minimum: ${minTime})`,
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 maxTime = parseRelativeTime(options.max, new Date(), "future");
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 (maximum: ${maxTime})`,
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 minTime = parseRelativeTime(options.min, new Date(), "past");
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 (more recent than ${minTime})`,
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 maxTime = parseRelativeTime(options.max, new Date(), "past");
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 (older than ${maxTime})`,
77
+ message: `Time cannot be more than ${options.max} ago`,
74
78
  });
75
79
  }
76
80
  return schema;
@@ -1,4 +1,4 @@
1
- import { FlexibleTime } from "./time-schemas";
1
+ import { FlexibleTime } from "./time-schemas.js";
2
2
  /**
3
3
  * Parse a flexible time input (ISO 8601 or relative format) to ISO 8601 string
4
4
  * Uses Zod schemas for validation instead of manual parsing
@@ -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,gBAAgB,CAAC;AAKxB;;;;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
+ {"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
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { ISO8601Schema } from "./iso8601.types";
2
+ import { ISO8601Schema } from "./iso8601.types.js";
3
3
  export const RelativeTimeSchema = z.union([
4
4
  z.literal("now"),
5
5
  z.literal("today"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schafevormfenster/rest-commons",
3
- "version": "0.1.9",
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.3",
24
- "@typescript-eslint/eslint-plugin": "^8.51.0",
25
- "@typescript-eslint/parser": "^8.51.0",
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.16",
29
- "@schafevormfenster/eslint-config": "0.0.9"
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.7",
36
- "@schafevormfenster/security": "0.1.4"
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
- it("validates error shape for 4xx/5xx", () => {
7
- const parsed = ApiErrorSchema.parse({ status: 404, error: "Not Found" });
8
- expect(parsed.status).toBe(404);
9
- expect(parsed.error).toMatch(/not found/i);
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
- it("rejects non-error status codes", () => {
13
- expect(() => ApiErrorSchema.parse({ status: 200, error: "ok" })).toThrow();
14
- expect(() =>
15
- ApiErrorSchema.parse({ status: 600, error: "invalid" })
16
- ).toThrow();
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
- it("sets status and name", () => {
22
- const error = new ApiErrorConstructor(418, "I'm a teapot");
23
- expect(error.status).toBe(418);
24
- expect(error.message).toMatch(/i'm a teapot/i);
25
- expect(error.name).toBe("ApiError");
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
- it("parses healthy service info", () => {
17
- const service = HealthyServiceInfoSchema.parse({
18
- name: "Example",
19
- status: 200,
20
- message: "healthy",
21
- version: "1.0.0",
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
- it("parses unhealthy service info", () => {
29
- const service = UnhealthyServiceInfoSchema.parse({
30
- name: "Example",
31
- status: 503,
32
- error: "Service unavailable",
33
- version: "1.0.0",
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
- it("parses degraded service info", () => {
40
- const service = DegradedServiceInfoSchema.parse({
41
- name: "Example",
42
- status: 202,
43
- message: "degraded",
44
- version: "1.0.0",
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
- it("parses healthy API status", () => {
52
- const status = HealthyApiStatusSchema.parse({
53
- status: 200,
54
- name: "api",
55
- version: "1.0.0",
56
- description: "ok",
57
- services: [
58
- { name: "svc", status: 200, message: "healthy" },
59
- { name: "svc2", status: 500, error: "fail" },
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
- it("parses unhealthy API status", () => {
67
- const status = UnhealthyApiStatusSchema.parse({
68
- status: 500,
69
- error: "bad",
70
- name: "api",
71
- version: "1.0.0",
72
- services: [{ name: "svc", status: 500, error: "boom" }],
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
- it("parses degraded API status", () => {
78
- const status = DegradedApiStatusSchema.parse({
79
- status: 202,
80
- name: "api",
81
- version: "1.0.0",
82
- description: "partially operational",
83
- services: [
84
- { name: "svc1", status: 200, message: "healthy" },
85
- { name: "svc2", status: 202, message: "degraded" },
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
- it("ApiInfoSchema validates basic api info", () => {
93
- const info = ApiInfoSchema.parse({
94
- name: "api",
95
- version: "1.2.3",
96
- description: "desc",
97
- services: [{ name: "svc", status: 200, message: "healthy" }],
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
- it("ApiStatusSchema accepts healthy, degraded, and unhealthy variants", () => {
103
- const healthy = ApiStatusSchema.parse({
104
- status: 200,
105
- name: "api",
106
- version: "1.0.0",
107
- services: [{ name: "svc", status: 200, message: "healthy" }],
108
- });
109
- expect(healthy.status).toBe(200);
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
- const degraded = ApiStatusSchema.parse({
112
- status: 202,
113
- name: "api",
114
- version: "1.0.0",
115
- services: [{ name: "svc", status: 202, message: "degraded" }],
116
- });
117
- expect(degraded.status).toBe(202);
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
- const unhealthy = ApiStatusSchema.parse({
120
- status: 503,
121
- error: "down",
122
- name: "api",
123
- version: "1.0.0",
124
- services: [{ name: "svc", status: 503, error: "down" }],
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
- it("ServiceStatusSchema union validates all three variants", () => {
130
- expect(
131
- ServiceStatusSchema.parse({
132
- name: "svc",
133
- status: 200,
134
- message: "healthy",
135
- })
136
- ).toBeTruthy();
137
- expect(
138
- ServiceStatusSchema.parse({
139
- name: "svc",
140
- status: 202,
141
- message: "degraded",
142
- })
143
- ).toBeTruthy();
144
- expect(
145
- ServiceStatusSchema.parse({ name: "svc", status: 500, error: "err" })
146
- ).toBeTruthy();
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
  });
@@ -1,8 +1,8 @@
1
1
 
2
2
  import { z } from "zod";
3
3
 
4
- import { ApiErrorSchema } from "./error.schema";
5
- import { OkaySchema } from "./okay.schema";
4
+ import { ApiErrorSchema } from "./error.schema.js";
5
+ import { OkaySchema } from "./okay.schema.js";
6
6
 
7
7
  /**
8
8
  * Services
@@ -3,13 +3,17 @@ import { describe, it, expect } from "vitest";
3
3
  import { OkaySchema } from "./okay.schema";
4
4
 
5
5
  describe("OkaySchema", () => {
6
- it("accepts 2xx status codes", () => {
7
- expect(OkaySchema.parse({ status: 200 })).toEqual({ status: 200 });
8
- expect(OkaySchema.parse({ status: 299 })).toEqual({ status: 299 });
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
- it("rejects non-2xx status codes", () => {
12
- expect(() => OkaySchema.parse({ status: 199 })).toThrow();
13
- expect(() => OkaySchema.parse({ status: 300 })).toThrow();
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
  });