tshex-cli 1.0.25 → 1.0.27

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.
@@ -9,12 +9,26 @@ The root contains the main entry points of the generated library.
9
9
 
10
10
  | File | Responsibility |
11
11
  | --- | --- |
12
- | `index.d.ts` | Declares root-level shared types such as `Generic<T>`. |
13
12
  | `main.ts` | Starts as a placeholder for the main implementation and root exports. |
14
13
 
15
- `index.d.ts` is the place for root-level type declarations. `main.ts` is the
16
- place for the main runtime entry point when the library starts exposing shared
17
- runtime components.
14
+ `main.ts` is the place for the main runtime entry point when the library
15
+ starts exposing shared runtime components.
16
+
17
+ #### Types Files
18
+
19
+ The `types/` directory contains root-level ambient type declarations.
20
+
21
+ | File | Responsibility |
22
+ | --- | --- |
23
+ | `types/objects.d.ts` | Declares root-level shared types such as `Generic<T>`. |
24
+ | `types/json.d.ts` | Declares `JsonValue` and the other plain, serializable JSON shapes. |
25
+ | `types/cldr.d.ts` | Declares the `Locale` union from Unicode CLDR. |
26
+ | `types/iana.d.ts` | Declares the `TimeZone` union from the IANA time zone database. |
27
+
28
+ `types/objects.d.ts` and `types/json.d.ts` are the place for general-purpose
29
+ root-level type declarations. `types/cldr.d.ts` and `types/iana.d.ts` are
30
+ generated reference types consumed by other shared contracts, such as
31
+ `shared/application/loggers.ts`.
18
32
 
19
33
  #### Shared Domain Files
20
34
 
@@ -39,13 +53,29 @@ cases and integrations.
39
53
  | --- | --- |
40
54
  | `shared/application/validations.ts` | Declares the `Validatable` contract. |
41
55
  | `shared/application/services.ts` | Declares the `Service` base class for use cases. |
42
- | `shared/application/http.ts` | Declares framework-agnostic HTTP contracts. |
43
56
  | `shared/application/loggers.ts` | Declares shared log levels and the `Logger` contract. |
44
57
  | `shared/application/events.ts` | Declares `Event`, `EventHandler`, and `EventDispatcher`. |
45
58
 
46
59
  These files do not implement frameworks or transports. They define the stable
47
60
  contracts that adapters and services can share.
48
61
 
62
+ #### Shared HTTP Files
63
+
64
+ The `shared/application/http` directory groups the framework-agnostic HTTP
65
+ boundary and the type-only specifications for common web content formats.
66
+
67
+ | File | Responsibility |
68
+ | --- | --- |
69
+ | `shared/application/http/http.ts` | Declares `HttpRequestHandler`, `HttpMiddleware`, and `HttpError`. |
70
+ | `shared/application/http/json-api.ts` | Type-only JSON:API v1.1 document, resource, and Atomic Operations declarations. |
71
+ | `shared/application/http/json-web-token.ts` | Type-only JOSE/JWT declarations (JWK, JWS, JWE, JWT claims). |
72
+ | `shared/application/http/opengraph.ts` | Type-only Open Graph, Twitter Card, and social metadata declarations. |
73
+
74
+ `http.ts` is the only file in this directory with runtime code. `json-api.ts`,
75
+ `json-web-token.ts`, and `opengraph.ts` contain compile-time structure only;
76
+ they describe the shape of external formats without implementing parsing,
77
+ validation, or serialization.
78
+
49
79
  #### Shared Data Files
50
80
 
51
81
  The generated template also includes a small set of data-access abstractions.
@@ -15,15 +15,34 @@ The root contains the entry points of the generated library.
15
15
 
16
16
  ```mermaid
17
17
  flowchart TD
18
- root["Library root"] --> index["index.d.ts"]
18
+ root["Library root"] --> types["types/"]
19
19
  root --> main["main.ts"]
20
20
  root --> shared["shared/"]
21
21
  root --> users["users/"]
22
22
  ```
23
23
 
