@rocicorp/zero 1.12.0-canary.10 → 1.12.0-canary.12

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 (231) hide show
  1. package/out/analyze-query/src/zql-chain.js +9 -1
  2. package/out/analyze-query/src/zql-chain.js.map +1 -1
  3. package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
  4. package/out/ast-to-zql/src/ast-to-zql.js +61 -20
  5. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  6. package/out/z2s/src/compiler.d.ts +1 -1
  7. package/out/z2s/src/compiler.d.ts.map +1 -1
  8. package/out/z2s/src/compiler.js +339 -2
  9. package/out/z2s/src/compiler.js.map +1 -1
  10. package/out/z2s/src/sql.d.ts +8 -0
  11. package/out/z2s/src/sql.d.ts.map +1 -1
  12. package/out/z2s/src/sql.js +58 -11
  13. package/out/z2s/src/sql.js.map +1 -1
  14. package/out/zero/package.js +1 -1
  15. package/out/zero/package.js.map +1 -1
  16. package/out/zero/src/bindings.js +4 -4
  17. package/out/zero-cache/src/auth/write-authorizer.js +1 -1
  18. package/out/zero-cache/src/config/zero-config.d.ts +4 -0
  19. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  20. package/out/zero-cache/src/config/zero-config.js +13 -0
  21. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  22. package/out/zero-cache/src/db/create.js +1 -1
  23. package/out/zero-cache/src/scripts/decommission.js +1 -1
  24. package/out/zero-cache/src/server/replicator.js +4 -2
  25. package/out/zero-cache/src/server/replicator.js.map +1 -1
  26. package/out/zero-cache/src/server/runner/run-worker.d.ts.map +1 -1
  27. package/out/zero-cache/src/server/runner/run-worker.js +8 -5
  28. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  29. package/out/zero-cache/src/server/serving-checkpointer.js +41 -0
  30. package/out/zero-cache/src/server/serving-checkpointer.js.map +1 -0
  31. package/out/zero-cache/src/server/worker-urls.d.ts +1 -0
  32. package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
  33. package/out/zero-cache/src/server/worker-urls.js +2 -1
  34. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  35. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
  36. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +1 -1
  37. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +1 -1
  38. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
  39. package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
  40. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
  41. package/out/zero-cache/src/services/http-service.d.ts +6 -0
  42. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  43. package/out/zero-cache/src/services/http-service.js +13 -1
  44. package/out/zero-cache/src/services/http-service.js.map +1 -1
  45. package/out/zero-cache/src/services/life-cycle.d.ts +19 -0
  46. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  47. package/out/zero-cache/src/services/life-cycle.js +53 -1
  48. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  49. package/out/zero-cache/src/services/replicator/change-processor.js +1 -1
  50. package/out/zero-cache/src/services/replicator/serving-checkpointer.js +85 -0
  51. package/out/zero-cache/src/services/replicator/serving-checkpointer.js.map +1 -0
  52. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +101 -13
  53. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  54. package/out/zero-cache/src/services/view-syncer/query-covering.js +2 -2
  55. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  56. package/out/zero-cache/src/services/view-syncer/snapshotter.js +1 -1
  57. package/out/zero-cache/src/services/view-syncer/view-syncer.js +1 -1
  58. package/out/zero-cache/src/types/processes.js +4 -4
  59. package/out/zero-cache/src/types/processes.js.map +1 -1
  60. package/out/zero-cache/src/types/row-key.js +8 -1
  61. package/out/zero-cache/src/types/row-key.js.map +1 -1
  62. package/out/zero-cache/src/workers/connection.js +2 -2
  63. package/out/zero-cache/src/workers/replicator.js +9 -4
  64. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  65. package/out/zero-client/src/client/bindings.d.ts +1 -1
  66. package/out/zero-client/src/client/bindings.d.ts.map +1 -1
  67. package/out/zero-client/src/client/bindings.js +4 -4
  68. package/out/zero-client/src/client/context.d.ts +7 -1
  69. package/out/zero-client/src/client/context.d.ts.map +1 -1
  70. package/out/zero-client/src/client/context.js +7 -0
  71. package/out/zero-client/src/client/context.js.map +1 -1
  72. package/out/zero-client/src/client/crud-impl.d.ts.map +1 -1
  73. package/out/zero-client/src/client/crud-impl.js +171 -2
  74. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  75. package/out/zero-client/src/client/custom.d.ts.map +1 -1
  76. package/out/zero-client/src/client/custom.js +42 -3
  77. package/out/zero-client/src/client/custom.js.map +1 -1
  78. package/out/zero-client/src/client/ivm-branch.d.ts +62 -2
  79. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  80. package/out/zero-client/src/client/ivm-branch.js +152 -31
  81. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  82. package/out/zero-client/src/client/keys.d.ts +33 -0
  83. package/out/zero-client/src/client/keys.d.ts.map +1 -1
  84. package/out/zero-client/src/client/keys.js +43 -4
  85. package/out/zero-client/src/client/keys.js.map +1 -1
  86. package/out/zero-client/src/client/version.js +1 -1
  87. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  88. package/out/zero-client/src/client/zero-poke-handler.js +29 -1
  89. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  90. package/out/zero-client/src/client/zero-rep.d.ts +1 -1
  91. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  92. package/out/zero-client/src/client/zero-rep.js +2 -15
  93. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  94. package/out/zero-client/src/client/zero.js +1 -1
  95. package/out/zero-client/src/mod.d.ts +3 -3
  96. package/out/zero-client/src/mod.d.ts.map +1 -1
  97. package/out/zero-protocol/src/aggregate-table.d.ts +15 -0
  98. package/out/zero-protocol/src/aggregate-table.d.ts.map +1 -0
  99. package/out/zero-protocol/src/aggregate-table.js +5 -0
  100. package/out/zero-protocol/src/aggregate-table.js.map +1 -0
  101. package/out/zero-protocol/src/ast.d.ts +141 -5
  102. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  103. package/out/zero-protocol/src/ast.js +244 -18
  104. package/out/zero-protocol/src/ast.js.map +1 -1
  105. package/out/zero-protocol/src/down.d.ts +29 -29
  106. package/out/zero-protocol/src/inspect-down.d.ts +81 -81
  107. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  108. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  109. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  110. package/out/zero-protocol/src/query-hash-visitor.d.ts.map +1 -1
  111. package/out/zero-protocol/src/query-hash-visitor.js +38 -0
  112. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
  113. package/out/zero-react/src/bindings.js +4 -4
  114. package/out/zero-react/src/use-query.d.ts +3 -1
  115. package/out/zero-react/src/use-query.d.ts.map +1 -1
  116. package/out/zero-react/src/use-query.js +4 -2
  117. package/out/zero-react/src/use-query.js.map +1 -1
  118. package/out/zero-schema/src/builder/schema-builder.d.ts.map +1 -1
  119. package/out/zero-schema/src/builder/schema-builder.js +2 -0
  120. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  121. package/out/zero-schema/src/name-mapper.d.ts.map +1 -1
  122. package/out/zero-schema/src/name-mapper.js +7 -4
  123. package/out/zero-schema/src/name-mapper.js.map +1 -1
  124. package/out/zero-server/src/custom.js +1 -1
  125. package/out/zero-server/src/schema.d.ts.map +1 -1
  126. package/out/zero-server/src/schema.js +13 -2
  127. package/out/zero-server/src/schema.js.map +1 -1
  128. package/out/zero-solid/src/bindings.js +4 -4
  129. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  130. package/out/zero-solid/src/solid-view.js +3 -3
  131. package/out/zero-solid/src/solid-view.js.map +1 -1
  132. package/out/zero-types/src/format.d.ts +13 -0
  133. package/out/zero-types/src/format.d.ts.map +1 -1
  134. package/out/zero-types/src/format.js.map +1 -1
  135. package/out/zero-types/src/name-mapper.d.ts +14 -0
  136. package/out/zero-types/src/name-mapper.d.ts.map +1 -1
  137. package/out/zero-types/src/name-mapper.js +12 -0
  138. package/out/zero-types/src/name-mapper.js.map +1 -1
  139. package/out/zero-types/src/schema-value.d.ts +2 -0
  140. package/out/zero-types/src/schema-value.d.ts.map +1 -1
  141. package/out/zero-types/src/schema-value.js +15 -0
  142. package/out/zero-types/src/schema-value.js.map +1 -0
  143. package/out/zero-types/src/server-schema.d.ts +18 -0
  144. package/out/zero-types/src/server-schema.d.ts.map +1 -1
  145. package/out/zql/src/builder/builder.d.ts +126 -1
  146. package/out/zql/src/builder/builder.d.ts.map +1 -1
  147. package/out/zql/src/builder/builder.js +281 -11
  148. package/out/zql/src/builder/builder.js.map +1 -1
  149. package/out/zql/src/builder/filter.d.ts +1 -1
  150. package/out/zql/src/builder/filter.d.ts.map +1 -1
  151. package/out/zql/src/builder/filter.js +41 -3
  152. package/out/zql/src/builder/filter.js.map +1 -1
  153. package/out/zql/src/ivm/aggregate.d.ts +188 -0
  154. package/out/zql/src/ivm/aggregate.d.ts.map +1 -0
  155. package/out/zql/src/ivm/aggregate.js +470 -0
  156. package/out/zql/src/ivm/aggregate.js.map +1 -0
  157. package/out/zql/src/ivm/array-view.d.ts +1 -1
  158. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  159. package/out/zql/src/ivm/array-view.js +3 -3
  160. package/out/zql/src/ivm/array-view.js.map +1 -1
  161. package/out/zql/src/ivm/cap.js +2 -2
  162. package/out/zql/src/ivm/constraint.d.ts.map +1 -1
  163. package/out/zql/src/ivm/constraint.js +2 -1
  164. package/out/zql/src/ivm/constraint.js.map +1 -1
  165. package/out/zql/src/ivm/deferred-input.js +1 -1
  166. package/out/zql/src/ivm/fan-in.js +1 -1
  167. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  168. package/out/zql/src/ivm/flipped-join.js +3 -2
  169. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  170. package/out/zql/src/ivm/group-by.d.ts +98 -0
  171. package/out/zql/src/ivm/group-by.d.ts.map +1 -0
  172. package/out/zql/src/ivm/group-by.js +384 -0
  173. package/out/zql/src/ivm/group-by.js.map +1 -0
  174. package/out/zql/src/ivm/join-index.d.ts.map +1 -1
  175. package/out/zql/src/ivm/join-index.js +2 -1
  176. package/out/zql/src/ivm/join-index.js.map +1 -1
  177. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  178. package/out/zql/src/ivm/join-utils.js +2 -1
  179. package/out/zql/src/ivm/join-utils.js.map +1 -1
  180. package/out/zql/src/ivm/join.js +1 -1
  181. package/out/zql/src/ivm/lift-field.d.ts +54 -0
  182. package/out/zql/src/ivm/lift-field.d.ts.map +1 -0
  183. package/out/zql/src/ivm/lift-field.js +168 -0
  184. package/out/zql/src/ivm/lift-field.js.map +1 -0
  185. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +1 -1
  186. package/out/zql/src/ivm/memory-source.d.ts +2 -0
  187. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  188. package/out/zql/src/ivm/memory-source.js +9 -4
  189. package/out/zql/src/ivm/memory-source.js.map +1 -1
  190. package/out/zql/src/ivm/push-accumulated.js +1 -1
  191. package/out/zql/src/ivm/schema.d.ts +1 -0
  192. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  193. package/out/zql/src/ivm/source.d.ts +21 -0
  194. package/out/zql/src/ivm/source.d.ts.map +1 -1
  195. package/out/zql/src/ivm/source.js.map +1 -1
  196. package/out/zql/src/ivm/take.js +1 -1
  197. package/out/zql/src/ivm/union-fan-in.js +1 -1
  198. package/out/zql/src/ivm/view-apply-change.d.ts +5 -0
  199. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  200. package/out/zql/src/ivm/view-apply-change.js +23 -3
  201. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  202. package/out/zql/src/planner/planner-debug.d.ts +1 -1
  203. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  204. package/out/zql/src/planner/planner-debug.js +4 -0
  205. package/out/zql/src/planner/planner-debug.js.map +1 -1
  206. package/out/zql/src/query/expression.d.ts +50 -3
  207. package/out/zql/src/query/expression.d.ts.map +1 -1
  208. package/out/zql/src/query/expression.js +62 -6
  209. package/out/zql/src/query/expression.js.map +1 -1
  210. package/out/zql/src/query/measure-push-operator.js +1 -1
  211. package/out/zql/src/query/query-delegate-base.d.ts +8 -0
  212. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  213. package/out/zql/src/query/query-delegate-base.js +13 -5
  214. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  215. package/out/zql/src/query/query-impl.d.ts +17 -3
  216. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  217. package/out/zql/src/query/query-impl.js +239 -7
  218. package/out/zql/src/query/query-impl.js.map +1 -1
  219. package/out/zql/src/query/query.d.ts +319 -7
  220. package/out/zql/src/query/query.d.ts.map +1 -1
  221. package/out/zqlite/src/pending-delta.d.ts.map +1 -1
  222. package/out/zqlite/src/pending-delta.js +4 -3
  223. package/out/zqlite/src/pending-delta.js.map +1 -1
  224. package/out/zqlite/src/query-builder.d.ts +12 -1
  225. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  226. package/out/zqlite/src/query-builder.js +114 -13
  227. package/out/zqlite/src/query-builder.js.map +1 -1
  228. package/out/zqlite/src/table-source.d.ts.map +1 -1
  229. package/out/zqlite/src/table-source.js +37 -3
  230. package/out/zqlite/src/table-source.js.map +1 -1
  231. package/package.json +1 -1
