@decaf-ts/core 0.8.51 → 0.8.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +206 -93
  2. package/dist/core.cjs +1 -1
  3. package/dist/core.cjs.map +1 -1
  4. package/dist/core.js +1 -1
  5. package/dist/core.js.map +1 -1
  6. package/lib/esm/fs/FilesystemAdapter.d.ts +67 -0
  7. package/lib/esm/fs/FilesystemAdapter.js +430 -0
  8. package/lib/esm/fs/FilesystemAdapter.js.map +1 -0
  9. package/lib/esm/fs/FsDispatch.d.ts +7 -0
  10. package/lib/esm/fs/FsDispatch.js +20 -0
  11. package/lib/esm/fs/FsDispatch.js.map +1 -0
  12. package/lib/esm/fs/helpers.d.ts +17 -0
  13. package/lib/esm/fs/helpers.js +64 -0
  14. package/lib/esm/fs/helpers.js.map +1 -0
  15. package/lib/esm/fs/index.d.ts +4 -0
  16. package/lib/esm/fs/index.js +5 -0
  17. package/lib/esm/fs/index.js.map +1 -0
  18. package/lib/esm/fs/indexStore.d.ts +28 -0
  19. package/lib/esm/fs/indexStore.js +173 -0
  20. package/lib/esm/fs/indexStore.js.map +1 -0
  21. package/lib/esm/fs/locks/FilesystemLock.d.ts +13 -0
  22. package/lib/esm/fs/locks/FilesystemLock.js +49 -0
  23. package/lib/esm/fs/locks/FilesystemLock.js.map +1 -0
  24. package/lib/esm/fs/locks/FilesystemMultiLock.d.ts +8 -0
  25. package/lib/esm/fs/locks/FilesystemMultiLock.js +23 -0
  26. package/lib/esm/fs/locks/FilesystemMultiLock.js.map +1 -0
  27. package/lib/esm/fs/types.d.ts +34 -0
  28. package/lib/esm/fs/types.js +2 -0
  29. package/lib/esm/fs/types.js.map +1 -0
  30. package/lib/esm/index.d.ts +1 -1
  31. package/lib/esm/index.js +1 -1
  32. package/lib/esm/ram/RamAdapter.d.ts +1 -1
  33. package/lib/esm/ram/types.d.ts +2 -1
  34. package/lib/esm/tasks/TaskEngine.d.ts +30 -1
  35. package/lib/esm/tasks/TaskEngine.js +361 -16
  36. package/lib/esm/tasks/TaskEngine.js.map +1 -1
  37. package/lib/esm/tasks/TaskService.js +3 -0
  38. package/lib/esm/tasks/TaskService.js.map +1 -1
  39. package/lib/esm/tasks/builder.js +1 -1
  40. package/lib/esm/tasks/builder.js.map +1 -1
  41. package/lib/esm/tasks/constants.js +1 -0
  42. package/lib/esm/tasks/constants.js.map +1 -1
  43. package/lib/esm/tasks/types.d.ts +12 -0
  44. package/lib/esm/tasks/workers/WorkThreadEnvironment.d.ts +32 -0
  45. package/lib/esm/tasks/workers/WorkThreadEnvironment.js +28 -0
  46. package/lib/esm/tasks/workers/WorkThreadEnvironment.js.map +1 -0
  47. package/lib/esm/tasks/workers/messages.d.ts +69 -0
  48. package/lib/esm/tasks/workers/messages.js +2 -0
  49. package/lib/esm/tasks/workers/messages.js.map +1 -0
  50. package/lib/esm/tasks/workers/workerThread.d.ts +1 -0
  51. package/lib/esm/tasks/workers/workerThread.js +185 -0
  52. package/lib/esm/tasks/workers/workerThread.js.map +1 -0
  53. package/lib/fs/FilesystemAdapter.cjs +437 -0
  54. package/lib/fs/FilesystemAdapter.d.ts +67 -0
  55. package/lib/fs/FilesystemAdapter.js.map +1 -0
  56. package/lib/fs/FsDispatch.cjs +24 -0
  57. package/lib/fs/FsDispatch.d.ts +7 -0
  58. package/lib/fs/FsDispatch.js.map +1 -0
  59. package/lib/fs/helpers.cjs +76 -0
  60. package/lib/fs/helpers.d.ts +17 -0
  61. package/lib/fs/helpers.js.map +1 -0
  62. package/lib/fs/index.cjs +21 -0
  63. package/lib/fs/index.d.ts +4 -0
  64. package/lib/fs/index.js.map +1 -0
  65. package/lib/fs/indexStore.cjs +181 -0
  66. package/lib/fs/indexStore.d.ts +28 -0
  67. package/lib/fs/indexStore.js.map +1 -0
  68. package/lib/fs/locks/FilesystemLock.cjs +56 -0
  69. package/lib/fs/locks/FilesystemLock.d.ts +13 -0
  70. package/lib/fs/locks/FilesystemLock.js.map +1 -0
  71. package/lib/fs/locks/FilesystemMultiLock.cjs +30 -0
  72. package/lib/fs/locks/FilesystemMultiLock.d.ts +8 -0
  73. package/lib/fs/locks/FilesystemMultiLock.js.map +1 -0
  74. package/lib/fs/types.cjs +3 -0
  75. package/lib/fs/types.d.ts +34 -0
  76. package/lib/fs/types.js.map +1 -0
  77. package/lib/index.cjs +1 -1
  78. package/lib/index.d.ts +1 -1
  79. package/lib/ram/RamAdapter.d.ts +1 -1
  80. package/lib/ram/types.d.ts +2 -1
  81. package/lib/tasks/TaskEngine.cjs +360 -15
  82. package/lib/tasks/TaskEngine.d.ts +30 -1
  83. package/lib/tasks/TaskEngine.js.map +1 -1
  84. package/lib/tasks/TaskService.cjs +3 -0
  85. package/lib/tasks/TaskService.js.map +1 -1
  86. package/lib/tasks/builder.cjs +1 -1
  87. package/lib/tasks/builder.js.map +1 -1
  88. package/lib/tasks/constants.cjs +1 -0
  89. package/lib/tasks/constants.js.map +1 -1
  90. package/lib/tasks/types.d.ts +12 -0
  91. package/lib/tasks/workers/WorkThreadEnvironment.cjs +31 -0
  92. package/lib/tasks/workers/WorkThreadEnvironment.d.ts +32 -0
  93. package/lib/tasks/workers/WorkThreadEnvironment.js.map +1 -0
  94. package/lib/tasks/workers/messages.cjs +3 -0
  95. package/lib/tasks/workers/messages.d.ts +69 -0
  96. package/lib/tasks/workers/messages.js.map +1 -0
  97. package/lib/tasks/workers/workerThread.cjs +220 -0
  98. package/lib/tasks/workers/workerThread.d.ts +1 -0
  99. package/lib/tasks/workers/workerThread.js.map +1 -0
  100. package/package.json +19 -8
