@loggerjs/codecs 0.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 loggerjs contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @loggerjs/codecs
2
+
3
+ Additional serialization codecs for transports.
4
+
5
+ ```ts
6
+ import { fastEventJsonCodec, msgpackrCodec, projectorCodec } from "@loggerjs/codecs";
7
+
8
+ const json = fastEventJsonCodec();
9
+ const msgpack = msgpackrCodec();
10
+
11
+ const projected = projectorCodec({
12
+ name: "ids-only",
13
+ contentType: "application/json",
14
+ project: (events) => events,
15
+ serialize: JSON.stringify,
16
+ });
17
+ ```
18
+
19
+ Codecs are transport-owned. Middleware should keep raw values and let the chosen transport serialize.
@@ -0,0 +1,24 @@
1
+ import { type Codec, type SafeStringifyOptions } from "@loggerjs/core";
2
+ /**
3
+ * Without any option set, encode runs on a native `JSON.stringify` fast path: nested
4
+ * `Error` values serialize as `{}` and circular or BigInt payloads trigger a safe
5
+ * re-encode of the whole input (circular refs become "[Circular]", BigInt becomes a
6
+ * string). Setting any {@link SafeStringifyOptions} field opts into the safe encoder
7
+ * everywhere, which also preserves `Error` name/message/stack inside data payloads.
8
+ *
9
+ * `includeId`, `includeSeq`, and `includeLevelName` trim the envelope for
10
+ * pino-shaped minimal NDJSON output; turning `includeId` off also skips id
11
+ * computation entirely on the record path.
12
+ */
13
+ export interface FastEventJsonCodecOptions extends SafeStringifyOptions {
14
+ includeContext?: boolean;
15
+ includeData?: boolean;
16
+ includeError?: boolean;
17
+ includeTrace?: boolean;
18
+ includeSource?: boolean;
19
+ includeId?: boolean;
20
+ includeSeq?: boolean;
21
+ includeLevelName?: boolean;
22
+ }
23
+ export declare function fastEventJsonCodec(options?: FastEventJsonCodecOptions): Codec<string>;
24
+ //# sourceMappingURL=fast-event-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fast-event-json.d.ts","sourceRoot":"","sources":["../src/fast-event-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,KAAK,EAIV,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,MAAM,WAAW,yBAA0B,SAAQ,oBAAoB;IACrE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA4RD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,KAAK,CAAC,MAAM,CAAC,CAoCzF"}
package/dist/index.cjs ADDED
@@ -0,0 +1,253 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _loggerjs_core = require("@loggerjs/core");
3
+ let msgpackr = require("msgpackr");
4
+ //#region src/fast-event-json.ts
5
+ function hasSafeOptions(options) {
6
+ return options.maxDepth !== void 0 || options.maxArrayLength !== void 0 || options.maxObjectKeys !== void 0 || options.includeStack !== void 0 || options.stable !== void 0 || options.space !== void 0;
7
+ }
8
+ function canUseNativeEventJson(options) {
9
+ return !hasSafeOptions(options) && options.includeContext !== false && options.includeData !== false && options.includeError !== false && options.includeTrace !== false && options.includeSource !== false && options.includeId !== false && options.includeSeq !== false && options.includeLevelName !== false;
10
+ }
11
+ function hasFullHeader(options) {
12
+ return (options.includeId ?? true) && (options.includeSeq ?? true) && (options.includeLevelName ?? true);
13
+ }
14
+ function createStringify(options) {
15
+ if (hasSafeOptions(options)) return (value) => (0, _loggerjs_core.safeJsonStringify)(value, options);
16
+ return JSON.stringify;
17
+ }
18
+ function appendField(output, name, value, stringify) {
19
+ if (value === void 0) return output;
20
+ const encoded = stringify(value);
21
+ return encoded === void 0 ? output : `${output},"${name}":${encoded}`;
22
+ }
23
+ function asJsonString(value) {
24
+ if (value.length > 256) return JSON.stringify(value);
25
+ for (let index = 0; index < value.length; index += 1) {
26
+ const code = value.charCodeAt(index);
27
+ if (code === 34 || code === 92 || code < 32) return JSON.stringify(value);
28
+ }
29
+ return `"${value}"`;
30
+ }
31
+ function tryFlatObjectJson(value) {
32
+ const proto = Object.getPrototypeOf(value);
33
+ if (proto !== Object.prototype && proto !== null) return void 0;
34
+ let output = "{";
35
+ let first = true;
36
+ for (const key of Object.keys(value)) {
37
+ const item = value[key];
38
+ let encoded;
39
+ switch (typeof item) {
40
+ case "string":
41
+ encoded = asJsonString(item);
42
+ break;
43
+ case "number":
44
+ encoded = Number.isFinite(item) ? String(item) : "null";
45
+ break;
46
+ case "boolean":
47
+ encoded = item ? "true" : "false";
48
+ break;
49
+ case "object":
50
+ if (item !== null) return void 0;
51
+ encoded = "null";
52
+ break;
53
+ case "undefined": continue;
54
+ default: return;
55
+ }
56
+ output += `${first ? "" : ","}${asJsonString(key)}:${encoded}`;
57
+ first = false;
58
+ }
59
+ return `${output}}`;
60
+ }
61
+ function appendDataField(output, value, stringify) {
62
+ if (value === void 0) return output;
63
+ const flat = tryFlatObjectJson(value);
64
+ if (flat !== void 0) return `${output},"data":${flat}`;
65
+ return appendField(output, "data", value, stringify);
66
+ }
67
+ let lastFragmentTime = -1;
68
+ let lastTimeFragment = "";
69
+ function timeFragment(time) {
70
+ if (time !== lastFragmentTime) {
71
+ lastFragmentTime = time;
72
+ lastTimeFragment = `","time":${time},"seq":`;
73
+ }
74
+ return lastTimeFragment;
75
+ }
76
+ const levelFragments = /* @__PURE__ */ new Map();
77
+ function levelFragment(level) {
78
+ let fragment = levelFragments.get(level);
79
+ if (fragment === void 0) {
80
+ fragment = `,"level":${level},"levelName":"${(0, _loggerjs_core.toLevelName)(level)}","logger":`;
81
+ levelFragments.set(level, fragment);
82
+ }
83
+ return fragment;
84
+ }
85
+ const categoryFragments = /* @__PURE__ */ new WeakMap();
86
+ function loggerFragment(category) {
87
+ let fragment = categoryFragments.get(category);
88
+ if (fragment === void 0) {
89
+ fragment = asJsonString(category.join("."));
90
+ categoryFragments.set(category, fragment);
91
+ }
92
+ return fragment;
93
+ }
94
+ function errorForRecord(record) {
95
+ if (record.err === null || record.err === void 0) return void 0;
96
+ return (0, _loggerjs_core.normalizeError)(record.err);
97
+ }
98
+ function encodeEvent(event, options, stringify, tagsFragment) {
99
+ let output;
100
+ if (hasFullHeader(options)) output = `{"id":${asJsonString(event.id)},"time":${event.time},"seq":${event.seq},"level":${event.level},"levelName":${asJsonString(event.levelName)},"logger":${asJsonString(event.logger)},"message":${asJsonString(event.message)}`;
101
+ else {
102
+ output = "{";
103
+ if (options.includeId ?? true) output += `"id":${asJsonString(event.id)},`;
104
+ output += `"time":${event.time}`;
105
+ if (options.includeSeq ?? true) output += `,"seq":${event.seq}`;
106
+ output += `,"level":${event.level}`;
107
+ if (options.includeLevelName ?? true) output += `,"levelName":${asJsonString(event.levelName)}`;
108
+ output += `,"logger":${asJsonString(event.logger)},"message":${asJsonString(event.message)}`;
109
+ }
110
+ if (event.type !== void 0) output += `,"type":${asJsonString(event.type)}`;
111
+ if (event.tags !== void 0) output += tagsFragment(event.tags);
112
+ if (options.includeData ?? true) output = appendField(output, "data", event.data, stringify);
113
+ if (options.includeError ?? true) output = appendField(output, "error", event.error, stringify);
114
+ if (options.includeContext ?? true) output = appendField(output, "context", event.context, stringify);
115
+ if (options.includeTrace ?? true) output = appendField(output, "trace", event.trace, stringify);
116
+ if (options.includeSource ?? true) output = appendField(output, "source", event.source, stringify);
117
+ return `${output}}`;
118
+ }
119
+ function encodeRecord(record, options, stringify, tagsFragment) {
120
+ const levelName = (0, _loggerjs_core.toLevelName)(record.level);
121
+ let output;
122
+ if (hasFullHeader(options)) output = `{"id":"${(0, _loggerjs_core.defaultRecordId)(record, levelName)}${timeFragment(record.time)}${record.seq}${levelFragment(record.level)}${loggerFragment(record.category)},"message":${asJsonString((0, _loggerjs_core.resolveMessage)(record))}`;
123
+ else {
124
+ output = "{";
125
+ if (options.includeId ?? true) output += `"id":"${(0, _loggerjs_core.defaultRecordId)(record, levelName)}",`;
126
+ output += `"time":${record.time}`;
127
+ if (options.includeSeq ?? true) output += `,"seq":${record.seq}`;
128
+ output += `,"level":${record.level}`;
129
+ if (options.includeLevelName ?? true) output += `,"levelName":"${levelName}"`;
130
+ output += `,"logger":${loggerFragment(record.category)},"message":${asJsonString((0, _loggerjs_core.resolveMessage)(record))}`;
131
+ }
132
+ if (record.type !== null) output += `,"type":${asJsonString(record.type)}`;
133
+ if (record.tags !== null) output += tagsFragment(record.tags);
134
+ if (options.includeData ?? true) output = appendDataField(output, record.props ?? void 0, stringify);
135
+ if (options.includeError ?? true) output = appendField(output, "error", errorForRecord(record), stringify);
136
+ if (options.includeContext ?? true) output = appendField(output, "context", record.ctx ?? void 0, stringify);
137
+ if (options.includeTrace ?? true) output = appendField(output, "trace", record.trace ?? void 0, stringify);
138
+ if ((options.includeSource ?? true) && record.source !== "app") output += `,"source":{"integration":${asJsonString(record.source)}}`;
139
+ return `${output}}`;
140
+ }
141
+ function encodeItem(item, options, stringify, tagsFragment) {
142
+ return (0, _loggerjs_core.isLogRecord)(item) ? encodeRecord(item, options, stringify, tagsFragment) : encodeEvent(item, options, stringify, tagsFragment);
143
+ }
144
+ function encodeArray(items, options, stringify, tagsFragment) {
145
+ let output = "[";
146
+ for (let index = 0; index < items.length; index += 1) {
147
+ if (index > 0) output += ",";
148
+ output += encodeItem(items[index], options, stringify, tagsFragment);
149
+ }
150
+ return `${output}]`;
151
+ }
152
+ function isCodecArray(input) {
153
+ return Array.isArray(input);
154
+ }
155
+ function hasLogRecord(items) {
156
+ for (const item of items) if ((0, _loggerjs_core.isLogRecord)(item)) return true;
157
+ return false;
158
+ }
159
+ function createTagsFragment(encoder, cached) {
160
+ const fragments = cached ? /* @__PURE__ */ new WeakMap() : void 0;
161
+ return (tags) => {
162
+ if (fragments && Object.isFrozen(tags)) {
163
+ let fragment = fragments.get(tags);
164
+ if (fragment === void 0) {
165
+ const encoded = encoder(tags);
166
+ fragment = encoded === void 0 ? "" : `,"tags":${encoded}`;
167
+ fragments.set(tags, fragment);
168
+ }
169
+ return fragment;
170
+ }
171
+ const encoded = encoder(tags);
172
+ return encoded === void 0 ? "" : `,"tags":${encoded}`;
173
+ };
174
+ }
175
+ function fastEventJsonCodec(options = {}) {
176
+ const stringify = createStringify(options);
177
+ const safeStringify = (value) => (0, _loggerjs_core.safeJsonStringify)(value, options);
178
+ const useNativeEventJson = canUseNativeEventJson(options);
179
+ const tagsFragment = createTagsFragment(stringify, true);
180
+ const safeTagsFragment = createTagsFragment(safeStringify, false);
181
+ const fastEncode = (input) => {
182
+ if (isCodecArray(input)) {
183
+ if (useNativeEventJson && !hasLogRecord(input)) return JSON.stringify(input);
184
+ return encodeArray(input, options, stringify, tagsFragment);
185
+ }
186
+ if (useNativeEventJson && !(0, _loggerjs_core.isLogRecord)(input)) return JSON.stringify(input);
187
+ return encodeItem(input, options, stringify, tagsFragment);
188
+ };
189
+ return {
190
+ name: "fast-event-json",
191
+ contentType: "application/json",
192
+ encode(input) {
193
+ try {
194
+ return fastEncode(input);
195
+ } catch {
196
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("codec.fallback");
197
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("codec.fallback.fast-event-json");
198
+ }
199
+ if (isCodecArray(input)) return encodeArray(input, options, safeStringify, safeTagsFragment);
200
+ return encodeItem(input, options, safeStringify, safeTagsFragment);
201
+ },
202
+ decode(payload) {
203
+ return JSON.parse(payload);
204
+ }
205
+ };
206
+ }
207
+ //#endregion
208
+ //#region src/msgpackr.ts
209
+ function isMsgpackRuntime(value) {
210
+ return typeof value.pack === "function" && typeof value.unpack === "function";
211
+ }
212
+ function msgpackrCodec(options = {}) {
213
+ const runtime = isMsgpackRuntime(options) ? options : new msgpackr.Packr({
214
+ useRecords: true,
215
+ ...options
216
+ });
217
+ return {
218
+ name: "msgpackr",
219
+ contentType: "application/msgpack",
220
+ encode(input) {
221
+ return runtime.pack((0, _loggerjs_core.normalizeCodecInput)(input));
222
+ },
223
+ decode(payload) {
224
+ return runtime.unpack(payload);
225
+ }
226
+ };
227
+ }
228
+ //#endregion
229
+ //#region src/projector.ts
230
+ function projectorCodec(options) {
231
+ return {
232
+ name: options.name,
233
+ contentType: options.contentType,
234
+ encode(input) {
235
+ return options.serialize(options.project((0, _loggerjs_core.normalizeCodecInput)(input)));
236
+ },
237
+ decode: options.parse && options.unproject ? (payload) => options.unproject(options.parse(payload)) : void 0
238
+ };
239
+ }
240
+ //#endregion
241
+ exports.fastEventJsonCodec = fastEventJsonCodec;
242
+ exports.msgpackrCodec = msgpackrCodec;
243
+ exports.projectorCodec = projectorCodec;
244
+ Object.keys(_loggerjs_core).forEach(function(k) {
245
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
246
+ enumerable: true,
247
+ get: function() {
248
+ return _loggerjs_core[k];
249
+ }
250
+ });
251
+ });
252
+
253
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["Packr"],"sources":["../src/fast-event-json.ts","../src/msgpackr.ts","../src/projector.ts"],"sourcesContent":["import {\n defaultRecordId,\n incrementLoggerMetaCounter,\n isLogRecord,\n normalizeError,\n resolveMessage,\n toLevelName,\n safeJsonStringify,\n type Codec,\n type CodecInput,\n type LogEvent,\n type LogRecord,\n type SafeStringifyOptions,\n} from \"@loggerjs/core\";\n\n/**\n * Without any option set, encode runs on a native `JSON.stringify` fast path: nested\n * `Error` values serialize as `{}` and circular or BigInt payloads trigger a safe\n * re-encode of the whole input (circular refs become \"[Circular]\", BigInt becomes a\n * string). Setting any {@link SafeStringifyOptions} field opts into the safe encoder\n * everywhere, which also preserves `Error` name/message/stack inside data payloads.\n *\n * `includeId`, `includeSeq`, and `includeLevelName` trim the envelope for\n * pino-shaped minimal NDJSON output; turning `includeId` off also skips id\n * computation entirely on the record path.\n */\nexport interface FastEventJsonCodecOptions extends SafeStringifyOptions {\n includeContext?: boolean;\n includeData?: boolean;\n includeError?: boolean;\n includeTrace?: boolean;\n includeSource?: boolean;\n includeId?: boolean;\n includeSeq?: boolean;\n includeLevelName?: boolean;\n}\n\ntype JsonStringify = (value: unknown) => string | undefined;\n\nfunction hasSafeOptions(options: SafeStringifyOptions): boolean {\n return (\n options.maxDepth !== undefined ||\n options.maxArrayLength !== undefined ||\n options.maxObjectKeys !== undefined ||\n options.includeStack !== undefined ||\n options.stable !== undefined ||\n options.space !== undefined\n );\n}\n\nfunction canUseNativeEventJson(options: FastEventJsonCodecOptions): boolean {\n return (\n !hasSafeOptions(options) &&\n options.includeContext !== false &&\n options.includeData !== false &&\n options.includeError !== false &&\n options.includeTrace !== false &&\n options.includeSource !== false &&\n options.includeId !== false &&\n options.includeSeq !== false &&\n options.includeLevelName !== false\n );\n}\n\nfunction hasFullHeader(options: FastEventJsonCodecOptions): boolean {\n return (\n (options.includeId ?? true) &&\n (options.includeSeq ?? true) &&\n (options.includeLevelName ?? true)\n );\n}\n\nfunction createStringify(options: SafeStringifyOptions): JsonStringify {\n if (hasSafeOptions(options)) return (value) => safeJsonStringify(value, options);\n return JSON.stringify;\n}\n\nfunction appendField(\n output: string,\n name: string,\n value: unknown,\n stringify: JsonStringify,\n): string {\n if (value === undefined) return output;\n const encoded = stringify(value);\n return encoded === undefined ? output : `${output},\"${name}\":${encoded}`;\n}\n\n// JSON.stringify costs ~35ns per short string mostly in call overhead. Short\n// strings without quotes, backslashes, or control characters can be quoted\n// directly after a linear scan.\nfunction asJsonString(value: string): string {\n if (value.length > 256) return JSON.stringify(value);\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code === 34 || code === 92 || code < 32) return JSON.stringify(value);\n }\n return `\"${value}\"`;\n}\n\n// Data payloads are usually small flat objects of primitives. Writing them\n// directly skips the native JSON.stringify call overhead; any nested or\n// exotic value bails out so the caller can use the configured stringifier.\nfunction tryFlatObjectJson(value: Record<string, unknown>): string | undefined {\n // Class instances may carry toJSON or accessors; only plain objects take\n // the fast path so behavior matches JSON.stringify.\n const proto = Object.getPrototypeOf(value) as unknown;\n if (proto !== Object.prototype && proto !== null) return undefined;\n let output = \"{\";\n let first = true;\n for (const key of Object.keys(value)) {\n const item = value[key];\n let encoded: string;\n switch (typeof item) {\n case \"string\":\n encoded = asJsonString(item);\n break;\n case \"number\":\n encoded = Number.isFinite(item) ? String(item) : \"null\";\n break;\n case \"boolean\":\n encoded = item ? \"true\" : \"false\";\n break;\n case \"object\":\n if (item !== null) return undefined;\n encoded = \"null\";\n break;\n case \"undefined\":\n continue;\n default:\n return undefined;\n }\n output += `${first ? \"\" : \",\"}${asJsonString(key)}:${encoded}`;\n first = false;\n }\n return `${output}}`;\n}\n\nfunction appendDataField(\n output: string,\n value: Record<string, unknown> | undefined,\n stringify: JsonStringify,\n): string {\n if (value === undefined) return output;\n const flat = tryFlatObjectJson(value);\n if (flat !== undefined) return `${output},\"data\":${flat}`;\n return appendField(output, \"data\", value, stringify);\n}\n\n// The timestamp changes once per millisecond while bursts of records share\n// it, so the rendered `\",\"time\":...,\"seq\":` fragment can be memoized the same\n// way the default id memoizes its time segment.\nlet lastFragmentTime = -1;\nlet lastTimeFragment = \"\";\n\nfunction timeFragment(time: number): string {\n if (time !== lastFragmentTime) {\n lastFragmentTime = time;\n lastTimeFragment = `\",\"time\":${time},\"seq\":`;\n }\n return lastTimeFragment;\n}\n\n// level -> ',\"level\":30,\"levelName\":\"info\",\"logger\":' — derived from the\n// numeric level alone, so a module-level cache is safe.\nconst levelFragments = new Map<number, string>();\n\nfunction levelFragment(level: number): string {\n let fragment = levelFragments.get(level);\n if (fragment === undefined) {\n fragment = `,\"level\":${level},\"levelName\":\"${toLevelName(level)}\",\"logger\":`;\n levelFragments.set(level, fragment);\n }\n return fragment;\n}\n\n// Loggers reuse one frozen category array for every record, so the joined and\n// escaped logger name can be cached per array identity.\nconst categoryFragments = new WeakMap<readonly string[], string>();\n\nfunction loggerFragment(category: readonly string[]): string {\n let fragment = categoryFragments.get(category);\n if (fragment === undefined) {\n fragment = asJsonString(category.join(\".\"));\n categoryFragments.set(category, fragment);\n }\n return fragment;\n}\n\nfunction errorForRecord(record: LogRecord): LogEvent[\"error\"] | undefined {\n if (record.err === null || record.err === undefined) return undefined;\n return normalizeError(record.err);\n}\n\nfunction encodeEvent(\n event: LogEvent,\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: (tags: NonNullable<LogEvent[\"tags\"]>) => string,\n): string {\n let output: string;\n if (hasFullHeader(options)) {\n output = `{\"id\":${asJsonString(event.id)},\"time\":${event.time},\"seq\":${event.seq},\"level\":${event.level},\"levelName\":${asJsonString(event.levelName)},\"logger\":${asJsonString(event.logger)},\"message\":${asJsonString(event.message)}`;\n } else {\n output = \"{\";\n if (options.includeId ?? true) output += `\"id\":${asJsonString(event.id)},`;\n output += `\"time\":${event.time}`;\n if (options.includeSeq ?? true) output += `,\"seq\":${event.seq}`;\n output += `,\"level\":${event.level}`;\n if (options.includeLevelName ?? true) output += `,\"levelName\":${asJsonString(event.levelName)}`;\n output += `,\"logger\":${asJsonString(event.logger)},\"message\":${asJsonString(event.message)}`;\n }\n if (event.type !== undefined) output += `,\"type\":${asJsonString(event.type)}`;\n if (event.tags !== undefined) output += tagsFragment(event.tags);\n if (options.includeData ?? true) output = appendField(output, \"data\", event.data, stringify);\n if (options.includeError ?? true) output = appendField(output, \"error\", event.error, stringify);\n if (options.includeContext ?? true)\n output = appendField(output, \"context\", event.context, stringify);\n if (options.includeTrace ?? true) output = appendField(output, \"trace\", event.trace, stringify);\n if (options.includeSource ?? true)\n output = appendField(output, \"source\", event.source, stringify);\n return `${output}}`;\n}\n\nfunction encodeRecord(\n record: LogRecord,\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: (tags: NonNullable<LogRecord[\"tags\"]>) => string,\n): string {\n const levelName = toLevelName(record.level);\n let output: string;\n if (hasFullHeader(options)) {\n // The default id only contains [0-9a-z-] and the level name, so it never\n // needs escaping.\n output = `{\"id\":\"${defaultRecordId(record, levelName)}${timeFragment(record.time)}${record.seq}${levelFragment(record.level)}${loggerFragment(record.category)},\"message\":${asJsonString(resolveMessage(record))}`;\n } else {\n output = \"{\";\n if (options.includeId ?? true) output += `\"id\":\"${defaultRecordId(record, levelName)}\",`;\n output += `\"time\":${record.time}`;\n if (options.includeSeq ?? true) output += `,\"seq\":${record.seq}`;\n output += `,\"level\":${record.level}`;\n if (options.includeLevelName ?? true) output += `,\"levelName\":\"${levelName}\"`;\n output += `,\"logger\":${loggerFragment(record.category)},\"message\":${asJsonString(resolveMessage(record))}`;\n }\n if (record.type !== null) output += `,\"type\":${asJsonString(record.type)}`;\n if (record.tags !== null) output += tagsFragment(record.tags);\n if (options.includeData ?? true)\n output = appendDataField(output, record.props ?? undefined, stringify);\n if (options.includeError ?? true)\n output = appendField(output, \"error\", errorForRecord(record), stringify);\n if (options.includeContext ?? true)\n output = appendField(output, \"context\", record.ctx ?? undefined, stringify);\n if (options.includeTrace ?? true)\n output = appendField(output, \"trace\", record.trace ?? undefined, stringify);\n if ((options.includeSource ?? true) && record.source !== \"app\")\n output += `,\"source\":{\"integration\":${asJsonString(record.source)}}`;\n return `${output}}`;\n}\n\ntype TagsFragment = (tags: NonNullable<LogEvent[\"tags\"]>) => string;\n\nfunction encodeItem(\n item: LogEvent | LogRecord,\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: TagsFragment,\n): string {\n return isLogRecord(item)\n ? encodeRecord(item, options, stringify, tagsFragment)\n : encodeEvent(item, options, stringify, tagsFragment);\n}\n\nfunction encodeArray(\n items: readonly (LogEvent | LogRecord)[],\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: TagsFragment,\n): string {\n let output = \"[\";\n for (let index = 0; index < items.length; index += 1) {\n if (index > 0) output += \",\";\n output += encodeItem(items[index]!, options, stringify, tagsFragment);\n }\n return `${output}]`;\n}\n\nfunction isCodecArray(input: CodecInput): input is readonly (LogEvent | LogRecord)[] {\n return Array.isArray(input);\n}\n\nfunction hasLogRecord(items: readonly (LogEvent | LogRecord)[]): boolean {\n for (const item of items) {\n if (isLogRecord(item)) return true;\n }\n return false;\n}\n\n// Loggers share one frozen tags object across records, so the encoded\n// fragment can be cached per object identity. Mutable tags objects are\n// re-encoded every time. Callers create one fragment encoder per codec\n// because it bakes in that codec's stringify options.\nfunction createTagsFragment(encoder: JsonStringify, cached: boolean): TagsFragment {\n const fragments = cached ? new WeakMap<object, string>() : undefined;\n return (tags) => {\n if (fragments && Object.isFrozen(tags)) {\n let fragment = fragments.get(tags);\n if (fragment === undefined) {\n const encoded = encoder(tags);\n fragment = encoded === undefined ? \"\" : `,\"tags\":${encoded}`;\n fragments.set(tags, fragment);\n }\n return fragment;\n }\n const encoded = encoder(tags);\n return encoded === undefined ? \"\" : `,\"tags\":${encoded}`;\n };\n}\n\nexport function fastEventJsonCodec(options: FastEventJsonCodecOptions = {}): Codec<string> {\n const stringify = createStringify(options);\n const safeStringify: JsonStringify = (value) => safeJsonStringify(value, options);\n const useNativeEventJson = canUseNativeEventJson(options);\n const tagsFragment = createTagsFragment(stringify, true);\n // The fallback must not reuse the fast fragments: a cached entry or the\n // fast stringify could throw on the exact payload that triggered fallback.\n const safeTagsFragment = createTagsFragment(safeStringify, false);\n const fastEncode = (input: CodecInput): string => {\n if (isCodecArray(input)) {\n if (useNativeEventJson && !hasLogRecord(input)) return JSON.stringify(input);\n return encodeArray(input, options, stringify, tagsFragment);\n }\n if (useNativeEventJson && !isLogRecord(input)) return JSON.stringify(input);\n return encodeItem(input, options, stringify, tagsFragment);\n };\n return {\n name: \"fast-event-json\",\n contentType: \"application/json\",\n encode(input: CodecInput) {\n // Circular references and BigInt values throw on the native fast path. Logs\n // must never be lost to encoding, so re-encode the input with the safe\n // stringifier instead of surfacing the error as a transport failure.\n try {\n return fastEncode(input);\n } catch {\n incrementLoggerMetaCounter(\"codec.fallback\");\n incrementLoggerMetaCounter(\"codec.fallback.fast-event-json\");\n }\n if (isCodecArray(input)) return encodeArray(input, options, safeStringify, safeTagsFragment);\n return encodeItem(input, options, safeStringify, safeTagsFragment);\n },\n decode(payload) {\n return JSON.parse(payload) as LogEvent | LogEvent[];\n },\n };\n}\n","import { normalizeCodecInput, type Codec, type CodecInput, type LogEvent } from \"@loggerjs/core\";\nimport { Packr, type Options as MsgpackrOptions } from \"msgpackr\";\n\nexport interface MsgpackRuntime {\n pack: (input: unknown) => Uint8Array;\n unpack: (payload: Uint8Array) => unknown;\n}\n\nexport type MsgpackrCodecOptions = MsgpackrOptions;\n\nfunction isMsgpackRuntime(value: MsgpackRuntime | MsgpackrCodecOptions): value is MsgpackRuntime {\n return (\n typeof (value as MsgpackRuntime).pack === \"function\" &&\n typeof (value as MsgpackRuntime).unpack === \"function\"\n );\n}\n\nexport function msgpackrCodec(\n options: MsgpackRuntime | MsgpackrCodecOptions = {},\n): Codec<Uint8Array> {\n const runtime = isMsgpackRuntime(options) ? options : new Packr({ useRecords: true, ...options });\n return {\n name: \"msgpackr\",\n contentType: \"application/msgpack\",\n encode(input: CodecInput) {\n return runtime.pack(normalizeCodecInput(input));\n },\n decode(payload: Uint8Array) {\n return runtime.unpack(payload) as LogEvent | LogEvent[];\n },\n };\n}\n","import { normalizeCodecInput, type Codec, type CodecInput, type LogEvent } from \"@loggerjs/core\";\n\nexport interface ProjectorCodecOptions<TWire> {\n name: string;\n contentType: string;\n project: (input: LogEvent | LogEvent[]) => TWire;\n serialize: (wire: TWire) => string | Uint8Array;\n parse?: (payload: string | Uint8Array) => TWire;\n unproject?: (wire: TWire) => LogEvent | LogEvent[];\n}\n\nexport function projectorCodec<TWire>(\n options: ProjectorCodecOptions<TWire>,\n): Codec<string | Uint8Array> {\n return {\n name: options.name,\n contentType: options.contentType,\n encode(input: CodecInput) {\n return options.serialize(options.project(normalizeCodecInput(input)));\n },\n decode:\n options.parse && options.unproject\n ? (payload) => options.unproject!(options.parse!(payload))\n : undefined,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from "@loggerjs/core";
2
+ export * from "./fast-event-json.js";
3
+ export * from "./msgpackr.js";
4
+ export * from "./projector.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,243 @@
1
+ import { defaultRecordId, incrementLoggerMetaCounter, isLogRecord, normalizeCodecInput, normalizeError, resolveMessage, safeJsonStringify, toLevelName } from "@loggerjs/core";
2
+ import { Packr } from "msgpackr";
3
+ export * from "@loggerjs/core";
4
+ //#region src/fast-event-json.ts
5
+ function hasSafeOptions(options) {
6
+ return options.maxDepth !== void 0 || options.maxArrayLength !== void 0 || options.maxObjectKeys !== void 0 || options.includeStack !== void 0 || options.stable !== void 0 || options.space !== void 0;
7
+ }
8
+ function canUseNativeEventJson(options) {
9
+ return !hasSafeOptions(options) && options.includeContext !== false && options.includeData !== false && options.includeError !== false && options.includeTrace !== false && options.includeSource !== false && options.includeId !== false && options.includeSeq !== false && options.includeLevelName !== false;
10
+ }
11
+ function hasFullHeader(options) {
12
+ return (options.includeId ?? true) && (options.includeSeq ?? true) && (options.includeLevelName ?? true);
13
+ }
14
+ function createStringify(options) {
15
+ if (hasSafeOptions(options)) return (value) => safeJsonStringify(value, options);
16
+ return JSON.stringify;
17
+ }
18
+ function appendField(output, name, value, stringify) {
19
+ if (value === void 0) return output;
20
+ const encoded = stringify(value);
21
+ return encoded === void 0 ? output : `${output},"${name}":${encoded}`;
22
+ }
23
+ function asJsonString(value) {
24
+ if (value.length > 256) return JSON.stringify(value);
25
+ for (let index = 0; index < value.length; index += 1) {
26
+ const code = value.charCodeAt(index);
27
+ if (code === 34 || code === 92 || code < 32) return JSON.stringify(value);
28
+ }
29
+ return `"${value}"`;
30
+ }
31
+ function tryFlatObjectJson(value) {
32
+ const proto = Object.getPrototypeOf(value);
33
+ if (proto !== Object.prototype && proto !== null) return void 0;
34
+ let output = "{";
35
+ let first = true;
36
+ for (const key of Object.keys(value)) {
37
+ const item = value[key];
38
+ let encoded;
39
+ switch (typeof item) {
40
+ case "string":
41
+ encoded = asJsonString(item);
42
+ break;
43
+ case "number":
44
+ encoded = Number.isFinite(item) ? String(item) : "null";
45
+ break;
46
+ case "boolean":
47
+ encoded = item ? "true" : "false";
48
+ break;
49
+ case "object":
50
+ if (item !== null) return void 0;
51
+ encoded = "null";
52
+ break;
53
+ case "undefined": continue;
54
+ default: return;
55
+ }
56
+ output += `${first ? "" : ","}${asJsonString(key)}:${encoded}`;
57
+ first = false;
58
+ }
59
+ return `${output}}`;
60
+ }
61
+ function appendDataField(output, value, stringify) {
62
+ if (value === void 0) return output;
63
+ const flat = tryFlatObjectJson(value);
64
+ if (flat !== void 0) return `${output},"data":${flat}`;
65
+ return appendField(output, "data", value, stringify);
66
+ }
67
+ let lastFragmentTime = -1;
68
+ let lastTimeFragment = "";
69
+ function timeFragment(time) {
70
+ if (time !== lastFragmentTime) {
71
+ lastFragmentTime = time;
72
+ lastTimeFragment = `","time":${time},"seq":`;
73
+ }
74
+ return lastTimeFragment;
75
+ }
76
+ const levelFragments = /* @__PURE__ */ new Map();
77
+ function levelFragment(level) {
78
+ let fragment = levelFragments.get(level);
79
+ if (fragment === void 0) {
80
+ fragment = `,"level":${level},"levelName":"${toLevelName(level)}","logger":`;
81
+ levelFragments.set(level, fragment);
82
+ }
83
+ return fragment;
84
+ }
85
+ const categoryFragments = /* @__PURE__ */ new WeakMap();
86
+ function loggerFragment(category) {
87
+ let fragment = categoryFragments.get(category);
88
+ if (fragment === void 0) {
89
+ fragment = asJsonString(category.join("."));
90
+ categoryFragments.set(category, fragment);
91
+ }
92
+ return fragment;
93
+ }
94
+ function errorForRecord(record) {
95
+ if (record.err === null || record.err === void 0) return void 0;
96
+ return normalizeError(record.err);
97
+ }
98
+ function encodeEvent(event, options, stringify, tagsFragment) {
99
+ let output;
100
+ if (hasFullHeader(options)) output = `{"id":${asJsonString(event.id)},"time":${event.time},"seq":${event.seq},"level":${event.level},"levelName":${asJsonString(event.levelName)},"logger":${asJsonString(event.logger)},"message":${asJsonString(event.message)}`;
101
+ else {
102
+ output = "{";
103
+ if (options.includeId ?? true) output += `"id":${asJsonString(event.id)},`;
104
+ output += `"time":${event.time}`;
105
+ if (options.includeSeq ?? true) output += `,"seq":${event.seq}`;
106
+ output += `,"level":${event.level}`;
107
+ if (options.includeLevelName ?? true) output += `,"levelName":${asJsonString(event.levelName)}`;
108
+ output += `,"logger":${asJsonString(event.logger)},"message":${asJsonString(event.message)}`;
109
+ }
110
+ if (event.type !== void 0) output += `,"type":${asJsonString(event.type)}`;
111
+ if (event.tags !== void 0) output += tagsFragment(event.tags);
112
+ if (options.includeData ?? true) output = appendField(output, "data", event.data, stringify);
113
+ if (options.includeError ?? true) output = appendField(output, "error", event.error, stringify);
114
+ if (options.includeContext ?? true) output = appendField(output, "context", event.context, stringify);
115
+ if (options.includeTrace ?? true) output = appendField(output, "trace", event.trace, stringify);
116
+ if (options.includeSource ?? true) output = appendField(output, "source", event.source, stringify);
117
+ return `${output}}`;
118
+ }
119
+ function encodeRecord(record, options, stringify, tagsFragment) {
120
+ const levelName = toLevelName(record.level);
121
+ let output;
122
+ if (hasFullHeader(options)) output = `{"id":"${defaultRecordId(record, levelName)}${timeFragment(record.time)}${record.seq}${levelFragment(record.level)}${loggerFragment(record.category)},"message":${asJsonString(resolveMessage(record))}`;
123
+ else {
124
+ output = "{";
125
+ if (options.includeId ?? true) output += `"id":"${defaultRecordId(record, levelName)}",`;
126
+ output += `"time":${record.time}`;
127
+ if (options.includeSeq ?? true) output += `,"seq":${record.seq}`;
128
+ output += `,"level":${record.level}`;
129
+ if (options.includeLevelName ?? true) output += `,"levelName":"${levelName}"`;
130
+ output += `,"logger":${loggerFragment(record.category)},"message":${asJsonString(resolveMessage(record))}`;
131
+ }
132
+ if (record.type !== null) output += `,"type":${asJsonString(record.type)}`;
133
+ if (record.tags !== null) output += tagsFragment(record.tags);
134
+ if (options.includeData ?? true) output = appendDataField(output, record.props ?? void 0, stringify);
135
+ if (options.includeError ?? true) output = appendField(output, "error", errorForRecord(record), stringify);
136
+ if (options.includeContext ?? true) output = appendField(output, "context", record.ctx ?? void 0, stringify);
137
+ if (options.includeTrace ?? true) output = appendField(output, "trace", record.trace ?? void 0, stringify);
138
+ if ((options.includeSource ?? true) && record.source !== "app") output += `,"source":{"integration":${asJsonString(record.source)}}`;
139
+ return `${output}}`;
140
+ }
141
+ function encodeItem(item, options, stringify, tagsFragment) {
142
+ return isLogRecord(item) ? encodeRecord(item, options, stringify, tagsFragment) : encodeEvent(item, options, stringify, tagsFragment);
143
+ }
144
+ function encodeArray(items, options, stringify, tagsFragment) {
145
+ let output = "[";
146
+ for (let index = 0; index < items.length; index += 1) {
147
+ if (index > 0) output += ",";
148
+ output += encodeItem(items[index], options, stringify, tagsFragment);
149
+ }
150
+ return `${output}]`;
151
+ }
152
+ function isCodecArray(input) {
153
+ return Array.isArray(input);
154
+ }
155
+ function hasLogRecord(items) {
156
+ for (const item of items) if (isLogRecord(item)) return true;
157
+ return false;
158
+ }
159
+ function createTagsFragment(encoder, cached) {
160
+ const fragments = cached ? /* @__PURE__ */ new WeakMap() : void 0;
161
+ return (tags) => {
162
+ if (fragments && Object.isFrozen(tags)) {
163
+ let fragment = fragments.get(tags);
164
+ if (fragment === void 0) {
165
+ const encoded = encoder(tags);
166
+ fragment = encoded === void 0 ? "" : `,"tags":${encoded}`;
167
+ fragments.set(tags, fragment);
168
+ }
169
+ return fragment;
170
+ }
171
+ const encoded = encoder(tags);
172
+ return encoded === void 0 ? "" : `,"tags":${encoded}`;
173
+ };
174
+ }
175
+ function fastEventJsonCodec(options = {}) {
176
+ const stringify = createStringify(options);
177
+ const safeStringify = (value) => safeJsonStringify(value, options);
178
+ const useNativeEventJson = canUseNativeEventJson(options);
179
+ const tagsFragment = createTagsFragment(stringify, true);
180
+ const safeTagsFragment = createTagsFragment(safeStringify, false);
181
+ const fastEncode = (input) => {
182
+ if (isCodecArray(input)) {
183
+ if (useNativeEventJson && !hasLogRecord(input)) return JSON.stringify(input);
184
+ return encodeArray(input, options, stringify, tagsFragment);
185
+ }
186
+ if (useNativeEventJson && !isLogRecord(input)) return JSON.stringify(input);
187
+ return encodeItem(input, options, stringify, tagsFragment);
188
+ };
189
+ return {
190
+ name: "fast-event-json",
191
+ contentType: "application/json",
192
+ encode(input) {
193
+ try {
194
+ return fastEncode(input);
195
+ } catch {
196
+ incrementLoggerMetaCounter("codec.fallback");
197
+ incrementLoggerMetaCounter("codec.fallback.fast-event-json");
198
+ }
199
+ if (isCodecArray(input)) return encodeArray(input, options, safeStringify, safeTagsFragment);
200
+ return encodeItem(input, options, safeStringify, safeTagsFragment);
201
+ },
202
+ decode(payload) {
203
+ return JSON.parse(payload);
204
+ }
205
+ };
206
+ }
207
+ //#endregion
208
+ //#region src/msgpackr.ts
209
+ function isMsgpackRuntime(value) {
210
+ return typeof value.pack === "function" && typeof value.unpack === "function";
211
+ }
212
+ function msgpackrCodec(options = {}) {
213
+ const runtime = isMsgpackRuntime(options) ? options : new Packr({
214
+ useRecords: true,
215
+ ...options
216
+ });
217
+ return {
218
+ name: "msgpackr",
219
+ contentType: "application/msgpack",
220
+ encode(input) {
221
+ return runtime.pack(normalizeCodecInput(input));
222
+ },
223
+ decode(payload) {
224
+ return runtime.unpack(payload);
225
+ }
226
+ };
227
+ }
228
+ //#endregion
229
+ //#region src/projector.ts
230
+ function projectorCodec(options) {
231
+ return {
232
+ name: options.name,
233
+ contentType: options.contentType,
234
+ encode(input) {
235
+ return options.serialize(options.project(normalizeCodecInput(input)));
236
+ },
237
+ decode: options.parse && options.unproject ? (payload) => options.unproject(options.parse(payload)) : void 0
238
+ };
239
+ }
240
+ //#endregion
241
+ export { fastEventJsonCodec, msgpackrCodec, projectorCodec };
242
+
243
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/fast-event-json.ts","../src/msgpackr.ts","../src/projector.ts"],"sourcesContent":["import {\n defaultRecordId,\n incrementLoggerMetaCounter,\n isLogRecord,\n normalizeError,\n resolveMessage,\n toLevelName,\n safeJsonStringify,\n type Codec,\n type CodecInput,\n type LogEvent,\n type LogRecord,\n type SafeStringifyOptions,\n} from \"@loggerjs/core\";\n\n/**\n * Without any option set, encode runs on a native `JSON.stringify` fast path: nested\n * `Error` values serialize as `{}` and circular or BigInt payloads trigger a safe\n * re-encode of the whole input (circular refs become \"[Circular]\", BigInt becomes a\n * string). Setting any {@link SafeStringifyOptions} field opts into the safe encoder\n * everywhere, which also preserves `Error` name/message/stack inside data payloads.\n *\n * `includeId`, `includeSeq`, and `includeLevelName` trim the envelope for\n * pino-shaped minimal NDJSON output; turning `includeId` off also skips id\n * computation entirely on the record path.\n */\nexport interface FastEventJsonCodecOptions extends SafeStringifyOptions {\n includeContext?: boolean;\n includeData?: boolean;\n includeError?: boolean;\n includeTrace?: boolean;\n includeSource?: boolean;\n includeId?: boolean;\n includeSeq?: boolean;\n includeLevelName?: boolean;\n}\n\ntype JsonStringify = (value: unknown) => string | undefined;\n\nfunction hasSafeOptions(options: SafeStringifyOptions): boolean {\n return (\n options.maxDepth !== undefined ||\n options.maxArrayLength !== undefined ||\n options.maxObjectKeys !== undefined ||\n options.includeStack !== undefined ||\n options.stable !== undefined ||\n options.space !== undefined\n );\n}\n\nfunction canUseNativeEventJson(options: FastEventJsonCodecOptions): boolean {\n return (\n !hasSafeOptions(options) &&\n options.includeContext !== false &&\n options.includeData !== false &&\n options.includeError !== false &&\n options.includeTrace !== false &&\n options.includeSource !== false &&\n options.includeId !== false &&\n options.includeSeq !== false &&\n options.includeLevelName !== false\n );\n}\n\nfunction hasFullHeader(options: FastEventJsonCodecOptions): boolean {\n return (\n (options.includeId ?? true) &&\n (options.includeSeq ?? true) &&\n (options.includeLevelName ?? true)\n );\n}\n\nfunction createStringify(options: SafeStringifyOptions): JsonStringify {\n if (hasSafeOptions(options)) return (value) => safeJsonStringify(value, options);\n return JSON.stringify;\n}\n\nfunction appendField(\n output: string,\n name: string,\n value: unknown,\n stringify: JsonStringify,\n): string {\n if (value === undefined) return output;\n const encoded = stringify(value);\n return encoded === undefined ? output : `${output},\"${name}\":${encoded}`;\n}\n\n// JSON.stringify costs ~35ns per short string mostly in call overhead. Short\n// strings without quotes, backslashes, or control characters can be quoted\n// directly after a linear scan.\nfunction asJsonString(value: string): string {\n if (value.length > 256) return JSON.stringify(value);\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code === 34 || code === 92 || code < 32) return JSON.stringify(value);\n }\n return `\"${value}\"`;\n}\n\n// Data payloads are usually small flat objects of primitives. Writing them\n// directly skips the native JSON.stringify call overhead; any nested or\n// exotic value bails out so the caller can use the configured stringifier.\nfunction tryFlatObjectJson(value: Record<string, unknown>): string | undefined {\n // Class instances may carry toJSON or accessors; only plain objects take\n // the fast path so behavior matches JSON.stringify.\n const proto = Object.getPrototypeOf(value) as unknown;\n if (proto !== Object.prototype && proto !== null) return undefined;\n let output = \"{\";\n let first = true;\n for (const key of Object.keys(value)) {\n const item = value[key];\n let encoded: string;\n switch (typeof item) {\n case \"string\":\n encoded = asJsonString(item);\n break;\n case \"number\":\n encoded = Number.isFinite(item) ? String(item) : \"null\";\n break;\n case \"boolean\":\n encoded = item ? \"true\" : \"false\";\n break;\n case \"object\":\n if (item !== null) return undefined;\n encoded = \"null\";\n break;\n case \"undefined\":\n continue;\n default:\n return undefined;\n }\n output += `${first ? \"\" : \",\"}${asJsonString(key)}:${encoded}`;\n first = false;\n }\n return `${output}}`;\n}\n\nfunction appendDataField(\n output: string,\n value: Record<string, unknown> | undefined,\n stringify: JsonStringify,\n): string {\n if (value === undefined) return output;\n const flat = tryFlatObjectJson(value);\n if (flat !== undefined) return `${output},\"data\":${flat}`;\n return appendField(output, \"data\", value, stringify);\n}\n\n// The timestamp changes once per millisecond while bursts of records share\n// it, so the rendered `\",\"time\":...,\"seq\":` fragment can be memoized the same\n// way the default id memoizes its time segment.\nlet lastFragmentTime = -1;\nlet lastTimeFragment = \"\";\n\nfunction timeFragment(time: number): string {\n if (time !== lastFragmentTime) {\n lastFragmentTime = time;\n lastTimeFragment = `\",\"time\":${time},\"seq\":`;\n }\n return lastTimeFragment;\n}\n\n// level -> ',\"level\":30,\"levelName\":\"info\",\"logger\":' — derived from the\n// numeric level alone, so a module-level cache is safe.\nconst levelFragments = new Map<number, string>();\n\nfunction levelFragment(level: number): string {\n let fragment = levelFragments.get(level);\n if (fragment === undefined) {\n fragment = `,\"level\":${level},\"levelName\":\"${toLevelName(level)}\",\"logger\":`;\n levelFragments.set(level, fragment);\n }\n return fragment;\n}\n\n// Loggers reuse one frozen category array for every record, so the joined and\n// escaped logger name can be cached per array identity.\nconst categoryFragments = new WeakMap<readonly string[], string>();\n\nfunction loggerFragment(category: readonly string[]): string {\n let fragment = categoryFragments.get(category);\n if (fragment === undefined) {\n fragment = asJsonString(category.join(\".\"));\n categoryFragments.set(category, fragment);\n }\n return fragment;\n}\n\nfunction errorForRecord(record: LogRecord): LogEvent[\"error\"] | undefined {\n if (record.err === null || record.err === undefined) return undefined;\n return normalizeError(record.err);\n}\n\nfunction encodeEvent(\n event: LogEvent,\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: (tags: NonNullable<LogEvent[\"tags\"]>) => string,\n): string {\n let output: string;\n if (hasFullHeader(options)) {\n output = `{\"id\":${asJsonString(event.id)},\"time\":${event.time},\"seq\":${event.seq},\"level\":${event.level},\"levelName\":${asJsonString(event.levelName)},\"logger\":${asJsonString(event.logger)},\"message\":${asJsonString(event.message)}`;\n } else {\n output = \"{\";\n if (options.includeId ?? true) output += `\"id\":${asJsonString(event.id)},`;\n output += `\"time\":${event.time}`;\n if (options.includeSeq ?? true) output += `,\"seq\":${event.seq}`;\n output += `,\"level\":${event.level}`;\n if (options.includeLevelName ?? true) output += `,\"levelName\":${asJsonString(event.levelName)}`;\n output += `,\"logger\":${asJsonString(event.logger)},\"message\":${asJsonString(event.message)}`;\n }\n if (event.type !== undefined) output += `,\"type\":${asJsonString(event.type)}`;\n if (event.tags !== undefined) output += tagsFragment(event.tags);\n if (options.includeData ?? true) output = appendField(output, \"data\", event.data, stringify);\n if (options.includeError ?? true) output = appendField(output, \"error\", event.error, stringify);\n if (options.includeContext ?? true)\n output = appendField(output, \"context\", event.context, stringify);\n if (options.includeTrace ?? true) output = appendField(output, \"trace\", event.trace, stringify);\n if (options.includeSource ?? true)\n output = appendField(output, \"source\", event.source, stringify);\n return `${output}}`;\n}\n\nfunction encodeRecord(\n record: LogRecord,\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: (tags: NonNullable<LogRecord[\"tags\"]>) => string,\n): string {\n const levelName = toLevelName(record.level);\n let output: string;\n if (hasFullHeader(options)) {\n // The default id only contains [0-9a-z-] and the level name, so it never\n // needs escaping.\n output = `{\"id\":\"${defaultRecordId(record, levelName)}${timeFragment(record.time)}${record.seq}${levelFragment(record.level)}${loggerFragment(record.category)},\"message\":${asJsonString(resolveMessage(record))}`;\n } else {\n output = \"{\";\n if (options.includeId ?? true) output += `\"id\":\"${defaultRecordId(record, levelName)}\",`;\n output += `\"time\":${record.time}`;\n if (options.includeSeq ?? true) output += `,\"seq\":${record.seq}`;\n output += `,\"level\":${record.level}`;\n if (options.includeLevelName ?? true) output += `,\"levelName\":\"${levelName}\"`;\n output += `,\"logger\":${loggerFragment(record.category)},\"message\":${asJsonString(resolveMessage(record))}`;\n }\n if (record.type !== null) output += `,\"type\":${asJsonString(record.type)}`;\n if (record.tags !== null) output += tagsFragment(record.tags);\n if (options.includeData ?? true)\n output = appendDataField(output, record.props ?? undefined, stringify);\n if (options.includeError ?? true)\n output = appendField(output, \"error\", errorForRecord(record), stringify);\n if (options.includeContext ?? true)\n output = appendField(output, \"context\", record.ctx ?? undefined, stringify);\n if (options.includeTrace ?? true)\n output = appendField(output, \"trace\", record.trace ?? undefined, stringify);\n if ((options.includeSource ?? true) && record.source !== \"app\")\n output += `,\"source\":{\"integration\":${asJsonString(record.source)}}`;\n return `${output}}`;\n}\n\ntype TagsFragment = (tags: NonNullable<LogEvent[\"tags\"]>) => string;\n\nfunction encodeItem(\n item: LogEvent | LogRecord,\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: TagsFragment,\n): string {\n return isLogRecord(item)\n ? encodeRecord(item, options, stringify, tagsFragment)\n : encodeEvent(item, options, stringify, tagsFragment);\n}\n\nfunction encodeArray(\n items: readonly (LogEvent | LogRecord)[],\n options: FastEventJsonCodecOptions,\n stringify: JsonStringify,\n tagsFragment: TagsFragment,\n): string {\n let output = \"[\";\n for (let index = 0; index < items.length; index += 1) {\n if (index > 0) output += \",\";\n output += encodeItem(items[index]!, options, stringify, tagsFragment);\n }\n return `${output}]`;\n}\n\nfunction isCodecArray(input: CodecInput): input is readonly (LogEvent | LogRecord)[] {\n return Array.isArray(input);\n}\n\nfunction hasLogRecord(items: readonly (LogEvent | LogRecord)[]): boolean {\n for (const item of items) {\n if (isLogRecord(item)) return true;\n }\n return false;\n}\n\n// Loggers share one frozen tags object across records, so the encoded\n// fragment can be cached per object identity. Mutable tags objects are\n// re-encoded every time. Callers create one fragment encoder per codec\n// because it bakes in that codec's stringify options.\nfunction createTagsFragment(encoder: JsonStringify, cached: boolean): TagsFragment {\n const fragments = cached ? new WeakMap<object, string>() : undefined;\n return (tags) => {\n if (fragments && Object.isFrozen(tags)) {\n let fragment = fragments.get(tags);\n if (fragment === undefined) {\n const encoded = encoder(tags);\n fragment = encoded === undefined ? \"\" : `,\"tags\":${encoded}`;\n fragments.set(tags, fragment);\n }\n return fragment;\n }\n const encoded = encoder(tags);\n return encoded === undefined ? \"\" : `,\"tags\":${encoded}`;\n };\n}\n\nexport function fastEventJsonCodec(options: FastEventJsonCodecOptions = {}): Codec<string> {\n const stringify = createStringify(options);\n const safeStringify: JsonStringify = (value) => safeJsonStringify(value, options);\n const useNativeEventJson = canUseNativeEventJson(options);\n const tagsFragment = createTagsFragment(stringify, true);\n // The fallback must not reuse the fast fragments: a cached entry or the\n // fast stringify could throw on the exact payload that triggered fallback.\n const safeTagsFragment = createTagsFragment(safeStringify, false);\n const fastEncode = (input: CodecInput): string => {\n if (isCodecArray(input)) {\n if (useNativeEventJson && !hasLogRecord(input)) return JSON.stringify(input);\n return encodeArray(input, options, stringify, tagsFragment);\n }\n if (useNativeEventJson && !isLogRecord(input)) return JSON.stringify(input);\n return encodeItem(input, options, stringify, tagsFragment);\n };\n return {\n name: \"fast-event-json\",\n contentType: \"application/json\",\n encode(input: CodecInput) {\n // Circular references and BigInt values throw on the native fast path. Logs\n // must never be lost to encoding, so re-encode the input with the safe\n // stringifier instead of surfacing the error as a transport failure.\n try {\n return fastEncode(input);\n } catch {\n incrementLoggerMetaCounter(\"codec.fallback\");\n incrementLoggerMetaCounter(\"codec.fallback.fast-event-json\");\n }\n if (isCodecArray(input)) return encodeArray(input, options, safeStringify, safeTagsFragment);\n return encodeItem(input, options, safeStringify, safeTagsFragment);\n },\n decode(payload) {\n return JSON.parse(payload) as LogEvent | LogEvent[];\n },\n };\n}\n","import { normalizeCodecInput, type Codec, type CodecInput, type LogEvent } from \"@loggerjs/core\";\nimport { Packr, type Options as MsgpackrOptions } from \"msgpackr\";\n\nexport interface MsgpackRuntime {\n pack: (input: unknown) => Uint8Array;\n unpack: (payload: Uint8Array) => unknown;\n}\n\nexport type MsgpackrCodecOptions = MsgpackrOptions;\n\nfunction isMsgpackRuntime(value: MsgpackRuntime | MsgpackrCodecOptions): value is MsgpackRuntime {\n return (\n typeof (value as MsgpackRuntime).pack === \"function\" &&\n typeof (value as MsgpackRuntime).unpack === \"function\"\n );\n}\n\nexport function msgpackrCodec(\n options: MsgpackRuntime | MsgpackrCodecOptions = {},\n): Codec<Uint8Array> {\n const runtime = isMsgpackRuntime(options) ? options : new Packr({ useRecords: true, ...options });\n return {\n name: \"msgpackr\",\n contentType: \"application/msgpack\",\n encode(input: CodecInput) {\n return runtime.pack(normalizeCodecInput(input));\n },\n decode(payload: Uint8Array) {\n return runtime.unpack(payload) as LogEvent | LogEvent[];\n },\n };\n}\n","import { normalizeCodecInput, type Codec, type CodecInput, type LogEvent } from \"@loggerjs/core\";\n\nexport interface ProjectorCodecOptions<TWire> {\n name: string;\n contentType: string;\n project: (input: LogEvent | LogEvent[]) => TWire;\n serialize: (wire: TWire) => string | Uint8Array;\n parse?: (payload: string | Uint8Array) => TWire;\n unproject?: (wire: TWire) => LogEvent | LogEvent[];\n}\n\nexport function projectorCodec<TWire>(\n options: ProjectorCodecOptions<TWire>,\n): Codec<string | Uint8Array> {\n return {\n name: options.name,\n contentType: options.contentType,\n encode(input: CodecInput) {\n return options.serialize(options.project(normalizeCodecInput(input)));\n },\n decode:\n options.parse && options.unproject\n ? (payload) => options.unproject!(options.parse!(payload))\n : undefined,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type Codec } from "@loggerjs/core";
2
+ import { type Options as MsgpackrOptions } from "msgpackr";
3
+ export interface MsgpackRuntime {
4
+ pack: (input: unknown) => Uint8Array;
5
+ unpack: (payload: Uint8Array) => unknown;
6
+ }
7
+ export type MsgpackrCodecOptions = MsgpackrOptions;
8
+ export declare function msgpackrCodec(options?: MsgpackRuntime | MsgpackrCodecOptions): Codec<Uint8Array>;
9
+ //# sourceMappingURL=msgpackr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msgpackr.d.ts","sourceRoot":"","sources":["../src/msgpackr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,KAAK,EAAkC,MAAM,gBAAgB,CAAC;AACjG,OAAO,EAAS,KAAK,OAAO,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,UAAU,CAAC;IACrC,MAAM,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC;CAC1C;AAED,MAAM,MAAM,oBAAoB,GAAG,eAAe,CAAC;AASnD,wBAAgB,aAAa,CAC3B,OAAO,GAAE,cAAc,GAAG,oBAAyB,GAClD,KAAK,CAAC,UAAU,CAAC,CAYnB"}
@@ -0,0 +1,11 @@
1
+ import { type Codec, type LogEvent } from "@loggerjs/core";
2
+ export interface ProjectorCodecOptions<TWire> {
3
+ name: string;
4
+ contentType: string;
5
+ project: (input: LogEvent | LogEvent[]) => TWire;
6
+ serialize: (wire: TWire) => string | Uint8Array;
7
+ parse?: (payload: string | Uint8Array) => TWire;
8
+ unproject?: (wire: TWire) => LogEvent | LogEvent[];
9
+ }
10
+ export declare function projectorCodec<TWire>(options: ProjectorCodecOptions<TWire>): Codec<string | Uint8Array>;
11
+ //# sourceMappingURL=projector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projector.d.ts","sourceRoot":"","sources":["../src/projector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,KAAK,EAAmB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEjG,MAAM,WAAW,qBAAqB,CAAC,KAAK;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,EAAE,KAAK,KAAK,CAAC;IACjD,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,GAAG,UAAU,CAAC;IAChD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,KAAK,KAAK,CAAC;IAChD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,QAAQ,GAAG,QAAQ,EAAE,CAAC;CACpD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAClC,OAAO,EAAE,qBAAqB,CAAC,KAAK,CAAC,GACpC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAY5B"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@loggerjs/codecs",
3
+ "version": "0.0.1",
4
+ "description": "Serialization codecs for loggerjs.",
5
+ "homepage": "https://github.com/jskits/loggerjs/tree/main/packages/codecs#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/jskits/loggerjs/issues"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/jskits/loggerjs.git",
13
+ "directory": "packages/codecs"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "type": "module",
21
+ "sideEffects": false,
22
+ "main": "./dist/index.cjs",
23
+ "module": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "require": "./dist/index.cjs"
30
+ }
31
+ },
32
+ "publishConfig": {
33
+ "access": "public",
34
+ "provenance": true
35
+ },
36
+ "dependencies": {
37
+ "msgpackr": "^2.0.4",
38
+ "@loggerjs/core": "0.0.1"
39
+ },
40
+ "scripts": {
41
+ "build": "pnpm run clean && pnpm run build:js && pnpm run build:types",
42
+ "build:js": "rolldown -c ../../rolldown.config.mjs",
43
+ "build:types": "tsc -p tsconfig.build.json && node ../../scripts/fix-dts-extensions.mjs dist",
44
+ "typecheck": "tsc --noEmit -p tsconfig.json",
45
+ "test": "vitest run --passWithNoTests",
46
+ "clean": "rm -rf dist"
47
+ }
48
+ }