@zenbujs/core 0.0.8 → 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-w5QyDjuf.d.mts → index-C0mXKol5.d.mts} +189 -141
  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 +25 -11
  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-DCkz9M1c.mjs +0 -1008
  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/launcher.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- import { BaseWindow, WebContentsView, app } from "electron";
2
+ import { BaseWindow, WebContentsView, app, nativeTheme } from "electron";
3
3
  import fs, { existsSync } from "node:fs";
4
4
  import fsp from "node:fs/promises";
5
5
  import os from "node:os";
@@ -18808,9 +18808,10 @@ async function writeDepsSig(appsDir, sig) {
18808
18808
  //#region src/shared/host-version.ts
18809
18809
  /**
18810
18810
  * Single source of truth for reading the .app's "host version" — the
18811
- * concrete semver string the developer authored on
18812
- * `defineBuildConfig({ hostVersion: "..." })` and that
18813
- * `zen build:electron` baked into `<bundle>/host.json`.
18811
+ * concrete semver string `zen build:electron` baked into
18812
+ * `<bundle>/host.json` from the developer's `package.json#version` at
18813
+ * build time. Frozen into the .app so subsequent `git pull`s of the
18814
+ * source can't change it.
18814
18815
  *
18815
18816
  * Imported by both:
18816
18817
  * - `packages/core/src/launcher.ts` (tsdown inlines this into
@@ -20393,6 +20394,51 @@ async function resolveTargetSha(input) {
20393
20394
  };
20394
20395
  }
20395
20396
  //#endregion
20397
+ //#region src/shared/zenbu-bg-parse.ts
20398
+ const META_TAG_RE = /<meta\b([^>]*?)\/?>/gi;
20399
+ const ATTR_RE = /([\w-]+)\s*=\s*["']([^"']+)["']/g;
20400
+ /**
20401
+ * Parse all `<meta name="zenbu-bg">` tags from `html`. Mirrors the W3C
20402
+ * `<meta name="theme-color">` pattern: multiple tags carry a `media`
20403
+ * attribute (typically `(prefers-color-scheme: light|dark)`), and an
20404
+ * unmediated tag acts as the fallback.
20405
+ *
20406
+ * <meta name="zenbu-bg" content="#fafafa" media="(prefers-color-scheme: light)">
20407
+ * <meta name="zenbu-bg" content="#09090b" media="(prefers-color-scheme: dark)">
20408
+ * <meta name="zenbu-bg" content="#27272a"> <!-- default -->
20409
+ */
20410
+ function parseZenbuBgEntries(html) {
20411
+ const out = [];
20412
+ for (const tag of html.matchAll(META_TAG_RE)) {
20413
+ const body = tag[1] ?? "";
20414
+ const attrs = {};
20415
+ for (const a of body.matchAll(ATTR_RE)) attrs[a[1].toLowerCase()] = a[2];
20416
+ if (attrs.name === "zenbu-bg" && attrs.content) out.push({
20417
+ color: attrs.content,
20418
+ media: attrs.media ?? null
20419
+ });
20420
+ }
20421
+ return out;
20422
+ }
20423
+ /**
20424
+ * Pick the right entry given the current dark-mode preference. Search
20425
+ * order:
20426
+ * 1. First entry whose `media` matches `(prefers-color-scheme: <dark|light>)`.
20427
+ * 2. First entry without a `media` attribute (the unconditional default).
20428
+ * 3. The first entry, regardless of `media`.
20429
+ *
20430
+ * Returns `null` only when `entries` is empty.
20431
+ */
20432
+ function pickZenbuBgEntry(entries, dark) {
20433
+ for (const e of entries) {
20434
+ if (!e.media) continue;
20435
+ if (dark && /prefers-color-scheme:\s*dark/i.test(e.media)) return e.color;
20436
+ if (!dark && /prefers-color-scheme:\s*light/i.test(e.media)) return e.color;
20437
+ }
20438
+ for (const e of entries) if (!e.media) return e.color;
20439
+ return entries[0]?.color ?? null;
20440
+ }
20441
+ //#endregion
20396
20442
  //#region src/launcher.ts
20397
20443
  /**
20398
20444
  * Zenbu launcher shim.
@@ -20419,6 +20465,9 @@ async function resolveTargetSha(input) {
20419
20465
  * at runtime), and `isomorphic-git` (bundled into this file by tsdown — see
20420
20466
  * `packages/core/tsdown.config.ts` `neverBundle: ["electron"]`).
20421
20467
  */
20468
+ /**
20469
+ * fixme this is nonsense
20470
+ */
20422
20471
  const _logDir = path.join(os.homedir(), ".zenbu", ".internal");
20423
20472
  fs.mkdirSync(_logDir, { recursive: true });
20424
20473
  const _logStream = fs.createWriteStream(path.join(_logDir, "launcher.log"), { flags: "a" });
@@ -20462,8 +20511,8 @@ const APP_PATH = app.getAppPath();
20462
20511
  const RESOURCES_PATH = path.dirname(APP_PATH);
20463
20512
  function readBgColor(htmlPath, fallback) {
20464
20513
  try {
20465
- const match = fs.readFileSync(htmlPath, "utf8").match(/<meta\s+name=["']zenbu-bg["']\s+content=["']([^"']+)["']/i);
20466
- if (match?.[1]) return match[1];
20514
+ const picked = pickZenbuBgEntry(parseZenbuBgEntries(fs.readFileSync(htmlPath, "utf8")), nativeTheme.shouldUseDarkColors);
20515
+ if (picked) return picked;
20467
20516
  } catch {}
20468
20517
  return fallback;
20469
20518
  }
@@ -20507,6 +20556,15 @@ async function maybeOpenInstallingWindow(cfg) {
20507
20556
  };
20508
20557
  layout();
20509
20558
  win.on("resize", layout);
20559
+ const onThemeChange = () => {
20560
+ try {
20561
+ win.setBackgroundColor(readBgColor(htmlPath, "#F4F4F4"));
20562
+ } catch {}
20563
+ };
20564
+ nativeTheme.on("updated", onThemeChange);
20565
+ win.on("closed", () => {
20566
+ nativeTheme.removeListener("updated", onThemeChange);
20567
+ });
20510
20568
  let ready = false;
20511
20569
  const pending = [];
20512
20570
  view.webContents.once("did-finish-load", () => {
@@ -0,0 +1,586 @@
1
+ import { n as __exportAll } from "./chunk-DsiFFCwN.mjs";
2
+ import { i as loadPluginManifest, n as loadConfig, r as loadPluginFromPath, t as findConfigPath } from "./load-config-C2XloBaQ.mjs";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ //#region src/cli/commands/link.ts
6
+ var link_exports = /* @__PURE__ */ __exportAll({
7
+ linkProject: () => linkProject,
8
+ linkSinglePlugin: () => linkSinglePlugin,
9
+ runLink: () => runLink
10
+ });
11
+ const DOTZENBU_TYPES = path.join(".zenbu", "types");
12
+ const SERVICE_BASE_LITERAL = [
13
+ ` | "evaluate"`,
14
+ ` | "shutdown"`,
15
+ ` | "constructor"`,
16
+ ` | "effect"`,
17
+ ` | "__cleanupAllEffects"`,
18
+ ` | "__effectCleanups"`,
19
+ ` | "ctx"`
20
+ ].join("\n");
21
+ const EXTRACT_RPC_DECL = [
22
+ "type ExtractRpcMethods<T> = {",
23
+ " [K in Exclude<keyof T, ServiceBase | `_${string}`> as T[K] extends (",
24
+ " ...args: any[]",
25
+ " ) => any",
26
+ " ? K",
27
+ " : never]: T[K]",
28
+ "}"
29
+ ].join("\n");
30
+ function expandGlob(baseDir, pattern) {
31
+ if (!pattern.includes("*")) {
32
+ const full = path.resolve(baseDir, pattern);
33
+ return fs.existsSync(full) ? [full] : [];
34
+ }
35
+ const dir = path.resolve(baseDir, path.dirname(pattern));
36
+ const filePattern = path.basename(pattern);
37
+ const regex = new RegExp("^" + filePattern.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$");
38
+ try {
39
+ return fs.readdirSync(dir).filter((f) => regex.test(f)).map((f) => path.resolve(dir, f));
40
+ } catch {
41
+ return [];
42
+ }
43
+ }
44
+ const SERVICE_CLASS_KEY_RE = /export\s+class\s+(\w+)\s+extends\s+Service\.create\s*\(\s*\{[\s\S]*?\bkey\s*:\s*["']([^"']+)["']/;
45
+ function discoverServices(baseDir, serviceGlobs) {
46
+ const entries = [];
47
+ for (const glob of serviceGlobs) for (const filePath of expandGlob(baseDir, glob)) {
48
+ const match = fs.readFileSync(filePath, "utf8").match(SERVICE_CLASS_KEY_RE);
49
+ if (match) entries.push({
50
+ className: match[1],
51
+ key: match[2],
52
+ filePath
53
+ });
54
+ }
55
+ return entries;
56
+ }
57
+ function discoverOwnSurface(plugin) {
58
+ const serviceGlobs = plugin.services.map((abs) => path.relative(plugin.dir, abs).split(path.sep).join("/"));
59
+ return {
60
+ services: discoverServices(plugin.dir, serviceGlobs),
61
+ schemaPath: plugin.schemaPath,
62
+ eventsPath: plugin.eventsPath,
63
+ preloadPath: plugin.preloadPath
64
+ };
65
+ }
66
+ function relImport(from, to) {
67
+ let r = path.relative(from, to).split(path.sep).join("/");
68
+ if (!r.startsWith(".")) r = "./" + r;
69
+ return r.replace(/\.ts$/, "");
70
+ }
71
+ function quoteKey(name) {
72
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
73
+ }
74
+ function sanitizeIdent(name) {
75
+ return name.replace(/[^a-zA-Z0-9_$]/g, "_");
76
+ }
77
+ function writeIfChanged(target, body, opts) {
78
+ fs.mkdirSync(path.dirname(target), { recursive: true });
79
+ let prev = null;
80
+ try {
81
+ prev = fs.readFileSync(target, "utf8");
82
+ } catch {}
83
+ if (prev === body) return false;
84
+ fs.writeFileSync(target, body);
85
+ if (!opts.quiet) console.log(` Wrote ${target}`);
86
+ return true;
87
+ }
88
+ /** jsonc-lite: strips // and /* * / comments and trailing commas. */
89
+ function readJsonLoose(raw) {
90
+ const stripped = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1").replace(/,\s*([\]}])/g, "$1");
91
+ return JSON.parse(stripped);
92
+ }
93
+ function generateServicesFile(surfaceDir, surface) {
94
+ const imports = [];
95
+ const usedNames = /* @__PURE__ */ new Map();
96
+ const lines = [];
97
+ const uniqueName = (base) => {
98
+ const count = usedNames.get(base) ?? 0;
99
+ usedNames.set(base, count + 1);
100
+ return count === 0 ? base : `${base}_${count}`;
101
+ };
102
+ for (const svc of surface.services) {
103
+ const alias = uniqueName(svc.className);
104
+ imports.push(`import type { ${svc.className}${alias !== svc.className ? ` as ${alias}` : ""} } from "${relImport(surfaceDir, svc.filePath)}"`);
105
+ lines.push(` ${quoteKey(svc.key)}: ExtractRpcMethods<${alias}>;`);
106
+ }
107
+ return [
108
+ "// Generated by: zen link",
109
+ "// DO NOT EDIT. Pointer-only file: imports resolve directly to source on disk.",
110
+ "",
111
+ ...imports,
112
+ "",
113
+ `type ServiceBase =\n${SERVICE_BASE_LITERAL}`,
114
+ "",
115
+ EXTRACT_RPC_DECL,
116
+ "",
117
+ "export type SelfServiceMap = {",
118
+ ...lines,
119
+ "}",
120
+ ""
121
+ ].join("\n");
122
+ }
123
+ function generateDbFile(surfaceDir, surface) {
124
+ if (!surface.schemaPath) return [
125
+ "// Generated by: zen link",
126
+ "",
127
+ "export type SelfDbSection = {}",
128
+ ""
129
+ ].join("\n");
130
+ return [
131
+ "// Generated by: zen link",
132
+ "",
133
+ `import type { InferSchemaRoot } from "@zenbujs/core/db"`,
134
+ `import type schema from "${relImport(surfaceDir, surface.schemaPath)}"`,
135
+ "",
136
+ "export type SelfDbSection = InferSchemaRoot<typeof schema>",
137
+ ""
138
+ ].join("\n");
139
+ }
140
+ function generateEventsFile(surfaceDir, surface) {
141
+ if (!surface.eventsPath) return [
142
+ "// Generated by: zen link",
143
+ "",
144
+ "export type SelfEvents = {}",
145
+ ""
146
+ ].join("\n");
147
+ return [
148
+ "// Generated by: zen link",
149
+ "",
150
+ `import type { Events } from "${relImport(surfaceDir, surface.eventsPath)}"`,
151
+ "",
152
+ "export type SelfEvents = Events",
153
+ ""
154
+ ].join("\n");
155
+ }
156
+ function generatePreloadsFile(surfaceDir, surface) {
157
+ if (!surface.preloadPath) return [
158
+ "// Generated by: zen link",
159
+ "",
160
+ "export type SelfPreload = unknown",
161
+ ""
162
+ ].join("\n");
163
+ return [
164
+ "// Generated by: zen link",
165
+ "",
166
+ `import type { default as preload } from "${relImport(surfaceDir, surface.preloadPath)}"`,
167
+ "",
168
+ "export type SelfPreload = Awaited<ReturnType<typeof preload>>",
169
+ ""
170
+ ].join("\n");
171
+ }
172
+ function generateIndexFile() {
173
+ return [
174
+ "// Generated by: zen link",
175
+ "",
176
+ `import type { SelfServiceMap } from "./services"`,
177
+ `import type { SelfDbSection } from "./db-sections"`,
178
+ `import type { SelfEvents } from "./events"`,
179
+ `import type { SelfPreload } from "./preloads"`,
180
+ "",
181
+ "export type Own = {",
182
+ " services: SelfServiceMap",
183
+ " db: SelfDbSection",
184
+ " events: SelfEvents",
185
+ " preloads: SelfPreload",
186
+ "}",
187
+ ""
188
+ ].join("\n");
189
+ }
190
+ function writeSurface(surfaceDir, surface, opts) {
191
+ fs.mkdirSync(surfaceDir, { recursive: true });
192
+ writeIfChanged(path.join(surfaceDir, "services.ts"), generateServicesFile(surfaceDir, surface), opts);
193
+ writeIfChanged(path.join(surfaceDir, "db-sections.ts"), generateDbFile(surfaceDir, surface), opts);
194
+ writeIfChanged(path.join(surfaceDir, "events.ts"), generateEventsFile(surfaceDir, surface), opts);
195
+ writeIfChanged(path.join(surfaceDir, "preloads.ts"), generatePreloadsFile(surfaceDir, surface), opts);
196
+ writeIfChanged(path.join(surfaceDir, "index.ts"), generateIndexFile(), opts);
197
+ }
198
+ function generateCompositeFile(args) {
199
+ const lines = [
200
+ "// Generated by: zen link",
201
+ "// DO NOT EDIT. Composite augmentation (own + deps) for this plugin.",
202
+ "",
203
+ `import type {} from "@zenbujs/core/registry"`,
204
+ `import type { CoreServiceRouter, CoreEvents, CoreDbSections } from "@zenbujs/core/registry-generated"`
205
+ ];
206
+ if (args.selfName && args.selfOwnImport) lines.push(`import type { Own as Self_${sanitizeIdent(args.selfName)} } from "${args.selfOwnImport}"`);
207
+ for (const d of args.deps) lines.push(`import type { Own as Dep_${sanitizeIdent(d.name)} } from "${d.ownImport}"`);
208
+ lines.push("");
209
+ const rpcEntries = [];
210
+ const evtEntries = [];
211
+ const dbEntries = [];
212
+ if (args.selfName && args.selfOwnImport) {
213
+ const k = quoteKey(args.selfName);
214
+ const a = sanitizeIdent(args.selfName);
215
+ rpcEntries.push(` ${k}: Self_${a}["services"];`);
216
+ evtEntries.push(` ${k}: Self_${a}["events"];`);
217
+ dbEntries.push(` ${k}: Self_${a}["db"];`);
218
+ }
219
+ for (const d of args.deps) {
220
+ const k = quoteKey(d.name);
221
+ const a = sanitizeIdent(d.name);
222
+ rpcEntries.push(` ${k}: Dep_${a}["services"];`);
223
+ evtEntries.push(` ${k}: Dep_${a}["events"];`);
224
+ dbEntries.push(` ${k}: Dep_${a}["db"];`);
225
+ }
226
+ lines.push(`declare module "@zenbujs/core/registry" {`);
227
+ lines.push(` interface ZenbuRegister {`);
228
+ lines.push(` rpc: CoreServiceRouter & {`);
229
+ if (rpcEntries.length === 0) lines.push(` // (no plugins)`);
230
+ lines.push(...rpcEntries);
231
+ lines.push(` };`);
232
+ lines.push(` events: CoreEvents & {`);
233
+ if (evtEntries.length === 0) lines.push(` // (no plugins)`);
234
+ lines.push(...evtEntries);
235
+ lines.push(` };`);
236
+ lines.push(` db: CoreDbSections & {`);
237
+ if (dbEntries.length === 0) lines.push(` // (no plugins)`);
238
+ lines.push(...dbEntries);
239
+ lines.push(` };`);
240
+ lines.push(` }`);
241
+ lines.push(`}`);
242
+ lines.push("");
243
+ lines.push("export {}");
244
+ lines.push("");
245
+ return lines.join("\n");
246
+ }
247
+ const REGISTER_INCLUDE = "./.zenbu/types/zenbu-register.ts";
248
+ function bootstrapTsconfigJson(pluginDir, opts) {
249
+ const tsconfigPath = path.join(pluginDir, "tsconfig.json");
250
+ if (!fs.existsSync(tsconfigPath)) return;
251
+ const raw = fs.readFileSync(tsconfigPath, "utf8");
252
+ let parsed;
253
+ try {
254
+ parsed = readJsonLoose(raw);
255
+ } catch {
256
+ return;
257
+ }
258
+ const includeArr = Array.isArray(parsed.include) ? [...parsed.include] : [];
259
+ if (includeArr.includes(REGISTER_INCLUDE)) return;
260
+ includeArr.push(REGISTER_INCLUDE);
261
+ parsed.include = includeArr;
262
+ const next = JSON.stringify(parsed, null, 2) + "\n";
263
+ if (next === raw) return;
264
+ fs.writeFileSync(tsconfigPath, next);
265
+ if (!opts.quiet) console.log(` Updated ${tsconfigPath}`);
266
+ }
267
+ const GITIGNORE_MARKER = "# zen link: generated types";
268
+ const GITIGNORE_RULE = ".zenbu/types/";
269
+ const GITIGNORE_BROAD_RULES = new Set([
270
+ ".zenbu",
271
+ ".zenbu/",
272
+ "/.zenbu",
273
+ "/.zenbu/",
274
+ ".zenbu/*",
275
+ ".zenbu/**",
276
+ ".zenbu/**/*",
277
+ ".zenbu/types",
278
+ ".zenbu/types/",
279
+ ".zenbu/types/**",
280
+ ".zenbu/types/**/*"
281
+ ]);
282
+ function bootstrapGitignore(pluginDir, opts) {
283
+ const gitignorePath = path.join(pluginDir, ".gitignore");
284
+ let raw = "";
285
+ try {
286
+ raw = fs.readFileSync(gitignorePath, "utf8");
287
+ } catch {
288
+ return;
289
+ }
290
+ const lines = raw.split(/\r?\n/);
291
+ for (const line of lines) {
292
+ const trimmed = line.trim();
293
+ if (!trimmed || trimmed.startsWith("#")) continue;
294
+ if (GITIGNORE_BROAD_RULES.has(trimmed)) return;
295
+ if (trimmed === GITIGNORE_RULE) return;
296
+ }
297
+ const append = `${raw.endsWith("\n") ? "" : "\n"}\n${GITIGNORE_MARKER}\n${GITIGNORE_RULE}\n`;
298
+ fs.writeFileSync(gitignorePath, raw + append);
299
+ if (!opts.quiet) console.log(` Updated ${gitignorePath}`);
300
+ }
301
+ function generateCoreSurfaceFile(args) {
302
+ const sorted = [...args.services].sort((a, b) => a.className.localeCompare(b.className));
303
+ const importedNames = sorted.map((s) => ` ${s.className},`).join("\n");
304
+ const routerEntries = sorted.map((s) => ` ${quoteKey(s.key)}: ExtractRpcMethods<${s.className}>;`).join("\n");
305
+ const lines = [
306
+ "// Generated by: pnpm link:types",
307
+ "// DO NOT EDIT. Regenerated automatically (also wired into `prebuild`).",
308
+ ""
309
+ ];
310
+ if (sorted.length > 0) {
311
+ lines.push("import type {");
312
+ lines.push(importedNames);
313
+ lines.push(`} from "@zenbujs/core/services"`);
314
+ }
315
+ if (args.hasEvents) lines.push(`import type { Events as Events_core } from "@zenbujs/core/events"`);
316
+ if (args.hasSchema) {
317
+ lines.push(`import type schema_core from "@zenbujs/core/schema"`);
318
+ lines.push(`import type { InferSchemaRoot } from "@zenbujs/core/db"`);
319
+ }
320
+ lines.push("");
321
+ lines.push(`type ServiceBase =\n${SERVICE_BASE_LITERAL}`);
322
+ lines.push("");
323
+ lines.push(EXTRACT_RPC_DECL);
324
+ lines.push("");
325
+ lines.push("export type CoreServiceRouter = {");
326
+ lines.push(" core: {");
327
+ if (routerEntries.length > 0) lines.push(routerEntries);
328
+ lines.push(" };");
329
+ lines.push("}");
330
+ lines.push("");
331
+ lines.push(`export type CoreEvents = { core: ${args.hasEvents ? "Events_core" : "{}"} }`);
332
+ lines.push("");
333
+ lines.push("export type CoreDbSections = {");
334
+ if (args.hasSchema) lines.push(" core: InferSchemaRoot<typeof schema_core>;");
335
+ lines.push("}");
336
+ lines.push("");
337
+ lines.push("export type CorePreloads = {}");
338
+ lines.push("");
339
+ return lines.join("\n");
340
+ }
341
+ function generateCoreAugmentFile() {
342
+ return [
343
+ "// Generated by: pnpm link:types",
344
+ "// DO NOT EDIT. Local-only augmentation (NOT shipped via package.json#files).",
345
+ "// Drives core's own typecheck so useRpc()/useEvents()/useDb() resolve",
346
+ "// to the registered surface inside packages/core/src/.",
347
+ "",
348
+ `import type {} from "@zenbujs/core/registry"`,
349
+ `import type {`,
350
+ ` CoreServiceRouter,`,
351
+ ` CoreEvents,`,
352
+ ` CoreDbSections,`,
353
+ `} from "../src/registry-generated"`,
354
+ "",
355
+ `declare module "@zenbujs/core/registry" {`,
356
+ " interface ZenbuRegister {",
357
+ " rpc: CoreServiceRouter",
358
+ " events: CoreEvents",
359
+ " db: CoreDbSections",
360
+ " }",
361
+ "}",
362
+ "",
363
+ "export {}",
364
+ ""
365
+ ].join("\n");
366
+ }
367
+ function writeCoreSurfaceFiles(args) {
368
+ writeIfChanged(args.surfaceOut, generateCoreSurfaceFile({
369
+ services: args.services,
370
+ hasEvents: args.hasEvents,
371
+ hasSchema: args.hasSchema
372
+ }), args.opts);
373
+ writeIfChanged(args.augmentOut, generateCoreAugmentFile(), args.opts);
374
+ }
375
+ function parseLinkArgs(argv) {
376
+ let manifestArg = null;
377
+ let typesConfigArg = null;
378
+ let surfaceOutArg = null;
379
+ let augmentOutArg = null;
380
+ let pluginMode = false;
381
+ let pluginDirArg = null;
382
+ for (let i = 0; i < argv.length; i++) {
383
+ const arg = argv[i];
384
+ if (arg === "--types-config" && i + 1 < argv.length) typesConfigArg = argv[++i];
385
+ else if (arg.startsWith("--types-config=")) typesConfigArg = arg.slice(15);
386
+ else if (arg === "--out" && i + 1 < argv.length) surfaceOutArg = argv[++i];
387
+ else if (arg.startsWith("--out=")) surfaceOutArg = arg.slice(6);
388
+ else if (arg === "--augment-out" && i + 1 < argv.length) augmentOutArg = argv[++i];
389
+ else if (arg.startsWith("--augment-out=")) augmentOutArg = arg.slice(14);
390
+ else if (arg === "--plugin") {
391
+ pluginMode = true;
392
+ const next = argv[i + 1];
393
+ if (next && !next.startsWith("-")) {
394
+ pluginDirArg = next;
395
+ i++;
396
+ }
397
+ } else if (arg.startsWith("--plugin=")) {
398
+ pluginMode = true;
399
+ pluginDirArg = arg.slice(9);
400
+ } else if (!arg.startsWith("-") && !manifestArg) manifestArg = arg;
401
+ }
402
+ return {
403
+ manifestArg,
404
+ typesConfigArg,
405
+ surfaceOutArg,
406
+ augmentOutArg,
407
+ pluginMode,
408
+ pluginDirArg
409
+ };
410
+ }
411
+ /**
412
+ * Per-plugin link pipeline. Used by both `linkProject` (host context) and
413
+ * `linkSinglePlugin` (standalone). Writes the plugin's own surface, each
414
+ * declared dep surface, the composite augmentation, and bootstraps
415
+ * `tsconfig.json` / `.gitignore`. Stale `deps/<name>/` dirs that aren't in
416
+ * the current `dependsOn` set are pruned.
417
+ */
418
+ async function processOnePlugin(plugin, writeOpts, log) {
419
+ const surface = discoverOwnSurface(plugin);
420
+ log(`Linking own surface "${plugin.name}" at ${plugin.dir}`);
421
+ log(` ${surface.services.length} service(s)`);
422
+ if (surface.schemaPath) log(` schema: ${surface.schemaPath}`);
423
+ if (surface.eventsPath) log(` events: ${surface.eventsPath}`);
424
+ if (surface.preloadPath) log(` preload: ${surface.preloadPath}`);
425
+ writeSurface(path.join(plugin.dir, DOTZENBU_TYPES, "own"), surface, writeOpts);
426
+ const deps = [];
427
+ for (const dep of plugin.dependsOn ?? []) {
428
+ const upstream = await loadPluginFromPath({
429
+ fromPath: dep.fromPath,
430
+ name: dep.name
431
+ });
432
+ const surfaceDir = path.join(plugin.dir, DOTZENBU_TYPES, "deps", dep.name);
433
+ const upstreamSurface = discoverOwnSurface(upstream);
434
+ log(`Linking dep "${dep.name}" into "${plugin.name}" (${upstream.dir})`);
435
+ writeSurface(surfaceDir, upstreamSurface, writeOpts);
436
+ deps.push({
437
+ depName: dep.name,
438
+ ownImportFromComposite: relImport(path.join(plugin.dir, DOTZENBU_TYPES), path.join(surfaceDir, "index.ts"))
439
+ });
440
+ }
441
+ const wantedDepNames = new Set(deps.map((d) => d.depName));
442
+ const depsRoot = path.join(plugin.dir, DOTZENBU_TYPES, "deps");
443
+ if (fs.existsSync(depsRoot)) for (const entry of fs.readdirSync(depsRoot)) {
444
+ if (wantedDepNames.has(entry)) continue;
445
+ const stale = path.join(depsRoot, entry);
446
+ try {
447
+ fs.rmSync(stale, {
448
+ recursive: true,
449
+ force: true
450
+ });
451
+ if (!writeOpts.quiet) console.log(` Pruned ${stale}`);
452
+ } catch {}
453
+ }
454
+ const compositePath = path.join(plugin.dir, DOTZENBU_TYPES, "zenbu-register.ts");
455
+ const selfOwnImport = relImport(path.dirname(compositePath), path.join(plugin.dir, DOTZENBU_TYPES, "own", "index.ts"));
456
+ writeIfChanged(compositePath, generateCompositeFile({
457
+ selfName: plugin.name,
458
+ selfOwnImport,
459
+ deps: deps.map((d) => ({
460
+ name: d.depName,
461
+ ownImport: d.ownImportFromComposite
462
+ }))
463
+ }), writeOpts);
464
+ bootstrapTsconfigJson(plugin.dir, writeOpts);
465
+ bootstrapGitignore(plugin.dir, writeOpts);
466
+ }
467
+ async function linkProject(projectDir, opts = {}) {
468
+ const writeOpts = { quiet: !!opts.quiet };
469
+ const log = opts.quiet ? () => {} : (msg) => console.log(msg);
470
+ const { resolved, pluginSourceFiles } = await loadConfig(projectDir);
471
+ const inlinePlugins = resolved.plugins.filter((p) => p.dir === resolved.projectDir);
472
+ if (inlinePlugins.length > 1) throw new Error(`zen link: ${resolved.configPath} declares ${inlinePlugins.length} inline plugins (${inlinePlugins.map((p) => `"${p.name}"`).join(", ")}). Move all but one into separate zenbu.plugin.ts files so each owns its own .zenbu/types/ dir.`);
473
+ for (const plugin of resolved.plugins) await processOnePlugin(plugin, writeOpts, log);
474
+ if (inlinePlugins.length === 0) {
475
+ bootstrapTsconfigJson(resolved.projectDir, writeOpts);
476
+ bootstrapGitignore(resolved.projectDir, writeOpts);
477
+ }
478
+ return {
479
+ registryDir: path.join(resolved.projectDir, DOTZENBU_TYPES),
480
+ resolvedConfigPath: resolved.configPath,
481
+ pluginSourceFiles,
482
+ resolved
483
+ };
484
+ }
485
+ /**
486
+ * Standalone plugin link. Used by `zen link --plugin <dir>` when there's no
487
+ * host context — for example, right after `create-zenbu-app --plugin`
488
+ * scaffolds a plugin folder that hasn't been wired into any host yet.
489
+ *
490
+ * The plugin's `zenbu.plugin.ts` is loaded directly (no `zenbu.config.ts`
491
+ * required), and we run the same per-plugin pipeline `linkProject` uses.
492
+ */
493
+ async function linkSinglePlugin(pluginDir, opts = {}) {
494
+ const writeOpts = { quiet: !!opts.quiet };
495
+ const log = opts.quiet ? () => {} : (msg) => console.log(msg);
496
+ const candidates = [
497
+ "zenbu.plugin.ts",
498
+ "zenbu.plugin.mts",
499
+ "zenbu.plugin.js",
500
+ "zenbu.plugin.mjs"
501
+ ];
502
+ let manifestPath = null;
503
+ for (const name of candidates) {
504
+ const candidate = path.join(pluginDir, name);
505
+ if (fs.existsSync(candidate)) {
506
+ manifestPath = candidate;
507
+ break;
508
+ }
509
+ }
510
+ if (!manifestPath) throw new Error(`zen link --plugin: no zenbu.plugin.ts found in ${pluginDir}. Expected one of: ${candidates.join(", ")}.`);
511
+ await processOnePlugin(await loadPluginManifest(manifestPath), writeOpts, log);
512
+ }
513
+ const CONFIG_NAMES = [
514
+ "zenbu.config.ts",
515
+ "zenbu.config.mts",
516
+ "zenbu.config.js",
517
+ "zenbu.config.mjs"
518
+ ];
519
+ function findProjectDir(from) {
520
+ let dir = path.resolve(from);
521
+ while (true) {
522
+ for (const name of CONFIG_NAMES) if (fs.existsSync(path.join(dir, name))) return dir;
523
+ const parent = path.dirname(dir);
524
+ if (parent === dir) return null;
525
+ dir = parent;
526
+ }
527
+ }
528
+ async function runLink(argv) {
529
+ const args = parseLinkArgs(argv);
530
+ if (args.typesConfigArg) {
531
+ const typeConfigPath = path.resolve(args.typesConfigArg);
532
+ const rootManifest = JSON.parse(fs.readFileSync(typeConfigPath, "utf8"));
533
+ const baseDir = path.dirname(typeConfigPath);
534
+ const surfaceOut = args.surfaceOutArg ? path.resolve(args.surfaceOutArg) : path.join(baseDir, "src", "registry-generated.ts");
535
+ const augmentOut = args.augmentOutArg ? path.resolve(args.augmentOutArg) : path.join(baseDir, "types", "zenbu-register.ts");
536
+ try {
537
+ console.log(`Linking core types from ${baseDir}`);
538
+ const services = discoverServices(baseDir, rootManifest.services ?? []);
539
+ console.log(` Found ${services.length} service(s)`);
540
+ const hasEvents = !!rootManifest.events;
541
+ const hasSchema = !!rootManifest.schema;
542
+ if (rootManifest.events) console.log(` Events: ${path.resolve(baseDir, rootManifest.events)}`);
543
+ if (rootManifest.schema) console.log(` Schema: ${path.resolve(baseDir, rootManifest.schema)}`);
544
+ writeCoreSurfaceFiles({
545
+ services,
546
+ hasEvents,
547
+ hasSchema,
548
+ surfaceOut,
549
+ augmentOut,
550
+ opts: { quiet: false }
551
+ });
552
+ console.log("Done.");
553
+ } catch (err) {
554
+ console.error(err instanceof Error ? err.message : err);
555
+ process.exit(1);
556
+ }
557
+ return;
558
+ }
559
+ if (args.pluginMode) {
560
+ const pluginDir = path.resolve(args.pluginDirArg ?? process.cwd());
561
+ try {
562
+ await linkSinglePlugin(pluginDir);
563
+ console.log("Done.");
564
+ } catch (err) {
565
+ console.error(err instanceof Error ? err.message : err);
566
+ process.exit(1);
567
+ }
568
+ return;
569
+ }
570
+ const projectDir = args.manifestArg ? path.resolve(args.manifestArg) : findProjectDir(process.cwd());
571
+ if (!projectDir) {
572
+ console.error("zen link: could not find zenbu.config.ts in current directory or any parent.");
573
+ console.error(" For internal framework types, pass --types-config <path>.");
574
+ process.exit(1);
575
+ }
576
+ try {
577
+ findConfigPath(projectDir);
578
+ await linkProject(projectDir);
579
+ console.log("Done.");
580
+ } catch (err) {
581
+ console.error(err instanceof Error ? err.message : err);
582
+ process.exit(1);
583
+ }
584
+ }
585
+ //#endregion
586
+ export { link_exports as n, linkProject as t };