24
- `index.d.ts` defines root-level types. `main.ts` starts as a placeholder for
25
- main runtime exports. The rest of the structure lives under `shared/` and one
26
- or more context directories.
24
+ `types/` groups the root-level type declarations. `main.ts` starts as a
25
+ placeholder for main runtime exports. The rest of the structure lives under
26
+ `shared/` and one or more context directories.
27
+
28
+ #### Types
29
+
30
+ The `types/` directory contains ambient type declarations shared by the whole
31
+ library.
32
+
33
+ ```mermaid
34
+ flowchart TD
35
+ types["types/"] --> typesObjects["objects.d.ts"]
36
+ types --> json["json.d.ts"]
37
+ types --> cldr["cldr.d.ts"]
38
+ types --> iana["iana.d.ts"]
39
+ ```
40
+
41
+ `types/objects.d.ts` defines root-level types such as `Generic<T>`.
42
+ `types/json.d.ts` defines `JsonValue` and the other plain, serializable JSON
43
+ shapes. `types/cldr.d.ts` declares the `Locale` union from Unicode CLDR.
44
+ `types/iana.d.ts` declares the `TimeZone` union from the IANA time zone
45
+ database.
27
46
 
28
47
  #### Shared
29
48
 
@@ -7,10 +7,10 @@ data but does not need a more specific shape yet.
7
7
 
8
8
  #### Root Declaration
9
9
 
10
- The root declaration lives in `index.d.ts`.
10
+ The root declaration lives in `types/objects.d.ts`.
11
11
 
12
- ```ts title="index.d.ts"
13
- type Generic<T = unknown> = Record<string, T>
12
+ ```ts title="types/objects.d.ts"
13
+ export type Generic<T = unknown> = Record<string, T>
14
14
  ```
15
15
 
16
16
  This alias expands to `Record<string, T>`. When no type argument is provided,
@@ -21,6 +21,8 @@ the values use `unknown`.
21
21
  In the following example we use `Generic<string>` for a set of plain filters.
22
22
 
23
23
  ```ts