@@ -1,9 +1,15 @@
1
1
  import { __exportAll } from "../../../_virtual/_rolldown/runtime.js";
2
+ import { unreachable } from "../../../shared/src/asserts.js";
3
+ import { hasOwn } from "../../../shared/src/has-own.js";
2
4
  import { must } from "../../../shared/src/must.js";
3
- import "../../../zero-protocol/src/mutation.js";
5
+ import { aggregateValue } from "../../../zero-protocol/src/ast.js";
6
+ import { assignProperty } from "../../../shared/src/objects.js";
4
7
  import { consume } from "../../../zql/src/ivm/stream.js";
8
+ import { AGGREGATE_COUNT_COLUMN, AGGREGATE_KEY_COLUMN, AGGREGATE_ROWS_COLUMN, AGGREGATE_SUM_COLUMN, AGGREGATE_VALUE_COLUMN } from "../../../zql/src/ivm/aggregate.js";
9
+ import "../../../zero-protocol/src/mutation.js";
5
10
  import { makeSourceChangeAdd, makeSourceChangeEdit, makeSourceChangeRemove } from "../../../zql/src/ivm/source.js";
6
- import { toPrimaryKeyString } from "./keys.js";
11
+ import { groupKey, groupValues } from "../../../zql/src/ivm/group-by.js";
12
+ import { aggregateRowKey, toPrimaryKeyString } from "./keys.js";
7
13
  //#region ../zero-client/src/client/crud-impl.ts
