@horizon-republic/nestjs-jetstream 2.6.1 → 2.7.1

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/index.js CHANGED
@@ -809,6 +809,23 @@ var EventBus = class {
809
809
  emit(event, ...args) {
810
810
  const hook = this.hooks[event];
811
811
  if (!hook) return;
812
+ this.callHook(event, hook, ...args);
813
+ }
814
+ /**
815
+ * Hot-path optimized emit for MessageRouted events.
816
+ * Avoids rest/spread overhead of the generic `emit()`.
817
+ */
818
+ emitMessageRouted(subject, kind) {
819
+ const hook = this.hooks["messageRouted" /* MessageRouted */];
820
+ if (!hook) return;
821
+ this.callHook(
822
+ "messageRouted" /* MessageRouted */,
823
+ hook,
824
+ subject,
825
+ kind
826
+ );
827
+ }
828
+ callHook(event, hook, ...args) {
812
829
  try {
813
830
  const result = hook(...args);
814
831
  if (result && typeof result.catch === "function") {
@@ -938,7 +955,7 @@ var JetstreamStrategy = class extends Server {
938
955
  this.eventRouter.start();
939
956
  }
940
957
  if (isJetStreamRpcMode(this.options.rpc) && this.patternRegistry.hasRpcHandlers()) {
941
- this.rpcRouter.start();
958
+ await this.rpcRouter.start();
942
959
  }
943
960
  }
944
961
  if (isCoreRpcMode(this.options.rpc) && this.patternRegistry.hasRpcHandlers()) {
@@ -1032,6 +1049,13 @@ import { headers as natsHeaders2 } from "nats";
1032
1049
  // src/context/rpc.context.ts
1033
1050
  import { BaseRpcContext } from "@nestjs/microservices";
1034
1051
  var RpcContext = class extends BaseRpcContext {
1052
+ _shouldRetry = false;
1053
+ _retryDelay;
1054
+ _shouldTerminate = false;
1055
+ _terminateReason;
1056
+ // ---------------------------------------------------------------------------
1057
+ // Message accessors
1058
+ // ---------------------------------------------------------------------------
1035
1059
  /**
1036
1060
  * Get the underlying NATS message.
1037
1061
  *
@@ -1066,6 +1090,96 @@ var RpcContext = class extends BaseRpcContext {
1066
1090
  isJetStream() {
1067
1091
  return "ack" in this.args[0];
1068
1092
  }
1093
+ // ---------------------------------------------------------------------------
1094
+ // JetStream metadata (return undefined for Core NATS messages)
1095
+ // ---------------------------------------------------------------------------
1096
+ /** How many times this message has been delivered. */
1097
+ getDeliveryCount() {
1098
+ return this.asJetStream()?.info.deliveryCount;
1099
+ }
1100
+ /** The JetStream stream this message belongs to. */
1101
+ getStream() {
1102
+ return this.asJetStream()?.info.stream;
1103
+ }
1104
+ /** The stream sequence number. */
1105
+ getSequence() {
1106
+ return this.asJetStream()?.seq;
1107
+ }
1108
+ /** The message timestamp as a `Date` (derived from `info.timestampNanos`). */
1109
+ getTimestamp() {
1110
+ const nanos = this.asJetStream()?.info.timestampNanos;
1111
+ return typeof nanos === "number" ? new Date(nanos / 1e6) : void 0;
1112
+ }
1113
+ /** The name of the service that published this message (from `x-caller-name` header). */
1114
+ getCallerName() {
1115
+ return this.getHeader("x-caller-name" /* CallerName */);
1116
+ }
1117
+ // ---------------------------------------------------------------------------
1118
+ // Handler-controlled settlement
1119
+ // ---------------------------------------------------------------------------
1120
+ /**
1121
+ * Signal the transport to retry (nak) this message instead of acknowledging it.
1122
+ *
1123
+ * Use for business-level retries without throwing errors.
1124
+ * Only affects JetStream event handlers (workqueue/broadcast).
1125
+ *
1126
+ * @param opts - Optional delay in ms before redelivery.
1127
+ * @throws Error if {@link terminate} was already called.
1128
+ */
1129
+ retry(opts) {
1130
+ this.assertJetStream("retry");
1131
+ if (this._shouldTerminate) {
1132
+ throw new Error("Cannot retry \u2014 terminate() was already called");
1133
+ }
1134
+ this._shouldRetry = true;
1135
+ this._retryDelay = opts?.delayMs;
1136
+ }
1137
+ /**
1138
+ * Signal the transport to permanently reject (term) this message.
1139
+ *
1140
+ * Use when a message is no longer relevant and should not be retried or sent to DLQ.
1141
+ * Only affects JetStream event handlers (workqueue/broadcast).
1142
+ *
1143
+ * @param reason - Optional reason for termination (logged by NATS).
1144
+ * @throws Error if {@link retry} was already called.
1145
+ */
1146
+ terminate(reason) {
1147
+ this.assertJetStream("terminate");
1148
+ if (this._shouldRetry) {
1149
+ throw new Error("Cannot terminate \u2014 retry() was already called");
1150
+ }
1151
+ this._shouldTerminate = true;
1152
+ this._terminateReason = reason;
1153
+ }
1154
+ /** Narrow to JsMsg or return null for Core messages. Used by metadata getters. */
1155
+ asJetStream() {
1156
+ return this.isJetStream() ? this.args[0] : null;
1157
+ }
1158
+ /** Ensure the message is JetStream — settlement actions are not available for Core NATS. */
1159
+ assertJetStream(method) {
1160
+ if (!this.isJetStream()) {
1161
+ throw new Error(`${method}() is only available for JetStream messages`);
1162
+ }
1163
+ }
1164
+ // ---------------------------------------------------------------------------
1165
+ // Transport-facing state (read by EventRouter)
1166
+ // ---------------------------------------------------------------------------
1167
+ /** @internal */
1168
+ get shouldRetry() {
1169
+ return this._shouldRetry;
1170
+ }
1171
+ /** @internal */
1172
+ get retryDelay() {
1173
+ return this._retryDelay;
1174
+ }
1175
+ /** @internal */
1176
+ get shouldTerminate() {
1177
+ return this._shouldTerminate;
1178
+ }
1179
+ /** @internal */
1180
+ get terminateReason() {
1181
+ return this._terminateReason;
1182
+ }
1069
1183
  };
1070
1184
 
1071
1185
  // src/utils/ack-extension.ts
@@ -1104,15 +1218,20 @@ var serializeError = (err) => {
1104
1218
 
1105
1219
  // src/utils/unwrap-result.ts
1106
1220
  import { isObservable } from "rxjs";
1107
- var unwrapResult = async (result) => {
1221
+ var RESOLVED_VOID = Promise.resolve(void 0);
1222
+ var RESOLVED_NULL = Promise.resolve(null);
1223
+ var unwrapResult = (result) => {
1224
+ if (result === void 0) return RESOLVED_VOID;
1225
+ if (result === null) return RESOLVED_NULL;
1108
1226
  if (isObservable(result)) {
1109
1227
  return subscribeToFirst(result);
1110
1228
  }
1111
- const resolved = await result;
1112
- if (isObservable(resolved)) {
1113
- return subscribeToFirst(resolved);
1229
+ if (typeof result.then === "function") {
1230
+ return result.then(
1231
+ (resolved) => isObservable(resolved) ? subscribeToFirst(resolved) : resolved
1232
+ );
1114
1233
  }
1115
- return resolved;
1234
+ return Promise.resolve(result);
1116
1235
  };
1117
1236
  var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
1118
1237
  let done = false;
@@ -1190,7 +1309,7 @@ var CoreRpcServer = class {
1190
1309
  this.respondWithError(msg, new Error(`No handler for subject: ${msg.subject}`));
1191
1310
  return;
1192
1311
  }
1193
- this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "rpc" /* Rpc */);
1312
+ this.eventBus.emitMessageRouted(msg.subject, "rpc" /* Rpc */);
1194
1313
  let data;
1195
1314
  try {
1196
1315
  data = this.codec.decode(msg.data);
@@ -1688,6 +1807,10 @@ var PatternRegistry = class {
1688
1807
  registry = /* @__PURE__ */ new Map();
1689
1808
  // Cached after registerHandlers() — the registry is immutable from that point
1690
1809
  cachedPatterns = null;
1810
+ _hasEvents = false;
1811
+ _hasCommands = false;
1812
+ _hasBroadcasts = false;
1813
+ _hasOrdered = false;
1691
1814
  /**
1692
1815
  * Register all handlers from the NestJS strategy.
1693
1816
  *
@@ -1721,6 +1844,10 @@ var PatternRegistry = class {
1721
1844
  this.logger.debug(`Registered ${HANDLER_LABELS[kind]}: ${pattern} -> ${fullSubject}`);
1722
1845
  }
1723
1846
  this.cachedPatterns = this.buildPatternsByKind();
1847
+ this._hasEvents = this.cachedPatterns.events.length > 0;
1848
+ this._hasCommands = this.cachedPatterns.commands.length > 0;
1849
+ this._hasBroadcasts = this.cachedPatterns.broadcasts.length > 0;
1850
+ this._hasOrdered = this.cachedPatterns.ordered.length > 0;
1724
1851
  this.logSummary();
1725
1852
  }
1726
1853
  /** Find handler for a full NATS subject. */
@@ -1732,16 +1859,16 @@ var PatternRegistry = class {
1732
1859
  return this.getPatternsByKind().broadcasts.map((p) => buildBroadcastSubject(p));
1733
1860
  }
1734
1861
  hasBroadcastHandlers() {
1735
- return this.getPatternsByKind().broadcasts.length > 0;
1862
+ return this._hasBroadcasts;
1736
1863
  }
1737
1864
  hasRpcHandlers() {
1738
- return this.getPatternsByKind().commands.length > 0;
1865
+ return this._hasCommands;
1739
1866
  }
1740
1867
  hasEventHandlers() {
1741
- return this.getPatternsByKind().events.length > 0;
1868
+ return this._hasEvents;
1742
1869
  }
1743
1870
  hasOrderedHandlers() {
1744
- return this.getPatternsByKind().ordered.length > 0;
1871
+ return this._hasOrdered;
1745
1872
  }
1746
1873
  /** Get fully-qualified NATS subjects for ordered handlers. */
1747
1874
  getOrderedSubjects() {
@@ -1804,14 +1931,7 @@ var PatternRegistry = class {
1804
1931
 
1805
1932
  // src/server/routing/event.router.ts
1806
1933
  import { Logger as Logger9 } from "@nestjs/common";
1807
- import {
1808
- catchError as catchError2,
1809
- concatMap,
1810
- defer as defer3,
1811
- EMPTY as EMPTY2,
1812
- from as from2,
1813
- mergeMap
1814
- } from "rxjs";
1934
+ import { concatMap, from as from2, mergeMap } from "rxjs";
1815
1935
  var EventRouter = class {
1816
1936
  constructor(messageProvider, patternRegistry, codec, eventBus, deadLetterConfig, processingConfig, ackWaitMap) {
1817
1937
  this.messageProvider = messageProvider;
@@ -1852,13 +1972,8 @@ var EventRouter = class {
1852
1972
  const isOrdered = kind === "ordered" /* Ordered */;
1853
1973
  const ackExtensionInterval = isOrdered ? null : resolveAckExtensionInterval(this.getAckExtensionConfig(kind), this.ackWaitMap?.get(kind));
1854
1974
  const concurrency = this.getConcurrency(kind);
1855
- const route = (msg) => defer3(
1856
- () => isOrdered ? this.handleOrdered(msg) : this.handle(msg, ackExtensionInterval)
1857
- ).pipe(
1858
- catchError2((err) => {
1859
- this.logger.error(`Unexpected error in ${kind} event router`, err);
1860
- return EMPTY2;
1861
- })
1975
+ const route = (msg) => from2(
1976
+ isOrdered ? this.handleOrderedSafe(msg) : this.handleSafe(msg, ackExtensionInterval, kind)
1862
1977
  );
1863
1978
  const subscription = stream$.pipe(isOrdered ? concatMap(route) : mergeMap(route, concurrency)).subscribe();
1864
1979
  this.subscriptions.push(subscription);
@@ -1873,23 +1988,36 @@ var EventRouter = class {
1873
1988
  if (kind === "broadcast" /* Broadcast */) return this.processingConfig?.broadcast?.ackExtension;
1874
1989
  return void 0;
1875
1990
  }
1876
- /** Handle a single event message: decode -> execute handler -> ack/nak. */
1877
- handle(msg, ackExtensionInterval) {
1878
- const resolved = this.decodeMessage(msg);
1879
- if (!resolved) return EMPTY2;
1880
- return from2(
1881
- this.executeHandler(resolved.handler, resolved.data, resolved.ctx, msg, ackExtensionInterval)
1882
- );
1991
+ /** Handle a single event message with error isolation. */
1992
+ async handleSafe(msg, ackExtensionInterval, kind) {
1993
+ try {
1994
+ const resolved = this.decodeMessage(msg);
1995
+ if (!resolved) return;
1996
+ await this.executeHandler(
1997
+ resolved.handler,
1998
+ resolved.data,
1999
+ resolved.ctx,
2000
+ msg,
2001
+ ackExtensionInterval
2002
+ );
2003
+ } catch (err) {
2004
+ this.logger.error(`Unexpected error in ${kind} event router`, err);
2005
+ }
1883
2006
  }
1884
- /** Handle an ordered message: decode -> execute handler -> no ack/nak. */
1885
- handleOrdered(msg) {
1886
- const resolved = this.decodeMessage(msg, true);
1887
- if (!resolved) return EMPTY2;
1888
- return from2(
1889
- unwrapResult(resolved.handler(resolved.data, resolved.ctx)).catch((err) => {
1890
- this.logger.error(`Ordered handler error (${msg.subject}):`, err);
1891
- })
1892
- );
2007
+ /** Handle an ordered message with error isolation. */
2008
+ async handleOrderedSafe(msg) {
2009
+ try {
2010
+ const resolved = this.decodeMessage(msg, true);
2011
+ if (!resolved) return;
2012
+ await unwrapResult(resolved.handler(resolved.data, resolved.ctx));
2013
+ if (resolved.ctx.shouldRetry || resolved.ctx.shouldTerminate) {
2014
+ this.logger.warn(
2015
+ `retry()/terminate() ignored for ordered message ${msg.subject} \u2014 ordered consumers auto-acknowledge`
2016
+ );
2017
+ }
2018
+ } catch (err) {
2019
+ this.logger.error(`Ordered handler error (${msg.subject}):`, err);
2020
+ }
1893
2021
  }
1894
2022
  /** Resolve handler, decode payload, and build context. Returns null on failure. */
1895
2023
  decodeMessage(msg, isOrdered = false) {
@@ -1907,7 +2035,7 @@ var EventRouter = class {
1907
2035
  this.logger.error(`Decode error for ${msg.subject}:`, err);
1908
2036
  return null;
1909
2037
  }
1910
- this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "event" /* Event */);
2038
+ this.eventBus.emitMessageRouted(msg.subject, "event" /* Event */);
1911
2039
  return { handler, data, ctx: new RpcContext([msg]) };
1912
2040
  }
1913
2041
  /** Execute handler, then ack on success or nak/dead-letter on failure. */
@@ -1915,7 +2043,13 @@ var EventRouter = class {
1915
2043
  const stopAckExtension = startAckExtensionTimer(msg, ackExtensionInterval);
1916
2044
  try {
1917
2045
  await unwrapResult(handler(data, ctx));
1918
- msg.ack();
2046
+ if (ctx.shouldTerminate) {
2047
+ msg.term(ctx.terminateReason);
2048
+ } else if (ctx.shouldRetry) {
2049
+ msg.nak(ctx.retryDelay);
2050
+ } else {
2051
+ msg.ack();
2052
+ }
1919
2053
  } catch (err) {
1920
2054
  this.logger.error(`Event handler error (${msg.subject}):`, err);
1921
2055
  if (this.isDeadLetter(msg)) {
@@ -1964,7 +2098,7 @@ var EventRouter = class {
1964
2098
  // src/server/routing/rpc.router.ts
1965
2099
  import { Logger as Logger10 } from "@nestjs/common";
1966
2100
  import { headers } from "nats";
1967
- import { catchError as catchError3, defer as defer4, EMPTY as EMPTY3, from as from3, mergeMap as mergeMap2 } from "rxjs";
2101
+ import { from as from3, mergeMap as mergeMap2 } from "rxjs";
1968
2102
  var RpcRouter = class {
1969
2103
  constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap) {
1970
2104
  this.messageProvider = messageProvider;
@@ -1982,6 +2116,7 @@ var RpcRouter = class {
1982
2116
  concurrency;
1983
2117
  resolvedAckExtensionInterval;
1984
2118
  subscription = null;
2119
+ cachedNc = null;
1985
2120
  /** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
1986
2121
  get ackExtensionInterval() {
1987
2122
  if (this.resolvedAckExtensionInterval !== void 0) return this.resolvedAckExtensionInterval;
@@ -1992,56 +2127,50 @@ var RpcRouter = class {
1992
2127
  return this.resolvedAckExtensionInterval;
1993
2128
  }
1994
2129
  /** Start routing command messages to handlers. */
1995
- start() {
1996
- this.subscription = this.messageProvider.commands$.pipe(
1997
- mergeMap2(
1998
- (msg) => defer4(() => this.handle(msg)).pipe(
1999
- catchError3((err) => {
2000
- this.logger.error("Unexpected error in RPC router", err);
2001
- return EMPTY3;
2002
- })
2003
- ),
2004
- this.concurrency
2005
- )
2006
- ).subscribe();
2130
+ async start() {
2131
+ this.cachedNc = await this.connection.getConnection();
2132
+ this.subscription = this.messageProvider.commands$.pipe(mergeMap2((msg) => from3(this.handleSafe(msg)), this.concurrency)).subscribe();
2007
2133
  }
2008
2134
  /** Stop routing and unsubscribe. */
2009
2135
  destroy() {
2010
2136
  this.subscription?.unsubscribe();
2011
2137
  this.subscription = null;
2012
2138
  }
2013
- /** Handle a single RPC command message. */
2014
- handle(msg) {
2015
- const handler = this.patternRegistry.getHandler(msg.subject);
2016
- if (!handler) {
2017
- msg.term(`No handler for RPC: ${msg.subject}`);
2018
- this.logger.error(`No handler for RPC subject: ${msg.subject}`);
2019
- return EMPTY3;
2020
- }
2021
- const replyTo = msg.headers?.get("x-reply-to" /* ReplyTo */);
2022
- const correlationId = msg.headers?.get("x-correlation-id" /* CorrelationId */);
2023
- if (!replyTo || !correlationId) {
2024
- msg.term("Missing required headers (reply-to or correlation-id)");
2025
- this.logger.error(`Missing headers for RPC: ${msg.subject}`);
2026
- return EMPTY3;
2027
- }
2028
- let data;
2139
+ /** Handle a single RPC command message with error isolation. */
2140
+ async handleSafe(msg) {
2029
2141
  try {
2030
- data = this.codec.decode(msg.data);
2142
+ const handler = this.patternRegistry.getHandler(msg.subject);
2143
+ if (!handler) {
2144
+ msg.term(`No handler for RPC: ${msg.subject}`);
2145
+ this.logger.error(`No handler for RPC subject: ${msg.subject}`);
2146
+ return;
2147
+ }
2148
+ const { headers: msgHeaders } = msg;
2149
+ const replyTo = msgHeaders?.get("x-reply-to" /* ReplyTo */);
2150
+ const correlationId = msgHeaders?.get("x-correlation-id" /* CorrelationId */);
2151
+ if (!replyTo || !correlationId) {
2152
+ msg.term("Missing required headers (reply-to or correlation-id)");
2153
+ this.logger.error(`Missing headers for RPC: ${msg.subject}`);
2154
+ return;
2155
+ }
2156
+ let data;
2157
+ try {
2158
+ data = this.codec.decode(msg.data);
2159
+ } catch (err) {
2160
+ msg.term("Decode error");
2161
+ this.logger.error(`Decode error for RPC ${msg.subject}:`, err);
2162
+ return;
2163
+ }
2164
+ this.eventBus.emitMessageRouted(msg.subject, "rpc" /* Rpc */);
2165
+ await this.executeHandler(handler, data, msg, replyTo, correlationId);
2031
2166
  } catch (err) {
2032
- msg.term("Decode error");
2033
- this.logger.error(`Decode error for RPC ${msg.subject}:`, err);
2034
- return EMPTY3;
2167
+ this.logger.error("Unexpected error in RPC router", err);
2035
2168
  }
2036
- this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "rpc" /* Rpc */);
2037
- return from3(this.executeHandler(handler, data, msg, replyTo, correlationId));
2038
2169
  }
2039
2170
  /** Execute handler, publish response, settle message. */
2040
2171
  async executeHandler(handler, data, msg, replyTo, correlationId) {
2041
- const nc = await this.connection.getConnection();
2172
+ const nc = this.cachedNc ?? await this.connection.getConnection();
2042
2173
  const ctx = new RpcContext([msg]);
2043
- const hdrs = headers();
2044
- hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
2045
2174
  let settled = false;
2046
2175
  const stopAckExtension = startAckExtensionTimer(msg, this.ackExtensionInterval);
2047
2176
  const timeoutId = setTimeout(() => {
@@ -2060,6 +2189,8 @@ var RpcRouter = class {
2060
2189
  stopAckExtension?.();
2061
2190
  msg.ack();
2062
2191
  try {
2192
+ const hdrs = headers();
2193
+ hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
2063
2194
  nc.publish(replyTo, this.codec.encode(result), { headers: hdrs });
2064
2195
  } catch (publishErr) {
2065
2196
  this.logger.error(`Failed to publish RPC response for ${msg.subject}`, publishErr);
@@ -2070,6 +2201,8 @@ var RpcRouter = class {
2070
2201
  clearTimeout(timeoutId);
2071
2202
  stopAckExtension?.();
2072
2203
  try {
2204
+ const hdrs = headers();
2205
+ hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
2073
2206
  hdrs.set("x-error" /* Error */, "true");
2074
2207
  nc.publish(replyTo, this.codec.encode(serializeError(err)), { headers: hdrs });
2075
2208
  } catch (encodeErr) {
@@ -2517,8 +2650,12 @@ export {
2517
2650
  RpcContext,
2518
2651
  StreamKind,
2519
2652
  TransportEvent,
2653
+ buildSubject,
2654
+ consumerName,
2520
2655
  getClientToken,
2656
+ internalName,
2521
2657
  isCoreRpcMode,
2522
2658
  isJetStreamRpcMode,
2659
+ streamName,
2523
2660
  toNanos
2524
2661
  };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@horizon-republic/nestjs-jetstream",
3
- "version": "2.6.1",
3
+ "version": "2.7.1",
4
4
  "description": "A NestJS transport for NATS with JetStream events, broadcast fan-out, and Core/JetStream RPC.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/HorizonRepublic/nestjs-jetstream.git"
8
8
  },
9
- "homepage": "https://github.com/HorizonRepublic/nestjs-jetstream#readme",
9
+ "homepage": "https://horizonrepublic.github.io/nestjs-jetstream/",
10
10
  "bugs": {
11
11
  "url": "https://github.com/HorizonRepublic/nestjs-jetstream/issues"
12
12
  },
@@ -67,7 +67,7 @@
67
67
  "@nestjs/platform-express": "^11.1.17",
68
68
  "@nestjs/testing": "^11.1.17",
69
69
  "@types/node": "^25.5.0",
70
- "@vitest/coverage-v8": "^4.1.1",
70
+ "@vitest/coverage-v8": "^4.1.2",
71
71
  "eslint": "^10.1.0",
72
72
  "eslint-config-prettier": "^10.1.8",
73
73
  "eslint-plugin-prefer-arrow": "^1.2.3",
@@ -77,9 +77,9 @@
77
77
  "prettier": "^3.8.1",
78
78
  "tsup": "^8.5.1",
79
79
  "tsx": "^4.21.0",
80
- "typescript": "~5.9.0",
80
+ "typescript": "~5.9.3",
81
81
  "typescript-eslint": "^8.57.2",
82
- "vitest": "^4.1.1"
82
+ "vitest": "^4.1.2"
83
83
  },
84
84
  "scripts": {
85
85
  "build": "tsup",