@vmz/vmz 0.1.4 → 0.1.6

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.
@@ -17,6 +17,7 @@ import { buildIntegratedDocuments, projectHasDocuments } from './document-integr
17
17
  import { createWorkspace, resolveNativePath } from './index.js';
18
18
  import { emitLocaleRuntimeModules, localeHasErrors } from './locale-check.js';
19
19
  import { log } from './log.js';
20
+ import { coalesceRootBurst, collectDevWatchRoots, isDependencyPath, mergeDirtySets, } from './dev-watch-roots.js';
20
21
  import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
21
22
  /**
22
23
  * @typedef {object} DevSessionOptions
@@ -189,13 +190,20 @@ export function createDevSession(options) {
189
190
  }
190
191
  const docsRoot = path.join(project, 'documents');
191
192
  const localesRoot = path.join(project, 'locales');
192
- const watchRoots = [src].concat(existsSync(docsRoot) ? [docsRoot] : []).concat(existsSync(localesRoot) ? [localesRoot] : []);
193
+ const watched = collectDevWatchRoots({ project, outDir });
194
+ /** @type {string[]} */
195
+ const watchRoots = [...watched.roots];
196
+ /** @type {string[]} */
197
+ let dependencyRoots = [...watched.dependencyRoots];
193
198
  if (wechatPreview) {
194
199
  log.info(`dev → WeChat DevTools (watching ${watchRoots.join(', ')}; keep dist/wechat open)`);
195
200
  }
196
201
  else {
197
202
  log.info(`dev → http://${host}:${port} (watching ${watchRoots.join(', ')})`);
198
203
  }
204
+ if (dependencyRoots.length) {
205
+ log.info(`dev dependency watch roots (${dependencyRoots.length}): ${dependencyRoots.join(', ')}`);
206
+ }
199
207
  /** @type {Map<string, Map<string, string>>} */
200
208
  const fingerprints = new Map();
