akanjs 2.3.5-rc.1 → 2.3.5-rc.11

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/README.ko.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  [English](https://unpkg.com/akanjs@latest/README.md) | [Docs](https://akanjs.com/docs) | [npm](https://www.npmjs.com/package/akanjs)
4
4
 
5
- **코드가 아니라, 비즈니스를 씁니다.**
6
- **Write the business, not the plumbing.**
5
+ **한 줄의 비즈니스 코드로 웹·iOS·Android·서버·DB를 한 번에.**
6
+ **One line of business code ships web, iOS, Android, server, and database together.**
7
7
 
8
- Akan.js는 비즈니스 정의가 애플리케이션이 되도록 설계된 Bun-first 풀스택 TypeScript
9
- 프레임워크입니다. 하나의 코드베이스에서 SEO 웹, iOS/Android 앱 화면, 서버, 데이터베이스 계약,
10
- 인프라 산출물, 문서화까지 이어갑니다.
8
+ 프레임워크 조립, 중복 선언, 플랫폼별 재작성은 이제 그만. Akan.js는 비즈니스 의도를 곳에 쓰면
9
+ SEO 웹, iOS/Android 앱 패키지, 서버, 데이터베이스 계약, 인프라 산출물, 문서화까지 함께 이어지는
10
+ Bun-first 풀스택 TypeScript 프레임워크입니다.
11
11
 
12
12
  ```bash
13
13
  bunx create-akan-workspace@latest
@@ -15,14 +15,14 @@ bunx create-akan-workspace@latest
15
15
 
16
16
  ## 왜 Akan인가
17
17
 
18
- Akan은 기술 계층마다 같은 의도를 다시 쓰는 대신, 비즈니스가 무엇을 하는지 또렷하게 선언하도록
19
- 돕습니다. 도메인을 한 번 정의하면 schema, API, fetch client, store, UI, server, deployment
20
- artifact, generated reference가 같은 의도에서 이어집니다.
18
+ Akan은 기술 계층을 손으로 배선하는 대신 비즈니스를 먼저 출시하도록 돕습니다. 도메인을
19
+ 정의하면 schema, API, fetch client, store, UI, server, app packaging, deployment artifact,
20
+ generated reference가 같은 의도에서 이어집니다.
21
21
 
22
- - **Agents write**: 비즈니스 정의가 코드의 원천입니다. 반복적인 프레임워크 코드는 생성과 규칙이 맡습니다.
23
- - **Keep it minimal**: 줄의 의도가 웹, 앱, 서버, 데이터베이스, 배포까지 이어집니다.
24
- - **Always readable**: 적은 파일, 엄격한 규칙, 예측 가능한 선언 방식으로 6개월 뒤에도 읽히는 코드를 만듭니다.
25
- - **Nice to review**: 군더더기 없는 도메인 변경은 확인하기 쉽고, 리뷰가 빠르며, 곧장 배포하기 좋습니다.
22
+ - **웹 개발자**: 같은 비즈니스 코드가 SEO 웹과 네이티브 느낌의 iOS/Android 화면까지 이어집니다.
23
+ - **앱 개발자**: Bun 서버, SQLite, API 계약, 검증이 손배선 없이 따라옵니다.
24
+ - **1인 창업가 / 소규모 팀**: 풀스택 1명이 5개 표면을 책임집니다. 사람 명, 코드 1/4.
25
+ - **AI 에이전트도 일관되게**: 엄격한 규칙이 파일 위치, 이름, 구조, 선언 방식을 읽기 좋게 유지합니다.
26
26
 
27
27
  ## 요구사항
28
28
 
@@ -104,12 +104,39 @@ flowchart LR
104
104
  하나의 모델과 하나의 계약이 DB, 서버, API, 상태관리, UI까지 이어지기 때문에 계층마다 의도를
105
105
  반복해서 작성할 필요가 줄어듭니다.
106
106
 
107
+ ## 8 in 1
108
+
109
+ 필드 하나. 8개 레이어가 따라옵니다.
110
+
111
+ 기존 풀스택 앱에서 비즈니스 필드 하나를 추가하려면 보통 이만큼을 직접 손으로 해야 합니다.
112
+
113
+ 1. DB 스키마 정의.
114
+ 2. 쿼리 필드 추가.
115
+ 3. 서비스 로직 추가.
116
+ 4. API 필드 추가.
117
+ 5. fetch 필드 추가.
118
+ 6. 클라이언트 타입 선언.
119
+ 7. 상태관리 선언.
120
+ 8. UI prop 선언.
121
+
122
+ 한 곳만 바뀌어도 8곳을 따라 고쳐야 하고, 한 곳을 빠뜨리면 타입이 깨지거나 런타임에서 터집니다.
123
+ Akan에서는 비즈니스 필드가 코드의 원천입니다.
124
+
125
+ ```ts
126
+ export class ProductInput extends via((field) => ({
127
+ name: field(String),
128
+ })) {}
129
+ ```
130
+
131
+ 이 선언 하나로 스키마와 타입이 동시에 정의됩니다. 주변 레이어는 같은 의도에서 생성되므로 스택이
132
+ 흩어져 스파게티가 되는 일을 줄입니다.
133
+
107
134
  ## Akan이 만드는 것
108
135
 
109
136
  Akan은 보통 여러 프로젝트로 흩어지는 것들을 하나의 프레임워크로 묶습니다.
110
137
 
111
138
  - SEO에 최적화된 server-side rendered web surface.
112
- - 아름다운 페이지 전환을 가진 iOS/Android 친화 client-side rendered app surface.
139
+ - 컴파일된 surface를 앱으로 패키징한 iOS/Android surface와 네이티브 수준의 built-in 화면 전환.
113
140
  - Bun 기반 HTTP/WebSocket server.
114
141
  - SQLite-first 단순 서버 경로와 더 큰 시스템을 위한 Postgres/Redis 확장 경로.
115
142
  - Schema validation, error handling, security helper, middleware.
@@ -121,6 +148,10 @@ Akan은 보통 여러 프로젝트로 흩어지는 것들을 하나의 프레임
121
148
  배포와 문서 작성에 하루를 쓰고 있다면 뭔가 잘못된 것입니다. Akan은 schema와 endpoint 정의가
122
149
  table reference, runtime contract, API docs, testable endpoint reference가 되도록 설계됩니다.
123
150
 
151
+ 일반적인 앱 패키징은 감싼 웹사이트처럼 느껴지기 쉽습니다. Akan의 client runtime은 목록-상세 흐름,
152
+ 오버레이, 맥락 전환을 위한 transition preset을 제공하므로 별도 UI 재작성 없이도 앱 패키징된 웹에서
153
+ 네이티브 수준의 사용자 경험을 만들 수 있습니다.
154
+
124
155
  ## 하나의 패키지, 여러 경계
125
156
 
126
157
  Akan은 하나의 npm package인 `akanjs`로 배포됩니다.
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  [한국어](https://unpkg.com/akanjs@latest/README.ko.md) | [Docs](https://akanjs.com/docs) | [npm](https://www.npmjs.com/package/akanjs)
4
4
 
5
- **Write the business, not the plumbing.**
5
+ **One line of business code ships web, iOS, Android, server, and database together.**
6
6
 
7
- Akan.js is a Bun-first full-stack TypeScript framework for teams that want business definitions to be the
8
- application. One cohesive codebase can produce SEO web surfaces, iOS and Android app surfaces, servers,
9
- database contracts, infrastructure artifacts, and documentation.
7
+ No more framework assembly, duplicated declarations, or per-platform rewrites. Akan.js is a Bun-first
8
+ full-stack TypeScript framework where business intent is written once, then compiled into SEO web surfaces,
9
+ iOS and Android app packages, servers, database contracts, infrastructure artifacts, and documentation.
10
10
 
11
11
  ```bash
12
12
  bunx create-akan-workspace@latest
@@ -14,14 +14,14 @@ bunx create-akan-workspace@latest
14
14
 
15
15
  ## Why Akan
16
16
 
17
- Akan is designed for teams and solo builders who would rather describe what the business does than keep
18
- rewriting the same intent for each technical layer. Define the domain once, then let the framework connect
19
- the schema, API, fetch client, store, UI, server, deployment artifacts, and generated references.
17
+ Akan is designed for teams and solo builders who would rather ship the business than keep wiring technical
18
+ layers by hand. Define the domain once, then let the framework connect schema, API, fetch client, store, UI,
19
+ server, app packaging, deployment artifacts, and generated references.
20
20
 
21
- - **Agents write**: business definitions are the source of truth; generation handles repetitive framework code.
22
- - **Keep it minimal**: one line of intent can flow across web, app, server, database, and deployment.
23
- - **Always readable**: fewer files, strict conventions, and predictable declarations keep code clear months later.
24
- - **Nice to review**: focused domain changes are easier to inspect, approve, and ship.
21
+ - **Web devs**: the same business code becomes SEO-ready web and native-feeling iOS/Android screens.
22
+ - **App devs**: Bun server, SQLite, API contracts, and validation follow without hand-wiring.
23
+ - **Solo and small teams**: one full-stack developer can own all five surfaces with a quarter of the code.
24
+ - **Agents stay consistent**: strict conventions keep file paths, names, structures, and declarations readable.
25
25
 
26
26
  ## Requirements
27
27
 
@@ -102,12 +102,39 @@ flowchart LR
102
102
  This structure lets one model and one contract flow through the database, server, API, state management,
103
103
  and UI without duplicating intent across layers.
104
104
 
105
+ ## 8 in 1
106
+
107
+ One field. Eight layers follow.
108
+
109
+ Adding a single business field in a traditional full-stack app usually means changing all of this by hand:
110
+
111
+ 1. Define the DB schema.
112
+ 2. Add the query field.
113
+ 3. Add the service logic.
114
+ 4. Add the API field.
115
+ 5. Add the fetch field.
116
+ 6. Declare the client type.
117
+ 7. Declare the state management.
118
+ 8. Declare the UI prop.
119
+
120
+ Change one place, chase eight. Miss one, and types break, or it fails at runtime. In Akan, the business field is
121
+ the source of truth:
122
+
123
+ ```ts
124
+ export class ProductInput extends via((field) => ({
125
+ name: field(String),
126
+ })) {}
127
+ ```
128
+
129
+ That one declaration defines schema and type together. The surrounding layers are generated from the same
130
+ intent, so the stack stays connected instead of turning into spaghetti.
131
+
105
132
  ## What Akan Builds
106
133
 
107
134
  Akan is one framework for the pieces that normally become separate projects:
108
135
 
109
136
  - SEO-optimized server-side rendered web surfaces.
110
- - iOS and Android friendly client-side rendered app surfaces with page transitions.
137
+ - iOS and Android app packages from compiled web surfaces, with native-level built-in screen transitions.
111
138
  - Bun-powered HTTP and WebSocket servers.
112
139
  - SQLite-first simple server paths, with Postgres and Redis expansion paths for larger systems.
113
140
  - Schema validation, error handling, security helpers, and middleware.
@@ -119,6 +146,10 @@ Akan is one framework for the pieces that normally become separate projects:
119
146
  Deployment and documentation should not consume a day of work. Akan is built so schema and endpoint
120
147
  definitions can also become table references, runtime contracts, API docs, and testable endpoint references.
121
148
 
149
+ Unlike ordinary app packaging that can feel like a wrapped website, Akan's client runtime includes transition
150
+ presets for list-detail flows, overlays, and context changes. Packaged web surfaces can deliver a native-level
151
+ user experience without a separate UI rewrite.
152
+
122
153
  ## One Package, Many Boundaries
123
154
 
124
155
  Akan is published as a single npm package: `akanjs`.
@@ -35,7 +35,7 @@ const serializeInput = <Input = unknown>(
35
35
  { optional = false }: { optional?: boolean } = {},
36
36
  ): Input | Input[] => {
37
37
  if (arrDepth && Array.isArray(value))
38
- return value.map((v) => serializeInput(v, inputRef, arrDepth - 1) as Input) as unknown as Input[];
38
+ return value.map((v) => serializeInput(v, inputRef, arrDepth - 1, serializeType) as Input) as unknown as Input[];
39
39
  else if ((inputRef as MapConstructor).prototype === Map.prototype) {
40
40
  const [valueRef] = getNonArrayModel(inputRef as Cls);
41
41
  const serializeFn = getSerializeFn(valueRef, { optional });
package/constant/via.ts CHANGED
@@ -174,10 +174,8 @@ export interface ConstantStatics<Schema = any, FieldObj extends FieldObject = Fi
174
174
  text: { search: Set<string>; filter: Set<string>; children: { search: Set<string>; filter: Set<string> } };
175
175
  _DatabaseSchema: {
176
176
  [K in keyof Schema]: K extends keyof FieldObj
177
- ? FieldObj[K] extends ConstantField<infer FieldType, any, any, any, any, any>
178
- ? FieldType extends "hidden"
179
- ? NonNullable<Schema[K]>
180
- : Schema[K]
177
+ ? FieldObj[K]["fieldType"] extends "hidden"
178
+ ? NonNullable<Schema[K]>
181
179
  : Schema[K]
182
180
  : Schema[K];
183
181
  };
@@ -1,5 +1,4 @@
1
- import type { Assign } from "akanjs/base";
2
- import type { DatabaseSignal, SerializedSignal, ServiceSignal } from "akanjs/signal";
1
+ import type { DatabaseSignal, EndpointCls, SerializedSignal, ServiceSignal, SliceCls } from "akanjs/signal";
3
2
  import type { FetchProxy } from "../client";
4
3
  import type { GetSliceMetaObjFromDatabaseSignals } from "../types";
5
4
  import type { SliceMeta } from "./appliedReturn.type";
@@ -20,20 +19,34 @@ export type FetchSignalInput = FetchProxySignalInput | DatabaseSignal | ServiceS
20
19
 
21
20
  export type FetchTypeOfSignal<Signal extends FetchSignalInput> = Signal extends { _FetchType: infer FetchType }
22
21
  ? FetchType
23
- : Signal extends DatabaseSignal<infer _IntlCls, infer EndpCls, infer SlceCls, infer _SrvrCls>
22
+ : Signal extends { endpoint: infer EndpCls extends EndpointCls; slice: infer SlceCls extends SliceCls }
24
23
  ? GetFetchTypeFromEndpoint<EndpCls, SlceCls> & GetFetchTypeFromSlice<SlceCls>
25
- : Signal extends ServiceSignal<infer _IntlCls, infer EndpCls, infer _SrvrCls>
24
+ : Signal extends { endpoint: infer EndpCls extends EndpointCls }
26
25
  ? GetFetchTypeFromEndpoint<EndpCls>
27
26
  : unknown;
28
27
 
29
- type MergeProxyFetchTypes<
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<
30
32
  Signals extends readonly FetchSignalInput[],
31
- Acc = Record<never, never>,
33
+ Key extends PropertyKey,
34
+ Current = never,
32
35
  > = Signals extends readonly [infer First extends FetchSignalInput, ...infer Rest extends readonly FetchSignalInput[]]
33
36
  ? First extends { _FetchType: infer FetchType }
34
- ? MergeProxyFetchTypes<Rest, Assign<Acc, FetchType>>
35
- : MergeProxyFetchTypes<Rest, Acc>
36
- : Acc;
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,
45
+ ]
46
+ ? Record<never, never>
47
+ : {
48
+ [Key in FetchKeysOfProxySignals<Signals>]: LastProxyFetchValue<Signals, Key>;
49
+ };
37
50
 
38
51
  type MergeDirectFetchTypes<
39
52
  Signals extends readonly FetchSignalInput[],
@@ -41,13 +54,13 @@ type MergeDirectFetchTypes<
41
54
  > = Signals extends readonly [infer First extends FetchSignalInput, ...infer Rest extends readonly FetchSignalInput[]]
42
55
  ? First extends { _FetchType: unknown }
43
56
  ? MergeDirectFetchTypes<Rest, Acc>
44
- : MergeDirectFetchTypes<Rest, Assign<Acc, FetchTypeOfSignal<First>>>
57
+ : MergeDirectFetchTypes<Rest, Acc & FetchTypeOfSignal<First>>
45
58
  : Acc;
46
59
 
47
60
  export type MergeAllFetchTypes<Signals extends readonly FetchSignalInput[]> =
48
61
  MergeProxyFetchTypes<Signals> extends infer ProxyFetch
49
62
  ? MergeDirectFetchTypes<Signals> extends infer DirectFetch
50
- ? Assign<ProxyFetch, DirectFetch>
63
+ ? Omit<ProxyFetch, keyof DirectFetch> & DirectFetch
51
64
  : never
52
65
  : never;
53
66
 
@@ -10,95 +10,81 @@ import type {
10
10
  SliceCls,
11
11
  } from "akanjs/signal";
12
12
 
13
- export type EndpointReturnContext<Full = unknown, Light = unknown, Insight = unknown> = {
14
- full: Full;
15
- light: Light;
16
- insight: Insight;
17
- };
18
-
19
- type EndpointReturnContextOf<SlceCls extends SliceCls | never> = [SlceCls] extends [never]
20
- ? never
21
- : SlceCls extends SliceCls
22
- ? EndpointReturnContext<
23
- SlceCls["srv"]["cnst"]["_Full"],
24
- SlceCls["srv"]["cnst"]["_Light"],
25
- SlceCls["srv"]["cnst"]["_Insight"]
26
- >
27
- : never;
28
-
29
- type ExtendedEndpointReturn<ClientReturns, Context> = ClientReturns extends (infer R)[]
30
- ? ExtendedEndpointReturn<R, Context>[]
31
- : Context extends EndpointReturnContext<infer Full, infer Light, infer Insight>
32
- ? Full extends ClientReturns
33
- ? Full
34
- : Light extends ClientReturns
35
- ? Light
36
- : Insight extends ClientReturns
37
- ? Insight
38
- : ClientReturns
39
- : ClientReturns;
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;
40
22
 
41
- type EndpointClientReturns<E, Context> = [Context] extends [never]
23
+ type EndpointClientReturns<E, SlceCls extends SliceCls | never> = [SlceCls] extends [never]
42
24
  ? EndpInfoClientReturns<E>
43
- : ExtendedEndpointReturn<EndpInfoClientReturns<E>, Context>;
25
+ : ExtendedEndpointReturn<
26
+ EndpInfoClientReturns<E>,
27
+ SlceCls["srv"]["cnst"]["_Full"],
28
+ SlceCls["srv"]["cnst"]["_Light"],
29
+ SlceCls["srv"]["cnst"]["_Insight"]
30
+ >;
44
31
 
45
- type EndpInfoReturns<E, Context> =
46
- | EndpointClientReturns<E, Context>
32
+ type EndpInfoReturns<E, SlceCls extends SliceCls | never> =
33
+ | EndpointClientReturns<E, SlceCls>
47
34
  | (EndpInfoNullable<E> extends true ? null : never);
48
35
 
49
- type QueryOrMutationFetchFn<E, Context> = (
36
+ type QueryOrMutationFetchFn<E, SlceCls extends SliceCls | never> = (
50
37
  ...args: [...EndpInfoArgs<E>, fetchPolicy?: FetchPolicy]
51
- ) => Promise<EndpInfoReturns<E, Context>>;
38
+ ) => Promise<EndpInfoReturns<E, SlceCls>>;
52
39
 
53
- type MessageEmitFn<E, Context> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E, Context>;
40
+ type MessageEmitFn<E, SlceCls extends SliceCls | never> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E, SlceCls>;
54
41
 
55
- type MessageListenFn<E, Context> = (
56
- handleEvent: (data: EndpInfoReturns<E, Context>) => PromiseOrObject<void>,
42
+ type MessageListenFn<E, SlceCls extends SliceCls | never> = (
43
+ handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
57
44
  options?: FetchPolicy,
58
45
  ) => () => void;
59
46
 
60
- type PubsubSubscribeFn<E, Context> = (
47
+ type PubsubSubscribeFn<E, SlceCls extends SliceCls | never> = (
61
48
  ...args: [
62
49
  ...EndpInfoArgs<E>,
63
- handleEvent: (data: EndpInfoReturns<E, Context>) => PromiseOrObject<void>,
50
+ handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
64
51
  options?: FetchPolicy,
65
52
  ]
66
53
  ) => () => void;
67
54
 
68
- type PrimaryFetchFn<E, Context> =
55
+ type PrimaryFetchFn<E, SlceCls extends SliceCls | never> =
69
56
  EndpInfoReqType<E> extends "query" | "mutation"
70
- ? QueryOrMutationFetchFn<E, Context>
57
+ ? QueryOrMutationFetchFn<E, SlceCls>
71
58
  : EndpInfoReqType<E> extends "message"
72
- ? MessageEmitFn<E, Context>
59
+ ? MessageEmitFn<E, SlceCls>
73
60
  : never;
74
61
 
75
- type PrimaryFetchType<EInfoObj extends { [key: string]: EndpointInfo }, Context> = {
62
+ type PrimaryFetchType<EInfoObj extends { [key: string]: EndpointInfo }, SlceCls extends SliceCls | never> = {
76
63
  [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "query" | "mutation" | "message"
77
64
  ? K
78
- : never]: PrimaryFetchFn<EInfoObj[K], Context>;
65
+ : never]: PrimaryFetchFn<EInfoObj[K], SlceCls>;
79
66
  };
80
67
 
81
- type PubsubFetchType<EInfoObj extends { [key: string]: EndpointInfo }, Context> = {
68
+ type PubsubFetchType<EInfoObj extends { [key: string]: EndpointInfo }, SlceCls extends SliceCls | never> = {
82
69
  [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "pubsub"
83
70
  ? K extends string
84
71
  ? `subscribe${Capitalize<K>}`
85
72
  : never
86
- : never]: PubsubSubscribeFn<EInfoObj[K], Context>;
73
+ : never]: PubsubSubscribeFn<EInfoObj[K], SlceCls>;
87
74
  };
88
75
 
89
- type MessageListenFetchType<EInfoObj extends { [key: string]: EndpointInfo }, Context> = {
76
+ type MessageListenFetchType<EInfoObj extends { [key: string]: EndpointInfo }, SlceCls extends SliceCls | never> = {
90
77
  [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "message"
91
78
  ? K extends string
92
79
  ? `listen${Capitalize<K>}`
93
80
  : never
94
- : never]: MessageListenFn<EInfoObj[K], Context>;
81
+ : never]: MessageListenFn<EInfoObj[K], SlceCls>;
95
82
  };
96
83
 
97
84
  export type GetFetchTypeFromEndpoint<
98
85
  EndpCls extends EndpointCls,
99
86
  SlceCls extends SliceCls | never = never,
100
87
  _EndpointInfoObj extends { [key: string]: EndpointInfo } = EndpCls[typeof ENDPOINT_META],
101
- _ReturnContext = EndpointReturnContextOf<SlceCls>,
102
- > = PrimaryFetchType<_EndpointInfoObj, _ReturnContext> &
103
- PubsubFetchType<_EndpointInfoObj, _ReturnContext> &
104
- MessageListenFetchType<_EndpointInfoObj, _ReturnContext>;
88
+ > = PrimaryFetchType<_EndpointInfoObj, SlceCls> &
89
+ PubsubFetchType<_EndpointInfoObj, SlceCls> &
90
+ MessageListenFetchType<_EndpointInfoObj, SlceCls>;
@@ -2,164 +2,104 @@ import type { GetStateObject, SLICE_META } from "akanjs/base";
2
2
  import type { FetchPolicy } from "akanjs/common";
3
3
  import type { ConstantModel, DefaultOf, ProtoFile, PurifiedModel } from "akanjs/constant";
4
4
  import type { DatabaseModel, ExtractSort, FilterInstance } from "akanjs/document";
5
- import type { SliceCls, SliceInfo, SliceInfoArgs } from "akanjs/signal";
5
+ import type { SliceCls, SliceInfoArgs } from "akanjs/signal";
6
6
  import type { ClientEdit, ClientInit, ClientView, EditReturn, InitReturn, ViewReturn } from "./appliedReturn.type";
7
7
 
8
- type SliceInfoMap = { [key: string]: SliceInfo };
9
- type LightWithId<Light> = Light extends { id: string } ? Light : { id: string };
8
+ type _SliceMap<S extends SliceCls> = S[typeof SLICE_META];
9
+ type _RefName<S extends SliceCls> = S["srv"]["cnst"]["refName"];
10
+ type _Cap<S extends SliceCls> = S["srv"]["cnst"]["_CapitalizedRefName"];
11
+ type _Input<S extends SliceCls> = S["srv"]["cnst"]["_Input"];
12
+ type _Full<S extends SliceCls> = S["srv"]["cnst"]["_Full"];
13
+ type _Light<S extends SliceCls> = S["srv"]["cnst"]["_Light"];
14
+ type _Insight<S extends SliceCls> = S["srv"]["cnst"]["_Insight"];
15
+ type _PurifiedInput<S extends SliceCls> = S["srv"]["cnst"]["_PurifiedInput"];
16
+ type _DefaultInput<S extends SliceCls> = S["srv"]["cnst"]["_DefaultInput"];
17
+ type _StateLight<S extends SliceCls> = S["srv"]["cnst"]["_StateLight"];
18
+ type _StateInsight<S extends SliceCls> = S["srv"]["cnst"]["_StateInsight"];
19
+ type _Filter<S extends SliceCls> = S["srv"]["db"]["_Filter"];
20
+ type _Sort<S extends SliceCls> = S["srv"]["db"]["_Sort"];
21
+ type _LightWithId<S extends SliceCls> = _Light<S> extends { id: string } ? _Light<S> : { id: string };
22
+ type _SliceFetchInitOption<S extends SliceCls> = FetchInitOption<_Input<S>, _Filter<S>, _DefaultInput<S>, _Sort<S>>;
10
23
 
11
- export type SliceFetchDescriptor<
12
- SliceMap extends SliceInfoMap = SliceInfoMap,
13
- RefName extends string = string,
14
- CapRefName extends string = Capitalize<RefName>,
15
- Input = unknown,
16
- Full = unknown,
17
- Light = unknown,
18
- Insight = unknown,
19
- PurifiedInput = PurifiedModel<Input>,
20
- DefaultInput = DefaultOf<Input>,
21
- StateLight = GetStateObject<LightWithId<Light>>,
22
- StateInsight = GetStateObject<Insight>,
23
- Filter extends FilterInstance = FilterInstance,
24
- Sort = ExtractSort<Filter>,
25
- > = {
26
- sliceMap: SliceMap;
27
- refName: RefName;
28
- capRefName: CapRefName;
29
- input: Input;
30
- full: Full;
31
- light: Light;
32
- insight: Insight;
33
- purifiedInput: PurifiedInput;
34
- defaultInput: DefaultInput;
35
- stateLight: StateLight;
36
- stateInsight: StateInsight;
37
- filter: Filter;
38
- sort: Sort;
39
- };
40
-
41
- export type SliceFetchDescriptorOf<SlceCls extends SliceCls> = SlceCls["srv"]["cnst"] extends ConstantModel
42
- ? SlceCls["srv"]["db"] extends DatabaseModel
43
- ? SliceFetchDescriptor<
44
- SlceCls[typeof SLICE_META],
45
- SlceCls["srv"]["cnst"]["refName"],
46
- SlceCls["srv"]["cnst"]["_CapitalizedRefName"],
47
- SlceCls["srv"]["cnst"]["_Input"],
48
- SlceCls["srv"]["cnst"]["_Full"],
49
- SlceCls["srv"]["cnst"]["_Light"],
50
- SlceCls["srv"]["cnst"]["_Insight"],
51
- SlceCls["srv"]["cnst"]["_PurifiedInput"],
52
- SlceCls["srv"]["cnst"]["_DefaultInput"],
53
- GetStateObject<LightWithId<SlceCls["srv"]["cnst"]["_Light"]>>,
54
- GetStateObject<SlceCls["srv"]["cnst"]["_Insight"]>,
55
- SlceCls["srv"]["db"]["_Filter"],
56
- SlceCls["srv"]["db"]["_Sort"]
57
- >
58
- : never
59
- : never;
60
-
61
- type _SliceMap<D extends SliceFetchDescriptor> = D["sliceMap"];
62
- type _RefName<D extends SliceFetchDescriptor> = D["refName"];
63
- type _Cap<D extends SliceFetchDescriptor> = D["capRefName"];
64
- type _Input<D extends SliceFetchDescriptor> = D["input"];
65
- type _Full<D extends SliceFetchDescriptor> = D["full"];
66
- type _Light<D extends SliceFetchDescriptor> = D["light"];
67
- type _Insight<D extends SliceFetchDescriptor> = D["insight"];
68
- type _PurifiedInput<D extends SliceFetchDescriptor> = D["purifiedInput"];
69
- type _DefaultInput<D extends SliceFetchDescriptor> = D["defaultInput"];
70
- type _StateLight<D extends SliceFetchDescriptor> = D["stateLight"];
71
- type _StateInsight<D extends SliceFetchDescriptor> = D["stateInsight"];
72
- type _Filter<D extends SliceFetchDescriptor> = D["filter"];
73
- type _Sort<D extends SliceFetchDescriptor> = D["sort"];
74
- type _LightWithId<D extends SliceFetchDescriptor> = LightWithId<_Light<D>>;
75
- type _SliceFetchInitOption<D extends SliceFetchDescriptor> = FetchInitOption<
76
- _Input<D>,
77
- _Filter<D>,
78
- _DefaultInput<D>,
79
- _Sort<D>
80
- >;
81
-
82
- type SliceListFetch<D extends SliceFetchDescriptor> = {
83
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `${_RefName<D>}List${Capitalize<Suffix>}` : never]: (
24
+ type SliceListFetch<S extends SliceCls> = {
25
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `${_RefName<S>}List${Capitalize<Suffix>}` : never]: (
84
26
  ...args: [
85
- ...SliceInfoArgs<_SliceMap<D>[Suffix]>,
27
+ ...SliceInfoArgs<_SliceMap<S>[Suffix]>,
86
28
  skip?: number | null,
87
29
  limit?: number | null,
88
- sort?: _Sort<D> | null,
30
+ sort?: _Sort<S> | null,
89
31
  fetchPolicy?: FetchPolicy,
90
32
  ]
91
- ) => Promise<_Light<D>[]>;
33
+ ) => Promise<_Light<S>[]>;
92
34
  };
93
35
 
94
- type SliceInsightFetch<D extends SliceFetchDescriptor> = {
95
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `${_RefName<D>}Insight${Capitalize<Suffix>}` : never]: (
96
- ...args: [...SliceInfoArgs<_SliceMap<D>[Suffix]>, fetchPolicy?: FetchPolicy]
97
- ) => Promise<_Insight<D>>;
36
+ type SliceInsightFetch<S extends SliceCls> = {
37
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `${_RefName<S>}Insight${Capitalize<Suffix>}` : never]: (
38
+ ...args: [...SliceInfoArgs<_SliceMap<S>[Suffix]>, fetchPolicy?: FetchPolicy]
39
+ ) => Promise<_Insight<S>>;
98
40
  };
99
41
 
100
- type SliceInitFetch<D extends SliceFetchDescriptor> = {
101
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `init${_Cap<D>}${Capitalize<Suffix>}` : never]: (
102
- ...args: [...SliceInfoArgs<_SliceMap<D>[Suffix]>, option?: _SliceFetchInitOption<D>]
42
+ type SliceInitFetch<S extends SliceCls> = {
43
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `init${_Cap<S>}${Capitalize<Suffix>}` : never]: (
44
+ ...args: [...SliceInfoArgs<_SliceMap<S>[Suffix]>, option?: _SliceFetchInitOption<S>]
103
45
  ) => Promise<
104
46
  InitReturn<
105
- _RefName<D>,
47
+ _RefName<S>,
106
48
  Suffix & string,
107
- _Light<D>,
108
- _Insight<D>,
109
- SliceInfoArgs<_SliceMap<D>[Suffix]>,
110
- _Filter<D>,
111
- _Cap<D>,
49
+ _Light<S>,
50
+ _Insight<S>,
51
+ SliceInfoArgs<_SliceMap<S>[Suffix]>,
52
+ _Filter<S>,
53
+ _Cap<S>,
112
54
  Suffix extends string ? Capitalize<Suffix> : never,
113
- _LightWithId<D>,
114
- _StateLight<D>,
115
- _StateInsight<D>,
116
- _Sort<D>
55
+ _LightWithId<S>,
56
+ GetStateObject<_LightWithId<S>>,
57
+ GetStateObject<_Insight<S>>,
58
+ _Sort<S>
117
59
  >
118
60
  >;
119
61
  };
120
62
 
121
- type SliceGetInitFetch<D extends SliceFetchDescriptor> = {
122
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `get${_Cap<D>}Init${Capitalize<Suffix>}` : never]: (
123
- ...args: [...SliceInfoArgs<_SliceMap<D>[Suffix]>, option?: _SliceFetchInitOption<D>]
63
+ type SliceGetInitFetch<S extends SliceCls> = {
64
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `get${_Cap<S>}Init${Capitalize<Suffix>}` : never]: (
65
+ ...args: [...SliceInfoArgs<_SliceMap<S>[Suffix]>, option?: _SliceFetchInitOption<S>]
124
66
  ) => ClientInit<
125
- _RefName<D>,
126
- _Light<D>,
127
- _Insight<D>,
128
- SliceInfoArgs<_SliceMap<D>[Suffix]>,
129
- _Filter<D>,
130
- _Cap<D>,
131
- _StateLight<D>,
132
- _StateInsight<D>,
133
- _Sort<D>
67
+ _RefName<S>,
68
+ _Light<S>,
69
+ _Insight<S>,
70
+ SliceInfoArgs<_SliceMap<S>[Suffix]>,
71
+ _Filter<S>,
72
+ _Cap<S>,
73
+ GetStateObject<_LightWithId<S>>,
74
+ GetStateObject<_Insight<S>>,
75
+ _Sort<S>
134
76
  >;
135
77
  };
136
78
 
137
- export type GetFetchTypeFromSliceDescriptor<Descriptor> = [Descriptor] extends [SliceFetchDescriptor]
138
- ? SliceListFetch<Descriptor> &
139
- SliceInsightFetch<Descriptor> &
140
- SliceInitFetch<Descriptor> &
141
- SliceGetInitFetch<Descriptor> &
142
- RawBaseSliceFetchType<
143
- _RefName<Descriptor>,
144
- _Input<Descriptor>,
145
- _Full<Descriptor>,
146
- _Light<Descriptor>,
147
- _Cap<Descriptor>,
148
- _PurifiedInput<Descriptor>
149
- > &
150
- AppliedBaseSliceFetchType<
151
- _RefName<Descriptor>,
152
- _Input<Descriptor>,
153
- _Full<Descriptor>,
154
- _Cap<Descriptor>,
155
- _PurifiedInput<Descriptor>
156
- >
79
+ export type GetFetchTypeFromSlice<SlceCls extends SliceCls> = SlceCls["srv"]["cnst"] extends ConstantModel
80
+ ? SlceCls["srv"]["db"] extends DatabaseModel
81
+ ? SliceListFetch<SlceCls> &
82
+ SliceInsightFetch<SlceCls> &
83
+ SliceInitFetch<SlceCls> &
84
+ SliceGetInitFetch<SlceCls> &
85
+ RawBaseSliceFetchType<
86
+ _RefName<SlceCls>,
87
+ _Input<SlceCls>,
88
+ _Full<SlceCls>,
89
+ _Light<SlceCls>,
90
+ _Cap<SlceCls>,
91
+ _PurifiedInput<SlceCls>
92
+ > &
93
+ AppliedBaseSliceFetchType<
94
+ _RefName<SlceCls>,
95
+ _Input<SlceCls>,
96
+ _Full<SlceCls>,
97
+ _Cap<SlceCls>,
98
+ _PurifiedInput<SlceCls>
99
+ >
100
+ : never
157
101
  : never;
158
102
 
159
- export type GetFetchTypeFromSlice<SlceCls extends SliceCls> = GetFetchTypeFromSliceDescriptor<
160
- SliceFetchDescriptorOf<SlceCls>
161
- >;
162
-
163
103
  type RawBaseSliceFetchType<
164
104
  RefName extends string,
165
105
  Input,
package/fetch/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Assign, type Environment, getEnv, type SLICE_META } from "akanjs/base";
1
+ import { type Environment, getEnv, type SLICE_META } from "akanjs/base";
2
2
  import type { DatabaseSignal } from "akanjs/signal";
3
3
  import type { SliceMeta } from "./fetchType/appliedReturn.type";
4
4
 
@@ -16,14 +16,14 @@ type GetSliceMetaObjFromSignal<Signal> = Signal extends {
16
16
  _SliceMetaObj: infer SliceMetaObj extends Record<string, SliceMeta>;
17
17
  }
18
18
  ? SliceMetaObj
19
- : Signal extends DatabaseSignal<infer _IntlCls, infer _EndpCls, infer SlceCls, infer _SrvrCls>
19
+ : Signal extends DatabaseSignal
20
20
  ? {
21
- [K in `${SlceCls["baseName"]}${Capitalize<keyof SlceCls[typeof SLICE_META] & string>}`]: SliceMeta;
21
+ [K in `${Signal["slice"]["baseName"]}${Capitalize<keyof Signal["slice"][typeof SLICE_META] & string>}`]: SliceMeta;
22
22
  }
23
23
  : Record<never, never>;
24
24
  export type GetSliceMetaObjFromDatabaseSignals<
25
25
  Signals extends readonly unknown[],
26
26
  Acc extends Record<string, SliceMeta> = Record<never, never>,
27
27
  > = Signals extends readonly [infer First, ...infer Rest]
28
- ? GetSliceMetaObjFromDatabaseSignals<Rest, Assign<Acc, GetSliceMetaObjFromSignal<First>>>
28
+ ? GetSliceMetaObjFromDatabaseSignals<Rest, Acc & GetSliceMetaObjFromSignal<First>>
29
29
  : Acc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.3.5-rc.1",
3
+ "version": "2.3.5-rc.11",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -1,5 +1,5 @@
1
1
  import { type Cls, type EnumInstance, FIELD_META, type MergeAllKeyOfObjects, type MergeAllTypes } from "akanjs/base";
2
- import { ConstantField, type ExtractFieldInfoObject, type FieldBuilder, type FieldInfoObject, type FieldInfoObjectToFieldObject, type FieldObject, type FieldResolver } from "./fieldInfo.d.ts";
2
+ import { type ExtractFieldInfoObject, type FieldBuilder, type FieldInfoObject, type FieldInfoObjectToFieldObject, type FieldObject, type FieldResolver } from "./fieldInfo.d.ts";
3
3
  import { type PurifyFunc } from "./purify.d.ts";
4
4
  import type { BaseInsight, BaseObject, ConstantType, DefaultOf, NonFunctionalKeys } from "./types.d.ts";
5
5
  type BaseFields = "id" | "createdAt" | "updatedAt" | "removedAt";
@@ -24,7 +24,7 @@ export interface ConstantStatics<Schema = any, FieldObj extends FieldObject = Fi
24
24
  };
