@tanstack/router-core 1.167.1 → 1.167.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/dist/cjs/Matches.cjs +15 -12
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/config.cjs +9 -8
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/defer.cjs +37 -21
- package/dist/cjs/defer.cjs.map +1 -1
- package/dist/cjs/index.cjs +87 -89
- package/dist/cjs/isServer/client.cjs +5 -3
- package/dist/cjs/isServer/client.cjs.map +1 -1
- package/dist/cjs/isServer/development.cjs +5 -3
- package/dist/cjs/isServer/development.cjs.map +1 -1
- package/dist/cjs/isServer/server.cjs +5 -3
- package/dist/cjs/isServer/server.cjs.map +1 -1
- package/dist/cjs/link.cjs +5 -4
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/load-matches.cjs +619 -766
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/lru-cache.cjs +67 -64
- package/dist/cjs/lru-cache.cjs.map +1 -1
- package/dist/cjs/new-process-route-tree.cjs +707 -792
- package/dist/cjs/new-process-route-tree.cjs.map +1 -1
- package/dist/cjs/not-found.cjs +20 -7
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/path.cjs +221 -232
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/qss.cjs +62 -28
- package/dist/cjs/qss.cjs.map +1 -1
- package/dist/cjs/redirect.cjs +44 -30
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/rewrite.cjs +56 -56
- package/dist/cjs/rewrite.cjs.map +1 -1
- package/dist/cjs/root.cjs +6 -4
- package/dist/cjs/root.cjs.map +1 -1
- package/dist/cjs/route.cjs +96 -105
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/router.cjs +1153 -1524
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.cjs +189 -207
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/searchMiddleware.cjs +48 -37
- package/dist/cjs/searchMiddleware.cjs.map +1 -1
- package/dist/cjs/searchParams.cjs +57 -45
- package/dist/cjs/searchParams.cjs.map +1 -1
- package/dist/cjs/ssr/client.cjs +6 -8
- package/dist/cjs/ssr/constants.cjs +6 -5
- package/dist/cjs/ssr/constants.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.cjs +41 -59
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/handlerCallback.cjs +5 -4
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
- package/dist/cjs/ssr/headers.cjs +17 -26
- package/dist/cjs/ssr/headers.cjs.map +1 -1
- package/dist/cjs/ssr/json.cjs +8 -4
- package/dist/cjs/ssr/json.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/RawStream.cjs +268 -268
- package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs +31 -32
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs +12 -12
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.cjs +45 -41
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
- package/dist/cjs/ssr/server.cjs +12 -14
- package/dist/cjs/ssr/ssr-client.cjs +173 -211
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-match-id.cjs +6 -5
- package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +266 -300
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +317 -337
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/tsrScript.cjs +6 -4
- package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
- package/dist/cjs/utils/batch.cjs +13 -13
- package/dist/cjs/utils/batch.cjs.map +1 -1
- package/dist/cjs/utils.cjs +274 -208
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/Matches.js +16 -13
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/config.js +10 -9
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/defer.js +37 -22
- package/dist/esm/defer.js.map +1 -1
- package/dist/esm/index.js +12 -82
- package/dist/esm/isServer/client.js +6 -5
- package/dist/esm/isServer/client.js.map +1 -1
- package/dist/esm/isServer/development.js +6 -5
- package/dist/esm/isServer/development.js.map +1 -1
- package/dist/esm/isServer/server.js +6 -5
- package/dist/esm/isServer/server.js.map +1 -1
- package/dist/esm/link.js +6 -5
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/load-matches.js +614 -765
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/lru-cache.js +68 -65
- package/dist/esm/lru-cache.js.map +1 -1
- package/dist/esm/new-process-route-tree.js +705 -797
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/not-found.js +21 -9
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/path.js +220 -241
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/qss.js +63 -30
- package/dist/esm/qss.js.map +1 -1
- package/dist/esm/redirect.js +45 -34
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/rewrite.js +57 -60
- package/dist/esm/rewrite.js.map +1 -1
- package/dist/esm/root.js +7 -5
- package/dist/esm/root.js.map +1 -1
- package/dist/esm/route.js +92 -105
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.js +1147 -1527
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.js +188 -213
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/searchMiddleware.js +48 -38
- package/dist/esm/searchMiddleware.js.map +1 -1
- package/dist/esm/searchParams.js +57 -48
- package/dist/esm/searchParams.js.map +1 -1
- package/dist/esm/ssr/client.js +1 -6
- package/dist/esm/ssr/constants.js +7 -7
- package/dist/esm/ssr/constants.js.map +1 -1
- package/dist/esm/ssr/createRequestHandler.js +39 -58
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/handlerCallback.js +6 -5
- package/dist/esm/ssr/handlerCallback.js.map +1 -1
- package/dist/esm/ssr/headers.js +16 -26
- package/dist/esm/ssr/headers.js.map +1 -1
- package/dist/esm/ssr/json.js +9 -5
- package/dist/esm/ssr/json.js.map +1 -1
- package/dist/esm/ssr/serializer/RawStream.js +267 -273
- package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js +31 -32
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
- package/dist/esm/ssr/serializer/seroval-plugins.js +10 -11
- package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
- package/dist/esm/ssr/serializer/transformer.js +44 -43
- package/dist/esm/ssr/serializer/transformer.js.map +1 -1
- package/dist/esm/ssr/server.js +2 -12
- package/dist/esm/ssr/ssr-client.js +169 -209
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-match-id.js +7 -7
- package/dist/esm/ssr/ssr-match-id.js.map +1 -1
- package/dist/esm/ssr/ssr-server.js +262 -300
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.js +315 -338
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/ssr/tsrScript.js +6 -5
- package/dist/esm/ssr/tsrScript.js.map +1 -1
- package/dist/esm/utils/batch.js +13 -14
- package/dist/esm/utils/batch.js.map +1 -1
- package/dist/esm/utils.js +273 -224
- package/dist/esm/utils.js.map +1 -1
- package/package.json +2 -2
- package/dist/cjs/index.cjs.map +0 -1
- package/dist/cjs/ssr/client.cjs.map +0 -1
- package/dist/cjs/ssr/server.cjs.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/ssr/client.js.map +0 -1
- package/dist/esm/ssr/server.js.map +0 -1
package/dist/cjs/utils.cjs
CHANGED
|
@@ -1,252 +1,317 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let _tanstack_router_core_isServer = require("@tanstack/router-core/isServer");
|
|
3
|
+
//#region src/utils.ts
|
|
4
|
+
/**
|
|
5
|
+
* Return the last element of an array.
|
|
6
|
+
* Intended for non-empty arrays used within router internals.
|
|
7
|
+
*/
|
|
4
8
|
function last(arr) {
|
|
5
|
-
|
|
9
|
+
return arr[arr.length - 1];
|
|
6
10
|
}
|
|
7
11
|
function isFunction(d) {
|
|
8
|
-
|
|
12
|
+
return typeof d === "function";
|
|
9
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Apply a value-or-updater to a previous value.
|
|
16
|
+
* Accepts either a literal value or a function of the previous value.
|
|
17
|
+
*/
|
|
10
18
|
function functionalUpdate(updater, previous) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
return updater;
|
|
19
|
+
if (isFunction(updater)) return updater(previous);
|
|
20
|
+
return updater;
|
|
15
21
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
var hasOwn = Object.prototype.hasOwnProperty;
|
|
23
|
+
var isEnumerable = Object.prototype.propertyIsEnumerable;
|
|
24
|
+
var createNull = () => Object.create(null);
|
|
25
|
+
var nullReplaceEqualDeep = (prev, next) => replaceEqualDeep(prev, next, createNull);
|
|
26
|
+
/**
|
|
27
|
+
* This function returns `prev` if `_next` is deeply equal.
|
|
28
|
+
* If not, it will replace any deeply equal children of `b` with those of `a`.
|
|
29
|
+
* This can be used for structural sharing between immutable JSON values for example.
|
|
30
|
+
* Do not use this with signals
|
|
31
|
+
*/
|
|
20
32
|
function replaceEqualDeep(prev, _next, _makeObj = () => ({}), _depth = 0) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
copy[key] = v;
|
|
54
|
-
if (v === p) equalItems++;
|
|
55
|
-
}
|
|
56
|
-
return prevSize === nextSize && equalItems === prevSize ? prev : copy;
|
|
33
|
+
if (_tanstack_router_core_isServer.isServer) return _next;
|
|
34
|
+
if (prev === _next) return prev;
|
|
35
|
+
if (_depth > 500) return _next;
|
|
36
|
+
const next = _next;
|
|
37
|
+
const array = isPlainArray(prev) && isPlainArray(next);
|
|
38
|
+
if (!array && !(isPlainObject(prev) && isPlainObject(next))) return next;
|
|
39
|
+
const prevItems = array ? prev : getEnumerableOwnKeys(prev);
|
|
40
|
+
if (!prevItems) return next;
|
|
41
|
+
const nextItems = array ? next : getEnumerableOwnKeys(next);
|
|
42
|
+
if (!nextItems) return next;
|
|
43
|
+
const prevSize = prevItems.length;
|
|
44
|
+
const nextSize = nextItems.length;
|
|
45
|
+
const copy = array ? new Array(nextSize) : _makeObj();
|
|
46
|
+
let equalItems = 0;
|
|
47
|
+
for (let i = 0; i < nextSize; i++) {
|
|
48
|
+
const key = array ? i : nextItems[i];
|
|
49
|
+
const p = prev[key];
|
|
50
|
+
const n = next[key];
|
|
51
|
+
if (p === n) {
|
|
52
|
+
copy[key] = p;
|
|
53
|
+
if (array ? i < prevSize : hasOwn.call(prev, key)) equalItems++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (p === null || n === null || typeof p !== "object" || typeof n !== "object") {
|
|
57
|
+
copy[key] = n;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const v = replaceEqualDeep(p, n, _makeObj, _depth + 1);
|
|
61
|
+
copy[key] = v;
|
|
62
|
+
if (v === p) equalItems++;
|
|
63
|
+
}
|
|
64
|
+
return prevSize === nextSize && equalItems === prevSize ? prev : copy;
|
|
57
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Equivalent to `Reflect.ownKeys`, but ensures that objects are "clone-friendly":
|
|
68
|
+
* will return false if object has any non-enumerable properties.
|
|
69
|
+
*
|
|
70
|
+
* Optimized for the common case where objects have no symbol properties.
|
|
71
|
+
*/
|
|
58
72
|
function getEnumerableOwnKeys(o) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
return keys;
|
|
73
|
+
const names = Object.getOwnPropertyNames(o);
|
|
74
|
+
for (const name of names) if (!isEnumerable.call(o, name)) return false;
|
|
75
|
+
const symbols = Object.getOwnPropertySymbols(o);
|
|
76
|
+
if (symbols.length === 0) return names;
|
|
77
|
+
const keys = names;
|
|
78
|
+
for (const symbol of symbols) {
|
|
79
|
+
if (!isEnumerable.call(o, symbol)) return false;
|
|
80
|
+
keys.push(symbol);
|
|
81
|
+
}
|
|
82
|
+
return keys;
|
|
71
83
|
}
|
|
72
84
|
function isPlainObject(o) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const prot = ctor.prototype;
|
|
81
|
-
if (!hasObjectPrototype(prot)) {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
if (!prot.hasOwnProperty("isPrototypeOf")) {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
return true;
|
|
85
|
+
if (!hasObjectPrototype(o)) return false;
|
|
86
|
+
const ctor = o.constructor;
|
|
87
|
+
if (typeof ctor === "undefined") return true;
|
|
88
|
+
const prot = ctor.prototype;
|
|
89
|
+
if (!hasObjectPrototype(prot)) return false;
|
|
90
|
+
if (!prot.hasOwnProperty("isPrototypeOf")) return false;
|
|
91
|
+
return true;
|
|
88
92
|
}
|
|
89
93
|
function hasObjectPrototype(o) {
|
|
90
|
-
|
|
94
|
+
return Object.prototype.toString.call(o) === "[object Object]";
|
|
91
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if a value is a "plain" array (no extra enumerable keys).
|
|
98
|
+
*/
|
|
92
99
|
function isPlainArray(value) {
|
|
93
|
-
|
|
100
|
+
return Array.isArray(value) && value.length === Object.keys(value).length;
|
|
94
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Perform a deep equality check with options for partial comparison and
|
|
104
|
+
* ignoring `undefined` values. Optimized for router state comparisons.
|
|
105
|
+
*/
|
|
95
106
|
function deepEqual(a, b, opts) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
} else {
|
|
123
|
-
for (const k in a) {
|
|
124
|
-
if (a[k] !== void 0) aCount++;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
let bCount = 0;
|
|
128
|
-
for (const k in b) {
|
|
129
|
-
if (!ignoreUndefined || b[k] !== void 0) {
|
|
130
|
-
bCount++;
|
|
131
|
-
if (bCount > aCount || !deepEqual(a[k], b[k], opts)) return false;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return aCount === bCount;
|
|
135
|
-
}
|
|
136
|
-
return false;
|
|
107
|
+
if (a === b) return true;
|
|
108
|
+
if (typeof a !== typeof b) return false;
|
|
109
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
110
|
+
if (a.length !== b.length) return false;
|
|
111
|
+
for (let i = 0, l = a.length; i < l; i++) if (!deepEqual(a[i], b[i], opts)) return false;
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
115
|
+
const ignoreUndefined = opts?.ignoreUndefined ?? true;
|
|
116
|
+
if (opts?.partial) {
|
|
117
|
+
for (const k in b) if (!ignoreUndefined || b[k] !== void 0) {
|
|
118
|
+
if (!deepEqual(a[k], b[k], opts)) return false;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
let aCount = 0;
|
|
123
|
+
if (!ignoreUndefined) aCount = Object.keys(a).length;
|
|
124
|
+
else for (const k in a) if (a[k] !== void 0) aCount++;
|
|
125
|
+
let bCount = 0;
|
|
126
|
+
for (const k in b) if (!ignoreUndefined || b[k] !== void 0) {
|
|
127
|
+
bCount++;
|
|
128
|
+
if (bCount > aCount || !deepEqual(a[k], b[k], opts)) return false;
|
|
129
|
+
}
|
|
130
|
+
return aCount === bCount;
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
137
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Create a promise with exposed resolve/reject and status fields.
|
|
136
|
+
* Useful for coordinating async router lifecycle operations.
|
|
137
|
+
*/
|
|
138
138
|
function createControlledPromise(onResolve) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
139
|
+
let resolveLoadPromise;
|
|
140
|
+
let rejectLoadPromise;
|
|
141
|
+
const controlledPromise = new Promise((resolve, reject) => {
|
|
142
|
+
resolveLoadPromise = resolve;
|
|
143
|
+
rejectLoadPromise = reject;
|
|
144
|
+
});
|
|
145
|
+
controlledPromise.status = "pending";
|
|
146
|
+
controlledPromise.resolve = (value) => {
|
|
147
|
+
controlledPromise.status = "resolved";
|
|
148
|
+
controlledPromise.value = value;
|
|
149
|
+
resolveLoadPromise(value);
|
|
150
|
+
onResolve?.(value);
|
|
151
|
+
};
|
|
152
|
+
controlledPromise.reject = (e) => {
|
|
153
|
+
controlledPromise.status = "rejected";
|
|
154
|
+
rejectLoadPromise(e);
|
|
155
|
+
};
|
|
156
|
+
return controlledPromise;
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Heuristically detect dynamic import "module not found" errors
|
|
160
|
+
* across major browsers for lazy route component handling.
|
|
161
|
+
*/
|
|
158
162
|
function isModuleNotFoundError(error) {
|
|
159
|
-
|
|
160
|
-
|
|
163
|
+
if (typeof error?.message !== "string") return false;
|
|
164
|
+
return error.message.startsWith("Failed to fetch dynamically imported module") || error.message.startsWith("error loading dynamically imported module") || error.message.startsWith("Importing a module script failed");
|
|
161
165
|
}
|
|
162
166
|
function isPromise(value) {
|
|
163
|
-
|
|
164
|
-
value && typeof value === "object" && typeof value.then === "function"
|
|
165
|
-
);
|
|
167
|
+
return Boolean(value && typeof value === "object" && typeof value.then === "function");
|
|
166
168
|
}
|
|
167
169
|
function findLast(array, predicate) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return void 0;
|
|
170
|
+
for (let i = array.length - 1; i >= 0; i--) {
|
|
171
|
+
const item = array[i];
|
|
172
|
+
if (predicate(item)) return item;
|
|
173
|
+
}
|
|
173
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Remove control characters that can cause open redirect vulnerabilities.
|
|
177
|
+
* Characters like \r (CR) and \n (LF) can trick URL parsers into interpreting
|
|
178
|
+
* paths like "/\r/evil.com" as "http://evil.com".
|
|
179
|
+
*/
|
|
174
180
|
function sanitizePathSegment(segment) {
|
|
175
|
-
|
|
181
|
+
return segment.replace(/[\x00-\x1f\x7f]/g, "");
|
|
176
182
|
}
|
|
177
183
|
function decodeSegment(segment) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
let decoded;
|
|
185
|
+
try {
|
|
186
|
+
decoded = decodeURI(segment);
|
|
187
|
+
} catch {
|
|
188
|
+
decoded = segment.replaceAll(/%[0-9A-F]{2}/gi, (match) => {
|
|
189
|
+
try {
|
|
190
|
+
return decodeURI(match);
|
|
191
|
+
} catch {
|
|
192
|
+
return match;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return sanitizePathSegment(decoded);
|
|
191
197
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Default list of URL protocols to allow in links, redirects, and navigation.
|
|
200
|
+
* Any absolute URL protocol not in this list is treated as dangerous by default.
|
|
201
|
+
*/
|
|
202
|
+
var DEFAULT_PROTOCOL_ALLOWLIST = [
|
|
203
|
+
"http:",
|
|
204
|
+
"https:",
|
|
205
|
+
"mailto:",
|
|
206
|
+
"tel:"
|
|
199
207
|
];
|
|
208
|
+
/**
|
|
209
|
+
* Check if a URL string uses a protocol that is not in the allowlist.
|
|
210
|
+
* Returns true for blocked protocols like javascript:, blob:, data:, etc.
|
|
211
|
+
*
|
|
212
|
+
* The URL constructor correctly normalizes:
|
|
213
|
+
* - Mixed case (JavaScript: → javascript:)
|
|
214
|
+
* - Whitespace/control characters (java\nscript: → javascript:)
|
|
215
|
+
* - Leading whitespace
|
|
216
|
+
*
|
|
217
|
+
* For relative URLs (no protocol), returns false (safe).
|
|
218
|
+
*
|
|
219
|
+
* @param url - The URL string to check
|
|
220
|
+
* @param allowlist - Set of protocols to allow
|
|
221
|
+
* @returns true if the URL uses a protocol that is not allowed
|
|
222
|
+
*/
|
|
200
223
|
function isDangerousProtocol(url, allowlist) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
224
|
+
if (!url) return false;
|
|
225
|
+
try {
|
|
226
|
+
const parsed = new URL(url);
|
|
227
|
+
return !allowlist.has(parsed.protocol);
|
|
228
|
+
} catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
208
231
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
232
|
+
var HTML_ESCAPE_LOOKUP = {
|
|
233
|
+
"&": "\\u0026",
|
|
234
|
+
">": "\\u003e",
|
|
235
|
+
"<": "\\u003c",
|
|
236
|
+
"\u2028": "\\u2028",
|
|
237
|
+
"\u2029": "\\u2029"
|
|
215
238
|
};
|
|
216
|
-
|
|
239
|
+
var HTML_ESCAPE_REGEX = /[&><\u2028\u2029]/g;
|
|
240
|
+
/**
|
|
241
|
+
* Escape HTML special characters in a string to prevent XSS attacks
|
|
242
|
+
* when embedding strings in script tags during SSR.
|
|
243
|
+
*
|
|
244
|
+
* This is essential for preventing XSS vulnerabilities when user-controlled
|
|
245
|
+
* content is embedded in inline scripts.
|
|
246
|
+
*/
|
|
217
247
|
function escapeHtml(str) {
|
|
218
|
-
|
|
248
|
+
return str.replace(HTML_ESCAPE_REGEX, (match) => HTML_ESCAPE_LOOKUP[match]);
|
|
219
249
|
}
|
|
220
250
|
function decodePath(path) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
251
|
+
if (!path) return {
|
|
252
|
+
path,
|
|
253
|
+
handledProtocolRelativeURL: false
|
|
254
|
+
};
|
|
255
|
+
if (!/[%\\\x00-\x1f\x7f]/.test(path) && !path.startsWith("//")) return {
|
|
256
|
+
path,
|
|
257
|
+
handledProtocolRelativeURL: false
|
|
258
|
+
};
|
|
259
|
+
const re = /%25|%5C/gi;
|
|
260
|
+
let cursor = 0;
|
|
261
|
+
let result = "";
|
|
262
|
+
let match;
|
|
263
|
+
while (null !== (match = re.exec(path))) {
|
|
264
|
+
result += decodeSegment(path.slice(cursor, match.index)) + match[0];
|
|
265
|
+
cursor = re.lastIndex;
|
|
266
|
+
}
|
|
267
|
+
result = result + decodeSegment(cursor ? path.slice(cursor) : path);
|
|
268
|
+
let handledProtocolRelativeURL = false;
|
|
269
|
+
if (result.startsWith("//")) {
|
|
270
|
+
handledProtocolRelativeURL = true;
|
|
271
|
+
result = "/" + result.replace(/^\/+/, "");
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
path: result,
|
|
275
|
+
handledProtocolRelativeURL
|
|
276
|
+
};
|
|
240
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Encodes a path the same way `new URL()` would, but without the overhead of full URL parsing.
|
|
280
|
+
*
|
|
281
|
+
* This function encodes:
|
|
282
|
+
* - Whitespace characters (spaces → %20, tabs → %09, etc.)
|
|
283
|
+
* - Non-ASCII/Unicode characters (emojis, accented characters, etc.)
|
|
284
|
+
*
|
|
285
|
+
* It preserves:
|
|
286
|
+
* - Already percent-encoded sequences (won't double-encode %2F, %25, etc.)
|
|
287
|
+
* - ASCII special characters valid in URL paths (@, $, &, +, etc.)
|
|
288
|
+
* - Forward slashes as path separators
|
|
289
|
+
*
|
|
290
|
+
* Used to generate proper href values for SSR without constructing URL objects.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* encodePathLikeUrl('/path/file name.pdf') // '/path/file%20name.pdf'
|
|
294
|
+
* encodePathLikeUrl('/path/日本語') // '/path/%E6%97%A5%E6%9C%AC%E8%AA%9E'
|
|
295
|
+
* encodePathLikeUrl('/path/already%20encoded') // '/path/already%20encoded' (preserved)
|
|
296
|
+
*/
|
|
241
297
|
function encodePathLikeUrl(path) {
|
|
242
|
-
|
|
243
|
-
|
|
298
|
+
if (!/\s|[^\u0000-\u007F]/.test(path)) return path;
|
|
299
|
+
return path.replace(/\s|[^\u0000-\u007F]/gu, encodeURIComponent);
|
|
244
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Builds the dev-mode CSS styles URL for route-scoped CSS collection.
|
|
303
|
+
* Used by HeadContent components in all framework implementations to construct
|
|
304
|
+
* the URL for the `/@tanstack-start/styles.css` endpoint.
|
|
305
|
+
*
|
|
306
|
+
* @param basepath - The router's basepath (may or may not have leading slash)
|
|
307
|
+
* @param routeIds - Array of matched route IDs to include in the CSS collection
|
|
308
|
+
* @returns The full URL path for the dev styles CSS endpoint
|
|
309
|
+
*/
|
|
245
310
|
function buildDevStylesUrl(basepath, routeIds) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return `${normalizedBasepath}/@tanstack-start/styles.css?routes=${encodeURIComponent(routeIds.join(","))}`;
|
|
311
|
+
const trimmedBasepath = basepath.replace(/^\/+|\/+$/g, "");
|
|
312
|
+
return `${trimmedBasepath === "" ? "" : `/${trimmedBasepath}`}/@tanstack-start/styles.css?routes=${encodeURIComponent(routeIds.join(","))}`;
|
|
249
313
|
}
|
|
314
|
+
//#endregion
|
|
250
315
|
exports.DEFAULT_PROTOCOL_ALLOWLIST = DEFAULT_PROTOCOL_ALLOWLIST;
|
|
251
316
|
exports.buildDevStylesUrl = buildDevStylesUrl;
|
|
252
317
|
exports.createControlledPromise = createControlledPromise;
|
|
@@ -264,4 +329,5 @@ exports.isPromise = isPromise;
|
|
|
264
329
|
exports.last = last;
|
|
265
330
|
exports.nullReplaceEqualDeep = nullReplaceEqualDeep;
|
|
266
331
|
exports.replaceEqualDeep = replaceEqualDeep;
|
|
267
|
-
|
|
332
|
+
|
|
333
|
+
//# sourceMappingURL=utils.cjs.map
|