@voltro/kv 0.86.0 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +5321 -0
  2. package/README.md +110 -0
  3. package/THIRD-PARTY-NOTICES.md +864 -1
  4. package/dist/analytics.d.ts +337 -0
  5. package/dist/analytics.js +16 -0
  6. package/dist/cacheAtomicProtocol.js +247 -0
  7. package/dist/cacheAtomicScript.js +309 -0
  8. package/dist/cacheAtomicState.js +141 -0
  9. package/dist/connection.d.ts +990 -9
  10. package/dist/connection.js +330 -50
  11. package/dist/connectionAnalyticsConfig.js +153 -0
  12. package/dist/connectionConfigProvider.js +111 -0
  13. package/dist/connectionDefinitions.js +285 -0
  14. package/dist/connectionMemory.js +207 -0
  15. package/dist/connectionMetrics.js +79 -0
  16. package/dist/connectionNamespace.js +19 -0
  17. package/dist/connectionNats.js +148 -0
  18. package/dist/connectionNatsConfig.js +87 -0
  19. package/dist/connectionNatsSubjects.js +16 -0
  20. package/dist/connectionOwner.js +44 -0
  21. package/dist/connectionPool.js +738 -0
  22. package/dist/connectionPrefix.js +37 -0
  23. package/dist/connectionScripts.js +4 -0
  24. package/dist/connectionSqlConfig.js +119 -0
  25. package/dist/connectionStorageConfig.js +190 -0
  26. package/dist/connectionStorageFacade.js +78 -0
  27. package/dist/connectionSubscriber.js +130 -0
  28. package/dist/errors.js +5 -1
  29. package/dist/hot-contract.d.ts +297 -0
  30. package/dist/hot-contract.js +18 -0
  31. package/dist/hot-testing.d.ts +229 -0
  32. package/dist/hot-testing.js +1 -0
  33. package/dist/hot.d.ts +936 -0
  34. package/dist/hot.js +7 -0
  35. package/dist/hotMemory.js +46 -0
  36. package/dist/hotOperations.js +223 -0
  37. package/dist/hotRateLimit.js +103 -0
  38. package/dist/hotReadAvailability.js +19 -0
  39. package/dist/hotRedis.js +139 -0
  40. package/dist/hotRedisScript.js +4 -0
  41. package/dist/hotRouting.js +78 -0
  42. package/dist/hotState.js +182 -0
  43. package/dist/hotStoreConformance.js +367 -0
  44. package/dist/index.d.ts +113 -9
  45. package/dist/nativeRedisReads.js +78 -0
  46. package/dist/nativeRedisTopology.js +221 -0
  47. package/dist/redisDiagnostics.js +27 -0
  48. package/dist/redisReadPolicy.js +39 -0
  49. package/dist/sql.d.ts +77 -0
  50. package/dist/sql.js +0 -0
  51. package/dist/storage.d.ts +166 -0
  52. package/dist/storage.js +48 -0
  53. package/dist/storageAddress.js +20 -0
  54. package/package.json +43 -3