24
+ import { type Generic } from './types/objects.js'
25
+
24
26
  const filters: Generic<string> = {
25
27
  status: 'active',
26
28
  sort: 'email',
@@ -35,6 +37,8 @@ value shape for the whole object.
35
37
  Now consider the same pattern without providing a type argument.
36
38
 
37
39
  ```ts
40
+ import { type Generic } from './types/objects.js'
41
+
38
42
  const metadata: Generic = {
39
43
  retries: 2,
40
44
  cached: true,
@@ -64,6 +68,44 @@ generic record.
64
68
  > `Generic<T>` is intentionally small. It should support loose object contracts,
65
69
  > not replace explicit domain or application types.
66
70
 
71
+ #### JSON Values
72
+
73
+ The generated root also declares a small family of types that describe plain,
74
+ serializable JSON data. They live in `types/json.d.ts`.
75
+
76
+ ```ts title="types/json.d.ts"
77
+ export type JsonPrimitive = string | number | boolean | null
78
+
79
+ export type JsonValue = JsonPrimitive | JsonObject | JsonArray
80
+
81
+ export type JsonArray = readonly JsonValue[]
82
+
83
+ export type JsonObject = {
84
+ readonly [key: string]: JsonValue
85
+ }
86
+ ```
87
+
88
+ `JsonPrimitive` covers the scalar values allowed in JSON. `JsonValue` extends
89
+ that with nested objects and arrays, so it recursively describes any value that
90
+ survives a round trip through `JSON.stringify()`/`JSON.parse()`. `JsonObject`
91
+ and `JsonArray` name the two composite shapes so other declarations can refer
92
+ to them directly instead of repeating the union.
93
+
94
+ ```ts
95
+ import { type JsonValue } from './types/json.js'
96
+
97
+ function toLogPayload(value: JsonValue): string {
98
+ return JSON.stringify(value)
99
+ }
100
+ ```
101
+
102
+ Use `JsonValue` and `JsonObject` when a contract must guarantee its data is
103
+ plain and serializable, such as request payloads, stored metadata, or wire
104
+ formats. Prefer `Generic<T>` instead when the value type is not required to be
105
+ JSON-safe. `shared/application/http/json-api.ts` and
106
+ `shared/application/http/json-web-token.ts` build on these types to describe
107
+ JSON:API documents and JOSE/JWT structures.
108
+
67
109
  #### Next Step
68
110
 
69
111
  For the rest of the generated shared abstractions, continue with the pages in
@@ -1,94 +1,64 @@
1
1
  ### HTTP
2
2
 
3
- The HTTP contracts define a transport-facing boundary without coupling the
4
- generated structure to a specific framework.
3
+ The HTTP contracts define a framework-agnostic, transport-facing boundary.
5
4
  They are used when an adapter needs to describe requests, responses, handlers,
6
5
  or middleware in a consistent way.
7
6
 
8
- The generated template keeps `HttpRequest` and `HttpResponse` empty on purpose.
9
- Each project can extend them with the fields required by its own transport.
7
+ The generated template relies on the standard `Request` and `Response` types
8
+ from the Fetch API, so adapters work directly with the platform's own APIs.
10
9
 
11
- #### Response Body
10
+ The concern is split across four files under `shared/application/http/`:
12
11
 
13
- `HttpResponseBody` is responsible for standardizing the shape of the response
14
- payload.
12
+ 1. `http.ts` for the request/response boundary contracts and `HttpError`;
13
+ 2. `json-api.ts` for a type-only JSON:API v1.1 specification;
14
+ 3. `json-web-token.ts` for a type-only JOSE/JWT specification;
15
+ 4. `opengraph.ts` for a type-only Open Graph and social metadata specification.
15
16
 
16
- ```ts title="shared/application/http.ts"
17
- export interface HttpResponseBody {
18
- readonly data: Record<string, unknown> | null
19
- readonly errors: string[] | null
20
- readonly links: Record<string, URL> | null
21
- }
22
- ```
23
-
24
- This structure makes successful data, error messages, and related links
25
- explicit without forcing a specific router or server implementation.
17
+ Only `http.ts` contains runtime code. The other three files describe
18
+ compile-time structure for widely used formats so adapters do not have to
19
+ redefine them; they do not implement parsing, validation, or serialization.
26
20
 
27
21
  #### Request Handler
28
22
 
29
23
  `HttpRequestHandler` is responsible for processing a request and returning a
30
24
  response.
31
25
 
32
- ```ts title="shared/application/http.ts"
26
+ ```ts title="shared/application/http/http.ts"
33
27
  export interface HttpRequestHandler {
34
- handle(request: HttpRequest): HttpResponse | Promise<HttpResponse>
28
+ handle(request: Request): Response | Promise<Response>
35
29
  }
36
30
  ```
37
31
 
38
- In the following example we define adapter-specific request and response types,
39
- then implement a handler.
32
+ In the following example we implement a handler using the standard `Request`
33
+ and `Response` objects.
40
34
 
41
35
  ```ts title="users/adapters/get-user-handler.ts"
42
- import {
43
- HttpRequest,
44
- HttpRequestHandler,
45
- HttpResponse,
46
- HttpResponseBody,
47
- } from '../../shared/application/http.js'
48
-
49
- interface UserHttpRequest extends HttpRequest {
50
- readonly params: {
51
- id: string
52
- }
53
- }
54
-
55
- interface UserHttpResponse extends HttpResponse {
56
- readonly status: number
57
- readonly body: HttpResponseBody
58
- }
36
+ import { HttpRequestHandler } from '../../shared/application/http/http.js'
59
37
 
60
38
  export class GetUserHandler implements HttpRequestHandler {
61
- public handle(request: HttpRequest): HttpResponse {
62
- const typedRequest = request as UserHttpRequest
63
-
64
- return {
65
- status: 200,
66
- body: {
67
- data: {
68
- id: typedRequest.params.id,
69
- },
70
- errors: null,
71
- links: null,
72
- },
73
- } as UserHttpResponse
39
+ public handle(request: Request): Response {
40
+ const id = new URL(request.url).pathname.split('/').at(-1)
41
+
42
+ return Response.json({ data: { id } }, { status: 200 })
74
43
  }
75
44
  }
76
45
  ```
77
46
 
78
- The generated `HttpRequest` and `HttpResponse` interfaces stay empty, so the
79
- adapter declares the transport-specific fields locally. This keeps the shared
80
- contract small and portable.
47
+ Using the standard `Request` and `Response` types means the adapter relies
48
+ on the platform's own APIs, such as `request.url`, `request.headers`, and
49
+ `Response.json`. The shape of the JSON body itself, when the adapter follows
50
+ JSON:API, is described by the document types in `json-api.ts`.
81
51
 
82
52
  #### Middleware
83
53
 
84
54
  `HttpMiddleware` is responsible for running logic before or around the handler.
85
55
 
86
- ```ts title="shared/application/http.ts"
56
+ ```ts title="shared/application/http/http.ts"
87
57
  export interface HttpMiddleware {
88
58
  process(
89
- request: HttpRequest,
59
+ request: Request,
90
60
  handler: HttpRequestHandler,
91
- ): HttpResponse | Promise<HttpResponse>
61
+ ): Response | Promise<Response>
92
62
  }
