@friggframework/core 2.0.0--canary.622.faffe5f.0 → 2.0.0--canary.623.71305f2.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 (59) hide show
  1. package/README.md +0 -40
  2. package/application/index.js +10 -15
  3. package/core/create-handler.js +10 -114
  4. package/generated/prisma-mongodb/edge.js +4 -16
  5. package/generated/prisma-mongodb/index-browser.js +1 -13
  6. package/generated/prisma-mongodb/index.d.ts +96 -1495
  7. package/generated/prisma-mongodb/index.js +4 -16
  8. package/generated/prisma-mongodb/package.json +1 -1
  9. package/generated/prisma-mongodb/schema.prisma +0 -24
  10. package/generated/prisma-mongodb/wasm.js +4 -16
  11. package/generated/prisma-postgresql/edge.js +4 -16
  12. package/generated/prisma-postgresql/index-browser.js +1 -13
  13. package/generated/prisma-postgresql/index.d.ts +91 -1541
  14. package/generated/prisma-postgresql/index.js +4 -16
  15. package/generated/prisma-postgresql/package.json +1 -1
  16. package/generated/prisma-postgresql/schema.prisma +0 -23
  17. package/generated/prisma-postgresql/wasm.js +4 -16
  18. package/handlers/app-definition-loader.js +3 -26
  19. package/handlers/integration-event-dispatcher.js +15 -34
  20. package/handlers/routers/integration-webhook-routers.js +7 -23
  21. package/index.js +9 -11
  22. package/integrations/integration-base.js +23 -88
  23. package/modules/module.js +15 -2
  24. package/modules/requester/requester.js +46 -111
  25. package/package.json +5 -12
  26. package/prisma-mongodb/schema.prisma +0 -24
  27. package/prisma-postgresql/schema.prisma +0 -23
  28. package/reporting/README.md +1 -8
  29. package/reporting/reporting-router.js +1 -16
  30. package/reporting/use-cases/list-integrations-report.js +6 -61
  31. package/application/commands/usage-commands.js +0 -77
  32. package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +0 -26
  33. package/telemetry/README.md +0 -317
  34. package/telemetry/bind-telemetry-context.js +0 -51
  35. package/telemetry/canonical-counters.js +0 -52
  36. package/telemetry/exporters/exporter-factory.js +0 -79
  37. package/telemetry/index.js +0 -56
  38. package/telemetry/instrument-handler.js +0 -82
  39. package/telemetry/no-op-telemetry.js +0 -66
  40. package/telemetry/north-star.js +0 -103
  41. package/telemetry/otel-telemetry.js +0 -215
  42. package/telemetry/plugin-subscribers-singleton.js +0 -51
  43. package/telemetry/plugin-subscribers.js +0 -77
  44. package/telemetry/telemetry-config.js +0 -118
  45. package/telemetry/telemetry-context.js +0 -40
  46. package/telemetry/telemetry-event-bus.js +0 -58
  47. package/telemetry/telemetry-service.js +0 -44
  48. package/telemetry/telemetry-singleton.js +0 -41
  49. package/telemetry/usage-rollup-singleton.js +0 -74
  50. package/telemetry/usage-rollup-subscriber.js +0 -116
  51. package/telemetry/usage-windows.js +0 -14
  52. package/usage/README.md +0 -54
  53. package/usage/index.js +0 -19
  54. package/usage/repositories/usage-repository-documentdb.js +0 -199
  55. package/usage/repositories/usage-repository-factory.js +0 -28
  56. package/usage/repositories/usage-repository-interface.js +0 -37
  57. package/usage/repositories/usage-repository-mongo.js +0 -12
  58. package/usage/repositories/usage-repository-postgres.js +0 -152
  59. package/usage/tracked-metrics.js +0 -38
