@async/framework 0.10.0 → 0.10.2

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/browser.ts CHANGED
@@ -319,10 +319,20 @@ const __lazyRegistryModule = (() => {
319
319
  const resolved = resolveDescriptorUrl(type, id, descriptor, registryAssets);
320
320
  let modulePromise = moduleCache.get(resolved.moduleUrl);
321
321
  if (!modulePromise) {
322
- modulePromise = Promise.resolve(importModule(resolved.moduleUrl));
322
+ modulePromise = Promise.resolve().then(() => importModule(resolved.moduleUrl));
323
323
  moduleCache.set(resolved.moduleUrl, modulePromise);
324
324
  }
325
- const module = await modulePromise;
325
+ let module;
326
+ try {
327
+ module = await modulePromise;
328
+ } catch (cause) {
329
+ if (moduleCache.get(resolved.moduleUrl) === modulePromise) {
330
+ moduleCache.delete(resolved.moduleUrl);
331
+ }
332
+ throw new Error(`Lazy ${type} "${id}" failed to import ${resolved.moduleUrl}: ${errorMessage(cause)}`, {
333
+ cause
334
+ });
335
+ }
326
336
  const value = resolveExport(module, resolved.exportNames, type, id);
327
337
  exportCache.set(cacheKey, value);
328
338
  return value;
@@ -482,6 +492,10 @@ const __lazyRegistryModule = (() => {
482
492
  return /^[A-Za-z][A-Za-z\d+.-]*:/.test(value);
483
493
  }
484
494
 
495
+ function errorMessage(error) {
496
+ return error instanceof Error ? error.message : String(error);
497
+ }
498
+
485
499
  function stableStringify(value) {
486
500
  if (!value || typeof value !== "object" || Array.isArray(value)) {
487
501
  return JSON.stringify(value);
@@ -810,15 +824,7 @@ const __cacheModule = (() => {
810
824
 
811
825
  get(key) {
812
826
  assertKey(key);
813
- const entry = entries.get(key);
814
- if (!entry) {
815
- return undefined;
816
- }
817
- if (entry.expiresAt !== undefined && entry.expiresAt <= now()) {
818
- entries.delete(key);
819
- return undefined;
820
- }
821
- return entry.value;
827
+ return readEntry(key).value;
822
828
  },
823
829
 
824
830
  set(key, value, options = {}) {
@@ -836,9 +842,9 @@ const __cacheModule = (() => {
836
842
  if (typeof fn !== "function") {
837
843
  throw new TypeError("cache.getOrSet(key, fn) requires a function.");
838
844
  }
839
- const cached = registryApi.get(key);
840
- if (cached !== undefined) {
841
- return cached;
845
+ const cached = readEntry(key);
846
+ if (cached.found) {
847
+ return cached.value;
842
848
  }
843
849
  if (pending.has(key)) {
844
850
  return pending.get(key);
@@ -889,8 +895,8 @@ const __cacheModule = (() => {
889
895
  snapshot() {
890
896
  const snapshot = {};
891
897
  for (const [key] of entries) {
892
- const value = registryApi.get(key);
893
- if (value !== undefined) {
898
+ const { found, value } = readEntry(key);
899
+ if (found && value !== undefined) {
894
900
  snapshot[key] = value;
895
901
  }
896
902
  }
@@ -930,6 +936,18 @@ const __cacheModule = (() => {
930
936
  const prefix = key.split(":")[0];
931
937
  return definitions.get(prefix);
932
938
  }
939
+
940
+ function readEntry(key) {
941
+ const entry = entries.get(key);
942
+ if (!entry) {
943
+ return { found: false, value: undefined };
944
+ }
945
+ if (entry.expiresAt !== undefined && entry.expiresAt <= now()) {
946
+ entries.delete(key);
947
+ return { found: false, value: undefined };
948
+ }
949
+ return { found: true, value: entry.value };
950
+ }
933
951
  }
934
952
 
935
953
  function normalizeDefinition(definition) {
@@ -2449,14 +2467,17 @@ const __serverModule = (() => {
2449
2467
  return output;
2450
2468
  }
2451
2469
 
2452
- function assertJsonTransportable(value, seen = new Set()) {
2470
+ function assertJsonTransportable(value, stack = new Set()) {
2471
+ if (typeof value === "bigint") {
2472
+ throw new Error("Server proxy JSON transport does not support BigInt values.");
2473
+ }
2453
2474
  if (value == null || typeof value !== "object") {
2454
2475
  return;
2455
2476
  }
2456
- if (seen.has(value)) {
2457
- return;
2477
+ if (stack.has(value)) {
2478
+ throw new Error("Server proxy JSON transport does not support circular values.");
2458
2479
  }
2459
- seen.add(value);
2480
+ stack.add(value);
2460
2481
 
2461
2482
  const tag = Object.prototype.toString.call(value);
2462
2483
  if (tag === "[object File]" || tag === "[object Blob]" || tag === "[object FormData]") {
@@ -2464,13 +2485,15 @@ const __serverModule = (() => {
2464
2485
  }
2465
2486
  if (Array.isArray(value)) {
2466
2487
  for (const item of value) {
2467
- assertJsonTransportable(item, seen);
2488
+ assertJsonTransportable(item, stack);
2468
2489
  }
2490
+ stack.delete(value);
2469
2491
  return;
2470
2492
  }
2471
2493
  for (const item of Object.values(value)) {
2472
- assertJsonTransportable(item, seen);
2494
+ assertJsonTransportable(item, stack);
2473
2495
  }
2496
+ stack.delete(value);
2474
2497
  }
2475
2498
 
2476
2499
  function joinEndpoint(endpoint, id) {
@@ -3115,6 +3138,7 @@ const __loaderModule = (() => {
3115
3138
  return;
3116
3139
  }
3117
3140
  destroyed = true;
3141
+ markDestroyedScopes(rootNode);
3118
3142
  for (const cleanup of [...cleanups]) {
3119
3143
  runCleanup(cleanup);
3120
3144
  }
@@ -3565,6 +3589,12 @@ const __loaderModule = (() => {
3565
3589
  });
3566
3590
  }
3567
3591
 
3592
+ function markDestroyedScopes(scope) {
3593
+ for (const element of elementsIn(scope)) {
3594
+ schedulerInstance.markScopeDestroyed(element);
3595
+ }
3596
+ }
3597
+
3568
3598
  return api;
3569
3599
  }
3570
3600
 
@@ -3990,7 +4020,7 @@ const __routerModule = (() => {
3990
4020
  }
3991
4021
  const params = {};
3992
4022
  candidate.keys.forEach((key, index) => {
3993
- params[key] = decodeURIComponent(match[index + 1] ?? "");
4023
+ params[key] = safeDecodeURIComponent(match[index + 1] ?? "");
3994
4024
  });
3995
4025
  return {
3996
4026
  pattern: candidate.pattern,
@@ -4105,11 +4135,11 @@ const __routerModule = (() => {
4105
4135
  }
4106
4136
  bindNavigation();
4107
4137
  if (mode === "csr") {
4108
- void api.navigate(currentUrl(), {
4138
+ handleNavigation(api.navigate(currentUrl(), {
4109
4139
  replace: true,
4110
4140
  initial: true,
4111
4141
  source: "client"
4112
- }).catch(() => {});
4142
+ }));
4113
4143
  return api;
4114
4144
  }
4115
4145
  updateStateFromLocation();
@@ -4174,7 +4204,7 @@ const __routerModule = (() => {
4174
4204
  return;
4175
4205
  }
4176
4206
  event.preventDefault();
4177
- api.navigate(anchor.href);
4207
+ handleNavigation(api.navigate(anchor.href));
4178
4208
  };
4179
4209
  const submit = (event) => {
4180
4210
  const form = closest(event.target, "form");
@@ -4182,9 +4212,9 @@ const __routerModule = (() => {
4182
4212
  return;
4183
4213
  }
4184
4214
  event.preventDefault();
4185
- api.navigate(formActionUrl(form));
4215
+ handleNavigation(api.navigate(formActionUrl(form)));
4186
4216
  };
4187
- const popstate = () => api.navigate(currentUrl(), { history: false });
4217
+ const popstate = () => handleNavigation(api.navigate(currentUrl(), { history: false }));
4188
4218
 
4189
4219
  rootNode.addEventListener?.("click", click);
4190
4220
  rootNode.addEventListener?.("submit", submit);
@@ -4377,6 +4407,19 @@ const __routerModule = (() => {
4377
4407
  }
4378
4408
  }
4379
4409
 
4410
+ function handleNavigation(promise) {
4411
+ void promise.catch((error) => {
4412
+ if (destroyed) {
4413
+ return;
4414
+ }
4415
+ setRouterState({
4416
+ pending: false,
4417
+ error
4418
+ });
4419
+ dispatchAsyncError(rootNode, error);
4420
+ });
4421
+ }
4422
+
4380
4423
  function currentUrl() {
4381
4424
  return resolveUrl(documentRef.defaultView?.location?.href ?? "http://localhost/");
4382
4425
  }
@@ -4393,6 +4436,33 @@ const __routerModule = (() => {
4393
4436
  throw new Error("Router has been destroyed.");
4394
4437
  }
4395
4438
  }
4439
+
4440
+ function shouldIgnoreLink(event, anchor) {
4441
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
4442
+ return true;
4443
+ }
4444
+ if (anchor.target || anchor.hasAttribute("download")) {
4445
+ return true;
4446
+ }
4447
+ const target = resolveUrl(anchor.href);
4448
+ const current = currentUrl();
4449
+ if (target.origin !== current.origin) {
4450
+ return true;
4451
+ }
4452
+ return isHashOnlyNavigation(target, current, anchor);
4453
+ }
4454
+
4455
+ function shouldIgnoreForm(form) {
4456
+ const method = String(form.method || "get").toLowerCase();
4457
+ return method !== "get" || resolveUrl(form.action).origin !== currentUrl().origin;
4458
+ }
4459
+
4460
+ function formActionUrl(form) {
4461
+ const url = resolveUrl(form.action || form.ownerDocument.defaultView.location.href);
4462
+ const formData = new form.ownerDocument.defaultView.FormData(form);
4463
+ url.search = new URLSearchParams(formData).toString();
4464
+ return url.href;
4465
+ }
4396
4466
  }
4397
4467
 
4398
4468
  function normalizeRoute(pattern, definition) {
@@ -4430,28 +4500,6 @@ const __routerModule = (() => {
4430
4500
  return { regex: new RegExp(`^${source}$`), keys };
4431
4501
  }
4432
4502
 
4433
- function shouldIgnoreLink(event, anchor) {
4434
- if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
4435
- return true;
4436
- }
4437
- if (anchor.target || anchor.hasAttribute("download")) {
4438
- return true;
4439
- }
4440
- return toUrl(anchor.href).origin !== toUrl(anchor.ownerDocument.defaultView.location.href).origin;
4441
- }
4442
-
4443
- function shouldIgnoreForm(form) {
4444
- const method = String(form.method || "get").toLowerCase();
4445
- return method !== "get" || toUrl(form.action).origin !== toUrl(form.ownerDocument.defaultView.location.href).origin;
4446
- }
4447
-
4448
- function formActionUrl(form) {
4449
- const url = toUrl(form.action || form.ownerDocument.defaultView.location.href);
4450
- const formData = new form.ownerDocument.defaultView.FormData(form);
4451
- url.search = new URLSearchParams(formData).toString();
4452
- return url.href;
4453
- }
4454
-
4455
4503
  function closest(target, selector) {
4456
4504
  return target?.closest?.(selector);
4457
4505
  }
@@ -4467,6 +4515,34 @@ const __routerModule = (() => {
4467
4515
  return Object.fromEntries(url.searchParams.entries());
4468
4516
  }
4469
4517
 
4518
+ function safeDecodeURIComponent(value) {
4519
+ try {
4520
+ return decodeURIComponent(value);
4521
+ } catch {
4522
+ return value;
4523
+ }
4524
+ }
4525
+
4526
+ function isHashOnlyNavigation(target, current, anchor) {
4527
+ if (target.origin !== current.origin || target.pathname !== current.pathname || target.search !== current.search) {
4528
+ return false;
4529
+ }
4530
+ return target.hash !== current.hash || anchor.getAttribute?.("href")?.startsWith("#") === true;
4531
+ }
4532
+
4533
+ function dispatchAsyncError(element, error) {
4534
+ const EventCtor = element.ownerDocument?.defaultView?.CustomEvent ?? globalThis.CustomEvent;
4535
+ if (typeof EventCtor !== "function") {
4536
+ return;
4537
+ }
4538
+ element.dispatchEvent?.(
4539
+ new EventCtor("async:error", {
4540
+ bubbles: true,
4541
+ detail: { error }
4542
+ })
4543
+ );
4544
+ }
4545
+
4470
4546
  function escapeRegExp(value) {
4471
4547
  return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4472
4548
  }
package/browser.umd.js CHANGED
@@ -329,10 +329,20 @@
329
329
  const resolved = resolveDescriptorUrl(type, id, descriptor, registryAssets);
330
330
  let modulePromise = moduleCache.get(resolved.moduleUrl);
331
331
  if (!modulePromise) {
332
- modulePromise = Promise.resolve(importModule(resolved.moduleUrl));
332
+ modulePromise = Promise.resolve().then(() => importModule(resolved.moduleUrl));
333
333
  moduleCache.set(resolved.moduleUrl, modulePromise);
334
334
  }
335
- const module = await modulePromise;
335
+ let module;
336
+ try {
337
+ module = await modulePromise;
338
+ } catch (cause) {
339
+ if (moduleCache.get(resolved.moduleUrl) === modulePromise) {
340
+ moduleCache.delete(resolved.moduleUrl);
341
+ }
342
+ throw new Error(`Lazy ${type} "${id}" failed to import ${resolved.moduleUrl}: ${errorMessage(cause)}`, {
343
+ cause
344
+ });
345
+ }
336
346
  const value = resolveExport(module, resolved.exportNames, type, id);
337
347
  exportCache.set(cacheKey, value);
338
348
  return value;
@@ -492,6 +502,10 @@
492
502
  return /^[A-Za-z][A-Za-z\d+.-]*:/.test(value);
493
503
  }
494
504
 
505
+ function errorMessage(error) {
506
+ return error instanceof Error ? error.message : String(error);
507
+ }
508
+
495
509
  function stableStringify(value) {
496
510
  if (!value || typeof value !== "object" || Array.isArray(value)) {
497
511
  return JSON.stringify(value);
@@ -820,15 +834,7 @@
820
834
 
821
835
  get(key) {
822
836
  assertKey(key);
823
- const entry = entries.get(key);
824
- if (!entry) {
825
- return undefined;
826
- }
827
- if (entry.expiresAt !== undefined && entry.expiresAt <= now()) {
828
- entries.delete(key);
829
- return undefined;
830
- }
831
- return entry.value;
837
+ return readEntry(key).value;
832
838
  },
833
839
 
834
840
  set(key, value, options = {}) {
@@ -846,9 +852,9 @@
846
852
  if (typeof fn !== "function") {
847
853
  throw new TypeError("cache.getOrSet(key, fn) requires a function.");
848
854
  }
849
- const cached = registryApi.get(key);
850
- if (cached !== undefined) {
851
- return cached;
855
+ const cached = readEntry(key);
856
+ if (cached.found) {
857
+ return cached.value;
852
858
  }
853
859
  if (pending.has(key)) {
854
860
  return pending.get(key);
@@ -899,8 +905,8 @@
899
905
  snapshot() {
900
906
  const snapshot = {};
901
907
  for (const [key] of entries) {
902
- const value = registryApi.get(key);
903
- if (value !== undefined) {
908
+ const { found, value } = readEntry(key);
909
+ if (found && value !== undefined) {
904
910
  snapshot[key] = value;
905
911
  }
906
912
  }
@@ -940,6 +946,18 @@
940
946
  const prefix = key.split(":")[0];
941
947
  return definitions.get(prefix);
942
948
  }
949
+
950
+ function readEntry(key) {
951
+ const entry = entries.get(key);
952
+ if (!entry) {
953
+ return { found: false, value: undefined };
954
+ }
955
+ if (entry.expiresAt !== undefined && entry.expiresAt <= now()) {
956
+ entries.delete(key);
957
+ return { found: false, value: undefined };
958
+ }
959
+ return { found: true, value: entry.value };
960
+ }
943
961
  }
944
962
 
945
963
  function normalizeDefinition(definition) {
@@ -2459,14 +2477,17 @@
2459
2477
  return output;
2460
2478
  }
2461
2479
 
2462
- function assertJsonTransportable(value, seen = new Set()) {
2480
+ function assertJsonTransportable(value, stack = new Set()) {
2481
+ if (typeof value === "bigint") {
2482
+ throw new Error("Server proxy JSON transport does not support BigInt values.");
2483
+ }
2463
2484
  if (value == null || typeof value !== "object") {
2464
2485
  return;
2465
2486
  }
2466
- if (seen.has(value)) {
2467
- return;
2487
+ if (stack.has(value)) {
2488
+ throw new Error("Server proxy JSON transport does not support circular values.");
2468
2489
  }
2469
- seen.add(value);
2490
+ stack.add(value);
2470
2491
 
2471
2492
  const tag = Object.prototype.toString.call(value);
2472
2493
  if (tag === "[object File]" || tag === "[object Blob]" || tag === "[object FormData]") {
@@ -2474,13 +2495,15 @@
2474
2495
  }
2475
2496
  if (Array.isArray(value)) {
2476
2497
  for (const item of value) {
2477
- assertJsonTransportable(item, seen);
2498
+ assertJsonTransportable(item, stack);
2478
2499
  }
2500
+ stack.delete(value);
2479
2501
  return;
2480
2502
  }
2481
2503
  for (const item of Object.values(value)) {
2482
- assertJsonTransportable(item, seen);
2504
+ assertJsonTransportable(item, stack);
2483
2505
  }
2506
+ stack.delete(value);
2484
2507
  }
2485
2508
 
2486
2509
  function joinEndpoint(endpoint, id) {
@@ -3125,6 +3148,7 @@
3125
3148
  return;
3126
3149
  }
3127
3150
  destroyed = true;
3151
+ markDestroyedScopes(rootNode);
3128
3152
  for (const cleanup of [...cleanups]) {
3129
3153
  runCleanup(cleanup);
3130
3154
  }
@@ -3575,6 +3599,12 @@
3575
3599
  });
3576
3600
  }
3577
3601
 
3602
+ function markDestroyedScopes(scope) {
3603
+ for (const element of elementsIn(scope)) {
3604
+ schedulerInstance.markScopeDestroyed(element);
3605
+ }
3606
+ }
3607
+
3578
3608
  return api;
3579
3609
  }
3580
3610
 
@@ -4000,7 +4030,7 @@
4000
4030
  }
4001
4031
  const params = {};
4002
4032
  candidate.keys.forEach((key, index) => {
4003
- params[key] = decodeURIComponent(match[index + 1] ?? "");
4033
+ params[key] = safeDecodeURIComponent(match[index + 1] ?? "");
4004
4034
  });
4005
4035
  return {
4006
4036
  pattern: candidate.pattern,
@@ -4115,11 +4145,11 @@
4115
4145
  }
4116
4146
  bindNavigation();
4117
4147
  if (mode === "csr") {
4118
- void api.navigate(currentUrl(), {
4148
+ handleNavigation(api.navigate(currentUrl(), {
4119
4149
  replace: true,
4120
4150
  initial: true,
4121
4151
  source: "client"
4122
- }).catch(() => {});
4152
+ }));
4123
4153
  return api;
4124
4154
  }
4125
4155
  updateStateFromLocation();
@@ -4184,7 +4214,7 @@
4184
4214
  return;
4185
4215
  }
4186
4216
  event.preventDefault();
4187
- api.navigate(anchor.href);
4217
+ handleNavigation(api.navigate(anchor.href));
4188
4218
  };
4189
4219
  const submit = (event) => {
4190
4220
  const form = closest(event.target, "form");
@@ -4192,9 +4222,9 @@
4192
4222
  return;
4193
4223
  }
4194
4224
  event.preventDefault();
4195
- api.navigate(formActionUrl(form));
4225
+ handleNavigation(api.navigate(formActionUrl(form)));
4196
4226
  };
4197
- const popstate = () => api.navigate(currentUrl(), { history: false });
4227
+ const popstate = () => handleNavigation(api.navigate(currentUrl(), { history: false }));
4198
4228
 
4199
4229
  rootNode.addEventListener?.("click", click);
4200
4230
  rootNode.addEventListener?.("submit", submit);
@@ -4387,6 +4417,19 @@
4387
4417
  }
4388
4418
  }
4389
4419
 
4420
+ function handleNavigation(promise) {
4421
+ void promise.catch((error) => {
4422
+ if (destroyed) {
4423
+ return;
4424
+ }
4425
+ setRouterState({
4426
+ pending: false,
4427
+ error
4428
+ });
4429
+ dispatchAsyncError(rootNode, error);
4430
+ });
4431
+ }
4432
+
4390
4433
  function currentUrl() {
4391
4434
  return resolveUrl(documentRef.defaultView?.location?.href ?? "http://localhost/");
4392
4435
  }
@@ -4403,6 +4446,33 @@
4403
4446
  throw new Error("Router has been destroyed.");
4404
4447
  }
4405
4448
  }
4449
+
4450
+ function shouldIgnoreLink(event, anchor) {
4451
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
4452
+ return true;
4453
+ }
4454
+ if (anchor.target || anchor.hasAttribute("download")) {
4455
+ return true;
4456
+ }
4457
+ const target = resolveUrl(anchor.href);
4458
+ const current = currentUrl();
4459
+ if (target.origin !== current.origin) {
4460
+ return true;
4461
+ }
4462
+ return isHashOnlyNavigation(target, current, anchor);
4463
+ }
4464
+
4465
+ function shouldIgnoreForm(form) {
4466
+ const method = String(form.method || "get").toLowerCase();
4467
+ return method !== "get" || resolveUrl(form.action).origin !== currentUrl().origin;
4468
+ }
4469
+
4470
+ function formActionUrl(form) {
4471
+ const url = resolveUrl(form.action || form.ownerDocument.defaultView.location.href);
4472
+ const formData = new form.ownerDocument.defaultView.FormData(form);
4473
+ url.search = new URLSearchParams(formData).toString();
4474
+ return url.href;
4475
+ }
4406
4476
  }
4407
4477
 
4408
4478
  function normalizeRoute(pattern, definition) {
@@ -4440,28 +4510,6 @@
4440
4510
  return { regex: new RegExp(`^${source}$`), keys };
4441
4511
  }
4442
4512
 
4443
- function shouldIgnoreLink(event, anchor) {
4444
- if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
4445
- return true;
4446
- }
4447
- if (anchor.target || anchor.hasAttribute("download")) {
4448
- return true;
4449
- }
4450
- return toUrl(anchor.href).origin !== toUrl(anchor.ownerDocument.defaultView.location.href).origin;
4451
- }
4452
-
4453
- function shouldIgnoreForm(form) {
4454
- const method = String(form.method || "get").toLowerCase();
4455
- return method !== "get" || toUrl(form.action).origin !== toUrl(form.ownerDocument.defaultView.location.href).origin;
4456
- }
4457
-
4458
- function formActionUrl(form) {
4459
- const url = toUrl(form.action || form.ownerDocument.defaultView.location.href);
4460
- const formData = new form.ownerDocument.defaultView.FormData(form);
4461
- url.search = new URLSearchParams(formData).toString();
4462
- return url.href;
4463
- }
4464
-
4465
4513
  function closest(target, selector) {
4466
4514
  return target?.closest?.(selector);
4467
4515
  }
@@ -4477,6 +4525,34 @@
4477
4525
  return Object.fromEntries(url.searchParams.entries());
4478
4526
  }
4479
4527
 
4528
+ function safeDecodeURIComponent(value) {
4529
+ try {
4530
+ return decodeURIComponent(value);
4531
+ } catch {
4532
+ return value;
4533
+ }
4534
+ }
4535
+
4536
+ function isHashOnlyNavigation(target, current, anchor) {
4537
+ if (target.origin !== current.origin || target.pathname !== current.pathname || target.search !== current.search) {
4538
+ return false;
4539
+ }
4540
+ return target.hash !== current.hash || anchor.getAttribute?.("href")?.startsWith("#") === true;
4541
+ }
4542
+
4543
+ function dispatchAsyncError(element, error) {
4544
+ const EventCtor = element.ownerDocument?.defaultView?.CustomEvent ?? globalThis.CustomEvent;
4545
+ if (typeof EventCtor !== "function") {
4546
+ return;
4547
+ }
4548
+ element.dispatchEvent?.(
4549
+ new EventCtor("async:error", {
4550
+ bubbles: true,
4551
+ detail: { error }
4552
+ })
4553
+ );
4554
+ }
4555
+
4480
4556
  function escapeRegExp(value) {
4481
4557
  return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4482
4558
  }