package/README.md CHANGED
@@ -39,108 +39,94 @@ Decaf Core provides the foundational building blocks for the Decaf TypeScript ec
39
39
 
40
40
  Documentation [here](https://decaf-ts.github.io/injectable-decorators/), Test results [here](https://decaf-ts.github.io/injectable-decorators/workdocs/reports/html/test-report.html) and Coverage [here](https://decaf-ts.github.io/injectable-decorators/workdocs/reports/coverage/lcov-report/index.html)
41
41
 
42
- Minimal size: 37.4 KB kb gzipped
42
+ Minimal size: 39.4 KB kb gzipped
43
43
 
44
44
 
45
45
  # Core Package — Detailed Description
46
46
 
47
- The Decaf Core package provides a cohesive set of primitives for building strongly-typed data-access layers in TypeScript. It centers around:
47
+ The Decaf Core package provides a cohesive set of primitives for building strongly-typed data-access layers and managing background tasks in TypeScript. It centers around:
48
48
 
49
- - Models (from @decaf-ts/decorator-validation) enhanced with identity and persistence metadata
50
- - A Repository abstraction that encapsulates CRUD, querying, and observation
51
- - Adapters that bridge repositories to underlying storage (in-memory, HTTP, TypeORM, etc.)
52
- - A fluent Query DSL (Statement/Condition) with pagination
53
- - Lightweight dependency injection utilities to auto-resolve repositories
49
+ - Models (from @decaf-ts/decorator-validation) enhanced with identity and persistence metadata.
50
+ - A Repository abstraction that encapsulates CRUD, querying, and observation.
51
+ - A powerful Task Engine for defining, scheduling, and executing background jobs with support for worker threads.
52
+ - Adapters that bridge repositories to underlying storage (in-memory, HTTP, TypeORM, etc.).
53
+ - A fluent Query DSL (Statement/Condition) with pagination.
54
+ - Lightweight dependency injection utilities to auto-resolve repositories.
54
55
 
55
56
  Below is an overview of the main modules and their public APIs exposed by core.
56
57
 
57
- 1) Repository module
58
- - Repository<M>
59
- - Constructor: new Repository(adapter: Adapter, clazz: Constructor<M>, ...)
60
- - CRUD: create, read, update, delete
61
- - Bulk ops: createAll, readAll, updateAll, deleteAll
62
- - Hooks: createPrefix/createSuffix, updateAllPrefix, readAllPrefix, deleteAllPrefix (internal orchestration helpers)
63
- - Query: select(...selectors?), query(condition?, orderBy?, order?, limit?, skip?)
64
- - Observation: observe(observer, filter?), unObserve(observer), updateObservers(...), refresh(...)
58
+ ## 1. Repository Module
59
+ - **`Repository<M>`**
60
+ - Constructor: `new Repository(adapter: Adapter, clazz: Constructor<M>, ...)`
61
+ - CRUD: `create`, `read`, `update`, `delete`
62
+ - Bulk ops: `createAll`, `readAll`, `updateAll`, `deleteAll`
63
+ - Querying:
64
+ - `select(...selectors?)`: Start a fluent query chain.
65
+ - `query(condition?, orderBy?, order?, limit?, skip?)`: Execute a simple query.
66
+ - **New High-Level Queries:** A set of methods, often used with the `@prepared` decorator, for common query patterns:
67
+ - `find(value, order?)`: Searches default attributes of a model for partial matches (starts-with).
68
+ - `findBy(key, value)`: Finds records by a specific attribute-value pair.
69
+ - `findOneBy(key, value)`: Finds a single record or throws a `NotFoundError`.
70
+ - `listBy(key, order)`: Lists all records ordered by a specific key.
71
+ - `countOf(key?)`: Counts records, optionally for a specific attribute.
72
+ - `maxOf(key)`, `minOf(key)`, `avgOf(key)`, `sumOf(key)`: Perform aggregate calculations.
73
+ - `distinctOf(key)`: Retrieves distinct values for an attribute.
74
+ - `groupOf(key)`: Groups records by a given attribute.
75
+ - `page(value, direction?, ref?)`: Paginates through records matching a default partial-match query.
76
+ - `paginateBy(key, order, ref?)`: Paginates records ordered by a specific key.
77
+ - Observation: `observe(observer, filter?)`, `unObserve(observer)`, `updateObservers(...)`, `refresh(...)`
78
+ - **Statement Execution**:
79
+ - `statement(name, ...args)`: Executes a custom method on the repository decorated with `@prepared`.
65
80
  - Repository registry helpers:
