typeferry 0.12.0 → 0.13.1
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.
- package/README.md +5 -0
- package/dist/application/config.d.ts +47 -39
- package/dist/application/config.js +29 -16
- package/dist/application/config.js.map +1 -1
- package/dist/application/native-simulator-settings.js +1 -1
- package/dist/application/native-simulator-settings.js.map +1 -1
- package/dist/application/proxy.d.ts +5 -1
- package/dist/application/proxy.js +25 -4
- package/dist/application/proxy.js.map +1 -1
- package/dist/application/run-tests.js +1 -1
- package/dist/application/run-tests.js.map +1 -1
- package/dist/auth/client/native-session.js +4 -2
- package/dist/auth/client/native-session.js.map +1 -1
- package/dist/auth/client/oauth/use-google-login.js +2 -0
- package/dist/auth/client/oauth/use-google-login.js.map +1 -1
- package/dist/auth/server/cookie-utils.js +4 -3
- package/dist/auth/server/cookie-utils.js.map +1 -1
- package/dist/auth/server/device-info.js +8 -8
- package/dist/auth/server/device-info.js.map +1 -1
- package/dist/auth/server/oauth/google.js +11 -7
- package/dist/auth/server/oauth/google.js.map +1 -1
- package/dist/auth/server/session-manager.js +2 -2
- package/dist/auth/server/session-manager.js.map +1 -1
- package/dist/client/client-channel.d.ts +3 -11
- package/dist/client/client-channel.js +11 -4
- package/dist/client/client-channel.js.map +1 -1
- package/dist/client/client-http.d.ts +2 -2
- package/dist/client/client-http.js.map +1 -1
- package/dist/client/client.d.ts +13 -13
- package/dist/client/client.js +24 -11
- package/dist/client/client.js.map +1 -1
- package/dist/client/context-manager.d.ts +1 -1
- package/dist/client/logger.d.ts +1 -0
- package/dist/client/logger.js +12 -15
- package/dist/client/logger.js.map +1 -1
- package/dist/client/visibility-manager.d.ts +22 -11
- package/dist/client/visibility-manager.js +258 -42
- package/dist/client/visibility-manager.js.map +1 -1
- package/dist/ejson/adjust-types-from-json-value.d.ts +1 -1
- package/dist/ejson/adjust-types-from-json-value.js +4 -2
- package/dist/ejson/adjust-types-from-json-value.js.map +1 -1
- package/dist/ejson/base64.d.ts +1 -1
- package/dist/ejson/base64.js +1 -1
- package/dist/ejson/base64.js.map +1 -1
- package/dist/ejson/built-in-converters.d.ts +59 -1
- package/dist/ejson/built-in-converters.js +109 -125
- package/dist/ejson/built-in-converters.js.map +1 -1
- package/dist/ejson/clone.js +1 -1
- package/dist/ejson/clone.js.map +1 -1
- package/dist/ejson/custom-models.d.ts +10 -10
- package/dist/ejson/custom-models.js +22 -2
- package/dist/ejson/custom-models.js.map +1 -1
- package/dist/ejson/custom-types.d.ts +2 -1
- package/dist/ejson/custom-types.js.map +1 -1
- package/dist/ejson/equals.d.ts +4 -1
- package/dist/ejson/equals.js +6 -3
- package/dist/ejson/equals.js.map +1 -1
- package/dist/ejson/from-json-value.d.ts +1 -1
- package/dist/ejson/from-json-value.js +1 -1
- package/dist/ejson/from-json-value.js.map +1 -1
- package/dist/ejson/helpers/adjust-types-to-json-value.d.ts +1 -1
- package/dist/ejson/helpers/adjust-types-to-json-value.js +4 -2
- package/dist/ejson/helpers/adjust-types-to-json-value.js.map +1 -1
- package/dist/ejson/helpers/from-json-value-helper.d.ts +1 -1
- package/dist/ejson/helpers/from-json-value-helper.js +5 -3
- package/dist/ejson/helpers/from-json-value-helper.js.map +1 -1
- package/dist/ejson/helpers/to-json-value-helper.d.ts +1 -1
- package/dist/ejson/helpers/to-json-value-helper.js +3 -1
- package/dist/ejson/helpers/to-json-value-helper.js.map +1 -1
- package/dist/ejson/index.d.ts +14 -12
- package/dist/ejson/index.js +3 -3
- package/dist/ejson/index.js.map +1 -1
- package/dist/ejson/is-binary.d.ts +1 -1
- package/dist/ejson/is-binary.js +1 -1
- package/dist/ejson/is-binary.js.map +1 -1
- package/dist/ejson/parse.d.ts +1 -1
- package/dist/ejson/parse.js +1 -1
- package/dist/ejson/parse.js.map +1 -1
- package/dist/ejson/stable-stringify.d.ts +1 -6
- package/dist/ejson/stable-stringify.js +3 -1
- package/dist/ejson/stable-stringify.js.map +1 -1
- package/dist/ejson/to-json-value.d.ts +1 -1
- package/dist/ejson/to-json-value.js +1 -1
- package/dist/ejson/to-json-value.js.map +1 -1
- package/dist/ejson/utils.d.ts +15 -13
- package/dist/ejson/utils.js +39 -37
- package/dist/ejson/utils.js.map +1 -1
- package/dist/minimongo/NOTICE.md +30 -0
- package/dist/minimongo/README.md +329 -0
- package/dist/minimongo/components.d.ts +31 -0
- package/dist/minimongo/components.js +53 -0
- package/dist/minimongo/components.js.map +1 -0
- package/dist/minimongo/contracts.type-test.d.ts +1 -0
- package/dist/minimongo/contracts.type-test.js +21 -0
- package/dist/minimongo/contracts.type-test.js.map +1 -0
- package/dist/minimongo/cursor.d.ts +67 -0
- package/dist/minimongo/cursor.js +157 -0
- package/dist/minimongo/cursor.js.map +1 -0
- package/dist/minimongo/document-store.d.ts +31 -0
- package/dist/minimongo/document-store.js +48 -0
- package/dist/minimongo/document-store.js.map +1 -0
- package/dist/minimongo/errors.d.ts +14 -0
- package/dist/minimongo/errors.js +16 -0
- package/dist/minimongo/errors.js.map +1 -0
- package/dist/minimongo/events.d.ts +20 -0
- package/dist/minimongo/events.js +80 -0
- package/dist/minimongo/events.js.map +1 -0
- package/dist/minimongo/identity.d.ts +8 -0
- package/dist/minimongo/identity.js +15 -0
- package/dist/minimongo/identity.js.map +1 -0
- package/dist/minimongo/immutable.d.ts +6 -0
- package/dist/minimongo/immutable.js +17 -0
- package/dist/minimongo/immutable.js.map +1 -0
- package/dist/minimongo/index.d.ts +7 -0
- package/dist/minimongo/index.js +6 -0
- package/dist/minimongo/index.js.map +1 -0
- package/dist/minimongo/local-collection.d.ts +48 -0
- package/dist/minimongo/local-collection.js +129 -0
- package/dist/minimongo/local-collection.js.map +1 -0
- package/dist/minimongo/matcher.d.ts +27 -0
- package/dist/minimongo/matcher.js +457 -0
- package/dist/minimongo/matcher.js.map +1 -0
- package/dist/minimongo/modifier.d.ts +12 -0
- package/dist/minimongo/modifier.js +323 -0
- package/dist/minimongo/modifier.js.map +1 -0
- package/dist/minimongo/path.d.ts +17 -0
- package/dist/minimongo/path.js +126 -0
- package/dist/minimongo/path.js.map +1 -0
- package/dist/minimongo/projection.d.ts +5 -0
- package/dist/minimongo/projection.js +100 -0
- package/dist/minimongo/projection.js.map +1 -0
- package/dist/minimongo/sorter.d.ts +16 -0
- package/dist/minimongo/sorter.js +140 -0
- package/dist/minimongo/sorter.js.map +1 -0
- package/dist/minimongo/transform.d.ts +4 -0
- package/dist/minimongo/transform.js +30 -0
- package/dist/minimongo/transform.js.map +1 -0
- package/dist/minimongo/types.d.ts +169 -0
- package/dist/minimongo/types.js +2 -0
- package/dist/minimongo/types.js.map +1 -0
- package/dist/minimongo/value-semantics.d.ts +8 -0
- package/dist/minimongo/value-semantics.js +156 -0
- package/dist/minimongo/value-semantics.js.map +1 -0
- package/dist/mongodb/change-streams.js +14 -8
- package/dist/mongodb/change-streams.js.map +1 -1
- package/dist/mongodb/decorators/collection.js +4 -1
- package/dist/mongodb/decorators/collection.js.map +1 -1
- package/dist/mongodb/decorators/metadata.js +1 -1
- package/dist/mongodb/decorators/metadata.js.map +1 -1
- package/dist/mongodb/find-one-or-create.d.ts +2 -2
- package/dist/mongodb/find-one-or-create.js +1 -1
- package/dist/mongodb/find-one-or-create.js.map +1 -1
- package/dist/mongodb/live/engine.js.map +1 -1
- package/dist/mongodb/live/source.js +11 -5
- package/dist/mongodb/live/source.js.map +1 -1
- package/dist/mongodb/live/types.d.ts +1 -1
- package/dist/react/components/client-provider.d.ts +3 -2
- package/dist/react/components/client-provider.js +1 -1
- package/dist/react/components/client-provider.js.map +1 -1
- package/dist/react/contracts.type-test.d.ts +1 -0
- package/dist/react/contracts.type-test.js +21 -0
- package/dist/react/contracts.type-test.js.map +1 -0
- package/dist/react/hooks/use-auth.d.ts +5 -1
- package/dist/react/hooks/use-auth.js +4 -2
- package/dist/react/hooks/use-auth.js.map +1 -1
- package/dist/react/hooks/use-caller.d.ts +10 -5
- package/dist/react/hooks/use-caller.js +12 -8
- package/dist/react/hooks/use-caller.js.map +1 -1
- package/dist/react/hooks/use-channel.d.ts +1 -1
- package/dist/react/hooks/use-channel.js +1 -1
- package/dist/react/hooks/use-channel.js.map +1 -1
- package/dist/react/hooks/use-circuit-breaker.d.ts +14 -6
- package/dist/react/hooks/use-circuit-breaker.js +5 -2
- package/dist/react/hooks/use-circuit-breaker.js.map +1 -1
- package/dist/react/hooks/use-client.d.ts +1 -1
- package/dist/react/hooks/use-client.js +2 -0
- package/dist/react/hooks/use-client.js.map +1 -1
- package/dist/react/hooks/use-connection-state.d.ts +4 -4
- package/dist/react/hooks/use-connection-state.js +2 -0
- package/dist/react/hooks/use-connection-state.js.map +1 -1
- package/dist/react/hooks/use-deps-change.d.ts +1 -1
- package/dist/react/hooks/use-deps-change.js.map +1 -1
- package/dist/react/hooks/use-local-event.d.ts +2 -2
- package/dist/react/hooks/use-local-event.js +2 -2
- package/dist/react/hooks/use-local-event.js.map +1 -1
- package/dist/react/hooks/use-method-refresh.d.ts +24 -15
- package/dist/react/hooks/use-method-refresh.js +18 -22
- package/dist/react/hooks/use-method-refresh.js.map +1 -1
- package/dist/react/hooks/use-method.d.ts +12 -11
- package/dist/react/hooks/use-method.js +9 -5
- package/dist/react/hooks/use-method.js.map +1 -1
- package/dist/react/hooks/use-mongodb-live-publication.js +2 -0
- package/dist/react/hooks/use-mongodb-live-publication.js.map +1 -1
- package/dist/react/hooks/use-object.d.ts +2 -2
- package/dist/react/hooks/use-object.js.map +1 -1
- package/dist/react/hooks/use-subscribe.d.ts +1 -2
- package/dist/react/hooks/use-subscribe.js +11 -9
- package/dist/react/hooks/use-subscribe.js.map +1 -1
- package/dist/react/hooks/use-throttled-events.d.ts +1 -1
- package/dist/react/hooks/use-throttled-events.js +2 -0
- package/dist/react/hooks/use-throttled-events.js.map +1 -1
- package/dist/react/hooks/use-token-refresh.js +1 -1
- package/dist/react/hooks/use-token-refresh.js.map +1 -1
- package/dist/server/client-node.d.ts +17 -10
- package/dist/server/client-node.js +16 -13
- package/dist/server/client-node.js.map +1 -1
- package/dist/server/decorators/register.js +10 -6
- package/dist/server/decorators/register.js.map +1 -1
- package/dist/server/method.d.ts +1 -1
- package/dist/server/method.js +9 -6
- package/dist/server/method.js.map +1 -1
- package/dist/server/methods.js +2 -1
- package/dist/server/methods.js.map +1 -1
- package/dist/server/server-channel.d.ts +4 -2
- package/dist/server/server-channel.js +33 -20
- package/dist/server/server-channel.js.map +1 -1
- package/dist/server/server.d.ts +25 -12
- package/dist/server/server.js +18 -17
- package/dist/server/server.js.map +1 -1
- package/dist/server/transports/node-hono-transport.d.ts +4 -4
- package/dist/server/transports/node-hono-transport.js +66 -14
- package/dist/server/transports/node-hono-transport.js.map +1 -1
- package/dist/server/transports/redis-transport.d.ts +5 -4
- package/dist/server/transports/redis-transport.js +9 -4
- package/dist/server/transports/redis-transport.js.map +1 -1
- package/dist/server/transports/websocket-transport.d.ts +1 -1
- package/dist/server/transports/websocket-transport.js +6 -1
- package/dist/server/transports/websocket-transport.js.map +1 -1
- package/dist/server/transports/ws-shared.d.ts +2 -1
- package/dist/server/transports/ws-shared.js +4 -1
- package/dist/server/transports/ws-shared.js.map +1 -1
- package/dist/utils/errors.d.ts +1 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/event-emitter.d.ts +1 -1
- package/dist/utils/event-emitter.js.map +1 -1
- package/dist/utils/events.d.ts +4 -4
- package/dist/utils/events.js.map +1 -1
- package/dist/utils/helpers.d.ts +4 -4
- package/dist/utils/helpers.js +4 -3
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/intercept.d.ts +1 -2
- package/dist/utils/intercept.js.map +1 -1
- package/dist/utils/iterator.d.ts +6 -8
- package/dist/utils/iterator.js +3 -3
- package/dist/utils/iterator.js.map +1 -1
- package/dist/utils/presentation.d.ts +1 -1
- package/dist/utils/presentation.js +2 -0
- package/dist/utils/presentation.js.map +1 -1
- package/dist/utils/promise.d.ts +5 -5
- package/dist/utils/promise.js +2 -2
- package/dist/utils/promise.js.map +1 -1
- 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
|