25
25
  };
26
26
  _DatabaseSchema: {
27
- [K in keyof Schema]: K extends keyof FieldObj ? FieldObj[K] extends ConstantField<infer FieldType, any, any, any, any, any> ? FieldType extends "hidden" ? NonNullable<Schema[K]> : Schema[K] : Schema[K] : Schema[K];
27
+ [K in keyof Schema]: K extends keyof FieldObj ? FieldObj[K]["fieldType"] extends "hidden" ? NonNullable<Schema[K]> : Schema[K] : Schema[K];
28
28
  };
29
29
  }
30
30
  export type ConstantCls<Schema = any, FieldObj extends FieldObject = FieldObject> = (new (obj?: Partial<Schema>) => Schema & ConstantMethods<Schema>) & ConstantStatics<Schema, FieldObj>;
@@ -1,5 +1,4 @@
1
- import type { Assign } from "akanjs/base";
2
- import type { DatabaseSignal, SerializedSignal, ServiceSignal } from "akanjs/signal";
1
+ import type { DatabaseSignal, EndpointCls, SerializedSignal, ServiceSignal, SliceCls } from "akanjs/signal";
3
2
  import type { FetchProxy } from "../client.d.ts";
4
3
  import type { GetSliceMetaObjFromDatabaseSignals } from "../types.d.ts";
