@loglayer/mixin-wide-events 1.0.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/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # @loglayer/mixin-wide-events
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@loglayer/mixin-wide-events.svg)](https://www.npmjs.com/package/@loglayer/mixin-wide-events)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@loglayer/mixin-wide-events.svg)](https://www.npmjs.com/package/@loglayer/mixin-wide-events)
5
+ [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
6
+
7
+ Adds wide event logging functionality to LogLayer for comprehensive, self-contained log entries.
8
+
9
+ [Documentation](https://loglayer.dev/mixins/wide-events) | [API Reference](https://loglayer.dev/mixins/wide-events)
package/dist/index.cjs ADDED
@@ -0,0 +1,213 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ //#region ../../core/plugin/dist/index.js
4
+ /**
5
+ * List of plugin callbacks that can be called by the plugin manager.
6
+ *
7
+ * @see {@link https://loglayer.dev/plugins/creating-plugins.html#transformloglevel | transformLogLevel Docs}
8
+ * @see {@link https://loglayer.dev/plugins/creating-plugins.html#onbeforedataout | onBeforeDataOut Docs}
9
+ * @see {@link https://loglayer.dev/plugins/creating-plugins.html#shouldsendtologger | shouldSendToLogger Docs}
10
+ * @see {@link https://loglayer.dev/plugins/creating-plugins.html#onmetadatacalled | onMetadataCalled Docs}
11
+ * @see {@link https://loglayer.dev/plugins/creating-plugins.html#onbeforemessageout | onBeforeMessageOut Docs}
12
+ * @see {@link https://loglayer.dev/plugins/creating-plugins.html#oncontextcalled | onContextCalled Docs}
13
+ */
14
+ let PluginCallbackType = /* @__PURE__ */ function(PluginCallbackType) {
15
+ PluginCallbackType["transformLogLevel"] = "transformLogLevel";
16
+ PluginCallbackType["onBeforeDataOut"] = "onBeforeDataOut";
17
+ PluginCallbackType["shouldSendToLogger"] = "shouldSendToLogger";
18
+ PluginCallbackType["onMetadataCalled"] = "onMetadataCalled";
19
+ PluginCallbackType["onBeforeMessageOut"] = "onBeforeMessageOut";
20
+ PluginCallbackType["onContextCalled"] = "onContextCalled";
21
+ return PluginCallbackType;
22
+ }({});
23
+
24
+ //#endregion
25
+ //#region ../../core/loglayer/dist/index.js
26
+ const CALLBACK_LIST = [
27
+ PluginCallbackType.onBeforeDataOut,
28
+ PluginCallbackType.onMetadataCalled,
29
+ PluginCallbackType.onBeforeMessageOut,
30
+ PluginCallbackType.transformLogLevel,
31
+ PluginCallbackType.shouldSendToLogger,
32
+ PluginCallbackType.onContextCalled
33
+ ];
34
+ /**
35
+ * The class that the mixin extends
36
+ */
37
+ let LogLayerMixinAugmentType = /* @__PURE__ */ function(LogLayerMixinAugmentType) {
38
+ /**
39
+ * Mixin extends the LogBuilder prototype
40
+ */
41
+ LogLayerMixinAugmentType["LogBuilder"] = "LogBuilder";
42
+ /**
43
+ * Mixin extends the LogLayer prototype
44
+ */
45
+ LogLayerMixinAugmentType["LogLayer"] = "LogLayer";
46
+ return LogLayerMixinAugmentType;
47
+ }({});
48
+
49
+ //#endregion
50
+ //#region src/mixin.ts
51
+ /**
52
+ * Creates a LogLayer mixin that adds wide event logging functionality.
53
+ *
54
+ * Wide events are comprehensive, self-contained log entries that capture an entire
55
+ * operation's context and data in a single emission. This pattern helps with
56
+ * observability by ensuring all relevant data is available in one log entry.
57
+ *
58
+ * @param options - Configuration options
59
+ * @param options.asyncContext - An async context implementation for propagating
60
+ * wide event data across async boundaries. For Node.js, use `new AsyncLocalStorage()`
61
+ * from `async_hooks`. For browser environments, provide your own compatible implementation.
62
+ * @param options.includeContext - Whether to include withContext() data in emitted wide events (default: true)
63
+
64
+ *
65
+ * @returns A LogLayer mixin registration object
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import { AsyncLocalStorage } from "async_hooks";
70
+ * import { LogLayer, StructuredTransport } from "loglayer";
71
+ * import { createWideEventMixin } from "@loglayer/mixin-wide-events";
72
+ *
73
+ * const wideEventMixin = createWideEventMixin({
74
+ * asyncContext: new AsyncLocalStorage(),
75
+ * });
76
+ *
77
+ * const log = new LogLayer({
78
+ * transport: new StructuredTransport({ logger: console }),
79
+ * mixins: [wideEventMixin],
80
+ * });
81
+ *
82
+ * // Create a child logger for the request
83
+ * const logger = log.child().withContext({ requestId: "123" });
84
+ *
85
+ * // Accumulate data throughout the request
86
+ * logger.withWideEvents({ userId: "456" });
87
+ * await doSomething();
88
+ * logger.withWideEvents({ orderId: "789" });
89
+ *
90
+ * // Emit the wide event
91
+ * logger.emitWideEvent({ message: "Request completed" });
92
+ * ```
93
+ */
94
+ function createWideEventMixin(options) {
95
+ const asyncContext = options.asyncContext;
96
+ const includeContext = options.includeContext ?? true;
97
+ const wideEventField = options.wideEventField;
98
+ /**
99
+ * Context tracker plugin - stores context in async storage
100
+ * for inclusion in wide events (only if includeContext is true)
101
+ */
102
+ const contextTrackerPlugin = {
103
+ id: `@loglayer/wide-events-context-tracker-${Date.now()}-${Math.random().toString(36).slice(2)}`,
104
+ onBeforeDataOut: (params) => {
105
+ if (!includeContext) return params;
106
+ const store = asyncContext.getStore();
107
+ if (store && params.context && Object.keys(params.context).length > 0) {
108
+ if (!store._llContext) store._llContext = {};
109
+ Object.assign(store._llContext, params.context);
110
+ }
111
+ return params;
112
+ }
113
+ };
114
+ /**
115
+ * Deep merge utility - merges source into target recursively for nested objects.
116
+ * Filters out dangerous keys to prevent prototype pollution attacks.
117
+ * Uses WeakRef to detect circular references.
118
+ */
119
+ function deepMerge(target, source, visited = /* @__PURE__ */ new WeakSet()) {
120
+ const result = { ...target };
121
+ for (const key of Object.keys(source)) {
122
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
123
+ const sourceValue = source[key];
124
+ const targetValue = result[key];
125
+ if (sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== null && typeof targetValue === "object" && !Array.isArray(targetValue)) {
126
+ if (visited.has(sourceValue)) continue;
127
+ visited.add(sourceValue);
128
+ result[key] = deepMerge(targetValue, sourceValue, visited);
129
+ } else result[key] = sourceValue;
130
+ }
131
+ return result;
132
+ }
133
+ /**
134
+ * Implementation for withWideEvents
135
+ */
136
+ function withWideEventsImpl(data, self) {
137
+ const store = asyncContext.getStore();
138
+ if (store) {
139
+ if (!store._llWideEvents) store._llWideEvents = {};
140
+ store._llWideEvents = deepMerge(store._llWideEvents, data);
141
+ }
142
+ return self;
143
+ }
144
+ /**
145
+ * Implementation for getWideEvents
146
+ */
147
+ function getWideEventsImpl(key, _self) {
148
+ const data = asyncContext.getStore()?._llWideEvents;
149
+ if (!data) return key ? void 0 : {};
150
+ if (key !== void 0) return data[key];
151
+ return { ...data };
152
+ }
153
+ /**
154
+ * Implementation for clearWideEvents
155
+ */
156
+ function clearWideEventsImpl(key, self) {
157
+ const store = asyncContext.getStore();
158
+ if (store?._llWideEvents) if (key !== void 0) delete store._llWideEvents[key];
159
+ else store._llWideEvents = {};
160
+ return self;
161
+ }
162
+ /**
163
+ * Implementation for emitWideEvent
164
+ */
165
+ function emitWideEventImpl(config, self) {
166
+ const store = asyncContext.getStore();
167
+ const wideEventData = {};
168
+ if (includeContext && store?._llContext) Object.assign(wideEventData, store._llContext);
169
+ if (store?._llWideEvents) Object.assign(wideEventData, store._llWideEvents);
170
+ if (config.metadata) Object.assign(wideEventData, config.metadata);
171
+ const level = config.level ?? "info";
172
+ const metadataToEmit = wideEventField ? { [wideEventField]: wideEventData } : wideEventData;
173
+ self.withMetadata(metadataToEmit)[level](config.message);
174
+ return self;
175
+ }
176
+ return {
177
+ mixinsToAdd: [{
178
+ augmentationType: LogLayerMixinAugmentType.LogLayer,
179
+ augment: (prototype) => {
180
+ prototype.withWideEvents = function(data) {
181
+ return withWideEventsImpl(data, this);
182
+ };
183
+ prototype.getWideEvents = function(key) {
184
+ return getWideEventsImpl(key, this);
185
+ };
186
+ prototype.clearWideEvents = function(key) {
187
+ return clearWideEventsImpl(key, this);
188
+ };
189
+ prototype.emitWideEvent = function(config) {
190
+ return emitWideEventImpl(config, this);
191
+ };
192
+ },
193
+ augmentMock: (prototype) => {
194
+ prototype.withWideEvents = function(data) {
195
+ return withWideEventsImpl(data, this);
196
+ };
197
+ prototype.getWideEvents = function(key) {
198
+ return getWideEventsImpl(key, this);
199
+ };
200
+ prototype.clearWideEvents = function(key) {
201
+ return clearWideEventsImpl(key, this);
202
+ };
203
+ prototype.emitWideEvent = function(config) {
204
+ return emitWideEventImpl(config, this);
205
+ };
206
+ }
207
+ }],
208
+ pluginsToAdd: [contextTrackerPlugin]
209
+ };
210
+ }
211
+
212
+ //#endregion
213
+ exports.createWideEventMixin = createWideEventMixin;