@supalive/core 1.17.0 → 1.19.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 (143) hide show
  1. package/dist/index-B3i2G1gr.d.ts +2402 -0
  2. package/dist/index-B3i2G1gr.d.ts.map +1 -0
  3. package/dist/index-BHs0EtJk.d.ts +2402 -0
  4. package/dist/index-BHs0EtJk.d.ts.map +1 -0
  5. package/dist/index-BMz7jVbX.d.ts +2402 -0
  6. package/dist/index-BMz7jVbX.d.ts.map +1 -0
  7. package/dist/index-BuWa503L.d.ts +2445 -0
  8. package/dist/index-BuWa503L.d.ts.map +1 -0
  9. package/dist/index-CBIjmPUS.d.ts +2444 -0
  10. package/dist/index-CBIjmPUS.d.ts.map +1 -0
  11. package/dist/index-CKo6HvJc.d.ts +2455 -0
  12. package/dist/index-CKo6HvJc.d.ts.map +1 -0
  13. package/dist/index-CjkNEwSc.d.ts +2402 -0
  14. package/dist/index-CjkNEwSc.d.ts.map +1 -0
  15. package/dist/index-Cng74DL5.d.ts +2404 -0
  16. package/dist/index-Cng74DL5.d.ts.map +1 -0
  17. package/dist/index-DShKjpzF.d.ts +2453 -0
  18. package/dist/index-DShKjpzF.d.ts.map +1 -0
  19. package/dist/index-Dcxf_Gm-.d.ts +2454 -0
  20. package/dist/index-Dcxf_Gm-.d.ts.map +1 -0
  21. package/dist/index-yXV5AMh9.d.ts +2416 -0
  22. package/dist/index-yXV5AMh9.d.ts.map +1 -0
  23. package/dist/logger-DrXccWFZ.js +346 -0
  24. package/dist/logger-DrXccWFZ.js.map +1 -0
  25. package/dist/mysql-1ENHI0yX.d.ts +114 -0
  26. package/dist/mysql-1ENHI0yX.d.ts.map +1 -0
  27. package/dist/mysql-B2u0Ye4x.d.ts +114 -0
  28. package/dist/mysql-B2u0Ye4x.d.ts.map +1 -0
  29. package/dist/mysql-BHW-tAqt.d.ts +114 -0
  30. package/dist/mysql-BHW-tAqt.d.ts.map +1 -0
  31. package/dist/mysql-C6jg1Vpr.d.ts +114 -0
  32. package/dist/mysql-C6jg1Vpr.d.ts.map +1 -0
  33. package/dist/mysql-C_TTCZ8j.d.ts +114 -0
  34. package/dist/mysql-C_TTCZ8j.d.ts.map +1 -0
  35. package/dist/mysql-CyzJqrvO.d.ts +114 -0
  36. package/dist/mysql-CyzJqrvO.d.ts.map +1 -0
  37. package/dist/mysql-DKR33wfH.d.ts +114 -0
  38. package/dist/mysql-DKR33wfH.d.ts.map +1 -0
  39. package/dist/mysql-DcHgRk9f.d.ts +114 -0
  40. package/dist/mysql-DcHgRk9f.d.ts.map +1 -0
  41. package/dist/mysql-DnrIJoWL.d.ts +114 -0
  42. package/dist/mysql-DnrIJoWL.d.ts.map +1 -0
  43. package/dist/mysql-GsWYd6fG.d.ts +114 -0
  44. package/dist/mysql-GsWYd6fG.d.ts.map +1 -0
  45. package/dist/mysql-ygH2EMGn.js +622 -0
  46. package/dist/mysql-ygH2EMGn.js.map +1 -0
  47. package/dist/mysql-z9Jefhd6.d.ts +114 -0
  48. package/dist/mysql-z9Jefhd6.d.ts.map +1 -0
  49. package/dist/one-shot-query-9mCboWaB.js +565 -0
  50. package/dist/one-shot-query-9mCboWaB.js.map +1 -0
  51. package/dist/one-shot-query-B9SjfcCD.js +550 -0
  52. package/dist/one-shot-query-B9SjfcCD.js.map +1 -0
  53. package/dist/one-shot-query-BBiv9LGP.js +565 -0
  54. package/dist/one-shot-query-BBiv9LGP.js.map +1 -0
  55. package/dist/one-shot-query-Bxz_N-NE.js +563 -0
  56. package/dist/one-shot-query-Bxz_N-NE.js.map +1 -0
  57. package/dist/one-shot-query-DE9oQU40.js +550 -0
  58. package/dist/one-shot-query-DE9oQU40.js.map +1 -0
  59. package/dist/one-shot-query-adoWqGLs.js +565 -0
  60. package/dist/one-shot-query-adoWqGLs.js.map +1 -0
  61. package/dist/overlap-checker-CCgq_Tpa.js +284 -0
  62. package/dist/overlap-checker-CCgq_Tpa.js.map +1 -0
  63. package/dist/postgres-B2FLN1Kw.d.ts +118 -0
  64. package/dist/postgres-B2FLN1Kw.d.ts.map +1 -0
  65. package/dist/postgres-C7xHWbij.d.ts +118 -0
  66. package/dist/postgres-C7xHWbij.d.ts.map +1 -0
  67. package/dist/postgres-CFarR3cB.d.ts +118 -0
  68. package/dist/postgres-CFarR3cB.d.ts.map +1 -0
  69. package/dist/postgres-CWVM-KKP.d.ts +118 -0
  70. package/dist/postgres-CWVM-KKP.d.ts.map +1 -0
  71. package/dist/postgres-D50xpaQ-.d.ts +118 -0
  72. package/dist/postgres-D50xpaQ-.d.ts.map +1 -0
  73. package/dist/postgres-DBMAv_dq.d.ts +118 -0
  74. package/dist/postgres-DBMAv_dq.d.ts.map +1 -0
  75. package/dist/postgres-DBuP9ON_.d.ts +118 -0
  76. package/dist/postgres-DBuP9ON_.d.ts.map +1 -0
  77. package/dist/postgres-DwiUT_A6.js +867 -0
  78. package/dist/postgres-DwiUT_A6.js.map +1 -0
  79. package/dist/postgres-I4sgdBnG.d.ts +118 -0
  80. package/dist/postgres-I4sgdBnG.d.ts.map +1 -0
  81. package/dist/postgres-tVdgYJkj.d.ts +118 -0
  82. package/dist/postgres-tVdgYJkj.d.ts.map +1 -0
  83. package/dist/postgres-yAn2CF7j.d.ts +118 -0
  84. package/dist/postgres-yAn2CF7j.d.ts.map +1 -0
  85. package/dist/postgres-yTjQ-Nxk.d.ts +118 -0
  86. package/dist/postgres-yTjQ-Nxk.d.ts.map +1 -0
  87. package/dist/router-DP2ThAwh.js.map +1 -1
  88. package/dist/src/client/index.d.ts +1 -1
  89. package/dist/src/client/index.js +46 -51
  90. package/dist/src/client/index.js.map +1 -1
  91. package/dist/src/exports/mysql.d.ts +1 -1
  92. package/dist/src/exports/mysql.js +1 -1
  93. package/dist/src/exports/postgres.d.ts +1 -1
  94. package/dist/src/exports/postgres.js +1 -1
  95. package/dist/src/exports/procedure.d.ts +1 -1
  96. package/dist/src/exports/schema-sql.d.ts +1 -1
  97. package/dist/src/exports/server.d.ts +17 -7
  98. package/dist/src/exports/server.d.ts.map +1 -1
  99. package/dist/src/exports/server.js +42 -414
  100. package/dist/src/exports/server.js.map +1 -1
  101. package/dist/src/exports/sub-manager-worker-entry.js +1 -1
  102. package/dist/src/exports/types.d.ts +3 -3
  103. package/dist/src/exports/types.js +1 -1
  104. package/dist/sub-worker-dispatch-BLujn13f.js +792 -0
  105. package/dist/sub-worker-dispatch-BLujn13f.js.map +1 -0
  106. package/dist/sub-worker-dispatch-Cvqarej5.js +826 -0
  107. package/dist/sub-worker-dispatch-Cvqarej5.js.map +1 -0
  108. package/dist/sub-worker-dispatch-D-3VPkd4.js +826 -0
  109. package/dist/sub-worker-dispatch-D-3VPkd4.js.map +1 -0
  110. package/dist/sub-worker-dispatch-qiSs5Xkm.js.map +1 -1
  111. package/dist/types_client_rpc-B8jh6MKl.js +154 -0
  112. package/dist/types_client_rpc-B8jh6MKl.js.map +1 -0
  113. package/dist/types_client_rpc-BRCAgeyJ.js +155 -0
  114. package/dist/types_client_rpc-BRCAgeyJ.js.map +1 -0
  115. package/dist/types_server-B5KQ6PAO.d.ts +692 -0
  116. package/dist/types_server-B5KQ6PAO.d.ts.map +1 -0
  117. package/dist/types_server-BCst4TfZ.d.ts +703 -0
  118. package/dist/types_server-BCst4TfZ.d.ts.map +1 -0
  119. package/dist/types_server-BJBeSk6n.d.ts +691 -0
  120. package/dist/types_server-BJBeSk6n.d.ts.map +1 -0
  121. package/dist/types_server-BXeiSmd8.d.ts +691 -0
  122. package/dist/types_server-BXeiSmd8.d.ts.map +1 -0
  123. package/dist/types_server-BYwLkwbC.d.ts +691 -0
  124. package/dist/types_server-BYwLkwbC.d.ts.map +1 -0
  125. package/dist/types_server-BnRmctWG.d.ts +699 -0
  126. package/dist/types_server-BnRmctWG.d.ts.map +1 -0
  127. package/dist/types_server-Bvrohyc8.d.ts +691 -0
  128. package/dist/types_server-Bvrohyc8.d.ts.map +1 -0
  129. package/dist/types_server-C20ibVGu.d.ts +692 -0
  130. package/dist/types_server-C20ibVGu.d.ts.map +1 -0
  131. package/dist/types_server-C6oCm9oC.d.ts +691 -0
  132. package/dist/types_server-C6oCm9oC.d.ts.map +1 -0
  133. package/dist/types_server-CHJLfdxb.d.ts +691 -0
  134. package/dist/types_server-CHJLfdxb.d.ts.map +1 -0
  135. package/dist/types_server-CjjJIQps.d.ts +691 -0
  136. package/dist/types_server-CjjJIQps.d.ts.map +1 -0
  137. package/dist/types_server-CjzRPllt.d.ts +691 -0
  138. package/dist/types_server-CjzRPllt.d.ts.map +1 -0
  139. package/dist/types_server-Cy4bvdeN.d.ts +691 -0
  140. package/dist/types_server-Cy4bvdeN.d.ts.map +1 -0
  141. package/dist/types_server-DnO_fp-F.d.ts +691 -0
  142. package/dist/types_server-DnO_fp-F.d.ts.map +1 -0
  143. package/package.json +3 -2
