typeferry 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/README.md +5 -0
  2. package/dist/application/cli-arguments.d.ts +37 -5
  3. package/dist/application/cli-arguments.js +57 -7
  4. package/dist/application/cli-arguments.js.map +1 -1
  5. package/dist/application/config.d.ts +58 -31
  6. package/dist/application/config.js +40 -16
  7. package/dist/application/config.js.map +1 -1
  8. package/dist/application/native-command.d.ts +35 -0
  9. package/dist/application/native-command.js +148 -0
  10. package/dist/application/native-command.js.map +1 -0
  11. package/dist/application/native-devices.d.ts +31 -0
  12. package/dist/application/native-devices.js +94 -0
  13. package/dist/application/native-devices.js.map +1 -0
  14. package/dist/application/native-doctor.d.ts +17 -0
  15. package/dist/application/native-doctor.js +79 -0
  16. package/dist/application/native-doctor.js.map +1 -0
  17. package/dist/application/native-simulator-settings.d.ts +24 -0
  18. package/dist/application/native-simulator-settings.js +55 -0
  19. package/dist/application/native-simulator-settings.js.map +1 -0
  20. package/dist/application/native-simulator.d.ts +19 -0
  21. package/dist/application/native-simulator.js +101 -0
  22. package/dist/application/native-simulator.js.map +1 -0
  23. package/dist/application/proxy.d.ts +5 -1
  24. package/dist/application/proxy.js +25 -4
  25. package/dist/application/proxy.js.map +1 -1
  26. package/dist/application/run-tests.js +1 -1
  27. package/dist/application/run-tests.js.map +1 -1
  28. package/dist/auth/client/native-session.js +4 -2
  29. package/dist/auth/client/native-session.js.map +1 -1
  30. package/dist/auth/client/oauth/use-google-login.js +2 -0
  31. package/dist/auth/client/oauth/use-google-login.js.map +1 -1
  32. package/dist/auth/server/cookie-utils.js +4 -3
  33. package/dist/auth/server/cookie-utils.js.map +1 -1
  34. package/dist/auth/server/device-info.js +8 -8
  35. package/dist/auth/server/device-info.js.map +1 -1
  36. package/dist/auth/server/oauth/google.js +11 -7
  37. package/dist/auth/server/oauth/google.js.map +1 -1
  38. package/dist/auth/server/session-manager.js +2 -2
  39. package/dist/auth/server/session-manager.js.map +1 -1
  40. package/dist/cli/index.js +5 -0
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/cli/run.js +13 -1
  43. package/dist/cli/run.js.map +1 -1
  44. package/dist/client/client-channel.d.ts +3 -11
  45. package/dist/client/client-channel.js +11 -4
  46. package/dist/client/client-channel.js.map +1 -1
  47. package/dist/client/client-http.d.ts +2 -2
  48. package/dist/client/client-http.js.map +1 -1
  49. package/dist/client/client.d.ts +13 -13
  50. package/dist/client/client.js +24 -11
  51. package/dist/client/client.js.map +1 -1
  52. package/dist/client/context-manager.d.ts +1 -1
  53. package/dist/client/logger.d.ts +1 -0
  54. package/dist/client/logger.js +12 -15
  55. package/dist/client/logger.js.map +1 -1
  56. package/dist/ejson/adjust-types-from-json-value.d.ts +1 -1
  57. package/dist/ejson/adjust-types-from-json-value.js +4 -2
  58. package/dist/ejson/adjust-types-from-json-value.js.map +1 -1
  59. package/dist/ejson/base64.d.ts +1 -1
  60. package/dist/ejson/base64.js +1 -1
  61. package/dist/ejson/base64.js.map +1 -1
  62. package/dist/ejson/built-in-converters.d.ts +59 -1
  63. package/dist/ejson/built-in-converters.js +109 -125
  64. package/dist/ejson/built-in-converters.js.map +1 -1
  65. package/dist/ejson/clone.js +1 -1
  66. package/dist/ejson/clone.js.map +1 -1
  67. package/dist/ejson/custom-models.d.ts +10 -10
  68. package/dist/ejson/custom-models.js +22 -2
  69. package/dist/ejson/custom-models.js.map +1 -1
  70. package/dist/ejson/custom-types.d.ts +2 -1
  71. package/dist/ejson/custom-types.js.map +1 -1
  72. package/dist/ejson/equals.d.ts +4 -1
  73. package/dist/ejson/equals.js +6 -3
  74. package/dist/ejson/equals.js.map +1 -1
  75. package/dist/ejson/from-json-value.d.ts +1 -1
  76. package/dist/ejson/from-json-value.js +1 -1
  77. package/dist/ejson/from-json-value.js.map +1 -1
  78. package/dist/ejson/helpers/adjust-types-to-json-value.d.ts +1 -1
  79. package/dist/ejson/helpers/adjust-types-to-json-value.js +4 -2
  80. package/dist/ejson/helpers/adjust-types-to-json-value.js.map +1 -1
  81. package/dist/ejson/helpers/from-json-value-helper.d.ts +1 -1
  82. package/dist/ejson/helpers/from-json-value-helper.js +5 -3
  83. package/dist/ejson/helpers/from-json-value-helper.js.map +1 -1
  84. package/dist/ejson/helpers/to-json-value-helper.d.ts +1 -1
  85. package/dist/ejson/helpers/to-json-value-helper.js +3 -1
  86. package/dist/ejson/helpers/to-json-value-helper.js.map +1 -1
  87. package/dist/ejson/index.d.ts +14 -12
  88. package/dist/ejson/index.js +3 -3
  89. package/dist/ejson/index.js.map +1 -1
  90. package/dist/ejson/is-binary.d.ts +1 -1
  91. package/dist/ejson/is-binary.js +1 -1
  92. package/dist/ejson/is-binary.js.map +1 -1
  93. package/dist/ejson/parse.d.ts +1 -1
  94. package/dist/ejson/parse.js +1 -1
  95. package/dist/ejson/parse.js.map +1 -1
  96. package/dist/ejson/stable-stringify.d.ts +1 -6
  97. package/dist/ejson/stable-stringify.js +3 -1
  98. package/dist/ejson/stable-stringify.js.map +1 -1
  99. package/dist/ejson/to-json-value.d.ts +1 -1
  100. package/dist/ejson/to-json-value.js +1 -1
  101. package/dist/ejson/to-json-value.js.map +1 -1
  102. package/dist/ejson/utils.d.ts +15 -13
  103. package/dist/ejson/utils.js +39 -37
  104. package/dist/ejson/utils.js.map +1 -1
  105. package/dist/minimongo/NOTICE.md +30 -0
  106. package/dist/minimongo/README.md +329 -0
  107. package/dist/minimongo/components.d.ts +31 -0
  108. package/dist/minimongo/components.js +53 -0
  109. package/dist/minimongo/components.js.map +1 -0
  110. package/dist/minimongo/contracts.type-test.d.ts +1 -0
  111. package/dist/minimongo/contracts.type-test.js +21 -0
  112. package/dist/minimongo/contracts.type-test.js.map +1 -0
  113. package/dist/minimongo/cursor.d.ts +67 -0
  114. package/dist/minimongo/cursor.js +157 -0
  115. package/dist/minimongo/cursor.js.map +1 -0
  116. package/dist/minimongo/document-store.d.ts +31 -0
  117. package/dist/minimongo/document-store.js +48 -0
  118. package/dist/minimongo/document-store.js.map +1 -0
  119. package/dist/minimongo/errors.d.ts +14 -0
  120. package/dist/minimongo/errors.js +16 -0
  121. package/dist/minimongo/errors.js.map +1 -0
  122. package/dist/minimongo/events.d.ts +20 -0
  123. package/dist/minimongo/events.js +80 -0
  124. package/dist/minimongo/events.js.map +1 -0
  125. package/dist/minimongo/identity.d.ts +8 -0
  126. package/dist/minimongo/identity.js +15 -0
  127. package/dist/minimongo/identity.js.map +1 -0
  128. package/dist/minimongo/immutable.d.ts +6 -0
  129. package/dist/minimongo/immutable.js +17 -0
  130. package/dist/minimongo/immutable.js.map +1 -0
  131. package/dist/minimongo/index.d.ts +7 -0
  132. package/dist/minimongo/index.js +6 -0
  133. package/dist/minimongo/index.js.map +1 -0
  134. package/dist/minimongo/local-collection.d.ts +48 -0
  135. package/dist/minimongo/local-collection.js +129 -0
  136. package/dist/minimongo/local-collection.js.map +1 -0
  137. package/dist/minimongo/matcher.d.ts +27 -0
  138. package/dist/minimongo/matcher.js +457 -0
  139. package/dist/minimongo/matcher.js.map +1 -0
  140. package/dist/minimongo/modifier.d.ts +12 -0
  141. package/dist/minimongo/modifier.js +323 -0
  142. package/dist/minimongo/modifier.js.map +1 -0
  143. package/dist/minimongo/path.d.ts +17 -0
  144. package/dist/minimongo/path.js +126 -0
  145. package/dist/minimongo/path.js.map +1 -0
  146. package/dist/minimongo/projection.d.ts +5 -0
  147. package/dist/minimongo/projection.js +100 -0
  148. package/dist/minimongo/projection.js.map +1 -0
  149. package/dist/minimongo/sorter.d.ts +16 -0
  150. package/dist/minimongo/sorter.js +140 -0
  151. package/dist/minimongo/sorter.js.map +1 -0
  152. package/dist/minimongo/transform.d.ts +4 -0
  153. package/dist/minimongo/transform.js +30 -0
  154. package/dist/minimongo/transform.js.map +1 -0
  155. package/dist/minimongo/types.d.ts +169 -0
  156. package/dist/minimongo/types.js +2 -0
  157. package/dist/minimongo/types.js.map +1 -0
  158. package/dist/minimongo/value-semantics.d.ts +8 -0
  159. package/dist/minimongo/value-semantics.js +156 -0
  160. package/dist/minimongo/value-semantics.js.map +1 -0
  161. package/dist/mongodb/change-streams.js +14 -8
  162. package/dist/mongodb/change-streams.js.map +1 -1
  163. package/dist/mongodb/decorators/collection.js +4 -1
  164. package/dist/mongodb/decorators/collection.js.map +1 -1
  165. package/dist/mongodb/decorators/metadata.js +1 -1
  166. package/dist/mongodb/decorators/metadata.js.map +1 -1
  167. package/dist/mongodb/find-one-or-create.d.ts +2 -2
  168. package/dist/mongodb/find-one-or-create.js +1 -1
  169. package/dist/mongodb/find-one-or-create.js.map +1 -1
  170. package/dist/mongodb/live/engine.js.map +1 -1
  171. package/dist/mongodb/live/source.js +11 -5
  172. package/dist/mongodb/live/source.js.map +1 -1
  173. package/dist/mongodb/live/types.d.ts +1 -1
  174. package/dist/react/components/client-provider.d.ts +3 -2
  175. package/dist/react/components/client-provider.js +1 -1
  176. package/dist/react/components/client-provider.js.map +1 -1
  177. package/dist/react/contracts.type-test.d.ts +1 -0
  178. package/dist/react/contracts.type-test.js +21 -0
  179. package/dist/react/contracts.type-test.js.map +1 -0
  180. package/dist/react/hooks/use-auth.d.ts +5 -1
  181. package/dist/react/hooks/use-auth.js +4 -2
  182. package/dist/react/hooks/use-auth.js.map +1 -1
  183. package/dist/react/hooks/use-caller.d.ts +10 -5
  184. package/dist/react/hooks/use-caller.js +12 -8
  185. package/dist/react/hooks/use-caller.js.map +1 -1
  186. package/dist/react/hooks/use-channel.d.ts +1 -1
  187. package/dist/react/hooks/use-channel.js +1 -1
  188. package/dist/react/hooks/use-channel.js.map +1 -1
  189. package/dist/react/hooks/use-circuit-breaker.d.ts +14 -6
  190. package/dist/react/hooks/use-circuit-breaker.js +5 -2
  191. package/dist/react/hooks/use-circuit-breaker.js.map +1 -1
  192. package/dist/react/hooks/use-client.d.ts +1 -1
  193. package/dist/react/hooks/use-client.js +2 -0
  194. package/dist/react/hooks/use-client.js.map +1 -1
  195. package/dist/react/hooks/use-connection-state.d.ts +4 -4
  196. package/dist/react/hooks/use-connection-state.js +2 -0
  197. package/dist/react/hooks/use-connection-state.js.map +1 -1
  198. package/dist/react/hooks/use-deps-change.d.ts +1 -1
  199. package/dist/react/hooks/use-deps-change.js.map +1 -1
  200. package/dist/react/hooks/use-local-event.d.ts +2 -2
  201. package/dist/react/hooks/use-local-event.js +2 -2
  202. package/dist/react/hooks/use-local-event.js.map +1 -1
  203. package/dist/react/hooks/use-method-refresh.d.ts +24 -15
  204. package/dist/react/hooks/use-method-refresh.js +18 -22
  205. package/dist/react/hooks/use-method-refresh.js.map +1 -1
  206. package/dist/react/hooks/use-method.d.ts +12 -11
  207. package/dist/react/hooks/use-method.js +9 -5
  208. package/dist/react/hooks/use-method.js.map +1 -1
  209. package/dist/react/hooks/use-mongodb-live-publication.js +2 -0
  210. package/dist/react/hooks/use-mongodb-live-publication.js.map +1 -1
  211. package/dist/react/hooks/use-object.d.ts +2 -2
  212. package/dist/react/hooks/use-object.js.map +1 -1
  213. package/dist/react/hooks/use-subscribe.d.ts +1 -2
  214. package/dist/react/hooks/use-subscribe.js +11 -9
  215. package/dist/react/hooks/use-subscribe.js.map +1 -1
  216. package/dist/react/hooks/use-throttled-events.d.ts +1 -1
  217. package/dist/react/hooks/use-throttled-events.js +2 -0
  218. package/dist/react/hooks/use-throttled-events.js.map +1 -1
  219. package/dist/react/hooks/use-token-refresh.js +1 -1
  220. package/dist/react/hooks/use-token-refresh.js.map +1 -1
  221. package/dist/server/client-node.d.ts +17 -10
  222. package/dist/server/client-node.js +16 -13
  223. package/dist/server/client-node.js.map +1 -1
  224. package/dist/server/decorators/register.js +10 -6
  225. package/dist/server/decorators/register.js.map +1 -1
  226. package/dist/server/method.d.ts +1 -1
  227. package/dist/server/method.js +9 -6
  228. package/dist/server/method.js.map +1 -1
  229. package/dist/server/methods.js +2 -1
  230. package/dist/server/methods.js.map +1 -1
  231. package/dist/server/server-channel.d.ts +4 -2
  232. package/dist/server/server-channel.js +33 -20
  233. package/dist/server/server-channel.js.map +1 -1
  234. package/dist/server/server.d.ts +25 -12
  235. package/dist/server/server.js +18 -17
  236. package/dist/server/server.js.map +1 -1
  237. package/dist/server/transports/node-hono-transport.d.ts +4 -4
  238. package/dist/server/transports/node-hono-transport.js +66 -14
  239. package/dist/server/transports/node-hono-transport.js.map +1 -1
  240. package/dist/server/transports/redis-transport.d.ts +5 -4
  241. package/dist/server/transports/redis-transport.js +9 -4
  242. package/dist/server/transports/redis-transport.js.map +1 -1
  243. package/dist/server/transports/websocket-transport.d.ts +1 -1
  244. package/dist/server/transports/websocket-transport.js +6 -1
  245. package/dist/server/transports/websocket-transport.js.map +1 -1
  246. package/dist/server/transports/ws-shared.d.ts +2 -1
  247. package/dist/server/transports/ws-shared.js +4 -1
  248. package/dist/server/transports/ws-shared.js.map +1 -1
  249. package/dist/utils/errors.d.ts +1 -1
  250. package/dist/utils/errors.js.map +1 -1
  251. package/dist/utils/event-emitter.d.ts +1 -1
  252. package/dist/utils/event-emitter.js.map +1 -1
  253. package/dist/utils/events.d.ts +4 -4
  254. package/dist/utils/events.js.map +1 -1
  255. package/dist/utils/helpers.d.ts +4 -4
  256. package/dist/utils/helpers.js +4 -3
  257. package/dist/utils/helpers.js.map +1 -1
  258. package/dist/utils/intercept.d.ts +1 -2
  259. package/dist/utils/intercept.js.map +1 -1
  260. package/dist/utils/iterator.d.ts +6 -8
  261. package/dist/utils/iterator.js +3 -3
  262. package/dist/utils/iterator.js.map +1 -1
  263. package/dist/utils/presentation.d.ts +1 -1
  264. package/dist/utils/presentation.js +2 -0
  265. package/dist/utils/presentation.js.map +1 -1
  266. package/dist/utils/promise.d.ts +5 -5
  267. package/dist/utils/promise.js +2 -2
  268. package/dist/utils/promise.js.map +1 -1
  269. package/package.json +9 -2
