tshex-cli 1.0.27 → 1.0.28

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.
@@ -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.27",
3
+ "version": "1.0.28",
4
4
  "author": "https://github.com/virtualitems/",
5
5
  "license": "MIT",
6
6
  "description": "Typescript Hexagonal Architecture CLI",
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.ts
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)
@@ -1,5 +1,5 @@
1
- import { type TimeZone } from '../../types/iana'
2
- import { type Locale } from '../../types/cldr'
1
+ import { type TimeZone } from '../../types/timezones'
2
+ import { type Locale } from '../../types/locales'
3
3
 
4
4
  export const DEBUG = 10
5
5
 
@@ -1,112 +0,0 @@
1
- ### Library Types
2
-
3
- The generated root type `Generic<T>` represents a plain object whose keys are
4
- strings 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
- #### Root Declaration
9
-
10
- The root declaration 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
- #### Basic Usage
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
- #### Default 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.
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
-
109
- #### Next Step
110
-
111
- For the rest of the generated shared abstractions, continue with the pages in
112
- `shared/application` and `shared/domain`.
@@ -1,283 +0,0 @@
1
- ### HTTP
2
-
3
- The HTTP contracts define a framework-agnostic, transport-facing boundary.
4
- They are used when an adapter needs to describe requests, responses, handlers,
5
- or middleware in a consistent way.
6
-
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.
9
-
10
- The concern is split across four files under `shared/application/http/`:
11
-
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.
16
-
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.
20
-
21
- #### Request Handler
22
-
23
- `HttpRequestHandler` is responsible for processing a request and returning a
24
- response.
25
-
26
- ```ts title="shared/application/http/http.ts"
27
- export interface HttpRequestHandler {
28
- handle(request: Request): Response | Promise<Response>
29
- }
30
- ```
31
-
32
- In the following example we implement a handler using the standard `Request`
33
- and `Response` objects.
34
-
35
- ```ts title="users/adapters/get-user-handler.ts"
36
- import { HttpRequestHandler } from '../../shared/application/http/http.js'
37
-
38
- export class GetUserHandler implements HttpRequestHandler {
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 })
43
- }
44
- }
45
- ```
46
-
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`.
51
-
52
- #### Middleware
53
-
54
- `HttpMiddleware` is responsible for running logic before or around the handler.
55
-
56
- ```ts title="shared/application/http/http.ts"
57
- export interface HttpMiddleware {
58
- process(
59
- request: Request,
60
- handler: HttpRequestHandler,
61
- ): Response | Promise<Response>
62
- }
63
- ```
64
-
65
- Now that the handler exists, middleware can wrap it.
66
-
67
- ```ts title="users/adapters/request-logger.ts"
68
- import {
69
- HttpMiddleware,
70
- HttpRequestHandler,
71
- } from '../../shared/application/http/http.js'
72
-
73
- export class RequestLoggerMiddleware implements HttpMiddleware {
74
- public async process(
75
- request: Request,
76
- handler: HttpRequestHandler,
77
- ): Promise<Response> {
78
- void request
79
- return handler.handle(request)
80
- }
81
- }
82
- ```
83
-
84
- This middleware passes the request through unchanged, showing where
85
- cross-cutting behavior belongs in the generated HTTP abstraction.
86
-
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.
133
-
134
- #### Example Flow
135
-
136
- ```mermaid
137
- flowchart LR
138
- request[Request] --> middleware[Middleware]
139
- middleware --> handler[Handler]
140
- handler --> response["Response body"]
141
- ```
142
-
143
- This flow keeps the transport boundary explicit while leaving framework choices
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`.
File without changes