@rocicorp/zero 0.25.0-canary.10 → 0.25.0-canary.11

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 (184) hide show
  1. package/out/analyze-query/src/run-ast.d.ts.map +1 -1
  2. package/out/analyze-query/src/run-ast.js +3 -7
  3. package/out/analyze-query/src/run-ast.js.map +1 -1
  4. package/out/shared/src/iterables.d.ts +0 -1
  5. package/out/shared/src/iterables.d.ts.map +1 -1
  6. package/out/shared/src/iterables.js +0 -34
  7. package/out/shared/src/iterables.js.map +1 -1
  8. package/out/zero/package.json.js +1 -1
  9. package/out/zero-cache/src/config/zero-config.d.ts +0 -4
  10. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  11. package/out/zero-cache/src/config/zero-config.js +5 -15
  12. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  13. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  14. package/out/zero-cache/src/db/transaction-pool.js +3 -6
  15. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  16. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  17. package/out/zero-cache/src/server/change-streamer.js +2 -8
  18. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  19. package/out/zero-cache/src/services/analyze.d.ts +1 -1
  20. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  21. package/out/zero-cache/src/services/analyze.js +50 -37
  22. package/out/zero-cache/src/services/analyze.js.map +1 -1
  23. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  24. package/out/zero-cache/src/services/change-source/pg/change-source.js +62 -42
  25. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  26. package/out/zero-cache/src/services/change-source/protocol/current/control.d.ts +1 -0
  27. package/out/zero-cache/src/services/change-source/protocol/current/control.d.ts.map +1 -1
  28. package/out/zero-cache/src/services/change-source/protocol/current/control.js +5 -1
  29. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  30. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +2 -0
  31. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  32. package/out/zero-cache/src/services/change-source/protocol/current/json.d.ts +8 -0
  33. package/out/zero-cache/src/services/change-source/protocol/current/json.d.ts.map +1 -0
  34. package/out/zero-cache/src/services/change-source/protocol/current/json.js +19 -0
  35. package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -0
  36. package/out/zero-cache/src/services/change-source/protocol/current.d.ts +1 -0
  37. package/out/zero-cache/src/services/change-source/protocol/current.d.ts.map +1 -1
  38. package/out/zero-cache/src/services/change-source/protocol/current.js +3 -0
  39. package/out/zero-cache/src/services/change-source/protocol/current.js.map +1 -1
  40. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +0 -2
  41. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  42. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +0 -5
  43. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  44. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  45. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +8 -1
  46. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  47. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  48. package/out/zero-cache/src/services/change-streamer/storer.js +2 -3
  49. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  50. package/out/zero-cache/src/services/http-service.d.ts +0 -1
  51. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  52. package/out/zero-cache/src/services/http-service.js +0 -4
  53. package/out/zero-cache/src/services/http-service.js.map +1 -1
  54. package/out/zero-cache/src/services/replicator/replication-status.d.ts +2 -0
  55. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
  56. package/out/zero-cache/src/services/replicator/replication-status.js +14 -1
  57. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  58. package/out/zero-cache/src/services/run-ast.d.ts +1 -1
  59. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  60. package/out/zero-cache/src/services/run-ast.js +5 -1
  61. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  62. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +6 -4
  63. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  64. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +71 -23
  65. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  66. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +1 -1
  67. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  68. package/out/zero-cache/src/services/view-syncer/view-syncer.js +10 -6
  69. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  70. package/out/zero-client/src/client/version.js +1 -1
  71. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  72. package/out/zero-client/src/client/zero.js +6 -2
  73. package/out/zero-client/src/client/zero.js.map +1 -1
  74. package/out/zero-events/src/status.d.ts +1 -1
  75. package/out/zero-events/src/status.d.ts.map +1 -1
  76. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  77. package/out/zero-solid/src/solid-view.js +4 -3
  78. package/out/zero-solid/src/solid-view.js.map +1 -1
  79. package/out/zql/src/builder/builder.d.ts.map +1 -1
  80. package/out/zql/src/builder/builder.js +0 -1
  81. package/out/zql/src/builder/builder.js.map +1 -1
  82. package/out/zql/src/ivm/array-view.d.ts +1 -1
  83. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  84. package/out/zql/src/ivm/array-view.js +2 -1
  85. package/out/zql/src/ivm/array-view.js.map +1 -1
  86. package/out/zql/src/ivm/data.d.ts +7 -2
  87. package/out/zql/src/ivm/data.d.ts.map +1 -1
  88. package/out/zql/src/ivm/data.js +3 -0
  89. package/out/zql/src/ivm/data.js.map +1 -1
  90. package/out/zql/src/ivm/exists.d.ts +3 -2
  91. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  92. package/out/zql/src/ivm/exists.js +29 -76
  93. package/out/zql/src/ivm/exists.js.map +1 -1
  94. package/out/zql/src/ivm/fan-in.d.ts +2 -0
  95. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  96. package/out/zql/src/ivm/fan-in.js +4 -0
  97. package/out/zql/src/ivm/fan-in.js.map +1 -1
  98. package/out/zql/src/ivm/fan-out.d.ts +2 -0
  99. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  100. package/out/zql/src/ivm/fan-out.js +4 -0
  101. package/out/zql/src/ivm/fan-out.js.map +1 -1
  102. package/out/zql/src/ivm/filter-operators.d.ts +6 -2
  103. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  104. package/out/zql/src/ivm/filter-operators.js +16 -0
  105. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  106. package/out/zql/src/ivm/filter.d.ts +2 -0
  107. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  108. package/out/zql/src/ivm/filter.js +4 -0
  109. package/out/zql/src/ivm/filter.js.map +1 -1
  110. package/out/zql/src/ivm/flipped-join.d.ts +1 -1
  111. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  112. package/out/zql/src/ivm/flipped-join.js +26 -13
  113. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  114. package/out/zql/src/ivm/join-utils.d.ts +2 -1
  115. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  116. package/out/zql/src/ivm/join-utils.js +8 -0
  117. package/out/zql/src/ivm/join-utils.js.map +1 -1
  118. package/out/zql/src/ivm/join.d.ts +1 -1
  119. package/out/zql/src/ivm/join.d.ts.map +1 -1
  120. package/out/zql/src/ivm/join.js +32 -13
  121. package/out/zql/src/ivm/join.js.map +1 -1
  122. package/out/zql/src/ivm/memory-source.d.ts +1 -1
  123. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  124. package/out/zql/src/ivm/memory-source.js +8 -1
  125. package/out/zql/src/ivm/memory-source.js.map +1 -1
  126. package/out/zql/src/ivm/operator.d.ts +9 -1
  127. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  128. package/out/zql/src/ivm/operator.js +8 -0
  129. package/out/zql/src/ivm/operator.js.map +1 -1
  130. package/out/zql/src/ivm/push-accumulated.d.ts +1 -1
  131. package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
  132. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  133. package/out/zql/src/ivm/skip.d.ts +1 -1
  134. package/out/zql/src/ivm/skip.d.ts.map +1 -1
  135. package/out/zql/src/ivm/skip.js +7 -1
  136. package/out/zql/src/ivm/skip.js.map +1 -1
  137. package/out/zql/src/ivm/take.d.ts +1 -1
  138. package/out/zql/src/ivm/take.d.ts.map +1 -1
  139. package/out/zql/src/ivm/take.js +87 -59
  140. package/out/zql/src/ivm/take.js.map +1 -1
  141. package/out/zql/src/ivm/union-fan-in.d.ts +2 -1
  142. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  143. package/out/zql/src/ivm/union-fan-in.js +67 -5
  144. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  145. package/out/zql/src/ivm/union-fan-out.d.ts +1 -1
  146. package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
  147. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  148. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  149. package/out/zql/src/ivm/view-apply-change.js +3 -2
  150. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  151. package/out/zql/src/query/abstract-query.d.ts +43 -0
  152. package/out/zql/src/query/abstract-query.d.ts.map +1 -0
  153. package/out/zql/src/query/abstract-query.js +408 -0
  154. package/out/zql/src/query/abstract-query.js.map +1 -0
  155. package/out/zql/src/query/create-builder.d.ts +2 -0
  156. package/out/zql/src/query/create-builder.d.ts.map +1 -1
  157. package/out/zql/src/query/create-builder.js +7 -2
  158. package/out/zql/src/query/create-builder.js.map +1 -1
  159. package/out/zql/src/query/measure-push-operator.d.ts +1 -1
  160. package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
  161. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  162. package/out/zql/src/query/query-delegate-base.d.ts +7 -1
  163. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  164. package/out/zql/src/query/query-delegate-base.js +132 -2
  165. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  166. package/out/zql/src/query/query-impl.d.ts +5 -39
  167. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  168. package/out/zql/src/query/query-impl.js +3 -521
  169. package/out/zql/src/query/query-impl.js.map +1 -1
  170. package/out/zql/src/query/query.d.ts +21 -0
  171. package/out/zql/src/query/query.d.ts.map +1 -1
  172. package/out/zql/src/query/runnable-query-impl.d.ts +22 -0
  173. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -0
  174. package/out/zql/src/query/runnable-query-impl.js +60 -0
  175. package/out/zql/src/query/runnable-query-impl.js.map +1 -0
  176. package/out/zql/src/query/static-query.d.ts +1 -1
  177. package/out/zql/src/query/static-query.d.ts.map +1 -1
  178. package/out/zql/src/query/static-query.js +1 -1
  179. package/out/zql/src/query/static-query.js.map +1 -1
  180. package/out/zqlite/src/table-source.d.ts +7 -1
  181. package/out/zqlite/src/table-source.d.ts.map +1 -1
  182. package/out/zqlite/src/table-source.js +34 -14
  183. package/out/zqlite/src/table-source.js.map +1 -1
  184. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"push-accumulated.js","sources":["../../../../../zql/src/ivm/push-accumulated.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\n/**\n * # pushAccumulatedChanges\n *\n * Pushes the changes that were accumulated by\n * [fan-out, fan-in] or [ufo, ufi] sub-graphs.\n *\n * This function is called at the end of the sub-graph.\n *\n * The sub-graphs represents `OR`s.\n *\n * Changes that can enter the subgraphs:\n * 1. child (due to exist joins being above the sub-graph)\n * 2. add\n * 3. remove\n * 4. edit\n *\n * # Changes that can exit into `pushAccumulatedChanges`:\n *\n * ## Child\n * If a `child` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `child` change\n * - stop the `child` change (e.g., filter)\n * - convert it to an `add` or `remove` (e.g., exists filter)\n *\n * ## Add\n * If an `add` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `add` change\n * - hide the change (e.g., filter)\n *\n * ## Remove\n * If a `remove` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `remove` change\n * - hide the change (e.g., filter)\n *\n * ## Edit\n * If an `edit` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `edit` change\n * - convert it to an `add` (e.g., filter where old didn't match but new does)\n * - convert it to a `remove` (e.g., filter where old matched but new doesn't)\n *\n * This results in some invariants:\n * - an add coming in will only create adds coming out\n * - a remove coming in will only create removes coming out\n * - an edit coming in can create adds, removes, and edits coming out\n * - a child coming in can create adds, removes, and children coming out\n *\n * # Return of `pushAccumulatedChanges`\n *\n * This function will only push a single change.\n * Given the above invariants, how is this possible?\n *\n * An add that becomes many `adds` results in a single add\n * as the `add` is the same row across all adds. Branches do not change the row.\n *\n * A remove that becomes many `removes` results in a single remove\n * for the same reason.\n *\n * If a child enters and exits, it takes precedence over all other changes.\n * If a child enters and is converted only to add and remove it exits as an edit.\n * If a child enters and is converted to only add or only remove, it exits as that change.\n *\n * If an edit enters and is converted to add and remove it exits as an edit.\n * If an edit enters and is converted to only add or only remove, it exits as that change.\n * If an edit enters and exits as edits only, it exits as a single edit.\n */\nexport function pushAccumulatedChanges(\n accumulatedPushes: Change[],\n output: Output,\n pusher: InputBase,\n fanOutChangeType: Change['type'],\n mergeRelationships: (existing: Change, incoming: Change) => Change,\n addEmptyRelationships: (change: Change) => Change,\n) {\n if (accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return;\n }\n\n // collapse down to a single change per type\n const candidatesToPush = new Map<Change['type'], Change>();\n for (const change of accumulatedPushes) {\n if (fanOutChangeType === 'child' && change.type !== 'child') {\n assert(\n candidatesToPush.has(change.type) === false,\n () =>\n `Fan-in:child expected at most one ${change.type} when fan-out is of type child`,\n );\n }\n\n const existing = candidatesToPush.get(change.type);\n let mergedChange = change;\n if (existing) {\n // merge in relationships\n mergedChange = mergeRelationships(existing, change);\n }\n candidatesToPush.set(change.type, mergedChange);\n }\n\n accumulatedPushes.length = 0;\n\n const types = [...candidatesToPush.keys()];\n /**\n * Based on the received `fanOutChangeType` only certain output types are valid.\n *\n * - remove must result in all removes\n * - add must result in all adds\n * - edit must result in add or removes or edits\n * - child must result in a single add or single remove or many child changes\n * - Single add or remove because the relationship will be unique to one exist check within the fan-out,fan-in sub-graph\n * - Many child changes because other operators may preserve the child change\n */\n switch (fanOutChangeType) {\n case 'remove':\n assert(\n types.length === 1 && types[0] === 'remove',\n 'Fan-in:remove expected all removes',\n );\n output.push(\n addEmptyRelationships(must(candidatesToPush.get('remove'))),\n pusher,\n );\n return;\n case 'add':\n assert(\n types.length === 1 && types[0] === 'add',\n 'Fan-in:add expected all adds',\n );\n output.push(\n addEmptyRelationships(must(candidatesToPush.get('add'))),\n pusher,\n );\n return;\n case 'edit': {\n assert(\n types.every(\n type => type === 'add' || type === 'remove' || type === 'edit',\n ),\n 'Fan-in:edit expected all adds, removes, or edits',\n );\n const addChange = candidatesToPush.get('add');\n const removeChange = candidatesToPush.get('remove');\n let editChange = candidatesToPush.get('edit');\n\n // If an `edit` is present, it supersedes `add` and `remove`\n // as it semantically represents both.\n if (editChange) {\n if (addChange) {\n editChange = mergeRelationships(editChange, addChange);\n }\n if (removeChange) {\n editChange = mergeRelationships(editChange, removeChange);\n }\n output.push(addEmptyRelationships(editChange), pusher);\n return;\n }\n\n // If `edit` didn't make it through but both `add` and `remove` did,\n // convert back to an edit.\n //\n // When can this happen?\n //\n // EDIT old: a=1, new: a=2\n // |\n // FanOut\n // / \\\n // a=1 a=2\n // | |\n // remove add\n // \\ /\n // FanIn\n //\n // The left filter converts the edit into a remove.\n // The right filter converts the edit into an add.\n if (addChange && removeChange) {\n output.push(\n addEmptyRelationships({\n type: 'edit',\n node: addChange.node,\n oldNode: removeChange.node,\n } as const),\n pusher,\n );\n return;\n }\n\n output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n case 'child': {\n assert(\n types.every(\n type =>\n type === 'add' || // exists can change child to add or remove\n type === 'remove' || // exists can change child to add or remove\n type === 'child', // other operators may preserve the child change\n ),\n 'Fan-in:child expected all adds, removes, or children',\n );\n assert(\n types.length <= 2,\n 'Fan-in:child expected at most 2 types on a child change from fan-out',\n );\n\n // If any branch preserved the original child change, that takes precedence over all other changes.\n const childChange = candidatesToPush.get('child');\n if (childChange) {\n output.push(childChange, pusher);\n return;\n }\n\n const addChange = candidatesToPush.get('add');\n const removeChange = candidatesToPush.get('remove');\n\n assert(\n addChange === undefined || removeChange === undefined,\n 'Fan-in:child expected either add or remove, not both',\n );\n\n output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n default:\n fanOutChangeType satisfies never;\n }\n}\n\n/**\n * Puts relationships from `right` into `left` if they don't already exist in `left`.\n */\nexport function mergeRelationships(left: Change, right: Change): Change {\n // change types will always match\n // unless we have an edit on the left\n // then the right could be edit, add, or remove\n if (left.type === right.type) {\n switch (left.type) {\n case 'add': {\n return {\n type: 'add',\n node: {\n row: left.node.row,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n };\n }\n case 'remove': {\n return {\n type: 'remove',\n node: {\n row: left.node.row,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n };\n }\n case 'edit': {\n assert(right.type === 'edit');\n // merge edits into a single edit\n return {\n type: 'edit',\n node: {\n row: left.node.row,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n oldNode: {\n row: left.oldNode.row,\n relationships: {\n ...right.oldNode.relationships,\n ...left.oldNode.relationships,\n },\n },\n };\n }\n }\n }\n\n // left is always an edit here\n assert(left.type === 'edit');\n switch (right.type) {\n case 'add': {\n return {\n type: 'edit',\n node: {\n ...left.node,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n oldNode: left.oldNode,\n };\n }\n case 'remove': {\n return {\n type: 'edit',\n node: left.node,\n oldNode: {\n ...left.oldNode,\n relationships: {\n ...right.node.relationships,\n ...left.oldNode.relationships,\n },\n },\n };\n }\n }\n\n unreachable();\n}\n\nexport function makeAddEmptyRelationships(\n schema: SourceSchema,\n): (change: Change) => Change {\n return (change: Change): Change => {\n if (Object.keys(schema.relationships).length === 0) {\n return change;\n }\n\n switch (change.type) {\n case 'add':\n case 'remove': {\n const ret = {\n ...change,\n node: {\n ...change.node,\n relationships: {\n ...change.node.relationships,\n },\n },\n };\n\n mergeEmpty(ret.node.relationships, Object.keys(schema.relationships));\n\n return ret;\n }\n case 'edit': {\n const ret = {\n ...change,\n node: {\n ...change.node,\n relationships: {\n ...change.node.relationships,\n },\n },\n oldNode: {\n ...change.oldNode,\n relationships: {\n ...change.oldNode.relationships,\n },\n },\n };\n\n mergeEmpty(ret.node.relationships, Object.keys(schema.relationships));\n mergeEmpty(\n ret.oldNode.relationships,\n Object.keys(schema.relationships),\n );\n\n return ret;\n }\n case 'child':\n return change; // children only have relationships along the path to the change\n }\n };\n}\n\n/**\n * For each relationship in `schema` that does not exist\n * in `relationships`, add it with an empty stream.\n *\n * This modifies the `relationships` object in place.\n */\nexport function mergeEmpty(\n relationships: Record<string, () => Stream<Node>>,\n relationshipNames: string[],\n) {\n for (const relName of relationshipNames) {\n if (relationships[relName] === undefined) {\n relationships[relName] = () => emptyArray;\n }\n }\n}\n"],"names":["mergeRelationships"],"mappings":"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"}
