@pasosdejesus/m 0.16.0 → 0.17.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/m.js CHANGED
@@ -28,6 +28,7 @@ import { cliTranslations } from './i18n/locales/cli.js';
28
28
  import { problem, success } from './util/colors.js';
29
29
  import { parseFlags } from './util/cli-parser.js';
30
30
  import { getPluginCommands } from './plugin.js';
31
+ import { loadExtensions } from './util/extensions.js';
31
32
  // Custom .env loading
32
33
  const CWD = process.cwd();
33
34
  const searchPaths = [
@@ -39,10 +40,52 @@ for (const p of searchPaths) {
39
40
  if (fs.existsSync(p))
40
41
  dotenv.config({ path: p, override: true, quiet: true });
41
42
  }
43
+ /**
44
+ * Auto-discover plugins from package.json.
45
+ * Looks for dependencies matching @pasosdejesus/m-* pattern
46
+ * and imports them (each plugin self-registers via registerCommands).
47
+ */
48
+ async function loadPlugins() {
49
+ const pkgPaths = [
50
+ path.join(CWD, 'package.json'),
51
+ path.join(CWD, '..', 'package.json'),
52
+ path.join(CWD, '..', '..', 'package.json'),
53
+ ];
54
+ let pkg = null;
55
+ for (const p of pkgPaths) {
56
+ if (fs.existsSync(p)) {
57
+ try {
58
+ pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
59
+ break;
60
+ }
61
+ catch { }
62
+ }
63
+ }
64
+ if (!pkg)
65
+ return;
66
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
67
+ for (const [name] of Object.entries(deps)) {
68
+ // Match @pasosdejesus/m-* plugins (e.g., @pasosdejesus/mpdj)
69
+ if (name.startsWith('@pasosdejesus/m') && name !== '@pasosdejesus/m') {
70
+ try {
71
+ await import(name);
72
+ }
73
+ catch {
74
+ // Plugin not installed or not a valid module — skip silently
75
+ }
76
+ }
77
+ // Also support packages with mEngine: true metadata
78
+ // This requires resolving the package.json of the dependency
79
+ }
80
+ }
42
81
  async function main() {
43
82
  const locale = detectLocale();
44
83
  setLocale(locale);
45
84
  const cliT = createTranslator(locale, cliTranslations, commonCli);
85
+ // Auto-discover plugins from package.json
86
+ await loadPlugins();
87
+ // Load m-extensions/ (project-specific hooks)
88
+ await loadExtensions();
46
89
  const commands = {
47
90
  'db:create': { handler: runDbCreate, description: cliT('cmd.db_create_description') },
48
91
  'db:drop': { handler: runDbDrop, description: cliT('cmd.db_drop_description') },
package/dist/plugin.d.ts CHANGED
@@ -7,4 +7,5 @@ export interface CommandDef {
7
7
  }
8
8
  export declare function registerCommands(commands: Record<string, CommandDef>): void;
9
9
  export declare function getPluginCommands(): Record<string, CommandDef>;
10
+ export { registerHook, runHooks } from './util/hooks.js';
10
11
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,CAE3E;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAE9D"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,CAE3E;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAE9D;AAGD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA"}
package/dist/plugin.js CHANGED
@@ -5,3 +5,5 @@ export function registerCommands(commands) {
5
5
  export function getPluginCommands() {
6
6
  return pluginCommands;
7
7
  }
8
+ // Re-export hooks API for m-extensions/
9
+ export { registerHook, runHooks } from './util/hooks.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Extension loader for m — auto-discovers and loads m-extensions/.
3
+ *
4
+ * Searches upward from process.cwd() for a directory named "m-extensions"
5
+ * and imports all .js and .ts files found within.
6
+ *
7
+ * "Y todo lo que hagáis, hacedlo de corazón, como para el Señor y no para los hombres" (Colosenses 3:23)
8
+ */
9
+ /**
10
+ * Load all extension files from m-extensions/ directory.
11
+ * Each file is dynamically imported; its side effects (registerHook calls)
12
+ * register hooks that will fire at the appropriate extension points.
13
+ */
14
+ export declare function loadExtensions(): Promise<void>;
15
+ //# sourceMappingURL=extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../src/util/extensions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwBH;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBpD"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Extension loader for m — auto-discovers and loads m-extensions/.
3
+ *
4
+ * Searches upward from process.cwd() for a directory named "m-extensions"
5
+ * and imports all .js and .ts files found within.
6
+ *
7
+ * "Y todo lo que hagáis, hacedlo de corazón, como para el Señor y no para los hombres" (Colosenses 3:23)
8
+ */
9
+ import { readdir, access as fsAccess } from 'node:fs/promises';
10
+ import { join, dirname } from 'node:path';
11
+ /**
12
+ * Find the nearest m-extensions/ directory by searching upward from cwd.
13
+ */
14
+ async function findExtensionsDir() {
15
+ let dir = process.cwd();
16
+ for (let i = 0; i < 10; i++) {
17
+ const extDir = join(dir, 'm-extensions');
18
+ try {
19
+ await fsAccess(extDir);
20
+ return extDir;
21
+ }
22
+ catch {
23
+ const parent = dirname(dir);
24
+ if (parent === dir)
25
+ break;
26
+ dir = parent;
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ /**
32
+ * Load all extension files from m-extensions/ directory.
33
+ * Each file is dynamically imported; its side effects (registerHook calls)
34
+ * register hooks that will fire at the appropriate extension points.
35
+ */
36
+ export async function loadExtensions() {
37
+ const extDir = await findExtensionsDir();
38
+ if (!extDir)
39
+ return;
40
+ let files;
41
+ try {
42
+ files = await readdir(extDir);
43
+ }
44
+ catch {
45
+ return;
46
+ }
47
+ for (const file of files) {
48
+ if (!file.endsWith('.js') && !file.endsWith('.ts'))
49
+ continue;
50
+ if (file.endsWith('.d.ts'))
51
+ continue;
52
+ try {
53
+ await import(join(extDir, file));
54
+ }
55
+ catch (err) {
56
+ console.error(`[m] Failed to load extension ${file}: ${err.message || err}`);
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Hook system for m extensions.
3
+ *
4
+ * Projects can register hooks to inject logic at specific extension points.
5
+ * Hooks are async callbacks that receive a context object.
6
+ *
7
+ * "Y todo lo que hagáis, hacedlo de corazón, como para el Señor y no para los hombres" (Colosenses 3:23)
8
+ */
9
+ /**
10
+ * Register a hook callback for a named extension point.
11
+ * Safe to call multiple times — callbacks are appended.
12
+ */
13
+ export declare function registerHook(name: string, callback: (ctx: any) => Promise<void>): void;
14
+ /**
15
+ * Run all registered hooks for a named extension point.
16
+ * Callbacks are executed in series (order of registration).
17
+ * Errors are caught and logged; they do not stop subsequent hooks.
18
+ */
19
+ export declare function runHooks(name: string, ctx: any): Promise<void>;
20
+ /**
21
+ * Clear all registered hooks (for testing).
22
+ */
23
+ export declare function clearHooks(): void;
24
+ /**
25
+ * Get hook names and callback counts (for debugging/testing).
26
+ */
27
+ export declare function debugHooks(): Record<string, number>;
28
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/util/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAKtF;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAWpE;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMnD"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Hook system for m extensions.
3
+ *
4
+ * Projects can register hooks to inject logic at specific extension points.
5
+ * Hooks are async callbacks that receive a context object.
6
+ *
7
+ * "Y todo lo que hagáis, hacedlo de corazón, como para el Señor y no para los hombres" (Colosenses 3:23)
8
+ */
9
+ const hooks = new Map();
10
+ /**
11
+ * Register a hook callback for a named extension point.
12
+ * Safe to call multiple times — callbacks are appended.
13
+ */
14
+ export function registerHook(name, callback) {
15
+ if (!hooks.has(name)) {
16
+ hooks.set(name, []);
17
+ }
18
+ hooks.get(name).push(callback);
19
+ }
20
+ /**
21
+ * Run all registered hooks for a named extension point.
22
+ * Callbacks are executed in series (order of registration).
23
+ * Errors are caught and logged; they do not stop subsequent hooks.
24
+ */
25
+ export async function runHooks(name, ctx) {
26
+ const callbacks = hooks.get(name);
27
+ if (!callbacks || callbacks.length === 0)
28
+ return;
29
+ for (const cb of callbacks) {
30
+ try {
31
+ await cb(ctx);
32
+ }
33
+ catch (err) {
34
+ console.error(`[m] Hook "${name}" failed: ${err.message || err}`);
35
+ }
36
+ }
37
+ }
38
+ /**
39
+ * Clear all registered hooks (for testing).
40
+ */
41
+ export function clearHooks() {
42
+ hooks.clear();
43
+ }
44
+ /**
45
+ * Get hook names and callback counts (for debugging/testing).
46
+ */
47
+ export function debugHooks() {
48
+ const out = {};
49
+ for (const [name, cbs] of hooks) {
50
+ out[name] = cbs.length;
51
+ }
52
+ return out;
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasosdejesus/m",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Motor para crear y gestionar proyectos al estilo Pasos de Jesus.",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -125,8 +125,8 @@
125
125
  "picocolors": "^1.1.1"
126
126
  },
127
127
  "optionalDependencies": {
128
- "@pasosdejesus/edr": "0.1.0",
129
- "@pasosdejesus/contract-test": "0.1.0"
128
+ "@pasosdejesus/edr": "0.1.1",
129
+ "@pasosdejesus/contract-test": "0.1.1"
130
130
  },
131
131
  "devDependencies": {
132
132
  "@radix-ui/react-accordion": "^1.2.12",