@symbo.ls/sdk 2.31.37 → 2.32.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.
@@ -9166,7 +9166,7 @@ var require_dexie = __commonJS({
9166
9166
  });
9167
9167
  }
9168
9168
  }
9169
- var DEXIE_VERSION = "4.2.0";
9169
+ var DEXIE_VERSION = "4.2.1";
9170
9170
  var maxString = String.fromCharCode(65535);
9171
9171
  var minKey = -Infinity;
9172
9172
  var INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array<string | number | Date>.";
@@ -9291,6 +9291,73 @@ var require_dexie = __commonJS({
9291
9291
  return res;
9292
9292
  });
9293
9293
  }
9294
+ var PropModification2 = (function() {
9295
+ function PropModification3(spec) {
9296
+ this["@@propmod"] = spec;
9297
+ }
9298
+ PropModification3.prototype.execute = function(value2) {
9299
+ var _a2;
9300
+ var spec = this["@@propmod"];
9301
+ if (spec.add !== void 0) {
9302
+ var term = spec.add;
9303
+ if (isArray2(term)) {
9304
+ return __spreadArray(__spreadArray([], isArray2(value2) ? value2 : [], true), term, true).sort();
9305
+ }
9306
+ if (typeof term === "number")
9307
+ return (Number(value2) || 0) + term;
9308
+ if (typeof term === "bigint") {
9309
+ try {
9310
+ return BigInt(value2) + term;
9311
+ } catch (_b) {
9312
+ return BigInt(0) + term;
9313
+ }
9314
+ }
9315
+ throw new TypeError("Invalid term ".concat(term));
9316
+ }
9317
+ if (spec.remove !== void 0) {
9318
+ var subtrahend_1 = spec.remove;
9319
+ if (isArray2(subtrahend_1)) {
9320
+ return isArray2(value2) ? value2.filter(function(item) {
9321
+ return !subtrahend_1.includes(item);
9322
+ }).sort() : [];
9323
+ }
9324
+ if (typeof subtrahend_1 === "number")
9325
+ return Number(value2) - subtrahend_1;
9326
+ if (typeof subtrahend_1 === "bigint") {
9327
+ try {
9328
+ return BigInt(value2) - subtrahend_1;
9329
+ } catch (_c) {
9330
+ return BigInt(0) - subtrahend_1;
9331
+ }
9332
+ }
9333
+ throw new TypeError("Invalid subtrahend ".concat(subtrahend_1));
9334
+ }
9335
+ var prefixToReplace = (_a2 = spec.replacePrefix) === null || _a2 === void 0 ? void 0 : _a2[0];
9336
+ if (prefixToReplace && typeof value2 === "string" && value2.startsWith(prefixToReplace)) {
9337
+ return spec.replacePrefix[1] + value2.substring(prefixToReplace.length);
9338
+ }
9339
+ return value2;
9340
+ };
9341
+ return PropModification3;
9342
+ })();
9343
+ function applyUpdateSpec(obj, changes) {
9344
+ var keyPaths = keys2(changes);
9345
+ var numKeys = keyPaths.length;
9346
+ var anythingModified = false;
9347
+ for (var i = 0; i < numKeys; ++i) {
9348
+ var keyPath = keyPaths[i];
9349
+ var value2 = changes[keyPath];
9350
+ var origValue = getByKeyPath(obj, keyPath);
9351
+ if (value2 instanceof PropModification2) {
9352
+ setByKeyPath(obj, keyPath, value2.execute(origValue));
9353
+ anythingModified = true;
9354
+ } else if (origValue !== value2) {
9355
+ setByKeyPath(obj, keyPath, value2);
9356
+ anythingModified = true;
9357
+ }
9358
+ }
9359
+ return anythingModified;
9360
+ }
9294
9361
  var Table = (function() {
9295
9362
  function Table2() {
9296
9363
  }
@@ -9486,6 +9553,28 @@ var require_dexie = __commonJS({
9486
9553
  return lastResult;
9487
9554
  });
9488
9555
  };
9556
+ Table2.prototype.upsert = function(key, modifications) {
9557
+ var _this = this;
9558
+ var keyPath = this.schema.primKey.keyPath;
9559
+ return this._trans("readwrite", function(trans) {
9560
+ return _this.core.get({ trans, key }).then(function(existing) {
9561
+ var obj = existing !== null && existing !== void 0 ? existing : {};
9562
+ applyUpdateSpec(obj, modifications);
9563
+ if (keyPath)
9564
+ setByKeyPath(obj, keyPath, key);
9565
+ return _this.core.mutate({
9566
+ trans,
9567
+ type: "put",
9568
+ values: [obj],
9569
+ keys: [key],
9570
+ upsert: true,
9571
+ updates: { keys: [key], changeSpecs: [modifications] }
9572
+ }).then(function(res) {
9573
+ return res.numFailures ? DexiePromise.reject(res.failures[0]) : !!existing;
9574
+ });
9575
+ });
9576
+ });
9577
+ };
9489
9578
  Table2.prototype.update = function(keyOrObject, modifications) {
9490
9579
  if (typeof keyOrObject === "object" && !isArray2(keyOrObject)) {
9491
9580
  var key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath);
@@ -9848,55 +9937,6 @@ var require_dexie = __commonJS({
9848
9937
  }
9849
9938
  });
9850
9939
  }
