@net-vert/core
Version:
Dependency Inversion Network Library with Type-Safe Injection.
683 lines (682 loc) • 15.9 kB
JavaScript
import { TaskQueue as j } from "id-queue";
var f = /* @__PURE__ */ ((r) => (r.GET = "get", r.POST = "post", r.PUT = "put", r.DELETE = "delete", r))(f || {}), g = /* @__PURE__ */ ((r) => (r.CACHE = "cache", r.RETRY = "retry", r.IDEMPOTENT = "idempotent", r.CONCURRENT = "concurrent", r.THROTTLE = "throttle", r))(g || {});
const k = "default", S = /* @__PURE__ */ new Map(), me = (r, e = k) => {
S.set(e, r);
}, q = (r = k) => {
const e = S.get(r);
if (!e) throw new Error(`Requestor实例 ${String(r)} 未注册`);
return e;
}, v = {
get: (r, e) => ({
url: r,
method: f.GET,
...e,
params: e?.params
}),
post: (r, e, t) => ({
url: r,
method: f.POST,
data: e,
...t
}),
delete: (r, e) => ({
url: r,
method: f.DELETE,
...e
}),
put: (r, e, t) => ({
url: r,
method: f.PUT,
data: e,
...t
}),
request: (r) => r
}, F = Object.keys(v);
function $(r, e, t) {
const n = {}, s = (o) => {
if (o === e.length)
return t(r);
const i = e[o];
return i({
config: r,
ctx: n,
next: () => s(o + 1)
});
};
return s(0);
}
function R(r, e) {
const t = {}, n = e ?? [];
return F.forEach(
(s) => {
t[s] = (...o) => {
const i = v[s](...o);
return $(
i,
n,
r
);
};
}
), t;
}
function b(r) {
const { extensions: e, instanceKey: t } = r ?? {}, n = q(t);
return R(
n,
e
);
}
const V = {
retries: 3,
delay: 0,
retryCondition: () => !0
}, B = (r) => {
const e = { ...V, ...r };
return Object.assign(async ({ config: n, next: s }) => {
let o = 0, i;
for (; o <= e.retries; )
try {
return await s();
} catch (a) {
if (i = a, o === e.retries)
throw a;
const c = {
config: n,
lastResponse: a,
attempt: o
};
if (!e.retryCondition(c))
throw a;
o++;
const h = typeof e.delay == "function" ? e.delay(c) : e.delay;
h > 0 && await new Promise((I) => setTimeout(I, h));
}
throw i;
}, { __middlewareType: g.RETRY });
}, _ = () => {
const r = /* @__PURE__ */ new Map();
return { getPromise: (o) => r.get(o), setPromise: (o, i) => {
r.set(o, i), i.finally(() => r.delete(o));
}, delPromise: (o) => r.delete(o), clearCache: () => r.clear() };
}, K = (r) => {
const { config: e } = r, { method: t, url: n, data: s } = e;
return [t, n, JSON.stringify(s)].join("|");
}, A = {
key: K
}, J = (r) => {
const e = { ...A, ...r }, t = _();
return Object.assign(({ config: s, next: o }) => {
const i = e.key({ config: s }), a = t.getPromise(i);
if (a)
return a;
const c = o();
return t.setPromise(i, c), c;
}, {
__middlewareType: g.IDEMPOTENT,
promiseCache: t
});
};
class z {
parallelCount;
tasks;
runningCount;
constructor(e = 4) {
this.parallelCount = e, this.tasks = new j(), this.runningCount = 0;
}
// 加入
add(e, t) {
return new Promise((n, s) => {
this.tasks.enqueue(e, {
task: t,
resolve: n,
reject: s
}), this._run();
});
}
// 删除
remove(e) {
this.tasks.remove(e);
}
execute(e) {
const { task: t, resolve: n, reject: s } = e;
return t().then(n).catch(s).finally(() => {
this.runningCount--, this._run();
});
}
_run() {
for (; this.runningCount < this.parallelCount && this.tasks.size > 0; ) {
const e = this.tasks.dequeue();
this.runningCount++, this.execute(e);
}
}
}
let G = 0;
const H = {
parallelCount: 4,
createId: () => G++
}, L = (r) => {
const { parallelCount: e, createId: t } = { ...H, ...r }, n = new z(e);
return Object.assign(({ config: o, next: i }) => {
const a = t({ config: o });
return n.add(a, () => i());
}, { __middlewareType: g.CONCURRENT, pool: n });
}, T = /* @__PURE__ */ new Map(), w = (r, e) => {
T.set(e, r);
};
function U(r) {
const e = T.get(r);
if (!e)
throw new Error(`Store实例 ${String(r)} 未注册`);
return e;
}
class M {
store = /* @__PURE__ */ new Map();
getItem(e) {
return this.store.get(e);
}
setItem(e, t) {
return this.store.set(e, t), t;
}
removeItem(e) {
this.store.delete(e);
}
clear() {
this.store.clear();
}
length() {
return this.store.size;
}
key(e) {
return Array.from(this.store.keys())[e];
}
keys() {
return Array.from(this.store.keys());
}
iterate(e) {
let t = 0;
for (const [n, s] of this.store.entries())
e(s, n, t), t++;
}
}
const X = (r) => !!r && (typeof r == "object" || typeof r == "function") && typeof r.then == "function", x = (r) => typeof window < "u" ? r() : {
getItem() {
return null;
},
setItem() {
},
removeItem() {
},
clear() {
},
key() {
return null;
},
get length() {
return 0;
}
}, m = x(() => window.localStorage);
class Z {
constructor() {
}
getItem(e) {
const t = String(e), n = m.getItem(t);
if (n !== null)
try {
return JSON.parse(n);
} catch (s) {
console.error(`Failed to parse value for key: ${t}`, s);
return;
}
}
setItem(e, t) {
const n = String(e);
try {
m.setItem(n, JSON.stringify(t));
} catch (s) {
throw console.error(`Failed to set value for key: ${n}`, s), s;
}
return t;
}
removeItem(e) {
const t = String(e);
m.removeItem(t);
}
clear() {
m.clear();
}
length() {
return m.length;
}
key(e) {
return m.key(e);
}
keys() {
const e = [];
for (let t = 0; t < m.length; t++) {
const n = m.key(t);
n && e.push(n);
}
return e;
}
iterate(e) {
this.keys().forEach((t, n) => {
const s = this.getItem(t);
s !== void 0 && e(s, t, n);
});
}
}
const y = x(() => window.sessionStorage);
class Q {
constructor() {
}
getItem(e) {
const t = String(e), n = y.getItem(t);
if (n !== null)
try {
return JSON.parse(n);
} catch (s) {
console.error(`Failed to parse value for key: ${t}`, s);
return;
}
}
setItem(e, t) {
const n = String(e);
try {
y.setItem(n, JSON.stringify(t));
} catch (s) {
throw console.error(`Failed to set value for key: ${n}`, s), s;
}
return t;
}
removeItem(e) {
const t = String(e);
y.removeItem(t);
}
clear() {
y.clear();
}
length() {
return y.length;
}
key(e) {
return y.key(e);
}
keys() {
const e = [];
for (let t = 0; t < y.length; t++) {
const n = y.key(t);
n && e.push(n);
}
return e;
}
iterate(e) {
this.keys().forEach((t, n) => {
const s = this.getItem(t);
s !== void 0 && e(s, t, n);
});
}
}
class Y {
dbName;
storeName;
dbPromise = null;
DB_VERSION = 1;
constructor(e, t) {
this.dbName = e, this.storeName = t, this.initDB();
}
/**
* 初始化数据库连接
*/
initDB() {
if (typeof window > "u" || !window.indexedDB) {
console.warn("IndexedDB is not available");
return;
}
this.dbPromise = new Promise((e, t) => {
const n = indexedDB.open(this.dbName, this.DB_VERSION);
n.onerror = () => {
t(new Error(`Failed to open database: ${this.dbName}`));
}, n.onsuccess = () => {
e(n.result);
}, n.onupgradeneeded = (s) => {
const o = s.target.result;
o.objectStoreNames.contains(this.storeName) || o.createObjectStore(this.storeName);
};
});
}
/**
* 获取数据库实例
*/
async getDB() {
if (!this.dbPromise)
throw new Error("IndexedDB is not initialized");
return this.dbPromise;
}
/**
* 执行事务
*/
async withStore(e, t) {
const n = await this.getDB();
return new Promise((s, o) => {
const i = n.transaction([this.storeName], e).objectStore(this.storeName), a = t(i);
a.onsuccess = () => {
s(a.result);
}, a.onerror = () => {
o(a.error);
};
});
}
async getItem(e) {
try {
const t = await this.withStore("readonly", (n) => n.get(String(e)));
return t === void 0 ? void 0 : t;
} catch (t) {
console.error(`Failed to get value for key: ${String(e)}`, t);
return;
}
}
async setItem(e, t) {
try {
return await this.withStore("readwrite", (n) => n.put(t, String(e))), t;
} catch (n) {
throw console.error(`Failed to set value for key: ${String(e)}`, n), n;
}
}
async removeItem(e) {
try {
await this.withStore("readwrite", (t) => t.delete(String(e)));
} catch (t) {
throw console.error(`Failed to remove value for key: ${String(e)}`, t), t;
}
}
async clear() {
try {
await this.withStore("readwrite", (e) => e.clear());
} catch (e) {
throw console.error("Failed to clear storage", e), e;
}
}
async length() {
try {
return await this.withStore("readonly", (e) => e.count());
} catch (e) {
return console.error("Failed to get storage length", e), 0;
}
}
async key(e) {
try {
return (await this.withStore("readonly", (t) => t.getAllKeys()))[e];
} catch (t) {
console.error("Failed to get key", t);
return;
}
}
async keys() {
try {
return await this.withStore("readonly", (e) => e.getAllKeys());
} catch (e) {
return console.error("Failed to get all keys", e), [];
}
}
async iterate(e) {
try {
const t = (await this.getDB()).transaction([this.storeName], "readonly").objectStore(this.storeName).openCursor();
return new Promise((n, s) => {
let o = 0;
t.onsuccess = (i) => {
const a = i.target.result;
if (a) {
const c = a.key, h = a.value;
e(h, c, o), o++, a.continue();
} else
n();
}, t.onerror = () => {
s(t.error);
};
});
} catch (t) {
console.error("Failed to iterate storage", t);
}
}
}
const p = {
memory: "memory",
local: "local",
session: "session",
indexeddb: "indexeddb"
};
function P(r, e) {
Object.assign(r, e);
}
function W(r) {
return P(r, {
async getItemOrDefault(e, t) {
const n = await this.getItem(e);
return n !== null ? n : t;
},
async hasItem(e) {
return await this.getItem(e) !== null;
},
async removeItems(e) {
await Promise.all(e.map((t) => this.removeItem(t)));
},
async getItems(e) {
return await Promise.all(e.map((t) => this.getItem(t)));
}
}), r;
}
function ee(r) {
return P(r, {
getItemOrDefault(e, t) {
const n = this.getItem(e);
return n !== null ? n : t;
},
hasItem(e) {
return this.getItem(e) !== null;
},
removeItems(e) {
e.forEach((t) => this.removeItem(t));
},
getItems(e) {
return e.map((t) => this.getItem(t));
}
}), r;
}
function te(r) {
const e = r.getItem("");
return X(e) ? W(r) : ee(r);
}
function C(r, ...e) {
const t = U(r);
let n;
try {
n = new t(...e);
} catch {
n = t(...e);
}
return te(n);
}
w(M, p.memory);
w(Z, p.local);
w(Q, p.session);
w(Y, p.indexeddb);
function N(r, e) {
return {
value: r,
expireAt: Date.now() + e
};
}
function O(r, e = Date.now()) {
return r.expireAt <= e;
}
function re(r, e = Date.now()) {
return O(r, e) ? void 0 : r.value;
}
const ne = (r) => {
const { config: e } = r, { method: t, url: n, data: s } = e;
return [t, n, JSON.stringify(s)].join("|");
}, se = () => !0, E = 24 * 60 * 60 * 1e3, oe = {
key: ne,
duration: E,
isValid: se,
store: p.memory
// 默认不持久化,使用内存存储
};
class ae {
store;
constructor(e) {
return typeof e == "string" ? this.store = C(e) : (w(e.factory, e.key), this.store = C(e.key)), new Proxy(this, {
get(t, n) {
if (n in t)
return t[n];
const s = t.store[n];
return typeof s == "function" ? s.bind(t.store) : s;
}
});
}
/**
* 设置缓存(自动包装成 ExpirableValue)
* @param key 缓存 key
* @param value 要缓存的值
* @param duration 过期时长(毫秒),默认 24 小时
*/
setCache(e, t, n = E) {
const s = N(t, n);
this.store.setItem(e, s);
}
/**
* 获取缓存值(检查过期,如果过期返回 undefined)
* @param key 缓存 key
* @returns 未过期返回值,已过期返回 undefined
*/
async getCache(e) {
const t = await this.store.getItem(e);
if (t)
return re(t);
}
/**
* 检查是否有有效的缓存(未过期)
* @param key 缓存 key
* @returns true 表示有有效缓存,false 表示无缓存或已过期
*/
async hasValidCache(e) {
const t = await this.store.getItem(e);
return t ? !O(t) : !1;
}
}
const ie = ae, ce = (r) => {
const e = { ...oe, ...r }, t = new ie(e.store), n = (o) => typeof e.duration == "function" ? e.duration(o) : e.duration;
return Object.assign(async ({ config: o, next: i }) => {
const a = e.key({ config: o }), c = await t.getCache(a);
if (c) {
if (await e.isValid({
key: a,
config: o,
cachedData: c
})) return c;
t.removeItem(a);
}
const h = await i(), I = n({ key: a, config: o, cachedData: c, response: h }), u = N(h, I);
return t.setItem(a, u), h;
}, {
__middlewareType: g.CACHE,
storage: t
});
};
class ue extends Error {
constructor(e, t) {
super(`请求在节流队列中等待超时:配置超时 ${e}ms,实际等待 ${t}ms`), this.name = "ThrottleTimeoutError";
}
}
function le(r, e) {
const t = [];
let n = 0, s = !1;
const o = (u) => new Promise((l) => setTimeout(l, u)), i = () => {
if (e === void 0 || t.length === 0)
return !1;
const u = Date.now(), l = t[0], d = u - l.timestamp;
return d >= e ? (t.shift(), l.reject(new ue(e, d)), !0) : !1;
}, a = async () => {
if (!(s || t.length === 0)) {
for (s = !0; t.length > 0; ) {
if (e !== void 0 && i())
continue;
if (t.length === 0)
break;
const l = Date.now() - n;
if (n > 0 && l < r) {
const D = r - l;
await o(D);
}
const d = t.shift();
d && (n = Date.now(), d.task().then(d.resolve).catch(d.reject));
}
s = !1;
}
};
return {
add: (u) => new Promise((l, d) => {
t.push({
task: u,
resolve: l,
reject: d,
timestamp: Date.now()
}), a();
}),
getStatus: () => {
const u = t.length > 0 ? n + r : null;
return {
queueLength: t.length,
lastExecutionTime: n,
isProcessing: s,
nextExecutionTime: u
};
},
clear: () => {
t.length = 0;
}
};
}
const he = {
interval: 1e3
// timeout 默认 undefined,永不超时
}, ye = (r) => {
const e = { ...he, ...r }, t = le(e.interval, e.timeout);
return Object.assign(async ({ config: s, next: o }) => t.add(o), {
__middlewareType: g.THROTTLE,
throttler: t
});
};
function ge(r) {
const { instanceKey: e, key: t, duration: n, isValid: s, store: o, ...i } = r;
return b({
instanceKey: e,
extensions: [
J(i),
ce({ key: t, duration: n, isValid: s, store: o })
]
});
}
function fe(r) {
const { instanceKey: e, parallelCount: t, createId: n, retries: s, delay: o, retryCondition: i } = r;
return b({
instanceKey: e,
extensions: [
L({ parallelCount: t, createId: n }),
B({ retries: s, delay: o, retryCondition: i })
]
});
}
export {
ce as cache,
L as concurrent,
ge as createCachedIdempotentRequestor,
fe as createConcurrentRetryRequestor,
b as createRequestor,
J as idempotent,
me as inject,
B as retry,
ye as throttle,
q as useRequestor
};