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,31 @@
/*
* 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 { useRef } from 'react';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- .
type Fn<ARGS extends any[], R> = (...args: ARGS) => R;
// https://github.com/Volune/use-event-callback/blob/master/src/index.ts
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- .
export const useEventCallback = <A extends any[], R>(
fn: Fn<A, R>,
): Fn<A, R> => {
const ref = useRef(fn);
ref.current = fn;
const exposedRef = useRef((...args: A) => ref.current(...args));
return exposedRef.current;
};

View File

@@ -0,0 +1,42 @@
/*
* 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 { useState, useRef, useLayoutEffect } from 'react';
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type -- x
type Destructor = (() => void) | void;
type Fn<ARGS extends unknown[]> = (...args: ARGS) => Destructor;
export const useImperativeLayoutEffect = <Params extends unknown[]>(
effect: Fn<Params>,
) => {
const [effectValue, setEffectValue] = useState(0);
const paramRef = useRef<Params>();
const effectRef = useRef<Fn<Params>>(() => undefined);
effectRef.current = effect;
useLayoutEffect(() => {
if (!effectValue) {
return;
}
// 经过一次运行后一定不为 undefined
return paramRef.current && effectRef.current(...paramRef.current);
}, [effectValue]);
return (...args: Params) => {
paramRef.current = args;
setEffectValue(pre => pre + 1);
};
};

View File

@@ -0,0 +1,97 @@
/*
* 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 { useEffect, useRef, useState } from 'react';
import { useEventCallback } from './use-event-callback';
type SearchStage = 'empty' | 'debouncing' | 'searching' | 'failed' | 'success';
export interface UseSearchConfig<Payload, Res> {
debounceInterval: number;
adjustDebounce?: (payload: Payload | null) => number;
onError?: (e: unknown) => void;
onSuccess?: (searchRes: Res, payload: Payload) => void;
}
// todo 补充关于 res 重置的测试 case
/** 小心 service 引用变化! 一旦变化会触发重新加载 */
export const useSearch = <Payload, Res>(
service: (payload: Payload) => Promise<Res>,
{
onError,
debounceInterval,
adjustDebounce,
onSuccess,
}: UseSearchConfig<Payload, Res>,
) => {
const [payload, setPayload] = useState<Payload | null>(null);
const [searchStage, setSearchStage] = useState<SearchStage>('empty');
const [res, setRes] = useState<Res | null>(null);
const [triggerId, setTriggerId] = useState(0);
const debounceIdRef = useRef<ReturnType<typeof setTimeout>>();
const isEmpty = (localPayload: Payload | null): localPayload is null =>
localPayload === null;
const doSearch = useEventCallback(() => {
clearTimeout(debounceIdRef.current);
const finalDebounceTime = adjustDebounce?.(payload) ?? debounceInterval;
debounceIdRef.current = setTimeout(async () => {
setRes(null);
const searchCount = debounceIdRef.current;
if (isEmpty(payload)) {
setSearchStage('empty');
return;
}
setSearchStage('searching');
try {
const searchRes = await service(payload);
if (searchCount !== debounceIdRef.current) {
return;
}
setRes(searchRes);
setSearchStage('success');
onSuccess?.(searchRes, payload);
} catch (e) {
if (searchCount !== debounceIdRef.current) {
return;
}
console.error('[doSearch in use-search]', e);
onError?.(e);
setSearchStage('failed');
}
}, finalDebounceTime);
});
useEffect(() => {
setRes(null);
if (isEmpty(payload)) {
setSearchStage('empty');
} else {
setSearchStage('debouncing');
}
doSearch();
}, [payload, service, triggerId]);
return {
/** 注意清空时设置为 null */
setPayload,
searchStage,
res,
/** 主要用于重试 */
run: () => setTriggerId(c => c + 1),
};
};

View File

@@ -0,0 +1,19 @@
/*
* 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.
*/
export { useImperativeLayoutEffect } from './hooks/use-imperative-layout-effect';
export { useSearch } from './hooks/use-search';
export { useEventCallback } from './hooks/use-event-callback';

View File

@@ -0,0 +1,17 @@
/*
* 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.
*/
/// <reference types='@coze-arch/bot-typings' />