5
4
  import type { SliceMeta } from "./appliedReturn.type";
@@ -14,13 +13,27 @@ export type FetchProxySignalInput<FetchType = unknown, SliceMetaObj extends Reco
14
13
  export type FetchSignalInput = FetchProxySignalInput | DatabaseSignal | ServiceSignal;
15
14
  export type FetchTypeOfSignal<Signal extends FetchSignalInput> = Signal extends {
16
15
  _FetchType: infer FetchType;
17
- } ? FetchType : Signal extends DatabaseSignal<infer _IntlCls, infer EndpCls, infer SlceCls, infer _SrvrCls> ? GetFetchTypeFromEndpoint<EndpCls, SlceCls> & GetFetchTypeFromSlice<SlceCls> : Signal extends ServiceSignal<infer _IntlCls, infer EndpCls, infer _SrvrCls> ? GetFetchTypeFromEndpoint<EndpCls> : unknown;
18
- type MergeProxyFetchTypes<Signals extends readonly FetchSignalInput[], Acc = Record<never, never>> = Signals extends readonly [infer First extends FetchSignalInput, ...infer Rest extends readonly FetchSignalInput[]] ? First extends {
16
+ } ? FetchType : Signal extends {
17
+ endpoint: infer EndpCls extends EndpointCls;
18
+ slice: infer SlceCls extends SliceCls;
19
+ } ? GetFetchTypeFromEndpoint<EndpCls, SlceCls> & GetFetchTypeFromSlice<SlceCls> : Signal extends {
20
+ endpoint: infer EndpCls extends EndpointCls;
21
+ } ? GetFetchTypeFromEndpoint<EndpCls> : unknown;
22
+ type FetchKeysOfProxySignal<Signal> = Signal extends {
19
23
  _FetchType: infer FetchType;
20
- } ? MergeProxyFetchTypes<Rest, Assign<Acc, FetchType>> : MergeProxyFetchTypes<Rest, Acc> : Acc;
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
+ };
21
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 {
22
35
  _FetchType: unknown;
23
- } ? MergeDirectFetchTypes<Rest, Acc> : MergeDirectFetchTypes<Rest, Assign<Acc, FetchTypeOfSignal<First>>> : Acc;
24
- export type MergeAllFetchTypes<Signals extends readonly FetchSignalInput[]> = MergeProxyFetchTypes<Signals> extends infer ProxyFetch ? MergeDirectFetchTypes<Signals> extends infer DirectFetch ? Assign<ProxyFetch, DirectFetch> : never : never;
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;
25
38
  export type FetchClientType<Signals extends readonly FetchSignalInput[]> = FetchProxy<MergeAllFetchTypes<Signals>, GetSliceMetaObjFromDatabaseSignals<Signals>>;
