@tokenoftrust/storefront-runner 4.7.3 → 4.7.4
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.4",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* the renderer-published boot descriptor global, guarantees ONE lifecycle per document
|
|
6
6
|
* with a window-level singleton (so a nested layout that re-runs the boot island cannot
|
|
7
7
|
* double-init), wires same-document navigations to the reserved completed-navigation
|
|
8
|
-
* event, disposes on
|
|
8
|
+
* event, disposes on every `pagehide`, boots a fresh lifecycle when the back-forward
|
|
9
|
+
* cache restores the page, and is a guarded no-op off the browser.
|
|
9
10
|
*
|
|
10
11
|
* The entry module for each integration is the same-origin, exact-version asset the
|
|
11
12
|
* boot descriptor carries (`integration.entry.url`): the default loader dynamically
|
|
@@ -440,20 +441,38 @@ function deferFetchCapability(
|
|
|
440
441
|
})) as typeof fetch;
|
|
441
442
|
}
|
|
442
443
|
|
|
444
|
+
/** The window property marking that this window's event wiring is installed (once per window). */
|
|
445
|
+
const LIFECYCLE_WIRED_GLOBAL = "__TOT_INTEGRATION_LIFECYCLE_WIRED__";
|
|
446
|
+
|
|
443
447
|
/**
|
|
444
|
-
* Boot the lifecycle for `win
|
|
445
|
-
* (nested-layout safety).
|
|
448
|
+
* Boot the lifecycle for `win`. While a live lifecycle exists a second call returns it
|
|
449
|
+
* (nested-layout safety). A disposed one — the page was hidden into the back-forward
|
|
450
|
+
* cache — is replaced by a fresh lifecycle. Returns `null` when there is no boot
|
|
451
|
+
* descriptor to act on.
|
|
446
452
|
*/
|
|
447
453
|
export function bootIntegrationLifecycle(
|
|
448
454
|
win: LifecycleWindow,
|
|
449
455
|
options: BootLifecycleOptions = {},
|
|
450
456
|
): IntegrationLifecycle | null {
|
|
451
457
|
const existing = win[LIFECYCLE_SINGLETON_GLOBAL];
|
|
452
|
-
if (existing instanceof IntegrationLifecycle) return existing;
|
|
458
|
+
if (existing instanceof IntegrationLifecycle && !existing.isDisposed) return existing;
|
|
453
459
|
|
|
454
460
|
const descriptor = readDescriptor(win);
|
|
455
461
|
if (!descriptor) return null;
|
|
456
462
|
|
|
463
|
+
const lifecycle = createDocumentLifecycle(win, descriptor, options);
|
|
464
|
+
win[LIFECYCLE_SINGLETON_GLOBAL] = lifecycle;
|
|
465
|
+
wireWindow(win, options);
|
|
466
|
+
void lifecycle.init();
|
|
467
|
+
return lifecycle;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function createDocumentLifecycle(
|
|
471
|
+
win: LifecycleWindow,
|
|
472
|
+
descriptor: IntegrationBootDescriptor,
|
|
473
|
+
options: BootLifecycleOptions,
|
|
474
|
+
): IntegrationLifecycle {
|
|
475
|
+
|
|
457
476
|
const hostFetch = options.fetchCapability ??
|
|
458
477
|
(typeof win.fetch === "function" ? win.fetch.bind(win) : undefined);
|
|
459
478
|
const schedulePlatformAdapterStart =
|
|
@@ -508,7 +527,7 @@ export function bootIntegrationLifecycle(
|
|
|
508
527
|
// The managed-analytics bridge over the SAME broker the adapters subscribe to — the
|
|
509
528
|
// only seam that emits this document's governed page view. The lifecycle owns when it
|
|
510
529
|
// fires (after every adapter has subscribed, and re-attempted on a consent grant).
|
|
511
|
-
|
|
530
|
+
return new IntegrationLifecycle({
|
|
512
531
|
descriptor,
|
|
513
532
|
loadModule,
|
|
514
533
|
isEligible,
|
|
@@ -536,39 +555,48 @@ export function bootIntegrationLifecycle(
|
|
|
536
555
|
pageView: () => documentPageView(win, descriptor, options.now ?? Date.now),
|
|
537
556
|
},
|
|
538
557
|
});
|
|
539
|
-
|
|
558
|
+
}
|
|
540
559
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
return
|
|
560
|
+
/** The window's current live lifecycle, if any. */
|
|
561
|
+
function liveLifecycle(win: LifecycleWindow): IntegrationLifecycle | undefined {
|
|
562
|
+
const current = win[LIFECYCLE_SINGLETON_GLOBAL];
|
|
563
|
+
return current instanceof IntegrationLifecycle && !current.isDisposed ? current : undefined;
|
|
545
564
|
}
|
|
546
565
|
|
|
547
566
|
/**
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
* navigations
|
|
551
|
-
*
|
|
567
|
+
* Install this window's event wiring once; every listener acts on the CURRENT lifecycle.
|
|
568
|
+
*
|
|
569
|
+
* Same-document navigations — hash changes and history back/forward — emit the reserved
|
|
570
|
+
* completed-navigation event and never re-initialize integrations; a real document load
|
|
571
|
+
* re-inits by construction.
|
|
572
|
+
*
|
|
573
|
+
* Every `pagehide` disposes the lifecycle, so integrations clean up and abandon any
|
|
574
|
+
* deferred work (a held navigation never resumes after the shopper left). A
|
|
575
|
+
* `pageshow` from the back-forward cache boots a fresh lifecycle, so the restored page
|
|
576
|
+
* has live integrations again.
|
|
552
577
|
*/
|
|
553
|
-
function
|
|
578
|
+
function wireWindow(win: LifecycleWindow, options: BootLifecycleOptions): void {
|
|
579
|
+
if (win[LIFECYCLE_WIRED_GLOBAL] === true) return;
|
|
580
|
+
win[LIFECYCLE_WIRED_GLOBAL] = true;
|
|
554
581
|
let lastPath = win.location.pathname + win.location.hash;
|
|
555
582
|
win.addEventListener("hashchange", () => {
|
|
556
583
|
const to = win.location.pathname + win.location.hash;
|
|
557
|
-
|
|
584
|
+
liveLifecycle(win)?.notifyNavigationCompleted({ from: lastPath, to, kind: "hash" });
|
|
558
585
|
lastPath = to;
|
|
559
586
|
});
|
|
560
587
|
win.addEventListener("popstate", () => {
|
|
561
588
|
const to = win.location.pathname + win.location.hash;
|
|
562
589
|
const kind: NavigationKind = "back-forward";
|
|
563
|
-
|
|
590
|
+
liveLifecycle(win)?.notifyNavigationCompleted({ from: lastPath, to, kind });
|
|
564
591
|
lastPath = to;
|
|
565
592
|
});
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/** Dispose the lifecycle when the document is torn down, so integrations clean up once. */
|
|
569
|
-
function wireTeardown(win: LifecycleWindow, lifecycle: IntegrationLifecycle): void {
|
|
570
593
|
win.addEventListener("pagehide", () => {
|
|
571
|
-
|
|
594
|
+
const current = win[LIFECYCLE_SINGLETON_GLOBAL];
|
|
595
|
+
if (current instanceof IntegrationLifecycle) void current.dispose();
|
|
596
|
+
});
|
|
597
|
+
win.addEventListener("pageshow", (event) => {
|
|
598
|
+
if ((event as { persisted?: unknown } | null)?.persisted !== true) return;
|
|
599
|
+
bootIntegrationLifecycle(win, options);
|
|
572
600
|
});
|
|
573
601
|
}
|
|
574
602
|
|
|
@@ -224,6 +224,11 @@ export class IntegrationLifecycle {
|
|
|
224
224
|
return this.state === "initialized";
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/** True once dispose has started; a disposed lifecycle never runs again. */
|
|
228
|
+
get isDisposed(): boolean {
|
|
229
|
+
return this.isTerminal();
|
|
230
|
+
}
|
|
231
|
+
|
|
227
232
|
/**
|
|
228
233
|
* Initialize every integration EXACTLY ONCE per document, in the plan's resolved order.
|
|
229
234
|
* A second call — including one from a nested layout that re-runs the boot island — is a
|