svedit 0.9.0 → 0.10.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/README.md +113 -4
- package/dist/AnnotatedTextProperty.svelte +41 -8
- package/dist/AnnotatedTextProperty.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.d.ts +0 -1
- package/dist/Command.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.js +18 -3
- package/dist/CustomProperty.svelte +20 -9
- package/dist/CustomProperty.svelte.d.ts +2 -1
- package/dist/CustomProperty.svelte.d.ts.map +1 -1
- package/dist/Node.svelte +7 -5
- package/dist/Node.svelte.d.ts.map +1 -1
- package/dist/NodeArrayProperty.svelte +65 -42
- package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
- package/dist/NodeCaret.svelte +0 -7
- package/dist/NodeGap.svelte +179 -81
- package/dist/NodeGap.svelte.d.ts +0 -2
- package/dist/NodeGap.svelte.d.ts.map +1 -1
- package/dist/NodeGapMarkers.svelte +251 -84
- package/dist/NodeGapMarkers.svelte.d.ts +2 -0
- package/dist/NodeGapMarkers.svelte.d.ts.map +1 -1
- package/dist/NodeSelectionMarkers.svelte +4 -3
- package/dist/NodeSelectionMarkers.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.d.ts +12 -1
- package/dist/Session.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.js +40 -3
- package/dist/Svedit.svelte +336 -165
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.d.ts +24 -5
- package/dist/Transaction.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.js +103 -61
- package/dist/doc_utils.d.ts +64 -3
- package/dist/doc_utils.d.ts.map +1 -1
- package/dist/doc_utils.js +276 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/dist/node_visibility.svelte.d.ts +9 -0
- package/dist/node_visibility.svelte.d.ts.map +1 -0
- package/dist/node_visibility.svelte.js +718 -0
- package/dist/styles/svedit-animations.css +8 -0
- package/dist/styles/svedit-colors.css +1 -0
- package/dist/types.d.ts +46 -3
- package/dist/utils.d.ts +52 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +109 -3
- package/package.json +3 -2
- package/src/lib/AnnotatedTextProperty.svelte +41 -8
- package/src/lib/Command.svelte.js +18 -3
- package/src/lib/CustomProperty.svelte +20 -9
- package/src/lib/Node.svelte +7 -5
- package/src/lib/NodeArrayProperty.svelte +65 -42
- package/src/lib/NodeCaret.svelte +0 -7
- package/src/lib/NodeGap.svelte +179 -81
- package/src/lib/NodeGapMarkers.svelte +251 -84
- package/src/lib/NodeSelectionMarkers.svelte +4 -3
- package/src/lib/Session.svelte.js +40 -3
- package/src/lib/Svedit.svelte +336 -165
- package/src/lib/Transaction.svelte.js +103 -61
- package/src/lib/doc_utils.js +276 -10
- package/src/lib/index.js +5 -1
- package/src/lib/node_visibility.svelte.js +718 -0
- package/src/lib/styles/svedit-animations.css +8 -0
- package/src/lib/styles/svedit-colors.css +1 -0
- package/src/lib/types.d.ts +46 -3
- package/src/lib/utils.js +109 -3
- package/dist/node_gap_computation.svelte.d.ts +0 -5
- package/dist/node_gap_computation.svelte.d.ts.map +0 -1
- package/dist/node_gap_computation.svelte.js +0 -621
- package/src/lib/node_gap_computation.svelte.js +0 -621
package/src/lib/doc_utils.js
CHANGED
|
@@ -4,17 +4,23 @@
|
|
|
4
4
|
* These functions operate on the core document state (schema, doc, selection, config)
|
|
5
5
|
* without any history management or transaction tracking.
|
|
6
6
|
*
|
|
7
|
-
* @import { NodeId, DocumentPath, PrimitiveType, NodeProperty, NodeArrayProperty, NodeSchema, DocumentSchema, Selection, Annotation, Document } from './types'
|
|
7
|
+
* @import { NodeId, DocumentPath, PrimitiveType, NodeProperty, NodeArrayProperty, NodeSchema, DocumentSchema, Selection, Annotation, Document, AnnotatedTextProperty, AnnotatedText, ValidateDocumentSchema } from './types'
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
assert_path_string_segment,
|
|
12
|
+
is_path_string_segment_valid,
|
|
13
|
+
get_selection_range,
|
|
14
|
+
get_char_length,
|
|
15
|
+
serialize_path
|
|
16
|
+
} from './utils.js';
|
|
11
17
|
|
|
12
18
|
/**
|
|
13
19
|
* Identity function — keeps schema at runtime & makes IDE infer types.
|
|
14
20
|
* Similar to your define_schema pattern but for document schemas.
|
|
15
21
|
*
|
|
16
22
|
* @template {Record<string, NodeSchema>} S
|
|
17
|
-
* @param {S} schema - The document schema to validate
|
|
23
|
+
* @param {S & ValidateDocumentSchema<S>} schema - The document schema to validate
|
|
18
24
|
* @returns {S} The same schema, but with type information preserved
|
|
19
25
|
*/
|
|
20
26
|
export function define_document_schema(schema) {
|
|
@@ -57,6 +63,84 @@ export function get_default_node_type(property_definition) {
|
|
|
57
63
|
);
|
|
58
64
|
}
|
|
59
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @param {import('./types').PropertyDefinition} property_definition
|
|
68
|
+
* @returns {any}
|
|
69
|
+
*/
|
|
70
|
+
function get_property_default(property_definition) {
|
|
71
|
+
if ('default' in property_definition) return structuredClone(property_definition.default);
|
|
72
|
+
|
|
73
|
+
if (property_definition.type === 'string') return '';
|
|
74
|
+
if (property_definition.type === 'integer') return 0;
|
|
75
|
+
if (property_definition.type === 'number') return 0;
|
|
76
|
+
if (property_definition.type === 'boolean') return false;
|
|
77
|
+
if (property_definition.type === 'annotated_text') return { text: '', annotations: [] };
|
|
78
|
+
if (property_definition.type === 'node_array') return [];
|
|
79
|
+
if (
|
|
80
|
+
property_definition.type === 'string_array' ||
|
|
81
|
+
property_definition.type === 'number_array' ||
|
|
82
|
+
property_definition.type === 'boolean_array' ||
|
|
83
|
+
property_definition.type === 'integer_array'
|
|
84
|
+
) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Fill omitted properties with schema defaults and Svedit's built-in type defaults.
|
|
93
|
+
*
|
|
94
|
+
* This is a convenience helper for schema evolution, not a complete document
|
|
95
|
+
* migration system. Callers are still responsible for proper migrations when
|
|
96
|
+
* schema changes cannot be represented by defaults, such as property renames or
|
|
97
|
+
* data transformations.
|
|
98
|
+
*
|
|
99
|
+
* @param {any} node - The node to fill defaults for
|
|
100
|
+
* @param {DocumentSchema} schema - The document schema
|
|
101
|
+
* @returns {any} A shallow copy of the node with cloned default values filled in
|
|
102
|
+
*/
|
|
103
|
+
export function fill_node_defaults(node, schema) {
|
|
104
|
+
const node_schema = schema[node.type];
|
|
105
|
+
if (!node_schema) return { ...node };
|
|
106
|
+
|
|
107
|
+
const node_with_defaults = { ...node };
|
|
108
|
+
|
|
109
|
+
for (const [property_name, property_definition] of Object.entries(node_schema.properties)) {
|
|
110
|
+
if (node_with_defaults[property_name] === undefined) {
|
|
111
|
+
const property_default = get_property_default(property_definition);
|
|
112
|
+
if (property_default !== undefined) node_with_defaults[property_name] = property_default;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return node_with_defaults;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Fill omitted properties with schema defaults and Svedit's built-in type defaults across a document.
|
|
121
|
+
*
|
|
122
|
+
* This does not infer values, rename fields, or make an invalid migration valid
|
|
123
|
+
* by itself. Call this as one step in an explicit document migration when it is
|
|
124
|
+
* appropriate.
|
|
125
|
+
*
|
|
126
|
+
* @param {Document} doc - The document to fill defaults for
|
|
127
|
+
* @param {DocumentSchema} schema - The document schema
|
|
128
|
+
* @returns {Document} A document copy with cloned default values filled in
|
|
129
|
+
*/
|
|
130
|
+
export function fill_document_defaults(doc, schema) {
|
|
131
|
+
/** @type {Record<string, any>} */
|
|
132
|
+
const nodes = {};
|
|
133
|
+
|
|
134
|
+
for (const [node_id, node] of Object.entries(doc.nodes)) {
|
|
135
|
+
nodes[node_id] = fill_node_defaults(node, schema);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
...doc,
|
|
140
|
+
nodes
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
60
144
|
/**
|
|
61
145
|
* Validate a document schema to ensure all referenced node types exist.
|
|
62
146
|
* @param {DocumentSchema} document_schema - The document schema to validate
|
|
@@ -66,6 +150,7 @@ export function validate_document_schema(document_schema) {
|
|
|
66
150
|
// Check that all referenced node types exist
|
|
67
151
|
for (const [node_type, node_schema] of Object.entries(document_schema)) {
|
|
68
152
|
for (const [prop_name, prop_def] of Object.entries(node_schema.properties)) {
|
|
153
|
+
assert_path_string_segment(prop_name, `Property name "${prop_name}"`);
|
|
69
154
|
if (prop_def.type === 'node' || prop_def.type === 'node_array') {
|
|
70
155
|
const missing_types = prop_def.node_types.filter(
|
|
71
156
|
(ref_type) => !(ref_type in document_schema)
|
|
@@ -118,12 +203,88 @@ function validate_primitive_value(type, value) {
|
|
|
118
203
|
}
|
|
119
204
|
}
|
|
120
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Validate annotated text annotations for bounds, references, allowed types, and exclusivity.
|
|
208
|
+
*
|
|
209
|
+
* @param {string} node_id - Owner node id, used for error messages
|
|
210
|
+
* @param {string} prop_name - Owner property name, used for error messages
|
|
211
|
+
* @param {AnnotatedText} value - Annotated text value to validate
|
|
212
|
+
* @param {AnnotatedTextProperty} prop_def - Annotated text property definition
|
|
213
|
+
* @param {Record<string, any>} all_nodes - All document nodes
|
|
214
|
+
* @param {boolean} require_references - Whether referenced nodes must already exist
|
|
215
|
+
* @throws {Error} Throws if annotations are invalid
|
|
216
|
+
*/
|
|
217
|
+
function validate_annotated_text_property(node_id, prop_name, value, prop_def, all_nodes, require_references) {
|
|
218
|
+
const char_length = get_char_length(value.text);
|
|
219
|
+
const annotations = value.annotations;
|
|
220
|
+
|
|
221
|
+
for (const [index, annotation] of annotations.entries()) {
|
|
222
|
+
if (
|
|
223
|
+
typeof annotation !== 'object' ||
|
|
224
|
+
annotation === null ||
|
|
225
|
+
!Number.isInteger(annotation.start_offset) ||
|
|
226
|
+
!Number.isInteger(annotation.end_offset) ||
|
|
227
|
+
!is_id_valid(annotation.node_id)
|
|
228
|
+
) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`Node ${node_id} property ${prop_name} has an invalid annotation at index ${index}. Annotations must have integer start_offset/end_offset and a valid node_id.`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (annotation.start_offset < 0 || annotation.end_offset > char_length) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Node ${node_id} property ${prop_name} annotation ${annotation.node_id} is out of bounds: ${annotation.start_offset}-${annotation.end_offset}, text length is ${char_length}.`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (annotation.start_offset >= annotation.end_offset) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`Node ${node_id} property ${prop_name} annotation ${annotation.node_id} must not be empty or reversed: ${annotation.start_offset}-${annotation.end_offset}.`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const referenced_node = all_nodes[annotation.node_id];
|
|
247
|
+
if (!referenced_node) {
|
|
248
|
+
if (require_references) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`Node ${node_id} property ${prop_name} annotation references missing node ${annotation.node_id}.`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (prop_def.node_types?.length && !prop_def.node_types.includes(referenced_node.type)) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
`Node ${node_id} property ${prop_name} annotation references node ${annotation.node_id} of type ${referenced_node.type}, but only types [${prop_def.node_types.join(', ')}] are allowed.`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const sorted_annotations = annotations
|
|
264
|
+
.map((annotation, index) => ({ annotation, index }))
|
|
265
|
+
.sort((a, b) =>
|
|
266
|
+
a.annotation.start_offset - b.annotation.start_offset ||
|
|
267
|
+
a.annotation.end_offset - b.annotation.end_offset
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
for (let index = 1; index < sorted_annotations.length; index++) {
|
|
271
|
+
const previous = sorted_annotations[index - 1];
|
|
272
|
+
const current = sorted_annotations[index];
|
|
273
|
+
|
|
274
|
+
if (current.annotation.start_offset < previous.annotation.end_offset) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`Node ${node_id} property ${prop_name} has overlapping annotations: annotation index ${previous.index} (${previous.annotation.node_id}, ${previous.annotation.start_offset}-${previous.annotation.end_offset}) overlaps annotation index ${current.index} (${current.annotation.node_id}, ${current.annotation.start_offset}-${current.annotation.end_offset}). Annotations must be exclusive.`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
121
282
|
/**
|
|
122
283
|
* @param {string} id
|
|
123
284
|
* @returns {boolean}
|
|
124
285
|
*/
|
|
125
|
-
function is_id_valid(id) {
|
|
126
|
-
return typeof id === 'string' && id.length > 0;
|
|
286
|
+
export function is_id_valid(id) {
|
|
287
|
+
return typeof id === 'string' && id.length > 0 && is_path_string_segment_valid(id);
|
|
127
288
|
}
|
|
128
289
|
|
|
129
290
|
/**
|
|
@@ -131,9 +292,11 @@ function is_id_valid(id) {
|
|
|
131
292
|
* @param {any} node - The node to validate
|
|
132
293
|
* @param {DocumentSchema} schema - The document schema
|
|
133
294
|
* @param {Record<string, any>} all_nodes - All nodes in the document to check references
|
|
295
|
+
* @param {{ require_references?: boolean }} [options] - Validation options
|
|
134
296
|
* @throws {Error} Throws if the node is invalid
|
|
135
297
|
*/
|
|
136
|
-
export function validate_node(node, schema, all_nodes = {}) {
|
|
298
|
+
export function validate_node(node, schema, all_nodes = {}, options = {}) {
|
|
299
|
+
const require_references = options.require_references ?? true;
|
|
137
300
|
if (!is_id_valid(node.id)) {
|
|
138
301
|
throw new Error(`Node ${node.id} has an invalid id.`);
|
|
139
302
|
}
|
|
@@ -155,6 +318,16 @@ export function validate_node(node, schema, all_nodes = {}) {
|
|
|
155
318
|
);
|
|
156
319
|
}
|
|
157
320
|
}
|
|
321
|
+
if (prop_def.type === 'annotated_text') {
|
|
322
|
+
validate_annotated_text_property(
|
|
323
|
+
node.id,
|
|
324
|
+
prop_name,
|
|
325
|
+
value,
|
|
326
|
+
/** @type {AnnotatedTextProperty} */ (prop_def),
|
|
327
|
+
all_nodes,
|
|
328
|
+
require_references
|
|
329
|
+
);
|
|
330
|
+
}
|
|
158
331
|
// Check node references
|
|
159
332
|
if (prop_def.type === 'node') {
|
|
160
333
|
if (!is_id_valid(value)) {
|
|
@@ -164,7 +337,15 @@ export function validate_node(node, schema, all_nodes = {}) {
|
|
|
164
337
|
}
|
|
165
338
|
// Check if referenced node exists and is of allowed type
|
|
166
339
|
const referenced_node = all_nodes[value];
|
|
167
|
-
if (
|
|
340
|
+
if (!referenced_node) {
|
|
341
|
+
if (require_references) {
|
|
342
|
+
throw new Error(
|
|
343
|
+
`Node ${node.id} property ${prop_name} references missing node ${value}.`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (!prop_def.node_types.includes(referenced_node.type)) {
|
|
168
349
|
throw new Error(
|
|
169
350
|
`Node ${node.id} property ${prop_name} references node ${value} of type ${referenced_node.type}, but only types [${/** @type {NodeProperty} */ (prop_def).node_types.join(', ')}] are allowed.`
|
|
170
351
|
);
|
|
@@ -180,10 +361,18 @@ export function validate_node(node, schema, all_nodes = {}) {
|
|
|
180
361
|
`Node ${node.id} has an invalid property: ${prop_name} must be an array of node ids.`
|
|
181
362
|
);
|
|
182
363
|
}
|
|
183
|
-
// Check if all referenced nodes are of allowed types
|
|
364
|
+
// Check if all referenced nodes exist and are of allowed types
|
|
184
365
|
for (const ref_id of value) {
|
|
185
366
|
const referenced_node = all_nodes[ref_id];
|
|
186
|
-
if (
|
|
367
|
+
if (!referenced_node) {
|
|
368
|
+
if (require_references) {
|
|
369
|
+
throw new Error(
|
|
370
|
+
`Node ${node.id} property ${prop_name} references missing node ${ref_id}.`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (!prop_def.node_types.includes(referenced_node.type)) {
|
|
187
376
|
throw new Error(
|
|
188
377
|
`Node ${node.id} property ${prop_name} references node ${ref_id} of type ${referenced_node.type}, but only types [${prop_def.node_types.join(', ')}] are allowed.`
|
|
189
378
|
);
|
|
@@ -193,6 +382,22 @@ export function validate_node(node, schema, all_nodes = {}) {
|
|
|
193
382
|
}
|
|
194
383
|
}
|
|
195
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Validate a document against its schema.
|
|
387
|
+
*
|
|
388
|
+
* @param {Document} doc - The document to validate
|
|
389
|
+
* @param {DocumentSchema} schema - The document schema
|
|
390
|
+
* @throws {Error} Throws if the document is invalid
|
|
391
|
+
*/
|
|
392
|
+
export function validate_document(doc, schema) {
|
|
393
|
+
if (!is_id_valid(doc.document_id)) {
|
|
394
|
+
throw new Error(`Document ${doc.document_id} has an invalid id.`);
|
|
395
|
+
}
|
|
396
|
+
for (const node of Object.values(doc.nodes)) {
|
|
397
|
+
validate_node(node, schema, doc.nodes);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
196
401
|
/**
|
|
197
402
|
* Gets a value from the document at the specified path.
|
|
198
403
|
*
|
|
@@ -310,6 +515,31 @@ export function kind(schema, node) {
|
|
|
310
515
|
return schema[node.type].kind;
|
|
311
516
|
}
|
|
312
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Checks whether an annotation node can be switched to another annotation type
|
|
520
|
+
* without losing data.
|
|
521
|
+
*
|
|
522
|
+
* @param {DocumentSchema} schema - The document schema
|
|
523
|
+
* @param {any} annotation_node - The annotation node to switch
|
|
524
|
+
* @param {string} target_annotation_type - The target annotation type
|
|
525
|
+
* @returns {boolean} True if the annotation can be switched safely
|
|
526
|
+
*/
|
|
527
|
+
export function can_switch_annotation_type(schema, annotation_node, target_annotation_type) {
|
|
528
|
+
if (!annotation_node || annotation_node.type === target_annotation_type) return false;
|
|
529
|
+
|
|
530
|
+
const source_schema = schema[annotation_node.type];
|
|
531
|
+
const target_schema = schema[target_annotation_type];
|
|
532
|
+
if (source_schema?.kind !== 'annotation' || target_schema?.kind !== 'annotation') return false;
|
|
533
|
+
|
|
534
|
+
const source_has_properties = Object.keys(source_schema.properties ?? {}).length > 0;
|
|
535
|
+
const target_has_properties = Object.keys(target_schema.properties ?? {}).length > 0;
|
|
536
|
+
if (source_has_properties || target_has_properties) return false;
|
|
537
|
+
|
|
538
|
+
// eslint-disable-next-line no-unused-vars
|
|
539
|
+
const { id, type, ...extra_properties } = annotation_node;
|
|
540
|
+
return Object.keys(extra_properties).length === 0;
|
|
541
|
+
}
|
|
542
|
+
|
|
313
543
|
/**
|
|
314
544
|
* Inspects a path to get metadata about the value at that location.
|
|
315
545
|
*
|
|
@@ -481,6 +711,42 @@ export function count_references_excluding_deleted(schema, doc, target_node_id,
|
|
|
481
711
|
return count;
|
|
482
712
|
}
|
|
483
713
|
|
|
714
|
+
/**
|
|
715
|
+
* Gets ids of nodes that reference any of the target nodes.
|
|
716
|
+
*
|
|
717
|
+
* @param {DocumentSchema} schema - The document schema
|
|
718
|
+
* @param {Document} doc - The document containing nodes
|
|
719
|
+
* @param {Iterable<NodeId>} target_node_ids - Node IDs to find referrers for
|
|
720
|
+
* @returns {NodeId[]} IDs of nodes with references to any target node
|
|
721
|
+
*/
|
|
722
|
+
export function get_referencing_node_ids(schema, doc, target_node_ids) {
|
|
723
|
+
const target_ids = new Set(target_node_ids);
|
|
724
|
+
const referencing_node_ids = new Set();
|
|
725
|
+
if (target_ids.size === 0) return [];
|
|
726
|
+
|
|
727
|
+
for (const node of Object.values(doc.nodes)) {
|
|
728
|
+
for (const [property, value] of Object.entries(node)) {
|
|
729
|
+
if (property === 'id' || property === 'type') continue;
|
|
730
|
+
|
|
731
|
+
const prop_type = property_type(schema, node.type, property);
|
|
732
|
+
|
|
733
|
+
if (prop_type === 'node_array' && Array.isArray(value)) {
|
|
734
|
+
if (value.some((id) => target_ids.has(id))) {
|
|
735
|
+
referencing_node_ids.add(node.id);
|
|
736
|
+
}
|
|
737
|
+
} else if (prop_type === 'node' && target_ids.has(value)) {
|
|
738
|
+
referencing_node_ids.add(node.id);
|
|
739
|
+
} else if (prop_type === 'annotated_text' && value?.annotations) {
|
|
740
|
+
if (value.annotations.some((annotation) => target_ids.has(annotation.node_id))) {
|
|
741
|
+
referencing_node_ids.add(node.id);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return [...referencing_node_ids];
|
|
748
|
+
}
|
|
749
|
+
|
|
484
750
|
/**
|
|
485
751
|
* Validates a selection against the current document state.
|
|
486
752
|
* Works with any object that implements get() and inspect() (Session or Transaction).
|
|
@@ -535,7 +801,7 @@ export function validate_selection(selection, session_or_transaction) {
|
|
|
535
801
|
}
|
|
536
802
|
} else if (selection_type === 'property') {
|
|
537
803
|
if (!session_or_transaction.inspect(selection.path)) {
|
|
538
|
-
throw new Error(`Property selection path not found: ${selection.path
|
|
804
|
+
throw new Error(`Property selection path not found: ${serialize_path(selection.path)}`);
|
|
539
805
|
}
|
|
540
806
|
}
|
|
541
807
|
}
|
package/src/lib/index.js
CHANGED
|
@@ -18,8 +18,12 @@ export {
|
|
|
18
18
|
define_document_schema,
|
|
19
19
|
is_primitive_type,
|
|
20
20
|
get_default_node_type,
|
|
21
|
+
fill_node_defaults,
|
|
22
|
+
fill_document_defaults,
|
|
21
23
|
validate_document_schema,
|
|
22
|
-
|
|
24
|
+
validate_document,
|
|
25
|
+
validate_node,
|
|
26
|
+
get_referencing_node_ids
|
|
23
27
|
} from './doc_utils.js';
|
|
24
28
|
|
|
25
29
|
// Command system
|