8
14
  var crud_impl_exports = /* @__PURE__ */ __exportAll({
9
15
  delete: () => deleteImpl,
@@ -11,6 +17,166 @@ var crud_impl_exports = /* @__PURE__ */ __exportAll({
11
17
  update: () => update,
12
18
  upsert: () => upsert
13
19
  });
20
+ function numberOr0(v) {
21
+ return typeof v === "number" ? v : 0;
22
+ }
23
+ /**
24
+ * The value a child row contributes to a sum/avg: its field, or the JSON leaf
25
+ * at the aggregate's path (see `aggregateValue`).
26
+ */
27
+ function contribution(agg, row) {
28
+ return row === void 0 ? void 0 : aggregateValue(agg, row);
29
+ }
30
+ /** A child row's contribution as a number (null/absent ⇒ 0, SQL-style). */
31
+ function fieldNumber(agg, row) {
32
+ return numberOr0(contribution(agg, row));
33
+ }
34
+ /** Whether a child row contributes a non-null number to a sum/avg. */
35
+ function contributes(agg, row) {
36
+ return typeof contribution(agg, row) === "number";
37
+ }
38
+ /**
39
+ * The new aggregate row after a child change (`oldRow`→`newRow`: insert has only
40
+ * `newRow`, delete only `oldRow`, update both), or `undefined` if unchanged.
41
+ */
42
+ function nextAggregateRow(agg, curRow, oldRowRaw, newRowRaw) {
43
+ const { predicate } = agg;
44
+ const oldRow = predicate && oldRowRaw && !predicate(oldRowRaw) ? void 0 : oldRowRaw;
45
+ const newRow = predicate && newRowRaw && !predicate(newRowRaw) ? void 0 : newRowRaw;
46
+ const value = curRow[AGGREGATE_VALUE_COLUMN];
47
+ switch (agg.fn) {
48
+ case "count": {
49
+ const delta = (newRow ? 1 : 0) - (oldRow ? 1 : 0);
50
+ return delta === 0 ? void 0 : {
51
+ ...curRow,
52
+ [AGGREGATE_VALUE_COLUMN]: numberOr0(value) + delta
53
+ };
54
+ }
55
+ case "sum":
56
+ case "avg": {
57
+ if (agg.field === void 0) return;
58
+ const sumDelta = fieldNumber(agg, newRow) - fieldNumber(agg, oldRow);
59
+ const countDelta = (contributes(agg, newRow) ? 1 : 0) - (contributes(agg, oldRow) ? 1 : 0);
60
+ const hasRows = hasOwn(curRow, AGGREGATE_ROWS_COLUMN);
61
+ const rowsDelta = hasRows ? (newRow ? 1 : 0) - (oldRow ? 1 : 0) : 0;
62
+ if (sumDelta === 0 && countDelta === 0 && rowsDelta === 0) return;
63
+ const sum = numberOr0(curRow[AGGREGATE_SUM_COLUMN]) + sumDelta;
64
+ const count = numberOr0(curRow[AGGREGATE_COUNT_COLUMN]) + countDelta;
65
+ const result = agg.fn === "sum" ? sum : sum / count;
66
+ const next = {
67
+ ...curRow,
68
+ [AGGREGATE_VALUE_COLUMN]: count > 0 ? result : null,
69
+ [AGGREGATE_SUM_COLUMN]: sum,
70
+ [AGGREGATE_COUNT_COLUMN]: count
71
+ };
72
+ if (hasRows) next[AGGREGATE_ROWS_COLUMN] = numberOr0(curRow[AGGREGATE_ROWS_COLUMN]) + rowsDelta;
73
+ return next;
74
+ }
75
+ case "min":
76
+ case "max": return;
77
+ default: unreachable(agg.fn);
78
+ }
79
+ }
80
+ /**
81
+ * The Replicache key of the aggregate row a child row contributes to, or
82
+ * `undefined` when a correlation value is null: such a child has no parent, so
83
+ * it is in no aggregate. A grouped query's row is its group's, whose values
84
+ * can be null.
85
+ */
86
+ function aggregateKeyFor(agg, child) {
87
+ if (agg.grouped) return aggregateRowKey(agg.aggTableName, { [AGGREGATE_KEY_COLUMN]: groupKey(groupValues(child, agg.childField)) });
88
+ const keyRow = {};
89
+ for (let i = 0; i < agg.childField.length; i++) {
90
+ const value = child[agg.childField[i]];
91
+ if (value === null || value === void 0) return;
92
+ assignProperty(keyRow, agg.keyField[i], value);
93
+ }
94
+ return aggregateRowKey(agg.aggTableName, keyRow);
95
+ }
96
+ /**
97
+ * Writes the aggregate row at `aggKey` as moved by one child change, if that
98
+ * row is on the client (its parent is in some query's result). The row is
99
+ * also pushed to the transaction's branch, so a query the mutator runs after
100
+ * the change reads the new value.
101
+ *
102
+ * A grouped query's row is the group's: a row joining a group that isn't on
103
+ * the client adds it, and the last row leaving one removes it.
104
+ */
105
+ async function applyAggregateDelta(tx, ivmBranch, agg, aggKey, oldRow, newRow) {
106
+ const source = ivmBranch.getSource(agg.aggTableName);
107
+ const cur = await tx.get(aggKey);
108
+ if (cur === void 0) {
109
+ const added = agg.grouped ? newGroupRow(agg, oldRow, newRow) : void 0;
110
+ if (added !== void 0) {
111
+ await tx.set(aggKey, added);
112
+ if (source) consume(source.push(makeSourceChangeAdd(added)));
113
+ }
114
+ return;
115
+ }
116
+ const updated = nextAggregateRow(agg, cur, oldRow, newRow);
117
+ if (updated === void 0) return;
118
+ const inBranch = source?.data.has(cur) ?? false;
119
+ if (agg.grouped && groupRowCount(agg, updated) === 0) {
120
+ await tx.del(aggKey);
121
+ if (inBranch) consume(must(source).push(makeSourceChangeRemove(cur)));
122
+ return;
123
+ }
124
+ await tx.set(aggKey, updated);
125
+ if (inBranch) consume(must(source).push(makeSourceChangeEdit(updated, cur)));
126
+ }
127
+ /**
128
+ * The number of rows of the group whose synced row is `row`, or `undefined`
129
+ * for a sum/avg row stored before the rows carried it.
130
+ */
131
+ function groupRowCount(agg, row) {
132
+ return row[agg.fn === "count" ? AGGREGATE_VALUE_COLUMN : AGGREGATE_ROWS_COLUMN];
133
+ }
134
+ /**
135
+ * The row of a group of a grouped query that isn't on the client, when the
136
+ * change from `oldRow` to `newRow` makes `newRow` its first row: `newRow`
137
+ * passes the query's `where` and `oldRow` wasn't counted in the group
138
+ * already. Otherwise `undefined`.
139
+ */
140
+ function newGroupRow(agg, oldRow, newRow) {
141
+ const counted = (row) => row !== void 0 && (!agg.predicate || agg.predicate(row));
142
+ if (!counted(newRow) || counted(oldRow)) return;
143
+ const values = groupValues(newRow, agg.childField);
144
+ const empty = {};
145
+ agg.keyField.forEach((col, i) => assignProperty(empty, col, values[i]));
146
+ empty[AGGREGATE_KEY_COLUMN] = groupKey(values);
147
+ if (agg.fn === "count") empty[AGGREGATE_VALUE_COLUMN] = 0;
148
+ else {
149
+ empty[AGGREGATE_VALUE_COLUMN] = null;
150
+ empty[AGGREGATE_SUM_COLUMN] = 0;
151
+ empty[AGGREGATE_COUNT_COLUMN] = 0;
152
+ empty[AGGREGATE_ROWS_COLUMN] = 0;
153
+ }
154
+ return nextAggregateRow(agg, empty, void 0, newRow);
155
+ }
156
+ /**
157
+ * Optimistically adjust any synced aggregates whose child rows live in `table`
158
+ * when a child row is locally inserted/deleted/updated. The adjusted value is
159
+ * written to Replicache (the aggregate's synthetic row, keyed by the child's
160
+ * correlation field) inside the mutator's transaction — so the view updates
161
+ * immediately, and Replicache's rebase reconciles it against the server's
162
+ * authoritative value when it pokes back (the pending mutation's delta is
163
+ * replayed while pending and absorbed into the base once confirmed — no
164
+ * double-count). An aggregate row that is not on the client is left alone,
165
+ * except that a grouped query's row is added by the first row of its group.
166
+ */
167
+ async function applyOptimisticAggregateDeltas(tx, ivmBranch, table, oldRow, newRow) {
168
+ if (!ivmBranch) return;
169
+ for (const agg of ivmBranch.getOptimisticAggregates(table)) {
170
+ const oldKey = oldRow && aggregateKeyFor(agg, oldRow);
171
+ const newKey = newRow && aggregateKeyFor(agg, newRow);
172
+ if (oldKey === newKey) {
173
+ if (oldKey !== void 0) await applyAggregateDelta(tx, ivmBranch, agg, oldKey, oldRow, newRow);
174
+ } else {
175
+ if (oldKey !== void 0) await applyAggregateDelta(tx, ivmBranch, agg, oldKey, oldRow, void 0);
176
+ if (newKey !== void 0) await applyAggregateDelta(tx, ivmBranch, agg, newKey, void 0, newRow);
177
+ }
178
+ }
179
+ }
14
180
  function defaultOptionalFieldsToNull(schema, value) {
15
181
  let rv = value;
16
182
  for (const name in schema.columns) if (rv[name] === void 0) rv = {
@@ -25,6 +191,7 @@ async function insert(tx, arg, schema, ivmBranch) {
25
191
  const val = defaultOptionalFieldsToNull(schema.tables[arg.tableName], arg.value);
26
192
  await tx.set(key, val);
27
193
  if (ivmBranch) consume(must(ivmBranch.getSource(arg.tableName)).push(makeSourceChangeAdd(val)));
194
+ await applyOptimisticAggregateDeltas(tx, ivmBranch, arg.tableName, void 0, val);
28
195
  }
29
196
  }
30
197
  async function upsert(tx, arg, schema, ivmBranch) {
@@ -47,6 +214,7 @@ async function update(tx, arg, schema, ivmBranch) {
47
214
  for (const k in update) if (update[k] !== void 0) next[k] = update[k];
48
215
  await tx.set(key, next);
49
216
  if (ivmBranch) consume(must(ivmBranch.getSource(arg.tableName)).push(makeSourceChangeEdit(next, prev)));
217
+ await applyOptimisticAggregateDeltas(tx, ivmBranch, arg.tableName, prev, next);
50
218
  }
51
219
  async function deleteImpl(tx, arg, schema, ivmBranch) {
52
220
  const key = toPrimaryKeyString(arg.tableName, schema.tables[arg.tableName].primaryKey, arg.value);
@@ -54,6 +222,7 @@ async function deleteImpl(tx, arg, schema, ivmBranch) {
54
222
  if (prev === void 0) return;
55
223
  await tx.del(key);
56
224
  if (ivmBranch) consume(must(ivmBranch.getSource(arg.tableName)).push(makeSourceChangeRemove(prev)));
225
+ await applyOptimisticAggregateDeltas(tx, ivmBranch, arg.tableName, prev, void 0);
57
226
  }
58
227
  //#endregion
59
228
  export { crud_impl_exports, deleteImpl as delete, insert, update, upsert };
@@ -1 +1 @@
1
- {"version":3,"file":"crud-impl.js","names":[],"sources":["../../../../../zero-client/src/client/crud-impl.ts"],"sourcesContent":["import type {ReadonlyJSONObject} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport {\n type DeleteOp,\n type InsertOp,\n type UpdateOp,\n type UpsertOp,\n} from '../../../zero-protocol/src/mutation.ts';\nimport type {TableSchema} from '../../../zero-schema/src/table-schema.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {\n makeSourceChangeAdd,\n makeSourceChangeEdit,\n makeSourceChangeRemove,\n} from '../../../zql/src/ivm/source.ts';\nimport {consume} from '../../../zql/src/ivm/stream.ts';\nimport type {IVMSourceBranch} from './ivm-branch.ts';\nimport {toPrimaryKeyString} from './keys.ts';\nimport type {WriteTransaction} from './replicache-types.ts';\nexport type {TableMutator} from '../../../zql/src/mutate/crud.ts';\n\nfunction defaultOptionalFieldsToNull(\n schema: TableSchema,\n value: ReadonlyJSONObject,\n): ReadonlyJSONObject {\n let rv = value;\n for (const name in schema.columns) {\n if (rv[name] === undefined) {\n rv = {...rv, [name]: null};\n }\n }\n return rv;\n}\n\nexport async function insert(\n tx: WriteTransaction,\n arg: InsertOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n if (!(await tx.has(key))) {\n const val = defaultOptionalFieldsToNull(\n schema.tables[arg.tableName],\n arg.value,\n );\n await tx.set(key, val);\n if (ivmBranch) {\n consume(\n must(ivmBranch.getSource(arg.tableName)).push(makeSourceChangeAdd(val)),\n );\n }\n }\n}\n\nexport async function upsert(\n tx: WriteTransaction,\n arg: InsertOp | UpsertOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n if (await tx.has(key)) {\n await update(tx, {...arg, op: 'update'}, schema, ivmBranch);\n } else {\n await insert(tx, {...arg, op: 'insert'}, schema, ivmBranch);\n }\n}\n\nexport async function update(\n tx: WriteTransaction,\n arg: UpdateOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n const prev = await tx.get(key);\n if (prev === undefined) {\n return;\n }\n const update = arg.value;\n const next = {...(prev as ReadonlyJSONObject)};\n for (const k in update) {\n if (update[k] !== undefined) {\n next[k] = update[k];\n }\n }\n await tx.set(key, next);\n if (ivmBranch) {\n consume(\n must(ivmBranch.getSource(arg.tableName)).push(\n makeSourceChangeEdit(next, prev as Row),\n ),\n );\n }\n}\n\nasync function deleteImpl(\n tx: WriteTransaction,\n arg: DeleteOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n const prev = await tx.get(key);\n if (prev === undefined) {\n return;\n }\n await tx.del(key);\n if (ivmBranch) {\n consume(\n must(ivmBranch.getSource(arg.tableName)).push(\n makeSourceChangeRemove(prev as Row),\n ),\n );\n }\n}\n\nexport {deleteImpl as delete};\n"],"mappings":";;;;;;;;;;;;;AAsBA,SAAS,4BACP,QACA,OACoB;CACpB,IAAI,KAAK;CACT,KAAK,MAAM,QAAQ,OAAO,SACxB,IAAI,GAAG,UAAU,KAAA,GACf,KAAK;EAAC,GAAG;GAAK,OAAO;CAAI;CAG7B,OAAO;AACT;AAEA,eAAsB,OACpB,IACA,KACA,QACA,WACe;CACf,MAAM,MAAM,mBACV,IAAI,WACJ,OAAO,OAAO,IAAI,UAAU,CAAC,YAC7B,IAAI,KACN;CACA,IAAI,CAAE,MAAM,GAAG,IAAI,GAAG,GAAI;EACxB,MAAM,MAAM,4BACV,OAAO,OAAO,IAAI,YAClB,IAAI,KACN;EACA,MAAM,GAAG,IAAI,KAAK,GAAG;EACrB,IAAI,WACF,QACE,KAAK,UAAU,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,oBAAoB,GAAG,CAAC,CACxE;CAEJ;AACF;AAEA,eAAsB,OACpB,IACA,KACA,QACA,WACe;CACf,MAAM,MAAM,mBACV,IAAI,WACJ,OAAO,OAAO,IAAI,UAAU,CAAC,YAC7B,IAAI,KACN;CACA,IAAI,MAAM,GAAG,IAAI,GAAG,GAClB,MAAM,OAAO,IAAI;EAAC,GAAG;EAAK,IAAI;CAAQ,GAAG,QAAQ,SAAS;MAE1D,MAAM,OAAO,IAAI;EAAC,GAAG;EAAK,IAAI;CAAQ,GAAG,QAAQ,SAAS;AAE9D;AAEA,eAAsB,OACpB,IACA,KACA,QACA,WACe;CACf,MAAM,MAAM,mBACV,IAAI,WACJ,OAAO,OAAO,IAAI,UAAU,CAAC,YAC7B,IAAI,KACN;CACA,MAAM,OAAO,MAAM,GAAG,IAAI,GAAG;CAC7B,IAAI,SAAS,KAAA,GACX;CAEF,MAAM,SAAS,IAAI;CACnB,MAAM,OAAO,EAAC,GAAI,KAA2B;CAC7C,KAAK,MAAM,KAAK,QACd,IAAI,OAAO,OAAO,KAAA,GAChB,KAAK,KAAK,OAAO;CAGrB,MAAM,GAAG,IAAI,KAAK,IAAI;CACtB,IAAI,WACF,QACE,KAAK,UAAU,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,KACvC,qBAAqB,MAAM,IAAW,CACxC,CACF;AAEJ;AAEA,eAAe,WACb,IACA,KACA,QACA,WACe;CACf,MAAM,MAAM,mBACV,IAAI,WACJ,OAAO,OAAO,IAAI,UAAU,CAAC,YAC7B,IAAI,KACN;CACA,MAAM,OAAO,MAAM,GAAG,IAAI,GAAG;CAC7B,IAAI,SAAS,KAAA,GACX;CAEF,MAAM,GAAG,IAAI,GAAG;CAChB,IAAI,WACF,QACE,KAAK,UAAU,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,KACvC,uBAAuB,IAAW,CACpC,CACF;AAEJ"}
1
+ {"version":3,"file":"crud-impl.js","names":[],"sources":["../../../../../zero-client/src/client/crud-impl.ts"],"sourcesContent":["import {unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {ReadonlyJSONObject} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {assignProperty} from '../../../shared/src/objects.ts';\nimport {aggregateValue} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {\n type DeleteOp,\n type InsertOp,\n type UpdateOp,\n type UpsertOp,\n} from '../../../zero-protocol/src/mutation.ts';\nimport type {TableSchema} from '../../../zero-schema/src/table-schema.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {\n AGGREGATE_COUNT_COLUMN,\n AGGREGATE_KEY_COLUMN,\n AGGREGATE_ROWS_COLUMN,\n AGGREGATE_SUM_COLUMN,\n AGGREGATE_VALUE_COLUMN,\n} from '../../../zql/src/ivm/aggregate.ts';\nimport {groupKey, groupValues} from '../../../zql/src/ivm/group-by.ts';\nimport {\n makeSourceChangeAdd,\n makeSourceChangeEdit,\n makeSourceChangeRemove,\n} from '../../../zql/src/ivm/source.ts';\nimport {consume} from '../../../zql/src/ivm/stream.ts';\nimport type {IVMSourceBranch, OptimisticAggregate} from './ivm-branch.ts';\nimport {aggregateRowKey, toPrimaryKeyString} from './keys.ts';\nimport type {WriteTransaction} from './replicache-types.ts';\nexport type {TableMutator} from '../../../zql/src/mutate/crud.ts';\n\nfunction numberOr0(v: unknown): number {\n return typeof v === 'number' ? v : 0;\n}\n\n/**\n * The value a child row contributes to a sum/avg: its field, or the JSON leaf\n * at the aggregate's path (see `aggregateValue`).\n */\nfunction contribution(agg: OptimisticAggregate, row: Row | undefined): unknown {\n return row === undefined ? undefined : aggregateValue(agg, row);\n}\n\n/** A child row's contribution as a number (null/absent ⇒ 0, SQL-style). */\nfunction fieldNumber(agg: OptimisticAggregate, row: Row | undefined): number {\n return numberOr0(contribution(agg, row));\n}\n\n/** Whether a child row contributes a non-null number to a sum/avg. */\nfunction contributes(agg: OptimisticAggregate, row: Row | undefined): boolean {\n return typeof contribution(agg, row) === 'number';\n}\n\n/**\n * The new aggregate row after a child change (`oldRow`→`newRow`: insert has only\n * `newRow`, delete only `oldRow`, update both), or `undefined` if unchanged.\n */\nfunction nextAggregateRow(\n agg: OptimisticAggregate,\n curRow: Row,\n oldRowRaw: Row | undefined,\n newRowRaw: Row | undefined,\n): Row | undefined {\n // A child row contributes only if it matches the aggregate's `where`\n // predicate (if any). Treating a non-matching row as absent lets all the\n // delta math below — count ±1, sum/avg ±field — naturally handle rows\n // entering or leaving the filtered set on an update (old matched / new\n // doesn't ⇒ −delta, and vice versa).\n const {predicate} = agg;\n const oldRow =\n predicate && oldRowRaw && !predicate(oldRowRaw) ? undefined : oldRowRaw;\n const newRow =\n predicate && newRowRaw && !predicate(newRowRaw) ? undefined : newRowRaw;\n const value = curRow[AGGREGATE_VALUE_COLUMN];\n switch (agg.fn) {\n case 'count': {\n const delta = (newRow ? 1 : 0) - (oldRow ? 1 : 0);\n return delta === 0\n ? undefined\n : {...curRow, [AGGREGATE_VALUE_COLUMN]: numberOr0(value) + delta};\n }\n case 'sum':\n case 'avg': {\n if (agg.field === undefined) {\n return undefined;\n }\n // The synced row carries the sum of non-null values and their count, so\n // adjust both and derive the value: SQL's sum/avg ignore nulls and are\n // null over no contributors. A group's row also carries its number of\n // rows, nulls included, except one stored before it did, which gets it\n // when the server next sends the group.\n const sumDelta = fieldNumber(agg, newRow) - fieldNumber(agg, oldRow);\n const countDelta =\n (contributes(agg, newRow) ? 1 : 0) - (contributes(agg, oldRow) ? 1 : 0);\n const hasRows = hasOwn(curRow, AGGREGATE_ROWS_COLUMN);\n const rowsDelta = hasRows ? (newRow ? 1 : 0) - (oldRow ? 1 : 0) : 0;\n if (sumDelta === 0 && countDelta === 0 && rowsDelta === 0) {\n return undefined;\n }\n const sum = numberOr0(curRow[AGGREGATE_SUM_COLUMN]) + sumDelta;\n const count = numberOr0(curRow[AGGREGATE_COUNT_COLUMN]) + countDelta;\n const result = agg.fn === 'sum' ? sum : sum / count;\n const next: Record<string, Value> = {\n ...curRow,\n [AGGREGATE_VALUE_COLUMN]: count > 0 ? result : null,\n [AGGREGATE_SUM_COLUMN]: sum,\n [AGGREGATE_COUNT_COLUMN]: count,\n };\n if (hasRows) {\n next[AGGREGATE_ROWS_COLUMN] =\n numberOr0(curRow[AGGREGATE_ROWS_COLUMN]) + rowsDelta;\n }\n return next;\n }\n // `min`/`max` are non-invertible; they update on the server poke.\n case 'min':\n case 'max':\n return undefined;\n default:\n unreachable(agg.fn);\n }\n}\n\n/**\n * The Replicache key of the aggregate row a child row contributes to, or\n * `undefined` when a correlation value is null: such a child has no parent, so\n * it is in no aggregate. A grouped query's row is its group's, whose values\n * can be null.\n */\nfunction aggregateKeyFor(\n agg: OptimisticAggregate,\n child: Row,\n): string | undefined {\n if (agg.grouped) {\n return aggregateRowKey(agg.aggTableName, {\n [AGGREGATE_KEY_COLUMN]: groupKey(groupValues(child, agg.childField)),\n });\n }\n const keyRow: Record<string, Value> = {};\n for (let i = 0; i < agg.childField.length; i++) {\n const value = child[agg.childField[i]];\n if (value === null || value === undefined) {\n return undefined;\n }\n assignProperty(keyRow, agg.keyField[i], value);\n }\n return aggregateRowKey(agg.aggTableName, keyRow);\n}\n\n/**\n * Writes the aggregate row at `aggKey` as moved by one child change, if that\n * row is on the client (its parent is in some query's result). The row is\n * also pushed to the transaction's branch, so a query the mutator runs after\n * the change reads the new value.\n *\n * A grouped query's row is the group's: a row joining a group that isn't on\n * the client adds it, and the last row leaving one removes it.\n */\nasync function applyAggregateDelta(\n tx: WriteTransaction,\n ivmBranch: IVMSourceBranch,\n agg: OptimisticAggregate,\n aggKey: string,\n oldRow: Row | undefined,\n newRow: Row | undefined,\n): Promise<void> {\n // The branch holds the row when a view of its query reads it.\n const source = ivmBranch.getSource(agg.aggTableName);\n const cur = await tx.get(aggKey);\n if (cur === undefined) {\n const added = agg.grouped ? newGroupRow(agg, oldRow, newRow) : undefined;\n if (added !== undefined) {\n await tx.set(aggKey, added);\n if (source) {\n consume(source.push(makeSourceChangeAdd(added)));\n }\n }\n return;\n }\n const updated = nextAggregateRow(agg, cur as Row, oldRow, newRow);\n if (updated === undefined) {\n return;\n }\n const inBranch = source?.data.has(cur as Row) ?? false;\n if (agg.grouped && groupRowCount(agg, updated) === 0) {\n await tx.del(aggKey);\n if (inBranch) {\n consume(must(source).push(makeSourceChangeRemove(cur as Row)));\n }\n return;\n }\n await tx.set(aggKey, updated);\n if (inBranch) {\n consume(must(source).push(makeSourceChangeEdit(updated, cur as Row)));\n }\n}\n\n/**\n * The number of rows of the group whose synced row is `row`, or `undefined`\n * for a sum/avg row stored before the rows carried it.\n */\nfunction groupRowCount(agg: OptimisticAggregate, row: Row): Value {\n return row[\n agg.fn === 'count' ? AGGREGATE_VALUE_COLUMN : AGGREGATE_ROWS_COLUMN\n ];\n}\n\n/**\n * The row of a group of a grouped query that isn't on the client, when the\n * change from `oldRow` to `newRow` makes `newRow` its first row: `newRow`\n * passes the query's `where` and `oldRow` wasn't counted in the group\n * already. Otherwise `undefined`.\n */\nfunction newGroupRow(\n agg: OptimisticAggregate,\n oldRow: Row | undefined,\n newRow: Row | undefined,\n): Row | undefined {\n const counted = (row: Row | undefined): row is Row =>\n row !== undefined && (!agg.predicate || agg.predicate(row));\n if (!counted(newRow) || counted(oldRow)) {\n return undefined;\n }\n const values = groupValues(newRow, agg.childField);\n // The group with no rows, which `newRow` then joins.\n const empty: Record<string, Value> = {};\n agg.keyField.forEach((col, i) => assignProperty(empty, col, values[i]));\n empty[AGGREGATE_KEY_COLUMN] = groupKey(values);\n if (agg.fn === 'count') {\n empty[AGGREGATE_VALUE_COLUMN] = 0;\n } else {\n empty[AGGREGATE_VALUE_COLUMN] = null;\n empty[AGGREGATE_SUM_COLUMN] = 0;\n empty[AGGREGATE_COUNT_COLUMN] = 0;\n empty[AGGREGATE_ROWS_COLUMN] = 0;\n }\n return nextAggregateRow(agg, empty, undefined, newRow);\n}\n\n/**\n * Optimistically adjust any synced aggregates whose child rows live in `table`\n * when a child row is locally inserted/deleted/updated. The adjusted value is\n * written to Replicache (the aggregate's synthetic row, keyed by the child's\n * correlation field) inside the mutator's transaction — so the view updates\n * immediately, and Replicache's rebase reconciles it against the server's\n * authoritative value when it pokes back (the pending mutation's delta is\n * replayed while pending and absorbed into the base once confirmed — no\n * double-count). An aggregate row that is not on the client is left alone,\n * except that a grouped query's row is added by the first row of its group.\n */\nasync function applyOptimisticAggregateDeltas(\n tx: WriteTransaction,\n ivmBranch: IVMSourceBranch | undefined,\n table: string,\n oldRow: Row | undefined,\n newRow: Row | undefined,\n): Promise<void> {\n if (!ivmBranch) {\n return;\n }\n for (const agg of ivmBranch.getOptimisticAggregates(table)) {\n const oldKey = oldRow && aggregateKeyFor(agg, oldRow);\n const newKey = newRow && aggregateKeyFor(agg, newRow);\n if (oldKey === newKey) {\n if (oldKey !== undefined) {\n await applyAggregateDelta(tx, ivmBranch, agg, oldKey, oldRow, newRow);\n }\n } else {\n // The child leaves one parent's aggregate and joins another's (either\n // side may be none: an insert, a delete, or a correlation value that\n // was or becomes null).\n if (oldKey !== undefined) {\n await applyAggregateDelta(\n tx,\n ivmBranch,\n agg,\n oldKey,\n oldRow,\n undefined,\n );\n }\n if (newKey !== undefined) {\n await applyAggregateDelta(\n tx,\n ivmBranch,\n agg,\n newKey,\n undefined,\n newRow,\n );\n }\n }\n }\n}\n\nfunction defaultOptionalFieldsToNull(\n schema: TableSchema,\n value: ReadonlyJSONObject,\n): ReadonlyJSONObject {\n let rv = value;\n for (const name in schema.columns) {\n if (rv[name] === undefined) {\n rv = {...rv, [name]: null};\n }\n }\n return rv;\n}\n\nexport async function insert(\n tx: WriteTransaction,\n arg: InsertOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n if (!(await tx.has(key))) {\n const val = defaultOptionalFieldsToNull(\n schema.tables[arg.tableName],\n arg.value,\n );\n await tx.set(key, val);\n if (ivmBranch) {\n consume(\n must(ivmBranch.getSource(arg.tableName)).push(makeSourceChangeAdd(val)),\n );\n }\n await applyOptimisticAggregateDeltas(\n tx,\n ivmBranch,\n arg.tableName,\n undefined,\n val,\n );\n }\n}\n\nexport async function upsert(\n tx: WriteTransaction,\n arg: InsertOp | UpsertOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n if (await tx.has(key)) {\n await update(tx, {...arg, op: 'update'}, schema, ivmBranch);\n } else {\n await insert(tx, {...arg, op: 'insert'}, schema, ivmBranch);\n }\n}\n\nexport async function update(\n tx: WriteTransaction,\n arg: UpdateOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n const prev = await tx.get(key);\n if (prev === undefined) {\n return;\n }\n const update = arg.value;\n const next = {...(prev as ReadonlyJSONObject)};\n for (const k in update) {\n if (update[k] !== undefined) {\n next[k] = update[k];\n }\n }\n await tx.set(key, next);\n if (ivmBranch) {\n consume(\n must(ivmBranch.getSource(arg.tableName)).push(\n makeSourceChangeEdit(next, prev as Row),\n ),\n );\n }\n await applyOptimisticAggregateDeltas(\n tx,\n ivmBranch,\n arg.tableName,\n prev as Row,\n next as Row,\n );\n}\n\nasync function deleteImpl(\n tx: WriteTransaction,\n arg: DeleteOp,\n schema: Schema,\n ivmBranch: IVMSourceBranch | undefined,\n): Promise<void> {\n const key = toPrimaryKeyString(\n arg.tableName,\n schema.tables[arg.tableName].primaryKey,\n arg.value,\n );\n const prev = await tx.get(key);\n if (prev === undefined) {\n return;\n }\n await tx.del(key);\n if (ivmBranch) {\n consume(\n must(ivmBranch.getSource(arg.tableName)).push(\n makeSourceChangeRemove(prev as Row),\n ),\n );\n }\n await applyOptimisticAggregateDeltas(\n tx,\n ivmBranch,\n arg.tableName,\n prev as Row,\n undefined,\n );\n}\n\nexport {deleteImpl as delete};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/custom.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/custom.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,12 @@
1
1
  import { assert } from "../../../shared/src/asserts.js";
2
2
  import { must } from "../../../shared/src/must.js";
3
+ import { asQueryInternals } from "../../../zql/src/query/query-internals.js";
3
4
  import { createRunnableBuilder } from "../../../zql/src/query/create-builder.js";
4
5
  import "../../../zql/src/query/query.js";
5
6
  import { zeroData } from "../../../replicache/src/transactions.js";
6
7
  import { emptyFunction } from "../../../shared/src/sentinels.js";
8
+ import { queryIDOf } from "../../../zql/src/query/query-delegate-base.js";
9
+ import { toGotQueriesKey } from "./keys.js";
7
10
  import { ZeroContext } from "./context.js";
8
11
  import { makeCRUDExecutor } from "./crud.js";
9
12
  import { makeTransactionMutate } from "../../../zql/src/mutate/crud.js";
@@ -24,7 +27,7 @@ var TransactionImpl = class {
24
27
  this.#repTx = repTx;
25
28
  const executor = makeCRUDExecutor(repTx, schema, ivmBranch);
26
29
  this.mutate = makeTransactionMutate(schema, executor);
27
- const zeroContext = newZeroContext(lc, txData.ivmSources);
30
+ const zeroContext = newZeroContext(lc, txData.ivmSources, repTx);
28
31
  this.query = createRunnableBuilder(zeroContext, schema);
29
32
  this.#zeroContext = zeroContext;
30
33
  }
@@ -55,9 +58,45 @@ function makeReplicacheMutator(lc, mutator, schema, context) {
55
58
  function assertValidRunOptions(options) {
56
59
  assert(options?.type !== "complete" && options?.type !== "cached", "Cannot wait for cached or complete results in custom mutations");
57
60
  }
58
- function newZeroContext(lc, ivmBranch) {
59
- return new ZeroContext(lc, ivmBranch, () => emptyFunction, () => emptyFunction, emptyFunction, emptyFunction, emptyFunction, (applyViewUpdates) => applyViewUpdates(), emptyFunction, assertValidRunOptions).markPipelinesReady();
61
+ function newZeroContext(lc, ivmBranch, repTx) {
62
+ return new MutatorContext(lc, ivmBranch, repTx).markPipelinesReady();
60
63
  }
64
+ function newContextArgs(lc, ivmBranch) {
65
+ return [
66
+ lc,
67
+ ivmBranch,
68
+ () => emptyFunction,
69
+ () => emptyFunction,
70
+ emptyFunction,
71
+ emptyFunction,
72
+ emptyFunction,
73
+ (applyViewUpdates) => applyViewUpdates(),
74
+ emptyFunction,
75
+ assertValidRunOptions
76
+ ];
77
+ }
78
+ /**
79
+ * The query delegate of a custom mutator. A query whose result the server has
80
+ * sent reads its aggregates from the rows the server synced, with this
81
+ * client's pending changes applied, as a view of it does. Any other query has
82
+ * no synced aggregate rows to read, so it computes its aggregates from the
83
+ * rows in the mutator's branch, like every other read the mutator makes. The
84
+ * server's run of the mutator computes them from all the rows.
85
+ */
86
+ var MutatorContext = class extends ZeroContext {
87
+ aggregatesFromSource = false;
88
+ #repTx;
89
+ #synced;
90
+ constructor(lc, ivmBranch, repTx) {
91
+ super(...newContextArgs(lc, ivmBranch));
92
+ this.#repTx = repTx;
93
+ this.#synced = new ZeroContext(...newContextArgs(lc, ivmBranch)).markPipelinesReady();
94
+ }
95
+ async run(query, options) {
96
+ const queryID = queryIDOf(asQueryInternals(query));
97
+ return await this.#repTx.has(toGotQueriesKey(queryID)) ? this.#synced.run(query, options) : super.run(query, options);
98
+ }
99
+ };
61
100
  //#endregion
62
101
  export { TransactionImpl, makeReplicacheMutator };
63
102
 
@@ -1 +1 @@
1
- {"version":3,"file":"custom.js","names":[],"sources":["../../../../../zero-client/src/client/custom.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {ZeroTxData} from '../../../replicache/src/replicache-options.ts';\nimport type {WriteTransactionImpl} from '../../../replicache/src/transactions.ts';\nimport {zeroData} from '../../../replicache/src/transactions.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {emptyFunction} from '../../../shared/src/sentinels.ts';\nimport type {DefaultSchema} from '../../../zero-types/src/default-types.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {\n makeTransactionMutate,\n type TransactionMutate,\n} from '../../../zql/src/mutate/crud.ts';\nimport type {\n ClientTransaction,\n Transaction,\n} from '../../../zql/src/mutate/custom.ts';\nimport {createRunnableBuilder} from '../../../zql/src/query/create-builder.ts';\nimport {\n type HumanReadable,\n type Query,\n type RunOptions,\n} from '../../../zql/src/query/query.ts';\nimport type {ConditionalSchemaQuery} from '../../../zql/src/query/schema-query.ts';\nimport type {ClientID} from '../types/client-state.ts';\nimport {ZeroContext} from './context.ts';\nimport {makeCRUDExecutor} from './crud.ts';\nimport type {IVMSourceBranch} from './ivm-branch.ts';\nimport type {WriteTransaction} from './replicache-types.ts';\n\n/**\n * The shape which a user's custom mutator definitions must conform to.\n * Supports arbitrary depth nesting of namespaces.\n */\nexport type CustomMutatorDefs = {\n // oxlint-disable-next-line no-explicit-any\n [namespaceOrKey: string]: CustomMutatorImpl<any> | CustomMutatorDefs;\n};\n\nexport type MutatorResultDetails =\n | {\n readonly type: 'success';\n readonly data?: ReadonlyJSONValue | undefined;\n }\n | {\n readonly type: 'error';\n readonly error:\n | {\n readonly type: 'app';\n readonly message: string;\n readonly details: ReadonlyJSONValue | undefined;\n }\n | {\n readonly type: 'zero';\n readonly message: string;\n };\n };\n\nexport type MutatorResultSuccessDetails = Extract<\n MutatorResultDetails,\n {type: 'success'}\n>;\nexport type MutatorResultErrorDetails = Extract<\n MutatorResultDetails,\n {type: 'error'}\n>;\n\nexport type MutatorResult = {\n client: Promise<MutatorResultDetails & {}>;\n server: Promise<MutatorResultDetails & {}>;\n} & {};\n\nexport type CustomMutatorImpl<\n S extends Schema,\n TWrappedTransaction = unknown,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n TArgs = any,\n Context = unknown,\n> = (\n tx: Transaction<S, TWrappedTransaction>,\n // TODO: many args. See commit: 52657c2f934b4a458d628ea77e56ce92b61eb3c6 which did have many args.\n // The issue being that it will be a protocol change to support varargs.\n args: TArgs,\n ctx: Context,\n) => Promise<ReadonlyJSONValue | void>;\n\n/**\n * The shape exposed on the `Zero.mutate` instance.\n * The signature of a custom mutator takes a `transaction` as its first arg\n * but the user does not provide this arg when calling the mutator.\n *\n * This utility strips the `tx` arg from the user's custom mutator signatures.\n * Supports arbitrary depth nesting of namespaces.\n */\nexport type MakeCustomMutatorInterfaces<\n S extends Schema,\n MD extends CustomMutatorDefs,\n TContext,\n> = {\n readonly [NamespaceOrName in keyof MD]: MD[NamespaceOrName] extends (\n tx: Transaction<S>,\n ...args: infer Args\n ) => Promise<unknown>\n ? (...args: Args) => MutatorResult\n : MD[NamespaceOrName] extends CustomMutatorDefs\n ? MakeCustomMutatorInterfaces<S, MD[NamespaceOrName], TContext>\n : never;\n};\n\nexport type MakeCustomMutatorInterface<TSchema extends Schema, F> = F extends (\n tx: ClientTransaction<TSchema>,\n ...args: infer Args\n) => Promise<unknown>\n ? (...args: Args) => MutatorResult\n : never;\n\nexport class TransactionImpl<\n TSchema extends Schema = DefaultSchema,\n> implements ClientTransaction<TSchema> {\n readonly location = 'client';\n readonly mutate: TransactionMutate<TSchema>;\n /**\n * @deprecated Use {@link createBuilder} with `tx.run(zql.table.where(...))` instead.\n */\n readonly query: ConditionalSchemaQuery<TSchema>;\n\n readonly #repTx: WriteTransaction;\n readonly #zeroContext: ZeroContext;\n\n constructor(lc: LogContext, repTx: WriteTransaction, schema: TSchema) {\n must(repTx.reason === 'initial' || repTx.reason === 'rebase');\n const txData = getZeroTxData(repTx);\n const ivmBranch = txData.ivmSources as IVMSourceBranch;\n\n this.#repTx = repTx;\n\n const executor = makeCRUDExecutor(repTx, schema, ivmBranch);\n this.mutate = makeTransactionMutate(schema, executor);\n\n const zeroContext = newZeroContext(\n lc,\n txData.ivmSources as IVMSourceBranch,\n );\n\n this.query = createRunnableBuilder(zeroContext, schema);\n\n this.#zeroContext = zeroContext;\n }\n\n get clientID(): ClientID {\n return this.#repTx.clientID;\n }\n\n get mutationID(): number {\n return this.#repTx.mutationID;\n }\n\n get reason(): 'optimistic' | 'rebase' {\n return this.#repTx.reason === 'initial' ? 'optimistic' : 'rebase';\n }\n\n get token(): string | undefined {\n return (this.#repTx as WriteTransactionImpl)[zeroData]?.token;\n }\n\n run<TTable extends keyof TSchema['tables'] & string, TReturn>(\n query: Query<TTable, TSchema, TReturn>,\n options?: RunOptions,\n ): Promise<HumanReadable<TReturn>> {\n return this.#zeroContext.run(query, options);\n }\n}\n\nfunction getZeroTxData(repTx: WriteTransaction): ZeroTxData {\n const txData = must(\n (repTx as WriteTransactionImpl)[zeroData],\n 'zero was not set on replicache internal options!',\n );\n return txData as ZeroTxData;\n}\n\nexport function makeReplicacheMutator<\n S extends Schema,\n TWrappedTransaction,\n Context,\n>(\n lc: LogContext,\n mutator: CustomMutatorImpl<S, TWrappedTransaction>,\n schema: S,\n context: Context,\n): (\n repTx: WriteTransaction,\n args: ReadonlyJSONValue,\n) => Promise<ReadonlyJSONValue | void> {\n return async (repTx: WriteTransaction, args: ReadonlyJSONValue) => {\n const tx = new TransactionImpl(lc, repTx, schema);\n return await mutator(tx, args, context);\n };\n}\n\nfunction assertValidRunOptions(options: RunOptions | undefined): void {\n // TODO(arv): We should enforce this with the type system too.\n assert(\n options?.type !== 'complete' && options?.type !== 'cached',\n 'Cannot wait for cached or complete results in custom mutations',\n );\n}\n\nfunction newZeroContext(lc: LogContext, ivmBranch: IVMSourceBranch) {\n // The forked branch is already populated, so there is nothing to defer.\n return new ZeroContext(\n lc,\n ivmBranch,\n () => emptyFunction,\n () => emptyFunction,\n emptyFunction,\n emptyFunction,\n emptyFunction,\n applyViewUpdates => applyViewUpdates(),\n emptyFunction,\n assertValidRunOptions,\n ).markPipelinesReady();\n}\n"],"mappings":";;;;;;;;;;AAqHA,IAAa,kBAAb,MAEwC;CACtC,WAAoB;CACpB;;;;CAIA;CAEA;CACA;CAEA,YAAY,IAAgB,OAAyB,QAAiB;EACpE,KAAK,MAAM,WAAW,aAAa,MAAM,WAAW,QAAQ;EAC5D,MAAM,SAAS,cAAc,KAAK;EAClC,MAAM,YAAY,OAAO;EAEzB,KAAK,SAAS;EAEd,MAAM,WAAW,iBAAiB,OAAO,QAAQ,SAAS;EAC1D,KAAK,SAAS,sBAAsB,QAAQ,QAAQ;EAEpD,MAAM,cAAc,eAClB,IACA,OAAO,UACT;EAEA,KAAK,QAAQ,sBAAsB,aAAa,MAAM;EAEtD,KAAK,eAAe;CACtB;CAEA,IAAI,WAAqB;EACvB,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,aAAqB;EACvB,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,SAAkC;EACpC,OAAO,KAAK,OAAO,WAAW,YAAY,eAAe;CAC3D;CAEA,IAAI,QAA4B;EAC9B,OAAQ,KAAK,OAAgC,SAAS,EAAE;CAC1D;CAEA,IACE,OACA,SACiC;EACjC,OAAO,KAAK,aAAa,IAAI,OAAO,OAAO;CAC7C;AACF;AAEA,SAAS,cAAc,OAAqC;CAK1D,OAJe,KACZ,MAA+B,WAChC,kDAEK;AACT;AAEA,SAAgB,sBAKd,IACA,SACA,QACA,SAIqC;CACrC,OAAO,OAAO,OAAyB,SAA4B;EAEjE,OAAO,MAAM,QAAQ,IADN,gBAAgB,IAAI,OAAO,MACrB,GAAI,MAAM,OAAO;CACxC;AACF;AAEA,SAAS,sBAAsB,SAAuC;CAEpE,OACE,SAAS,SAAS,cAAc,SAAS,SAAS,UAClD,gEACF;AACF;AAEA,SAAS,eAAe,IAAgB,WAA4B;CAElE,OAAO,IAAI,YACT,IACA,iBACM,qBACA,eACN,eACA,eACA,gBACA,qBAAoB,iBAAiB,GACrC,eACA,qBACF,CAAC,CAAC,mBAAmB;AACvB"}
1
+ {"version":3,"file":"custom.js","names":[],"sources":["../../../../../zero-client/src/client/custom.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {ZeroTxData} from '../../../replicache/src/replicache-options.ts';\nimport type {WriteTransactionImpl} from '../../../replicache/src/transactions.ts';\nimport {zeroData} from '../../../replicache/src/transactions.ts';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {emptyFunction} from '../../../shared/src/sentinels.ts';\nimport type {DefaultSchema} from '../../../zero-types/src/default-types.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {\n makeTransactionMutate,\n type TransactionMutate,\n} from '../../../zql/src/mutate/crud.ts';\nimport type {\n ClientTransaction,\n Transaction,\n} from '../../../zql/src/mutate/custom.ts';\nimport {createRunnableBuilder} from '../../../zql/src/query/create-builder.ts';\nimport {queryIDOf} from '../../../zql/src/query/query-delegate-base.ts';\nimport {asQueryInternals} from '../../../zql/src/query/query-internals.ts';\nimport {\n type HumanReadable,\n type Query,\n type RunOptions,\n} from '../../../zql/src/query/query.ts';\nimport type {ConditionalSchemaQuery} from '../../../zql/src/query/schema-query.ts';\nimport type {ClientID} from '../types/client-state.ts';\nimport {ZeroContext} from './context.ts';\nimport {makeCRUDExecutor} from './crud.ts';\nimport type {IVMSourceBranch} from './ivm-branch.ts';\nimport {toGotQueriesKey} from './keys.ts';\nimport type {WriteTransaction} from './replicache-types.ts';\n\n/**\n * The shape which a user's custom mutator definitions must conform to.\n * Supports arbitrary depth nesting of namespaces.\n */\nexport type CustomMutatorDefs = {\n // oxlint-disable-next-line no-explicit-any\n [namespaceOrKey: string]: CustomMutatorImpl<any> | CustomMutatorDefs;\n};\n\nexport type MutatorResultDetails =\n | {\n readonly type: 'success';\n readonly data?: ReadonlyJSONValue | undefined;\n }\n | {\n readonly type: 'error';\n readonly error:\n | {\n readonly type: 'app';\n readonly message: string;\n readonly details: ReadonlyJSONValue | undefined;\n }\n | {\n readonly type: 'zero';\n readonly message: string;\n };\n };\n\nexport type MutatorResultSuccessDetails = Extract<\n MutatorResultDetails,\n {type: 'success'}\n>;\nexport type MutatorResultErrorDetails = Extract<\n MutatorResultDetails,\n {type: 'error'}\n>;\n\nexport type MutatorResult = {\n client: Promise<MutatorResultDetails & {}>;\n server: Promise<MutatorResultDetails & {}>;\n} & {};\n\nexport type CustomMutatorImpl<\n S extends Schema,\n TWrappedTransaction = unknown,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n TArgs = any,\n Context = unknown,\n> = (\n tx: Transaction<S, TWrappedTransaction>,\n // TODO: many args. See commit: 52657c2f934b4a458d628ea77e56ce92b61eb3c6 which did have many args.\n // The issue being that it will be a protocol change to support varargs.\n args: TArgs,\n ctx: Context,\n) => Promise<ReadonlyJSONValue | void>;\n\n/**\n * The shape exposed on the `Zero.mutate` instance.\n * The signature of a custom mutator takes a `transaction` as its first arg\n * but the user does not provide this arg when calling the mutator.\n *\n * This utility strips the `tx` arg from the user's custom mutator signatures.\n * Supports arbitrary depth nesting of namespaces.\n */\nexport type MakeCustomMutatorInterfaces<\n S extends Schema,\n MD extends CustomMutatorDefs,\n TContext,\n> = {\n readonly [NamespaceOrName in keyof MD]: MD[NamespaceOrName] extends (\n tx: Transaction<S>,\n ...args: infer Args\n ) => Promise<unknown>\n ? (...args: Args) => MutatorResult\n : MD[NamespaceOrName] extends CustomMutatorDefs\n ? MakeCustomMutatorInterfaces<S, MD[NamespaceOrName], TContext>\n : never;\n};\n\nexport type MakeCustomMutatorInterface<TSchema extends Schema, F> = F extends (\n tx: ClientTransaction<TSchema>,\n ...args: infer Args\n) => Promise<unknown>\n ? (...args: Args) => MutatorResult\n : never;\n\nexport class TransactionImpl<\n TSchema extends Schema = DefaultSchema,\n> implements ClientTransaction<TSchema> {\n readonly location = 'client';\n readonly mutate: TransactionMutate<TSchema>;\n /**\n * @deprecated Use {@link createBuilder} with `tx.run(zql.table.where(...))` instead.\n */\n readonly query: ConditionalSchemaQuery<TSchema>;\n\n readonly #repTx: WriteTransaction;\n readonly #zeroContext: ZeroContext;\n\n constructor(lc: LogContext, repTx: WriteTransaction, schema: TSchema) {\n must(repTx.reason === 'initial' || repTx.reason === 'rebase');\n const txData = getZeroTxData(repTx);\n const ivmBranch = txData.ivmSources as IVMSourceBranch;\n\n this.#repTx = repTx;\n\n const executor = makeCRUDExecutor(repTx, schema, ivmBranch);\n this.mutate = makeTransactionMutate(schema, executor);\n\n const zeroContext = newZeroContext(\n lc,\n txData.ivmSources as IVMSourceBranch,\n repTx,\n );\n\n this.query = createRunnableBuilder(zeroContext, schema);\n\n this.#zeroContext = zeroContext;\n }\n\n get clientID(): ClientID {\n return this.#repTx.clientID;\n }\n\n get mutationID(): number {\n return this.#repTx.mutationID;\n }\n\n get reason(): 'optimistic' | 'rebase' {\n return this.#repTx.reason === 'initial' ? 'optimistic' : 'rebase';\n }\n\n get token(): string | undefined {\n return (this.#repTx as WriteTransactionImpl)[zeroData]?.token;\n }\n\n run<TTable extends keyof TSchema['tables'] & string, TReturn>(\n query: Query<TTable, TSchema, TReturn>,\n options?: RunOptions,\n ): Promise<HumanReadable<TReturn>> {\n return this.#zeroContext.run(query, options);\n }\n}\n\nfunction getZeroTxData(repTx: WriteTransaction): ZeroTxData {\n const txData = must(\n (repTx as WriteTransactionImpl)[zeroData],\n 'zero was not set on replicache internal options!',\n );\n return txData as ZeroTxData;\n}\n\nexport function makeReplicacheMutator<\n S extends Schema,\n TWrappedTransaction,\n Context,\n>(\n lc: LogContext,\n mutator: CustomMutatorImpl<S, TWrappedTransaction>,\n schema: S,\n context: Context,\n): (\n repTx: WriteTransaction,\n args: ReadonlyJSONValue,\n) => Promise<ReadonlyJSONValue | void> {\n return async (repTx: WriteTransaction, args: ReadonlyJSONValue) => {\n const tx = new TransactionImpl(lc, repTx, schema);\n return await mutator(tx, args, context);\n };\n}\n\nfunction assertValidRunOptions(options: RunOptions | undefined): void {\n // TODO(arv): We should enforce this with the type system too.\n assert(\n options?.type !== 'complete' && options?.type !== 'cached',\n 'Cannot wait for cached or complete results in custom mutations',\n );\n}\n\nfunction newZeroContext(\n lc: LogContext,\n ivmBranch: IVMSourceBranch,\n repTx: WriteTransaction,\n) {\n // The forked branch is already populated, so there is nothing to defer.\n return new MutatorContext(lc, ivmBranch, repTx).markPipelinesReady();\n}\n\nfunction newContextArgs(lc: LogContext, ivmBranch: IVMSourceBranch) {\n return [\n lc,\n ivmBranch,\n () => emptyFunction,\n () => emptyFunction,\n emptyFunction,\n emptyFunction,\n emptyFunction,\n (applyViewUpdates: () => void) => applyViewUpdates(),\n emptyFunction,\n assertValidRunOptions,\n ] as const;\n}\n\n/**\n * The query delegate of a custom mutator. A query whose result the server has\n * sent reads its aggregates from the rows the server synced, with this\n * client's pending changes applied, as a view of it does. Any other query has\n * no synced aggregate rows to read, so it computes its aggregates from the\n * rows in the mutator's branch, like every other read the mutator makes. The\n * server's run of the mutator computes them from all the rows.\n */\nclass MutatorContext extends ZeroContext {\n override readonly aggregatesFromSource = false;\n readonly #repTx: WriteTransaction;\n readonly #synced: ZeroContext;\n\n constructor(\n lc: LogContext,\n ivmBranch: IVMSourceBranch,\n repTx: WriteTransaction,\n ) {\n super(...newContextArgs(lc, ivmBranch));\n this.#repTx = repTx;\n this.#synced = new ZeroContext(\n ...newContextArgs(lc, ivmBranch),\n ).markPipelinesReady();\n }\n\n override async run<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends Schema,\n TReturn,\n >(\n query: Query<TTable, TSchema, TReturn>,\n options?: RunOptions,\n ): Promise<HumanReadable<TReturn>> {\n const queryID = queryIDOf(asQueryInternals(query));\n return (await this.#repTx.has(toGotQueriesKey(queryID)))\n ? this.#synced.run(query, options)\n : super.run(query, options);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAwHA,IAAa,kBAAb,MAEwC;CACtC,WAAoB;CACpB;;;;CAIA;CAEA;CACA;CAEA,YAAY,IAAgB,OAAyB,QAAiB;EACpE,KAAK,MAAM,WAAW,aAAa,MAAM,WAAW,QAAQ;EAC5D,MAAM,SAAS,cAAc,KAAK;EAClC,MAAM,YAAY,OAAO;EAEzB,KAAK,SAAS;EAEd,MAAM,WAAW,iBAAiB,OAAO,QAAQ,SAAS;EAC1D,KAAK,SAAS,sBAAsB,QAAQ,QAAQ;EAEpD,MAAM,cAAc,eAClB,IACA,OAAO,YACP,KACF;EAEA,KAAK,QAAQ,sBAAsB,aAAa,MAAM;EAEtD,KAAK,eAAe;CACtB;CAEA,IAAI,WAAqB;EACvB,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,aAAqB;EACvB,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,SAAkC;EACpC,OAAO,KAAK,OAAO,WAAW,YAAY,eAAe;CAC3D;CAEA,IAAI,QAA4B;EAC9B,OAAQ,KAAK,OAAgC,SAAS,EAAE;CAC1D;CAEA,IACE,OACA,SACiC;EACjC,OAAO,KAAK,aAAa,IAAI,OAAO,OAAO;CAC7C;AACF;AAEA,SAAS,cAAc,OAAqC;CAK1D,OAJe,KACZ,MAA+B,WAChC,kDAEK;AACT;AAEA,SAAgB,sBAKd,IACA,SACA,QACA,SAIqC;CACrC,OAAO,OAAO,OAAyB,SAA4B;EAEjE,OAAO,MAAM,QAAQ,IADN,gBAAgB,IAAI,OAAO,MACrB,GAAI,MAAM,OAAO;CACxC;AACF;AAEA,SAAS,sBAAsB,SAAuC;CAEpE,OACE,SAAS,SAAS,cAAc,SAAS,SAAS,UAClD,gEACF;AACF;AAEA,SAAS,eACP,IACA,WACA,OACA;CAEA,OAAO,IAAI,eAAe,IAAI,WAAW,KAAK,CAAC,CAAC,mBAAmB;AACrE;AAEA,SAAS,eAAe,IAAgB,WAA4B;CAClE,OAAO;EACL;EACA;QACM;QACA;EACN;EACA;EACA;GACC,qBAAiC,iBAAiB;EACnD;EACA;CACF;AACF;;;;;;;;;AAUA,IAAM,iBAAN,cAA6B,YAAY;CACvC,uBAAyC;CACzC;CACA;CAEA,YACE,IACA,WACA,OACA;EACA,MAAM,GAAG,eAAe,IAAI,SAAS,CAAC;EACtC,KAAK,SAAS;EACd,KAAK,UAAU,IAAI,YACjB,GAAG,eAAe,IAAI,SAAS,CACjC,CAAC,CAAC,mBAAmB;CACvB;CAEA,MAAe,IAKb,OACA,SACiC;EACjC,MAAM,UAAU,UAAU,iBAAiB,KAAK,CAAC;EACjD,OAAQ,MAAM,KAAK,OAAO,IAAI,gBAAgB,OAAO,CAAC,IAClD,KAAK,QAAQ,IAAI,OAAO,OAAO,IAC/B,MAAM,IAAI,OAAO,OAAO;CAC9B;AACF"}
@@ -1,9 +1,14 @@
1
- import type { NoIndexDiff } from '../../../replicache/src/btree/node.ts';
1
+ import type { InternalDiffOperation, NoIndexDiff } from '../../../replicache/src/btree/node.ts';
2
2
  import type { LazyStore } from '../../../replicache/src/dag/lazy-store.ts';
3
3
  import { type Store } from '../../../replicache/src/dag/store.ts';
4
4
  import type { Hash } from '../../../replicache/src/hash.ts';
5
5
  import type { ZeroReadOptions } from '../../../replicache/src/replicache-options.ts';
6
+ import type { AggregateFunction, CompoundKey } from '../../../zero-protocol/src/ast.ts';
7
+ import type { Row } from '../../../zero-protocol/src/data.ts';
8
+ import type { PrimaryKey } from '../../../zero-protocol/src/primary-key.ts';
6
9
  import type { TableSchema } from '../../../zero-schema/src/table-schema.ts';
10
+ import { type SchemaValue } from '../../../zero-types/src/schema-value.ts';
11
+ import { type AggregateOptimisticDelta } from '../../../zql/src/builder/builder.ts';
7
12
  import { MemorySource } from '../../../zql/src/ivm/memory-source.ts';
8
13
  /**
9
14
  * Replicache needs to rebase mutations onto different
@@ -21,11 +26,61 @@ import { MemorySource } from '../../../zql/src/ivm/memory-source.ts';
21
26
  * ever be a few outstanding diffs to apply given Zero is meant
22
27
  * to be run in a connected state.
23
28
  */
29
+ /**
30
+ * Metadata for optimistically updating a synced aggregate when a child row is
31
+ * locally mutated. Registered when a relationship-aggregate query materializes.
32
+ */
33
+ export type OptimisticAggregate = {
34
+ /** The synthetic aggregate table, e.g. `aggregate:<queryID>:<alias>`. */
35
+ readonly aggTableName: string;
36
+ /** The correlation columns of a child row, as the client names them. */
37
+ readonly childField: readonly string[];
38
+ /**
39
+ * The same columns as the synced aggregate rows name them (server names), in
40
+ * the same order. An aggregate row's key is built from these.
41
+ */
42
+ readonly keyField: readonly string[];
43
+ /** The aggregate function (`count`/`sum`/`avg` are optimistically deltaed). */
44
+ readonly fn: AggregateFunction;
45
+ /** The aggregated field, for `sum`/`avg` (`undefined` for `count`). */
46
+ readonly field: string | undefined;
47
+ /** A JSON path into `field` (see `Aggregate.path`). */
48
+ readonly path: readonly (string | number)[] | undefined;
49
+ /**
50
+ * The aggregate's `where`, compiled to a per-row predicate. A child row
51
+ * contributes only when it returns true; `undefined` means no filter (every
52
+ * child contributes). Only set when the whole `where` is per-row evaluable —
53
+ * a correlated subquery in the `where` makes the aggregate
54
+ * server-authoritative (it is never registered for optimism at all).
55
+ */
56
+ readonly predicate: ((row: Row) => boolean) | undefined;
57
+ /** The rows are a grouped query's groups (see `AggregateOptimisticDelta`). */
58
+ readonly grouped: boolean;
59
+ };
24
60
  export declare class IVMSourceBranch {
25
61
  #private;
26
62
  hash: Hash | undefined;
27
- constructor(tables: Record<string, TableSchema>, hash?: Hash, sources?: Map<string, MemorySource | undefined>);
63
+ constructor(tables: Record<string, TableSchema>, hash?: Hash, sources?: Map<string, MemorySource | undefined>, aggregateOptimism?: Map<string, Map<string, OptimisticAggregate>>, isFork?: boolean);
28
64
  getSource(name: string): MemorySource | undefined;
65
+ /**
66
+ * Get-or-create the synthetic source for a *relationship* aggregate
67
+ * (`aggregate:<queryID>:<alias>`), whose shape (the correlation-key columns +
68
+ * value) cannot be derived from the name. The query builder supplies the
69
+ * schema when a query materializes; {@link applyDiffs} supplies an inferred
70
+ * one when persisted rows are replayed before the query re-materializes (e.g.
71
+ * on reload). Idempotent — the first caller wins.
72
+ */
73
+ getOrCreateAggregateSource(name: string, columns: Record<string, SchemaValue>, primaryKey: PrimaryKey, optimistic?: AggregateOptimisticDelta): MemorySource;
74
+ /**
75
+ * The server names of `columns` of `table`, in the same order. A column with
76
+ * no server name of its own, or of a table not in the schema, keeps its name.
77
+ */
78
+ serverColumnNames(table: string, columns: CompoundKey): CompoundKey;
79
+ /**
80
+ * The optimistic-aggregate registrations whose child rows live in `table`.
81
+ * An optimistic insert/delete of such a child row bumps these aggregates.
82
+ */
83
+ getOptimisticAggregates(table: string): Iterable<OptimisticAggregate>;
29
84
  clear(): void;
30
85
  /**
31
86
  * Mutates the current branch, advancing it to the new head
@@ -47,4 +102,9 @@ export declare class IVMSourceBranch {
47
102
  fork(): IVMSourceBranch;
48
103
  }
49
104
  export declare function initFromStore(branch: IVMSourceBranch, hash: Hash, store: Store): Promise<void>;
105
+ /**
106
+ * An `add` diff for each row at `hash` that the IVM sources hold: the table
107
+ * rows under `e/` and the aggregate rows under `a/`, in key order.
108
+ */
109
+ export declare function readRowDiffs(store: Store, hash: Hash): Promise<InternalDiffOperation[]>;
50
110
  //# sourceMappingURL=ivm-branch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ivm-branch.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/ivm-branch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,WAAW,EACZ,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,2CAA2C,CAAC;AACzE,OAAO,EAAY,KAAK,KAAK,EAAC,MAAM,sCAAsC,CAAC;AAG3E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,iCAAiC,CAAC;AAC1D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+CAA+C,CAAC;AASnF,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0CAA0C,CAAC;AAC1E,OAAO,EAAC,YAAY,EAAC,MAAM,uCAAuC,CAAC;AAQnE;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,eAAe;;IAI1B,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IAEvB,YACE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACnC,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,GAAE,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAAa,EAK3D;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAahD;IAED,KAAK,SAEJ;IAQD;;;OAGG;IACH,OAAO,CAAC,YAAY,EAAE,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,QAiBxE;IAED;;OAEG;IACG,UAAU,CACd,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,IAAI,EACjB,WAAW,CAAC,EAAE,eAAe,GAC5B,OAAO,CAAC,eAAe,CAAC,CAY1B;IAED;;;;;;;OAOG;IACH,IAAI,oBAaH;CACF;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,iBAkBb"}
1
+ {"version":3,"file":"ivm-branch.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/ivm-branch.ts"],"names":[],"mappings":"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"}