1
+ {"version":3,"file":"push-accumulated.js","sources":["../../../../../zql/src/ivm/push-accumulated.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\n/**\n * # pushAccumulatedChanges\n *\n * Pushes the changes that were accumulated by\n * [fan-out, fan-in] or [ufo, ufi] sub-graphs.\n *\n * This function is called at the end of the sub-graph.\n *\n * The sub-graphs represents `OR`s.\n *\n * Changes that can enter the subgraphs:\n * 1. child (due to exist joins being above the sub-graph)\n * 2. add\n * 3. remove\n * 4. edit\n *\n * # Changes that can exit into `pushAccumulatedChanges`:\n *\n * ## Child\n * If a `child` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `child` change\n * - stop the `child` change (e.g., filter)\n * - convert it to an `add` or `remove` (e.g., exists filter)\n *\n * ## Add\n * If an `add` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `add` change\n * - hide the change (e.g., filter)\n *\n * ## Remove\n * If a `remove` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `remove` change\n * - hide the change (e.g., filter)\n *\n * ## Edit\n * If an `edit` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `edit` change\n * - convert it to an `add` (e.g., filter where old didn't match but new does)\n * - convert it to a `remove` (e.g., filter where old matched but new doesn't)\n *\n * This results in some invariants:\n * - an add coming in will only create adds coming out\n * - a remove coming in will only create removes coming out\n * - an edit coming in can create adds, removes, and edits coming out\n * - a child coming in can create adds, removes, and children coming out\n *\n * # Return of `pushAccumulatedChanges`\n *\n * This function will only push a single change.\n * Given the above invariants, how is this possible?\n *\n * An add that becomes many `adds` results in a single add\n * as the `add` is the same row across all adds. Branches do not change the row.\n *\n * A remove that becomes many `removes` results in a single remove\n * for the same reason.\n *\n * If a child enters and exits, it takes precedence over all other changes.\n * If a child enters and is converted only to add and remove it exits as an edit.\n * If a child enters and is converted to only add or only remove, it exits as that change.\n *\n * If an edit enters and is converted to add and remove it exits as an edit.\n * If an edit enters and is converted to only add or only remove, it exits as that change.\n * If an edit enters and exits as edits only, it exits as a single edit.\n */\nexport function pushAccumulatedChanges(\n accumulatedPushes: Change[],\n output: Output,\n pusher: InputBase,\n fanOutChangeType: Change['type'],\n mergeRelationships: (existing: Change, incoming: Change) => Change,\n addEmptyRelationships: (change: Change) => Change,\n) {\n if (accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return;\n }\n\n // collapse down to a single change per type\n const candidatesToPush = new Map<Change['type'], Change>();\n for (const change of accumulatedPushes) {\n if (fanOutChangeType === 'child' && change.type !== 'child') {\n assert(\n candidatesToPush.has(change.type) === false,\n () =>\n `Fan-in:child expected at most one ${change.type} when fan-out is of type child`,\n );\n }\n\n const existing = candidatesToPush.get(change.type);\n let mergedChange = change;\n if (existing) {\n // merge in relationships\n mergedChange = mergeRelationships(existing, change);\n }\n candidatesToPush.set(change.type, mergedChange);\n }\n\n accumulatedPushes.length = 0;\n\n const types = [...candidatesToPush.keys()];\n /**\n * Based on the received `fanOutChangeType` only certain output types are valid.\n *\n * - remove must result in all removes\n * - add must result in all adds\n * - edit must result in add or removes or edits\n * - child must result in a single add or single remove or many child changes\n * - Single add or remove because the relationship will be unique to one exist check within the fan-out,fan-in sub-graph\n * - Many child changes because other operators may preserve the child change\n */\n switch (fanOutChangeType) {\n case 'remove':\n assert(\n types.length === 1 && types[0] === 'remove',\n 'Fan-in:remove expected all removes',\n );\n output.push(\n addEmptyRelationships(must(candidatesToPush.get('remove'))),\n pusher,\n );\n return;\n case 'add':\n assert(\n types.length === 1 && types[0] === 'add',\n 'Fan-in:add expected all adds',\n );\n output.push(\n addEmptyRelationships(must(candidatesToPush.get('add'))),\n pusher,\n );\n return;\n case 'edit': {\n assert(\n types.every(\n type => type === 'add' || type === 'remove' || type === 'edit',\n ),\n 'Fan-in:edit expected all adds, removes, or edits',\n );\n const addChange = candidatesToPush.get('add');\n const removeChange = candidatesToPush.get('remove');\n let editChange = candidatesToPush.get('edit');\n\n // If an `edit` is present, it supersedes `add` and `remove`\n // as it semantically represents both.\n if (editChange) {\n if (addChange) {\n editChange = mergeRelationships(editChange, addChange);\n }\n if (removeChange) {\n editChange = mergeRelationships(editChange, removeChange);\n }\n output.push(addEmptyRelationships(editChange), pusher);\n return;\n }\n\n // If `edit` didn't make it through but both `add` and `remove` did,\n // convert back to an edit.\n //\n // When can this happen?\n //\n // EDIT old: a=1, new: a=2\n // |\n // FanOut\n // / \\\n // a=1 a=2\n // | |\n // remove add\n // \\ /\n // FanIn\n //\n // The left filter converts the edit into a remove.\n // The right filter converts the edit into an add.\n if (addChange && removeChange) {\n output.push(\n addEmptyRelationships({\n type: 'edit',\n node: addChange.node,\n oldNode: removeChange.node,\n } as const),\n pusher,\n );\n return;\n }\n\n output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n case 'child': {\n assert(\n types.every(\n type =>\n type === 'add' || // exists can change child to add or remove\n type === 'remove' || // exists can change child to add or remove\n type === 'child', // other operators may preserve the child change\n ),\n 'Fan-in:child expected all adds, removes, or children',\n );\n assert(\n types.length <= 2,\n 'Fan-in:child expected at most 2 types on a child change from fan-out',\n );\n\n // If any branch preserved the original child change, that takes precedence over all other changes.\n const childChange = candidatesToPush.get('child');\n if (childChange) {\n output.push(childChange, pusher);\n return;\n }\n\n const addChange = candidatesToPush.get('add');\n const removeChange = candidatesToPush.get('remove');\n\n assert(\n addChange === undefined || removeChange === undefined,\n 'Fan-in:child expected either add or remove, not both',\n );\n\n output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n default:\n fanOutChangeType satisfies never;\n }\n}\n\n/**\n * Puts relationships from `right` into `left` if they don't already exist in `left`.\n */\nexport function mergeRelationships(left: Change, right: Change): Change {\n // change types will always match\n // unless we have an edit on the left\n // then the right could be edit, add, or remove\n if (left.type === right.type) {\n switch (left.type) {\n case 'add': {\n return {\n type: 'add',\n node: {\n row: left.node.row,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n };\n }\n case 'remove': {\n return {\n type: 'remove',\n node: {\n row: left.node.row,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n };\n }\n case 'edit': {\n assert(right.type === 'edit');\n // merge edits into a single edit\n return {\n type: 'edit',\n node: {\n row: left.node.row,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n oldNode: {\n row: left.oldNode.row,\n relationships: {\n ...right.oldNode.relationships,\n ...left.oldNode.relationships,\n },\n },\n };\n }\n }\n }\n\n // left is always an edit here\n assert(left.type === 'edit');\n switch (right.type) {\n case 'add': {\n return {\n type: 'edit',\n node: {\n ...left.node,\n relationships: {\n ...right.node.relationships,\n ...left.node.relationships,\n },\n },\n oldNode: left.oldNode,\n };\n }\n case 'remove': {\n return {\n type: 'edit',\n node: left.node,\n oldNode: {\n ...left.oldNode,\n relationships: {\n ...right.node.relationships,\n ...left.oldNode.relationships,\n },\n },\n };\n }\n }\n\n unreachable();\n}\n\nexport function makeAddEmptyRelationships(\n schema: SourceSchema,\n): (change: Change) => Change {\n return (change: Change): Change => {\n if (Object.keys(schema.relationships).length === 0) {\n return change;\n }\n\n switch (change.type) {\n case 'add':\n case 'remove': {\n const ret = {\n ...change,\n node: {\n ...change.node,\n relationships: {\n ...change.node.relationships,\n },\n },\n };\n\n mergeEmpty(ret.node.relationships, Object.keys(schema.relationships));\n\n return ret;\n }\n case 'edit': {\n const ret = {\n ...change,\n node: {\n ...change.node,\n relationships: {\n ...change.node.relationships,\n },\n },\n oldNode: {\n ...change.oldNode,\n relationships: {\n ...change.oldNode.relationships,\n },\n },\n };\n\n mergeEmpty(ret.node.relationships, Object.keys(schema.relationships));\n mergeEmpty(\n ret.oldNode.relationships,\n Object.keys(schema.relationships),\n );\n\n return ret;\n }\n case 'child':\n return change; // children only have relationships along the path to the change\n }\n };\n}\n\n/**\n * For each relationship in `schema` that does not exist\n * in `relationships`, add it with an empty stream.\n *\n * This modifies the `relationships` object in place.\n */\nexport function mergeEmpty(\n relationships: Record<string, () => Stream<Node | 'yield'>>,\n relationshipNames: string[],\n) {\n for (const relName of relationshipNames) {\n if (relationships[relName] === undefined) {\n relationships[relName] = () => emptyArray;\n }\n }\n}\n"],"names":["mergeRelationships"],"mappings":"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"}
@@ -16,7 +16,7 @@ export declare class Skip implements Operator {
16
16
  #private;
17
17
  constructor(input: Input, bound: Bound);
18
18
  getSchema(): SourceSchema;
19
- fetch(req: FetchRequest): Stream<Node>;
19
+ fetch(req: FetchRequest): Stream<Node | 'yield'>;
20
20
  cleanup(req: FetchRequest): Stream<Node>;
21
21
  setOutput(output: Output): void;
22
22
  destroy(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"skip.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/skip.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,oCAAoC,CAAC;AAC5D,OAAO,KAAK,EAAY,MAAM,EAA4B,MAAM,aAAa,CAAC;AAC9E,OAAO,KAAK,EAAa,IAAI,EAAC,MAAM,WAAW,CAAC;AAEhD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EAEZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,qBAAa,IAAK,YAAW,QAAQ;;gBAOvB,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;IAOtC,SAAS,IAAI,YAAY;IAIzB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IAItC,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IAsBxC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,OAAO,IAAI,IAAI;IASf,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CA0E3B"}
1
+ {"version":3,"file":"skip.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/skip.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,oCAAoC,CAAC;AAC5D,OAAO,KAAK,EAAY,MAAM,EAA4B,MAAM,aAAa,CAAC;AAC9E,OAAO,KAAK,EAAa,IAAI,EAAC,MAAM,WAAW,CAAC;AAEhD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EAEZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,qBAAa,IAAK,YAAW,QAAQ;;gBAOvB,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;IAOtC,SAAS,IAAI,YAAY;IAIzB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IAIhD,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IA4BxC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,OAAO,IAAI,IAAI;IASf,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CA0E3B"}
@@ -18,7 +18,7 @@ class Skip {
18
18
  return this.#fetchOrCleanup("fetch", req);
19
19
  }
20
20
  cleanup(req) {
21
- return this.#fetchOrCleanup("fetch", req);
21
+ return this.#fetchOrCleanup("cleanup", req);
22
22
  }
23
23
  *#fetchOrCleanup(method, req) {
24
24
  const start = this.#getStart(req);
@@ -31,6 +31,12 @@ class Skip {
31
31
  return;
32
32
  }
33
33
  for (const node of nodes) {
34
+ if (node === "yield") {
35
+ if (method === "fetch") {
36
+ yield node;
37
+ }
38
+ continue;
39
+ }
34
40
  if (!this.#shouldBePresent(node.row)) {
35
41
  return;
36
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skip.js","sources":["../../../../../zql/src/ivm/skip.ts"],"sourcesContent":["import type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {AddChange, Change, ChildChange, RemoveChange} from './change.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {maybeSplitAndPushEditChange} from './maybe-split-and-push-edit-change.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Bound = {\n row: Row;\n exclusive: boolean;\n};\n\n/**\n * Skip sets the start position for the pipeline. No rows before the bound will\n * be output.\n */\nexport class Skip implements Operator {\n readonly #input: Input;\n readonly #bound: Bound;\n readonly #comparator: Comparator;\n\n #output: Output = throwOutput;\n\n constructor(input: Input, bound: Bound) {\n this.#input = input;\n this.#bound = bound;\n this.#comparator = input.getSchema().compareRows;\n input.setOutput(this);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n fetch(req: FetchRequest): Stream<Node> {\n return this.#fetchOrCleanup('fetch', req);\n }\n\n cleanup(req: FetchRequest): Stream<Node> {\n return this.#fetchOrCleanup('fetch', req);\n }\n\n *#fetchOrCleanup(method: 'fetch' | 'cleanup', req: FetchRequest) {\n const start = this.#getStart(req);\n if (start === 'empty') {\n return;\n }\n const nodes = this.#input[method]({...req, start});\n if (!req.reverse) {\n yield* nodes;\n return;\n }\n for (const node of nodes) {\n if (!this.#shouldBePresent(node.row)) {\n return;\n }\n yield node;\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n #shouldBePresent(row: Row): boolean {\n const cmp = this.#comparator(this.#bound.row, row);\n return cmp < 0 || (cmp === 0 && !this.#bound.exclusive);\n }\n\n push(change: Change): void {\n const shouldBePresent = (row: Row) => this.#shouldBePresent(row);\n if (change.type === 'edit') {\n maybeSplitAndPushEditChange(change, shouldBePresent, this.#output, this);\n return;\n }\n\n change satisfies AddChange | RemoveChange | ChildChange;\n\n if (shouldBePresent(change.node.row)) {\n this.#output.push(change, this);\n }\n }\n\n #getStart(req: FetchRequest): Start | undefined | 'empty' {\n const boundStart = {\n row: this.#bound.row,\n basis: this.#bound.exclusive ? 'after' : 'at',\n } as const;\n\n if (!req.start) {\n if (req.reverse) {\n return undefined;\n }\n return boundStart;\n }\n\n const cmp = this.#comparator(this.#bound.row, req.start.row);\n\n if (!req.reverse) {\n // The skip bound is after the requested bound. The requested bound cannot\n // be relevant because even if it was basis: 'after', the skip bound is\n // itself after the requested bound. Return the skip bound.\n if (cmp > 0) {\n return boundStart;\n }\n\n // The skip bound and requested bound are equal. If either is exclusive,\n // return that bound with exclusive. Otherwise, return the skip bound.\n if (cmp === 0) {\n if (this.#bound.exclusive || req.start.basis === 'after') {\n return {\n row: this.#bound.row,\n basis: 'after',\n };\n }\n return boundStart;\n }\n\n return req.start;\n }\n\n req.reverse satisfies true;\n\n // bound is after the start, but request is for reverse so results\n // must be empty\n if (cmp > 0) {\n return 'empty';\n }\n\n if (cmp === 0) {\n // if both are inclusive, the result can be the single row at bound\n // return it as start\n if (!this.#bound.exclusive && req.start.basis === 'at') {\n return boundStart;\n }\n // otherwise the results must be empty, one or both are exclusive\n // in opposite directions\n return 'empty';\n }\n\n // bound is before the start, return start\n return req.start;\n }\n}\n"],"names":[],"mappings":";;AAwBO,MAAM,KAAyB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EAET,UAAkB;AAAA,EAElB,YAAY,OAAc,OAAc;AACtC,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,cAAc,MAAM,UAAA,EAAY;AACrC,UAAM,UAAU,IAAI;AAAA,EACtB;AAAA,EAEA,YAA0B;AACxB,WAAO,KAAK,OAAO,UAAA;AAAA,EACrB;AAAA,EAEA,MAAM,KAAiC;AACrC,WAAO,KAAK,gBAAgB,SAAS,GAAG;AAAA,EAC1C;AAAA,EAEA,QAAQ,KAAiC;AACvC,WAAO,KAAK,gBAAgB,SAAS,GAAG;AAAA,EAC1C;AAAA,EAEA,CAAC,gBAAgB,QAA6B,KAAmB;AAC/D,UAAM,QAAQ,KAAK,UAAU,GAAG;AAChC,QAAI,UAAU,SAAS;AACrB;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,OAAO,MAAM,EAAE,EAAC,GAAG,KAAK,OAAM;AACjD,QAAI,CAAC,IAAI,SAAS;AAChB,aAAO;AACP;AAAA,IACF;AACA,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,KAAK,iBAAiB,KAAK,GAAG,GAAG;AACpC;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,UAAU,QAAsB;AAC9B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,QAAA;AAAA,EACd;AAAA,EAEA,iBAAiB,KAAmB;AAClC,UAAM,MAAM,KAAK,YAAY,KAAK,OAAO,KAAK,GAAG;AACjD,WAAO,MAAM,KAAM,QAAQ,KAAK,CAAC,KAAK,OAAO;AAAA,EAC/C;AAAA,EAEA,KAAK,QAAsB;AACzB,UAAM,kBAAkB,CAAC,QAAa,KAAK,iBAAiB,GAAG;AAC/D,QAAI,OAAO,SAAS,QAAQ;AAC1B,kCAA4B,QAAQ,iBAAiB,KAAK,SAAS,IAAI;AACvE;AAAA,IACF;AAIA,QAAI,gBAAgB,OAAO,KAAK,GAAG,GAAG;AACpC,WAAK,QAAQ,KAAK,QAAQ,IAAI;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,UAAU,KAAgD;AACxD,UAAM,aAAa;AAAA,MACjB,KAAK,KAAK,OAAO;AAAA,MACjB,OAAO,KAAK,OAAO,YAAY,UAAU;AAAA,IAAA;AAG3C,QAAI,CAAC,IAAI,OAAO;AACd,UAAI,IAAI,SAAS;AACf,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,KAAK,YAAY,KAAK,OAAO,KAAK,IAAI,MAAM,GAAG;AAE3D,QAAI,CAAC,IAAI,SAAS;AAIhB,UAAI,MAAM,GAAG;AACX,eAAO;AAAA,MACT;AAIA,UAAI,QAAQ,GAAG;AACb,YAAI,KAAK,OAAO,aAAa,IAAI,MAAM,UAAU,SAAS;AACxD,iBAAO;AAAA,YACL,KAAK,KAAK,OAAO;AAAA,YACjB,OAAO;AAAA,UAAA;AAAA,QAEX;AACA,eAAO;AAAA,MACT;AAEA,aAAO,IAAI;AAAA,IACb;AAEA,QAAI;AAIJ,QAAI,MAAM,GAAG;AACX,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,GAAG;AAGb,UAAI,CAAC,KAAK,OAAO,aAAa,IAAI,MAAM,UAAU,MAAM;AACtD,eAAO;AAAA,MACT;AAGA,aAAO;AAAA,IACT;AAGA,WAAO,IAAI;AAAA,EACb;AACF;"}
1
+ {"version":3,"file":"skip.js","sources":["../../../../../zql/src/ivm/skip.ts"],"sourcesContent":["import type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {AddChange, Change, ChildChange, RemoveChange} from './change.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {maybeSplitAndPushEditChange} from './maybe-split-and-push-edit-change.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Bound = {\n row: Row;\n exclusive: boolean;\n};\n\n/**\n * Skip sets the start position for the pipeline. No rows before the bound will\n * be output.\n */\nexport class Skip implements Operator {\n readonly #input: Input;\n readonly #bound: Bound;\n readonly #comparator: Comparator;\n\n #output: Output = throwOutput;\n\n constructor(input: Input, bound: Bound) {\n this.#input = input;\n this.#bound = bound;\n this.#comparator = input.getSchema().compareRows;\n input.setOutput(this);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#fetchOrCleanup('fetch', req);\n }\n\n cleanup(req: FetchRequest): Stream<Node> {\n return this.#fetchOrCleanup('cleanup', req) as Stream<Node>;\n }\n\n *#fetchOrCleanup(method: 'fetch' | 'cleanup', req: FetchRequest) {\n const start = this.#getStart(req);\n if (start === 'empty') {\n return;\n }\n const nodes = this.#input[method]({...req, start});\n if (!req.reverse) {\n yield* nodes;\n return;\n }\n for (const node of nodes) {\n if (node === 'yield') {\n if (method === 'fetch') {\n yield node;\n }\n continue;\n }\n if (!this.#shouldBePresent(node.row)) {\n return;\n }\n yield node;\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n #shouldBePresent(row: Row): boolean {\n const cmp = this.#comparator(this.#bound.row, row);\n return cmp < 0 || (cmp === 0 && !this.#bound.exclusive);\n }\n\n push(change: Change): void {\n const shouldBePresent = (row: Row) => this.#shouldBePresent(row);\n if (change.type === 'edit') {\n maybeSplitAndPushEditChange(change, shouldBePresent, this.#output, this);\n return;\n }\n\n change satisfies AddChange | RemoveChange | ChildChange;\n\n if (shouldBePresent(change.node.row)) {\n this.#output.push(change, this);\n }\n }\n\n #getStart(req: FetchRequest): Start | undefined | 'empty' {\n const boundStart = {\n row: this.#bound.row,\n basis: this.#bound.exclusive ? 'after' : 'at',\n } as const;\n\n if (!req.start) {\n if (req.reverse) {\n return undefined;\n }\n return boundStart;\n }\n\n const cmp = this.#comparator(this.#bound.row, req.start.row);\n\n if (!req.reverse) {\n // The skip bound is after the requested bound. The requested bound cannot\n // be relevant because even if it was basis: 'after', the skip bound is\n // itself after the requested bound. Return the skip bound.\n if (cmp > 0) {\n return boundStart;\n }\n\n // The skip bound and requested bound are equal. If either is exclusive,\n // return that bound with exclusive. Otherwise, return the skip bound.\n if (cmp === 0) {\n if (this.#bound.exclusive || req.start.basis === 'after') {\n return {\n row: this.#bound.row,\n basis: 'after',\n };\n }\n return boundStart;\n }\n\n return req.start;\n }\n\n req.reverse satisfies true;\n\n // bound is after the start, but request is for reverse so results\n // must be empty\n if (cmp > 0) {\n return 'empty';\n }\n\n if (cmp === 0) {\n // if both are inclusive, the result can be the single row at bound\n // return it as start\n if (!this.#bound.exclusive && req.start.basis === 'at') {\n return boundStart;\n }\n // otherwise the results must be empty, one or both are exclusive\n // in opposite directions\n return 'empty';\n }\n\n // bound is before the start, return start\n return req.start;\n }\n}\n"],"names":[],"mappings":";;AAwBO,MAAM,KAAyB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EAET,UAAkB;AAAA,EAElB,YAAY,OAAc,OAAc;AACtC,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,cAAc,MAAM,UAAA,EAAY;AACrC,UAAM,UAAU,IAAI;AAAA,EACtB;AAAA,EAEA,YAA0B;AACxB,WAAO,KAAK,OAAO,UAAA;AAAA,EACrB;AAAA,EAEA,MAAM,KAA2C;AAC/C,WAAO,KAAK,gBAAgB,SAAS,GAAG;AAAA,EAC1C;AAAA,EAEA,QAAQ,KAAiC;AACvC,WAAO,KAAK,gBAAgB,WAAW,GAAG;AAAA,EAC5C;AAAA,EAEA,CAAC,gBAAgB,QAA6B,KAAmB;AAC/D,UAAM,QAAQ,KAAK,UAAU,GAAG;AAChC,QAAI,UAAU,SAAS;AACrB;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,OAAO,MAAM,EAAE,EAAC,GAAG,KAAK,OAAM;AACjD,QAAI,CAAC,IAAI,SAAS;AAChB,aAAO;AACP;AAAA,IACF;AACA,eAAW,QAAQ,OAAO;AACxB,UAAI,SAAS,SAAS;AACpB,YAAI,WAAW,SAAS;AACtB,gBAAM;AAAA,QACR;AACA;AAAA,MACF;AACA,UAAI,CAAC,KAAK,iBAAiB,KAAK,GAAG,GAAG;AACpC;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,UAAU,QAAsB;AAC9B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,QAAA;AAAA,EACd;AAAA,EAEA,iBAAiB,KAAmB;AAClC,UAAM,MAAM,KAAK,YAAY,KAAK,OAAO,KAAK,GAAG;AACjD,WAAO,MAAM,KAAM,QAAQ,KAAK,CAAC,KAAK,OAAO;AAAA,EAC/C;AAAA,EAEA,KAAK,QAAsB;AACzB,UAAM,kBAAkB,CAAC,QAAa,KAAK,iBAAiB,GAAG;AAC/D,QAAI,OAAO,SAAS,QAAQ;AAC1B,kCAA4B,QAAQ,iBAAiB,KAAK,SAAS,IAAI;AACvE;AAAA,IACF;AAIA,QAAI,gBAAgB,OAAO,KAAK,GAAG,GAAG;AACpC,WAAK,QAAQ,KAAK,QAAQ,IAAI;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,UAAU,KAAgD;AACxD,UAAM,aAAa;AAAA,MACjB,KAAK,KAAK,OAAO;AAAA,MACjB,OAAO,KAAK,OAAO,YAAY,UAAU;AAAA,IAAA;AAG3C,QAAI,CAAC,IAAI,OAAO;AACd,UAAI,IAAI,SAAS;AACf,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,KAAK,YAAY,KAAK,OAAO,KAAK,IAAI,MAAM,GAAG;AAE3D,QAAI,CAAC,IAAI,SAAS;AAIhB,UAAI,MAAM,GAAG;AACX,eAAO;AAAA,MACT;AAIA,UAAI,QAAQ,GAAG;AACb,YAAI,KAAK,OAAO,aAAa,IAAI,MAAM,UAAU,SAAS;AACxD,iBAAO;AAAA,YACL,KAAK,KAAK,OAAO;AAAA,YACjB,OAAO;AAAA,UAAA;AAAA,QAEX;AACA,eAAO;AAAA,MACT;AAEA,aAAO,IAAI;AAAA,IACb;AAEA,QAAI;AAIJ,QAAI,MAAM,GAAG;AACX,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,GAAG;AAGb,UAAI,CAAC,KAAK,OAAO,aAAa,IAAI,MAAM,UAAU,MAAM;AACtD,eAAO;AAAA,MACT;AAGA,aAAO;AAAA,IACT;AAGA,WAAO,IAAI;AAAA,EACb;AACF;"}
@@ -21,7 +21,7 @@ export declare class Take implements Operator {
21
21
  constructor(input: Input, storage: Storage, limit: number, partitionKey?: PartitionKey);
22
22
  setOutput(output: Output): void;
23
23
  getSchema(): SourceSchema;
24
- fetch(req: FetchRequest): Stream<Node>;
24
+ fetch(req: FetchRequest): Stream<Node | 'yield'>;
25
25
  cleanup(req: FetchRequest): Stream<Node>;
26
26
  push(change: Change): void;
27
27
  destroy(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"take.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/take.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAE1E,OAAO,EAAC,KAAK,MAAM,EAAqC,MAAM,aAAa,CAAC;AAE5E,OAAO,EAAiC,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AACpE,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAc,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAiBrD,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAEtC;;;;;;;;;;GAUG;AACH,qBAAa,IAAK,YAAW,QAAQ;;gBAYjC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,YAAY;IAgB7B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IAgHtC,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IA+CzC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAsa1B,OAAO,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"take.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/take.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAE1E,OAAO,EAAC,KAAK,MAAM,EAAqC,MAAM,aAAa,CAAC;AAE5E,OAAO,EAAiC,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AACpE,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAc,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAiBrD,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAEtC;;;;;;;;;;GAUG;AACH,qBAAa,IAAK,YAAW,QAAQ;;gBAYjC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,YAAY;IAgB7B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IA4HhD,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IA+CzC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAsb1B,OAAO,IAAI,IAAI;CAGhB"}
@@ -3,7 +3,7 @@ import { hasOwn } from "../../../shared/src/has-own.js";
3
3
  import { must } from "../../../shared/src/must.js";
4
4
  import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
5
5
  import { compareValues } from "./data.js";
6
- import { throwOutput } from "./operator.js";
6
+ import { throwOutput, skipYields } from "./operator.js";
7
7
  import { first, take } from "./stream.js";
8
8
  const MAX_BOUND_KEY = "maxBound";
9
9
  class Take {
@@ -46,6 +46,10 @@ class Take {
46
46
  return;
47
47
  }
48
48
  for (const inputNode of this.#input.fetch(req)) {
49
+ if (inputNode === "yield") {
50
+ yield inputNode;
51
+ continue;
52
+ }
49
53
  if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {
50
54
  return;
51
55
  }
@@ -64,6 +68,10 @@ class Take {
64
68
  return;
65
69
  }
66
70
  for (const inputNode of this.#input.fetch(req)) {
71
+ if (inputNode === "yield") {
72
+ yield inputNode;
73
+ continue;
74
+ }
67
75
  if (this.getSchema().compareRows(inputNode.row, maxBound) > 0) {
68
76
  return;
69
77
  }
@@ -95,6 +103,10 @@ class Take {
95
103
  let exceptionThrown = false;
96
104
  try {
97
105
  for (const inputNode of this.#input.fetch(req)) {
106
+ if (inputNode === "yield") {
107
+ yield "yield";
108
+ continue;
109
+ }
98
110
  yield inputNode;
99
111
  bound = inputNode.row;
100
112
  size++;
@@ -180,25 +192,29 @@ class Take {
180
192
  if (this.#limit === 1) {
181
193
  boundNode = must(
182
194
  first(
195
+ skipYields(
196
+ this.#input.fetch({
197
+ start: {
198
+ row: takeState.bound,
199
+ basis: "at"
200
+ },
201
+ constraint
202
+ })
203
+ )
204
+ )
205
+ );
206
+ } else {
207
+ [boundNode, beforeBoundNode] = take(
208
+ skipYields(
183
209
  this.#input.fetch({
184
210
  start: {
185
211
  row: takeState.bound,
186
212
  basis: "at"
187
213
  },
188
- constraint
214
+ constraint,
215
+ reverse: true
189
216
  })
190
- )
191
- );
192
- } else {
193
- [boundNode, beforeBoundNode] = take(
194
- this.#input.fetch({
195
- start: {
196
- row: takeState.bound,
197
- basis: "at"
198
- },
199
- constraint,
200
- reverse: true
201
- }),
217
+ ),
202
218
  2
203
219
  );
204
220
  }
@@ -225,14 +241,16 @@ class Take {
225
241
  return;
226
242
  }
227
243
  const [beforeBoundNode] = take(
228
- this.#input.fetch({
229
- start: {
230
- row: takeState.bound,
231
- basis: "after"
232
- },
233
- constraint,
234
- reverse: true
235
- }),
244
+ skipYields(
245
+ this.#input.fetch({
246
+ start: {
247
+ row: takeState.bound,
248
+ basis: "after"
249
+ },
250
+ constraint,
251
+ reverse: true
252
+ })
253
+ ),
236
254
  1
237
255
  );
238
256
  let newBound;
@@ -244,13 +262,15 @@ class Take {
244
262
  };
245
263
  }
246
264
  if (!newBound?.push) {
247
- for (const node of this.#input.fetch({
248
- start: {
249
- row: takeState.bound,
250
- basis: "at"
251
- },
252
- constraint
253
- })) {
265
+ for (const node of skipYields(
266
+ this.#input.fetch({
267
+ start: {
268
+ row: takeState.bound,
269
+ basis: "at"
270
+ },
271
+ constraint
272
+ })
273
+ )) {
254
274
  const push = compareRows(node.row, takeState.bound) > 0;
255
275
  newBound = {
256
276
  node,
@@ -325,14 +345,16 @@ class Take {
325
345
  }
326
346
  const beforeBoundNode = must(
327
347
  first(
328
- this.#input.fetch({
329
- start: {
330
- row: takeState.bound,
331
- basis: "after"
332
- },
333
- constraint,
334
- reverse: true
335
- })
348
+ skipYields(
349
+ this.#input.fetch({
350
+ start: {
351
+ row: takeState.bound,
352
+ basis: "after"
353
+ },
354
+ constraint,
355
+ reverse: true
356
+ })
357
+ )
336
358
  )
