@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/connectionPool.js
CHANGED
|
@@ -16,33 +16,34 @@ import { CONNECTION_PUBLISH_SCRIPT as ce } from "./connectionScripts.js";
|
|
|
16
16
|
import { AnalyticsCapabilityNotSupported as g, AnalyticsError as _ } from "./analytics.js";
|
|
17
17
|
import { makeConnectionMetrics as le } from "./connectionMetrics.js";
|
|
18
18
|
import { withConnectionRegistryConfigProvider as v } from "./connectionConfigProvider.js";
|
|
19
|
+
import { connectionConfigError as y } from "./connectionConfigError.js";
|
|
19
20
|
import { RedisConnections as ue, connect as de, connectionConfig as fe } from "./connection.js";
|
|
20
|
-
import { Cause as
|
|
21
|
+
import { Cause as b, Clock as x, Config as S, ConfigProvider as C, Context as w, Deferred as pe, Effect as T, Layer as E, Pull as me, Scope as he, Stream as D, SynchronizedRef as O } from "effect";
|
|
21
22
|
import { CircuitBreaker as ge, CircuitOpen as _e } from "@voltro/shared";
|
|
22
|
-
|
|
23
|
-
var
|
|
24
|
-
let ue = 0,
|
|
25
|
-
if (v.requirements !== void 0 && !Array.isArray(v.requirements)) return yield*
|
|
26
|
-
let
|
|
27
|
-
for (let e of
|
|
23
|
+
// src/connectionPool.ts
|
|
24
|
+
var k = class extends w.Service()("@voltro/ConnectionRegistry") {}, A = (v = {}) => T.gen(function* () {
|
|
25
|
+
let ue = 0, w = v.connections === void 0 ? {} : v.connections;
|
|
26
|
+
if (v.requirements !== void 0 && !Array.isArray(v.requirements)) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Connection requirements must be an array" })));
|
|
27
|
+
let E = (v.requirements ?? []).map((e) => Object.freeze({ ...e }));
|
|
28
|
+
for (let e of E) if (e.onUnavailable !== void 0 && e.onUnavailable !== "degrade" && e.onUnavailable !== "fail" || typeof e.consumer != "string" || !e.consumer.trim() || typeof e.role != "string" || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(e.role) || e.reference !== "" && (typeof e.reference != "string" || !/^(redis|nats|storage|analytics|sql):[A-Za-z][A-Za-z0-9_-]*$/.test(e.reference)) || [
|
|
28
29
|
"crossNode",
|
|
29
30
|
"pubsub",
|
|
30
31
|
"atomicMulti"
|
|
31
32
|
].some((t) => {
|
|
32
33
|
let n = e[t];
|
|
33
34
|
return n !== void 0 && typeof n != "boolean";
|
|
34
|
-
})) return yield*
|
|
35
|
-
v.namespaceIdentity !== void 0 && (yield*
|
|
35
|
+
})) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Connection requirements need consumer, role, a typed reference, boolean capabilities and a degrade/fail unavailable policy" })));
|
|
36
|
+
v.namespaceIdentity !== void 0 && (yield* T.try({
|
|
36
37
|
try: () => p(v.namespaceIdentity),
|
|
37
|
-
catch: () => new
|
|
38
|
+
catch: () => new S.ConfigError(new C.SourceError({ message: "Connection namespace requires a nonempty app name and environment" }))
|
|
38
39
|
}));
|
|
39
|
-
let
|
|
40
|
-
if (
|
|
41
|
-
let
|
|
40
|
+
let k = le(v.namespaceIdentity === void 0 ? "unscoped" : p(v.namespaceIdentity)), A = c(w);
|
|
41
|
+
if (A) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: A })));
|
|
42
|
+
let j = w.maxDynamicPools ?? 128, ve = w.rotationGraceMs ?? 3e4, ye = v.failureThreshold ?? 5, be = v.resetTimeoutMs ?? 3e4;
|
|
42
43
|
for (let [e, t, n] of [
|
|
43
44
|
[
|
|
44
45
|
"maxDynamicPools",
|
|
45
|
-
|
|
46
|
+
j,
|
|
46
47
|
!1
|
|
47
48
|
],
|
|
48
49
|
[
|
|
@@ -60,141 +61,141 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
60
61
|
be,
|
|
61
62
|
!1
|
|
62
63
|
]
|
|
63
|
-
]) if (!Number.isSafeInteger(t) || t < +!n) return yield*
|
|
64
|
-
let
|
|
65
|
-
if (e.startsWith("sql:")) return `sql:${i(e,
|
|
66
|
-
if (e.startsWith("analytics:")) return `analytics:${s(e,
|
|
67
|
-
if (e.startsWith("storage:")) return `storage:${oe(e,
|
|
68
|
-
if (e.startsWith("nats:")) return `nats:${l(e,
|
|
69
|
-
let t = u(e,
|
|
70
|
-
return
|
|
71
|
-
},
|
|
64
|
+
]) if (!Number.isSafeInteger(t) || t < +!n) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: `${e} must be a ${n ? "nonnegative" : "positive"} safe integer` })));
|
|
65
|
+
let M = yield* T.context(), xe = T.runPromiseWith(M), N = yield* O.make(/* @__PURE__ */ new Map()), P = /* @__PURE__ */ new Map(), F = /* @__PURE__ */ new Map(), I = /* @__PURE__ */ new Map(), L = /* @__PURE__ */ new Map(), R = /* @__PURE__ */ new Map(), z = /* @__PURE__ */ new Map(), B = (e) => {
|
|
66
|
+
if (e.startsWith("sql:")) return `sql:${i(e, w)}`;
|
|
67
|
+
if (e.startsWith("analytics:")) return `analytics:${s(e, w)}`;
|
|
68
|
+
if (e.startsWith("storage:")) return `storage:${oe(e, w)}`;
|
|
69
|
+
if (e.startsWith("nats:")) return `nats:${l(e, w)}`;
|
|
70
|
+
let t = u(e, w);
|
|
71
|
+
return w.redis?.[t] === void 0 ? e.startsWith("redis:") ? t : e : `redis:${t}`;
|
|
72
|
+
}, V = (e, t) => JSON.stringify([B(e), H(e) ? t?.tenantId ?? null : null]), Se = (e) => e.startsWith("sql:") ? w.sql?.[i(e, w)] : e.startsWith("analytics:") ? w.analytics?.[s(e, w)] : e.startsWith("storage:") ? w.storage?.[oe(e, w)] : e.startsWith("nats:") ? w.nats?.[l(e, w)] : w.redis?.[u(e, w)], H = (e) => {
|
|
72
73
|
let t = Se(e);
|
|
73
74
|
return t !== void 0 && "_tag" in t && (t._tag === "DynamicRedisConnection" || t._tag === "DynamicNatsConnection" || t._tag === "DynamicStorageConnection" || t._tag === "DynamicAnalyticsConnection" || t._tag === "DynamicSqlConnection");
|
|
74
|
-
}, Ce = (e) =>
|
|
75
|
-
if (e !== "" && !/^(redis|nats|storage|analytics|sql):[A-Za-z][A-Za-z0-9_-]*$/.test(e)) return
|
|
76
|
-
let t =
|
|
77
|
-
reference:
|
|
75
|
+
}, Ce = (e) => T.suspend(() => {
|
|
76
|
+
if (e !== "" && !/^(redis|nats|storage|analytics|sql):[A-Za-z][A-Za-z0-9_-]*$/.test(e)) return T.fail(new S.ConfigError(new C.SourceError({ message: "A connection reference must use redis:name, nats:name, storage:name, analytics:name or sql:name" })));
|
|
77
|
+
let t = H(e), n = {
|
|
78
|
+
reference: B(e),
|
|
78
79
|
tenantScoped: t
|
|
79
80
|
};
|
|
80
|
-
return t ?
|
|
81
|
-
}),
|
|
81
|
+
return t ? T.succeed(n) : Oe(e).pipe(T.as(n));
|
|
82
|
+
}), U = (e, t) => fe(e, w, t).pipe(T.flatMap((e) => v.namespaceIdentity === void 0 ? T.succeed(e) : T.try({
|
|
82
83
|
try: () => f(e, v.namespaceIdentity),
|
|
83
|
-
catch: (e) => new
|
|
84
|
-
})),
|
|
85
|
-
let n =
|
|
86
|
-
for (let e of
|
|
87
|
-
if (t.driver === "memory" && e.crossNode) return
|
|
84
|
+
catch: (e) => new S.ConfigError(new C.SourceError({ message: e instanceof Error ? e.message : "Connection namespace admission failed" }))
|
|
85
|
+
})), T.flatMap((t) => {
|
|
86
|
+
let n = B(e);
|
|
87
|
+
for (let e of E) if (B(e.reference) === n) {
|
|
88
|
+
if (t.driver === "memory" && e.crossNode) return T.fail(new S.ConfigError(new C.SourceError({ message: `Plugin ${e.consumer} role ${e.role} requires a network Redis connection; memory is local` })));
|
|
88
89
|
if (t.driver === "http" && (e.pubsub || e.atomicMulti)) {
|
|
89
90
|
let t = e.pubsub ? "Pub/Sub" : "atomic MULTI";
|
|
90
|
-
return
|
|
91
|
+
return T.fail(new S.ConfigError(new C.SourceError({ message: `Plugin ${e.consumer} role ${e.role} requires ${t}; its selected Redis connection requires the RESP driver` })));
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
|
-
return
|
|
94
|
-
}),
|
|
94
|
+
return T.succeed(t);
|
|
95
|
+
}), T.mapError((e) => y(e, "redis"))), we = (e, t) => re(e, w, t).pipe(T.flatMap((e) => v.namespaceIdentity === void 0 ? T.succeed(e) : T.try({
|
|
95
96
|
try: () => f(e, v.namespaceIdentity),
|
|
96
|
-
catch: () => new
|
|
97
|
-
})),
|
|
97
|
+
catch: () => new S.ConfigError(new C.SourceError({ message: "Connection namespace admission failed" }))
|
|
98
|
+
})), T.mapError((e) => y(e, "nats"))), Te = (e, t) => ae(e, w, t).pipe(T.flatMap((t) => {
|
|
98
99
|
if (v.namespaceIdentity?.environment === "production" && (t = {
|
|
99
100
|
...t,
|
|
100
101
|
tls: "required"
|
|
101
102
|
}), v.namespaceIdentity !== void 0 && t.keyPrefix === void 0 && (t = {
|
|
102
103
|
...t,
|
|
103
104
|
keyPrefix: `${p(v.namespaceIdentity)}/`
|
|
104
|
-
}), v.namespaceIdentity?.environment === "production" && t.keyPrefix !== `${p(v.namespaceIdentity)}/` && !t.allowSharedNamespace) return
|
|
105
|
-
for (let n of
|
|
106
|
-
if (n.pubsub || n.atomicMulti) return
|
|
105
|
+
}), v.namespaceIdentity?.environment === "production" && t.keyPrefix !== `${p(v.namespaceIdentity)}/` && !t.allowSharedNamespace) return T.fail(new S.ConfigError(new C.SourceError({ message: "A custom production storage namespace requires allowSharedNamespace" })));
|
|
106
|
+
for (let n of E) if (B(n.reference) === B(e)) {
|
|
107
|
+
if (n.pubsub || n.atomicMulti) return T.fail(new S.ConfigError(new C.SourceError({ message: "Storage cannot provide Pub/Sub or atomic MULTI" })));
|
|
107
108
|
if (n.crossNode && [
|
|
108
109
|
"memory",
|
|
109
110
|
"filesystem",
|
|
110
111
|
"database"
|
|
111
|
-
].includes(t.provider)) return
|
|
112
|
+
].includes(t.provider)) return T.fail(new S.ConfigError(new C.SourceError({ message: "Cross-node storage requires a declared object-storage service" })));
|
|
112
113
|
}
|
|
113
114
|
let n = ie(t);
|
|
114
|
-
return n ?
|
|
115
|
-
}),
|
|
115
|
+
return n ? T.fail(new S.ConfigError(new C.SourceError({ message: n }))) : T.succeed(t);
|
|
116
|
+
}), T.mapError((e) => y(e, "storage"))), Ee = (e, t) => o(e, w, t).pipe(T.flatMap((t) => {
|
|
116
117
|
v.namespaceIdentity?.environment === "production" && (t = {
|
|
117
118
|
...t,
|
|
118
119
|
tls: "required"
|
|
119
120
|
});
|
|
120
121
|
let n = a(t);
|
|
121
|
-
if (n) return
|
|
122
|
-
for (let n of
|
|
123
|
-
if (n.pubsub || n.atomicMulti) return
|
|
124
|
-
if (n.crossNode && t.provider === "duckdb") return
|
|
122
|
+
if (n) return T.fail(new S.ConfigError(new C.SourceError({ message: n })));
|
|
123
|
+
for (let n of E) if (B(n.reference) === B(e)) {
|
|
124
|
+
if (n.pubsub || n.atomicMulti) return T.fail(new S.ConfigError(new C.SourceError({ message: "Analytics cannot provide Redis Pub/Sub or atomic MULTI" })));
|
|
125
|
+
if (n.crossNode && t.provider === "duckdb") return T.fail(new S.ConfigError(new C.SourceError({ message: "Cross-node analytics requires a network provider; DuckDB is local" })));
|
|
125
126
|
}
|
|
126
|
-
return
|
|
127
|
-
}),
|
|
127
|
+
return T.succeed(t);
|
|
128
|
+
}), T.mapError((e) => y(e, "analytics"))), De = (e, t) => r(e, w, t).pipe(T.flatMap((t) => {
|
|
128
129
|
v.namespaceIdentity?.environment === "production" && !["sqlite", "turso"].includes(t.dialect) && (t = {
|
|
129
130
|
...t,
|
|
130
131
|
tls: "required"
|
|
131
132
|
});
|
|
132
133
|
let r = n(t);
|
|
133
|
-
if (r) return
|
|
134
|
-
for (let n of
|
|
135
|
-
if (n.pubsub || n.atomicMulti) return
|
|
136
|
-
if (n.crossNode && ["sqlite", "turso"].includes(t.dialect)) return
|
|
134
|
+
if (r) return T.fail(new S.ConfigError(new C.SourceError({ message: r })));
|
|
135
|
+
for (let n of E) if (B(n.reference) === B(e)) {
|
|
136
|
+
if (n.pubsub || n.atomicMulti) return T.fail(new S.ConfigError(new C.SourceError({ message: "SQL cannot provide Redis Pub/Sub or atomic MULTI" })));
|
|
137
|
+
if (n.crossNode && ["sqlite", "turso"].includes(t.dialect)) return T.fail(new S.ConfigError(new C.SourceError({ message: "Cross-node SQL requires a network SQL dialect" })));
|
|
137
138
|
}
|
|
138
|
-
return
|
|
139
|
-
}),
|
|
139
|
+
return T.succeed(t);
|
|
140
|
+
}), T.mapError((e) => y(e, "sql")));
|
|
140
141
|
function Oe(e, t) {
|
|
141
|
-
return e.startsWith("sql:") ? De(e, t) : e.startsWith("analytics:") ? Ee(e, t) : e.startsWith("storage:") ? Te(e, t) : e.startsWith("nats:") ? we(e, t) :
|
|
142
|
+
return e.startsWith("sql:") ? De(e, t) : e.startsWith("analytics:") ? Ee(e, t) : e.startsWith("storage:") ? Te(e, t) : e.startsWith("nats:") ? we(e, t) : U(e, t);
|
|
142
143
|
}
|
|
143
|
-
for (let e of
|
|
144
|
-
let
|
|
144
|
+
for (let e of E) if ((e.reference.startsWith("nats:") || e.reference.startsWith("storage:") || e.reference.startsWith("analytics:") || e.reference.startsWith("sql:")) && e.atomicMulti) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: `Plugin ${e.consumer} role ${e.role} requires atomic MULTI; NATS cannot provide Redis commands` })));
|
|
145
|
+
let W = yield* d({
|
|
145
146
|
rotationGraceMs: ve,
|
|
146
147
|
onStopping: () => {
|
|
147
|
-
|
|
148
|
+
P.clear();
|
|
148
149
|
},
|
|
149
150
|
onStopped: () => {
|
|
150
|
-
for (let e of
|
|
151
|
-
|
|
152
|
-
for (let e of
|
|
153
|
-
|
|
154
|
-
for (let e of
|
|
155
|
-
|
|
151
|
+
for (let e of F.values()) e.channels.clear(), e.listeners.clear();
|
|
152
|
+
F.clear();
|
|
153
|
+
for (let e of z.values()) e.dispose();
|
|
154
|
+
z.clear();
|
|
155
|
+
for (let e of I.values()) e.clear();
|
|
156
|
+
I.clear(), R.clear(), L.clear();
|
|
156
157
|
},
|
|
157
|
-
dispose: (e) =>
|
|
158
|
-
e.detachCommandReconnect?.(), e.binding?.current === e && delete e.binding.current, e.kind === "sql" || e.kind === "storage" || e.kind === "analytics" ? yield* e.transport.close : e.kind === "nats" ? yield*
|
|
159
|
-
}).pipe(
|
|
160
|
-
}), { close:
|
|
161
|
-
|
|
162
|
-
}, Ae = (e) =>
|
|
163
|
-
e.errors++,
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
}),
|
|
167
|
-
e.latencyMs = Number(n - t) / 1e6,
|
|
158
|
+
dispose: (e) => T.gen(function* () {
|
|
159
|
+
e.detachCommandReconnect?.(), e.binding?.current === e && delete e.binding.current, e.kind === "sql" || e.kind === "storage" || e.kind === "analytics" ? yield* e.transport.close : e.kind === "nats" ? yield* T.promise(() => e.transport.close().catch(() => void 0)) : (yield* T.promise(() => e.subscriber?.close().catch(() => void 0) ?? Promise.resolve()), yield* T.promise(() => e.client.close().catch(() => void 0)));
|
|
160
|
+
}).pipe(T.ensuring(T.sync(() => k.close(e))))
|
|
161
|
+
}), { close: G, trackOpen: ke } = W, K = (e) => {
|
|
162
|
+
W.register(e), k.register(e, e.reference);
|
|
163
|
+
}, Ae = (e) => T.sync(() => k.retire(e)).pipe(T.andThen(W.retire(e))), q = (e) => T.sync(() => k.release(e)).pipe(T.andThen(W.release(e))), J = (e) => {
|
|
164
|
+
e.errors++, k.error(e.reference);
|
|
165
|
+
}, Y = (e) => (t) => b.hasInterruptsOnly(t) ? T.void : T.sync(() => {
|
|
166
|
+
J(e);
|
|
167
|
+
}), X = (e, t) => x.currentTimeNanos.pipe(T.flatMap((n) => e.breaker.state.pipe(T.tap((r) => T.sync(() => {
|
|
168
|
+
e.latencyMs = Number(n - t) / 1e6, k.completed(e.reference, e.latencyMs), r === "open" && k.available(e, !1);
|
|
168
169
|
}))))), je = (e, t) => {
|
|
169
170
|
for (let n of e.listeners) try {
|
|
170
171
|
n(t);
|
|
171
172
|
} catch {}
|
|
172
|
-
}, Me = (t, n) =>
|
|
173
|
-
if (t.kind === "sql" || t.kind === "storage" || t.kind === "analytics") return yield*
|
|
173
|
+
}, Me = (t, n) => T.gen(function* () {
|
|
174
|
+
if (t.kind === "sql" || t.kind === "storage" || t.kind === "analytics") return yield* T.fail(new e({
|
|
174
175
|
operation: "subscribe",
|
|
175
176
|
key: "",
|
|
176
177
|
cause: "Selected connection has no Pub/Sub transport"
|
|
177
178
|
}));
|
|
178
179
|
if (t.subscriber !== void 0) return;
|
|
179
|
-
if (t.kind === "redis" && t.config.driver === "http") return yield*
|
|
180
|
+
if (t.kind === "redis" && t.config.driver === "http") return yield* T.fail(new e({
|
|
180
181
|
operation: "subscribe",
|
|
181
182
|
key: "",
|
|
182
183
|
cause: "Redis Pub/Sub requires the RESP driver"
|
|
183
184
|
}));
|
|
184
|
-
let r = yield* (t.kind === "nats" ?
|
|
185
|
+
let r = yield* (t.kind === "nats" ? T.succeed(te(t.transport, t.config.keyPrefix)) : t.memory === void 0 ? (v.connectSubscriber ?? m)(t.config) : T.sync(() => t.memory.subscriber())).pipe(T.mapError(() => new e({
|
|
185
186
|
operation: "subscribe",
|
|
186
187
|
key: "",
|
|
187
188
|
cause: "Redis subscriber could not be opened"
|
|
188
189
|
})));
|
|
189
190
|
t.subscriber = r, t.binding = n, r.onMessage((e, n) => {
|
|
190
191
|
let r = t.binding;
|
|
191
|
-
if (!(
|
|
192
|
+
if (!(W.stopped || r?.current !== t || t.closed)) for (let t of r.channels.get(e) ?? []) try {
|
|
192
193
|
t(n);
|
|
193
194
|
} catch {}
|
|
194
195
|
}), r.onTransportEvent((e) => {
|
|
195
196
|
let n = t.binding;
|
|
196
|
-
!
|
|
197
|
-
}), yield*
|
|
197
|
+
!W.stopped && n?.current === t && !t.closed && je(n, e);
|
|
198
|
+
}), yield* T.forEach([...n.channels.keys()], (t) => T.tryPromise({
|
|
198
199
|
try: () => r.subscribe(t),
|
|
199
200
|
catch: () => new e({
|
|
200
201
|
operation: "subscribe",
|
|
@@ -202,35 +203,35 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
202
203
|
cause: "Redis subscription could not be registered"
|
|
203
204
|
})
|
|
204
205
|
}), { discard: !0 });
|
|
205
|
-
}), Ne = (e, t) => ke(Me(e, t)),
|
|
206
|
-
|
|
206
|
+
}), Ne = (e, t) => ke(Me(e, t)), Pe = (e) => {
|
|
207
|
+
k.activate(e), e.kind === "storage" && L.set(V(e.reference, e.context), e.config);
|
|
207
208
|
let t = e.binding;
|
|
208
209
|
if (t === void 0) return;
|
|
209
210
|
let n = t.current;
|
|
210
211
|
t.current = e, n !== void 0 && n !== e && t.channels.size > 0 && (je(t, { kind: "disconnected" }), je(t, { kind: "reconnected" }));
|
|
211
|
-
},
|
|
212
|
-
let s = yield* o((n.startsWith("sql:") ? De(n, r).pipe(
|
|
212
|
+
}, Fe = (n, r, i, a) => T.uninterruptibleMask((o) => T.gen(function* () {
|
|
213
|
+
let s = yield* o((n.startsWith("sql:") ? De(n, r).pipe(T.map((e) => ({
|
|
213
214
|
kind: "sql",
|
|
214
215
|
config: e
|
|
215
|
-
}))) : n.startsWith("analytics:") ? Ee(n, r).pipe(
|
|
216
|
+
}))) : n.startsWith("analytics:") ? Ee(n, r).pipe(T.map((e) => ({
|
|
216
217
|
kind: "analytics",
|
|
217
218
|
config: e
|
|
218
|
-
}))) : n.startsWith("storage:") ? Te(n, r).pipe(
|
|
219
|
+
}))) : n.startsWith("storage:") ? Te(n, r).pipe(T.map((e) => ({
|
|
219
220
|
kind: "storage",
|
|
220
221
|
config: e
|
|
221
|
-
}))) : n.startsWith("nats:") ? we(n, r).pipe(
|
|
222
|
+
}))) : n.startsWith("nats:") ? we(n, r).pipe(T.map((e) => ({
|
|
222
223
|
kind: "nats",
|
|
223
224
|
config: e
|
|
224
|
-
}))) :
|
|
225
|
+
}))) : U(n, r).pipe(T.map((e) => ({
|
|
225
226
|
kind: "redis",
|
|
226
227
|
config: e
|
|
227
|
-
})))).pipe(
|
|
228
|
+
})))).pipe(T.interruptible)), c = yield* x.currentTimeMillis, l = Se(n), u = H(n), d = yield* ge.make({
|
|
228
229
|
name: n,
|
|
229
230
|
failureThreshold: ye,
|
|
230
231
|
resetTimeout: be,
|
|
231
232
|
...s.kind === "sql" ? { isFailure: (t) => t instanceof e || t instanceof _ || typeof t == "object" && !!t && "_tag" in t && t._tag === "HotError" && "reason" in t && t.reason === "unavailable" } : {}
|
|
232
233
|
}), f = yield* pe.make(), p = {
|
|
233
|
-
reference:
|
|
234
|
+
reference: B(n),
|
|
234
235
|
context: r,
|
|
235
236
|
breaker: d,
|
|
236
237
|
dynamic: u,
|
|
@@ -245,9 +246,9 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
245
246
|
closed: !1
|
|
246
247
|
}, m;
|
|
247
248
|
if (s.kind === "sql") {
|
|
248
|
-
if (v.connectSql === void 0) return yield*
|
|
249
|
-
if (a !== void 0 && (a.kind !== "sql" || a.config.dialect !== s.config.dialect)) return yield*
|
|
250
|
-
let t = yield* v.connectSql(s.config).pipe(
|
|
249
|
+
if (v.connectSql === void 0) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "SQL requires an app-owned SQL connector" })));
|
|
250
|
+
if (a !== void 0 && (a.kind !== "sql" || a.config.dialect !== s.config.dialect)) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "SQL rotation must preserve the dialect contract" })));
|
|
251
|
+
let t = yield* v.connectSql(s.config).pipe(T.mapError(() => new e({
|
|
251
252
|
operation: "connect",
|
|
252
253
|
key: "",
|
|
253
254
|
cause: "SQL transport could not be constructed"
|
|
@@ -258,14 +259,14 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
258
259
|
drainUntilReleased: !0,
|
|
259
260
|
transport: t,
|
|
260
261
|
config: s.config
|
|
261
|
-
},
|
|
262
|
+
}, K(m), yield* o(t.probe.pipe(T.interruptible, T.timeout("5 seconds"), T.mapError(() => new e({
|
|
262
263
|
operation: "connect",
|
|
263
264
|
key: "",
|
|
264
265
|
cause: "SQL readiness failed"
|
|
265
|
-
})))).pipe(
|
|
266
|
+
})))).pipe(T.onError(() => G(m)));
|
|
266
267
|
} else if (s.kind === "analytics") {
|
|
267
|
-
if (v.connectAnalytics === void 0) return yield*
|
|
268
|
-
let t = yield* v.connectAnalytics(s.config).pipe(
|
|
268
|
+
if (v.connectAnalytics === void 0) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Analytics requires an app-owned analytics connector" })));
|
|
269
|
+
let t = yield* v.connectAnalytics(s.config).pipe(T.mapError(() => new e({
|
|
269
270
|
operation: "connect",
|
|
270
271
|
key: "",
|
|
271
272
|
cause: "Analytics transport could not be constructed"
|
|
@@ -275,24 +276,24 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
275
276
|
kind: "analytics",
|
|
276
277
|
transport: t,
|
|
277
278
|
config: s.config
|
|
278
|
-
},
|
|
279
|
-
if (a.kind !== "analytics" || a.config.provider !== s.config.provider) return yield*
|
|
279
|
+
}, K(m), a !== void 0) {
|
|
280
|
+
if (a.kind !== "analytics" || a.config.provider !== s.config.provider) return yield* G(m), yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Analytics rotation must preserve the provider contract" })));
|
|
280
281
|
let e = a.transport.sink, n = t.sink;
|
|
281
|
-
if (JSON.stringify(e.info.capabilities) !== JSON.stringify(n.info.capabilities) || JSON.stringify(e.mirror?.tables ?? []) !== JSON.stringify(n.mirror?.tables ?? []) || e.mirror?.primaryKey !== n.mirror?.primaryKey) return yield*
|
|
282
|
+
if (JSON.stringify(e.info.capabilities) !== JSON.stringify(n.info.capabilities) || JSON.stringify(e.mirror?.tables ?? []) !== JSON.stringify(n.mirror?.tables ?? []) || e.mirror?.primaryKey !== n.mirror?.primaryKey) return yield* G(m), yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Analytics rotation must preserve capabilities and mirror schema" })));
|
|
282
283
|
}
|
|
283
|
-
yield* o(t.probe.pipe(
|
|
284
|
+
yield* o(t.probe.pipe(T.interruptible, T.timeout("5 seconds"), T.mapError(() => new e({
|
|
284
285
|
operation: "connect",
|
|
285
286
|
key: "",
|
|
286
287
|
cause: a === void 0 ? "Analytics readiness failed" : "Replacement analytics readiness failed"
|
|
287
|
-
})))).pipe(
|
|
288
|
+
})))).pipe(T.onError(() => G(m)));
|
|
288
289
|
} else if (s.kind === "storage") {
|
|
289
|
-
if (v.connectStorage === void 0) return yield*
|
|
290
|
+
if (v.connectStorage === void 0) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Storage requires an app-owned storage connector" })));
|
|
290
291
|
let t;
|
|
291
292
|
if (s.config.provider === "memory") {
|
|
292
|
-
let e =
|
|
293
|
-
t =
|
|
293
|
+
let e = V(n, r);
|
|
294
|
+
t = I.get(e), t === void 0 && (t = /* @__PURE__ */ new Map(), I.set(e, t));
|
|
294
295
|
}
|
|
295
|
-
let i = yield* v.connectStorage(s.config, t === void 0 ? {} : { memory: t }).pipe(
|
|
296
|
+
let i = yield* v.connectStorage(s.config, t === void 0 ? {} : { memory: t }).pipe(T.mapError(() => new e({
|
|
296
297
|
operation: "connect",
|
|
297
298
|
key: "",
|
|
298
299
|
cause: "Storage transport could not be constructed"
|
|
@@ -302,15 +303,15 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
302
303
|
kind: "storage",
|
|
303
304
|
transport: i,
|
|
304
305
|
config: s.config
|
|
305
|
-
},
|
|
306
|
-
let o =
|
|
307
|
-
o !== void 0 && i.provider.bindDataStore?.(o), a !== void 0 && (yield* i.probe.pipe(
|
|
306
|
+
}, K(m);
|
|
307
|
+
let o = R.get(V(n, r));
|
|
308
|
+
o !== void 0 && i.provider.bindDataStore?.(o), a !== void 0 && (yield* i.probe.pipe(T.timeout("5 seconds"), T.mapError(() => new e({
|
|
308
309
|
operation: "connect",
|
|
309
310
|
key: "",
|
|
310
311
|
cause: "Replacement storage readiness failed"
|
|
311
|
-
})),
|
|
312
|
+
})), T.onError(() => G(m))));
|
|
312
313
|
} else if (s.kind === "nats") {
|
|
313
|
-
let t = s.config, n = yield* (v.connectNats === void 0 ? ne(t).pipe(
|
|
314
|
+
let t = s.config, n = yield* (v.connectNats === void 0 ? ne(t).pipe(T.timeout("5 seconds"), T.interruptible) : v.connectNats(t)).pipe(T.mapError(() => new e({
|
|
314
315
|
operation: "connect",
|
|
315
316
|
key: "",
|
|
316
317
|
cause: "NATS driver could not be opened"
|
|
@@ -320,21 +321,21 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
320
321
|
kind: "nats",
|
|
321
322
|
transport: n,
|
|
322
323
|
config: t
|
|
323
|
-
},
|
|
324
|
+
}, K(m);
|
|
324
325
|
let r = m;
|
|
325
326
|
m.detachCommandReconnect = n.onTransportEvent((e) => {
|
|
326
|
-
e.kind
|
|
327
|
+
e.kind !== "reconnected" || W.stopped || r.closed || r.retired || T.runSyncWith(M)(r.breaker.reset);
|
|
327
328
|
});
|
|
328
329
|
} else {
|
|
329
330
|
let i = s.config, { keyPrefix: a, ...o } = i, c;
|
|
330
331
|
if (i.driver === "memory") {
|
|
331
|
-
let e =
|
|
332
|
-
if (c =
|
|
333
|
-
let { makeMemoryConnectionState: t } = yield*
|
|
334
|
-
c = t(i.memoryClock),
|
|
332
|
+
let e = V(n, r);
|
|
333
|
+
if (c = z.get(e), c === void 0) {
|
|
334
|
+
let { makeMemoryConnectionState: t } = yield* T.promise(() => import("./connectionMemory.js"));
|
|
335
|
+
c = t(i.memoryClock), z.set(e, c);
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
|
-
let l = yield* (c === void 0 ? v.connect === void 0 ? de(o).pipe(
|
|
338
|
+
let l = yield* (c === void 0 ? v.connect === void 0 ? de(o).pipe(T.interruptible) : v.connect(o) : T.sync(() => c.command())).pipe(T.map((e) => t(e, a)), T.mapError(() => new e({
|
|
338
339
|
operation: "connect",
|
|
339
340
|
key: "",
|
|
340
341
|
cause: "Connection driver could not be opened"
|
|
@@ -345,63 +346,63 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
345
346
|
client: l,
|
|
346
347
|
config: i,
|
|
347
348
|
...c === void 0 ? {} : { memory: c }
|
|
348
|
-
},
|
|
349
|
+
}, K(m);
|
|
349
350
|
let u = m, d = l.onReconnect?.(() => {
|
|
350
|
-
|
|
351
|
+
W.stopped || u.closed || u.retired || T.runSyncWith(M)(u.breaker.reset);
|
|
351
352
|
});
|
|
352
353
|
d !== void 0 && (m.detachCommandReconnect = d);
|
|
353
354
|
}
|
|
354
|
-
return
|
|
355
|
-
})),
|
|
356
|
-
let t =
|
|
357
|
-
/^(redis|nats|storage|analytics|sql):[A-Za-z][A-Za-z0-9_-]*$/.test(t) &&
|
|
358
|
-
}))), Z = (t, n) =>
|
|
359
|
-
if (
|
|
355
|
+
return k.available(m, m.kind !== "storage" || a !== void 0), i?.(m), a?.binding !== void 0 && (m.binding = a.binding, a.binding.channels.size > 0 && (yield* Ne(m, a.binding).pipe(T.onError(() => G(m))))), i === void 0 && Pe(m), m;
|
|
356
|
+
})), Ie = (e, t, n, r) => ke(Fe(e, t, n, r)).pipe(T.tapCause((t) => b.hasInterruptsOnly(t) ? T.void : T.sync(() => {
|
|
357
|
+
let t = B(e);
|
|
358
|
+
/^(redis|nats|storage|analytics|sql):[A-Za-z][A-Za-z0-9_-]*$/.test(t) && k.error(t);
|
|
359
|
+
}))), Z = (t, n) => O.modifyEffect(N, (r) => T.gen(function* () {
|
|
360
|
+
if (W.stopped) return yield* T.fail(new e({
|
|
360
361
|
operation: "connect",
|
|
361
362
|
key: "",
|
|
362
363
|
cause: "Connection registry is closed"
|
|
363
364
|
}));
|
|
364
|
-
let i =
|
|
365
|
-
if (o && o.expiresAt > a) return o.active++,
|
|
365
|
+
let i = V(t, n), a = yield* x.currentTimeMillis, o = r.get(i);
|
|
366
|
+
if (o && o.expiresAt > a) return o.active++, k.acquire(o), o.lastUsed = a, [o, r];
|
|
366
367
|
let s = new Map(r);
|
|
367
|
-
if (!o &&
|
|
368
|
+
if (!o && H(t)) {
|
|
368
369
|
let r = [...s.entries()].filter(([, e]) => e.dynamic);
|
|
369
|
-
if (r.length >=
|
|
370
|
+
if (r.length >= j) {
|
|
370
371
|
let a = r.filter(([, e]) => e.active === 0 && (e.binding?.channels.size ?? 0) === 0).sort((e, t) => e[1].lastUsed - t[1].lastUsed)[0];
|
|
371
|
-
if (!a) return yield*
|
|
372
|
+
if (!a) return yield* T.fail(new e({
|
|
372
373
|
operation: "connect",
|
|
373
374
|
key: "",
|
|
374
375
|
cause: "All dynamic connection pools are busy; maxDynamicPools reached"
|
|
375
376
|
}));
|
|
376
|
-
let c = yield*
|
|
377
|
-
return s.delete(a[0]), yield* Ae(a[1]), c.active++,
|
|
377
|
+
let c = yield* Ie(t, n, void 0, o);
|
|
378
|
+
return s.delete(a[0]), yield* Ae(a[1]), c.active++, k.acquire(c), s.set(i, c), [c, s];
|
|
378
379
|
}
|
|
379
380
|
}
|
|
380
|
-
let c = yield*
|
|
381
|
-
return o && (yield* Ae(o)), c.active++,
|
|
382
|
-
})), Q = (t, n, r, i) =>
|
|
383
|
-
let n = yield*
|
|
384
|
-
return t.commands++,
|
|
381
|
+
let c = yield* Ie(t, n, void 0, o);
|
|
382
|
+
return o && (yield* Ae(o)), c.active++, k.acquire(c), s.set(i, c), [c, s];
|
|
383
|
+
})), Q = (t, n, r, i) => T.acquireUseRelease(Z(t, n), (t) => T.gen(function* () {
|
|
384
|
+
let n = yield* x.currentTimeNanos;
|
|
385
|
+
return t.commands++, k.command(t.reference), yield* t.breaker(i(t)).pipe(T.tap(() => T.sync(() => k.available(t, !0))), T.tapCause(Y(t)), T.mapError((t) => t instanceof _e ? new e({
|
|
385
386
|
operation: r,
|
|
386
387
|
key: "",
|
|
387
388
|
cause: `Connection circuit is open; retry after ${t.retryAfterMs} ms`
|
|
388
|
-
}) : t),
|
|
389
|
-
}),
|
|
389
|
+
}) : t), T.ensuring(X(t, n)));
|
|
390
|
+
}), q), Le = (t, n, r, i) => xe(Q(t, n, r, (t) => T.tryPromise({
|
|
390
391
|
try: () => i(t),
|
|
391
392
|
catch: () => new e({
|
|
392
393
|
operation: r,
|
|
393
394
|
key: "",
|
|
394
395
|
cause: "Connection command failed"
|
|
395
396
|
})
|
|
396
|
-
}))),
|
|
397
|
+
}))), Re = (t, n) => Q(t, n, "probe", (t) => t.kind === "sql" ? t.transport.probe : t.kind === "analytics" ? t.transport.probe.pipe(T.mapError(() => new e({
|
|
397
398
|
operation: "probe",
|
|
398
399
|
key: "",
|
|
399
400
|
cause: "Analytics readiness probe failed"
|
|
400
|
-
}))) : t.kind === "storage" ? t.transport.probe.pipe(
|
|
401
|
+
}))) : t.kind === "storage" ? t.transport.probe.pipe(T.mapError(() => new e({
|
|
401
402
|
operation: "probe",
|
|
402
403
|
key: "",
|
|
403
404
|
cause: "Storage readiness probe failed"
|
|
404
|
-
}))) :
|
|
405
|
+
}))) : T.tryPromise({
|
|
405
406
|
try: async () => {
|
|
406
407
|
if (t.kind === "nats") return t.transport.flush();
|
|
407
408
|
if (t.client.ping === void 0 || await t.client.ping() !== "PONG") throw Error("Redis readiness requires PONG");
|
|
@@ -411,15 +412,15 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
411
412
|
key: "",
|
|
412
413
|
cause: "Connection readiness probe failed"
|
|
413
414
|
})
|
|
414
|
-
})), $ = (e, t, n, r) =>
|
|
415
|
+
})), $ = (e, t, n, r) => Le(e, t, n, (e) => {
|
|
415
416
|
if (e.kind !== "redis") throw Error("Redis commands require a Redis connection");
|
|
416
417
|
return r(e.client);
|
|
417
|
-
}),
|
|
418
|
-
if (e.includes(":") && !e.startsWith("redis:")) return yield*
|
|
419
|
-
let n = t === void 0 ? void 0 : Object.freeze({ tenantId: t.tenantId }), r = yield*
|
|
418
|
+
}), ze = (e, t) => T.gen(function* () {
|
|
419
|
+
if (e.includes(":") && !e.startsWith("redis:")) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Redis commands require a redis:name reference" })));
|
|
420
|
+
let n = t === void 0 ? void 0 : Object.freeze({ tenantId: t.tenantId }), r = yield* T.acquireUseRelease(Z(e, n), (e) => e.kind === "redis" ? T.succeed({
|
|
420
421
|
ping: e.client.ping !== void 0,
|
|
421
422
|
crossNode: e.client.crossNode
|
|
422
|
-
}) :
|
|
423
|
+
}) : T.fail(new S.ConfigError(new C.SourceError({ message: "Redis commands require a redis:name reference" }))), q), i = V(e, n), a = P.get(i);
|
|
423
424
|
if (a) return a;
|
|
424
425
|
let o = (t, r) => $(e, n, t, (e) => {
|
|
425
426
|
if (e.readOnly === void 0) throw Error("Connection has no read-only transport");
|
|
@@ -447,15 +448,15 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
447
448
|
scan: (t, r, i) => $(e, n, "scan", (e) => e.scan(t, r, i)),
|
|
448
449
|
close: () => Promise.resolve()
|
|
449
450
|
};
|
|
450
|
-
return
|
|
451
|
+
return H(e) || P.set(i, s), s;
|
|
451
452
|
});
|
|
452
453
|
return {
|
|
453
|
-
client:
|
|
454
|
+
client: ze,
|
|
454
455
|
config: Oe,
|
|
455
456
|
validate: Ce,
|
|
456
|
-
storage: (e, t) =>
|
|
457
|
-
if (!e.startsWith("storage:")) return yield*
|
|
458
|
-
let n = t === void 0 ? void 0 : Object.freeze({ tenantId: t.tenantId }), r = yield*
|
|
457
|
+
storage: (e, t) => T.gen(function* () {
|
|
458
|
+
if (!e.startsWith("storage:")) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Storage requires a storage:name reference" })));
|
|
459
|
+
let n = t === void 0 ? void 0 : Object.freeze({ tenantId: t.tenantId }), r = yield* T.acquireUseRelease(Z(e, n), (e) => e.kind === "storage" ? T.succeed(e.config) : T.fail(new S.ConfigError(new C.SourceError({ message: "Storage requires a storage:name reference" }))), q), i = V(e, n), a = (e) => new h({
|
|
459
460
|
provider: r.provider,
|
|
460
461
|
message: "Storage connection operation failed",
|
|
461
462
|
transient: e instanceof h ? e.transient : !0,
|
|
@@ -463,48 +464,48 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
463
464
|
...e instanceof h && e.status !== void 0 ? { status: e.status } : {}
|
|
464
465
|
});
|
|
465
466
|
return se({
|
|
466
|
-
config: () =>
|
|
467
|
-
command: (t, r) =>
|
|
468
|
-
if (e.kind !== "storage") return yield*
|
|
469
|
-
let t = yield*
|
|
470
|
-
return e.commands++,
|
|
471
|
-
}),
|
|
472
|
-
stream: (t, r) =>
|
|
473
|
-
if (e.kind !== "storage") return yield*
|
|
474
|
-
let n = yield*
|
|
475
|
-
e.commands++,
|
|
476
|
-
let i = e.transport.provider, o = i.getStream === void 0 ? i.get(t).pipe(
|
|
467
|
+
config: () => L.get(i) ?? r,
|
|
468
|
+
command: (t, r) => T.acquireUseRelease(Z(e, n).pipe(T.mapError(a)), (e) => T.gen(function* () {
|
|
469
|
+
if (e.kind !== "storage") return yield* T.fail(a(void 0));
|
|
470
|
+
let t = yield* x.currentTimeNanos;
|
|
471
|
+
return e.commands++, k.command(e.reference), yield* e.breaker(r(e.transport.provider)).pipe(T.tap(() => T.sync(() => k.available(e, !0))), T.tapCause(Y(e)), T.mapError(a), T.ensuring(X(e, t)));
|
|
472
|
+
}), q),
|
|
473
|
+
stream: (t, r) => D.unwrap(T.acquireRelease(Z(e, n).pipe(T.mapError(a)), q).pipe(T.flatMap((e) => T.gen(function* () {
|
|
474
|
+
if (e.kind !== "storage") return yield* T.fail(a(void 0));
|
|
475
|
+
let n = yield* x.currentTimeNanos;
|
|
476
|
+
e.commands++, k.command(e.reference), yield* T.addFinalizer(() => X(e, n));
|
|
477
|
+
let i = e.transport.provider, o = i.getStream === void 0 ? i.get(t).pipe(T.flatMap((t) => t.bytes.byteLength !== r.size || t.contentType !== (r.contentType ?? "application/octet-stream") ? T.fail(new h({
|
|
477
478
|
provider: e.config.provider,
|
|
478
479
|
message: "Storage metadata changed before stream consumption",
|
|
479
480
|
transient: !0
|
|
480
|
-
})) :
|
|
481
|
+
})) : T.succeed(D.succeed(t.bytes)))) : T.acquireRelease(i.getStream(t), (e) => e.close ?? T.void).pipe(T.flatMap((t) => t.size !== void 0 && t.size !== r.size || t.contentType !== (r.contentType ?? "application/octet-stream") ? T.fail(new h({
|
|
481
482
|
provider: e.config.provider,
|
|
482
483
|
message: "Storage metadata changed before stream consumption",
|
|
483
484
|
transient: !0
|
|
484
|
-
})) :
|
|
485
|
-
return yield* e.breaker(o).pipe(
|
|
485
|
+
})) : T.succeed(t.stream)));
|
|
486
|
+
return yield* e.breaker(o).pipe(T.tap(() => T.sync(() => k.available(e, !0))), T.tapCause(Y(e)), T.mapError(a), T.map((t) => t.pipe(D.mapError(a), D.tapCause((t) => Y(e)(t)))));
|
|
486
487
|
})))),
|
|
487
488
|
bind: (e) => {
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
for (let t of
|
|
489
|
+
if (W.stopped) throw Error("Connection registry is closed");
|
|
490
|
+
R.set(i, e);
|
|
491
|
+
for (let t of T.runSyncWith(M)(O.get(N)).values()) t.kind === "storage" && V(t.reference, t.context) === i && t.transport.provider.bindDataStore?.(e);
|
|
491
492
|
}
|
|
492
493
|
});
|
|
493
494
|
}),
|
|
494
|
-
analytics: (e, t) =>
|
|
495
|
-
if (!e.startsWith("analytics:")) return yield*
|
|
496
|
-
let n = t === void 0 ? void 0 : Object.freeze({ tenantId: t.tenantId }), r = yield*
|
|
495
|
+
analytics: (e, t) => T.gen(function* () {
|
|
496
|
+
if (!e.startsWith("analytics:")) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Analytics requires an analytics:name reference" })));
|
|
497
|
+
let n = t === void 0 ? void 0 : Object.freeze({ tenantId: t.tenantId }), r = yield* T.acquireUseRelease(Z(e, n), (e) => e.kind === "analytics" ? T.succeed(e.transport.sink) : T.fail(new S.ConfigError(new C.SourceError({ message: "Analytics requires an analytics:name reference" }))), q), i = Object.freeze({
|
|
497
498
|
provider: r.info.provider,
|
|
498
499
|
capabilities: Object.freeze({ ...r.info.capabilities })
|
|
499
|
-
}), a = (t, r) => Q(e, n, t, (e) => e.kind === "analytics" ?
|
|
500
|
+
}), a = (t, r) => Q(e, n, t, (e) => e.kind === "analytics" ? T.suspend(() => r(e.transport.sink)).pipe(T.mapError((e) => e instanceof g ? e : new _({
|
|
500
501
|
provider: i.provider,
|
|
501
502
|
operation: t,
|
|
502
503
|
cause: "Analytics connection operation failed"
|
|
503
|
-
}))) :
|
|
504
|
+
}))) : T.fail(new _({
|
|
504
505
|
provider: i.provider,
|
|
505
506
|
operation: t,
|
|
506
507
|
cause: "Selected connection is not analytics"
|
|
507
|
-
}))).pipe(
|
|
508
|
+
}))).pipe(T.mapError((e) => e instanceof g ? e : new _({
|
|
508
509
|
provider: i.provider,
|
|
509
510
|
operation: t,
|
|
510
511
|
cause: "Analytics connection operation failed"
|
|
@@ -518,116 +519,116 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
518
519
|
...r.mirror === void 0 ? {} : { mirror: {
|
|
519
520
|
tables: Object.freeze([...r.mirror.tables]),
|
|
520
521
|
...r.mirror.primaryKey === void 0 ? {} : { primaryKey: r.mirror.primaryKey },
|
|
521
|
-
upsert: (e) => a("mirror.upsert", (t) => t.mirror === void 0 ?
|
|
522
|
+
upsert: (e) => a("mirror.upsert", (t) => t.mirror === void 0 ? T.fail(new g({
|
|
522
523
|
provider: i.provider,
|
|
523
524
|
capability: "mirror"
|
|
524
525
|
})) : t.mirror.upsert(e)),
|
|
525
|
-
remove: (e) => a("mirror.remove", (t) => t.mirror === void 0 ?
|
|
526
|
+
remove: (e) => a("mirror.remove", (t) => t.mirror === void 0 ? T.fail(new g({
|
|
526
527
|
provider: i.provider,
|
|
527
528
|
capability: "mirror"
|
|
528
529
|
})) : t.mirror.remove(e)),
|
|
529
|
-
maxVersion: (e) => a("mirror.maxVersion", (t) => t.mirror === void 0 ?
|
|
530
|
+
maxVersion: (e) => a("mirror.maxVersion", (t) => t.mirror === void 0 ? T.fail(new g({
|
|
530
531
|
provider: i.provider,
|
|
531
532
|
capability: "mirror"
|
|
532
533
|
})) : t.mirror.maxVersion(e))
|
|
533
534
|
} }
|
|
534
535
|
};
|
|
535
536
|
}),
|
|
536
|
-
sql: (t, n) =>
|
|
537
|
-
if (!/^sql:[A-Za-z][A-Za-z0-9_-]*$/.test(t)) return yield*
|
|
538
|
-
let r = n === void 0 ? void 0 : Object.freeze({ tenantId: n.tenantId }), i = yield*
|
|
537
|
+
sql: (t, n) => T.gen(function* () {
|
|
538
|
+
if (!/^sql:[A-Za-z][A-Za-z0-9_-]*$/.test(t)) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "SQL requires a sql:name reference" })));
|
|
539
|
+
let r = n === void 0 ? void 0 : Object.freeze({ tenantId: n.tenantId }), i = yield* T.acquireUseRelease(Z(t, r), (e) => e.kind === "sql" ? T.succeed(e.config.dialect) : T.fail(new S.ConfigError(new C.SourceError({ message: "SQL requires a sql:name reference" }))), q), a = () => new e({
|
|
539
540
|
operation: "sql",
|
|
540
541
|
key: "",
|
|
541
542
|
cause: "SQL connection operation failed"
|
|
542
543
|
}), o = (e) => typeof e == "object" && e && "_tag" in e && e._tag === "SqlError" ? a() : e;
|
|
543
544
|
return {
|
|
544
545
|
dialect: i,
|
|
545
|
-
use: (e) => Q(t, r, "sql", (t) => t.kind === "sql" ?
|
|
546
|
-
stream: (n) =>
|
|
547
|
-
let i = yield*
|
|
548
|
-
if (i.kind !== "sql") return
|
|
549
|
-
let s = yield*
|
|
550
|
-
i.commands++,
|
|
551
|
-
let c = yield*
|
|
552
|
-
return
|
|
546
|
+
use: (e) => Q(t, r, "sql", (t) => t.kind === "sql" ? T.suspend(() => e(t.transport.client)).pipe(T.mapError(o)) : T.fail(a())),
|
|
547
|
+
stream: (n) => D.unwrap(T.gen(function* () {
|
|
548
|
+
let i = yield* T.acquireRelease(Z(t, r), q);
|
|
549
|
+
if (i.kind !== "sql") return D.fail(a());
|
|
550
|
+
let s = yield* x.currentTimeNanos;
|
|
551
|
+
i.commands++, k.command(i.reference), yield* T.addFinalizer(() => X(i, s));
|
|
552
|
+
let c = yield* T.scope, l = yield* T.cached(D.toPull(D.suspend(() => n(i.transport.client))).pipe(T.provideService(he.Scope, c))), u = T.flatMap(l, (e) => e);
|
|
553
|
+
return D.fromPull(T.succeed(i.breaker(u.pipe(T.mapError(o), me.catchDone(() => T.succeed(null)))).pipe(T.tap(() => T.sync(() => k.available(i, !0))), T.tapCause(Y(i)), T.mapError((t) => t instanceof _e ? new e({
|
|
553
554
|
operation: "sql",
|
|
554
555
|
key: "",
|
|
555
556
|
cause: `Connection circuit is open; retry after ${t.retryAfterMs} ms`
|
|
556
|
-
}) : t),
|
|
557
|
+
}) : t), T.flatMap((e) => e === null ? b.done() : T.succeed(e)))));
|
|
557
558
|
}))
|
|
558
559
|
};
|
|
559
560
|
}),
|
|
560
|
-
pubsub: (t, n) =>
|
|
561
|
-
if (t.includes(":") && !/^(redis|nats):/.test(t)) return yield*
|
|
562
|
-
if (
|
|
561
|
+
pubsub: (t, n) => T.gen(function* () {
|
|
562
|
+
if (t.includes(":") && !/^(redis|nats):/.test(t)) return yield* T.fail(new S.ConfigError(new C.SourceError({ message: "Pub/Sub requires a Redis or NATS reference" })));
|
|
563
|
+
if (W.stopped) return yield* T.fail(new e({
|
|
563
564
|
operation: "subscribe",
|
|
564
565
|
key: "",
|
|
565
566
|
cause: "Connection registry is closed"
|
|
566
567
|
}));
|
|
567
|
-
let r = n === void 0 ? void 0 : Object.freeze({ tenantId: n.tenantId }), i = yield* t.startsWith("nats:") ? we(t, r) :
|
|
568
|
-
if (
|
|
568
|
+
let r = n === void 0 ? void 0 : Object.freeze({ tenantId: n.tenantId }), i = yield* t.startsWith("nats:") ? we(t, r) : U(t, r);
|
|
569
|
+
if (W.stopped) return yield* T.fail(new e({
|
|
569
570
|
operation: "subscribe",
|
|
570
571
|
key: "",
|
|
571
572
|
cause: "Connection registry is closed"
|
|
572
573
|
}));
|
|
573
|
-
if ("driver" in i && i.driver === "http") return yield*
|
|
574
|
+
if ("driver" in i && i.driver === "http") return yield* T.fail(new e({
|
|
574
575
|
operation: "subscribe",
|
|
575
576
|
key: "",
|
|
576
577
|
cause: "Redis Pub/Sub requires the RESP driver"
|
|
577
578
|
}));
|
|
578
|
-
let a =
|
|
579
|
+
let a = V(t, r), o = F.get(a);
|
|
579
580
|
o === void 0 && (o = {
|
|
580
581
|
channels: /* @__PURE__ */ new Map(),
|
|
581
582
|
listeners: /* @__PURE__ */ new Set(),
|
|
582
583
|
consumers: 0
|
|
583
|
-
},
|
|
584
|
+
}, F.set(a, o));
|
|
584
585
|
let s = o;
|
|
585
586
|
s.consumers++;
|
|
586
|
-
let c = !1, l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set(), d = (e) =>
|
|
587
|
+
let c = !1, l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set(), d = (e) => O.updateEffect(N, (t) => T.gen(function* () {
|
|
587
588
|
if (e.released) return t;
|
|
588
589
|
e.released = !0, l.delete(e);
|
|
589
590
|
let n = s.channels.get(e.channel);
|
|
590
591
|
if (n?.delete(e.handler), n?.size === 0) {
|
|
591
592
|
s.channels.delete(e.channel);
|
|
592
593
|
let t = s.current;
|
|
593
|
-
t?.subscriber !== void 0 && !t.closed && (yield*
|
|
594
|
-
|
|
594
|
+
t?.subscriber !== void 0 && !t.closed && (yield* T.promise(() => t.subscriber.unsubscribe(e.channel).catch(() => {
|
|
595
|
+
J(t);
|
|
595
596
|
})));
|
|
596
597
|
}
|
|
597
598
|
return t;
|
|
598
|
-
})).pipe(
|
|
599
|
-
c = !0, yield*
|
|
599
|
+
})).pipe(T.uninterruptible), f = yield* T.cached(T.gen(function* () {
|
|
600
|
+
c = !0, yield* O.update(N, (e) => e);
|
|
600
601
|
for (let e of u) s.listeners.delete(e);
|
|
601
|
-
u.clear(), yield*
|
|
602
|
-
}).pipe(
|
|
602
|
+
u.clear(), yield* T.forEach([...l], d, { discard: !0 }), s.consumers--, s.consumers === 0 && F.get(a) === s && F.delete(a);
|
|
603
|
+
}).pipe(T.uninterruptible)), p = (n) => t.startsWith("nats:") ? T.try({
|
|
603
604
|
try: () => ee(`${i.keyPrefix ?? ""}${n}`),
|
|
604
605
|
catch: () => new e({
|
|
605
606
|
operation: "subscribe",
|
|
606
607
|
key: "",
|
|
607
608
|
cause: "NATS channels must be exact nonempty subjects"
|
|
608
609
|
})
|
|
609
|
-
}) :
|
|
610
|
+
}) : T.void;
|
|
610
611
|
return {
|
|
611
|
-
publish: (n, i) =>
|
|
612
|
+
publish: (n, i) => T.suspend(() => c || W.stopped ? T.fail(new e({
|
|
612
613
|
operation: "publish",
|
|
613
614
|
key: "",
|
|
614
615
|
cause: "Pub/Sub consumer is closed"
|
|
615
|
-
})) : p(n).pipe(
|
|
616
|
-
try: () =>
|
|
616
|
+
})) : p(n).pipe(T.andThen(T.tryPromise({
|
|
617
|
+
try: () => Le(t, r, "publish", (e) => e.kind === "nats" ? e.transport.publish(`${e.config.keyPrefix ?? ""}${n}`, i) : e.kind === "redis" ? e.client.eval(ce, 0, n, i) : Promise.reject(/* @__PURE__ */ Error("Storage has no Pub/Sub"))),
|
|
617
618
|
catch: () => new e({
|
|
618
619
|
operation: "publish",
|
|
619
620
|
key: "",
|
|
620
621
|
cause: "Pub/Sub publication failed"
|
|
621
622
|
})
|
|
622
|
-
}))).pipe(
|
|
623
|
-
subscribe: (n, i) => p(n).pipe(
|
|
624
|
-
if (c ||
|
|
623
|
+
}))).pipe(T.asVoid)),
|
|
624
|
+
subscribe: (n, i) => p(n).pipe(T.andThen(T.uninterruptibleMask(() => T.acquireUseRelease(Z(t, r), () => O.modifyEffect(N, (t) => T.gen(function* () {
|
|
625
|
+
if (c || W.stopped) return yield* T.fail(new e({
|
|
625
626
|
operation: "subscribe",
|
|
626
627
|
key: "",
|
|
627
628
|
cause: "Pub/Sub consumer is closed"
|
|
628
629
|
}));
|
|
629
630
|
let r = t.get(a);
|
|
630
|
-
if (r === void 0) return yield*
|
|
631
|
+
if (r === void 0) return yield* T.fail(new e({
|
|
631
632
|
operation: "subscribe",
|
|
632
633
|
key: "",
|
|
633
634
|
cause: "Connection generation is unavailable"
|
|
@@ -639,37 +640,37 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
639
640
|
},
|
|
640
641
|
released: !1
|
|
641
642
|
}, u = s.channels.get(n) ?? /* @__PURE__ */ new Set(), f = u.size === 0;
|
|
642
|
-
u.add(o.handler), s.channels.set(n, u), r.binding = s,
|
|
643
|
+
u.add(o.handler), s.channels.set(n, u), r.binding = s, Pe(r);
|
|
643
644
|
let p = r.subscriber !== void 0;
|
|
644
|
-
return yield*
|
|
645
|
-
if (yield* Ne(r, s), p && f && (yield*
|
|
645
|
+
return yield* T.gen(function* () {
|
|
646
|
+
if (yield* Ne(r, s), p && f && (yield* T.tryPromise({
|
|
646
647
|
try: () => r.subscriber.subscribe(n),
|
|
647
648
|
catch: () => new e({
|
|
648
649
|
operation: "subscribe",
|
|
649
650
|
key: "",
|
|
650
651
|
cause: "Redis subscription could not be registered"
|
|
651
652
|
})
|
|
652
|
-
})), c ||
|
|
653
|
+
})), c || W.stopped) return yield* T.fail(new e({
|
|
653
654
|
operation: "subscribe",
|
|
654
655
|
key: "",
|
|
655
656
|
cause: "Pub/Sub consumer is closed"
|
|
656
657
|
}));
|
|
657
|
-
}).pipe(
|
|
658
|
-
if (
|
|
658
|
+
}).pipe(T.onError(() => T.gen(function* () {
|
|
659
|
+
if (J(r), u.delete(o.handler), u.size === 0) {
|
|
659
660
|
s.channels.delete(n);
|
|
660
661
|
let e = r.subscriber;
|
|
661
|
-
e !== void 0 && !r.closed && (yield*
|
|
662
|
-
|
|
662
|
+
e !== void 0 && !r.closed && (yield* T.promise(() => e.unsubscribe(n).catch(() => {
|
|
663
|
+
J(r);
|
|
663
664
|
})));
|
|
664
665
|
}
|
|
665
|
-
}))), l.add(o), [yield*
|
|
666
|
-
})),
|
|
666
|
+
}))), l.add(o), [yield* T.cached(d(o)), t];
|
|
667
|
+
})), q)).pipe(ke, T.mapError(() => new e({
|
|
667
668
|
operation: "subscribe",
|
|
668
669
|
key: "",
|
|
669
670
|
cause: "Pub/Sub subscription could not be registered"
|
|
670
671
|
}))))),
|
|
671
672
|
onTransportEvent: (e) => {
|
|
672
|
-
if (c ||
|
|
673
|
+
if (c || W.stopped) throw Error("Pub/Sub consumer is closed");
|
|
673
674
|
let t = (t) => {
|
|
674
675
|
c || e(t);
|
|
675
676
|
};
|
|
@@ -680,34 +681,34 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
680
681
|
close: f
|
|
681
682
|
};
|
|
682
683
|
}),
|
|
683
|
-
probe:
|
|
684
|
-
reload:
|
|
685
|
-
if (
|
|
684
|
+
probe: Re,
|
|
685
|
+
reload: T.uninterruptibleMask((t) => T.gen(function* () {
|
|
686
|
+
if (W.stopped) return yield* T.fail(new e({
|
|
686
687
|
operation: "reload",
|
|
687
688
|
key: "",
|
|
688
689
|
cause: "Connection registry is closed"
|
|
689
690
|
}));
|
|
690
|
-
let n = yield*
|
|
691
|
-
let n = /* @__PURE__ */ new Map(), r = yield* t(
|
|
691
|
+
let n = yield* x.currentTimeMillis, r = yield* x.currentTimeNanos, i = yield* t(O.updateEffect(N, (e) => T.uninterruptibleMask((t) => T.gen(function* () {
|
|
692
|
+
let n = /* @__PURE__ */ new Map(), r = yield* t(T.forEach([...e], ([e, t]) => Ie(t.reference, t.context, (t) => {
|
|
692
693
|
n.set(e, t);
|
|
693
|
-
}, t))).pipe(
|
|
694
|
-
if (r._tag === "Failure") return yield*
|
|
695
|
-
for (let e of n.values())
|
|
696
|
-
return yield*
|
|
697
|
-
})))).pipe(
|
|
694
|
+
}, t))).pipe(T.exit);
|
|
695
|
+
if (r._tag === "Failure") return yield* T.forEach([...n.values()], G, { discard: !0 }), yield* T.failCause(r.cause);
|
|
696
|
+
for (let e of n.values()) Pe(e);
|
|
697
|
+
return yield* T.forEach([...e.values()], Ae, { discard: !0 }), n;
|
|
698
|
+
})))).pipe(T.exit), a = yield* x.currentTimeNanos, o = Object.freeze({
|
|
698
699
|
sequence: ++ue,
|
|
699
700
|
startedAt: n,
|
|
700
701
|
durationMs: Number(a - r) / 1e6,
|
|
701
702
|
outcome: i._tag === "Success" ? "succeeded" : "failed"
|
|
702
703
|
});
|
|
703
|
-
if (yield*
|
|
704
|
-
|
|
704
|
+
if (yield* T.sync(() => {
|
|
705
|
+
k.rotated(o.outcome, o.durationMs);
|
|
705
706
|
try {
|
|
706
707
|
v.onRotation?.(o);
|
|
707
708
|
} catch {}
|
|
708
|
-
}), i._tag === "Failure") return yield*
|
|
709
|
+
}), i._tag === "Failure") return yield* T.failCause(i.cause);
|
|
709
710
|
})),
|
|
710
|
-
inspect:
|
|
711
|
+
inspect: O.get(N).pipe(T.flatMap((e) => T.forEach([...e.values()], (e) => T.gen(function* () {
|
|
711
712
|
let t = e.config;
|
|
712
713
|
return {
|
|
713
714
|
reference: e.reference,
|
|
@@ -719,20 +720,19 @@ var O = class extends C.Service()("@voltro/ConnectionRegistry") {}, k = (v = {})
|
|
|
719
720
|
errors: e.errors,
|
|
720
721
|
latencyMs: e.latencyMs,
|
|
721
722
|
state: yield* e.breaker.state,
|
|
722
|
-
source: t.source ?? (
|
|
723
|
+
source: t.source ?? (w.redis && Object.keys(w.redis).length > 0 ? "config" : "environment"),
|
|
723
724
|
...t.label === void 0 ? {} : { label: t.label },
|
|
724
725
|
...t.region === void 0 ? {} : { region: t.region },
|
|
725
726
|
..."persistence" in t && t.persistence !== void 0 ? { persistence: t.persistence } : {}
|
|
726
727
|
};
|
|
727
728
|
})))),
|
|
728
729
|
redis: {
|
|
729
|
-
client: (e) =>
|
|
730
|
-
config: (e) =>
|
|
730
|
+
client: (e) => ze(e),
|
|
731
|
+
config: (e) => U(e)
|
|
731
732
|
}
|
|
732
733
|
};
|
|
733
|
-
}),
|
|
734
|
-
let t = yield*
|
|
735
|
-
return
|
|
734
|
+
}), j = (e = {}) => E.effectContext(T.gen(function* () {
|
|
735
|
+
let t = yield* C.ConfigProvider, n = v(yield* A(e), t);
|
|
736
|
+
return w.make(k, n).pipe(w.add(ue, n.redis));
|
|
736
737
|
}));
|
|
737
|
-
|
|
738
|
-
export { O as ConnectionRegistry, A as layerConfiguredConnections, k as makeConnectionRegistry };
|
|
738
|
+
export { k as ConnectionRegistry, j as layerConfiguredConnections, A as makeConnectionRegistry };
|