everything-dev 0.1.2 → 0.1.4

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,115 @@
1
+ import type { Snapshot } from "../resource-monitor";
2
+
3
+ export interface SessionConfig {
4
+ ports: number[];
5
+ snapshotIntervalMs: number;
6
+ headless: boolean;
7
+ baseUrl: string;
8
+ timeout: number;
9
+ outputPath?: string;
10
+ devMode?: "local" | "remote";
11
+ }
12
+
13
+ export interface BrowserMetrics {
14
+ jsHeapUsedSize: number;
15
+ jsHeapTotalSize: number;
16
+ documents: number;
17
+ frames: number;
18
+ jsEventListeners: number;
19
+ nodes: number;
20
+ layoutCount: number;
21
+ recalcStyleCount: number;
22
+ scriptDuration: number;
23
+ taskDuration: number;
24
+ }
25
+
26
+ export type SessionEventType =
27
+ | "baseline"
28
+ | "interval"
29
+ | "pageload"
30
+ | "navigation"
31
+ | "click"
32
+ | "popup_open"
33
+ | "popup_close"
34
+ | "auth_start"
35
+ | "auth_complete"
36
+ | "auth_failed"
37
+ | "error"
38
+ | "custom";
39
+
40
+ export interface SessionEvent {
41
+ id: string;
42
+ timestamp: number;
43
+ type: SessionEventType;
44
+ label: string;
45
+ snapshot: Snapshot;
46
+ browserMetrics?: BrowserMetrics;
47
+ url?: string;
48
+ error?: string;
49
+ metadata?: Record<string, unknown>;
50
+ }
51
+
52
+ export interface SessionSummary {
53
+ totalMemoryDeltaMb: number;
54
+ peakMemoryMb: number;
55
+ averageMemoryMb: number;
56
+ processesSpawned: number;
57
+ processesKilled: number;
58
+ orphanedProcesses: number;
59
+ portsUsed: number[];
60
+ portsLeaked: number;
61
+ hasLeaks: boolean;
62
+ eventCount: number;
63
+ duration: number;
64
+ browserMetricsSummary?: {
65
+ peakJsHeapMb: number;
66
+ averageJsHeapMb: number;
67
+ totalLayoutCount: number;
68
+ totalScriptDuration: number;
69
+ };
70
+ }
71
+
72
+ export interface SessionReport {
73
+ sessionId: string;
74
+ config: SessionConfig;
75
+ startTime: number;
76
+ endTime: number;
77
+ events: SessionEvent[];
78
+ summary: SessionSummary;
79
+ platform: NodeJS.Platform;
80
+ nodeVersion: string;
81
+ }
82
+
83
+ export interface ServerHandle {
84
+ pid: number;
85
+ port: number;
86
+ name: string;
87
+ kill: () => Promise<void>;
88
+ waitForExit: (timeoutMs?: number) => Promise<number | null>;
89
+ }
90
+
91
+ export interface ServerOrchestrator {
92
+ handles: ServerHandle[];
93
+ ports: number[];
94
+ shutdown: () => Promise<void>;
95
+ waitForReady: () => Promise<boolean>;
96
+ }
97
+
98
+ export type SessionFlow = (context: FlowContext) => Promise<void>;
99
+
100
+ export interface FlowContext {
101
+ page: unknown;
102
+ context: unknown;
103
+ recordEvent: (type: SessionEventType, label: string, metadata?: Record<string, unknown>) => Promise<void>;
104
+ headless: boolean;
105
+ baseUrl: string;
106
+ }
107
+
108
+ export const DEFAULT_SESSION_CONFIG: SessionConfig = {
109
+ ports: [3000, 3002, 3014],
110
+ snapshotIntervalMs: 2000,
111
+ headless: true,
112
+ baseUrl: "http://localhost:3000",
113
+ timeout: 120000,
114
+ devMode: "remote",
115
+ };
package/src/plugin.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { createPlugin } from "every-plugin";
2
2
  import { Effect } from "every-plugin/effect";
3
3
  import { z } from "every-plugin/zod";
4
- import { Graph, calculateRequiredDeposit } from "near-social-js";
4
+ import { calculateRequiredDeposit, Graph } from "near-social-js";
5
5
 
