@atolljs/cli 0.1.6 → 0.1.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.
- package/README.md +4 -0
- package/dist/cli.js +123 -3
- package/package.json +1 -1
- package/src/cli.ts +9 -1
- package/src/commands.ts +89 -1
- package/src/templates.ts +63 -0
package/README.md
CHANGED
|
@@ -42,6 +42,10 @@ atoll add mfe <name> publishable micro-frontend — framework-free
|
|
|
42
42
|
contract (defineIslandContract) + worker entry
|
|
43
43
|
attaching it + vite.mfe.config.ts publish build
|
|
44
44
|
→ dist-mfe/<name>.worker.js behind CORS
|
|
45
|
+
atoll add devtools [name] observability — src/atoll/devtools.ts init module
|
|
46
|
+
(import first in the app entry) + @atolljs/devtools
|
|
47
|
+
dev dep. Browser apps get the ?__atoll_devtools gate;
|
|
48
|
+
node hosts get the /node entry + ATOLL_DEVTOOLS=1
|
|
45
49
|
|
|
46
50
|
atoll doctor [--fix] verify deps, lockfile, .npmrc policy,
|
|
47
51
|
worker-entry detectability, COOP/COEP headers
|
package/dist/cli.js
CHANGED
|
@@ -2194,6 +2194,57 @@ export async function register() {
|
|
|
2194
2194
|
`
|
|
2195
2195
|
};
|
|
2196
2196
|
}
|
|
2197
|
+
function devtoolsSetup(name, host, wsUrl) {
|
|
2198
|
+
if (host === "node") {
|
|
2199
|
+
return `import { initDevtools } from '@atolljs/devtools/node';
|
|
2200
|
+
|
|
2201
|
+
/**
|
|
2202
|
+
* Devtools sink \u2014 imported first from the entry so it's installed before any
|
|
2203
|
+
* pool spawns (worker INIT carries the flag at spawn time; late installs
|
|
2204
|
+
* leave existing workers silent).
|
|
2205
|
+
*
|
|
2206
|
+
* No-op unless the process was started with ATOLL_DEVTOOLS=1 \u2014 then events
|
|
2207
|
+
* stream over WebSocket to the standalone dashboard (\`atoll devtools\`,
|
|
2208
|
+
* http://127.0.0.1:4780). Node has no BroadcastChannel page context, so the
|
|
2209
|
+
* aggregate server is the transport.
|
|
2210
|
+
*/
|
|
2211
|
+
initDevtools({ session: { name: '${name}' } });
|
|
2212
|
+
`;
|
|
2213
|
+
}
|
|
2214
|
+
if (wsUrl) {
|
|
2215
|
+
return `import { initDevtools } from '@atolljs/devtools';
|
|
2216
|
+
|
|
2217
|
+
/**
|
|
2218
|
+
* Devtools sink \u2014 imported first from the app entry so it's installed before
|
|
2219
|
+
* any pool spawns (worker INIT carries the flag at spawn time; late installs
|
|
2220
|
+
* leave existing workers silent).
|
|
2221
|
+
*
|
|
2222
|
+
* No-op unless the page URL carries ?__atoll_devtools \u2014 then events stream
|
|
2223
|
+
* over WebSocket to the standalone aggregate dashboard (\`atoll devtools\`,
|
|
2224
|
+
* http://127.0.0.1:4780). This app's dev server doesn't mount /__atoll/, so
|
|
2225
|
+
* the overlay is off and the aggregate server is the viewer.
|
|
2226
|
+
*/
|
|
2227
|
+
initDevtools({
|
|
2228
|
+
session: { name: '${name}' },
|
|
2229
|
+
overlay: false,
|
|
2230
|
+
url: '${wsUrl}',
|
|
2231
|
+
});
|
|
2232
|
+
`;
|
|
2233
|
+
}
|
|
2234
|
+
return `import { initDevtools } from '@atolljs/devtools';
|
|
2235
|
+
|
|
2236
|
+
/**
|
|
2237
|
+
* Devtools sink \u2014 imported first from the app entry so it's installed before
|
|
2238
|
+
* any pool spawns (worker INIT carries the flag at spawn time; late installs
|
|
2239
|
+
* leave existing workers silent).
|
|
2240
|
+
*
|
|
2241
|
+
* No-op unless the page URL carries ?__atoll_devtools \u2014 then events stream
|
|
2242
|
+
* over BroadcastChannel on the app's own origin and a flyout overlay mounts.
|
|
2243
|
+
* /__atoll/ itself is served by the @atolljs/vite plugin (plugins: [atoll()]).
|
|
2244
|
+
*/
|
|
2245
|
+
initDevtools({ session: { name: '${name}' } });
|
|
2246
|
+
`;
|
|
2247
|
+
}
|
|
2197
2248
|
|
|
2198
2249
|
// src/commands.ts
|
|
2199
2250
|
var NODE_HOSTS = ["node", "nestjs"];
|
|
@@ -2292,7 +2343,8 @@ var ADD_KINDS = [
|
|
|
2292
2343
|
"housed",
|
|
2293
2344
|
"route",
|
|
2294
2345
|
"component",
|
|
2295
|
-
"instrumentation"
|
|
2346
|
+
"instrumentation",
|
|
2347
|
+
"devtools"
|
|
2296
2348
|
];
|
|
2297
2349
|
var KIND_SCOPE = {
|
|
2298
2350
|
worker: null,
|
|
@@ -2305,7 +2357,8 @@ var KIND_SCOPE = {
|
|
|
2305
2357
|
housed: ["nestjs"],
|
|
2306
2358
|
route: ["nextjs"],
|
|
2307
2359
|
component: ["nextjs"],
|
|
2308
|
-
instrumentation: ["nextjs"]
|
|
2360
|
+
instrumentation: ["nextjs"],
|
|
2361
|
+
devtools: null
|
|
2309
2362
|
};
|
|
2310
2363
|
var KIND_VARIANTS = {
|
|
2311
2364
|
island: ["facade", "mono"],
|
|
@@ -2399,7 +2452,7 @@ async function runAdd(ctx) {
|
|
|
2399
2452
|
} else {
|
|
2400
2453
|
nameArg = rest[1];
|
|
2401
2454
|
}
|
|
2402
|
-
const name = await pickName(ctx, kind, nameArg);
|
|
2455
|
+
const name = kind === "devtools" ? nameArg ?? flag(ctx.args, "name") ?? project.pkg?.name?.split("/").pop() ?? "app" : await pickName(ctx, kind, nameArg);
|
|
2403
2456
|
if (!name) return 1;
|
|
2404
2457
|
const force = { force: hasFlag(ctx.args, "force") };
|
|
2405
2458
|
const atollDir = flag(ctx.args, "dir") ?? rel(join3(project.srcDir, "atoll"));
|
|
@@ -2578,6 +2631,45 @@ pools warm at boot \u2014 extend register() as more routes appear.`
|
|
|
2578
2631
|
);
|
|
2579
2632
|
return 0;
|
|
2580
2633
|
}
|
|
2634
|
+
case "devtools": {
|
|
2635
|
+
const host = hostOf(framework);
|
|
2636
|
+
const wsUrl = host === "browser" && framework === "angular" ? "ws://127.0.0.1:4780/events" : void 0;
|
|
2637
|
+
const file = `${atollDir}/devtools.ts`;
|
|
2638
|
+
await writeTree(
|
|
2639
|
+
ctx.io,
|
|
2640
|
+
ctx.cwd,
|
|
2641
|
+
[{ path: file, content: devtoolsSetup(name, host, wsUrl) }],
|
|
2642
|
+
force
|
|
2643
|
+
);
|
|
2644
|
+
if (missingDeps(project.pkg, ["@atolljs/devtools"]).length) {
|
|
2645
|
+
await offerInstall(ctx, project, ["-D", "@atolljs/devtools"]);
|
|
2646
|
+
}
|
|
2647
|
+
const srcRel = rel(project.srcDir);
|
|
2648
|
+
const spec = file.startsWith(`${srcRel}/`) ? `./${file.slice(srcRel.length + 1).replace(/\.ts$/, "")}` : `./${file.replace(/\.ts$/, "")}`;
|
|
2649
|
+
ctx.io.print("");
|
|
2650
|
+
ctx.io.print(ctx.io.fmt.strong("wire it:"));
|
|
2651
|
+
ctx.io.print(` import the module first in your entry \u2014 before any pool spawns:`);
|
|
2652
|
+
ctx.io.print(ctx.io.fmt.accent(` import '${spec}';`));
|
|
2653
|
+
if (host === "node") {
|
|
2654
|
+
ctx.io.print(
|
|
2655
|
+
` ${ctx.io.fmt.accent("ATOLL_DEVTOOLS=1")} starts streaming \xB7 ${ctx.io.fmt.accent("atoll devtools")} serves the dashboard`
|
|
2656
|
+
);
|
|
2657
|
+
} else if (wsUrl) {
|
|
2658
|
+
ctx.io.print(
|
|
2659
|
+
` ${ctx.io.fmt.accent("atoll devtools")} serves the aggregate dashboard \xB7 open the app with ${ctx.io.fmt.accent("?__atoll_devtools")}`
|
|
2660
|
+
);
|
|
2661
|
+
} else {
|
|
2662
|
+
ctx.io.print(
|
|
2663
|
+
` open the app with ${ctx.io.fmt.accent("?__atoll_devtools")} \u2014 the flyout mounts and /__atoll/ serves the dashboard`
|
|
2664
|
+
);
|
|
2665
|
+
ctx.io.print(
|
|
2666
|
+
ctx.io.fmt.dim(
|
|
2667
|
+
` /__atoll/ needs the @atolljs/vite plugin (plugins: [atoll()]) \u2014 no plugin? run \`atoll devtools\` + pass transport: 'websocket'`
|
|
2668
|
+
)
|
|
2669
|
+
);
|
|
2670
|
+
}
|
|
2671
|
+
return 0;
|
|
2672
|
+
}
|
|
2581
2673
|
default:
|
|
2582
2674
|
ctx.io.error(`unhandled add kind ${kind}`);
|
|
2583
2675
|
return 1;
|
|
@@ -2639,6 +2731,26 @@ async function runCreate(ctx) {
|
|
|
2639
2731
|
}
|
|
2640
2732
|
return 0;
|
|
2641
2733
|
}
|
|
2734
|
+
async function runDevtools(ctx) {
|
|
2735
|
+
const { io } = ctx;
|
|
2736
|
+
const spec = "@atolljs/devtools/server";
|
|
2737
|
+
const mod = await import(spec).catch(async () => {
|
|
2738
|
+
const local = new URL("../../devtools/dist/server.js", import.meta.url).href;
|
|
2739
|
+
return import(local).catch(() => null);
|
|
2740
|
+
});
|
|
2741
|
+
if (!mod?.createDevtoolsServer) {
|
|
2742
|
+
io.error(`@atolljs/devtools isn't installed \u2014 ${io.fmt.accent("npm i -D @atolljs/devtools")}`);
|
|
2743
|
+
return 1;
|
|
2744
|
+
}
|
|
2745
|
+
const port = Number(flag(ctx.args, "port") ?? 4780);
|
|
2746
|
+
const server = await mod.createDevtoolsServer({ port: Number.isFinite(port) ? port : 4780 });
|
|
2747
|
+
io.print(`atoll devtools \u2192 ${io.fmt.accent(server.url)}`);
|
|
2748
|
+
io.print(io.fmt.dim(" ingest endpoint: " + server.url.replace("http://", "ws://") + "/events"));
|
|
2749
|
+
io.print(io.fmt.dim(" in the app: connectDevtools() from '@atolljs/devtools'"));
|
|
2750
|
+
io.warn("serving \u2014 Ctrl+C to stop");
|
|
2751
|
+
return new Promise(() => {
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2642
2754
|
function runDoctor(ctx) {
|
|
2643
2755
|
const project = detectProject(ctx.cwd);
|
|
2644
2756
|
const fix = hasFlag(ctx.args, "fix");
|
|
@@ -2750,6 +2862,8 @@ var HELP = (fmt) => `${fmt.strong("atoll")} \u2014 bootstrap multithreading ${fm
|
|
|
2750
2862
|
route nextjs: api route pool \u2014 task | client variants
|
|
2751
2863
|
component nextjs: 'use client' hooks component over a pool
|
|
2752
2864
|
instrumentation nextjs: server bootstrap that warms route pools
|
|
2865
|
+
devtools observability \u2014 init module + @atolljs/devtools install
|
|
2866
|
+
(browser: ?__atoll_devtools + /__atoll/ \xB7 node: ATOLL_DEVTOOLS=1)
|
|
2753
2867
|
|
|
2754
2868
|
Frameworks: react vue solid svelte angular nestjs nextjs node
|
|
2755
2869
|
[--dir <path>] [--memory <name>|--no-memory] [--variant <v>] [--force]
|
|
@@ -2758,6 +2872,10 @@ var HELP = (fmt) => `${fmt.strong("atoll")} \u2014 bootstrap multithreading ${fm
|
|
|
2758
2872
|
Check the setup \u2014 deps, lockfile, .npmrc policy, bundler-detectable
|
|
2759
2873
|
worker entries, COOP/COEP headers. --fix writes a hardened .npmrc.
|
|
2760
2874
|
|
|
2875
|
+
${fmt.accent("atoll devtools")} [--port 4780]
|
|
2876
|
+
Serve the local devtools dashboard \u2014 pools, task timings, shared-memory
|
|
2877
|
+
writes, island traffic. Apps connect via connectDevtools().
|
|
2878
|
+
|
|
2761
2879
|
${fmt.dim(`flags: --yes/-y accepts defaults, --force overwrites existing files,
|
|
2762
2880
|
--no-install skips dependency installation`)}
|
|
2763
2881
|
`;
|
|
@@ -2778,6 +2896,8 @@ async function runCli(argv, io, cwd) {
|
|
|
2778
2896
|
return await runAdd(ctx);
|
|
2779
2897
|
case "doctor":
|
|
2780
2898
|
return runDoctor(ctx);
|
|
2899
|
+
case "devtools":
|
|
2900
|
+
return await runDevtools(ctx);
|
|
2781
2901
|
case "help":
|
|
2782
2902
|
case void 0:
|
|
2783
2903
|
io.print(HELP(io.fmt));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atolljs/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "atoll — scaffold worker pools, shared-memory contracts, and framework islands (experimental)",
|
|
5
5
|
"homepage": "https://jwhenry3.github.io/atolljs/consumer/quickstart/",
|
|
6
6
|
"type": "module",
|
package/src/cli.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { consoleIo, type Fmt, type Io } from './io.ts';
|
|
14
14
|
import { hasFlag, parseArgs } from './parse.ts';
|
|
15
|
-
import { runAdd, runCreate, runDoctor, runInit, type Ctx } from './commands.ts';
|
|
15
|
+
import { runAdd, runCreate, runDevtools, runDoctor, runInit, type Ctx } from './commands.ts';
|
|
16
16
|
|
|
17
17
|
const HELP = (fmt: Fmt) => `${fmt.strong('atoll')} — bootstrap multithreading ${fmt.dim('(experimental)')}
|
|
18
18
|
|
|
@@ -38,6 +38,8 @@ const HELP = (fmt: Fmt) => `${fmt.strong('atoll')} — bootstrap multithreading
|
|
|
38
38
|
route nextjs: api route pool — task | client variants
|
|
39
39
|
component nextjs: 'use client' hooks component over a pool
|
|
40
40
|
instrumentation nextjs: server bootstrap that warms route pools
|
|
41
|
+
devtools observability — init module + @atolljs/devtools install
|
|
42
|
+
(browser: ?__atoll_devtools + /__atoll/ · node: ATOLL_DEVTOOLS=1)
|
|
41
43
|
|
|
42
44
|
Frameworks: react vue solid svelte angular nestjs nextjs node
|
|
43
45
|
[--dir <path>] [--memory <name>|--no-memory] [--variant <v>] [--force]
|
|
@@ -46,6 +48,10 @@ const HELP = (fmt: Fmt) => `${fmt.strong('atoll')} — bootstrap multithreading
|
|
|
46
48
|
Check the setup — deps, lockfile, .npmrc policy, bundler-detectable
|
|
47
49
|
worker entries, COOP/COEP headers. --fix writes a hardened .npmrc.
|
|
48
50
|
|
|
51
|
+
${fmt.accent('atoll devtools')} [--port 4780]
|
|
52
|
+
Serve the local devtools dashboard — pools, task timings, shared-memory
|
|
53
|
+
writes, island traffic. Apps connect via connectDevtools().
|
|
54
|
+
|
|
49
55
|
${fmt.dim(`flags: --yes/-y accepts defaults, --force overwrites existing files,
|
|
50
56
|
--no-install skips dependency installation`)}
|
|
51
57
|
`;
|
|
@@ -72,6 +78,8 @@ export async function runCli(argv: readonly string[], io: Io, cwd: string): Prom
|
|
|
72
78
|
return await runAdd(ctx);
|
|
73
79
|
case 'doctor':
|
|
74
80
|
return runDoctor(ctx);
|
|
81
|
+
case 'devtools':
|
|
82
|
+
return await runDevtools(ctx);
|
|
75
83
|
case 'help':
|
|
76
84
|
case undefined:
|
|
77
85
|
io.print(HELP(io.fmt));
|
package/src/commands.ts
CHANGED
|
@@ -145,6 +145,7 @@ const ADD_KINDS = [
|
|
|
145
145
|
'route',
|
|
146
146
|
'component',
|
|
147
147
|
'instrumentation',
|
|
148
|
+
'devtools',
|
|
148
149
|
] as const;
|
|
149
150
|
type AddKind = (typeof ADD_KINDS)[number];
|
|
150
151
|
|
|
@@ -161,6 +162,7 @@ const KIND_SCOPE: Record<AddKind, readonly Framework[] | null> = {
|
|
|
161
162
|
route: ['nextjs'],
|
|
162
163
|
component: ['nextjs'],
|
|
163
164
|
instrumentation: ['nextjs'],
|
|
165
|
+
devtools: null,
|
|
164
166
|
};
|
|
165
167
|
|
|
166
168
|
/**
|
|
@@ -283,7 +285,12 @@ export async function runAdd(ctx: Ctx): Promise<number> {
|
|
|
283
285
|
nameArg = rest[1];
|
|
284
286
|
}
|
|
285
287
|
|
|
286
|
-
|
|
288
|
+
// devtools writes a fixed devtools.ts — a positional/--name argument
|
|
289
|
+
// becomes the dashboard session label, not a file name.
|
|
290
|
+
const name =
|
|
291
|
+
kind === 'devtools'
|
|
292
|
+
? (nameArg ?? flag(ctx.args, 'name') ?? project.pkg?.name?.split('/').pop() ?? 'app')
|
|
293
|
+
: await pickName(ctx, kind, nameArg);
|
|
287
294
|
if (!name) return 1;
|
|
288
295
|
const force = { force: hasFlag(ctx.args, 'force') };
|
|
289
296
|
const atollDir = flag(ctx.args, 'dir') ?? rel(join(project.srcDir, 'atoll'));
|
|
@@ -480,6 +487,54 @@ export async function runAdd(ctx: Ctx): Promise<number> {
|
|
|
480
487
|
return 0;
|
|
481
488
|
}
|
|
482
489
|
|
|
490
|
+
case 'devtools': {
|
|
491
|
+
const host = hostOf(framework);
|
|
492
|
+
// Angular's CLI dev server mounts no /__atoll/ — stream to the
|
|
493
|
+
// aggregate server instead of the BroadcastChannel + flyout path.
|
|
494
|
+
const wsUrl = host === 'browser' && framework === 'angular'
|
|
495
|
+
? 'ws://127.0.0.1:4780/events'
|
|
496
|
+
: undefined;
|
|
497
|
+
const file = `${atollDir}/devtools.ts`;
|
|
498
|
+
await writeTree(
|
|
499
|
+
ctx.io,
|
|
500
|
+
ctx.cwd,
|
|
501
|
+
[{ path: file, content: T.devtoolsSetup(name, host, wsUrl) }],
|
|
502
|
+
force,
|
|
503
|
+
);
|
|
504
|
+
if (missingDeps(project.pkg, ['@atolljs/devtools']).length) {
|
|
505
|
+
await offerInstall(ctx, project, ['-D', '@atolljs/devtools']);
|
|
506
|
+
}
|
|
507
|
+
// Print the specifier relative to srcDir — the import lands in the
|
|
508
|
+
// app entry (src/main.ts style), not in cwd.
|
|
509
|
+
const srcRel = rel(project.srcDir);
|
|
510
|
+
const spec = file.startsWith(`${srcRel}/`)
|
|
511
|
+
? `./${file.slice(srcRel.length + 1).replace(/\.ts$/, '')}`
|
|
512
|
+
: `./${file.replace(/\.ts$/, '')}`;
|
|
513
|
+
ctx.io.print('');
|
|
514
|
+
ctx.io.print(ctx.io.fmt.strong('wire it:'));
|
|
515
|
+
ctx.io.print(` import the module first in your entry — before any pool spawns:`);
|
|
516
|
+
ctx.io.print(ctx.io.fmt.accent(` import '${spec}';`));
|
|
517
|
+
if (host === 'node') {
|
|
518
|
+
ctx.io.print(
|
|
519
|
+
` ${ctx.io.fmt.accent('ATOLL_DEVTOOLS=1')} starts streaming · ${ctx.io.fmt.accent('atoll devtools')} serves the dashboard`,
|
|
520
|
+
);
|
|
521
|
+
} else if (wsUrl) {
|
|
522
|
+
ctx.io.print(
|
|
523
|
+
` ${ctx.io.fmt.accent('atoll devtools')} serves the aggregate dashboard · open the app with ${ctx.io.fmt.accent('?__atoll_devtools')}`,
|
|
524
|
+
);
|
|
525
|
+
} else {
|
|
526
|
+
ctx.io.print(
|
|
527
|
+
` open the app with ${ctx.io.fmt.accent('?__atoll_devtools')} — the flyout mounts and /__atoll/ serves the dashboard`,
|
|
528
|
+
);
|
|
529
|
+
ctx.io.print(
|
|
530
|
+
ctx.io.fmt.dim(
|
|
531
|
+
` /__atoll/ needs the @atolljs/vite plugin (plugins: [atoll()]) — no plugin? run \`atoll devtools\` + pass transport: 'websocket'`,
|
|
532
|
+
),
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
483
538
|
default:
|
|
484
539
|
ctx.io.error(`unhandled add kind ${kind}`);
|
|
485
540
|
return 1;
|
|
@@ -557,6 +612,39 @@ export async function runCreate(ctx: Ctx): Promise<number> {
|
|
|
557
612
|
return 0;
|
|
558
613
|
}
|
|
559
614
|
|
|
615
|
+
/* ── atoll devtools ─────────────────────────────────────────────────────── */
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* `atoll devtools [--port N]` — boot the local devtools server + dashboard.
|
|
619
|
+
* The server lives in @atolljs/devtools: resolved as an installed package in
|
|
620
|
+
* consumer projects, with a relative fallback so `npm run atoll devtools`
|
|
621
|
+
* works inside the atoll monorepo where the package isn't installed.
|
|
622
|
+
*
|
|
623
|
+
* Never resolves — the process serves until SIGINT.
|
|
624
|
+
*/
|
|
625
|
+
export async function runDevtools(ctx: Ctx): Promise<number> {
|
|
626
|
+
const { io } = ctx;
|
|
627
|
+
const spec = '@atolljs/devtools/server';
|
|
628
|
+
const mod: { createDevtoolsServer?: (o: { port: number }) => Promise<{ url: string }> } | null =
|
|
629
|
+
await import(spec).catch(async () => {
|
|
630
|
+
// In-repo fallback: the workspace package's built dist (extensionless
|
|
631
|
+
// sources can't run under type stripping — `npm run build` there first).
|
|
632
|
+
const local = new URL('../../devtools/dist/server.js', import.meta.url).href;
|
|
633
|
+
return import(local).catch(() => null);
|
|
634
|
+
});
|
|
635
|
+
if (!mod?.createDevtoolsServer) {
|
|
636
|
+
io.error(`@atolljs/devtools isn't installed — ${io.fmt.accent('npm i -D @atolljs/devtools')}`);
|
|
637
|
+
return 1;
|
|
638
|
+
}
|
|
639
|
+
const port = Number(flag(ctx.args, 'port') ?? 4780);
|
|
640
|
+
const server = await mod.createDevtoolsServer({ port: Number.isFinite(port) ? port : 4780 });
|
|
641
|
+
io.print(`atoll devtools → ${io.fmt.accent(server.url)}`);
|
|
642
|
+
io.print(io.fmt.dim(' ingest endpoint: ' + server.url.replace('http://', 'ws://') + '/events'));
|
|
643
|
+
io.print(io.fmt.dim(" in the app: connectDevtools() from '@atolljs/devtools'"));
|
|
644
|
+
io.warn('serving — Ctrl+C to stop');
|
|
645
|
+
return new Promise<number>(() => {});
|
|
646
|
+
}
|
|
647
|
+
|
|
560
648
|
/* ── atoll doctor ───────────────────────────────────────────────────────── */
|
|
561
649
|
|
|
562
650
|
interface Check {
|
package/src/templates.ts
CHANGED
|
@@ -2163,3 +2163,66 @@ export async function register() {
|
|
|
2163
2163
|
};
|
|
2164
2164
|
}
|
|
2165
2165
|
|
|
2166
|
+
|
|
2167
|
+
/**
|
|
2168
|
+
* `add devtools` — the init module, imported first from the app entry so the
|
|
2169
|
+
* sink is installed before any pool spawns (worker INIT carries the flag at
|
|
2170
|
+
* spawn time; late installs leave existing workers silent).
|
|
2171
|
+
*
|
|
2172
|
+
* Three shapes:
|
|
2173
|
+
* - node host → '@atolljs/devtools/node' entry, ATOLL_DEVTOOLS env gate
|
|
2174
|
+
* - browser → '@atolljs/devtools', ?__atoll_devtools URL gate + overlay
|
|
2175
|
+
* - browser + wsUrl → same browser gate but streams to the aggregate server
|
|
2176
|
+
* (angular: nothing mounts /__atoll/)
|
|
2177
|
+
*/
|
|
2178
|
+
export function devtoolsSetup(name: string, host: 'browser' | 'node', wsUrl?: string): string {
|
|
2179
|
+
if (host === 'node') {
|
|
2180
|
+
return `import { initDevtools } from '@atolljs/devtools/node';
|
|
2181
|
+
|
|
2182
|
+
/**
|
|
2183
|
+
* Devtools sink — imported first from the entry so it's installed before any
|
|
2184
|
+
* pool spawns (worker INIT carries the flag at spawn time; late installs
|
|
2185
|
+
* leave existing workers silent).
|
|
2186
|
+
*
|
|
2187
|
+
* No-op unless the process was started with ATOLL_DEVTOOLS=1 — then events
|
|
2188
|
+
* stream over WebSocket to the standalone dashboard (\`atoll devtools\`,
|
|
2189
|
+
* http://127.0.0.1:4780). Node has no BroadcastChannel page context, so the
|
|
2190
|
+
* aggregate server is the transport.
|
|
2191
|
+
*/
|
|
2192
|
+
initDevtools({ session: { name: '${name}' } });
|
|
2193
|
+
`;
|
|
2194
|
+
}
|
|
2195
|
+
if (wsUrl) {
|
|
2196
|
+
return `import { initDevtools } from '@atolljs/devtools';
|
|
2197
|
+
|
|
2198
|
+
/**
|
|
2199
|
+
* Devtools sink — imported first from the app entry so it's installed before
|
|
2200
|
+
* any pool spawns (worker INIT carries the flag at spawn time; late installs
|
|
2201
|
+
* leave existing workers silent).
|
|
2202
|
+
*
|
|
2203
|
+
* No-op unless the page URL carries ?__atoll_devtools — then events stream
|
|
2204
|
+
* over WebSocket to the standalone aggregate dashboard (\`atoll devtools\`,
|
|
2205
|
+
* http://127.0.0.1:4780). This app's dev server doesn't mount /__atoll/, so
|
|
2206
|
+
* the overlay is off and the aggregate server is the viewer.
|
|
2207
|
+
*/
|
|
2208
|
+
initDevtools({
|
|
2209
|
+
session: { name: '${name}' },
|
|
2210
|
+
overlay: false,
|
|
2211
|
+
url: '${wsUrl}',
|
|
2212
|
+
});
|
|
2213
|
+
`;
|
|
2214
|
+
}
|
|
2215
|
+
return `import { initDevtools } from '@atolljs/devtools';
|
|
2216
|
+
|
|
2217
|
+
/**
|
|
2218
|
+
* Devtools sink — imported first from the app entry so it's installed before
|
|
2219
|
+
* any pool spawns (worker INIT carries the flag at spawn time; late installs
|
|
2220
|
+
* leave existing workers silent).
|
|
2221
|
+
*
|
|
2222
|
+
* No-op unless the page URL carries ?__atoll_devtools — then events stream
|
|
2223
|
+
* over BroadcastChannel on the app's own origin and a flyout overlay mounts.
|
|
2224
|
+
* /__atoll/ itself is served by the @atolljs/vite plugin (plugins: [atoll()]).
|
|
2225
|
+
*/
|
|
2226
|
+
initDevtools({ session: { name: '${name}' } });
|
|
2227
|
+
`;
|
|
2228
|
+
}
|