@tanstack/router-core 1.167.1 → 1.167.3

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.
Files changed (164) hide show
  1. package/dist/cjs/Matches.cjs +15 -12
  2. package/dist/cjs/Matches.cjs.map +1 -1
  3. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  4. package/dist/cjs/config.cjs +9 -8
  5. package/dist/cjs/config.cjs.map +1 -1
  6. package/dist/cjs/defer.cjs +37 -21
  7. package/dist/cjs/defer.cjs.map +1 -1
  8. package/dist/cjs/index.cjs +87 -89
  9. package/dist/cjs/isServer/client.cjs +5 -3
  10. package/dist/cjs/isServer/client.cjs.map +1 -1
  11. package/dist/cjs/isServer/development.cjs +5 -3
  12. package/dist/cjs/isServer/development.cjs.map +1 -1
  13. package/dist/cjs/isServer/server.cjs +5 -3
  14. package/dist/cjs/isServer/server.cjs.map +1 -1
  15. package/dist/cjs/link.cjs +5 -4
  16. package/dist/cjs/link.cjs.map +1 -1
  17. package/dist/cjs/load-matches.cjs +622 -766
  18. package/dist/cjs/load-matches.cjs.map +1 -1
  19. package/dist/cjs/lru-cache.cjs +67 -64
  20. package/dist/cjs/lru-cache.cjs.map +1 -1
  21. package/dist/cjs/new-process-route-tree.cjs +707 -792
  22. package/dist/cjs/new-process-route-tree.cjs.map +1 -1
  23. package/dist/cjs/not-found.cjs +20 -7
  24. package/dist/cjs/not-found.cjs.map +1 -1
  25. package/dist/cjs/path.cjs +221 -232
  26. package/dist/cjs/path.cjs.map +1 -1
  27. package/dist/cjs/qss.cjs +62 -28
  28. package/dist/cjs/qss.cjs.map +1 -1
  29. package/dist/cjs/redirect.cjs +44 -30
  30. package/dist/cjs/redirect.cjs.map +1 -1
  31. package/dist/cjs/rewrite.cjs +56 -56
  32. package/dist/cjs/rewrite.cjs.map +1 -1
  33. package/dist/cjs/root.cjs +6 -4
  34. package/dist/cjs/root.cjs.map +1 -1
  35. package/dist/cjs/route.cjs +96 -105
  36. package/dist/cjs/route.cjs.map +1 -1
  37. package/dist/cjs/router.cjs +1154 -1524
  38. package/dist/cjs/router.cjs.map +1 -1
  39. package/dist/cjs/scroll-restoration.cjs +189 -207
  40. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  41. package/dist/cjs/searchMiddleware.cjs +48 -37
  42. package/dist/cjs/searchMiddleware.cjs.map +1 -1
  43. package/dist/cjs/searchParams.cjs +57 -45
  44. package/dist/cjs/searchParams.cjs.map +1 -1
  45. package/dist/cjs/ssr/client.cjs +6 -8
  46. package/dist/cjs/ssr/constants.cjs +6 -5
  47. package/dist/cjs/ssr/constants.cjs.map +1 -1
  48. package/dist/cjs/ssr/createRequestHandler.cjs +41 -59
  49. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  50. package/dist/cjs/ssr/handlerCallback.cjs +5 -4
  51. package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
  52. package/dist/cjs/ssr/headers.cjs +17 -26
  53. package/dist/cjs/ssr/headers.cjs.map +1 -1
  54. package/dist/cjs/ssr/json.cjs +8 -4
  55. package/dist/cjs/ssr/json.cjs.map +1 -1
  56. package/dist/cjs/ssr/serializer/RawStream.cjs +268 -268
  57. package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
  58. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs +31 -32
  59. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
  60. package/dist/cjs/ssr/serializer/seroval-plugins.cjs +12 -12
  61. package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
  62. package/dist/cjs/ssr/serializer/transformer.cjs +45 -41
  63. package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
  64. package/dist/cjs/ssr/server.cjs +12 -14
  65. package/dist/cjs/ssr/ssr-client.cjs +173 -211
  66. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  67. package/dist/cjs/ssr/ssr-match-id.cjs +6 -5
  68. package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
  69. package/dist/cjs/ssr/ssr-server.cjs +266 -300
  70. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  71. package/dist/cjs/ssr/transformStreamWithRouter.cjs +317 -337
  72. package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
  73. package/dist/cjs/ssr/tsrScript.cjs +6 -4
  74. package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
  75. package/dist/cjs/utils/batch.cjs +13 -13
  76. package/dist/cjs/utils/batch.cjs.map +1 -1
  77. package/dist/cjs/utils.cjs +274 -208
  78. package/dist/cjs/utils.cjs.map +1 -1
  79. package/dist/esm/Matches.js +16 -13
  80. package/dist/esm/Matches.js.map +1 -1
  81. package/dist/esm/config.js +10 -9
  82. package/dist/esm/config.js.map +1 -1
  83. package/dist/esm/defer.js +37 -22
  84. package/dist/esm/defer.js.map +1 -1
  85. package/dist/esm/index.js +12 -82
  86. package/dist/esm/isServer/client.js +6 -5
  87. package/dist/esm/isServer/client.js.map +1 -1
  88. package/dist/esm/isServer/development.js +6 -5
  89. package/dist/esm/isServer/development.js.map +1 -1
  90. package/dist/esm/isServer/server.js +6 -5
  91. package/dist/esm/isServer/server.js.map +1 -1
  92. package/dist/esm/link.js +6 -5
  93. package/dist/esm/link.js.map +1 -1
  94. package/dist/esm/load-matches.js +617 -765
  95. package/dist/esm/load-matches.js.map +1 -1
  96. package/dist/esm/lru-cache.js +68 -65
  97. package/dist/esm/lru-cache.js.map +1 -1
  98. package/dist/esm/new-process-route-tree.js +705 -797
  99. package/dist/esm/new-process-route-tree.js.map +1 -1
  100. package/dist/esm/not-found.js +21 -9
  101. package/dist/esm/not-found.js.map +1 -1
  102. package/dist/esm/path.js +220 -241
  103. package/dist/esm/path.js.map +1 -1
  104. package/dist/esm/qss.js +63 -30
  105. package/dist/esm/qss.js.map +1 -1
  106. package/dist/esm/redirect.js +45 -34
  107. package/dist/esm/redirect.js.map +1 -1
  108. package/dist/esm/rewrite.js +57 -60
  109. package/dist/esm/rewrite.js.map +1 -1
  110. package/dist/esm/root.js +7 -5
  111. package/dist/esm/root.js.map +1 -1
  112. package/dist/esm/route.js +92 -105
  113. package/dist/esm/route.js.map +1 -1
  114. package/dist/esm/router.js +1148 -1527
  115. package/dist/esm/router.js.map +1 -1
  116. package/dist/esm/scroll-restoration.js +188 -213
  117. package/dist/esm/scroll-restoration.js.map +1 -1
  118. package/dist/esm/searchMiddleware.js +48 -38
  119. package/dist/esm/searchMiddleware.js.map +1 -1
  120. package/dist/esm/searchParams.js +57 -48
  121. package/dist/esm/searchParams.js.map +1 -1
  122. package/dist/esm/ssr/client.js +1 -6
  123. package/dist/esm/ssr/constants.js +7 -7
  124. package/dist/esm/ssr/constants.js.map +1 -1
  125. package/dist/esm/ssr/createRequestHandler.js +39 -58
  126. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  127. package/dist/esm/ssr/handlerCallback.js +6 -5
  128. package/dist/esm/ssr/handlerCallback.js.map +1 -1
  129. package/dist/esm/ssr/headers.js +16 -26
  130. package/dist/esm/ssr/headers.js.map +1 -1
  131. package/dist/esm/ssr/json.js +9 -5
  132. package/dist/esm/ssr/json.js.map +1 -1
  133. package/dist/esm/ssr/serializer/RawStream.js +267 -273
  134. package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
  135. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js +31 -32
  136. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
  137. package/dist/esm/ssr/serializer/seroval-plugins.js +10 -11
  138. package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
  139. package/dist/esm/ssr/serializer/transformer.js +44 -43
  140. package/dist/esm/ssr/serializer/transformer.js.map +1 -1
  141. package/dist/esm/ssr/server.js +2 -12
  142. package/dist/esm/ssr/ssr-client.js +169 -209
  143. package/dist/esm/ssr/ssr-client.js.map +1 -1
  144. package/dist/esm/ssr/ssr-match-id.js +7 -7
  145. package/dist/esm/ssr/ssr-match-id.js.map +1 -1
  146. package/dist/esm/ssr/ssr-server.js +262 -300
  147. package/dist/esm/ssr/ssr-server.js.map +1 -1
  148. package/dist/esm/ssr/transformStreamWithRouter.js +315 -338
  149. package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
  150. package/dist/esm/ssr/tsrScript.js +6 -5
  151. package/dist/esm/ssr/tsrScript.js.map +1 -1
  152. package/dist/esm/utils/batch.js +13 -14
  153. package/dist/esm/utils/batch.js.map +1 -1
  154. package/dist/esm/utils.js +273 -224
  155. package/dist/esm/utils.js.map +1 -1
  156. package/package.json +2 -2
  157. package/src/load-matches.ts +4 -1
  158. package/src/router.ts +2 -1
  159. package/dist/cjs/index.cjs.map +0 -1
  160. package/dist/cjs/ssr/client.cjs.map +0 -1
  161. package/dist/cjs/ssr/server.cjs.map +0 -1
  162. package/dist/esm/index.js.map +0 -1
  163. package/dist/esm/ssr/client.js.map +0 -1
  164. package/dist/esm/ssr/server.js.map +0 -1
