@zenbujs/core 0.0.9 → 0.0.12

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.
Files changed (82) hide show
  1. package/dist/advice-config-BiYhyeTz.d.mts +41 -0
  2. package/dist/advice.d.mts +2 -36
  3. package/dist/advice.mjs +2 -2
  4. package/dist/{base-window-BxBZ2md_.mjs → base-window-4P-fVvC_.mjs} +37 -26
  5. package/dist/{build-config-Dzg2frpk.d.mts → build-config-GF0XzR_Y.d.mts} +42 -18
  6. package/dist/{build-config-pWdmLnrk.mjs → build-config-HMMqpXI1.mjs} +0 -8
  7. package/dist/{build-electron-Dsbb1EMl.mjs → build-electron-Di_FE62r.mjs} +10 -6
  8. package/dist/{build-source-d1J3shV8.mjs → build-source-BIaWpaxE.mjs} +2 -2
  9. package/dist/cli/bin.mjs +7 -7
  10. package/dist/cli/build.d.mts +1 -1
  11. package/dist/cli/build.mjs +1 -1
  12. package/dist/cli/resolve-config.mjs +6 -1
  13. package/dist/{cli-kL6mPgBE.mjs → cli-5jFDJWM4.mjs} +4 -4
  14. package/dist/config.d.mts +3 -3
  15. package/dist/config.mjs +2 -2
  16. package/dist/{db-Bc292RYo.mjs → db-MkOccvBS.mjs} +2 -2
  17. package/dist/db.d.mts +3 -2
  18. package/dist/db.mjs +2 -10
  19. package/dist/{dev-B2emj0HZ.mjs → dev-BSDyzO4j.mjs} +3 -9
  20. package/dist/env-bootstrap.d.mts +1 -1
  21. package/dist/events.d.mts +0 -9
  22. package/dist/{host-version-BIrF8tX7.mjs → host-version-Cog_odmD.mjs} +4 -3
  23. package/dist/{index-CVF768Xs.d.mts → index-C0mXKol5.d.mts} +188 -143
  24. package/dist/{index-DeDxePAa.d.mts → index-FaexRVl_.d.mts} +13 -1
  25. package/dist/index.d.mts +4 -4
  26. package/dist/index.mjs +2 -2
  27. package/dist/launcher.mjs +64 -6
  28. package/dist/link-Bt3LB_NW.mjs +586 -0
  29. package/dist/{load-config-C4Oe2qZO.mjs → load-config-C2XloBaQ.mjs} +68 -5
  30. package/dist/node-loader.mjs +1 -1
  31. package/dist/{publish-source-Dq2c0iOw.mjs → publish-source-v93eB9kA.mjs} +6 -2
  32. package/dist/react.d.mts +6 -6
  33. package/dist/react.mjs +4 -4
  34. package/dist/registry-generated.d.mts +19 -14
  35. package/dist/registry-saQDMUhT.d.mts +13 -0
  36. package/dist/registry.d.mts +1 -1
  37. package/dist/{reloader-B22UiNA2.mjs → reloader-CFzxYa67.mjs} +3 -3
  38. package/dist/{renderer-host-DD16MXhI.mjs → renderer-host-Cw38dSDe.mjs} +35 -24
  39. package/dist/{rpc-C4_NQmpT.mjs → rpc-Dg9zwZ33.mjs} +4 -4
  40. package/dist/rpc.d.mts +1 -1
  41. package/dist/rpc.mjs +1 -1
  42. package/dist/runtime-DYUONc3S.mjs +861 -0
  43. package/dist/{runtime-BQWntcOb.d.mts → runtime-fnPDZFYM.d.mts} +100 -3
  44. package/dist/runtime.d.mts +2 -2
  45. package/dist/runtime.mjs +2 -578
  46. package/dist/{schema-CjrMVk36.d.mts → schema-brYpUjYO.d.mts} +13 -25
  47. package/dist/schema.d.mts +2 -2
  48. package/dist/schema.mjs +9 -2
  49. package/dist/{server-CZLMF8Dj.mjs → server-BJ2ZC2z2.mjs} +2 -2
  50. package/dist/services/default.d.mts +1 -5
  51. package/dist/services/default.mjs +12 -16
  52. package/dist/services/index.d.mts +1 -1
  53. package/dist/services/index.mjs +7 -7
  54. package/dist/setup-gate.d.mts +1 -1
  55. package/dist/setup-gate.mjs +20 -12
  56. package/dist/{transport-F2hv_OEm.mjs → transport-Bqlv9pmJ.mjs} +1 -1
  57. package/dist/updater-Bs1Jtem6.mjs +480 -0
  58. package/dist/{vite-plugins-tt6KAtyE.mjs → vite-plugins-Df-cfldF.mjs} +2 -49
  59. package/dist/vite.d.mts +0 -5
  60. package/dist/vite.mjs +1 -1
  61. package/dist/{window-YFKvAM0l.mjs → window-DgB70qeZ.mjs} +113 -22
  62. package/dist/{write-DgIRjo23.mjs → write-7IfKa_nq.mjs} +1 -1
  63. package/dist/zenbu-bg-parse-CIyPkJOY.mjs +46 -0
  64. package/package.json +19 -18
  65. package/LICENSE +0 -11
  66. package/dist/advice-config-DXSIo0sg.mjs +0 -154
  67. package/dist/link-glX89NV5.mjs +0 -673
  68. package/dist/registry-CMp8FYgS.d.mts +0 -47
  69. package/dist/updater-BtB_Ki1r.mjs +0 -1011
  70. /package/dist/{config-BK78JDRI.mjs → config-DfciRzDu.mjs} +0 -0
  71. /package/dist/{env-bootstrap-rTs8KR3-.d.mts → env-bootstrap-UBug-4Kw.d.mts} +0 -0
  72. /package/dist/{index-C-ALz_SH.d.mts → index-CSMHYi3u.d.mts} +0 -0
  73. /package/dist/{index-ClXLQ1fw.d.mts → index-DJOHDG5e.d.mts} +0 -0
  74. /package/dist/{log-6rzaCV0I.mjs → log-BkRqDwwB.mjs} +0 -0
  75. /package/dist/{mirror-sync-pYU6f3-c.mjs → mirror-sync-snqh9kEp.mjs} +0 -0
  76. /package/dist/{monorepo-Dct-kkbQ.mjs → monorepo-CBzK3l2i.mjs} +0 -0
  77. /package/dist/{node-BhfLKYCi.mjs → node-BuHlEsE4.mjs} +0 -0
  78. /package/dist/{schema-Ca7SxXgS.mjs → schema-C6k0SroY.mjs} +0 -0
  79. /package/dist/{setup-gate-BQq0QgZH.d.mts → setup-gate-DkysEZQO.d.mts} +0 -0
  80. /package/dist/{src-Cven45mq.mjs → src-BpZAt9zL.mjs} +0 -0
  81. /package/dist/{trace-BaVg0rnY.mjs → trace-BVcQSD59.mjs} +0 -0
  82. /package/dist/{transform-BzrwkEdf.mjs → transform-czrcGnVV.mjs} +0 -0
