@reykjavik/webtools 0.1.24 → 0.1.26

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,20 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.1.26
8
+
9
+ _2024-05-25_
10
+
11
+ - `@reykjavik/webtools/fixIcelandicLocale`:
12
+ - fix: Emit `"*dagur, "`, not `"*dagurinn "` from `Intl.DateTimeFormat`
13
+
14
+ ## 0.1.25
15
+
16
+ _2024-05-17_
17
+
18
+ - `@reykjavik/webtools/http`:
19
+ - feat: `cacheControl` now also accepts standard `Response` objects
20
+
7
21
  ## 0.1.24
8
22
 
9
23
  _2024-03-22_
package/README.md CHANGED
@@ -268,7 +268,8 @@ values.
268
268
 
269
269
  - `Intl.Collator` and `String.prototype.localeCompare`
270
270
  - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
271
- - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
271
+ - `Intl.DateTimeFormat` and `Date.prototype.toLocaleString`,
272
+ `.toLocaleDateString`, and `.toLocaleTimeString`
272
273
  - `Intl.PluralRules`
273
274
  - `Intl.ListFormat`
274
275
 
@@ -341,28 +342,20 @@ editor), but there's a brief summary:
341
342
  - `scriptUrl?: string` — The full SiteImprove analytics script URL.
342
343
  (alternative to `accountId` prop).
343
344
  - `hasConsented?: boolean` — Manual GDPR 'analytics' consent flag. Allows hard
344
- opt-out, but defers to
345
- [`CookieHubProvider` values](./README.md#usecookiehubconsent) if they are
346
- available.
345
+ opt-out, but defers to [`CookieHubProvider` values](#usecookiehubconsent) if
346
+ they are available.
347
347
  - `onLoad?: (e: unknown) => void` — Fires when the script has loaded.
348
348
  - `onError?: (e: unknown) => void` — Fires if loading the script failed.
349
349
 
350
350
  Example usage somewhere in your application:
351
351
 
352
- ```js
352
+ ```jsz
353
353
  import { SiteImprove } from '@reykjavik/webtools/SiteImprove';
354
354
 
355
- // ideally emit this from your loader function
356
355
  const siteImproveAccountId = '[ACCOUNT_ID]'; // e.g. "7654321"
357
356
 
358
- const location = useRouter()
359
- // ...then Inside root.tsx component:
360
- <SiteImprove
361
- accountId={siteImproveAccountId}
362
- onError={(error) =>
363
- Logger('error', 'An error occured initializing siteimprove', error)
364
- }
365
- />;
357
+ // ...then inside your main App component
358
+ <SiteImprove accountId={siteImproveAccountId} />;
366
359
  ```
367
360
 
368
361
  In dev mode it does NOT load the SiteImprove script and merely logs page-view
@@ -172,7 +172,7 @@ const partMappers = {
172
172
  },
173
173
  literal: (value, lastType) => {
174
174
  if (value === ' den ') {
175
- return 'inn ';
175
+ return ', ';
176
176
  }
177
177
  else if (value === '.' && (lastType === 'hour' || lastType === 'minute')) {
178
178
  return ':';
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
@@ -177,7 +177,7 @@ const partMappers = {
177
177
  },
178
178
  literal: (value, lastType) => {
179
179
  if (value === ' den ') {
180
- return 'inn ';
180
+ return ', ';
181
181
  }
182
182
  else if (value === '.' && (lastType === 'hour' || lastType === 'minute')) {
183
183
  return ':';
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.24",
3
+ "version": "0.1.26",
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