@telorun/type 0.0.1
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/index.d.ts +14 -0
- package/dist/index.js +57 -0
- package/package.json +30 -0
- package/src/index.ts +74 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ResourceContext, ResourceManifest, TypeRule } from "@telorun/sdk";
|
|
2
|
+
declare class TypeResource {
|
|
3
|
+
private readonly qualifiedName;
|
|
4
|
+
private readonly rules;
|
|
5
|
+
constructor(qualifiedName: string, rules: TypeRule[]);
|
|
6
|
+
/**
|
|
7
|
+
* Validate data against this type's rules (CEL conditions).
|
|
8
|
+
* Schema validation is handled by AJV via the schema registry;
|
|
9
|
+
* this method evaluates the business-rule layer on top.
|
|
10
|
+
*/
|
|
11
|
+
validateRules(data: unknown): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function create(resource: ResourceManifest, ctx: ResourceContext): Promise<TypeResource | null>;
|
|
14
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { evaluate } from "@marcbachmann/cel-js";
|
|
2
|
+
import { RuntimeError } from "@telorun/sdk";
|
|
3
|
+
class TypeResource {
|
|
4
|
+
qualifiedName;
|
|
5
|
+
rules;
|
|
6
|
+
constructor(qualifiedName, rules) {
|
|
7
|
+
this.qualifiedName = qualifiedName;
|
|
8
|
+
this.rules = rules;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate data against this type's rules (CEL conditions).
|
|
12
|
+
* Schema validation is handled by AJV via the schema registry;
|
|
13
|
+
* this method evaluates the business-rule layer on top.
|
|
14
|
+
*/
|
|
15
|
+
validateRules(data) {
|
|
16
|
+
for (const rule of this.rules) {
|
|
17
|
+
let result;
|
|
18
|
+
try {
|
|
19
|
+
result = evaluate(rule.condition, { this: data });
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
throw new RuntimeError("ERR_TYPE_VALIDATION_FAILED", `Type "${this.qualifiedName}" rule evaluation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23
|
+
}
|
|
24
|
+
if (result !== true) {
|
|
25
|
+
throw new RuntimeError(rule.code, rule.message ?? `Type "${this.qualifiedName}" validation failed: rule "${rule.code}" not satisfied`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function create(resource, ctx) {
|
|
31
|
+
const qualifiedName = `${resource.metadata.module}.${resource.metadata.name}`;
|
|
32
|
+
let schema = resource.schema;
|
|
33
|
+
const extendsField = resource.extends;
|
|
34
|
+
if (extendsField) {
|
|
35
|
+
const parents = Array.isArray(extendsField) ? extendsField : [extendsField];
|
|
36
|
+
// Defer if any parent schema isn't registered yet (multi-pass resolution)
|
|
37
|
+
for (const parent of parents) {
|
|
38
|
+
if (!ctx.lookupSchema(parent)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Merge: allOf [{ $ref: "Parent" }, ..., ownSchema]
|
|
43
|
+
schema = {
|
|
44
|
+
allOf: [...parents.map((p) => ({ $ref: p })), schema],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const rules = (Array.isArray(resource.rules) ? resource.rules : []);
|
|
48
|
+
ctx.registerSchema(qualifiedName, schema);
|
|
49
|
+
ctx.registerTypeRules(qualifiedName, rules);
|
|
50
|
+
// Also register under the short name so types can be referenced without module prefix
|
|
51
|
+
const shortName = resource.metadata.name;
|
|
52
|
+
if (shortName !== qualifiedName) {
|
|
53
|
+
ctx.registerSchema(shortName, schema);
|
|
54
|
+
ctx.registerTypeRules(shortName, rules);
|
|
55
|
+
}
|
|
56
|
+
return new TypeResource(qualifiedName, rules);
|
|
57
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@telorun/type",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"module": "./src/index.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**",
|
|
9
|
+
"src/**"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
"./type": {
|
|
13
|
+
"bun": "./src/index.ts",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.lib.json"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@marcbachmann/cel-js": "^7.5.3",
|
|
23
|
+
"@sinclair/typebox": "^0.34.48",
|
|
24
|
+
"@telorun/sdk": "workspace:*"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.0.0",
|
|
28
|
+
"typescript": "^5.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { evaluate } from "@marcbachmann/cel-js";
|
|
2
|
+
import type { ResourceContext, ResourceManifest, TypeRule } from "@telorun/sdk";
|
|
3
|
+
import { RuntimeError } from "@telorun/sdk";
|
|
4
|
+
|
|
5
|
+
class TypeResource {
|
|
6
|
+
constructor(
|
|
7
|
+
private readonly qualifiedName: string,
|
|
8
|
+
private readonly rules: TypeRule[],
|
|
9
|
+
) {}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate data against this type's rules (CEL conditions).
|
|
13
|
+
* Schema validation is handled by AJV via the schema registry;
|
|
14
|
+
* this method evaluates the business-rule layer on top.
|
|
15
|
+
*/
|
|
16
|
+
validateRules(data: unknown): void {
|
|
17
|
+
for (const rule of this.rules) {
|
|
18
|
+
let result: unknown;
|
|
19
|
+
try {
|
|
20
|
+
result = evaluate(rule.condition, { this: data });
|
|
21
|
+
} catch (err) {
|
|
22
|
+
throw new RuntimeError(
|
|
23
|
+
"ERR_TYPE_VALIDATION_FAILED",
|
|
24
|
+
`Type "${this.qualifiedName}" rule evaluation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
if (result !== true) {
|
|
28
|
+
throw new RuntimeError(
|
|
29
|
+
rule.code,
|
|
30
|
+
rule.message ?? `Type "${this.qualifiedName}" validation failed: rule "${rule.code}" not satisfied`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function create(
|
|
38
|
+
resource: ResourceManifest,
|
|
39
|
+
ctx: ResourceContext,
|
|
40
|
+
): Promise<TypeResource | null> {
|
|
41
|
+
const qualifiedName = `${resource.metadata.module}.${resource.metadata.name}`;
|
|
42
|
+
let schema = resource.schema as object;
|
|
43
|
+
|
|
44
|
+
const extendsField = resource.extends as string | string[] | undefined;
|
|
45
|
+
if (extendsField) {
|
|
46
|
+
const parents = Array.isArray(extendsField) ? extendsField : [extendsField];
|
|
47
|
+
|
|
48
|
+
// Defer if any parent schema isn't registered yet (multi-pass resolution)
|
|
49
|
+
for (const parent of parents) {
|
|
50
|
+
if (!ctx.lookupSchema(parent)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Merge: allOf [{ $ref: "Parent" }, ..., ownSchema]
|
|
56
|
+
schema = {
|
|
57
|
+
allOf: [...parents.map((p) => ({ $ref: p })), schema],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const rules = (Array.isArray(resource.rules) ? resource.rules : []) as TypeRule[];
|
|
62
|
+
|
|
63
|
+
ctx.registerSchema(qualifiedName, schema);
|
|
64
|
+
ctx.registerTypeRules(qualifiedName, rules);
|
|
65
|
+
|
|
66
|
+
// Also register under the short name so types can be referenced without module prefix
|
|
67
|
+
const shortName = resource.metadata.name;
|
|
68
|
+
if (shortName !== qualifiedName) {
|
|
69
|
+
ctx.registerSchema(shortName, schema);
|
|
70
|
+
ctx.registerTypeRules(shortName, rules);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return new TypeResource(qualifiedName, rules);
|
|
74
|
+
}
|