201
209
  for (const root of watchRoots) {
@@ -207,18 +215,48 @@ export function createDevSession(options) {
207
215
  };
208
216
  signal?.addEventListener('abort', onAbort, { once: true });
209
217
  /**
210
- * Wait until src/ stops changing (multi-file agent edits).
218
+ * Scan all watch roots into a batch. Does not update fingerprints.
211
219
  */
212
- async function coalesceSrcBurst() {
213
- let guard = 0;
214
- while (guard++ < 20) {
215
- await sleep(220);
216
- const prev = fingerprints.get(src) || new Map();
217
- const next = fileFingerprintMap(src);
220
+ function scanBatch() {
221
+ /** @type {{ srcChanged: string[], srcDeleted: string[], depChanged: string[], depDeleted: string[], docsDirty: boolean, localesDirty: boolean }} */
222
+ const batch = {
223
+ srcChanged: [],
224
+ srcDeleted: [],
225
+ depChanged: [],
226
+ depDeleted: [],
227
+ docsDirty: false,
228
+ localesDirty: false,
229
+ };
230
+ for (const root of watchRoots) {
231
+ const prev = fingerprints.get(root) || new Map();
232
+ const next = fileFingerprintMap(root);
218
233
  const diff = diffFingerprints(prev, next);
219
234
  if (!diff.changed.length && !diff.deleted.length)
220
- break;
221
- fingerprints.set(src, next);
235
+ continue;
236
+ if (root === src) {
237
+ batch.srcChanged.push(...diff.changed);
238
+ batch.srcDeleted.push(...diff.deleted);
239
+ }
240
+ else if (root === localesRoot) {
241
+ batch.localesDirty = true;
242
+ }
243
+ else if (root === docsRoot) {
244
+ batch.docsDirty = true;
245
+ }
246
+ else if (dependencyRoots.includes(root)) {
247
+ batch.depChanged.push(...diff.changed);
248
+ batch.depDeleted.push(...diff.deleted);
249
+ }
250
+ else {
251
+ // designs or other application roots → treat like docs (full reload)
252
+ batch.docsDirty = true;
253
+ }
254
+ }
255
+ return batch;
256
+ }
257
+ function commitFingerprints() {
258
+ for (const root of watchRoots) {
259
+ fingerprints.set(root, fileFingerprintMap(root));
222
260
  }
223
261
  }
224
262
  try {
@@ -232,58 +270,112 @@ export function createDevSession(options) {
232
270
  await waitHostReady().catch(() => { });
233
271
  continue;
234
272
  }
235
- /** @type {{ srcChanged: string[], srcDeleted: string[], docsDirty: boolean, localesDirty: boolean }} */
236
- let batch = { srcChanged: [], srcDeleted: [], docsDirty: false, localesDirty: false };
273
+ let batch;
237
274
  try {
238
- for (const root of watchRoots) {
239
- const prev = fingerprints.get(root) || new Map();
240
- const next = fileFingerprintMap(root);
241
- const diff = diffFingerprints(prev, next);
242
- if (root === src) {
243
- batch.srcChanged = diff.changed;
244
- batch.srcDeleted = diff.deleted;
245
- }
246
- else if (root === localesRoot) {
247
- if (diff.changed.length || diff.deleted.length)
248
- batch.localesDirty = true;
249
- }
250
- else if (diff.changed.length || diff.deleted.length) {
251
- batch.docsDirty = true;
252
- }
253
- }
275
+ batch = scanBatch();
254
276
  }
255
277
  catch (err) {
256
278
  log.warn('watch error:', err);
257
279
  continue;
258
280
  }
259
- if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
281
+ const srcDirty = batch.srcChanged.length + batch.srcDeleted.length;
282
+ const depDirty = batch.depChanged.length + batch.depDeleted.length;
283
+ if (!srcDirty && !depDirty && !batch.docsDirty && !batch.localesDirty)
260
284
  continue;
261
- if (batch.srcChanged.length + batch.srcDeleted.length > 1) {
262
- await coalesceSrcBurst();
285
+ // Coalesce multi-file bursts without dropping the initial dirty set.
286
+ if (srcDirty > 1) {
287
+ const coalesced = await coalesceRootBurst(src, fingerprints, {
288
+ changed: batch.srcChanged,
289
+ deleted: batch.srcDeleted,
290
+ });
291
+ batch.srcChanged = coalesced.changed;
292
+ batch.srcDeleted = coalesced.deleted;
263
293
  }
264
- else {
294
+ if (depDirty > 1) {
295
+ // Coalesce each dirty dependency root independently, then merge.
296
+ /** @type {{ changed: string[], deleted: string[] }} */
297
+ let acc = { changed: [...batch.depChanged], deleted: [...batch.depDeleted] };
298
+ for (const root of dependencyRoots) {
299
+ const prev = fingerprints.get(root) || new Map();
300
+ const next = fileFingerprintMap(root);
301
+ const peek = diffFingerprints(prev, next);
302
+ if (peek.changed.length + peek.deleted.length <= 1 && !acc.changed.some((f) => isDependencyPath(f, [root]))) {
303
+ continue;
304
+ }
305
+ const coalesced = await coalesceRootBurst(root, fingerprints, {
306
+ changed: acc.changed.filter((f) => isDependencyPath(f, [root])),
307
+ deleted: acc.deleted.filter((f) => isDependencyPath(f, [root])),
308
+ });
309
+ const others = {
310
+ changed: acc.changed.filter((f) => !isDependencyPath(f, [root])),
311
+ deleted: acc.deleted.filter((f) => !isDependencyPath(f, [root])),
312
+ };
313
+ acc = mergeDirtySets(others, coalesced);
314
+ }
315
+ batch.depChanged = acc.changed;
316
+ batch.depDeleted = acc.deleted;
317
+ }
318
+ else if (srcDirty === 1 || depDirty === 1) {
265
319
  await sleep(200);
266
320
  }
267
- batch = { srcChanged: [], srcDeleted: [], docsDirty: false, localesDirty: false };
268
- for (const root of watchRoots) {
269
- const prev = fingerprints.get(root) || new Map();
270
- const next = fileFingerprintMap(root);
271
- const diff = diffFingerprints(prev, next);
272
- fingerprints.set(root, next);
273
- if (root === src) {
274
- batch.srcChanged = diff.changed;
275
- batch.srcDeleted = diff.deleted;
321
+ // Refresh residual dirty after settle (without discarding coalesced sets).
322
+ const residual = scanBatch();
323
+ {
324
+ const srcMerged = mergeDirtySets({ changed: batch.srcChanged, deleted: batch.srcDeleted }, { changed: residual.srcChanged, deleted: residual.srcDeleted });
325
+ batch.srcChanged = srcMerged.changed;
326
+ batch.srcDeleted = srcMerged.deleted;
327
+ const depMerged = mergeDirtySets({ changed: batch.depChanged, deleted: batch.depDeleted }, { changed: residual.depChanged, deleted: residual.depDeleted });
328
+ batch.depChanged = depMerged.changed;
329
+ batch.depDeleted = depMerged.deleted;
330
+ batch.docsDirty = batch.docsDirty || residual.docsDirty;
331
+ batch.localesDirty = batch.localesDirty || residual.localesDirty;
332
+ }
333
+ commitFingerprints();
334
+ if (!batch.srcChanged.length &&
335
+ !batch.srcDeleted.length &&
336
+ !batch.depChanged.length &&
337
+ !batch.depDeleted.length &&
338
+ !batch.docsDirty &&
339
+ !batch.localesDirty) {
340
+ continue;
341
+ }
342
+ // Dependency changes: conservative full rebuild + full reload (v0.1.5).
343
+ if (batch.depChanged.length || batch.depDeleted.length) {
344
+ log.info(`dependency change detected (${batch.depChanged.length} update, ${batch.depDeleted.length} delete) — full rebuild…`);
345
+ const report = rebuild();
346
+ if (!printReport(report, 'rebuild')) {
347
+ log.warn('rebuild failed — keeping previous server');
348
+ continue;
276
349
  }
277
- else if (root === localesRoot) {
278
- if (diff.changed.length || diff.deleted.length)
279
- batch.localesDirty = true;
350
+ // Refresh watch roots in case the graph gained new packages.
351
+ const refreshed = collectDevWatchRoots({ project, outDir });
352
+ for (const r of refreshed.roots) {
353
+ if (!watchRoots.includes(r)) {
354
+ watchRoots.push(r);
355
+ fingerprints.set(r, fileFingerprintMap(r));
356
+ }
280
357
  }
281
- else if (diff.changed.length || diff.deleted.length) {
282
- batch.docsDirty = true;
358
+ dependencyRoots = [...refreshed.dependencyRoots];
359
+ if (batch.docsDirty || projectHasDocuments(project)) {
360
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
361
+ if (!docs.ok)
362
+ log.warn('document mount rebuild failed — keeping previous docs');
283
363
  }
284
- }
285
- if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
364
+ if (wechatPreview) {
365
+ if (!packWechatPreview())
366
+ log.warn('wechat pack failed — keeping previous dist/wechat');
367
+ continue;
368
+ }
369
+ const kind = await reloadAfterBuild({
370
+ affectedChunks: report.affectedChunks ?? [],
371
+ seedChunks: report.seedChunks ?? [],
372
+ emitted: report.emitted ?? [],
373
+ full: true,
374
+ islandHmr: false,
375
+ });
376
+ log.info(kind === 'respawn' ? 'reload ok (respawned; deps)' : 'soft reload ok (full page; deps)');
286
377
  continue;
378
+ }
287
379
  let needFullReload = batch.docsDirty;
288
380
  if (batch.srcChanged.length || batch.srcDeleted.length) {
289
381
  log.info(`change detected (${batch.srcChanged.length} update, ${batch.srcDeleted.length} delete) — affected rebuild…`);
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Dev watch helpers: coalesce multi-file bursts without dropping dirty set,
3
+ * and derive extra watch roots from the compile graph (deployment unit sources).
4
+ */
5
+ /**
6
+ * @typedef {{ changed: string[], deleted: string[] }} DirtySet
7
+ */
8
+ /**
9
+ * Merge dirty sets: later change cancels delete and vice versa.
10
+ * @param {DirtySet} a
11
+ * @param {DirtySet} b
12
+ * @returns {DirtySet}
13
+ */
14
+ export declare function mergeDirtySets(a: any, b: any): {
15
+ changed: unknown[];
16
+ deleted: unknown[];
17
+ };
18
+ /**
19
+ * Wait until `root` stops changing; return the **accumulated** dirty set since `initial`.
20
+ * Updates `fingerprints` for `root` as it polls. Does **not** discard `initial`.
21
+ *
22
+ * @param {string} root
23
+ * @param {Map<string, Map<string, string>>} fingerprints
24
+ * @param {DirtySet} initial
25
+ * @param {{ sleep?: (ms: number) => Promise<void>, maxRounds?: number, settleMs?: number }} [opts]
26
+ * @returns {Promise<DirtySet>}
27
+ */
28
+ export declare function coalesceRootBurst(root: any, fingerprints: any, initial: any, opts?: {}): Promise<{
29
+ changed: any[];
30
+ deleted: any[];
31
+ }>;
32
+ /**
33
+ * Walk up from a file to find a package.json directory.
34
+ * @param {string} file
35
+ * @returns {string | null}
36
+ */
37
+ export declare function findPackageRoot(file: any): string;
38
+ /**
39
+ * Prefer package/src when present; otherwise the directory containing the source file.
40
+ * @param {string} sourceFile
41
+ * @returns {string | null}
42
+ */
43
+ export declare function watchRootForSourceFile(sourceFile: any): string;
44
+ /**
45
+ * Absolute roots for workspace / file / link deps that have a src tree.
46
+ * @param {string} project
47
+ * @returns {string[]}
48
+ */
49
+ export declare function localLinkDependencyRoots(project: any): any[];
50
+ /**
51
+ * Collect watch roots: project src/locales/documents + compile-graph external sources
52
+ * + local link/workspace package roots. Never adds a bare registry node_modules tree.
53
+ *
54
+ * @param {{ project: string, outDir: string }} opts
55
+ * @returns {{ roots: string[], dependencyRoots: string[], applicationRoots: string[] }}
56
+ */
57
+ export declare function collectDevWatchRoots(opts: any): {
58
+ roots: any[];
59
+ dependencyRoots: unknown[];
60
+ applicationRoots: any[];
61
+ };
62
+ /**
63
+ * Classify whether a changed file lives under a dependency watch root (not app src).
64
+ * @param {string} file
65
+ * @param {string[]} dependencyRoots
66
+ */
67
+ export declare function isDependencyPath(file: any, dependencyRoots: any): any;
@@ -0,0 +1,220 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Dev watch helpers: coalesce multi-file bursts without dropping dirty set,
4
+ * and derive extra watch roots from the compile graph (deployment unit sources).
5
+ */
6
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
9
+ /**
10
+ * @typedef {{ changed: string[], deleted: string[] }} DirtySet
11
+ */
12
+ /**
13
+ * Merge dirty sets: later change cancels delete and vice versa.
14
+ * @param {DirtySet} a
15
+ * @param {DirtySet} b
16
+ * @returns {DirtySet}
17
+ */
18
+ export function mergeDirtySets(a, b) {
19
+ const changed = new Set(a?.changed || []);
20
+ const deleted = new Set(a?.deleted || []);
21
+ for (const f of b?.changed || []) {
22
+ changed.add(f);
23
+ deleted.delete(f);
24
+ }
25
+ for (const f of b?.deleted || []) {
26
+ deleted.add(f);
27
+ changed.delete(f);
28
+ }
29
+ return { changed: [...changed], deleted: [...deleted] };
30
+ }
31
+ /**
32
+ * Wait until `root` stops changing; return the **accumulated** dirty set since `initial`.
33
+ * Updates `fingerprints` for `root` as it polls. Does **not** discard `initial`.
34
+ *
35
+ * @param {string} root
36
+ * @param {Map<string, Map<string, string>>} fingerprints
37
+ * @param {DirtySet} initial
38
+ * @param {{ sleep?: (ms: number) => Promise<void>, maxRounds?: number, settleMs?: number }} [opts]
39
+ * @returns {Promise<DirtySet>}
40
+ */
41
+ export async function coalesceRootBurst(root, fingerprints, initial, opts = {}) {
42
+ const sleepFn = opts.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
43
+ const maxRounds = opts.maxRounds ?? 20;
44
+ const settleMs = opts.settleMs ?? 220;
45
+ let accumulated = {
46
+ changed: [...(initial?.changed || [])],
47
+ deleted: [...(initial?.deleted || [])],
48
+ };
49
+ let guard = 0;
50
+ while (guard++ < maxRounds) {
51
+ await sleepFn(settleMs);
52
+ const prev = fingerprints.get(root) || new Map();
53
+ const next = fileFingerprintMap(root);
54
+ const diff = diffFingerprints(prev, next);
55
+ fingerprints.set(root, next);
56
+ if (!diff.changed.length && !diff.deleted.length)
57
+ break;
58
+ accumulated = mergeDirtySets(accumulated, diff);
59
+ }
60
+ return accumulated;
61
+ }
62
+ /**
63
+ * Walk up from a file to find a package.json directory.
64
+ * @param {string} file
65
+ * @returns {string | null}
66
+ */
67
+ export function findPackageRoot(file) {
68
+ let dir = path.dirname(path.resolve(file));
69
+ for (let i = 0; i < 24; i++) {
70
+ if (existsSync(path.join(dir, 'package.json')))
71
+ return dir;
72
+ const parent = path.dirname(dir);
73
+ if (parent === dir)
74
+ break;
75
+ dir = parent;
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * Prefer package/src when present; otherwise the directory containing the source file.
81
+ * @param {string} sourceFile
82
+ * @returns {string | null}
83
+ */
84
+ export function watchRootForSourceFile(sourceFile) {
85
+ const abs = path.resolve(sourceFile);
86
+ if (!existsSync(abs))
87
+ return null;
88
+ const pkg = findPackageRoot(abs);
89
+ if (pkg) {
90
+ const src = path.join(pkg, 'src');
91
+ if (existsSync(src))
92
+ return src;
93
+ return pkg;
94
+ }
95
+ return path.dirname(abs);
96
+ }
97
+ /**
98
+ * Absolute roots for workspace / file / link deps that have a src tree.
99
+ * @param {string} project
100
+ * @returns {string[]}
101
+ */
102
+ export function localLinkDependencyRoots(project) {
103
+ const pkgPath = path.join(path.resolve(project), 'package.json');
104
+ if (!existsSync(pkgPath))
105
+ return [];
106
+ /** @type {string[]} */
107
+ const roots = [];
108
+ try {
109
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
110
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
111
+ for (const [name, spec] of Object.entries(deps)) {
112
+ if (typeof spec !== 'string')
113
+ continue;
114
+ let target = null;
115
+ if (spec.startsWith('workspace:')) {
116
+ // Resolve via node_modules (pnpm links workspace packages there).
117
+ const nm = path.join(path.resolve(project), 'node_modules', ...name.split('/'));
118
+ if (existsSync(path.join(nm, 'package.json')))
119
+ target = nm;
120
+ }
121
+ else if (spec.startsWith('file:') || spec.startsWith('link:')) {
122
+ const rel = spec.replace(/^(file|link):/, '');
123
+ target = path.resolve(project, rel);
124
+ }
125
+ if (!target)
126
+ continue;
127
+ try {
128
+ target = realpathSync(target);
129
+ }
130
+ catch {
131
+ /* keep as-is */
132
+ }
133
+ const src = path.join(target, 'src');
134
+ if (existsSync(src))
135
+ roots.push(src);
136
+ else if (existsSync(target))
137
+ roots.push(target);
138
+ }
139
+ }
140
+ catch {
141
+ /* ignore */
142
+ }
143
+ return roots;
144
+ }
145
+ /**
146
+ * Collect watch roots: project src/locales/documents + compile-graph external sources
147
+ * + local link/workspace package roots. Never adds a bare registry node_modules tree.
148
+ *
149
+ * @param {{ project: string, outDir: string }} opts
150
+ * @returns {{ roots: string[], dependencyRoots: string[], applicationRoots: string[] }}
151
+ */
152
+ export function collectDevWatchRoots(opts) {
153
+ const project = path.resolve(opts.project);
154
+ const outDir = path.resolve(opts.outDir);
155
+ const src = path.join(project, 'src');
156
+ const docsRoot = path.join(project, 'documents');
157
+ const localesRoot = path.join(project, 'locales');
158
+ const designsRoot = path.join(project, 'designs');
159
+ /** @type {string[]} */
160
+ const applicationRoots = [];
161
+ if (existsSync(src))
162
+ applicationRoots.push(src);
163
+ if (existsSync(docsRoot))
164
+ applicationRoots.push(docsRoot);
165
+ if (existsSync(localesRoot))
166
+ applicationRoots.push(localesRoot);
167
+ if (existsSync(designsRoot))
168
+ applicationRoots.push(designsRoot);
169
+ /** @type {Set<string>} */
170
+ const depSet = new Set();
171
+ const depJson = path.join(outDir, 'vmz-deployment.json');
172
+ if (existsSync(depJson)) {
173
+ try {
174
+ const dep = JSON.parse(readFileSync(depJson, 'utf8'));
175
+ for (const unit of dep.units || []) {
176
+ const source = unit?.source;
177
+ if (typeof source !== 'string' || !source)
178
+ continue;
179
+ let abs = path.resolve(source);
180
+ try {
181
+ abs = realpathSync(abs);
182
+ }
183
+ catch {
184
+ /* keep */
185
+ }
186
+ const underProject = abs === project || abs.startsWith(project + path.sep) || abs.startsWith(project + '/');
187
+ if (underProject)
188
+ continue;
189
+ const root = watchRootForSourceFile(abs);
190
+ if (root)
191
+ depSet.add(path.resolve(root));
192
+ }
193
+ }
194
+ catch {
195
+ /* ignore corrupt deployment */
196
+ }
197
+ }
198
+ for (const r of localLinkDependencyRoots(project)) {
199
+ depSet.add(path.resolve(r));
200
+ }
201
+ // Drop dependency roots that are already under an application root.
202
+ const dependencyRoots = [...depSet].filter((r) => {
203
+ return !applicationRoots.some((app) => r === app || r.startsWith(app + path.sep) || r.startsWith(app + '/'));
204
+ });
205
+ const roots = [...applicationRoots];
206
+ for (const r of dependencyRoots) {
207
+ if (!roots.includes(r))
208
+ roots.push(r);
209
+ }
210
+ return { roots, dependencyRoots, applicationRoots };
211
+ }
212
+ /**
213
+ * Classify whether a changed file lives under a dependency watch root (not app src).
214
+ * @param {string} file
215
+ * @param {string[]} dependencyRoots
216
+ */
217
+ export function isDependencyPath(file, dependencyRoots) {
218
+ const abs = path.resolve(file);
219
+ return (dependencyRoots || []).some((r) => abs === r || abs.startsWith(r + path.sep) || abs.startsWith(r + '/'));
220
+ }
package/dist/index.d.ts CHANGED
@@ -147,6 +147,7 @@ export declare function checkApplicationHostCompositionJson(hostRoot: any, packa
147
147
  */
148
148
  export declare function checkApplicationDevTestDeployJson(hostRoot: any, packageRoots: any, dirtyPaths?: any[]): any;
149
149
  export { createDevSession, listWatchedFiles, srcFingerprint } from './dev-session.js';
150
+ export { coalesceRootBurst, collectDevWatchRoots, mergeDirtySets, localLinkDependencyRoots, watchRootForSourceFile, } from './dev-watch-roots.js';
150
151
  export { findAvailablePort } from './port.js';
151
152
  export { runCli, parseArgs, printHelp, printGlobalHelp, printProjectHelp } from './cli.js';
152
153
  export { findNearestProjectVmz, getInvocationContext, isGlobalAllowedCommand, isUnderNodeModules, resolveThisPackageRoot, resolveVmzBin, gateGlobalProjectCommand, } from './invocation.js';
package/dist/index.js CHANGED
@@ -369,6 +369,7 @@ export function checkApplicationDevTestDeployJson(hostRoot, packageRoots, dirtyP
369
369
  return native.checkApplicationDevTestDeployJson(hostRoot, packageRoots, dirtyPaths);
370
370
  }
371
371
  export { createDevSession, listWatchedFiles, srcFingerprint } from './dev-session.js';
372
+ export { coalesceRootBurst, collectDevWatchRoots, mergeDirtySets, localLinkDependencyRoots, watchRootForSourceFile, } from './dev-watch-roots.js';
372
373
  export { findAvailablePort } from './port.js';
373
374
  export { runCli, parseArgs, printHelp, printGlobalHelp, printProjectHelp } from './cli.js';
374
375
  export { findNearestProjectVmz, getInvocationContext, isGlobalAllowedCommand, isUnderNodeModules, resolveThisPackageRoot, resolveVmzBin, gateGlobalProjectCommand, } from './invocation.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vmz/vmz",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "VMZ Node toolchain — N-API workspace session + CLI (publish name @vmz/vmz)",
6
6
  "license": "MIT",
@@ -48,15 +48,15 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@vmz/core": "0.1.4",
52
- "@vmz/plugin": "0.1.4",
53
- "@vmz/protocol": "0.1.4",
51
+ "@vmz/core": "0.1.6",
52
+ "@vmz/plugin": "0.1.6",
53
+ "@vmz/protocol": "0.1.6",
54
54
  "jiti": "^2.6.1",
55
55
  "json5": "^2.2.3"
56
56
  },
57
57
  "peerDependencies": {
58
- "@vmz/plugin-markdown-it": "0.1.4",
59
- "@vmz/test": "0.1.4",
58
+ "@vmz/plugin-markdown-it": "0.1.6",
59
+ "@vmz/test": "0.1.6",
60
60
  "typescript": "^5.8.3"
61
61
  },
62
62
  "peerDependenciesMeta": {
@@ -90,12 +90,12 @@
90
90
  "cli"
91
91
  ],
92
92
  "optionalDependencies": {
93
- "@vmz/vmz-win32-x64": "0.1.4",
94
- "@vmz/vmz-win32-arm64": "0.1.4",
95
- "@vmz/vmz-darwin-x64": "0.1.4",
96
- "@vmz/vmz-darwin-arm64": "0.1.4",
97
- "@vmz/vmz-linux-x64": "0.1.4",
98
- "@vmz/vmz-linux-arm64": "0.1.4"
93
+ "@vmz/vmz-win32-x64": "0.1.6",
94
+ "@vmz/vmz-win32-arm64": "0.1.6",
95
+ "@vmz/vmz-darwin-x64": "0.1.6",
96
+ "@vmz/vmz-darwin-arm64": "0.1.6",
97
+ "@vmz/vmz-linux-x64": "0.1.6",
98
+ "@vmz/vmz-linux-arm64": "0.1.6"
99
99
  },
100
100
  "publishConfig": {
101
101
  "access": "public"