@outfoxx/sunday 1.0.9 → 1.1.0-alpha.11

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 (179) hide show
  1. package/LICENSE.txt +203 -0
  2. package/README.md +35 -2
  3. package/dist/any-type.d.ts +2 -0
  4. package/dist/any-type.js +13 -0
  5. package/dist/any-type.js.map +1 -1
  6. package/dist/class-type.d.ts +2 -2
  7. package/dist/class-type.js +13 -0
  8. package/dist/class-type.js.map +1 -1
  9. package/dist/date-time-types.d.ts +2 -7
  10. package/dist/date-time-types.js +15 -4
  11. package/dist/date-time-types.js.map +1 -1
  12. package/dist/event-parser.d.ts +17 -0
  13. package/dist/event-parser.js +153 -0
  14. package/dist/event-parser.js.map +1 -0
  15. package/dist/fetch-event-source.d.ts +12 -10
  16. package/dist/fetch-event-source.js +154 -138
  17. package/dist/fetch-event-source.js.map +1 -1
  18. package/dist/fetch-request-factory.d.ts +10 -5
  19. package/dist/fetch-request-factory.js +100 -71
  20. package/dist/fetch-request-factory.js.map +1 -1
  21. package/dist/fetch.d.ts +7 -1
  22. package/dist/fetch.js +74 -8
  23. package/dist/fetch.js.map +1 -1
  24. package/dist/header-parameters.d.ts +3 -0
  25. package/dist/header-parameters.js +53 -0
  26. package/dist/header-parameters.js.map +1 -0
  27. package/dist/index.d.ts +12 -12
  28. package/dist/index.js +25 -12
  29. package/dist/index.js.map +1 -1
  30. package/dist/logger.js +13 -0
  31. package/dist/logger.js.map +1 -1
  32. package/dist/{binary-decoder.d.ts → media-type-codecs/binary-decoder.d.ts} +2 -2
  33. package/dist/media-type-codecs/binary-decoder.js +29 -0
  34. package/dist/media-type-codecs/binary-decoder.js.map +1 -0
  35. package/dist/{binary-encoder.d.ts → media-type-codecs/binary-encoder.d.ts} +0 -0
  36. package/dist/media-type-codecs/binary-encoder.js +26 -0
  37. package/dist/media-type-codecs/binary-encoder.js.map +1 -0
  38. package/dist/media-type-codecs/cbor-decoder.d.ts +39 -0
  39. package/dist/media-type-codecs/cbor-decoder.js +426 -0
  40. package/dist/media-type-codecs/cbor-decoder.js.map +1 -0
  41. package/dist/media-type-codecs/cbor-encoder.d.ts +43 -0
  42. package/dist/media-type-codecs/cbor-encoder.js +239 -0
  43. package/dist/media-type-codecs/cbor-encoder.js.map +1 -0
  44. package/dist/media-type-codecs/cbor-tags.d.ts +4 -0
  45. package/dist/media-type-codecs/cbor-tags.js +18 -0
  46. package/dist/media-type-codecs/cbor-tags.js.map +1 -0
  47. package/dist/media-type-codecs/json-decoder.d.ts +39 -0
  48. package/dist/media-type-codecs/json-decoder.js +380 -0
  49. package/dist/media-type-codecs/json-decoder.js.map +1 -0
  50. package/dist/media-type-codecs/json-encoder.d.ts +44 -0
  51. package/dist/media-type-codecs/json-encoder.js +276 -0
  52. package/dist/media-type-codecs/json-encoder.js.map +1 -0
  53. package/dist/media-type-codecs/media-type-decoder.d.ts +11 -0
  54. package/dist/media-type-codecs/media-type-decoder.js +19 -0
  55. package/dist/media-type-codecs/media-type-decoder.js.map +1 -0
  56. package/dist/{media-type-decoders.d.ts → media-type-codecs/media-type-decoders.d.ts} +5 -4
  57. package/dist/media-type-codecs/media-type-decoders.js +53 -0
  58. package/dist/media-type-codecs/media-type-decoders.js.map +1 -0
  59. package/dist/{media-type-encoder.d.ts → media-type-codecs/media-type-encoder.d.ts} +5 -1
  60. package/dist/media-type-codecs/media-type-encoder.js +24 -0
  61. package/dist/media-type-codecs/media-type-encoder.js.map +1 -0
  62. package/dist/{media-type-encoders.d.ts → media-type-codecs/media-type-encoders.d.ts} +5 -4
  63. package/dist/media-type-codecs/media-type-encoders.js +55 -0
  64. package/dist/media-type-codecs/media-type-encoders.js.map +1 -0
  65. package/dist/{url-encoder.d.ts → media-type-codecs/www-form-url-encoder.d.ts} +8 -8
  66. package/dist/{url-encoder.js → media-type-codecs/www-form-url-encoder.js} +51 -37
  67. package/dist/media-type-codecs/www-form-url-encoder.js.map +1 -0
  68. package/dist/media-type.d.ts +91 -13
  69. package/dist/media-type.js +286 -15
  70. package/dist/media-type.js.map +1 -1
  71. package/dist/problem.d.ts +24 -9
  72. package/dist/problem.js +153 -6
  73. package/dist/problem.js.map +1 -1
  74. package/dist/request-factory.d.ts +10 -10
  75. package/dist/request-factory.js +13 -0
  76. package/dist/request-factory.js.map +1 -1
  77. package/dist/sunday-error.d.ts +11 -0
  78. package/dist/sunday-error.js +31 -0
  79. package/dist/sunday-error.js.map +1 -0
  80. package/dist/url-template.js +13 -0
  81. package/dist/url-template.js.map +1 -1
  82. package/dist/util/any.d.ts +2 -0
  83. package/dist/util/any.js +24 -0
  84. package/dist/util/any.js.map +1 -0
  85. package/dist/util/base64.js +13 -0
  86. package/dist/util/base64.js.map +1 -1
  87. package/dist/util/error.d.ts +2 -0
  88. package/dist/util/error.js +26 -0
  89. package/dist/util/error.js.map +1 -0
  90. package/dist/util/hex.js +16 -2
  91. package/dist/util/hex.js.map +1 -1
  92. package/dist/util/rxjs.d.ts +3 -0
  93. package/dist/util/rxjs.js +22 -5
  94. package/dist/util/rxjs.js.map +1 -1
  95. package/dist/util/stream-rxjs.js +13 -0
  96. package/dist/util/stream-rxjs.js.map +1 -1
  97. package/dist/util/temporal.d.ts +2 -0
  98. package/dist/util/temporal.js +31 -0
  99. package/dist/util/temporal.js.map +1 -0
  100. package/package.json +29 -22
  101. package/src/any-type.ts +18 -0
  102. package/src/class-type.ts +20 -2
  103. package/src/date-time-types.ts +35 -8
  104. package/src/event-parser.ts +204 -0
  105. package/src/fetch-event-source.ts +163 -159
  106. package/src/fetch-request-factory.ts +142 -100
  107. package/src/fetch.ts +79 -14
  108. package/src/header-parameters.ts +66 -0
  109. package/src/index.ts +26 -12
  110. package/src/logger.ts +14 -0
  111. package/src/media-type-codecs/binary-decoder.ts +38 -0
  112. package/src/media-type-codecs/binary-encoder.ts +33 -0
  113. package/src/media-type-codecs/cbor-decoder.ts +529 -0
  114. package/src/media-type-codecs/cbor-encoder.ts +321 -0
  115. package/src/media-type-codecs/cbor-tags.ts +18 -0
  116. package/src/media-type-codecs/json-decoder.ts +484 -0
  117. package/src/media-type-codecs/json-encoder.ts +342 -0
  118. package/src/media-type-codecs/media-type-decoder.ts +34 -0
  119. package/src/media-type-codecs/media-type-decoders.ts +81 -0
  120. package/src/media-type-codecs/media-type-encoder.ts +45 -0
  121. package/src/media-type-codecs/media-type-encoders.ts +83 -0
  122. package/src/media-type-codecs/www-form-url-encoder.ts +193 -0
  123. package/src/media-type.ts +340 -22
  124. package/src/problem.ts +158 -12
  125. package/src/request-factory.ts +35 -12
  126. package/src/sunday-error.ts +51 -0
  127. package/src/url-template.ts +14 -0
  128. package/src/util/any.ts +24 -0
  129. package/src/util/base64.ts +14 -0
  130. package/src/util/error.ts +28 -0
  131. package/src/util/hex.ts +17 -2
  132. package/src/util/rxjs.ts +30 -5
  133. package/src/util/stream-rxjs.ts +14 -0
  134. package/src/util/temporal.ts +32 -0
  135. package/dist/binary-decoder.js +0 -16
  136. package/dist/binary-decoder.js.map +0 -1
  137. package/dist/binary-encoder.js +0 -13
  138. package/dist/binary-encoder.js.map +0 -1
  139. package/dist/cbor-decoder.d.ts +0 -15
  140. package/dist/cbor-decoder.js +0 -126
  141. package/dist/cbor-decoder.js.map +0 -1
  142. package/dist/cbor-encoder.d.ts +0 -29
  143. package/dist/cbor-encoder.js +0 -81
  144. package/dist/cbor-encoder.js.map +0 -1
  145. package/dist/cbor-tags.d.ts +0 -3
  146. package/dist/cbor-tags.js +0 -4
  147. package/dist/cbor-tags.js.map +0 -1
  148. package/dist/http-error.d.ts +0 -10
  149. package/dist/http-error.js +0 -45
  150. package/dist/http-error.js.map +0 -1
  151. package/dist/json-decoder.d.ts +0 -31
  152. package/dist/json-decoder.js +0 -139
  153. package/dist/json-decoder.js.map +0 -1
  154. package/dist/json-encoder.d.ts +0 -35
  155. package/dist/json-encoder.js +0 -134
  156. package/dist/json-encoder.js.map +0 -1
  157. package/dist/media-type-decoder.d.ts +0 -4
  158. package/dist/media-type-decoder.js +0 -2
  159. package/dist/media-type-decoder.js.map +0 -1
  160. package/dist/media-type-decoders.js +0 -40
  161. package/dist/media-type-decoders.js.map +0 -1
  162. package/dist/media-type-encoder.js +0 -6
  163. package/dist/media-type-encoder.js.map +0 -1
  164. package/dist/media-type-encoders.js +0 -42
  165. package/dist/media-type-encoders.js.map +0 -1
  166. package/dist/url-encoder.js.map +0 -1
  167. package/src/binary-decoder.ts +0 -24
  168. package/src/binary-encoder.ts +0 -19
  169. package/src/cbor-decoder.ts +0 -148
  170. package/src/cbor-encoder.ts +0 -95
  171. package/src/cbor-tags.ts +0 -3
  172. package/src/http-error.ts +0 -55
  173. package/src/json-decoder.ts +0 -164
  174. package/src/json-encoder.ts +0 -162
  175. package/src/media-type-decoder.ts +0 -5
  176. package/src/media-type-decoders.ts +0 -59
  177. package/src/media-type-encoder.ts +0 -16
  178. package/src/media-type-encoders.ts +0 -61
  179. package/src/url-encoder.ts +0 -173
