@a16njs/engine 0.5.0 → 0.6.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
@@ -1,4 +1,6 @@
1
- import type { A16nPlugin, AgentCustomization, DiscoveryResult, Warning, WrittenFile, CustomizationType } from '@a16njs/models';
1
+ import type { A16nPlugin, AgentCustomization, DiscoveryResult, Warning, WrittenFile, CustomizationType, Workspace } from '@a16njs/models';
2
+ import { type PluginDiscoveryOptions } from './plugin-discovery.js';
3
+ import { type ContentTransformation } from './transformation.js';
2
4
  /**
3
5
  * Options for a conversion operation.
4
6
  */
@@ -7,25 +9,25 @@ export interface ConversionOptions {
7
9
  source: string;
8
10
  /** Target plugin ID */
9
11
  target: string;
10
- /** Project root directory (used as default for both source and target) */
12
+ /** Project root directory */
11
13
  root: string;
12
14
  /** If true, only discover without writing */
13
15
  dryRun?: boolean;
14
- /**
15
- * Override root directory for discovery (reading).
16
- * When set, discover() uses this instead of `root`.
17
- */
16
+ /** Override root for discovery (source plugin) */
18
17
  sourceRoot?: string;
19
- /**
20
- * Override root directory for emission (writing).
21
- * When set, emit() uses this instead of `root`.
22
- */
18
+ /** Override root for emission (target plugin) */
23
19
  targetRoot?: string;
20
+ /** Workspace for source discovery (takes precedence over sourceRoot/root) */
21
+ sourceWorkspace?: Workspace;
22
+ /** Workspace for target emission (takes precedence over targetRoot/root) */
23
+ targetWorkspace?: Workspace;
24
24
  /**
25
- * If true, rewrite file path references in content so they point
26
- * to the target-format paths instead of source-format paths.
25
+ * If true, rewrite path references in content during conversion.
26
+ * @deprecated Use `transformations: [new PathRewritingTransformation()]` instead.
27
27
  */
28
28
  rewritePathRefs?: boolean;
29
+ /** Content transformations to apply between discovery and emission */
30
+ transformations?: ContentTransformation[];
29
31
  }
30
32
  /**
31
33
  * Git-ignore change information.
@@ -62,22 +64,44 @@ export interface PluginInfo {
62
64
  supports: CustomizationType[];
63
65
  source: 'bundled' | 'installed';
64
66
  }
67
+ /**
68
+ * Result of discovering and registering plugins.
69
+ */
70
+ export interface DiscoverAndRegisterResult {
71
+ /** Plugin IDs that were successfully registered */
72
+ registered: string[];
73
+ /** Plugin IDs that were skipped (already registered) */
74
+ skipped: string[];
75
+ /** Errors encountered during discovery */
76
+ errors: Array<{
77
+ packageName: string;
78
+ error: string;
79
+ }>;
80
+ }
65
81
  /**
66
82
  * The a16n conversion engine.
67
83
  * Orchestrates plugins to discover and emit agent customizations.
68
84
  */
