sloplog 0.0.3
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 +204 -0
- package/dist/codegen.d.ts +43 -0
- package/dist/codegen.js +500 -0
- package/dist/collectors/betterstack.d.ts +47 -0
- package/dist/collectors/betterstack.js +74 -0
- package/dist/collectors/composite.d.ts +1 -0
- package/dist/collectors/composite.js +1 -0
- package/dist/collectors/file.d.ts +1 -0
- package/dist/collectors/file.js +1 -0
- package/dist/collectors/filtered.d.ts +1 -0
- package/dist/collectors/filtered.js +1 -0
- package/dist/collectors/index.d.ts +102 -0
- package/dist/collectors/index.js +127 -0
- package/dist/collectors/sentry.d.ts +46 -0
- package/dist/collectors/sentry.js +45 -0
- package/dist/collectors/stdio.d.ts +1 -0
- package/dist/collectors/stdio.js +1 -0
- package/dist/generated/partials.d.ts +40 -0
- package/dist/generated/partials.js +3 -0
- package/dist/index.d.ts +199 -0
- package/dist/index.js +354 -0
- package/dist/originator/index.d.ts +150 -0
- package/dist/originator/index.js +217 -0
- package/dist/partials.d.ts +154 -0
- package/dist/partials.js +52 -0
- package/dist/registry.d.ts +89 -0
- package/dist/registry.js +44 -0
- package/dist/wevt-0.0.1-py3-none-any.whl +0 -0
- package/dist/wevt-0.0.1.tar.gz +0 -0
- package/dist/wevt-0.0.2-py3-none-any.whl +0 -0
- package/dist/wevt-0.0.2.tar.gz +0 -0
- package/package.json +101 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Originator module - functions for creating originators from various sources
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Generate a nano ID for unique identifiers
|
|
6
|
+
*/
|
|
7
|
+
function nanoId() {
|
|
8
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
9
|
+
let result = '';
|
|
10
|
+
for (let i = 0; i < 21; i++) {
|
|
11
|
+
result += chars[Math.floor(Math.random() * chars.length)];
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
/** Header name for propagating originator ID across services */
|
|
16
|
+
export const ORIGINATOR_HEADER = 'x-sloplog-originator';
|
|
17
|
+
/** Header name for propagating trace ID across services */
|
|
18
|
+
export const TRACE_ID_HEADER = 'x-sloplog-trace-id';
|
|
19
|
+
/**
|
|
20
|
+
* Create headers for propagating tracing context to downstream services
|
|
21
|
+
*/
|
|
22
|
+
export function tracingHeaders(context) {
|
|
23
|
+
return {
|
|
24
|
+
[TRACE_ID_HEADER]: context.traceId,
|
|
25
|
+
[ORIGINATOR_HEADER]: context.originatorId,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extract tracing context from incoming request headers
|
|
30
|
+
* Returns null if no tracing headers are present
|
|
31
|
+
*/
|
|
32
|
+
export function extractTracingContext(headers) {
|
|
33
|
+
// Case-insensitive header lookup for trace ID
|
|
34
|
+
let traceIdValue;
|
|
35
|
+
let originatorIdValue;
|
|
36
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
37
|
+
const lowerKey = key.toLowerCase();
|
|
38
|
+
if (lowerKey === TRACE_ID_HEADER.toLowerCase()) {
|
|
39
|
+
traceIdValue = Array.isArray(value) ? value[0] : value;
|
|
40
|
+
}
|
|
41
|
+
else if (lowerKey === ORIGINATOR_HEADER.toLowerCase()) {
|
|
42
|
+
originatorIdValue = Array.isArray(value) ? value[0] : value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!traceIdValue || !originatorIdValue) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
traceId: traceIdValue,
|
|
50
|
+
originatorId: originatorIdValue,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Placeholder for redacted values */
|
|
54
|
+
const REDACTED = '[REDACTED]';
|
|
55
|
+
/** Headers that should be redacted (case-insensitive) */
|
|
56
|
+
const SENSITIVE_HEADERS = new Set([
|
|
57
|
+
'authorization',
|
|
58
|
+
'x-api-key',
|
|
59
|
+
'x-auth-token',
|
|
60
|
+
'cookie',
|
|
61
|
+
'set-cookie',
|
|
62
|
+
]);
|
|
63
|
+
/** Query parameters that should be redacted (case-insensitive) */
|
|
64
|
+
const SENSITIVE_QUERY_PARAMS = new Set([
|
|
65
|
+
'code',
|
|
66
|
+
'token',
|
|
67
|
+
'access_token',
|
|
68
|
+
'refresh_token',
|
|
69
|
+
'api_key',
|
|
70
|
+
'apikey',
|
|
71
|
+
'secret',
|
|
72
|
+
'password',
|
|
73
|
+
]);
|
|
74
|
+
/**
|
|
75
|
+
* Redact sensitive headers from a headers object
|
|
76
|
+
*/
|
|
77
|
+
function redactHeaders(headers) {
|
|
78
|
+
const redacted = {};
|
|
79
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
80
|
+
if (SENSITIVE_HEADERS.has(key.toLowerCase())) {
|
|
81
|
+
redacted[key] = REDACTED;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
redacted[key] = value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return redacted;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Redact sensitive query parameters from a query string
|
|
91
|
+
*/
|
|
92
|
+
function redactQueryString(query) {
|
|
93
|
+
if (!query)
|
|
94
|
+
return query;
|
|
95
|
+
const params = new URLSearchParams(query);
|
|
96
|
+
const redactedParams = new URLSearchParams();
|
|
97
|
+
for (const [key, value] of params.entries()) {
|
|
98
|
+
if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {
|
|
99
|
+
redactedParams.set(key, REDACTED);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
redactedParams.set(key, value);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const result = redactedParams.toString();
|
|
106
|
+
return result || undefined;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create an HTTP originator from a Web Fetch API Request
|
|
110
|
+
* Extracts tracing context from headers if present:
|
|
111
|
+
* - traceId: extracted from x-sloplog-trace-id header, or generated if not present
|
|
112
|
+
* - parentId: set to the incoming x-sloplog-originator header value (the caller's originatorId),
|
|
113
|
+
* or can be explicitly provided via options.parentId
|
|
114
|
+
*/
|
|
115
|
+
export function httpOriginator(request, options = {}) {
|
|
116
|
+
const url = new URL(request.url);
|
|
117
|
+
const headers = {};
|
|
118
|
+
request.headers.forEach((value, key) => {
|
|
119
|
+
headers[key.toLowerCase()] = value;
|
|
120
|
+
});
|
|
121
|
+
// Check for incoming tracing context
|
|
122
|
+
const tracingContext = extractTracingContext(headers);
|
|
123
|
+
// Redact sensitive data
|
|
124
|
+
const redactedHeaders = redactHeaders(headers);
|
|
125
|
+
const query = url.search ? url.search.slice(1) : undefined;
|
|
126
|
+
const redactedQuery = redactQueryString(query);
|
|
127
|
+
// Determine parentId: explicit option > tracing context > undefined
|
|
128
|
+
const parentId = options.parentId ?? tracingContext?.originatorId;
|
|
129
|
+
const originator = {
|
|
130
|
+
originatorId: options.originatorId || `orig_${nanoId()}`,
|
|
131
|
+
type: 'http',
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
...(parentId && { parentId }),
|
|
134
|
+
method: request.method.toUpperCase(),
|
|
135
|
+
path: url.pathname,
|
|
136
|
+
query: redactedQuery,
|
|
137
|
+
headers: redactedHeaders,
|
|
138
|
+
host: url.host,
|
|
139
|
+
userAgent: headers['user-agent'],
|
|
140
|
+
contentType: headers['content-type'],
|
|
141
|
+
contentLength: headers['content-length'] ? parseInt(headers['content-length'], 10) : undefined,
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
originator,
|
|
145
|
+
// Use incoming traceId if present, otherwise generate a new one
|
|
146
|
+
traceId: tracingContext?.traceId || `trace_${nanoId()}`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create an HTTP originator from a Node.js IncomingMessage (http/https/express)
|
|
151
|
+
* Extracts tracing context from headers if present:
|
|
152
|
+
* - traceId: extracted from x-sloplog-trace-id header, or generated if not present
|
|
153
|
+
* - parentId: set to the incoming x-sloplog-originator header value (the caller's originatorId),
|
|
154
|
+
* or can be explicitly provided via options.parentId
|
|
155
|
+
*/
|
|
156
|
+
export function nodeHttpOriginator(request, options = {}) {
|
|
157
|
+
const headers = {};
|
|
158
|
+
for (const [key, value] of Object.entries(request.headers)) {
|
|
159
|
+
if (value) {
|
|
160
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value[0] : value;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Parse URL
|
|
164
|
+
const urlStr = request.url || '/';
|
|
165
|
+
const host = headers['host'] || 'localhost';
|
|
166
|
+
let path = urlStr;
|
|
167
|
+
let query;
|
|
168
|
+
const queryIndex = urlStr.indexOf('?');
|
|
169
|
+
if (queryIndex !== -1) {
|
|
170
|
+
path = urlStr.slice(0, queryIndex);
|
|
171
|
+
query = urlStr.slice(queryIndex + 1);
|
|
172
|
+
}
|
|
173
|
+
// Check for incoming tracing context
|
|
174
|
+
const tracingContext = extractTracingContext(headers);
|
|
175
|
+
// Get client IP (check x-forwarded-for for proxied requests)
|
|
176
|
+
const clientIp = headers['x-forwarded-for']?.split(',')[0].trim() || request.socket?.remoteAddress;
|
|
177
|
+
// Redact sensitive data
|
|
178
|
+
const redactedHeaders = redactHeaders(headers);
|
|
179
|
+
const redactedQuery = redactQueryString(query);
|
|
180
|
+
// Determine parentId: explicit option > tracing context > undefined
|
|
181
|
+
const parentId = options.parentId ?? tracingContext?.originatorId;
|
|
182
|
+
const originator = {
|
|
183
|
+
originatorId: options.originatorId || `orig_${nanoId()}`,
|
|
184
|
+
type: 'http',
|
|
185
|
+
timestamp: Date.now(),
|
|
186
|
+
...(parentId && { parentId }),
|
|
187
|
+
method: (request.method?.toUpperCase() || 'GET'),
|
|
188
|
+
path,
|
|
189
|
+
query: redactedQuery,
|
|
190
|
+
headers: redactedHeaders,
|
|
191
|
+
host,
|
|
192
|
+
clientIp,
|
|
193
|
+
userAgent: headers['user-agent'],
|
|
194
|
+
contentType: headers['content-type'],
|
|
195
|
+
contentLength: headers['content-length'] ? parseInt(headers['content-length'], 10) : undefined,
|
|
196
|
+
httpVersion: request.httpVersion,
|
|
197
|
+
};
|
|
198
|
+
return {
|
|
199
|
+
originator,
|
|
200
|
+
// Use incoming traceId if present, otherwise generate a new one
|
|
201
|
+
traceId: tracingContext?.traceId || `trace_${nanoId()}`,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Create a cron originator for scheduled tasks
|
|
206
|
+
*/
|
|
207
|
+
export function cronOriginator(cron, jobName, options = {}) {
|
|
208
|
+
return {
|
|
209
|
+
originatorId: `orig_${nanoId()}`,
|
|
210
|
+
type: 'cron',
|
|
211
|
+
timestamp: Date.now(),
|
|
212
|
+
...(options.parentId && { parentId: options.parentId }),
|
|
213
|
+
cron,
|
|
214
|
+
jobName,
|
|
215
|
+
scheduledTime: Date.now(),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z, type InferPartial } from './registry.js';
|
|
2
|
+
declare const error: import("./registry.js").PartialFactory<"error", {
|
|
3
|
+
message: z.ZodString;
|
|
4
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
5
|
+
code: z.ZodOptional<z.ZodNumber>;
|
|
6
|
+
}, {
|
|
7
|
+
repeatable: true;
|
|
8
|
+
alwaysSample: true;
|
|
9
|
+
description: string;
|
|
10
|
+
}>;
|
|
11
|
+
declare const logMessage: import("./registry.js").PartialFactory<"log_message", {
|
|
12
|
+
message: z.ZodString;
|
|
13
|
+
level: z.ZodEnum<{
|
|
14
|
+
error: "error";
|
|
15
|
+
trace: "trace";
|
|
16
|
+
debug: "debug";
|
|
17
|
+
info: "info";
|
|
18
|
+
warn: "warn";
|
|
19
|
+
fatal: "fatal";
|
|
20
|
+
}>;
|
|
21
|
+
data: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, {
|
|
23
|
+
repeatable: true;
|
|
24
|
+
description: string;
|
|
25
|
+
}>;
|
|
26
|
+
declare const span: import("./registry.js").PartialFactory<"span", {
|
|
27
|
+
name: z.ZodString;
|
|
28
|
+
startedAt: z.ZodNumber;
|
|
29
|
+
endedAt: z.ZodNumber;
|
|
30
|
+
durationMs: z.ZodNumber;
|
|
31
|
+
}, {
|
|
32
|
+
repeatable: true;
|
|
33
|
+
description: string;
|
|
34
|
+
}>;
|
|
35
|
+
declare const sloplogUsageError: import("./registry.js").PartialFactory<"sloplog_usage_error", {
|
|
36
|
+
kind: z.ZodString;
|
|
37
|
+
message: z.ZodString;
|
|
38
|
+
partialType: z.ZodOptional<z.ZodString>;
|
|
39
|
+
spanName: z.ZodOptional<z.ZodString>;
|
|
40
|
+
startedAt: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
count: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
}, {
|
|
43
|
+
repeatable: true;
|
|
44
|
+
description: string;
|
|
45
|
+
}>;
|
|
46
|
+
/** Built-in partial definitions shipped with sloplog. */
|
|
47
|
+
export declare const builtInPartials: {
|
|
48
|
+
error: import("./registry.js").PartialFactory<"error", {
|
|
49
|
+
message: z.ZodString;
|
|
50
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
51
|
+
code: z.ZodOptional<z.ZodNumber>;
|
|
52
|
+
}, {
|
|
53
|
+
repeatable: true;
|
|
54
|
+
alwaysSample: true;
|
|
55
|
+
description: string;
|
|
56
|
+
}>;
|
|
57
|
+
log_message: import("./registry.js").PartialFactory<"log_message", {
|
|
58
|
+
message: z.ZodString;
|
|
59
|
+
level: z.ZodEnum<{
|
|
60
|
+
error: "error";
|
|
61
|
+
trace: "trace";
|
|
62
|
+
debug: "debug";
|
|
63
|
+
info: "info";
|
|
64
|
+
warn: "warn";
|
|
65
|
+
fatal: "fatal";
|
|
66
|
+
}>;
|
|
67
|
+
data: z.ZodOptional<z.ZodString>;
|
|
68
|
+
}, {
|
|
69
|
+
repeatable: true;
|
|
70
|
+
description: string;
|
|
71
|
+
}>;
|
|
72
|
+
span: import("./registry.js").PartialFactory<"span", {
|
|
73
|
+
name: z.ZodString;
|
|
74
|
+
startedAt: z.ZodNumber;
|
|
75
|
+
endedAt: z.ZodNumber;
|
|
76
|
+
durationMs: z.ZodNumber;
|
|
77
|
+
}, {
|
|
78
|
+
repeatable: true;
|
|
79
|
+
description: string;
|
|
80
|
+
}>;
|
|
81
|
+
sloplog_usage_error: import("./registry.js").PartialFactory<"sloplog_usage_error", {
|
|
82
|
+
kind: z.ZodString;
|
|
83
|
+
message: z.ZodString;
|
|
84
|
+
partialType: z.ZodOptional<z.ZodString>;
|
|
85
|
+
spanName: z.ZodOptional<z.ZodString>;
|
|
86
|
+
startedAt: z.ZodOptional<z.ZodNumber>;
|
|
87
|
+
count: z.ZodOptional<z.ZodNumber>;
|
|
88
|
+
}, {
|
|
89
|
+
repeatable: true;
|
|
90
|
+
description: string;
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
/** Registry of built-in partial definitions. */
|
|
94
|
+
export declare const builtInRegistry: import("./registry.js").Registry<(import("./registry.js").PartialFactory<"error", {
|
|
95
|
+
message: z.ZodString;
|
|
96
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
97
|
+
code: z.ZodOptional<z.ZodNumber>;
|
|
98
|
+
}, {
|
|
99
|
+
repeatable: true;
|
|
100
|
+
alwaysSample: true;
|
|
101
|
+
description: string;
|
|
102
|
+
}> | import("./registry.js").PartialFactory<"log_message", {
|
|
103
|
+
message: z.ZodString;
|
|
104
|
+
level: z.ZodEnum<{
|
|
105
|
+
error: "error";
|
|
106
|
+
trace: "trace";
|
|
107
|
+
debug: "debug";
|
|
108
|
+
info: "info";
|
|
109
|
+
warn: "warn";
|
|
110
|
+
fatal: "fatal";
|
|
111
|
+
}>;
|
|
112
|
+
data: z.ZodOptional<z.ZodString>;
|
|
113
|
+
}, {
|
|
114
|
+
repeatable: true;
|
|
115
|
+
description: string;
|
|
116
|
+
}> | import("./registry.js").PartialFactory<"span", {
|
|
117
|
+
name: z.ZodString;
|
|
118
|
+
startedAt: z.ZodNumber;
|
|
119
|
+
endedAt: z.ZodNumber;
|
|
120
|
+
durationMs: z.ZodNumber;
|
|
121
|
+
}, {
|
|
122
|
+
repeatable: true;
|
|
123
|
+
description: string;
|
|
124
|
+
}> | import("./registry.js").PartialFactory<"sloplog_usage_error", {
|
|
125
|
+
kind: z.ZodString;
|
|
126
|
+
message: z.ZodString;
|
|
127
|
+
partialType: z.ZodOptional<z.ZodString>;
|
|
128
|
+
spanName: z.ZodOptional<z.ZodString>;
|
|
129
|
+
startedAt: z.ZodOptional<z.ZodNumber>;
|
|
130
|
+
count: z.ZodOptional<z.ZodNumber>;
|
|
131
|
+
}, {
|
|
132
|
+
repeatable: true;
|
|
133
|
+
description: string;
|
|
134
|
+
}>)[]>;
|
|
135
|
+
/** Runtime metadata for built-in partials (repeatable, alwaysSample). */
|
|
136
|
+
export declare const builtInPartialMetadata: Map<string, import("./registry.js").PartialMetadata>;
|
|
137
|
+
/** Error partial payload type. */
|
|
138
|
+
export type ErrorPartial = InferPartial<typeof error>;
|
|
139
|
+
/** log_message partial payload type. */
|
|
140
|
+
export type LogMessagePartial = InferPartial<typeof logMessage>;
|
|
141
|
+
/** span partial payload type. */
|
|
142
|
+
export type SpanPartial = InferPartial<typeof span>;
|
|
143
|
+
/** sloplog_usage_error partial payload type. */
|
|
144
|
+
export type SloplogUsageErrorPartial = InferPartial<typeof sloplogUsageError>;
|
|
145
|
+
/** Built-in partial registry shape. */
|
|
146
|
+
export type BuiltInRegistry = {
|
|
147
|
+
error: ErrorPartial[];
|
|
148
|
+
log_message: LogMessagePartial[];
|
|
149
|
+
span: SpanPartial[];
|
|
150
|
+
sloplog_usage_error: SloplogUsageErrorPartial[];
|
|
151
|
+
};
|
|
152
|
+
/** Union of built-in partial names. */
|
|
153
|
+
export type BuiltInPartialName = keyof BuiltInRegistry;
|
|
154
|
+
export {};
|
package/dist/partials.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { extractPartialMetadata, partial, registry, z } from './registry.js';
|
|
2
|
+
const error = partial('error', {
|
|
3
|
+
message: z.string(),
|
|
4
|
+
stack: z.string().optional(),
|
|
5
|
+
code: z.number().optional(),
|
|
6
|
+
}, {
|
|
7
|
+
repeatable: true,
|
|
8
|
+
alwaysSample: true,
|
|
9
|
+
description: 'Error details captured during event processing.',
|
|
10
|
+
});
|
|
11
|
+
const logMessageLevel = z
|
|
12
|
+
.enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])
|
|
13
|
+
.describe('Log severity level for a log_message partial.');
|
|
14
|
+
const logMessage = partial('log_message', {
|
|
15
|
+
message: z.string().describe('Human-readable log message.'),
|
|
16
|
+
level: logMessageLevel,
|
|
17
|
+
data: z.string().optional().describe('Optional stringified payload for the log.'),
|
|
18
|
+
}, {
|
|
19
|
+
repeatable: true,
|
|
20
|
+
description: 'Lightweight log entry for ad-hoc logging. Prefer structured partials when possible.',
|
|
21
|
+
});
|
|
22
|
+
const span = partial('span', {
|
|
23
|
+
name: z.string().describe('Span name.'),
|
|
24
|
+
startedAt: z.number().describe('Span start time in milliseconds since epoch.'),
|
|
25
|
+
endedAt: z.number().describe('Span end time in milliseconds since epoch.'),
|
|
26
|
+
durationMs: z.number().describe('Span duration in milliseconds.'),
|
|
27
|
+
}, {
|
|
28
|
+
repeatable: true,
|
|
29
|
+
description: 'Timing span emitted by WideEvent span helpers.',
|
|
30
|
+
});
|
|
31
|
+
const sloplogUsageError = partial('sloplog_usage_error', {
|
|
32
|
+
kind: z.string().describe('Usage error category.'),
|
|
33
|
+
message: z.string().describe('Human-readable usage error message.'),
|
|
34
|
+
partialType: z.string().optional().describe('Partial type involved in the error.'),
|
|
35
|
+
spanName: z.string().optional().describe('Span name involved in the error.'),
|
|
36
|
+
startedAt: z.number().optional().describe('Span start time in ms when relevant.'),
|
|
37
|
+
count: z.number().optional().describe('Optional count related to the error.'),
|
|
38
|
+
}, {
|
|
39
|
+
repeatable: true,
|
|
40
|
+
description: 'Internal usage errors emitted by sloplog (e.g. partial overwrites or span misuse).',
|
|
41
|
+
});
|
|
42
|
+
/** Built-in partial definitions shipped with sloplog. */
|
|
43
|
+
export const builtInPartials = {
|
|
44
|
+
error,
|
|
45
|
+
log_message: logMessage,
|
|
46
|
+
span,
|
|
47
|
+
sloplog_usage_error: sloplogUsageError,
|
|
48
|
+
};
|
|
49
|
+
/** Registry of built-in partial definitions. */
|
|
50
|
+
export const builtInRegistry = registry([error, logMessage, span, sloplogUsageError]);
|
|
51
|
+
/** Runtime metadata for built-in partials (repeatable, alwaysSample). */
|
|
52
|
+
export const builtInPartialMetadata = extractPartialMetadata(builtInRegistry);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z, type ZodObject, type ZodRawShape } from 'zod';
|
|
2
|
+
export { z };
|
|
3
|
+
type AllowedZodPrimitive = z.ZodString | z.ZodNumber | z.ZodBoolean | z.ZodEnum<Record<string, string>>;
|
|
4
|
+
type AllowedZodType = AllowedZodPrimitive | z.ZodArray<AllowedZodPrimitive> | z.ZodOptional<AllowedZodPrimitive> | z.ZodOptional<z.ZodArray<AllowedZodPrimitive>>;
|
|
5
|
+
type AllowedShape = {
|
|
6
|
+
[key: string]: AllowedZodType;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Options for defining a partial
|
|
10
|
+
*/
|
|
11
|
+
export interface PartialOptions {
|
|
12
|
+
/**
|
|
13
|
+
* If true, multiple instances of this partial can be attached to a single wide event.
|
|
14
|
+
* The partial will be stored as an array in the final log.
|
|
15
|
+
* @default false
|
|
16
|
+
*/
|
|
17
|
+
repeatable?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* If true, adding this partial to an event will mark the event to always be sampled.
|
|
20
|
+
* Useful for error partials or other critical events that should never be dropped.
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
alwaysSample?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Optional description used for generated docs and schemas.
|
|
26
|
+
*/
|
|
27
|
+
description?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface PartialDefinition<T extends string, S extends ZodRawShape, O extends PartialOptions = PartialOptions> {
|
|
30
|
+
/** Partial type discriminator */
|
|
31
|
+
name: T;
|
|
32
|
+
/** Zod schema for the partial payload */
|
|
33
|
+
schema: ZodObject<S>;
|
|
34
|
+
/** Repeatable and sampling behavior */
|
|
35
|
+
options: O;
|
|
36
|
+
}
|
|
37
|
+
export interface Registry<T extends PartialDefinition<string, ZodRawShape, PartialOptions>[]> {
|
|
38
|
+
/** List of partial definitions that make up this registry */
|
|
39
|
+
partials: T;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Runtime metadata about partials, extracted from the registry for use by WideEvent
|
|
43
|
+
*/
|
|
44
|
+
export interface PartialMetadata {
|
|
45
|
+
/** Whether this partial may appear multiple times */
|
|
46
|
+
repeatable: boolean;
|
|
47
|
+
/** Whether this partial forces alwaysSample for the event */
|
|
48
|
+
alwaysSample: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Callable partial definition that creates a partial payload from input data.
|
|
52
|
+
*/
|
|
53
|
+
export type PartialFactory<T extends string, S extends ZodRawShape, O extends PartialOptions = PartialOptions> = PartialDefinition<T, S, O> & {
|
|
54
|
+
(data: z.input<ZodObject<S>>): z.infer<ZodObject<S>> & {
|
|
55
|
+
type: T;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Define a partial with a name and Zod schema
|
|
60
|
+
* Only allows: z.string(), z.number(), z.boolean(), and their arrays/optionals
|
|
61
|
+
*
|
|
62
|
+
* @param name - The unique name/type discriminator for this partial
|
|
63
|
+
* @param schema - Zod schema defining the partial's fields
|
|
64
|
+
* @param options - Optional configuration for repeatable and alwaysSample behavior
|
|
65
|
+
*/
|
|
66
|
+
export declare function partial<T extends string, S extends AllowedShape, O extends PartialOptions = PartialOptions>(name: T, schema: S, options?: O): PartialFactory<T, S, O>;
|
|
67
|
+
/**
|
|
68
|
+
* Create a registry of partials
|
|
69
|
+
*/
|
|
70
|
+
export declare function registry<T extends PartialDefinition<string, ZodRawShape, PartialOptions>[]>(partials: T): Registry<T>;
|
|
71
|
+
/**
|
|
72
|
+
* Extract runtime metadata from a registry for use by WideEvent
|
|
73
|
+
*/
|
|
74
|
+
export declare function extractPartialMetadata(reg: Registry<PartialDefinition<string, ZodRawShape, PartialOptions>[]>): Map<string, PartialMetadata>;
|
|
75
|
+
/**
|
|
76
|
+
* Infer the TypeScript type from a partial definition
|
|
77
|
+
*/
|
|
78
|
+
export type InferPartial<T extends PartialDefinition<string, ZodRawShape>> = z.infer<T['schema']> & {
|
|
79
|
+
type: T['name'];
|
|
80
|
+
};
|
|
81
|
+
type PartialValueFor<P extends PartialDefinition<string, ZodRawShape, PartialOptions>> = P['options'] extends {
|
|
82
|
+
repeatable: true;
|
|
83
|
+
} ? InferPartial<P>[] : InferPartial<P>;
|
|
84
|
+
/**
|
|
85
|
+
* Infer the registry log shape from a registry definition.
|
|
86
|
+
*/
|
|
87
|
+
export type RegistryType<T extends Registry<PartialDefinition<string, ZodRawShape, PartialOptions>[]>> = {
|
|
88
|
+
[P in T['partials'][number] as P['name']]: PartialValueFor<P>;
|
|
89
|
+
};
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Re-export z for schema definitions
|
|
3
|
+
export { z };
|
|
4
|
+
/**
|
|
5
|
+
* Define a partial with a name and Zod schema
|
|
6
|
+
* Only allows: z.string(), z.number(), z.boolean(), and their arrays/optionals
|
|
7
|
+
*
|
|
8
|
+
* @param name - The unique name/type discriminator for this partial
|
|
9
|
+
* @param schema - Zod schema defining the partial's fields
|
|
10
|
+
* @param options - Optional configuration for repeatable and alwaysSample behavior
|
|
11
|
+
*/
|
|
12
|
+
export function partial(name, schema, options = {}) {
|
|
13
|
+
const objectSchema = z.object(schema);
|
|
14
|
+
const describedSchema = options.description
|
|
15
|
+
? objectSchema.describe(options.description)
|
|
16
|
+
: objectSchema;
|
|
17
|
+
const factory = ((data) => {
|
|
18
|
+
const parsed = describedSchema.parse(data);
|
|
19
|
+
return { type: name, ...parsed };
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(factory, 'name', { value: name, configurable: true });
|
|
22
|
+
factory.schema = describedSchema;
|
|
23
|
+
factory.options = options;
|
|
24
|
+
return factory;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a registry of partials
|
|
28
|
+
*/
|
|
29
|
+
export function registry(partials) {
|
|
30
|
+
return { partials };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract runtime metadata from a registry for use by WideEvent
|
|
34
|
+
*/
|
|
35
|
+
export function extractPartialMetadata(reg) {
|
|
36
|
+
const metadata = new Map();
|
|
37
|
+
for (const partial of reg.partials) {
|
|
38
|
+
metadata.set(partial.name, {
|
|
39
|
+
repeatable: partial.options.repeatable ?? false,
|
|
40
|
+
alwaysSample: partial.options.alwaysSample ?? false,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return metadata;
|
|
44
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sloplog",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "A TypeScript library for constructing wide events",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./codegen": {
|
|
14
|
+
"import": "./dist/codegen.js",
|
|
15
|
+
"types": "./dist/codegen.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./partials": {
|
|
18
|
+
"import": "./dist/partials.js",
|
|
19
|
+
"types": "./dist/partials.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./collectors/stdio": {
|
|
22
|
+
"import": "./dist/collectors/stdio.js",
|
|
23
|
+
"types": "./dist/collectors/stdio.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"./collectors/composite": {
|
|
26
|
+
"import": "./dist/collectors/composite.js",
|
|
27
|
+
"types": "./dist/collectors/composite.d.ts"
|
|
28
|
+
},
|
|
29
|
+
"./collectors/filtered": {
|
|
30
|
+
"import": "./dist/collectors/filtered.js",
|
|
31
|
+
"types": "./dist/collectors/filtered.d.ts"
|
|
32
|
+
},
|
|
33
|
+
"./collectors/file": {
|
|
34
|
+
"import": "./dist/collectors/file.js",
|
|
35
|
+
"types": "./dist/collectors/file.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./collectors/betterstack": {
|
|
38
|
+
"import": "./dist/collectors/betterstack.js",
|
|
39
|
+
"types": "./dist/collectors/betterstack.d.ts"
|
|
40
|
+
},
|
|
41
|
+
"./collectors/sentry": {
|
|
42
|
+
"import": "./dist/collectors/sentry.js",
|
|
43
|
+
"types": "./dist/collectors/sentry.d.ts"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"lint": "eslint .",
|
|
54
|
+
"lint:fix": "eslint . --fix",
|
|
55
|
+
"format": "prettier --write '**/*.{ts,js,json,md}'",
|
|
56
|
+
"format:check": "prettier --check '**/*.{ts,js,json,md}'",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"check": "npm run lint && npm run format:check && npm run typecheck",
|
|
59
|
+
"prepublishOnly": "npm run check && npm run build && npm test"
|
|
60
|
+
},
|
|
61
|
+
"keywords": [
|
|
62
|
+
"wide-events",
|
|
63
|
+
"logging",
|
|
64
|
+
"observability",
|
|
65
|
+
"tracing",
|
|
66
|
+
"structured-logging"
|
|
67
|
+
],
|
|
68
|
+
"author": "",
|
|
69
|
+
"license": "MIT",
|
|
70
|
+
"repository": {
|
|
71
|
+
"type": "git",
|
|
72
|
+
"url": "https://github.com/cmsparks/sloplog.git"
|
|
73
|
+
},
|
|
74
|
+
"homepage": "https://github.com/cmsparks/sloplog#readme",
|
|
75
|
+
"bugs": {
|
|
76
|
+
"url": "https://github.com/cmsparks/sloplog/issues"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@eslint/js": "^9.39.2",
|
|
80
|
+
"@types/node": "^25.0.9",
|
|
81
|
+
"eslint": "^9.39.2",
|
|
82
|
+
"eslint-config-prettier": "^10.1.8",
|
|
83
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
84
|
+
"prettier": "^3.8.0",
|
|
85
|
+
"pyright": "^1.1.408",
|
|
86
|
+
"typescript": "^5.7.0",
|
|
87
|
+
"typescript-eslint": "^8.53.1",
|
|
88
|
+
"vitest": "^2.1.0"
|
|
89
|
+
},
|
|
90
|
+
"dependencies": {
|
|
91
|
+
"zod": "^4.3.5"
|
|
92
|
+
},
|
|
93
|
+
"peerDependencies": {
|
|
94
|
+
"@sentry/node": "*"
|
|
95
|
+
},
|
|
96
|
+
"peerDependenciesMeta": {
|
|
97
|
+
"@sentry/node": {
|
|
98
|
+
"optional": true
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|