@uniflowed/router 0.33.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/action.js +5 -5
  2. package/client.js +20 -20
  3. package/handler.js +6 -6
  4. package/http-client.js +10 -10
  5. package/index.js +3 -3
  6. package/instrumentation.js +5 -5
  7. package/internal/action-endpoint.js +7 -7
  8. package/internal/action-wire.js +3 -3
  9. package/internal/base-path.js +3 -3
  10. package/internal/boundaries.js +8 -8
  11. package/internal/boundary-data.js +12 -12
  12. package/internal/compose.js +4 -4
  13. package/internal/devtools.js +1 -1
  14. package/internal/diagnostics.js +8 -8
  15. package/internal/error-view.js +6 -6
  16. package/internal/flight-browser.js +6 -6
  17. package/internal/flight-rows.js +4 -4
  18. package/internal/flight-ssr.js +3 -3
  19. package/internal/flight.js +26 -26
  20. package/internal/form-action.js +11 -11
  21. package/internal/hydrate-options.js +1 -1
  22. package/internal/hydration.js +37 -37
  23. package/internal/inspector.js +15 -15
  24. package/internal/native-links.js +3 -3
  25. package/internal/native-tree.js +11 -11
  26. package/internal/navigation-cache.js +12 -12
  27. package/internal/payload-rows.js +2 -2
  28. package/internal/payload.js +10 -10
  29. package/internal/resolve.js +103 -103
  30. package/internal/resolved-summary.js +76 -76
  31. package/internal/routing.js +52 -52
  32. package/internal/runtime.js +47 -47
  33. package/internal/search-params.js +1 -1
  34. package/internal/shell.js +8 -8
  35. package/internal/stream.js +44 -44
  36. package/middleware.js +11 -11
  37. package/native-navigation.js +21 -21
  38. package/native.js +45 -45
  39. package/package.json +4 -4
  40. package/rsc-client.js +5 -5
  41. package/rsc-ssr.js +31 -31
  42. package/rsc.js +17 -17
  43. package/server-components.js +5 -5
  44. package/server.js +33 -33
  45. package/testing.js +62 -62
@@ -21,46 +21,46 @@ type BoundaryLike = {
21
21
  };
22
22
 
23
23
  type ModuleBoundaryLike = {
24
- readonly above: number,
24
+ readonly above : number,
25
25
  readonly module?: mixed,
26
26
  ...
27
27
  };
28
28
 
29
29
  type SlotLike = {
30
- readonly name: string,
31
- readonly above: number,
32
- readonly page: mixed,
33
- readonly params: RouteParams,
34
- readonly layouts: $ReadOnlyArray<mixed>,
35
- readonly loading: $ReadOnlyArray<BoundaryLike>,
36
- readonly templates: $ReadOnlyArray<BoundaryLike>,
30
+ readonly name : string,
31
+ readonly above : number,
32
+ readonly page : mixed,
33
+ readonly params : RouteParams,
34
+ readonly layouts : $ReadOnlyArray<mixed>,
35
+ readonly loading : $ReadOnlyArray<BoundaryLike>,
36
+ readonly templates : $ReadOnlyArray<BoundaryLike>,
37
37
  readonly errorBoundary: ?ModuleBoundaryLike,
38
- readonly slots: $ReadOnlyArray<SlotLike>,
38
+ readonly slots : $ReadOnlyArray<SlotLike>,
39
39
  ...
40
40
  };
41
41
 
42
42
  type RouteLike = {
43
- readonly pathname: string,
44
- readonly search: string,
45
- readonly path: string,
46
- readonly params: RouteParams,
47
- readonly searchParams: SearchParams,
48
- readonly layouts: $ReadOnlyArray<mixed>,
49
- readonly data: mixed,
50
- readonly deferred: mixed,
51
- readonly metadata: RouteMetadata,
43
+ readonly pathname : string,
44
+ readonly search : string,
45
+ readonly path : string,
46
+ readonly params : RouteParams,
47
+ readonly searchParams : SearchParams,
48
+ readonly layouts : $ReadOnlyArray<mixed>,
49
+ readonly data : mixed,
50
+ readonly deferred : mixed,
51
+ readonly metadata : RouteMetadata,
52
52
  readonly viewTransition: ?string,
53
- readonly status: RouteStatus,
54
- readonly error: ?RouteErrorLike,
55
- readonly errorBoundary: ModuleBoundaryLike,
56
- readonly loading: $ReadOnlyArray<BoundaryLike>,
57
- readonly templates: $ReadOnlyArray<BoundaryLike>,
58
- readonly slots: $ReadOnlyArray<SlotLike>,
53
+ readonly status : RouteStatus,
54
+ readonly error : ?RouteErrorLike,
55
+ readonly errorBoundary : ModuleBoundaryLike,
56
+ readonly loading : $ReadOnlyArray<BoundaryLike>,
57
+ readonly templates : $ReadOnlyArray<BoundaryLike>,
58
+ readonly slots : $ReadOnlyArray<SlotLike>,
59
59
  ...
60
60
  };
