@lakutata/nats 2.3.0 → 3.0.0-beta.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 (121) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +288 -25
  3. package/dist/cjs/codecs/JSONCodec.d.ts +32 -0
  4. package/dist/cjs/codecs/JSONCodec.js +44 -0
  5. package/dist/cjs/codecs/MessagePackCodec.d.ts +22 -0
  6. package/dist/cjs/codecs/MessagePackCodec.js +35 -0
  7. package/dist/cjs/components/NATS.d.ts +376 -0
  8. package/dist/cjs/components/NATS.js +772 -0
  9. package/dist/cjs/entrypoints/NatsServiceEntrypoint.d.ts +102 -0
  10. package/dist/cjs/entrypoints/NatsServiceEntrypoint.js +127 -0
  11. package/dist/cjs/exceptions/NatsBadRequestException.d.ts +11 -0
  12. package/dist/cjs/exceptions/NatsBadRequestException.js +18 -0
  13. package/dist/cjs/exceptions/NatsBulkException.d.ts +19 -0
  14. package/dist/cjs/exceptions/NatsBulkException.js +26 -0
  15. package/dist/cjs/exceptions/NatsForbiddenException.d.ts +11 -0
  16. package/dist/cjs/exceptions/NatsForbiddenException.js +18 -0
  17. package/dist/cjs/exceptions/NatsInternalServerException.d.ts +12 -0
  18. package/dist/cjs/exceptions/NatsInternalServerException.js +19 -0
  19. package/dist/cjs/exceptions/NatsNoRespondersAvailableException.d.ts +12 -0
  20. package/dist/cjs/exceptions/NatsNoRespondersAvailableException.js +19 -0
  21. package/dist/cjs/exceptions/NatsNotFoundException.d.ts +11 -0
  22. package/dist/cjs/exceptions/NatsNotFoundException.js +18 -0
  23. package/dist/cjs/exceptions/NatsRequestTimeoutException.d.ts +11 -0
  24. package/dist/cjs/exceptions/NatsRequestTimeoutException.js +18 -0
  25. package/dist/cjs/exports/Nats.d.ts +18 -0
  26. package/dist/cjs/exports/Nats.js +39 -0
  27. package/dist/cjs/interfaces/NatsClientOptions.d.ts +106 -0
  28. package/dist/{lib → cjs/lib}/ServiceEventCodec.d.ts +0 -1
  29. package/dist/{lib → cjs/lib}/ServiceEventCodec.js +19 -9
  30. package/dist/cjs/package.json +1 -0
  31. package/dist/cjs/providers/ServiceProxy.d.ts +210 -0
  32. package/dist/cjs/providers/ServiceProxy.js +274 -0
  33. package/dist/cjs/types/SubscribeOptions.d.ts +33 -0
  34. package/dist/esm/codecs/JSONCodec.js +41 -0
  35. package/dist/esm/codecs/MessagePackCodec.js +32 -0
  36. package/dist/esm/components/NATS.js +767 -0
  37. package/dist/esm/entrypoints/NatsServiceEntrypoint.js +123 -0
  38. package/dist/esm/exceptions/NatsBadRequestException.js +14 -0
  39. package/dist/esm/exceptions/NatsBulkException.js +22 -0
  40. package/dist/esm/exceptions/NatsForbiddenException.js +14 -0
  41. package/dist/esm/exceptions/NatsInternalServerException.js +15 -0
  42. package/dist/esm/exceptions/NatsNoRespondersAvailableException.js +15 -0
  43. package/dist/esm/exceptions/NatsNotFoundException.js +14 -0
  44. package/dist/esm/exceptions/NatsRequestTimeoutException.js +14 -0
  45. package/dist/esm/exports/Nats.js +15 -0
  46. package/dist/esm/interfaces/NatsClientOptions.js +1 -0
  47. package/dist/esm/lib/ServiceEventCodec.js +21 -0
  48. package/dist/esm/providers/ServiceProxy.js +270 -0
  49. package/dist/esm/types/SubscribeOptions.js +1 -0
  50. package/dist/types/codecs/JSONCodec.d.ts +32 -0
  51. package/dist/types/codecs/MessagePackCodec.d.ts +22 -0
  52. package/dist/types/components/NATS.d.ts +376 -0
  53. package/dist/types/entrypoints/NatsServiceEntrypoint.d.ts +102 -0
  54. package/dist/types/exceptions/NatsBadRequestException.d.ts +11 -0
  55. package/dist/types/exceptions/NatsBulkException.d.ts +19 -0
  56. package/dist/types/exceptions/NatsForbiddenException.d.ts +11 -0
  57. package/dist/types/exceptions/NatsInternalServerException.d.ts +12 -0
  58. package/dist/types/exceptions/NatsNoRespondersAvailableException.d.ts +12 -0
  59. package/dist/types/exceptions/NatsNotFoundException.d.ts +11 -0
  60. package/dist/types/exceptions/NatsRequestTimeoutException.d.ts +11 -0
  61. package/dist/types/exports/Nats.d.ts +18 -0
  62. package/dist/types/interfaces/NatsClientOptions.d.ts +106 -0
  63. package/dist/types/lib/ServiceEventCodec.d.ts +9 -0
  64. package/dist/types/providers/ServiceProxy.d.ts +210 -0
  65. package/dist/types/types/SubscribeOptions.d.ts +33 -0
  66. package/package.json +42 -33
  67. package/CHANGELOG.md +0 -288
  68. package/dist/CommonExports.d.ts +0 -19
  69. package/dist/CommonExports.d.ts.map +0 -1
  70. package/dist/CommonExports.js +0 -35
  71. package/dist/codecs/JSONCodec.d.ts +0 -3
  72. package/dist/codecs/JSONCodec.d.ts.map +0 -1
  73. package/dist/codecs/JSONCodec.js +0 -15
  74. package/dist/codecs/MessagePackCodec.d.ts +0 -3
  75. package/dist/codecs/MessagePackCodec.d.ts.map +0 -1
  76. package/dist/codecs/MessagePackCodec.js +0 -16
  77. package/dist/components/NATS.d.ts +0 -252
  78. package/dist/components/NATS.d.ts.map +0 -1
  79. package/dist/components/NATS.js +0 -564
  80. package/dist/entrypoints/SetupNatsServiceEntrypoint.d.ts +0 -8
  81. package/dist/entrypoints/SetupNatsServiceEntrypoint.d.ts.map +0 -1
  82. package/dist/entrypoints/SetupNatsServiceEntrypoint.js +0 -41
  83. package/dist/exceptions/NatsBadRequestException.d.ts +0 -7
  84. package/dist/exceptions/NatsBadRequestException.d.ts.map +0 -1
  85. package/dist/exceptions/NatsBadRequestException.js +0 -13
  86. package/dist/exceptions/NatsBulkException.d.ts +0 -11
  87. package/dist/exceptions/NatsBulkException.d.ts.map +0 -1
  88. package/dist/exceptions/NatsBulkException.js +0 -17
  89. package/dist/exceptions/NatsForbiddenException.d.ts +0 -7
  90. package/dist/exceptions/NatsForbiddenException.d.ts.map +0 -1
  91. package/dist/exceptions/NatsForbiddenException.js +0 -13
  92. package/dist/exceptions/NatsInternalServerException.d.ts +0 -7
  93. package/dist/exceptions/NatsInternalServerException.d.ts.map +0 -1
  94. package/dist/exceptions/NatsInternalServerException.js +0 -13
  95. package/dist/exceptions/NatsNoRespondersAvailableException.d.ts +0 -7
  96. package/dist/exceptions/NatsNoRespondersAvailableException.d.ts.map +0 -1
  97. package/dist/exceptions/NatsNoRespondersAvailableException.js +0 -13
  98. package/dist/exceptions/NatsNotFoundException.d.ts +0 -7
  99. package/dist/exceptions/NatsNotFoundException.d.ts.map +0 -1
  100. package/dist/exceptions/NatsNotFoundException.js +0 -13
  101. package/dist/exceptions/NatsRequestTimeoutException.d.ts +0 -7
  102. package/dist/exceptions/NatsRequestTimeoutException.d.ts.map +0 -1
  103. package/dist/exceptions/NatsRequestTimeoutException.js +0 -13
  104. package/dist/exceptions/ServiceInvokeException.d.ts +0 -6
  105. package/dist/exceptions/ServiceInvokeException.d.ts.map +0 -1
  106. package/dist/exceptions/ServiceInvokeException.js +0 -11
  107. package/dist/interfaces/NatsClientOptions.d.ts +0 -26
  108. package/dist/interfaces/NatsClientOptions.d.ts.map +0 -1
  109. package/dist/lib/ServiceEventCodec.d.ts.map +0 -1
  110. package/dist/lib/ServiceResponseCodec.d.ts +0 -22
  111. package/dist/lib/ServiceResponseCodec.d.ts.map +0 -1
  112. package/dist/lib/ServiceResponseCodec.js +0 -50
  113. package/dist/providers/ServiceProxy.d.ts +0 -85
  114. package/dist/providers/ServiceProxy.d.ts.map +0 -1
  115. package/dist/providers/ServiceProxy.js +0 -124
  116. package/dist/types/SubscribeOptions.d.ts +0 -12
  117. package/dist/types/SubscribeOptions.d.ts.map +0 -1
  118. package/docs/large-payload-design.md +0 -199
  119. package/tsconfig.json +0 -8
  120. /package/dist/{interfaces → cjs/interfaces}/NatsClientOptions.js +0 -0
  121. /package/dist/{types → cjs/types}/SubscribeOptions.js +0 -0
