@proteinjs/db 1.25.2 → 1.26.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.
Files changed (61) hide show
  1. package/dist/generated/index.js +7 -7
  2. package/dist/generated/index.js.map +1 -1
  3. package/dist/generated/test/index.js +7 -7
  4. package/dist/generated/test/index.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/src/Db.d.ts +48 -1
  10. package/dist/src/Db.d.ts.map +1 -1
  11. package/dist/src/Db.js +206 -2
  12. package/dist/src/Db.js.map +1 -1
  13. package/dist/src/MigrationRunner.d.ts.map +1 -1
  14. package/dist/src/MigrationRunner.js +6 -0
  15. package/dist/src/MigrationRunner.js.map +1 -1
  16. package/dist/src/UpdatePreserving.d.ts +34 -0
  17. package/dist/src/UpdatePreserving.d.ts.map +1 -0
  18. package/dist/src/UpdatePreserving.js +72 -0
  19. package/dist/src/UpdatePreserving.js.map +1 -0
  20. package/dist/src/auth/TableAuth.d.ts +9 -0
  21. package/dist/src/auth/TableAuth.d.ts.map +1 -1
  22. package/dist/src/auth/TableAuth.js +36 -5
  23. package/dist/src/auth/TableAuth.js.map +1 -1
  24. package/dist/src/auth/TableServiceAuth.d.ts +7 -0
  25. package/dist/src/auth/TableServiceAuth.d.ts.map +1 -1
  26. package/dist/src/auth/TableServiceAuth.js +34 -0
  27. package/dist/src/auth/TableServiceAuth.js.map +1 -1
  28. package/dist/src/reference/ArrayMembershipOps.d.ts +69 -0
  29. package/dist/src/reference/ArrayMembershipOps.d.ts.map +1 -0
  30. package/dist/src/reference/ArrayMembershipOps.js +146 -0
  31. package/dist/src/reference/ArrayMembershipOps.js.map +1 -0
  32. package/dist/src/services/DbService.d.ts +38 -0
  33. package/dist/src/services/DbService.d.ts.map +1 -1
  34. package/dist/src/services/DbService.js.map +1 -1
  35. package/dist/src/transaction/Transaction.d.ts +19 -2
  36. package/dist/src/transaction/Transaction.d.ts.map +1 -1
  37. package/dist/src/transaction/Transaction.js +38 -0
  38. package/dist/src/transaction/Transaction.js.map +1 -1
  39. package/dist/src/transaction/TransactionContextFactory.d.ts +10 -0
  40. package/dist/src/transaction/TransactionContextFactory.d.ts.map +1 -1
  41. package/dist/test/ArrayMembershipOps.test.d.ts +2 -0
  42. package/dist/test/ArrayMembershipOps.test.d.ts.map +1 -0
  43. package/dist/test/ArrayMembershipOps.test.js +147 -0
  44. package/dist/test/ArrayMembershipOps.test.js.map +1 -0
  45. package/dist/test/TableServiceAuth.test.js +108 -4
  46. package/dist/test/TableServiceAuth.test.js.map +1 -1
  47. package/generated/index.ts +7 -7
  48. package/generated/test/index.ts +7 -7
  49. package/index.ts +2 -0
  50. package/package.json +4 -4
  51. package/src/Db.ts +160 -1
  52. package/src/MigrationRunner.ts +6 -0
  53. package/src/UpdatePreserving.ts +89 -0
  54. package/src/auth/TableAuth.ts +17 -4
  55. package/src/auth/TableServiceAuth.ts +36 -1
  56. package/src/reference/ArrayMembershipOps.ts +155 -0
  57. package/src/services/DbService.ts +38 -0
  58. package/src/transaction/Transaction.ts +52 -2
  59. package/src/transaction/TransactionContextFactory.ts +11 -0
  60. package/test/ArrayMembershipOps.test.ts +160 -0
  61. package/test/TableServiceAuth.test.ts +148 -4
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ /**
3
+ * Support for `Db.updatePreserving` / `Transaction.updatePreserving`: an update
4
+ * whose payload carries a column value the writer only PARTIALLY owns — the
5
+ * listed sub-paths belong to other writers, so their committed values are
6
+ * preserved (overlaid into the payload read-modify-write) instead of being
7
+ * clobbered by whatever stale copy the writer's snapshot happened to hold.
8
+ *
9
+ * The motivating case: a structural editor operation changes non-text fields of
10
+ * a JSON object column (styling, child overrides) while a debounced text save
11
+ * owns the object's text content. The structural payload's `content` is stale
12
+ * by construction (text syncs into the record at save time, not per keystroke);
13
+ * writing it would erase a concurrently committed text save. Preserving
14
+ * `content` from the committed row makes the two writers commute.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.overlayPreservedPaths = void 0;
18
+ /**
19
+ * Overlay committed values at `paths` into a clone of `incomingValue`.
20
+ * Pure: returns the (possibly cloned) value to write. Only plain-JSON values
21
+ * are supported (the clone is JSON-based).
22
+ */
23
+ function overlayPreservedPaths(committedValue, incomingValue, paths, whenType) {
24
+ if (incomingValue == null || committedValue == null) {
25
+ return incomingValue;
26
+ }
27
+ var result;
28
+ for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) {
29
+ var path = paths_1[_i];
30
+ var committedAtPath = deepGet(committedValue, path);
31
+ if (committedAtPath === undefined) {
32
+ continue;
33
+ }
34
+ if (whenType && typeof committedAtPath !== whenType) {
35
+ continue;
36
+ }
37
+ if (deepGet(result !== null && result !== void 0 ? result : incomingValue, path) === committedAtPath) {
38
+ continue;
39
+ }
40
+ if (result === undefined) {
41
+ result = JSON.parse(JSON.stringify(incomingValue));
42
+ }
43
+ deepSet(result, path, committedAtPath);
44
+ }
45
+ return result === undefined ? incomingValue : result;
46
+ }
47
+ exports.overlayPreservedPaths = overlayPreservedPaths;
48
+ function deepGet(value, path) {
49
+ var current = value;
50
+ for (var _i = 0, _a = path.split('.'); _i < _a.length; _i++) {
51
+ var key = _a[_i];
52
+ if (current == null || typeof current !== 'object') {
53
+ return undefined;
54
+ }
55
+ current = current[key];
56
+ }
57
+ return current;
58
+ }
59
+ function deepSet(value, path, pathValue) {
60
+ var keys = path.split('.');
61
+ var current = value;
62
+ for (var i = 0; i < keys.length - 1; i++) {
63
+ var next = current === null || current === void 0 ? void 0 : current[keys[i]];
64
+ if (next == null || typeof next !== 'object') {
65
+ // The incoming value doesn't have this path's parent — nothing to preserve into.
66
+ return;
67
+ }
68
+ current = next;
69
+ }
70
+ current[keys[keys.length - 1]] = pathValue;
71
+ }
72
+ //# sourceMappingURL=UpdatePreserving.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdatePreserving.js","sourceRoot":"","sources":["../../src/UpdatePreserving.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;AAgBH;;;;GAIG;AACH,SAAgB,qBAAqB,CACnC,cAAuB,EACvB,aAAsB,EACtB,KAAe,EACf,QAAoC;IAEpC,IAAI,aAAa,IAAI,IAAI,IAAI,cAAc,IAAI,IAAI,EAAE;QACnD,OAAO,aAAa,CAAC;KACtB;IAED,IAAI,MAA2B,CAAC;IAChC,KAAmB,UAAK,EAAL,eAAK,EAAL,mBAAK,EAAL,IAAK,EAAE;QAArB,IAAM,IAAI,cAAA;QACb,IAAM,eAAe,GAAG,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QACtD,IAAI,eAAe,KAAK,SAAS,EAAE;YACjC,SAAS;SACV;QACD,IAAI,QAAQ,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE;YACnD,SAAS;SACV;QACD,IAAI,OAAO,CAAC,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,aAAa,EAAE,IAAI,CAAC,KAAK,eAAe,EAAE;YAC9D,SAAS;SACV;QACD,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;SACpD;QACD,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;KACxC;IAED,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;AACvD,CAAC;AA7BD,sDA6BC;AAED,SAAS,OAAO,CAAC,KAAc,EAAE,IAAY;IAC3C,IAAI,OAAO,GAAQ,KAAK,CAAC;IACzB,KAAkB,UAAe,EAAf,KAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAf,cAAe,EAAf,IAAe,EAAE;QAA9B,IAAM,GAAG,SAAA;QACZ,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;YAClD,OAAO,SAAS,CAAC;SAClB;QACD,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;KACxB;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,OAAO,CAAC,KAAc,EAAE,IAAY,EAAE,SAAkB;IAC/D,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,OAAO,GAAQ,KAAK,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;QACxC,IAAM,IAAI,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;YAC5C,iFAAiF;YACjF,OAAO;SACR;QACD,OAAO,GAAG,IAAI,CAAC;KAChB;IACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;AAC7C,CAAC"}
@@ -16,6 +16,15 @@ export type TableOperationsAuth = {
16
16
  update?: Identity;
17
17
  delete?: Identity;
18
18
  };