61
61
 
62
62
  export type ResolvedBoundarySummary = {|
63
- readonly id: string,
63
+ readonly id : string,
64
64
  readonly above: number,
65
65
  |};
66
66
 
@@ -75,41 +75,41 @@ export type ResolvedRouteErrorSummary =
75
75
  | {| readonly kind: "badRequest" |};
76
76
 
77
77
  export type ResolvedErrorBoundarySummary = {|
78
- readonly above: number,
79
- readonly custom: boolean,
78
+ readonly above : number,
79
+ readonly custom : boolean,
80
80
  readonly rendered: boolean,
81
81
  |};
82
82
 
83
83
  export type ResolvedSlotSummary = {|
84
- readonly name: string,
85
- readonly above: number,
86
- readonly active: boolean,
87
- readonly params: RouteParams,
88
- readonly layoutCount: number,
89
- readonly loading: $ReadOnlyArray<ResolvedBoundarySummary>,
90
- readonly templates: $ReadOnlyArray<ResolvedTemplateSummary>,
84
+ readonly name : string,
85
+ readonly above : number,
86
+ readonly active : boolean,
87
+ readonly params : RouteParams,
88
+ readonly layoutCount : number,
89
+ readonly loading : $ReadOnlyArray<ResolvedBoundarySummary>,
90
+ readonly templates : $ReadOnlyArray<ResolvedTemplateSummary>,
91
91
  readonly errorBoundary: ?ResolvedErrorBoundarySummary,
92
- readonly slots: $ReadOnlyArray<ResolvedSlotSummary>,
92
+ readonly slots : $ReadOnlyArray<ResolvedSlotSummary>,
93
93
  |};
94
94
 
95
95
  export type ResolvedRouteSummary = {|
96
- readonly pathname: string,
97
- readonly search: string,
98
- readonly path: string,
99
- readonly params: RouteParams,
100
- readonly searchParams: SearchParams,
101
- readonly layoutCount: number,
102
- readonly data: mixed,
103
- readonly deferred: boolean,
104
- readonly metadata: RouteMetadata,
96
+ readonly pathname : string,
97
+ readonly search : string,
98
+ readonly path : string,
99
+ readonly params : RouteParams,
100
+ readonly searchParams : SearchParams,
101
+ readonly layoutCount : number,
102
+ readonly data : mixed,
103
+ readonly deferred : boolean,
104
+ readonly metadata : RouteMetadata,
105
105
  readonly viewTransition: ?string,
106
- readonly status: RouteStatus,
107
- readonly error: ?ResolvedRouteErrorSummary,
108
- readonly errorBoundary: ResolvedErrorBoundarySummary,
109
- readonly loading: $ReadOnlyArray<ResolvedBoundarySummary>,
110
- readonly templates: $ReadOnlyArray<ResolvedTemplateSummary>,
111
- readonly slots: $ReadOnlyArray<ResolvedSlotSummary>,
112
- readonly boundaries: $ReadOnlyArray<RouteBoundary>,
106
+ readonly status : RouteStatus,
107
+ readonly error : ?ResolvedRouteErrorSummary,
108
+ readonly errorBoundary : ResolvedErrorBoundarySummary,
109
+ readonly loading : $ReadOnlyArray<ResolvedBoundarySummary>,
110
+ readonly templates : $ReadOnlyArray<ResolvedTemplateSummary>,
111
+ readonly slots : $ReadOnlyArray<ResolvedSlotSummary>,
112
+ readonly boundaries : $ReadOnlyArray<RouteBoundary>,
113
113
  |};
