@supalive/core 1.9.0 → 1.10.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 (44) hide show
  1. package/dist/index-BQRlJY_1.d.ts +2192 -0
  2. package/dist/index-BQRlJY_1.d.ts.map +1 -0
  3. package/dist/index-D8O39FoZ.d.ts +2192 -0
  4. package/dist/index-D8O39FoZ.d.ts.map +1 -0
  5. package/dist/index-WMhtVzJA.d.ts +2192 -0
  6. package/dist/index-WMhtVzJA.d.ts.map +1 -0
  7. package/dist/mysql-CBSuONm5.d.ts +109 -0
  8. package/dist/mysql-CBSuONm5.d.ts.map +1 -0
  9. package/dist/mysql-CDenA4XE.d.ts +109 -0
  10. package/dist/mysql-CDenA4XE.d.ts.map +1 -0
  11. package/dist/mysql-DAfFK5v1.d.ts +109 -0
  12. package/dist/mysql-DAfFK5v1.d.ts.map +1 -0
  13. package/dist/postgres-BKGLPrKt.d.ts +113 -0
  14. package/dist/postgres-BKGLPrKt.d.ts.map +1 -0
  15. package/dist/postgres-CKBWXyY8.d.ts +113 -0
  16. package/dist/postgres-CKBWXyY8.d.ts.map +1 -0
  17. package/dist/postgres-DQxKs5Nu.d.ts +113 -0
  18. package/dist/postgres-DQxKs5Nu.d.ts.map +1 -0
  19. package/dist/src/client/index.d.ts +1 -1
  20. package/dist/src/exports/mysql.d.ts +1 -1
  21. package/dist/src/exports/postgres.d.ts +1 -1
  22. package/dist/src/exports/procedure.d.ts +1 -1
  23. package/dist/src/exports/schema-sql.d.ts +1 -1
  24. package/dist/src/exports/server.d.ts +24 -124
  25. package/dist/src/exports/server.d.ts.map +1 -1
  26. package/dist/src/exports/server.js +128 -30
  27. package/dist/src/exports/server.js.map +1 -1
  28. package/dist/src/exports/sub-manager-worker-entry.d.ts +1 -0
  29. package/dist/src/exports/sub-manager-worker-entry.js +71 -0
  30. package/dist/src/exports/sub-manager-worker-entry.js.map +1 -0
  31. package/dist/src/exports/subscription-manager-worker-entry.js +3 -49
  32. package/dist/src/exports/subscription-manager-worker-entry.js.map +1 -1
  33. package/dist/src/exports/types.d.ts +2 -2
  34. package/dist/sub-worker-dispatch-G8V8asfR.js +810 -0
  35. package/dist/sub-worker-dispatch-G8V8asfR.js.map +1 -0
  36. package/dist/subscription-worker-dispatch-CSQV-PO3.js +810 -0
  37. package/dist/subscription-worker-dispatch-CSQV-PO3.js.map +1 -0
  38. package/dist/types_server-AQ8Jl2_R.d.ts +521 -0
  39. package/dist/types_server-AQ8Jl2_R.d.ts.map +1 -0
  40. package/dist/types_server-BB2WvXEA.d.ts +521 -0
  41. package/dist/types_server-BB2WvXEA.d.ts.map +1 -0
  42. package/dist/types_server-DU9JMSXd.d.ts +521 -0
  43. package/dist/types_server-DU9JMSXd.d.ts.map +1 -0
  44. package/package.json +1 -1
@@ -4,7 +4,7 @@ import { r as evaluateCacheFreshness } from "../../overlap-checker-BqR9qCfy.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 RetentionWatermark, D as logger, E as logLevelOf, O as serializeMapValue, S as RETENTION_WATERMARK_CHANNEL, T as isProduction, _ as UnregisterSubscriptionResultSchema, a as createDatabase, b as UpdateSubscriptionReadSetParamsSchema, c as initDatabase, d as InvalidateWritesetResultSchema, g as UnregisterSubscriptionParamsSchema, h as RegisterSubscriptionResultSchema, i as bootstrapDbTypes, l as CacheLayer, m as RegisterSubscriptionParamsSchema, n as CORE_VERSION, o as initCacheLayer, p as RegisterSubscriptionBatchResultSchema, r as DB_QUERY_TIMEOUT_MS, s as initCore, u as InvalidateWritesetParamsSchema, w as appInstanceId, x as UpdateSubscriptionReadSetResultSchema, y as UnregisterSubscriptionsResultSchema } from "../../subscription-worker-DC9v3_Xn.js";
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-G8V8asfR.js";
8
8
  import { createServer } from "http";
