@jeffusion/bungee-core 2.3.1 → 3.0.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.
Files changed (3) hide show
  1. package/dist/master.js +489 -39
  2. package/dist/worker.js +6696 -3506
  3. package/package.json +9 -3
package/dist/master.js CHANGED
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
19
37
  var __export = (target, all) => {
20
38
  for (var name in all)
21
39
  __defProp(target, name, {
22
40
  get: all[name],
23
41
  enumerable: true,
24
42
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
26
44
  });
27
45
  };
28
46
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -566,7 +584,7 @@ var require_colors = __commonJS((exports, module) => {
566
584
  colors.stripColors = colors.strip = function(str) {
567
585
  return ("" + str).replace(/\x1B\[\d+m/g, "");
568
586
  };
569
- var stylize = colors.stylize = function stylize(str, style) {
587
+ var stylize = colors.stylize = function stylize2(str, style) {
570
588
  if (!colors.enabled) {
571
589
  return str + "";
572
590
  }
@@ -584,8 +602,8 @@ var require_colors = __commonJS((exports, module) => {
584
602
  return str.replace(matchOperatorsRe, "\\$&");
585
603
  };
586
604
  function build(_styles) {
587
- var builder = function builder() {
588
- return applyStyle.apply(builder, arguments);
605
+ var builder = function builder2() {
606
+ return applyStyle.apply(builder2, arguments);
589
607
  };
590
608
  builder._styles = _styles;
591
609
  builder.__proto__ = proto;
@@ -604,7 +622,7 @@ var require_colors = __commonJS((exports, module) => {
604
622
  });
605
623
  return ret;
606
624
  }();
607
- var proto = defineProps(function colors() {}, styles);
625
+ var proto = defineProps(function colors2() {}, styles);
608
626
  function applyStyle() {
609
627
  var args = Array.prototype.slice.call(arguments);
610
628
  var str = args.map(function(arg) {
@@ -663,7 +681,7 @@ var require_colors = __commonJS((exports, module) => {
663
681
  });
664
682
  return ret;
665
683
  }
666
- var sequencer = function sequencer(map2, str) {
684
+ var sequencer = function sequencer2(map2, str) {
667
685
  var exploded = str.split("");
668
686
  exploded = exploded.map(map2);
669
687
  return exploded.join("");
@@ -3389,28 +3407,28 @@ var require_end_of_stream = __commonJS((exports, module) => {
3389
3407
  callback = once(callback || noop);
3390
3408
  var readable = opts.readable || opts.readable !== false && stream.readable;
3391
3409
  var writable = opts.writable || opts.writable !== false && stream.writable;
3392
- var onlegacyfinish = function onlegacyfinish() {
3410
+ var onlegacyfinish = function onlegacyfinish2() {
3393
3411
  if (!stream.writable)
3394
3412
  onfinish();
3395
3413
  };
3396
3414
  var writableEnded = stream._writableState && stream._writableState.finished;
3397
- var onfinish = function onfinish() {
3415
+ var onfinish = function onfinish2() {
3398
3416
  writable = false;
3399
3417
  writableEnded = true;
3400
3418
  if (!readable)
3401
3419
  callback.call(stream);
3402
3420
  };
3403
3421
  var readableEnded = stream._readableState && stream._readableState.endEmitted;
3404
- var onend = function onend() {
3422
+ var onend = function onend2() {
3405
3423
  readable = false;
3406
3424
  readableEnded = true;
3407
3425
  if (!writable)
3408
3426
  callback.call(stream);
3409
3427
  };
3410
- var onerror = function onerror(err) {
3428
+ var onerror = function onerror2(err) {
3411
3429
  callback.call(stream, err);
3412
3430
  };
3413
- var onclose = function onclose() {
3431
+ var onclose = function onclose2() {
3414
3432
  var err;
3415
3433
  if (readable && !readableEnded) {
3416
3434
  if (!stream._readableState || !stream._readableState.ended)
@@ -3423,7 +3441,7 @@ var require_end_of_stream = __commonJS((exports, module) => {
3423
3441
  return callback.call(stream, err);
3424
3442
  }
3425
3443
  };
3426
- var onrequest = function onrequest() {
3444
+ var onrequest = function onrequest2() {
3427
3445
  stream.req.on("finish", onfinish);
3428
3446
  };
3429
3447
  if (isRequest(stream)) {
@@ -3580,7 +3598,7 @@ var require_async_iterator = __commonJS((exports, module) => {
3580
3598
  });
3581
3599
  });
3582
3600
  }), _Object$setPrototypeO), AsyncIteratorPrototype);
3583
- var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) {
3601
+ var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator2(stream) {
3584
3602
  var _Object$create;
3585
3603
  var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, {
3586
3604
  value: stream,
@@ -3769,7 +3787,7 @@ var require__stream_readable = __commonJS((exports, module) => {
3769
3787
  var Duplex;
3770
3788
  Readable.ReadableState = ReadableState;
3771
3789
  var EE = __require("events").EventEmitter;
3772
- var EElistenerCount = function EElistenerCount(emitter, type) {
3790
+ var EElistenerCount = function EElistenerCount2(emitter, type) {
3773
3791
  return emitter.listeners(type).length;
3774
3792
  };
3775
3793
  var Stream = __require("stream");
@@ -3786,7 +3804,7 @@ var require__stream_readable = __commonJS((exports, module) => {
3786
3804
  if (debugUtil && debugUtil.debuglog) {
3787
3805
  debug = debugUtil.debuglog("stream");
3788
3806
  } else {
3789
- debug = function debug() {};
3807
+ debug = function debug2() {};
3790
3808
  }
3791
3809
  var BufferList = require_buffer_list();
3792
3810
  var destroyImpl = require_destroy();
@@ -4732,7 +4750,7 @@ var require__stream_writable = __commonJS((exports, module) => {
4732
4750
  }
4733
4751
  });
4734
4752
  } else {
4735
- realHasInstance = function realHasInstance(object) {
4753
+ realHasInstance = function realHasInstance2(object) {
4736
4754
  return object instanceof this;
4737
4755
  };
4738
4756
  }
@@ -5110,7 +5128,7 @@ var require_modern = __commonJS((exports, module) => {
5110
5128
  var util = __require("util");
5111
5129
  var Writable = require__stream_writable();
5112
5130
  var { LEVEL } = require_triple_beam();
5113
- var TransportStream = module.exports = function TransportStream(options = {}) {
5131
+ var TransportStream = module.exports = function TransportStream2(options = {}) {
5114
5132
  Writable.call(this, { objectMode: true, highWaterMark: options.highWaterMark });
5115
5133
  this.format = options.format;
5116
5134
  this.level = options.level;
@@ -5221,7 +5239,7 @@ var require_legacy = __commonJS((exports, module) => {
5221
5239
  var util = __require("util");
5222
5240
  var { LEVEL } = require_triple_beam();
5223
5241
  var TransportStream = require_modern();
5224
- var LegacyTransportStream = module.exports = function LegacyTransportStream(options = {}) {
5242
+ var LegacyTransportStream = module.exports = function LegacyTransportStream2(options = {}) {
5225
5243
  TransportStream.call(this, options);
5226
5244
  if (!options.transport || typeof options.transport.log !== "function") {
5227
5245
  throw new Error("Invalid transport, must be an object with a log method.");
@@ -7739,7 +7757,7 @@ var require_index_cjs = __commonJS((exports, module) => {
7739
7757
  function getDefaultExportFromCjs(x) {
7740
7758
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
7741
7759
  }
7742
- var textHex = function hex(str) {
7760
+ var textHex = function hex2(str) {
7743
7761
  for (var i = 0, hash = 0;i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash))
7744
7762
  ;
7745
7763
  var color = Math.floor(Math.abs(Math.sin(hash) * 1e4 % 1 * 16777216)).toString(16);
@@ -14448,7 +14466,7 @@ var require_FileStreamRotator = __commonJS((exports, module) => {
14448
14466
  return fullPath;
14449
14467
  }, "");
14450
14468
  };
14451
- var BubbleEvents = function BubbleEvents(emitter, proxy) {
14469
+ var BubbleEvents = function BubbleEvents2(emitter, proxy) {
14452
14470
  emitter.on("close", function() {
14453
14471
  proxy.emit("close");
14454
14472
  });
@@ -15159,16 +15177,26 @@ var require_main = __commonJS((exports, module) => {
15159
15177
  // src/logger.ts
15160
15178
  import path from "path";
15161
15179
  import fs from "fs";
15162
- function createLogMethod(level) {
15180
+ function createLogMethod(level, defaultMeta = {}) {
15163
15181
  return function(objOrMsg, msg) {
15164
15182
  if (typeof objOrMsg === "string") {
15165
- winstonLogger.log(level, objOrMsg);
15183
+ winstonLogger.log(level, objOrMsg, defaultMeta);
15166
15184
  } else {
15167
15185
  const message = msg || "";
15168
- winstonLogger.log(level, message, objOrMsg);
15186
+ winstonLogger.log(level, message, { ...defaultMeta, ...objOrMsg });
15169
15187
  }
15170
15188
  };
15171
15189
  }
15190
+ function createLogger(defaultMeta = {}) {
15191
+ return {
15192
+ info: createLogMethod("info", defaultMeta),
15193
+ warn: createLogMethod("warn", defaultMeta),
15194
+ error: createLogMethod("error", defaultMeta),
15195
+ debug: createLogMethod("debug", defaultMeta),
15196
+ fatal: createLogMethod("error", defaultMeta),
15197
+ child: (bindings) => createLogger({ ...defaultMeta, ...bindings })
15198
+ };
15199
+ }
15172
15200
  var import_winston, import_winston_daily_rotate_file, import_dotenv, logsDir, logLevel, isDevelopment = true, isMaster, pinoStyleConsoleFormat, transports, winstonLogger, logger;
15173
15201
  var init_logger = __esm(() => {
15174
15202
  import_winston = __toESM(require_winston(), 1);
@@ -15216,13 +15244,7 @@ var init_logger = __esm(() => {
15216
15244
  level: logLevel,
15217
15245
  transports
15218
15246
  });
15219
- logger = {
15220
- info: createLogMethod("info"),
15221
- warn: createLogMethod("warn"),
15222
- error: createLogMethod("error"),
15223
- debug: createLogMethod("debug"),
15224
- fatal: createLogMethod("error")
15225
- };
15247
+ logger = createLogger();
15226
15248
  });
15227
15249
 
15228
15250
  // src/logger/access-log-writer.ts
@@ -15233,6 +15255,7 @@ import fs2 from "fs";
15233
15255
  class AccessLogWriter {
15234
15256
  db;
15235
15257
  writeQueue = [];
15258
+ pendingRespBodyIdUpdates = new Map;
15236
15259
  isProcessing = false;
15237
15260
  flushInterval = null;
15238
15261
  constructor(dbPath) {
@@ -15246,6 +15269,10 @@ class AccessLogWriter {
15246
15269
  this.startFlushInterval();
15247
15270
  }
15248
15271
  write(entry) {
15272
+ const pendingRespBodyId = this.pendingRespBodyIdUpdates.get(entry.requestId);
15273
+ if (pendingRespBodyId) {
15274
+ entry.respBodyId = pendingRespBodyId;
15275
+ }
15249
15276
  this.writeQueue.push(entry);
15250
15277
  if (this.writeQueue.length >= 100) {
15251
15278
  this.flush().catch((err) => {
@@ -15276,12 +15303,19 @@ class AccessLogWriter {
15276
15303
  let insertedCount = 0;
15277
15304
  let ignoredCount = 0;
15278
15305
  for (const entry of batch) {
15306
+ const pendingRespBodyId = this.pendingRespBodyIdUpdates.get(entry.requestId);
15307
+ if (pendingRespBodyId) {
15308
+ entry.respBodyId = pendingRespBodyId;
15309
+ }
15279
15310
  const result = insert.run(entry.requestId, entry.timestamp, entry.method, entry.path, entry.query || null, entry.status, entry.duration, entry.routePath || null, entry.upstream || null, entry.transformer || null, entry.processingSteps ? JSON.stringify(entry.processingSteps) : null, entry.authSuccess !== undefined ? entry.authSuccess ? 1 : 0 : 1, entry.authLevel || null, entry.errorMessage || null, entry.reqBodyId || null, entry.respBodyId || null, entry.reqHeaderId || null, entry.respHeaderId || null, entry.originalReqHeaderId || null, entry.originalReqBodyId || null, entry.transformedPath || null, entry.status < 400 ? 1 : 0, Math.floor(entry.timestamp / 1000), entry.isFailoverAttempt ? 1 : 0, entry.parentRequestId || null, entry.attemptNumber || null, entry.attemptUpstream || null, entry.requestType || "final");
15280
15311
  if (result.changes > 0) {
15281
15312
  insertedCount++;
15282
15313
  } else {
15283
15314
  ignoredCount++;
15284
15315
  }
15316
+ if (pendingRespBodyId) {
15317
+ this.pendingRespBodyIdUpdates.delete(entry.requestId);
15318
+ }
15285
15319
  }
15286
15320
  this.db.run("COMMIT");
15287
15321
  transactionStarted = false;
@@ -15322,6 +15356,29 @@ class AccessLogWriter {
15322
15356
  getDatabase() {
15323
15357
  return this.db;
15324
15358
  }
15359
+ updateResponseBodyId(requestId, respBodyId) {
15360
+ if (!requestId || !respBodyId) {
15361
+ return;
15362
+ }
15363
+ this.pendingRespBodyIdUpdates.set(requestId, respBodyId);
15364
+ for (const entry of this.writeQueue) {
15365
+ if (entry.requestId === requestId) {
15366
+ entry.respBodyId = respBodyId;
15367
+ }
15368
+ }
15369
+ try {
15370
+ const result = this.db.prepare("UPDATE access_logs SET resp_body_id = ? WHERE request_id = ?").run(respBodyId, requestId);
15371
+ if (result.changes > 0) {
15372
+ this.pendingRespBodyIdUpdates.delete(requestId);
15373
+ }
15374
+ } catch (error) {
15375
+ console.error("Failed to update response body id for streamed log:", {
15376
+ requestId,
15377
+ respBodyId,
15378
+ error
15379
+ });
15380
+ }
15381
+ }
15325
15382
  }
15326
15383
  var dbPath, accessLogWriter;
15327
15384
  var init_access_log_writer = __esm(() => {
@@ -15777,6 +15834,58 @@ var init_001_initial_schema_with_failover = __esm(() => {
15777
15834
  };
15778
15835
  });
15779
15836
 
15837
+ // src/migrations/versions/002_add_plugin_storage.ts
15838
+ var migration2;
15839
+ var init_002_add_plugin_storage = __esm(() => {
15840
+ migration2 = {
15841
+ version: "002",
15842
+ name: "add_plugin_storage",
15843
+ up: (db) => {
15844
+ db.run(`
15845
+ CREATE TABLE IF NOT EXISTS plugin_storage (
15846
+ plugin_name TEXT NOT NULL,
15847
+ key TEXT NOT NULL,
15848
+ value TEXT NOT NULL,
15849
+ ttl INTEGER, -- \u8FC7\u671F\u65F6\u95F4\u6233\uFF08\u79D2\uFF09\uFF0CNULL \u8868\u793A\u6C38\u4E0D\u8FC7\u671F
15850
+ updated_at INTEGER NOT NULL,
15851
+ PRIMARY KEY (plugin_name, key)
15852
+ )
15853
+ `);
15854
+ db.run(`
15855
+ CREATE INDEX IF NOT EXISTS idx_plugin_storage_ttl
15856
+ ON plugin_storage(ttl)
15857
+ WHERE ttl IS NOT NULL
15858
+ `);
15859
+ }
15860
+ };
15861
+ });
15862
+
15863
+ // src/migrations/versions/003_add_plugin_registry.ts
15864
+ var migration3;
15865
+ var init_003_add_plugin_registry = __esm(() => {
15866
+ migration3 = {
15867
+ version: "003",
15868
+ name: "add_plugin_registry",
15869
+ up: (db) => {
15870
+ db.run(`
15871
+ CREATE TABLE IF NOT EXISTS plugin_registry (
15872
+ name TEXT PRIMARY KEY,
15873
+ version TEXT NOT NULL,
15874
+ description TEXT,
15875
+ path TEXT,
15876
+ enabled INTEGER NOT NULL DEFAULT 0,
15877
+ created_at INTEGER NOT NULL,
15878
+ updated_at INTEGER NOT NULL
15879
+ )
15880
+ `);
15881
+ db.run(`
15882
+ CREATE INDEX IF NOT EXISTS idx_plugin_registry_enabled
15883
+ ON plugin_registry(enabled)
15884
+ `);
15885
+ }
15886
+ };
15887
+ });
15888
+
15780
15889
  // src/migrations/migration-manager.ts
15781
15890
  import { Database as Database2 } from "bun:sqlite";
15782
15891
  import fs5 from "fs";
@@ -15805,11 +15914,11 @@ class MigrationManager {
15805
15914
  logger.info({ count: pending.length, migrations: pending.map((m) => m.name) }, "Executing pending migrations");
15806
15915
  this.db.run("BEGIN TRANSACTION");
15807
15916
  try {
15808
- for (const migration2 of pending) {
15809
- logger.debug({ migration: migration2.name }, "Applying migration");
15810
- migration2.up(this.db);
15811
- this.recordMigration(migration2);
15812
- logger.info({ migration: migration2.name }, "Migration applied successfully");
15917
+ for (const migration4 of pending) {
15918
+ logger.debug({ migration: migration4.name }, "Applying migration");
15919
+ migration4.up(this.db);
15920
+ this.recordMigration(migration4);
15921
+ logger.info({ migration: migration4.name }, "Migration applied successfully");
15813
15922
  }
15814
15923
  this.db.run("COMMIT");
15815
15924
  logger.info("All migrations completed successfully");
@@ -15859,12 +15968,12 @@ class MigrationManager {
15859
15968
  const appliedVersions = new Set(applied.map((r) => r.version));
15860
15969
  return migrations.filter((m) => !appliedVersions.has(m.version));
15861
15970
  }
15862
- recordMigration(migration2) {
15971
+ recordMigration(migration4) {
15863
15972
  if (!this.db) {
15864
15973
  throw new Error("Database not initialized");
15865
15974
  }
15866
15975
  const stmt = this.db.prepare("INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)");
15867
- stmt.run(migration2.version, migration2.name, Date.now());
15976
+ stmt.run(migration4.version, migration4.name, Date.now());
15868
15977
  }
15869
15978
  async attemptAutoRecovery(error) {
15870
15979
  const errorMessage = error.message.toLowerCase();
@@ -15927,9 +16036,13 @@ var init_migration_manager = __esm(() => {
15927
16036
  var migrations;
15928
16037
  var init_migrations = __esm(() => {
15929
16038
  init_001_initial_schema_with_failover();
16039
+ init_002_add_plugin_storage();
16040
+ init_003_add_plugin_registry();
15930
16041
  init_migration_manager();
15931
16042
  migrations = [
15932
- migration
16043
+ migration,
16044
+ migration2,
16045
+ migration3
15933
16046
  ];
15934
16047
  });
15935
16048
 
@@ -16087,17 +16200,339 @@ var init_config = __esm(() => {
16087
16200
  init_logger();
16088
16201
  });
16089
16202
 
16203
+ // src/plugin-storage-cleanup.ts
16204
+ class PluginStorageCleanupService {
16205
+ db;
16206
+ config;
16207
+ timer = null;
16208
+ running = false;
16209
+ stats = {
16210
+ totalRuns: 0,
16211
+ totalDeleted: 0,
16212
+ lastRunTime: 0,
16213
+ lastDeletedCount: 0,
16214
+ lastDuration: 0
16215
+ };
16216
+ constructor(db, config) {
16217
+ this.db = db;
16218
+ this.config = { ...DEFAULT_CONFIG3, ...config };
16219
+ logger.info({ config: this.config }, "PluginStorageCleanupService initialized");
16220
+ }
16221
+ start() {
16222
+ if (!this.config.enabled) {
16223
+ logger.info("PluginStorageCleanupService is disabled");
16224
+ return;
16225
+ }
16226
+ if (this.timer) {
16227
+ logger.warn("PluginStorageCleanupService already started");
16228
+ return;
16229
+ }
16230
+ logger.info({ interval: this.config.interval }, "Starting PluginStorageCleanupService");
16231
+ this.cleanup().catch((err) => {
16232
+ logger.error({ error: err }, "Initial cleanup failed");
16233
+ });
16234
+ this.timer = setInterval(() => {
16235
+ this.cleanup().catch((err) => {
16236
+ logger.error({ error: err }, "Scheduled cleanup failed");
16237
+ });
16238
+ }, this.config.interval);
16239
+ }
16240
+ stop() {
16241
+ if (this.timer) {
16242
+ clearInterval(this.timer);
16243
+ this.timer = null;
16244
+ logger.info("PluginStorageCleanupService stopped");
16245
+ }
16246
+ }
16247
+ async cleanup() {
16248
+ if (this.running) {
16249
+ logger.warn("Cleanup already running, skipping");
16250
+ return 0;
16251
+ }
16252
+ this.running = true;
16253
+ const startTime = Date.now();
16254
+ let totalDeleted = 0;
16255
+ try {
16256
+ logger.info("Starting plugin storage cleanup");
16257
+ const now = Math.floor(Date.now() / 1000);
16258
+ let batchCount = 0;
16259
+ let hasMore = true;
16260
+ while (hasMore && batchCount < 10) {
16261
+ const deleteStmt = this.db.prepare(`
16262
+ DELETE FROM plugin_storage
16263
+ WHERE rowid IN (
16264
+ SELECT rowid FROM plugin_storage
16265
+ WHERE ttl IS NOT NULL AND ttl < ?
16266
+ LIMIT ?
16267
+ )
16268
+ `);
16269
+ const result = deleteStmt.run(now, this.config.batchSize);
16270
+ const deleted = result.changes;
16271
+ totalDeleted += deleted;
16272
+ batchCount++;
16273
+ logger.debug({ batch: batchCount, deleted, totalDeleted }, "Cleanup batch completed");
16274
+ hasMore = deleted >= this.config.batchSize;
16275
+ if (hasMore) {
16276
+ await new Promise((resolve) => setTimeout(resolve, 100));
16277
+ }
16278
+ }
16279
+ if (this.config.vacuumAfterCleanup && totalDeleted > 0) {
16280
+ logger.info("Running VACUUM after cleanup");
16281
+ this.db.run("VACUUM");
16282
+ }
16283
+ const duration = Date.now() - startTime;
16284
+ this.stats.totalRuns++;
16285
+ this.stats.totalDeleted += totalDeleted;
16286
+ this.stats.lastRunTime = startTime;
16287
+ this.stats.lastDeletedCount = totalDeleted;
16288
+ this.stats.lastDuration = duration;
16289
+ logger.info({
16290
+ deleted: totalDeleted,
16291
+ duration,
16292
+ batches: batchCount
16293
+ }, "Plugin storage cleanup completed");
16294
+ return totalDeleted;
16295
+ } catch (error) {
16296
+ logger.error({ error }, "Plugin storage cleanup failed");
16297
+ throw error;
16298
+ } finally {
16299
+ this.running = false;
16300
+ }
16301
+ }
16302
+ getStats() {
16303
+ return {
16304
+ ...this.stats,
16305
+ running: this.running,
16306
+ enabled: this.config.enabled
16307
+ };
16308
+ }
16309
+ resetStats() {
16310
+ this.stats = {
16311
+ totalRuns: 0,
16312
+ totalDeleted: 0,
16313
+ lastRunTime: 0,
16314
+ lastDeletedCount: 0,
16315
+ lastDuration: 0
16316
+ };
16317
+ }
16318
+ async getExpiredCount() {
16319
+ const now = Math.floor(Date.now() / 1000);
16320
+ const stmt = this.db.prepare(`
16321
+ SELECT COUNT(*) as count FROM plugin_storage
16322
+ WHERE ttl IS NOT NULL AND ttl < ?
16323
+ `);
16324
+ const result = stmt.get(now);
16325
+ return result.count;
16326
+ }
16327
+ async getStorageStats() {
16328
+ const now = Math.floor(Date.now() / 1000);
16329
+ const totalEntriesStmt = this.db.prepare(`
16330
+ SELECT COUNT(*) as count FROM plugin_storage
16331
+ `);
16332
+ const totalEntries = totalEntriesStmt.get().count;
16333
+ const totalWithTTLStmt = this.db.prepare(`
16334
+ SELECT COUNT(*) as count FROM plugin_storage
16335
+ WHERE ttl IS NOT NULL
16336
+ `);
16337
+ const totalWithTTL = totalWithTTLStmt.get().count;
16338
+ const totalExpiredStmt = this.db.prepare(`
16339
+ SELECT COUNT(*) as count FROM plugin_storage
16340
+ WHERE ttl IS NOT NULL AND ttl < ?
16341
+ `);
16342
+ const totalExpired = totalExpiredStmt.get(now).count;
16343
+ const totalPluginsStmt = this.db.prepare(`
16344
+ SELECT COUNT(DISTINCT plugin_name) as count FROM plugin_storage
16345
+ `);
16346
+ const totalPlugins = totalPluginsStmt.get().count;
16347
+ return {
16348
+ totalEntries,
16349
+ totalWithTTL,
16350
+ totalExpired,
16351
+ totalPlugins
16352
+ };
16353
+ }
16354
+ }
16355
+ var DEFAULT_CONFIG3;
16356
+ var init_plugin_storage_cleanup = __esm(() => {
16357
+ init_logger();
16358
+ DEFAULT_CONFIG3 = {
16359
+ enabled: true,
16360
+ interval: 3600000,
16361
+ batchSize: 1000,
16362
+ vacuumAfterCleanup: false
16363
+ };
16364
+ });
16365
+
16366
+ // src/plugin-permissions.ts
16367
+ class PluginPermissionManager {
16368
+ permissions = new Map;
16369
+ registerPlugin(pluginName, metadata) {
16370
+ const permissions = new Set;
16371
+ permissions.add("storage" /* STORAGE */);
16372
+ if (metadata.permissions && Array.isArray(metadata.permissions)) {
16373
+ for (const perm of metadata.permissions) {
16374
+ if (perm === "network")
16375
+ permissions.add("network" /* NETWORK */);
16376
+ else if (perm === "filesystem")
16377
+ permissions.add("filesystem" /* FILESYSTEM */);
16378
+ else if (perm === "storage")
16379
+ permissions.add("storage" /* STORAGE */);
16380
+ else if (perm === "ui:modals")
16381
+ permissions.add("ui:modals" /* UI_MODALS */);
16382
+ else if (perm === "ui:popups")
16383
+ permissions.add("ui:popups" /* UI_POPUPS */);
16384
+ else if (perm === "ui:forms")
16385
+ permissions.add("ui:forms" /* UI_FORMS */);
16386
+ else if (perm === "ui:navigation")
16387
+ permissions.add("ui:navigation" /* UI_NAVIGATION */);
16388
+ else if (perm === "api:routes")
16389
+ permissions.add("api:routes" /* API_ROUTES */);
16390
+ else if (perm === "api:plugins")
16391
+ permissions.add("api:plugins" /* API_PLUGINS */);
16392
+ else if (perm === "api:logs")
16393
+ permissions.add("api:logs" /* API_LOGS */);
16394
+ }
16395
+ }
16396
+ if (metadata.capabilities) {
16397
+ const { network, filesystem, database } = metadata.capabilities;
16398
+ if (network)
16399
+ permissions.add("network" /* NETWORK */);
16400
+ if (filesystem)
16401
+ permissions.add("filesystem" /* FILESYSTEM */);
16402
+ if (database)
16403
+ permissions.add("database" /* DATABASE */);
16404
+ }
16405
+ if (metadata.contributes) {
16406
+ if (metadata.contributes.navigation?.length) {
16407
+ permissions.add("ui:navigation" /* UI_NAVIGATION */);
16408
+ }
16409
+ if (metadata.contributes.commands?.length) {
16410
+ permissions.add("api:routes" /* API_ROUTES */);
16411
+ }
16412
+ }
16413
+ if (metadata.contributes?.navigation || metadata.contributes?.widgets) {
16414
+ permissions.add("ui:forms" /* UI_FORMS */);
16415
+ permissions.add("ui:modals" /* UI_MODALS */);
16416
+ }
16417
+ this.permissions.set(pluginName, permissions);
16418
+ logger.debug({ pluginName, permissions: Array.from(permissions) }, "Registered plugin permissions");
16419
+ }
16420
+ hasPermission(pluginName, permission) {
16421
+ const perms = this.permissions.get(pluginName);
16422
+ if (!perms) {
16423
+ logger.warn({ pluginName, permission }, "Plugin not registered in permission manager");
16424
+ return false;
16425
+ }
16426
+ return perms.has(permission);
16427
+ }
16428
+ hasPermissions(pluginName, requiredPermissions) {
16429
+ return requiredPermissions.every((perm) => this.hasPermission(pluginName, perm));
16430
+ }
16431
+ getPermissions(pluginName) {
16432
+ return this.permissions.get(pluginName) || new Set;
16433
+ }
16434
+ getSandboxAttributes(pluginName) {
16435
+ const perms = this.permissions.get(pluginName);
16436
+ if (!perms) {
16437
+ return "allow-scripts";
16438
+ }
16439
+ const sandboxAttrs = new Set([
16440
+ "allow-scripts",
16441
+ "allow-same-origin"
16442
+ ]);
16443
+ for (const perm of perms) {
16444
+ const sandboxAttr = PERMISSION_TO_SANDBOX[perm];
16445
+ if (sandboxAttr) {
16446
+ sandboxAttr.split(" ").forEach((attr) => sandboxAttrs.add(attr));
16447
+ }
16448
+ }
16449
+ return Array.from(sandboxAttrs).join(" ");
16450
+ }
16451
+ getCSP(pluginName) {
16452
+ const perms = this.permissions.get(pluginName);
16453
+ if (!perms) {
16454
+ return "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'";
16455
+ }
16456
+ const cspDirectives = [];
16457
+ cspDirectives.push("default-src 'self'");
16458
+ cspDirectives.push("script-src 'self' 'unsafe-inline' 'unsafe-eval'");
16459
+ cspDirectives.push("style-src 'self' 'unsafe-inline'");
16460
+ cspDirectives.push("font-src 'self' data:");
16461
+ cspDirectives.push("img-src 'self' data: blob:");
16462
+ if (perms.has("network" /* NETWORK */)) {
16463
+ cspDirectives.push("connect-src 'self' https: wss:");
16464
+ } else {
16465
+ cspDirectives.push("connect-src 'self'");
16466
+ }
16467
+ cspDirectives.push("frame-ancestors 'self'");
16468
+ cspDirectives.push("object-src 'none'");
16469
+ cspDirectives.push("base-uri 'self'");
16470
+ return cspDirectives.join("; ");
16471
+ }
16472
+ canAccessAPI(pluginName, apiPath) {
16473
+ const apiPermissionMap = {
16474
+ "/api/plugins": "api:plugins" /* API_PLUGINS */,
16475
+ "/api/routes": "api:routes" /* API_ROUTES */,
16476
+ "/api/logs": "api:logs" /* API_LOGS */
16477
+ };
16478
+ for (const [prefix, permission] of Object.entries(apiPermissionMap)) {
16479
+ if (apiPath.startsWith(prefix)) {
16480
+ return this.hasPermission(pluginName, permission);
16481
+ }
16482
+ }
16483
+ return true;
16484
+ }
16485
+ unregisterPlugin(pluginName) {
16486
+ this.permissions.delete(pluginName);
16487
+ logger.debug({ pluginName }, "Unregistered plugin permissions");
16488
+ }
16489
+ clear() {
16490
+ this.permissions.clear();
16491
+ }
16492
+ getStats() {
16493
+ return {
16494
+ totalPlugins: this.permissions.size,
16495
+ plugins: Array.from(this.permissions.keys())
16496
+ };
16497
+ }
16498
+ }
16499
+ function initializePermissionManager() {
16500
+ if (!globalPermissionManager) {
16501
+ globalPermissionManager = new PluginPermissionManager;
16502
+ logger.info("Plugin permission manager initialized");
16503
+ }
16504
+ return globalPermissionManager;
16505
+ }
16506
+ function getPermissionManager() {
16507
+ if (!globalPermissionManager) {
16508
+ throw new Error("PermissionManager not initialized. Call initializePermissionManager() first.");
16509
+ }
16510
+ return globalPermissionManager;
16511
+ }
16512
+ var PERMISSION_TO_SANDBOX, globalPermissionManager = null;
16513
+ var init_plugin_permissions = __esm(() => {
16514
+ init_logger();
16515
+ PERMISSION_TO_SANDBOX = {
16516
+ ["ui:modals" /* UI_MODALS */]: "allow-modals",
16517
+ ["ui:popups" /* UI_POPUPS */]: "allow-popups allow-popups-to-escape-sandbox",
16518
+ ["ui:forms" /* UI_FORMS */]: "allow-forms",
16519
+ ["ui:navigation" /* UI_NAVIGATION */]: "allow-top-navigation-by-user-activation"
16520
+ };
16521
+ });
16522
+
16090
16523
  // src/master.ts
16091
16524
  var exports_master = {};
16092
16525
  import path7 from "path";
16093
16526
  import { spawn } from "child_process";
16094
16527
  import fs7 from "fs";
16528
+ import { Database as Database3 } from "bun:sqlite";
16095
16529
 
16096
16530
  class Master {
16097
16531
  workers = new Map;
16098
16532
  config;
16099
16533
  isReloading = false;
16100
16534
  reloadTimeout = null;
16535
+ pluginStorageCleanupService = null;
16101
16536
  constructor() {
16102
16537
  this.loadAndStart();
16103
16538
  this.watchConfig();
@@ -16125,6 +16560,16 @@ class Master {
16125
16560
  } else {
16126
16561
  logger.info("Database migrations completed successfully");
16127
16562
  }
16563
+ const db = new Database3(dbPath2);
16564
+ this.pluginStorageCleanupService = new PluginStorageCleanupService(db, {
16565
+ enabled: true,
16566
+ interval: 3600000,
16567
+ batchSize: 1000,
16568
+ vacuumAfterCleanup: false
16569
+ });
16570
+ this.pluginStorageCleanupService.start();
16571
+ initializePermissionManager();
16572
+ logger.info("Plugin permission manager initialized");
16128
16573
  await this.startWorkers();
16129
16574
  } catch (error) {
16130
16575
  logger.error({ error }, "Failed to load or parse config.json");
@@ -16332,6 +16777,9 @@ class Master {
16332
16777
  await Promise.all(shutdownPromises);
16333
16778
  this.workers.clear();
16334
16779
  logCleanupService.stop();
16780
+ if (this.pluginStorageCleanupService) {
16781
+ this.pluginStorageCleanupService.stop();
16782
+ }
16335
16783
  }
16336
16784
  }
16337
16785
  var import_dotenv2, CONFIG_PATH, WORKER_COUNT, PORT;
@@ -16340,6 +16788,8 @@ var init_master = __esm(() => {
16340
16788
  init_log_cleanup();
16341
16789
  init_migrations();
16342
16790
  init_config();
16791
+ init_plugin_storage_cleanup();
16792
+ init_plugin_permissions();
16343
16793
  import_dotenv2 = __toESM(require_main(), 1);
16344
16794
  import_dotenv2.default.config();
16345
16795
  CONFIG_PATH = process.env.CONFIG_PATH || path7.resolve(process.cwd(), "config.json");