@@ -1,2 +1,2 @@
1
- import { t as PgDatabase } from "../../postgres-B0fqJe1X.js";
1
+ import { t as PgDatabase } from "../../postgres-DBuP9ON_.js";
2
2
  export { PgDatabase };
@@ -1,2 +1,2 @@
1
- import { t as PgDatabase } from "../../postgres-CN03Espg.js";
1
+ import { t as PgDatabase } from "../../postgres-DwiUT_A6.js";
2
2
  export { PgDatabase };
@@ -1,4 +1,4 @@
1
- import { $ as ActionProcedures, An as MutationArgs, At as QueryFn, Bn as StoredSchemaColumnsOptions, Cn as ComputedFieldConfig, Ct as MutationConfig, Dn as InferSchema, Dt as OutputOf, En as IndexDefinition, Et as MutationProcedure, Fn as ReturnQuery, Ft as createMutationBuilder, Gn as tableNameToId, Hn as defineSchema, In as SchemaCodecs, It as createQueryBuilder, Ln as SchemaColumnMapping, Mn as Prettify, Mt as TypeOf, Nn as QueryArgs, Nt as createActionBuilder, On as InsertData, Ot as QueryConfig, Pn as ReturnLiveQuery, Pt as createJobBuilder, Rn as SchemaColumnsOptions, Sn as ComputedField, St as JobProcedure, Tn as DefineSchemaConfig, Tt as MutationFn, Un as schemaRegistry, Vn as defineComputedField, Wn as shouldTrackSchema, _n as matchesPredicate, _t as HandlerLogger, an as IdAndCommitTs, at as PublicProcedures, bn as ComputedCodec, bt as JobCtx, cn as JsonOpOptions, ct as Router, dn as PaginationClause, dt as ActionConfig, et as AppRouter, fn as QueryBuilder, ft as ActionCtx, gn as jsonPathExtract, gt as ContextOf, hn as jsonPathExists, ht as AnyProcedure, it as ProcedureNames, jn as OverrideField, jt as QueryProcedure, kn as Model, kt as QueryCtx, ln as OrderByOptions, lt as getContextRegistry, mn as jsonContains, mt as ActionProcedure, nt as JobProcedures, on as JsonContainsOptions, ot as QueryProcedures, pn as buildPredicateSql, pt as ActionFn, rt as MutationProcedures, sn as JsonHasKeyMultiOptions, st as RegisteredProcedure, tt as ContextRegistry, un as OrderDirection, ut as router, vn as ActionArgs, vt as HandlerObservability, wn as DeclarativeIndex, wt as MutationCtx, xn as ComputedDataType, xt as JobFn, yn as ColumnCodec, yt as JobConfig, zn as SchemaDefinition } from "../../index-B5hAqW3O.js";
1
+ import { $ as ActionProcedures, An as MutationArgs, At as QueryFn, Bn as StoredSchemaColumnsOptions, Cn as ComputedFieldConfig, Ct as MutationConfig, Dn as InferSchema, Dt as OutputOf, En as IndexDefinition, Et as MutationProcedure, Fn as ReturnQuery, Ft as createMutationBuilder, Gn as tableNameToId, Hn as defineSchema, In as SchemaCodecs, It as createQueryBuilder, Ln as SchemaColumnMapping, Mn as Prettify, Mt as TypeOf, Nn as QueryArgs, Nt as createActionBuilder, On as InsertData, Ot as QueryConfig, Pn as ReturnLiveQuery, Pt as createJobBuilder, Rn as SchemaColumnsOptions, Sn as ComputedField, St as JobProcedure, Tn as DefineSchemaConfig, Tt as MutationFn, Un as schemaRegistry, Vn as defineComputedField, Wn as shouldTrackSchema, _n as matchesPredicate, _t as HandlerLogger, an as IdAndCommitTs, at as PublicProcedures, bn as ComputedCodec, bt as JobCtx, cn as JsonOpOptions, ct as Router, dn as PaginationClause, dt as ActionConfig, et as AppRouter, fn as QueryBuilder, ft as ActionCtx, gn as jsonPathExtract, gt as ContextOf, hn as jsonPathExists, ht as AnyProcedure, it as ProcedureNames, jn as OverrideField, jt as QueryProcedure, kn as Model, kt as QueryCtx, ln as OrderByOptions, lt as getContextRegistry, mn as jsonContains, mt as ActionProcedure, nt as JobProcedures, on as JsonContainsOptions, ot as QueryProcedures, pn as buildPredicateSql, pt as ActionFn, rt as MutationProcedures, sn as JsonHasKeyMultiOptions, st as RegisteredProcedure, tt as ContextRegistry, un as OrderDirection, ut as router, vn as ActionArgs, vt as HandlerObservability, wn as DeclarativeIndex, wt as MutationCtx, xn as ComputedDataType, xt as JobFn, yn as ColumnCodec, yt as JobConfig, zn as SchemaDefinition } from "../../index-CKo6HvJc.js";
2
2
  import { n as stableStringify, r as supaliveStringify, t as groupByToMap } from "../../helper-CiacMqje.js";
