@voltro/cache 0.85.0 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # Third-Party Notices — @voltro/cache
2
2
 
3
- This package depends on the open-source software listed below. Each is the
3
+ This package uses the open-source software listed below. Each is the
4
4
  property of its respective copyright holders and is used under the terms of
5
5
  its license. This file is provided for attribution; it grants no rights in
6
6
  @voltro/cache itself, which is proprietary (see LICENSE).
package/dist/Cache.js ADDED
@@ -0,0 +1,89 @@
1
+ import { CacheStore as e } from "./CacheStore.js";
2
+ import { Clock as t, Context as n, Deferred as r, Effect as i, HashMap as a, Layer as o, Metric as s, Option as c, SynchronizedRef as l } from "effect";
3
+ //#region src/Cache.ts
4
+ var u = (e) => typeof e == "object" && !!e && e._voltroSwr === 1, d = class d extends n.Service()("@voltro/Cache", { make: i.gen(function* () {
5
+ let n = yield* e, o = yield* l.make(a.empty()), d = s.counter("voltro_cache_hits"), f = s.counter("voltro_cache_misses"), p = (e, r, a) => {
6
+ let o = a.tags === void 0 ? {} : { tags: a.tags }, s = a.swrMs;
7
+ if (s !== void 0) {
8
+ let c = a.ttlMs ?? 0;
9
+ return t.currentTimeMillis.pipe(i.flatMap((t) => n.setWithTags(e, {
10
+ _voltroSwr: 1,
11
+ v: r,
12
+ freshUntil: t + c
13
+ }, {
14
+ ttlMs: c + s,
15
+ ...o
16
+ })));
17
+ }
18
+ let c = a.ttlMs === void 0 ? {} : { ttlMs: a.ttlMs };
19
+ return n.setWithTags(e, r, {
20
+ ...c,
21
+ ...o
22
+ });
23
+ }, m = (e, t, n) => n.pipe(i.tap((n) => p(e, n, t))), h = (e, t) => l.modifyEffect(o, (t) => {
24
+ let n = a.get(t, e);
25
+ return c.isSome(n) ? i.succeed([{
26
+ kind: "await",
27
+ deferred: n.value
28
+ }, t]) : r.make().pipe(i.map((n) => [{
29
+ kind: "own",
30
+ deferred: n
31
+ }, a.set(t, e, n)]));
32
+ }).pipe(i.flatMap((n) => {
33
+ if (n.kind === "await") return r.await(n.deferred);
34
+ let s = n.deferred;
35
+ return t.pipe(i.onExit((t) => r.done(s, t).pipe(i.andThen(l.update(o, (t) => a.remove(t, e))))));
36
+ })), g = (e, r, a) => n.get(e).pipe(i.flatMap((n) => {
37
+ if (c.isSome(n)) {
38
+ let o = n.value;
39
+ return u(o) ? s.update(d, 1).pipe(i.andThen(t.currentTimeMillis), i.flatMap((t) => {
40
+ if (t < o.freshUntil) return i.succeed(o.v);
41
+ let n = h(e, m(e, r, a)).pipe(i.catchCause(() => i.void));
42
+ return i.forkDetach(n).pipe(i.as(o.v));
43
+ })) : s.update(d, 1).pipe(i.as(o));
44
+ }
45
+ return s.update(f, 1).pipe(i.andThen(h(e, m(e, r, a))));
46
+ }));
47
+ return {
48
+ get: (e) => n.get(e),
49
+ set: (e, t, r) => n.set(e, t, r),
50
+ setWithTags: (e, t, r) => n.setWithTags(e, t, r),
51
+ put: (e, t, n) => p(e, n, t),
52
+ remove: n.remove,
53
+ has: n.has,
54
+ clear: n.clear,
55
+ invalidateTag: n.invalidateTag,
56
+ wrap: g,
57
+ partition: (e, t = {}) => {
58
+ let r = (t) => `${e}:${t}`, i = (e) => ({
59
+ ...t.ttlMs === void 0 ? {} : { ttlMs: t.ttlMs },
60
+ ...t.swrMs === void 0 ? {} : { swrMs: t.swrMs },
61
+ ...t.tags === void 0 ? {} : { tags: t.tags },
62
+ ...e
63
+ });
64
+ return {
65
+ get: (e) => n.get(r(e)),
66
+ set: (e, i, a) => n.set(r(e), i, a ?? (t.ttlMs === void 0 ? void 0 : { ttlMs: t.ttlMs })),
67
+ has: (e) => n.has(r(e)),
68
+ remove: (e) => n.remove(r(e)),
69
+ wrap: (e, t, n) => g(r(e), i(t), n),
70
+ invalidateTag: (e) => n.invalidateTag(e)
71
+ };
72
+ },
73
+ stats: () => i.all({
74
+ hits: s.value(d),
75
+ misses: s.value(f)
76
+ }).pipe(i.map(({ hits: e, misses: t }) => {
77
+ let n = Number(e.count), r = Number(t.count), i = n + r;
78
+ return {
79
+ hits: n,
80
+ misses: r,
81
+ hitRate: i === 0 ? 0 : n / i
82
+ };
83
+ }))
84
+ };
85
+ }) }) {
86
+ static layer = o.effect(d, d.make);
87
+ };
88
+ //#endregion
89
+ export { d as Cache };
@@ -0,0 +1,5 @@
1
+ import { Context as e } from "effect";
2
+ //#region src/CacheStore.ts
3
+ var t = class extends e.Service()("@voltro/CacheStore") {};
4
+ //#endregion
5
+ export { t as CacheStore };
@@ -0,0 +1,11 @@
1
+ //#region \0rolldown/runtime.js
2
+ var e = Object.defineProperty, t = (t, n) => {
3
+ let r = {};
4
+ for (var i in t) e(r, i, {
5
+ get: t[i],
6
+ enumerable: !0
7
+ });
8
+ return n || e(r, Symbol.toStringTag, { value: "Module" }), r;
9
+ };
10
+ //#endregion
11
+ export { t as __exportAll };
@@ -0,0 +1,114 @@
1
+ import { CacheStore as e } from "./CacheStore.js";
2
+ import { Clock as t, Effect as n, HashMap as r, HashSet as i, Layer as a, Option as o, Ref as s } from "effect";
3
+ //#region src/backendMemory.ts
4
+ var c = {
5
+ entries: r.empty(),
6
+ tagIndex: r.empty(),
7
+ seq: 0,
8
+ oldest: void 0,
9
+ newest: void 0
10
+ }, l = (e, t, n) => {
11
+ let a = e;
12
+ for (let e of n) {
13
+ let n = r.get(a, e);
14
+ if (o.isNone(n)) continue;
15
+ let s = i.remove(n.value, t);
16
+ a = i.size(s) === 0 ? r.remove(a, e) : r.set(a, e, s);
17
+ }
18
+ return a;
19
+ }, u = (e, t) => {
20
+ let n = r.get(e.entries, t);
21
+ if (o.isNone(n)) return e;
22
+ let { older: i, newer: a } = n.value, s = r.remove(e.entries, t);
23
+ if (i !== void 0) {
24
+ let e = r.get(s, i);
25
+ o.isSome(e) && (s = r.set(s, i, {
26
+ ...e.value,
27
+ newer: a
28
+ }));
29
+ }
30
+ if (a !== void 0) {
31
+ let e = r.get(s, a);
32
+ o.isSome(e) && (s = r.set(s, a, {
33
+ ...e.value,
34
+ older: i
35
+ }));
36
+ }
37
+ return {
38
+ entries: s,
39
+ tagIndex: l(e.tagIndex, t, n.value.tags),
40
+ seq: e.seq,
41
+ oldest: i === void 0 ? a : e.oldest,
42
+ newest: a === void 0 ? i : e.newest
43
+ };
44
+ }, d = (e, t, n) => {
45
+ let a = r.get(e, t), s = o.isSome(a) ? i.add(a.value, n) : i.make(n);
46
+ return r.set(e, t, s);
47
+ }, f = (e, t) => {
48
+ let n = r.size(e.entries) - t;
49
+ if (n <= 0) return e;
50
+ let i = e;
51
+ for (let e = 0; e < n; e++) {
52
+ let e = i.oldest;
53
+ if (e === void 0) break;
54
+ i = u(i, e);
55
+ }
56
+ return i;
57
+ }, p = (e, t, n, i, a, s) => {
58
+ let c = u(e, t), l = c.seq + 1, p = c.tagIndex;
59
+ for (let e of a) p = d(p, e, t);
60
+ let m = r.set(c.entries, t, {
61
+ value: n,
62
+ expiresAt: i,
63
+ tags: a,
64
+ seq: l,
65
+ older: c.newest,
66
+ newer: void 0
67
+ });
68
+ if (c.newest !== void 0) {
69
+ let e = r.get(m, c.newest);
70
+ o.isSome(e) && (m = r.set(m, c.newest, {
71
+ ...e.value,
72
+ newer: t
73
+ }));
74
+ }
75
+ let h = {
76
+ entries: m,
77
+ tagIndex: p,
78
+ seq: l,
79
+ oldest: c.oldest ?? t,
80
+ newest: t
81
+ };
82
+ return s === void 0 ? h : f(h, s);
83
+ }, m = (e, t) => t === void 0 ? Infinity : e + t, h = (e) => n.gen(function* () {
84
+ let i = yield* s.make(c), a = e.maxEntries, l = e.now === void 0 ? t.currentTimeMillis : n.sync(e.now);
85
+ return {
86
+ get: (e) => l.pipe(n.flatMap((t) => s.modify(i, (n) => {
87
+ let i = r.get(n.entries, e);
88
+ return o.isNone(i) ? [o.none(), n] : i.value.expiresAt <= t ? [o.none(), u(n, e)] : [o.some(i.value.value), n];
89
+ }))),
90
+ set: (e, t, r) => l.pipe(n.flatMap((n) => s.update(i, (i) => p(i, e, t, m(n, r?.ttlMs), [], a)))),
91
+ setWithTags: (e, t, r) => l.pipe(n.flatMap((n) => s.update(i, (i) => p(i, e, t, m(n, r.ttlMs), r.tags ?? [], a)))),
92
+ remove: (e) => l.pipe(n.flatMap((t) => s.modify(i, (n) => {
93
+ let i = r.get(n.entries, e);
94
+ return o.isNone(i) ? [!1, n] : [i.value.expiresAt > t, u(n, e)];
95
+ }))),
96
+ has: (e) => l.pipe(n.flatMap((t) => s.modify(i, (n) => {
97
+ let i = r.get(n.entries, e);
98
+ return o.isNone(i) ? [!1, n] : i.value.expiresAt <= t ? [!1, u(n, e)] : [!0, n];
99
+ }))),
100
+ clear: () => s.set(i, c),
101
+ invalidateTag: (e) => s.modify(i, (t) => {
102
+ let n = r.get(t.tagIndex, e);
103
+ if (o.isNone(n)) return [0, t];
104
+ let i = t, a = 0;
105
+ for (let e of n.value) i = u(i, e), a += 1;
106
+ return [a, {
107
+ ...i,
108
+ tagIndex: r.remove(i.tagIndex, e)
109
+ }];
110
+ })
111
+ };
112
+ }), g = (t = {}) => a.effect(e, h(t));
113
+ //#endregion
114
+ export { g as layerMemory };
@@ -0,0 +1,56 @@
1
+ import { CacheError as e } from "./errors.js";
2
+ import { CacheStore as t } from "./CacheStore.js";
3
+ import { Clock as n, Effect as r, Layer as i, Option as a } from "effect";
4
+ import { commandRetry as o, connect as s } from "@voltro/kv/connection";
5
+ //#region src/backendRedis.ts
6
+ var c = (t) => s({
7
+ url: t.url,
8
+ driver: t.driver,
9
+ ...t.token === void 0 ? {} : { token: t.token }
10
+ }).pipe(r.mapError((t) => new e({
11
+ operation: "connect",
12
+ key: "",
13
+ cause: String(t)
14
+ }))), l = "\nlocal NOEXP = 9000000000000000\nlocal ttl = tonumber(ARGV[1])\nlocal now = tonumber(ARGV[2])\nlocal score\nif ttl < 0 then score = NOEXP else score = now + ttl end\nredis.call('ZADD', KEYS[1], score, ARGV[3])\nlocal top = redis.call('ZREVRANGE', KEYS[1], 0, 0, 'WITHSCORES')\nlocal maxScore = tonumber(top[2])\nif maxScore >= NOEXP then\n redis.call('PERSIST', KEYS[1])\nelse\n local rem = maxScore - now\n if rem < 1 then rem = 1 end\n redis.call('PEXPIRE', KEYS[1], rem)\nend\nreturn 1\n", u = (t) => r.gen(function* () {
15
+ let i = yield* r.acquireRelease(c(t), (e) => r.promise(() => e.close().catch(() => void 0))), s = t.keyPrefix ?? "voltro:cache", u = (e) => `${s}:v:${e}`, d = (e) => `${s}:t:${e}`, f = (t, n, a) => r.tryPromise({
16
+ try: () => a(i),
17
+ catch: (r) => new e({
18
+ operation: t,
19
+ key: n,
20
+ cause: String(r)
21
+ })
22
+ }).pipe(r.retry(o)), p = (t, n) => r.try({
23
+ try: () => JSON.stringify(n),
24
+ catch: (n) => new e({
25
+ operation: "serialize",
26
+ key: t,
27
+ cause: String(n)
28
+ })
29
+ }), m = (t, n) => r.try({
30
+ try: () => a.some(JSON.parse(n)),
31
+ catch: (n) => new e({
32
+ operation: "deserialize",
33
+ key: t,
34
+ cause: String(n)
35
+ })
36
+ }), h = (e) => f("clear", "", (t) => t.scan(e, `${s}:*`, 200)).pipe(r.flatMap(({ cursor: e, keys: t }) => (t.length > 0 ? f("clear", "", (e) => e.del(t)) : r.succeed(0)).pipe(r.flatMap(() => e === "0" ? r.void : h(e))))), g = (e, t, n) => p(e, t).pipe(r.flatMap((t) => n !== void 0 && n > 0 ? f("set", e, (r) => r.psetex(u(e), Math.ceil(n), t)) : f("set", e, (n) => n.set(u(e), t))), r.asVoid);
37
+ return {
38
+ get: (e) => f("get", e, (t) => t.get(u(e))).pipe(r.flatMap((t) => t === null ? r.succeed(a.none()) : m(e, t))),
39
+ set: (e, t, n) => g(e, t, n?.ttlMs),
40
+ setWithTags: (e, t, i) => g(e, t, i.ttlMs).pipe(r.flatMap(() => {
41
+ let t = i.tags ?? [];
42
+ if (t.length === 0) return r.void;
43
+ let a = i.ttlMs !== void 0 && i.ttlMs > 0 ? Math.ceil(i.ttlMs) : -1;
44
+ return n.currentTimeMillis.pipe(r.flatMap((n) => r.forEach(t, (t) => f("setWithTags", e, (r) => r.eval(l, 1, d(t), a, n, e)), { discard: !0 })));
45
+ })),
46
+ remove: (e) => f("remove", e, (t) => t.del([u(e)])).pipe(r.map((e) => e > 0)),
47
+ has: (e) => f("has", e, (t) => t.exists(u(e))).pipe(r.map((e) => e > 0)),
48
+ clear: () => h("0"),
49
+ invalidateTag: (e) => f("invalidateTag", e, (t) => t.zrange(d(e))).pipe(r.flatMap((t) => f("invalidateTag", e, async (n) => {
50
+ let r = t.map(u), i = r.length > 0 ? await n.del(r) : 0;
51
+ return await n.del([d(e)]), i;
52
+ })))
53
+ };
54
+ }), d = (e) => i.effect(t, u(e));
55
+ //#endregion
56
+ export { d as layerRedis };
package/dist/errors.js ADDED
@@ -0,0 +1,9 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/errors.ts
3
+ var t = class extends e.TaggedError()("CacheError", {
4
+ operation: e.String,
5
+ key: e.String,
6
+ cause: e.String
7
+ }) {};
8
+ //#endregion
9
+ export { t as CacheError };