@indigoai-us/hq-cloud 5.25.0 → 5.27.0

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 (87) hide show
  1. package/.github/workflows/ci.yml +34 -0
  2. package/dist/bin/sync-runner.d.ts +138 -1
  3. package/dist/bin/sync-runner.d.ts.map +1 -1
  4. package/dist/bin/sync-runner.js +288 -16
  5. package/dist/bin/sync-runner.js.map +1 -1
  6. package/dist/bin/sync-runner.test.js +372 -1
  7. package/dist/bin/sync-runner.test.js.map +1 -1
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +6 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/sync/feature-flags.d.ts +136 -0
  13. package/dist/sync/feature-flags.d.ts.map +1 -0
  14. package/dist/sync/feature-flags.js +160 -0
  15. package/dist/sync/feature-flags.js.map +1 -0
  16. package/dist/sync/feature-flags.test.d.ts +24 -0
  17. package/dist/sync/feature-flags.test.d.ts.map +1 -0
  18. package/dist/sync/feature-flags.test.js +330 -0
  19. package/dist/sync/feature-flags.test.js.map +1 -0
  20. package/dist/sync/index.d.ts +19 -0
  21. package/dist/sync/index.d.ts.map +1 -0
  22. package/dist/sync/index.js +13 -0
  23. package/dist/sync/index.js.map +1 -0
  24. package/dist/sync/logger.d.ts +61 -0
  25. package/dist/sync/logger.d.ts.map +1 -0
  26. package/dist/sync/logger.js +51 -0
  27. package/dist/sync/logger.js.map +1 -0
  28. package/dist/sync/logger.test.d.ts +19 -0
  29. package/dist/sync/logger.test.d.ts.map +1 -0
  30. package/dist/sync/logger.test.js +199 -0
  31. package/dist/sync/logger.test.js.map +1 -0
  32. package/dist/sync/metrics.d.ts +89 -0
  33. package/dist/sync/metrics.d.ts.map +1 -0
  34. package/dist/sync/metrics.js +105 -0
  35. package/dist/sync/metrics.js.map +1 -0
  36. package/dist/sync/metrics.test.d.ts +19 -0
  37. package/dist/sync/metrics.test.d.ts.map +1 -0
  38. package/dist/sync/metrics.test.js +280 -0
  39. package/dist/sync/metrics.test.js.map +1 -0
  40. package/dist/sync/push-event.d.ts +110 -0
  41. package/dist/sync/push-event.d.ts.map +1 -0
  42. package/dist/sync/push-event.js +153 -0
  43. package/dist/sync/push-event.js.map +1 -0
  44. package/dist/sync/push-event.test.d.ts +15 -0
  45. package/dist/sync/push-event.test.d.ts.map +1 -0
  46. package/dist/sync/push-event.test.js +188 -0
  47. package/dist/sync/push-event.test.js.map +1 -0
  48. package/dist/sync/push-receiver.d.ts +442 -0
  49. package/dist/sync/push-receiver.d.ts.map +1 -0
  50. package/dist/sync/push-receiver.js +782 -0
  51. package/dist/sync/push-receiver.js.map +1 -0
  52. package/dist/sync/push-receiver.test.d.ts +25 -0
  53. package/dist/sync/push-receiver.test.d.ts.map +1 -0
  54. package/dist/sync/push-receiver.test.js +477 -0
  55. package/dist/sync/push-receiver.test.js.map +1 -0
  56. package/dist/sync/push-transport.d.ts +150 -0
  57. package/dist/sync/push-transport.d.ts.map +1 -0
  58. package/dist/sync/push-transport.js +150 -0
  59. package/dist/sync/push-transport.js.map +1 -0
  60. package/dist/watcher.d.ts +271 -0
  61. package/dist/watcher.d.ts.map +1 -1
  62. package/dist/watcher.js +480 -3
  63. package/dist/watcher.js.map +1 -1
  64. package/dist/watcher.test.d.ts +2 -0
  65. package/dist/watcher.test.d.ts.map +1 -0
  66. package/dist/watcher.test.js +334 -0
  67. package/dist/watcher.test.js.map +1 -0
  68. package/package.json +10 -5
  69. package/src/bin/sync-runner.test.ts +487 -1
  70. package/src/bin/sync-runner.ts +406 -9
  71. package/src/index.ts +38 -0
  72. package/src/sync/feature-flags.test.ts +392 -0
  73. package/src/sync/feature-flags.ts +229 -0
  74. package/src/sync/index.ts +74 -0
  75. package/src/sync/logger.test.ts +241 -0
  76. package/src/sync/logger.ts +79 -0
  77. package/src/sync/metrics.test.ts +380 -0
  78. package/src/sync/metrics.ts +158 -0
  79. package/src/sync/push-event.test.ts +224 -0
  80. package/src/sync/push-event.ts +208 -0
  81. package/src/sync/push-receiver.test.ts +545 -0
  82. package/src/sync/push-receiver.ts +1077 -0
  83. package/src/sync/push-transport.ts +231 -0
  84. package/src/watcher.test.ts +388 -0
  85. package/src/watcher.ts +672 -4
  86. package/test/e2e/sync/cross-tenant-isolation.test.ts +502 -0
  87. package/test/e2e/watcher-real-chokidar.test.ts +105 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * PushTransport — outbound shipping seam for the watcher daemon.
