@voltro/kv 0.85.0 → 0.87.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +110 -0
  3. package/THIRD-PARTY-NOTICES.md +865 -2
  4. package/dist/Kv.js +19 -0
  5. package/dist/KvStore.js +5 -0
  6. package/dist/analytics.d.ts +337 -0
  7. package/dist/analytics.js +16 -0
  8. package/dist/backendMemory.js +36 -0
  9. package/dist/backendRedis.js +48 -0
  10. package/dist/cacheAtomicProtocol.js +247 -0
  11. package/dist/cacheAtomicScript.js +309 -0
  12. package/dist/cacheAtomicState.js +141 -0
  13. package/dist/connection.d.ts +990 -9
  14. package/dist/connection.js +361 -2
  15. package/dist/connectionAnalyticsConfig.js +153 -0
  16. package/dist/connectionConfigProvider.js +111 -0
  17. package/dist/connectionDefinitions.js +285 -0
  18. package/dist/connectionMemory.js +207 -0
  19. package/dist/connectionMetrics.js +79 -0
  20. package/dist/connectionNamespace.js +19 -0
  21. package/dist/connectionNats.js +148 -0
  22. package/dist/connectionNatsConfig.js +87 -0
  23. package/dist/connectionNatsSubjects.js +16 -0
  24. package/dist/connectionOwner.js +44 -0
  25. package/dist/connectionPool.js +738 -0
  26. package/dist/connectionPrefix.js +37 -0
  27. package/dist/connectionScripts.js +4 -0
  28. package/dist/connectionSqlConfig.js +119 -0
  29. package/dist/connectionStorageConfig.js +190 -0
  30. package/dist/connectionStorageFacade.js +78 -0
  31. package/dist/connectionSubscriber.js +130 -0
  32. package/dist/errors.js +13 -0
  33. package/dist/expirySweep.js +26 -0
  34. package/dist/hot-contract.d.ts +297 -0
  35. package/dist/hot-contract.js +18 -0
  36. package/dist/hot-testing.d.ts +229 -0
  37. package/dist/hot-testing.js +1 -0
  38. package/dist/hot.d.ts +936 -0
  39. package/dist/hot.js +7 -0
  40. package/dist/hotMemory.js +46 -0
  41. package/dist/hotOperations.js +223 -0
  42. package/dist/hotRateLimit.js +103 -0
  43. package/dist/hotReadAvailability.js +19 -0
  44. package/dist/hotRedis.js +139 -0
  45. package/dist/hotRedisScript.js +4 -0
  46. package/dist/hotRouting.js +78 -0
  47. package/dist/hotState.js +182 -0
  48. package/dist/hotStoreConformance.js +367 -0
  49. package/dist/index.d.ts +113 -9
  50. package/dist/index.js +7 -115
  51. package/dist/nativeRedisReads.js +78 -0
  52. package/dist/nativeRedisTopology.js +221 -0
  53. package/dist/redisDiagnostics.js +27 -0
  54. package/dist/redisReadPolicy.js +39 -0
  55. package/dist/sql.d.ts +77 -0
  56. package/dist/sql.js +0 -0
  57. package/dist/storage.d.ts +166 -0
  58. package/dist/storage.js +48 -0
  59. package/dist/storageAddress.js +20 -0
  60. package/package.json +43 -3
  61. package/dist/connection-Dsn5fgcH.js +0 -84
