qarjs 0.8.2

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/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zsolt Tövis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,771 @@
1
+ ██████╗ █████╗ ██████╗
2
+ ██╔═══██╗██╔══██╗██╔══██╗
3
+ ██║ ██║███████║██████╔╝
4
+ ██║▄▄ ██║██╔══██║██╔══██╗
5
+ ╚██████╔╝██║ ██║██║ ██║
6
+ ╚══▀▀═╝ ╚═╝ ╚═╝╚═╝ ╚═╝
7
+
8
+ # Qar - Query Arrays
9
+
10
+ MongoDB-style queries for plain JavaScript arrays. Simple, lightweight, and perfect for Next.js, static sites, and JSON data.
11
+
12
+ ## Why Qar?
13
+
14
+ When you have a JSON file or an array of objects and need to filter, search, or query them with MongoDB-style syntax - without setting up a database.
15
+
16
+ ```javascript
17
+ import { Qar } from 'qarjs';
18
+
19
+ const products = new Qar(productsData);
20
+
21
+ // Simple queries
22
+ products.find({ category: 'electronics', price: { $lt: 500 } }).toArray();
23
+
24
+ // Chainable API with sorting and pagination
25
+ products.find({ inStock: true }).sort({ price: -1 }).limit(10).toArray();
26
+
27
+ // Field projection
28
+ products.find({ category: 'phones' }, { name: 1, price: 1, _id: 0 }).toArray();
29
+
30
+ // Aggregation pipeline
31
+ products.aggregate([
32
+ { $match: { inStock: true } },
33
+ { $group: { _id: '$category', count: { $sum: 1 } } },
34
+ { $sort: { count: -1 } },
35
+ ]);
36
+
37
+ // Get unique values
38
+ products.distinct('category'); // => ['electronics', 'clothing', 'books']
39
+ ```
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ npm install qarjs
45
+ ```
46
+
47
+ ```bash
48
+ yarn add qarjs
49
+ ```
50
+
51
+ ```bash
52
+ pnpm add qarjs
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ```javascript
58
+ import { Qar } from 'qarjs';
59
+
60
+ const data = [
61
+ { _id: 1, name: 'Alice', age: 20, role: 'user' },
62
+ { _id: 2, name: 'Bob', age: 30, role: 'admin' },
63
+ { _id: 3, name: 'Carol', age: 40, role: 'user' },
64
+ ];
65
+
66
+ const users = new Qar(data);
67
+
68
+ // Find all users older than 25
69
+ users.find({ age: { $gt: 25 } }).toArray();
70
+ // => [{ _id: 2, ... }, { _id: 3, ... }]
71
+
72
+ // Find one admin
73
+ users.findOne({ role: 'admin' });
74
+ // => { _id: 2, name: 'Bob', age: 30, role: 'admin' }
75
+
76
+ // Count users
77
+ users.count({ role: 'user' });
78
+ // => 2
79
+
80
+ // Check if exists
81
+ users.exists({ name: 'Alice' });
82
+ // => true
83
+ ```
84
+
85
+ ## API Reference
86
+
87
+ ### Constructor
88
+
89
+ ```javascript
90
+ const collection = new Qar(arrayOfObjects);
91
+ ```
92
+
93
+ Creates a new Qar instance with your array of objects.
94
+
95
+ ### Methods
96
+
97
+ #### `find(query, projection?)`
98
+
99
+ Returns a cursor for chaining operations. Call `.toArray()` to execute.
100
+
101
+ ```javascript
102
+ // Basic query
103
+ users.find({ age: { $gte: 30 } }).toArray();
104
+
105
+ // With projection
106
+ users.find({ role: 'admin' }, { name: 1, email: 1 }).toArray();
107
+
108
+ // Chainable
109
+ users.find({ active: true }).sort({ name: 1 }).skip(10).limit(5).toArray();
110
+ ```
111
+
112
+ #### `findOne(query, projection?)`
113
+
114
+ Returns the first object matching the query, or `null` if not found.
115
+
116
+ ```javascript
117
+ users.findOne({ name: 'Alice' });
118
+ // => { _id: 1, name: 'Alice', ... } or null
119
+
120
+ users.findOne({ role: 'admin' }, { name: 1, email: 1 });
121
+ // => { name: 'Bob', email: 'bob@example.com' }
122
+ ```
123
+
124
+ #### `count(query)`
125
+
126
+ Returns the number of objects matching the query.
127
+
128
+ ```javascript
129
+ users.count({ role: 'admin' });
130
+ // => 1
131
+ ```
132
+
133
+ #### `exists(query)`
134
+
135
+ Returns `true` if at least one object matches the query, `false` otherwise.
136
+
137
+ ```javascript
138
+ users.exists({ age: { $lt: 18 } });
139
+ // => false
140
+ ```
141
+
142
+ #### `distinct(field)`
143
+
144
+ Returns an array of unique values for the specified field.
145
+
146
+ ```javascript
147
+ users.distinct('role');
148
+ // => ['user', 'admin']
149
+
150
+ products.distinct('category');
151
+ // => ['electronics', 'clothing', 'books']
152
+ ```
153
+
154
+ #### `aggregate(pipeline)`
155
+
156
+ Execute an aggregation pipeline. See [Aggregation Pipeline](#aggregation-pipeline) section.
157
+
158
+ ```javascript
159
+ users.aggregate([{ $match: { age: { $gte: 18 } } }, { $group: { _id: '$role', count: { $sum: 1 } } }]);
160
+ ```
161
+
162
+ #### `toArray()`
163
+
164
+ Returns a shallow copy of the raw array.
165
+
166
+ ```javascript
167
+ users.toArray();
168
+ // => [{ ... }, { ... }, { ... }]
169
+ ```
170
+
171
+ ### Cursor Methods
172
+
173
+ When you call `find()`, you get a cursor that supports chaining:
174
+
175
+ #### `sort(spec)`
176
+
177
+ Sort results by one or more fields.
178
+
179
+ ```javascript
180
+ // Single field, ascending
181
+ users.find({}).sort({ name: 1 }).toArray();
182
+
183
+ // Single field, descending
184
+ users.find({}).sort({ age: -1 }).toArray();
185
+
186
+ // Multiple fields
187
+ users.find({}).sort({ role: 1, age: -1 }).toArray();
188
+ ```
189
+
190
+ #### `skip(n)`
191
+
192
+ Skip the first `n` results (useful for pagination).
193
+
194
+ ```javascript
195
+ users.find({}).skip(10).toArray();
196
+ ```
197
+
198
+ #### `limit(n)`
199
+
200
+ Limit results to `n` items.
201
+
202
+ ```javascript
203
+ users.find({}).limit(5).toArray();
204
+ ```
205
+
206
+ #### `project(spec)`
207
+
208
+ Apply field projection (alternative to passing projection to `find()`).
209
+
210
+ ```javascript
211
+ users.find({}).project({ name: 1, email: 1 }).toArray();
212
+ ```
213
+
214
+ #### `toArray()`
215
+
216
+ Execute the query and return results as an array.
217
+
218
+ ```javascript
219
+ users.find({ active: true }).sort({ createdAt: -1 }).limit(10).toArray();
220
+ ```
221
+
222
+ ## Query Operators
223
+
224
+ ### Comparison Operators
225
+
226
+ | Operator | Description | Example |
227
+ | -------- | --------------------- | -------------------------------------------- |
228
+ | `$eq` | Equal to | `{ age: { $eq: 30 } }` or `{ age: 30 }` |
229
+ | `$ne` | Not equal to | `{ role: { $ne: 'admin' } }` |
230
+ | `$gt` | Greater than | `{ price: { $gt: 100 } }` |
231
+ | `$gte` | Greater than or equal | `{ age: { $gte: 18 } }` |
232
+ | `$lt` | Less than | `{ stock: { $lt: 10 } }` |
233
+ | `$lte` | Less than or equal | `{ rating: { $lte: 3 } }` |
234
+ | `$in` | In array | `{ status: { $in: ['active', 'pending'] } }` |
235
+ | `$nin` | Not in array | `{ role: { $nin: ['guest', 'banned'] } }` |
236
+
237
+ ### Logical Operators
238
+
239
+ | Operator | Description | Example |
240
+ | -------- | ------------------------------ | ----------------------------------------------------- |
241
+ | `$and` | All conditions must match | `{ $and: [{ age: { $gt: 18 } }, { role: 'user' }] }` |
242
+ | `$or` | At least one condition matches | `{ $or: [{ role: 'admin' }, { role: 'moderator' }] }` |
243
+ | `$not` | Inverts the query | `{ $not: { age: { $lt: 18 } } }` |
244
+ | `$nor` | None of the conditions match | `{ $nor: [{ status: 'deleted' }, { banned: true }] }` |
245
+
246
+ ### Element Operators
247
+
248
+ | Operator | Description | Example |
249
+ | --------- | --------------------- | ------------------------------ |
250
+ | `$exists` | Field exists (or not) | `{ email: { $exists: true } }` |
251
+ | `$type` | Field type check | `{ age: { $type: 'number' } }` |
252
+
253
+ Type values: `'string'`, `'number'`, `'boolean'`, `'array'`, `'object'`, `'null'`, `'undefined'`, `'date'`
254
+
255
+ ### Array Operators
256
+
257
+ | Operator | Description | Example |
258
+ | ------------ | ------------------------------- | ---------------------------------------------------- |
259
+ | `$all` | Array contains all values | `{ tags: { $all: ['javascript', 'nodejs'] } }` |
260
+ | `$size` | Array has exact length | `{ tags: { $size: 3 } }` |
261
+ | `$elemMatch` | Array contains matching element | `{ items: { $elemMatch: { price: { $gt: 100 } } } }` |
262
+
263
+ ### Evaluation Operators
264
+
265
+ | Operator | Description | Example |
266
+ | -------- | ------------------------------- | -------------------------------------------- |
267
+ | `$regex` | Regular expression match | `{ name: { $regex: '^A', $options: 'i' } }` |
268
+ | `$mod` | Modulo operation | `{ qty: { $mod: [4, 0] } }` (divisible by 4) |
269
+ | `$expr` | Compare fields with expressions | `{ $expr: { $gt: ['$price', '$cost'] } }` |
270
+
271
+ ### Expression Operators (used in $expr)
272
+
273
+ #### Comparison
274
+
275
+ `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`
276
+
277
+ ```javascript
278
+ // Find products where price is less than MSRP
279
+ products.find({ $expr: { $lt: ['$price', '$msrp'] } });
280
+ ```
281
+
282
+ #### Arithmetic
283
+
284
+ `$add`, `$subtract`, `$multiply`, `$divide`
285
+
286
+ ```javascript
287
+ // Total value greater than 1000
288
+ products.find({
289
+ $expr: { $gt: [{ $multiply: ['$price', '$quantity'] }, 1000] },
290
+ });
291
+ ```
292
+
293
+ #### String
294
+
295
+ `$concat`, `$toLower`, `$toUpper`, `$substr`, `$strLenCP`, `$indexOfCP`, `$split`
296
+
297
+ ```javascript
298
+ // Concatenate fields
299
+ users.find({
300
+ $expr: { $eq: [{ $concat: ['$firstName', ' ', '$lastName'] }, 'John Doe'] },
301
+ });
302
+ ```
303
+
304
+ #### Array
305
+
306
+ `$size`, `$arrayElemAt`, `$filter`, `$map`, `$reduce`
307
+
308
+ ```javascript
309
+ // Products with more than 5 tags
310
+ products.find({ $expr: { $gt: [{ $size: '$tags' }, 5] } });
311
+ ```
312
+
313
+ #### Date
314
+
315
+ `$year`, `$month`, `$dayOfMonth`, `$hour`, `$minute`, `$second`
316
+
317
+ ```javascript
318
+ // Posts from 2024
319
+ posts.find({ $expr: { $eq: [{ $year: '$publishDate' }, 2024] } });
320
+ ```
321
+
322
+ #### Conditional
323
+
324
+ `$cond`, `$ifNull`, `$switch`
325
+
326
+ ```javascript
327
+ // Using $cond (if-then-else)
328
+ products.find({
329
+ $expr: {
330
+ $cond: [{ $gt: ['$stock', 0] }, { $eq: ['$status', 'available'] }, { $eq: ['$status', 'sold-out'] }],
331
+ },
332
+ });
333
+ ```
334
+
335
+ #### Type Conversion
336
+
337
+ `$toString`, `$toInt`, `$toDouble`, `$toDate`
338
+
339
+ ### Nested Properties
340
+
341
+ Use dot notation to query nested objects:
342
+
343
+ ```javascript
344
+ const data = [
345
+ { name: 'Alice', address: { city: 'New York', zip: '10001' } },
346
+ { name: 'Bob', address: { city: 'Los Angeles', zip: '90001' } },
347
+ ];
348
+
349
+ const collection = new Qar(data);
350
+
351
+ collection.find({ 'address.city': 'New York' }).toArray();
352
+ // => [{ name: 'Alice', ... }]
353
+ ```
354
+
355
+ ## Field Projection
356
+
357
+ Control which fields are returned in results.
358
+
359
+ ### Include/Exclude Fields
360
+
361
+ ```javascript
362
+ // Include only specific fields
363
+ users.find({}, { name: 1, email: 1 });
364
+ // => [{ _id: 1, name: 'Alice', email: 'alice@...' }, ...]
365
+
366
+ // Exclude _id
367
+ users.find({}, { name: 1, email: 1, _id: 0 });
368
+ // => [{ name: 'Alice', email: 'alice@...' }, ...]
369
+
370
+ // Exclude specific fields
371
+ users.find({}, { password: 0, secretKey: 0 });
372
+ // => All fields except password and secretKey
373
+ ```
374
+
375
+ ### Array Slicing with $slice
376
+
377
+ Limit array elements returned:
378
+
379
+ ```javascript
380
+ // Get only first 3 comments
381
+ posts.find({}, { title: 1, comments: { $slice: 3 } });
382
+
383
+ // Get last 5 items
384
+ posts.find({}, { title: 1, recentViews: { $slice: -5 } });
385
+
386
+ // Multiple slices
387
+ posts.find(
388
+ {},
389
+ {
390
+ title: 1,
391
+ comments: { $slice: 3 },
392
+ tags: { $slice: 5 },
393
+ },
394
+ );
395
+ ```
396
+
397
+ ### Positional Operator ($)
398
+
399
+ Return only the first matching array element:
400
+
401
+ ```javascript
402
+ // Only return the first comment by Alice
403
+ posts.find({ 'comments.author': 'Alice' }, { 'comments.$': 1 });
404
+ ```
405
+
406
+ ### $elemMatch Projection
407
+
408
+ Return only the first array element matching a condition:
409
+
410
+ ```javascript
411
+ // Only return comments with more than 10 likes
412
+ posts.find(
413
+ {},
414
+ {
415
+ title: 1,
416
+ comments: { $elemMatch: { likes: { $gt: 10 } } },
417
+ },
418
+ );
419
+ ```
420
+
421
+ ## Aggregation Pipeline
422
+
423
+ Perform advanced data processing with aggregation pipelines.
424
+
425
+ ### Stages
426
+
427
+ #### $match
428
+
429
+ Filter documents (same syntax as `find()`):
430
+
431
+ ```javascript
432
+ users.aggregate([{ $match: { age: { $gte: 18 } } }]);
433
+ ```
434
+
435
+ #### $group
436
+
437
+ Group documents and calculate aggregated values:
438
+
439
+ ```javascript
440
+ // Count users by role
441
+ users.aggregate([
442
+ {
443
+ $group: {
444
+ _id: '$role',
445
+ count: { $sum: 1 },
446
+ },
447
+ },
448
+ ]);
449
+
450
+ // Multiple aggregations
451
+ products.aggregate([
452
+ {
453
+ $group: {
454
+ _id: '$category',
455
+ totalProducts: { $sum: 1 },
456
+ avgPrice: { $avg: '$price' },
457
+ maxPrice: { $max: '$price' },
458
+ minPrice: { $min: '$price' },
459
+ },
460
+ },
461
+ ]);
462
+ ```
463
+
464
+ **Accumulator Operators:**
465
+
466
+ - `$sum` - Sum values
467
+ - `$avg` - Calculate average
468
+ - `$max` - Maximum value
469
+ - `$min` - Minimum value
470
+ - `$push` - Build array of values
471
+ - `$first` - First value in group
472
+ - `$last` - Last value in group
473
+
474
+ #### $sort
475
+
476
+ Sort documents:
477
+
478
+ ```javascript
479
+ users.aggregate([{ $group: { _id: '$city', count: { $sum: 1 } } }, { $sort: { count: -1 } }]);
480
+ ```
481
+
482
+ #### $project
483
+
484
+ Reshape documents and create computed fields:
485
+
486
+ ```javascript
487
+ users.aggregate([
488
+ {
489
+ $project: {
490
+ name: 1,
491
+ email: 1,
492
+ fullName: { $concat: ['$firstName', ' ', '$lastName'] },
493
+ },
494
+ },
495
+ ]);
496
+ ```
497
+
498
+ #### $limit
499
+
500
+ Limit number of documents:
501
+
502
+ ```javascript
503
+ users.aggregate([{ $group: { _id: '$role', count: { $sum: 1 } } }, { $sort: { count: -1 } }, { $limit: 5 }]);
504
+ ```
505
+
506
+ #### $unwind
507
+
508
+ Deconstruct an array field into separate documents:
509
+
510
+ ```javascript
511
+ const posts = new Qar([
512
+ { title: 'Post 1', tags: ['js', 'node'] },
513
+ { title: 'Post 2', tags: ['react', 'js'] },
514
+ ]);
515
+
516
+ posts.aggregate([{ $unwind: '$tags' }, { $group: { _id: '$tags', count: { $sum: 1 } } }]);
517
+ // => [{ _id: 'js', count: 2 }, { _id: 'node', count: 1 }, { _id: 'react', count: 1 }]
518
+ ```
519
+
520
+ ### Complete Example
521
+
522
+ ```javascript
523
+ // Analyze product sales by category
524
+ const salesReport = products.aggregate([
525
+ // Filter in-stock products
526
+ { $match: { inStock: true } },
527
+
528
+ // Calculate total value per product
529
+ {
530
+ $project: {
531
+ category: 1,
532
+ totalValue: { $multiply: ['$price', '$quantity'] },
533
+ },
534
+ },
535
+
536
+ // Group by category
537
+ {
538
+ $group: {
539
+ _id: '$category',
540
+ totalRevenue: { $sum: '$totalValue' },
541
+ productCount: { $sum: 1 },
542
+ avgValue: { $avg: '$totalValue' },
543
+ },
544
+ },
545
+
546
+ // Sort by revenue
547
+ { $sort: { totalRevenue: -1 } },
548
+
549
+ // Top 10 categories
550
+ { $limit: 10 },
551
+ ]);
552
+ ```
553
+
554
+ ## Use Cases
555
+
556
+ ### Next.js Static Data
557
+
558
+ Perfect for querying JSON data in Next.js applications:
559
+
560
+ ```javascript
561
+ // app/products/page.tsx
562
+ import productsData from '@/data/products.json';
563
+ import { Qar } from 'qarjs';
564
+
565
+ const products = new Qar(productsData);
566
+
567
+ export default function ProductsPage({ searchParams }) {
568
+ const filtered = products
569
+ .find({
570
+ category: searchParams.category,
571
+ price: { $lte: Number(searchParams.maxPrice) || 1000 },
572
+ inStock: true,
573
+ })
574
+ .sort({ price: 1 })
575
+ .skip((Number(searchParams.page) - 1) * 20)
576
+ .limit(20)
577
+ .toArray();
578
+
579
+ return <ProductList items={filtered} />;
580
+ }
581
+ ```
582
+
583
+ ### API Routes
584
+
585
+ ```javascript
586
+ // app/api/search/route.ts
587
+ import data from '@/data/items.json';
588
+ import { Qar } from 'qarjs';
589
+
590
+ const items = new Qar(data);
591
+
592
+ export async function GET(request) {
593
+ const q = request.nextUrl.searchParams.get('q');
594
+
595
+ const results = items
596
+ .find({
597
+ $or: [{ name: { $regex: q, $options: 'i' } }, { description: { $regex: q, $options: 'i' } }],
598
+ })
599
+ .limit(50)
600
+ .toArray();
601
+
602
+ return Response.json(results);
603
+ }
604
+ ```
605
+
606
+ ### Static Site Generation
607
+
608
+ ```javascript
609
+ import blogPosts from './posts.json';
610
+ import { Qar } from 'qarjs';
611
+
612
+ const posts = new Qar(blogPosts);
613
+
614
+ // Get published posts by category
615
+ const techPosts = posts
616
+ .find({
617
+ category: 'technology',
618
+ published: true,
619
+ publishDate: { $lte: new Date().toISOString() },
620
+ })
621
+ .sort({ publishDate: -1 })
622
+ .toArray();
623
+
624
+ // Get featured post
625
+ const featured = posts.findOne({ featured: true });
626
+
627
+ // Count drafts
628
+ const draftCount = posts.count({ published: false });
629
+
630
+ // Get all categories with post counts
631
+ const categories = posts.aggregate([
632
+ { $match: { published: true } },
633
+ { $group: { _id: '$category', count: { $sum: 1 } } },
634
+ { $sort: { count: -1 } },
635
+ ]);
636
+ ```
637
+
638
+ ## Why Qar over alternatives?
639
+
640
+ | Feature | Qar | mingo | sift.js | lodash |
641
+ | --------------------- | ------- | ---------- | ------- | -------- |
642
+ | MongoDB-style queries | ✅ | ✅ | ✅ | ❌ |
643
+ | Chainable API | ✅ | ⚠️ Limited | ❌ | ✅ |
644
+ | Aggregation pipeline | ✅ | ✅ | ❌ | ❌ |
645
+ | Field projection | ✅ | ✅ | ❌ | ❌ |
646
+ | Expression operators | ✅ 30+ | ✅ | ❌ | ❌ |
647
+ | Zero dependencies | ✅ | ❌ | ✅ | ❌ |
648
+ | Bundle size (gzipped) | ✅ ~5KB | ❌ ~15KB | ✅ ~2KB | ❌ ~24KB |
649
+
650
+ **Qar is perfect when you:**
651
+
652
+ - Work with static JSON data in Next.js or static sites
653
+ - Want MongoDB-style queries without a database
654
+ - Need aggregation and data transformation
655
+ - Want a clean, intuitive API
656
+ - Care about bundle size
657
+
658
+ **Not recommended for:**
659
+
660
+ - Very large datasets - use a real database
661
+ - Real-time data updates - use a database with subscriptions
662
+ - Complex JOIN operations across multiple collections
663
+
664
+ ## Examples
665
+
666
+ ### Complex Queries
667
+
668
+ ```javascript
669
+ const users = new Qar(userData);
670
+
671
+ // Find active admins or moderators over 25
672
+ users
673
+ .find({
674
+ $and: [{ age: { $gt: 25 } }, { status: 'active' }, { $or: [{ role: 'admin' }, { role: 'moderator' }] }],
675
+ })
676
+ .toArray();
677
+
678
+ // Users NOT in specific roles
679
+ users
680
+ .find({
681
+ $nor: [{ role: 'guest' }, { role: 'banned' }],
682
+ })
683
+ .toArray();
684
+
685
+ // Regex search (case-insensitive)
686
+ users
687
+ .find({
688
+ email: { $regex: '@gmail\\.com$', $options: 'i' },
689
+ })
690
+ .toArray();
691
+
692
+ // Compare fields
693
+ products
694
+ .find({
695
+ $expr: { $lt: ['$currentPrice', '$originalPrice'] },
696
+ })
697
+ .toArray();
698
+ ```
699
+
700
+ ### Pagination
701
+
702
+ ```javascript
703
+ const page = 2;
704
+ const pageSize = 20;
705
+
706
+ const results = products
707
+ .find({ category: 'electronics' })
708
+ .sort({ price: 1 })
709
+ .skip((page - 1) * pageSize)
710
+ .limit(pageSize)
711
+ .toArray();
712
+
713
+ const total = products.count({ category: 'electronics' });
714
+ const totalPages = Math.ceil(total / pageSize);
715
+ ```
716
+
717
+ ### Multiple Collections
718
+
719
+ ```javascript
720
+ const products = new Qar(productsData);
721
+ const orders = new Qar(ordersData);
722
+ const users = new Qar(usersData);
723
+
724
+ // Query each independently
725
+ const electronics = products.find({ category: 'electronics' }).toArray();
726
+ const recentOrders = orders.find({ date: { $gte: '2026-01-01' } }).toArray();
727
+ const admins = users.find({ role: 'admin' }).toArray();
728
+ ```
729
+
730
+ ## TypeScript
731
+
732
+ Full TypeScript support included:
733
+
734
+ ```typescript
735
+ import { Qar } from 'qarjs';
736
+
737
+ interface User {
738
+ id: number;
739
+ name: string;
740
+ age: number;
741
+ role: 'user' | 'admin';
742
+ }
743
+
744
+ const users = new Qar<User>(userData);
745
+
746
+ // Type-safe queries
747
+ const result = users.find({ age: { $gt: 25 } }).toArray(); // User[]
748
+ const admin = users.findOne({ role: 'admin' }); // User | null
749
+ const roles = users.distinct('role'); // string[]
750
+ ```
751
+
752
+ ## Browser Support
753
+
754
+ Works in all modern browsers and Node.js environments:
755
+
756
+ - Chrome/Edge: ✅
757
+ - Firefox: ✅
758
+ - Safari: ✅
759
+ - Node.js: ✅ (v14+)
760
+
761
+ ## Contributing
762
+
763
+ Contributions are welcome! Please open an issue or submit a pull request.
764
+
765
+ ## License
766
+
767
+ MIT © Zsolt Tövis
768
+
769
+ ---
770
+
771
+ Made with ❤️ for developers who love clean APIs and don't need a database for everything
package/dist/qar.cjs ADDED
@@ -0,0 +1 @@
1
+ var w=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var I=(e,t)=>{for(var i in t)w(e,i,{get:t[i],enumerable:!0})},V=(e,t,i,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of W(t))!R.call(e,r)&&r!==i&&w(e,r,{get:()=>t[r],enumerable:!(n=E(t,r))||n.enumerable});return e};var D=e=>V(w({},"__esModule",{value:!0}),e);var G={};I(G,{Qar:()=>P});module.exports=D(G);var s=e=>e==null?e===null?"null":"undefined":Array.isArray(e)?"array":e instanceof Date?"date":typeof e;var $=(e={},t="")=>t.includes(".")?Q(e,t):e[t],Q=(e={},t="")=>t?(e=e||{},t.split(".").reduce((r,o)=>{if(r!=null)return r[o]},e)):e,y=e=>{if(e==null)return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);if(e instanceof Map){let t=new Map;for(let[i,n]of e.entries())t.set(i,y(n));return t}if(e instanceof Set){let t=new Set;for(let i of e.values())t.add(y(i));return t}if(s(e)==="array")return e.map(t=>y(t));if(s(e)==="object"){let t={},i=Object.keys(e);for(let n=0;n<i.length;n++)t[i[n]]=y(e[i[n]]);return t}return e};var p=(e,t)=>{if(e instanceof Date)return e;if(s(e)==="string"){if(e.startsWith("$$")){let i=e.slice(2);return t&&t.__vars?t.__vars[i]:void 0}if(e.startsWith("$"))return $(t,e.slice(1))}return s(e)==="array"?e.map(i=>p(i,t)):s(e)==="object"?g(e,t):e},g=(e,t)=>{if(e==null||s(e)!=="object")return e;let i=Object.keys(e);if(i.length===0)return!1;let n=i[0];if(n==="$and"){let r=e[n];return s(r)!=="array"?!1:r.every(o=>!!g(o,t))}if(n==="$or"){let r=e[n];return s(r)!=="array"?!1:r.some(o=>!!g(o,t))}if(n==="$not")return!g(e[n],t);if(["$add","$subtract","$multiply","$divide"].includes(n)){let r=e[n];if(s(r)!=="array"||r.length<2)return!1;let o=p(r[0],t),a=p(r[1],t);switch(n){case"$add":return o+a;case"$subtract":return o-a;case"$multiply":return o*a;case"$divide":return o/a}}if(["$lt","$lte","$gt","$gte","$eq","$ne"].includes(n)){let r=e[n];if(s(r)!=="array"||r.length<2)return!1;let o=p(r[0],t),a=p(r[1],t);switch(n){case"$lt":return o<a;case"$lte":return o<=a;case"$gt":return o>a;case"$gte":return o>=a;case"$eq":return o===a;case"$ne":return o!==a}}if(n==="$in"){let r=e[n];if(s(r)!=="array"||r.length<2)return!1;let o=p(r[0],t),a=p(r[1],t);return s(a)==="array"&&a.includes(o)}if(n==="$strLenCP"){let r=p(e[n],t);return r==null?0:s(r)==="array"?r.length:Array.from(String(r)).length}if(n==="$indexOfCP"){let r=e[n];if(s(r)!=="array"||r.length<2)return-1;let o=String(p(r[0],t)),a=String(p(r[1],t)),f=r.length>2&&Number(p(r[2],t))||0;return o.indexOf(a,f)}if(n==="$split"){let r=e[n];if(s(r)!=="array"||r.length<2)return[];let o=String(p(r[0],t)),a=String(p(r[1],t));return o.split(a)}if(n==="$size"){let r=p(e[n],t);return s(r)==="array"?r.length:0}if(n==="$arrayElemAt"){let r=e[n];if(s(r)!=="array"||r.length<2)return;let o=p(r[0],t),a=Number(p(r[1],t));return s(o)!=="array"?void 0:a<0?o[o.length+a]:o[a]}if(n==="$filter"){let r=e[n];if(s(r)!=="object")return[];let o=p(r.input,t);if(s(o)!=="array")return[];let a=r.as||"this",f=r.cond;return o.filter((u,d)=>{let c=Object.assign({},u);return c.__vars=Object.assign({},t&&t.__vars?t.__vars:{},{[a]:u,index:d}),!!g(f,c)})}if(n==="$map"){let r=e[n];if(s(r)!=="object")return[];let o=p(r.input,t);if(s(o)!=="array")return[];let a=r.as||"this",f=r.in;return o.map((u,d)=>{let c=Object.assign({},u);return c.__vars=Object.assign({},t&&t.__vars?t.__vars:{},{[a]:u,index:d}),g(f,c)})}if(n==="$reduce"){let r=e[n];if(s(r)!=="object")return null;let o=p(r.input,t),a=r.in,f=p(r.initialValue,t);return s(o)!=="array"?f:o.reduce((u,d,c)=>{let l=Object.assign({},d);return l.__vars=Object.assign({},t&&t.__vars?t.__vars:{},{value:u,this:d,index:c}),g(a,l)},f)}if(n==="$year"||n==="$month"||n==="$dayOfMonth"||n==="$hour"||n==="$minute"||n==="$second"){let r=new Date(p(e[n],t));if(isNaN(r.getTime()))return null;switch(n){case"$year":return r.getUTCFullYear();case"$month":return r.getUTCMonth()+1;case"$dayOfMonth":return r.getUTCDate();case"$hour":return r.getUTCHours();case"$minute":return r.getUTCMinutes();case"$second":return r.getUTCSeconds()}}if(n==="$cond"){let r=e[n];if(s(r)==="array"&&r.length===3)return p(r[0],t)?p(r[1],t):p(r[2],t);if(s(r)==="object"){let{if:o,then:a,else:f}=r;return g(o,t)?g(a,t):g(f,t)}return null}if(n==="$ifNull"){let r=e[n];if(s(r)!=="array"||r.length<2)return p(r,t);let o=p(r[0],t);return o??p(r[1],t)}if(n==="$switch"){let r=e[n];if(s(r)!=="object")return null;let o=r.branches||[];for(let a of o)if(g(a.case,t))return g(a.then,t);return r.default!==void 0?g(r.default,t):null}if(n==="$toString")return p(e[n],t)==null?null:String(p(e[n],t));if(n==="$toInt")return Number.parseInt(p(e[n],t),10)||0;if(n==="$toDouble")return Number.parseFloat(p(e[n],t))||0;if(n==="$toDate"){let r=p(e[n],t),o=new Date(r);return isNaN(o.getTime())?null:o}if(n==="$concat"){let r=e[n];return s(r)!=="array"?"":r.map(o=>p(o,t)).join("")}if(n==="$toLower"){let r=p(e[n],t);return r==null?r:String(r).toLowerCase()}if(n==="$toUpper"){let r=p(e[n],t);return r==null?r:String(r).toUpperCase()}if(n==="$substr"){let r=e[n];if(s(r)!=="array"||r.length<3)return"";let o=String(p(r[0],t)),a=Number(p(r[1],t))||0,f=Number(p(r[2],t))||0;return o.substr(a,f)}return!1};var S=Object.freeze({$eq:(e,t)=>e===t,$ne:(e,t)=>e!==t,$in:(e,t)=>s(t)!=="array"?!1:s(e)==="array"?e.some(i=>t.includes(i)):t.includes(e),$nin:(e,t)=>s(t)!=="array"?!1:s(e)==="array"?!e.some(i=>t.includes(i)):!t.includes(e),$exists:(e,t)=>e!==void 0===t,$size:(e,t)=>s(e)==="array"&&e.length===t,$all:(e,t)=>s(e)!=="array"||s(t)!=="array"?!1:t.every(i=>e.includes(i)),$elemMatch:(e,t)=>{if(s(e)!=="array"||s(t)!=="object")return!1;let i=Object.keys(t);return i.length===0?!1:e.some(n=>{if(s(n)!=="object")return!1;for(let r of i){let o=$(n,r);if(!j(o,t[r]))return!1}return!0})},$type:(e,t)=>s(t)==="array"?t.some(i=>s(e)===i):s(e)===t,$mod:(e,t)=>{if(s(e)!=="number"||s(t)!=="array"||t.length<2)return!1;let i=t[0],n=t[1];return s(i)!=="number"||s(n)!=="number"?!1:e%i===n},$lt:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()<t.getTime():s(e)!==s(t)?!1:e<t,$lte:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()<=t.getTime():s(e)!==s(t)?!1:e<=t,$gt:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()>t.getTime():s(e)!==s(t)?!1:e>t,$gte:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()>=t.getTime():s(e)!==s(t)?!1:e>=t,$regex:(e,t,i)=>{try{return new RegExp(t,i).test(String(e))}catch(n){return console.warn("[applyQuery] invalid $regex pattern:",t,"error:",n&&n.message),!1}}}),j=(e,t)=>{if(s(t)!=="object")return e===t;let i=Object.keys(t).filter(n=>Object.prototype.hasOwnProperty.call(S,n));return i.length===0?(console.warn("[applyQuery] no recognized operators in condition:",t),!1):i.every(n=>n==="$regex"?S[n](e,t[n],t.$options):S[n](e,t[n]))},b=(e,t)=>{if(s(t)!=="object")return!1;if(s(t.$and)==="array")return t.$and.every(i=>b(e,i));if(s(t.$or)==="array")return t.$or.some(i=>b(e,i));if(s(t.$not)==="object")return!b(e,t.$not);if(s(t.$nor)==="array")return!t.$nor.some(i=>b(e,i));if(t.$expr!==void 0)return!!g(t.$expr,e);for(let i of Object.keys(t)){let n=t[i],r=$(e,i);if((i==="$regex"||s(n)==="object"&&Object.prototype.hasOwnProperty.call(n,"$regex"))&&s(r)!=="string")return console.warn("[applyQuery] $regex used on non-string value, skipping match"),!1;if(!j(r,n))return!1}return!0},_=(e=[],t,i={})=>{let n=e||[];if(s(t)!=="object")return n;t.$and!==void 0&&s(t.$and)!=="array"&&console.warn("[applyQuery] $and should be an array, ignoring as combinator:",t.$and),t.$or!==void 0&&s(t.$or)!=="array"&&console.warn("[applyQuery] $or should be an array, ignoring as combinator:",t.$or),t.$nor!==void 0&&s(t.$nor)!=="array"&&console.warn("[applyQuery] $nor should be an array, ignoring as combinator:",t.$nor),t.$not!==void 0&&s(t.$not)!=="object"&&console.warn("[applyQuery] $not should be an object, ignoring as combinator:",t.$not);let r=i&&i.indexes;if(r&&s(r)==="object"){let o=Object.keys(t).filter(a=>!["$and","$or","$not","$nor"].includes(a));if(o.length===1){let a=o[0],f=t[a],u=s(f)!=="object"?f:Object.prototype.hasOwnProperty.call(f,"$eq")?f.$eq:void 0;if(u!==void 0&&r[a]instanceof Map)return(r[a].get(u)||[]).filter(h=>b(h,t))}}return n.filter(o=>b(o,t))};var U=(e,t)=>{let i=f=>s(f)==="string"?{path:f,preserveNull:!1}:s(f)==="object"?{path:f.path||f.$path||f.field,preserveNull:!!f.preserveNullAndEmptyArrays}:{path:void 0,preserveNull:!1},{path:n,preserveNull:r}=i(t);if(!n)return e;let o=n.startsWith("$")?n.slice(1):n,a=[];for(let f of e){let u=$(f??{},o);if(s(u)!=="array"){if(r){let d=y(f),c=o.split("."),l=N(d,c);l!=null&&(l[c[c.length-1]]=null),a.push(d)}continue}for(let d of u){let c=y(f),l=o.split("."),h=N(c,l);h!=null&&(h[l[l.length-1]]=d),a.push(c)}}return a},N=(e,t)=>{let i=e;for(let n of t.slice(0,-1)){if(i==null)return i;(!Object.prototype.hasOwnProperty.call(i,n)||s(i[n])!=="object")&&(i[n]={}),i=i[n]}return i},B=(e,t)=>{let i=new Map;for(let r of e){let o=t._id===null?null:s(t._id)==="string"&&t._id.startsWith("$")?$(r,t._id.slice(1)):s(t._id)==="object"?g(t._id,r):t._id,a=s(o)==="object"?JSON.stringify(o):String(o);if(!i.has(a)){let u={_id:o};for(let[d,c]of Object.entries(t)){if(d==="_id")continue;let l=Object.keys(c)[0];l==="$sum"?u[d]=0:l==="$push"?u[d]=[]:l==="$avg"?u[d]={sum:0,cnt:0}:l==="$max"||l==="$min"||l==="$first"||l==="$last"?u[d]=void 0:u[d]=null}i.set(a,u)}let f=i.get(a);for(let[u,d]of Object.entries(t)){if(u==="_id")continue;let c=Object.keys(d)[0],l=d[c];if(c==="$sum"){let h=s(l)==="number"?l:s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u]=(f[u]||0)+(Number(h)||0)}else if(c==="$push"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u].push(h)}else if(c==="$avg"){let h=s(l)==="number"?l:s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r),m=Number(h);Number.isNaN(m)||(f[u].sum+=m,f[u].cnt+=1)}else if(c==="$max"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);(f[u]===void 0||h>f[u])&&(f[u]=h)}else if(c==="$min"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);(f[u]===void 0||h<f[u])&&(f[u]=h)}else if(c==="$first"){if(f[u]===void 0){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u]=h}}else if(c==="$last"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u]=h}}}let n=[];for(let r of i.values()){for(let[o,a]of Object.entries(r)){let f=t[o];if(!f)continue;Object.keys(f)[0]==="$avg"&&(r[o]=a.cnt===0?null:a.sum/a.cnt)}n.push(r)}return n},z=(e,t)=>{if(s(t)!=="object")return e;let i=Object.keys(t);return e.slice().sort((n,r)=>{for(let o of i){let a=t[o]===-1?-1:1,f=$(n,o),u=$(r,o);if(!(f==null&&u==null)){if(f==null)return-1*a;if(u==null)return 1*a;if(f<u)return-1*a;if(f>u)return 1*a}}return 0})},F=(e,t)=>e.map(i=>{if(s(t)!=="object")return{...i};let n=Object.keys(t).some(o=>t[o]===1),r={};if(n){for(let[o,a]of Object.entries(t))if(a===1){let f=$(i,o);f!==void 0&&(r[o]=f)}else s(a)==="string"&&a.startsWith("$")?r[o]=$(i,a.slice(1)):s(a)==="object"&&(r[o]=g(a,i));return r}for(let o of Object.keys(i))r[o]=i[o];for(let[o,a]of Object.entries(t))a===0?delete r[o]:s(a)==="string"&&a.startsWith("$")?r[o]=$(i,a.slice(1)):s(a)==="object"&&(r[o]=g(a,i));return r}),L=(e,t)=>{let i=Math.max(0,parseInt(t,10)||0);return e.slice(0,i)},C=(e=[],t=[])=>{let i=s(e)==="array"?e.slice():[];return t.reduce((n,r)=>{let o=Object.keys(r)[0],a=r[o];return o==="$match"?_(n,a):o==="$unwind"?U(n,a):o==="$group"?B(n,a):o==="$sort"?z(n,a):o==="$project"?F(n,a):o==="$limit"?L(n,a):n},i)};var v=(e,t,i)=>{let n=t.split("."),r=n[n.length-1],o=n.slice(0,-1).reduce((a,f)=>a==null?a:((!Object.prototype.hasOwnProperty.call(a,f)||s(a[f])!=="object")&&(a[f]={}),a[f]),e);o!=null&&(o[r]=i)},O=(e,t)=>t.split(".").reduce((n,r)=>{if(!(n==null||!Object.prototype.hasOwnProperty.call(n,r)))return n[r]},e),M=(e,t,i=[])=>{if(!e||s(e)!=="object")return i;if(s(e.$and)==="array"){for(let n of e.$and)M(n,t,i);return i}for(let n of Object.keys(e))if(!(n==="$and"||n==="$or"||n==="$not"||n==="$nor")&&(n===t&&e[n]&&s(e[n])==="object"&&Object.prototype.hasOwnProperty.call(e[n],"$elemMatch")&&i.push({type:"elemMatch",cond:e[n].$elemMatch}),n.startsWith(t+"."))){let r=n.slice(t.length+1);i.push({type:"subfield",subfield:r,cond:e[n]})}return i},H=(e,t)=>{let i=M(e,t);return i.length===0?null:n=>{for(let r of i)if(r.type==="elemMatch"){if(s(r.cond)!=="object")return!1;let o=Object.keys(r.cond);if(o.length===0)return!1;for(let a of o){let f=$(n,a);if(!j(f,r.cond[a]))return!1}}else if(r.type==="subfield"){let o=$(n,r.subfield);if(!j(o,r.cond))return!1}return!0}},J=(e,t)=>{if(!t||s(t)!=="object")return!1;let i=Object.keys(t);if(i.length===0)return!1;for(let n of i){let r=$(e,n);if(!j(r,t[n]))return!1}return!0},Y=(e={},t,i=null)=>{if(!t||s(t)!=="object")return{...e};let n=Object.keys(t),r=n.some(u=>t[u]===0),a=n.some(u=>{let d=t[u];return d===1?!0:s(d)==="object"?Object.prototype.hasOwnProperty.call(d,"$slice")||Object.prototype.hasOwnProperty.call(d,"$elemMatch"):!1})?"include":"exclude",f={};if(a==="include"){for(let u of n){if(u==="_id"&&t._id===0)continue;let d=t[u];if(u.endsWith(".$")){let c=u.slice(0,-2),l=O(e,c);if(s(l)==="array"){let h=H(i,c);if(h){let m=l.find(A=>h(A));m!==void 0&&v(f,c,[m])}}continue}if(d===1){let c=O(e,u);c!==void 0&&v(f,u,c)}else if(s(d)==="object"&&Object.prototype.hasOwnProperty.call(d,"$elemMatch")){let c=O(e,u);if(s(c)==="array"){let l=c.find(h=>J(h,d.$elemMatch));l!==void 0&&v(f,u,[l])}}else if(s(d)==="object"&&Object.prototype.hasOwnProperty.call(d,"$slice")){let c=O(e,u);if(c!==void 0){let l=d.$slice,h=s(c)==="array"?s(l)!=="number"||l===0?[]:l>0?c.slice(0,l):c.slice(l):c;v(f,u,h)}}}return t._id!==0&&Object.prototype.hasOwnProperty.call(e,"_id")&&(f._id=e._id),f}for(let u of Object.keys(e))f[u]=e[u];for(let u of n)if(t[u]===0){let c=u.split(".");if(c.length===1)delete f[u];else{let l=c.pop(),h=c.join("."),m=O(f,h);m&&Object.prototype.hasOwnProperty.call(m,l)&&delete m[l]}}return f},T=(e=[],t,i=null)=>s(e)!=="array"?e:!t||s(t)!=="object"?e.map(n=>({...n})):e.map(n=>Y(n,t,i));var k=class e{constructor(t=[],i={},n=null){this._items=t||[],this._query=i||{},this._projection=n||null,this._sortSpec=null,this._skip=0,this._limit=null}sort(t={}){return this._sortSpec=t&&s(t)==="object"?t:null,this}project(t=null){return this._projection=t&&s(t)==="object"?t:this._projection,this}skip(t=0){return this._skip=Math.max(0,parseInt(String(t),10)||0),this}limit(t){return t==null?this.toArray():(this._limit=Math.max(0,parseInt(String(t),10)||0),this)}toArray(){let t=_(this._items,this._query),i=this._sortSpec?(()=>{let a=Object.keys(this._sortSpec);return t.slice().sort((f,u)=>{for(let d of a){let c=this._sortSpec[d]===-1?-1:1,l=$(f,d),h=$(u,d);if(!(l==null&&h==null)){if(l==null)return-1*c;if(h==null)return 1*c;if(l<h)return-1*c;if(l>h)return 1*c}}return 0})})():t,n=this._skip&&this._skip>0?i.slice(this._skip):i,r=this._limit!=null?n.slice(0,this._limit):n;return this._projection?T(r,this._projection,this._query):r}static from(t,i={},n=null){return new e(t,i,n)}};var P=class{constructor(t=[]){if(s(t)!=="array")throw new TypeError("Items must be an array of objects");this._items=t}find(t={},i){return k.from(this._items,t,i)}findOne(t={},i){let n=this.find(t,i).toArray();return n.length>0?n[0]:null}count(t={}){return this.find(t).toArray().length}exists(t={}){return this.find(t).toArray().length>0}distinct(t){if(!t)return[];let i=new Set;for(let n of this._items){let r=s(t)==="string"&&t.startsWith("$")?$(n,t.slice(1)):$(n,t);r!==void 0&&i.add(r)}return Array.from(i)}aggregate(t=[]){return C(this._items,t)}toArray(){return s(this._items)==="array"?[...this._items]:[]}};0&&(module.exports={Qar});
package/dist/qar.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ export type QueryOperators<T = any> = {
2
+ $eq?: T;
3
+ $ne?: T;
4
+ $lt?: T;
5
+ $lte?: T;
6
+ $gt?: T;
7
+ $gte?: T;
8
+ $in?: T[];
9
+ $nin?: T[];
10
+ $exists?: boolean;
11
+ $regex?: string | RegExp;
12
+ $options?: string;
13
+ $size?: number;
14
+ $all?: any[];
15
+ $elemMatch?: Record<string, any>;
16
+ $type?: string | string[];
17
+ $mod?: [number, number];
18
+ };
19
+
20
+ export type Query<T = any> = {
21
+ [K in keyof T]?: T[K] | QueryOperators<T[K]>;
22
+ } & {
23
+ $and?: Query<T>[];
24
+ $or?: Query<T>[];
25
+ $nor?: Query<T>[];
26
+ $not?: Query<T>;
27
+ $expr?: Record<string, any>;
28
+ };
29
+
30
+ export type Projection<T = any> =
31
+ | ({
32
+ [K in keyof T]?: 1 | 0 | { $slice?: number; $elemMatch?: Record<string, any> };
33
+ } & {
34
+ _id?: 0 | 1;
35
+ [key: string]: 1 | 0 | { $slice?: number; $elemMatch?: Record<string, any> } | undefined;
36
+ })
37
+ | null;
38
+
39
+ export type Pipeline = Array<
40
+ | { $match: Record<string, any> }
41
+ | { $group: Record<string, any> }
42
+ | { $sort: Record<string, 1 | -1> }
43
+ | { $project: Record<string, any> }
44
+ | { $limit: number }
45
+ | { $unwind: string | { path: string; preserveNullAndEmptyArrays?: boolean } }
46
+ | Record<string, any>
47
+ >;
48
+
49
+ export class QueryCursor<T = any> {
50
+ constructor(items?: T[], query?: Query<T>, projection?: Projection<T>);
51
+
52
+ sort(spec?: Record<string, 1 | -1>): this;
53
+ project(spec?: Projection<T>): this;
54
+ skip(n?: number): this;
55
+ limit(): T[];
56
+ limit(n: number): this;
57
+ limit(n?: number): this | T[];
58
+
59
+ toArray(): T[];
60
+
61
+ static from<T = any>(items: T[], query?: Query<T>, projection?: Projection<T>): QueryCursor<T>;
62
+ }
63
+
64
+ export class Qar<T = any> {
65
+ constructor(items?: T[]);
66
+
67
+ find(query?: Query<T>, projection?: Projection<T>): QueryCursor<T>;
68
+ findOne(query?: Query<T>, projection?: Projection<T>): T | null;
69
+ count(query?: Query<T>): number;
70
+ exists(query?: Query<T>): boolean;
71
+
72
+ distinct<K extends keyof T>(field: K): Array<T[K]>;
73
+ distinct(field: string): any[];
74
+
75
+ aggregate<R = any>(pipeline?: Pipeline): R[];
76
+ toArray(): T[];
77
+ }
78
+
79
+ export default Qar;
package/dist/qar.js ADDED
@@ -0,0 +1 @@
1
+ var s=e=>e==null?e===null?"null":"undefined":Array.isArray(e)?"array":e instanceof Date?"date":typeof e;var $=(e={},t="")=>t.includes(".")?A(e,t):e[t],A=(e={},t="")=>t?(e=e||{},t.split(".").reduce((r,o)=>{if(r!=null)return r[o]},e)):e,y=e=>{if(e==null)return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);if(e instanceof Map){let t=new Map;for(let[i,n]of e.entries())t.set(i,y(n));return t}if(e instanceof Set){let t=new Set;for(let i of e.values())t.add(y(i));return t}if(s(e)==="array")return e.map(t=>y(t));if(s(e)==="object"){let t={},i=Object.keys(e);for(let n=0;n<i.length;n++)t[i[n]]=y(e[i[n]]);return t}return e};var p=(e,t)=>{if(e instanceof Date)return e;if(s(e)==="string"){if(e.startsWith("$$")){let i=e.slice(2);return t&&t.__vars?t.__vars[i]:void 0}if(e.startsWith("$"))return $(t,e.slice(1))}return s(e)==="array"?e.map(i=>p(i,t)):s(e)==="object"?g(e,t):e},g=(e,t)=>{if(e==null||s(e)!=="object")return e;let i=Object.keys(e);if(i.length===0)return!1;let n=i[0];if(n==="$and"){let r=e[n];return s(r)!=="array"?!1:r.every(o=>!!g(o,t))}if(n==="$or"){let r=e[n];return s(r)!=="array"?!1:r.some(o=>!!g(o,t))}if(n==="$not")return!g(e[n],t);if(["$add","$subtract","$multiply","$divide"].includes(n)){let r=e[n];if(s(r)!=="array"||r.length<2)return!1;let o=p(r[0],t),a=p(r[1],t);switch(n){case"$add":return o+a;case"$subtract":return o-a;case"$multiply":return o*a;case"$divide":return o/a}}if(["$lt","$lte","$gt","$gte","$eq","$ne"].includes(n)){let r=e[n];if(s(r)!=="array"||r.length<2)return!1;let o=p(r[0],t),a=p(r[1],t);switch(n){case"$lt":return o<a;case"$lte":return o<=a;case"$gt":return o>a;case"$gte":return o>=a;case"$eq":return o===a;case"$ne":return o!==a}}if(n==="$in"){let r=e[n];if(s(r)!=="array"||r.length<2)return!1;let o=p(r[0],t),a=p(r[1],t);return s(a)==="array"&&a.includes(o)}if(n==="$strLenCP"){let r=p(e[n],t);return r==null?0:s(r)==="array"?r.length:Array.from(String(r)).length}if(n==="$indexOfCP"){let r=e[n];if(s(r)!=="array"||r.length<2)return-1;let o=String(p(r[0],t)),a=String(p(r[1],t)),f=r.length>2&&Number(p(r[2],t))||0;return o.indexOf(a,f)}if(n==="$split"){let r=e[n];if(s(r)!=="array"||r.length<2)return[];let o=String(p(r[0],t)),a=String(p(r[1],t));return o.split(a)}if(n==="$size"){let r=p(e[n],t);return s(r)==="array"?r.length:0}if(n==="$arrayElemAt"){let r=e[n];if(s(r)!=="array"||r.length<2)return;let o=p(r[0],t),a=Number(p(r[1],t));return s(o)!=="array"?void 0:a<0?o[o.length+a]:o[a]}if(n==="$filter"){let r=e[n];if(s(r)!=="object")return[];let o=p(r.input,t);if(s(o)!=="array")return[];let a=r.as||"this",f=r.cond;return o.filter((u,d)=>{let c=Object.assign({},u);return c.__vars=Object.assign({},t&&t.__vars?t.__vars:{},{[a]:u,index:d}),!!g(f,c)})}if(n==="$map"){let r=e[n];if(s(r)!=="object")return[];let o=p(r.input,t);if(s(o)!=="array")return[];let a=r.as||"this",f=r.in;return o.map((u,d)=>{let c=Object.assign({},u);return c.__vars=Object.assign({},t&&t.__vars?t.__vars:{},{[a]:u,index:d}),g(f,c)})}if(n==="$reduce"){let r=e[n];if(s(r)!=="object")return null;let o=p(r.input,t),a=r.in,f=p(r.initialValue,t);return s(o)!=="array"?f:o.reduce((u,d,c)=>{let l=Object.assign({},d);return l.__vars=Object.assign({},t&&t.__vars?t.__vars:{},{value:u,this:d,index:c}),g(a,l)},f)}if(n==="$year"||n==="$month"||n==="$dayOfMonth"||n==="$hour"||n==="$minute"||n==="$second"){let r=new Date(p(e[n],t));if(isNaN(r.getTime()))return null;switch(n){case"$year":return r.getUTCFullYear();case"$month":return r.getUTCMonth()+1;case"$dayOfMonth":return r.getUTCDate();case"$hour":return r.getUTCHours();case"$minute":return r.getUTCMinutes();case"$second":return r.getUTCSeconds()}}if(n==="$cond"){let r=e[n];if(s(r)==="array"&&r.length===3)return p(r[0],t)?p(r[1],t):p(r[2],t);if(s(r)==="object"){let{if:o,then:a,else:f}=r;return g(o,t)?g(a,t):g(f,t)}return null}if(n==="$ifNull"){let r=e[n];if(s(r)!=="array"||r.length<2)return p(r,t);let o=p(r[0],t);return o??p(r[1],t)}if(n==="$switch"){let r=e[n];if(s(r)!=="object")return null;let o=r.branches||[];for(let a of o)if(g(a.case,t))return g(a.then,t);return r.default!==void 0?g(r.default,t):null}if(n==="$toString")return p(e[n],t)==null?null:String(p(e[n],t));if(n==="$toInt")return Number.parseInt(p(e[n],t),10)||0;if(n==="$toDouble")return Number.parseFloat(p(e[n],t))||0;if(n==="$toDate"){let r=p(e[n],t),o=new Date(r);return isNaN(o.getTime())?null:o}if(n==="$concat"){let r=e[n];return s(r)!=="array"?"":r.map(o=>p(o,t)).join("")}if(n==="$toLower"){let r=p(e[n],t);return r==null?r:String(r).toLowerCase()}if(n==="$toUpper"){let r=p(e[n],t);return r==null?r:String(r).toUpperCase()}if(n==="$substr"){let r=e[n];if(s(r)!=="array"||r.length<3)return"";let o=String(p(r[0],t)),a=Number(p(r[1],t))||0,f=Number(p(r[2],t))||0;return o.substr(a,f)}return!1};var w=Object.freeze({$eq:(e,t)=>e===t,$ne:(e,t)=>e!==t,$in:(e,t)=>s(t)!=="array"?!1:s(e)==="array"?e.some(i=>t.includes(i)):t.includes(e),$nin:(e,t)=>s(t)!=="array"?!1:s(e)==="array"?!e.some(i=>t.includes(i)):!t.includes(e),$exists:(e,t)=>e!==void 0===t,$size:(e,t)=>s(e)==="array"&&e.length===t,$all:(e,t)=>s(e)!=="array"||s(t)!=="array"?!1:t.every(i=>e.includes(i)),$elemMatch:(e,t)=>{if(s(e)!=="array"||s(t)!=="object")return!1;let i=Object.keys(t);return i.length===0?!1:e.some(n=>{if(s(n)!=="object")return!1;for(let r of i){let o=$(n,r);if(!j(o,t[r]))return!1}return!0})},$type:(e,t)=>s(t)==="array"?t.some(i=>s(e)===i):s(e)===t,$mod:(e,t)=>{if(s(e)!=="number"||s(t)!=="array"||t.length<2)return!1;let i=t[0],n=t[1];return s(i)!=="number"||s(n)!=="number"?!1:e%i===n},$lt:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()<t.getTime():s(e)!==s(t)?!1:e<t,$lte:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()<=t.getTime():s(e)!==s(t)?!1:e<=t,$gt:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()>t.getTime():s(e)!==s(t)?!1:e>t,$gte:(e,t)=>s(e)==="date"&&s(t)==="date"?e.getTime()>=t.getTime():s(e)!==s(t)?!1:e>=t,$regex:(e,t,i)=>{try{return new RegExp(t,i).test(String(e))}catch(n){return console.warn("[applyQuery] invalid $regex pattern:",t,"error:",n&&n.message),!1}}}),j=(e,t)=>{if(s(t)!=="object")return e===t;let i=Object.keys(t).filter(n=>Object.prototype.hasOwnProperty.call(w,n));return i.length===0?(console.warn("[applyQuery] no recognized operators in condition:",t),!1):i.every(n=>n==="$regex"?w[n](e,t[n],t.$options):w[n](e,t[n]))},b=(e,t)=>{if(s(t)!=="object")return!1;if(s(t.$and)==="array")return t.$and.every(i=>b(e,i));if(s(t.$or)==="array")return t.$or.some(i=>b(e,i));if(s(t.$not)==="object")return!b(e,t.$not);if(s(t.$nor)==="array")return!t.$nor.some(i=>b(e,i));if(t.$expr!==void 0)return!!g(t.$expr,e);for(let i of Object.keys(t)){let n=t[i],r=$(e,i);if((i==="$regex"||s(n)==="object"&&Object.prototype.hasOwnProperty.call(n,"$regex"))&&s(r)!=="string")return console.warn("[applyQuery] $regex used on non-string value, skipping match"),!1;if(!j(r,n))return!1}return!0},_=(e=[],t,i={})=>{let n=e||[];if(s(t)!=="object")return n;t.$and!==void 0&&s(t.$and)!=="array"&&console.warn("[applyQuery] $and should be an array, ignoring as combinator:",t.$and),t.$or!==void 0&&s(t.$or)!=="array"&&console.warn("[applyQuery] $or should be an array, ignoring as combinator:",t.$or),t.$nor!==void 0&&s(t.$nor)!=="array"&&console.warn("[applyQuery] $nor should be an array, ignoring as combinator:",t.$nor),t.$not!==void 0&&s(t.$not)!=="object"&&console.warn("[applyQuery] $not should be an object, ignoring as combinator:",t.$not);let r=i&&i.indexes;if(r&&s(r)==="object"){let o=Object.keys(t).filter(a=>!["$and","$or","$not","$nor"].includes(a));if(o.length===1){let a=o[0],f=t[a],u=s(f)!=="object"?f:Object.prototype.hasOwnProperty.call(f,"$eq")?f.$eq:void 0;if(u!==void 0&&r[a]instanceof Map)return(r[a].get(u)||[]).filter(h=>b(h,t))}}return n.filter(o=>b(o,t))};var E=(e,t)=>{let i=f=>s(f)==="string"?{path:f,preserveNull:!1}:s(f)==="object"?{path:f.path||f.$path||f.field,preserveNull:!!f.preserveNullAndEmptyArrays}:{path:void 0,preserveNull:!1},{path:n,preserveNull:r}=i(t);if(!n)return e;let o=n.startsWith("$")?n.slice(1):n,a=[];for(let f of e){let u=$(f??{},o);if(s(u)!=="array"){if(r){let d=y(f),c=o.split("."),l=S(d,c);l!=null&&(l[c[c.length-1]]=null),a.push(d)}continue}for(let d of u){let c=y(f),l=o.split("."),h=S(c,l);h!=null&&(h[l[l.length-1]]=d),a.push(c)}}return a},S=(e,t)=>{let i=e;for(let n of t.slice(0,-1)){if(i==null)return i;(!Object.prototype.hasOwnProperty.call(i,n)||s(i[n])!=="object")&&(i[n]={}),i=i[n]}return i},W=(e,t)=>{let i=new Map;for(let r of e){let o=t._id===null?null:s(t._id)==="string"&&t._id.startsWith("$")?$(r,t._id.slice(1)):s(t._id)==="object"?g(t._id,r):t._id,a=s(o)==="object"?JSON.stringify(o):String(o);if(!i.has(a)){let u={_id:o};for(let[d,c]of Object.entries(t)){if(d==="_id")continue;let l=Object.keys(c)[0];l==="$sum"?u[d]=0:l==="$push"?u[d]=[]:l==="$avg"?u[d]={sum:0,cnt:0}:l==="$max"||l==="$min"||l==="$first"||l==="$last"?u[d]=void 0:u[d]=null}i.set(a,u)}let f=i.get(a);for(let[u,d]of Object.entries(t)){if(u==="_id")continue;let c=Object.keys(d)[0],l=d[c];if(c==="$sum"){let h=s(l)==="number"?l:s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u]=(f[u]||0)+(Number(h)||0)}else if(c==="$push"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u].push(h)}else if(c==="$avg"){let h=s(l)==="number"?l:s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r),m=Number(h);Number.isNaN(m)||(f[u].sum+=m,f[u].cnt+=1)}else if(c==="$max"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);(f[u]===void 0||h>f[u])&&(f[u]=h)}else if(c==="$min"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);(f[u]===void 0||h<f[u])&&(f[u]=h)}else if(c==="$first"){if(f[u]===void 0){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u]=h}}else if(c==="$last"){let h=s(l)==="string"&&l.startsWith("$")?$(r,l.slice(1)):g(l,r);f[u]=h}}}let n=[];for(let r of i.values()){for(let[o,a]of Object.entries(r)){let f=t[o];if(!f)continue;Object.keys(f)[0]==="$avg"&&(r[o]=a.cnt===0?null:a.sum/a.cnt)}n.push(r)}return n},R=(e,t)=>{if(s(t)!=="object")return e;let i=Object.keys(t);return e.slice().sort((n,r)=>{for(let o of i){let a=t[o]===-1?-1:1,f=$(n,o),u=$(r,o);if(!(f==null&&u==null)){if(f==null)return-1*a;if(u==null)return 1*a;if(f<u)return-1*a;if(f>u)return 1*a}}return 0})},I=(e,t)=>e.map(i=>{if(s(t)!=="object")return{...i};let n=Object.keys(t).some(o=>t[o]===1),r={};if(n){for(let[o,a]of Object.entries(t))if(a===1){let f=$(i,o);f!==void 0&&(r[o]=f)}else s(a)==="string"&&a.startsWith("$")?r[o]=$(i,a.slice(1)):s(a)==="object"&&(r[o]=g(a,i));return r}for(let o of Object.keys(i))r[o]=i[o];for(let[o,a]of Object.entries(t))a===0?delete r[o]:s(a)==="string"&&a.startsWith("$")?r[o]=$(i,a.slice(1)):s(a)==="object"&&(r[o]=g(a,i));return r}),V=(e,t)=>{let i=Math.max(0,parseInt(t,10)||0);return e.slice(0,i)},P=(e=[],t=[])=>{let i=s(e)==="array"?e.slice():[];return t.reduce((n,r)=>{let o=Object.keys(r)[0],a=r[o];return o==="$match"?_(n,a):o==="$unwind"?E(n,a):o==="$group"?W(n,a):o==="$sort"?R(n,a):o==="$project"?I(n,a):o==="$limit"?V(n,a):n},i)};var v=(e,t,i)=>{let n=t.split("."),r=n[n.length-1],o=n.slice(0,-1).reduce((a,f)=>a==null?a:((!Object.prototype.hasOwnProperty.call(a,f)||s(a[f])!=="object")&&(a[f]={}),a[f]),e);o!=null&&(o[r]=i)},O=(e,t)=>t.split(".").reduce((n,r)=>{if(!(n==null||!Object.prototype.hasOwnProperty.call(n,r)))return n[r]},e),N=(e,t,i=[])=>{if(!e||s(e)!=="object")return i;if(s(e.$and)==="array"){for(let n of e.$and)N(n,t,i);return i}for(let n of Object.keys(e))if(!(n==="$and"||n==="$or"||n==="$not"||n==="$nor")&&(n===t&&e[n]&&s(e[n])==="object"&&Object.prototype.hasOwnProperty.call(e[n],"$elemMatch")&&i.push({type:"elemMatch",cond:e[n].$elemMatch}),n.startsWith(t+"."))){let r=n.slice(t.length+1);i.push({type:"subfield",subfield:r,cond:e[n]})}return i},D=(e,t)=>{let i=N(e,t);return i.length===0?null:n=>{for(let r of i)if(r.type==="elemMatch"){if(s(r.cond)!=="object")return!1;let o=Object.keys(r.cond);if(o.length===0)return!1;for(let a of o){let f=$(n,a);if(!j(f,r.cond[a]))return!1}}else if(r.type==="subfield"){let o=$(n,r.subfield);if(!j(o,r.cond))return!1}return!0}},Q=(e,t)=>{if(!t||s(t)!=="object")return!1;let i=Object.keys(t);if(i.length===0)return!1;for(let n of i){let r=$(e,n);if(!j(r,t[n]))return!1}return!0},U=(e={},t,i=null)=>{if(!t||s(t)!=="object")return{...e};let n=Object.keys(t),r=n.some(u=>t[u]===0),a=n.some(u=>{let d=t[u];return d===1?!0:s(d)==="object"?Object.prototype.hasOwnProperty.call(d,"$slice")||Object.prototype.hasOwnProperty.call(d,"$elemMatch"):!1})?"include":"exclude",f={};if(a==="include"){for(let u of n){if(u==="_id"&&t._id===0)continue;let d=t[u];if(u.endsWith(".$")){let c=u.slice(0,-2),l=O(e,c);if(s(l)==="array"){let h=D(i,c);if(h){let m=l.find(T=>h(T));m!==void 0&&v(f,c,[m])}}continue}if(d===1){let c=O(e,u);c!==void 0&&v(f,u,c)}else if(s(d)==="object"&&Object.prototype.hasOwnProperty.call(d,"$elemMatch")){let c=O(e,u);if(s(c)==="array"){let l=c.find(h=>Q(h,d.$elemMatch));l!==void 0&&v(f,u,[l])}}else if(s(d)==="object"&&Object.prototype.hasOwnProperty.call(d,"$slice")){let c=O(e,u);if(c!==void 0){let l=d.$slice,h=s(c)==="array"?s(l)!=="number"||l===0?[]:l>0?c.slice(0,l):c.slice(l):c;v(f,u,h)}}}return t._id!==0&&Object.prototype.hasOwnProperty.call(e,"_id")&&(f._id=e._id),f}for(let u of Object.keys(e))f[u]=e[u];for(let u of n)if(t[u]===0){let c=u.split(".");if(c.length===1)delete f[u];else{let l=c.pop(),h=c.join("."),m=O(f,h);m&&Object.prototype.hasOwnProperty.call(m,l)&&delete m[l]}}return f},C=(e=[],t,i=null)=>s(e)!=="array"?e:!t||s(t)!=="object"?e.map(n=>({...n})):e.map(n=>U(n,t,i));var k=class e{constructor(t=[],i={},n=null){this._items=t||[],this._query=i||{},this._projection=n||null,this._sortSpec=null,this._skip=0,this._limit=null}sort(t={}){return this._sortSpec=t&&s(t)==="object"?t:null,this}project(t=null){return this._projection=t&&s(t)==="object"?t:this._projection,this}skip(t=0){return this._skip=Math.max(0,parseInt(String(t),10)||0),this}limit(t){return t==null?this.toArray():(this._limit=Math.max(0,parseInt(String(t),10)||0),this)}toArray(){let t=_(this._items,this._query),i=this._sortSpec?(()=>{let a=Object.keys(this._sortSpec);return t.slice().sort((f,u)=>{for(let d of a){let c=this._sortSpec[d]===-1?-1:1,l=$(f,d),h=$(u,d);if(!(l==null&&h==null)){if(l==null)return-1*c;if(h==null)return 1*c;if(l<h)return-1*c;if(l>h)return 1*c}}return 0})})():t,n=this._skip&&this._skip>0?i.slice(this._skip):i,r=this._limit!=null?n.slice(0,this._limit):n;return this._projection?C(r,this._projection,this._query):r}static from(t,i={},n=null){return new e(t,i,n)}};var M=class{constructor(t=[]){if(s(t)!=="array")throw new TypeError("Items must be an array of objects");this._items=t}find(t={},i){return k.from(this._items,t,i)}findOne(t={},i){let n=this.find(t,i).toArray();return n.length>0?n[0]:null}count(t={}){return this.find(t).toArray().length}exists(t={}){return this.find(t).toArray().length>0}distinct(t){if(!t)return[];let i=new Set;for(let n of this._items){let r=s(t)==="string"&&t.startsWith("$")?$(n,t.slice(1)):$(n,t);r!==void 0&&i.add(r)}return Array.from(i)}aggregate(t=[]){return P(this._items,t)}toArray(){return s(this._items)==="array"?[...this._items]:[]}};export{M as Qar};
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "qarjs",
3
+ "version": "0.8.2",
4
+ "description": "MongoDB-like queries for JavaScript arrays",
5
+ "keywords": [
6
+ "query",
7
+ "array",
8
+ "mongodb",
9
+ "filter",
10
+ "json"
11
+ ],
12
+ "author": {
13
+ "name": "Zsolt Tovis",
14
+ "email": "tovis.zsolt@gmail.com",
15
+ "url": "https://toviszsolt.github.io/"
16
+ },
17
+ "funding": [
18
+ {
19
+ "type": "github",
20
+ "url": "https://github.com/sponsors/toviszsolt"
21
+ },
22
+ {
23
+ "type": "paypal",
24
+ "url": "https://www.paypal.com/paypalme/toviszsolt"
25
+ }
26
+ ],
27
+ "homepage": "https://github.com/toviszsolt/qar",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/toviszsolt/qar.git"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/toviszsolt/qar/issues"
34
+ },
35
+ "type": "module",
36
+ "license": "MIT",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "files": [
41
+ "dist/*"
42
+ ],
43
+ "exports": {
44
+ ".": {
45
+ "types": "./dist/qar.d.ts",
46
+ "import": "./dist/qar.js",
47
+ "require": "./dist/qar.cjs"
48
+ }
49
+ },
50
+ "scripts": {
51
+ "watch": "node build.js --watch",
52
+ "build": "jest --verbose ./test && node build.js",
53
+ "test": "jest --verbose ./test",
54
+ "coverage": "jest --collect-coverage",
55
+ "coverage:open": "(start ./coverage/lcov-report/index.html || open ./coverage/lcov-report/index.html || xdg-open ./coverage/lcov-report/index.html)"
56
+ },
57
+ "devDependencies": {
58
+ "@types/jest": "^30.0.0",
59
+ "esbuild": "^0.27.3",
60
+ "jest": "^30.2.0",
61
+ "jest-esbuild": "^0.4.0"
62
+ }
63
+ }