@@ -0,0 +1,329 @@
1
+ # TypeFerry local collections
2
+
3
+ `typeferry/minimongo` is TypeFerry's browser-safe, framework-neutral local
4
+ document store. The import path is stable package history; the behavior is
5
+ defined only by TypeFerry's types, tests, and documentation.
6
+
7
+ It provides immutable documents, Mongo-style selectors and modifiers, and
8
+ typed events without importing TypeFerry transports, React, or the MongoDB
9
+ driver.
10
+
11
+ ```ts
12
+ import { LocalCollection } from 'typeferry/minimongo'
13
+
14
+ interface Task {
15
+ title: string
16
+ priority: number
17
+ details: {
18
+ done: boolean
19
+ }
20
+ }
21
+
22
+ const tasks = new LocalCollection<Task>('tasks')
23
+ const id = tasks.insert({
24
+ title: 'Ship local collections',
25
+ priority: 1,
26
+ details: { done: false },
27
+ })
28
+
29
+ tasks.update(id, { $set: { 'details.done': true } })
30
+
31
+ const urgent = tasks.find(
32
+ { priority: { $lte: 2 } },
33
+ { sort: { priority: 1 } },
34
+ )
35
+
36
+ console.log(urgent.fetch())
37
+ ```
38
+
39
+ ## IDs and immutable documents
40
+
41
+ Every document has a lowercase, 24-character hexadecimal string `_id`. This is
42
+ the client representation TypeFerry uses for the default MongoDB ObjectId
43
+ identity. `insert()` generates an ID when none is supplied and rejects IDs in
44
+ other formats. The local package does not import the MongoDB driver.
45
+
46
+ Inserted values are cloned before storage. Materialized documents and event
47
+ payloads are deeply readonly and frozen. Unchanged documents retain their
48
+ identity across query snapshots, which makes reference comparison useful in UI
49
+ and state-management code.
50
+
51
+ ```ts
52
+ const before = urgent.fetch()
53
+
54
+ tasks.insert({
55
+ title: 'Unrelated task',
56
+ priority: 10,
57
+ details: { done: false },
58
+ })
59
+
60
+ const after = urgent.fetch()
61
+
62
+ console.log(before[0] === after[0]) // true
63
+ ```
64
+
65
+ ## Collection events
66
+
67
+ Collections follow TypeFerry's `on`, `off`, `once`, `onAny`, and `offAny`
68
+ event-emitter conventions. Events contain immutable snapshots.
69
+
70
+ ```ts
71
+ tasks.on('insert', ({ document }) => {
72
+ console.log('inserted', document._id)
73
+ })
74
+
75
+ tasks.on('update', ({ previous, document }) => {
76
+ console.log('changed', previous, document)
77
+ })
78
+
79
+ tasks.on('remove', ({ document }) => {
80
+ console.log('removed', document._id)
81
+ })
82
+ ```
83
+
84
+ Collection events are emitted before dependent cursors publish their updated
85
+ results.
86
+
87
+ ## Cursor events
88
+
89
+ A cursor starts observing its collection when its first event listener is
90
+ added. It stops automatically after all listeners are removed, or explicitly
91
+ with `cursor.stop()`.
92
+
93
+ ```ts
94
+ urgent.on('added', ({ document, index }) => {
95
+ console.log('entered query', document, index)
96
+ })
97
+
98
+ urgent.on('changed', ({ previous, document, previousIndex, index }) => {
99
+ console.log('changed inside query', previous, document, previousIndex, index)
100
+ })
101
+
102
+ urgent.on('removed', ({ document, index }) => {
103
+ console.log('left query', document, index)
104
+ })
105
+
106
+ urgent.on('change', documents => {
107
+ console.log('complete result snapshot', documents)
108
+ })
109
+ ```
110
+
111
+ Granular events describe rows entering, changing within, or leaving the result.
112
+ The `change` event follows them once with the complete result snapshot. A
113
+ mutation that does not affect the query result emits nothing from that cursor.
114
+
115
+ ## API reference
116
+
117
+ ### `LocalCollection<TSchema>`
118
+
119
+ Create a collection with an optional diagnostic name:
120
+
121
+ ```ts
122
+ const tasks = new LocalCollection<Task>('tasks')
123
+ ```
124
+
125
+ `TSchema` describes insertable application fields. Materialized documents add
126
+ a readonly string `_id` and recursively make schema fields readonly.
127
+
128
+ | Member | Result | Behavior |
129
+ |---|---|---|
130
+ | `name` | `string \| undefined` | The name supplied to the constructor. It does not create persistence or a global registry. |
131
+ | `insert(document)` | `string` | Clones, validates, freezes, and inserts one document. Generates `_id` when omitted. Emits `insert`. |
132
+ | `insertAsync(document)` | `Promise<string>` | Async convenience form of `insert`; the mutation and event happen synchronously before the promise settles. |
133
+ | `find(selector?, options?)` | `Cursor` | Creates a lazy cursor. Calling `find()` with no arguments selects all documents. |
134
+ | `findOne(selector?, options?)` | document or `undefined` | Returns the first matching result after options are applied. Calling `findOne()` with no arguments selects all documents. |
135
+ | `findOneAsync(selector?, options?)` | `Promise<document \| undefined>` | Async convenience form of `findOne`. |
136
+ | `countDocuments(selector?, options?)` | `Promise<number>` | Counts matching documents after `skip` and `limit`. |
137
+ | `estimatedDocumentCount(options?)` | `Promise<number>` | Counts all documents after `skip` and `limit`; no estimate or index is involved. |
138
+ | `update(selector, modifier, options?)` | `number` | Updates the first match, or every match with `{ multi: true }`. Emits one `update` per document. |
139
+ | `updateAsync(...)` | `Promise<number>` | Async convenience form of `update`. |
140
+ | `upsert(selector, modifier, options?)` | `UpsertResult` | Updates matching documents or inserts one derived document. `insertedId` can supply the ID for the insert branch. |
141
+ | `upsertAsync(...)` | `Promise<UpsertResult>` | Async convenience form of `upsert`. |
142
+ | `remove(selector)` | `number` | Removes every match and emits one `remove` per document. |
143
+ | `removeAsync(selector)` | `Promise<number>` | Async convenience form of `remove`. |
144
+ | `on`, `once`, `off` | collection | Adds or removes a typed listener and supports chaining. Duplicate registration of the same listener is ignored. |
145
+ | `onAny`, `offAny` | collection | Adds or removes a listener receiving `(eventName, payload)`. |
146
+ | `removeAllListeners(event?)` | collection | Removes listeners for one event or for the entire collection. |
147
+
148
+ `documents()` and `addObserver()` are exposed to satisfy the cursor contract.
149
+ They are low-level integration members: application queries should use
150
+ `find()`, and application observation should use typed collection or cursor
151
+ events. `documents()` returns the current stored snapshots in insertion order.
152
+ `addObserver(callback)` returns an unsubscribe function and supplies only an
153
+ invalidation signal.
154
+
155
+ ### Collection events
156
+
157
+ | Event | Payload |
158
+ |---|---|
159
+ | `insert` | `{ document }` containing the inserted snapshot. |
160
+ | `update` | `{ previous, document }` containing the snapshots before and after the update. |
161
+ | `remove` | `{ document }` containing the removed snapshot. |
162
+
163
+ For a multi-document mutation, collection events follow collection iteration
164
+ order. Collection events run synchronously after state changes and before
165
+ dependent cursor events. A listener exception propagates to the mutating
166
+ caller; it does not roll back the completed state change.
167
+
168
+ ### `Cursor<TDocument, TOutput>`
169
+
170
+ Obtain cursors from `LocalCollection.find()`. Constructing a cursor directly is
171
+ not an application API because its collection contract contains package-owned
172
+ runtime state.
173
+
174
+ | Member | Result | Behavior |
175
+ |---|---|---|
176
+ | `matcher` | `Matcher<TDocument>` | Compiled matcher used by this cursor. |
177
+ | `count()` | `number` | Counts the current result after `skip` and `limit`. |
178
+ | `countAsync()` | `Promise<number>` | Async convenience form of `count`. |
179
+ | `fetch()` | readonly document array | Evaluates and freezes the current result. |
180
+ | `fetchAsync()` | promise of readonly document array | Async convenience form of `fetch`. |
181
+ | `forEach(callback, thisArg?)` | `void` | Iterates one evaluated snapshot in result order. |
182
+ | `forEachAsync(callback, thisArg?)` | `Promise<void>` | Awaits each callback sequentially in result order. |
183
+ | `map(callback, thisArg?)` | array | Maps one evaluated snapshot. |
184
+ | `mapAsync(callback, thisArg?)` | `Promise<array>` | Awaits each mapping callback sequentially. |
185
+ | `getTransform()` | transform or `undefined` | Returns the identity-preserving transform configured through `FindOptions`. |
186
+ | `stop()` | `void` | Detaches collection observation and clears the saved result. Existing listeners remain registered. Adding another listener starts observation again. |
187
+ | `[Symbol.iterator]()` | iterator | Iterates the result of one `fetch()`. |
188
+ | `[Symbol.asyncIterator]()` | async iterator | Asynchronously iterates the result of one `fetch()`. |
189
+ | `on`, `once`, `off` | cursor | Manages typed cursor listeners and supports chaining. |
190
+ | `onAny`, `offAny` | cursor | Manages listeners receiving `(eventName, payload)`. |
191
+ | `removeAllListeners(event?)` | cursor | Removes listeners; removing the last listener automatically stops observation. |
192
+
193
+ Adding the first listener captures the current result as the baseline. Initial
194
+ rows are not emitted as `added`; events describe subsequent mutations.
195
+
196
+ ### Cursor events
197
+
198
+ | Event | Payload |
199
+ |---|---|
200
+ | `added` | `{ document, index }` when a document enters the result. |
201
+ | `changed` | `{ previous, document, previousIndex, index }` when a result document's visible value changes. |
202
+ | `removed` | `{ document, index }` when a document leaves the result. |
203
+ | `change` | The complete readonly result array, emitted once after granular events. |
204
+
205
+ An ordering-only change emits `change` without inventing a row-level change.
206
+ Projection and transform output determine whether a cursor-visible document
207
+ changed. Every payload and snapshot is frozen.
208
+
209
+ ### Selectors
210
+
211
+ A selector is either a string document ID or a typed object. Object fields use
212
+ dot-separated paths and may contain literal values or operators.
213
+
214
+ | Group | Supported syntax |
215
+ |---|---|
216
+ | Comparison | `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin` |
217
+ | Presence and negation | `$exists`, `$not` |
218
+ | Logical | `$and`, `$or`, `$nor`; `$comment` is accepted and does not affect matching |
219
+ | Strings | `RegExp`, `$regex`, `$options`; supported flags are `i`, `m`, and `g` |
220
+ | Arrays | Literal element matching, `$all`, `$elemMatch`, `$size` |
221
+ | Numbers and binary | `$mod`, `$bitsAllClear`, `$bitsAllSet`, `$bitsAnyClear`, `$bitsAnySet` |
222
+ | Value type | `$type` with supported BSON-style aliases or numeric codes |
223
+ | Location | `$near`, optional `$maxDistance`, coordinate pairs, and GeoJSON `Point` values |
224
+
225
+ `$near` uses Euclidean distance for coordinate pairs and meters for GeoJSON
226
+ points. It also supplies the default nearest-first ordering when no explicit
227
+ sort is provided. JavaScript predicates and `$where` are not supported.
228
+
229
+ ### Find options
230
+
231
+ | Option | Type | Behavior |
232
+ |---|---|---|
233
+ | `sort` | object, tuple array, field array, or comparator | Orders results. Directions are `1`, `-1`, `asc`, `desc`, `ascending`, or `descending`. |
234
+ | `skip` | `number` | Omits results from the beginning of the ordered result. Defaults to `0`. |
235
+ | `limit` | `number` | Caps the returned result after `skip`. |
236
+ | `projection` | field map | Includes or excludes fields using `1`/`true` or `0`/`false`. Inclusion and exclusion cannot be mixed except for `_id`. |
237
+ | `transform` | function or `null` | Maps every projected document while retaining its original `_id`. It must return an object and cannot replace `_id`. |
238
+ | `collation` | `CollationOptions` | Applies `Intl.Collator` string comparison using the required `locale` and supported strength, case, and numeric options. |
239
+
240
+ Sorting accepts nested paths. Sorting across parallel arrays is rejected.
241
+ Projection operators such as `$slice`, `$elemMatch`, and `$meta` are not
242
+ supported inside the projection document.
243
+
244
+ ### Mutations
245
+
246
+ An update can be a replacement document or an operator document, but cannot mix
247
+ the two forms. `_id` is immutable. Dot paths are typed from the collection
248
+ schema, and positional `.$.` updates use array-match metadata from the selector.
249
+
250
+ | Operator | Behavior |
251
+ |---|---|
252
+ | `$set`, `$unset`, `$rename` | Writes, removes, or renames fields. |
253
+ | `$inc`, `$mul`, `$min`, `$max` | Applies numeric changes or bounds. |
254
+ | `$currentDate` | Stores the current `Date`; accepts `true` or `{ $type: 'date' }`. |
255
+ | `$push` | Appends an item or uses `$each`, `$position`, `$slice`, and `$sort`. |
256
+ | `$pushAll` | Appends an array of items. |
257
+ | `$addToSet` | Adds distinct items; accepts one value or `$each`. |
258
+ | `$pop` | Removes from the beginning for a negative argument or the end otherwise. |
259
+ | `$pull`, `$pullAll` | Removes array elements matching a value selector or value list. |
260
+ | `$setOnInsert` | Applies only when `upsert()` takes its insert branch. |
261
+
262
+ `UpdateOptions` contains `multi?: boolean`. `UpsertOptions` contains
263
+ `multi?: boolean` and `insertedId?: string`. `UpsertResult` always contains
264
+ `numberAffected`; `insertedId` is present only when an insert occurred.
265
+
266
+ ### `Matcher` and `Sorter`
267
+
268
+ These are exported for advanced query tooling:
269
+
270
+ ```ts
271
+ const matcher = new Matcher<Task>({ priority: { $lte: 2 } })
272
+ const matches = matcher.documentMatches(task)
273
+
274
+ const compare = new Sorter<Task>({ priority: 1 }).getComparator()
275
+ ```
276
+
277
+ `Matcher.documentMatches()` returns `MatchResult` with `result` and optional
278
+ geospatial `distance` or positional `arrayIndices`. `hasGeoQuery()` identifies
279
+ a `$near` selector, and `isSimple()` reports whether compilation found only
280
+ simple field matching. `isIdSelector(value)` narrows any string to the ID
281
+ selector type. It identifies selector syntax; collection mutations separately
282
+ validate the required lowercase, 24-character hexadecimal ID format.
283
+
284
+ `Sorter.getComparator({ distances? })` returns a comparator. The optional
285
+ distance map supports nearest-first sorting for geospatial queries. `_getPaths()`
286
+ on both helpers exposes their compiled field paths for package tooling; the
287
+ underscore marks it as a low-level API.
288
+
289
+ ### Errors
290
+
291
+ `LocalQueryError` reports invalid selectors, collation, and sort input.
292
+ `LocalCollectionError` reports duplicate IDs, invalid projections, and invalid
293
+ mutations. It includes optional `field` and `setPropertyError` context when the
294
+ failure is tied to a mutation path. Invalid ID formats and cyclic document
295
+ values raise `TypeError`.
296
+
297
+ ### Exported types
298
+
299
+ The entrypoint exports the following type families:
300
+
301
+ - Documents: `MinimongoId`, `InsertDocument`, `MaterializedDocument`,
302
+ `TransformedDocument`, and `DeepReadonly`.
303
+ - Queries: `Selector`, `SelectorValue`, `ComparisonSelector`, `StringSelector`,
304
+ `NumericSelector`, `ArraySelector`, `LogicalSelector`, `FieldPath`,
305
+ `ValueAtPath`, and `MatchResult`.
306
+ - Results: `FindOptions`, `Projection`, `SortSpecifier`, `SortDirection`,
307
+ `CollationOptions`, and `CollectionCursor`.
308
+ - Mutations: `Modifier`, `UpdateOptions`, `UpsertOptions`, and `UpsertResult`.
309
+ - Events: `CollectionInsertEvent`, `CollectionUpdateEvent`,
310
+ `CollectionRemoveEvent`, `CursorAddedEvent`, `CursorChangedEvent`, and
311
+ `CursorRemovedEvent`.
312
+
313
+ The store is intentionally local-only and in-memory. It does not provide
314
+ persistence, indexes, aggregation, transport synchronization, publications, or
315
+ implicit React bindings.
316
+
317
+ ## Migrating from the former API
318
+
319
+ - Change every document identity to a lowercase 24-character hexadecimal
320
+ string and remove uses of the former object identity class.
321
+ - Replace observer callback objects with `cursor.on(...)` listeners.
322
+ - Replace `fields` with `projection`; cursor events are always reactive.
323
+ - Remove compatibility runtime factories, component overrides, observer
324
+ pause/resume calls, original-document tracking, mutation callbacks, predicate
325
+ selectors, and `$where` selectors.
326
+ - Treat returned documents as immutable snapshots and perform all changes
327
+ through collection mutations.
328
+
329
+ See `NOTICE.md` for required attribution covering inherited algorithmic work.
@@ -0,0 +1,31 @@
1
+ import { type DocumentStoreFactory } from './document-store.js';
2
+ import { type IdentityCodec } from './identity.js';
3
+ import { Matcher } from './matcher.js';
4
+ import { type ModifyOptions } from './modifier.js';
5
+ import { type ProjectionFunction } from './projection.js';
6
+ import { Sorter } from './sorter.js';
7
+ import type { CollationOptions, Modifier, Projection, Selector, SortSpecifier } from './types.js';
8
+ import { type ValueSemantics } from './value-semantics.js';
9
+ export interface QueryEngine {
10
+ matcher<TDocument extends object>(selector: Selector<TDocument> | unknown, options?: {
11
+ readonly isUpdate?: boolean;
12
+ readonly collation?: CollationOptions;
13
+ }): Matcher<TDocument>;
14
+ sorter<TDocument extends object>(sort: SortSpecifier<TDocument> | unknown, collation?: CollationOptions): Sorter<TDocument>;
15
+ projection<TDocument extends object>(projection?: Projection<TDocument> | Record<string, unknown>): ProjectionFunction<TDocument>;
16
+ }
17
+ export interface MutationEngine {
18
+ modify<TDocument extends Record<string, unknown>>(document: TDocument, modifier: Modifier<TDocument> | Partial<TDocument> | unknown, options?: ModifyOptions): void;
19
+ createUpsert<TDocument extends Record<string, unknown>>(selector: unknown, modifier: Modifier<TDocument> | Partial<TDocument>): TDocument;
20
+ }
21
+ export interface LocalStoreRuntime {
22
+ readonly values: ValueSemantics;
23
+ readonly identities: IdentityCodec<unknown>;
24
+ readonly documentStoreFactory: DocumentStoreFactory;
25
+ readonly query: QueryEngine;
26
+ readonly mutations: MutationEngine;
27
+ readonly randomId: () => string;
28
+ readonly now: () => Date;
29
+ }
30
+ export declare const LOCAL_STORE_RUNTIME: unique symbol;
31
+ export declare const localStoreRuntime: LocalStoreRuntime;
@@ -0,0 +1,53 @@
1
+ import { MemoryDocumentStore } from './document-store.js';
2
+ import { stringIdentityCodec } from './identity.js';
3
+ import { Matcher } from './matcher.js';
4
+ import { createUpsertDocument, modifyDocument } from './modifier.js';
5
+ import { compileProjection } from './projection.js';
6
+ import { Sorter } from './sorter.js';
7
+ import { localValueSemantics } from './value-semantics.js';
8
+ export const LOCAL_STORE_RUNTIME = Symbol('TypeFerryLocalStoreRuntime');
9
+ function randomId() {
10
+ const bytes = new Uint8Array(12);
11
+ crypto.getRandomValues(bytes);
12
+ return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
13
+ }
14
+ function queryEngine(values) {
15
+ return {
16
+ matcher(selector, options) {
17
+ return new Matcher(selector, options?.isUpdate, options?.collation, values);
18
+ },
19
+ sorter(sort, collation) {
20
+ return new Sorter(sort, collation, values);
21
+ },
22
+ projection(projection) {
23
+ return compileProjection(projection, values);
24
+ },
25
+ };
26
+ }
27
+ function mutationEngine(values) {
28
+ return {
29
+ modify(document, modifier, options) {
30
+ modifyDocument(document, modifier, options, values);
31
+ },
32
+ createUpsert(selector, modifier) {
33
+ return createUpsertDocument(selector, modifier, values);
34
+ },
35
+ };
36
+ }
37
+ function documentStoreFactory(identities, values) {
38
+ return {
39
+ create() {
40
+ return new MemoryDocumentStore(identities, values);
41
+ },
42
+ };
43
+ }
44
+ export const localStoreRuntime = Object.freeze({
45
+ values: localValueSemantics,
46
+ identities: stringIdentityCodec,
47
+ documentStoreFactory: documentStoreFactory(stringIdentityCodec, localValueSemantics),
48
+ query: queryEngine(localValueSemantics),
49
+ mutations: mutationEngine(localValueSemantics),
50
+ randomId,
51
+ now: () => new Date(),
52
+ });
53
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/minimongo/components.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAiD,MAAM,kBAAkB,CAAA;AACrG,OAAO,EAAE,mBAAmB,EAAsB,MAAM,YAAY,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAsB,MAAM,YAAY,CAAA;AACrF,OAAO,EAAE,iBAAiB,EAA2B,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,OAAO,EAAE,mBAAmB,EAAuB,MAAM,mBAAmB,CAAA;AAyC5E,MAAM,CAAC,MAAM,mBAAmB,GAAkB,MAAM,CAAC,4BAA4B,CAAC,CAAA;AAEtF,SAAS,QAAQ;IACf,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAChC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAE7B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC/E,CAAC;AAED,SAAS,WAAW,CAAC,MAAsB;IACzC,OAAO;QACL,OAAO,CAAC,QAAQ,EAAE,OAAO;YACvB,OAAO,IAAI,OAAO,CAChB,QAAQ,EACR,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,SAAS,EAClB,MAAM,CACP,CAAA;QACH,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,SAAS;YACpB,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC;QACD,UAAU,CAAC,UAAU;YACnB,OAAO,iBAAiB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QAC9C,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,MAAsB;IAC5C,OAAO;QACL,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO;YAChC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;QACrD,CAAC;QACD,YAAY,CAAC,QAAQ,EAAE,QAAQ;YAC7B,OAAO,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;QACzD,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,oBAAoB,CAC3B,UAAkC,EAClC,MAAsB;IAEtB,OAAO;QACL,MAAM;YACJ,OAAO,IAAI,mBAAmB,CAC5B,UAAgC,EAChC,MAAM,CACP,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAsB,MAAM,CAAC,MAAM,CAAC;IAChE,MAAM,EAAE,mBAAmB;IAC3B,UAAU,EAAE,mBAAmB;IAC/B,oBAAoB,EAAE,oBAAoB,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IACpF,KAAK,EAAE,WAAW,CAAC,mBAAmB,CAAC;IACvC,SAAS,EAAE,cAAc,CAAC,mBAAmB,CAAC;IAC9C,QAAQ;IACR,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;CACtB,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { LocalCollection, } from './index.js';
2
+ const tasks = new LocalCollection();
3
+ const id = tasks.insert({ title: 'Typed', nested: { done: false } });
4
+ const document = tasks.findOne(id);
5
+ const snapshot = tasks.find().fetch();
6
+ tasks.on('insert', event => {
7
+ const insertedId = event.document._id;
8
+ void insertedId;
9
+ // @ts-expect-error Public snapshots are deeply readonly.
10
+ event.document.nested.done = true;
11
+ });
12
+ tasks.find().on('change', documents => {
13
+ const first = documents[0];
14
+ // @ts-expect-error Cursor snapshots are readonly arrays.
15
+ documents.push(first);
16
+ });
17
+ // @ts-expect-error Local collections accept only string IDs.
18
+ tasks.insert({ _id: 1, title: 'Invalid', nested: { done: false } });
19
+ void document;
20
+ void snapshot;
21
+ //# sourceMappingURL=contracts.type-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.type-test.js","sourceRoot":"","sources":["../../src/minimongo/contracts.type-test.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,GAGhB,MAAM,SAAS,CAAA;AAShB,MAAM,KAAK,GAAG,IAAI,eAAe,EAAQ,CAAA;AACzC,MAAM,EAAE,GAAW,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;AAC5E,MAAM,QAAQ,GAA2C,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;AAC1E,MAAM,QAAQ,GAAwD,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAA;AAE1F,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE;IACzB,MAAM,UAAU,GAAW,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAA;IAC7C,KAAK,UAAU,CAAA;IAEf,yDAAyD;IACzD,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;AACnC,CAAC,CAAC,CAAA;AAEF,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE;IACpC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;IAE1B,yDAAyD;IACzD,SAAS,CAAC,IAAI,CAAC,KAAM,CAAC,CAAA;AACxB,CAAC,CAAC,CAAA;AAEF,6DAA6D;AAC7D,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;AAEnE,KAAK,QAAQ,CAAA;AACb,KAAK,QAAQ,CAAA"}
@@ -0,0 +1,67 @@
1
+ import { LOCAL_STORE_RUNTIME, type LocalStoreRuntime } from './components.js';
2
+ import { TypedEventEmitter } from './events.js';
3
+ import { type DeepReadonly } from './immutable.js';
4
+ import { Matcher } from './matcher.js';
5
+ import type { FindOptions, MaterializedDocument, Selector, TransformedDocument } from './types.js';
6
+ export interface CursorCollection<TDocument extends object> {
7
+ readonly name: string | undefined;
8
+ readonly [LOCAL_STORE_RUNTIME]: LocalStoreRuntime;
9
+ documents(): Iterable<TDocument>;
10
+ addObserver(observer: () => void): () => void;
11
+ }
12
+ export interface CursorAddedEvent<TDocument extends object> {
13
+ readonly document: TDocument;
14
+ readonly index: number;
15
+ }
16
+ export interface CursorChangedEvent<TDocument extends object> {
17
+ readonly previous: TDocument;
18
+ readonly document: TDocument;
19
+ readonly previousIndex: number;
20
+ readonly index: number;
21
+ }
22
+ export interface CursorRemovedEvent<TDocument extends object> {
23
+ readonly document: TDocument;
24
+ readonly index: number;
25
+ }
26
+ type CursorEvents<TDocument extends object> = {
27
+ change: readonly [documents: readonly TDocument[]];
28
+ added: readonly [event: CursorAddedEvent<TDocument>];
29
+ changed: readonly [event: CursorChangedEvent<TDocument>];
30
+ removed: readonly [event: CursorRemovedEvent<TDocument>];
31
+ };
32
+ /** A lazily evaluated query with event-driven immutable result snapshots. */
33
+ export declare class Cursor<TDocument extends object, TOutput extends object = TDocument> extends TypedEventEmitter<CursorEvents<DeepReadonly<TransformedDocument<TDocument, TOutput>>>> implements Iterable<DeepReadonly<TransformedDocument<TDocument, TOutput>>>, AsyncIterable<DeepReadonly<TransformedDocument<TDocument, TOutput>>> {
34
+ private readonly collection;
35
+ private readonly options;
36
+ readonly matcher: Matcher<TDocument>;
37
+ private readonly projection;
38
+ private readonly transform;
39
+ private readonly sorter;
40
+ private readonly skip;
41
+ private readonly limit;
42
+ private previous;
43
+ private removeObserver;
44
+ private listenerCount;
45
+ constructor(collection: CursorCollection<TDocument>, selector: Selector<TDocument> | unknown, options?: FindOptions<TDocument, TOutput>);
46
+ count(): number;
47
+ countAsync(): Promise<number>;
48
+ fetch(): readonly DeepReadonly<TransformedDocument<TDocument, TOutput>>[];
49
+ fetchAsync(): Promise<readonly DeepReadonly<TransformedDocument<TDocument, TOutput>>[]>;
50
+ forEach(callback: (document: DeepReadonly<TransformedDocument<TDocument, TOutput>>, index: number, cursor: this) => void, thisArg?: unknown): void;
51
+ map<TResult>(callback: (document: DeepReadonly<TransformedDocument<TDocument, TOutput>>, index: number, cursor: this) => TResult, thisArg?: unknown): TResult[];
52
+ forEachAsync(callback: (document: DeepReadonly<TransformedDocument<TDocument, TOutput>>, index: number, cursor: this) => void | Promise<void>, thisArg?: unknown): Promise<void>;
53
+ mapAsync<TResult>(callback: (document: DeepReadonly<TransformedDocument<TDocument, TOutput>>, index: number, cursor: this) => TResult | Promise<TResult>, thisArg?: unknown): Promise<TResult[]>;
54
+ getTransform(): ((document: TDocument) => TransformedDocument<TDocument, TOutput>) | undefined;
55
+ [Symbol.iterator](): Iterator<DeepReadonly<TransformedDocument<TDocument, TOutput>>>;
56
+ [Symbol.asyncIterator](): AsyncIterator<DeepReadonly<TransformedDocument<TDocument, TOutput>>>;
57
+ /** Stops observing collection mutations while retaining current listeners. */
58
+ stop(): void;
59
+ protected onListenerAdded(): void;
60
+ protected onListenerRemoved(): void;
61
+ private handleMutation;
62
+ private rawDocuments;
63
+ private outputDocument;
64
+ private idOf;
65
+ }
66
+ export type CollectionCursor<TSchema extends object, TOutput extends object = MaterializedDocument<TSchema, string>> = Cursor<MaterializedDocument<TSchema, string>, TOutput>;
67
+ export {};
@@ -0,0 +1,157 @@
1
+ import { LOCAL_STORE_RUNTIME } from './components.js';
2
+ import { TypedEventEmitter } from './events.js';
3
+ import { deepFreeze } from './immutable.js';
4
+ import { Matcher } from './matcher.js';
5
+ import { wrapTransform } from './transform.js';
6
+ /** A lazily evaluated query with event-driven immutable result snapshots. */
7
+ export class Cursor extends TypedEventEmitter {
8
+ collection;
9
+ options;
10
+ matcher;
11
+ projection;
12
+ transform;
13
+ sorter;
14
+ skip;
15
+ limit;
16
+ previous;
17
+ removeObserver;
18
+ listenerCount = 0;
19
+ constructor(collection, selector, options = {}) {
20
+ super();
21
+ this.collection = collection;
22
+ this.options = options;
23
+ this.matcher = collection[LOCAL_STORE_RUNTIME].query.matcher(selector, {
24
+ ...(options.collation ? { collation: options.collation } : {}),
25
+ });
26
+ this.sorter = options.sort
27
+ ? collection[LOCAL_STORE_RUNTIME].query.sorter(options.sort, options.collation)
28
+ : this.matcher.hasGeoQuery()
29
+ ? collection[LOCAL_STORE_RUNTIME].query.sorter([], options.collation)
30
+ : undefined;
31
+ this.projection = collection[LOCAL_STORE_RUNTIME].query.projection(options.projection);
32
+ this.transform = wrapTransform(options.transform, collection[LOCAL_STORE_RUNTIME].values);
33
+ this.skip = options.skip ?? 0;
34
+ this.limit = options.limit;
35
+ }
36
+ count() {
37
+ return this.rawDocuments().length;
38
+ }
39
+ countAsync() {
40
+ return Promise.resolve(this.count());
41
+ }
42
+ fetch() {
43
+ return deepFreeze(this.rawDocuments().map(document => this.outputDocument(document)));
44
+ }
45
+ fetchAsync() {
46
+ return Promise.resolve(this.fetch());
47
+ }
48
+ forEach(callback, thisArg) {
49
+ this.fetch().forEach((document, index) => callback.call(thisArg, document, index, this));
50
+ }
51
+ map(callback, thisArg) {
52
+ return this.fetch().map((document, index) => callback.call(thisArg, document, index, this));
53
+ }
54
+ async forEachAsync(callback, thisArg) {
55
+ let index = 0;
56
+ for (const document of this.fetch()) {
57
+ await callback.call(thisArg, document, index, this);
58
+ index += 1;
59
+ }
60
+ }
61
+ async mapAsync(callback, thisArg) {
62
+ const results = [];
63
+ await this.forEachAsync(async (document, index) => {
64
+ results.push(await callback.call(thisArg, document, index, this));
65
+ });
66
+ return results;
67
+ }
68
+ getTransform() {
69
+ return this.transform;
70
+ }
71
+ [Symbol.iterator]() {
72
+ return this.fetch()[Symbol.iterator]();
73
+ }
74
+ [Symbol.asyncIterator]() {
75
+ const iterator = this[Symbol.iterator]();
76
+ return { next: async () => iterator.next() };
77
+ }
78
+ /** Stops observing collection mutations while retaining current listeners. */
79
+ stop() {
80
+ this.removeObserver?.();
81
+ this.removeObserver = undefined;
82
+ this.previous = undefined;
83
+ }
84
+ onListenerAdded() {
85
+ this.listenerCount += 1;
86
+ if (this.removeObserver)
87
+ return;
88
+ this.previous = this.fetch();
89
+ this.removeObserver = this.collection.addObserver(() => this.handleMutation());
90
+ }
91
+ onListenerRemoved() {
92
+ this.listenerCount = Math.max(0, this.listenerCount - 1);
93
+ if (this.listenerCount === 0)
94
+ this.stop();
95
+ }
96
+ handleMutation() {
97
+ const previous = this.previous ?? [];
98
+ const current = this.fetch();
99
+ const previousById = new Map(previous.map((document, index) => [this.idOf(document), { document, index }]));
100
+ const currentById = new Map(current.map((document, index) => [this.idOf(document), { document, index }]));
101
+ let changed = false;
102
+ for (const [id, entry] of previousById) {
103
+ if (currentById.has(id))
104
+ continue;
105
+ this.emit('removed', deepFreeze({ document: entry.document, index: entry.index }));
106
+ changed = true;
107
+ }
108
+ for (const [id, entry] of currentById) {
109
+ const old = previousById.get(id);
110
+ if (!old) {
111
+ this.emit('added', deepFreeze({ document: entry.document, index: entry.index }));
112
+ changed = true;
113
+ }
114
+ else if (old.document !== entry.document
115
+ && !this.collection[LOCAL_STORE_RUNTIME].values.equals(old.document, entry.document)) {
116
+ this.emit('changed', deepFreeze({
117
+ previous: old.document,
118
+ document: entry.document,
119
+ previousIndex: old.index,
120
+ index: entry.index,
121
+ }));
122
+ changed = true;
123
+ }
124
+ else if (old.index !== entry.index) {
125
+ changed = true;
126
+ }
127
+ }
128
+ this.previous = current;
129
+ if (changed)
130
+ this.emit('change', current);
131
+ }
132
+ rawDocuments() {
133
+ const distances = new Map();
134
+ const documents = [];
135
+ for (const document of this.collection.documents()) {
136
+ const result = this.matcher.documentMatches(document);
137
+ if (!result.result)
138
+ continue;
139
+ documents.push(document);
140
+ if (result.distance !== undefined)
141
+ distances.set(this.idOf(document), result.distance);
142
+ }
143
+ if (this.sorter)
144
+ documents.sort(this.sorter.getComparator({ distances }));
145
+ return documents.slice(this.skip, this.limit === undefined ? undefined : this.skip + this.limit);
146
+ }
147
+ outputDocument(document) {
148
+ const hasProjection = Object.keys(this.options.projection ?? {}).length > 0;
149
+ const projected = hasProjection ? this.projection(document) : document;
150
+ const output = this.transform ? this.transform(projected) : projected;
151
+ return deepFreeze(output);
152
+ }
153
+ idOf(document) {
154
+ return String(Reflect.get(document, '_id'));
155
+ }
156
+ }
157
+ //# sourceMappingURL=cursor.js.map