akanjs 2.2.4-rc.0 → 2.2.4-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ interface FileUploadSerializedEndpoint {
2
+ fileUpload?: boolean;
3
+ }
4
+
5
+ interface FileUploadSerializedSignal {
6
+ prefix?: string;
7
+ endpoint: Record<string, FileUploadSerializedEndpoint>;
8
+ }
9
+
10
+ /** Framework-owned file-upload contract shared by client-safe packages. */
11
+ export const fileUploadContract = {
12
+ fields: { files: "files", metas: "metas", type: "type", parentId: "parentId" },
13
+ buildMetas: (fileList: FileList) =>
14
+ Array.from(fileList).map((f) => ({ lastModifiedAt: new Date(f.lastModified).toISOString(), size: f.size })),
15
+ } as const;
16
+
17
+ export interface FileUploadCapability {
18
+ refName: string;
19
+ endpointKey: string;
20
+ prefix?: string;
21
+ }
22
+
23
+ /** Discovers the upload endpoint marked with `{ fileUpload: true }` from the serialized signal. */
24
+ export const resolveFileUploadCapability = (
25
+ serializedSignal: Record<string, FileUploadSerializedSignal>,
26
+ ): FileUploadCapability | null => {
27
+ const matches: FileUploadCapability[] = [];
28
+ for (const [refName, signal] of Object.entries(serializedSignal))
29
+ for (const [endpointKey, endpoint] of Object.entries(signal.endpoint))
30
+ if (endpoint.fileUpload) matches.push({ refName, endpointKey, prefix: signal.prefix });
31
+ if (matches.length > 1)
32
+ console.warn(
33
+ `[akan] Multiple fileUpload endpoints found (${matches
34
+ .map((m) => `${m.refName}.${m.endpointKey}`)
35
+ .join(", ")}). Using the first; mark only one mutation with { fileUpload: true }.`,
36
+ );
37
+ return matches[0] ?? null;
38
+ };
package/common/index.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  export { applyMixins } from "./applyMixins";
2
2
  export { capitalize } from "./capitalize";
3
3
  export { deepObjectify } from "./deepObjectify";
4
+ export {
5
+ type FileUploadCapability,
6
+ fileUploadContract,
7
+ resolveFileUploadCapability,
8
+ } from "./fileUpload";
4
9
  export { formatNumber } from "./formatNumber";
5
10
  export { formatPhone } from "./formatPhone";
6
11
  export { getAllPropertyDescriptors } from "./getAllPropertyDescriptors";
@@ -1,5 +1,11 @@
1
1
  import { DataList, getEnv, PrimitiveRegistry, type PromiseOrObject } from "akanjs/base";
2
- import { capitalize, type FetchPolicy, Logger } from "akanjs/common";
2
+ import {
3
+ capitalize,
4
+ type FetchPolicy,
5
+ fileUploadContract,
6
+ Logger,
7
+ resolveFileUploadCapability,
8
+ } from "akanjs/common";
3
9
  import { type BaseInsight, type BaseObject, ConstantRegistry, deserialize, serialize } from "akanjs/constant";
