@bleedingdev/modern-js-server-runtime-extensions 0.0.0-trusted-publisher-bootstrap

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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/cjs/contractGateAutopilot.js +162 -0
  4. package/dist/cjs/contractGateSnapshotStore.js +253 -0
  5. package/dist/cjs/env.js +58 -0
  6. package/dist/cjs/index.js +162 -0
  7. package/dist/cjs/mfCache.js +106 -0
  8. package/dist/cjs/moduleFederationCss.js +285 -0
  9. package/dist/cjs/runtimeFallbackSignal.js +311 -0
  10. package/dist/cjs/telemetry.js +373 -0
  11. package/dist/cjs/telemetryCore.js +819 -0
  12. package/dist/esm/contractGateAutopilot.mjs +124 -0
  13. package/dist/esm/contractGateSnapshotStore.mjs +190 -0
  14. package/dist/esm/env.mjs +17 -0
  15. package/dist/esm/index.mjs +6 -0
  16. package/dist/esm/mfCache.mjs +55 -0
  17. package/dist/esm/moduleFederationCss.mjs +225 -0
  18. package/dist/esm/runtimeFallbackSignal.mjs +222 -0
  19. package/dist/esm/telemetry.mjs +275 -0
  20. package/dist/esm/telemetryCore.mjs +759 -0
  21. package/dist/esm-node/contractGateAutopilot.mjs +125 -0
  22. package/dist/esm-node/contractGateSnapshotStore.mjs +192 -0
  23. package/dist/esm-node/env.mjs +18 -0
  24. package/dist/esm-node/index.mjs +7 -0
  25. package/dist/esm-node/mfCache.mjs +56 -0
  26. package/dist/esm-node/moduleFederationCss.mjs +226 -0
  27. package/dist/esm-node/runtimeFallbackSignal.mjs +223 -0
  28. package/dist/esm-node/telemetry.mjs +276 -0
  29. package/dist/esm-node/telemetryCore.mjs +760 -0
  30. package/dist/types/contractGateAutopilot.d.ts +35 -0
  31. package/dist/types/contractGateSnapshotStore.d.ts +57 -0
  32. package/dist/types/env.d.ts +40 -0
  33. package/dist/types/index.d.ts +6 -0
  34. package/dist/types/mfCache.d.ts +27 -0
  35. package/dist/types/moduleFederationCss.d.ts +87 -0
  36. package/dist/types/runtimeFallbackSignal.d.ts +94 -0
  37. package/dist/types/telemetry.d.ts +12 -0
  38. package/dist/types/telemetryCore.d.ts +257 -0
  39. package/package.json +69 -0
  40. package/rslib.config.mts +4 -0
  41. package/rstest.config.mts +7 -0
  42. package/src/contractGateAutopilot.ts +247 -0
  43. package/src/contractGateSnapshotStore.ts +420 -0
  44. package/src/env.ts +63 -0
  45. package/src/index.ts +84 -0
  46. package/src/mfCache.ts +119 -0
  47. package/src/moduleFederationCss.ts +473 -0
  48. package/src/runtimeFallbackSignal.ts +584 -0
  49. package/src/telemetry.ts +554 -0
  50. package/src/telemetryCore.ts +1332 -0
  51. package/tests/contractGateAutopilot.test.ts +203 -0
  52. package/tests/contractGateSnapshotStore.test.ts +223 -0
  53. package/tests/env.test.ts +73 -0
  54. package/tests/helpers.ts +19 -0
  55. package/tests/mfCache.test.ts +150 -0
  56. package/tests/moduleFederationCss.test.ts +392 -0
  57. package/tests/registration.test.ts +112 -0
  58. package/tests/telemetry.test.ts +360 -0
  59. package/tests/telemetryAutopilot.test.ts +993 -0
  60. package/tests/telemetryCanaryOrchestrator.test.ts +140 -0
  61. package/tests/telemetryLifecycle.test.ts +168 -0
  62. package/tests/telemetryTraceparent.test.ts +167 -0
  63. package/tests/tsconfig.json +11 -0
  64. package/tsconfig.json +10 -0
