tshex-cli 1.0.29 → 1.0.31

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,7 +9,7 @@ import { DataManager } from './managers.js'
9
9
  export abstract class DriverAdapter<M extends DataManager = DataManager> {
10
10
  [property: string]: unknown
11
11
 
12
- public abstract connect(...args: unknown[]): Promise<M>
12
+ public abstract connect(...args: unknown[]): M
13
13
 
14
- public abstract disconnect(): Promise<unknown>
14
+ public abstract disconnect(): unknown
15
15
  } //:: class
@@ -12,13 +12,13 @@ export abstract class DataManager<T = Record<string, unknown>> {
12
12
  export abstract class DatasetManager<T = Record<string, unknown>> extends DataManager<T> {
13
13
  [property: string]: unknown
14
14
 
15
- public abstract union(other: Array<T>): Promise<Array<T>>
15
+ public abstract union(other: Array<T>): Array<T>
16
16
 
17
- public abstract intersection(other: Array<T>): Promise<Array<T>>
17
+ public abstract intersection(other: Array<T>): Array<T>
18
18
 
19
- public abstract difference(other: Array<T>): Promise<Array<T>>
19
+ public abstract difference(other: Array<T>): Array<T>
20
20
 
21
- public abstract symmetricDifference(other: Array<T>): Promise<Array<T>>
21
+ public abstract symmetricDifference(other: Array<T>): Array<T>
22
22
 
23
- public abstract complement(other: Array<T>): Promise<Array<T>>
23
+ public abstract complement(other: Array<T>): Array<T>
24
24
  } //:: class
@@ -1,5 +1,4 @@
1
1
  import { type DataManager } from './managers.js'
2
- import { type DriverAdapter } from './drivers.js'
3
2
 
4
3
  type Generic = Record<string, unknown>
5
4
 
@@ -7,12 +6,12 @@ type Generic = Record<string, unknown>
7
6
  * @description Acts as an intermediary between plain source data and domain objects.
8
7
  * It transforms records into domain representations and can translate them back when needed.
9
8
  */
10
- export abstract class Repository<RawDataShape = Generic, EntityShape = Generic> {
9
+ export abstract class Repository<RawDataShape = Generic, EntityShape = Generic, M extends DataManager<RawDataShape> = DataManager<RawDataShape>> {
11
10
  [property: string]: unknown
12
11
 
13
12
  public constructor(
14
- public readonly driver: DriverAdapter<DataManager<RawDataShape>>
13
+ public readonly manager: M
15
14
  ) {}
16
15
 
17
- protected abstract transform(data: RawDataShape): EntityShape
16
+ protected abstract transform(data: RawDataShape, ...args: unknown[]): EntityShape
18
17
  } //:: class
@@ -17,7 +17,7 @@ export abstract class Event {
17
17
  export abstract class EventHandler {
18
18
  [property: string]: unknown
19
19
 
20
- public abstract handle(event: Event): Promise<void>
20
+ public abstract handle(event: Event): void
21
21
  } //:: class
22
22
 
23
23
  /**
@@ -2,6 +2,8 @@
2
2
  * @description HTTP error with a specific status code and message.
3
3
  */
4
4
  export class HttpError extends Error {
5
+ [property: string]: unknown
6
+
5
7
  public static readonly messages: { [code: number]: string } = Object.freeze({
6
8
  400: 'Bad Request',
7
9
  401: 'Unauthorized',
@@ -1,102 +0,0 @@
1
- ### HTTP Handlers
2
-
3
- The handler contracts define a framework-agnostic, transport-facing boundary.
4
- They are used when an adapter needs to describe request processing and
5
- cross-cutting request logic in a consistent way, without depending on a
6
- specific server framework.
7
-
8
- The generated template relies on the standard `Request` and `Response` types
9
- from the Fetch API, so adapters work directly with the platform's own APIs.
10
-
11
- #### Request Handler
12
-
13
- `HttpRequestHandler` is responsible for processing a request and returning a
14
- response.
15
-
16
- ```ts title="shared/application/http/handlers.ts"
17
- export interface HttpRequestHandler {
18
- handle(request: Request): Response | Promise<Response>
19
- }
20
- ```
21
-
22
- In the following example we implement a handler using the standard `Request`
23
- and `Response` objects.
24
-
25
- ```ts title="users/adapters/get-user-handler.ts"
26
- import { HttpRequestHandler } from '../../shared/application/http/handlers.js'
27
-
28
- export class GetUserHandler implements HttpRequestHandler {
29
- public handle(request: Request): Response {
30
- const id = new URL(request.url).pathname.split('/').at(-1)
31
-
32
- return Response.json({ data: { id } }, { status: 200 })
33
- }
34
- }
35
- ```
36
-
37
- Using the standard `Request` and `Response` types means the adapter relies on
38
- the platform's own APIs, such as `request.url`, `request.headers`, and
39
- `Response.json`. The shape of the JSON body itself, when the adapter follows
40
- JSON:API, is described by the document types in
41
- `shared/application/http/json-api.md`.
42
-
43
- `handle()` can be synchronous or asynchronous; both `Response` and
44
- `Promise<Response>` are valid return types, so an adapter that awaits a
45
- database call satisfies the same contract as one that returns immediately.
46
-
47
- #### Middleware
48
-
49
- `HttpMiddleware` is responsible for running logic before or around the
50
- handler.
51
-
52
- ```ts title="shared/application/http/handlers.ts"
53
- export interface HttpMiddleware {
54
- process(
55
- request: Request,
56
- handler: HttpRequestHandler,
57
- ): Response | Promise<Response>
58
- }
59
- ```
60
-
61
- Now that the handler exists, middleware can wrap it.
62
-
63
- ```ts title="users/adapters/request-logger.ts"
64
- import {
65
- HttpMiddleware,
66
- HttpRequestHandler,
67
- } from '../../shared/application/http/handlers.js'
68
-
69
- export class RequestLoggerMiddleware implements HttpMiddleware {
70
- public async process(
71
- request: Request,
72
- handler: HttpRequestHandler,
73
- ): Promise<Response> {
74
- void request
75
- return handler.handle(request)
76
- }
77
- }
78
- ```
79
-
80
- This middleware passes the request through unchanged, showing where
81
- cross-cutting behavior belongs in the generated HTTP abstraction. A
82
- short-circuiting middleware, such as an authentication guard, follows the same
83
- shape but returns a `Response` (for example built from
84
- `shared/application/http/errors.md`'s `HttpError`) without calling
85
- `handler.handle()`.
86
-
87
- > **Note**
88
- > The handler contracts define the minimum boundary for adapters. Routing,
89
- > middleware chaining/composition, and status code policies beyond
90
- > `HttpError` are the responsibility of the concrete transport.
91
-
92
- #### Example Flow
93
-
94
- ```mermaid
95
- flowchart LR
96
- request[Request] --> middleware[Middleware]
97
- middleware --> handler[Handler]
98
- handler --> response["Response body"]
99
- ```
100
-
101
- This flow keeps the transport boundary explicit while leaving framework
102
- choices to the adapter layer.
@@ -1,235 +0,0 @@
1
- ### JSON:API
2
-
3
- `json-api.ts` declares a type-only implementation of the
4
- [JSON:API v1.1](https://jsonapi.org/format/) specification, including the
5
- [Atomic Operations extension](https://jsonapi.org/ext/atomic/). It gives an
6
- adapter a shared vocabulary for request and response bodies without forcing a
7
- particular server framework.
8
-
9
- This module has no runtime code. It only describes compile-time structure;
10
- rules that depend on runtime values, URI validity, document-wide uniqueness,
11
- full linkage, HTTP semantics, or member-name character validation still
12
- require explicit checks in the adapter.
13
-
14
- #### Resource Identifiers
15
-
16
- A resource is referenced in one of two ways, depending on whether the server
17
- has assigned it a permanent id yet.
18
-
19
- | Type | Option | Required members |
20
- | --- | --- | --- |
21
- | `JsonApiPersistedResourceIdentifier` | Server-assigned id | `type`, `id` |
22
- | `JsonApiLocalResourceIdentifier` | Client-assigned local id, not yet persisted | `type`, `lid` |
23
-
24
- `JsonApiResourceIdentifier` is the union of both.
25
-
26
- ```ts
27
- import type { JsonApiResourceIdentifier } from '../../../shared/application/http/json-api.js'
28
-
29
- const persisted: JsonApiResourceIdentifier<'users'> = { type: 'users', id: '1' }
30
- const local: JsonApiResourceIdentifier<'users'> = { type: 'users', lid: 'tmp-1' }
31
- ```
32
-
33
- Resource linkage (`JsonApiResourceLinkage`) is either a single identifier or
34
- `null` (`JsonApiToOneLinkage`), or an array of identifiers
35
- (`JsonApiToManyLinkage`).
36
-
37
- #### Resource Objects
38
-
39
- A resource object has the same persisted-versus-local split as its
40
- identifier.
41
-
42
- | Type | Option | `id` | `lid` |
43
- | --- | --- | --- | --- |
44
- | `JsonApiResourceObject` | Server-originated resource | required | optional |
45
- | `JsonApiNewResourceObject` | Client-originated resource without a server id | never | optional |
46
-
47
- `JsonApiCreateResourceObject` is the union accepted by create requests, since
48
- a client is allowed to submit a client-generated id.
49
-
50
- ```ts title="users/adapters/get-user-handler.ts"
51
- import { HttpRequestHandler } from '../../shared/application/http/handlers.js'
52
- import {
53
- JsonApiSingleResourceDocument,
54
- JsonApiResourceObject,
55
- } from '../../shared/application/http/json-api.js'
56
-
57
- type UserAttributes = { email: string }
58
- type UserResource = JsonApiResourceObject<'users', UserAttributes>
59
-
60
- export class GetUserHandler implements HttpRequestHandler {
61
- public handle(request: Request): Response {
62
- const id = new URL(request.url).pathname.split('/').at(-1) ?? ''
63
-
64
- const body: JsonApiSingleResourceDocument<UserResource> = {
65
- data: {
66
- type: 'users',
67
- id,
68
- attributes: { email: 'ada@example.com' },
69
- },
70
- }
71
-
72
- return Response.json(body, { status: 200 })
73
- }
74
- }
75
- ```
76
-
77
- #### Relationships
78
-
79
- `JsonApiRelationship` requires at least one of `links`, `data`, or `meta`, per
80
- the base specification. Two narrower aliases fix the shape of `data`:
81
-
82
- | Type | `data` shape |
83
- | --- | --- |
84
- | `JsonApiToOneRelationship` | single identifier or `null` |
85
- | `JsonApiToManyRelationship` | array of identifiers |
86
-
87
- ```ts
88
- import type {
89
- JsonApiToOneRelationship,
90
- JsonApiToManyRelationship,
91
- } from '../../../shared/application/http/json-api.js'
92
-
93
- const author: JsonApiToOneRelationship<{ type: 'authors' }> = {
94
- data: { type: 'authors', id: '9' },
95
- }
96
-
97
- const comments: JsonApiToManyRelationship<{ type: 'comments' }> = {
98
- data: [{ type: 'comments', id: '1' }, { type: 'comments', id: '2' }],
99
- }
100
- ```
101
-
102
- #### Documents
103
-
104
- `JsonApiDocument` is a discriminated union over which top-level member is
105
- required. Exactly one of these three shapes is valid at a time:
106
-
107
- | Type | Required top-level member | Use |
108
- | --- | --- | --- |
109
- | `JsonApiDataDocument` | `data` | success responses returning resources |
110
- | `JsonApiErrorDocument` | `errors` (non-empty) | failure responses |
111
- | `JsonApiMetaDocument` | `meta` | responses with no primary data, such as a heartbeat |
112
-
113
- `JsonApiDataDocument` itself is narrowed by four ready-made aliases depending
114
- on what `data` contains:
115
-
116
- | Alias | `data` shape |
117
- | --- | --- |
118
- | `JsonApiSingleResourceDocument` | resource or `null` |
119
- | `JsonApiResourceCollectionDocument` | array of resources |
120
- | `JsonApiSingleIdentifierDocument` | identifier or `null` |
121
- | `JsonApiIdentifierCollectionDocument` | array of identifiers |
122
- | `JsonApiRelationshipDocument` | any resource linkage |
123
-
124
- ```ts
125
- import type {
126
- JsonApiErrorDocument,
127
- JsonApiMetaDocument,
128
- } from '../../../shared/application/http/json-api.js'
129
-
130
- const notFound: JsonApiErrorDocument = {
131
- errors: [{ status: '404', title: 'Not Found' }],
132
- }
133
-
134
- const heartbeat: JsonApiMetaDocument<{ status: string }> = {
135
- meta: { status: 'ok' },
136
- }
137
- ```
138
-
139
- #### Request Documents
140
-
141
- Three request-only document shapes fix `data` to exactly what each operation
142
- accepts:
143
-
144
- | Type | `data` shape | Use |
145
- | --- | --- | --- |
146
- | `JsonApiCreateDocument` | `JsonApiCreateResourceObject` | `POST` requests |
147
- | `JsonApiUpdateDocument` | `JsonApiResourceObject` | `PATCH` requests |
148
- | `JsonApiRelationshipUpdateDocument` | resource linkage | relationship endpoints |
149
-
150
- `JsonApiQueryParameters` describes the standard `include`, `sort`, `filter`,
151
- and `page` query members, plus the `fields[TYPE]`, `filter[FIELD]`, and
152
- `page[FIELD]` bracketed member-name patterns.
153
-
154
- #### Errors
155
-
156
- `JsonApiError` requires at least one of `id`, `links`, `status`, `code`,
157
- `title`, `detail`, `source`, or `meta`.
158
-
159
- ```ts
160
- import type { JsonApiError } from '../../../shared/application/http/json-api.js'
161
-
162
- const validationError: JsonApiError = {
163
- status: '422',
164
- title: 'Invalid Attribute',
165
- detail: 'email must be a valid address',
166
- source: { pointer: '/data/attributes/email' },
167
- }
168
- ```
169
-
170
- #### Atomic Operations Extension
171
-
172
- The Atomic Operations extension adds a request document that carries one or
173
- more operations, and a response document that carries their results. Every
174
- operation targets either a resource or a relationship, and every operation
175
- uses exactly one of three op codes. `JsonApiAtomicStrictOperation` is the
176
- union of the seven concrete shapes that combination produces:
177
-
178
- | Type | `op` | Target | `data` |
179
- | --- | --- | --- | --- |
180
- | `JsonApiAtomicAddResourceOperation` | `'add'` | resource (`ref` optional, `href` optional) | resource to create |
181
- | `JsonApiAtomicUpdateResourceOperation` | `'update'` | resource | resource to replace |
182
- | `JsonApiAtomicRemoveResourceOperation` | `'remove'` | resource (required) | none |
183
- | `JsonApiAtomicUpdateToOneRelationshipOperation` | `'update'` | relationship (required) | identifier or `null` |
184
- | `JsonApiAtomicAddToManyRelationshipOperation` | `'add'` | relationship (required) | identifier array |
185
- | `JsonApiAtomicUpdateToManyRelationshipOperation` | `'update'` | relationship (required) | identifier array |
186
- | `JsonApiAtomicRemoveFromManyRelationshipOperation` | `'remove'` | relationship (required) | identifier array |
187
-
188
- A target is expressed through `ref` (a `JsonApiAtomicResourceRef` or
189
- `JsonApiAtomicRelationshipRef`) or through `href`, never both at once
190
- (`JsonApiExclusive`).
191
-
192
- ```ts title="users/adapters/atomic-operations-handler.ts"
193
- import {
194
- JsonApiAtomicOperationsDocument,
195
- JsonApiAtomicResultsDocument,
196
- JsonApiAtomicStrictOperation,
197
- } from '../../shared/application/http/json-api.js'
198
-
199
- const request: JsonApiAtomicOperationsDocument<JsonApiAtomicStrictOperation> = {
200
- 'atomic:operations': [
201
- { op: 'add', href: '/users', data: { type: 'users', attributes: { email: 'ada@example.com' } } },
202
- { op: 'update', ref: { type: 'users', id: '1' }, data: { type: 'users', id: '1', attributes: { email: 'ada@lovelace.dev' } } },
203
- { op: 'remove', ref: { type: 'users', id: '2' } },
204
- { op: 'update', ref: { type: 'users', id: '1', relationship: 'team' }, data: { type: 'teams', id: '9' } },
205
- { op: 'add', ref: { type: 'teams', id: '9', relationship: 'members' }, data: [{ type: 'users', id: '1' }] },
206
- ],
207
- }
208
-
209
- const response: JsonApiAtomicResultsDocument = {
210
- 'atomic:results': [{ data: { type: 'users', id: '1' } }, {}, {}, {}, {}],
211
- }
212
- ```
213
-
214
- `JsonApiAtomicRequestDocument` and `JsonApiAtomicResponseDocument` are the
215
- named aliases for the request and response side of the extension; the latter
216
- is a union of `JsonApiAtomicResultsDocument` and `JsonApiErrorDocument`, since
217
- a batch of operations can still fail as a whole.
218
-
219
- #### Extension And `@`-Members
220
-
221
- `JsonApiExtensionMembers` and `JsonApiAtMembers` compose extension-namespaced
222
- (`'ext:member'`) and meta (`'@member'`) properties through intersections, for
223
- custom or third-party JSON:API extensions beyond Atomic Operations.
224
-
225
- ```ts
226
- import type { JsonApiExtensionMembers, JsonApiResourceObject } from '../../../shared/application/http/json-api.js'
227
-
228
- type VersionedResource = JsonApiResourceObject<'users'> &
229
- JsonApiExtensionMembers<'version', { id: string }>
230
- ```
231
-
232
- > **Hint**
233
- > These declarations only provide compile-time structure. Rules that depend on
234
- > runtime values, URI validity, document-wide uniqueness, or member-name
235
- > character validation still require explicit checks in the adapter.
@@ -1,209 +0,0 @@
1
- ### JSON Web Tokens
2
-
3
- `json-web-token.ts` declares a type-only implementation of the JOSE and JWT
4
- family of RFCs (JWS, JWE, JWK, JWT, and related extensions such as DPoP and
5
- selective disclosure). It lets an adapter describe tokens and keys precisely
6
- without depending on a specific JOSE library's own types.
7
-
8
- This module has no runtime implementation. Pair it with a concrete JOSE
9
- library (for signing, encryption, or verification) and use these types to
10
- annotate its inputs and outputs.
11
-
12
- #### Branded Wire Values
13
-
14
- Wire-format primitives are branded so a plain `string` cannot be passed where
15
- an encoded value is expected: `Base64Url`, `Base64`, `NumericDate`,
16
- `StringOrURI`, `UriString`, `MediaType`, and `KeyId`. `BinaryData` is the
17
- union `ArrayBuffer | Uint8Array` used wherever a JOSE library accepts raw
18
- bytes.
19
-
20
- #### JSON Web Keys
21
-
22
- Every JWK shares `JwkCommonParameters` (`kty`, `use`, `key_ops`, `alg`, `kid`,
23
- the `x5*` certificate members, and the OpenID Federation `iat`/`nbf`/`exp`/
24
- `revoked` members). `JwkKeyType` (`kty`) then selects one of five key-family
25
- options, each with a public and a private variant where the family supports
26
- one:
27
-
28
- | `kty` | Curve/size parameter | Public type | Private type |
29
- | --- | --- | --- | --- |
30
- | `'EC'` | `EcCurve` (`P-256`, `P-384`, `P-521`, `secp256k1`) | `EcPublicJwk` (`x`, `y`) | `EcPrivateJwk` (adds `d`) |
31
- | `'RSA'` | — | `RsaPublicJwk` (`n`, `e`) | `RsaPrivateJwk` (adds `d`, `p`, `q`, `dp`, `dq`, `qi`, `oth`) |
32
- | `'oct'` | — | — (symmetric only) | `OctJwk` (`k`) |
33
- | `'OKP'` | `OkpCurve` (`Ed25519`, `Ed448`, `X25519`, `X448`) | `OkpPublicJwk` (`x`) | `OkpPrivateJwk` (adds `d`) |
34
- | `'AKP'` | `MldsaAlgorithm` (`ML-DSA-44`/`65`/`87`) | `AkpPublicJwk` (`pub`) | `AkpPrivateJwk` (adds `priv`) |
35
-
36
- `PublicJwk` and `PrivateJwk` are the unions across all five families;
37
- `JsonWebKey` is `PublicJwk | PrivateJwk`.
38
-
39
- ```ts
40
- import type { EcPublicJwk, RsaPrivateJwk, OctJwk } from '../../../shared/application/http/json-web-token.js'
41
-
42
- const ecKey: EcPublicJwk = { kty: 'EC', crv: 'P-256', x: '...' as Base64Url, y: '...' as Base64Url }
43
- const rsaKey: RsaPrivateJwk = { kty: 'RSA', n: '...' as Base64Url, e: '...' as Base64Url, d: '...' as Base64Url }
44
- const symmetricKey: OctJwk = { kty: 'oct', k: '...' as Base64Url }
45
- ```
46
-
47
- `JsonWebKeySet` wraps a `keys` array of mixed key types; `PublicJsonWebKeySet`
48
- narrows that array to public keys only, for a key set safe to publish. A
49
- thumbprint (`JwkThumbprint`) is a `Base64Url`; `JwkThumbprintUri` is the
50
- `urn:ietf:params:oauth:jwk-thumbprint:` URI form.
51
-
52
- #### Headers
53
-
54
- `JoseCommonHeaderParameters` covers the parameters shared by both header
55
- kinds (`jku`, `jwk`, `kid`, the `x5*` members, `typ`, `cty`, `crit`).
56
- `JwsHeaderParameters` and `JweHeaderParameters` extend it with the two
57
- distinct options:
58
-
59
- | Type | Required members | Extra members |
60
- | --- | --- | --- |
61
- | `JwsHeaderParameters` | `alg: JwsAlgorithm` | `b64`, `ppt`, `url`, `nonce`, `svt`, `jwt`, `client_id`, `trust_chain`, `peer_trust_chain` |
62
- | `JweHeaderParameters` | `alg: JweKeyManagementAlgorithm`, `enc: JweContentEncryptionAlgorithm` | `zip`, `epk`, `apu`, `apv`, `iv`, `tag`, `p2s`, `p2c`, replicated `iss`/`sub`/`aud`, `url`, `nonce` |
63
-
64
- `JwsHeader<CustomHeader>` and `JweHeader<CustomHeader>` intersect the
65
- respective parameters with a caller-supplied custom header shape.
66
-
67
- #### Serializations
68
-
69
- A JWS or a JWE can be represented four ways: as a single delimited string
70
- (compact), or as JSON with either one signer/recipient inlined (flattened) or
71
- several (general).
72
-
73
- | Type | Kind | Shape |
74
- | --- | --- | --- |
75
- | `CompactJws` | JWS, compact | `` `${string}.${string}.${string}` `` branded string |
76
- | `GeneralJwsJsonSerialization` | JWS, JSON, general | `payload` + `signatures[]` |
77
- | `FlattenedJwsJsonSerialization` | JWS, JSON, flattened | `payload` + one signature inlined |
78
- | `CompactJwe` | JWE, compact | `` `${string}.${string}.${string}.${string}.${string}` `` branded string |
79
- | `GeneralJweJsonSerialization` | JWE, JSON, general | shared members + `recipients[]` |
80
- | `FlattenedJweJsonSerialization` | JWE, JSON, flattened | shared members + one recipient inlined |
81
-
82
- `JwsJsonSerialization` and `JweJsonSerialization` are the general/flattened
83
- unions for each. `UnsecuredCompactJws` types the `alg: 'none'` compact form
84
- (`` `${string}.${string}.` `` — an empty signature segment).
85
-
86
- ```ts
87
- import type { CompactJws, FlattenedJwsJsonSerialization } from '../../../shared/application/http/json-web-token.js'
88
-
89
- const compact = 'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiIxIn0.c2ln' as CompactJws
90
-
91
- const flattened: FlattenedJwsJsonSerialization = {
92
- payload: 'eyJzdWIiOiIxIn0' as Base64Url,
93
- protected: 'eyJhbGciOiJFUzI1NiJ9' as Base64Url,
94
- signature: 'c2ln' as Base64Url,
95
- }
96
- ```
97
-
98
- `DecodedJws` and `DecodedJwe` describe a decoded token's structure;
99
- `DecodedJwt` is their union, discriminated by a `kind: 'JWS' | 'JWE'` tag.
100
- `CompactJwt` is `CompactJws | CompactJwe`, matching how RFC 7519 allows a JWT
101
- to use either serialization.
102
-
103
- #### JWT Claims
104
-
105
- `JwtRegisteredClaims` covers the seven RFC 7519 claims (`iss`, `sub`, `aud`,
106
- `exp`, `nbf`, `iat`, `jti`). `IanaRegisteredJwtClaims` extends it with every
107
- claim name currently registered by IANA (OpenID Connect profile claims, SIP,
108
- CDNI, GNAP, EAT, RATS, RFC 9901 selective-disclosure claims, OpenID
109
- Federation claims, and more), so `JwtClaims<CustomClaims>` covers arbitrary
110
- custom claims through intersection while still typing every standard one.
111
-
112
- Four ready-made profiles fix `JwtRegisteredClaims` members to `required`
113
- where the profile mandates them, as distinct implementation options for
114
- common token kinds:
115
-
116
- | Type | Profile | Required beyond the base seven |
117
- | --- | --- | --- |
118
- | `OpenIdConnectIdTokenClaims` | OIDC ID Token | `iss`, `sub`, `aud`, `exp`, `iat` |
119
- | `OAuth2JwtAccessTokenClaims` | RFC 9068 OAuth 2.0 access token | `iss`, `exp`, `aud`, `sub`, `client_id`, `iat`, `jti` |
120
- | `DpopProofClaims` | RFC 9449 DPoP proof | `jti`, `htm`, `htu`, `iat` |
121
- | `SdJwtClaims` | RFC 9901 SD-JWT | none beyond `IanaRegisteredJwtClaims`, adds `_sd`/`_sd_alg` |
122
-
123
- ```ts title="users/adapters/verify-access-token.ts"
124
- import {
125
- JwsVerifier,
126
- OAuth2JwtAccessTokenClaims,
127
- CompactJws,
128
- JsonWebKey,
129
- } from '../../shared/application/http/json-web-token.js'
130
-
131
- export function verifyAccessToken(
132
- verifier: JwsVerifier,
133
- token: CompactJws,
134
- key: JsonWebKey,
135
- ) {
136
- return verifier.verify<OAuth2JwtAccessTokenClaims>(token, key)
137
- }
138
- ```
139
-
140
- `SdJwtDisclosure`, `SdJwt`, and `SdJwtPresentation` brand the three distinct
141
- string forms used by selective disclosure: a single disclosure, an issued
142
- SD-JWT, and a presentation with disclosures appended.
143
-
144
- #### Service Contracts
145
-
146
- An adapter implements these interfaces against a concrete JOSE library. Each
147
- one exposes a distinct operation:
148
-
149
- | Contract | Method(s) | Responsibility |
150
- | --- | --- | --- |
151
- | `JwtDecoder` | `decode()` | Parse a compact token into a `DecodedJwt` without necessarily validating it. |
152
- | `JwsSigner` | `signCompact()`, `signFlattened()` | Produce a JWS in either serialization. |
153
- | `JwsVerifier` | `verify()` | Validate a JWS and return a `JwtValidationResult`. |
154
- | `JweEncrypter` | `encryptCompact()`, `encryptFlattened()` | Produce a JWE in either serialization. |
155
- | `JweDecrypter` | `decrypt()` | Decrypt a JWE back into `BinaryData`. |
156
- | `JwkThumbprinter` | `thumbprint()`, `thumbprintUri()` | Compute an RFC 7638 thumbprint, plain or as a URI. |
157
- | `JwksResolver` | `resolve()`, `select()` | Fetch a JWK Set and pick the key matching a header's `kid`/`x5t`/`x5t#S256`. |
158
-
159
- ```ts
160
- import type { JwkThumbprinter, JsonWebKey } from '../../../shared/application/http/json-web-token.js'
161
-
162
- function fingerprint(thumbprinter: JwkThumbprinter, key: JsonWebKey) {
163
- return thumbprinter.thumbprintUri(key)
164
- }
165
- ```
166
-
167
- #### Validating A Token
168
-
169
- `JwtValidationResult` is the outcome of checking a token against a
170
- `JwtValidationPolicy` (`issuers`, `audiences`, `subjects`, `algorithms`,
171
- `requiredClaims`, `clockToleranceSeconds`, `maxTokenAgeSeconds`, `typ`). It
172
- has exactly two implementation options, discriminated by `valid`:
173
-
174
- | Type | `valid` | Payload |
175
- | --- | --- | --- |
176
- | `JwtValidationSuccess` | `true` | `value: DecodedJws<Claims>` |
177
- | `JwtValidationFailure` | `false` | `code`, `message`, optional `cause` |
178
-
179
- `JwtValidationFailure['code']` enumerates the recognized rejection reasons:
180
- `'malformed'`, `'unsupported_serialization'`, `'unsupported_algorithm'`,
181
- `'invalid_signature'`, `'decryption_failed'`, `'expired'`, `'not_active'`,
182
- `'issued_in_future'`, `'issuer_mismatch'`, `'audience_mismatch'`,
183
- `'subject_mismatch'`, `'missing_claim'`, `'critical_header_unsupported'`, and
184
- `'policy_rejected'` (plus an open string for adapter-specific codes).
185
-
186
- ```ts
187
- import type { JwtValidationResult } from '../../../shared/application/http/json-web-token.js'
188
-
189
- function describeResult(result: JwtValidationResult): string {
190
- if (result.valid) {
191
- return `ok, subject=${result.value.claims.sub}`
192
- }
193
-
194
- return `rejected: ${result.code}`
195
- }
196
- ```
197
-
198
- #### Registry Metadata
199
-
200
- `JoseAlgorithmRegistryEntry`, `JoseHeaderParameterRegistryEntry`, and
201
- `JwtClaimRegistryEntry` type the shape of a row describing an algorithm, a
202
- header parameter, or a claim, for adapters that want to render or validate
203
- against IANA's own registry data instead of hardcoding the unions above.
204
-
205
- > **Hint**
206
- > This module has no runtime implementation. Choose a JOSE library, then use
207
- > `JwsSigner`/`JwsVerifier`/`JweEncrypter`/`JweDecrypter`/`JwtDecoder` to
208
- > annotate its inputs and outputs so the rest of the codebase stays independent
209
- > from that library's own types.