akanjs 2.3.2-rc.6 → 2.3.2-rc.8

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.
@@ -23,13 +23,15 @@ interface ErrPayload extends ErrRestoreOption {
23
23
  const getPageInfo = (): { locale: string; path: string } => {
24
24
  const { defaultLocale, locales } = parseAkanI18nEnv();
25
25
  const localeSet = new Set(locales);
26
+ const activeLocale = Translator.getActiveLocale();
27
+ const activePath = Translator.getActivePath();
28
+ if (activePath) return { locale: activeLocale ?? defaultLocale, path: activePath };
26
29
  if (getEnv().side !== "server") {
27
30
  const [, firstSegment = "", ...rest] = window.location.pathname.split("/");
28
31
  const hasLocalePrefix = localeSet.has(firstSegment);
29
32
 
30
- const activeLocale = Translator.getActiveLocale();
31
33
  const locale = activeLocale ?? (hasLocalePrefix ? firstSegment : defaultLocale);
32
- return { locale, path: hasLocalePrefix ? `/${rest.join("/")}` : window.location.pathname };
34
+ return { locale, path: activePath ?? (hasLocalePrefix ? `/${rest.join("/")}` : window.location.pathname) };
33
35
  }
34
36
  const h = untrackedHeaders();
35
37
 
@@ -14,7 +14,11 @@ interface TranslatorState {
14
14
 
15
15
  seededDicts: WeakSet<object>;
16
16
 
17
+ replacedDicts: WeakSet<object>;
18
+
17
19
  activeLocale?: string;
20
+
21
+ activePath?: string;
18
22
  }
19
23
 
20
24
  const TRANSLATOR_STATE_KEY = "__AKAN_TRANSLATOR_STATE__";
@@ -25,6 +29,7 @@ const getTranslatorState = (): TranslatorState => {
25
29
  globalScope[TRANSLATOR_STATE_KEY] ??= {
26
30
  langDictionaryMap: new Map<string, Dictionary>(),
27
31
  seededDicts: new WeakSet<object>(),
32
+ replacedDicts: new WeakSet<object>(),
28
33
  };
29
34
  return globalScope[TRANSLATOR_STATE_KEY];
30
35
  };
@@ -44,6 +49,17 @@ export class Translator {
44
49
  static getActiveLocale(): string | undefined {
45
50
  return getTranslatorState().activeLocale;
46
51
  }
52
+ static setActivePath(path: string | undefined) {
53
+ const state = getTranslatorState();
54
+ if (path) state.activePath = path;
55
+ else delete state.activePath;
56
+ }
57
+ static getActivePath(): string | undefined {
58
+ return getTranslatorState().activePath;
59
+ }
60
+ static markHydrated() {
61
+ delete getTranslatorState().activePath;
62
+ }
47
63
  static translateByLocale(lang: string, key: string, param?: Record<string, string | number>): string {
48
64
  const dictionary = getTranslatorState().langDictionaryMap.get(lang);
49
65
  if (!dictionary) return key;
@@ -63,6 +79,14 @@ export class Translator {
63
79
  });
64
80
  state.langDictionaryMap.set(lang, existingDictionary);
65
81
  }
82
+ static replace(lang: string, dict: Dictionary | undefined) {
83
+ if (!dict) return;
84
+ const state = getTranslatorState();
85
+ if (state.replacedDicts.has(dict)) return;
86
+ state.seededDicts.add(dict);
87
+ state.replacedDicts.add(dict);
88
+ state.langDictionaryMap.set(lang, { ...dict });
89
+ }
66
90
  translate(lang: string, key: string, param?: Record<string, string | number>): string {
67
91
  return Translator.translateByLocale(lang, key, param);
68
92
  }
@@ -80,7 +80,6 @@ export const msg = {
80
80
  loading: (key: TransMessage<Record<string, unknown>>, option?: TransMessageOption) => void;
81
81
  };
82
82
 
83
- const rootDictionary = {} as RootDictionary;
84
83
  export const makeTrans = <
85
84
  GlobalTransMap extends Record<string, DictModule<string, string>>,
86
85
  _DictKey extends string = GlobalTransMap[keyof GlobalTransMap]["__Dict_Key__"],
@@ -103,6 +102,7 @@ export const makeTrans = <
103
102
  __Dict_Key__: _DictKey;
104
103
  __Error_Key__: _ErrorKey;
105
104
  } => {
105
+ const rootDictionary = {} as RootDictionary;
106
106
  Object.entries(transMap).forEach(([refName, trans]) => {
107
107
  trans.dict._registerToRoot(refName, rootDictionary);
108
108
  });
@@ -35,7 +35,7 @@ export type FetchProxy<
35
35
  SliceMetaObj extends Record<string, SliceMeta> = Record<never, never>,
36
36
  > = typeof global.fetch &
37
37
  FetchClient &
38
- FetchType & { slice: SliceMetaObj; instance: FetchClient; _FetchType: FetchType };
38
+ FetchType & { slice: SliceMetaObj; instance: FetchClient; _FetchType: FetchType; _SliceMetaObj: SliceMetaObj };
39
39
 
40
40
  type ClientSignalMap<SigType extends { fetch: any }> = {
41
41
  [K in keyof SigType as SigType[K] extends DatabaseSignal<any, any, any, any>
@@ -662,8 +662,8 @@ export class FetchClient {
662
662
  (signal as DatabaseSignal | ServiceSignal).serializedSignal,
663
663
  );
664
664
  } else {
665
- Object.assign(handler, (signal as FetchClient).handler);
666
- Object.entries((signal as FetchClient).serializedSignal).forEach(([refName, signal]) => {
665
+ Object.assign(handler, signal.handler);
666
+ Object.entries(signal.serializedSignal).forEach(([refName, signal]) => {
667
667
  FetchClient.#mergeSerializedSignalInto(serializedSignal, refName, signal);
668
668
  });
669
669
  }
@@ -1,28 +1,68 @@
1
- import type { DatabaseSignal, EndpointCls, ServiceSignal, SliceCls } from "akanjs/signal";
1
+ import type { DatabaseSignal, EndpointCls, SerializedSignal, ServiceSignal, SliceCls } from "akanjs/signal";
2
2
  import type { FetchProxy } from "../client";
3
3
  import type { GetSliceMetaObjFromDatabaseSignals } from "../types";
4
+ import type { SliceMeta } from "./appliedReturn.type";
4
5
  import type { GetFetchTypeFromEndpoint } from "./endpointFetch.type";
5
6
  import type { GetFetchTypeFromSlice } from "./sliceFetch.type";
6
7
 
7
- export type FetchSignalInput = FetchProxy | DatabaseSignal | ServiceSignal;
8
+ export type FetchProxySignalInput<
9
+ FetchType = unknown,
10
+ SliceMetaObj extends Record<string, SliceMeta> = Record<never, never>,
11
+ > = {
12
+ handler: Record<string, unknown>;
13
+ serializedSignal: Record<string, SerializedSignal>;
14
+ _FetchType: FetchType;
15
+ _SliceMetaObj: SliceMetaObj;
16
+ };
8
17
 
9
- export type FetchTypeOfSignal<Signal extends FetchSignalInput> =
10
- Signal extends FetchProxy<infer FetchType>
11
- ? FetchType
12
- : Signal extends { endpoint: infer EndpCls extends EndpointCls; slice: infer SlceCls extends SliceCls }
13
- ? GetFetchTypeFromEndpoint<EndpCls> & GetFetchTypeFromSlice<SlceCls>
14
- : Signal extends { endpoint: infer EndpCls extends EndpointCls }
15
- ? GetFetchTypeFromEndpoint<EndpCls>
16
- : unknown;
18
+ export type FetchSignalInput = FetchProxySignalInput | DatabaseSignal | ServiceSignal;
17
19
 
18
- type OverrideFetchType<Current, Next> = Omit<Current, keyof Next> & Next;
20
+ export type FetchTypeOfSignal<Signal extends FetchSignalInput> = Signal extends { _FetchType: infer FetchType }
21
+ ? FetchType
22
+ : Signal extends { endpoint: infer EndpCls extends EndpointCls; slice: infer SlceCls extends SliceCls }
23
+ ? GetFetchTypeFromEndpoint<EndpCls, SlceCls> & GetFetchTypeFromSlice<SlceCls>
24
+ : Signal extends { endpoint: infer EndpCls extends EndpointCls }
25
+ ? GetFetchTypeFromEndpoint<EndpCls>
26
+ : unknown;
19
27
 
20
- export type MergeAllFetchTypes<Signals extends readonly FetchSignalInput[]> = Signals extends readonly [
21
- infer First extends FetchSignalInput,
22
- ...infer Rest extends readonly FetchSignalInput[],
28
+ type FetchKeysOfProxySignal<Signal> = Signal extends { _FetchType: infer FetchType } ? keyof FetchType : never;
29
+ type FetchKeysOfProxySignals<Signals extends readonly FetchSignalInput[]> = FetchKeysOfProxySignal<Signals[number]>;
30
+
31
+ type LastProxyFetchValue<
32
+ Signals extends readonly FetchSignalInput[],
33
+ Key extends PropertyKey,
34
+ Current = never,
35
+ > = Signals extends readonly [infer First extends FetchSignalInput, ...infer Rest extends readonly FetchSignalInput[]]
36
+ ? First extends { _FetchType: infer FetchType }
37
+ ? Key extends keyof FetchType
38
+ ? LastProxyFetchValue<Rest, Key, FetchType[Key]>
39
+ : LastProxyFetchValue<Rest, Key, Current>
40
+ : LastProxyFetchValue<Rest, Key, Current>
41
+ : Current;
42
+
43
+ type MergeProxyFetchTypes<Signals extends readonly FetchSignalInput[]> = [FetchKeysOfProxySignals<Signals>] extends [
44
+ never,
23
45
  ]
24
- ? OverrideFetchType<FetchTypeOfSignal<First>, MergeAllFetchTypes<Rest>>
25
- : unknown;
46
+ ? Record<never, never>
47
+ : {
48
+ [Key in FetchKeysOfProxySignals<Signals>]: LastProxyFetchValue<Signals, Key>;
49
+ };
50
+
51
+ type MergeDirectFetchTypes<
52
+ Signals extends readonly FetchSignalInput[],
53
+ Acc = Record<never, never>,
54
+ > = Signals extends readonly [infer First extends FetchSignalInput, ...infer Rest extends readonly FetchSignalInput[]]
55
+ ? First extends { _FetchType: unknown }
56
+ ? MergeDirectFetchTypes<Rest, Acc>
57
+ : MergeDirectFetchTypes<Rest, Acc & FetchTypeOfSignal<First>>
58
+ : Acc;
59
+
60
+ export type MergeAllFetchTypes<Signals extends readonly FetchSignalInput[]> =
61
+ MergeProxyFetchTypes<Signals> extends infer ProxyFetch
62
+ ? MergeDirectFetchTypes<Signals> extends infer DirectFetch
63
+ ? Omit<ProxyFetch, keyof DirectFetch> & DirectFetch
64
+ : never
65
+ : never;
26
66
 
27
67
  export type FetchClientType<Signals extends readonly FetchSignalInput[]> = FetchProxy<
28
68
  MergeAllFetchTypes<Signals>,
@@ -7,55 +7,84 @@ import type {
7
7
  EndpInfoReqType,
8
8
  EndpointCls,
9
9
  EndpointInfo,
10
+ SliceCls,
10
11
  } from "akanjs/signal";
11
12
 
12
- type EndpInfoReturns<E> = EndpInfoClientReturns<E> | (EndpInfoNullable<E> extends true ? null : never);
13
+ type ExtendedEndpointReturn<ClientReturns, Full, Light, Insight> = ClientReturns extends (infer R)[]
14
+ ? ExtendedEndpointReturn<R, Full, Light, Insight>[]
15
+ : Full extends ClientReturns
16
+ ? Full
17
+ : Light extends ClientReturns
18
+ ? Light
19
+ : Insight extends ClientReturns
20
+ ? Insight
21
+ : ClientReturns;
13
22
 
14
- type QueryOrMutationFetchFn<E> = (
23
+ type EndpointClientReturns<E, SlceCls extends SliceCls | never> = [SlceCls] extends [never]
24
+ ? EndpInfoClientReturns<E>
25
+ : ExtendedEndpointReturn<
26
+ EndpInfoClientReturns<E>,
27
+ SlceCls["srv"]["cnst"]["_Full"],
28
+ SlceCls["srv"]["cnst"]["_Light"],
29
+ SlceCls["srv"]["cnst"]["_Insight"]
30
+ >;
31
+
32
+ type EndpInfoReturns<E, SlceCls extends SliceCls | never> =
33
+ | EndpointClientReturns<E, SlceCls>
34
+ | (EndpInfoNullable<E> extends true ? null : never);
35
+
36
+ type QueryOrMutationFetchFn<E, SlceCls extends SliceCls | never> = (
15
37
  ...args: [...EndpInfoArgs<E>, fetchPolicy?: FetchPolicy]
16
- ) => Promise<EndpInfoReturns<E>>;
38
+ ) => Promise<EndpInfoReturns<E, SlceCls>>;
17
39
 
18
- type MessageEmitFn<E> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E>;
40
+ type MessageEmitFn<E, SlceCls extends SliceCls | never> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E, SlceCls>;
19
41
 
20
- type MessageListenFn<E> = (
21
- handleEvent: (data: EndpInfoReturns<E>) => PromiseOrObject<void>,
42
+ type MessageListenFn<E, SlceCls extends SliceCls | never> = (
43
+ handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
22
44
  options?: FetchPolicy,
23
45
  ) => () => void;
24
46
 
25
- type PubsubSubscribeFn<E> = (
26
- ...args: [...EndpInfoArgs<E>, handleEvent: (data: EndpInfoReturns<E>) => PromiseOrObject<void>, options?: FetchPolicy]
47
+ type PubsubSubscribeFn<E, SlceCls extends SliceCls | never> = (
48
+ ...args: [
49
+ ...EndpInfoArgs<E>,
50
+ handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
51
+ options?: FetchPolicy,
52
+ ]
27
53
  ) => () => void;
28
54
 
29
- type PrimaryFetchFn<E> =
55
+ type PrimaryFetchFn<E, SlceCls extends SliceCls | never> =
30
56
  EndpInfoReqType<E> extends "query" | "mutation"
31
- ? QueryOrMutationFetchFn<E>
57
+ ? QueryOrMutationFetchFn<E, SlceCls>
32
58
  : EndpInfoReqType<E> extends "message"
33
- ? MessageEmitFn<E>
59
+ ? MessageEmitFn<E, SlceCls>
34
60
  : never;
35
61
 
36
- type PrimaryFetchType<EInfoObj extends { [key: string]: EndpointInfo }> = {
62
+ type PrimaryFetchType<EInfoObj extends { [key: string]: EndpointInfo }, SlceCls extends SliceCls | never> = {
37
63
  [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "query" | "mutation" | "message"
38
64
  ? K
39
- : never]: PrimaryFetchFn<EInfoObj[K]>;
65
+ : never]: PrimaryFetchFn<EInfoObj[K], SlceCls>;
40
66
  };
41
67
 
42
- type PubsubFetchType<EInfoObj extends { [key: string]: EndpointInfo }> = {
68
+ type PubsubFetchType<EInfoObj extends { [key: string]: EndpointInfo }, SlceCls extends SliceCls | never> = {
43
69
  [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "pubsub"
44
70
  ? K extends string
45
71
  ? `subscribe${Capitalize<K>}`
46
72
  : never
47
- : never]: PubsubSubscribeFn<EInfoObj[K]>;
73
+ : never]: PubsubSubscribeFn<EInfoObj[K], SlceCls>;
48
74
  };
49
75
 
50
- type MessageListenFetchType<EInfoObj extends { [key: string]: EndpointInfo }> = {
76
+ type MessageListenFetchType<EInfoObj extends { [key: string]: EndpointInfo }, SlceCls extends SliceCls | never> = {
51
77
  [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "message"
52
78
  ? K extends string
53
79
  ? `listen${Capitalize<K>}`
54
80
  : never
55
- : never]: MessageListenFn<EInfoObj[K]>;
81
+ : never]: MessageListenFn<EInfoObj[K], SlceCls>;
56
82
  };
57
83
 
58
84
  export type GetFetchTypeFromEndpoint<
59
85
  EndpCls extends EndpointCls,
86
+ SlceCls extends SliceCls | never = never,
60
87
  _EndpointInfoObj extends { [key: string]: EndpointInfo } = EndpCls[typeof ENDPOINT_META],
61
- > = PrimaryFetchType<_EndpointInfoObj> & PubsubFetchType<_EndpointInfoObj> & MessageListenFetchType<_EndpointInfoObj>;
88
+ > = PrimaryFetchType<_EndpointInfoObj, SlceCls> &
89
+ PubsubFetchType<_EndpointInfoObj, SlceCls> &
90
+ MessageListenFetchType<_EndpointInfoObj, SlceCls>;
package/fetch/types.ts CHANGED
@@ -12,12 +12,18 @@ export const getDefaultAccount = (): Account => {
12
12
  return { appName: env.appName, environment: env.environment };
13
13
  };
14
14
 
15
- type GetSliceNameFromSignal<Signal> = Signal extends DatabaseSignal
16
- ? `${Signal["slice"]["baseName"]}${Capitalize<keyof Signal["slice"][typeof SLICE_META] & string>}`
17
- : Signal extends { slice: { [K in infer Key]: SliceMeta } }
18
- ? Key
19
- : never;
20
- type GetSliceNamesFromSignals<Signals extends readonly unknown[]> = GetSliceNameFromSignal<Signals[number]>;
21
- export type GetSliceMetaObjFromDatabaseSignals<Signals extends readonly unknown[]> = {
22
- [K in GetSliceNamesFromSignals<Signals>]: SliceMeta;
23
- };
15
+ type GetSliceMetaObjFromSignal<Signal> = Signal extends {
16
+ _SliceMetaObj: infer SliceMetaObj extends Record<string, SliceMeta>;
17
+ }
18
+ ? SliceMetaObj
19
+ : Signal extends DatabaseSignal
20
+ ? {
21
+ [K in `${Signal["slice"]["baseName"]}${Capitalize<keyof Signal["slice"][typeof SLICE_META] & string>}`]: SliceMeta;
22
+ }
23
+ : Record<never, never>;
24
+ export type GetSliceMetaObjFromDatabaseSignals<
25
+ Signals extends readonly unknown[],
26
+ Acc extends Record<string, SliceMeta> = Record<never, never>,
27
+ > = Signals extends readonly [infer First, ...infer Rest]
28
+ ? GetSliceMetaObjFromDatabaseSignals<Rest, Acc & GetSliceMetaObjFromSignal<First>>
29
+ : Acc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.3.2-rc.6",
3
+ "version": "2.3.2-rc.8",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -9,6 +9,8 @@ export const HMR_CLIENT_SCRIPT = `(function(){
9
9
  var socket = null;
10
10
  var lastBuildId = null;
11
11
  var refreshRuntimePromise = null;
12
+ var refreshRuntime = null;
13
+ var pendingRefreshRegistrations = [];
12
14
  var refreshQueue = Promise.resolve();
13
15
  var overlayEl = null;
14
16
  var overlayLabelEl = null;
@@ -19,8 +21,14 @@ export const HMR_CLIENT_SCRIPT = `(function(){
19
21
  var overlayJobs = {};
20
22
  self.__AKAN_HMR_PHASE__ = null;
21
23
 
22
- self.$RefreshReg$ = self.$RefreshReg$ || function(){};
24
+ self.$RefreshReg$ = self.$RefreshReg$ || function(type, id){
25
+ if (refreshRuntime) refreshRuntime.register(type, id);
26
+ else pendingRefreshRegistrations.push([type, id]);
27
+ };
23
28
  self.$RefreshSig$ = self.$RefreshSig$ || function(){ return function(type){ return type; }; };
29
+ ensureRefreshRuntime().catch(function(err){
30
+ console.warn("[akan-hmr] React Refresh runtime preload failed", err);
31
+ });
24
32
 
25
33
  function connect(){
26
34
  try { socket = new WebSocket(url); }
@@ -194,9 +202,14 @@ export const HMR_CLIENT_SCRIPT = `(function(){
194
202
  refreshRuntimePromise = import("react-refresh/runtime").then(function(mod){
195
203
  var runtime = mod.default || mod;
196
204
  if (!self.__AKAN_REACT_REFRESH_READY__) {
205
+ refreshRuntime = runtime;
197
206
  runtime.injectIntoGlobalHook(self);
198
207
  self.$RefreshReg$ = function(type, id){ runtime.register(type, id); };
199
208
  self.$RefreshSig$ = runtime.createSignatureFunctionForTransform;
209
+ for (var i = 0; i < pendingRefreshRegistrations.length; i++) {
210
+ self.$RefreshReg$(pendingRefreshRegistrations[i][0], pendingRefreshRegistrations[i][1]);
211
+ }
212
+ pendingRefreshRegistrations = [];
200
213
  self.__AKAN_REACT_REFRESH_READY__ = true;
201
214
  self.__AKAN_REACT_REFRESH_RUNTIME__ = runtime;
202
215
  }
@@ -12,6 +12,38 @@ import type { RenderState } from "../types";
12
12
  import type { ChangeKind } from "./changeBatch";
13
13
  import { type HmrWsData, HmrWsHub } from "./wsHub";
14
14
 
15
+ const APP_RUNTIME_METADATA_BASENAMES = new Set(["dict.ts", "sig.ts", "useClient.ts"]);
16
+
17
+ export function isAkanRuntimeMetadataFile(file: string): boolean {
18
+ const resolved = path.resolve(file);
19
+ const parts = resolved.split(/[\\/]+/).filter(Boolean);
20
+ const base = parts.at(-1);
21
+ if (!base) return false;
22
+
23
+ const parent = parts.at(-2);
24
+ if (parent === "lib" && APP_RUNTIME_METADATA_BASENAMES.has(base)) return true;
25
+
26
+ const libIndex = parts.lastIndexOf("lib");
27
+ if (libIndex < 0 || parts.length <= libIndex + 1) return false;
28
+ return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
29
+ }
30
+
31
+ export function manifestClientEntriesForFiles(
32
+ files: string[],
33
+ clientManifest: ClientManifest,
34
+ workspaceRoot = process.cwd(),
35
+ ): Set<string> {
36
+ const normalizedFiles = new Set(files.map((file) => path.resolve(file)));
37
+ const entries = new Set<string>();
38
+ for (const key of Object.keys(clientManifest)) {
39
+ const hashIdx = key.lastIndexOf("#");
40
+ const entryKey = hashIdx >= 0 ? key.slice(0, hashIdx) : key;
41
+ const resolved = path.isAbsolute(entryKey) ? path.resolve(entryKey) : path.resolve(workspaceRoot, entryKey);
42
+ if (normalizedFiles.has(resolved)) entries.add(resolved);
43
+ }
44
+ return entries;
45
+ }
46
+
15
47
  export interface DevHmrControllerOptions {
16
48
  renderState: RenderState;
17
49
  rsc: RscWorker;
@@ -125,6 +157,10 @@ export class DevHmrController {
125
157
  return [...urls];
126
158
  }
127
159
 
160
+ static shouldFullReloadForRuntimeMetadata(files: string[]): boolean {
161
+ return files.some(isAkanRuntimeMetadataFile);
162
+ }
163
+
128
164
  #createBuilderRpc() {
129
165
  return new BuilderRpc({
130
166
  onInvalidate: (ev) => {
@@ -148,13 +184,17 @@ export class DevHmrController {
148
184
  },
149
185
  onPagesUpdated: async ({ bundlePath, buildId, generation, changedFiles }) => {
150
186
  const started = Date.now();
151
- const staleClientEntries = this.#staleClientEntriesForFiles(changedFiles ?? []);
152
- const routeIds = this.#routeIdsForFiles(changedFiles ?? [], staleClientEntries);
153
- const fastRefreshCandidate = this.#isFastRefreshCandidate(changedFiles ?? []);
187
+ const files = changedFiles ?? [];
188
+ const runtimeMetadataChanged = DevHmrController.shouldFullReloadForRuntimeMetadata(files);
189
+ const staleClientEntries = runtimeMetadataChanged ? new Set<string>() : this.#staleClientEntriesForFiles(files);
190
+ const routeIds = runtimeMetadataChanged ? undefined : this.#routeIdsForFiles(files, staleClientEntries);
191
+ const fastRefreshCandidate = !runtimeMetadataChanged && this.#isFastRefreshCandidate(files);
154
192
  this.#logger.verbose(
155
- `[SSR] pages-updated bundlePath=${bundlePath} buildId=${buildId} generation=${generation ?? "(unknown)"} files=${changedFiles?.length ?? 0} routes=${routeIds?.length ?? 0} fastRefresh=${fastRefreshCandidate} staleEntries=${staleClientEntries.size}`,
193
+ `[SSR] pages-updated bundlePath=${bundlePath} buildId=${buildId} generation=${generation ?? "(unknown)"} files=${files.length} routes=${routeIds?.length ?? 0} fastRefresh=${fastRefreshCandidate} staleEntries=${staleClientEntries.size} runtimeMetadata=${runtimeMetadataChanged}`,
156
194
  );
157
- const dropped = this.#invalidateRoutes(changedFiles ?? [], routeIds, staleClientEntries);
195
+ const dropped = this.#invalidateRoutes(files, routeIds, staleClientEntries, {
196
+ forceClear: runtimeMetadataChanged,
197
+ });
158
198
  this.#renderState.buildId = buildId;
159
199
  const manifest = this.routeCache.snapshot();
160
200
  const reloadStarted = Date.now();
@@ -165,7 +205,7 @@ export class DevHmrController {
165
205
  pagesBundlePath: bundlePath,
166
206
  });
167
207
  this.#logger.verbose(`[SSR] rsc reload buildId=${buildId} in ${Date.now() - reloadStarted}ms`);
168
- const shouldReload = this.#shouldFullReloadForFiles(changedFiles ?? [], routeIds);
208
+ const shouldReload = runtimeMetadataChanged || this.#shouldFullReloadForFiles(files, routeIds);
169
209
  if (shouldReload) this.#hub.broadcast({ type: "reload", buildId });
170
210
  else if (fastRefreshCandidate)
171
211
  this.#hub.broadcast({ type: "client-refresh", buildId, generation, changedFiles, routeIds });
@@ -233,9 +273,15 @@ export class DevHmrController {
233
273
  this.#dirtyFiles.clear();
234
274
  }
235
275
 
236
- #invalidateRoutes(files: string[], routeIds: string[] | undefined, staleClientEntries = new Set<string>()): string[] {
276
+ #invalidateRoutes(
277
+ files: string[],
278
+ routeIds: string[] | undefined,
279
+ staleClientEntries = new Set<string>(),
280
+ { forceClear = false }: { forceClear?: boolean } = {},
281
+ ): string[] {
237
282
  this.#dirty.clear();
238
283
  this.#dirtyFiles.clear();
284
+ if (forceClear) return this.routeCache.clear();
239
285
  if (staleClientEntries.size > 0) {
240
286
  const staleKeys = this.#clientEntryManifestKeys(staleClientEntries);
241
287
  return this.routeCache.invalidateClientEntries({
@@ -268,6 +314,7 @@ export class DevHmrController {
268
314
 
269
315
  #isFastRefreshCandidate(files: string[]): boolean {
270
316
  if (!this.#fastRefreshEnabled || files.length === 0) return false;
317
+ if (manifestClientEntriesForFiles(files, this.routeCache.merged.clientManifest).size > 0) return true;
271
318
  return files.some((file) => {
272
319
  const resolved = path.resolve(file);
273
320
  return this.#recentClientEntries.has(resolved) || this.#recentClientFiles.has(resolved);
@@ -328,7 +375,7 @@ export class DevHmrController {
328
375
  }
329
376
 
330
377
  #staleClientEntriesForFiles(files: string[]): Set<string> {
331
- const stale = new Set<string>();
378
+ const stale = manifestClientEntriesForFiles(files, this.routeCache.merged.clientManifest);
332
379
  for (const file of files) {
333
380
  const resolved = path.resolve(file);
334
381
  if (this.#recentClientEntries.has(resolved)) stale.add(resolved);
package/service/types.ts CHANGED
@@ -70,12 +70,12 @@ export type DatabaseService<
70
70
  __exists(query?: _QueryOfDoc): Promise<string | null>;
71
71
  __count(query?: _QueryOfDoc): Promise<number>;
72
72
  __insight(query?: _QueryOfDoc): Promise<Insight>;
73
- _preCreate?(data: _DataInputOfDoc): PromiseOrObject<_DataInputOfDoc> | never;
74
- _postCreate?(doc: Doc): Promise<Doc> | Doc | never;
75
- _preUpdate?(id: string, data: Partial<Doc>): PromiseOrObject<Partial<Doc>> | never;
76
- _postUpdate?(doc: Doc): Promise<Doc> | Doc | never;
77
- _preRemove?(id: string): Promise<void> | void | never;
78
- _postRemove?(doc: Doc): Promise<Doc> | Doc | never;
73
+ _preCreate?(data: _DataInputOfDoc): PromiseOrObject<_DataInputOfDoc>;
74
+ _postCreate?(doc: Doc): Promise<Doc> | Doc;
75
+ _preUpdate?(id: string, data: Partial<Doc>): PromiseOrObject<Partial<Doc>>;
76
+ _postUpdate?(doc: Doc): Promise<Doc> | Doc;
77
+ _preRemove?(id: string): Promise<void> | void;
78
+ _postRemove?(doc: Doc): Promise<Doc> | Doc;
79
79
  __libsPreCreate: (data: _DataInputOfDoc) => Promise<_DataInputOfDoc>;
80
80
  __libsPostCreate: (doc: Doc) => Promise<Doc>;
81
81
  __libsPreUpdate: (id: string, data: Partial<Doc>) => Promise<Partial<Doc>>;
@@ -15,16 +15,6 @@ export interface EndpointCls<
15
15
  [ENDPOINT_META]: EndpointInfoObj;
16
16
  }
17
17
 
18
- type ExtendedEndpointReturn<ClientReturns, Full, Light, Insight> = ClientReturns extends (infer R)[]
19
- ? ExtendedEndpointReturn<R, Full, Light, Insight>[]
20
- : Full extends ClientReturns
21
- ? Full
22
- : Light extends ClientReturns
23
- ? Light
24
- : Insight extends ClientReturns
25
- ? Insight
26
- : ClientReturns;
27
-
28
18
  type EndpointMetaOf<EndpCls> = EndpCls extends EndpointCls<any, infer EndpointInfoObj> ? EndpointInfoObj : never;
29
19
 
30
20
  type MergeEndpointMetas<EndpClses extends readonly EndpointCls[], Acc = unknown> = EndpClses extends readonly [
@@ -34,41 +24,6 @@ type MergeEndpointMetas<EndpClses extends readonly EndpointCls[], Acc = unknown>
34
24
  ? MergeEndpointMetas<Rest, Assign<Acc, EndpointMetaOf<First>>>
35
25
  : Acc;
36
26
 
37
- type ExtendEndpoints<
38
- SrvModule extends ServiceModel,
39
- LibEndpoints extends readonly EndpointCls[],
40
- _Full = NonNullable<SrvModule["cnst"]>["_Full"],
41
- _Light = NonNullable<SrvModule["cnst"]>["_Light"],
42
- _Insight = NonNullable<SrvModule["cnst"]>["_Insight"],
43
- _Merged = MergeEndpointMetas<LibEndpoints>,
44
- > = {
45
- [K in keyof _Merged]: _Merged[K] extends EndpointInfo<
46
- infer ReqType,
47
- infer Srvs,
48
- infer ArgNames,
49
- infer Args,
50
- infer InternalArgs,
51
- infer ServerArgs,
52
- infer Returns,
53
- infer ClientReturns,
54
- infer ServerReturns,
55
- infer Nullable
56
- >
57
- ? EndpointInfo<
58
- ReqType,
59
- Srvs,
60
- ArgNames,
61
- Args,
62
- InternalArgs,
63
- ServerArgs,
64
- Returns,
65
- ExtendedEndpointReturn<ClientReturns, _Full, _Light, _Insight>,
66
- ServerReturns,
67
- Nullable
68
- >
69
- : never;
70
- };
71
-
72
27
  /** Builds a typed endpoint adaptor from a service module and endpoint builder. */
73
28
  export function endpoint<
74
29
  SrvModule extends ServiceModel,
@@ -80,9 +35,7 @@ export function endpoint<
80
35
  ...libEndpoints: LibEndpoints
81
36
  ): EndpointCls<
82
37
  SrvModule,
83
- LibEndpoints extends readonly []
84
- ? ReturnType<Builder>
85
- : Assign<ReturnType<Builder>, ExtendEndpoints<SrvModule, LibEndpoints>>
38
+ LibEndpoints extends readonly [] ? ReturnType<Builder> : Assign<ReturnType<Builder>, MergeEndpointMetas<LibEndpoints>>
86
39
  > {
87
40
  const srvKeys = [
88
41
  ...new Set([
@@ -11,8 +11,12 @@ export declare class Translator {
11
11
  hasDictionary(lang: string): boolean;
12
12
  static setActiveLocale(lang: string | undefined): void;
13
13
  static getActiveLocale(): string | undefined;
14
+ static setActivePath(path: string | undefined): void;
15
+ static getActivePath(): string | undefined;
16
+ static markHydrated(): void;
14
17
  static translateByLocale(lang: string, key: string, param?: Record<string, string | number>): string;
15
18
  static seed(lang: string, dict: Dictionary | undefined): void;
19
+ static replace(lang: string, dict: Dictionary | undefined): void;
16
20
  translate(lang: string, key: string, param?: Record<string, string | number>): string;
17
21
  getDictionary(lang: string): Promise<Dictionary>;
18
22
  }
@@ -9,6 +9,7 @@ export type FetchProxy<FetchType = unknown, SliceMetaObj extends Record<string,
9
9
  slice: SliceMetaObj;
10
10
  instance: FetchClient;
11
11
  _FetchType: FetchType;
12
+ _SliceMetaObj: SliceMetaObj;
12
13
  };
13
14
  type ClientSignalMap<SigType extends {
14
15
  fetch: any;
@@ -1,19 +1,39 @@
1
- import type { DatabaseSignal, EndpointCls, ServiceSignal, SliceCls } from "akanjs/signal";
1
+ import type { DatabaseSignal, EndpointCls, SerializedSignal, ServiceSignal, SliceCls } from "akanjs/signal";
2
2
  import type { FetchProxy } from "../client.d.ts";
3
3
  import type { GetSliceMetaObjFromDatabaseSignals } from "../types.d.ts";
4
+ import type { SliceMeta } from "./appliedReturn.type";
4
5
  import type { GetFetchTypeFromEndpoint } from "./endpointFetch.type";
5
6
  import type { GetFetchTypeFromSlice } from "./sliceFetch.type";
6
- export type FetchSignalInput = FetchProxy | DatabaseSignal | ServiceSignal;
7
- export type FetchTypeOfSignal<Signal extends FetchSignalInput> = Signal extends FetchProxy<infer FetchType> ? FetchType : Signal extends {
7
+ export type FetchProxySignalInput<FetchType = unknown, SliceMetaObj extends Record<string, SliceMeta> = Record<never, never>> = {
8
+ handler: Record<string, unknown>;
9
+ serializedSignal: Record<string, SerializedSignal>;
10
+ _FetchType: FetchType;
11
+ _SliceMetaObj: SliceMetaObj;
12
+ };
13
+ export type FetchSignalInput = FetchProxySignalInput | DatabaseSignal | ServiceSignal;
14
+ export type FetchTypeOfSignal<Signal extends FetchSignalInput> = Signal extends {
15
+ _FetchType: infer FetchType;
16
+ } ? FetchType : Signal extends {
8
17
  endpoint: infer EndpCls extends EndpointCls;
9
18
  slice: infer SlceCls extends SliceCls;
10
- } ? GetFetchTypeFromEndpoint<EndpCls> & GetFetchTypeFromSlice<SlceCls> : Signal extends {
19
+ } ? GetFetchTypeFromEndpoint<EndpCls, SlceCls> & GetFetchTypeFromSlice<SlceCls> : Signal extends {
11
20
  endpoint: infer EndpCls extends EndpointCls;
12
21
  } ? GetFetchTypeFromEndpoint<EndpCls> : unknown;
13
- type OverrideFetchType<Current, Next> = Omit<Current, keyof Next> & Next;
14
- export type MergeAllFetchTypes<Signals extends readonly FetchSignalInput[]> = Signals extends readonly [
15
- infer First extends FetchSignalInput,
16
- ...infer Rest extends readonly FetchSignalInput[]
17
- ] ? OverrideFetchType<FetchTypeOfSignal<First>, MergeAllFetchTypes<Rest>> : unknown;
22
+ type FetchKeysOfProxySignal<Signal> = Signal extends {
23
+ _FetchType: infer FetchType;
24
+ } ? keyof FetchType : never;
25
+ type FetchKeysOfProxySignals<Signals extends readonly FetchSignalInput[]> = FetchKeysOfProxySignal<Signals[number]>;
26
+ type LastProxyFetchValue<Signals extends readonly FetchSignalInput[], Key extends PropertyKey, Current = never> = Signals extends readonly [infer First extends FetchSignalInput, ...infer Rest extends readonly FetchSignalInput[]] ? First extends {
27
+ _FetchType: infer FetchType;
28
+ } ? Key extends keyof FetchType ? LastProxyFetchValue<Rest, Key, FetchType[Key]> : LastProxyFetchValue<Rest, Key, Current> : LastProxyFetchValue<Rest, Key, Current> : Current;
29
+ type MergeProxyFetchTypes<Signals extends readonly FetchSignalInput[]> = [FetchKeysOfProxySignals<Signals>] extends [
30
+ never
31
+ ] ? Record<never, never> : {
32
+ [Key in FetchKeysOfProxySignals<Signals>]: LastProxyFetchValue<Signals, Key>;
33
+ };
34
+ type MergeDirectFetchTypes<Signals extends readonly FetchSignalInput[], Acc = Record<never, never>> = Signals extends readonly [infer First extends FetchSignalInput, ...infer Rest extends readonly FetchSignalInput[]] ? First extends {
35
+ _FetchType: unknown;
36
+ } ? MergeDirectFetchTypes<Rest, Acc> : MergeDirectFetchTypes<Rest, Acc & FetchTypeOfSignal<First>> : Acc;
37
+ export type MergeAllFetchTypes<Signals extends readonly FetchSignalInput[]> = MergeProxyFetchTypes<Signals> extends infer ProxyFetch ? MergeDirectFetchTypes<Signals> extends infer DirectFetch ? Omit<ProxyFetch, keyof DirectFetch> & DirectFetch : never : never;
18
38
  export type FetchClientType<Signals extends readonly FetchSignalInput[]> = FetchProxy<MergeAllFetchTypes<Signals>, GetSliceMetaObjFromDatabaseSignals<Signals>>;
19
39
  export {};
@@ -1,28 +1,34 @@
1
1
  import type { ENDPOINT_META, PromiseOrObject } from "akanjs/base";
2
2
  import type { FetchPolicy } from "akanjs/common";
3
- import type { EndpInfoArgs, EndpInfoClientReturns, EndpInfoNullable, EndpInfoReqType, EndpointCls, EndpointInfo } from "akanjs/signal";
4
- type EndpInfoReturns<E> = EndpInfoClientReturns<E> | (EndpInfoNullable<E> extends true ? null : never);
5
- type QueryOrMutationFetchFn<E> = (...args: [...EndpInfoArgs<E>, fetchPolicy?: FetchPolicy]) => Promise<EndpInfoReturns<E>>;
6
- type MessageEmitFn<E> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E>;
7
- type MessageListenFn<E> = (handleEvent: (data: EndpInfoReturns<E>) => PromiseOrObject<void>, options?: FetchPolicy) => () => void;
8
- type PubsubSubscribeFn<E> = (...args: [...EndpInfoArgs<E>, handleEvent: (data: EndpInfoReturns<E>) => PromiseOrObject<void>, options?: FetchPolicy]) => () => void;
9
- type PrimaryFetchFn<E> = EndpInfoReqType<E> extends "query" | "mutation" ? QueryOrMutationFetchFn<E> : EndpInfoReqType<E> extends "message" ? MessageEmitFn<E> : never;
3
+ import type { EndpInfoArgs, EndpInfoClientReturns, EndpInfoNullable, EndpInfoReqType, EndpointCls, EndpointInfo, SliceCls } from "akanjs/signal";
4
+ type ExtendedEndpointReturn<ClientReturns, Full, Light, Insight> = ClientReturns extends (infer R)[] ? ExtendedEndpointReturn<R, Full, Light, Insight>[] : Full extends ClientReturns ? Full : Light extends ClientReturns ? Light : Insight extends ClientReturns ? Insight : ClientReturns;
5
+ type EndpointClientReturns<E, SlceCls extends SliceCls | never> = [SlceCls] extends [never] ? EndpInfoClientReturns<E> : ExtendedEndpointReturn<EndpInfoClientReturns<E>, SlceCls["srv"]["cnst"]["_Full"], SlceCls["srv"]["cnst"]["_Light"], SlceCls["srv"]["cnst"]["_Insight"]>;
6
+ type EndpInfoReturns<E, SlceCls extends SliceCls | never> = EndpointClientReturns<E, SlceCls> | (EndpInfoNullable<E> extends true ? null : never);
7
+ type QueryOrMutationFetchFn<E, SlceCls extends SliceCls | never> = (...args: [...EndpInfoArgs<E>, fetchPolicy?: FetchPolicy]) => Promise<EndpInfoReturns<E, SlceCls>>;
8
+ type MessageEmitFn<E, SlceCls extends SliceCls | never> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E, SlceCls>;
9
+ type MessageListenFn<E, SlceCls extends SliceCls | never> = (handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>, options?: FetchPolicy) => () => void;
10
+ type PubsubSubscribeFn<E, SlceCls extends SliceCls | never> = (...args: [
11
+ ...EndpInfoArgs<E>,
12
+ handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
13
+ options?: FetchPolicy
14
+ ]) => () => void;
15
+ type PrimaryFetchFn<E, SlceCls extends SliceCls | never> = EndpInfoReqType<E> extends "query" | "mutation" ? QueryOrMutationFetchFn<E, SlceCls> : EndpInfoReqType<E> extends "message" ? MessageEmitFn<E, SlceCls> : never;
10
16
  type PrimaryFetchType<EInfoObj extends {
11
17
  [key: string]: EndpointInfo;
12
- }> = {
13
- [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "query" | "mutation" | "message" ? K : never]: PrimaryFetchFn<EInfoObj[K]>;
18
+ }, SlceCls extends SliceCls | never> = {
19
+ [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "query" | "mutation" | "message" ? K : never]: PrimaryFetchFn<EInfoObj[K], SlceCls>;
14
20
  };
15
21
  type PubsubFetchType<EInfoObj extends {
16
22
  [key: string]: EndpointInfo;
17
- }> = {
18
- [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "pubsub" ? K extends string ? `subscribe${Capitalize<K>}` : never : never]: PubsubSubscribeFn<EInfoObj[K]>;
23
+ }, SlceCls extends SliceCls | never> = {
24
+ [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "pubsub" ? K extends string ? `subscribe${Capitalize<K>}` : never : never]: PubsubSubscribeFn<EInfoObj[K], SlceCls>;
19
25
  };
20
26
  type MessageListenFetchType<EInfoObj extends {
21
27
  [key: string]: EndpointInfo;
22
- }> = {
23
- [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "message" ? K extends string ? `listen${Capitalize<K>}` : never : never]: MessageListenFn<EInfoObj[K]>;
28
+ }, SlceCls extends SliceCls | never> = {
29
+ [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "message" ? K extends string ? `listen${Capitalize<K>}` : never : never]: MessageListenFn<EInfoObj[K], SlceCls>;
24
30
  };
25
- export type GetFetchTypeFromEndpoint<EndpCls extends EndpointCls, _EndpointInfoObj extends {
31
+ export type GetFetchTypeFromEndpoint<EndpCls extends EndpointCls, SlceCls extends SliceCls | never = never, _EndpointInfoObj extends {
26
32
  [key: string]: EndpointInfo;
27
- } = EndpCls[typeof ENDPOINT_META]> = PrimaryFetchType<_EndpointInfoObj> & PubsubFetchType<_EndpointInfoObj> & MessageListenFetchType<_EndpointInfoObj>;
33
+ } = EndpCls[typeof ENDPOINT_META]> = PrimaryFetchType<_EndpointInfoObj, SlceCls> & PubsubFetchType<_EndpointInfoObj, SlceCls> & MessageListenFetchType<_EndpointInfoObj, SlceCls>;
28
34
  export {};
@@ -7,13 +7,10 @@ export type Account<AddData = unknown> = {
7
7
  environment: Environment;
8
8
  } & AddData;
9
9
  export declare const getDefaultAccount: () => Account;
10
- type GetSliceNameFromSignal<Signal> = Signal extends DatabaseSignal ? `${Signal["slice"]["baseName"]}${Capitalize<keyof Signal["slice"][typeof SLICE_META] & string>}` : Signal extends {
11
- slice: {
12
- [K in infer Key]: SliceMeta;
13
- };
14
- } ? Key : never;
15
- type GetSliceNamesFromSignals<Signals extends readonly unknown[]> = GetSliceNameFromSignal<Signals[number]>;
16
- export type GetSliceMetaObjFromDatabaseSignals<Signals extends readonly unknown[]> = {
17
- [K in GetSliceNamesFromSignals<Signals>]: SliceMeta;
18
- };
10
+ type GetSliceMetaObjFromSignal<Signal> = Signal extends {
11
+ _SliceMetaObj: infer SliceMetaObj extends Record<string, SliceMeta>;
12
+ } ? SliceMetaObj : Signal extends DatabaseSignal ? {
13
+ [K in `${Signal["slice"]["baseName"]}${Capitalize<keyof Signal["slice"][typeof SLICE_META] & string>}`]: SliceMeta;
14
+ } : Record<never, never>;
15
+ export type GetSliceMetaObjFromDatabaseSignals<Signals extends readonly unknown[], Acc extends Record<string, SliceMeta> = Record<never, never>> = Signals extends readonly [infer First, ...infer Rest] ? GetSliceMetaObjFromDatabaseSignals<Rest, Acc & GetSliceMetaObjFromSignal<First>> : Acc;
19
16
  export {};
@@ -1 +1 @@
1
- export declare const HMR_CLIENT_SCRIPT = "(function(){\n if (self.__AKAN_HMR_INSTALLED__) return;\n self.__AKAN_HMR_INSTALLED__ = true;\n var proto = location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n var url = proto + \"//\" + location.host + \"/_akan/hmr\";\n var attempts = 0;\n var socket = null;\n var lastBuildId = null;\n var refreshRuntimePromise = null;\n var refreshQueue = Promise.resolve();\n var overlayEl = null;\n var overlayLabelEl = null;\n var overlayStyleEl = null;\n var overlayTimer = null;\n var overlayHideTimer = null;\n var overlayNextToken = 1;\n var overlayJobs = {};\n self.__AKAN_HMR_PHASE__ = null;\n\n // Bun's React Fast Refresh transform can emit top-level calls to these globals\n // even when we fall back to full reload instead of applying React Refresh.\n self.$RefreshReg$ = self.$RefreshReg$ || function(){};\n self.$RefreshSig$ = self.$RefreshSig$ || function(){ return function(type){ return type; }; };\n\n function connect(){\n try { socket = new WebSocket(url); }\n catch(e){ console.error(\"[akan-hmr] ws init failed\", e); schedule(); return; }\n socket.addEventListener(\"open\", function(){ attempts = 0; });\n socket.addEventListener(\"message\", function(ev){\n var msg;\n try { msg = JSON.parse(ev.data); } catch (e){ return; }\n if (!msg || typeof msg.type !== \"string\") return;\n if (msg.type === \"hello\") {\n if (lastBuildId !== null && msg.buildId !== lastBuildId) {\n location.reload();\n return;\n }\n lastBuildId = msg.buildId;\n return;\n }\n if (msg.type === \"reload\") {\n beginHmrOverlay(\"Reloading...\", true);\n try { self.__AKAN_RSC_CLEAR_CACHE__ && self.__AKAN_RSC_CLEAR_CACHE__(); } catch(e){}\n setTimeout(function(){ location.reload(); }, 30);\n return;\n }\n if (msg.type === \"rsc-refresh\") {\n refreshRsc(msg);\n return;\n }\n if (msg.type === \"client-refresh\") {\n refreshClient(msg);\n return;\n }\n if (msg.type === \"css-update\") {\n var cssUrl = selectCssUrl(msg.cssAssets);\n if (cssUrl) swapCss(cssUrl);\n else {\n beginHmrOverlay(\"Reloading...\", true);\n location.reload();\n }\n return;\n }\n if (msg.type === \"error\") { console.error(\"[akan-hmr]\", msg.message); return; }\n });\n socket.addEventListener(\"close\", function(){ socket = null; schedule(); });\n socket.addEventListener(\"error\", function(){ try { socket && socket.close(); } catch(e){} });\n }\n\n function schedule(){\n attempts = Math.min(attempts + 1, 6);\n var delay = Math.min(30000, 250 * Math.pow(2, attempts - 1));\n setTimeout(connect, delay);\n }\n\n function ensureOverlay(){\n if (overlayEl && overlayLabelEl) return overlayEl;\n if (!overlayStyleEl) {\n overlayStyleEl = document.createElement(\"style\");\n overlayStyleEl.textContent =\n \"@keyframes akan-hmr-spin{to{transform:rotate(360deg)}}\" +\n \".__akan_hmr_overlay{position:fixed;left:16px;bottom:16px;z-index:2147483647;display:flex;align-items:center;gap:9px;padding:10px 12px;border-radius:999px;background:rgba(17,24,39,.94);color:#fff;font:500 13px/1.2 ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;box-shadow:0 10px 28px rgba(0,0,0,.28);pointer-events:none;opacity:0;transform:translateY(6px);transition:opacity .15s ease,transform .15s ease;backdrop-filter:blur(8px)}\" +\n \".__akan_hmr_overlay[data-show=true]{opacity:1;transform:translateY(0)}\" +\n \".__akan_hmr_spinner{width:14px;height:14px;border:2px solid rgba(255,255,255,.32);border-top-color:#fff;border-radius:999px;animation:akan-hmr-spin .75s linear infinite;flex:none}\" +\n \"@media (prefers-reduced-motion:reduce){.__akan_hmr_overlay{transition:none}.__akan_hmr_spinner{animation:none}}\";\n document.head.appendChild(overlayStyleEl);\n }\n overlayEl = document.createElement(\"div\");\n overlayEl.className = \"__akan_hmr_overlay\";\n overlayEl.setAttribute(\"role\", \"status\");\n overlayEl.setAttribute(\"aria-live\", \"polite\");\n overlayEl.innerHTML = '<span class=\"__akan_hmr_spinner\" aria-hidden=\"true\"></span><span data-akan-hmr-label>Updating...</span>';\n overlayLabelEl = overlayEl.querySelector(\"[data-akan-hmr-label]\");\n (document.body || document.documentElement).appendChild(overlayEl);\n return overlayEl;\n }\n\n function activeOverlayTokens(){\n return Object.keys(overlayJobs);\n }\n\n function latestOverlayLabel(){\n var keys = activeOverlayTokens();\n if (keys.length === 0) return \"Updating...\";\n return overlayJobs[keys[keys.length - 1]] || \"Updating...\";\n }\n\n function showOverlayNow(){\n overlayTimer = null;\n if (activeOverlayTokens().length === 0) return;\n var el = ensureOverlay();\n if (overlayHideTimer) {\n clearTimeout(overlayHideTimer);\n overlayHideTimer = null;\n }\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n requestAnimationFrame(function(){ el.setAttribute(\"data-show\", \"true\"); });\n }\n\n function beginHmrOverlay(label, immediate){\n var token = overlayNextToken++;\n overlayJobs[token] = label || \"Updating...\";\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n if (overlayHideTimer) {\n clearTimeout(overlayHideTimer);\n overlayHideTimer = null;\n }\n if (immediate) {\n if (overlayTimer) clearTimeout(overlayTimer);\n showOverlayNow();\n } else if (!overlayTimer && (!overlayEl || overlayEl.getAttribute(\"data-show\") !== \"true\")) {\n overlayTimer = setTimeout(showOverlayNow, 120);\n }\n return token;\n }\n\n function setHmrOverlayLabel(token, label){\n if (!overlayJobs[token]) return;\n overlayJobs[token] = label || \"Updating...\";\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n }\n\n function endHmrOverlay(token){\n delete overlayJobs[token];\n if (activeOverlayTokens().length > 0) {\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n return;\n }\n if (overlayTimer) {\n clearTimeout(overlayTimer);\n overlayTimer = null;\n }\n if (!overlayEl) return;\n overlayEl.setAttribute(\"data-show\", \"false\");\n if (overlayHideTimer) clearTimeout(overlayHideTimer);\n overlayHideTimer = setTimeout(function(){\n if (overlayEl && activeOverlayTokens().length === 0 && overlayEl.parentNode) {\n overlayEl.parentNode.removeChild(overlayEl);\n overlayEl = null;\n overlayLabelEl = null;\n }\n }, 180);\n }\n\n function refreshRsc(msg){\n var started = performance.now();\n var overlayToken = beginHmrOverlay(\"Refreshing page...\");\n try { self.__AKAN_RSC_CLEAR_CACHE__ && self.__AKAN_RSC_CLEAR_CACHE__(); } catch(e){}\n if (!self.__AKAN_RSC_REFRESH__) {\n console.warn(\"[akan-hmr] RSC refresh API unavailable, falling back to full reload\");\n setHmrOverlayLabel(overlayToken, \"Reloading...\");\n setTimeout(function(){ location.reload(); }, 30);\n return;\n }\n Promise.resolve(self.__AKAN_RSC_REFRESH__({ buildId: msg.buildId })).then(function(){\n lastBuildId = msg.buildId;\n endHmrOverlay(overlayToken);\n console.debug && console.debug(\"[akan-hmr] RSC refreshed\", {\n buildId: msg.buildId,\n generation: msg.generation,\n routeIds: msg.routeIds,\n changedFiles: msg.changedFiles && msg.changedFiles.length,\n durationMs: Math.round(performance.now() - started)\n });\n }, function(err){\n console.error(\"[akan-hmr] RSC refresh failed, falling back to full reload\", err);\n setHmrOverlayLabel(overlayToken, \"Update failed, reloading...\");\n setTimeout(function(){ location.reload(); }, 250);\n });\n }\n\n function ensureRefreshRuntime(){\n if (refreshRuntimePromise) return refreshRuntimePromise;\n refreshRuntimePromise = import(\"react-refresh/runtime\").then(function(mod){\n var runtime = mod.default || mod;\n if (!self.__AKAN_REACT_REFRESH_READY__) {\n runtime.injectIntoGlobalHook(self);\n self.$RefreshReg$ = function(type, id){ runtime.register(type, id); };\n self.$RefreshSig$ = runtime.createSignatureFunctionForTransform;\n self.__AKAN_REACT_REFRESH_READY__ = true;\n self.__AKAN_REACT_REFRESH_RUNTIME__ = runtime;\n }\n return runtime;\n });\n return refreshRuntimePromise;\n }\n\n function refreshClient(msg){\n refreshQueue = refreshQueue.then(function(){ return doRefreshClient(msg); }, function(){ return doRefreshClient(msg); });\n }\n\n function setHmrPhase(phase){\n self.__AKAN_HMR_PHASE__ = phase;\n }\n\n function doRefreshClient(msg){\n var started = performance.now();\n var metadataAt = started;\n var importAt = started;\n var refreshAt = started;\n var overlayToken = beginHmrOverlay(\"Updating...\");\n var fallbackToRsc = false;\n return ensureRefreshRuntime().then(function(runtime){\n setHmrOverlayLabel(overlayToken, \"Fetching update...\");\n var endpoint = new URL(\"/_akan/hmr/client-refresh\", location.origin);\n endpoint.searchParams.set(\"url\", location.href);\n if (msg.buildId != null) endpoint.searchParams.set(\"buildId\", String(msg.buildId));\n return fetch(endpoint, { credentials: \"same-origin\", cache: \"no-store\" })\n .then(function(res){\n if (!res.ok) throw new Error(\"client-refresh metadata failed \" + res.status + \" \" + res.statusText);\n return res.json();\n })\n .then(function(info){\n metadataAt = performance.now();\n var chunks = Array.isArray(info.chunks) ? info.chunks : [];\n if (chunks.length === 0) throw new Error(\"no client chunks returned\");\n setHmrPhase(\"refresh-import\");\n setHmrOverlayLabel(overlayToken, \"Importing update...\");\n return Promise.all(chunks.map(function(chunk){ return import(chunk); })).then(function(){\n importAt = performance.now();\n setHmrPhase(\"react-refresh\");\n setHmrOverlayLabel(overlayToken, \"Applying update...\");\n try {\n runtime.performReactRefresh();\n } finally {\n setHmrPhase(null);\n }\n refreshAt = performance.now();\n lastBuildId = msg.buildId;\n console.debug && console.debug(\"[akan-hmr] React Fast Refresh applied\", {\n buildId: msg.buildId,\n generation: msg.generation,\n chunks: chunks.length,\n routeIds: info.routeIds || msg.routeIds,\n changedFiles: msg.changedFiles && msg.changedFiles.length,\n metadataMs: Math.round(metadataAt - started),\n importMs: Math.round(importAt - metadataAt),\n refreshMs: Math.round(refreshAt - importAt),\n durationMs: Math.round(refreshAt - started)\n });\n endHmrOverlay(overlayToken);\n }, function(err){\n setHmrPhase(null);\n throw err;\n });\n });\n }).catch(function(err){\n console.warn(\"[akan-hmr] React Fast Refresh failed, falling back to RSC refresh\", err);\n fallbackToRsc = true;\n endHmrOverlay(overlayToken);\n refreshRsc(msg);\n }).finally(function(){\n if (!fallbackToRsc) endHmrOverlay(overlayToken);\n });\n }\n\n function swapCss(href){\n var overlayToken = beginHmrOverlay(\"Updating styles...\");\n var link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = href;\n link.setAttribute(\"data-akan-css\", \"pending\");\n link.addEventListener(\"load\", function(){\n var prev = document.querySelectorAll(\"link[data-akan-css=active]\");\n for (var i = 0; i < prev.length; i++) prev[i].parentNode && prev[i].parentNode.removeChild(prev[i]);\n link.setAttribute(\"data-akan-css\", \"active\");\n endHmrOverlay(overlayToken);\n });\n link.addEventListener(\"error\", function(){\n if (link.parentNode) link.parentNode.removeChild(link);\n endHmrOverlay(overlayToken);\n });\n document.head.appendChild(link);\n }\n\n function selectCssUrl(cssAssets){\n if (!cssAssets || typeof cssAssets !== \"object\") return null;\n var parts = location.pathname.split(\"/\").filter(Boolean);\n for (var i = 0; i < parts.length; i++) {\n var asset = cssAssets[parts[i]];\n if (asset && asset.cssUrl) return asset.cssUrl;\n }\n return cssAssets[\"\"] && cssAssets[\"\"].cssUrl ? cssAssets[\"\"].cssUrl : null;\n }\n\n connect();\n})();";
1
+ export declare const HMR_CLIENT_SCRIPT = "(function(){\n if (self.__AKAN_HMR_INSTALLED__) return;\n self.__AKAN_HMR_INSTALLED__ = true;\n var proto = location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n var url = proto + \"//\" + location.host + \"/_akan/hmr\";\n var attempts = 0;\n var socket = null;\n var lastBuildId = null;\n var refreshRuntimePromise = null;\n var refreshRuntime = null;\n var pendingRefreshRegistrations = [];\n var refreshQueue = Promise.resolve();\n var overlayEl = null;\n var overlayLabelEl = null;\n var overlayStyleEl = null;\n var overlayTimer = null;\n var overlayHideTimer = null;\n var overlayNextToken = 1;\n var overlayJobs = {};\n self.__AKAN_HMR_PHASE__ = null;\n\n // Bun's React Fast Refresh transform can emit top-level calls to these globals\n // even when we fall back to full reload instead of applying React Refresh.\n self.$RefreshReg$ = self.$RefreshReg$ || function(type, id){\n if (refreshRuntime) refreshRuntime.register(type, id);\n else pendingRefreshRegistrations.push([type, id]);\n };\n self.$RefreshSig$ = self.$RefreshSig$ || function(){ return function(type){ return type; }; };\n // Start installing React Refresh before the application module graph loads.\n // Injecting the runtime only on the first update is too late for React's renderer hook.\n ensureRefreshRuntime().catch(function(err){\n console.warn(\"[akan-hmr] React Refresh runtime preload failed\", err);\n });\n\n function connect(){\n try { socket = new WebSocket(url); }\n catch(e){ console.error(\"[akan-hmr] ws init failed\", e); schedule(); return; }\n socket.addEventListener(\"open\", function(){ attempts = 0; });\n socket.addEventListener(\"message\", function(ev){\n var msg;\n try { msg = JSON.parse(ev.data); } catch (e){ return; }\n if (!msg || typeof msg.type !== \"string\") return;\n if (msg.type === \"hello\") {\n if (lastBuildId !== null && msg.buildId !== lastBuildId) {\n location.reload();\n return;\n }\n lastBuildId = msg.buildId;\n return;\n }\n if (msg.type === \"reload\") {\n beginHmrOverlay(\"Reloading...\", true);\n try { self.__AKAN_RSC_CLEAR_CACHE__ && self.__AKAN_RSC_CLEAR_CACHE__(); } catch(e){}\n setTimeout(function(){ location.reload(); }, 30);\n return;\n }\n if (msg.type === \"rsc-refresh\") {\n refreshRsc(msg);\n return;\n }\n if (msg.type === \"client-refresh\") {\n refreshClient(msg);\n return;\n }\n if (msg.type === \"css-update\") {\n var cssUrl = selectCssUrl(msg.cssAssets);\n if (cssUrl) swapCss(cssUrl);\n else {\n beginHmrOverlay(\"Reloading...\", true);\n location.reload();\n }\n return;\n }\n if (msg.type === \"error\") { console.error(\"[akan-hmr]\", msg.message); return; }\n });\n socket.addEventListener(\"close\", function(){ socket = null; schedule(); });\n socket.addEventListener(\"error\", function(){ try { socket && socket.close(); } catch(e){} });\n }\n\n function schedule(){\n attempts = Math.min(attempts + 1, 6);\n var delay = Math.min(30000, 250 * Math.pow(2, attempts - 1));\n setTimeout(connect, delay);\n }\n\n function ensureOverlay(){\n if (overlayEl && overlayLabelEl) return overlayEl;\n if (!overlayStyleEl) {\n overlayStyleEl = document.createElement(\"style\");\n overlayStyleEl.textContent =\n \"@keyframes akan-hmr-spin{to{transform:rotate(360deg)}}\" +\n \".__akan_hmr_overlay{position:fixed;left:16px;bottom:16px;z-index:2147483647;display:flex;align-items:center;gap:9px;padding:10px 12px;border-radius:999px;background:rgba(17,24,39,.94);color:#fff;font:500 13px/1.2 ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;box-shadow:0 10px 28px rgba(0,0,0,.28);pointer-events:none;opacity:0;transform:translateY(6px);transition:opacity .15s ease,transform .15s ease;backdrop-filter:blur(8px)}\" +\n \".__akan_hmr_overlay[data-show=true]{opacity:1;transform:translateY(0)}\" +\n \".__akan_hmr_spinner{width:14px;height:14px;border:2px solid rgba(255,255,255,.32);border-top-color:#fff;border-radius:999px;animation:akan-hmr-spin .75s linear infinite;flex:none}\" +\n \"@media (prefers-reduced-motion:reduce){.__akan_hmr_overlay{transition:none}.__akan_hmr_spinner{animation:none}}\";\n document.head.appendChild(overlayStyleEl);\n }\n overlayEl = document.createElement(\"div\");\n overlayEl.className = \"__akan_hmr_overlay\";\n overlayEl.setAttribute(\"role\", \"status\");\n overlayEl.setAttribute(\"aria-live\", \"polite\");\n overlayEl.innerHTML = '<span class=\"__akan_hmr_spinner\" aria-hidden=\"true\"></span><span data-akan-hmr-label>Updating...</span>';\n overlayLabelEl = overlayEl.querySelector(\"[data-akan-hmr-label]\");\n (document.body || document.documentElement).appendChild(overlayEl);\n return overlayEl;\n }\n\n function activeOverlayTokens(){\n return Object.keys(overlayJobs);\n }\n\n function latestOverlayLabel(){\n var keys = activeOverlayTokens();\n if (keys.length === 0) return \"Updating...\";\n return overlayJobs[keys[keys.length - 1]] || \"Updating...\";\n }\n\n function showOverlayNow(){\n overlayTimer = null;\n if (activeOverlayTokens().length === 0) return;\n var el = ensureOverlay();\n if (overlayHideTimer) {\n clearTimeout(overlayHideTimer);\n overlayHideTimer = null;\n }\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n requestAnimationFrame(function(){ el.setAttribute(\"data-show\", \"true\"); });\n }\n\n function beginHmrOverlay(label, immediate){\n var token = overlayNextToken++;\n overlayJobs[token] = label || \"Updating...\";\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n if (overlayHideTimer) {\n clearTimeout(overlayHideTimer);\n overlayHideTimer = null;\n }\n if (immediate) {\n if (overlayTimer) clearTimeout(overlayTimer);\n showOverlayNow();\n } else if (!overlayTimer && (!overlayEl || overlayEl.getAttribute(\"data-show\") !== \"true\")) {\n overlayTimer = setTimeout(showOverlayNow, 120);\n }\n return token;\n }\n\n function setHmrOverlayLabel(token, label){\n if (!overlayJobs[token]) return;\n overlayJobs[token] = label || \"Updating...\";\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n }\n\n function endHmrOverlay(token){\n delete overlayJobs[token];\n if (activeOverlayTokens().length > 0) {\n if (overlayLabelEl) overlayLabelEl.textContent = latestOverlayLabel();\n return;\n }\n if (overlayTimer) {\n clearTimeout(overlayTimer);\n overlayTimer = null;\n }\n if (!overlayEl) return;\n overlayEl.setAttribute(\"data-show\", \"false\");\n if (overlayHideTimer) clearTimeout(overlayHideTimer);\n overlayHideTimer = setTimeout(function(){\n if (overlayEl && activeOverlayTokens().length === 0 && overlayEl.parentNode) {\n overlayEl.parentNode.removeChild(overlayEl);\n overlayEl = null;\n overlayLabelEl = null;\n }\n }, 180);\n }\n\n function refreshRsc(msg){\n var started = performance.now();\n var overlayToken = beginHmrOverlay(\"Refreshing page...\");\n try { self.__AKAN_RSC_CLEAR_CACHE__ && self.__AKAN_RSC_CLEAR_CACHE__(); } catch(e){}\n if (!self.__AKAN_RSC_REFRESH__) {\n console.warn(\"[akan-hmr] RSC refresh API unavailable, falling back to full reload\");\n setHmrOverlayLabel(overlayToken, \"Reloading...\");\n setTimeout(function(){ location.reload(); }, 30);\n return;\n }\n Promise.resolve(self.__AKAN_RSC_REFRESH__({ buildId: msg.buildId })).then(function(){\n lastBuildId = msg.buildId;\n endHmrOverlay(overlayToken);\n console.debug && console.debug(\"[akan-hmr] RSC refreshed\", {\n buildId: msg.buildId,\n generation: msg.generation,\n routeIds: msg.routeIds,\n changedFiles: msg.changedFiles && msg.changedFiles.length,\n durationMs: Math.round(performance.now() - started)\n });\n }, function(err){\n console.error(\"[akan-hmr] RSC refresh failed, falling back to full reload\", err);\n setHmrOverlayLabel(overlayToken, \"Update failed, reloading...\");\n setTimeout(function(){ location.reload(); }, 250);\n });\n }\n\n function ensureRefreshRuntime(){\n if (refreshRuntimePromise) return refreshRuntimePromise;\n refreshRuntimePromise = import(\"react-refresh/runtime\").then(function(mod){\n var runtime = mod.default || mod;\n if (!self.__AKAN_REACT_REFRESH_READY__) {\n refreshRuntime = runtime;\n runtime.injectIntoGlobalHook(self);\n self.$RefreshReg$ = function(type, id){ runtime.register(type, id); };\n self.$RefreshSig$ = runtime.createSignatureFunctionForTransform;\n for (var i = 0; i < pendingRefreshRegistrations.length; i++) {\n self.$RefreshReg$(pendingRefreshRegistrations[i][0], pendingRefreshRegistrations[i][1]);\n }\n pendingRefreshRegistrations = [];\n self.__AKAN_REACT_REFRESH_READY__ = true;\n self.__AKAN_REACT_REFRESH_RUNTIME__ = runtime;\n }\n return runtime;\n });\n return refreshRuntimePromise;\n }\n\n function refreshClient(msg){\n refreshQueue = refreshQueue.then(function(){ return doRefreshClient(msg); }, function(){ return doRefreshClient(msg); });\n }\n\n function setHmrPhase(phase){\n self.__AKAN_HMR_PHASE__ = phase;\n }\n\n function doRefreshClient(msg){\n var started = performance.now();\n var metadataAt = started;\n var importAt = started;\n var refreshAt = started;\n var overlayToken = beginHmrOverlay(\"Updating...\");\n var fallbackToRsc = false;\n return ensureRefreshRuntime().then(function(runtime){\n setHmrOverlayLabel(overlayToken, \"Fetching update...\");\n var endpoint = new URL(\"/_akan/hmr/client-refresh\", location.origin);\n endpoint.searchParams.set(\"url\", location.href);\n if (msg.buildId != null) endpoint.searchParams.set(\"buildId\", String(msg.buildId));\n return fetch(endpoint, { credentials: \"same-origin\", cache: \"no-store\" })\n .then(function(res){\n if (!res.ok) throw new Error(\"client-refresh metadata failed \" + res.status + \" \" + res.statusText);\n return res.json();\n })\n .then(function(info){\n metadataAt = performance.now();\n var chunks = Array.isArray(info.chunks) ? info.chunks : [];\n if (chunks.length === 0) throw new Error(\"no client chunks returned\");\n setHmrPhase(\"refresh-import\");\n setHmrOverlayLabel(overlayToken, \"Importing update...\");\n return Promise.all(chunks.map(function(chunk){ return import(chunk); })).then(function(){\n importAt = performance.now();\n setHmrPhase(\"react-refresh\");\n setHmrOverlayLabel(overlayToken, \"Applying update...\");\n try {\n runtime.performReactRefresh();\n } finally {\n setHmrPhase(null);\n }\n refreshAt = performance.now();\n lastBuildId = msg.buildId;\n console.debug && console.debug(\"[akan-hmr] React Fast Refresh applied\", {\n buildId: msg.buildId,\n generation: msg.generation,\n chunks: chunks.length,\n routeIds: info.routeIds || msg.routeIds,\n changedFiles: msg.changedFiles && msg.changedFiles.length,\n metadataMs: Math.round(metadataAt - started),\n importMs: Math.round(importAt - metadataAt),\n refreshMs: Math.round(refreshAt - importAt),\n durationMs: Math.round(refreshAt - started)\n });\n endHmrOverlay(overlayToken);\n }, function(err){\n setHmrPhase(null);\n throw err;\n });\n });\n }).catch(function(err){\n console.warn(\"[akan-hmr] React Fast Refresh failed, falling back to RSC refresh\", err);\n fallbackToRsc = true;\n endHmrOverlay(overlayToken);\n refreshRsc(msg);\n }).finally(function(){\n if (!fallbackToRsc) endHmrOverlay(overlayToken);\n });\n }\n\n function swapCss(href){\n var overlayToken = beginHmrOverlay(\"Updating styles...\");\n var link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = href;\n link.setAttribute(\"data-akan-css\", \"pending\");\n link.addEventListener(\"load\", function(){\n var prev = document.querySelectorAll(\"link[data-akan-css=active]\");\n for (var i = 0; i < prev.length; i++) prev[i].parentNode && prev[i].parentNode.removeChild(prev[i]);\n link.setAttribute(\"data-akan-css\", \"active\");\n endHmrOverlay(overlayToken);\n });\n link.addEventListener(\"error\", function(){\n if (link.parentNode) link.parentNode.removeChild(link);\n endHmrOverlay(overlayToken);\n });\n document.head.appendChild(link);\n }\n\n function selectCssUrl(cssAssets){\n if (!cssAssets || typeof cssAssets !== \"object\") return null;\n var parts = location.pathname.split(\"/\").filter(Boolean);\n for (var i = 0; i < parts.length; i++) {\n var asset = cssAssets[parts[i]];\n if (asset && asset.cssUrl) return asset.cssUrl;\n }\n return cssAssets[\"\"] && cssAssets[\"\"].cssUrl ? cssAssets[\"\"].cssUrl : null;\n }\n\n connect();\n})();";
@@ -2,6 +2,8 @@ import { BuilderRpc, type ClientManifest, RouteClientCache, type RouteSeedIndex
2
2
  import type { RscWorker } from "../rscWorkerHost.d.ts";
3
3
  import type { RenderState } from "../types.d.ts";
4
4
  import { type HmrWsData, HmrWsHub } from "./wsHub.d.ts";
5
+ export declare function isAkanRuntimeMetadataFile(file: string): boolean;
6
+ export declare function manifestClientEntriesForFiles(files: string[], clientManifest: ClientManifest, workspaceRoot?: string): Set<string>;
5
7
  export interface DevHmrControllerOptions {
6
8
  renderState: RenderState;
7
9
  rsc: RscWorker;
@@ -21,4 +23,5 @@ export declare class DevHmrController {
21
23
  ensureRoute(url: URL): Promise<import("..").MergedManifest>;
22
24
  routeIdsForPath(pathname: string): string[] | undefined;
23
25
  static clientChunkUrls(clientManifest: ClientManifest): string[];
26
+ static shouldFullReloadForRuntimeMetadata(files: string[]): boolean;
24
27
  }
@@ -21,12 +21,12 @@ export type DatabaseService<T extends string = string, Input = any, Doc = any, O
21
21
  __exists(query?: _QueryOfDoc): Promise<string | null>;
22
22
  __count(query?: _QueryOfDoc): Promise<number>;
23
23
  __insight(query?: _QueryOfDoc): Promise<Insight>;
24
- _preCreate?(data: _DataInputOfDoc): PromiseOrObject<_DataInputOfDoc> | never;
25
- _postCreate?(doc: Doc): Promise<Doc> | Doc | never;
26
- _preUpdate?(id: string, data: Partial<Doc>): PromiseOrObject<Partial<Doc>> | never;
27
- _postUpdate?(doc: Doc): Promise<Doc> | Doc | never;
28
- _preRemove?(id: string): Promise<void> | void | never;
29
- _postRemove?(doc: Doc): Promise<Doc> | Doc | never;
24
+ _preCreate?(data: _DataInputOfDoc): PromiseOrObject<_DataInputOfDoc>;
25
+ _postCreate?(doc: Doc): Promise<Doc> | Doc;
26
+ _preUpdate?(id: string, data: Partial<Doc>): PromiseOrObject<Partial<Doc>>;
27
+ _postUpdate?(doc: Doc): Promise<Doc> | Doc;
28
+ _preRemove?(id: string): Promise<void> | void;
29
+ _postRemove?(doc: Doc): Promise<Doc> | Doc;
30
30
  __libsPreCreate: (data: _DataInputOfDoc) => Promise<_DataInputOfDoc>;
31
31
  __libsPostCreate: (doc: Doc) => Promise<Doc>;
32
32
  __libsPreUpdate: (id: string, data: Partial<Doc>) => Promise<Partial<Doc>>;
@@ -13,16 +13,12 @@ export interface EndpointCls<SrvModule extends ServiceModel = ServiceModel, Endp
13
13
  srv: SrvModule;
14
14
  [ENDPOINT_META]: EndpointInfoObj;
15
15
  }
16
- type ExtendedEndpointReturn<ClientReturns, Full, Light, Insight> = ClientReturns extends (infer R)[] ? ExtendedEndpointReturn<R, Full, Light, Insight>[] : Full extends ClientReturns ? Full : Light extends ClientReturns ? Light : Insight extends ClientReturns ? Insight : ClientReturns;
17
16
  type EndpointMetaOf<EndpCls> = EndpCls extends EndpointCls<any, infer EndpointInfoObj> ? EndpointInfoObj : never;
18
17
  type MergeEndpointMetas<EndpClses extends readonly EndpointCls[], Acc = unknown> = EndpClses extends readonly [
19
18
  infer First extends EndpointCls,
20
19
  ...infer Rest extends readonly EndpointCls[]
21
20
  ] ? MergeEndpointMetas<Rest, Assign<Acc, EndpointMetaOf<First>>> : Acc;
22
- type ExtendEndpoints<SrvModule extends ServiceModel, LibEndpoints extends readonly EndpointCls[], _Full = NonNullable<SrvModule["cnst"]>["_Full"], _Light = NonNullable<SrvModule["cnst"]>["_Light"], _Insight = NonNullable<SrvModule["cnst"]>["_Insight"], _Merged = MergeEndpointMetas<LibEndpoints>> = {
23
- [K in keyof _Merged]: _Merged[K] extends EndpointInfo<infer ReqType, infer Srvs, infer ArgNames, infer Args, infer InternalArgs, infer ServerArgs, infer Returns, infer ClientReturns, infer ServerReturns, infer Nullable> ? EndpointInfo<ReqType, Srvs, ArgNames, Args, InternalArgs, ServerArgs, Returns, ExtendedEndpointReturn<ClientReturns, _Full, _Light, _Insight>, ServerReturns, Nullable> : never;
24
- };
25
21
  /** Builds a typed endpoint adaptor from a service module and endpoint builder. */
26
- export declare function endpoint<SrvModule extends ServiceModel, Builder extends EndpointBuilder<SrvModule>, LibEndpoints extends readonly EndpointCls[]>(srv: SrvModule, builder: Builder, ...libEndpoints: LibEndpoints): EndpointCls<SrvModule, LibEndpoints extends readonly [] ? ReturnType<Builder> : Assign<ReturnType<Builder>, ExtendEndpoints<SrvModule, LibEndpoints>>>;
22
+ export declare function endpoint<SrvModule extends ServiceModel, Builder extends EndpointBuilder<SrvModule>, LibEndpoints extends readonly EndpointCls[]>(srv: SrvModule, builder: Builder, ...libEndpoints: LibEndpoints): EndpointCls<SrvModule, LibEndpoints extends readonly [] ? ReturnType<Builder> : Assign<ReturnType<Builder>, MergeEndpointMetas<LibEndpoints>>>;
27
23
  export declare function sliceEndpoint<SrvModule extends ServiceModel, Builder extends EndpointBuilder<SrvModule>>(srv: SrvModule, builder: Builder): EndpointCls<SrvModule, ReturnType<Builder>>;
28
24
  export {};
@@ -5,7 +5,7 @@ import type { SerializedSignal } from "akanjs/signal";
5
5
  import { type HTMLAttributes, type ReactNode, type RefObject } from "react";
6
6
  export declare const Client: {
7
7
  (): import("react/jsx-runtime").JSX.Element;
8
- Wrapper: ({ children, theme, lang, dictionary, signals, reconnect, }: ClientWrapperProps) => import("react/jsx-runtime").JSX.Element;
8
+ Wrapper: ({ children, theme, lang, path, dictionary, signals, reconnect, }: ClientWrapperProps) => import("react/jsx-runtime").JSX.Element;
9
9
  Bridge: ({ env, lang, theme, prefix, gaTrackingId, wsConnect }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
10
10
  Inner: () => import("react/jsx-runtime").JSX.Element;
11
11
  SsrBridge: ({ lang, prefix }: ClientSsrBridgeProps) => null;
@@ -14,22 +14,30 @@ interface ClientWrapperProps {
14
14
  children: ReactNode;
15
15
  theme?: AkanTheme;
16
16
  lang?: string;
17
+ path?: string;
17
18
  dictionary?: Record<string, Record<string, unknown>>;
18
19
  signals?: SerializedSignal[];
19
20
  reconnect?: boolean;
20
21
  }
21
- export declare const ClientWrapper: ({ children, theme, lang, dictionary, signals, reconnect, }: ClientWrapperProps) => import("react/jsx-runtime").JSX.Element;
22
+ export declare const ClientWrapper: ({ children, theme, lang, path, dictionary, signals, reconnect, }: ClientWrapperProps) => import("react/jsx-runtime").JSX.Element;
22
23
  interface ClientPathWrapperProps extends Omit<HTMLAttributes<HTMLDivElement>, "style"> {
23
24
  bind?: () => HTMLAttributes<HTMLDivElement>;
24
25
  wrapperRef?: RefObject<HTMLDivElement | null> | null;
25
26
  pageType?: "current" | "prev" | "cached";
26
27
  location?: Location;
28
+ initialHref?: string;
29
+ initialPath?: string;
30
+ initialPathname?: string;
31
+ initialParams?: Record<string, string>;
32
+ initialSearch?: string;
33
+ initialSearchParams?: Record<string, string | string[]>;
34
+ initialHash?: string;
27
35
  style?: TransitionStyle;
28
36
  prefix?: string;
29
37
  children?: ReactNode;
30
38
  layoutStyle?: "web" | "mobile";
31
39
  }
32
- export declare const ClientPathWrapper: ({ className, bind, wrapperRef, pageType, location, prefix, children, layoutStyle, ...props }: ClientPathWrapperProps) => import("react/jsx-runtime").JSX.Element;
40
+ export declare const ClientPathWrapper: ({ className, bind, wrapperRef, pageType, location, initialHref, initialPath, initialPathname, initialParams, initialSearch, initialSearchParams, initialHash, prefix, children, layoutStyle, ...props }: ClientPathWrapperProps) => import("react/jsx-runtime").JSX.Element;
33
41
  interface ClientBridgeProps {
34
42
  env: ClientEnv;
35
43
  lang?: string;
@@ -4,7 +4,7 @@ import { type ProviderProps } from "./Common.d.ts";
4
4
  export declare const SSR: {
5
5
  (): import("react/jsx-runtime").JSX.Element;
6
6
  Provider: ({ className, appName, params, head, manifest, env, gaTrackingId, children, theme, prefix, fonts, layoutStyle, reconnect, wsConnect, dictionary, allDictionary, of, }: SSRProviderProps) => import("react/jsx-runtime").JSX.Element;
7
- Wrapper: ({ children, head, manifest, fonts, className, prefix, layoutStyle, }: SSRWrapperProps) => import("react/jsx-runtime").JSX.Element;
7
+ Wrapper: ({ children, head, manifest, fonts, className, lang, path, prefix, layoutStyle, }: SSRWrapperProps) => import("react/jsx-runtime").JSX.Element;
8
8
  };
9
9
  export type SSRProviderProps = ProviderProps & {
10
10
  fonts?: ReactFont[];
@@ -14,6 +14,7 @@ interface SSRWrapperProps {
14
14
  className?: string;
15
15
  appName: string;
16
16
  lang: "en" | "ko" | (string & {});
17
+ path: string;
17
18
  head?: ReactNode;
18
19
  manifest?: WebAppManifest;
19
20
  fonts?: ReactFont[];
@@ -3,7 +3,6 @@
3
3
  import { getEnv } from "akanjs/base";
4
4
  import { clsx, getPathInfo, router, usePage, usePathCtx } from "akanjs/client";
5
5
  import { Logger } from "akanjs/common";
6
- import { st } from "akanjs/store";
7
6
  import type { SsrLinkProps } from "./types";
8
7
 
9
8
  export default function SsrLink({
@@ -21,11 +20,7 @@ export default function SsrLink({
21
20
  const pathCtx = usePathCtx();
22
21
  const prefix = pathCtx.prefix;
23
22
  const { lang, path: pagePath } = usePage();
24
- const storePathname = st.use.pathname();
25
- const currentPath =
26
- getEnv().side === "server"
27
- ? getPathInfo(pagePath, lang, prefix ?? "").path
28
- : getPathInfo(storePathname, lang, prefix ?? "").path;
23
+ const currentPath = pathCtx.location?.pathRoute?.path ?? getPathInfo(pagePath, lang, prefix ?? "").path;
29
24
  const isExternal = href.startsWith("http") || href.startsWith("mailto:") || href.startsWith("tel:");
30
25
  const internalPathInfo = getPathInfo(href, lang, prefix ?? "");
31
26
  const publicPathInfo = getPathInfo(href, lang, "");
@@ -43,6 +43,7 @@ interface ClientWrapperProps {
43
43
  children: ReactNode;
44
44
  theme?: AkanTheme;
45
45
  lang?: string;
46
+ path?: string;
46
47
  dictionary?: Record<string, Record<string, unknown>>;
47
48
  signals?: SerializedSignal[];
48
49
  reconnect?: boolean;
@@ -51,6 +52,7 @@ export const ClientWrapper = ({
51
52
  children,
52
53
  theme,
53
54
  lang = "en",
55
+ path,
54
56
  dictionary,
55
57
  signals = [],
56
58
  reconnect = true,
@@ -59,8 +61,12 @@ export const ClientWrapper = ({
59
61
  if (dictionary) {
60
62
  Translator.seed(lang, dictionary);
61
63
 
62
- if (typeof window !== "undefined") Translator.setActiveLocale(lang);
64
+ Translator.setActiveLocale(lang);
63
65
  }
66
+ Translator.setActivePath(path);
67
+ useEffect(() => {
68
+ Translator.markHydrated();
69
+ }, [path]);
64
70
  useLayoutEffect(() => {
65
71
  Logger.rawLog(logo);
66
72
  }, []);
@@ -79,6 +85,13 @@ interface ClientPathWrapperProps extends Omit<HTMLAttributes<HTMLDivElement>, "s
79
85
  wrapperRef?: RefObject<HTMLDivElement | null> | null;
80
86
  pageType?: "current" | "prev" | "cached";
81
87
  location?: Location;
88
+ initialHref?: string;
89
+ initialPath?: string;
90
+ initialPathname?: string;
91
+ initialParams?: Record<string, string>;
92
+ initialSearch?: string;
93
+ initialSearchParams?: Record<string, string | string[]>;
94
+ initialHash?: string;
82
95
  style?: TransitionStyle;
83
96
  prefix?: string;
84
97
  children?: ReactNode;
@@ -90,22 +103,29 @@ export const ClientPathWrapper = ({
90
103
  wrapperRef,
91
104
  pageType = "current",
92
105
  location,
106
+ initialHref,
107
+ initialPath,
108
+ initialPathname,
109
+ initialParams,
110
+ initialSearch,
111
+ initialSearchParams,
112
+ initialHash,
93
113
  prefix = "",
94
114
  children,
95
115
  layoutStyle = "web",
96
116
  ...props
97
117
  }: ClientPathWrapperProps) => {
98
- const href = location?.href ?? (typeof window !== "undefined" ? window.location.href : "");
99
- const hash = location?.hash ?? (typeof window !== "undefined" ? window.location.hash : "");
100
- const pathname = location?.pathname ?? "/";
101
- const params = location?.params ?? {};
102
- const searchParams = location?.searchParams ?? {};
103
- const search = location?.search ?? (typeof window !== "undefined" ? window.location.search : "");
118
+ const href = location?.href ?? initialHref ?? (typeof window !== "undefined" ? window.location.href : "");
119
+ const hash = location?.hash ?? initialHash ?? (typeof window !== "undefined" ? window.location.hash : "");
120
+ const pathname = location?.pathname ?? initialPathname ?? "/";
121
+ const params = location?.params ?? initialParams ?? {};
122
+ const searchParams = location?.searchParams ?? initialSearchParams ?? {};
123
+ const search = location?.search ?? initialSearch ?? (typeof window !== "undefined" ? window.location.search : "");
104
124
  const lang = params.lang;
105
125
  const firstPath = pathname.split("/")[2];
106
126
  const pathRoute: PathRoute = location?.pathRoute ?? {
107
- path: `/${pathname.split("/").slice(2).join("/")}`,
108
- pathSegments: pathname.split("/").slice(2),
127
+ path: initialPath ?? `/${pathname.split("/").slice(2).join("/")}`,
128
+ pathSegments: (initialPath ?? `/${pathname.split("/").slice(2).join("/")}`).split("/").filter(Boolean),
109
129
  renderPage: { render: () => <></> },
110
130
  pageState: defaultPageState,
111
131
  renderRootLayouts: [],
package/ui/System/SSR.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getEnv } from "akanjs/base";
2
- import { clsx, type ReactFont, router, Translator, usePage, type WebAppManifest } from "akanjs/client";
2
+ import { clsx, getPathInfo, type ReactFont, router, Translator, usePage, type WebAppManifest } from "akanjs/client";
3
3
  import { setRequestTheme } from "akanjs/fetch";
4
4
  import { Children, Fragment, type ReactNode, Suspense } from "react";
5
5
  import { FontCss } from "../fontCss";
@@ -35,8 +35,9 @@ const SSRProvider = ({
35
35
  of,
36
36
  }: SSRProviderProps) => {
37
37
  setRequestTheme(theme);
38
+ Translator.markHydrated();
38
39
 
39
- const { lang: activeLocale } = usePage();
40
+ const { lang: activeLocale, path: activePath } = usePage();
40
41
 
41
42
  if (allDictionary) for (const [lng, dict] of Object.entries(allDictionary)) Translator.seed(lng, dict);
42
43
 
@@ -47,20 +48,21 @@ const SSRProvider = ({
47
48
  of={of}
48
49
  loader={async () => {
49
50
  if (!router.isInitialized) router.init({ type: "ssr", side: "server", lang: activeLocale, prefix });
50
- return { lang: activeLocale } as const;
51
+ return { lang: activeLocale, path: activePath } as const;
51
52
  }}
52
- render={({ lang }) => (
53
+ render={({ lang, path }) => (
53
54
  <SSRWrapper
54
55
  className={className}
55
56
  appName={appName}
56
57
  lang={lang}
58
+ path={path}
57
59
  head={head}
58
60
  manifest={manifest}
59
61
  fonts={fonts}
60
62
  prefix={prefix}
61
63
  layoutStyle={layoutStyle}
62
64
  >
63
- <ClientWrapper theme={theme} lang={lang} reconnect={reconnect} dictionary={activeDictionary}>
65
+ <ClientWrapper theme={theme} lang={lang} path={path} reconnect={reconnect} dictionary={activeDictionary}>
64
66
  <Fragment key="children">{Children.toArray(children)}</Fragment>
65
67
  <Suspense key="client-inner" fallback={null}>
66
68
  <ClientInner />
@@ -106,6 +108,7 @@ interface SSRWrapperProps {
106
108
  className?: string;
107
109
  appName: string;
108
110
  lang: "en" | "ko" | (string & {});
111
+ path: string;
109
112
  head?: ReactNode;
110
113
  manifest?: WebAppManifest;
111
114
  fonts?: ReactFont[];
@@ -120,59 +123,74 @@ const SSRWrapper = ({
120
123
  manifest,
121
124
  fonts = [],
122
125
  className,
126
+ lang,
127
+ path,
123
128
  prefix,
124
129
  layoutStyle = "web",
125
- }: SSRWrapperProps) => (
126
- <>
127
- <ServerFontFace key="fonts" fonts={fonts} />
128
- <ManifestLink key="manifest" manifest={manifest} />
129
- {head ? <Fragment key="head">{head}</Fragment> : null}
130
- <div key="frame-root" id="frameRoot" className={className}>
131
- <ClientPathWrapper layoutStyle={layoutStyle} prefix={prefix}>
132
- <div key="top-safe-area" id="topSafeArea" className={clsx("fixed inset-x-0 top-0 bg-base-100")} />
133
- <div key="page-containers" id="pageContainers" className={clsx("isolate")}>
134
- <div id="pageContainer">
135
- <div
136
- id="pageContent"
137
- className={clsx("relative isolate", {
138
- "w-full": layoutStyle === "web",
139
- "left-1/2 h-screen w-[600px] -translate-x-1/2": layoutStyle === "mobile",
140
- })}
141
- >
142
- {Children.toArray(children)}
130
+ }: SSRWrapperProps) => {
131
+ const { href, pathname, search, hash } = getPathInfo(path, lang, prefix ?? "");
132
+
133
+ return (
134
+ <>
135
+ <ServerFontFace key="fonts" fonts={fonts} />
136
+ <ManifestLink key="manifest" manifest={manifest} />
137
+ {head ? <Fragment key="head">{head}</Fragment> : null}
138
+ <div key="frame-root" id="frameRoot" className={className}>
139
+ <ClientPathWrapper
140
+ layoutStyle={layoutStyle}
141
+ prefix={prefix}
142
+ initialHref={href}
143
+ initialPath={path}
144
+ initialPathname={pathname}
145
+ initialParams={{ lang }}
146
+ initialSearch={search}
147
+ initialHash={hash}
148
+ >
149
+ <div key="top-safe-area" id="topSafeArea" className={clsx("fixed inset-x-0 top-0 bg-base-100")} />
150
+ <div key="page-containers" id="pageContainers" className={clsx("isolate")}>
151
+ <div id="pageContainer">
152
+ <div
153
+ id="pageContent"
154
+ className={clsx("relative isolate", {
155
+ "w-full": layoutStyle === "web",
156
+ "left-1/2 h-screen w-[600px] -translate-x-1/2": layoutStyle === "mobile",
157
+ })}
158
+ >
159
+ {Children.toArray(children)}
160
+ </div>
143
161
  </div>
144
162
  </div>
145
- </div>
146
- <div
147
- key="top-inset"
148
- id="topInsetContainer"
149
- className={clsx("fixed inset-x-0 top-0 isolate bg-base-100", {
150
- "left-1/2 w-[600px] -translate-x-1/2": layoutStyle === "mobile",
151
- "w-full": layoutStyle === "web",
152
- })}
153
- >
154
- <div id="topInsetContent" className={clsx("relative isolate size-full")} />
155
- </div>
156
- <div
157
- key="top-left-action"
158
- id="topLeftActionContainer"
159
- className="absolute top-0 left-0 isolate flex aspect-1 items-center justify-center"
160
- />
161
- <div
162
- key="bottom-inset"
163
- id="bottomInsetContainer"
164
- className={clsx("fixed inset-x-0 bottom-0 isolate", {
165
- "left-1/2 w-[600px] -translate-x-1/2": layoutStyle === "mobile",
166
- "w-full": layoutStyle === "web",
167
- })}
168
- >
169
- <div id="bottomInsetContent" className="isolate size-full" />
170
- </div>
171
- <div key="bottom-safe-area" id="bottomSafeArea" className="fixed inset-x-0 bg-base-100" />
172
- </ClientPathWrapper>
173
- </div>
174
- </>
175
- );
163
+ <div
164
+ key="top-inset"
165
+ id="topInsetContainer"
166
+ className={clsx("fixed inset-x-0 top-0 isolate bg-base-100", {
167
+ "left-1/2 w-[600px] -translate-x-1/2": layoutStyle === "mobile",
168
+ "w-full": layoutStyle === "web",
169
+ })}
170
+ >
171
+ <div id="topInsetContent" className={clsx("relative isolate size-full")} />
172
+ </div>
173
+ <div
174
+ key="top-left-action"
175
+ id="topLeftActionContainer"
176
+ className="absolute top-0 left-0 isolate flex aspect-1 items-center justify-center"
177
+ />
178
+ <div
179
+ key="bottom-inset"
180
+ id="bottomInsetContainer"
181
+ className={clsx("fixed inset-x-0 bottom-0 isolate", {
182
+ "left-1/2 w-[600px] -translate-x-1/2": layoutStyle === "mobile",
183
+ "w-full": layoutStyle === "web",
184
+ })}
185
+ >
186
+ <div id="bottomInsetContent" className="isolate size-full" />
187
+ </div>
188
+ <div key="bottom-safe-area" id="bottomSafeArea" className="fixed inset-x-0 bg-base-100" />
189
+ </ClientPathWrapper>
190
+ </div>
191
+ </>
192
+ );
193
+ };
176
194
  SSR.Wrapper = SSRWrapper;
177
195
 
178
196
  export default SSRProvider;