@kb-labs/adapters-log-document 2.111.0 → 2.114.0

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/dist/index.d.ts CHANGED
@@ -42,11 +42,12 @@ declare class DocumentLogPersistence implements ILogPersistence {
42
42
  write(record: LogRecord): Promise<void>;
43
43
  writeBatch(records: LogRecord[]): Promise<void>;
44
44
  private flush;
45
+ private scheduleFlush;
45
46
  query(query: LogQuery, options?: {
46
47
  limit?: number;
47
48
  offset?: number;
48
- sortBy?: 'timestamp' | 'level';
49
- sortOrder?: 'asc' | 'desc';
49
+ sortBy?: "timestamp" | "level";
50
+ sortOrder?: "asc" | "desc";
50
51
  }): Promise<{
51
52
  logs: LogRecord[];
52
53
  total: number;
@@ -56,6 +57,7 @@ declare class DocumentLogPersistence implements ILogPersistence {
56
57
  search(searchText: string, options?: {
57
58
  limit?: number;
58
59
  offset?: number;
60
+ filters?: LogQuery;
59
61
  }): Promise<{
60
62
  logs: LogRecord[];
61
63
  total: number;
@@ -71,6 +73,7 @@ declare class DocumentLogPersistence implements ILogPersistence {
71
73
  }>;
72
74
  close(): Promise<void>;
73
75
  private runRetention;
76
+ private expire;
74
77
  }
75
78
  declare function createAdapter(config: LogPersistenceConfig, deps?: Record<string, unknown>): DocumentLogPersistence;
76
79
 
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { LOG_CONTEXT_FIELDS } from '@kb-labs/sdk/adapters';
2
+
3
+ // src/index.ts
4
+
1
5
  // src/manifest.ts
2
6
  var manifest = {
3
7
  manifestVersion: "1.0.0",
@@ -7,10 +11,18 @@ var manifest = {
7
11
  description: "ILogPersistence implementation storing log entries in any IDocumentDatabase. Driver-agnostic.",
8
12
  author: "KB Labs Team",
9
13
  license: "KBPL-1.1",
10
- type: "core",
14
+ type: "extension",
11
15
  implements: "ILogPersistence",
16
+ extends: {
17
+ adapter: "logger",
18
+ hook: "onLog",
19
+ method: "write",
20
+ priority: 0
21
+ },
12
22
  contexts: ["workspace", "process"],
13
- requires: { adapters: [{ id: "documentDatabase", alias: "documentDatabase" }] },
23
+ requires: {
24
+ adapters: [{ id: "documentDatabase", alias: "documentDatabase" }]
25
+ },
14
26
  capabilities: {
15
27
  batch: true,
16
28
  search: true,
@@ -48,17 +60,16 @@ var LEVEL_ORDER = {
48
60
  };
49
61
  var LEVELS_AT_OR_ABOVE = (min) => {
50
62
  const threshold = LEVEL_ORDER[min];
51
- return Object.keys(LEVEL_ORDER).filter((l) => LEVEL_ORDER[l] >= threshold);
63
+ return Object.keys(LEVEL_ORDER).filter(
64
+ (l) => LEVEL_ORDER[l] >= threshold
65
+ );
52
66
  };
53
67
  var DEFAULT_RETENTION = {
54
- maxAge: 7 * 24 * 60 * 60 * 1e3,
55
- // 7 days for warn/error/fatal
56
- maxAgeDebug: 60 * 60 * 1e3,
57
- // 1 hour for debug/trace
58
- maxAgeInfo: 24 * 60 * 60 * 1e3,
59
- // 24 hours for info
60
- maxSizeBytes: 500 * 1024 * 1024,
61
- // 500 MB
68
+ maxAgeFatal: 30 * 24 * 60 * 60 * 1e3,
69
+ maxAgeError: 14 * 24 * 60 * 60 * 1e3,
70
+ maxAgeWarn: 7 * 24 * 60 * 60 * 1e3,
71
+ maxAgeDebug: 30 * 60 * 1e3,
72
+ maxAgeInfo: 30 * 60 * 1e3,
62
73
  cleanupIntervalMs: 5 * 60 * 1e3
63
74
  // 5 minutes
64
75
  };
@@ -85,11 +96,12 @@ var DocumentLogPersistence = class {
85
96
  { path: "logId", unique: true },
86
97
  { path: "timestamp" },
87
98
  { path: "level" },
88
- { path: "source" }
99
+ { path: "source" },
100
+ ...LOG_CONTEXT_FIELDS.map((field) => ({ path: `fields.${field}` }))
89
101
  ]
90
102
  });
91
103
  this.flushTimer = setInterval(() => {
92
- void this.flush();
104
+ this.scheduleFlush();
93
105
  }, this.flushIntervalMs).unref();
94
106
  this.retentionTimer = setInterval(() => {
95
107
  void this.runRetention();
@@ -100,23 +112,23 @@ var DocumentLogPersistence = class {
100
112
  // ──────────────────────────────────────────────────────────────────────────
101
113
  async write(record) {
102
114
  if (this.closed) {
103
- throw new Error("DocumentLogPersistence is closed");
115
+ return;
104
116
  }
105
117
  this.buffer.push(record);
106
118
  if (this.buffer.length >= this.batchSize) {
107
- void this.flush();
119
+ this.scheduleFlush();
108
120
  }
109
121
  }
110
122
  async writeBatch(records) {
111
123
  if (this.closed) {
112
- throw new Error("DocumentLogPersistence is closed");
124
+ return;
113
125
  }
114
126
  if (records.length === 0) {
115
127
  return;
116
128
  }
117
129
  this.buffer.push(...records);
118
130
  if (this.buffer.length >= this.batchSize) {
119
- void this.flush();
131
+ this.scheduleFlush();
120
132
  }
121
133
  }
122
134
  async flush() {
@@ -131,14 +143,17 @@ var DocumentLogPersistence = class {
131
143
  this.flushing = (async () => {
132
144
  try {
133
145
  await this.initialised;
134
- await this.docs.insertMany(this.collection, batch.map((r) => ({
135
- logId: r.id,
136
- timestamp: r.timestamp,
137
- level: r.level,
138
- message: r.message,
139
- fields: r.fields,
140
- source: r.source
141
- })));
146
+ await this.docs.insertMany(
147
+ this.collection,
148
+ batch.map((r) => ({
149
+ logId: r.id,
150
+ timestamp: r.timestamp,
151
+ level: r.level,
152
+ message: r.message,
153
+ fields: r.fields,
154
+ source: r.source
155
+ }))
156
+ );
142
157
  } catch (err) {
143
158
  this.buffer.unshift(...batch);
144
159
  throw err;
@@ -148,6 +163,10 @@ var DocumentLogPersistence = class {
148
163
  })();
149
164
  await this.flushing;
150
165
  }
166
+ scheduleFlush() {
167
+ void this.flush().catch(() => {
168
+ });
169
+ }
151
170
  // ──────────────────────────────────────────────────────────────────────────
152
171
  // Read path
153
172
  // ──────────────────────────────────────────────────────────────────────────
@@ -174,13 +193,21 @@ var DocumentLogPersistence = class {
174
193
  }
175
194
  async getById(id) {
176
195
  await this.initialised;
177
- const docs = await this.docs.find(this.collection, { logId: { $eq: id } }, { limit: 1 });
196
+ const docs = await this.docs.find(
197
+ this.collection,
198
+ { logId: { $eq: id } },
199
+ { limit: 1 }
200
+ );
178
201
  const first = docs[0];
179
202
  return first ? toLogRecord(first) : null;
180
203
  }
181
204
  async search(searchText, options) {
182
205
  await this.initialised;
183
- const filter = { message: { $contains: searchText } };
206
+ const textFilter = {
207
+ message: { $contains: searchText }
208
+ };
209
+ const structuredFilter = buildFilter(options?.filters ?? {});
210
+ const filter = Object.keys(structuredFilter).length === 0 ? textFilter : { $and: [structuredFilter, textFilter] };
184
211
  const limit = options?.limit ?? 100;
185
212
  const offset = options?.offset ?? 0;
186
213
  const [total, docs] = await Promise.all([
@@ -216,8 +243,16 @@ var DocumentLogPersistence = class {
216
243
  await this.initialised;
217
244
  const [totalLogs, oldest, newest] = await Promise.all([
218
245
  this.docs.count(this.collection, {}),
219
- this.docs.find(this.collection, {}, { sort: { timestamp: 1 }, limit: 1 }),
220
- this.docs.find(this.collection, {}, { sort: { timestamp: -1 }, limit: 1 })
246
+ this.docs.find(
247
+ this.collection,
248
+ {},
249
+ { sort: { timestamp: 1 }, limit: 1 }
250
+ ),
251
+ this.docs.find(
252
+ this.collection,
253
+ {},
254
+ { sort: { timestamp: -1 }, limit: 1 }
255
+ )
221
256
  ]);
222
257
  return {
223
258
  totalLogs,
@@ -253,20 +288,20 @@ var DocumentLogPersistence = class {
253
288
  }
254
289
  const now = Date.now();
255
290
  try {
256
- if (this.retention.maxAgeDebug > 0) {
257
- await this.deleteByLevelOlderThan(["trace", "debug"], now - this.retention.maxAgeDebug);
258
- } else {
259
- await this.deleteByLevelOlderThan(["trace", "debug"], now + 1);
260
- }
261
- if (this.retention.maxAgeInfo > 0) {
262
- await this.deleteByLevelOlderThan(["info"], now - this.retention.maxAgeInfo);
263
- }
264
- if (this.retention.maxAge > 0) {
265
- await this.deleteByLevelOlderThan(["warn", "error", "fatal"], now - this.retention.maxAge);
266
- }
291
+ await this.expire(["trace", "debug"], this.retention.maxAgeDebug, now);
292
+ await this.expire(["info"], this.retention.maxAgeInfo, now);
293
+ await this.expire(["warn"], this.retention.maxAgeWarn, now);
294
+ await this.expire(["error"], this.retention.maxAgeError, now);
295
+ await this.expire(["fatal"], this.retention.maxAgeFatal, now);
267
296
  } catch {
268
297
  }
269
298
  }
299
+ async expire(levels, maxAge, now) {
300
+ await this.deleteByLevelOlderThan(
301
+ levels,
302
+ maxAge > 0 ? now - maxAge : now + 1
303
+ );
304
+ }
270
305
  };
271
306
  var buildFilter = (query) => {
272
307
  const clauses = [];
@@ -282,6 +317,12 @@ var buildFilter = (query) => {
282
317
  if (query.to !== void 0) {
283
318
  clauses.push({ timestamp: { $lte: query.to } });
284
319
  }
320
+ for (const field of LOG_CONTEXT_FIELDS) {
321
+ const value = query[field];
322
+ if (value !== void 0) {
323
+ clauses.push({ [`fields.${field}`]: { $eq: value } });
324
+ }
325
+ }
285
326
  if (clauses.length === 0) {
286
327
  return {};
287
328
  }
@@ -299,7 +340,10 @@ var toLogRecord = (doc) => ({
299
340
  source: doc.source
300
341
  });
301
342
  function createAdapter(config, deps) {
302
- const resolved = deps?.["documentDatabase"] ? { ...config, documentDatabase: deps["documentDatabase"] } : config;
343
+ const resolved = deps?.["documentDatabase"] ? {
344
+ ...config,
345
+ documentDatabase: deps["documentDatabase"]
346
+ } : config;
303
347
  return new DocumentLogPersistence(resolved);
304
348
  }
305
349
  var index_default = createAdapter;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/manifest.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/adapters-log-document/manifest\n *\n * Manifest for the Document-backed log persistence adapter. It depends on\n * the platform's `documentDatabase` service — register a concrete document\n * driver (sqlite, postgres, mongo) first and this adapter rides on top.\n */\n\nimport type { AdapterManifest } from '@kb-labs/sdk/adapters';\n\nexport const manifest: AdapterManifest = {\n manifestVersion: '1.0.0',\n id: 'log-document',\n name: 'Document-backed Log Persistence',\n version: '1.0.0',\n description:\n 'ILogPersistence implementation storing log entries in any IDocumentDatabase. Driver-agnostic.',\n author: 'KB Labs Team',\n license: 'KBPL-1.1',\n type: 'core',\n implements: 'ILogPersistence',\n contexts: ['workspace', 'process'],\n requires: { adapters: [{ id: 'documentDatabase', alias: 'documentDatabase' }] },\n capabilities: {\n batch: true,\n search: true,\n custom: {\n retentionPolicy: true,\n },\n },\n configSchema: {\n collection: {\n type: 'string',\n default: 'logs',\n description: 'Name of the document collection that stores log entries.',\n },\n batchSize: {\n type: 'number',\n default: 100,\n description: 'Batch size for buffered writes.',\n },\n flushInterval: {\n type: 'number',\n default: 5000,\n description: 'Maximum delay before a partial batch is flushed (ms).',\n },\n },\n};\n","/**\n * @module @kb-labs/adapters-log-document\n *\n * `ILogPersistence` implementation backed by any `IDocumentDatabase`.\n *\n * This is the dogfooded replacement for the old sqlite-only log persistence\n * adapter: log entries are now stored as documents in a `logs` collection\n * using the same abstraction every other plugin uses. The platform stays\n * driver-agnostic — switch from sqlite to postgres-JSONB and logs follow.\n *\n * Buffering: writes are queued in memory and flushed in batches to amortise\n * per-call overhead. `close()` (and the next flush after batchSize entries)\n * drains the queue.\n *\n * Retention: a periodic timer deletes documents older than the configured\n * thresholds per log level. Without retention the collection grows\n * unbounded — defaults are applied if `retention` is absent.\n *\n * Search: implemented via `$contains` on the message field. Full-text\n * indexing (FTS5, MongoDB text) is intentionally OUT of scope here — that's\n * a backend-specific capability and would leak through the contract. If\n * advanced search is needed, route it via an external index (Meilisearch\n * etc.) and keep this layer simple.\n */\n\nimport type {\n ILogPersistence,\n LogPersistenceConfig,\n LogRetentionPolicy,\n LogRecord,\n LogQuery,\n LogLevel,\n IDocumentDatabase,\n DocumentFilter,\n BaseDocument,\n} from '@kb-labs/sdk/adapters';\n\nexport { manifest } from './manifest.js';\n\ninterface LogDoc extends BaseDocument {\n logId: string;\n timestamp: number;\n level: LogLevel;\n message: string;\n fields: Record<string, unknown>;\n source: string;\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n trace: 0,\n debug: 1,\n info: 2,\n warn: 3,\n error: 4,\n fatal: 5,\n};\n\nconst LEVELS_AT_OR_ABOVE = (min: LogLevel): LogLevel[] => {\n const threshold = LEVEL_ORDER[min];\n return (Object.keys(LEVEL_ORDER) as LogLevel[]).filter((l) => LEVEL_ORDER[l] >= threshold);\n};\n\nconst DEFAULT_RETENTION: Required<Pick<LogRetentionPolicy,\n 'maxAge' | 'maxAgeDebug' | 'maxAgeInfo' | 'maxSizeBytes' | 'cleanupIntervalMs'>> = {\n maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days for warn/error/fatal\n maxAgeDebug: 60 * 60 * 1000, // 1 hour for debug/trace\n maxAgeInfo: 24 * 60 * 60 * 1000, // 24 hours for info\n maxSizeBytes: 500 * 1024 * 1024, // 500 MB\n cleanupIntervalMs: 5 * 60 * 1000, // 5 minutes\n};\n\nexport class DocumentLogPersistence implements ILogPersistence {\n private readonly docs: IDocumentDatabase;\n private readonly collection: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private readonly retention: Required<Pick<LogRetentionPolicy,\n 'maxAge' | 'maxAgeDebug' | 'maxAgeInfo' | 'maxSizeBytes' | 'cleanupIntervalMs'>>;\n\n private buffer: LogRecord[] = [];\n private flushTimer: NodeJS.Timeout | null = null;\n private retentionTimer: NodeJS.Timeout | null = null;\n private closed = false;\n private flushing: Promise<void> | null = null;\n private initialised: Promise<void>;\n\n constructor(config: LogPersistenceConfig) {\n this.docs = config.documentDatabase;\n this.collection = config.collection ?? 'logs';\n this.batchSize = config.batchSize ?? 100;\n this.flushIntervalMs = config.flushInterval ?? 5_000;\n this.retention = { ...DEFAULT_RETENTION, ...(config.retention ?? {}) };\n\n this.initialised = this.docs.ensureCollection(this.collection, {\n indexes: [\n { path: 'logId', unique: true },\n { path: 'timestamp' },\n { path: 'level' },\n { path: 'source' },\n ],\n });\n\n this.flushTimer = setInterval(() => { void this.flush(); }, this.flushIntervalMs).unref();\n this.retentionTimer = setInterval(() => { void this.runRetention(); }, this.retention.cleanupIntervalMs).unref();\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Write path (buffered)\n // ──────────────────────────────────────────────────────────────────────────\n\n async write(record: LogRecord): Promise<void> {\n if (this.closed) {throw new Error('DocumentLogPersistence is closed');}\n this.buffer.push(record);\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n async writeBatch(records: LogRecord[]): Promise<void> {\n if (this.closed) {throw new Error('DocumentLogPersistence is closed');}\n if (records.length === 0) {return;}\n this.buffer.push(...records);\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n private async flush(): Promise<void> {\n if (this.flushing) {\n await this.flushing;\n return;\n }\n if (this.buffer.length === 0) {return;}\n const batch = this.buffer.splice(0, this.buffer.length);\n\n this.flushing = (async () => {\n try {\n await this.initialised;\n await this.docs.insertMany<LogDoc>(this.collection, batch.map((r) => ({\n logId: r.id,\n timestamp: r.timestamp,\n level: r.level,\n message: r.message,\n fields: r.fields,\n source: r.source,\n })));\n } catch (err) {\n // Re-queue on failure so the next attempt picks up the lost batch.\n this.buffer.unshift(...batch);\n throw err;\n } finally {\n this.flushing = null;\n }\n })();\n\n await this.flushing;\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Read path\n // ──────────────────────────────────────────────────────────────────────────\n\n async query(\n query: LogQuery,\n options?: {\n limit?: number;\n offset?: number;\n sortBy?: 'timestamp' | 'level';\n sortOrder?: 'asc' | 'desc';\n },\n ): Promise<{ logs: LogRecord[]; total: number; hasMore: boolean }> {\n await this.initialised;\n const filter = buildFilter(query);\n const limit = options?.limit ?? 100;\n const offset = options?.offset ?? 0;\n const sortField = options?.sortBy ?? 'timestamp';\n const sortDir = options?.sortOrder === 'asc' ? 1 : -1;\n\n const [total, docs] = await Promise.all([\n this.docs.count<LogDoc>(this.collection, filter),\n this.docs.find<LogDoc>(this.collection, filter, {\n sort: { [sortField]: sortDir },\n limit,\n skip: offset,\n }),\n ]);\n\n return {\n logs: docs.map(toLogRecord),\n total,\n hasMore: offset + docs.length < total,\n };\n }\n\n async getById(id: string): Promise<LogRecord | null> {\n await this.initialised;\n const docs = await this.docs.find<LogDoc>(this.collection, { logId: { $eq: id } }, { limit: 1 });\n const first = docs[0];\n return first ? toLogRecord(first) : null;\n }\n\n async search(\n searchText: string,\n options?: { limit?: number; offset?: number },\n ): Promise<{ logs: LogRecord[]; total: number; hasMore: boolean }> {\n await this.initialised;\n const filter: DocumentFilter<LogDoc> = { message: { $contains: searchText } };\n const limit = options?.limit ?? 100;\n const offset = options?.offset ?? 0;\n\n const [total, docs] = await Promise.all([\n this.docs.count<LogDoc>(this.collection, filter),\n this.docs.find<LogDoc>(this.collection, filter, {\n sort: { timestamp: -1 },\n limit,\n skip: offset,\n }),\n ]);\n\n return {\n logs: docs.map(toLogRecord),\n total,\n hasMore: offset + docs.length < total,\n };\n }\n\n async deleteOlderThan(beforeTimestamp: number): Promise<number> {\n await this.initialised;\n return this.docs.deleteMany<LogDoc>(this.collection, {\n timestamp: { $lt: beforeTimestamp },\n });\n }\n\n async deleteByLevelOlderThan(levels: string[], beforeTimestamp: number): Promise<number> {\n await this.initialised;\n return this.docs.deleteMany<LogDoc>(this.collection, {\n $and: [\n { level: { $in: levels as LogLevel[] } },\n { timestamp: { $lt: beforeTimestamp } },\n ],\n });\n }\n\n async getStats(): Promise<{\n totalLogs: number;\n oldestTimestamp: number;\n newestTimestamp: number;\n sizeBytes: number;\n }> {\n await this.initialised;\n const [totalLogs, oldest, newest] = await Promise.all([\n this.docs.count<LogDoc>(this.collection, {}),\n this.docs.find<LogDoc>(this.collection, {}, { sort: { timestamp: 1 }, limit: 1 }),\n this.docs.find<LogDoc>(this.collection, {}, { sort: { timestamp: -1 }, limit: 1 }),\n ]);\n // Size is not exposed by IDocumentDatabase by design (it varies wildly\n // by backend). Returning 0 here is honest — callers driving retention\n // by size should configure maxAge instead and stop pretending bytes are\n // a universal currency across drivers.\n return {\n totalLogs,\n oldestTimestamp: oldest[0]?.timestamp ?? 0,\n newestTimestamp: newest[0]?.timestamp ?? 0,\n sizeBytes: 0,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {return;}\n this.closed = true;\n if (this.flushTimer) { clearInterval(this.flushTimer); this.flushTimer = null; }\n if (this.retentionTimer) { clearInterval(this.retentionTimer); this.retentionTimer = null; }\n try {\n await this.flush();\n } catch {\n /* swallow — we tried */\n }\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Retention\n // ──────────────────────────────────────────────────────────────────────────\n\n private async runRetention(): Promise<void> {\n if (this.closed) {return;}\n const now = Date.now();\n try {\n if (this.retention.maxAgeDebug > 0) {\n await this.deleteByLevelOlderThan(['trace', 'debug'], now - this.retention.maxAgeDebug);\n } else {\n // maxAgeDebug = 0 means do not persist debug/trace at all → wipe immediately.\n await this.deleteByLevelOlderThan(['trace', 'debug'], now + 1);\n }\n if (this.retention.maxAgeInfo > 0) {\n await this.deleteByLevelOlderThan(['info'], now - this.retention.maxAgeInfo);\n }\n if (this.retention.maxAge > 0) {\n await this.deleteByLevelOlderThan(['warn', 'error', 'fatal'], now - this.retention.maxAge);\n }\n } catch {\n /* retention failures should not propagate — they're best-effort cleanup */\n }\n }\n}\n\nconst buildFilter = (query: LogQuery): DocumentFilter<LogDoc> => {\n const clauses: Array<DocumentFilter<LogDoc>> = [];\n if (query.level !== undefined) {\n clauses.push({ level: { $in: LEVELS_AT_OR_ABOVE(query.level) } });\n }\n if (query.source !== undefined) {\n clauses.push({ source: { $eq: query.source } });\n }\n if (query.from !== undefined) {\n clauses.push({ timestamp: { $gte: query.from } });\n }\n if (query.to !== undefined) {\n clauses.push({ timestamp: { $lte: query.to } });\n }\n if (clauses.length === 0) {return {};}\n if (clauses.length === 1) {return clauses[0]!;}\n return { $and: clauses };\n};\n\nconst toLogRecord = (doc: LogDoc): LogRecord => ({\n id: doc.logId,\n timestamp: doc.timestamp,\n level: doc.level,\n message: doc.message,\n fields: doc.fields,\n source: doc.source,\n});\n\nexport function createAdapter(\n config: LogPersistenceConfig,\n deps?: Record<string, unknown>,\n): DocumentLogPersistence {\n // deps.documentDatabase is injected by the adapter-loader when the manifest\n // declares requires.adapters = [{ id: \"documentDatabase\" }]. Merge it in so\n // callers don't need to duplicate the reference in adapterOptions config.\n const resolved: LogPersistenceConfig = deps?.['documentDatabase']\n ? { ...config, documentDatabase: deps['documentDatabase'] as IDocumentDatabase }\n : config;\n return new DocumentLogPersistence(resolved);\n}\n\nexport default createAdapter;\n"]}
1
+ {"version":3,"sources":["../src/manifest.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/adapters-log-document/manifest\n *\n * Manifest for the Document-backed log persistence adapter. It depends on\n * the platform's `documentDatabase` service — register a concrete document\n * driver (sqlite, postgres, mongo) first and this adapter rides on top.\n */\n\nimport type { AdapterManifest } from \"@kb-labs/sdk/adapters\";\n\nexport const manifest: AdapterManifest = {\n manifestVersion: \"1.0.0\",\n id: \"log-document\",\n name: \"Document-backed Log Persistence\",\n version: \"1.0.0\",\n description:\n \"ILogPersistence implementation storing log entries in any IDocumentDatabase. Driver-agnostic.\",\n author: \"KB Labs Team\",\n license: \"KBPL-1.1\",\n type: \"extension\",\n implements: \"ILogPersistence\",\n extends: {\n adapter: \"logger\",\n hook: \"onLog\",\n method: \"write\",\n priority: 0,\n },\n contexts: [\"workspace\", \"process\"],\n requires: {\n adapters: [{ id: \"documentDatabase\", alias: \"documentDatabase\" }],\n },\n capabilities: {\n batch: true,\n search: true,\n custom: {\n retentionPolicy: true,\n },\n },\n configSchema: {\n collection: {\n type: \"string\",\n default: \"logs\",\n description: \"Name of the document collection that stores log entries.\",\n },\n batchSize: {\n type: \"number\",\n default: 100,\n description: \"Batch size for buffered writes.\",\n },\n flushInterval: {\n type: \"number\",\n default: 5000,\n description: \"Maximum delay before a partial batch is flushed (ms).\",\n },\n },\n};\n","/**\n * @module @kb-labs/adapters-log-document\n *\n * `ILogPersistence` implementation backed by any `IDocumentDatabase`.\n *\n * This is the dogfooded replacement for the old sqlite-only log persistence\n * adapter: log entries are now stored as documents in a `logs` collection\n * using the same abstraction every other plugin uses. The platform stays\n * driver-agnostic — switch from sqlite to postgres-JSONB and logs follow.\n *\n * Buffering: writes are queued in memory and flushed in batches to amortise\n * per-call overhead. `close()` (and the next flush after batchSize entries)\n * drains the queue.\n *\n * Retention: a periodic timer deletes documents older than the configured\n * thresholds per log level. Without retention the collection grows\n * unbounded — defaults are applied if `retention` is absent.\n *\n * Search: implemented via `$contains` on the message field. Full-text\n * indexing (FTS5, MongoDB text) is intentionally OUT of scope here — that's\n * a backend-specific capability and would leak through the contract. If\n * advanced search is needed, route it via an external index (Meilisearch\n * etc.) and keep this layer simple.\n */\n\nimport { LOG_CONTEXT_FIELDS } from \"@kb-labs/sdk/adapters\";\nimport type {\n ILogPersistence,\n LogPersistenceConfig,\n LogRetentionPolicy,\n LogRecord,\n LogQuery,\n LogLevel,\n IDocumentDatabase,\n DocumentFilter,\n BaseDocument,\n} from \"@kb-labs/sdk/adapters\";\n\nexport { manifest } from \"./manifest.js\";\n\ninterface LogDoc extends BaseDocument {\n logId: string;\n timestamp: number;\n level: LogLevel;\n message: string;\n fields: Record<string, unknown>;\n source: string;\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n trace: 0,\n debug: 1,\n info: 2,\n warn: 3,\n error: 4,\n fatal: 5,\n};\n\nconst LEVELS_AT_OR_ABOVE = (min: LogLevel): LogLevel[] => {\n const threshold = LEVEL_ORDER[min];\n return (Object.keys(LEVEL_ORDER) as LogLevel[]).filter(\n (l) => LEVEL_ORDER[l] >= threshold,\n );\n};\n\nconst DEFAULT_RETENTION: Required<\n Pick<\n LogRetentionPolicy,\n | \"maxAgeFatal\"\n | \"maxAgeError\"\n | \"maxAgeWarn\"\n | \"maxAgeDebug\"\n | \"maxAgeInfo\"\n | \"cleanupIntervalMs\"\n >\n> = {\n maxAgeFatal: 30 * 24 * 60 * 60 * 1000,\n maxAgeError: 14 * 24 * 60 * 60 * 1000,\n maxAgeWarn: 7 * 24 * 60 * 60 * 1000,\n maxAgeDebug: 30 * 60 * 1000,\n maxAgeInfo: 30 * 60 * 1000,\n cleanupIntervalMs: 5 * 60 * 1000, // 5 minutes\n};\n\nexport class DocumentLogPersistence implements ILogPersistence {\n private readonly docs: IDocumentDatabase;\n private readonly collection: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private readonly retention: Required<\n Pick<\n LogRetentionPolicy,\n | \"maxAgeFatal\"\n | \"maxAgeError\"\n | \"maxAgeWarn\"\n | \"maxAgeDebug\"\n | \"maxAgeInfo\"\n | \"cleanupIntervalMs\"\n >\n >;\n\n private buffer: LogRecord[] = [];\n private flushTimer: NodeJS.Timeout | null = null;\n private retentionTimer: NodeJS.Timeout | null = null;\n private closed = false;\n private flushing: Promise<void> | null = null;\n private initialised: Promise<void>;\n\n constructor(config: LogPersistenceConfig) {\n this.docs = config.documentDatabase;\n this.collection = config.collection ?? \"logs\";\n this.batchSize = config.batchSize ?? 100;\n this.flushIntervalMs = config.flushInterval ?? 5_000;\n this.retention = { ...DEFAULT_RETENTION, ...(config.retention ?? {}) };\n\n this.initialised = this.docs.ensureCollection(this.collection, {\n indexes: [\n { path: \"logId\", unique: true },\n { path: \"timestamp\" },\n { path: \"level\" },\n { path: \"source\" },\n ...LOG_CONTEXT_FIELDS.map((field) => ({ path: `fields.${field}` })),\n ],\n });\n\n this.flushTimer = setInterval(() => {\n this.scheduleFlush();\n }, this.flushIntervalMs).unref();\n this.retentionTimer = setInterval(() => {\n void this.runRetention();\n }, this.retention.cleanupIntervalMs).unref();\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Write path (buffered)\n // ──────────────────────────────────────────────────────────────────────────\n\n async write(record: LogRecord): Promise<void> {\n if (this.closed) {\n return;\n }\n this.buffer.push(record);\n if (this.buffer.length >= this.batchSize) {\n this.scheduleFlush();\n }\n }\n\n async writeBatch(records: LogRecord[]): Promise<void> {\n if (this.closed) {\n return;\n }\n if (records.length === 0) {\n return;\n }\n this.buffer.push(...records);\n if (this.buffer.length >= this.batchSize) {\n this.scheduleFlush();\n }\n }\n\n private async flush(): Promise<void> {\n if (this.flushing) {\n await this.flushing;\n return;\n }\n if (this.buffer.length === 0) {\n return;\n }\n const batch = this.buffer.splice(0, this.buffer.length);\n\n this.flushing = (async () => {\n try {\n await this.initialised;\n await this.docs.insertMany<LogDoc>(\n this.collection,\n batch.map((r) => ({\n logId: r.id,\n timestamp: r.timestamp,\n level: r.level,\n message: r.message,\n fields: r.fields,\n source: r.source,\n })),\n );\n } catch (err) {\n // Re-queue on failure so the next attempt picks up the lost batch.\n this.buffer.unshift(...batch);\n throw err;\n } finally {\n this.flushing = null;\n }\n })();\n\n await this.flushing;\n }\n\n private scheduleFlush(): void {\n void this.flush().catch(() => {\n // The batch was re-queued by flush(). A subsequent interval or write\n // retries it; logging itself must not create an unhandled rejection.\n });\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Read path\n // ──────────────────────────────────────────────────────────────────────────\n\n async query(\n query: LogQuery,\n options?: {\n limit?: number;\n offset?: number;\n sortBy?: \"timestamp\" | \"level\";\n sortOrder?: \"asc\" | \"desc\";\n },\n ): Promise<{ logs: LogRecord[]; total: number; hasMore: boolean }> {\n await this.initialised;\n const filter = buildFilter(query);\n const limit = options?.limit ?? 100;\n const offset = options?.offset ?? 0;\n const sortField = options?.sortBy ?? \"timestamp\";\n const sortDir = options?.sortOrder === \"asc\" ? 1 : -1;\n\n const [total, docs] = await Promise.all([\n this.docs.count<LogDoc>(this.collection, filter),\n this.docs.find<LogDoc>(this.collection, filter, {\n sort: { [sortField]: sortDir },\n limit,\n skip: offset,\n }),\n ]);\n\n return {\n logs: docs.map(toLogRecord),\n total,\n hasMore: offset + docs.length < total,\n };\n }\n\n async getById(id: string): Promise<LogRecord | null> {\n await this.initialised;\n const docs = await this.docs.find<LogDoc>(\n this.collection,\n { logId: { $eq: id } },\n { limit: 1 },\n );\n const first = docs[0];\n return first ? toLogRecord(first) : null;\n }\n\n async search(\n searchText: string,\n options?: { limit?: number; offset?: number; filters?: LogQuery },\n ): Promise<{ logs: LogRecord[]; total: number; hasMore: boolean }> {\n await this.initialised;\n const textFilter: DocumentFilter<LogDoc> = {\n message: { $contains: searchText },\n };\n const structuredFilter = buildFilter(options?.filters ?? {});\n const filter: DocumentFilter<LogDoc> =\n Object.keys(structuredFilter).length === 0\n ? textFilter\n : { $and: [structuredFilter, textFilter] };\n const limit = options?.limit ?? 100;\n const offset = options?.offset ?? 0;\n\n const [total, docs] = await Promise.all([\n this.docs.count<LogDoc>(this.collection, filter),\n this.docs.find<LogDoc>(this.collection, filter, {\n sort: { timestamp: -1 },\n limit,\n skip: offset,\n }),\n ]);\n\n return {\n logs: docs.map(toLogRecord),\n total,\n hasMore: offset + docs.length < total,\n };\n }\n\n async deleteOlderThan(beforeTimestamp: number): Promise<number> {\n await this.initialised;\n return this.docs.deleteMany<LogDoc>(this.collection, {\n timestamp: { $lt: beforeTimestamp },\n });\n }\n\n async deleteByLevelOlderThan(\n levels: string[],\n beforeTimestamp: number,\n ): Promise<number> {\n await this.initialised;\n return this.docs.deleteMany<LogDoc>(this.collection, {\n $and: [\n { level: { $in: levels as LogLevel[] } },\n { timestamp: { $lt: beforeTimestamp } },\n ],\n });\n }\n\n async getStats(): Promise<{\n totalLogs: number;\n oldestTimestamp: number;\n newestTimestamp: number;\n sizeBytes: number;\n }> {\n await this.initialised;\n const [totalLogs, oldest, newest] = await Promise.all([\n this.docs.count<LogDoc>(this.collection, {}),\n this.docs.find<LogDoc>(\n this.collection,\n {},\n { sort: { timestamp: 1 }, limit: 1 },\n ),\n this.docs.find<LogDoc>(\n this.collection,\n {},\n { sort: { timestamp: -1 }, limit: 1 },\n ),\n ]);\n // Size is not exposed by IDocumentDatabase by design (it varies wildly\n // by backend). Returning 0 here is honest — callers driving retention\n // by size should configure maxAge instead and stop pretending bytes are\n // a universal currency across drivers.\n return {\n totalLogs,\n oldestTimestamp: oldest[0]?.timestamp ?? 0,\n newestTimestamp: newest[0]?.timestamp ?? 0,\n sizeBytes: 0,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n if (this.retentionTimer) {\n clearInterval(this.retentionTimer);\n this.retentionTimer = null;\n }\n try {\n await this.flush();\n } catch {\n /* swallow — we tried */\n }\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Retention\n // ──────────────────────────────────────────────────────────────────────────\n\n private async runRetention(): Promise<void> {\n if (this.closed) {\n return;\n }\n const now = Date.now();\n try {\n await this.expire([\"trace\", \"debug\"], this.retention.maxAgeDebug, now);\n await this.expire([\"info\"], this.retention.maxAgeInfo, now);\n await this.expire([\"warn\"], this.retention.maxAgeWarn, now);\n await this.expire([\"error\"], this.retention.maxAgeError, now);\n await this.expire([\"fatal\"], this.retention.maxAgeFatal, now);\n } catch {\n /* retention failures should not propagate — they're best-effort cleanup */\n }\n }\n\n private async expire(\n levels: LogLevel[],\n maxAge: number,\n now: number,\n ): Promise<void> {\n await this.deleteByLevelOlderThan(\n levels,\n maxAge > 0 ? now - maxAge : now + 1,\n );\n }\n}\n\nconst buildFilter = (query: LogQuery): DocumentFilter<LogDoc> => {\n const clauses: Array<DocumentFilter<LogDoc>> = [];\n if (query.level !== undefined) {\n clauses.push({ level: { $in: LEVELS_AT_OR_ABOVE(query.level) } });\n }\n if (query.source !== undefined) {\n clauses.push({ source: { $eq: query.source } });\n }\n if (query.from !== undefined) {\n clauses.push({ timestamp: { $gte: query.from } });\n }\n if (query.to !== undefined) {\n clauses.push({ timestamp: { $lte: query.to } });\n }\n for (const field of LOG_CONTEXT_FIELDS) {\n const value = query[field];\n if (value !== undefined) {\n clauses.push({ [`fields.${field}`]: { $eq: value } });\n }\n }\n if (clauses.length === 0) {\n return {};\n }\n if (clauses.length === 1) {\n return clauses[0]!;\n }\n return { $and: clauses };\n};\n\nconst toLogRecord = (doc: LogDoc): LogRecord => ({\n id: doc.logId,\n timestamp: doc.timestamp,\n level: doc.level,\n message: doc.message,\n fields: doc.fields,\n source: doc.source,\n});\n\nexport function createAdapter(\n config: LogPersistenceConfig,\n deps?: Record<string, unknown>,\n): DocumentLogPersistence {\n // deps.documentDatabase is injected by the adapter-loader when the manifest\n // declares requires.adapters = [{ id: \"documentDatabase\" }]. Merge it in so\n // callers don't need to duplicate the reference in adapterOptions config.\n const resolved: LogPersistenceConfig = deps?.[\"documentDatabase\"]\n ? {\n ...config,\n documentDatabase: deps[\"documentDatabase\"] as IDocumentDatabase,\n }\n : config;\n return new DocumentLogPersistence(resolved);\n}\n\nexport default createAdapter;\n"]}
package/dist/manifest.js CHANGED
@@ -7,10 +7,18 @@ var manifest = {
7
7
  description: "ILogPersistence implementation storing log entries in any IDocumentDatabase. Driver-agnostic.",
8
8
  author: "KB Labs Team",
9
9
  license: "KBPL-1.1",
10
- type: "core",
10
+ type: "extension",
11
11
  implements: "ILogPersistence",
12
+ extends: {
13
+ adapter: "logger",
14
+ hook: "onLog",
15
+ method: "write",
16
+ priority: 0
17
+ },
12
18
  contexts: ["workspace", "process"],
13
- requires: { adapters: [{ id: "documentDatabase", alias: "documentDatabase" }] },
19
+ requires: {
20
+ adapters: [{ id: "documentDatabase", alias: "documentDatabase" }]
21
+ },
14
22
  capabilities: {
15
23
  batch: true,
16
24
  search: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":";AAUO,IAAM,QAAA,GAA4B;AAAA,EACvC,eAAA,EAAiB,OAAA;AAAA,EACjB,EAAA,EAAI,cAAA;AAAA,EACJ,IAAA,EAAM,iCAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,WAAA,EACE,+FAAA;AAAA,EACF,MAAA,EAAQ,cAAA;AAAA,EACR,OAAA,EAAS,UAAA;AAAA,EACT,IAAA,EAAM,MAAA;AAAA,EACN,UAAA,EAAY,iBAAA;AAAA,EACZ,QAAA,EAAU,CAAC,WAAA,EAAa,SAAS,CAAA;AAAA,EACjC,QAAA,EAAU,EAAE,QAAA,EAAU,CAAC,EAAE,IAAI,kBAAA,EAAoB,KAAA,EAAO,kBAAA,EAAoB,CAAA,EAAE;AAAA,EAC9E,YAAA,EAAc;AAAA,IACZ,KAAA,EAAO,IAAA;AAAA,IACP,MAAA,EAAQ,IAAA;AAAA,IACR,MAAA,EAAQ;AAAA,MACN,eAAA,EAAiB;AAAA;AACnB,GACF;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,UAAA,EAAY;AAAA,MACV,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,MAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA,IACA,SAAA,EAAW;AAAA,MACT,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,GAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA,IACA,aAAA,EAAe;AAAA,MACb,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,GAAA;AAAA,MACT,WAAA,EAAa;AAAA;AACf;AAEJ","file":"manifest.js","sourcesContent":["/**\n * @module @kb-labs/adapters-log-document/manifest\n *\n * Manifest for the Document-backed log persistence adapter. It depends on\n * the platform's `documentDatabase` service — register a concrete document\n * driver (sqlite, postgres, mongo) first and this adapter rides on top.\n */\n\nimport type { AdapterManifest } from '@kb-labs/sdk/adapters';\n\nexport const manifest: AdapterManifest = {\n manifestVersion: '1.0.0',\n id: 'log-document',\n name: 'Document-backed Log Persistence',\n version: '1.0.0',\n description:\n 'ILogPersistence implementation storing log entries in any IDocumentDatabase. Driver-agnostic.',\n author: 'KB Labs Team',\n license: 'KBPL-1.1',\n type: 'core',\n implements: 'ILogPersistence',\n contexts: ['workspace', 'process'],\n requires: { adapters: [{ id: 'documentDatabase', alias: 'documentDatabase' }] },\n capabilities: {\n batch: true,\n search: true,\n custom: {\n retentionPolicy: true,\n },\n },\n configSchema: {\n collection: {\n type: 'string',\n default: 'logs',\n description: 'Name of the document collection that stores log entries.',\n },\n batchSize: {\n type: 'number',\n default: 100,\n description: 'Batch size for buffered writes.',\n },\n flushInterval: {\n type: 'number',\n default: 5000,\n description: 'Maximum delay before a partial batch is flushed (ms).',\n },\n },\n};\n"]}
1
+ {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":";AAUO,IAAM,QAAA,GAA4B;AAAA,EACvC,eAAA,EAAiB,OAAA;AAAA,EACjB,EAAA,EAAI,cAAA;AAAA,EACJ,IAAA,EAAM,iCAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,WAAA,EACE,+FAAA;AAAA,EACF,MAAA,EAAQ,cAAA;AAAA,EACR,OAAA,EAAS,UAAA;AAAA,EACT,IAAA,EAAM,WAAA;AAAA,EACN,UAAA,EAAY,iBAAA;AAAA,EACZ,OAAA,EAAS;AAAA,IACP,OAAA,EAAS,QAAA;AAAA,IACT,IAAA,EAAM,OAAA;AAAA,IACN,MAAA,EAAQ,OAAA;AAAA,IACR,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,QAAA,EAAU,CAAC,WAAA,EAAa,SAAS,CAAA;AAAA,EACjC,QAAA,EAAU;AAAA,IACR,UAAU,CAAC,EAAE,IAAI,kBAAA,EAAoB,KAAA,EAAO,oBAAoB;AAAA,GAClE;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,KAAA,EAAO,IAAA;AAAA,IACP,MAAA,EAAQ,IAAA;AAAA,IACR,MAAA,EAAQ;AAAA,MACN,eAAA,EAAiB;AAAA;AACnB,GACF;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,UAAA,EAAY;AAAA,MACV,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,MAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA,IACA,SAAA,EAAW;AAAA,MACT,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,GAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA,IACA,aAAA,EAAe;AAAA,MACb,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,GAAA;AAAA,MACT,WAAA,EAAa;AAAA;AACf;AAEJ","file":"manifest.js","sourcesContent":["/**\n * @module @kb-labs/adapters-log-document/manifest\n *\n * Manifest for the Document-backed log persistence adapter. It depends on\n * the platform's `documentDatabase` service — register a concrete document\n * driver (sqlite, postgres, mongo) first and this adapter rides on top.\n */\n\nimport type { AdapterManifest } from \"@kb-labs/sdk/adapters\";\n\nexport const manifest: AdapterManifest = {\n manifestVersion: \"1.0.0\",\n id: \"log-document\",\n name: \"Document-backed Log Persistence\",\n version: \"1.0.0\",\n description:\n \"ILogPersistence implementation storing log entries in any IDocumentDatabase. Driver-agnostic.\",\n author: \"KB Labs Team\",\n license: \"KBPL-1.1\",\n type: \"extension\",\n implements: \"ILogPersistence\",\n extends: {\n adapter: \"logger\",\n hook: \"onLog\",\n method: \"write\",\n priority: 0,\n },\n contexts: [\"workspace\", \"process\"],\n requires: {\n adapters: [{ id: \"documentDatabase\", alias: \"documentDatabase\" }],\n },\n capabilities: {\n batch: true,\n search: true,\n custom: {\n retentionPolicy: true,\n },\n },\n configSchema: {\n collection: {\n type: \"string\",\n default: \"logs\",\n description: \"Name of the document collection that stores log entries.\",\n },\n batchSize: {\n type: \"number\",\n default: 100,\n description: \"Batch size for buffered writes.\",\n },\n flushInterval: {\n type: \"number\",\n default: 5000,\n description: \"Maximum delay before a partial batch is flushed (ms).\",\n },\n },\n};\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kb-labs/adapters-log-document",
3
3
  "description": "ILogPersistence implementation backed by IDocumentDatabase (dogfooded — same abstraction the rest of the platform uses).",
4
- "version": "2.111.0",
4
+ "version": "2.114.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",