@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/runtime.mjs CHANGED
@@ -1,578 +1,2 @@
1
- //#region src/runtime.ts
2
- const DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
3
- function readShutdownTimeoutMs() {
4
- const raw = process.env.ZENBU_SHUTDOWN_TIMEOUT_MS;
5
- if (!raw) return DEFAULT_SHUTDOWN_TIMEOUT_MS;
6
- const value = Number(raw);
7
- return Number.isFinite(value) && value > 0 ? value : DEFAULT_SHUTDOWN_TIMEOUT_MS;
8
- }
9
- function resolveDepKey(entry) {
10
- if (typeof entry === "string") return entry;
11
- return entry.key;
12
- }
13
- var Service = class {
14
- static deps = {};
15
- /**
16
- * Define a Service base class. The returned abstract class has
17
- * `static key`, `static deps`, and a typed `this.ctx` already set up;
18
- * extend it and add your `evaluate()` body.
19
- *
20
- * export class WindowService extends Service.create({
21
- * key: "window",
22
- * deps: {
23
- * baseWindow: BaseWindowService,
24
- * http: HttpService,
25
- * },
26
- * }) {
27
- * evaluate() {
28
- * this.ctx.baseWindow // BaseWindowService
29
- * this.ctx.http // HttpService
30
- * }
31
- * }
32
- *
33
- * `key` is a required field on the config object, so TypeScript errors
34
- * if you forget it. `deps` is optional (defaults to no deps).
35
- *
36
- * For dynamic / optional access to another service, use
37
- * `runtime.get(SomeService, cb)` instead of declaring it in `deps`.
38
- */
39
- static create(config) {
40
- const { key, deps } = config;
41
- const resolvedDeps = deps ?? {};
42
- class ConfiguredService extends Service {
43
- static key = key;
44
- static deps = resolvedDeps;
45
- }
46
- return ConfiguredService;
47
- }
48
- ctx;
49
- /** @internal */
50
- __setupCleanups = /* @__PURE__ */ new Map();
51
- evaluate() {}
52
- setup(key, fn) {
53
- const existing = this.__setupCleanups.get(key);
54
- if (existing) try {
55
- existing("reload");
56
- } catch (e) {
57
- console.error(`[hot] setup cleanup "${key}" failed:`, e);
58
- }
59
- const cleanup = fn();
60
- if (cleanup) this.__setupCleanups.set(key, cleanup);
61
- else this.__setupCleanups.delete(key);
62
- }
63
- /**
64
- * Run `fn` and return its result. Historically reported a boot-trace span;
65
- * now a thin wrapper preserved for caller ergonomics inside service
66
- * `evaluate()` bodies.
67
- */
68
- trace(_name, fn, _meta) {
69
- return Promise.resolve(fn());
70
- }
71
- traceSync(_name, fn, _meta) {
72
- return fn();
73
- }
74
- /** @internal */
75
- async __cleanupAllSetups(reason = "shutdown") {
76
- for (const [key, cleanup] of this.__setupCleanups) try {
77
- await cleanup(reason);
78
- } catch (e) {
79
- console.error(`[hot] setup cleanup "${key}" failed:`, e);
80
- }
81
- this.__setupCleanups.clear();
82
- }
83
- };
84
- const SERVICE_BASE_METHODS = new Set(Object.getOwnPropertyNames(Service.prototype));
85
- var ServiceRuntime = class {
86
- definitions = /* @__PURE__ */ new Map();
87
- dependentsIndex = /* @__PURE__ */ new Map();
88
- dirtyKeys = /* @__PURE__ */ new Set();
89
- drainError = null;
90
- draining = null;
91
- registrationTokens = /* @__PURE__ */ new Map();
92
- slots = /* @__PURE__ */ new Map();
93
- onReconciledCallbacks = [];
94
- subscribers = /* @__PURE__ */ new Map();
95
- register(ServiceClass, importMeta) {
96
- const hot = importMeta?.hot ?? null;
97
- const slotKey = ServiceClass.key;
98
- if (typeof slotKey !== "string" || slotKey.length === 0) {
99
- const name = ServiceClass.name ?? "<anonymous>";
100
- throw new Error(`[runtime] service "${name}" is missing \`static key\`. Define it via \`Service.create({ key: "...", ... })\`.`);
101
- }
102
- this.definitions.set(slotKey, ServiceClass);
103
- const slot = this.slots.get(slotKey);
104
- if (slot) slot.ServiceClass = ServiceClass;
105
- else this.slots.set(slotKey, {
106
- error: null,
107
- instance: null,
108
- ServiceClass,
109
- status: "blocked"
110
- });
111
- this.rebuildDependentsIndex();
112
- hot?.accept();
113
- const token = Symbol(slotKey);
114
- this.registrationTokens.set(slotKey, token);
115
- hot?.prune?.(() => {
116
- this.unregister(slotKey, token);
117
- });
118
- this.scheduleReconcile([slotKey]);
119
- }
120
- getAllKeys() {
121
- return [...this.slots.keys()];
122
- }
123
- getSlot(key) {
124
- return this.slots.get(key);
125
- }
126
- async whenIdle() {
127
- while (this.draining) await this.draining;
128
- if (this.drainError) {
129
- const error = this.drainError;
130
- this.drainError = null;
131
- throw error;
132
- }
133
- }
134
- async reloadAll() {
135
- const keys = [...this.slots.keys()];
136
- if (keys.length === 0) return;
137
- await this.scheduleReconcile(keys);
138
- await this.whenIdle();
139
- }
140
- /**
141
- * Reload a single service by key. No-op if the key is not registered.
142
- * Used by infrastructure that watches resources outside dynohot's
143
- * import-graph (e.g. the migrations directory watcher in DbService) and
144
- * needs to nudge a specific service to re-evaluate without leaning on
145
- * the devtools-only `__zenbu_dev__.reloadService` hook.
146
- */
147
- async reload(key) {
148
- if (!this.slots.has(key)) return;
149
- await this.scheduleReconcile([key]);
150
- await this.whenIdle();
151
- }
152
- async shutdown() {
153
- try {
154
- await this.whenIdle();
155
- } catch (error) {
156
- console.error("[hot] runtime idle wait failed during shutdown:", error);
157
- }
158
- const keys = [...this.slots.keys()].reverse();
159
- for (const key of keys) await this.teardownService(key, { removeSlot: true });
160
- this.definitions.clear();
161
- this.dependentsIndex.clear();
162
- this.dirtyKeys.clear();
163
- this.registrationTokens.clear();
164
- }
165
- /**
166
- * Subscribe to a service. Behavior-subject style: the callback fires
167
- * synchronously once with the current value (the live instance if ready,
168
- * `undefined` otherwise), then again on every reconcile of that service —
169
- * so you see the new instance after each HMR. Pass through `undefined`
170
- * when the service tears down or unregisters.
171
- *
172
- * Returns an unsubscribe function. Always call it from a `setup()` cleanup
173
- * (or wherever you'd otherwise leak callbacks across reloads).
174
- */
175
- get(ref, cb) {
176
- const key = ref.key;
177
- let subs = this.subscribers.get(key);
178
- if (!subs) {
179
- subs = /* @__PURE__ */ new Set();
180
- this.subscribers.set(key, subs);
181
- }
182
- const wrapped = cb;
183
- subs.add(wrapped);
184
- const slot = this.slots.get(key);
185
- const current = slot?.status === "ready" && slot.instance ? slot.instance : void 0;
186
- try {
187
- cb(current);
188
- } catch (e) {
189
- console.error(`[hot] runtime.get subscriber for "${key}" threw:`, e);
190
- }
191
- return () => {
192
- const set = this.subscribers.get(key);
193
- if (!set) return;
194
- set.delete(wrapped);
195
- if (set.size === 0) this.subscribers.delete(key);
196
- };
197
- }
198
- fireSubscribers(key) {
199
- const subs = this.subscribers.get(key);
200
- if (!subs || subs.size === 0) return;
201
- const slot = this.slots.get(key);
202
- const instance = slot?.status === "ready" && slot.instance ? slot.instance : void 0;
203
- for (const cb of [...subs]) try {
204
- cb(instance);
205
- } catch (e) {
206
- console.error(`[hot] runtime.get subscriber for "${key}" threw:`, e);
207
- }
208
- }
209
- buildRouter() {
210
- const router = {};
211
- for (const [slotKey, slot] of this.slots) {
212
- if (slot.status !== "ready" || !slot.instance) continue;
213
- const proto = Object.getPrototypeOf(slot.instance);
214
- const methods = {};
215
- for (const name of Object.getOwnPropertyNames(proto)) {
216
- if (SERVICE_BASE_METHODS.has(name)) continue;
217
- if (name.startsWith("_")) continue;
218
- const desc = Object.getOwnPropertyDescriptor(proto, name);
219
- if (!desc || typeof desc.value !== "function") continue;
220
- const instance = slot.instance;
221
- methods[name] = (...args) => instance[name](...args);
222
- }
223
- if (Object.keys(methods).length > 0) router[slotKey] = methods;
224
- }
225
- return router;
226
- }
227
- onReconciled(cb) {
228
- this.onReconciledCallbacks.push(cb);
229
- return () => {
230
- const idx = this.onReconciledCallbacks.indexOf(cb);
231
- if (idx >= 0) this.onReconciledCallbacks.splice(idx, 1);
232
- };
233
- }
234
- resolveDepSlot(depKey) {
235
- return this.slots.get(depKey);
236
- }
237
- injectCtx(instance, ServiceClass) {
238
- const deps = ServiceClass.deps ?? {};
239
- const ctx = {};
240
- for (const [name, entry] of Object.entries(deps)) {
241
- const key = resolveDepKey(entry);
242
- const slot = this.resolveDepSlot(key);
243
- if (!slot || slot.status !== "ready" || !slot.instance) throw new Error(`Dependency "${key}" not ready for "${ServiceClass.key}"`);
244
- ctx[name] = slot.instance;
245
- }
246
- instance.ctx = ctx;
247
- }
248
- rebuildDependentsIndex() {
249
- const next = /* @__PURE__ */ new Map();
250
- for (const [slotKey, ServiceClass] of this.definitions) {
251
- const deps = ServiceClass.deps ?? {};
252
- for (const entry of Object.values(deps)) {
253
- const depKey = resolveDepKey(entry);
254
- const dependents = next.get(depKey) ?? /* @__PURE__ */ new Set();
255
- dependents.add(slotKey);
256
- next.set(depKey, dependents);
257
- }
258
- }
259
- this.dependentsIndex = next;
260
- }
261
- getAffectedKeys(changedKeys) {
262
- const affected = /* @__PURE__ */ new Set();
263
- const visit = (key) => {
264
- if (affected.has(key)) return;
265
- affected.add(key);
266
- for (const dependent of this.dependentsIndex.get(key) ?? []) visit(dependent);
267
- };
268
- for (const key of changedKeys) visit(key);
269
- return [...affected].filter((key) => this.definitions.has(key));
270
- }
271
- listMissingDeps(ServiceClass) {
272
- const deps = ServiceClass.deps ?? {};
273
- const missing = [];
274
- for (const entry of Object.values(deps)) {
275
- const key = resolveDepKey(entry);
276
- const slot = this.resolveDepSlot(key);
277
- if (!slot || slot.status !== "ready" || !slot.instance) missing.push(key);
278
- }
279
- return missing;
280
- }
281
- ensureSlot(key, ServiceClass) {
282
- const existing = this.slots.get(key);
283
- if (existing) return existing;
284
- const slot = {
285
- error: null,
286
- instance: null,
287
- ServiceClass,
288
- status: "blocked"
289
- };
290
- this.slots.set(key, slot);
291
- return slot;
292
- }
293
- async teardownService(key, options = {}) {
294
- const slot = this.slots.get(key);
295
- if (!slot) return;
296
- const instance = slot.instance;
297
- slot.instance = null;
298
- slot.status = "blocked";
299
- slot.error = null;
300
- if (instance) {
301
- const reason = options.reason ?? "shutdown";
302
- const timeoutMs = readShutdownTimeoutMs();
303
- const cleanup = instance.__cleanupAllSetups(reason);
304
- let timer = null;
305
- const timeout = new Promise((resolve) => {
306
- timer = setTimeout(() => {
307
- console.error(`[hot] ${key} ${reason} cleanup timed out after ${timeoutMs}ms; forcing teardown`);
308
- resolve();
309
- }, timeoutMs);
310
- timer.unref?.();
311
- });
312
- try {
313
- await Promise.race([cleanup, timeout]);
314
- } finally {
315
- if (timer) clearTimeout(timer);
316
- }
317
- }
318
- if (options.removeSlot) this.slots.delete(key);
319
- }
320
- async unregister(key, token) {
321
- if (this.registrationTokens.get(key) !== token) return;
322
- this.registrationTokens.delete(key);
323
- this.definitions.delete(key);
324
- await this.teardownService(key, { removeSlot: true });
325
- this.rebuildDependentsIndex();
326
- this.fireSubscribers(key);
327
- await this.scheduleReconcile([key]);
328
- }
329
- async scheduleReconcile(keys) {
330
- for (const key of keys) this.dirtyKeys.add(key);
331
- if (this.draining) return this.draining;
332
- this.draining = (async () => {
333
- try {
334
- while (this.dirtyKeys.size > 0) {
335
- const batch = [...this.dirtyKeys];
336
- this.dirtyKeys.clear();
337
- await this.reconcileBatch(batch);
338
- }
339
- } catch (error) {
340
- this.drainError = error;
341
- console.error("[hot] runtime reconcile failed:", error);
342
- } finally {
343
- this.draining = null;
344
- if (this.dirtyKeys.size > 0) this.scheduleReconcile([]);
345
- }
346
- })();
347
- return this.draining;
348
- }
349
- async reconcileBatch(changedKeys) {
350
- const affectedKeys = this.getAffectedKeys(changedKeys);
351
- if (affectedKeys.length === 0) return;
352
- const affected = /* @__PURE__ */ new Map();
353
- for (const key of affectedKeys) {
354
- const ServiceClass = this.definitions.get(key);
355
- if (ServiceClass) affected.set(key, ServiceClass);
356
- }
357
- const levels = this.topologicalLevels(affected);
358
- const wasReady = /* @__PURE__ */ new Set();
359
- for (const key of affectedKeys) if (this.slots.get(key)?.status === "ready") wasReady.add(key);
360
- for (const level of [...levels].reverse()) await Promise.all(level.map((key) => this.teardownService(key, { reason: "reload" })));
361
- for (const level of levels) await Promise.all(level.map((key) => this.reconcileKey(key, wasReady)));
362
- for (const key of affectedKeys) this.fireSubscribers(key);
363
- if (affectedKeys.length > 0) for (const cb of this.onReconciledCallbacks) try {
364
- cb(affectedKeys);
365
- } catch (e) {
366
- console.error("[hot] onReconciled callback failed:", e);
367
- }
368
- }
369
- async reconcileKey(key, wasReady) {
370
- const ServiceClass = this.definitions.get(key);
371
- if (!ServiceClass) return;
372
- const slot = this.ensureSlot(key, ServiceClass);
373
- slot.ServiceClass = ServiceClass;
374
- const missingDeps = this.listMissingDeps(ServiceClass);
375
- if (missingDeps.length > 0) {
376
- await this.teardownService(key, { reason: "reload" });
377
- if (wasReady.has(key)) console.log(`[hot] ${key} waiting on: ${missingDeps.join(", ")}`);
378
- return;
379
- }
380
- const instance = new ServiceClass();
381
- slot.instance = instance;
382
- slot.status = "evaluating";
383
- slot.error = null;
384
- try {
385
- this.injectCtx(instance, ServiceClass);
386
- await instance.evaluate();
387
- slot.status = "ready";
388
- } catch (e) {
389
- slot.status = "failed";
390
- slot.error = e;
391
- console.error(`[hot] ${key} failed to evaluate:`, e);
392
- }
393
- }
394
- topologicalLevels(services) {
395
- const keys = new Set(services.keys());
396
- const inDegree = /* @__PURE__ */ new Map();
397
- const dependents = /* @__PURE__ */ new Map();
398
- for (const key of keys) {
399
- inDegree.set(key, 0);
400
- dependents.set(key, []);
401
- }
402
- for (const [slotKey, ServiceClass] of services) {
403
- const deps = ServiceClass.deps ?? {};
404
- let degree = 0;
405
- for (const entry of Object.values(deps)) {
406
- const depKey = resolveDepKey(entry);
407
- if (keys.has(depKey)) {
408
- degree++;
409
- dependents.get(depKey).push(slotKey);
410
- }
411
- }
412
- inDegree.set(slotKey, degree);
413
- }
414
- const levels = [];
415
- let queue = [...keys].filter((k) => inDegree.get(k) === 0);
416
- while (queue.length > 0) {
417
- levels.push(queue);
418
- const next = [];
419
- for (const key of queue) for (const dep of dependents.get(key)) {
420
- const d = inDegree.get(dep) - 1;
421
- inDegree.set(dep, d);
422
- if (d === 0) next.push(dep);
423
- }
424
- queue = next;
425
- }
426
- const resolved = levels.flat();
427
- if (resolved.length !== keys.size) {
428
- const missing = [...keys].filter((k) => !resolved.includes(k));
429
- throw new Error(`Circular dependency detected involving: ${missing.join(", ")}`);
430
- }
431
- return levels;
432
- }
433
- };
434
- /**
435
- * fixme: i don't think these commments make sense
436
- */
437
- /**
438
- * Devtools-only handles, attached to the global so they aren't part of the
439
- * public `ServiceRuntime` autocomplete surface. Same shape React DevTools
440
- * uses (`__REACT_DEVTOOLS_GLOBAL_HOOK__`): user code reading
441
- * `runtime.<...>` never lands on these methods; you only find them if you
442
- * specifically type `globalThis.__zenbu_dev__`. Kept here (next to the
443
- * runtime that owns the state) because the hook reaches into private
444
- * implementation details — `scheduleReconcile` stays `private` on the
445
- * class; the cast lives inside the encapsulation boundary.
446
- */
447
- function installDevHook(rt) {
448
- const internals = rt;
449
- globalThis.__zenbu_dev__ = { reloadService: async (key) => {
450
- if (!internals.definitions.has(key)) throw new Error(`No service registered for key "${key}"`);
451
- await internals.scheduleReconcile([key]);
452
- await rt.whenIdle();
453
- } };
454
- }
455
- const runtime = (() => {
456
- const existing = globalThis.__zenbu_service_runtime__;
457
- if (existing) {
458
- Object.setPrototypeOf(existing, ServiceRuntime.prototype);
459
- installDevHook(existing);
460
- return existing;
461
- }
462
- const fresh = new ServiceRuntime();
463
- globalThis.__zenbu_service_runtime__ = fresh;
464
- installDevHook(fresh);
465
- return fresh;
466
- })();
467
- function getPluginRegistry() {
468
- const slot = globalThis;
469
- if (!slot.__zenbu_plugin_registry__) slot.__zenbu_plugin_registry__ = {
470
- plugins: /* @__PURE__ */ new Map(),
471
- appEntrypoint: null,
472
- splashPath: null,
473
- subscribers: /* @__PURE__ */ new Set()
474
- };
475
- else if (!slot.__zenbu_plugin_registry__.subscribers) slot.__zenbu_plugin_registry__.subscribers = /* @__PURE__ */ new Set();
476
- return slot.__zenbu_plugin_registry__;
477
- }
478
- function snapshotConfig(reg) {
479
- return {
480
- plugins: [...reg.plugins.values()],
481
- appEntrypoint: reg.appEntrypoint,
482
- splashPath: reg.splashPath
483
- };
484
- }
485
- function notifySubscribers(reg) {
486
- if (reg.subscribers.size === 0) return;
487
- const snapshot = snapshotConfig(reg);
488
- for (const cb of reg.subscribers) try {
489
- cb(snapshot);
490
- } catch (err) {
491
- console.error("[zenbu config subscriber] threw:", err);
492
- }
493
- }
494
- /**
495
- * Register a plugin's resolved manifest. Idempotent — replaces any existing
496
- * entry for the same `name`. Called by the loader-emitted barrel; user code
497
- * normally does not call this directly.
498
- */
499
- function registerPlugin(record) {
500
- const reg = getPluginRegistry();
501
- reg.plugins.set(record.name, record);
502
- notifySubscribers(reg);
503
- }
504
- /**
505
- * Drop a plugin from the registry. Used when the loader regenerates the
506
- * barrel and needs to clear stale entries.
507
- */
508
- function unregisterPlugin(name) {
509
- const reg = getPluginRegistry();
510
- if (!reg.plugins.delete(name)) return;
511
- notifySubscribers(reg);
512
- }
513
- /**
514
- * Replace the entire plugin set in one shot. The loader uses this on every
515
- * barrel regeneration so removed plugins disappear cleanly.
516
- */
517
- function replacePlugins(records) {
518
- const reg = getPluginRegistry();
519
- reg.plugins.clear();
520
- for (const record of records) reg.plugins.set(record.name, record);
521
- notifySubscribers(reg);
522
- }
523
- function getPlugins() {
524
- return [...getPluginRegistry().plugins.values()];
525
- }
526
- function getPlugin(name) {
527
- return getPluginRegistry().plugins.get(name);
528
- }
529
- /**
530
- * Set the renderer entrypoint directory + the absolute path to splash.html
531
- * inside it. Called once by the loader-emitted barrel. Consumers
532
- * (`view-registry`, `vite-plugins`, `setup-gate`'s splash window) read
533
- * via `getAppEntrypoint()` / `getSplashPath()`.
534
- */
535
- function registerAppEntrypoint(rendererDir, splashPath) {
536
- const reg = getPluginRegistry();
537
- reg.appEntrypoint = rendererDir;
538
- reg.splashPath = splashPath;
539
- notifySubscribers(reg);
540
- }
541
- function getAppEntrypoint() {
542
- return getPluginRegistry().appEntrypoint;
543
- }
544
- function getSplashPath() {
545
- return getPluginRegistry().splashPath;
546
- }
547
- /**
548
- * Snapshot of the current resolved config — plugins + entrypoints. Cheap;
549
- * just walks the in-memory registry. The returned object is a fresh copy,
550
- * safe to retain or pass through serialization boundaries.
551
- */
552
- function getConfig() {
553
- return snapshotConfig(getPluginRegistry());
554
- }
555
- /**
556
- * Subscribe to config changes. The callback fires:
557
- * - immediately on subscription (with the current snapshot),
558
- * - after each `replacePlugins(...)` / `registerPlugin(...)` /
559
- * `registerAppEntrypoint(...)` call — i.e. every time the loader
560
- * regenerates the plugin barrel after a `zenbu.config.ts` edit.
561
- *
562
- * Callback exceptions are logged and swallowed so one buggy subscriber
563
- * can't break others. The returned function unsubscribes.
564
- */
565
- function subscribeConfig(callback) {
566
- const reg = getPluginRegistry();
567
- reg.subscribers.add(callback);
568
- try {
569
- callback(snapshotConfig(reg));
570
- } catch (err) {
571
- console.error("[zenbu config subscriber] initial fire threw:", err);
572
- }
573
- return () => {
574
- reg.subscribers.delete(callback);
575
- };
576
- }
577
- //#endregion
578
- export { Service, ServiceRuntime, getAppEntrypoint, getConfig, getPlugin, getPlugins, getSplashPath, registerAppEntrypoint, registerPlugin, replacePlugins, runtime, subscribeConfig, unregisterPlugin };
1
+ import { a as getConfig, c as getSplashPath, d as replacePlugins, f as runtime, i as getAppEntrypoint, l as registerAppEntrypoint, m as unregisterPlugin, n as Service, o as getPlugin, p as subscribeConfig, r as ServiceRuntime, s as getPlugins, t as CORE_PLUGIN_NAME, u as registerPlugin } from "./runtime-DYUONc3S.mjs";
2
+ export { CORE_PLUGIN_NAME, Service, ServiceRuntime, getAppEntrypoint, getConfig, getPlugin, getPlugins, getSplashPath, registerAppEntrypoint, registerPlugin, replacePlugins, runtime, subscribeConfig, unregisterPlugin };
@@ -1,5 +1,7 @@
1
- import { m as Field, v as Schema } from "./index-DeDxePAa.mjs";
1
+ import { m as Field, v as InferSchemaRoot, y as Schema } from "./index-FaexRVl_.mjs";
2
+ import * as _$zod from "zod";
2
3
  import zod from "zod";