337
359
  );
338
360
  this.#setTakeState(
@@ -347,13 +369,15 @@ class Take {
347
369
  assert(newCmp > 0, "New comparison must be greater than 0");
348
370
  const newBoundNode = must(
349
371
  first(
350
- this.#input.fetch({
351
- start: {
352
- row: takeState.bound,
353
- basis: "at"
354
- },
355
- constraint
356
- })
372
+ skipYields(
373
+ this.#input.fetch({
374
+ start: {
375
+ row: takeState.bound,
376
+ basis: "at"
377
+ },
378
+ constraint
379
+ })
380
+ )
357
381
  )
358
382
  );
359
383
  if (compareRows(newBoundNode.row, change.node.row) === 0) {
@@ -391,14 +415,16 @@ class Take {
391
415
  }
392
416
  assert(newCmp < 0, "New comparison must be less than 0");
393
417
  const [oldBoundNode, newBoundNode] = take(
394
- this.#input.fetch({
395
- start: {
396
- row: takeState.bound,
397
- basis: "at"
398
- },
399
- constraint,
400
- reverse: true
401
- }),
418
+ skipYields(
419
+ this.#input.fetch({
420
+ start: {
421
+ row: takeState.bound,
422
+ basis: "at"
423
+ },
424
+ constraint,
425
+ reverse: true
426
+ })
427
+ ),
402
428
  2
