@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 CHANGED
@@ -32,6 +32,13 @@ You can still import the package migrations manually if you prefer to keep them
32
32
  TRACE_ENABLED=true
33
33
  TRACE_DB_CONNECTION=d1 # optional — omit to inherit DB_CONNECTION
34
34
  TRACE_QUERY_CONNECTION=main # optional — app DB to observe for SQL traces
35
+ TRACE_SERVICE_TAG= # optional — global trace tag, falls back to APP_NAME when empty
36
+ TRACE_PROXY=false # optional — send writes to a remote trace server instead of local DB
37
+ TRACE_PROXY_URL= # required when TRACE_PROXY=true
38
+ TRACE_PROXY_PATH=/zin/trace/write
39
+ TRACE_PROXY_KEY_ID= # optional — falls back to APP_NAME
40
+ TRACE_PROXY_SECRET= # optional — falls back to APP_KEY
41
+ TRACE_PROXY_TIMEOUT_MS=30000
35
42
  TRACE_PRUNE_HOURS=24 # how long entries are kept (default: 24)
36
43
  TRACE_SLOW_QUERY_MS=100 # slow-query threshold in ms (default: 100)
37
44
  TRACE_LOG_LEVEL=info # minimum log level captured (default: info)
@@ -52,6 +59,8 @@ TRACE_REDACT_QUERY=
52
59
 
53
60
  When `TRACE_CONTENT_QUEUE_DRIVER` is set, trace writes enqueue through that registered queue driver and an internal trace worker drains them outside the live request path. When it is unset, oversized content is replaced with `Trace content exceeded budget and was replaced.` before persistence instead of running the heavy compaction loop inline.
54
61
 
62
+ When `TRACE_PROXY=true`, the local runtime keeps collecting the same trace payload it would normally send to storage, but it sends the write/update/stale-family operations to `TRACE_PROXY_URL + TRACE_PROXY_PATH` instead of writing directly to the local trace database. The receiver can then persist those entries with the standard `TraceStorage` flow.
63
+
55
64
  This currently works with any queue driver already registered in ZinTrust. First-class Cloudflare Queue support still requires a dedicated queue driver and queue-runtime registration for that transport.
56
65
 
57
66
  ### 2. Enable the plugin in `zintrust.plugins.*`
@@ -154,6 +163,28 @@ registerTraceDashboard(router, {
154
163
  });