@@ -0,0 +1,37 @@
1
+ //#region src/connectionPrefix.ts
2
+ var e = (e) => e.replace(/[\\*?\[\]]/g, "\\$&"), t = (t, n) => {
3
+ if (n === void 0 || n === "") return t;
4
+ let r = (e) => n + e, i = (e, t) => {
5
+ if (!Number.isSafeInteger(e) || e < 0 || e > t.length || t.slice(0, e).some((e) => typeof e != "string")) throw Error("Namespaced EVAL requires a valid key count and string keys");
6
+ return t.map((t, n) => n < e ? r(t) : t);
7
+ };
8
+ return {
9
+ ...t.readOnly === void 0 ? {} : { readOnly: {
10
+ get: (e) => t.readOnly.get(r(e)),
11
+ exists: (e) => t.readOnly.exists(r(e)),
12
+ zrange: (e) => t.readOnly.zrange(r(e)),
13
+ eval: async (e, n, ...r) => t.readOnly.eval(e, n, ...i(n, r))
14
+ } },
15
+ ...t.crossNode === void 0 ? {} : { crossNode: t.crossNode },
16
+ ...t.ping === void 0 ? {} : { ping: () => t.ping() },
17
+ ...t.diagnostics === void 0 ? {} : { diagnostics: () => t.diagnostics() },
18
+ ...t.onReconnect === void 0 ? {} : { onReconnect: (e) => t.onReconnect(e) },
19
+ close: () => t.close(),
20
+ get: (e) => t.get(r(e)),
21
+ set: (e, n) => t.set(r(e), n),
22
+ psetex: (e, n, i) => t.psetex(r(e), n, i),
23
+ del: (e) => t.del(e.map(r)),
24
+ exists: (e) => t.exists(r(e)),
25
+ zrange: (e) => t.zrange(r(e)),
26
+ eval: async (e, n, ...r) => t.eval(e, n, ...i(n, r)),
27
+ scan: async (r, i, a) => {
28
+ let o = await t.scan(r, e(n) + i, a);
29
+ return {
30
+ cursor: o.cursor,
31
+ keys: o.keys.filter((e) => e.startsWith(n)).map((e) => e.slice(n.length))
32
+ };
33
+ }
34
+ };
35
+ };
36
+ //#endregion
37
+ export { t as withRespKeyPrefix };
@@ -0,0 +1,4 @@
1
+ //#region src/connectionScripts.ts
2
+ var e = "return redis.call('PUBLISH', ARGV[1], ARGV[2])";
3
+ //#endregion
4
+ export { e as CONNECTION_PUBLISH_SCRIPT };
@@ -0,0 +1,119 @@
1
+ import { connectionDefinitionsProblem as e } from "./connectionDefinitions.js";
2
+ import { Config as t, ConfigProvider as n, Effect as r, Option as i } from "effect";
3
+ //#region src/connectionSqlConfig.ts
4
+ var a = (e, t = {}) => {
5
+ let n = t.ttlMs ?? 6e4;
6
+ if (typeof e != "function" || !Number.isSafeInteger(n) || n <= 0) throw Error("Dynamic SQL requires a resolver and positive ttlMs");
7
+ return Object.freeze({
8
+ _tag: "DynamicSqlConnection",
9
+ resolve: e,
10
+ ttlMs: n
11
+ });
12
+ }, o = (e) => typeof e == "object" && !!e && !Array.isArray(e), s = (e) => o(e) && e._tag === "ConnectionEnv" && typeof e.name == "string" && /^[A-Z][A-Z0-9_]*$/.test(e.name), c = [
13
+ "dialect",
14
+ "url",
15
+ "tls",
16
+ "maxConnections",
17
+ "acquireTimeoutMs",
18
+ "acquireQueueLimit",
19
+ "statementTimeoutMs",
20
+ "schema",
21
+ "label",
22
+ "region",
23
+ "source"
24
+ ], l = (e) => {
25
+ if (Object.keys(e).some((e) => !c.includes(e))) return "SQL connection options contain an unknown field";
26
+ if (![
27
+ "postgres",
28
+ "mysql",
29
+ "mariadb",
30
+ "mssql",
31
+ "sqlite",
32
+ "turso"
33
+ ].includes(String(e.dialect))) return "SQL connections require an implemented dialect";
34
+ if (e.tls !== void 0 && !["auto", "required"].includes(String(e.tls))) return "SQL TLS must be auto or required";
35
+ for (let t of [
36
+ "maxConnections",
37
+ "acquireTimeoutMs",
38
+ "acquireQueueLimit",
39
+ "statementTimeoutMs"
40
+ ]) {
41
+ let n = e[t];
42
+ if (n !== void 0 && (typeof n != "number" || !Number.isSafeInteger(n) || n < +(t === "maxConnections"))) return "SQL pool and timeout options must be safe integers in their allowed range";
43
+ }
44
+ if (["sqlite", "turso"].includes(String(e.dialect)) && [
45
+ "maxConnections",
46
+ "acquireTimeoutMs",
47
+ "acquireQueueLimit",
48
+ "statementTimeoutMs"
49
+ ].some((t) => e[t] !== void 0)) return "Embedded SQL does not implement network pool or timeout options";
50
+ if (e.statementTimeoutMs !== void 0 && e.dialect !== "postgres") return "SQL statement timeout is implemented only for PostgreSQL";
51
+ if (e.acquireQueueLimit !== void 0 && !["mysql", "mariadb"].includes(String(e.dialect))) return "SQL acquire queue limit is implemented only for MySQL and MariaDB";
52
+ if (e.schema !== void 0 && (e.dialect !== "postgres" || typeof e.schema != "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.schema))) return "SQL schema requires a PostgreSQL identifier";
53
+ for (let t of ["label", "region"]) if (e[t] !== void 0 && typeof e[t] != "string") return "SQL metadata must be strings";
54
+ if (e.source !== void 0 && !["config", "override"].includes(String(e.source))) return "SQL source metadata is invalid";
55
+ }, u = (e) => o(e) ? "_tag" in e ? e._tag === "DynamicSqlConnection" && typeof e.resolve == "function" && typeof e.ttlMs == "number" && Number.isSafeInteger(e.ttlMs) && e.ttlMs > 0 ? void 0 : "Dynamic SQL requires a resolver and positive ttlMs" : l(e) || (e.source === void 0 ? s(e.url) ? void 0 : "SQL endpoints and credentials must use env() declarations" : "SQL source is resolved metadata, not a declaration") : "SQL declarations must be named objects", d = (e) => {
56
+ if (!o(e)) return "SQL configuration must be a named object";
57
+ let t = l(e);
58
+ if (t) return t;
59
+ if (typeof e.url != "string" || !e.url.trim()) return "SQL requires a resolved nonempty URL";
60
+ let n = e.dialect;
61
+ if (["sqlite", "turso"].includes(String(n)) && e.url === ":memory:") return e.tls === "required" ? "SQL TLS requires a network transport with encryption" : void 0;
62
+ try {
63
+ let t = new URL(e.url);
64
+ if (!(n === "postgres" ? ["postgres:", "postgresql:"] : n === "mysql" || n === "mariadb" ? ["mysql:", "mariadb:"] : n === "mssql" ? ["mssql:", "sqlserver:"] : ["file:"]).includes(t.protocol) || t.hash) return "SQL URL must match the declared dialect and have no fragment";
65
+ if (["sqlite", "turso"].includes(String(n))) {
66
+ if (t.protocol !== "file:" || t.hostname || t.search || t.username || t.password || !t.pathname || e.url === "file:") return "Embedded SQL registry connections require a local file path without URL authority or query options";
67
+ } else {
68
+ if (!t.hostname || !t.pathname.replace(/^\//, "")) return "Network SQL requires an explicit host and database";
69
+ for (let [r, i] of t.searchParams) {
70
+ if (![
71
+ "sslmode",
72
+ "ssl",
73
+ ...n === "mssql" ? ["encrypt"] : []
74
+ ].includes(r)) return "SQL URL contains an unimplemented query option";
75
+ if (!(r === "sslmode" ? ["require", "disable"] : [
76
+ "true",
77
+ "false",
78
+ "1",
79
+ "0"
80
+ ]).includes(i)) return "SQL URL requests an unimplemented TLS mode";
81
+ if (e.tls === "required" && [
82
+ "disable",
83
+ "false",
84
+ "0"
85
+ ].includes(i)) return "SQL TLS configuration conflicts with the URL";
86
+ if (t.searchParams.getAll(r).length !== 1) return "SQL URL contains duplicate query options";
87
+ }
88
+ let r = [...t.searchParams].map(([e, t]) => e === "sslmode" ? t === "require" : ["true", "1"].includes(t));
89
+ if (new Set(r).size > 1) return "SQL URL contains conflicting TLS options";
90
+ decodeURIComponent(t.username), decodeURIComponent(t.password);
91
+ }
92
+ if (e.tls === "required" && ["sqlite", "turso"].includes(String(n)) && !["libsql:", "https:"].includes(t.protocol)) return "SQL TLS requires a network transport with encryption";
93
+ } catch {
94
+ return "SQL requires a valid dialect URL";
95
+ }
96
+ }, f = (e, t) => {
97
+ let n = e.replace(/^sql:/, ""), r = Object.keys(t.sql ?? {});
98
+ return n || t.defaults?.sql || (r.length === 1 ? r[0] : "");
99
+ }, p = (a, o, c) => r.gen(function* () {
100
+ let l = e(o);
101
+ if (l) return yield* r.fail(new t.ConfigError(new n.SourceError({ message: l })));
102
+ if (a !== "sql:" && !/^sql:[A-Za-z][A-Za-z0-9_-]*$/.test(a)) return yield* r.fail(new t.ConfigError(new n.SourceError({ message: "SQL requires a sql:name reference" })));
103
+ let u = f(a, o), p = o.sql ?? {}, m = Object.hasOwn(p, u) ? p[u] : void 0;
104
+ if (m === void 0) return yield* r.fail(new t.ConfigError(new n.SourceError({ message: `Unknown SQL connection ${u || "(default)"}. Known names: ${Object.keys(p).sort().join(", ")}` })));
105
+ let h;
106
+ if ("_tag" in m) {
107
+ if (typeof c?.tenantId != "string" || !c.tenantId) return yield* r.fail(new t.ConfigError(new n.SourceError({ message: "Dynamic SQL requires tenantId" })));
108
+ h = { ...yield* m.resolve(Object.freeze({ tenantId: c.tenantId })) };
109
+ } else h = { ...m };
110
+ let g = yield* t.String(`CONNECTIONS_SQL_${u.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_URL`).pipe(t.option);
111
+ i.isSome(g) ? h.url = g.value : s(h.url) && (h.url = yield* t.String(h.url.name));
112
+ let _ = d(h);
113
+ return _ ? yield* r.fail(new t.ConfigError(new n.SourceError({ message: _ }))) : {
114
+ ...h,
115
+ source: i.isSome(g) ? "override" : "config"
116
+ };
117
+ });
118
+ //#endregion
119
+ export { a as dynamicSql, d as sqlConfigProblem, p as sqlConnectionConfig, f as sqlConnectionName, u as sqlDefinitionProblem };
@@ -0,0 +1,190 @@
1
+ import { connectionDefinitionsProblem as e, connectionMetadataProblem as t } from "./connectionDefinitions.js";
2
+ import { Config as n, ConfigProvider as r, Effect as i, Option as a } from "effect";
3
+ //#region src/connectionStorageConfig.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 storage requires a resolver and positive ttlMs");
7
+ return Object.freeze({
8
+ _tag: "DynamicStorageConnection",
9
+ resolve: e,
10
+ ttlMs: n
11
+ });
12
+ }, s = (e, t) => {
13
+ let n = e.replace(/^storage:/, ""), r = Object.keys(t.storage ?? {});
14
+ return n || t.defaults?.storage || (r.length === 1 ? r[0] : "");
15
+ }, c = (e) => {
16
+ if (typeof e != "object" || !e || Array.isArray(e)) return "Storage configuration must be a named object";
17
+ let n = t(e);
18
+ if (n) return n;
19
+ if (Object.keys(e).some((e) => ![
20
+ "provider",
21
+ "bucket",
22
+ "root",
23
+ "endpoint",
24
+ "accessKeyId",
25
+ "secretAccessKey",
26
+ "accountName",
27
+ "accountKey",
28
+ "connectionString",
29
+ "cdnBaseUrl",
30
+ "forcePathStyle",
31
+ "publicAcl",
32
+ "tls",
33
+ "persistence",
34
+ "keyPrefix",
35
+ "allowSharedNamespace",
36
+ "region",
37
+ "label",
38
+ "acl",
39
+ "source"
40
+ ].includes(e))) return "Storage configuration contains an unknown field";
41
+ if (e.persistence !== void 0 && ![
42
+ "required",
43
+ "optional",
44
+ "none"
45
+ ].includes(e.persistence)) return "Storage persistence must be required, optional or none";
46
+ if (![
47
+ "s3",
48
+ "minio",
49
+ "azure",
50
+ "filesystem",
51
+ "memory",
52
+ "database"
53
+ ].includes(e.provider)) return "Storage provider must be s3, minio, azure, filesystem, memory or database";
54
+ for (let t of [
55
+ "bucket",
56
+ "root",
57
+ "endpoint",
58
+ "accessKeyId",
59
+ "secretAccessKey",
60
+ "accountName",
61
+ "accountKey",
62
+ "connectionString",
63
+ "cdnBaseUrl",
64
+ "region",
65
+ "label",
66
+ "keyPrefix"
67
+ ]) if (e[t] !== void 0 && (typeof e[t] != "string" || !e[t].trim())) return `Storage ${t} must be nonempty`;
68
+ if (e.accessKeyId === void 0 != (e.secretAccessKey === void 0)) return "Storage access key and secret must resolve together";
69
+ if (e.accountName === void 0 != (e.accountKey === void 0) && e.connectionString === void 0) return "Azure account and key must resolve together";
70
+ if ((e.provider === "s3" || e.provider === "minio" || e.provider === "azure") && !e.bucket) return "Object storage requires a bucket or container";
71
+ if (e.provider === "filesystem" && !e.root) return "Filesystem storage requires a root";
72
+ if (e.provider === "azure" && !e.connectionString && (!e.accountName || !e.accountKey)) return "Azure storage requires declared credentials";
73
+ for (let t of [
74
+ "forcePathStyle",
75
+ "publicAcl",
76
+ "allowSharedNamespace"
77
+ ]) if (e[t] !== void 0 && typeof e[t] != "boolean") return `Storage ${t} must be a boolean`;
78
+ if (e.keyPrefix !== void 0 && (!/^[A-Za-z0-9_:/.-]+$/.test(e.keyPrefix) || e.keyPrefix.startsWith("/") || e.keyPrefix.split("/").includes(".."))) return "Storage namespace must be an opaque relative prefix";
79
+ for (let t of ["endpoint", "cdnBaseUrl"]) {
80
+ let n = e[t];
81
+ if (n !== void 0) try {
82
+ let t = new URL(n);
83
+ if (!["https:", "http:"].includes(t.protocol) || t.username || t.password || t.search || t.hash) return "Storage endpoints require HTTP(S) without credentials or query";
84
+ if (e.tls === "required" && t.protocol !== "https:") return "Storage TLS policy requires HTTPS";
85
+ } catch {
86
+ return "Storage endpoints must be valid HTTP(S) URLs";
87
+ }
88
+ }
89
+ }, l = (t, o, l) => i.gen(function* () {
90
+ let u = e(o);
91
+ if (u) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: u })));
92
+ if (!/^storage:[A-Za-z][A-Za-z0-9_-]*$/.test(t) && t !== "storage:") return yield* i.fail(new n.ConfigError(new r.SourceError({ message: "Storage requires a storage:name reference" })));
93
+ let d = s(t, o), f = o.storage ?? {}, p = Object.hasOwn(f, d) ? f[d] : void 0;
94
+ if (p === void 0) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: `Unknown storage connection ${d || "(default)"}. Known names: ${Object.keys(f).sort().join(", ")}` })));
95
+ let m, h = !1, g = `CONNECTIONS_STORAGE_${d.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`, _ = (e, t) => i.gen(function* () {
96
+ let r = yield* n.String(`${g}_${e}`).pipe(n.option);
97
+ return a.isSome(r) ? (h = !0, r.value) : t === void 0 || typeof t == "string" ? t : yield* n.String(t.name);
98
+ });
99
+ if ("_tag" in p) {
100
+ if (!l?.tenantId) return yield* i.fail(new n.ConfigError(new r.SourceError({ message: "Dynamic storage requires tenantId" })));
101
+ m = { ...yield* p.resolve(Object.freeze({ tenantId: l.tenantId })) };
102
+ } else {
103
+ let { bucket: e, root: t, endpoint: n, accessKeyId: r, secretAccessKey: i, accountName: a, accountKey: o, connectionString: s, cdnBaseUrl: c, ...l } = p;
104
+ m = { ...l };
105
+ for (let [l, u, d] of [
106
+ [
107
+ "bucket",
108
+ "BUCKET",
109
+ e
110
+ ],
111
+ [
112
+ "root",
113
+ "ROOT",
114
+ t
115
+ ],
116
+ [
117
+ "endpoint",
118
+ "ENDPOINT",
119
+ n
120
+ ],
121
+ [
122
+ "accessKeyId",
123
+ "ACCESS_KEY_ID",
124
+ r
125
+ ],
126
+ [
127
+ "secretAccessKey",
128
+ "SECRET_ACCESS_KEY",
129
+ i
130
+ ],
131
+ [
132
+ "accountName",
133
+ "ACCOUNT_NAME",
134
+ a
135
+ ],
136
+ [
137
+ "accountKey",
138
+ "ACCOUNT_KEY",
139
+ o
140
+ ],
141
+ [
142
+ "connectionString",
143
+ "CONNECTION_STRING",
144
+ s
145
+ ],
146
+ [
147
+ "cdnBaseUrl",
148
+ "CDN_BASE_URL",
149
+ c
150
+ ]
151
+ ]) {
152
+ let e = yield* _(u, d);
153
+ e !== void 0 && (m = {
154
+ ...m,
155
+ [l]: e
156
+ });
157
+ }
158
+ }
159
+ if ("_tag" in p) for (let [e, t] of [
160
+ ["bucket", "BUCKET"],
161
+ ["root", "ROOT"],
162
+ ["endpoint", "ENDPOINT"],
163
+ ["accessKeyId", "ACCESS_KEY_ID"],
164
+ ["secretAccessKey", "SECRET_ACCESS_KEY"],
165
+ ["accountName", "ACCOUNT_NAME"],
166
+ ["accountKey", "ACCOUNT_KEY"],
167
+ ["connectionString", "CONNECTION_STRING"],
168
+ ["cdnBaseUrl", "CDN_BASE_URL"]
169
+ ]) {
170
+ let n = yield* _(t, m[e]);
171
+ n !== void 0 && (m = {
172
+ ...m,
173
+ [e]: n
174
+ });
175
+ }
176
+ for (let [e, t] of [["keyPrefix", "KEY_PREFIX"], ["region", "REGION"]]) {
177
+ let n = yield* _(t, m[e]);
178
+ n !== void 0 && (m = {
179
+ ...m,
180
+ [e]: n
181
+ });
182
+ }
183
+ let v = c(m);
184
+ return v ? yield* i.fail(new n.ConfigError(new r.SourceError({ message: v }))) : {
185
+ ...m,
186
+ source: h ? "override" : "config"
187
+ };
188
+ });
189
+ //#endregion
190
+ export { o as dynamicStorage, c as storageConfigProblem, l as storageConnectionConfig, s as storageConnectionName };
@@ -0,0 +1,78 @@
1
+ import { StorageError as e } from "./storage.js";
2
+ import { Effect as t, Stream as n } from "effect";
3
+ //#region src/connectionStorageFacade.ts
4
+ var r = (r) => {
5
+ let i = (n) => t.fail(new e({
6
+ provider: r.config().provider,
7
+ message: `Storage provider does not offer ${n}`,
8
+ transient: !1
9
+ })), { config: a, command: o } = r, s = {
10
+ objectNamespace: o("objectNamespace", (e) => e.objectNamespace),
11
+ objectLocation: (e) => o("objectLocation", (t) => t.objectLocation?.(e) ?? i("objectLocation")),
12
+ get name() {
13
+ return a().provider;
14
+ },
15
+ get presigns() {
16
+ return [
17
+ "s3",
18
+ "minio",
19
+ "azure"
20
+ ].includes(a().provider);
21
+ },
22
+ put: (...e) => o("put", (t) => t.put(...e)),
23
+ get: (...e) => o("get", (t) => t.get(...e)),
24
+ delete: (...e) => o("delete", (t) => t.delete(...e)),
25
+ getUrl: (...e) => o("getUrl", (t) => t.getUrl(...e)),
26
+ head: (...e) => o("head", (t) => t.head(...e)),
27
+ publicUrl: (e) => {
28
+ let t = a();
29
+ return t.cdnBaseUrl ? `${t.cdnBaseUrl.replace(/\/$/, "")}/${t.keyPrefix ?? ""}${e}` : null;
30
+ },
31
+ getStream: (n) => o("head", (e) => e.head(n)).pipe(t.flatMap((i) => i === null ? t.fail(new e({
32
+ provider: a().provider,
33
+ message: "Storage object is absent",
34
+ transient: !1,
35
+ status: 404
36
+ })) : t.succeed({
37
+ stream: r.stream(n, i),
38
+ contentType: i.contentType ?? "application/octet-stream",
39
+ size: i.size
40
+ }))),
41
+ putStream: (e, r, i) => o("putStream", (a) => a.putStream === void 0 ? n.runCollect(r).pipe(t.flatMap((t) => {
42
+ let n = new Uint8Array([...t].reduce((e, t) => e + t.byteLength, 0)), r = 0;
43
+ for (let e of t) n.set(e, r), r += e.byteLength;
44
+ return a.put(e, n, i);
45
+ })) : a.putStream(e, r, i)),
46
+ getRange: (e, n, r) => o("getRange", (i) => i.getRange === void 0 ? i.get(e).pipe(t.map((e) => e.bytes.slice(n, r + 1))) : i.getRange(e, n, r)),
47
+ bindDataStore: r.bind
48
+ };
49
+ return Object.defineProperties(s, Object.getOwnPropertyDescriptors({
50
+ get root() {
51
+ return a().root;
52
+ },
53
+ get setObjectPublic() {
54
+ return ["s3", "minio"].includes(a().provider) && a().publicAcl !== !1 ? (e, t) => o("setObjectPublic", (n) => n.setObjectPublic?.(e, t) ?? i("setObjectPublic")) : void 0;
55
+ },
56
+ get getUploadUrl() {
57
+ return [
58
+ "s3",
59
+ "minio",
60
+ "azure"
61
+ ].includes(a().provider) ? (e, t, n) => o("getUploadUrl", (r) => r.getUploadUrl?.(e, t, n) ?? i("getUploadUrl")) : void 0;
62
+ },
63
+ get createMultipartUpload() {
64
+ return ["s3", "minio"].includes(a().provider) ? (e, t, n) => o("createMultipartUpload", (r) => r.createMultipartUpload?.(e, t, n) ?? i("createMultipartUpload")) : void 0;
65
+ },
66
+ get presignUploadPart() {
67
+ return ["s3", "minio"].includes(a().provider) ? (e, t, n, r) => o("presignUploadPart", (a) => a.presignUploadPart?.(e, t, n, r) ?? i("presignUploadPart")) : void 0;
68
+ },
69
+ get completeMultipartUpload() {
70
+ return ["s3", "minio"].includes(a().provider) ? (e, t, n) => o("completeMultipartUpload", (r) => r.completeMultipartUpload?.(e, t, n) ?? i("completeMultipartUpload")) : void 0;
71
+ },
72
+ get abortMultipartUpload() {
73
+ return ["s3", "minio"].includes(a().provider) ? (e, t) => o("abortMultipartUpload", (n) => n.abortMultipartUpload?.(e, t) ?? i("abortMultipartUpload")) : void 0;
74
+ }
75
+ }));
76
+ };
77
+ //#endregion
78
+ export { r as makeStorageFacade };
@@ -0,0 +1,130 @@
1
+ import { KvError as e } from "./errors.js";
2
+ import { createNativeRedisConnection as t, disconnectNativeRedis as n, nativeRedisOptions as r, nativeRedisTopology as i } from "./nativeRedisTopology.js";
3
+ import { Effect as a } from "effect";
4
+ //#region src/connectionSubscriber.ts
5
+ var o = async (e, a) => {
6
+ a.throwIfAborted();
7
+ let o = await t(e, "subscriber"), s, c = () => Promise.all([n(o), s === void 0 ? Promise.resolve() : n({
8
+ kind: "standalone",
9
+ client: s
10
+ })]).then(() => void 0), l = () => {
11
+ c();
12
+ };
13
+ a.addEventListener("abort", l, { once: !0 }), o.client.on("error", () => {});
14
+ try {
15
+ if (a.throwIfAborted(), await o.client.connect(), await o.ready(), a.throwIfAborted(), o.kind !== "cluster") return {
16
+ client: o.client,
17
+ dispose: c
18
+ };
19
+ let t = o.client.nodes("master").find((e) => e.status === "ready");
20
+ if (t === void 0) throw Error("No ready Cluster primary");
21
+ let l = t.options.host, u = t.options.port;
22
+ if (l === void 0 || u === void 0) throw Error("Cluster primary endpoint missing");
23
+ let d = i(e);
24
+ if (d.kind !== "cluster") throw Error("Expected Cluster topology");
25
+ let { default: f } = await import("ioredis");
26
+ return a.throwIfAborted(), s = new f({
27
+ ...d.options,
28
+ ...r("subscriber"),
29
+ host: l,
30
+ port: u
31
+ }), s.on("error", () => {}), await s.connect(), a.throwIfAborted(), await n(o), a.throwIfAborted(), {
32
+ client: s,
33
+ dispose: c
34
+ };
35
+ } catch (e) {
36
+ throw await c(), e;
37
+ } finally {
38
+ a.removeEventListener("abort", l);
39
+ }
40
+ }, s = (t) => a.tryPromise({
41
+ try: async (e) => {
42
+ if (t.driver !== "resp") throw Error("RESP is required for subscriptions");
43
+ let n = await o(t, e);
44
+ e.aborted && (await n.dispose(), e.throwIfAborted());
45
+ let r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(), s = n, c = !1, l, u, d, f = Promise.resolve(), p = Promise.resolve(), m = (e) => {
46
+ let t = p.then(e);
47
+ return p = t.then(() => void 0, () => void 0), t;
48
+ }, h = (e) => {
49
+ for (let t of i) try {
50
+ t(e);
51
+ } catch {}
52
+ }, g = (e) => {
53
+ let t = e.dispose();
54
+ return f = Promise.all([f, t]).then(() => void 0), t;
55
+ }, _ = (e) => {
56
+ e.client.on("message", (t, n) => {
57
+ if (!(c || s !== e)) for (let e of r) try {
58
+ e(t, n);
59
+ } catch {}
60
+ });
61
+ let t = () => {
62
+ c || s !== e || (s = void 0, h({ kind: "disconnected" }), g(e), y());
63
+ };
64
+ e.client.on("close", t), e.client.on("reconnecting", t);
65
+ }, v = async () => {
66
+ let e = new AbortController();
67
+ u = e;
68
+ let n, r = () => {
69
+ n !== void 0 && g(n);
70
+ };
71
+ e.signal.addEventListener("abort", r, { once: !0 });
72
+ try {
73
+ n = await o(t, e.signal);
74
+ let r = n;
75
+ await m(async () => {
76
+ if (e.signal.throwIfAborted(), c) throw Error("Subscriber closed");
77
+ for (let e of a) await r.client.subscribe(e);
78
+ if (e.signal.throwIfAborted(), r.client.status !== "ready") throw Error("Subscriber lost during restoration");
79
+ _(r), s = r;
80
+ }), n = void 0, h({ kind: "reconnected" });
81
+ } catch {
82
+ n !== void 0 && await g(n);
83
+ } finally {
84
+ e.signal.removeEventListener("abort", r), u = void 0;
85
+ }
86
+ }, y = () => {
87
+ c || l !== void 0 || d !== void 0 || (l = setTimeout(() => {
88
+ l = void 0, d = v().finally(() => {
89
+ d = void 0, !c && s === void 0 && y();
90
+ });
91
+ }, 100));
92
+ };
93
+ if (_(n), n.client.status !== "ready") throw s = void 0, await g(n), Error("Subscriber lost before admission");
94
+ let b = () => {
95
+ if (c || s === void 0 || s.client.status !== "ready") throw Error("Subscriber unavailable");
96
+ return s;
97
+ };
98
+ return {
99
+ subscribe: (e) => m(async () => {
100
+ let t = b();
101
+ if (await t.client.subscribe(e), c || s !== t) throw Error("Subscriber lost during registration");
102
+ a.add(e);
103
+ }),
104
+ unsubscribe: (e) => m(async () => {
105
+ a.delete(e), !c && s !== void 0 && await s.client.unsubscribe(e);
106
+ }),
107
+ onMessage: (e) => (r.add(e), () => {
108
+ r.delete(e);
109
+ }),
110
+ onTransportEvent: (e) => (i.add(e), () => {
111
+ i.delete(e);
112
+ }),
113
+ close: async () => {
114
+ if (!c) {
115
+ c = !0, l !== void 0 && clearTimeout(l), u?.abort();
116
+ let e = s;
117
+ s = void 0, e !== void 0 && g(e), a.clear(), r.clear(), i.clear();
118
+ }
119
+ await d, await p, await f;
120
+ }
121
+ };
122
+ },
123
+ catch: () => new e({
124
+ operation: "subscribe",
125
+ key: "",
126
+ cause: "Redis subscriber could not be opened"
127
+ })
128
+ });
129
+ //#endregion
130
+ export { s as connectRedisSubscriber };
package/dist/errors.js CHANGED
@@ -4,6 +4,10 @@ var t = class extends e.TaggedError()("KvError", {
4
4
  operation: e.String,
5
5
  key: e.String,
6
6
  cause: e.String
7
- }) {};
7
+ }) {
8
+ get message() {
9
+ return this.cause;
10
+ }
11
+ };
8
12
  //#endregion
9
13
  export { t as KvError };