@revisium/schema-toolkit 0.15.0 → 0.16.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.
@@ -68,6 +68,9 @@ var NullNodeImpl = class {
68
68
  removeChild(_name) {
69
69
  return false;
70
70
  }
71
+ replaceChild(_name, _node) {
72
+ return false;
73
+ }
71
74
  setItems(_node) {
72
75
  }
73
76
  setDefaultValue(_value) {
@@ -147,6 +150,9 @@ var BaseNode = class {
147
150
  removeChild(_name) {
148
151
  return false;
149
152
  }
153
+ replaceChild(_name, _node) {
154
+ return false;
155
+ }
150
156
  setItems(_node) {
151
157
  }
152
158
  setDefaultValue(_value) {
@@ -195,6 +201,14 @@ var ObjectNode = class _ObjectNode extends BaseNode {
195
201
  this._children.splice(index, 1);
196
202
  return true;
197
203
  }
204
+ replaceChild(name, node) {
205
+ const index = this._children.findIndex((child) => child.name() === name);
206
+ if (index === -1) {
207
+ return false;
208
+ }
209
+ this._children[index] = node;
210
+ return true;
211
+ }
198
212
  };
199
213
  function createObjectNode(id, name, children = [], metadata = EMPTY_METADATA) {
200
214
  return new ObjectNode(id, name, children, metadata);
@@ -561,14 +575,15 @@ var TreeNodeIndex = class {
561
575
 
562
576
  // src/core/schema-tree/SchemaTreeImpl.ts
563
577
  var SchemaTreeImpl = class _SchemaTreeImpl {
578
+ index = new TreeNodeIndex();
579
+ _replacements = /* @__PURE__ */ new Map();
580
+ _rootNode;
564
581
  constructor(rootNode) {
565
- this.rootNode = rootNode;
582
+ this._rootNode = rootNode;
566
583
  this.index.rebuild(rootNode);
567
584
  }
568
- index = new TreeNodeIndex();
569
- _replacements = /* @__PURE__ */ new Map();
570
585
  root() {
571
- return this.rootNode;
586
+ return this._rootNode;
572
587
  }
573
588
  nodeById(id) {
574
589
  return this.index.getNode(id);
@@ -578,9 +593,9 @@ var SchemaTreeImpl = class _SchemaTreeImpl {
578
593
  }
579
594
  nodeAt(path) {
580
595
  if (path.isEmpty()) {
581
- return this.rootNode;
596
+ return this._rootNode;
582
597
  }
583
- let current = this.rootNode;
598
+ let current = this._rootNode;
584
599
  for (const segment of path.segments()) {
585
600
  if (current.isNull()) {
586
601
  return NULL_NODE;
@@ -600,7 +615,7 @@ var SchemaTreeImpl = class _SchemaTreeImpl {
600
615
  return this.index.countNodes();
601
616
  }
602
617
  clone() {
603
- const cloned = new _SchemaTreeImpl(this.rootNode.clone());
618
+ const cloned = new _SchemaTreeImpl(this._rootNode.clone());
604
619
  for (const [oldId, newId] of this._replacements) {
605
620
  cloned._replacements.set(oldId, newId);
606
621
  }
@@ -698,14 +713,18 @@ var SchemaTreeImpl = class _SchemaTreeImpl {
698
713
  if (lastSegment.isItems()) {
699
714
  parent.setItems(node);
700
715
  } else {
701
- parent.removeChild(lastSegment.propertyName());
702
- node.setName(lastSegment.propertyName());
703
- parent.addChild(node);
716
+ const propertyName = lastSegment.propertyName();
717
+ node.setName(propertyName);
718
+ parent.replaceChild(propertyName, node);
704
719
  }
705
720
  this.rebuildIndex();
706
721
  }
722
+ replaceRoot(newRoot) {
723
+ this._rootNode = newRoot;
724
+ this.rebuildIndex();
725
+ }
707
726
  rebuildIndex() {
708
- this.index.rebuild(this.rootNode);
727
+ this.index.rebuild(this._rootNode);
709
728
  }
710
729
  };
711
730
  function createSchemaTree(root) {
@@ -2142,6 +2161,409 @@ var PatchBuilder = class {
2142
2161
  }
2143
2162
  };
2144
2163
 
2145
- export { AbstractBasePath, ChangeCoalescer, ChangeCollector, EMPTY_METADATA, EMPTY_PATH, FormulaDependencyIndex, ItemsSegment, NULL_NODE, NodePathIndex, ParsedFormula, PatchBuilder, PatchEnricher, PatchGenerator, PropertySegment, SchemaDiff, SchemaSerializer, areNodesContentEqual, areNodesEqual, coalesceChanges, collectChanges, createArrayNode, createBooleanNode, createNumberNode, createObjectNode, createPath, createRefNode, createSchemaTree, createStringNode, jsonPointerToPath, jsonPointerToSegments, jsonPointerToSimplePath };
2146
- //# sourceMappingURL=chunk-F4UBBZAL.js.map
2147
- //# sourceMappingURL=chunk-F4UBBZAL.js.map
2164
+ // src/core/validation/ValidatorRegistry.ts
2165
+ var ValidatorRegistry = class {
2166
+ validators = /* @__PURE__ */ new Map();
2167
+ rules = [];
2168
+ register(type, factory) {
2169
+ this.validators.set(type, factory);
2170
+ return this;
2171
+ }
2172
+ addRule(rule) {
2173
+ this.rules.push(rule);
2174
+ return this;
2175
+ }
2176
+ get(type) {
2177
+ const factory = this.validators.get(type);
2178
+ return factory ? factory() : void 0;
2179
+ }
2180
+ has(type) {
2181
+ return this.validators.has(type);
2182
+ }
2183
+ getRules() {
2184
+ return this.rules;
2185
+ }
2186
+ getValidatorTypes() {
2187
+ return Array.from(this.validators.keys());
2188
+ }
2189
+ };
2190
+
2191
+ // src/core/validation/ValidatorResolver.ts
2192
+ var ValidatorResolver = class {
2193
+ constructor(registry) {
2194
+ this.registry = registry;
2195
+ }
2196
+ resolve(context) {
2197
+ const validators = [];
2198
+ const rules = this.registry.getRules();
2199
+ for (const rule of rules) {
2200
+ if (rule.shouldApply(context)) {
2201
+ const validator = this.registry.get(rule.validatorType);
2202
+ if (validator) {
2203
+ validators.push(validator);
2204
+ }
2205
+ }
2206
+ }
2207
+ return validators;
2208
+ }
2209
+ };
2210
+
2211
+ // src/core/validation/ValidationEngine.ts
2212
+ var ValidationEngine = class {
2213
+ constructor(resolver) {
2214
+ this.resolver = resolver;
2215
+ }
2216
+ validate(context) {
2217
+ const validators = this.resolver.resolve(context);
2218
+ const diagnostics = [];
2219
+ for (const validator of validators) {
2220
+ const diagnostic = validator.validate(context);
2221
+ if (diagnostic) {
2222
+ diagnostics.push(diagnostic);
2223
+ }
2224
+ }
2225
+ return diagnostics;
2226
+ }
2227
+ };
2228
+
2229
+ // src/core/validation/rules/SchemaBasedRule.ts
2230
+ var SchemaPropertyRule = class {
2231
+ constructor(validatorType, propertyName) {
2232
+ this.validatorType = validatorType;
2233
+ this.propertyName = propertyName;
2234
+ }
2235
+ shouldApply(context) {
2236
+ const value = context.schema[this.propertyName];
2237
+ return value !== void 0 && value !== null;
2238
+ }
2239
+ };
2240
+ var SchemaTruthyRule = class {
2241
+ constructor(validatorType, propertyName) {
2242
+ this.validatorType = validatorType;
2243
+ this.propertyName = propertyName;
2244
+ }
2245
+ shouldApply(context) {
2246
+ return context.schema[this.propertyName] === true;
2247
+ }
2248
+ };
2249
+ var CompositeRule = class {
2250
+ constructor(validatorType, conditions) {
2251
+ this.validatorType = validatorType;
2252
+ this.conditions = conditions;
2253
+ }
2254
+ shouldApply(context) {
2255
+ return this.conditions.every((condition) => condition(context));
2256
+ }
2257
+ };
2258
+
2259
+ // src/core/validation/validators/RequiredValidator.ts
2260
+ var RequiredValidator = class {
2261
+ type = "required";
2262
+ validate(context) {
2263
+ const { value, nodeName } = context;
2264
+ if (value === "" || value === null || value === void 0) {
2265
+ return {
2266
+ severity: "error",
2267
+ type: this.type,
2268
+ message: "Field is required",
2269
+ path: nodeName
2270
+ };
2271
+ }
2272
+ return null;
2273
+ }
2274
+ };
2275
+
2276
+ // src/core/validation/validators/PatternValidator.ts
2277
+ var PatternValidator = class {
2278
+ type = "pattern";
2279
+ validate(context) {
2280
+ const { value, schema, nodeName } = context;
2281
+ const pattern = schema.pattern;
2282
+ if (!pattern) {
2283
+ return null;
2284
+ }
2285
+ if (typeof value !== "string") {
2286
+ return null;
2287
+ }
2288
+ if (value.length === 0) {
2289
+ return null;
2290
+ }
2291
+ try {
2292
+ if (!new RegExp(pattern).test(value)) {
2293
+ return {
2294
+ severity: "error",
2295
+ type: this.type,
2296
+ message: "Value does not match pattern",
2297
+ path: nodeName,
2298
+ params: { pattern }
2299
+ };
2300
+ }
2301
+ } catch {
2302
+ return {
2303
+ severity: "error",
2304
+ type: "invalidPattern",
2305
+ message: "Invalid regex pattern in schema",
2306
+ path: nodeName,
2307
+ params: { pattern }
2308
+ };
2309
+ }
2310
+ return null;
2311
+ }
2312
+ };
2313
+
2314
+ // src/core/validation/validators/MinLengthValidator.ts
2315
+ var MinLengthValidator = class {
2316
+ type = "minLength";
2317
+ validate(context) {
2318
+ const { value, schema, nodeName } = context;
2319
+ const minLength = schema.minLength;
2320
+ if (minLength === void 0) {
2321
+ return null;
2322
+ }
2323
+ if (typeof value !== "string") {
2324
+ return null;
2325
+ }
2326
+ if (value.length < minLength) {
2327
+ return {
2328
+ severity: "error",
2329
+ type: this.type,
2330
+ message: `Value must be at least ${minLength} characters`,
2331
+ path: nodeName,
2332
+ params: { min: minLength, actual: value.length }
2333
+ };
2334
+ }
2335
+ return null;
2336
+ }
2337
+ };
2338
+
2339
+ // src/core/validation/validators/MaxLengthValidator.ts
2340
+ var MaxLengthValidator = class {
2341
+ type = "maxLength";
2342
+ validate(context) {
2343
+ const { value, schema, nodeName } = context;
2344
+ const maxLength = schema.maxLength;
2345
+ if (maxLength === void 0) {
2346
+ return null;
2347
+ }
2348
+ if (typeof value !== "string") {
2349
+ return null;
2350
+ }
2351
+ if (value.length > maxLength) {
2352
+ return {
2353
+ severity: "error",
2354
+ type: this.type,
2355
+ message: `Value must be at most ${maxLength} characters`,
2356
+ path: nodeName,
2357
+ params: { max: maxLength, actual: value.length }
2358
+ };
2359
+ }
2360
+ return null;
2361
+ }
2362
+ };
2363
+
2364
+ // src/core/validation/validators/MinimumValidator.ts
2365
+ var MinimumValidator = class {
2366
+ type = "minimum";
2367
+ validate(context) {
2368
+ const { value, schema, nodeName } = context;
2369
+ const minimum = schema.minimum;
2370
+ if (minimum === void 0) {
2371
+ return null;
2372
+ }
2373
+ if (typeof value !== "number") {
2374
+ return null;
2375
+ }
2376
+ if (value < minimum) {
2377
+ return {
2378
+ severity: "error",
2379
+ type: this.type,
2380
+ message: `Value must be at least ${minimum}`,
2381
+ path: nodeName,
2382
+ params: { min: minimum, actual: value }
2383
+ };
2384
+ }
2385
+ return null;
2386
+ }
2387
+ };
2388
+
2389
+ // src/core/validation/validators/MaximumValidator.ts
2390
+ var MaximumValidator = class {
2391
+ type = "maximum";
2392
+ validate(context) {
2393
+ const { value, schema, nodeName } = context;
2394
+ const maximum = schema.maximum;
2395
+ if (maximum === void 0) {
2396
+ return null;
2397
+ }
2398
+ if (typeof value !== "number") {
2399
+ return null;
2400
+ }
2401
+ if (value > maximum) {
2402
+ return {
2403
+ severity: "error",
2404
+ type: this.type,
2405
+ message: `Value must be at most ${maximum}`,
2406
+ path: nodeName,
2407
+ params: { max: maximum, actual: value }
2408
+ };
2409
+ }
2410
+ return null;
2411
+ }
2412
+ };
2413
+
2414
+ // src/core/validation/validators/EnumValidator.ts
2415
+ var EnumValidator = class {
2416
+ type = "enum";
2417
+ validate(context) {
2418
+ const { value, schema, nodeName } = context;
2419
+ const enumValues = schema.enum;
2420
+ if (!enumValues || enumValues.length === 0) {
2421
+ return null;
2422
+ }
2423
+ if (!enumValues.includes(value)) {
2424
+ return {
2425
+ severity: "error",
2426
+ type: this.type,
2427
+ message: "Value must be one of the allowed values",
2428
+ path: nodeName,
2429
+ params: { allowed: [...enumValues], actual: value }
2430
+ };
2431
+ }
2432
+ return null;
2433
+ }
2434
+ };
2435
+
2436
+ // src/core/validation/validators/ForeignKeyValidator.ts
2437
+ var ForeignKeyValidator = class {
2438
+ type = "foreignKey";
2439
+ validate(context) {
2440
+ const { value, schema, nodeName } = context;
2441
+ const foreignKey = schema.foreignKey;
2442
+ if (!foreignKey) {
2443
+ return null;
2444
+ }
2445
+ if (value === "" || value === null || value === void 0) {
2446
+ return {
2447
+ severity: "error",
2448
+ type: this.type,
2449
+ message: "Foreign key reference is required",
2450
+ path: nodeName,
2451
+ params: { table: foreignKey }
2452
+ };
2453
+ }
2454
+ return null;
2455
+ }
2456
+ };
2457
+
2458
+ // src/core/validation/createValidationEngine.ts
2459
+ function createDefaultValidatorRegistry() {
2460
+ const registry = new ValidatorRegistry();
2461
+ registry.register("required", () => new RequiredValidator()).register("pattern", () => new PatternValidator()).register("minLength", () => new MinLengthValidator()).register("maxLength", () => new MaxLengthValidator()).register("minimum", () => new MinimumValidator()).register("maximum", () => new MaximumValidator()).register("enum", () => new EnumValidator()).register("foreignKey", () => new ForeignKeyValidator());
2462
+ registry.addRule(new SchemaTruthyRule("required", "required")).addRule(new SchemaPropertyRule("pattern", "pattern")).addRule(new SchemaPropertyRule("minLength", "minLength")).addRule(new SchemaPropertyRule("maxLength", "maxLength")).addRule(new SchemaPropertyRule("minimum", "minimum")).addRule(new SchemaPropertyRule("maximum", "maximum")).addRule(new SchemaPropertyRule("enum", "enum")).addRule(new SchemaPropertyRule("foreignKey", "foreignKey"));
2463
+ return registry;
2464
+ }
2465
+ function createValidationEngine(registry) {
2466
+ const validatorRegistry = registry ?? createDefaultValidatorRegistry();
2467
+ const resolver = new ValidatorResolver(validatorRegistry);
2468
+ return new ValidationEngine(resolver);
2469
+ }
2470
+
2471
+ // src/core/validation/schema/FieldNameValidator.ts
2472
+ var FIELD_NAME_PATTERN = /^(?!__)[a-zA-Z_][a-zA-Z0-9-_]*$/;
2473
+ var FIELD_NAME_MAX_LENGTH = 64;
2474
+ var FIELD_NAME_ERROR_MESSAGE = "Must start with a letter or underscore, cannot start with __, and can only include letters, numbers, hyphens, and underscores (max 64 chars)";
2475
+ function isValidFieldName(name) {
2476
+ if (name.length === 0 || name.length > FIELD_NAME_MAX_LENGTH) {
2477
+ return false;
2478
+ }
2479
+ return FIELD_NAME_PATTERN.test(name);
2480
+ }
2481
+
2482
+ // src/core/validation/schema/SchemaValidator.ts
2483
+ function validateSchema(root) {
2484
+ const errors = [];
2485
+ collectValidationErrors(root, errors);
2486
+ return errors;
2487
+ }
2488
+ function collectValidationErrors(node, errors) {
2489
+ if (node.isNull()) {
2490
+ return;
2491
+ }
2492
+ if (node.isObject()) {
2493
+ const children = node.properties();
2494
+ const nameSet = /* @__PURE__ */ new Set();
2495
+ for (const child of children) {
2496
+ const childName = child.name();
2497
+ if (childName === "") {
2498
+ errors.push(createError(child.id(), "empty-name", "Field name cannot be empty"));
2499
+ } else if (nameSet.has(childName)) {
2500
+ errors.push(
2501
+ createError(child.id(), "duplicate-name", `Duplicate field name: ${childName}`)
2502
+ );
2503
+ } else if (!isValidFieldName(childName)) {
2504
+ errors.push(createError(child.id(), "invalid-name", FIELD_NAME_ERROR_MESSAGE));
2505
+ }
2506
+ nameSet.add(childName);
2507
+ collectValidationErrors(child, errors);
2508
+ }
2509
+ } else if (node.isArray()) {
2510
+ collectValidationErrors(node.items(), errors);
2511
+ }
2512
+ }
2513
+ function createError(nodeId, type, message) {
2514
+ return { nodeId, type, message };
2515
+ }
2516
+
2517
+ // src/core/validation/formula/FormulaValidator.ts
2518
+ function validateFormulas(tree) {
2519
+ const errors = [];
2520
+ collectFormulaErrors(tree.root(), tree, errors, "");
2521
+ return errors;
2522
+ }
2523
+ function collectFormulaErrors(node, tree, errors, fieldPath) {
2524
+ if (node.isNull()) {
2525
+ return;
2526
+ }
2527
+ validateNodeFormula(node, tree, errors, fieldPath);
2528
+ collectChildErrors(node, tree, errors, fieldPath);
2529
+ }
2530
+ function validateNodeFormula(node, tree, errors, fieldPath) {
2531
+ if (!node.isPrimitive() || !node.hasFormula()) {
2532
+ return;
2533
+ }
2534
+ const formula = node.formula();
2535
+ if (!formula) {
2536
+ return;
2537
+ }
2538
+ for (const dep of formula.dependencies()) {
2539
+ const targetNode = tree.nodeById(dep.targetNodeId());
2540
+ if (targetNode.isNull()) {
2541
+ errors.push({
2542
+ nodeId: node.id(),
2543
+ message: "Cannot resolve formula dependency: target node not found",
2544
+ fieldPath: fieldPath || node.name()
2545
+ });
2546
+ }
2547
+ }
2548
+ }
2549
+ function collectChildErrors(node, tree, errors, fieldPath) {
2550
+ if (node.isObject()) {
2551
+ for (const child of node.properties()) {
2552
+ const childPath = buildChildPath(fieldPath, child.name());
2553
+ collectFormulaErrors(child, tree, errors, childPath);
2554
+ }
2555
+ } else if (node.isArray()) {
2556
+ const itemsPath = buildArrayItemsPath(fieldPath);
2557
+ collectFormulaErrors(node.items(), tree, errors, itemsPath);
2558
+ }
2559
+ }
2560
+ function buildChildPath(parentPath, childName) {
2561
+ return parentPath ? `${parentPath}.${childName}` : childName;
2562
+ }
2563
+ function buildArrayItemsPath(parentPath) {
2564
+ return parentPath ? `${parentPath}[*]` : "[*]";
2565
+ }
2566
+
2567
+ export { AbstractBasePath, ChangeCoalescer, ChangeCollector, CompositeRule, EMPTY_METADATA, EMPTY_PATH, EnumValidator, FIELD_NAME_ERROR_MESSAGE, ForeignKeyValidator, FormulaDependencyIndex, ItemsSegment, MaxLengthValidator, MaximumValidator, MinLengthValidator, MinimumValidator, NULL_NODE, NodePathIndex, ParsedFormula, PatchBuilder, PatchEnricher, PatchGenerator, PatternValidator, PropertySegment, RequiredValidator, SchemaDiff, SchemaPropertyRule, SchemaSerializer, SchemaTruthyRule, ValidationEngine, ValidatorRegistry, ValidatorResolver, areNodesContentEqual, areNodesEqual, coalesceChanges, collectChanges, createArrayNode, createBooleanNode, createDefaultValidatorRegistry, createNumberNode, createObjectNode, createPath, createRefNode, createSchemaTree, createStringNode, createValidationEngine, isValidFieldName, jsonPointerToPath, jsonPointerToSegments, jsonPointerToSimplePath, validateFormulas, validateSchema };
2568
+ //# sourceMappingURL=chunk-S7GEDTSL.js.map
2569
+ //# sourceMappingURL=chunk-S7GEDTSL.js.map