155
164
  ```
156
165
 
166
+ ### 3b. Optional remote trace ingest gateway
167
+
168
+ If you want one project to send trace writes to another project, mount the signed ingest gateway on the trace server:
169
+
170
+ ```ts
171
+ // routes/api.ts
172
+ import { registerTraceIngestGateway } from '@zintrust/trace';
173
+
174
+ registerTraceIngestGateway(router, {
175
+ basePath: '/zin/trace/write',
176
+ middleware: ['admin'], // optional
177
+ });
178
+ ```
179
+
180
+ The sender runtime uses `TRACE_PROXY_URL`, `TRACE_PROXY_PATH`, `TRACE_PROXY_KEY_ID`, and `TRACE_PROXY_SECRET` to sign write requests. On the receiver side, `TRACE_PROXY_KEY_ID` and `TRACE_PROXY_SECRET` must match the sender pair or fall back to the same `APP_NAME` and `APP_KEY` values.
181
+
182
+ The ingest gateway exposes exactly these signed POST endpoints:
183
+
184
+ - `TRACE_PROXY_PATH` for new trace entries
185
+ - `TRACE_PROXY_PATH + /update` for trace content updates
186
+ - `TRACE_PROXY_PATH + /mark-family-stale` for latest-entry rotation
187
+
157
188
  The dashboard SPA will be available at `GET /trace` (or your chosen `basePath`).
158
189
 
159
190
  If you need custom storage wiring, keep using the low-level route registrar:
@@ -0,0 +1,25 @@
1
+ import type { IDatabase } from '@zintrust/core';
2
+ type TraceErrorFactory = {
3
+ createConfigError?(message: string, details?: unknown): Error;
4
+ };
5
+ type TraceErrorApi = {
6
+ ErrorFactory?: TraceErrorFactory;
7
+ };
8
+ type TraceEnvApi = {
9
+ get(key: string, fallback: string): string;
10
+ };
11
+ export declare const TRACE_REQUIRED_TABLES: readonly ["zin_trace_entries", "zin_trace_entries_tags", "zin_trace_monitoring"];
12
+ export declare const createTraceConfigError: (coreApi: TraceErrorApi, message: string, details?: unknown) => Error;
13
+ export declare const getRuntimeTraceConnectionName: () => string | undefined;
14
+ export declare const resolveDashboardTraceConnectionName: (coreApi: TraceErrorApi, input: {
15
+ explicitConnectionName?: string;
16
+ configuredConnectionName?: string;
17
+ }) => string;
18
+ export declare const resolveTraceConnectionName: (env: Pick<TraceEnvApi, "get"> | undefined, configuredConnection?: string) => string;
19
+ export declare const resolveObservedConnectionName: (env: Pick<TraceEnvApi, "get"> | undefined, configuredObservedConnection: string | undefined, storageConnectionName: string) => string;
20
+ export declare function assertTraceConnectionResolved(coreApi: TraceErrorApi, db: IDatabase | undefined, params: {
21
+ connectionName: string;
22
+ envKey: 'TRACE_DB_CONNECTION' | 'TRACE_QUERY_CONNECTION';
23
+ }): asserts db is IDatabase;
24
+ export declare const assertTraceStorageReady: (coreApi: TraceErrorApi, db: IDatabase, connectionName: string, operation?: string) => Promise<void>;
25
+ export {};
@@ -0,0 +1,98 @@
1
+ export const TRACE_REQUIRED_TABLES = [
2
+ 'zin_trace_entries',
3
+ 'zin_trace_entries_tags',
4
+ 'zin_trace_monitoring',
5
+ ];
6
+ const createFallbackTraceConfigError = (message, details) => {
7
+ const error = new globalThis.Error(message);
8
+ error.name = 'ConfigError';
9
+ error.code = 'CONFIG_ERROR';
10
+ error.statusCode = 500;
11
+ error.details = details;
12
+ return error;
13
+ };
14
+ export const createTraceConfigError = (coreApi, message, details) => {
15
+ if (coreApi.ErrorFactory?.createConfigError !== undefined) {
16
+ return coreApi.ErrorFactory.createConfigError(message, details);
17
+ }
18
+ return createFallbackTraceConfigError(message, details);
19
+ };
20
+ export const getRuntimeTraceConnectionName = () => {
21
+ const runtimeConnection = globalThis.__zintrust_system_trace_connection_name__?.trim();
22
+ return runtimeConnection === undefined || runtimeConnection === ''
23
+ ? undefined
24
+ : runtimeConnection;
25
+ };
26
+ export const resolveDashboardTraceConnectionName = (coreApi, input) => {
27
+ const explicitConnection = input.explicitConnectionName?.trim();
28
+ if (explicitConnection !== undefined && explicitConnection !== '') {
29
+ return explicitConnection;
30
+ }
31
+ const runtimeConnection = getRuntimeTraceConnectionName();
32
+ if (runtimeConnection !== undefined) {
33
+ return runtimeConnection;
34
+ }
35
+ const configuredConnection = input.configuredConnectionName?.trim();
36
+ if (configuredConnection !== undefined && configuredConnection !== '') {
37
+ return configuredConnection;
38
+ }
39
+ throw createTraceConfigError(coreApi, 'Trace dashboard connection is not configured.', {
40
+ envKey: 'TRACE_DB_CONNECTION',
41
+ hint: 'Import @zintrust/trace/register before mounting the dashboard, pass connectionName explicitly, or set TRACE_DB_CONNECTION to the trace storage connection.',
42
+ });
43
+ };
44
+ export const resolveTraceConnectionName = (env, configuredConnection) => {
45
+ const resolveDefaultConnection = () => {
46
+ const defaultConnection = env?.get('DB_CONNECTION', '').trim() ?? '';
47
+ if (defaultConnection === '' || defaultConnection === 'default')
48
+ return 'default';
49
+ return defaultConnection;
50
+ };
51
+ const explicitConnection = configuredConnection?.trim();
52
+ if (explicitConnection !== undefined && explicitConnection !== '') {
53
+ return explicitConnection === 'default' ? resolveDefaultConnection() : explicitConnection;
54
+ }
55
+ return resolveDefaultConnection();
56
+ };
57
+ export const resolveObservedConnectionName = (env, configuredObservedConnection, storageConnectionName) => {
58
+ if (typeof configuredObservedConnection === 'string' &&
59
+ configuredObservedConnection.trim() !== '') {
60
+ return resolveTraceConnectionName(env, configuredObservedConnection);
61
+ }
62
+ const defaultConnectionName = resolveTraceConnectionName(env);
63
+ if (storageConnectionName !== defaultConnectionName) {
64
+ return defaultConnectionName;
65
+ }
66
+ return storageConnectionName;
67
+ };
68
+ export function assertTraceConnectionResolved(coreApi, db, params) {
69
+ if (db !== undefined) {
70
+ return;
71
+ }
72
+ const pluginRequested = globalThis.__zintrust_system_trace_plugin_requested__ === true;
73
+ let hint = 'Configure TRACE_QUERY_CONNECTION, or ensure DB_CONNECTION resolves to an existing database connection.';
74
+ if (params.envKey === 'TRACE_DB_CONNECTION') {
75
+ hint = pluginRequested
76
+ ? 'Configure TRACE_DB_CONNECTION to an existing database connection before enabling TRACE_ENABLED.'
77
+ : 'If this module is being imported from zintrust.plugins.*, switch that import to @zintrust/trace/plugin so trace registration runs after database runtime registration. Otherwise configure TRACE_DB_CONNECTION to an existing database connection before enabling TRACE_ENABLED.';
78
+ }
79
+ throw createTraceConfigError(coreApi, `Trace connection "${params.connectionName}" could not be resolved.`, {
80
+ connectionName: params.connectionName,
81
+ envKey: params.envKey,
82
+ hint,
83
+ });
84
+ }
85
+ export const assertTraceStorageReady = async (coreApi, db, connectionName, operation = 'Trace storage connection') => {
86
+ try {
87
+ await Promise.all(TRACE_REQUIRED_TABLES.map(async (table) => {
88
+ await db.queryOne(`SELECT 1 AS ok FROM ${table} LIMIT 1`, []);
89
+ }));
90
+ }
91
+ catch (error) {
92
+ throw createTraceConfigError(coreApi, `${operation} "${connectionName}" is not ready. Create the database if needed and run \`zin migrate:trace\` before enabling TRACE_ENABLED.`, {
93
+ connectionName,
94
+ error,
95
+ requiredTables: [...TRACE_REQUIRED_TABLES],
96
+ });
97
+ }
98
+ };
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@zintrust/trace",
3
- "version": "0.9.1",
4
- "buildDate": "2026-04-21T12:53:31.026Z",
3
+ "version": "0.9.4",
4
+ "buildDate": "2026-04-23T11:29:28.904Z",
5
5
  "buildEnvironment": {
6
6
  "node": "v22.22.1",
7
7
  "platform": "darwin",
8
8
  "arch": "arm64"
9
9
  },
10
10
  "git": {
11
- "commit": "37f8ec23",
11
+ "commit": "eebcc3ad",
12
12
  "branch": "release"
13
13
  },
14
14
  "package": {
@@ -21,9 +21,17 @@
21
21
  ]
22
22
  },
