@happyvertical/smrt-core 0.36.8 → 0.37.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.
- package/dist/child-accessors.d.ts +1 -1
- package/dist/child-accessors.d.ts.map +1 -1
- package/dist/child-accessors.js +1 -1
- package/dist/child-accessors.js.map +1 -1
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +3 -1
- package/dist/class.js.map +1 -1
- package/dist/collection-cache.d.ts.map +1 -1
- package/dist/collection-cache.js +5 -3
- package/dist/collection-cache.js.map +1 -1
- package/dist/collection.d.ts +39 -16
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +40 -19
- package/dist/collection.js.map +1 -1
- package/dist/decorators/compatibility.d.ts +1 -1
- package/dist/decorators/compatibility.d.ts.map +1 -1
- package/dist/decorators/compatibility.js.map +1 -1
- package/dist/decorators/index.d.ts +4 -4
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/hierarchical.d.ts.map +1 -1
- package/dist/hierarchical.js.map +1 -1
- package/dist/junction.d.ts.map +1 -1
- package/dist/junction.js.map +1 -1
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +39 -20
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +2 -2
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
- package/dist/manifest/test-manifest-stub.js +2301 -629
- package/dist/manifest/test-manifest-stub.js.map +1 -1
- package/dist/manifest.json +39 -20
- package/dist/object.d.ts +64 -17
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +76 -30
- package/dist/object.js.map +1 -1
- package/dist/registry/class-registration.d.ts +3 -3
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +39 -42
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/inheritance-resolver.d.ts +17 -3
- package/dist/registry/inheritance-resolver.d.ts.map +1 -1
- package/dist/registry/inheritance-resolver.js +1 -1
- package/dist/registry/inheritance-resolver.js.map +1 -1
- package/dist/registry/manifest-field-merge.d.ts +17 -3
- package/dist/registry/manifest-field-merge.d.ts.map +1 -1
- package/dist/registry/manifest-field-merge.js +8 -6
- package/dist/registry/manifest-field-merge.js.map +1 -1
- package/dist/registry/schema-builder.d.ts +1 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/shared-state.d.ts +3 -3
- package/dist/registry/shared-state.d.ts.map +1 -1
- package/dist/registry/shared-state.js.map +1 -1
- package/dist/registry/types.d.ts +78 -19
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry/validator.d.ts +2 -1
- package/dist/registry/validator.d.ts.map +1 -1
- package/dist/registry/validator.js +38 -39
- package/dist/registry/validator.js.map +1 -1
- package/dist/registry.d.ts +84 -57
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +31 -25
- package/dist/registry.js.map +1 -1
- package/dist/smrt-knowledge.json +5 -4
- package/dist/system-fields.d.ts +1 -1
- package/dist/system-fields.d.ts.map +1 -1
- package/dist/system-fields.js.map +1 -1
- package/package.json +4 -4
package/dist/registry/types.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { DomainKnowledgeConfig } from '@happyvertical/smrt-types';
|
|
2
2
|
import { SmrtCollection } from '../collection';
|
|
3
3
|
import { CollectionCacheConfig } from '../collection-cache';
|
|
4
|
+
import { FieldOptions } from '../decorators/index.js';
|
|
4
5
|
import { SmrtObject } from '../object';
|
|
5
|
-
import { QualifiedClassName, SmartObjectManifest, SmrtVisibility, ValidationRule } from '../scanner/types.js';
|
|
6
|
+
import { FieldDefinition, FieldMeta, MethodDefinition, QualifiedClassName, SmartObjectManifest, SmrtVisibility, ValidationRule } from '../scanner/types.js';
|
|
6
7
|
import { SchemaDefinition } from '../schema/types.js';
|
|
7
8
|
/**
|
|
8
9
|
* Type for any constructor function that extends SmrtObject.
|
|
@@ -88,11 +89,11 @@ export interface ApiConfig {
|
|
|
88
89
|
/**
|
|
89
90
|
* Custom middleware for this object's endpoints
|
|
90
91
|
*/
|
|
91
|
-
middleware?:
|
|
92
|
+
middleware?: unknown[];
|
|
92
93
|
/**
|
|
93
94
|
* Custom endpoint handlers (supports both standard CRUD actions and custom methods)
|
|
94
95
|
*/
|
|
95
|
-
customize?: Record<string, (req:
|
|
96
|
+
customize?: Record<string, (req: unknown, collection: SmrtCollection<SmrtObject>) => Promise<unknown>>;
|
|
96
97
|
/**
|
|
97
98
|
* Route metadata for custom API methods.
|
|
98
99
|
* Lets generated routes declare collection-vs-item scope, HTTP verb, and path.
|
|
@@ -348,14 +349,14 @@ export interface SmartObjectConfig {
|
|
|
348
349
|
* Lifecycle hooks
|
|
349
350
|
*/
|
|
350
351
|
hooks?: {
|
|
351
|
-
beforeSave?: string | ((instance:
|
|
352
|
-
afterSave?: string | ((instance:
|
|
353
|
-
beforeCreate?: string | ((instance:
|
|
354
|
-
afterCreate?: string | ((instance:
|
|
355
|
-
beforeUpdate?: string | ((instance:
|
|
356
|
-
afterUpdate?: string | ((instance:
|
|
357
|
-
beforeDelete?: string | ((instance:
|
|
358
|
-
afterDelete?: string | ((instance:
|
|
352
|
+
beforeSave?: string | ((instance: SmrtObject) => Promise<void>);
|
|
353
|
+
afterSave?: string | ((instance: SmrtObject) => Promise<void>);
|
|
354
|
+
beforeCreate?: string | ((instance: SmrtObject) => Promise<void>);
|
|
355
|
+
afterCreate?: string | ((instance: SmrtObject) => Promise<void>);
|
|
356
|
+
beforeUpdate?: string | ((instance: SmrtObject) => Promise<void>);
|
|
357
|
+
afterUpdate?: string | ((instance: SmrtObject) => Promise<void>);
|
|
358
|
+
beforeDelete?: string | ((instance: SmrtObject) => Promise<void>);
|
|
359
|
+
afterDelete?: string | ((instance: SmrtObject) => Promise<void>);
|
|
359
360
|
};
|
|
360
361
|
/**
|
|
361
362
|
* Embedding configuration for semantic search
|
|
@@ -584,7 +585,7 @@ export interface SmartObjectConfig {
|
|
|
584
585
|
* Validation function that takes an object instance and returns
|
|
585
586
|
* a ValidationError if validation fails, or null if validation passes
|
|
586
587
|
*/
|
|
587
|
-
export type ValidatorFunction = (instance:
|
|
588
|
+
export type ValidatorFunction = (instance: SmrtObject) => Promise<import('../errors').ValidationError | null>;
|
|
588
589
|
/**
|
|
589
590
|
* Relationship type for the relationship map
|
|
590
591
|
*/
|
|
@@ -601,8 +602,54 @@ export interface RelationshipMetadata {
|
|
|
601
602
|
targetClass: string;
|
|
602
603
|
/** Type of relationship */
|
|
603
604
|
type: RelationshipType;
|
|
604
|
-
/** Options for the relationship (onDelete, etc.) */
|
|
605
|
-
options:
|
|
605
|
+
/** Options for the relationship (onDelete, etc.) — sourced from `field._meta`. */
|
|
606
|
+
options: FieldMeta | undefined;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Runtime shape of a field stored in a {@link RegisteredClass} `fields` /
|
|
610
|
+
* `inheritedFields` map.
|
|
611
|
+
*
|
|
612
|
+
* This is a {@link FieldDefinition} superset: registration
|
|
613
|
+
* (`class-registration.ts`) and the manifest-field merge helpers build these
|
|
614
|
+
* objects by folding decorator options into the AST-scanned field, which
|
|
615
|
+
* attaches a few extra runtime-only members beyond the scanner contract:
|
|
616
|
+
*
|
|
617
|
+
* - `options`: legacy mirror of the decorator option bag, persisted into the
|
|
618
|
+
* `_smrt_registry` snapshot row (see `registry.ts` `saveRegistry`).
|
|
619
|
+
* - `validate`: optional cross-package-ref validation hint read off either the
|
|
620
|
+
* field or its `_meta` bag (`object.ts` `validateCrossPackageRefs`).
|
|
621
|
+
* - `__report`: report-aggregate marker mirrored at the top level for the
|
|
622
|
+
* report conflict-column derivation (`registry.ts`).
|
|
623
|
+
* - `idType`: synthetic-id storage override mirrored at the top level
|
|
624
|
+
* (decorator bags; also read off `_meta.idType` in `schema-builder.ts`).
|
|
625
|
+
* - `value`: runtime field value carried through the inheritance-chain merge
|
|
626
|
+
* (`inheritance-resolver.ts` `mergeFieldConfigs`).
|
|
627
|
+
*
|
|
628
|
+
* This is the single canonical runtime-field shape: the manifest-merge helpers
|
|
629
|
+
* (`manifest-field-merge.ts`) and the inheritance-chain merge
|
|
630
|
+
* (`inheritance-resolver.ts`) both alias this type, so registered fields flow
|
|
631
|
+
* through one definition instead of drifting per-module supersets.
|
|
632
|
+
*
|
|
633
|
+
* The index signature keeps the bag open for forward-compatible runtime keys
|
|
634
|
+
* without forcing consumers back through `any`.
|
|
635
|
+
*/
|
|
636
|
+
export interface RegisteredField extends FieldDefinition {
|
|
637
|
+
/** Decorator option bag mirrored at the top level (carries `description`,
|
|
638
|
+
* `required`, relationship keys, etc. that consumers like the CLI read). */
|
|
639
|
+
options?: FieldOptions;
|
|
640
|
+
/** Cross-package-ref validation hint (also looked up under `_meta`). */
|
|
641
|
+
validate?: unknown;
|
|
642
|
+
/** Report-aggregate marker mirrored from `_meta.__report`. */
|
|
643
|
+
__report?: FieldMeta['__report'];
|
|
644
|
+
/** Tenancy marker mirrored at the top level on decorator option bags. */
|
|
645
|
+
__tenancy?: FieldMeta['__tenancy'];
|
|
646
|
+
/** Explicit SQL type override mirrored at the top level (decorator bags). */
|
|
647
|
+
sqlType?: string;
|
|
648
|
+
/** Synthetic-id storage override mirrored at the top level (decorator bags). */
|
|
649
|
+
idType?: 'uuid' | 'text';
|
|
650
|
+
/** Runtime field value carried through the inheritance-chain merge. */
|
|
651
|
+
value?: unknown;
|
|
652
|
+
[key: string]: unknown;
|
|
606
653
|
}
|
|
607
654
|
/**
|
|
608
655
|
* Internal representation of a registered SMRT class
|
|
@@ -621,11 +668,23 @@ export interface RegisteredClass {
|
|
|
621
668
|
*/
|
|
622
669
|
qualifiedName?: QualifiedClassName;
|
|
623
670
|
constructor: typeof SmrtObject;
|
|
671
|
+
/**
|
|
672
|
+
* Collection constructor for this class.
|
|
673
|
+
*
|
|
674
|
+
* The `options` param and `SmrtCollection<any>` element type are left as
|
|
675
|
+
* `any` deliberately (S4 #1579): this slot is assigned from `typeof
|
|
676
|
+
* SmrtCollection` (a generic constructor whose `options?` is contravariant)
|
|
677
|
+
* AND instantiated by generators that pass a loosely-typed context bag
|
|
678
|
+
* (`{ ai, db }` where `context.ai` is `unknown`). A precise `options` type
|
|
679
|
+
* breaks the call sites; a precise element type breaks the assignment from
|
|
680
|
+
* `typeof SmrtCollection`. This mirrors the `SmrtObjectConstructor` escape
|
|
681
|
+
* hatch above.
|
|
682
|
+
*/
|
|
624
683
|
collectionConstructor?: new (options: any) => SmrtCollection<any>;
|
|
625
684
|
config: SmartObjectConfig;
|
|
626
|
-
fields: Map<string,
|
|
685
|
+
fields: Map<string, RegisteredField>;
|
|
627
686
|
/** Method definitions from manifest (for custom CLI/API/MCP generation) */
|
|
628
|
-
methods: Map<string,
|
|
687
|
+
methods: Map<string, MethodDefinition>;
|
|
629
688
|
/** Cached schema definition generated during registration */
|
|
630
689
|
schema?: SchemaDefinition;
|
|
631
690
|
/** Compiled validation functions for efficient runtime validation */
|
|
@@ -642,7 +701,7 @@ export interface RegisteredClass {
|
|
|
642
701
|
function: {
|
|
643
702
|
name: string;
|
|
644
703
|
description?: string;
|
|
645
|
-
parameters?: Record<string,
|
|
704
|
+
parameters?: Record<string, unknown>;
|
|
646
705
|
};
|
|
647
706
|
}>;
|
|
648
707
|
/** Package name from manifest (for external package classes) */
|
|
@@ -666,9 +725,9 @@ export interface RegisteredClass {
|
|
|
666
725
|
/** Full inheritance chain from base to this class (cached for performance) */
|
|
667
726
|
inheritanceChain?: string[];
|
|
668
727
|
/** Merged fields from entire inheritance chain (cached, includes parent fields) */
|
|
669
|
-
inheritedFields?: Map<string,
|
|
728
|
+
inheritedFields?: Map<string, RegisteredField>;
|
|
670
729
|
/** Merged methods from entire inheritance chain (cached, includes parent methods) */
|
|
671
|
-
inheritedMethods?: Map<string,
|
|
730
|
+
inheritedMethods?: Map<string, MethodDefinition>;
|
|
672
731
|
/** Normalized tenant scoping configuration (Issue #688) */
|
|
673
732
|
tenantScopedConfig?: {
|
|
674
733
|
mode: 'required' | 'optional';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;GAGG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FieldDefinition } from '../scanner/types.js';
|
|
1
2
|
import { ValidatorFunction } from './types';
|
|
2
3
|
/**
|
|
3
4
|
* Compile validation functions from field definitions.
|
|
@@ -9,5 +10,5 @@ import { ValidatorFunction } from './types';
|
|
|
9
10
|
* @param fields - Map of field definitions
|
|
10
11
|
* @returns Array of compiled validation functions
|
|
11
12
|
*/
|
|
12
|
-
export declare function compileValidators(className: string, fields: Map<string,
|
|
13
|
+
export declare function compileValidators(className: string, fields: Map<string, FieldDefinition>): ValidatorFunction[];
|
|
13
14
|
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/registry/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/registry/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAiBjD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GACnC,iBAAiB,EAAE,CAwKrB"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
function readInstanceField(instance, fieldName) {
|
|
2
|
+
return instance[fieldName];
|
|
3
|
+
}
|
|
1
4
|
function compileValidators(className, fields) {
|
|
2
5
|
const validators = [];
|
|
3
6
|
for (const [fieldName, field] of fields) {
|
|
4
|
-
const options = field._meta
|
|
7
|
+
const options = field._meta ?? {};
|
|
5
8
|
if (options.transient || field.transient) {
|
|
6
9
|
continue;
|
|
7
10
|
}
|
|
8
|
-
if (options.required) {
|
|
11
|
+
if (options.required || field.required) {
|
|
9
12
|
validators.push(async (instance) => {
|
|
10
|
-
const value = instance
|
|
13
|
+
const value = readInstanceField(instance, fieldName);
|
|
11
14
|
if (value === null || value === void 0 || value === "") {
|
|
12
15
|
const ValidationError = await import("../errors.js").then(
|
|
13
16
|
(m) => m.ValidationError
|
|
@@ -17,79 +20,72 @@ function compileValidators(className, fields) {
|
|
|
17
20
|
return null;
|
|
18
21
|
});
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
const fieldType = field.type;
|
|
24
|
+
if (fieldType === "integer" || fieldType === "decimal" || fieldType === "number") {
|
|
25
|
+
const { min, max } = options;
|
|
26
|
+
if (min !== void 0) {
|
|
22
27
|
validators.push(async (instance) => {
|
|
23
|
-
const value = instance
|
|
24
|
-
if (value !== null && value !== void 0 && value <
|
|
28
|
+
const value = readInstanceField(instance, fieldName);
|
|
29
|
+
if (value !== null && value !== void 0 && value < min) {
|
|
25
30
|
const ValidationError = await import("../errors.js").then(
|
|
26
31
|
(m) => m.ValidationError
|
|
27
32
|
);
|
|
28
|
-
return ValidationError.rangeError(
|
|
29
|
-
fieldName,
|
|
30
|
-
value,
|
|
31
|
-
options.min,
|
|
32
|
-
options.max
|
|
33
|
-
);
|
|
33
|
+
return ValidationError.rangeError(fieldName, value, min, max);
|
|
34
34
|
}
|
|
35
35
|
return null;
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
if (
|
|
38
|
+
if (max !== void 0) {
|
|
39
39
|
validators.push(async (instance) => {
|
|
40
|
-
const value = instance
|
|
41
|
-
if (value !== null && value !== void 0 && value >
|
|
40
|
+
const value = readInstanceField(instance, fieldName);
|
|
41
|
+
if (value !== null && value !== void 0 && value > max) {
|
|
42
42
|
const ValidationError = await import("../errors.js").then(
|
|
43
43
|
(m) => m.ValidationError
|
|
44
44
|
);
|
|
45
|
-
return ValidationError.rangeError(
|
|
46
|
-
fieldName,
|
|
47
|
-
value,
|
|
48
|
-
options.min,
|
|
49
|
-
options.max
|
|
50
|
-
);
|
|
45
|
+
return ValidationError.rangeError(fieldName, value, min, max);
|
|
51
46
|
}
|
|
52
47
|
return null;
|
|
53
48
|
});
|
|
54
49
|
}
|
|
55
50
|
}
|
|
56
|
-
if (
|
|
57
|
-
|
|
51
|
+
if (fieldType === "text") {
|
|
52
|
+
const { minLength, maxLength, pattern } = options;
|
|
53
|
+
if (minLength !== void 0) {
|
|
58
54
|
validators.push(async (instance) => {
|
|
59
|
-
const value = instance
|
|
60
|
-
if (value && typeof value === "string" && value.length <
|
|
55
|
+
const value = readInstanceField(instance, fieldName);
|
|
56
|
+
if (value && typeof value === "string" && value.length < minLength) {
|
|
61
57
|
const ValidationError = await import("../errors.js").then(
|
|
62
58
|
(m) => m.ValidationError
|
|
63
59
|
);
|
|
64
60
|
return ValidationError.invalidValue(
|
|
65
61
|
fieldName,
|
|
66
62
|
value,
|
|
67
|
-
`string with minimum length ${
|
|
63
|
+
`string with minimum length ${minLength}`
|
|
68
64
|
);
|
|
69
65
|
}
|
|
70
66
|
return null;
|
|
71
67
|
});
|
|
72
68
|
}
|
|
73
|
-
if (
|
|
69
|
+
if (maxLength !== void 0) {
|
|
74
70
|
validators.push(async (instance) => {
|
|
75
|
-
const value = instance
|
|
76
|
-
if (value && typeof value === "string" && value.length >
|
|
71
|
+
const value = readInstanceField(instance, fieldName);
|
|
72
|
+
if (value && typeof value === "string" && value.length > maxLength) {
|
|
77
73
|
const ValidationError = await import("../errors.js").then(
|
|
78
74
|
(m) => m.ValidationError
|
|
79
75
|
);
|
|
80
76
|
return ValidationError.invalidValue(
|
|
81
77
|
fieldName,
|
|
82
78
|
value,
|
|
83
|
-
`string with maximum length ${
|
|
79
|
+
`string with maximum length ${maxLength}`
|
|
84
80
|
);
|
|
85
81
|
}
|
|
86
82
|
return null;
|
|
87
83
|
});
|
|
88
84
|
}
|
|
89
|
-
if (
|
|
90
|
-
const regex = new RegExp(
|
|
85
|
+
if (typeof pattern === "string" || pattern instanceof RegExp) {
|
|
86
|
+
const regex = new RegExp(pattern);
|
|
91
87
|
validators.push(async (instance) => {
|
|
92
|
-
const value = instance
|
|
88
|
+
const value = readInstanceField(instance, fieldName);
|
|
93
89
|
if (value && typeof value === "string" && !regex.test(value)) {
|
|
94
90
|
const ValidationError = await import("../errors.js").then(
|
|
95
91
|
(m) => m.ValidationError
|
|
@@ -97,23 +93,26 @@ function compileValidators(className, fields) {
|
|
|
97
93
|
return ValidationError.invalidValue(
|
|
98
94
|
fieldName,
|
|
99
95
|
value,
|
|
100
|
-
`string matching pattern ${
|
|
96
|
+
`string matching pattern ${String(pattern)}`
|
|
101
97
|
);
|
|
102
98
|
}
|
|
103
99
|
return null;
|
|
104
100
|
});
|
|
105
101
|
}
|
|
106
102
|
}
|
|
107
|
-
|
|
103
|
+
const customValidate = options.validate;
|
|
104
|
+
if (typeof customValidate === "function") {
|
|
105
|
+
const validateFn = customValidate;
|
|
106
|
+
const customMessage = typeof options.customMessage === "string" ? options.customMessage : void 0;
|
|
108
107
|
validators.push(async (instance) => {
|
|
109
|
-
const value = instance
|
|
108
|
+
const value = readInstanceField(instance, fieldName);
|
|
110
109
|
try {
|
|
111
|
-
const isValid = await
|
|
110
|
+
const isValid = await validateFn(value);
|
|
112
111
|
if (!isValid) {
|
|
113
112
|
const ValidationError = await import("../errors.js").then(
|
|
114
113
|
(m) => m.ValidationError
|
|
115
114
|
);
|
|
116
|
-
const message =
|
|
115
|
+
const message = customMessage || `Field ${fieldName} failed custom validation`;
|
|
117
116
|
return ValidationError.invalidValue(fieldName, value, message);
|
|
118
117
|
}
|
|
119
118
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sources":["../../src/registry/validator.ts"],"sourcesContent":["/**\n * Validator module for the SMRT ObjectRegistry.\n *\n * Compiles validation functions from field definitions for runtime use.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n */\n\nimport type { ValidatorFunction } from './types';\n\n/**\n * Compile validation functions from field definitions.\n *\n * Extracts validation rules from field options and compiles them into\n * efficient validation functions that can be executed at runtime.\n *\n * @param className - Name of the class being validated\n * @param fields - Map of field definitions\n * @returns Array of compiled validation functions\n */\nexport function compileValidators(\n className: string,\n fields: Map<string, any>,\n): ValidatorFunction[] {\n const validators: ValidatorFunction[] = [];\n\n for (const [fieldName, field] of fields) {\n const options = field._meta || {};\n\n // Skip transient fields (they're not persisted, so no validation needed)\n if (options.transient || field.transient) {\n continue;\n }\n\n // Required field validator\n if (options.required) {\n validators.push(async (instance: any) => {\n const value = instance[fieldName];\n if (value === null || value === undefined || value === '') {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.requiredField(fieldName, className);\n }\n return null;\n });\n }\n\n // Numeric range validators\n if (\n field.type === 'integer' ||\n field.type === 'decimal' ||\n field.type === 'number'\n ) {\n if (options.min !== undefined) {\n validators.push(async (instance: any) => {\n const value = instance[fieldName];\n if (value !== null && value !== undefined && value < options.min) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.rangeError(\n fieldName,\n value,\n options.min,\n options.max,\n );\n }\n return null;\n });\n }\n\n if (options.max !== undefined) {\n validators.push(async (instance: any) => {\n const value = instance[fieldName];\n if (value !== null && value !== undefined && value > options.max) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.rangeError(\n fieldName,\n value,\n options.min,\n options.max,\n );\n }\n return null;\n });\n }\n }\n\n // String length validators\n if (field.type === 'text') {\n if (options.minLength !== undefined) {\n validators.push(async (instance: any) => {\n const value = instance[fieldName];\n if (\n value &&\n typeof value === 'string' &&\n value.length < options.minLength\n ) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `string with minimum length ${options.minLength}`,\n );\n }\n return null;\n });\n }\n\n if (options.maxLength !== undefined) {\n validators.push(async (instance: any) => {\n const value = instance[fieldName];\n if (\n value &&\n typeof value === 'string' &&\n value.length > options.maxLength\n ) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `string with maximum length ${options.maxLength}`,\n );\n }\n return null;\n });\n }\n\n // Pattern validator (regex)\n if (options.pattern) {\n const regex = new RegExp(options.pattern);\n validators.push(async (instance: any) => {\n const value = instance[fieldName];\n if (value && typeof value === 'string' && !regex.test(value)) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `string matching pattern ${options.pattern}`,\n );\n }\n return null;\n });\n }\n }\n\n // Custom validator function\n if (options.validate && typeof options.validate === 'function') {\n validators.push(async (instance: any) => {\n const value = instance[fieldName];\n try {\n const isValid = await options.validate(value);\n if (!isValid) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n const message =\n options.customMessage ||\n `Field ${fieldName} failed custom validation`;\n return ValidationError.invalidValue(fieldName, value, message);\n }\n } catch (error) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `custom validation error: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n return null;\n });\n }\n }\n\n return validators;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validator.js","sources":["../../src/registry/validator.ts"],"sourcesContent":["/**\n * Validator module for the SMRT ObjectRegistry.\n *\n * Compiles validation functions from field definitions for runtime use.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n */\n\nimport type { SmrtObject } from '../object';\nimport type { FieldDefinition, FieldMeta } from '../scanner/types.js';\nimport type { ValidatorFunction } from './types';\n\n/**\n * Read a runtime field value off a SmrtObject instance by dynamic field name.\n *\n * Compiled validators index the instance by `fieldName` (a manifest-driven\n * string), but `SmrtObject` has no string index signature, so a direct\n * `instance[fieldName]` is an implicit-`any` access (TS7053). This single\n * documented view casts through `unknown` to a `Record<string, unknown>` so the\n * dynamic read is typed `unknown` (not `any`) while preserving exact runtime\n * behavior. Routing every validator's field read through here keeps the cast in\n * one place instead of scattering it across each closure.\n */\nfunction readInstanceField(instance: SmrtObject, fieldName: string): unknown {\n return (instance as unknown as Record<string, unknown>)[fieldName];\n}\n\n/**\n * Compile validation functions from field definitions.\n *\n * Extracts validation rules from field options and compiles them into\n * efficient validation functions that can be executed at runtime.\n *\n * @param className - Name of the class being validated\n * @param fields - Map of field definitions\n * @returns Array of compiled validation functions\n */\nexport function compileValidators(\n className: string,\n fields: Map<string, FieldDefinition>,\n): ValidatorFunction[] {\n const validators: ValidatorFunction[] = [];\n\n for (const [fieldName, field] of fields) {\n const options: FieldMeta = field._meta ?? {};\n\n // Skip transient fields (they're not persisted, so no validation needed)\n if (options.transient || field.transient) {\n continue;\n }\n\n // Required field validator. Check both the `_meta` bag and the top-level\n // `field.required` (some registration paths, e.g. tenantScoped injection,\n // set the top-level flag) — mirrors the `transient` check above.\n if (options.required || field.required) {\n validators.push(async (instance) => {\n const value = readInstanceField(instance, fieldName);\n if (value === null || value === undefined || value === '') {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.requiredField(fieldName, className);\n }\n return null;\n });\n }\n\n // Numeric range validators. The scanner's `FieldDefinition['type']` union\n // does not list `'number'`, but external/legacy manifests can still carry\n // it, so widen the comparison to a string check rather than dropping it.\n const fieldType: string = field.type;\n if (\n fieldType === 'integer' ||\n fieldType === 'decimal' ||\n fieldType === 'number'\n ) {\n const { min, max } = options;\n if (min !== undefined) {\n validators.push(async (instance) => {\n // Numeric-field branch: the runtime value is treated as a number for\n // the range comparison (JS coercion semantics preserved — the cast is\n // erased, so the runtime read/compare is unchanged).\n const value = readInstanceField(instance, fieldName) as number;\n if (value !== null && value !== undefined && value < min) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.rangeError(fieldName, value, min, max);\n }\n return null;\n });\n }\n\n if (max !== undefined) {\n validators.push(async (instance) => {\n // See the `min` branch: numeric-field value typed as `number` for the\n // range comparison; runtime behavior is unchanged.\n const value = readInstanceField(instance, fieldName) as number;\n if (value !== null && value !== undefined && value > max) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.rangeError(fieldName, value, min, max);\n }\n return null;\n });\n }\n }\n\n // String length validators\n if (fieldType === 'text') {\n const { minLength, maxLength, pattern } = options;\n if (minLength !== undefined) {\n validators.push(async (instance) => {\n const value = readInstanceField(instance, fieldName);\n if (value && typeof value === 'string' && value.length < minLength) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `string with minimum length ${minLength}`,\n );\n }\n return null;\n });\n }\n\n if (maxLength !== undefined) {\n validators.push(async (instance) => {\n const value = readInstanceField(instance, fieldName);\n if (value && typeof value === 'string' && value.length > maxLength) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `string with maximum length ${maxLength}`,\n );\n }\n return null;\n });\n }\n\n // Pattern validator (regex). `FieldMeta.pattern` is `unknown` (a string\n // source, a `RegExp`, or an opaque `{}` from a JSON-collapsed manifest);\n // only string/RegExp sources can build a usable regex.\n if (typeof pattern === 'string' || pattern instanceof RegExp) {\n const regex = new RegExp(pattern);\n validators.push(async (instance) => {\n const value = readInstanceField(instance, fieldName);\n if (value && typeof value === 'string' && !regex.test(value)) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `string matching pattern ${String(pattern)}`,\n );\n }\n return null;\n });\n }\n }\n\n // Custom validator function. `validate`/`customMessage` arrive through the\n // open `FieldMeta` index signature as `unknown`; narrow to the callable and\n // string shapes the validator relies on.\n const customValidate = options.validate;\n if (typeof customValidate === 'function') {\n const validateFn = customValidate as (\n value: unknown,\n ) => boolean | Promise<boolean>;\n const customMessage =\n typeof options.customMessage === 'string'\n ? options.customMessage\n : undefined;\n validators.push(async (instance) => {\n const value = readInstanceField(instance, fieldName);\n try {\n const isValid = await validateFn(value);\n if (!isValid) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n const message =\n customMessage || `Field ${fieldName} failed custom validation`;\n return ValidationError.invalidValue(fieldName, value, message);\n }\n } catch (error) {\n const ValidationError = await import('../errors').then(\n (m) => m.ValidationError,\n );\n return ValidationError.invalidValue(\n fieldName,\n value,\n `custom validation error: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n return null;\n });\n }\n }\n\n return validators;\n}\n"],"names":[],"mappings":"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"}
|