403
429
  );
404
430
  this.#setTakeState(
@@ -434,13 +460,15 @@ class Take {
434
460
  assert(newCmp > 0, "New comparison must be greater than 0");
435
461
  const afterBoundNode = must(
436
462
  first(
437
- this.#input.fetch({
438
- start: {
439
- row: takeState.bound,
440
- basis: "after"
441
- },
442
- constraint
443
- })
463
+ skipYields(
464
+ this.#input.fetch({
465
+ start: {
466
+ row: takeState.bound,
467
+ basis: "after"
468
+ },
469
+ constraint
470
+ })
471
+ )
444
472
  )
445
473
  );
446
474
  if (compareRows(afterBoundNode.row, change.node.row) === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"take.js","sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {type Change, type EditChange, type RemoveChange} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {first, take, type Stream} from './stream.ts';\n\nconst MAX_BOUND_KEY = 'maxBound';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: typeof MAX_BOUND_KEY): Row | undefined;\n get(key: string): TakeState | undefined;\n set(key: typeof MAX_BOUND_KEY, value: Row): void;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #output: Output = throwOutput;\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n assertOrderingIncludesPK(\n input.getSchema().sort,\n input.getSchema().primaryKey,\n );\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *fetch(req: FetchRequest): Stream<Node> {\n if (\n !this.#partitionKey ||\n (req.constraint &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey))\n ) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n yield* this.#initialFetch(req);\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n }\n return;\n }\n // There is a partition key, but the fetch is not constrained or constrained\n // on a different key. Thus we don't have a single take state to bound by.\n // This currently only happens with nested sub-queries\n // e.g. issues include issuelabels include label. We could remove this\n // case if we added a translation layer (powered by some state) in join.\n // Specifically we need joinKeyValue => parent constraint key\n const maxBound = this.#storage.get(MAX_BOUND_KEY);\n if (maxBound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (this.getSchema().compareRows(inputNode.row, maxBound) > 0) {\n return;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, inputNode.row);\n const takeState = this.#storage.get(takeStateKey);\n if (\n takeState?.bound !== undefined &&\n this.getSchema().compareRows(takeState.bound, inputNode.row) >= 0\n ) {\n yield inputNode;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n if (this.#limit === 0) {\n return;\n }\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(takeStateKey) === undefined,\n 'Take state should be undefined',\n );\n\n let size = 0;\n let bound: Row | undefined;\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n yield inputNode;\n bound = inputNode.row;\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#setTakeState(\n takeStateKey,\n size,\n bound,\n this.#storage.get(MAX_BOUND_KEY),\n );\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n *cleanup(req: FetchRequest): Stream<Node> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n this.#storage.del(takeStateKey);\n let size = 0;\n for (const inputNode of this.#input.cleanup(req)) {\n if (size === this.#limit) {\n return;\n }\n size++;\n yield inputNode;\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let maxBound: Row | undefined;\n let constraint: Constraint | undefined;\n if (takeState) {\n maxBound = this.#storage.get(MAX_BOUND_KEY);\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, maxBound, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n maxBound: undefined;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n maxBound: Row | undefined;\n constraint: Constraint | undefined;\n };\n }\n\n push(change: Change): void {\n if (change.type === 'edit') {\n this.#pushEditChange(change);\n return;\n }\n\n const {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.node.row);\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change.type === 'add') {\n if (takeState.size < this.#limit) {\n this.#setTakeState(\n takeStateKey,\n takeState.size + 1,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change.node.row) < 0\n ? change.node.row\n : takeState.bound,\n maxBound,\n );\n this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change.node.row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node;\n if (this.#limit === 1) {\n boundNode = must(\n first(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n }),\n ),\n );\n } else {\n [boundNode, beforeBoundNode] = take(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n }),\n 2,\n );\n }\n const removeChange: RemoveChange = {\n type: 'remove',\n node: boundNode,\n };\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change.node.row, beforeBoundNode.row) > 0\n ? change.node.row\n : beforeBoundNode.row,\n maxBound,\n );\n this.#withRowHiddenFromFetch(change.node.row, () => {\n this.#output.push(removeChange, this);\n });\n this.#output.push(change, this);\n } else if (change.type === 'remove') {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(change.node.row, takeState.bound);\n if (compToBound > 0) {\n // change is after bound\n return;\n }\n const [beforeBoundNode] = take(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n }),\n 1,\n );\n\n let newBound: {node: Node; push: boolean} | undefined;\n if (beforeBoundNode) {\n const push = compareRows(beforeBoundNode.row, takeState.bound) > 0;\n newBound = {\n node: beforeBoundNode,\n push,\n };\n }\n if (!newBound?.push) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n const push = compareRows(node.row, takeState.bound) > 0;\n newBound = {\n node,\n push,\n };\n if (push) {\n break;\n }\n }\n }\n\n if (newBound?.push) {\n this.#output.push(change, this);\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBound.node.row,\n maxBound,\n );\n this.#output.push(\n {\n type: 'add',\n node: newBound.node,\n },\n this,\n );\n return;\n }\n this.#setTakeState(\n takeStateKey,\n takeState.size - 1,\n newBound?.node.row,\n maxBound,\n );\n this.#output.push(change, this);\n } else if (change.type === 'child') {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change.node.row, takeState.bound) <= 0\n ) {\n this.#output.push(change, this);\n }\n }\n }\n\n #pushEditChange(change: EditChange): void {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(change.oldNode.row, change.node.row) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.oldNode.row);\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(change.oldNode.row, takeState.bound);\n const newCmp = compareRows(change.node.row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change.node.row,\n maxBound,\n );\n this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n const beforeBoundNode = must(\n first(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n }),\n ),\n );\n\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode.row,\n maxBound,\n );\n this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n const newBoundNode = must(\n first(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n }),\n ),\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change.node.row) === 0) {\n replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n this.#withRowHiddenFromFetch(newBoundNode.row, () => {\n this.#output.push(\n {\n type: 'remove',\n node: change.oldNode,\n },\n this,\n );\n });\n this.#output.push(\n {\n type: 'add',\n node: newBoundNode,\n },\n this,\n );\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n const [oldBoundNode, newBoundNode] = take(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n }),\n 2,\n );\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n this.#withRowHiddenFromFetch(change.node.row, () => {\n this.#output.push(\n {\n type: 'remove',\n node: oldBoundNode,\n },\n this,\n );\n });\n this.#output.push(\n {\n type: 'add',\n node: change.node,\n },\n this,\n );\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n const afterBoundNode = must(\n first(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n }),\n ),\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change.node.row) === 0) {\n replaceBoundAndForwardChange();\n return;\n }\n\n this.#output.push(\n {\n type: 'remove',\n node: change.oldNode,\n },\n this,\n );\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n afterBoundNode.row,\n maxBound,\n );\n this.#output.push(\n {\n type: 'add',\n node: afterBoundNode,\n },\n this,\n );\n return;\n }\n\n unreachable();\n }\n\n #withRowHiddenFromFetch(row: Row, fn: () => void) {\n this.#rowHiddenFromFetch = row;\n try {\n fn();\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(\n takeStateKey: string,\n size: number,\n bound: Row | undefined,\n maxBound: Row | undefined,\n ) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n if (\n bound !== undefined &&\n (maxBound === undefined ||\n this.getSchema().compareRows(bound, maxBound) > 0)\n ) {\n this.#storage.set(MAX_BOUND_KEY, bound);\n }\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['take', ...partitionValues]);\n}\n\nfunction constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nfunction makePartitionKeyComparator(partitionKey: PartitionKey): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"take.js","sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {type Change, type EditChange, type RemoveChange} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n skipYields,\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {first, take, type Stream} from './stream.ts';\n\nconst MAX_BOUND_KEY = 'maxBound';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: typeof MAX_BOUND_KEY): Row | undefined;\n get(key: string): TakeState | undefined;\n set(key: typeof MAX_BOUND_KEY, value: Row): void;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #output: Output = throwOutput;\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n assertOrderingIncludesPK(\n input.getSchema().sort,\n input.getSchema().primaryKey,\n );\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n if (\n !this.#partitionKey ||\n (req.constraint &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey))\n ) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n yield* this.#initialFetch(req);\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n }\n return;\n }\n // There is a partition key, but the fetch is not constrained or constrained\n // on a different key. Thus we don't have a single take state to bound by.\n // This currently only happens with nested sub-queries\n // e.g. issues include issuelabels include label. We could remove this\n // case if we added a translation layer (powered by some state) in join.\n // Specifically we need joinKeyValue => parent constraint key\n const maxBound = this.#storage.get(MAX_BOUND_KEY);\n if (maxBound === undefined) {\n return;\n }\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(inputNode.row, maxBound) > 0) {\n return;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, inputNode.row);\n const takeState = this.#storage.get(takeStateKey);\n if (\n takeState?.bound !== undefined &&\n this.getSchema().compareRows(takeState.bound, inputNode.row) >= 0\n ) {\n yield inputNode;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n if (this.#limit === 0) {\n return;\n }\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(takeStateKey) === undefined,\n 'Take state should be undefined',\n );\n\n let size = 0;\n let bound: Row | undefined;\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n bound = inputNode.row;\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#setTakeState(\n takeStateKey,\n size,\n bound,\n this.#storage.get(MAX_BOUND_KEY),\n );\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n *cleanup(req: FetchRequest): Stream<Node> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n this.#storage.del(takeStateKey);\n let size = 0;\n for (const inputNode of this.#input.cleanup(req)) {\n if (size === this.#limit) {\n return;\n }\n size++;\n yield inputNode;\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let maxBound: Row | undefined;\n let constraint: Constraint | undefined;\n if (takeState) {\n maxBound = this.#storage.get(MAX_BOUND_KEY);\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, maxBound, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n maxBound: undefined;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n maxBound: Row | undefined;\n constraint: Constraint | undefined;\n };\n }\n\n push(change: Change): void {\n if (change.type === 'edit') {\n this.#pushEditChange(change);\n return;\n }\n\n const {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.node.row);\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change.type === 'add') {\n if (takeState.size < this.#limit) {\n this.#setTakeState(\n takeStateKey,\n takeState.size + 1,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change.node.row) < 0\n ? change.node.row\n : takeState.bound,\n maxBound,\n );\n this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change.node.row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node;\n if (this.#limit === 1) {\n boundNode = must(\n first(\n skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n }),\n ),\n ),\n );\n } else {\n [boundNode, beforeBoundNode] = take(\n skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n }),\n ),\n 2,\n );\n }\n const removeChange: RemoveChange = {\n type: 'remove',\n node: boundNode,\n };\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change.node.row, beforeBoundNode.row) > 0\n ? change.node.row\n : beforeBoundNode.row,\n maxBound,\n );\n this.#withRowHiddenFromFetch(change.node.row, () => {\n this.#output.push(removeChange, this);\n });\n this.#output.push(change, this);\n } else if (change.type === 'remove') {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(change.node.row, takeState.bound);\n if (compToBound > 0) {\n // change is after bound\n return;\n }\n const [beforeBoundNode] = take(\n skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n }),\n ),\n 1,\n );\n\n let newBound: {node: Node; push: boolean} | undefined;\n if (beforeBoundNode) {\n const push = compareRows(beforeBoundNode.row, takeState.bound) > 0;\n newBound = {\n node: beforeBoundNode,\n push,\n };\n }\n if (!newBound?.push) {\n for (const node of skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n }),\n )) {\n const push = compareRows(node.row, takeState.bound) > 0;\n newBound = {\n node,\n push,\n };\n if (push) {\n break;\n }\n }\n }\n\n if (newBound?.push) {\n this.#output.push(change, this);\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBound.node.row,\n maxBound,\n );\n this.#output.push(\n {\n type: 'add',\n node: newBound.node,\n },\n this,\n );\n return;\n }\n this.#setTakeState(\n takeStateKey,\n takeState.size - 1,\n newBound?.node.row,\n maxBound,\n );\n this.#output.push(change, this);\n } else if (change.type === 'child') {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change.node.row, takeState.bound) <= 0\n ) {\n this.#output.push(change, this);\n }\n }\n }\n\n #pushEditChange(change: EditChange): void {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(change.oldNode.row, change.node.row) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, maxBound, constraint} =\n this.#getStateAndConstraint(change.oldNode.row);\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(change.oldNode.row, takeState.bound);\n const newCmp = compareRows(change.node.row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change.node.row,\n maxBound,\n );\n this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n const beforeBoundNode = must(\n first(\n skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n }),\n ),\n ),\n );\n\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode.row,\n maxBound,\n );\n this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n const newBoundNode = must(\n first(\n skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n }),\n ),\n ),\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change.node.row) === 0) {\n replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n this.#withRowHiddenFromFetch(newBoundNode.row, () => {\n this.#output.push(\n {\n type: 'remove',\n node: change.oldNode,\n },\n this,\n );\n });\n this.#output.push(\n {\n type: 'add',\n node: newBoundNode,\n },\n this,\n );\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n const [oldBoundNode, newBoundNode] = take(\n skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n }),\n ),\n 2,\n );\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n newBoundNode.row,\n maxBound,\n );\n this.#withRowHiddenFromFetch(change.node.row, () => {\n this.#output.push(\n {\n type: 'remove',\n node: oldBoundNode,\n },\n this,\n );\n });\n this.#output.push(\n {\n type: 'add',\n node: change.node,\n },\n this,\n );\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n const afterBoundNode = must(\n first(\n skipYields(\n this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n }),\n ),\n ),\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change.node.row) === 0) {\n replaceBoundAndForwardChange();\n return;\n }\n\n this.#output.push(\n {\n type: 'remove',\n node: change.oldNode,\n },\n this,\n );\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n afterBoundNode.row,\n maxBound,\n );\n this.#output.push(\n {\n type: 'add',\n node: afterBoundNode,\n },\n this,\n );\n return;\n }\n\n unreachable();\n }\n\n #withRowHiddenFromFetch(row: Row, fn: () => void) {\n this.#rowHiddenFromFetch = row;\n try {\n fn();\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(\n takeStateKey: string,\n size: number,\n bound: Row | undefined,\n maxBound: Row | undefined,\n ) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n if (\n bound !== undefined &&\n (maxBound === undefined ||\n this.getSchema().compareRows(bound, maxBound) > 0)\n ) {\n this.#storage.set(MAX_BOUND_KEY, bound);\n }\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['take', ...partitionValues]);\n}\n\nfunction constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nfunction makePartitionKeyComparator(partitionKey: PartitionKey): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"names":[],"mappings":"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"}