23
23
  "files": {
24
+ "TraceConnection.d.ts": {
25
+ "size": 1500,
26
+ "sha256": "0748601bebff011a0b3dbab736617d5e3dffe4af671f4b6f7af03012d58e5464"
27
+ },
28
+ "TraceConnection.js": {
29
+ "size": 4640,
30
+ "sha256": "c51cc312046b6b2bbe1673f1ff9508425cc7140a1d2341907f67aa36069c09f9"
31
+ },
24
32
  "build-manifest.json": {
25
33
  "size": 14744,
26
- "sha256": "42a990aa416b8ccae3d5a48fe0366bb45424167b63423311b94547aeafcee2b1"
34
+ "sha256": "92fc9e1e76ba84696dbdaf02aa2ed7588c6e1234d586e24bf8bab4f54104ad61"
27
35
  },
28
36
  "cli-register.d.ts": {
29
37
  "size": 255,
@@ -38,8 +46,8 @@
38
46
  "sha256": "b034cbef0c71fb868071363624ef7a9f8d7acc20f8be8c895dd5db5a75e81f37"
39
47
  },
40
48
  "config.js": {
41
- "size": 10064,
42
- "sha256": "cef8c30f280d02b765c3930c1f5d9bac083596f9b944197fe4ad008be6627475"
49
+ "size": 10503,
50
+ "sha256": "4514e95067194c7afacb1202a8f1caffc64a400dd995cf809301b1a51a67da38"
43
51
  },
44
52
  "context.d.ts": {
45
53
  "size": 596,
@@ -62,24 +70,32 @@
62
70
  "sha256": "0ac87bc54e57978e8a9ac5924d248e1f984927bb6c819ea5528ab1179b0b50b8"
63
71
  },
64
72
  "dashboard/routes.js": {
65
- "size": 2694,
66
- "sha256": "3afcfe7d01ea4592520f76af4bfd2b0107f85ee2d7736e68d741642a5f769a58"
73
+ "size": 2501,
74
+ "sha256": "06f0bf42214ed7691907443c0a1d5a4d8c3db3ce3f7b8269ba401ff421a54dfd"
67
75
  },
68
76
  "dashboard/ui.d.ts": {
69
77
  "size": 117,
70
78
  "sha256": "4862b41e0477f01afa0dbb446d4553b65c22ed774cd1e2db3489059ced392f94"
71
79
  },
72
80
  "dashboard/ui.js": {
73
- "size": 82022,
74
- "sha256": "e4bff41250c0251d33534b2c54412902aecf61051394b55a32b3673b42e5be48"
81
+ "size": 84221,
82
+ "sha256": "b260cd17012cd5bf5f24f24eb8958d0cf79bed1d976412def3706f960b3aefa6"
75
83
  },
76
84
  "index.d.ts": {
77
- "size": 2599,
78
- "sha256": "5ee9fa0c65942078a1ac7f38afcdc6b86595842191c766190974733c6f8d86c6"
85
+ "size": 2693,
86
+ "sha256": "901aaefe88fb2cc1e7771cd541f41ebcfe6443390bd66905eacbda51f1a6f73d"
79
87
  },
80
88
  "index.js": {
81
- "size": 3324,
82
- "sha256": "079fb9a8cab3c7ef159dc01e6c9d6078aaaa546745cd800dc66ac03364f6aeda"
89
+ "size": 3421,
90
+ "sha256": "478b80c6aa6c2eea2d109aa2e6fe090d77469d186b3e6a19ababb7d1f6d0be7e"
91
+ },
92
+ "ingest/TraceIngestGateway.d.ts": {
93
+ "size": 786,
94
+ "sha256": "3a0a46fa5bbf5367047214533ec0ee92a171ee35a60d25c197eea1eed1ff0f65"
95
+ },
96
+ "ingest/TraceIngestGateway.js": {
97
+ "size": 8456,
98
+ "sha256": "56df56cecda7110d1096c3beef1ef62c386f4a0e85120ebfb4cdfc5ab3b758d7"
83
99
  },
84
100
  "migrations/20260331000001_create_zin_trace_entries_table.d.ts": {
85
101
  "size": 304,
@@ -134,8 +150,8 @@
134
150
  "sha256": "71d366165dd36f1675aa253a76262b226fb6c62e5ab632746b8aea61c0c625fc"
135
151
  },
136
152
  "register.js": {
137
- "size": 19340,
138
- "sha256": "f4338b5bc506958558f9f54a6db9867c2ab0c5885d3ef4a340c814a7d53ef63a"
153
+ "size": 19822,
154
+ "sha256": "c553356d90c812f7de430d5679b1d44468131433e034ae2ea89e2876b1254444"
139
155
  },
140
156
  "storage/DebuggerStorage.d.ts": {
141
157
  "size": 517,
@@ -145,13 +161,21 @@
145
161
  "size": 7442,
146
162
  "sha256": "5ecce0fcfcf695df587a7b90a7a5c7efd2e64ad13c9f2d104b392f89f34f0dc4"
147
163
  },
164
+ "storage/ProxyTraceStorage.d.ts": {
165
+ "size": 339,
166
+ "sha256": "9c724ff342dfe82da12e7cce95593f6623faa80c40f97593719b8e78e95f266d"
167
+ },
168
+ "storage/ProxyTraceStorage.js": {
169
+ "size": 4158,
170
+ "sha256": "097d82e94c6ef38661fb57a20d378ba890dbacd03a22666aaf4aba4dfdedf735"
171
+ },
148
172
  "storage/TraceContentBudget.d.ts": {
149
173
  "size": 1306,
150
174
  "sha256": "606a37af0a4aef4866c22cc727f67f485c43181b40eb831e1920b8b90fdaf503"
151
175
  },
152
176
  "storage/TraceContentBudget.js": {
153
- "size": 11412,
154
- "sha256": "e5a72a6bb0c8bb432ea4aa7e8c5b43a8bf9e83d1b2935e102b4d1697da5fd18e"
177
+ "size": 11434,
178
+ "sha256": "f53229e7233cf13b095780451b68c912a95dc3f44e17c49289446219231e7e06"
155
179
  },
156
180
  "storage/TraceContentRedaction.d.ts": {
157
181
  "size": 207,
@@ -169,6 +193,14 @@
169
193
  "size": 2465,
170
194
  "sha256": "ace7e492373d2dccdbefe20040d30fffd7ad9f8e71113b7a044bddf92dcdf6fb"
171
195
  },
196
+ "storage/TraceServiceTag.d.ts": {
197
+ "size": 224,
198
+ "sha256": "35d93c82d6db80071946adaa8e40d012408b469949f1002f85ae3fd20b0a70c8"
199
+ },
200
+ "storage/TraceServiceTag.js": {
201
+ "size": 1916,
202
+ "sha256": "1ddad82563c98ddbb4033e9b8fb31901d635cffa7024d499a896f0d7a6d00900"
203
+ },
172
204
  "storage/TraceStorage.d.ts": {
173
205
  "size": 517,
174
206
  "sha256": "c9c215aaa414f7b0c1fec6c82b054fc52bdf97af58f96f35c7f96672fb859c31"
@@ -186,16 +218,16 @@
186
218
  "sha256": "6fc34e6e52a9b463db0967dba4aec4c8c706b6978c496f568637ffc15327279a"
187
219
  },
188
220
  "storage/index.d.ts": {
189
- "size": 100,
190
- "sha256": "1f2c1529bd5fa9c7a026695e4a8b184973b80d149afc778a8d86bcd03cbe093c"
221
+ "size": 210,
222
+ "sha256": "bb4c3a0c73eb3e2629dd1a1dbc29eecedd1910a37b221357cc7981ced320bdeb"
191
223
  },
192
224
  "storage/index.js": {
193
- "size": 50,
194
- "sha256": "d916e8e3abb1b1087f6b184851b0e6265e53380d7857b008e745d566aad15d44"
225
+ "size": 166,
226
+ "sha256": "ed5e83e108cd15f9a3976be71e966bdf0c44d8e0266d1d2dbad189f0885ad501"
195
227
  },
196
228
  "types.d.ts": {
197
- "size": 9825,
198
- "sha256": "53a1b2a572b9b5a73261f8c9b85903a3955bf71ec582f96c5b5249951aeb9b88"
229
+ "size": 10037,
230
+ "sha256": "68a2de953af2fce1a12447078177ff886f10e853c760378e13478b2558648868"
199
231
  },
200
232
  "types.js": {
201
233
  "size": 696,
package/dist/config.js CHANGED
@@ -163,10 +163,27 @@ const mergeContentDispatch = (base, override) => {
163
163
  },
164
164
  };
165
165
  };
