akanjs 2.3.5-rc.1 → 2.3.5-rc.10
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 +44 -13
- package/README.md +43 -12
- package/fetch/fetchType/buildFetch.type.ts +24 -11
- package/fetch/fetchType/endpointFetch.type.ts +37 -51
- package/fetch/fetchType/sliceFetch.type.ts +72 -132
- package/fetch/types.ts +4 -4
- package/package.json +1 -1
- package/types/fetch/fetchType/buildFetch.type.d.ts +20 -7
- package/types/fetch/fetchType/endpointFetch.type.d.ts +16 -22
- package/types/fetch/fetchType/sliceFetch.type.d.ts +27 -50
- package/types/fetch/types.d.ts +4 -4
- package/types/ui/InfiniteScroll.d.ts +3 -1
- package/types/ui/More.d.ts +3 -1
- package/ui/InfiniteScroll.tsx +32 -6
- package/ui/Load/Units.tsx +6 -4
- package/ui/More.tsx +10 -5
- package/webkit/lazy.tsx +7 -1
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
|
-
**
|
|
5
|
+
**한 줄의 비즈니스 코드로 웹·iOS·Android·서버·DB를 한 번에.**
|
|
6
|
+
**One line of business code ships web, iOS, Android, server, and database together.**
|
|
7
7
|
|
|
8
|
-
Akan.js는 비즈니스
|
|
9
|
-
|
|
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
|
-
|
|
20
|
-
|
|
18
|
+
Akan은 기술 계층을 손으로 배선하는 대신 비즈니스를 먼저 출시하도록 돕습니다. 도메인을 한 번
|
|
19
|
+
정의하면 schema, API, fetch client, store, UI, server, app packaging, deployment artifact,
|
|
20
|
+
generated reference가 같은 의도에서 이어집니다.
|
|
21
21
|
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
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
|
-
-
|
|
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
|
-
**
|
|
5
|
+
**One line of business code ships web, iOS, Android, server, and database together.**
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
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
|
|
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`.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
22
|
+
: Signal extends { endpoint: infer EndpCls extends EndpointCls; slice: infer SlceCls extends SliceCls }
|
|
24
23
|
? GetFetchTypeFromEndpoint<EndpCls, SlceCls> & GetFetchTypeFromSlice<SlceCls>
|
|
25
|
-
: Signal extends
|
|
24
|
+
: Signal extends { endpoint: infer EndpCls extends EndpointCls }
|
|
26
25
|
? GetFetchTypeFromEndpoint<EndpCls>
|
|
27
26
|
: unknown;
|
|
28
27
|
|
|
29
|
-
type
|
|
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
|
-
|
|
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
|
-
?
|
|
35
|
-
|
|
36
|
-
|
|
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,
|
|
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
|
-
?
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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,
|
|
23
|
+
type EndpointClientReturns<E, SlceCls extends SliceCls | never> = [SlceCls] extends [never]
|
|
42
24
|
? EndpInfoClientReturns<E>
|
|
43
|
-
: ExtendedEndpointReturn<
|
|
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,
|
|
46
|
-
| EndpointClientReturns<E,
|
|
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,
|
|
36
|
+
type QueryOrMutationFetchFn<E, SlceCls extends SliceCls | never> = (
|
|
50
37
|
...args: [...EndpInfoArgs<E>, fetchPolicy?: FetchPolicy]
|
|
51
|
-
) => Promise<EndpInfoReturns<E,
|
|
38
|
+
) => Promise<EndpInfoReturns<E, SlceCls>>;
|
|
52
39
|
|
|
53
|
-
type MessageEmitFn<E,
|
|
40
|
+
type MessageEmitFn<E, SlceCls extends SliceCls | never> = (...args: EndpInfoArgs<E>) => EndpInfoReturns<E, SlceCls>;
|
|
54
41
|
|
|
55
|
-
type MessageListenFn<E,
|
|
56
|
-
handleEvent: (data: EndpInfoReturns<E,
|
|
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,
|
|
47
|
+
type PubsubSubscribeFn<E, SlceCls extends SliceCls | never> = (
|
|
61
48
|
...args: [
|
|
62
49
|
...EndpInfoArgs<E>,
|
|
63
|
-
handleEvent: (data: EndpInfoReturns<E,
|
|
50
|
+
handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
|
|
64
51
|
options?: FetchPolicy,
|
|
65
52
|
]
|
|
66
53
|
) => () => void;
|
|
67
54
|
|
|
68
|
-
type PrimaryFetchFn<E,
|
|
55
|
+
type PrimaryFetchFn<E, SlceCls extends SliceCls | never> =
|
|
69
56
|
EndpInfoReqType<E> extends "query" | "mutation"
|
|
70
|
-
? QueryOrMutationFetchFn<E,
|
|
57
|
+
? QueryOrMutationFetchFn<E, SlceCls>
|
|
71
58
|
: EndpInfoReqType<E> extends "message"
|
|
72
|
-
? MessageEmitFn<E,
|
|
59
|
+
? MessageEmitFn<E, SlceCls>
|
|
73
60
|
: never;
|
|
74
61
|
|
|
75
|
-
type PrimaryFetchType<EInfoObj extends { [key: string]: EndpointInfo },
|
|
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],
|
|
65
|
+
: never]: PrimaryFetchFn<EInfoObj[K], SlceCls>;
|
|
79
66
|
};
|
|
80
67
|
|
|
81
|
-
type PubsubFetchType<EInfoObj extends { [key: string]: EndpointInfo },
|
|
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],
|
|
73
|
+
: never]: PubsubSubscribeFn<EInfoObj[K], SlceCls>;
|
|
87
74
|
};
|
|
88
75
|
|
|
89
|
-
type MessageListenFetchType<EInfoObj extends { [key: string]: EndpointInfo },
|
|
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],
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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,
|
|
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
|
|
9
|
-
type
|
|
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
|
-
|
|
12
|
-
|
|
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<
|
|
27
|
+
...SliceInfoArgs<_SliceMap<S>[Suffix]>,
|
|
86
28
|
skip?: number | null,
|
|
87
29
|
limit?: number | null,
|
|
88
|
-
sort?: _Sort<
|
|
30
|
+
sort?: _Sort<S> | null,
|
|
89
31
|
fetchPolicy?: FetchPolicy,
|
|
90
32
|
]
|
|
91
|
-
) => Promise<_Light<
|
|
33
|
+
) => Promise<_Light<S>[]>;
|
|
92
34
|
};
|
|
93
35
|
|
|
94
|
-
type SliceInsightFetch<
|
|
95
|
-
[Suffix in keyof _SliceMap<
|
|
96
|
-
...args: [...SliceInfoArgs<_SliceMap<
|
|
97
|
-
) => Promise<_Insight<
|
|
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<
|
|
101
|
-
[Suffix in keyof _SliceMap<
|
|
102
|
-
...args: [...SliceInfoArgs<_SliceMap<
|
|
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<
|
|
47
|
+
_RefName<S>,
|
|
106
48
|
Suffix & string,
|
|
107
|
-
_Light<
|
|
108
|
-
_Insight<
|
|
109
|
-
SliceInfoArgs<_SliceMap<
|
|
110
|
-
_Filter<
|
|
111
|
-
_Cap<
|
|
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<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
_Sort<
|
|
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<
|
|
122
|
-
[Suffix in keyof _SliceMap<
|
|
123
|
-
...args: [...SliceInfoArgs<_SliceMap<
|
|
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<
|
|
126
|
-
_Light<
|
|
127
|
-
_Insight<
|
|
128
|
-
SliceInfoArgs<_SliceMap<
|
|
129
|
-
_Filter<
|
|
130
|
-
_Cap<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
_Sort<
|
|
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
|
|
138
|
-
?
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
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
|
|
19
|
+
: Signal extends DatabaseSignal
|
|
20
20
|
? {
|
|
21
|
-
[K in `${
|
|
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,
|
|
28
|
+
? GetSliceMetaObjFromDatabaseSignals<Rest, Acc & GetSliceMetaObjFromSignal<First>>
|
|
29
29
|
: Acc;
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
18
|
-
|
|
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
|
-
} ?
|
|
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,
|
|
24
|
-
export type MergeAllFetchTypes<Signals extends readonly FetchSignalInput[]> = MergeProxyFetchTypes<Signals> extends infer ProxyFetch ? MergeDirectFetchTypes<Signals> extends infer DirectFetch ?
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type
|
|
10
|
-
type
|
|
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,
|
|
12
|
+
handleEvent: (data: EndpInfoReturns<E, SlceCls>) => PromiseOrObject<void>,
|
|
19
13
|
options?: FetchPolicy
|
|
20
14
|
]) => () => void;
|
|
21
|
-
type PrimaryFetchFn<E,
|
|
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
|
-
},
|
|
25
|
-
[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>;
|
|
26
20
|
};
|
|
27
21
|
type PubsubFetchType<EInfoObj extends {
|
|
28
22
|
[key: string]: EndpointInfo;
|
|
29
|
-
},
|
|
30
|
-
[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>;
|
|
31
25
|
};
|
|
32
26
|
type MessageListenFetchType<EInfoObj extends {
|
|
33
27
|
[key: string]: EndpointInfo;
|
|
34
|
-
},
|
|
35
|
-
[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>;
|
|
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]
|
|
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,
|
|
5
|
+
import type { SliceCls, SliceInfoArgs } from "akanjs/signal";
|
|
6
6
|
import type { ClientEdit, ClientInit, ClientView, EditReturn, InitReturn, ViewReturn } from "./appliedReturn.type";
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type
|
|
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
|
-
} ?
|
|
20
|
+
} ? _Light<S> : {
|
|
13
21
|
id: string;
|
|
14
22
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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<
|
|
29
|
+
sort?: _Sort<S> | null,
|
|
52
30
|
fetchPolicy?: FetchPolicy
|
|
53
|
-
]) => Promise<_Light<
|
|
31
|
+
]) => Promise<_Light<S>[]>;
|
|
54
32
|
};
|
|
55
|
-
type SliceInsightFetch<
|
|
56
|
-
[Suffix in keyof _SliceMap<
|
|
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<
|
|
59
|
-
[Suffix in keyof _SliceMap<
|
|
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<
|
|
62
|
-
[Suffix in keyof _SliceMap<
|
|
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
|
|
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
|
} & {
|
package/types/fetch/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
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
|
|
13
|
-
[K in `${
|
|
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,
|
|
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
|
|
6
|
+
onPageSelect: (page: number, option?: {
|
|
7
|
+
scrollToTop?: boolean;
|
|
8
|
+
}) => void;
|
|
7
9
|
children: React.ReactNode;
|
|
8
10
|
reverse?: boolean;
|
|
9
11
|
}
|
package/types/ui/More.d.ts
CHANGED
|
@@ -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
|
|
6
|
+
onPageSelect: (page: number, option?: {
|
|
7
|
+
scrollToTop?: boolean;
|
|
8
|
+
}) => void;
|
|
7
9
|
children?: React.ReactNode;
|
|
8
10
|
className?: string;
|
|
9
11
|
reverse?: boolean;
|
package/ui/InfiniteScroll.tsx
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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 {
|
|
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 (
|
|
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(
|
|
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} />);
|