@voltro/kv 0.86.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 (54) hide show
  1. package/CHANGELOG.md +5321 -0
  2. package/README.md +110 -0
  3. package/THIRD-PARTY-NOTICES.md +864 -1
  4. package/dist/analytics.d.ts +337 -0
  5. package/dist/analytics.js +16 -0
  6. package/dist/cacheAtomicProtocol.js +247 -0
  7. package/dist/cacheAtomicScript.js +309 -0
  8. package/dist/cacheAtomicState.js +141 -0
  9. package/dist/connection.d.ts +990 -9
  10. package/dist/connection.js +330 -50
  11. package/dist/connectionAnalyticsConfig.js +153 -0
  12. package/dist/connectionConfigProvider.js +111 -0
  13. package/dist/connectionDefinitions.js +285 -0
  14. package/dist/connectionMemory.js +207 -0
  15. package/dist/connectionMetrics.js +79 -0
  16. package/dist/connectionNamespace.js +19 -0
  17. package/dist/connectionNats.js +148 -0
  18. package/dist/connectionNatsConfig.js +87 -0
  19. package/dist/connectionNatsSubjects.js +16 -0
  20. package/dist/connectionOwner.js +44 -0
  21. package/dist/connectionPool.js +738 -0
  22. package/dist/connectionPrefix.js +37 -0
  23. package/dist/connectionScripts.js +4 -0
  24. package/dist/connectionSqlConfig.js +119 -0
  25. package/dist/connectionStorageConfig.js +190 -0
  26. package/dist/connectionStorageFacade.js +78 -0
  27. package/dist/connectionSubscriber.js +130 -0
  28. package/dist/errors.js +5 -1
  29. package/dist/hot-contract.d.ts +297 -0
  30. package/dist/hot-contract.js +18 -0
  31. package/dist/hot-testing.d.ts +229 -0
  32. package/dist/hot-testing.js +1 -0
  33. package/dist/hot.d.ts +936 -0
  34. package/dist/hot.js +7 -0
  35. package/dist/hotMemory.js +46 -0
  36. package/dist/hotOperations.js +223 -0
  37. package/dist/hotRateLimit.js +103 -0
  38. package/dist/hotReadAvailability.js +19 -0
  39. package/dist/hotRedis.js +139 -0
  40. package/dist/hotRedisScript.js +4 -0
  41. package/dist/hotRouting.js +78 -0
  42. package/dist/hotState.js +182 -0
  43. package/dist/hotStoreConformance.js +367 -0
  44. package/dist/index.d.ts +113 -9
  45. package/dist/nativeRedisReads.js +78 -0
  46. package/dist/nativeRedisTopology.js +221 -0
  47. package/dist/redisDiagnostics.js +27 -0
  48. package/dist/redisReadPolicy.js +39 -0
  49. package/dist/sql.d.ts +77 -0
  50. package/dist/sql.js +0 -0
  51. package/dist/storage.d.ts +166 -0
  52. package/dist/storage.js +48 -0
  53. package/dist/storageAddress.js +20 -0
  54. package/package.json +43 -3
@@ -1,27 +1,97 @@
1
1
  import { KvError as e } from "./errors.js";
2
- import { Config as t, Context as n, Effect as r, HashMap as i, Layer as a, Option as o, Schedule as s, Scope as c, SynchronizedRef as l } from "effect";
2
+ import { withRespKeyPrefix as t } from "./connectionPrefix.js";
3
+ import { dynamicSql as n } from "./connectionSqlConfig.js";
4
+ import { dynamicAnalytics as r } from "./connectionAnalyticsConfig.js";
5
+ import { byTenant as i, connectionDefinitionsProblem as a, connectionMetadataProblem as o, declaredConnectionEnv as s, dynamic as c, dynamicNats as l, env as u, memory as d, redisConnectionName as f, redisTimeoutsProblem as p, roundRobin as m, shardBy as h } from "./connectionDefinitions.js";
6
+ import { createNativeRedisConnection as g, disconnectNativeRedis as _, nativeRedisTopology as v, scanNativeRedis as y } from "./nativeRedisTopology.js";
7
+ import { connectRedisSubscriber as b } from "./connectionSubscriber.js";
8
+ import { dynamicStorage as x } from "./connectionStorageConfig.js";
9
+ import { withConnectionRegistryConfigProvider as S } from "./connectionConfigProvider.js";
10
+ import { ConnectionRegistry as C, layerConfiguredConnections as w, makeConnectionRegistry as T } from "./connectionPool.js";
11
+ import { httpRedisReaders as ee } from "./redisReadPolicy.js";
12
+ import { nativeRedisReadConfigs as E, openNativeRedisReaders as te } from "./nativeRedisReads.js";
13
+ import { readRedisNodeDiagnostics as ne } from "./redisDiagnostics.js";
14
+ import { CACHE_FENCE_RETENTION_MS as D, CacheAtomicProtocolError as O, cacheAtomicArray as k, cacheAtomicComponent as A, cacheAtomicKeys as re, cacheAtomicMember as ie, cacheAtomicMembership as ae, cacheAtomicRoot as oe, cacheAtomicScanPage as j, cacheAtomicSnapshot as M, cacheAtomicTtl as N, cacheAtomicValueSnapshots as P, decodeCacheAtomicCommand as F, decodeCacheAtomicPolicy as I, decodeCacheAtomicReply as L, isCacheAtomicRead as R } from "./cacheAtomicProtocol.js";
15
+ import { CACHE_ATOMIC_SCRIPT as z } from "./cacheAtomicScript.js";
16
+ import { Config as B, ConfigProvider as V, Context as H, Effect as U, Layer as W, Option as G, Schedule as K } from "effect";
3
17
  //#region src/connection.ts
