@voltro/kv 0.87.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.
- package/CHANGELOG.md +169 -6
- package/THIRD-PARTY-NOTICES.md +2 -1345
- package/dist/Kv.js +1 -2
- package/dist/KvStore.js +1 -2
- package/dist/analytics.js +1 -2
- package/dist/backendMemory.js +1 -2
- package/dist/backendRedis.js +1 -2
- package/dist/cacheAtomicProtocol.js +34 -24
- package/dist/cacheAtomicScript.js +19 -2
- package/dist/cacheAtomicState.js +1 -2
- package/dist/connection.js +74 -70
- package/dist/connectionAnalyticsConfig.js +1 -2
- package/dist/connectionConfigError.js +10 -0
- package/dist/connectionConfigProvider.js +1 -2
- package/dist/connectionDefinitions.js +1 -2
- package/dist/connectionMemory.js +1 -2
- package/dist/connectionMetrics.js +1 -2
- package/dist/connectionNamespace.js +1 -2
- package/dist/connectionNats.js +44 -44
- package/dist/connectionNatsConfig.js +1 -2
- package/dist/connectionNatsSubjects.js +1 -2
- package/dist/connectionOwner.js +1 -2
- package/dist/connectionPool.js +252 -252
- package/dist/connectionPrefix.js +1 -2
- package/dist/connectionScripts.js +1 -2
- package/dist/connectionSqlConfig.js +1 -2
- package/dist/connectionStorageConfig.js +1 -2
- package/dist/connectionStorageFacade.js +1 -2
- package/dist/connectionSubscriber.js +47 -47
- package/dist/driverModule.js +9 -0
- package/dist/errors.js +1 -2
- package/dist/expirySweep.js +1 -2
- package/dist/hot-contract.js +1 -2
- package/dist/hot-testing.d.ts +14 -1
- package/dist/hotMemory.js +1 -2
- package/dist/hotOperations.js +1 -2
- package/dist/hotRateLimit.js +1 -2
- package/dist/hotReadAvailability.js +1 -2
- package/dist/hotRedis.js +1 -2
- package/dist/hotRedisScript.js +1 -2
- package/dist/hotRouting.js +2 -3
- package/dist/hotState.js +1 -2
- package/dist/hotStoreConformance.js +76 -78
- package/dist/nativeRedisReads.js +2 -3
- package/dist/nativeRedisTopology.js +64 -63
- package/dist/redisDiagnostics.js +1 -2
- package/dist/redisReadPolicy.js +1 -2
- package/dist/storage.js +1 -2
- package/dist/storageAddress.js +1 -2
- package/package.json +13 -10
package/dist/Kv.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { KvStore as e } from "./KvStore.js";
|
|
2
2
|
import { Context as t, Effect as n, Layer as r, Option as i } from "effect";
|
|
3
|
-
|
|
3
|
+
// src/Kv.ts
|
|
4
4
|
var a = class a extends t.Service()("@voltro/Kv", { make: n.gen(function* () {
|
|
5
5
|
let t = yield* e;
|
|
6
6
|
return {
|
|
@@ -15,5 +15,4 @@ var a = class a extends t.Service()("@voltro/Kv", { make: n.gen(function* () {
|
|
|
15
15
|
}) }) {
|
|
16
16
|
static layer = r.effect(a, a.make);
|
|
17
17
|
};
|
|
18
|
-
//#endregion
|
|
19
18
|
export { a as Kv };
|
package/dist/KvStore.js
CHANGED
package/dist/analytics.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema as e } from "effect";
|
|
2
|
-
|
|
2
|
+
// src/connectionAnalytics.ts
|
|
3
3
|
var t = class extends e.TaggedError()("AnalyticsError", {
|
|
4
4
|
provider: e.String,
|
|
5
5
|
operation: e.String,
|
|
@@ -12,5 +12,4 @@ var t = class extends e.TaggedError()("AnalyticsError", {
|
|
|
12
12
|
provider: e.String,
|
|
13
13
|
capability: e.String
|
|
14
14
|
}) {};
|
|
15
|
-
//#endregion
|
|
16
15
|
export { n as AnalyticsCapabilityNotSupported, t as AnalyticsError };
|
package/dist/backendMemory.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { KvStore as e } from "./KvStore.js";
|
|
2
2
|
import { makeExpirySweep as t } from "./expirySweep.js";
|
|
3
3
|
import { Clock as n, Effect as r, HashMap as i, Layer as a, Option as o, Ref as s } from "effect";
|
|
4
|
-
|
|
4
|
+
// src/backendMemory.ts
|
|
5
5
|
var c = (e, t) => t === void 0 ? Infinity : e + t, l = (e) => r.gen(function* () {
|
|
6
6
|
let a = yield* s.make(i.empty()), l = e.now === void 0 ? n.currentTimeMillis : r.sync(e.now), u = t(), d = u.sweep;
|
|
7
7
|
return {
|
|
@@ -32,5 +32,4 @@ var c = (e, t) => t === void 0 ? Infinity : e + t, l = (e) => r.gen(function* ()
|
|
|
32
32
|
clear: () => r.sync(u.reset).pipe(r.andThen(s.set(a, i.empty())))
|
|
33
33
|
};
|
|
34
34
|
}), u = (t = {}) => a.effect(e, l(t));
|
|
35
|
-
//#endregion
|
|
36
35
|
export { u as layerMemory };
|
package/dist/backendRedis.js
CHANGED
|
@@ -2,7 +2,7 @@ import { KvStore as e } from "./KvStore.js";
|
|
|
2
2
|
import { KvError as t } from "./errors.js";
|
|
3
3
|
import { RedisConnections as n, commandRetry as r } from "./connection.js";
|
|
4
4
|
import { Effect as i, Layer as a, Option as o } from "effect";
|
|
5
|
-
|
|
5
|
+
// src/backendRedis.ts
|
|
6
6
|
var s = (e, n = "voltro:kv") => {
|
|
7
7
|
let a = (e) => `${n}:${e}`, s = (e) => e.slice(n.length + 1), c = (n, a, o) => i.tryPromise({
|
|
8
8
|
try: () => o(e),
|
|
@@ -44,5 +44,4 @@ var s = (e, n = "voltro:kv") => {
|
|
|
44
44
|
cause: String(e)
|
|
45
45
|
})))), r.keyPrefix ?? "voltro:kv");
|
|
46
46
|
}));
|
|
47
|
-
//#endregion
|
|
48
47
|
export { c as layerRedis, s as makeRedisKvStore };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/cacheAtomicProtocol.ts
|
|
2
2
|
var e = class extends Error {
|
|
3
3
|
constructor() {
|
|
4
4
|
super("Invalid atomic cache command or state."), this.name = "CacheAtomicProtocolError";
|
|
@@ -148,49 +148,60 @@ var e = class extends Error {
|
|
|
148
148
|
default: return n();
|
|
149
149
|
}
|
|
150
150
|
}, h = (e, t) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
151
|
+
let r = typeof e == "string" ? e.match(/\{voltro:cache:atomic:(?:[a-f0-9]{2})+\}:r$/)?.[0] : void 0;
|
|
152
|
+
if (r === void 0) return n();
|
|
153
|
+
let i = e.slice(0, -2), o = /* @__PURE__ */ new Set(), s = (e) => {
|
|
154
|
+
o.add(`${i}:v:${a(e)}`);
|
|
155
|
+
}, c = (e) => {
|
|
156
|
+
o.add(`${i}:t:${a(e)}`);
|
|
157
|
+
}, l = () => {
|
|
158
|
+
o.add(`${i}:s`);
|
|
159
|
+
}, u = (e) => {
|
|
160
|
+
o.add(`${i}:e:${a(e)}`);
|
|
160
161
|
};
|
|
161
162
|
switch (t.op) {
|
|
162
163
|
case "inspect":
|
|
163
|
-
|
|
164
|
+
s(t.key);
|
|
164
165
|
break;
|
|
165
166
|
case "inspect-values":
|
|
166
|
-
t.keys.forEach(
|
|
167
|
+
t.keys.forEach(s);
|
|
167
168
|
break;
|
|
168
169
|
case "read":
|
|
169
|
-
|
|
170
|
+
s(t.key), t.snapshot?.tags.forEach(c);
|
|
170
171
|
break;
|
|
171
172
|
case "fence":
|
|
172
|
-
|
|
173
|
+
l();
|
|
173
174
|
break;
|
|
174
175
|
case "write":
|
|
175
|
-
|
|
176
|
+
s(t.key), t.tags.forEach(c), t.snapshot?.tags.forEach(c), t.fence !== null && t.tags.forEach(u);
|
|
176
177
|
break;
|
|
177
178
|
case "remove":
|
|
178
|
-
|
|
179
|
+
s(t.key), t.snapshot?.tags.forEach(c);
|
|
179
180
|
break;
|
|
180
181
|
case "tag":
|
|
181
|
-
|
|
182
|
+
c(t.tag);
|
|
182
183
|
break;
|
|
183
184
|
case "invalidate":
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
c(t.tag), l(), u(t.tag), t.snapshots.forEach((e) => {
|
|
186
|
+
s(e.key), e.snapshot?.tags.forEach(c);
|
|
186
187
|
});
|
|
187
188
|
break;
|
|
188
|
-
case "clear":
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
case "clear": {
|
|
190
|
+
let s = r.slice(0, -2), c = /* @__PURE__ */ new Set();
|
|
191
|
+
for (let l of t.keys) {
|
|
192
|
+
let t = l;
|
|
193
|
+
if (l !== e && !l.startsWith(`${i}:v:`) && !l.startsWith(`${i}:t:`)) {
|
|
194
|
+
if (l === r) t = e;
|
|
195
|
+
else if (l.startsWith(`${s}:v:`) || l.startsWith(`${s}:t:`)) t = i + l.slice(s.length);
|
|
196
|
+
else return n();
|
|
197
|
+
}
|
|
198
|
+
if (c.has(t)) return n();
|
|
199
|
+
c.add(t), t !== e && (a(t.slice(i.length + 3)), o.add(t));
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
191
202
|
}
|
|
192
203
|
}
|
|
193
|
-
return [e, ...[...
|
|
204
|
+
return [e, ...[...o].sort()];
|
|
194
205
|
}, g = (e, t) => `${o(t)}:${a(e)}`, _ = (e) => e === void 0 ? null : !Number.isFinite(e) || !Number.isSafeInteger(Math.ceil(e)) ? n() : Math.max(0, Math.ceil(e)), v = (e) => {
|
|
195
206
|
if (typeof e != "object" || !e || Array.isArray(e) || Object.getPrototypeOf(e) !== Object.prototype && Object.getPrototypeOf(e) !== null) return n();
|
|
196
207
|
let t = e;
|
|
@@ -243,5 +254,4 @@ var e = class extends Error {
|
|
|
243
254
|
"tag",
|
|
244
255
|
"scan"
|
|
245
256
|
].includes(e.op);
|
|
246
|
-
//#endregion
|
|
247
257
|
export { t as CACHE_FENCE_RETENTION_MS, e as CacheAtomicProtocolError, l as cacheAtomicArray, f as cacheAtomicComponent, h as cacheAtomicKeys, d as cacheAtomicMember, g as cacheAtomicMembership, p as cacheAtomicRoot, x as cacheAtomicScanPage, u as cacheAtomicSnapshot, _ as cacheAtomicTtl, b as cacheAtomicValueSnapshots, m as decodeCacheAtomicCommand, v as decodeCacheAtomicPolicy, y as decodeCacheAtomicReply, S as isCacheAtomicRead };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CACHE_FENCE_RETENTION_MS as e } from "./cacheAtomicProtocol.js";
|
|
2
|
-
|
|
2
|
+
// src/cacheAtomicScript.ts
|
|
3
3
|
var t = String.raw`
|
|
4
4
|
local c = cjson.decode(ARGV[1])
|
|
5
5
|
local policy = ARGV[2] and cjson.decode(ARGV[2]) or {}
|
|
@@ -126,8 +126,26 @@ elseif c.op == 'invalidate' then
|
|
|
126
126
|
for i = 2, #c.snapshots do if c.snapshots[i - 1].key >= c.snapshots[i].key then invalid() end end
|
|
127
127
|
elseif c.op == 'clear' then
|
|
128
128
|
if not null(c.revision) then version(c.revision) end
|
|
129
|
+
-- KEYS may carry a registry prefix; ARGV retains caller-logical keys.
|
|
130
|
+
-- The canonical cache token binds those two domains exactly, without
|
|
131
|
+
-- accepting a foreign connection prefix or another cache namespace.
|
|
132
|
+
local logicalRoot = string.match(root, '({voltro:cache:atomic:[a-f0-9]+}:r)$')
|
|
133
|
+
if not logicalRoot then invalid() end
|
|
134
|
+
id(string.match(logicalRoot, '^{voltro:cache:atomic:([a-f0-9]+)}:r$'))
|
|
135
|
+
local logicalSpace = string.sub(logicalRoot, 1, #logicalRoot - 2)
|
|
136
|
+
local selected = {}
|
|
129
137
|
array(c.keys, function(key)
|
|
130
138
|
if type(key) ~= 'string' then invalid() end
|
|
139
|
+
local kind = string.sub(key, 1, #space + 3)
|
|
140
|
+
if key ~= root and kind ~= space .. ':v:' and kind ~= space .. ':t:' then
|
|
141
|
+
local logicalKind = string.sub(key, 1, #logicalSpace + 3)
|
|
142
|
+
if key == logicalRoot then key = root
|
|
143
|
+
elseif logicalKind == logicalSpace .. ':v:' or logicalKind == logicalSpace .. ':t:' then
|
|
144
|
+
key = space .. string.sub(key, #logicalSpace + 1)
|
|
145
|
+
else invalid() end
|
|
146
|
+
end
|
|
147
|
+
if selected[key] then invalid() end
|
|
148
|
+
selected[key] = true
|
|
131
149
|
if key ~= root then
|
|
132
150
|
local prefix = string.sub(key, 1, #space + 3)
|
|
133
151
|
if prefix ~= space .. ':v:' and prefix ~= space .. ':t:' then invalid() end
|
|
@@ -305,5 +323,4 @@ elseif c.op == 'clear' then
|
|
|
305
323
|
end
|
|
306
324
|
invalid()
|
|
307
325
|
`;
|
|
308
|
-
//#endregion
|
|
309
326
|
export { t as CACHE_ATOMIC_SCRIPT };
|
package/dist/cacheAtomicState.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CACHE_FENCE_RETENTION_MS as e, CacheAtomicProtocolError as t, cacheAtomicKeys as n, cacheAtomicMember as r, cacheAtomicMembership as i, cacheAtomicSnapshot as a, decodeCacheAtomicCommand as o, decodeCacheAtomicPolicy as s } from "./cacheAtomicProtocol.js";
|
|
2
|
-
|
|
2
|
+
// src/cacheAtomicState.ts
|
|
3
3
|
var c = 9e15, l = () => {
|
|
4
4
|
throw new t();
|
|
5
5
|
}, u = (t, u, d, f = {}) => {
|
|
@@ -137,5 +137,4 @@ var c = 9e15, l = () => {
|
|
|
137
137
|
return O("ok");
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
|
-
//#endregion
|
|
141
140
|
export { u as applyCacheAtomicCommand };
|
package/dist/connection.js
CHANGED
|
@@ -3,19 +3,20 @@ import { withRespKeyPrefix as t } from "./connectionPrefix.js";
|
|
|
3
3
|
import { dynamicSql as n } from "./connectionSqlConfig.js";
|
|
4
4
|
import { dynamicAnalytics as r } from "./connectionAnalyticsConfig.js";
|
|
5
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 {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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";
|
|
13
14
|
import { readRedisNodeDiagnostics as ne } from "./redisDiagnostics.js";
|
|
14
|
-
import { CACHE_FENCE_RETENTION_MS as
|
|
15
|
-
import { CACHE_ATOMIC_SCRIPT as
|
|
16
|
-
import { Config as
|
|
17
|
-
|
|
18
|
-
var
|
|
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) => {
|
|
19
20
|
let n = e.client;
|
|
20
21
|
return {
|
|
21
22
|
readOnly: {
|
|
@@ -29,11 +30,15 @@ var q = (e) => new B.ConfigError(new V.SourceError({ message: e })), se = (e, t)
|
|
|
29
30
|
},
|
|
30
31
|
zrange: async (e) => {
|
|
31
32
|
let n = await t.select();
|
|
32
|
-
return await n.ready(), n.client.zrange(e, 0, -1);
|
|
33
|
+
return await n.ready(), n.client.zrange(e, 0, "-1");
|
|
33
34
|
},
|
|
34
35
|
eval: async (e, n, ...r) => {
|
|
35
36
|
let i = await t.select();
|
|
36
|
-
return await i.ready(), i.client.call("eval_ro",
|
|
37
|
+
return await i.ready(), i.client.call("eval_ro", [
|
|
38
|
+
e,
|
|
39
|
+
n,
|
|
40
|
+
...r
|
|
41
|
+
]);
|
|
37
42
|
}
|
|
38
43
|
},
|
|
39
44
|
ping: async () => (await e.ready(), n.ping()),
|
|
@@ -65,8 +70,8 @@ var q = (e) => new B.ConfigError(new V.SourceError({ message: e })), se = (e, t)
|
|
|
65
70
|
},
|
|
66
71
|
exists: async (t) => (await e.ready(), n.exists(t)),
|
|
67
72
|
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(),
|
|
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)),
|
|
70
75
|
close: async () => {
|
|
71
76
|
let r, i = () => {
|
|
72
77
|
try {
|
|
@@ -80,11 +85,11 @@ var q = (e) => new B.ConfigError(new V.SourceError({ message: e })), se = (e, t)
|
|
|
80
85
|
}, 1e3);
|
|
81
86
|
})]);
|
|
82
87
|
} finally {
|
|
83
|
-
r !== void 0 && clearTimeout(r), await Promise.all([t.close(),
|
|
88
|
+
r !== void 0 && clearTimeout(r), await Promise.all([t.close(), v(e)]).then(() => void 0);
|
|
84
89
|
}
|
|
85
90
|
}
|
|
86
91
|
};
|
|
87
|
-
},
|
|
92
|
+
}, q = (e) => ({
|
|
88
93
|
readOnly: {
|
|
89
94
|
get: (t) => e.get(t),
|
|
90
95
|
exists: (t) => e.exists(t),
|
|
@@ -110,7 +115,7 @@ var q = (e) => new B.ConfigError(new V.SourceError({ message: e })), se = (e, t)
|
|
|
110
115
|
};
|
|
111
116
|
},
|
|
112
117
|
close: () => Promise.resolve()
|
|
113
|
-
}),
|
|
118
|
+
}), J = (e) => e.toUpperCase().replace(/[^A-Z0-9]+/g, "_"), Y = (e) => {
|
|
114
119
|
if (![
|
|
115
120
|
"resp",
|
|
116
121
|
"http",
|
|
@@ -148,79 +153,79 @@ var q = (e) => new B.ConfigError(new V.SourceError({ message: e })), se = (e, t)
|
|
|
148
153
|
"none"
|
|
149
154
|
].includes(e.persistence)) return "Connection persistence must be required, optional or none";
|
|
150
155
|
if (e.maxmemoryPolicyHint !== void 0 && typeof e.maxmemoryPolicyHint != "string") return "Redis maxmemoryPolicyHint must be a string";
|
|
151
|
-
},
|
|
152
|
-
let t =
|
|
153
|
-
return t ?
|
|
156
|
+
}, X = (e) => {
|
|
157
|
+
let t = Y(e);
|
|
158
|
+
return t ? H.fail(K(t)) : e.driver === "resp" ? H.try({
|
|
154
159
|
try: () => {
|
|
155
|
-
let t =
|
|
156
|
-
if (
|
|
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");
|
|
157
162
|
return t.kind === "cluster" && e.cluster === void 0 ? {
|
|
158
163
|
...e,
|
|
159
164
|
cluster: !0
|
|
160
165
|
} : e;
|
|
161
166
|
},
|
|
162
|
-
catch: () =>
|
|
163
|
-
}) :
|
|
164
|
-
},
|
|
165
|
-
let n =
|
|
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);
|
|
166
171
|
return {
|
|
167
172
|
url: r,
|
|
168
173
|
driver: a,
|
|
169
|
-
...
|
|
170
|
-
...
|
|
174
|
+
...W.isSome(o) ? { token: o.value } : {},
|
|
175
|
+
...W.isSome(s) ? { keyPrefix: s.value } : {},
|
|
171
176
|
...t.source === void 0 ? {} : { source: t.source },
|
|
172
177
|
...t.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: t.connectTimeoutMs },
|
|
173
178
|
...t.commandTimeoutMs === void 0 ? {} : { commandTimeoutMs: t.commandTimeoutMs }
|
|
174
179
|
};
|
|
175
|
-
}).pipe(
|
|
176
|
-
function
|
|
180
|
+
}).pipe(H.flatMap(X));
|
|
181
|
+
function Q(e, t, n) {
|
|
177
182
|
let r = n === void 0 ? void 0 : Object.freeze({ tenantId: n.tenantId });
|
|
178
|
-
return
|
|
183
|
+
return H.gen(function* () {
|
|
179
184
|
let n = a(t ?? {});
|
|
180
|
-
if (n) return yield*
|
|
185
|
+
if (n) return yield* H.fail(K(n));
|
|
181
186
|
let i = e.includes(":"), o = i ? e.slice(e.indexOf(":") + 1) : e;
|
|
182
|
-
if (i && !e.startsWith("redis:")) return yield*
|
|
183
|
-
if (i && o && !/^[A-Za-z][A-Za-z0-9_-]*$/.test(o)) return yield*
|
|
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"));
|
|
184
189
|
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*
|
|
186
|
-
let d = `CONNECTIONS_REDIS_${
|
|
187
|
-
...
|
|
188
|
-
...
|
|
189
|
-
...
|
|
190
|
-
...
|
|
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 } : {},
|
|
191
196
|
...[
|
|
192
197
|
p,
|
|
193
198
|
m,
|
|
194
199
|
h,
|
|
195
200
|
g
|
|
196
|
-
].some(
|
|
201
|
+
].some(W.isSome) ? { source: "override" } : {}
|
|
197
202
|
};
|
|
198
203
|
if (u && "_tag" in u && u._tag === "MemoryRedisConnection") return [
|
|
199
204
|
p,
|
|
200
205
|
m,
|
|
201
206
|
h,
|
|
202
207
|
g
|
|
203
|
-
].some(
|
|
208
|
+
].some(W.isSome) ? yield* H.fail(K("A local memory connection cannot be redirected by transport environment overrides")) : {
|
|
204
209
|
url: `memory:${l}`,
|
|
205
210
|
driver: "memory",
|
|
206
211
|
source: "config",
|
|
207
212
|
...u.now === void 0 ? {} : { memoryClock: u.now }
|
|
208
213
|
};
|
|
209
214
|
if (u && "_tag" in u) {
|
|
210
|
-
if (!r?.tenantId) return yield*
|
|
215
|
+
if (!r?.tenantId) return yield* H.fail(K("A dynamic Redis connection requires tenantId"));
|
|
211
216
|
let e = {
|
|
212
217
|
...yield* u.resolve(r),
|
|
213
218
|
..._
|
|
214
|
-
}, t =
|
|
215
|
-
return t ? yield*
|
|
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);
|
|
216
221
|
}
|
|
217
|
-
if (!u) return yield*
|
|
218
|
-
let v =
|
|
219
|
-
if (y === "http" &&
|
|
220
|
-
let x =
|
|
221
|
-
if (u.tls === "required" && !/^rediss(?:\+cluster|\+sentinel)?:\/\//i.test(v) && !(y === "http" && /^https:\/\//i.test(v))) return yield*
|
|
222
|
-
let C = yield*
|
|
223
|
-
return yield*
|
|
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({
|
|
224
229
|
url: v,
|
|
225
230
|
driver: y,
|
|
226
231
|
cluster: S,
|
|
@@ -229,8 +234,8 @@ function le(e, t, n) {
|
|
|
229
234
|
m,
|
|
230
235
|
h,
|
|
231
236
|
g
|
|
232
|
-
].some(
|
|
233
|
-
...
|
|
237
|
+
].some(W.isSome) ? "override" : "config",
|
|
238
|
+
...W.isSome(b) ? { token: b.value } : {},
|
|
234
239
|
...x === void 0 ? {} : { keyPrefix: x },
|
|
235
240
|
...u.allowSharedNamespace === void 0 ? {} : { allowSharedNamespace: u.allowSharedNamespace },
|
|
236
241
|
...u.tls === void 0 ? {} : { tls: u.tls },
|
|
@@ -246,12 +251,12 @@ function le(e, t, n) {
|
|
|
246
251
|
});
|
|
247
252
|
});
|
|
248
253
|
}
|
|
249
|
-
var
|
|
254
|
+
var de = (n) => H.tryPromise({
|
|
250
255
|
try: async (e) => {
|
|
251
256
|
e.throwIfAborted();
|
|
252
|
-
let t =
|
|
257
|
+
let t = Y(n);
|
|
253
258
|
if (t) throw Error(t);
|
|
254
|
-
if (n.driver === "resp" &&
|
|
259
|
+
if (n.driver === "resp" && D(n), n.driver === "memory") {
|
|
255
260
|
let { makeMemoryConnectionState: e } = await import("./connectionMemory.js"), t = e(n.memoryClock), r = t.command();
|
|
256
261
|
return {
|
|
257
262
|
...r,
|
|
@@ -261,7 +266,7 @@ var ue = (n) => U.tryPromise({
|
|
|
261
266
|
};
|
|
262
267
|
}
|
|
263
268
|
if (n.driver === "http") {
|
|
264
|
-
let t = await import("@upstash/redis");
|
|
269
|
+
let t = await import("@upstash/redis").catch(g("@upstash/redis", "An Upstash HTTP connection"));
|
|
265
270
|
e.throwIfAborted();
|
|
266
271
|
let r = typeof t.default == "function" ? t.default : t.default?.Redis, i = t.Redis ?? r;
|
|
267
272
|
if (!i) throw Error("@upstash/redis: Redis constructor not found in module");
|
|
@@ -280,7 +285,7 @@ var ue = (n) => U.tryPromise({
|
|
|
280
285
|
signal: r.signal
|
|
281
286
|
});
|
|
282
287
|
if (await a.ping() !== "PONG") throw Error("HTTP reader readiness failed");
|
|
283
|
-
let o =
|
|
288
|
+
let o = q(a);
|
|
284
289
|
if (Number(await o.readOnly.eval("return 1", 0)) !== 1) throw Error("HTTP read-only Lua readiness failed");
|
|
285
290
|
return e.throwIfAborted(), {
|
|
286
291
|
...o,
|
|
@@ -309,14 +314,14 @@ var ue = (n) => U.tryPromise({
|
|
|
309
314
|
if (await t.ping() !== "PONG") throw Error("HTTP primary readiness failed");
|
|
310
315
|
e.throwIfAborted();
|
|
311
316
|
let r = {
|
|
312
|
-
...
|
|
317
|
+
...q(t),
|
|
313
318
|
close: async () => {
|
|
314
319
|
o.abort();
|
|
315
320
|
}
|
|
316
321
|
}, s = [];
|
|
317
322
|
try {
|
|
318
323
|
if (n.readFrom !== void 0 && n.readFrom !== "primary") for (let e of n.replicas ?? []) s.push(await a(e));
|
|
319
|
-
let t =
|
|
324
|
+
let t = E(r, s, n.readFrom);
|
|
320
325
|
return (n.readFrom === "replica" || n.readFrom === "nearest") && await t.readOnly.eval("return 1", 0), e.throwIfAborted(), t;
|
|
321
326
|
} catch (e) {
|
|
322
327
|
throw await Promise.all([r, ...s].map((e) => e.close())), e;
|
|
@@ -327,7 +332,7 @@ var ue = (n) => U.tryPromise({
|
|
|
327
332
|
e.removeEventListener("abort", s);
|
|
328
333
|
}
|
|
329
334
|
}
|
|
330
|
-
let r = await
|
|
335
|
+
let r = await _(n), i = r.client;
|
|
331
336
|
i.on("error", () => {});
|
|
332
337
|
let a = () => {
|
|
333
338
|
i.disconnect();
|
|
@@ -336,19 +341,19 @@ var ue = (n) => U.tryPromise({
|
|
|
336
341
|
try {
|
|
337
342
|
e.throwIfAborted(), await i.connect(), await r.ready(), e.throwIfAborted();
|
|
338
343
|
} catch (e) {
|
|
339
|
-
throw await
|
|
344
|
+
throw await v(r), e;
|
|
340
345
|
} finally {
|
|
341
346
|
e.removeEventListener("abort", a);
|
|
342
347
|
}
|
|
343
348
|
try {
|
|
344
349
|
let t = await te(n, r, e);
|
|
345
350
|
try {
|
|
346
|
-
return e.throwIfAborted(),
|
|
351
|
+
return e.throwIfAborted(), le(r, t);
|
|
347
352
|
} catch (e) {
|
|
348
353
|
throw await t.close(), e;
|
|
349
354
|
}
|
|
350
355
|
} catch (e) {
|
|
351
|
-
throw await
|
|
356
|
+
throw await v(r), e;
|
|
352
357
|
}
|
|
353
358
|
},
|
|
354
359
|
catch: () => new e({
|
|
@@ -356,6 +361,5 @@ var ue = (n) => U.tryPromise({
|
|
|
356
361
|
key: "",
|
|
357
362
|
cause: n.keyPrefix !== void 0 && typeof n.keyPrefix != "string" ? "Connection keyPrefix must be a string" : "Redis connection could not be opened"
|
|
358
363
|
})
|
|
359
|
-
}).pipe(
|
|
360
|
-
|
|
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 };
|
|
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 };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { connectionDefinitionsProblem as e, connectionMetadataProblem as t } from "./connectionDefinitions.js";
|
|
2
2
|
import { Config as n, ConfigProvider as r, Effect as i, Option as a } from "effect";
|
|
3
|
-
|
|
3
|
+
// src/connectionAnalyticsConfig.ts
|
|
4
4
|
var o = (e, t = {}) => {
|
|
5
5
|
let n = t.ttlMs ?? 6e4;
|
|
6
6
|
if (typeof e != "function" || !Number.isSafeInteger(n) || n <= 0) throw Error("Dynamic analytics requires a resolver and positive ttlMs");
|
|
@@ -149,5 +149,4 @@ var o = (e, t = {}) => {
|
|
|
149
149
|
source: _ ? "override" : "config"
|
|
150
150
|
};
|
|
151
151
|
});
|
|
152
|
-
//#endregion
|
|
153
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 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConfigProvider as e, Effect as t, Stream as n } from "effect";
|
|
2
|
-
|
|
2
|
+
// src/connectionConfigProvider.ts
|
|
3
3
|
var r = /* @__PURE__ */ new WeakMap(), i = (i, s) => {
|
|
4
4
|
i = r.get(i) ?? i;
|
|
5
5
|
function c(n, r) {
|
|
@@ -107,5 +107,4 @@ var r = /* @__PURE__ */ new WeakMap(), i = (i, s) => {
|
|
|
107
107
|
maxVersion: (i) => n.mirror.maxVersion(i).pipe(t.provideService(e.ConfigProvider, r))
|
|
108
108
|
} }
|
|
109
109
|
});
|
|
110
|
-
//#endregion
|
|
111
110
|
export { i as withConnectionRegistryConfigProvider };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sqlDefinitionProblem as e } from "./connectionSqlConfig.js";
|
|
2
2
|
import { analyticsDefinitionProblem as t } from "./connectionAnalyticsConfig.js";
|
|
3
|
-
|
|
3
|
+
// src/connectionDefinitions.ts
|
|
4
4
|
var n = (e) => {
|
|
5
5
|
if (!/^[A-Z][A-Z0-9_]*$/.test(e)) throw Error("Connection env names must use uppercase letters, digits and underscores");
|
|
6
6
|
return Object.freeze({
|
|
@@ -281,5 +281,4 @@ var n = (e) => {
|
|
|
281
281
|
for (let n of [...Object.values(e.analytics ?? {}), ...Object.values(e.sql ?? {})]) if (!("_tag" in n)) for (let e of Object.values(n)) typeof e == "object" && e && "_tag" in e && e._tag === "ConnectionEnv" && t.add(e.name);
|
|
282
282
|
return [...t].sort();
|
|
283
283
|
};
|
|
284
|
-
//#endregion
|
|
285
284
|
export { p as byTenant, l as connectionDefinitionsProblem, u as connectionMetadataProblem, m as declaredConnectionEnv, a as dynamic, o as dynamicNats, n as env, i as memory, s as natsConnectionName, c as redisConnectionName, r as redisTimeoutsProblem, f as roundRobin, d as shardBy };
|
package/dist/connectionMemory.js
CHANGED
|
@@ -6,7 +6,7 @@ import { applyHotOperations as i, compareHotMembers as a } from "./hotState.js";
|
|
|
6
6
|
import { HotError as o } from "./hot-contract.js";
|
|
7
7
|
import "./hotRedisScript.js";
|
|
8
8
|
import { applyCacheAtomicCommand as s } from "./cacheAtomicState.js";
|
|
9
|
-
|
|
9
|
+
// src/connectionMemory.ts
|
|
10
10
|
var c = (c = Date.now) => {
|
|
11
11
|
let l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Set(), p = !1, m = () => {
|
|
12
12
|
if (p) throw Error("Memory connection is closed");
|
|
@@ -203,5 +203,4 @@ var c = (c = Date.now) => {
|
|
|
203
203
|
}
|
|
204
204
|
};
|
|
205
205
|
};
|
|
206
|
-
//#endregion
|
|
207
206
|
export { c as makeMemoryConnectionState };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect as e, Metric as t } from "effect";
|
|
2
|
-
|
|
2
|
+
// src/connectionMetrics.ts
|
|
3
3
|
var n = /* @__PURE__ */ new Map(), r = t.gauge("voltro_connection_up", { description: "Whether every current pool for this reference has observed readiness and admits commands; not a persistence or capacity measurement." }), i = t.gauge("voltro_connection_pools", { description: "Owned physical generations, including draining generations." }), a = t.gauge("voltro_connection_active_commands", { description: "Borrowed generation leases, including complete streams and transactions." }), o = t.counter("voltro_connection_commands_total", { description: "Admitted commands and scoped streams; includes read-only probes." }), s = t.counter("voltro_connection_errors_total", { description: "Failed registry operations; includes rejected circuit admission and connection construction." }), c = t.gauge("voltro_connection_latency_ms", { description: "Duration of the most recently completed command or scoped stream in milliseconds." }), l = t.histogram("voltro_connection_duration_seconds", {
|
|
4
4
|
boundaries: t.exponentialBoundaries({
|
|
5
5
|
start: .001,
|
|
@@ -75,5 +75,4 @@ var n = /* @__PURE__ */ new Map(), r = t.gauge("voltro_connection_up", { descrip
|
|
|
75
75
|
}
|
|
76
76
|
};
|
|
77
77
|
};
|
|
78
|
-
//#endregion
|
|
79
78
|
export { f as makeConnectionMetrics };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/connectionNamespace.ts
|
|
2
2
|
var e = (e) => encodeURIComponent(e).replace(/[!'()*]/g, (e) => `%${e.charCodeAt(0).toString(16).toUpperCase()}`), t = (t) => {
|
|
3
3
|
if (typeof t.appName != "string" || !t.appName.trim() || typeof t.environment != "string" || !t.environment.trim()) throw Error("Connection namespace requires a nonempty app name and environment");
|
|
4
4
|
return `voltro:${e(t.appName)}:${e(t.environment)}:`;
|
|
@@ -15,5 +15,4 @@ var e = (e) => encodeURIComponent(e).replace(/[!'()*]/g, (e) => `%${e.charCodeAt
|
|
|
15
15
|
keyPrefix: i
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
|
-
//#endregion
|
|
19
18
|
export { n as applyConnectionNamespace, t as connectionNamespace };
|