4
+ import * as _$zod_v4_core0 from "zod/v4/core";
3
5
 
4
6
  //#region src/schema.d.ts
5
7
  declare const viewRegistryEntrySchema: zod.ZodObject<{
@@ -29,34 +31,20 @@ declare const windowPrefsSchema: zod.ZodObject<{
29
31
  }, zod.core.$strip>>;
30
32
  }, zod.core.$strip>;
31
33
  declare const schema: Schema<{
32
- lastKnownViewRegistry: Field<zod.ZodArray<zod.ZodObject<{
33
- type: zod.ZodString;
34
- url: zod.ZodString;
35
- port: zod.ZodNumber;
36
- icon: zod.ZodOptional<zod.ZodString>;
37
- meta: zod.ZodOptional<zod.ZodObject<{
38
- kind: zod.ZodOptional<zod.ZodString>;
39
- sidebar: zod.ZodOptional<zod.ZodBoolean>;
40
- bottomPanel: zod.ZodOptional<zod.ZodBoolean>;
41
- label: zod.ZodOptional<zod.ZodString>;
42
- }, zod.core.$strip>>;
43
- }, zod.core.$strip>>, true>;
44
- windowPrefs: Field<zod.ZodRecord<zod.ZodString, zod.ZodObject<{
45
- lastKnownBounds: zod.ZodOptional<zod.ZodObject<{
46
- x: zod.ZodNumber;
47
- y: zod.ZodNumber;
48
- width: zod.ZodNumber;
49
- height: zod.ZodNumber;
50
- }, zod.core.$strip>>;
51
- }, zod.core.$strip>>, true>;
34
+ /**
35
+ *
36
+ * this needs to be changed, and we probably
37
+ * should have an api for reading in memory state
38
+ * on the service so we don't need to do these hacks
39
+ *
40
+ * */
41
+ lastKnownViewRegistry: Field<_$zod.ZodArray<_$zod.ZodType<unknown, unknown, _$zod_v4_core0.$ZodTypeInternals<unknown, unknown>>>, true>;
42
+ windowPrefs: Field<_$zod.ZodRecord<_$zod.ZodType<string | number | symbol, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | symbol, unknown>>, _$zod.ZodType<unknown, unknown, _$zod_v4_core0.$ZodTypeInternals<unknown, unknown>>>, true>;
52
43
  }>;
