@boo-labs/server 0.0.0-stage → 1.0.0

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 (114) hide show
  1. package/README.md +229 -2
  2. package/index.mjs +23 -0
  3. package/package.json +71 -4
  4. package/source/application/Kernel/index.mjs +1137 -0
  5. package/source/application/Server/index.mjs +1201 -0
  6. package/source/application/configurations/Http2CleartextServerConfiguration/index.mjs +168 -0
  7. package/source/application/configurations/Http2SecureServerConfiguration/index.mjs +172 -0
  8. package/source/application/configurations/HttpServerConfiguration/index.mjs +169 -0
  9. package/source/application/configurations/HttpsServerConfiguration/index.mjs +172 -0
  10. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/CORSOptionsValue.mjs +158 -0
  11. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/index.mjs +87 -0
  12. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/CSRFOptionsValue.mjs +178 -0
  13. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/index.mjs +80 -0
  14. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/CompressOptionsValue.mjs +126 -0
  15. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/index.mjs +81 -0
  16. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/CookieParserOptionsValue.mjs +107 -0
  17. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/index.mjs +82 -0
  18. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/CookieSessionOptionsValue.mjs +182 -0
  19. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/index.mjs +86 -0
  20. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/ListenOptionsValue.mjs +95 -0
  21. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/index.mjs +47 -0
  22. package/source/application/configurations/ServerConfiguration/Options/MiddlewareOption.mjs +102 -0
  23. package/source/application/configurations/ServerConfiguration/Options/Options.mjs +105 -0
  24. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/RateLimitOptionsValue.mjs +164 -0
  25. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/index.mjs +84 -0
  26. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/RequestOptionsValue.mjs +108 -0
  27. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/index.mjs +47 -0
  28. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/ResponseOptionsValue.mjs +101 -0
  29. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/index.mjs +47 -0
  30. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/SecurityHeadersOptionsValue.mjs +272 -0
  31. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/index.mjs +85 -0
  32. package/source/application/configurations/ServerConfiguration/Options/ServerOption.mjs +50 -0
  33. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/StaticOptionsValue.mjs +164 -0
  34. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/index.mjs +89 -0
  35. package/source/application/configurations/ServerConfiguration/index.mjs +781 -0
  36. package/source/application/resolvers/TransportResolver/index.mjs +119 -0
  37. package/source/application/use-cases/CloseServer/index.mjs +85 -0
  38. package/source/application/use-cases/ListenServer/index.mjs +98 -0
  39. package/source/core/EventSystem/index.mjs +110 -0
  40. package/source/core/Exchange/ExchangeContext.mjs +291 -0
  41. package/source/core/Exchange/Telemetry.mjs +193 -0
  42. package/source/core/Exchange/index.mjs +604 -0
  43. package/source/core/ExchangeLayerBuilder/index.mjs +133 -0
  44. package/source/core/IPCMain/IPCMain.mjs +507 -0
  45. package/source/core/IPCMain/IPCProtocol.mjs +441 -0
  46. package/source/core/IPCMain/IPCServerChannel.mjs +331 -0
  47. package/source/core/IPCMain/WebSocketAdapter.mjs +189 -0
  48. package/source/core/Request/BodyParser/BodyParser.mjs +197 -0
  49. package/source/core/Request/BodyParser/JSONParser.mjs +70 -0
  50. package/source/core/Request/BodyParser/MultipartParser.mjs +612 -0
  51. package/source/core/Request/BodyParser/URLEncodedParser.mjs +71 -0
  52. package/source/core/Request/RequestHeaders.mjs +177 -0
  53. package/source/core/Request/index.mjs +805 -0
  54. package/source/core/Response/ResponseHeaders.mjs +402 -0
  55. package/source/core/Response/index.mjs +1370 -0
  56. package/source/core/middlewares/CORSMiddleware/index.mjs +289 -0
  57. package/source/core/middlewares/CSRFMiddleware/index.mjs +284 -0
  58. package/source/core/middlewares/CompressMiddleware/index.mjs +460 -0
  59. package/source/core/middlewares/CookieParserMiddleware/index.mjs +194 -0
  60. package/source/core/middlewares/CookieSessionMiddleware/index.mjs +460 -0
  61. package/source/core/middlewares/RateLimitMiddleware/index.mjs +245 -0
  62. package/source/core/middlewares/SecurityHeadersMiddleware/index.mjs +151 -0
  63. package/source/core/middlewares/StaticMiddleware/index.mjs +628 -0
  64. package/source/core/routing/ExchangeLayer/ExchangeMatcher.mjs +127 -0
  65. package/source/core/routing/ExchangeLayer/index.mjs +254 -0
  66. package/source/core/routing/Pipeline/index.mjs +157 -0
  67. package/source/core/routing/Router/Route.mjs +184 -0
  68. package/source/core/routing/Router/index.mjs +523 -0
  69. package/source/core/routing/RouterRegister/OptionsMiddleware.mjs +147 -0
  70. package/source/core/routing/RouterRegister/index.mjs +150 -0
  71. package/source/core/routing/RouterSystem/index.mjs +225 -0
  72. package/source/core/value-objects/Address/index.mjs +281 -0
  73. package/source/core/value-objects/Endpoint/index.mjs +293 -0
  74. package/source/index.mjs +62 -0
  75. package/source/infrastructure/adapters/Http2Adapter/index.mjs +144 -0
  76. package/source/infrastructure/adapters/Http2CleartextAdapter/index.mjs +147 -0
  77. package/source/infrastructure/adapters/HttpAdapter/index.mjs +124 -0
  78. package/source/infrastructure/adapters/HttpsAdapter/index.mjs +124 -0
  79. package/source/infrastructure/adapters/TransportAdapter/createWebSocketOpenRequest.mjs +843 -0
  80. package/source/infrastructure/adapters/TransportAdapter/index.mjs +555 -0
  81. package/source/shared/Identifier/index.mjs +671 -0
  82. package/source/shared/constants/symbols/brands/index.mjs +45 -0
  83. package/source/shared/domain/UUIDV7/index.mjs +346 -0
  84. package/source/shared/errors/BaseError/index.mjs +157 -0
  85. package/source/shared/errors/IPCError/index.mjs +97 -0
  86. package/source/shared/errors/MiddlewareError/index.mjs +106 -0
  87. package/source/shared/errors/PathnamePatternError/index.mjs +103 -0
  88. package/source/shared/errors/RequestError/index.mjs +116 -0
  89. package/source/shared/errors/ResponseError/index.mjs +140 -0
  90. package/source/shared/errors/RouterError/index.mjs +90 -0
  91. package/source/shared/errors/ServerConfigurationError/index.mjs +551 -0
  92. package/source/shared/errors/ServerError/index.mjs +127 -0
  93. package/source/shared/errors/WebSocketError/index.mjs +58 -0
  94. package/source/shared/outcome/ErrorSchema.mjs +361 -0
  95. package/source/shared/outcome/Result/index.mjs +239 -0
  96. package/source/shared/outcome/Validation/index.mjs +208 -0
  97. package/source/shared/validations/Middleware/index.mjs +160 -0
  98. package/source/shared/validations/PathnamePattern/index.mjs +256 -0
  99. package/source/shared/validations/Server/NotFoundHandler/index.mjs +94 -0
  100. package/source/shared/validations/Server/Router/index.mjs +68 -0
  101. package/source/shared/validations/Server/ServerConfiguration/index.mjs +68 -0
  102. package/source/shared/validations/Server/SystemErrorHandler/index.mjs +94 -0
  103. package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +310 -0
  104. package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +299 -0
  105. package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +204 -0
  106. package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +102 -0
  107. package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +332 -0
  108. package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +178 -0
  109. package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +229 -0
  110. package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +132 -0
  111. package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +122 -0
  112. package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +137 -0
  113. package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +248 -0
  114. package/types/index.d.ts +719 -0