@@ -0,0 +1,193 @@
1
+ // Copyright 2020 Outfox, Inc.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import { Instant } from '@js-joda/core';
16
+ import { JsonStringifier } from '@outfoxx/jackson-js';
17
+ import { URLQueryParamsEncoder } from './media-type-encoder';
18
+
19
+ export class WWWFormUrlEncoder implements URLQueryParamsEncoder {
20
+ static get default(): WWWFormUrlEncoder {
21
+ return new WWWFormUrlEncoder(
22
+ WWWFormUrlEncoder.ArrayEncoding.UNBRACKETED,
23
+ WWWFormUrlEncoder.BoolEncoding.LITERAL,
24
+ WWWFormUrlEncoder.DateEncoding.DECIMAL_SECONDS_SINCE_EPOCH
25
+ );
26
+ }
27
+
28
+ constructor(
29
+ private arrayEncoding: WWWFormUrlEncoder.ArrayEncoding,
30
+ private boolEncoding: WWWFormUrlEncoder.BoolEncoding,
31
+ private dateEncoding: WWWFormUrlEncoder.DateEncoding,
32
+ private json = new JsonStringifier(),
33
+ private encoder = new TextEncoder()
34
+ ) {}
35
+
36
+ encode<T = unknown>(value: T): ArrayBuffer | SharedArrayBuffer {
37
+ const parameters = this.json.transform(value);
38
+
39
+ return this.encoder.encode(this.encodeQueryString(parameters));
40
+ }
41
+
42
+ encodeQueryString(parameters: Record<string, unknown>): string {
43
+ const components: string[] = [];
44
+
45
+ for (const key of Object.keys(parameters).sort()) {
46
+ components.push(...this.encodeQueryComponent(key, parameters[key]));
47
+ }
48
+ return components.join('&');
49
+ }
50
+
51
+ encodeQueryComponent(key: string, value: unknown): string[] {
52
+ const components: string[] = [];
53
+
54
+ if (value === undefined) {
55
+ return components;
56
+ }
57
+
58
+ if (value == null) {
59
+ //
60
+ components.push(encodeURIComponent(key));
61
+ } else if (value instanceof Array) {
62
+ // encode key according to `arrayEncoding`
63
+ for (const item of value) {
64
+ components.push(
65
+ ...this.encodeQueryComponent(
66
+ encodeArrayKey(key, this.arrayEncoding),
67
+ item
68
+ )
69
+ );
70
+ }
71
+ } else if (value instanceof Instant) {
72
+ //
73
+ components.push(
74
+ encodeURIComponent(key) +
75
+ '=' +
76
+ encodeURIComponent(encodeDate(value, this.dateEncoding))
77
+ );
78
+ } else if (typeof value === 'boolean') {
79
+ //
80
+ components.push(
81
+ encodeURIComponent(key) +
82
+ '=' +
83
+ encodeURIComponent(encodeBoolean(value, this.boolEncoding))
84
+ );
85
+ } else if (typeof value === 'object') {
86
+ //
87
+ const rec = (value ?? {}) as Record<string, unknown>;
88
+
89
+ for (const nestedKey of Object.keys(rec).sort()) {
90
+ components.push(
91
+ ...this.encodeQueryComponent(`${key}[${nestedKey}]`, rec[nestedKey])
92
+ );
93
+ }
94
+ } else {
95
+ //
96
+ components.push(
97
+ encodeURIComponent(key) + '=' + encodeURIComponent(`${value}`)
98
+ );
99
+ }
100
+
101
+ return components;
102
+ }
103
+ }
104
+
105
+ function encodeArrayKey(
106
+ key: string,
107
+ encoding: WWWFormUrlEncoder.ArrayEncoding
108
+ ): string {
109
+ return encoding === WWWFormUrlEncoder.ArrayEncoding.BRACKETED
110
+ ? `${key}[]`
111
+ : key;
112
+ }
113
+
114
+ function encodeBoolean(
115
+ value: boolean,
116
+ encoding: WWWFormUrlEncoder.BoolEncoding
117
+ ): string {
118
+ switch (encoding) {
119
+ case WWWFormUrlEncoder.BoolEncoding.NUMERIC:
120
+ return value ? '1' : '0';
121
+ case WWWFormUrlEncoder.BoolEncoding.LITERAL:
122
+ return value ? 'true' : 'false';
123
+ default:
124
+ throw new Error('unknown boolean encoding');
125
+ }
126
+ }
127
+
128
+ function encodeDate(
129
+ value: Date | Instant,
130
+ encoding: WWWFormUrlEncoder.DateEncoding
131
+ ): string {
132
+ value = value instanceof Date ? Instant.ofEpochMilli(value.getTime()) : value;
133
+ switch (encoding) {
134
+ case WWWFormUrlEncoder.DateEncoding.DECIMAL_SECONDS_SINCE_EPOCH:
135
+ return (value.epochSecond() + value.nano() / 1_000_000_000.0).toFixed(7);
136
+ case WWWFormUrlEncoder.DateEncoding.MILLISECONDS_SINCE_EPOCH:
137
+ return `${value.toEpochMilli()}`;
138
+ case WWWFormUrlEncoder.DateEncoding.ISO8601:
139
+ return value.toString();
140
+ default:
141
+ throw new Error('unknown date encoding');
142
+ }
143
+ }
144
+
145
+ export namespace WWWFormUrlEncoder {
146
+ /**
147
+ * Configures how `Array` parameters are encoded.
148
+ */
149
+ export enum ArrayEncoding {
150
+ /**
151
+ * An empty set of square brackets is appended to the key for every value. This is the default behavior.
152
+ */
153
+ BRACKETED,
154
+ /**
155
+ * No brackets are appended. The key is encoded as is.
156
+ */
157
+ UNBRACKETED,
158
+ }
159
+
160
+ /**
161
+ * Configures how `Bool` parameters are encoded.
162
+ */
163
+ export enum BoolEncoding {
164
+ /**
165
+ * Encode `true` as `1` and `false` as `0`. This is the default behavior.
166
+ */
167
+ NUMERIC,
168
+ /**
169
+ * Encode `true` and `false` as string literals.
170
+ */
171
+ LITERAL,
172
+ }
173
+
174
+ /**
175
+ * Configures how `Date` parameters are encoded.
176
+ */
177
+ export enum DateEncoding {
178
+ /**
179
+ * Encode the `Date` as a UNIX timestamp (decimal seconds since epoch).
180
+ */
181
+ DECIMAL_SECONDS_SINCE_EPOCH,
182
+
183
+ /**
184
+ * Encode the `Date` as UNIX millisecond timestamp (integer milliseconds since epoch).
185
+ */
186
+ MILLISECONDS_SINCE_EPOCH,
187
+
188
+ /**
189
+ * Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format). This is the default behavior.
190
+ */
191
+ ISO8601,
192
+ }
193
+ }
package/src/media-type.ts CHANGED
@@ -1,25 +1,343 @@
1
- export enum MediaType {
2
- TEXT = 'text/plain',
3
- HTML = 'text/html',
4
- JSON = 'application/json',
5
- YAML = 'application/yaml',
6
- CBOR = 'application/cbor',
7
- OCTET_STREAM = 'application/octet-stream',
8
- EVENT_STREAM = 'text/event-stream',
9
- X509_CA_CERT = 'application/x-x509-ca-cert',
10
- WWW_URL_FORM_ENCODED = 'application/x-www-form-urlencoded',
11
- PROBLEM_JSON = 'application/problem+json',
1
+ // Copyright 2020 Outfox, Inc.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ export class MediaType {
16
+ type: MediaType.Type;
17
+ tree: MediaType.Tree;
18
+ subtype: string;
19
+ suffix?: MediaType.Suffix;
20
+ parameters: Record<string, string>;
21
+
22
+ constructor(params: {
23
+ type: MediaType.Type;
24
+ tree?: MediaType.Tree;
25
+ subtype: string;
26
+ suffix?: MediaType.Suffix;
27
+ parameters?: Record<string, string>;
28
+ }) {
29
+ this.type = params.type;
30
+ this.tree = params.tree ?? MediaType.Tree.Standard;
31
+ this.subtype = params.subtype.toLowerCase();
32
+ this.suffix = params.suffix;
33
+ this.parameters = Object.entries(params.parameters ?? {}).reduce(
34
+ (obj, [name, value]) => {
35
+ obj[name.toLowerCase()] = value.toLowerCase();
36
+ return obj;
37
+ },
38
+ {} as Record<string, string>
39
+ );
40
+ }
41
+
42
+ parameter(name: MediaType.ParameterName | string): string | undefined {
43
+ return this.parameters[name];
44
+ }
45
+
46
+ with(params: {
47
+ type?: MediaType.Type;
48
+ tree?: MediaType.Tree;
49
+ subtype?: string;
50
+ suffix?: MediaType.Suffix;
51
+ parameters?: Record<string, string>;
52
+ }): MediaType {
53
+ return new MediaType({
54
+ type: params.type ?? this.type,
55
+ tree: params.tree ?? this.tree,
56
+ subtype: params.subtype ?? this.subtype,
57
+ suffix: params.suffix ?? this.suffix,
58
+ parameters: params.parameters ?? this.parameters,
59
+ });
60
+ }
61
+
62
+ withParameter(
63
+ parameter: MediaType.ParameterName | string,
64
+ value: string
65
+ ): MediaType {
66
+ const parameters = Object.assign({}, this.parameters, {
67
+ [parameter]: value,
68
+ });
69
+ return this.with({ parameters });
70
+ }
71
+
72
+ get value(): string {
73
+ const type = this.type;
74
+ const tree = this.tree;
75
+ const suffix = this.suffix ? `+${this.suffix.toLowerCase()}` : '';
76
+ const parameters = Object.keys(this.parameters)
77
+ .sort()
78
+ .map((key) => `;${key}=${this.parameters[key]}`)
79
+ .join('');
80
+ return `${type}/${tree}${this.subtype}${suffix}${parameters}`;
81
+ }
82
+
83
+ compatible(other: MediaType): boolean {
84
+ if (
85
+ this.type != MediaType.Type.Any &&
86
+ other.type != MediaType.Type.Any &&
87
+ this.type != other.type
88
+ ) {
89
+ return false;
90
+ }
91
+ if (
92
+ this.tree != MediaType.Tree.Any &&
93
+ other.tree != MediaType.Tree.Any &&
94
+ this.tree != other.tree
95
+ ) {
96
+ return false;
97
+ }
98
+ if (
99
+ this.subtype != '*' &&
100
+ other.subtype != '*' &&
101
+ this.subtype != other.subtype
102
+ ) {
103
+ return false;
104
+ }
105
+ if (this.suffix != other.suffix) {
106
+ return false;
107
+ }
108
+ const thisKeys = Object.keys(this.parameters);
109
+ const otherKeys = Object.keys(other.parameters);
110
+ return thisKeys
111
+ .filter((key) => otherKeys.includes(key))
112
+ .every((key) => this.parameters[key] == other.parameters[key]);
113
+ }
114
+
115
+ equals(other: MediaType): boolean {
116
+ if (this === other) return true;
117
+
118
+ if (this.type != other.type) return false;
119
+ if (this.tree != other.tree) return false;
120
+ if (this.suffix != other.suffix) return false;
121
+ if (this.subtype != other.subtype) return false;
122
+ for (const parameterName of Object.keys(this.parameters)) {
123
+ if (this.parameters[parameterName] != other.parameters[parameterName]) {
124
+ return false;
125
+ }
126
+ }
127
+ return true;
128
+ }
129
+
130
+ toString(): string {
131
+ return this.value;
132
+ }
12
133
  }
