@net-vert/core
Version:
Dependency Inversion Network Library with Type-Safe Injection.
694 lines (693 loc) • 16.4 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 || {}), m = /* @__PURE__ */ ((r) => (r.CACHE = "cache", r.RETRY = "retry", r.IDEMPOTENT = "idempotent", r.CONCURRENT = "concurrent", r.SYNC = "sync", r))(m || {});
const S = "default", D = {
retries: 3,
delay: 0,
retryCondition: () => !0
}, F = (r) => {
const e = { ...D, ...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 u = typeof e.delay == "function" ? e.delay(c) : e.delay;
u > 0 && await new Promise((l) => setTimeout(l, u));
}
throw i;
}, { __middlewareType: m.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() };
}, E = (r) => {
const { config: e } = r, { method: t, url: n, data: s } = e;
return [t, n, JSON.stringify(s)].join("|");
}, T = {
key: E
}, q = (r) => {
const e = { ...T, ...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: m.IDEMPOTENT,
promiseCache: t
});
};
class R {
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 B = 0;
const _ = {
parallelCount: 4,
createId: () => B++
}, K = (r) => {
const { parallelCount: e, createId: t } = { ..._, ...r }, n = new R(e);
return Object.assign(({ config: o, next: i }) => {
const a = t({ config: o });
return n.add(a, () => i());
}, { __middlewareType: m.CONCURRENT, pool: n });
}, v = /* @__PURE__ */ new Map(), y = (r, e) => {
v.set(e, r);
};
function A(r) {
const e = v.get(r);
if (!e)
throw new Error(`Store实例 ${String(r)} 未注册`);
return e;
}
class J {
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 z = (r) => !!r && (typeof r == "object" || typeof r == "function") && typeof r.then == "function", b = (r) => typeof window < "u" ? r() : {
getItem() {
return null;
},
setItem() {
},
removeItem() {
},
clear() {
},
key() {
return null;
},
get length() {
return 0;
}
}, h = b(() => window.localStorage);
class G {
constructor() {
}
getItem(e) {
const t = String(e), n = h.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 {
h.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);
h.removeItem(t);
}
clear() {
h.clear();
}
length() {
return h.length;
}
key(e) {
return h.key(e);
}
keys() {
const e = [];
for (let t = 0; t < h.length; t++) {
const n = h.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 d = b(() => window.sessionStorage);
class U {
constructor() {
}
getItem(e) {
const t = String(e), n = d.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 {
d.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);
d.removeItem(t);
}
clear() {
d.clear();
}
length() {
return d.length;
}
key(e) {
return d.key(e);
}
keys() {
const e = [];
for (let t = 0; t < d.length; t++) {
const n = d.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, u = a.value;
e(u, c, o), o++, a.continue();
} else
n();
}, t.onerror = () => {
s(t.error);
};
});
} catch (t) {
console.error("Failed to iterate storage", t);
}
}
}
const g = {
memory: "memory",
local: "local",
session: "session",
indexeddb: "indexeddb"
};
function N(r, e) {
Object.assign(r, e);
}
function M(r) {
return N(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 X(r) {
return N(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 Z(r) {
const e = r.getItem("");
return z(e) ? M(r) : X(r);
}
function w(r, ...e) {
const t = A(r);
let n;
try {
n = new t(...e);
} catch {
n = t(...e);
}
return Z(n);
}
y(J, g.memory);
y(G, g.local);
y(U, g.session);
y(Y, g.indexeddb);
function C(r, e) {
return {
value: r,
expireAt: Date.now() + e
};
}
function I(r, e = Date.now()) {
return r.expireAt <= e;
}
function x(r, e = Date.now()) {
return I(r, e) ? void 0 : r.value;
}
const H = (r) => {
const { config: e } = r, { method: t, url: n, data: s } = e;
return [t, n, JSON.stringify(s)].join("|");
}, L = () => !0, k = 24 * 60 * 60 * 1e3, Q = {
key: H,
duration: k,
isValid: L,
store: g.memory
// 默认不持久化,使用内存存储
};
let W = class {
store;
constructor(e) {
return typeof e == "string" ? this.store = w(e) : (y(e.factory, e.key), this.store = w(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 = k) {
const s = C(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 x(t);
}
/**
* 检查是否有有效的缓存(未过期)
* @param key 缓存 key
* @returns true 表示有有效缓存,false 表示无缓存或已过期
*/
async hasValidCache(e) {
const t = await this.store.getItem(e);
return t ? !I(t) : !1;
}
};
const ee = W, te = (r) => {
const e = { ...Q, ...r }, t = new ee(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 u = await i(), l = n({ key: a, config: o, cachedData: c, response: u }), p = C(u, l);
return t.setItem(a, p), u;
}, {
__middlewareType: m.CACHE,
storage: t
});
};
class re {
store;
constructor(e) {
return typeof e == "string" ? this.store = w(e) : (y(e.factory, e.key), this.store = w(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 = k) {
const s = C(t, n);
this.store.setItem(e, s);
}
/**
* 获取缓存值(检查过期,如果过期返回 undefined)
* @param key 缓存 key
* @returns 未过期返回值,已过期返回 undefined
*/
getCache(e) {
const t = this.store.getItem(e);
if (t)
return x(t);
}
/**
* 检查是否有有效的缓存(未过期)
* @param key 缓存 key
* @returns true 表示有有效缓存,false 表示无缓存或已过期
*/
hasValidCache(e) {
const t = this.store.getItem(e);
return t ? !I(t) : !1;
}
}
const ne = re, se = (r) => {
const { config: e } = r, { method: t, url: n } = e;
return [t, n].join("|");
}, oe = {
suspense: !0,
key: se,
duration: 24 * 60 * 60 * 1e3,
isValid: () => !0,
store: g.memory
};
function me(r) {
const e = { ...oe, ...r }, t = new ne(e.store), n = (o) => typeof e.duration == "function" ? e.duration(o) : e.duration;
return Object.assign(({ config: o, next: i }) => {
const a = e.key({ config: o }), c = t.getCache(a);
if (c) {
if (e.isValid({
key: a,
config: o,
cachedData: c
})) return c;
t.removeItem(a);
}
if (e.suspense)
throw (e.wrapSuspense ? e.wrapSuspense({ key: a, config: o, p: i() }) : i()).then((l) => {
const p = n({ key: a, config: o, cachedData: c, response: l });
return t.setCache(a, l, p), l;
});
return i().then((u) => {
const l = n({ key: a, config: o, cachedData: c, response: u });
return t.setCache(a, u, l), u;
});
}, { __middlewareType: m.SYNC });
}
const P = /* @__PURE__ */ new Map(), ye = (r, e = S) => {
P.set(e, r);
}, ae = (r = S) => {
const e = P.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
}, ie = Object.keys(V);
function ce(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 ue(r, e) {
const t = {}, n = e ?? [];
return ie.forEach(
(s) => {
t[s] = (...o) => {
const i = V[s](...o);
return ce(
i,
n,
r
);
};
}
), t;
}
function O(r) {
const { extensions: e, instanceKey: t } = r ?? {}, n = ae(t);
return ue(
n,
e
);
}
function ge(r) {
const { instanceKey: e, key: t, duration: n, isValid: s, store: o, ...i } = r;
return O({
instanceKey: e,
extensions: [
q(i),
te({ 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 O({
instanceKey: e,
extensions: [
K({ parallelCount: t, createId: n }),
F({ retries: s, delay: o, retryCondition: i })
]
});
}
export {
te as cache,
K as concurrent,
ge as createCachedIdempotentRequestor,
fe as createConcurrentRetryRequestor,
O as createRequestor,
q as idempotent,
ye as inject,
F as retry,
me as sync,
ae as useRequestor
};