93
63
  ```
94
64
 
@@ -97,29 +67,69 @@ Now that the handler exists, middleware can wrap it.
97
67
  ```ts title="users/adapters/request-logger.ts"
98
68
  import {
99
69
  HttpMiddleware,
100
- HttpRequest,
101
70
  HttpRequestHandler,
102
- HttpResponse,
103
- } from '../../shared/application/http.js'
71
+ } from '../../shared/application/http/http.js'
104
72
 
105
73
  export class RequestLoggerMiddleware implements HttpMiddleware {
106
74
  public async process(
107
- request: HttpRequest,
75
+ request: Request,
108
76
  handler: HttpRequestHandler,
109
- ): Promise<HttpResponse> {
77
+ ): Promise<Response> {
110
78
  void request
111
79
  return handler.handle(request)
112
80
  }
113
81
  }
114
82
  ```
115
83
 
116
- This middleware does not mutate the request or response. It only shows where
84
+ This middleware passes the request through unchanged, showing where
117
85
  cross-cutting behavior belongs in the generated HTTP abstraction.
118
86
 
119
- > **Warning**
120
- > Do not treat the shared HTTP contracts as a full framework abstraction. They
121
- > only define the minimum boundary for adapters. Routing, serialization, and
122
- > status code policies remain the responsibility of the concrete transport.
87
+ #### HTTP Errors
88
+
89
+ `HttpError` is responsible for carrying an HTTP status code alongside a
90
+ matching message.
91
+
92
+ ```ts title="shared/application/http/http.ts"
93
+ export class HttpError extends Error {
94
+ public static readonly messages: { [code: number]: string } = Object.freeze({
95
+ 400: 'Bad Request',
96
+ 404: 'Not Found',
97
+ 409: 'Conflict',
98
+ // ...remaining standard 4xx/5xx status codes
99
+ 500: 'Internal Server Error',
100
+ })
101
+
102
+ public readonly code: number
103
+
104
+ constructor(code: number, message?: string) {
105
+ super(message ?? HttpError.messages[code] ?? 'Unknown Error')
106
+ this.code = code
107
+ this.name = 'HttpError'
108
+ }
109
+ }
110
+ ```
111
+
112
+ `HttpError.messages` maps every standard 4xx/5xx status code to its reason
113
+ phrase. A caller can throw `new HttpError(404)` to get the standard message for
114
+ free, or pass an explicit `message` to override it. Codes outside the map fall
115
+ back to `'Unknown Error'`.
116
+
117
+ ```ts title="users/adapters/get-user-handler.ts"
118
+ import { HttpError } from '../../shared/application/http/http.js'
119
+
120
+ function assertFound<T>(value: T | null): T {
121
+ if (value === null) {
122
+ throw new HttpError(404)
123
+ }
124
+
125
+ return value
126
+ }
127
+ ```
128
+
129
+ > **Note**
130
+ > The shared HTTP contracts define the minimum boundary for adapters. Routing,
131
+ > serialization, and status code policies beyond `HttpError` are the
132
+ > responsibility of the concrete transport.
123
133
 
124
134
  #### Example Flow
125
135
 
@@ -132,3 +142,142 @@ flowchart LR
132
142
 
133
143
  This flow keeps the transport boundary explicit while leaving framework choices
134
144
  to the adapter layer.
145
+
146
+ #### JSON:API
147
+
148
+ `json-api.ts` declares a type-only implementation of the
149
+ [JSON:API v1.1](https://jsonapi.org/format/) specification, including the
150
+ [Atomic Operations extension](https://jsonapi.org/ext/atomic/). It gives an
151
+ adapter a shared vocabulary for request and response bodies without forcing a
152
+ particular server framework.
153
+
154
+ The main building blocks are:
155
+
156
+ - `JsonApiResourceObject` / `JsonApiResourceIdentifier` for resources and
157
+ resource linkage, generic over the resource `type`, `attributes`, and
158
+ `relationships`;
159
+ - `JsonApiRelationship`, `JsonApiToOneRelationship`, and
160
+ `JsonApiToManyRelationship` for relationship objects;
161
+ - `JsonApiError` for the top-level error object;
162
+ - `JsonApiDocument` (and its narrower aliases such as
163
+ `JsonApiSingleResourceDocument` and `JsonApiResourceCollectionDocument`) for
164
+ the top-level document, discriminated between a data document, an error
165
+ document, and a meta-only document;
166
+ - `JsonApiAtomicOperationsDocument` / `JsonApiAtomicResultsDocument` for the
167
+ Atomic Operations extension request and response bodies.
168
+
169
+ ```ts title="users/adapters/get-user-handler.ts"
170
+ import { HttpRequestHandler } from '../../shared/application/http/http.js'
171
+ import {
172
+ JsonApiSingleResourceDocument,
173
+ JsonApiResourceObject,
174
+ } from '../../shared/application/http/json-api.js'
175
+
176
+ type UserAttributes = { email: string }
177
+ type UserResource = JsonApiResourceObject<'users', UserAttributes>
178
+
179
+ export class GetUserHandler implements HttpRequestHandler {
180
+ public handle(request: Request): Response {
181
+ const id = new URL(request.url).pathname.split('/').at(-1) ?? ''
182
+
183
+ const body: JsonApiSingleResourceDocument<UserResource> = {
184
+ data: {
185
+ type: 'users',
186
+ id,
187
+ attributes: { email: 'ada@example.com' },
188
+ },
189
+ }
190
+
191
+ return Response.json(body, { status: 200 })
192
+ }
193
+ }
194
+ ```
195
+
196
+ > **Hint**
197
+ > These declarations only provide compile-time structure. Rules that depend on
198
+ > runtime values, URI validity, document-wide uniqueness, or member-name
199
+ > character validation still require explicit checks in the adapter.
200
+
201
+ #### JSON Web Tokens
202
+
203
+ `json-web-token.ts` declares a type-only implementation of the JOSE and JWT
204
+ family of RFCs (JWS, JWE, JWK, JWT, and related extensions such as DPoP and
205
+ selective disclosure). It lets an adapter describe tokens and keys precisely
206
+ without depending on a specific JOSE library's own types.
207
+
208
+ The main building blocks are:
209
+
210
+ - branded wire-format primitives such as `Base64Url`, `NumericDate`, and
211
+ `CompactJwt`;
212
+ - `JsonWebKey` / `JsonWebKeySet` for keys, covering EC, RSA, `oct`, OKP, and
213
+ ML-DSA (`AKP`) key types;
214
+ - `JwsHeader` / `JweHeader` for protected header parameters, and
215
+ `JwsJsonSerialization` / `JweJsonSerialization` for the JSON serializations;
216
+ - `JwtClaims` (built on `IanaRegisteredJwtClaims`) for decoded payloads, plus
217
+ ready-made profiles such as `OpenIdConnectIdTokenClaims`,
218
+ `OAuth2JwtAccessTokenClaims`, `DpopProofClaims`, and `SdJwtClaims`;
219
+ - service contracts an adapter can implement against a concrete JOSE
220
+ library: `JwtDecoder`, `JwsSigner`, `JwsVerifier`, `JweEncrypter`,
221
+ `JweDecrypter`, `JwkThumbprinter`, and `JwksResolver`, plus
222
+ `JwtValidationResult` for the outcome of validating a token against a
223
+ `JwtValidationPolicy`.
224
+
225
+ ```ts title="users/adapters/verify-access-token.ts"
226
+ import {
227
+ JwsVerifier,
228
+ OAuth2JwtAccessTokenClaims,
229
+ CompactJws,
230
+ JsonWebKey,
231
+ } from '../../shared/application/http/json-web-token.js'
232
+
233
+ export function verifyAccessToken(
234
+ verifier: JwsVerifier,
235
+ token: CompactJws,
236
+ key: JsonWebKey,
237
+ ) {
238
+ return verifier.verify<OAuth2JwtAccessTokenClaims>(token, key)
239
+ }
240
+ ```
241
+
242
+ > **Hint**
243
+ > This module has no runtime implementation. Pair it with a concrete JOSE
244
+ > library (for signing, encryption, or verification) and use these types to
245
+ > annotate its inputs and outputs.
246
+
247
+ #### Open Graph
248
+
249
+ `opengraph.ts` declares a type-only implementation of the
250
+ [Open Graph protocol](https://ogp.me/), the Twitter Card meta tags, and
251
+ Facebook's compatibility extensions. It is used when an adapter needs to build
252
+ or read the social-sharing metadata of a page.
253
+
254
+ The main building blocks are:
255
+
256
+ - `OpenGraphMetadata`, a union of every standard Open Graph object type
257
+ (`OpenGraphWebsite`, `OpenGraphArticle`, `OpenGraphBook`, `OpenGraphProfile`,
258
+ the `music.*` and `video.*` types, `OpenGraphPaymentLink`, and
259
+ `OpenGraphCustomObject` for CURIE-style custom types);
260
+ - `OpenGraphMetaTag` and `TwitterMetaTag`, the flat `property`/`content` and
261
+ `name`/`content` tag representations closer to the actual `<meta>` markup;
262
+ - `SocialMetadataDocument`, an aggregate of Open Graph, Twitter, Facebook, and
263
+ standard head metadata for a single page, and `RawSocialMetadataDocument`
264
+ for its rendered, tag-list form.
265
+
266
+ ```ts title="users/adapters/user-profile-metadata.ts"
267
+ import { OpenGraphProfile } from '../../shared/application/http/opengraph.js'
268
+
269
+ export function buildProfileMetadata(username: string): OpenGraphProfile {
270
+ return {
271
+ type: 'profile',
272
+ title: username,
273
+ url: `https://example.com/users/${username}`,
274
+ images: [{ url: `https://example.com/users/${username}/avatar.png` }],
275
+ username,
276
+ }
277
+ }
278
+ ```
279
+
280
+ > **Hint**
281
+ > This module has no runtime implementation, including no HTML rendering. Use
282
+ > `OpenGraphMetadata` to build the data and a separate template or renderer to
283
+ > emit the `<meta>` tags described by `OpenGraphMetaTag`/`TwitterMetaTag`.
@@ -28,22 +28,57 @@ any adapter to use a particular logger implementation.
28
28
  `Logger` is responsible for receiving log data from the application layer.
29
29
 
30
30
  ```ts title="shared/application/loggers.ts"
