@revisium/schema-toolkit 0.19.4 → 0.20.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.
Files changed (29) hide show
  1. package/dist/{FormulaPathBuilder--okalwdl.d.cts → FormulaPathBuilder-D9LkBcSv.d.cts} +1 -1
  2. package/dist/{FormulaPathBuilder-rW6PJd-r.d.ts → FormulaPathBuilder-DwlKOvVB.d.ts} +1 -1
  3. package/dist/{chunk-O4QRUV7I.cjs → chunk-FZ2MS4Q5.cjs} +848 -73
  4. package/dist/chunk-FZ2MS4Q5.cjs.map +1 -0
  5. package/dist/{chunk-5BBFBSGI.js → chunk-U5YDA3LA.js} +850 -76
  6. package/dist/chunk-U5YDA3LA.js.map +1 -0
  7. package/dist/core/index.d.cts +2 -2
  8. package/dist/core/index.d.ts +2 -2
  9. package/dist/index.cjs +46 -42
  10. package/dist/index.d.cts +5 -5
  11. package/dist/index.d.ts +5 -5
  12. package/dist/index.js +1 -1
  13. package/dist/{json-string.store-DRmQFKrp.d.ts → json-string.store-C58zbl31.d.ts} +1 -1
  14. package/dist/{json-string.store-DOZn38CI.d.cts → json-string.store-DuI--IL2.d.cts} +1 -1
  15. package/dist/{json-value-patch.types-U2y_ugm1.d.ts → json-value-patch.types-CgiEpchp.d.ts} +1 -1
  16. package/dist/{json-value-patch.types-CjBpq5VF.d.cts → json-value-patch.types-LEJxcIWR.d.cts} +1 -1
  17. package/dist/{json.types-46Cq-WxU.d.cts → json.types-PrMmLhba.d.cts} +1 -1
  18. package/dist/{json.types-46Cq-WxU.d.ts → json.types-PrMmLhba.d.ts} +1 -1
  19. package/dist/lib/index.d.cts +2 -2
  20. package/dist/lib/index.d.ts +2 -2
  21. package/dist/model/index.cjs +46 -42
  22. package/dist/model/index.d.cts +130 -99
  23. package/dist/model/index.d.ts +130 -99
  24. package/dist/model/index.js +1 -1
  25. package/dist/types/index.d.cts +2 -2
  26. package/dist/types/index.d.ts +2 -2
  27. package/package.json +1 -1
  28. package/dist/chunk-5BBFBSGI.js.map +0 -1
  29. package/dist/chunk-O4QRUV7I.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
- import { AbstractBasePath, EMPTY_PATH, FormulaError, ResolvedDependency, FormulaSerializer, createRefNode, createObjectNode, createArrayNode, createStringNode, createNumberNode, createBooleanNode, makeAutoObservable, observable, makeObservable, runInAction, PatchBuilder, SchemaSerializer, createSchemaTree, NULL_NODE, validateSchema, validateFormulas } from './chunk-3JZKQWQH.js';
1
+ import { AbstractBasePath, EMPTY_PATH, FormulaError, ResolvedDependency, FormulaSerializer, createRefNode, createObjectNode, createArrayNode, createStringNode, createNumberNode, createBooleanNode, observable, makeObservable, makeAutoObservable, runInAction, PatchBuilder, SchemaSerializer, createSchemaTree, NULL_NODE, validateSchema, validateFormulas, reaction } from './chunk-3JZKQWQH.js';
2
2
  import { obj, ref } from './chunk-R4CFU33U.js';
3
3
  import { nanoid } from 'nanoid';
4
- import { parseFormula } from '@revisium/formula';
4
+ import { parseFormula, parseExpression, evaluateWithContext } from '@revisium/formula';
5
5
 