@@ -9,11 +9,12 @@ export declare class UnionFanIn implements Operator {
9
9
  constructor(fanOut: UnionFanOut, inputs: Input[]);
10
10
  cleanup(_req: FetchRequest): Stream<Node>;
11
11
  destroy(): void;
12
- fetch(req: FetchRequest): Stream<Node>;
12
+ fetch(req: FetchRequest): Stream<Node | 'yield'>;
13
13
  getSchema(): SourceSchema;
14
14
  push(change: Change, pusher: InputBase): void;
15
15
  fanOutStartedPushing(): void;
16
16
  fanOutDonePushing(fanOutChangeType: Change['type']): void;
17
17
  setOutput(output: Output): void;
18
18
  }
19
+ export declare function mergeFetches(fetches: Iterable<Node | 'yield'>[], comparator: (l: Node, r: Node) => number): IterableIterator<Node | 'yield'>;
19
20
  //# sourceMappingURL=union-fan-in.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"union-fan-in.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AAMvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAQ,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAEpD,qBAAa,UAAW,YAAW,QAAQ;;gBAO7B,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE;IAgEhD,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IAKzC,OAAO,IAAI,IAAI;IAMf,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IAStC,SAAS,IAAI,YAAY;IAIzB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAgE7C,oBAAoB;IAKpB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC;IAuBlD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAGhC"}
1
+ {"version":3,"file":"union-fan-in.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AAMvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAQ,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAEpD,qBAAa,UAAW,YAAW,QAAQ;;gBAO7B,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE;IAgEhD,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;IAKzC,OAAO,IAAI,IAAI;IAMf,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IAOhD,SAAS,IAAI,YAAY;IAIzB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAgE7C,oBAAoB;IAKpB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC;IAuBlD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAGhC;AAED,wBAAiB,YAAY,CAC3B,OAAO,EAAE,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,EAAE,EACnC,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK,MAAM,GACvC,gBAAgB,CAAC,IAAI,GAAG,OAAO,CAAC,CAuElC"}