3
+ *
4
+ * The daemon wires file-watcher events into a transport that ships each
5
+ * PushEvent to the cloud. Defining the interface here lets the daemon ship
6
+ * with a swappable boundary — tests inject a fake, a later story swaps in a
7
+ * concrete WebSocket/HTTP implementation, and the daemon entry point never
8
+ * changes.
9
+ *
10
+ * Lifecycle
11
+ * ─────────
12
+ * - `start()` is awaited BEFORE the watcher is started. It's the place
13
+ * to open sockets, refresh tokens, etc.
14
+ * - `publish(event)` is called for every coalesced PushEvent the watcher
15
+ * emits. Implementations decide whether to buffer, batch, or send
16
+ * inline; the daemon awaits the returned promise so back-pressure can
17
+ * be honored.
18
+ * - `dispose()` is awaited DURING shutdown, AFTER the watcher has been
19
+ * torn down. Implementations should drain in-flight publishes (with
20
+ * their own internal timeout) and close any sockets.
21
+ * - `connected` is a passive boolean used by the health endpoint. It MAY
22
+ * flap during reconnect attempts — that's fine; consumers treat it as
23
+ * advisory, not a contract.
24
+ *
25
+ * The `NoopPushTransport` shipped here is the default when no transport
26
+ * is wired in: it counts publishes (so unit tests can assert delivery)
27
+ * and logs nothing — observers should rely on the watcher's `onEvent`
28
+ * counter on the daemon side, not the transport's internals.
29
+ *
30
+ * Ported from indigoai-us/hq-pro PR #112 (src/sync/push-transport.ts) into
31
+ * @indigoai-us/hq-cloud (Path B) per project event-driven-sync-menubar US-007.
32
+ */
33
+ import { type PushEvent } from "./push-event.js";
34
+ export interface PushTransport {
35
+ /** Open sockets, refresh tokens. Awaited before the watcher starts. */
36
+ start(): Promise<void>;
37
+ /** Ship one coalesced PushEvent. Awaited per event for back-pressure. */
38
+ publish(event: PushEvent): Promise<void>;
39
+ /** Drain + close. Awaited during daemon shutdown after watcher.dispose. */
40
+ dispose(): Promise<void>;
41
+ /** Advisory: is the transport currently believed to be connected? */
42
+ readonly connected: boolean;
43
+ }
44
+ /**
45
+ * Default `PushTransport` used until a real implementation lands.
46
+ *
47
+ * Behavior:
48
+ * - `start()` flips `connected` to true.
49
+ * - `publish()` increments a counter (visible via `publishedCount`).
50
+ * - `dispose()` flips `connected` back to false.
51
+ *
52
+ * Deliberately silent: when the daemon runs with this default, the
53
+ * watcher's per-event log line (emitted by the daemon itself, not the
54
+ * transport) is the only observability. That keeps the noop from drowning
55
+ * the log when high-rate writes hit a dev machine.
56
+ */
57
+ export declare class NoopPushTransport implements PushTransport {
58
+ private _connected;
59
+ private _count;
60
+ get connected(): boolean;
61
+ /** Test/observability hook: how many events have been published. */
62
+ get publishedCount(): number;
63
+ start(): Promise<void>;
64
+ publish(_event: PushEvent): Promise<void>;
65
+ dispose(): Promise<void>;
66
+ }
67
+ /**
68
+ * Minimal fetch surface so tests can inject a mocked `fetch` without pulling
69
+ * in DOM/undici types. Matches the subset of the global `fetch` we use.
70
+ */
71
+ export type FetchLike = (input: string, init?: {
72
+ method?: string;
73
+ headers?: Record<string, string>;
74
+ body?: string;
75
+ signal?: AbortSignal;
76
+ }) => Promise<{
77
+ ok: boolean;
78
+ status: number;
79
+ text(): Promise<string>;
80
+ }>;
81
+ /**
82
+ * Async getter for the current Cognito access token. Long-running daemons MUST
83
+ * pass a getter (not a captured string) so each publish resolves the freshest
84
+ * token — mirrors {@link VaultServiceConfig.authToken} semantics in types.ts.
85
+ * A static string is also accepted for short-lived tools and tests.
86
+ */
87
+ export type AuthTokenSource = string | (() => string | Promise<string>);
88
+ export interface HttpPushTransportOptions {
89
+ /**
90
+ * Vault API base URL (e.g. `https://vault-api.example.com`). The same
91
+ * `apiUrl` the runner already resolves for VaultClient. Trailing slashes
92
+ * are stripped. Required — config/env-driven, no hard-coded default.
93
+ */
94
+ apiUrl: string;
95
+ /**
96
+ * Endpoint path the PushEvent is POSTed to. Defaults to `/sync/push`
97
+ * (matches the deployed hq-pro endpoint from US-006). Override for testing
98
+ * or future endpoint moves.
99
+ */
100
+ pushPath?: string;
101
+ /** Cognito JWT — static string OR async getter. See {@link AuthTokenSource}. */
102
+ authToken: AuthTokenSource;
103
+ /**
104
+ * Optional extra headers (e.g. client identification) merged onto every
105
+ * request. Authorization + Content-Type are always set by the transport.
106
+ */
107
+ headers?: Record<string, string>;
108
+ /**
109
+ * Per-request timeout in milliseconds. Default 10_000. On timeout the
110
+ * publish rejects — the daemon treats a rejected publish as a transient
111
+ * miss (the cadence poll still covers it) and MUST NOT crash.
112
+ */
113
+ timeoutMs?: number;
114
+ /** Injectable fetch (tests). Defaults to the global `fetch`. */
115
+ fetchImpl?: FetchLike;
116
+ }
117
+ /**
118
+ * Real client `PushTransport` that POSTs encoded PushEvents to the deployed
119
+ * `/sync/push` endpoint, authenticating with the menubar's existing Cognito
120
+ * bearer token — the same auth path VaultClient uses (Authorization: Bearer
121
+ * <accessToken>, token resolved per-request via the supplied getter so it
122
+ * self-heals across refreshes).
123
+ *
124
+ * Failure posture
125
+ * ───────────────
126
+ * `publish()` rejects on a network error, a non-2xx response, or a timeout.
127
+ * The DAEMON is responsible for not letting that rejection crash it — the
128
+ * watcher's emit path catches publish errors and logs them; the periodic
129
+ * cadence poll remains the safety net that eventually ships the change. This
130
+ * transport never swallows errors itself, so callers retain full visibility.
131
+ *
132
+ * `connected` flips true on `start()` and false on `dispose()`. It is purely
133
+ * advisory (HTTP is connectionless); the health endpoint may read it but it
134
+ * carries no delivery guarantee.
135
+ */
136
+ export declare class HttpPushTransport implements PushTransport {
137
+ private readonly apiUrl;
138
+ private readonly pushPath;
139
+ private readonly getAuthToken;
140
+ private readonly extraHeaders;
141
+ private readonly timeoutMs;
142
+ private readonly fetchImpl;
143
+ private _connected;
144
+ constructor(opts: HttpPushTransportOptions);
145
+ get connected(): boolean;
146
+ start(): Promise<void>;
147
+ publish(event: PushEvent): Promise<void>;
148
+ dispose(): Promise<void>;
149
+ }
150
+ //# sourceMappingURL=push-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-transport.d.ts","sourceRoot":"","sources":["../../src/sync/push-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,yEAAyE;IACzE,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,2EAA2E;IAC3E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAK;IAEnB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,oEAAoE;IACpE,IAAI,cAAc,IAAI,MAAM,CAE3B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B;AAID;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IACL,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KACE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAExE,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,SAAS,EAAE,eAAe,CAAC;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwB;IACrD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,UAAU,CAAS;gBAEf,IAAI,EAAE,wBAAwB;IAmB1C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCxC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * PushTransport — outbound shipping seam for the watcher daemon.
3
+ *
4
+ * The daemon wires file-watcher events into a transport that ships each
5
+ * PushEvent to the cloud. Defining the interface here lets the daemon ship
6
+ * with a swappable boundary — tests inject a fake, a later story swaps in a
7
+ * concrete WebSocket/HTTP implementation, and the daemon entry point never
8
+ * changes.
9
+ *
10
+ * Lifecycle
11
+ * ─────────
12
+ * - `start()` is awaited BEFORE the watcher is started. It's the place
13
+ * to open sockets, refresh tokens, etc.
14
+ * - `publish(event)` is called for every coalesced PushEvent the watcher
15
+ * emits. Implementations decide whether to buffer, batch, or send
16
+ * inline; the daemon awaits the returned promise so back-pressure can
17
+ * be honored.
18
+ * - `dispose()` is awaited DURING shutdown, AFTER the watcher has been
19
+ * torn down. Implementations should drain in-flight publishes (with
20
+ * their own internal timeout) and close any sockets.
21
+ * - `connected` is a passive boolean used by the health endpoint. It MAY
22
+ * flap during reconnect attempts — that's fine; consumers treat it as
23
+ * advisory, not a contract.
24
+ *
25
+ * The `NoopPushTransport` shipped here is the default when no transport
26
+ * is wired in: it counts publishes (so unit tests can assert delivery)
27
+ * and logs nothing — observers should rely on the watcher's `onEvent`
28
+ * counter on the daemon side, not the transport's internals.
29
+ *
30
+ * Ported from indigoai-us/hq-pro PR #112 (src/sync/push-transport.ts) into
31
+ * @indigoai-us/hq-cloud (Path B) per project event-driven-sync-menubar US-007.
32
+ */
33
+ import { encodePushEvent } from "./push-event.js";
34
+ /**
35
+ * Default `PushTransport` used until a real implementation lands.
36
+ *
37
+ * Behavior:
38
+ * - `start()` flips `connected` to true.
39
+ * - `publish()` increments a counter (visible via `publishedCount`).
40
+ * - `dispose()` flips `connected` back to false.
41
+ *
42
+ * Deliberately silent: when the daemon runs with this default, the
43
+ * watcher's per-event log line (emitted by the daemon itself, not the
44
+ * transport) is the only observability. That keeps the noop from drowning
45
+ * the log when high-rate writes hit a dev machine.
46
+ */
47
+ export class NoopPushTransport {
48
+ _connected = false;
49
+ _count = 0;
50
+ get connected() {
51
+ return this._connected;
52
+ }
53
+ /** Test/observability hook: how many events have been published. */
54
+ get publishedCount() {
55
+ return this._count;
56
+ }
57
+ async start() {
58
+ this._connected = true;
59
+ }
60
+ async publish(_event) {
61
+ this._count += 1;
62
+ }
63
+ async dispose() {
64
+ this._connected = false;
65
+ }
66
+ }
67
+ /**
68
+ * Real client `PushTransport` that POSTs encoded PushEvents to the deployed
69
+ * `/sync/push` endpoint, authenticating with the menubar's existing Cognito
70
+ * bearer token — the same auth path VaultClient uses (Authorization: Bearer
71
+ * <accessToken>, token resolved per-request via the supplied getter so it
72
+ * self-heals across refreshes).
73
+ *
74
+ * Failure posture
75
+ * ───────────────
76
+ * `publish()` rejects on a network error, a non-2xx response, or a timeout.
77
+ * The DAEMON is responsible for not letting that rejection crash it — the
78
+ * watcher's emit path catches publish errors and logs them; the periodic
79
+ * cadence poll remains the safety net that eventually ships the change. This
80
+ * transport never swallows errors itself, so callers retain full visibility.
81
+ *
82
+ * `connected` flips true on `start()` and false on `dispose()`. It is purely
83
+ * advisory (HTTP is connectionless); the health endpoint may read it but it
84
+ * carries no delivery guarantee.
85
+ */
86
+ export class HttpPushTransport {
87
+ apiUrl;
88
+ pushPath;
89
+ getAuthToken;
90
+ extraHeaders;
91
+ timeoutMs;
92
+ fetchImpl;
93
+ _connected = false;
94
+ constructor(opts) {
95
+ if (!opts.apiUrl || opts.apiUrl.trim() === "") {
96
+ throw new Error("HttpPushTransport: apiUrl is required");
97
+ }
98
+ this.apiUrl = opts.apiUrl.replace(/\/+$/, "");
99
+ const path = opts.pushPath ?? "/sync/push";
100
+ this.pushPath = path.startsWith("/") ? path : `/${path}`;
101
+ const tok = opts.authToken;
102
+ // Normalize string|getter into a single async getter (mirrors VaultClient).
103
+ this.getAuthToken =
104
+ typeof tok === "function" ? async () => tok() : async () => tok;
105
+ this.extraHeaders = opts.headers ?? {};
106
+ this.timeoutMs = opts.timeoutMs ?? 10_000;
107
+ // Bind so a destructured global fetch keeps its receiver.
108
+ this.fetchImpl =
109
+ opts.fetchImpl ??
110
+ ((input, init) => globalThis.fetch(input, init));
111
+ }
112
+ get connected() {
113
+ return this._connected;
114
+ }
115
+ async start() {
116
+ this._connected = true;
117
+ }
118
+ async publish(event) {
119
+ // Validate-on-encode (US-007 contract) — a malformed event throws a typed
120
+ // PushEventDecodeError BEFORE we hit the network.
121
+ const body = encodePushEvent(event);
122
+ const token = await this.getAuthToken();
123
+ const controller = new AbortController();
124
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
125
+ try {
126
+ const res = await this.fetchImpl(`${this.apiUrl}${this.pushPath}`, {
127
+ method: "POST",
128
+ headers: {
129
+ ...this.extraHeaders,
130
+ Authorization: `Bearer ${token}`,
131
+ "Content-Type": "application/json",
132
+ Accept: "application/json",
133
+ },
134
+ body,
135
+ signal: controller.signal,
136
+ });
137
+ if (!res.ok) {
138
+ const text = await res.text().catch(() => "");
139
+ throw new Error(`HttpPushTransport: POST ${this.pushPath} failed (${res.status})${text ? `: ${text}` : ""}`);
140
+ }
141
+ }
142
+ finally {
143
+ clearTimeout(timer);
144
+ }
145
+ }
146
+ async dispose() {
147
+ this._connected = false;
148
+ }
149
+ }
150
+ //# sourceMappingURL=push-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-transport.js","sourceRoot":"","sources":["../../src/sync/push-transport.ts"],"names":[],"mappings":"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"}
package/dist/watcher.d.ts CHANGED
@@ -8,6 +8,92 @@
8
8
  */