26
39
  export {};
@@ -1,40 +1,34 @@
1
1
  import type { ENDPOINT_META, PromiseOrObject } from "akanjs/base";
2
2
  import type { FetchPolicy } from "akanjs/common";
3
3
  import type { EndpInfoArgs, EndpInfoClientReturns, EndpInfoNullable, EndpInfoReqType, EndpointCls, EndpointInfo, SliceCls } from "akanjs/signal";
4
- export type EndpointReturnContext<Full = unknown, Light = unknown, Insight = unknown> = {
5
- full: Full;
6
- light: Light;
7
- insight: Insight;
8
- };
9
- type EndpointReturnContextOf<SlceCls extends SliceCls | never> = [SlceCls] extends [never] ? never : SlceCls extends SliceCls ? EndpointReturnContext<SlceCls["srv"]["cnst"]["_Full"], SlceCls["srv"]["cnst"]["_Light"], SlceCls["srv"]["cnst"]["_Insight"]> : never;
10
- type ExtendedEndpointReturn<ClientReturns, Context> = ClientReturns extends (infer R)[] ? ExtendedEndpointReturn<R, Context>[] : Context extends EndpointReturnContext<infer Full, infer Light, infer Insight> ? Full extends ClientReturns ? Full : Light extends ClientReturns ? Light : Insight extends ClientReturns ? Insight : ClientReturns : ClientReturns;
11
- type EndpointClientReturns<E, Context> = [Context] extends [never] ? EndpInfoClientReturns<E> : ExtendedEndpointReturn<EndpInfoClientReturns<E>, Context>;
12
- type EndpInfoReturns<E, Context> = EndpointClientReturns<E, Context> | (EndpInfoNullable<E> extends true ? null : never);
13
- type QueryOrMutationFetchFn<E, Context> = (...args: [...EndpInfoArgs<E>, fetchPolicy?: FetchPolicy]) => Promise<EndpInfoReturns<E, Context>>;
14
- type MessageEmitFn<E, Context> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E, Context>;
15
- type MessageListenFn<E, Context> = (handleEvent: (data: EndpInfoReturns<E, Context>) => PromiseOrObject<void>, options?: FetchPolicy) => () => void;
16
- type PubsubSubscribeFn<E, Context> = (...args: [
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: [
17
11
  ...EndpInfoArgs<E>,
18
- handleEvent: (data: EndpInfoReturns<E, Context>) => PromiseOrObject<void>,
12
+ handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
19
13
  options?: FetchPolicy
20
14
  ]) => () => void;
21
- type PrimaryFetchFn<E, Context> = EndpInfoReqType<E> extends "query" | "mutation" ? QueryOrMutationFetchFn<E, Context> : EndpInfoReqType<E> extends "message" ? MessageEmitFn<E, Context> : never;
15
+ type PrimaryFetchFn<E, SlceCls extends SliceCls | never> = EndpInfoReqType<E> extends "query" | "mutation" ? QueryOrMutationFetchFn<E, SlceCls> : EndpInfoReqType<E> extends "message" ? MessageEmitFn<E, SlceCls> : never;
22
16
  type PrimaryFetchType<EInfoObj extends {
23
17
  [key: string]: EndpointInfo;
24
- }, Context> = {
25
- [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "query" | "mutation" | "message" ? K : never]: PrimaryFetchFn<EInfoObj[K], Context>;
18
+ }, SlceCls extends SliceCls | never> = {
19
+ [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "query" | "mutation" | "message" ? K : never]: PrimaryFetchFn<EInfoObj[K], SlceCls>;
26
20
  };
27
21
  type PubsubFetchType<EInfoObj extends {
28
22
  [key: string]: EndpointInfo;
29
- }, Context> = {
30
- [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "pubsub" ? K extends string ? `subscribe${Capitalize<K>}` : never : never]: PubsubSubscribeFn<EInfoObj[K], Context>;
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>;
31
25
  };
32
26
  type MessageListenFetchType<EInfoObj extends {
33
27
  [key: string]: EndpointInfo;
34
- }, Context> = {
35
- [K in keyof EInfoObj as EndpInfoReqType<EInfoObj[K]> extends "message" ? K extends string ? `listen${Capitalize<K>}` : never : never]: MessageListenFn<EInfoObj[K], Context>;
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>;
36
30
  };
37
31
  export type GetFetchTypeFromEndpoint<EndpCls extends EndpointCls, SlceCls extends SliceCls | never = never, _EndpointInfoObj extends {
38
32
  [key: string]: EndpointInfo;
39
- } = EndpCls[typeof ENDPOINT_META], _ReturnContext = EndpointReturnContextOf<SlceCls>> = PrimaryFetchType<_EndpointInfoObj, _ReturnContext> & PubsubFetchType<_EndpointInfoObj, _ReturnContext> & MessageListenFetchType<_EndpointInfoObj, _ReturnContext>;
33
+ } = EndpCls[typeof ENDPOINT_META]> = PrimaryFetchType<_EndpointInfoObj, SlceCls> & PubsubFetchType<_EndpointInfoObj, SlceCls> & MessageListenFetchType<_EndpointInfoObj, SlceCls>;
40
34
  export {};