53
44
  type ViewRegistryEntry = zod.infer<typeof viewRegistryEntrySchema>;
54
45
  type WindowBounds = zod.infer<typeof windowBoundsSchema>;
55
46
  type WindowPrefs = zod.infer<typeof windowPrefsSchema>;
56
- type SchemaRoot = {
57
- lastKnownViewRegistry: ViewRegistryEntry[];
58
- windowPrefs: Record<string, WindowPrefs>;
59
- };
47
+ type SchemaRoot = InferSchemaRoot<typeof schema>;
60
48
  type CoreSchema = typeof schema.shape;
61
49
  //#endregion
62
50
  export { WindowPrefs as a, WindowBounds as i, SchemaRoot as n, schema as o, ViewRegistryEntry as r, CoreSchema as t };
package/dist/schema.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as WindowPrefs, i as WindowBounds, n as SchemaRoot, o as schema, r as ViewRegistryEntry, t as CoreSchema } from "./schema-CjrMVk36.mjs";
2
- export { CoreSchema, SchemaRoot, ViewRegistryEntry, WindowBounds, WindowPrefs, schema };
1
+ import { a as WindowPrefs, i as WindowBounds, n as SchemaRoot, o as schema, r as ViewRegistryEntry, t as CoreSchema } from "./schema-brYpUjYO.mjs";
2
+ export { CoreSchema, SchemaRoot, ViewRegistryEntry, WindowBounds, WindowPrefs, schema as default, schema };
package/dist/schema.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as f, i as createSchema } from "./schema-Ca7SxXgS.mjs";
1
+ import { a as f, i as createSchema } from "./schema-C6k0SroY.mjs";
2
2
  import zod from "zod";