@@ -1,252 +1,317 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const isServer = require("@tanstack/router-core/isServer");
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
- return arr[arr.length - 1];
9
+ return arr[arr.length - 1];
6
10
  }
7
11
  function isFunction(d) {
8
- return typeof d === "function";
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
- if (isFunction(updater)) {
12
- return updater(previous);
13
- }
14
- return updater;
19
+ if (isFunction(updater)) return updater(previous);
20
+ return updater;
15
21
  }
16
- const hasOwn = Object.prototype.hasOwnProperty;
17
- const isEnumerable = Object.prototype.propertyIsEnumerable;
18
- const createNull = () => /* @__PURE__ */ Object.create(null);
19
- const nullReplaceEqualDeep = (prev, next) => replaceEqualDeep(prev, next, createNull);
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
- if (isServer.isServer) {
22
- return _next;
23
- }
24
- if (prev === _next) {
25
- return prev;
26
- }
27
- if (_depth > 500) return _next;
28
- const next = _next;
29
- const array = isPlainArray(prev) && isPlainArray(next);
30
- if (!array && !(isPlainObject(prev) && isPlainObject(next))) return next;
31
- const prevItems = array ? prev : getEnumerableOwnKeys(prev);
32
- if (!prevItems) return next;
33
- const nextItems = array ? next : getEnumerableOwnKeys(next);
34
- if (!nextItems) return next;
35
- const prevSize = prevItems.length;
36
- const nextSize = nextItems.length;
37
- const copy = array ? new Array(nextSize) : _makeObj();
38
- let equalItems = 0;
39
- for (let i = 0; i < nextSize; i++) {
40
- const key = array ? i : nextItems[i];
41
- const p = prev[key];
42
- const n = next[key];
43
- if (p === n) {
44
- copy[key] = p;
45
- if (array ? i < prevSize : hasOwn.call(prev, key)) equalItems++;
46
- continue;
47
- }
48
- if (p === null || n === null || typeof p !== "object" || typeof n !== "object") {
49
- copy[key] = n;
50
- continue;
51
- }
52
- const v = replaceEqualDeep(p, n, _makeObj, _depth + 1);
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
- const names = Object.getOwnPropertyNames(o);
60
- for (const name of names) {
61
- if (!isEnumerable.call(o, name)) return false;
62
- }
63
- const symbols = Object.getOwnPropertySymbols(o);
64
- if (symbols.length === 0) return names;
65
- const keys = names;
66
- for (const symbol of symbols) {
67
- if (!isEnumerable.call(o, symbol)) return false;
68
- keys.push(symbol);
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
- if (!hasObjectPrototype(o)) {
74
- return false;
75
- }
76
- const ctor = o.constructor;
77
- if (typeof ctor === "undefined") {
78
- return true;
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
- return Object.prototype.toString.call(o) === "[object Object]";
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
- return Array.isArray(value) && value.length === Object.keys(value).length;
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
- if (a === b) {
97
- return true;
98
- }
99
- if (typeof a !== typeof b) {
100
- return false;
101
- }
102
- if (Array.isArray(a) && Array.isArray(b)) {
103
- if (a.length !== b.length) return false;
104
- for (let i = 0, l = a.length; i < l; i++) {
105
- if (!deepEqual(a[i], b[i], opts)) return false;
106
- }
107
- return true;
108
- }
109
- if (isPlainObject(a) && isPlainObject(b)) {
110
- const ignoreUndefined = opts?.ignoreUndefined ?? true;
111
- if (opts?.partial) {
112
- for (const k in b) {
113
- if (!ignoreUndefined || b[k] !== void 0) {
114
- if (!deepEqual(a[k], b[k], opts)) return false;
115
- }
116
- }
117
- return true;
118
- }
119
- let aCount = 0;
120
- if (!ignoreUndefined) {
121
- aCount = Object.keys(a).length;
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
- 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;
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
- if (typeof error?.message !== "string") return false;
160
- 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");
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
- return Boolean(
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
- for (let i = array.length - 1; i >= 0; i--) {
169
- const item = array[i];
170
- if (predicate(item)) return item;
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
- return segment.replace(/[\x00-\x1f\x7f]/g, "");
181
+ return segment.replace(/[\x00-\x1f\x7f]/g, "");
176
182
  }
177
183
  function decodeSegment(segment) {
178
- let decoded;
179
- try {
180
- decoded = decodeURI(segment);
181
- } catch {
182
- decoded = segment.replaceAll(/%[0-9A-F]{2}/gi, (match) => {
183
- try {
184
- return decodeURI(match);
185
- } catch {
186
- return match;
187
- }
188
- });
189
- }
190
- return sanitizePathSegment(decoded);
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
- const DEFAULT_PROTOCOL_ALLOWLIST = [
193
- // Standard web navigation
194
- "http:",
195
- "https:",
196
- // Common browser-safe actions
197
- "mailto:",
198
- "tel:"
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
- if (!url) return false;
202
- try {
203
- const parsed = new URL(url);
204
- return !allowlist.has(parsed.protocol);
205
- } catch {
206
- return false;
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
- const HTML_ESCAPE_LOOKUP = {
210
- "&": "\\u0026",
211
- ">": "\\u003e",
212
- "<": "\\u003c",
213
- "\u2028": "\\u2028",
214
- "\u2029": "\\u2029"
232
+ var HTML_ESCAPE_LOOKUP = {
233
+ "&": "\\u0026",
234
+ ">": "\\u003e",
235
+ "<": "\\u003c",
236
+ "\u2028": "\\u2028",
237
+ "\u2029": "\\u2029"
215
238
  };
216
- const HTML_ESCAPE_REGEX = /[&><\u2028\u2029]/g;
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
- return str.replace(HTML_ESCAPE_REGEX, (match) => HTML_ESCAPE_LOOKUP[match]);
248
+ return str.replace(HTML_ESCAPE_REGEX, (match) => HTML_ESCAPE_LOOKUP[match]);
219
249
  }
220
250
  function decodePath(path) {
221
- if (!path) return { path, handledProtocolRelativeURL: false };
222
- if (!/[%\\\x00-\x1f\x7f]/.test(path) && !path.startsWith("//")) {
223
- return { path, handledProtocolRelativeURL: false };
224
- }
225
- const re = /%25|%5C/gi;
226
- let cursor = 0;
227
- let result = "";
228
- let match;
229
- while (null !== (match = re.exec(path))) {
230
- result += decodeSegment(path.slice(cursor, match.index)) + match[0];
231
- cursor = re.lastIndex;
232
- }
233
- result = result + decodeSegment(cursor ? path.slice(cursor) : path);
234
- let handledProtocolRelativeURL = false;
235
- if (result.startsWith("//")) {
236
- handledProtocolRelativeURL = true;
237
- result = "/" + result.replace(/^\/+/, "");
238
- }
239
- return { path: result, handledProtocolRelativeURL };
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
- if (!/\s|[^\u0000-\u007F]/.test(path)) return path;
243
- return path.replace(/\s|[^\u0000-\u007F]/gu, encodeURIComponent);
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
- const trimmedBasepath = basepath.replace(/^\/+|\/+$/g, "");
247
- const normalizedBasepath = trimmedBasepath === "" ? "" : `/${trimmedBasepath}`;
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
- //# sourceMappingURL=utils.cjs.map
332
+
333
+ //# sourceMappingURL=utils.cjs.map