@@ -1,79 +0,0 @@
1
- /**
2
- * Builds OpenTelemetry span/metric exporters from an app-definition descriptor.
3
- * All OTel packages are lazy-`require`d here so the no-op
4
- * path (which never calls this) loads zero OTel modules.
5
- *
6
- * Supported descriptor `type`s:
7
- * - 'console' → ConsoleSpanExporter / ConsoleMetricExporter (local dev)
8
- * - 'otlp' → OTLP/HTTP to descriptor.endpoint (+ headers)
9
- * - 'honeycomb' → OTLP preset (api.honeycomb.io, x-honeycomb-team header)
10
- * - 'datadog' → OTLP preset (descriptor.endpoint required)
11
- *
12
- * A descriptor may also carry pre-built exporter instances
13
- * (`traceExporter` / `metricExporter`) — used by tests (in-memory exporters) and
14
- * advanced adopters. Instances win over `type`.
15
- *
16
- * @returns {{ traceExporter: object|null, metricExporter: object|null }}
17
- */
18
- function buildExporters(descriptor = {}) {
19
- if (descriptor.traceExporter || descriptor.metricExporter) {
20
- return {
21
- traceExporter: descriptor.traceExporter || null,
22
- metricExporter: descriptor.metricExporter || null,
23
- };
24
- }
25
-
26
- const type = descriptor.type;
27
-
28
- if (type === 'console') {
29
- const {
30
- ConsoleSpanExporter,
31
- } = require('@opentelemetry/sdk-trace-base');
32
- const { ConsoleMetricExporter } = require('@opentelemetry/sdk-metrics');
33
- return {
34
- traceExporter: new ConsoleSpanExporter(),
35
- metricExporter: new ConsoleMetricExporter(),
36
- };
37
- }
38
-
39
- const { endpoint, headers } = resolveOtlpTarget(descriptor);
40
- const {
41
- OTLPTraceExporter,
42
- } = require('@opentelemetry/exporter-trace-otlp-http');
43
- const {
44
- OTLPMetricExporter,
45
- } = require('@opentelemetry/exporter-metrics-otlp-http');
46
-
47
- // When no endpoint is set the exporters fall back to the standard
48
- // OTEL_EXPORTER_OTLP_ENDPOINT env var, so leave `url` undefined in that case.
49
- return {
50
- traceExporter: new OTLPTraceExporter({
51
- ...(endpoint ? { url: joinPath(endpoint, '/v1/traces') } : {}),
52
- ...(headers ? { headers } : {}),
53
- }),
54
- metricExporter: new OTLPMetricExporter({
55
- ...(endpoint ? { url: joinPath(endpoint, '/v1/metrics') } : {}),
56
- ...(headers ? { headers } : {}),
57
- }),
58
- };
59
- }
60
-
61
- /** Resolve endpoint + headers for OTLP and its vendor presets. */
62
- function resolveOtlpTarget(descriptor) {
63
- if (descriptor.type === 'honeycomb') {
64
- return {
65
- endpoint: descriptor.endpoint || 'https://api.honeycomb.io',
66
- headers: descriptor.apiKey
67
- ? { 'x-honeycomb-team': descriptor.apiKey }
68
- : descriptor.headers,
69
- };
70
- }
71
- // 'otlp' and 'datadog' both use a plain OTLP endpoint + optional headers.
72
- return { endpoint: descriptor.endpoint, headers: descriptor.headers };
73
- }
74
-
75
- function joinPath(base, path) {
76
- return `${String(base).replace(/\/$/, '')}${path}`;
77
- }
78
-
79
- module.exports = { buildExporters };
@@ -1,56 +0,0 @@
1
- const { createTelemetry, isNoOpExporter } = require('./telemetry-service');
2
- const { createNoOpTelemetry } = require('./no-op-telemetry');
3
- const { createTelemetryEventBus } = require('./telemetry-event-bus');
4
- const { resolveTelemetryConfig } = require('./telemetry-config');
5
- const {
6
- getTelemetry,
7
- resetTelemetryForTests,
8
- } = require('./telemetry-singleton');
9
- const { buildExporters } = require('./exporters/exporter-factory');
10
- const { instrumentHandler } = require('./instrument-handler');
11
- const { bindTelemetryContext } = require('./bind-telemetry-context');
12
- const { createUsageRollupSubscriber } = require('./usage-rollup-subscriber');
13
- const {
14
- getUsageRollupSubscriber,
15
- resetUsageRollupForTests,
16
- } = require('./usage-rollup-singleton');
17
- const { computeUsageWindows } = require('./usage-windows');
18
- const {
19
- resolveNorthStarEntry,
20
- northStarKeys,
21
- createNorthStarDerivationSubscriber,
22
- } = require('./north-star');
23
- const {
24
- CANONICAL_COUNTERS,
25
- isCanonicalCounter,
26
- } = require('./canonical-counters');
27
- const { wireTelemetrySubscribers } = require('./plugin-subscribers');
28
- const {
29
- getPluginTelemetrySubscribers,
30
- resetPluginTelemetrySubscribersForTests,
31
- } = require('./plugin-subscribers-singleton');
32
-
33
- module.exports = {
34
- createTelemetry,
35
- createNoOpTelemetry,
36
- createTelemetryEventBus,
37
- resolveTelemetryConfig,
38
- getTelemetry,
39
- resetTelemetryForTests,
40
- isNoOpExporter,
41
- buildExporters,
42
- instrumentHandler,
43
- bindTelemetryContext,
44
- createUsageRollupSubscriber,
45
- getUsageRollupSubscriber,
46
- resetUsageRollupForTests,
47
- computeUsageWindows,
48
- resolveNorthStarEntry,
49
- northStarKeys,
50
- createNorthStarDerivationSubscriber,
51
- CANONICAL_COUNTERS,
52
- isCanonicalCounter,
53
- wireTelemetrySubscribers,
54
- getPluginTelemetrySubscribers,
55
- resetPluginTelemetrySubscribersForTests,
56
- };
@@ -1,82 +0,0 @@
1
- /**
2
- * Wraps a single integration handler invocation with a span + the
3
- * `frigg.handler.invocations` counter. Shared by the two
4
- * dispatch seams — `IntegrationBase.send()` and `IntegrationEventDispatcher` —
5
- * so both paths are instrumented identically.
6
- *
7
- * Cardinality discipline: the metric is keyed by the **bounded event type**
8
- * (USER_ACTION / CRON / QUEUE / WEBHOOK / LIFE_CYCLE_EVENT), never the specific
9
- * event/action name. High-cardinality ids (integrationId, userId) ride span
10
- * baggage via `telemetry.withContext`, never metric attributes. The full event
11
- * name is kept on the span only.
12
- *
13
- * @param {object|null} telemetry Telemetry service (no-op-safe; null → just runs fn).
14
- * @param {object} context Standard identifiers from `getTelemetryContext()`.
15
- * @param {{event: string, eventType: string}} descriptor Event name + bounded type.
16
- * @param {Function} fn The handler invocation.
17
- */
18
- async function instrumentHandler(telemetry, context = {}, descriptor = {}, fn) {
19
- if (!telemetry || typeof telemetry.span !== 'function') {
20
- return fn();
21
- }
22
-
23
- const integrationType = context.integrationType || 'unknown';
24
- const eventType = descriptor.eventType || 'unknown';
25
- const eventName = descriptor.event;
26
-
27
- const runInstrumented = () =>
28
- telemetry.span(`frigg.handler.${eventType}`, async (span) => {
29
- if (span && typeof span.setAttributes === 'function') {
30
- span.setAttributes({
31
- integration_type: integrationType,
32
- event: eventType,
33
- event_name: eventName,
34
- });
35
- }
36
- // `event_name` (potentially high-cardinality action id) rides the
37
- // bus-only context — used by North Star derived-from-trace matching,
38
- // never a metric label.
39
- const busContext = { event_name: eventName };
40
- try {
41
- const result = await fn();
42
- telemetry.count(
43
- 'frigg.handler.invocations',
44
- 1,
45
- {
46
- integration_type: integrationType,
47
- event: eventType,
48
- status: 'ok',
49
- },
50
- busContext
51
- );
52
- return result;
53
- } catch (err) {
54
- telemetry.count(
55
- 'frigg.handler.invocations',
56
- 1,
57
- {
58
- integration_type: integrationType,
59
- event: eventType,
60
- status: 'error',
61
- },
62
- busContext
63
- );
64
- throw err;
65
- }
66
- });
67
-
68
- if (typeof telemetry.withContext === 'function') {
69
- return telemetry.withContext(
70
- {
71
- integrationId: context.integrationId,
72
- userId: context.userId,
73
- integrationType: context.integrationType,
74
- version: context.version,
75
- },
76
- runInstrumented
77
- );
78
- }
79
- return runInstrumented();
80
- }
81
-
82
- module.exports = { instrumentHandler };
@@ -1,66 +0,0 @@
1
- const { createTelemetryEventBus } = require('./telemetry-event-bus');
2
- const {
3
- runWithTelemetryContext,
4
- mergeTelemetryContext,
5
- } = require('./telemetry-context');
6
-
7
- /**
8
- * No-op telemetry implementation.
9
- *
10
- * This is the default when no OTel exporter is configured, so telemetry "rides
11
- * for free": integration code can call `this.telemetry.*`
12
- * unconditionally and it costs nothing on the OTel side. Critically, this module
13
- * imports **zero** OpenTelemetry packages — the no-op path never loads the OTel
14
- * SDK (guarded by a require-graph test), protecting Lambda cold-start.
15
- *
16
- * The internal event bus is still active here (Decision 7): `count`/`event` are
17
- * mirrored onto it so the durable usage rollup and plugin/extension taps work
18
- * even with OTel export disabled. Emitting to a bus with no subscribers is a
19
- * cheap no-op.
20
- *
21
- * @param {object} [options]
22
- * @param {object} [options.bus] Event bus to mirror emissions onto.
23
- */
24
- function createNoOpTelemetry({ bus = createTelemetryEventBus() } = {}) {
25
- const noop = {
26
- count(name, value = 1, attributes = {}, context) {
27
- const merged = mergeTelemetryContext(context);
28
- const payload = { name, value, attributes };
29
- if (merged) payload.context = merged;
30
- bus.emit('metric', payload);
31
- },
32
- event(name, attributes = {}, context) {
33
- const merged = mergeTelemetryContext(context);
34
- const payload = { name, attributes };
35
- if (merged) payload.context = merged;
36
- bus.emit('event', payload);
37
- },
38
- async span(_name, fn) {
39
- return typeof fn === 'function' ? fn() : undefined;
40
- },
41
- startSpan() {
42
- return {
43
- setAttributes() {},
44
- setAttribute() {},
45
- recordException() {},
46
- setStatus() {},
47
- end() {},
48
- };
49
- },
50
- async withContext(context, fn) {
51
- return runWithTelemetryContext(context, () =>
52
- typeof fn === 'function' ? fn() : undefined
53
- );
54
- },
55
- on(eventType, callback) {
56
- return bus.on(eventType, callback);
57
- },
58
- async forceFlush() {},
59
- isEnabled() {
60
- return false;
61
- },
62
- };
63
- return noop;
64
- }
65
-
66
- module.exports = { createNoOpTelemetry };
@@ -1,103 +0,0 @@
1
- /**
2
- * Adopter North Star metric. A North Star simply references
3
- * a usage-counter key (canonical or custom); its value is then read like any
4
- * other counter via `frigg.usage.totals({ metric: northStarKey })`. It is
5
- * populated either by (a) direct emission of that counter, or (b) derived from a
6
- * framework trace signal — this module implements (b) as a small bus subscriber
7
- * that emits the North Star counter when a configured signal matches.
8
- */
9
-
10
- /** Resolve the North Star counter entry for an integration type (byType wins). */
11
- function resolveNorthStarEntry(northStar, integrationType) {
12
- if (!northStar) return null;
13
- return (
14
- (northStar.byType && northStar.byType[integrationType]) ||
15
- northStar.default ||
16
- null
17
- );
18
- }
19
-
20
- /** The set of counter keys any North Star references — added to trackedMetrics. */
21
- function northStarKeys(northStar) {
22
- const keys = new Set();
23
- if (!northStar) return keys;
24
- if (northStar.default?.name) keys.add(northStar.default.name);
25
- for (const entry of Object.values(northStar.byType || {})) {
26
- if (entry?.name) keys.add(entry.name);
27
- }
28
- return keys;
29
- }
30
-
31
- function matchesDeriveFrom(
32
- deriveFrom,
33
- { name, attributes = {}, context = {} }
34
- ) {
35
- if (!deriveFrom) return false;
36
-
37
- if (deriveFrom.userAction) {
38
- const rule = deriveFrom.userAction;
39
- return (
40
- name === 'frigg.handler.invocations' &&
41
- attributes.event === 'USER_ACTION' &&
42
- (!rule.action || context.event_name === rule.action)
43
- );
44
- }
45
-
46
- if (deriveFrom.apiRequest) {
47
- const rule = deriveFrom.apiRequest;
48
- return (
49
- name === 'frigg.apimodule.requests' &&
50
- (!rule.method || attributes.method === rule.method) &&
51
- (!rule.endpoint ||
52
- (typeof context.url === 'string' &&
53
- context.url.includes(rule.endpoint)))
54
- );
55
- }
56
-
57
- return false;
58
- }
59
-
60
- /**
61
- * Subscribe to the bus and emit the North Star counter whenever a configured
62
- * `deriveFrom` signal matches (config-only, no integration code). The emitted
63
- * counter flows back through the bus to the usage rollup like any other metric.
64
- */
65
- function createNorthStarDerivationSubscriber({ telemetry, northStar }) {
66
- if (!telemetry || !northStar) return { unsubscribe() {} };
67
-
68
- // Hoisted — the referenced-key set is fixed for the life of the subscriber.
69
- const keys = northStarKeys(northStar);
70
-
71
- function onMetric(payload) {
72
- try {
73
- // Never react to a North Star counter's own emission (no loop).
74
- if (keys.has(payload.name)) return;
75
-
76
- const integrationType =
77
- payload.context?.integrationType ||
78
- payload.attributes?.integration_type ||
79
- null;
80
- const entry = resolveNorthStarEntry(northStar, integrationType);
81
- if (!entry || !entry.deriveFrom) return;
82
- if (!matchesDeriveFrom(entry.deriveFrom, payload)) return;
83
-
84
- telemetry.count(
85
- entry.name,
86
- 1,
87
- integrationType ? { integration_type: integrationType } : {},
88
- payload.context
89
- );
90
- } catch (_) {
91
- // Derivation must never break emission.
92
- }
93
- }
94
-
95
- const unsubscribe = telemetry.on('metric', onMetric);
96
- return { unsubscribe };
97
- }
98
-
99
- module.exports = {
100
- resolveNorthStarEntry,
101
- northStarKeys,
102
- createNorthStarDerivationSubscriber,
103
- };
@@ -1,215 +0,0 @@
1
- const otelApi = require('@opentelemetry/api');
2
- const {
3
- BasicTracerProvider,
4
- BatchSpanProcessor,
5
- ParentBasedSampler,
6
- TraceIdRatioBasedSampler,
7
- AlwaysOnSampler,
8
- } = require('@opentelemetry/sdk-trace-base');
9
- const {
10
- MeterProvider,
11
- PeriodicExportingMetricReader,
12
- } = require('@opentelemetry/sdk-metrics');
13
- const { resourceFromAttributes } = require('@opentelemetry/resources');
14
- const { buildExporters } = require('./exporters/exporter-factory');
15
- const { createTelemetryEventBus } = require('./telemetry-event-bus');
16
- const {
17
- runWithTelemetryContext,
18
- mergeTelemetryContext,
19
- } = require('./telemetry-context');
20
-
21
- const TRACER_NAME = 'frigg';
22
- const METRIC_EXPORT_INTERVAL_MS =
23
- Number(process.env.OTEL_METRIC_EXPORT_INTERVAL_MS) || 60000;
24
-
25
- /**
26
- * Register an AsyncLocalStorage-backed OTel context manager once per process.
27
- * Without it `BasicTracerProvider` uses the Noop context manager, so
28
- * `startActiveSpan` never sets an active context — spans would be unparented
29
- * roots, `getActiveSpan()` always undefined, and baggage inert. Guarded so
30
- * repeated telemetry construction (tests) doesn't re-register.
31
- */
32
- let contextManagerRegistered = false;
33
- function ensureContextManager() {
34
- if (contextManagerRegistered) return;
35
- contextManagerRegistered = true;
36
- try {
37
- const {
38
- AsyncLocalStorageContextManager,
39
- } = require('@opentelemetry/context-async-hooks');
40
- const manager = new AsyncLocalStorageContextManager();
41
- manager.enable();
42
- otelApi.context.setGlobalContextManager(manager);
43
- } catch (_) {
44
- // If registration fails, traces are flat but usage attribution (which
45
- // rides the separate telemetry-context ALS) is unaffected.
46
- }
47
- }
48
-
49
- /**
50
- * The OpenTelemetry-backed telemetry service. Reached only
51
- * when a real exporter is configured; `createTelemetry` returns the no-op
52
- * otherwise. `BatchSpanProcessor` is used uniformly — spans are delivered by the
53
- * bounded `forceFlush()` the Lambda handler awaits before the container freezes
54
- * (`callbackWaitsForEmptyEventLoop=false`), never by background timers.
55
- */
56
- function createOtelTelemetry({
57
- exporter,
58
- resource = {},
59
- sampleRatio,
60
- bus = createTelemetryEventBus(),
61
- } = {}) {
62
- ensureContextManager();
63
- const { traceExporter, metricExporter } = buildExporters(exporter);
64
-
65
- const resourceAttrs = {
66
- 'service.name': resource.service || 'frigg',
67
- };
68
- if (resource.stage) {
69
- resourceAttrs['deployment.environment'] = resource.stage;
70
- resourceAttrs.stage = resource.stage;
71
- }
72
- if (resource.appName) resourceAttrs.appName = resource.appName;
73
- const res = resourceFromAttributes(resourceAttrs);
74
-
75
- const sampler =
76
- typeof sampleRatio === 'number' && sampleRatio < 1
77
- ? new ParentBasedSampler({
78
- root: new TraceIdRatioBasedSampler(sampleRatio),
79
- })
80
- : new AlwaysOnSampler();
81
-
82
- const tracerProvider = new BasicTracerProvider({
83
- resource: res,
84
- sampler,
85
- spanProcessors: traceExporter
86
- ? [new BatchSpanProcessor(traceExporter)]
87
- : [],
88
- });
89
-
90
- const metricReader = metricExporter
91
- ? new PeriodicExportingMetricReader({
92
- exporter: metricExporter,
93
- exportIntervalMillis: METRIC_EXPORT_INTERVAL_MS,
94
- })
95
- : null;
96
- const meterProvider = new MeterProvider({
97
- resource: res,
98
- readers: metricReader ? [metricReader] : [],
99
- });
100
-
101
- const tracer = tracerProvider.getTracer(TRACER_NAME);
102
- const meter = meterProvider.getMeter(TRACER_NAME);
103
-
104
- // Counters/UpDownCounters are cached — OTel requires one instrument instance
105
- // per metric name, and re-creating them would drop data points.
106
- const counters = new Map();
107
- function getCounter(name) {
108
- if (!counters.has(name)) counters.set(name, meter.createCounter(name));
109
- return counters.get(name);
110
- }
111
-
112
- const service = {
113
- count(name, value = 1, attributes = {}, context) {
114
- try {
115
- getCounter(name).add(value, attributes);
116
- } catch (_) {
117
- // Telemetry must never break the wrapped path.
118
- }
119
- // Mirror onto the internal stream for the usage rollup + plugin taps.
120
- // The bus `context` merges the ambient handler context (incl.
121
- // high-cardinality ids) with any explicit per-call context — it rides
122
- // the bus only, never the OTel metric attributes (Cardinality note).
123
- const merged = mergeTelemetryContext(context);
124
- const payload = { name, value, attributes };
125
- if (merged) payload.context = merged;
126
- bus.emit('metric', payload);
127
- },
128
-
129
- event(name, attributes = {}, context) {
130
- try {
131
- const active = otelApi.trace.getActiveSpan();
132
- if (active) active.addEvent(name, attributes);
133
- } catch (_) {}
134
- const merged = mergeTelemetryContext(context);
135
- const payload = { name, attributes };
136
- if (merged) payload.context = merged;
137
- bus.emit('event', payload);
138
- },
139
-
140
- startSpan(name, options) {
141
- return tracer.startSpan(name, options);
142
- },
143
-
144
- async span(name, fn, options = {}) {
145
- return tracer.startActiveSpan(name, options, async (span) => {
146
- try {
147
- const result =
148
- typeof fn === 'function' ? await fn(span) : undefined;
149
- span.setStatus({ code: otelApi.SpanStatusCode.OK });
150
- return result;
151
- } catch (err) {
152
- span.recordException(err);
153
- span.setStatus({
154
- code: otelApi.SpanStatusCode.ERROR,
155
- message: err && err.message,
156
- });
157
- throw err;
158
- } finally {
159
- span.end();
160
- }
161
- });
162
- },
163
-
164
- /**
165
- * Run `fn` with the given identifiers on (a) the AsyncLocalStorage
166
- * telemetry context — which the usage rollup reads to attribute emissions
167
- * per-integration on ANY path — and (b) OTel baggage for trace
168
- * propagation. High-cardinality ids ride here, never on metric labels.
169
- */
170
- async withContext(attributes = {}, fn) {
171
- const entries = {};
172
- for (const [key, value] of Object.entries(attributes)) {
173
- if (value !== undefined && value !== null) {
174
- entries[key] = { value: String(value) };
175
- }
176
- }
177
- const baggage = otelApi.propagation.createBaggage(entries);
178
- const ctx = otelApi.propagation.setBaggage(
179
- otelApi.context.active(),
180
- baggage
181
- );
182
- return runWithTelemetryContext(attributes, () =>
183
- otelApi.context.with(ctx, () =>
184
- typeof fn === 'function' ? fn() : undefined
185
- )
186
- );
187
- },
188
-
189
- on(eventType, callback) {
190
- return bus.on(eventType, callback);
191
- },
192
-
193
- async forceFlush() {
194
- await Promise.allSettled([
195
- tracerProvider.forceFlush(),
196
- meterProvider.forceFlush(),
197
- ]);
198
- },
199
-
200
- async shutdown() {
201
- await Promise.allSettled([
202
- tracerProvider.shutdown(),
203
- meterProvider.shutdown(),
204
- ]);
205
- },
206
-
207
- isEnabled() {
208
- return true;
209
- },
210
- };
211
-
212
- return service;
213
- }
214
-
215
- module.exports = { createOtelTelemetry };
@@ -1,51 +0,0 @@
1
- const { wireTelemetrySubscribers } = require('./plugin-subscribers');
2
-
3
- /**
4
- * Process-wide wiring of adopter-declared telemetry subscribers. Built once per
5
- * cold start and reused for the life of the Lambda
6
- * container, mirroring the usage-rollup singleton. Independent of the usage
7
- * rollup: adopter taps wire even when no integration declares `Definition.usage`.
8
- * Never throws — a bad/absent app definition just means no adopter taps.
9
- */
10
- let wired = null;
11
- let initialized = false;
12
-
13
- function getPluginTelemetrySubscribers() {
14
- if (initialized) return wired;
15
- initialized = true;
16
- try {
17
- const {
18
- loadAppDefinition,
19
- } = require('../handlers/app-definition-loader');
20
- const { telemetry = {} } = loadAppDefinition();
21
- const subscribers = telemetry.subscribers || [];
22
- if (subscribers.length === 0) {
23
- wired = [];
24
- return wired;
25
- }
26
-
27
- const { getTelemetry } = require('./telemetry-singleton');
28
- wired = wireTelemetrySubscribers({
29
- telemetry: getTelemetry(),
30
- subscribers,
31
- });
32
- } catch (error) {
33
- console.warn(
34
- `[Frigg][telemetry] plugin subscribers disabled: ${
35
- error && error.message
36
- }`
37
- );
38
- wired = [];
39
- }
40
- return wired;
41
- }
42
-
43
- function resetPluginTelemetrySubscribersForTests() {
44
- wired = null;
45
- initialized = false;
46
- }
47
-
48
- module.exports = {
49
- getPluginTelemetrySubscribers,
50
- resetPluginTelemetrySubscribersForTests,
51
- };