@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.
Files changed (71) hide show
  1. package/dist/db-ClCicssQ.js.map +1 -1
  2. package/dist/index-BQRlJY_1.d.ts +2192 -0
  3. package/dist/index-BQRlJY_1.d.ts.map +1 -0
  4. package/dist/index-C_Zew1wH.d.ts +2209 -0
  5. package/dist/index-C_Zew1wH.d.ts.map +1 -0
  6. package/dist/index-D8O39FoZ.d.ts +2192 -0
  7. package/dist/index-D8O39FoZ.d.ts.map +1 -0
  8. package/dist/index-WMhtVzJA.d.ts +2192 -0
  9. package/dist/index-WMhtVzJA.d.ts.map +1 -0
  10. package/dist/mysql-BXy4mk-9.d.ts +113 -0
  11. package/dist/mysql-BXy4mk-9.d.ts.map +1 -0
  12. package/dist/mysql-CBSuONm5.d.ts +109 -0
  13. package/dist/mysql-CBSuONm5.d.ts.map +1 -0
  14. package/dist/mysql-CDenA4XE.d.ts +109 -0
  15. package/dist/mysql-CDenA4XE.d.ts.map +1 -0
  16. package/dist/mysql-D-PGYc1L.js +609 -0
  17. package/dist/mysql-D-PGYc1L.js.map +1 -0
  18. package/dist/mysql-DAfFK5v1.d.ts +109 -0
  19. package/dist/mysql-DAfFK5v1.d.ts.map +1 -0
  20. package/dist/mysql-txOOzccX.js +609 -0
  21. package/dist/mysql-txOOzccX.js.map +1 -0
  22. package/dist/overlap-checker-BoWNco2y.js +258 -0
  23. package/dist/overlap-checker-BoWNco2y.js.map +1 -0
  24. package/dist/overlap-checker-DvRqQOTe.js +265 -0
  25. package/dist/overlap-checker-DvRqQOTe.js.map +1 -0
  26. package/dist/postgres-BKGLPrKt.d.ts +113 -0
  27. package/dist/postgres-BKGLPrKt.d.ts.map +1 -0
  28. package/dist/postgres-CKBWXyY8.d.ts +113 -0
  29. package/dist/postgres-CKBWXyY8.d.ts.map +1 -0
  30. package/dist/postgres-CapFFTTn.d.ts +117 -0
  31. package/dist/postgres-CapFFTTn.d.ts.map +1 -0
  32. package/dist/postgres-Cn9vTBX7.js +864 -0
  33. package/dist/postgres-Cn9vTBX7.js.map +1 -0
  34. package/dist/postgres-DQxKs5Nu.d.ts +113 -0
  35. package/dist/postgres-DQxKs5Nu.d.ts.map +1 -0
  36. package/dist/postgres-tLkdxhFu.js +864 -0
  37. package/dist/postgres-tLkdxhFu.js.map +1 -0
  38. package/dist/src/client/index.d.ts +1 -1
  39. package/dist/src/exports/mysql.d.ts +1 -1
  40. package/dist/src/exports/mysql.js +1 -1
  41. package/dist/src/exports/postgres.d.ts +1 -1
  42. package/dist/src/exports/postgres.js +1 -1
  43. package/dist/src/exports/procedure.d.ts +1 -1
  44. package/dist/src/exports/schema-sql.d.ts +1 -1
  45. package/dist/src/exports/server.d.ts +24 -124
  46. package/dist/src/exports/server.d.ts.map +1 -1
  47. package/dist/src/exports/server.js +137 -32
  48. package/dist/src/exports/server.js.map +1 -1
  49. package/dist/src/exports/sub-manager-worker-entry.d.ts +1 -0
  50. package/dist/src/exports/sub-manager-worker-entry.js +71 -0
  51. package/dist/src/exports/sub-manager-worker-entry.js.map +1 -0
  52. package/dist/src/exports/subscription-manager-worker-entry.js +3 -49
  53. package/dist/src/exports/subscription-manager-worker-entry.js.map +1 -1
  54. package/dist/src/exports/types.d.ts +2 -2
  55. package/dist/sub-worker-dispatch-Cokz99D1.js +810 -0
  56. package/dist/sub-worker-dispatch-Cokz99D1.js.map +1 -0
  57. package/dist/sub-worker-dispatch-DohrszOw.js +810 -0
  58. package/dist/sub-worker-dispatch-DohrszOw.js.map +1 -0
  59. package/dist/sub-worker-dispatch-G8V8asfR.js +810 -0
  60. package/dist/sub-worker-dispatch-G8V8asfR.js.map +1 -0
  61. package/dist/subscription-worker-dispatch-CSQV-PO3.js +810 -0
  62. package/dist/subscription-worker-dispatch-CSQV-PO3.js.map +1 -0
  63. package/dist/types_server-AQ8Jl2_R.d.ts +521 -0
  64. package/dist/types_server-AQ8Jl2_R.d.ts.map +1 -0
  65. package/dist/types_server-BB2WvXEA.d.ts +521 -0
  66. package/dist/types_server-BB2WvXEA.d.ts.map +1 -0
  67. package/dist/types_server-DU9JMSXd.d.ts +521 -0
  68. package/dist/types_server-DU9JMSXd.d.ts.map +1 -0
  69. package/dist/types_server-SPElhjga.d.ts +521 -0
  70. package/dist/types_server-SPElhjga.d.ts.map +1 -0
  71. 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-BqR9qCfy.js";
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 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-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) return cached.data;
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/subscription-manager-client.ts
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 (!config.subManagerUrl) throw new Error("subManagerUrl config is required (WebSocket URL of the sub-manager process)");
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/subscription-manager.ts
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(`./subscription-manager-worker-entry${isTs ? ".ts" : ".js"}`, import.meta.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 SubscriptionWorkerHandle = class {
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.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();
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.close();
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
- 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));
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.maxConnections ?? (config.database.type === "mysql" ? 20 : 10);
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