@@ -0,0 +1,148 @@
1
+ import { KvError as e } from "./errors.js";
2
+ import { assertNatsSubject as t } from "./connectionNatsSubjects.js";
3
+ import { Effect as n } from "effect";
4
+ //#region src/connectionNats.ts
5
+ var r = new TextEncoder(), i = new TextDecoder(), a = 5e3, o = 1e3, s = async (e, t) => {
6
+ let n;
7
+ try {
8
+ return await Promise.race([e, new Promise((e, r) => {
9
+ n = setTimeout(() => {
10
+ r(/* @__PURE__ */ Error("NATS transport deadline elapsed"));
11
+ }, t);
12
+ })]);
13
+ } finally {
14
+ n !== void 0 && clearTimeout(n);
15
+ }
16
+ }, c = (e) => {
17
+ let n = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Map(), d = !0, f = !1, p, m = (e) => {
18
+ for (let t of c) try {
19
+ t(e);
20
+ } catch {}
21
+ }, h = () => {
22
+ !f && d && (d = !1, m({ kind: "disconnected" }));
23
+ }, g = e.status()[Symbol.asyncIterator](), _ = (async () => {
24
+ try {
25
+ for (;;) {
26
+ let e = await g.next();
27
+ if (e.done) break;
28
+ f || (e.value.type === "disconnect" ? h() : e.value.type === "reconnect" && !d && (d = !0, m({ kind: "reconnected" })));
29
+ }
30
+ } finally {
31
+ h();
32
+ }
33
+ })().catch(() => {
34
+ h();
35
+ }), v = () => {
36
+ if (f) throw Error("NATS transport is closed");
37
+ if (!d) throw Error("NATS transport is disconnected");
38
+ }, y = async (e) => {
39
+ let t = u.get(e);
40
+ t !== void 0 && (t.released = !0, u.delete(e), t.subscription.unsubscribe(), await t.done);
41
+ };
42
+ return {
43
+ publish: async (n, i) => {
44
+ t(n), v(), e.publish(n, r.encode(i));
45
+ },
46
+ flush: async () => {
47
+ v(), await s(e.flush(), a);
48
+ },
49
+ subscribe: async (r) => {
50
+ t(r), v();
51
+ let o = u.get(r);
52
+ if (o !== void 0) return o.ready;
53
+ let c = e.subscribe(r), d = {
54
+ subscription: c,
55
+ released: !1,
56
+ done: Promise.resolve(),
57
+ ready: Promise.resolve()
58
+ };
59
+ u.set(r, d), d.done = (async () => {
60
+ try {
61
+ for await (let e of c) {
62
+ if (f || d.released) continue;
63
+ let t = i.decode(e.data);
64
+ for (let e of n) try {
65
+ e(r, t);
66
+ } catch {}
67
+ }
68
+ } finally {
69
+ d.released || h();
70
+ }
71
+ })().catch(() => {
72
+ d.released || h();
73
+ }), l.add(d.done), d.done.then(() => {
74
+ l.delete(d.done);
75
+ }), d.ready = s(e.flush(), a).then(() => {
76
+ if (v(), d.released) throw Error("NATS subscription is closed");
77
+ }).catch(async (e) => {
78
+ throw u.get(r) === d && await y(r), e;
79
+ }), await d.ready;
80
+ },
81
+ unsubscribe: y,
82
+ onMessage: (e) => {
83
+ if (f) throw Error("NATS transport is closed");
84
+ return n.add(e), () => {
85
+ n.delete(e);
86
+ };
87
+ },
88
+ onTransportEvent: (e) => {
89
+ if (f) throw Error("NATS transport is closed");
90
+ return c.add(e), () => {
91
+ c.delete(e);
92
+ };
93
+ },
94
+ close: () => p ??= (async () => {
95
+ f = !0, n.clear(), c.clear();
96
+ for (let e of u.values()) {
97
+ e.released = !0;
98
+ try {
99
+ e.subscription.unsubscribe();
100
+ } catch {}
101
+ }
102
+ u.clear();
103
+ try {
104
+ await s(e.drain(), o);
105
+ } catch {} finally {
106
+ await e.close(), await g.return?.(), await Promise.all([...l]), await _;
107
+ }
108
+ })()
109
+ };
110
+ }, l = (t) => n.callback((r) => {
111
+ let i = !1, o, s = (async () => {
112
+ let e = await (await import("@nats-io/transport-node")).connect({
113
+ servers: t.url.split(",").map((e) => e.trim()),
114
+ maxReconnectAttempts: -1,
115
+ waitOnFirstConnect: !0,
116
+ reconnectTimeWait: 2e3,
117
+ timeout: a,
118
+ ...t.tls === "required" ? { tls: {} } : {},
119
+ ...t.token === void 0 ? {} : { token: t.token },
120
+ ...t.user === void 0 ? {} : { user: t.user },
121
+ ...t.pass === void 0 ? {} : { pass: t.pass },
122
+ reconnectToServer: () => {
123
+ if (i) throw Error("NATS acquisition cancelled");
124
+ return null;
125
+ }
126
+ });
127
+ if (i) await e.close();
128
+ else {
129
+ try {
130
+ o = c(e);
131
+ } catch (t) {
132
+ throw await e.close(), t;
133
+ }
134
+ r(n.succeed(o));
135
+ }
136
+ })().catch(() => {
137
+ i || r(n.fail(new e({
138
+ operation: "connect",
139
+ key: "",
140
+ cause: "NATS driver could not be opened"
141
+ })));
142
+ });
143
+ return n.promise(async () => {
144
+ i = !0, await s, await o?.close();
145
+ });
146
+ });
147
+ //#endregion
148
+ export { l as connectNats };
@@ -0,0 +1,87 @@
1
+ import { connectionDefinitionsProblem as e, natsConnectionName as t } from "./connectionDefinitions.js";
2
+ import { Config as n, ConfigProvider as r, Effect as i, Option as a } from "effect";
3
+ //#region src/connectionNatsConfig.ts
4
+ var o = (e) => e.toUpperCase().replace(/[^A-Z0-9]+/g, "_"), s = (e) => {
5
+ if (typeof e.url != "string" || !e.url.trim()) return "A NATS connection requires an endpoint";
6
+ for (let t of e.url.split(",")) try {
7
+ let e = new URL(t.trim());
8
+ if (!["nats:", "tls:"].includes(e.protocol) || !e.hostname || e.pathname && e.pathname !== "/" || e.search || e.hash) return "A NATS connection requires nats:// or tls:// servers";
9
+ if (e.username || e.password) return "NATS credentials must use token or user/pass declarations, not endpoint userinfo";
10
+ } catch {
11
+ return "A NATS connection requires valid servers";
12
+ }
13
+ for (let t of [
14
+ e.token,
15
+ e.user,
16
+ e.pass
17
+ ]) if (t !== void 0 && (typeof t != "string" || !t.trim())) return "NATS credentials must be nonempty";
18
+ if (e.user === void 0 != (e.pass === void 0)) return "NATS user and pass must resolve together";
19
+ if (e.token !== void 0 && e.user !== void 0) return "NATS token and user/pass authentication are exclusive";
20
+ if (e.tls !== void 0 && !["auto", "required"].includes(e.tls)) return "Connection tls must be auto or required";
21
+ if (e.keyPrefix !== void 0 && typeof e.keyPrefix != "string") return "Connection keyPrefix must be a string";
22
+ if (e.keyPrefix !== void 0 && (/[\s*>]/u.test(e.keyPrefix) || e.keyPrefix.startsWith(".") || e.keyPrefix.includes(".."))) return "A NATS namespace must preserve exact subjects";
23
+ if (e.allowSharedNamespace !== void 0 && typeof e.allowSharedNamespace != "boolean") return "Connection allowSharedNamespace must be a boolean";
24
+ }, c = (c, l = {}, u) => {
25
+ let d = u === void 0 ? void 0 : Object.freeze({ tenantId: u.tenantId });
26
+ return i.gen(function* () {
27
+ let u = e(l);
28
+ if (u) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: u })));
29
+ if (c.includes(":") && !/^nats:[A-Za-z][A-Za-z0-9_-]*$/.test(c)) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: "A NATS consumer requires a nats:name connection reference" })));
30
+ if (c && !c.includes(":") && !/^[A-Za-z][A-Za-z0-9_-]*$/.test(c)) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: "Malformed NATS connection reference" })));
31
+ let f = t(c, l), p = l.nats ?? {}, m = Object.keys(p), h = Object.hasOwn(p, f) ? p[f] : void 0;
32
+ if (h === void 0 && m.length > 0) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: `Unknown NATS connection ${f || "(default)"}. Known names: ${m.sort().join(", ")}` })));
33
+ let g = `CONNECTIONS_NATS_${o(f)}`, _ = yield* n.String(`${g}_URL`).pipe(n.option), v = yield* n.String(`${g}_TOKEN`).pipe(n.option), y = yield* n.String(`${g}_USER`).pipe(n.option), b = yield* n.String(`${g}_PASS`).pipe(n.option), x = yield* n.String(`${g}_KEY_PREFIX`).pipe(n.option), S = {
34
+ ...a.isSome(_) ? { url: _.value } : {},
35
+ ...a.isSome(v) ? { token: v.value } : {},
36
+ ...a.isSome(y) ? { user: y.value } : {},
37
+ ...a.isSome(b) ? { pass: b.value } : {},
38
+ ...a.isSome(x) ? { keyPrefix: x.value } : {}
39
+ }, C;
40
+ if (h !== void 0 && "_tag" in h) {
41
+ if (!d?.tenantId) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: "A dynamic NATS connection requires tenantId" })));
42
+ C = {
43
+ ...yield* h.resolve(d),
44
+ ...S
45
+ };
46
+ } else if (h !== void 0) {
47
+ let { url: e, token: t, user: o, pass: s, ...c } = h, l = (e) => e === void 0 ? i.succeed(a.none()) : n.String(e.name).pipe(n.option), u = a.isSome(v) ? v : yield* l(t), d = a.isSome(y) ? y : yield* l(o), f = a.isSome(b) ? b : yield* l(s);
48
+ for (let [e, c] of [
49
+ [t, u],
50
+ [o, d],
51
+ [s, f]
52
+ ]) if (e !== void 0 && a.isNone(c)) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: `Missing declared NATS credential ${e.name}` })));
53
+ C = {
54
+ ...c,
55
+ url: a.isSome(_) ? _.value : yield* n.String(e.name),
56
+ ...a.isSome(u) ? { token: u.value } : {},
57
+ ...a.isSome(d) ? { user: d.value } : {},
58
+ ...a.isSome(f) ? { pass: f.value } : {},
59
+ ...a.isSome(x) ? { keyPrefix: x.value } : {}
60
+ };
61
+ } else {
62
+ let e = o(f), t = (t) => n.String(`${e}_NATS_${t}`).pipe(n.orElse(() => n.String(`NATS_${t}`)), n.option), r = a.isSome(v) ? v : yield* t("TOKEN"), i = a.isSome(y) ? y : yield* t("USER"), s = a.isSome(b) ? b : yield* t("PASS");
63
+ C = {
64
+ url: a.isSome(_) ? _.value : yield* n.String(`${e}_NATS_URL`).pipe(n.orElse(() => n.String("NATS_URL"))),
65
+ ...a.isSome(r) ? { token: r.value } : {},
66
+ ...a.isSome(i) ? { user: i.value } : {},
67
+ ...a.isSome(s) ? { pass: s.value } : {},
68
+ ...a.isSome(x) ? { keyPrefix: x.value } : {}
69
+ };
70
+ }
71
+ let w = s(C);
72
+ if (w) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: w })));
73
+ let T = [
74
+ _,
75
+ v,
76
+ y,
77
+ b,
78
+ x
79
+ ].some(a.isSome);
80
+ return {
81
+ ...C,
82
+ source: T ? "override" : h === void 0 ? "environment" : "config"
83
+ };
84
+ });
85
+ };
86
+ //#endregion
87
+ export { s as natsConfigProblem, c as natsConnectionConfig };
@@ -0,0 +1,16 @@
1
+ //#region src/connectionNatsSubjects.ts
2
+ var e = (e) => {
3
+ if (!e || /[\s*>]/u.test(e) || e.split(".").some((e) => !e)) throw Error("Registry NATS channels must be exact nonempty subjects");
4
+ }, t = (e, t = "") => ({
5
+ publish: (n, r) => e.publish(t + n, r),
6
+ subscribe: (n) => e.subscribe(t + n),
7
+ unsubscribe: (n) => e.unsubscribe(t + n),
8
+ onMessage: (n) => e.onMessage((e, r) => {
9
+ e.startsWith(t) && n(e.slice(t.length), r);
10
+ }),
11
+ onTransportEvent: e.onTransportEvent,
12
+ flush: e.flush,
13
+ close: e.close
14
+ });
15
+ //#endregion
16
+ export { e as assertNatsSubject, t as withNatsSubjectPrefix };
@@ -0,0 +1,44 @@
1
+ import { KvError as e } from "./errors.js";
2
+ import { Deferred as t, Effect as n, Scope as r } from "effect";
3
+ //#region src/connectionOwner.ts
4
+ var i = (i) => n.gen(function* () {
5
+ let a = yield* n.scope, o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), c = !1, l = (e) => n.uninterruptible(n.suspend(() => e.closed ? t.await(e.closedSignal) : (e.closed = !0, i.dispose(e).pipe(n.ensuring(n.sync(() => {
6
+ o.delete(e);
7
+ }).pipe(n.andThen(t.succeed(e.closedSignal, void 0))))))));
8
+ return yield* r.addFinalizer(a, n.gen(function* () {
9
+ c = !0, i.onStopping(), yield* n.forEach([...s], t.await, {
10
+ concurrency: "unbounded",
11
+ discard: !0
12
+ }), yield* n.forEach([...o], (e) => n.gen(function* () {
13
+ e.retired = !0, e.active > 0 && !e.closed && (yield* (e.drainUntilReleased ? t.await(e.closedSignal) : n.raceFirst(n.sleep(i.rotationGraceMs), t.await(e.closedSignal))).pipe(n.interruptible)), yield* l(e);
14
+ }), {
15
+ concurrency: "unbounded",
16
+ discard: !0
17
+ }), i.onStopped();
18
+ })), {
19
+ get stopped() {
20
+ return c;
21
+ },
22
+ register: (e) => {
23
+ o.add(e);
24
+ },
25
+ close: l,
26
+ retire: (e) => n.gen(function* () {
27
+ e.retired = !0, e.active === 0 ? yield* l(e) : e.drainUntilReleased || (yield* n.raceFirst(n.sleep(i.rotationGraceMs), t.await(e.closedSignal)).pipe(n.andThen(l(e)), n.interruptible, n.forkIn(a)));
28
+ }),
29
+ release: (e) => n.suspend(() => (e.active--, e.retired && e.active === 0 ? l(e) : n.void)),
30
+ trackOpen: (r) => n.uninterruptibleMask((i) => n.gen(function* () {
31
+ if (c) return yield* n.fail(new e({
32
+ operation: "connect",
33
+ key: "",
34
+ cause: "Connection registry is closed"
35
+ }));
36
+ let a = yield* t.make();
37
+ return s.add(a), yield* i(r).pipe(n.ensuring(n.sync(() => {
38
+ s.delete(a);
39
+ }).pipe(n.andThen(t.succeed(a, void 0)))));
40
+ }))
41
+ };
42
+ });
43
+ //#endregion
44
+ export { i as makeConnectionOwner };