youeduc-sdk-messaging 0.0.0 → 0.5.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.
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/admin.d.ts +61 -0
- package/dist/admin.js +143 -0
- package/dist/admin.js.map +1 -0
- package/dist/config.d.ts +74 -0
- package/dist/config.js +128 -0
- package/dist/config.js.map +1 -0
- package/dist/consumer-client.d.ts +155 -0
- package/dist/consumer-client.js +299 -0
- package/dist/consumer-client.js.map +1 -0
- package/dist/consumer.d.ts +156 -0
- package/dist/consumer.js +1228 -0
- package/dist/consumer.js.map +1 -0
- package/dist/contracts/index.d.ts +16 -0
- package/dist/contracts/index.js +42 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/schemas.d.ts +570 -0
- package/dist/contracts/schemas.js +902 -0
- package/dist/contracts/schemas.js.map +1 -0
- package/dist/contracts/usuarios.d.ts +163 -0
- package/dist/contracts/usuarios.js +5 -0
- package/dist/contracts/usuarios.js.map +1 -0
- package/dist/dedup.d.ts +29 -0
- package/dist/dedup.js +65 -0
- package/dist/dedup.js.map +1 -0
- package/dist/dlq.d.ts +161 -0
- package/dist/dlq.js +341 -0
- package/dist/dlq.js.map +1 -0
- package/dist/errors.d.ts +72 -0
- package/dist/errors.js +95 -0
- package/dist/errors.js.map +1 -0
- package/dist/headers.d.ts +92 -0
- package/dist/headers.js +144 -0
- package/dist/headers.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/redis.d.ts +118 -0
- package/dist/integrations/redis.js +272 -0
- package/dist/integrations/redis.js.map +1 -0
- package/dist/integrations/runtime.d.ts +232 -0
- package/dist/integrations/runtime.js +554 -0
- package/dist/integrations/runtime.js.map +1 -0
- package/dist/logger.d.ts +37 -0
- package/dist/logger.js +49 -0
- package/dist/logger.js.map +1 -0
- package/dist/messaging.d.ts +145 -0
- package/dist/messaging.js +164 -0
- package/dist/messaging.js.map +1 -0
- package/dist/naming.d.ts +64 -0
- package/dist/naming.js +167 -0
- package/dist/naming.js.map +1 -0
- package/dist/payload.d.ts +8 -0
- package/dist/payload.js +32 -0
- package/dist/payload.js.map +1 -0
- package/dist/publisher.d.ts +50 -0
- package/dist/publisher.js +199 -0
- package/dist/publisher.js.map +1 -0
- package/dist/retry.d.ts +24 -0
- package/dist/retry.js +42 -0
- package/dist/retry.js.map +1 -0
- package/dist/schema.d.ts +83 -0
- package/dist/schema.js +676 -0
- package/dist/schema.js.map +1 -0
- package/dist/telemetry.d.ts +52 -0
- package/dist/telemetry.js +221 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/transport.d.ts +130 -0
- package/dist/transport.js +283 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.js +35 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +38 -0
- package/dist/util.js +112 -0
- package/dist/util.js.map +1 -0
- package/dist/w3c.d.ts +16 -0
- package/dist/w3c.js +119 -0
- package/dist/w3c.js.map +1 -0
- package/package.json +74 -1
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic runtime of the SDK (Python `integrations/runtime.py`, without the sync
|
|
3
|
+
* bridge: Node is async-native).
|
|
4
|
+
*
|
|
5
|
+
* - {@link MessagingSettings} — canonical configuration (`SDK_MESSAGING`, spec §14), the same
|
|
6
|
+
* mapping used by the Python SDK (Django `settings.SDK_MESSAGING`, Flask `app.config`).
|
|
7
|
+
* - {@link buildMessaging} / {@link buildDedupStore} — facade and shared dedup store from it.
|
|
8
|
+
* - {@link serveConsumers} / {@link runConsumers} — consumer process for CLI entry points
|
|
9
|
+
* (signals, draining, exit code).
|
|
10
|
+
*
|
|
11
|
+
* Redis is loaded only by {@link buildDedupStore} (dynamic `import('./redis.js')`), so `redis`
|
|
12
|
+
* stays an optional peer dependency.
|
|
13
|
+
*/
|
|
14
|
+
import { statSync } from 'node:fs';
|
|
15
|
+
import { inspect } from 'node:util';
|
|
16
|
+
import { isMainThread } from 'node:worker_threads';
|
|
17
|
+
import { KafkaConfig } from '../config.js';
|
|
18
|
+
import { schemaLoader as bundledSchemaLoader } from '../contracts/index.js';
|
|
19
|
+
import { defaultLogger } from '../logger.js';
|
|
20
|
+
import { Messaging } from '../messaging.js';
|
|
21
|
+
import * as naming from '../naming.js';
|
|
22
|
+
import { ChainSchemaLoader, FileSystemSchemaLoader } from '../schema.js';
|
|
23
|
+
import { OtelTelemetry } from '../telemetry.js';
|
|
24
|
+
import { errorFields, isPlainObject, pyRepr } from '../util.js';
|
|
25
|
+
// =========================================================================== settings
|
|
26
|
+
const REQUIRED_KEYS = ['SERVICE', 'ENVIRONMENT'];
|
|
27
|
+
const OPTIONAL_KEYS = [
|
|
28
|
+
'SCHEMAS_DIR',
|
|
29
|
+
'KAFKA',
|
|
30
|
+
'SEND_TIMEOUT_S',
|
|
31
|
+
'PUBLISH_TIMEOUT_S',
|
|
32
|
+
'DRAIN_TIMEOUT_S',
|
|
33
|
+
'TENANT_METRIC_LABEL',
|
|
34
|
+
'REDIS',
|
|
35
|
+
'RESOURCE_PREFIX',
|
|
36
|
+
];
|
|
37
|
+
const KNOWN_KEYS = [...REQUIRED_KEYS, ...OPTIONAL_KEYS];
|
|
38
|
+
const REDIS_REQUIRED_KEYS = ['URL', 'NAMESPACE'];
|
|
39
|
+
const REDIS_KNOWN_KEYS = [...REDIS_REQUIRED_KEYS, 'TTL_S', 'TIMEOUT_S'];
|
|
40
|
+
/**
|
|
41
|
+
* Keys of the `KAFKA` mapping: the canonical (snake_case) field names of `KafkaConfig`, identical
|
|
42
|
+
* in both SDKs (spec §14) so one `SDK_MESSAGING` document configures either of them.
|
|
43
|
+
*/
|
|
44
|
+
const KAFKA_KEYS = [
|
|
45
|
+
'bootstrap_servers',
|
|
46
|
+
'client_id',
|
|
47
|
+
'security_protocol',
|
|
48
|
+
'sasl_mechanism',
|
|
49
|
+
'sasl_username',
|
|
50
|
+
'sasl_password',
|
|
51
|
+
];
|
|
52
|
+
// Below gunicorn's default --timeout (30 s) on the Python side; same canonical defaults here.
|
|
53
|
+
/** Default `SEND_TIMEOUT_S` (20 s), in ms. */
|
|
54
|
+
export const DEFAULT_SEND_TIMEOUT_MS = 20_000;
|
|
55
|
+
/** Default `PUBLISH_TIMEOUT_S` (25 s), in ms. */
|
|
56
|
+
export const DEFAULT_PUBLISH_TIMEOUT_MS = 25_000;
|
|
57
|
+
/** Default `DRAIN_TIMEOUT_S` (30 s), in ms. */
|
|
58
|
+
export const DEFAULT_DRAIN_TIMEOUT_MS = 30_000;
|
|
59
|
+
/** Default `REDIS.TTL_S` (7 days), in ms. */
|
|
60
|
+
export const DEFAULT_REDIS_TTL_MS = 604_800_000;
|
|
61
|
+
/** Default `REDIS.TIMEOUT_S` (5 s), in ms. */
|
|
62
|
+
export const DEFAULT_REDIS_TIMEOUT_MS = 5000;
|
|
63
|
+
/** `REDIS` block of {@link MessagingSettings} (shared dedup). Durations in ms. */
|
|
64
|
+
export class RedisSettings {
|
|
65
|
+
// Private: the URL may carry a password (redis://:secret@host); kept out of JSON/inspect.
|
|
66
|
+
#url;
|
|
67
|
+
namespace;
|
|
68
|
+
ttlMs;
|
|
69
|
+
timeoutMs;
|
|
70
|
+
constructor(init) {
|
|
71
|
+
this.#url = init.url;
|
|
72
|
+
this.namespace = init.namespace;
|
|
73
|
+
this.ttlMs = init.ttlMs ?? DEFAULT_REDIS_TTL_MS;
|
|
74
|
+
this.timeoutMs = init.timeoutMs ?? DEFAULT_REDIS_TIMEOUT_MS;
|
|
75
|
+
Object.freeze(this);
|
|
76
|
+
}
|
|
77
|
+
get url() {
|
|
78
|
+
return this.#url;
|
|
79
|
+
}
|
|
80
|
+
toJSON() {
|
|
81
|
+
return { namespace: this.namespace, ttlMs: this.ttlMs, timeoutMs: this.timeoutMs };
|
|
82
|
+
}
|
|
83
|
+
[inspect.custom]() {
|
|
84
|
+
return `RedisSettings ${inspect(this.toJSON())}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validated canonical configuration. Build it with {@link MessagingSettings.fromMapping} from the
|
|
89
|
+
* `SDK_MESSAGING` object:
|
|
90
|
+
*
|
|
91
|
+
* ```ts
|
|
92
|
+
* const settings = MessagingSettings.fromMapping({
|
|
93
|
+
* SERVICE: 'enrollment-api', // required (kebab-case)
|
|
94
|
+
* ENVIRONMENT: 'production', // required
|
|
95
|
+
* SCHEMAS_DIR: './schemas', // optional: null/absent → bundled contracts only
|
|
96
|
+
* KAFKA: null, // null → KafkaConfig.fromEnv() (KAFKA_BOOTSTRAP_SERVERS...)
|
|
97
|
+
* SEND_TIMEOUT_S: 20, // broker ack wait
|
|
98
|
+
* PUBLISH_TIMEOUT_S: 25, // caller-side publish bound (> SEND_TIMEOUT_S)
|
|
99
|
+
* DRAIN_TIMEOUT_S: 30, // draining on shutdown
|
|
100
|
+
* TENANT_METRIC_LABEL: true,
|
|
101
|
+
* REDIS: null, // or { URL, NAMESPACE, TTL_S, TIMEOUT_S }
|
|
102
|
+
* RESOURCE_PREFIX: null, // e.g. 'hmg'/'prd' → hmg.<topic>, hmg.<group> (spec §2.7)
|
|
103
|
+
* });
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* Units: the mapping is the canonical document shared with the Python SDK, so its durations are
|
|
107
|
+
* in **seconds** (`*_S`); the settings object exposes them in **milliseconds** (`sendTimeoutMs`,
|
|
108
|
+
* `publishTimeoutMs`, `drainTimeoutMs`, `redis.ttlMs`, `redis.timeoutMs`), like the rest of the
|
|
109
|
+
* Node API. `KAFKA` is a mapping with the snake_case `KafkaConfig` field names
|
|
110
|
+
* (`{ bootstrap_servers: 'kafka:9092', ... }`), never a `KafkaConfig` instance (spec §18).
|
|
111
|
+
*
|
|
112
|
+
* `publishTimeoutMs` is validated and exposed for parity; Node has no sync bridge, so the SDK
|
|
113
|
+
* itself does not use it (applications may use it to bound their own publish waits).
|
|
114
|
+
*/
|
|
115
|
+
export class MessagingSettings {
|
|
116
|
+
service;
|
|
117
|
+
environment;
|
|
118
|
+
/** `null` = no `SCHEMAS_DIR`: {@link buildMessaging} uses only the bundled contracts. */
|
|
119
|
+
schemasDir;
|
|
120
|
+
kafka;
|
|
121
|
+
sendTimeoutMs;
|
|
122
|
+
publishTimeoutMs;
|
|
123
|
+
drainTimeoutMs;
|
|
124
|
+
tenantMetricLabel;
|
|
125
|
+
redis;
|
|
126
|
+
/** Resource prefix applied by {@link buildMessaging} to every topic and group id (`null` = none). */
|
|
127
|
+
resourcePrefix;
|
|
128
|
+
/** No validation here: use {@link MessagingSettings.fromMapping}. */
|
|
129
|
+
constructor(init) {
|
|
130
|
+
this.service = init.service;
|
|
131
|
+
this.environment = init.environment;
|
|
132
|
+
this.schemasDir = init.schemasDir ?? null;
|
|
133
|
+
this.kafka = init.kafka;
|
|
134
|
+
this.sendTimeoutMs = init.sendTimeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
135
|
+
this.publishTimeoutMs = init.publishTimeoutMs ?? DEFAULT_PUBLISH_TIMEOUT_MS;
|
|
136
|
+
this.drainTimeoutMs = init.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;
|
|
137
|
+
this.tenantMetricLabel = init.tenantMetricLabel ?? true;
|
|
138
|
+
this.redis = init.redis ?? null;
|
|
139
|
+
this.resourcePrefix = init.resourcePrefix ?? null;
|
|
140
|
+
Object.freeze(this);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Validates and converts `SDK_MESSAGING`. Any problem → `TypeError` (Python `ValueError`; the
|
|
144
|
+
* `KafkaConfig` and `naming` validations also throw `TypeError`). Unknown keys are rejected,
|
|
145
|
+
* also inside `KAFKA` and `REDIS` (a typo must not pass silently). `env` replaces
|
|
146
|
+
* `process.env` when `KAFKA` is `null`/absent. An `undefined` value counts as absent.
|
|
147
|
+
*/
|
|
148
|
+
static fromMapping(mapping, options = {}) {
|
|
149
|
+
if (!isPlainObject(mapping)) {
|
|
150
|
+
throw new TypeError(`SDK_MESSAGING must be a mapping; got ${typeName(mapping)}`);
|
|
151
|
+
}
|
|
152
|
+
rejectUnknown(mapping, KNOWN_KEYS, 'SDK_MESSAGING');
|
|
153
|
+
requireKeys(mapping, REQUIRED_KEYS, 'SDK_MESSAGING');
|
|
154
|
+
const service = requireStr(mapping.SERVICE, "SDK_MESSAGING['SERVICE']");
|
|
155
|
+
naming.serviceName(service);
|
|
156
|
+
const environment = requireStr(mapping.ENVIRONMENT, "SDK_MESSAGING['ENVIRONMENT']");
|
|
157
|
+
const schemasDir = mapping.SCHEMAS_DIR ?? null;
|
|
158
|
+
if (schemasDir !== null && typeof schemasDir !== 'string') {
|
|
159
|
+
throw new TypeError(`SDK_MESSAGING['SCHEMAS_DIR'] must be null or a str; got ${typeName(schemasDir)}`);
|
|
160
|
+
}
|
|
161
|
+
if (schemasDir !== null && schemasDir.trim() === '') {
|
|
162
|
+
throw new TypeError("SDK_MESSAGING['SCHEMAS_DIR'] must not be empty");
|
|
163
|
+
}
|
|
164
|
+
const tenantMetricLabel = mapping.TENANT_METRIC_LABEL === undefined ? true : mapping.TENANT_METRIC_LABEL;
|
|
165
|
+
if (typeof tenantMetricLabel !== 'boolean') {
|
|
166
|
+
throw new TypeError(`SDK_MESSAGING['TENANT_METRIC_LABEL'] must be a bool; got ${typeName(tenantMetricLabel)}`);
|
|
167
|
+
}
|
|
168
|
+
const resourcePrefix = mapping.RESOURCE_PREFIX ?? null;
|
|
169
|
+
if (resourcePrefix !== null) {
|
|
170
|
+
if (typeof resourcePrefix !== 'string') {
|
|
171
|
+
throw new TypeError(`SDK_MESSAGING['RESOURCE_PREFIX'] must be null or a str; got ${typeName(resourcePrefix)}`);
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
naming.resourcePrefix(resourcePrefix);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
throw new TypeError(`SDK_MESSAGING['RESOURCE_PREFIX']: ${error.message}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const sendTimeoutS = positiveSeconds(mapping, 'SEND_TIMEOUT_S', 20, 'SDK_MESSAGING');
|
|
181
|
+
const publishTimeoutS = positiveSeconds(mapping, 'PUBLISH_TIMEOUT_S', 25, 'SDK_MESSAGING');
|
|
182
|
+
if (publishTimeoutS <= sendTimeoutS) {
|
|
183
|
+
throw new TypeError("SDK_MESSAGING['PUBLISH_TIMEOUT_S'] must be greater than" +
|
|
184
|
+
` 'SEND_TIMEOUT_S' (${pyFloat(publishTimeoutS)} <= ${pyFloat(sendTimeoutS)}):` +
|
|
185
|
+
' the caller must outlive the broker ack wait');
|
|
186
|
+
}
|
|
187
|
+
const kafka = parseKafka(mapping.KAFKA, options.env);
|
|
188
|
+
const drainTimeoutS = positiveSeconds(mapping, 'DRAIN_TIMEOUT_S', 30, 'SDK_MESSAGING');
|
|
189
|
+
const redis = parseRedis(mapping.REDIS);
|
|
190
|
+
return new MessagingSettings({
|
|
191
|
+
service,
|
|
192
|
+
environment,
|
|
193
|
+
schemasDir,
|
|
194
|
+
kafka,
|
|
195
|
+
sendTimeoutMs: sendTimeoutS * 1000,
|
|
196
|
+
publishTimeoutMs: publishTimeoutS * 1000,
|
|
197
|
+
drainTimeoutMs: drainTimeoutS * 1000,
|
|
198
|
+
tenantMetricLabel,
|
|
199
|
+
redis,
|
|
200
|
+
resourcePrefix,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function typeName(value) {
|
|
205
|
+
if (value === null)
|
|
206
|
+
return 'null';
|
|
207
|
+
if (Array.isArray(value))
|
|
208
|
+
return 'array';
|
|
209
|
+
if (typeof value === 'object') {
|
|
210
|
+
const ctor = value.constructor;
|
|
211
|
+
return typeof ctor?.name === 'string' && ctor.name !== '' ? ctor.name : 'object';
|
|
212
|
+
}
|
|
213
|
+
return typeof value;
|
|
214
|
+
}
|
|
215
|
+
/** Python-like repr of a configuration value (never used for secrets). */
|
|
216
|
+
function repr(value) {
|
|
217
|
+
if (typeof value === 'string')
|
|
218
|
+
return pyRepr(value);
|
|
219
|
+
if (typeof value === 'number' || typeof value === 'boolean' || value == null)
|
|
220
|
+
return String(value);
|
|
221
|
+
try {
|
|
222
|
+
return JSON.stringify(value) ?? typeName(value);
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return typeName(value);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** `25` → `25.0` (Python float repr), for messages identical in shape to the Python SDK. */
|
|
229
|
+
function pyFloat(value) {
|
|
230
|
+
return Number.isInteger(value) ? `${value}.0` : String(value);
|
|
231
|
+
}
|
|
232
|
+
function rejectUnknown(mapping, known, where) {
|
|
233
|
+
const unknown = Object.keys(mapping)
|
|
234
|
+
.filter((key) => !known.includes(key))
|
|
235
|
+
.sort();
|
|
236
|
+
if (unknown.length > 0) {
|
|
237
|
+
throw new TypeError(`unknown key(s) in ${where}: ${unknown.join(', ')} (accepted: ${[...known].sort().join(', ')})`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function requireKeys(mapping, keys, where) {
|
|
241
|
+
const missing = keys.filter((key) => mapping[key] === undefined);
|
|
242
|
+
if (missing.length > 0) {
|
|
243
|
+
throw new TypeError(`missing required key(s) in ${where}: ${missing.join(', ')}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function requireStr(value, where) {
|
|
247
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
248
|
+
throw new TypeError(`${where} must be a non-empty str; got ${repr(value)}`);
|
|
249
|
+
}
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
252
|
+
/** Finite number > 0, in seconds (booleans and numeric strings rejected). */
|
|
253
|
+
function positiveSeconds(mapping, key, defaultS, where) {
|
|
254
|
+
const value = mapping[key] === undefined ? defaultS : mapping[key];
|
|
255
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
256
|
+
throw new TypeError(`${where}['${key}'] must be a finite number > 0; got ${repr(value)}`);
|
|
257
|
+
}
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
function optionalStr(value, key, where) {
|
|
261
|
+
const field = value[key];
|
|
262
|
+
if (field === undefined || field === null || typeof field === 'string')
|
|
263
|
+
return field;
|
|
264
|
+
throw new TypeError(`${where}['${key}'] must be a str or null; got ${typeName(field)}`);
|
|
265
|
+
}
|
|
266
|
+
function parseKafka(value, env) {
|
|
267
|
+
if (value === undefined || value === null) {
|
|
268
|
+
return KafkaConfig.fromEnv(env === undefined ? {} : { env });
|
|
269
|
+
}
|
|
270
|
+
const where = "SDK_MESSAGING['KAFKA']";
|
|
271
|
+
if (!isPlainObject(value)) {
|
|
272
|
+
throw new TypeError(`${where} must be null or a mapping of KafkaConfig fields; got ${typeName(value)}`);
|
|
273
|
+
}
|
|
274
|
+
rejectUnknown(value, KAFKA_KEYS, where);
|
|
275
|
+
if (value.bootstrap_servers === undefined) {
|
|
276
|
+
throw new TypeError(`missing required key(s) in ${where}: bootstrap_servers`);
|
|
277
|
+
}
|
|
278
|
+
if (typeof value.bootstrap_servers !== 'string') {
|
|
279
|
+
throw new TypeError(`invalid ${where}: 'bootstrap_servers' must be a str`);
|
|
280
|
+
}
|
|
281
|
+
const protocol = value.security_protocol;
|
|
282
|
+
if (protocol !== undefined && typeof protocol !== 'string') {
|
|
283
|
+
throw new TypeError(`invalid ${where}: 'security_protocol' must be a str; got ${typeName(protocol)}`);
|
|
284
|
+
}
|
|
285
|
+
return new KafkaConfig({
|
|
286
|
+
bootstrapServers: value.bootstrap_servers,
|
|
287
|
+
clientId: optionalStr(value, 'client_id', where),
|
|
288
|
+
securityProtocol: protocol,
|
|
289
|
+
saslMechanism: optionalStr(value, 'sasl_mechanism', where),
|
|
290
|
+
saslUsername: optionalStr(value, 'sasl_username', where),
|
|
291
|
+
saslPassword: optionalStr(value, 'sasl_password', where),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function parseRedis(value) {
|
|
295
|
+
if (value === undefined || value === null)
|
|
296
|
+
return null;
|
|
297
|
+
const where = "SDK_MESSAGING['REDIS']";
|
|
298
|
+
if (!isPlainObject(value)) {
|
|
299
|
+
throw new TypeError(`${where} must be null or a mapping; got ${typeName(value)}`);
|
|
300
|
+
}
|
|
301
|
+
rejectUnknown(value, REDIS_KNOWN_KEYS, where);
|
|
302
|
+
requireKeys(value, REDIS_REQUIRED_KEYS, where);
|
|
303
|
+
const url = requireStr(value.URL, `${where}['URL']`);
|
|
304
|
+
const namespace = naming.dedupNamespace(requireStr(value.NAMESPACE, `${where}['NAMESPACE']`));
|
|
305
|
+
return new RedisSettings({
|
|
306
|
+
url,
|
|
307
|
+
namespace,
|
|
308
|
+
ttlMs: positiveSeconds(value, 'TTL_S', 604_800, where) * 1000,
|
|
309
|
+
timeoutMs: positiveSeconds(value, 'TIMEOUT_S', 5, where) * 1000,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
// =========================================================================== factories
|
|
313
|
+
/**
|
|
314
|
+
* Schema loader of {@link buildMessaging} (spec §14): no `SCHEMAS_DIR` → the bundled contracts
|
|
315
|
+
* (`contracts.schemaLoader()`); with it → `ChainSchemaLoader(FileSystemSchemaLoader(SCHEMAS_DIR),
|
|
316
|
+
* bundled)` (the service's schemas win, the bundled ones cover the rest). A `SCHEMAS_DIR` that is
|
|
317
|
+
* not a directory → `TypeError` (fail early instead of `SchemaNotFoundError` later).
|
|
318
|
+
*/
|
|
319
|
+
function buildSchemaLoader(settings) {
|
|
320
|
+
const dir = settings.schemasDir;
|
|
321
|
+
if (dir === null)
|
|
322
|
+
return bundledSchemaLoader();
|
|
323
|
+
let isDirectory = false;
|
|
324
|
+
try {
|
|
325
|
+
isDirectory = statSync(dir).isDirectory();
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
// missing / unreadable → not a directory
|
|
329
|
+
}
|
|
330
|
+
if (!isDirectory) {
|
|
331
|
+
throw new TypeError(`SDK_MESSAGING['SCHEMAS_DIR'] is not a directory: ${dir}`);
|
|
332
|
+
}
|
|
333
|
+
return new ChainSchemaLoader(new FileSystemSchemaLoader(dir), bundledSchemaLoader());
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* {@link Messaging} with the schema loader described in spec §14 (bundled contracts, preceded
|
|
337
|
+
* by `FileSystemSchemaLoader(SCHEMAS_DIR)` when set) and `OtelTelemetry`. Starts nothing. A
|
|
338
|
+
* `SCHEMAS_DIR` that is not a directory → `TypeError`.
|
|
339
|
+
*/
|
|
340
|
+
export function buildMessaging(settings, options = {}) {
|
|
341
|
+
const schemaLoader = buildSchemaLoader(settings);
|
|
342
|
+
return new Messaging(settings.kafka, {
|
|
343
|
+
service: settings.service,
|
|
344
|
+
environment: settings.environment,
|
|
345
|
+
schemaLoader,
|
|
346
|
+
telemetry: new OtelTelemetry(settings.service, settings.environment, {
|
|
347
|
+
tenantMetricLabel: settings.tenantMetricLabel,
|
|
348
|
+
}),
|
|
349
|
+
sendTimeoutMs: settings.sendTimeoutMs,
|
|
350
|
+
resourcePrefix: settings.resourcePrefix,
|
|
351
|
+
...(options.logger === undefined ? {} : { logger: options.logger }),
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* `RedisDedupStore` from the `REDIS` block (`null` without it). The Redis module (and `redis`
|
|
356
|
+
* itself) is imported only here, so the peer dependency stays optional.
|
|
357
|
+
*/
|
|
358
|
+
export async function buildDedupStore(settings) {
|
|
359
|
+
const redis = settings.redis;
|
|
360
|
+
if (redis === null)
|
|
361
|
+
return null;
|
|
362
|
+
const { RedisDedupStore } = await import('./redis.js');
|
|
363
|
+
return RedisDedupStore.fromUrl(redis.url, {
|
|
364
|
+
namespace: redis.namespace,
|
|
365
|
+
ttlMs: redis.ttlMs,
|
|
366
|
+
timeoutMs: redis.timeoutMs,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Runs consumers until SIGINT/SIGTERM (or `requestStop`) or the first fatal error.
|
|
371
|
+
*
|
|
372
|
+
* - Facade: `messagingFactory()` or `buildMessaging(settings)`.
|
|
373
|
+
* - Dedup store: `dedupStoreFactory()` or `buildDedupStore(settings)`; pinged once
|
|
374
|
+
* (`dedup_store_unreachable` when down) and always closed at the end.
|
|
375
|
+
* - Shutdown: stops the consumers and the facade, draining up to `drainTimeoutMs`. Signals stay
|
|
376
|
+
* captured while draining: a second signal is only logged.
|
|
377
|
+
*
|
|
378
|
+
* Resolves `0` (signal / requested stop) or `1` (a consumer crashed — `consumers_crashed` — or the
|
|
379
|
+
* shutdown failed). Startup errors (`build`, `start`, empty build) are rethrown after cleanup.
|
|
380
|
+
*/
|
|
381
|
+
export async function serveConsumers(build, options = {}) {
|
|
382
|
+
const { settings, messagingFactory } = options;
|
|
383
|
+
if (messagingFactory === undefined && settings === undefined) {
|
|
384
|
+
throw new TypeError("serveConsumers() needs 'settings' or 'messagingFactory'");
|
|
385
|
+
}
|
|
386
|
+
const drainTimeoutMs = options.drainTimeoutMs ?? settings?.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;
|
|
387
|
+
if (typeof drainTimeoutMs !== 'number' || !Number.isFinite(drainTimeoutMs) || drainTimeoutMs <= 0) {
|
|
388
|
+
throw new RangeError(`'drain_timeout_ms' must be a finite number > 0; got ${String(drainTimeoutMs)}`);
|
|
389
|
+
}
|
|
390
|
+
let stopRequested = false;
|
|
391
|
+
let signalStop = () => { };
|
|
392
|
+
const stopPromise = new Promise((resolve) => {
|
|
393
|
+
signalStop = resolve;
|
|
394
|
+
});
|
|
395
|
+
const requestStop = () => {
|
|
396
|
+
stopRequested = true;
|
|
397
|
+
signalStop();
|
|
398
|
+
};
|
|
399
|
+
const messaging = messagingFactory !== undefined
|
|
400
|
+
? await messagingFactory()
|
|
401
|
+
: buildMessaging(settings, {
|
|
402
|
+
...(options.logger === undefined ? {} : { logger: options.logger }),
|
|
403
|
+
});
|
|
404
|
+
const logger = options.logger ?? messaging.logger ?? defaultLogger();
|
|
405
|
+
let store = null;
|
|
406
|
+
let consumers = [];
|
|
407
|
+
let restoreSignals = null;
|
|
408
|
+
let exitCode = 0;
|
|
409
|
+
try {
|
|
410
|
+
if (options.installSignalHandlers ?? true) {
|
|
411
|
+
// Before start: a SIGTERM during startup also ends cleanly.
|
|
412
|
+
restoreSignals = installSignalHandlers(options.signals, requestStop, logger);
|
|
413
|
+
}
|
|
414
|
+
if (options.dedupStoreFactory !== undefined)
|
|
415
|
+
store = await options.dedupStoreFactory();
|
|
416
|
+
else if (settings !== undefined)
|
|
417
|
+
store = await buildDedupStore(settings);
|
|
418
|
+
if (store !== null && !(await store.ping()))
|
|
419
|
+
logger.warn('dedup_store_unreachable');
|
|
420
|
+
await messaging.start();
|
|
421
|
+
consumers = [...(await build(messaging, store))];
|
|
422
|
+
if (consumers.length === 0)
|
|
423
|
+
throw new TypeError('build() returned no consumers');
|
|
424
|
+
for (const consumer of consumers)
|
|
425
|
+
await consumer.start();
|
|
426
|
+
const results = consumers.map(() => ({ settled: false }));
|
|
427
|
+
const waiters = consumers.map((consumer, index) => Promise.resolve()
|
|
428
|
+
.then(() => consumer.wait())
|
|
429
|
+
.then(() => {
|
|
430
|
+
results[index] = { settled: true, error: undefined, failed: false };
|
|
431
|
+
}, (error) => {
|
|
432
|
+
results[index] = { settled: true, error, failed: true };
|
|
433
|
+
}));
|
|
434
|
+
logger.info('consumers_running', { messaging: { groups: consumers.map((c) => c.group) } });
|
|
435
|
+
options.onReady?.(requestStop);
|
|
436
|
+
await Promise.race([stopPromise, ...waiters]);
|
|
437
|
+
const stopNow = stopRequested;
|
|
438
|
+
results.forEach((result, index) => {
|
|
439
|
+
if (!result.settled)
|
|
440
|
+
return;
|
|
441
|
+
if (!result.failed && stopNow)
|
|
442
|
+
return; // stopped together with the stop request
|
|
443
|
+
// Fatal error, or loop ended without anyone asking: the process must not stay alive
|
|
444
|
+
// without consuming.
|
|
445
|
+
exitCode = 1;
|
|
446
|
+
logger.error('consumers_crashed', {
|
|
447
|
+
messaging: { group: consumers[index]?.group },
|
|
448
|
+
error: result.failed
|
|
449
|
+
? errorFields(result.error)
|
|
450
|
+
: { type: 'ConsumerStopped', message: 'stopped unexpectedly' },
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
if (exitCode === 0)
|
|
454
|
+
logger.info('consumers_stop_requested');
|
|
455
|
+
}
|
|
456
|
+
finally {
|
|
457
|
+
try {
|
|
458
|
+
exitCode = await shutdownConsumers(messaging, consumers, store, drainTimeoutMs, exitCode, logger);
|
|
459
|
+
}
|
|
460
|
+
finally {
|
|
461
|
+
restoreSignals?.();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return exitCode;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* CLI entry point: `await serveConsumers(...)` and sets `process.exitCode` to the result.
|
|
468
|
+
*
|
|
469
|
+
* Run it in a process/Deployment **separate** from the web server and keep the termination
|
|
470
|
+
* grace period above `drainTimeoutMs`. Startup errors are rethrown (unhandled → exit code 1).
|
|
471
|
+
*/
|
|
472
|
+
export async function runConsumers(build, options = {}) {
|
|
473
|
+
const code = await serveConsumers(build, options);
|
|
474
|
+
process.exitCode = code;
|
|
475
|
+
return code;
|
|
476
|
+
}
|
|
477
|
+
async function shutdownConsumers(messaging, consumers, store, drainTimeoutMs, exitCode, logger) {
|
|
478
|
+
let code = exitCode;
|
|
479
|
+
try {
|
|
480
|
+
try {
|
|
481
|
+
// Consumers created outside messaging.consumer() are stopped too (stop() is idempotent).
|
|
482
|
+
const results = await Promise.allSettled(consumers.map((consumer) => Promise.resolve().then(() => consumer.stop({ drainTimeoutMs }))));
|
|
483
|
+
results.forEach((result, index) => {
|
|
484
|
+
if (result.status === 'fulfilled')
|
|
485
|
+
return;
|
|
486
|
+
code = 1;
|
|
487
|
+
logger.error('consumer_stop_failed', {
|
|
488
|
+
messaging: { group: consumers[index]?.group },
|
|
489
|
+
error: errorFields(result.reason),
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
await messaging.stop({ drainTimeoutMs });
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
code = 1;
|
|
496
|
+
logger.error('consumers_shutdown_failed', { error: errorFields(error) });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
if (store !== null) {
|
|
501
|
+
try {
|
|
502
|
+
await store.close();
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
logger.warn('dedup_store_close_failed', { error: errorFields(error) });
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
logger.info('consumers_stopped', { exit_code: code });
|
|
510
|
+
return code;
|
|
511
|
+
}
|
|
512
|
+
const STOP_SIGNALS = ['SIGINT', 'SIGTERM'];
|
|
513
|
+
/** Installs SIGINT/SIGTERM listeners; returns the function that removes them. */
|
|
514
|
+
function installSignalHandlers(source, requestStop, logger) {
|
|
515
|
+
if (source === undefined && !isMainThread) {
|
|
516
|
+
logger.warn('signal_handlers_not_installed', { reason: 'not the main thread' });
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
const target = source ?? process;
|
|
520
|
+
const installed = [];
|
|
521
|
+
let received = 0;
|
|
522
|
+
const remove = () => {
|
|
523
|
+
for (const [signal, listener] of installed.splice(0)) {
|
|
524
|
+
try {
|
|
525
|
+
target.off(signal, listener);
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
// defensive: nothing else to restore
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
try {
|
|
533
|
+
for (const signal of STOP_SIGNALS) {
|
|
534
|
+
const listener = () => {
|
|
535
|
+
received += 1;
|
|
536
|
+
if (received === 1)
|
|
537
|
+
logger.info('consumers_signal_received', { signal });
|
|
538
|
+
else
|
|
539
|
+
logger.warn('consumers_shutdown_already_in_progress', { signal });
|
|
540
|
+
requestStop();
|
|
541
|
+
};
|
|
542
|
+
target.on(signal, listener);
|
|
543
|
+
installed.push([signal, listener]);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
remove();
|
|
548
|
+
const { type, message } = errorFields(error);
|
|
549
|
+
logger.warn('signal_handlers_not_installed', { reason: `${type}: ${message}` });
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
return remove;
|
|
553
|
+
}
|
|
554
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/integrations/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging contract. The SDK logs structured events (same event names as the Python SDK) with
|
|
3
|
+
* the context grouped in `messaging` and failures in `error: {type, message}`. Payload and
|
|
4
|
+
* message key are never logged.
|
|
5
|
+
*
|
|
6
|
+
* Default: JSON via `pino`, logger name `sdk_messaging`, the event name in the `event` field
|
|
7
|
+
* (like structlog) and the level as text (`"level":"info"`).
|
|
8
|
+
*/
|
|
9
|
+
import pino from 'pino';
|
|
10
|
+
/** Structured fields of a log event (`messaging`, `error`, ...). */
|
|
11
|
+
export type LogFields = Record<string, unknown>;
|
|
12
|
+
export interface Logger {
|
|
13
|
+
debug(event: string, fields?: LogFields): void;
|
|
14
|
+
info(event: string, fields?: LogFields): void;
|
|
15
|
+
warn(event: string, fields?: LogFields): void;
|
|
16
|
+
error(event: string, fields?: LogFields): void;
|
|
17
|
+
}
|
|
18
|
+
/** Logger that discards everything. */
|
|
19
|
+
export declare const noopLogger: Logger;
|
|
20
|
+
/** Minimal shape of a pino logger used by {@link pinoLogger}. */
|
|
21
|
+
export interface PinoLike {
|
|
22
|
+
debug(obj: object, msg?: string): void;
|
|
23
|
+
info(obj: object, msg?: string): void;
|
|
24
|
+
warn(obj: object, msg?: string): void;
|
|
25
|
+
error(obj: object, msg?: string): void;
|
|
26
|
+
}
|
|
27
|
+
/** Options of the default pino instance (`name: "sdk_messaging"`, `event` as message key). */
|
|
28
|
+
export declare function defaultPinoOptions(): pino.LoggerOptions;
|
|
29
|
+
/**
|
|
30
|
+
* Adapts a pino instance (default: a new one with {@link defaultPinoOptions}) to {@link Logger}.
|
|
31
|
+
* The event name is passed as the pino message; the fields are merged into the JSON line.
|
|
32
|
+
*/
|
|
33
|
+
export declare function pinoLogger(instance?: PinoLike): Logger;
|
|
34
|
+
/** Process-wide default logger (lazily created pino JSON logger). */
|
|
35
|
+
export declare function defaultLogger(): Logger;
|
|
36
|
+
/** Replaces the process-wide default logger (e.g. to plug the application's pino instance). */
|
|
37
|
+
export declare function setDefaultLogger(logger: Logger): void;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging contract. The SDK logs structured events (same event names as the Python SDK) with
|
|
3
|
+
* the context grouped in `messaging` and failures in `error: {type, message}`. Payload and
|
|
4
|
+
* message key are never logged.
|
|
5
|
+
*
|
|
6
|
+
* Default: JSON via `pino`, logger name `sdk_messaging`, the event name in the `event` field
|
|
7
|
+
* (like structlog) and the level as text (`"level":"info"`).
|
|
8
|
+
*/
|
|
9
|
+
import pino from 'pino';
|
|
10
|
+
/** Logger that discards everything. */
|
|
11
|
+
export const noopLogger = Object.freeze({
|
|
12
|
+
debug() { },
|
|
13
|
+
info() { },
|
|
14
|
+
warn() { },
|
|
15
|
+
error() { },
|
|
16
|
+
});
|
|
17
|
+
/** Options of the default pino instance (`name: "sdk_messaging"`, `event` as message key). */
|
|
18
|
+
export function defaultPinoOptions() {
|
|
19
|
+
return {
|
|
20
|
+
name: 'sdk_messaging',
|
|
21
|
+
messageKey: 'event',
|
|
22
|
+
formatters: { level: (label) => ({ level: label }) },
|
|
23
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Adapts a pino instance (default: a new one with {@link defaultPinoOptions}) to {@link Logger}.
|
|
28
|
+
* The event name is passed as the pino message; the fields are merged into the JSON line.
|
|
29
|
+
*/
|
|
30
|
+
export function pinoLogger(instance) {
|
|
31
|
+
const target = instance ?? pino(defaultPinoOptions());
|
|
32
|
+
return {
|
|
33
|
+
debug: (event, fields) => target.debug(fields ?? {}, event),
|
|
34
|
+
info: (event, fields) => target.info(fields ?? {}, event),
|
|
35
|
+
warn: (event, fields) => target.warn(fields ?? {}, event),
|
|
36
|
+
error: (event, fields) => target.error(fields ?? {}, event),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
let shared;
|
|
40
|
+
/** Process-wide default logger (lazily created pino JSON logger). */
|
|
41
|
+
export function defaultLogger() {
|
|
42
|
+
shared ??= pinoLogger();
|
|
43
|
+
return shared;
|
|
44
|
+
}
|
|
45
|
+
/** Replaces the process-wide default logger (e.g. to plug the application's pino instance). */
|
|
46
|
+
export function setDefaultLogger(logger) {
|
|
47
|
+
shared = logger;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=logger.js.map
|