@middy/util 7.3.1 → 7.3.3

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 (3) hide show
  1. package/index.d.ts +8 -1
  2. package/index.js +36 -3
  3. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -219,6 +219,7 @@ export type BooleanRule = {
219
219
  export type ArrayRule = {
220
220
  type: "array";
221
221
  items?: OptionSchemaRule;
222
+ uniqueItems?: boolean;
222
223
  examples?: readonly unknown[];
223
224
  };
224
225
 
@@ -251,6 +252,11 @@ export type OneOfRule = {
251
252
  examples?: readonly unknown[];
252
253
  };
253
254
 
255
+ export type AllOfRule = {
256
+ allOf: readonly OptionSchemaRule[];
257
+ examples?: readonly unknown[];
258
+ };
259
+
254
260
  export type OptionSchemaRule =
255
261
  | StringRule
256
262
  | NumberRule
@@ -260,7 +266,8 @@ export type OptionSchemaRule =
260
266
  | EnumRule
261
267
  | ConstRule
262
268
  | InstanceofRule
263
- | OneOfRule;
269
+ | OneOfRule
270
+ | AllOfRule;
264
271
 
265
272
  export type OptionSchema = ObjectRule;
266
273
 
package/index.js CHANGED
@@ -69,15 +69,16 @@ const checkTypeSpec = (rawType, value, path, fail) => {
69
69
  return true;
70
70
  };
71
71
 
72
- // Plain object with no rule-marker key (`type`, `enum`, `oneOf`, `const`,
73
- // `instanceof`) is a flat object schema; anything else is a rule. Used when
74
- // dispatching `items` and `additionalProperties`.
72
+ // Plain object with no rule-marker key (`type`, `enum`, `oneOf`, `allOf`,
73
+ // `const`, `instanceof`) is a flat object schema; anything else is a rule.
74
+ // Used when dispatching `items` and `additionalProperties`.
75
75
  const checkNestedRule = (rule, value, path, fail) => {
76
76
  if (
77
77
  isPlainObject(rule) &&
78
78
  typeof rule.type !== "string" &&
79
79
  !Array.isArray(rule.enum) &&
80
80
  !Array.isArray(rule.oneOf) &&
81
+ !Array.isArray(rule.allOf) &&
81
82
  !Object.hasOwn(rule, "const") &&
82
83
  typeof rule.instanceof !== "string"
83
84
  ) {
@@ -89,6 +90,20 @@ const checkNestedRule = (rule, value, path, fail) => {
89
90
 
90
91
  const childPathOf = (path, key) => (path ? `${path}.${key}` : key);
91
92
 
93
+ // Stable JSON form: recursively sorts object keys, skips function-typed
94
+ // values. Used for `uniqueItems` so items that differ only by handler
95
+ // identity or key ordering collide.
96
+ const stableStringify = (value) => {
97
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
98
+ if (Array.isArray(value)) {
99
+ return `[${value.map(stableStringify).join(",")}]`;
100
+ }
101
+ const keys = Object.keys(value)
102
+ .filter((k) => typeof value[k] !== "function")
103
+ .sort();
104
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
105
+ };
106
+
92
107
  const resolveInstance = (name) => {
93
108
  const ctor = globalThis[name];
94
109
  if (typeof ctor !== "function") {
@@ -115,6 +130,13 @@ const checkRule = (rule, value, path, fail) => {
115
130
  }
116
131
  return;
117
132
  }
133
+ if (isPlainObject(rule) && Array.isArray(rule.allOf)) {
134
+ if (value === undefined) return;
135
+ for (const sub of rule.allOf) {
136
+ checkRule(sub, value, path, fail);
137
+ }
138
+ return;
139
+ }
118
140
  if (isPlainObject(rule) && Array.isArray(rule.oneOf)) {
119
141
  if (value === undefined) return;
120
142
  let matches = 0;
@@ -154,6 +176,7 @@ const checkRule = (rule, value, path, fail) => {
154
176
  const {
155
177
  type: rawType,
156
178
  items,
179
+ uniqueItems,
157
180
  properties,
158
181
  required,
159
182
  additionalProperties,
@@ -185,6 +208,16 @@ const checkRule = (rule, value, path, fail) => {
185
208
  checkNestedRule(items, value[i], `${path}[${i}]`, fail);
186
209
  }
187
210
  }
211
+ if (type === "array" && uniqueItems === true) {
212
+ const seen = new Set();
213
+ for (let i = 0; i < value.length; i++) {
214
+ const key = stableStringify(value[i]);
215
+ if (seen.has(key)) {
216
+ fail(`Duplicate item at '${path}[${i}]'`);
217
+ }
218
+ seen.add(key);
219
+ }
220
+ }
188
221
  if (type === "object" && Array.isArray(required)) {
189
222
  for (const key of required) {
190
223
  if (value[key] === undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@middy/util",
3
- "version": "7.3.1",
3
+ "version": "7.3.3",
4
4
  "description": "🛵 The stylish Node.js middleware engine for AWS Lambda (util package)",
5
5
  "type": "module",
6
6
  "engines": {
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@aws-sdk/client-ssm": "^3.0.0",
60
- "@middy/core": "7.3.1",
60
+ "@middy/core": "7.3.3",
61
61
  "@types/aws-lambda": "^8.0.0",
62
62
  "@types/node": "^22.0.0",
63
63
  "aws-xray-sdk": "^3.3.3"