166
+ const mergeProxyConfig = (base, override) => {
167
+ if (override === undefined)
168
+ return base;
169
+ return {
170
+ ...base,
171
+ ...override,
172
+ };
173
+ };
166
174
  const DEFAULTS = Object.freeze({
167
175
  enabled: false,
168
176
  connection: undefined,
169
177
  observeConnection: undefined,
178
+ serviceTag: undefined,
179
+ proxy: {
180
+ enabled: false,
181
+ url: undefined,
182
+ path: '/zin/trace/write',
183
+ keyId: undefined,
184
+ secret: undefined,
185
+ timeoutMs: 30000,
186
+ },
170
187
  pruneAfterHours: 24,
171
188
  ignoreRoutes: ['/trace', '/health', '/ping'],
172
189
  ignorePaths: [],
@@ -247,6 +264,7 @@ export const TraceConfig = Object.freeze({
247
264
  return Object.freeze({
248
265
  ...DEFAULTS,
249
266
  ...overrides,
267
+ proxy: mergeProxyConfig(DEFAULTS.proxy, overrides.proxy),
250
268
  contentDispatch: mergeContentDispatch(DEFAULTS.contentDispatch, overrides.contentDispatch),
251
269
  watchers: mergeWatchers(DEFAULTS.watchers, overrides.watchers),
252
270
  redaction: {
@@ -3,25 +3,12 @@
3
3
  * Mounts the SPA + all REST API endpoints under the configured basePath.
4
4
  * Auth is NOT applied here — callers add middleware via routeOptions.
5
5
  */
6
- import { appConfig, Router, useDatabase } from '@zintrust/core';
6
+ import { appConfig, ErrorFactory, Router, useDatabase, } from '@zintrust/core';
7
7
  import { TraceConfig } from '../config.js';
8
8
  import { TraceStorage } from '../storage/index.js';
9
+ import { assertTraceConnectionResolved, resolveDashboardTraceConnectionName, } from '../TraceConnection.js';
9
10
  import { addMonitoring, clearEntries, getBatch, getEntry, getMonitoring, getStats, listEntries, removeMonitoring, setHandlerStorage, } from './handlers.js';
10
11
  import { buildDashboardHtml } from './ui.js';
11
- const resolveDashboardConnectionName = (connectionName) => {
12
- const explicitConnection = connectionName?.trim();
13
- if (explicitConnection !== undefined && explicitConnection !== '') {
14
- return explicitConnection;
15
- }
16
- const runtimeConnection = globalThis.__zintrust_system_trace_connection_name__?.trim();
17
- if (runtimeConnection !== undefined && runtimeConnection !== '') {
18
- return runtimeConnection;
19
- }
20
- const configuredConnection = TraceConfig.merge().connection?.trim();
21
- return configuredConnection === undefined || configuredConnection === ''
22
- ? undefined
23
- : configuredConnection;
24
- };
25
12
  export const registerTraceRoutes = (router, storage, options = {}) => {
26
13
  setHandlerStorage(storage);
27
14
  const base = options.basePath ?? '/trace';
@@ -49,6 +36,15 @@ export const registerTraceRoutes = (router, storage, options = {}) => {
49
36
  });
50
37
  };
51
38
  export const registerTraceDashboard = (router, options = {}) => {
52
- const storage = TraceStorage.resolveStorage(useDatabase(undefined, resolveDashboardConnectionName(options.connectionName)));
39
+ const connectionName = resolveDashboardTraceConnectionName({ ErrorFactory }, {
40
+ explicitConnectionName: options.connectionName,
41
+ configuredConnectionName: TraceConfig.merge().connection,
42
+ });
43
+ const db = useDatabase(undefined, connectionName);
44
+ assertTraceConnectionResolved({ ErrorFactory }, db, {
45
+ connectionName,
46
+ envKey: 'TRACE_DB_CONNECTION',
47
+ });
48
+ const storage = TraceStorage.resolveStorage(db);
53
49
  registerTraceRoutes(router, storage, options);
54
50
  };
@@ -72,7 +72,7 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
72
72
  .section-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:22px 24px 16px}.section-head h3{margin:0;font-size:1.04rem}.section-head p{margin:6px 0 0;color:var(--muted);font-size:.92rem}.toolbar{display:flex;flex-wrap:wrap;gap:10px;padding:0 24px 18px}.control,.toolbar input,.toolbar select{height:44px;border-radius:13px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);padding:0 14px;min-width:0}.toolbar input,.toolbar select{flex:1 1 180px}.toolbar input::placeholder{color:var(--muted)}.btn{height:44px;border:none;border-radius:13px;padding:0 16px;cursor:pointer;font-weight:800}.btn-primary{background:linear-gradient(135deg,var(--accent-strong),var(--accent));color:#fff}.btn-danger{background:rgba(239,68,68,.12);color:var(--danger);border:1px solid rgba(239,68,68,.18)}.btn-ghost{background:var(--surface-soft);color:var(--text);border:1px solid var(--line)}
73
73
  .table-wrap{overflow:auto;padding:0 12px 12px}.table-wrap table{width:100%;border-collapse:separate;border-spacing:0;min-width:880px}th{padding:14px;color:var(--muted);font-size:.74rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;text-align:left;border-bottom:1px solid var(--line)}td{padding:15px 14px;border-bottom:1px solid var(--line);vertical-align:top}.row-button{cursor:pointer}.row-button:hover td{background:rgba(56,189,248,.05)}.summary{font-size:.93rem;font-weight:700;line-height:1.4;color:var(--text)}.summary-sub{margin-top:6px;color:var(--muted);font-size:.82rem;line-height:1.4}.mono{font-family:var(--mono)}.empty{padding:44px 24px;color:var(--muted);line-height:1.65;text-align:center}.pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 24px 24px;color:var(--muted);flex-wrap:wrap}.pagination-controls{display:flex;gap:8px}.pagination button{height:40px;min-width:92px;padding:0 14px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer}.pagination button:disabled{opacity:.45;cursor:not-allowed}
74
74
  .activity-list{list-style:none;margin:0;padding:0 24px 24px}.activity-item{padding:14px 0;border-top:1px solid var(--line)}.activity-item:first-child{border-top:none}.activity-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.activity-time{color:var(--muted);font-size:.85rem}.activity-summary{margin-top:8px;color:var(--text);line-height:1.48}.back-link{display:inline-flex;align-items:center;gap:8px;margin:0 0 14px;color:var(--accent);font-weight:800;cursor:pointer}.detail-card{padding:24px}.detail-meta{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0 20px;color:var(--muted);font-size:.9rem;overflow-wrap:anywhere}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.detail-stack{display:grid;gap:16px;margin-top:18px}.detail-box{padding:16px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.detail-box h4{margin:0 0 10px;font-size:.92rem}.detail-box dl{margin:0;display:grid;gap:8px}.detail-box dt{font-size:.76rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.detail-box dd{margin:0;color:var(--text);line-height:1.45;overflow-wrap:anywhere}.trace-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:20px 0 16px}.trace-tab{border:none;border-radius:12px;padding:10px 12px;background:transparent;color:var(--muted);cursor:pointer;box-shadow:inset 0 0 0 1px var(--line);font-weight:800}.trace-tab.active{background:rgba(56,189,248,.12);color:var(--text);box-shadow:inset 0 0 0 1px rgba(56,189,248,.28)}.trace-panel{display:grid;gap:14px}.trace-item{padding:18px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.trace-item-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.trace-item-summary{margin-top:10px;display:grid;gap:10px}.trace-note{color:var(--muted);line-height:1.6}.trace-disclosure{padding:0;overflow:hidden}.trace-disclosure[open]{padding-bottom:18px}.trace-disclosure .trace-item-summary{margin-top:0}.trace-disclosure-body{display:grid;gap:12px;padding:0 18px}.trace-summary{list-style:none;cursor:pointer;padding:18px}.trace-summary::-webkit-details-marker{display:none}.trace-summary-main{display:grid;gap:10px;min-width:0;flex:1}.trace-summary-copy{display:grid;gap:6px;min-width:0}.trace-summary-copy .summary,.trace-summary-copy .summary-sub{display:block;overflow-wrap:anywhere}.trace-disclosure-body .summary-sub{overflow-wrap:anywhere}
75
- .tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;background:rgba(56,189,248,.12);color:#bae6fd;font-size:.78rem;font-weight:800;margin:0 6px 6px 0;border:1px solid rgba(56,189,248,.18);text-decoration:none}button.tag{cursor:pointer}html[data-theme='light'] .tag{color:#075985}.tag.failed{background:rgba(239,68,68,.14);color:#fecaca;border-color:rgba(239,68,68,.2)}html[data-theme='light'] .tag.failed{color:#b91c1c}.tag.slow{background:rgba(245,158,11,.12);color:#fde68a;border-color:rgba(245,158,11,.18)}html[data-theme='light'] .tag.slow{color:#92400e}.type-pill{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:.74rem;font-weight:900;text-transform:uppercase;letter-spacing:.08em;border:1px solid transparent}.pill-request{background:rgba(56,189,248,.14);color:#93c5fd}.pill-request.method-get{background:rgba(34,197,94,.16);color:#bbf7d0}.pill-request.method-post{background:rgba(59,130,246,.16);color:#bfdbfe}.pill-request.method-other{background:rgba(245,158,11,.16);color:#fde68a}.pill-query{background:rgba(34,197,94,.12);color:#86efac}.pill-exception{background:rgba(239,68,68,.14);color:#fecaca}.pill-log{background:rgba(168,85,247,.14);color:#ddd6fe}.pill-job,.pill-batch{background:rgba(245,158,11,.14);color:#fde68a}.pill-cache{background:rgba(20,184,166,.12);color:#99f6e4}.pill-schedule,.pill-command{background:rgba(14,165,233,.14);color:#bae6fd}.pill-mail,.pill-notification{background:rgba(236,72,153,.14);color:#fbcfe8}.pill-auth{background:rgba(148,163,184,.16);color:#e2e8f0}.pill-event,.pill-model{background:rgba(74,222,128,.14);color:#bbf7d0}.pill-redis{background:rgba(239,68,68,.12);color:#fecaca}.pill-gate{background:rgba(99,102,241,.14);color:#c7d2fe}.pill-middleware{background:rgba(45,212,191,.12);color:#ccfbf1}.pill-dump,.pill-view{background:rgba(148,163,184,.14);color:#e2e8f0}.pill-client-request{background:rgba(59,130,246,.14);color:#bfdbfe}html[data-theme='light'] .pill-request{color:#1d4ed8}html[data-theme='light'] .pill-request.method-get{color:#166534}html[data-theme='light'] .pill-request.method-post{color:#1d4ed8}html[data-theme='light'] .pill-request.method-other{color:#92400e}html[data-theme='light'] .pill-query{color:#166534}html[data-theme='light'] .pill-exception{color:#b91c1c}html[data-theme='light'] .pill-log{color:#6d28d9}html[data-theme='light'] .pill-job,html[data-theme='light'] .pill-batch{color:#92400e}html[data-theme='light'] .pill-cache{color:#115e59}html[data-theme='light'] .pill-schedule,html[data-theme='light'] .pill-command{color:#0c4a6e}html[data-theme='light'] .pill-mail,html[data-theme='light'] .pill-notification{color:#9d174d}html[data-theme='light'] .pill-auth,html[data-theme='light'] .pill-dump,html[data-theme='light'] .pill-view{color:#334155}html[data-theme='light'] .pill-event,html[data-theme='light'] .pill-model{color:#166534}html[data-theme='light'] .pill-redis{color:#991b1b}html[data-theme='light'] .pill-gate{color:#3730a3}html[data-theme='light'] .pill-middleware{color:#155e75}html[data-theme='light'] .pill-client-request{color:#1d4ed8}
75
+ .tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;background:rgba(56,189,248,.12);color:#bae6fd;font-size:.78rem;font-weight:800;margin:0 6px 6px 0;border:1px solid rgba(56,189,248,.18);text-decoration:none}button.tag{cursor:pointer}html[data-theme='light'] .tag{color:#075985}.tag.failed{background:rgba(239,68,68,.14);color:#fecaca;border-color:rgba(239,68,68,.2)}html[data-theme='light'] .tag.failed{color:#b91c1c}.tag.slow{background:rgba(245,158,11,.12);color:#fde68a;border-color:rgba(245,158,11,.18)}html[data-theme='light'] .tag.slow{color:#92400e}.type-pill,.status-pill{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:.74rem;font-weight:900;text-transform:uppercase;letter-spacing:.08em;border:1px solid transparent}.pill-request{background:rgba(56,189,248,.14);color:#93c5fd}.pill-request.method-get{background:rgba(34,197,94,.16);color:#bbf7d0}.pill-request.method-post{background:rgba(59,130,246,.16);color:#bfdbfe}.pill-request.method-other{background:rgba(245,158,11,.16);color:#fde68a}.pill-query{background:rgba(34,197,94,.12);color:#86efac}.pill-exception{background:rgba(239,68,68,.14);color:#fecaca}.pill-log{background:rgba(168,85,247,.14);color:#ddd6fe}.pill-job,.pill-batch{background:rgba(245,158,11,.14);color:#fde68a}.pill-cache{background:rgba(20,184,166,.12);color:#99f6e4}.pill-schedule,.pill-command{background:rgba(14,165,233,.14);color:#bae6fd}.pill-mail,.pill-notification{background:rgba(236,72,153,.14);color:#fbcfe8}.pill-auth{background:rgba(148,163,184,.16);color:#e2e8f0}.pill-event,.pill-model{background:rgba(74,222,128,.14);color:#bbf7d0}.pill-redis{background:rgba(239,68,68,.12);color:#fecaca}.pill-gate{background:rgba(99,102,241,.14);color:#c7d2fe}.pill-middleware{background:rgba(45,212,191,.12);color:#ccfbf1}.pill-dump,.pill-view{background:rgba(148,163,184,.14);color:#e2e8f0}.pill-client-request{background:rgba(59,130,246,.14);color:#bfdbfe}.status-pill{white-space:nowrap}.status-pill.status-2xx{background:rgba(34,197,94,.16);color:#bbf7d0;border-color:rgba(34,197,94,.24)}.status-pill.status-4xx{background:rgba(245,158,11,.16);color:#fde68a;border-color:rgba(245,158,11,.24)}.status-pill.status-5xx{background:rgba(239,68,68,.16);color:#fecaca;border-color:rgba(239,68,68,.24)}.status-pill.status-other{background:rgba(148,163,184,.14);color:#e2e8f0;border-color:rgba(148,163,184,.2)}html[data-theme='light'] .pill-request{color:#1d4ed8}html[data-theme='light'] .pill-request.method-get{color:#166534}html[data-theme='light'] .pill-request.method-post{color:#1d4ed8}html[data-theme='light'] .pill-request.method-other{color:#92400e}html[data-theme='light'] .pill-query{color:#166534}html[data-theme='light'] .pill-exception{color:#b91c1c}html[data-theme='light'] .pill-log{color:#6d28d9}html[data-theme='light'] .pill-job,html[data-theme='light'] .pill-batch{color:#92400e}html[data-theme='light'] .pill-cache{color:#115e59}html[data-theme='light'] .pill-schedule,html[data-theme='light'] .pill-command{color:#0c4a6e}html[data-theme='light'] .pill-mail,html[data-theme='light'] .pill-notification{color:#9d174d}html[data-theme='light'] .pill-auth,html[data-theme='light'] .pill-dump,html[data-theme='light'] .pill-view{color:#334155}html[data-theme='light'] .pill-event,html[data-theme='light'] .pill-model{color:#166534}html[data-theme='light'] .pill-redis{color:#991b1b}html[data-theme='light'] .pill-gate{color:#3730a3}html[data-theme='light'] .pill-middleware{color:#155e75}html[data-theme='light'] .pill-client-request{color:#1d4ed8}html[data-theme='light'] .status-pill.status-2xx{color:#166534}html[data-theme='light'] .status-pill.status-4xx{color:#92400e}html[data-theme='light'] .status-pill.status-5xx{color:#b91c1c}html[data-theme='light'] .status-pill.status-other{color:#334155}
76
76
  .monitoring-wrap{padding:0 24px 24px}.tag-list{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:18px}.tag-item{display:inline-flex;align-items:center;gap:10px;padding:10px 14px;border-radius:999px;border:1px solid var(--line);background:var(--surface-strong)}.tag-remove{border:none;background:rgba(239,68,68,.14);color:var(--danger);border-radius:999px;width:24px;height:24px;cursor:pointer;font-size:1rem;line-height:1}.helper-text{color:var(--muted);line-height:1.6}
77
77
  .duration-chip{display:inline-flex;align-items:center;padding:5px 9px;border-radius:999px;border:1px solid transparent;font-size:.8rem;font-weight:700;color:var(--text);white-space:nowrap}.duration-chip.vfast{background:rgba(34,197,94,.14);border-color:rgba(34,197,94,.28);color:#bbf7d0}.duration-chip.fast{background:rgba(56,189,248,.12);border-color:rgba(56,189,248,.24);color:#bae6fd}.duration-chip.slow{background:rgba(245,158,11,.12);border-color:rgba(245,158,11,.22);color:#fde68a}.duration-chip.vslow{background:rgba(239,68,68,.14);border-color:rgba(239,68,68,.24);color:#fecaca}html[data-theme='light'] .duration-chip.vfast{color:#166534}html[data-theme='light'] .duration-chip.fast{color:#1d4ed8}html[data-theme='light'] .duration-chip.slow{color:#92400e}html[data-theme='light'] .duration-chip.vslow{color:#b91c1c}
78
78
  .code-card{border-radius:16px;border:1px solid var(--code-border);background:var(--surface-soft);overflow:hidden}.code-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--line)}.code-label{font-size:.76rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:800}.copy-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:38px;height:38px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer;transition:border-color .16s ease,color .16s ease}.copy-button:hover{border-color:rgba(56,189,248,.35);color:var(--accent)}.copy-button[data-copied='true']{color:var(--success);border-color:rgba(34,197,94,.28)}.copy-button svg{width:16px;height:16px;display:block}.code-block{margin:0;padding:18px 20px;background:var(--code-bg);color:#dbeafe;border:0;overflow:auto;white-space:pre;line-height:1.72;font-family:var(--mono);font-size:.92rem}.code-block.wrap{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.code-block code{font-family:inherit}.html-preview-wrap{padding:14px;background:var(--surface-strong);border-top:1px solid var(--line)}.html-preview{display:block;width:100%;min-height:320px;border:1px solid var(--line);border-radius:14px;background:#fff}.inline-collapse{margin:0;border-top:1px solid var(--line);background:var(--surface-strong)}.inline-collapse summary{cursor:pointer;list-style:none;padding:14px 16px;font-size:.82rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.inline-collapse summary::-webkit-details-marker{display:none}.inline-collapse[open] summary{border-bottom:1px solid var(--line)}.inline-collapse .code-block{border-top:none}.tok-key{color:#93c5fd}.tok-string{color:#86efac}.tok-number{color:#f9a8d4}.tok-boolean{color:#facc15}.tok-null{color:#fb7185}.tok-punctuation{color:#94a3b8}.tok-sql-keyword{color:#f472b6;font-weight:700}.tok-sql-identifier{color:#93c5fd}.tok-sql-string{color:#86efac}.tok-sql-number{color:#facc15}.tok-sql-comment{color:#64748b;font-style:italic}html[data-theme='light'] .code-block{color:#0f172a}html[data-theme='light'] .tok-key{color:#1d4ed8}html[data-theme='light'] .tok-string{color:#15803d}html[data-theme='light'] .tok-number{color:#c026d3}html[data-theme='light'] .tok-boolean{color:#b45309}html[data-theme='light'] .tok-null{color:#dc2626}html[data-theme='light'] .tok-punctuation{color:#64748b}html[data-theme='light'] .tok-sql-keyword{color:#db2777}html[data-theme='light'] .tok-sql-identifier{color:#2563eb}html[data-theme='light'] .tok-sql-string{color:#15803d}html[data-theme='light'] .tok-sql-number{color:#b45309}html[data-theme='light'] .tok-sql-comment{color:#6b7280}
@@ -258,6 +258,21 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
258
258
  return 'type-pill pill-' + String(type || '').replace(/_/g, '-') + requestMethodClass(entry);
259
259
  };
260
260
 
261
+ const statusToneClass = (value) => {
262
+ const status = Number(value);
263
+ if (!Number.isFinite(status)) return 'status-other';
264
+ if (status >= 500 && status < 600) return 'status-5xx';
265
+ if (status >= 400 && status < 500) return 'status-4xx';
266
+ if (status >= 200 && status < 300) return 'status-2xx';
267
+ return 'status-other';
268
+ };
269
+
270
+ const statusBadgeHtml = (value) => {
271
+ const status = Number(value);
272
+ if (!Number.isFinite(status)) return '';
273
+ return '<span class="status-pill ' + statusToneClass(status) + '">' + escapeHtml(String(status)) + '</span>';
274
+ };
275
+
261
276
  const timeSince = (value) => {
262
277
  const createdAt = Number(value);
263
278
  if (!Number.isFinite(createdAt)) return 'Unknown';
@@ -518,6 +533,19 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
518
533
  };
519
534
 
520
535
  const entrySummaryHtml = (entry) => {
536
+ const content = entry && entry.content ? entry.content : {};
537
+ if (entry.type === 'request') {
538
+ return '<div class="summary">' + statusBadgeHtml(content.responseStatus) + ' <span class="mono">' + escapeHtml(content.uri || '') + '</span></div><div class="summary-sub">Incoming request</div>';
539
+ }
540
+ if (entry.type === 'client_request') {
541
+ const clientParts = [
542
+ content.method ? '<span class="mono">' + escapeHtml(content.method) + '</span>' : '',
543
+ content.url ? '<span class="mono">' + escapeHtml(content.url) + '</span>' : '',
544
+ statusBadgeHtml(content.responseStatus) || (content.error ? '<span class="status-pill status-5xx">Failed</span>' : '')
545
+ ].filter(Boolean).join(' ');
546
+ return '<div class="summary">' + clientParts + '</div><div class="summary-sub">Outbound HTTP call</div>';
547
+ }
548
+
521
549
  const summary = escapeHtml(entrySummaryText(entry) || 'No summary available');
522
550
  const secondary = [
523
551
  entry.type === 'request' ? 'Incoming request' : '',
@@ -613,7 +641,7 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
613
641
  renderMetricBox('Request', [
614
642
  { label: 'Method', value: escapeHtml(content.method || '') },
615
643
  { label: 'URL', value: '<span class="mono">' + escapeHtml(content.url || '') + '</span>' },
616
- { label: 'Status', value: escapeHtml(content.responseStatus || (content.error ? 'Failed' : 'Pending')) },
644
+ { label: 'Status', value: statusBadgeHtml(content.responseStatus) || escapeHtml(content.error ? 'Failed' : 'Pending') },
617
645
  { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }
618
646
  ]),
619
647
  renderMetricBox('Runtime', [
@@ -797,7 +825,7 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
797
825
  ].join(''),
798
826
  payload: detailJson(content.payload || {}, 'Payload Json'),
799
827
  headers: '<div class="detail-stack">' + detailJson(content.headers || {}, 'Request Header Json') + detailJson(content.responseHeaders || {}, 'Response Header Json') + '</div>',
800
- response: '<div class="detail-stack"><div class="detail-grid">' + renderMetricBox('Status', [{ label: 'Response status', value: escapeHtml(content.responseStatus || '') }, { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }]) + '</div>' + (content.responseBody === undefined ? '<p class="trace-note">No response body was captured for this request.</p>' : detailJson(content.responseBody, 'Response Body Json')) + detailJson(content.responseHeaders || {}, 'Response Header Json') + '</div>',
828
+ response: '<div class="detail-stack"><div class="detail-grid">' + renderMetricBox('Status', [{ label: 'Response status', value: statusBadgeHtml(content.responseStatus) || escapeHtml(content.responseStatus || '') }, { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }]) + '</div>' + (content.responseBody === undefined ? '<p class="trace-note">No response body was captured for this request.</p>' : detailJson(content.responseBody, 'Response Body Json')) + detailJson(content.responseHeaders || {}, 'Response Header Json') + '</div>',
801
829
  queries: renderDetailBatchPanel('queries'),
802
830
  middleware: renderDetailBatchPanel('middleware'),
803
831
  models: renderDetailBatchPanel('models'),
@@ -812,7 +840,7 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
812
840
  '<span class="back-link" data-action="close-detail"><- Back to entries</span>',
813
841
  '<section class="panel detail-card">',
814
842
  '<div>' + (entry.type === 'request'
815
- ? '<span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span> <span class="' + typeClass(entry) + '">' + escapeHtml(content.responseStatus || '') + '</span> <span class="mono">' + escapeHtml(content.uri || '') + '</span> ' + tagsHtml(entry.tags)
843
+ ? '<span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span> ' + statusBadgeHtml(content.responseStatus) + ' <span class="mono">' + escapeHtml(content.uri || '') + '</span> ' + tagsHtml(entry.tags)
816
844
  : '<span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span> ' + tagsHtml(entry.tags)) + '</div>',
817
845
  '<div class="detail-meta"><span>UUID <span class="mono">' + escapeHtml(entry.uuid) + '</span></span><span>Batch <span class="mono">' + escapeHtml(entry.batchId || '-') + '</span></span><span>' + durationHtml(entry) + '</span><span>' + escapeHtml(new Date(Number(entry.createdAt)).toISOString()) + '</span></div>',
818
846
  '<div class="trace-tabs">',
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { TraceContentRedaction } from './storage/TraceContentRedaction';
13
13
  export { TraceContext } from './context';
14
14
  export { registerTraceDashboard, registerTraceRoutes } from './dashboard/routes';
15
15
  export type { TraceDashboardOptions, TraceDashboardRegistrationOptions } from './dashboard/routes';
16
+ export { registerTraceIngestGateway, TraceIngestGateway } from './ingest/TraceIngestGateway';
16
17
  export { AuthWatcher } from './watchers/AuthWatcher';
17
18
  export { BatchWatcher } from './watchers/BatchWatcher';
18
19
  export { CacheWatcher } from './watchers/CacheWatcher';
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export { TraceContext } from './context.js';
24
24
  // Dashboard
25
25
  // ---------------------------------------------------------------------------
26
26
  export { registerTraceDashboard, registerTraceRoutes } from './dashboard/routes.js';
27
+ export { registerTraceIngestGateway, TraceIngestGateway } from './ingest/TraceIngestGateway.js';
27
28
  // ---------------------------------------------------------------------------
28
29
  // Watchers (named re-exports for use with custom wiring)
29
30
  // ---------------------------------------------------------------------------
@@ -0,0 +1,22 @@
1
+ import { type IRouter } from '@zintrust/core';
2
+ import type { ITraceStorage } from '../types';
3
+ type TraceIngestGatewaySettings = {
4
+ basePath: string;
5
+ keyId: string;
6
+ secret: string;
7
+ signingWindowMs: number;
8
+ nonceTtlMs: number;
9
+ middleware: ReadonlyArray<string>;
10
+ storage: ITraceStorage;
11
+ };
12
+ type TraceIngestGatewayOverrides = Partial<Omit<TraceIngestGatewaySettings, 'storage'> & {
13
+ storage: ITraceStorage;
14
+ connectionName: string;
15
+ }>;
16
+ export declare const TraceIngestGateway: Readonly<{
17
+ create(overrides?: TraceIngestGatewayOverrides): {
18
+ registerRoutes: (router: IRouter) => void;
19
+ };
20
+ }>;
21
+ export declare const registerTraceIngestGateway: (router: IRouter, overrides?: TraceIngestGatewayOverrides) => void;
22
+ export default TraceIngestGateway;