akanjs 2.3.2-rc.5 → 2.3.2-rc.7
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/makePageProto.tsx +4 -2
- package/client/translator.ts +24 -0
- package/dictionary/trans.ts +1 -1
- package/fetch/client/fetchClient.ts +3 -3
- package/fetch/fetchType/buildFetch.type.ts +56 -16
- package/fetch/fetchType/endpointFetch.type.ts +47 -18
- package/fetch/types.ts +15 -9
- package/package.json +1 -1
- package/server/hmr/clientScript.ts +14 -1
- package/server/hmr/devHmrController.ts +55 -8
- package/signal/endpoint.ts +1 -48
- package/types/client/translator.d.ts +4 -0
- package/types/fetch/client/fetchClient.d.ts +1 -0
- package/types/fetch/fetchType/buildFetch.type.d.ts +29 -9
- package/types/fetch/fetchType/endpointFetch.type.d.ts +21 -15
- package/types/fetch/types.d.ts +6 -9
- package/types/server/hmr/clientScript.d.ts +1 -1
- package/types/server/hmr/devHmrController.d.ts +3 -0
- package/types/signal/endpoint.d.ts +1 -5
- package/types/ui/System/Client.d.ts +11 -3
- package/types/ui/System/SSR.d.ts +2 -1
- package/ui/Link/SsrLink.tsx +1 -6
- package/ui/System/Client.tsx +29 -9
- package/ui/System/SSR.tsx +72 -54
package/client/makePageProto.tsx
CHANGED
|
@@ -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
|
|
package/client/translator.ts
CHANGED
|
@@ -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
|
}
|
package/dictionary/trans.ts
CHANGED
|
@@ -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,
|
|
666
|
-
Object.entries(
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
?
|
|
25
|
-
:
|
|
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
|
|
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
|
|
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: [
|
|
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
|
|
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
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
@@ -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
|
|
152
|
-
const
|
|
153
|
-
const
|
|
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=${
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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/signal/endpoint.ts
CHANGED
|
@@ -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
|
}
|
|
@@ -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
|
|
7
|
-
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
]
|
|
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
|
|
5
|
-
type
|
|
6
|
-
type
|
|
7
|
-
type
|
|
8
|
-
type
|
|
9
|
-
type
|
|
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 {};
|
package/types/fetch/types.d.ts
CHANGED
|
@@ -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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
type
|
|
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
|
}
|
|
@@ -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>,
|
|
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;
|
package/types/ui/System/SSR.d.ts
CHANGED
|
@@ -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[];
|
package/ui/Link/SsrLink.tsx
CHANGED
|
@@ -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
|
|
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, "");
|
package/ui/System/Client.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
</
|
|
173
|
-
|
|
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;
|