Skip to content

Commit 2409562

Browse files
committed
[fix]: enhance KeyPath decorator to track individual keypaths and enforce single keypath per class;
1 parent e893a6b commit 2409562

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

index.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ function KeyPath(fieldOrOptions?: string | KeyPathOptions, options?: KeyPathOpti
189189
if (arguments.length === 0) {
190190
return (target: any, propertyKey: string | symbol) => {
191191
const constructor = target.constructor as Function;
192+
193+
// Get existing individual keypaths or create new array
194+
const existingKeypaths = Reflect.getMetadata("individual_keypaths", constructor) || [];
195+
existingKeypaths.push(propertyKey as string);
196+
Reflect.defineMetadata("individual_keypaths", existingKeypaths, constructor);
197+
192198
const metadata: KeyPathMetadata = {
193199
fields: propertyKey as string,
194200
options: undefined
@@ -202,6 +208,14 @@ function KeyPath(fieldOrOptions?: string | KeyPathOptions, options?: KeyPathOpti
202208
return (target: any, propertyKey?: string | symbol) => {
203209
const constructor = target.constructor as Function;
204210
const field = propertyKey ? propertyKey as string : fieldOrOptions;
211+
212+
// Track individual property keypaths for validation only if it's a property decorator
213+
if (propertyKey) {
214+
const existingKeypaths = Reflect.getMetadata("individual_keypaths", constructor) || [];
215+
existingKeypaths.push(propertyKey as string);
216+
Reflect.defineMetadata("individual_keypaths", existingKeypaths, constructor);
217+
}
218+
205219
const metadata: KeyPathMetadata = {
206220
fields: field,
207221
options: options
@@ -214,6 +228,12 @@ function KeyPath(fieldOrOptions?: string | KeyPathOptions, options?: KeyPathOpti
214228
if (typeof fieldOrOptions === 'object' && !Array.isArray(fieldOrOptions)) {
215229
return (target: any, propertyKey: string | symbol) => {
216230
const constructor = target.constructor as Function;
231+
232+
// Get existing individual keypaths or create new array
233+
const existingKeypaths = Reflect.getMetadata("individual_keypaths", constructor) || [];
234+
existingKeypaths.push(propertyKey as string);
235+
Reflect.defineMetadata("individual_keypaths", existingKeypaths, constructor);
236+
217237
const metadata: KeyPathMetadata = {
218238
fields: propertyKey as string,
219239
options: fieldOrOptions
@@ -254,6 +274,14 @@ function DataClass(options: DataClassOptions = {}): ClassDecorator {
254274
if (!keyPathMetadata) {
255275
throw new Error(`No keypath field defined for the class ${target.name}.`);
256276
}
277+
278+
// Check for multiple property-level @KeyPath decorators (which is invalid)
279+
// This is different from composite keys which are defined at class level
280+
const individualKeypaths = Reflect.getMetadata("individual_keypaths", target) || [];
281+
if (individualKeypaths.length > 1) {
282+
throw new Error(`Only one keypath field can be defined for the class ${target.name}.`);
283+
}
284+
257285
const version = options.version || 1;
258286
Reflect.defineMetadata("dataclass", true, target);
259287
Reflect.defineMetadata("version", version, target);

0 commit comments

Comments
 (0)