@dereekb/browser 13.2.2 → 13.3.1

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/index.cjs.js CHANGED
@@ -6,13 +6,11 @@ var rxjs = require('rxjs');
6
6
 
7
7
  /**
8
8
  * Creatse a new BrowserObjectURLRef.
9
- */
10
- function browserObjectUrlRef() {
11
- let browserUrl;
9
+ */ function browserObjectUrlRef() {
10
+ var browserUrl;
12
11
  /**
13
12
  * Revokes the existing browser object URL, if one exists.
14
- */
15
- function destroy() {
13
+ */ function destroy() {
16
14
  if (browserUrl) {
17
15
  URL.revokeObjectURL(browserUrl);
18
16
  }
@@ -25,128 +23,386 @@ function browserObjectUrlRef() {
25
23
  return browserUrl;
26
24
  }
27
25
  return {
28
- destroy,
29
- getBrowserUrl: () => browserUrl,
26
+ destroy: destroy,
27
+ getBrowserUrl: function getBrowserUrl() {
28
+ return browserUrl;
29
+ },
30
30
  createBrowserUrl: createBrowserUrl
31
31
  };
32
32
  }
33
33
 
34
- /*eslint @typescript-eslint/no-explicit-any:"off"*/
35
- // any is used with intent here. Proper typing with Window requires using the dynamic strings _windowKey and _callbackKey.
34
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
35
+ try {
36
+ var info = gen[key](arg);
37
+ var value = info.value;
38
+ } catch (error) {
39
+ reject(error);
40
+ return;
41
+ }
42
+ if (info.done) {
43
+ resolve(value);
44
+ } else {
45
+ Promise.resolve(value).then(_next, _throw);
46
+ }
47
+ }
48
+ function _async_to_generator(fn) {
49
+ return function() {
50
+ var self = this, args = arguments;
51
+ return new Promise(function(resolve, reject) {
52
+ var gen = fn.apply(self, args);
53
+ function _next(value) {
54
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
55
+ }
56
+ function _throw(err) {
57
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
58
+ }
59
+ _next(undefined);
60
+ });
61
+ };
62
+ }
63
+ function _class_call_check(instance, Constructor) {
64
+ if (!(instance instanceof Constructor)) {
65
+ throw new TypeError("Cannot call a class as a function");
66
+ }
67
+ }
68
+ function _defineProperties(target, props) {
69
+ for(var i = 0; i < props.length; i++){
70
+ var descriptor = props[i];
71
+ descriptor.enumerable = descriptor.enumerable || false;
72
+ descriptor.configurable = true;
73
+ if ("value" in descriptor) descriptor.writable = true;
74
+ Object.defineProperty(target, descriptor.key, descriptor);
75
+ }
76
+ }
77
+ function _create_class(Constructor, protoProps, staticProps) {
78
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
79
+ return Constructor;
80
+ }
81
+ function _define_property(obj, key, value) {
82
+ if (key in obj) {
83
+ Object.defineProperty(obj, key, {
84
+ value: value,
85
+ enumerable: true,
86
+ configurable: true,
87
+ writable: true
88
+ });
89
+ } else {
90
+ obj[key] = value;
91
+ }
92
+ return obj;
93
+ }
94
+ function _ts_generator(thisArg, body) {
95
+ var f, y, t, _ = {
96
+ label: 0,
97
+ sent: function() {
98
+ if (t[0] & 1) throw t[1];
99
+ return t[1];
100
+ },
101
+ trys: [],
102
+ ops: []
103
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
104
+ return d(g, "next", {
105
+ value: verb(0)
106
+ }), d(g, "throw", {
107
+ value: verb(1)
108
+ }), d(g, "return", {
109
+ value: verb(2)
110
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
111
+ value: function() {
112
+ return this;
113
+ }
114
+ }), g;
115
+ function verb(n) {
116
+ return function(v) {
117
+ return step([
118
+ n,
119
+ v
120
+ ]);
121
+ };
122
+ }
123
+ function step(op) {
124
+ if (f) throw new TypeError("Generator is already executing.");
125
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
126
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
127
+ if (y = 0, t) op = [
128
+ op[0] & 2,
129
+ t.value
130
+ ];
131
+ switch(op[0]){
132
+ case 0:
133
+ case 1:
134
+ t = op;
135
+ break;
136
+ case 4:
137
+ _.label++;
138
+ return {
139
+ value: op[1],
140
+ done: false
141
+ };
142
+ case 5:
143
+ _.label++;
144
+ y = op[1];
145
+ op = [
146
+ 0
147
+ ];
148
+ continue;
149
+ case 7:
150
+ op = _.ops.pop();
151
+ _.trys.pop();
152
+ continue;
153
+ default:
154
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
155
+ _ = 0;
156
+ continue;
157
+ }
158
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
159
+ _.label = op[1];
160
+ break;
161
+ }
162
+ if (op[0] === 6 && _.label < t[1]) {
163
+ _.label = t[1];
164
+ t = op;
165
+ break;
166
+ }
167
+ if (t && _.label < t[2]) {
168
+ _.label = t[2];
169
+ _.ops.push(op);
170
+ break;
171
+ }
172
+ if (t[2]) _.ops.pop();
173
+ _.trys.pop();
174
+ continue;
175
+ }
176
+ op = body.call(thisArg, _);
177
+ } catch (e) {
178
+ op = [
179
+ 6,
180
+ e
181
+ ];
182
+ y = 0;
183
+ } finally{
184
+ f = t = 0;
185
+ }
186
+ if (op[0] & 5) throw op[1];
187
+ return {
188
+ value: op[0] ? op[1] : void 0,
189
+ done: true
190
+ };
191
+ }
192
+ }
36
193
  /**
37
194
  * Used for loading services in the browser that are imported from other scripts, such as Facebook, Segment, Stripe, etc.
38
- */
39
- class AbstractAsyncWindowLoadedService {
40
- _loading = new rxjs.BehaviorSubject(undefined);
41
- /**
195
+ */ var AbstractAsyncWindowLoadedService = /*#__PURE__*/ function() {
196
+ function AbstractAsyncWindowLoadedService(windowKey, callbackKey, serviceName, preload) {
197
+ var _this = this;
198
+ _class_call_check(this, AbstractAsyncWindowLoadedService);
199
+ _define_property(this, "_loading", new rxjs.BehaviorSubject(undefined));
200
+ /**
42
201
  * Key that is attached to the window for the object that is the service when finished loading.
43
- */
44
- _windowKey;
45
- /**
202
+ */ _define_property(this, "_windowKey", void 0);
203
+ /**
46
204
  * Optional key attached to window that is a function that is executed when the setup is complete.
47
- */
48
- _callbackKey;
49
- /**
205
+ */ _define_property(this, "_callbackKey", void 0);
206
+ /**
50
207
  * Service name used in logging. Defaults to the windowKey.
51
- */
52
- _serviceName;
53
- loading$ = this._loading.pipe(rxjs$1.tapFirst(() => this.loadService()), rxjs$1.filterMaybe(), rxjs.shareReplay(1));
54
- service$ = this.loading$.pipe(rxjs.switchMap((x) => rxjs.from(x)), rxjs.shareReplay(1));
55
- /**
56
- * @param windowKey
57
- * @param callbackKey
58
- */
59
- constructor(windowKey, callbackKey, serviceName, preload) {
208
+ */ _define_property(this, "_serviceName", void 0);
209
+ /**
210
+ * Observable that emits the loading promise. Subscribing triggers the initial load if not already started.
211
+ */ _define_property(this, "loading$", this._loading.pipe(rxjs$1.tapFirst(function() {
212
+ return _this.loadService();
213
+ }), rxjs$1.filterMaybe(), rxjs.shareReplay(1)));
214
+ /**
215
+ * Observable that emits the resolved service instance once loading completes. Replays the last value to new subscribers.
216
+ */ _define_property(this, "service$", this.loading$.pipe(rxjs.switchMap(function(x) {
217
+ return rxjs.from(x);
218
+ }), rxjs.shareReplay(1)));
60
219
  this._windowKey = windowKey;
61
220
  this._callbackKey = callbackKey;
62
- this._serviceName = serviceName ?? windowKey;
221
+ this._serviceName = serviceName !== null && serviceName !== void 0 ? serviceName : windowKey;
63
222
  if (util.stringToBoolean(preload)) {
64
223
  // Begin loading the service immediately.
65
- setTimeout(() => this.loadService().catch());
66
- }
67
- }
68
- destroy() {
69
- this._loading.complete();
70
- }
71
- getService() {
72
- return rxjs.firstValueFrom(this.service$);
73
- }
74
- // MARK: Loading
75
- loadService() {
76
- if (!this._loading.value) {
77
- const loadingPromise = new Promise((resolve, reject) => {
78
- let loadTry = 0;
79
- const rejectWithError = () => {
80
- reject(new Error(`Service "${this._serviceName}" failed loading with windowKey "${this._windowKey}"`));
81
- };
82
- const tryLoad = () => {
83
- const windowRef = window;
84
- // Loaded before the promise.
85
- if (windowRef[this._windowKey]) {
86
- // Not yet finished loading async. Intercept the function.
87
- // console.log('Window key.');
88
- return resolve(this.completeLoadingService());
89
- }
90
- else if (this._callbackKey && windowRef[this._callbackKey]) {
91
- // console.log('Callback key.');
92
- windowRef[this._callbackKey] = () => resolve(this.completeLoadingService());
93
- }
94
- else if (loadTry < 10) {
95
- loadTry += 1;
96
- // console.log('Try reload...');
97
- setTimeout(() => tryLoad(), 1000);
98
- }
99
- else {
100
- const retry = this._onLoadServiceFailure();
101
- if (retry) {
102
- retry.then((x) => resolve(x)).catch(() => rejectWithError());
103
- }
104
- else {
105
- rejectWithError();
106
- }
107
- }
108
- };
109
- tryLoad();
224
+ setTimeout(function() {
225
+ return _this.loadService().catch();
110
226
  });
111
- this._loading.next(loadingPromise);
112
227
  }
113
- return this._loading.value;
114
228
  }
115
- _onLoadServiceFailure() {
116
- // override in parent if needed.
117
- }
118
- async completeLoadingService() {
119
- await this._prepareCompleteLoadingService();
120
- const service = window[this._windowKey];
121
- if (!service) {
122
- throw new Error(`Service "${this._serviceName}" could not complete loading.`);
229
+ _create_class(AbstractAsyncWindowLoadedService, [
230
+ {
231
+ /**
232
+ * Completes the internal loading subject, stopping any pending service resolution.
233
+ */ key: "destroy",
234
+ value: function destroy() {
235
+ this._loading.complete();
236
+ }
237
+ },
238
+ {
239
+ /**
240
+ * Returns a promise that resolves with the loaded service instance.
241
+ *
242
+ * Triggers loading if not already started.
243
+ */ key: "getService",
244
+ value: function getService() {
245
+ return rxjs.firstValueFrom(this.service$);
246
+ }
247
+ },
248
+ {
249
+ // MARK: Loading
250
+ /**
251
+ * Initiates the service loading process by polling the `window` object for the service key.
252
+ *
253
+ * Retries up to 10 times at 1-second intervals before invoking `_onLoadServiceFailure()`.
254
+ * Subsequent calls return the same promise without re-initiating.
255
+ *
256
+ * @returns Promise that resolves with the loaded service
257
+ */ key: "loadService",
258
+ value: function loadService() {
259
+ var _this = this;
260
+ if (!this._loading.value) {
261
+ var loadingPromise = new Promise(function(resolve, reject) {
262
+ var loadTry = 0;
263
+ var rejectWithError = function rejectWithError() {
264
+ reject(new Error('Service "'.concat(_this._serviceName, '" failed loading with windowKey "').concat(_this._windowKey, '"')));
265
+ };
266
+ var tryLoad = function tryLoad1() {
267
+ var windowRef = window;
268
+ // Loaded before the promise.
269
+ if (windowRef[_this._windowKey]) {
270
+ // Not yet finished loading async. Intercept the function.
271
+ // console.log('Window key.');
272
+ return resolve(_this.completeLoadingService());
273
+ } else if (_this._callbackKey && windowRef[_this._callbackKey]) {
274
+ // console.log('Callback key.');
275
+ windowRef[_this._callbackKey] = function() {
276
+ return resolve(_this.completeLoadingService());
277
+ };
278
+ } else if (loadTry < 10) {
279
+ loadTry += 1;
280
+ // console.log('Try reload...');
281
+ setTimeout(function() {
282
+ return tryLoad();
283
+ }, 1000);
284
+ } else {
285
+ var retry = _this._onLoadServiceFailure();
286
+ if (retry) {
287
+ retry.then(function(x) {
288
+ return resolve(x);
289
+ }).catch(function() {
290
+ return rejectWithError();
291
+ });
292
+ } else {
293
+ rejectWithError();
294
+ }
295
+ }
296
+ };
297
+ tryLoad();
298
+ });
299
+ this._loading.next(loadingPromise);
300
+ }
301
+ return this._loading.value;
302
+ }
303
+ },
304
+ {
305
+ /**
306
+ * Hook called when the service fails to load after all retry attempts.
307
+ *
308
+ * Subclasses can override to attempt an alternative loading strategy or return a fallback promise.
309
+ *
310
+ * @returns A promise resolving with the service if recovery succeeds, or `void` to reject
311
+ */ key: "_onLoadServiceFailure",
312
+ value: function _onLoadServiceFailure() {
313
+ // override in parent if needed.
314
+ }
315
+ },
316
+ {
317
+ key: "completeLoadingService",
318
+ value: function completeLoadingService() {
319
+ return _async_to_generator(function() {
320
+ var service, initializedService;
321
+ return _ts_generator(this, function(_state) {
322
+ switch(_state.label){
323
+ case 0:
324
+ return [
325
+ 4,
326
+ this._prepareCompleteLoadingService()
327
+ ];
328
+ case 1:
329
+ _state.sent();
330
+ service = window[this._windowKey];
331
+ if (!service) {
332
+ throw new Error('Service "'.concat(this._serviceName, '" could not complete loading.'));
333
+ }
334
+ return [
335
+ 4,
336
+ this._initService(service)
337
+ ];
338
+ case 2:
339
+ initializedService = _state.sent();
340
+ return [
341
+ 2,
342
+ initializedService !== null && initializedService !== void 0 ? initializedService : service
343
+ ];
344
+ }
345
+ });
346
+ }).call(this);
347
+ }
348
+ },
349
+ {
350
+ /**
351
+ * Hook called before completing the service load. Subclasses can override to perform
352
+ * additional async setup before the service reference is read from `window`.
353
+ */ key: "_prepareCompleteLoadingService",
354
+ value: function _prepareCompleteLoadingService() {
355
+ return Promise.resolve();
356
+ }
357
+ },
358
+ {
359
+ /**
360
+ * Hook called after the service is retrieved from `window` to perform initialization.
361
+ *
362
+ * Subclasses can override to configure or wrap the service before it is emitted to subscribers.
363
+ *
364
+ * @param service - The raw service instance from the window
365
+ * @returns The initialized service, or void to use the original
366
+ */ key: "_initService",
367
+ value: function _initService(service) {
368
+ return Promise.resolve(service);
369
+ }
123
370
  }
124
- // Init the API
125
- const initializedService = await this._initService(service);
126
- return initializedService ?? service;
127
- }
128
- _prepareCompleteLoadingService() {
129
- return Promise.resolve();
130
- }
131
- _initService(service) {
132
- return Promise.resolve(service);
133
- }
134
- }
371
+ ]);
372
+ return AbstractAsyncWindowLoadedService;
373
+ }
374
+ ();
135
375
 
136
376
  // https://dev.to/maciejtrzcinski/100vh-problem-with-ios-safari-3ge9
137
- const DEFAULT_VH100_VARIABLE_NAME = 'vh100';
138
- function refreshVh100Function(cssVariableName = DEFAULT_VH100_VARIABLE_NAME) {
139
- const cssProperty = `--${cssVariableName}`;
140
- return () => {
141
- const doc = document.documentElement;
142
- doc.style.setProperty(cssProperty, `${window.innerHeight}px`);
377
+ /**
378
+ * Default CSS custom property name used to store the viewport height value.
379
+ */ var DEFAULT_VH100_VARIABLE_NAME = 'vh100';
380
+ /**
381
+ * Creates a function that sets a CSS custom property on `document.documentElement` to the current `window.innerHeight` in pixels.
382
+ *
383
+ * This is a workaround for the iOS Safari 100vh bug where `100vh` includes the browser chrome,
384
+ * causing layout overflow. The returned function can be called to refresh the property value.
385
+ *
386
+ * @param cssVariableName - Name of the CSS custom property (without the `--` prefix)
387
+ * @returns A zero-argument function that updates the CSS property to the current inner height
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * const refresh = refreshVh100Function('vh100');
392
+ * refresh(); // sets --vh100 to e.g. "812px"
393
+ * ```
394
+ */ function refreshVh100Function() {
395
+ var cssVariableName = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : DEFAULT_VH100_VARIABLE_NAME;
396
+ var cssProperty = "--".concat(cssVariableName);
397
+ return function() {
398
+ var doc = document.documentElement;
399
+ doc.style.setProperty(cssProperty, "".concat(window.innerHeight, "px"));
143
400
  };
144
401
  }
145
402
  /**
146
403
  * Adds window event listeners to populate the css variable `vh100`, or another input variable name, with the current window height.
147
- */
148
- function watchWindowAndUpdateVh100StyleProperty(cssVariableName) {
149
- const refreshPropertyValue = refreshVh100Function(cssVariableName);
404
+ */ function watchWindowAndUpdateVh100StyleProperty(cssVariableName) {
405
+ var refreshPropertyValue = refreshVh100Function(cssVariableName);
150
406
  window.addEventListener('resize', refreshPropertyValue);
151
407
  window.addEventListener('orientationchange', refreshPropertyValue);
152
408
  refreshPropertyValue();
@@ -155,18 +411,50 @@ function watchWindowAndUpdateVh100StyleProperty(cssVariableName) {
155
411
  // MARK: Window Location Utiltiies
156
412
  /**
157
413
  * Whether or not the current host is localhost. Useful for determining local dev environments.
158
- */
159
- function isLocalhost() {
414
+ */ function isLocalhost() {
160
415
  return window.location.hostname === 'localhost';
161
416
  }
162
- function makeWindowPath(path) {
163
- return `${getBaseWindowUrl()}${path}`;
417
+ /**
418
+ * Constructs a full URL by combining the current window's base URL with the given relative path.
419
+ *
420
+ * @param path - Relative path to append to the base URL
421
+ * @returns Full URL string
422
+ *
423
+ * @example
424
+ * ```typescript
425
+ * // On https://example.com:3000
426
+ * const url = makeWindowPath('/api/users'); // "https://example.com:3000/api/users"
427
+ * ```
428
+ */ function makeWindowPath(path) {
429
+ return "".concat(getBaseWindowUrl()).concat(path);
164
430
  }
165
- function getBaseWindowUrl() {
166
- const port = window.location.port ? ':' + window.location.port : '';
167
- return `${window.location.protocol}//${window.location.hostname}${port}`;
431
+ /**
432
+ * Returns the base URL of the current window, including protocol, hostname, and port (if present).
433
+ *
434
+ * @returns Base URL string without trailing slash
435
+ *
436
+ * @example
437
+ * ```typescript
438
+ * // On https://example.com:8080/some/path
439
+ * const base = getBaseWindowUrl(); // "https://example.com:8080"
440
+ * ```
441
+ */ function getBaseWindowUrl() {
442
+ var port = window.location.port ? ':' + window.location.port : '';
443
+ return "".concat(window.location.protocol, "//").concat(window.location.hostname).concat(port);
168
444
  }
169
- function getWindowPathNameWithQuery() {
445
+ /**
446
+ * Returns the current window pathname concatenated with the query string.
447
+ *
448
+ * Useful for capturing the full relative URL for redirect-after-login or deep linking scenarios.
449
+ *
450
+ * @returns Pathname and query string (e.g. "/app/dashboard?tab=settings")
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * // On https://example.com/app/dashboard?tab=settings
455
+ * const path = getWindowPathNameWithQuery(); // "/app/dashboard?tab=settings"
456
+ * ```
457
+ */ function getWindowPathNameWithQuery() {
170
458
  return window.location.pathname + window.location.search;
171
459
  }
172
460
 
package/index.esm.js CHANGED
@@ -4,13 +4,11 @@ import { BehaviorSubject, shareReplay, switchMap, from, firstValueFrom } from 'r
4
4
 
5
5
  /**
6
6
  * Creatse a new BrowserObjectURLRef.
7
- */
8
- function browserObjectUrlRef() {
9
- let browserUrl;
7
+ */ function browserObjectUrlRef() {
8
+ var browserUrl;
10
9
  /**
11
10
  * Revokes the existing browser object URL, if one exists.
12
- */
13
- function destroy() {
11
+ */ function destroy() {
14
12
  if (browserUrl) {
15
13
  URL.revokeObjectURL(browserUrl);
16
14
  }
@@ -23,128 +21,386 @@ function browserObjectUrlRef() {
23
21
  return browserUrl;
24
22
  }
25
23
  return {
26
- destroy,
27
- getBrowserUrl: () => browserUrl,
24
+ destroy: destroy,
25
+ getBrowserUrl: function getBrowserUrl() {
26
+ return browserUrl;
27
+ },
28
28
  createBrowserUrl: createBrowserUrl
29
29
  };
30
30
  }
31
31
 
32
- /*eslint @typescript-eslint/no-explicit-any:"off"*/
33
- // any is used with intent here. Proper typing with Window requires using the dynamic strings _windowKey and _callbackKey.
32
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
33
+ try {
34
+ var info = gen[key](arg);
35
+ var value = info.value;
36
+ } catch (error) {
37
+ reject(error);
38
+ return;
39
+ }
40
+ if (info.done) {
41
+ resolve(value);
42
+ } else {
43
+ Promise.resolve(value).then(_next, _throw);
44
+ }
45
+ }
46
+ function _async_to_generator(fn) {
47
+ return function() {
48
+ var self = this, args = arguments;
49
+ return new Promise(function(resolve, reject) {
50
+ var gen = fn.apply(self, args);
51
+ function _next(value) {
52
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
53
+ }
54
+ function _throw(err) {
55
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
56
+ }
57
+ _next(undefined);
58
+ });
59
+ };
60
+ }
61
+ function _class_call_check(instance, Constructor) {
62
+ if (!(instance instanceof Constructor)) {
63
+ throw new TypeError("Cannot call a class as a function");
64
+ }
65
+ }
66
+ function _defineProperties(target, props) {
67
+ for(var i = 0; i < props.length; i++){
68
+ var descriptor = props[i];
69
+ descriptor.enumerable = descriptor.enumerable || false;
70
+ descriptor.configurable = true;
71
+ if ("value" in descriptor) descriptor.writable = true;
72
+ Object.defineProperty(target, descriptor.key, descriptor);
73
+ }
74
+ }
75
+ function _create_class(Constructor, protoProps, staticProps) {
76
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
77
+ return Constructor;
78
+ }
79
+ function _define_property(obj, key, value) {
80
+ if (key in obj) {
81
+ Object.defineProperty(obj, key, {
82
+ value: value,
83
+ enumerable: true,
84
+ configurable: true,
85
+ writable: true
86
+ });
87
+ } else {
88
+ obj[key] = value;
89
+ }
90
+ return obj;
91
+ }
92
+ function _ts_generator(thisArg, body) {
93
+ var f, y, t, _ = {
94
+ label: 0,
95
+ sent: function() {
96
+ if (t[0] & 1) throw t[1];
97
+ return t[1];
98
+ },
99
+ trys: [],
100
+ ops: []
101
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
102
+ return d(g, "next", {
103
+ value: verb(0)
104
+ }), d(g, "throw", {
105
+ value: verb(1)
106
+ }), d(g, "return", {
107
+ value: verb(2)
108
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
109
+ value: function() {
110
+ return this;
111
+ }
112
+ }), g;
113
+ function verb(n) {
114
+ return function(v) {
115
+ return step([
116
+ n,
117
+ v
118
+ ]);
119
+ };
120
+ }
121
+ function step(op) {
122
+ if (f) throw new TypeError("Generator is already executing.");
123
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
124
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
125
+ if (y = 0, t) op = [
126
+ op[0] & 2,
127
+ t.value
128
+ ];
129
+ switch(op[0]){
130
+ case 0:
131
+ case 1:
132
+ t = op;
133
+ break;
134
+ case 4:
135
+ _.label++;
136
+ return {
137
+ value: op[1],
138
+ done: false
139
+ };
140
+ case 5:
141
+ _.label++;
142
+ y = op[1];
143
+ op = [
144
+ 0
145
+ ];
146
+ continue;
147
+ case 7:
148
+ op = _.ops.pop();
149
+ _.trys.pop();
150
+ continue;
151
+ default:
152
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
153
+ _ = 0;
154
+ continue;
155
+ }
156
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
157
+ _.label = op[1];
158
+ break;
159
+ }
160
+ if (op[0] === 6 && _.label < t[1]) {
161
+ _.label = t[1];
162
+ t = op;
163
+ break;
164
+ }
165
+ if (t && _.label < t[2]) {
166
+ _.label = t[2];
167
+ _.ops.push(op);
168
+ break;
169
+ }
170
+ if (t[2]) _.ops.pop();
171
+ _.trys.pop();
172
+ continue;
173
+ }
174
+ op = body.call(thisArg, _);
175
+ } catch (e) {
176
+ op = [
177
+ 6,
178
+ e
179
+ ];
180
+ y = 0;
181
+ } finally{
182
+ f = t = 0;
183
+ }
184
+ if (op[0] & 5) throw op[1];
185
+ return {
186
+ value: op[0] ? op[1] : void 0,
187
+ done: true
188
+ };
189
+ }
190
+ }
34
191
  /**
35
192
  * Used for loading services in the browser that are imported from other scripts, such as Facebook, Segment, Stripe, etc.
36
- */
37
- class AbstractAsyncWindowLoadedService {
38
- _loading = new BehaviorSubject(undefined);
39
- /**
193
+ */ var AbstractAsyncWindowLoadedService = /*#__PURE__*/ function() {
194
+ function AbstractAsyncWindowLoadedService(windowKey, callbackKey, serviceName, preload) {
195
+ var _this = this;
196
+ _class_call_check(this, AbstractAsyncWindowLoadedService);
197
+ _define_property(this, "_loading", new BehaviorSubject(undefined));
198
+ /**
40
199
  * Key that is attached to the window for the object that is the service when finished loading.
41
- */
42
- _windowKey;
43
- /**
200
+ */ _define_property(this, "_windowKey", void 0);
201
+ /**
44
202
  * Optional key attached to window that is a function that is executed when the setup is complete.
45
- */
46
- _callbackKey;
47
- /**
203
+ */ _define_property(this, "_callbackKey", void 0);
204
+ /**
48
205
  * Service name used in logging. Defaults to the windowKey.
49
- */
50
- _serviceName;
51
- loading$ = this._loading.pipe(tapFirst(() => this.loadService()), filterMaybe(), shareReplay(1));
52
- service$ = this.loading$.pipe(switchMap((x) => from(x)), shareReplay(1));
53
- /**
54
- * @param windowKey
55
- * @param callbackKey
56
- */
57
- constructor(windowKey, callbackKey, serviceName, preload) {
206
+ */ _define_property(this, "_serviceName", void 0);
207
+ /**
208
+ * Observable that emits the loading promise. Subscribing triggers the initial load if not already started.
209
+ */ _define_property(this, "loading$", this._loading.pipe(tapFirst(function() {
210
+ return _this.loadService();
211
+ }), filterMaybe(), shareReplay(1)));
212
+ /**
213
+ * Observable that emits the resolved service instance once loading completes. Replays the last value to new subscribers.
214
+ */ _define_property(this, "service$", this.loading$.pipe(switchMap(function(x) {
215
+ return from(x);
216
+ }), shareReplay(1)));
58
217
  this._windowKey = windowKey;
59
218
  this._callbackKey = callbackKey;
60
- this._serviceName = serviceName ?? windowKey;
219
+ this._serviceName = serviceName !== null && serviceName !== void 0 ? serviceName : windowKey;
61
220
  if (stringToBoolean(preload)) {
62
221
  // Begin loading the service immediately.
63
- setTimeout(() => this.loadService().catch());
64
- }
65
- }
66
- destroy() {
67
- this._loading.complete();
68
- }
69
- getService() {
70
- return firstValueFrom(this.service$);
71
- }
72
- // MARK: Loading
73
- loadService() {
74
- if (!this._loading.value) {
75
- const loadingPromise = new Promise((resolve, reject) => {
76
- let loadTry = 0;
77
- const rejectWithError = () => {
78
- reject(new Error(`Service "${this._serviceName}" failed loading with windowKey "${this._windowKey}"`));
79
- };
80
- const tryLoad = () => {
81
- const windowRef = window;
82
- // Loaded before the promise.
83
- if (windowRef[this._windowKey]) {
84
- // Not yet finished loading async. Intercept the function.
85
- // console.log('Window key.');
86
- return resolve(this.completeLoadingService());
87
- }
88
- else if (this._callbackKey && windowRef[this._callbackKey]) {
89
- // console.log('Callback key.');
90
- windowRef[this._callbackKey] = () => resolve(this.completeLoadingService());
91
- }
92
- else if (loadTry < 10) {
93
- loadTry += 1;
94
- // console.log('Try reload...');
95
- setTimeout(() => tryLoad(), 1000);
96
- }
97
- else {
98
- const retry = this._onLoadServiceFailure();
99
- if (retry) {
100
- retry.then((x) => resolve(x)).catch(() => rejectWithError());
101
- }
102
- else {
103
- rejectWithError();
104
- }
105
- }
106
- };
107
- tryLoad();
222
+ setTimeout(function() {
223
+ return _this.loadService().catch();
108
224
  });
109
- this._loading.next(loadingPromise);
110
225
  }
111
- return this._loading.value;
112
226
  }
113
- _onLoadServiceFailure() {
114
- // override in parent if needed.
115
- }
116
- async completeLoadingService() {
117
- await this._prepareCompleteLoadingService();
118
- const service = window[this._windowKey];
119
- if (!service) {
120
- throw new Error(`Service "${this._serviceName}" could not complete loading.`);
227
+ _create_class(AbstractAsyncWindowLoadedService, [
228
+ {
229
+ /**
230
+ * Completes the internal loading subject, stopping any pending service resolution.
231
+ */ key: "destroy",
232
+ value: function destroy() {
233
+ this._loading.complete();
234
+ }
235
+ },
236
+ {
237
+ /**
238
+ * Returns a promise that resolves with the loaded service instance.
239
+ *
240
+ * Triggers loading if not already started.
241
+ */ key: "getService",
242
+ value: function getService() {
243
+ return firstValueFrom(this.service$);
244
+ }
245
+ },
246
+ {
247
+ // MARK: Loading
248
+ /**
249
+ * Initiates the service loading process by polling the `window` object for the service key.
250
+ *
251
+ * Retries up to 10 times at 1-second intervals before invoking `_onLoadServiceFailure()`.
252
+ * Subsequent calls return the same promise without re-initiating.
253
+ *
254
+ * @returns Promise that resolves with the loaded service
255
+ */ key: "loadService",
256
+ value: function loadService() {
257
+ var _this = this;
258
+ if (!this._loading.value) {
259
+ var loadingPromise = new Promise(function(resolve, reject) {
260
+ var loadTry = 0;
261
+ var rejectWithError = function rejectWithError() {
262
+ reject(new Error('Service "'.concat(_this._serviceName, '" failed loading with windowKey "').concat(_this._windowKey, '"')));
263
+ };
264
+ var tryLoad = function tryLoad1() {
265
+ var windowRef = window;
266
+ // Loaded before the promise.
267
+ if (windowRef[_this._windowKey]) {
268
+ // Not yet finished loading async. Intercept the function.
269
+ // console.log('Window key.');
270
+ return resolve(_this.completeLoadingService());
271
+ } else if (_this._callbackKey && windowRef[_this._callbackKey]) {
272
+ // console.log('Callback key.');
273
+ windowRef[_this._callbackKey] = function() {
274
+ return resolve(_this.completeLoadingService());
275
+ };
276
+ } else if (loadTry < 10) {
277
+ loadTry += 1;
278
+ // console.log('Try reload...');
279
+ setTimeout(function() {
280
+ return tryLoad();
281
+ }, 1000);
282
+ } else {
283
+ var retry = _this._onLoadServiceFailure();
284
+ if (retry) {
285
+ retry.then(function(x) {
286
+ return resolve(x);
287
+ }).catch(function() {
288
+ return rejectWithError();
289
+ });
290
+ } else {
291
+ rejectWithError();
292
+ }
293
+ }
294
+ };
295
+ tryLoad();
296
+ });
297
+ this._loading.next(loadingPromise);
298
+ }
299
+ return this._loading.value;
300
+ }
301
+ },
302
+ {
303
+ /**
304
+ * Hook called when the service fails to load after all retry attempts.
305
+ *
306
+ * Subclasses can override to attempt an alternative loading strategy or return a fallback promise.
307
+ *
308
+ * @returns A promise resolving with the service if recovery succeeds, or `void` to reject
309
+ */ key: "_onLoadServiceFailure",
310
+ value: function _onLoadServiceFailure() {
311
+ // override in parent if needed.
312
+ }
313
+ },
314
+ {
315
+ key: "completeLoadingService",
316
+ value: function completeLoadingService() {
317
+ return _async_to_generator(function() {
318
+ var service, initializedService;
319
+ return _ts_generator(this, function(_state) {
320
+ switch(_state.label){
321
+ case 0:
322
+ return [
323
+ 4,
324
+ this._prepareCompleteLoadingService()
325
+ ];
326
+ case 1:
327
+ _state.sent();
328
+ service = window[this._windowKey];
329
+ if (!service) {
330
+ throw new Error('Service "'.concat(this._serviceName, '" could not complete loading.'));
331
+ }
332
+ return [
333
+ 4,
334
+ this._initService(service)
335
+ ];
336
+ case 2:
337
+ initializedService = _state.sent();
338
+ return [
339
+ 2,
340
+ initializedService !== null && initializedService !== void 0 ? initializedService : service
341
+ ];
342
+ }
343
+ });
344
+ }).call(this);
345
+ }
346
+ },
347
+ {
348
+ /**
349
+ * Hook called before completing the service load. Subclasses can override to perform
350
+ * additional async setup before the service reference is read from `window`.
351
+ */ key: "_prepareCompleteLoadingService",
352
+ value: function _prepareCompleteLoadingService() {
353
+ return Promise.resolve();
354
+ }
355
+ },
356
+ {
357
+ /**
358
+ * Hook called after the service is retrieved from `window` to perform initialization.
359
+ *
360
+ * Subclasses can override to configure or wrap the service before it is emitted to subscribers.
361
+ *
362
+ * @param service - The raw service instance from the window
363
+ * @returns The initialized service, or void to use the original
364
+ */ key: "_initService",
365
+ value: function _initService(service) {
366
+ return Promise.resolve(service);
367
+ }
121
368
  }
122
- // Init the API
123
- const initializedService = await this._initService(service);
124
- return initializedService ?? service;
125
- }
126
- _prepareCompleteLoadingService() {
127
- return Promise.resolve();
128
- }
129
- _initService(service) {
130
- return Promise.resolve(service);
131
- }
132
- }
369
+ ]);
370
+ return AbstractAsyncWindowLoadedService;
371
+ }
372
+ ();
133
373
 
134
374
  // https://dev.to/maciejtrzcinski/100vh-problem-with-ios-safari-3ge9
135
- const DEFAULT_VH100_VARIABLE_NAME = 'vh100';
136
- function refreshVh100Function(cssVariableName = DEFAULT_VH100_VARIABLE_NAME) {
137
- const cssProperty = `--${cssVariableName}`;
138
- return () => {
139
- const doc = document.documentElement;
140
- doc.style.setProperty(cssProperty, `${window.innerHeight}px`);
375
+ /**
376
+ * Default CSS custom property name used to store the viewport height value.
377
+ */ var DEFAULT_VH100_VARIABLE_NAME = 'vh100';
378
+ /**
379
+ * Creates a function that sets a CSS custom property on `document.documentElement` to the current `window.innerHeight` in pixels.
380
+ *
381
+ * This is a workaround for the iOS Safari 100vh bug where `100vh` includes the browser chrome,
382
+ * causing layout overflow. The returned function can be called to refresh the property value.
383
+ *
384
+ * @param cssVariableName - Name of the CSS custom property (without the `--` prefix)
385
+ * @returns A zero-argument function that updates the CSS property to the current inner height
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * const refresh = refreshVh100Function('vh100');
390
+ * refresh(); // sets --vh100 to e.g. "812px"
391
+ * ```
392
+ */ function refreshVh100Function() {
393
+ var cssVariableName = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : DEFAULT_VH100_VARIABLE_NAME;
394
+ var cssProperty = "--".concat(cssVariableName);
395
+ return function() {
396
+ var doc = document.documentElement;
397
+ doc.style.setProperty(cssProperty, "".concat(window.innerHeight, "px"));
141
398
  };
142
399
  }
143
400
  /**
144
401
  * Adds window event listeners to populate the css variable `vh100`, or another input variable name, with the current window height.
145
- */
146
- function watchWindowAndUpdateVh100StyleProperty(cssVariableName) {
147
- const refreshPropertyValue = refreshVh100Function(cssVariableName);
402
+ */ function watchWindowAndUpdateVh100StyleProperty(cssVariableName) {
403
+ var refreshPropertyValue = refreshVh100Function(cssVariableName);
148
404
  window.addEventListener('resize', refreshPropertyValue);
149
405
  window.addEventListener('orientationchange', refreshPropertyValue);
150
406
  refreshPropertyValue();
@@ -153,18 +409,50 @@ function watchWindowAndUpdateVh100StyleProperty(cssVariableName) {
153
409
  // MARK: Window Location Utiltiies
154
410
  /**
155
411
  * Whether or not the current host is localhost. Useful for determining local dev environments.
156
- */
157
- function isLocalhost() {
412
+ */ function isLocalhost() {
158
413
  return window.location.hostname === 'localhost';
159
414
  }
160
- function makeWindowPath(path) {
161
- return `${getBaseWindowUrl()}${path}`;
415
+ /**
416
+ * Constructs a full URL by combining the current window's base URL with the given relative path.
417
+ *
418
+ * @param path - Relative path to append to the base URL
419
+ * @returns Full URL string
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * // On https://example.com:3000
424
+ * const url = makeWindowPath('/api/users'); // "https://example.com:3000/api/users"
425
+ * ```
426
+ */ function makeWindowPath(path) {
427
+ return "".concat(getBaseWindowUrl()).concat(path);
162
428
  }
163
- function getBaseWindowUrl() {
164
- const port = window.location.port ? ':' + window.location.port : '';
165
- return `${window.location.protocol}//${window.location.hostname}${port}`;
429
+ /**
430
+ * Returns the base URL of the current window, including protocol, hostname, and port (if present).
431
+ *
432
+ * @returns Base URL string without trailing slash
433
+ *
434
+ * @example
435
+ * ```typescript
436
+ * // On https://example.com:8080/some/path
437
+ * const base = getBaseWindowUrl(); // "https://example.com:8080"
438
+ * ```
439
+ */ function getBaseWindowUrl() {
440
+ var port = window.location.port ? ':' + window.location.port : '';
441
+ return "".concat(window.location.protocol, "//").concat(window.location.hostname).concat(port);
166
442
  }
167
- function getWindowPathNameWithQuery() {
443
+ /**
444
+ * Returns the current window pathname concatenated with the query string.
445
+ *
446
+ * Useful for capturing the full relative URL for redirect-after-login or deep linking scenarios.
447
+ *
448
+ * @returns Pathname and query string (e.g. "/app/dashboard?tab=settings")
449
+ *
450
+ * @example
451
+ * ```typescript
452
+ * // On https://example.com/app/dashboard?tab=settings
453
+ * const path = getWindowPathNameWithQuery(); // "/app/dashboard?tab=settings"
454
+ * ```
455
+ */ function getWindowPathNameWithQuery() {
168
456
  return window.location.pathname + window.location.search;
169
457
  }
170
458
 
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@dereekb/browser",
3
- "version": "13.2.2",
3
+ "version": "13.3.1",
4
4
  "peerDependencies": {
5
- "@dereekb/util": "13.2.2",
6
- "@dereekb/rxjs": "13.2.2",
5
+ "@dereekb/util": "13.3.1",
6
+ "@dereekb/rxjs": "13.3.1",
7
7
  "rxjs": "^7.8.0"
8
8
  },
9
9
  "dependencies": {},
@@ -1,6 +1,12 @@
1
1
  import { type Maybe, type Destroyable } from '@dereekb/util';
2
2
  import { type Observable } from 'rxjs';
3
+ /**
4
+ * Record type representing a service instance attached to the `window` object, keyed by a string identifier.
5
+ */
3
6
  export type ServiceInWindow<T> = Record<string, Maybe<T>>;
7
+ /**
8
+ * Record type representing a callback function attached to the `window` object, called when a service finishes loading.
9
+ */
4
10
  export type ServiceCallbackInWindow = Record<string, () => void>;
5
11
  /**
6
12
  * Used for loading services in the browser that are imported from other scripts, such as Facebook, Segment, Stripe, etc.
@@ -19,18 +25,61 @@ export declare abstract class AbstractAsyncWindowLoadedService<T> implements Des
19
25
  * Service name used in logging. Defaults to the windowKey.
20
26
  */
21
27
  private readonly _serviceName;
28
+ /**
29
+ * Observable that emits the loading promise. Subscribing triggers the initial load if not already started.
30
+ */
22
31
  readonly loading$: Observable<Promise<T>>;
32
+ /**
33
+ * Observable that emits the resolved service instance once loading completes. Replays the last value to new subscribers.
34
+ */
23
35
  readonly service$: Observable<T>;
24
36
  /**
25
- * @param windowKey
26
- * @param callbackKey
37
+ * @param windowKey - Key on the `window` object where the loaded service is stored
38
+ * @param callbackKey - Optional key on `window` for a callback invoked when the service script finishes loading
39
+ * @param serviceName - Human-readable name for logging; defaults to `windowKey`
40
+ * @param preload - When truthy, begins loading the service immediately on construction
27
41
  */
28
42
  constructor(windowKey: string, callbackKey?: string, serviceName?: Maybe<string>, preload?: Maybe<boolean | string>);
43
+ /**
44
+ * Completes the internal loading subject, stopping any pending service resolution.
45
+ */
29
46
  destroy(): void;
47
+ /**
48
+ * Returns a promise that resolves with the loaded service instance.
49
+ *
50
+ * Triggers loading if not already started.
51
+ */
30
52
  getService(): Promise<T>;
53
+ /**
54
+ * Initiates the service loading process by polling the `window` object for the service key.
55
+ *
56
+ * Retries up to 10 times at 1-second intervals before invoking `_onLoadServiceFailure()`.
57
+ * Subsequent calls return the same promise without re-initiating.
58
+ *
59
+ * @returns Promise that resolves with the loaded service
60
+ */
31
61
  protected loadService(): Promise<T>;
62
+ /**
63
+ * Hook called when the service fails to load after all retry attempts.
64
+ *
65
+ * Subclasses can override to attempt an alternative loading strategy or return a fallback promise.
66
+ *
67
+ * @returns A promise resolving with the service if recovery succeeds, or `void` to reject
68
+ */
32
69
  protected _onLoadServiceFailure(): Promise<T> | void;
33
70
  private completeLoadingService;
71
+ /**
72
+ * Hook called before completing the service load. Subclasses can override to perform
73
+ * additional async setup before the service reference is read from `window`.
74
+ */
34
75
  protected _prepareCompleteLoadingService(): Promise<unknown>;
76
+ /**
77
+ * Hook called after the service is retrieved from `window` to perform initialization.
78
+ *
79
+ * Subclasses can override to configure or wrap the service before it is emitted to subscribers.
80
+ *
81
+ * @param service - The raw service instance from the window
82
+ * @returns The initialized service, or void to use the original
83
+ */
35
84
  protected _initService(service: T): Promise<T | void>;
36
85
  }
@@ -1,4 +1,22 @@
1
+ /**
2
+ * Default CSS custom property name used to store the viewport height value.
3
+ */
1
4
  export declare const DEFAULT_VH100_VARIABLE_NAME = "vh100";
5
+ /**
6
+ * Creates a function that sets a CSS custom property on `document.documentElement` to the current `window.innerHeight` in pixels.
7
+ *
8
+ * This is a workaround for the iOS Safari 100vh bug where `100vh` includes the browser chrome,
9
+ * causing layout overflow. The returned function can be called to refresh the property value.
10
+ *
11
+ * @param cssVariableName - Name of the CSS custom property (without the `--` prefix)
12
+ * @returns A zero-argument function that updates the CSS property to the current inner height
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const refresh = refreshVh100Function('vh100');
17
+ * refresh(); // sets --vh100 to e.g. "812px"
18
+ * ```
19
+ */
2
20
  export declare function refreshVh100Function(cssVariableName?: string): () => void;
3
21
  /**
4
22
  * Adds window event listeners to populate the css variable `vh100`, or another input variable name, with the current window height.
@@ -2,6 +2,42 @@
2
2
  * Whether or not the current host is localhost. Useful for determining local dev environments.
3
3
  */
4
4
  export declare function isLocalhost(): boolean;
5
+ /**
6
+ * Constructs a full URL by combining the current window's base URL with the given relative path.
7
+ *
8
+ * @param path - Relative path to append to the base URL
9
+ * @returns Full URL string
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // On https://example.com:3000
14
+ * const url = makeWindowPath('/api/users'); // "https://example.com:3000/api/users"
15
+ * ```
16
+ */
5
17
  export declare function makeWindowPath(path: string): string;
18
+ /**
19
+ * Returns the base URL of the current window, including protocol, hostname, and port (if present).
20
+ *
21
+ * @returns Base URL string without trailing slash
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // On https://example.com:8080/some/path
26
+ * const base = getBaseWindowUrl(); // "https://example.com:8080"
27
+ * ```
28
+ */
6
29
  export declare function getBaseWindowUrl(): string;
30
+ /**
31
+ * Returns the current window pathname concatenated with the query string.
32
+ *
33
+ * Useful for capturing the full relative URL for redirect-after-login or deep linking scenarios.
34
+ *
35
+ * @returns Pathname and query string (e.g. "/app/dashboard?tab=settings")
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // On https://example.com/app/dashboard?tab=settings
40
+ * const path = getWindowPathNameWithQuery(); // "/app/dashboard?tab=settings"
41
+ * ```
42
+ */
7
43
  export declare function getWindowPathNameWithQuery(): string;