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