@reykjavik/webtools 0.1.23 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.1.25
8
+
9
+ _2024-05-17_
10
+
11
+ - `@reykjavik/webtools/http`:
12
+ - feat: `cacheControl` now also accepts standard `Response` objects
13
+
14
+ ## 0.1.24
15
+
16
+ _2024-03-22_
17
+
18
+ - `@reykjavik/webtools/fixIcelandicLocale`:
19
+ - fix: Add missing patches for `Date.prototype.toLocaleString` and
20
+ `Date.prototype.toLocaleTimeString`
21
+ - fix: Correct `Date.prototype.toLocaleDateString` options defaults/handling
22
+
7
23
  ## 0.1.23
8
24
 
9
25
  _2024-03-21_
package/README.md CHANGED
@@ -56,7 +56,8 @@ Various framework agnostic helpers for leveraging HTTP magic.
56
56
 
57
57
  ### HTTP Status Codes
58
58
 
59
- All the web-related HTTP status codes are exported with human-readable names:
59
+ All the web-related HTTP status codes are exported with human-readable names
60
+ and a short JSDoc comment:
60
61
 
61
62
  - `HTTP_200_OK`
62
63
  - `HTTP_303_SeeOther`
@@ -71,34 +72,42 @@ All the web-related HTTP status codes are exported with human-readable names:
71
72
  - `HTTP_500_InternalServerError`
72
73
  - ...ad nauseum.
73
74
 
75
+ These make your code more readable and less prone to accidental mistakes:
76
+
77
+ ```ts
78
+ import { HTTP_200_OK, HTTP_404_NotFound } from '@reykjavik/webtools/http';
79
+
80
+ console.log(HTTP_200_OK); // 200
81
+ console.log(HTTP_404_NotFound); // 404
82
+ ```
83
+
74
84
  ### Types for HTTP Status code groups
75
85
 
76
86
  These type unions are useful when writing HTTP helper functions and error
77
- handling, etc.
87
+ handlers, etc.
78
88
 
79
89
  Union Types for the more commonly occurrring HTTP Status codes:
80
90
 
81
- - `HTTP_STATUS` (all the status-codes!)
82
- - `HTTP_INFO` (100, 101)
83
- - `HTTP_SUCCESS` (200, 201, 202)
84
- - `HTTP_REDIRECTION` (301, 302, 303, 304, 307, 308)
85
- - `HTTP_NOTMODIFIED` (304)
86
- - `HTTP_ERROR`
87
- - `HTTP_CLIENT_ERROR`
88
- - `HTTP_NOT_FOUND` (400, 404, 410)
89
- - `HTTP_BANNED` (401, 403)
90
- - `HTTP_SERVER_ERROR` (500)
91
-
92
- More complete union types, including all the esoteric status codes, are also
93
- available:
94
-
95
- - `HTTP_STATUS` (all the status-codes!)
91
+ - `HTTP_INFO` (100, 101)
92
+ - `HTTP_SUCCESS` (200, 201, 202)
93
+ - `HTTP_REDIRECTION` (301, 302, 303, 304, 307, 308)
94
+ - `HTTP_NOTMODIFIED` (304)
95
+ - `HTTP_ERROR` (400, 404, 410, 401, 403, 500)
96
+ - `HTTP_CLIENT_ERROR` (400, 404, 410, 401, 403)
97
+ - `HTTP_NOT_FOUND` (400, 404, 410)
98
+ - `HTTP_BANNED` (401, 403)
99
+ - `HTTP_SERVER_ERROR` (500)
100
+
101
+ It also offers more complete union types, including all the esoteric status
102
+ codes, are also available:
103
+
104
+ - `HTTP_STATUS` (**all** the status-codes!)
96
105
  - `HTTP_INFO_ALL` (1\*\*)
97
106
  - `HTTP_SUCCESS_ALL` (2\*\*)
98
107
  - `HTTP_REDIRECTION_ALL` (3\*\*)
99
- - `HTTP_ERROR_ALL`
108
+ - `HTTP_ERROR_ALL` (4\*\* and 5\*\*)
100
109
  - `HTTP_CLIENT_ERROR_ALL` (4\*\*)
101
- - `HTTP_SERVER_ERROR_ALL` (4\*\*)
110
+ - `HTTP_SERVER_ERROR_ALL` (5\*\*)
102
111
 
103
112
  ### `cacheControl` helper
104
113
 
@@ -259,7 +268,8 @@ values.
259
268
 
260
269
  - `Intl.Collator` and `String.prototype.localeCompare`
261
270
  - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
262
- - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
271
+ - `Intl.DateTimeFormat` and `Date.prototype.toLocaleString`,
272
+ `.toLocaleDateString`, and `.toLocaleTimeString`
263
273
  - `Intl.PluralRules`
264
274
  - `Intl.ListFormat`
265
275
 