114
114
 
115
115
  /**
@@ -121,39 +121,39 @@ export type ResolvedRouteSummary = {|
121
121
  * fallback modules left behind in the renderer graph.
122
122
  */
123
123
  export function summarizeResolvedRoute(
124
- route: RouteLike,
124
+ route : RouteLike,
125
125
  errorSource?: ?string,
126
126
  ): ResolvedRouteSummary {
127
127
  return {
128
- pathname: route.pathname,
129
- search: route.search,
130
- path: route.path,
131
- params: route.params,
132
- searchParams: route.searchParams,
133
- layoutCount: route.layouts.length,
134
- data: route.data,
135
- deferred: route.deferred != null,
136
- metadata: route.metadata,
128
+ pathname : route.pathname,
129
+ search : route.search,
130
+ path : route.path,
131
+ params : route.params,
132
+ searchParams : route.searchParams,
133
+ layoutCount : route.layouts.length,
134
+ data : route.data,
135
+ deferred : route.deferred != null,
136
+ metadata : route.metadata,
137
137
  viewTransition: route.viewTransition,
138
- status: route.status,
139
- error: summarizeError(route.error),
140
- errorBoundary: summarizeErrorBoundary(route.errorBoundary, route.error == null),
141
- loading: summarizeLoading(route.loading),
142
- templates: summarizeTemplates(route.templates),
143
- slots: route.slots.map(summarizeSlot),
144
- boundaries: [...routeBoundaries(route, errorSource).values()],
138
+ status : route.status,
139
+ error : summarizeError(route.error),
140
+ errorBoundary : summarizeErrorBoundary(route.errorBoundary, route.error == null),
141
+ loading : summarizeLoading(route.loading),
142
+ templates : summarizeTemplates(route.templates),
143
+ slots : route.slots.map(summarizeSlot),
144
+ boundaries : [...routeBoundaries(route, errorSource).values()],
145
145
  };
146
146
  }
147
147
 