19
+ /**
20
+ * A table-auth denial. The message names the table and operation and is safe to show the caller
21
+ * (it only echoes what they asked for). Name-tagged rather than relying on instanceof — the
22
+ * prototype chain is unreliable across package compile targets (same reason ServiceRouter's
23
+ * isServiceError checks `name`).
24
+ */
25
+ export declare class TableAuthError extends Error {
26
+ constructor(message: string);
27
+ }
19
28
  /**
20
29
  * Util to check which table operations a user can perform
21
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"TableAuth.d.ts","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEjC;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,EAAE,CAAC;AAE7D;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,SAAS;IAoBjB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAM/D,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAMhE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAMhE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;CAKjE"}
1
+ {"version":3,"file":"TableAuth.d.ts","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEjC;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,EAAE,CAAC;AAE7D;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,SAAS;IAoBjB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAM/D,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAMhE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAMhE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;CAKjE"}
@@ -1,7 +1,38 @@
1
1
  "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
2
17
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TableAuth = void 0;
18
+ exports.TableAuth = exports.TableAuthError = void 0;
4
19
  var user_auth_1 = require("@proteinjs/user-auth");
20
+ /**
21
+ * A table-auth denial. The message names the table and operation and is safe to show the caller
22
+ * (it only echoes what they asked for). Name-tagged rather than relying on instanceof — the
23
+ * prototype chain is unreliable across package compile targets (same reason ServiceRouter's
24
+ * isServiceError checks `name`).
25
+ */
26
+ var TableAuthError = /** @class */ (function (_super) {
27
+ __extends(TableAuthError, _super);
28
+ function TableAuthError(message) {
29
+ var _this = _super.call(this, message) || this;
30
+ _this.name = 'TableAuthError';
31
+ return _this;
32
+ }
33
+ return TableAuthError;
34
+ }(Error));
35
+ exports.TableAuthError = TableAuthError;
5
36
  /**
6
37
  * Util to check which table operations a user can perform
7
38
  */
