@plumile/router 0.1.19 → 0.1.20
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/README.md +110 -180
- package/lib/esm/eslint-rules/no-direct-window-location-search.js +3 -3
- package/lib/esm/index.d.ts +0 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +1 -2
- package/lib/esm/routing/Link.d.ts.map +1 -1
- package/lib/esm/routing/Link.js +14 -25
- package/lib/esm/routing/RouteComponentWrapper.js +2 -2
- package/lib/esm/routing/createRouter.d.ts.map +1 -1
- package/lib/esm/routing/createRouter.js +91 -103
- package/lib/esm/routing/index.d.ts +1 -1
- package/lib/esm/routing/index.d.ts.map +1 -1
- package/lib/esm/routing/index.js +2 -2
- package/lib/esm/routing/useAllQuery.js +1 -1
- package/lib/esm/routing/useFilters.js +2 -2
- package/lib/esm/routing/useQueryState.d.ts.map +1 -1
- package/lib/esm/routing/useQueryState.js +10 -45
- package/lib/esm/tools/buildCombinedSearch.d.ts +0 -4
- package/lib/esm/tools/buildCombinedSearch.d.ts.map +1 -1
- package/lib/esm/tools/buildCombinedSearch.js +4 -13
- package/lib/esm/tools.d.ts +2 -6
- package/lib/esm/tools.d.ts.map +1 -1
- package/lib/esm/tools.js +1 -1
- package/lib/esm/type-tests/query-infer.test-d.d.ts.map +1 -1
- package/lib/esm/type-tests/query-infer.test-d.js +2 -49
- package/lib/esm/type-tests/routes-inference.test-d.d.ts +0 -1
- package/lib/esm/type-tests/routes-inference.test-d.js +2 -49
- package/lib/esm/types.d.ts +2 -16
- package/lib/esm/types.d.ts.map +1 -1
- package/lib/esm/types.js +1 -1
- package/lib/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/types/index.d.ts +0 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/routing/Link.d.ts.map +1 -1
- package/lib/types/routing/createRouter.d.ts.map +1 -1
- package/lib/types/routing/index.d.ts +1 -1
- package/lib/types/routing/index.d.ts.map +1 -1
- package/lib/types/routing/useQueryState.d.ts.map +1 -1
- package/lib/types/tools/buildCombinedSearch.d.ts +0 -4
- package/lib/types/tools/buildCombinedSearch.d.ts.map +1 -1
- package/lib/types/tools.d.ts +2 -6
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/type-tests/query-infer.test-d.d.ts.map +1 -1
- package/lib/types/type-tests/routes-inference.test-d.d.ts +0 -1
- package/lib/types/types.d.ts +2 -16
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/lib/esm/routing/useTypedQuery.d.ts +0 -2
- package/lib/esm/routing/useTypedQuery.d.ts.map +0 -1
- package/lib/esm/routing/useTypedQuery.js +0 -36
- package/lib/esm/tools/buildSearch.d.ts +0 -6
- package/lib/esm/tools/buildSearch.d.ts.map +0 -1
- package/lib/esm/tools/buildSearch.js +0 -60
- package/lib/esm/tools/query-dsl.d.ts +0 -28
- package/lib/esm/tools/query-dsl.d.ts.map +0 -1
- package/lib/esm/tools/query-dsl.js +0 -250
- package/lib/types/routing/useTypedQuery.d.ts +0 -2
- package/lib/types/routing/useTypedQuery.d.ts.map +0 -1
- package/lib/types/tools/buildSearch.d.ts +0 -6
- package/lib/types/tools/buildSearch.d.ts.map +0 -1
- package/lib/types/tools/query-dsl.d.ts +0 -28
- package/lib/types/tools/query-dsl.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -228,175 +228,104 @@ Prepares route data and components for rendering.
|
|
|
228
228
|
|
|
229
229
|
Type helper for strongly-typed route definitions.
|
|
230
230
|
|
|
231
|
-
##
|
|
231
|
+
## Unified Query & Filter Model
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
The router now unifies page-like query parameters and structured filters under a single model powered by `@plumile/filter-query`.
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
Key points:
|
|
236
236
|
|
|
237
|
-
-
|
|
238
|
-
- URL
|
|
239
|
-
-
|
|
237
|
+
- A route can declare a `querySchema` (filter schema) on its deepest branch.
|
|
238
|
+
- All URL parameters (simple key=value and filter operators like `price.gt=10`) are parsed into a single `filters` object.
|
|
239
|
+
- Equality is implicit: `field=value` maps to internal operator `eq`.
|
|
240
|
+
- The current active schema is exposed as `entry.activeQuerySchema` for tooling.
|
|
241
|
+
- Serialization is centralized via `buildCombinedSearch` (used by both `navigate` and `Link`).
|
|
240
242
|
|
|
241
|
-
|
|
243
|
+
### Defining a Schema
|
|
242
244
|
|
|
243
245
|
```ts
|
|
244
|
-
import { r
|
|
246
|
+
import { r } from '@plumile/router';
|
|
247
|
+
import { defineSchema, numberField, stringField } from '@plumile/filter-query';
|
|
245
248
|
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
});
|
|
251
|
-
const user = r<{ userId: string }, { id: string }>({
|
|
252
|
-
path: '/users/:id',
|
|
253
|
-
prepare: ({ variables }) => ({ userId: variables.id }),
|
|
249
|
+
const productFilters = defineSchema({
|
|
250
|
+
page: numberField(),
|
|
251
|
+
price: numberField(),
|
|
252
|
+
title: stringField(),
|
|
254
253
|
});
|
|
255
|
-
const { context } = createRouter([plans, user] as const);
|
|
256
|
-
|
|
257
|
-
const entry = context.get();
|
|
258
|
-
if (entry.route?.path === '/plans') {
|
|
259
|
-
const prepared = context.getPrepared('/plans');
|
|
260
|
-
// prepared?.page inferred as number | undefined
|
|
261
|
-
const typed = context.getTypedQuery('/plans');
|
|
262
|
-
// typed?.page inferred
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Typed navigate – query validated against the route schema
|
|
266
|
-
context.navigate({ pathname: '/plans', query: { page: 2 } });
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
APIs:
|
|
270
|
-
|
|
271
|
-
- `context.getPrepared(path)` → prepared object for that matched path (if present).
|
|
272
|
-
- `context.getTypedQuery(path)` → typed query for path.
|
|
273
|
-
- `context.navigate({...})` → enforces schema shape when `pathname` matches a known literal path.
|
|
274
|
-
|
|
275
|
-
If a path is dynamic or no schema exists, the untyped fallback remains available.
|
|
276
|
-
|
|
277
|
-
### Typed Query Parameters (DSL)
|
|
278
|
-
|
|
279
|
-
The router provides a lightweight schema DSL for parsing, typing, normalizing and serializing query strings.
|
|
280
|
-
|
|
281
|
-
#### 1. Define a schema on the deepest route
|
|
282
|
-
|
|
283
|
-
```ts
|
|
284
|
-
import { q, r } from '@plumile/router';
|
|
285
254
|
|
|
286
255
|
const routes = [
|
|
287
256
|
r({
|
|
288
|
-
path: '/
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
page: q.default(q.number(), 1),
|
|
292
|
-
tags: q.array(q.string()),
|
|
293
|
-
flag: q.optional(q.boolean()),
|
|
294
|
-
},
|
|
295
|
-
prepare: ({ query }) => {
|
|
296
|
-
// query is typed: { page: number; tags: string[]; flag?: boolean }
|
|
297
|
-
return { page: query.page };
|
|
298
|
-
},
|
|
257
|
+
path: '/products',
|
|
258
|
+
querySchema: productFilters,
|
|
259
|
+
prepare: ({ filters }) => ({ page: filters.page?.eq ?? 1 }),
|
|
299
260
|
render: () => null,
|
|
300
261
|
}),
|
|
301
262
|
];
|
|
302
263
|
```
|
|
303
264
|
|
|
304
|
-
|
|
265
|
+
### Accessing Filters
|
|
305
266
|
|
|
306
267
|
```tsx
|
|
307
|
-
import {
|
|
268
|
+
import { useFilters } from '@plumile/router';
|
|
308
269
|
|
|
309
270
|
function List() {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
271
|
+
const [filters, actions] = useFilters();
|
|
272
|
+
// filters.price?.gt, filters.title?.contains, filters.page?.eq
|
|
273
|
+
actions.set('price', 'gt', 10);
|
|
274
|
+
actions.set('page', 'eq', 2); // becomes page=2 in URL
|
|
275
|
+
return null;
|
|
314
276
|
}
|
|
315
277
|
```
|
|
316
278
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
Type inference:
|
|
320
|
-
|
|
321
|
-
- If the matched deepest route has a `query` schema, `useTypedQuery()` returns the inferred `InferQuery<typeof schema>` shape automatically.
|
|
322
|
-
- If no schema exists, it returns the raw parsed object (record of string | string[]) so you can still read values safely.
|
|
323
|
-
- You can still supply a generic manually (`useTypedQuery<MyShape>()`) in edge cases (e.g. incremental migration) but it is usually unnecessary now.
|
|
324
|
-
|
|
325
|
-
#### 3. Programmatic navigation with typed query
|
|
326
|
-
|
|
327
|
-
```tsx
|
|
328
|
-
import { useNavigate } from '@plumile/router';
|
|
279
|
+
### Programmatic Navigation
|
|
329
280
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return (
|
|
333
|
-
<button
|
|
334
|
-
onClick={() => {
|
|
335
|
-
navigate({ query: { page: page + 1 } });
|
|
336
|
-
}}
|
|
337
|
-
>
|
|
338
|
-
Next page
|
|
339
|
-
</button>
|
|
340
|
-
);
|
|
341
|
-
}
|
|
281
|
+
```ts
|
|
282
|
+
context.navigate({ pathname: '/products', filters: { page: { eq: 3 } } });
|
|
342
283
|
```
|
|
343
284
|
|
|
344
|
-
|
|
285
|
+
### Page Normalization
|
|
345
286
|
|
|
346
|
-
|
|
347
|
-
import { Link } from '@plumile/router';
|
|
348
|
-
|
|
349
|
-
<Link to="/items" query={{ page: 2, tags: ['a', 'b'], flag: true }}>
|
|
350
|
-
Filter
|
|
351
|
-
</Link>;
|
|
352
|
-
```
|
|
287
|
+
The router clamps `page.eq < 1` to `1` synchronously and performs a history replace so the back stack is not polluted.
|
|
353
288
|
|
|
354
|
-
|
|
289
|
+
### Serialization Rules
|
|
355
290
|
|
|
356
|
-
|
|
357
|
-
- Array repetition (`?tags=a&tags=b`)
|
|
358
|
-
- Omission of default values when `omitDefaults` optimization applies internally
|
|
291
|
+
`buildCombinedSearch` produces a leading `?` (or empty string) by:
|
|
359
292
|
|
|
360
|
-
|
|
293
|
+
1. Ordering simple query params (from schema order).
|
|
294
|
+
2. Appending filter operator segments (`field.operator=value`).
|
|
295
|
+
3. Using implicit equality (`field=value`).
|
|
296
|
+
4. Maintaining reference stability (no unnecessary object churn).
|
|
361
297
|
|
|
362
|
-
|
|
298
|
+
### Migration Note
|
|
363
299
|
|
|
364
|
-
|
|
300
|
+
The legacy query DSL (`q.*`, `parseTypedQuery`, `buildSearch`) has been removed. Replace any usage with a filter schema (`defineSchema`) and rely on `useFilters`, `navigate({ filters })`, and/or `buildCombinedSearch`.
|
|
365
301
|
|
|
366
|
-
|
|
367
|
-
import { buildSearch, q } from '@plumile/router';
|
|
368
|
-
|
|
369
|
-
const schema = {
|
|
370
|
-
page: q.default(q.number(), 1),
|
|
371
|
-
tag: q.array(q.string()),
|
|
372
|
-
} as const;
|
|
373
|
-
const search = buildSearch({ page: 1, tag: ['x', 'y'] }, schema, {
|
|
374
|
-
omitDefaults: true,
|
|
375
|
-
});
|
|
376
|
-
// => '?tag=x&tag=y'
|
|
377
|
-
```
|
|
302
|
+
### Performance & Stability
|
|
378
303
|
|
|
379
|
-
|
|
304
|
+
Filters cache: repeated navigations with semantically identical search strings reuse the same filter object reference, enabling cheap React renders.
|
|
380
305
|
|
|
381
|
-
|
|
382
|
-
import { parseSearch, q } from '@plumile/router';
|
|
306
|
+
### Example Link
|
|
383
307
|
|
|
384
|
-
|
|
385
|
-
|
|
308
|
+
```tsx
|
|
309
|
+
<Link to="/products" filters={{ page: { eq: 1 }, price: { gt: 10 } }}>
|
|
310
|
+
Deals
|
|
311
|
+
</Link>
|
|
386
312
|
```
|
|
387
313
|
|
|
388
|
-
|
|
314
|
+
Renders `href="/products?page=1&price.gt=10"`.
|
|
389
315
|
|
|
390
|
-
|
|
316
|
+
### Manual Serialization Example
|
|
391
317
|
|
|
392
|
-
|
|
393
|
-
2. Typed query cache: A WeakMap keyed by (schema reference → canonical search signature) that parses once and reuses the typed value. Structural deep-equality canonicalization means semantically equivalent searches (e.g. reordered keys) reuse object identity.
|
|
318
|
+
If you need to build a URL outside of navigation (e.g. constructing a sitemap entry):
|
|
394
319
|
|
|
395
|
-
|
|
320
|
+
```ts
|
|
321
|
+
import buildCombinedSearch from '@plumile/router/lib/esm/routing/tools/buildCombinedSearch.js'; // or routed re-export
|
|
396
322
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
323
|
+
const search = buildCombinedSearch({
|
|
324
|
+
filters: { page: { eq: 1 }, price: { gt: 10 } },
|
|
325
|
+
schema: productFilters,
|
|
326
|
+
});
|
|
327
|
+
// search === '?page=1&price.gt=10'
|
|
328
|
+
```
|
|
400
329
|
|
|
401
330
|
Guideline: If you need to derive lightweight projections (e.g. `const { page } = typed`), you can still destructure; but avoid spreading into a new object if you rely on reference equality downstream.
|
|
402
331
|
|
|
@@ -420,13 +349,13 @@ Devtools option forms:
|
|
|
420
349
|
- `panel` (default: false) mounts an overlay; closed with the close button or page reload.
|
|
421
350
|
- `shortcut` (default: `Alt+Shift+R`) toggles panel visibility.
|
|
422
351
|
|
|
423
|
-
The panel displays current path+search, variables, raw query and
|
|
352
|
+
The panel displays current path+search, variables, raw query aggregation, active schema, and parsed filters. It is intentionally framework‑agnostic (no React runtime cost) and uses a shadow root to minimize style collisions.
|
|
424
353
|
|
|
425
354
|
```js
|
|
426
355
|
window.__PLUMILE_ROUTER__.get(); // current RouteEntry
|
|
427
|
-
const unsub = window.__PLUMILE_ROUTER__.subscribe((entry) =>
|
|
428
|
-
console.log(entry.
|
|
429
|
-
);
|
|
356
|
+
const unsub = window.__PLUMILE_ROUTER__.subscribe((entry) => {
|
|
357
|
+
console.log('filters', entry.filters);
|
|
358
|
+
});
|
|
430
359
|
```
|
|
431
360
|
|
|
432
361
|
This lets you introspect parsed queries & variables without adding debug code. Neither global nor panel is present in production unless explicitly forced with `devtools: { global: true, panel: true }` (discouraged).
|
|
@@ -455,15 +384,16 @@ type RouteEntry = {
|
|
|
455
384
|
route: { match; params; path } | null; // current matched route (or null)
|
|
456
385
|
preparedMatch: { routes: { prepared; render?; resourcePage? }[]; match }; // internal prepared tree
|
|
457
386
|
forceRerender: boolean; // indicates forced re-render situations
|
|
458
|
-
rawSearch: string; // '?page=2&
|
|
459
|
-
query: Record<string, string | string[]>; // raw aggregated query params
|
|
460
|
-
|
|
387
|
+
rawSearch: string; // '?page=2&price.gt=10'
|
|
388
|
+
query: Record<string, string | string[]>; // raw aggregated query params (for legacy/simple access)
|
|
389
|
+
filters: Record<string, any>; // unified parsed filters (implicit eq)
|
|
390
|
+
activeQuerySchema?: any; // active deepest schema (when present)
|
|
461
391
|
};
|
|
462
392
|
```
|
|
463
393
|
|
|
464
394
|
Common console patterns:
|
|
465
395
|
|
|
466
|
-
1. Log every navigation with
|
|
396
|
+
1. Log every navigation with filters & params:
|
|
467
397
|
|
|
468
398
|
```js
|
|
469
399
|
const dev = window.__PLUMILE_ROUTER__;
|
|
@@ -471,7 +401,7 @@ if (dev) {
|
|
|
471
401
|
const off = dev.subscribe((e) => {
|
|
472
402
|
console.log('[router]', e.location.pathname + e.location.search, {
|
|
473
403
|
vars: e.route?.params,
|
|
474
|
-
|
|
404
|
+
filters: e.filters,
|
|
475
405
|
});
|
|
476
406
|
});
|
|
477
407
|
// later: off();
|
|
@@ -492,7 +422,7 @@ deepest?.prepared; // Prepared data returned by deepest prepare()
|
|
|
492
422
|
let last = window.__PLUMILE_ROUTER__?.get().rawSearch;
|
|
493
423
|
const off = window.__PLUMILE_ROUTER__?.subscribe((e) => {
|
|
494
424
|
if (e.rawSearch !== last) {
|
|
495
|
-
console.log('query changed', last, '=>', e.rawSearch, e.
|
|
425
|
+
console.log('query changed', last, '=>', e.rawSearch, e.filters);
|
|
496
426
|
last = e.rawSearch;
|
|
497
427
|
}
|
|
498
428
|
});
|
|
@@ -503,7 +433,7 @@ const off = window.__PLUMILE_ROUTER__?.subscribe((e) => {
|
|
|
503
433
|
```js
|
|
504
434
|
const { get } = window.__PLUMILE_ROUTER__;
|
|
505
435
|
const before = performance.now();
|
|
506
|
-
for (let i = 0; i < 200; i++) get().
|
|
436
|
+
for (let i = 0; i < 200; i++) get().filters; // use cache; ensures no GC
|
|
507
437
|
console.log('elapsed ms', performance.now() - before);
|
|
508
438
|
```
|
|
509
439
|
|
|
@@ -511,7 +441,7 @@ console.log('elapsed ms', performance.now() - before);
|
|
|
511
441
|
|
|
512
442
|
```js
|
|
513
443
|
const R = window.__PLUMILE_ROUTER__;
|
|
514
|
-
R && R.subscribe((e) => console.debug('[
|
|
444
|
+
R && R.subscribe((e) => console.debug('[filters]', e.filters));
|
|
515
445
|
```
|
|
516
446
|
|
|
517
447
|
Unsubscribing: every `subscribe` returns a disposer function. Always call it if you set up long‑lived listeners in a debugging session to avoid memory leaks during hot reloads.
|
|
@@ -520,39 +450,42 @@ Extending: if you need more (e.g. trigger navigations) you can wrap `createRoute
|
|
|
520
450
|
|
|
521
451
|
Production: the symbol is not defined; accessing it will yield `undefined`. Never ship logic depending on it; keep usage inside `if (process.env.NODE_ENV !== 'production')` blocks or guarded optional checks.
|
|
522
452
|
|
|
523
|
-
###
|
|
453
|
+
### Unified Query Schema & useFilters (Integration with @plumile/filter-query)
|
|
524
454
|
|
|
525
|
-
The router
|
|
455
|
+
The router uses a single unified schema (`querySchema`) defined with `@plumile/filter-query` to describe allowed key/operator pairs. Plain `field=value` maps to the implicit equality operator (`eq`). Each filter segment is encoded as `field.operator=value` (with `eq` omitted).
|
|
526
456
|
|
|
527
457
|
```ts
|
|
528
|
-
import { r,
|
|
458
|
+
import { r, useFilters } from '@plumile/router';
|
|
529
459
|
import { defineSchema, numberField, stringField } from '@plumile/filter-query';
|
|
530
460
|
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
461
|
+
const querySchema = defineSchema({
|
|
462
|
+
page: numberField(), // page.eq=2 => ?page=2 (page normalization clamps <1 to 1)
|
|
463
|
+
price: numberField(), // price.gt=10 => ?price.gt=10
|
|
464
|
+
name: stringField(), // name.contains=ultra => ?name.contains=ultra
|
|
534
465
|
});
|
|
535
466
|
|
|
536
|
-
const routes = [
|
|
467
|
+
export const routes = [
|
|
537
468
|
r({
|
|
538
469
|
path: '/items',
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
render: () => null,
|
|
470
|
+
querySchema,
|
|
471
|
+
render: () => <Items />,
|
|
542
472
|
}),
|
|
543
473
|
];
|
|
544
474
|
|
|
545
|
-
|
|
546
|
-
const [filters,
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
475
|
+
function Items() {
|
|
476
|
+
const [filters, { set, remove, clear, merge }] = useFilters();
|
|
477
|
+
set('price', 'gt', 10); // => ?price.gt=10
|
|
478
|
+
set('page', 'eq', 2); // => ?page=2
|
|
479
|
+
remove('price', 'gt');
|
|
480
|
+
merge([{ field: 'price', op: 'between', value: [10, 20] }]);
|
|
481
|
+
clear();
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
551
484
|
```
|
|
552
485
|
|
|
553
|
-
`navigate({ filters })` is
|
|
486
|
+
`navigate({ filters })` is available for programmatic updates. Serialization order: schema field keys first, then any extra non‑schema keys (if present). Operators follow deterministic ordering from `@plumile/filter-query`.
|
|
554
487
|
|
|
555
|
-
Diagnostics (unknown field/operator, invalid values, etc.) are accessible via `useFilterDiagnostics()
|
|
488
|
+
Diagnostics (unknown field/operator, invalid values, etc.) are accessible via `useFilterDiagnostics()`.
|
|
556
489
|
|
|
557
490
|
### ESLint Rule: no-direct-window-location-search
|
|
558
491
|
|
|
@@ -600,32 +533,29 @@ const qs = window.location.search;
|
|
|
600
533
|
with:
|
|
601
534
|
|
|
602
535
|
```ts
|
|
603
|
-
const query = useQuery(); //
|
|
536
|
+
const query = useQuery(); // raw key=value aggregation (simple); prefer useFilters() for structured access.
|
|
604
537
|
```
|
|
605
538
|
|
|
606
|
-
### Migration Guide (
|
|
539
|
+
### Migration Guide (Legacy DSL → Unified Filters)
|
|
540
|
+
|
|
541
|
+
1. Remove legacy imports of `q`, `parseTypedQuery`, `buildSearch`.
|
|
542
|
+
2. Define a schema with `defineSchema` from `@plumile/filter-query` and attach as `querySchema`.
|
|
543
|
+
3. Replace (removed) `useTypedQuery()` usages with `useFilters()`.
|
|
544
|
+
4. Update navigation: `navigate({ filters: { page: { eq: 2 } } })`.
|
|
545
|
+
5. Update links: `<Link to="/x" filters={{ page: { eq: 2 } }} />`.
|
|
546
|
+
6. Page defaults / normalization: rely on built-in clamp (`page < 1 -> 1`).
|
|
547
|
+
7. Remove any type tests referencing the DSL; rely on schema inference.
|
|
607
548
|
|
|
608
|
-
|
|
609
|
-
2. Phase 3: Add `query` schema to your deepest route; adopt `useTypedQuery()` where type safety is desirable.
|
|
610
|
-
3. Phase 4: Replace manual URL building with `navigate({ query })` or `<Link query={...} />`. Remove ad‑hoc serialization logic.
|
|
611
|
-
4. Phase 5: Move lightweight data loading logic that depends on query into `prepare({ query })` (now typed). Rely on built‑in normalization (e.g. page clamp) or add custom normalization inside `prepare` followed by a `navigate({ replace: true, query: normalized })` if needed.
|
|
612
|
-
5. Optional: Use `buildSearch` / `parseSearch` for unit tests & utilities.
|
|
549
|
+
### Removed APIs
|
|
613
550
|
|
|
614
|
-
|
|
551
|
+
The following legacy APIs were removed in favor of the unified filter model:
|
|
615
552
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
| `q.boolean()` | Presence / true/false/1/0 | `{ f: true } -> ?f=1` |
|
|
621
|
-
| `q.enum('a','b')` | Restricted string | `{ e: 'a' } -> ?e=a` |
|
|
622
|
-
| `q.array(inner)` | Repeated key multi-values | `{ tag: ['x','y'] } -> ?tag=x&tag=y` |
|
|
623
|
-
| `q.optional(d)` | Marks descriptor optional | omitted if undefined |
|
|
624
|
-
| `q.default(d, v)` | Supplies default + omit on serialize (omitDefaults) | default skipped |
|
|
625
|
-
| `q.emptyAsUndefined(q.string())` | Maps empty string '' to undefined | omitted |
|
|
626
|
-
| `q.custom({ parse, serialize })` | Custom parse/serialize logic | depends |
|
|
553
|
+
- `q.*` descriptor DSL (replaced by `defineSchema` in `@plumile/filter-query`)
|
|
554
|
+
- `parseTypedQuery`
|
|
555
|
+
- `buildSearch` (use `buildCombinedSearch`)
|
|
556
|
+
- `useTypedQuery`
|
|
627
557
|
|
|
628
|
-
|
|
558
|
+
Use `@plumile/filter-query` for schema definitions and `useFilters` / `buildCombinedSearch` for runtime access & serialization.
|
|
629
559
|
|
|
630
560
|
### useQueryState Hook
|
|
631
561
|
|
|
@@ -639,10 +569,10 @@ setPage(page! + 1, { replace: true });
|
|
|
639
569
|
|
|
640
570
|
Behavior:
|
|
641
571
|
|
|
642
|
-
- Reads from
|
|
643
|
-
-
|
|
572
|
+
- Reads from filters (implicit equality) and falls back to raw query key when not covered by schema.
|
|
573
|
+
- Allows `defaultValue` override in options and omits key when value equals default (with `omitIfDefault: true`).
|
|
644
574
|
- Pass `{ raw: true }` to force raw (string) source for incremental migrations.
|
|
645
|
-
- Uses
|
|
575
|
+
- Uses unified serialization ordering (schema keys then extras; operators deterministic).
|
|
646
576
|
|
|
647
577
|
Options:
|
|
648
578
|
`{ defaultValue?, omitIfDefault?: boolean = true, replace?: boolean, raw?: boolean }`
|
|
@@ -2,11 +2,11 @@ const rule = {
|
|
|
2
2
|
meta: {
|
|
3
3
|
type: 'suggestion',
|
|
4
4
|
docs: {
|
|
5
|
-
description: 'Discourage direct window.location.search access; use useQuery()/
|
|
5
|
+
description: 'Discourage direct window.location.search access; use useQuery()/useFilters() instead',
|
|
6
6
|
recommended: false,
|
|
7
7
|
},
|
|
8
8
|
messages: {
|
|
9
|
-
avoid: 'Avoid direct access to window.location.search. Use useQuery() /
|
|
9
|
+
avoid: 'Avoid direct access to window.location.search. Use useQuery() / useFilters() from @plumile/router.',
|
|
10
10
|
},
|
|
11
11
|
schema: [
|
|
12
12
|
{
|
|
@@ -45,4 +45,4 @@ const rule = {
|
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
47
|
export default rule;
|
|
48
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
48
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm8tZGlyZWN0LXdpbmRvdy1sb2NhdGlvbi1zZWFyY2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXNsaW50LXJ1bGVzL25vLWRpcmVjdC13aW5kb3ctbG9jYXRpb24tc2VhcmNoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU9BLE1BQU0sSUFBSSxHQUFvQjtJQUM1QixJQUFJLEVBQUU7UUFDSixJQUFJLEVBQUUsWUFBWTtRQUNsQixJQUFJLEVBQUU7WUFDSixXQUFXLEVBQ1Qsc0ZBQXNGO1lBQ3hGLFdBQVcsRUFBRSxLQUFLO1NBQ25CO1FBQ0QsUUFBUSxFQUFFO1lBQ1IsS0FBSyxFQUNILG9HQUFvRztTQUN2RztRQUNELE1BQU0sRUFBRTtZQUNOO2dCQUNFLElBQUksRUFBRSxRQUFRO2dCQUNkLFVBQVUsRUFBRTtvQkFDVixZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsRUFBRTtpQkFDM0Q7Z0JBQ0Qsb0JBQW9CLEVBQUUsS0FBSzthQUM1QjtTQUNGO0tBQ0Y7SUFDRCxNQUFNLENBQUMsT0FBTztRQUNaLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUE2QixDQUFDO1FBQ2pFLE1BQU0sT0FBTyxHQUFHLElBQUksR0FBRyxDQUFDLFNBQVMsRUFBRSxZQUFZLElBQUksRUFBRSxDQUFDLENBQUM7UUFDdkQsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3ZDLE1BQU0sU0FBUyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRTtZQUMxQyxPQUFPLFFBQVEsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDaEMsQ0FBQyxDQUFDLENBQUM7UUFFSCxPQUFPO1lBQ0wsZ0JBQWdCLENBQUMsSUFBSTtnQkFDbkIsSUFBSSxTQUFTO29CQUFFLE9BQU87Z0JBQ3RCLElBQ0UsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEtBQUssWUFBWTtvQkFDbkMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEtBQUssUUFBUTtvQkFDL0IsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssa0JBQWtCLEVBQ3ZDLENBQUM7b0JBQ0QsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztvQkFDeEIsSUFDRSxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksS0FBSyxZQUFZO3dCQUNsQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksS0FBSyxVQUFVO3dCQUNoQyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxZQUFZO3dCQUNoQyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxRQUFRLEVBQzVCLENBQUM7d0JBQ0QsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztvQkFDL0MsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztTQUNGLENBQUM7SUFDSixDQUFDO0NBQ0YsQ0FBQztBQUVGLGVBQWUsSUFBSSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gTW92ZWQgZnJvbSBwYWNrYWdlIHJvb3QgZXNsaW50LXJ1bGVzIGRpcmVjdG9yeSBpbnRvIHNyYyB0byBzYXRpc2Z5IFRTIHJvb3REaXJcbmltcG9ydCB0eXBlIHsgUnVsZSB9IGZyb20gJ2VzbGludCc7XG5cbmludGVyZmFjZSBPcHRpb25zU2hhcGUge1xuICBhbGxvd0luRmlsZXM/OiBzdHJpbmdbXTtcbn1cblxuY29uc3QgcnVsZTogUnVsZS5SdWxlTW9kdWxlID0ge1xuICBtZXRhOiB7XG4gICAgdHlwZTogJ3N1Z2dlc3Rpb24nLFxuICAgIGRvY3M6IHtcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnRGlzY291cmFnZSBkaXJlY3Qgd2luZG93LmxvY2F0aW9uLnNlYXJjaCBhY2Nlc3M7IHVzZSB1c2VRdWVyeSgpL3VzZUZpbHRlcnMoKSBpbnN0ZWFkJyxcbiAgICAgIHJlY29tbWVuZGVkOiBmYWxzZSxcbiAgICB9LFxuICAgIG1lc3NhZ2VzOiB7XG4gICAgICBhdm9pZDpcbiAgICAgICAgJ0F2b2lkIGRpcmVjdCBhY2Nlc3MgdG8gd2luZG93LmxvY2F0aW9uLnNlYXJjaC4gVXNlIHVzZVF1ZXJ5KCkgLyB1c2VGaWx0ZXJzKCkgZnJvbSBAcGx1bWlsZS9yb3V0ZXIuJyxcbiAgICB9LFxuICAgIHNjaGVtYTogW1xuICAgICAge1xuICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgIGFsbG93SW5GaWxlczogeyB0eXBlOiAnYXJyYXknLCBpdGVtczogeyB0eXBlOiAnc3RyaW5nJyB9IH0sXG4gICAgICAgIH0sXG4gICAgICAgIGFkZGl0aW9uYWxQcm9wZXJ0aWVzOiBmYWxzZSxcbiAgICAgIH0sXG4gICAgXSxcbiAgfSxcbiAgY3JlYXRlKGNvbnRleHQpIHtcbiAgICBjb25zdCByYXdPcHRpb24gPSBjb250ZXh0Lm9wdGlvbnNbMF0gYXMgT3B0aW9uc1NoYXBlIHwgdW5kZWZpbmVkO1xuICAgIGNvbnN0IGFsbG93SW4gPSBuZXcgU2V0KHJhd09wdGlvbj8uYWxsb3dJbkZpbGVzID8/IFtdKTtcbiAgICBjb25zdCBmaWxlbmFtZSA9IGNvbnRleHQuZ2V0RmlsZW5hbWUoKTtcbiAgICBjb25zdCBpc0FsbG93ZWQgPSBbLi4uYWxsb3dJbl0uc29tZSgocGF0KSA9PiB7XG4gICAgICByZXR1cm4gZmlsZW5hbWUuaW5jbHVkZXMocGF0KTtcbiAgICB9KTtcblxuICAgIHJldHVybiB7XG4gICAgICBNZW1iZXJFeHByZXNzaW9uKG5vZGUpIHtcbiAgICAgICAgaWYgKGlzQWxsb3dlZCkgcmV0dXJuO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgbm9kZS5wcm9wZXJ0eS50eXBlID09PSAnSWRlbnRpZmllcicgJiZcbiAgICAgICAgICBub2RlLnByb3BlcnR5Lm5hbWUgPT09ICdzZWFyY2gnICYmXG4gICAgICAgICAgbm9kZS5vYmplY3QudHlwZSA9PT0gJ01lbWJlckV4cHJlc3Npb24nXG4gICAgICAgICkge1xuICAgICAgICAgIGNvbnN0IG9iaiA9IG5vZGUub2JqZWN0O1xuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIG9iai5wcm9wZXJ0eS50eXBlID09PSAnSWRlbnRpZmllcicgJiZcbiAgICAgICAgICAgIG9iai5wcm9wZXJ0eS5uYW1lID09PSAnbG9jYXRpb24nICYmXG4gICAgICAgICAgICBvYmoub2JqZWN0LnR5cGUgPT09ICdJZGVudGlmaWVyJyAmJlxuICAgICAgICAgICAgb2JqLm9iamVjdC5uYW1lID09PSAnd2luZG93J1xuICAgICAgICAgICkge1xuICAgICAgICAgICAgY29udGV4dC5yZXBvcnQoeyBub2RlLCBtZXNzYWdlSWQ6ICdhdm9pZCcgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuICAgIH07XG4gIH0sXG59O1xuXG5leHBvcnQgZGVmYXVsdCBydWxlO1xuIl19
|
package/lib/esm/index.d.ts
CHANGED
package/lib/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAC;AAGlC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,cAAc,CAAC;AAG7B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAC;AAGlC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,cAAc,CAAC;AAG7B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,YAAY,CAAC;AAG3B,mBAAmB,YAAY,CAAC"}
|
package/lib/esm/index.js
CHANGED
|
@@ -4,5 +4,4 @@ export * from './routing/index.js';
|
|
|
4
4
|
export * from './builder.js';
|
|
5
5
|
export * from './ResourcePage.js';
|
|
6
6
|
export * from './tools.js';
|
|
7
|
-
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsY0FBYyxtQkFBbUIsQ0FBQztBQUdsQyxjQUFjLG9CQUFvQixDQUFDO0FBR25DLGNBQWMsb0JBQW9CLENBQUM7QUFHbkMsY0FBYyxjQUFjLENBQUM7QUFHN0IsY0FBYyxtQkFBbUIsQ0FBQztBQUdsQyxjQUFjLFlBQVksQ0FBQztBQUMzQixPQUFPLEVBQUUsQ0FBQyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gRXhwb3J0IGVycm9yIGhhbmRsaW5nIHV0aWxpdGllc1xuZXhwb3J0ICogZnJvbSAnLi9lcnJvcnMvaW5kZXguanMnO1xuXG4vLyBFeHBvcnQgYnJvd3NlciBoaXN0b3J5IG1hbmFnZW1lbnRcbmV4cG9ydCAqIGZyb20gJy4vaGlzdG9yeS9pbmRleC5qcyc7XG5cbi8vIEV4cG9ydCByb3V0aW5nIGNvbXBvbmVudHMgYW5kIHV0aWxpdGllc1xuZXhwb3J0ICogZnJvbSAnLi9yb3V0aW5nL2luZGV4LmpzJztcblxuLy8gRXhwb3J0IHJvdXRlIGJ1aWxkaW5nIHV0aWxpdGllc1xuZXhwb3J0ICogZnJvbSAnLi9idWlsZGVyLmpzJztcblxuLy8gRXhwb3J0IHJlc291cmNlIHBhZ2UgbWFuYWdlbWVudCBmb3IgbGF6eSBsb2FkaW5nXG5leHBvcnQgKiBmcm9tICcuL1Jlc291cmNlUGFnZS5qcyc7XG5cbi8vIEV4cG9ydCB1dGlsaXR5IGZ1bmN0aW9uc1xuZXhwb3J0ICogZnJvbSAnLi90b29scy5qcyc7XG5leHBvcnQgeyBxLCBwYXJzZVR5cGVkUXVlcnkgfSBmcm9tICcuL3Rvb2xzL3F1ZXJ5LWRzbC5qcyc7XG5cbi8vIEV4cG9ydCBhbGwgVHlwZVNjcmlwdCB0eXBlc1xuZXhwb3J0IHR5cGUgKiBmcm9tICcuL3R5cGVzLmpzJztcbiJdfQ==
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsY0FBYyxtQkFBbUIsQ0FBQztBQUdsQyxjQUFjLG9CQUFvQixDQUFDO0FBR25DLGNBQWMsb0JBQW9CLENBQUM7QUFHbkMsY0FBYyxjQUFjLENBQUM7QUFHN0IsY0FBYyxtQkFBbUIsQ0FBQztBQUdsQyxjQUFjLFlBQVksQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEV4cG9ydCBlcnJvciBoYW5kbGluZyB1dGlsaXRpZXNcbmV4cG9ydCAqIGZyb20gJy4vZXJyb3JzL2luZGV4LmpzJztcblxuLy8gRXhwb3J0IGJyb3dzZXIgaGlzdG9yeSBtYW5hZ2VtZW50XG5leHBvcnQgKiBmcm9tICcuL2hpc3RvcnkvaW5kZXguanMnO1xuXG4vLyBFeHBvcnQgcm91dGluZyBjb21wb25lbnRzIGFuZCB1dGlsaXRpZXNcbmV4cG9ydCAqIGZyb20gJy4vcm91dGluZy9pbmRleC5qcyc7XG5cbi8vIEV4cG9ydCByb3V0ZSBidWlsZGluZyB1dGlsaXRpZXNcbmV4cG9ydCAqIGZyb20gJy4vYnVpbGRlci5qcyc7XG5cbi8vIEV4cG9ydCByZXNvdXJjZSBwYWdlIG1hbmFnZW1lbnQgZm9yIGxhenkgbG9hZGluZ1xuZXhwb3J0ICogZnJvbSAnLi9SZXNvdXJjZVBhZ2UuanMnO1xuXG4vLyBFeHBvcnQgdXRpbGl0eSBmdW5jdGlvbnNcbmV4cG9ydCAqIGZyb20gJy4vdG9vbHMuanMnO1xuXG4vLyBFeHBvcnQgYWxsIFR5cGVTY3JpcHQgdHlwZXNcbmV4cG9ydCB0eXBlICogZnJvbSAnLi90eXBlcy5qcyc7XG4iXX0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../../src/routing/Link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,KAAK,yBAAyB,EAE9B,KAAK,SAAS,EAIf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAa3E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAmBT;AAKD,KAAK,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAE7E,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,QAAQ,EAAE,SAAS,CAAC;IAEpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAErD,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAErD,WAAW,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAEzD,MAAM,CAAC,EAAE,yBAAyB,CAAC;IAEnC,EAAE,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IAE9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAyBF,QAAA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../../src/routing/Link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,KAAK,yBAAyB,EAE9B,KAAK,SAAS,EAIf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAa3E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAmBT;AAKD,KAAK,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAE7E,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,QAAQ,EAAE,SAAS,CAAC;IAEpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAErD,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAErD,WAAW,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAEzD,MAAM,CAAC,EAAE,yBAAyB,CAAC;IAEnC,EAAE,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IAE9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAyBF,QAAA,MAAM,IAAI,0GAuJR,CAAC;AAEH,eAAe,IAAI,CAAC"}
|