@celilo/cli 0.2.1 → 0.3.1
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/package.json +11 -4
- package/src/cli/command-registry.ts +114 -0
- package/src/cli/commands/events.test.ts +156 -0
- package/src/cli/commands/events.ts +356 -0
- package/src/cli/commands/module-remove.ts +14 -0
- package/src/cli/index.ts +103 -0
- package/src/config/paths.ts +20 -0
- package/src/manifest/schema.ts +34 -0
- package/src/manifest/validate.test.ts +75 -0
- package/src/module/import.ts +45 -5
- package/src/services/celilo-events.test.ts +98 -0
- package/src/services/celilo-events.ts +104 -0
- package/src/services/events-daemon.test.ts +184 -0
- package/src/services/events-daemon.ts +244 -0
- package/src/services/module-deploy.ts +51 -0
- package/src/services/module-subscriptions.test.ts +197 -0
- package/src/services/module-subscriptions.ts +120 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire a module's manifest `subscriptions:` block into the SQLite event
|
|
3
|
+
* bus. Called from `module/import.ts` on install and from
|
|
4
|
+
* `cli/commands/module-remove.ts` on remove.
|
|
5
|
+
*
|
|
6
|
+
* Substitutions performed at subscribe time:
|
|
7
|
+
* - `$self` in `pattern` → the module's id
|
|
8
|
+
* - `${MODULE_PATH}` in `handler` → the module's installed targetPath
|
|
9
|
+
*
|
|
10
|
+
* The bus subscriber's name is namespaced as `<module-id>.<sub-name>`
|
|
11
|
+
* so two modules can declare a subscription named `smoke` without
|
|
12
|
+
* colliding.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { defineEvents, openBus } from '@celilo/event-bus';
|
|
16
|
+
import { getEventBusPath } from '../config/paths';
|
|
17
|
+
import type { ModuleManifest, ModuleSubscription } from '../manifest/schema';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The bus is opened by the celilo CLI without an event registry — the
|
|
21
|
+
* CLI doesn't know the schemas of every module's events. The bus's
|
|
22
|
+
* empty-registry path skips payload validation, leaving that to the
|
|
23
|
+
* linked handlers (which open the bus *with* their own registry).
|
|
24
|
+
*/
|
|
25
|
+
const NO_SCHEMAS = defineEvents({});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the per-module substitutions on a single subscription. Pure
|
|
29
|
+
* function: takes a parsed manifest entry, returns the bus-shaped
|
|
30
|
+
* subscribe options.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveSubscription(
|
|
33
|
+
sub: ModuleSubscription,
|
|
34
|
+
moduleId: string,
|
|
35
|
+
modulePath: string,
|
|
36
|
+
): {
|
|
37
|
+
name: string;
|
|
38
|
+
pattern: string;
|
|
39
|
+
handler: string;
|
|
40
|
+
maxAttempts?: number;
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
registeredBy: string;
|
|
43
|
+
} {
|
|
44
|
+
return {
|
|
45
|
+
name: scopedName(moduleId, sub.name),
|
|
46
|
+
pattern: substituteSelf(sub.pattern, moduleId),
|
|
47
|
+
handler: substituteModulePath(sub.handler, modulePath),
|
|
48
|
+
maxAttempts: sub.max_attempts,
|
|
49
|
+
timeoutMs: sub.timeout_ms,
|
|
50
|
+
registeredBy: moduleId,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Register all of a module's subscriptions on the bus. Idempotent —
|
|
56
|
+
* re-running with the same manifest updates existing rows in place.
|
|
57
|
+
*
|
|
58
|
+
* If the module's manifest declares no subscriptions, this is a
|
|
59
|
+
* cheap no-op (the bus DB isn't even touched).
|
|
60
|
+
*/
|
|
61
|
+
export function registerModuleSubscriptions(
|
|
62
|
+
manifest: ModuleManifest,
|
|
63
|
+
modulePath: string,
|
|
64
|
+
): { registered: number } {
|
|
65
|
+
const subs = manifest.subscriptions ?? [];
|
|
66
|
+
if (subs.length === 0) return { registered: 0 };
|
|
67
|
+
|
|
68
|
+
const bus = openBus({ dbPath: getEventBusPath(), events: NO_SCHEMAS });
|
|
69
|
+
try {
|
|
70
|
+
for (const sub of subs) {
|
|
71
|
+
const resolved = resolveSubscription(sub, manifest.id, modulePath);
|
|
72
|
+
bus.subscribe(resolved);
|
|
73
|
+
}
|
|
74
|
+
return { registered: subs.length };
|
|
75
|
+
} finally {
|
|
76
|
+
bus.close();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Tear down every bus subscription that belongs to this module. Looks
|
|
82
|
+
* up rows by name prefix `<module-id>.` rather than rereading the
|
|
83
|
+
* old manifest, so a stale subscription left behind by a manifest
|
|
84
|
+
* change still gets cleaned up.
|
|
85
|
+
*
|
|
86
|
+
* Best-effort: if the bus DB doesn't exist (never opened), returns 0.
|
|
87
|
+
*/
|
|
88
|
+
export function unregisterModuleSubscriptions(moduleId: string): {
|
|
89
|
+
unregistered: number;
|
|
90
|
+
} {
|
|
91
|
+
const bus = openBus({ dbPath: getEventBusPath(), events: NO_SCHEMAS });
|
|
92
|
+
try {
|
|
93
|
+
const likePattern = `${moduleId}.%`;
|
|
94
|
+
const rows = bus.db
|
|
95
|
+
.query<{ name: string }, [string]>('SELECT name FROM subscribers WHERE name LIKE ?')
|
|
96
|
+
.all(likePattern);
|
|
97
|
+
for (const row of rows) {
|
|
98
|
+
bus.unsubscribe(row.name);
|
|
99
|
+
}
|
|
100
|
+
return { unregistered: rows.length };
|
|
101
|
+
} finally {
|
|
102
|
+
bus.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function scopedName(moduleId: string, subName: string): string {
|
|
107
|
+
return `${moduleId}.${subName}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function substituteSelf(pattern: string, moduleId: string): string {
|
|
111
|
+
// `$self` matches when followed by a dot or end-of-string, so a
|
|
112
|
+
// pattern like `deploy.$self.foo` substitutes correctly without
|
|
113
|
+
// confusing `$selfish` if anyone wrote that. (No real reason they
|
|
114
|
+
// would, but be precise.)
|
|
115
|
+
return pattern.replace(/\$self(?=\.|$)/g, moduleId);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function substituteModulePath(handler: string, modulePath: string): string {
|
|
119
|
+
return handler.replace(/\$\{MODULE_PATH\}/g, modulePath);
|
|
120
|
+
}
|