@vector-nodes/core 0.1.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/LICENSE +201 -0
- package/README.md +20 -0
- package/dist/colors.d.ts +13 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +23 -0
- package/dist/colors.js.map +1 -0
- package/dist/conversions.d.ts +12 -0
- package/dist/conversions.d.ts.map +1 -0
- package/dist/conversions.js +45 -0
- package/dist/conversions.js.map +1 -0
- package/dist/graph.d.ts +104 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +66 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/node-definition.d.ts +55 -0
- package/dist/node-definition.d.ts.map +1 -0
- package/dist/node-definition.js +14 -0
- package/dist/node-definition.js.map +1 -0
- package/dist/nodes.d.ts +16 -0
- package/dist/nodes.d.ts.map +1 -0
- package/dist/nodes.js +207 -0
- package/dist/nodes.js.map +1 -0
- package/dist/registry.d.ts +28 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +50 -0
- package/dist/registry.js.map +1 -0
- package/dist/schema.d.ts +220 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +163 -0
- package/dist/schema.js.map +1 -0
- package/dist/socket-types.d.ts +25 -0
- package/dist/socket-types.d.ts.map +1 -0
- package/dist/socket-types.js +26 -0
- package/dist/socket-types.js.map +1 -0
- package/dist/validate-graph.d.ts +37 -0
- package/dist/validate-graph.d.ts.map +1 -0
- package/dist/validate-graph.js +201 -0
- package/dist/validate-graph.js.map +1 -0
- package/dist/vnodes.d.ts +37 -0
- package/dist/vnodes.d.ts.map +1 -0
- package/dist/vnodes.js +59 -0
- package/dist/vnodes.js.map +1 -0
- package/package.json +40 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `.vnodes` JSON Schema (draft 2020-12), embedded so the package is
|
|
3
|
+
* self-contained when published.
|
|
4
|
+
*
|
|
5
|
+
* This is a verbatim copy of the canonical `docs/vnodes.schema.json`. The two
|
|
6
|
+
* are kept in sync by `schema.test.ts`, which fails if they diverge.
|
|
7
|
+
*/
|
|
8
|
+
export const VNODES_SCHEMA = {
|
|
9
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
10
|
+
$id: 'https://vector-nodes.dev/schema/vnodes.schema.json',
|
|
11
|
+
title: 'Vector Nodes network (.vnodes)',
|
|
12
|
+
description: 'Skeleton schema for the Vector Nodes JSON network definition format. Refined during Phase 1.',
|
|
13
|
+
type: 'object',
|
|
14
|
+
required: ['format', 'version', 'nodes', 'links'],
|
|
15
|
+
additionalProperties: false,
|
|
16
|
+
properties: {
|
|
17
|
+
format: {
|
|
18
|
+
const: 'vector-nodes',
|
|
19
|
+
description: 'Format discriminator.',
|
|
20
|
+
},
|
|
21
|
+
version: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
pattern: '^\\d+\\.\\d+$',
|
|
24
|
+
description: 'Format version, e.g. "1.0".',
|
|
25
|
+
},
|
|
26
|
+
metadata: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
description: 'Human-facing info. `name` seeds the generated function name.',
|
|
29
|
+
properties: {
|
|
30
|
+
name: { type: 'string' },
|
|
31
|
+
author: { type: 'string' },
|
|
32
|
+
description: { type: 'string' },
|
|
33
|
+
created: { type: 'string' },
|
|
34
|
+
},
|
|
35
|
+
additionalProperties: true,
|
|
36
|
+
},
|
|
37
|
+
parameters: {
|
|
38
|
+
type: 'array',
|
|
39
|
+
description: "External, typed inputs exposed as the generated function's arguments.",
|
|
40
|
+
items: { $ref: '#/$defs/parameter' },
|
|
41
|
+
},
|
|
42
|
+
nodes: {
|
|
43
|
+
type: 'array',
|
|
44
|
+
description: 'The nodes in the network. Exactly one OutputGeometry node is expected.',
|
|
45
|
+
items: { $ref: '#/$defs/node' },
|
|
46
|
+
},
|
|
47
|
+
links: {
|
|
48
|
+
type: 'array',
|
|
49
|
+
description: 'Edges connecting an output socket to an input socket.',
|
|
50
|
+
items: { $ref: '#/$defs/link' },
|
|
51
|
+
},
|
|
52
|
+
metaNodes: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
description: 'Reusable meta-node (function) definitions, keyed by name.',
|
|
55
|
+
additionalProperties: { $ref: '#/$defs/metaNode' },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
$defs: {
|
|
59
|
+
socketType: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
enum: ['Float', 'Integer', 'Boolean', 'Vector', 'Color', 'String', 'Geometry', 'Matrix'],
|
|
62
|
+
description: 'Socket data type. Drives color-coding and link type-checking.',
|
|
63
|
+
},
|
|
64
|
+
parameter: {
|
|
65
|
+
type: 'object',
|
|
66
|
+
required: ['id', 'type'],
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
properties: {
|
|
69
|
+
id: { type: 'string' },
|
|
70
|
+
type: { $ref: '#/$defs/socketType' },
|
|
71
|
+
isArray: {
|
|
72
|
+
type: 'boolean',
|
|
73
|
+
default: false,
|
|
74
|
+
description: 'True if this carries a field/array of the type.',
|
|
75
|
+
},
|
|
76
|
+
default: {
|
|
77
|
+
description: 'Default value; shape depends on `type`.',
|
|
78
|
+
},
|
|
79
|
+
min: { type: 'number' },
|
|
80
|
+
max: { type: 'number' },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
node: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
required: ['id', 'type'],
|
|
86
|
+
additionalProperties: false,
|
|
87
|
+
properties: {
|
|
88
|
+
id: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Stable unique id used by links.',
|
|
91
|
+
},
|
|
92
|
+
type: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: 'Node type from the node library (e.g. "PointCircle", "OutputGeometry").',
|
|
95
|
+
},
|
|
96
|
+
position: {
|
|
97
|
+
type: 'array',
|
|
98
|
+
description: 'Editor canvas position [x, y].',
|
|
99
|
+
items: { type: 'number' },
|
|
100
|
+
minItems: 2,
|
|
101
|
+
maxItems: 2,
|
|
102
|
+
},
|
|
103
|
+
params: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
description: 'Static parameter values baked into the node.',
|
|
106
|
+
additionalProperties: true,
|
|
107
|
+
},
|
|
108
|
+
label: { type: 'string' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
endpoint: {
|
|
112
|
+
type: 'array',
|
|
113
|
+
description: 'A [nodeId, socketName] reference.',
|
|
114
|
+
items: { type: 'string' },
|
|
115
|
+
minItems: 2,
|
|
116
|
+
maxItems: 2,
|
|
117
|
+
},
|
|
118
|
+
link: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
required: ['from', 'to'],
|
|
121
|
+
additionalProperties: false,
|
|
122
|
+
properties: {
|
|
123
|
+
from: {
|
|
124
|
+
$ref: '#/$defs/endpoint',
|
|
125
|
+
description: 'Source output socket [nodeId, outputSocketName].',
|
|
126
|
+
},
|
|
127
|
+
to: {
|
|
128
|
+
$ref: '#/$defs/endpoint',
|
|
129
|
+
description: 'Target input socket [nodeId, inputSocketName].',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
metaNode: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
required: ['interface', 'nodes', 'links'],
|
|
136
|
+
additionalProperties: false,
|
|
137
|
+
properties: {
|
|
138
|
+
interface: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
required: ['inputs', 'outputs'],
|
|
141
|
+
additionalProperties: false,
|
|
142
|
+
properties: {
|
|
143
|
+
inputs: { type: 'array', items: { $ref: '#/$defs/interfaceSocket' } },
|
|
144
|
+
outputs: { type: 'array', items: { $ref: '#/$defs/interfaceSocket' } },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
nodes: { type: 'array', items: { $ref: '#/$defs/node' } },
|
|
148
|
+
links: { type: 'array', items: { $ref: '#/$defs/link' } },
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
interfaceSocket: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
required: ['name', 'type'],
|
|
154
|
+
additionalProperties: false,
|
|
155
|
+
properties: {
|
|
156
|
+
name: { type: 'string' },
|
|
157
|
+
type: { $ref: '#/$defs/socketType' },
|
|
158
|
+
isArray: { type: 'boolean', default: false },
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eight socket types of the Vector Nodes type system.
|
|
3
|
+
*
|
|
4
|
+
* A single internal `Vector` type represents both points and directions (2D is a
|
|
5
|
+
* `Vector` with `z = 0`); there is no separate `Point` type. See the README's
|
|
6
|
+
* "type system" section for the full rationale.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SOCKET_TYPES: readonly ["Float", "Integer", "Boolean", "Vector", "Color", "String", "Geometry", "Matrix"];
|
|
9
|
+
/** Union of every valid socket type. */
|
|
10
|
+
export type SocketType = (typeof SOCKET_TYPES)[number];
|
|
11
|
+
/** Narrow an arbitrary string to a {@link SocketType}. */
|
|
12
|
+
export declare function isSocketType(value: string): value is SocketType;
|
|
13
|
+
/**
|
|
14
|
+
* A socket's full type: its base {@link SocketType} plus whether it carries a
|
|
15
|
+
* single value or an array ("field" in Blender terms). Any socket may be a
|
|
16
|
+
* field, so this flag is orthogonal to the base type.
|
|
17
|
+
*/
|
|
18
|
+
export interface SocketTypeDescriptor {
|
|
19
|
+
readonly type: SocketType;
|
|
20
|
+
/** When `true`, the socket carries an array/field of `type` rather than one value. */
|
|
21
|
+
readonly isArray: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Convenience constructor for a {@link SocketTypeDescriptor}. */
|
|
24
|
+
export declare function socketTypeDescriptor(type: SocketType, isArray?: boolean): SocketTypeDescriptor;
|
|
25
|
+
//# sourceMappingURL=socket-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-types.d.ts","sourceRoot":"","sources":["../src/socket-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,6FASf,CAAC;AAEX,wCAAwC;AACxC,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,UAAQ,GAAG,oBAAoB,CAE5F"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eight socket types of the Vector Nodes type system.
|
|
3
|
+
*
|
|
4
|
+
* A single internal `Vector` type represents both points and directions (2D is a
|
|
5
|
+
* `Vector` with `z = 0`); there is no separate `Point` type. See the README's
|
|
6
|
+
* "type system" section for the full rationale.
|
|
7
|
+
*/
|
|
8
|
+
export const SOCKET_TYPES = [
|
|
9
|
+
'Float',
|
|
10
|
+
'Integer',
|
|
11
|
+
'Boolean',
|
|
12
|
+
'Vector',
|
|
13
|
+
'Color',
|
|
14
|
+
'String',
|
|
15
|
+
'Geometry',
|
|
16
|
+
'Matrix',
|
|
17
|
+
];
|
|
18
|
+
/** Narrow an arbitrary string to a {@link SocketType}. */
|
|
19
|
+
export function isSocketType(value) {
|
|
20
|
+
return SOCKET_TYPES.includes(value);
|
|
21
|
+
}
|
|
22
|
+
/** Convenience constructor for a {@link SocketTypeDescriptor}. */
|
|
23
|
+
export function socketTypeDescriptor(type, isArray = false) {
|
|
24
|
+
return { type, isArray };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=socket-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-types.js","sourceRoot":"","sources":["../src/socket-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,OAAO;IACP,SAAS;IACT,SAAS;IACT,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,UAAU;IACV,QAAQ;CACA,CAAC;AAKX,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAQ,YAAkC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC;AAaD,kEAAkE;AAClE,MAAM,UAAU,oBAAoB,CAAC,IAAgB,EAAE,OAAO,GAAG,KAAK;IACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Graph } from './graph';
|
|
2
|
+
import type { NodeRegistry } from './registry';
|
|
3
|
+
/** The kinds of static-validation problems a graph can have. */
|
|
4
|
+
export type GraphValidationCode = 'duplicate-node-id' | 'unknown-node-type' | 'missing-output' | 'multiple-outputs' | 'dangling-link-node' | 'dangling-link-socket' | 'duplicate-input-link' | 'type-mismatch' | 'field-mismatch' | 'cycle';
|
|
5
|
+
/** A single static-validation problem with enough context to locate it. */
|
|
6
|
+
export interface GraphValidationIssue {
|
|
7
|
+
code: GraphValidationCode;
|
|
8
|
+
message: string;
|
|
9
|
+
/** Relevant node id, when the issue concerns a node. */
|
|
10
|
+
nodeId?: string;
|
|
11
|
+
/** Index into `graph.links`, when the issue concerns a link. */
|
|
12
|
+
linkIndex?: number;
|
|
13
|
+
}
|
|
14
|
+
/** The outcome of {@link validateGraph}. */
|
|
15
|
+
export interface GraphValidationResult {
|
|
16
|
+
valid: boolean;
|
|
17
|
+
issues: GraphValidationIssue[];
|
|
18
|
+
}
|
|
19
|
+
/** Error thrown by {@link assertValidGraph} carrying the structured issues. */
|
|
20
|
+
export declare class GraphValidationError extends Error {
|
|
21
|
+
readonly issues: GraphValidationIssue[];
|
|
22
|
+
constructor(issues: GraphValidationIssue[]);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Statically validate a {@link Graph} against the node definitions in
|
|
26
|
+
* `registry`. Checks, in order: duplicate node ids, unknown node types, the
|
|
27
|
+
* single-output rule, dangling links (to a missing node or socket), duplicate
|
|
28
|
+
* links into one input, link type/field compatibility (honoring implicit
|
|
29
|
+
* conversions), and dependency cycles.
|
|
30
|
+
*
|
|
31
|
+
* Returns every issue found; an empty `issues` array means the graph is valid.
|
|
32
|
+
* Validation of embedded meta-node definitions is deferred to Phase 5.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateGraph(graph: Graph, registry: NodeRegistry): GraphValidationResult;
|
|
35
|
+
/** Assert a graph is valid against `registry`, throwing on any issue. */
|
|
36
|
+
export declare function assertValidGraph(graph: Graph, registry: NodeRegistry): void;
|
|
37
|
+
//# sourceMappingURL=validate-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-graph.d.ts","sourceRoot":"","sources":["../src/validate-graph.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,KAAK,EAEX,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,gEAAgE;AAChE,MAAM,MAAM,mBAAmB,GAC3B,mBAAmB,GACnB,mBAAmB,GACnB,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,sBAAsB,GACtB,sBAAsB,GACtB,eAAe,GACf,gBAAgB,GAChB,OAAO,CAAC;AAEZ,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,4CAA4C;AAC5C,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,+EAA+E;AAC/E,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC;gBAE5B,MAAM,EAAE,oBAAoB,EAAE;CAO3C;AAaD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,GAAG,qBAAqB,CAkIzF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAK3E"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { canConvertImplicitly } from './conversions';
|
|
2
|
+
import { endpointNode, endpointSocket, OUTPUT_NODE_TYPE, } from './graph';
|
|
3
|
+
/** Error thrown by {@link assertValidGraph} carrying the structured issues. */
|
|
4
|
+
export class GraphValidationError extends Error {
|
|
5
|
+
issues;
|
|
6
|
+
constructor(issues) {
|
|
7
|
+
super(`Invalid graph:\n${issues.map((issue) => ` - [${issue.code}] ${issue.message}`).join('\n')}`);
|
|
8
|
+
this.name = 'GraphValidationError';
|
|
9
|
+
this.issues = issues;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function endpointLabel(endpoint) {
|
|
13
|
+
return `${endpointNode(endpoint)}.${endpointSocket(endpoint)}`;
|
|
14
|
+
}
|
|
15
|
+
function findSocket(sockets, name) {
|
|
16
|
+
return sockets.find((socket) => socket.name === name);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Statically validate a {@link Graph} against the node definitions in
|
|
20
|
+
* `registry`. Checks, in order: duplicate node ids, unknown node types, the
|
|
21
|
+
* single-output rule, dangling links (to a missing node or socket), duplicate
|
|
22
|
+
* links into one input, link type/field compatibility (honoring implicit
|
|
23
|
+
* conversions), and dependency cycles.
|
|
24
|
+
*
|
|
25
|
+
* Returns every issue found; an empty `issues` array means the graph is valid.
|
|
26
|
+
* Validation of embedded meta-node definitions is deferred to Phase 5.
|
|
27
|
+
*/
|
|
28
|
+
export function validateGraph(graph, registry) {
|
|
29
|
+
const issues = [];
|
|
30
|
+
// Resolve each node's definition once; track known node ids.
|
|
31
|
+
const definitions = new Map();
|
|
32
|
+
const knownNodeIds = new Set();
|
|
33
|
+
for (const node of graph.nodes) {
|
|
34
|
+
if (knownNodeIds.has(node.id)) {
|
|
35
|
+
issues.push({
|
|
36
|
+
code: 'duplicate-node-id',
|
|
37
|
+
nodeId: node.id,
|
|
38
|
+
message: `Duplicate node id "${node.id}".`,
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
knownNodeIds.add(node.id);
|
|
43
|
+
const def = registry.get(node.type);
|
|
44
|
+
if (def === undefined) {
|
|
45
|
+
issues.push({
|
|
46
|
+
code: 'unknown-node-type',
|
|
47
|
+
nodeId: node.id,
|
|
48
|
+
message: `Node "${node.id}" has unknown type "${node.type}".`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
definitions.set(node.id, def);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Single-output rule.
|
|
56
|
+
const outputs = graph.nodes.filter((node) => node.type === OUTPUT_NODE_TYPE);
|
|
57
|
+
if (outputs.length === 0) {
|
|
58
|
+
issues.push({
|
|
59
|
+
code: 'missing-output',
|
|
60
|
+
message: `Graph has no ${OUTPUT_NODE_TYPE} node; exactly one is required.`,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else if (outputs.length > 1) {
|
|
64
|
+
issues.push({
|
|
65
|
+
code: 'multiple-outputs',
|
|
66
|
+
message: `Graph has ${outputs.length} ${OUTPUT_NODE_TYPE} nodes; exactly one is allowed.`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Per-link checks: dangling references, fan-in, type/field compatibility.
|
|
70
|
+
const inputSeen = new Set();
|
|
71
|
+
graph.links.forEach((link, linkIndex) => {
|
|
72
|
+
/** Report a dangling node reference; returns the node's definition if usable. */
|
|
73
|
+
const checkEndpointNode = (endpoint, side) => {
|
|
74
|
+
const id = endpointNode(endpoint);
|
|
75
|
+
if (!knownNodeIds.has(id)) {
|
|
76
|
+
issues.push({
|
|
77
|
+
code: 'dangling-link-node',
|
|
78
|
+
linkIndex,
|
|
79
|
+
message: `Link ${side} references unknown node "${id}".`,
|
|
80
|
+
});
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
return definitions.get(id);
|
|
84
|
+
};
|
|
85
|
+
const fromDef = checkEndpointNode(link.from, 'from');
|
|
86
|
+
const toDef = checkEndpointNode(link.to, 'to');
|
|
87
|
+
// Detect more than one link into the same input socket.
|
|
88
|
+
const toKey = endpointLabel(link.to);
|
|
89
|
+
if (inputSeen.has(toKey)) {
|
|
90
|
+
issues.push({
|
|
91
|
+
code: 'duplicate-input-link',
|
|
92
|
+
linkIndex,
|
|
93
|
+
message: `Input ${toKey} receives more than one link.`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
inputSeen.add(toKey);
|
|
97
|
+
if (fromDef === undefined || toDef === undefined)
|
|
98
|
+
return;
|
|
99
|
+
const fromSocket = findSocket(fromDef.outputs, endpointSocket(link.from));
|
|
100
|
+
const toSocket = findSocket(toDef.inputs, endpointSocket(link.to));
|
|
101
|
+
if (fromSocket === undefined) {
|
|
102
|
+
issues.push({
|
|
103
|
+
code: 'dangling-link-socket',
|
|
104
|
+
linkIndex,
|
|
105
|
+
message: `No output socket "${endpointSocket(link.from)}" on node "${endpointNode(link.from)}".`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (toSocket === undefined) {
|
|
109
|
+
issues.push({
|
|
110
|
+
code: 'dangling-link-socket',
|
|
111
|
+
linkIndex,
|
|
112
|
+
message: `No input socket "${endpointSocket(link.to)}" on node "${endpointNode(link.to)}".`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (fromSocket === undefined || toSocket === undefined)
|
|
116
|
+
return;
|
|
117
|
+
if ((fromSocket.isArray ?? false) !== (toSocket.isArray ?? false)) {
|
|
118
|
+
issues.push({
|
|
119
|
+
code: 'field-mismatch',
|
|
120
|
+
linkIndex,
|
|
121
|
+
message: `Link ${endpointLabel(link.from)} → ${endpointLabel(link.to)} connects a ${fromSocket.isArray ? 'field' : 'single value'} to a ${toSocket.isArray ? 'field' : 'single value'}.`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else if (!canConvertImplicitly(fromSocket.type, toSocket.type)) {
|
|
125
|
+
issues.push({
|
|
126
|
+
code: 'type-mismatch',
|
|
127
|
+
linkIndex,
|
|
128
|
+
message: `Link ${endpointLabel(link.from)} → ${endpointLabel(link.to)} connects ${fromSocket.type} to ${toSocket.type}, with no implicit conversion.`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
// Dependency cycle detection over links between known nodes.
|
|
133
|
+
const cycle = detectCycle(graph.links, knownNodeIds);
|
|
134
|
+
if (cycle.length > 0) {
|
|
135
|
+
issues.push({
|
|
136
|
+
code: 'cycle',
|
|
137
|
+
message: `Graph contains a cycle: ${cycle.join(' → ')}.`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return { valid: issues.length === 0, issues };
|
|
141
|
+
}
|
|
142
|
+
/** Assert a graph is valid against `registry`, throwing on any issue. */
|
|
143
|
+
export function assertValidGraph(graph, registry) {
|
|
144
|
+
const { issues } = validateGraph(graph, registry);
|
|
145
|
+
if (issues.length > 0) {
|
|
146
|
+
throw new GraphValidationError(issues);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Find one dependency cycle among `links` (treating each link as a directed
|
|
151
|
+
* edge `from → to`), restricted to nodes in `knownNodeIds`. Returns the cycle
|
|
152
|
+
* as a list of node ids (closing back on the first), or an empty array if the
|
|
153
|
+
* graph is acyclic.
|
|
154
|
+
*/
|
|
155
|
+
function detectCycle(links, knownNodeIds) {
|
|
156
|
+
const adjacency = new Map();
|
|
157
|
+
for (const link of links) {
|
|
158
|
+
const from = endpointNode(link.from);
|
|
159
|
+
const to = endpointNode(link.to);
|
|
160
|
+
if (!knownNodeIds.has(from) || !knownNodeIds.has(to))
|
|
161
|
+
continue;
|
|
162
|
+
const targets = adjacency.get(from);
|
|
163
|
+
if (targets)
|
|
164
|
+
targets.push(to);
|
|
165
|
+
else
|
|
166
|
+
adjacency.set(from, [to]);
|
|
167
|
+
}
|
|
168
|
+
const WHITE = 0;
|
|
169
|
+
const GRAY = 1;
|
|
170
|
+
const BLACK = 2;
|
|
171
|
+
const color = new Map();
|
|
172
|
+
const stack = [];
|
|
173
|
+
function visit(node) {
|
|
174
|
+
color.set(node, GRAY);
|
|
175
|
+
stack.push(node);
|
|
176
|
+
for (const next of adjacency.get(node) ?? []) {
|
|
177
|
+
const state = color.get(next) ?? WHITE;
|
|
178
|
+
if (state === GRAY) {
|
|
179
|
+
const start = stack.indexOf(next);
|
|
180
|
+
return [...stack.slice(start), next];
|
|
181
|
+
}
|
|
182
|
+
if (state === WHITE) {
|
|
183
|
+
const found = visit(next);
|
|
184
|
+
if (found)
|
|
185
|
+
return found;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
stack.pop();
|
|
189
|
+
color.set(node, BLACK);
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
for (const node of knownNodeIds) {
|
|
193
|
+
if ((color.get(node) ?? WHITE) === WHITE) {
|
|
194
|
+
const found = visit(node);
|
|
195
|
+
if (found)
|
|
196
|
+
return found;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=validate-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-graph.js","sourceRoot":"","sources":["../src/validate-graph.ts"],"names":[],"mappings":"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"}
|
package/dist/vnodes.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Graph } from './graph';
|
|
2
|
+
/** A single schema-validation problem, derived from an ajv error. */
|
|
3
|
+
export interface VnodesValidationIssue {
|
|
4
|
+
/** JSON Pointer to the offending value (ajv `instancePath`). */
|
|
5
|
+
path: string;
|
|
6
|
+
/** Human-readable description. */
|
|
7
|
+
message: string;
|
|
8
|
+
/** The schema keyword that failed (e.g. `"required"`, `"enum"`). */
|
|
9
|
+
keyword: string;
|
|
10
|
+
}
|
|
11
|
+
/** Error thrown when a value fails `.vnodes` schema validation. */
|
|
12
|
+
export declare class VnodesValidationError extends Error {
|
|
13
|
+
readonly issues: VnodesValidationIssue[];
|
|
14
|
+
constructor(issues: VnodesValidationIssue[]);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate an arbitrary value against the `.vnodes` schema. Returns the list of
|
|
18
|
+
* issues; an empty array means the value is valid.
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateVnodes(value: unknown): VnodesValidationIssue[];
|
|
21
|
+
/** Type guard: whether a value is a schema-valid {@link Graph}. */
|
|
22
|
+
export declare function isValidVnodes(value: unknown): value is Graph;
|
|
23
|
+
/** Assert a value is a schema-valid {@link Graph}, throwing otherwise. */
|
|
24
|
+
export declare function assertValidVnodes(value: unknown): asserts value is Graph;
|
|
25
|
+
/**
|
|
26
|
+
* Parse `.vnodes` JSON text into a validated {@link Graph}.
|
|
27
|
+
*
|
|
28
|
+
* @throws SyntaxError if the text is not valid JSON.
|
|
29
|
+
* @throws VnodesValidationError if the document does not match the schema.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseVnodes(text: string): Graph;
|
|
32
|
+
/**
|
|
33
|
+
* Serialize a {@link Graph} to `.vnodes` JSON text. Indents with `space` spaces
|
|
34
|
+
* (default 2) and appends a trailing newline.
|
|
35
|
+
*/
|
|
36
|
+
export declare function serializeVnodes(graph: Graph, space?: number): string;
|
|
37
|
+
//# sourceMappingURL=vnodes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vnodes.d.ts","sourceRoot":"","sources":["../src/vnodes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGrC,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,mEAAmE;AACnE,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC;gBAE7B,MAAM,EAAE,qBAAqB,EAAE;CAS5C;AAaD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,EAAE,CAEtE;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAE5D;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAKxE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAI/C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,SAAI,GAAG,MAAM,CAE/D"}
|
package/dist/vnodes.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Ajv2020, {} from 'ajv/dist/2020';
|
|
2
|
+
import { VNODES_SCHEMA } from './schema';
|
|
3
|
+
/** Error thrown when a value fails `.vnodes` schema validation. */
|
|
4
|
+
export class VnodesValidationError extends Error {
|
|
5
|
+
issues;
|
|
6
|
+
constructor(issues) {
|
|
7
|
+
super(`Invalid .vnodes document:\n${issues
|
|
8
|
+
.map((issue) => ` - ${issue.path || '/'} ${issue.message}`)
|
|
9
|
+
.join('\n')}`);
|
|
10
|
+
this.name = 'VnodesValidationError';
|
|
11
|
+
this.issues = issues;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
15
|
+
const validateFn = ajv.compile(VNODES_SCHEMA);
|
|
16
|
+
function toIssues(errors) {
|
|
17
|
+
return (errors ?? []).map((error) => ({
|
|
18
|
+
path: error.instancePath,
|
|
19
|
+
message: error.message ?? 'is invalid',
|
|
20
|
+
keyword: error.keyword,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate an arbitrary value against the `.vnodes` schema. Returns the list of
|
|
25
|
+
* issues; an empty array means the value is valid.
|
|
26
|
+
*/
|
|
27
|
+
export function validateVnodes(value) {
|
|
28
|
+
return validateFn(value) ? [] : toIssues(validateFn.errors);
|
|
29
|
+
}
|
|
30
|
+
/** Type guard: whether a value is a schema-valid {@link Graph}. */
|
|
31
|
+
export function isValidVnodes(value) {
|
|
32
|
+
return validateFn(value);
|
|
33
|
+
}
|
|
34
|
+
/** Assert a value is a schema-valid {@link Graph}, throwing otherwise. */
|
|
35
|
+
export function assertValidVnodes(value) {
|
|
36
|
+
const issues = validateVnodes(value);
|
|
37
|
+
if (issues.length > 0) {
|
|
38
|
+
throw new VnodesValidationError(issues);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse `.vnodes` JSON text into a validated {@link Graph}.
|
|
43
|
+
*
|
|
44
|
+
* @throws SyntaxError if the text is not valid JSON.
|
|
45
|
+
* @throws VnodesValidationError if the document does not match the schema.
|
|
46
|
+
*/
|
|
47
|
+
export function parseVnodes(text) {
|
|
48
|
+
const value = JSON.parse(text);
|
|
49
|
+
assertValidVnodes(value);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Serialize a {@link Graph} to `.vnodes` JSON text. Indents with `space` spaces
|
|
54
|
+
* (default 2) and appends a trailing newline.
|
|
55
|
+
*/
|
|
56
|
+
export function serializeVnodes(graph, space = 2) {
|
|
57
|
+
return `${JSON.stringify(graph, null, space)}\n`;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=vnodes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vnodes.js","sourceRoot":"","sources":["../src/vnodes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAA2C,MAAM,eAAe,CAAC;AAGjF,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAYzC,mEAAmE;AACnE,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACrC,MAAM,CAA0B;IAEzC,YAAY,MAA+B;QACzC,KAAK,CACH,8BAA8B,MAAM;aACjC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,IAAI,IAAI,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;aAC3D,IAAI,CAAC,IAAI,CAAC,EAAE,CAChB,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5D,MAAM,UAAU,GAAqB,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;AAEhE,SAAS,QAAQ,CAAC,MAAwC;IACxD,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACpC,IAAI,EAAE,KAAK,CAAC,YAAY;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,YAAY;QACtC,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAC9D,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,UAAU,CAAC,KAAK,CAAY,CAAC;AACtC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxC,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACzB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAY,EAAE,KAAK,GAAG,CAAC;IACrD,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;AACnD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vector-nodes/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Types, graph model, and .vnodes schema for Vector Nodes.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Sergey Chernyshev <sergey.chernyshev@gmail.com>",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/sergeychernyshev/vector-nodes.git",
|
|
12
|
+
"directory": "packages/core"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=24"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc -b",
|
|
33
|
+
"typecheck": "tsc -b",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"prepack": "tsc -b"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"ajv": "^8.20.0"
|
|
39
|
+
}
|
|
40
|
+
}
|