akanjs 2.1.1-rc.1 → 2.1.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/client/router.ts CHANGED
@@ -64,6 +64,11 @@ const getConfiguredBasePaths = () => new Set(parseBasePaths(process.env.AKAN_PUB
64
64
 
65
65
  const shouldExposeBasePath = () => getEnv().operationMode === "local";
66
66
 
67
+ const getLocaleFromPathname = (pathname: string) => {
68
+ const [firstSegment] = pathname.split("/").filter(Boolean);
69
+ return parseAkanI18nEnv().locales.find((locale) => locale === firstSegment);
70
+ };
71
+
67
72
  const getServerBasePath = (reqPathname: string, lang: string, headerBasePath: string | undefined, fallback: string) => {
68
73
  return (
69
74
  getBasePathFromPathname(reqPathname, {
@@ -195,6 +200,9 @@ class Router {
195
200
  #getNavigationPathInfo(href: string) {
196
201
  return this.#getPathInfo(href, shouldExposeBasePath() ? this.#prefix : "");
197
202
  }
203
+ #getVisiblePathInfo(href: string, lang = this.#lang) {
204
+ return getPathInfo(href, lang, shouldExposeBasePath() ? this.#prefix : "");
205
+ }
198
206
  #postPathChange({ path, pathname, hash }: { path: string; pathname: string; hash: string }) {
199
207
  Logger.log(`pathChange-start:${path}${hash ? `#${hash}` : ""}`);
200
208
  window.parent.postMessage({ type: "pathChange", path, pathname, hash }, "*");
@@ -251,9 +259,13 @@ class Router {
251
259
  setLang(lang: string) {
252
260
  if (getEnv().side === "server") throw new Error("setLang is only available in client side");
253
261
  this.#checkInitialized();
254
- const { path } = getPathInfo(window.location.pathname, this.#lang, this.#prefix);
262
+ const currentLang = getLocaleFromPathname(window.location.pathname) ?? this.#lang;
263
+ const { path, search, hash } = this.#getVisiblePathInfo(
264
+ `${window.location.pathname}${window.location.search ?? ""}${window.location.hash ?? ""}`,
265
+ currentLang,
266
+ );
255
267
  this.#lang = lang;
256
- this.#instance.replace(`/${lang}${path}`);
268
+ this.#instance.replace(`${path}${search ? `?${search}` : ""}${hash ? `#${hash}` : ""}`);
257
269
  return undefined as never;
258
270
  }
259
271
  getPath(pathname = window.location.pathname) {
@@ -13,7 +13,13 @@ import {
13
13
  } from "akanjs/base";
14
14
  import { Logger } from "akanjs/common";
15
15
 
16
- import { type BaseObject, type ConstantCls, ConstantRegistry, type DefaultOf, type FieldProps } from ".";
16
+ import {
17
+ type BaseObject,
18
+ type ConstantCls,
19
+ ConstantRegistry,
20
+ type DefaultOf,
21
+ type FieldProps,
22
+ } from ".";
17
23
 
18
24
  type Purified<O> = O extends BaseObject
19
25
  ? string
@@ -24,10 +30,15 @@ type Purified<O> = O extends BaseObject
24
30
  : O extends object
25
31
  ? PurifiedModel<O>
26
32
  : O;
27
- type PurifiedWithObjectToId<T, StateKeys extends keyof GetStateObject<T> = keyof GetStateObject<T>> = {
33
+ type PurifiedWithObjectToId<
34
+ T,
35
+ StateKeys extends keyof GetStateObject<T> = keyof GetStateObject<T>,
36
+ > = {
28
37
  [K in StateKeys as null extends T[K] ? never : K]: Purified<T[K]>;
29
38
  } & {
30
- [K in StateKeys as null extends T[K] ? K : never]?: Purified<T[K]> | undefined;
39
+ [K in StateKeys as null extends T[K] ? K : never]?:
40
+ | Purified<T[K]>
41
+ | undefined;
31
42
  };
32
43
  export type PurifiedModel<T> = T extends Upload[]
33
44
  ? FileList
@@ -39,20 +50,29 @@ export type PurifiedModel<T> = T extends Upload[]
39
50
  ? Map<K, PurifiedModel<V>>
40
51
  : PurifiedWithObjectToId<T>;
41
52
 
42
- export type PurifyFunc<Input, _DefaultInput = DefaultOf<Input>, _PurifiedInput = PurifiedModel<Input>> = (
43
- self: _DefaultInput,
44
- isChild?: boolean,
45
- ) => _PurifiedInput | null;
53
+ export type PurifyFunc<
54
+ Input,
55
+ _DefaultInput = DefaultOf<Input>,
56
+ _PurifiedInput = PurifiedModel<Input>,
57
+ > = (self: _DefaultInput, isChild?: boolean) => _PurifiedInput | null;
46
58
 
47
59
  const getPurifyFn = (modelRef: Cls): ((value: unknown) => unknown) => {
48
60
  const [valueRef] = getNonArrayModel(modelRef);
49
61
  const purifyFn = PrimitiveRegistry.has(valueRef)
50
- ? (value: unknown) => (valueRef as unknown as typeof PrimitiveScalar)._serialize(value as never)
62
+ ? (value: unknown) =>
63
+ (valueRef as unknown as typeof PrimitiveScalar)._serialize(
64
+ value as never,
65
+ )
51
66
  : (value: unknown) => value as object;
52
67
  return purifyFn;
53
68
  };
54
69
 
55
- const purify = (field: FieldProps, key: string, value: unknown, self: Record<string, unknown>): unknown => {
70
+ const purify = (
71
+ field: FieldProps,
72
+ key: string,
73
+ value: unknown,
74
+ self: Record<string, unknown>,
75
+ ): unknown => {
56
76
 
57
77
  if (
58
78
  field.nullable &&
@@ -63,30 +83,67 @@ const purify = (field: FieldProps, key: string, value: unknown, self: Record<str
63
83
  )
64
84
  return null;
65
85
  if (field.isArray) {
66
- if (!Array.isArray(value)) throw new Error(`Invalid Array Value in ${key} for value ${value}`);
86
+ if (!Array.isArray(value))
87
+ throw new Error(`Invalid Array Value in ${key} for value ${value}`);
67
88
  if (field.minlength && value.length < field.minlength)
68
- throw new Error(`Invalid Array Length (Min) in ${key} for value ${value}`);
89
+ throw new Error(
90
+ `Invalid Array Length (Min) in ${key} for value ${value}`,
91
+ );
69
92
  else if (field.maxlength && value.length > field.maxlength)
70
- throw new Error(`Invalid Array Length (Max) in ${key} for value ${value}`);
71
- else if (field.optArrDepth === 0 && field.validate && !field.validate(value, self))
72
- throw new Error(`Invalid Array Value (Failed to pass validation) in ${key} for value ${value}`);
73
- return value.map((v) => purify({ ...field, isArray: field.arrDepth > 1, arrDepth: field.arrDepth - 1 }, key, v, v));
93
+ throw new Error(
94
+ `Invalid Array Length (Max) in ${key} for value ${value}`,
95
+ );
96
+ else if (
97
+ field.optArrDepth === 0 &&
98
+ field.validate &&
99
+ !field.validate(value, self)
100
+ )
101
+ throw new Error(
102
+ `Invalid Array Value (Failed to pass validation) in ${key} for value ${value}`,
103
+ );
104
+ return value.map((v) =>
105
+ purify(
106
+ { ...field, isArray: field.arrDepth > 1, arrDepth: field.arrDepth - 1 },
107
+ key,
108
+ v,
109
+ v,
110
+ ),
111
+ );
74
112
  }
75
113
  if (field.isMap && field.of) {
76
114
  const purifyFn = PrimitiveRegistry.has(field.of as Cls)
77
115
  ? getPurifyFn(field.of as Cls)
78
- : (value: unknown) => makePurify(field.of as ConstantCls)(value as object);
116
+ : (value: unknown) =>
117
+ makePurify(field.of as ConstantCls)(value as object);
79
118
  return Object.fromEntries(
80
- [...(value as Map<string, unknown>).entries()].map(([key, val]) => [key, applyFnToArrayObjects(val, purifyFn)]),
119
+ [...(value as Map<string, unknown>).entries()].map(([key, val]) => [
120
+ key,
121
+ applyFnToArrayObjects(val, purifyFn),
122
+ ]),
81
123
  );
82
124
  }
83
- if (field.isClass) return makePurify(field.modelRef)(value as object, true) as object;
84
- if (field.modelRef === Date && dayjs(value as Date).isBefore(dayjs(new Date("0000"))))
85
- throw new Error(`Invalid Date Value (Default) in ${key} for value ${value}`);
86
- if ([String, ID].includes(field.modelRef as unknown as StringConstructor | typeof ID) && (value === "" || !value))
87
- throw new Error(`Invalid String Value (Default) in ${key} for value ${value}`);
125
+ if (field.isClass)
126
+ return makePurify(field.modelRef)(value as object, true) as object;
127
+ if (
128
+ field.modelRef === Date &&
129
+ dayjs(value as Date).isBefore(dayjs(new Date("0000")))
130
+ )
131
+ throw new Error(
132
+ `Invalid Date Value (Default) in ${key} for value ${value}`,
133
+ );
134
+ if (
135
+ [String, ID].includes(
136
+ field.modelRef as unknown as StringConstructor | typeof ID,
137
+ ) &&
138
+ (value === "" || !value)
139
+ )
140
+ throw new Error(
141
+ `Invalid String Value (Default) in ${key} for value ${value}`,
142
+ );
88
143
  if (field.validate && !field.validate(value, self))
89
- throw new Error(`Invalid Value (Failed to pass validation) / ${value} in ${key}`);
144
+ throw new Error(
145
+ `Invalid Value (Failed to pass validation) / ${value} in ${key}`,
146
+ );
90
147
  if (!field.nullable && !value && value !== 0 && value !== false)
91
148
  throw new Error(`Invalid Value (Nullable) in ${key} for value ${value}`);
92
149
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.1.1-rc.1",
3
+ "version": "2.1.1",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -241,6 +241,7 @@ interface ClientSsrBridgeProps {
241
241
  }
242
242
  export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) => {
243
243
  useEffect(() => {
244
+ const visiblePrefix = getEnv().operationMode === "local" ? prefix : "";
244
245
  const navigateRscWithFallback = (
245
246
  href: string,
246
247
  routeOptions: Parameters<typeof navigateRsc>[1],
@@ -258,7 +259,7 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
258
259
  };
259
260
  const syncHref = (href: string) => {
260
261
  const url = new URL(href, window.location.origin);
261
- const { path } = getPathInfo(`${url.pathname}${url.search}${url.hash}`, lang, prefix);
262
+ const { path } = getPathInfo(`${url.pathname}${url.search}${url.hash}`, lang, visiblePrefix);
262
263
  const searchParams = buildSearchParams(url.searchParams.entries());
263
264
  st.set({ pathname: url.pathname, path, searchParams });
264
265
  };
@@ -290,9 +291,10 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
290
291
  }, [lang, prefix]);
291
292
 
292
293
  useEffect(() => {
294
+ const visiblePrefix = getEnv().operationMode === "local" ? prefix : "";
293
295
  const sync = () => {
294
296
  const { pathname, search, hash } = window.location;
295
- const { path } = getPathInfo(`${pathname}${search}${hash}`, lang, prefix);
297
+ const { path } = getPathInfo(`${pathname}${search}${hash}`, lang, visiblePrefix);
296
298
  const searchParams = buildSearchParams(new URLSearchParams(search).entries());
297
299
  st.set({ pathname: window.location.pathname, path, searchParams });
298
300
  };