3
3
  import * as z$2 from "zod";
4
4
 
@@ -1,4 +1,4 @@
1
- import { Un as schemaRegistry, zn as SchemaDefinition } from "../../index-B5hAqW3O.js";
1
+ import { Un as schemaRegistry, zn as SchemaDefinition } from "../../index-CKo6HvJc.js";
2
2
 
3
3
  //#region src/db/schema-sql.d.ts
4
4
  type SqlDialect = "postgres" | "mysql";
@@ -1,9 +1,8 @@
1
- import { Bt as IncomingJobRequest, D as Context, Gt as ScheduleHandle, Ht as JobScheduler, Kt as ScheduledJobDef, Lt as DevScheduler, Rt as DevSchedulerOptions, Ut as QStashScheduler, Vt as JobClient, Wt as QStashSchedulerOptions, _r as UpdateSubscriptionReadSetResult, ar as InvalidateWritesetParamsSchema, cr as RegisterSubscriptionParamsSchema, ct as Router, dr as UnregisterSubscriptionParamsSchema, fr as UnregisterSubscriptionResult, gr as UpdateSubscriptionReadSetParamsSchema, hr as UpdateSubscriptionReadSetParams, ir as InvalidateWritesetParams, lr as RegisterSubscriptionResult, nr as CacheLayer, or as InvalidateWritesetResult, qn as Database, qt as SupaliveDb, rr as AffectedSubscription, sr as RegisterSubscriptionParams, ur as UnregisterSubscriptionParams, zt as EnqueueOptions } from "../../index-B5hAqW3O.js";
2
- import { t as MySqlDatabase } from "../../mysql-C7Km_lgc.js";
3
- import { t as PgDatabase } from "../../postgres-B0fqJe1X.js";
4
- import { A as QueryTrigger, C as SubManagerLink, D as OperationKind, E as OperationEvent, F as startTimer, I as traceQueryExecution, M as configureRootLogger, N as elapsedMs, O as OperationOutcome, P as getRootLogger, S as SubManagerClient, T as ObservabilityOptions, b as SupaliveServerConfig, j as RequestLogMode, k as OperationTransport, p as DatabaseConfig, u as SubId, w as Observability, x as UpstashConfig, y as SubscriptionManagerConfig } from "../../types_server-kkQJDSjA.js";
1
+ import { Bt as IncomingJobRequest, D as Context, Gt as ScheduleHandle, Ht as JobScheduler, Kt as ScheduledJobDef, Lt as DevScheduler, Rt as DevSchedulerOptions, Ut as QStashScheduler, Vt as JobClient, Wt as QStashSchedulerOptions, _r as UpdateSubscriptionReadSetParamsSchema, ar as InvalidateWritesetParams, cr as RegisterSubscriptionParams, ct as Router, dr as UnregisterSubscriptionParams, fr as UnregisterSubscriptionParamsSchema, gr as UpdateSubscriptionReadSetParams, ir as AffectedSubscription, lr as RegisterSubscriptionParamsSchema, nr as CacheLayer, or as InvalidateWritesetParamsSchema, pr as UnregisterSubscriptionResult, qn as Database, qt as SupaliveDb, rr as IORedisClient, sr as InvalidateWritesetResult, ur as RegisterSubscriptionResult, vr as UpdateSubscriptionReadSetResult, zt as EnqueueOptions } from "../../index-CKo6HvJc.js";
2
+ import { t as MySqlDatabase } from "../../mysql-CyzJqrvO.js";
3
+ import { t as PgDatabase } from "../../postgres-DBuP9ON_.js";
4
+ import { A as QueryTrigger, C as SubManagerLink, D as OperationKind, E as OperationEvent, F as startTimer, I as traceQueryExecution, M as configureRootLogger, N as elapsedMs, O as OperationOutcome, P as getRootLogger, S as SubManagerClient, T as ObservabilityOptions, b as SupaliveServerConfig, j as RequestLogMode, k as OperationTransport, p as DatabaseConfig, u as SubId, w as Observability, x as UpstashConfig, y as SubscriptionManagerConfig } from "../../types_server-BCst4TfZ.js";
5
5
  import pino, { Level } from "pino";