@@ -2,67 +2,44 @@ import type { GetStateObject, SLICE_META } from "akanjs/base";
2
2
  import type { FetchPolicy } from "akanjs/common";
3
3
  import type { ConstantModel, DefaultOf, ProtoFile, PurifiedModel } from "akanjs/constant";
4
4
  import type { DatabaseModel, ExtractSort, FilterInstance } from "akanjs/document";
5
- import type { SliceCls, SliceInfo, SliceInfoArgs } from "akanjs/signal";
5
+ import type { SliceCls, SliceInfoArgs } from "akanjs/signal";
6
6
  import type { ClientEdit, ClientInit, ClientView, EditReturn, InitReturn, ViewReturn } from "./appliedReturn.type";
7
- type SliceInfoMap = {
8
- [key: string]: SliceInfo;
9
- };
10
- type LightWithId<Light> = Light extends {
7
+ type _SliceMap<S extends SliceCls> = S[typeof SLICE_META];
8
+ type _RefName<S extends SliceCls> = S["srv"]["cnst"]["refName"];
9
+ type _Cap<S extends SliceCls> = S["srv"]["cnst"]["_CapitalizedRefName"];
10
+ type _Input<S extends SliceCls> = S["srv"]["cnst"]["_Input"];
11
+ type _Full<S extends SliceCls> = S["srv"]["cnst"]["_Full"];
12
+ type _Light<S extends SliceCls> = S["srv"]["cnst"]["_Light"];
13
+ type _Insight<S extends SliceCls> = S["srv"]["cnst"]["_Insight"];
14
+ type _PurifiedInput<S extends SliceCls> = S["srv"]["cnst"]["_PurifiedInput"];
15
+ type _DefaultInput<S extends SliceCls> = S["srv"]["cnst"]["_DefaultInput"];
16
+ type _Filter<S extends SliceCls> = S["srv"]["db"]["_Filter"];
17
+ type _Sort<S extends SliceCls> = S["srv"]["db"]["_Sort"];
18
+ type _LightWithId<S extends SliceCls> = _Light<S> extends {
11
19
  id: string;
12
- } ? Light : {
20
+ } ? _Light<S> : {
13
21
  id: string;
14
22
  };
15
- export type SliceFetchDescriptor<SliceMap extends SliceInfoMap = SliceInfoMap, RefName extends string = string, CapRefName extends string = Capitalize<RefName>, Input = unknown, Full = unknown, Light = unknown, Insight = unknown, PurifiedInput = PurifiedModel<Input>, DefaultInput = DefaultOf<Input>, StateLight = GetStateObject<LightWithId<Light>>, StateInsight = GetStateObject<Insight>, Filter extends FilterInstance = FilterInstance, Sort = ExtractSort<Filter>> = {
16
- sliceMap: SliceMap;
17
- refName: RefName;
18
- capRefName: CapRefName;
19
- input: Input;
20
- full: Full;
21
- light: Light;
22
- insight: Insight;
23
- purifiedInput: PurifiedInput;
24
- defaultInput: DefaultInput;
25
- stateLight: StateLight;
26
- stateInsight: StateInsight;
27
- filter: Filter;
28
- sort: Sort;
29
- };
30
- export type SliceFetchDescriptorOf<SlceCls extends SliceCls> = SlceCls["srv"]["cnst"] extends ConstantModel ? SlceCls["srv"]["db"] extends DatabaseModel ? SliceFetchDescriptor<SlceCls[typeof SLICE_META], SlceCls["srv"]["cnst"]["refName"], SlceCls["srv"]["cnst"]["_CapitalizedRefName"], SlceCls["srv"]["cnst"]["_Input"], SlceCls["srv"]["cnst"]["_Full"], SlceCls["srv"]["cnst"]["_Light"], SlceCls["srv"]["cnst"]["_Insight"], SlceCls["srv"]["cnst"]["_PurifiedInput"], SlceCls["srv"]["cnst"]["_DefaultInput"], GetStateObject<LightWithId<SlceCls["srv"]["cnst"]["_Light"]>>, GetStateObject<SlceCls["srv"]["cnst"]["_Insight"]>, SlceCls["srv"]["db"]["_Filter"], SlceCls["srv"]["db"]["_Sort"]> : never : never;
31
- type _SliceMap<D extends SliceFetchDescriptor> = D["sliceMap"];
32
- type _RefName<D extends SliceFetchDescriptor> = D["refName"];
33
- type _Cap<D extends SliceFetchDescriptor> = D["capRefName"];
34
- type _Input<D extends SliceFetchDescriptor> = D["input"];
35
- type _Full<D extends SliceFetchDescriptor> = D["full"];
36
- type _Light<D extends SliceFetchDescriptor> = D["light"];
37
- type _Insight<D extends SliceFetchDescriptor> = D["insight"];
38
- type _PurifiedInput<D extends SliceFetchDescriptor> = D["purifiedInput"];
39
- type _DefaultInput<D extends SliceFetchDescriptor> = D["defaultInput"];
40
- type _StateLight<D extends SliceFetchDescriptor> = D["stateLight"];
41
- type _StateInsight<D extends SliceFetchDescriptor> = D["stateInsight"];
42
- type _Filter<D extends SliceFetchDescriptor> = D["filter"];
43
- type _Sort<D extends SliceFetchDescriptor> = D["sort"];
44
- type _LightWithId<D extends SliceFetchDescriptor> = LightWithId<_Light<D>>;
45
- type _SliceFetchInitOption<D extends SliceFetchDescriptor> = FetchInitOption<_Input<D>, _Filter<D>, _DefaultInput<D>, _Sort<D>>;
46
- type SliceListFetch<D extends SliceFetchDescriptor> = {
47
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `${_RefName<D>}List${Capitalize<Suffix>}` : never]: (...args: [
48
- ...SliceInfoArgs<_SliceMap<D>[Suffix]>,
23
+ type _SliceFetchInitOption<S extends SliceCls> = FetchInitOption<_Input<S>, _Filter<S>, _DefaultInput<S>, _Sort<S>>;
24
+ type SliceListFetch<S extends SliceCls> = {
25
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `${_RefName<S>}List${Capitalize<Suffix>}` : never]: (...args: [
26
+ ...SliceInfoArgs<_SliceMap<S>[Suffix]>,
49
27
  skip?: number | null,
50
28
  limit?: number | null,
51
- sort?: _Sort<D> | null,
29
+ sort?: _Sort<S> | null,
52
30
  fetchPolicy?: FetchPolicy
53
- ]) => Promise<_Light<D>[]>;
31
+ ]) => Promise<_Light<S>[]>;
54
32
  };