31
+ import { type TimeZone } from '../../types/iana.js'
32
+ import { type Locale } from '../../types/cldr.js'
33
+
31
34
  export abstract class Logger {
32
- public abstract debug(data: unknown): void
35
+ [property: string]: unknown
33
36
 
34
- public abstract info(data: unknown): void
37
+ public name: string = 'main'
35
38
 
36
- public abstract warning(data: unknown): void
39
+ public level: number = 0
37
40
 
38
- public abstract error(data: unknown): void
41
+ public datetimeLocales: Locale[] = ['en-GB']
39
42
 
40
- public abstract critical(data: unknown): void
41
- }
43
+ public datetimeFormatOptions: Intl.DateTimeFormatOptions & { timeZone: TimeZone } = {
44
+ timeZone: 'UTC',
45
+ year: 'numeric',
46
+ month: '2-digit',
47
+ day: '2-digit',
48
+ hour: '2-digit',
49
+ minute: '2-digit',
50
+ second: '2-digit',
51
+ fractionalSecondDigits: 3,
52
+ hourCycle: 'h23'
53
+ }
54
+
55
+ public abstract debug(data: unknown): void
56
+
57
+ public abstract info(data: unknown): void
58
+
59
+ public abstract warning(data: unknown): void
60
+
61
+ public abstract error(data: unknown): void
62
+
63
+ public abstract critical(data: unknown): void
64
+
65
+ protected getCurrentDatetime(): string {
66
+ return new Date().toLocaleString(this.datetimeLocales, this.datetimeFormatOptions)
67
+ }
68
+ } //:: class
42
69
  ```
43
70
 
44
71
  The contract is intentionally small. It defines the actions the application can
45
72
  request, while the adapter decides how those actions are persisted or displayed.
46
73
 
74
+ `name` and `level` identify the logger instance and its minimum severity, so an
75
+ adapter can decide which logs to emit or route. `datetimeLocales` and
76
+ `datetimeFormatOptions` control how `getCurrentDatetime()` formats the current
77
+ moment, using the `Locale` type from `types/cldr.d.ts` and the `TimeZone` type
78
+ from `types/iana.d.ts`. Adapters can use `getCurrentDatetime()` to timestamp
79
+ log entries consistently, regardless of the runtime environment's own locale
80
+ or timezone.
81
+
47
82
  #### First Adapter
48
83
 
49
84
  In the following example we implement a console-based logger.
@@ -51,43 +86,32 @@ In the following example we implement a console-based logger.
51
86
  ```ts title="users/adapters/console-logger.ts"
