@makeswift/runtime 0.28.1-canary.1 → 0.28.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/dist/cjs/api-handler/handlers/manifest.js +1 -1
- package/dist/cjs/client/index.js +3 -3
- package/dist/cjs/global.d.js.map +1 -1
- package/dist/cjs/state/builder-api/api.js.map +1 -1
- package/dist/cjs/state/builder-api/navigation-listener.js +31 -11
- package/dist/cjs/state/builder-api/navigation-listener.js.map +1 -1
- package/dist/esm/api-handler/handlers/manifest.js +1 -1
- package/dist/esm/client/index.js +3 -3
- package/dist/esm/global.d.js.map +1 -1
- package/dist/esm/state/builder-api/navigation-listener.js +31 -11
- package/dist/esm/state/builder-api/navigation-listener.js.map +1 -1
- package/dist/types/state/builder-api/api.d.ts +2 -1
- package/dist/types/state/builder-api/api.d.ts.map +1 -1
- package/dist/types/state/builder-api/navigation-listener.d.ts +2 -5
- package/dist/types/state/builder-api/navigation-listener.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -28,7 +28,7 @@ async function manifestHandler(req, { apiKey, manifest }) {
|
|
|
28
28
|
return import_request_response.ApiResponse.json({ message: "Unauthorized" }, { status: 401 });
|
|
29
29
|
}
|
|
30
30
|
return import_request_response.ApiResponse.json({
|
|
31
|
-
version: "0.28.1
|
|
31
|
+
version: "0.28.1",
|
|
32
32
|
interactionMode: true,
|
|
33
33
|
clientSideNavigation: false,
|
|
34
34
|
elementFromPoint: false,
|
package/dist/cjs/client/index.js
CHANGED
|
@@ -211,7 +211,7 @@ Received "${apiKey}" instead.`
|
|
|
211
211
|
}
|
|
212
212
|
this.apiKey = apiKey;
|
|
213
213
|
this.graphqlClient = new import_client.GraphQLClient(new URL("graphql", runtime.apiOrigin).href, {
|
|
214
|
-
"makeswift-runtime-version": "0.28.1
|
|
214
|
+
"makeswift-runtime-version": "0.28.1"
|
|
215
215
|
});
|
|
216
216
|
this.runtime = runtime;
|
|
217
217
|
}
|
|
@@ -223,7 +223,7 @@ Received "${apiKey}" instead.`
|
|
|
223
223
|
const requestHeaders = new Headers({
|
|
224
224
|
"x-api-key": this.apiKey,
|
|
225
225
|
"makeswift-site-api-key": this.apiKey,
|
|
226
|
-
"makeswift-runtime-version": "0.28.1
|
|
226
|
+
"makeswift-runtime-version": "0.28.1"
|
|
227
227
|
});
|
|
228
228
|
if (siteVersion?.token) {
|
|
229
229
|
requestUrl.searchParams.set("version", siteVersion.version);
|
|
@@ -681,7 +681,7 @@ Received "${apiKey}" instead.`
|
|
|
681
681
|
headers: {
|
|
682
682
|
"x-api-key": this.apiKey,
|
|
683
683
|
"makeswift-site-api-key": this.apiKey,
|
|
684
|
-
"makeswift-runtime-version": "0.28.1
|
|
684
|
+
"makeswift-runtime-version": "0.28.1",
|
|
685
685
|
"content-type": "application/json"
|
|
686
686
|
},
|
|
687
687
|
body: JSON.stringify({ token }),
|
package/dist/cjs/global.d.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/global.d.ts"],"sourcesContent":["import '@emotion/jest'\n\ndeclare global {\n const PACKAGE_VERSION: string\n\n // partial typings for the new Navigation API, see\n // https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API\n interface NavigateEvent extends Event {\n readonly navigationType: 'push' | 'reload' | 'replace' | 'traverse'\n readonly destination: {\n readonly id: string\n readonly index: number\n readonly key: string\n readonly sameDocument: boolean\n readonly url: string\n }\n\n readonly hashChange: boolean\n readonly userInitiated: boolean\n }\n\n interface Navigation {\n addEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n removeEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n }\n\n interface Window {\n navigation?: Navigation\n }\n}\n\nexport {}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;AAAA,kBAAO;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/global.d.ts"],"sourcesContent":["import '@emotion/jest'\n\ndeclare global {\n const PACKAGE_VERSION: string\n\n // partial typings for the new Navigation API, see\n // https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API\n interface NavigateEvent extends Event {\n readonly navigationType: 'push' | 'reload' | 'replace' | 'traverse'\n readonly destination: {\n readonly id: string\n readonly index: number\n readonly key: string\n readonly sameDocument: boolean\n readonly url: string\n }\n\n readonly hashChange: boolean\n readonly userInitiated: boolean\n }\n\n interface NavigationHistoryEntry {\n readonly id: string\n readonly index: number\n readonly key: string\n readonly sameDocument: boolean\n readonly url: string | null\n }\n\n interface Navigation {\n addEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n removeEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n addEventListener(type: 'navigatesuccess', listener: (event: Event) => void): void\n removeEventListener(type: 'navigatesuccess', listener: (event: Event) => void): void\n\n readonly currentEntry: NavigationHistoryEntry\n }\n\n interface Window {\n navigation?: Navigation\n }\n}\n\nexport {}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;AAAA,kBAAO;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/state/builder-api/api.ts"],"sourcesContent":["export type HostNavigationEvent = {\n url: string | null\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/state/builder-api/api.ts"],"sourcesContent":["export type HostNavigationEvent = {\n url: string | null\n navigationCompleted?: 'initial-page-load' | 'client-side-navigation'\n // indicates whether the navigation event was captured via the Navigation API or\n // via our polyfill for browsers lacking Navigation API support\n polyfilled?: boolean\n}\n\nexport type BuilderApi = {\n handleHostNavigate(event: HostNavigationEvent): void\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -22,16 +22,17 @@ __export(navigation_listener_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(navigation_listener_exports);
|
|
24
24
|
const CLICK_NAVIGATION_THRESHOLD_MS = 100;
|
|
25
|
-
const
|
|
25
|
+
const CLICK_NAVIGATION_CHECK_INTERVAL_MS = 200;
|
|
26
|
+
const CLICK_NAVIGATION_MAX_CHECKS = 20;
|
|
26
27
|
function setupNavigationListener(callback) {
|
|
27
28
|
let previousLocation = null;
|
|
28
29
|
const handleNavigate = (event) => {
|
|
29
|
-
if (event.url === previousLocation)
|
|
30
|
+
if (!event.navigationCompleted && event.url === previousLocation)
|
|
30
31
|
return;
|
|
31
32
|
callback(event);
|
|
32
33
|
previousLocation = event.url;
|
|
33
34
|
};
|
|
34
|
-
handleNavigate({ url: windowLocation(),
|
|
35
|
+
handleNavigate({ url: windowLocation(), navigationCompleted: "initial-page-load" });
|
|
35
36
|
if (typeof window === "undefined") {
|
|
36
37
|
return () => {
|
|
37
38
|
};
|
|
@@ -39,9 +40,19 @@ function setupNavigationListener(callback) {
|
|
|
39
40
|
const unsubscribes = [];
|
|
40
41
|
if ("navigation" in window && window.navigation) {
|
|
41
42
|
const navigation = window.navigation;
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const navigateHandler = ({ destination }) => handleNavigate({ url: destination.url });
|
|
44
|
+
const navigateSuccessHandler = () => {
|
|
45
|
+
handleNavigate({
|
|
46
|
+
url: navigation.currentEntry.url,
|
|
47
|
+
navigationCompleted: "client-side-navigation"
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
navigation.addEventListener("navigate", navigateHandler);
|
|
51
|
+
navigation.addEventListener("navigatesuccess", navigateSuccessHandler);
|
|
52
|
+
unsubscribes.push(() => navigation.removeEventListener("navigate", navigateHandler));
|
|
53
|
+
unsubscribes.push(
|
|
54
|
+
() => navigation.removeEventListener("navigatesuccess", navigateSuccessHandler)
|
|
55
|
+
);
|
|
45
56
|
return () => {
|
|
46
57
|
unsubscribes.forEach((u) => u());
|
|
47
58
|
};
|
|
@@ -52,10 +63,19 @@ function setupNavigationListener(callback) {
|
|
|
52
63
|
if (!a)
|
|
53
64
|
return;
|
|
54
65
|
lastClickEvent = { href: a.href, timestamp: Date.now() };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
const pageUrlBeforeClick = windowLocation();
|
|
67
|
+
let navigationCheckCounter = 0;
|
|
68
|
+
const checkIfNavigationOccurred = () => {
|
|
69
|
+
const url = windowLocation();
|
|
70
|
+
if (url !== pageUrlBeforeClick) {
|
|
71
|
+
handleNavigate({ url, polyfilled: true });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (++navigationCheckCounter < CLICK_NAVIGATION_MAX_CHECKS) {
|
|
75
|
+
window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS);
|
|
59
79
|
};
|
|
60
80
|
window.document.addEventListener(
|
|
61
81
|
"click",
|
|
@@ -72,7 +92,7 @@ function setupNavigationListener(callback) {
|
|
|
72
92
|
const msSinceLastClick = Date.now() - lastClickEvent.timestamp;
|
|
73
93
|
handleNavigate({
|
|
74
94
|
url: msSinceLastClick < CLICK_NAVIGATION_THRESHOLD_MS ? lastClickEvent.href ?? null : null,
|
|
75
|
-
|
|
95
|
+
polyfilled: true
|
|
76
96
|
});
|
|
77
97
|
lastClickEvent = null;
|
|
78
98
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/state/builder-api/navigation-listener.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../../src/state/builder-api/navigation-listener.ts"],"sourcesContent":["import { type HostNavigationEvent } from './api'\n\ntype ClickEvent = {\n href: string\n timestamp: number\n}\n\nconst CLICK_NAVIGATION_THRESHOLD_MS = 100\nconst CLICK_NAVIGATION_CHECK_INTERVAL_MS = 200\nconst CLICK_NAVIGATION_MAX_CHECKS = 20\n\nexport function setupNavigationListener(\n callback: (args: HostNavigationEvent) => void,\n): VoidFunction {\n let previousLocation: string | null = null\n\n const handleNavigate = (event: HostNavigationEvent) => {\n if (!event.navigationCompleted && event.url === previousLocation) return\n\n callback(event)\n previousLocation = event.url\n }\n\n // trigger navigation callback on initial page load\n handleNavigate({ url: windowLocation(), navigationCompleted: 'initial-page-load' })\n\n if (typeof window === 'undefined') {\n return () => {}\n }\n\n const unsubscribes: (() => void)[] = []\n\n // check for availability of the Navigation API (baseline feature since January 2026),\n // see https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API\n if ('navigation' in window && window.navigation) {\n const navigation = window.navigation\n\n const navigateHandler = ({ destination }: NavigateEvent) =>\n handleNavigate({ url: destination.url })\n\n const navigateSuccessHandler = () => {\n handleNavigate({\n url: navigation.currentEntry.url,\n navigationCompleted: 'client-side-navigation',\n })\n }\n\n // note that in order to capture destination URLs that might not be Makeswift-enabled,\n // we send a `SiteNavigationEvent` at the start of navigation, but do not track whether\n // the navigation was successful or not (possible future improvement)\n navigation.addEventListener('navigate', navigateHandler)\n navigation.addEventListener('navigatesuccess', navigateSuccessHandler)\n unsubscribes.push(() => navigation.removeEventListener('navigate', navigateHandler))\n unsubscribes.push(() =>\n navigation.removeEventListener('navigatesuccess', navigateSuccessHandler),\n )\n\n return () => {\n unsubscribes.forEach(u => u())\n }\n }\n\n // for browsers lacking Navigation API support, we manually track:\n // - link clicks to capture destination URLs\n // - page unload events to detect cross-page navigation\n //\n // this works well enough to keep this polyfill in place for now, but not nearly as\n // reliably as the Navigation API\n let lastClickEvent: ClickEvent | null = null\n\n const clickHandler = (e: MouseEvent) => {\n const a =\n e.composedPath?.()?.find(n => n instanceof HTMLAnchorElement) ??\n (e.target instanceof Element && e.target.closest?.('a'))\n\n if (!a) return\n\n lastClickEvent = { href: a.href, timestamp: Date.now() }\n\n const pageUrlBeforeClick = windowLocation()\n let navigationCheckCounter = 0\n\n // handle navigation between pages in the host; note that we intentionally are\n // not cancelling the timer on cleanup to ensure we report the navigation\n const checkIfNavigationOccurred = () => {\n const url = windowLocation()\n\n if (url !== pageUrlBeforeClick) {\n // the host navigated to a different page, report the new URL to the builder\n handleNavigate({ url, polyfilled: true })\n return\n }\n\n // we're still on the same page, recheck until the max number of checks is reached\n if (++navigationCheckCounter < CLICK_NAVIGATION_MAX_CHECKS) {\n window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS)\n }\n }\n\n window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS)\n }\n\n window.document.addEventListener(\n 'click',\n clickHandler,\n { capture: true }, // run before bubbling to fortify against `stopPropagation()` calls\n )\n\n unsubscribes.push(() =>\n window.document.removeEventListener('click', clickHandler, { capture: true }),\n )\n\n // handle external navigation\n const unloadHandler = () => {\n if (!lastClickEvent) return\n\n const msSinceLastClick = Date.now() - lastClickEvent.timestamp\n handleNavigate({\n url: msSinceLastClick < CLICK_NAVIGATION_THRESHOLD_MS ? (lastClickEvent.href ?? null) : null,\n polyfilled: true,\n })\n\n lastClickEvent = null\n }\n\n window.addEventListener('beforeunload', unloadHandler)\n unsubscribes.push(() => window.removeEventListener('beforeunload', unloadHandler))\n\n return () => {\n unsubscribes.forEach(u => u())\n }\n}\n\nconst windowLocation = (): string | null =>\n typeof window !== 'undefined' ? window.location.href : null\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,MAAM,gCAAgC;AACtC,MAAM,qCAAqC;AAC3C,MAAM,8BAA8B;AAE7B,SAAS,wBACd,UACc;AACd,MAAI,mBAAkC;AAEtC,QAAM,iBAAiB,CAAC,UAA+B;AACrD,QAAI,CAAC,MAAM,uBAAuB,MAAM,QAAQ;AAAkB;AAElE,aAAS,KAAK;AACd,uBAAmB,MAAM;AAAA,EAC3B;AAGA,iBAAe,EAAE,KAAK,eAAe,GAAG,qBAAqB,oBAAoB,CAAC;AAElF,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,QAAM,eAA+B,CAAC;AAItC,MAAI,gBAAgB,UAAU,OAAO,YAAY;AAC/C,UAAM,aAAa,OAAO;AAE1B,UAAM,kBAAkB,CAAC,EAAE,YAAY,MACrC,eAAe,EAAE,KAAK,YAAY,IAAI,CAAC;AAEzC,UAAM,yBAAyB,MAAM;AACnC,qBAAe;AAAA,QACb,KAAK,WAAW,aAAa;AAAA,QAC7B,qBAAqB;AAAA,MACvB,CAAC;AAAA,IACH;AAKA,eAAW,iBAAiB,YAAY,eAAe;AACvD,eAAW,iBAAiB,mBAAmB,sBAAsB;AACrE,iBAAa,KAAK,MAAM,WAAW,oBAAoB,YAAY,eAAe,CAAC;AACnF,iBAAa;AAAA,MAAK,MAChB,WAAW,oBAAoB,mBAAmB,sBAAsB;AAAA,IAC1E;AAEA,WAAO,MAAM;AACX,mBAAa,QAAQ,OAAK,EAAE,CAAC;AAAA,IAC/B;AAAA,EACF;AAQA,MAAI,iBAAoC;AAExC,QAAM,eAAe,CAAC,MAAkB;AACtC,UAAM,IACJ,EAAE,eAAe,GAAG,KAAK,OAAK,aAAa,iBAAiB,MAC3D,EAAE,kBAAkB,WAAW,EAAE,OAAO,UAAU,GAAG;AAExD,QAAI,CAAC;AAAG;AAER,qBAAiB,EAAE,MAAM,EAAE,MAAM,WAAW,KAAK,IAAI,EAAE;AAEvD,UAAM,qBAAqB,eAAe;AAC1C,QAAI,yBAAyB;AAI7B,UAAM,4BAA4B,MAAM;AACtC,YAAM,MAAM,eAAe;AAE3B,UAAI,QAAQ,oBAAoB;AAE9B,uBAAe,EAAE,KAAK,YAAY,KAAK,CAAC;AACxC;AAAA,MACF;AAGA,UAAI,EAAE,yBAAyB,6BAA6B;AAC1D,eAAO,WAAW,2BAA2B,kCAAkC;AAAA,MACjF;AAAA,IACF;AAEA,WAAO,WAAW,2BAA2B,kCAAkC;AAAA,EACjF;AAEA,SAAO,SAAS;AAAA,IACd;AAAA,IACA;AAAA,IACA,EAAE,SAAS,KAAK;AAAA;AAAA,EAClB;AAEA,eAAa;AAAA,IAAK,MAChB,OAAO,SAAS,oBAAoB,SAAS,cAAc,EAAE,SAAS,KAAK,CAAC;AAAA,EAC9E;AAGA,QAAM,gBAAgB,MAAM;AAC1B,QAAI,CAAC;AAAgB;AAErB,UAAM,mBAAmB,KAAK,IAAI,IAAI,eAAe;AACrD,mBAAe;AAAA,MACb,KAAK,mBAAmB,gCAAiC,eAAe,QAAQ,OAAQ;AAAA,MACxF,YAAY;AAAA,IACd,CAAC;AAED,qBAAiB;AAAA,EACnB;AAEA,SAAO,iBAAiB,gBAAgB,aAAa;AACrD,eAAa,KAAK,MAAM,OAAO,oBAAoB,gBAAgB,aAAa,CAAC;AAEjF,SAAO,MAAM;AACX,iBAAa,QAAQ,OAAK,EAAE,CAAC;AAAA,EAC/B;AACF;AAEA,MAAM,iBAAiB,MACrB,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;","names":[]}
|
|
@@ -8,7 +8,7 @@ async function manifestHandler(req, { apiKey, manifest }) {
|
|
|
8
8
|
return ApiResponse.json({ message: "Unauthorized" }, { status: 401 });
|
|
9
9
|
}
|
|
10
10
|
return ApiResponse.json({
|
|
11
|
-
version: "0.28.1
|
|
11
|
+
version: "0.28.1",
|
|
12
12
|
interactionMode: true,
|
|
13
13
|
clientSideNavigation: false,
|
|
14
14
|
elementFromPoint: false,
|
package/dist/esm/client/index.js
CHANGED
|
@@ -195,7 +195,7 @@ Received "${apiKey}" instead.`
|
|
|
195
195
|
}
|
|
196
196
|
this.apiKey = apiKey;
|
|
197
197
|
this.graphqlClient = new GraphQLClient(new URL("graphql", runtime.apiOrigin).href, {
|
|
198
|
-
"makeswift-runtime-version": "0.28.1
|
|
198
|
+
"makeswift-runtime-version": "0.28.1"
|
|
199
199
|
});
|
|
200
200
|
this.runtime = runtime;
|
|
201
201
|
}
|
|
@@ -207,7 +207,7 @@ Received "${apiKey}" instead.`
|
|
|
207
207
|
const requestHeaders = new Headers({
|
|
208
208
|
"x-api-key": this.apiKey,
|
|
209
209
|
"makeswift-site-api-key": this.apiKey,
|
|
210
|
-
"makeswift-runtime-version": "0.28.1
|
|
210
|
+
"makeswift-runtime-version": "0.28.1"
|
|
211
211
|
});
|
|
212
212
|
if (siteVersion?.token) {
|
|
213
213
|
requestUrl.searchParams.set("version", siteVersion.version);
|
|
@@ -665,7 +665,7 @@ Received "${apiKey}" instead.`
|
|
|
665
665
|
headers: {
|
|
666
666
|
"x-api-key": this.apiKey,
|
|
667
667
|
"makeswift-site-api-key": this.apiKey,
|
|
668
|
-
"makeswift-runtime-version": "0.28.1
|
|
668
|
+
"makeswift-runtime-version": "0.28.1",
|
|
669
669
|
"content-type": "application/json"
|
|
670
670
|
},
|
|
671
671
|
body: JSON.stringify({ token }),
|
package/dist/esm/global.d.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/global.d.ts"],"sourcesContent":["import '@emotion/jest'\n\ndeclare global {\n const PACKAGE_VERSION: string\n\n // partial typings for the new Navigation API, see\n // https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API\n interface NavigateEvent extends Event {\n readonly navigationType: 'push' | 'reload' | 'replace' | 'traverse'\n readonly destination: {\n readonly id: string\n readonly index: number\n readonly key: string\n readonly sameDocument: boolean\n readonly url: string\n }\n\n readonly hashChange: boolean\n readonly userInitiated: boolean\n }\n\n interface Navigation {\n addEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n removeEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n }\n\n interface Window {\n navigation?: Navigation\n }\n}\n\nexport {}\n"],"mappings":"AAAA,OAAO;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/global.d.ts"],"sourcesContent":["import '@emotion/jest'\n\ndeclare global {\n const PACKAGE_VERSION: string\n\n // partial typings for the new Navigation API, see\n // https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API\n interface NavigateEvent extends Event {\n readonly navigationType: 'push' | 'reload' | 'replace' | 'traverse'\n readonly destination: {\n readonly id: string\n readonly index: number\n readonly key: string\n readonly sameDocument: boolean\n readonly url: string\n }\n\n readonly hashChange: boolean\n readonly userInitiated: boolean\n }\n\n interface NavigationHistoryEntry {\n readonly id: string\n readonly index: number\n readonly key: string\n readonly sameDocument: boolean\n readonly url: string | null\n }\n\n interface Navigation {\n addEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n removeEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void\n addEventListener(type: 'navigatesuccess', listener: (event: Event) => void): void\n removeEventListener(type: 'navigatesuccess', listener: (event: Event) => void): void\n\n readonly currentEntry: NavigationHistoryEntry\n }\n\n interface Window {\n navigation?: Navigation\n }\n}\n\nexport {}\n"],"mappings":"AAAA,OAAO;","names":[]}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
const CLICK_NAVIGATION_THRESHOLD_MS = 100;
|
|
2
|
-
const
|
|
2
|
+
const CLICK_NAVIGATION_CHECK_INTERVAL_MS = 200;
|
|
3
|
+
const CLICK_NAVIGATION_MAX_CHECKS = 20;
|
|
3
4
|
function setupNavigationListener(callback) {
|
|
4
5
|
let previousLocation = null;
|
|
5
6
|
const handleNavigate = (event) => {
|
|
6
|
-
if (event.url === previousLocation)
|
|
7
|
+
if (!event.navigationCompleted && event.url === previousLocation)
|
|
7
8
|
return;
|
|
8
9
|
callback(event);
|
|
9
10
|
previousLocation = event.url;
|
|
10
11
|
};
|
|
11
|
-
handleNavigate({ url: windowLocation(),
|
|
12
|
+
handleNavigate({ url: windowLocation(), navigationCompleted: "initial-page-load" });
|
|
12
13
|
if (typeof window === "undefined") {
|
|
13
14
|
return () => {
|
|
14
15
|
};
|
|
@@ -16,9 +17,19 @@ function setupNavigationListener(callback) {
|
|
|
16
17
|
const unsubscribes = [];
|
|
17
18
|
if ("navigation" in window && window.navigation) {
|
|
18
19
|
const navigation = window.navigation;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const navigateHandler = ({ destination }) => handleNavigate({ url: destination.url });
|
|
21
|
+
const navigateSuccessHandler = () => {
|
|
22
|
+
handleNavigate({
|
|
23
|
+
url: navigation.currentEntry.url,
|
|
24
|
+
navigationCompleted: "client-side-navigation"
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
navigation.addEventListener("navigate", navigateHandler);
|
|
28
|
+
navigation.addEventListener("navigatesuccess", navigateSuccessHandler);
|
|
29
|
+
unsubscribes.push(() => navigation.removeEventListener("navigate", navigateHandler));
|
|
30
|
+
unsubscribes.push(
|
|
31
|
+
() => navigation.removeEventListener("navigatesuccess", navigateSuccessHandler)
|
|
32
|
+
);
|
|
22
33
|
return () => {
|
|
23
34
|
unsubscribes.forEach((u) => u());
|
|
24
35
|
};
|
|
@@ -29,10 +40,19 @@ function setupNavigationListener(callback) {
|
|
|
29
40
|
if (!a)
|
|
30
41
|
return;
|
|
31
42
|
lastClickEvent = { href: a.href, timestamp: Date.now() };
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const pageUrlBeforeClick = windowLocation();
|
|
44
|
+
let navigationCheckCounter = 0;
|
|
45
|
+
const checkIfNavigationOccurred = () => {
|
|
46
|
+
const url = windowLocation();
|
|
47
|
+
if (url !== pageUrlBeforeClick) {
|
|
48
|
+
handleNavigate({ url, polyfilled: true });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (++navigationCheckCounter < CLICK_NAVIGATION_MAX_CHECKS) {
|
|
52
|
+
window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS);
|
|
36
56
|
};
|
|
37
57
|
window.document.addEventListener(
|
|
38
58
|
"click",
|
|
@@ -49,7 +69,7 @@ function setupNavigationListener(callback) {
|
|
|
49
69
|
const msSinceLastClick = Date.now() - lastClickEvent.timestamp;
|
|
50
70
|
handleNavigate({
|
|
51
71
|
url: msSinceLastClick < CLICK_NAVIGATION_THRESHOLD_MS ? lastClickEvent.href ?? null : null,
|
|
52
|
-
|
|
72
|
+
polyfilled: true
|
|
53
73
|
});
|
|
54
74
|
lastClickEvent = null;
|
|
55
75
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/state/builder-api/navigation-listener.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../../src/state/builder-api/navigation-listener.ts"],"sourcesContent":["import { type HostNavigationEvent } from './api'\n\ntype ClickEvent = {\n href: string\n timestamp: number\n}\n\nconst CLICK_NAVIGATION_THRESHOLD_MS = 100\nconst CLICK_NAVIGATION_CHECK_INTERVAL_MS = 200\nconst CLICK_NAVIGATION_MAX_CHECKS = 20\n\nexport function setupNavigationListener(\n callback: (args: HostNavigationEvent) => void,\n): VoidFunction {\n let previousLocation: string | null = null\n\n const handleNavigate = (event: HostNavigationEvent) => {\n if (!event.navigationCompleted && event.url === previousLocation) return\n\n callback(event)\n previousLocation = event.url\n }\n\n // trigger navigation callback on initial page load\n handleNavigate({ url: windowLocation(), navigationCompleted: 'initial-page-load' })\n\n if (typeof window === 'undefined') {\n return () => {}\n }\n\n const unsubscribes: (() => void)[] = []\n\n // check for availability of the Navigation API (baseline feature since January 2026),\n // see https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API\n if ('navigation' in window && window.navigation) {\n const navigation = window.navigation\n\n const navigateHandler = ({ destination }: NavigateEvent) =>\n handleNavigate({ url: destination.url })\n\n const navigateSuccessHandler = () => {\n handleNavigate({\n url: navigation.currentEntry.url,\n navigationCompleted: 'client-side-navigation',\n })\n }\n\n // note that in order to capture destination URLs that might not be Makeswift-enabled,\n // we send a `SiteNavigationEvent` at the start of navigation, but do not track whether\n // the navigation was successful or not (possible future improvement)\n navigation.addEventListener('navigate', navigateHandler)\n navigation.addEventListener('navigatesuccess', navigateSuccessHandler)\n unsubscribes.push(() => navigation.removeEventListener('navigate', navigateHandler))\n unsubscribes.push(() =>\n navigation.removeEventListener('navigatesuccess', navigateSuccessHandler),\n )\n\n return () => {\n unsubscribes.forEach(u => u())\n }\n }\n\n // for browsers lacking Navigation API support, we manually track:\n // - link clicks to capture destination URLs\n // - page unload events to detect cross-page navigation\n //\n // this works well enough to keep this polyfill in place for now, but not nearly as\n // reliably as the Navigation API\n let lastClickEvent: ClickEvent | null = null\n\n const clickHandler = (e: MouseEvent) => {\n const a =\n e.composedPath?.()?.find(n => n instanceof HTMLAnchorElement) ??\n (e.target instanceof Element && e.target.closest?.('a'))\n\n if (!a) return\n\n lastClickEvent = { href: a.href, timestamp: Date.now() }\n\n const pageUrlBeforeClick = windowLocation()\n let navigationCheckCounter = 0\n\n // handle navigation between pages in the host; note that we intentionally are\n // not cancelling the timer on cleanup to ensure we report the navigation\n const checkIfNavigationOccurred = () => {\n const url = windowLocation()\n\n if (url !== pageUrlBeforeClick) {\n // the host navigated to a different page, report the new URL to the builder\n handleNavigate({ url, polyfilled: true })\n return\n }\n\n // we're still on the same page, recheck until the max number of checks is reached\n if (++navigationCheckCounter < CLICK_NAVIGATION_MAX_CHECKS) {\n window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS)\n }\n }\n\n window.setTimeout(checkIfNavigationOccurred, CLICK_NAVIGATION_CHECK_INTERVAL_MS)\n }\n\n window.document.addEventListener(\n 'click',\n clickHandler,\n { capture: true }, // run before bubbling to fortify against `stopPropagation()` calls\n )\n\n unsubscribes.push(() =>\n window.document.removeEventListener('click', clickHandler, { capture: true }),\n )\n\n // handle external navigation\n const unloadHandler = () => {\n if (!lastClickEvent) return\n\n const msSinceLastClick = Date.now() - lastClickEvent.timestamp\n handleNavigate({\n url: msSinceLastClick < CLICK_NAVIGATION_THRESHOLD_MS ? (lastClickEvent.href ?? null) : null,\n polyfilled: true,\n })\n\n lastClickEvent = null\n }\n\n window.addEventListener('beforeunload', unloadHandler)\n unsubscribes.push(() => window.removeEventListener('beforeunload', unloadHandler))\n\n return () => {\n unsubscribes.forEach(u => u())\n }\n}\n\nconst windowLocation = (): string | null =>\n typeof window !== 'undefined' ? window.location.href : null\n"],"mappings":"AAOA,MAAM,gCAAgC;AACtC,MAAM,qCAAqC;AAC3C,MAAM,8BAA8B;AAE7B,SAAS,wBACd,UACc;AACd,MAAI,mBAAkC;AAEtC,QAAM,iBAAiB,CAAC,UAA+B;AACrD,QAAI,CAAC,MAAM,uBAAuB,MAAM,QAAQ;AAAkB;AAElE,aAAS,KAAK;AACd,uBAAmB,MAAM;AAAA,EAC3B;AAGA,iBAAe,EAAE,KAAK,eAAe,GAAG,qBAAqB,oBAAoB,CAAC;AAElF,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,QAAM,eAA+B,CAAC;AAItC,MAAI,gBAAgB,UAAU,OAAO,YAAY;AAC/C,UAAM,aAAa,OAAO;AAE1B,UAAM,kBAAkB,CAAC,EAAE,YAAY,MACrC,eAAe,EAAE,KAAK,YAAY,IAAI,CAAC;AAEzC,UAAM,yBAAyB,MAAM;AACnC,qBAAe;AAAA,QACb,KAAK,WAAW,aAAa;AAAA,QAC7B,qBAAqB;AAAA,MACvB,CAAC;AAAA,IACH;AAKA,eAAW,iBAAiB,YAAY,eAAe;AACvD,eAAW,iBAAiB,mBAAmB,sBAAsB;AACrE,iBAAa,KAAK,MAAM,WAAW,oBAAoB,YAAY,eAAe,CAAC;AACnF,iBAAa;AAAA,MAAK,MAChB,WAAW,oBAAoB,mBAAmB,sBAAsB;AAAA,IAC1E;AAEA,WAAO,MAAM;AACX,mBAAa,QAAQ,OAAK,EAAE,CAAC;AAAA,IAC/B;AAAA,EACF;AAQA,MAAI,iBAAoC;AAExC,QAAM,eAAe,CAAC,MAAkB;AACtC,UAAM,IACJ,EAAE,eAAe,GAAG,KAAK,OAAK,aAAa,iBAAiB,MAC3D,EAAE,kBAAkB,WAAW,EAAE,OAAO,UAAU,GAAG;AAExD,QAAI,CAAC;AAAG;AAER,qBAAiB,EAAE,MAAM,EAAE,MAAM,WAAW,KAAK,IAAI,EAAE;AAEvD,UAAM,qBAAqB,eAAe;AAC1C,QAAI,yBAAyB;AAI7B,UAAM,4BAA4B,MAAM;AACtC,YAAM,MAAM,eAAe;AAE3B,UAAI,QAAQ,oBAAoB;AAE9B,uBAAe,EAAE,KAAK,YAAY,KAAK,CAAC;AACxC;AAAA,MACF;AAGA,UAAI,EAAE,yBAAyB,6BAA6B;AAC1D,eAAO,WAAW,2BAA2B,kCAAkC;AAAA,MACjF;AAAA,IACF;AAEA,WAAO,WAAW,2BAA2B,kCAAkC;AAAA,EACjF;AAEA,SAAO,SAAS;AAAA,IACd;AAAA,IACA;AAAA,IACA,EAAE,SAAS,KAAK;AAAA;AAAA,EAClB;AAEA,eAAa;AAAA,IAAK,MAChB,OAAO,SAAS,oBAAoB,SAAS,cAAc,EAAE,SAAS,KAAK,CAAC;AAAA,EAC9E;AAGA,QAAM,gBAAgB,MAAM;AAC1B,QAAI,CAAC;AAAgB;AAErB,UAAM,mBAAmB,KAAK,IAAI,IAAI,eAAe;AACrD,mBAAe;AAAA,MACb,KAAK,mBAAmB,gCAAiC,eAAe,QAAQ,OAAQ;AAAA,MACxF,YAAY;AAAA,IACd,CAAC;AAED,qBAAiB;AAAA,EACnB;AAEA,SAAO,iBAAiB,gBAAgB,aAAa;AACrD,eAAa,KAAK,MAAM,OAAO,oBAAoB,gBAAgB,aAAa,CAAC;AAEjF,SAAO,MAAM;AACX,iBAAa,QAAQ,OAAK,EAAE,CAAC;AAAA,EAC/B;AACF;AAEA,MAAM,iBAAiB,MACrB,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;","names":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type HostNavigationEvent = {
|
|
2
2
|
url: string | null;
|
|
3
|
-
|
|
3
|
+
navigationCompleted?: 'initial-page-load' | 'client-side-navigation';
|
|
4
|
+
polyfilled?: boolean;
|
|
4
5
|
};
|
|
5
6
|
export type BuilderApi = {
|
|
6
7
|
handleHostNavigate(event: HostNavigationEvent): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/state/builder-api/api.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/state/builder-api/api.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,wBAAwB,CAAA;IAGpE,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAA;CACrD,CAAA"}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
initialPageLoad: boolean;
|
|
4
|
-
};
|
|
5
|
-
export declare function setupNavigationListener(callback: (args: SiteNavigationEvent) => void): VoidFunction;
|
|
1
|
+
import { type HostNavigationEvent } from './api';
|
|
2
|
+
export declare function setupNavigationListener(callback: (args: HostNavigationEvent) => void): VoidFunction;
|
|
6
3
|
//# sourceMappingURL=navigation-listener.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-listener.d.ts","sourceRoot":"","sources":["../../../../src/state/builder-api/navigation-listener.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"navigation-listener.d.ts","sourceRoot":"","sources":["../../../../src/state/builder-api/navigation-listener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAWhD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,GAC5C,YAAY,CAsHd"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makeswift/runtime",
|
|
3
|
-
"version": "0.28.1
|
|
3
|
+
"version": "0.28.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "makeswift/makeswift",
|
|
@@ -239,8 +239,8 @@
|
|
|
239
239
|
"uuid": "^9.0.0",
|
|
240
240
|
"zod": "^3.21.4",
|
|
241
241
|
"@makeswift/controls": "0.1.16",
|
|
242
|
-
"@makeswift/
|
|
243
|
-
"@makeswift/
|
|
242
|
+
"@makeswift/prop-controllers": "0.4.10",
|
|
243
|
+
"@makeswift/next-plugin": "0.6.1"
|
|
244
244
|
},
|
|
245
245
|
"devDependencies": {
|
|
246
246
|
"@emotion/jest": "^11.11.0",
|