4
- var u = (e) => ({
5
- get: (t) => e.get(t),
6
- set: (t, n) => e.set(t, n),
7
- psetex: (t, n, r) => e.psetex(t, n, r),
8
- del: (t) => t.length === 0 ? Promise.resolve(0) : e.del(...t),
9
- exists: (t) => e.exists(t),
10
- eval: (t, n, ...r) => e.eval(t, n, ...r),
11
- zrange: (t) => e.zrange(t, 0, -1),
12
- scan: async (t, n, r) => {
13
- let [i, a] = await e.scan(t, "MATCH", n, "COUNT", r);
14
- return {
15
- cursor: i,
16
- keys: a
17
- };
18
+ var q = (e) => new B.ConfigError(new V.SourceError({ message: e })), se = (e, t) => {
19
+ let n = e.client;
20
+ return {
21
+ readOnly: {
22
+ get: async (e) => {
23
+ let n = await t.select();
24
+ return await n.ready(), n.client.get(e);
25
+ },
26
+ exists: async (e) => {
27
+ let n = await t.select();
28
+ return await n.ready(), n.client.exists(e);
29
+ },
30
+ zrange: async (e) => {
31
+ let n = await t.select();
32
+ return await n.ready(), n.client.zrange(e, 0, -1);
33
+ },
34
+ eval: async (e, n, ...r) => {
35
+ let i = await t.select();
36
+ return await i.ready(), i.client.call("eval_ro", e, n, ...r);
37
+ }
38
+ },
39
+ ping: async () => (await e.ready(), n.ping()),
40
+ diagnostics: async () => {
41
+ await e.ready();
42
+ let t = e.kind === "cluster" ? n.nodes("master") : [n];
43
+ return { nodes: await Promise.all(t.map((e) => ne({
44
+ info: (t) => e.info(t),
45
+ config: (t, n) => e.config(t, n)
46
+ }))) };
47
+ },
48
+ onReconnect: (t) => {
49
+ let r = () => {
50
+ e.ready().then(t).catch(() => {});
51
+ };
52
+ return n.on("ready", r), () => {
53
+ n.off("ready", r);
54
+ };
55
+ },
56
+ get: async (t) => (await e.ready(), n.get(t)),
57
+ set: async (t, r) => (await e.ready(), n.set(t, r)),
58
+ psetex: async (t, r, i) => (await e.ready(), n.psetex(t, r, i)),
59
+ del: async (t) => {
60
+ if (t.length === 0) return 0;
61
+ if (await e.ready(), e.kind !== "cluster") return n.del(...t);
62
+ let r = 0;
63
+ for (let e of t) r += await n.del(e);
64
+ return r;
65
+ },
66
+ exists: async (t) => (await e.ready(), n.exists(t)),
67
+ eval: async (t, r, ...i) => (await e.ready(), n.eval(t, r, ...i)),
68
+ zrange: async (t) => (await e.ready(), n.zrange(t, 0, -1)),
69
+ scan: async (t, n, r) => (await e.ready(), y(e, t, n, r)),
70
+ close: async () => {
71
+ let r, i = () => {
72
+ try {
73
+ n.disconnect();
74
+ } catch {}
75
+ };
76
+ try {
77
+ await Promise.race([n.quit().then(() => void 0).catch(i), new Promise((e) => {
78
+ r = setTimeout(() => {
79
+ i(), e();
80
+ }, 1e3);
81
+ })]);
82
+ } finally {
83
+ r !== void 0 && clearTimeout(r), await Promise.all([t.close(), _(e)]).then(() => void 0);
84
+ }
85
+ }
86
+ };
87
+ }, J = (e) => ({
88
+ readOnly: {
89
+ get: (t) => e.get(t),
90
+ exists: (t) => e.exists(t),
91
+ zrange: (t) => e.zrange(t, 0, -1),
92
+ eval: (t, n, ...r) => e.evalRo === void 0 ? Promise.reject(/* @__PURE__ */ Error("HTTP read-only Lua is unavailable")) : e.evalRo(t, r.slice(0, n), r.slice(n))
18
93
  },
19
- close: () => e.quit().then(() => void 0).catch(() => {
20
- try {
21
- e.disconnect();
22
- } catch {}
23
- })
24
- }), d = (e) => ({
94
+ ping: () => e.ping(),
25
95
  get: (t) => e.get(t),
26
96
  set: (t, n) => e.set(t, n),
27
97
  psetex: (t, n, r) => e.psetex(t, n, r),
@@ -40,42 +110,252 @@ var u = (e) => ({
40
110
  };
41
111
  },
42
112
  close: () => Promise.resolve()
43
- }), f = (e) => e.toUpperCase().replace(/[^A-Z0-9]+/g, "_"), p = ["resp", "http"], m = (e) => r.gen(function* () {
44
- let n = f(e), r = yield* t.String(`${n}_REDIS_URL`).pipe(t.orElse(() => t.String("REDIS_URL"))), i = yield* t.Literals(p, `${n}_REDIS_DRIVER`).pipe(t.option), a = o.isSome(i) ? i.value : yield* t.Literals(p, "REDIS_DRIVER").pipe(t.withDefault("resp")), s = yield* t.String(`${n}_REDIS_TOKEN`).pipe(t.orElse(() => t.String("REDIS_TOKEN")), t.orElse(() => t.String("UPSTASH_REDIS_REST_TOKEN")), t.option), c = yield* t.String(`${n}_REDIS_KEY_PREFIX`).pipe(t.option);
113
+ }), Y = (e) => e.toUpperCase().replace(/[^A-Z0-9]+/g, "_"), X = (e) => {
114
+ if (![
115
+ "resp",
116
+ "http",
117
+ "memory"
118
+ ].includes(e.driver)) return "Redis driver must be resp, http or memory";
119
+ if (typeof e.url != "string" || !e.url.trim()) return "Redis URL must be a nonempty string";
120
+ let t = o(e);
121
+ if (t) return t;
122
+ let n = p(e, e.driver);
123
+ if (n) return n;
124
+ if (e.token !== void 0 && (typeof e.token != "string" || !e.token)) return "Redis token must be a nonempty string";
125
+ if (e.cluster !== void 0 && typeof e.cluster != "boolean") return "Redis cluster must be a boolean";
126
+ if (e.readFrom !== void 0 && ![
127
+ "primary",
128
+ "replica",
129
+ "nearest"
130
+ ].includes(e.readFrom)) return "Redis readFrom must be primary, replica or nearest";
131
+ if (e.replicas !== void 0 && (!Array.isArray(e.replicas) || e.replicas.some((e) => typeof e != "string" || !e.trim()))) return "Redis replicas must be nonempty URL strings";
132
+ if (e.driver === "memory" && ((e.replicas?.length ?? 0) > 0 || e.readFrom !== void 0 && e.readFrom !== "primary")) return "Local memory does not declare network readers";
133
+ if (e.driver === "http") {
134
+ if (e.cluster === !0) return "HTTP Redis does not declare native Cluster topology";
135
+ if (e.readFrom === "replica" && !e.replicas?.length) return "HTTP replica reads require replica endpoints";
136
+ try {
137
+ for (let t of [e.url, ...e.replicas ?? []]) {
138
+ let n = new URL(t);
139
+ if (!["http:", "https:"].includes(n.protocol) || n.username || n.password || n.hash || t !== t.trim() || e.tls === "required" && n.protocol !== "https:") return "HTTP Redis endpoints require admitted HTTP URLs and TLS policy";
140
+ }
141
+ } catch {
142
+ return "HTTP Redis endpoint is invalid";
143
+ }
144
+ }
145
+ if (e.persistence !== void 0 && ![
146
+ "required",
147
+ "optional",
148
+ "none"
149
+ ].includes(e.persistence)) return "Connection persistence must be required, optional or none";
150
+ if (e.maxmemoryPolicyHint !== void 0 && typeof e.maxmemoryPolicyHint != "string") return "Redis maxmemoryPolicyHint must be a string";
151
+ }, Z = (e) => {
152
+ let t = X(e);
153
+ return t ? U.fail(q(t)) : e.driver === "resp" ? U.try({
154
+ try: () => {
155
+ let t = v(e);
156
+ if (E(e), e.readFrom === "replica" && t.kind === "standalone" && !e.replicas?.length) throw Error("Standalone replica reads require replica endpoints");
157
+ return t.kind === "cluster" && e.cluster === void 0 ? {
158
+ ...e,
159
+ cluster: !0
160
+ } : e;
161
+ },
162
+ catch: () => q("Invalid native Redis topology declaration")
163
+ }) : U.succeed(e);
164
+ }, Q = ["resp", "http"], ce = (e, t = {}) => U.gen(function* () {
165
+ let n = Y(e), r = t.url ?? (yield* B.String(`${n}_REDIS_URL`).pipe(B.orElse(() => B.String("REDIS_URL")))), i = t.driver === void 0 ? yield* B.Literals(Q, `${n}_REDIS_DRIVER`).pipe(B.option) : G.none(), a = t.driver ?? (G.isSome(i) ? i.value : yield* B.Literals(Q, "REDIS_DRIVER").pipe(B.withDefault("resp"))), o = t.token === void 0 ? yield* B.String(`${n}_REDIS_TOKEN`).pipe(B.orElse(() => B.String("REDIS_TOKEN")), B.orElse(() => B.String("UPSTASH_REDIS_REST_TOKEN")), B.option) : G.some(t.token), s = t.keyPrefix === void 0 ? yield* B.String(`${n}_REDIS_KEY_PREFIX`).pipe(B.option) : G.some(t.keyPrefix);
45
166
  return {
46
167
  url: r,
47
168
  driver: a,
48
- ...o.isSome(s) ? { token: s.value } : {},
49
- ...o.isSome(c) ? { keyPrefix: c.value } : {}
169
+ ...G.isSome(o) ? { token: o.value } : {},
170
+ ...G.isSome(s) ? { keyPrefix: s.value } : {},
171
+ ...t.source === void 0 ? {} : { source: t.source },
172
+ ...t.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: t.connectTimeoutMs },
173
+ ...t.commandTimeoutMs === void 0 ? {} : { commandTimeoutMs: t.commandTimeoutMs }
50
174
  };
51
- }), h = (t) => r.tryPromise({
52
- try: async () => {
53
- if (t.driver === "http") {
54
- let e = await import("@upstash/redis"), n = typeof e.default == "function" ? e.default : e.default?.Redis, r = e.Redis ?? n;
55
- if (!r) throw Error("@upstash/redis: Redis constructor not found in module");
56
- return d(new r({
57
- url: t.url,
58
- token: t.token,
59
- automaticDeserialization: !1
60
- }));
175
+ }).pipe(U.flatMap(Z));
176
+ function le(e, t, n) {
177
+ let r = n === void 0 ? void 0 : Object.freeze({ tenantId: n.tenantId });
178
+ return U.gen(function* () {
179
+ let n = a(t ?? {});
180
+ if (n) return yield* U.fail(q(n));
181
+ let i = e.includes(":"), o = i ? e.slice(e.indexOf(":") + 1) : e;
182
+ if (i && !e.startsWith("redis:")) return yield* U.fail(q("A Redis consumer requires a redis:name connection reference"));
183
+ if (i && o && !/^[A-Za-z][A-Za-z0-9_-]*$/.test(o)) return yield* U.fail(q("Malformed Redis connection reference"));
184
+ let s = t?.redis ?? {}, c = Object.keys(s), l = f(o, t ?? {}), u = Object.hasOwn(s, l) ? s[l] : void 0;
185
+ if (!u && c.length > 0) return yield* U.fail(q(`Unknown Redis connection ${l || "(default)"}. Known names: ${c.sort().join(", ") || "(none)"}`));
186
+ let d = `CONNECTIONS_REDIS_${Y(l)}`, p = yield* B.String(`${d}_URL`).pipe(B.option), m = yield* B.Literals(["resp", "http"], `${d}_DRIVER`).pipe(B.option), h = yield* B.String(`${d}_TOKEN`).pipe(B.option), g = yield* B.String(`${d}_KEY_PREFIX`).pipe(B.option), _ = {
187
+ ...G.isSome(p) ? { url: p.value } : {},
188
+ ...G.isSome(m) ? { driver: m.value } : {},
189
+ ...G.isSome(h) ? { token: h.value } : {},
190
+ ...G.isSome(g) ? { keyPrefix: g.value } : {},
191
+ ...[
192
+ p,
193
+ m,
194
+ h,
195
+ g
196
+ ].some(G.isSome) ? { source: "override" } : {}
197
+ };
198
+ if (u && "_tag" in u && u._tag === "MemoryRedisConnection") return [
199
+ p,
200
+ m,
201
+ h,
202
+ g
203
+ ].some(G.isSome) ? yield* U.fail(q("A local memory connection cannot be redirected by transport environment overrides")) : {
204
+ url: `memory:${l}`,
205
+ driver: "memory",
206
+ source: "config",
207
+ ...u.now === void 0 ? {} : { memoryClock: u.now }
208
+ };
209
+ if (u && "_tag" in u) {
210
+ if (!r?.tenantId) return yield* U.fail(q("A dynamic Redis connection requires tenantId"));
211
+ let e = {
212
+ ...yield* u.resolve(r),
213
+ ..._
214
+ }, t = X(e);
215
+ return t ? yield* U.fail(q(t)) : e.driver === "memory" ? yield* U.fail(q("Local memory connections require a memory() declaration, not a tenant credential resolver")) : e.keyPrefix !== void 0 && typeof e.keyPrefix != "string" ? yield* U.fail(q("Connection keyPrefix must be a string")) : e.allowSharedNamespace !== void 0 && typeof e.allowSharedNamespace != "boolean" ? yield* U.fail(q("Connection allowSharedNamespace must be a boolean")) : e.driver === "http" && !e.token ? yield* U.fail(q("An HTTP Redis connection requires a declared token")) : e.tls === "required" && !/^rediss(?:\+cluster|\+sentinel)?:\/\//i.test(e.url) && !(e.driver === "http" && /^https:\/\//i.test(e.url)) ? yield* U.fail(q("A Redis connection with tls: required needs a secure Redis scheme or HTTPS")) : yield* Z(e);
216
+ }
217
+ if (!u) return yield* ce(l || e, _);
218
+ let v = G.isSome(p) ? p.value : yield* B.String(u.url.name), y = G.isSome(m) ? m.value : u.driver ?? "resp", b = G.isSome(h) ? G.some(h.value) : u.token ? G.some(yield* B.String(u.token.name)) : G.none();
219
+ if (y === "http" && G.isNone(b)) return yield* U.fail(q("An HTTP Redis connection requires a declared token"));
220
+ let x = G.isSome(g) ? g.value : u.keyPrefix, S = u.cluster ?? /^rediss?\+cluster:\/\//i.test(v);
221
+ if (u.tls === "required" && !/^rediss(?:\+cluster|\+sentinel)?:\/\//i.test(v) && !(y === "http" && /^https:\/\//i.test(v))) return yield* U.fail(q("A Redis connection with tls: required needs a secure Redis scheme or HTTPS"));
222
+ let C = yield* U.forEach(u.replicas ?? [], (e) => B.String(e.name));
223
+ return yield* Z({
224
+ url: v,
225
+ driver: y,
226
+ cluster: S,
227
+ source: [
228
+ p,
229
+ m,
230
+ h,
231
+ g
232
+ ].some(G.isSome) ? "override" : "config",
233
+ ...G.isSome(b) ? { token: b.value } : {},
234
+ ...x === void 0 ? {} : { keyPrefix: x },
235
+ ...u.allowSharedNamespace === void 0 ? {} : { allowSharedNamespace: u.allowSharedNamespace },
236
+ ...u.tls === void 0 ? {} : { tls: u.tls },
237
+ ...u.persistence === void 0 ? {} : { persistence: u.persistence },
238
+ ...u.region === void 0 ? {} : { region: u.region },
239
+ ...u.label === void 0 ? {} : { label: u.label },
240
+ ...u.acl === void 0 ? {} : { acl: u.acl },
241
+ ...u.readFrom === void 0 ? {} : { readFrom: u.readFrom },
242
+ ...u.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: u.connectTimeoutMs },
243
+ ...u.commandTimeoutMs === void 0 ? {} : { commandTimeoutMs: u.commandTimeoutMs },
244
+ ...C.length > 0 ? { replicas: C } : {},
245
+ ...u.maxmemoryPolicyHint === void 0 ? {} : { maxmemoryPolicyHint: u.maxmemoryPolicyHint }
246
+ });
247
+ });
248
+ }
249
+ var ue = (n) => U.tryPromise({
250
+ try: async (e) => {
251
+ e.throwIfAborted();
252
+ let t = X(n);
253
+ if (t) throw Error(t);
254
+ if (n.driver === "resp" && E(n), n.driver === "memory") {
255
+ let { makeMemoryConnectionState: e } = await import("./connectionMemory.js"), t = e(n.memoryClock), r = t.command();
256
+ return {
257
+ ...r,
258
+ close: async () => {
259
+ await r.close(), t.dispose();
260
+ }
261
+ };
262
+ }
263
+ if (n.driver === "http") {
264
+ let t = await import("@upstash/redis");
265
+ e.throwIfAborted();
266
+ let r = typeof t.default == "function" ? t.default : t.default?.Redis, i = t.Redis ?? r;
267
+ if (!i) throw Error("@upstash/redis: Redis constructor not found in module");
268
+ let a = async (t) => {
269
+ let r = new AbortController(), a = () => {
270
+ r.abort();
271
+ };
272
+ e.addEventListener("abort", a, { once: !0 });
273
+ try {
274
+ e.throwIfAborted();
275
+ let a = new i({
276
+ url: t,
277
+ token: n.token,
278
+ automaticDeserialization: !1,
279
+ retry: !1,
280
+ signal: r.signal
281
+ });
282
+ if (await a.ping() !== "PONG") throw Error("HTTP reader readiness failed");
283
+ let o = J(a);
284
+ if (Number(await o.readOnly.eval("return 1", 0)) !== 1) throw Error("HTTP read-only Lua readiness failed");
285
+ return e.throwIfAborted(), {
286
+ ...o,
287
+ close: async () => {
288
+ r.abort();
289
+ }
290
+ };
291
+ } catch (e) {
292
+ throw r.abort(), e;
293
+ } finally {
294
+ e.removeEventListener("abort", a);
295
+ }
296
+ }, o = new AbortController(), s = () => {
297
+ o.abort();
298
+ };
299
+ e.addEventListener("abort", s, { once: !0 });
300
+ try {
301
+ e.throwIfAborted();
302
+ let t = new i({
303
+ url: n.url,
304
+ token: n.token,
305
+ automaticDeserialization: !1,
306
+ retry: !1,
307
+ signal: o.signal
308
+ });
309
+ if (await t.ping() !== "PONG") throw Error("HTTP primary readiness failed");
310
+ e.throwIfAborted();
311
+ let r = {
312
+ ...J(t),
313
+ close: async () => {
314
+ o.abort();
315
+ }
316
+ }, s = [];
317
+ try {
318
+ if (n.readFrom !== void 0 && n.readFrom !== "primary") for (let e of n.replicas ?? []) s.push(await a(e));
319
+ let t = ee(r, s, n.readFrom);
320
+ return (n.readFrom === "replica" || n.readFrom === "nearest") && await t.readOnly.eval("return 1", 0), e.throwIfAborted(), t;
321
+ } catch (e) {
322
+ throw await Promise.all([r, ...s].map((e) => e.close())), e;
323
+ }
324
+ } catch (e) {
325
+ throw o.abort(), e;
326
+ } finally {
327
+ e.removeEventListener("abort", s);
328
+ }
329
+ }
330
+ let r = await g(n), i = r.client;
331
+ i.on("error", () => {});
332
+ let a = () => {
333
+ i.disconnect();
334
+ };
335
+ e.addEventListener("abort", a, { once: !0 });
336
+ try {
337
+ e.throwIfAborted(), await i.connect(), await r.ready(), e.throwIfAborted();
338
+ } catch (e) {
339
+ throw await _(r), e;
340
+ } finally {
341
+ e.removeEventListener("abort", a);
342
+ }
343
+ try {
344
+ let t = await te(n, r, e);
345
+ try {
346
+ return e.throwIfAborted(), se(r, t);
347
+ } catch (e) {
348
+ throw await t.close(), e;
349
+ }
350
+ } catch (e) {
351
+ throw await _(r), e;
61
352
  }
62
- let e = (await import("ioredis")).default, n = new e(t.url);
63
- return n.on("error", () => {}), u(n);
64
353
  },
65
- catch: (t) => new e({
354
+ catch: () => new e({
66
355
  operation: "connect",
67
356
  key: "",
68
- cause: String(t)
357
+ cause: n.keyPrefix !== void 0 && typeof n.keyPrefix != "string" ? "Connection keyPrefix must be a string" : "Redis connection could not be opened"
69
358
  })
70
- }), g = s.max([s.exponential("10 millis"), s.recurs(2)]), _ = class extends n.Service()("@voltro/RedisConnections") {}, v = a.effect(_, r.gen(function* () {
71
- let e = yield* r.scope, t = yield* l.make(i.empty()), n = (e) => m(e);
72
- return {
73
- client: (a) => l.modifyEffect(t, (t) => {
74
- let s = i.get(t, a);
75
- return o.isSome(s) ? r.succeed([s.value, t]) : n(a).pipe(r.flatMap(h), r.tap((t) => c.addFinalizer(e, r.promise(() => t.close().catch(() => void 0)))), r.map((e) => [e, i.set(t, a, e)]));
76
- }),
77
- config: n
78
- };
79
- }));
359
+ }).pipe(U.map((e) => t(e, n.keyPrefix))), de = K.max([K.exponential("10 millis"), K.recurs(2)]), $ = class extends H.Service()("@voltro/RedisConnections") {}, fe = W.effect($, U.suspend(() => T()).pipe(U.orDie, U.map((e) => e.redis)));
80
360
  //#endregion
81
- export { _ as RedisConnections, g as commandRetry, h as connect, m as connectionConfig, v as layerConnections };
361
+ export { z as CACHE_ATOMIC_SCRIPT, D as CACHE_FENCE_RETENTION_MS, O as CacheAtomicProtocolError, C as ConnectionRegistry, $ as RedisConnections, i as byTenant, k as cacheAtomicArray, A as cacheAtomicComponent, re as cacheAtomicKeys, ie as cacheAtomicMember, ae as cacheAtomicMembership, oe as cacheAtomicRoot, j as cacheAtomicScanPage, M as cacheAtomicSnapshot, N as cacheAtomicTtl, P as cacheAtomicValueSnapshots, de as commandRetry, ue as connect, b as connectRedisSubscriber, le as connectionConfig, a as connectionDefinitionsProblem, s as declaredConnectionEnv, F as decodeCacheAtomicCommand, I as decodeCacheAtomicPolicy, L as decodeCacheAtomicReply, c as dynamic, r as dynamicAnalytics, l as dynamicNats, n as dynamicSql, x as dynamicStorage, u as env, R as isCacheAtomicRead, w as layerConfiguredConnections, fe as layerConnections, T as makeConnectionRegistry, d as memory, m as roundRobin, h as shardBy, S as withConnectionRegistryConfigProvider };
@@ -0,0 +1,153 @@
1
+ import { connectionDefinitionsProblem as e, connectionMetadataProblem as t } from "./connectionDefinitions.js";
2
+ import { Config as n, ConfigProvider as r, Effect as i, Option as a } from "effect";
3
+ //#region src/connectionAnalyticsConfig.ts
4
+ var o = (e, t = {}) => {
5
+ let n = t.ttlMs ?? 6e4;
6
+ if (typeof e != "function" || !Number.isSafeInteger(n) || n <= 0) throw Error("Dynamic analytics requires a resolver and positive ttlMs");
7
+ return Object.freeze({
8
+ _tag: "DynamicAnalyticsConnection",
9
+ resolve: e,
10
+ ttlMs: n
11
+ });
12
+ }, s = [
13
+ "url",
14
+ "username",
15
+ "password",
16
+ "token",
17
+ "apiUrl",
18
+ "apiKey",
19
+ "host"
20
+ ], c = {
21
+ duckdb: [
22
+ "path",
23
+ "mirrorTables",
24
+ "mirrorPrimaryKey"
25
+ ],
26
+ clickhouse: [
27
+ "url",
28
+ "username",
29
+ "password",
30
+ "database",
31
+ "table",
32
+ "batch",
33
+ "mirrorTables",
34
+ "mirrorPrimaryKey"
35
+ ],
36
+ tinybird: [
37
+ "token",
38
+ "apiUrl",
39
+ "region",
40
+ "datasource",
41
+ "pipes"
42
+ ],
43
+ posthog: [
44
+ "apiKey",
45
+ "host",
46
+ "batch"
47
+ ]
48
+ }, l = [
49
+ "provider",
50
+ "tls",
51
+ "persistence",
52
+ "keyPrefix",
53
+ "allowSharedNamespace",
54
+ "region",
55
+ "label",
56
+ "acl",
57
+ "source"
58
+ ], u = (e) => typeof e == "object" && !!e && !Array.isArray(e), d = (e) => u(e) && e._tag === "ConnectionEnv" && typeof e.name == "string" && /^[A-Z][A-Z0-9_]*$/.test(e.name), f = (e) => typeof e == "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(e), p = (e) => {
59
+ let n = e.provider;
60
+ if (typeof n != "string" || !Object.hasOwn(c, n)) return "Analytics requires an implemented provider";
61
+ let r = c[n];
62
+ if (Object.keys(e).some((e) => !r.includes(e) && !l.includes(e))) return "Analytics options must belong to the selected provider";
63
+ let i = t(e);
64
+ if (i) return i;
65
+ if (e.persistence !== void 0 && ![
66
+ "required",
67
+ "optional",
68
+ "none"
69
+ ].includes(String(e.persistence))) return "Analytics persistence must be required, optional or none";
70
+ for (let t of [
71
+ "database",
72
+ "table",
73
+ "datasource",
74
+ "mirrorPrimaryKey"
75
+ ]) if (e[t] !== void 0 && !f(e[t])) return "Analytics SQL and datasource names must be identifiers";
76
+ if (e.mirrorTables !== void 0 && (!Array.isArray(e.mirrorTables) || !e.mirrorTables.every(f))) return "Analytics mirror tables must be identifiers";
77
+ if (e.pipes !== void 0) {
78
+ let t = e.pipes;
79
+ if (!u(t) || Object.keys(t).some((e) => ![
80
+ "aggregate",
81
+ "timeseries",
82
+ "topN"
83
+ ].includes(e)) || Object.values(t).some((e) => e !== void 0 && !f(e))) return "Tinybird pipes must name implemented read capabilities";
84
+ }
85
+ let a = e.batch;
86
+ if (a !== void 0 && a !== !1 && (!u(a) || Object.keys(a).some((e) => !["maxSize", "flushIntervalMs"].includes(e)) || Object.values(a).some((e) => e !== void 0 && (typeof e != "number" || !Number.isSafeInteger(e) || e <= 0)))) return "Analytics batch limits must be positive safe integers";
87
+ if (a === !1 && n !== "clickhouse") return "Only ClickHouse accepts batch false";
88
+ if (e.source !== void 0 && ![
89
+ "config",
90
+ "environment",
91
+ "override"
92
+ ].includes(String(e.source))) return "Analytics source metadata is invalid";
93
+ }, m = (e) => {
94
+ if (!u(e)) return "Analytics declarations must be named objects";
95
+ if ("_tag" in e) return e._tag === "DynamicAnalyticsConnection" && typeof e.resolve == "function" && typeof e.ttlMs == "number" && Number.isSafeInteger(e.ttlMs) && e.ttlMs > 0 ? void 0 : "Dynamic analytics requires a resolver and positive ttlMs";
96
+ let t = p(e);
97
+ if (t) return t;
98
+ for (let t of s) if (e[t] !== void 0 && !d(e[t])) return "Analytics endpoints and credentials must use env() declarations";
99
+ if (e.path !== void 0 && typeof e.path != "string" && !d(e.path)) return "DuckDB path must be a string or env() declaration";
100
+ if (e.provider === "clickhouse" && !d(e.url)) return "ClickHouse requires a declared URL";
101
+ if (e.provider === "tinybird" && !d(e.token)) return "Tinybird requires a declared token";
102
+ if (e.provider === "posthog" && !d(e.apiKey)) return "PostHog requires a declared API key";
103
+ }, h = (e) => {
104
+ if (!u(e)) return "Analytics configuration must be a named object";
105
+ let t = p(e);
106
+ if (t) return t;
107
+ for (let t of [...s, "path"]) if (e[t] !== void 0 && (typeof e[t] != "string" || !e[t].trim())) return "Analytics endpoints, credentials and paths must be nonempty strings";
108
+ for (let t of [
109
+ "url",
110
+ "apiUrl",
111
+ "host"
112
+ ]) if (e[t] !== void 0) try {
113
+ let n = new URL(String(e[t]));
114
+ if (!["http:", "https:"].includes(n.protocol) || n.username || n.password || n.search || n.hash) return "Analytics endpoints require HTTP(S) without credentials or query";
115
+ if (e.tls === "required" && n.protocol !== "https:") return "Analytics TLS policy requires HTTPS";
116
+ } catch {
117
+ return "Analytics endpoints must be valid HTTP(S) URLs";
118
+ }
119
+ if (e.provider === "clickhouse" && !e.url) return "ClickHouse requires a resolved URL";
120
+ if (e.provider === "tinybird" && !e.token) return "Tinybird requires a resolved token";
121
+ if (e.provider === "posthog" && !e.apiKey) return "PostHog requires a resolved API key";
122
+ }, g = (e, t) => {
123
+ let n = e.replace(/^analytics:/, ""), r = Object.keys(t.analytics ?? {});
124
+ return n || t.defaults?.analytics || (r.length === 1 ? r[0] : "");
125
+ }, _ = (t, o, c) => i.gen(function* () {
126
+ let l = e(o);
127
+ if (l) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: l })));
128
+ if (t !== "analytics:" && !/^analytics:[A-Za-z][A-Za-z0-9_-]*$/.test(t)) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: "Analytics requires an analytics:name reference" })));
129
+ let u = g(t, o), f = o.analytics ?? {}, p = Object.hasOwn(f, u) ? f[u] : void 0;
130
+ if (p === void 0) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: `Unknown analytics connection ${u || "(default)"}. Known names: ${Object.keys(f).sort().join(", ")}` })));
131
+ let m;
132
+ if ("_tag" in p) {
133
+ if (!c?.tenantId) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: "Dynamic analytics requires tenantId" })));
134
+ m = { ...yield* p.resolve(Object.freeze({ tenantId: c.tenantId })) };
135
+ } else m = { ...p };
136
+ let _ = !1, v = `CONNECTIONS_ANALYTICS_${u.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`;
137
+ for (let e of [
138
+ ...s,
139
+ "path",
140
+ "keyPrefix",
141
+ "region"
142
+ ]) {
143
+ let t = e.replace(/[A-Z]/g, (e) => `_${e}`).toUpperCase(), r = yield* n.String(`${v}_${t}`).pipe(n.option);
144
+ a.isSome(r) ? (_ = !0, m[e] = r.value) : d(m[e]) && (m[e] = yield* n.String(m[e].name));
145
+ }
146
+ let y = h(m);
147
+ return y ? yield* i.fail(new n.ConfigError(new r.SourceError({ message: y }))) : {
148
+ ...m,
149
+ source: _ ? "override" : "config"
150
+ };
151
+ });
152
+ //#endregion
153
+ export { h as analyticsConfigProblem, _ as analyticsConnectionConfig, g as analyticsConnectionName, m as analyticsDefinitionProblem, o as dynamicAnalytics };
@@ -0,0 +1,111 @@
1
+ import { ConfigProvider as e, Effect as t, Stream as n } from "effect";
2
+ //#region src/connectionConfigProvider.ts
3
+ var r = /* @__PURE__ */ new WeakMap(), i = (i, s) => {
4
+ i = r.get(i) ?? i;
5
+ function c(n, r) {
6
+ return i.config(n, r).pipe(t.provideService(e.ConfigProvider, s));
7
+ }
8
+ let l = {
9
+ config: c,
10
+ validate: (n) => i.validate(n).pipe(t.provideService(e.ConfigProvider, s)),
11
+ client: (n, r) => i.client(n, r).pipe(t.provideService(e.ConfigProvider, s)),
12
+ sql: (r, a) => i.sql(r, a).pipe(t.provideService(e.ConfigProvider, s), t.map((r) => ({
13
+ dialect: r.dialect,
14
+ use: (n) => r.use(n).pipe(t.provideService(e.ConfigProvider, s)),
15
+ stream: (t) => r.stream(t).pipe(n.provideService(e.ConfigProvider, s))
16
+ }))),
17
+ analytics: (n, r) => i.analytics(n, r).pipe(t.provideService(e.ConfigProvider, s), t.map((e) => o(e, s))),
18
+ storage: (n, r) => i.storage(n, r).pipe(t.provideService(e.ConfigProvider, s), t.map((e) => a(e, s))),
19
+ probe: (n, r) => i.probe(n, r).pipe(t.provideService(e.ConfigProvider, s)),
20
+ pubsub: (n, r) => i.pubsub(n, r).pipe(t.provideService(e.ConfigProvider, s), t.map((n) => ({
21
+ ...n,
22
+ publish: (r, i) => n.publish(r, i).pipe(t.provideService(e.ConfigProvider, s)),
23
+ subscribe: (r, i) => n.subscribe(r, i).pipe(t.provideService(e.ConfigProvider, s)),
24
+ close: n.close.pipe(t.provideService(e.ConfigProvider, s))
25
+ }))),
26
+ reload: i.reload.pipe(t.provideService(e.ConfigProvider, s)),
27
+ inspect: i.inspect.pipe(t.provideService(e.ConfigProvider, s)),
28
+ redis: {
29
+ config: (n) => i.redis.config(n).pipe(t.provideService(e.ConfigProvider, s)),
30
+ client: (n) => i.redis.client(n).pipe(t.provideService(e.ConfigProvider, s))
31
+ }
32
+ };
33
+ return r.set(l, i), l;
34
+ }, a = (r, i) => {
35
+ let a = (n) => n === void 0 ? void 0 : (...r) => n(...r).pipe(t.provideService(e.ConfigProvider, i));
36
+ return Object.defineProperties({
37
+ get objectNamespace() {
38
+ return r.objectNamespace.pipe(t.provideService(e.ConfigProvider, i));
39
+ },
40
+ get name() {
41
+ return r.name;
42
+ },
43
+ get presigns() {
44
+ return r.presigns;
45
+ },
46
+ put: (...n) => r.put(...n).pipe(t.provideService(e.ConfigProvider, i)),
47
+ get: (...n) => r.get(...n).pipe(t.provideService(e.ConfigProvider, i)),
48
+ delete: (...n) => r.delete(...n).pipe(t.provideService(e.ConfigProvider, i)),
49
+ head: (...n) => r.head(...n).pipe(t.provideService(e.ConfigProvider, i)),
50
+ getUrl: (...n) => r.getUrl(...n).pipe(t.provideService(e.ConfigProvider, i))
51
+ }, Object.getOwnPropertyDescriptors({
52
+ get objectLocation() {
53
+ return a(r.objectLocation);
54
+ },
55
+ get root() {
56
+ return r.root;
57
+ },
58
+ get publicUrl() {
59
+ return r.publicUrl;
60
+ },
61
+ get bindDataStore() {
62
+ return r.bindDataStore;
63
+ },
64
+ get getStream() {
65
+ let a = r.getStream;
66
+ return a === void 0 ? void 0 : (r) => a(r).pipe(t.provideService(e.ConfigProvider, i), t.map((t) => ({
67
+ ...t,
68
+ stream: t.stream.pipe(n.provideService(e.ConfigProvider, i))
69
+ })));
70
+ },
71
+ get putStream() {
72
+ return a(r.putStream);
73
+ },
74
+ get getRange() {
75
+ return a(r.getRange);
76
+ },
77
+ get setObjectPublic() {
78
+ return a(r.setObjectPublic);
79
+ },
80
+ get getUploadUrl() {
81
+ return a(r.getUploadUrl);
82
+ },
83
+ get createMultipartUpload() {
84
+ return a(r.createMultipartUpload);
85
+ },
86
+ get presignUploadPart() {
87
+ return a(r.presignUploadPart);
88
+ },
89
+ get completeMultipartUpload() {
90
+ return a(r.completeMultipartUpload);
91
+ },
92
+ get abortMultipartUpload() {
93
+ return a(r.abortMultipartUpload);
94
+ }
95
+ }));
96
+ }, o = (n, r) => ({
97
+ info: n.info,
98
+ track: (i) => n.track(i).pipe(t.provideService(e.ConfigProvider, r)),
99
+ aggregate: (i) => n.aggregate(i).pipe(t.provideService(e.ConfigProvider, r)),
100
+ timeseries: (i) => n.timeseries(i).pipe(t.provideService(e.ConfigProvider, r)),
101
+ topN: (i) => n.topN(i).pipe(t.provideService(e.ConfigProvider, r)),
102
+ ...n.mirror === void 0 ? {} : { mirror: {
103
+ tables: n.mirror.tables,
104
+ ...n.mirror.primaryKey === void 0 ? {} : { primaryKey: n.mirror.primaryKey },
105
+ upsert: (i) => n.mirror.upsert(i).pipe(t.provideService(e.ConfigProvider, r)),
106
+ remove: (i) => n.mirror.remove(i).pipe(t.provideService(e.ConfigProvider, r)),
107
+ maxVersion: (i) => n.mirror.maxVersion(i).pipe(t.provideService(e.ConfigProvider, r))
108
+ } }
109
+ });
110
+ //#endregion
111
+ export { i as withConnectionRegistryConfigProvider };