@zintrust/trace 0.9.2 → 0.9.4
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 +31 -0
- package/dist/TraceConnection.d.ts +25 -0
- package/dist/TraceConnection.js +98 -0
- package/dist/build-manifest.json +56 -24
- package/dist/config.js +18 -0
- package/dist/dashboard/routes.js +12 -16
- package/dist/dashboard/ui.js +32 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ingest/TraceIngestGateway.d.ts +22 -0
- package/dist/ingest/TraceIngestGateway.js +215 -0
- package/dist/register.js +59 -69
- package/dist/storage/ProxyTraceStorage.d.ts +12 -0
- package/dist/storage/ProxyTraceStorage.js +102 -0
- package/dist/storage/TraceContentBudget.js +1 -0
- package/dist/storage/TraceServiceTag.d.ts +5 -0
- package/dist/storage/TraceServiceTag.js +43 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.js +2 -0
- package/dist/types.d.ts +10 -0
- package/package.json +3 -3
- package/src/TraceConnection.ts +182 -0
- package/src/config.ts +23 -0
- package/src/dashboard/routes.ts +24 -26
- package/src/dashboard/ui.ts +32 -4
- package/src/index.ts +1 -0
- package/src/ingest/TraceIngestGateway.ts +317 -0
- package/src/register.ts +73 -114
- package/src/storage/ProxyTraceStorage.ts +182 -0
- package/src/storage/TraceContentBudget.ts +1 -0
- package/src/storage/TraceServiceTag.ts +56 -0
- package/src/storage/index.ts +2 -0
- package/src/types.ts +11 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { ErrorFactory, RemoteSignedJson } from '@zintrust/core';
|
|
2
|
+
import type { ITraceEntry, ITraceStorage } from '../types';
|
|
3
|
+
|
|
4
|
+
type ProxyTraceStorageSettings = {
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
path: string;
|
|
7
|
+
keyId: string;
|
|
8
|
+
secret: string;
|
|
9
|
+
timeoutMs: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type TraceProxyWriteRequest = {
|
|
13
|
+
entry: ITraceEntry;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type TraceProxyUpdateRequest = {
|
|
17
|
+
uuid: string;
|
|
18
|
+
patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type TraceProxyMarkFamilyStaleRequest = {
|
|
22
|
+
familyHash: string;
|
|
23
|
+
exceptUuid: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const ensureConfigured = (settings: ProxyTraceStorageSettings): void => {
|
|
27
|
+
if (settings.baseUrl.trim() === '') {
|
|
28
|
+
throw ErrorFactory.createConfigError('TRACE_PROXY_URL is required when TRACE_PROXY=true');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (settings.keyId.trim() === '' || settings.secret.trim() === '') {
|
|
32
|
+
throw ErrorFactory.createConfigError(
|
|
33
|
+
'TRACE_PROXY signing credentials are required when TRACE_PROXY=true'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const normalizePath = (value: string): string => {
|
|
39
|
+
const trimmed = value.trim();
|
|
40
|
+
if (trimmed === '') return '/zin/trace/write';
|
|
41
|
+
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const createUnsupportedReadError = (): Error =>
|
|
45
|
+
ErrorFactory.createConfigError(
|
|
46
|
+
'Trace proxy sender storage does not expose dashboard/query operations. Use the trace server for reads.'
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
type ProxyRequestSettings = {
|
|
50
|
+
baseUrl: string;
|
|
51
|
+
keyId: string;
|
|
52
|
+
secret: string;
|
|
53
|
+
timeoutMs: number;
|
|
54
|
+
signaturePathPrefixToStrip: string;
|
|
55
|
+
missingUrlMessage: string;
|
|
56
|
+
missingCredentialsMessage: string;
|
|
57
|
+
messages: {
|
|
58
|
+
unauthorized: string;
|
|
59
|
+
forbidden: string;
|
|
60
|
+
rateLimited: string;
|
|
61
|
+
rejected: string;
|
|
62
|
+
error: string;
|
|
63
|
+
timedOut: string;
|
|
64
|
+
};
|
|
65
|
+
normalizedPath: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const buildSettings = (settings: ProxyTraceStorageSettings): ProxyRequestSettings => {
|
|
69
|
+
ensureConfigured(settings);
|
|
70
|
+
const normalizedPath = normalizePath(settings.path);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
baseUrl: settings.baseUrl,
|
|
74
|
+
keyId: settings.keyId,
|
|
75
|
+
secret: settings.secret,
|
|
76
|
+
timeoutMs: settings.timeoutMs,
|
|
77
|
+
signaturePathPrefixToStrip: new URL(settings.baseUrl).pathname,
|
|
78
|
+
missingUrlMessage: 'TRACE_PROXY_URL is required when TRACE_PROXY=true',
|
|
79
|
+
missingCredentialsMessage: 'TRACE_PROXY signing credentials are required when TRACE_PROXY=true',
|
|
80
|
+
messages: {
|
|
81
|
+
unauthorized: 'Trace proxy rejected the request credentials',
|
|
82
|
+
forbidden: 'Trace proxy rejected the request signature',
|
|
83
|
+
rateLimited: 'Trace proxy rate-limited the request',
|
|
84
|
+
rejected: 'Trace proxy rejected the request payload',
|
|
85
|
+
error: 'Trace proxy request failed',
|
|
86
|
+
timedOut: 'Trace proxy request timed out',
|
|
87
|
+
},
|
|
88
|
+
normalizedPath,
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const appendSuffix = (path: string, suffix: string): string => {
|
|
93
|
+
const base = normalizePath(path).replace(/\/+$/, '');
|
|
94
|
+
const tail = suffix.startsWith('/') ? suffix : `/${suffix}`;
|
|
95
|
+
return `${base}${tail}`;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const unsupportedQueryEntries: ITraceStorage['queryEntries'] = async () => {
|
|
99
|
+
throw createUnsupportedReadError();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const unsupportedGetEntry: ITraceStorage['getEntry'] = async () => {
|
|
103
|
+
throw createUnsupportedReadError();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const unsupportedGetBatch: ITraceStorage['getBatch'] = async () => {
|
|
107
|
+
throw createUnsupportedReadError();
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const unsupportedQueryBatchEntries: ITraceStorage['queryBatchEntries'] = async () => {
|
|
111
|
+
throw createUnsupportedReadError();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const unsupportedPrune: ITraceStorage['prune'] = async () => {
|
|
115
|
+
throw createUnsupportedReadError();
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const unsupportedClear: ITraceStorage['clear'] = async () => {
|
|
119
|
+
throw createUnsupportedReadError();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const unsupportedGetMonitoring: ITraceStorage['getMonitoring'] = async () => {
|
|
123
|
+
throw createUnsupportedReadError();
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const unsupportedAddMonitoring: ITraceStorage['addMonitoring'] = async () => {
|
|
127
|
+
throw createUnsupportedReadError();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const unsupportedRemoveMonitoring: ITraceStorage['removeMonitoring'] = async () => {
|
|
131
|
+
throw createUnsupportedReadError();
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const unsupportedStats: ITraceStorage['stats'] = async () => {
|
|
135
|
+
throw createUnsupportedReadError();
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const ProxyTraceStorage = Object.freeze({
|
|
139
|
+
create(settings: ProxyTraceStorageSettings): ITraceStorage {
|
|
140
|
+
const normalized = buildSettings(settings);
|
|
141
|
+
|
|
142
|
+
return Object.freeze({
|
|
143
|
+
async writeEntry(entry: ITraceEntry): Promise<void> {
|
|
144
|
+
await RemoteSignedJson.request<{ ok: true }>(normalized, normalized.normalizedPath, {
|
|
145
|
+
entry,
|
|
146
|
+
} satisfies TraceProxyWriteRequest);
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
async updateEntry(
|
|
150
|
+
uuid: string,
|
|
151
|
+
patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
await RemoteSignedJson.request<{ ok: true }>(
|
|
154
|
+
normalized,
|
|
155
|
+
appendSuffix(normalized.normalizedPath, '/update'),
|
|
156
|
+
{ uuid, patch } satisfies TraceProxyUpdateRequest
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
async markFamilyStale(familyHash: string, exceptUuid: string): Promise<void> {
|
|
161
|
+
await RemoteSignedJson.request<{ ok: true }>(
|
|
162
|
+
normalized,
|
|
163
|
+
appendSuffix(normalized.normalizedPath, '/mark-family-stale'),
|
|
164
|
+
{ familyHash, exceptUuid } satisfies TraceProxyMarkFamilyStaleRequest
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
queryEntries: unsupportedQueryEntries,
|
|
169
|
+
getEntry: unsupportedGetEntry,
|
|
170
|
+
getBatch: unsupportedGetBatch,
|
|
171
|
+
queryBatchEntries: unsupportedQueryBatchEntries,
|
|
172
|
+
prune: unsupportedPrune,
|
|
173
|
+
clear: unsupportedClear,
|
|
174
|
+
getMonitoring: unsupportedGetMonitoring,
|
|
175
|
+
addMonitoring: unsupportedAddMonitoring,
|
|
176
|
+
removeMonitoring: unsupportedRemoveMonitoring,
|
|
177
|
+
stats: unsupportedStats,
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
export default ProxyTraceStorage;
|
|
@@ -285,6 +285,7 @@ const getCoreRuntime = async (): Promise<{
|
|
|
285
285
|
|
|
286
286
|
const getQueueWorkerApi = async (): Promise<QueueWorkerApi | null> => {
|
|
287
287
|
try {
|
|
288
|
+
// @ts-ignore
|
|
288
289
|
const mod = (await import('@zintrust/workers')) as unknown as QueueWorkerApi;
|
|
289
290
|
return typeof mod.createQueueWorker === 'function' ? mod : null;
|
|
290
291
|
} catch {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ErrorFactory } from '@zintrust/core';
|
|
2
|
+
import type { ITraceConfig, ITraceEntry, ITraceStorage } from '../types';
|
|
3
|
+
|
|
4
|
+
const appendServiceTag = (entry: ITraceEntry, serviceTag?: string): ITraceEntry => {
|
|
5
|
+
const normalizedTag = serviceTag?.trim() ?? '';
|
|
6
|
+
if (normalizedTag === '' || entry.tags.includes(normalizedTag)) {
|
|
7
|
+
return entry;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
...entry,
|
|
12
|
+
tags: [...entry.tags, normalizedTag],
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const unsupportedRead = async <T>(): Promise<T> => {
|
|
17
|
+
throw ErrorFactory.createConfigError(
|
|
18
|
+
'Trace proxy mode only supports runtime persistence on the sender. Query the trace server database or dashboard directly.'
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const bindOrUnsupported = <T extends (...args: never[]) => Promise<unknown>>(
|
|
23
|
+
method: T | undefined
|
|
24
|
+
): T => {
|
|
25
|
+
if (method === undefined) {
|
|
26
|
+
return unsupportedRead as unknown as T;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return method;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const TraceServiceTag = Object.freeze({
|
|
33
|
+
wrapStorage(storage: ITraceStorage, config: ITraceConfig): ITraceStorage {
|
|
34
|
+
const writeEntry = async (entry: ITraceEntry): Promise<void> => {
|
|
35
|
+
await storage.writeEntry(appendServiceTag(entry, config.serviceTag));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
writeEntry,
|
|
40
|
+
updateEntry: storage.updateEntry.bind(storage),
|
|
41
|
+
markFamilyStale: storage.markFamilyStale.bind(storage),
|
|
42
|
+
queryEntries: bindOrUnsupported(storage.queryEntries?.bind(storage)),
|
|
43
|
+
getEntry: bindOrUnsupported(storage.getEntry?.bind(storage)),
|
|
44
|
+
getBatch: bindOrUnsupported(storage.getBatch?.bind(storage)),
|
|
45
|
+
queryBatchEntries: bindOrUnsupported(storage.queryBatchEntries?.bind(storage)),
|
|
46
|
+
prune: bindOrUnsupported(storage.prune?.bind(storage)),
|
|
47
|
+
clear: bindOrUnsupported(storage.clear?.bind(storage)),
|
|
48
|
+
getMonitoring: bindOrUnsupported(storage.getMonitoring?.bind(storage)),
|
|
49
|
+
addMonitoring: bindOrUnsupported(storage.addMonitoring?.bind(storage)),
|
|
50
|
+
removeMonitoring: bindOrUnsupported(storage.removeMonitoring?.bind(storage)),
|
|
51
|
+
stats: bindOrUnsupported(storage.stats?.bind(storage)),
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export default TraceServiceTag;
|
package/src/storage/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -373,6 +373,15 @@ export type TraceContentDispatchConfig = {
|
|
|
373
373
|
worker: TraceContentDispatchWorkerConfig;
|
|
374
374
|
};
|
|
375
375
|
|
|
376
|
+
export type TraceProxyConfig = {
|
|
377
|
+
enabled: boolean;
|
|
378
|
+
url?: string;
|
|
379
|
+
path: string;
|
|
380
|
+
keyId?: string;
|
|
381
|
+
secret?: string;
|
|
382
|
+
timeoutMs: number;
|
|
383
|
+
};
|
|
384
|
+
|
|
376
385
|
export type TraceWatcherToggle = boolean | TraceFilterRule;
|
|
377
386
|
export type TraceRequestWatcherToggle = boolean | TraceRequestWatcherConfig;
|
|
378
387
|
export type TraceClientRequestWatcherToggle = boolean | TraceClientRequestWatcherConfig;
|
|
@@ -404,6 +413,8 @@ export interface ITraceConfig {
|
|
|
404
413
|
enabled: boolean;
|
|
405
414
|
connection?: string;
|
|
406
415
|
observeConnection?: string;
|
|
416
|
+
serviceTag?: string;
|
|
417
|
+
proxy: TraceProxyConfig;
|
|
407
418
|
pruneAfterHours: number;
|
|
408
419
|
ignoreRoutes: string[];
|
|
409
420
|
ignorePaths: string[];
|