@reykjavik/webtools 0.2.1 → 0.2.3
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 +19 -1
- package/CookieHubConsent.d.ts +4 -2
- package/CookieHubConsent.js +21 -6
- package/README.md +40 -4
- package/errorhandling.d.ts +2 -0
- package/errorhandling.js +12 -1
- package/esm/CookieHubConsent.d.ts +4 -2
- package/esm/CookieHubConsent.js +21 -6
- package/esm/errorhandling.d.ts +2 -0
- package/esm/errorhandling.js +12 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
- ... <!-- Add new lines here. -->
|
|
6
6
|
|
|
7
|
+
## 0.2.3
|
|
8
|
+
|
|
9
|
+
_2025-03-31_
|
|
10
|
+
|
|
11
|
+
- `@reykjavik/webtools/errorhandling`:
|
|
12
|
+
- feat: Add a `.mapTo(fn)` method to `ResultTupleObj`s
|
|
13
|
+
- fix: `Result.map` does not catch errors thrown by the mapping function
|
|
14
|
+
- docs: Fix minor error in README code example
|
|
15
|
+
|
|
16
|
+
## 0.2.2
|
|
17
|
+
|
|
18
|
+
_2024-12-19_
|
|
19
|
+
|
|
20
|
+
- `@reykjavik/webtools/CookieHubConsent`:
|
|
21
|
+
- feat: Allow passing explicit `undefined` as `accountId` to skip loading
|
|
22
|
+
- fix: Note that `window.cookiehub` is possibly `undefined` (before load)
|
|
23
|
+
|
|
7
24
|
## 0.2.1
|
|
8
25
|
|
|
9
26
|
_2024-12-17_
|
|
10
27
|
|
|
11
|
-
-
|
|
28
|
+
- `@reykjavik/webtools/errorhandling`:
|
|
29
|
+
- fix: Make typing of failed ResultTuple/ResultTupleObj less ambiguous
|
|
12
30
|
|
|
13
31
|
## 0.2.0
|
|
14
32
|
|
package/CookieHubConsent.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';
|
|
|
2
2
|
import { EitherObj } from '@reykjavik/hanna-utils';
|
|
3
3
|
declare global {
|
|
4
4
|
interface Window {
|
|
5
|
-
cookiehub
|
|
5
|
+
cookiehub?: CookieHub;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
type CookieHub = {
|
|
@@ -180,9 +180,11 @@ export type CookieHubProviderProps = EitherObj<{
|
|
|
180
180
|
* extracted from the script embed URL like this:
|
|
181
181
|
* `"https://cookiehub.net/c2/[ACCOUNT_ID].js"`
|
|
182
182
|
*
|
|
183
|
+
* Pass `undefined` to disable/skip the script loading
|
|
184
|
+
*
|
|
183
185
|
* @see https://support.cookiehub.com/article/155-manual-implementation-guide
|
|
184
186
|
*/
|
|
185
|
-
accountId: string;
|
|
187
|
+
accountId: string | undefined;
|
|
186
188
|
}, {
|
|
187
189
|
/**
|
|
188
190
|
* The full CookieHub embed script URL.
|
package/CookieHubConsent.js
CHANGED
|
@@ -65,14 +65,26 @@ const moveCookiehubScriptInDomTree = () => {
|
|
|
65
65
|
*/
|
|
66
66
|
const CookieHubProvider = (props) => {
|
|
67
67
|
const [state, setState] = (0, react_1.useState)(initialConsentState);
|
|
68
|
+
const scriptUrLOrId = props.scriptUrl
|
|
69
|
+
? `@ ${props.scriptUrl}`
|
|
70
|
+
: props.accountId || undefined;
|
|
68
71
|
(0, react_1.useEffect)(() => {
|
|
72
|
+
if (!scriptUrLOrId) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const scriptSrc = scriptUrLOrId.startsWith('@ ')
|
|
76
|
+
? scriptUrLOrId.slice(2)
|
|
77
|
+
: scriptUrlTemplate.replace(idToken, scriptUrLOrId);
|
|
78
|
+
if (window.cookiehub && document.querySelector('script#cookiehub-script')) {
|
|
79
|
+
// We can't load the script from more than one source at a time
|
|
80
|
+
console.warn('CookieHub script already loaded.');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const opts = props.options || {};
|
|
69
84
|
const script = document.createElement('script');
|
|
70
85
|
script.async = true;
|
|
71
|
-
|
|
72
|
-
script.src =
|
|
73
|
-
props.scriptUrl != null
|
|
74
|
-
? props.scriptUrl
|
|
75
|
-
: scriptUrlTemplate.replace(idToken, props.accountId);
|
|
86
|
+
script.id = 'cookiehub-script';
|
|
87
|
+
script.src = scriptSrc;
|
|
76
88
|
script.onload = () => {
|
|
77
89
|
window.cookiehub.load({
|
|
78
90
|
...opts,
|
|
@@ -124,8 +136,11 @@ const CookieHubProvider = (props) => {
|
|
|
124
136
|
props.onError && (script.onerror = props.onError);
|
|
125
137
|
document.body.append(script);
|
|
126
138
|
},
|
|
139
|
+
// Unless we can find a safe way to tear down the CookieHub script and
|
|
140
|
+
// clean up after it we can only load it once, so monitoring anything other
|
|
141
|
+
// than accountId or scriptUrl is pointless.
|
|
127
142
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
128
|
-
[]);
|
|
143
|
+
[scriptUrLOrId]);
|
|
129
144
|
return (react_1.default.createElement(CookieHubContext.Provider, { value: state }, props.children));
|
|
130
145
|
};
|
|
131
146
|
exports.CookieHubProvider = CookieHubProvider;
|
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ bun add @reykjavik/webtools
|
|
|
34
34
|
- [`Result` Singleton](#result-singleton)
|
|
35
35
|
- [Type `ResultTuple`](#type-resulttuple)
|
|
36
36
|
- [Type `ResultTupleObj`](#type-resulttupleobj)
|
|
37
|
+
- [Type `ResultTupleObj.mapTo`](#type-resulttupleobjmapto)
|
|
37
38
|
- [`Result.catch`](#resultcatch)
|
|
38
39
|
- [`Result.map`](#resultmap)
|
|
39
40
|
- [`Result.Success`](#resultsuccess)
|
|
@@ -456,10 +457,12 @@ if (error) {
|
|
|
456
457
|
Discriminated tuple type for a `[error, result]` pair (same as `ResultTuple`)
|
|
457
458
|
but with named properties `error` and `result` attached for dev convenience.
|
|
458
459
|
|
|
460
|
+
It also has a `.mapTo` method ([see below](#type-resulttupleobjmapto)).
|
|
461
|
+
|
|
459
462
|
```ts
|
|
460
|
-
import { type
|
|
463
|
+
import { type ResultTupleObj } from '@reykjavik/webtools/errorhandling';
|
|
461
464
|
|
|
462
|
-
declare const myResult:
|
|
465
|
+
declare const myResult: ResultTupleObj<string, Error>;
|
|
463
466
|
|
|
464
467
|
const [error, result] = myResult;
|
|
465
468
|
// (One of these two is always `undefined`)
|
|
@@ -482,6 +485,39 @@ if (myResult.error) {
|
|
|
482
485
|
}
|
|
483
486
|
```
|
|
484
487
|
|
|
488
|
+
#### Type `ResultTupleObj.mapTo`
|
|
489
|
+
|
|
490
|
+
**Syntax:**
|
|
491
|
+
`ResultTupleObj.mapTo<T2, E>(mapResult: (resultValue: T) => T2): ResultTuple<T2, E>`
|
|
492
|
+
|
|
493
|
+
This convenience method allows quick mapping of the `ResultTubleOBj`'s result
|
|
494
|
+
value to a new type. The returned value is also a `ResultTubleOBj`.
|
|
495
|
+
|
|
496
|
+
(Internally this method calls [`Result.map`](#resultmap).)
|
|
497
|
+
|
|
498
|
+
```ts
|
|
499
|
+
import { type ResultTuple } from '@reykjavik/webtools/errorhandling';
|
|
500
|
+
|
|
501
|
+
declare const myResult: ResultTuple<string, Error>;
|
|
502
|
+
|
|
503
|
+
const mappedResult: ResultTupleObj<number, Error> = myResult.mapTo(
|
|
504
|
+
(result: string) => result.length
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
if (mappedRes.error) {
|
|
508
|
+
console.error(myResult.error.message);
|
|
509
|
+
} else {
|
|
510
|
+
// Here `myResult.result` is a number
|
|
511
|
+
console.log(myResult.result);
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
If the original `ResultTupleObj` is in a failed state, the mapping function is
|
|
516
|
+
not called.
|
|
517
|
+
|
|
518
|
+
If the mapping function throws an error it gets caught and turned into a
|
|
519
|
+
failed `ResultTupleObj`.
|
|
520
|
+
|
|
485
521
|
### `Result.catch`
|
|
486
522
|
|
|
487
523
|
**Syntax:**
|
|
@@ -723,8 +759,8 @@ export default function App() {
|
|
|
723
759
|
The Component's props have detailed JSDoc comments (displayed in your code
|
|
724
760
|
editor), but there's a brief summary:
|
|
725
761
|
|
|
726
|
-
- `accountId?: string` — Your CookieHub account ID. (alternative
|
|
727
|
-
`scriptUrl` prop).
|
|
762
|
+
- `accountId?: string | undefined` — Your CookieHub account ID. (alternative
|
|
763
|
+
to `scriptUrl` prop). Pass `undefined` to skip loading the script.
|
|
728
764
|
- `scriptUrl?: string` — The full CookieHub embed script URL. (alternative to
|
|
729
765
|
`accountId` prop).
|
|
730
766
|
- `options?: CookieHubOptions` — Raw CookieHub options object that gets used
|
package/errorhandling.d.ts
CHANGED
|
@@ -23,10 +23,12 @@ export declare const asError: (maybeError: unknown) => ErrorFromPayload;
|
|
|
23
23
|
type SuccessResult<T> = [error: undefined, result: T] & {
|
|
24
24
|
error?: undefined;
|
|
25
25
|
result: T;
|
|
26
|
+
mapTo: <T2, E extends Error = Error>(fn: (result: T) => T2) => ResultTupleObj<T2, E>;
|
|
26
27
|
};
|
|
27
28
|
type FailResult<E extends Error> = [error: E, result?: undefined] & {
|
|
28
29
|
error: E;
|
|
29
30
|
result?: undefined;
|
|
31
|
+
mapTo: () => FailResult<E>;
|
|
30
32
|
};
|
|
31
33
|
/**
|
|
32
34
|
* Simple bare-bones discriminated tuple type for a [error, result] pair.
|
package/errorhandling.js
CHANGED
|
@@ -39,11 +39,15 @@ exports.asError = asError;
|
|
|
39
39
|
const Success = (result) => {
|
|
40
40
|
const tuple = [undefined, result];
|
|
41
41
|
tuple.result = result;
|
|
42
|
+
tuple.mapTo = (fn) =>
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
44
|
+
map(tuple, fn);
|
|
42
45
|
return tuple;
|
|
43
46
|
};
|
|
44
47
|
const Fail = (e) => {
|
|
45
48
|
const tuple = [(0, exports.asError)(e)];
|
|
46
49
|
tuple.error = tuple[0];
|
|
50
|
+
tuple.mapTo = () => tuple;
|
|
47
51
|
return tuple;
|
|
48
52
|
};
|
|
49
53
|
function catch_(something) {
|
|
@@ -57,6 +61,13 @@ function catch_(something) {
|
|
|
57
61
|
return Fail(e);
|
|
58
62
|
}
|
|
59
63
|
}
|
|
64
|
+
const map = (result, mapFn) => {
|
|
65
|
+
const [error, resultValue] = result;
|
|
66
|
+
if (error) {
|
|
67
|
+
return Fail(error);
|
|
68
|
+
}
|
|
69
|
+
return catch_(() => mapFn(resultValue));
|
|
70
|
+
};
|
|
60
71
|
/**
|
|
61
72
|
* Singleton object with small methods for creating, mapping or handling
|
|
62
73
|
* `ResultTupleObj` instances.
|
|
@@ -90,7 +101,7 @@ exports.Result = {
|
|
|
90
101
|
if (error) {
|
|
91
102
|
return Fail(error);
|
|
92
103
|
}
|
|
93
|
-
return
|
|
104
|
+
return catch_(() => mapFn(resultValue));
|
|
94
105
|
},
|
|
95
106
|
/**
|
|
96
107
|
* Unwraps a discriminated [error, result] `Result.Tuple`-like object
|
|
@@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';
|
|
|
2
2
|
import { EitherObj } from '@reykjavik/hanna-utils';
|
|
3
3
|
declare global {
|
|
4
4
|
interface Window {
|
|
5
|
-
cookiehub
|
|
5
|
+
cookiehub?: CookieHub;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
type CookieHub = {
|
|
@@ -180,9 +180,11 @@ export type CookieHubProviderProps = EitherObj<{
|
|
|
180
180
|
* extracted from the script embed URL like this:
|
|
181
181
|
* `"https://cookiehub.net/c2/[ACCOUNT_ID].js"`
|
|
182
182
|
*
|
|
183
|
+
* Pass `undefined` to disable/skip the script loading
|
|
184
|
+
*
|
|
183
185
|
* @see https://support.cookiehub.com/article/155-manual-implementation-guide
|
|
184
186
|
*/
|
|
185
|
-
accountId: string;
|
|
187
|
+
accountId: string | undefined;
|
|
186
188
|
}, {
|
|
187
189
|
/**
|
|
188
190
|
* The full CookieHub embed script URL.
|
package/esm/CookieHubConsent.js
CHANGED
|
@@ -39,14 +39,26 @@ const moveCookiehubScriptInDomTree = () => {
|
|
|
39
39
|
*/
|
|
40
40
|
export const CookieHubProvider = (props) => {
|
|
41
41
|
const [state, setState] = useState(initialConsentState);
|
|
42
|
+
const scriptUrLOrId = props.scriptUrl
|
|
43
|
+
? `@ ${props.scriptUrl}`
|
|
44
|
+
: props.accountId || undefined;
|
|
42
45
|
useEffect(() => {
|
|
46
|
+
if (!scriptUrLOrId) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const scriptSrc = scriptUrLOrId.startsWith('@ ')
|
|
50
|
+
? scriptUrLOrId.slice(2)
|
|
51
|
+
: scriptUrlTemplate.replace(idToken, scriptUrLOrId);
|
|
52
|
+
if (window.cookiehub && document.querySelector('script#cookiehub-script')) {
|
|
53
|
+
// We can't load the script from more than one source at a time
|
|
54
|
+
console.warn('CookieHub script already loaded.');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const opts = props.options || {};
|
|
43
58
|
const script = document.createElement('script');
|
|
44
59
|
script.async = true;
|
|
45
|
-
|
|
46
|
-
script.src =
|
|
47
|
-
props.scriptUrl != null
|
|
48
|
-
? props.scriptUrl
|
|
49
|
-
: scriptUrlTemplate.replace(idToken, props.accountId);
|
|
60
|
+
script.id = 'cookiehub-script';
|
|
61
|
+
script.src = scriptSrc;
|
|
50
62
|
script.onload = () => {
|
|
51
63
|
window.cookiehub.load({
|
|
52
64
|
...opts,
|
|
@@ -98,8 +110,11 @@ export const CookieHubProvider = (props) => {
|
|
|
98
110
|
props.onError && (script.onerror = props.onError);
|
|
99
111
|
document.body.append(script);
|
|
100
112
|
},
|
|
113
|
+
// Unless we can find a safe way to tear down the CookieHub script and
|
|
114
|
+
// clean up after it we can only load it once, so monitoring anything other
|
|
115
|
+
// than accountId or scriptUrl is pointless.
|
|
101
116
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
102
|
-
[]);
|
|
117
|
+
[scriptUrLOrId]);
|
|
103
118
|
return (React.createElement(CookieHubContext.Provider, { value: state }, props.children));
|
|
104
119
|
};
|
|
105
120
|
// ---------------------------------------------------------------------------
|
package/esm/errorhandling.d.ts
CHANGED
|
@@ -23,10 +23,12 @@ export declare const asError: (maybeError: unknown) => ErrorFromPayload;
|
|
|
23
23
|
type SuccessResult<T> = [error: undefined, result: T] & {
|
|
24
24
|
error?: undefined;
|
|
25
25
|
result: T;
|
|
26
|
+
mapTo: <T2, E extends Error = Error>(fn: (result: T) => T2) => ResultTupleObj<T2, E>;
|
|
26
27
|
};
|
|
27
28
|
type FailResult<E extends Error> = [error: E, result?: undefined] & {
|
|
28
29
|
error: E;
|
|
29
30
|
result?: undefined;
|
|
31
|
+
mapTo: () => FailResult<E>;
|
|
30
32
|
};
|
|
31
33
|
/**
|
|
32
34
|
* Simple bare-bones discriminated tuple type for a [error, result] pair.
|
package/esm/errorhandling.js
CHANGED
|
@@ -34,11 +34,15 @@ export const asError = (maybeError) => {
|
|
|
34
34
|
const Success = (result) => {
|
|
35
35
|
const tuple = [undefined, result];
|
|
36
36
|
tuple.result = result;
|
|
37
|
+
tuple.mapTo = (fn) =>
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
39
|
+
map(tuple, fn);
|
|
37
40
|
return tuple;
|
|
38
41
|
};
|
|
39
42
|
const Fail = (e) => {
|
|
40
43
|
const tuple = [asError(e)];
|
|
41
44
|
tuple.error = tuple[0];
|
|
45
|
+
tuple.mapTo = () => tuple;
|
|
42
46
|
return tuple;
|
|
43
47
|
};
|
|
44
48
|
function catch_(something) {
|
|
@@ -52,6 +56,13 @@ function catch_(something) {
|
|
|
52
56
|
return Fail(e);
|
|
53
57
|
}
|
|
54
58
|
}
|
|
59
|
+
const map = (result, mapFn) => {
|
|
60
|
+
const [error, resultValue] = result;
|
|
61
|
+
if (error) {
|
|
62
|
+
return Fail(error);
|
|
63
|
+
}
|
|
64
|
+
return catch_(() => mapFn(resultValue));
|
|
65
|
+
};
|
|
55
66
|
/**
|
|
56
67
|
* Singleton object with small methods for creating, mapping or handling
|
|
57
68
|
* `ResultTupleObj` instances.
|
|
@@ -85,7 +96,7 @@ export const Result = {
|
|
|
85
96
|
if (error) {
|
|
86
97
|
return Fail(error);
|
|
87
98
|
}
|
|
88
|
-
return
|
|
99
|
+
return catch_(() => mapFn(resultValue));
|
|
89
100
|
},
|
|
90
101
|
/**
|
|
91
102
|
* Unwraps a discriminated [error, result] `Result.Tuple`-like object
|
package/package.json
CHANGED