@outfoxx/sunday 1.0.8 → 1.1.0-alpha.10

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.
Files changed (151) hide show
  1. package/README.md +35 -2
  2. package/dist/any-type.d.ts +2 -0
  3. package/dist/class-type.d.ts +2 -2
  4. package/dist/date-time-types.d.ts +2 -7
  5. package/dist/date-time-types.js +2 -4
  6. package/dist/date-time-types.js.map +1 -1
  7. package/dist/event-parser.d.ts +17 -0
  8. package/dist/event-parser.js +140 -0
  9. package/dist/event-parser.js.map +1 -0
  10. package/dist/fetch-event-source.d.ts +12 -10
  11. package/dist/fetch-event-source.js +141 -138
  12. package/dist/fetch-event-source.js.map +1 -1
  13. package/dist/fetch-request-factory.d.ts +10 -5
  14. package/dist/fetch-request-factory.js +87 -71
  15. package/dist/fetch-request-factory.js.map +1 -1
  16. package/dist/fetch.d.ts +7 -1
  17. package/dist/fetch.js +61 -8
  18. package/dist/fetch.js.map +1 -1
  19. package/dist/header-parameters.d.ts +3 -0
  20. package/dist/header-parameters.js +40 -0
  21. package/dist/header-parameters.js.map +1 -0
  22. package/dist/index.d.ts +12 -12
  23. package/dist/index.js +12 -12
  24. package/dist/index.js.map +1 -1
  25. package/dist/{binary-decoder.d.ts → media-type-codecs/binary-decoder.d.ts} +2 -2
  26. package/dist/{binary-decoder.js → media-type-codecs/binary-decoder.js} +0 -0
  27. package/dist/media-type-codecs/binary-decoder.js.map +1 -0
  28. package/dist/{binary-encoder.d.ts → media-type-codecs/binary-encoder.d.ts} +0 -0
  29. package/dist/{binary-encoder.js → media-type-codecs/binary-encoder.js} +0 -0
  30. package/dist/media-type-codecs/binary-encoder.js.map +1 -0
  31. package/dist/media-type-codecs/cbor-decoder.d.ts +39 -0
  32. package/dist/media-type-codecs/cbor-decoder.js +413 -0
  33. package/dist/media-type-codecs/cbor-decoder.js.map +1 -0
  34. package/dist/media-type-codecs/cbor-encoder.d.ts +43 -0
  35. package/dist/media-type-codecs/cbor-encoder.js +226 -0
  36. package/dist/media-type-codecs/cbor-encoder.js.map +1 -0
  37. package/dist/media-type-codecs/cbor-tags.d.ts +4 -0
  38. package/dist/media-type-codecs/cbor-tags.js +5 -0
  39. package/dist/media-type-codecs/cbor-tags.js.map +1 -0
  40. package/dist/media-type-codecs/json-decoder.d.ts +39 -0
  41. package/dist/media-type-codecs/json-decoder.js +367 -0
  42. package/dist/media-type-codecs/json-decoder.js.map +1 -0
  43. package/dist/media-type-codecs/json-encoder.d.ts +44 -0
  44. package/dist/media-type-codecs/json-encoder.js +263 -0
  45. package/dist/media-type-codecs/json-encoder.js.map +1 -0
  46. package/dist/media-type-codecs/media-type-decoder.d.ts +11 -0
  47. package/dist/media-type-codecs/media-type-decoder.js +6 -0
  48. package/dist/media-type-codecs/media-type-decoder.js.map +1 -0
  49. package/dist/{media-type-decoders.d.ts → media-type-codecs/media-type-decoders.d.ts} +5 -4
  50. package/dist/{media-type-decoders.js → media-type-codecs/media-type-decoders.js} +6 -6
  51. package/dist/media-type-codecs/media-type-decoders.js.map +1 -0
  52. package/dist/{media-type-encoder.d.ts → media-type-codecs/media-type-encoder.d.ts} +5 -1
  53. package/dist/media-type-codecs/media-type-encoder.js +11 -0
  54. package/dist/media-type-codecs/media-type-encoder.js.map +1 -0
  55. package/dist/{media-type-encoders.d.ts → media-type-codecs/media-type-encoders.d.ts} +5 -4
  56. package/dist/{media-type-encoders.js → media-type-codecs/media-type-encoders.js} +8 -8
  57. package/dist/media-type-codecs/media-type-encoders.js.map +1 -0
  58. package/dist/{url-encoder.d.ts → media-type-codecs/www-form-url-encoder.d.ts} +8 -8
  59. package/dist/{url-encoder.js → media-type-codecs/www-form-url-encoder.js} +38 -37
  60. package/dist/media-type-codecs/www-form-url-encoder.js.map +1 -0
  61. package/dist/media-type.d.ts +91 -13
  62. package/dist/media-type.js +273 -15
  63. package/dist/media-type.js.map +1 -1
  64. package/dist/problem.d.ts +24 -9
  65. package/dist/problem.js +140 -6
  66. package/dist/problem.js.map +1 -1
  67. package/dist/request-factory.d.ts +10 -10
  68. package/dist/sunday-error.d.ts +11 -0
  69. package/dist/sunday-error.js +18 -0
  70. package/dist/sunday-error.js.map +1 -0
  71. package/dist/util/any.d.ts +2 -0
  72. package/dist/util/any.js +11 -0
  73. package/dist/util/any.js.map +1 -0
  74. package/dist/util/error.d.ts +2 -0
  75. package/dist/util/error.js +13 -0
  76. package/dist/util/error.js.map +1 -0
  77. package/dist/util/hex.js +3 -2
  78. package/dist/util/hex.js.map +1 -1
  79. package/dist/util/rxjs.d.ts +3 -0
  80. package/dist/util/rxjs.js +9 -5
  81. package/dist/util/rxjs.js.map +1 -1
  82. package/dist/util/temporal.d.ts +2 -0
  83. package/dist/util/temporal.js +18 -0
  84. package/dist/util/temporal.js.map +1 -0
  85. package/package.json +27 -21
  86. package/src/any-type.ts +4 -0
  87. package/src/class-type.ts +6 -2
  88. package/src/date-time-types.ts +22 -9
  89. package/src/event-parser.ts +190 -0
  90. package/src/fetch-event-source.ts +149 -159
  91. package/src/fetch-request-factory.ts +129 -101
  92. package/src/fetch.ts +65 -14
  93. package/src/header-parameters.ts +52 -0
  94. package/src/index.ts +12 -12
  95. package/src/{binary-decoder.ts → media-type-codecs/binary-decoder.ts} +3 -3
  96. package/src/{binary-encoder.ts → media-type-codecs/binary-encoder.ts} +0 -0
  97. package/src/media-type-codecs/cbor-decoder.ts +515 -0
  98. package/src/media-type-codecs/cbor-encoder.ts +307 -0
  99. package/src/media-type-codecs/cbor-tags.ts +4 -0
  100. package/src/media-type-codecs/json-decoder.ts +470 -0
  101. package/src/media-type-codecs/json-encoder.ts +328 -0
  102. package/src/media-type-codecs/media-type-decoder.ts +20 -0
  103. package/src/{media-type-decoders.ts → media-type-codecs/media-type-decoders.ts} +21 -13
  104. package/src/media-type-codecs/media-type-encoder.ts +31 -0
  105. package/src/{media-type-encoders.ts → media-type-codecs/media-type-encoders.ts} +23 -15
  106. package/src/{url-encoder.ts → media-type-codecs/www-form-url-encoder.ts} +53 -47
  107. package/src/media-type.ts +326 -22
  108. package/src/problem.ts +144 -12
  109. package/src/request-factory.ts +21 -12
  110. package/src/sunday-error.ts +37 -0
  111. package/src/util/any.ts +10 -0
  112. package/src/util/error.ts +14 -0
  113. package/src/util/hex.ts +3 -2
  114. package/src/util/rxjs.ts +16 -5
  115. package/src/util/temporal.ts +18 -0
  116. package/dist/binary-decoder.js.map +0 -1
  117. package/dist/binary-encoder.js.map +0 -1
  118. package/dist/cbor-decoder.d.ts +0 -15
  119. package/dist/cbor-decoder.js +0 -126
  120. package/dist/cbor-decoder.js.map +0 -1
  121. package/dist/cbor-encoder.d.ts +0 -29
  122. package/dist/cbor-encoder.js +0 -81
  123. package/dist/cbor-encoder.js.map +0 -1
  124. package/dist/cbor-tags.d.ts +0 -3
  125. package/dist/cbor-tags.js +0 -4
  126. package/dist/cbor-tags.js.map +0 -1
  127. package/dist/http-error.d.ts +0 -10
  128. package/dist/http-error.js +0 -45
  129. package/dist/http-error.js.map +0 -1
  130. package/dist/json-decoder.d.ts +0 -31
  131. package/dist/json-decoder.js +0 -139
  132. package/dist/json-decoder.js.map +0 -1
  133. package/dist/json-encoder.d.ts +0 -35
  134. package/dist/json-encoder.js +0 -116
  135. package/dist/json-encoder.js.map +0 -1
  136. package/dist/media-type-decoder.d.ts +0 -4
  137. package/dist/media-type-decoder.js +0 -2
  138. package/dist/media-type-decoder.js.map +0 -1
  139. package/dist/media-type-decoders.js.map +0 -1
  140. package/dist/media-type-encoder.js +0 -6
  141. package/dist/media-type-encoder.js.map +0 -1
  142. package/dist/media-type-encoders.js.map +0 -1
  143. package/dist/url-encoder.js.map +0 -1
  144. package/src/cbor-decoder.ts +0 -148
  145. package/src/cbor-encoder.ts +0 -95
  146. package/src/cbor-tags.ts +0 -3
  147. package/src/http-error.ts +0 -55
  148. package/src/json-decoder.ts +0 -164
  149. package/src/json-encoder.ts +0 -144
  150. package/src/media-type-decoder.ts +0 -5
  151. package/src/media-type-encoder.ts +0 -16
