@stonecrop/stonecrop 0.6.3 → 0.7.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/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -0
- package/dist/src/schema-validator.d.ts +132 -0
- package/dist/src/schema-validator.d.ts.map +1 -0
- package/dist/src/schema-validator.js +315 -0
- package/dist/stonecrop.d.ts +140 -14
- package/dist/stonecrop.js +949 -730
- package/dist/stonecrop.js.map +1 -1
- package/dist/stonecrop.umd.cjs +3 -3
- package/dist/stonecrop.umd.cjs.map +1 -1
- package/package.json +21 -21
- package/src/index.ts +9 -0
- package/src/schema-validator.ts +427 -0
- package/dist/composable.js +0 -348
- package/dist/composables/operation-log.js +0 -221
- package/dist/doctype.js +0 -73
- package/dist/exceptions.js +0 -16
- package/dist/field-triggers.js +0 -564
- package/dist/index.js +0 -18
- package/dist/operation-log-DB-dGNT9.js +0 -593
- package/dist/operation-log-DB-dGNT9.js.map +0 -1
- package/dist/plugins/index.js +0 -90
- package/dist/registry.js +0 -72
- package/dist/src/stores/data.d.ts +0 -11
- package/dist/src/stores/data.d.ts.map +0 -1
- package/dist/src/stores/xstate.d.ts +0 -31
- package/dist/src/stores/xstate.d.ts.map +0 -1
- package/dist/stores/data.js +0 -7
- package/dist/stores/hst.js +0 -483
- package/dist/stores/index.js +0 -12
- package/dist/stores/operation-log.js +0 -571
- package/dist/stores/xstate.js +0 -29
- package/dist/types/field-triggers.js +0 -4
- package/dist/types/index.js +0 -4
- package/dist/types/operation-log.js +0 -0
- package/dist/types/registry.js +0 -0
package/dist/src/index.d.ts
CHANGED
|
@@ -9,10 +9,13 @@ import Registry from './registry';
|
|
|
9
9
|
import { Stonecrop } from './stonecrop';
|
|
10
10
|
import { HST, createHST, type HSTNode } from './stores/hst';
|
|
11
11
|
import { useOperationLogStore } from './stores/operation-log';
|
|
12
|
+
import { SchemaValidator, createValidator, validateSchema } from './schema-validator';
|
|
12
13
|
export type * from './types';
|
|
13
14
|
export type { BaseStonecropReturn, HSTChangeData, HSTStonecropReturn, OperationLogAPI } from './composable';
|
|
14
15
|
export type { FieldTriggerEngine } from './field-triggers';
|
|
15
16
|
export type { FieldChangeContext, TransitionChangeContext, FieldTriggerExecutionResult, ActionExecutionResult, TransitionExecutionResult, FieldActionFunction, TransitionActionFunction, } from './types/field-triggers';
|
|
16
|
-
export {
|
|
17
|
+
export type { ValidationIssue, ValidationResult, ValidatorOptions } from './schema-validator';
|
|
18
|
+
export { ValidationSeverity } from './schema-validator';
|
|
19
|
+
export { DoctypeMeta, Registry, Stonecrop, useStonecrop, HST, createHST, HSTNode, getGlobalTriggerEngine, registerGlobalAction, registerTransitionAction, setFieldRollback, triggerTransition, markOperationIrreversible, SchemaValidator, createValidator, validateSchema, useOperationLog, useOperationLogStore, useUndoRedoShortcuts, withBatch, };
|
|
17
20
|
export default plugin;
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,wBAAwB,CAAA;AAC3C,mBAAmB,yBAAyB,CAAA;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAC9F,OAAO,WAAW,MAAM,WAAW,CAAA;AACnC,OAAO,EACN,sBAAsB,EACtB,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,MAAM,MAAM,WAAW,CAAA;AAC9B,OAAO,QAAQ,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,wBAAwB,CAAA;AAC3C,mBAAmB,yBAAyB,CAAA;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAC9F,OAAO,WAAW,MAAM,WAAW,CAAA;AACnC,OAAO,EACN,sBAAsB,EACtB,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,MAAM,MAAM,WAAW,CAAA;AAC9B,OAAO,QAAQ,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAE7D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACrF,mBAAmB,SAAS,CAAA;AAC5B,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC3G,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,YAAY,EACX,kBAAkB,EAClB,uBAAuB,EACvB,2BAA2B,EAC3B,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,GACxB,MAAM,wBAAwB,CAAA;AAE/B,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAC7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAEvD,OAAO,EACN,WAAW,EACX,QAAQ,EACR,SAAS,EACT,YAAY,EAEZ,GAAG,EACH,SAAS,EACT,OAAO,EAEP,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EAEzB,eAAe,EACf,eAAe,EACf,cAAc,EAEd,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,SAAS,GACT,CAAA;AAGD,eAAe,MAAM,CAAA"}
|
package/dist/src/index.js
CHANGED
|
@@ -7,11 +7,16 @@ import Registry from './registry';
|
|
|
7
7
|
import { Stonecrop } from './stonecrop';
|
|
8
8
|
import { HST, createHST } from './stores/hst';
|
|
9
9
|
import { useOperationLogStore } from './stores/operation-log';
|
|
10
|
+
// Export schema validator
|
|
11
|
+
import { SchemaValidator, createValidator, validateSchema } from './schema-validator';
|
|
12
|
+
export { ValidationSeverity } from './schema-validator';
|
|
10
13
|
export { DoctypeMeta, Registry, Stonecrop, useStonecrop,
|
|
11
14
|
// HST exports for advanced usage
|
|
12
15
|
HST, createHST,
|
|
13
16
|
// Field trigger system exports
|
|
14
17
|
getGlobalTriggerEngine, registerGlobalAction, registerTransitionAction, setFieldRollback, triggerTransition, markOperationIrreversible,
|
|
18
|
+
// Schema validator exports
|
|
19
|
+
SchemaValidator, createValidator, validateSchema,
|
|
15
20
|
// Operation log exports
|
|
16
21
|
useOperationLog, useOperationLogStore, useUndoRedoShortcuts, withBatch, };
|
|
17
22
|
// Default export is the Vue plugin
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Validation Utilities
|
|
3
|
+
* Validates Stonecrop schemas for integrity and consistency
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
import type { SchemaTypes } from '@stonecrop/aform';
|
|
7
|
+
import type { List, Map as ImmutableMap } from 'immutable';
|
|
8
|
+
import type { AnyStateNodeConfig } from 'xstate';
|
|
9
|
+
import type Registry from './registry';
|
|
10
|
+
/**
|
|
11
|
+
* Validation severity levels
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export declare enum ValidationSeverity {
|
|
15
|
+
/** Blocking error that prevents save */
|
|
16
|
+
ERROR = "error",
|
|
17
|
+
/** Advisory warning that allows save */
|
|
18
|
+
WARNING = "warning",
|
|
19
|
+
/** Informational message */
|
|
20
|
+
INFO = "info"
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validation issue
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export interface ValidationIssue {
|
|
27
|
+
/** Severity level */
|
|
28
|
+
severity: ValidationSeverity;
|
|
29
|
+
/** Validation rule that failed */
|
|
30
|
+
rule: string;
|
|
31
|
+
/** Human-readable message */
|
|
32
|
+
message: string;
|
|
33
|
+
/** Doctype name */
|
|
34
|
+
doctype?: string;
|
|
35
|
+
/** Field name if applicable */
|
|
36
|
+
fieldname?: string;
|
|
37
|
+
/** Additional context */
|
|
38
|
+
context?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Validation result
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
export interface ValidationResult {
|
|
45
|
+
/** Whether validation passed (no blocking errors) */
|
|
46
|
+
valid: boolean;
|
|
47
|
+
/** List of validation issues */
|
|
48
|
+
issues: ValidationIssue[];
|
|
49
|
+
/** Count of errors */
|
|
50
|
+
errorCount: number;
|
|
51
|
+
/** Count of warnings */
|
|
52
|
+
warningCount: number;
|
|
53
|
+
/** Count of info messages */
|
|
54
|
+
infoCount: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Schema validator options
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
export interface ValidatorOptions {
|
|
61
|
+
/** Registry instance for doctype lookups */
|
|
62
|
+
registry?: Registry;
|
|
63
|
+
/** Whether to validate Link field targets */
|
|
64
|
+
validateLinkTargets?: boolean;
|
|
65
|
+
/** Whether to validate workflow reachability */
|
|
66
|
+
validateWorkflows?: boolean;
|
|
67
|
+
/** Whether to validate action registration */
|
|
68
|
+
validateActions?: boolean;
|
|
69
|
+
/** Whether to validate required schema properties */
|
|
70
|
+
validateRequiredProperties?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Schema validator class
|
|
74
|
+
* @public
|
|
75
|
+
*/
|
|
76
|
+
export declare class SchemaValidator {
|
|
77
|
+
private options;
|
|
78
|
+
/**
|
|
79
|
+
* Creates a new SchemaValidator instance
|
|
80
|
+
* @param options - Validator configuration options
|
|
81
|
+
*/
|
|
82
|
+
constructor(options?: ValidatorOptions);
|
|
83
|
+
/**
|
|
84
|
+
* Validates a complete doctype schema
|
|
85
|
+
* @param doctype - Doctype name
|
|
86
|
+
* @param schema - Schema fields (List or Array)
|
|
87
|
+
* @param workflow - Optional workflow configuration
|
|
88
|
+
* @param actions - Optional actions map
|
|
89
|
+
* @returns Validation result
|
|
90
|
+
*/
|
|
91
|
+
validate(doctype: string, schema: List<SchemaTypes> | SchemaTypes[] | undefined, workflow?: AnyStateNodeConfig, actions?: ImmutableMap<string, string[]> | Map<string, string[]>): ValidationResult;
|
|
92
|
+
/**
|
|
93
|
+
* Validates that required schema properties are present
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
private validateRequiredProperties;
|
|
97
|
+
/**
|
|
98
|
+
* Validates Link field targets exist in registry
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
private validateLinkFields;
|
|
102
|
+
/**
|
|
103
|
+
* Validates workflow state machine configuration
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
private validateWorkflow;
|
|
107
|
+
/**
|
|
108
|
+
* Validates that actions are registered in the FieldTriggerEngine
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
private validateActionRegistration;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Creates a validator with the given registry
|
|
115
|
+
* @param registry - Registry instance
|
|
116
|
+
* @param options - Additional validator options
|
|
117
|
+
* @returns SchemaValidator instance
|
|
118
|
+
* @public
|
|
119
|
+
*/
|
|
120
|
+
export declare function createValidator(registry: Registry, options?: Partial<ValidatorOptions>): SchemaValidator;
|
|
121
|
+
/**
|
|
122
|
+
* Quick validation helper
|
|
123
|
+
* @param doctype - Doctype name
|
|
124
|
+
* @param schema - Schema fields
|
|
125
|
+
* @param registry - Registry instance
|
|
126
|
+
* @param workflow - Optional workflow configuration
|
|
127
|
+
* @param actions - Optional actions map
|
|
128
|
+
* @returns Validation result
|
|
129
|
+
* @public
|
|
130
|
+
*/
|
|
131
|
+
export declare function validateSchema(doctype: string, schema: List<SchemaTypes> | SchemaTypes[] | undefined, registry: Registry, workflow?: AnyStateNodeConfig, actions?: ImmutableMap<string, string[]> | Map<string, string[]>): ValidationResult;
|
|
132
|
+
//# sourceMappingURL=schema-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../../src/schema-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Validation Utilities
|
|
3
|
+
* Validates Stonecrop schemas for integrity and consistency
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
import { getGlobalTriggerEngine } from './field-triggers';
|
|
7
|
+
/**
|
|
8
|
+
* Validation severity levels
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export var ValidationSeverity;
|
|
12
|
+
(function (ValidationSeverity) {
|
|
13
|
+
/** Blocking error that prevents save */
|
|
14
|
+
ValidationSeverity["ERROR"] = "error";
|
|
15
|
+
/** Advisory warning that allows save */
|
|
16
|
+
ValidationSeverity["WARNING"] = "warning";
|
|
17
|
+
/** Informational message */
|
|
18
|
+
ValidationSeverity["INFO"] = "info";
|
|
19
|
+
})(ValidationSeverity || (ValidationSeverity = {}));
|
|
20
|
+
/**
|
|
21
|
+
* Schema validator class
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export class SchemaValidator {
|
|
25
|
+
options;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new SchemaValidator instance
|
|
28
|
+
* @param options - Validator configuration options
|
|
29
|
+
*/
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.options = {
|
|
32
|
+
registry: options.registry || null,
|
|
33
|
+
validateLinkTargets: options.validateLinkTargets ?? true,
|
|
34
|
+
validateActions: options.validateActions ?? true,
|
|
35
|
+
validateWorkflows: options.validateWorkflows ?? true,
|
|
36
|
+
validateRequiredProperties: options.validateRequiredProperties ?? true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validates a complete doctype schema
|
|
41
|
+
* @param doctype - Doctype name
|
|
42
|
+
* @param schema - Schema fields (List or Array)
|
|
43
|
+
* @param workflow - Optional workflow configuration
|
|
44
|
+
* @param actions - Optional actions map
|
|
45
|
+
* @returns Validation result
|
|
46
|
+
*/
|
|
47
|
+
validate(doctype, schema, workflow, actions) {
|
|
48
|
+
const issues = [];
|
|
49
|
+
// Convert schema to array for easier iteration
|
|
50
|
+
const schemaArray = schema ? (Array.isArray(schema) ? schema : schema.toArray()) : [];
|
|
51
|
+
// Validate required properties
|
|
52
|
+
if (this.options.validateRequiredProperties) {
|
|
53
|
+
issues.push(...this.validateRequiredProperties(doctype, schemaArray));
|
|
54
|
+
}
|
|
55
|
+
// Validate Link field targets
|
|
56
|
+
if (this.options.validateLinkTargets && this.options.registry) {
|
|
57
|
+
issues.push(...this.validateLinkFields(doctype, schemaArray, this.options.registry));
|
|
58
|
+
}
|
|
59
|
+
// Validate workflow configuration
|
|
60
|
+
if (this.options.validateWorkflows && workflow) {
|
|
61
|
+
issues.push(...this.validateWorkflow(doctype, workflow));
|
|
62
|
+
}
|
|
63
|
+
// Validate action registration
|
|
64
|
+
if (this.options.validateActions && actions) {
|
|
65
|
+
const actionsMap = actions instanceof Map ? actions : actions.toObject();
|
|
66
|
+
issues.push(...this.validateActionRegistration(doctype, actionsMap));
|
|
67
|
+
}
|
|
68
|
+
// Calculate counts
|
|
69
|
+
const errorCount = issues.filter(i => i.severity === ValidationSeverity.ERROR).length;
|
|
70
|
+
const warningCount = issues.filter(i => i.severity === ValidationSeverity.WARNING).length;
|
|
71
|
+
const infoCount = issues.filter(i => i.severity === ValidationSeverity.INFO).length;
|
|
72
|
+
return {
|
|
73
|
+
valid: errorCount === 0,
|
|
74
|
+
issues,
|
|
75
|
+
errorCount,
|
|
76
|
+
warningCount,
|
|
77
|
+
infoCount,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Validates that required schema properties are present
|
|
82
|
+
* @internal
|
|
83
|
+
*/
|
|
84
|
+
validateRequiredProperties(doctype, schema) {
|
|
85
|
+
const issues = [];
|
|
86
|
+
for (const field of schema) {
|
|
87
|
+
// Check for fieldname
|
|
88
|
+
if (!field.fieldname) {
|
|
89
|
+
issues.push({
|
|
90
|
+
severity: ValidationSeverity.ERROR,
|
|
91
|
+
rule: 'required-fieldname',
|
|
92
|
+
message: 'Field is missing required property: fieldname',
|
|
93
|
+
doctype,
|
|
94
|
+
context: { field },
|
|
95
|
+
});
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
// Check for component or fieldtype
|
|
99
|
+
if (!field.component && !('fieldtype' in field)) {
|
|
100
|
+
issues.push({
|
|
101
|
+
severity: ValidationSeverity.ERROR,
|
|
102
|
+
rule: 'required-component-or-fieldtype',
|
|
103
|
+
message: `Field "${field.fieldname}" must have either component or fieldtype property`,
|
|
104
|
+
doctype,
|
|
105
|
+
fieldname: field.fieldname,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Validate nested schemas (recursively)
|
|
109
|
+
if ('schema' in field) {
|
|
110
|
+
const nestedSchema = field.schema;
|
|
111
|
+
const nestedArray = (Array.isArray(nestedSchema) ? nestedSchema : nestedSchema.toArray?.() || []);
|
|
112
|
+
issues.push(...this.validateRequiredProperties(doctype, nestedArray));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return issues;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Validates Link field targets exist in registry
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
validateLinkFields(doctype, schema, registry) {
|
|
122
|
+
const issues = [];
|
|
123
|
+
for (const field of schema) {
|
|
124
|
+
const fieldtype = 'fieldtype' in field ? field.fieldtype : undefined;
|
|
125
|
+
// Check Link fields
|
|
126
|
+
if (fieldtype === 'Link') {
|
|
127
|
+
const options = 'options' in field ? field.options : undefined;
|
|
128
|
+
if (!options) {
|
|
129
|
+
issues.push({
|
|
130
|
+
severity: ValidationSeverity.ERROR,
|
|
131
|
+
rule: 'link-missing-options',
|
|
132
|
+
message: `Link field "${field.fieldname}" is missing options property (target doctype)`,
|
|
133
|
+
doctype,
|
|
134
|
+
fieldname: field.fieldname,
|
|
135
|
+
});
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// Check if target doctype exists in registry
|
|
139
|
+
// Options should be a string representing the target doctype name
|
|
140
|
+
const targetDoctype = typeof options === 'string' ? options : '';
|
|
141
|
+
if (!targetDoctype) {
|
|
142
|
+
issues.push({
|
|
143
|
+
severity: ValidationSeverity.ERROR,
|
|
144
|
+
rule: 'link-invalid-options',
|
|
145
|
+
message: `Link field "${field.fieldname}" has invalid options format (expected string doctype name)`,
|
|
146
|
+
doctype,
|
|
147
|
+
fieldname: field.fieldname,
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const targetMeta = registry.registry[targetDoctype] || registry.registry[targetDoctype.toLowerCase()];
|
|
152
|
+
if (!targetMeta) {
|
|
153
|
+
issues.push({
|
|
154
|
+
severity: ValidationSeverity.ERROR,
|
|
155
|
+
rule: 'link-invalid-target',
|
|
156
|
+
message: `Link field "${field.fieldname}" references non-existent doctype: "${targetDoctype}"`,
|
|
157
|
+
doctype,
|
|
158
|
+
fieldname: field.fieldname,
|
|
159
|
+
context: { targetDoctype },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Recursively check nested schemas
|
|
164
|
+
if ('schema' in field) {
|
|
165
|
+
const nestedSchema = field.schema;
|
|
166
|
+
const nestedArray = (Array.isArray(nestedSchema) ? nestedSchema : nestedSchema.toArray?.() || []);
|
|
167
|
+
issues.push(...this.validateLinkFields(doctype, nestedArray, registry));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return issues;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Validates workflow state machine configuration
|
|
174
|
+
* @internal
|
|
175
|
+
*/
|
|
176
|
+
validateWorkflow(doctype, workflow) {
|
|
177
|
+
const issues = [];
|
|
178
|
+
// Check for initial state
|
|
179
|
+
if (!workflow.initial && !workflow.type) {
|
|
180
|
+
issues.push({
|
|
181
|
+
severity: ValidationSeverity.WARNING,
|
|
182
|
+
rule: 'workflow-missing-initial',
|
|
183
|
+
message: 'Workflow is missing initial state property',
|
|
184
|
+
doctype,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// Check for states
|
|
188
|
+
if (!workflow.states || Object.keys(workflow.states).length === 0) {
|
|
189
|
+
issues.push({
|
|
190
|
+
severity: ValidationSeverity.WARNING,
|
|
191
|
+
rule: 'workflow-no-states',
|
|
192
|
+
message: 'Workflow has no states defined',
|
|
193
|
+
doctype,
|
|
194
|
+
});
|
|
195
|
+
return issues;
|
|
196
|
+
}
|
|
197
|
+
// Validate initial state exists
|
|
198
|
+
if (workflow.initial && typeof workflow.initial === 'string' && !workflow.states[workflow.initial]) {
|
|
199
|
+
issues.push({
|
|
200
|
+
severity: ValidationSeverity.ERROR,
|
|
201
|
+
rule: 'workflow-invalid-initial',
|
|
202
|
+
message: `Workflow initial state "${workflow.initial}" does not exist in states`,
|
|
203
|
+
doctype,
|
|
204
|
+
context: { initialState: workflow.initial },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// Check state reachability (simple check - all states should have at least one incoming transition or be initial)
|
|
208
|
+
const stateNames = Object.keys(workflow.states);
|
|
209
|
+
const reachableStates = new Set();
|
|
210
|
+
// Initial state is always reachable
|
|
211
|
+
if (workflow.initial && typeof workflow.initial === 'string') {
|
|
212
|
+
reachableStates.add(workflow.initial);
|
|
213
|
+
}
|
|
214
|
+
// Find all target states from transitions
|
|
215
|
+
for (const [_stateName, stateConfig] of Object.entries(workflow.states)) {
|
|
216
|
+
const state = stateConfig;
|
|
217
|
+
if (state.on) {
|
|
218
|
+
for (const [_event, transition] of Object.entries(state.on)) {
|
|
219
|
+
if (typeof transition === 'string') {
|
|
220
|
+
reachableStates.add(transition);
|
|
221
|
+
}
|
|
222
|
+
else if (transition && typeof transition === 'object') {
|
|
223
|
+
const target = 'target' in transition ? transition.target : undefined;
|
|
224
|
+
if (typeof target === 'string') {
|
|
225
|
+
reachableStates.add(target);
|
|
226
|
+
}
|
|
227
|
+
else if (Array.isArray(target)) {
|
|
228
|
+
target.forEach((t) => {
|
|
229
|
+
if (typeof t === 'string') {
|
|
230
|
+
reachableStates.add(t);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Check for unreachable states
|
|
239
|
+
for (const stateName of stateNames) {
|
|
240
|
+
if (!reachableStates.has(stateName)) {
|
|
241
|
+
issues.push({
|
|
242
|
+
severity: ValidationSeverity.WARNING,
|
|
243
|
+
rule: 'workflow-unreachable-state',
|
|
244
|
+
message: `Workflow state "${stateName}" may not be reachable`,
|
|
245
|
+
doctype,
|
|
246
|
+
context: { stateName },
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return issues;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Validates that actions are registered in the FieldTriggerEngine
|
|
254
|
+
* @internal
|
|
255
|
+
*/
|
|
256
|
+
validateActionRegistration(doctype, actions) {
|
|
257
|
+
const issues = [];
|
|
258
|
+
const triggerEngine = getGlobalTriggerEngine();
|
|
259
|
+
for (const [triggerName, actionNames] of Object.entries(actions)) {
|
|
260
|
+
if (!Array.isArray(actionNames)) {
|
|
261
|
+
issues.push({
|
|
262
|
+
severity: ValidationSeverity.ERROR,
|
|
263
|
+
rule: 'action-invalid-format',
|
|
264
|
+
message: `Action configuration for "${triggerName}" must be an array`,
|
|
265
|
+
doctype,
|
|
266
|
+
context: { triggerName, actionNames },
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
// Check each action name
|
|
271
|
+
for (const actionName of actionNames) {
|
|
272
|
+
// Check if action is registered globally
|
|
273
|
+
const engine = triggerEngine;
|
|
274
|
+
const isRegistered = engine.globalActions?.has(actionName) || engine.globalTransitionActions?.has(actionName);
|
|
275
|
+
if (!isRegistered) {
|
|
276
|
+
issues.push({
|
|
277
|
+
severity: ValidationSeverity.WARNING,
|
|
278
|
+
rule: 'action-not-registered',
|
|
279
|
+
message: `Action "${actionName}" referenced in "${triggerName}" is not registered in FieldTriggerEngine`,
|
|
280
|
+
doctype,
|
|
281
|
+
context: { triggerName, actionName },
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return issues;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Creates a validator with the given registry
|
|
291
|
+
* @param registry - Registry instance
|
|
292
|
+
* @param options - Additional validator options
|
|
293
|
+
* @returns SchemaValidator instance
|
|
294
|
+
* @public
|
|
295
|
+
*/
|
|
296
|
+
export function createValidator(registry, options) {
|
|
297
|
+
return new SchemaValidator({
|
|
298
|
+
registry,
|
|
299
|
+
...options,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Quick validation helper
|
|
304
|
+
* @param doctype - Doctype name
|
|
305
|
+
* @param schema - Schema fields
|
|
306
|
+
* @param registry - Registry instance
|
|
307
|
+
* @param workflow - Optional workflow configuration
|
|
308
|
+
* @param actions - Optional actions map
|
|
309
|
+
* @returns Validation result
|
|
310
|
+
* @public
|
|
311
|
+
*/
|
|
312
|
+
export function validateSchema(doctype, schema, registry, workflow, actions) {
|
|
313
|
+
const validator = createValidator(registry);
|
|
314
|
+
return validator.validate(doctype, schema, workflow, actions);
|
|
315
|
+
}
|