66
- - static for(config, ...args): Proxy factory for building repositories with specific adapter config
67
- - static forModel(model, alias?, ...args): returns a Repository instance or repository constructor registered for the model
68
- - static get(model, alias?): low-level retrieval of a registered repository constructor
69
- - static register(model, repoCtor, alias?)
70
- - static getMetadata/setMetadata/removeMetadata(model)
71
- - static getSequenceOptions(model)
72
- - static indexes(model): reads index definitions for model
73
- - static relations(model)
74
- - static table(model), static column(model, attribute)
75
- - Decorators (repository/decorators)
76
- - repository(modelCtor, flavour?):
77
- - As property decorator: injects the repository instance for the annotated model
78
- - As class decorator: registers the annotated class as the repository for the model; integrates with Injectables
79
- - Injectables registry (repository/injectables)
80
- - InjectablesRegistry extends InjectableRegistryImp
81
- - get<T>(name | token | ctor, flavour?): resolves a registered injectable; if not registered, attempts to infer the model and construct or fetch the appropriate repository based on adapter flavour or metadata (falling back to current adapter)
82
- - Types/utilities (repository/types, repository/utils)
83
- - IndexMetadata, OrderDirection, generateInjectableNameForRepository, and other helpers/constants
84
-
85
- 2) Persistence module
86
- - Adapter<N = any, Q = any, R = any, Ctx = Context>
87
- - Base bridge between repository and the back-end. Offers:
88
- - initialize(...), flags(...), context(...)
89
- - prepare(model, pk): model -> record mapping using model metadata
90
- - revert(record, clazz, pk, id, transient?): record -> model mapping
91
- - CRUD: create, createAll, read, readAll, update, updateAll, delete, deleteAll
92
- - raw(rawInput): pass-through for back-end specific commands
93
- - Observation: observe/unObserve, updateObservers, refresh
94
- - Flavour/alias management: current(), get(flavour), setCurrent(flavour), alias(), models(flavour), flavourOf(model)
95
- - Factory helpers: Statement(), Dispatch(), ObserverHandler(), Sequence(options)
96
- - for(config, ...args): proxy-bound adapter for a given configuration
97
- - Dispatch: batching/dispatch helpers used by Adapter
98
- - Sequence: provides identity/sequence generation based on SequenceOptions (see interfaces)
99
- - ObserverHandler: internal observer list and filtering logic used by repositories/adapters
100
- - constants, errors, types: PersistenceKeys, EventIds, ObserverFilter, etc.
101
-
102
- 3) Query module
103
- - Statement<M extends Model>
104
- - Fluent DSL to build and execute queries via the configured Adapter
105
- - Methods:
106
- - select(...keys?), distinct(key), count(key), max(key), min(key)
107
- - from(modelCtor), where(Condition), orderBy([key, OrderDirection]), groupBy(key)
108
- - limit(n), offset(n), execute(), raw(input), paginate(size)
109
- - Condition<M extends Model>
110
- - Composable condition tree with a builder API and logical combinators
111
- - Methods:
112
- - and(cond), or(cond), not(cond)
113
- - attribute/attr(name): switch attribute under construction
114
- - hasErrors(exceptions?): validation helper
115
- - group(cond1, GroupOperator, cond2)
116
- - builder(): ConditionBuilder
117
- - ConditionBuilder methods: eq, dif, gt, lt, gte, lte, in, regexp, build
118
- - Paginator<M>
119
- - Abstract pagination helper returned by Statement.paginate(size)
120
- - Properties: current, total, count, size
121
- - Methods: page(n?), next(), previous(); requires an Adapter-specific concrete implementation
122
-
123
- 4) Interfaces module
124
- - Observable<T>, Observer<T>: basic observer pattern primitives
125
- - Executor, RawExecutor: contracts for query execution
126
- - Queriable: minimal interface for types that can return a Statement
127
- - Paginatable: marks types that can paginate
128
- - SequenceOptions and defaults: sequence/generator configuration presets
129
-
130
- 5) Model & Identity modules
131
- - BaseModel and supporting types: base class all models extend from
132
- - identity/decorators and identity/utils: helpers to derive table names, etc.
133
- - model/decorators: e.g., @model and other persistence-related metadata (provided by @decaf-ts/decorator-validation and enriched here)
134
-
135
- 6) RAM runtime (core/src/ram)
136
- - RamAdapter, RamRepository, RamStatement, RamPaginator (in-memory implementations used by tests and examples)
137
- - Useful for local testing and reference behavior of the core abstractions.
138
-
139
- Design intent
140
- - Provide a consistent, typed data access layer decoupled from any particular storage or framework
141
- - Allow adapters to plug into multiple backends while preserving a uniform repository and query API
142
- - Make querying expressive but type-safe through fluent builders and model metadata
143
- - Enable DI and decorators for ergonomic repository wiring and testing
81
+ - `static for(config, ...args)`: Proxy factory for building repositories with specific adapter config.
82
+ - `static forModel(model, alias?, ...args)`: Returns a Repository instance for a model.
83
+ - `static register(model, repoCtor, alias?)`: Registers a repository for a model.
84
+
85
+ - **Decorators (`repository/decorators`)**
86
+ - `@repository(modelCtor, flavour?)`: Injects a repository instance or registers a repository class.
87
+ - `@prepared()`: Marks a repository method as an executable "prepared statement", allowing it to be called via `repository.statement()`.
88
+
89
+ ## 2. Task Engine Module
90
+ A robust system for managing background jobs.
91
+ - **`TaskEngine<A>`**: The core engine that polls for and executes tasks. Manages the task lifecycle, concurrency, and worker threads.
92
+ - **`TaskService<A>`**: A high-level service providing a clean API for interacting with the `TaskEngine`. It's the recommended entry point for managing tasks.
93
+ - `push(task, track?)`: Submits a new task for execution.
94
+ - `schedule(task, track?).for(date)`: Schedules a task to run at a specific time.
95
+ - `track(id)`: Returns a `TaskTracker` to monitor an existing task.
96
+ - **Models**:
97
+ - `TaskModel`: Represents a task, its status (`PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`), input, and configuration (e.g., `maxAttempts`, `backoff`). Can be `ATOMIC` or `COMPOSITE`.
98
+ - `TaskEventModel`: Logs status changes and progress for a task.
99
+ - **Builders**:
100
+ - `TaskBuilder`: A fluent API for constructing `TaskModel` instances.
101
+ - `CompositeTaskBuilder`: A builder for creating multi-step (`COMPOSITE`) tasks.
102
+ - **Handlers & Tracking**:
103
+ - `ITaskHandler`: The interface to implement for defining the logic of a task. Handlers are registered with the `TaskHandlerRegistry`.
104
+ - `TaskTracker`: An object returned when tracking a task, allowing you to await its completion and receive progress updates.
105
+ - **Worker Threads**: The engine can be configured to run tasks in Node.js `worker_threads`, providing true parallelism and non-blocking execution for CPU-intensive jobs. Configuration is done via the `workerPool` and `workerAdapter` properties in the `TaskEngineConfig`.
106
+
107
+ ## 3. Persistence Module
108
+ - **`Adapter<N, Q, R, Ctx>`**: The bridge between a repository and the back-end storage.
109
+ - Handles CRUD operations, raw queries, and model/record transformation (`prepare`/`revert`).
110
+ - Manages different storage "flavours" (e.g., 'ram', 'fs', 'typeorm').
111
+ - **`Sequence`**: Provides identity/sequence generation.
112
+ - **`ObserverHandler`**: Manages observer notifications.
113
+
114
+ ## 4. Query Module
115
+ - **`Statement<M>`**: A fluent DSL for building and executing queries.
116
+ - Methods: `select`, `from`, `where`, `orderBy`, `groupBy`, `limit`, `offset`, `execute`, `paginate`.
117
+ - Now includes enhanced logic to "squash" simple queries into efficient prepared statements.
118
+ - **`Condition<M>`**: A composable condition tree for building `where` clauses.
119
+ - **`Paginator<M>`**: An abstract pagination helper.
120
+ - Now includes `serialize()` and `deserialize()` methods to easily pass pagination state.
121
+
122
+ ## 5. Model & Identity Modules
123
+ - **`BaseModel`**: The base class all models extend from.
124
+ - Decorators like `@table`, `@pk`, `@column`, `@index`, and relation decorators (`@oneToOne`, `@oneToMany`, `@manyToOne`) are used to define persistence metadata.
125
+ - Includes updated logic for handling complex relations, including `oneToManyOnCreateUpdate` and initial support for `manyToMany`.
126
+
127
+ ## 6. RAM & Filesystem Runtimes
128
+ - **`RamAdapter`**: An in-memory adapter, perfect for tests and quick prototyping.
129
+ - **`FilesystemAdapter`**: A `RamAdapter`-compatible adapter that persists data to the local filesystem, enabling data to survive process restarts. Ideal for local development and testing.
144
130
 