@@ -0,0 +1,328 @@
1
+ import {
2
+ ChronoField,
3
+ DateTimeFormatter,
4
+ DateTimeFormatterBuilder,
5
+ Instant,
6
+ LocalDate,
7
+ LocalDateTime,
8
+ LocalTime,
9
+ OffsetDateTime,
10
+ OffsetTime,
11
+ ResolverStyle,
12
+ ZonedDateTime,
13
+ } from '@js-joda/core';
14
+ import { JsonIncludeType, JsonStringifier } from '@outfoxx/jackson-js';
15
+ import { CustomMapper, Serializer } from '@outfoxx/jackson-js/dist/@types';
16
+ import 'reflect-metadata';
17
+ import { AnyType } from '../any-type';
18
+ import { Base64 } from '../util/base64';
19
+ import { encodeSeconds, secondsToNumber } from '../util/temporal';
20
+ import { StructuredMediaTypeEncoder } from './media-type-encoder';
21
+
22
+ export class JSONEncoder implements StructuredMediaTypeEncoder {
23
+ static get default(): JSONEncoder {
24
+ return new JSONEncoder(
25
+ JSONEncoder.DateEncoding.DECIMAL_SECONDS_SINCE_EPOCH
26
+ );
27
+ }
28
+
29
+ private readonly customSerializers: CustomMapper<Serializer>[];
30
+ private stringifier = new JsonStringifier();
31
+
32
+ constructor(readonly dateEncoding: JSONEncoder.DateEncoding) {
33
+ this.customSerializers = [
34
+ {
35
+ type: () => Date,
36
+ mapper: this.dateSerializer,
37
+ },
38
+ {
39
+ type: () => Instant,
40
+ mapper: this.instantSerializer,
41
+ },
42
+ {
43
+ type: () => ZonedDateTime,
44
+ mapper: this.zonedDateTimeSerializer,
45
+ },
46
+ {
47
+ type: () => OffsetDateTime,
48
+ mapper: this.offsetDateTimeSerializer,
49
+ },
50
+ {
51
+ type: () => OffsetTime,
52
+ mapper: this.offsetTimeSerializer,
53
+ },
54
+ {
55
+ type: () => LocalDateTime,
56
+ mapper: this.localDateTimeSerializer,
57
+ },
58
+ {
59
+ type: () => LocalDate,
60
+ mapper: this.localDateSerializer,
61
+ },
62
+ {
63
+ type: () => LocalTime,
64
+ mapper: this.localTimeSerializer,
65
+ },
66
+ {
67
+ type: () => URL,
68
+ mapper: this.urlSerializer,
69
+ },
70
+ {
71
+ type: () => ArrayBuffer,
72
+ mapper: this.arrayBufferSerializer,
73
+ },
74
+ ];
75
+ }
76
+
77
+ encode<T>(value: T, type?: AnyType, includeNulls = false): string {
78
+ // Use natural type when subtypes exist
79
+ if (
80
+ Reflect.hasMetadata(
81
+ 'jackson:defaultContextGroup:JsonSubTypes',
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ (value as any).constructor ?? {}
84
+ )
85
+ ) {
86
+ type = [Object];
87
+ }
88
+
89
+ return this.stringifier.stringify(value, {
90
+ serializers: this.customSerializers,
91
+ features: {
92
+ serialization: {
93
+ DEFAULT_PROPERTY_INCLUSION: {
94
+ value: includeNulls
95
+ ? JsonIncludeType.ALWAYS
96
+ : JsonIncludeType.NON_NULL,
97
+ },
98
+ },
99
+ },
100
+ mainCreator: () => type ?? [Object],
101
+ });
102
+ }
103
+
104
+ encodeObject<T>(
105
+ value: T,
106
+ type?: AnyType,
107
+ includeNulls = false
108
+ ): Record<string, unknown> {
109
+ // Use natural type when subtypes exist
110
+ if (
111
+ Reflect.hasMetadata(
112
+ 'jackson:defaultContextGroup:JsonSubTypes',
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ (value as any).constructor ?? {}
115
+ )
116
+ ) {
117
+ type = [Object];
118
+ }
119
+
120
+ return this.stringifier.transform(value, {
121
+ serializers: this.customSerializers,
122
+ features: {
123
+ serialization: {
124
+ DEFAULT_PROPERTY_INCLUSION: {
125
+ value: includeNulls
126
+ ? JsonIncludeType.ALWAYS
127
+ : JsonIncludeType.NON_NULL,
128
+ },
129
+ },
130
+ },
131
+ mainCreator: () => type ?? [Object],
132
+ });
133
+ }
134
+
135
+ private instantSerializer: Serializer = (_, value: Instant) => {
136
+ if (value == null) {
137
+ return null;
138
+ }
139
+
140
+ switch (this.dateEncoding) {
141
+ case JSONEncoder.DateEncoding.ISO8601:
142
+ return DateTimeFormatter.ISO_INSTANT.format(value);
143
+ case JSONEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH:
144
+ return value.toEpochMilli();
145
+ case JSONEncoder.DateEncoding.DECIMAL_SECONDS_SINCE_EPOCH:
146
+ return secondsToNumber(value.epochSecond(), value.nano());
147
+ }
148
+ };
149
+
150
+ private zonedDateTimeSerializer: Serializer = (_, value: ZonedDateTime) => {
151
+ if (value == null) {
152
+ return null;
153
+ }
154
+
155
+ switch (this.dateEncoding) {
156
+ case JSONEncoder.DateEncoding.ISO8601:
157
+ return DateTimeFormatter.ISO_ZONED_DATE_TIME.format(value);
158
+ case JSONEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH:
159
+ return value.toInstant().toEpochMilli();
160
+ case JSONEncoder.DateEncoding.DECIMAL_SECONDS_SINCE_EPOCH:
161
+ const instant = value.toInstant();
162
+ return secondsToNumber(instant.epochSecond(), instant.nano());
163
+ }
164
+ };
165
+
166
+ private offsetDateTimeSerializer: Serializer = (_, value: OffsetDateTime) => {
167
+ if (value == null) {
168
+ return null;
169
+ }
170
+
171
+ switch (this.dateEncoding) {
172
+ case JSONEncoder.DateEncoding.ISO8601:
173
+ return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value);
174
+ case JSONEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH:
175
+ return value.toInstant().toEpochMilli();
176
+ case JSONEncoder.DateEncoding.DECIMAL_SECONDS_SINCE_EPOCH:
177
+ const instant = value.toInstant();
178
+ return secondsToNumber(instant.epochSecond(), instant.nano());
179
+ }
180
+ };
181
+
182
+ private offsetTimeFormatter = new DateTimeFormatterBuilder()
183
+ .parseCaseInsensitive()
184
+ .append(DateTimeFormatter.ISO_LOCAL_TIME)
185
+ .appendOffsetId()
186
+ .toFormatter(ResolverStyle.STRICT);
187
+
188
+ private offsetTimeSerializer: Serializer = (_, value: OffsetTime) => {
189
+ if (value == null) {
190
+ return null;
191
+ }
192
+
193
+ switch (this.dateEncoding) {
194
+ case JSONEncoder.DateEncoding.ISO8601:
195
+ return this.offsetTimeFormatter.format(value);
196
+ default:
197
+ return [
198
+ value.hour(),
199
+ value.minute(),
200
+ ...encodeSeconds(
201
+ value.second(),
202
+ this.dateEncoding ==
203
+ JSONEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH
204
+ ? value.get(ChronoField.MILLI_OF_SECOND)
205
+ : value.nano()
206
+ ),
207
+ value.offset().toString(),
208
+ ];
209
+ }
210
+ };
211
+
212
+ private localDateTimeSerializer: Serializer = (_, value: LocalDateTime) => {
213
+ if (value == null) {
214
+ return null;
215
+ }
216
+
217
+ switch (this.dateEncoding) {
218
+ case JSONEncoder.DateEncoding.ISO8601:
219
+ return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value);
220
+ default:
221
+ return [
222
+ value.year(),
223
+ value.monthValue(),
224
+ value.dayOfMonth(),
225
+ value.hour(),
226
+ value.minute(),
227
+ ...encodeSeconds(
228
+ value.second(),
229
+ this.dateEncoding ==
230
+ JSONEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH
231
+ ? value.get(ChronoField.MILLI_OF_SECOND)
232
+ : value.nano()
233
+ ),
234
+ ];
235
+ }
236
+ };
237
+
238
+ private localDateSerializer: Serializer = (_, value: LocalDate) => {
239
+ if (value == null) {
240
+ return null;
241
+ }
242
+
243
+ switch (this.dateEncoding) {
244
+ case JSONEncoder.DateEncoding.ISO8601:
245
+ return DateTimeFormatter.ISO_LOCAL_DATE.format(value);
246
+ default:
247
+ return [value.year(), value.monthValue(), value.dayOfMonth()];
248
+ }
249
+ };
250
+
251
+ private localTimeSerializer: Serializer = (_, value: LocalTime) => {
252
+ if (value == null) {
253
+ return null;
254
+ }
255
+
256
+ switch (this.dateEncoding) {
257
+ case JSONEncoder.DateEncoding.ISO8601:
258
+ return DateTimeFormatter.ISO_LOCAL_TIME.format(value);
259
+ default:
260
+ return [
261
+ value.hour(),
262
+ value.minute(),
263
+ ...encodeSeconds(
264
+ value.second(),
265
+ this.dateEncoding ==
266
+ JSONEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH
267
+ ? value.get(ChronoField.MILLI_OF_SECOND)
268
+ : value.nano()
269
+ ),
270
+ ];
271
+ }
272
+ };
273
+
274
+ private dateSerializer: Serializer = (_, value: Date) => {
275
+ if (value == null) {
276
+ return null;
277
+ }
278
+
279
+ switch (this.dateEncoding) {
280
+ case JSONEncoder.DateEncoding.ISO8601:
281
+ return value.toISOString();
282
+ case JSONEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH:
283
+ return value.getTime();
284
+ case JSONEncoder.DateEncoding.DECIMAL_SECONDS_SINCE_EPOCH:
285
+ return value.getTime() / 1000.0;
286
+ }
287
+ };
288
+
289
+ private urlSerializer: Serializer = (_, value: URL) => {
290
+ if (value == null) {
291
+ return null;
292
+ }
293
+
294
+ return value.toString();
295
+ };
296
+
297
+ private arrayBufferSerializer: Serializer = (_, value: ArrayBuffer) => {
298
+ if (value == null) {
299
+ return null;
300
+ }
301
+
302
+ return Base64.encode(value);
303
+ };
304
+ }
305
+
306
+ export namespace JSONEncoder {
307
+ /**
308
+ * Configures how temporal values are encoded.
309
+ */
310
+ export enum DateEncoding {
311
+ /**
312
+ * Encode temporal values numerically using seconds with decimal
313
+ * sub-second precision.
314
+ */
315
+ DECIMAL_SECONDS_SINCE_EPOCH,
316
+
317
+ /**
318
+ * Encode temporal values numerically using integer milliseconds.
319
+ */
320
+ MILLISECONDS_SINCE_EPOCH,
321
+
322
+ /**
323
+ * Encode temporal values values as an ISO-8601-formatted string (in
324
+ * RFC 3339 format). This is the default behavior.
325
+ */
326
+ ISO8601,
327
+ }
328
+ }
@@ -0,0 +1,20 @@
1
+ import { AnyType } from '../any-type';
2
+
3
+ export interface MediaTypeDecoder {
4
+ decode<T>(response: Response, type: AnyType): Promise<T>;
5
+ }
6
+
7
+ export interface TextMediaTypeDecoder extends MediaTypeDecoder {
8
+ decodeText<T>(text: string, type: AnyType): T;
9
+ }
10
+
11
+ export interface StructuredMediaTypeDecoder extends MediaTypeDecoder {
12
+ decodeObject<T>(data: unknown, type: AnyType): T;
13
+ }
14
+
15
+ export function isStructuredMediaTypeDecoder(
16
+ decoder: MediaTypeDecoder | StructuredMediaTypeDecoder | undefined
17
+ ): decoder is StructuredMediaTypeDecoder {
18
+ const rec = decoder as unknown as Record<string, unknown>;
19
+ return !!rec.decodeObject ?? false;
20
+ }
@@ -1,13 +1,16 @@
1
1
  import { BinaryDecoder } from './binary-decoder';