@@ -23,25 +54,25 @@ var TableAuth = /** @class */ (function () {
23
54
  TableAuth.prototype.canQuery = function (table, api) {
24
55
  if (api === void 0) { api = 'db'; }
25
56
  if (!this.canAccess(table, api, 'query')) {
26
- throw new Error("User is not authorized to query table: ".concat(table.name));
57
+ throw new TableAuthError("User is not authorized to query table: ".concat(table.name));
27
58
  }
28
59
  };
29
60
  TableAuth.prototype.canInsert = function (table, api) {
30
61
  if (api === void 0) { api = 'db'; }
31
62
  if (!this.canAccess(table, api, 'insert')) {
32
- throw new Error("User is not authorized to insert records into table: ".concat(table.name));
63
+ throw new TableAuthError("User is not authorized to insert records into table: ".concat(table.name));
33
64
  }
34
65
  };
35
66
  TableAuth.prototype.canUpdate = function (table, api) {
36
67
  if (api === void 0) { api = 'db'; }
37
68
  if (!this.canAccess(table, api, 'update')) {
38
- throw new Error("User is not authorized to update records in table: ".concat(table.name));
69
+ throw new TableAuthError("User is not authorized to update records in table: ".concat(table.name));
39
70
  }
40
71
  };
41
72
  TableAuth.prototype.canDelete = function (table, api) {
42
73
  if (api === void 0) { api = 'db'; }
43
74
  if (!this.canAccess(table, api, 'delete')) {
44
- throw new Error("User is not authorized to delete records from table: ".concat(table.name));
75
+ throw new TableAuthError("User is not authorized to delete records from table: ".concat(table.name));
45
76
  }
46
77
  };
47
78
  return TableAuth;
@@ -1 +1 @@
1
- {"version":3,"file":"TableAuth.js","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":";;;AAAA,kDAAgD;AAsBhD;;GAEG;AACH;IAAA;IA4CA,CAAC;IA3CS,6BAAS,GAAjB,UACE,KAAiB,EACjB,GAAqB,EACrB,SAAmD;QAEnD,IAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE;YACpD,OAAO,oBAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SAClC;QAED,OAAO,CACL,SAAS,CAAC,GAAG,KAAK,QAAQ;YAC1B,SAAS,CAAC,SAAS,CAAC,KAAK,QAAQ;YACjC,CAAC,SAAS,CAAC,GAAG,KAAK,eAAe,IAAI,oBAAQ,CAAC,UAAU,EAAE,CAAC;YAC5D,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,eAAe,IAAI,oBAAQ,CAAC,UAAU,EAAE,CAAC;YACnE,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,oBAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAClF,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,oBAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAa,EAAE,cAAc,CAAC,CAAC,CAC7G,CAAC;IACJ,CAAC;IAED,4BAAQ,GAAR,UAAS,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACtD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE;YACxC,MAAM,IAAI,KAAK,CAAC,iDAA0C,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SACzE;IACH,CAAC;IAED,6BAAS,GAAT,UAAU,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACvD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,+DAAwD,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SACvF;IACH,CAAC;IAED,6BAAS,GAAT,UAAU,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACvD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,6DAAsD,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SACrF;IACH,CAAC;IAED,6BAAS,GAAT,UAAU,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACvD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,+DAAwD,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SACvF;IACH,CAAC;IACH,gBAAC;AAAD,CAAC,AA5CD,IA4CC;AA5CY,8BAAS"}
1
+ {"version":3,"file":"TableAuth.js","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAgD;AAsBhD;;;;;GAKG;AACH;IAAoC,kCAAK;IACvC,wBAAY,OAAe;QAA3B,YACE,kBAAM,OAAO,CAAC,SAEf;QADC,KAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;;IAC/B,CAAC;IACH,qBAAC;AAAD,CAAC,AALD,CAAoC,KAAK,GAKxC;AALY,wCAAc;AAO3B;;GAEG;AACH;IAAA;IA4CA,CAAC;IA3CS,6BAAS,GAAjB,UACE,KAAiB,EACjB,GAAqB,EACrB,SAAmD;QAEnD,IAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE;YACpD,OAAO,oBAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SAClC;QAED,OAAO,CACL,SAAS,CAAC,GAAG,KAAK,QAAQ;YAC1B,SAAS,CAAC,SAAS,CAAC,KAAK,QAAQ;YACjC,CAAC,SAAS,CAAC,GAAG,KAAK,eAAe,IAAI,oBAAQ,CAAC,UAAU,EAAE,CAAC;YAC5D,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,eAAe,IAAI,oBAAQ,CAAC,UAAU,EAAE,CAAC;YACnE,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,oBAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAClF,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,oBAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAa,EAAE,cAAc,CAAC,CAAC,CAC7G,CAAC;IACJ,CAAC;IAED,4BAAQ,GAAR,UAAS,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACtD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE;YACxC,MAAM,IAAI,cAAc,CAAC,iDAA0C,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SAClF;IACH,CAAC;IAED,6BAAS,GAAT,UAAU,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACvD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE;YACzC,MAAM,IAAI,cAAc,CAAC,+DAAwD,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SAChG;IACH,CAAC;IAED,6BAAS,GAAT,UAAU,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACvD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE;YACzC,MAAM,IAAI,cAAc,CAAC,6DAAsD,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SAC9F;IACH,CAAC;IAED,6BAAS,GAAT,UAAU,KAAiB,EAAE,GAA4B;QAA5B,oBAAA,EAAA,UAA4B;QACvD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE;YACzC,MAAM,IAAI,cAAc,CAAC,+DAAwD,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;SAChG;IACH,CAAC;IACH,gBAAC;AAAD,CAAC,AA5CD,IA4CC;AA5CY,8BAAS"}
@@ -8,5 +8,12 @@ export declare class TableServiceAuth {
8
8
  * specific, actionable error. Server-side `Db` usage never runs this check.
9
9
  */
10
10
  private checkServiceProtectedColumns;
11
+ /**
12
+ * `updateArrayMembership`'s payload names its target column (`columnPropertyName`) instead of
13
+ * carrying record fields, so the record-shaped protected-column check above can't see the
14
+ * write. Membership ops always SET real membership — there is no null-clearing form — so
15
+ * targeting a protected column is rejected outright.
16
+ */
17
+ private checkArrayMembershipProtectedColumn;
11
18
  }
12
19
  //# sourceMappingURL=TableServiceAuth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TableServiceAuth.d.ts","sourceRoot":"","sources":["../../../src/auth/TableServiceAuth.ts"],"names":[],"mappings":"AAKA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAA+C;IAE7D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO;IAoCnD;;;;;OAKG;IACH,OAAO,CAAC,4BAA4B;CAarC"}
1
+ {"version":3,"file":"TableServiceAuth.d.ts","sourceRoot":"","sources":["../../../src/auth/TableServiceAuth.ts"],"names":[],"mappings":"AAKA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAA+C;IAE7D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO;IAoDnD;;;;;OAKG;IACH,OAAO,CAAC,4BAA4B;IAcpC;;;;;OAKG;IACH,OAAO,CAAC,mCAAmC;CAY5C"}
@@ -27,6 +27,17 @@ var TableServiceAuth = /** @class */ (function () {
27
27
  tableAuth.canUpdate(table, 'service');
28
28
  this.checkServiceProtectedColumns(table, args[1]);
29
29
  }
30
+ else if (methodName === 'updateArrayMembership') {
31
+ // An update in RMW clothing: gated exactly like `update`. Row visibility is enforced
32
+ // inside the operation itself — scoped/column query injection runs on the server-side
33
+ // read-modify-write, so a caller can only touch rows they could already update.
34
+ tableAuth.canUpdate(table, 'service');
35
+ this.checkArrayMembershipProtectedColumn(table, args[1]);
36
+ }
37
+ else if (methodName === 'updatePreserving') {
38
+ tableAuth.canUpdate(table, 'service');
39
+ this.checkServiceProtectedColumns(table, args[1]);
40
+ }
30
41
  else if (methodName === 'delete') {
31
42
  tableAuth.canDelete(table, 'service');
32
43
  }
@@ -42,6 +53,13 @@ var TableServiceAuth = /** @class */ (function () {
42
53
  if ((error === null || error === void 0 ? void 0 : error.name) === 'ServiceError') {
43
54
  throw error;
44
55
  }
56
+ // A table-auth denial is equally client-safe (it only names the table/operation the caller
57
+ // asked for). Cross the wire verbatim so the caller sees "not authorized to query table:
58
+ // session" rather than the generic run-service denial — a denied query must be
59
+ // distinguishable from an empty one at the surface that shows it.
60
+ if ((error === null || error === void 0 ? void 0 : error.name) === 'TableAuthError') {
61
+ throw new service_1.ServiceError(error.message);
62
+ }
45
63
  this.logger.error({ message: "Failed evaluating auth for method: ".concat(methodName), error: error });
46
64
  return false;
47
65
  }
@@ -67,6 +85,22 @@ var TableServiceAuth = /** @class */ (function () {
67
85
  }
68
86
  }
69
87
  };
88
+ /**
89
+ * `updateArrayMembership`'s payload names its target column (`columnPropertyName`) instead of
90
+ * carrying record fields, so the record-shaped protected-column check above can't see the
91
+ * write. Membership ops always SET real membership — there is no null-clearing form — so
92
+ * targeting a protected column is rejected outright.
93
+ */
94
+ TableServiceAuth.prototype.checkArrayMembershipProtectedColumn = function (table, update) {
95
+ var _a;
96
+ var protectedColumns = (_a = table.auth) === null || _a === void 0 ? void 0 : _a.serviceProtectedColumns;
97
+ if (!(protectedColumns === null || protectedColumns === void 0 ? void 0 : protectedColumns.length) || !update || typeof update !== 'object') {
98
+ return;
99
+ }
100
+ if (protectedColumns.includes(update.columnPropertyName)) {
101
+ throw new service_1.ServiceError("Column '".concat(update.columnPropertyName, "' cannot be written via the db service on table: ").concat(table.name));
102
+ }
103
+ };
70
104
  return TableServiceAuth;
71
105
  }());
72
106
  exports.TableServiceAuth = TableServiceAuth;
@@ -1 +1 @@
1
- {"version":3,"file":"TableServiceAuth.js","sourceRoot":"","sources":["../../../src/auth/TableServiceAuth.ts"],"names":[],"mappings":";;;AAAA,4CAA2C;AAC3C,8CAAkD;AAClD,kCAA0C;AAC1C,yCAAwC;AAExC;IAAA;QACU,WAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAyD/D,CAAC;IAvDC,oCAAS,GAAT,UAAU,UAAkB,EAAE,IAAW;QACvC,IAAI;YACF,IAAM,KAAK,GAA2B,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9C,IAAI,CAAC,IAAA,eAAO,EAAC,KAAK,CAAC,EAAE;gBACnB,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;aACrE;YAED,IAAM,SAAS,GAAG,IAAI,qBAAS,EAAE,CAAC;YAClC,IAAI,UAAU,KAAK,KAAK,IAAI,UAAU,IAAI,OAAO,IAAI,UAAU,IAAI,aAAa,EAAE;gBAChF,SAAS,CAAC,QAAQ,CAAC,KAAmB,EAAE,SAAS,CAAC,CAAC;aACpD;iBAAM,IAAI,UAAU,KAAK,QAAQ,EAAE;gBAClC,SAAS,CAAC,SAAS,CAAC,KAAmB,EAAE,SAAS,CAAC,CAAC;gBACpD,IAAI,CAAC,4BAA4B,CAAC,KAAmB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;aACjE;iBAAM,IAAI,UAAU,KAAK,QAAQ,EAAE;gBAClC,SAAS,CAAC,SAAS,CAAC,KAAmB,EAAE,SAAS,CAAC,CAAC;gBACpD,IAAI,CAAC,4BAA4B,CAAC,KAAmB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;aACjE;iBAAM,IAAI,UAAU,KAAK,QAAQ,EAAE;gBAClC,SAAS,CAAC,SAAS,CAAC,KAAmB,EAAE,SAAS,CAAC,CAAC;aACrD;iBAAM;gBACL,MAAM,IAAI,KAAK,CAAC,uEAAgE,UAAU,CAAE,CAAC,CAAC;aAC/F;SACF;QAAC,OAAO,KAAU,EAAE;YACnB,yFAAyF;YACzF,qFAAqF;YACrF,uFAAuF;YACvF,uFAAuF;YACvF,IAAI,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,MAAK,cAAc,EAAE;gBAClC,MAAM,KAAK,CAAC;aACb;YACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,6CAAsC,UAAU,CAAE,EAAE,KAAK,OAAA,EAAE,CAAC,CAAC;YAC1F,OAAO,KAAK,CAAC;SACd;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACK,uDAA4B,GAApC,UAAqC,KAAiB,EAAE,MAAW;;QACjE,IAAM,gBAAgB,GAAG,MAAA,KAAK,CAAC,IAAI,0CAAE,uBAAuB,CAAC;QAC7D,IAAI,CAAC,CAAA,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAE,MAAM,CAAA,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;YACtE,OAAO;SACR;QAED,KAAqB,UAAgB,EAAhB,qCAAgB,EAAhB,8BAAgB,EAAhB,IAAgB,EAAE;YAAlC,IAAM,MAAM,yBAAA;YACf,IAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE;gBACzC,MAAM,IAAI,sBAAY,CAAC,kBAAW,MAAM,8DAAoD,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;aAC3G;SACF;IACH,CAAC;IACH,uBAAC;AAAD,CAAC,AA1DD,IA0DC;AA1DY,4CAAgB"}
1
+ {"version":3,"file":"TableServiceAuth.js","sourceRoot":"","sources":["../../../src/auth/TableServiceAuth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Commutative membership operations for `ReferenceArrayColumn` values.
3
+ *
4
+ * Motivation (the write-side lost-update class): a client writer that persists a
5
+ * reference-array column as a FULL ID LIST snapshots its in-memory state — when two
6
+ * such writes race (fire-and-forget issuance + driver-level abort/retry can commit
7
+ * an earlier-issued transaction later), the last commit wins wholesale and erases
8
+ * the other writer's committed membership change. Expressing membership changes as
9
+ * ops (add/remove/move) applied read-modify-write against COMMITTED truth inside a
10
+ * transaction makes concurrent writes converge instead of clobber:
11
+ * `remove(x)` + `remove(y)` in any commit order removes both; `add(x)` + `add(y)`
12
+ * keeps both.
13
+ *
14
+ * `applyArrayMembershipOps` is the single applier used by `Db.updateArrayMembership`
15
+ * (server-side RMW) — and by any test that needs the committed rule as importable
16
+ * truth. `computeArrayMembershipOps` derives the minimal op set from a
17
+ * before/after id-list pair, for callers whose op layer only knows list states.
18
+ */
19
+ export type ArrayMembershipOp =
20
+ /** Insert `id` after `afterId` (`null` = at the head). If `id` is already present it is repositioned. */
21
+ {
22
+ op: 'add';
23
+ id: string;
24
+ afterId: string | null;
25
+ }
26
+ /** Remove `id`. No-op when absent. */
27
+ | {
28
+ op: 'remove';
29
+ id: string;
30
+ }
31
+ /** Reposition `id` after `afterId` (`null` = at the head). No-op when `id` is absent (a concurrent remove wins). */
32
+ | {
33
+ op: 'move';
34
+ id: string;
35
+ afterId: string | null;
36
+ };
37
+ export type ArrayMembershipUpdate = {
38
+ /** The record whose array column is being updated. */
39
+ recordId: string;
40
+ /** Property name of the `ReferenceArrayColumn` on the table. */
41
+ columnPropertyName: string;
42
+ /** Ops applied in order against the committed id list. */
43
+ ops: ArrayMembershipOp[];
44
+ };
45
+ /**
46
+ * Apply membership ops to an id list. Pure; returns a new array and whether it
47
+ * differs from the input.
48
+ *
49
+ * Anchor resolution (`afterId`) against a diverged committed list is
50
+ * convergence-by-anchor: a missing anchor appends at the end (the anchor was
51
+ * concurrently removed — the element still lands in the list, order is
52
+ * best-effort), `afterId: null` inserts at the head.
53
+ */
54
+ export declare function applyArrayMembershipOps(currentIds: string[], ops: ArrayMembershipOp[]): {
55
+ ids: string[];
56
+ changed: boolean;
57
+ };
58
+ /**
59
+ * Compute the op set that transforms `beforeIds` into `afterIds`.
60
+ *
61
+ * Removes first, then a single walk of `afterIds` emitting `add` for new
62
+ * elements and `move` for surviving elements whose relative order changed.
63
+ * Moves are minimized via the longest increasing subsequence of surviving
64
+ * elements (elements on the LIS stay put; everything else moves). Replaying
65
+ * the result on `beforeIds` yields exactly `afterIds`; replaying it on a
66
+ * DIVERGED committed list converges by anchor instead of clobbering.
67
+ */
68
+ export declare function computeArrayMembershipOps(beforeIds: string[], afterIds: string[]): ArrayMembershipOp[];
69
+ //# sourceMappingURL=ArrayMembershipOps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArrayMembershipOps.d.ts","sourceRoot":"","sources":["../../../src/reference/ArrayMembershipOps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,iBAAiB;AAC3B,yGAAyG;AACvG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AACnD,sCAAsC;GACpC;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAC9B,oHAAoH;GAClH;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,qBAAqB,GAAG;IAClC,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0DAA0D;IAC1D,GAAG,EAAE,iBAAiB,EAAE,CAAC;CAC1B,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAAE,EACpB,GAAG,EAAE,iBAAiB,EAAE,GACvB;IAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAmCrC;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CA8BtG"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Commutative membership operations for `ReferenceArrayColumn` values.
4
+ *
5
+ * Motivation (the write-side lost-update class): a client writer that persists a
6
+ * reference-array column as a FULL ID LIST snapshots its in-memory state — when two
7
+ * such writes race (fire-and-forget issuance + driver-level abort/retry can commit
8
+ * an earlier-issued transaction later), the last commit wins wholesale and erases
9
+ * the other writer's committed membership change. Expressing membership changes as
10
+ * ops (add/remove/move) applied read-modify-write against COMMITTED truth inside a
11
+ * transaction makes concurrent writes converge instead of clobber:
12
+ * `remove(x)` + `remove(y)` in any commit order removes both; `add(x)` + `add(y)`
13
+ * keeps both.
14
+ *
15
+ * `applyArrayMembershipOps` is the single applier used by `Db.updateArrayMembership`
16
+ * (server-side RMW) — and by any test that needs the committed rule as importable
17
+ * truth. `computeArrayMembershipOps` derives the minimal op set from a
18
+ * before/after id-list pair, for callers whose op layer only knows list states.
19
+ */
20
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
21
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
22
+ if (ar || !(i in from)) {
23
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
24
+ ar[i] = from[i];
25
+ }
26
+ }
27
+ return to.concat(ar || Array.prototype.slice.call(from));
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.computeArrayMembershipOps = exports.applyArrayMembershipOps = void 0;
31
+ /**
32
+ * Apply membership ops to an id list. Pure; returns a new array and whether it
33
+ * differs from the input.
34
+ *
35
+ * Anchor resolution (`afterId`) against a diverged committed list is
36
+ * convergence-by-anchor: a missing anchor appends at the end (the anchor was
37
+ * concurrently removed — the element still lands in the list, order is
38
+ * best-effort), `afterId: null` inserts at the head.
39
+ */
40
+ function applyArrayMembershipOps(currentIds, ops) {
41
+ var ids = __spreadArray([], currentIds, true);
42
+ for (var _i = 0, ops_1 = ops; _i < ops_1.length; _i++) {
43
+ var op = ops_1[_i];
44
+ if (op.op === 'remove') {
45
+ var idx = ids.indexOf(op.id);
46
+ if (idx !== -1) {
47
+ ids.splice(idx, 1);
48
+ }
49
+ continue;
50
+ }
51
+ if (op.op === 'move' && ids.indexOf(op.id) === -1) {
52
+ // Move of a concurrently-removed element: the remove intent wins.
53
+ continue;
54
+ }
55
+ // add (insert or reposition) and move (reposition) share placement logic.
56
+ var existingIdx = ids.indexOf(op.id);
57
+ if (existingIdx !== -1) {
58
+ ids.splice(existingIdx, 1);
59
+ }
60
+ if (op.afterId === null) {
61
+ ids.unshift(op.id);
62
+ }
63
+ else {
64
+ var anchorIdx = ids.indexOf(op.afterId);
65
+ if (anchorIdx === -1) {
66
+ ids.push(op.id);
67
+ }
68
+ else {
69
+ ids.splice(anchorIdx + 1, 0, op.id);
70
+ }
71
+ }
72
+ }
73
+ var changed = ids.length !== currentIds.length || ids.some(function (id, i) { return id !== currentIds[i]; });
74
+ return { ids: ids, changed: changed };
75
+ }
76
+ exports.applyArrayMembershipOps = applyArrayMembershipOps;
77
+ /**
78
+ * Compute the op set that transforms `beforeIds` into `afterIds`.
79
+ *
80
+ * Removes first, then a single walk of `afterIds` emitting `add` for new
81
+ * elements and `move` for surviving elements whose relative order changed.
82
+ * Moves are minimized via the longest increasing subsequence of surviving
83
+ * elements (elements on the LIS stay put; everything else moves). Replaying
84
+ * the result on `beforeIds` yields exactly `afterIds`; replaying it on a
85
+ * DIVERGED committed list converges by anchor instead of clobbering.
86
+ */
87
+ function computeArrayMembershipOps(beforeIds, afterIds) {
88
+ var before = beforeIds;
89
+ var after = afterIds;
90
+ var beforeSet = new Set(before);
91
+ var afterSet = new Set(after);
92
+ var ops = [];
93
+ for (var _i = 0, before_1 = before; _i < before_1.length; _i++) {
94
+ var id = before_1[_i];
95
+ if (!afterSet.has(id)) {
96
+ ops.push({ op: 'remove', id: id });
97
+ }
98
+ }
99
+ // Surviving elements, in after-order, with their positions in `before`.
100
+ var surviving = after.filter(function (id) { return beforeSet.has(id); });
101
+ var beforeIndex = new Map(before.map(function (id, i) { return [id, i]; }));
102
+ var stable = longestIncreasingSubsequence(surviving.map(function (id) { return beforeIndex.get(id); }));
103
+ var stableIds = new Set(stable.map(function (i) { return surviving[i]; }));
104
+ for (var i = 0; i < after.length; i++) {
105
+ var id = after[i];
106
+ var afterId = i === 0 ? null : after[i - 1];
107
+ if (!beforeSet.has(id)) {
108
+ ops.push({ op: 'add', id: id, afterId: afterId });
109
+ }
110
+ else if (!stableIds.has(id)) {
111
+ ops.push({ op: 'move', id: id, afterId: afterId });
112
+ }
113
+ }
114
+ return ops;
115
+ }
116
+ exports.computeArrayMembershipOps = computeArrayMembershipOps;
117
+ /** Indices (into the input array) of one longest strictly-increasing subsequence. */
118
+ function longestIncreasingSubsequence(values) {
119
+ var tailIndices = [];
120
+ var prev = new Array(values.length).fill(-1);
121
+ for (var i = 0; i < values.length; i++) {
122
+ var lo = 0;
123
+ var hi = tailIndices.length;
124
+ while (lo < hi) {
125
+ var mid = (lo + hi) >> 1;
126
+ if (values[tailIndices[mid]] < values[i]) {
127
+ lo = mid + 1;
128
+ }
129
+ else {
130
+ hi = mid;
131
+ }
132
+ }
133
+ if (lo > 0) {
134
+ prev[i] = tailIndices[lo - 1];
135
+ }
136
+ tailIndices[lo] = i;
137
+ }
138
+ var result = [];
139
+ var k = tailIndices.length > 0 ? tailIndices[tailIndices.length - 1] : -1;
140
+ while (k !== -1) {
141
+ result.unshift(k);
142
+ k = prev[k];
143
+ }
144
+ return result;
145
+ }
146
+ //# sourceMappingURL=ArrayMembershipOps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArrayMembershipOps.js","sourceRoot":"","sources":["../../../src/reference/ArrayMembershipOps.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;;;;;;;;;;AAmBH;;;;;;;;GAQG;AACH,SAAgB,uBAAuB,CACrC,UAAoB,EACpB,GAAwB;IAExB,IAAM,GAAG,qBAAO,UAAU,OAAC,CAAC;IAC5B,KAAiB,UAAG,EAAH,WAAG,EAAH,iBAAG,EAAH,IAAG,EAAE;QAAjB,IAAM,EAAE,YAAA;QACX,IAAI,EAAE,CAAC,EAAE,KAAK,QAAQ,EAAE;YACtB,IAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE;gBACd,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;aACpB;YACD,SAAS;SACV;QAED,IAAI,EAAE,CAAC,EAAE,KAAK,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE;YACjD,kEAAkE;YAClE,SAAS;SACV;QAED,0EAA0E;QAC1E,IAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE;YACtB,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;SAC5B;QACD,IAAI,EAAE,CAAC,OAAO,KAAK,IAAI,EAAE;YACvB,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;SACpB;aAAM;YACL,IAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE;gBACpB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;aACjB;iBAAM;gBACL,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;aACrC;SACF;KACF;IAED,IAAM,OAAO,GAAG,GAAG,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,UAAC,EAAE,EAAE,CAAC,IAAK,OAAA,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,EAApB,CAAoB,CAAC,CAAC;IAC9F,OAAO,EAAE,GAAG,KAAA,EAAE,OAAO,SAAA,EAAE,CAAC;AAC1B,CAAC;AAtCD,0DAsCC;AAED;;;;;;;;;GASG;AACH,SAAgB,yBAAyB,CAAC,SAAmB,EAAE,QAAkB;IAC/E,IAAM,MAAM,GAAG,SAAS,CAAC;IACzB,IAAM,KAAK,GAAG,QAAQ,CAAC;IACvB,IAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,IAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhC,IAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAiB,UAAM,EAAN,iBAAM,EAAN,oBAAM,EAAN,IAAM,EAAE;QAApB,IAAM,EAAE,eAAA;QACX,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YACrB,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAA,EAAE,CAAC,CAAC;SAChC;KACF;IAED,wEAAwE;IACxE,IAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,UAAC,EAAE,IAAK,OAAA,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAjB,CAAiB,CAAC,CAAC;IAC1D,IAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,UAAC,EAAE,EAAE,CAAC,IAAK,OAAA,CAAC,EAAE,EAAE,CAAC,CAAU,EAAhB,CAAgB,CAAC,CAAC,CAAC;IACrE,IAAM,MAAM,GAAG,4BAA4B,CAAC,SAAS,CAAC,GAAG,CAAC,UAAC,EAAE,IAAK,OAAA,WAAW,CAAC,GAAG,CAAC,EAAE,CAAW,EAA7B,CAA6B,CAAC,CAAC,CAAC;IAClG,IAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,UAAC,CAAC,IAAK,OAAA,SAAS,CAAC,CAAC,CAAC,EAAZ,CAAY,CAAC,CAAC,CAAC;IAE3D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACrC,IAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACpB,IAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YACtB,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAA,EAAE,OAAO,SAAA,EAAE,CAAC,CAAC;SACtC;aAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,IAAA,EAAE,OAAO,SAAA,EAAE,CAAC,CAAC;SACvC;KACF;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AA9BD,8DA8BC;AAED,qFAAqF;AACrF,SAAS,4BAA4B,CAAC,MAAgB;IACpD,IAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAM,IAAI,GAAa,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACtC,IAAI,EAAE,GAAG,CAAC,CAAC;QACX,IAAI,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC;QAC5B,OAAO,EAAE,GAAG,EAAE,EAAE;YACd,IAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE;gBACxC,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;aACd;iBAAM;gBACL,EAAE,GAAG,GAAG,CAAC;aACV;SACF;QACD,IAAI,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;SAC/B;QACD,WAAW,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;KACrB;IACD,IAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE;QACf,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;KACb;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -2,6 +2,8 @@ import { Service } from '@proteinjs/service';
2
2
  import { Table } from '../Table';
3
3
  import { Record } from '../Record';
4
4
  import { QueryBuilder } from '@proteinjs/db-query';
5
+ import { ArrayMembershipUpdate } from '../reference/ArrayMembershipOps';
6
+ import { PreservedPath } from '../UpdatePreserving';
5
7
  export declare const getDbService: () => DbService<Record>;
6
8
  export type Query<T> = ObjectQuery<T> | QueryBuilder<T>;
7
9
  export type ObjectQuery<T> = Partial<{
@@ -25,6 +27,42 @@ export interface DbService<R extends Record = Record> extends Service {
25
27
  get<T extends R>(table: Table<T>, query: Query<T>, options?: QueryOptions<T>): Promise<T>;
26
28
  insert<T extends R>(table: Table<T>, record: Omit<T, keyof R>): Promise<T>;
27
29
  update<T extends R>(table: Table<T>, record: Partial<T>, query?: Query<T>): Promise<number>;
30
+ /**
31
+ * Apply commutative membership ops (add/remove/move) to a `ReferenceArrayColumn`,
32
+ * read-modify-write against COMMITTED truth inside a server-side transaction, so concurrent
33
+ * membership writers converge instead of last-write-wins clobbering each other (the
34
+ * write-side lost-update class).
35
+ *
36
+ * Use when the column has a NAMED multi-writer split — more than one writer changes the
37
+ * list's membership concurrently. Use plain `update` when the column has a single writer,
38
+ * or when the intent genuinely is wholesale assignment of the entire list.
39
+ *
40
+ * Authorization is identical to `update`: the table's `update` grant gates the call, and
41
+ * scoped/column query injection applies to the server-side read-modify-write — a scoped
42
+ * caller can only touch rows they could already update (an out-of-scope record behaves as
43
+ * nonexistent: returns 0).
44
+ *
45
+ * @returns the update count (0 when the ops are a no-op against committed truth, or the
46
+ * record does not exist / is not visible to the caller)
47
+ */
48
+ updateArrayMembership<T extends R>(table: Table<T>, update: ArrayMembershipUpdate): Promise<number>;
49
+ /**
50
+ * Update with committed-truth preservation for column sub-paths the writer does not own:
51
+ * the payload's listed paths are overlaid with their committed values (read in the same
52
+ * server-side transaction), so this write commutes with the writers that own those paths.
53
+ * Plain-JSON columns only.
54
+ *
55
+ * Use when ownership of a column's VALUE is split across writers by sub-path (e.g. a
56
+ * structural editor op writes an object's styling while a debounced text save owns
57
+ * `content` — the structural payload's `content` is stale by construction). Use plain
58
+ * `update` when the writer owns the whole value it writes.
59
+ *
60
+ * Authorization is identical to `update` (update grant, scoped/column query injection,
61
+ * `serviceProtectedColumns` enforced on the payload).
62
+ *
63
+ * @returns the update count (0 when the record does not exist / is not visible to the caller)
64
+ */
65
+ updatePreserving<T extends R>(table: Table<T>, record: Partial<T>, preserve: PreservedPath[]): Promise<number>;
28
66
  delete<T extends R>(table: Table<T>, query: Query<T>): Promise<number>;
29
67
  query<T extends R>(table: Table<T>, query: Query<T>, options?: QueryOptions<T>): Promise<T[]>;
30
68
  getRowCount<T extends R>(table: Table<T>, query?: Query<T>): Promise<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"DbService.d.ts","sourceRoot":"","sources":["../../../src/services/DbService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAkB,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,eAAO,MAAM,YAAY,yBAAuD,CAAC;AAEjF,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;AACxD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,OAAO,CAAC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,GAAG;CAAE,CAAC,CAAC;AAC9D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAAC,cAAc,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;KAAE,CAAC;CAC5G,CAAC;AAEF,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,OAAO;IACnE,WAAW,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,GAAG,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1F,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3E,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5F,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9F,WAAW,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9E"}
1
+ {"version":3,"file":"DbService.d.ts","sourceRoot":"","sources":["../../../src/services/DbService.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DbService.js","sourceRoot":"","sources":["../../../src/services/DbService.ts"],"names":[],"mappings":";;;AAAA,8CAA6D;AAKhD,QAAA,YAAY,GAAG,IAAA,wBAAc,EAAY,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"DbService.js","sourceRoot":"","sources":["../../../src/services/DbService.ts"],"names":[],"mappings":";;;AAAA,8CAA6D;AAOhD,QAAA,YAAY,GAAG,IAAA,wBAAc,EAAY,yBAAyB,CAAC,CAAC"}
@@ -1,14 +1,16 @@
1
1
  import { Record } from '../Record';
2
2
  import { DbService } from '../services/DbService';
3
3
  import { Table } from '../Table';
4
+ import { ArrayMembershipUpdate } from '../reference/ArrayMembershipOps';
5
+ import { PreservedPath } from '../UpdatePreserving';
4
6
  export type OperationQueue<R extends Record = Record> = {
5
7
  insert: (...args: Parameters<DbService<R>['insert']>) => Promise<R>;
6
8
  update: (...args: Parameters<DbService<R>['update']>) => void;
7
9
  delete: (...args: Parameters<DbService<R>['delete']>) => void;
8
10
  };
9
11
  export type Operation<R extends Record = Record> = {
10
- name: 'insert' | 'update' | 'delete';
11
- args: Parameters<DbService<R>['insert']> | Parameters<DbService<R>['update']> | Parameters<DbService<R>['delete']>;
12
+ name: 'insert' | 'update' | 'delete' | 'updateArrayMembership' | 'updatePreserving';
13
+ args: Parameters<DbService<R>['insert']> | Parameters<DbService<R>['update']> | Parameters<DbService<R>['delete']> | [Table<any>, ArrayMembershipUpdate] | [Table<any>, Partial<R>, PreservedPath[]];
12
14
  };
13
15
  /**
14
16
  * A queue of db write operations that are executed sequentially (not batched) as a single transaction when `run` is called.
@@ -61,6 +63,21 @@ export declare class Transaction implements OperationQueue {
61
63
  * Passing in an `ObjectQuery` or a `QueryBuilder` with a `id IN string[]` condition will update the cached `db`.
62
64
  */
63
65
  delete<R extends Record = Record>(...args: Parameters<DbService<R>['delete']>): void;
66
+ /**
67
+ * Queue a commutative array-membership update (see `Db.updateArrayMembership`).
68
+ *
69
+ * The op is applied read-modify-write against COMMITTED truth server-side, so it
70
+ * does not touch the local record cache (the caller's in-memory state is the
71
+ * client-side truth and was already mutated by the operation that queued this);
72
+ * `onUpdate` listeners do not fire for membership ops.
73
+ */
74
+ updateArrayMembership(table: Table<any>, update: ArrayMembershipUpdate): void;
75
+ /**
76
+ * Queue an update with committed-truth preservation for the listed column
77
+ * sub-paths (see `Db.updatePreserving`). Cache semantics match `update`
78
+ * for the provided fields; the preservation itself happens server-side.
79
+ */
80
+ updatePreserving<R extends Record = Record>(table: Table<R>, record: Partial<R>, preserve: PreservedPath[]): void;
64
81
  /**
65
82
  * If a QueryBuilder contains a condition like this:
66
83
  * `{ field: 'id', operator: 'IN', value: string[] }`
@@ -1 +1 @@
1
- {"version":3,"file":"Transaction.d.ts","sourceRoot":"","sources":["../../../src/transaction/Transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,SAAS,EAAe,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAyB,KAAK,EAAE,MAAM,UAAU,CAAC;AAIxD,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACtD,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,CAAC;IAC9D,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACjD,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACrC,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;CACpH,CAAC;AAIF;;;;;;GAMG;AACH,qBAAa,WAAY,YAAW,cAAc;IAY9C,OAAO,CAAC,QAAQ,CAAC;IACjB,OAAO,CAAC,QAAQ,CAAC;IACjB,OAAO,CAAC,QAAQ,CAAC;IAbnB,8EAA8E;IAC9E,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAM;IAE3B;;;;OAIG;IACH,EAAE,EAAE;QAAE,CAAC,KAAK,EAAE,MAAM,GAAG;YAAE,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,CAAA;SAAE,CAAA;KAAE,CAAM;gBAG1C,QAAQ,CAAC,WAAU,MAAM,GAAG,CAAC,UAAU,GAAG,KAAK,IAAI,aAAA,EACnD,QAAQ,CAAC,WAAU,MAAM,GAAG,CAAC,cAAc,GAAG,iBAAiB,GAAG,KAAK,IAAI,aAAA,EAC3E,QAAQ,CAAC,WAAU,MAAM,GAAG,CAAC,UAAU,GAAG,KAAK,IAAI,aAAA;IAG7D;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM;;;IAQvB;;OAEG;IACG,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBhG;;;;;;;;OAQG;IACH,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;IAwDpF;;;;;OAKG;IACH,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;IAoCpF;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IA6B7B;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAS3B"}
1
+ {"version":3,"file":"Transaction.d.ts","sourceRoot":"","sources":["../../../src/transaction/Transaction.ts"],"names":[],"mappings":"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"}