145
131
 
146
132
  # How to Use
@@ -190,6 +176,47 @@ sequenceDiagram
190
176
  7. **`createSuffix`**: The `Repository`'s `createSuffix` method is called. This is where you can add logic to be executed after the main `create` operation.
191
177
  8. **Decorators (AFTER)**: Any decorators configured to run `AFTER` the `CREATE` operation are executed.
192
178
 
179
+ ### FilesystemAdapter Setup
180
+
181
+ `FilesystemAdapter` (found under `core/src/fs`) extends `RamAdapter` but writes every dataset to disk so repositories survive restarts. You can swap it anywhere you would use `RamAdapter`.
182
+
183
+ **Configuration highlights**
184
+
185
+ - `rootDir`: Base directory where databases live. Each adapter alias becomes its own sub-folder.
186
+ - `jsonSpacing`: Optional pretty-print spacing for the JSON payloads (handy while debugging).
187
+ - `fs`: Custom `fs/promises` implementation — forward your own for tests or sandboxes.
188
+ - `onHydrated(info)`: Callback executed after a table is read from disk; great for metrics or warm-up logs.
189
+
190
+ **Directory layout**
191
+
192
+ - Records -> `{rootDir}/{alias}/{table}/{encodedPk}.json` storing `{ id, record }`.
193
+ - Indexes -> `{rootDir}/{alias}/{table}/indexes/{indexName}.json`, mirroring `@index` metadata so range/aggregate queries stay fast.
194
+
195
+ ```typescript
196
+ import path from "node:path";
197
+ import { FilesystemAdapter, Repository } from "@decaf-ts/core";
198
+ import { User } from "./models/User";
199
+
200
+ const adapter = new FilesystemAdapter(
201
+ {
202
+ rootDir: path.join(process.cwd(), ".decaf-data"),
203
+ jsonSpacing: 2,
204
+ onHydrated: ({ table, records }) => {
205
+ console.info(`Hydrated ${records} ${table} records from disk`);
206
+ },
207
+ },
208
+ "local-fs"
209
+ );
210
+
211
+ const repo = new Repository(adapter, User);
212
+ await repo.create(new User({ id: "user-1", name: "Persistent" }));
213
+ const reloaded = await repo.read("user-1"); // survives process restarts
214
+
215
+ await adapter.shutdown(); // closes open file handles when the app exits
216
+ ```
217
+
218
+ For tests, point `rootDir` at a temporary folder (see `tests/fs/__helpers__/tempFs.ts`) and clean it up after each suite.
219
+
193
220
  ## Core Decorators
