You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oaer/lib/components/proxy.ts

117 lines
3.0 KiB
TypeScript

/*
* proxy.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-10-23 17:20
* Distributed under terms of the GPL license.
*/
type voidFn = () => void
// TODO: 没有删除机制
const callbackCache: voidFn[] = []
const cacheUpdateList: number[] = []
// 界面响应频率40hz
setInterval(() => {
let list = new Set(cacheUpdateList.splice(0))
for (let l of list) {
callbackCache[l]()
}
}, 25)
function generateUniqueId() {
const timestamp = performance.now().toString(36);
const random = Math.random().toString(36).substring(2, 5);
return `${timestamp}-${random}`;
}
function ForceUpdate() {
for (let c of callbackCache) {
c()
}
}
var listen_tags: number[] = []
function Listen(callback: voidFn) {
listen_tags.push(callbackCache.length)
callbackCache.push(callback)
callback()
listen_tags.pop()
}
const isProxy = Symbol("isProxy")
const DataID = Symbol("DataID")
function Watch<T extends Object>(data: T) {
const did = generateUniqueId()
let isArray = false
if (Object.prototype.toString.call(data) === '[object Array]') {
isArray = true
}
// console.log(`watch ${did} ${isArray}`, data)
const listeners: { [key: string | symbol]: number[] } = {}
const handler = {
get(target: Object, key: string | symbol, receiver: any) {
if (key === isProxy) {
return true
}
if (key === DataID) {
return did
}
const value = Reflect.get(target, key, receiver)
if (typeof value === 'object' && value !== null) {
if (value[isProxy]) {
return value
} else {
let newValue = Watch(value)
Reflect.set(target, key, newValue, receiver)
return newValue
}
}
let idx = -1
if (listen_tags.length > 0) {
let lkey = key
idx = listen_tags[listen_tags.length - 1]
if (isArray) {
lkey = ''
}
if (!listeners[lkey]) {
listeners[lkey] = [idx]
} else if (listeners[lkey].indexOf(idx) == -1) {
listeners[lkey].push(idx)
}
}
// console.log(`${did} get ${key.toString()}:${value} ${idx}`)
return value;
},
set(target: Object, key: string | symbol, newValue: any, receiver: any) {
// console.log(`${did} set ${key.toString()} ${newValue}`)
const result = Reflect.set(target, key, newValue, receiver);
if (result) {
let lkey = key
if (isArray) {
lkey = ''
}
if (listeners[lkey]) {
for (let cb of listeners[lkey]) {
cacheUpdateList.push(cb)
}
}
}
return result;
},
deleteProperty(target: Object, key: string) {
// console.log(`del ${key}`)
const result = Reflect.deleteProperty(target, key);
if (result) {
}
return result
}
};
let res = new Proxy<T>(data, handler);
// Symbol(Symbol.toStringTag)
// res[Symbol.toStringTag] = 'Proxy'
return res
}
export default { Watch, Listen, ForceUpdate, DataID, generateUniqueId }