@oktis-works/plugin-runtime 0.1.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.
@@ -0,0 +1,82 @@
1
+ export type HookCallback<TArgs extends unknown[] = unknown[]> = (...args: TArgs) => unknown;
2
+ export interface AddHookOptions {
3
+ priority?: number;
4
+ acceptedArgs?: number;
5
+ /** Identificador da origem ('blacklotus' para core ou id do plugin). */
6
+ sourceId?: string;
7
+ sourceType?: 'core' | 'plugin';
8
+ /** Nível mínimo de confiança exigido pelo ponto de extensão. */
9
+ requiredTrustLevel?: number;
10
+ }
11
+ export interface AuditLogEntry {
12
+ timestamp: string;
13
+ type: 'HOOK_BLOCKED' | 'HOOK_REJECTED';
14
+ sourceId: string;
15
+ sourceType: 'core' | 'plugin';
16
+ action: string;
17
+ reason: string;
18
+ }
19
+ export declare class HookRegistry {
20
+ private registrationSeq;
21
+ private readonly actions;
22
+ private readonly filters;
23
+ private readonly didActionCounts;
24
+ private currentHook;
25
+ private readonly auditLog;
26
+ private devMode;
27
+ private trustLevels;
28
+ setDevMode(enabled: boolean): void;
29
+ setTrustLevel(sourceId: string, level: number): void;
30
+ /**
31
+ * Catálogo de hooks registrados (REQ-REQU-035-002): lista cada hook com tipo,
32
+ * prioridades e fontes, gerado automaticamente a partir do registry.
33
+ */
34
+ listRegistered(): Array<{
35
+ hook: string;
36
+ type: 'action' | 'filter';
37
+ priorities: number[];
38
+ sources: string[];
39
+ total: number;
40
+ }>;
41
+ getAuditLog(): AuditLogEntry[];
42
+ clearAuditLog(): void;
43
+ /**
44
+ * Registra um callback de ação (executado por efeitos colaterais).
45
+ */
46
+ addAction(action: string, callback: HookCallback, options?: AddHookOptions): void;
47
+ /**
48
+ * Registra um callback de filtro (transforma e retorna o valor).
49
+ */
50
+ addFilter(filter: string, callback: HookCallback<[unknown, ...unknown[]]>, options?: AddHookOptions): void;
51
+ removeAction(action: string, callback: HookCallback, priority?: number): boolean;
52
+ removeFilter(filter: string, callback: HookCallback, priority?: number): boolean;
53
+ hasAction(action: string, callback?: HookCallback): boolean;
54
+ hasFilter(filter: string, callback?: HookCallback): boolean;
55
+ didAction(action: string): number;
56
+ getCurrentHook(): string | null;
57
+ /**
58
+ * Executa todos os callbacks registrados para uma ação.
59
+ * Reentrante: chamadas aninhadas preservam e restauram o hook atual.
60
+ */
61
+ doAction(action: string, ...args: unknown[]): Promise<void>;
62
+ /**
63
+ * Aplica filtros em cadeia: cada callback recebe o valor transformado pelo anterior.
64
+ * Semântica síncrona estilo WordPress (DECI-026).
65
+ */
66
+ applyFilters(filter: string, value: unknown, ...args: unknown[]): unknown;
67
+ /**
68
+ * Versão assíncrona do pipeline de filtros — callbacks que retornam Promise
69
+ * são resolvidos antes de alimentar o próximo da cadeia.
70
+ */
71
+ applyFiltersAsync(filter: string, value: unknown, ...args: unknown[]): Promise<unknown>;
72
+ private sortedEntries;
73
+ private authorizeRegistration;
74
+ private removeEntry;
75
+ private hasEntry;
76
+ }
77
+ export declare function getHookRegistry(): HookRegistry;
78
+ /**
79
+ * Substitui o registry global. Uso interno: testes e hot-reload de plugins.
80
+ */
81
+ export declare function setHookRegistry(registry: HookRegistry): void;
82
+ //# sourceMappingURL=hook-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-registry.d.ts","sourceRoot":"","sources":["../src/hook-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,255 @@
1
+ // @oktis-works/plugin-runtime - Hook Registry (WordPress-style Actions & Filters)
2
+ const CORE_PREFIX = 'blacklotus.';
3
+ const PLUGIN_PREFIX = 'plugin.';
4
+ function isPrefixed(action) {
5
+ return action.startsWith(CORE_PREFIX) || action.startsWith(PLUGIN_PREFIX);
6
+ }
7
+ export class HookRegistry {
8
+ registrationSeq = 0;
9
+ actions = new Map();
10
+ filters = new Map();
11
+ didActionCounts = new Map();
12
+ currentHook = null;
13
+ auditLog = [];
14
+ devMode = false;
15
+ trustLevels = new Map();
16
+ setDevMode(enabled) {
17
+ this.devMode = enabled;
18
+ }
19
+ setTrustLevel(sourceId, level) {
20
+ this.trustLevels.set(sourceId, level);
21
+ }
22
+ /**
23
+ * Catálogo de hooks registrados (REQ-REQU-035-002): lista cada hook com tipo,
24
+ * prioridades e fontes, gerado automaticamente a partir do registry.
25
+ */
26
+ listRegistered() {
27
+ const merge = (map, type) => {
28
+ const out = [];
29
+ for (const [hook, buckets] of map) {
30
+ const entries = [...buckets.values()].flat();
31
+ out.push({
32
+ hook,
33
+ type,
34
+ priorities: [...buckets.keys()].sort((a, b) => a - b),
35
+ sources: [...new Set(entries.map((e) => e.sourceId))],
36
+ total: entries.length,
37
+ });
38
+ }
39
+ return out;
40
+ };
41
+ return [...merge(this.actions, 'action'), ...merge(this.filters, 'filter')].sort((a, b) => a.hook.localeCompare(b.hook));
42
+ }
43
+ getAuditLog() {
44
+ return [...this.auditLog];
45
+ }
46
+ clearAuditLog() {
47
+ this.auditLog.length = 0;
48
+ }
49
+ /**
50
+ * Registra um callback de ação (executado por efeitos colaterais).
51
+ */
52
+ addAction(action, callback, options = {}) {
53
+ if (!this.authorizeRegistration(action, options))
54
+ return;
55
+ const bucket = this.actions.get(action) ?? new Map();
56
+ const priority = options.priority ?? 10;
57
+ const entries = bucket.get(priority) ?? [];
58
+ entries.push({
59
+ seq: ++this.registrationSeq,
60
+ id: `${action}:${options.sourceId ?? 'anonymous'}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`,
61
+ callback,
62
+ priority,
63
+ acceptedArgs: options.acceptedArgs ?? callback.length,
64
+ sourceId: options.sourceId ?? 'anonymous',
65
+ sourceType: options.sourceType ?? 'plugin',
66
+ });
67
+ bucket.set(priority, entries);
68
+ this.actions.set(action, bucket);
69
+ }
70
+ /**
71
+ * Registra um callback de filtro (transforma e retorna o valor).
72
+ */
73
+ addFilter(filter, callback, options = {}) {
74
+ if (!this.authorizeRegistration(filter, options))
75
+ return;
76
+ const bucket = this.filters.get(filter) ?? new Map();
77
+ const priority = options.priority ?? 10;
78
+ const entries = bucket.get(priority) ?? [];
79
+ entries.push({
80
+ seq: ++this.registrationSeq,
81
+ id: `${filter}:${options.sourceId ?? 'anonymous'}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`,
82
+ callback,
83
+ priority,
84
+ acceptedArgs: options.acceptedArgs ?? callback.length,
85
+ sourceId: options.sourceId ?? 'anonymous',
86
+ sourceType: options.sourceType ?? 'plugin',
87
+ });
88
+ bucket.set(priority, entries);
89
+ this.filters.set(filter, bucket);
90
+ }
91
+ removeAction(action, callback, priority) {
92
+ return this.removeEntry(this.actions, action, callback, priority);
93
+ }
94
+ removeFilter(filter, callback, priority) {
95
+ return this.removeEntry(this.filters, filter, callback, priority);
96
+ }
97
+ hasAction(action, callback) {
98
+ return this.hasEntry(this.actions, action, callback);
99
+ }
100
+ hasFilter(filter, callback) {
101
+ return this.hasEntry(this.filters, filter, callback);
102
+ }
103
+ didAction(action) {
104
+ return this.didActionCounts.get(action) ?? 0;
105
+ }
106
+ getCurrentHook() {
107
+ return this.currentHook;
108
+ }
109
+ /**
110
+ * Executa todos os callbacks registrados para uma ação.
111
+ * Reentrante: chamadas aninhadas preservam e restauram o hook atual.
112
+ */
113
+ async doAction(action, ...args) {
114
+ const previous = this.currentHook;
115
+ this.currentHook = action;
116
+ this.didActionCounts.set(action, (this.didActionCounts.get(action) ?? 0) + 1);
117
+ try {
118
+ for (const entry of this.sortedEntries(this.actions, action)) {
119
+ try {
120
+ await entry.callback(...args.slice(0, Math.max(entry.acceptedArgs, 0)));
121
+ }
122
+ catch (error) {
123
+ // Callbacks com falha não interrompem os demais (BUSI-031).
124
+ console.error(`[hooks] Erro no callback de ação "${action}" (${entry.sourceId}):`, error);
125
+ }
126
+ }
127
+ }
128
+ finally {
129
+ this.currentHook = previous;
130
+ }
131
+ }
132
+ /**
133
+ * Aplica filtros em cadeia: cada callback recebe o valor transformado pelo anterior.
134
+ * Semântica síncrona estilo WordPress (DECI-026).
135
+ */
136
+ applyFilters(filter, value, ...args) {
137
+ const previous = this.currentHook;
138
+ this.currentHook = filter;
139
+ try {
140
+ let result = value;
141
+ for (const entry of this.sortedEntries(this.filters, filter)) {
142
+ try {
143
+ result = entry.callback(result, ...args.slice(0, Math.max(entry.acceptedArgs - 1, 0)));
144
+ }
145
+ catch (error) {
146
+ console.error(`[hooks] Erro no callback de filtro "${filter}" (${entry.sourceId}):`, error);
147
+ }
148
+ }
149
+ return result;
150
+ }
151
+ finally {
152
+ this.currentHook = previous;
153
+ }
154
+ }
155
+ /**
156
+ * Versão assíncrona do pipeline de filtros — callbacks que retornam Promise
157
+ * são resolvidos antes de alimentar o próximo da cadeia.
158
+ */
159
+ async applyFiltersAsync(filter, value, ...args) {
160
+ const previous = this.currentHook;
161
+ this.currentHook = filter;
162
+ try {
163
+ let result = value;
164
+ for (const entry of this.sortedEntries(this.filters, filter)) {
165
+ try {
166
+ result = await entry.callback(result, ...args.slice(0, Math.max(entry.acceptedArgs - 1, 0)));
167
+ }
168
+ catch (error) {
169
+ console.error(`[hooks] Erro no callback de filtro "${filter}" (${entry.sourceId}):`, error);
170
+ }
171
+ }
172
+ return result;
173
+ }
174
+ finally {
175
+ this.currentHook = previous;
176
+ }
177
+ }
178
+ sortedEntries(map, action) {
179
+ const bucket = map.get(action);
180
+ if (!bucket)
181
+ return [];
182
+ return [...bucket.entries()]
183
+ .sort(([a], [b]) => a - b)
184
+ .flatMap(([, entries]) => [...entries].sort((a, b) => a.seq - b.seq));
185
+ }
186
+ authorizeRegistration(action, options) {
187
+ 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}"`);
190
+ }
191
+ const requiredLevel = options.requiredTrustLevel ?? 0;
192
+ const sourceLevel = this.trustLevels.get(sourceId) ?? 0;
193
+ if (sourceLevel < requiredLevel) {
194
+ this.auditLog.push({
195
+ timestamp: new Date().toISOString(),
196
+ type: 'HOOK_BLOCKED',
197
+ sourceId,
198
+ sourceType: options.sourceType ?? 'plugin',
199
+ action,
200
+ reason: `trust level ${sourceLevel} insuficiente (mínimo ${requiredLevel})`,
201
+ });
202
+ return false;
203
+ }
204
+ return true;
205
+ }
206
+ removeEntry(map, name, callback, priority) {
207
+ const bucket = map.get(name);
208
+ if (!bucket)
209
+ return false;
210
+ let removed = false;
211
+ const prioritiesToRemove = priority !== undefined ? [priority] : [...bucket.keys()];
212
+ for (const p of prioritiesToRemove) {
213
+ const entries = bucket.get(p);
214
+ if (!entries)
215
+ continue;
216
+ const index = entries.findIndex((entry) => entry.callback === callback);
217
+ if (index >= 0) {
218
+ entries.splice(index, 1);
219
+ removed = true;
220
+ if (entries.length === 0)
221
+ bucket.delete(p);
222
+ }
223
+ }
224
+ if (bucket.size === 0)
225
+ map.delete(name);
226
+ return removed;
227
+ }
228
+ hasEntry(map, name, callback) {
229
+ const bucket = map.get(name);
230
+ if (!bucket)
231
+ return false;
232
+ if (!callback)
233
+ return true;
234
+ for (const entries of bucket.values()) {
235
+ if (entries.some((entry) => entry.callback === callback))
236
+ return true;
237
+ }
238
+ return false;
239
+ }
240
+ }
241
+ let globalRegistry = null;
242
+ export function getHookRegistry() {
243
+ if (!globalRegistry) {
244
+ globalRegistry = new HookRegistry();
245
+ globalRegistry.setDevMode(process.env['NODE_ENV'] !== 'production');
246
+ }
247
+ return globalRegistry;
248
+ }
249
+ /**
250
+ * Substitui o registry global. Uso interno: testes e hot-reload de plugins.
251
+ */
252
+ export function setHookRegistry(registry) {
253
+ globalRegistry = registry;
254
+ }
255
+ //# sourceMappingURL=hook-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-registry.js","sourceRoot":"","sources":["../src/hook-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { PluginSandbox } from './sandbox.js';
2
+ export type { SandboxConfig, LoadedPlugin } from './sandbox.js';
3
+ export { PluginLoader } from './loader.js';
4
+ export type { DiscoveredPlugin } from './loader.js';
5
+ export { HookRegistry, getHookRegistry, setHookRegistry, } from './hook-registry.js';
6
+ export type { AddHookOptions, AuditLogEntry, HookCallback } from './hook-registry.js';
7
+ export { scopePluginCss, getPluginScopeAttribute, wrapWithPluginScope, lintPluginCss, } from './style-isolation.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,mBAAmB,EACnB,aAAa,GACd,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { PluginSandbox } from './sandbox.js';
2
+ export { PluginLoader } from './loader.js';
3
+ export { HookRegistry, getHookRegistry, setHookRegistry, } from './hook-registry.js';
4
+ export { scopePluginCss, getPluginScopeAttribute, wrapWithPluginScope, lintPluginCss, } from './style-isolation.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,mBAAmB,EACnB,aAAa,GACd,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { PluginManifest } from '@oktis-works/plugin-sdk';
2
+ import type { LoadedPlugin } from './sandbox.js';
3
+ export interface DiscoveredPlugin {
4
+ manifest: PluginManifest;
5
+ path: string;
6
+ }
7
+ export declare class PluginLoader {
8
+ private pluginsDir;
9
+ constructor(pluginsDir: string);
10
+ discoverPlugins(): Promise<DiscoveredPlugin[]>;
11
+ loadPlugin(manifest: PluginManifest, pluginPath: string): Promise<LoadedPlugin>;
12
+ resolveDependencies(plugins: DiscoveredPlugin[]): DiscoveredPlugin[][];
13
+ topologicalSort(plugins: DiscoveredPlugin[], pluginMap: Map<string, DiscoveredPlugin>): string[];
14
+ private visit;
15
+ private groupByLevel;
16
+ private findLevelForPlugin;
17
+ private loadManifest;
18
+ }
19
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAIxB,eAAe,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAgB9C,UAAU,CAAC,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAuBrF,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,EAAE;IAMtE,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,MAAM,EAAE;IAYhG,OAAO,CAAC,KAAK;IAyBb,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,kBAAkB;YAYZ,YAAY;CAS3B"}
package/dist/loader.js ADDED
@@ -0,0 +1,121 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { join, resolve } from 'node:path';
3
+ import { satisfies, validRange } from 'semver';
4
+ import { CMS_VERSION } from '@oktis-works/validation';
5
+ export class PluginLoader {
6
+ pluginsDir;
7
+ constructor(pluginsDir) {
8
+ this.pluginsDir = resolve(pluginsDir);
9
+ }
10
+ async discoverPlugins() {
11
+ const entries = await readdir(this.pluginsDir, { withFileTypes: true });
12
+ const plugins = [];
13
+ for (const entry of entries) {
14
+ if (!entry.isDirectory())
15
+ continue;
16
+ const pluginPath = join(this.pluginsDir, entry.name);
17
+ const manifest = await this.loadManifest(pluginPath);
18
+ if (manifest) {
19
+ plugins.push({ manifest, path: pluginPath });
20
+ }
21
+ }
22
+ return plugins;
23
+ }
24
+ async loadPlugin(manifest, pluginPath) {
25
+ // RULE-semver-compatibility: range compatibility.blacklotus deve satisfazer a versão do CMS
26
+ const compat = manifest.compatibility?.blacklotus;
27
+ if (typeof compat === 'string' && validRange(compat) && !satisfies(CMS_VERSION, compat)) {
28
+ throw new Error(`Plugin "${manifest.name}" requer BlackLotus ${compat}, mas a versão atual é ${CMS_VERSION}`);
29
+ }
30
+ const entryPath = resolve(join(pluginPath, manifest.main));
31
+ if (!entryPath.startsWith(this.pluginsDir)) {
32
+ throw new Error(`Plugin entry point escapes plugins directory: ${manifest.main}`);
33
+ }
34
+ const mod = await import(entryPath);
35
+ const api = mod.default ?? mod;
36
+ return {
37
+ manifest,
38
+ api,
39
+ loaded: true,
40
+ loadedAt: Date.now(),
41
+ };
42
+ }
43
+ resolveDependencies(plugins) {
44
+ const pluginMap = new Map(plugins.map(p => [p.manifest.name, p]));
45
+ const sorted = this.topologicalSort(plugins, pluginMap);
46
+ return this.groupByLevel(sorted, pluginMap);
47
+ }
48
+ topologicalSort(plugins, pluginMap) {
49
+ const visited = new Set();
50
+ const visiting = new Set();
51
+ const result = [];
52
+ for (const plugin of plugins) {
53
+ this.visit(plugin.manifest.name, pluginMap, visited, visiting, result);
54
+ }
55
+ return result;
56
+ }
57
+ visit(name, pluginMap, visited, visiting, result) {
58
+ if (visited.has(name))
59
+ return;
60
+ if (visiting.has(name))
61
+ throw new Error(`Circular dependency detected: ${name}`);
62
+ const plugin = pluginMap.get(name);
63
+ if (!plugin)
64
+ return;
65
+ visiting.add(name);
66
+ const deps = plugin.manifest.dependencies ?? [];
67
+ for (const dep of deps) {
68
+ this.visit(dep.name, pluginMap, visited, visiting, result);
69
+ }
70
+ visiting.delete(name);
71
+ visited.add(name);
72
+ result.push(name);
73
+ }
74
+ groupByLevel(sorted, pluginMap) {
75
+ const levels = [];
76
+ const placed = new Set();
77
+ for (const name of sorted) {
78
+ const plugin = pluginMap.get(name);
79
+ if (!plugin)
80
+ continue;
81
+ const deps = plugin.manifest.dependencies ?? [];
82
+ let level = 0;
83
+ for (const dep of deps) {
84
+ const depIndex = sorted.indexOf(dep.name);
85
+ const pluginIndex = sorted.indexOf(name);
86
+ if (depIndex >= 0 && depIndex < pluginIndex) {
87
+ level = Math.max(level, ...this.findLevelForPlugin(dep.name, levels, pluginMap));
88
+ }
89
+ }
90
+ if (!levels[level])
91
+ levels[level] = [];
92
+ levels[level].push(plugin);
93
+ placed.add(name);
94
+ }
95
+ return levels.filter(l => l.length > 0);
96
+ }
97
+ findLevelForPlugin(name, levels, _pluginMap) {
98
+ const result = [];
99
+ for (let i = 0; i < levels.length; i++) {
100
+ const level = levels[i];
101
+ if (!level)
102
+ continue;
103
+ for (const p of level) {
104
+ if (p.manifest.name === name)
105
+ result.push(i);
106
+ }
107
+ }
108
+ return result.length > 0 ? result : [0];
109
+ }
110
+ async loadManifest(pluginPath) {
111
+ try {
112
+ const manifestPath = join(pluginPath, 'package.json');
113
+ const content = await readFile(manifestPath, 'utf-8');
114
+ return JSON.parse(content);
115
+ }
116
+ catch {
117
+ return null;
118
+ }
119
+ }
120
+ }
121
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { PluginManifest, PluginContext, PluginHookHandler, PluginAPI } from '@oktis-works/plugin-sdk';
2
+ export interface SandboxConfig {
3
+ timeoutMs: number;
4
+ maxMemoryMb: number;
5
+ allowedPermissions: string[];
6
+ }
7
+ export declare class PluginSandbox {
8
+ private config;
9
+ constructor(config?: Partial<SandboxConfig>);
10
+ loadPlugin(manifest: PluginManifest, api: PluginAPI): LoadedPlugin;
11
+ /** BUSI plugin-style-isolation: bloqueia CSS global se plugin declarar style inline sem escopo */
12
+ private enforceStyleIsolation;
13
+ executeHook(hookName: string, context: PluginContext, handler: PluginHookHandler, payload: unknown, pluginPermissions?: string[]): Promise<void>;
14
+ callHandler<T>(handler: (...args: unknown[]) => T | Promise<T>, context: PluginContext, ...args: unknown[]): Promise<T>;
15
+ private withTimeout;
16
+ private checkPermissions;
17
+ }
18
+ export interface LoadedPlugin {
19
+ manifest: PluginManifest;
20
+ api: PluginAPI;
21
+ loaded: boolean;
22
+ loadedAt: number;
23
+ }
24
+ //# sourceMappingURL=sandbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE3G,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAQD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM;IAI/C,UAAU,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,SAAS,GAAG,YAAY;IAMlE,kGAAkG;IAClG,OAAO,CAAC,qBAAqB;IASvB,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,EAChB,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAC3B,OAAO,CAAC,IAAI,CAAC;IAOV,WAAW,CAAC,CAAC,EACjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC/C,OAAO,EAAE,aAAa,EACtB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,CAAC,CAAC;YAIC,WAAW;IAczB,OAAO,CAAC,gBAAgB;CAOzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,57 @@
1
+ const defaultSandboxConfig = {
2
+ timeoutMs: 5000,
3
+ maxMemoryMb: 128,
4
+ allowedPermissions: [],
5
+ };
6
+ export class PluginSandbox {
7
+ config;
8
+ constructor(config = {}) {
9
+ this.config = { ...defaultSandboxConfig, ...config };
10
+ }
11
+ loadPlugin(manifest, api) {
12
+ this.checkPermissions(manifest.permissions ?? []);
13
+ this.enforceStyleIsolation(manifest);
14
+ return { manifest, api, loaded: true, loadedAt: Date.now() };
15
+ }
16
+ /** BUSI plugin-style-isolation: bloqueia CSS global se plugin declarar style inline sem escopo */
17
+ enforceStyleIsolation(manifest) {
18
+ const anyManifest = manifest;
19
+ const style = anyManifest['style'] ?? anyManifest['styles'];
20
+ if (typeof style === 'string' && (style.includes(':root') || style.includes('html{') || style.includes('body{')) && !style.includes('data-plugin')) {
21
+ // apenas warn — escopo será aplicado em runtime via scopePluginCss
22
+ console.warn(`[sandbox] Plugin ${manifest.name} declara CSS global sem [data-plugin] — será escopado automaticamente`);
23
+ }
24
+ }
25
+ async executeHook(hookName, context, handler, payload, pluginPermissions) {
26
+ if (pluginPermissions) {
27
+ this.checkPermissions(pluginPermissions);
28
+ }
29
+ await this.withTimeout(Promise.resolve(handler(context, payload)));
30
+ }
31
+ async callHandler(handler, context, ...args) {
32
+ return this.withTimeout(Promise.resolve(handler(context, ...args)));
33
+ }
34
+ async withTimeout(promise) {
35
+ let timer;
36
+ try {
37
+ return await Promise.race([
38
+ promise,
39
+ new Promise((_, reject) => {
40
+ timer = setTimeout(() => reject(new Error('Plugin operation timed out')), this.config.timeoutMs);
41
+ }),
42
+ ]);
43
+ }
44
+ finally {
45
+ if (timer)
46
+ clearTimeout(timer);
47
+ }
48
+ }
49
+ checkPermissions(permissions) {
50
+ for (const permission of permissions) {
51
+ if (!this.config.allowedPermissions.includes(permission)) {
52
+ throw new Error(`Plugin permission denied: ${permission}`);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAQA,MAAM,oBAAoB,GAAkB;IAC1C,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,GAAG;IAChB,kBAAkB,EAAE,EAAE;CACvB,CAAC;AAEF,MAAM,OAAO,aAAa;IAChB,MAAM,CAAgB;IAE9B,YAAY,SAAiC,EAAE;QAC7C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,oBAAoB,EAAE,GAAG,MAAM,EAAE,CAAC;IACvD,CAAC;IAED,UAAU,CAAC,QAAwB,EAAE,GAAc;QACjD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAC/D,CAAC;IAED,kGAAkG;IAC1F,qBAAqB,CAAC,QAAwB;QACpD,MAAM,WAAW,GAAG,QAA8C,CAAC;QACnE,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACnJ,mEAAmE;YACnE,OAAO,CAAC,IAAI,CAAC,oBAAoB,QAAQ,CAAC,IAAI,uEAAuE,CAAC,CAAC;QACzH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CACf,QAAgB,EAChB,OAAsB,EACtB,OAA0B,EAC1B,OAAgB,EAChB,iBAA4B;QAE5B,IAAI,iBAAiB,EAAE,CAAC;YACtB,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,WAAW,CACf,OAA+C,EAC/C,OAAsB,EACtB,GAAG,IAAe;QAElB,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC;IAEO,KAAK,CAAC,WAAW,CAAI,OAAmB;QAC9C,IAAI,KAAiC,CAAC;QACtC,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;gBACxB,OAAO;gBACP,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;oBAC3B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACnG,CAAC,CAAC;aACH,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,WAAqB;QAC5C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBACzD,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,EAAE,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Escopa CSS sob [data-plugin="name"] — isolamento fino para plugins.
3
+ * Cobre seletores, @media/@supports recursivo, preserva @keyframes/@font-face.
4
+ * Sem dependência de PostCSS (fallback runtime); para build use mesmo algoritmo de theme-runtime.
5
+ */
6
+ export declare function scopePluginCss(css: string, pluginName: string, prefix?: string): string;
7
+ export declare function getPluginScopeAttribute(pluginName: string): {
8
+ attr: string;
9
+ selector: string;
10
+ dataAttr: string;
11
+ };
12
+ /**
13
+ * Envelopa HTML de plugin com [data-plugin] para garantir escopo no DOM.
14
+ * Idempotente: se já contém data-plugin, retorna intacto.
15
+ */
16
+ export declare function wrapWithPluginScope(html: string, pluginName: string): string;
17
+ /**
18
+ * Valida que CSS de plugin não contém vazamento global óbvio.
19
+ * Retorna warnings se encontrar :root/html/body sem escopo ou * global sem prefix.
20
+ */
21
+ export declare function lintPluginCss(css: string, pluginName: string): string[];
22
+ //# sourceMappingURL=style-isolation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style-isolation.d.ts","sourceRoot":"","sources":["../src/style-isolation.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAkC,GAAG,MAAM,CAgEhH;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAGhH;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAK5E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAMvE"}
@@ -0,0 +1,125 @@
1
+ // @oktis-works/plugin-runtime - Plugin Style Isolation [data-plugin="nome"] — obrigatório
2
+ // Equivalente ao isolamento de tema [data-theme], mas para plugins (admin + site).
3
+ // BUSI plugin-style-isolation: todo CSS injetado por plugin deve ser escopado.
4
+ /**
5
+ * Escopa CSS sob [data-plugin="name"] — isolamento fino para plugins.
6
+ * Cobre seletores, @media/@supports recursivo, preserva @keyframes/@font-face.
7
+ * Sem dependência de PostCSS (fallback runtime); para build use mesmo algoritmo de theme-runtime.
8
+ */
9
+ export function scopePluginCss(css, pluginName, prefix = `[data-plugin="${pluginName}"]`) {
10
+ const isExcludedAtRule = (selector) => {
11
+ const s = selector.trim();
12
+ return s.startsWith('@keyframes') || s.startsWith('@font-face') || s.startsWith('@import');
13
+ };
14
+ const shouldReplaceRoot = (sel) => /:root|\bhtml\b|\bbody\b/.test(sel);
15
+ let out = '';
16
+ let i = 0;
17
+ const len = css.length;
18
+ while (i < len) {
19
+ if (css[i]?.trim() === '') {
20
+ out += css[i++];
21
+ continue;
22
+ }
23
+ if (css.startsWith('/*', i)) {
24
+ const end = css.indexOf('*/', i + 2);
25
+ const comment = end === -1 ? css.slice(i) : css.slice(i, end + 2);
26
+ out += comment;
27
+ i += comment.length;
28
+ continue;
29
+ }
30
+ if (css[i] === '@') {
31
+ const semi = css.indexOf(';', i);
32
+ const brace = css.indexOf('{', i);
33
+ if (semi !== -1 && (brace === -1 || semi < brace)) {
34
+ out += css.slice(i, semi + 1);
35
+ i = semi + 1;
36
+ continue;
37
+ }
38
+ }
39
+ const braceIdx = css.indexOf('{', i);
40
+ if (braceIdx === -1) {
41
+ out += css.slice(i);
42
+ break;
43
+ }
44
+ const selectorPart = css.slice(i, braceIdx).trim();
45
+ let depth = 0;
46
+ let endIdx = -1;
47
+ for (let k = braceIdx; k < len; k++) {
48
+ if (css[k] === '{')
49
+ depth++;
50
+ else if (css[k] === '}') {
51
+ depth--;
52
+ if (depth === 0) {
53
+ endIdx = k;
54
+ break;
55
+ }
56
+ }
57
+ }
58
+ if (endIdx === -1) {
59
+ out += css.slice(i);
60
+ break;
61
+ }
62
+ const block = css.slice(i, endIdx + 1);
63
+ const body = css.slice(braceIdx, endIdx + 1);
64
+ if (isExcludedAtRule(selectorPart)) {
65
+ out += block;
66
+ }
67
+ else if (selectorPart.startsWith('@media') || selectorPart.startsWith('@supports') || selectorPart.startsWith('@layer')) {
68
+ const inner = css.slice(braceIdx + 1, endIdx);
69
+ out += `${selectorPart} {${scopePluginCss(inner, pluginName, prefix)}}`;
70
+ }
71
+ else if (selectorPart.startsWith('@')) {
72
+ const inner = css.slice(braceIdx + 1, endIdx);
73
+ out += `${selectorPart} {${inner}}`;
74
+ }
75
+ else {
76
+ const selectors = selectorPart.split(',').map(s => s.trim()).filter(Boolean);
77
+ const scoped = selectors.map(sel => {
78
+ if (sel.startsWith('@'))
79
+ return sel;
80
+ if (sel.includes(prefix))
81
+ return sel;
82
+ if (shouldReplaceRoot(sel)) {
83
+ if (sel === ':root' || sel === 'html' || sel === 'body')
84
+ return prefix;
85
+ return sel.replace(':root', prefix).replace(/\bhtml\b/, prefix).replace(/\bbody\b/, prefix);
86
+ }
87
+ return `${prefix} ${sel}`;
88
+ });
89
+ out += `${scoped.join(', ')}${body}`;
90
+ }
91
+ i = endIdx + 1;
92
+ }
93
+ if (!out.trim() && css.trim())
94
+ return `${prefix} { ${css} }`;
95
+ return out || css;
96
+ }
97
+ export function getPluginScopeAttribute(pluginName) {
98
+ const escaped = pluginName.replace(/"/g, '&quot;');
99
+ return { attr: `data-plugin="${escaped}"`, selector: `[data-plugin="${escaped}"]`, dataAttr: 'data-plugin' };
100
+ }
101
+ /**
102
+ * Envelopa HTML de plugin com [data-plugin] para garantir escopo no DOM.
103
+ * Idempotente: se já contém data-plugin, retorna intacto.
104
+ */
105
+ export function wrapWithPluginScope(html, pluginName) {
106
+ const { attr } = getPluginScopeAttribute(pluginName);
107
+ if (html.includes('data-plugin='))
108
+ return html;
109
+ // prefere wrapper <div data-plugin="...">
110
+ return `<div ${attr}>${html}</div>`;
111
+ }
112
+ /**
113
+ * Valida que CSS de plugin não contém vazamento global óbvio.
114
+ * Retorna warnings se encontrar :root/html/body sem escopo ou * global sem prefix.
115
+ */
116
+ export function lintPluginCss(css, pluginName) {
117
+ const warnings = [];
118
+ const prefix = `[data-plugin="${pluginName}"]`;
119
+ if (css.includes(':root') && !css.includes(prefix))
120
+ warnings.push('CSS contém :root sem escopo [data-plugin] — será remapeado para prefix');
121
+ if (/\bhtml\s*\{/.test(css) || /\bbody\s*\{/.test(css))
122
+ warnings.push('CSS contém html/body global — será remapeado para [data-plugin]');
123
+ return warnings;
124
+ }
125
+ //# sourceMappingURL=style-isolation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style-isolation.js","sourceRoot":"","sources":["../src/style-isolation.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@oktis-works/plugin-runtime",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "clean": "rm -rf dist tsconfig.tsbuildinfo"
17
+ },
18
+ "dependencies": {
19
+ "@oktis-works/plugin-sdk": "0.1.0",
20
+ "@oktis-works/types": "0.1.0",
21
+ "@oktis-works/validation": "0.1.0",
22
+ "semver": "7"
23
+ },
24
+ "devDependencies": {
25
+ "typescript": "5.7.3"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "registry": "https://registry.npmjs.org/"
33
+ },
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/blacklotus/blacklotus-cms",
38
+ "directory": "packages/plugin-runtime"
39
+ }
40
+ }