tshex-cli 1.0.27 → 1.0.29
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/generated-file-reference.md +13 -10
- package/docs/library-structure.md +4 -4
- package/docs/shared/application/data.md +13 -1
- package/docs/shared/application/http/errors.md +96 -0
- package/docs/shared/application/http/handlers.md +102 -0
- package/docs/shared/application/http/json-api.md +235 -0
- package/docs/shared/application/http/json-web-token.md +209 -0
- package/docs/shared/application/http/opengraph.md +161 -0
- package/docs/shared/application/loggers.md +5 -4
- package/docs/types/json.md +82 -0
- package/docs/types/locales.md +77 -0
- package/docs/types/objects.md +70 -0
- package/docs/types/timezones.md +75 -0
- package/package.json +7 -8
- package/readme.md +23 -4
- package/source/main.ts +34 -11
- package/templates/ctx/example-ports.ts +1 -0
- package/templates/lib/shared/application/data/capabilities.ts +56 -0
- package/templates/lib/shared/application/data/managers.ts +0 -57
- package/templates/lib/shared/application/data/repositories.ts +7 -18
- package/templates/lib/shared/application/loggers.ts +0 -21
- package/templates/lib/shared/domain/entities.ts +1 -1
- package/docs/library-types.md +0 -112
- package/docs/shared/application/http.md +0 -283
- 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
- /package/templates/lib/types/{cldr.d.ts → locales.d.ts} +0 -0
- /package/templates/lib/types/{iana.d.ts → timezones.d.ts} +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
### JSON
|
|
2
|
+
|
|
3
|
+
The generated root also declares a small family of types that describe plain,
|
|
4
|
+
serializable JSON data.
|
|
5
|
+
They are used when a contract must guarantee that a value survives a round
|
|
6
|
+
trip through `JSON.stringify()` / `JSON.parse()`.
|
|
7
|
+
|
|
8
|
+
#### Declaration
|
|
9
|
+
|
|
10
|
+
These types live in `types/json.d.ts`.
|
|
11
|
+
|
|
12
|
+
```ts title="types/json.d.ts"
|
|
13
|
+
export type JsonPrimitive = string | number | boolean | null
|
|
14
|
+
|
|
15
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonArray
|
|
16
|
+
|
|
17
|
+
export type JsonArray = readonly JsonValue[]
|
|
18
|
+
|
|
19
|
+
export type JsonObject = {
|
|
20
|
+
readonly [key: string]: JsonValue
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`JsonPrimitive` covers the scalar values allowed in JSON. `JsonValue` extends
|
|
25
|
+
that with nested objects and arrays, so it recursively describes any JSON-safe
|
|
26
|
+
value. `JsonObject` and `JsonArray` name the two composite shapes so other
|
|
27
|
+
declarations can refer to them directly instead of repeating the union.
|
|
28
|
+
|
|
29
|
+
#### Implementation Options
|
|
30
|
+
|
|
31
|
+
A `JsonValue` is always one of four shapes. Each one is a distinct option a
|
|
32
|
+
consumer must be ready to handle.
|
|
33
|
+
|
|
34
|
+
| Shape | Type | Example |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| Primitive | `JsonPrimitive` | `'active'`, `42`, `true`, `null` |
|
|
37
|
+
| Object | `JsonObject` | `{ id: '1', active: true }` |
|
|
38
|
+
| Array | `JsonArray` | `[1, 2, 3]`, `[{ id: '1' }]` |
|
|
39
|
+
| Nested composite | `JsonValue` | `{ tags: ['a', 'b'], meta: { retries: 2 } }` |
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { type JsonValue } from './types/json.js'
|
|
43
|
+
|
|
44
|
+
const primitive: JsonValue = 'ada@example.com'
|
|
45
|
+
const object: JsonValue = { id: '1', active: true }
|
|
46
|
+
const array: JsonValue = [1, 2, 3]
|
|
47
|
+
const nested: JsonValue = { tags: ['a', 'b'], meta: { retries: 2 } }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
All four are valid `JsonValue` values because the type is a recursive union;
|
|
51
|
+
there is no separate constructor or runtime check to opt into a shape.
|
|
52
|
+
|
|
53
|
+
#### Basic Usage
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { type JsonValue } from './types/json.js'
|
|
57
|
+
|
|
58
|
+
function toLogPayload(value: JsonValue): string {
|
|
59
|
+
return JSON.stringify(value)
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Because `JsonValue` excludes functions, `undefined`, symbols, and other
|
|
64
|
+
non-serializable values, `toLogPayload()` can call `JSON.stringify()` without
|
|
65
|
+
guarding against values that would silently disappear or throw.
|
|
66
|
+
|
|
67
|
+
#### JsonObject Versus Generic
|
|
68
|
+
|
|
69
|
+
Use `JsonObject`/`JsonValue` when a contract must guarantee its data is plain
|
|
70
|
+
and serializable, such as request payloads, stored metadata, or wire formats.
|
|
71
|
+
Prefer `types/objects.md`'s `Generic<T>` instead when the value type is not
|
|
72
|
+
required to be JSON-safe.
|
|
73
|
+
|
|
74
|
+
`shared/application/http/json-api.ts` and
|
|
75
|
+
`shared/application/http/json-web-token.ts` build on these types to describe
|
|
76
|
+
JSON:API documents and JOSE/JWT structures; see `shared/application/http/json-api.md`
|
|
77
|
+
and `shared/application/http/json-web-token.md`.
|
|
78
|
+
|
|
79
|
+
> **Hint**
|
|
80
|
+
> These declarations only provide compile-time structure. They do not validate
|
|
81
|
+
> that a runtime value is actually JSON-safe; a value typed as `JsonValue` can
|
|
82
|
+
> still contain a `Date` or a class instance if it was cast into the type.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
### Locales
|
|
2
|
+
|
|
3
|
+
`Locale` is a literal string union of every locale identifier available in
|
|
4
|
+
Unicode CLDR.
|
|
5
|
+
It is used when a contract needs to accept only valid locale tags instead of
|
|
6
|
+
an open `string`.
|
|
7
|
+
|
|
8
|
+
#### Declaration
|
|
9
|
+
|
|
10
|
+
`Locale` lives in `types/locales.d.ts` and is generated from Unicode CLDR
|
|
11
|
+
48.2.1.
|
|
12
|
+
|
|
13
|
+
```ts title="types/locales.d.ts"
|
|
14
|
+
export type Locale =
|
|
15
|
+
| 'aa'
|
|
16
|
+
| 'af'
|
|
17
|
+
| 'am'
|
|
18
|
+
| 'ar'
|
|
19
|
+
| 'ar-EG'
|
|
20
|
+
| 'de'
|
|
21
|
+
| 'de-AT'
|
|
22
|
+
| 'en'
|
|
23
|
+
| 'en-GB'
|
|
24
|
+
| 'en-US'
|
|
25
|
+
| 'es'
|
|
26
|
+
| 'es-419'
|
|
27
|
+
| 'fr'
|
|
28
|
+
| 'ja'
|
|
29
|
+
| 'zh-Hans'
|
|
30
|
+
// ...every other CLDR locale identifier
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The generated file lists every language identifier and every regional variant
|
|
34
|
+
registered by CLDR, from bare language tags such as `'en'` to script- and
|
|
35
|
+
region-qualified tags such as `'zh-Hant-HK'` or `'ca-ES-valencia'`.
|
|
36
|
+
|
|
37
|
+
#### Basic Usage
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { type Locale } from './types/locales.js'
|
|
41
|
+
|
|
42
|
+
function formatCount(value: number, locale: Locale): string {
|
|
43
|
+
return new Intl.NumberFormat(locale).format(value)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
formatCount(1200, 'en-US')
|
|
47
|
+
formatCount(1200, 'es-419')
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Because `Locale` only accepts identifiers CLDR actually defines, a typo such
|
|
51
|
+
as `'en-USA'` fails at compile time instead of silently reaching
|
|
52
|
+
`Intl.NumberFormat`.
|
|
53
|
+
|
|
54
|
+
#### Accepting Multiple Locales
|
|
55
|
+
|
|
56
|
+
`Intl` APIs commonly accept a locale or a list of locales in priority order.
|
|
57
|
+
`Locale[]` expresses that same option without widening to `string[]`.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { type Locale } from './types/locales.js'
|
|
61
|
+
|
|
62
|
+
const preferredLocales: Locale[] = ['fr-CA', 'fr', 'en']
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The runtime resolves the first supported locale from the list; `Locale[]`
|
|
66
|
+
only guarantees that every candidate is a real CLDR identifier.
|
|
67
|
+
|
|
68
|
+
#### Where It Is Used
|
|
69
|
+
|
|
70
|
+
`shared/application/loggers.ts` uses `Locale[]` for `Logger.datetimeLocales`,
|
|
71
|
+
the locale list passed to `Date.prototype.toLocaleString()` when formatting a
|
|
72
|
+
log timestamp. See `shared/application/loggers.md`.
|
|
73
|
+
|
|
74
|
+
> **Hint**
|
|
75
|
+
> `Locale` is a compile-time contract only. It does not validate that the
|
|
76
|
+
> runtime's ICU data actually supports every listed locale; `Intl` APIs fall
|
|
77
|
+
> back to a default when a requested locale is unsupported at runtime.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
### Objects
|
|
2
|
+
|
|
3
|
+
The generated root declares `Generic<T>`, a plain object whose keys are strings
|
|
4
|
+
and whose values share the same type.
|
|
5
|
+
It provides a small common building block for code that works with object-like
|
|
6
|
+
data but does not need a more specific shape yet.
|
|
7
|
+
|
|
8
|
+
#### Declaration
|
|
9
|
+
|
|
10
|
+
`Generic<T>` lives in `types/objects.d.ts`.
|
|
11
|
+
|
|
12
|
+
```ts title="types/objects.d.ts"
|
|
13
|
+
export type Generic<T = unknown> = Record<string, T>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This alias expands to `Record<string, T>`. When no type argument is provided,
|
|
17
|
+
the values use `unknown`.
|
|
18
|
+
|
|
19
|
+
#### With A Type Argument
|
|
20
|
+
|
|
21
|
+
In the following example we use `Generic<string>` for a set of plain filters.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { type Generic } from './types/objects.js'
|
|
25
|
+
|
|
26
|
+
const filters: Generic<string> = {
|
|
27
|
+
status: 'active',
|
|
28
|
+
sort: 'email',
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`filters` can only store string values because the type argument fixes the
|
|
33
|
+
value shape for the whole object.
|
|
34
|
+
|
|
35
|
+
#### Without A Type Argument
|
|
36
|
+
|
|
37
|
+
Now consider the same pattern without providing a type argument.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { type Generic } from './types/objects.js'
|
|
41
|
+
|
|
42
|
+
const metadata: Generic = {
|
|
43
|
+
retries: 2,
|
|
44
|
+
cached: true,
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
In this case the values use `unknown`. This is useful when the object is plain
|
|
49
|
+
and open-ended, but the caller must narrow each value before using it in a
|
|
50
|
+
specific way.
|
|
51
|
+
|
|
52
|
+
#### When To Use It
|
|
53
|
+
|
|
54
|
+
Use `Generic<T>` when the code needs a simple object contract and the exact set
|
|
55
|
+
of keys is not the main concern.
|
|
56
|
+
|
|
57
|
+
Typical uses include:
|
|
58
|
+
|
|
59
|
+
1. filter objects;
|
|
60
|
+
2. metadata objects;
|
|
61
|
+
3. plain configuration maps;
|
|
62
|
+
4. transport-neutral dictionaries.
|
|
63
|
+
|
|
64
|
+
When the object has a stable business meaning, prefer a named type instead of a
|
|
65
|
+
generic record.
|
|
66
|
+
|
|
67
|
+
> **Hint**
|
|
68
|
+
> `Generic<T>` is intentionally small. It should support loose object contracts,
|
|
69
|
+
> not replace explicit domain or application types. It is also the base shape
|
|
70
|
+
> used by `types/json.md` when a value only needs to be JSON-safe.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
### Time Zones
|
|
2
|
+
|
|
3
|
+
`TimeZone` is a literal string union of every IANA time zone identifier.
|
|
4
|
+
It is used when a contract needs to accept only valid time zone names instead
|
|
5
|
+
of an open `string`.
|
|
6
|
+
|
|
7
|
+
#### Declaration
|
|
8
|
+
|
|
9
|
+
`TimeZone` lives in `types/timezones.d.ts` and is generated from the IANA time
|
|
10
|
+
zone database.
|
|
11
|
+
|
|
12
|
+
```ts title="types/timezones.d.ts"
|
|
13
|
+
export type TimeZone =
|
|
14
|
+
| 'Africa/Cairo'
|
|
15
|
+
| 'America/Argentina/Buenos_Aires'
|
|
16
|
+
| 'America/Indiana/Indianapolis'
|
|
17
|
+
| 'America/New_York'
|
|
18
|
+
| 'Asia/Tokyo'
|
|
19
|
+
| 'Australia/Sydney'
|
|
20
|
+
| 'Europe/London'
|
|
21
|
+
| 'Europe/Paris'
|
|
22
|
+
| 'Pacific/Auckland'
|
|
23
|
+
| 'UTC'
|
|
24
|
+
// ...every other IANA time zone identifier
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The generated file lists every canonical zone, including three-level entries
|
|
28
|
+
such as `'America/Argentina/Buenos_Aires'` and `'America/Indiana/Knox'`, and
|
|
29
|
+
ends with the fixed `'UTC'` identifier.
|
|
30
|
+
|
|
31
|
+
#### Basic Usage
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { type TimeZone } from './types/timezones.js'
|
|
35
|
+
|
|
36
|
+
function formatInZone(date: Date, timeZone: TimeZone): string {
|
|
37
|
+
return date.toLocaleString('en-GB', { timeZone })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
formatInZone(new Date(), 'America/New_York')
|
|
41
|
+
formatInZone(new Date(), 'UTC')
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Because `TimeZone` only accepts identifiers the IANA database actually
|
|
45
|
+
defines, a typo such as `'America/New York'` (with a space) fails at compile
|
|
46
|
+
time instead of throwing a `RangeError` at runtime.
|
|
47
|
+
|
|
48
|
+
#### Combining With `Intl.DateTimeFormatOptions`
|
|
49
|
+
|
|
50
|
+
`TimeZone` is meant to replace the loosely typed `timeZone` member of
|
|
51
|
+
`Intl.DateTimeFormatOptions`.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { type TimeZone } from './types/timezones.js'
|
|
55
|
+
|
|
56
|
+
const options: Intl.DateTimeFormatOptions & { timeZone: TimeZone } = {
|
|
57
|
+
timeZone: 'Europe/Madrid',
|
|
58
|
+
hour: '2-digit',
|
|
59
|
+
minute: '2-digit',
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The intersection keeps every other formatting option from
|
|
64
|
+
`Intl.DateTimeFormatOptions` while narrowing `timeZone` to a real identifier.
|
|
65
|
+
|
|
66
|
+
#### Where It Is Used
|
|
67
|
+
|
|
68
|
+
`shared/application/loggers.ts` uses this same intersection for
|
|
69
|
+
`Logger.datetimeFormatOptions`, defaulting `timeZone` to `'UTC'`. See
|
|
70
|
+
`shared/application/loggers.md`.
|
|
71
|
+
|
|
72
|
+
> **Hint**
|
|
73
|
+
> `TimeZone` is a compile-time contract only. It does not validate that the
|
|
74
|
+
> runtime's ICU data actually supports every listed zone, and it does not
|
|
75
|
+
> account for future IANA database changes such as renamed or merged zones.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tshex-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
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/readme.md
CHANGED
|
@@ -82,7 +82,6 @@ The command creates this structure:
|
|
|
82
82
|
|
|
83
83
|
```text
|
|
84
84
|
core/
|
|
85
|
-
|-- index.d.ts
|
|
86
85
|
|-- main.ts
|
|
87
86
|
|-- shared/
|
|
88
87
|
| |-- application/
|
|
@@ -91,7 +90,12 @@ core/
|
|
|
91
90
|
| | | |-- managers.ts
|
|
92
91
|
| | | `-- repositories.ts
|
|
93
92
|
| | |-- events.ts
|
|
94
|
-
| | |-- http
|
|
93
|
+
| | |-- http/
|
|
94
|
+
| | | |-- errors.ts
|
|
95
|
+
| | | |-- handlers.ts
|
|
96
|
+
| | | |-- json-api.ts
|
|
97
|
+
| | | |-- json-web-token.ts
|
|
98
|
+
| | | `-- opengraph.ts
|
|
95
99
|
| | |-- loggers.ts
|
|
96
100
|
| | |-- services.ts
|
|
97
101
|
| | `-- validations.ts
|
|
@@ -100,6 +104,11 @@ core/
|
|
|
100
104
|
| |-- entities.ts
|
|
101
105
|
| |-- errors.ts
|
|
102
106
|
| `-- value-objects.ts
|
|
107
|
+
|-- types/
|
|
108
|
+
| |-- json.d.ts
|
|
109
|
+
| |-- locales.d.ts
|
|
110
|
+
| |-- objects.d.ts
|
|
111
|
+
| `-- timezones.d.ts
|
|
103
112
|
`-- users/
|
|
104
113
|
|-- adapters/
|
|
105
114
|
|-- application/
|
|
@@ -224,15 +233,25 @@ From this point on, the guide is split into dedicated documents under `docs/`.
|
|
|
224
233
|
### General
|
|
225
234
|
|
|
226
235
|
- [Project structure](https://github.com/virtualitems/tshex-cli/blob/main/docs/library-structure.md)
|
|
227
|
-
- [Project types](https://github.com/virtualitems/tshex-cli/blob/main/docs/library-types.md)
|
|
228
236
|
- [Context ports](https://github.com/virtualitems/tshex-cli/blob/main/docs/context-ports.md)
|
|
229
237
|
- [Generated file reference](https://github.com/virtualitems/tshex-cli/blob/main/docs/generated-file-reference.md)
|
|
230
238
|
|
|
239
|
+
### Types
|
|
240
|
+
|
|
241
|
+
- [types/objects.d.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/types/objects.md)
|
|
242
|
+
- [types/json.d.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/types/json.md)
|
|
243
|
+
- [types/locales.d.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/types/locales.md)
|
|
244
|
+
- [types/timezones.d.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/types/timezones.md)
|
|
245
|
+
|
|
231
246
|
### Shared application
|
|
232
247
|
|
|
233
248
|
- [shared/application/data](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/data.md)
|
|
234
249
|
- [shared/application/events.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/events.md)
|
|
235
|
-
- [shared/application/http.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/http.md)
|
|
250
|
+
- [shared/application/http/errors.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/http/errors.md)
|
|
251
|
+
- [shared/application/http/handlers.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/http/handlers.md)
|
|
252
|
+
- [shared/application/http/json-api.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/http/json-api.md)
|
|
253
|
+
- [shared/application/http/json-web-token.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/http/json-web-token.md)
|
|
254
|
+
- [shared/application/http/opengraph.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/http/opengraph.md)
|
|
236
255
|
- [shared/application/loggers.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/loggers.md)
|
|
237
256
|
- [shared/application/services.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/services.md)
|
|
238
257
|
- [shared/application/validations.ts](https://github.com/virtualitems/tshex-cli/blob/main/docs/shared/application/validations.md)
|
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,56 @@
|
|
|
1
|
+
type Generic = Record<string, unknown>
|
|
2
|
+
|
|
3
|
+
export interface Listable {
|
|
4
|
+
all(): Generic | Promise<Generic[]>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @description Declares a filtering operation over plain source records.
|
|
9
|
+
*/
|
|
10
|
+
export interface Filterable {
|
|
11
|
+
filter(selector: unknown): Generic | Promise<Generic[]>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @description Declares a sorting operation over plain source records.
|
|
16
|
+
*/
|
|
17
|
+
export interface Sortable {
|
|
18
|
+
sort(selector: unknown): Generic | Promise<Generic[]>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @description Declares a creation operation for plain source records.
|
|
23
|
+
*/
|
|
24
|
+
export interface Creatable {
|
|
25
|
+
create(data: unknown): unknown
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @description Declares an update operation that selects source records and applies new plain data.
|
|
30
|
+
*/
|
|
31
|
+
export interface Updatable {
|
|
32
|
+
update(selector: unknown, data: unknown): unknown
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @description Declares a deletion operation over source records selected by plain criteria.
|
|
37
|
+
*/
|
|
38
|
+
export interface Deletable {
|
|
39
|
+
delete(selector: unknown): unknown
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @description Declares an aggregation operation over source records.
|
|
44
|
+
*/
|
|
45
|
+
export interface Aggregatable {
|
|
46
|
+
aggregate(selector: unknown): unknown
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @description Declares operations for selecting or preloading relationships from a data source.
|
|
51
|
+
*/
|
|
52
|
+
export interface Relatable {
|
|
53
|
+
selectRelated(...args: unknown[]): unknown
|
|
54
|
+
|
|
55
|
+
prefetchRelated(...args: unknown[]): unknown
|
|
56
|
+
}
|
|
@@ -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
|
/**
|
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
import { type DataManager } from './managers.js'
|
|
2
2
|
import { type DriverAdapter } from './drivers.js'
|
|
3
3
|
|
|
4
|
+
type Generic = Record<string, unknown>
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* @description Acts as an intermediary between plain source data and domain objects.
|
|
6
8
|
* It transforms records into domain representations and can translate them back when needed.
|
|
7
9
|
*/
|
|
8
|
-
export abstract class Repository<
|
|
9
|
-
DataShape extends Record<string, unknown> = Record<string, unknown>,
|
|
10
|
-
EntityShape extends Record<string, unknown> = Record<string, unknown>
|
|
11
|
-
> {
|
|
10
|
+
export abstract class Repository<RawDataShape = Generic, EntityShape = Generic> {
|
|
12
11
|
[property: string]: unknown
|
|
13
12
|
|
|
14
|
-
public constructor(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const connection = await this.driver.connect()
|
|
18
|
-
const raw = await connection.all()
|
|
19
|
-
const entities = this.transformList(raw)
|
|
20
|
-
await this.driver.disconnect()
|
|
21
|
-
return entities
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
protected transformList(data: Array<DataShape>): Array<EntityShape> {
|
|
25
|
-
return data.map(this.transform)
|
|
26
|
-
}
|
|
13
|
+
public constructor(
|
|
14
|
+
public readonly driver: DriverAdapter<DataManager<RawDataShape>>
|
|
15
|
+
) {}
|
|
27
16
|
|
|
28
|
-
protected abstract transform(data:
|
|
17
|
+
protected abstract transform(data: RawDataShape): EntityShape
|
|
29
18
|
} //:: class
|