6
- import Redis$1 from "ioredis";
7
6
 
8
7
  //#region src/server/supalive-server.d.ts
9
8
  /**
@@ -63,6 +62,11 @@ declare class SupaliveWebSocketServer<TContext extends Context> {
63
62
  * the scheduler registers are already reachable.
64
63
  */
65
64
  private syncJobSchedules;
65
+ /**
66
+ * The server's {@link Observability} — hand it to the internal caller's
67
+ * `init({ obs })` so its standalone calls log as per-procedure children of
68
+ * this server's request logger instead of a no-op.
69
+ */
66
70
  getObservability(): Observability;
67
71
  /** Absolute URL the scheduler POSTs to for a given job name. */
68
72
  endpointFor(name: string): string;
@@ -79,6 +83,13 @@ declare class SupaliveWebSocketServer<TContext extends Context> {
79
83
  * procedures can schedule/cancel one-shots without reaching for the server.
80
84
  */
81
85
  getJobClient(): JobClient;
86
+ /**
87
+ * The current in-memory retention watermark (oldest retained commit-log ts) —
88
+ * the value the one-shot query cache freshness scan uses as its retention
89
+ * guard. Hand it to the internal caller's `init({ getMinRetainedTs })` so its
90
+ * cache-first reads use the SAME guard as the WS/HTTP query paths.
91
+ */
92
+ getMinRetainedTs(): bigint;
82
93
  stop(): Promise<void>;
83
94
  getStats(): {
84
95
  instanceId: string;
@@ -159,7 +170,6 @@ declare class SupaliveWebSocketServer<TContext extends Context> {
159
170
  private reexecuteAffectedQuery;
160
171
  private executeLiveQuery;
161
172
  private sendMessage;
162
- private sendMessageRaw;
163
173
  private sendError;
164
174
  private subscribeToInstanceChannel;
165
175
  /**
@@ -316,7 +326,7 @@ type InitCacheLayerOptions = {
316
326
  };
317
327
  declare function initCacheLayer(config: InitCacheLayerOptions): {
318
328
  cacheLayer: CacheLayer;
319
- redisSubClient?: Redis$1;
329
+ redisSubClient?: IORedisClient;
320
330
  };
321
331
  //#endregion
322
332
  //#region src/server/sub_hash.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","names":[],"sources":["../../../src/server/supalive-server.ts","../../../src/logger.ts","../../../src/server/sub-manager.ts","../../../src/db/init_db.ts","../../../src/server/sub_hash.ts"],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../../../src/server/supalive-server.ts","../../../src/logger.ts","../../../src/server/sub-manager.ts","../../../src/db/init_db.ts","../../../src/server/sub_hash.ts"],"mappings":"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"}
@@ -1,418 +1,19 @@
1
1
  import { l as primaryOnlyConn, n as getContextRegistry } from "../../router-DP2ThAwh.js";
2
2
  import { n as stableStringify, r as supaliveStringify, t as groupByToMap } from "../../helper-zdJT5FUc.js";
3
- import { s as traceCacheCall } from "../../query-LJranz0c.js";
4
- import { r as evaluateCacheFreshness } from "../../overlap-checker-1TwhkAwy.js";
5
- import { i as AuthenticationError, o as ClientMessageSchema } from "../../types_client_rpc-CWZmlWmM.js";
3
+ import { i as AuthenticationError, o as ClientMessageSchema } from "../../types_client_rpc-B8jh6MKl.js";
6
4
  import { n as RemoteSubscriptionMessageSchema } from "../../types_server-BhC19R2a.js";
7
- import { n as sleep } from "../../realtime_db-B0Tin87E.js";
8
- 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-qiSs5Xkm.js";
9
- import { n as resolveReadRouting, t as makeConnRouting } from "../../read-routing-BUcQAwnd.js";
10
- import { ROOT_CONTEXT, SpanKind, SpanStatusCode, context, isSpanContextValid, metrics, propagation, trace } from "@opentelemetry/api";
5
+ import { a as serializeMapValue, i as logger, n as isProduction, r as logLevelOf, s as sleep, t as appInstanceId } from "../../logger-DrXccWFZ.js";
6
+ import { a as elapsedMs, c as traceQueryExecution, d as generateSubscriptionId, f as getHashOf, i as configureRootLogger, l as ANONYMOUS_IDENTITY, o as getRootLogger, p as resolveQueryIdentity, r as Observability, s as startTimer, t as runCachedQuery, u as generateCacheKey } from "../../one-shot-query-BBiv9LGP.js";
7
+ import { S as RetentionWatermark, _ 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, x as RETENTION_WATERMARK_CHANNEL, y as UpdateSubscriptionReadSetParamsSchema } from "../../sub-worker-dispatch-BLujn13f.js";
8
+ import { context, trace } from "@opentelemetry/api";
11
9
  import { v4 } from "uuid";
12
10
  import { createServer } from "http";
13
11
  import { WebSocket, WebSocketServer } from "ws";
14
12
  import { ExponentialBackoff, handleAll, retry } from "cockatiel";
15
- import { sha1 } from "hash-wasm";
16
13
  import { Client, Server } from "rpc-websockets";
17
14
  import { Client as Client$1, Receiver } from "@upstash/qstash";
18
15
  import { Worker } from "node:worker_threads";
19
16
  import { fileURLToPath } from "node:url";
20
- //#region src/server/sub_hash.ts
21
- /** Compute stable hash using json-stable-stringify and SHA1. */
22
- async function getHashOf(input) {
23
- return sha1((typeof input === "string" ? input : stableStringify(input)) ?? "");
24
- }
25
- /**
26
- * Default identity literal used when neither a procedure-level
27
- * `queryIdentity` override nor a per-session user is available.
28
- */
29
- const ANONYMOUS_IDENTITY = "anonymous";
30
- /**
31
- * Resolve the segmentation key that participates in the subId/cacheKey hash for
32
- * a query. Shared by every query transport (WS subscribe, WS one-shot call, and
33
- * HTTP RPC) so identity — and therefore cache segmentation — is computed
34
- * identically regardless of how the query is invoked. Priority:
35
- *
36
- * 1. Procedure-level `queryIdentity` override:
37
- * - `false` → `ANONYMOUS_IDENTITY` (cache shared across users)
38
- * - `string` → that literal
39
- * - `function` → serverCtx + input → string | null | undefined
40
- * 2. `getUserId(serverCtx)` → falls back to `ANONYMOUS_IDENTITY` when it
41
- * returns null/undefined or isn't configured.
42
- */
43
- function resolveQueryIdentity(procedure, serverCtx, input, getUserId) {
44
- const override = procedure.queryIdentity;
45
- if (override !== void 0) {
46
- if (override === false) return ANONYMOUS_IDENTITY;
47
- if (typeof override === "string") return override;
48
- if (typeof override === "function") {
49
- const value = override(serverCtx, input);
50
- if (value === null || value === void 0) return ANONYMOUS_IDENTITY;
51
- return value;
52
- }
53
- }
54
- if (!getUserId) return ANONYMOUS_IDENTITY;
55
- return getUserId(serverCtx) ?? "anonymous";
56
- }
57
- /**
58
- * Compute a subscription id from hash of procedure + input + queryIdentity.
59
- *
60
- * `queryIdentity` is the segmentation key for cache/subscriptions. By default
61
- * it is the authenticated user's id (so two users get distinct subscriptions
62
- * for the same query), but a procedure may override it to share cache/subs
63
- * across users (e.g. for a public, user-independent query).
64
- */
65
- async function generateSubscriptionId(procedure, input, queryIdentity) {
66
- return `sub_${await getHashOf(`${procedure}_${typeof input === "string" ? input : stableStringify(input)}_${queryIdentity}`)}`;
67
- }
68
- /**
69
- * Compute cache key from query name + args + queryIdentity. The identity
70
- * participates so that user-specific results don't poison each other's
71
- * cache when a procedure does authorize per-user.
72
- */
73
- async function generateCacheKey(queryName, input, queryIdentity) {
74
- return `ck_${await getHashOf(`${queryName}_${typeof input === "string" ? input : stableStringify(input)}_${queryIdentity}`)}`;
75
- }
76
- //#endregion
77
- //#region src/observability.ts
78
- let rootLogger = logger;
79
- /** Replace the process-wide root logger. Call once at startup, before creating
80
- * the server, to route Supalive's request/lifecycle logs through your own pino
81
- * instance. */
82
- function configureRootLogger(logger) {
83
- rootLogger = logger;
84
- }
85
- /** The current root logger (defaults to the env-configured singleton). */
86
- function getRootLogger() {
87
- return rootLogger;
88
- }
89
- const CLIENT_ERROR_CODES = /* @__PURE__ */ new Set([
90
- "NOT_FOUND",
91
- "BAD_REQUEST",
92
- "INVALID_MESSAGE",
93
- "PARSE_ERROR",
94
- "UNAUTHENTICATED",
95
- "RATE_LIMITED",
96
- "METHOD_NOT_ALLOWED",
97
- "TOO_MANY_CONCURRENT_MUTATIONS",
98
- "TOO_MANY_CONCURRENT_ACTIONS",
99
- "SUBSCRIBE_ERROR"
100
- ]);
101
- /**
102
- * The bound observability surface for one server instance: an injectable logger,
103
- * OTel metric instruments, and the `record()` seam every operation funnels
104
- * through. Built once in the server constructor and shared with the HTTP RPC
105
- * handler.
106
- */
107
- var Observability = class {
108
- logger;
109
- requestLog;
110
- onEvent;
111
- requestCounter;
112
- durationHistogram;
113
- activeConnections;
114
- activeSubscriptions;
115
- tracer;
116
- constructor(opts = {}) {
117
- const base = opts.logger ?? getRootLogger();
118
- this.logger = opts.level ? base.child({}, { level: opts.level }) : base;
119
- this.requestLog = opts.requestLog ?? "all";
120
- this.onEvent = opts.onEvent;
121
- const meterName = opts.meterName ?? "@supalive/core";
122
- const meter = metrics.getMeter(meterName, opts.meterVersion);
123
- this.tracer = trace.getTracer(meterName, opts.meterVersion);
124
- this.requestCounter = meter.createCounter("supalive_requests_total", { description: "Total Supalive operations (WS calls, subscribes, HTTP RPC, jobs)." });
125
- this.durationHistogram = meter.createHistogram("supalive_request_duration_ms", {
126
- description: "Operation duration in milliseconds.",
127
- unit: "ms"
128
- });
129
- this.activeConnections = meter.createUpDownCounter("supalive_active_connections", { description: "Currently open WebSocket connections." });
130
- this.activeSubscriptions = meter.createUpDownCounter("supalive_active_subscriptions", { description: "Currently registered live-query subscriptions on this instance." });
131
- }
132
- /** Record one completed operation: metrics + optional onEvent + one log line,
133
- * and finalize the op's span (status/attributes/exception) when present. */
134
- record(e) {
135
- const traceId = this.finalizeSpan(e);
136
- if (traceId) (e.extra ??= {}).traceId = traceId;
137
- const attrs = {
138
- transport: e.transport,
139
- procedure: e.procedure ?? "",
140
- kind: e.kind ?? "",
141
- outcome: e.outcome,
142
- code: e.code ?? ""
143
- };
144
- this.requestCounter.add(1, attrs);
145
- this.durationHistogram.record(e.durationMs, attrs);
146
- if (this.onEvent) try {
147
- this.onEvent(e);
148
- } catch {}
149
- if (this.requestLog === "off") return;
150
- if (e.outcome === "ok") {
151
- if (this.requestLog !== "all") return;
152
- this.logger.info(logFields(e), summary(e));
153
- return;
154
- }
155
- const fields = logFields(e);
156
- if (!e.error && e.code != null && CLIENT_ERROR_CODES.has(e.code)) this.logger.warn(fields, summary(e));
157
- else this.logger.error({
158
- ...fields,
159
- err: e.error
160
- }, summary(e));
161
- }
162
- /**
163
- * Start a SERVER span for an operation, as a child of `parent` (or the active
164
- * context). No-op — returns a non-recording span — until a TracerProvider is
165
- * registered by the app, so this costs nothing when tracing is disabled.
166
- * Returns the span plus a context that has it active, for `context.with(...)`
167
- * wrapping so nested handler work / `ctx.span` children attach correctly.
168
- */
169
- startSpan(name, opts = {}) {
170
- const parent = opts.parent ?? context.active();
171
- const span = this.tracer.startSpan(name, {
172
- kind: SpanKind.SERVER,
173
- attributes: {
174
- ...opts.transport ? { "supalive.transport": opts.transport } : {},
175
- ...opts.procedure ? { "supalive.procedure": opts.procedure } : {},
176
- ...opts.kind ? { "supalive.kind": opts.kind } : {}
177
- }
178
- }, parent);
179
- return {
180
- span,
181
- context: trace.setSpan(parent, span)
182
- };
183
- }
184
- /**
185
- * Continue a trace from an inbound carrier (HTTP headers, or a WS message's
186
- * `trace` field). Returns a context to pass as `startSpan({ parent })`. When
187
- * no propagator is registered / no trace context is present, returns the root
188
- * context (a fresh trace).
189
- */
190
- extractContext(carrier) {
191
- return propagation.extract(ROOT_CONTEXT, carrier);
192
- }
193
- /** Set the span's final status/attributes from the event, then end it.
194
- * Returns the (valid) trace id for log correlation, or undefined. */
195
- finalizeSpan(e) {
196
- const span = e.span;
197
- if (!span) return void 0;
198
- try {
199
- span.setAttribute("supalive.outcome", e.outcome);
200
- if (e.code) span.setAttribute("supalive.code", e.code);
201
- if (e.procedure) span.setAttribute("supalive.procedure", e.procedure);
202
- if (e.kind) span.setAttribute("supalive.kind", e.kind);
203
- if (e.outcome === "error") {
204
- if (e.error instanceof Error) span.recordException(e.error);
205
- span.setStatus({
206
- code: SpanStatusCode.ERROR,
207
- message: e.code
208
- });
209
- } else span.setStatus({ code: SpanStatusCode.OK });
210
- } finally {
211
- span.end();
212
- }
213
- const sc = span.spanContext();
214
- return isSpanContextValid(sc) ? sc.traceId : void 0;
215
- }
216
- /** A request-scoped child logger for `ctx.log`, bound with the given fields
217
- * (reqId, procedure, and trace id when tracing is on). */
218
- childLogger(fields) {
219
- return this.logger.child(fields);
220
- }
221
- /**
222
- * A LAZY request-scoped `ctx.log`. Most handlers never log, and pino's
223
- * `.child()` (bindings merge + serialization) isn't free — so this defers the
224
- * child creation until the first `debug/info/warn/error` call and reuses it
225
- * after. When a handler logs nothing, no child logger is ever allocated; the
226
- * only per-request cost is this small delegator object. Returns the narrow
227
- * {@link HandlerLogger} surface the handler context exposes.
228
- */
229
- lazyChildLogger(fields) {
230
- const base = this.logger;
231
- let child;
232
- const get = () => child ??= base.child(fields);
233
- return {
234
- debug: (...args) => get().debug(...args),
235
- info: (...args) => get().info(...args),
236
- warn: (...args) => get().warn(...args),
237
- error: (...args) => get().error(...args)
238
- };
239
- }
240
- /** The span's trace id, or undefined when tracing is off / the span is
241
- * non-recording. For binding onto `ctx.log`. */
242
- traceIdOf(span) {
243
- if (!span) return void 0;
244
- const sc = span.spanContext();
245
- return isSpanContextValid(sc) ? sc.traceId : void 0;
246
- }
247
- connectionOpened() {
248
- this.activeConnections.add(1);
249
- }
250
- connectionClosed() {
251
- this.activeConnections.add(-1);
252
- }
253
- subscriptionAdded(n = 1) {
254
- this.activeSubscriptions.add(n);
255
- }
256
- subscriptionRemoved(n = 1) {
257
- this.activeSubscriptions.add(-n);
258
- }
259
- };
260
- /** Start marker for an operation; pass to {@link elapsedMs} to get its duration. */
261
- function startTimer() {
262
- return performance.now();
263
- }
264
- function elapsedMs(startedAt) {
265
- return Math.round((performance.now() - startedAt) * 1e3) / 1e3;
266
- }
267
- const coreTracer = trace.getTracer("@supalive/core");
268
- /**
269
- * Run a query handler inside a `query <procedureName>` span, nested under
270
- * `parentContext` (the operation span) or the active context. The span is handed
271
- * to `run` so it becomes the query handler's `ctx.span` and the parent of any DB
272
- * spans. Cache HITs never call this (no execution), so a span appearing means a
273
- * real recompute — including the otherwise-invisible background reactive path,
274
- * where `parentContext` is omitted and the span is a root.
275
- *
276
- * No-op-cheap when tracing is off: the started span is non-recording, so we skip
277
- * the `context.with` and just run.
278
- */
279
- function traceQueryExecution(procedureName, trigger, parentContext, run) {
280
- const parent = parentContext ?? context.active();
281
- const span = coreTracer.startSpan(`query ${procedureName}`, { attributes: {
282
- "supalive.kind": "query",
283
- "supalive.procedure": procedureName,
284
- "supalive.trigger": trigger
285
- } }, parent);
286
- const exec = async () => {
287
- try {
288
- const r = await run(span);
289
- span.setStatus({ code: SpanStatusCode.OK });
290
- return r;
291
- } catch (err) {
292
- if (err instanceof Error) span.recordException(err);
293
- span.setStatus({ code: SpanStatusCode.ERROR });
294
- throw err;
295
- } finally {
296
- span.end();
297
- }
298
- };
299
- return span.isRecording() ? context.with(trace.setSpan(parent, span), exec) : exec();
300
- }
301
- function logFields(e) {
302
- const f = {
303
- op: e.op,
304
- transport: e.transport,
305
- outcome: e.outcome,
306
- durMs: e.durationMs
307
- };
308
- if (e.procedure) f.procedure = e.procedure;
309
- if (e.kind) f.kind = e.kind;
310
- if (e.code) f.code = e.code;
311
- if (e.reqId) f.reqId = e.reqId;
312
- if (e.sessionId) f.sessionId = e.sessionId;
313
- if (e.userId != null) f.userId = e.userId;
314
- if (e.extra) Object.assign(f, e.extra);
315
- return f;
316
- }
317
- function summary(e) {
318
- const label = e.procedure ? `${e.kind ?? e.op} ${e.procedure}` : e.op;
319
- return `${e.transport} ${label} ${e.outcome}`;
320
- }
321
- //#endregion
322
- //#region src/server/one-shot-query.ts
323
- /**
324
- * Execute a one-shot query (WS `call` or HTTP RPC) with the same caching model
325
- * as a live subscription — but without registering a tracked subscription.
326
- *
327
- * Flow (mirrors `handleSubscribe` / worker `register`):
328
- * 1. Resolve `queryIdentity` → `cacheKey` (identical segmentation to subs).
329
- * 2. Pick the read connection ONCE (replica when it's caught up to the client's
330
- * `clientMinTs`, else primary) and use it for BOTH the freshness scan and
331
- * the row reads, so a cache HIT offloads the primary too — not just a miss.
332
- * 3. Freshness check via {@link evaluateCacheFreshness} on that connection:
333
- * cached metadata + the retention watermark + a between-ts commit-log scan.
334
- * 4. Fresh → return the cached data.
335
- * Stale/miss → recompute via `queryInternalOn` (on the chosen connection)
336
- * and write BOTH the data and the metadata (readSet + snapshotTs) back, so
337
- * a later one-shot or a fresh subscription can reuse it.
338
- *
339
- * The retention guard is what makes an untracked one-shot safe: nothing holds
340
- * the prune watermark down for it, so an entry whose snapshot has fallen below
341
- * the oldest retained commit log is treated as a miss (its freshness scan would
342
- * be incomplete).
343
- *
344
- * `lastSnapshotTs` is the primary freshness upper bound — the latest snapshot ts
345
- * as of this call. The caller loads it (via `db.getLatestSnapshotTimestamp()`)
346
- * and passes it in, exactly like the subscribe path loads it before `register`,
347
- * so the snapshot read lives at the caller seam rather than being hidden here.
348
- *
349
- * `clientMinTs` is the caller's read high-water (the largest commit/snapshot ts
350
- * it has already observed). It gates replica routing: a query only runs on the
351
- * replica when the replica has applied at least `clientMinTs` — otherwise reads
352
- * (and the freshness scan) stay on the primary, preserving read-your-writes and
353
- * monotonic reads. It is clamped to `lastSnapshotTs` here, so a client can at
354
- * most force the primary (never a value beyond it). Returns the data plus the
355
- * `ts` the result reflects, which the caller echoes so the client can advance
356
- * its high-water.
357
- */
358
- async function runCachedQuery(deps, handlerObs, procedure, procedureName, input, serverCtx, lastSnapshotTs, clientMinTs) {
359
- const queryIdentity = resolveQueryIdentity(procedure, serverCtx, input, deps.getUserId);
360
- const cacheKey = await generateCacheKey(procedureName, stableStringify(input), queryIdentity);
361
- const routing = await resolveReadRouting(deps.db, lastSnapshotTs, clientMinTs ?? 0n);
362
- const readTs = routing.readTs;
363
- const { metadata, freshness } = await traceCacheCall("freshness", async () => {
364
- const metadata = await deps.cache.getQueryCacheMetaData(cacheKey);
365
- return {
366
- metadata,
367
- freshness: await evaluateCacheFreshness(routing.readImpl, metadata, readTs, deps.getMinRetainedTs())
368
- };
369
- });
370
- if (freshness.fresh) {
371
- const cached = await traceCacheCall("get", async (setHit) => {
372
- const c = await deps.cache.getQueryCacheFor(cacheKey);
373
- setHit(!!(c && metadata && c.version === metadata.version));
374
- return c;
375
- });
376
- if (cached && metadata && cached.version === metadata.version) {
377
- if (readTs > metadata.lastSnapshotTs) deps.cache.advanceQueryCacheMetadata(cacheKey, {
378
- lastSnapshotTs: readTs,
379
- version: metadata.version,
380
- readSet: metadata.readSet
381
- }).catch((err) => logger.error(err, "one-shot horizon advance failed"));
382
- const servedTs = readTs > metadata.lastSnapshotTs ? readTs : metadata.lastSnapshotTs;
383
- return {
384
- data: cached.data,
385
- ts: servedTs
386
- };
387
- }
388
- }
389
- const result = await deps.db.queryInternalOn({
390
- impl: routing.readImpl,
391
- beginTs: readTs
392
- }, async (ctx) => {
393
- const fn = procedure.fn;
394
- return traceQueryExecution(procedureName, "request", void 0, (querySpan) => {
395
- const qctx = {
396
- db: ctx,
397
- serverCtx,
398
- ...makeConnRouting(ctx, routing),
399
- log: handlerObs.log,
400
- span: querySpan
401
- };
402
- return fn(qctx, input);
403
- });
404
- });
405
- await deps.cache.setQueryCacheAndMetadataFor(cacheKey, result.data, {
406
- lastSnapshotTs: result.ts,
407
- version: String(result.ts),
408
- readSet: result.readSet
409
- }).catch((err) => logger.error(err, "one-shot query cache write failed"));
410
- return {
411
- data: result.data,
412
- ts: result.ts
413
- };
414
- }
415
- //#endregion
416
17
  //#region src/server/sub-manager-client.ts
417
18
  const logger$3 = logger.child({ name: "SUBS_MGR_CLIENT" }, { level: logLevelOf("SUB_MANAGER_CLIENT_LOG_LEVEL") });
418
19
  const subscriptionManagerDataPack = {
@@ -842,6 +443,9 @@ var HttpRpcHandler = class {
842
443
  rateLimiter;
843
444
  /** Job webhook prefix (`{jobPath}/`) — a job POST is `{jobPath}/<jobName>`. */
844
445
  jobPrefix;
446
+ /** App build number added as a top-level `build` field to every response
447
+ * envelope (see {@link HttpRpcDeps.buildNumber}). Undefined = omit. */
448
+ buildNumber;
845
449
  constructor(deps) {
846
450
  this.deps = deps;
847
451
  this.rpc = deps.rpc;
@@ -852,6 +456,7 @@ var HttpRpcHandler = class {
852
456
  this.rateLimiter = new FixedWindowRateLimiter(rl.windowMs ?? 6e4, rl.max ?? 120);
853
457
  }
854
458
  this.jobPrefix = `${deps.jobPath}/`;
459
+ this.buildNumber = deps.buildNumber;
855
460
  }
856
461
  /** True when this handler owns the given request path as an RPC call. The
857
462
  * procedure name is carried in the body now, so a single exact path is the
@@ -1182,6 +787,7 @@ var HttpRpcHandler = class {
1182
787
  res.setHeader("access-control-max-age", "86400");
1183
788
  }
1184
789
  send(res, status, body) {
790
+ body.build = this.buildNumber;
1185
791
  res.writeHead(status, { "content-type": "application/json" });
1186
792
  res.end(supaliveStringify(body));
1187
793
  }
@@ -1356,7 +962,7 @@ var SupaliveWebSocketServer = class {
1356
962
  this.oneShotDeps = {
1357
963
  db: this.db,
1358
964
  cache: this.cache,
1359
- getUserId: config.getUserId,
965
+ getUserId: config.getUserId ?? (() => null),
1360
966
  getMinRetainedTs: () => this.retentionWatermark.get()
1361
967
  };
1362
968
  this.rpcHandler = new HttpRpcHandler({
@@ -1366,6 +972,7 @@ var SupaliveWebSocketServer = class {
1366
972
  verifyAuth: config.verifyAuth,
1367
973
  rpc: config.rpc ?? {},
1368
974
  rpcEnabled: config.rpc !== void 0,
975
+ buildNumber: config.buildNumber,
1369
976
  storage: this.storage,
1370
977
  scheduler: this.jobClient,
1371
978
  obs: this.obs,
@@ -1465,6 +1072,11 @@ var SupaliveWebSocketServer = class {
1465
1072
  this.log.error(err, "Failed to sync job schedules");
1466
1073
  }
1467
1074
  }
1075
+ /**
1076
+ * The server's {@link Observability} — hand it to the internal caller's
1077
+ * `init({ obs })` so its standalone calls log as per-procedure children of
1078
+ * this server's request logger instead of a no-op.
1079
+ */
1468
1080
  getObservability() {
1469
1081
  return this.obs;
1470
1082
  }
@@ -1493,6 +1105,15 @@ var SupaliveWebSocketServer = class {
1493
1105
  getJobClient() {
1494
1106
  return this.jobClient;
1495
1107
  }
1108
+ /**
1109
+ * The current in-memory retention watermark (oldest retained commit-log ts) —
1110
+ * the value the one-shot query cache freshness scan uses as its retention
1111
+ * guard. Hand it to the internal caller's `init({ getMinRetainedTs })` so its
1112
+ * cache-first reads use the SAME guard as the WS/HTTP query paths.
1113
+ */
1114
+ getMinRetainedTs() {
1115
+ return this.retentionWatermark.get();
1116
+ }
1496
1117
  async stop() {
1497
1118
  this.wss.close();
1498
1119
  for (const [ws] of this.sessions) ws.close(1001, "Server shutting down");
@@ -1518,10 +1139,18 @@ var SupaliveWebSocketServer = class {
1518
1139
  */
1519
1140
  async handleHttpRequest(req, res) {
1520
1141
  const path = (req.url ?? "/").split("?")[0];
1521
- if (req.method === "GET" && path === "/_health") return sendJson(res, 200, {
1522
- status: "ok",
1523
- instanceId: this.instanceId
1524
- });
1142
+ if (req.method === "GET" && path === "/_health") {
1143
+ const guard = this.config.guardHealthByHeader;
1144
+ if (guard) {
1145
+ const raw = req.headers[guard.name.toLowerCase()];
1146
+ const headerValue = Array.isArray(raw) ? raw[0] : raw;
1147
+ if (headerValue !== void 0 && (guard.value === void 0 || headerValue === guard.value)) return sendJson(res, 404, { error: "not found" });
1148
+ }
1149
+ return sendJson(res, 200, {
1150
+ status: "ok",
1151
+ instanceId: this.instanceId
1152
+ });
1153
+ }
1525
1154
  if (this.rpcHandler.handlesPath(path)) return this.rpcHandler.handle(req, res, path);
1526
1155
  if (this.rpcHandler.handlesJobPath(path)) return this.rpcHandler.handleJob(req, res, path);
1527
1156
  if (this.config.onHttpRequest) {
@@ -1953,13 +1582,14 @@ var SupaliveWebSocketServer = class {
1953
1582
  this.obs.subscriptionAdded();
1954
1583
  }
1955
1584
  if (liveResult) {
1956
- const [updateResult] = await Promise.all([this.subManagerClient.updateSubscriptionReadSet({
1585
+ const updateResult = await this.subManagerClient.updateSubscriptionReadSet({
1957
1586
  cacheKey,
1958
1587
  subId,
1959
1588
  lastSnapshotTs: liveResult.ts,
1960
1589
  readSet: liveResult.readSet,
1961
1590
  instanceName: this.instanceId
1962
- }), this.cache.setQueryCacheFor(cacheKey, data, liveResult.ts)]);
1591
+ });
1592
+ this.cache.setQueryCacheFor(cacheKey, data, liveResult.ts).catch((err) => this.log.warn(err, "writing query cache failed"));
1963
1593
  if (!updateResult.tracked) this.log.warn(`updateSubscriptionReadSet returned tracked:false immediately after register for ${subId} — possible sub-manager restart mid-subscribe`);
1964
1594
  }
1965
1595
  dataHash = await getHashOf(data);
@@ -2256,12 +1886,10 @@ var SupaliveWebSocketServer = class {
2256
1886
  }
2257
1887
  sendMessage(session, message) {
2258
1888
  if (session.ws.readyState !== WebSocket.OPEN) return;
1889
+ message.build = this.config.buildNumber;
2259
1890
  session.ws.send(supaliveStringify(message));
2260
1891
  session.lastSentAt = Date.now();
2261
1892
  }
2262
- sendMessageRaw(session, message) {
2263
- if (session.ws.readyState === WebSocket.OPEN) session.ws.send(message);
2264
- }
2265
1893
  sendError(session, id, error, code) {
2266
1894
  this.sendMessage(session, {
2267
1895
  type: "response",