onecore 0.6.3 → 0.6.6

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 CHANGED
@@ -4,154 +4,140 @@
4
4
 
5
5
  `onecore` provides the common contracts, metadata model, and reusable application-layer base classes used throughout the Core TS ecosystem.
6
6
 
7
- It allows libraries such as **sql-core**, **mysql2-core**, **postgres-kit**, **mongodb-kit**, **validation-core**, **cache-plus**, **redis-plus**, **message-processing**, **nats-plus**, **redis-messaging**, **rabbitmq-transport**, **logger-core**, and **health-service** to work together through a unified API.
7
+ It is designed to let infrastructure libraries and applications share the same abstractions without coupling application code to a specific database, messaging system, validation implementation, cache, or framework.
8
8
 
9
- ## Features
10
-
11
- * Common interfaces for enterprise applications
12
- * Unified metadata model (`Attribute`)
13
- * Reusable CRUD and Search use-case base classes
14
- * Zero-cost abstractions through TypeScript interfaces
15
- * Tree-shakeable architecture
16
- * Works in both backend and frontend applications
17
- * No framework dependency
18
- * Fully written in TypeScript
19
-
20
- ---
21
-
22
- # Installation
9
+ ## Installation
23
10
 
24
11
  ```bash
25
12
  npm install onecore
26
13
  ```
27
14
 
28
- ---
15
+ ## What is onecore?
29
16
 
30
- # Why onecore?
31
-
32
- Enterprise applications often use many infrastructure libraries.
33
-
34
- For example:
17
+ Large applications commonly depend on many infrastructure libraries:
35
18
 
36
- * Database
37
- * Validation
38
19
  * Cache
39
- * Message Queue
40
20
  * Logging
41
21
  * Health Check
42
- * Import/Export
43
-
44
- Without a shared foundation, every library exposes its own interfaces and metadata.
45
-
46
- ```text
47
- Repository (sql)
48
- Validator (validation)
49
- Producer (mq)
50
- Logger (logger)
51
- Cache (cache)
52
- ```
53
-
54
- `onecore` provides a single common language for the entire ecosystem.
55
-
56
- ```ts
57
- import {
58
- Repository,
59
- Validator,
60
- Producer,
61
- CachePort,
62
- Logger,
63
- HealthChecker
64
- } from "onecore";
65
- ```
66
-
67
- Applications only depend on **onecore**, while implementation libraries implement these contracts.
68
-
69
- ---
22
+ * Validation
23
+ * Database
24
+ * DB
25
+ * Transaction
26
+ * Statement
27
+ * Repository
28
+ * Service
29
+ * Message Queue
30
+ * Import / Export
31
+ * HTTP clients
32
+ * Localization
70
33
 
71
- # Ecosystem
34
+ Without a shared foundation, each library tends to define its own interfaces and metadata.
72
35
 
73
- ```
74
- Applications
75
- ┌───────────────────────────────────────────────────────────────┐
76
- │ │
77
- │ React Apps • REST APIs • Microservices • Workers • CLI │
78
- │ │
79
- └───────────────────────────────────────────────────────────────┘
80
- ▲
81
- │
82
- Reusable Use Cases (Optional)
83
- CRUD • Search • View • Generic Services
84
- ▲
85
- │
86
- ┌─────────────────────────────────────────────────────────────────────────────────┐
87
- │ onecore │
88
- │ │
89
- │ • Common Interfaces │
90
- │ • Unified Attribute Metadata │
91
- │ • Locale & Currency │
92
- │ • Shared Models │
93
- │ • Reusable Service Layer │
94
- └─────────────────────────────────────────────────────────────────────────────────┘
95
- ▲ ▲ ▲ ▲ ▲
96
- │ │ │ │ │
97
- │ │ │ │ │
98
- ┌────────────────┐ ┌────────────────┐ ┌──────────────────┐ ┌────────────────┐ ┌────────────────┐
99
- │ Database │ │ Validation │ │ Messaging │ │ Import / Export│ │ Cache │
100
- ├────────────────┤ ├────────────────┤ ├──────────────────┤ ├────────────────┤ ├────────────────┤
101
- │ sql-core │ │validation-core │ │message-processing│ │import-service │ │ cache-plus │
102
- │ mysql2-core │ │ │ │nats-plus │ │export-kit │ │ redis-plus │
103
- │ postgres-kit │ │ │ │redis-messaging │ │ │ │ │
104
- │ oracle-core │ │ │ │rabbitmq-transport│ │ │ │ │
105
- │ mssql-core │ │ │ │activemq │ │ │ │ │
106
- │ mongodb-kit │ │ │ │ibmmq-plus │ │ │ │ │
107
- └────────────────┘ └────────────────┘ └──────────────────┘ └────────────────┘ └────────────────┘
36
+ `onecore` provides a common set of contracts so that these libraries can work together through a consistent API.
108
37
 
38
+ ```text
39
+ Application
40
+ │
41
+ Use Cases
42
+ │
43
+ ┌─────────┴─────────┐
44
+ │ onecore │
45
+ │ │
46
+ │ Interfaces │
47
+ │ Attribute │
48
+ │ Transaction │
49
+ │ Repository │
50
+ │ Use Cases │
51
+ └─────────┬─────────┘
52
+ │
53
+ ┌──────────┬──────────┼──────────┬──────────┐
54
+ │ │ │ │ │
55
+ Database Validation Cache Messaging Logging
109
56
  ```
110
57
 
111
- All libraries share the same interfaces defined by `onecore`.
112
-
113
- ---
58
+ The implementation packages depend on these contracts, while application code can depend on the contracts rather than concrete infrastructure implementations.
114
59
 
115
- # Zero-Cost Abstractions
60
+ ## Features
116
61
 
117
- One of the goals of `onecore` is to provide a common API **without adding runtime overhead**.
62
+ * Shared TypeScript interfaces for the Core TS ecosystem
63
+ * Unified `Attribute` metadata model
64
+ * `ErrorMessage` for data validation
65
+ * Generic `Filter` and `SearchResult` models
66
+ * Database executor and transaction abstractions
67
+ * Type-safe database parameter abstraction
68
+ * Repository abstractions for CRUD and search
69
+ * Reusable CRUD and Search use-case classes
118
70
 
119
- ## Interfaces
71
+ * Interfaces with no runtime representation after TypeScript compilation
72
+ * No framework dependency
73
+ * Suitable for both backend and frontend projects
74
+ * Tree-shakeable runtime code
75
+ ## Unified metadata
120
76
 
121
- Interfaces disappear after TypeScript compilation.
77
+ The central concept in `onecore` is `Attribute`.
122
78
 
123
79
  ```ts
