@asteby/metacore-runtime-react 23.12.0 → 23.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 23.12.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 8788161: fix(dynamic-table): stop infinite URL replaceState loop on filtered deep-links
8
+
9
+ The host router re-serializes the query string in its own canonical form
10
+ (different key order, percent-encoded operator colons) than the table writes,
11
+ so the raw-string comparison never matched and the table's write effect fought
12
+ the router's re-serialize forever ("Throttling navigation…" + React #185),
13
+ hanging any filtered sidebar view (e.g. `f_status=eq:reception`). Both URL-sync
14
+ effects now compare an order/encoding-independent fingerprint and the write is
15
+ skipped when semantically a no-op.
16
+
3
17
  ## 23.12.0
4
18
 
5
19
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAanF,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC/B,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,EACR,cAAsB,GACzB,EAAE,iBAAiB,+BAujCnB"}
1
+ {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAanF,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC/B,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,EACR,cAAsB,GACzB,EAAE,iBAAiB,+BAmlCnB"}
@@ -114,6 +114,21 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
114
114
  const alias = operatorToUrlAlias[prefix];
115
115
  return alias ? `${alias}:${rest}` : value;
116
116
  };
117
+ // Order- and encoding-independent fingerprint of a query string. The host
118
+ // router (TanStack) re-serializes the same params in its own canonical form
119
+ // — different key order and percent-encoded operator colons
120
+ // (`f_status=eq%3Areception`) — than the table writes (`f_status=eq:reception`).
121
+ // Comparing raw strings then NEVER matches, so the table's write effect and
122
+ // the router's re-serialize fight forever ("Throttling navigation…" +
123
+ // React #185). URLSearchParams decodes values, so a sorted `key=value` list
124
+ // is stable across both spellings and lets us detect real no-ops.
125
+ const canonicalSearch = (searchStr) => {
126
+ const p = new URLSearchParams(searchStr);
127
+ const entries = [];
128
+ p.forEach((value, key) => entries.push(`${key}=${value}`));
129
+ entries.sort();
130
+ return entries.join('&');
131
+ };
117
132
  useEffect(() => {
118
133
  if (!enableUrlSync || initializedFromUrl.current)
119
134
  return;
@@ -195,6 +210,16 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
195
210
  params.set(`f_${key}`, `in:${values.join(',')}`);
196
211
  });
197
212
  const search = params.toString();
213
+ // If what we'd write is semantically identical to what's already in the
214
+ // bar (only key order / colon-encoding differ from the router's form),
215
+ // skip the write entirely. Rewriting would flip the raw string, the
216
+ // router would re-serialize back to its form, and the two would ping-pong
217
+ // forever. We still adopt the current location as "ours" so the resync
218
+ // effect below doesn't treat the router's spelling as an external change.
219
+ if (canonicalSearch(search) === canonicalSearch(window.location.search)) {
220
+ lastSelfSearch.current = window.location.search;
221
+ return;
222
+ }
198
223
  const newUrl = search ? `${window.location.pathname}?${search}` : window.location.pathname;
199
224
  lastSelfSearch.current = search ? `?${search}` : '';
200
225
  window.history.replaceState(null, '', newUrl);
@@ -209,7 +234,9 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
209
234
  useEffect(() => {
210
235
  if (!enableUrlSync || !initializedFromUrl.current)
211
236
  return;
212
- if (locationSearch === lastSelfSearch.current)
237
+ // Semantic compare: the router's spelling of the query string we just
238
+ // wrote (different order/encoding) is NOT an external change.
239
+ if (canonicalSearch(locationSearch) === canonicalSearch(lastSelfSearch.current ?? ''))
213
240
  return;
214
241
  lastSelfSearch.current = locationSearch;
215
242
  const params = new URLSearchParams(locationSearch);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "23.12.0",
3
+ "version": "23.12.1",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -229,6 +229,22 @@ export function DynamicTable({
229
229
  return alias ? `${alias}:${rest}` : value
230
230
  }
231
231
 
232
+ // Order- and encoding-independent fingerprint of a query string. The host
233
+ // router (TanStack) re-serializes the same params in its own canonical form
234
+ // — different key order and percent-encoded operator colons
235
+ // (`f_status=eq%3Areception`) — than the table writes (`f_status=eq:reception`).
236
+ // Comparing raw strings then NEVER matches, so the table's write effect and
237
+ // the router's re-serialize fight forever ("Throttling navigation…" +
238
+ // React #185). URLSearchParams decodes values, so a sorted `key=value` list
239
+ // is stable across both spellings and lets us detect real no-ops.
240
+ const canonicalSearch = (searchStr: string): string => {
241
+ const p = new URLSearchParams(searchStr)
242
+ const entries: string[] = []
243
+ p.forEach((value, key) => entries.push(`${key}=${value}`))
244
+ entries.sort()
245
+ return entries.join('&')
246
+ }
247
+
232
248
  useEffect(() => {
233
249
  if (!enableUrlSync || initializedFromUrl.current) return
234
250
  initializedFromUrl.current = true
@@ -295,6 +311,16 @@ export function DynamicTable({
295
311
  else params.set(`f_${key}`, `in:${values.join(',')}`)
296
312
  })
297
313
  const search = params.toString()
314
+ // If what we'd write is semantically identical to what's already in the
315
+ // bar (only key order / colon-encoding differ from the router's form),
316
+ // skip the write entirely. Rewriting would flip the raw string, the
317
+ // router would re-serialize back to its form, and the two would ping-pong
318
+ // forever. We still adopt the current location as "ours" so the resync
319
+ // effect below doesn't treat the router's spelling as an external change.
320
+ if (canonicalSearch(search) === canonicalSearch(window.location.search)) {
321
+ lastSelfSearch.current = window.location.search
322
+ return
323
+ }
298
324
  const newUrl = search ? `${window.location.pathname}?${search}` : window.location.pathname
299
325
  lastSelfSearch.current = search ? `?${search}` : ''
300
326
  window.history.replaceState(null, '', newUrl)
@@ -309,7 +335,9 @@ export function DynamicTable({
309
335
  const locationSearch = typeof window !== 'undefined' ? window.location.search : ''
310
336
  useEffect(() => {
311
337
  if (!enableUrlSync || !initializedFromUrl.current) return
312
- if (locationSearch === lastSelfSearch.current) return
338
+ // Semantic compare: the router's spelling of the query string we just
339
+ // wrote (different order/encoding) is NOT an external change.
340
+ if (canonicalSearch(locationSearch) === canonicalSearch(lastSelfSearch.current ?? '')) return
313
341
  lastSelfSearch.current = locationSearch
314
342
  const params = new URLSearchParams(locationSearch)
315
343
  const filters: Record<string, string[]> = {}