tshex-cli 1.0.26 → 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.
- package/docs/generated-file-reference.md +13 -10
- package/docs/library-structure.md +4 -4
- 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 +1 -1
- package/readme.md +23 -4
- package/templates/lib/shared/application/http/{http.ts → errors.ts} +0 -14
- package/templates/lib/shared/application/http/handlers.ts +13 -0
- package/templates/lib/shared/application/loggers.ts +2 -2
- package/docs/library-types.md +0 -112
- package/docs/shared/application/http.md +0 -283
- /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,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
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)
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Http request handler to process incoming requests and generate responses.
|
|
3
|
-
*/
|
|
4
|
-
export interface HttpRequestHandler {
|
|
5
|
-
handle(request: Request): Response | Promise<Response>
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @description An HTTP middleware that pipes requests through handlers.
|
|
10
|
-
*/
|
|
11
|
-
export interface HttpMiddleware {
|
|
12
|
-
process(request: Request, handler: HttpRequestHandler): Response | Promise<Response>
|
|
13
|
-
}
|
|
14
|
-
|
|
15
1
|
/**
|
|
16
2
|
* @description HTTP error with a specific status code and message.
|
|
17
3
|
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Http request handler to process incoming requests and generate responses.
|
|
3
|
+
*/
|
|
4
|
+
export interface HttpRequestHandler {
|
|
5
|
+
handle(request: Request): Response | Promise<Response>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @description An HTTP middleware that pipes requests through handlers.
|
|
10
|
+
*/
|
|
11
|
+
export interface HttpMiddleware {
|
|
12
|
+
process(request: Request, handler: HttpRequestHandler): Response | Promise<Response>
|
|
13
|
+
}
|
package/docs/library-types.md
DELETED
|
@@ -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
|
|
File without changes
|