4
10
  import type {
5
11
  DatabaseSignal,
@@ -362,18 +368,20 @@ export class FetchClient {
362
368
 
363
369
  Object.assign(this.handler, {
364
370
  [names.addModelFiles]: async (fileList: FileList, parentId?: string, option?: FetchPolicy) => {
371
+ const cap = resolveFileUploadCapability(this.serializedSignal);
372
+ const endpoint = cap ? this.serializedSignal[cap.refName]?.endpoint[cap.endpointKey] : undefined;
373
+ if (!cap || !endpoint)
374
+ throw new Error(
375
+ "File upload is not configured. Mark an upload mutation with { fileUpload: true } (e.g. shared FileEndpoint.addFiles).",
376
+ );
377
+ const { fields, buildMetas } = fileUploadContract;
365
378
  const formData = new FormData();
366
- for (let i = 0; i < fileList.length; i++) formData.append("files", fileList[i]);
367
- const metas = Array.from(fileList).map((f) => ({
368
- lastModifiedAt: new Date(f.lastModified).toISOString(),
369
- size: f.size,
370
- }));
371
- formData.append("metas", JSON.stringify(metas));
372
- formData.append("type", refName);
373
- if (parentId) formData.append("parentId", parentId);
374
- return await this.http.post(`/file/addFilesRestApi`, formData, {
375
- headers: { ...(this.jwt ? { Authorization: `Bearer ${this.jwt}` } : {}) },
376
- });
379
+ for (let i = 0; i < fileList.length; i++) formData.append(fields.files, fileList[i]);
380
+ formData.append(fields.metas, JSON.stringify(buildMetas(fileList)));
381
+ formData.append(fields.type, refName);
382
+ if (parentId) formData.append(fields.parentId, parentId);
383
+ const url = FetchClient.makeHttpUrl(cap.endpointKey, endpoint, cap.prefix, new Map());
384
+ return await this.http.post(url, formData, { headers: this.#makeAuthHeaders(option) });
377
385
  },
378
386
  });
379
387
  }
@@ -67,6 +67,7 @@ export class FetchSerializer {
67
67
  ...(endpointInfo.signalOption.globalPrefix !== undefined
68
68
  ? { globalPrefix: endpointInfo.signalOption.globalPrefix }
69
69
  : {}),
70
+ ...(endpointInfo.signalOption.fileUpload ? { fileUpload: true } : {}),
70
71
  ...(guards?.length ? { guards } : {}),
71
72
  };
72
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.2.4-rc.0",
3
+ "version": "2.2.4-rc.2",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -259,7 +259,7 @@ export class InjectInfo<
259
259
  get: async (key: string) => {
260
260
  const getter = injectInfo.get as unknown as (value: unknown) => unknown;
261
261
  const value = await cacheAdaptor.hget(`akan:memory:${injectInfo.parentRefName}`, propKey, key);
262
- return value === null ? value : getter(value);
262
+ return value === null ? undefined : getter(value);
263
263
  },
264
264
  set: async (key: string, value: unknown) => {
265
265
  const setter = injectInfo.set as unknown as (value: unknown) => string | number | Buffer;
@@ -346,6 +346,7 @@ export const injectionBuilder = (parentRefName: string) => ({
346
346
  const isMap = modelRef === Map;
347
347
  if (isMap && !opts.of) throw new Error("of should be provided when modelRef is Map");
348
348
  type FieldValue = never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>;
349
+ type MapFieldValue = never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>;
349
350
  type IsNullable = DefaultValue extends never ? true : false;
350
351
  type UseValue = IsNullable extends true ? FieldValue | null : FieldValue;
351
352
  return new InjectInfo<
@@ -356,8 +357,8 @@ export const injectionBuilder = (parentRefName: string) => ({
356
357
  : UseValue
357
358
  : MapConstructor extends ValueRef
358
359
  ? {
359
- get: (key: string) => Promise<FieldToValue<MapValue>>;
360
- set: (key: string, value: FieldToValue<MapValue>) => Promise<void>;
360
+ get: (key: string) => Promise<MapFieldValue | undefined>;
361
+ set: (key: string, value: MapFieldValue) => Promise<void>;
361
362
  delete: (key: string) => Promise<void>;
362
363
  }
363
364
  : { get: () => Promise<UseValue>; set: (value: UseValue) => Promise<void>; delete: () => Promise<void> },
@@ -64,6 +64,7 @@ export class FetchSerializer {
64
64
  args: endpointInfo.args.map(FetchSerializer.#serializeArg),
65
65
  returns: FetchSerializer.#serializeReturns(endpointInfo),
66
66
  ...(endpointInfo.signalOption.path ? { path: endpointInfo.signalOption.path } : {}),
67
+ ...(endpointInfo.signalOption.fileUpload ? { fileUpload: true } : {}),
67
68
  ...(guards?.length ? { guards } : {}),
68
69
  };
69
70
  }
package/signal/types.ts CHANGED
@@ -70,6 +70,8 @@ export interface SignalOption<Response = any, Nullable extends boolean = false,
70
70
  middlewares?: MiddlewareCls[];
71
71
  prefix?: false | string;
72
72
  globalPrefix?: false;
73
+ /** Marks this mutation as the framework file-upload endpoint (see resolveFileUploadCapability). */
74
+ fileUpload?: boolean;
73
75
 
74
76
  scheduleType?: "init" | "destroy" | "cron" | "interval" | "timeout";
75
77
  scheduleCron?: string;
@@ -84,6 +86,7 @@ interface SerializedSignalOption {
84
86
  prefix?: false | string;
85
87
  globalPrefix?: false;
86
88
  guards?: string[];
89
+ fileUpload?: boolean;
87
90
  }
88
91
  export interface SerializedSlice extends SerializedSignalOption {}
89
92
 
package/store/action.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import { DataList, type Dayjs, FIELD_META, type GetStateObject, type SLICE_META } from "akanjs/base";
2
- import { capitalize, deepObjectify, type FetchPolicy, isQueryEqual, Logger, lowerlize, pathSet } from "akanjs/common";
2
+ import {
3
+ capitalize,
4
+ deepObjectify,
5
+ type FetchPolicy,
6
+ isQueryEqual,
7
+ Logger,
8
+ lowerlize,
9
+ pathSet,
10
+ resolveFileUploadCapability,
11
+ } from "akanjs/common";
3
12
  import {
4
13
  type BaseInsight,
5
14
  type BaseObject,
@@ -277,6 +286,7 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
277
286
  type Light = BaseObject;
278
287
  const [fieldName, className] = [refName, capitalize(refName)];
279
288
  const modelRef = ConstantRegistry.getDatabase(refName).full;
289
+ const fileUploadRefName = resolveFileUploadCapability(fetch.serializedSignal)?.refName;
280
290
 
281
291
  const names = {
282
292
  model: fieldName,
@@ -356,7 +366,7 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
356
366
  },
357
367
  }
358
368
  : {}),
359
- ...(field.isClass && ConstantRegistry.getRefName(field.modelRef) === "file"
369
+ ...(field.isClass && !!fileUploadRefName && ConstantRegistry.getRefName(field.modelRef) === fileUploadRefName
360
370
  ? {
361
371
  [namesOfField.uploadFieldOnModel]: async function (this: SetGet, fileList: FileList, index?: number) {
362
372
  const form = (this.get() as { [key: string]: any })[names.modelForm] as { [key: string]: any };
@@ -383,7 +393,9 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
383
393
  const intervalKey = setInterval(() => {
384
394
  void (async () => {
385
395
  const currentFile = await (
386
- (fetch as { [key: string]: any }).file as (id: string) => Promise<ProtoFile>
396
+ (fetch as { [key: string]: any })[fileUploadRefName as string] as (
397
+ id: string,
398
+ ) => Promise<ProtoFile>
387
399
  )(file.id);
388
400
  if (field.isArray)
389
401
  this.set((state: { [key: string]: { [key: string]: ProtoFile[] } }) => {
@@ -0,0 +1,28 @@
1
+ interface FileUploadSerializedEndpoint {
2
+ fileUpload?: boolean;
3
+ }
4
+ interface FileUploadSerializedSignal {
5
+ prefix?: string;
6
+ endpoint: Record<string, FileUploadSerializedEndpoint>;
7
+ }
8
+ /** Framework-owned file-upload contract shared by client-safe packages. */
9
+ export declare const fileUploadContract: {
10
+ readonly fields: {
11
+ readonly files: "files";
12
+ readonly metas: "metas";
13
+ readonly type: "type";
14
+ readonly parentId: "parentId";
15
+ };
16
+ readonly buildMetas: (fileList: FileList) => {
17
+ lastModifiedAt: string;
18
+ size: number;
19
+ }[];
20
+ };
21
+ export interface FileUploadCapability {
22
+ refName: string;
23
+ endpointKey: string;
24
+ prefix?: string;
25
+ }
26
+ /** Discovers the upload endpoint marked with `{ fileUpload: true }` from the serialized signal. */
27
+ export declare const resolveFileUploadCapability: (serializedSignal: Record<string, FileUploadSerializedSignal>) => FileUploadCapability | null;
28
+ export {};
@@ -1,6 +1,7 @@
1
1
  export { applyMixins } from "./applyMixins.d.ts";
2
2
  export { capitalize } from "./capitalize.d.ts";
3
3
  export { deepObjectify } from "./deepObjectify.d.ts";
4
+ export { type FileUploadCapability, fileUploadContract, resolveFileUploadCapability, } from "./fileUpload.d.ts";
4
5
  export { formatNumber } from "./formatNumber.d.ts";
5
6
  export { formatPhone } from "./formatPhone.d.ts";
6
7
  export { getAllPropertyDescriptors } from "./getAllPropertyDescriptors.d.ts";
@@ -76,8 +76,8 @@ export declare const injectionBuilder: (parentRefName: string) => {
76
76
  get?: GetFn;
77
77
  set?: (value: ReturnType<GetFn>) => GetFieldValue<ValueRef, ExplicitType>;
78
78
  }) => InjectInfo<"memory", Local extends true ? MapConstructor extends ValueRef ? Map<string, FieldToValue<MapValue>> : (DefaultValue extends never ? true : false) extends true ? (never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>) | null : never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn> : MapConstructor extends ValueRef ? {
79
- get: (key: string) => Promise<FieldToValue<MapValue>>;
80
- set: (key: string, value: FieldToValue<MapValue>) => Promise<void>;
79
+ get: (key: string) => Promise<(never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>) | undefined>;
80
+ set: (key: string, value: never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>) => Promise<void>;
81
81
  delete: (key: string) => Promise<void>;
82
82
  } : {
83
83
  get: () => Promise<(DefaultValue extends never ? true : false) extends true ? (never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>) | null : never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>>;
@@ -60,6 +60,8 @@ export interface SignalOption<Response = any, Nullable extends boolean = false,
60
60
  middlewares?: MiddlewareCls[];
61
61
  prefix?: false | string;
62
62
  globalPrefix?: false;
63
+ /** Marks this mutation as the framework file-upload endpoint (see resolveFileUploadCapability). */
64
+ fileUpload?: boolean;
63
65
  scheduleType?: "init" | "destroy" | "cron" | "interval" | "timeout";
64
66
  scheduleCron?: string;
65
67
  scheduleTime?: number;
@@ -72,6 +74,7 @@ interface SerializedSignalOption {
72
74
  prefix?: false | string;
73
75
  globalPrefix?: false;
74
76
  guards?: string[];
77
+ fileUpload?: boolean;
75
78
  }
76
79
  export interface SerializedSlice extends SerializedSignalOption {
77
80
  }
@@ -6,9 +6,9 @@ import { type HTMLAttributes, type ReactNode, type RefObject } from "react";
6
6
  export declare const Client: {
7
7
  (): import("react/jsx-runtime").JSX.Element;
8
8
  Wrapper: ({ children, theme, lang, dictionary, signals, reconnect, }: ClientWrapperProps) => import("react/jsx-runtime").JSX.Element;
9
- Bridge: ({ env, lang, theme, prefix, gaTrackingId, }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
9
+ Bridge: ({ env, lang, theme, prefix, gaTrackingId }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
10
10
  Inner: () => import("react/jsx-runtime").JSX.Element;
11
- SsrBridge: ({ lang, prefix, }: ClientSsrBridgeProps) => null;
11
+ SsrBridge: ({ lang, prefix }: ClientSsrBridgeProps) => null;
12
12
  };
13
13
  interface ClientWrapperProps {
14
14
  children: ReactNode;
@@ -37,11 +37,11 @@ interface ClientBridgeProps {
37
37
  prefix?: string;
38
38
  gaTrackingId?: string;
39
39
  }
40
- export declare const ClientBridge: ({ env, lang, theme, prefix, gaTrackingId, }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
40
+ export declare const ClientBridge: ({ env, lang, theme, prefix, gaTrackingId }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
41
41
  export declare const ClientInner: () => import("react/jsx-runtime").JSX.Element;
42
42
  interface ClientSsrBridgeProps {
43
43
  lang: string;
44
44
  prefix?: string;
45
45
  }
46
- export declare const ClientSsrBridge: ({ lang, prefix, }: ClientSsrBridgeProps) => null;
46
+ export declare const ClientSsrBridge: ({ lang, prefix }: ClientSsrBridgeProps) => null;
47
47
  export {};
@@ -60,8 +60,6 @@ export const ClientWrapper = ({
60
60
 
61
61
  if (typeof window !== "undefined") Translator.setActiveLocale(lang);
62
62
  }
63
- if (getEnv().renderMode === "ssr") {
64
- }
65
63
  useLayoutEffect(() => {
66
64
  Logger.rawLog(logo);
67
65
  }, []);
@@ -75,8 +73,7 @@ export const ClientWrapper = ({
75
73
  };
76
74
  Client.Wrapper = ClientWrapper;
77
75
 
78
- interface ClientPathWrapperProps
79
- extends Omit<HTMLAttributes<HTMLDivElement>, "style"> {
76
+ interface ClientPathWrapperProps extends Omit<HTMLAttributes<HTMLDivElement>, "style"> {
80
77
  bind?: () => HTMLAttributes<HTMLDivElement>;
81
78
  wrapperRef?: RefObject<HTMLDivElement | null> | null;
82
79
  pageType?: "current" | "prev" | "cached";
@@ -97,18 +94,12 @@ export const ClientPathWrapper = ({
97
94
  layoutStyle = "web",
98
95
  ...props
99
96
  }: ClientPathWrapperProps) => {
100
- const href =
101
- location?.href ??
102
- (typeof window !== "undefined" ? window.location.href : "");
103
- const hash =
104
- location?.hash ??
105
- (typeof window !== "undefined" ? window.location.hash : "");
97
+ const href = location?.href ?? (typeof window !== "undefined" ? window.location.href : "");
98
+ const hash = location?.hash ?? (typeof window !== "undefined" ? window.location.hash : "");
106
99
  const pathname = location?.pathname ?? "/";
107
100
  const params = location?.params ?? {};
108
101
  const searchParams = location?.searchParams ?? {};
109
- const search =
110
- location?.search ??
111
- (typeof window !== "undefined" ? window.location.search : "");
102
+ const search = location?.search ?? (typeof window !== "undefined" ? window.location.search : "");
112
103
  const lang = params.lang;
113
104
  const firstPath = pathname.split("/")[2];
114
105
  const pathRoute: PathRoute = location?.pathRoute ?? {
@@ -141,9 +132,7 @@ export const ClientPathWrapper = ({
141
132
  }}
142
133
  >
143
134
  <animated.div
144
- {...(bind && pathRoute.pageState.gesture && gestureEnabled
145
- ? bind()
146
- : {})}
135
+ {...(bind && pathRoute.pageState.gesture && gestureEnabled ? bind() : {})}
147
136
  className={clsx("group/path", className)}
148
137
  ref={wrapperRef}
149
138
  {...props}
@@ -165,13 +154,7 @@ interface ClientBridgeProps {
165
154
  gaTrackingId?: string;
166
155
  }
167
156
 
168
- export const ClientBridge = ({
169
- env,
170
- lang,
171
- theme,
172
- prefix,
173
- gaTrackingId,
174
- }: ClientBridgeProps) => {
157
+ export const ClientBridge = ({ env, lang, theme, prefix, gaTrackingId }: ClientBridgeProps) => {
175
158
  const uiOperation = st.use.uiOperation();
176
159
  const pathname = st.use.pathname();
177
160
  const params = st.use.params();
@@ -238,26 +221,18 @@ function applyThemePolicy(theme: AkanTheme): void {
238
221
  }
239
222
  if (theme === "system") {
240
223
  const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
241
- document.documentElement.setAttribute(
242
- "data-theme",
243
- dark ? "dark" : "light",
244
- );
224
+ document.documentElement.setAttribute("data-theme", dark ? "dark" : "light");
245
225
  return;
246
226
  }
247
227
  document.documentElement.setAttribute("data-theme", theme);
248
228
  }
249
229
 
250
- function buildSearchParams(
251
- entries: Iterable<[string, string]>,
252
- ): Record<string, string | string[]> {
230
+ function buildSearchParams(entries: Iterable<[string, string]>): Record<string, string | string[]> {
253
231
  const params: Record<string, string | string[]> = {};
254
232
  for (const [key, value] of entries) {
255
233
  const current = params[key];
256
234
  if (current === undefined) params[key] = value;
257
- else
258
- params[key] = Array.isArray(current)
259
- ? [...current, value]
260
- : [current, value];
235
+ else params[key] = Array.isArray(current) ? [...current, value] : [current, value];
261
236
  }
262
237
  return params;
263
238
  }
@@ -277,10 +252,7 @@ interface ClientSsrBridgeProps {
277
252
  lang: string;
278
253
  prefix?: string;
279
254
  }
280
- export const ClientSsrBridge = ({
281
- lang,
282
- prefix = "",
283
- }: ClientSsrBridgeProps) => {
255
+ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) => {
284
256
  useEffect(() => {
285
257
  const visiblePrefix = getEnv().operationMode === "local" ? prefix : "";
286
258
  const navigateRscWithFallback = (
@@ -294,19 +266,13 @@ export const ClientSsrBridge = ({
294
266
  return;
295
267
  }
296
268
  void navigation.catch((error) => {
297
- Logger.warn(
298
- `RSC navigation failed, falling back to document navigation: ${String(error)}`,
299
- );
269
+ Logger.warn(`RSC navigation failed, falling back to document navigation: ${String(error)}`);
300
270
  fallback();
301
271
  });
302
272
  };
303
273
  const syncHref = (href: string) => {
304
274
  const url = new URL(href, window.location.origin);
305
- const { path } = getPathInfo(
306
- `${url.pathname}${url.search}${url.hash}`,
307
- lang,
308
- visiblePrefix,
309
- );
275
+ const { path } = getPathInfo(`${url.pathname}${url.search}${url.hash}`, lang, visiblePrefix);
310
276
  const searchParams = buildSearchParams(url.searchParams.entries());
311
277
  st.set({ pathname: url.pathname, path, searchParams });
312
278
  };
@@ -318,17 +284,11 @@ export const ClientSsrBridge = ({
318
284
  router: {
319
285
  push: (href, routeOptions) => {
320
286
  syncHref(href);
321
- navigateRscWithFallback(href, routeOptions, () =>
322
- window.location.assign(href),
323
- );
287
+ navigateRscWithFallback(href, routeOptions, () => window.location.assign(href));
324
288
  },
325
289
  replace: (href, routeOptions) => {
326
290
  syncHref(href);
327
- navigateRscWithFallback(
328
- href,
329
- { ...routeOptions, replace: true },
330
- () => window.location.replace(href),
331
- );
291
+ navigateRscWithFallback(href, { ...routeOptions, replace: true }, () => window.location.replace(href));
332
292
  },
333
293
  back: () => {
334
294
  window.history.back();
@@ -350,14 +310,8 @@ export const ClientSsrBridge = ({
350
310
  const visiblePrefix = getEnv().operationMode === "local" ? prefix : "";
351
311
  const sync = () => {
352
312
  const { pathname, search, hash } = window.location;
353
- const { path } = getPathInfo(
354
- `${pathname}${search}${hash}`,
355
- lang,
356
- visiblePrefix,
357
- );
358
- const searchParams = buildSearchParams(
359
- new URLSearchParams(search).entries(),
360
- );
313
+ const { path } = getPathInfo(`${pathname}${search}${hash}`, lang, visiblePrefix);
314
+ const searchParams = buildSearchParams(new URLSearchParams(search).entries());
361
315
  st.set({ pathname: window.location.pathname, path, searchParams });
362
316
  };
363
317
  sync();