6
6
  var ARRAY_NOTATION_REGEX = /^([^[]+)\[(?:\d+|\*)?\]$/;
7
7
  var FormulaPath = class {
@@ -1077,87 +1077,525 @@ var SchemaModelImpl = class {
1077
1077
  function createSchemaModel(schema, options) {
1078
1078
  return new SchemaModelImpl(schema, options);
1079
1079
  }
1080
-
1081
- // src/model/table/row/RowModelImpl.ts
1082
- var UNSET_INDEX = -1;
1083
- var RowModelImpl = class {
1084
- constructor(_rowId, _tree) {
1085
- this._rowId = _rowId;
1086
- this._tree = _tree;
1087
- makeAutoObservable(this, {
1088
- _rowId: false,
1089
- _tree: false,
1090
- _tableModel: "observable.ref"
1080
+ var INDEX_REGEX = /^\[(-?\d+)\]/;
1081
+ var PROP_REGEX = /^\.?([a-zA-Z_]\w*)/;
1082
+ var FormulaCollector = class {
1083
+ root = null;
1084
+ collect(root) {
1085
+ this.root = root;
1086
+ const formulas = [];
1087
+ this.traverse(root, { parent: null, arrayLevels: [] }, formulas);
1088
+ return formulas;
1089
+ }
1090
+ traverse(node, context, formulas) {
1091
+ if (node.isPrimitive()) {
1092
+ this.collectPrimitive(node, context, formulas);
1093
+ } else if (node.isObject()) {
1094
+ for (const child of node.children) {
1095
+ this.traverse(child, { ...context, parent: node }, formulas);
1096
+ }
1097
+ } else if (node.isArray()) {
1098
+ for (let i = 0; i < node.length; i++) {
1099
+ const item = node.at(i);
1100
+ if (item) {
1101
+ this.traverse(
1102
+ item,
1103
+ {
1104
+ ...context,
1105
+ arrayLevels: [...context.arrayLevels, { array: node, index: i }]
1106
+ },
1107
+ formulas
1108
+ );
1109
+ }
1110
+ }
1111
+ }
1112
+ }
1113
+ collectPrimitive(node, context, formulas) {
1114
+ const formula = node.formula;
1115
+ if (!formula) {
1116
+ return;
1117
+ }
1118
+ const rawDependencies = this.extractDependencies(formula.expression);
1119
+ const dependencyNodes = this.resolveDependencies(
1120
+ rawDependencies,
1121
+ node,
1122
+ context
1123
+ );
1124
+ formulas.push({
1125
+ node,
1126
+ expression: formula.expression,
1127
+ parent: context.parent,
1128
+ dependencyNodes,
1129
+ arrayLevels: context.arrayLevels
1091
1130
  });
1092
1131
  }
1093
- _tableModel = null;
1094
- get rowId() {
1095
- return this._rowId;
1132
+ extractDependencies(expression) {
1133
+ try {
1134
+ const parsed = parseExpression(expression);
1135
+ return parsed.dependencies;
1136
+ } catch {
1137
+ return [];
1138
+ }
1096
1139
  }
1097
- get tableModel() {
1098
- return this._tableModel;
1140
+ resolveDependencies(rawDeps, formulaNode, context) {
1141
+ const nodes = [];
1142
+ for (const dep of rawDeps) {
1143
+ const node = this.resolveOneDependency(dep, formulaNode, context);
1144
+ if (node) {
1145
+ this.collectPrimitiveNodes(node, nodes);
1146
+ }
1147
+ }
1148
+ return nodes;
1099
1149
  }
1100
- get tree() {
1101
- return this._tree;
1150
+ collectPrimitiveNodes(node, result) {
1151
+ if (node.isPrimitive()) {
1152
+ result.push(node);
1153
+ } else if (node.isArray()) {
1154
+ for (let i = 0; i < node.length; i++) {
1155
+ const item = node.at(i);
1156
+ if (item) {
1157
+ this.collectPrimitiveNodes(item, result);
1158
+ }
1159
+ }
1160
+ } else if (node.isObject()) {
1161
+ for (const child of node.children) {
1162
+ this.collectPrimitiveNodes(child, result);
1163
+ }
1164
+ }
1102
1165
  }
1103
- get index() {
1104
- if (!this._tableModel) {
1105
- return UNSET_INDEX;
1166
+ resolveOneDependency(dep, _formulaNode, context) {
1167
+ if (dep.startsWith("/")) {
1168
+ return this.resolveFromRoot(dep.slice(1));
1106
1169
  }
1107
- return this._tableModel.getRowIndex(this._rowId);
1170
+ if (dep.startsWith("../")) {
1171
+ return this.resolveRelative(dep, context);
1172
+ }
1173
+ if (context.parent) {
1174
+ return this.resolveFromNode(context.parent, dep);
1175
+ }
1176
+ return this.resolveFromRoot(dep);
1108
1177
  }
1109
- get prev() {
1110
- if (!this._tableModel) {
1178
+ resolveFromRoot(pathStr) {
1179
+ if (!this.root) {
1111
1180
  return null;
1112
1181
  }
1113
- const currentIndex = this.index;
1114
- if (currentIndex <= 0) {
1182
+ return this.resolveFromNode(this.root, pathStr);
1183
+ }
1184
+ resolveFromNode(startNode, pathStr) {
1185
+ const segments = this.parsePathSegments(pathStr);
1186
+ let current = startNode;
1187
+ for (const segment of segments) {
1188
+ if (!current) {
1189
+ return null;
1190
+ }
1191
+ if (segment.type === "property") {
1192
+ if (current.isObject()) {
1193
+ current = current.children.find((c) => c.name === segment.name) ?? null;
1194
+ } else {
1195
+ return null;
1196
+ }
1197
+ } else if (segment.type === "index") {
1198
+ if (current.isArray()) {
1199
+ current = current.at(segment.index) ?? null;
1200
+ } else {
1201
+ return null;
1202
+ }
1203
+ }
1204
+ }
1205
+ return current;
1206
+ }
1207
+ resolveRelative(dep, context) {
1208
+ let upLevels = 0;
1209
+ let remaining = dep;
1210
+ while (remaining.startsWith("../")) {
1211
+ upLevels++;
1212
+ remaining = remaining.slice(3);
1213
+ }
1214
+ let current = context.parent;
1215
+ for (let i = 0; i < upLevels && current; i++) {
1216
+ current = this.findParent(current);
1217
+ if (current?.isArray()) {
1218
+ current = this.findParent(current);
1219
+ }
1220
+ }
1221
+ if (!current) {
1115
1222
  return null;
1116
1223
  }
1117
- return this._tableModel.getRowAt(currentIndex - 1) ?? null;
1224
+ if (remaining) {
1225
+ return this.resolveFromNode(current, remaining);
1226
+ }
1227
+ return current;
1118
1228
  }
1119
- get next() {
1120
- if (!this._tableModel) {
1229
+ findParent(node) {
1230
+ if (!this.root || node === this.root) {
1121
1231
  return null;
1122
1232
  }
1123
- const currentIndex = this.index;
1124
- if (currentIndex === UNSET_INDEX || currentIndex >= this._tableModel.rowCount - 1) {
1233
+ return this.findParentRecursive(this.root, node);
1234
+ }
1235
+ findParentRecursive(current, target) {
1236
+ const children = this.getChildNodes(current);
1237
+ for (const child of children) {
1238
+ if (child === target) {
1239
+ return current;
1240
+ }
1241
+ const found = this.findParentRecursive(child, target);
1242
+ if (found) {
1243
+ return found;
1244
+ }
1245
+ }
1246
+ return null;
1247
+ }
1248
+ getChildNodes(node) {
1249
+ if (node.isObject()) {
1250
+ return [...node.children];
1251
+ }
1252
+ if (node.isArray()) {
1253
+ const items = [];
1254
+ for (let i = 0; i < node.length; i++) {
1255
+ const item = node.at(i);
1256
+ if (item) {
1257
+ items.push(item);
1258
+ }
1259
+ }
1260
+ return items;
1261
+ }
1262
+ return [];
1263
+ }
1264
+ parsePathSegments(pathStr) {
1265
+ const segments = [];
1266
+ let current = pathStr;
1267
+ while (current.length > 0) {
1268
+ const indexMatch = INDEX_REGEX.exec(current);
1269
+ if (indexMatch?.[1]) {
1270
+ segments.push({
1271
+ type: "index",
1272
+ index: Number.parseInt(indexMatch[1], 10)
1273
+ });
1274
+ current = current.slice(indexMatch[0].length);
1275
+ continue;
1276
+ }
1277
+ const propMatch = PROP_REGEX.exec(current);
1278
+ if (propMatch?.[1]) {
1279
+ segments.push({ type: "property", name: propMatch[1] });
1280
+ current = current.slice(propMatch[0].length);
1281
+ continue;
1282
+ }
1283
+ break;
1284
+ }
1285
+ return segments;
1286
+ }
1287
+ };
1288
+
1289
+ // src/model/value-formula/DependencyGraph.ts
1290
+ var DependencyGraph = class {
1291
+ buildDependencyMap(formulas) {
1292
+ const map = /* @__PURE__ */ new Map();
1293
+ for (const field of formulas) {
1294
+ for (const depNode of field.dependencyNodes) {
1295
+ let depSet = map.get(depNode);
1296
+ if (!depSet) {
1297
+ depSet = /* @__PURE__ */ new Set();
1298
+ map.set(depNode, depSet);
1299
+ }
1300
+ depSet.add(field);
1301
+ }
1302
+ }
1303
+ return map;
1304
+ }
1305
+ buildEvaluationOrder(formulas) {
1306
+ const formulaNodes = new Set(formulas.map((f) => f.node));
1307
+ const visited = /* @__PURE__ */ new Set();
1308
+ const order = [];
1309
+ const formulaByNode = /* @__PURE__ */ new Map();
1310
+ for (const field of formulas) {
1311
+ formulaByNode.set(field.node, field);
1312
+ }
1313
+ const visit = (field, stack) => {
1314
+ if (visited.has(field.node)) {
1315
+ return;
1316
+ }
1317
+ if (stack.has(field.node)) {
1318
+ return;
1319
+ }
1320
+ stack.add(field.node);
1321
+ for (const depNode of field.dependencyNodes) {
1322
+ if (formulaNodes.has(depNode)) {
1323
+ const depFormula = formulaByNode.get(depNode);
1324
+ if (depFormula) {
1325
+ visit(depFormula, stack);
1326
+ }
1327
+ }
1328
+ }
1329
+ stack.delete(field.node);
1330
+ visited.add(field.node);
1331
+ order.push(field);
1332
+ };
1333
+ for (const field of formulas) {
1334
+ visit(field, /* @__PURE__ */ new Set());
1335
+ }
1336
+ return order;
1337
+ }
1338
+ getAffectedFormulas(changedNode, dependencyMap, evaluationOrder) {
1339
+ const affected = /* @__PURE__ */ new Set();
1340
+ const queue = [changedNode];
1341
+ while (queue.length > 0) {
1342
+ const current = queue.shift();
1343
+ if (!current) {
1344
+ continue;
1345
+ }
1346
+ const dependents = dependencyMap.get(current);
1347
+ if (dependents) {
1348
+ for (const field of dependents) {
1349
+ if (!affected.has(field)) {
1350
+ affected.add(field);
1351
+ queue.push(field.node);
1352
+ }
1353
+ }
1354
+ }
1355
+ }
1356
+ return evaluationOrder.filter((field) => affected.has(field));
1357
+ }
1358
+ };
1359
+ var FormulaEvaluator = class {
1360
+ constructor(tree, options = {}) {
1361
+ this.tree = tree;
1362
+ this.options = options;
1363
+ }
1364
+ evaluate(field) {
1365
+ try {
1366
+ const context = this.buildContext(field);
1367
+ const result = evaluateWithContext(field.expression, context);
1368
+ this.setNodeValue(field, result);
1369
+ this.checkForWarnings(field, result);
1370
+ } catch (error) {
1371
+ this.handleError(field, error);
1372
+ }
1373
+ }
1374
+ evaluateAll(fields) {
1375
+ for (const field of fields) {
1376
+ this.evaluate(field);
1377
+ }
1378
+ }
1379
+ buildContext(field) {
1380
+ const rootData = this.tree.getPlainValue();
1381
+ const context = { rootData };
1382
+ if (field.parent) {
1383
+ const itemData = this.getPlainObjectValue(field.parent);
1384
+ if (itemData) {
1385
+ context.itemData = itemData;
1386
+ }
1387
+ }
1388
+ if (field.arrayLevels.length > 0) {
1389
+ context.arrayContext = { levels: this.buildArrayLevels(field) };
1390
+ }
1391
+ return context;
1392
+ }
1393
+ buildArrayLevels(field) {
1394
+ const levels = [];
1395
+ for (let i = field.arrayLevels.length - 1; i >= 0; i--) {
1396
+ const level = field.arrayLevels[i];
1397
+ if (!level) {
1398
+ continue;
1399
+ }
1400
+ const { array, index } = level;
1401
+ levels.push({
1402
+ index,
1403
+ length: array.length,
1404
+ prev: this.getPrevItemValue(array, index),
1405
+ next: this.getNextItemValue(array, index)
1406
+ });
1407
+ }
1408
+ return levels;
1409
+ }
1410
+ getPrevItemValue(array, index) {
1411
+ if (index <= 0) {
1125
1412
  return null;
1126
1413
  }
1127
- return this._tableModel.getRowAt(currentIndex + 1) ?? null;
1414
+ const prevItem = array.at(index - 1);
1415
+ return prevItem ? this.getPlainNodeValue(prevItem) : null;
1128
1416
  }
1129
- get(path) {
1130
- return this._tree.get(path);
1417
+ getNextItemValue(array, index) {
1418
+ if (index >= array.length - 1) {
1419
+ return null;
1420
+ }
1421
+ const nextItem = array.at(index + 1);
1422
+ return nextItem ? this.getPlainNodeValue(nextItem) : null;
1131
1423
  }
1132
- getValue(path) {
1133
- return this._tree.getValue(path);
1424
+ getPlainObjectValue(node) {
1425
+ const result = {};
1426
+ for (const child of node.children) {
1427
+ result[child.name] = this.getPlainNodeValue(child);
1428
+ }
1429
+ return result;
1134
1430
  }
1135
- setValue(path, value) {
1136
- this._tree.setValue(path, value);
1431
+ getPlainNodeValue(node) {
1432
+ if (node.isPrimitive()) {
1433
+ return node.value;
1434
+ }
1435
+ if (node.isObject()) {
1436
+ return this.getPlainObjectValue(node);
1437
+ }
1438
+ if (node.isArray()) {
1439
+ const result = [];
1440
+ for (let i = 0; i < node.length; i++) {
1441
+ const item = node.at(i);
1442
+ if (item) {
1443
+ result.push(this.getPlainNodeValue(item));
1444
+ }
1445
+ }
1446
+ return result;
1447
+ }
1448
+ return null;
1137
1449
  }
1138
- getPlainValue() {
1139
- return this._tree.getPlainValue();
1450
+ setNodeValue(field, result) {
1451
+ const { node } = field;
1452
+ const defaultValue = node.defaultValue;
1453
+ if (result === null || result === void 0) {
1454
+ node.setValue(defaultValue, { internal: true });
1455
+ return;
1456
+ }
1457
+ if (typeof result === "number" && (Number.isNaN(result) || !Number.isFinite(result))) {
1458
+ node.setValue(defaultValue, { internal: true });
1459
+ return;
1460
+ }
1461
+ node.setValue(result, { internal: true });
1462
+ }
1463
+ checkForWarnings(field, result) {
1464
+ const { node } = field;
1465
+ if (typeof result === "number") {
1466
+ if (Number.isNaN(result)) {
1467
+ node.setFormulaWarning({
1468
+ type: "nan",
1469
+ message: "Formula result is NaN",
1470
+ expression: field.expression,
1471
+ computedValue: result
1472
+ });
1473
+ return;
1474
+ }
1475
+ if (!Number.isFinite(result)) {
1476
+ node.setFormulaWarning({
1477
+ type: "infinity",
1478
+ message: "Formula result is Infinity",
1479
+ expression: field.expression,
1480
+ computedValue: result
1481
+ });
1482
+ return;
1483
+ }
1484
+ }
1485
+ node.setFormulaWarning(null);
1140
1486
  }
1141
- get isDirty() {
1142
- return this._tree.isDirty;
1487
+ handleError(field, error) {
1488
+ const { node } = field;
1489
+ node.setValue(node.defaultValue, { internal: true });
1490
+ node.setFormulaWarning({
1491
+ type: "runtime-error",
1492
+ message: error.message,
1493
+ expression: field.expression,
1494
+ computedValue: void 0
1495
+ });
1496
+ if (this.options.onError) {
1497
+ this.options.onError(node, error);
1498
+ }
1143
1499
  }
1144
- get isValid() {
1145
- return this._tree.isValid;
1500
+ };
1501
+
1502
+ // src/model/value-formula/FormulaEngine.ts
1503
+ var FormulaEngine = class {
1504
+ constructor(tree, options = {}) {
1505
+ this.tree = tree;
1506
+ this.collector = new FormulaCollector();
1507
+ this.graph = new DependencyGraph();
1508
+ this.evaluator = new FormulaEvaluator(tree, options);
1509
+ this.initialize();
1510
+ }
1511
+ collector;
1512
+ graph;
1513
+ evaluator;
1514
+ formulas = [];
1515
+ dependencyMap = /* @__PURE__ */ new Map();
1516
+ evaluationOrder = [];
1517
+ disposers = [];
1518
+ reinitialize() {
1519
+ this.disposeReactions();
1520
+ this.initialize();
1146
1521
  }
1147
- get errors() {
1148
- return this._tree.errors;
1522
+ dispose() {
1523
+ this.disposeReactions();
1524
+ this.formulas = [];
1525
+ this.dependencyMap = /* @__PURE__ */ new Map();
1526
+ this.evaluationOrder = [];
1149
1527
  }
1150
- getPatches() {
1151
- return this._tree.getPatches();
1528
+ getFormulas() {
1529
+ return this.formulas;
1152
1530
  }
1153
- commit() {
1154
- this._tree.commit();
1531
+ getEvaluationOrder() {
1532
+ return this.evaluationOrder;
1155
1533
  }
1156
- revert() {
1157
- this._tree.revert();
1534
+ initialize() {
1535
+ this.formulas = this.collector.collect(this.tree.root);
1536
+ this.dependencyMap = this.graph.buildDependencyMap(this.formulas);
1537
+ this.evaluationOrder = this.graph.buildEvaluationOrder(this.formulas);
1538
+ this.evaluateAll();
1539
+ this.setupReactions();
1158
1540
  }
1159
- setTableModel(tableModel) {
1160
- this._tableModel = tableModel;
1541
+ evaluateAll() {
1542
+ runInAction(() => {
1543
+ for (const field of this.evaluationOrder) {
1544
+ this.evaluator.evaluate(field);
1545
+ }
1546
+ });
1547
+ }
1548
+ setupReactions() {
1549
+ const watchedNodes = /* @__PURE__ */ new Set();
1550
+ for (const [depNode] of this.dependencyMap) {
1551
+ if (watchedNodes.has(depNode)) {
1552
+ continue;
1553
+ }
1554
+ watchedNodes.add(depNode);
1555
+ const disposer = reaction(
1556
+ () => depNode.value,
1557
+ () => this.handleDependencyChange(depNode)
1558
+ );
1559
+ this.disposers.push(disposer);
1560
+ }
1561
+ this.setupArrayReactions(this.tree.root);
1562
+ }
1563
+ setupArrayReactions(node) {
1564
+ if (node.isArray()) {
1565
+ const disposer = reaction(
1566
+ () => node.length,
1567
+ () => this.handleStructureChange()
1568
+ );
1569
+ this.disposers.push(disposer);
1570
+ for (const item of node.value) {
1571
+ this.setupArrayReactions(item);
1572
+ }
1573
+ } else if (node.isObject()) {
1574
+ for (const child of node.children) {
1575
+ this.setupArrayReactions(child);
1576
+ }
1577
+ }
1578
+ }
1579
+ handleDependencyChange(changedNode) {
1580
+ const affected = this.graph.getAffectedFormulas(
1581
+ changedNode,
1582
+ this.dependencyMap,
1583
+ this.evaluationOrder
1584
+ );
1585
+ runInAction(() => {
1586
+ for (const field of affected) {
1587
+ this.evaluator.evaluate(field);
1588
+ }
1589
+ });
1590
+ }
1591
+ handleStructureChange() {
1592
+ this.reinitialize();
1593
+ }
1594
+ disposeReactions() {
1595
+ for (const disposer of this.disposers) {
1596
+ disposer();
1597
+ }
1598
+ this.disposers = [];
1161
1599
  }
1162
1600
  };
1163
1601
 
@@ -2105,6 +2543,20 @@ var ValuePathImpl = class _ValuePathImpl extends AbstractBasePath {
2105
2543
  }
2106
2544
  return parts.join("");
2107
2545
  }
2546
+ asJsonPointer() {
2547
+ if (this.segs.length === 0) {
2548
+ return "";
2549
+ }
2550
+ const parts = [];
2551
+ for (const seg of this.segs) {
2552
+ if (seg.isProperty()) {
2553
+ parts.push(seg.propertyName());
2554
+ } else if (seg.isIndex()) {
2555
+ parts.push(String(seg.indexValue()));
2556
+ }
2557
+ }
2558
+ return "/" + parts.join("/");
2559
+ }
2108
2560
  parent() {
2109
2561
  if (this.segs.length <= 1) {
2110
2562
  return EMPTY_VALUE_PATH;
@@ -2119,6 +2571,9 @@ var ValuePathImpl = class _ValuePathImpl extends AbstractBasePath {
2119
2571
  }
2120
2572
  };
2121
2573
  var EMPTY_VALUE_PATH = new ValuePathImpl([]);
2574
+ function createValuePath(segments) {
2575
+ return segments.length === 0 ? EMPTY_VALUE_PATH : new ValuePathImpl(segments);
2576
+ }
2122
2577
 
2123
2578
  // src/core/value-path/ValuePathParser.ts
2124
2579
  function parseIndexSegment(path, startIndex) {
@@ -2174,17 +2629,194 @@ function parseValuePath(path) {
2174
2629
  return segments;
2175
2630
  }
2176
2631
 
2632
+ // src/model/value-tree/ChangeTracker.ts
2633
+ var ChangeTracker = class {
2634
+ _changes = [];
2635
+ get changes() {
2636
+ return this._changes;
2637
+ }
2638
+ get hasChanges() {
2639
+ return this._changes.length > 0;
2640
+ }
2641
+ track(change) {
2642
+ this._changes.push(change);
2643
+ }
2644
+ clear() {
2645
+ this._changes = [];
2646
+ }
2647
+ toPatches() {
2648
+ const patches = [];
2649
+ for (const change of this._changes) {
2650
+ const converted = this.changeToPatch(change);
2651
+ patches.push(...converted);
2652
+ }
2653
+ return patches;
2654
+ }
2655
+ changeToPatch(change) {
2656
+ const path = change.path.asJsonPointer();
2657
+ switch (change.type) {
2658
+ case "setValue":
2659
+ return [{ op: "replace", path, value: change.value }];
2660
+ case "addProperty":
2661
+ return [{ op: "add", path, value: change.value }];
2662
+ case "removeProperty":
2663
+ return [{ op: "remove", path }];
2664
+ case "arrayPush":
2665
+ return [{ op: "add", path: `${path}/-`, value: change.value }];
2666
+ case "arrayInsert":
2667
+ return [
2668
+ { op: "add", path: `${path}/${change.index}`, value: change.value }
2669
+ ];
2670
+ case "arrayRemove":
2671
+ return [{ op: "remove", path: `${path}/${change.index}` }];
2672
+ case "arrayMove":
2673
+ return [
2674
+ {
2675
+ op: "move",
2676
+ from: `${path}/${change.fromIndex}`,
2677
+ path: `${path}/${change.toIndex}`
2678
+ }
2679
+ ];
2680
+ case "arrayReplace":
2681
+ return [
2682
+ {
2683
+ op: "replace",
2684
+ path: `${path}/${change.index}`,
2685
+ value: change.value
2686
+ }
2687
+ ];
2688
+ case "arrayClear":
2689
+ return [{ op: "replace", path, value: [] }];
2690
+ }
2691
+ }
2692
+ };
2693
+
2694
+ // src/model/value-tree/TreeIndex.ts
2695
+ var TreeIndex = class {
2696
+ constructor(root) {
2697
+ this.root = root;
2698
+ this.rebuild();
2699
+ }
2700
+ nodesById = /* @__PURE__ */ new Map();
2701
+ pathCache = /* @__PURE__ */ new Map();
2702
+ nodeById(id) {
2703
+ const node = this.nodesById.get(id);
2704
+ if (node) {
2705
+ return node;
2706
+ }
2707
+ this.rebuild();
2708
+ return this.nodesById.get(id);
2709
+ }
2710
+ pathOf(node) {
2711
+ if (this.isInsideArray(node)) {
2712
+ return this.computePath(node);
2713
+ }
2714
+ const cached = this.pathCache.get(node.id);
2715
+ if (cached) {
2716
+ return cached;
2717
+ }
2718
+ const path = this.computePath(node);
2719
+ this.pathCache.set(node.id, path);
2720
+ return path;
2721
+ }
2722
+ rebuild() {
2723
+ this.nodesById.clear();
2724
+ this.pathCache.clear();
2725
+ this.indexNode(this.root);
2726
+ }
2727
+ registerNode(node) {
2728
+ this.indexNode(node);
2729
+ }
2730
+ invalidatePathsUnder(node) {
2731
+ this.pathCache.delete(node.id);
2732
+ this.invalidateChildPaths(node);
2733
+ }
2734
+ isInsideArray(node) {
2735
+ let current = node.parent;
2736
+ while (current) {
2737
+ if (current.isArray()) {
2738
+ return true;
2739
+ }
2740
+ current = current.parent;
2741
+ }
2742
+ return false;
2743
+ }
2744
+ indexNode(node) {
2745
+ this.nodesById.set(node.id, node);
2746
+ if (node.isObject()) {
2747
+ for (const child of node.children) {
2748
+ this.indexNode(child);
2749
+ }
2750
+ } else if (node.isArray()) {
2751
+ for (const item of node.value) {
2752
+ this.indexNode(item);
2753
+ }
2754
+ }
2755
+ }
2756
+ computePath(node) {
2757
+ const segments = [];
2758
+ let current = node;
2759
+ while (current?.parent) {
2760
+ const parent = current.parent;
2761
+ if (parent.isObject()) {
2762
+ segments.unshift(new PropertySegment(current.name));
2763
+ } else if (parent.isArray()) {
2764
+ const index = parent.value.indexOf(current);
2765
+ if (index >= 0) {
2766
+ segments.unshift(new IndexSegment(index));
2767
+ }
2768
+ }
2769
+ current = parent;
2770
+ }
2771
+ if (segments.length === 0) {
2772
+ return EMPTY_VALUE_PATH;
2773
+ }
2774
+ return createValuePath(segments);
2775
+ }
2776
+ invalidateChildPaths(node) {
2777
+ if (node.isObject()) {
2778
+ for (const child of node.children) {
2779
+ this.pathCache.delete(child.id);
2780
+ this.invalidateChildPaths(child);
2781
+ }
2782
+ } else if (node.isArray()) {
2783
+ for (const item of node.value) {
2784
+ this.pathCache.delete(item.id);
2785
+ this.invalidateChildPaths(item);
2786
+ }
2787
+ }
2788
+ }
2789
+ };
2790
+
2177
2791
  // src/model/value-tree/ValueTree.ts
2178
2792
  var ValueTree = class {
2179
2793
  constructor(_root) {
2180
2794
  this._root = _root;
2795
+ this.index = new TreeIndex(_root);
2796
+ this.changeTracker = new ChangeTracker();
2181
2797
  makeAutoObservable(this, {
2182
- _root: false
2798
+ _root: false,
2799
+ index: false,
2800
+ changeTracker: false,
2801
+ _formulaEngine: false
2183
2802
  });
2184
2803
  }
2804
+ index;
2805
+ changeTracker;
2806
+ _formulaEngine = null;
2185
2807
  get root() {
2186
2808
  return this._root;
2187
2809
  }
2810
+ nodeById(id) {
2811
+ return this.index.nodeById(id);
2812
+ }
2813
+ pathOf(nodeOrId) {
2814
+ const node = typeof nodeOrId === "string" ? this.index.nodeById(nodeOrId) : nodeOrId;
2815
+ if (!node) {
2816
+ return EMPTY_VALUE_PATH;
2817
+ }
2818
+ return this.index.pathOf(node);
2819
+ }
2188
2820
  get(path) {
2189
2821
  const segments = parseValuePath(path);
2190
2822
  if (segments.length === 0) {
@@ -2217,7 +2849,14 @@ var ValueTree = class {
2217
2849
  if (!node.isPrimitive()) {
2218
2850
  throw new Error(`Cannot set value on non-primitive node: ${path}`);
2219
2851
  }
2852
+ const oldValue = node.value;
2220
2853
  node.setValue(value);
2854
+ this.changeTracker.track({
2855
+ type: "setValue",
2856
+ path: this.index.pathOf(node),
2857
+ value,
2858
+ oldValue
2859
+ });
2221
2860
  }
2222
2861
  getPlainValue() {
2223
2862
  return this._root.getPlainValue();
@@ -2236,22 +2875,147 @@ var ValueTree = class {
2236
2875
  return this._root.errors;
2237
2876
  }
2238
2877
  getPatches() {
2239
- return [];
2878
+ return this.changeTracker.toPatches();
2879
+ }
2880
+ trackChange(change) {
2881
+ this.changeTracker.track(change);
2240
2882
  }
2241
2883
  commit() {
2242
2884
  const root = this._root;
2243
2885
  if ("commit" in root && typeof root.commit === "function") {
2244
2886
  root.commit();
2245
2887
  }
2888
+ this.changeTracker.clear();
2246
2889
  }
2247
2890
  revert() {
2248
2891
  const root = this._root;
2249
2892
  if ("revert" in root && typeof root.revert === "function") {
2250
2893
  root.revert();
2251
2894
  }
2895
+ this.changeTracker.clear();
2896
+ }
2897
+ rebuildIndex() {
2898
+ this.index.rebuild();
2899
+ }
2900
+ registerNode(node) {
2901
+ this.index.registerNode(node);
2902
+ }
2903
+ invalidatePathsUnder(node) {
2904
+ this.index.invalidatePathsUnder(node);
2905
+ }
2906
+ setFormulaEngine(engine) {
2907
+ this._formulaEngine = engine;
2908
+ }
2909
+ get formulaEngine() {
2910
+ return this._formulaEngine;
2911
+ }
2912
+ dispose() {
2913
+ this._formulaEngine?.dispose();
2914
+ this._formulaEngine = null;
2252
2915
  }
2253
2916
  };
2254
2917
 
2918
+ // src/model/table/row/RowModelImpl.ts
2919
+ var UNSET_INDEX = -1;
2920
+ var RowModelImpl = class {
2921
+ constructor(_rowId, _tree) {
2922
+ this._rowId = _rowId;
2923
+ this._tree = _tree;
2924
+ makeAutoObservable(this, {
2925
+ _rowId: false,
2926
+ _tree: false,
2927
+ _tableModel: "observable.ref"
2928
+ });
2929
+ }
2930
+ _tableModel = null;
2931
+ get rowId() {
2932
+ return this._rowId;
2933
+ }
2934
+ get tableModel() {
2935
+ return this._tableModel;
2936
+ }
2937
+ get tree() {
2938
+ return this._tree;
2939
+ }
2940
+ get index() {
2941
+ if (!this._tableModel) {
2942
+ return UNSET_INDEX;
2943
+ }
2944
+ return this._tableModel.getRowIndex(this._rowId);
2945
+ }
2946
+ get prev() {
2947
+ if (!this._tableModel) {
2948
+ return null;
2949
+ }
2950
+ const currentIndex = this.index;
2951
+ if (currentIndex <= 0) {
2952
+ return null;
2953
+ }
2954
+ return this._tableModel.getRowAt(currentIndex - 1) ?? null;
2955
+ }
2956
+ get next() {
2957
+ if (!this._tableModel) {
2958
+ return null;
2959
+ }
2960
+ const currentIndex = this.index;
2961
+ if (currentIndex === UNSET_INDEX || currentIndex >= this._tableModel.rowCount - 1) {
2962
+ return null;
2963
+ }
2964
+ return this._tableModel.getRowAt(currentIndex + 1) ?? null;
2965
+ }
2966
+ get(path) {
2967
+ return this._tree.get(path);
2968
+ }
2969
+ getValue(path) {
2970
+ return this._tree.getValue(path);
2971
+ }
2972
+ setValue(path, value) {
2973
+ this._tree.setValue(path, value);
2974
+ }
2975
+ getPlainValue() {
2976
+ return this._tree.getPlainValue();
2977
+ }
2978
+ nodeById(id) {
2979
+ return this._tree.nodeById(id);
2980
+ }
2981
+ get isDirty() {
2982
+ return this._tree.isDirty;
2983
+ }
2984
+ get isValid() {
2985
+ return this._tree.isValid;
2986
+ }
2987
+ get errors() {
2988
+ return this._tree.errors;
2989
+ }
2990
+ getPatches() {
2991
+ return this._tree.getPatches();
2992
+ }
2993
+ commit() {
2994
+ this._tree.commit();
2995
+ }
2996
+ revert() {
2997
+ this._tree.revert();
2998
+ }
2999
+ dispose() {
3000
+ this._tree.dispose();
3001
+ }
3002
+ setTableModel(tableModel) {
3003
+ this._tableModel = tableModel;
3004
+ }
3005
+ };
3006
+ function createRowModel(options) {
3007
+ const factory = createNodeFactory({
3008
+ fkResolver: options.fkResolver,
3009
+ refSchemas: options.refSchemas
3010
+ });
3011
+ const rowData = options.data ?? generateDefaultValue(options.schema, { refSchemas: options.refSchemas });
3012
+ const rootNode = factory.createTree(options.schema, rowData);
3013
+ const valueTree = new ValueTree(rootNode);
3014
+ const formulaEngine = new FormulaEngine(valueTree);
3015
+ valueTree.setFormulaEngine(formulaEngine);
3016
+ return new RowModelImpl(options.rowId, valueTree);
3017
+ }
3018
+
2255
3019
  // src/model/table/TableModelImpl.ts
2256
3020
  var TableModelImpl = class {
2257
3021
  _tableId;
@@ -2271,7 +3035,7 @@ var TableModelImpl = class {
2271
3035
  this._rows = observable.array();
2272
3036
  if (options.rows) {
2273
3037
  for (const row of options.rows) {
2274
- this._rows.push(this.createRowModel(row.rowId, row.data));
3038
+ this._rows.push(this.createRow(row.rowId, row.data));
2275
3039
  }
2276
3040
  }
2277
3041
  makeAutoObservable(this, {
@@ -2313,19 +3077,24 @@ var TableModelImpl = class {
2313
3077
  if (this.getRow(rowId)) {
2314
3078
  throw new Error(`Row with id already exists: ${rowId}`);
2315
3079
  }
2316
- const rowModel = this.createRowModel(rowId, data);
3080
+ const rowModel = this.createRow(rowId, data);
2317
3081
  this._rows.push(rowModel);
2318
3082
  return rowModel;
2319
3083
  }
2320
3084
  removeRow(rowId) {
2321
- const index = this._rows.findIndex((row) => row.rowId === rowId);
2322
- if (index !== -1) {
2323
- const row = this._rows[index];
2324
- if (row instanceof RowModelImpl) {
2325
- row.setTableModel(null);
2326
- }
2327
- this._rows.splice(index, 1);
3085
+ const index = this._rows.findIndex((row2) => row2.rowId === rowId);
3086
+ if (index === -1) {
3087
+ return;
3088
+ }
3089
+ const row = this._rows[index];
3090
+ if (!row) {
3091
+ return;
2328
3092
  }
3093
+ row.dispose();
3094
+ if (row instanceof RowModelImpl) {
3095
+ row.setTableModel(null);
3096
+ }
3097
+ this._rows.splice(index, 1);
2329
3098
  }
2330
3099
  getRow(rowId) {
2331
3100
  return this._rows.find((row) => row.rowId === rowId);
@@ -2364,15 +3133,20 @@ var TableModelImpl = class {
2364
3133
  row.revert();
2365
3134
  }
2366
3135
  }
2367
- createRowModel(rowId, data) {
2368
- const factory = createNodeFactory({
3136
+ dispose() {
3137
+ for (const row of this._rows) {
3138
+ row.dispose();
3139
+ }
3140
+ this._rows.splice(0);
3141
+ }
3142
+ createRow(rowId, data) {
3143
+ const rowModel = createRowModel({
3144
+ rowId,
3145
+ schema: this._jsonSchema,
3146
+ data,
2369
3147
  fkResolver: this._fkResolver,
2370
3148
  refSchemas: this._refSchemas
2371
3149
  });
2372
- const rowData = data ?? generateDefaultValue(this._jsonSchema, { refSchemas: this._refSchemas });
2373
- const rootNode = factory.createTree(this._jsonSchema, rowData);
2374
- const valueTree = new ValueTree(rootNode);
2375
- const rowModel = new RowModelImpl(rowId, valueTree);
2376
3150
  rowModel.setTableModel(this);
2377
3151
  return rowModel;
2378
3152
  }
@@ -2773,6 +3547,6 @@ function createDataModel(options) {
2773
3547
  return new DataModelImpl(options);
2774
3548
  }
2775
3549
 
2776
- export { ArrayToItemsTypeTransformer, ArrayValueNode, BasePrimitiveValueNode, BaseValueNode, BooleanValueNode, DataModelImpl, DefaultTransformer, ForeignKeyNotFoundError, ForeignKeyResolverImpl, ForeignKeyResolverNotConfiguredError, ForeignKeyValueNodeImpl, FormulaChangeDetector, FormulaDependencyIndex, FormulaPath, NodeFactory, NodeFactory2, NodeFactoryRegistry, NumberValueNode, ObjectToArrayTransformer, ObjectValueNode, ParsedFormula, PrimitiveToArrayTransformer, RefTransformer, RowModelImpl, SchemaParser, StringValueNode, TableModelImpl, TypeTransformChain, ValueType, createDataModel, createDefaultRegistry, createForeignKeyResolver, createNodeFactory, createSchemaModel, createTableModel, createTypeTransformChain, extractFormulaDefinition, generateDefaultValue, generateNodeId, isForeignKeyValueNode, resetNodeIdCounter };
2777
- //# sourceMappingURL=chunk-5BBFBSGI.js.map
2778
- //# sourceMappingURL=chunk-5BBFBSGI.js.map
3550
+ export { ArrayToItemsTypeTransformer, ArrayValueNode, BasePrimitiveValueNode, BaseValueNode, BooleanValueNode, DataModelImpl, DefaultTransformer, ForeignKeyNotFoundError, ForeignKeyResolverImpl, ForeignKeyResolverNotConfiguredError, ForeignKeyValueNodeImpl, FormulaChangeDetector, FormulaDependencyIndex, FormulaPath, NodeFactory, NodeFactory2, NodeFactoryRegistry, NumberValueNode, ObjectToArrayTransformer, ObjectValueNode, ParsedFormula, PrimitiveToArrayTransformer, RefTransformer, RowModelImpl, SchemaParser, StringValueNode, TableModelImpl, TypeTransformChain, ValueType, createDataModel, createDefaultRegistry, createForeignKeyResolver, createNodeFactory, createRowModel, createSchemaModel, createTableModel, createTypeTransformChain, extractFormulaDefinition, generateDefaultValue, generateNodeId, isForeignKeyValueNode, resetNodeIdCounter };
3551
+ //# sourceMappingURL=chunk-U5YDA3LA.js.map
3552
+ //# sourceMappingURL=chunk-U5YDA3LA.js.map