feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type StoreApi, type UseBoundStore } from 'zustand';
import {
type AutosaveObserverConfig,
type HostedObserverConfig,
type EventCallBacks,
type SaveRequest,
} from '../type';
import { AutosaveObserver } from './observer';
export interface AutosaveManagerProps<StoreType, ScopeKey, ScopeStateType> {
store: UseBoundStore<StoreApi<StoreType>>;
registers: HostedObserverConfig<StoreType, ScopeKey, ScopeStateType>[];
saveRequest: SaveRequest<ScopeStateType, ScopeKey>;
eventCallBacks?: EventCallBacks<ScopeStateType, ScopeKey>;
}
export class AutosaveManager<StoreType, ScopeKey, ScopeStateType> {
private configList: AutosaveObserverConfig<
StoreType,
ScopeKey,
ScopeStateType
>[];
private observerList: AutosaveObserver<StoreType, ScopeKey, ScopeStateType>[];
private store: UseBoundStore<StoreApi<StoreType>>;
private eventCallBacks?: EventCallBacks<ScopeStateType, ScopeKey>;
private saveRequest: SaveRequest<ScopeStateType, ScopeKey>;
constructor(
props: AutosaveManagerProps<StoreType, ScopeKey, ScopeStateType>,
) {
this.configList = [];
this.observerList = [];
this.saveRequest = props.saveRequest;
this.eventCallBacks = props.eventCallBacks;
this.store = props.store;
this.register(props.registers);
}
/**
* 注册数据源和定义对应的 Observer 配置
* @param _config
*/
public register = (
registers: HostedObserverConfig<StoreType, ScopeKey, ScopeStateType>[],
) => {
this.close();
this.configList = [];
registers.forEach(register => {
const config: AutosaveObserverConfig<
StoreType,
ScopeKey,
ScopeStateType
> = {
...register,
eventCallBacks: this.eventCallBacks,
saveRequest: this.saveRequest,
};
this.configList.push(config);
});
};
/**
* 启动 Manager 模块
*/
public start = () => {
if (this.observerList.length > 0) {
return;
}
this.observerList = this.configList.map(
config =>
new AutosaveObserver({
store: this.store,
...config,
}),
);
};
/**
* 关闭 Manager 模块下的所有属性监听
*/
public close = () => {
this.observerList.forEach(observer => observer.close());
this.observerList = [];
};
/**
* 手动保存
* @param params
*/
public manualSave = async (key: ScopeKey, params: ScopeStateType) => {
const config = this.getConfig(key);
if (!config) {
return;
}
const { middleware, eventCallBacks, saveRequest } = config;
const beforeSavePayload = middleware?.onBeforeSave
? await middleware?.onBeforeSave(params)
: params;
eventCallBacks?.onBeforeSave?.(beforeSavePayload);
await saveRequest(beforeSavePayload as ScopeStateType, key, []);
const afterSavePayload = middleware?.onAfterSave
? await middleware?.onAfterSave(params)
: params;
eventCallBacks?.onAfterSave?.(afterSavePayload);
};
/**
* 回调过程中关闭自动保存
* @param params
*/
public handleWithoutAutosave = async (params: {
key: ScopeKey;
handler: () => Promise<void>;
}) => {
const { key, handler } = params;
const observers = this.observerList.filter(o => o.config.key === key);
if (observers.length) {
observers.forEach(o => (o.lock = true));
await handler();
observers.forEach(o => (o.lock = false));
}
};
/**
* 立即触发保存
* @param key
*/
public saveFlush = (key: ScopeKey) => {
const observer = this.getObserver(key);
observer?.debouncedSaveFunc?.flush?.();
};
/**
* 立即触发所有保存
* @param key
*/
public saveFlushAll = () => {
this.observerList.forEach(observer =>
observer?.debouncedSaveFunc?.flush?.(),
);
};
/**
* 获取目标 observer 配置
* @param key
*/
private getObserver = (key: ScopeKey) =>
this.observerList.find(i => i.config.key === key);
/**
* 获取目标配置项
* @param key
*/
private getConfig = (key: ScopeKey) =>
this.configList.find(i => i.key === key);
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type StoreApi, type UseBoundStore } from 'zustand';
import { createSelector } from 'reselect';
import { debounce, has, get, type DebouncedFunc } from 'lodash-es';
import diff, { type Diff } from 'deep-diff';
import { isFunction, isObject, getPayloadByFormatter } from '../utils';
import {
DebounceTime,
type AutosaveObserverConfig,
type UseStoreType,
type PathType,
type AutosaveObserverProps,
} from '../type/index';
export class AutosaveObserver<StoreType, ScopeKey, ScopeStateType> {
private store: UseBoundStore<StoreApi<StoreType>>;
public lock: boolean;
public debouncedSaveFunc: DebouncedFunc<() => Promise<void>>;
public nextState!: ScopeStateType;
public prevState!: ScopeStateType;
private diff!: Diff<ScopeStateType, ScopeStateType>[];
private unobserver?: () => void;
private unsubscribe!: () => void;
public config: AutosaveObserverConfig<StoreType, ScopeKey, ScopeStateType>;
constructor(
props: AutosaveObserverProps<StoreType, ScopeKey, ScopeStateType>,
) {
const { store, ...config } = props;
this.store = store;
this.lock = false;
this.config = config;
// 订阅字段初始化
this.initSubscribe();
}
private initSubscribe = () => {
const memoizeSelector = this.getMemoizeSelector();
this.unsubscribe = (
this.store as unknown as UseStoreType<StoreType, ScopeStateType>
).subscribe(memoizeSelector, this.subscribeCallback);
};
private getMemoizeSelector = () => {
if (typeof this.config.selector === 'function') {
return this.config.selector;
} else {
// 使用createSelector创建可记忆化的选择器
const { deps, transformer } = this.config.selector;
return createSelector(deps, transformer);
}
};
private subscribeCallback = async (nextState, prevState) => {
console.log('nextState :>> ', nextState);
console.log('prevState :>> ', prevState);
// selector 返回的 state
this.nextState = nextState;
this.prevState = prevState;
if (this.lock) {
return;
}
const diffChange: Diff<ScopeStateType, ScopeStateType>[] | undefined = diff(
prevState,
nextState,
);
console.log('diffChange:>>', diffChange);
if (!diffChange) {
return;
}
this.debouncedSaveFunc?.cancel?.();
this.diff = diffChange;
const delayTime = this.getTriggerDelayTime(prevState, diffChange);
console.log('delayTime:>>>>>', delayTime);
if (delayTime === 0 || this.config.immediate) {
await this.parsedSaveFunc();
return;
}
this.debouncedSaveFunc = debounce(this.parsedSaveFunc, delayTime);
await this.debouncedSaveFunc();
};
private parsedSaveFunc = async () => {
// 中间件-保存前
const beforeSavePayload = await getPayloadByFormatter<ScopeStateType>(
this.nextState,
this.config?.middleware?.onBeforeSave,
);
// 生命周期-保存前
await this.config?.eventCallBacks?.onBeforeSave?.({
key: this.config.key,
data: beforeSavePayload,
});
console.log('beforeSavePayload:>>', beforeSavePayload);
try {
await this.config.saveRequest(
beforeSavePayload,
this.config.key,
this.diff,
);
// 中间件-保存后
const afterSavePayload = await getPayloadByFormatter<ScopeStateType>(
this.nextState,
this.config?.middleware?.onAfterSave,
);
console.log('afterSavePayload:>>', afterSavePayload);
// 生命周期-保存后
await this.config?.eventCallBacks?.onAfterSave?.({
key: this.config.key,
data: afterSavePayload,
});
} catch (error) {
console.log('error:>>', error);
// 生命周期-异常
this.config?.eventCallBacks?.onError?.({
key: this.config.key,
error: error as Error,
});
}
};
/**
* 取消订阅
*/
public close = () => {
this.debouncedSaveFunc?.flush();
this.unsubscribe();
this.unobserver?.();
};
/**
* 获取状态变更带来的触发延时时间
* @param prevState selector 选择的 store 的内容
* @param diffChange 前后比对的diff
* @returns 延时时间
*/
private getTriggerDelayTime = (
prevState?: ScopeStateType,
diffChange?: Diff<ScopeStateType, ScopeStateType>[],
) => {
const configDebounce = this.config.debounce;
if (!configDebounce) {
return DebounceTime.Immediate;
}
if (isFunction(configDebounce)) {
return configDebounce();
}
if (!isObject(configDebounce)) {
return configDebounce;
}
if (!diffChange || diffChange.length === 0) {
return configDebounce.default;
}
const targetDelayTimes: number[] = [];
for (const change of diffChange) {
const changePath = change.path;
const debouncePath = this.getdebouncePath(changePath);
if (
!changePath ||
!has(prevState, changePath) ||
typeof debouncePath === 'number'
) {
targetDelayTimes.push(configDebounce.default);
continue;
}
const debounceType = get(
configDebounce,
debouncePath,
configDebounce.default,
);
if (!isObject(debounceType)) {
targetDelayTimes.push(debounceType);
continue;
}
if (!debounceType.arrayType) {
targetDelayTimes.push(configDebounce.default);
continue;
}
if (!isObject(debounceType.action as DebounceTime)) {
targetDelayTimes.push(debounceType.action as DebounceTime);
} else {
const kind =
change.kind === 'A' && change.item?.kind
? change.item?.kind
: change.kind;
const triggerKind = debounceType.action[kind];
targetDelayTimes.push(triggerKind);
}
}
return Math.min(...targetDelayTimes);
};
/**
* 获取变更与 trigger 声明配置对应的 key
* @param changePath diff path
* @returns path key
*/
private getdebouncePath = (changePath?: PathType[]) => {
if (!changePath) {
return '';
}
const indexPath = path => typeof path === 'number';
const isArrayPath = changePath.some(indexPath);
if (isArrayPath) {
return changePath[0];
}
return changePath.join('.');
};
}