@@ -0,0 +1,360 @@
1
+ import {
2
+ createOtlpTelemetryExporter,
3
+ createTelemetryAwareMetrics,
4
+ createVictoriaMetricsTelemetryExporter,
5
+ type TelemetryEnvelope,
6
+ TelemetryRegistry,
7
+ TelemetryStartupHealthError,
8
+ } from '../src/telemetry';
9
+
10
+ const createEnvelope = (partial: Record<string, unknown> = {}) => ({
11
+ timestamp: Date.now(),
12
+ service: 'svc',
13
+ module: 'server',
14
+ environment: 'test',
15
+ signalType: 'metric' as const,
16
+ name: 'server.handle.request',
17
+ value: 10,
18
+ unit: 'ms',
19
+ ...partial,
20
+ });
21
+
22
+ describe('telemetry registry', () => {
23
+ test('applies redaction and emits dropped-count metric under backpressure', async () => {
24
+ const batches: unknown[] = [];
25
+ const registry = new TelemetryRegistry({
26
+ service: 'svc',
27
+ module: 'server',
28
+ environment: 'test',
29
+ maxBatchSize: 10,
30
+ maxQueueSize: 3,
31
+ flushIntervalMs: 60_000,
32
+ redactionKeys: ['token'],
33
+ });
34
+ await registry.register({
35
+ name: 'memory',
36
+ async emit(batch) {
37
+ batches.push(...batch);
38
+ },
39
+ });
40
+
41
+ registry.enqueue(
42
+ createEnvelope({
43
+ name: 'first',
44
+ attributes: { token: 's1', keep: 'ok' },
45
+ }),
46
+ );
47
+ registry.enqueue(
48
+ createEnvelope({
49
+ name: 'second',
50
+ }),
51
+ );
52
+ registry.enqueue(
53
+ createEnvelope({
54
+ name: 'third',
55
+ }),
56
+ );
57
+ registry.enqueue(
58
+ createEnvelope({
59
+ name: 'fourth',
60
+ }),
61
+ );
62
+
63
+ await registry.flush();
64
+ await registry.shutdown();
65
+
66
+ const names = batches.map(
67
+ item => (item as { name?: string }).name || 'unknown',
68
+ );
69
+ expect(names).toContain('telemetry.queue.dropped');
70
+ const withAttributes = batches.find(
71
+ item => (item as { name?: string }).name === 'first',
72
+ ) as
73
+ | {
74
+ attributes?: Record<string, unknown>;
75
+ }
76
+ | undefined;
77
+ expect(withAttributes).toBeUndefined();
78
+ });
79
+
80
+ test('redacts configured keys recursively', async () => {
81
+ const batches: unknown[] = [];
82
+ const registry = new TelemetryRegistry({
83
+ service: 'svc',
84
+ module: 'server',
85
+ environment: 'test',
86
+ flushIntervalMs: 60_000,
87
+ redactionKeys: ['token'],
88
+ });
89
+ await registry.register({
90
+ name: 'memory',
91
+ async emit(batch) {
92
+ batches.push(...batch);
93
+ },
94
+ });
95
+
96
+ registry.enqueue(
97
+ createEnvelope({
98
+ name: 'sensitive',
99
+ attributes: {
100
+ token: 'secret',
101
+ nested: {
102
+ token: 'inner-secret',
103
+ keep: true,
104
+ },
105
+ },
106
+ }),
107
+ );
108
+
109
+ await registry.flush();
110
+ await registry.shutdown();
111
+
112
+ const item = batches.find(
113
+ entry => (entry as { name?: string }).name === 'sensitive',
114
+ ) as
115
+ | {
116
+ attributes?: {
117
+ token?: string;
118
+ nested?: {
119
+ token?: string;
120
+ keep?: boolean;
121
+ };
122
+ };
123
+ }
124
+ | undefined;
125
+ expect(item).toBeDefined();
126
+ expect(item?.attributes?.token).toBe('[REDACTED]');
127
+ expect(item?.attributes?.nested?.token).toBe('[REDACTED]');
128
+ expect(item?.attributes?.nested?.keep).toBe(true);
129
+ });
130
+
131
+ test('wraps metrics and preserves trace tags in envelopes', async () => {
132
+ const emitted: TelemetryEnvelope[] = [];
133
+ const registry = new TelemetryRegistry({
134
+ service: 'svc',
135
+ module: 'server',
136
+ environment: 'test',
137
+ flushIntervalMs: 60_000,
138
+ });
139
+ await registry.register({
140
+ name: 'memory',
141
+ async emit(batch) {
142
+ emitted.push(...batch);
143
+ },
144
+ });
145
+
146
+ const base = {
147
+ gauges: rs.fn(),
148
+ emitCounter: rs.fn(),
149
+ emitTimer: rs.fn(),
150
+ };
151
+ const metrics = createTelemetryAwareMetrics(base as any, registry);
152
+ metrics.emitCounter('server.request.count', 1, {
153
+ pathname: '/foo',
154
+ trace_id: '11112222333344445555666677778888',
155
+ });
156
+ metrics.emitTimer('server.request.cost', 25, {
157
+ pathname: '/foo',
158
+ span_id: '1111222233334444',
159
+ });
160
+
161
+ await registry.flush();
162
+ await registry.shutdown();
163
+
164
+ expect(base.emitCounter).toHaveBeenCalledTimes(1);
165
+ expect(base.emitTimer).toHaveBeenCalledTimes(1);
166
+
167
+ const countEnvelope = emitted.find(
168
+ item => item.name === 'server.request.count',
169
+ )!;
170
+ const timerEnvelope = emitted.find(
171
+ item => item.name === 'server.request.cost',
172
+ )!;
173
+
174
+ expect(countEnvelope.unit).toBe('count');
175
+ expect(countEnvelope.traceId).toBe('11112222333344445555666677778888');
176
+ expect(timerEnvelope.unit).toBe('ms');
177
+ expect(timerEnvelope.spanId).toBe('1111222233334444');
178
+ });
179
+
180
+ test('emits queue depth and utilization metrics during flush', async () => {
181
+ const emitted: TelemetryEnvelope[] = [];
182
+ const registry = new TelemetryRegistry({
183
+ service: 'svc',
184
+ module: 'server',
185
+ environment: 'test',
186
+ maxQueueSize: 10,
187
+ flushIntervalMs: 60_000,
188
+ });
189
+ await registry.register({
190
+ name: 'memory',
191
+ async emit(batch) {
192
+ emitted.push(...batch);
193
+ },
194
+ });
195
+
196
+ registry.enqueue(createEnvelope({ name: 'a' }));
197
+ registry.enqueue(createEnvelope({ name: 'b' }));
198
+ await registry.flush();
199
+
200
+ const depthEnvelope = emitted.find(
201
+ item => item.name === 'telemetry.queue.depth',
202
+ );
203
+ const utilizationEnvelope = emitted.find(
204
+ item => item.name === 'telemetry.queue.utilization',
205
+ );
206
+ expect(depthEnvelope?.value).toBe(2);
207
+ expect(utilizationEnvelope?.value).toBe(0.2);
208
+ await registry.shutdown();
209
+ });
210
+
211
+ test('exposes queue stats and emits SLO alerts for utilization and dropped envelopes', async () => {
212
+ const alerts: Array<{ type: string; value: number }> = [];
213
+ const registry = new TelemetryRegistry({
214
+ service: 'svc',
215
+ module: 'server',
216
+ environment: 'test',
217
+ maxQueueSize: 2,
218
+ flushIntervalMs: 60_000,
219
+ slo: {
220
+ queueUtilizationWarnThreshold: 0.5,
221
+ queueDroppedWarnThreshold: 1,
222
+ alertCooldownMs: 0,
223
+ onAlert(alert) {
224
+ alerts.push({ type: alert.type, value: alert.value });
225
+ },
226
+ },
227
+ });
228
+
229
+ registry.enqueue(createEnvelope({ name: 'first' }));
230
+ registry.enqueue(createEnvelope({ name: 'second' }));
231
+ registry.enqueue(createEnvelope({ name: 'third' }));
232
+
233
+ const queueStats = registry.getQueueStats();
234
+ expect(queueStats.depth).toBe(2);
235
+ expect(queueStats.capacity).toBe(2);
236
+ expect(queueStats.utilization).toBe(1);
237
+ expect(queueStats.pendingDropped).toBe(1);
238
+ expect(queueStats.totalDropped).toBe(1);
239
+ expect(alerts.some(item => item.type === 'queue.utilization')).toBe(true);
240
+ expect(alerts.some(item => item.type === 'queue.drop')).toBe(true);
241
+ await registry.shutdown();
242
+ });
243
+
244
+ test('startup health check fails loud by default when exporter is unhealthy', async () => {
245
+ const registry = new TelemetryRegistry({
246
+ service: 'svc',
247
+ module: 'server',
248
+ environment: 'test',
249
+ flushIntervalMs: 60_000,
250
+ });
251
+ await registry.register({
252
+ name: 'failing',
253
+ async emit() {
254
+ throw new Error('connection refused');
255
+ },
256
+ });
257
+
258
+ await expect(registry.startupHealthCheck()).rejects.toBeInstanceOf(
259
+ TelemetryStartupHealthError,
260
+ );
261
+ const health = registry.getExporterHealth();
262
+ expect(health).toHaveLength(1);
263
+ expect(health[0].healthy).toBe(false);
264
+ expect(health[0].failures).toBeGreaterThan(0);
265
+ await registry.shutdown();
266
+ });
267
+
268
+ test('startup health check can degrade without throwing when failLoud is false', async () => {
269
+ const registry = new TelemetryRegistry({
270
+ service: 'svc',
271
+ module: 'server',
272
+ environment: 'test',
273
+ flushIntervalMs: 60_000,
274
+ });
275
+ await registry.register({
276
+ name: 'failing',
277
+ async emit() {
278
+ throw new Error('connection refused');
279
+ },
280
+ });
281
+
282
+ await expect(
283
+ registry.startupHealthCheck({ failLoud: false }),
284
+ ).resolves.toBeUndefined();
285
+ const health = registry.getExporterHealth();
286
+ expect(health).toHaveLength(1);
287
+ expect(health[0].healthy).toBe(false);
288
+ expect(health[0].failures).toBeGreaterThan(0);
289
+ await registry.shutdown();
290
+ });
291
+ });
292
+
293
+ describe('telemetry exporters', () => {
294
+ const originalFetch = globalThis.fetch;
295
+
296
+ afterEach(() => {
297
+ globalThis.fetch = originalFetch;
298
+ });
299
+
300
+ test('otlp exporter sends envelope batch with JSON body', async () => {
301
+ const mockFetch = rs.fn(async () => new Response('ok', { status: 200 }));
302
+ globalThis.fetch = mockFetch as typeof fetch;
303
+
304
+ const exporter = createOtlpTelemetryExporter({
305
+ endpoint: 'http://localhost:4318/v1/logs',
306
+ });
307
+ await exporter.emit([
308
+ createEnvelope({
309
+ signalType: 'log',
310
+ name: 'hello',
311
+ level: 'info',
312
+ }),
313
+ ]);
314
+
315
+ expect(mockFetch).toHaveBeenCalledTimes(1);
316
+ const [url, init] = mockFetch.mock.calls[0] as unknown as [
317
+ string,
318
+ RequestInit,
319
+ ];
320
+ expect(url).toBe('http://localhost:4318/v1/logs');
321
+ expect(init.method).toBe('POST');
322
+ const payload = JSON.parse(String(init.body)) as {
323
+ events: unknown[];
324
+ };
325
+ expect(payload.events).toHaveLength(1);
326
+ });
327
+
328
+ test('victoria metrics exporter emits prometheus lines', async () => {
329
+ const mockFetch = rs.fn(async () => new Response('ok', { status: 200 }));
330
+ globalThis.fetch = mockFetch as typeof fetch;
331
+
332
+ const exporter = createVictoriaMetricsTelemetryExporter({
333
+ endpoint: 'http://localhost:8428/api/v1/import/prometheus',
334
+ metricPrefix: 'modernjs',
335
+ });
336
+ await exporter.emit([
337
+ createEnvelope({
338
+ signalType: 'metric',
339
+ name: 'server.handle.request',
340
+ value: 42,
341
+ }),
342
+ createEnvelope({
343
+ signalType: 'log',
344
+ name: 'request.error',
345
+ level: 'error',
346
+ }),
347
+ ]);
348
+
349
+ expect(mockFetch).toHaveBeenCalledTimes(1);
350
+ const [url, init] = mockFetch.mock.calls[0] as unknown as [
351
+ string,
352
+ RequestInit,
353
+ ];
354
+ expect(url).toBe('http://localhost:8428/api/v1/import/prometheus');
355
+ expect(init.method).toBe('POST');
356
+ const body = String(init.body);
357
+ expect(body).toContain('modernjs_metric_server_handle_request');
358
+ expect(body).toContain('modernjs_log_request_error');
359
+ });
360
+ });