@reykjavik/webtools 0.3.14 → 0.3.16

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,14 +4,23 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
- ## 0.3.14
7
+ ## 0.3.16
8
8
 
9
- _2026-03-17_
9
+ _2026-03-18_
10
+
11
+ - `@reykjavik/webtools/alertsStore`:
12
+ - feat: console.warn instead of throwing when recreating an extisting store
13
+ — return old store instead of creating a new one.
14
+
15
+ ## 0.3.15
16
+
17
+ _2026-03-18_
10
18
 
11
19
  - `@reykjavik/webtools/alertsStore`:
12
- - fix: Regression in `InferAlerterPayload` type
20
+ - feat: Support `defaultDuration` object w durations for each alert `level`
21
+ - feat: Bump the default `defaultDuration` to `'MEDIUM'` (more reasonable)
13
22
 
14
- ## 0.3.12 – 0.3.13
23
+ ## 0.3.12 – 0.3.14
15
24
 
16
25
  _2026-03-17_
17
26
 
package/README.md CHANGED
@@ -1129,10 +1129,12 @@ The configuration options are as follows:
1129
1129
  Default:
1130
1130
  `{ BLINK: 2_000, SHORT: 4_000, MEDIUM: 8_000, LONG: 16_000, XLONG: 32_000, INDEFINITE: 0 }`.
1131
1131
 
1132
- - **`defaultDuration?: string`**
1132
+ - **`defaultDuration?: string | Record<Level, Duration>`**
1133
1133
  The duration to use for alerts if no duration is specified when
1134
1134
  dispatching.
1135
- Default: `SHORT` if using the default durations, otherwise the default is
1135
+ You can also pass an object with different default durations for each alert
1136
+ level, e.g. longer defaults for "errors" than "success" alerts.
1137
+ Default: `MEDIUM` if using the default durations, otherwise the default is
1136
1138
  `0` (indefinite)
1137
1139
 
1138
1140
  - **`storage?: Pick<Storage, 'getItem' | 'setItem'>`**
