@oktis-works/plugin-runtime 0.5.5 → 0.5.7

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.
@@ -0,0 +1,74 @@
1
+ import { type HookRegistry } from './hook-registry.js';
2
+ import type { PluginApiDependencies, RouteDefinition } from './plugin-api.js';
3
+ export interface BootstrapPluginsSql {
4
+ unsafe(text: string, params?: unknown[]): Promise<unknown>;
5
+ }
6
+ export interface BootstrapPluginsOptions {
7
+ /** Diretório de plugins (o chamador resolve — plugin-runtime não conhece o core). */
8
+ pluginsDir: string;
9
+ /** Cliente postgres para ler os habilitados; omitido = estado desconhecido (no-op). */
10
+ sql?: BootstrapPluginsSql;
11
+ /** Registry alvo (default: registry do escopo — platform ou `tenantId`). */
12
+ registry?: HookRegistry;
13
+ /** Statuses considerados habilitados (default: ACTIVATED, RUNNING). */
14
+ enabledStatuses?: string[];
15
+ /**
16
+ * Escopo de carga: `null`/omitido → platform (`plugins.tenant_id IS NULL`);
17
+ * string → tenant (`plugin_tenants` com status ENABLED).
18
+ */
19
+ tenantId?: string | null;
20
+ /**
21
+ * Política de permissões concedidas (allowlist). Default: catálogo completo
22
+ * de permissões do CMS. Nunca é preenchida com as permissões do manifesto.
23
+ */
24
+ permissionPolicy?: string[];
25
+ /** Timeout de callbacks de hook em ms (0 = sem timeout). Default 5000. */
26
+ hookTimeoutMs?: number;
27
+ /**
28
+ * Dependências reais para a PluginAPI (settings, eventBus, db, etc.).
29
+ * Se omitido, usa apenas o Hooks API legado (compatibilidade).
30
+ * O core injeta isto com serviços reais.
31
+ */
32
+ apiDeps?: Partial<PluginApiDependencies>;
33
+ }
34
+ export interface PluginBootstrapResult {
35
+ /** Nomes habilitados no banco (vazio quando o banco falhou = estado desconhecido). */
36
+ enabled: string[];
37
+ /** Registrados nesta sincronização. */
38
+ loaded: string[];
39
+ /** Hooks removidos nesta sincronização (saíram dos habilitados). */
40
+ unloaded: string[];
41
+ /** Falhas isoladas por plugin (nunca abortam o lote). */
42
+ failed: Array<{
43
+ name: string;
44
+ error: string;
45
+ }>;
46
+ }
47
+ /** Escopo com carga registrada (para inspeção/estado de runtime). */
48
+ export declare function listLoadedScopes(): Array<{
49
+ scope: string;
50
+ registry: HookRegistry;
51
+ plugins: string[];
52
+ }>;
53
+ /** Obtém as rotas registradas pelos plugins em um escopo. */
54
+ export declare function getLoadedPluginRoutes(scopeKey: string): Array<{
55
+ pluginName: string;
56
+ routes: RouteDefinition[];
57
+ }>;
58
+ /**
59
+ * Remove todos os hooks dos escopos carregados e limpa o estado.
60
+ * Útil em testes e em shutdown. Quando `registry` é informado, os hooks são
61
+ * removidos dele; caso contrário, do registry em que foram registrados.
62
+ */
63
+ export declare function resetLoadedPlugins(registry?: HookRegistry): void;
64
+ /** Descarrega todos os plugins de um escopo (tenant desativado / LRU). */
65
+ export declare function unloadScope(scopeKey: string): boolean;
66
+ /** Descarrega um tenant (chave `tenant:<id>` ou o próprio id). */
67
+ export declare function unloadTenant(tenantId: string): boolean;
68
+ /**
69
+ * Sincroniza o registry de um escopo com os plugins habilitados no banco.
70
+ * Seguro para chamar no boot de qualquer processo e em re-syncs (ex.: logo
71
+ * após ativar/desativar pela API).
72
+ */
73
+ export declare function bootstrapPlugins(options: BootstrapPluginsOptions): Promise<PluginBootstrapResult>;
74
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAyC,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9F,OAAO,KAAK,EAAE,qBAAqB,EAAqB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEjG,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,uBAAuB;IACtC,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,GAAG,CAAC,EAAE,mBAAmB,CAAC;IAC1B,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,qBAAqB;IACpC,sFAAsF;IACtF,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uCAAuC;IACvC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAoCD,qEAAqE;AACrE,wBAAgB,gBAAgB,IAAI,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAMtG;AAED,6DAA6D;AAC7D,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAAC,CAahH;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAWhE;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWrD;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEtD;AA6DD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA4HvG"}
@@ -0,0 +1,272 @@
1
+ // @oktis-works/plugin-runtime - Bootstrap de plugins em runtime
2
+ //
3
+ // O PLUGIN.md promete: "`register(registry)` é chamado quando o plugin é
4
+ // carregado". Este módulo cumpre a promessa: descobre os plugins habilitados
5
+ // no banco (escopo **platform** = `tenant_id IS NULL`; escopo **tenant** =
6
+ // `plugin_tenants` habilitados), importa o entrypoint, chama
7
+ // `register(registry)` com uma API de hooks atribuída ao plugin (BUSI-032/033)
8
+ // e sincroniza o registry do escopo.
9
+ //
10
+ // Regras:
11
+ // - **nunca lança**: plugin quebrado, manifesto inválido, incompatibilidade
12
+ // semântica ou banco indisponível não derrubam o boot (isolamento de falha);
13
+ // - **sincroniza com o banco**: quem saiu da lista de habilitados tem os
14
+ // hooks removidos do registry (unload);
15
+ // - **idempotente**: plugin já carregado não é recarregado (sem hooks
16
+ // duplicados) — chamadas repetidas carregam só o que é novo;
17
+ // - **por escopo**: o estado de carga é indexado por escopo (platform ou um
18
+ // tenant), então ativar/desativar num tenant não afeta outro.
19
+ import { createPluginHooksApi } from '@oktis-works/plugin-sdk';
20
+ import { PLUGIN_PERMISSION_DEFAULT } from '@oktis-works/validation';
21
+ import { PluginLoader } from './loader.js';
22
+ import { PluginSandbox } from './sandbox.js';
23
+ import { getHookRegistry, getHookRegistryStore } from './hook-registry.js';
24
+ /** Estado de carga por escopo (`''` = platform, `tenant:<id>` = tenant). */
25
+ const loadedScopes = new Map();
26
+ function scopeKeyOf(tenantId) {
27
+ return tenantId ? `tenant:${tenantId}` : '';
28
+ }
29
+ function getScope(scopeKey, registry) {
30
+ let scope = loadedScopes.get(scopeKey);
31
+ if (!scope) {
32
+ scope = { registry, plugins: new Map() };
33
+ loadedScopes.set(scopeKey, scope);
34
+ }
35
+ return scope;
36
+ }
37
+ /** Escopo com carga registrada (para inspeção/estado de runtime). */
38
+ export function listLoadedScopes() {
39
+ return [...loadedScopes.entries()].map(([scope, entry]) => ({
40
+ scope,
41
+ registry: entry.registry,
42
+ plugins: [...entry.plugins.keys()],
43
+ }));
44
+ }
45
+ /** Obtém as rotas registradas pelos plugins em um escopo. */
46
+ export function getLoadedPluginRoutes(scopeKey) {
47
+ const scope = loadedScopes.get(scopeKey);
48
+ if (!scope)
49
+ return [];
50
+ const result = [];
51
+ for (const [pluginName, entry] of scope.plugins) {
52
+ if (entry.internal) {
53
+ const routes = entry.internal.getRoutes();
54
+ if (routes.length > 0) {
55
+ result.push({ pluginName, routes });
56
+ }
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+ /**
62
+ * Remove todos os hooks dos escopos carregados e limpa o estado.
63
+ * Útil em testes e em shutdown. Quando `registry` é informado, os hooks são
64
+ * removidos dele; caso contrário, do registry em que foram registrados.
65
+ */
66
+ export function resetLoadedPlugins(registry) {
67
+ for (const [scopeKey, scope] of loadedScopes) {
68
+ const target = registry ?? scope.registry;
69
+ for (const entry of scope.plugins.values()) {
70
+ for (const reg of entry.registrations) {
71
+ if (reg.type === 'action')
72
+ target.removeAction(reg.name, reg.callback, reg.priority);
73
+ else
74
+ target.removeFilter(reg.name, reg.callback, reg.priority);
75
+ }
76
+ }
77
+ loadedScopes.delete(scopeKey);
78
+ }
79
+ }
80
+ /** Descarrega todos os plugins de um escopo (tenant desativado / LRU). */
81
+ export function unloadScope(scopeKey) {
82
+ const scope = loadedScopes.get(scopeKey);
83
+ if (!scope)
84
+ return false;
85
+ for (const entry of scope.plugins.values()) {
86
+ for (const reg of entry.registrations) {
87
+ if (reg.type === 'action')
88
+ scope.registry.removeAction(reg.name, reg.callback, reg.priority);
89
+ else
90
+ scope.registry.removeFilter(reg.name, reg.callback, reg.priority);
91
+ }
92
+ }
93
+ loadedScopes.delete(scopeKey);
94
+ return true;
95
+ }
96
+ /** Descarrega um tenant (chave `tenant:<id>` ou o próprio id). */
97
+ export function unloadTenant(tenantId) {
98
+ return unloadScope(scopeKeyOf(tenantId));
99
+ }
100
+ function unloadPlugin(scope, name) {
101
+ const entry = scope.plugins.get(name);
102
+ if (!entry)
103
+ return false;
104
+ for (const reg of entry.registrations) {
105
+ if (reg.type === 'action')
106
+ scope.registry.removeAction(reg.name, reg.callback, reg.priority);
107
+ else
108
+ scope.registry.removeFilter(reg.name, reg.callback, reg.priority);
109
+ }
110
+ scope.plugins.delete(name);
111
+ return true;
112
+ }
113
+ /**
114
+ * Registry que grava cada registro num sink (para poder descarregar depois),
115
+ * delega ao registry real com `sourceId` atribuído pelo SDK e **aplica o
116
+ * timeout do sandbox** no callback registrado (WP5 — proteção real contra hook
117
+ * pendurado; documentada como limitada a trabalho assíncrono).
118
+ */
119
+ function recordingRegistry(registry, sink, sandbox) {
120
+ const acceptedArgsOf = (callback, explicit) => explicit ?? callback.length;
121
+ return {
122
+ addAction(action, callback, options) {
123
+ const wrapped = sandbox.wrapCallback(callback);
124
+ sink.push({ type: 'action', name: action, callback: wrapped, priority: options?.priority ?? 10 });
125
+ registry.addAction(action, wrapped, { ...options, acceptedArgs: acceptedArgsOf(callback, options?.acceptedArgs) });
126
+ },
127
+ addFilter(filter, callback, options) {
128
+ const wrapped = sandbox.wrapCallback(callback);
129
+ sink.push({ type: 'filter', name: filter, callback: wrapped, priority: options?.priority ?? 10 });
130
+ registry.addFilter(filter, wrapped, { ...options, acceptedArgs: acceptedArgsOf(callback, options?.acceptedArgs) });
131
+ },
132
+ removeAction: (action, callback, priority) => registry.removeAction(action, callback, priority),
133
+ removeFilter: (action, callback, priority) => registry.removeFilter(action, callback, priority),
134
+ hasAction: (action, callback) => registry.hasAction(action, callback),
135
+ hasFilter: (action, callback) => registry.hasFilter(action, callback),
136
+ didAction: (action) => registry.didAction(action),
137
+ getCurrentHook: () => registry.getCurrentHook(),
138
+ };
139
+ }
140
+ /** Query de habilitados por escopo. */
141
+ function enabledQuery(tenantId) {
142
+ if (tenantId) {
143
+ return {
144
+ text: `SELECT p.name FROM plugins p
145
+ JOIN plugin_tenants pt ON pt.plugin_id = p.id
146
+ WHERE pt.tenant_id = $1 AND pt.status = 'ENABLED'
147
+ AND p.status NOT IN ('UNINSTALLED', 'MISSING')`,
148
+ params: [tenantId],
149
+ };
150
+ }
151
+ return { text: 'SELECT name FROM plugins WHERE status = ANY($1) AND tenant_id IS NULL' };
152
+ }
153
+ /**
154
+ * Sincroniza o registry de um escopo com os plugins habilitados no banco.
155
+ * Seguro para chamar no boot de qualquer processo e em re-syncs (ex.: logo
156
+ * após ativar/desativar pela API).
157
+ */
158
+ export async function bootstrapPlugins(options) {
159
+ const scopeKey = scopeKeyOf(options.tenantId);
160
+ const registry = options.registry ??
161
+ (options.tenantId ? getHookRegistryStore().tenant(options.tenantId) : getHookRegistry());
162
+ const statuses = options.enabledStatuses ?? ['ACTIVATED', 'RUNNING'];
163
+ const result = { enabled: [], loaded: [], unloaded: [], failed: [] };
164
+ // 1) Quem está habilitado no banco? Falha = estado desconhecido → não toca em nada.
165
+ let enabled = null;
166
+ if (options.sql) {
167
+ try {
168
+ const query = enabledQuery(options.tenantId);
169
+ const rows = (await options.sql.unsafe(query.text, options.tenantId ? query.params : [statuses]));
170
+ enabled = rows
171
+ .map((row) => row.name)
172
+ .filter((name) => typeof name === 'string' && name.length > 0);
173
+ }
174
+ catch {
175
+ enabled = null;
176
+ }
177
+ }
178
+ result.enabled = enabled ?? [];
179
+ if (enabled === null)
180
+ return result;
181
+ // 2) Descobre no diretório (diretório ausente = nenhum plugin, não é erro)
182
+ const loader = new PluginLoader(options.pluginsDir);
183
+ let discovered = [];
184
+ try {
185
+ discovered = await loader.discoverPlugins();
186
+ }
187
+ catch {
188
+ discovered = [];
189
+ }
190
+ const scope = getScope(scopeKey, registry);
191
+ // 3) Descarrega quem saiu da lista de habilitados (deste escopo)
192
+ for (const name of [...scope.plugins.keys()]) {
193
+ if (!enabled.includes(name) && unloadPlugin(scope, name)) {
194
+ result.unloaded.push(name);
195
+ }
196
+ }
197
+ // 4) Carrega habilitados ainda não carregados, respeitando ordem de dependência
198
+ const enabledDiscovered = discovered.filter((entry) => enabled.includes(entry.manifest.name));
199
+ let levels;
200
+ try {
201
+ levels = loader.resolveDependencies(enabledDiscovered);
202
+ }
203
+ catch {
204
+ levels = enabledDiscovered.map((entry) => [entry]); // ordenação falhou: ordem arbitrária
205
+ }
206
+ // Política de permissões: allowlist de configuração (ou catálogo completo).
207
+ const policy = options.permissionPolicy ?? PLUGIN_PERMISSION_DEFAULT;
208
+ const timeoutMs = options.hookTimeoutMs ?? 5000;
209
+ for (const level of levels) {
210
+ for (const entry of level) {
211
+ const name = entry.manifest.name;
212
+ if (scope.plugins.has(name))
213
+ continue;
214
+ try {
215
+ const loaded = await loader.loadPlugin(entry.manifest, entry.path);
216
+ // O sandbox valida o manifesto contra a POLÍTICA (nunca contra si mesmo)
217
+ const sandbox = new PluginSandbox({ allowedPermissions: [...policy], timeoutMs });
218
+ sandbox.loadPlugin(entry.manifest, loaded.api);
219
+ // Contrato do PLUGIN.md: module.exports.register(registry)
220
+ const register = loaded.api.register;
221
+ if (typeof register !== 'function') {
222
+ throw new Error('não exporta register(registry)');
223
+ }
224
+ const registrations = [];
225
+ // Se o core forneceu apiDeps, cria a API completa (hooks + settings + query + routes + ...)
226
+ // Caso contrário, usa apenas o Hooks API legado (compatibilidade).
227
+ let apiToPass;
228
+ let internal;
229
+ if (options.apiDeps) {
230
+ // Mescla os deps do core com os específicos do plugin/runtime
231
+ const deps = {
232
+ hooks: recordingRegistry(registry, registrations, sandbox),
233
+ manifest: entry.manifest,
234
+ settings: options.apiDeps.settings ?? { get: async () => undefined, set: async () => { } },
235
+ eventBus: options.apiDeps.eventBus ?? { emit: async () => { } },
236
+ db: options.apiDeps.db ?? { query: async () => [] },
237
+ getTenantId: options.apiDeps.getTenantId ?? (() => options.tenantId ?? null),
238
+ getUserId: options.apiDeps.getUserId ?? (() => null),
239
+ getRequestId: options.apiDeps.getRequestId ?? (() => crypto.randomUUID?.() ?? Math.random().toString(36).slice(2)),
240
+ getTenantById: options.apiDeps.getTenantById ?? (async () => null),
241
+ getUserById: options.apiDeps.getUserById ?? (async () => null),
242
+ registerRoutes: options.apiDeps.registerRoutes ?? (() => { }),
243
+ registerContentFields: options.apiDeps.registerContentFields ?? (() => { }),
244
+ allowedPermissions: [...policy],
245
+ };
246
+ // Cria a API completa usando a fábrica interna
247
+ const { createPluginApi } = await import('./plugin-api.js');
248
+ const apiResult = createPluginApi(deps);
249
+ apiToPass = apiResult.api;
250
+ internal = apiResult.internal;
251
+ }
252
+ else {
253
+ apiToPass = createPluginHooksApi(name, recordingRegistry(registry, registrations, sandbox));
254
+ }
255
+ register(apiToPass);
256
+ if (internal) {
257
+ scope.plugins.set(name, { registrations, internal });
258
+ }
259
+ else {
260
+ scope.plugins.set(name, { registrations });
261
+ }
262
+ result.loaded.push(name);
263
+ }
264
+ catch (error) {
265
+ // Isolamento de falha: plugin quebrado não impede os demais nem o boot
266
+ result.failed.push({ name, error: error instanceof Error ? error.message : String(error) });
267
+ }
268
+ }
269
+ }
270
+ return result;
271
+ }
272
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,27 @@ export declare class HookRegistry {
25
25
  private readonly auditLog;
26
26
  private devMode;
27
27
  private trustLevels;
28
+ /** Nomes de hooks oficiais (catálogo do core) — sempre aceitos em dev. */
29
+ private readonly knownHooks;
30
+ /** Prefixos de hooks oficiais parametrizados (terminam em '*'). */
31
+ private readonly knownPrefixes;
28
32
  setDevMode(enabled: boolean): void;
33
+ /**
34
+ * Registra o catálogo de hooks oficiais do core. Em modo dev a regra de
35
+ * prefixo (`okcms.`/`plugin.`) continua exigida para hooks customizados,
36
+ * mas os nomes oficiais (`content:query`, `theme:data:*`, ...) são
37
+ * aceitos — sem isso, o próprio catálogo do core é reprovado em dev.
38
+ *
39
+ * Entradas terminadas em `*` viram prefixos (ex.: `theme:data:*`,
40
+ * `manage:*`, `before_publish_content_*`).
41
+ *
42
+ * O runtime não depende do core (seria ciclo): quem conhece o catálogo é
43
+ * o chamador (bootstrap do core) e injeta aqui.
44
+ */
45
+ setKnownHooks(names: Iterable<string>): void;
46
+ clearKnownHooks(): void;
47
+ /** True quando o nome pertence ao catálogo oficial (exato ou por prefixo). */
48
+ isKnownHook(action: string): boolean;
29
49
  setTrustLevel(sourceId: string, level: number): void;
30
50
  /**
31
51
  * Catálogo de hooks registrados (REQ-REQU-035-002): lista cada hook com tipo,
@@ -74,9 +94,48 @@ export declare class HookRegistry {
74
94
  private removeEntry;
75
95
  private hasEntry;
76
96
  }
97
+ export declare function setHookTenantResolver(resolver: () => string | null): void;
98
+ export declare function getCurrentHookTenantId(): string | null;
99
+ /**
100
+ * Store de registries por tenant (isolamento de plugins por tenant).
101
+ *
102
+ * - `platform`: hooks do core (`okcms.*`) — compartilhado por todo o processo;
103
+ * - `tenant(tenantId)`: hooks dos plugins habilitados daquele tenant, criado
104
+ * sob demanda e descarregável (invalidate/drop).
105
+ *
106
+ * O contrato é `tenant(id)` para poder evoluir para vm/worker por tenant sem
107
+ * mudar quem chama (`getHookRegistry()` continua síncrono).
108
+ */
109
+ export declare class HookRegistryStore {
110
+ private platformRegistry;
111
+ private readonly byTenant;
112
+ constructor(platform?: HookRegistry);
113
+ platform(): HookRegistry;
114
+ setPlatform(registry: HookRegistry): void;
115
+ /** Registry do tenant (criado sob demanda com devMode + catálogo do core). */
116
+ tenant(tenantId: string): HookRegistry;
117
+ hasTenant(tenantId: string): boolean;
118
+ tenantIds(): string[];
119
+ /**
120
+ * Descarrega os hooks de um tenant (ativação/desativação, LRU, shutdown).
121
+ * Retorna true quando havia um registry carregado.
122
+ */
123
+ invalidateTenant(tenantId: string): boolean;
124
+ clearTenants(): void;
125
+ /** Propaga o catálogo do core para todos os registries (atuais e futuros). */
126
+ setKnownHooks(names: Iterable<string>): void;
127
+ get size(): number;
128
+ }
129
+ export declare function getHookRegistryStore(): HookRegistryStore;
130
+ /**
131
+ * Registry do escopo atual: tenant quando há contexto de tenant (plugins por
132
+ * tenant), platform quando não há (hooks do core).
133
+ */
77
134
  export declare function getHookRegistry(): HookRegistry;
78
135
  /**
79
- * Substitui o registry global. Uso interno: testes e hot-reload de plugins.
136
+ * Substitui o registry platform. Uso interno: testes e hot-reload de plugins.
80
137
  */
81
138
  export declare function setHookRegistry(registry: HookRegistry): void;
139
+ /** Troca o store inteiro (testes). */
140
+ export declare function setHookRegistryStore(store: HookRegistryStore | null): void;
82
141
  //# sourceMappingURL=hook-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hook-registry.d.ts","sourceRoot":"","sources":["../src/hook-registry.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,CAAC,KAAK,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC;AAY5F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,gEAAgE;IAChE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,cAAc,GAAG,eAAe,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,qBAAa,YAAY;IACvB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+C;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+C;IACvE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAC7D,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAA6B;IAEhD,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpD;;;OAGG;IACH,cAAc,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;QAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IAoBF,WAAW,IAAI,aAAa,EAAE;IAI9B,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,GAAE,cAAmB,GAAG,IAAI;IAqBrF;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,cAAmB,GAAG,IAAI;IAqB9G,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAIhF,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAIhF,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO;IAI3D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO;IAI3D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAIjC,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjE;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO;IAqBzE;;;OAGG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAqB7F,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,qBAAqB;IA2B7B,OAAO,CAAC,WAAW;IA+BnB,OAAO,CAAC,QAAQ;CAWjB;AAID,wBAAgB,eAAe,IAAI,YAAY,CAM9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAE5D"}
1
+ {"version":3,"file":"hook-registry.d.ts","sourceRoot":"","sources":["../src/hook-registry.ts"],"names":[],"mappings":"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"}
@@ -13,9 +13,43 @@ export class HookRegistry {
13
13
  auditLog = [];
14
14
  devMode = false;
15
15
  trustLevels = new Map();
16
+ /** Nomes de hooks oficiais (catálogo do core) — sempre aceitos em dev. */
17
+ knownHooks = new Set();
18
+ /** Prefixos de hooks oficiais parametrizados (terminam em '*'). */
19
+ knownPrefixes = [];
16
20
  setDevMode(enabled) {
17
21
  this.devMode = enabled;
18
22
  }
23
+ /**
24
+ * Registra o catálogo de hooks oficiais do core. Em modo dev a regra de
25
+ * prefixo (`okcms.`/`plugin.`) continua exigida para hooks customizados,
26
+ * mas os nomes oficiais (`content:query`, `theme:data:*`, ...) são
27
+ * aceitos — sem isso, o próprio catálogo do core é reprovado em dev.
28
+ *
29
+ * Entradas terminadas em `*` viram prefixos (ex.: `theme:data:*`,
30
+ * `manage:*`, `before_publish_content_*`).
31
+ *
32
+ * O runtime não depende do core (seria ciclo): quem conhece o catálogo é
33
+ * o chamador (bootstrap do core) e injeta aqui.
34
+ */
35
+ setKnownHooks(names) {
36
+ for (const name of names) {
37
+ if (name.endsWith('*'))
38
+ this.knownPrefixes.push(name.slice(0, -1));
39
+ else
40
+ this.knownHooks.add(name);
41
+ }
42
+ }
43
+ clearKnownHooks() {
44
+ this.knownHooks.clear();
45
+ this.knownPrefixes.length = 0;
46
+ }
47
+ /** True quando o nome pertence ao catálogo oficial (exato ou por prefixo). */
48
+ isKnownHook(action) {
49
+ if (this.knownHooks.has(action))
50
+ return true;
51
+ return this.knownPrefixes.some((prefix) => action.startsWith(prefix));
52
+ }
19
53
  setTrustLevel(sourceId, level) {
20
54
  this.trustLevels.set(sourceId, level);
21
55
  }
@@ -185,8 +219,8 @@ export class HookRegistry {
185
219
  }
186
220
  authorizeRegistration(action, options) {
187
221
  const sourceId = options.sourceId ?? 'anonymous';
188
- if (this.devMode && !isPrefixed(action)) {
189
- throw new Error(`[hooks] Em modo dev, hooks customizados devem ser prefixados ("${CORE_PREFIX}..." ou "plugin.<id>...."). Recebido: "${action}"`);
222
+ if (this.devMode && !isPrefixed(action) && !this.isKnownHook(action)) {
223
+ throw new Error(`[hooks] Em modo dev, hooks customizados devem ser prefixados ("${CORE_PREFIX}..." ou "plugin.<id>....") ou pertencer ao catálogo oficial. Recebido: "${action}"`);
190
224
  }
191
225
  const requiredLevel = options.requiredTrustLevel ?? 0;
192
226
  const sourceLevel = this.trustLevels.get(sourceId) ?? 0;
@@ -238,18 +272,116 @@ export class HookRegistry {
238
272
  return false;
239
273
  }
240
274
  }
241
- let globalRegistry = null;
242
- export function getHookRegistry() {
243
- if (!globalRegistry) {
244
- globalRegistry = new HookRegistry();
245
- globalRegistry.setDevMode(process.env['NODE_ENV'] !== 'production');
275
+ /** Nomes do catálogo do core injetados via store — aplicados a novos registries. */
276
+ const pendingKnownHooks = [];
277
+ function createDefaultRegistry() {
278
+ const registry = new HookRegistry();
279
+ registry.setDevMode(process.env['NODE_ENV'] !== 'production');
280
+ if (pendingKnownHooks.length > 0)
281
+ registry.setKnownHooks(pendingKnownHooks);
282
+ return registry;
283
+ }
284
+ /**
285
+ * Resolução síncrona do tenant ativo. Injetado pelo core (ALS de tenant);
286
+ * sem contexto → registry platform (hooks do core).
287
+ */
288
+ let hookTenantResolver = () => null;
289
+ export function setHookTenantResolver(resolver) {
290
+ hookTenantResolver = resolver;
291
+ }
292
+ export function getCurrentHookTenantId() {
293
+ try {
294
+ return hookTenantResolver();
295
+ }
296
+ catch {
297
+ return null;
246
298
  }
247
- return globalRegistry;
248
299
  }
249
300
  /**
250
- * Substitui o registry global. Uso interno: testes e hot-reload de plugins.
301
+ * Store de registries por tenant (isolamento de plugins por tenant).
302
+ *
303
+ * - `platform`: hooks do core (`okcms.*`) — compartilhado por todo o processo;
304
+ * - `tenant(tenantId)`: hooks dos plugins habilitados daquele tenant, criado
305
+ * sob demanda e descarregável (invalidate/drop).
306
+ *
307
+ * O contrato é `tenant(id)` para poder evoluir para vm/worker por tenant sem
308
+ * mudar quem chama (`getHookRegistry()` continua síncrono).
309
+ */
310
+ export class HookRegistryStore {
311
+ platformRegistry;
312
+ byTenant = new Map();
313
+ constructor(platform) {
314
+ this.platformRegistry = platform ?? createDefaultRegistry();
315
+ }
316
+ platform() {
317
+ return this.platformRegistry;
318
+ }
319
+ setPlatform(registry) {
320
+ this.platformRegistry = registry;
321
+ }
322
+ /** Registry do tenant (criado sob demanda com devMode + catálogo do core). */
323
+ tenant(tenantId) {
324
+ let registry = this.byTenant.get(tenantId);
325
+ if (!registry) {
326
+ registry = createDefaultRegistry();
327
+ this.byTenant.set(tenantId, registry);
328
+ }
329
+ return registry;
330
+ }
331
+ hasTenant(tenantId) {
332
+ return this.byTenant.has(tenantId);
333
+ }
334
+ tenantIds() {
335
+ return [...this.byTenant.keys()];
336
+ }
337
+ /**
338
+ * Descarrega os hooks de um tenant (ativação/desativação, LRU, shutdown).
339
+ * Retorna true quando havia um registry carregado.
340
+ */
341
+ invalidateTenant(tenantId) {
342
+ return this.byTenant.delete(tenantId);
343
+ }
344
+ clearTenants() {
345
+ this.byTenant.clear();
346
+ }
347
+ /** Propaga o catálogo do core para todos os registries (atuais e futuros). */
348
+ setKnownHooks(names) {
349
+ const list = [...names];
350
+ pendingKnownHooks.length = 0;
351
+ pendingKnownHooks.push(...list);
352
+ this.platformRegistry.setKnownHooks(list);
353
+ for (const registry of this.byTenant.values())
354
+ registry.setKnownHooks(list);
355
+ }
356
+ get size() {
357
+ return this.byTenant.size;
358
+ }
359
+ }
360
+ let globalStore = null;
361
+ export function getHookRegistryStore() {
362
+ if (!globalStore)
363
+ globalStore = new HookRegistryStore();
364
+ return globalStore;
365
+ }
366
+ /**
367
+ * Registry do escopo atual: tenant quando há contexto de tenant (plugins por
368
+ * tenant), platform quando não há (hooks do core).
369
+ */
370
+ export function getHookRegistry() {
371
+ const store = getHookRegistryStore();
372
+ const tenantId = getCurrentHookTenantId();
373
+ return tenantId ? store.tenant(tenantId) : store.platform();
374
+ }
375
+ /**
376
+ * Substitui o registry platform. Uso interno: testes e hot-reload de plugins.
251
377
  */
252
378
  export function setHookRegistry(registry) {
253
- globalRegistry = registry;
379
+ getHookRegistryStore().setPlatform(registry);
380
+ }
381
+ /** Troca o store inteiro (testes). */
382
+ export function setHookRegistryStore(store) {
383
+ globalStore = store;
384
+ if (store === null)
385
+ pendingKnownHooks.length = 0;
254
386
  }
255
387
  //# sourceMappingURL=hook-registry.js.map