3
3
  //#region src/schema.ts
4
4
  const viewRegistryEntrySchema = zod.object({
@@ -21,8 +21,15 @@ const windowBoundsSchema = zod.object({
21
21
  });
22
22
  const windowPrefsSchema = zod.object({ lastKnownBounds: windowBoundsSchema.optional() });
23
23
  const schema = createSchema({
24
+ /**
25
+ *
26
+ * this needs to be changed, and we probably
27
+ * should have an api for reading in memory state
28
+ * on the service so we don't need to do these hacks
29
+ *
30
+ * */
24
31
  lastKnownViewRegistry: f.array(viewRegistryEntrySchema).default([]),
25
32
  windowPrefs: f.record(zod.string(), windowPrefsSchema).default({})
26
33
  });
27
34
  //#endregion
28
- export { schema };
35
+ export { schema as default, schema };
@@ -1,6 +1,6 @@
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";
2
+ import { f as runtime, n as Service } from "./runtime-DYUONc3S.mjs";
3
+ import { t as createLogger } from "./log-BkRqDwwB.mjs";
4
4
  import http from "node:http";
5
5
  import { randomBytes, timingSafeEqual } from "node:crypto";
6
6
  import { WebSocketServer } from "ws";
@@ -1,10 +1,6 @@
1
1
  //#region src/services/default.d.ts
2
2
  /**
3
- * Loads and registers all built-in services. Kept separate from
4
- * `./index.ts` (which statically re-exports the service classes for
5
- * `Service.withDeps(...)` consumers) so that `setup-gate` can import it
6
- * without eagerly loading every service module before `setupGate()` has
7
- * had a chance to bootstrap env vars and `process.chdir(projectRoot)`.
3
+ * parallelize? i forgor is it serial no matter what?
8
4
  */
9
5
  declare function defaultServices(): Promise<void>;
10
6
  //#endregion