52
87
  import { Logger } from '../../shared/application/loggers.js'
53
88
 
54
- type ExternalService = {
55
- debug(data: unknown): void
56
- info(data: unknown): void
57
- warning(data: unknown): void
58
- error(data: unknown): void
59
- critical(data: unknown): void
60
- }
61
-
62
89
  export class ConsoleLogger extends Logger {
63
- constructor(protected readonly externalService: ExternalService) {
64
- super()
65
- }
66
-
67
90
  public debug(data: unknown): void {
68
- this.externalService.debug(data)
91
+ console.debug(this.getCurrentDatetime(), this.name, data)
69
92
  }
70
93
 
71
94
  public info(data: unknown): void {
72
- this.externalService.info(data)
95
+ console.info(this.getCurrentDatetime(), this.name, data)
73
96
  }
74
97
 
75
98
  public warning(data: unknown): void {
76
- this.externalService.warning(data)
99
+ console.warn(this.getCurrentDatetime(), this.name, data)
77
100
  }
78
101
 
79
102
  public error(data: unknown): void {
80
- this.externalService.error(data)
103
+ console.error(this.getCurrentDatetime(), this.name, data)
81
104
  }
82
105
 
83
106
  public critical(data: unknown): void {
84
- this.externalService.critical(data)
107
+ console.error(this.getCurrentDatetime(), this.name, data)
85
108
  }
86
109
  }
87
110
  ```
88
111
 
89
112
  This adapter satisfies the generated contract without changing the application
90
- layer.
113
+ layer. It reuses `getCurrentDatetime()` to prefix every entry with a
114
+ consistently formatted timestamp.
91
115
 
92
116
  #### Service Integration
93
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tshex-cli",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "author": "https://github.com/virtualitems/",
5
5
  "license": "MIT",
6
6
  "description": "Typescript Hexagonal Architecture CLI",