9
9
  import { WebSocket, WebSocketServer } from "ws";
10
10
  import { v4 } from "uuid";
@@ -118,7 +118,7 @@ async function runCachedQuery(deps, procedure, procedureName, input, serverCtx,
118
118
  return result.data;
119
119
  }
120
120
  //#endregion
121
- //#region src/server/subscription-manager-client.ts
121
+ //#region src/server/sub-manager-client.ts
122
122
  const logger$5 = logger.child({ name: "SUBS_MGR_CLIENT" }, { level: logLevelOf("SUB_MANAGER_CLIENT_LOG_LEVEL") });
123
123
  const subscriptionManagerDataPack = {
124
124
  encode: (data) => supaliveStringify(data),
@@ -828,8 +828,9 @@ var SupaliveWebSocketServer = class {
828
828
  ...config
829
829
  };
830
830
  this.instanceId = appInstanceId;
831
- if (!config.subManagerUrl) throw new Error("subManagerUrl config is required (WebSocket URL of the sub-manager process)");
832
- this.subManagerClient = new SubManagerClient(config.subManagerUrl);
831
+ if (config.subManager) this.subManagerClient = config.subManager;
832
+ else if (config.subManagerUrl) this.subManagerClient = new SubManagerClient(config.subManagerUrl);
833
+ else throw new Error("either `subManager` (in-process link) or `subManagerUrl` (WebSocket URL) is required");
833
834
  this.cache = config.cacheLayer;
834
835
  this.redisSub = config.redisSubClient;
835
836
  this.appPubSubChannels = new Set(config.appPubSub?.channels ?? []);
@@ -1812,12 +1813,12 @@ function fnv1a32(s) {
1812
1813
  return h >>> 0;
1813
1814
  }
1814
1815
  //#endregion
1815
- //#region src/server/subscription-manager.ts
1816
+ //#region src/server/sub-manager.ts
1816
1817
  const logger$1 = logger.child({ name: "SUBS_MGR" }, { level: logLevelOf("SUBS_MANAGER_SERVER_LOG_LEVEL") });
1817
1818
  function resolveWorkerEntry() {
1818
1819
  const isTs = new URL(import.meta.url).pathname.endsWith(".ts");
1819
1820
  return {
1820
- url: new URL(`./subscription-manager-worker-entry${isTs ? ".ts" : ".js"}`, import.meta.url),
1821
+ url: new URL(`./sub-manager-worker-entry${isTs ? ".ts" : ".js"}`, import.meta.url),
1821
1822
  isTs
1822
1823
  };
1823
1824
  }
@@ -1826,7 +1827,7 @@ function workerExecArgv(isTs) {
1826
1827
  if (typeof globalThis.Bun !== "undefined") return void 0;
1827
1828
  return ["--import", "tsx"];
1828
1829
  }
1829
- var SubscriptionWorkerHandle = class {
1830
+ var ThreadWorkerHandle = class {
1830
1831
  id;
1831
1832
  worker;
1832
1833
  pending = /* @__PURE__ */ new Map();
@@ -1881,6 +1882,38 @@ var SubscriptionWorkerHandle = class {
1881
1882
  }
1882
1883
  };
1883
1884
  /**
1885
+ * In-process worker handle: holds a live {@link SubscriptionWorker} and drives
1886
+ * it via {@link dispatchWorkerMessage} with `trusted: true`. Every `send` is a
1887
+ * direct `await worker.method(payload)` on the main event loop — no
1888
+ * `postMessage`, no structured-clone. Used when the manager is embedded in the
1889
+ * app server. When `ownsDb` is false the db is borrowed from the host server
1890
+ * and is NOT closed on shutdown.
1891
+ */
1892
+ var InlineWorkerHandle = class {
1893
+ id;
1894
+ worker;
1895
+ db;
1896
+ ownsDb;
1897
+ constructor(id, worker, db, ownsDb) {
1898
+ this.id = id;
1899
+ this.worker = worker;
1900
+ this.db = db;
1901
+ this.ownsDb = ownsDb;
1902
+ }
1903
+ ready() {
1904
+ return Promise.resolve();
1905
+ }
1906
+ send(type, payload) {
1907
+ return dispatchWorkerMessage(this.worker, this.db, type, payload, { trusted: true });
1908
+ }
1909
+ async shutdown() {
1910
+ if (!this.ownsDb) return;
1911
+ try {
1912
+ await this.db.close();
1913
+ } catch (_) {}
1914
+ }
1915
+ };
1916
+ /**
1884
1917
  * One process per deployment. Routes each subscription operation to one of N
1885
1918
  * worker threads by hashing subId. Worker threads run in parallel on separate
1886
1919
  * CPU cores; this dispatcher does no Zod parsing — it reads `subId` directly
@@ -1890,28 +1923,72 @@ var SubscriptionWorkerHandle = class {
1890
1923
  var SubscriptionManager = class SubscriptionManager {
1891
1924
  wss;
1892
1925
  port;
1926
+ listen;
1893
1927
  workers;
1894
1928
  pruneInterval = null;
1895
1929
  static PRUNE_INTERVAL_MS = 10080 * 60 * 1e3;
1896
1930
  constructor(config) {
1897
- this.port = config.port;
1898
- this.wss = new Server({ port: this.port }, subscriptionManagerDataPack);
1899
- this.wss.dataPack = subscriptionManagerDataPack;
1900
- const numWorkers = resolveWorkerCount(config.workers);
1901
- const maxConnectionsPerWorker = resolveMaxConnectionsPerWorker(config, numWorkers);
1902
- logger$1.info(`Spawning ${numWorkers} subscription worker${numWorkers === 1 ? "" : "s"}`);
1903
- this.workers = Array.from({ length: numWorkers }, (_, i) => new SubscriptionWorkerHandle(i + 1, {
1904
- workerId: i + 1,
1905
- database: config.database,
1906
- upstash: config.upstash,
1907
- cacheTTLSeconds: config.cacheTTLSeconds,
1908
- maxConnectionsPerWorker
1909
- }));
1910
- this.registerMethods();
1931
+ this.listen = config.listen !== false;
1932
+ if (this.listen) {
1933
+ if (config.port == null) throw new Error("SubscriptionManager: `port` is required unless `listen: false`");
1934
+ this.port = config.port;
1935
+ this.wss = new Server({ port: this.port }, subscriptionManagerDataPack);
1936
+ this.wss.dataPack = subscriptionManagerDataPack;
1937
+ }
1938
+ const inline = config.inline === true;
1939
+ let numWorkers = resolveWorkerCount(config.workers);
1940
+ if (inline && numWorkers !== 1) {
1941
+ logger$1.warn(`inline mode runs on the main event loop; forcing workers=1 (was ${numWorkers})`);
1942
+ numWorkers = 1;
1943
+ }
1944
+ if (inline) this.workers = [this.buildInlineWorker(config)];
1945
+ else {
1946
+ if (!config.database || !config.upstash) throw new Error("SubscriptionManager: `database` and `upstash` are required in threaded (non-inline) mode");
1947
+ const maxConnectionsPerWorker = resolveMaxConnectionsPerWorker(config, numWorkers);
1948
+ logger$1.info(`Spawning ${numWorkers} subscription worker${numWorkers === 1 ? "" : "s"}`);
1949
+ this.workers = Array.from({ length: numWorkers }, (_, i) => new ThreadWorkerHandle(i + 1, {
1950
+ workerId: i + 1,
1951
+ database: config.database,
1952
+ upstash: config.upstash,
1953
+ cacheTTLSeconds: config.cacheTTLSeconds,
1954
+ maxConnectionsPerWorker
1955
+ }));
1956
+ }
1957
+ if (this.listen) this.registerMethods();
1958
+ }
1959
+ /**
1960
+ * Build the single in-process worker for {@link SubscriptionManagerConfig.inline}
1961
+ * mode. Reuses the host server's {@link SubscriptionManagerConfig.db} +
1962
+ * {@link SubscriptionManagerConfig.cache} when provided (borrowed — not closed
1963
+ * on stop); otherwise opens its own pool/cache from `database`/`upstash`.
1964
+ */
1965
+ buildInlineWorker(config) {
1966
+ let db;
1967
+ let cache;
1968
+ let ownsDb;
1969
+ if (config.db && config.cache) {
1970
+ db = config.db;
1971
+ cache = config.cache;
1972
+ ownsDb = false;
1973
+ logger$1.info("Inline subscription worker: reusing host db + cache");
1974
+ } else {
1975
+ if (!config.database || !config.upstash) throw new Error("SubscriptionManager inline mode needs either { db, cache } to reuse, or { database, upstash } to build its own");
1976
+ const { cacheLayer } = initCacheLayer({
1977
+ upstash: config.upstash,
1978
+ cacheTtlSeconds: config.cacheTTLSeconds,
1979
+ commandMaxRetries: 5,
1980
+ createSubClient: false
1981
+ });
1982
+ cache = cacheLayer;
1983
+ db = createDatabase(config.database, 1);
1984
+ ownsDb = true;
1985
+ logger$1.info("Inline subscription worker: built own db + cache");
1986
+ }
1987
+ return new InlineWorkerHandle(1, new SubscriptionWorker(1, cache, db), db, ownsDb);
1911
1988
  }
1912
1989
  async start() {
1913
1990
  await Promise.all(this.workers.map((w) => w.ready()));
1914
- logger$1.info(`Listening on port ${this.port}`);
1991
+ logger$1.info(this.listen ? `Listening on port ${this.port}` : "Started (embedded, in-process)");
1915
1992
  try {
1916
1993
  const minRetainedTs = await this.workers[0].send(12, null);
1917
1994
  await this.broadcastRetentionWatermark(minRetainedTs);
@@ -1928,10 +2005,29 @@ var SubscriptionManager = class SubscriptionManager {
1928
2005
  clearInterval(this.pruneInterval);
1929
2006
  this.pruneInterval = null;
1930
2007
  }
1931
- this.wss.close();
2008
+ this.wss?.close();
1932
2009
  await Promise.all(this.workers.map((w) => w.shutdown()));
1933
2010
  logger$1.info("Stopped");
1934
2011
  }
2012
+ /**
2013
+ * A direct, in-process {@link SubManagerLink} to this manager, for embedding
2014
+ * it in the app server (pass as `SupaliveServerConfig.subManager`). Calls the
2015
+ * dispatch handlers directly — no socket, no serialization. The recovery
2016
+ * callbacks are no-ops: an in-process link never disconnects, so the app
2017
+ * server's reconnect/buffer machinery is inert.
2018
+ */
2019
+ localLink() {
2020
+ return {
2021
+ registerSubscription: (p) => this.registerSubscription(p),
2022
+ registerSubscriptionBatch: (p) => this.registerSubscriptionBatch(p),
2023
+ updateSubscriptionReadSet: (p) => this.updateSubscriptionReadSet(p),
2024
+ unregisterSubscription: (p) => this.unregisterSubscription(p),
2025
+ unregisterSubscriptions: (p) => this.unregisterSubscriptions(p),
2026
+ invalidateWriteset: (p) => this.invalidateWriteset(p),
2027
+ setRecoveryDriver: () => {},
2028
+ setAffectedHandler: () => {}
2029
+ };
2030
+ }
1935
2031
  async pruneCommitLogs() {
1936
2032
  const minPerWorker = await Promise.all(this.workers.map((w) => w.send(8, null)));
1937
2033
  let minRetainedTs = null;
@@ -1964,12 +2060,14 @@ var SubscriptionManager = class SubscriptionManager {
1964
2060
  return this.workers[this.workerIndexFor(subId)];
1965
2061
  }
1966
2062
  registerMethods() {
1967
- this.wss.register("registerSubscription", this.registerSubscription.bind(this));
1968
- this.wss.register("registerSubscriptionBatch", this.registerSubscriptionBatch.bind(this));
1969
- this.wss.register("updateSubscriptionReadSet", this.updateSubscriptionReadSet.bind(this));
1970
- this.wss.register("invalidateWriteset", this.invalidateWriteset.bind(this));
1971
- this.wss.register("unregisterSubscription", this.unregisterSubscription.bind(this));
1972
- this.wss.register("unregisterSubscriptions", this.unregisterSubscriptions.bind(this));
2063
+ const wss = this.wss;
2064
+ if (!wss) return;
2065
+ wss.register("registerSubscription", this.registerSubscription.bind(this));
2066
+ wss.register("registerSubscriptionBatch", this.registerSubscriptionBatch.bind(this));
2067
+ wss.register("updateSubscriptionReadSet", this.updateSubscriptionReadSet.bind(this));
2068
+ wss.register("invalidateWriteset", this.invalidateWriteset.bind(this));
2069
+ wss.register("unregisterSubscription", this.unregisterSubscription.bind(this));
2070
+ wss.register("unregisterSubscriptions", this.unregisterSubscriptions.bind(this));
1973
2071
  }
1974
2072
  readSubId(params) {
1975
2073
  return params.subId;
@@ -2037,7 +2135,7 @@ function resolveWorkerCount(configured) {
2037
2135
  }
2038
2136
  function resolveMaxConnectionsPerWorker(config, workers) {
2039
2137
  if (config.maxConnectionsPerWorker && config.maxConnectionsPerWorker > 0) return Math.floor(config.maxConnectionsPerWorker);
2040
- const total = config.database.maxConnections ?? (config.database.type === "mysql" ? 20 : 10);
2138
+ const total = config.database?.maxConnections ?? (config.database?.type === "mysql" ? 20 : 10);
2041
2139
  return Math.max(1, Math.floor(total / workers));
2042
2140
  }
2043
2141
  //#endregion