@zenbujs/core 0.0.5 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{advice-config-QYB2qEd_.mjs → advice-config-DXSIo0sg.mjs} +40 -39
- package/dist/advice.d.mts +8 -8
- package/dist/advice.mjs +2 -2
- package/dist/{base-window-BbFRRhKP.mjs → base-window-BxBZ2md_.mjs} +51 -7
- package/dist/{transforms-CuTODvDx.d.mts → build-config-Dzg2frpk.d.mts} +98 -28
- package/dist/build-config-pWdmLnrk.mjs +53 -0
- package/dist/{build-electron-CNJ0dLND.mjs → build-electron-Dsbb1EMl.mjs} +308 -120
- package/dist/{build-source-C2puqEVr.mjs → build-source-d1J3shV8.mjs} +62 -27
- package/dist/cli/bin.mjs +7 -7
- package/dist/cli/build.d.mts +2 -2
- package/dist/cli/build.mjs +2 -3
- package/dist/cli/resolve-config.mjs +1 -1
- package/dist/{cli-C3R1LBMY.mjs → cli-kL6mPgBE.mjs} +2 -2
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +2 -3
- package/dist/{db-xjvahRFJ.mjs → db-Bc292RYo.mjs} +2 -2
- package/dist/db.d.mts +1 -1
- package/dist/dev-B2emj0HZ.mjs +301 -0
- package/dist/env-bootstrap.d.mts +1 -1
- package/dist/events.d.mts +19 -0
- package/dist/events.mjs +1 -0
- package/dist/host-version-BIrF8tX7.mjs +65 -0
- package/dist/index-CVF768Xs.d.mts +783 -0
- package/dist/index.d.mts +5 -6
- package/dist/index.mjs +2 -2
- package/dist/installing-preload.cjs +60 -0
- package/dist/launcher.mjs +2615 -122
- package/dist/{link-c0_aLWQ3.mjs → link-glX89NV5.mjs} +215 -89
- package/dist/{load-config-xMf2wxH8.mjs → load-config-C4Oe2qZO.mjs} +5 -1
- package/dist/loaders/zenbu.mjs +102 -0
- package/dist/node-loader.mjs +1 -1
- package/dist/{publish-source-Dill72NS.mjs → publish-source-Dq2c0iOw.mjs} +2 -2
- package/dist/react.d.mts +55 -6
- package/dist/react.mjs +116 -5
- package/dist/registry-CMp8FYgS.d.mts +47 -0
- package/dist/registry-generated.d.mts +26 -0
- package/dist/registry-generated.mjs +1 -0
- package/dist/registry.d.mts +2 -2
- package/dist/{reloader-DzEO8kJr.mjs → reloader-B22UiNA2.mjs} +2 -4
- package/dist/{renderer-host-Cau9JK0v.mjs → renderer-host-DD16MXhI.mjs} +152 -43
- package/dist/{rpc-JfGv-Wuw.mjs → rpc-C4_NQmpT.mjs} +5 -4
- package/dist/{runtime-pCeVzj--.d.mts → runtime-BQWntcOb.d.mts} +85 -48
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.mjs +139 -83
- package/dist/{schema-Dl85YjXW.d.mts → schema-CjrMVk36.d.mts} +3 -3
- package/dist/schema.d.mts +1 -1
- package/dist/schema.mjs +1 -1
- package/dist/{server-y3PPbh3l.mjs → server-CZLMF8Dj.mjs} +1 -3
- package/dist/services/default.d.mts +3 -3
- package/dist/services/default.mjs +14 -13
- package/dist/services/index.d.mts +2 -280
- package/dist/services/index.mjs +8 -7
- package/dist/setup-gate.d.mts +1 -1
- package/dist/setup-gate.mjs +123 -24
- package/dist/{transform-CmFYPmt8.mjs → transform-BzrwkEdf.mjs} +22 -916
- package/dist/updater-BtB_Ki1r.mjs +1011 -0
- package/dist/{vite-plugins-Do7liKi_.mjs → vite-plugins-tt6KAtyE.mjs} +26 -25
- package/dist/vite.d.mts +3 -3
- package/dist/vite.mjs +1 -1
- package/dist/{window-o2NGUsIb.mjs → window-YFKvAM0l.mjs} +30 -16
- package/package.json +17 -4
- package/dist/build-config-C3a-o3_B.mjs +0 -23
- package/dist/dev-Dazhu66l.mjs +0 -85
- package/dist/registry-eX6e2oql.d.mts +0 -61
- package/dist/transforms-htxfTwsY.mjs +0 -47
- /package/dist/{config-DXRCDUxG.mjs → config-BK78JDRI.mjs} +0 -0
- /package/dist/{env-bootstrap-DW2hVhSO.d.mts → env-bootstrap-rTs8KR3-.d.mts} +0 -0
- /package/dist/{index-M_lSNBrq.d.mts → index-DeDxePAa.d.mts} +0 -0
- /package/dist/{mirror-sync-PDzxhf1w.mjs → mirror-sync-pYU6f3-c.mjs} +0 -0
- /package/dist/{monorepo-3avKJwzJ.mjs → monorepo-Dct-kkbQ.mjs} +0 -0
- /package/dist/{node-_8xShqxr.mjs → node-BhfLKYCi.mjs} +0 -0
- /package/dist/{setup-gate-Dcy8gGPJ.d.mts → setup-gate-BQq0QgZH.d.mts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as loadConfig } from "./load-config-
|
|
2
|
-
import { t as hashDir } from "./mirror-sync-
|
|
1
|
+
import { t as loadConfig } from "./load-config-C4Oe2qZO.mjs";
|
|
2
|
+
import { t as hashDir } from "./mirror-sync-pYU6f3-c.mjs";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
@@ -59,24 +59,41 @@ async function collectFiles(sourceDir, config) {
|
|
|
59
59
|
}
|
|
60
60
|
return [...seen].sort();
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
if (
|
|
62
|
+
/**
|
|
63
|
+
* Run every plugin's `transform` in declaration order. A `null` return drops
|
|
64
|
+
* the file and short-circuits remaining plugins. A string replaces the
|
|
65
|
+
* working contents and feeds the next plugin. `void` leaves contents alone.
|
|
66
|
+
*/
|
|
67
|
+
async function runTransform(filePath, initialContents, plugins, ctx) {
|
|
68
|
+
let contents = initialContents;
|
|
69
|
+
for (const plugin of plugins) {
|
|
70
|
+
if (!plugin.transform) continue;
|
|
71
|
+
const result = await plugin.transform({
|
|
72
|
+
path: filePath,
|
|
73
|
+
contents
|
|
74
|
+
}, ctx);
|
|
75
|
+
if (result === null) return {
|
|
76
|
+
contents,
|
|
77
|
+
drop: true
|
|
78
|
+
};
|
|
79
|
+
if (typeof result === "string") contents = result;
|
|
76
80
|
}
|
|
77
81
|
return {
|
|
78
|
-
|
|
79
|
-
drop
|
|
82
|
+
contents,
|
|
83
|
+
drop: false
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function makeEmit(outDir, recordWrite) {
|
|
87
|
+
return (relPath, contents) => {
|
|
88
|
+
if (typeof relPath !== "string" || relPath.length === 0) throw new Error(`emit: relPath must be a non-empty string, got ${typeof relPath}`);
|
|
89
|
+
if (path.isAbsolute(relPath)) throw new Error(`emit: relPath must be relative, got absolute path "${relPath}"`);
|
|
90
|
+
const normalized = path.posix.normalize(relPath.split(path.sep).join("/"));
|
|
91
|
+
if (normalized.startsWith("../") || normalized === "..") throw new Error(`emit: relPath must not escape outDir, got "${relPath}"`);
|
|
92
|
+
const dst = path.join(outDir, normalized);
|
|
93
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
94
|
+
if (typeof contents === "string") fs.writeFileSync(dst, contents);
|
|
95
|
+
else fs.writeFileSync(dst, contents);
|
|
96
|
+
recordWrite(normalized);
|
|
80
97
|
};
|
|
81
98
|
}
|
|
82
99
|
async function runBuildSource(argv) {
|
|
@@ -101,40 +118,58 @@ async function runBuildSource(argv) {
|
|
|
101
118
|
console.error("zen build:source: no files matched the include/ignore globs");
|
|
102
119
|
process.exit(1);
|
|
103
120
|
}
|
|
121
|
+
const sourceSha = resolveSourceHead(projectDir);
|
|
122
|
+
const ctx = {
|
|
123
|
+
sourceDir,
|
|
124
|
+
outDir,
|
|
125
|
+
sourceSha,
|
|
126
|
+
configPath: resolved.configPath
|
|
127
|
+
};
|
|
104
128
|
let written = 0;
|
|
105
129
|
let dropped = 0;
|
|
130
|
+
let emitted = 0;
|
|
106
131
|
for (const rel of files) {
|
|
107
132
|
const src = path.join(sourceDir, rel);
|
|
108
133
|
const dst = path.join(outDir, rel);
|
|
109
134
|
const isText = isLikelyText(src);
|
|
110
|
-
if (config.
|
|
135
|
+
if (config.plugins.length === 0 || !isText) {
|
|
111
136
|
await fsp.mkdir(path.dirname(dst), { recursive: true });
|
|
112
137
|
await fsp.copyFile(src, dst);
|
|
113
138
|
written += 1;
|
|
114
139
|
continue;
|
|
115
140
|
}
|
|
116
|
-
const {
|
|
117
|
-
path: rel,
|
|
118
|
-
code: await fsp.readFile(src, "utf8")
|
|
119
|
-
}, config.transforms);
|
|
141
|
+
const { contents, drop } = await runTransform(rel, await fsp.readFile(src, "utf8"), config.plugins, ctx);
|
|
120
142
|
if (drop) {
|
|
121
143
|
dropped += 1;
|
|
122
144
|
continue;
|
|
123
145
|
}
|
|
124
146
|
await fsp.mkdir(path.dirname(dst), { recursive: true });
|
|
125
|
-
await fsp.writeFile(dst,
|
|
147
|
+
await fsp.writeFile(dst, contents);
|
|
126
148
|
written += 1;
|
|
127
149
|
}
|
|
128
|
-
|
|
150
|
+
if (config.plugins.length > 0) {
|
|
151
|
+
const emitCtx = {
|
|
152
|
+
...ctx,
|
|
153
|
+
emit: makeEmit(outDir, () => {
|
|
154
|
+
emitted += 1;
|
|
155
|
+
})
|
|
156
|
+
};
|
|
157
|
+
for (const plugin of config.plugins) {
|
|
158
|
+
if (!plugin.done) continue;
|
|
159
|
+
await plugin.done(emitCtx);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
129
162
|
const meta = {
|
|
130
163
|
sourceSha,
|
|
131
164
|
contentHash: await hashDir(outDir),
|
|
132
165
|
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
133
166
|
files: written,
|
|
134
|
-
dropped
|
|
167
|
+
dropped,
|
|
168
|
+
emitted
|
|
135
169
|
};
|
|
136
170
|
await fsp.writeFile(path.join(outDir, ".sha"), JSON.stringify(meta, null, 2) + "\n");
|
|
137
|
-
|
|
171
|
+
const emittedSuffix = emitted > 0 ? `, ${emitted} emitted by plugins` : "";
|
|
172
|
+
console.log(`\n ✓ ${written} file(s) written, ${dropped} dropped${emittedSuffix}`);
|
|
138
173
|
console.log(` source HEAD: ${sourceSha === "uncommitted" ? sourceSha : sourceSha.slice(0, 7)}\n`);
|
|
139
174
|
}
|
|
140
175
|
const TEXT_EXTENSIONS = new Set([
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -41,37 +41,37 @@ async function main() {
|
|
|
41
41
|
const rest = argv.slice(1);
|
|
42
42
|
switch (first) {
|
|
43
43
|
case "dev": {
|
|
44
|
-
const { runDev } = await import("../dev-
|
|
44
|
+
const { runDev } = await import("../dev-B2emj0HZ.mjs");
|
|
45
45
|
await runDev(rest);
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
case "build:source": {
|
|
49
|
-
const { runBuildSource } = await import("../build-source-
|
|
49
|
+
const { runBuildSource } = await import("../build-source-d1J3shV8.mjs");
|
|
50
50
|
await runBuildSource(rest);
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
case "build:electron": {
|
|
54
|
-
const { runBuildElectron } = await import("../build-electron-
|
|
54
|
+
const { runBuildElectron } = await import("../build-electron-Dsbb1EMl.mjs");
|
|
55
55
|
await runBuildElectron(rest);
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
case "publish:source": {
|
|
59
|
-
const { runPublishSource } = await import("../publish-source-
|
|
59
|
+
const { runPublishSource } = await import("../publish-source-Dq2c0iOw.mjs");
|
|
60
60
|
await runPublishSource(rest);
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
case "link": {
|
|
64
|
-
const { runLink } = await import("../link-
|
|
64
|
+
const { runLink } = await import("../link-glX89NV5.mjs").then((n) => n.n);
|
|
65
65
|
await runLink(rest);
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
case "monorepo": {
|
|
69
|
-
const { runMonorepo } = await import("../monorepo-
|
|
69
|
+
const { runMonorepo } = await import("../monorepo-Dct-kkbQ.mjs");
|
|
70
70
|
await runMonorepo(rest);
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
case "db": {
|
|
74
|
-
const { runDb } = await import("../db-
|
|
74
|
+
const { runDb } = await import("../db-Bc292RYo.mjs");
|
|
75
75
|
await runDb(rest);
|
|
76
76
|
return;
|
|
77
77
|
}
|
package/dist/cli/build.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { type BuildConfig, type
|
|
1
|
+
import { g as resolveBuildConfig, i as BundleConfig, n as BuildContext, o as EmitContext, p as defineBuildConfig, r as BuildPlugin, s as MirrorConfig, t as BuildConfig, u as ResolvedBuildConfig } from "../build-config-Dzg2frpk.mjs";
|
|
2
|
+
export { type BuildConfig, type BuildContext, type BuildPlugin, type BundleConfig, type EmitContext, type MirrorConfig, type ResolvedBuildConfig, defineBuildConfig, resolveBuildConfig };
|
package/dist/cli/build.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { i as resolveBuildConfig, t as defineBuildConfig } from "../build-config-
|
|
2
|
-
|
|
3
|
-
export { defineBuildConfig, dropFiles, resolveBuildConfig, stripIfDisabled };
|
|
1
|
+
import { i as resolveBuildConfig, t as defineBuildConfig } from "../build-config-pWdmLnrk.mjs";
|
|
2
|
+
export { defineBuildConfig, resolveBuildConfig };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as getZodDefault, t as NO_DEFAULT } from "./schema-Ca7SxXgS.mjs";
|
|
2
2
|
import { _ as handleErrnoException, d as readJsonFile, f as readJsonlFile, g as layer$7, l as paths, n as makeRootCache, t as handleWrite, v as FileSystem, y as BadArgument } from "./write-DgIRjo23.mjs";
|
|
3
|
-
import { loadSchema } from "./config-
|
|
3
|
+
import { loadSchema } from "./config-BK78JDRI.mjs";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import * as NodePath from "node:path";
|
|
6
6
|
import path from "node:path";
|
|
@@ -8030,7 +8030,7 @@ async function run(argv) {
|
|
|
8030
8030
|
process.exit(args.help ? 0 : 1);
|
|
8031
8031
|
}
|
|
8032
8032
|
if (args.command === "generate") {
|
|
8033
|
-
const { findConfigFile, loadConfig } = await import("./config-
|
|
8033
|
+
const { findConfigFile, loadConfig } = await import("./config-BK78JDRI.mjs");
|
|
8034
8034
|
const resolved = await loadConfig(args.config ?? findConfigFile(process.cwd()));
|
|
8035
8035
|
await generateMigration({
|
|
8036
8036
|
schemaPath: resolved.schemaPath,
|
package/dist/config.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as Config, c as
|
|
2
|
-
import {
|
|
3
|
-
export { type BuildConfig, type
|
|
1
|
+
import { a as Config, c as PackageManagerSpec, d as ResolvedConfig, f as ResolvedPlugin, h as definePlugin, i as BundleConfig, l as Plugin, m as defineConfig, n as BuildContext, o as EmitContext, p as defineBuildConfig, r as BuildPlugin, s as MirrorConfig, t as BuildConfig, u as ResolvedBuildConfig } from "./build-config-Dzg2frpk.mjs";
|
|
2
|
+
import { c as getConfig, d as getSplashPath, g as subscribeConfig, l as getPlugin, n as ConfigSnapshot, r as PluginRecord, s as getAppEntrypoint, u as getPlugins } from "./runtime-BQWntcOb.mjs";
|
|
3
|
+
export { type BuildConfig, type BuildContext, type BuildPlugin, type BundleConfig, type Config, type ConfigSnapshot, type EmitContext, type MirrorConfig, type PackageManagerSpec, type Plugin, type PluginRecord, type ResolvedBuildConfig, type ResolvedConfig, type ResolvedPlugin, defineBuildConfig, defineConfig, definePlugin, getAppEntrypoint, getConfig, getPlugin, getPlugins, getSplashPath, subscribeConfig };
|
package/dist/config.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { getAppEntrypoint, getConfig, getPlugin, getPlugins, getSplashPath, subscribeConfig } from "./runtime.mjs";
|
|
2
|
-
import { n as defineConfig, r as definePlugin, t as defineBuildConfig } from "./build-config-
|
|
3
|
-
|
|
4
|
-
export { defineBuildConfig, defineConfig, definePlugin, dropFiles, getAppEntrypoint, getConfig, getPlugin, getPlugins, getSplashPath, stripIfDisabled, subscribeConfig };
|
|
2
|
+
import { n as defineConfig, r as definePlugin, t as defineBuildConfig } from "./build-config-pWdmLnrk.mjs";
|
|
3
|
+
export { defineBuildConfig, defineConfig, definePlugin, getAppEntrypoint, getConfig, getPlugin, getPlugins, getSplashPath, subscribeConfig };
|
|
@@ -469,7 +469,7 @@ function parseGenerateArgs(argv) {
|
|
|
469
469
|
}
|
|
470
470
|
async function runGenerate(argv) {
|
|
471
471
|
const flags = parseGenerateArgs(argv);
|
|
472
|
-
const { generateMigration } = await import("./cli-
|
|
472
|
+
const { generateMigration } = await import("./cli-kL6mPgBE.mjs");
|
|
473
473
|
if (flags.schema || flags.migrationsOut) {
|
|
474
474
|
if (!flags.schema || !flags.migrationsOut) {
|
|
475
475
|
console.error("zen db generate: --schema and --migrations must be passed together.");
|
|
@@ -495,7 +495,7 @@ async function runGenerate(argv) {
|
|
|
495
495
|
console.error(" --schema <path> --migrations <path> directly.");
|
|
496
496
|
process.exit(1);
|
|
497
497
|
}
|
|
498
|
-
const { loadConfig } = await import("./load-config-
|
|
498
|
+
const { loadConfig } = await import("./load-config-C4Oe2qZO.mjs").then((n) => n.n);
|
|
499
499
|
const { resolved } = await loadConfig(projectDir);
|
|
500
500
|
const cwd = path.resolve(process.cwd());
|
|
501
501
|
let bestMatch = null;
|
package/dist/db.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as createSchema, _ as InferSchema$1, a as dbStringify, b as blob, f as CollectionRefBrand$1, g as InferRoot$1, i as dbParse, n as connectReplica, o as ClientProxy, p as CollectionRefValue$1, s as CollectionNode$1, v as Schema$1, x as collection, y as SchemaShape$1 } from "./index-
|
|
1
|
+
import { S as createSchema, _ as InferSchema$1, a as dbStringify, b as blob, f as CollectionRefBrand$1, g as InferRoot$1, i as dbParse, n as connectReplica, o as ClientProxy, p as CollectionRefValue$1, s as CollectionNode$1, v as Schema$1, x as collection, y as SchemaShape$1 } from "./index-DeDxePAa.mjs";
|
|
2
2
|
import { z as z$1 } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/db.d.ts
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { t as linkProject } from "./link-glX89NV5.mjs";
|
|
2
|
+
import { existsSync, statSync } from "node:fs";
|
|
3
|
+
import path, { resolve } from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { subscribe } from "@parcel/watcher";
|
|
6
|
+
//#region src/cli/lib/link-watcher.ts
|
|
7
|
+
const RELINK_DEBOUNCE_MS = 120;
|
|
8
|
+
const COMMON_IGNORES = [
|
|
9
|
+
"**/node_modules",
|
|
10
|
+
"**/.git",
|
|
11
|
+
"**/.zenbu",
|
|
12
|
+
"**/.next",
|
|
13
|
+
"**/dist",
|
|
14
|
+
"**/build",
|
|
15
|
+
"**/.turbo",
|
|
16
|
+
"**/.cache"
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Start a file watcher that re-runs `zen link` whenever any source the
|
|
20
|
+
* link command consumes changes. Used by `zen dev` so users don't have to
|
|
21
|
+
* remember to rerun `zen link` after adding a service file or editing
|
|
22
|
+
* `zenbu.config.ts`.
|
|
23
|
+
*
|
|
24
|
+
* Failure modes:
|
|
25
|
+
* - Initial link throws (bad config): we still start the watcher so the
|
|
26
|
+
* user can recover by editing the file. The error is reported once;
|
|
27
|
+
* subsequent failed relinks during typing stay silent on purpose.
|
|
28
|
+
* - Relink throws (broken syntax mid-edit): swallowed silently. The next
|
|
29
|
+
* successful relink will catch up.
|
|
30
|
+
*
|
|
31
|
+
* The watcher rebuilds its subscription set after every successful relink:
|
|
32
|
+
* the resolved plugin set drives which directories are watched, and that
|
|
33
|
+
* set itself can change when the user edits `zenbu.config.ts` to add or
|
|
34
|
+
* remove a plugin entry.
|
|
35
|
+
*/
|
|
36
|
+
async function startLinkWatcher(projectDir, opts = {}) {
|
|
37
|
+
let subscriptions = [];
|
|
38
|
+
let closed = false;
|
|
39
|
+
let relinkScheduled = false;
|
|
40
|
+
let relinkInFlight = false;
|
|
41
|
+
let relinkPending = false;
|
|
42
|
+
let lastWatchKey = null;
|
|
43
|
+
let initial = null;
|
|
44
|
+
try {
|
|
45
|
+
initial = await linkProject(projectDir, {
|
|
46
|
+
registryOverride: opts.registryOverride ?? null,
|
|
47
|
+
quiet: true
|
|
48
|
+
});
|
|
49
|
+
if (opts.verbose) console.error(`[zen dev] initial link ok`);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(`[zen dev] initial link failed (will retry on file change): ${err instanceof Error ? err.message : err}`);
|
|
52
|
+
}
|
|
53
|
+
const compileFromResult = (result) => {
|
|
54
|
+
if (!result) return [{
|
|
55
|
+
rootDir: projectDir,
|
|
56
|
+
exactPaths: new Set([
|
|
57
|
+
"zenbu.config.ts",
|
|
58
|
+
"zenbu.config.mts",
|
|
59
|
+
"zenbu.config.js",
|
|
60
|
+
"zenbu.config.mjs"
|
|
61
|
+
].map((n) => path.join(projectDir, n))),
|
|
62
|
+
globMatchers: []
|
|
63
|
+
}];
|
|
64
|
+
return compileTargets(result);
|
|
65
|
+
};
|
|
66
|
+
const subscribeAll = async (targets) => {
|
|
67
|
+
await teardownAll();
|
|
68
|
+
if (closed) return;
|
|
69
|
+
for (const target of targets) try {
|
|
70
|
+
const sub = await subscribe(target.rootDir, (err, events) => {
|
|
71
|
+
if (err) return;
|
|
72
|
+
if (!eventsAreRelevant(events, target)) return;
|
|
73
|
+
scheduleRelink();
|
|
74
|
+
}, { ignore: COMMON_IGNORES });
|
|
75
|
+
if (closed) {
|
|
76
|
+
await sub.unsubscribe().catch(() => {});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
subscriptions.push(sub);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (opts.verbose) console.error(`[zen dev] watcher subscribe failed for ${target.rootDir}: ${err instanceof Error ? err.message : err}`);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const teardownAll = async () => {
|
|
85
|
+
const subs = subscriptions;
|
|
86
|
+
subscriptions = [];
|
|
87
|
+
await Promise.all(subs.map((s) => s.unsubscribe().catch(() => {})));
|
|
88
|
+
};
|
|
89
|
+
const reconcileSubscriptions = async (result) => {
|
|
90
|
+
const targets = compileFromResult(result);
|
|
91
|
+
const key = watchKey(targets);
|
|
92
|
+
if (key === lastWatchKey) return;
|
|
93
|
+
lastWatchKey = key;
|
|
94
|
+
await subscribeAll(targets);
|
|
95
|
+
};
|
|
96
|
+
await reconcileSubscriptions(initial);
|
|
97
|
+
function scheduleRelink() {
|
|
98
|
+
if (closed) return;
|
|
99
|
+
if (relinkInFlight) {
|
|
100
|
+
relinkPending = true;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (relinkScheduled) return;
|
|
104
|
+
relinkScheduled = true;
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
relinkScheduled = false;
|
|
107
|
+
runRelink();
|
|
108
|
+
}, RELINK_DEBOUNCE_MS);
|
|
109
|
+
}
|
|
110
|
+
async function runRelink() {
|
|
111
|
+
if (closed) return;
|
|
112
|
+
relinkInFlight = true;
|
|
113
|
+
try {
|
|
114
|
+
const result = await linkProject(projectDir, {
|
|
115
|
+
registryOverride: opts.registryOverride ?? null,
|
|
116
|
+
quiet: true
|
|
117
|
+
});
|
|
118
|
+
if (opts.verbose) console.error(`[zen dev] relinked`);
|
|
119
|
+
await reconcileSubscriptions(result);
|
|
120
|
+
} catch {} finally {
|
|
121
|
+
relinkInFlight = false;
|
|
122
|
+
if (relinkPending && !closed) {
|
|
123
|
+
relinkPending = false;
|
|
124
|
+
scheduleRelink();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { async close() {
|
|
129
|
+
closed = true;
|
|
130
|
+
await teardownAll();
|
|
131
|
+
} };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build the set of (directory, filter) pairs we need watchers for. The
|
|
135
|
+
* filter side matters: we subscribe recursively to a dir but only treat
|
|
136
|
+
* matching events as link-relevant.
|
|
137
|
+
*
|
|
138
|
+
* The set covers everything `linkProject` reads:
|
|
139
|
+
* - the `zenbu.config.ts` itself,
|
|
140
|
+
* - any `zenbu.plugin.ts` files imported from the config,
|
|
141
|
+
* - per-plugin schema/preload/events/migrations files,
|
|
142
|
+
* - per-plugin service glob expansions.
|
|
143
|
+
*/
|
|
144
|
+
function compileTargets(result) {
|
|
145
|
+
const exactPaths = new Set([result.resolvedConfigPath]);
|
|
146
|
+
for (const f of result.pluginSourceFiles) exactPaths.add(f);
|
|
147
|
+
const globMatchers = [];
|
|
148
|
+
const candidateDirs = /* @__PURE__ */ new Set();
|
|
149
|
+
candidateDirs.add(path.dirname(result.resolvedConfigPath));
|
|
150
|
+
for (const f of result.pluginSourceFiles) candidateDirs.add(path.dirname(f));
|
|
151
|
+
for (const plugin of result.resolved.plugins) addPluginPaths(plugin, exactPaths, globMatchers, candidateDirs);
|
|
152
|
+
return minimalCover([...candidateDirs]).map((rootDir) => ({
|
|
153
|
+
rootDir,
|
|
154
|
+
exactPaths,
|
|
155
|
+
globMatchers
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
function addPluginPaths(plugin, exactPaths, globMatchers, candidateDirs) {
|
|
159
|
+
candidateDirs.add(plugin.dir);
|
|
160
|
+
for (const p of [
|
|
161
|
+
plugin.schemaPath,
|
|
162
|
+
plugin.preloadPath,
|
|
163
|
+
plugin.eventsPath,
|
|
164
|
+
plugin.migrationsPath
|
|
165
|
+
]) if (p) {
|
|
166
|
+
exactPaths.add(p);
|
|
167
|
+
candidateDirs.add(path.dirname(p));
|
|
168
|
+
}
|
|
169
|
+
for (const serviceGlobAbs of plugin.services) {
|
|
170
|
+
if (!serviceGlobAbs.includes("*")) {
|
|
171
|
+
exactPaths.add(serviceGlobAbs);
|
|
172
|
+
candidateDirs.add(path.dirname(serviceGlobAbs));
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const dir = path.dirname(serviceGlobAbs);
|
|
176
|
+
const base = path.basename(serviceGlobAbs);
|
|
177
|
+
const re = new RegExp("^" + base.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$");
|
|
178
|
+
globMatchers.push({
|
|
179
|
+
dir,
|
|
180
|
+
re
|
|
181
|
+
});
|
|
182
|
+
candidateDirs.add(dir);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function eventsAreRelevant(events, target) {
|
|
186
|
+
for (const event of events) {
|
|
187
|
+
const abs = event.path;
|
|
188
|
+
if (target.exactPaths.has(abs)) return true;
|
|
189
|
+
for (const { dir, re } of target.globMatchers) if (path.dirname(abs) === dir && re.test(path.basename(abs))) return true;
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
function minimalCover(dirs) {
|
|
194
|
+
const unique = [...new Set(dirs.map((d) => path.resolve(d)))];
|
|
195
|
+
unique.sort((a, b) => a.length - b.length);
|
|
196
|
+
const kept = [];
|
|
197
|
+
for (const d of unique) if (!kept.some((k) => d === k || d.startsWith(k + path.sep))) kept.push(d);
|
|
198
|
+
return kept;
|
|
199
|
+
}
|
|
200
|
+
function watchKey(targets) {
|
|
201
|
+
const parts = [];
|
|
202
|
+
for (const t of targets) {
|
|
203
|
+
const exacts = [...t.exactPaths].sort().join("|");
|
|
204
|
+
const globs = t.globMatchers.map((m) => `${m.dir}::${m.re.source}`).sort().join("|");
|
|
205
|
+
parts.push(`${t.rootDir}<<${exacts}>>${globs}`);
|
|
206
|
+
}
|
|
207
|
+
return parts.sort().join("\n");
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/cli/commands/dev.ts
|
|
211
|
+
/**
|
|
212
|
+
* `zen dev` — launch the local app under Electron with the setup-gate as the
|
|
213
|
+
* main entry. This is the entrypoint of `pnpm dev` in scaffolded apps. The
|
|
214
|
+
* command is intentionally hookable: future versions can layer doctor checks,
|
|
215
|
+
* environment validation, or a managed dev-server here without touching the
|
|
216
|
+
* user's `package.json` scripts.
|
|
217
|
+
*
|
|
218
|
+
* Defaults to a foreground/blocking child so Ctrl+C in the terminal kills the
|
|
219
|
+
* Electron process and `pnpm dev` exits cleanly. Pass `--detach` to spawn it
|
|
220
|
+
* in the background instead (returns immediately, Electron lives until quit).
|
|
221
|
+
*/
|
|
222
|
+
function parseArgs(argv) {
|
|
223
|
+
let pathArg;
|
|
224
|
+
let detach = false;
|
|
225
|
+
let verbose = false;
|
|
226
|
+
let watch = true;
|
|
227
|
+
for (let i = 0; i < argv.length; i++) {
|
|
228
|
+
const arg = argv[i];
|
|
229
|
+
if (arg === "--detach") detach = true;
|
|
230
|
+
else if (arg === "--verbose" || arg === "-v") verbose = true;
|
|
231
|
+
else if (arg === "--no-watch") watch = false;
|
|
232
|
+
else if (!arg.startsWith("-") && pathArg == null) pathArg = arg;
|
|
233
|
+
else {
|
|
234
|
+
console.error(`zen dev: unknown flag "${arg}"`);
|
|
235
|
+
console.error(`valid: zen dev [path] [--detach] [--verbose] [--no-watch]`);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const projectDir = pathArg ? resolve(process.cwd(), pathArg) : process.cwd();
|
|
240
|
+
if (!existsSync(projectDir)) {
|
|
241
|
+
console.error(`zen dev: path "${pathArg}" does not exist`);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
if (!statSync(projectDir).isDirectory()) {
|
|
246
|
+
console.error(`zen dev: path "${pathArg}" is not a directory`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
250
|
+
console.error(`zen dev: cannot stat "${pathArg}"`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
projectDir,
|
|
255
|
+
detach,
|
|
256
|
+
verbose,
|
|
257
|
+
watch
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function resolveLocalElectron(projectDir) {
|
|
261
|
+
const candidates = [path.join(projectDir, "node_modules", "electron", "dist", "Electron.app", "Contents", "MacOS", "Electron"), path.join(projectDir, "node_modules", ".bin", "electron")];
|
|
262
|
+
for (const candidate of candidates) if (existsSync(candidate)) return candidate;
|
|
263
|
+
throw new Error(`Electron is not installed in ${projectDir}. Run \`pnpm install\` in the app.`);
|
|
264
|
+
}
|
|
265
|
+
function ensureSetupGate(projectDir) {
|
|
266
|
+
const setupGate = path.join(projectDir, "node_modules", "@zenbujs", "core", "dist", "setup-gate.mjs");
|
|
267
|
+
if (!existsSync(setupGate)) throw new Error(`@zenbujs/core setup-gate not found at ${setupGate}. Run \`pnpm install\` in the app.`);
|
|
268
|
+
}
|
|
269
|
+
async function runDev(argv) {
|
|
270
|
+
const { projectDir, detach, verbose, watch } = parseArgs(argv);
|
|
271
|
+
if (verbose) console.error("[zen dev] launching:", projectDir);
|
|
272
|
+
const electron = resolveLocalElectron(projectDir);
|
|
273
|
+
ensureSetupGate(projectDir);
|
|
274
|
+
let watcher = null;
|
|
275
|
+
if (watch && !detach) try {
|
|
276
|
+
watcher = await startLinkWatcher(projectDir, { verbose });
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.error(`[zen dev] link watcher disabled: ${err instanceof Error ? err.message : err}`);
|
|
279
|
+
}
|
|
280
|
+
const electronArgs = [projectDir, `--project=${projectDir}`];
|
|
281
|
+
if (detach) {
|
|
282
|
+
spawn(electron, electronArgs, {
|
|
283
|
+
cwd: projectDir,
|
|
284
|
+
detached: true,
|
|
285
|
+
stdio: "ignore"
|
|
286
|
+
}).unref();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const child = spawn(electron, electronArgs, {
|
|
290
|
+
cwd: projectDir,
|
|
291
|
+
stdio: "inherit"
|
|
292
|
+
});
|
|
293
|
+
process.on("SIGINT", () => child.kill("SIGINT"));
|
|
294
|
+
process.on("SIGTERM", () => child.kill("SIGTERM"));
|
|
295
|
+
child.on("exit", async (code, signal) => {
|
|
296
|
+
if (watcher) await watcher.close().catch(() => {});
|
|
297
|
+
process.exit(code ?? (signal ? 1 : 0));
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
export { runDev };
|
package/dist/env-bootstrap.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as bootstrapEnv } from "./env-bootstrap-
|
|
1
|
+
import { t as bootstrapEnv } from "./env-bootstrap-rTs8KR3-.mjs";
|
|
2
2
|
export { bootstrapEnv };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/events.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Core's own events file. Mirrors the contract every plugin follows
|
|
4
|
+
* (`events: "./path/to/events.ts"` in zenbu.config / zenbu.plugin),
|
|
5
|
+
* exporting a single `Events` type that the link generator scans into
|
|
6
|
+
* the registry's `CoreEvents`.
|
|
7
|
+
*
|
|
8
|
+
* Each top-level key is a namespace; `rpc.emit.<namespace>.<event>(payload)`
|
|
9
|
+
* inside a service emits with the matching payload type.
|
|
10
|
+
*/
|
|
11
|
+
type Events = {
|
|
12
|
+
advice: {
|
|
13
|
+
reload: {
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { Events };
|
package/dist/events.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/shared/host-version.ts
|
|
4
|
+
/**
|
|
5
|
+
* Single source of truth for reading the .app's "host version" — the
|
|
6
|
+
* concrete semver string the developer authored on
|
|
7
|
+
* `defineBuildConfig({ hostVersion: "..." })` and that
|
|
8
|
+
* `zen build:electron` baked into `<bundle>/host.json`.
|
|
9
|
+
*
|
|
10
|
+
* Imported by both:
|
|
11
|
+
* - `packages/core/src/launcher.ts` (tsdown inlines this into
|
|
12
|
+
* `dist/launcher.mjs`; the launcher cannot `import "@zenbujs/core"`)
|
|
13
|
+
* - `packages/core/src/services/updater.ts` (resolved through normal
|
|
14
|
+
* `@zenbujs/core/...` resolution at runtime)
|
|
15
|
+
*
|
|
16
|
+
* The file lives at the bundle root (`<APP_PATH>/host.json`, where
|
|
17
|
+
* `APP_PATH = app.getAppPath()`), separate from `app-config.json` so
|
|
18
|
+
* its single purpose stays obvious.
|
|
19
|
+
*/
|
|
20
|
+
const HOST_VERSION_FILENAME = "host.json";
|
|
21
|
+
/**
|
|
22
|
+
* Read `<appPath>/host.json` and return its `version` field. Throws an
|
|
23
|
+
* informative error if the file is missing, unreadable, malformed, or
|
|
24
|
+
* the `version` field is missing/empty. Callers in dev mode should
|
|
25
|
+
* check for the file's existence first (or use `tryReadHostVersion`).
|
|
26
|
+
*/
|
|
27
|
+
function readHostVersion(appPath) {
|
|
28
|
+
const filePath = path.join(appPath, HOST_VERSION_FILENAME);
|
|
29
|
+
let raw;
|
|
30
|
+
try {
|
|
31
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const e = err;
|
|
34
|
+
if (e.code === "ENOENT") throw new Error(`[host-version] missing ${filePath}. Was this .app built with a recent \`zen build:electron\`? The build step is responsible for writing host.json.`);
|
|
35
|
+
throw new Error(`[host-version] failed to read ${filePath}: ${e.message ?? String(e)}`);
|
|
36
|
+
}
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
throw new Error(`[host-version] ${filePath} is not valid JSON: ${err.message}`);
|
|
42
|
+
}
|
|
43
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.version !== "string" || parsed.version.trim().length === 0) throw new Error(`[host-version] ${filePath} is missing a non-empty \`version\` string.`);
|
|
44
|
+
return {
|
|
45
|
+
version: parsed.version.trim(),
|
|
46
|
+
path: filePath
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Non-throwing variant. Returns `null` when the file is absent or invalid. */
|
|
50
|
+
function tryReadHostVersion(appPath) {
|
|
51
|
+
try {
|
|
52
|
+
return readHostVersion(appPath);
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Inverse of `readHostVersion` — used by `zen build:electron`. */
|
|
58
|
+
function writeHostVersion(appPath, version) {
|
|
59
|
+
const filePath = path.join(appPath, HOST_VERSION_FILENAME);
|
|
60
|
+
const body = { version };
|
|
61
|
+
fs.writeFileSync(filePath, JSON.stringify(body, null, 2) + "\n");
|
|
62
|
+
return filePath;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { tryReadHostVersion as n, writeHostVersion as r, HOST_VERSION_FILENAME as t };
|