55
- type SliceInsightFetch<D extends SliceFetchDescriptor> = {
56
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `${_RefName<D>}Insight${Capitalize<Suffix>}` : never]: (...args: [...SliceInfoArgs<_SliceMap<D>[Suffix]>, fetchPolicy?: FetchPolicy]) => Promise<_Insight<D>>;
33
+ type SliceInsightFetch<S extends SliceCls> = {
34
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `${_RefName<S>}Insight${Capitalize<Suffix>}` : never]: (...args: [...SliceInfoArgs<_SliceMap<S>[Suffix]>, fetchPolicy?: FetchPolicy]) => Promise<_Insight<S>>;
57
35
  };
58
- type SliceInitFetch<D extends SliceFetchDescriptor> = {
59
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `init${_Cap<D>}${Capitalize<Suffix>}` : never]: (...args: [...SliceInfoArgs<_SliceMap<D>[Suffix]>, option?: _SliceFetchInitOption<D>]) => Promise<InitReturn<_RefName<D>, Suffix & string, _Light<D>, _Insight<D>, SliceInfoArgs<_SliceMap<D>[Suffix]>, _Filter<D>, _Cap<D>, Suffix extends string ? Capitalize<Suffix> : never, _LightWithId<D>, _StateLight<D>, _StateInsight<D>, _Sort<D>>>;
36
+ type SliceInitFetch<S extends SliceCls> = {
37
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `init${_Cap<S>}${Capitalize<Suffix>}` : never]: (...args: [...SliceInfoArgs<_SliceMap<S>[Suffix]>, option?: _SliceFetchInitOption<S>]) => Promise<InitReturn<_RefName<S>, Suffix & string, _Light<S>, _Insight<S>, SliceInfoArgs<_SliceMap<S>[Suffix]>, _Filter<S>, _Cap<S>, Suffix extends string ? Capitalize<Suffix> : never, _LightWithId<S>, GetStateObject<_LightWithId<S>>, GetStateObject<_Insight<S>>, _Sort<S>>>;
60
38
  };
