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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -0
  3. package/dist/admin.d.ts +61 -0
  4. package/dist/admin.js +143 -0
  5. package/dist/admin.js.map +1 -0
  6. package/dist/config.d.ts +74 -0
  7. package/dist/config.js +128 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/consumer-client.d.ts +155 -0
  10. package/dist/consumer-client.js +299 -0
  11. package/dist/consumer-client.js.map +1 -0
  12. package/dist/consumer.d.ts +156 -0
  13. package/dist/consumer.js +1228 -0
  14. package/dist/consumer.js.map +1 -0
  15. package/dist/contracts/index.d.ts +16 -0
  16. package/dist/contracts/index.js +42 -0
  17. package/dist/contracts/index.js.map +1 -0
  18. package/dist/contracts/schemas.d.ts +570 -0
  19. package/dist/contracts/schemas.js +902 -0
  20. package/dist/contracts/schemas.js.map +1 -0
  21. package/dist/contracts/usuarios.d.ts +163 -0
  22. package/dist/contracts/usuarios.js +5 -0
  23. package/dist/contracts/usuarios.js.map +1 -0
  24. package/dist/dedup.d.ts +29 -0
  25. package/dist/dedup.js +65 -0
  26. package/dist/dedup.js.map +1 -0
  27. package/dist/dlq.d.ts +161 -0
  28. package/dist/dlq.js +341 -0
  29. package/dist/dlq.js.map +1 -0
  30. package/dist/errors.d.ts +72 -0
  31. package/dist/errors.js +95 -0
  32. package/dist/errors.js.map +1 -0
  33. package/dist/headers.d.ts +92 -0
  34. package/dist/headers.js +144 -0
  35. package/dist/headers.js.map +1 -0
  36. package/dist/index.d.ts +30 -0
  37. package/dist/index.js +32 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/integrations/redis.d.ts +118 -0
  40. package/dist/integrations/redis.js +272 -0
  41. package/dist/integrations/redis.js.map +1 -0
  42. package/dist/integrations/runtime.d.ts +232 -0
  43. package/dist/integrations/runtime.js +554 -0
  44. package/dist/integrations/runtime.js.map +1 -0
  45. package/dist/logger.d.ts +37 -0
  46. package/dist/logger.js +49 -0
  47. package/dist/logger.js.map +1 -0
  48. package/dist/messaging.d.ts +145 -0
  49. package/dist/messaging.js +164 -0
  50. package/dist/messaging.js.map +1 -0
  51. package/dist/naming.d.ts +64 -0
  52. package/dist/naming.js +167 -0
  53. package/dist/naming.js.map +1 -0
  54. package/dist/payload.d.ts +8 -0
  55. package/dist/payload.js +32 -0
  56. package/dist/payload.js.map +1 -0
  57. package/dist/publisher.d.ts +50 -0
  58. package/dist/publisher.js +199 -0
  59. package/dist/publisher.js.map +1 -0
  60. package/dist/retry.d.ts +24 -0
  61. package/dist/retry.js +42 -0
  62. package/dist/retry.js.map +1 -0
  63. package/dist/schema.d.ts +83 -0
  64. package/dist/schema.js +676 -0
  65. package/dist/schema.js.map +1 -0
  66. package/dist/telemetry.d.ts +52 -0
  67. package/dist/telemetry.js +221 -0
  68. package/dist/telemetry.js.map +1 -0
  69. package/dist/transport.d.ts +130 -0
  70. package/dist/transport.js +283 -0
  71. package/dist/transport.js.map +1 -0
  72. package/dist/types.d.ts +148 -0
  73. package/dist/types.js +35 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/util.d.ts +38 -0
  76. package/dist/util.js +112 -0
  77. package/dist/util.js.map +1 -0
  78. package/dist/w3c.d.ts +16 -0
  79. package/dist/w3c.js +119 -0
  80. package/dist/w3c.js.map +1 -0
  81. package/package.json +74 -1