@@ -0,0 +1,402 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the mutable response headers abstraction.
4
+ *
5
+ * @description `ResponseHeaders` owns the mutable header map of an outgoing
6
+ * HTTP response. It validates header names and values, normalizes names to
7
+ * lowercase, and enforces the `Content-Type` lock once the response body has
8
+ * been described.
9
+ *
10
+ * The class does not know about the response body. It only knows when its
11
+ * `Content-Type` becomes locked, which is signaled by `Response` through
12
+ * `lockContentType()`.
13
+ *
14
+ * @module ResponseHeaders
15
+ */
16
+
17
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
18
+ // » IMPORT LOCAL MODULES
19
+ import ResponseError from '#shared/errors/ResponseError';
20
+
21
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
22
+ /**
23
+ * Represents a supported response header value.
24
+ *
25
+ * @typedef {string|number|(string|number)[]} ResponseHeaderValue
26
+ */
27
+
28
+ /**
29
+ * Represents the collection of response headers as a plain object.
30
+ *
31
+ * Used by `toObject()` and by the native `writeHead` call. The internal
32
+ * representation of `ResponseHeaders` is a `Map`, not this type.
33
+ *
34
+ * @typedef {Record<string, ResponseHeaderValue>} ResponseHeadersData
35
+ */
36
+
37
+ /**
38
+ * Identifies the operation being performed on a response header.
39
+ *
40
+ * @typedef {'set'|'append'|'remove'} HeaderOperation
41
+ */
42
+
43
+ /**
44
+ * Represents an iterator over response header entries.
45
+ *
46
+ * @typedef {Iterator<[string, ResponseHeaderValue]>} ResponseHeadersIterator
47
+ */
48
+
49
+ // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
50
+ /**
51
+ * Validates HTTP field names according to the HTTP token syntax.
52
+ *
53
+ * @type {RegExp}
54
+ */
55
+ const HEADER_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
56
+
57
+ /**
58
+ * Validates HTTP field values supported by this response abstraction.
59
+ *
60
+ * @type {RegExp}
61
+ */
62
+ const HEADER_VALUE_PATTERN = /^[\t\x20-\x7E\x80-\xFF]*$/;
63
+
64
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
65
+ /**
66
+ * Mutable, validated collection of outgoing HTTP response headers.
67
+ *
68
+ * @classdesc Response header names are normalized to lowercase. Values may
69
+ * be strings, finite numbers, or arrays of strings and finite numbers.
70
+ *
71
+ * Once `lockContentType()` has been called, the `Content-Type` header can
72
+ * no longer be set, appended, or removed.
73
+ */
74
+ class ResponseHeaders {
75
+ /**
76
+ * Private snapshot of the normalized response headers.
77
+ *
78
+ * @type {Map<string, ResponseHeaderValue>}
79
+ */
80
+ #headers = new Map();
81
+
82
+ /**
83
+ * Whether the `Content-Type` header is locked.
84
+ *
85
+ * @type {boolean}
86
+ */
87
+ #contentTypeLocked = false;
88
+
89
+ /**
90
+ * Sets a response header, replacing any existing value.
91
+ *
92
+ * @param {string} name - The HTTP header name.
93
+ * @param {ResponseHeaderValue} value - The HTTP header value.
94
+ * @returns {void}
95
+ * @throws {ResponseError} If the header name or value is invalid.
96
+ * @throws {ResponseError} If `Content-Type` is already locked.
97
+ * @example
98
+ * ```js
99
+ * headers.set('Content-Type', 'application/json');
100
+ * headers.set('Content-Length', 42);
101
+ * ```
102
+ */
103
+ set(name, value) {
104
+ this.#assertHeader(name, value);
105
+ this.#assertContentTypeNotLocked(name, 'set');
106
+ this.#headers.set(name.toLowerCase(), value);
107
+ }
108
+
109
+ /**
110
+ * Sets multiple response headers.
111
+ *
112
+ * @param {ResponseHeadersData} headers - The response headers to set.
113
+ * @returns {void}
114
+ * @throws {ResponseError} If the headers value is not an object.
115
+ * @throws {ResponseError} If any header name or value is invalid.
116
+ * @throws {ResponseError} If `Content-Type` is already locked.
117
+ * @example
118
+ * ```js
119
+ * headers.setMany({
120
+ * 'Content-Type': 'application/json',
121
+ * 'Cache-Control': 'no-cache',
122
+ * 'Content-Length': 42,
123
+ * });
124
+ * ```
125
+ */
126
+ setMany(headers) {
127
+ if (headers === null || typeof headers !== 'object' || Array.isArray(headers)) {
128
+ throw new ResponseError('INVALID_HEADERS');
129
+ }
130
+
131
+ for (const [name, value] of Object.entries(headers)) {
132
+ this.set(name, value);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Appends a value to an existing header or creates the header when it
138
+ * does not exist.
139
+ *
140
+ * Existing scalar values are converted to an array when another value is
141
+ * appended.
142
+ *
143
+ * @param {string} name - The HTTP header name.
144
+ * @param {string|number} value - The value to append.
145
+ * @returns {void}
146
+ * @throws {ResponseError} If the header name or value is invalid.
147
+ * @throws {ResponseError} If `Content-Type` is already locked.
148
+ * @example
149
+ * ```js
150
+ * headers.append('Set-Cookie', 'session=abc');
151
+ * headers.append('Set-Cookie', 'theme=dark');
152
+ *
153
+ * headers.get('Set-Cookie');
154
+ * // ['session=abc', 'theme=dark']
155
+ * ```
156
+ */
157
+ append(name, value) {
158
+ this.#assertHeader(name, value);
159
+ this.#assertContentTypeNotLocked(name, 'append');
160
+
161
+ const key = name.toLowerCase();
162
+ const current = this.#headers.get(key);
163
+
164
+ if (current === undefined) {
165
+ this.#headers.set(key, value);
166
+ } else if (Array.isArray(current)) {
167
+ current.push(value);
168
+ } else {
169
+ this.#headers.set(key, [current, value]);
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Returns the value of a response header.
175
+ *
176
+ * When the header value is an array, a defensive copy is returned.
177
+ *
178
+ * @param {string} name - The HTTP header name.
179
+ * @returns {ResponseHeaderValue|undefined} The header value, or `undefined` when the header does not exist.
180
+ * @example
181
+ * ```js
182
+ * const contentType = headers.get('Content-Type');
183
+ * ```
184
+ */
185
+ get(name) {
186
+ const value = this.#headers.get(name.toLowerCase());
187
+ return Array.isArray(value) ? [...value] : value;
188
+ }
189
+
190
+ /**
191
+ * Checks whether a response header exists.
192
+ *
193
+ * @param {string} name - The HTTP header name.
194
+ * @returns {boolean} `true` when the header exists; otherwise, `false`.
195
+ * @example
196
+ * ```js
197
+ * if (headers.has('Content-Type')) {
198
+ * console.log('Content-Type is set');
199
+ * }
200
+ * ```
201
+ */
202
+ has(name) {
203
+ return this.#headers.has(name.toLowerCase());
204
+ }
205
+
206
+ /**
207
+ * Removes a response header.
208
+ *
209
+ * Removing a header that does not exist has no effect.
210
+ *
211
+ * @param {string} name - The HTTP header name.
212
+ * @returns {void}
213
+ * @throws {ResponseError} If `Content-Type` is already locked.
214
+ * @example
215
+ * ```js
216
+ * headers.remove('Cache-Control');
217
+ * ```
218
+ */
219
+ remove(name) {
220
+ this.#assertContentTypeNotLocked(name, 'remove');
221
+ this.#headers.delete(name.toLowerCase());
222
+ }
223
+
224
+ /**
225
+ * Sets a default `Content-Type` header when one has not already been set.
226
+ *
227
+ * @param {string} value - The default content type.
228
+ * @returns {void}
229
+ * @throws {ResponseError} If the value is not a string.
230
+ * @throws {ResponseError} If `Content-Type` is already locked.
231
+ * @example
232
+ * ```js
233
+ * headers.ensureContentType('application/json');
234
+ * ```
235
+ */
236
+ ensureContentType(value) {
237
+ if (typeof value !== 'string') {
238
+ throw new ResponseError('INVALID_CONTENT_TYPE', {
239
+ values: {
240
+ expected: 'string',
241
+ received: value === null ? 'null' : typeof value,
242
+ },
243
+ });
244
+ }
245
+
246
+ if (!this.#headers.has('content-type')) {
247
+ this.set('content-type', value);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Locks the `Content-Type` header.
253
+ *
254
+ * After this method is called, the `Content-Type` header can no longer be
255
+ * set, appended, or removed.
256
+ *
257
+ * @returns {void}
258
+ * @example
259
+ * ```js
260
+ * headers.lockContentType();
261
+ * ```
262
+ */
263
+ lockContentType() {
264
+ this.#contentTypeLocked = true;
265
+ }
266
+
267
+ /**
268
+ * Returns the names of all response headers.
269
+ *
270
+ * @returns {string[]} The normalized response header names.
271
+ * @example
272
+ * ```js
273
+ * headers.set('Content-Type', 'application/json');
274
+ * headers.set('Cache-Control', 'no-cache');
275
+ *
276
+ * const names = headers.getNames();
277
+ * // ['content-type', 'cache-control']
278
+ * ```
279
+ */
280
+ getNames() {
281
+ return [...this.#headers.keys()];
282
+ }
283
+
284
+ /**
285
+ * Returns an iterator over the response headers as `[name, value]` pairs.
286
+ *
287
+ * Array values are copied so the caller cannot mutate the internal snapshot
288
+ * through the iterator.
289
+ *
290
+ * @yields {[string, ResponseHeaderValue]} The response header entry.
291
+ * @returns {ResponseHeadersIterator} The response header entries.
292
+ * @example
293
+ * ```js
294
+ * for (const [name, value] of headers.entries()) {
295
+ * console.log(name, value);
296
+ * }
297
+ * ```
298
+ */
299
+ *entries() {
300
+ for (const [name, value] of this.#headers) {
301
+ yield [name, Array.isArray(value) ? [...value] : value];
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Returns an iterator over the response headers as `[name, value]` pairs.
307
+ *
308
+ * Makes `ResponseHeaders` directly iterable with `for...of`, spread, and
309
+ * `Object.fromEntries`.
310
+ *
311
+ * @returns {ResponseHeadersIterator} The response header entries iterator.
312
+ * @example
313
+ * ```js
314
+ * for (const [name, value] of headers) {
315
+ * console.log(name, value);
316
+ * }
317
+ * ```
318
+ */
319
+ [Symbol.iterator]() {
320
+ return this.entries();
321
+ }
322
+
323
+ /**
324
+ * Returns a plain object representation of the response headers.
325
+ *
326
+ * The returned object is a shallow copy. Array values are copied so the
327
+ * caller cannot mutate the internal state through the result.
328
+ *
329
+ * @returns {ResponseHeadersData} A plain object representation of the headers.
330
+ * @example
331
+ * ```js
332
+ * const headersObject = headers.toObject();
333
+ * ```
334
+ */
335
+ toObject() {
336
+ /** @type {ResponseHeadersData} */
337
+ const result = Object.create(null);
338
+
339
+ for (const [name, value] of this.#headers) {
340
+ result[name] = Array.isArray(value) ? [...value] : value;
341
+ }
342
+
343
+ return result;
344
+ }
345
+
346
+ /**
347
+ * Validates a response header name and value.
348
+ *
349
+ * @param {string} name - The HTTP header name.
350
+ * @param {ResponseHeaderValue} value - The HTTP header value.
351
+ * @throws {ResponseError} If the header name or value is invalid.
352
+ */
353
+ #assertHeader(name, value) {
354
+ if (typeof name !== 'string' || !HEADER_NAME_PATTERN.test(name)) {
355
+ throw new ResponseError('INVALID_HEADER_NAME');
356
+ }
357
+
358
+ const items = Array.isArray(value) ? value : [value];
359
+
360
+ for (const item of items) {
361
+ this.#assertHeaderValue(item);
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Validates a response header value.
367
+ *
368
+ * @param {string|number} value - The header value to validate.
369
+ * @throws {ResponseError} If the value is not a valid header value.
370
+ */
371
+ #assertHeaderValue(value) {
372
+ if (typeof value !== 'string' && typeof value !== 'number') {
373
+ throw new ResponseError('INVALID_HEADER_VALUE');
374
+ }
375
+
376
+ if (typeof value === 'number' && !Number.isFinite(value)) {
377
+ throw new ResponseError('INVALID_HEADER_VALUE');
378
+ }
379
+
380
+ if (typeof value === 'string' && !HEADER_VALUE_PATTERN.test(value)) {
381
+ throw new ResponseError('INVALID_HEADER_VALUE');
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Ensures that the `Content-Type` header cannot be mutated once locked.
387
+ *
388
+ * @param {string} name - The HTTP header name being modified.
389
+ * @param {HeaderOperation} operation - The operation being performed.
390
+ * @throws {ResponseError} If `Content-Type` is already locked.
391
+ */
392
+ #assertContentTypeNotLocked(name, operation) {
393
+ if (name.toLowerCase() === 'content-type' && this.#contentTypeLocked) {
394
+ throw new ResponseError('CONTENT_TYPE_LOCKED', {
395
+ values: { operation },
396
+ });
397
+ }
398
+ }
399
+ }
400
+
401
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
402
+ export default ResponseHeaders;