tshex-cli 1.0.28 → 1.0.30
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/build/main.js +1 -158
- package/docs/context-ports.md +66 -28
- package/docs/generated-file-reference.md +2 -1
- package/docs/library-structure.md +22 -6
- package/docs/shared/application/data.md +188 -106
- package/docs/shared/application/services.md +90 -87
- package/docs/shared/domain/aggregates.md +22 -131
- package/docs/shared/domain/entities.md +96 -51
- package/package.json +7 -8
- package/source/main.ts +34 -11
- package/templates/ctx/example-ports.ts +1 -0
- package/templates/lib/shared/application/data/capabilities.ts +60 -0
- package/templates/lib/shared/application/data/drivers.ts +2 -2
- package/templates/lib/shared/application/data/managers.ts +5 -62
- package/templates/lib/shared/application/data/repositories.ts +7 -19
- package/templates/lib/shared/application/events.ts +1 -1
- package/templates/lib/shared/application/http/errors.ts +2 -0
- package/templates/lib/shared/application/loggers.ts +0 -21
- package/templates/lib/shared/domain/entities.ts +1 -1
- package/templates/lib/shared/application/http/handlers.ts +0 -13
- package/templates/lib/shared/application/http/json-api.ts +0 -611
- package/templates/lib/shared/application/http/json-web-token.ts +0 -980
- package/templates/lib/shared/application/http/opengraph.ts +0 -533
|
@@ -11,148 +11,39 @@ The generated template provides `Aggregate` as a semantic base class.
|
|
|
11
11
|
`Aggregate` is an abstract class with no concrete methods.
|
|
12
12
|
|
|
13
13
|
```ts title="shared/domain/aggregates.ts"
|
|
14
|
-
export abstract class Aggregate {
|
|
14
|
+
export abstract class Aggregate {
|
|
15
|
+
[property: string]: unknown
|
|
16
|
+
}
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
The class is intentionally empty. Its role is to provide the semantic base for
|
|
18
|
-
an aggregate whose concrete implementation gathers the involved entities
|
|
19
|
-
|
|
20
|
-
`order`, or `invoice`. Persistence rules, event publication, and identity
|
|
21
|
-
behavior live in the concrete domain model of the project.
|
|
22
|
-
|
|
23
|
-
#### First Aggregate
|
|
24
|
-
|
|
25
|
-
In the following example we model a sale as one domain unit composed of a
|
|
26
|
-
buyer, a seller, an order, and an invoice generated by the aggregate itself.
|
|
27
|
-
|
|
28
|
-
```ts title="sales/domain/sale.ts"
|
|
29
|
-
import { Aggregate } from '../../shared/domain/aggregates.js'
|
|
30
|
-
import { Entity } from '../../shared/domain/entities.js'
|
|
31
|
-
|
|
32
|
-
class Person extends Entity {
|
|
33
|
-
public readonly id: string
|
|
34
|
-
public readonly name: string
|
|
35
|
-
|
|
36
|
-
public constructor(id: string, name: string) {
|
|
37
|
-
super()
|
|
38
|
-
this.id = id
|
|
39
|
-
this.name = name
|
|
40
|
-
}
|
|
20
|
+
an aggregate whose concrete implementation gathers the involved entities and
|
|
21
|
+
exposes operations that span more than one of them.
|
|
41
22
|
|
|
42
|
-
|
|
43
|
-
return other instanceof Person && this.id === other.id
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
class Product extends Entity {
|
|
48
|
-
public readonly id: string
|
|
49
|
-
public readonly name: string
|
|
50
|
-
public readonly price: number
|
|
51
|
-
|
|
52
|
-
public constructor(id: string, name: string, price: number) {
|
|
53
|
-
super()
|
|
54
|
-
this.id = id
|
|
55
|
-
this.name = name
|
|
56
|
-
this.price = price
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public equals(other: Entity): boolean {
|
|
60
|
-
return other instanceof Product && this.id === other.id
|
|
61
|
-
}
|
|
62
|
-
}
|
|
23
|
+
#### Usage
|
|
63
24
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
public readonly products: Product[]
|
|
25
|
+
In the following example we model an enrollment aggregate that groups a
|
|
26
|
+
student and a course to produce an inscription with the current date.
|
|
67
27
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
28
|
+
```ts title="enrollment/domain/inscriptions.ts"
|
|
29
|
+
import { Aggregate } from '../shared/domain/aggregates.ts'
|
|
30
|
+
import { Course } from './courses.ts'
|
|
31
|
+
import { Student } from './students.ts'
|
|
32
|
+
import { Inscription } from './inscriptions.ts'
|
|
73
33
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
class Invoice extends Entity {
|
|
80
|
-
public readonly id: string
|
|
81
|
-
public readonly order: Order
|
|
82
|
-
public readonly buyer: Person
|
|
83
|
-
public readonly seller: Person
|
|
84
|
-
public readonly total: number
|
|
85
|
-
|
|
86
|
-
public constructor(
|
|
87
|
-
id: string,
|
|
88
|
-
order: Order,
|
|
89
|
-
buyer: Person,
|
|
90
|
-
seller: Person,
|
|
91
|
-
) {
|
|
92
|
-
super()
|
|
93
|
-
this.id = id
|
|
94
|
-
this.order = order
|
|
95
|
-
this.buyer = buyer
|
|
96
|
-
this.seller = seller
|
|
97
|
-
this.total = this.order.products.reduce(
|
|
98
|
-
(sum, product) => sum + product.price,
|
|
99
|
-
0,
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public equals(other: Entity): boolean {
|
|
104
|
-
return other instanceof Invoice && this.id === other.id
|
|
105
|
-
}
|
|
106
|
-
}
|
|
34
|
+
export class InscriptionAggregate extends Aggregate {
|
|
35
|
+
[property: string]: unknown
|
|
107
36
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
protected invoice: Invoice | null
|
|
113
|
-
|
|
114
|
-
public constructor(
|
|
115
|
-
buyer: Person,
|
|
116
|
-
seller: Person,
|
|
117
|
-
order: Order,
|
|
118
|
-
) {
|
|
119
|
-
super()
|
|
120
|
-
this.buyer = buyer
|
|
121
|
-
this.seller = seller
|
|
122
|
-
this.order = order
|
|
123
|
-
this.invoice = null
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
public generateInvoice(): { invoice: Invoice } {
|
|
127
|
-
const id = Math.random().toString()
|
|
128
|
-
const invoice = new Invoice(
|
|
129
|
-
id,
|
|
130
|
-
this.order,
|
|
131
|
-
this.buyer,
|
|
132
|
-
this.seller,
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
this.invoice = invoice
|
|
136
|
-
|
|
137
|
-
return { invoice }
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
public getInvoice(): Invoice | null {
|
|
141
|
-
return this.invoice
|
|
142
|
-
}
|
|
37
|
+
public static enroll(student: Student, course: Course): Inscription {
|
|
38
|
+
const currentDate = new Date()
|
|
39
|
+
return new Inscription(student, course, currentDate)
|
|
40
|
+
}
|
|
143
41
|
}
|
|
144
42
|
```
|
|
145
43
|
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
stores the list of purchased products and `Invoice` stores the computed
|
|
150
|
-
`total`. `Invoice` is created inside the aggregate, and its constructor
|
|
151
|
-
calculates the total from the order products when the invoice is generated. The
|
|
152
|
-
`invoice` field stays inside the aggregate lifecycle and `getInvoice()` offers
|
|
153
|
-
controlled access to the generated invoice. When a domain object already
|
|
154
|
-
exists, the example uses that object directly and keeps the collaboration
|
|
155
|
-
between domain parts explicit.
|
|
44
|
+
`InscriptionAggregate` centralizes the rule for creating an inscription. The
|
|
45
|
+
enrollment date is set automatically, so callers do not pass it directly and
|
|
46
|
+
the rule stays in one place.
|
|
156
47
|
|
|
157
48
|
#### Responsibility Boundary
|
|
158
49
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
### Entities
|
|
2
2
|
|
|
3
3
|
An entity is responsible for representing a domain concept with its own
|
|
4
|
-
identity.
|
|
5
|
-
|
|
6
|
-
identity, even if other attributes change over time.
|
|
4
|
+
identity. Two entity instances refer to the same conceptual element when they
|
|
5
|
+
share that identity, even if other attributes change over time.
|
|
7
6
|
|
|
8
7
|
The generated template provides `Entity` as a base class for this pattern.
|
|
9
8
|
|
|
@@ -13,73 +12,119 @@ The generated template provides `Entity` as a base class for this pattern.
|
|
|
13
12
|
|
|
14
13
|
```ts title="shared/domain/entities.ts"
|
|
15
14
|
export abstract class Entity {
|
|
16
|
-
|
|
15
|
+
[property: string]: unknown
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
return this
|
|
20
|
-
}
|
|
17
|
+
public abstract equals(other: Entity): boolean
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
public toJSON(): Record<string, unknown> {
|
|
20
|
+
return this
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public toString(): string {
|
|
24
|
+
return this.constructor.name
|
|
25
|
+
}
|
|
25
26
|
}
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
Every concrete entity must implement `equals()`.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
#### First Entity
|
|
32
|
-
|
|
33
|
-
In the following example we model a user entity.
|
|
29
|
+
Every concrete entity must implement `equals()`. Override `toJSON()` to control
|
|
30
|
+
the plain representation returned when the entity is serialized.
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
import { Entity } from '../../shared/domain/entities.js'
|
|
37
|
-
import { Email } from '../../shared/domain/value-objects.js'
|
|
32
|
+
#### Usage
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
public constructor(
|
|
41
|
-
public readonly id: string,
|
|
42
|
-
public readonly email: Email,
|
|
43
|
-
) {
|
|
44
|
-
super()
|
|
45
|
-
}
|
|
34
|
+
In the following example we model the entities of a course enrollment context.
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
```
|
|
36
|
+
```ts title="enrollment/domain/students.ts"
|
|
37
|
+
import { Entity } from '../shared/domain/entities.ts'
|
|
52
38
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
the same user.
|
|
39
|
+
export class Student extends Entity {
|
|
40
|
+
[property: string]: unknown
|
|
56
41
|
|
|
57
|
-
|
|
42
|
+
constructor(
|
|
43
|
+
public name: string,
|
|
44
|
+
public email: string
|
|
45
|
+
) {
|
|
46
|
+
super()
|
|
47
|
+
}
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
public equals(other: Student): boolean {
|
|
50
|
+
return this.email === other.email
|
|
51
|
+
}
|
|
60
52
|
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
public override toJSON() {
|
|
54
|
+
return {
|
|
55
|
+
name: this.name,
|
|
56
|
+
email: this.email
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
```ts title="enrollment/domain/courses.ts"
|
|
63
|
+
import { Entity } from '../shared/domain/entities.ts'
|
|
64
|
+
|
|
65
|
+
export class Course extends Entity {
|
|
66
|
+
[property: string]: unknown
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
public name: string,
|
|
70
|
+
public description: string,
|
|
71
|
+
public duration_hours: number
|
|
72
|
+
) {
|
|
73
|
+
super()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public equals(other: Course): boolean {
|
|
77
|
+
return this.name === other.name
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public override toJSON() {
|
|
81
|
+
return {
|
|
82
|
+
name: this.name,
|
|
83
|
+
description: this.description,
|
|
84
|
+
duration_hours: this.duration_hours
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
66
88
|
```
|
|
67
89
|
|
|
68
|
-
|
|
69
|
-
|
|
90
|
+
```ts title="enrollment/domain/inscriptions.ts"
|
|
91
|
+
import { Entity } from '../shared/domain/entities.ts'
|
|
92
|
+
import { Course } from './courses.ts'
|
|
93
|
+
import { Student } from './students.ts'
|
|
94
|
+
|
|
95
|
+
export class Inscription extends Entity {
|
|
96
|
+
[property: string]: unknown
|
|
97
|
+
|
|
98
|
+
constructor(
|
|
99
|
+
public readonly student: Student,
|
|
100
|
+
public readonly course: Course,
|
|
101
|
+
public readonly enrolled_at: Date
|
|
102
|
+
) {
|
|
103
|
+
super()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public equals(other: Inscription): boolean {
|
|
107
|
+
return this.student.equals(other.student) && this.course.equals(other.course)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public override toJSON() {
|
|
111
|
+
return {
|
|
112
|
+
student: this.student.toJSON(),
|
|
113
|
+
course: this.course.toJSON(),
|
|
114
|
+
enrolled_at: this.enrolled_at
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
70
119
|
|
|
71
120
|
#### Equality Rules
|
|
72
121
|
|
|
73
122
|
The most important design decision in an entity is the identity comparison.
|
|
74
123
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
3. a combination of fields that the domain treats as unique.
|
|
80
|
-
|
|
81
|
-
Changing fields that are not part of the identity should not make `equals()`
|
|
82
|
-
return `false` for the same conceptual entity.
|
|
124
|
+
`Student` uses `email` as the identity because two students with the same email
|
|
125
|
+
represent the same person. `Course` uses `name`. `Inscription` combines the
|
|
126
|
+
student and course identities — two inscriptions are the same when both the
|
|
127
|
+
student and the course match.
|
|
83
128
|
|
|
84
129
|
> **Hint**
|
|
85
130
|
> Keep `equals()` explicit and small. If the comparison starts depending on many
|
|
@@ -88,4 +133,4 @@ return `false` for the same conceptual entity.
|
|
|
88
133
|
#### Next Step
|
|
89
134
|
|
|
90
135
|
When the identity of a concept is determined entirely by its value, use a value
|
|
91
|
-
object instead. The generated abstraction is documented in `value-objects.md`.
|
|
136
|
+
object instead. The generated abstraction is documented in `value-objects.md`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tshex-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.30",
|
|
4
4
|
"author": "https://github.com/virtualitems/",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Typescript Hexagonal Architecture CLI",
|
|
@@ -41,15 +41,14 @@
|
|
|
41
41
|
"bin": {
|
|
42
42
|
"tshex": "./build/main.js"
|
|
43
43
|
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "deno run --allow-all npm:esbuild --bundle --minify --platform=node --external:commander --format=esm --outfile=./build/main.js ./source/main.ts",
|
|
46
|
+
"test": "deno test --allow-all tests/"
|
|
47
|
+
},
|
|
44
48
|
"dependencies": {
|
|
45
|
-
"commander": "^
|
|
49
|
+
"commander": "^15.0.0"
|
|
46
50
|
},
|
|
47
51
|
"devDependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"@types/node": "^22.5.4",
|
|
50
|
-
"typescript": "^5.4.5"
|
|
51
|
-
},
|
|
52
|
-
"scripts": {
|
|
53
|
-
"build": "tsc"
|
|
52
|
+
"@types/node": "^18"
|
|
54
53
|
}
|
|
55
54
|
}
|
package/source/main.ts
CHANGED
|
@@ -9,12 +9,12 @@ import { program } from 'commander'
|
|
|
9
9
|
import fs from 'node:fs'
|
|
10
10
|
import path from 'node:path'
|
|
11
11
|
import readline from 'node:readline/promises'
|
|
12
|
-
import { stdin
|
|
12
|
+
import { stdin, stdout } from 'node:process'
|
|
13
13
|
|
|
14
14
|
// FUNCTIONS
|
|
15
15
|
|
|
16
16
|
function readPackageJson() {
|
|
17
|
-
const filePath = path.join(import.meta.dirname
|
|
17
|
+
const filePath = path.join(import.meta.dirname!, '..', 'package.json')
|
|
18
18
|
const fileContents = fs.readFileSync(filePath, 'utf-8')
|
|
19
19
|
return JSON.parse(fileContents)
|
|
20
20
|
}
|
|
@@ -77,19 +77,35 @@ function executeCreateTests(
|
|
|
77
77
|
continue
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
if (
|
|
80
|
+
if (
|
|
81
|
+
ignoredSourceDir !== undefined &&
|
|
82
|
+
sourcePath.startsWith(ignoredSourceDir)
|
|
83
|
+
) {
|
|
81
84
|
continue
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
if (
|
|
87
|
+
if (
|
|
88
|
+
fs.existsSync(destinationPath) &&
|
|
89
|
+
fs.statSync(destinationPath).isDirectory() === false
|
|
90
|
+
) {
|
|
85
91
|
continue
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
executeCreateTests(
|
|
94
|
+
executeCreateTests(
|
|
95
|
+
sourcePath,
|
|
96
|
+
destinationPath,
|
|
97
|
+
fileContents,
|
|
98
|
+
ignoredSourceDir,
|
|
99
|
+
rootSourceDir
|
|
100
|
+
)
|
|
89
101
|
continue
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
entry.isFile() &&
|
|
106
|
+
entry.name.endsWith('.ts') &&
|
|
107
|
+
fs.existsSync(destinationPath) === false
|
|
108
|
+
) {
|
|
93
109
|
fs.writeFileSync(destinationPath, fileContents)
|
|
94
110
|
}
|
|
95
111
|
}
|
|
@@ -104,10 +120,12 @@ async function ensureTestsDirectory(testsRootDir: string) {
|
|
|
104
120
|
return
|
|
105
121
|
}
|
|
106
122
|
|
|
107
|
-
const rl = readline.createInterface({ input, output })
|
|
123
|
+
const rl = readline.createInterface({ input: stdin, output: stdout })
|
|
108
124
|
|
|
109
125
|
try {
|
|
110
|
-
const answer = await rl.question(
|
|
126
|
+
const answer = await rl.question(
|
|
127
|
+
`Tests directory does not exist at ${testsRootDir}. Create it? (y/N) `
|
|
128
|
+
)
|
|
111
129
|
|
|
112
130
|
if (answer.trim().toLowerCase() !== 'y') {
|
|
113
131
|
return false
|
|
@@ -121,7 +139,7 @@ async function ensureTestsDirectory(testsRootDir: string) {
|
|
|
121
139
|
}
|
|
122
140
|
|
|
123
141
|
async function main(program: typeof import('commander').program) {
|
|
124
|
-
const templatesDir = path.join(import.meta.dirname
|
|
142
|
+
const templatesDir = path.join(import.meta.dirname!, '..', 'templates')
|
|
125
143
|
|
|
126
144
|
const options = program.opts()
|
|
127
145
|
|
|
@@ -182,7 +200,9 @@ async function main(program: typeof import('commander').program) {
|
|
|
182
200
|
const testsDir = path.join(testsRootDir, path.basename(sourceDir))
|
|
183
201
|
|
|
184
202
|
if (testsDir === sourceDir) {
|
|
185
|
-
program.error(
|
|
203
|
+
program.error(
|
|
204
|
+
'Tests destination directory cannot be the same as the source directory'
|
|
205
|
+
)
|
|
186
206
|
}
|
|
187
207
|
|
|
188
208
|
executeCreateTests(sourceDir, testsDir, testsTemplateContents, testsRootDir)
|
|
@@ -198,7 +218,10 @@ program
|
|
|
198
218
|
.option('-P, --project <name>', "creates a new project with it's shared directory")
|
|
199
219
|
.option('-C, --context <name>', 'creates a new context')
|
|
200
220
|
.option('-R, --react', 'creates a React context with --context')
|
|
201
|
-
.option(
|
|
221
|
+
.option(
|
|
222
|
+
'-T, --tests <path>',
|
|
223
|
+
'creates a .ts tests structure from an existing directory'
|
|
224
|
+
)
|
|
202
225
|
.option('--dir <path>', 'sets the directory to create the new item')
|
|
203
226
|
.parse(process.argv)
|
|
204
227
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
type Generic = Record<string, unknown>
|
|
2
|
+
|
|
3
|
+
export interface Listable<DataShape extends Generic = Generic> {
|
|
4
|
+
all(): DataShape[]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @description Declares a filtering operation over plain source records.
|
|
9
|
+
*/
|
|
10
|
+
export interface Filterable<DataShape extends Generic = Generic, Selector = unknown> {
|
|
11
|
+
filter(selector: Selector): DataShape[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @description Declares a sorting operation over plain source records.
|
|
16
|
+
*/
|
|
17
|
+
export interface Sortable<DataShape extends Generic = Generic, Selector = unknown> {
|
|
18
|
+
sort(selector: Selector): DataShape[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @description Declares a creation operation for plain source records.
|
|
23
|
+
*/
|
|
24
|
+
export interface Creatable<DataShape extends Generic = Generic, Feedback = unknown> {
|
|
25
|
+
create(data: DataShape): Feedback
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @description Declares an update operation that selects source records and applies new plain data.
|
|
30
|
+
*/
|
|
31
|
+
export interface Updatable<
|
|
32
|
+
DataShape extends Generic = Generic,
|
|
33
|
+
Selector = unknown,
|
|
34
|
+
Feedback = unknown
|
|
35
|
+
> {
|
|
36
|
+
update(selector: Selector, data: Partial<DataShape>): Feedback
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description Declares a deletion operation over source records selected by plain criteria.
|
|
41
|
+
*/
|
|
42
|
+
export interface Deletable<Selector = unknown, Feedback = unknown> {
|
|
43
|
+
delete(selector: Selector): Feedback
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @description Declares an aggregation operation over source records.
|
|
48
|
+
*/
|
|
49
|
+
export interface Aggregatable<Selector = unknown, Feedback = unknown> {
|
|
50
|
+
aggregate(selector: Selector): Feedback
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @description Declares operations for selecting or preloading relationships from a data source.
|
|
55
|
+
*/
|
|
56
|
+
export interface Relatable {
|
|
57
|
+
selectRelated(...args: unknown[]): unknown
|
|
58
|
+
|
|
59
|
+
prefetchRelated(...args: unknown[]): unknown
|
|
60
|
+
}
|
|
@@ -9,7 +9,7 @@ import { DataManager } from './managers.js'
|
|
|
9
9
|
export abstract class DriverAdapter<M extends DataManager = DataManager> {
|
|
10
10
|
[property: string]: unknown
|
|
11
11
|
|
|
12
|
-
public abstract connect(...args: unknown[]):
|
|
12
|
+
public abstract connect(...args: unknown[]): M
|
|
13
13
|
|
|
14
|
-
public abstract disconnect():
|
|
14
|
+
public abstract disconnect(): unknown
|
|
15
15
|
} //:: class
|
|
@@ -1,66 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Declares a filtering operation over plain source records.
|
|
3
|
-
*/
|
|
4
|
-
export interface Filterable<S = Record<string, unknown>> {
|
|
5
|
-
filter(selector: S): Promise<Array<S>>
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @description Declares a sorting operation over plain source records.
|
|
10
|
-
*/
|
|
11
|
-
export interface Sortable<S = Record<string, unknown>> {
|
|
12
|
-
sort(selector: S): Promise<Array<S>>
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @description Declares a creation operation for plain source records.
|
|
17
|
-
*/
|
|
18
|
-
export interface Creatable<D = Record<string, unknown>> {
|
|
19
|
-
create(data: D): Promise<unknown>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @description Declares an update operation that selects source records and applies new plain data.
|
|
24
|
-
*/
|
|
25
|
-
export interface Updatable<S = Record<string, unknown>, D = Record<string, unknown>> {
|
|
26
|
-
update(selector: S, data: D): Promise<unknown>
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @description Declares a deletion operation over source records selected by plain criteria.
|
|
31
|
-
*/
|
|
32
|
-
export interface Deletable<S = Record<string, unknown>> {
|
|
33
|
-
delete(selector: S): Promise<unknown>
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @description Declares an aggregation operation over source records.
|
|
38
|
-
*/
|
|
39
|
-
export interface Aggregatable<S = Record<string, unknown>> {
|
|
40
|
-
aggregate(selector: S): Promise<S>
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @description Declares operations for selecting or preloading relationships from a data source.
|
|
45
|
-
*/
|
|
46
|
-
export interface Relatable {
|
|
47
|
-
selectRelated(...args: unknown[]): unknown
|
|
48
|
-
|
|
49
|
-
prefetchRelated(...args: unknown[]): unknown
|
|
50
|
-
}
|
|
51
|
-
|
|
52
1
|
/**
|
|
53
2
|
* @description Operates on a data source using plain objects and arrays.
|
|
54
3
|
* It exposes the raw data without transforming it.
|
|
55
4
|
*/
|
|
56
5
|
export abstract class DataManager<T = Record<string, unknown>> {
|
|
57
6
|
[property: string]: unknown
|
|
58
|
-
|
|
59
|
-
public none(): Array<T> {
|
|
60
|
-
return []
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public abstract all(): Promise<Array<T>>
|
|
64
7
|
} //:: class
|
|
65
8
|
|
|
66
9
|
/**
|
|
@@ -69,13 +12,13 @@ export abstract class DataManager<T = Record<string, unknown>> {
|
|
|
69
12
|
export abstract class DatasetManager<T = Record<string, unknown>> extends DataManager<T> {
|
|
70
13
|
[property: string]: unknown
|
|
71
14
|
|
|
72
|
-
public abstract union(other: Array<T>):
|
|
15
|
+
public abstract union(other: Array<T>): Array<T>
|
|
73
16
|
|
|
74
|
-
public abstract intersection(other: Array<T>):
|
|
17
|
+
public abstract intersection(other: Array<T>): Array<T>
|
|
75
18
|
|
|
76
|
-
public abstract difference(other: Array<T>):
|
|
19
|
+
public abstract difference(other: Array<T>): Array<T>
|
|
77
20
|
|
|
78
|
-
public abstract symmetricDifference(other: Array<T>):
|
|
21
|
+
public abstract symmetricDifference(other: Array<T>): Array<T>
|
|
79
22
|
|
|
80
|
-
public abstract complement(other: Array<T>):
|
|
23
|
+
public abstract complement(other: Array<T>): Array<T>
|
|
81
24
|
} //:: class
|