@@ -332,28 +342,20 @@ editor), but there's a brief summary:
332
342
  - `scriptUrl?: string` — The full SiteImprove analytics script URL.
333
343
  (alternative to `accountId` prop).
334
344
  - `hasConsented?: boolean` — Manual GDPR 'analytics' consent flag. Allows hard
335
- opt-out, but defers to
336
- [`CookieHubProvider` values](./README.md#usecookiehubconsent) if they are
337
- available.
345
+ opt-out, but defers to [`CookieHubProvider` values](#usecookiehubconsent) if
346
+ they are available.
338
347
  - `onLoad?: (e: unknown) => void` — Fires when the script has loaded.
339
348
  - `onError?: (e: unknown) => void` — Fires if loading the script failed.
340
349
 
341
350
  Example usage somewhere in your application:
342
351
 
343
- ```js
352
+ ```jsz
344
353
  import { SiteImprove } from '@reykjavik/webtools/SiteImprove';
345
354
 
346
- // ideally emit this from your loader function
347
355
  const siteImproveAccountId = '[ACCOUNT_ID]'; // e.g. "7654321"
348
356
 
349
- const location = useRouter()
350
- // ...then Inside root.tsx component:
351
- <SiteImprove
352
- accountId={siteImproveAccountId}
353
- onError={(error) =>
354
- Logger('error', 'An error occured initializing siteimprove', error)
355
- }
356
- />;
357
+ // ...then inside your main App component
358
+ <SiteImprove accountId={siteImproveAccountId} />;
357
359
  ```
358
360
 
359
361
  In dev mode it does NOT load the SiteImprove script and merely logs page-view
@@ -1,4 +1,4 @@
1
- import { _PatchedCollator, _PatchedDateTimeFormat, _PatchedListFormat, _patchedLocaleCompare, _PatchedNumberFormat, _PatchedPluralRules, _patchedToLocaleDateString, _patchedToLocaleString, } from './fixIcelandicLocale.privates.js';
1
+ import { _PatchedCollator, _PatchedDateTimeFormat, _patchedDateToLocaleDateString, _patchedDateToLocaleString, _patchedDateToLocaleTimeString, _PatchedListFormat, _PatchedNumberFormat, _patchedNumberToLocaleString, _PatchedPluralRules, _patchedStringLocaleCompare, } from './fixIcelandicLocale.privates.js';
2
2
  /*
3
3
  Mantra: Partial Icelandic suppoort is better than none. Partial Icelandic
4
4
  suppoort is better than none. Partial Icelandic suppoort is better than
@@ -6,11 +6,13 @@ import { _PatchedCollator, _PatchedDateTimeFormat, _PatchedListFormat, _patchedL
6
6
  */
7
7
  if (Intl.Collator.supportedLocalesOf(['is']).length < 1) {
8
8
  Intl.Collator = _PatchedCollator;
9
- String.prototype.localeCompare = _patchedLocaleCompare;
9
+ String.prototype.localeCompare = _patchedStringLocaleCompare;
10
10
  Intl.NumberFormat = _PatchedNumberFormat;
11
- Number.prototype.toLocaleString = _patchedToLocaleString;
11
+ Number.prototype.toLocaleString = _patchedNumberToLocaleString;
12
12
  Intl.DateTimeFormat = _PatchedDateTimeFormat;
13
- Date.prototype.toLocaleDateString = _patchedToLocaleDateString;
13
+ Date.prototype.toLocaleString = _patchedDateToLocaleString;
14
+ Date.prototype.toLocaleDateString = _patchedDateToLocaleDateString;
15
+ Date.prototype.toLocaleTimeString = _patchedDateToLocaleTimeString;
14
16
  }
15
17
  /* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unnecessary-condition */
16
18
  if (Intl.ListFormat && Intl.ListFormat.supportedLocalesOf(['is']).length < 1) {
@@ -5,7 +5,7 @@ export declare const _PatchedCollator: {
5
5
  } & {
6
6
  $original: typeof Intl.Collator;
7
7
  };
8
- export declare const _patchedLocaleCompare: {
8
+ export declare const _patchedStringLocaleCompare: {
9
9
  (this: string, that: string, locales?: string | Array<string>, options?: Intl.CollatorOptions): number;
10
10
  $original: {
11
11
  (that: string): number;
@@ -20,7 +20,7 @@ export declare const _PatchedNumberFormat: {
20
20
  } & {
21
21
  $original: typeof Intl.NumberFormat;
22
22
  };
23
- export declare const _patchedToLocaleString: {
23
+ export declare const _patchedNumberToLocaleString: {
24
24
  (this: number, locales?: string | Array<string>, options?: Intl.NumberFormatOptions): string;
25
25
  $original: {
26
26
  (locales?: string | string[] | undefined, options?: Intl.NumberFormatOptions | undefined): string;
@@ -35,7 +35,23 @@ export declare const _PatchedDateTimeFormat: {
35
35
  } & {
36
36
  $original: typeof Intl.DateTimeFormat;
37
37
  };
38
- export declare const _patchedToLocaleDateString: {
38
+ export declare const _patchedDateToLocaleString: {
39
+ (this: Date, locales?: string | Array<string>, options?: Intl.DateTimeFormatOptions): string;
40
+ $original: {
41
+ (): string;
42
+ (locales?: string | string[] | undefined, options?: Intl.DateTimeFormatOptions | undefined): string;
43
+ (locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions | undefined): string;
44
+ };
45
+ };
46
+ export declare const _patchedDateToLocaleDateString: {
47
+ (this: Date, locales?: string | Array<string>, options?: Intl.DateTimeFormatOptions): string;
48
+ $original: {
49
+ (): string;
50
+ (locales?: string | string[] | undefined, options?: Intl.DateTimeFormatOptions | undefined): string;
51
+ (locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions | undefined): string;
52
+ };
53
+ };
54
+ export declare const _patchedDateToLocaleTimeString: {
39
55
  (this: Date, locales?: string | Array<string>, options?: Intl.DateTimeFormatOptions): string;
40
56
  $original: {
41
57
  (): string;
@@ -50,11 +50,11 @@ PatchedCollator.supportedLocalesOf = _Collator.supportedLocalesOf;
50
50
  PatchedCollator.$original = _Collator;
51
51
  export const _PatchedCollator = PatchedCollator;
52
52
  // ---------------------------------------------------------------------------
53
- const _localeCompare = String.prototype.localeCompare;
54
- export const _patchedLocaleCompare = function localeCompare(that, locales, options) {
53
+ const _stringLocaleCompare = String.prototype.localeCompare;
54
+ export const _patchedStringLocaleCompare = function localeCompare(that, locales, options) {
55
55
  return _PatchedCollator(locales, options).compare(this, that);
56
56
  };
57
- _patchedLocaleCompare.$original = _localeCompare;
57
+ _patchedStringLocaleCompare.$original = _stringLocaleCompare;
58
58
  // ===========================================================================
59
59
  // NumberFormat
60
60
  // ===========================================================================
@@ -99,11 +99,11 @@ PatchedNumberFormat.supportedLocalesOf = _NumberFormat.supportedLocalesOf;
99
99
  PatchedNumberFormat.$original = _NumberFormat;
100
100
  export const _PatchedNumberFormat = PatchedNumberFormat;
101
101
  // ---------------------------------------------------------------------------
102
- const _toLocaleString = Number.prototype.toLocaleString;
103
- export const _patchedToLocaleString = function toLocaleString(locales, options) {
102
+ const _numberToLocaleString = Number.prototype.toLocaleString;
103
+ export const _patchedNumberToLocaleString = function toLocaleString(locales, options) {
104
104
  return _PatchedNumberFormat(locales, options).format(this);
105
105
  };
106
- _patchedToLocaleString.$original = _toLocaleString;
106
+ _patchedNumberToLocaleString.$original = _numberToLocaleString;
107
107
  // ===========================================================================
108
108
  // DateTimeFormat
109
109
  // ===========================================================================
@@ -225,11 +225,71 @@ PatchedDateTimeFormat.supportedLocalesOf = _DateTimeFormat.supportedLocalesOf;
225
225
  PatchedDateTimeFormat.$original = _DateTimeFormat;
226
226
  export const _PatchedDateTimeFormat = PatchedDateTimeFormat;
227
227
  // ---------------------------------------------------------------------------
228
- const _toLocaleDateString = Date.prototype.toLocaleDateString;
229
- export const _patchedToLocaleDateString = function toLocaleDateString(locales, options) {
228
+ const _dateToLocaleString = Date.prototype.toLocaleString;
229
+ export const _patchedDateToLocaleString = function toLocaleString(locales, options) {
230
+ options = options || {};
231
+ if (!options.weekday &&
232
+ !options.year &&
233
+ !options.month &&
234
+ !options.day &&
235
+ !options.dayPeriod &&
236
+ !options.hour &&
237
+ !options.minute &&
238
+ !options.second &&
239
+ !options.fractionalSecondDigits) {
240
+ options = {
241
+ ...options,
242
+ year: 'numeric',
243
+ month: 'numeric',
244
+ day: 'numeric',
245
+ hour: 'numeric',
246
+ minute: 'numeric',
247
+ second: 'numeric',
248
+ };
249
+ }
250
+ return _PatchedDateTimeFormat(locales, options).format(this);
251
+ };
252
+ _patchedDateToLocaleString.$original = _dateToLocaleString;
253
+ // ---------------------------------------------------------------------------
254
+ const _dateToLocaleDateString = Date.prototype.toLocaleDateString;
255
+ export const _patchedDateToLocaleDateString = function toLocaleDateString(locales, options) {
256
+ options = options || {};
257
+ if (options.timeStyle) {
258
+ throw new TypeError("can't set option timeStyle in Date.toLocaleDateString()");
259
+ }
260
+ if (!options.weekday && !options.year && !options.month && !options.day) {
261
+ options = {
262
+ ...options,
263
+ year: 'numeric',
264
+ month: 'numeric',
265
+ day: 'numeric',
266
+ };
267
+ }
268
+ return _PatchedDateTimeFormat(locales, options).format(this);
269
+ };
270
+ _patchedDateToLocaleDateString.$original = _dateToLocaleDateString;
271
+ // ---------------------------------------------------------------------------
272
+ const _dateToLocaleTimeString = Date.prototype.toLocaleTimeString;
273
+ export const _patchedDateToLocaleTimeString = function toLocaleTimeString(locales, options) {
274
+ options = options || {};
275
+ if (options.dateStyle) {
276
+ throw new TypeError("can't set option dateStyle in Date.toLocaleTimeString()");
277
+ }
278
+ if (!options.dayPeriod &&
279
+ !options.hour &&
280
+ !options.minute &&
281
+ !options.second &&
282
+ !options.fractionalSecondDigits) {
283
+ options = {
284
+ ...options,
285
+ hour: 'numeric',
286
+ minute: 'numeric',
287
+ second: 'numeric',
288
+ };
289
+ }
230
290
  return _PatchedDateTimeFormat(locales, options).format(this);
231
291
  };
232
- _patchedToLocaleDateString.$original = _toLocaleDateString;
292
+ _patchedDateToLocaleTimeString.$original = _dateToLocaleTimeString;
233
293
  // ===========================================================================
234
294
  // PluralRules
235
295
  // ===========================================================================
package/esm/http.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { ServerResponse } from 'http';
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { ServerResponse } from 'node:http';
2
3
  /** The client should continue the request or ignore the response if the request is already finished. */
3
4
  export declare const HTTP_100_Continue = 100;
4
5
  /** Response to an Upgrade request header from the client and indicates the protocol the server is switching to. */
@@ -163,13 +164,19 @@ export type TTLConfig = TTL | TTLKeywords | TTLObj;
163
164
  * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#tosec-ttl-helper
164
165
  */
165
166
  export declare const toSec: (ttl: TTL) => number;
167
+ type ServerResponseStub = Pick<ServerResponse, 'setHeader' | 'getHeader' | 'removeHeader'> & {
168
+ headers?: Record<string, string | Array<string>>;
169
+ };
170
+ type ResponseStub = {
171
+ headers: Pick<Headers, 'set' | 'get' | 'delete' | 'append'>;
172
+ };
166
173
  /**
167
174
  * Use this function to quickly set the `Cache-Control` header with a `max-age=`
168
175
  * on a HTTP response
169
176
  *
170
177
  * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#getcssbundleurl
171
178
  */
172
- export declare const cacheControl: (response: ServerResponse | {
173
- res: ServerResponse;
179
+ export declare const cacheControl: (response: ServerResponseStub | ResponseStub | {
180
+ res: ServerResponseStub | ResponseStub;
174
181
  }, ttlCfg: TTLConfig, eTag?: string | number) => void;
175
182
  export {};
package/esm/http.js CHANGED
@@ -146,6 +146,31 @@ export const toSec = (ttl) => {
146
146
  }
147
147
  return Math.max(0, Math.round(ttl)) || 0;
148
148
  };
149
+ const toRespnseStubHeaders = (response) => {
150
+ if ('headers' in response && !('setHeader' in response)) {
151
+ return response.headers;
152
+ }
153
+ return {
154
+ get: (name) => {
155
+ const val = response.getHeader(name);
156
+ if (Array.isArray(val)) {
157
+ return val.join(', ');
158
+ }
159
+ return val != null ? `${val}` : null;
160
+ },
161
+ set: (name, value) => response.setHeader(name, value),
162
+ append: (name, value) => {
163
+ const existing = response.getHeader(name);
164
+ if (existing) {
165
+ response.setHeader(name, `${existing}, ${value}`);
166
+ }
167
+ else {
168
+ response.setHeader(name, value);
169
+ }
170
+ },
171
+ delete: (name) => response.removeHeader(name),
172
+ };
173
+ };
149
174
  const stabilities = {
150
175
  revalidate: ', must-revalidate',
151
176
  immutable: ', immutable',
@@ -153,16 +178,17 @@ const stabilities = {
153
178
  };
154
179
  const setCC = (response, cc) => {
155
180
  const devModeHeader = 'X-Cache-Control';
181
+ const headers = toRespnseStubHeaders(response);
156
182
  // Also set `X-Cache-Control` in dev mode, because some frameworks
157
- // **cough** **nextjs** **cough** forcefully override the `Cache-Control`
183
+ // **cough** **Nextjs** **cough** forcefully override the `Cache-Control`
158
184
  // header when the server is in dev mode.
159
185
  if (!cc) {
160
- response.removeHeader('Cache-Control');
161
- process.env.NODE_ENV !== 'production' && response.removeHeader(devModeHeader);
186
+ headers.delete('Cache-Control');
187
+ process.env.NODE_ENV !== 'production' && headers.delete(devModeHeader);
162
188
  return;
163
189
  }
164
- response.setHeader('Cache-Control', cc);
165
- process.env.NODE_ENV !== 'production' && response.setHeader(devModeHeader, cc);
190
+ headers.set('Cache-Control', cc);
191
+ process.env.NODE_ENV !== 'production' && headers.set(devModeHeader, cc);
166
192
  };
167
193
  /**
168
194
  * Use this function to quickly set the `Cache-Control` header with a `max-age=`
@@ -189,7 +215,7 @@ export const cacheControl = (response, ttlCfg, eTag) => {
189
215
  }
190
216
  }
191
217
  if (maxAge == null) {
192
- response.removeHeader('Cache-Control');
218
+ toRespnseStubHeaders(response).delete('Cache-Control');
193
219
  return;
194
220
  }
195
221
  maxAge = toSec(maxAge);
@@ -204,5 +230,5 @@ export const cacheControl = (response, ttlCfg, eTag) => {
204
230
  const scope = opts.publ ? 'public' : 'private';
205
231
  const stability = (opts.stability && stabilities[opts.stability]) || stabilities.immutable;
206
232
  setCC(response, `${scope}, max-age=${maxAge + sWR + sIE + stability}`);
207
- eTag != null && response.setHeader('ETag', eTag);
233
+ eTag != null && toRespnseStubHeaders(response).set('ETag', String(eTag));
208
234
  };
@@ -37,7 +37,7 @@ export type WaitComponent<CustomProps extends Record<string, unknown> = Record<n
37
37
  displayName?: string;
38
38
  };
39
39
  /**
40
- * Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
40
+ * A thin wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
41
41
  * component, to provide a more ergonomic API.
42
42
  *
43
43
  * If the awaited promise (`props.for`) resolves to an object with a truthy
package/esm/remix/Wait.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import React, { Suspense } from 'react';
2
2
  import { Await } from '@remix-run/react';
3
3
  /**
4
- * Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
4
+ * A thin wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
5
5
  * component, to provide a more ergonomic API.
6
6
  *
7
7
  * If the awaited promise (`props.for`) resolves to an object with a truthy
@@ -8,11 +8,13 @@ const fixIcelandicLocale_privates_js_1 = require("./fixIcelandicLocale.privates.
8
8
  */
9
9
  if (Intl.Collator.supportedLocalesOf(['is']).length < 1) {
10
10
  Intl.Collator = fixIcelandicLocale_privates_js_1._PatchedCollator;
11
- String.prototype.localeCompare = fixIcelandicLocale_privates_js_1._patchedLocaleCompare;
11
+ String.prototype.localeCompare = fixIcelandicLocale_privates_js_1._patchedStringLocaleCompare;
12
12
  Intl.NumberFormat = fixIcelandicLocale_privates_js_1._PatchedNumberFormat;
13
- Number.prototype.toLocaleString = fixIcelandicLocale_privates_js_1._patchedToLocaleString;
13
+ Number.prototype.toLocaleString = fixIcelandicLocale_privates_js_1._patchedNumberToLocaleString;
14
14
  Intl.DateTimeFormat = fixIcelandicLocale_privates_js_1._PatchedDateTimeFormat;
15
- Date.prototype.toLocaleDateString = fixIcelandicLocale_privates_js_1._patchedToLocaleDateString;
15
+ Date.prototype.toLocaleString = fixIcelandicLocale_privates_js_1._patchedDateToLocaleString;
16
+ Date.prototype.toLocaleDateString = fixIcelandicLocale_privates_js_1._patchedDateToLocaleDateString;
17
+ Date.prototype.toLocaleTimeString = fixIcelandicLocale_privates_js_1._patchedDateToLocaleTimeString;
16
18
  }
17
19
  /* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unnecessary-condition */
18
20
  if (Intl.ListFormat && Intl.ListFormat.supportedLocalesOf(['is']).length < 1) {
@@ -5,7 +5,7 @@ export declare const _PatchedCollator: {
5
5
  } & {
6
6
  $original: typeof Intl.Collator;
7
7
  };
8
- export declare const _patchedLocaleCompare: {
8
+ export declare const _patchedStringLocaleCompare: {
9
9
  (this: string, that: string, locales?: string | Array<string>, options?: Intl.CollatorOptions): number;
10
10
  $original: {
11
11
  (that: string): number;
@@ -20,7 +20,7 @@ export declare const _PatchedNumberFormat: {
20
20
  } & {
21
21
  $original: typeof Intl.NumberFormat;
22
22
  };
23
- export declare const _patchedToLocaleString: {
23
+ export declare const _patchedNumberToLocaleString: {
24
24
  (this: number, locales?: string | Array<string>, options?: Intl.NumberFormatOptions): string;
25
25
  $original: {
26
26
  (locales?: string | string[] | undefined, options?: Intl.NumberFormatOptions | undefined): string;
@@ -35,7 +35,23 @@ export declare const _PatchedDateTimeFormat: {
35
35
  } & {
36
36
  $original: typeof Intl.DateTimeFormat;
37
37
  };
38
- export declare const _patchedToLocaleDateString: {
38
+ export declare const _patchedDateToLocaleString: {
39
+ (this: Date, locales?: string | Array<string>, options?: Intl.DateTimeFormatOptions): string;
40
+ $original: {
41
+ (): string;
42
+ (locales?: string | string[] | undefined, options?: Intl.DateTimeFormatOptions | undefined): string;
43
+ (locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions | undefined): string;
44
+ };
45
+ };
46
+ export declare const _patchedDateToLocaleDateString: {
47
+ (this: Date, locales?: string | Array<string>, options?: Intl.DateTimeFormatOptions): string;
48
+ $original: {
49
+ (): string;
50
+ (locales?: string | string[] | undefined, options?: Intl.DateTimeFormatOptions | undefined): string;
51
+ (locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions | undefined): string;
52
+ };
53
+ };
54
+ export declare const _patchedDateToLocaleTimeString: {
39
55
  (this: Date, locales?: string | Array<string>, options?: Intl.DateTimeFormatOptions): string;
40
56
  $original: {
41
57
  (): string;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _a, _b;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports._PatchedListFormat = exports._PatchedPluralRules = exports._patchedToLocaleDateString = exports._PatchedDateTimeFormat = exports._patchedToLocaleString = exports._PatchedNumberFormat = exports._patchedLocaleCompare = exports._PatchedCollator = void 0;
4
+ exports._PatchedListFormat = exports._PatchedPluralRules = exports._patchedDateToLocaleTimeString = exports._patchedDateToLocaleDateString = exports._patchedDateToLocaleString = exports._PatchedDateTimeFormat = exports._patchedNumberToLocaleString = exports._PatchedNumberFormat = exports._patchedStringLocaleCompare = exports._PatchedCollator = void 0;
5
5
  const _Collator = Intl.Collator;
6
6
  const mapLocales = (locales) => {
7
7
  locales = typeof locales === 'string' ? [locales] : locales || [];
@@ -53,12 +53,12 @@ PatchedCollator.supportedLocalesOf = _Collator.supportedLocalesOf;
53
53
  PatchedCollator.$original = _Collator;
54
54
  exports._PatchedCollator = PatchedCollator;
55
55
  // ---------------------------------------------------------------------------
56
- const _localeCompare = String.prototype.localeCompare;
57
- const _patchedLocaleCompare = function localeCompare(that, locales, options) {
56
+ const _stringLocaleCompare = String.prototype.localeCompare;
57
+ const _patchedStringLocaleCompare = function localeCompare(that, locales, options) {
58
58
  return (0, exports._PatchedCollator)(locales, options).compare(this, that);
59
59
  };
60
- exports._patchedLocaleCompare = _patchedLocaleCompare;
61
- exports._patchedLocaleCompare.$original = _localeCompare;
60
+ exports._patchedStringLocaleCompare = _patchedStringLocaleCompare;
61
+ exports._patchedStringLocaleCompare.$original = _stringLocaleCompare;
62
62
  // ===========================================================================
63
63
  // NumberFormat
64
64
  // ===========================================================================
@@ -103,12 +103,12 @@ PatchedNumberFormat.supportedLocalesOf = _NumberFormat.supportedLocalesOf;
103
103
  PatchedNumberFormat.$original = _NumberFormat;
104
104
  exports._PatchedNumberFormat = PatchedNumberFormat;
105
105
  // ---------------------------------------------------------------------------
106
- const _toLocaleString = Number.prototype.toLocaleString;
107
- const _patchedToLocaleString = function toLocaleString(locales, options) {
106
+ const _numberToLocaleString = Number.prototype.toLocaleString;
107
+ const _patchedNumberToLocaleString = function toLocaleString(locales, options) {
108
108
  return (0, exports._PatchedNumberFormat)(locales, options).format(this);
109
109
  };
110
- exports._patchedToLocaleString = _patchedToLocaleString;
111
- exports._patchedToLocaleString.$original = _toLocaleString;
110
+ exports._patchedNumberToLocaleString = _patchedNumberToLocaleString;
111
+ exports._patchedNumberToLocaleString.$original = _numberToLocaleString;
112
112
  // ===========================================================================
113
113
  // DateTimeFormat
114
114
  // ===========================================================================
@@ -230,12 +230,74 @@ PatchedDateTimeFormat.supportedLocalesOf = _DateTimeFormat.supportedLocalesOf;
230
230
  PatchedDateTimeFormat.$original = _DateTimeFormat;
231
231
  exports._PatchedDateTimeFormat = PatchedDateTimeFormat;
232
232
  // ---------------------------------------------------------------------------
233
- const _toLocaleDateString = Date.prototype.toLocaleDateString;
234
- const _patchedToLocaleDateString = function toLocaleDateString(locales, options) {
233
+ const _dateToLocaleString = Date.prototype.toLocaleString;
234
+ const _patchedDateToLocaleString = function toLocaleString(locales, options) {
235
+ options = options || {};
236
+ if (!options.weekday &&
237
+ !options.year &&
238
+ !options.month &&
239
+ !options.day &&
240
+ !options.dayPeriod &&
241
+ !options.hour &&
242
+ !options.minute &&
243
+ !options.second &&
244
+ !options.fractionalSecondDigits) {
245
+ options = {
246
+ ...options,
247
+ year: 'numeric',
248
+ month: 'numeric',
249
+ day: 'numeric',
250
+ hour: 'numeric',
251
+ minute: 'numeric',
252
+ second: 'numeric',
253
+ };
254
+ }
255
+ return (0, exports._PatchedDateTimeFormat)(locales, options).format(this);
256
+ };
257
+ exports._patchedDateToLocaleString = _patchedDateToLocaleString;
258
+ exports._patchedDateToLocaleString.$original = _dateToLocaleString;
259
+ // ---------------------------------------------------------------------------
260
+ const _dateToLocaleDateString = Date.prototype.toLocaleDateString;
261
+ const _patchedDateToLocaleDateString = function toLocaleDateString(locales, options) {
262
+ options = options || {};
263
+ if (options.timeStyle) {
264
+ throw new TypeError("can't set option timeStyle in Date.toLocaleDateString()");
265
+ }
266
+ if (!options.weekday && !options.year && !options.month && !options.day) {
267
+ options = {
268
+ ...options,
269
+ year: 'numeric',
270
+ month: 'numeric',
271
+ day: 'numeric',
272
+ };
273
+ }
274
+ return (0, exports._PatchedDateTimeFormat)(locales, options).format(this);
275
+ };
276
+ exports._patchedDateToLocaleDateString = _patchedDateToLocaleDateString;
277
+ exports._patchedDateToLocaleDateString.$original = _dateToLocaleDateString;
278
+ // ---------------------------------------------------------------------------
279
+ const _dateToLocaleTimeString = Date.prototype.toLocaleTimeString;
280
+ const _patchedDateToLocaleTimeString = function toLocaleTimeString(locales, options) {
281
+ options = options || {};
282
+ if (options.dateStyle) {
283
+ throw new TypeError("can't set option dateStyle in Date.toLocaleTimeString()");
284
+ }
285
+ if (!options.dayPeriod &&
286
+ !options.hour &&
287
+ !options.minute &&
288
+ !options.second &&
289
+ !options.fractionalSecondDigits) {
290
+ options = {
291
+ ...options,
292
+ hour: 'numeric',
293
+ minute: 'numeric',
294
+ second: 'numeric',
295
+ };
296
+ }
235
297
  return (0, exports._PatchedDateTimeFormat)(locales, options).format(this);
236
298
  };
237
- exports._patchedToLocaleDateString = _patchedToLocaleDateString;
238
- exports._patchedToLocaleDateString.$original = _toLocaleDateString;
299
+ exports._patchedDateToLocaleTimeString = _patchedDateToLocaleTimeString;
300
+ exports._patchedDateToLocaleTimeString.$original = _dateToLocaleTimeString;
239
301
  // ===========================================================================
240
302
  // PluralRules
241
303
  // ===========================================================================
package/http.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { ServerResponse } from 'http';
1
+ /// <reference types="node" />
2
+ import { ServerResponse } from 'node:http';
2
3
  /** The client should continue the request or ignore the response if the request is already finished. */
3
4
  export declare const HTTP_100_Continue = 100;
4
5
  /** Response to an Upgrade request header from the client and indicates the protocol the server is switching to. */
@@ -163,13 +164,19 @@ export type TTLConfig = TTL | TTLKeywords | TTLObj;
163
164
  * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#tosec-ttl-helper
164
165
  */
165
166
  export declare const toSec: (ttl: TTL) => number;
167
+ type ServerResponseStub = Pick<ServerResponse, 'setHeader' | 'getHeader' | 'removeHeader'> & {
168
+ headers?: Record<string, string | Array<string>>;
169
+ };
170
+ type ResponseStub = {
171
+ headers: Pick<Headers, 'set' | 'get' | 'delete' | 'append'>;
172
+ };
166
173
  /**
167
174
  * Use this function to quickly set the `Cache-Control` header with a `max-age=`
168
175
  * on a HTTP response
169
176
  *
170
177
  * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#getcssbundleurl
171
178
  */
172
- export declare const cacheControl: (response: ServerResponse | {
173
- res: ServerResponse;
179
+ export declare const cacheControl: (response: ServerResponseStub | ResponseStub | {
180
+ res: ServerResponseStub | ResponseStub;
174
181
  }, ttlCfg: TTLConfig, eTag?: string | number) => void;
175
182
  export {};
package/http.js CHANGED
@@ -151,6 +151,31 @@ const toSec = (ttl) => {
151
151
  return Math.max(0, Math.round(ttl)) || 0;
152
152
  };
153
153
  exports.toSec = toSec;
154
+ const toRespnseStubHeaders = (response) => {
155
+ if ('headers' in response && !('setHeader' in response)) {
156
+ return response.headers;
157
+ }
158
+ return {
159
+ get: (name) => {
160
+ const val = response.getHeader(name);
161
+ if (Array.isArray(val)) {
162
+ return val.join(', ');
163
+ }
164
+ return val != null ? `${val}` : null;
165
+ },
166
+ set: (name, value) => response.setHeader(name, value),
167
+ append: (name, value) => {
168
+ const existing = response.getHeader(name);
169
+ if (existing) {
170
+ response.setHeader(name, `${existing}, ${value}`);
171
+ }
172
+ else {
173
+ response.setHeader(name, value);
174
+ }
175
+ },
176
+ delete: (name) => response.removeHeader(name),
177
+ };
178
+ };
154
179
  const stabilities = {
155
180
  revalidate: ', must-revalidate',
156
181
  immutable: ', immutable',
@@ -158,16 +183,17 @@ const stabilities = {
158
183
  };
159
184
  const setCC = (response, cc) => {
160
185
  const devModeHeader = 'X-Cache-Control';
186
+ const headers = toRespnseStubHeaders(response);
161
187
  // Also set `X-Cache-Control` in dev mode, because some frameworks
162
- // **cough** **nextjs** **cough** forcefully override the `Cache-Control`
188
+ // **cough** **Nextjs** **cough** forcefully override the `Cache-Control`
163
189
  // header when the server is in dev mode.
164
190
  if (!cc) {
165
- response.removeHeader('Cache-Control');
166
- process.env.NODE_ENV !== 'production' && response.removeHeader(devModeHeader);
191
+ headers.delete('Cache-Control');
192
+ process.env.NODE_ENV !== 'production' && headers.delete(devModeHeader);
167
193
  return;
168
194
  }
169
- response.setHeader('Cache-Control', cc);
170
- process.env.NODE_ENV !== 'production' && response.setHeader(devModeHeader, cc);
195
+ headers.set('Cache-Control', cc);
196
+ process.env.NODE_ENV !== 'production' && headers.set(devModeHeader, cc);
171
197
  };
172
198
  /**
173
199
  * Use this function to quickly set the `Cache-Control` header with a `max-age=`
@@ -194,7 +220,7 @@ const cacheControl = (response, ttlCfg, eTag) => {
194
220
  }
195
221
  }
196
222
  if (maxAge == null) {
197
- response.removeHeader('Cache-Control');
223
+ toRespnseStubHeaders(response).delete('Cache-Control');
198
224
  return;
199
225
  }
200
226
  maxAge = (0, exports.toSec)(maxAge);
@@ -209,6 +235,6 @@ const cacheControl = (response, ttlCfg, eTag) => {
209
235
  const scope = opts.publ ? 'public' : 'private';
210
236
  const stability = (opts.stability && stabilities[opts.stability]) || stabilities.immutable;
211
237
  setCC(response, `${scope}, max-age=${maxAge + sWR + sIE + stability}`);
212
- eTag != null && response.setHeader('ETag', eTag);
238
+ eTag != null && toRespnseStubHeaders(response).set('ETag', String(eTag));
213
239
  };
214
240
  exports.cacheControl = cacheControl;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/webtools",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "Misc. JS/TS helpers used by Reykjavík City's web dev teams.",
5
5
  "main": "index.js",
6
6
  "repository": "ssh://git@github.com:reykjavikcity/webtools.git",
package/remix/Wait.d.ts CHANGED
@@ -37,7 +37,7 @@ export type WaitComponent<CustomProps extends Record<string, unknown> = Record<n
37
37
  displayName?: string;
38
38
  };
39
39
  /**
40
- * Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
40
+ * A thin wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
41
41
  * component, to provide a more ergonomic API.
42
42
  *
43
43
  * If the awaited promise (`props.for`) resolves to an object with a truthy
package/remix/Wait.js CHANGED
@@ -27,7 +27,7 @@ exports.Wait = void 0;
27
27
  const react_1 = __importStar(require("react"));
28
28
  const react_2 = require("@remix-run/react");
29
29
  /**
30
- * Wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
30
+ * A thin wrapper around [Remix's `Await`](https://remix.run/docs/en/2/components/await)
31
31
  * component, to provide a more ergonomic API.
32
32
  *
33
33
  * If the awaited promise (`props.for`) resolves to an object with a truthy