69
85
  export declare class A16nEngine {
70
- private plugins;
86
+ private registry;
87
+ private loader;
71
88
  /**
72
89
  * Create a new engine with the given plugins.
73
- * @param plugins - Plugins to register
90
+ * @param plugins - Plugins to register (registered as 'bundled')
74
91
  */
75
92
  constructor(plugins?: A16nPlugin[]);
76
93
  /**
77
94
  * Register a plugin with the engine.
78
95
  * @param plugin - The plugin to register
96
+ * @param source - Whether the plugin is bundled or installed
97
+ */
98
+ registerPlugin(plugin: A16nPlugin, source?: 'bundled' | 'installed'): void;
99
+ /**
100
+ * Discover and register installed plugins from node_modules.
101
+ * @param options - Plugin discovery options
102
+ * @returns Result with registered, skipped, and error info
79
103
  */
80
- registerPlugin(plugin: A16nPlugin): void;
104
+ discoverAndRegisterPlugins(options?: PluginDiscoveryOptions): Promise<DiscoverAndRegisterResult>;
81
105
  /**
82
106
  * List all registered plugins.
83
107
  * @returns Array of plugin info
@@ -92,10 +116,10 @@ export declare class A16nEngine {
92
116
  /**
93
117
  * Discover customizations using a specific plugin.
94
118
  * @param pluginId - The plugin to use for discovery
95
- * @param root - The project root to scan
119
+ * @param rootOrWorkspace - The project root path or Workspace to scan
96
120
  * @returns Discovery result with items and warnings
97
121
  */
98
- discover(pluginId: string, root: string): Promise<DiscoveryResult>;
122
+ discover(pluginId: string, rootOrWorkspace: string | Workspace): Promise<DiscoveryResult>;
99
123
  /**
100
124
  * Convert customizations from one format to another.
101
125
  * @param options - Conversion options
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,WAAW,EACX,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAsBxB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,8BAA8B;IAC9B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,2CAA2C;IAC3C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;CACjC;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAsC;IAErD;;;OAGG;gBACS,OAAO,GAAE,UAAU,EAAO;IAMtC;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIxC;;;OAGG;IACH,WAAW,IAAI,UAAU,EAAE;IAS3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI7C;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAQxE;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAmFrE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,54 +1,61 @@
1
- import { buildMapping, rewriteContent, detectOrphans } from './path-rewriter.js';
2
- /**
3
- * Well-known directory prefixes and file extensions for each plugin.
4
- * Used by detectOrphans() to identify source-format path references.
5
- */
6
- const PLUGIN_PATH_PATTERNS = {
7
- cursor: {
8
- prefixes: ['.cursor/rules/', '.cursor/skills/', '.cursor/commands/'],
9
- extensions: ['.mdc', '.md'],
10
- },
11
- claude: {
12
- prefixes: ['.claude/rules/', '.claude/skills/'],
13
- extensions: ['.md'],
14
- },
15
- a16n: {
16
- prefixes: ['.a16n/'],
17
- extensions: ['.md', '.json'],
18
- },
19
- };
1
+ import { PluginRegistry } from './plugin-registry.js';
2
+ import { PluginLoader, PluginConflictStrategy } from './plugin-loader.js';
3
+ import { LocalWorkspace } from './workspace.js';
4
+ import { PathRewritingTransformation, } from './transformation.js';
20
5
  /**
21
6
  * The a16n conversion engine.
22
7
  * Orchestrates plugins to discover and emit agent customizations.
23
8
  */
24
9
  export class A16nEngine {
25
- plugins = new Map();
10
+ registry = new PluginRegistry();
11
+ loader;
26
12
  /**
27
13
  * Create a new engine with the given plugins.
28
- * @param plugins - Plugins to register
14
+ * @param plugins - Plugins to register (registered as 'bundled')
29
15
  */
30
16
  constructor(plugins = []) {
17
+ this.loader = new PluginLoader(PluginConflictStrategy.PREFER_BUNDLED);
31
18
  for (const plugin of plugins) {
32
- this.registerPlugin(plugin);
19
+ this.registerPlugin(plugin, 'bundled');
33
20
  }
34
21
  }
35
22
  /**
36
23
  * Register a plugin with the engine.
37
24
  * @param plugin - The plugin to register
25
+ * @param source - Whether the plugin is bundled or installed
26
+ */
27
+ registerPlugin(plugin, source = 'bundled') {
28
+ this.registry.register({ plugin, source });
29
+ }
30
+ /**
31
+ * Discover and register installed plugins from node_modules.
32
+ * @param options - Plugin discovery options
33
+ * @returns Result with registered, skipped, and error info
38
34
  */
39
- registerPlugin(plugin) {
40
- this.plugins.set(plugin.id, plugin);
35
+ async discoverAndRegisterPlugins(options) {
36
+ const candidates = await this.loader.loadInstalled(options);
37
+ const resolved = this.loader.resolveConflicts(this.registry, candidates);
38
+ const registered = [];
39
+ for (const reg of resolved.loaded) {
40
+ this.registry.register(reg);
41
+ registered.push(reg.plugin.id);
42
+ }
43
+ return {
44
+ registered,
45
+ skipped: resolved.skipped.map((s) => s.plugin.id),
46
+ errors: resolved.errors,
47
+ };
41
48
  }
42
49
  /**
43
50
  * List all registered plugins.
44
51
  * @returns Array of plugin info
45
52
  */
46
53
  listPlugins() {
47
- return Array.from(this.plugins.values()).map((p) => ({
48
- id: p.id,
49
- name: p.name,
50
- supports: p.supports,
51
- source: 'bundled',
54
+ return this.registry.list().map((r) => ({
55
+ id: r.plugin.id,
56
+ name: r.plugin.name,
57
+ supports: r.plugin.supports,
58
+ source: r.source,
52
59
  }));
53
60
  }
54
61
  /**
@@ -57,20 +64,23 @@ export class A16nEngine {
57
64
  * @returns The plugin or undefined if not found
58
65
  */
59
66
  getPlugin(id) {
60
- return this.plugins.get(id);
67
+ return this.registry.getPlugin(id);
61
68
  }
62
69
  /**
63
70
  * Discover customizations using a specific plugin.
64
71
  * @param pluginId - The plugin to use for discovery
65
- * @param root - The project root to scan
72
+ * @param rootOrWorkspace - The project root path or Workspace to scan
66
73
  * @returns Discovery result with items and warnings
67
74
  */
68
- async discover(pluginId, root) {
75
+ async discover(pluginId, rootOrWorkspace) {
69
76
  const plugin = this.getPlugin(pluginId);
70
77
  if (!plugin) {
71
78
  throw new Error(`Unknown plugin: ${pluginId}`);
72
79
  }
73
- return plugin.discover(root);
80
+ const workspace = typeof rootOrWorkspace === 'string'
81
+ ? new LocalWorkspace('discover', rootOrWorkspace)
82
+ : rootOrWorkspace;
83
+ return plugin.discover(workspace);
74
84
  }
75
85
  /**
76
86
  * Convert customizations from one format to another.
@@ -86,55 +96,52 @@ export class A16nEngine {
86
96
  if (!targetPlugin) {
87
97
  throw new Error(`Unknown target: ${options.target}`);
88
98
  }
89
- // Resolve split roots: sourceRoot for discover, targetRoot for emit
90
- const effectiveSourceRoot = options.sourceRoot ?? options.root;
91
- const effectiveTargetRoot = options.targetRoot ?? options.root;
92
- // Discover from source
93
- const discovery = await sourcePlugin.discover(effectiveSourceRoot);
94
- const allWarnings = [...discovery.warnings];
95
- if (options.rewritePathRefs && discovery.items.length > 0) {
96
- // Two-pass emit approach:
97
- // Pass 1: Dry-run emit to get target paths for building the mapping
98
- const dryEmission = await targetPlugin.emit(discovery.items, effectiveTargetRoot, {
99
- dryRun: true,
100
- });
101
- allWarnings.push(...dryEmission.warnings);
102
- // Build source→target path mapping from the dry-run results
103
- const mapping = buildMapping(discovery.items, dryEmission.written, effectiveSourceRoot, effectiveTargetRoot);
104
- // Rewrite content in discovered items
105
- const rewriteResult = rewriteContent(discovery.items, mapping);
106
- // Detect orphan references
107
- const sourcePatterns = PLUGIN_PATH_PATTERNS[options.source];
108
- if (sourcePatterns) {
109
- const orphanWarnings = detectOrphans(rewriteResult.items, mapping, sourcePatterns.prefixes, sourcePatterns.extensions);
110
- allWarnings.push(...orphanWarnings);
111
- }
112
- // Pass 2: Real emit with rewritten items
113
- const emission = await targetPlugin.emit(rewriteResult.items, effectiveTargetRoot, {
114
- dryRun: options.dryRun,
115
- });
116
- // Deduplicate: add only emission warnings not already present from dry-run
117
- const existingMessages = new Set(allWarnings.map((w) => w.message));
118
- for (const w of emission.warnings) {
119
- if (!existingMessages.has(w.message)) {
120
- allWarnings.push(w);
121
- }
99
+ // Resolve source and target roots/workspaces
100
+ // Priority: explicit workspace > explicit root override > default root
101
+ const discoverRoot = options.sourceRoot ?? options.root;
102
+ const emitRoot = options.targetRoot ?? options.root;
103
+ const sourceWorkspace = options.sourceWorkspace
104
+ ?? new LocalWorkspace('source', discoverRoot);
105
+ const targetWorkspace = options.targetWorkspace
106
+ ?? new LocalWorkspace('target', emitRoot);
107
+ // Discover from source using workspace
108
+ const discovery = await sourcePlugin.discover(sourceWorkspace);
109
+ // Collect warnings
110
+ const warnings = [...discovery.warnings];
111
+ // Build transformations list
112
+ const transformations = options.transformations
113
+ ? [...options.transformations]
114
+ : [];
115
+ // Backward compatibility: rewritePathRefs maps to PathRewritingTransformation
116
+ if (options.rewritePathRefs && !transformations.some((t) => t.id === 'path-rewriting')) {
117
+ transformations.push(new PathRewritingTransformation());
118
+ }
119
+ // Apply transformation pipeline
120
+ let itemsToEmit = discovery.items;
121
+ if (transformations.length > 0) {
122
+ for (const transformation of transformations) {
123
+ const transformContext = {
124
+ items: itemsToEmit,
125
+ sourcePlugin,
126
+ targetPlugin,
127
+ sourceRoot: sourceWorkspace.root,
128
+ targetRoot: targetWorkspace.root,
129
+ trialEmit: (items) => targetPlugin.emit(items, targetWorkspace, { dryRun: true }),
130
+ };
131
+ const result = await transformation.transform(transformContext);
132
+ itemsToEmit = result.items;
133
+ warnings.push(...result.warnings);
122
134
  }
123
- return {
124
- discovered: discovery.items,
125
- written: emission.written,
126
- warnings: allWarnings,
127
- unsupported: emission.unsupported,
128
- };
129
135
  }
130
- // Standard single-pass emit (no rewriting)
131
- const emission = await targetPlugin.emit(discovery.items, effectiveTargetRoot, {
136
+ // Single emission at the end using workspace
137
+ const emission = await targetPlugin.emit(itemsToEmit, targetWorkspace, {
132
138
  dryRun: options.dryRun,
133
139
  });
140
+ warnings.push(...emission.warnings);
134
141
  return {
135
142
  discovered: discovery.items,
136
143
  written: emission.written,
137
- warnings: [...allWarnings, ...emission.warnings],
144
+ warnings,
138
145
  unsupported: emission.unsupported,
139
146
  };
140
147
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEjF;;;GAGG;AACH,MAAM,oBAAoB,GAAiE;IACzF,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,CAAC;QACpE,UAAU,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;KAC5B;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;QAC/C,UAAU,EAAE,CAAC,KAAK,CAAC;KACpB;IACD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,QAAQ,CAAC;QACpB,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;KAC7B;CACF,CAAC;AAqEF;;;GAGG;AACH,MAAM,OAAO,UAAU;IACb,OAAO,GAA4B,IAAI,GAAG,EAAE,CAAC;IAErD;;;OAGG;IACH,YAAY,UAAwB,EAAE;QACpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,MAAkB;QAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnD,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,MAAM,EAAE,SAAkB;SAC3B,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,EAAU;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,IAAY;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,OAA0B;QACtC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEpD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,oEAAoE;QACpE,MAAM,mBAAmB,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;QAC/D,MAAM,mBAAmB,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;QAE/D,uBAAuB;QACvB,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QAEnE,MAAM,WAAW,GAAc,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAEvD,IAAI,OAAO,CAAC,eAAe,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1D,0BAA0B;YAC1B,oEAAoE;YACpE,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,mBAAmB,EAAE;gBAChF,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;YACH,WAAW,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;YAE1C,4DAA4D;YAC5D,MAAM,OAAO,GAAG,YAAY,CAC1B,SAAS,CAAC,KAAK,EACf,WAAW,CAAC,OAAO,EACnB,mBAAmB,EACnB,mBAAmB,CACpB,CAAC;YAEF,sCAAsC;YACtC,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAE/D,2BAA2B;YAC3B,MAAM,cAAc,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5D,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,cAAc,GAAG,aAAa,CAClC,aAAa,CAAC,KAAK,EACnB,OAAO,EACP,cAAc,CAAC,QAAQ,EACvB,cAAc,CAAC,UAAU,CAC1B,CAAC;gBACF,WAAW,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;YACtC,CAAC;YAED,yCAAyC;YACzC,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,mBAAmB,EAAE;gBACjF,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;YACH,2EAA2E;YAC3E,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACpE,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBAClC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;oBACrC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;YAED,OAAO;gBACL,UAAU,EAAE,SAAS,CAAC,KAAK;gBAC3B,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,QAAQ,EAAE,WAAW;gBACrB,WAAW,EAAE,QAAQ,CAAC,WAAW;aAClC,CAAC;QACJ,CAAC;QAED,2CAA2C;QAC3C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,mBAAmB,EAAE;YAC7E,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,OAAO;YACL,UAAU,EAAE,SAAS,CAAC,KAAK;YAC3B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,QAAQ,EAAE,CAAC,GAAG,WAAW,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC;YAChD,WAAW,EAAE,QAAQ,CAAC,WAAW;SAClC,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAGL,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAiF7B;;;GAGG;AACH,MAAM,OAAO,UAAU;IACb,QAAQ,GAAmB,IAAI,cAAc,EAAE,CAAC;IAChD,MAAM,CAAe;IAE7B;;;OAGG;IACH,YAAY,UAAwB,EAAE;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,YAAY,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;QACtE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,cAAc,CAAC,MAAkB,EAAE,SAAkC,SAAS;QAC5E,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,0BAA0B,CAC9B,OAAgC;QAEhC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEzE,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC5B,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjC,CAAC;QAED,OAAO;YACL,UAAU;YACV,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YACjD,MAAM,EAAE,QAAQ,CAAC,MAAM;SACxB,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE;YACf,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI;YACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ;YAC3B,MAAM,EAAE,CAAC,CAAC,MAAM;SACjB,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,EAAU;QAClB,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,eAAmC;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,eAAe,KAAK,QAAQ;YACnD,CAAC,CAAC,IAAI,cAAc,CAAC,UAAU,EAAE,eAAe,CAAC;YACjD,CAAC,CAAC,eAAe,CAAC;QACpB,OAAO,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,OAA0B;QACtC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEpD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,6CAA6C;QAC7C,uEAAuE;QACvE,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;QACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;QAEpD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe;eAC1C,IAAI,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAChD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe;eAC1C,IAAI,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAE5C,uCAAuC;QACvC,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAE/D,mBAAmB;QACnB,MAAM,QAAQ,GAAc,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAEpD,6BAA6B;QAC7B,MAAM,eAAe,GAA4B,OAAO,CAAC,eAAe;YACtE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;YAC9B,CAAC,CAAC,EAAE,CAAC;QAEP,8EAA8E;QAC9E,IAAI,OAAO,CAAC,eAAe,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,gBAAgB,CAAC,EAAE,CAAC;YACvF,eAAe,CAAC,IAAI,CAAC,IAAI,2BAA2B,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,gCAAgC;QAChC,IAAI,WAAW,GAAG,SAAS,CAAC,KAAK,CAAC;QAElC,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;gBAC7C,MAAM,gBAAgB,GAA0B;oBAC9C,KAAK,EAAE,WAAW;oBAClB,YAAY;oBACZ,YAAY;oBACZ,UAAU,EAAE,eAAe,CAAC,IAAI;oBAChC,UAAU,EAAE,eAAe,CAAC,IAAI;oBAChC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CACnB,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;iBAC9D,CAAC;gBAEF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;gBAChE,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAED,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,EAAE;YACrE,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEpC,OAAO;YACL,UAAU,EAAE,SAAS,CAAC,KAAK;YAC3B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,QAAQ;YACR,WAAW,EAAE,QAAQ,CAAC,WAAW;SAClC,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,64 @@
1
+ import type { A16nPlugin } from '@a16njs/models';
2
+ /**
3
+ * Options for controlling where plugin discovery searches.
4
+ */
5
+ export interface PluginDiscoveryOptions {
6
+ /** Custom search paths to scan for plugins. If omitted, uses getDefaultSearchPaths(). */
7
+ searchPaths?: string[];
8
+ }
9
+ /**
10
+ * Result of a plugin discovery scan.
11
+ */
12
+ export interface PluginDiscoveryResult {
13
+ /** Successfully loaded and validated plugins. */
14
+ plugins: A16nPlugin[];
15
+ /** Packages that were found but failed to load or validate. */
16
+ errors: PluginLoadError[];
17
+ }
18
+ /**
19
+ * Describes a plugin package that was found but could not be loaded.
20
+ */
21
+ export interface PluginLoadError {
22
+ /** The npm package name (e.g. "a16n-plugin-foo"). */
23
+ packageName: string;
24
+ /** Human-readable description of what went wrong. */
25
+ error: string;
26
+ }
27
+ /**
28
+ * Scan search paths for installed `a16n-plugin-*` packages, dynamically import
29
+ * each one, validate its default export, and return the valid plugins along
30
+ * with error info for any that failed.
31
+ *
32
+ * @param options - Optional search path overrides
33
+ * @returns Discovered plugins and any load errors
34
+ */
35
+ export declare function discoverInstalledPlugins(options?: PluginDiscoveryOptions): Promise<PluginDiscoveryResult>;
36
+ /**
37
+ * Type-guard that checks whether an unknown value satisfies the A16nPlugin interface.
38
+ *
39
+ * Validates presence and types of: id (string), name (string), supports (array),
40
+ * discover (function), emit (function).
41
+ *
42
+ * @param obj - The value to check
43
+ * @returns True if obj is a valid A16nPlugin
44
+ */
45
+ export declare function isValidPlugin(obj: unknown): obj is A16nPlugin;
46
+ /**
47
+ * Compute the default search paths for plugin discovery.
48
+ *
49
+ * Returns paths where `a16n-plugin-*` packages might be installed:
50
+ * - The global npm `node_modules` directory (derived from this package's location)
51
+ * - The local `node_modules` in the current working directory
52
+ *
53
+ * In a global install, the engine lives inside node_modules:
54
+ * .../lib/node_modules/@a16njs/engine/dist/plugin-discovery.js
55
+ * so walking up finds the node_modules parent directly.
56
+ *
57
+ * In a monorepo (e.g. pnpm workspace), the engine lives at:
58
+ * <root>/packages/engine/dist/plugin-discovery.js
59
+ * so we also check for a node_modules child directory at each level.
60
+ *
61
+ * @returns Array of directory paths to scan
62
+ */
63
+ export declare function getDefaultSearchPaths(): string[];
64
+ //# sourceMappingURL=plugin-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-discovery.d.ts","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,+DAA+D;IAC/D,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;CACf;AAKD;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAyDhC;AAwBD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU,CAY7D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAiChD"}
@@ -0,0 +1,161 @@
1
+ import * as fs from 'fs/promises';
2
+ import { statSync } from 'fs';
3
+ import * as path from 'path';
4
+ import { pathToFileURL, fileURLToPath } from 'url';
5
+ /** Pattern used to match plugin package directory names. */
6
+ const PLUGIN_PREFIX = 'a16n-plugin-';
7
+ /**
8
+ * Scan search paths for installed `a16n-plugin-*` packages, dynamically import
9
+ * each one, validate its default export, and return the valid plugins along
10
+ * with error info for any that failed.
11
+ *
12
+ * @param options - Optional search path overrides
13
+ * @returns Discovered plugins and any load errors
14
+ */
15
+ export async function discoverInstalledPlugins(options) {
16
+ const searchPaths = options?.searchPaths ?? getDefaultSearchPaths();
17
+ const plugins = [];
18
+ const errors = [];
19
+ const seenPluginIds = new Set();
20
+ for (const searchPath of searchPaths) {
21
+ // Read directory entries; skip if path doesn't exist
22
+ let entries;
23
+ try {
24
+ entries = await fs.readdir(searchPath);
25
+ }
26
+ catch {
27
+ // Non-existent or unreadable path — skip silently
28
+ continue;
29
+ }
30
+ // Filter for directories matching the plugin naming convention
31
+ const pluginDirs = entries.filter((name) => name.startsWith(PLUGIN_PREFIX));
32
+ for (const dirName of pluginDirs) {
33
+ const pkgPath = path.join(searchPath, dirName);
34
+ try {
35
+ // Resolve entry point from package.json main field, falling back to index.js
36
+ const entryFile = await resolvePluginEntry(pkgPath);
37
+ const moduleUrl = pathToFileURL(entryFile).href;
38
+ const mod = await import(moduleUrl);
39
+ // Extract default export (handles both `export default` and CJS interop)
40
+ const candidate = mod.default ?? mod;
41
+ if (isValidPlugin(candidate)) {
42
+ if (seenPluginIds.has(candidate.id)) {
43
+ errors.push({
44
+ packageName: dirName,
45
+ error: `Duplicate plugin id: ${candidate.id} — already discovered`,
46
+ });
47
+ }
48
+ else {
49
+ seenPluginIds.add(candidate.id);
50
+ plugins.push(candidate);
51
+ }
52
+ }
53
+ else {
54
+ errors.push({
55
+ packageName: dirName,
56
+ error: `Invalid plugin export: missing or incorrect required fields (id, name, supports, discover, emit)`,
57
+ });
58
+ }
59
+ }
60
+ catch (err) {
61
+ errors.push({
62
+ packageName: dirName,
63
+ error: err instanceof Error ? err.message : String(err),
64
+ });
65
+ }
66
+ }
67
+ }
68
+ return { plugins, errors };
69
+ }
70
+ /**
71
+ * Resolve the JavaScript entry point for a plugin package directory.
72
+ *
73
+ * Reads the `main` field from the package's `package.json` if it exists,
74
+ * otherwise falls back to `index.js` at the package root.
75
+ *
76
+ * @param pkgPath - Absolute path to the plugin package directory
77
+ * @returns Absolute path to the entry JavaScript file
78
+ */
79
+ async function resolvePluginEntry(pkgPath) {
80
+ try {
81
+ const raw = await fs.readFile(path.join(pkgPath, 'package.json'), 'utf-8');
82
+ const pkg = JSON.parse(raw);
83
+ if (typeof pkg.main === 'string' && pkg.main.length > 0) {
84
+ return path.resolve(pkgPath, pkg.main);
85
+ }
86
+ }
87
+ catch {
88
+ // No package.json or unreadable — fall through to default
89
+ }
90
+ return path.join(pkgPath, 'index.js');
91
+ }
92
+ /**
93
+ * Type-guard that checks whether an unknown value satisfies the A16nPlugin interface.
94
+ *
95
+ * Validates presence and types of: id (string), name (string), supports (array),
96
+ * discover (function), emit (function).
97
+ *
98
+ * @param obj - The value to check
99
+ * @returns True if obj is a valid A16nPlugin
100
+ */
101
+ export function isValidPlugin(obj) {
102
+ if (obj == null || typeof obj !== 'object') {
103
+ return false;
104
+ }
105
+ const candidate = obj;
106
+ return (typeof candidate.id === 'string' &&
107
+ typeof candidate.name === 'string' &&
108
+ Array.isArray(candidate.supports) &&
109
+ typeof candidate.discover === 'function' &&
110
+ typeof candidate.emit === 'function');
111
+ }
112
+ /**
113
+ * Compute the default search paths for plugin discovery.
114
+ *
115
+ * Returns paths where `a16n-plugin-*` packages might be installed:
116
+ * - The global npm `node_modules` directory (derived from this package's location)
117
+ * - The local `node_modules` in the current working directory
118
+ *
119
+ * In a global install, the engine lives inside node_modules:
120
+ * .../lib/node_modules/@a16njs/engine/dist/plugin-discovery.js
121
+ * so walking up finds the node_modules parent directly.
122
+ *
123
+ * In a monorepo (e.g. pnpm workspace), the engine lives at:
124
+ * <root>/packages/engine/dist/plugin-discovery.js
125
+ * so we also check for a node_modules child directory at each level.
126
+ *
127
+ * @returns Array of directory paths to scan
128
+ */
129
+ export function getDefaultSearchPaths() {
130
+ const paths = [];
131
+ const thisFile = fileURLToPath(import.meta.url);
132
+ let dir = path.dirname(thisFile);
133
+ while (dir !== path.dirname(dir)) {
134
+ // Global install: this file is inside a node_modules tree
135
+ if (path.basename(dir) === 'node_modules') {
136
+ paths.push(dir);
137
+ break;
138
+ }
139
+ // Monorepo / local dev: check for a sibling node_modules directory.
140
+ // Don't stop — keep walking up to find all ancestor node_modules
141
+ // (e.g. packages/engine/node_modules AND root/node_modules).
142
+ const siblingNodeModules = path.join(dir, 'node_modules');
143
+ try {
144
+ const stat = statSync(siblingNodeModules);
145
+ if (stat.isDirectory()) {
146
+ paths.push(siblingNodeModules);
147
+ }
148
+ }
149
+ catch {
150
+ // Directory doesn't exist, keep walking
151
+ }
152
+ dir = path.dirname(dir);
153
+ }
154
+ // Local node_modules in cwd
155
+ const localNodeModules = path.join(process.cwd(), 'node_modules');
156
+ if (!paths.includes(localNodeModules)) {
157
+ paths.push(localNodeModules);
158
+ }
159
+ return paths;
160
+ }
161
+ //# sourceMappingURL=plugin-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-discovery.js","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"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"}