61
- type SliceGetInitFetch<D extends SliceFetchDescriptor> = {
62
- [Suffix in keyof _SliceMap<D> as Suffix extends string ? `get${_Cap<D>}Init${Capitalize<Suffix>}` : never]: (...args: [...SliceInfoArgs<_SliceMap<D>[Suffix]>, option?: _SliceFetchInitOption<D>]) => ClientInit<_RefName<D>, _Light<D>, _Insight<D>, SliceInfoArgs<_SliceMap<D>[Suffix]>, _Filter<D>, _Cap<D>, _StateLight<D>, _StateInsight<D>, _Sort<D>>;
39
+ type SliceGetInitFetch<S extends SliceCls> = {
40
+ [Suffix in keyof _SliceMap<S> as Suffix extends string ? `get${_Cap<S>}Init${Capitalize<Suffix>}` : never]: (...args: [...SliceInfoArgs<_SliceMap<S>[Suffix]>, option?: _SliceFetchInitOption<S>]) => ClientInit<_RefName<S>, _Light<S>, _Insight<S>, SliceInfoArgs<_SliceMap<S>[Suffix]>, _Filter<S>, _Cap<S>, GetStateObject<_LightWithId<S>>, GetStateObject<_Insight<S>>, _Sort<S>>;
63
41
  };
64
- export type GetFetchTypeFromSliceDescriptor<Descriptor> = [Descriptor] extends [SliceFetchDescriptor] ? SliceListFetch<Descriptor> & SliceInsightFetch<Descriptor> & SliceInitFetch<Descriptor> & SliceGetInitFetch<Descriptor> & RawBaseSliceFetchType<_RefName<Descriptor>, _Input<Descriptor>, _Full<Descriptor>, _Light<Descriptor>, _Cap<Descriptor>, _PurifiedInput<Descriptor>> & AppliedBaseSliceFetchType<_RefName<Descriptor>, _Input<Descriptor>, _Full<Descriptor>, _Cap<Descriptor>, _PurifiedInput<Descriptor>> : never;
65
- export type GetFetchTypeFromSlice<SlceCls extends SliceCls> = GetFetchTypeFromSliceDescriptor<SliceFetchDescriptorOf<SlceCls>>;
42
+ export type GetFetchTypeFromSlice<SlceCls extends SliceCls> = SlceCls["srv"]["cnst"] extends ConstantModel ? SlceCls["srv"]["db"] extends DatabaseModel ? SliceListFetch<SlceCls> & SliceInsightFetch<SlceCls> & SliceInitFetch<SlceCls> & SliceGetInitFetch<SlceCls> & RawBaseSliceFetchType<_RefName<SlceCls>, _Input<SlceCls>, _Full<SlceCls>, _Light<SlceCls>, _Cap<SlceCls>, _PurifiedInput<SlceCls>> & AppliedBaseSliceFetchType<_RefName<SlceCls>, _Input<SlceCls>, _Full<SlceCls>, _Cap<SlceCls>, _PurifiedInput<SlceCls>> : never : never;
66
43
  type RawBaseSliceFetchType<RefName extends string, Input, Full, Light, _CapitalizedRefName extends string = Capitalize<RefName>, _PurifiedInput = PurifiedModel<Input>> = {
67
44
  [K in RefName]: (id: string, fetchPolicy?: FetchPolicy) => Promise<Full>;
68
45
  } & {
@@ -1,4 +1,4 @@
1
- import { type Assign, type Environment, type SLICE_META } from "akanjs/base";
1
+ import { type Environment, type SLICE_META } from "akanjs/base";
2
2
  import type { DatabaseSignal } from "akanjs/signal";
3
3
  import type { SliceMeta } from "./fetchType/appliedReturn.type";
4
4
  /** Account data made available to services for the current app/environment. */
@@ -9,8 +9,8 @@ export type Account<AddData = unknown> = {
9
9
  export declare const getDefaultAccount: () => Account;
10
10
  type GetSliceMetaObjFromSignal<Signal> = Signal extends {
11
11
  _SliceMetaObj: infer SliceMetaObj extends Record<string, SliceMeta>;
12
- } ? SliceMetaObj : Signal extends DatabaseSignal<infer _IntlCls, infer _EndpCls, infer SlceCls, infer _SrvrCls> ? {
13
- [K in `${SlceCls["baseName"]}${Capitalize<keyof SlceCls[typeof SLICE_META] & string>}`]: SliceMeta;
12
+ } ? SliceMetaObj : Signal extends DatabaseSignal ? {
13
+ [K in `${Signal["slice"]["baseName"]}${Capitalize<keyof Signal["slice"][typeof SLICE_META] & string>}`]: SliceMeta;
14
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, Assign<Acc, GetSliceMetaObjFromSignal<First>>> : Acc;
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;
16
16
  export {};
@@ -3,7 +3,9 @@ export interface InfiniteScrollProps {
3
3
  currentPage: number;
4
4
  itemsPerPage: number;
5
5
  onAddPage: (page: number) => Promise<void>;
6
- onPageSelect: (page: number) => void;
6
+ onPageSelect: (page: number, option?: {
7
+ scrollToTop?: boolean;
8
+ }) => void;
7
9
  children: React.ReactNode;
8
10
  reverse?: boolean;
9
11
  }
@@ -3,7 +3,9 @@ interface MoreProps {
3
3
  itemsPerPage: number;
4
4
  currentPage: number;
5
5
  onAddPage: (page: number) => Promise<void>;
6
- onPageSelect: (page: number) => void;
6
+ onPageSelect: (page: number, option?: {
7
+ scrollToTop?: boolean;
8
+ }) => void;
7
9
  children?: React.ReactNode;
8
10
  className?: string;
9
11
  reverse?: boolean;
@@ -7,7 +7,7 @@ export interface InfiniteScrollProps {
7
7
  currentPage: number;
8
8
  itemsPerPage: number;
9
9
  onAddPage: (page: number) => Promise<void>;
10
- onPageSelect: (page: number) => void;
10
+ onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => void;
11
11
  children: React.ReactNode;
12
12
  reverse?: boolean;
13
13
  }
@@ -22,6 +22,7 @@ export const InfiniteScroll = ({
22
22
  reverse,
23
23
  }: InfiniteScrollProps) => {
24
24
  const [isFetching, setIsFetching] = useState(false);
25
+ const isFetchingRef = useRef(false);
25
26
  const target = useRef<HTMLDivElement>(null);
26
27
  const page = useRef<number>(currentPage);
27
28
  const totalPages = Math.ceil(total / (itemsPerPage || 1));
@@ -38,14 +39,39 @@ export const InfiniteScroll = ({
38
39
  }, []);
39
40
 
40
41
  const fetchMoreItems = async () => {
41
- if (isFetching) return;
42
+ if (isFetchingRef.current) return;
42
43
  const nextPage = page.current + 1;
43
44
  if (nextPage > totalPages) return;
45
+
46
+ const scroller = reverse ? document.scrollingElement : null;
47
+ const prevScrollHeight = scroller?.scrollHeight ?? 0;
48
+ const prevScrollTop = scroller?.scrollTop ?? 0;
49
+
50
+ isFetchingRef.current = true;
44
51
  setIsFetching(true);
45
- await onAddPage(nextPage);
46
- onPageSelect(nextPage);
47
- setIsFetching(false);
48
- page.current = nextPage;
52
+ try {
53
+ await onAddPage(nextPage);
54
+ onPageSelect(nextPage, { scrollToTop: false });
55
+ page.current = nextPage;
56
+
57
+ const restoreScroll = () => {
58
+ if (scroller) {
59
+ scroller.scrollTop = prevScrollTop + (scroller.scrollHeight - prevScrollHeight);
60
+ }
61
+ isFetchingRef.current = false;
62
+ setIsFetching(false);
63
+ };
64
+
65
+ if (typeof requestAnimationFrame === "function") {
66
+ requestAnimationFrame(restoreScroll);
67
+ } else {
68
+ restoreScroll();
69
+ }
70
+ } catch (error) {
71
+ isFetchingRef.current = false;
72
+ setIsFetching(false);
73
+ throw error;
74
+ }
49
75
  };
50
76
 
51
77
  return (
package/ui/Load/Units.tsx CHANGED
@@ -156,11 +156,13 @@ function Render<RefName extends string, Light extends { id: string }>({
156
156
  onAddPage: async (page: number) => {
157
157
  await storeDo[namesOfSlice.addPageOfModel](page);
158
158
  },
159
- onPageSelect: (page: number) => {
159
+ onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => {
160
160
  void storeDo[namesOfSlice.setPageOfModel](page);
161
161
 
162
- window.parent.postMessage({ type: "pathChange", page }, "*");
163
- window.scrollTo({ top: 0, behavior: "instant" });
162
+ if (option?.scrollToTop !== false) {
163
+ window.parent.postMessage({ type: "pathChange", page }, "*");
164
+ window.scrollTo({ top: 0, behavior: "instant" });
165
+ }
164
166
  },
165
167
  reverse,
166
168
  };
@@ -279,7 +281,7 @@ interface MoreProps {
279
281
  itemsPerPage: number;
280
282
  currentPage: number;
281
283
  onAddPage: (page: number) => Promise<void>;
282
- onPageSelect: (page: number) => void;
284
+ onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => void;
283
285
  children?: React.ReactNode;
284
286
  className?: string;
285
287
  reverse?: boolean;
package/ui/More.tsx CHANGED
@@ -1,16 +1,15 @@
1
1
  "use client";
2
2
  import { clsx, isMobileDevice } from "akanjs/client";
3
- import { lazy } from "akanjs/webkit";
3
+ import { useEffect, useState } from "react";
4
+ import { InfiniteScroll } from "./InfiniteScroll";
4
5
  import { Pagination } from "./Pagination";
5
6
 
6
- const InfiniteScroll = lazy(() => import("./InfiniteScroll").then((mod) => mod.InfiniteScroll), { ssr: false });
7
-
8
7
  interface MoreProps {
9
8
  total: number;
10
9
  itemsPerPage: number;
11
10
  currentPage: number;
12
11
  onAddPage: (page: number) => Promise<void>;
13
- onPageSelect: (page: number) => void;
12
+ onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => void;
14
13
  children?: React.ReactNode;
15
14
  className?: string;
16
15
  reverse?: boolean;
@@ -26,11 +25,17 @@ export const More = ({
26
25
  className,
27
26
  reverse,
28
27
  }: MoreProps) => {
28
+ const [isMobile, setIsMobile] = useState(false);
29
+
30
+ useEffect(() => {
31
+ setIsMobile(isMobileDevice());
32
+ }, []);
33
+
29
34
  if (total <= itemsPerPage) {
30
35
  return <>{children}</>;
31
36
  }
32
37
 
33
- if (isMobileDevice()) {
38
+ if (isMobile) {
34
39
  return (
35
40
  <InfiniteScroll
36
41
  total={total}
package/webkit/lazy.tsx CHANGED
@@ -8,6 +8,12 @@ const isServer = typeof window === "undefined";
8
8
  type LazyOption = { ssr?: boolean; loading?: () => ReactNode };
9
9
  type LazyProps = Record<string, unknown>;
10
10
  type LoadedOf<Loaded> = Loaded extends { default: infer T } ? T : Loaded;
11
+ type LazyModule = { default: ComponentType<LazyProps> };
12
+
13
+ const normalizeLazyModule = <Loaded,>(loaded: Loaded): LazyModule => {
14
+ if (loaded && typeof loaded === "object" && "default" in loaded) return loaded as LazyModule;
15
+ return { default: loaded as ComponentType<LazyProps> };
16
+ };
11
17
 
12
18
  /** React lazy wrapper with Akan's `ssr: false` server stub and client mount gate. */
13
19
  export const lazy = <Loaded,>(loader: () => Promise<Loaded>, option?: LazyOption): LoadedOf<Loaded> => {
@@ -18,7 +24,7 @@ export const lazy = <Loaded,>(loader: () => Promise<Loaded>, option?: LazyOption
18
24
  Stub.displayName = "LazySsrFalseStub";
19
25
  return Stub as unknown as LoadedOf<Loaded>;
20
26
  }
21
- const LazyInner = reactLazy(loader as () => Promise<{ default: ComponentType<LazyProps> }>);
27
+ const LazyInner = reactLazy(async () => normalizeLazyModule(await loader()));
22
28
 
23
29
  if (!ssrFalse) {
24
30
  const Wrapper = forwardRef<unknown, LazyProps>((props, ref) => <LazyInner {...props} ref={ref as never} />);