2
2
  import { CBORDecoder } from './cbor-decoder';
3
3
  import { JSONDecoder } from './json-decoder';
4
- import { MediaType } from './media-type';
4
+ import { MediaType } from '../media-type';
5
5
  import { MediaTypeDecoder } from './media-type-decoder';
6
6
 
7
7
  export interface MediaTypeDecodersBuilder {
8
8
  addDefaults(): MediaTypeDecodersBuilder;
9
9
 
10
- add(mediaType: string, decoder: MediaTypeDecoder): MediaTypeDecodersBuilder;
10
+ add(
11
+ mediaType: MediaType,
12
+ decoder: MediaTypeDecoder
13
+ ): MediaTypeDecodersBuilder;
11
14
 
12
15
  build(): MediaTypeDecoders;
13
16
  }
@@ -17,11 +20,12 @@ export interface MediaTypeDecodersBuilderConstructor {
17
20
 
18
21
  export class MediaTypeDecoders {
19
22
  static Builder: MediaTypeDecodersBuilderConstructor = class Builder
20
- implements MediaTypeDecodersBuilder {
21
- decoders = new Map<string, MediaTypeDecoder>();
23
+ implements MediaTypeDecodersBuilder
24
+ {
25
+ decoders = new Map<MediaType, MediaTypeDecoder>();
22
26
 
23
27
  add(
24
- mediaType: string,
28
+ mediaType: MediaType,
25
29
  decoder: MediaTypeDecoder
26
30
  ): MediaTypeDecodersBuilder {
27
31
  this.decoders.set(mediaType, decoder);
@@ -30,7 +34,7 @@ export class MediaTypeDecoders {
30
34
 
31
35
  addDefaults(): MediaTypeDecodersBuilder {
32
36
  return this.add(MediaType.JSON, JSONDecoder.default)
33
- .add(MediaType.OCTET_STREAM, new BinaryDecoder())
37
+ .add(MediaType.OctetStream, new BinaryDecoder())
34
38
  .add(MediaType.CBOR, CBORDecoder.default);
35
39
  }
36
40
 
@@ -43,17 +47,21 @@ export class MediaTypeDecoders {
43
47
  .addDefaults()
44
48
  .build();
45
49
 
46
- constructor(private decoders: Map<string, MediaTypeDecoder>) {}
50
+ constructor(private decoders: Map<MediaType, MediaTypeDecoder>) {}
47
51
 
48
- supports(mediaType: string): boolean {
49
- return this.decoders.has(mediaType);
52
+ supports(mediaType: MediaType): boolean {
53
+ return Array.from(this.decoders.keys()).some((key) =>
54
+ key.compatible(mediaType)
55
+ );
50
56
  }
51
57
 
52
- find(mediaType: string): MediaTypeDecoder {
53
- const decoder = this.decoders.get(mediaType);
54
- if (!decoder) {
58
+ find(mediaType: MediaType): MediaTypeDecoder {
59
+ const found = Array.from(this.decoders.entries()).find(([type]) =>
60
+ type.compatible(mediaType)
61
+ );
62
+ if (!found) {
55
63
  throw Error(`Unsupported media type - ${mediaType}`);
56
64
  }
57
- return decoder;
65
+ return found[1];
58
66
  }
59
67
  }
@@ -0,0 +1,31 @@
1
+ import { AnyType } from '../any-type';
2
+
3
+ export interface MediaTypeEncoder {
4
+ encode<T = unknown>(value: T, type: AnyType | undefined): BodyInit;
5
+ }
6
+
7
+ export interface URLQueryParamsEncoder extends MediaTypeEncoder {
8
+ encodeQueryString(value: Record<string, unknown>): string;
9
+ }
10
+
11
+ export function isURLQueryParamsEncoder(
12
+ encoder: MediaTypeEncoder | URLQueryParamsEncoder | undefined
13
+ ): encoder is URLQueryParamsEncoder {
14
+ const rec = encoder as unknown as Record<string, unknown>;
15
+ return !!rec.encodeQueryString ?? false;
16
+ }
17
+
18
+ export interface StructuredMediaTypeEncoder extends MediaTypeEncoder {
19
+ encodeObject<T = unknown>(
20
+ value: T,
21
+ type?: AnyType,
22
+ includeNulls?: boolean
23
+ ): Record<string, unknown>;
24
+ }
25
+
26
+ export function isStructuredMediaTypeEncoder(
27
+ encoder: MediaTypeEncoder | StructuredMediaTypeEncoder | undefined
28
+ ): encoder is StructuredMediaTypeEncoder {
29
+ const rec = encoder as unknown as Record<string, unknown>;
30
+ return !!rec.encodeObject ?? false;
31
+ }
@@ -1,14 +1,17 @@
1
+ import { MediaType } from '../media-type';
1
2
  import { BinaryEncoder } from './binary-encoder';
2
3
  import { CBOREncoder } from './cbor-encoder';
3
4
  import { JSONEncoder } from './json-encoder';
4
- import { MediaType } from './media-type';
5
5
  import { MediaTypeEncoder } from './media-type-encoder';
6
- import { URLEncoder } from './url-encoder';
6
+ import { WWWFormUrlEncoder } from './www-form-url-encoder';
7
7
 
8
8
  export interface MediaTypeEncodersBuilder {
9
9
  addDefaults(): MediaTypeEncodersBuilder;
10
10
 
11
- add(mediaType: string, encoder: MediaTypeEncoder): MediaTypeEncodersBuilder;
11
+ add(
12
+ mediaType: MediaType,
13
+ encoder: MediaTypeEncoder
14
+ ): MediaTypeEncodersBuilder;
12
15
 
13
16
  build(): MediaTypeEncoders;
14
17
  }
@@ -18,11 +21,12 @@ export interface MediaTypeEncodersBuilderConstructor {
18
21
 
19
22
  export class MediaTypeEncoders {
20
23
  static Builder: MediaTypeEncodersBuilderConstructor = class Builder
21
- implements MediaTypeEncodersBuilder {
22
- encoders = new Map<string, MediaTypeEncoder>();
24
+ implements MediaTypeEncodersBuilder
25
+ {
26
+ encoders = new Map<MediaType, MediaTypeEncoder>();
23
27
 
24
28
  add(
25
- mediaType: string,
29
+ mediaType: MediaType,
26
30
  encoder: MediaTypeEncoder
27
31
  ): MediaTypeEncodersBuilder {
28
32
  this.encoders.set(mediaType, encoder);
@@ -31,8 +35,8 @@ export class MediaTypeEncoders {
31
35
 
32
36
  addDefaults(): MediaTypeEncodersBuilder {
33
37
  return this.add(MediaType.JSON, JSONEncoder.default)
34
- .add(MediaType.OCTET_STREAM, BinaryEncoder.default)
35
- .add(MediaType.WWW_URL_FORM_ENCODED, URLEncoder.default)
38
+ .add(MediaType.OctetStream, BinaryEncoder.default)
39
+ .add(MediaType.WWWFormUrlEncoded, WWWFormUrlEncoder.default)
36
40
  .add(MediaType.CBOR, CBOREncoder.default);
37
41
  }
38
42
 
@@ -45,17 +49,21 @@ export class MediaTypeEncoders {
45
49
  .addDefaults()
46
50
  .build();
47
51
 
48
- constructor(private encoders: Map<string, MediaTypeEncoder>) {}
52
+ constructor(private encoders: Map<MediaType, MediaTypeEncoder>) {}
49
53
 
50
- supports(mediaType: string): boolean {
51
- return this.encoders.has(mediaType);
54
+ supports(mediaType: MediaType): boolean {
55
+ return Array.from(this.encoders.keys()).some((key) =>
56
+ key.compatible(mediaType)
57
+ );
52
58
  }
53
59
 
54
- find(mediaType: string): MediaTypeEncoder {
55
- const encoder = this.encoders.get(mediaType);
56
- if (!encoder) {
60
+ find(mediaType: MediaType): MediaTypeEncoder {
61
+ const found = Array.from(this.encoders.entries()).find(([type]) =>
62
+ type.compatible(mediaType)
63
+ );
64
+ if (!found) {
57
65
  throw Error(`Unsupported media type - ${mediaType}`);
58
66
  }
59
- return encoder;
67
+ return found[1];
60
68
  }
61
69
  }