@rpcbase/client 0.331.0 → 0.332.0
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/index.js +125 -0
- package/dist/initWithRoutes.d.ts.map +1 -1
- package/dist/navigationGuard/installGlobalNavigationGuard.d.ts +3 -0
- package/dist/navigationGuard/installGlobalNavigationGuard.d.ts.map +1 -0
- package/dist/navigationGuard/installGlobalNavigationGuard.test.d.ts +2 -0
- package/dist/navigationGuard/installGlobalNavigationGuard.test.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,6 +79,130 @@ const cleanupURL = () => {
|
|
|
79
79
|
document.addEventListener("DOMContentLoaded", runCleanup, { once: true });
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
|
+
const DEFAULT_PRIORITY = 0;
|
|
83
|
+
const canBlockNavigation = (guard, {
|
|
84
|
+
isPathnameChange,
|
|
85
|
+
isSearchChange
|
|
86
|
+
}) => {
|
|
87
|
+
if (!guard.enabled) return false;
|
|
88
|
+
if (isPathnameChange) return true;
|
|
89
|
+
if (isSearchChange && guard.blockOnSearch) return true;
|
|
90
|
+
return false;
|
|
91
|
+
};
|
|
92
|
+
const pickNavigationGuard = (args) => {
|
|
93
|
+
const isPathnameChange = args.currentLocation.pathname !== args.nextLocation.pathname;
|
|
94
|
+
const isSearchChange = args.currentLocation.search !== args.nextLocation.search;
|
|
95
|
+
if (!isPathnameChange && !isSearchChange) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const eligibleGuards = getNavigationGuards().filter((guard) => canBlockNavigation(guard, { isPathnameChange, isSearchChange })).filter((guard) => guard.shouldBlockNavigation(args));
|
|
99
|
+
if (eligibleGuards.length === 0) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return eligibleGuards.reduce((best, guard) => {
|
|
103
|
+
const bestPriority = best.priority ?? DEFAULT_PRIORITY;
|
|
104
|
+
const guardPriority = guard.priority ?? DEFAULT_PRIORITY;
|
|
105
|
+
return guardPriority > bestPriority ? guard : best;
|
|
106
|
+
}, eligibleGuards[0]);
|
|
107
|
+
};
|
|
108
|
+
const getLocationDedupKey = (location) => location.key ?? `${location.pathname}${location.search}`;
|
|
109
|
+
const hasUnloadBlockers = () => getNavigationGuards().some((guard) => guard.enabled && guard.shouldBlockUnload);
|
|
110
|
+
const getWindowState = () => {
|
|
111
|
+
if (typeof window === "undefined") return null;
|
|
112
|
+
const key = "__rpcbaseNavigationGuardWindowState";
|
|
113
|
+
const globalAny = window;
|
|
114
|
+
if (globalAny[key]) {
|
|
115
|
+
return globalAny[key];
|
|
116
|
+
}
|
|
117
|
+
const created = {
|
|
118
|
+
suppressBeforeUnloadUntil: 0,
|
|
119
|
+
lastBeforeUnloadAt: 0
|
|
120
|
+
};
|
|
121
|
+
globalAny[key] = created;
|
|
122
|
+
return created;
|
|
123
|
+
};
|
|
124
|
+
const NATIVE_PROMPT_COOLDOWN_MS = 1e3;
|
|
125
|
+
const SUPPRESS_BEFOREUNLOAD_MS = 1e3;
|
|
126
|
+
const getGlobalGuardWeakSet = () => {
|
|
127
|
+
const key = "__rpcbaseNavigationGuardInstalledRouters";
|
|
128
|
+
const globalAny = globalThis;
|
|
129
|
+
const existing = globalAny[key];
|
|
130
|
+
if (existing && existing instanceof WeakSet) {
|
|
131
|
+
return existing;
|
|
132
|
+
}
|
|
133
|
+
const created = /* @__PURE__ */ new WeakSet();
|
|
134
|
+
globalAny[key] = created;
|
|
135
|
+
return created;
|
|
136
|
+
};
|
|
137
|
+
const installGlobalNavigationGuard = (router) => {
|
|
138
|
+
const installedRouters = getGlobalGuardWeakSet();
|
|
139
|
+
if (installedRouters.has(router)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
installedRouters.add(router);
|
|
143
|
+
const blockerKey = "rpcbase:navigation-guards";
|
|
144
|
+
const windowState = getWindowState();
|
|
145
|
+
let lastArgs = null;
|
|
146
|
+
let lastPromptedLocationKey = null;
|
|
147
|
+
router.getBlocker(blockerKey, (args) => {
|
|
148
|
+
lastArgs = args;
|
|
149
|
+
return pickNavigationGuard(args) !== null;
|
|
150
|
+
});
|
|
151
|
+
router.subscribe((state) => {
|
|
152
|
+
const blocker = state.blockers.get(blockerKey);
|
|
153
|
+
if (!blocker || blocker.state !== "blocked") {
|
|
154
|
+
lastPromptedLocationKey = null;
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const blockedLocation = blocker.location;
|
|
158
|
+
const dedupKey = getLocationDedupKey(blockedLocation);
|
|
159
|
+
if (lastPromptedLocationKey === dedupKey) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
lastPromptedLocationKey = dedupKey;
|
|
163
|
+
if (windowState && Date.now() - windowState.lastBeforeUnloadAt < NATIVE_PROMPT_COOLDOWN_MS) {
|
|
164
|
+
blocker.reset();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const args = lastArgs;
|
|
168
|
+
const guard = args ? pickNavigationGuard(args) : null;
|
|
169
|
+
if (!guard) {
|
|
170
|
+
blocker.proceed();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const ok = window.confirm(guard.message);
|
|
174
|
+
if (ok) {
|
|
175
|
+
if (windowState) {
|
|
176
|
+
windowState.suppressBeforeUnloadUntil = Date.now() + SUPPRESS_BEFOREUNLOAD_MS;
|
|
177
|
+
}
|
|
178
|
+
blocker.proceed();
|
|
179
|
+
} else {
|
|
180
|
+
blocker.reset();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
if (typeof window !== "undefined") {
|
|
184
|
+
const key = "__rpcbaseBeforeUnloadNavigationGuardInstalled";
|
|
185
|
+
const globalAny = window;
|
|
186
|
+
if (!globalAny[key]) {
|
|
187
|
+
globalAny[key] = true;
|
|
188
|
+
window.addEventListener("beforeunload", (event) => {
|
|
189
|
+
const state = getWindowState();
|
|
190
|
+
if (state && state.suppressBeforeUnloadUntil > 0 && Date.now() < state.suppressBeforeUnloadUntil) {
|
|
191
|
+
state.suppressBeforeUnloadUntil = 0;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (!hasUnloadBlockers()) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (state) {
|
|
198
|
+
state.lastBeforeUnloadAt = Date.now();
|
|
199
|
+
}
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
event.returnValue = "";
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
82
206
|
const SSR_ERROR_STATE_GLOBAL_KEY = "__RPCBASE_SSR_ERROR__";
|
|
83
207
|
const ESCAPED_LT = /</g;
|
|
84
208
|
const ESCAPED_U2028 = /\u2028/g;
|
|
@@ -220,6 +344,7 @@ const initWithRoutes = async (routesElement, opts) => {
|
|
|
220
344
|
const routes = createRoutesFromElements(routesElement);
|
|
221
345
|
applyLoaderTimeouts(routes);
|
|
222
346
|
const router = createBrowserRouter(routes, {});
|
|
347
|
+
installGlobalNavigationGuard(router);
|
|
223
348
|
const toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
224
349
|
const mentionsHydration = (value, depth = 0) => {
|
|
225
350
|
if (depth > 5) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initWithRoutes.d.ts","sourceRoot":"","sources":["../src/initWithRoutes.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAmC,MAAM,OAAO,CAAA;AAElE,OAAO,EAAsB,wBAAwB,EAAiB,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"initWithRoutes.d.ts","sourceRoot":"","sources":["../src/initWithRoutes.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAmC,MAAM,OAAO,CAAA;AAElE,OAAO,EAAsB,wBAAwB,EAAiB,MAAM,iBAAiB,CAAA;AAO7F,OAAO,EAGL,oBAAoB,EACrB,MAAM,iBAAiB,CAAA;AAgFxB,KAAK,qBAAqB,GAAG;IAC3B,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAA;CAC9D,CAAA;AAED,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAA;AA0CnE,eAAO,MAAM,cAAc,GACzB,eAAe,aAAa,EAC5B,OAAO,qBAAqB,kBAsF7B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installGlobalNavigationGuard.d.ts","sourceRoot":"","sources":["../../src/navigationGuard/installGlobalNavigationGuard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAGvB,MAAM,iBAAiB,CAAA;AA2FxB,eAAO,MAAM,4BAA4B,GAAI,QAAQ,UAAU,KAAG,IAoFjE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installGlobalNavigationGuard.test.d.ts","sourceRoot":"","sources":["../../src/navigationGuard/installGlobalNavigationGuard.test.ts"],"names":[],"mappings":""}
|