124
- import { Repository, Validator } from "onecore";
80
+ export interface Attribute {
81
+ name?: string
82
+ field?: string
83
+ column?: string
84
+ type?: DataType
85
+ format?: FormatType
86
+ required?: boolean
87
+
88
+ key?: boolean
89
+ unique?: boolean
90
+
91
+ enum?: string[] | number[]
92
+
93
+ noinsert?: boolean
94
+ noupdate?: boolean
95
+ nopatch?: boolean
96
+
97
+ version?: boolean
98
+ createdAt?: boolean
99
+ updatedAt?: boolean
100
+
101
+ length?: number
102
+ min?: number | Date | string
103
+ max?: number | Date | string
104
+ gt?: number | Date | string
105
+ lt?: number | Date | string
106
+
107
+ precision?: number
108
+ scale?: number
109
+ }
125
110
  ```
126
111
 
127
- The generated JavaScript contains no implementation from `onecore`.
128
-
129
- ## Base Classes
130
-
131
- If an application extends one of the reusable CRUD or Search use-case classes, only those classes are included in the final bundle.
132
-
133
- Modern bundlers such as Webpack, Rollup, Vite, and esbuild tree-shake unused code automatically.
134
-
135
- This allows frontend and backend projects to share the same package while keeping bundles minimal.
112
+ Unlike persistence-specific or validation-specific metadata, `Attribute` is the canonical metadata model shared across the entire ecosystem.
136
113
 
137
- ---
114
+ `Attribute` is intended to be shared by multiple parts of an application instead of maintaining separate metadata definitions for every subsystem.
138
115
 
139
- # Unified Metadata
116
+ For example, the same metadata can describe:
140
117
 
141
- The most important type in `onecore` is `Attribute`.
118
+ * Database mapping
119
+ * Validation
120
+ * Search
121
+ * Formatting
122
+ * Import
123
+ * Export
124
+ * Serialization
142
125
 
143
- Unlike persistence-specific or validation-specific metadata, `Attribute` is the canonical metadata model shared across the entire ecosystem.
126
+ Individual libraries consume only the properties relevant to them.
144
127
 
145
128
  ```
146
129
  Attribute
147
130
  │
148
131
  ┌───────────────┼────────────────┐
149
132
  │ │ │
150
- Validation Database Import/Export
133
+ Validation Database Import/Export
151
134
  │ │ │
152
- validation-core sql-core import-service
153
- mysql2-core export-kit
154
- postgres-kit
135
+ validation-core sql-core import-service
136
+ postgres-kit export-kit
137
+ mysql2-core
138
+ oracle-core
139
+ mssql-core
140
+ cassandra-core
155
141
  mongodb-kit
