@plures/praxis 1.1.0 → 1.1.2
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/LICENSE +21 -21
- package/README.md +4 -4
- package/core/codegen/ts-generator.ts +15 -15
- package/dist/node/cli/index.cjs +3 -2282
- package/dist/node/cli/index.js +1 -1
- package/dist/node/{verify-YBZ7W24H.js → verify-QRYKRIDU.js} +3 -2282
- package/docs/REACTIVE_REDESIGN.md +132 -132
- package/docs/SVELTE_INTEGRATION_STRATEGY.md +68 -68
- package/package.json +132 -132
- package/src/components/TerminalNode.svelte +457 -457
- package/src/core/reactive-engine.svelte.ts +65 -65
- package/src/core/reactive-engine.ts +58 -67
- package/src/core/schema/loader.common.ts +150 -150
- package/src/examples/advanced-todo/App.svelte +506 -506
- package/src/index.browser.ts +204 -204
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Praxis Reactive Logic Engine
|
|
3
|
-
*
|
|
4
|
-
* A Svelte 5 native implementation of the Praxis Logic Engine.
|
|
5
|
-
* Uses Runes ($state, $derived, $effect) for fine-grained reactivity.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface ReactiveEngineOptions<TContext> {
|
|
9
|
-
initialContext: TContext;
|
|
10
|
-
initialFacts?: any[];
|
|
11
|
-
initialMeta?: Record<string, unknown>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class ReactiveLogicEngine<TContext extends object> {
|
|
15
|
-
// The single source of truth, reactive by default
|
|
16
|
-
// We use $state.raw for things that shouldn't be deeply reactive if needed,
|
|
17
|
-
// but for context we usually want deep reactivity.
|
|
18
|
-
state = $state<{
|
|
19
|
-
context: TContext;
|
|
20
|
-
facts: any[];
|
|
21
|
-
meta: Record<string, unknown>;
|
|
22
|
-
}>({
|
|
23
|
-
context: {} as TContext,
|
|
24
|
-
facts: [],
|
|
25
|
-
meta: {}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
constructor(options: ReactiveEngineOptions<TContext>) {
|
|
29
|
-
this.state.context = options.initialContext;
|
|
30
|
-
this.state.facts = options.initialFacts ?? [];
|
|
31
|
-
this.state.meta = options.initialMeta ?? {};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Access the reactive context directly.
|
|
36
|
-
* Consumers can use this in $derived() or $effect().
|
|
37
|
-
*/
|
|
38
|
-
get context() {
|
|
39
|
-
return this.state.context;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Access the reactive facts list.
|
|
44
|
-
*/
|
|
45
|
-
get facts() {
|
|
46
|
-
return this.state.facts;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Apply a mutation to the state.
|
|
51
|
-
* This is the "Action" or "Rule" equivalent.
|
|
52
|
-
*
|
|
53
|
-
* @param mutator A function that receives the state and modifies it.
|
|
54
|
-
*/
|
|
55
|
-
apply(mutator: (state: { context: TContext; facts: any[]; meta: Record<string, unknown> }) => void) {
|
|
56
|
-
mutator(this.state);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Access the reactive meta.
|
|
61
|
-
*/
|
|
62
|
-
get meta() {
|
|
63
|
-
return this.state.meta;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Praxis Reactive Logic Engine
|
|
3
|
+
*
|
|
4
|
+
* A Svelte 5 native implementation of the Praxis Logic Engine.
|
|
5
|
+
* Uses Runes ($state, $derived, $effect) for fine-grained reactivity.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface ReactiveEngineOptions<TContext> {
|
|
9
|
+
initialContext: TContext;
|
|
10
|
+
initialFacts?: any[];
|
|
11
|
+
initialMeta?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ReactiveLogicEngine<TContext extends object> {
|
|
15
|
+
// The single source of truth, reactive by default
|
|
16
|
+
// We use $state.raw for things that shouldn't be deeply reactive if needed,
|
|
17
|
+
// but for context we usually want deep reactivity.
|
|
18
|
+
state: { context: TContext; facts: any[]; meta: Record<string, unknown> } = $state<{
|
|
19
|
+
context: TContext;
|
|
20
|
+
facts: any[];
|
|
21
|
+
meta: Record<string, unknown>;
|
|
22
|
+
}>({
|
|
23
|
+
context: {} as TContext,
|
|
24
|
+
facts: [],
|
|
25
|
+
meta: {}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
constructor(options: ReactiveEngineOptions<TContext>) {
|
|
29
|
+
this.state.context = options.initialContext;
|
|
30
|
+
this.state.facts = options.initialFacts ?? [];
|
|
31
|
+
this.state.meta = options.initialMeta ?? {};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Access the reactive context directly.
|
|
36
|
+
* Consumers can use this in $derived() or $effect().
|
|
37
|
+
*/
|
|
38
|
+
get context(): TContext {
|
|
39
|
+
return this.state.context;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Access the reactive facts list.
|
|
44
|
+
*/
|
|
45
|
+
get facts(): any[] {
|
|
46
|
+
return this.state.facts;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Apply a mutation to the state.
|
|
51
|
+
* This is the "Action" or "Rule" equivalent.
|
|
52
|
+
*
|
|
53
|
+
* @param mutator A function that receives the state and modifies it.
|
|
54
|
+
*/
|
|
55
|
+
apply(mutator: (state: { context: TContext; facts: any[]; meta: Record<string, unknown> }) => void): void {
|
|
56
|
+
mutator(this.state);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Access the reactive meta.
|
|
61
|
+
*/
|
|
62
|
+
get meta(): Record<string, unknown> {
|
|
63
|
+
return this.state.meta;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -1,67 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Praxis Reactive Logic Engine
|
|
3
|
-
*
|
|
4
|
-
* A
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.state.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Access the reactive meta.
|
|
63
|
-
*/
|
|
64
|
-
get meta() {
|
|
65
|
-
return this.state.meta;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Praxis Reactive Logic Engine
|
|
3
|
+
*
|
|
4
|
+
* A minimal TypeScript implementation of the Praxis Logic Engine.
|
|
5
|
+
* This variant avoids Svelte-specific reactivity primitives to remain framework-agnostic.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface ReactiveEngineOptions<TContext> {
|
|
9
|
+
initialContext: TContext;
|
|
10
|
+
initialFacts?: any[];
|
|
11
|
+
initialMeta?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ReactiveLogicEngine<TContext extends object> {
|
|
15
|
+
state: { context: TContext; facts: any[]; meta: Record<string, unknown> } = {
|
|
16
|
+
context: {} as TContext,
|
|
17
|
+
facts: [],
|
|
18
|
+
meta: {}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
constructor(options: ReactiveEngineOptions<TContext>) {
|
|
22
|
+
this.state.context = options.initialContext;
|
|
23
|
+
this.state.facts = options.initialFacts ?? [];
|
|
24
|
+
this.state.meta = options.initialMeta ?? {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Access the context directly.
|
|
29
|
+
* Framework-specific wrappers (e.g., Svelte runes) can build on top of this value.
|
|
30
|
+
*/
|
|
31
|
+
get context() {
|
|
32
|
+
return this.state.context;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Access the facts list.
|
|
37
|
+
*/
|
|
38
|
+
get facts() {
|
|
39
|
+
return this.state.facts;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Apply a mutation to the state.
|
|
44
|
+
* This is the "Action" or "Rule" equivalent.
|
|
45
|
+
*
|
|
46
|
+
* @param mutator A function that receives the state and modifies it.
|
|
47
|
+
*/
|
|
48
|
+
apply(mutator: (state: { context: TContext; facts: any[]; meta: Record<string, unknown> }) => void) {
|
|
49
|
+
mutator(this.state);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Access the metadata.
|
|
54
|
+
*/
|
|
55
|
+
get meta() {
|
|
56
|
+
return this.state.meta;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Praxis Schema Loader (Common/Browser Compatible)
|
|
3
|
-
*
|
|
4
|
-
* Core schema loading and validation functions that don't depend on Node.js APIs.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { load as yamlLoad } from 'js-yaml';
|
|
8
|
-
import type { PraxisSchema, ValidationResult } from './types.js';
|
|
9
|
-
import { validateSchema } from './types.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Loader options
|
|
13
|
-
*/
|
|
14
|
-
export interface LoaderOptions {
|
|
15
|
-
/** Validate schema after loading */
|
|
16
|
-
validate?: boolean;
|
|
17
|
-
/** Base directory for resolving relative paths */
|
|
18
|
-
baseDir?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Loader result
|
|
23
|
-
*/
|
|
24
|
-
export interface LoaderResult {
|
|
25
|
-
/** Loaded schema */
|
|
26
|
-
schema?: PraxisSchema;
|
|
27
|
-
/** Validation result */
|
|
28
|
-
validation?: ValidationResult;
|
|
29
|
-
/** Load errors */
|
|
30
|
-
errors: string[];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Create a new empty schema
|
|
35
|
-
*/
|
|
36
|
-
export function createSchema(name: string): PraxisSchema {
|
|
37
|
-
return {
|
|
38
|
-
version: '1.0.0',
|
|
39
|
-
name,
|
|
40
|
-
description: `Schema for ${name}`,
|
|
41
|
-
models: [],
|
|
42
|
-
components: [],
|
|
43
|
-
logic: [],
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Load schema from JSON string
|
|
49
|
-
*/
|
|
50
|
-
export function loadSchemaFromJson(json: string, options: LoaderOptions = {}): LoaderResult {
|
|
51
|
-
const errors: string[] = [];
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const schema = JSON.parse(json) as PraxisSchema;
|
|
55
|
-
|
|
56
|
-
// Validate if requested
|
|
57
|
-
let validation: ValidationResult | undefined;
|
|
58
|
-
if (options.validate !== false) {
|
|
59
|
-
validation = validateSchema(schema);
|
|
60
|
-
if (!validation.valid) {
|
|
61
|
-
errors.push('Schema validation failed:');
|
|
62
|
-
validation.errors.forEach((error) => {
|
|
63
|
-
errors.push(` ${error.path}: ${error.message}`);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
schema,
|
|
70
|
-
validation,
|
|
71
|
-
errors,
|
|
72
|
-
};
|
|
73
|
-
} catch (error) {
|
|
74
|
-
if (error instanceof Error) {
|
|
75
|
-
errors.push(`Failed to parse JSON: ${error.message}`);
|
|
76
|
-
} else {
|
|
77
|
-
errors.push('Failed to parse JSON: Unknown error');
|
|
78
|
-
}
|
|
79
|
-
return { errors };
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Load schema from YAML string
|
|
85
|
-
*/
|
|
86
|
-
export function loadSchemaFromYaml(yaml: string, options: LoaderOptions = {}): LoaderResult {
|
|
87
|
-
const errors: string[] = [];
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const schema = yamlLoad(yaml) as PraxisSchema;
|
|
91
|
-
|
|
92
|
-
// Validate if requested
|
|
93
|
-
let validation: ValidationResult | undefined;
|
|
94
|
-
if (options.validate !== false) {
|
|
95
|
-
validation = validateSchema(schema);
|
|
96
|
-
if (!validation.valid) {
|
|
97
|
-
errors.push('Schema validation failed:');
|
|
98
|
-
validation.errors.forEach((error) => {
|
|
99
|
-
errors.push(` ${error.path}: ${error.message}`);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
schema,
|
|
106
|
-
validation,
|
|
107
|
-
errors,
|
|
108
|
-
};
|
|
109
|
-
} catch (error) {
|
|
110
|
-
if (error instanceof Error) {
|
|
111
|
-
errors.push(`Failed to parse YAML: ${error.message}`);
|
|
112
|
-
} else {
|
|
113
|
-
errors.push('Failed to parse YAML: Unknown error');
|
|
114
|
-
}
|
|
115
|
-
return { errors };
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Validate that a loaded schema has required fields for generation
|
|
121
|
-
*/
|
|
122
|
-
export function validateForGeneration(schema: PraxisSchema): ValidationResult {
|
|
123
|
-
const errors: string[] = [];
|
|
124
|
-
|
|
125
|
-
// Check for entities/models
|
|
126
|
-
if (!schema.models || schema.models.length === 0) {
|
|
127
|
-
errors.push('Schema must define at least one model for generation');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Check that models have valid fields
|
|
131
|
-
schema.models?.forEach((model, index) => {
|
|
132
|
-
if (!model.fields || model.fields.length === 0) {
|
|
133
|
-
errors.push(`Model "${model.name}" at index ${index} must have at least one field`);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
model.fields?.forEach((field, fieldIndex) => {
|
|
137
|
-
if (!field.name) {
|
|
138
|
-
errors.push(`Field at index ${fieldIndex} in model "${model.name}" must have a name`);
|
|
139
|
-
}
|
|
140
|
-
if (!field.type) {
|
|
141
|
-
errors.push(`Field "${field.name}" in model "${model.name}" must have a type`);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
valid: errors.length === 0,
|
|
148
|
-
errors: errors.map((message) => ({ path: 'schema', message })),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Praxis Schema Loader (Common/Browser Compatible)
|
|
3
|
+
*
|
|
4
|
+
* Core schema loading and validation functions that don't depend on Node.js APIs.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { load as yamlLoad } from 'js-yaml';
|
|
8
|
+
import type { PraxisSchema, ValidationResult } from './types.js';
|
|
9
|
+
import { validateSchema } from './types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Loader options
|
|
13
|
+
*/
|
|
14
|
+
export interface LoaderOptions {
|
|
15
|
+
/** Validate schema after loading */
|
|
16
|
+
validate?: boolean;
|
|
17
|
+
/** Base directory for resolving relative paths */
|
|
18
|
+
baseDir?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Loader result
|
|
23
|
+
*/
|
|
24
|
+
export interface LoaderResult {
|
|
25
|
+
/** Loaded schema */
|
|
26
|
+
schema?: PraxisSchema;
|
|
27
|
+
/** Validation result */
|
|
28
|
+
validation?: ValidationResult;
|
|
29
|
+
/** Load errors */
|
|
30
|
+
errors: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a new empty schema
|
|
35
|
+
*/
|
|
36
|
+
export function createSchema(name: string): PraxisSchema {
|
|
37
|
+
return {
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
name,
|
|
40
|
+
description: `Schema for ${name}`,
|
|
41
|
+
models: [],
|
|
42
|
+
components: [],
|
|
43
|
+
logic: [],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Load schema from JSON string
|
|
49
|
+
*/
|
|
50
|
+
export function loadSchemaFromJson(json: string, options: LoaderOptions = {}): LoaderResult {
|
|
51
|
+
const errors: string[] = [];
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const schema = JSON.parse(json) as PraxisSchema;
|
|
55
|
+
|
|
56
|
+
// Validate if requested
|
|
57
|
+
let validation: ValidationResult | undefined;
|
|
58
|
+
if (options.validate !== false) {
|
|
59
|
+
validation = validateSchema(schema);
|
|
60
|
+
if (!validation.valid) {
|
|
61
|
+
errors.push('Schema validation failed:');
|
|
62
|
+
validation.errors.forEach((error) => {
|
|
63
|
+
errors.push(` ${error.path}: ${error.message}`);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
schema,
|
|
70
|
+
validation,
|
|
71
|
+
errors,
|
|
72
|
+
};
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error instanceof Error) {
|
|
75
|
+
errors.push(`Failed to parse JSON: ${error.message}`);
|
|
76
|
+
} else {
|
|
77
|
+
errors.push('Failed to parse JSON: Unknown error');
|
|
78
|
+
}
|
|
79
|
+
return { errors };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Load schema from YAML string
|
|
85
|
+
*/
|
|
86
|
+
export function loadSchemaFromYaml(yaml: string, options: LoaderOptions = {}): LoaderResult {
|
|
87
|
+
const errors: string[] = [];
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const schema = yamlLoad(yaml) as PraxisSchema;
|
|
91
|
+
|
|
92
|
+
// Validate if requested
|
|
93
|
+
let validation: ValidationResult | undefined;
|
|
94
|
+
if (options.validate !== false) {
|
|
95
|
+
validation = validateSchema(schema);
|
|
96
|
+
if (!validation.valid) {
|
|
97
|
+
errors.push('Schema validation failed:');
|
|
98
|
+
validation.errors.forEach((error) => {
|
|
99
|
+
errors.push(` ${error.path}: ${error.message}`);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
schema,
|
|
106
|
+
validation,
|
|
107
|
+
errors,
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (error instanceof Error) {
|
|
111
|
+
errors.push(`Failed to parse YAML: ${error.message}`);
|
|
112
|
+
} else {
|
|
113
|
+
errors.push('Failed to parse YAML: Unknown error');
|
|
114
|
+
}
|
|
115
|
+
return { errors };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Validate that a loaded schema has required fields for generation
|
|
121
|
+
*/
|
|
122
|
+
export function validateForGeneration(schema: PraxisSchema): ValidationResult {
|
|
123
|
+
const errors: string[] = [];
|
|
124
|
+
|
|
125
|
+
// Check for entities/models
|
|
126
|
+
if (!schema.models || schema.models.length === 0) {
|
|
127
|
+
errors.push('Schema must define at least one model for generation');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check that models have valid fields
|
|
131
|
+
schema.models?.forEach((model, index) => {
|
|
132
|
+
if (!model.fields || model.fields.length === 0) {
|
|
133
|
+
errors.push(`Model "${model.name}" at index ${index} must have at least one field`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
model.fields?.forEach((field, fieldIndex) => {
|
|
137
|
+
if (!field.name) {
|
|
138
|
+
errors.push(`Field at index ${fieldIndex} in model "${model.name}" must have a name`);
|
|
139
|
+
}
|
|
140
|
+
if (!field.type) {
|
|
141
|
+
errors.push(`Field "${field.name}" in model "${model.name}" must have a type`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
valid: errors.length === 0,
|
|
148
|
+
errors: errors.map((message) => ({ path: 'schema', message })),
|
|
149
|
+
};
|
|
150
|
+
}
|