@@ -0,0 +1,232 @@
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 { inspect } from 'node:util';
15
+ import { type Environment, KafkaConfig } from '../config.js';
16
+ import { type Logger } from '../logger.js';
17
+ import { Messaging, type MessagingConsumerOptions } from '../messaging.js';
18
+ import type { DuplicateChecker } from '../types.js';
19
+ /** Default `SEND_TIMEOUT_S` (20 s), in ms. */
20
+ export declare const DEFAULT_SEND_TIMEOUT_MS = 20000;
21
+ /** Default `PUBLISH_TIMEOUT_S` (25 s), in ms. */
22
+ export declare const DEFAULT_PUBLISH_TIMEOUT_MS = 25000;
23
+ /** Default `DRAIN_TIMEOUT_S` (30 s), in ms. */
24
+ export declare const DEFAULT_DRAIN_TIMEOUT_MS = 30000;
25
+ /** Default `REDIS.TTL_S` (7 days), in ms. */
26
+ export declare const DEFAULT_REDIS_TTL_MS = 604800000;
27
+ /** Default `REDIS.TIMEOUT_S` (5 s), in ms. */
28
+ export declare const DEFAULT_REDIS_TIMEOUT_MS = 5000;
29
+ /** Canonical configuration document (`SDK_MESSAGING`): UPPERCASE keys, durations in **seconds**. */
30
+ export interface SdkMessagingMapping {
31
+ SERVICE: string;
32
+ ENVIRONMENT: string;
33
+ /** `null`/absent → only the bundled contracts (`contracts.schemaLoader()`). */
34
+ SCHEMAS_DIR?: string | null;
35
+ /** `null`/absent → `KafkaConfig.fromEnv({ env })`. */
36
+ KAFKA?: {
37
+ bootstrap_servers: string;
38
+ client_id?: string | null;
39
+ security_protocol?: string;
40
+ sasl_mechanism?: string | null;
41
+ sasl_username?: string | null;
42
+ sasl_password?: string | null;
43
+ } | null;
44
+ SEND_TIMEOUT_S?: number;
45
+ PUBLISH_TIMEOUT_S?: number;
46
+ DRAIN_TIMEOUT_S?: number;
47
+ TENANT_METRIC_LABEL?: boolean;
48
+ REDIS?: {
49
+ URL: string;
50
+ NAMESPACE: string;
51
+ TTL_S?: number;
52
+ TIMEOUT_S?: number;
53
+ } | null;
54
+ /** `null`/absent → no prefix; else kebab-case (`hmg`, `prd`), spec §2.7. */
55
+ RESOURCE_PREFIX?: string | null;
56
+ }
57
+ /** `REDIS` block of {@link MessagingSettings} (shared dedup). Durations in ms. */
58
+ export declare class RedisSettings {
59
+ #private;
60
+ readonly namespace: string;
61
+ readonly ttlMs: number;
62
+ readonly timeoutMs: number;
63
+ constructor(init: {
64
+ url: string;
65
+ namespace: string;
66
+ ttlMs?: number;
67
+ timeoutMs?: number;
68
+ });
69
+ get url(): string;
70
+ toJSON(): Record<string, unknown>;
71
+ [inspect.custom](): string;
72
+ }
73
+ export interface MessagingSettingsInit {
74
+ service: string;
75
+ environment: string;
76
+ /** `null`/absent → only the bundled contracts. */
77
+ schemasDir?: string | null;
78
+ kafka: KafkaConfig;
79
+ sendTimeoutMs?: number;
80
+ publishTimeoutMs?: number;
81
+ drainTimeoutMs?: number;
82
+ tenantMetricLabel?: boolean;
83
+ redis?: RedisSettings | null;
84
+ /** `null`/absent → no prefix. */
85
+ resourcePrefix?: string | null;
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 declare class MessagingSettings {
116
+ readonly service: string;
117
+ readonly environment: string;
118
+ /** `null` = no `SCHEMAS_DIR`: {@link buildMessaging} uses only the bundled contracts. */
119
+ readonly schemasDir: string | null;
120
+ readonly kafka: KafkaConfig;
121
+ readonly sendTimeoutMs: number;
122
+ readonly publishTimeoutMs: number;
123
+ readonly drainTimeoutMs: number;
124
+ readonly tenantMetricLabel: boolean;
125
+ readonly redis: RedisSettings | null;
126
+ /** Resource prefix applied by {@link buildMessaging} to every topic and group id (`null` = none). */
127
+ readonly resourcePrefix: string | null;
128
+ /** No validation here: use {@link MessagingSettings.fromMapping}. */
129
+ constructor(init: MessagingSettingsInit);
130
+ /**
131
+ * Validates and converts `SDK_MESSAGING`. Any problem → `TypeError` (Python `ValueError`; the
132
+ * `KafkaConfig` and `naming` validations also throw `TypeError`). Unknown keys are rejected,
133
+ * also inside `KAFKA` and `REDIS` (a typo must not pass silently). `env` replaces
134
+ * `process.env` when `KAFKA` is `null`/absent. An `undefined` value counts as absent.
135
+ */
136
+ static fromMapping(mapping: unknown, options?: {
137
+ env?: Environment;
138
+ }): MessagingSettings;
139
+ }
140
+ /**
141
+ * {@link Messaging} with the schema loader described in spec §14 (bundled contracts, preceded
142
+ * by `FileSystemSchemaLoader(SCHEMAS_DIR)` when set) and `OtelTelemetry`. Starts nothing. A
143
+ * `SCHEMAS_DIR` that is not a directory → `TypeError`.
144
+ */
145
+ export declare function buildMessaging(settings: MessagingSettings, options?: {
146
+ logger?: Logger;
147
+ }): Messaging;
148
+ /** What {@link serveConsumers} uses from a dedup store (`RedisDedupStore` satisfies it). */
149
+ export interface DedupStore {
150
+ checker(group: string): DuplicateChecker;
151
+ /** Never throws: failure → `false`. */
152
+ ping(): Promise<boolean>;
153
+ close(): Promise<void>;
154
+ }
155
+ /**
156
+ * `RedisDedupStore` from the `REDIS` block (`null` without it). The Redis module (and `redis`
157
+ * itself) is imported only here, so the peer dependency stays optional.
158
+ */
159
+ export declare function buildDedupStore(settings: MessagingSettings): Promise<DedupStore | null>;
160
+ /** What the runner uses from a consumer (`Consumer` satisfies it). */
161
+ export interface RunnableConsumer {
162
+ readonly group: string;
163
+ start(): Promise<void>;
164
+ stop(options?: {
165
+ drainTimeoutMs?: number;
166
+ }): Promise<void>;
167
+ /** Resolves when the consumer loop ends; rejects with the fatal error that stopped it. */
168
+ wait(): Promise<void>;
169
+ }
170
+ /** Options of `Messaging.consumer(...)` (spec §17): the facade's own type, re-exported. */
171
+ export type { MessagingConsumerOptions } from '../messaging.js';
172
+ /** A facade able to create consumers (`Messaging` once `consumer()` is available). */
173
+ export interface ConsumerFactory {
174
+ consumer(options: MessagingConsumerOptions): RunnableConsumer;
175
+ }
176
+ /** What the runner uses from the facade (`Messaging` satisfies it). */
177
+ export interface RunnableMessaging {
178
+ start(): Promise<void>;
179
+ stop(options?: {
180
+ drainTimeoutMs?: number;
181
+ }): Promise<void>;
182
+ readonly logger?: Logger;
183
+ }
184
+ /**
185
+ * `build(messaging, dedupStore) → [consumer, ...]`: create them with `messaging.consumer({...})`
186
+ * (pass `duplicateChecker: dedupStore.checker(group)` for shared dedup). Do not start them: the
187
+ * runner does.
188
+ */
189
+ export type ConsumerBuilder<M extends RunnableMessaging = Messaging> = (messaging: M, dedupStore: DedupStore | null) => Iterable<RunnableConsumer> | Promise<Iterable<RunnableConsumer>>;
190
+ /** Where stop signals come from (default `process`); injectable for tests and embedding. */
191
+ export interface SignalSource {
192
+ on(signal: 'SIGINT' | 'SIGTERM', listener: () => void): unknown;
193
+ off(signal: 'SIGINT' | 'SIGTERM', listener: () => void): unknown;
194
+ }
195
+ export interface ServeConsumersOptions<M extends RunnableMessaging = Messaging> {
196
+ /** Used by the default factories (`buildMessaging`, `buildDedupStore`) and for `drainTimeoutMs`. */
197
+ settings?: MessagingSettings;
198
+ /** Default `buildMessaging(settings)`. */
199
+ messagingFactory?: () => M | Promise<M>;
200
+ /** Default `buildDedupStore(settings)` (`null` without `REDIS`). Always closed at the end. */
201
+ dedupStoreFactory?: () => DedupStore | null | Promise<DedupStore | null>;
202
+ /** Default `true`: SIGINT/SIGTERM request the stop (main thread only). */
203
+ installSignalHandlers?: boolean;
204
+ /** Default `settings.drainTimeoutMs`, or 30 000 ms. */
205
+ drainTimeoutMs?: number;
206
+ /** Called with the consumers already started; `requestStop()` is equivalent to a signal. */
207
+ onReady?: (requestStop: () => void) => unknown;
208
+ /** Runner logs. Default: the facade logger (`messaging.logger`), else the SDK default logger. */
209
+ logger?: Logger;
210
+ /** Signal source. Default `process`. */
211
+ signals?: SignalSource | undefined;
212
+ }
213
+ /**
214
+ * Runs consumers until SIGINT/SIGTERM (or `requestStop`) or the first fatal error.
215
+ *
216
+ * - Facade: `messagingFactory()` or `buildMessaging(settings)`.
217
+ * - Dedup store: `dedupStoreFactory()` or `buildDedupStore(settings)`; pinged once
218
+ * (`dedup_store_unreachable` when down) and always closed at the end.
219
+ * - Shutdown: stops the consumers and the facade, draining up to `drainTimeoutMs`. Signals stay
220
+ * captured while draining: a second signal is only logged.
221
+ *
222
+ * Resolves `0` (signal / requested stop) or `1` (a consumer crashed — `consumers_crashed` — or the
223
+ * shutdown failed). Startup errors (`build`, `start`, empty build) are rethrown after cleanup.
224
+ */
225
+ export declare function serveConsumers<M extends RunnableMessaging = Messaging>(build: ConsumerBuilder<M>, options?: ServeConsumersOptions<M>): Promise<number>;
226
+ /**
227
+ * CLI entry point: `await serveConsumers(...)` and sets `process.exitCode` to the result.
228
+ *
229
+ * Run it in a process/Deployment **separate** from the web server and keep the termination
230
+ * grace period above `drainTimeoutMs`. Startup errors are rethrown (unhandled → exit code 1).
231
+ */
232
+ export declare function runConsumers<M extends RunnableMessaging = Messaging>(build: ConsumerBuilder<M>, options?: ServeConsumersOptions<M>): Promise<number>;