9
9
  import type { EntityContext } from "./types.js";
10
10
  import type { UploadAuthor } from "./s3.js";
11
+ import type { PushTransport } from "./sync/push-transport.js";
12
+ import type { EventDrivenPushFlagProvider } from "./sync/feature-flags.js";
13
+ /**
14
+ * Injectable clock seam (US-001).
15
+ *
16
+ * Production code uses the host timers; tests inject a {@link FakeClock} so the
17
+ * debounce window can be advanced deterministically without real wall-clock
18
+ * sleeps. US-002 (real chokidar watcher) and US-003 (runner wiring) build on
19
+ * this same seam — keep the surface minimal and stable.
20
+ */
21
+ export interface Clock {
22
+ /** Schedule `fn` to run after `ms`. Returns an opaque handle. */
23
+ setTimeout(fn: () => void, ms: number): unknown;
24
+ /** Cancel a previously scheduled timeout. Safe to call with a stale handle. */
25
+ clearTimeout(handle: unknown): void;
26
+ /** Current epoch milliseconds. */
27
+ now(): number;
28
+ }
29
+ /** Real clock backed by host timers + Date.now. The production default. */
30
+ export declare const systemClock: Clock;
31
+ /**
32
+ * Deterministic clock for tests. Advance virtual time with {@link advance};
33
+ * any timers whose deadline has passed fire in scheduled order. No real timers
34
+ * are ever created, so a test using only this clock leaks nothing.
35
+ */
36
+ export declare class FakeClock implements Clock {
37
+ private current;
38
+ private nextId;
39
+ private timers;
40
+ now(): number;
41
+ setTimeout(fn: () => void, ms: number): unknown;
42
+ clearTimeout(handle: unknown): void;
43
+ /**
44
+ * Advance virtual time by `ms`, firing every timer whose deadline falls in
45
+ * the interval (in deadline order). Timers scheduled by a firing callback are
46
+ * honored within the same advance if their new deadline is still within the
47
+ * advanced window.
48
+ */
49
+ advance(ms: number): void;
50
+ /** Number of timers still pending — a leak check for tests. */
51
+ pendingTimerCount(): number;
52
+ }
53
+ /** A push pass to run when a debounced change settles. May be async. */
54
+ export type PushFn = () => void | Promise<void>;
55
+ export interface WatchPushDriverOptions {
56
+ /** Quiet window (ms) before a settled change triggers a push. */
57
+ debounceMs?: number;
58
+ /** Clock seam — defaults to {@link systemClock}; tests inject {@link FakeClock}. */
59
+ clock?: Clock;
60
+ /** Push pass to invoke when the window settles. */
61
+ push: PushFn;
62
+ }
63
+ /**
64
+ * The reusable debounce + coalesce + concurrency-guard core of event-driven
65
+ * push (US-001 seam).
66
+ *
67
+ * It is intentionally decoupled from chokidar and from S3: callers feed it
68
+ * synthetic or real change notifications via {@link notifyChange}, and it
69
+ * invokes the injected `push` fn at most once per quiet window. A push that is
70
+ * still in flight is never overlapped — a change arriving mid-push is collapsed
71
+ * and re-triggers a single follow-up pass after the next quiet window.
72
+ *
73
+ * US-002 wires a real chokidar watcher's events into {@link notifyChange};
74
+ * US-003 supplies the targeted-push `push` fn. Tests drive it directly with a
75
+ * {@link FakeClock} and a spy `push` fn (no real S3, no 10-minute sleep).
76
+ */
77
+ export declare class WatchPushDriver {
78
+ private readonly debounceMs;
79
+ private readonly clock;
80
+ private readonly push;
81
+ private timer;
82
+ private pushing;
83
+ private pendingWhilePushing;
84
+ private disposed;
85
+ constructor(opts: WatchPushDriverOptions);
86
+ /**
87
+ * Register a change. Resets the quiet window; the push fires `debounceMs`
88
+ * after the LAST change in a burst, coalescing the burst to one push.
89
+ */
90
+ notifyChange(): void;
91
+ private fire;
92
+ /** True while a push pass is executing. */
93
+ isPushing(): boolean;
94
+ /** Cancel any pending debounce timer; idempotent. Leaves no timers behind. */
95
+ dispose(): void;
96
+ }
11
97
  export declare class SyncWatcher {
12
98
  private watcher;
13
99
  private hqRoot;
@@ -23,4 +109,189 @@ export declare class SyncWatcher {
23
109
  private queueChange;
24
110
  private flush;
25
111
  }
112
+ /** Decision for a single path: emit a change for it, or ignore it. */
113
+ export type WatchPathFilter = (absolutePath: string, isDir?: boolean) => boolean;
114
+ /**
115
+ * Build the composite emit-decision predicate. Returns true when a change to
116
+ * `absolutePath` SHOULD wake the watcher (i.e. it survives every exclusion
117
+ * layer). Pure and chokidar-free so the matching logic is unit-testable
118
+ * directly.
119
+ *
120
+ * @param hqRoot sync root (== personal-vault root in personalMode).
121
+ * @param personalMode when true, also applies the personal-vault default
122
+ * exclusions and the excluded-top-level buckets.
123
+ */
124
+ export declare function createWatchPathFilter(hqRoot: string, personalMode?: boolean): WatchPathFilter;
125
+ export interface TreeWatcherOptions {
126
+ /** Sync root to watch (== personal-vault root in personalMode). */
127
+ hqRoot: string;
128
+ /** Quiet window (ms) before a settled burst emits one `changed` call. */
129
+ debounceMs?: number;
130
+ /** Apply personal-vault default + top-level exclusions. */
131
+ personalMode?: boolean;
132
+ /** Clock seam — defaults to {@link systemClock}; tests inject {@link FakeClock}. */
133
+ clock?: Clock;
134
+ /**
135
+ * Pre-built path filter override (test seam). When omitted, one is built
136
+ * from {@link createWatchPathFilter}.
137
+ */
138
+ pathFilter?: WatchPathFilter;
139
+ }
140
+ /**
141
+ * Chokidar-backed file watcher that emits a single debounced `changed` signal
142
+ * after a {@link debounceMs} quiet window, coalescing bursts. It honors the
143
+ * full exclusion stack via {@link createWatchPathFilter}, so excluded paths
144
+ * (`.env`, `output/`, `.git/`, `companies/` in personalMode, …) never emit.
145
+ *
146
+ * Lifecycle: {@link start} (idempotent), {@link stop}, {@link dispose}. Stop
147
+ * closes the chokidar watcher (releasing fds) and cancels any pending debounce
148
+ * timer. dispose() is stop() + permanent shutdown.
149
+ */
150
+ /**
151
+ * One settled change-burst, handed to {@link TreeWatcher} listeners. Carries
152
+ * the set of relative paths that changed during the quiet window so a listener
153
+ * (e.g. {@link PushEventEmitter}) can build one PushEvent per path. `paths` is
154
+ * absolute-path → relative-path; both are needed (relative for the wire shape,
155
+ * absolute for hashing/statting the file on disk).
156
+ */
157
+ export interface TreeChangeBatch {
158
+ /** Map of absolutePath → relativePath for every path in the settled burst. */
159
+ paths: Map<string, string>;
160
+ }
161
+ /**
162
+ * Listener invoked once per settled debounce window.
163
+ *
164
+ * Backwards compatible with the US-003 `WatcherSurface` contract: the first
165
+ * argument is the OPTIONAL changed relative path the loop routes its targeted
166
+ * push to (the first path of the burst; undefined when the window settled with
167
+ * no captured path). US-008's {@link PushEventEmitter} consumes the SECOND
168
+ * argument — the full {@link TreeChangeBatch} of every path in the burst — to
169
+ * build one PushEvent per path. Listeners are free to ignore either argument.
170
+ */
171
+ export type TreeChangeListener = (changedRelPath?: string, batch?: TreeChangeBatch) => void;
172
+ export declare class TreeWatcher {
173
+ private readonly hqRoot;
174
+ private readonly debounceMs;
175
+ private readonly clock;
176
+ private readonly shouldEmit;
177
+ private watcher;
178
+ private timer;
179
+ private listeners;
180
+ /** Paths accumulated for the current (in-flight) debounce window. */
181
+ private pending;
182
+ private disposed;
183
+ constructor(opts: TreeWatcherOptions);
184
+ /**
185
+ * Register a debounced-`changed` listener. Returns an unsubscribe fn.
186
+ *
187
+ * Listeners receive a {@link TreeChangeBatch} of the paths that changed in
188
+ * the settled window. Existing US-003 callers that only need the "something
189
+ * changed" signal can ignore the argument — the contract is backwards
190
+ * compatible (a zero-arg callback still type-checks).
191
+ */
192
+ onChange(listener: TreeChangeListener): () => void;
193
+ /**
194
+ * Begin watching. Idempotent — a second call while already running is a
195
+ * no-op (no second chokidar instance, no leaked fds).
196
+ */
197
+ start(): void;
198
+ /**
199
+ * Test/seam entry point: feed a raw filesystem path as if chokidar reported
200
+ * it. Applies the emit filter then arms the debounce. Real chokidar events
201
+ * route through here too.
202
+ */
203
+ handleEvent(absolutePath: string): void;
204
+ private arm;
205
+ private emit;
206
+ /** True while the chokidar watcher is active. */
207
+ isWatching(): boolean;
208
+ /** Number of pending debounce timers — a leak check for tests. */
209
+ pendingTimerCount(): number;
210
+ /**
211
+ * Stop watching: close the chokidar watcher (releasing fds) and cancel any
212
+ * pending debounce timer. Idempotent. The instance can be restarted with
213
+ * {@link start} unless {@link dispose} was called.
214
+ */
215
+ stop(): void;
216
+ /** Permanent shutdown: stop() + drop listeners; further events are no-ops. */
217
+ dispose(): void;
218
+ }
219
+ export interface PushEventEmitterOptions {
220
+ /** Tenant identifier stamped onto every PushEvent + checked against the flag. */
221
+ originTenantId: string;
222
+ /** Device identifier stamped onto every PushEvent. */
223
+ originDeviceId: string;
224
+ /** Transport that ships each PushEvent (US-007 NoopPushTransport / HttpPushTransport). */
225
+ transport: PushTransport;
226
+ /**
227
+ * Feature-flag seam. When `isEnabled(originTenantId)` is false the emitter is
228
+ * dormant: {@link attach} subscribes nothing and {@link emitForBatch} is a
229
+ * no-op. Defaults are NOT supplied here — the caller injects an
230
+ * EventDrivenPushFlagProvider so dormancy is explicit.
231
+ */
232
+ flagProvider: EventDrivenPushFlagProvider;
233
+ /**
234
+ * Returns the next monotonic sequence number for this device. Default: an
235
+ * internal counter starting at 0. Inject to persist across daemon restarts.
236
+ */
237
+ getSequenceNumber?: () => number;
238
+ /** Clock for eventTimestamp. Default `() => new Date()`. */
239
+ now?: () => Date;
240
+ /**
241
+ * Where publish failures + hash/stat errors go. Default `console.error`.
242
+ * Receives the offending PushEvent (when known) so callers can correlate.
243
+ */
244
+ onError?: (err: Error, ctx: {
245
+ relativePath?: string;
246
+ }) => void;
247
+ /**
248
+ * Optional structured logger for the US-011 3-log diagnostic chain. When
249
+ * supplied, the emitter logs `event=watcher.emit` (the 1st correlated link)
250
+ * carrying the PushEvent's `sequenceNumber` — the same join key stamped by
251
+ * the server `push.receive` log and the client `fanout.receive` log. Default:
252
+ * no log (the daemon stays quiet unless wired with a logger).
253
+ */
254
+ logger?: EmitterLogger;
255
+ }
256
+ /**
257
+ * Minimal structured logger surface for {@link PushEventEmitter}. A pino
258
+ * `Logger` (from `./sync/logger.ts`) satisfies this; tests inject a fake.
259
+ */
260
+ export interface EmitterLogger {
261
+ info(obj: Record<string, unknown>, msg?: string): void;
262
+ }
263
+ /**
264
+ * Bridges {@link TreeWatcher} change batches to a {@link PushTransport} as
265
+ * typed PushEvents. Construct once per daemon, then {@link attach} to a
266
+ * running TreeWatcher (returns an unsubscribe fn). Flag-gated + failure-safe.
267
+ */
268
+ export declare class PushEventEmitter {
269
+ private readonly originTenantId;
270
+ private readonly originDeviceId;
271
+ private readonly transport;
272
+ private readonly flagProvider;
273
+ private readonly now;
274
+ private readonly onError;
275
+ private readonly logger;
276
+ private internalSeq;
277
+ private readonly nextSeq;
278
+ constructor(opts: PushEventEmitterOptions);
279
+ /** True iff event-driven push is enabled for this emitter's tenant. */
280
+ get enabled(): boolean;
281
+ /**
282
+ * Subscribe to a TreeWatcher's change batches. Dormant (no subscription)
283
+ * when the flag is OFF for this tenant. Returns an unsubscribe fn (a no-op
284
+ * when dormant).
285
+ */
286
+ attach(watcher: TreeWatcher): () => void;
287
+ /**
288
+ * Build + ship one PushEvent per changed path in the batch. No-op when the
289
+ * flag is OFF. Each path is independent: a hash/stat failure or a transport
290
+ * publish rejection for one path is caught + surfaced via `onError` and does
291
+ * NOT abort the others or propagate (the daemon must not crash; the cadence
292
+ * poll covers any miss).
293
+ */
294
+ emitForBatch(batch: TreeChangeBatch): Promise<void>;
295
+ private emitOne;
296
+ }
26
297
  //# sourceMappingURL=watcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAU5C,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,CAAe;IAC9B,OAAO,CAAC,UAAU,CAAiD;IACnE,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,YAAY;IAOrE,KAAK,IAAI,IAAI;IAuBb,IAAI,IAAI,IAAI;IAWZ,OAAO,CAAC,WAAW;YAqBL,KAAK;CAgDpB"}
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"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"}