@donkeylabs/server 2.0.22 → 2.0.24

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/src/core.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { sql, type Kysely } from "kysely";
2
2
  import { readdir } from "node:fs/promises";
3
- import { join, dirname } from "node:path";
4
- import { fileURLToPath } from "node:url";
3
+ import { join, dirname, resolve } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  import type { z } from "zod";
6
6
  import type { Logger } from "./core/logger";
7
7
  import type { Cache } from "./core/cache";
@@ -18,6 +18,32 @@ import type { WebSocketService } from "./core/websocket";
18
18
  import type { Storage } from "./core/storage";
19
19
  import type { Logs } from "./core/logs";
20
20
 
21
+ // ============================================
22
+ // Auto-detect caller module for plugin define()
23
+ // ============================================
24
+
25
+ const CORE_FILE = resolve(fileURLToPath(import.meta.url));
26
+
27
+ /**
28
+ * Walk the call stack to find the file that invoked define().
29
+ * Returns a file:// URL string or undefined if detection fails.
30
+ * Skips frames originating from this file (core.ts).
31
+ */
32
+ function captureCallerUrl(): string | undefined {
33
+ const stack = new Error().stack ?? "";
34
+ for (const line of stack.split("\n").slice(1)) {
35
+ const match = line.match(/at\s+(?:.*?\s+\(?)?([^\s():]+):\d+:\d+/);
36
+ if (match) {
37
+ let filePath = match[1];
38
+ if (filePath.startsWith("file://")) filePath = fileURLToPath(filePath);
39
+ if (filePath.startsWith("native")) continue;
40
+ filePath = resolve(filePath);
41
+ if (filePath !== CORE_FILE) return pathToFileURL(filePath).href;
42
+ }
43
+ }
44
+ return undefined;
45
+ }
46
+
21
47
  export interface PluginRegistry {}
22
48
 
23
49
  export interface ClientConfig {
@@ -330,7 +356,7 @@ export class PluginBuilder<LocalSchema = {}> {
330
356
  client?: ClientConfig;
331
357
  customErrors?: CustomErrors;
332
358
  } {
333
- return config as any;
359
+ return { ...config, _modulePath: captureCallerUrl() } as any;
334
360
  }
335
361
  }
336
362
 
@@ -386,9 +412,11 @@ export class ConfiguredPluginBuilder<LocalSchema, Config> {
386
412
  client?: ClientConfig;
387
413
  customErrors?: CustomErrors;
388
414
  }> {
415
+ const modulePath = captureCallerUrl();
389
416
  const factory = (config: Config) => ({
390
417
  ...pluginDef,
391
418
  _boundConfig: config,
419
+ _modulePath: modulePath,
392
420
  });
393
421
  return factory as any;
394
422
  }
@@ -425,6 +453,8 @@ export type Plugin = {
425
453
  service: (ctx: any) => any;
426
454
  /** Called after service is created - use for registering crons, events, etc. */
427
455
  init?: (ctx: any, service: any) => void | Promise<void>;
456
+ /** Auto-detected module path where the plugin was defined */
457
+ _modulePath?: string;
428
458
  };
429
459
 
430
460
  export type PluginWithConfig<Config = void> = Plugin & {
@@ -454,6 +484,54 @@ export class PluginManager {
454
484
  return Array.from(this.plugins.values());
455
485
  }
456
486
 
487
+ getPluginNames(): string[] {
488
+ return Array.from(this.plugins.keys());
489
+ }
490
+
491
+ /** Returns { name: modulePath } for plugins that have a captured module path */
492
+ getPluginModulePaths(): Record<string, string> {
493
+ const result: Record<string, string> = {};
494
+ for (const [name, plugin] of this.plugins) {
495
+ if (plugin._modulePath) {
496
+ result[name] = plugin._modulePath;
497
+ }
498
+ }
499
+ return result;
500
+ }
501
+
502
+ /** Returns { name: boundConfig } for configured plugins */
503
+ getPluginConfigs(): Record<string, any> {
504
+ const result: Record<string, any> = {};
505
+ for (const [name, plugin] of this.plugins) {
506
+ if ((plugin as ConfiguredPlugin)._boundConfig !== undefined) {
507
+ result[name] = (plugin as ConfiguredPlugin)._boundConfig;
508
+ }
509
+ }
510
+ return result;
511
+ }
512
+
513
+ /** Returns { name: [...deps] } for plugins with dependencies */
514
+ getPluginDependencies(): Record<string, string[]> {
515
+ const result: Record<string, string[]> = {};
516
+ for (const [name, plugin] of this.plugins) {
517
+ if (plugin.dependencies && plugin.dependencies.length > 0) {
518
+ result[name] = [...plugin.dependencies];
519
+ }
520
+ }
521
+ return result;
522
+ }
523
+
524
+ /** Returns custom error definitions per plugin */
525
+ getPluginCustomErrors(): Record<string, Record<string, any>> {
526
+ const result: Record<string, Record<string, any>> = {};
527
+ for (const [name, plugin] of this.plugins) {
528
+ if (plugin.customErrors && Object.keys(plugin.customErrors).length > 0) {
529
+ result[name] = plugin.customErrors;
530
+ }
531
+ }
532
+ return result;
533
+ }
534
+
457
535
  register(plugin: ConfiguredPlugin): void {
458
536
  if (this.plugins.has(plugin.name)) {
459
537
  throw new Error(`Plugin ${plugin.name} is already registered.`);
package/src/server.ts CHANGED
@@ -264,6 +264,7 @@ export class AppServer {
264
264
  const cron = createCron({
265
265
  ...options.cron,
266
266
  logger,
267
+ events,
267
268
  });
268
269
 
269
270
  // Create adapters - use Kysely by default, or legacy SQLite if requested
@@ -1023,6 +1024,15 @@ ${factoryFunction}
1023
1024
  // Pass plugins to workflows so handlers can access ctx.plugins
1024
1025
  this.coreServices.workflows.setPlugins(this.manager.getServices());
1025
1026
 
1027
+ // Forward plugin metadata so isolated workflows can instantiate plugins locally
1028
+ this.coreServices.workflows.setPluginMetadata({
1029
+ names: this.manager.getPluginNames(),
1030
+ modulePaths: this.manager.getPluginModulePaths(),
1031
+ configs: this.manager.getPluginConfigs(),
1032
+ dependencies: this.manager.getPluginDependencies(),
1033
+ customErrors: this.manager.getPluginCustomErrors(),
1034
+ });
1035
+
1026
1036
  this.isInitialized = true;
1027
1037
 
1028
1038
  this.coreServices.cron.start();