9851
- var PropModification2 = (function() {
9852
- function PropModification3(spec) {
9853
- this["@@propmod"] = spec;
9854
- }
9855
- PropModification3.prototype.execute = function(value2) {
9856
- var _a2;
9857
- var spec = this["@@propmod"];
9858
- if (spec.add !== void 0) {
9859
- var term = spec.add;
9860
- if (isArray2(term)) {
9861
- return __spreadArray(__spreadArray([], isArray2(value2) ? value2 : [], true), term, true).sort();
9862
- }
9863
- if (typeof term === "number")
9864
- return (Number(value2) || 0) + term;
9865
- if (typeof term === "bigint") {
9866
- try {
9867
- return BigInt(value2) + term;
9868
- } catch (_b) {
9869
- return BigInt(0) + term;
9870
- }
9871
- }
9872
- throw new TypeError("Invalid term ".concat(term));
9873
- }
9874
- if (spec.remove !== void 0) {
9875
- var subtrahend_1 = spec.remove;
9876
- if (isArray2(subtrahend_1)) {
9877
- return isArray2(value2) ? value2.filter(function(item) {
9878
- return !subtrahend_1.includes(item);
9879
- }).sort() : [];
9880
- }
9881
- if (typeof subtrahend_1 === "number")
9882
- return Number(value2) - subtrahend_1;
9883
- if (typeof subtrahend_1 === "bigint") {
9884
- try {
9885
- return BigInt(value2) - subtrahend_1;
9886
- } catch (_c) {
9887
- return BigInt(0) - subtrahend_1;
9888
- }
9889
- }
9890
- throw new TypeError("Invalid subtrahend ".concat(subtrahend_1));
9891
- }
9892
- var prefixToReplace = (_a2 = spec.replacePrefix) === null || _a2 === void 0 ? void 0 : _a2[0];
9893
- if (prefixToReplace && typeof value2 === "string" && value2.startsWith(prefixToReplace)) {
9894
- return spec.replacePrefix[1] + value2.substring(prefixToReplace.length);
9895
- }
9896
- return value2;
9897
- };
9898
- return PropModification3;
9899
- })();
9900
9940
  var Collection = (function() {
9901
9941
  function Collection2() {
9902
9942
  }
@@ -10176,23 +10216,8 @@ var require_dexie = __commonJS({
10176
10216
  if (typeof changes === "function") {
10177
10217
  modifyer = changes;
10178
10218
  } else {
10179
- var keyPaths = keys2(changes);
10180
- var numKeys = keyPaths.length;
10181
10219
  modifyer = function(item) {
10182
- var anythingModified = false;
10183
- for (var i = 0; i < numKeys; ++i) {
10184
- var keyPath = keyPaths[i];
10185
- var val = changes[keyPath];
10186
- var origVal = getByKeyPath(item, keyPath);
10187
- if (val instanceof PropModification2) {
10188
- setByKeyPath(item, keyPath, val.execute(origVal));
10189
- anythingModified = true;
10190
- } else if (origVal !== val) {
10191
- setByKeyPath(item, keyPath, val);
10192
- anythingModified = true;
10193
- }
10194
- }
10195
- return anythingModified;
10220
+ return applyUpdateSpec(item, changes);
10196
10221
  };
10197
10222
  }
10198
10223
  var coreTable = ctx.table.core;
@@ -12196,8 +12221,8 @@ var require_dexie = __commonJS({
12196
12221
  state.vcFired = true;
12197
12222
  db.on("versionchange").fire(ev);
12198
12223
  });
12199
- idbdb.onclose = wrap(function(ev) {
12200
- db.on("close").fire(ev);
12224
+ idbdb.onclose = wrap(function() {
12225
+ db.close({ disableAutoOpen: false });
12201
12226
  });
12202
12227
  if (wasCreated)
12203
12228
  _onDatabaseCreated(db._deps, dbName);
@@ -0,0 +1,277 @@
1
+ // src/utils/ordering.js
2
+ function isObjectLike(val) {
3
+ return val && typeof val === "object" && !Array.isArray(val);
4
+ }
5
+ function normalizePath(path) {
6
+ if (Array.isArray(path)) {
7
+ return path;
8
+ }
9
+ if (typeof path === "string") {
10
+ return [path];
11
+ }
12
+ return [];
13
+ }
14
+ function getParentPathsFromTuples(tuples = []) {
15
+ const seen = /* @__PURE__ */ new Set();
16
+ const parents = [];
17
+ const META_KEYS = /* @__PURE__ */ new Set([
18
+ "style",
19
+ "class",
20
+ "text",
21
+ "html",
22
+ "content",
23
+ "data",
24
+ "attr",
25
+ "state",
26
+ "scope",
27
+ "props",
28
+ "define",
29
+ "on",
30
+ "extend",
31
+ "extends",
32
+ "childExtend",
33
+ "childExtends",
34
+ "childProps",
35
+ "children",
36
+ "component",
37
+ "context",
38
+ "tag",
39
+ "key",
40
+ "__order",
41
+ "if"
42
+ ]);
43
+ for (let i = 0; i < tuples.length; i++) {
44
+ const tuple = tuples[i];
45
+ if (!Array.isArray(tuple) || tuple.length < 2) {
46
+ continue;
47
+ }
48
+ const path = normalizePath(tuple[1]);
49
+ if (!path.length) {
50
+ continue;
51
+ }
52
+ if (path[0] === "schema") {
53
+ continue;
54
+ }
55
+ const immediateParent = path.slice(0, -1);
56
+ if (immediateParent.length) {
57
+ const key = JSON.stringify(immediateParent);
58
+ if (!seen.has(key)) {
59
+ seen.add(key);
60
+ parents.push(immediateParent);
61
+ }
62
+ }
63
+ const last = path[path.length - 1];
64
+ if (META_KEYS.has(last) && path.length >= 2) {
65
+ const containerParent = path.slice(0, -2);
66
+ if (containerParent.length) {
67
+ const key2 = JSON.stringify(containerParent);
68
+ if (!seen.has(key2)) {
69
+ seen.add(key2);
70
+ parents.push(containerParent);
71
+ }
72
+ }
73
+ }
74
+ for (let j = 0; j < path.length; j++) {
75
+ const seg = path[j];
76
+ if (!META_KEYS.has(seg)) {
77
+ continue;
78
+ }
79
+ const containerParent2 = path.slice(0, j);
80
+ if (!containerParent2.length) {
81
+ continue;
82
+ }
83
+ const key3 = JSON.stringify(containerParent2);
84
+ if (!seen.has(key3)) {
85
+ seen.add(key3);
86
+ parents.push(containerParent2);
87
+ }
88
+ }
89
+ }
90
+ return parents;
91
+ }
92
+ function computeOrdersFromState(root, parentPaths = []) {
93
+ if (!root || typeof root.getByPath !== "function") {
94
+ return [];
95
+ }
96
+ const orders = [];
97
+ const EXCLUDE_KEYS = /* @__PURE__ */ new Set(["__order"]);
98
+ for (let i = 0; i < parentPaths.length; i++) {
99
+ const parentPath = parentPaths[i];
100
+ const obj = (() => {
101
+ try {
102
+ return root.getByPath(parentPath);
103
+ } catch {
104
+ return null;
105
+ }
106
+ })();
107
+ if (!isObjectLike(obj)) {
108
+ continue;
109
+ }
110
+ const keys = Object.keys(obj).filter((k) => !EXCLUDE_KEYS.has(k));
111
+ orders.push({ path: parentPath, keys });
112
+ }
113
+ return orders;
114
+ }
115
+ function normaliseSchemaCode(code) {
116
+ if (typeof code !== "string" || !code.length) {
117
+ return "";
118
+ }
119
+ return code.replaceAll("/////n", "\n").replaceAll("/////tilde", "`");
120
+ }
121
+ function extractTopLevelKeysFromCode(code) {
122
+ const src = normaliseSchemaCode(code);
123
+ if (!src) {
124
+ return [];
125
+ }
126
+ const idx = src.indexOf("export default");
127
+ if (idx === -1) {
128
+ return [];
129
+ }
130
+ let i = src.indexOf("{", idx);
131
+ if (i === -1) {
132
+ return [];
133
+ }
134
+ const keys = [];
135
+ let depth = 0;
136
+ let inStr = false;
137
+ let strCh = "";
138
+ let inEsc = false;
139
+ for (; i < src.length; i++) {
140
+ const ch = src[i];
141
+ if (inStr) {
142
+ if (inEsc) {
143
+ inEsc = false;
144
+ } else if (ch === "\\") {
145
+ inEsc = true;
146
+ } else if (ch === strCh) {
147
+ inStr = false;
148
+ strCh = "";
149
+ }
150
+ continue;
151
+ }
152
+ if (ch === '"' || ch === "'" || ch === "`") {
153
+ inStr = true;
154
+ strCh = ch;
155
+ continue;
156
+ }
157
+ if (ch === "{") {
158
+ depth++;
159
+ continue;
160
+ }
161
+ if (ch === "}") {
162
+ depth--;
163
+ if (depth === 0) {
164
+ break;
165
+ }
166
+ continue;
167
+ }
168
+ if (depth !== 1) {
169
+ continue;
170
+ }
171
+ if (/[A-Za-z_$]/u.test(ch)) {
172
+ let j = i;
173
+ while (j < src.length && /[A-Za-z0-9_$]/u.test(src[j])) {
174
+ j++;
175
+ }
176
+ let k = j;
177
+ while (k < src.length && /\s/u.test(src[k])) {
178
+ k++;
179
+ }
180
+ if (src[k] === ":") {
181
+ const key = src.slice(i, j);
182
+ keys.push(key);
183
+ }
184
+ i = j;
185
+ continue;
186
+ }
187
+ }
188
+ if (!keys.length) {
189
+ const bodyStart = src.indexOf("{", idx);
190
+ const bodyEnd = src.lastIndexOf("}");
191
+ if (bodyStart === -1 || bodyEnd === -1 || bodyEnd <= bodyStart) {
192
+ return Array.from(new Set(keys));
193
+ }
194
+ const body = src.slice(bodyStart + 1, bodyEnd);
195
+ const re = /(?:[A-Za-z_$][A-Za-z0-9_$]*|"[^"]+"|'[^']+')\s*:/gu;
196
+ for (const m of body.matchAll(re)) {
197
+ const raw = m[0].split(":")[0].trim();
198
+ const key = raw[0] === '"' || raw[0] === "'" ? raw.slice(1, -1) : raw;
199
+ keys.push(key);
200
+ }
201
+ }
202
+ return Array.from(new Set(keys));
203
+ }
204
+ function computeOrdersForTuples(root, tuples = []) {
205
+ const preferredOrderMap = /* @__PURE__ */ new Map();
206
+ for (let i = 0; i < tuples.length; i++) {
207
+ const t = tuples[i];
208
+ if (!Array.isArray(t)) {
209
+ continue;
210
+ }
211
+ const [action, path, value] = t;
212
+ const p = normalizePath(path);
213
+ if (action !== "update" || !Array.isArray(p) || p.length < 3) {
214
+ continue;
215
+ }
216
+ if (p[0] !== "schema") {
217
+ continue;
218
+ }
219
+ const [, type, key] = p;
220
+ const containerPath = [type, key];
221
+ const uses = value && Array.isArray(value.uses) ? value.uses : null;
222
+ const code = value && value.code;
223
+ const obj = (() => {
224
+ try {
225
+ return root && typeof root.getByPath === "function" ? root.getByPath(containerPath) : null;
226
+ } catch {
227
+ return null;
228
+ }
229
+ })();
230
+ if (!obj) {
231
+ continue;
232
+ }
233
+ const present = new Set(Object.keys(obj));
234
+ const EXCLUDE_KEYS = /* @__PURE__ */ new Set(["__order"]);
235
+ const codeKeys = extractTopLevelKeysFromCode(code);
236
+ let resolved = [];
237
+ if (Array.isArray(codeKeys) && codeKeys.length) {
238
+ resolved = codeKeys.filter((k) => present.has(k) && !EXCLUDE_KEYS.has(k));
239
+ }
240
+ if (resolved.length && Array.isArray(uses) && uses.length) {
241
+ for (let u = 0; u < uses.length; u++) {
242
+ const keyName = uses[u];
243
+ if (present.has(keyName) && !EXCLUDE_KEYS.has(keyName) && !resolved.includes(keyName)) {
244
+ resolved.push(keyName);
245
+ }
246
+ }
247
+ }
248
+ if (resolved.length) {
249
+ preferredOrderMap.set(JSON.stringify(containerPath), { path: containerPath, keys: resolved });
250
+ }
251
+ }
252
+ const parents = getParentPathsFromTuples(tuples);
253
+ const orders = [];
254
+ const seen = /* @__PURE__ */ new Set();
255
+ preferredOrderMap.forEach((v) => {
256
+ const k = JSON.stringify(v.path);
257
+ if (!seen.has(k)) {
258
+ seen.add(k);
259
+ orders.push(v);
260
+ }
261
+ });
262
+ const fallbackOrders = computeOrdersFromState(root, parents);
263
+ for (let i = 0; i < fallbackOrders.length; i++) {
264
+ const v = fallbackOrders[i];
265
+ const k = JSON.stringify(v.path);
266
+ if (!seen.has(k)) {
267
+ seen.add(k);
268
+ orders.push(v);
269
+ }
270
+ }
271
+ return orders;
272
+ }
273
+ export {
274
+ computeOrdersForTuples,
275
+ computeOrdersFromState,
276
+ getParentPathsFromTuples
277
+ };
@@ -4,6 +4,8 @@ import { RootStateManager } from "../state/RootStateManager.js";
4
4
  import { rootBus } from "../state/rootEventBus.js";
5
5
  import { validateParams } from "../utils/validation.js";
6
6
  import { deepStringifyFunctions } from "@domql/utils";
7
+ import { diffJson } from "../utils/jsonDiff.js";
8
+ import { computeOrdersForTuples } from "../utils/ordering.js";
7
9
  class CollabService extends BaseService {
8
10
  constructor(config) {
9
11
  super(config);
@@ -141,8 +143,8 @@ class CollabService extends BaseService {
141
143
  console.log(
142
144
  `[CollabService] Flushing ${this._pendingOps.length} offline operation batch(es)`
143
145
  );
144
- this._pendingOps.forEach(({ tuples }) => {
145
- this.socket.emit("ops", { changes: tuples, ts: Date.now() });
146
+ this._pendingOps.forEach(({ changes, orders }) => {
147
+ this.socket.emit("ops", { changes, orders, ts: Date.now() });
146
148
  });
147
149
  this._pendingOps.length = 0;
148
150
  }
@@ -187,24 +189,118 @@ class CollabService extends BaseService {
187
189
  }
188
190
  /* ---------- data helpers ---------- */
189
191
  updateData(tuples, options = {}) {
190
- var _a;
192
+ var _a, _b;
191
193
  this._ensureStateManager();
192
194
  const { isUndo = false, isRedo = false } = options;
193
195
  if (!isUndo && !isRedo && !this._isUndoRedo) {
194
196
  this._trackForUndo(tuples, options);
195
197
  }
198
+ const processedTuples = (() => {
199
+ var _a2;
200
+ try {
201
+ const root = (_a2 = this._stateManager) == null ? void 0 : _a2.root;
202
+ const isPlainObject = (o) => o && typeof o === "object" && !Array.isArray(o);
203
+ const getByPath = (state2, path) => {
204
+ if (!state2 || typeof state2.getByPath !== "function") {
205
+ return null;
206
+ }
207
+ try {
208
+ return state2.getByPath(path);
209
+ } catch {
210
+ return null;
211
+ }
212
+ };
213
+ const expandTuple = (t) => {
214
+ const [action, path, value] = t || [];
215
+ const isSchemaPath = Array.isArray(path) && path[0] === "schema";
216
+ if (action === "delete" || isSchemaPath) {
217
+ return [t];
218
+ }
219
+ const canConsiderExpansion = action === "update" && Array.isArray(path) && (path.length === 1 || path.length === 2) && isPlainObject(value);
220
+ if (!canConsiderExpansion) {
221
+ return [t];
222
+ }
223
+ const prev = getByPath(root, path) || {};
224
+ const next = value || {};
225
+ if (!isPlainObject(prev) || !isPlainObject(next)) {
226
+ return [t];
227
+ }
228
+ const ops = diffJson(prev, next, []);
229
+ if (!ops.length) {
230
+ return [];
231
+ }
232
+ const arr = [];
233
+ for (let j = 0; j < ops.length; j++) {
234
+ const op = ops[j];
235
+ const fullPath = [...path, ...op.path];
236
+ const last = fullPath[fullPath.length - 1];
237
+ if (op.action === "set") {
238
+ arr.push(["update", fullPath, op.value]);
239
+ } else if (op.action === "del") {
240
+ if (last !== "__order") {
241
+ arr.push(["delete", fullPath]);
242
+ }
243
+ }
244
+ }
245
+ return arr;
246
+ };
247
+ const minimizeTuples = (inputTuples) => {
248
+ const out = [];
249
+ for (let i = 0; i < inputTuples.length; i++) {
250
+ const expanded = expandTuple(inputTuples[i]);
251
+ for (let k = 0; k < expanded.length; k++) {
252
+ const tuple = expanded[k];
253
+ const isDelete = Array.isArray(tuple) && tuple[0] === "delete";
254
+ const isOrderKey = isDelete && Array.isArray(tuple[1]) && tuple[1][tuple[1].length - 1] === "__order";
255
+ if (!isOrderKey) {
256
+ out.push(tuple);
257
+ }
258
+ }
259
+ }
260
+ console.log(`Minimized tuples`, out);
261
+ return out;
262
+ };
263
+ console.log(`Processing tuples`, tuples);
264
+ return minimizeTuples(tuples);
265
+ } catch (err) {
266
+ console.warn(
267
+ "[CollabService] Minimal diff expansion failed \u2013 using original tuples",
268
+ err
269
+ );
270
+ return tuples;
271
+ }
272
+ })();
196
273
  if (options.append && options.append.length) {
197
- tuples.push(...options.append);
274
+ processedTuples.push(...options.append);
198
275
  }
199
276
  this._stateManager.applyChanges(tuples, { ...options });
200
- tuples = deepStringifyFunctions(tuples, []);
277
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
278
+ const el = state == null ? void 0 : state.__element;
279
+ const orders = computeOrdersForTuples(state, processedTuples);
280
+ const stringifiedTuples = (el == null ? void 0 : el.call) ? el.call(
281
+ "deepStringifyFunctions",
282
+ processedTuples,
283
+ Array.isArray(processedTuples) ? [] : {}
284
+ ) : deepStringifyFunctions(
285
+ processedTuples,
286
+ Array.isArray(processedTuples) ? [] : {}
287
+ );
201
288
  if (!this.isConnected()) {
202
289
  console.warn("[CollabService] Not connected, queuing real-time update");
203
- this._pendingOps.push({ tuples, options });
290
+ this._pendingOps.push({ changes: stringifiedTuples, orders, options });
204
291
  return;
205
292
  }
206
- if ((_a = this.socket) == null ? void 0 : _a.connected) {
207
- this.socket.emit("ops", { changes: tuples, ts: Date.now() });
293
+ if ((_b = this.socket) == null ? void 0 : _b.connected) {
294
+ console.log("[CollabService] Sending operations to the backend", {
295
+ changes: stringifiedTuples,
296
+ orders,
297
+ ts: Date.now()
298
+ });
299
+ this.socket.emit("ops", {
300
+ changes: stringifiedTuples,
301
+ orders,
302
+ ts: Date.now()
303
+ });
208
304
  }
209
305
  return { success: true };
210
306
  }
@@ -1,4 +1,5 @@
1
1
  import { BaseService } from "./BaseService.js";
2
+ import { computeOrdersForTuples } from "../utils/ordering.js";
2
3
  class ProjectService extends BaseService {
3
4
  // ==================== PROJECT METHODS ====================
4
5
  async createProject(projectData) {
@@ -530,6 +531,8 @@ class ProjectService extends BaseService {
530
531
  throw new Error("Changes must be an array");
531
532
  }
532
533
  const { message, branch = "main", type = "patch" } = options;
534
+ const state = this._context && this._context.state;
535
+ const derivedOrders = options.orders || (state ? computeOrdersForTuples(state, changes) : []);
533
536
  try {
534
537
  const response = await this._request(`/projects/${projectId}/changes`, {
535
538
  method: "POST",
@@ -537,7 +540,8 @@ class ProjectService extends BaseService {
537
540
  changes,
538
541
  message,
539
542
  branch,
540
- type
543
+ type,
544
+ ...derivedOrders && derivedOrders.length ? { orders: derivedOrders } : {}
541
545
  }),
542
546
  methodName: "applyProjectChanges"
543
547
  });