package/dist/vite.d.mts CHANGED
@@ -51,11 +51,6 @@ declare function advicePreludePlugin(): Plugin;
51
51
  */
52
52
  declare function zenbuAdviceTransform(): Plugin;
53
53
  /**
54
- * Returns the framework Vite plugins in the canonical order. Auto-injected
55
- * by `ReloaderService` for every renderer; exported for advanced users who
56
- * want to compose them into a custom Vite stack.
57
- *
58
- * Order matters:
59
54
  * 1. `zenbuFrameworkResolve` — `enforce: "pre"`, runs first so it gets
60
55
  * `react` / `@zenbujs/core/*` *before* Vite's default resolver walks
61
56
  * up and finds plugin-local copies.
package/dist/vite.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as zenbuVitePlugins, i as zenbuFrameworkResolve, n as resolveAdviceRuntime, r as zenbuAdviceTransform, t as advicePreludePlugin } from "./vite-plugins-tt6KAtyE.mjs";
1
+ import { a as zenbuVitePlugins, i as zenbuFrameworkResolve, n as resolveAdviceRuntime, r as zenbuAdviceTransform, t as advicePreludePlugin } from "./vite-plugins-Df-cfldF.mjs";
2
2
  export { advicePreludePlugin, resolveAdviceRuntime, zenbuAdviceTransform, zenbuFrameworkResolve, zenbuVitePlugins };
@@ -1,10 +1,10 @@
1
1
  import { n as __exportAll } from "./chunk-DsiFFCwN.mjs";
2
- import { Service, runtime } from "./runtime.mjs";
3
- import { t as createLogger } from "./log-6rzaCV0I.mjs";
4
- import { r as ViewRegistryService, s as HttpService, t as RendererHostService } from "./renderer-host-DD16MXhI.mjs";
5
- import { i as entrypointBgColor, t as BaseWindowService } from "./base-window-BxBZ2md_.mjs";
2
+ import { f as runtime, n as Service } from "./runtime-DYUONc3S.mjs";
3
+ import { t as createLogger } from "./log-BkRqDwwB.mjs";
4
+ import { r as ViewRegistryService, s as HttpService, t as RendererHostService } from "./renderer-host-Cw38dSDe.mjs";
5
+ import { i as entrypointBgColor, t as BaseWindowService } from "./base-window-4P-fVvC_.mjs";
6
6
  import { URLSearchParams } from "node:url";
7
- import { WebContentsView, app, clipboard, dialog, shell } from "electron";
7
+ import { Menu, WebContentsView, app, clipboard, dialog, shell } from "electron";
8
8
  import electronContextMenu from "electron-context-menu";
9
9
  //#region src/services/window.ts
10
10
  var window_exports = /* @__PURE__ */ __exportAll({ WindowService: () => WindowService });