6
- import { createProcessRegistry } from "./lib/process-registry";
6
+ import { runMonitorCli } from "./components/monitor-view";
7
7
  import {
8
8
  type AppConfig,
9
9
  type BosConfig as BosConfigType,
@@ -18,7 +18,7 @@ import {
18
18
  loadConfig,
19
19
  type RemoteConfig,
20
20
  resolvePackageModes,
21
- type SourceMode,
21
+ type SourceMode,
22
22
  setConfig
23
23
  } from "./config";
24
24
  import { bosContract } from "./contract";
@@ -37,6 +37,19 @@ import {
37
37
  verifyNovaCredentials
38
38
  } from "./lib/nova";
39
39
  import { type AppOrchestrator, startApp } from "./lib/orchestrator";
40
+ import { createProcessRegistry } from "./lib/process-registry";
41
+ import {
42
+ createSnapshotWithPlatform,
43
+ formatSnapshotSummary,
44
+ runWithInfo
45
+ } from "./lib/resource-monitor";
46
+ import {
47
+ formatReportSummary,
48
+ navigateTo,
49
+ runLoginFlow,
50
+ runNavigationFlow,
51
+ SessionRecorder,
52
+ } from "./lib/session-recorder";
40
53
  import { syncFiles } from "./lib/sync";
41
54
  import { run } from "./utils/run";
42
55
  import { colors, icons } from "./utils/theme";
@@ -76,8 +89,8 @@ function getSocialContract(network: "mainnet" | "testnet"): string {
76
89
  }
77
90
 
78
91
  function getSocialExplorerUrl(network: "mainnet" | "testnet", path: string): string {
79
- const baseUrl = network === "testnet"
80
- ? "https://test.near.social"
92
+ const baseUrl = network === "testnet"
93
+ ? "https://test.near.social"
81
94
  : "https://near.social";
82
95
  return `${baseUrl}/${path}`;
83
96
  }
@@ -359,7 +372,7 @@ export default createPlugin({
359
372
 
360
373
  serve: builder.serve.handler(async ({ input }) => {
361
374
  const port = input.port;
362
-
375
+
363
376
  return {
364
377
  status: "serving" as const,
365
378
  url: `http://localhost:${port}`,
@@ -481,7 +494,7 @@ export default createPlugin({
481
494
  };
482
495
  const argsBase64 = Buffer.from(JSON.stringify(socialArgs)).toString("base64");
483
496
 
484
- const graph = new Graph({
497
+ const graph = new Graph({
485
498
  network,
486
499
  contractId: socialContract,
487
500
  });
@@ -756,7 +769,12 @@ export default createPlugin({
756
769
  const novaConfig = yield* getNovaConfig;
757
770
  const nova = createNovaClient(novaConfig);
758
771
 
759
- yield* registerSecretsGroup(nova, fullAccount, parentAccount);
772
+ const gatewayNovaAccount = bosConfig.gateway?.nova?.account;
773
+ if (!gatewayNovaAccount) {
774
+ return yield* Effect.fail(new Error("gateway.nova.account is required for secrets registration"));
775
+ }
776
+
777
+ yield* registerSecretsGroup(nova, fullAccount, gatewayNovaAccount);
760
778
 
761
779
  return {
762
780
  status: "registered" as const,
@@ -1101,7 +1119,7 @@ export default createPlugin({
1101
1119
 
1102
1120
  try {
1103
1121
  const gatewayDomain = getGatewayDomain(bosConfig);
1104
- const gatewayAccount = bosConfig.account;
1122
+ const gatewayAccount = bosConfig.gateway?.account || bosConfig.account;
1105
1123
 
1106
1124
  const wranglerContent = await Bun.file(wranglerPath).text();
1107
1125
 
@@ -1356,11 +1374,11 @@ export default createPlugin({
1356
1374
 
1357
1375
  const mergeAppConfig = (localApp: Record<string, unknown>, remoteApp: Record<string, unknown>): Record<string, unknown> => {
1358
1376
  const merged: Record<string, unknown> = {};
1359
-
1377
+
1360
1378
  for (const key of Object.keys(remoteApp)) {
1361
1379
  const local = localApp[key] as Record<string, unknown> | undefined;
1362
1380
  const remote = remoteApp[key] as Record<string, unknown>;
1363
-
1381
+
1364
1382
  if (!local) {
1365
1383
  merged[key] = remote;
1366
1384
  continue;
@@ -1379,7 +1397,7 @@ export default createPlugin({
1379
1397
  },
1380
1398
  };
1381
1399
  }
1382
-
1400
+
1383
1401
  return merged;
1384
1402
  };
1385
1403
 
@@ -1603,7 +1621,7 @@ export default createPlugin({
1603
1621
  const port = input.port || (input.target === "development" ? 4000 : 3000);
1604
1622
 
1605
1623
  const args = ["run"];
1606
-
1624
+
1607
1625
  if (input.detach) {
1608
1626
  args.push("-d");
1609
1627
  }
@@ -1625,8 +1643,8 @@ export default createPlugin({
1625
1643
  args.push("-e", `BOS_ACCOUNT=${bosConfig.account}`);
1626
1644
  const gateway = bosConfig.gateway as { production?: string } | string | undefined;
1627
1645
  if (gateway) {
1628
- const domain = typeof gateway === "string"
1629
- ? gateway
1646
+ const domain = typeof gateway === "string"
1647
+ ? gateway
1630
1648
  : gateway.production?.replace(/^https?:\/\//, "") || "";
1631
1649
  if (domain) {
1632
1650
  args.push("-e", `GATEWAY_DOMAIN=${domain}`);
@@ -1683,14 +1701,14 @@ export default createPlugin({
1683
1701
  stopped.push(input.containerId!);
1684
1702
  } else if (input.all) {
1685
1703
  const imageName = bosConfig?.account?.replace(/\./g, "-") || "bos-app";
1686
-
1704
+
1687
1705
  const psResult = yield* Effect.tryPromise({
1688
1706
  try: () => execa("docker", ["ps", "-q", "--filter", `ancestor=${imageName}`]),
1689
1707
  catch: () => new Error("Failed to list containers"),
1690
1708
  });
1691
1709
 
1692
1710
  const containerIds = psResult.stdout.trim().split("\n").filter(Boolean);
1693
-
1711
+
1694
1712
  for (const id of containerIds) {
1695
1713
  yield* Effect.tryPromise({
1696
1714
  try: () => execa("docker", ["stop", id]),
@@ -1716,6 +1734,132 @@ export default createPlugin({
1716
1734
  };
1717
1735
  }
1718
1736
  }),
1737
+
1738
+ monitor: builder.monitor.handler(async ({ input }) => {
1739
+ try {
1740
+ if (input.json) {
1741
+ const snapshot = await runWithInfo(
1742
+ createSnapshotWithPlatform(input.ports ? { ports: input.ports } : undefined)
1743
+ );
1744
+ return {
1745
+ status: "snapshot" as const,
1746
+ snapshot: snapshot as any,
1747
+ };
1748
+ }
1749
+
1750
+ if (input.watch) {
1751
+ runMonitorCli({ ports: input.ports, json: false });
1752
+ return {
1753
+ status: "watching" as const,
1754
+ };
1755
+ }
1756
+
1757
+ const snapshot = await runWithInfo(
1758
+ createSnapshotWithPlatform(input.ports ? { ports: input.ports } : undefined)
1759
+ );
1760
+ console.log(formatSnapshotSummary(snapshot));
1761
+
1762
+ return {
1763
+ status: "snapshot" as const,
1764
+ snapshot: snapshot as any,
1765
+ };
1766
+ } catch (error) {
1767
+ return {
1768
+ status: "error" as const,
1769
+ error: error instanceof Error ? error.message : "Unknown error",
1770
+ };
1771
+ }
1772
+ }),
1773
+
1774
+ session: builder.session.handler(async ({ input }) => {
1775
+ const sessionEffect = Effect.gen(function* () {
1776
+ const recorder = yield* SessionRecorder.create({
1777
+ ports: [3000],
1778
+ snapshotIntervalMs: input.snapshotInterval,
1779
+ headless: input.headless,
1780
+ baseUrl: "http://localhost:3000",
1781
+ timeout: input.timeout,
1782
+ });
1783
+
1784
+ try {
1785
+ yield* recorder.startServers("start");
1786
+
1787
+ yield* recorder.startRecording();
1788
+
1789
+ const browser = yield* recorder.launchBrowser();
1790
+
1791
+ if (input.flow === "login") {
1792
+ yield* runLoginFlow(browser, {
1793
+ recordEvent: (type, label, metadata) =>
1794
+ recorder.recordEvent(type, label, metadata).pipe(
1795
+ Effect.asVoid,
1796
+ Effect.catchAll(() => Effect.void)
1797
+ ),
1798
+ }, {
1799
+ baseUrl: "http://localhost:3000",
1800
+ headless: input.headless,
1801
+ stubWallet: input.headless,
1802
+ timeout: 30000,
1803
+ });
1804
+ } else if (input.flow === "navigation" && input.routes) {
1805
+ yield* runNavigationFlow(
1806
+ browser,
1807
+ {
1808
+ recordEvent: (type, label, metadata) =>
1809
+ recorder.recordEvent(type, label, metadata).pipe(
1810
+ Effect.asVoid,
1811
+ Effect.catchAll(() => Effect.void)
1812
+ ),
1813
+ },
1814
+ input.routes,
1815
+ "http://localhost:3000"
1816
+ );
1817
+ } else {
1818
+ yield* navigateTo(browser.page, "http://localhost:3000");
1819
+ yield* Effect.sleep("5 seconds");
1820
+ }
1821
+
1822
+ yield* recorder.cleanup();
1823
+
1824
+ const report = yield* recorder.stopRecording();
1825
+
1826
+ yield* recorder.exportReport(input.output, input.format);
1827
+
1828
+ console.log(formatReportSummary(report));
1829
+
1830
+ return {
1831
+ status: report.summary.hasLeaks ? "leaks_detected" as const : "completed" as const,
1832
+ sessionId: recorder.getSessionId(),
1833
+ reportPath: input.output,
1834
+ summary: {
1835
+ totalMemoryDeltaMb: report.summary.totalMemoryDeltaMb,
1836
+ peakMemoryMb: report.summary.peakMemoryMb,
1837
+ averageMemoryMb: report.summary.averageMemoryMb,
1838
+ processesSpawned: report.summary.processesSpawned,
1839
+ processesKilled: report.summary.processesKilled,
1840
+ orphanedProcesses: report.summary.orphanedProcesses,
1841
+ portsUsed: report.summary.portsUsed,
1842
+ portsLeaked: report.summary.portsLeaked,
1843
+ hasLeaks: report.summary.hasLeaks,
1844
+ eventCount: report.summary.eventCount,
1845
+ duration: report.summary.duration,
1846
+ },
1847
+ };
1848
+ } catch (error) {
1849
+ yield* recorder.cleanup();
1850
+ throw error;
1851
+ }
1852
+ });
1853
+
1854
+ try {
1855
+ return await Effect.runPromise(sessionEffect);
1856
+ } catch (error) {
1857
+ return {
1858
+ status: "error" as const,
1859
+ error: error instanceof Error ? error.message : "Unknown error",
1860
+ };
1861
+ }
1862
+ }),
1719
1863
  }),
1720
1864
  });
1721
1865
 
package/src/types.ts CHANGED
@@ -30,9 +30,16 @@ export const RemoteConfigSchema = z.object({
30
30
  });
31
31
  export type RemoteConfig = z.infer<typeof RemoteConfigSchema>;
32
32
 
33
+ export const NovaConfigSchema = z.object({
34
+ account: z.string(),
35
+ });
36
+ export type NovaConfig = z.infer<typeof NovaConfigSchema>;
37
+
33
38
  export const GatewayConfigSchema = z.object({
34
39
  development: z.string(),
35
40
  production: z.string(),
41
+ account: z.string().optional(),
42
+ nova: NovaConfigSchema.optional(),
36
43
  });
37
44
  export type GatewayConfig = z.infer<typeof GatewayConfigSchema>;
38
45
 
@@ -54,6 +61,7 @@ export type SyncConfig = z.infer<typeof SyncConfigSchema>;
54
61
  export const BosConfigSchema = z.object({
55
62
  account: z.string(),
56
63
  testnet: z.string().optional(),
64
+ nova: NovaConfigSchema.optional(),
57
65
  gateway: GatewayConfigSchema,
58
66
  template: z.string().optional(),
59
67
  cli: z.object({