coll-fns 1.0.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.
- package/README.md +677 -0
- package/dist/coll-fns.cjs +2 -0
- package/dist/coll-fns.cjs.map +1 -0
- package/dist/coll-fns.mjs +2 -0
- package/dist/coll-fns.mjs.map +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
# coll-fns
|
|
2
|
+
|
|
3
|
+
A universal collection manipulation library that provides a unified API for working with different database backends through a protocol-based architecture.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`coll-fns` abstracts common database operations (CRUD, joins, field projections) behind a consistent interface, allowing you to write database-agnostic code that works across MongoDB, Meteor, and other data sources.
|
|
8
|
+
|
|
9
|
+
**Originally designed for Meteor**, `coll-fns` solves the challenge of writing isomorphic database code that works seamlessly on both client (synchronous) and server (asynchronous) with the exact same API.
|
|
10
|
+
|
|
11
|
+
## Key Features
|
|
12
|
+
|
|
13
|
+
- π **Powerful Join System**: Define relationships between collections with automatic field resolution and nested joins
|
|
14
|
+
- πͺ **Extensible Hooks**: React to CRUD operations with before/after hooks for validation, transformation, and side effects
|
|
15
|
+
- π **Isomorphic by Design**: Write once, run anywhere - same API for client-side (sync) and server-side (async) code
|
|
16
|
+
- π **Protocol-based Architecture**: Switch between different database backends seamlessly
|
|
17
|
+
- π **Advanced Field Projections**: Support for nested fields, dot notation, and MongoDB-style projections
|
|
18
|
+
- π **Promise/async Support**: Works with both synchronous and asynchronous protocols
|
|
19
|
+
- π **TypeScript-ready**: Includes JSDoc types for better IDE support
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install coll-fns
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import {
|
|
31
|
+
setProtocol,
|
|
32
|
+
fetchList,
|
|
33
|
+
insert,
|
|
34
|
+
update,
|
|
35
|
+
remove,
|
|
36
|
+
count,
|
|
37
|
+
protocols,
|
|
38
|
+
} from "coll-fns";
|
|
39
|
+
|
|
40
|
+
// Set up your protocol
|
|
41
|
+
setProtocol(protocols.node(mongoClient));
|
|
42
|
+
|
|
43
|
+
// Use the API
|
|
44
|
+
const users = await fetchList(UsersCollection, { age: { $gte: 18 } });
|
|
45
|
+
await insert(UsersCollection, { name: "Alice", age: 25 });
|
|
46
|
+
await update(UsersCollection, { name: "Alice" }, { $set: { age: 26 } });
|
|
47
|
+
const total = await count(UsersCollection, {});
|
|
48
|
+
await remove(UsersCollection, { name: "Alice" });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Joins: The Power Feature
|
|
52
|
+
|
|
53
|
+
One of the most powerful features of `coll-fns` is its ability to define declarative joins between collections, eliminating the need for manual data fetching and aggregation.
|
|
54
|
+
|
|
55
|
+
### Basic Join Example
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const posts = await fetchList(
|
|
59
|
+
PostsCollection,
|
|
60
|
+
{},
|
|
61
|
+
{
|
|
62
|
+
joins: {
|
|
63
|
+
author: {
|
|
64
|
+
coll: UsersCollection,
|
|
65
|
+
on: ["authorId", "_id"], // [local field, foreign field]
|
|
66
|
+
fields: { name: 1, avatar: 1, email: 1 },
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
fields: {
|
|
70
|
+
title: 1,
|
|
71
|
+
content: 1,
|
|
72
|
+
"+": { author: 1 }, // Use '+' prefix to include join fields
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Result: Each post includes an 'author' object with name, avatar, and email
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### One-to-Many Joins
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const posts = await fetchList(
|
|
84
|
+
PostsCollection,
|
|
85
|
+
{},
|
|
86
|
+
{
|
|
87
|
+
joins: {
|
|
88
|
+
comments: {
|
|
89
|
+
coll: CommentsCollection,
|
|
90
|
+
on: ["_id", "postId"],
|
|
91
|
+
many: true, // Returns an array of related documents
|
|
92
|
+
fields: { text: 1, createdAt: 1 },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
fields: {
|
|
96
|
+
title: 1,
|
|
97
|
+
"+": { comments: 1 },
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Result: Each post includes a 'comments' array
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Nested Joins
|
|
106
|
+
|
|
107
|
+
Joins can be nested to fetch deeply related data:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
const posts = await fetchList(
|
|
111
|
+
PostsCollection,
|
|
112
|
+
{},
|
|
113
|
+
{
|
|
114
|
+
joins: {
|
|
115
|
+
author: {
|
|
116
|
+
coll: UsersCollection,
|
|
117
|
+
on: ["authorId", "_id"],
|
|
118
|
+
fields: { name: 1, avatar: 1 },
|
|
119
|
+
},
|
|
120
|
+
comments: {
|
|
121
|
+
coll: CommentsCollection,
|
|
122
|
+
on: ["_id", "postId"],
|
|
123
|
+
many: true,
|
|
124
|
+
fields: { text: 1, "+": { user: 1 } },
|
|
125
|
+
joins: {
|
|
126
|
+
user: {
|
|
127
|
+
coll: UsersCollection,
|
|
128
|
+
on: ["userId", "_id"],
|
|
129
|
+
fields: { name: 1, avatar: 1 },
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
fields: {
|
|
135
|
+
title: 1,
|
|
136
|
+
content: 1,
|
|
137
|
+
"+": { author: 1, comments: 1 },
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Result: Posts with author details and comments, each comment with user details
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Recursive Join Depth Control
|
|
146
|
+
|
|
147
|
+
Control the depth of recursive joins to prevent infinite loops:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const users = await fetchList(
|
|
151
|
+
UsersCollection,
|
|
152
|
+
{},
|
|
153
|
+
{
|
|
154
|
+
joins: {
|
|
155
|
+
friends: {
|
|
156
|
+
coll: UsersCollection,
|
|
157
|
+
on: ["friendIds", "_id"],
|
|
158
|
+
many: true,
|
|
159
|
+
fields: { name: 1, "+": { friends: 1 } },
|
|
160
|
+
joins: {
|
|
161
|
+
friends: {
|
|
162
|
+
coll: UsersCollection,
|
|
163
|
+
on: ["friendIds", "_id"],
|
|
164
|
+
many: true,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
fields: {
|
|
170
|
+
name: 1,
|
|
171
|
+
"+": { friends: 2 }, // Limit to 2 levels deep
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Hooks: Extensibility Made Easy
|
|
178
|
+
|
|
179
|
+
Hooks allow you to react to CRUD operations, enabling validation, transformation, logging, and side effects without modifying your core business logic.
|
|
180
|
+
|
|
181
|
+
### Setting Up Hooks
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
import { hook } from "coll-fns";
|
|
185
|
+
|
|
186
|
+
// Before insert hook - validation and transformation
|
|
187
|
+
hook(UsersCollection, "insert", "before", (doc) => {
|
|
188
|
+
if (!doc.email) {
|
|
189
|
+
throw new Error("Email is required");
|
|
190
|
+
}
|
|
191
|
+
// Transform data
|
|
192
|
+
doc.email = doc.email.toLowerCase();
|
|
193
|
+
doc.createdAt = new Date();
|
|
194
|
+
return doc;
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// After insert hook - side effects
|
|
198
|
+
hook(UsersCollection, "insert", "after", (doc) => {
|
|
199
|
+
console.log(`New user created: ${doc.name}`);
|
|
200
|
+
// Send welcome email, update analytics, etc.
|
|
201
|
+
sendWelcomeEmail(doc.email);
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Available Hook Types
|
|
206
|
+
|
|
207
|
+
Hooks can be attached to any CRUD operation:
|
|
208
|
+
|
|
209
|
+
- **`insert`**: Before/after document insertion
|
|
210
|
+
- **`update`**: Before/after document updates
|
|
211
|
+
- **`remove`**: Before/after document removal
|
|
212
|
+
- **`fetch`**: Before/after fetching documents (useful for filtering)
|
|
213
|
+
|
|
214
|
+
### Hook Examples
|
|
215
|
+
|
|
216
|
+
#### Validation and Authorization
|
|
217
|
+
|
|
218
|
+
```js
|
|
219
|
+
// Prevent unauthorized updates
|
|
220
|
+
hook(PostsCollection, "update", "before", (selector, modifier, options) => {
|
|
221
|
+
const currentUserId = getCurrentUserId();
|
|
222
|
+
const post = fetchList(PostsCollection, selector)[0];
|
|
223
|
+
|
|
224
|
+
if (post.authorId !== currentUserId) {
|
|
225
|
+
throw new Error("Unauthorized");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return [selector, modifier, options];
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Automatic Timestamps
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
// Add timestamps automatically
|
|
236
|
+
hook(PostsCollection, "insert", "before", (doc) => {
|
|
237
|
+
doc.createdAt = new Date();
|
|
238
|
+
doc.updatedAt = new Date();
|
|
239
|
+
return doc;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
hook(PostsCollection, "update", "before", (selector, modifier, options) => {
|
|
243
|
+
if (!modifier.$set) modifier.$set = {};
|
|
244
|
+
modifier.$set.updatedAt = new Date();
|
|
245
|
+
return [selector, modifier, options];
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### Audit Logging
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
// Log all changes
|
|
253
|
+
hook(PostsCollection, "update", "after", (result, selector, modifier) => {
|
|
254
|
+
logToAuditTrail({
|
|
255
|
+
collection: "posts",
|
|
256
|
+
action: "update",
|
|
257
|
+
selector,
|
|
258
|
+
modifier,
|
|
259
|
+
timestamp: new Date(),
|
|
260
|
+
userId: getCurrentUserId(),
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Data Denormalization
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
// Update denormalized data
|
|
269
|
+
hook(UsersCollection, "update", "after", (result, selector, modifier) => {
|
|
270
|
+
const userId = selector._id;
|
|
271
|
+
const userName = modifier.$set?.name;
|
|
272
|
+
|
|
273
|
+
if (userName) {
|
|
274
|
+
// Update user name in all their posts
|
|
275
|
+
update(
|
|
276
|
+
PostsCollection,
|
|
277
|
+
{ authorId: userId },
|
|
278
|
+
{ $set: { authorName: userName } },
|
|
279
|
+
{ multi: true }
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Meteor Integration: Isomorphic by Design
|
|
286
|
+
|
|
287
|
+
`coll-fns` was specifically designed to solve Meteor's challenge of writing code that works both on the client (synchronous) and server (asynchronous) with the same API.
|
|
288
|
+
|
|
289
|
+
### Server-Side (Async)
|
|
290
|
+
|
|
291
|
+
```js
|
|
292
|
+
// server/main.js
|
|
293
|
+
import { setProtocol, protocols } from "coll-fns";
|
|
294
|
+
|
|
295
|
+
setProtocol(protocols.meteorAsync);
|
|
296
|
+
|
|
297
|
+
// Methods automatically work with async
|
|
298
|
+
Meteor.methods({
|
|
299
|
+
async createPost(title, content) {
|
|
300
|
+
const user = await fetchOne(UsersCollection, { _id: this.userId });
|
|
301
|
+
return await insert(PostsCollection, {
|
|
302
|
+
title,
|
|
303
|
+
content,
|
|
304
|
+
authorId: this.userId,
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Client-Side (Sync)
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
// client/main.js
|
|
314
|
+
import { setProtocol, protocols } from "coll-fns";
|
|
315
|
+
|
|
316
|
+
setProtocol(protocols.meteorSync);
|
|
317
|
+
|
|
318
|
+
// Same API, synchronous execution
|
|
319
|
+
const posts = fetchList(
|
|
320
|
+
PostsCollection,
|
|
321
|
+
{},
|
|
322
|
+
{
|
|
323
|
+
joins: {
|
|
324
|
+
author: {
|
|
325
|
+
coll: UsersCollection,
|
|
326
|
+
on: ["authorId", "_id"],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
fields: { title: 1, "+": { author: 1 } },
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Shared Code
|
|
335
|
+
|
|
336
|
+
```js
|
|
337
|
+
// imports/api/posts.js
|
|
338
|
+
import { fetchList } from "coll-fns";
|
|
339
|
+
|
|
340
|
+
// This function works on both client and server!
|
|
341
|
+
export function getPostsWithAuthors() {
|
|
342
|
+
return fetchList(
|
|
343
|
+
PostsCollection,
|
|
344
|
+
{},
|
|
345
|
+
{
|
|
346
|
+
joins: {
|
|
347
|
+
author: {
|
|
348
|
+
coll: UsersCollection,
|
|
349
|
+
on: ["authorId", "_id"],
|
|
350
|
+
fields: { name: 1, avatar: 1 },
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
fields: { title: 1, content: 1, "+": { author: 1 } },
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## API Reference
|
|
360
|
+
|
|
361
|
+
### Core Functions
|
|
362
|
+
|
|
363
|
+
#### `fetchList(collection, selector, options)`
|
|
364
|
+
|
|
365
|
+
Fetch an array of documents from a collection.
|
|
366
|
+
|
|
367
|
+
```js
|
|
368
|
+
const users = await fetchList(
|
|
369
|
+
UsersCollection,
|
|
370
|
+
{ status: "active" },
|
|
371
|
+
{
|
|
372
|
+
fields: { name: 1, email: 1 },
|
|
373
|
+
sort: { createdAt: -1 },
|
|
374
|
+
limit: 10,
|
|
375
|
+
skip: 0,
|
|
376
|
+
}
|
|
377
|
+
);
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Options:**
|
|
381
|
+
|
|
382
|
+
- `fields`: Field projection object
|
|
383
|
+
- `sort`: Sort specification
|
|
384
|
+
- `limit`: Maximum number of documents
|
|
385
|
+
- `skip`: Number of documents to skip
|
|
386
|
+
- `joins`: Join definitions for related collections
|
|
387
|
+
|
|
388
|
+
#### `fetchOne(collection, selector, options)`
|
|
389
|
+
|
|
390
|
+
Fetch a single document from a collection.
|
|
391
|
+
|
|
392
|
+
```js
|
|
393
|
+
const user = await fetchOne(
|
|
394
|
+
UsersCollection,
|
|
395
|
+
{ _id: userId },
|
|
396
|
+
{
|
|
397
|
+
fields: { name: 1, email: 1 },
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### `fetchIds(collection, selector, options)`
|
|
403
|
+
|
|
404
|
+
Fetch only the `_id` field of matching documents.
|
|
405
|
+
|
|
406
|
+
```js
|
|
407
|
+
const userIds = await fetchIds(UsersCollection, { status: "active" });
|
|
408
|
+
// Returns: ['id1', 'id2', 'id3']
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
#### `exists(collection, selector)`
|
|
412
|
+
|
|
413
|
+
Check if any documents match the selector.
|
|
414
|
+
|
|
415
|
+
```js
|
|
416
|
+
const hasActiveUsers = await exists(UsersCollection, { status: "active" });
|
|
417
|
+
// Returns: true or false
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
#### `insert(collection, doc)`
|
|
421
|
+
|
|
422
|
+
Insert a document into a collection.
|
|
423
|
+
|
|
424
|
+
```js
|
|
425
|
+
const newUser = await insert(UsersCollection, {
|
|
426
|
+
name: "Bob",
|
|
427
|
+
email: "bob@example.com",
|
|
428
|
+
});
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### `update(collection, selector, modifier, options)`
|
|
432
|
+
|
|
433
|
+
Update documents matching the selector.
|
|
434
|
+
|
|
435
|
+
```js
|
|
436
|
+
await update(
|
|
437
|
+
UsersCollection,
|
|
438
|
+
{ status: "pending" },
|
|
439
|
+
{ $set: { status: "active" } },
|
|
440
|
+
{ multi: true }
|
|
441
|
+
);
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
#### `remove(collection, selector)`
|
|
445
|
+
|
|
446
|
+
Remove documents matching the selector.
|
|
447
|
+
|
|
448
|
+
```js
|
|
449
|
+
await remove(UsersCollection, { inactive: true });
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### `count(collection, selector)`
|
|
453
|
+
|
|
454
|
+
Count documents matching the selector.
|
|
455
|
+
|
|
456
|
+
```js
|
|
457
|
+
const activeUsers = await count(UsersCollection, { status: "active" });
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Hook Functions
|
|
461
|
+
|
|
462
|
+
#### `hook(collection, operation, timing, fn)`
|
|
463
|
+
|
|
464
|
+
Add a hook to a collection operation.
|
|
465
|
+
|
|
466
|
+
```js
|
|
467
|
+
hook(UsersCollection, "insert", "before", (doc) => {
|
|
468
|
+
// Modify or validate doc
|
|
469
|
+
return doc;
|
|
470
|
+
});
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Parameters:**
|
|
474
|
+
|
|
475
|
+
- `collection`: The collection to hook into
|
|
476
|
+
- `operation`: `'insert'`, `'update'`, `'remove'`, or `'fetch'`
|
|
477
|
+
- `timing`: `'before'` or `'after'`
|
|
478
|
+
- `fn`: Hook function (return value depends on operation and timing)
|
|
479
|
+
|
|
480
|
+
### Protocol Management
|
|
481
|
+
|
|
482
|
+
#### `setProtocol(protocol)`
|
|
483
|
+
|
|
484
|
+
Set the active database protocol.
|
|
485
|
+
|
|
486
|
+
```js
|
|
487
|
+
import { setProtocol, protocols } from "coll-fns";
|
|
488
|
+
|
|
489
|
+
setProtocol(protocols.meteorAsync);
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
#### `getProtocol()`
|
|
493
|
+
|
|
494
|
+
Get the current active protocol.
|
|
495
|
+
|
|
496
|
+
```js
|
|
497
|
+
const currentProtocol = getProtocol();
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
#### `updateProtocol(updates)`
|
|
501
|
+
|
|
502
|
+
Update specific methods of the current protocol.
|
|
503
|
+
|
|
504
|
+
```js
|
|
505
|
+
import { updateProtocol } from "coll-fns";
|
|
506
|
+
|
|
507
|
+
updateProtocol({
|
|
508
|
+
fetch: customFetchImplementation,
|
|
509
|
+
});
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Field Projections
|
|
513
|
+
|
|
514
|
+
#### Nested Fields
|
|
515
|
+
|
|
516
|
+
```js
|
|
517
|
+
// Nested object notation
|
|
518
|
+
const users = await fetchList(
|
|
519
|
+
UsersCollection,
|
|
520
|
+
{},
|
|
521
|
+
{
|
|
522
|
+
fields: {
|
|
523
|
+
name: 1,
|
|
524
|
+
address: {
|
|
525
|
+
street: 1,
|
|
526
|
+
city: 1,
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
// Dot notation (MongoDB-style)
|
|
533
|
+
const users = await fetchList(
|
|
534
|
+
UsersCollection,
|
|
535
|
+
{},
|
|
536
|
+
{
|
|
537
|
+
fields: {
|
|
538
|
+
name: 1,
|
|
539
|
+
"address.street": 1,
|
|
540
|
+
"address.city": 1,
|
|
541
|
+
},
|
|
542
|
+
}
|
|
543
|
+
);
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
#### Flattening Fields
|
|
547
|
+
|
|
548
|
+
```js
|
|
549
|
+
import { flattenFields } from "coll-fns";
|
|
550
|
+
|
|
551
|
+
const flattened = flattenFields({
|
|
552
|
+
name: 1,
|
|
553
|
+
address: {
|
|
554
|
+
street: 1,
|
|
555
|
+
city: 1,
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
// Result: { name: 1, 'address.street': 1, 'address.city': 1 }
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
## Available Protocols
|
|
562
|
+
|
|
563
|
+
All protocols are available through the `protocols` namespace:
|
|
564
|
+
|
|
565
|
+
```js
|
|
566
|
+
import { protocols } from "coll-fns";
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### Node.js (MongoDB)
|
|
570
|
+
|
|
571
|
+
```js
|
|
572
|
+
import { setProtocol, protocols } from "coll-fns";
|
|
573
|
+
import { MongoClient } from "mongodb";
|
|
574
|
+
|
|
575
|
+
const client = new MongoClient(url);
|
|
576
|
+
await client.connect();
|
|
577
|
+
setProtocol(protocols.node(client));
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Meteor (Synchronous)
|
|
581
|
+
|
|
582
|
+
```js
|
|
583
|
+
import { setProtocol, protocols } from "coll-fns";
|
|
584
|
+
|
|
585
|
+
setProtocol(protocols.meteorSync);
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Meteor (Asynchronous)
|
|
589
|
+
|
|
590
|
+
```js
|
|
591
|
+
import { setProtocol, protocols } from "coll-fns";
|
|
592
|
+
|
|
593
|
+
setProtocol(protocols.meteorAsync);
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
## Advanced Usage
|
|
597
|
+
|
|
598
|
+
### Custom Protocols
|
|
599
|
+
|
|
600
|
+
Create your own protocol by implementing the required methods:
|
|
601
|
+
|
|
602
|
+
```js
|
|
603
|
+
const customProtocol = {
|
|
604
|
+
fetch: (coll, selector, options) => {
|
|
605
|
+
/* ... */
|
|
606
|
+
},
|
|
607
|
+
insert: (coll, doc) => {
|
|
608
|
+
/* ... */
|
|
609
|
+
},
|
|
610
|
+
update: (coll, selector, modifier, options) => {
|
|
611
|
+
/* ... */
|
|
612
|
+
},
|
|
613
|
+
remove: (coll, selector) => {
|
|
614
|
+
/* ... */
|
|
615
|
+
},
|
|
616
|
+
count: (coll, selector) => {
|
|
617
|
+
/* ... */
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
setProtocol(customProtocol);
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Join Management
|
|
625
|
+
|
|
626
|
+
#### Setting Custom Join Prefix
|
|
627
|
+
|
|
628
|
+
By default, join fields are prefixed with `+`. You can customize this:
|
|
629
|
+
|
|
630
|
+
```js
|
|
631
|
+
import { setJoinPrefix } from "coll-fns";
|
|
632
|
+
|
|
633
|
+
setJoinPrefix("joins"); // Now use { joins: { author: 1 } } instead of { '+': { author: 1 } }
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
If join prefix is set to a falsy value, join fields can be declared at the document root like any native field.
|
|
637
|
+
|
|
638
|
+
```js
|
|
639
|
+
import { setJoinPrefix } from "coll-fns";
|
|
640
|
+
|
|
641
|
+
setJoinPrefix(null); // Now use { author: 1 } instead of { '+': { author: 1 } }
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
#### Getting Join Configuration
|
|
645
|
+
|
|
646
|
+
```js
|
|
647
|
+
import { getJoins, getJoinPrefix } from "coll-fns";
|
|
648
|
+
|
|
649
|
+
const joins = getJoins(fields); // Extract join definitions from fields
|
|
650
|
+
const prefix = getJoinPrefix(); // Get current join prefix (default: '+')
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
## Project Structure
|
|
654
|
+
|
|
655
|
+
```
|
|
656
|
+
src/
|
|
657
|
+
βββ count.js - Count operation
|
|
658
|
+
βββ fetch.js - Fetch operations (fetchList, fetchOne, fetchIds, exists)
|
|
659
|
+
βββ fields.js - Field projection utilities
|
|
660
|
+
βββ hook.js - Hook system for extending operations
|
|
661
|
+
βββ index.js - Main exports
|
|
662
|
+
βββ insert.js - Insert operation
|
|
663
|
+
βββ join.js - Join functionality
|
|
664
|
+
βββ protocol.js - Protocol management
|
|
665
|
+
βββ remove.js - Remove operation
|
|
666
|
+
βββ update.js - Update operation
|
|
667
|
+
βββ util.js - Utility functions
|
|
668
|
+
βββ protocols/ - Database protocol implementations
|
|
669
|
+
βββ index.js
|
|
670
|
+
βββ meteorAsync.js
|
|
671
|
+
βββ meteorSync.js
|
|
672
|
+
βββ node.js
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
## License
|
|
676
|
+
|
|
677
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function n(){return n=Object.assign?Object.assign.bind():function(n){for(var r=1;r<arguments.length;r++){var t=arguments[r];for(var e in t)({}).hasOwnProperty.call(t,e)&&(n[e]=t[e])}return n},n.apply(null,arguments)}function r(n,r){if(null==n)return{};var t={};for(var e in n)if({}.hasOwnProperty.call(n,e)){if(-1!==r.indexOf(e))continue;t[e]=n[e]}return t}function t(n){var r=function(n){if("object"!=typeof n||!n)return n;var r=n[Symbol.toPrimitive];if(void 0!==r){var t=r.call(n,"string");if("object"!=typeof t)return t;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(n)}(n);return"symbol"==typeof r?r:r+""}var e={count:function(){throw new Error("'count' method must be defined with 'setProtocol'.")},cursor:function(){throw new Error("'cursor' method must be defined with 'setProtocol'.")},findList:function(){throw new Error("'findList' method must be defined with 'setProtocol'.")},getTransform:function(){},insert:function(){throw new Error("'insert' method must be defined with 'setProtocol'.")},remove:function(){throw new Error("'remove' method must be defined with 'setProtocol'.")},update:function(){throw new Error("'update' method must be defined with 'setProtocol'.")}},o=e;function i(n){return n?"function"==typeof n?n(o):"object"==typeof n?n:o:o}var u=function(n){return{}.toString.call(n).split(" ")[1].slice(0,-1).toLowerCase()},f=function(n){return Array.isArray(n)},c=function(n){return"function"==typeof n},l=function(n){return n&&!f(n)&&"object"===u(n)};function d(n){return c(null==n?void 0:n.then)}function s(n,r){return p(function(r){var t=r[0];return[n[t]||t,r[1]]},r)}function a(n,r){var t=n.split("."),e=t[0],o=function(n,r){(null==r||r>n.length)&&(r=n.length);for(var t=0,e=Array(r);t<r;t++)e[t]=n[t];return e}(t).slice(1),i=r[e];if(o.length<1)return i;var u=o.join(".");return l(i)?a(u,i):f(i)?i.flatMap(function(n){var r=a(u,n);return f(r)?r:[r]}):i}function v(n,r){if(f(n)){var t=n;return t.some(d)?Promise.all(t).then(function(n){return r(n,!0)}):r(t,!1)}return d(n)?n.then(function(n){return r(n,!0)}):r(n,!1)}function p(n,r){if(void 0===r)return function(r){return p(n,r)};if(f(r))return r.map(n);if(!l(r))throw new TypeError("'map' only works on array or plain object");return Object.fromEntries(Object.entries(r).map(n))}function m(n,r){if(void 0===r)return function(r){return m(n,r)};if(f(r))return r.filter(n);if(!l(r))throw new TypeError("'filter' only works on array or plain object");return Object.fromEntries(Object.entries(r).filter(n))}var h=["array","function","object"],y=h.join("', '"),b=new Map,w=null;function j(n){return b.get(n)||{}}function g(){return w}var _=["+"];function O(n,r){return void 0===r&&(r=!1),l(n)?r?x(n):n:n?void 0:{}}function x(r,t){var e;if(!r)return r;var o=Object.keys(r);return o.some(function(n){return n.startsWith("$")})?t?((e={})[t]=r,e):r:o.reduce(function(e,o){var i=o.indexOf(".");if(i>=0){var u=o.slice(0,i),f=r[u];if(f&&!l(f))return e}var c,d=r[o],s=t?[t,o].join("."):o;return l(d)?n({},e,x(d,s)):n({},e,((c={})[s]=!!d,c))},void 0)}function k(e,o){var i;if(void 0===o&&(o={}),!l(e))return{_:O(e)};var u=function(e,o){void 0===o&&(o={});var i=Object.keys(o),u=g();if(u){var f=e[u],c=r(e,[u].map(t));return n(f?{"+":m(function(n){return i.includes(n[0])},f)}:{"+":void 0},c)}return Object.entries(e).reduce(function(r,t){var e,o,u=t[0],f=t[1];return i.includes(u)?n({},r,{"+":n({},r["+"]||{},(o={},o[u]=f,o))}):n({},r,((e={})[u]=f,e))},{})}(e,o),f=u["+"],c=r(u,_);if(!f)return{_:O(c,!0),"+":void 0};if(!c||null==(i=Object.keys(c))||!i.length)return{_:O(c,!0),"+":f};var d=Object.keys(f).reduce(function(r,t){var e,i=o[t],u=i.on,f=i.fields,c=Array.isArray(u)?((e={})[u[0]]=1,e):void 0;return c||f?n({},r,c,f):r},c);return{_:O(d,!0),"+":f}}function E(n,r){if(!C(n)&&!C(r))return n||r?P(A(n),A(r)):{}}function P(r,t){void 0===r&&(r={}),void 0===t&&(t={});for(var e=n({},r),o=0,i=Object.entries(t||{});o<i.length;o++){var u=i[o],f=u[0],c=u[1],d=e[f];e[f]=l(d)&&l(c)?P(d,c):c}return e}function C(n){return void 0===n||!!n&&!l(n)}function A(n){return l(n)?m(function(n){return n[1]},n):{}}function T(r,t){var e,o,i,u;if(!l(t))return t;var f=g(),c=f?null==(e=t[f])?void 0:e[r]:t[r];if("number"!=typeof c)return t;if(Infinity===c)return t;var d=c-1;return n({},t,f?((u={})[f]=n({},t[f],((i={})[r]=d,i)),u):((o={})[r]=d,o))}var F=["fields","transform"],L=["_key","Coll","on","single","postFetch","limit"],M=["_key","Coll","on","single","postFetch","limit"];function S(t,e,o){void 0===e&&(e={}),void 0===o&&(o={});var d=i(),s=d.count,p=d.findList,m=d.getTransform,h=j(t),y=m(t),b=o.fields,w=o.transform,g=void 0===w?y:w,_=r(o,F),x=function(n){return c(g)?g(n):n},E=k(b,h),P=E._,C=E["+"],A=void 0===C?{}:C,M=Object.keys(A);return!h||null==M||!M.length||b&&!l(b)?v(p(t,e,n({},_,{fields:P,transform:null})),function(n){return n.map(x)}):v(p(t,e,n({},_,{fields:P,transform:null})),function(e){var i=M.reduce(function(r,t){var e,o=h[t];if(!o)return r;var i=u(o.on),f=n({},o,{_key:t});return n({},r,((e={})[i]=[].concat(r[i]||[],[f]),e))},{}),d=i.array,p=i.object,m=void 0===p?[]:p,y=i.function,w=void 0===y?[]:y;return v((void 0===d?[]:d).reduce(function(e,i){var d=i._key,p=i.Coll,m=i.on,h=i.single,y=i.postFetch,w=r(i,L);return v(e,function(r){var e,i,_=m[0],x=m[1],E=m[2],P=void 0===E?{}:E,C=f(_),F=C?r.flatMap(function(n){return n[_[0]]}):r.map(function(n){return n[_]}),L=f(x),M=n({},P,L?((e={})[x[0]]={$elemMatch:{$in:F}},e):((i={})[x]={$in:F},i)),I=p===t&&A[d]>1;return v(I&&s(t,M),function(t){var e,i=I&&!t,s=I?T(d,b):A[d],m=k(s,j(p)||{})._,E=!m||Object.keys(m).length<=0,P=l(s)&&!E&&"_id"!==x?n({},s,((e={})[x]=1,e)):s,F=n({},o,w,{fields:O(P),limit:void 0,transform:I?g:void 0});return v(i?[]:S(p,M,F),function(t){var e=L?{}:t.reduce(function(r,t){var e,o=a(x,t);return f(o)?o.reduce(function(r,e){var o;return n({},r,((o={})[e]=[].concat(r[e]||[],[t]),o))},r):n({},r,((e={})[o]=[].concat(r[o]||[],[t]),e))},{});return r.map(function(r){var o,i,f,l,s=[];L?s=t.filter(function(n){var t,e=n[x[0]]||[];return C?(t=e,(r[_[0]]||[]).some(function(n){return t.indexOf(n)>=0})):e.includes(r[_])}):C?(i="_id",f=(r[_[0]]||[]).flatMap(function(n){return e[n]||[]}),l=c(i)?i:"string"===u(i)?function(n){return n[i]}:function(n){return n},s=f.reduce(function(n,r){var t=l(r);return n.find(function(n){return l(n)===t})?n:[].concat(n,[r])},[])):s=e[r[_]]||[];var a=h?s[0]:s,v=c(y)?y(a,r):a;return n({},r,((o={})[d]=v,o))})})})})},e),function(n){return v(m.map(function(n){return J({Coll:t,join:n,fields:A[n._key],subSelector:n.on,options:_,parentFields:b})}),function(r){return v(n.map(function(n){var e=r.reduce(function(n,r){return r(n)},n);return v(w.reduce(function(r,e){var o=e.on;return v([r,J({Coll:t,join:e,fields:A[e._key],subSelector:c(o)?o(n):o,options:_,parentFields:b})],function(n){return(0,n[1])(n[0])})},e),function(n){return x(n)})}),function(n){return n})})})})}function I(r,t,e){return void 0===e&&(e={}),v(S(r,t,n({},e,{limit:1})),function(n){return n[0]})}function J(t){var e=t.Coll,o=t.join,u=o._key,f=o.Coll,l=o.single,d=o.postFetch,s=o.limit,a=r(o,M),p=t.fields,m=t.subSelector,h=t.options,y=t.parentFields,b=i(),w=f===e;return v(w&&(0,b.count)(e,m),function(r){var t=w&&(!p||!r),e=w?T(u,y):p,o=n({},h,a,{fields:O(e),limit:l?1:s||void 0});return v(t?[]:S(f,m,o),function(r){return function(t){var e,o=l?r[0]:r,i=c(d)?d(o,t):o;return n({},t,((e={})[u]=i,e))}})})}var $=["beforeInsert","beforeUpdate","beforeRemove","onInserted","onUpdated","onRemoved"],R=new Map;function U(n,r){var t=R.get(n)||{};return r?t[r]:t}function D(n,r){var t=U(n,r);if(t)return function(n){if(void 0===n&&(n=[]),n.length){var r=n.reduce(function(n,r){return{fields:E(n.fields,r.fields),before:n.before||r.before}},{fields:null,before:void 0});return{fields:r.fields,fn:function(){var r=[].slice.call(arguments);return v(n.map(function(n){return q.apply(void 0,[n].concat(r))}).filter(c).map(function(n){return n.apply(void 0,r)}),function(n){return n})},before:r.before}}}(t)}function q(n){var r=n.fn,t=n.unless,e=n.when,o=[].slice.call(arguments,1);if(!(null==t?void 0:t.apply(void 0,o))&&(!e||e.apply(void 0,o)))return r}var z=["multi"],W=["multi"],B=["multi"],G={__proto__:null,meteorAsync:{count:function(n,r,t){var e=n.rawCollection();return e.countDocuments?e.countDocuments(r):n.find(r,t).countAsync()},cursor:function(n,r,t){return n.find(r,t)},findList:function(n,r,t){return n.find(r,t).fetchAsync()},getTransform:function(n){return n._transform},insert:function(n,r){return n.insertAsync(r)},remove:function(n,r){return n.removeAsync(r)},update:function(r,t,e,o){return r.updateAsync(t,e,n({multi:!0},o))}},meteorSync:{count:function(n,r,t){return n.find(r,t).count()},cursor:function(n,r,t){return n.find(r,t)},findList:function(n,r,t){return n.find(r,t).fetch()},getTransform:function(n){return n._transform},insert:function(n,r){return n.insert(r)},remove:function(n,r){return n.remove(r)},update:function(r,t,e,o){return r.update(t,e,n({multi:!0},o))}},node:{count:function(n,r,t){return void 0===r&&(r={}),void 0===t&&(t={}),n.countDocuments(r||{},t)},cursor:function(n,r,t){void 0===r&&(r={}),void 0===t&&(t={});var e=s({fields:"projection"},t||{});return n.find(r||{},e)},findList:function(n,r,t){void 0===r&&(r={}),void 0===t&&(t={});var e=s({fields:"projection"},t||{});return n.find(r||{},e).toArray()},getTransform:function(n){return"function"==typeof(null==n?void 0:n.transform)?n.transform:void 0},insert:function(n,r,t){return n.insertOne(r,t).then(function(n){return null==n?void 0:n.insertedId})},remove:function(n,t,e){void 0===t&&(t={}),void 0===e&&(e={});var o=e||{},i=o.multi,u=void 0===i||i,f=r(o,W);return(u?n.deleteMany(t||{},f):n.deleteOne(t||{},f)).then(function(n){var r;return null!=(r=null==n?void 0:n.deletedCount)?r:0})},update:function(n,t,e,o){void 0===t&&(t={}),void 0===e&&(e={}),void 0===o&&(o={});var i=o||{},u=i.multi,f=void 0===u||u,c=r(i,B);return(f?n.updateMany(t||{},e||{},c):n.updateOne(t||{},e||{},c)).then(function(n){var r,t;return null!=(r=null!=(t=null==n?void 0:n.modifiedCount)?t:null==n?void 0:n.upsertedCount)?r:0})}}};exports.count=function(n,r){return v((0,i().count)(n,r),function(n){return n})},exports.exists=function(n,r){return v(I(n,r,{fields:{_id:1}}),function(n){return!!n})},exports.fetchIds=function(r,t,e){return v(S(r,t,n({},e,{fields:{_id:1}})),function(n){return n.map(function(n){return n._id})})},exports.fetchList=S,exports.fetchOne=I,exports.flattenFields=x,exports.getJoinPrefix=g,exports.getJoins=j,exports.getProtocol=i,exports.hook=function(r,t){Object.entries(t).forEach(function(t){var e=t[0],o=t[1];if(!f(o))throw new TypeError("'"+e+"' hooks must be an array");o.forEach(function(t){return function(r,t,e){var o;if(!$.includes(t))throw new TypeError("'"+t+"' is not a valid hook type");if(!c(null==e?void 0:e.fn))throw new TypeError("'hook' must be a function or contain a 'fn' key");var i=U(r),u=[].concat(i[t]||[],[e]);R.set(r,n({},i,((o={})[t]=u,o)))}(r,e,t)})})},exports.insert=function(n,r){var t=i(),e=D(n,"beforeInsert");return v(c(null==e?void 0:e.fn)&&e.fn(r),function(){var e=D(n,"onInserted");return v(t.insert(n,r),function(r){if(!e)return r;var t=e.fields,o=e.fn,i=t?Object.keys(t):[];return v(1===i.length&&"_id"===i[0]?{_id:r}:I(n,{_id:r},{fields:t}),function(n){return o(n),r})})})},exports.join=function(r,t){t?(Object.entries(t).forEach(function(n){var r=n[0],t=n[1],e=t.on,o=t.fields;if(!t.Coll)throw new Error("Collection 'Coll' for '"+r+"' join is required.");if(!e)throw new Error("Join '"+r+"' has no 'on' condition specified.");var i=u(e);if(!h.includes(i))throw new Error("Join '"+r+"' has an unrecognized 'on' condition type of '"+i+"'. Should be one of '"+y+"'.");c(e)&&!o&&function(){var n;console&&console.warn&&(n=console).warn.apply(n,[].slice.call(arguments))}("Join '"+r+"' is defined with a function 'on', but no 'fields' are explicitely specified. This could lead to failed joins if the keys necessary for the join are not specified at query time.")}),b.set(r,n({},b.get(r),t))):b.set(r,void 0)},exports.protocols=G,exports.remove=function(n,r){var t=i(),e=D(n,"beforeRemove"),o=D(n,"onRemoved"),u=function(){var n=[].slice.call(arguments).filter(function(n){return n});return n.length?n.reduce(function(n,r){return E(n,r.fields)},null):null}(e,o);return v(null===u?[]:S(n,r,{fields:u}),function(i){return v(c(null==e?void 0:e.fn)&&e.fn(i),function(){var e=t.remove(n,r);return e&&c(null==o?void 0:o.fn)?(i.forEach(function(n){return o.fn(n)}),e):e})})},exports.setJoinPrefix=function(n){w=n},exports.setProtocol=function(r){void 0===r&&(r={}),o=n({},e,r)},exports.update=function(t,e,o,u){var f=void 0===u?{}:u,l=f.multi,d=void 0===l||l,s=r(f,z),a=i(),p=D(t,"beforeUpdate"),m=D(t,"onUpdated"),h=n({multi:d},s),y=function(n,r){if(!n&&!r)return null;if(!r)return null==n?void 0:n.fields;var t=r.before?r.fields:{_id:1};return n?E(n.fields,t):t}(p,m);return v(null===y?[]:S(t,e,{fields:y,limit:d?void 0:1}),function(n){return v(c(null==p?void 0:p.fn)&&p.fn(n,o),function(){return v(a.update(t,e,o,h),function(r){if(!r||!c(null==m?void 0:m.fn))return r;var e=function(n){return void 0===n&&(n=[]),Object.fromEntries(n.map(function(n){return[n._id,n]}))}(n);return v(S(t,{_id:{$in:Object.keys(e)}},{fields:m.fields}),function(n){return n.forEach(function(n){m.fn(n,e[n._id])}),r})})})})},exports.updateProtocol=function(r){o="function"==typeof r?r(o):n({},o,r)};
|
|
2
|
+
//# sourceMappingURL=coll-fns.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coll-fns.cjs","sources":["../src/protocol.js","../src/util.js","../src/join.js","../src/fields.js","../src/fetch.js","../src/hook.js","../src/protocols/meteorAsync.js","../src/protocols/meteorSync.js","../src/protocols/node.js","../src/count.js","../src/insert.js","../src/remove.js","../src/update.js"],"sourcesContent":["/**\n * Protocol interface implemented by adapters (Meteor, Node, etc.).\n * All methods may return synchronously or as a Promise.\n *\n * @typedef {Object} Protocol\n * @property {(Coll:any, selector?:Object, options?:Object) => number|Promise<number>} count\n * Count documents matching selector.\n * @property {(Coll:any, selector?:Object, options?:Object) => any} cursor\n * Return a driver-specific cursor/iterator for advanced usage.\n * @property {(Coll:any, selector?:Object, options?:Object) => Array|Promise<Array>} findList\n * Return an array of documents for selector/options.\n * @property {(Coll:any) => ((doc:any)=>any)|undefined} getTransform\n * Optional per-collection transform applied to each fetched document.\n * @property {(Coll:any, doc:Object, options?:Object) => any|Promise<any>} insert\n * Insert a document and return the inserted _id (or driver-specific result).\n * @property {(Coll:any, selector:Object, options?:Object) => number|Promise<number>} remove\n * Remove matching documents and return the number removed.\n * @property {(Coll:any, selector:Object, modifier:Object, options?:Object) => number|Promise<number>} update\n * Update matching documents and return the number modified.\n */\n\n/**\n * Default protocol that throws for unimplemented methods.\n * Adapters should be provided via setProtocol to override these.\n * @type {Protocol}\n * @internal\n */\nconst DEFAULT_PROTOCOL = {\n count(/* Coll, selector = {}, options = {} */) {\n throw new Error(`'count' method must be defined with 'setProtocol'.`);\n },\n\n cursor(/* Coll, selector = {}, options = {} */) {\n throw new Error(`'cursor' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that takes a collection, selector and options\n * and returns a list of documents. */\n findList(/* Coll, selector = {}, options = {} */) {\n throw new Error(`'findList' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that transforms each document defined at the collection level. */\n getTransform(/* Coll */) {\n return undefined;\n },\n\n /* A function that inserts a doc in a collection and returns the inserted _id. */\n insert(/* Coll, doc, options */) {\n throw new Error(`'insert' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that removes docs in a collection and returns the number of removed documents. */\n remove(/* Coll, selector, options */) {\n throw new Error(`'remove' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that updates docs in a collection and returns the number of modified documents. */\n update(/* Coll, selector, modifier, options */) {\n throw new Error(`'update' method must be defined with 'setProtocol'.`);\n },\n};\n\n/**\n * The active protocol used by all high-level operations.\n * Initialized with DEFAULT_PROTOCOL and overridden via setProtocol/updateProtocol.\n * @type {Protocol}\n * @internal\n */\nlet protocol = DEFAULT_PROTOCOL;\n\n/**\n * Get the current protocol or a derived view of it.\n *\n * Usage:\n * - getProtocol() -> returns the active protocol object.\n * - getProtocol((p)=>wrap(p)) -> returns the result of calling your function with the active protocol.\n * - getProtocol(customObj) -> returns customObj (handy for inline overrides).\n *\n * @template T\n * @param {((p:Protocol)=>T)|Partial<Protocol>|undefined} overload\n * @returns {Protocol|T|Partial<Protocol>} The active protocol or the provided overload output.\n *\n * @example\n * // Get the active protocol\n * const p = getProtocol();\n *\n * @example\n * // Temporarily call a wrapped version without mutating global state\n * const wrapped = getProtocol((p) => ({ ...p, findList: (...args) => audit(p.findList(...args)) }));\n *\n * @example\n * // Provide a one-off protocol-like object\n * const custom = getProtocol({ findList: () => [] });\n */\nexport function getProtocol(overload) {\n if (!overload) return protocol;\n if (typeof overload === \"function\") return overload(protocol);\n if (typeof overload === \"object\") return overload;\n return protocol;\n}\n\n/**\n * Set the active protocol by merging user methods over DEFAULT_PROTOCOL.\n * Ensures all required methods exist (missing ones will still throw).\n *\n * @param {Partial<Protocol>} [methods={}] Implementation methods to install.\n * @example\n * import meteorAsync from './protocols/meteorAsync';\n * setProtocol(meteorAsync);\n */\nexport function setProtocol(methods = {}) {\n protocol = { ...DEFAULT_PROTOCOL, ...methods };\n}\n\n/**\n * Mutate the current protocol.\n * - If passed a function, it receives the current protocol and must return the next protocol.\n * - If passed a partial object, it shallow-merges over the current protocol.\n *\n * @param {((p:Protocol)=>Protocol)|Partial<Protocol>} fnOrMethods\n * @example\n * // Merge additional methods\n * updateProtocol({ getTransform: () => (d) => d });\n *\n * @example\n * // Wrap existing behavior\n * updateProtocol((p) => ({ ...p, count: (...args) => withMetrics('count', () => p.count(...args)) }));\n */\nexport function updateProtocol(fnOrMethods) {\n protocol =\n typeof fnOrMethods === \"function\"\n ? fnOrMethods(protocol)\n : { ...protocol, ...fnOrMethods };\n}\n","/**\n * Return a lowercase JS runtime type string for a value.\n * Examples: 'object', 'array', 'string', 'number', 'function', 'date', 'null', 'undefined'\n * @param {*} obj\n * @returns {string}\n */\nexport const typeOf = (obj) =>\n ({}).toString.call(obj).split(\" \")[1].slice(0, -1).toLowerCase();\n\n/**\n * Check if a value is an array.\n * @param {*} x\n * @returns {x is any[]}\n */\nexport const isArr = (x) => Array.isArray(x);\n\n/**\n * Check if an array is empty (treats undefined as empty).\n * @param {any[]} [x=[]]\n * @returns {boolean}\n */\nexport const isEmpty = (x = []) => x.length <= 0;\n\n/**\n * Check if a value is a function.\n * @param {*} x\n * @returns {x is Function}\n */\nexport const isFunc = (x) => typeof x === \"function\";\n\n/**\n * Check if a value is null or undefined.\n * @param {*} x\n * @returns {x is null|undefined}\n */\nexport const isNil = (x) => [null, undefined].includes(x);\n\n/**\n * Check if a value is a plain object (not an array, not null).\n * @param {*} x\n * @returns {x is Record<string, any>}\n */\nexport const isObj = (x) => x && !isArr(x) && typeOf(x) === \"object\";\n\n/**\n * Check if a value is a Promise-like (has a then function).\n * @param {*} x\n * @returns {x is Promise<any>}\n */\nexport function isPromise(x) {\n return isFunc(x?.then);\n}\n\n/**\n * Heuristic: value is a selector if it's a plain object or a string (id).\n * @param {*} x\n * @returns {boolean}\n */\nexport const isSelector = (x) => isObj(x) || typeOf(x) === \"string\";\n\n/**\n * Check if a value is a MongoDB-style modifier object with at least one key.\n * @param {*} x\n * @returns {boolean}\n */\nexport const isModifier = (x) => isObj(x) && !isEmpty(Object.keys(x));\n\n/**\n * Extract unique first-level field names touched by a modifier's nested paths.\n * Example: { $set: { 'profile.name': 'A', age: 1 } } -> ['profile', 'age']\n * @param {Record<string, any>} modifier\n * @returns {string[]}\n */\nexport function get2ndLevelFields(modifier) {\n if (!isModifier(modifier)) return [];\n return Object.values(modifier).flatMap((fieldsMap = {}) => {\n if (!isObj(fieldsMap)) return [];\n return [\n ...new Set(\n Object.keys(fieldsMap).map((key) => {\n const [rootKey] = key.split(\".\");\n return rootKey;\n }),\n ),\n ];\n });\n}\n\n/**\n * Shallow-merge two objects by merging their second-level props.\n * For each top-level key: out[key] = { ...prev[key], ...added[key] }\n * @template T extends Record<string, any>, U extends Record<string, any>\n * @param {T} prev\n * @param {U} added\n * @returns {Record<string, any>}\n */\nexport function assign(prev, added) {\n const allKeys = [...new Set([...Object.keys(prev), ...Object.keys(added)])];\n return allKeys.reduce(\n (acc, key) => ({ ...acc, [key]: { ...prev[key], ...added[key] } }),\n {},\n );\n}\n\n/**\n * Return all items from toKeep that are not present in toRemove (by strict equality).\n * @template T\n * @param {T[]} toKeep\n * @param {T[]} toRemove\n * @returns {T[]}\n */\nexport function difference(toKeep, toRemove) {\n return toKeep.filter((item) => toRemove.indexOf(item) < 0);\n}\n\n/**\n * Check whether sourceArr contains any of the values in searchedArr.\n * @template T\n * @param {T[]} searchedArr\n * @param {T[]} sourceArr\n * @returns {boolean}\n */\nexport function includesSome(searchedArr, sourceArr) {\n return sourceArr.some((el) => searchedArr.indexOf(el) >= 0);\n}\n\n/**\n * Rename object keys using a dictionary mapping.\n * Keys not present in the dictionary are preserved.\n * @param {Record<string, string>} dictionnary\n * @param {Record<string, any>} object\n * @returns {Record<string, any>}\n */\nexport function renameKeys(dictionnary, object) {\n return map(([k, v]) => [dictionnary[k] || k, v], object);\n}\n\n/**\n * Union of two arrays preserving order and removing duplicates.\n * @template T\n * @param {T[]} arr1\n * @param {T[]} arr2\n * @returns {T[]}\n */\nexport function union(arr1, arr2) {\n const itemsToAdd = arr2.filter((item) => arr1.indexOf(item) < 0);\n return [...arr1, ...itemsToAdd];\n}\n\n/**\n * Keep only unique items from a list by converting each item to a comparable value.\n * - If toValueOrProp is a function, it's used to compute the comparable value.\n * - If it's a string, it's treated as a property name to read from each item.\n * - Otherwise, item identity is used.\n * @template T\n * @param {((x: T) => any)|string} toValueOrProp\n * @param {T[]} list\n * @returns {T[]}\n */\nexport function uniqueBy(toValueOrProp, list) {\n const toValue = isFunc(toValueOrProp)\n ? toValueOrProp\n : typeOf(toValueOrProp) === \"string\"\n ? (x) => x[toValueOrProp]\n : (x) => x;\n\n return list.reduce((acc, item) => {\n const value = toValue(item);\n const exists = acc.find((prevItem) => toValue(prevItem) === value);\n return exists ? acc : [...acc, item];\n }, []);\n}\n\n/* eslint-disable no-console */\n/**\n * Thin wrapper around console.warn (safe in environments without console).\n * @param {...any} args\n */\nexport const warn = (...args) =>\n console && console.warn && console.warn(...args);\n/* eslint-enable no-console */\n\n/**\n * Concatenate two arrays and remove duplicate values (strict equality).\n * @template T\n * @param {T[]} arr1\n * @param {T[]} arr2\n * @returns {T[]}\n */\nexport function combineNoDuplicates(arr1, arr2) {\n return [...arr1, ...arr2].reduce(\n (acc, el) => (acc.includes(el) ? acc : [...acc, el]),\n [],\n );\n}\n\n/**\n * Build an object keyed by _id from an array of documents.\n * @template T extends { _id: string }\n * @param {T[]} [docs=[]]\n * @returns {Record<string, T>}\n */\nexport function indexById(docs = []) {\n return Object.fromEntries(docs.map((doc) => [doc._id, doc]));\n}\n\n/**\n * Get a (possibly nested) property value using dot-notation.\n * If an intermediate value is an array, returns a flattened array of sub-values.\n * Examples:\n * - getPropValue('a.b', { a: { b: 1 } }) -> 1\n * - getPropValue('a.b', { a: [{ b: 1 }, { b: 2 }] }) -> [1,2]\n * @param {string} dotKey\n * @param {Record<string, any>} doc\n * @returns {any}\n */\nexport function getPropValue(dotKey, doc) {\n const [rootKey, ...rest] = dotKey.split(\".\");\n const rootValue = doc[rootKey];\n\n if (rest.length < 1) return rootValue;\n\n const subDotKey = rest.join(\".\");\n if (isObj(rootValue)) return getPropValue(subDotKey, rootValue);\n if (isArr(rootValue))\n return rootValue.flatMap((subDoc) => {\n const subValue = getPropValue(subDotKey, subDoc);\n return isArr(subValue) ? subValue : [subValue];\n });\n return rootValue;\n}\n\n/**\n * Universal \"then\" helper that works with:\n * - Promises (awaits then applies fn(value, true))\n * - Arrays: if any element is a Promise, waits Promise.all, else passes array as-is\n * - Plain values: calls fn(value, false)\n *\n * This allows writing sync/async-agnostic code paths.\n *\n * @template T, R\n * @param {T|Promise<T>|Array<any>} maybePromise\n * @param {(value: any, isAsync: boolean) => R|Promise<R>} fn\n * @returns {R|Promise<R>}\n */\nexport function then(maybePromise, fn) {\n /* If `maybePromise` is an array, check if ANY element is a promise. */\n if (isArr(maybePromise)) {\n const arr = maybePromise;\n if (arr.some(isPromise)) {\n return Promise.all(arr).then((value) => fn(value, true));\n }\n\n return fn(arr, false);\n }\n\n if (isPromise(maybePromise))\n return maybePromise.then((value) => fn(value, true));\n\n return fn(maybePromise, false);\n}\n\n/**\n * Map over arrays or objects.\n * - If x is an array, behaves like Array.prototype.map(fn).\n * - If x is a plain object, maps over Object.entries and rebuilds an object from returned [k, v] tuples.\n * - If x is omitted, returns a curried function waiting for the collection.\n *\n * For objects, fn receives ([key, value]) and must return [nextKey, nextValue].\n *\n * @template T, U\n * @param {(value: any, index?: number) => any} fn\n * @param {T[]|Record<string, any>} [x]\n * @returns {U[]|Record<string, any>|((x: any)=>any)}\n * @throws {TypeError} If x is neither an array nor a plain object.\n */\nexport function map(fn, x) {\n /* Return a curried function if object is undefined. */\n if (x === undefined) return (y) => map(fn, y);\n\n /* If object is an array, dispatch to native method. */\n if (isArr(x)) return x.map(fn);\n\n /* Ensure plain object */\n if (!isObj(x))\n throw new TypeError(`'map' only works on array or plain object`);\n\n /* If not an array, assume a plain object.\n * If not so, will throw an error. */\n return Object.fromEntries(Object.entries(x).map(fn));\n}\n\n/**\n * Filter arrays or objects.\n * - If x is an array, behaves like Array.prototype.filter(pred).\n * - If x is a plain object, filters Object.entries and rebuilds an object from kept entries.\n * - If x is omitted, returns a curried function waiting for the collection.\n *\n * For objects, pred receives ([key, value]) and must return a boolean.\n *\n * @template T\n * @param {(value: any, index?: number) => boolean} pred\n * @param {T[]|Record<string, any>} [x]\n * @returns {T[]|Record<string, any>|((x: any)=>any)}\n * @throws {TypeError} If x is neither an array nor a plain object.\n */\nexport function filter(pred, x) {\n /* Return a curried function if object is undefined. */\n if (x === undefined) return (y) => filter(pred, y);\n\n /* If object is an array, dispatch to native method. */\n if (isArr(x)) return x.filter(pred);\n\n /* Ensure plain object */\n if (!isObj(x))\n throw new TypeError(`'filter' only works on array or plain object`);\n\n /* If not an array, assume a plain object.\n * If not so, will throw an error. */\n return Object.fromEntries(Object.entries(x).filter(pred));\n}\n","import { isFunc, typeOf, warn } from \"./util\";\n\n/**\n * Allowed runtime types for the `on` property in a join definition.\n * - 'array' => [fromProp, toProp, toSelector?]\n * - 'function' => (doc) => selector\n * - 'object' => static selector object\n * @type {Array<'array'|'function'|'object'>}\n * @internal\n */\nconst KNOWN_TYPES = [\"array\", \"function\", \"object\"];\nconst knownTypesCaption = KNOWN_TYPES.join(\"', '\");\n\n/**\n * Global registry of join definitions per collection instance.\n * Map<Collection, Record<string, JoinDef>>\n * @type {Map<*, Record<string, JoinDef>|undefined>}\n * @internal\n */\nlet joinsDictionnary = new Map();\n\n/**\n * Optional prefix used to distinguish join fields within the `fields` option.\n * If set, join fields must be specified under this prefix, e.g.:\n * { fields: { [joinPrefix]: { author: 1, comments: { user: 1 } } } }\n * If falsy, join fields may be mixed with base fields and detected by their names.\n * @type {string|null}\n */\nlet joinPrefix = null;\n\n/**\n * @typedef {[*] | [string, string] | [string, string, Object]} JoinArrayOn\n * Array form describing relation:\n * - [fromProp, toProp] or [fromProp, toProp, toSelector]\n * fromProp: field on parent doc (string or ['field'] to denote array-valued)\n * toProp: field on joined doc (string or ['field'] to denote array-valued)\n * toSelector: optional additional selector for joined docs\n */\n\n/**\n * @typedef {(doc: any) => Object} JoinFunctionOn\n * Function form: receives the parent document and returns a selector\n * for fetching the related documents from the joined collection.\n */\n\n/**\n * @typedef {Object} JoinObjectOn\n * Static selector object applied to the joined collection.\n */\n\n/**\n * @typedef {JoinArrayOn | JoinFunctionOn | JoinObjectOn} JoinOn\n */\n\n/**\n * @typedef {Object} JoinDef\n * @property {*} Coll - The target collection to join with.\n * @property {JoinOn} on - Relation description (array/function/object).\n * @property {boolean} [single] - If true, attach a single document instead of an array.\n * @property {(joined: any[]|any, parent: any) => any} [postFetch] - Transform the joined value before attaching.\n * @property {Object} [fields] - Base collection fields required to perform the join when `on` is a function.\n * @property {number} [limit] - Limit for the joined fetch (applies when not single).\n * @property {any} [options] - Any extra options passed through to the underlying fetch/find implementation.\n */\n\n/**\n * Register/augment join definitions for a collection.\n * Validates join shapes and emits warnings for potentially unsafe definitions.\n *\n * Notes:\n * - Calling with `joins` falsy clears existing joins for the collection.\n * - If `on` is a function and no `fields` are declared for the join, a warning is emitted,\n * because required linking keys may not be fetched unless explicitly requested.\n *\n * @template TColl\n * @param {TColl} Collection - The collection instance to attach joins to.\n * @param {Record<string, JoinDef>|undefined|null|false} joins - Map of joinKey -> join definition.\n * @example\n * join(Posts, {\n * author: {\n * Coll: Users,\n * on: ['authorId', '_id'],\n * single: true,\n * postFetch(author, post) { return author && { _id: author._id, name: author.name }; }\n * },\n * comments: {\n * Coll: Comments,\n * on: ['_id', 'postId'],\n * limit: 50\n * }\n * });\n */\nexport function join(Collection, joins) {\n if (!joins) {\n // Explicitly set to undefined to signal no joins for this collection\n joinsDictionnary.set(Collection, undefined);\n return;\n }\n\n Object.entries(joins).forEach(([key, { Coll, on, fields }]) => {\n if (!Coll) {\n throw new Error(`Collection 'Coll' for '${key}' join is required.`);\n }\n\n if (!on) {\n throw new Error(`Join '${key}' has no 'on' condition specified.`);\n }\n\n const joinType = typeOf(on);\n if (!KNOWN_TYPES.includes(joinType)) {\n throw new Error(\n `Join '${key}' has an unrecognized 'on' condition type of '${joinType}'. Should be one of '${knownTypesCaption}'.`,\n );\n }\n\n // When on is a function, the join likely depends on keys from the parent doc.\n // Encourage declaring the base fields required so callers don't forget them.\n if (isFunc(on) && !fields) {\n warn(\n `Join '${key}' is defined with a function 'on', but no 'fields' are explicitely specified. This could lead to failed joins if the keys necessary for the join are not specified at query time.`,\n );\n }\n });\n\n // Merge new join defs with existing ones on this collection\n joinsDictionnary.set(Collection, {\n ...joinsDictionnary.get(Collection),\n ...joins,\n });\n}\n\n/**\n * Retrieve declared joins for a collection.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @returns {Record<string, JoinDef>} The join definitions keyed by join name.\n */\nexport function getJoins(Coll) {\n return joinsDictionnary.get(Coll) || {};\n}\n\n/**\n * Get the currently configured join fields prefix used in field projections.\n * If set, join fields should be nested under this key within `fields`.\n * @returns {string|null} The prefix (e.g., '+') or null if not set.\n */\nexport function getJoinPrefix() {\n return joinPrefix;\n}\n\n/**\n * Set the join fields prefix used in field projections.\n * Pass a falsy value (e.g., null) to disable the prefix behavior.\n *\n * @param {string|null} prefix - Prefix symbol/key (e.g., '+') or null to disable.\n * @example\n * setJoinPrefix('+');\n * // later in queries:\n * // fetch(Posts, {}, { fields: { '+': { author: 1, comments: 1 }, title: 1 } })\n */\nexport function setJoinPrefix(prefix) {\n joinPrefix = prefix;\n}\n","import { getJoinPrefix } from \"./join\";\nimport { filter, isObj } from \"./util\";\n\n/**\n * @typedef {Record<string, (0|1|boolean|FieldSpec)>} FieldSpec\n * A MongoDB-like field projection where:\n * - keys are field paths (may be nested objects or dot-notation)\n * - values are 1/true to include, 0/false to exclude, or nested FieldSpec\n *\n * Special handling:\n * - join fields may be grouped under the join prefix (e.g. '+') when set via getJoinPrefix()\n */\n\n/**\n * Normalize a field projection for downstream usage.\n * - If fields is not a plain object: returns undefined when truthy (meaning \"select all\"),\n * or {} when falsy (meaning \"select none\").\n * - When flatten=true, converts nested objects to dot-notation MongoDB projection.\n *\n * @param {FieldSpec|undefined|null|false} fields - Field projection to normalize.\n * @param {boolean} [flatten=false] - Whether to flatten nested fields to dot-notation.\n * @returns {Record<string, boolean>|undefined} Normalized projection, {} (none), or undefined (all).\n */\nexport function normalizeFields(fields, flatten = false) {\n if (!isObj(fields)) return fields ? undefined : {};\n if (!flatten) return fields;\n // Flatten nested objects to dot-notation\n return flattenFields(fields);\n}\n\n/**\n * Flatten a general field specifiers object (which could include nested objects)\n * into a MongoDB-compatible one that uses dot-notation.\n * See: https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/#projection\n *\n * Notes:\n * - If a key starts with '$' (e.g., $elemMatch), the subtree is preserved as-is.\n * - Avoids path collisions by omitting dot-notation keys when their sub-root is already selected.\n *\n * @param {FieldSpec|undefined} fields - Nested projection to flatten.\n * @param {string} [root] - Internal accumulator for the current path.\n * @returns {Record<string, boolean>|FieldSpec|undefined} Dot-notation projection or original structure for $-keys.\n */\nexport function flattenFields(fields, root) {\n if (!fields) return fields;\n\n const keys = Object.keys(fields);\n\n // Do not flatten when a $-operator exists (e.g., { $elemMatch: ... })\n if (keys.some((k) => k.startsWith(\"$\")))\n return root ? { [root]: fields } : fields;\n\n return keys.reduce((acc, k) => {\n // Prevent path collisions when dot-notation key is under an already selected sub-root\n const dotStrIndex = k.indexOf(\".\");\n if (dotStrIndex >= 0) {\n const subRoot = k.slice(0, dotStrIndex);\n const subRootSelection = fields[subRoot];\n if (subRootSelection && !isObj(subRootSelection)) return acc;\n }\n\n const shouldSelect = fields[k];\n const dotKey = root ? [root, k].join(\".\") : k;\n\n if (!isObj(shouldSelect)) {\n return { ...acc, [dotKey]: !!shouldSelect };\n }\n\n return { ...acc, ...flattenFields(shouldSelect, dotKey) };\n }, undefined);\n}\n\n/**\n * Given a fields object and join definitions,\n * split fields into:\n * - '_' (own/base collection fields)\n * - '+' (join fields, keyed by join name)\n *\n * Ensures base fields are flattened and, when needed, augmented so joins have\n * access to required linking properties (e.g., on/from keys).\n *\n * @param {FieldSpec|undefined} fields - Field projection that may include join fields.\n * @param {Record<string, any>} [joins={}] - Join definitions keyed by join name.\n * @returns {{ _: Record<string, boolean>|undefined, '+': FieldSpec|undefined }}\n */\nexport function dispatchFields(fields, joins = {}) {\n if (!isObj(fields)) return { _: normalizeFields(fields) };\n\n const { \"+\": joinFields, ...ownFields } = isolateJoinFields(fields, joins);\n\n if (!joinFields) {\n return { _: normalizeFields(ownFields, true), \"+\": undefined };\n }\n\n // If all own fields are included (i.e., unspecified), we can return as-is\n const allOwnIncluded = !ownFields || !Object.keys(ownFields)?.length;\n\n if (allOwnIncluded) {\n return { _: normalizeFields(ownFields, true), \"+\": joinFields };\n }\n\n // Otherwise, ensure we include any fields required by the join definitions\n // (on/from keys and/or explicit fields defined on the join).\n const augmentedOwnFields = Object.keys(joinFields).reduce((acc, joinKey) => {\n const { on, fields } = joins[joinKey];\n const onFields = Array.isArray(on) ? { [on[0]]: 1 } : undefined;\n if (!(onFields || fields)) return acc;\n return { ...acc, ...onFields, ...fields };\n }, ownFields);\n\n return { _: normalizeFields(augmentedOwnFields, true), \"+\": joinFields };\n}\n\n/**\n * Combine two field projections into one.\n * Rules:\n * - If either is \"all fields\" (undefined or non-object truthy), the result is undefined (select all).\n * - If both are falsy, returns {} (select none).\n * - Otherwise, performs a deep merge where nested objects are merged recursively\n * and scalar values from b override a.\n *\n * @param {FieldSpec|undefined|null|false} a - First projection.\n * @param {FieldSpec|undefined|null|false} b - Second projection.\n * @returns {Record<string, boolean>|undefined} Combined projection.\n */\nexport function combineFields(a, b) {\n // If any fields targets all of them, return undefined to keep all fields\n if (allFields(a) || allFields(b)) return undefined;\n\n // If both fields are falsy, return {} to target no field.\n if (!a && !b) return {};\n\n return deepMergeFields(removeFalsyFields(a), removeFalsyFields(b));\n}\n\n/**\n * Deep merge utility for fields objects.\n * - If both values are plain objects, merge recursively.\n * - Otherwise the value from `b` overrides `a`.\n *\n * @param {Record<string, any>} [a={}] - Base projection.\n * @param {Record<string, any>} [b={}] - Projection to merge over a.\n * @returns {Record<string, any>} Merged projection.\n * @internal\n */\nfunction deepMergeFields(a = {}, b = {}) {\n const out = { ...a };\n for (const [key, valB] of Object.entries(b || {})) {\n const valA = out[key];\n if (isObj(valA) && isObj(valB)) {\n out[key] = deepMergeFields(valA, valB);\n } else {\n out[key] = valB;\n }\n }\n return out;\n}\n\n/**\n * Determine if a projection means \"all fields\".\n * Semantics:\n * - undefined => all fields\n * - falsy (null/false/0) => not all\n * - non-object truthy (e.g., 1/true) => all fields\n * - object => not all\n *\n * @param {any} fields - Projection to test.\n * @returns {boolean} True if it means \"all fields\".\n * @internal\n */\nfunction allFields(fields) {\n if (fields === undefined) return true;\n if (!fields) return false;\n if (isObj(fields)) return false;\n return true;\n}\n\n/**\n * Remove falsy values from a fields object.\n * When the input is not a plain object, returns an empty object.\n *\n * @param {any} fields - Projection to clean.\n * @returns {Record<string, any>} Cleaned fields.\n * @internal\n */\nfunction removeFalsyFields(fields) {\n if (!isObj(fields)) return {};\n return filter(([, v]) => v, fields);\n}\n\n/**\n * Given a fields object and a list of joins,\n * return a fields object where all join fields\n * are grouped under a \"+\" (or configured) key.\n *\n * Returned join fields are validated against the provided joins\n * and non-join fields remain at the root level.\n *\n * Behavior depends on the configured join prefix (via getJoinPrefix()):\n * - If a prefix exists, only fields under that key are considered join fields.\n * - If not, any field whose key exists in `joins` is treated as a join field.\n *\n * @param {FieldSpec} fields - Original fields projection.\n * @param {Record<string, any>} [joins={}] - Join definitions keyed by join name.\n * @returns {{ '+': FieldSpec|undefined } & FieldSpec} Fields split between join and own keys.\n * @internal\n */\nfunction isolateJoinFields(fields, joins = {}) {\n const joinKeys = Object.keys(joins);\n const joinPrefix = getJoinPrefix();\n\n if (joinPrefix) {\n const { [joinPrefix]: joinFields, ...ownFields } = fields;\n\n if (!joinFields) return { \"+\": undefined, ...ownFields };\n\n const existingJoinFields = filter(\n ([k]) => joinKeys.includes(k),\n joinFields,\n );\n return { \"+\": existingJoinFields, ...ownFields };\n }\n\n // If no configured prefix, treat any key that matches a declared join as a join field.\n return Object.entries(fields).reduce((acc, [k, v]) => {\n // Not a join: keep at root\n if (!joinKeys.includes(k)) return { ...acc, [k]: v };\n\n // Join: group under \"+\" key\n const prev = acc[\"+\"] || {};\n return { ...acc, \"+\": { ...prev, [k]: v } };\n }, {});\n}\n\n/**\n * Decrement by one the numeric depth for a given join key in a fields object.\n * Useful to control recursive join depth across nested fetches.\n *\n * - If the value is not a number or is Infinity, the fields are returned unchanged.\n * - Respects the configured join prefix (e.g., '+') when present.\n *\n * @param {string} key - Join key to decrement.\n * @param {FieldSpec|undefined} fields - Field projection possibly containing the join key.\n * @returns {FieldSpec|undefined} A new fields object with decremented depth, or the original.\n */\nexport function decrementRecursiveField(key, fields) {\n if (!isObj(fields)) return fields;\n\n const joinPrefix = getJoinPrefix();\n\n const prev = joinPrefix ? fields[joinPrefix]?.[key] : fields[key];\n\n if (typeof prev !== \"number\") return fields;\n if (prev === Infinity) return fields;\n\n const decremented = prev - 1;\n\n if (!joinPrefix) return { ...fields, [key]: decremented };\n\n return {\n ...fields,\n [joinPrefix]: { ...fields[joinPrefix], [key]: decremented },\n };\n}\n","import { getProtocol } from \"./protocol\";\nimport { then } from \"./util\";\nimport {\n decrementRecursiveField,\n dispatchFields,\n normalizeFields,\n} from \"./fields\";\nimport { getJoins } from \"./join\";\nimport {\n getPropValue,\n includesSome,\n isArr,\n isFunc,\n isObj,\n typeOf,\n uniqueBy,\n} from \"./util\";\n\n/**\n * @typedef {Object} FetchOptions\n * @property {Object} [fields] - Field projection. Supports nested objects and '+' join fields.\n * @property {Object} [sort] - Sort specification (e.g., { createdAt: -1 }).\n * @property {number} [limit] - Max number of documents to return.\n * @property {number} [skip] - Number of documents to skip.\n * @property {Function} [transform] - Document transform function. If omitted, protocol getTransform(Coll) is used.\n * @property {Object} [joins] - Optional per-call join overrides (usually joins come from getJoins(Coll)).\n *\n * @typedef {Object} JoinDef\n * @property {*} Coll - Target collection of the join.\n * @property {[*]|Object|Function} on - How to relate parent docs to target docs:\n * - Array form: [fromProp, toProp, toSelector?]\n * - Object form: static selector for target docs\n * - Function form: (doc) => selector, computed per parent doc\n * @property {boolean} [single] - If true, attach a single doc instead of an array.\n * @property {Function} [postFetch] - (joined, parentDoc) => any. Final shaping of joined value.\n * @property {number} [limit] - Limit for joined query when single is false.\n */\n\n/**\n * Retrieve documents of a collection, with optional joined subdocuments.\n * - Fields accept nested objects; dot-notation is normalized internally.\n * - Joins are defined via getJoins(Coll). Join usage is controlled through '+' in fields.\n * - Works with both sync and async protocols.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} [selector={}] - MongoDB-style query selector.\n * @param {FetchOptions} [options={}] - Fetch options and join controls.\n * @returns {Array|Promise<Array>} List of documents, possibly augmented with join keys.\n *\n * @example\n * // Simple list\n * const users = await fetchList(Users, { status: 'active' }, { fields: { name: 1 } });\n *\n * @example\n * // Join authors on posts\n * const posts = await fetchList(Posts, {}, {\n * fields: { title: 1, '+': { author: 1 } }\n * });\n */\nexport function fetchList(Coll, selector = {}, options = {}) {\n const { count, findList, getTransform } = getProtocol();\n\n const joins = getJoins(Coll);\n\n const collTransform = getTransform(Coll);\n const { fields, transform = collTransform, ...restOptions } = options;\n\n const enhance = (doc) => (isFunc(transform) ? transform(doc) : doc);\n\n // Partition field spec into own (base collection) and join fields ('+')\n const { _: ownFields, \"+\": joinFields = {} } = dispatchFields(fields, joins);\n const usedJoinKeys = Object.keys(joinFields);\n\n // If no joins or fields are not objects, perform a straight fetch\n if (!joins || !usedJoinKeys?.length || (fields && !isObj(fields))) {\n return then(\n findList(Coll, selector, {\n ...restOptions,\n fields: ownFields,\n transform: null,\n }),\n (docs) => docs.map(enhance),\n );\n }\n\n /* === END FETCH WHEN NO JOINS === */\n\n // When joins exist, exclude transform from base fetch to reapply after joining\n return then(\n findList(Coll, selector, {\n ...restOptions,\n fields: ownFields,\n transform: null,\n }),\n\n (docs) => {\n // Partition joins by \"on\" type to process differently\n const joinsByType = usedJoinKeys.reduce((acc, joinKey) => {\n const join = joins[joinKey];\n if (!join) return acc;\n\n const type = typeOf(join.on);\n const enhancedJoin = { ...join, _key: joinKey };\n const prev = acc[type] || [];\n return { ...acc, [type]: [...prev, enhancedJoin] };\n }, {});\n\n const {\n array: arrJoins = [],\n object: objJoins = [],\n function: fnJoins = [],\n } = joinsByType;\n\n // Process array-type joins: [fromProp, toProp, toSelector?]\n return then(\n arrJoins.reduce((_docs, join) => {\n const {\n _key,\n Coll: joinColl,\n on,\n single,\n postFetch,\n limit: joinLimit,\n ...joinRest\n } = join;\n\n return then(_docs, (readyDocs) => {\n const [fromProp, toProp, toSelector = {}] = on;\n const fromArray = isArr(fromProp);\n const propList = fromArray\n ? readyDocs.flatMap((doc) => doc[fromProp[0]])\n : readyDocs.map((doc) => doc[fromProp]);\n\n const toArray = isArr(toProp);\n const subSelector = toArray\n ? {\n ...toSelector,\n [toProp[0]]: { $elemMatch: { $in: propList } },\n }\n : { ...toSelector, [toProp]: { $in: propList } };\n\n // Support recursive joins by checking for additional depth and data existence\n const isRecursive = joinColl === Coll && joinFields[_key] > 1;\n\n return then(\n isRecursive && count(Coll, subSelector),\n\n (recursiveCount) => {\n const stopRecursion = isRecursive && !recursiveCount;\n\n const subJoinFields = isRecursive\n ? decrementRecursiveField(_key, fields)\n : joinFields[_key];\n\n // Determine whether we need to include toProp explicitly in subFields\n const { _: own } = dispatchFields(\n subJoinFields,\n getJoins(joinColl) || {},\n );\n\n const allOwnIncluded = !own || Object.keys(own).length <= 0;\n const shouldAddToProp =\n isObj(subJoinFields) && !allOwnIncluded && toProp !== \"_id\";\n\n const subFields = shouldAddToProp\n ? { ...subJoinFields, [toProp]: 1 }\n : subJoinFields;\n\n /** @type {FetchOptions} */\n const subOptions = {\n ...options,\n ...joinRest,\n fields: normalizeFields(subFields),\n limit: undefined,\n transform: isRecursive ? transform : undefined,\n };\n\n // Fetch all joined docs for this join and attach to each base doc\n return then(\n stopRecursion\n ? []\n : fetchList(joinColl, subSelector, subOptions),\n\n (allJoinedDocs) => {\n // Build index by toProp for faster lookups when toProp is scalar\n const indexedByToProp = toArray\n ? {}\n : allJoinedDocs.reduce((acc, joinedDoc) => {\n const toPropValue = getPropValue(toProp, joinedDoc);\n if (isArr(toPropValue)) {\n return toPropValue.reduce((acc2, v) => {\n const prev = acc2[v] || [];\n return { ...acc2, [v]: [...prev, joinedDoc] };\n }, acc);\n }\n const prev = acc[toPropValue] || [];\n return {\n ...acc,\n [toPropValue]: [...prev, joinedDoc],\n };\n }, {});\n\n return readyDocs.map((doc) => {\n let joinedDocs = [];\n\n if (toArray) {\n // toProp is an array on joined docs\n joinedDocs = allJoinedDocs.filter((joinedDoc) => {\n const toList = joinedDoc[toProp[0]] || [];\n if (!fromArray) return toList.includes(doc[fromProp]);\n\n const fromList = doc[fromProp[0]] || [];\n return includesSome(toList, fromList);\n });\n } else if (fromArray) {\n // fromProp is array on parent docs\n const fromValues = doc[fromProp[0]] || [];\n joinedDocs = uniqueBy(\n \"_id\",\n fromValues.flatMap(\n (fromValue) => indexedByToProp[fromValue] || [],\n ),\n );\n } else {\n // Both scalar\n const fromValue = doc[fromProp];\n joinedDocs = indexedByToProp[fromValue] || [];\n }\n\n const raw = single ? joinedDocs[0] : joinedDocs;\n const afterPostFetch = isFunc(postFetch)\n ? postFetch(raw, doc)\n : raw;\n return { ...doc, [_key]: afterPostFetch };\n });\n },\n );\n },\n );\n });\n }, docs),\n\n (docsWithArrJoins) => {\n // Prepare object-type joins (static selector): fetched once, applied per doc\n return then(\n objJoins.map((join) => {\n const { _key, on } = join;\n const subSelector = on;\n return createJoinFetcher({\n Coll,\n join,\n fields: joinFields[_key],\n subSelector,\n options: restOptions,\n parentFields: fields,\n });\n }),\n\n (objJoinsEnhancers) => {\n // For each doc, apply object-join enhancers, then function-type joins per doc\n return then(\n docsWithArrJoins.map((doc) => {\n const docWithObjJoins = objJoinsEnhancers.reduce(\n (_doc, fn) => fn(_doc),\n doc,\n );\n\n return then(\n fnJoins.reduce((_doc, join) => {\n const { _key, on } = join;\n\n return then(\n [\n _doc,\n createJoinFetcher({\n Coll,\n join,\n fields: joinFields[_key],\n subSelector: isFunc(on) ? on(doc) : on,\n options: restOptions,\n parentFields: fields,\n }),\n ],\n\n ([_doc, joinFetcher]) => joinFetcher(_doc),\n );\n }, docWithObjJoins),\n\n // Re-apply transform after all joins\n (docWithFnJoins) => enhance(docWithFnJoins),\n );\n }),\n\n (res) => res,\n );\n },\n );\n },\n );\n },\n );\n}\n\n/**\n * Fetch a single document matching the selector.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style query selector.\n * @param {FetchOptions} [options={}] - Fetch options.\n * @returns {Object|undefined|Promise<Object|undefined>} First matching document or undefined.\n */\nexport function fetchOne(Coll, selector, options = {}) {\n return then(\n fetchList(Coll, selector, { ...options, limit: 1 }),\n (res) => res[0],\n );\n}\n\n/**\n * Fetch only document IDs for the selector.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style query selector.\n * @param {FetchOptions} [options] - Fetch options.\n * @returns {Array<string>|Promise<Array<string>>} Array of IDs.\n */\nexport function fetchIds(Coll, selector, options) {\n return then(\n fetchList(Coll, selector, { ...options, fields: { _id: 1 } }),\n (res) => pluckIds(res),\n );\n}\n\n/**\n * Check existence of at least one document matching selector.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style query selector.\n * @returns {boolean|Promise<boolean>} True if a document exists.\n */\nexport function exists(Coll, selector) {\n return then(\n // Limit to _id field to ensure minimal data\n fetchOne(Coll, selector, { fields: { _id: 1 } }),\n\n (doc) => !!doc,\n );\n}\n\n/**\n * Extract _id values from an array of documents.\n * @param {Array<{_id: string}>} arr - Documents to pluck IDs from.\n * @returns {Array<string>} List of IDs.\n * @internal\n */\nfunction pluckIds(arr) {\n return arr.map(({ _id }) => _id);\n}\n\n/* HELPERS */\n\n/**\n * Create a join fetcher function for a given join definition.\n * Returns a function (doc) => docWithJoin that attaches joined data under join _key.\n *\n * Handles:\n * - Recursive joins (Coll === joinColl) with depth tracking via decrementRecursiveField\n * - Post-fetch shaping via join.postFetch\n *\n * @param {Object} args\n * @param {*} args.Coll - Parent collection.\n * @param {JoinDef & {_key: string}} args.join - The join definition with internal key.\n * @param {Object|number} args.fields - Join field spec or depth number (for '+').\n * @param {Object} args.subSelector - Selector for the joined collection.\n * @param {FetchOptions} args.options - Parent fetch options to forward.\n * @param {Object} args.parentFields - Parent fields, used for recursive depth.\n * @returns {Function|Promise<Function>} Function that attaches joined data to a doc.\n * @internal\n */\nfunction createJoinFetcher({\n Coll,\n join: {\n _key,\n Coll: joinColl,\n on,\n single,\n postFetch,\n limit: joinLimit,\n ...joinRest\n },\n fields,\n subSelector,\n options,\n parentFields,\n}) {\n const { count } = getProtocol();\n\n const isRecursive = joinColl === Coll;\n\n return then(\n // For recursive joins, check if there would be results to avoid unnecessary nested fetch\n isRecursive && count(Coll, subSelector),\n\n (recursiveCount) => {\n const stopRecursion = isRecursive && (!fields || !recursiveCount);\n\n const joinFields = isRecursive\n ? decrementRecursiveField(_key, parentFields)\n : fields;\n\n /** @type {FetchOptions} */\n const subOptions = {\n ...options,\n ...joinRest,\n fields: normalizeFields(joinFields),\n limit: single ? 1 : joinLimit || undefined,\n };\n\n // Fetch joined docs and build an applier function\n return then(\n stopRecursion ? [] : fetchList(joinColl, subSelector, subOptions),\n\n (joinedDocs) => {\n return (doc) => {\n const raw = single ? joinedDocs[0] : joinedDocs;\n const afterPostFetch = isFunc(postFetch)\n ? postFetch(raw, doc)\n : raw;\n return { ...doc, [_key]: afterPostFetch };\n };\n },\n );\n },\n );\n}\n","import { combineFields } from \"./fields\";\nimport { isArr, isFunc, then } from \"./util\";\n\n/**\n * List of supported hook types.\n * - beforeInsert: runs before inserting a document. (docToInsert)\n * - beforeUpdate: runs before updating documents. (docsToUpdate, modifier)\n * - beforeRemove: runs before removing documents. (docsToRemove)\n * - onInserted: runs after a document is inserted. (doc)\n * - onUpdated: runs after a document is updated. (doc, beforeDoc?)\n * - onRemoved: runs after a document is removed. (doc)\n * @type {Array<HookType>}\n * @readonly\n */\nconst HOOK_TYPES = [\n \"beforeInsert\", // (docToInsert)\n \"beforeUpdate\", // (docsToUpdate, modifier)\n \"beforeRemove\", // (docsToRemove)\n\n \"onInserted\", // (doc)\n \"onUpdated\", // (doc, before)\n \"onRemoved\", // (doc)\n];\n\n/**\n * @typedef {'beforeInsert'|'beforeUpdate'|'beforeRemove'|'onInserted'|'onUpdated'|'onRemoved'} HookType\n */\n\n/**\n * Generic hook function signature.\n * The concrete arguments depend on the HookType (see HOOK_TYPES above).\n * Returning a promise is supported.\n * @typedef {(β¦args:any[]) => any|Promise<any>} HookFn\n */\n\n/**\n * Optional predicate to prevent a hook from running.\n * If returns a truthy value, the hook is skipped.\n * Receives the same arguments as the HookFn.\n * @typedef {(...args:any[]) => boolean} HookUnlessPredicate\n */\n\n/**\n * Optional predicate that must be truthy for the hook to run.\n * Receives the same arguments as the HookFn.\n * @typedef {(...args:any[]) => boolean} HookWhenPredicate\n */\n\n/**\n * Hook definition object.\n * - before: Only meaningful for \"onUpdated\". If true, the \"before\" document should be fetched.\n * - fields: Projection of fields to fetch for the documents the hook needs.\n * Combined across all hooks of the same type via combineFields.\n * `undefined` or `true` means \"all fields\".\n * - fn: The hook function (required).\n * - unless: Optional predicate; if truthy, prevents the hook from running.\n * - when: Optional predicate; if truthy, allows the hook to run.\n * @typedef {Object} HookDef\n * @property {boolean} [before]\n * @property {import('./fields').FieldSpec|true|undefined} [fields]\n * @property {HookFn} fn\n * @property {HookUnlessPredicate} [unless]\n * @property {HookWhenPredicate} [when]\n */\n\n/**\n * Single registry holding hook definitions per collection instance.\n * Map<CollectionInstance, Record<HookType, HookDef[]>>\n * @type {Map<*, Record<HookType, HookDef[]>>}\n * @internal\n */\nconst hooksRegistry = new Map();\n\n/*\n * hookDef = {\n * before, // Bool. onUpdated only. When true, fetch document before update. Otherwise, document will be fetched, but only with its _id field to know which docs to retrieve after the update.\n * fields, // Fields of document to fetch. Will be combined for all hooks of the same type. `undefined` or `true` means all fields and subsequent field restrictions won't apply.\n * fn, // (doc, before<onUpdated>) => side effect. Function to run as hook\n * unless, // (doc, before<onUpdated>) => bool. Predicate to prevent hook from running\n * when, // (doc, before<onUpdated>) => bool. Predicate to run the hook\n * };\n */\n\n/**\n * Register multiple hooks for a collection.\n * The hooksObj keys must be valid HookType values, and each value must be an array of HookDef.\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {Record<HookType, HookDef[]>} hooksObj - Object mapping hook types to arrays of hook definitions.\n * @throws {TypeError} If a hook list is not an array or an unknown hook type is provided.\n * @example\n * hook(Users, {\n * beforeInsert: [{\n * fields: { email: 1 },\n * fn(doc) {\n * if (!doc.email) throw new Error('Email required');\n * }\n * }],\n * onInserted: [{\n * fn(doc) { console.log('Inserted user', doc._id); }\n * }]\n * });\n */\nexport function hook(\n Coll, // Collection class instance\n hooksObj, // { ...[hookType]: array of hook definition }\n) {\n Object.entries(hooksObj).forEach(([hookType, hooks]) => {\n if (!isArr(hooks)) {\n throw new TypeError(`'${hookType}' hooks must be an array`);\n }\n\n hooks.forEach((_hook) => addHookDefinition(Coll, hookType, _hook));\n });\n}\n\n/**\n * Add a hook definition for a given collection and hook type.\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {HookType} hookType - The hook type.\n * @param {HookDef} hookDef - The hook definition to add.\n * @throws {TypeError} If hookType is invalid or hookDef.fn is not a function.\n * @internal\n */\nfunction addHookDefinition(Coll, hookType, hookDef) {\n if (!HOOK_TYPES.includes(hookType)) {\n throw new TypeError(`'${hookType}' is not a valid hook type`);\n }\n\n if (!isFunc(hookDef?.fn)) {\n throw new TypeError(\"'hook' must be a function or contain a 'fn' key\");\n }\n\n const collHooks = getHookDefinitions(Coll);\n const prevHooks = collHooks[hookType] || [];\n const nextHooks = [...prevHooks, hookDef];\n hooksRegistry.set(Coll, { ...collHooks, [hookType]: nextHooks });\n}\n\n/**\n * Retrieve the hook definitions for a collection.\n *\n * - If hookType is omitted, returns the full record of hook arrays keyed by HookType.\n * - If hookType is provided, returns the array of HookDef for that type or undefined.\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {HookType} [hookType] - Optional hook type filter.\n * @returns {Record<HookType, HookDef[]>|HookDef[]|undefined} Hook definitions.\n */\nexport function getHookDefinitions(Coll, hookType) {\n const collHooks = hooksRegistry.get(Coll) || {};\n if (!hookType) return collHooks;\n return collHooks[hookType];\n}\n\n/**\n * Get a combined hook for a collection and type.\n * This merges multiple HookDef into a single executable hook with:\n * - fields: combined via combineFields across all HookDef.fields\n * - before: true if any HookDef sets before=true\n * - fn: a runner that applies all matching hooks honoring their when/unless predicates\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {HookType} hookType - The hook type.\n * @returns {{ fields: any, fn: HookFn, before?: boolean }|undefined} Combined hook or undefined if none.\n */\nexport function getHook(Coll, hookType) {\n const hookDefinitions = getHookDefinitions(Coll, hookType);\n\n if (!hookDefinitions) return undefined;\n\n return combineHookDefinitions(hookDefinitions);\n}\n\n/**\n * Combine multiple hook definitions into a single aggregated definition.\n * - fields are merged with combineFields\n * - before is true if any definition requires it\n * - fn executes all hooks (respecting when/unless) and resolves once all are done\n *\n * @param {HookDef[]} [hookDefs=[]]\n * @returns {{ fields: any, fn: HookFn, before?: boolean }|undefined}\n * @internal\n */\nfunction combineHookDefinitions(hookDefs = []) {\n if (!hookDefs.length) return undefined;\n\n /* Reduce hook definitions to derive combined fields and `before` option */\n const { fields, before } = hookDefs.reduce(\n (acc, hookDef) => {\n return {\n fields: combineFields(acc.fields, hookDef.fields),\n before: acc.before || hookDef.before,\n };\n },\n { fields: null, before: undefined },\n );\n\n /* Given the hook type arguments, convert each hook to a handler,\n * then execute them with the arguments as part of a single promise. */\n function globalHandler(...args) {\n const handlers = hookDefs\n .map((hookDef) => hookToHandler(hookDef, ...args))\n .filter(isFunc);\n\n return then(\n handlers.map((handler) => handler(...args)),\n (res) => res,\n );\n }\n\n return { fields, fn: globalHandler, before };\n}\n\n/**\n * Convert a HookDef into an executable handler (or undefined) by applying\n * the optional unless/when predicates with the invocation arguments.\n *\n * - If unless returns truthy, the hook is skipped.\n * - If when is provided and returns falsy, the hook is skipped.\n * - Otherwise returns the hook's fn.\n *\n * @param {HookDef} param0 - Hook definition.\n * @param {...any} args - Arguments the hook would receive.\n * @returns {HookFn|undefined} The executable function or undefined if filtered out.\n * @internal\n */\nfunction hookToHandler({ fn, unless, when }, ...args) {\n const prevented = unless?.(...args);\n if (prevented) return;\n\n const shouldRun = !when || when(...args);\n\n if (!shouldRun) return;\n\n return fn;\n}\n","export default {\n count(Coll, selector, options) {\n /* Use rawCollection's countDocuments method if available\n * to prevent loading in memory */\n const rawColl = Coll.rawCollection();\n if (rawColl.countDocuments) return rawColl.countDocuments(selector);\n\n /* If `countDocuments` raw collection method is not available,\n * use the MongoDB async count method. */\n return Coll.find(selector, options).countAsync();\n },\n\n cursor: (Coll, selector, options) => Coll.find(selector, options),\n\n findList: (Coll, selector, options) =>\n Coll.find(selector, options).fetchAsync(),\n\n getTransform: (Coll) => Coll._transform,\n\n insert: (Coll, doc) => Coll.insertAsync(doc),\n\n remove: (Coll, selector) => Coll.removeAsync(selector),\n\n update: (Coll, selector, modifier, options) => {\n /* Allow multi document update by default */\n return Coll.updateAsync(selector, modifier, { multi: true, ...options });\n },\n};\n","export default {\n count: (Coll, selector, options) => Coll.find(selector, options).count(),\n\n cursor: (Coll, selector, options) => Coll.find(selector, options),\n\n findList: (Coll, selector, options) => Coll.find(selector, options).fetch(),\n\n getTransform: (Coll) => Coll._transform,\n\n insert: (Coll, doc) => Coll.insert(doc),\n\n remove: (Coll, selector) => Coll.remove(selector),\n\n update: (Coll, selector, modifier, options) => {\n /* Allow multi document update by default */\n return Coll.update(selector, modifier, { multi: true, ...options });\n },\n};\n","import { renameKeys } from \"../util\";\n\n/**\n * Node protocol for the official MongoDB driver.\n * Assumes Coll is an instance of mongodb.Collection.\n */\nexport default {\n /**\n * Count documents matching selector.\n * Uses countDocuments (preferred over deprecated cursor.count()).\n */\n count(Coll, selector = {}, options = {}) {\n return Coll.countDocuments(selector || {}, options);\n },\n\n /**\n * Return a MongoDB FindCursor for advanced usage.\n * Maps fields -> projection.\n */\n cursor(Coll, selector = {}, options = {}) {\n const renamedOptions = renameKeys({ fields: \"projection\" }, options || {});\n return Coll.find(selector || {}, renamedOptions);\n },\n\n /**\n * Return an array of documents for selector/options.\n */\n findList(Coll, selector = {}, options = {}) {\n const renamedOptions = renameKeys({ fields: \"projection\" }, options || {});\n return Coll.find(selector || {}, renamedOptions).toArray();\n },\n\n /**\n * Optional per-collection transform; expose Coll.transform if present.\n */\n getTransform(Coll) {\n return typeof Coll?.transform === \"function\" ? Coll.transform : undefined;\n },\n\n /**\n * Insert a document and return insertedId.\n */\n insert(Coll, doc, options) {\n return Coll.insertOne(doc, options).then((res) => res?.insertedId);\n },\n\n /**\n * Remove documents. Honors options.multi (default true).\n */\n remove(Coll, selector = {}, options = {}) {\n const { multi = true, ...rest } = options || {};\n const p = multi\n ? Coll.deleteMany(selector || {}, rest)\n : Coll.deleteOne(selector || {}, rest);\n return p.then((res) => res?.deletedCount ?? 0);\n },\n\n /**\n * Update documents. Honors options.multi (default true).\n * Returns modifiedCount (or upsertedCount as fallback).\n */\n update(Coll, selector = {}, modifier = {}, options = {}) {\n const { multi = true, ...rest } = options || {};\n const p = multi\n ? Coll.updateMany(selector || {}, modifier || {}, rest)\n : Coll.updateOne(selector || {}, modifier || {}, rest);\n return p.then((res) => res?.modifiedCount ?? res?.upsertedCount ?? 0);\n },\n};\n","import { getProtocol } from \"./protocol\";\nimport { then } from \"./util\";\n\n/**\n * Count documents in a collection matching the selector.\n *\n * Works with both synchronous and asynchronous protocols.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance to count documents in.\n * @param {Object} selector - MongoDB-style query selector (e.g., { status: 'active' }).\n * @returns {number|Promise<number>} The number of matching documents.\n *\n * @example\n * // Count all active users\n * const active = await count(UsersCollection, { status: 'active' });\n *\n * @example\n * // Count all documents\n * const total = await count(UsersCollection, {});\n */\nexport function count(Coll, selector) {\n const { count: _count } = getProtocol();\n\n // Normalize sync/async protocol result to a Promise-like flow\n return then(_count(Coll, selector), (res) => res);\n}\n","import { fetchOne } from \"./fetch\";\nimport { getHook } from \"./hook\";\nimport { getProtocol } from \"./protocol\";\nimport { isFunc, then } from \"./util\";\n\n/**\n * Insert a document into a collection with hook support.\n *\n * Execution flow (sync or async depending on the active protocol):\n * 1) Run `beforeInsert` hook if defined: can validate/mutate the doc.\n * 2) Call protocol.insert(Coll, doc) to perform the insertion.\n * 3) Run `onInserted` hook if defined:\n * - If the hook requests only {_id: 1}, pass {_id} directly.\n * - Otherwise fetch the inserted document with the requested fields,\n * then pass it to the hook.\n *\n * Notes:\n * - Uses the `then` helper to normalize sync/async protocols and hooks.\n * - Does not await `onInserted` (fire-and-forget side effects).\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance to insert into.\n * @param {Object} doc - The document to insert (will be passed to beforeInsert hooks).\n * @returns {any|Promise<any>} The inserted document _id (type depends on protocol/driver).\n *\n * @example\n * // Basic usage\n * const _id = await insert(Users, { name: 'Alice', email: 'a@ex.com' });\n *\n * @example\n * // With hooks configured elsewhere\n * // beforeInsert could normalize fields; onInserted could enqueue a job\n * const userId = await insert(Users, payload);\n */\nexport function insert(Coll, doc) {\n const protocol = getProtocol();\n const beforeInsertHook = getHook(Coll, \"beforeInsert\");\n\n return then(\n // Run `beforeInsert` if present (may mutate/validate doc)\n isFunc(beforeInsertHook?.fn) && beforeInsertHook.fn(doc),\n\n () => {\n const onInsertedHook = getHook(Coll, \"onInserted\");\n\n return then(\n // Perform actual insert via protocol\n protocol.insert(Coll, doc),\n\n (_id) => {\n if (!onInsertedHook) return _id;\n\n const { fields, fn: onInserted } = onInsertedHook;\n\n // If hook only needs _id, no fetch is necessary\n const fieldKeys = fields ? Object.keys(fields) : [];\n const _idOnly = fieldKeys.length === 1 && fieldKeys[0] === \"_id\";\n\n return then(\n // Fetch inserted doc or pass {_id} directly\n _idOnly ? { _id } : fetchOne(Coll, { _id }, { fields }),\n\n (insertedDoc) => {\n // Fire-and-forget: don't await onInserted\n onInserted(insertedDoc);\n return _id;\n },\n );\n },\n );\n },\n );\n}\n","import { fetchList } from \"./fetch\";\nimport { combineFields } from \"./fields\";\nimport { getHook } from \"./hook\";\nimport { getProtocol } from \"./protocol\";\nimport { isFunc, then } from \"./util\";\n\n/**\n * Remove documents from a collection with hook support.\n *\n * Execution flow (sync or async depending on the active protocol):\n * 1) Compute the minimal fields to fetch for hooks (union of beforeRemove/onRemoved fields).\n * 2) If any hook is defined, fetch the matching documents once with those fields.\n * 3) Run `beforeRemove` hook (if present) with the array of docs.\n * 4) Call protocol.remove(Coll, selector).\n * 5) If something was removed and `onRemoved` exists, call it once per doc (fire-and-forget).\n *\n * Notes:\n * - Uses the `then` helper to normalize sync/async protocols and hooks.\n * - `onRemoved` is intentionally not awaited; it runs after remove is triggered.\n * - If no hooks are registered, no pre-fetch is performed.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance to remove from.\n * @param {Object} selector - MongoDB-style query selector.\n * @returns {number|Promise<number>} The number of removed documents (driver-dependent).\n *\n * @example\n * // Basic usage\n * const removed = await remove(Users, { inactive: true });\n *\n * @example\n * // With hooks configured elsewhere\n * // beforeRemove could check permissions; onRemoved could clear caches\n * const n = await remove(Posts, { authorId });\n */\nexport function remove(Coll, selector) {\n const protocol = getProtocol();\n\n const beforeRemoveHook = getHook(Coll, \"beforeRemove\");\n const onRemovedHook = getHook(Coll, \"onRemoved\");\n\n // Union of fields requested by before/on hooks; null means \"no hooks, no fetch\"\n const globalFields = getBeforeFields(beforeRemoveHook, onRemovedHook);\n\n return then(\n // Fetch docs only when at least one hook exists\n globalFields === null\n ? []\n : fetchList(Coll, selector, { fields: globalFields }),\n\n (docs) => {\n return then(\n // Run `beforeRemove` if defined\n isFunc(beforeRemoveHook?.fn) && beforeRemoveHook.fn(docs),\n\n () => {\n // Execute actual removal (can be sync or a Promise<number>)\n const removedCount = protocol.remove(Coll, selector);\n\n // If removal did nothing or there is no onRemoved hook, return as-is\n if (!removedCount || !isFunc(onRemovedHook?.fn)) return removedCount;\n\n // Fire-and-forget: pass each (pre-fetched) doc to onRemoved\n docs.forEach((doc) => onRemovedHook.fn(doc));\n\n return removedCount;\n },\n );\n },\n );\n}\n\n/**\n * Compute the union of the fields requested by provided hook definitions.\n *\n * Returns:\n * - null when no hooks are provided (signals \"no pre-fetch needed\")\n * - a FieldSpec (possibly undefined meaning \"all fields\") combining hook fields\n *\n * @param {...{fields?: import('./fields').FieldSpec|true|undefined}|undefined} maybeHooks\n * @returns {import('./fields').FieldSpec|true|undefined|null}\n * Combined fields, or null if there were no hooks at all.\n * @internal\n */\nfunction getBeforeFields(...maybeHooks) {\n const hookDefs = maybeHooks.filter((hookDef) => hookDef);\n if (!hookDefs.length) return null;\n\n return hookDefs.reduce(\n (fields, hookDef) => combineFields(fields, hookDef.fields),\n null,\n );\n}\n","import { fetchList } from \"./fetch\";\nimport { combineFields } from \"./fields\";\nimport { getHook } from \"./hook\";\nimport { getProtocol } from \"./protocol\";\nimport { indexById, isFunc, then } from \"./util\";\n\n/**\n * Update documents in a collection with hook support.\n *\n * Execution flow (sync or async depending on the active protocol):\n * 1) Determine the minimal fields to prefetch for hooks (union of beforeUpdate/onUpdated needs).\n * 2) If any hook exists, fetch the target documents once with those fields (limit 1 if multi=false).\n * 3) Run `beforeUpdate` hook with (docs, modifier) if present.\n * 4) Execute protocol.update(Coll, selector, modifier, options).\n * 5) If some docs were modified and `onUpdated` exists:\n * - Re-fetch affected docs by _id with `onUpdated.fields`\n * - Call `onUpdated(afterDoc, beforeDoc)` for each (fire-and-forget).\n *\n * Notes:\n * - Uses `then` helper to normalize sync/async protocols and hooks.\n * - `onUpdated` is not awaited; it is intended for side effects.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style selector to match documents.\n * @param {Object} modifier - MongoDB-style update modifier (e.g., {$set: {...}}).\n * @param {Object} [options] - Update options.\n * @param {boolean} [options.multi=true] - Update multiple documents by default.\n * @returns {number|Promise<number>} Number of modified documents (driver-dependent).\n *\n * @example\n * // Activate all pending users\n * await update(Users, { status: 'pending' }, { $set: { status: 'active' } }, { multi: true });\n *\n * @example\n * // Update a single document\n * await update(Users, { _id }, { $set: { name: 'Alice' } }, { multi: false });\n */\nexport function update(\n Coll,\n selector,\n modifier,\n {\n multi = true, // Ensure update targets multiple documents by default.\n ...restOptions\n } = {},\n) {\n const protocol = getProtocol();\n\n const beforeUpdateHook = getHook(Coll, \"beforeUpdate\");\n const onUpdatedHook = getHook(Coll, \"onUpdated\");\n\n const options = { multi, ...restOptions };\n\n // Fields to prefetch before update (null => no hooks => no prefetch)\n const fieldsBefore = getBeforeFields(beforeUpdateHook, onUpdatedHook);\n\n return then(\n /* Fetch docs only if at least one hook has been defined. */\n fieldsBefore === null\n ? []\n : fetchList(Coll, selector, {\n fields: fieldsBefore,\n limit: multi ? undefined : 1,\n }),\n (docs) => {\n return then(\n /* Execute `beforeUpdate` hook if defined */\n isFunc(beforeUpdateHook?.fn) && beforeUpdateHook.fn(docs, modifier),\n\n () => {\n return then(\n /* Execute the update */\n protocol.update(Coll, selector, modifier, options),\n\n (updatedCount) => {\n /* If update didn't work, don't execute comparators. */\n if (!updatedCount || !isFunc(onUpdatedHook?.fn))\n return updatedCount;\n\n /* Save a version of targeted docs prior to the update */\n const beforeById = indexById(docs);\n\n return then(\n /* Fetch again each targeted document by its previously saved _id */\n fetchList(\n Coll,\n { _id: { $in: Object.keys(beforeById) } },\n { fields: onUpdatedHook.fields },\n ),\n\n /* Pass the before and after versions to each comparator. */\n (afterDocs) => {\n /* Pass each after and before pairs to hook.\n * Do NOT await, should run asynchronously if protocol allows. */\n afterDocs.forEach((after) => {\n const before = beforeById[after._id];\n onUpdatedHook.fn(after, before);\n });\n\n return updatedCount;\n },\n );\n },\n );\n },\n );\n },\n );\n}\n\n/**\n * Compute fields to prefetch before the update for hooks.\n *\n * Returns:\n * - null if no hooks are defined (signals \"no prefetch needed\")\n * - the union of:\n * - beforeUpdate.fields (if any)\n * - onUpdated.fields, or {_id:1} if onUpdated.before is falsy\n *\n * @param {{fields?: import('./fields').FieldSpec|true|undefined}|undefined} beforeHook\n * @param {{fields?: import('./fields').FieldSpec|true|undefined, before?: boolean}|undefined} afterHook\n * @returns {import('./fields').FieldSpec|true|undefined|null}\n * Combined fields, or null if there are no hooks at all.\n * @internal\n */\nfunction getBeforeFields(beforeHook, afterHook) {\n /* If no hooks, return null */\n if (!beforeHook && !afterHook) return null;\n\n if (!afterHook) return beforeHook?.fields;\n\n const { fields, before } = afterHook;\n\n /* If `before` is false, documents before update are not necessary.\n * Only their ids will be fetched so after update documents\n * can be retrieved. */\n const fieldsBefore = before ? fields : { _id: 1 };\n\n if (!beforeHook) return fieldsBefore;\n\n return combineFields(beforeHook.fields, fieldsBefore);\n}\n"],"names":["DEFAULT_PROTOCOL","count","Error","cursor","findList","getTransform","insert","remove","update","protocol","getProtocol","overload","typeOf","obj","toString","call","split","slice","toLowerCase","isArr","x","Array","isArray","isFunc","isObj","isPromise","then","renameKeys","dictionnary","object","map","_ref","k","v","getPropValue","dotKey","doc","_dotKey$split","rootKey","rest","_arrayLikeToArray","rootValue","length","subDotKey","join","flatMap","subDoc","subValue","maybePromise","fn","arr","some","Promise","all","value","undefined","y","TypeError","Object","fromEntries","entries","filter","pred","KNOWN_TYPES","knownTypesCaption","joinsDictionnary","Map","joinPrefix","getJoins","Coll","get","getJoinPrefix","_excluded","normalizeFields","fields","flatten","flattenFields","root","keys","startsWith","reduce","acc","dotStrIndex","indexOf","subRoot","subRootSelection","_extends2","shouldSelect","_extends","dispatchFields","joins","_Object$keys","_","_isolateJoinFields","joinKeys","joinFields","ownFields","_objectWithoutPropertiesLoose","_toPropertyKey","_ref4","includes","_ref5","_extends3","_extends4","prev","isolateJoinFields","augmentedOwnFields","joinKey","_ref2","_joins$joinKey","on","onFields","combineFields","a","b","allFields","deepMergeFields","removeFalsyFields","out","_i","_Object$entries","_Object$entries$_i","key","valB","valA","_ref3","decrementRecursiveField","_fields$joinPrefix","_extends5","_extends6","_extends7","Infinity","decremented","fetchList","selector","options","_getProtocol","collTransform","_options$transform","transform","restOptions","enhance","_dispatchFields","_dispatchFields$","usedJoinKeys","docs","joinsByType","type","enhancedJoin","_key","concat","_joinsByType$array","array","_joinsByType$object","objJoins","_joinsByType$function","fnJoins","_docs","joinColl","single","postFetch","joinRest","_excluded2","readyDocs","fromProp","toProp","_on$","toSelector","fromArray","propList","toArray","subSelector","$elemMatch","$in","isRecursive","recursiveCount","stopRecursion","subJoinFields","own","allOwnIncluded","subFields","subOptions","limit","allJoinedDocs","indexedByToProp","joinedDoc","toPropValue","acc2","_extends8","toValueOrProp","list","toValue","joinedDocs","searchedArr","toList","el","fromValue","item","find","prevItem","raw","afterPostFetch","docsWithArrJoins","createJoinFetcher","parentFields","objJoinsEnhancers","docWithObjJoins","_doc","joinFetcher","docWithFnJoins","res","fetchOne","_ref3$join","joinLimit","_excluded3","_getProtocol2","_extends9","HOOK_TYPES","hooksRegistry","getHookDefinitions","hookType","collHooks","getHook","hookDefinitions","hookDefs","_hookDefs$reduce","hookDef","before","args","arguments","hookToHandler","apply","handler","combineHookDefinitions","unless","when","rawColl","rawCollection","countDocuments","countAsync","fetchAsync","_transform","insertAsync","removeAsync","modifier","updateAsync","multi","fetch","renamedOptions","insertOne","insertedId","_ref$multi","deleteMany","deleteOne","_res$deletedCount","deletedCount","_ref2$multi","updateMany","updateOne","_res$modifiedCount","modifiedCount","upsertedCount","_count","_id","hooksObj","forEach","hooks","_hook","nextHooks","set","addHookDefinition","beforeInsertHook","onInsertedHook","onInserted","fieldKeys","insertedDoc","Collection","_ref$","joinType","_console","console","warn","beforeRemoveHook","onRemovedHook","globalFields","getBeforeFields","removedCount","prefix","methods","_temp","beforeUpdateHook","onUpdatedHook","fieldsBefore","beforeHook","afterHook","updatedCount","beforeById","indexById","afterDocs","after","fnOrMethods"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function n(){return n=Object.assign?Object.assign.bind():function(n){for(var r=1;r<arguments.length;r++){var t=arguments[r];for(var e in t)({}).hasOwnProperty.call(t,e)&&(n[e]=t[e])}return n},n.apply(null,arguments)}function r(n,r){if(null==n)return{};var t={};for(var e in n)if({}.hasOwnProperty.call(n,e)){if(-1!==r.indexOf(e))continue;t[e]=n[e]}return t}function t(n){var r=function(n){if("object"!=typeof n||!n)return n;var r=n[Symbol.toPrimitive];if(void 0!==r){var t=r.call(n,"string");if("object"!=typeof t)return t;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(n)}(n);return"symbol"==typeof r?r:r+""}var e={count:function(){throw new Error("'count' method must be defined with 'setProtocol'.")},cursor:function(){throw new Error("'cursor' method must be defined with 'setProtocol'.")},findList:function(){throw new Error("'findList' method must be defined with 'setProtocol'.")},getTransform:function(){},insert:function(){throw new Error("'insert' method must be defined with 'setProtocol'.")},remove:function(){throw new Error("'remove' method must be defined with 'setProtocol'.")},update:function(){throw new Error("'update' method must be defined with 'setProtocol'.")}},o=e;function i(n){return n?"function"==typeof n?n(o):"object"==typeof n?n:o:o}function u(r){void 0===r&&(r={}),o=n({},e,r)}function f(r){o="function"==typeof r?r(o):n({},o,r)}var c=function(n){return{}.toString.call(n).split(" ")[1].slice(0,-1).toLowerCase()},l=function(n){return Array.isArray(n)},a=function(n){return"function"==typeof n},d=function(n){return n&&!l(n)&&"object"===c(n)};function s(n){return a(null==n?void 0:n.then)}function v(n,r){return h(function(r){var t=r[0];return[n[t]||t,r[1]]},r)}function m(n,r){var t=n.split("."),e=t[0],o=function(n,r){(null==r||r>n.length)&&(r=n.length);for(var t=0,e=Array(r);t<r;t++)e[t]=n[t];return e}(t).slice(1),i=r[e];if(o.length<1)return i;var u=o.join(".");return d(i)?m(u,i):l(i)?i.flatMap(function(n){var r=m(u,n);return l(r)?r:[r]}):i}function p(n,r){if(l(n)){var t=n;return t.some(s)?Promise.all(t).then(function(n){return r(n,!0)}):r(t,!1)}return s(n)?n.then(function(n){return r(n,!0)}):r(n,!1)}function h(n,r){if(void 0===r)return function(r){return h(n,r)};if(l(r))return r.map(n);if(!d(r))throw new TypeError("'map' only works on array or plain object");return Object.fromEntries(Object.entries(r).map(n))}function y(n,r){if(void 0===r)return function(r){return y(n,r)};if(l(r))return r.filter(n);if(!d(r))throw new TypeError("'filter' only works on array or plain object");return Object.fromEntries(Object.entries(r).filter(n))}function b(n,r){return p((0,i().count)(n,r),function(n){return n})}var w=["array","function","object"],j=w.join("', '"),_=new Map,g=null;function O(r,t){t?(Object.entries(t).forEach(function(n){var r=n[0],t=n[1],e=t.on,o=t.fields;if(!t.Coll)throw new Error("Collection 'Coll' for '"+r+"' join is required.");if(!e)throw new Error("Join '"+r+"' has no 'on' condition specified.");var i=c(e);if(!w.includes(i))throw new Error("Join '"+r+"' has an unrecognized 'on' condition type of '"+i+"'. Should be one of '"+j+"'.");a(e)&&!o&&function(){var n;console&&console.warn&&(n=console).warn.apply(n,[].slice.call(arguments))}("Join '"+r+"' is defined with a function 'on', but no 'fields' are explicitely specified. This could lead to failed joins if the keys necessary for the join are not specified at query time.")}),_.set(r,n({},_.get(r),t))):_.set(r,void 0)}function E(n){return _.get(n)||{}}function k(){return g}function C(n){g=n}var A=["+"];function T(n,r){return void 0===r&&(r=!1),d(n)?r?P(n):n:n?void 0:{}}function P(r,t){var e;if(!r)return r;var o=Object.keys(r);return o.some(function(n){return n.startsWith("$")})?t?((e={})[t]=r,e):r:o.reduce(function(e,o){var i=o.indexOf(".");if(i>=0){var u=o.slice(0,i),f=r[u];if(f&&!d(f))return e}var c,l=r[o],a=t?[t,o].join("."):o;return d(l)?n({},e,P(l,a)):n({},e,((c={})[a]=!!l,c))},void 0)}function M(e,o){var i;if(void 0===o&&(o={}),!d(e))return{_:T(e)};var u=function(e,o){void 0===o&&(o={});var i=Object.keys(o),u=k();if(u){var f=e[u],c=r(e,[u].map(t));return n(f?{"+":y(function(n){return i.includes(n[0])},f)}:{"+":void 0},c)}return Object.entries(e).reduce(function(r,t){var e,o,u=t[0],f=t[1];return i.includes(u)?n({},r,{"+":n({},r["+"]||{},(o={},o[u]=f,o))}):n({},r,((e={})[u]=f,e))},{})}(e,o),f=u["+"],c=r(u,A);if(!f)return{_:T(c,!0),"+":void 0};if(!c||null==(i=Object.keys(c))||!i.length)return{_:T(c,!0),"+":f};var l=Object.keys(f).reduce(function(r,t){var e,i=o[t],u=i.on,f=i.fields,c=Array.isArray(u)?((e={})[u[0]]=1,e):void 0;return c||f?n({},r,c,f):r},c);return{_:T(l,!0),"+":f}}function S(n,r){if(!L(n)&&!L(r))return n||r?F(I(n),I(r)):{}}function F(r,t){void 0===r&&(r={}),void 0===t&&(t={});for(var e=n({},r),o=0,i=Object.entries(t||{});o<i.length;o++){var u=i[o],f=u[0],c=u[1],l=e[f];e[f]=d(l)&&d(c)?F(l,c):c}return e}function L(n){return void 0===n||!!n&&!d(n)}function I(n){return d(n)?y(function(n){return n[1]},n):{}}function x(r,t){var e,o,i,u;if(!d(t))return t;var f=k(),c=f?null==(e=t[f])?void 0:e[r]:t[r];if("number"!=typeof c)return t;if(Infinity===c)return t;var l=c-1;return n({},t,f?((u={})[f]=n({},t[f],((i={})[r]=l,i)),u):((o={})[r]=l,o))}var $=["fields","transform"],R=["_key","Coll","on","single","postFetch","limit"],U=["_key","Coll","on","single","postFetch","limit"];function D(t,e,o){void 0===e&&(e={}),void 0===o&&(o={});var u=i(),f=u.count,s=u.findList,v=u.getTransform,h=E(t),y=v(t),b=o.fields,w=o.transform,j=void 0===w?y:w,_=r(o,$),g=function(n){return a(j)?j(n):n},O=M(b,h),k=O._,C=O["+"],A=void 0===C?{}:C,P=Object.keys(A);return!h||null==P||!P.length||b&&!d(b)?p(s(t,e,n({},_,{fields:k,transform:null})),function(n){return n.map(g)}):p(s(t,e,n({},_,{fields:k,transform:null})),function(e){var i=P.reduce(function(r,t){var e,o=h[t];if(!o)return r;var i=c(o.on),u=n({},o,{_key:t});return n({},r,((e={})[i]=[].concat(r[i]||[],[u]),e))},{}),u=i.array,s=i.object,v=void 0===s?[]:s,y=i.function,w=void 0===y?[]:y;return p((void 0===u?[]:u).reduce(function(e,i){var u=i._key,s=i.Coll,v=i.on,h=i.single,y=i.postFetch,w=r(i,R);return p(e,function(r){var e,i,_=v[0],g=v[1],O=v[2],k=void 0===O?{}:O,C=l(_),P=C?r.flatMap(function(n){return n[_[0]]}):r.map(function(n){return n[_]}),S=l(g),F=n({},k,S?((e={})[g[0]]={$elemMatch:{$in:P}},e):((i={})[g]={$in:P},i)),L=s===t&&A[u]>1;return p(L&&f(t,F),function(t){var e,i=L&&!t,f=L?x(u,b):A[u],v=M(f,E(s)||{})._,O=!v||Object.keys(v).length<=0,k=d(f)&&!O&&"_id"!==g?n({},f,((e={})[g]=1,e)):f,P=n({},o,w,{fields:T(k),limit:void 0,transform:L?j:void 0});return p(i?[]:D(s,F,P),function(t){var e=S?{}:t.reduce(function(r,t){var e,o=m(g,t);return l(o)?o.reduce(function(r,e){var o;return n({},r,((o={})[e]=[].concat(r[e]||[],[t]),o))},r):n({},r,((e={})[o]=[].concat(r[o]||[],[t]),e))},{});return r.map(function(r){var o,i,f,l,d=[];S?d=t.filter(function(n){var t,e=n[g[0]]||[];return C?(t=e,(r[_[0]]||[]).some(function(n){return t.indexOf(n)>=0})):e.includes(r[_])}):C?(i="_id",f=(r[_[0]]||[]).flatMap(function(n){return e[n]||[]}),l=a(i)?i:"string"===c(i)?function(n){return n[i]}:function(n){return n},d=f.reduce(function(n,r){var t=l(r);return n.find(function(n){return l(n)===t})?n:[].concat(n,[r])},[])):d=e[r[_]]||[];var s=h?d[0]:d,v=a(y)?y(s,r):s;return n({},r,((o={})[u]=v,o))})})})})},e),function(n){return p(v.map(function(n){return W({Coll:t,join:n,fields:A[n._key],subSelector:n.on,options:_,parentFields:b})}),function(r){return p(n.map(function(n){var e=r.reduce(function(n,r){return r(n)},n);return p(w.reduce(function(r,e){var o=e.on;return p([r,W({Coll:t,join:e,fields:A[e._key],subSelector:a(o)?o(n):o,options:_,parentFields:b})],function(n){return(0,n[1])(n[0])})},e),function(n){return g(n)})}),function(n){return n})})})})}function J(r,t,e){return void 0===e&&(e={}),p(D(r,t,n({},e,{limit:1})),function(n){return n[0]})}function q(r,t,e){return p(D(r,t,n({},e,{fields:{_id:1}})),function(n){return n.map(function(n){return n._id})})}function z(n,r){return p(J(n,r,{fields:{_id:1}}),function(n){return!!n})}function W(t){var e=t.Coll,o=t.join,u=o._key,f=o.Coll,c=o.single,l=o.postFetch,d=o.limit,s=r(o,U),v=t.fields,m=t.subSelector,h=t.options,y=t.parentFields,b=i(),w=f===e;return p(w&&(0,b.count)(e,m),function(r){var t=w&&(!v||!r),e=w?x(u,y):v,o=n({},h,s,{fields:T(e),limit:c?1:d||void 0});return p(t?[]:D(f,m,o),function(r){return function(t){var e,o=c?r[0]:r,i=a(l)?l(o,t):o;return n({},t,((e={})[u]=i,e))}})})}var B=["beforeInsert","beforeUpdate","beforeRemove","onInserted","onUpdated","onRemoved"],G=new Map;function H(r,t){Object.entries(t).forEach(function(t){var e=t[0],o=t[1];if(!l(o))throw new TypeError("'"+e+"' hooks must be an array");o.forEach(function(t){return function(r,t,e){var o;if(!B.includes(t))throw new TypeError("'"+t+"' is not a valid hook type");if(!a(null==e?void 0:e.fn))throw new TypeError("'hook' must be a function or contain a 'fn' key");var i=K(r),u=[].concat(i[t]||[],[e]);G.set(r,n({},i,((o={})[t]=u,o)))}(r,e,t)})})}function K(n,r){var t=G.get(n)||{};return r?t[r]:t}function N(n,r){var t=K(n,r);if(t)return function(n){if(void 0===n&&(n=[]),n.length){var r=n.reduce(function(n,r){return{fields:S(n.fields,r.fields),before:n.before||r.before}},{fields:null,before:void 0});return{fields:r.fields,fn:function(){var r=[].slice.call(arguments);return p(n.map(function(n){return Q.apply(void 0,[n].concat(r))}).filter(a).map(function(n){return n.apply(void 0,r)}),function(n){return n})},before:r.before}}}(t)}function Q(n){var r=n.fn,t=n.unless,e=n.when,o=[].slice.call(arguments,1);if(!(null==t?void 0:t.apply(void 0,o))&&(!e||e.apply(void 0,o)))return r}function V(n,r){var t=i(),e=N(n,"beforeInsert");return p(a(null==e?void 0:e.fn)&&e.fn(r),function(){var e=N(n,"onInserted");return p(t.insert(n,r),function(r){if(!e)return r;var t=e.fields,o=e.fn,i=t?Object.keys(t):[];return p(1===i.length&&"_id"===i[0]?{_id:r}:J(n,{_id:r},{fields:t}),function(n){return o(n),r})})})}function X(n,r){var t=i(),e=N(n,"beforeRemove"),o=N(n,"onRemoved"),u=function(){var n=[].slice.call(arguments).filter(function(n){return n});return n.length?n.reduce(function(n,r){return S(n,r.fields)},null):null}(e,o);return p(null===u?[]:D(n,r,{fields:u}),function(i){return p(a(null==e?void 0:e.fn)&&e.fn(i),function(){var e=t.remove(n,r);return e&&a(null==o?void 0:o.fn)?(i.forEach(function(n){return o.fn(n)}),e):e})})}var Y=["multi"];function Z(t,e,o,u){var f=void 0===u?{}:u,c=f.multi,l=void 0===c||c,d=r(f,Y),s=i(),v=N(t,"beforeUpdate"),m=N(t,"onUpdated"),h=n({multi:l},d),y=function(n,r){if(!n&&!r)return null;if(!r)return null==n?void 0:n.fields;var t=r.before?r.fields:{_id:1};return n?S(n.fields,t):t}(v,m);return p(null===y?[]:D(t,e,{fields:y,limit:l?void 0:1}),function(n){return p(a(null==v?void 0:v.fn)&&v.fn(n,o),function(){return p(s.update(t,e,o,h),function(r){if(!r||!a(null==m?void 0:m.fn))return r;var e=function(n){return void 0===n&&(n=[]),Object.fromEntries(n.map(function(n){return[n._id,n]}))}(n);return p(D(t,{_id:{$in:Object.keys(e)}},{fields:m.fields}),function(n){return n.forEach(function(n){m.fn(n,e[n._id])}),r})})})})}var nn=["multi"],rn=["multi"],tn={__proto__:null,meteorAsync:{count:function(n,r,t){var e=n.rawCollection();return e.countDocuments?e.countDocuments(r):n.find(r,t).countAsync()},cursor:function(n,r,t){return n.find(r,t)},findList:function(n,r,t){return n.find(r,t).fetchAsync()},getTransform:function(n){return n._transform},insert:function(n,r){return n.insertAsync(r)},remove:function(n,r){return n.removeAsync(r)},update:function(r,t,e,o){return r.updateAsync(t,e,n({multi:!0},o))}},meteorSync:{count:function(n,r,t){return n.find(r,t).count()},cursor:function(n,r,t){return n.find(r,t)},findList:function(n,r,t){return n.find(r,t).fetch()},getTransform:function(n){return n._transform},insert:function(n,r){return n.insert(r)},remove:function(n,r){return n.remove(r)},update:function(r,t,e,o){return r.update(t,e,n({multi:!0},o))}},node:{count:function(n,r,t){return void 0===r&&(r={}),void 0===t&&(t={}),n.countDocuments(r||{},t)},cursor:function(n,r,t){void 0===r&&(r={}),void 0===t&&(t={});var e=v({fields:"projection"},t||{});return n.find(r||{},e)},findList:function(n,r,t){void 0===r&&(r={}),void 0===t&&(t={});var e=v({fields:"projection"},t||{});return n.find(r||{},e).toArray()},getTransform:function(n){return"function"==typeof(null==n?void 0:n.transform)?n.transform:void 0},insert:function(n,r,t){return n.insertOne(r,t).then(function(n){return null==n?void 0:n.insertedId})},remove:function(n,t,e){void 0===t&&(t={}),void 0===e&&(e={});var o=e||{},i=o.multi,u=void 0===i||i,f=r(o,nn);return(u?n.deleteMany(t||{},f):n.deleteOne(t||{},f)).then(function(n){var r;return null!=(r=null==n?void 0:n.deletedCount)?r:0})},update:function(n,t,e,o){void 0===t&&(t={}),void 0===e&&(e={}),void 0===o&&(o={});var i=o||{},u=i.multi,f=void 0===u||u,c=r(i,rn);return(f?n.updateMany(t||{},e||{},c):n.updateOne(t||{},e||{},c)).then(function(n){var r,t;return null!=(r=null!=(t=null==n?void 0:n.modifiedCount)?t:null==n?void 0:n.upsertedCount)?r:0})}}};export{b as count,z as exists,q as fetchIds,D as fetchList,J as fetchOne,P as flattenFields,k as getJoinPrefix,E as getJoins,i as getProtocol,H as hook,V as insert,O as join,tn as protocols,X as remove,C as setJoinPrefix,u as setProtocol,Z as update,f as updateProtocol};
|
|
2
|
+
//# sourceMappingURL=coll-fns.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coll-fns.mjs","sources":["../src/protocol.js","../src/util.js","../src/count.js","../src/join.js","../src/fields.js","../src/fetch.js","../src/hook.js","../src/insert.js","../src/remove.js","../src/update.js","../src/protocols/meteorAsync.js","../src/protocols/meteorSync.js","../src/protocols/node.js"],"sourcesContent":["/**\n * Protocol interface implemented by adapters (Meteor, Node, etc.).\n * All methods may return synchronously or as a Promise.\n *\n * @typedef {Object} Protocol\n * @property {(Coll:any, selector?:Object, options?:Object) => number|Promise<number>} count\n * Count documents matching selector.\n * @property {(Coll:any, selector?:Object, options?:Object) => any} cursor\n * Return a driver-specific cursor/iterator for advanced usage.\n * @property {(Coll:any, selector?:Object, options?:Object) => Array|Promise<Array>} findList\n * Return an array of documents for selector/options.\n * @property {(Coll:any) => ((doc:any)=>any)|undefined} getTransform\n * Optional per-collection transform applied to each fetched document.\n * @property {(Coll:any, doc:Object, options?:Object) => any|Promise<any>} insert\n * Insert a document and return the inserted _id (or driver-specific result).\n * @property {(Coll:any, selector:Object, options?:Object) => number|Promise<number>} remove\n * Remove matching documents and return the number removed.\n * @property {(Coll:any, selector:Object, modifier:Object, options?:Object) => number|Promise<number>} update\n * Update matching documents and return the number modified.\n */\n\n/**\n * Default protocol that throws for unimplemented methods.\n * Adapters should be provided via setProtocol to override these.\n * @type {Protocol}\n * @internal\n */\nconst DEFAULT_PROTOCOL = {\n count(/* Coll, selector = {}, options = {} */) {\n throw new Error(`'count' method must be defined with 'setProtocol'.`);\n },\n\n cursor(/* Coll, selector = {}, options = {} */) {\n throw new Error(`'cursor' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that takes a collection, selector and options\n * and returns a list of documents. */\n findList(/* Coll, selector = {}, options = {} */) {\n throw new Error(`'findList' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that transforms each document defined at the collection level. */\n getTransform(/* Coll */) {\n return undefined;\n },\n\n /* A function that inserts a doc in a collection and returns the inserted _id. */\n insert(/* Coll, doc, options */) {\n throw new Error(`'insert' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that removes docs in a collection and returns the number of removed documents. */\n remove(/* Coll, selector, options */) {\n throw new Error(`'remove' method must be defined with 'setProtocol'.`);\n },\n\n /* A function that updates docs in a collection and returns the number of modified documents. */\n update(/* Coll, selector, modifier, options */) {\n throw new Error(`'update' method must be defined with 'setProtocol'.`);\n },\n};\n\n/**\n * The active protocol used by all high-level operations.\n * Initialized with DEFAULT_PROTOCOL and overridden via setProtocol/updateProtocol.\n * @type {Protocol}\n * @internal\n */\nlet protocol = DEFAULT_PROTOCOL;\n\n/**\n * Get the current protocol or a derived view of it.\n *\n * Usage:\n * - getProtocol() -> returns the active protocol object.\n * - getProtocol((p)=>wrap(p)) -> returns the result of calling your function with the active protocol.\n * - getProtocol(customObj) -> returns customObj (handy for inline overrides).\n *\n * @template T\n * @param {((p:Protocol)=>T)|Partial<Protocol>|undefined} overload\n * @returns {Protocol|T|Partial<Protocol>} The active protocol or the provided overload output.\n *\n * @example\n * // Get the active protocol\n * const p = getProtocol();\n *\n * @example\n * // Temporarily call a wrapped version without mutating global state\n * const wrapped = getProtocol((p) => ({ ...p, findList: (...args) => audit(p.findList(...args)) }));\n *\n * @example\n * // Provide a one-off protocol-like object\n * const custom = getProtocol({ findList: () => [] });\n */\nexport function getProtocol(overload) {\n if (!overload) return protocol;\n if (typeof overload === \"function\") return overload(protocol);\n if (typeof overload === \"object\") return overload;\n return protocol;\n}\n\n/**\n * Set the active protocol by merging user methods over DEFAULT_PROTOCOL.\n * Ensures all required methods exist (missing ones will still throw).\n *\n * @param {Partial<Protocol>} [methods={}] Implementation methods to install.\n * @example\n * import meteorAsync from './protocols/meteorAsync';\n * setProtocol(meteorAsync);\n */\nexport function setProtocol(methods = {}) {\n protocol = { ...DEFAULT_PROTOCOL, ...methods };\n}\n\n/**\n * Mutate the current protocol.\n * - If passed a function, it receives the current protocol and must return the next protocol.\n * - If passed a partial object, it shallow-merges over the current protocol.\n *\n * @param {((p:Protocol)=>Protocol)|Partial<Protocol>} fnOrMethods\n * @example\n * // Merge additional methods\n * updateProtocol({ getTransform: () => (d) => d });\n *\n * @example\n * // Wrap existing behavior\n * updateProtocol((p) => ({ ...p, count: (...args) => withMetrics('count', () => p.count(...args)) }));\n */\nexport function updateProtocol(fnOrMethods) {\n protocol =\n typeof fnOrMethods === \"function\"\n ? fnOrMethods(protocol)\n : { ...protocol, ...fnOrMethods };\n}\n","/**\n * Return a lowercase JS runtime type string for a value.\n * Examples: 'object', 'array', 'string', 'number', 'function', 'date', 'null', 'undefined'\n * @param {*} obj\n * @returns {string}\n */\nexport const typeOf = (obj) =>\n ({}).toString.call(obj).split(\" \")[1].slice(0, -1).toLowerCase();\n\n/**\n * Check if a value is an array.\n * @param {*} x\n * @returns {x is any[]}\n */\nexport const isArr = (x) => Array.isArray(x);\n\n/**\n * Check if an array is empty (treats undefined as empty).\n * @param {any[]} [x=[]]\n * @returns {boolean}\n */\nexport const isEmpty = (x = []) => x.length <= 0;\n\n/**\n * Check if a value is a function.\n * @param {*} x\n * @returns {x is Function}\n */\nexport const isFunc = (x) => typeof x === \"function\";\n\n/**\n * Check if a value is null or undefined.\n * @param {*} x\n * @returns {x is null|undefined}\n */\nexport const isNil = (x) => [null, undefined].includes(x);\n\n/**\n * Check if a value is a plain object (not an array, not null).\n * @param {*} x\n * @returns {x is Record<string, any>}\n */\nexport const isObj = (x) => x && !isArr(x) && typeOf(x) === \"object\";\n\n/**\n * Check if a value is a Promise-like (has a then function).\n * @param {*} x\n * @returns {x is Promise<any>}\n */\nexport function isPromise(x) {\n return isFunc(x?.then);\n}\n\n/**\n * Heuristic: value is a selector if it's a plain object or a string (id).\n * @param {*} x\n * @returns {boolean}\n */\nexport const isSelector = (x) => isObj(x) || typeOf(x) === \"string\";\n\n/**\n * Check if a value is a MongoDB-style modifier object with at least one key.\n * @param {*} x\n * @returns {boolean}\n */\nexport const isModifier = (x) => isObj(x) && !isEmpty(Object.keys(x));\n\n/**\n * Extract unique first-level field names touched by a modifier's nested paths.\n * Example: { $set: { 'profile.name': 'A', age: 1 } } -> ['profile', 'age']\n * @param {Record<string, any>} modifier\n * @returns {string[]}\n */\nexport function get2ndLevelFields(modifier) {\n if (!isModifier(modifier)) return [];\n return Object.values(modifier).flatMap((fieldsMap = {}) => {\n if (!isObj(fieldsMap)) return [];\n return [\n ...new Set(\n Object.keys(fieldsMap).map((key) => {\n const [rootKey] = key.split(\".\");\n return rootKey;\n }),\n ),\n ];\n });\n}\n\n/**\n * Shallow-merge two objects by merging their second-level props.\n * For each top-level key: out[key] = { ...prev[key], ...added[key] }\n * @template T extends Record<string, any>, U extends Record<string, any>\n * @param {T} prev\n * @param {U} added\n * @returns {Record<string, any>}\n */\nexport function assign(prev, added) {\n const allKeys = [...new Set([...Object.keys(prev), ...Object.keys(added)])];\n return allKeys.reduce(\n (acc, key) => ({ ...acc, [key]: { ...prev[key], ...added[key] } }),\n {},\n );\n}\n\n/**\n * Return all items from toKeep that are not present in toRemove (by strict equality).\n * @template T\n * @param {T[]} toKeep\n * @param {T[]} toRemove\n * @returns {T[]}\n */\nexport function difference(toKeep, toRemove) {\n return toKeep.filter((item) => toRemove.indexOf(item) < 0);\n}\n\n/**\n * Check whether sourceArr contains any of the values in searchedArr.\n * @template T\n * @param {T[]} searchedArr\n * @param {T[]} sourceArr\n * @returns {boolean}\n */\nexport function includesSome(searchedArr, sourceArr) {\n return sourceArr.some((el) => searchedArr.indexOf(el) >= 0);\n}\n\n/**\n * Rename object keys using a dictionary mapping.\n * Keys not present in the dictionary are preserved.\n * @param {Record<string, string>} dictionnary\n * @param {Record<string, any>} object\n * @returns {Record<string, any>}\n */\nexport function renameKeys(dictionnary, object) {\n return map(([k, v]) => [dictionnary[k] || k, v], object);\n}\n\n/**\n * Union of two arrays preserving order and removing duplicates.\n * @template T\n * @param {T[]} arr1\n * @param {T[]} arr2\n * @returns {T[]}\n */\nexport function union(arr1, arr2) {\n const itemsToAdd = arr2.filter((item) => arr1.indexOf(item) < 0);\n return [...arr1, ...itemsToAdd];\n}\n\n/**\n * Keep only unique items from a list by converting each item to a comparable value.\n * - If toValueOrProp is a function, it's used to compute the comparable value.\n * - If it's a string, it's treated as a property name to read from each item.\n * - Otherwise, item identity is used.\n * @template T\n * @param {((x: T) => any)|string} toValueOrProp\n * @param {T[]} list\n * @returns {T[]}\n */\nexport function uniqueBy(toValueOrProp, list) {\n const toValue = isFunc(toValueOrProp)\n ? toValueOrProp\n : typeOf(toValueOrProp) === \"string\"\n ? (x) => x[toValueOrProp]\n : (x) => x;\n\n return list.reduce((acc, item) => {\n const value = toValue(item);\n const exists = acc.find((prevItem) => toValue(prevItem) === value);\n return exists ? acc : [...acc, item];\n }, []);\n}\n\n/* eslint-disable no-console */\n/**\n * Thin wrapper around console.warn (safe in environments without console).\n * @param {...any} args\n */\nexport const warn = (...args) =>\n console && console.warn && console.warn(...args);\n/* eslint-enable no-console */\n\n/**\n * Concatenate two arrays and remove duplicate values (strict equality).\n * @template T\n * @param {T[]} arr1\n * @param {T[]} arr2\n * @returns {T[]}\n */\nexport function combineNoDuplicates(arr1, arr2) {\n return [...arr1, ...arr2].reduce(\n (acc, el) => (acc.includes(el) ? acc : [...acc, el]),\n [],\n );\n}\n\n/**\n * Build an object keyed by _id from an array of documents.\n * @template T extends { _id: string }\n * @param {T[]} [docs=[]]\n * @returns {Record<string, T>}\n */\nexport function indexById(docs = []) {\n return Object.fromEntries(docs.map((doc) => [doc._id, doc]));\n}\n\n/**\n * Get a (possibly nested) property value using dot-notation.\n * If an intermediate value is an array, returns a flattened array of sub-values.\n * Examples:\n * - getPropValue('a.b', { a: { b: 1 } }) -> 1\n * - getPropValue('a.b', { a: [{ b: 1 }, { b: 2 }] }) -> [1,2]\n * @param {string} dotKey\n * @param {Record<string, any>} doc\n * @returns {any}\n */\nexport function getPropValue(dotKey, doc) {\n const [rootKey, ...rest] = dotKey.split(\".\");\n const rootValue = doc[rootKey];\n\n if (rest.length < 1) return rootValue;\n\n const subDotKey = rest.join(\".\");\n if (isObj(rootValue)) return getPropValue(subDotKey, rootValue);\n if (isArr(rootValue))\n return rootValue.flatMap((subDoc) => {\n const subValue = getPropValue(subDotKey, subDoc);\n return isArr(subValue) ? subValue : [subValue];\n });\n return rootValue;\n}\n\n/**\n * Universal \"then\" helper that works with:\n * - Promises (awaits then applies fn(value, true))\n * - Arrays: if any element is a Promise, waits Promise.all, else passes array as-is\n * - Plain values: calls fn(value, false)\n *\n * This allows writing sync/async-agnostic code paths.\n *\n * @template T, R\n * @param {T|Promise<T>|Array<any>} maybePromise\n * @param {(value: any, isAsync: boolean) => R|Promise<R>} fn\n * @returns {R|Promise<R>}\n */\nexport function then(maybePromise, fn) {\n /* If `maybePromise` is an array, check if ANY element is a promise. */\n if (isArr(maybePromise)) {\n const arr = maybePromise;\n if (arr.some(isPromise)) {\n return Promise.all(arr).then((value) => fn(value, true));\n }\n\n return fn(arr, false);\n }\n\n if (isPromise(maybePromise))\n return maybePromise.then((value) => fn(value, true));\n\n return fn(maybePromise, false);\n}\n\n/**\n * Map over arrays or objects.\n * - If x is an array, behaves like Array.prototype.map(fn).\n * - If x is a plain object, maps over Object.entries and rebuilds an object from returned [k, v] tuples.\n * - If x is omitted, returns a curried function waiting for the collection.\n *\n * For objects, fn receives ([key, value]) and must return [nextKey, nextValue].\n *\n * @template T, U\n * @param {(value: any, index?: number) => any} fn\n * @param {T[]|Record<string, any>} [x]\n * @returns {U[]|Record<string, any>|((x: any)=>any)}\n * @throws {TypeError} If x is neither an array nor a plain object.\n */\nexport function map(fn, x) {\n /* Return a curried function if object is undefined. */\n if (x === undefined) return (y) => map(fn, y);\n\n /* If object is an array, dispatch to native method. */\n if (isArr(x)) return x.map(fn);\n\n /* Ensure plain object */\n if (!isObj(x))\n throw new TypeError(`'map' only works on array or plain object`);\n\n /* If not an array, assume a plain object.\n * If not so, will throw an error. */\n return Object.fromEntries(Object.entries(x).map(fn));\n}\n\n/**\n * Filter arrays or objects.\n * - If x is an array, behaves like Array.prototype.filter(pred).\n * - If x is a plain object, filters Object.entries and rebuilds an object from kept entries.\n * - If x is omitted, returns a curried function waiting for the collection.\n *\n * For objects, pred receives ([key, value]) and must return a boolean.\n *\n * @template T\n * @param {(value: any, index?: number) => boolean} pred\n * @param {T[]|Record<string, any>} [x]\n * @returns {T[]|Record<string, any>|((x: any)=>any)}\n * @throws {TypeError} If x is neither an array nor a plain object.\n */\nexport function filter(pred, x) {\n /* Return a curried function if object is undefined. */\n if (x === undefined) return (y) => filter(pred, y);\n\n /* If object is an array, dispatch to native method. */\n if (isArr(x)) return x.filter(pred);\n\n /* Ensure plain object */\n if (!isObj(x))\n throw new TypeError(`'filter' only works on array or plain object`);\n\n /* If not an array, assume a plain object.\n * If not so, will throw an error. */\n return Object.fromEntries(Object.entries(x).filter(pred));\n}\n","import { getProtocol } from \"./protocol\";\nimport { then } from \"./util\";\n\n/**\n * Count documents in a collection matching the selector.\n *\n * Works with both synchronous and asynchronous protocols.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance to count documents in.\n * @param {Object} selector - MongoDB-style query selector (e.g., { status: 'active' }).\n * @returns {number|Promise<number>} The number of matching documents.\n *\n * @example\n * // Count all active users\n * const active = await count(UsersCollection, { status: 'active' });\n *\n * @example\n * // Count all documents\n * const total = await count(UsersCollection, {});\n */\nexport function count(Coll, selector) {\n const { count: _count } = getProtocol();\n\n // Normalize sync/async protocol result to a Promise-like flow\n return then(_count(Coll, selector), (res) => res);\n}\n","import { isFunc, typeOf, warn } from \"./util\";\n\n/**\n * Allowed runtime types for the `on` property in a join definition.\n * - 'array' => [fromProp, toProp, toSelector?]\n * - 'function' => (doc) => selector\n * - 'object' => static selector object\n * @type {Array<'array'|'function'|'object'>}\n * @internal\n */\nconst KNOWN_TYPES = [\"array\", \"function\", \"object\"];\nconst knownTypesCaption = KNOWN_TYPES.join(\"', '\");\n\n/**\n * Global registry of join definitions per collection instance.\n * Map<Collection, Record<string, JoinDef>>\n * @type {Map<*, Record<string, JoinDef>|undefined>}\n * @internal\n */\nlet joinsDictionnary = new Map();\n\n/**\n * Optional prefix used to distinguish join fields within the `fields` option.\n * If set, join fields must be specified under this prefix, e.g.:\n * { fields: { [joinPrefix]: { author: 1, comments: { user: 1 } } } }\n * If falsy, join fields may be mixed with base fields and detected by their names.\n * @type {string|null}\n */\nlet joinPrefix = null;\n\n/**\n * @typedef {[*] | [string, string] | [string, string, Object]} JoinArrayOn\n * Array form describing relation:\n * - [fromProp, toProp] or [fromProp, toProp, toSelector]\n * fromProp: field on parent doc (string or ['field'] to denote array-valued)\n * toProp: field on joined doc (string or ['field'] to denote array-valued)\n * toSelector: optional additional selector for joined docs\n */\n\n/**\n * @typedef {(doc: any) => Object} JoinFunctionOn\n * Function form: receives the parent document and returns a selector\n * for fetching the related documents from the joined collection.\n */\n\n/**\n * @typedef {Object} JoinObjectOn\n * Static selector object applied to the joined collection.\n */\n\n/**\n * @typedef {JoinArrayOn | JoinFunctionOn | JoinObjectOn} JoinOn\n */\n\n/**\n * @typedef {Object} JoinDef\n * @property {*} Coll - The target collection to join with.\n * @property {JoinOn} on - Relation description (array/function/object).\n * @property {boolean} [single] - If true, attach a single document instead of an array.\n * @property {(joined: any[]|any, parent: any) => any} [postFetch] - Transform the joined value before attaching.\n * @property {Object} [fields] - Base collection fields required to perform the join when `on` is a function.\n * @property {number} [limit] - Limit for the joined fetch (applies when not single).\n * @property {any} [options] - Any extra options passed through to the underlying fetch/find implementation.\n */\n\n/**\n * Register/augment join definitions for a collection.\n * Validates join shapes and emits warnings for potentially unsafe definitions.\n *\n * Notes:\n * - Calling with `joins` falsy clears existing joins for the collection.\n * - If `on` is a function and no `fields` are declared for the join, a warning is emitted,\n * because required linking keys may not be fetched unless explicitly requested.\n *\n * @template TColl\n * @param {TColl} Collection - The collection instance to attach joins to.\n * @param {Record<string, JoinDef>|undefined|null|false} joins - Map of joinKey -> join definition.\n * @example\n * join(Posts, {\n * author: {\n * Coll: Users,\n * on: ['authorId', '_id'],\n * single: true,\n * postFetch(author, post) { return author && { _id: author._id, name: author.name }; }\n * },\n * comments: {\n * Coll: Comments,\n * on: ['_id', 'postId'],\n * limit: 50\n * }\n * });\n */\nexport function join(Collection, joins) {\n if (!joins) {\n // Explicitly set to undefined to signal no joins for this collection\n joinsDictionnary.set(Collection, undefined);\n return;\n }\n\n Object.entries(joins).forEach(([key, { Coll, on, fields }]) => {\n if (!Coll) {\n throw new Error(`Collection 'Coll' for '${key}' join is required.`);\n }\n\n if (!on) {\n throw new Error(`Join '${key}' has no 'on' condition specified.`);\n }\n\n const joinType = typeOf(on);\n if (!KNOWN_TYPES.includes(joinType)) {\n throw new Error(\n `Join '${key}' has an unrecognized 'on' condition type of '${joinType}'. Should be one of '${knownTypesCaption}'.`,\n );\n }\n\n // When on is a function, the join likely depends on keys from the parent doc.\n // Encourage declaring the base fields required so callers don't forget them.\n if (isFunc(on) && !fields) {\n warn(\n `Join '${key}' is defined with a function 'on', but no 'fields' are explicitely specified. This could lead to failed joins if the keys necessary for the join are not specified at query time.`,\n );\n }\n });\n\n // Merge new join defs with existing ones on this collection\n joinsDictionnary.set(Collection, {\n ...joinsDictionnary.get(Collection),\n ...joins,\n });\n}\n\n/**\n * Retrieve declared joins for a collection.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @returns {Record<string, JoinDef>} The join definitions keyed by join name.\n */\nexport function getJoins(Coll) {\n return joinsDictionnary.get(Coll) || {};\n}\n\n/**\n * Get the currently configured join fields prefix used in field projections.\n * If set, join fields should be nested under this key within `fields`.\n * @returns {string|null} The prefix (e.g., '+') or null if not set.\n */\nexport function getJoinPrefix() {\n return joinPrefix;\n}\n\n/**\n * Set the join fields prefix used in field projections.\n * Pass a falsy value (e.g., null) to disable the prefix behavior.\n *\n * @param {string|null} prefix - Prefix symbol/key (e.g., '+') or null to disable.\n * @example\n * setJoinPrefix('+');\n * // later in queries:\n * // fetch(Posts, {}, { fields: { '+': { author: 1, comments: 1 }, title: 1 } })\n */\nexport function setJoinPrefix(prefix) {\n joinPrefix = prefix;\n}\n","import { getJoinPrefix } from \"./join\";\nimport { filter, isObj } from \"./util\";\n\n/**\n * @typedef {Record<string, (0|1|boolean|FieldSpec)>} FieldSpec\n * A MongoDB-like field projection where:\n * - keys are field paths (may be nested objects or dot-notation)\n * - values are 1/true to include, 0/false to exclude, or nested FieldSpec\n *\n * Special handling:\n * - join fields may be grouped under the join prefix (e.g. '+') when set via getJoinPrefix()\n */\n\n/**\n * Normalize a field projection for downstream usage.\n * - If fields is not a plain object: returns undefined when truthy (meaning \"select all\"),\n * or {} when falsy (meaning \"select none\").\n * - When flatten=true, converts nested objects to dot-notation MongoDB projection.\n *\n * @param {FieldSpec|undefined|null|false} fields - Field projection to normalize.\n * @param {boolean} [flatten=false] - Whether to flatten nested fields to dot-notation.\n * @returns {Record<string, boolean>|undefined} Normalized projection, {} (none), or undefined (all).\n */\nexport function normalizeFields(fields, flatten = false) {\n if (!isObj(fields)) return fields ? undefined : {};\n if (!flatten) return fields;\n // Flatten nested objects to dot-notation\n return flattenFields(fields);\n}\n\n/**\n * Flatten a general field specifiers object (which could include nested objects)\n * into a MongoDB-compatible one that uses dot-notation.\n * See: https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/#projection\n *\n * Notes:\n * - If a key starts with '$' (e.g., $elemMatch), the subtree is preserved as-is.\n * - Avoids path collisions by omitting dot-notation keys when their sub-root is already selected.\n *\n * @param {FieldSpec|undefined} fields - Nested projection to flatten.\n * @param {string} [root] - Internal accumulator for the current path.\n * @returns {Record<string, boolean>|FieldSpec|undefined} Dot-notation projection or original structure for $-keys.\n */\nexport function flattenFields(fields, root) {\n if (!fields) return fields;\n\n const keys = Object.keys(fields);\n\n // Do not flatten when a $-operator exists (e.g., { $elemMatch: ... })\n if (keys.some((k) => k.startsWith(\"$\")))\n return root ? { [root]: fields } : fields;\n\n return keys.reduce((acc, k) => {\n // Prevent path collisions when dot-notation key is under an already selected sub-root\n const dotStrIndex = k.indexOf(\".\");\n if (dotStrIndex >= 0) {\n const subRoot = k.slice(0, dotStrIndex);\n const subRootSelection = fields[subRoot];\n if (subRootSelection && !isObj(subRootSelection)) return acc;\n }\n\n const shouldSelect = fields[k];\n const dotKey = root ? [root, k].join(\".\") : k;\n\n if (!isObj(shouldSelect)) {\n return { ...acc, [dotKey]: !!shouldSelect };\n }\n\n return { ...acc, ...flattenFields(shouldSelect, dotKey) };\n }, undefined);\n}\n\n/**\n * Given a fields object and join definitions,\n * split fields into:\n * - '_' (own/base collection fields)\n * - '+' (join fields, keyed by join name)\n *\n * Ensures base fields are flattened and, when needed, augmented so joins have\n * access to required linking properties (e.g., on/from keys).\n *\n * @param {FieldSpec|undefined} fields - Field projection that may include join fields.\n * @param {Record<string, any>} [joins={}] - Join definitions keyed by join name.\n * @returns {{ _: Record<string, boolean>|undefined, '+': FieldSpec|undefined }}\n */\nexport function dispatchFields(fields, joins = {}) {\n if (!isObj(fields)) return { _: normalizeFields(fields) };\n\n const { \"+\": joinFields, ...ownFields } = isolateJoinFields(fields, joins);\n\n if (!joinFields) {\n return { _: normalizeFields(ownFields, true), \"+\": undefined };\n }\n\n // If all own fields are included (i.e., unspecified), we can return as-is\n const allOwnIncluded = !ownFields || !Object.keys(ownFields)?.length;\n\n if (allOwnIncluded) {\n return { _: normalizeFields(ownFields, true), \"+\": joinFields };\n }\n\n // Otherwise, ensure we include any fields required by the join definitions\n // (on/from keys and/or explicit fields defined on the join).\n const augmentedOwnFields = Object.keys(joinFields).reduce((acc, joinKey) => {\n const { on, fields } = joins[joinKey];\n const onFields = Array.isArray(on) ? { [on[0]]: 1 } : undefined;\n if (!(onFields || fields)) return acc;\n return { ...acc, ...onFields, ...fields };\n }, ownFields);\n\n return { _: normalizeFields(augmentedOwnFields, true), \"+\": joinFields };\n}\n\n/**\n * Combine two field projections into one.\n * Rules:\n * - If either is \"all fields\" (undefined or non-object truthy), the result is undefined (select all).\n * - If both are falsy, returns {} (select none).\n * - Otherwise, performs a deep merge where nested objects are merged recursively\n * and scalar values from b override a.\n *\n * @param {FieldSpec|undefined|null|false} a - First projection.\n * @param {FieldSpec|undefined|null|false} b - Second projection.\n * @returns {Record<string, boolean>|undefined} Combined projection.\n */\nexport function combineFields(a, b) {\n // If any fields targets all of them, return undefined to keep all fields\n if (allFields(a) || allFields(b)) return undefined;\n\n // If both fields are falsy, return {} to target no field.\n if (!a && !b) return {};\n\n return deepMergeFields(removeFalsyFields(a), removeFalsyFields(b));\n}\n\n/**\n * Deep merge utility for fields objects.\n * - If both values are plain objects, merge recursively.\n * - Otherwise the value from `b` overrides `a`.\n *\n * @param {Record<string, any>} [a={}] - Base projection.\n * @param {Record<string, any>} [b={}] - Projection to merge over a.\n * @returns {Record<string, any>} Merged projection.\n * @internal\n */\nfunction deepMergeFields(a = {}, b = {}) {\n const out = { ...a };\n for (const [key, valB] of Object.entries(b || {})) {\n const valA = out[key];\n if (isObj(valA) && isObj(valB)) {\n out[key] = deepMergeFields(valA, valB);\n } else {\n out[key] = valB;\n }\n }\n return out;\n}\n\n/**\n * Determine if a projection means \"all fields\".\n * Semantics:\n * - undefined => all fields\n * - falsy (null/false/0) => not all\n * - non-object truthy (e.g., 1/true) => all fields\n * - object => not all\n *\n * @param {any} fields - Projection to test.\n * @returns {boolean} True if it means \"all fields\".\n * @internal\n */\nfunction allFields(fields) {\n if (fields === undefined) return true;\n if (!fields) return false;\n if (isObj(fields)) return false;\n return true;\n}\n\n/**\n * Remove falsy values from a fields object.\n * When the input is not a plain object, returns an empty object.\n *\n * @param {any} fields - Projection to clean.\n * @returns {Record<string, any>} Cleaned fields.\n * @internal\n */\nfunction removeFalsyFields(fields) {\n if (!isObj(fields)) return {};\n return filter(([, v]) => v, fields);\n}\n\n/**\n * Given a fields object and a list of joins,\n * return a fields object where all join fields\n * are grouped under a \"+\" (or configured) key.\n *\n * Returned join fields are validated against the provided joins\n * and non-join fields remain at the root level.\n *\n * Behavior depends on the configured join prefix (via getJoinPrefix()):\n * - If a prefix exists, only fields under that key are considered join fields.\n * - If not, any field whose key exists in `joins` is treated as a join field.\n *\n * @param {FieldSpec} fields - Original fields projection.\n * @param {Record<string, any>} [joins={}] - Join definitions keyed by join name.\n * @returns {{ '+': FieldSpec|undefined } & FieldSpec} Fields split between join and own keys.\n * @internal\n */\nfunction isolateJoinFields(fields, joins = {}) {\n const joinKeys = Object.keys(joins);\n const joinPrefix = getJoinPrefix();\n\n if (joinPrefix) {\n const { [joinPrefix]: joinFields, ...ownFields } = fields;\n\n if (!joinFields) return { \"+\": undefined, ...ownFields };\n\n const existingJoinFields = filter(\n ([k]) => joinKeys.includes(k),\n joinFields,\n );\n return { \"+\": existingJoinFields, ...ownFields };\n }\n\n // If no configured prefix, treat any key that matches a declared join as a join field.\n return Object.entries(fields).reduce((acc, [k, v]) => {\n // Not a join: keep at root\n if (!joinKeys.includes(k)) return { ...acc, [k]: v };\n\n // Join: group under \"+\" key\n const prev = acc[\"+\"] || {};\n return { ...acc, \"+\": { ...prev, [k]: v } };\n }, {});\n}\n\n/**\n * Decrement by one the numeric depth for a given join key in a fields object.\n * Useful to control recursive join depth across nested fetches.\n *\n * - If the value is not a number or is Infinity, the fields are returned unchanged.\n * - Respects the configured join prefix (e.g., '+') when present.\n *\n * @param {string} key - Join key to decrement.\n * @param {FieldSpec|undefined} fields - Field projection possibly containing the join key.\n * @returns {FieldSpec|undefined} A new fields object with decremented depth, or the original.\n */\nexport function decrementRecursiveField(key, fields) {\n if (!isObj(fields)) return fields;\n\n const joinPrefix = getJoinPrefix();\n\n const prev = joinPrefix ? fields[joinPrefix]?.[key] : fields[key];\n\n if (typeof prev !== \"number\") return fields;\n if (prev === Infinity) return fields;\n\n const decremented = prev - 1;\n\n if (!joinPrefix) return { ...fields, [key]: decremented };\n\n return {\n ...fields,\n [joinPrefix]: { ...fields[joinPrefix], [key]: decremented },\n };\n}\n","import { getProtocol } from \"./protocol\";\nimport { then } from \"./util\";\nimport {\n decrementRecursiveField,\n dispatchFields,\n normalizeFields,\n} from \"./fields\";\nimport { getJoins } from \"./join\";\nimport {\n getPropValue,\n includesSome,\n isArr,\n isFunc,\n isObj,\n typeOf,\n uniqueBy,\n} from \"./util\";\n\n/**\n * @typedef {Object} FetchOptions\n * @property {Object} [fields] - Field projection. Supports nested objects and '+' join fields.\n * @property {Object} [sort] - Sort specification (e.g., { createdAt: -1 }).\n * @property {number} [limit] - Max number of documents to return.\n * @property {number} [skip] - Number of documents to skip.\n * @property {Function} [transform] - Document transform function. If omitted, protocol getTransform(Coll) is used.\n * @property {Object} [joins] - Optional per-call join overrides (usually joins come from getJoins(Coll)).\n *\n * @typedef {Object} JoinDef\n * @property {*} Coll - Target collection of the join.\n * @property {[*]|Object|Function} on - How to relate parent docs to target docs:\n * - Array form: [fromProp, toProp, toSelector?]\n * - Object form: static selector for target docs\n * - Function form: (doc) => selector, computed per parent doc\n * @property {boolean} [single] - If true, attach a single doc instead of an array.\n * @property {Function} [postFetch] - (joined, parentDoc) => any. Final shaping of joined value.\n * @property {number} [limit] - Limit for joined query when single is false.\n */\n\n/**\n * Retrieve documents of a collection, with optional joined subdocuments.\n * - Fields accept nested objects; dot-notation is normalized internally.\n * - Joins are defined via getJoins(Coll). Join usage is controlled through '+' in fields.\n * - Works with both sync and async protocols.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} [selector={}] - MongoDB-style query selector.\n * @param {FetchOptions} [options={}] - Fetch options and join controls.\n * @returns {Array|Promise<Array>} List of documents, possibly augmented with join keys.\n *\n * @example\n * // Simple list\n * const users = await fetchList(Users, { status: 'active' }, { fields: { name: 1 } });\n *\n * @example\n * // Join authors on posts\n * const posts = await fetchList(Posts, {}, {\n * fields: { title: 1, '+': { author: 1 } }\n * });\n */\nexport function fetchList(Coll, selector = {}, options = {}) {\n const { count, findList, getTransform } = getProtocol();\n\n const joins = getJoins(Coll);\n\n const collTransform = getTransform(Coll);\n const { fields, transform = collTransform, ...restOptions } = options;\n\n const enhance = (doc) => (isFunc(transform) ? transform(doc) : doc);\n\n // Partition field spec into own (base collection) and join fields ('+')\n const { _: ownFields, \"+\": joinFields = {} } = dispatchFields(fields, joins);\n const usedJoinKeys = Object.keys(joinFields);\n\n // If no joins or fields are not objects, perform a straight fetch\n if (!joins || !usedJoinKeys?.length || (fields && !isObj(fields))) {\n return then(\n findList(Coll, selector, {\n ...restOptions,\n fields: ownFields,\n transform: null,\n }),\n (docs) => docs.map(enhance),\n );\n }\n\n /* === END FETCH WHEN NO JOINS === */\n\n // When joins exist, exclude transform from base fetch to reapply after joining\n return then(\n findList(Coll, selector, {\n ...restOptions,\n fields: ownFields,\n transform: null,\n }),\n\n (docs) => {\n // Partition joins by \"on\" type to process differently\n const joinsByType = usedJoinKeys.reduce((acc, joinKey) => {\n const join = joins[joinKey];\n if (!join) return acc;\n\n const type = typeOf(join.on);\n const enhancedJoin = { ...join, _key: joinKey };\n const prev = acc[type] || [];\n return { ...acc, [type]: [...prev, enhancedJoin] };\n }, {});\n\n const {\n array: arrJoins = [],\n object: objJoins = [],\n function: fnJoins = [],\n } = joinsByType;\n\n // Process array-type joins: [fromProp, toProp, toSelector?]\n return then(\n arrJoins.reduce((_docs, join) => {\n const {\n _key,\n Coll: joinColl,\n on,\n single,\n postFetch,\n limit: joinLimit,\n ...joinRest\n } = join;\n\n return then(_docs, (readyDocs) => {\n const [fromProp, toProp, toSelector = {}] = on;\n const fromArray = isArr(fromProp);\n const propList = fromArray\n ? readyDocs.flatMap((doc) => doc[fromProp[0]])\n : readyDocs.map((doc) => doc[fromProp]);\n\n const toArray = isArr(toProp);\n const subSelector = toArray\n ? {\n ...toSelector,\n [toProp[0]]: { $elemMatch: { $in: propList } },\n }\n : { ...toSelector, [toProp]: { $in: propList } };\n\n // Support recursive joins by checking for additional depth and data existence\n const isRecursive = joinColl === Coll && joinFields[_key] > 1;\n\n return then(\n isRecursive && count(Coll, subSelector),\n\n (recursiveCount) => {\n const stopRecursion = isRecursive && !recursiveCount;\n\n const subJoinFields = isRecursive\n ? decrementRecursiveField(_key, fields)\n : joinFields[_key];\n\n // Determine whether we need to include toProp explicitly in subFields\n const { _: own } = dispatchFields(\n subJoinFields,\n getJoins(joinColl) || {},\n );\n\n const allOwnIncluded = !own || Object.keys(own).length <= 0;\n const shouldAddToProp =\n isObj(subJoinFields) && !allOwnIncluded && toProp !== \"_id\";\n\n const subFields = shouldAddToProp\n ? { ...subJoinFields, [toProp]: 1 }\n : subJoinFields;\n\n /** @type {FetchOptions} */\n const subOptions = {\n ...options,\n ...joinRest,\n fields: normalizeFields(subFields),\n limit: undefined,\n transform: isRecursive ? transform : undefined,\n };\n\n // Fetch all joined docs for this join and attach to each base doc\n return then(\n stopRecursion\n ? []\n : fetchList(joinColl, subSelector, subOptions),\n\n (allJoinedDocs) => {\n // Build index by toProp for faster lookups when toProp is scalar\n const indexedByToProp = toArray\n ? {}\n : allJoinedDocs.reduce((acc, joinedDoc) => {\n const toPropValue = getPropValue(toProp, joinedDoc);\n if (isArr(toPropValue)) {\n return toPropValue.reduce((acc2, v) => {\n const prev = acc2[v] || [];\n return { ...acc2, [v]: [...prev, joinedDoc] };\n }, acc);\n }\n const prev = acc[toPropValue] || [];\n return {\n ...acc,\n [toPropValue]: [...prev, joinedDoc],\n };\n }, {});\n\n return readyDocs.map((doc) => {\n let joinedDocs = [];\n\n if (toArray) {\n // toProp is an array on joined docs\n joinedDocs = allJoinedDocs.filter((joinedDoc) => {\n const toList = joinedDoc[toProp[0]] || [];\n if (!fromArray) return toList.includes(doc[fromProp]);\n\n const fromList = doc[fromProp[0]] || [];\n return includesSome(toList, fromList);\n });\n } else if (fromArray) {\n // fromProp is array on parent docs\n const fromValues = doc[fromProp[0]] || [];\n joinedDocs = uniqueBy(\n \"_id\",\n fromValues.flatMap(\n (fromValue) => indexedByToProp[fromValue] || [],\n ),\n );\n } else {\n // Both scalar\n const fromValue = doc[fromProp];\n joinedDocs = indexedByToProp[fromValue] || [];\n }\n\n const raw = single ? joinedDocs[0] : joinedDocs;\n const afterPostFetch = isFunc(postFetch)\n ? postFetch(raw, doc)\n : raw;\n return { ...doc, [_key]: afterPostFetch };\n });\n },\n );\n },\n );\n });\n }, docs),\n\n (docsWithArrJoins) => {\n // Prepare object-type joins (static selector): fetched once, applied per doc\n return then(\n objJoins.map((join) => {\n const { _key, on } = join;\n const subSelector = on;\n return createJoinFetcher({\n Coll,\n join,\n fields: joinFields[_key],\n subSelector,\n options: restOptions,\n parentFields: fields,\n });\n }),\n\n (objJoinsEnhancers) => {\n // For each doc, apply object-join enhancers, then function-type joins per doc\n return then(\n docsWithArrJoins.map((doc) => {\n const docWithObjJoins = objJoinsEnhancers.reduce(\n (_doc, fn) => fn(_doc),\n doc,\n );\n\n return then(\n fnJoins.reduce((_doc, join) => {\n const { _key, on } = join;\n\n return then(\n [\n _doc,\n createJoinFetcher({\n Coll,\n join,\n fields: joinFields[_key],\n subSelector: isFunc(on) ? on(doc) : on,\n options: restOptions,\n parentFields: fields,\n }),\n ],\n\n ([_doc, joinFetcher]) => joinFetcher(_doc),\n );\n }, docWithObjJoins),\n\n // Re-apply transform after all joins\n (docWithFnJoins) => enhance(docWithFnJoins),\n );\n }),\n\n (res) => res,\n );\n },\n );\n },\n );\n },\n );\n}\n\n/**\n * Fetch a single document matching the selector.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style query selector.\n * @param {FetchOptions} [options={}] - Fetch options.\n * @returns {Object|undefined|Promise<Object|undefined>} First matching document or undefined.\n */\nexport function fetchOne(Coll, selector, options = {}) {\n return then(\n fetchList(Coll, selector, { ...options, limit: 1 }),\n (res) => res[0],\n );\n}\n\n/**\n * Fetch only document IDs for the selector.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style query selector.\n * @param {FetchOptions} [options] - Fetch options.\n * @returns {Array<string>|Promise<Array<string>>} Array of IDs.\n */\nexport function fetchIds(Coll, selector, options) {\n return then(\n fetchList(Coll, selector, { ...options, fields: { _id: 1 } }),\n (res) => pluckIds(res),\n );\n}\n\n/**\n * Check existence of at least one document matching selector.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style query selector.\n * @returns {boolean|Promise<boolean>} True if a document exists.\n */\nexport function exists(Coll, selector) {\n return then(\n // Limit to _id field to ensure minimal data\n fetchOne(Coll, selector, { fields: { _id: 1 } }),\n\n (doc) => !!doc,\n );\n}\n\n/**\n * Extract _id values from an array of documents.\n * @param {Array<{_id: string}>} arr - Documents to pluck IDs from.\n * @returns {Array<string>} List of IDs.\n * @internal\n */\nfunction pluckIds(arr) {\n return arr.map(({ _id }) => _id);\n}\n\n/* HELPERS */\n\n/**\n * Create a join fetcher function for a given join definition.\n * Returns a function (doc) => docWithJoin that attaches joined data under join _key.\n *\n * Handles:\n * - Recursive joins (Coll === joinColl) with depth tracking via decrementRecursiveField\n * - Post-fetch shaping via join.postFetch\n *\n * @param {Object} args\n * @param {*} args.Coll - Parent collection.\n * @param {JoinDef & {_key: string}} args.join - The join definition with internal key.\n * @param {Object|number} args.fields - Join field spec or depth number (for '+').\n * @param {Object} args.subSelector - Selector for the joined collection.\n * @param {FetchOptions} args.options - Parent fetch options to forward.\n * @param {Object} args.parentFields - Parent fields, used for recursive depth.\n * @returns {Function|Promise<Function>} Function that attaches joined data to a doc.\n * @internal\n */\nfunction createJoinFetcher({\n Coll,\n join: {\n _key,\n Coll: joinColl,\n on,\n single,\n postFetch,\n limit: joinLimit,\n ...joinRest\n },\n fields,\n subSelector,\n options,\n parentFields,\n}) {\n const { count } = getProtocol();\n\n const isRecursive = joinColl === Coll;\n\n return then(\n // For recursive joins, check if there would be results to avoid unnecessary nested fetch\n isRecursive && count(Coll, subSelector),\n\n (recursiveCount) => {\n const stopRecursion = isRecursive && (!fields || !recursiveCount);\n\n const joinFields = isRecursive\n ? decrementRecursiveField(_key, parentFields)\n : fields;\n\n /** @type {FetchOptions} */\n const subOptions = {\n ...options,\n ...joinRest,\n fields: normalizeFields(joinFields),\n limit: single ? 1 : joinLimit || undefined,\n };\n\n // Fetch joined docs and build an applier function\n return then(\n stopRecursion ? [] : fetchList(joinColl, subSelector, subOptions),\n\n (joinedDocs) => {\n return (doc) => {\n const raw = single ? joinedDocs[0] : joinedDocs;\n const afterPostFetch = isFunc(postFetch)\n ? postFetch(raw, doc)\n : raw;\n return { ...doc, [_key]: afterPostFetch };\n };\n },\n );\n },\n );\n}\n","import { combineFields } from \"./fields\";\nimport { isArr, isFunc, then } from \"./util\";\n\n/**\n * List of supported hook types.\n * - beforeInsert: runs before inserting a document. (docToInsert)\n * - beforeUpdate: runs before updating documents. (docsToUpdate, modifier)\n * - beforeRemove: runs before removing documents. (docsToRemove)\n * - onInserted: runs after a document is inserted. (doc)\n * - onUpdated: runs after a document is updated. (doc, beforeDoc?)\n * - onRemoved: runs after a document is removed. (doc)\n * @type {Array<HookType>}\n * @readonly\n */\nconst HOOK_TYPES = [\n \"beforeInsert\", // (docToInsert)\n \"beforeUpdate\", // (docsToUpdate, modifier)\n \"beforeRemove\", // (docsToRemove)\n\n \"onInserted\", // (doc)\n \"onUpdated\", // (doc, before)\n \"onRemoved\", // (doc)\n];\n\n/**\n * @typedef {'beforeInsert'|'beforeUpdate'|'beforeRemove'|'onInserted'|'onUpdated'|'onRemoved'} HookType\n */\n\n/**\n * Generic hook function signature.\n * The concrete arguments depend on the HookType (see HOOK_TYPES above).\n * Returning a promise is supported.\n * @typedef {(β¦args:any[]) => any|Promise<any>} HookFn\n */\n\n/**\n * Optional predicate to prevent a hook from running.\n * If returns a truthy value, the hook is skipped.\n * Receives the same arguments as the HookFn.\n * @typedef {(...args:any[]) => boolean} HookUnlessPredicate\n */\n\n/**\n * Optional predicate that must be truthy for the hook to run.\n * Receives the same arguments as the HookFn.\n * @typedef {(...args:any[]) => boolean} HookWhenPredicate\n */\n\n/**\n * Hook definition object.\n * - before: Only meaningful for \"onUpdated\". If true, the \"before\" document should be fetched.\n * - fields: Projection of fields to fetch for the documents the hook needs.\n * Combined across all hooks of the same type via combineFields.\n * `undefined` or `true` means \"all fields\".\n * - fn: The hook function (required).\n * - unless: Optional predicate; if truthy, prevents the hook from running.\n * - when: Optional predicate; if truthy, allows the hook to run.\n * @typedef {Object} HookDef\n * @property {boolean} [before]\n * @property {import('./fields').FieldSpec|true|undefined} [fields]\n * @property {HookFn} fn\n * @property {HookUnlessPredicate} [unless]\n * @property {HookWhenPredicate} [when]\n */\n\n/**\n * Single registry holding hook definitions per collection instance.\n * Map<CollectionInstance, Record<HookType, HookDef[]>>\n * @type {Map<*, Record<HookType, HookDef[]>>}\n * @internal\n */\nconst hooksRegistry = new Map();\n\n/*\n * hookDef = {\n * before, // Bool. onUpdated only. When true, fetch document before update. Otherwise, document will be fetched, but only with its _id field to know which docs to retrieve after the update.\n * fields, // Fields of document to fetch. Will be combined for all hooks of the same type. `undefined` or `true` means all fields and subsequent field restrictions won't apply.\n * fn, // (doc, before<onUpdated>) => side effect. Function to run as hook\n * unless, // (doc, before<onUpdated>) => bool. Predicate to prevent hook from running\n * when, // (doc, before<onUpdated>) => bool. Predicate to run the hook\n * };\n */\n\n/**\n * Register multiple hooks for a collection.\n * The hooksObj keys must be valid HookType values, and each value must be an array of HookDef.\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {Record<HookType, HookDef[]>} hooksObj - Object mapping hook types to arrays of hook definitions.\n * @throws {TypeError} If a hook list is not an array or an unknown hook type is provided.\n * @example\n * hook(Users, {\n * beforeInsert: [{\n * fields: { email: 1 },\n * fn(doc) {\n * if (!doc.email) throw new Error('Email required');\n * }\n * }],\n * onInserted: [{\n * fn(doc) { console.log('Inserted user', doc._id); }\n * }]\n * });\n */\nexport function hook(\n Coll, // Collection class instance\n hooksObj, // { ...[hookType]: array of hook definition }\n) {\n Object.entries(hooksObj).forEach(([hookType, hooks]) => {\n if (!isArr(hooks)) {\n throw new TypeError(`'${hookType}' hooks must be an array`);\n }\n\n hooks.forEach((_hook) => addHookDefinition(Coll, hookType, _hook));\n });\n}\n\n/**\n * Add a hook definition for a given collection and hook type.\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {HookType} hookType - The hook type.\n * @param {HookDef} hookDef - The hook definition to add.\n * @throws {TypeError} If hookType is invalid or hookDef.fn is not a function.\n * @internal\n */\nfunction addHookDefinition(Coll, hookType, hookDef) {\n if (!HOOK_TYPES.includes(hookType)) {\n throw new TypeError(`'${hookType}' is not a valid hook type`);\n }\n\n if (!isFunc(hookDef?.fn)) {\n throw new TypeError(\"'hook' must be a function or contain a 'fn' key\");\n }\n\n const collHooks = getHookDefinitions(Coll);\n const prevHooks = collHooks[hookType] || [];\n const nextHooks = [...prevHooks, hookDef];\n hooksRegistry.set(Coll, { ...collHooks, [hookType]: nextHooks });\n}\n\n/**\n * Retrieve the hook definitions for a collection.\n *\n * - If hookType is omitted, returns the full record of hook arrays keyed by HookType.\n * - If hookType is provided, returns the array of HookDef for that type or undefined.\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {HookType} [hookType] - Optional hook type filter.\n * @returns {Record<HookType, HookDef[]>|HookDef[]|undefined} Hook definitions.\n */\nexport function getHookDefinitions(Coll, hookType) {\n const collHooks = hooksRegistry.get(Coll) || {};\n if (!hookType) return collHooks;\n return collHooks[hookType];\n}\n\n/**\n * Get a combined hook for a collection and type.\n * This merges multiple HookDef into a single executable hook with:\n * - fields: combined via combineFields across all HookDef.fields\n * - before: true if any HookDef sets before=true\n * - fn: a runner that applies all matching hooks honoring their when/unless predicates\n *\n * @template TColl\n * @param {TColl} Coll - Collection class instance.\n * @param {HookType} hookType - The hook type.\n * @returns {{ fields: any, fn: HookFn, before?: boolean }|undefined} Combined hook or undefined if none.\n */\nexport function getHook(Coll, hookType) {\n const hookDefinitions = getHookDefinitions(Coll, hookType);\n\n if (!hookDefinitions) return undefined;\n\n return combineHookDefinitions(hookDefinitions);\n}\n\n/**\n * Combine multiple hook definitions into a single aggregated definition.\n * - fields are merged with combineFields\n * - before is true if any definition requires it\n * - fn executes all hooks (respecting when/unless) and resolves once all are done\n *\n * @param {HookDef[]} [hookDefs=[]]\n * @returns {{ fields: any, fn: HookFn, before?: boolean }|undefined}\n * @internal\n */\nfunction combineHookDefinitions(hookDefs = []) {\n if (!hookDefs.length) return undefined;\n\n /* Reduce hook definitions to derive combined fields and `before` option */\n const { fields, before } = hookDefs.reduce(\n (acc, hookDef) => {\n return {\n fields: combineFields(acc.fields, hookDef.fields),\n before: acc.before || hookDef.before,\n };\n },\n { fields: null, before: undefined },\n );\n\n /* Given the hook type arguments, convert each hook to a handler,\n * then execute them with the arguments as part of a single promise. */\n function globalHandler(...args) {\n const handlers = hookDefs\n .map((hookDef) => hookToHandler(hookDef, ...args))\n .filter(isFunc);\n\n return then(\n handlers.map((handler) => handler(...args)),\n (res) => res,\n );\n }\n\n return { fields, fn: globalHandler, before };\n}\n\n/**\n * Convert a HookDef into an executable handler (or undefined) by applying\n * the optional unless/when predicates with the invocation arguments.\n *\n * - If unless returns truthy, the hook is skipped.\n * - If when is provided and returns falsy, the hook is skipped.\n * - Otherwise returns the hook's fn.\n *\n * @param {HookDef} param0 - Hook definition.\n * @param {...any} args - Arguments the hook would receive.\n * @returns {HookFn|undefined} The executable function or undefined if filtered out.\n * @internal\n */\nfunction hookToHandler({ fn, unless, when }, ...args) {\n const prevented = unless?.(...args);\n if (prevented) return;\n\n const shouldRun = !when || when(...args);\n\n if (!shouldRun) return;\n\n return fn;\n}\n","import { fetchOne } from \"./fetch\";\nimport { getHook } from \"./hook\";\nimport { getProtocol } from \"./protocol\";\nimport { isFunc, then } from \"./util\";\n\n/**\n * Insert a document into a collection with hook support.\n *\n * Execution flow (sync or async depending on the active protocol):\n * 1) Run `beforeInsert` hook if defined: can validate/mutate the doc.\n * 2) Call protocol.insert(Coll, doc) to perform the insertion.\n * 3) Run `onInserted` hook if defined:\n * - If the hook requests only {_id: 1}, pass {_id} directly.\n * - Otherwise fetch the inserted document with the requested fields,\n * then pass it to the hook.\n *\n * Notes:\n * - Uses the `then` helper to normalize sync/async protocols and hooks.\n * - Does not await `onInserted` (fire-and-forget side effects).\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance to insert into.\n * @param {Object} doc - The document to insert (will be passed to beforeInsert hooks).\n * @returns {any|Promise<any>} The inserted document _id (type depends on protocol/driver).\n *\n * @example\n * // Basic usage\n * const _id = await insert(Users, { name: 'Alice', email: 'a@ex.com' });\n *\n * @example\n * // With hooks configured elsewhere\n * // beforeInsert could normalize fields; onInserted could enqueue a job\n * const userId = await insert(Users, payload);\n */\nexport function insert(Coll, doc) {\n const protocol = getProtocol();\n const beforeInsertHook = getHook(Coll, \"beforeInsert\");\n\n return then(\n // Run `beforeInsert` if present (may mutate/validate doc)\n isFunc(beforeInsertHook?.fn) && beforeInsertHook.fn(doc),\n\n () => {\n const onInsertedHook = getHook(Coll, \"onInserted\");\n\n return then(\n // Perform actual insert via protocol\n protocol.insert(Coll, doc),\n\n (_id) => {\n if (!onInsertedHook) return _id;\n\n const { fields, fn: onInserted } = onInsertedHook;\n\n // If hook only needs _id, no fetch is necessary\n const fieldKeys = fields ? Object.keys(fields) : [];\n const _idOnly = fieldKeys.length === 1 && fieldKeys[0] === \"_id\";\n\n return then(\n // Fetch inserted doc or pass {_id} directly\n _idOnly ? { _id } : fetchOne(Coll, { _id }, { fields }),\n\n (insertedDoc) => {\n // Fire-and-forget: don't await onInserted\n onInserted(insertedDoc);\n return _id;\n },\n );\n },\n );\n },\n );\n}\n","import { fetchList } from \"./fetch\";\nimport { combineFields } from \"./fields\";\nimport { getHook } from \"./hook\";\nimport { getProtocol } from \"./protocol\";\nimport { isFunc, then } from \"./util\";\n\n/**\n * Remove documents from a collection with hook support.\n *\n * Execution flow (sync or async depending on the active protocol):\n * 1) Compute the minimal fields to fetch for hooks (union of beforeRemove/onRemoved fields).\n * 2) If any hook is defined, fetch the matching documents once with those fields.\n * 3) Run `beforeRemove` hook (if present) with the array of docs.\n * 4) Call protocol.remove(Coll, selector).\n * 5) If something was removed and `onRemoved` exists, call it once per doc (fire-and-forget).\n *\n * Notes:\n * - Uses the `then` helper to normalize sync/async protocols and hooks.\n * - `onRemoved` is intentionally not awaited; it runs after remove is triggered.\n * - If no hooks are registered, no pre-fetch is performed.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance to remove from.\n * @param {Object} selector - MongoDB-style query selector.\n * @returns {number|Promise<number>} The number of removed documents (driver-dependent).\n *\n * @example\n * // Basic usage\n * const removed = await remove(Users, { inactive: true });\n *\n * @example\n * // With hooks configured elsewhere\n * // beforeRemove could check permissions; onRemoved could clear caches\n * const n = await remove(Posts, { authorId });\n */\nexport function remove(Coll, selector) {\n const protocol = getProtocol();\n\n const beforeRemoveHook = getHook(Coll, \"beforeRemove\");\n const onRemovedHook = getHook(Coll, \"onRemoved\");\n\n // Union of fields requested by before/on hooks; null means \"no hooks, no fetch\"\n const globalFields = getBeforeFields(beforeRemoveHook, onRemovedHook);\n\n return then(\n // Fetch docs only when at least one hook exists\n globalFields === null\n ? []\n : fetchList(Coll, selector, { fields: globalFields }),\n\n (docs) => {\n return then(\n // Run `beforeRemove` if defined\n isFunc(beforeRemoveHook?.fn) && beforeRemoveHook.fn(docs),\n\n () => {\n // Execute actual removal (can be sync or a Promise<number>)\n const removedCount = protocol.remove(Coll, selector);\n\n // If removal did nothing or there is no onRemoved hook, return as-is\n if (!removedCount || !isFunc(onRemovedHook?.fn)) return removedCount;\n\n // Fire-and-forget: pass each (pre-fetched) doc to onRemoved\n docs.forEach((doc) => onRemovedHook.fn(doc));\n\n return removedCount;\n },\n );\n },\n );\n}\n\n/**\n * Compute the union of the fields requested by provided hook definitions.\n *\n * Returns:\n * - null when no hooks are provided (signals \"no pre-fetch needed\")\n * - a FieldSpec (possibly undefined meaning \"all fields\") combining hook fields\n *\n * @param {...{fields?: import('./fields').FieldSpec|true|undefined}|undefined} maybeHooks\n * @returns {import('./fields').FieldSpec|true|undefined|null}\n * Combined fields, or null if there were no hooks at all.\n * @internal\n */\nfunction getBeforeFields(...maybeHooks) {\n const hookDefs = maybeHooks.filter((hookDef) => hookDef);\n if (!hookDefs.length) return null;\n\n return hookDefs.reduce(\n (fields, hookDef) => combineFields(fields, hookDef.fields),\n null,\n );\n}\n","import { fetchList } from \"./fetch\";\nimport { combineFields } from \"./fields\";\nimport { getHook } from \"./hook\";\nimport { getProtocol } from \"./protocol\";\nimport { indexById, isFunc, then } from \"./util\";\n\n/**\n * Update documents in a collection with hook support.\n *\n * Execution flow (sync or async depending on the active protocol):\n * 1) Determine the minimal fields to prefetch for hooks (union of beforeUpdate/onUpdated needs).\n * 2) If any hook exists, fetch the target documents once with those fields (limit 1 if multi=false).\n * 3) Run `beforeUpdate` hook with (docs, modifier) if present.\n * 4) Execute protocol.update(Coll, selector, modifier, options).\n * 5) If some docs were modified and `onUpdated` exists:\n * - Re-fetch affected docs by _id with `onUpdated.fields`\n * - Call `onUpdated(afterDoc, beforeDoc)` for each (fire-and-forget).\n *\n * Notes:\n * - Uses `then` helper to normalize sync/async protocols and hooks.\n * - `onUpdated` is not awaited; it is intended for side effects.\n *\n * @template TColl\n * @param {TColl} Coll - The collection instance.\n * @param {Object} selector - MongoDB-style selector to match documents.\n * @param {Object} modifier - MongoDB-style update modifier (e.g., {$set: {...}}).\n * @param {Object} [options] - Update options.\n * @param {boolean} [options.multi=true] - Update multiple documents by default.\n * @returns {number|Promise<number>} Number of modified documents (driver-dependent).\n *\n * @example\n * // Activate all pending users\n * await update(Users, { status: 'pending' }, { $set: { status: 'active' } }, { multi: true });\n *\n * @example\n * // Update a single document\n * await update(Users, { _id }, { $set: { name: 'Alice' } }, { multi: false });\n */\nexport function update(\n Coll,\n selector,\n modifier,\n {\n multi = true, // Ensure update targets multiple documents by default.\n ...restOptions\n } = {},\n) {\n const protocol = getProtocol();\n\n const beforeUpdateHook = getHook(Coll, \"beforeUpdate\");\n const onUpdatedHook = getHook(Coll, \"onUpdated\");\n\n const options = { multi, ...restOptions };\n\n // Fields to prefetch before update (null => no hooks => no prefetch)\n const fieldsBefore = getBeforeFields(beforeUpdateHook, onUpdatedHook);\n\n return then(\n /* Fetch docs only if at least one hook has been defined. */\n fieldsBefore === null\n ? []\n : fetchList(Coll, selector, {\n fields: fieldsBefore,\n limit: multi ? undefined : 1,\n }),\n (docs) => {\n return then(\n /* Execute `beforeUpdate` hook if defined */\n isFunc(beforeUpdateHook?.fn) && beforeUpdateHook.fn(docs, modifier),\n\n () => {\n return then(\n /* Execute the update */\n protocol.update(Coll, selector, modifier, options),\n\n (updatedCount) => {\n /* If update didn't work, don't execute comparators. */\n if (!updatedCount || !isFunc(onUpdatedHook?.fn))\n return updatedCount;\n\n /* Save a version of targeted docs prior to the update */\n const beforeById = indexById(docs);\n\n return then(\n /* Fetch again each targeted document by its previously saved _id */\n fetchList(\n Coll,\n { _id: { $in: Object.keys(beforeById) } },\n { fields: onUpdatedHook.fields },\n ),\n\n /* Pass the before and after versions to each comparator. */\n (afterDocs) => {\n /* Pass each after and before pairs to hook.\n * Do NOT await, should run asynchronously if protocol allows. */\n afterDocs.forEach((after) => {\n const before = beforeById[after._id];\n onUpdatedHook.fn(after, before);\n });\n\n return updatedCount;\n },\n );\n },\n );\n },\n );\n },\n );\n}\n\n/**\n * Compute fields to prefetch before the update for hooks.\n *\n * Returns:\n * - null if no hooks are defined (signals \"no prefetch needed\")\n * - the union of:\n * - beforeUpdate.fields (if any)\n * - onUpdated.fields, or {_id:1} if onUpdated.before is falsy\n *\n * @param {{fields?: import('./fields').FieldSpec|true|undefined}|undefined} beforeHook\n * @param {{fields?: import('./fields').FieldSpec|true|undefined, before?: boolean}|undefined} afterHook\n * @returns {import('./fields').FieldSpec|true|undefined|null}\n * Combined fields, or null if there are no hooks at all.\n * @internal\n */\nfunction getBeforeFields(beforeHook, afterHook) {\n /* If no hooks, return null */\n if (!beforeHook && !afterHook) return null;\n\n if (!afterHook) return beforeHook?.fields;\n\n const { fields, before } = afterHook;\n\n /* If `before` is false, documents before update are not necessary.\n * Only their ids will be fetched so after update documents\n * can be retrieved. */\n const fieldsBefore = before ? fields : { _id: 1 };\n\n if (!beforeHook) return fieldsBefore;\n\n return combineFields(beforeHook.fields, fieldsBefore);\n}\n","export default {\n count(Coll, selector, options) {\n /* Use rawCollection's countDocuments method if available\n * to prevent loading in memory */\n const rawColl = Coll.rawCollection();\n if (rawColl.countDocuments) return rawColl.countDocuments(selector);\n\n /* If `countDocuments` raw collection method is not available,\n * use the MongoDB async count method. */\n return Coll.find(selector, options).countAsync();\n },\n\n cursor: (Coll, selector, options) => Coll.find(selector, options),\n\n findList: (Coll, selector, options) =>\n Coll.find(selector, options).fetchAsync(),\n\n getTransform: (Coll) => Coll._transform,\n\n insert: (Coll, doc) => Coll.insertAsync(doc),\n\n remove: (Coll, selector) => Coll.removeAsync(selector),\n\n update: (Coll, selector, modifier, options) => {\n /* Allow multi document update by default */\n return Coll.updateAsync(selector, modifier, { multi: true, ...options });\n },\n};\n","export default {\n count: (Coll, selector, options) => Coll.find(selector, options).count(),\n\n cursor: (Coll, selector, options) => Coll.find(selector, options),\n\n findList: (Coll, selector, options) => Coll.find(selector, options).fetch(),\n\n getTransform: (Coll) => Coll._transform,\n\n insert: (Coll, doc) => Coll.insert(doc),\n\n remove: (Coll, selector) => Coll.remove(selector),\n\n update: (Coll, selector, modifier, options) => {\n /* Allow multi document update by default */\n return Coll.update(selector, modifier, { multi: true, ...options });\n },\n};\n","import { renameKeys } from \"../util\";\n\n/**\n * Node protocol for the official MongoDB driver.\n * Assumes Coll is an instance of mongodb.Collection.\n */\nexport default {\n /**\n * Count documents matching selector.\n * Uses countDocuments (preferred over deprecated cursor.count()).\n */\n count(Coll, selector = {}, options = {}) {\n return Coll.countDocuments(selector || {}, options);\n },\n\n /**\n * Return a MongoDB FindCursor for advanced usage.\n * Maps fields -> projection.\n */\n cursor(Coll, selector = {}, options = {}) {\n const renamedOptions = renameKeys({ fields: \"projection\" }, options || {});\n return Coll.find(selector || {}, renamedOptions);\n },\n\n /**\n * Return an array of documents for selector/options.\n */\n findList(Coll, selector = {}, options = {}) {\n const renamedOptions = renameKeys({ fields: \"projection\" }, options || {});\n return Coll.find(selector || {}, renamedOptions).toArray();\n },\n\n /**\n * Optional per-collection transform; expose Coll.transform if present.\n */\n getTransform(Coll) {\n return typeof Coll?.transform === \"function\" ? Coll.transform : undefined;\n },\n\n /**\n * Insert a document and return insertedId.\n */\n insert(Coll, doc, options) {\n return Coll.insertOne(doc, options).then((res) => res?.insertedId);\n },\n\n /**\n * Remove documents. Honors options.multi (default true).\n */\n remove(Coll, selector = {}, options = {}) {\n const { multi = true, ...rest } = options || {};\n const p = multi\n ? Coll.deleteMany(selector || {}, rest)\n : Coll.deleteOne(selector || {}, rest);\n return p.then((res) => res?.deletedCount ?? 0);\n },\n\n /**\n * Update documents. Honors options.multi (default true).\n * Returns modifiedCount (or upsertedCount as fallback).\n */\n update(Coll, selector = {}, modifier = {}, options = {}) {\n const { multi = true, ...rest } = options || {};\n const p = multi\n ? Coll.updateMany(selector || {}, modifier || {}, rest)\n : Coll.updateOne(selector || {}, modifier || {}, rest);\n return p.then((res) => res?.modifiedCount ?? res?.upsertedCount ?? 0);\n },\n};\n"],"names":["DEFAULT_PROTOCOL","count","Error","cursor","findList","getTransform","insert","remove","update","protocol","getProtocol","overload","setProtocol","methods","_extends","updateProtocol","fnOrMethods","typeOf","obj","toString","call","split","slice","toLowerCase","isArr","x","Array","isArray","isFunc","isObj","isPromise","then","renameKeys","dictionnary","object","map","_ref","k","v","getPropValue","dotKey","doc","_dotKey$split","rootKey","rest","_arrayLikeToArray","rootValue","length","subDotKey","join","flatMap","subDoc","subValue","maybePromise","fn","arr","some","Promise","all","value","undefined","y","TypeError","Object","fromEntries","entries","filter","pred","Coll","selector","_count","res","KNOWN_TYPES","knownTypesCaption","joinsDictionnary","Map","joinPrefix","Collection","joins","forEach","key","_ref$","on","fields","joinType","includes","_console","console","warn","apply","arguments","set","get","getJoins","getJoinPrefix","setJoinPrefix","prefix","_excluded","normalizeFields","flatten","flattenFields","root","keys","startsWith","reduce","acc","dotStrIndex","indexOf","subRoot","subRootSelection","_extends2","shouldSelect","dispatchFields","_Object$keys","_","_isolateJoinFields","joinKeys","joinFields","ownFields","_objectWithoutPropertiesLoose","_toPropertyKey","_ref4","_ref5","_extends3","_extends4","prev","isolateJoinFields","augmentedOwnFields","joinKey","_ref2","_joins$joinKey","onFields","combineFields","a","b","allFields","deepMergeFields","removeFalsyFields","out","_i","_Object$entries","_Object$entries$_i","valB","valA","_ref3","decrementRecursiveField","_fields$joinPrefix","_extends5","_extends6","_extends7","Infinity","decremented","fetchList","options","_getProtocol","collTransform","_options$transform","transform","restOptions","enhance","_dispatchFields","_dispatchFields$","usedJoinKeys","docs","joinsByType","type","enhancedJoin","_key","concat","_joinsByType$array","array","_joinsByType$object","objJoins","_joinsByType$function","fnJoins","_docs","joinColl","single","postFetch","joinRest","_excluded2","readyDocs","fromProp","toProp","_on$","toSelector","fromArray","propList","toArray","subSelector","$elemMatch","$in","isRecursive","recursiveCount","stopRecursion","subJoinFields","own","allOwnIncluded","subFields","subOptions","limit","allJoinedDocs","indexedByToProp","joinedDoc","toPropValue","acc2","_extends8","toValueOrProp","list","toValue","joinedDocs","searchedArr","toList","el","fromValue","item","find","prevItem","raw","afterPostFetch","docsWithArrJoins","createJoinFetcher","parentFields","objJoinsEnhancers","docWithObjJoins","_doc","joinFetcher","docWithFnJoins","fetchOne","fetchIds","_id","exists","_ref3$join","joinLimit","_excluded3","_getProtocol2","_extends9","HOOK_TYPES","hooksRegistry","hook","hooksObj","hookType","hooks","_hook","hookDef","collHooks","getHookDefinitions","nextHooks","addHookDefinition","getHook","hookDefinitions","hookDefs","_hookDefs$reduce","before","args","hookToHandler","handler","combineHookDefinitions","unless","when","beforeInsertHook","onInsertedHook","onInserted","fieldKeys","insertedDoc","beforeRemoveHook","onRemovedHook","globalFields","getBeforeFields","removedCount","modifier","_temp","_ref$multi","multi","beforeUpdateHook","onUpdatedHook","fieldsBefore","beforeHook","afterHook","updatedCount","beforeById","indexById","afterDocs","after","rawColl","rawCollection","countDocuments","countAsync","fetchAsync","_transform","insertAsync","removeAsync","updateAsync","fetch","renamedOptions","insertOne","insertedId","deleteMany","deleteOne","_res$deletedCount","deletedCount","_ref2$multi","updateMany","updateOne","_res$modifiedCount","modifiedCount","upsertedCount"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coll-fns",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "microbundle -f esm,cjs",
|
|
8
|
+
"dev": "microbundle watch -f esm,cjs",
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"source": "src/index.js",
|
|
15
|
+
"main": "./dist/coll-fns.cjs",
|
|
16
|
+
"module": "./dist/coll-fns.mjs",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/coll-fns.mjs",
|
|
20
|
+
"require": "./dist/coll-fns.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"keywords": [],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"eslint": "^9.39.1",
|
|
28
|
+
"eslint-config-prettier": "^10.1.8",
|
|
29
|
+
"microbundle": "^0.15.1",
|
|
30
|
+
"prettier": "^3.6.2"
|
|
31
|
+
}
|
|
32
|
+
}
|