@flowlist/js-core 2.3.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,114 +1,86 @@
1
1
  # @flowlist/js-core
2
2
 
3
- <p align="center">
4
- <a target="_blank" href="https://travis-ci.org/github/flowlist/js-core">
5
- <img alt="Build" src="https://travis-ci.org/flowlist/js-core.svg?branch=master" />
6
- </a>
7
- <a target="_blank" href="https://codecov.io/gh/flowlist/js-core">
8
- <img alt="Coverage" src="https://codecov.io/gh/flowlist/js-core/branch/master/graph/badge.svg" />
9
- </a>
10
- <a target="_blank" href="https://www.npmjs.com/package/@flowlist/js-core">
11
- <img alt="Version" src="https://badge.fury.io/js/%40flowlist%2Fjs-core.svg" />
12
- </a>
13
- <a target="_blank" href="https://github.com/flowlist/js-core/blob/master/LICENSE">
14
- <img alt="License" src="https://gitlicense.com/badge/flowlist/js-core"/>
15
- </a>
16
- </p>
3
+ [![npm version](https://img.shields.io/npm/v/@flowlist/js-core.svg)](https://www.npmjs.com/package/@flowlist/js-core)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
17
5
 
18
- ## 信息流业务通用容器的基础实现
6
+ A lightweight, type-safe JavaScript core library for managing data flow in list-based UIs. It provides a unified interface for initializing, loading, paginating, and updating list data with built-in state management and caching support.
19
7
 
20
- ### Download
8
+ ## ✨ Features
21
9
 
22
- ``` bash
23
- yarn add @flowlist/js-core
24
- ```
25
-
26
- ### Import
27
- ```javascript
28
- import flow from '@flowlist/js-core'
29
- ```
30
-
31
- ### Inject
32
-
33
- - getter:get state
34
- > 如 [Vuex.getter](https://github.com/flowlist/js-core/blob/master/tests/unit/env.js#L3)
35
-
36
- - setter:set state
37
- > 如:[React.setState](https://github.com/flowlist/js-core/blob/master/tests/unit/env.js#L8)
38
-
39
- - cache:cache instance
40
- > [包含 get<Promise>,set<Promise>,del 三个方法](https://github.com/flowlist/js-core/blob/master/tests/unit/env.js#L20)
41
-
42
- - api:named api list
43
- > [把需要调用的所有 API export 出来](https://github.com/flowlist/js-core/blob/master/tests/unit/api.js)
10
+ - 🧠 **Declarative Data Flow**: Define how your list data is fetched and updated.
11
+ - 📦 **Built-in State Management**: Handles loading, error, pagination, and cache states automatically.
12
+ - 🔁 **Multiple Fetch Strategies**: Supports pagination, infinite scroll, since-id, seen-ids, and more.
13
+ - 🔄 **Flexible Updates**: Add, remove, update, or merge list items with ease.
14
+ - 💾 **Cache Ready**: Designed to work seamlessly with external cache layers.
15
+ - 🧪 **TypeScript First**: Full TypeScript support with comprehensive type definitions.
16
+ - 🧩 **Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JS.
44
17
 
45
- ### Methods
18
+ ## 🚀 Installation
46
19
 
47
- - `initState`
20
+ ```bash
21
+ # Using npm
22
+ npm install @flowlist/js-core
48
23
 
49
- > 初始化数据容器
50
-
51
- | 参数 | 类型 | 介绍 |
52
- | --- | --- | --- |
53
- | getter | Function | 设置 state 的函数 |
54
- | setter | Function | 读取 state 的函数 |
55
- | func | String | API层的函数名 |
56
- | type | String\<ListType\> | 列表的类型 |
57
- | query | Object | 需要额外透传给 API 层的数据 |
58
-
59
- - `initData`
60
-
61
- > 加载首屏数据
62
-
63
- | 参数 | 类型 | 介绍 |
64
- | --- | --- | --- |
65
- | getter | Function | 设置 state 的函数 |
66
- | setter | Function | 读取 state 的函数 |
67
- | cache | Object | 用于读写缓存的对象 |
68
- | func | String | API层的函数名 |
69
- | type | String\<ListType\> | 列表的类型 |
70
- | query | Object | 需要额外透传给 API 层的数据 |
71
- | api | Array\<API\> | 整个 API 层 |
72
- | cacheTimeout | Integer | 缓存持久化的时间(秒) |
73
- | uniqueKey | String | 列表里每个元素独一无二的 key |
74
- | callback | Function | 请求成功之后的回调函数 |
75
-
76
- - `loadMore`
77
-
78
- > 加载分页数据
79
-
80
- | 参数 | 类型 | 介绍 |
81
- | --- | --- | --- |
82
- | getter | Function | 设置 state 的函数 |
83
- | setter | Function | 读取 state 的函数 |
84
- | cache | Object | 用于读写缓存的对象 |
85
- | func | String | API层的函数名 |
86
- | type | String\<ListType\> | 列表的类型 |
87
- | query | Object | 需要额外透传给 API 层的数据 |
88
- | api | Array\<API\> | 整个 API 层 |
89
- | cacheTimeout | Integer | 缓存持久化的时间(秒) |
90
- | uniqueKey | String | 列表里每个元素独一无二的 key |
91
- | errorRetry | Boolean | 是否是重试 |
92
- | callback | Function | 请求成功之后的回调函数 |
24
+ # Using yarn
25
+ yarn add @flowlist/js-core
26
+ ```
93
27
 
94
- - `updateState`
28
+ ## 📦 Usage
95
29
 
96
- > 更新数据容器
30
+ ```ts
31
+ import flow from '@flowlist/js-core'
97
32
 
98
- ```javascript
99
- flow.updateState({ getter, setter, cache, type, func, query, method, value, id, uniqueKey, changeKey, cacheTimeout })
33
+ // Example with a hypothetical state management system (e.g., React useState, Zustand, etc.)
34
+ const { getter, setter, api, cache } = yourAppStateManager
35
+
36
+ // 1. Initialize the data container
37
+ await flow.initState({
38
+ getter,
39
+ setter,
40
+ func: 'fetchPosts',
41
+ type: 'page', // or 'sinceId', 'seenIds', 'auto', etc.
42
+ query: { userId: 123 }
43
+ })
44
+
45
+ // 2. Load initial data
46
+ await flow.initData({
47
+ getter,
48
+ setter,
49
+ cache,
50
+ func: 'fetchPosts',
51
+ type: 'page',
52
+ query: { userId: 123 },
53
+ api,
54
+ uniqueKey: 'id',
55
+ cacheTimeout: 300 // 5 minutes
56
+ })
57
+
58
+ // 3. Load more data (e.g., on scroll)
59
+ await flow.loadMore({
60
+ getter,
61
+ setter,
62
+ cache,
63
+ func: 'fetchPosts',
64
+ type: 'page',
65
+ query: { userId: 123 },
66
+ api,
67
+ uniqueKey: 'id',
68
+ errorRetry: false
69
+ })
70
+
71
+ // 4. Update list data locally
72
+ await flow.updateState({
73
+ getter,
74
+ setter,
75
+ cache,
76
+ func: 'fetchPosts',
77
+ type: 'page',
78
+ query: { userId: 123 },
79
+ method: 'push', // or 'delete', 'update', 'merge', etc.
80
+ value: newPost,
81
+ id: newPost.id,
82
+ uniqueKey: 'id',
83
+ changeKey: 'result',
84
+ cacheTimeout: 300
85
+ })
100
86
  ```
101
- | 参数 | 类型 | 介绍 |
102
- | --- | --- | --- |
103
- | getter | Function | 设置 state 的函数 |
104
- | setter | Function | 读取 state 的函数 |
105
- | cache | Object | 用于读写缓存的对象 |
106
- | func | String | API层的函数名 |
107
- | type | String\<ListType\> | 列表的类型 |
108
- | query | Object | 需要额外透传给 API 层的数据 |
109
- | method | String | 需要调用的函数名 |
110
- | value | Any | 传值 |
111
- | id | String / Number | 用来索引的独一无二的 keyValue |
112
- | uniqueKey | String | 列表里每个元素独一无二的 keyName |
113
- | changeKey | String | 你想要修改的`field`是哪个字段,默认是`result` |
114
- | cacheTimeout | Integer | 缓存持久化的时间(秒) |
@@ -0,0 +1,263 @@
1
+ /**
2
+ * 对象的唯一标识符类型
3
+ */
4
+ type ObjectKey = string | number;
5
+ /**
6
+ * 通用键值对映射(注意:尽管 ObjectKey 包含 number,
7
+ * 但在实际对象中,number 键会被转为 string。
8
+ * 此处使用 string 以符合 Record 的实际行为 [[3]])
9
+ */
10
+ type KeyMap = Record<string, unknown>;
11
+ /**
12
+ * 形态数组
13
+ */
14
+ type MorphArray = unknown[];
15
+ /**
16
+ * 数据源类型:可以是 API 路径字符串,或返回 Promise 的函数
17
+ */
18
+ type DataSource = string | ((params: unknown) => Promise<unknown>);
19
+ /**
20
+ * 字段获取器:根据字段名获取状态对象
21
+ */
22
+ type FieldGetter<T = unknown, E = unknown> = (key: string) => DefaultField<T, E> | undefined;
23
+ /**
24
+ * 状态设置器函数类型
25
+ */
26
+ type FieldSetter = (obj: SetterFuncParams) => void;
27
+ /**
28
+ * 获取数据后的回调函数
29
+ */
30
+ type FetchResultCallback = (obj: {
31
+ params: GenerateParamsResp;
32
+ data: unknown;
33
+ refresh: boolean;
34
+ }) => void;
35
+ /**
36
+ * 数据获取类型枚举
37
+ */
38
+ type FetchType = 'jump' | 'sinceId' | 'page' | 'seenIds' | 'auto';
39
+ /**
40
+ * 字段数据的关键字
41
+ */
42
+ type FieldKeys = 'result' | 'noMore' | 'nothing' | 'loading' | 'error' | 'extra' | 'fetched' | 'page' | 'total';
43
+ /**
44
+ * 生成字段名称的参数
45
+ */
46
+ interface GenerateFieldProps {
47
+ func: DataSource;
48
+ type: FetchType;
49
+ query?: KeyMap;
50
+ }
51
+ /**
52
+ * 生成请求参数的输入
53
+ */
54
+ interface GenerateParamsType {
55
+ field: DefaultField<unknown, unknown>;
56
+ uniqueKey?: string;
57
+ query?: KeyMap;
58
+ type: FetchType;
59
+ }
60
+ /**
61
+ * 生成请求参数的输出
62
+ */
63
+ interface GenerateParamsResp {
64
+ seen_ids?: string;
65
+ since_id?: ObjectKey;
66
+ is_up?: 0 | 1;
67
+ page?: number;
68
+ }
69
+ /**
70
+ * Setter 函数的参数
71
+ */
72
+ interface SetterFuncParams {
73
+ key: string;
74
+ type: number;
75
+ value: unknown;
76
+ callback?: (obj?: KeyMap) => void;
77
+ }
78
+ /**
79
+ * 默认字段数据结构,使用泛型 T 和 E 分别代表 result 和 extra 的类型
80
+ */
81
+ interface DefaultField<T = unknown, E = unknown> {
82
+ result: T;
83
+ noMore: boolean;
84
+ nothing: boolean;
85
+ loading: boolean;
86
+ error: null | Error;
87
+ extra: E;
88
+ fetched: boolean;
89
+ page: number;
90
+ total: number;
91
+ }
92
+ /**
93
+ * API 响应结构,使用泛型 T 和 E
94
+ */
95
+ interface ApiResponse<T = unknown, E = unknown> {
96
+ result: T;
97
+ extra?: E;
98
+ total?: number;
99
+ no_more?: boolean;
100
+ }
101
+ interface BaseFetchConfig {
102
+ getter: FieldGetter;
103
+ setter: FieldSetter;
104
+ func: DataSource;
105
+ type: FetchType;
106
+ query?: KeyMap;
107
+ api?: KeyMap;
108
+ uniqueKey?: string;
109
+ callback?: FetchResultCallback;
110
+ }
111
+ /**
112
+ * 初始化状态的参数
113
+ */
114
+ interface InitStateType {
115
+ getter: FieldGetter;
116
+ setter: FieldSetter;
117
+ func: DataSource;
118
+ type: FetchType;
119
+ query?: KeyMap;
120
+ opts?: Partial<DefaultField<unknown, unknown>>;
121
+ }
122
+ /**
123
+ * 初始化数据的参数
124
+ */
125
+ type InitDataType = BaseFetchConfig;
126
+ /**
127
+ * 加载更多的参数
128
+ */
129
+ interface LoadMoreType extends BaseFetchConfig {
130
+ errorRetry?: boolean;
131
+ }
132
+ /**
133
+ * 更新状态的参数
134
+ */
135
+ interface UpdateStateType<T = unknown, E = unknown> {
136
+ getter: FieldGetter<T, E>;
137
+ setter: FieldSetter;
138
+ func: DataSource;
139
+ type: FetchType;
140
+ query?: KeyMap;
141
+ method: string;
142
+ value: ResultArrayType$1 | Record<ObjectKey, KeyMap>;
143
+ id?: string | number | ObjectKey[];
144
+ changeKey?: string;
145
+ uniqueKey?: string;
146
+ }
147
+ /**
148
+ * 设置数据的参数
149
+ */
150
+ interface SetDataType<T = unknown, E = unknown> {
151
+ getter: FieldGetter<T, E>;
152
+ setter: FieldSetter;
153
+ data: T | ApiResponse<T, E>;
154
+ fieldName: string;
155
+ type: FetchType;
156
+ page: number;
157
+ insertBefore: boolean;
158
+ }
159
+ /**
160
+ * 设置错误的参数
161
+ */
162
+ interface SetErrorType {
163
+ setter: FieldSetter;
164
+ fieldName: string;
165
+ error: Error;
166
+ }
167
+ type ResultArrayType$1 = KeyMap[];
168
+ type ResultObjectType$1 = Record<ObjectKey, KeyMap[]>;
169
+
170
+ declare const initState: ({ getter, setter, func, type, query, opts }: InitStateType) => Promise<void>;
171
+ declare const initData: ({ getter, setter, func, type, query, api, uniqueKey, callback }: InitDataType) => Promise<void>;
172
+ declare const loadMore: ({ getter, setter, query, type, func, api, uniqueKey, errorRetry, callback }: LoadMoreType) => Promise<void>;
173
+ declare const updateState: ({ getter, setter, func, type, query, method, value, id, uniqueKey, changeKey }: UpdateStateType) => Promise<unknown>;
174
+
175
+ /**
176
+ * 判断数据是否为对象结果(即没有 result 字段)
177
+ * 注意:这里我们假设 "对象结果" 指的是一个普通的对象,而不是 ApiResponse。
178
+ */
179
+ declare const isObjectResult: (data: unknown) => data is Record<string, unknown>;
180
+ /**
181
+ * 生成默认字段
182
+ */
183
+ declare const generateDefaultField: <T = unknown, E = unknown>(opts?: Partial<DefaultField<T, E>>) => DefaultField<T, E>;
184
+ /**
185
+ * 根据参数生成 field 的 namespace
186
+ */
187
+ declare const generateFieldName: ({ func, type, query }: GenerateFieldProps) => string;
188
+ /**
189
+ * 根据 key 从 object 里拿 value
190
+ */
191
+ declare const getObjectDeepValue: (field: unknown, keys?: string | string[]) => unknown;
192
+ /**
193
+ * 安全地更新对象的深层值
194
+ */
195
+ declare const updateObjectDeepValue: (field: Record<string, unknown>, changeKey: string, value: unknown) => void;
196
+ type ResultArrayType = KeyMap[];
197
+ type ResultObjectType = Record<ObjectKey, KeyMap[]>;
198
+ declare const searchValueByKey: (result: ResultArrayType | ResultObjectType, id: ObjectKey, key: string) => unknown;
199
+ declare const computeMatchedItemIndex: (itemId: ObjectKey, fieldArr: ResultArrayType, changingKey: string) => number;
200
+ declare const combineArrayData: (fieldArray: ResultArrayType, value: ResultArrayType | Record<ObjectKey, KeyMap>, changingKey: string) => void;
201
+ /**
202
+ * 判断参数是否为数组
203
+ */
204
+ declare const isArray: (data: unknown) => data is unknown[];
205
+ /**
206
+ * 设置一个响应式的数据到对象上
207
+ */
208
+ declare const setReactivityField: <T, E>(field: DefaultField<T, E>, key: FieldKeys, value: unknown, type: FetchType, insertBefore: boolean) => void;
209
+ /**
210
+ * 计算一个数据列的长度
211
+ */
212
+ declare const computeResultLength: (data: unknown) => number;
213
+ declare const generateRequestParams: ({ field, uniqueKey, query, type }: GenerateParamsType) => GenerateParamsResp;
214
+
215
+ declare const utils_combineArrayData: typeof combineArrayData;
216
+ declare const utils_computeMatchedItemIndex: typeof computeMatchedItemIndex;
217
+ declare const utils_computeResultLength: typeof computeResultLength;
218
+ declare const utils_generateDefaultField: typeof generateDefaultField;
219
+ declare const utils_generateFieldName: typeof generateFieldName;
220
+ declare const utils_generateRequestParams: typeof generateRequestParams;
221
+ declare const utils_getObjectDeepValue: typeof getObjectDeepValue;
222
+ declare const utils_isArray: typeof isArray;
223
+ declare const utils_isObjectResult: typeof isObjectResult;
224
+ declare const utils_searchValueByKey: typeof searchValueByKey;
225
+ declare const utils_setReactivityField: typeof setReactivityField;
226
+ declare const utils_updateObjectDeepValue: typeof updateObjectDeepValue;
227
+ declare namespace utils {
228
+ export { utils_combineArrayData as combineArrayData, utils_computeMatchedItemIndex as computeMatchedItemIndex, utils_computeResultLength as computeResultLength, utils_generateDefaultField as generateDefaultField, utils_generateFieldName as generateFieldName, utils_generateRequestParams as generateRequestParams, utils_getObjectDeepValue as getObjectDeepValue, utils_isArray as isArray, utils_isObjectResult as isObjectResult, utils_searchValueByKey as searchValueByKey, utils_setReactivityField as setReactivityField, utils_updateObjectDeepValue as updateObjectDeepValue };
229
+ }
230
+
231
+ declare const _default: {
232
+ readonly SETTER_TYPE: {
233
+ readonly RESET: 0;
234
+ readonly MERGE: 1;
235
+ };
236
+ readonly FETCH_TYPE_ARRAY: readonly ["jump", "sinceId", "page", "seenIds", "auto"];
237
+ readonly FETCH_TYPE: {
238
+ readonly PAGINATION: "jump";
239
+ readonly SINCE_FIRST_OR_END_ID: "sinceId";
240
+ readonly SCROLL_LOAD_MORE: "page";
241
+ readonly HAS_LOADED_IDS: "seenIds";
242
+ readonly AUTO: "auto";
243
+ };
244
+ readonly CHANGE_TYPE: {
245
+ readonly SEARCH_FIELD: "search";
246
+ readonly RESET_FIELD: "reset";
247
+ readonly RESULT_UPDATE_KV: "update";
248
+ readonly RESULT_ADD_AFTER: "push";
249
+ readonly RESULT_ADD_BEFORE: "unshift";
250
+ readonly RESULT_REMOVE_BY_ID: "delete";
251
+ readonly RESULT_INSERT_TO_BEFORE: "insert-before";
252
+ readonly RESULT_INSERT_TO_AFTER: "insert-after";
253
+ readonly RESULT_LIST_MERGE: "patch";
254
+ readonly RESULT_ITEM_MERGE: "merge";
255
+ };
256
+ readonly FIELD_DATA: {
257
+ readonly RESULT_KEY: "result";
258
+ readonly EXTRA_KEY: "extra";
259
+ };
260
+ readonly DEFAULT_UNIQUE_KEY_NAME: "id";
261
+ };
262
+
263
+ export { type ApiResponse, type DefaultField, _default as ENUM, type FetchType, type FieldKeys, type GenerateFieldProps, type GenerateParamsResp, type GenerateParamsType, type InitDataType, type InitStateType, type KeyMap, type LoadMoreType, type MorphArray, type ObjectKey, type ResultArrayType$1 as ResultArrayType, type ResultObjectType$1 as ResultObjectType, type SetDataType, type SetErrorType, type SetterFuncParams, type UpdateStateType, initData, initState, loadMore, updateState, utils };