@zintrust/trace 1.6.0 → 1.6.2

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.
Files changed (53) hide show
  1. package/dist/build-manifest.json +22 -10
  2. package/dist/register.js +14 -10
  3. package/dist/storage/DebuggerStorage.d.ts +13 -0
  4. package/dist/storage/DebuggerStorage.js +195 -0
  5. package/package.json +3 -4
  6. package/src/TraceConnection.ts +0 -182
  7. package/src/cli-register.ts +0 -63
  8. package/src/config.ts +0 -383
  9. package/src/context.ts +0 -101
  10. package/src/dashboard/handlers.ts +0 -353
  11. package/src/dashboard/routes.ts +0 -114
  12. package/src/dashboard/ui.ts +0 -1262
  13. package/src/dashboard/zintrust-debuger.svg +0 -30
  14. package/src/index.ts +0 -102
  15. package/src/ingest/TraceIngestGateway.ts +0 -414
  16. package/src/plugin.ts +0 -9
  17. package/src/register.ts +0 -659
  18. package/src/storage/ProxyTraceStorage.ts +0 -190
  19. package/src/storage/TraceContentBudget.ts +0 -491
  20. package/src/storage/TraceContentRedaction.ts +0 -44
  21. package/src/storage/TraceEntryFiltering.ts +0 -92
  22. package/src/storage/TraceServiceTag.ts +0 -56
  23. package/src/storage/TraceStorage.ts +0 -543
  24. package/src/storage/TraceWriteDiagnostics.ts +0 -289
  25. package/src/storage/index.ts +0 -4
  26. package/src/types.ts +0 -430
  27. package/src/ui.ts +0 -9
  28. package/src/utils/authTag.ts +0 -20
  29. package/src/utils/entryFilter.ts +0 -131
  30. package/src/utils/familyHash.ts +0 -8
  31. package/src/utils/redact.ts +0 -112
  32. package/src/utils/requestFilter.ts +0 -79
  33. package/src/utils/stackFrame.ts +0 -44
  34. package/src/watchers/AuthWatcher.ts +0 -53
  35. package/src/watchers/BatchWatcher.ts +0 -55
  36. package/src/watchers/CacheWatcher.ts +0 -72
  37. package/src/watchers/CommandWatcher.ts +0 -58
  38. package/src/watchers/DumpWatcher.ts +0 -45
  39. package/src/watchers/EventWatcher.ts +0 -46
  40. package/src/watchers/ExceptionWatcher.ts +0 -130
  41. package/src/watchers/GateWatcher.ts +0 -53
  42. package/src/watchers/HttpClientWatcher.ts +0 -219
  43. package/src/watchers/HttpWatcher.ts +0 -220
  44. package/src/watchers/JobWatcher.ts +0 -124
  45. package/src/watchers/LogWatcher.ts +0 -120
  46. package/src/watchers/MailWatcher.ts +0 -65
  47. package/src/watchers/MiddlewareWatcher.ts +0 -54
  48. package/src/watchers/ModelWatcher.ts +0 -60
  49. package/src/watchers/NotificationWatcher.ts +0 -60
  50. package/src/watchers/QueryWatcher.ts +0 -107
  51. package/src/watchers/RedisWatcher.ts +0 -42
  52. package/src/watchers/ScheduleWatcher.ts +0 -57
  53. package/src/watchers/ViewWatcher.ts +0 -40
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@zintrust/trace",
3
- "version": "1.6.0",
4
- "buildDate": "2026-04-29T16:58:34.543Z",
3
+ "version": "1.5.1",
4
+ "buildDate": "2026-04-28T16:08:58.961Z",
5
5
  "buildEnvironment": {
6
- "node": "v20.20.2",
7
- "platform": "linux",
8
- "arch": "x64"
6
+ "node": "v22.22.1",
7
+ "platform": "darwin",
8
+ "arch": "arm64"
9
9
  },
10
10
  "git": {
11
- "commit": "bcc45109",
12
- "branch": "master"
11
+ "commit": "6c6a3f59",
12
+ "branch": "release"
13
13
  },