156
142
  ```
157
143
 
@@ -190,31 +176,411 @@ The same metadata definition can therefore be used for
190
176
 
191
177
  without duplication.
192
178
 
193
- ---
179
+ ## Data types
194
180
 
195
- # Shared Interfaces
181
+ `onecore` defines a common `DataType` model:
196
182
 
197
- `onecore` defines the common contracts used by the ecosystem.
183
+ ```ts
184
+ export type DataType =
185
+ | 'ObjectId'
186
+ | 'date'
187
+ | 'datetime'
188
+ | 'time'
189
+ | 'boolean'
190
+ | 'number'
191
+ | 'integer'
192
+ | 'string'
193
+ | 'text'
194
+ | 'object'
195
+ | 'array'
196
+ | 'binary'
197
+ | 'primitives'
198
+ | 'booleans'
199
+ | 'numbers'
200
+ | 'integers'
201
+ | 'strings'
202
+ | 'dates'
203
+ | 'datetimes'
204
+ | 'times'
205
+ ```
198
206
 
199
- ## Database
207
+ Scalar and collection types can therefore share the same metadata system.
200
208
 
201
- * Repository
202
- * GenericRepository
203
- * SearchRepository
204
- * Transaction
209
+ Formatting is represented separately:
210
+
211
+ ```ts
212
+ export type FormatType =
213
+ | 'currency'
214
+ | 'percentage'
215
+ | 'email'
216
+ | 'url'
217
+ | 'phone'
218
+ | 'fax'
219
+ | 'ipv4'
220
+ | 'ipv6'
221
+ ```
222
+
223
+ ## Database abstraction
224
+
225
+ `onecore` provides a small database-independent execution contract.
226
+
227
+ ```ts
228
+ export interface Executor {
229
+ driver: string
230
+ param(i: number): string
231
+ query<T>(sql: string, args?: any[], m?: StringMap, bools?: Attribute[]): Promise<T[]>
232
+ execute(sql: string, args?: any[]): Promise<number>
233
+ executeBatch(statements: Statement[], requireFirstAffected?: boolean): Promise<number>
234
+ }
235
+ ```
236
+
237
+ A database implementation is responsible for adapting this interface to its native driver.
238
+
239
+ ### Parameter abstraction
240
+
241
+ Different databases use different parameter syntaxes.
242
+
243
+ For example:
244
+
245
+ ```text
246
+ PostgreSQL $1 $2 $3
247
+ MySQL ? ? ?
248
+ Oracle :1 :2 :3
249
+ SQL Server @p1 @p2 @p3
250
+ ```
251
+
252
+ `Executor.param()` allows higher-level SQL-building code to generate database-specific parameters without depending on a particular driver.
253
+
254
+ ### Statements
255
+
256
+ A batch operation is represented by:
257
+
258
+ ```ts
259
+ export interface Statement {
260
+ query: string
261
+ params?: any[]
262
+ }
263
+ ```
264
+
265
+ This keeps batch execution independent of the underlying database implementation.
266
+
267
+ ## Transactions
268
+
269
+ Transactions extend the same executor abstraction:
270
+
271
+ ```ts
272
+ export interface Transaction extends Executor {
273
+ commit(): Promise<void>
274
+ rollback(): Promise<void>
275
+ }
276
+ ```
277
+
278
+ A database provides transactions through:
279
+
280
+ ```ts
281
+ export interface DB extends Executor {
282
+ beginTransaction(): Promise<Transaction>
283
+ }
284
+ ```
285
+
286
+ This allows repository code to use either the normal database executor or a transaction without knowing the underlying connection type.
287
+
288
+ For example:
289
+
290
+ ```ts
291
+ const tx = await db.beginTransaction()
292
+
293
+ try {
294
+ await userRepository.create(user, tx)
295
+ await accountRepository.create(account, tx)
296
+
297
+ await tx.commit()
298
+ } catch (err) {
299
+ await tx.rollback()
300
+ throw err
301
+ }
302
+ ```
303
+
304
+ The transaction abstraction is particularly useful when several repositories participate in the same operation.
305
+
306
+ ## Repository abstraction
307
+
308
+ `onecore` defines generic repository contracts for CRUD and search operations.
309
+
310
+ ### CRUDRepository
311
+
312
+ ```ts
313
+ export interface CRUDRepository<T, ID> {
314
+ load(id: ID, tx?: Transaction): Promise<T | null>
315
+ create(obj: T, tx?: Transaction): Promise<number>
316
+ update(obj: T, tx?: Transaction): Promise<number>
317
+ patch(obj: Partial<T>, tx?: Transaction): Promise<number>
318
+ delete(id: ID, tx?: Transaction): Promise<number>
319
+ }
320
+ ```
321
+
322
+ The optional `Transaction` parameter allows the same repository method to participate in a larger transaction.
323
+
324
+ Normal operation:
325
+
326
+ ```ts
327
+ await repository.create(user)
328
+ ```
329
+
330
+ Transactional operation:
331
+
332
+ ```ts
333
+ await repository.create(user, tx)
334
+ ```
335
+
336
+ ### SearchRepository
337
+
338
+ ```ts
339
+ export interface SearchRepository<T, F extends Filter> {
340
+ search(
341
+ filter: F,
342
+ limit: number,
343
+ offset?: number | string,
344
+ fields?: string[],
345
+ tx?: Transaction
346
+ ): Promise<SearchResult<T>>
347
+ }
348
+ ```
349
+
350
+ The generic filter model is:
351
+
352
+ ```ts
353
+ export interface Filter {
354
+ page?: number
355
+ limit: number
356
+ fields?: string[]
357
+ sort?: string
358
+ q?: string
359
+ }
360
+ ```
361
+
362
+ Search results are represented as:
363
+
364
+ ```ts
365
+ export interface SearchResult<T> {
366
+ list: T[]
367
+ total?: number
368
+ next?: string
369
+ }
370
+ ```
371
+
372
+ This allows implementations to support result counts as well as cursor-style pagination.
373
+
374
+ ## Repository
375
+
376
+ CRUD and search repositories can be combined:
377
+
378
+ ```ts
379
+ export interface Repository<T, ID, F extends Filter>
380
+ extends CRUDRepository<T, ID>, SearchRepository<T, F> {
381
+ }
382
+ ```
383
+
384
+ An application can therefore define:
385
+
386
+ ```ts
387
+ interface UserFilter extends Filter {
388
+ active?: boolean
389
+ role?: string
390
+ }
391
+
392
+ type UserRepository = Repository<User, string, UserFilter>
393
+ ```
394
+
395
+ ## Reusable use cases
396
+
397
+ `onecore` includes reusable application-layer classes so applications do not have to repeatedly write trivial forwarding logic.
398
+
399
+ ### SearchUseCase
400
+
401
+ ```ts
402
+ export class SearchUseCase<T, F extends Filter> {
403
+ constructor(
404
+ protected repository: SearchRepository<T, F>
405
+ ) {}
406
+
407
+ search(filter: F, limit: number, page?: number | string,
408
+ fields?: string[]): Promise<SearchResult<T>> {
409
+ return this.repository.search(filter, limit, page, fields)
410
+ }
411
+ }
412
+ ```
413
+
414
+ ### CRUDUseCase
415
+
416
+ ```ts
417
+ export class CRUDUseCase<T, ID> {
418
+ constructor(protected repository: CRUDRepository<T, ID>) {}
419
+
420
+ load(id: ID): Promise<T | null> {
421
+ return this.repository.load(id)
422
+ }
423
+ create(obj: T): Promise<number> {
424
+ return this.repository.create(obj)
425
+ }
426
+ update(obj: T): Promise<number> {
427
+ return this.repository.update(obj)
428
+ }
429
+ patch(obj: Partial<T>): Promise<number> {
430
+ return this.repository.patch(obj)
431
+ }
432
+ delete(id: ID): Promise<number> {
433
+ return this.repository.delete(id)
434
+ }
435
+ }
436
+ ```
437
+
438
+ ### UseCase
439
+
440
+ For applications that need both CRUD and search operations:
441
+
442
+ ```ts
443
+ export class UseCase<T, ID, F extends Filter> {
444
+ constructor(protected repository: Repository<T, ID, F>) {}
445
+
446
+ search(filter: F, limit: number, page?: number | string,
447
+ fields?: string[]): Promise<SearchResult<T>> {
448
+ return this.repository.search(filter, limit, page, fields)
449
+ }
450
+
451
+ load(id: ID): Promise<T | null> {
452
+ return this.repository.load(id)
453
+ }
454
+ create(obj: T): Promise<number> {
455
+ return this.repository.create(obj)
456
+ }
457
+ update(obj: T): Promise<number> {
458
+ return this.repository.update(obj)
459
+ }
460
+ patch(obj: Partial<T>): Promise<number> {
461
+ return this.repository.patch(obj)
462
+ }
463
+ delete(id: ID): Promise<number> {
464
+ return this.repository.delete(id)
465
+ }
466
+ }
467
+ ```
468
+
469
+ Applications can extend these classes when the default repository-to-use-case behavior is sufficient, and override methods when additional business logic is required.
470
+
471
+ ## Services and results
472
+
473
+ Service contracts can be defined independently from the repository layer:
474
+
475
+ ```ts
476
+ export interface SearchService<T, F extends Filter> {
477
+ search(
478
+ filter: F,
479
+ limit: number,
480
+ page?: number | string,
481
+ fields?: string[]
482
+ ): Promise<SearchResult<T>>
483
+ }
484
+ ```
485
+
486
+ CRUD services expose application-facing operations:
487
+
488
+ ```ts
489
+ export interface CRUDService<T, ID> {
490
+ load(id: ID): Promise<T | null>
491
+ create(obj: T): Promise<Result<T>>
492
+ update(obj: T): Promise<Result<T>>
493
+ patch(obj: Partial<T>): Promise<Result<T>>
494
+ delete(id: ID): Promise<number>
495
+ }
496
+ ```
497
+
498
+ Validation or application errors can be represented by:
499
+
500
+ ```ts
501
+ export interface ErrorMessage {
502
+ field: string
503
+ code: string
504
+ param?: string | number | Date
505
+ message?: string
506
+ }
507
+ ```
508
+
509
+ with:
510
+
511
+ ```ts
512
+ export type Result<T> = number | T | ErrorMessage[]
513
+ ```
514
+
515
+ This allows an application layer to return structured field-level errors without coupling the service interface to a particular validation library.
516
+
517
+ ## Ecosystem
518
+
519
+ `onecore` is intended to be the common foundation for the Core TS packages.
520
+
521
+ Examples include:
522
+
523
+ ```text
524
+ Database
525
+ ├── sql-core
526
+ ├── postgres-kit
527
+ ├── mysql2-core
528
+ ├── oracle-core
529
+ ├── mssql-core
530
+ ├── cassandra-core
531
+ └── mongodb-kit
532
+
533
+ Validation
534
+ └── validation-core
535
+
536
+ Messaging
537
+ ├── message-processing
538
+ ├── nats-plus
539
+ ├── redis-messaging
540
+ ├── rabbitmq-transport
541
+ ├── activemq
542
+ ├── kafka-plus
543
+ └── ibmmq-plus
544
+
545
+ Cache
546
+ ├── cache-plus
547
+ └── redis-plus
548
+
549
+ Other
550
+ ├── import-service
551
+ ├── export-kit
552
+ ├── logger-core
553
+ ├── health-service
554
+ ├── locale-service
555
+ └── web-clients
556
+ ```
557
+
558
+ These packages can share the same contracts and metadata model while remaining independently implemented.
559
+
560
+
561
+ ## Shared Interfaces
562
+
563
+ `onecore` defines the common contracts used by the ecosystem.
564
+
565
+ ### Database
205
566
  * Statement
206
- * QueryBuilder
567
+ * DB
568
+ * Transaction
569
+ * SearchRepository
570
+ * CRUDRepository
571
+ * Repository
207
572
 
208
573
  Implemented by
209
574
 
210
575
  * sql-core
211
576
  * mysql2-core
577
+ * oracle-core
578
+ * mssql-core
212
579
  * postgres-kit
580
+ * cassandra-core
213
581
  * mongodb-kit
214
582
 
215
- ---
216
-
217
- ## Validation
583
+ ### Validation
218
584
 
219
585
  * Validator<T>
220
586
 
@@ -222,9 +588,7 @@ Implemented by
222
588
 
223
589
  * validation-core
224
590
 
225
- ---
226
-
227
- ## Cache
591
+ ### Cache
228
592
 
229
593
  * CachePort
230
594
 
@@ -233,9 +597,7 @@ Implemented by
233
597
  * cache-plus
234
598
  * redis-plus
235
599
 
236
- ---
237
-
238
- ## Message Queue
600
+ ### Message Queue
239
601
 
240
602
  * Producer
241
603
  * Consumer
@@ -248,11 +610,11 @@ Implemented by
248
610
  * redis-messaging
249
611
  * nats-plus
250
612
  * rabbitmq-transport
251
- * ActiveMQ libraries
252
-
253
- ---
613
+ * activemq
614
+ * kafka-plus
615
+ * ibmmq-plus
254
616
 
255
- ## Logging
617
+ ### Logging
256
618
 
257
619
  * Logger
258
620
 
@@ -260,9 +622,7 @@ Implemented by
260
622
 
261
623
  * logger-core
262
624
 
263
- ---
264
-
265
- ## Health Check
625
+ ### Health Check
266
626
 
267
627
  * HealthChecker
268
628
 
@@ -270,9 +630,7 @@ Implemented by
270
630
 
271
631
  * health-service
272
632
 
273
- ---
274
-
275
- ## HTTP
633
+ ### HTTP
276
634
 
277
635
  * HttpRequest
278
636
  * HttpOptionsService
@@ -281,9 +639,7 @@ Implemented by
281
639
 
282
640
  * web-clients
283
641
 
284
- ---
285
-
286
- ## Localization
642
+ ### Localization
287
643
 
288
644
  * Locale
289
645
  * Currency
@@ -292,25 +648,42 @@ Implemented by
292
648
 
293
649
  * locale-service
294
650
 
295
- ---
651
+ ## Zero-cost abstractions
296
652
 
297
- # Reusable Use Cases
653
+ One of the design goals of `onecore` is to provide shared APIs without imposing framework-level runtime overhead.
298
654
 
299
- `onecore` also provides reusable application-layer base classes.
655
+ TypeScript interfaces such as:
300
656
 
301
- Examples include
657
+ ```ts
658
+ Repository
659
+ Transaction
660
+ Executor
661
+ Validator
662
+ ```
663
+
664
+ do not exist in the generated JavaScript.
302
665
 
303
- * CRUD services
304
- * Search services
305
- * View services
666
+ Reusable classes such as `UseCase` are only included when they are actually imported and used, allowing modern bundlers to tree-shake unused code.
306
667
 
307
- Applications can extend these classes instead of implementing common CRUD logic repeatedly.
668
+ This makes the package suitable for both backend and frontend projects.
308
669
 
309
- Projects that do not use these classes pay no runtime cost.
670
+ ## Backend Support
310
671
 
311
- ---
672
+ Infrastructure services often depend only on interfaces.
673
+
674
+ Examples include
675
+
676
+ * Shared metadata
677
+ * Database transactions
678
+ * Repository contracts
679
+ * Application use cases
680
+ * Message Queue services
681
+ * Import services / Export services
682
+ * Infrastructure interfaces: Logging, Cache, Validation...
683
+
684
+ Since these projects do not use application-layer base classes, the generated JavaScript contains virtually no runtime code from `onecore`.
312
685
 
313
- # Frontend Support
686
+ ## Frontend Support
314
687
 
315
688
  `onecore` works in frontend applications.
316
689
 
@@ -323,34 +696,52 @@ React applications commonly use
323
696
 
324
697
  without importing any backend service classes.
325
698
 
699
+ For example:
700
+
701
+ ```ts
702
+ import type {
703
+ Attribute,
704
+ Filter,
705
+ SearchResult
706
+ } from 'onecore'
707
+ ```
708
+
709
+ This allows a frontend and backend to share the same domain metadata and contracts.
710
+
326
711
  The final JavaScript bundle remains minimal because interfaces are removed during compilation and unused code is tree-shaken.
327
712
 
328
- ---
713
+ ## Design principles
329
714
 
330
- # Backend Support
715
+ ### One common contract
331
716
 
332
- Infrastructure services often depend only on interfaces.
717
+ Infrastructure implementations should depend on common interfaces instead of forcing applications to depend on implementation details.
333
718
 
334
- Examples include
719
+ ### Metadata reuse
335
720
 
336
- * Message Queue services
337
- * Import services
338
- * Export services
339
- * Background workers
721
+ Define metadata once and reuse it across database mapping, validation, search, serialization, import/export, and formatting.
340
722
 
341
- Since these projects do not use application-layer base classes, the generated JavaScript contains virtually no runtime code from `onecore`.
723
+ ### Database independence
724
+
725
+ Application and repository contracts should not expose a PostgreSQL client, MySQL connection, Oracle connection, or other driver-specific resource.
726
+
727
+ ### Explicit transactions
728
+
729
+ Transactions are passed explicitly to repository operations when multiple operations must participate in the same atomic unit of work.
342
730
 
343
- ---
731
+ ### Minimal runtime
344
732
 
345
- # Sample Projects
733
+ Keep shared contracts in TypeScript interfaces and keep runtime implementations small and reusable.
734
+
735
+
736
+ ## Sample Projects
346
737
 
347
738
  The following sample applications demonstrate different ways of using `onecore`.
348
739
 
349
740
  ### CRUD Applications
350
741
 
351
- * [**sql-modular-sample**](https://github.com/source-code-template/sql-modular-sample) — SQL modular microservice using MySQL
352
- * [**sql-simple-modular-sample**](https://github.com/source-code-template/sql-simple-modular-sample) — SQL modular microservice using PosgreSQL
353
- * [**mongo-simple-modular-sample**](https://github.com/source-code-template/mongo-simple-modular-sample) — MongoDB modular microservice
742
+ * [sql-modular-sample](https://github.com/source-code-template/sql-modular-sample) — SQL modular microservice using MySQL
743
+ * [sql-simple-modular-sample](https://github.com/source-code-template/sql-simple-modular-sample) — SQL modular microservice using PosgreSQL
744
+ * [mongo-simple-modular-sample](https://github.com/source-code-template/mongo-simple-modular-sample) — MongoDB modular microservice
354
745
 
355
746
  These projects extend the reusable CRUD and Search use-case classes.
356
747
 
@@ -380,21 +771,8 @@ These projects depend only on interfaces and the shared `Attribute` metadata mod
380
771
 
381
772
  These projects depend only on interfaces and the shared `Attribute` metadata model.
382
773
 
383
- ---
384
-
385
- # Design Principles
774
+ ## License
386
775
 
387
- * Interface-first architecture
388
- * Dependency inversion
389
- * Clean Architecture
390
- * Hexagonal Architecture
391
- * Tree-shakeable design
392
- * Shared metadata model
393
- * Framework independent
394
- * Enterprise ready
395
-
396
- ---
776
+ MIT
397
777
 
398
- # License
399
778
 
400
- MIT
package/lib/index.js ADDED
@@ -0,0 +1,84 @@
1
+ export class SearchUseCase {
2
+ constructor(repository) {
3
+ this.repository = repository;
4
+ this.search = this.search.bind(this);
5
+ }
6
+ search(s, limit, page, fields) {
7
+ return this.repository.search(s, limit, page, fields);
8
+ }
9
+ }
10
+ export const SearchManager = SearchUseCase;
11
+ export class Writer {
12
+ constructor(repository) {
13
+ this.repository = repository;
14
+ this.create = this.create.bind(this);
15
+ this.update = this.update.bind(this);
16
+ this.patch = this.patch.bind(this);
17
+ }
18
+ create(obj) {
19
+ return this.repository.create(obj);
20
+ }
21
+ update(obj) {
22
+ return this.repository.update(obj);
23
+ }
24
+ patch(obj) {
25
+ return (this.repository.patch ? this.repository.patch(obj) : Promise.resolve(-1));
26
+ }
27
+ }
28
+ export class SearchWriter extends Writer {
29
+ constructor(repository) {
30
+ super(repository);
31
+ this.repository = repository;
32
+ this.search = this.search.bind(this);
33
+ }
34
+ search(filter, limit, page, fields) {
35
+ return this.repository.search(filter, limit, page, fields);
36
+ }
37
+ }
38
+ export class UseCase extends SearchWriter {
39
+ constructor(repository) {
40
+ super(repository);
41
+ this.repository = repository;
42
+ this.metadata = this.metadata.bind(this);
43
+ this.keys = this.keys.bind(this);
44
+ this.load = this.load.bind(this);
45
+ this.delete = this.delete.bind(this);
46
+ }
47
+ metadata() {
48
+ return (this.repository.metadata ? this.repository.metadata() : undefined);
49
+ }
50
+ keys() {
51
+ return (this.repository.keys ? this.repository.keys() : []);
52
+ }
53
+ load(id) {
54
+ return this.repository.load(id);
55
+ }
56
+ delete(id) {
57
+ return this.repository.delete(id);
58
+ }
59
+ }
60
+ export class CRUDUseCase extends Writer {
61
+ constructor(repository) {
62
+ super(repository);
63
+ this.repository = repository;
64
+ this.metadata = this.metadata.bind(this);
65
+ this.keys = this.keys.bind(this);
66
+ this.load = this.load.bind(this);
67
+ this.delete = this.delete.bind(this);
68
+ }
69
+ metadata() {
70
+ return (this.repository.metadata ? this.repository.metadata() : undefined);
71
+ }
72
+ keys() {
73
+ return (this.repository.keys ? this.repository.keys() : []);
74
+ }
75
+ load(id) {
76
+ return this.repository.load(id);
77
+ }
78
+ delete(id) {
79
+ return this.repository.delete(id);
80
+ }
81
+ }
82
+ export const GenericUseCase = CRUDUseCase;
83
+ export const GenericManager = CRUDUseCase;
84
+ export const CRUDManager = CRUDUseCase;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onecore",
3
- "version": "0.6.3",
3
+ "version": "0.6.6",
4
4
  "description": "onecore",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./src/index.ts",
package/src/db.ts CHANGED
@@ -18,7 +18,7 @@ export interface Executor {
18
18
  driver: string
19
19
  param(i: number): string
20
20
  execute(sql: string, args?: any[]): Promise<number>
21
- executeBatch(statements: Statement[], firstSuccess?: boolean): Promise<number>
21
+ executeBatch(statements: Statement[], requireFirstAffected?: boolean): Promise<number>
22
22
  query<T>(sql: string, args?: any[], m?: StringMap, bools?: Attribute[]): Promise<T[]>
23
23
  }
24
24
  export interface Transaction extends Executor {
@@ -35,7 +35,7 @@ export interface ViewRepository<T, ID> {
35
35
  load(id: ID, tx?: Transaction): Promise<T | null>;
36
36
  }
37
37
  export interface SearchRepository<T, F extends Filter> {
38
- search(s: F, limit: number, offset?: number|string, fields?: string[], tx?: Transaction): Promise<SearchResult<T>>;
38
+ search(filter: F, limit: number, offset?: number|string, fields?: string[], tx?: Transaction): Promise<SearchResult<T>>;
39
39
  }
40
40
  export interface ViewSearchRepository<T, ID, F extends Filter>
41
41
  extends ViewRepository<T, ID>, SearchRepository<T, F> {
package/src/formatter.ts CHANGED
@@ -1,20 +1,19 @@
1
1
  export interface Locale {
2
- id: string;
3
- countryCode: string;
4
- dateFormat: string;
5
- firstDayOfWeek: number;
6
- decimalSeparator: string;
7
- groupSeparator: string;
8
- decimalDigits: number;
9
- currencyCode: string;
10
- currencySymbol: string;
11
- currencyPattern: number;
12
- currencySample?: string;
2
+ code: string
3
+ countryCode: string
4
+ dateFormat: string
5
+ firstDayOfWeek: number
6
+ decimalSeparator: string
7
+ groupSeparator: string
8
+ decimalDigits: number
9
+ currencyCode: string
10
+ currencySymbol: string
11
+ currencyPattern: number
13
12
  }
14
13
  export interface Currency {
15
- code: string;
16
- decimalDigits: number;
17
- symbol: string;
14
+ code: string
15
+ decimalDigits: number
16
+ symbol: string
18
17
  }
19
18
 
20
19
  export interface StringFormatter<T> {
package/src/index.ts CHANGED
@@ -118,11 +118,15 @@ export interface Parser<T> {
118
118
  export interface Transformer<T> {
119
119
  transform: (data: string) => Promise<T>;
120
120
  }
121
+ export interface ExportResult {
122
+ total: number
123
+ success: number
124
+ }
121
125
  export interface ExportService {
122
- export(ctx?: any): Promise<number>;
126
+ export(ctx?: any): Promise<ExportResult>;
123
127
  }
124
128
  export interface Exporter {
125
- export(ctx?: any): Promise<number>;
129
+ export(ctx?: any): Promise<ExportResult>;
126
130
  }
127
131
 
128
132
  // tslint:disable-next-line:max-classes-per-file
@@ -171,8 +175,8 @@ export class SearchWriter<T, F extends Filter> extends Writer<T> {
171
175
  super(repository)
172
176
  this.search = this.search.bind(this);
173
177
  }
174
- search(s: F, limit: number, page?: number|string, fields?: string[]): Promise<SearchResult<T>> {
175
- return this.repository.search(s, limit, page, fields);
178
+ search(filter: F, limit: number, page?: number|string, fields?: string[]): Promise<SearchResult<T>> {
179
+ return this.repository.search(filter, limit, page, fields);
176
180
  }
177
181
  }
178
182
 
package/src/io.ts CHANGED
@@ -27,3 +27,8 @@ export interface ErrorHandler<T> {
27
27
  export interface ExceptionHandler<S> {
28
28
  handleException(res: S, err: any, i?: number, filename?: string): void
29
29
  }
30
+
31
+ export interface FileWriter<T> {
32
+ write(chunk: T): boolean
33
+ end(cb?: () => void): void
34
+ }
package/src/mq.ts CHANGED
@@ -56,27 +56,27 @@ export interface Writer<T, R, ID> {
56
56
  }
57
57
  */
58
58
  export interface SimpleConsumer<T, R> {
59
- consume(handle: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
59
+ consume(process: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
60
60
  }
61
61
  export interface SimpleSubscriber<T, R> {
62
- subscribe(handle: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
62
+ subscribe(process: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
63
63
  }
64
64
  export interface SimpleReceiver<T, R> {
65
- receive(handle: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
65
+ receive(process: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
66
66
  }
67
67
  export interface SimpleReader<T, R> {
68
- read(handle: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
68
+ read(process: (data: T, attributes?: StringMap, raw?: R) => Promise<number>): void;
69
69
  }
70
70
 
71
71
  export interface MessageConsumer<T, ID, R> {
72
72
  consume(data: Message<T, ID, R>): void;
73
73
  }
74
74
  export interface MessageSubscriber<T, ID, R> {
75
- subscribe(handle: (data: Message<T, ID, R>) => Promise<number>): void;
75
+ subscribe(process: (data: Message<T, ID, R>) => Promise<number>): void;
76
76
  }
77
77
  export interface MessageReceiver<T, ID, R> {
78
- receive(handle: (data: Message<T, ID, R>) => Promise<number>): void;
78
+ receive(process: (data: Message<T, ID, R>) => Promise<number>): void;
79
79
  }
80
80
  export interface MessageReader<T, ID, R> {
81
- read(handle: (data: Message<T, ID, R>) => Promise<number>): void;
81
+ read(process: (data: Message<T, ID, R>) => Promise<number>): void;
82
82
  }
package/src/service.ts CHANGED
@@ -9,7 +9,7 @@ export interface ViewService<T, ID> {
9
9
 
10
10
  export interface SearchService<T, F extends Filter> {
11
11
  keys?(): string[];
12
- search(s: F, limit: number, page?: number|string, fields?: string[]): Promise<SearchResult<T>>;
12
+ search(filter: F, limit: number, page?: number|string, fields?: string[]): Promise<SearchResult<T>>;
13
13
  }
14
14
 
15
15
  export interface Query<T, ID, F extends Filter> extends SearchService<T, F> {