13
134
 
14
- export function mediaType(value: string | null | undefined): string | undefined;
15
- export function mediaType(
16
- value: string | null | undefined,
17
- def: string
18
- ): string;
19
-
20
- export function mediaType(
21
- value: string | null | undefined,
22
- def?: string
23
- ): string | undefined {
24
- return value?.split(';')[0].trim() ?? def;
135
+ export namespace MediaType {
136
+ export enum Type {
137
+ Application = 'application',
138
+ Audio = 'audio',
139
+ Example = 'example',
140
+ Font = 'font',
141
+ Image = 'image',
142
+ Message = 'message',
143
+ Model = 'model',
144
+ Multipart = 'multipart',
145
+ Text = 'text',
146
+ Video = 'video',
147
+ Any = '*',
148
+ }
149
+
150
+ export namespace Type {
151
+ export function from(value: string): Type | undefined {
152
+ return Object.values(Type).includes(value as Type)
153
+ ? (value as Type)
154
+ : undefined;
155
+ }
156
+ }
157
+
158
+ export enum Tree {
159
+ Standard = '',
160
+ Vendor = 'vnd.',
161
+ Personal = 'prs.',
162
+ Unregistered = 'x.',
163
+ Obsolete = 'x-',
164
+ Any = '*',
165
+ }
166
+
167
+ export namespace Tree {
168
+ export function from(value: string): Tree | undefined {
169
+ return Object.values(Tree).includes(value as Tree)
170
+ ? (value as Tree)
171
+ : undefined;
172
+ }
173
+ }
174
+
175
+ export enum Suffix {
176
+ XML = 'xml',
177
+ JSON = 'json',
178
+ BER = 'ber',
179
+ DER = 'der',
180
+ FastInfoSet = 'fastinfoset',
181
+ WBXML = 'wbxml',
182
+ Zip = 'zip',
183
+ CBOR = 'cbor',
184
+ }
185
+
186
+ export namespace Suffix {
187
+ export function from(value: string): Suffix | undefined {
188
+ return Object.values(Suffix).includes(value as Suffix)
189
+ ? (value as Suffix)
190
+ : undefined;
191
+ }
192
+ }
193
+
194
+ export enum ParameterName {
195
+ CharSet = 'charset',
196
+ }
197
+
198
+ export function from(value?: string | null): MediaType | undefined;
199
+ export function from(
200
+ value: string | null | undefined,
201
+ def: MediaType
202
+ ): MediaType;
203
+ export function from(
204
+ value?: string | null,
205
+ def: MediaType | undefined = undefined
206
+ ): MediaType | undefined {
207
+ if (!value) {
208
+ return def;
209
+ }
210
+
211
+ fullRegex.lastIndex = 0;
212
+ const match = fullRegex.exec(value);
213
+ if (match?.[0] != value) {
214
+ return def;
215
+ }
216
+
217
+ const type = MediaType.Type.from(match[1]?.toLowerCase());
218
+ if (!type) {
219
+ return def;
220
+ }
221
+
222
+ const tree =
223
+ MediaType.Tree.from(match[2]?.toLowerCase()) ?? MediaType.Tree.Standard;
224
+
225
+ const subtype = match[3]?.toLowerCase();
226
+ if (!subtype) {
227
+ return def;
228
+ }
229
+
230
+ const suffix = MediaType.Suffix.from(match[4]?.toLowerCase());
231
+
232
+ const parameters: Record<string, string> = {};
233
+
234
+ let parametersMatch = paramRegex.exec(match[5] ?? '');
235
+ while (parametersMatch?.[0]) {
236
+ const name = parametersMatch[1];
237
+ if (!name) continue;
238
+
239
+ const value = parametersMatch[2];
240
+ if (!value) continue;
241
+
242
+ parameters[name.toLowerCase()] = value.toLowerCase();
243
+
244
+ parametersMatch = paramRegex.exec(match[5]);
245
+ }
246
+
247
+ return new MediaType({
248
+ type,
249
+ tree,
250
+ subtype,
251
+ suffix,
252
+ parameters,
253
+ });
254
+ }
255
+
256
+ const fullRegex =
257
+ /^((?:[a-z]+|\*))\/(x(?:-|\\.)|(?:(?:vnd|prs|x)\.)|\*)?([a-z0-9\-.]+|\*)(?:\+([a-z]+))?( *(?:; *(?:(?:[\w.-]+) *= *(?:[\w.-]+)) *)*)$/gi;
258
+ const paramRegex = / *; *([\w.-]+) *= *([\w.-]+)/gi;
259
+
260
+ export const Plain = new MediaType({
261
+ type: MediaType.Type.Text,
262
+ subtype: 'plain',
263
+ });
264
+ export const HTML = new MediaType({
265
+ type: MediaType.Type.Text,
266
+ subtype: 'html',
267
+ });
268
+ export const JSON = new MediaType({
269
+ type: MediaType.Type.Application,
270
+ subtype: 'json',
271
+ });
272
+ export const YAML = new MediaType({
273
+ type: MediaType.Type.Application,
274
+ subtype: 'yaml',
275
+ });
276
+ export const CBOR = new MediaType({
277
+ type: MediaType.Type.Application,
278
+ subtype: 'cbor',
279
+ });
280
+ export const EventStream = new MediaType({
281
+ type: MediaType.Type.Text,
282
+ subtype: 'event-stream',
283
+ });
284
+ export const OctetStream = new MediaType({
285
+ type: MediaType.Type.Application,
286
+ subtype: 'octet-stream',
287
+ });
288
+ export const WWWFormUrlEncoded = new MediaType({
289
+ type: MediaType.Type.Application,
290
+ tree: MediaType.Tree.Obsolete,
291
+ subtype: 'www-form-urlencoded',
292
+ });
293
+ export const X509CACert = new MediaType({
294
+ type: MediaType.Type.Application,
295
+ tree: MediaType.Tree.Obsolete,
296
+ subtype: 'x509-ca-cert',
297
+ });
298
+ export const X509UserCert = new MediaType({
299
+ type: MediaType.Type.Application,
300
+ tree: MediaType.Tree.Obsolete,
301
+ subtype: 'x509-user-cert',
302
+ });
303
+
304
+ export const Any = new MediaType({
305
+ type: MediaType.Type.Any,
306
+ subtype: '*',
307
+ });
308
+ export const AnyText = new MediaType({
309
+ type: MediaType.Type.Text,
310
+ subtype: '*',
311
+ });
312
+ export const AnyImage = new MediaType({
313
+ type: MediaType.Type.Image,
314
+ subtype: '*',
315
+ });
316
+ export const AnyAudio = new MediaType({
317
+ type: MediaType.Type.Audio,
318
+ subtype: '*',
319
+ });
320
+ export const AnyVideo = new MediaType({
321
+ type: MediaType.Type.Video,
322
+ subtype: '*',
323
+ });
324
+
325
+ export const JSONStructured = new MediaType({
326
+ type: MediaType.Type.Any,
327
+ tree: MediaType.Tree.Any,
328
+ subtype: '*',
329
+ suffix: Suffix.JSON,
330
+ });
331
+ export const XMLStructured = new MediaType({
332
+ type: MediaType.Type.Any,
333
+ tree: MediaType.Tree.Any,
334
+ subtype: '*',
335
+ suffix: Suffix.XML,
336
+ });
337
+
338
+ export const ProblemJSON = new MediaType({
339
+ type: MediaType.Type.Application,
340
+ subtype: 'problem',
341
+ suffix: Suffix.JSON,
342
+ });
25
343
  }