14
14
  "package": {
15
15
  "engines": {
@@ -29,6 +29,10 @@
29
29
  "size": 4640,
30
30
  "sha256": "c51cc312046b6b2bbe1673f1ff9508425cc7140a1d2341907f67aa36069c09f9"
31
31
  },
32
+ "build-manifest.json": {
33
+ "size": 15912,
34
+ "sha256": "b7ce0234e13707d03336dbad905b5c1403a910880469504ec6cde78ad67fe9a9"
35
+ },
32
36
  "cli-register.d.ts": {
33
37
  "size": 255,
34
38
  "sha256": "da8d689fe5ef32e97e755f28017e4d3cb1aa63489073a71907ea41ad5761ede9"
@@ -83,7 +87,7 @@
83
87
  },
84
88
  "index.js": {
85
89
  "size": 3421,
86
- "sha256": "34223487b1e087a904da56e1e76a4fabcc38c34538330feaa355636311b59fce"
90
+ "sha256": "6cab0d02424c088c23dce13c349f177993fd55f21864eea1842c2276a81ad526"
87
91
  },
88
92
  "ingest/TraceIngestGateway.d.ts": {
89
93
  "size": 786,
@@ -146,8 +150,16 @@
146
150
  "sha256": "535869aa1bfcdf0ec26fad27d7f18bb8822bc586c38fcc5ffdde5274ecbea17e"
147
151
  },
148
152
  "register.js": {
149
- "size": 19592,
150
- "sha256": "5f0510477750c2b7e40aaa869ae2e655a41ca643d5c3e1cf291cf1aed5338f79"
153
+ "size": 20927,
154
+ "sha256": "026fd79a98596d2be108b350668f4345b11020953f24369866c0aad42cd7ba0f"
155
+ },
156
+ "storage/DebuggerStorage.d.ts": {
157
+ "size": 517,
158
+ "sha256": "c9c215aaa414f7b0c1fec6c82b054fc52bdf97af58f96f35c7f96672fb859c31"
159
+ },
160
+ "storage/DebuggerStorage.js": {
161
+ "size": 7442,
162
+ "sha256": "5ecce0fcfcf695df587a7b90a7a5c7efd2e64ad13c9f2d104b392f89f34f0dc4"
151
163
  },
152
164
  "storage/ProxyTraceStorage.d.ts": {
153
165
  "size": 339,
package/dist/register.js CHANGED
@@ -291,26 +291,30 @@ const createTraceWatcherArgs = async (core, Env, config) => {
291
291
  globalTraceRegisterState.__zintrust_system_trace_connection_name__ = resolvedConnectionName;
292
292
  globalTraceRegisterState.__zintrust_system_trace_observe_connection_name__ =
293
293
  resolvedObservedConnectionName;
294
- const storageDb = core.useDatabase?.(undefined, resolvedConnectionName);
295
294
  const observedDb = core.useDatabase?.(undefined, resolvedObservedConnectionName);
296
- assertTraceConnectionResolved(core, storageDb, {
297
- connectionName: resolvedConnectionName,
298
- envKey: 'TRACE_DB_CONNECTION',
299
- });
300
295
  assertTraceConnectionResolved(core, observedDb, {
301
296
  connectionName: resolvedObservedConnectionName,
302
297
  envKey: 'TRACE_QUERY_CONNECTION',
303
298
  });
304
- await assertTraceStorageReady(core, storageDb, resolvedConnectionName);
305
- const resolvedStorage = config.proxy.enabled
306
- ? ProxyTraceStorage.create({
299
+ let resolvedStorage;
300
+ if (config.proxy.enabled) {
301
+ resolvedStorage = ProxyTraceStorage.create({
307
302
  baseUrl: config.proxy.url ?? '',
308
303
  path: config.proxy.path,
309
304
  keyId: config.proxy.keyId ?? '',
310
305
  secret: config.proxy.secret ?? '',
311
306
  timeoutMs: config.proxy.timeoutMs,
312
- })
313
- : TraceStorage.resolveStorage(storageDb);
307
+ });
308
+ }
309
+ else {
310
+ const storageDb = core.useDatabase?.(undefined, resolvedConnectionName);
311
+ assertTraceConnectionResolved(core, storageDb, {
312
+ connectionName: resolvedConnectionName,
313
+ envKey: 'TRACE_DB_CONNECTION',
314
+ });
315
+ await assertTraceStorageReady(core, storageDb, resolvedConnectionName);
316
+ resolvedStorage = TraceStorage.resolveStorage(storageDb);
317
+ }
314
318
  const storage = TraceWriteDiagnostics.wrapStorage(TraceContentBudget.wrapStorage(TraceContentRedaction.wrapStorage(TraceEntryFiltering.wrapStorage(TraceServiceTag.wrapStorage(resolvedStorage, config), config), config.redaction), config), {
315
319
  connectionName: resolvedConnectionName,
316
320
  logger: core.Logger,
@@ -0,0 +1,13 @@
1
+ /**
2
+ * TraceStorage — sealed namespace wrapping the D1/SQLite driver.
3
+ * Resolves the correct IDatabase from the app config, then delegates all
4
+ * read/write operations to the trace storage facade.
5
+ */
6
+ import type { IDatabase } from '@zintrust/core';
7
+ import type { ITraceStorage } from '../types';
8
+ export declare const TraceStorage: Readonly<{
9
+ resolveStorage: (db: IDatabase) => ITraceStorage;
10
+ reset: () => void;
11
+ familyHash: (input: string) => string;
12
+ }>;
13
+ export { type ITraceStorage } from '../types';
@@ -0,0 +1,195 @@
1
+ import { familyHash } from '../utils/familyHash.js';
2
+ const TABLE_ENTRIES = 'zin_trace_entries';
3
+ const TABLE_TAGS = 'zin_trace_entries_tags';
4
+ const TABLE_MONITORING = 'zin_trace_monitoring';
5
+ const generateUuid = () => crypto.randomUUID();
6
+ const rowToEntry = (row, tags) => ({
7
+ uuid: row.uuid,
8
+ batchId: row.batch_id,
9
+ familyHash: row.family_hash ?? undefined,
10
+ type: row.type,
11
+ content: JSON.parse(row.content),
12
+ tags,
13
+ isLatest: Boolean(row.is_latest),
14
+ createdAt: row.created_at,
15
+ });
16
+ const insertTags = async (db, uuid, tags) => {
17
+ if (tags.length === 0)
18
+ return;
19
+ await Promise.all(tags.map(async (tag) => {
20
+ await db.execute(`INSERT OR IGNORE INTO ${TABLE_TAGS} (entry_uuid, tag) VALUES (?, ?)`, [
21
+ uuid,
22
+ tag,
23
+ ]);
24
+ }));
25
+ };
26
+ const buildEntryFilters = (opts) => {
27
+ const conditions = [];
28
+ const params = [];
29
+ if (opts.type) {
30
+ conditions.push('e.type = ?');
31
+ params.push(opts.type);
32
+ }
33
+ if (opts.batchId) {
34
+ conditions.push('e.batch_id = ?');
35
+ params.push(opts.batchId);
36
+ }
37
+ if (opts.from) {
38
+ conditions.push('e.created_at >= ?');
39
+ params.push(opts.from);
40
+ }
41
+ if (opts.to) {
42
+ conditions.push('e.created_at <= ?');
43
+ params.push(opts.to);
44
+ }
45
+ let joinClause = '';
46
+ if (opts.tag) {
47
+ joinClause = `INNER JOIN ${TABLE_TAGS} t ON t.entry_uuid = e.uuid AND t.tag = ?`;
48
+ params.unshift(opts.tag);
49
+ }
50
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
51
+ const countParams = opts.tag ? [opts.tag, ...params.slice(1)] : [...params];
52
+ return { joinClause, whereClause, params, countParams };
53
+ };
54
+ const loadTagsByUuid = async (db, uuids) => {
55
+ const tagsByUuid = new Map();
56
+ if (uuids.length === 0)
57
+ return tagsByUuid;
58
+ const tagRows = (await db.query(`SELECT entry_uuid, tag FROM ${TABLE_TAGS} WHERE entry_uuid IN (${uuids.map(() => '?').join(',')})`, uuids));
59
+ for (const tagRow of tagRows) {
60
+ const tags = tagsByUuid.get(tagRow.entry_uuid) ?? [];
61
+ tags.push(tagRow.tag);
62
+ tagsByUuid.set(tagRow.entry_uuid, tags);
63
+ }
64
+ return tagsByUuid;
65
+ };
66
+ // The storage facade intentionally groups related DB operations in one factory.
67
+ // eslint-disable-next-line max-lines-per-function
68
+ const createStorage = (db) => {
69
+ const writeEntry = async (entry) => {
70
+ const uuid = entry.uuid || generateUuid();
71
+ await db.execute(`INSERT INTO ${TABLE_ENTRIES} (uuid, batch_id, family_hash, type, content, is_latest, created_at)
72
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
73
+ uuid,
74
+ entry.batchId,
75
+ entry.familyHash ?? null,
76
+ entry.type,
77
+ JSON.stringify(entry.content),
78
+ entry.isLatest ? 1 : 0,
79
+ entry.createdAt,
80
+ ]);
81
+ await insertTags(db, uuid, entry.tags);
82
+ };
83
+ const updateEntry = async (uuid, patch) => {
84
+ const sets = [];
85
+ const params = [];
86
+ if (patch.content !== undefined) {
87
+ sets.push('content = ?');
88
+ params.push(JSON.stringify(patch.content));
89
+ }
90
+ if (patch.isLatest !== undefined) {
91
+ sets.push('is_latest = ?');
92
+ params.push(patch.isLatest ? 1 : 0);
93
+ }
94
+ if (sets.length === 0)
95
+ return;
96
+ params.push(uuid);
97
+ await db.execute(`UPDATE ${TABLE_ENTRIES} SET ${sets.join(', ')} WHERE uuid = ?`, params);
98
+ };
99
+ const markFamilyStale = async (hash, exceptUuid) => {
100
+ await db.execute(`UPDATE ${TABLE_ENTRIES} SET is_latest = 0
101
+ WHERE family_hash = ? AND uuid != ? AND is_latest = 1`, [hash, exceptUuid]);
102
+ };
103
+ const queryEntries = async (opts) => {
104
+ const page = opts.page ?? 1;
105
+ const perPage = opts.perPage ?? 50;
106
+ const offset = (page - 1) * perPage;
107
+ const { joinClause, whereClause, params, countParams } = buildEntryFilters(opts);
108
+ const countResult = (await db.queryOne(`SELECT COUNT(*) as cnt FROM ${TABLE_ENTRIES} e ${joinClause} ${whereClause}`, countParams));
109
+ const total = countResult?.cnt ?? 0;
110
+ const rows = (await db.query(`SELECT e.id, e.uuid, e.batch_id, e.family_hash, e.type, e.content, e.is_latest, e.created_at
111
+ FROM ${TABLE_ENTRIES} e ${joinClause} ${whereClause}
112
+ ORDER BY e.created_at DESC, e.id DESC
113
+ LIMIT ? OFFSET ?`, [...params, perPage, offset]));
114
+ const tagsByUuid = await loadTagsByUuid(db, rows.map((row) => row.uuid));
115
+ return {
116
+ data: rows.map((row) => rowToEntry(row, tagsByUuid.get(row.uuid) ?? [])),
117
+ total,
118
+ };
119
+ };
120
+ const getEntry = async (uuid) => {
121
+ const row = (await db.queryOne(`SELECT id, uuid, batch_id, family_hash, type, content, is_latest, created_at
122
+ FROM ${TABLE_ENTRIES}
123
+ WHERE uuid = ?`, [uuid]));
124
+ if (!row)
125
+ return null;
126
+ const tags = (await db.query(`SELECT tag FROM ${TABLE_TAGS} WHERE entry_uuid = ?`, [
127
+ uuid,
128
+ ]));
129
+ return rowToEntry(row, tags.map((tag) => tag.tag));
130
+ };
131
+ const getBatch = async (batchId) => {
132
+ const rows = (await db.query(`SELECT id, uuid, batch_id, family_hash, type, content, is_latest, created_at
133
+ FROM ${TABLE_ENTRIES}
134
+ WHERE batch_id = ?
135
+ ORDER BY created_at ASC, id ASC`, [batchId]));
136
+ if (rows.length === 0)
137
+ return [];
138
+ const tagsByUuid = await loadTagsByUuid(db, rows.map((row) => row.uuid));
139
+ return rows.map((row) => rowToEntry(row, tagsByUuid.get(row.uuid) ?? []));
140
+ };
141
+ const prune = async (olderThanMs, keepExceptions = false) => {
142
+ const countResult = (await db.queryOne(`SELECT COUNT(*) as cnt FROM ${TABLE_ENTRIES}
143
+ WHERE created_at < ?
144
+ ${keepExceptions ? "AND type != 'exception'" : ''}`, [olderThanMs]));
145
+ const deleted = countResult?.cnt ?? 0;
146
+ if (deleted === 0)
147
+ return 0;
148
+ await db.execute(`DELETE FROM ${TABLE_ENTRIES}
149
+ WHERE created_at < ?
150
+ ${keepExceptions ? "AND type != 'exception'" : ''}`, [olderThanMs]);
151
+ return deleted;
152
+ };
153
+ const clear = async () => {
154
+ await db.execute(`DELETE FROM ${TABLE_ENTRIES}`, []);
155
+ };
156
+ const getMonitoring = async () => {
157
+ const rows = (await db.query(`SELECT tag FROM ${TABLE_MONITORING}`, []));
158
+ return rows.map((row) => row.tag);
159
+ };
160
+ const addMonitoring = async (tag) => {
161
+ await db.execute(`INSERT OR IGNORE INTO ${TABLE_MONITORING} (tag) VALUES (?)`, [tag]);
162
+ };
163
+ const removeMonitoring = async (tag) => {
164
+ await db.execute(`DELETE FROM ${TABLE_MONITORING} WHERE tag = ?`, [tag]);
165
+ };
166
+ const stats = async () => {
167
+ const rows = (await db.query(`SELECT type, COUNT(*) as cnt FROM ${TABLE_ENTRIES} GROUP BY type`, []));
168
+ const output = {};
169
+ for (const row of rows) {
170
+ output[row.type] = row.cnt;
171
+ }
172
+ return output;
173
+ };
174
+ return {
175
+ writeEntry,
176
+ updateEntry,
177
+ markFamilyStale,
178
+ queryEntries,
179
+ getEntry,
180
+ getBatch,
181
+ prune,
182
+ clear,
183
+ getMonitoring,
184
+ addMonitoring,
185
+ removeMonitoring,
186
+ stats,
187
+ };
188
+ };
189
+ const resolveStorage = (db) => {
190
+ return createStorage(db);
191
+ };
192
+ const reset = () => {
193
+ return;
194
+ };
195
+ export const TraceStorage = Object.freeze({ resolveStorage, reset, familyHash });
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@zintrust/trace",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Trace assistant for ZinTrust: logs requests, queries, exceptions, jobs, and more.",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "files": [
10
- "dist",
11
- "src"
10
+ "dist"
12
11
  ],
13
12
  "exports": {
14
13
  ".": {
@@ -56,4 +55,4 @@
56
55
  "build": "tsc -p tsconfig.json && tsc -p tsconfig.migrations.json && node ../../scripts/fix-dist-esm-imports.mjs dist",
57
56
  "prepublishOnly": "npm run build"
58
57
  }
59
- }
58
+ }
@@ -1,182 +0,0 @@
1
- import type { IDatabase } from '@zintrust/core';
2
-
3
- type TraceErrorFactory = {
4
- createConfigError?(message: string, details?: unknown): Error;
5
- };
6
-
7
- type TraceErrorApi = {
8
- ErrorFactory?: TraceErrorFactory;
9
- };
10
-
11
- type TraceEnvApi = {
12
- get(key: string, fallback: string): string;
13
- };
14
-
15
- type GlobalTraceConnectionState = {
16
- __zintrust_system_trace_connection_name__?: string;
17
- __zintrust_system_trace_plugin_requested__?: boolean;
18
- };
19
-
20
- export const TRACE_REQUIRED_TABLES = [
21
- 'zin_trace_entries',
22
- 'zin_trace_entries_tags',
23
- 'zin_trace_monitoring',
24
- ] as const;
25
-
26
- const createFallbackTraceConfigError = (message: string, details?: unknown): Error => {
27
- const error = new globalThis.Error(message) as Error & {
28
- code?: string;
29
- details?: unknown;
30
- name?: string;
31
- statusCode?: number;
32
- };
33
- error.name = 'ConfigError';
34
- error.code = 'CONFIG_ERROR';
35
- error.statusCode = 500;
36
- error.details = details;
37
- return error;
38
- };
39
-
40
- export const createTraceConfigError = (
41
- coreApi: TraceErrorApi,
42
- message: string,
43
- details?: unknown
44
- ): Error => {
45
- if (coreApi.ErrorFactory?.createConfigError !== undefined) {
46
- return coreApi.ErrorFactory.createConfigError(message, details);
47
- }
48
-
49
- return createFallbackTraceConfigError(message, details);
50
- };
51
-
52
- export const getRuntimeTraceConnectionName = (): string | undefined => {
53
- const runtimeConnection = (
54
- globalThis as GlobalTraceConnectionState
55
- ).__zintrust_system_trace_connection_name__?.trim();
56
-
57
- return runtimeConnection === undefined || runtimeConnection === ''
58
- ? undefined
59
- : runtimeConnection;
60
- };
61
-
62
- export const resolveDashboardTraceConnectionName = (
63
- coreApi: TraceErrorApi,
64
- input: {
65
- explicitConnectionName?: string;
66
- configuredConnectionName?: string;
67
- }
68
- ): string => {
69
- const explicitConnection = input.explicitConnectionName?.trim();
70
- if (explicitConnection !== undefined && explicitConnection !== '') {
71
- return explicitConnection;
72
- }
73
-
74
- const runtimeConnection = getRuntimeTraceConnectionName();
75
- if (runtimeConnection !== undefined) {
76
- return runtimeConnection;
77
- }
78
-
79
- const configuredConnection = input.configuredConnectionName?.trim();
80
- if (configuredConnection !== undefined && configuredConnection !== '') {
81
- return configuredConnection;
82
- }
83
-
84
- throw createTraceConfigError(coreApi, 'Trace dashboard connection is not configured.', {
85
- envKey: 'TRACE_DB_CONNECTION',
86
- hint: 'Import @zintrust/trace/register before mounting the dashboard, pass connectionName explicitly, or set TRACE_DB_CONNECTION to the trace storage connection.',
87
- });
88
- };
89
-
90
- export const resolveTraceConnectionName = (
91
- env: Pick<TraceEnvApi, 'get'> | undefined,
92
- configuredConnection?: string
93
- ): string => {
94
- const resolveDefaultConnection = (): string => {
95
- const defaultConnection = env?.get('DB_CONNECTION', '').trim() ?? '';
96
- if (defaultConnection === '' || defaultConnection === 'default') return 'default';
97
- return defaultConnection;
98
- };
99
-
100
- const explicitConnection = configuredConnection?.trim();
101
- if (explicitConnection !== undefined && explicitConnection !== '') {
102
- return explicitConnection === 'default' ? resolveDefaultConnection() : explicitConnection;
103
- }
104
-
105
- return resolveDefaultConnection();
106
- };
107
-
108
- export const resolveObservedConnectionName = (
109
- env: Pick<TraceEnvApi, 'get'> | undefined,
110
- configuredObservedConnection: string | undefined,
111
- storageConnectionName: string
112
- ): string => {
113
- if (
114
- typeof configuredObservedConnection === 'string' &&
115
- configuredObservedConnection.trim() !== ''
116
- ) {
117
- return resolveTraceConnectionName(env, configuredObservedConnection);
118
- }
119
-
120
- const defaultConnectionName = resolveTraceConnectionName(env);
121
- if (storageConnectionName !== defaultConnectionName) {
122
- return defaultConnectionName;
123
- }
124
-
125
- return storageConnectionName;
126
- };
127
-
128
- export function assertTraceConnectionResolved(
129
- coreApi: TraceErrorApi,
130
- db: IDatabase | undefined,
131
- params: { connectionName: string; envKey: 'TRACE_DB_CONNECTION' | 'TRACE_QUERY_CONNECTION' }
132
- ): asserts db is IDatabase {
133
- if (db !== undefined) {
134
- return;
135
- }
136
-
137
- const pluginRequested =
138
- (globalThis as GlobalTraceConnectionState).__zintrust_system_trace_plugin_requested__ === true;
139
- let hint =
140
- 'Configure TRACE_QUERY_CONNECTION, or ensure DB_CONNECTION resolves to an existing database connection.';
141
-
142
- if (params.envKey === 'TRACE_DB_CONNECTION') {
143
- hint = pluginRequested
144
- ? 'Configure TRACE_DB_CONNECTION to an existing database connection before enabling TRACE_ENABLED.'
145
- : '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.';
146
- }
147
-
148
- throw createTraceConfigError(
149
- coreApi,
150
- `Trace connection "${params.connectionName}" could not be resolved.`,
151
- {
152
- connectionName: params.connectionName,
153
- envKey: params.envKey,
154
- hint,
155
- }
156
- );
157
- }
158
-
159
- export const assertTraceStorageReady = async (
160
- coreApi: TraceErrorApi,
161
- db: IDatabase,
162
- connectionName: string,
163
- operation = 'Trace storage connection'
164
- ): Promise<void> => {
165
- try {
166
- await Promise.all(
167
- TRACE_REQUIRED_TABLES.map(async (table) => {
168
- await db.queryOne(`SELECT 1 AS ok FROM ${table} LIMIT 1`, []);
169
- })
170
- );
171
- } catch (error) {
172
- throw createTraceConfigError(
173
- coreApi,
174
- `${operation} "${connectionName}" is not ready. Create the database if needed and run \`zin migrate:trace\` before enabling TRACE_ENABLED.`,
175
- {
176
- connectionName,
177
- error,
178
- requiredTables: [...TRACE_REQUIRED_TABLES],
179
- }
180
- );
181
- }
182
- };
@@ -1,63 +0,0 @@
1
- type Registry = {
2
- register: (id: string, provider: CliCommandProvider) => void;
3
- };
4
-
5
- type CliCommandProvider = {
6
- getCommand: () => unknown;
7
- name?: string;
8
- };
9
-
10
- type TraceCommandsModule = {
11
- TraceCommands: {
12
- createTracePruneProvider: () => CliCommandProvider;
13
- createTraceClearProvider: () => CliCommandProvider;
14
- createTraceStatusProvider: () => CliCommandProvider;
15
- createTraceMigrateProvider: () => CliCommandProvider;
16
- };
17
- };
18
-
19
- const commandModule = (await import('@zintrust/core/cli')) as unknown as TraceCommandsModule;
20
-
21
- const getTraceProviders = (): Array<[string, CliCommandProvider]> => {
22
- const { TraceCommands } = commandModule;
23
-
24
- return [
25
- ['trace:prune', TraceCommands.createTracePruneProvider()],
26
- ['trace:clear', TraceCommands.createTraceClearProvider()],
27
- ['trace:status', TraceCommands.createTraceStatusProvider()],
28
- ['migrate:trace', TraceCommands.createTraceMigrateProvider()],
29
- ];
30
- };
31
-
32
- export function registerTraceCliCommands(registry: Registry): void {
33
- for (const [id, provider] of getTraceProviders()) {
34
- registry.register(id, provider);
35
- }
36
- }
37
-
38
- type GlobalWithRegistry = {
39
- __zintrust_cli_command_registry__?: Map<string, CliCommandProvider>;
40
- };
41
-
42
- const globalWithRegistry = globalThis as unknown as GlobalWithRegistry;
43
- const globalRegistry =
44
- globalWithRegistry.__zintrust_cli_command_registry__ ??
45
- (globalWithRegistry.__zintrust_cli_command_registry__ = new Map<string, CliCommandProvider>());
46
-
47
- registerTraceCliCommands({
48
- register: (id, provider) => {
49
- globalRegistry.set(id, provider);
50
- },
51
- });
52
-
53
- try {
54
- const coreCli = (await import('@zintrust/core/cli')) as unknown as {
55
- OptionalCliCommandRegistry?: Registry;
56
- };
57
-
58
- if (coreCli.OptionalCliCommandRegistry !== undefined) {
59
- registerTraceCliCommands(coreCli.OptionalCliCommandRegistry);
60
- }
61
- } catch {
62
- // no-op
63
- }