194
221
 
195
222
  The library provides a set of powerful decorators for defining models and their behavior.
@@ -368,6 +395,92 @@ console.log('Task result:', result);
368
395
  taskEngine.schedule(task).for(new Date(Date.now() + 5000)); // 5 seconds from now
369
396
  ```
370
397
 
398
+ ### Worker Threads
399
+
400
+ The Task Engine can be configured to execute tasks in separate worker threads, enabling true parallelism.
401
+
402
+ ```typescript
403
+ import { TaskEngine, TaskHandlerRegistry } from '@decaf-ts/core';
404
+ import path from 'path';
405
+
406
+ const taskEngine = new TaskEngine({
407
+ adapter,
408
+ registry,
409
+ workerPool: {
410
+ entry: path.resolve(__dirname, './worker-entry.ts'), // Path to your worker entry file
411
+ size: 4, // Number of worker threads
412
+ },
413
+ workerAdapter: {
414
+ adapterModule: '@decaf-ts/core/fs', // Module to load the adapter from
415
+ adapterClass: 'FilesystemAdapter', // Adapter class name
416
+ adapterArgs: [{ rootDir: './data' }, 'fs-worker'], // Arguments for the adapter constructor
417
+ }
418
+ });
419
+
420
+ await taskEngine.start();
421
+ ```
422
+
423
+ ## Advanced Repository Features
424
+
425
+ The `Repository` class now includes several high-level methods for common query patterns, simplifying data access.
426
+
427
+ ### Finding Records
428
+
429
+ ```typescript
430
+ // Find records by a specific attribute
431
+ const users = await userRepo.findBy('email', 'test@example.com');
432
+
433
+ // Find a single record (throws NotFoundError if not found)
434
+ const user = await userRepo.findOneBy('username', 'jdoe');
435
+
436
+ // List records ordered by a key
437
+ const sortedUsers = await userRepo.listBy('createdAt', OrderDirection.DESC);
438
+ ```
439
+
440
+ ### Partial Match Search
441
+
442
+ The `find` and `page` methods support partial matching (starts-with) on default query attributes.
443
+
444
+ ```typescript
445
+ // Assuming 'name' and 'email' are default query attributes for User
446
+ // This will find users where name OR email starts with "john"
447
+ const users = await userRepo.find('john');
448
+
449
+ // You can also specify the sort order
450
+ const sortedUsers = await userRepo.find('john', OrderDirection.DESC);
451
+ ```
452
+
453
+ ### Aggregations
454
+
455
+ Perform calculations directly on your data:
456
+
457
+ ```typescript
458
+ const totalUsers = await userRepo.countOf();
459
+ const activeUsersCount = await userRepo.countOf('isActive'); // Counts where isActive is truthy
460
+
461
+ const maxAge = await userRepo.maxOf('age');
462
+ const minAge = await userRepo.minOf('age');
463
+ const avgAge = await userRepo.avgOf('age');
464
+ const totalAge = await userRepo.sumOf('age');
465
+
466
+ const distinctCities = await userRepo.distinctOf('city');
467
+ ```
468
+
469
+ ### Pagination
470
+
471
+ Easily paginate through your data, including partial match searches:
472
+
473
+ ```typescript
474
+ // Paginate based on a default query (e.g., all users)
475
+ // This searches for users matching "search term" (partial match) and paginates the results
476
+ const page1 = await userRepo.page('search term', OrderDirection.ASC, { limit: 10, offset: 1 });
477
+
478
+ // Paginate ordered by a specific key without filtering
479
+ const page2 = await userRepo.paginateBy('createdAt', OrderDirection.DESC, { limit: 20, offset: 2 });
480
+
481
+ console.log(`Page ${page1.current} of ${page1.total}`);
482
+ ```
483
+
371
484
 
372
485
  ### Related
373
486