@@ -85,10 +85,13 @@ export type AlerterConfig<Level extends string = (typeof defaultAlertLevels)[num
85
85
  * Default duration to use for alerts if no duration is specified when
86
86
  * dispatching.
87
87
  *
88
- * Default: `SHORT` if using the default durations, otherwise the default
88
+ * You can also pass an object with different default durations for each
89
+ * alert level, e.g. longer defaults for "errors" than "success" alerts.
90
+ *
91
+ * Default: `MEDIUM` if using the default durations, otherwise the default
89
92
  * is `0` (indefinite)
90
93
  */
91
- defaultDuration?: Durations extends Record<infer D, number> ? D : never;
94
+ defaultDuration?: (Durations extends Record<infer D, number> ? (D extends string ? D : never) : never) | Record<Level, Durations extends Record<infer D, number> ? (D extends string ? D : never) : never>;
92
95
  /**
93
96
  * Whether to allow an optional `title` property on alerts.
94
97
  *
@@ -111,16 +114,6 @@ export type AlerterConfig<Level extends string = (typeof defaultAlertLevels)[num
111
114
  * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
112
115
  */
113
116
  export declare const createAlerterStore: <Level extends string = (typeof defaultAlertLevels)[number], Type extends string = string, Flag extends string = string, Title extends boolean = false, Duration extends string = keyof typeof defaultDurations>(cfg?: AlerterConfig<Level, Type, Flag, Title, Duration>) => {
114
- /**
115
- * Singleton object with methods for showing alerts of different levels.
116
- * Pass a payload object to the method of the level you want to dispatch,
117
- * and the alert will be added to the store.
118
- *
119
- * Use `subscribeToAlerts` elsewhere in the app to subscribe to alert
120
- * notifications and display them.
121
- *
122
- * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
123
- */
124
117
  alerter: Record<Level, (payload: AlertMessage | ({
125
118
  /**
126
119
  * A simple string containing the alert message.
@@ -164,18 +157,6 @@ export declare const createAlerterStore: <Level extends string = (typeof default
164
157
  */
165
158
  type?: Type;
166
159
  }))) => void>;
167
- /**
168
- * Subscribes to alert events. The provided callback will be called whenever a
169
- * alert is added or cleared.
170
- *
171
- * The callback is called immediately upon subscription if there are already
172
- * active alerts.
173
- *
174
- * Returns an unsubscribe function that can be called to stop receiving alert
175
- * events.
176
- *
177
- * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
178
- */
179
160
  subscribe: (callback: (notifications: Array<{
180
161
  level: Level;
181
162
  message: AlertMessage;
@@ -62,7 +62,7 @@ const messageSchema = v.union([
62
62
  ])),
63
63
  ]);
64
64
  // ---------------------------------------------------------------------------
65
- const defaultKey = 'app~alerts';
65
+ const DEFAULT_KEY = 'app~alerts';
66
66
  const defaultAlertLevels = ['info', 'warning', 'success', 'error'];
67
67
  const defaultDurations = {
68
68
  BLINK: 2000,
@@ -72,8 +72,8 @@ const defaultDurations = {
72
72
  XLONG: 32000,
73
73
  INDEFINITE: 0,
74
74
  };
75
- const defaultDefaultDuration = 'SHORT';
76
- const storeKeys = {};
75
+ const DEFAULT_DEFAULT_DURATION = 'MEDIUM';
76
+ const storeStore = {};
77
77
  /**
78
78
  * Factory function that creates an alerter store singleton with optional
79
79
  * configuration for the genarated alerts.
@@ -81,18 +81,27 @@ const storeKeys = {};
81
81
  * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
82
82
  */
83
83
  /*#__NO_SIDE_EFFECTS__*/
84
+ // eslint-disable-next-line complexity
84
85
  const createAlerterStore = (cfg = {}) => {
85
- const STORE_KEY = cfg.key || defaultKey;
86
- if (storeKeys[STORE_KEY]) {
87
- throw new Error(`An alerter store with key "${STORE_KEY}" already exists.`);
86
+ const STORE_KEY = cfg.key || DEFAULT_KEY;
87
+ if (storeStore[STORE_KEY]) {
88
+ process.env.NODE_ENV !== 'production' &&
89
+ console.warn(`An alerter store with key "${STORE_KEY}" already exists.\n` +
90
+ `Returning the existing store, which may have been configured differently.\n` +
91
+ `Make sure to use unique keys if you want multiple independent stores.`);
92
+ return storeStore[STORE_KEY];
88
93
  }
89
- storeKeys[STORE_KEY] = true;
90
94
  const storgae = cfg.storage || (typeof sessionStorage !== 'undefined' ? sessionStorage : undefined);
91
95
  const alertLevels = cfg.levels || defaultAlertLevels;
92
96
  const durations = cfg.durations || defaultDurations;
93
- const DEFAULT_DURATION = cfg.durations
97
+ const defaultDurationsByLevel = cfg.defaultDuration && typeof cfg.defaultDuration !== 'string'
94
98
  ? cfg.defaultDuration
95
- : defaultDefaultDuration;
99
+ : undefined;
100
+ const defaultDuration = !cfg.durations
101
+ ? DEFAULT_DEFAULT_DURATION
102
+ : typeof cfg.defaultDuration === 'string'
103
+ ? cfg.defaultDuration
104
+ : undefined;
96
105
  const _notificationSchema = v.object({
97
106
  level: v.picklist(alertLevels),
98
107
  title: cfg.title ? v.optional(v.string()) : v.never(),
@@ -177,7 +186,10 @@ const createAlerterStore = (cfg = {}) => {
177
186
  const buildNotification = (_payload, level) => {
178
187
  // Strip away duration and delay (not part of the notification object)
179
188
  const { duration, delay, ...payload } = _payload;
180
- const durationMs = durations[(duration || DEFAULT_DURATION || '')] || 0;
189
+ const durationMs = durations[duration ||
190
+ (defaultDurationsByLevel &&
191
+ defaultDurationsByLevel[level]) ||
192
+ (defaultDuration || '')];
181
193
  return {
182
194
  ...payload,
183
195
  level,
@@ -295,7 +307,7 @@ const createAlerterStore = (cfg = {}) => {
295
307
  // Save the (possibly) cleaned up alerts back to the storage, because why not. :-D
296
308
  _saveAlertsToStorage();
297
309
  })();
298
- return {
310
+ return (storeStore[STORE_KEY] = {
299
311
  /**
300
312
  * Singleton object with methods for showing alerts of different levels.
301
313
  * Pass a payload object to the method of the level you want to dispatch,
@@ -320,6 +332,6 @@ const createAlerterStore = (cfg = {}) => {
320
332
  * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
321
333
  */
322
334
  subscribe,
323
- };
335
+ });
324
336
  };
325
337
  exports.createAlerterStore = createAlerterStore;
@@ -85,10 +85,13 @@ export type AlerterConfig<Level extends string = (typeof defaultAlertLevels)[num
85
85
  * Default duration to use for alerts if no duration is specified when
86
86
  * dispatching.
87
87
  *
88
- * Default: `SHORT` if using the default durations, otherwise the default
88
+ * You can also pass an object with different default durations for each
89
+ * alert level, e.g. longer defaults for "errors" than "success" alerts.
90
+ *
91
+ * Default: `MEDIUM` if using the default durations, otherwise the default
89
92
  * is `0` (indefinite)
90
93
  */
91
- defaultDuration?: Durations extends Record<infer D, number> ? D : never;
94
+ defaultDuration?: (Durations extends Record<infer D, number> ? (D extends string ? D : never) : never) | Record<Level, Durations extends Record<infer D, number> ? (D extends string ? D : never) : never>;
92
95
  /**
93
96
  * Whether to allow an optional `title` property on alerts.
94
97
  *
@@ -111,16 +114,6 @@ export type AlerterConfig<Level extends string = (typeof defaultAlertLevels)[num
111
114
  * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
112
115
  */
113
116
  export declare const createAlerterStore: <Level extends string = (typeof defaultAlertLevels)[number], Type extends string = string, Flag extends string = string, Title extends boolean = false, Duration extends string = keyof typeof defaultDurations>(cfg?: AlerterConfig<Level, Type, Flag, Title, Duration>) => {
114
- /**
115
- * Singleton object with methods for showing alerts of different levels.
116
- * Pass a payload object to the method of the level you want to dispatch,
117
- * and the alert will be added to the store.
118
- *
119
- * Use `subscribeToAlerts` elsewhere in the app to subscribe to alert
120
- * notifications and display them.
121
- *
122
- * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
123
- */
124
117
  alerter: Record<Level, (payload: AlertMessage | ({
125
118
  /**
126
119
  * A simple string containing the alert message.
@@ -164,18 +157,6 @@ export declare const createAlerterStore: <Level extends string = (typeof default
164
157
  */
165
158
  type?: Type;
166
159
  }))) => void>;
167
- /**
168
- * Subscribes to alert events. The provided callback will be called whenever a
169
- * alert is added or cleared.
170
- *
171
- * The callback is called immediately upon subscription if there are already
172
- * active alerts.
173
- *
174
- * Returns an unsubscribe function that can be called to stop receiving alert
175
- * events.
176
- *
177
- * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
178
- */
179
160
  subscribe: (callback: (notifications: Array<{
180
161
  level: Level;
181
162
  message: AlertMessage;
@@ -26,7 +26,7 @@ const messageSchema = v.union([
26
26
  ])),
27
27
  ]);
28
28
  // ---------------------------------------------------------------------------
29
- const defaultKey = 'app~alerts';
29
+ const DEFAULT_KEY = 'app~alerts';
30
30
  const defaultAlertLevels = ['info', 'warning', 'success', 'error'];
31
31
  const defaultDurations = {
32
32
  BLINK: 2000,
@@ -36,8 +36,8 @@ const defaultDurations = {
36
36
  XLONG: 32000,
37
37
  INDEFINITE: 0,
38
38
  };
39
- const defaultDefaultDuration = 'SHORT';
40
- const storeKeys = {};
39
+ const DEFAULT_DEFAULT_DURATION = 'MEDIUM';
40
+ const storeStore = {};
41
41
  /**
42
42
  * Factory function that creates an alerter store singleton with optional
43
43
  * configuration for the genarated alerts.
@@ -45,18 +45,27 @@ const storeKeys = {};
45
45
  * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
46
46
  */
47
47
  /*#__NO_SIDE_EFFECTS__*/
48
+ // eslint-disable-next-line complexity
48
49
  export const createAlerterStore = (cfg = {}) => {
49
- const STORE_KEY = cfg.key || defaultKey;
50
- if (storeKeys[STORE_KEY]) {
51
- throw new Error(`An alerter store with key "${STORE_KEY}" already exists.`);
50
+ const STORE_KEY = cfg.key || DEFAULT_KEY;
51
+ if (storeStore[STORE_KEY]) {
52
+ process.env.NODE_ENV !== 'production' &&
53
+ console.warn(`An alerter store with key "${STORE_KEY}" already exists.\n` +
54
+ `Returning the existing store, which may have been configured differently.\n` +
55
+ `Make sure to use unique keys if you want multiple independent stores.`);
56
+ return storeStore[STORE_KEY];
52
57
  }
53
- storeKeys[STORE_KEY] = true;
54
58
  const storgae = cfg.storage || (typeof sessionStorage !== 'undefined' ? sessionStorage : undefined);
55
59
  const alertLevels = cfg.levels || defaultAlertLevels;
56
60
  const durations = cfg.durations || defaultDurations;
57
- const DEFAULT_DURATION = cfg.durations
61
+ const defaultDurationsByLevel = cfg.defaultDuration && typeof cfg.defaultDuration !== 'string'
58
62
  ? cfg.defaultDuration
59
- : defaultDefaultDuration;
63
+ : undefined;
64
+ const defaultDuration = !cfg.durations
65
+ ? DEFAULT_DEFAULT_DURATION
66
+ : typeof cfg.defaultDuration === 'string'
67
+ ? cfg.defaultDuration
68
+ : undefined;
60
69
  const _notificationSchema = v.object({
61
70
  level: v.picklist(alertLevels),
62
71
  title: cfg.title ? v.optional(v.string()) : v.never(),
@@ -141,7 +150,10 @@ export const createAlerterStore = (cfg = {}) => {
141
150
  const buildNotification = (_payload, level) => {
142
151
  // Strip away duration and delay (not part of the notification object)
143
152
  const { duration, delay, ...payload } = _payload;
144
- const durationMs = durations[(duration || DEFAULT_DURATION || '')] || 0;
153
+ const durationMs = durations[duration ||
154
+ (defaultDurationsByLevel &&
155
+ defaultDurationsByLevel[level]) ||
156
+ (defaultDuration || '')];
145
157
  return {
146
158
  ...payload,
147
159
  level,
@@ -259,7 +271,7 @@ export const createAlerterStore = (cfg = {}) => {
259
271
  // Save the (possibly) cleaned up alerts back to the storage, because why not. :-D
260
272
  _saveAlertsToStorage();
261
273
  })();
262
- return {
274
+ return (storeStore[STORE_KEY] = {
263
275
  /**
264
276
  * Singleton object with methods for showing alerts of different levels.
265
277
  * Pass a payload object to the method of the level you want to dispatch,
@@ -284,5 +296,5 @@ export const createAlerterStore = (cfg = {}) => {
284
296
  * @see https://github.com/reykjavikcity/webtools/blob/v0.3/README.md#createalerterstore
285
297
  */
286
298
  subscribe,
287
- };
299
+ });
288
300
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/webtools",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
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",