@@ -48,7 +48,7 @@ var WindowService = class extends Service.create({
48
48
  this.setup("activate-reopens-entrypoint", () => {
49
49
  const onActivate = () => {
50
50
  if (this.ctx.baseWindow.windows.size > 0) return;
51
- this.openView({ type: "app" }).catch((err) => {
51
+ this.openView({ type: "entrypoint" }).catch((err) => {
52
52
  log.error("activate-reopens-entrypoint: openView failed:", err);
53
53
  });
54
54
  };
@@ -57,13 +57,76 @@ var WindowService = class extends Service.create({
57
57
  app.removeListener("activate", onActivate);
58
58
  };
59
59
  });
60
+ this.setup("app-menu", () => this.installAppMenu());
61
+ }
62
+ /**
63
+ * Walk every mounted WebContentsView and return the one currently
64
+ * focused, so application-menu actions (reload, devtools) hit the view
65
+ * the user is actually looking at instead of the wrong window.
66
+ */
67
+ focusedMountedWebContents() {
68
+ for (const mounted of this.mounted.values()) {
69
+ const wc = mounted.view.webContents;
70
+ if (!wc.isDestroyed() && wc.isFocused()) return wc;
71
+ }
72
+ return null;
73
+ }
74
+ installAppMenu() {
75
+ const isMac = process.platform === "darwin";
76
+ const focusedWc = () => this.focusedMountedWebContents();
77
+ const viewSubmenu = [
78
+ {
79
+ label: "Reload",
80
+ accelerator: "CmdOrCtrl+R",
81
+ click: () => focusedWc()?.reload()
82
+ },
83
+ {
84
+ label: "Force Reload",
85
+ accelerator: "Shift+CmdOrCtrl+R",
86
+ click: () => focusedWc()?.reloadIgnoringCache()
87
+ },
88
+ {
89
+ label: "Toggle Developer Tools",
90
+ accelerator: isMac ? "Alt+Cmd+I" : "Ctrl+Shift+I",
91
+ click: () => {
92
+ const wc = focusedWc();
93
+ if (!wc) return;
94
+ if (wc.isDevToolsOpened()) wc.closeDevTools();
95
+ else wc.openDevTools({ mode: "detach" });
96
+ }
97
+ },
98
+ { type: "separator" },
99
+ { role: "resetZoom" },
100
+ { role: "zoomIn" },
101
+ { role: "zoomOut" },
102
+ { type: "separator" },
103
+ { role: "togglefullscreen" }
104
+ ];
105
+ const template = [
106
+ ...isMac ? [{ role: "appMenu" }] : [],
107
+ { role: "fileMenu" },
108
+ { role: "editMenu" },
109
+ {
110
+ label: "View",
111
+ submenu: viewSubmenu
112
+ },
113
+ { role: "windowMenu" }
114
+ ];
115
+ const previous = Menu.getApplicationMenu();
116
+ Menu.setApplicationMenu(Menu.buildFromTemplate(template));
117
+ return () => {
118
+ Menu.setApplicationMenu(previous);
119
+ };
60
120
  }
61
121
  async openView(args) {
62
122
  const entry = this.ctx.viewRegistry.get(args.type);
63
123
  if (!entry) throw new Error(`No registered view for type "${args.type}"`);
64
124
  const windowId = args.windowId ?? "main";
65
125
  let win = this.ctx.baseWindow.windows.get(windowId);
66
- if (!win) win = this.ctx.baseWindow.createWindow({ windowId }).win;
126
+ if (!win) win = this.ctx.baseWindow.createWindow({
127
+ windowId,
128
+ baseWindow: args.baseWindow
129
+ }).win;
67
130
  const existing = this.mounted.get(windowId);
68
131
  if (existing) {
69
132
  try {
@@ -75,25 +138,53 @@ var WindowService = class extends Service.create({
75
138
  existing.view.webContents.close();
76
139
  this.mounted.delete(windowId);
77
140
  }
78
- const view = new WebContentsView({ webPreferences: {
79
- nodeIntegration: false,
80
- contextIsolation: true,
81
- sandbox: true,
82
- ...args.view?.webPreferences
83
- } });
84
- view.setBackgroundColor(args.view?.backgroundColor ?? entrypointBgColor());
141
+ const view = new WebContentsView({
142
+ ...args.webContentsView,
143
+ webPreferences: {
144
+ nodeIntegration: false,
145
+ contextIsolation: true,
146
+ sandbox: true,
147
+ ...args.webContentsView?.webPreferences
148
+ }
149
+ });
150
+ view.setBackgroundColor(args.backgroundColor ?? entrypointBgColor());
85
151
  win.contentView.addChildView(view);
86
- const disposeContextMenu = electronContextMenu({
87
- window: view,
152
+ const disposeContextMenu = args.contextMenu === false ? () => {} : electronContextMenu({
88
153
  showInspectElement: true,
89
154
  showSearchWithGoogle: false,
90
- showSelectAll: true
155
+ showSelectAll: true,
156
+ prepend: (_defaults, _params, _win) => [
157
+ {
158
+ label: "Reload window",
159
+ click: () => {
160
+ try {
161
+ view.webContents.reload();
162
+ } catch (err) {
163
+ log.error("context menu: reload window failed:", err);
164
+ }
165
+ }
166
+ },
167
+ {
168
+ label: "Reload main process",
169
+ click: () => {
170
+ runtime.reloadAll().catch((err) => {
171
+ log.error("context menu: reload main process failed:", err);
172
+ });
173
+ }
174
+ },
175
+ { type: "separator" }
176
+ ],
177
+ ...args.contextMenu,
178
+ window: view
91
179
  });
92
- const handleInput = (_event, input) => {
93
- if ((input.key === "I" || input.key === "i") && (process.platform === "darwin" && input.alt && input.meta || process.platform !== "darwin" && input.shift && input.control) || input.key === "F12") if (view.webContents.isDevToolsOpened()) view.webContents.closeDevTools();
94
- else view.webContents.openDevTools({ mode: "detach" });
95
- };
96
- view.webContents.on("before-input-event", handleInput);
180
+ if (args.devtoolsShortcut !== false) {
181
+ const handleInput = (_event, input) => {
182
+ if (input.key !== "F12") return;
183
+ if (view.webContents.isDevToolsOpened()) view.webContents.closeDevTools();
184
+ else view.webContents.openDevTools({ mode: "detach" });
185
+ };
186
+ view.webContents.on("before-input-event", handleInput);
187
+ }
97
188
  const layout = () => {
98
189
  const { width, height } = win.getContentBounds();
99
190
  view.setBounds({
@@ -1,4 +1,4 @@
1
- import { n as traceKyjuSync, t as traceKyju } from "./trace-BaVg0rnY.mjs";
1
+ import { n as traceKyjuSync, t as traceKyju } from "./trace-BVcQSD59.mjs";
2
2
  import * as NFS from "node:fs";
3
3
  import * as OS from "node:os";
4
4
  import * as Path from "node:path";
@@ -0,0 +1,46 @@
1
+ //#region src/shared/zenbu-bg-parse.ts
2
+ const META_TAG_RE = /<meta\b([^>]*?)\/?>/gi;
3
+ const ATTR_RE = /([\w-]+)\s*=\s*["']([^"']+)["']/g;
4
+ /**
5
+ * Parse all `<meta name="zenbu-bg">` tags from `html`. Mirrors the W3C
6
+ * `<meta name="theme-color">` pattern: multiple tags carry a `media`
7
+ * attribute (typically `(prefers-color-scheme: light|dark)`), and an
8
+ * unmediated tag acts as the fallback.
9
+ *
10
+ * <meta name="zenbu-bg" content="#fafafa" media="(prefers-color-scheme: light)">
11
+ * <meta name="zenbu-bg" content="#09090b" media="(prefers-color-scheme: dark)">
12
+ * <meta name="zenbu-bg" content="#27272a"> <!-- default -->
13
+ */
14
+ function parseZenbuBgEntries(html) {
15
+ const out = [];
16
+ for (const tag of html.matchAll(META_TAG_RE)) {
17
+ const body = tag[1] ?? "";
18
+ const attrs = {};
19
+ for (const a of body.matchAll(ATTR_RE)) attrs[a[1].toLowerCase()] = a[2];
20
+ if (attrs.name === "zenbu-bg" && attrs.content) out.push({
21
+ color: attrs.content,
22
+ media: attrs.media ?? null
23
+ });
24
+ }
25
+ return out;
26
+ }
27
+ /**
28
+ * Pick the right entry given the current dark-mode preference. Search
29
+ * order:
30
+ * 1. First entry whose `media` matches `(prefers-color-scheme: <dark|light>)`.
31
+ * 2. First entry without a `media` attribute (the unconditional default).
32
+ * 3. The first entry, regardless of `media`.
33
+ *
34
+ * Returns `null` only when `entries` is empty.
35
+ */
36
+ function pickZenbuBgEntry(entries, dark) {
37
+ for (const e of entries) {
38
+ if (!e.media) continue;
39
+ if (dark && /prefers-color-scheme:\s*dark/i.test(e.media)) return e.color;
40
+ if (!dark && /prefers-color-scheme:\s*light/i.test(e.media)) return e.color;
41
+ }
42
+ for (const e of entries) if (!e.media) return e.color;
43
+ return entries[0]?.color ?? null;
44
+ }
45
+ //#endregion
46
+ export { pickZenbuBgEntry as n, parseZenbuBgEntries as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenbujs/core",
3
- "version": "0.0.9",
3
+ "version": "0.0.12",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -89,9 +89,19 @@
89
89
  "migrations",
90
90
  "package.json"
91
91
  ],
92
+ "scripts": {
93
+ "build": "node --max-old-space-size=8192 ./node_modules/tsdown/dist/run.mjs",
94
+ "prebuild": "pnpm link:types",
95
+ "dev": "tsdown --watch",
96
+ "link:types": "tsx ./src/cli/bin.ts link --types-config ./zenbu-types.config.json --out ./src/registry-generated.ts --augment-out ./types/zenbu-register.ts",
97
+ "db:generate": "node ./dist/cli/bin.mjs db generate --schema src/schema.ts --migrations ./migrations",
98
+ "test": "vitest run",
99
+ "typecheck": "tsc --noEmit -p tsconfig.json"
100
+ },
92
101
  "peerDependencies": {
93
102
  "electron": "^42.0.0",
94
- "react": ">=18"
103
+ "react": ">=18",
104
+ "zod": "^4"
95
105
  },
96
106
  "peerDependenciesMeta": {
97
107
  "react": {
@@ -101,6 +111,7 @@
101
111
  "dependencies": {
102
112
  "@parcel/watcher": "^2.5.0",
103
113
  "@vitejs/plugin-react": "^5.0.0",
114
+ "@zenbujs/hmr": "workspace:*",
104
115
  "dugite": "^3.2.1",
105
116
  "effect": "^3.21.1",
106
117
  "electron-context-menu": "^4.1.2",
@@ -109,30 +120,20 @@
109
120
  "semver": "^7.8.0",
110
121
  "tsx": "^4.21.0",
111
122
  "vite": "^6.0.0",
112
- "ws": "^8.18.0",
113
- "zod": "^4.3.6",
114
- "@zenbujs/hmr": "0.0.2"
123
+ "ws": "^8.18.0"
115
124
  },
116
125
  "devDependencies": {
117
126
  "@types/node": "^22.0.0",
118
127
  "@types/react": "^19.0.0",
119
128
  "@types/semver": "^7.7.1",
120
129
  "@types/ws": "^8.18.0",
130
+ "@zenbu/advice": "workspace:*",
131
+ "@zenbu/kyju": "workspace:*",
132
+ "@zenbu/zenrpc": "workspace:*",
121
133
  "electron": "^42.0.0",
122
134
  "tsdown": "^0.21.10",
123
135
  "typescript": "^5.4.5",
124
136
  "vitest": "^3.2.4",
125
- "@zenbu/advice": "0.0.0",
126
- "@zenbu/kyju": "0.0.0",
127
- "@zenbu/zenrpc": "0.0.0"
128
- },
129
- "scripts": {
130
- "build": "node --max-old-space-size=8192 ./node_modules/tsdown/dist/run.mjs",
131
- "prebuild": "pnpm link:types",
132
- "dev": "tsdown --watch",
133
- "link:types": "tsx ./src/cli/bin.ts link --types-config ./zenbu-types.config.json --out ./src/registry-generated.ts --augment-out ./types/zenbu-register.ts",
134
- "db:generate": "node ./dist/cli/bin.mjs db generate --schema src/schema.ts --migrations ./migrations",
135
- "test": "vitest run",
136
- "typecheck": "tsc --noEmit -p tsconfig.json"
137
+ "zod": "^4"
137
138
  }
138
- }
139
+ }
package/LICENSE DELETED
@@ -1,11 +0,0 @@
1
- All Rights Reserved
2
-
3
- Copyright (c) 2026 Zenbu Labs Inc.
4
-
5
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
8
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
9
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
11
- THE SOFTWARE.
@@ -1,154 +0,0 @@
1
- import { n as __exportAll } from "./chunk-DsiFFCwN.mjs";
2
- import { getPlugins, runtime } from "./runtime.mjs";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- //#region src/services/advice-config.ts
6
- var advice_config_exports = /* @__PURE__ */ __exportAll({
7
- getAdvice: () => getAdvice,
8
- getAllAdviceTypes: () => getAllAdviceTypes,
9
- getAllContentScriptPaths: () => getAllContentScriptPaths,
10
- getAllTypes: () => getAllTypes,
11
- getContentScripts: () => getContentScripts,
12
- registerAdvice: () => registerAdvice,
13
- registerContentScript: () => registerContentScript
14
- });
15
- /**
16
- * Find the plugin whose `dir` contains the file at `metaUrl`. The runtime
17
- * plugin registry (populated by the loader-emitted barrel) is the source
18
- * of truth; this no longer walks the filesystem looking for
19
- * `zenbu.plugin.json`. Returns the plugin's dir, used as the anchor for
20
- * `registerContentScript` / `registerAdvice` relative-path resolution.
21
- *
22
- * Throws if no plugin matches, because a silent fallback to `process.cwd()`
23
- * would attach a content script to a nonsensical location and you'd debug
24
- * it by staring at empty iframes.
25
- */
26
- function findPluginRoot(metaUrl) {
27
- const file = fileURLToPath(metaUrl);
28
- let bestMatch = null;
29
- for (const plugin of getPlugins()) {
30
- const rel = path.relative(plugin.dir, file);
31
- if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
32
- const depth = plugin.dir.split(path.sep).length;
33
- if (!bestMatch || depth > bestMatch.depth) bestMatch = {
34
- dir: plugin.dir,
35
- depth
36
- };
37
- }
38
- if (bestMatch) return bestMatch.dir;
39
- throw new Error(`Could not find owning plugin for ${metaUrl}. Pass an absolute path, or call this from a file inside a Zenbu plugin.`);
40
- }
41
- /**
42
- * Accepts either an absolute path (used as-is) or a path relative to the
43
- * caller's plugin root. The `meta` argument is `import.meta` from the calling
44
- * module; without it we can't compute the plugin root, so relative paths
45
- * require it.
46
- */
47
- function resolvePluginPath(modulePath, meta) {
48
- if (path.isAbsolute(modulePath)) return modulePath;
49
- if (!meta?.url) throw new Error(`registerContentScript/registerAdvice: relative path "${modulePath}" requires import.meta as the second argument so we can find the plugin root.`);
50
- return path.resolve(findPluginRoot(meta.url), modulePath);
51
- }
52
- const RESOLVED_PREFIX = "\0@advice-prelude/";
53
- const APP_RENDERER_RELOADER_ID = "app";
54
- const adviceEntries = /* @__PURE__ */ new Map();
55
- const contentScripts = /* @__PURE__ */ new Map();
56
- function invalidatePrelude(type) {
57
- try {
58
- const reloader = runtime.getSlot("reloader")?.instance;
59
- if (!reloader) return;
60
- const coreEntry = reloader.get(APP_RENDERER_RELOADER_ID);
61
- if (!coreEntry?.viteServer) return;
62
- const graph = coreEntry.viteServer.moduleGraph;
63
- const invalidateMatching = (test) => {
64
- const ids = [];
65
- for (const id of graph.idToModuleMap.keys()) {
66
- if (typeof id !== "string") continue;
67
- if (test(id)) ids.push(id);
68
- }
69
- for (const id of ids) {
70
- const mod = graph.getModuleById(id);
71
- if (mod) graph.invalidateModule(mod);
72
- }
73
- };
74
- if (type === "*") invalidateMatching((id) => id.startsWith(RESOLVED_PREFIX));
75
- else {
76
- const prefix = RESOLVED_PREFIX + type;
77
- invalidateMatching((id) => id === prefix || id.startsWith(prefix + "?"));
78
- }
79
- } catch {}
80
- }
81
- function emitReload(type) {
82
- invalidatePrelude(type);
83
- try {
84
- const rpc = runtime.getSlot("rpc")?.instance;
85
- if (!rpc) return;
86
- rpc.emit.advice.reload({ type });
87
- } catch {}
88
- }
89
- function registerAdvice(type, entry, meta) {
90
- const resolvedEntry = {
91
- ...entry,
92
- modulePath: resolvePluginPath(entry.modulePath, meta)
93
- };
94
- const list = adviceEntries.get(type) ?? [];
95
- list.push(resolvedEntry);
96
- adviceEntries.set(type, list);
97
- emitReload(type);
98
- return () => {
99
- const current = adviceEntries.get(type);
100
- if (!current) return;
101
- const idx = current.indexOf(resolvedEntry);
102
- if (idx >= 0) current.splice(idx, 1);
103
- if (current.length === 0) adviceEntries.delete(type);
104
- emitReload(type);
105
- };
106
- }
107
- function getAdvice(type) {
108
- return adviceEntries.get(type) ?? [];
109
- }
110
- function getAllAdviceTypes() {
111
- return [...adviceEntries.keys()];
112
- }
113
- /**
114
- * Register a content script for the given view type. `modulePath` is normally
115
- * a path relative to the plugin root (the folder with `zenbu.plugin.json`);
116
- * pass `import.meta` so we can resolve it. Absolute paths are accepted as
117
- * an escape hatch.
118
- *
119
- * this.setup("inject", () =>
120
- * registerContentScript("app", "src/content/clock.tsx", import.meta),
121
- * )
122
- */
123
- function registerContentScript(type, modulePath, meta) {
124
- const entry = { path: resolvePluginPath(modulePath, meta) };
125
- const list = contentScripts.get(type) ?? [];
126
- list.push(entry);
127
- contentScripts.set(type, list);
128
- emitReload(type === "*" ? "*" : type);
129
- return () => {
130
- const current = contentScripts.get(type);
131
- if (!current) return;
132
- const idx = current.indexOf(entry);
133
- if (idx >= 0) current.splice(idx, 1);
134
- if (current.length === 0) contentScripts.delete(type);
135
- emitReload(type === "*" ? "*" : type);
136
- };
137
- }
138
- function getContentScripts(type) {
139
- const scoped = (contentScripts.get(type) ?? []).map((e) => e.path);
140
- return [...type !== "*" ? (contentScripts.get("*") ?? []).map((e) => e.path) : [], ...scoped];
141
- }
142
- function getAllContentScriptPaths() {
143
- const paths = [];
144
- for (const list of contentScripts.values()) for (const entry of list) paths.push(entry.path);
145
- return paths;
146
- }
147
- function getAllTypes() {
148
- const types = /* @__PURE__ */ new Set();
149
- for (const k of adviceEntries.keys()) types.add(k);
150
- for (const k of contentScripts.keys()) if (k !== "*") types.add(k);
151
- return [...types];
152
- }
153
- //#endregion
154
- export { getAllTypes as a, registerContentScript as c, getAllContentScriptPaths as i, getAdvice as n, getContentScripts as o, getAllAdviceTypes as r, registerAdvice as s, advice_config_exports as t };