@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.
@@ -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;
@@ -1,2 +1,4 @@
1
1
  export { TraceStorage } from './TraceStorage';
2
2
  export type { ITraceStorage } from './TraceStorage';
3
+ export { ProxyTraceStorage } from './ProxyTraceStorage';
4
+ export { TraceServiceTag } from './TraceServiceTag';
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[];