148
148
  function summarizeSlot(slot: SlotLike): ResolvedSlotSummary {
149
149
  return {
150
- name: slot.name,
151
- above: slot.above,
152
- active: slot.page != null,
153
- params: slot.params,
150
+ name : slot.name,
151
+ above : slot.above,
152
+ active : slot.page != null,
153
+ params : slot.params,
154
154
  layoutCount: slot.layouts.length,
155
- loading: summarizeLoading(slot.loading),
156
- templates: summarizeTemplates(slot.templates),
155
+ loading : summarizeLoading(slot.loading),
156
+ templates : summarizeTemplates(slot.templates),
157
157
  errorBoundary:
158
158
  slot.errorBoundary == null ? null : summarizeErrorBoundary(slot.errorBoundary, true),
159
159
  slots: slot.slots.map(summarizeSlot),
@@ -164,7 +164,7 @@ function summarizeLoading(
164
164
  boundaries: $ReadOnlyArray<BoundaryLike>,
165
165
  ): $ReadOnlyArray<ResolvedBoundarySummary> {
166
166
  return boundaries.map((boundary, index) => ({
167
- id: suspenseId(index),
167
+ id : suspenseId(index),
168
168
  above: boundary.above,
169
169
  }));
170
170
  }
@@ -180,7 +180,7 @@ function summarizeErrorBoundary(
180
180
  rendered: boolean,
181
181
  ): ResolvedErrorBoundarySummary {
182
182
  return {
183
- above: boundary.above,
183
+ above : boundary.above,
184
184
  custom: boundary.module != null,
185
185
  rendered,
186
186
  };
@@ -33,9 +33,9 @@ export type SearchParamsAll = { readonly [string]: $ReadOnlyArray<string> };
33
33
  * `["tags", "1"]` for the second `tag`.
34
34
  */
35
35
  export type SearchParamsIssue = {|
36
- readonly code: string,
36
+ readonly code : string,
37
37
  readonly message: string,
38
- readonly path?: $ReadOnlyArray<string>,
38
+ readonly path? : $ReadOnlyArray<string>,
39
39
  |};
40
40
 
41
41
  /** A lazy route module entry from the generated route table. */
@@ -43,19 +43,19 @@ export type RouteModule<TModule = mixed> = () => Promise<TModule>;
43
43
 
44
44
  /** One `$template.js`, as the route table carries it. */
45
45
  export type TemplateRecord<TTemplate = mixed> = {|
46
- readonly above: number,
46
+ readonly above : number,
47
47
  readonly module: RouteModule<TTemplate>,
48
48
  |};
49
49
 
50
50
  /** One `$loading.js`, as the route table carries it. */
51
51
  export type LoadingRecord<TLoading = mixed> = {|
52
- readonly above: number,
52
+ readonly above : number,
53
53
  readonly module: RouteModule<TLoading>,
54
54
  |};
55
55
 
56
56
  /** One `$error.js` inside a slot, as the route table carries it. */
57
57
  export type SlotErrorBoundaryRecord<TError = mixed> = {|
58
- readonly above: number,
58
+ readonly above : number,
59
59
  readonly module: RouteModule<TError>,
60
60
  |};
61
61
 
@@ -104,16 +104,16 @@ export type SlotRouteRecord<
104
104
  TLoading = mixed,
105
105
  TError = mixed,
106
106
  > = {|
107
- readonly path: string,
108
- readonly params: $ReadOnlyArray<RouteParamSpec>,
109
- readonly mdx: boolean,
110
- readonly file: string,
111
- readonly page: RouteModule<TPage>,
112
- readonly layouts: $ReadOnlyArray<RouteModule<TLayout>>,
113
- readonly loading?: $ReadOnlyArray<LoadingRecord<TLoading>>,
114
- readonly templates?: $ReadOnlyArray<TemplateRecord<TTemplate>>,
107
+ readonly path : string,
108
+ readonly params : $ReadOnlyArray<RouteParamSpec>,
109
+ readonly mdx : boolean,
110
+ readonly file : string,
111
+ readonly page : RouteModule<TPage>,
112
+ readonly layouts : $ReadOnlyArray<RouteModule<TLayout>>,
113
+ readonly loading? : $ReadOnlyArray<LoadingRecord<TLoading>>,
114
+ readonly templates? : $ReadOnlyArray<TemplateRecord<TTemplate>>,
115
115
  readonly errorBoundary?: ?SlotErrorBoundaryRecord<TError>,
116
- readonly slots: $ReadOnlyArray<SlotRecord<TPage, TLayout, TTemplate, TLoading, TError>>,
116
+ readonly slots : $ReadOnlyArray<SlotRecord<TPage, TLayout, TTemplate, TLoading, TError>>,
117
117
  |};
118
118
 
119
119
  /** One entry of the generated route table. */
@@ -124,31 +124,31 @@ export type RouteRecord<
124
124
  TLoading = mixed,
125
125
  TError = mixed,
126
126
  > = {|
127
- readonly path: string,
128
- readonly params: $ReadOnlyArray<RouteParamSpec>,
129
- readonly mdx: boolean,
130
- readonly file: string,
131
- readonly page?: RouteModule<TPage>,
132
- readonly layouts: $ReadOnlyArray<RouteModule<TLayout>>,
133
- readonly loading?: $ReadOnlyArray<LoadingRecord<TLoading>>,
127
+ readonly path : string,
128
+ readonly params : $ReadOnlyArray<RouteParamSpec>,
129
+ readonly mdx : boolean,
130
+ readonly file : string,
131
+ readonly page? : RouteModule<TPage>,
132
+ readonly layouts : $ReadOnlyArray<RouteModule<TLayout>>,
133
+ readonly loading? : $ReadOnlyArray<LoadingRecord<TLoading>>,
134
134
  readonly templates?: $ReadOnlyArray<TemplateRecord<TTemplate>>,
135
- readonly slots?: $ReadOnlyArray<SlotRecord<TPage, TLayout, TTemplate, TLoading, TError>>,
135
+ readonly slots? : $ReadOnlyArray<SlotRecord<TPage, TLayout, TTemplate, TLoading, TError>>,
136
136
  |};
137
137
 
138
138
  /** One not-found boundary: the page for a path under `path` that matched nothing. */
139
139
  export type NotFoundBoundary<TPage = mixed, TLayout = mixed> = {|
140
- readonly path: string,
141
- readonly mdx: boolean,
142
- readonly file: string,
143
- readonly page: ?RouteModule<TPage>,
140
+ readonly path : string,
141
+ readonly mdx : boolean,
142
+ readonly file : string,
143
+ readonly page : ?RouteModule<TPage>,
144
144
  readonly layouts: $ReadOnlyArray<RouteModule<TLayout>>,
145
145
  |};
146
146
 
147
147
  /** One error boundary: what renders in place of a subtree that threw. */
148
148
  export type ErrorBoundary<TError = mixed, TLayout = mixed> = {|
149
- readonly path: string,
150
- readonly file: string,
151
- readonly module: ?RouteModule<TError>,
149
+ readonly path : string,
150
+ readonly file : string,
151
+ readonly module : ?RouteModule<TError>,
152
152
  readonly layouts: $ReadOnlyArray<RouteModule<TLayout>>,
153
153
  |};
154
154
 
@@ -163,17 +163,17 @@ export type RouteTable<
163
163
  readonly routes: $ReadOnlyArray<RouteRecord<TPage, TLayout, TTemplate, TLoading, TError>>,
164
164
  readonly nativeLinks?: {|
165
165
  readonly origins: $ReadOnlyArray<string>,
166
- readonly routes: $ReadOnlyArray<string>,
166
+ readonly routes : $ReadOnlyArray<string>,
167
167
  |},
168
168
  readonly notFound: $ReadOnlyArray<NotFoundBoundary<TPage, TLayout>>,
169
- readonly errors: $ReadOnlyArray<ErrorBoundary<TError, TLayout>>,
169
+ readonly errors : $ReadOnlyArray<ErrorBoundary<TError, TLayout>>,
170
170
  |};
171
171
 
172
172
  type UnknownRouteRecord = RouteRecord<mixed, mixed, mixed, mixed, mixed>;
173
173
 
174
174
  /** A URL matched against a table. */
175
175
  export type RouteMatch<TRoute extends { readonly path: string, ... } = UnknownRouteRecord> = {|
176
- readonly route: TRoute,
176
+ readonly route : TRoute,
177
177
  readonly params: RouteParams,
178
178
  |};
179
179
 
@@ -199,9 +199,9 @@ export type RouteError =
199
199
  export function routeErrorStatus(error: RouteError): 400 | 401 | 403 | 500 {
200
200
  return match (error) {
201
201
  {kind: "badRequest", ...} => 400,
202
- {kind: "unauthorized"} => 401,
203
- {kind: "forbidden"} => 403,
204
- {kind: "thrown", ...} => 500,
202
+ {kind: "unauthorized"} => 401,
203
+ {kind: "forbidden"} => 403,
204
+ {kind: "thrown", ...} => 500,
205
205
  };
206
206
  }
207
207
 
@@ -362,9 +362,9 @@ function specificity(segments: $ReadOnlyArray<Segment>): number {
362
362
  let score = 0;
363
363
  for (const segment of segments) {
364
364
  score += match (segment) {
365
- {kind: "static", ...} => 3,
366
- {kind: "param", ...} => 2,
367
- {kind: "catchAll", ...} => 1,
365
+ {kind: "static", ...} => 3,
366
+ {kind: "param", ...} => 2,
367
+ {kind: "catchAll", ...} => 1,
368
368
  {kind: "optionalCatchAll", ...} => 0,
369
369
  };
370
370
  }
@@ -373,26 +373,26 @@ function specificity(segments: $ReadOnlyArray<Segment>): number {
373
373
 
374
374
  function matchSegments(
375
375
  segments: $ReadOnlyArray<Segment>,
376
- parts: $ReadOnlyArray<string>,
376
+ parts : $ReadOnlyArray<string>,
377
377
  ): ?RouteParams {
378
378
  const params: { [string]: string | $ReadOnlyArray<string> } = {};
379
379
  let index = 0;
380
380
  for (const segment of segments) {
381
381
  match (segment) {
382
- {kind: "static", value: const value} => {
382
+ {kind: "static", value: const value} => {
383
383
  if (parts[index] !== value) {
384
384
  return null;
385
385
  }
386
386
  index += 1;
387
387
  }
388
- {kind: "param", name: const name} => {
388
+ {kind: "param", name: const name} => {
389
389
  if (index >= parts.length) {
390
390
  return null;
391
391
  }
392
392
  params[name] = decodeSegment(parts[index]);
393
393
  index += 1;
394
394
  }
395
- {kind: "catchAll", name: const name} => {
395
+ {kind: "catchAll", name: const name} => {
396
396
  // `[...slug]` needs something to take: `/docs` is not
397
397
  // `/docs/[...slug]`, which is what `[[...slug]]` is for. Without
398
398
  // this a catch-all outranked the page at its parent path — one more
@@ -425,10 +425,10 @@ export function buildRoute(routePath: string, params?: RouteParams): string {
425
425
  const parts: Array<string> = [];
426
426
  for (const segment of compile(routePath)) {
427
427
  match (segment) {
428
- {kind: "static", value: const value} => {
428
+ {kind: "static", value: const value} => {
429
429
  parts.push(value);
430
430
  }
431
- {kind: "param", name: const name} => {
431
+ {kind: "param", name: const name} => {
432
432
  const value = values[name];
433
433
  if (typeof value !== "string") {
434
434
  throw new Error(
@@ -437,7 +437,7 @@ export function buildRoute(routePath: string, params?: RouteParams): string {
437
437
  }
438
438
  parts.push(encodeURIComponent(value));
439
439
  }
440
- {kind: "catchAll", name: const name} => {
440
+ {kind: "catchAll", name: const name} => {
441
441
  const value = values[name];
442
442
  if (value == null || typeof value === "string") {
443
443
  throw new Error(
@@ -497,7 +497,7 @@ export function hasClientPage(route: { readonly page?: mixed, ... }): boolean {
497
497
 
498
498
  /** Match a pathname against the table, preferring the most specific route. */
499
499
  export function matchRoute<TRoute extends { readonly path: string, ... }>(
500
- routes: $ReadOnlyArray<TRoute>,
500
+ routes : $ReadOnlyArray<TRoute>,
501
501
  pathname: string,
502
502
  ): ?RouteMatch<TRoute> {
503
503
  return matchIn(routes, pathname);
@@ -510,7 +510,7 @@ export function matchRoute<TRoute extends { readonly path: string, ... }>(
510
510
  * matched by this function rather than by one of its own.
511
511
  */
512
512
  export function matchIn<TRoute extends { readonly path: string, ... }>(
513
- routes: $ReadOnlyArray<TRoute>,
513
+ routes : $ReadOnlyArray<TRoute>,
514
514
  pathname: string,
515
515
  ): ?RouteMatch<TRoute> {
516
516
  const parts = pathname.split("/").filter((part) => part !== "");
@@ -542,9 +542,9 @@ function covers(segments: $ReadOnlyArray<Segment>, parts: $ReadOnlyArray<string>
542
542
  for (const segment of segments) {
543
543
  const next = match (segment) {
544
544
  {kind: "static", value: const value} => parts[index] === value ? index + 1 : -1,
545
- {kind: "param", ...} => index < parts.length ? index + 1 : -1,
546
- {kind: "catchAll", ...} => index < parts.length ? parts.length : -1,
547
- {kind: "optionalCatchAll", ...} => parts.length,
545
+ {kind: "param", ...} => index < parts.length ? index + 1 : -1,
546
+ {kind: "catchAll", ...} => index < parts.length ? parts.length : -1,
547
+ {kind: "optionalCatchAll", ...} => parts.length,
548
548
  };
549
549
  if (next === -1) {
550
550
  return false;
@@ -557,7 +557,7 @@ function covers(segments: $ReadOnlyArray<Segment>, parts: $ReadOnlyArray<string>
557
557
  /** The nearest boundary above `pathname`, or `null` when none covers it. */
558
558
  export function nearestBoundary<TBoundary extends { readonly path: string, ... }>(
559
559
  boundaries: $ReadOnlyArray<TBoundary>,
560
- pathname: string,
560
+ pathname : string,
561
561
  ): ?TBoundary {
562
562
  const parts = pathname.split("/").filter((part) => part !== "");
563
563
  let best: ?TBoundary = null;
@@ -585,7 +585,7 @@ export function splitUrl(url: string): {| readonly pathname: string, readonly se
585
585
  }
586
586
  return {
587
587
  pathname: normalizePathname(withoutHash.slice(0, question)),
588
- search: withoutHash.slice(question),
588
+ search : withoutHash.slice(question),
589
589
  };
590
590
  }
591
591