@monoes/monobrowse 1.0.1 → 1.0.2
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/package.json +11 -15
- package/dist/src/browser/action-builder/analyzer.d.ts +0 -1
- package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -1
- package/dist/src/browser/action-builder/analyzer.js +43 -21
- package/dist/src/browser/action-builder/analyzer.js.map +1 -1
- package/dist/src/browser/dashboard/server.d.ts +12 -1
- package/dist/src/browser/dashboard/server.d.ts.map +1 -1
- package/dist/src/browser/dashboard/server.js +4 -198
- package/dist/src/browser/dashboard/server.js.map +1 -1
- package/dist/src/cli/action.d.ts.map +1 -1
- package/dist/src/cli/action.js +5 -2
- package/dist/src/cli/action.js.map +1 -1
- package/dist/src/cli/commands.d.ts.map +1 -1
- package/dist/src/cli/commands.js +0 -3
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/index.d.ts +0 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -16
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -15
- package/src/browser/action-builder/analyzer.ts +41 -23
- package/src/browser/dashboard/server.ts +16 -198
- package/src/cli/action.ts +6 -2
- package/src/cli/commands.ts +0 -3
- package/src/index.ts +0 -20
- package/src/browser/playbook/browser-handlers.ts +0 -449
- package/src/browser/playbook/builtin-handlers.ts +0 -400
- package/src/browser/playbook/index.ts +0 -2
- package/src/browser/playbook/store.ts +0 -87
- package/src/cli/playbook.ts +0 -280
|
@@ -5,23 +5,23 @@ import { join, dirname } from 'node:path';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
|
-
import type { StepEvent, RunRecord, PlaybookDef } from '@monoes/monoplaybook';
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (!existsSync(PLAYBOOKS_FILE)) return [];
|
|
15
|
-
try {
|
|
16
|
-
return JSON.parse(await readFile(PLAYBOOKS_FILE, 'utf-8')) as PlaybookDef[];
|
|
17
|
-
} catch { return []; }
|
|
9
|
+
export interface StepEvent {
|
|
10
|
+
type: string;
|
|
11
|
+
projectDir?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
18
13
|
}
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
export interface RunRecord {
|
|
16
|
+
id: string;
|
|
17
|
+
startedAt: number;
|
|
18
|
+
status: string;
|
|
19
|
+
itemsProcessed?: number;
|
|
20
|
+
[key: string]: unknown;
|
|
23
21
|
}
|
|
24
22
|
|
|
23
|
+
const RUNS_FILE = join(homedir(), '.monomind', 'browse-runs.json');
|
|
24
|
+
|
|
25
25
|
function readBody(req: IncomingMessage): Promise<string> {
|
|
26
26
|
return new Promise((resolve, reject) => {
|
|
27
27
|
let body = '';
|
|
@@ -90,7 +90,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
if (url === '/runs' && req.method === 'GET') {
|
|
93
|
-
// Merge in-memory runs with persisted runs so the UI shows history even across restarts
|
|
94
93
|
const persisted = await loadPersistedRuns().catch(() => [] as RunRecord[]);
|
|
95
94
|
const seen = new Set(runHistory.map(r => r.id));
|
|
96
95
|
const merged = [...runHistory, ...persisted.filter(r => !seen.has(r.id))]
|
|
@@ -109,12 +108,9 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
109
108
|
return;
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
// Parse the URL to correctly handle query parameters such as ?dir=
|
|
113
111
|
const parsed = new URL(url, 'http://localhost');
|
|
114
112
|
if (parsed.pathname === '/events' && (req.method === 'GET' || req.method === 'HEAD') && !WebSocketServer) {
|
|
115
|
-
// SSE
|
|
116
|
-
// No CORS header — the dashboard is served from 127.0.0.1:4242 and no cross-origin
|
|
117
|
-
// access is needed. A wildcard ACAO would let any web page subscribe to playbook events.
|
|
113
|
+
// SSE fallback when ws not available
|
|
118
114
|
const subscribedDir = parsed.searchParams.get('dir') ?? null;
|
|
119
115
|
res.writeHead(200, {
|
|
120
116
|
'Content-Type': 'text/event-stream',
|
|
@@ -123,7 +119,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
123
119
|
});
|
|
124
120
|
res.write(`data: ${JSON.stringify({ type: 'connected' })}\n\n`);
|
|
125
121
|
clientDirs.set(res, subscribedDir);
|
|
126
|
-
// 30-second keep-alive heartbeat prevents proxy idle-timeout drops.
|
|
127
122
|
const heartbeat = setInterval(() => {
|
|
128
123
|
try { res.write(': keep-alive\n\n'); } catch { clearInterval(heartbeat); }
|
|
129
124
|
}, 30_000);
|
|
@@ -134,176 +129,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
134
129
|
return;
|
|
135
130
|
}
|
|
136
131
|
|
|
137
|
-
// ── Playbook CRUD ────────────────────────────────────────────────────
|
|
138
|
-
// GET /api/playbooks → list all saved playbooks
|
|
139
|
-
// POST /api/playbooks → create/update a playbook (body: PlaybookDef)
|
|
140
|
-
// GET /api/playbooks/:id → get single playbook
|
|
141
|
-
// DELETE /api/playbooks/:id → delete a playbook
|
|
142
|
-
// POST /api/playbooks/:id/run → run a playbook (delegates to engine)
|
|
143
|
-
if (parsed.pathname === '/api/playbooks' && req.method === 'GET') {
|
|
144
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
145
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
146
|
-
res.end(JSON.stringify(list));
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (parsed.pathname === '/api/playbooks' && req.method === 'POST') {
|
|
151
|
-
try {
|
|
152
|
-
const raw = await readBody(req);
|
|
153
|
-
const pb = JSON.parse(raw) as PlaybookDef;
|
|
154
|
-
if (!pb.id || !pb.name) {
|
|
155
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
156
|
-
res.end(JSON.stringify({ error: 'id and name are required' }));
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
160
|
-
const idx = list.findIndex(w => w.id === pb.id);
|
|
161
|
-
if (idx >= 0) list[idx] = pb; else list.push(pb);
|
|
162
|
-
await savePlaybooks(list);
|
|
163
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
164
|
-
res.end(JSON.stringify(pb));
|
|
165
|
-
} catch (e) {
|
|
166
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
167
|
-
res.end(JSON.stringify({ error: String(e) }));
|
|
168
|
-
}
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const pbIdMatch = parsed.pathname.match(/^\/api\/playbooks\/([^/]+)$/);
|
|
173
|
-
const pbRunMatch = parsed.pathname.match(/^\/api\/playbooks\/([^/]+)\/run$/);
|
|
174
|
-
|
|
175
|
-
if (pbRunMatch && req.method === 'POST') {
|
|
176
|
-
const pbId = pbRunMatch[1];
|
|
177
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
178
|
-
const pb = list.find(w => w.id === pbId);
|
|
179
|
-
if (!pb) {
|
|
180
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
181
|
-
res.end(JSON.stringify({ error: `Playbook ${pbId} not found` }));
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
// Import engine and builtin handlers dynamically to avoid circular deps at startup
|
|
185
|
-
res.writeHead(202, { 'Content-Type': 'application/json' });
|
|
186
|
-
res.end(JSON.stringify({ ok: true, playbookId: pbId, message: 'Playbook run started' }));
|
|
187
|
-
// Run async, broadcast events via dashboard
|
|
188
|
-
Promise.resolve().then(async () => {
|
|
189
|
-
try {
|
|
190
|
-
const { runPlaybook, createDefaultHandlers } = await import('../../index.js');
|
|
191
|
-
const handlers = createDefaultHandlers();
|
|
192
|
-
const record = await runPlaybook(pb, {
|
|
193
|
-
handlers,
|
|
194
|
-
onEvent: (evt) => { instance?.broadcast(evt); },
|
|
195
|
-
isStopRequested: (id) => instance?.isStopRequested(id) ?? false,
|
|
196
|
-
});
|
|
197
|
-
instance?.addRunRecord(record);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
console.error('[dashboard] playbook run error:', err);
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (pbIdMatch && req.method === 'GET') {
|
|
206
|
-
const pbId = pbIdMatch[1];
|
|
207
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
208
|
-
const pb = list.find(w => w.id === pbId);
|
|
209
|
-
if (!pb) { res.writeHead(404); res.end('Not found'); return; }
|
|
210
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
211
|
-
res.end(JSON.stringify(pb));
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (pbIdMatch && req.method === 'DELETE') {
|
|
216
|
-
const pbId = pbIdMatch[1];
|
|
217
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
218
|
-
const newList = list.filter(w => w.id !== pbId);
|
|
219
|
-
await savePlaybooks(newList);
|
|
220
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
221
|
-
res.end(JSON.stringify({ ok: true, deleted: pbId }));
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// ── Webhook trigger ──────────────────────────────────────────────────────
|
|
226
|
-
// POST /webhooks/:playbookId
|
|
227
|
-
// Body (optional): JSON object or array — merged as `params` into the playbook run.
|
|
228
|
-
// Header (optional): X-Webhook-Signature: sha256=<hex> for HMAC verification.
|
|
229
|
-
// Response: 202 { ok, runId, playbookId } — run executes asynchronously.
|
|
230
|
-
const webhookMatch = parsed.pathname.match(/^\/webhooks\/([^/]+)$/);
|
|
231
|
-
if (webhookMatch && req.method === 'POST') {
|
|
232
|
-
const pbId = webhookMatch[1];
|
|
233
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
234
|
-
const pb = list.find(w => w.id === pbId);
|
|
235
|
-
if (!pb) {
|
|
236
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
237
|
-
res.end(JSON.stringify({ error: `Playbook ${pbId} not found` }));
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const rawBody = await readBody(req).catch(() => '');
|
|
242
|
-
|
|
243
|
-
// Optional HMAC-SHA256 signature verification
|
|
244
|
-
const sigHeader = (req.headers['x-webhook-signature'] as string | undefined) ?? '';
|
|
245
|
-
if (sigHeader) {
|
|
246
|
-
try {
|
|
247
|
-
const { createHmac } = await import('node:crypto');
|
|
248
|
-
// Secret is stored in the playbook's trigger config (first schedule/webhook trigger)
|
|
249
|
-
const triggers = (pb as unknown as Record<string, unknown>)['triggers'];
|
|
250
|
-
const secret: string | undefined =
|
|
251
|
-
Array.isArray(triggers)
|
|
252
|
-
? (triggers as Array<Record<string, unknown>>).find(t => t['secret'])?.[
|
|
253
|
-
'secret'
|
|
254
|
-
] as string | undefined
|
|
255
|
-
: undefined;
|
|
256
|
-
if (secret) {
|
|
257
|
-
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
|
|
258
|
-
if (sigHeader !== expected) {
|
|
259
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
260
|
-
res.end(JSON.stringify({ error: 'Invalid webhook signature' }));
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
} catch {
|
|
265
|
-
// crypto not available or other error — skip verification
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Parse body into params (string values only, per engine contract)
|
|
270
|
-
const params: Record<string, string> = {};
|
|
271
|
-
try {
|
|
272
|
-
if (rawBody.trim()) {
|
|
273
|
-
const parsed2 = JSON.parse(rawBody);
|
|
274
|
-
if (parsed2 && typeof parsed2 === 'object' && !Array.isArray(parsed2)) {
|
|
275
|
-
for (const [k, v] of Object.entries(parsed2 as Record<string, unknown>)) {
|
|
276
|
-
params[k] = typeof v === 'string' ? v : JSON.stringify(v);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
params['__body'] = rawBody;
|
|
280
|
-
}
|
|
281
|
-
} catch {
|
|
282
|
-
params['__body'] = rawBody;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const runId = `webhook-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
286
|
-
res.writeHead(202, { 'Content-Type': 'application/json' });
|
|
287
|
-
res.end(JSON.stringify({ ok: true, runId, playbookId: pbId }));
|
|
288
|
-
|
|
289
|
-
Promise.resolve().then(async () => {
|
|
290
|
-
try {
|
|
291
|
-
const { runPlaybook, createDefaultHandlers } = await import('../../index.js');
|
|
292
|
-
const handlers = createDefaultHandlers();
|
|
293
|
-
const record = await runPlaybook(pb, {
|
|
294
|
-
handlers,
|
|
295
|
-
params,
|
|
296
|
-
onEvent: (evt) => { instance?.broadcast(evt); },
|
|
297
|
-
isStopRequested: (id) => instance?.isStopRequested(id) ?? false,
|
|
298
|
-
});
|
|
299
|
-
instance?.addRunRecord(record);
|
|
300
|
-
} catch (err) {
|
|
301
|
-
console.error('[dashboard] webhook run error:', err);
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
132
|
res.writeHead(404);
|
|
308
133
|
res.end('Not found');
|
|
309
134
|
});
|
|
@@ -311,12 +136,10 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
311
136
|
if (WebSocketServer) {
|
|
312
137
|
const wss = new WebSocketServer({ server });
|
|
313
138
|
wss.on('connection', async (ws: any, req: any) => {
|
|
314
|
-
// Extract optional project-dir filter from the WebSocket upgrade URL
|
|
315
139
|
const upgradeUrl = req?.url ?? '/ws';
|
|
316
140
|
const upgradeParsed = new URL(upgradeUrl, 'http://localhost');
|
|
317
141
|
const subscribedDir = upgradeParsed.searchParams.get('dir') ?? null;
|
|
318
142
|
clientDirs.set(ws, subscribedDir);
|
|
319
|
-
// Merge in-memory and persisted runs so history survives server restarts
|
|
320
143
|
const persisted = await loadPersistedRuns().catch(() => [] as RunRecord[]);
|
|
321
144
|
const seen = new Set(runHistory.map(r => r.id));
|
|
322
145
|
const merged = [...runHistory, ...persisted.filter(r => !seen.has(r.id))]
|
|
@@ -329,11 +152,10 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
329
152
|
|
|
330
153
|
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
331
154
|
if (err.code === 'EADDRINUSE') {
|
|
332
|
-
console.log(`Dashboard port ${port} in use —
|
|
155
|
+
console.log(`Dashboard port ${port} in use — continuing without live dashboard.`);
|
|
333
156
|
} else {
|
|
334
157
|
console.error(`Dashboard server error: ${err.message}`);
|
|
335
158
|
}
|
|
336
|
-
// Replace with a no-op instance so the engine doesn't crash
|
|
337
159
|
instance = {
|
|
338
160
|
broadcast: () => {},
|
|
339
161
|
addRunRecord: () => {},
|
|
@@ -348,17 +170,14 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
348
170
|
function broadcast(event: StepEvent): void {
|
|
349
171
|
const msg = JSON.stringify(event);
|
|
350
172
|
for (const [client, subscribedDir] of clientDirs) {
|
|
351
|
-
// Server-side project filtering: if the client subscribed with ?dir=<path>, only
|
|
352
|
-
// deliver events whose projectDir matches. Clients without a dir filter (subscribedDir
|
|
353
|
-
// === null) receive all events, preserving backward-compatibility with ui.html.
|
|
354
173
|
if (subscribedDir !== null && event.projectDir !== undefined && subscribedDir !== event.projectDir) {
|
|
355
174
|
continue;
|
|
356
175
|
}
|
|
357
176
|
try {
|
|
358
177
|
if (typeof client.send === 'function') {
|
|
359
|
-
client.send(msg);
|
|
178
|
+
client.send(msg);
|
|
360
179
|
} else {
|
|
361
|
-
client.write(`data: ${msg}\n\n`);
|
|
180
|
+
client.write(`data: ${msg}\n\n`);
|
|
362
181
|
}
|
|
363
182
|
} catch {
|
|
364
183
|
clientDirs.delete(client);
|
|
@@ -374,7 +193,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
374
193
|
runHistory.unshift(record);
|
|
375
194
|
if (runHistory.length > MAX_RUN_HISTORY) runHistory.pop();
|
|
376
195
|
}
|
|
377
|
-
// Persist to disk so the CLI dashboard (/api/workflow-runs) can read run history
|
|
378
196
|
const dir = join(homedir(), '.monomind');
|
|
379
197
|
mkdir(dir, { recursive: true }).then(() =>
|
|
380
198
|
writeFile(RUNS_FILE, JSON.stringify(runHistory, null, 2))
|
package/src/cli/action.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
// src/commands/browse-action.ts
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import { readdir, writeFile, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import { analyzePageForAction, type AnalyzerPage
|
|
6
|
+
import { analyzePageForAction, type AnalyzerPage } from '../index.js';
|
|
7
7
|
import type { ActionDef } from '../index.js';
|
|
8
8
|
|
|
9
|
+
async function readAction(filePath: string): Promise<ActionDef> {
|
|
10
|
+
return JSON.parse(await readFile(filePath, 'utf8')) as ActionDef;
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
// Built-in actions (shipped with the CLI) — actual step definitions live in adapters/
|
|
10
14
|
const BUILTIN_ACTIONS: { id: string; platform: string; name: string }[] = [
|
|
11
15
|
{ id: 'linkedin:comment_post', platform: 'linkedin', name: 'Comment on Post' },
|
package/src/cli/commands.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Command, CommandContext, CommandResult } from './types.js';
|
|
7
7
|
import { output } from './output.js';
|
|
8
|
-
import { createPlaybookCommand } from './playbook.js';
|
|
9
8
|
import { createActionCommand } from './action.js';
|
|
10
9
|
import { createPlatformCommand } from './platform.js';
|
|
11
10
|
import type { CdpClient, ElementRef, NetworkRoute, FindAction } from '../index.js';
|
|
@@ -2734,7 +2733,6 @@ function wrapCommanderCommand(factory: () => import('commander').Command): Comma
|
|
|
2734
2733
|
};
|
|
2735
2734
|
}
|
|
2736
2735
|
|
|
2737
|
-
const playbookSubcommand: Command = wrapCommanderCommand(createPlaybookCommand);
|
|
2738
2736
|
const actionSubcommand: Command = wrapCommanderCommand(createActionCommand);
|
|
2739
2737
|
const platformSubcommand: Command = wrapCommanderCommand(createPlatformCommand);
|
|
2740
2738
|
|
|
@@ -2806,7 +2804,6 @@ const browseCommand: Command = {
|
|
|
2806
2804
|
harCommand,
|
|
2807
2805
|
resizeCommand,
|
|
2808
2806
|
closeCommand,
|
|
2809
|
-
playbookSubcommand,
|
|
2810
2807
|
actionSubcommand,
|
|
2811
2808
|
platformSubcommand,
|
|
2812
2809
|
],
|
package/src/index.ts
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
// Re-export the full monoplaybook engine so consumers only need @monoes/monobrowse
|
|
2
|
-
export * from '@monoes/monoplaybook';
|
|
3
|
-
|
|
4
1
|
export * from './browser/index.js';
|
|
5
2
|
export * from './browser/action-builder/analyzer.js';
|
|
6
3
|
export * from './browser/action-builder/types.js';
|
|
7
4
|
export * from './browser/adapters/index.js';
|
|
8
5
|
export { startDashboard, getDashboard } from './browser/dashboard/server.js';
|
|
9
|
-
export { createBuiltinHandlers, createBrowserHandlers } from './browser/playbook/index.js';
|
|
10
|
-
export { readAction } from './browser/playbook/store.js';
|
|
11
|
-
|
|
12
|
-
// Batteries-included handler factory: service nodes + browser automation + builtins.
|
|
13
|
-
// This is the recommended entry point when using monobrowse as the default executor.
|
|
14
|
-
import { createNodeHandlers } from '@monoes/monoplaybook';
|
|
15
|
-
import { createBrowserHandlers } from './browser/playbook/browser-handlers.js';
|
|
16
|
-
import { createBuiltinHandlers } from './browser/playbook/builtin-handlers.js';
|
|
17
|
-
import type { NodeHandler } from '@monoes/monoplaybook';
|
|
18
|
-
|
|
19
|
-
export function createDefaultHandlers(): Map<string, NodeHandler> {
|
|
20
|
-
return new Map<string, NodeHandler>([
|
|
21
|
-
...createNodeHandlers(),
|
|
22
|
-
...createBrowserHandlers(),
|
|
23
|
-
...createBuiltinHandlers(),
|
|
24
|
-
]);
|
|
25
|
-
}
|