@supalive/core 1.9.0 → 1.11.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/dist/db-ClCicssQ.js.map +1 -1
- package/dist/index-BQRlJY_1.d.ts +2192 -0
- package/dist/index-BQRlJY_1.d.ts.map +1 -0
- package/dist/index-C_Zew1wH.d.ts +2209 -0
- package/dist/index-C_Zew1wH.d.ts.map +1 -0
- package/dist/index-D8O39FoZ.d.ts +2192 -0
- package/dist/index-D8O39FoZ.d.ts.map +1 -0
- package/dist/index-WMhtVzJA.d.ts +2192 -0
- package/dist/index-WMhtVzJA.d.ts.map +1 -0
- package/dist/mysql-BXy4mk-9.d.ts +113 -0
- package/dist/mysql-BXy4mk-9.d.ts.map +1 -0
- package/dist/mysql-CBSuONm5.d.ts +109 -0
- package/dist/mysql-CBSuONm5.d.ts.map +1 -0
- package/dist/mysql-CDenA4XE.d.ts +109 -0
- package/dist/mysql-CDenA4XE.d.ts.map +1 -0
- package/dist/mysql-D-PGYc1L.js +609 -0
- package/dist/mysql-D-PGYc1L.js.map +1 -0
- package/dist/mysql-DAfFK5v1.d.ts +109 -0
- package/dist/mysql-DAfFK5v1.d.ts.map +1 -0
- package/dist/mysql-txOOzccX.js +609 -0
- package/dist/mysql-txOOzccX.js.map +1 -0
- package/dist/overlap-checker-BoWNco2y.js +258 -0
- package/dist/overlap-checker-BoWNco2y.js.map +1 -0
- package/dist/overlap-checker-DvRqQOTe.js +265 -0
- package/dist/overlap-checker-DvRqQOTe.js.map +1 -0
- package/dist/postgres-BKGLPrKt.d.ts +113 -0
- package/dist/postgres-BKGLPrKt.d.ts.map +1 -0
- package/dist/postgres-CKBWXyY8.d.ts +113 -0
- package/dist/postgres-CKBWXyY8.d.ts.map +1 -0
- package/dist/postgres-CapFFTTn.d.ts +117 -0
- package/dist/postgres-CapFFTTn.d.ts.map +1 -0
- package/dist/postgres-Cn9vTBX7.js +864 -0
- package/dist/postgres-Cn9vTBX7.js.map +1 -0
- package/dist/postgres-DQxKs5Nu.d.ts +113 -0
- package/dist/postgres-DQxKs5Nu.d.ts.map +1 -0
- package/dist/postgres-tLkdxhFu.js +864 -0
- package/dist/postgres-tLkdxhFu.js.map +1 -0
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/exports/mysql.d.ts +1 -1
- package/dist/src/exports/mysql.js +1 -1
- package/dist/src/exports/postgres.d.ts +1 -1
- package/dist/src/exports/postgres.js +1 -1
- package/dist/src/exports/procedure.d.ts +1 -1
- package/dist/src/exports/schema-sql.d.ts +1 -1
- package/dist/src/exports/server.d.ts +24 -124
- package/dist/src/exports/server.d.ts.map +1 -1
- package/dist/src/exports/server.js +137 -32
- package/dist/src/exports/server.js.map +1 -1
- package/dist/src/exports/sub-manager-worker-entry.d.ts +1 -0
- package/dist/src/exports/sub-manager-worker-entry.js +71 -0
- package/dist/src/exports/sub-manager-worker-entry.js.map +1 -0
- package/dist/src/exports/subscription-manager-worker-entry.js +3 -49
- package/dist/src/exports/subscription-manager-worker-entry.js.map +1 -1
- package/dist/src/exports/types.d.ts +2 -2
- package/dist/sub-worker-dispatch-Cokz99D1.js +810 -0
- package/dist/sub-worker-dispatch-Cokz99D1.js.map +1 -0
- package/dist/sub-worker-dispatch-DohrszOw.js +810 -0
- package/dist/sub-worker-dispatch-DohrszOw.js.map +1 -0
- package/dist/sub-worker-dispatch-G8V8asfR.js +810 -0
- package/dist/sub-worker-dispatch-G8V8asfR.js.map +1 -0
- package/dist/subscription-worker-dispatch-CSQV-PO3.js +810 -0
- package/dist/subscription-worker-dispatch-CSQV-PO3.js.map +1 -0
- package/dist/types_server-AQ8Jl2_R.d.ts +521 -0
- package/dist/types_server-AQ8Jl2_R.d.ts.map +1 -0
- package/dist/types_server-BB2WvXEA.d.ts +521 -0
- package/dist/types_server-BB2WvXEA.d.ts.map +1 -0
- package/dist/types_server-DU9JMSXd.d.ts +521 -0
- package/dist/types_server-DU9JMSXd.d.ts.map +1 -0
- package/dist/types_server-SPElhjga.d.ts +521 -0
- package/dist/types_server-SPElhjga.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { n as getContextRegistry } from "../../router-CiWYeWUR.js";
|
|
2
2
|
import { n as stableStringify, r as supaliveStringify, t as groupByToMap } from "../../helper-wGbzz6zo.js";
|
|
3
|
-
import { r as evaluateCacheFreshness } from "../../overlap-checker-
|
|
3
|
+
import { r as evaluateCacheFreshness } from "../../overlap-checker-BoWNco2y.js";
|
|
4
4
|
import { i as AuthenticationError, o as ClientMessageSchema } from "../../types_client_rpc-CIsjQQGG.js";
|
|
5
5
|
import { n as RemoteSubscriptionMessageSchema } from "../../types_server-Dk6o_B7N.js";
|
|
6
6
|
import { n as sleep } from "../../realtime_db-DiYQbfeA.js";
|
|
7
|
-
import { C as
|
|
7
|
+
import { C as appInstanceId, D as serializeMapValue, E as logger, S as RetentionWatermark, T as logLevelOf, _ as UnregisterSubscriptionResultSchema, a as bootstrapDbTypes, b as UpdateSubscriptionReadSetResultSchema, c as initCore, d as InvalidateWritesetParamsSchema, f as InvalidateWritesetResultSchema, g as UnregisterSubscriptionParamsSchema, h as RegisterSubscriptionResultSchema, i as DB_QUERY_TIMEOUT_MS, l as initDatabase, m as RegisterSubscriptionParamsSchema, n as SubscriptionWorker, o as createDatabase, p as RegisterSubscriptionBatchResultSchema, r as CORE_VERSION, s as initCacheLayer, t as dispatchWorkerMessage, u as CacheLayer, v as UnregisterSubscriptionsResultSchema, w as isProduction, x as RETENTION_WATERMARK_CHANNEL, y as UpdateSubscriptionReadSetParamsSchema } from "../../sub-worker-dispatch-DohrszOw.js";
|
|
8
8
|
import { createServer } from "http";
|
|
9
9
|
import { WebSocket, WebSocketServer } from "ws";
|
|
10
10
|
import { v4 } from "uuid";
|
|
@@ -101,7 +101,14 @@ async function runCachedQuery(deps, procedure, procedureName, input, serverCtx,
|
|
|
101
101
|
const metadata = await deps.cache.getQueryCacheMetaData(cacheKey);
|
|
102
102
|
if ((await evaluateCacheFreshness(deps.db.impl, metadata, lastSnapshotTs, deps.getMinRetainedTs())).fresh) {
|
|
103
103
|
const cached = await deps.cache.getQueryCacheFor(cacheKey);
|
|
104
|
-
if (cached && metadata && cached.version === metadata.version)
|
|
104
|
+
if (cached && metadata && cached.version === metadata.version) {
|
|
105
|
+
if (lastSnapshotTs > metadata.lastSnapshotTs) deps.cache.advanceQueryCacheMetadata(cacheKey, {
|
|
106
|
+
lastSnapshotTs,
|
|
107
|
+
version: metadata.version,
|
|
108
|
+
readSet: metadata.readSet
|
|
109
|
+
}).catch((err) => logger.error(err, "one-shot horizon advance failed"));
|
|
110
|
+
return cached.data;
|
|
111
|
+
}
|
|
105
112
|
}
|
|
106
113
|
const result = await deps.db.queryInternal(async (ctx) => {
|
|
107
114
|
const fn = procedure.fn;
|
|
@@ -118,7 +125,7 @@ async function runCachedQuery(deps, procedure, procedureName, input, serverCtx,
|
|
|
118
125
|
return result.data;
|
|
119
126
|
}
|
|
120
127
|
//#endregion
|
|
121
|
-
//#region src/server/
|
|
128
|
+
//#region src/server/sub-manager-client.ts
|
|
122
129
|
const logger$5 = logger.child({ name: "SUBS_MGR_CLIENT" }, { level: logLevelOf("SUB_MANAGER_CLIENT_LOG_LEVEL") });
|
|
123
130
|
const subscriptionManagerDataPack = {
|
|
124
131
|
encode: (data) => supaliveStringify(data),
|
|
@@ -828,8 +835,9 @@ var SupaliveWebSocketServer = class {
|
|
|
828
835
|
...config
|
|
829
836
|
};
|
|
830
837
|
this.instanceId = appInstanceId;
|
|
831
|
-
if (
|
|
832
|
-
this.subManagerClient = new SubManagerClient(config.subManagerUrl);
|
|
838
|
+
if (config.subManager) this.subManagerClient = config.subManager;
|
|
839
|
+
else if (config.subManagerUrl) this.subManagerClient = new SubManagerClient(config.subManagerUrl);
|
|
840
|
+
else throw new Error("either `subManager` (in-process link) or `subManagerUrl` (WebSocket URL) is required");
|
|
833
841
|
this.cache = config.cacheLayer;
|
|
834
842
|
this.redisSub = config.redisSubClient;
|
|
835
843
|
this.appPubSubChannels = new Set(config.appPubSub?.channels ?? []);
|
|
@@ -1812,12 +1820,12 @@ function fnv1a32(s) {
|
|
|
1812
1820
|
return h >>> 0;
|
|
1813
1821
|
}
|
|
1814
1822
|
//#endregion
|
|
1815
|
-
//#region src/server/
|
|
1823
|
+
//#region src/server/sub-manager.ts
|
|
1816
1824
|
const logger$1 = logger.child({ name: "SUBS_MGR" }, { level: logLevelOf("SUBS_MANAGER_SERVER_LOG_LEVEL") });
|
|
1817
1825
|
function resolveWorkerEntry() {
|
|
1818
1826
|
const isTs = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
1819
1827
|
return {
|
|
1820
|
-
url: new URL(`./
|
|
1828
|
+
url: new URL(`./sub-manager-worker-entry${isTs ? ".ts" : ".js"}`, import.meta.url),
|
|
1821
1829
|
isTs
|
|
1822
1830
|
};
|
|
1823
1831
|
}
|
|
@@ -1826,7 +1834,7 @@ function workerExecArgv(isTs) {
|
|
|
1826
1834
|
if (typeof globalThis.Bun !== "undefined") return void 0;
|
|
1827
1835
|
return ["--import", "tsx"];
|
|
1828
1836
|
}
|
|
1829
|
-
var
|
|
1837
|
+
var ThreadWorkerHandle = class {
|
|
1830
1838
|
id;
|
|
1831
1839
|
worker;
|
|
1832
1840
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -1881,6 +1889,38 @@ var SubscriptionWorkerHandle = class {
|
|
|
1881
1889
|
}
|
|
1882
1890
|
};
|
|
1883
1891
|
/**
|
|
1892
|
+
* In-process worker handle: holds a live {@link SubscriptionWorker} and drives
|
|
1893
|
+
* it via {@link dispatchWorkerMessage} with `trusted: true`. Every `send` is a
|
|
1894
|
+
* direct `await worker.method(payload)` on the main event loop — no
|
|
1895
|
+
* `postMessage`, no structured-clone. Used when the manager is embedded in the
|
|
1896
|
+
* app server. When `ownsDb` is false the db is borrowed from the host server
|
|
1897
|
+
* and is NOT closed on shutdown.
|
|
1898
|
+
*/
|
|
1899
|
+
var InlineWorkerHandle = class {
|
|
1900
|
+
id;
|
|
1901
|
+
worker;
|
|
1902
|
+
db;
|
|
1903
|
+
ownsDb;
|
|
1904
|
+
constructor(id, worker, db, ownsDb) {
|
|
1905
|
+
this.id = id;
|
|
1906
|
+
this.worker = worker;
|
|
1907
|
+
this.db = db;
|
|
1908
|
+
this.ownsDb = ownsDb;
|
|
1909
|
+
}
|
|
1910
|
+
ready() {
|
|
1911
|
+
return Promise.resolve();
|
|
1912
|
+
}
|
|
1913
|
+
send(type, payload) {
|
|
1914
|
+
return dispatchWorkerMessage(this.worker, this.db, type, payload, { trusted: true });
|
|
1915
|
+
}
|
|
1916
|
+
async shutdown() {
|
|
1917
|
+
if (!this.ownsDb) return;
|
|
1918
|
+
try {
|
|
1919
|
+
await this.db.close();
|
|
1920
|
+
} catch (_) {}
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
/**
|
|
1884
1924
|
* One process per deployment. Routes each subscription operation to one of N
|
|
1885
1925
|
* worker threads by hashing subId. Worker threads run in parallel on separate
|
|
1886
1926
|
* CPU cores; this dispatcher does no Zod parsing — it reads `subId` directly
|
|
@@ -1890,28 +1930,72 @@ var SubscriptionWorkerHandle = class {
|
|
|
1890
1930
|
var SubscriptionManager = class SubscriptionManager {
|
|
1891
1931
|
wss;
|
|
1892
1932
|
port;
|
|
1933
|
+
listen;
|
|
1893
1934
|
workers;
|
|
1894
1935
|
pruneInterval = null;
|
|
1895
1936
|
static PRUNE_INTERVAL_MS = 10080 * 60 * 1e3;
|
|
1896
1937
|
constructor(config) {
|
|
1897
|
-
this.
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
}
|
|
1910
|
-
this.
|
|
1938
|
+
this.listen = config.listen !== false;
|
|
1939
|
+
if (this.listen) {
|
|
1940
|
+
if (config.port == null) throw new Error("SubscriptionManager: `port` is required unless `listen: false`");
|
|
1941
|
+
this.port = config.port;
|
|
1942
|
+
this.wss = new Server({ port: this.port }, subscriptionManagerDataPack);
|
|
1943
|
+
this.wss.dataPack = subscriptionManagerDataPack;
|
|
1944
|
+
}
|
|
1945
|
+
const inline = config.inline === true;
|
|
1946
|
+
let numWorkers = resolveWorkerCount(config.workers);
|
|
1947
|
+
if (inline && numWorkers !== 1) {
|
|
1948
|
+
logger$1.warn(`inline mode runs on the main event loop; forcing workers=1 (was ${numWorkers})`);
|
|
1949
|
+
numWorkers = 1;
|
|
1950
|
+
}
|
|
1951
|
+
if (inline) this.workers = [this.buildInlineWorker(config)];
|
|
1952
|
+
else {
|
|
1953
|
+
if (!config.database || !config.upstash) throw new Error("SubscriptionManager: `database` and `upstash` are required in threaded (non-inline) mode");
|
|
1954
|
+
const maxConnectionsPerWorker = resolveMaxConnectionsPerWorker(config, numWorkers);
|
|
1955
|
+
logger$1.info(`Spawning ${numWorkers} subscription worker${numWorkers === 1 ? "" : "s"}`);
|
|
1956
|
+
this.workers = Array.from({ length: numWorkers }, (_, i) => new ThreadWorkerHandle(i + 1, {
|
|
1957
|
+
workerId: i + 1,
|
|
1958
|
+
database: config.database,
|
|
1959
|
+
upstash: config.upstash,
|
|
1960
|
+
cacheTTLSeconds: config.cacheTTLSeconds,
|
|
1961
|
+
maxConnectionsPerWorker
|
|
1962
|
+
}));
|
|
1963
|
+
}
|
|
1964
|
+
if (this.listen) this.registerMethods();
|
|
1965
|
+
}
|
|
1966
|
+
/**
|
|
1967
|
+
* Build the single in-process worker for {@link SubscriptionManagerConfig.inline}
|
|
1968
|
+
* mode. Reuses the host server's {@link SubscriptionManagerConfig.db} +
|
|
1969
|
+
* {@link SubscriptionManagerConfig.cache} when provided (borrowed — not closed
|
|
1970
|
+
* on stop); otherwise opens its own pool/cache from `database`/`upstash`.
|
|
1971
|
+
*/
|
|
1972
|
+
buildInlineWorker(config) {
|
|
1973
|
+
let db;
|
|
1974
|
+
let cache;
|
|
1975
|
+
let ownsDb;
|
|
1976
|
+
if (config.db && config.cache) {
|
|
1977
|
+
db = config.db;
|
|
1978
|
+
cache = config.cache;
|
|
1979
|
+
ownsDb = false;
|
|
1980
|
+
logger$1.info("Inline subscription worker: reusing host db + cache");
|
|
1981
|
+
} else {
|
|
1982
|
+
if (!config.database || !config.upstash) throw new Error("SubscriptionManager inline mode needs either { db, cache } to reuse, or { database, upstash } to build its own");
|
|
1983
|
+
const { cacheLayer } = initCacheLayer({
|
|
1984
|
+
upstash: config.upstash,
|
|
1985
|
+
cacheTtlSeconds: config.cacheTTLSeconds,
|
|
1986
|
+
commandMaxRetries: 5,
|
|
1987
|
+
createSubClient: false
|
|
1988
|
+
});
|
|
1989
|
+
cache = cacheLayer;
|
|
1990
|
+
db = createDatabase(config.database, 1);
|
|
1991
|
+
ownsDb = true;
|
|
1992
|
+
logger$1.info("Inline subscription worker: built own db + cache");
|
|
1993
|
+
}
|
|
1994
|
+
return new InlineWorkerHandle(1, new SubscriptionWorker(1, cache, db), db, ownsDb);
|
|
1911
1995
|
}
|
|
1912
1996
|
async start() {
|
|
1913
1997
|
await Promise.all(this.workers.map((w) => w.ready()));
|
|
1914
|
-
logger$1.info(`Listening on port ${this.port}`);
|
|
1998
|
+
logger$1.info(this.listen ? `Listening on port ${this.port}` : "Started (embedded, in-process)");
|
|
1915
1999
|
try {
|
|
1916
2000
|
const minRetainedTs = await this.workers[0].send(12, null);
|
|
1917
2001
|
await this.broadcastRetentionWatermark(minRetainedTs);
|
|
@@ -1928,10 +2012,29 @@ var SubscriptionManager = class SubscriptionManager {
|
|
|
1928
2012
|
clearInterval(this.pruneInterval);
|
|
1929
2013
|
this.pruneInterval = null;
|
|
1930
2014
|
}
|
|
1931
|
-
this.wss
|
|
2015
|
+
this.wss?.close();
|
|
1932
2016
|
await Promise.all(this.workers.map((w) => w.shutdown()));
|
|
1933
2017
|
logger$1.info("Stopped");
|
|
1934
2018
|
}
|
|
2019
|
+
/**
|
|
2020
|
+
* A direct, in-process {@link SubManagerLink} to this manager, for embedding
|
|
2021
|
+
* it in the app server (pass as `SupaliveServerConfig.subManager`). Calls the
|
|
2022
|
+
* dispatch handlers directly — no socket, no serialization. The recovery
|
|
2023
|
+
* callbacks are no-ops: an in-process link never disconnects, so the app
|
|
2024
|
+
* server's reconnect/buffer machinery is inert.
|
|
2025
|
+
*/
|
|
2026
|
+
localLink() {
|
|
2027
|
+
return {
|
|
2028
|
+
registerSubscription: (p) => this.registerSubscription(p),
|
|
2029
|
+
registerSubscriptionBatch: (p) => this.registerSubscriptionBatch(p),
|
|
2030
|
+
updateSubscriptionReadSet: (p) => this.updateSubscriptionReadSet(p),
|
|
2031
|
+
unregisterSubscription: (p) => this.unregisterSubscription(p),
|
|
2032
|
+
unregisterSubscriptions: (p) => this.unregisterSubscriptions(p),
|
|
2033
|
+
invalidateWriteset: (p) => this.invalidateWriteset(p),
|
|
2034
|
+
setRecoveryDriver: () => {},
|
|
2035
|
+
setAffectedHandler: () => {}
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
1935
2038
|
async pruneCommitLogs() {
|
|
1936
2039
|
const minPerWorker = await Promise.all(this.workers.map((w) => w.send(8, null)));
|
|
1937
2040
|
let minRetainedTs = null;
|
|
@@ -1964,12 +2067,14 @@ var SubscriptionManager = class SubscriptionManager {
|
|
|
1964
2067
|
return this.workers[this.workerIndexFor(subId)];
|
|
1965
2068
|
}
|
|
1966
2069
|
registerMethods() {
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2070
|
+
const wss = this.wss;
|
|
2071
|
+
if (!wss) return;
|
|
2072
|
+
wss.register("registerSubscription", this.registerSubscription.bind(this));
|
|
2073
|
+
wss.register("registerSubscriptionBatch", this.registerSubscriptionBatch.bind(this));
|
|
2074
|
+
wss.register("updateSubscriptionReadSet", this.updateSubscriptionReadSet.bind(this));
|
|
2075
|
+
wss.register("invalidateWriteset", this.invalidateWriteset.bind(this));
|
|
2076
|
+
wss.register("unregisterSubscription", this.unregisterSubscription.bind(this));
|
|
2077
|
+
wss.register("unregisterSubscriptions", this.unregisterSubscriptions.bind(this));
|
|
1973
2078
|
}
|
|
1974
2079
|
readSubId(params) {
|
|
1975
2080
|
return params.subId;
|
|
@@ -2037,7 +2142,7 @@ function resolveWorkerCount(configured) {
|
|
|
2037
2142
|
}
|
|
2038
2143
|
function resolveMaxConnectionsPerWorker(config, workers) {
|
|
2039
2144
|
if (config.maxConnectionsPerWorker && config.maxConnectionsPerWorker > 0) return Math.floor(config.maxConnectionsPerWorker);
|
|
2040
|
-
const total = config.database
|
|
2145
|
+
const total = config.database?.maxConnections ?? (config.database?.type === "mysql" ? 20 : 10);
|
|
2041
2146
|
return Math.max(1, Math.floor(total / workers));
|
|
2042
2147
|
}
|
|
2043
2148
|
//#endregion
|