@@ -1,564 +0,0 @@
1
- "use strict";
2
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
- return c > 3 && r && Object.defineProperty(target, key, r), r;
7
- };
8
- var __metadata = (this && this.__metadata) || function (k, v) {
9
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.NATS = exports.buildNatsClientOptions = void 0;
13
- const lakutata_1 = require("lakutata");
14
- const di_1 = require("lakutata/decorator/di");
15
- const nats_1 = require("nats");
16
- const NatsBadRequestException_1 = require("../exceptions/NatsBadRequestException");
17
- const NatsForbiddenException_1 = require("../exceptions/NatsForbiddenException");
18
- const NatsNotFoundException_1 = require("../exceptions/NatsNotFoundException");
19
- const NatsRequestTimeoutException_1 = require("../exceptions/NatsRequestTimeoutException");
20
- const NatsNoRespondersAvailableException_1 = require("../exceptions/NatsNoRespondersAvailableException");
21
- const NatsInternalServerException_1 = require("../exceptions/NatsInternalServerException");
22
- const NatsBulkException_1 = require("../exceptions/NatsBulkException");
23
- const ServiceEventCodec_1 = require("../lib/ServiceEventCodec");
24
- const MessagePackCodec_1 = require("../codecs/MessagePackCodec");
25
- const node_crypto_1 = require("node:crypto");
26
- // bulk 中转协议头(放 header,不污染 payload)
27
- const HDR_BULK = 'X-Lkt-Bulk'; // 能力声明:本端支持 bulk 引用中转
28
- const HDR_REF = 'X-Lkt-Ref'; // 标记:payload 是 Object Store 引用 {__ref}
29
- const buildNatsClientOptions = (options) => {
30
- return {
31
- class: NATS,
32
- servers: options.servers,
33
- codec: options.codec,
34
- timeout: options.timeout,
35
- token: options.token,
36
- user: options.user,
37
- pass: options.pass,
38
- debug: options.debug,
39
- maxPingOut: options.maxPingOut,
40
- maxReconnectAttempts: options.maxReconnectAttempts,
41
- name: options.name,
42
- noEcho: options.noEcho,
43
- noRandomize: options.noRandomize,
44
- pingInterval: options.pingInterval,
45
- reconnect: options.reconnect,
46
- bulk: options.bulk,
47
- bulkBucket: options.bulkBucket,
48
- bulkTTL: options.bulkTTL,
49
- bulkReplicas: options.bulkReplicas
50
- };
51
- };
52
- exports.buildNatsClientOptions = buildNatsClientOptions;
53
- class NATS extends lakutata_1.Component {
54
- constructor() {
55
- super(...arguments);
56
- /**
57
- * Service event subscription map
58
- * @protected
59
- */
60
- this.serviceEventSubscriptionMap = new Map();
61
- /**
62
- * 缓存对端(按 subject)是否支持 bulk 中转,从对方响应/请求的能力声明 header 学到。
63
- * 用于请求侧判断对端:已知支持才发引用,未知/不支持则直传(向后兼容)。
64
- * @private
65
- */
66
- this.#peerCapabilityCache = new Map();
67
- }
68
- /**
69
- * NATS client instance
70
- * @private
71
- */
72
- #conn;
73
- /**
74
- * bulk 连接与 Object Store(仅 bulk 启用时存在)
75
- * @private
76
- */
77
- #bulkConn;
78
- #objectStore;
79
- /**
80
- * 缓存对端(按 subject)是否支持 bulk 中转,从对方响应/请求的能力声明 header 学到。
81
- * 用于请求侧判断对端:已知支持才发引用,未知/不支持则直传(向后兼容)。
82
- * @private
83
- */
84
- #peerCapabilityCache;
85
- /**
86
- * Initializer
87
- * @protected
88
- */
89
- async init() {
90
- const connectOptions = {
91
- servers: this.servers,
92
- user: this.user,
93
- pass: this.pass,
94
- timeout: this.timeout,
95
- token: this.token,
96
- debug: this.debug,
97
- maxPingOut: this.maxPingOut,
98
- maxReconnectAttempts: this.maxReconnectAttempts,
99
- name: this.name,
100
- noEcho: this.noEcho,
101
- noRandomize: this.noRandomize,
102
- pingInterval: this.pingInterval,
103
- reconnect: this.reconnect
104
- };
105
- this.#conn = await (0, nats_1.connect)(connectOptions);
106
- if (this.bulk) {
107
- try {
108
- // 用同一套配置再开一条【独立】连接专供大数据,避免大对象传输堵塞 core RPC 连接;
109
- // 并 get-or-create 一个 Object Store bucket 做大对象中转(短 TTL 自动清理)。
110
- this.#bulkConn = await (0, nats_1.connect)({
111
- ...connectOptions,
112
- name: this.name ? `${this.name}-bulk` : undefined
113
- });
114
- const bucket = this.bulkBucket ?? `lkt-bulk-${this.name ?? 'default'}`;
115
- // 仅在用户【显式】配置了 bulkReplicas 时才下发 replicas;默认(undefined)不发该字段,
116
- // 让 server 用默认单副本。无条件发 replicas 在严格校验的 NATS server 上会被
117
- // 拒绝(invalid json: unknown field "replicas"),且默认单副本显式下发本就没有收益。
118
- const osOptions = {
119
- storage: nats_1.StorageType.File,
120
- ttl: (0, nats_1.nanos)(this.bulkTTL ?? 5 * 60 * 1000)
121
- };
122
- if (this.bulkReplicas !== undefined)
123
- osOptions.replicas = this.bulkReplicas;
124
- this.#objectStore = await this.#bulkConn.jetstream().views.os(bucket, osOptions);
125
- }
126
- catch (e) {
127
- // 优雅降级:bulk 初始化失败(JetStream 不可用 / 创建被拒 / 超时等)绝不阻塞启动。
128
- // 清理半建连接并禁用 bulk(bulkEnabled 依赖 #objectStore → 自动转 false),warn 后继续,
129
- // 核心 RPC/事件照常工作,仅大数据中转不可用。
130
- this.#objectStore = undefined;
131
- if (this.#bulkConn) {
132
- try {
133
- await this.#bulkConn.close();
134
- }
135
- catch {
136
- // 忽略关闭错误
137
- }
138
- this.#bulkConn = undefined;
139
- }
140
- console.warn(`[NATS] bulk 旁路初始化失败,已自动禁用 bulk(核心 RPC/事件不受影响,仅大数据中转不可用): ${e?.message ?? e}`);
141
- }
142
- }
143
- }
144
- /**
145
- * Destroyer
146
- * @protected
147
- */
148
- async destroy() {
149
- await this.drainConnection(this.#conn);
150
- if (this.#bulkConn)
151
- await this.drainConnection(this.#bulkConn);
152
- }
153
- /**
154
- * 优雅停机:drain 停止接收新消息、把 in-flight 处理完、flush 出站后再关闭,
155
- * 这样 K8s 滚动升级/缩容时正在处理的请求不被硬切断;drain 失败兜底强制关闭。
156
- * @param conn
157
- * @private
158
- */
159
- async drainConnection(conn) {
160
- try {
161
- await conn.drain();
162
- }
163
- catch {
164
- try {
165
- await conn.close();
166
- }
167
- catch {
168
- // 连接已不可用,忽略
169
- }
170
- }
171
- }
172
- /**
173
- * Publishes the specified data to the specified subject.
174
- * @param subject
175
- * @param payload
176
- */
177
- publish(subject, payload) {
178
- return this.#conn.publish(subject, this.codec.encode(payload));
179
- }
180
- /**
181
- * Publishes a request with specified data in the specified subject expecting a
182
- * response before timeout milliseconds. The api returns a
183
- * Promise that resolves when the first response to the request is received. If
184
- * there are no responders (a subscription) listening on the request subject,
185
- * the request will fail as soon as the server processes it
186
- * @param subject
187
- * @param payload
188
- * @param timeout
189
- */
190
- async request(subject, payload, timeout) {
191
- const reqHeaders = (0, nats_1.headers)();
192
- if (this.bulkEnabled)
193
- reqHeaders.set(HDR_BULK, '1'); // 仅本端启用 bulk 时声明能力
194
- const peerSupports = this.#peerCapabilityCache.get(subject) === true;
195
- const packed = await this.packPayload(payload, peerSupports);
196
- if (packed.isRef)
197
- reqHeaders.set(HDR_REF, '1');
198
- try {
199
- const response = await this.#conn.request(subject, packed.bytes, { timeout: timeout ? timeout : 0, headers: reqHeaders });
200
- // 双向协商:从响应学习对端是否支持中转
201
- this.#peerCapabilityCache.set(subject, response.headers?.get(HDR_BULK) === '1');
202
- // 还原响应(可能是 Object Store 引用)
203
- return await this.unpackPayload(response.data, response.headers?.get(HDR_REF) === '1');
204
- }
205
- catch (e) {
206
- if (e.code) {
207
- switch (e.code.toString()) {
208
- case '400':
209
- throw new NatsBadRequestException_1.NatsBadRequestException();
210
- case '403':
211
- throw new NatsForbiddenException_1.NatsForbiddenException();
212
- case '404':
213
- throw new NatsNotFoundException_1.NatsNotFoundException();
214
- case '408':
215
- case 'TIMEOUT':
216
- throw new NatsRequestTimeoutException_1.NatsRequestTimeoutException();
217
- case '503':
218
- throw new NatsNoRespondersAvailableException_1.NatsNoRespondersAvailableException();
219
- case '500':
220
- throw new NatsInternalServerException_1.NatsInternalServerException();
221
- default:
222
- throw e;
223
- }
224
- }
225
- else {
226
- throw e;
227
- }
228
- }
229
- finally {
230
- // 请求引用对象:服务端已取回(响应已返回),删除;失败靠 TTL 兜底
231
- if (packed.objId)
232
- this.deleteBulk(packed.objId).catch(() => undefined);
233
- }
234
- }
235
- /**
236
- * Subscribe expresses interest in the specified subject. The subject may
237
- * have wildcards. Messages are delivered to the callback.
238
- * If the subscription receives request, the callback return's value will respond to requester
239
- * @param subject
240
- * @param callback receives (data, subject) — `subject` is the concrete subject the message
241
- * arrived on, letting wildcard subscribers read routing tokens (e.g. an id embedded in the
242
- * subject) without the publisher having to duplicate them into the payload.
243
- * @param subscribeOptions
244
- */
245
- subscribe(subject, callback, subscribeOptions) {
246
- const subscription = this.#conn.subscribe(subject, {
247
- queue: subscribeOptions?.queue,
248
- max: subscribeOptions?.max
249
- });
250
- const handleMessage = async (msg) => {
251
- try {
252
- // 入站:若标记为引用,从 Object Store 取回真实 payload(对小消息/event 透明)
253
- const data = await this.unpackPayload(msg.data, msg.headers?.get(HDR_REF) === '1');
254
- const result = await callback(data, msg.subject);
255
- // 仅当消息是请求(带 reply)时才回响应;publish/event 无 reply,跳过。
256
- if (msg.reply) {
257
- const respHeaders = (0, nats_1.headers)();
258
- if (this.bulkEnabled)
259
- respHeaders.set(HDR_BULK, '1'); // 仅本端启用 bulk 时声明能力
260
- // 请求方是否支持中转 → 决定大响应能否走引用
261
- const clientSupports = msg.headers?.get(HDR_BULK) === '1';
262
- const packed = await this.packPayload(result, clientSupports);
263
- if (packed.isRef)
264
- respHeaders.set(HDR_REF, '1');
265
- msg.respond(packed.bytes, { headers: respHeaders });
266
- }
267
- }
268
- catch (e) {
269
- this.emitError(e);
270
- // 若是请求且上层提供了错误响应工厂,回标准失败响应(直传 + 能力声明),避免请求方挂到超时。
271
- if (msg.reply && subscribeOptions?.errorResponse) {
272
- try {
273
- const errHeaders = (0, nats_1.headers)();
274
- if (this.bulkEnabled)
275
- errHeaders.set(HDR_BULK, '1');
276
- const errorPayload = subscribeOptions.errorResponse(e);
277
- if (errorPayload !== undefined)
278
- msg.respond(this.codec.encode(errorPayload), { headers: errHeaders });
279
- }
280
- catch (encodeError) {
281
- this.emitError(encodeError);
282
- }
283
- }
284
- }
285
- };
286
- if (subscribeOptions?.iterator) {
287
- setImmediate(async () => {
288
- for await (const msg of subscription) {
289
- await handleMessage(msg);
290
- }
291
- });
292
- }
293
- else {
294
- subscription.callback = async (err, msg) => {
295
- // nats.js 在订阅级错误时以 (err, {}) 回调,此时 msg 为空对象无法处理,上报后返回。
296
- if (err) {
297
- this.emitError(err);
298
- return;
299
- }
300
- await handleMessage(msg);
301
- };
302
- }
303
- return subscription;
304
- }
305
- /**
306
- * 安全地发出 error 事件:仅在存在监听器时发出。
307
- * Node EventEmitter 在没有 'error' 监听器时 emit('error') 会抛出,
308
- * 在异步回调里那会变成 unhandled rejection,所以这里加监听器守卫。
309
- * @param error
310
- * @protected
311
- */
312
- emitError(error) {
313
- if (this.listenerCount('error') > 0)
314
- this.emit('error', error);
315
- }
316
- /**
317
- * 是否启用了 bulk 旁路(连接 + Object Store 均就绪)
318
- */
319
- get bulkEnabled() {
320
- return !!this.bulk && !!this.#objectStore;
321
- }
322
- /**
323
- * 走 bulk 的字节阈值 = server max_payload × 0.9(从连接 info 自动读,跟随集群配置)
324
- */
325
- get bulkThreshold() {
326
- const maxPayload = this.#conn.info?.max_payload ?? 1024 * 1024;
327
- return Math.floor(maxPayload * 0.9);
328
- }
329
- /**
330
- * server 是否支持消息 header(从连接 info)
331
- */
332
- get headersSupported() {
333
- return this.#conn.info?.headers === true;
334
- }
335
- /**
336
- * 把一块字节存入 Object Store,返回 objId(供引用中转)。
337
- * @param bytes
338
- */
339
- async putBulk(bytes) {
340
- if (!this.#objectStore)
341
- throw new NatsBulkException_1.NatsBulkException('bulk channel is not enabled');
342
- const objId = (0, node_crypto_1.randomUUID)();
343
- try {
344
- await this.#objectStore.putBlob({ name: objId }, bytes);
345
- }
346
- catch (e) {
347
- throw new NatsBulkException_1.NatsBulkException(`bulk put failed: ${e?.message ?? e}`);
348
- }
349
- return objId;
350
- }
351
- /**
352
- * 从 Object Store 取回一块字节。
353
- * @param objId
354
- */
355
- async getBulk(objId) {
356
- if (!this.#objectStore)
357
- throw new NatsBulkException_1.NatsBulkException('bulk channel is not enabled');
358
- let data;
359
- try {
360
- data = await this.#objectStore.getBlob(objId);
361
- }
362
- catch (e) {
363
- throw new NatsBulkException_1.NatsBulkException(`bulk get failed: ${e?.message ?? e}`);
364
- }
365
- if (data === null)
366
- throw new NatsBulkException_1.NatsBulkException(`bulk object not found: ${objId}`);
367
- return data;
368
- }
369
- /**
370
- * 删除 Object Store 中的对象(用完即清,失败由 TTL 兜底)。
371
- * @param objId
372
- */
373
- async deleteBulk(objId) {
374
- if (!this.#objectStore)
375
- return;
376
- await this.#objectStore.delete(objId);
377
- }
378
- /**
379
- * 返回 bulk Object Store 的运行时状态(bucket/ttl/replicas/storage/size 等);未启用 bulk 返回 undefined。
380
- */
381
- async bulkStatus() {
382
- return this.#objectStore?.status();
383
- }
384
- /**
385
- * 打包待发送 payload:编码后若对端支持中转且超阈值,存入 Object Store 改发小引用。
386
- * @param payload
387
- * @param peerSupportsBulk 对端是否支持引用中转(不支持只能直传)
388
- * @private
389
- */
390
- async packPayload(payload, peerSupportsBulk) {
391
- const bytes = this.codec.encode(payload);
392
- if (peerSupportsBulk && this.bulkEnabled && bytes.length >= this.bulkThreshold) {
393
- const objId = await this.putBulk(bytes);
394
- return { bytes: this.codec.encode({ __ref: objId }), isRef: true, objId };
395
- }
396
- return { bytes, isRef: false };
397
- }
398
- /**
399
- * 解包收到的字节:若是引用,从 Object Store 取回真实字节再解码(取回后即删,失败靠 TTL 兜底)。
400
- * @param bytes
401
- * @param isRef
402
- * @private
403
- */
404
- async unpackPayload(bytes, isRef) {
405
- if (isRef) {
406
- const ref = this.codec.decode(bytes);
407
- const realBytes = await this.getBulk(ref.__ref);
408
- this.deleteBulk(ref.__ref).catch(() => undefined);
409
- return this.codec.decode(realBytes);
410
- }
411
- return this.codec.decode(bytes);
412
- }
413
- /**
414
- * Emit service event
415
- * @param eventName
416
- * @param args
417
- */
418
- emitServiceEvent(eventName, ...args) {
419
- const eventSubject = ServiceEventCodec_1.ServiceEventCodec.formatSubject(this.app.appId, eventName);
420
- this.publish(eventSubject, ServiceEventCodec_1.ServiceEventCodec.encode(args));
421
- return this;
422
- }
423
- /**
424
- * On service event
425
- * @param serviceId
426
- * @param eventName
427
- * @param listener
428
- * @param onlySingleClientReceived
429
- */
430
- onServiceEvent(serviceId, eventName, listener, onlySingleClientReceived = false) {
431
- const eventSubject = ServiceEventCodec_1.ServiceEventCodec.formatSubject(serviceId, eventName);
432
- if (!this.serviceEventSubscriptionMap.has(eventSubject))
433
- this.serviceEventSubscriptionMap.set(eventSubject, new Map());
434
- if (!this.serviceEventSubscriptionMap.get(eventSubject)?.has(listener)) {
435
- const subscription = this.subscribe(eventSubject, (eventData) => {
436
- listener(...ServiceEventCodec_1.ServiceEventCodec.decode(eventData));
437
- }, onlySingleClientReceived ? { queue: eventSubject } : void (0));
438
- this.serviceEventSubscriptionMap.get(eventSubject)?.set(listener, subscription);
439
- }
440
- return this;
441
- }
442
- /**
443
- * Once service event
444
- * @param serviceId
445
- * @param eventName
446
- * @param listener
447
- */
448
- onceServiceEvent(serviceId, eventName, listener) {
449
- const eventSubject = ServiceEventCodec_1.ServiceEventCodec.formatSubject(serviceId, eventName);
450
- if (!this.serviceEventSubscriptionMap.has(eventSubject))
451
- this.serviceEventSubscriptionMap.set(eventSubject, new Map());
452
- if (!this.serviceEventSubscriptionMap.get(eventSubject)?.has(listener)) {
453
- const subscription = this.subscribe(eventSubject, (eventData) => {
454
- this.serviceEventSubscriptionMap.get(eventSubject)?.delete(listener);
455
- listener(...ServiceEventCodec_1.ServiceEventCodec.decode(eventData));
456
- }, { max: 1 });
457
- this.serviceEventSubscriptionMap.get(eventSubject)?.set(listener, subscription);
458
- }
459
- return this;
460
- }
461
- /**
462
- * Off server events
463
- * @param serviceId
464
- * @param eventName
465
- * @param listener
466
- */
467
- offServiceEvent(serviceId, eventName, listener) {
468
- const eventSubject = ServiceEventCodec_1.ServiceEventCodec.formatSubject(serviceId, eventName);
469
- if (listener) {
470
- this.serviceEventSubscriptionMap.get(eventSubject)?.get(listener)?.unsubscribe();
471
- this.serviceEventSubscriptionMap.get(eventSubject)?.delete(listener);
472
- }
473
- else {
474
- this.serviceEventSubscriptionMap.get(eventSubject)?.forEach((subscription) => subscription.unsubscribe());
475
- this.serviceEventSubscriptionMap.delete(eventSubject);
476
- }
477
- return this;
478
- }
479
- }
480
- exports.NATS = NATS;
481
- __decorate([
482
- (0, di_1.Inject)(lakutata_1.Application),
483
- __metadata("design:type", lakutata_1.Application
484
- /**
485
- * NATS servers
486
- * @protected
487
- */
488
- )
489
- ], NATS.prototype, "app", void 0);
490
- __decorate([
491
- (0, di_1.Configurable)(lakutata_1.DTO.Alternatives(lakutata_1.DTO.String(), lakutata_1.DTO.Array(lakutata_1.DTO.String())).required()),
492
- __metadata("design:type", Object)
493
- ], NATS.prototype, "servers", void 0);
494
- __decorate([
495
- (0, di_1.Configurable)(lakutata_1.DTO.Object({
496
- encode: lakutata_1.DTO.Function().arity(1).required(),
497
- decode: lakutata_1.DTO.Function().arity(1).required()
498
- }).optional().default((0, MessagePackCodec_1.MessagePackCodec)())),
499
- __metadata("design:type", Object)
500
- ], NATS.prototype, "codec", void 0);
501
- __decorate([
502
- (0, di_1.Configurable)(lakutata_1.DTO.Number().integer().optional()),
503
- __metadata("design:type", Number)
504
- ], NATS.prototype, "timeout", void 0);
505
- __decorate([
506
- (0, di_1.Configurable)(lakutata_1.DTO.String().optional()),
507
- __metadata("design:type", String)
508
- ], NATS.prototype, "token", void 0);
509
- __decorate([
510
- (0, di_1.Configurable)(lakutata_1.DTO.String().optional()),
511
- __metadata("design:type", String)
512
- ], NATS.prototype, "user", void 0);
513
- __decorate([
514
- (0, di_1.Configurable)(lakutata_1.DTO.String().optional()),
515
- __metadata("design:type", String)
516
- ], NATS.prototype, "pass", void 0);
517
- __decorate([
518
- (0, di_1.Configurable)(lakutata_1.DTO.Boolean().optional()),
519
- __metadata("design:type", Boolean)
520
- ], NATS.prototype, "debug", void 0);
521
- __decorate([
522
- (0, di_1.Configurable)(lakutata_1.DTO.Number().optional()),
523
- __metadata("design:type", Number)
524
- ], NATS.prototype, "maxPingOut", void 0);
525
- __decorate([
526
- (0, di_1.Configurable)(lakutata_1.DTO.Number().optional().default(10)),
527
- __metadata("design:type", Number)
528
- ], NATS.prototype, "maxReconnectAttempts", void 0);
529
- __decorate([
530
- (0, di_1.Configurable)(lakutata_1.DTO.String().optional()),
531
- __metadata("design:type", String)
532
- ], NATS.prototype, "name", void 0);
533
- __decorate([
534
- (0, di_1.Configurable)(lakutata_1.DTO.Boolean().optional()),
535
- __metadata("design:type", Boolean)
536
- ], NATS.prototype, "noEcho", void 0);
537
- __decorate([
538
- (0, di_1.Configurable)(lakutata_1.DTO.Boolean().optional()),
539
- __metadata("design:type", Boolean)
540
- ], NATS.prototype, "noRandomize", void 0);
541
- __decorate([
542
- (0, di_1.Configurable)(lakutata_1.DTO.Number().optional().default(1000 * 60 * 2)),
543
- __metadata("design:type", Number)
544
- ], NATS.prototype, "pingInterval", void 0);
545
- __decorate([
546
- (0, di_1.Configurable)(lakutata_1.DTO.Boolean().optional().default(true)),
547
- __metadata("design:type", Boolean)
548
- ], NATS.prototype, "reconnect", void 0);
549
- __decorate([
550
- (0, di_1.Configurable)(lakutata_1.DTO.Boolean().optional().default(true)),
551
- __metadata("design:type", Boolean)
552
- ], NATS.prototype, "bulk", void 0);
553
- __decorate([
554
- (0, di_1.Configurable)(lakutata_1.DTO.String().optional()),
555
- __metadata("design:type", String)
556
- ], NATS.prototype, "bulkBucket", void 0);
557
- __decorate([
558
- (0, di_1.Configurable)(lakutata_1.DTO.Number().integer().optional().default(5 * 60 * 1000)),
559
- __metadata("design:type", Number)
560
- ], NATS.prototype, "bulkTTL", void 0);
561
- __decorate([
562
- (0, di_1.Configurable)(lakutata_1.DTO.Number().integer().optional()),
563
- __metadata("design:type", Number)
564
- ], NATS.prototype, "bulkReplicas", void 0);
@@ -1,8 +0,0 @@
1
- import { ServiceEntrypoint } from 'lakutata/com/entrypoint';
2
- /**
3
- * Setup service based on NATS
4
- * @param natsComponentName
5
- * @constructor
6
- */
7
- export declare function SetupNatsServiceEntrypoint(natsComponentName: string): ServiceEntrypoint;
8
- //# sourceMappingURL=SetupNatsServiceEntrypoint.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SetupNatsServiceEntrypoint.d.ts","sourceRoot":"","sources":["../../src/entrypoints/SetupNatsServiceEntrypoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,iBAAiB,EAEpB,MAAM,yBAAyB,CAAA;AAehC;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,iBAAiB,EAAE,MAAM,GAAG,iBAAiB,CAmBvF"}
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SetupNatsServiceEntrypoint = void 0;
4
- const entrypoint_1 = require("lakutata/com/entrypoint");
5
- const ServiceResponseCodec_1 = require("../lib/ServiceResponseCodec");
6
- /**
7
- * 把异常拷成可序列化的普通对象(保留 message/errno/statusCode 等自有属性)。
8
- * @param error
9
- */
10
- function toErrorObject(error) {
11
- const errorObject = {};
12
- Object.getOwnPropertyNames(error).forEach((prop) => errorObject[prop] = error[prop]);
13
- return errorObject;
14
- }
15
- /**
16
- * Setup service based on NATS
17
- * @param natsComponentName
18
- * @constructor
19
- */
20
- function SetupNatsServiceEntrypoint(natsComponentName) {
21
- return (0, entrypoint_1.BuildServiceEntrypoint)(async (module, handler) => {
22
- const nats = await module.getObject(natsComponentName);
23
- const appId = module.appId;
24
- nats.subscribe(appId, async (incomeRequestPayload) => {
25
- try {
26
- return ServiceResponseCodec_1.ServiceResponseCodec.encode(await handler(new entrypoint_1.ServiceContext({
27
- data: incomeRequestPayload
28
- })), false);
29
- }
30
- catch (e) {
31
- return ServiceResponseCodec_1.ServiceResponseCodec.encode(toErrorObject(e), true);
32
- }
33
- }, {
34
- queue: `${appId}.serviceQueue`,
35
- // 入站 decode 失败 / 出站 encode 失败等发生在 handler 之外(subscribe 层)的错误,
36
- // 由此工厂统一回成标准失败响应,避免请求方挂到超时。
37
- errorResponse: (e) => ServiceResponseCodec_1.ServiceResponseCodec.encode(toErrorObject(e), true)
38
- });
39
- });
40
- }
41
- exports.SetupNatsServiceEntrypoint = SetupNatsServiceEntrypoint;
@@ -1,7 +0,0 @@
1
- import { Exception } from 'lakutata';
2
- export declare class NatsBadRequestException extends Exception {
3
- errno: string | number;
4
- message: string;
5
- statusCode: number;
6
- }
7
- //# sourceMappingURL=NatsBadRequestException.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NatsBadRequestException.d.ts","sourceRoot":"","sources":["../../src/exceptions/NatsBadRequestException.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,UAAU,CAAA;AAElC,qBAAa,uBAAwB,SAAQ,SAAS;IAC3C,KAAK,EAAE,MAAM,GAAG,MAAM,CAAuB;IAC7C,OAAO,EAAE,MAAM,CAAgB;IAC/B,UAAU,EAAE,MAAM,CAAM;CAClC"}
@@ -1,13 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NatsBadRequestException = void 0;
4
- const lakutata_1 = require("lakutata");
5
- class NatsBadRequestException extends lakutata_1.Exception {
6
- constructor() {
7
- super(...arguments);
8
- this.errno = 'E_NATS_BAD_REQUEST';
9
- this.message = 'Bad Request';
10
- this.statusCode = 400;
11
- }
12
- }
13
- exports.NatsBadRequestException = NatsBadRequestException;
@@ -1,11 +0,0 @@
1
- import { Exception } from 'lakutata';
2
- /**
3
- * bulk 中转层故障(Object Store put/get 失败、引用过期取不到、bulk 未启用等)。
4
- * 表示"大数据通道出问题",区别于业务错误;不应自动重试(响应取不到时对侧可能已处理)。
5
- * message 由抛出处给出具体原因(不设默认值,以保留原因)。
6
- */
7
- export declare class NatsBulkException extends Exception {
8
- errno: string | number;
9
- statusCode: number;
10
- }
11
- //# sourceMappingURL=NatsBulkException.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NatsBulkException.d.ts","sourceRoot":"","sources":["../../src/exceptions/NatsBulkException.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,UAAU,CAAA;AAElC;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IACrC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAgB;IACtC,UAAU,EAAE,MAAM,CAAM;CAClC"}
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NatsBulkException = void 0;
4
- const lakutata_1 = require("lakutata");
5
- /**
6
- * bulk 中转层故障(Object Store put/get 失败、引用过期取不到、bulk 未启用等)。
7
- * 表示"大数据通道出问题",区别于业务错误;不应自动重试(响应取不到时对侧可能已处理)。
8
- * message 由抛出处给出具体原因(不设默认值,以保留原因)。
9
- */
10
- class NatsBulkException extends lakutata_1.Exception {
11
- constructor() {
12
- super(...arguments);
13
- this.errno = 'E_NATS_BULK';
14
- this.statusCode = 502;
15
- }
16
- }
17
- exports.NatsBulkException = NatsBulkException;