@mulmoclaude/core 0.2.7 → 0.2.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.
Files changed (106) hide show
  1. package/assets/skills-preset/mc-manage-automations/SKILL.md +1 -1
  2. package/dist/feeds/server/index.d.ts +1 -0
  3. package/dist/feeds/server/index.js +37 -4
  4. package/dist/feeds/server/index.js.map +1 -1
  5. package/dist/feeds/server/scheduledRefresh.d.ts +21 -0
  6. package/package.json +1 -1
  7. package/dist/collection/core/actionVisible.d.ts +0 -34
  8. package/dist/collection/core/calendarGrid.d.ts +0 -120
  9. package/dist/collection/core/deriveAll.d.ts +0 -38
  10. package/dist/collection/core/derivedFormula.d.ts +0 -18
  11. package/dist/collection/core/draft.d.ts +0 -18
  12. package/dist/collection/core/enumColors.d.ts +0 -33
  13. package/dist/collection/core/errorMessage.d.ts +0 -4
  14. package/dist/collection/core/itemLabel.d.ts +0 -12
  15. package/dist/collection/core/presentCollection.d.ts +0 -13
  16. package/dist/collection/core/promptSafety.d.ts +0 -1
  17. package/dist/collection/core/schema.d.ts +0 -382
  18. package/dist/collection/core/shortHexId.d.ts +0 -8
  19. package/dist/collection/core/sortItems.d.ts +0 -29
  20. package/dist/collection/core/uiTypes.d.ts +0 -106
  21. package/dist/collection/index.cjs +0 -794
  22. package/dist/collection/index.cjs.map +0 -1
  23. package/dist/collection/index.d.ts +0 -14
  24. package/dist/collection/paths.cjs +0 -44
  25. package/dist/collection/paths.cjs.map +0 -1
  26. package/dist/collection/server/atomic.d.ts +0 -1
  27. package/dist/collection/server/delete.d.ts +0 -38
  28. package/dist/collection/server/derive.d.ts +0 -8
  29. package/dist/collection/server/discoveredCollection.d.ts +0 -18
  30. package/dist/collection/server/discovery.d.ts +0 -239
  31. package/dist/collection/server/host.d.ts +0 -77
  32. package/dist/collection/server/index.cjs +0 -51
  33. package/dist/collection/server/io.d.ts +0 -120
  34. package/dist/collection/server/paths.d.ts +0 -52
  35. package/dist/collection/server/spawn.d.ts +0 -55
  36. package/dist/collection/server/util.d.ts +0 -3
  37. package/dist/collection/server/validate.d.ts +0 -19
  38. package/dist/collection/server/views.d.ts +0 -20
  39. package/dist/collection-watchers/config.d.ts +0 -49
  40. package/dist/collection-watchers/index.cjs +0 -553
  41. package/dist/collection-watchers/index.cjs.map +0 -1
  42. package/dist/collection-watchers/index.d.ts +0 -3
  43. package/dist/collection-watchers/reconciler.d.ts +0 -33
  44. package/dist/collection-watchers/watcher.d.ts +0 -34
  45. package/dist/deriveAll-VRWrs3SF.cjs +0 -413
  46. package/dist/deriveAll-VRWrs3SF.cjs.map +0 -1
  47. package/dist/feeds/index.cjs +0 -8
  48. package/dist/feeds/index.d.ts +0 -1
  49. package/dist/feeds/ingestTypes.d.ts +0 -57
  50. package/dist/feeds/paths.cjs +0 -48
  51. package/dist/feeds/paths.cjs.map +0 -1
  52. package/dist/feeds/paths.d.ts +0 -22
  53. package/dist/feeds/server/fetch/httpClient.d.ts +0 -8
  54. package/dist/feeds/server/fetch/rssParser.d.ts +0 -12
  55. package/dist/feeds/server/index.cjs +0 -804
  56. package/dist/feeds/server/index.cjs.map +0 -1
  57. package/dist/feeds/server/pathResolver.d.ts +0 -6
  58. package/dist/feeds/server/projectItem.d.ts +0 -8
  59. package/dist/feeds/server/refreshResult.d.ts +0 -17
  60. package/dist/feeds/server/retrievers/httpJson.d.ts +0 -3
  61. package/dist/feeds/server/retrievers/index.d.ts +0 -12
  62. package/dist/feeds/server/retrievers/registerAll.d.ts +0 -0
  63. package/dist/feeds/server/retrievers/rss.d.ts +0 -3
  64. package/dist/file-change/index.cjs +0 -72
  65. package/dist/file-change/index.cjs.map +0 -1
  66. package/dist/file-change/index.d.ts +0 -43
  67. package/dist/ingestTypes-DDOVhOYC.cjs +0 -32
  68. package/dist/ingestTypes-DDOVhOYC.cjs.map +0 -1
  69. package/dist/notifier/engine.d.ts +0 -72
  70. package/dist/notifier/index.cjs +0 -21
  71. package/dist/notifier/index.d.ts +0 -3
  72. package/dist/notifier/store.d.ts +0 -18
  73. package/dist/notifier/types.d.ts +0 -118
  74. package/dist/notifier/validate.d.ts +0 -17
  75. package/dist/notifier-bS8IEeLA.cjs +0 -577
  76. package/dist/notifier-bS8IEeLA.cjs.map +0 -1
  77. package/dist/rolldown-runtime-D6vf50IK.cjs +0 -28
  78. package/dist/scheduler/adapter.d.ts +0 -48
  79. package/dist/scheduler/index.cjs +0 -352
  80. package/dist/scheduler/index.cjs.map +0 -1
  81. package/dist/scheduler/index.d.ts +0 -2
  82. package/dist/scheduler/task-manager.d.ts +0 -51
  83. package/dist/server-Db-x7OXz.cjs +0 -1993
  84. package/dist/server-Db-x7OXz.cjs.map +0 -1
  85. package/dist/skill-bridge/index.cjs +0 -88
  86. package/dist/skill-bridge/index.cjs.map +0 -1
  87. package/dist/skill-bridge/index.d.ts +0 -30
  88. package/dist/whisper/client.cjs +0 -241
  89. package/dist/whisper/client.cjs.map +0 -1
  90. package/dist/whisper/client.d.ts +0 -35
  91. package/dist/whisper/ffmpeg.d.ts +0 -6
  92. package/dist/whisper/index.cjs +0 -433
  93. package/dist/whisper/index.cjs.map +0 -1
  94. package/dist/whisper/index.d.ts +0 -5
  95. package/dist/whisper/internal.d.ts +0 -11
  96. package/dist/whisper/models.d.ts +0 -49
  97. package/dist/whisper/sidecar.d.ts +0 -8
  98. package/dist/whisper/whisper.d.ts +0 -28
  99. package/dist/workspace-setup/assets.d.ts +0 -10
  100. package/dist/workspace-setup/index.d.ts +0 -3
  101. package/dist/workspace-setup/index.js +0 -560
  102. package/dist/workspace-setup/index.js.map +0 -1
  103. package/dist/workspace-setup/slug.d.ts +0 -6
  104. package/dist/workspace-setup/slug.js +0 -13
  105. package/dist/workspace-setup/slug.js.map +0 -1
  106. package/dist/workspace-setup/sync.d.ts +0 -94
@@ -1,118 +0,0 @@
1
- /** Two notification shapes, distinguished by who fires the close call:
2
- *
3
- * `fyi` — informational. The host (bell panel) clears it when the
4
- * user dismisses the row. No deep-link target.
5
- * `action` — pending obligation. The plugin clears it when the
6
- * underlying domain state changes (the user paid the tax,
7
- * viewed the digest, etc.). The bell row navigates to
8
- * `navigateTarget` on click.
9
- *
10
- * The engine reads `lifecycle` only to enforce two publish-time rules
11
- * (everything downstream — pubsub fan-out, persistence, history — is
12
- * lifecycle-blind):
13
- *
14
- * 1. `action` requires a non-empty `navigateTarget`. Without one,
15
- * clicking the row does nothing and the entry is a degraded fyi.
16
- * 2. `action` cannot use `info` severity. A low-priority obligation
17
- * is incoherent — fyi if it's a ping, `nudge`/`urgent` if it's a
18
- * real obligation worth a landing page.
19
- *
20
- * Both rules are mirrored in the HTTP layer so plugin-runtime callers
21
- * and HTTP callers hit the same wall. */
22
- export declare const NOTIFIER_LIFECYCLES: readonly ["fyi", "action"];
23
- export type NotifierLifecycle = (typeof NOTIFIER_LIFECYCLES)[number];
24
- /** Severity drives badge color (gray / amber / red, worst-wins) and
25
- * in a future iteration channel routing. Mostly stored verbatim by
26
- * the engine; the one engine-visible interaction is the rule that
27
- * `action` lifecycle cannot pair with `info` severity (see
28
- * `NotifierLifecycle` above). */
29
- export declare const NOTIFIER_SEVERITIES: readonly ["info", "nudge", "urgent"];
30
- export type NotifierSeverity = (typeof NOTIFIER_SEVERITIES)[number];
31
- export interface NotifierEntry<TPluginData = unknown> {
32
- /** Engine-assigned UUID. Generated synchronously inside `publish()`
33
- * so the caller can use it before persistence completes. */
34
- id: string;
35
- /** Plugin namespace (e.g. `"encore"`, `"debug__system"`). The
36
- * engine never inspects it — used only for `listFor()` filtering
37
- * and as a UI grouping key. */
38
- pluginPkg: string;
39
- severity: NotifierSeverity;
40
- lifecycle?: NotifierLifecycle;
41
- title: string;
42
- body?: string;
43
- /** Optional in-app deep-link target (relative URL). The bell popup
44
- * routes here on row click, with `&notificationId=<id>` appended
45
- * so the landing page can identify which entry to clear. The
46
- * engine doesn't read this — it's a UI hint stored on the entry. */
47
- navigateTarget?: string;
48
- /** Opaque to the engine. Round-trips through JSON unchanged; only
49
- * the originating plugin's UI knows the shape. */
50
- pluginData?: TPluginData;
51
- /** ISO-8601 timestamp set at `publish()` time. */
52
- createdAt: string;
53
- }
54
- /** A history entry — a `NotifierEntry` after it has been cleared or
55
- * cancelled, with the terminal type and timestamp recorded. The
56
- * bell popup's "History" section renders these read-only. */
57
- export interface NotifierHistoryEntry<TPluginData = unknown> extends NotifierEntry<TPluginData> {
58
- terminalType: "cleared" | "cancelled";
59
- terminalAt: string;
60
- }
61
- /** Caller-supplied input for `publish()`. The engine fills in `id`
62
- * and `createdAt`; everything else flows through verbatim.
63
- *
64
- * Two publish-time rules apply to `action` lifecycle (see
65
- * `NotifierLifecycle`):
66
- *
67
- * - `navigateTarget` MUST be a non-empty string.
68
- * - `severity` MUST NOT be `"info"`.
69
- *
70
- * Violations cause `publish()` to throw. Currently expressed as
71
- * runtime validation rather than a discriminated-union type, so the
72
- * fields below are all individually optional / loose at the
73
- * type-level. */
74
- export interface PublishInput<TPluginData = unknown> {
75
- pluginPkg: string;
76
- severity: NotifierSeverity;
77
- title: string;
78
- body?: string;
79
- lifecycle?: NotifierLifecycle;
80
- navigateTarget?: string;
81
- pluginData?: TPluginData;
82
- }
83
- /** On-disk shape of `~/mulmoclaude/data/notifier/active.json`. Holds
84
- * only entries that haven't been cleared or cancelled — the file is
85
- * a snapshot, not an event log. */
86
- export interface NotifierFile {
87
- entries: Record<string, NotifierEntry>;
88
- }
89
- /** On-disk shape of `~/mulmoclaude/data/notifier/history.json`. Array
90
- * of terminated entries newest-first, capped at `HISTORY_CAP` with
91
- * FIFO eviction (push at index 0, slice from the tail). */
92
- export interface NotifierHistoryFile {
93
- entries: NotifierHistoryEntry[];
94
- }
95
- /** History size cap. The bell popup's History section renders this
96
- * many entries; older ones fall off when new terminations land. */
97
- export declare const HISTORY_CAP = 50;
98
- /** Pub-sub event published on the host's notifier channel after every
99
- * successful state change. Discriminated union — subscribers switch
100
- * on `type` to keep TypeScript narrowing the rest of the payload.
101
- *
102
- * `updated` carries the post-mutation entry — the receiver swaps
103
- * the matching `id` in their local active set. Reserved for in-
104
- * place edits via `updateForPlugin`; no history record is written
105
- * because the entry is still active, just with refreshed content. */
106
- export type NotifierEvent = {
107
- type: "published";
108
- entry: NotifierEntry;
109
- } | {
110
- type: "cleared";
111
- id: string;
112
- } | {
113
- type: "cancelled";
114
- id: string;
115
- } | {
116
- type: "updated";
117
- entry: NotifierEntry;
118
- };
@@ -1,17 +0,0 @@
1
- import { PublishInput } from './types.js';
2
- /** Hard caps on publish-input fields. The engine reads each entry on
3
- * every list/get call (no in-memory cache), so unbounded fields hurt
4
- * every reader. Caps chosen to be generous for legitimate UX copy
5
- * while bounding active.json growth: a notification fundamentally is
6
- * a short blurb, not a document. */
7
- export declare const NOTIFIER_LIMITS: {
8
- readonly titleMax: 200;
9
- readonly bodyMax: 4000;
10
- readonly navigateTargetMax: 1000;
11
- readonly pluginDataMaxBytes: number;
12
- };
13
- /** Validate a `PublishInput`. Returns `null` if OK, or a
14
- * human-readable error string. Order matters — shape/size errors are
15
- * reported before lifecycle/severity coherence errors so the message
16
- * the caller sees points at the most fundamental problem first. */
17
- export declare function validatePublishInput(input: PublishInput): string | null;
@@ -1,577 +0,0 @@
1
- let node_fs = require("node:fs");
2
- let node_crypto = require("node:crypto");
3
- //#region src/notifier/types.ts
4
- /** Two notification shapes, distinguished by who fires the close call:
5
- *
6
- * `fyi` — informational. The host (bell panel) clears it when the
7
- * user dismisses the row. No deep-link target.
8
- * `action` — pending obligation. The plugin clears it when the
9
- * underlying domain state changes (the user paid the tax,
10
- * viewed the digest, etc.). The bell row navigates to
11
- * `navigateTarget` on click.
12
- *
13
- * The engine reads `lifecycle` only to enforce two publish-time rules
14
- * (everything downstream — pubsub fan-out, persistence, history — is
15
- * lifecycle-blind):
16
- *
17
- * 1. `action` requires a non-empty `navigateTarget`. Without one,
18
- * clicking the row does nothing and the entry is a degraded fyi.
19
- * 2. `action` cannot use `info` severity. A low-priority obligation
20
- * is incoherent — fyi if it's a ping, `nudge`/`urgent` if it's a
21
- * real obligation worth a landing page.
22
- *
23
- * Both rules are mirrored in the HTTP layer so plugin-runtime callers
24
- * and HTTP callers hit the same wall. */
25
- var NOTIFIER_LIFECYCLES = ["fyi", "action"];
26
- /** Severity drives badge color (gray / amber / red, worst-wins) and
27
- * in a future iteration channel routing. Mostly stored verbatim by
28
- * the engine; the one engine-visible interaction is the rule that
29
- * `action` lifecycle cannot pair with `info` severity (see
30
- * `NotifierLifecycle` above). */
31
- var NOTIFIER_SEVERITIES = [
32
- "info",
33
- "nudge",
34
- "urgent"
35
- ];
36
- /** History size cap. The bell popup's History section renders this
37
- * many entries; older ones fall off when new terminations land. */
38
- var HISTORY_CAP = 50;
39
- //#endregion
40
- //#region src/notifier/store.ts
41
- function isNotFoundError(err) {
42
- return typeof err === "object" && err !== null && err.code === "ENOENT";
43
- }
44
- /** Read the active-entries file. Returns an empty store when the file
45
- * doesn't exist yet (first ever call on a fresh workspace). Any other
46
- * read or parse failure throws — the caller has to decide whether to
47
- * surface or recover, since silently treating "malformed file" as
48
- * "no entries" would lose data. */
49
- async function loadActive(filePath) {
50
- let text;
51
- try {
52
- text = await node_fs.promises.readFile(filePath, "utf-8");
53
- } catch (err) {
54
- if (isNotFoundError(err)) return { entries: {} };
55
- throw err;
56
- }
57
- const parsed = JSON.parse(text);
58
- if (typeof parsed !== "object" || parsed === null || !("entries" in parsed)) throw new Error(`notifier: malformed active.json at ${filePath}`);
59
- const { entries } = parsed;
60
- if (typeof entries !== "object" || entries === null || Array.isArray(entries)) throw new Error(`notifier: malformed active.json at ${filePath}`);
61
- return parsed;
62
- }
63
- /** Write the active-entries file via the injected atomic writer so a
64
- * half-written file is never visible to readers. The caller serialises
65
- * writes (engine.ts queues mutations) — this function makes no
66
- * concurrency guarantees of its own. */
67
- async function saveActive(writeJson, filePath, state) {
68
- await writeJson(filePath, state);
69
- }
70
- /** Read the history file. Empty array on first run. Same parse-error
71
- * policy as `loadActive`. */
72
- async function loadHistory(filePath) {
73
- let text;
74
- try {
75
- text = await node_fs.promises.readFile(filePath, "utf-8");
76
- } catch (err) {
77
- if (isNotFoundError(err)) return { entries: [] };
78
- throw err;
79
- }
80
- const parsed = JSON.parse(text);
81
- if (typeof parsed !== "object" || parsed === null || !("entries" in parsed) || !Array.isArray(parsed.entries)) throw new Error(`notifier: malformed history.json at ${filePath}`);
82
- return parsed;
83
- }
84
- async function saveHistory(writeJson, filePath, state) {
85
- await writeJson(filePath, state);
86
- }
87
- //#endregion
88
- //#region src/notifier/validate.ts
89
- /** Hard caps on publish-input fields. The engine reads each entry on
90
- * every list/get call (no in-memory cache), so unbounded fields hurt
91
- * every reader. Caps chosen to be generous for legitimate UX copy
92
- * while bounding active.json growth: a notification fundamentally is
93
- * a short blurb, not a document. */
94
- var NOTIFIER_LIMITS = {
95
- titleMax: 200,
96
- bodyMax: 4e3,
97
- navigateTargetMax: 1e3,
98
- pluginDataMaxBytes: 16 * 1024
99
- };
100
- function validateTitle(title) {
101
- if (typeof title !== "string" || title.length === 0) return "title must be a non-empty string";
102
- if (title.length > NOTIFIER_LIMITS.titleMax) return `title exceeds max length of ${NOTIFIER_LIMITS.titleMax} chars`;
103
- return null;
104
- }
105
- function validateBody(body) {
106
- if (body === void 0) return null;
107
- if (body.length > NOTIFIER_LIMITS.bodyMax) return `body exceeds max length of ${NOTIFIER_LIMITS.bodyMax} chars`;
108
- return null;
109
- }
110
- function validateNavigateTarget(target) {
111
- if (target === void 0) return null;
112
- if (target.length === 0) return "navigateTarget must be a non-empty relative path when set";
113
- if (target.length > NOTIFIER_LIMITS.navigateTargetMax) return `navigateTarget exceeds max length of ${NOTIFIER_LIMITS.navigateTargetMax} chars`;
114
- if (!target.startsWith("/") || target.startsWith("//")) return "navigateTarget must be a relative path beginning with a single '/' (no scheme, no '//')";
115
- return null;
116
- }
117
- function validatePluginData(pluginData) {
118
- if (pluginData === void 0) return null;
119
- let serialized;
120
- try {
121
- serialized = JSON.stringify(pluginData);
122
- } catch (err) {
123
- return `pluginData is not JSON-serialisable: ${String(err)}`;
124
- }
125
- if (typeof serialized !== "string") return "pluginData is not JSON-serialisable";
126
- if (serialized.length > NOTIFIER_LIMITS.pluginDataMaxBytes) return `pluginData JSON exceeds ${NOTIFIER_LIMITS.pluginDataMaxBytes} bytes`;
127
- return null;
128
- }
129
- function validateActionCoherence(input) {
130
- if (input.lifecycle !== "action") return null;
131
- if (input.severity === "info") return "action lifecycle is incompatible with info severity (use fyi for low-priority pings)";
132
- if (typeof input.navigateTarget !== "string" || input.navigateTarget.length === 0) return "action lifecycle requires a non-empty navigateTarget";
133
- return null;
134
- }
135
- /** Validate a `PublishInput`. Returns `null` if OK, or a
136
- * human-readable error string. Order matters — shape/size errors are
137
- * reported before lifecycle/severity coherence errors so the message
138
- * the caller sees points at the most fundamental problem first. */
139
- function validatePublishInput(input) {
140
- return validateTitle(input.title) ?? validateBody(input.body) ?? validateNavigateTarget(input.navigateTarget) ?? validatePluginData(input.pluginData) ?? validateActionCoherence(input);
141
- }
142
- //#endregion
143
- //#region src/notifier/engine.ts
144
- var NOOP_LOG = {
145
- warn: () => {},
146
- error: () => {}
147
- };
148
- var config = null;
149
- var activeFilePath = "";
150
- var historyFilePath = "";
151
- function logger() {
152
- return config?.log ?? NOOP_LOG;
153
- }
154
- /** Wire the engine's I/O deps. Call once at startup, before the first
155
- * mutation. Does NOT set file paths — those are set independently via
156
- * `setNotifierFilePaths` so a host can bind production paths at module
157
- * load and a test can override them without re-supplying the deps. */
158
- function configureNotifier(injected) {
159
- config = injected;
160
- }
161
- var listeners = [];
162
- /** Register an in-process listener for engine events. Returns an
163
- * unsubscribe function the caller can use during teardown. */
164
- function onEvent(listener) {
165
- listeners.push(listener);
166
- return () => {
167
- const idx = listeners.indexOf(listener);
168
- if (idx >= 0) listeners.splice(idx, 1);
169
- };
170
- }
171
- function emit(event) {
172
- for (const listener of listeners) try {
173
- listener(event);
174
- } catch (err) {
175
- logger().error("in-process listener failed", {
176
- type: event.type,
177
- error: String(err)
178
- });
179
- }
180
- if (!config) {
181
- logger().warn("emit before init", { type: event.type });
182
- return;
183
- }
184
- try {
185
- config.publishEvent(event);
186
- } catch (err) {
187
- logger().error("emit failed", {
188
- type: event.type,
189
- error: String(err)
190
- });
191
- }
192
- }
193
- var writing = false;
194
- var waiters = [];
195
- /** Point the engine at its active/history files. Resets the write
196
- * queue, so callers must not have in-flight mutations. The host calls
197
- * this once with the workspace paths; tests call it per-case with temp
198
- * files. */
199
- function setNotifierFilePaths(paths) {
200
- activeFilePath = paths.active;
201
- historyFilePath = paths.history;
202
- writing = false;
203
- waiters = [];
204
- }
205
- /** Test-only: clear config + queue so each suite starts clean. */
206
- function resetNotifier() {
207
- config = null;
208
- activeFilePath = "";
209
- historyFilePath = "";
210
- writing = false;
211
- waiters = [];
212
- listeners.length = 0;
213
- }
214
- function requireWriteJson() {
215
- if (!config) throw new Error("notifier: configureNotifier() not called");
216
- return config.writeJson;
217
- }
218
- function applyBatchMutations(batch, state) {
219
- return batch.map((waiter) => {
220
- try {
221
- return {
222
- ok: true,
223
- outcome: waiter.mutate(state)
224
- };
225
- } catch (err) {
226
- return {
227
- ok: false,
228
- error: err
229
- };
230
- }
231
- });
232
- }
233
- function collectEvents(results) {
234
- const events = [];
235
- for (const result of results) if (result.ok && result.outcome !== null) events.push(result.outcome.event);
236
- return events;
237
- }
238
- function collectHistoryEntries(results) {
239
- const entries = [];
240
- for (const result of results) if (result.ok && result.outcome !== null && result.outcome.historyEntry) entries.push(result.outcome.historyEntry);
241
- return entries;
242
- }
243
- function settleBatch(batch, results) {
244
- for (let index = 0; index < batch.length; index += 1) {
245
- const result = results[index];
246
- if (result.ok) batch[index].resolve();
247
- else batch[index].reject(result.error);
248
- }
249
- }
250
- function rejectBatch(batch, err) {
251
- for (const waiter of batch) waiter.reject(err);
252
- }
253
- async function persistHistory(newEntries) {
254
- const existing = await loadHistory(historyFilePath);
255
- const merged = [...newEntries.slice().reverse(), ...existing.entries].slice(0, 50);
256
- await saveHistory(requireWriteJson(), historyFilePath, { entries: merged });
257
- }
258
- async function processBatch(batch) {
259
- let state;
260
- try {
261
- state = await loadActive(activeFilePath);
262
- } catch (err) {
263
- logger().error("load failed", { error: String(err) });
264
- rejectBatch(batch, err);
265
- return;
266
- }
267
- const results = applyBatchMutations(batch, state);
268
- const events = collectEvents(results);
269
- const historyEntries = collectHistoryEntries(results);
270
- if (events.length > 0) {
271
- try {
272
- await saveActive(requireWriteJson(), activeFilePath, state);
273
- } catch (err) {
274
- logger().error("active write failed", { error: String(err) });
275
- rejectBatch(batch, err);
276
- return;
277
- }
278
- if (historyEntries.length > 0) try {
279
- await persistHistory(historyEntries);
280
- } catch (err) {
281
- logger().error("history write failed", { error: String(err) });
282
- }
283
- for (const event of events) emit(event);
284
- }
285
- settleBatch(batch, results);
286
- }
287
- async function drain() {
288
- writing = true;
289
- try {
290
- while (waiters.length > 0) {
291
- const batch = waiters;
292
- waiters = [];
293
- await processBatch(batch);
294
- }
295
- } finally {
296
- writing = false;
297
- }
298
- }
299
- function enqueue(mutate) {
300
- return new Promise((resolve, reject) => {
301
- waiters.push({
302
- mutate,
303
- resolve,
304
- reject
305
- });
306
- if (!writing) drain();
307
- });
308
- }
309
- function removeEntry(state, entryId) {
310
- const { [entryId]: __removed, ...remaining } = state.entries;
311
- return remaining;
312
- }
313
- function buildHistoryEntry(entry, terminalType) {
314
- return {
315
- ...entry,
316
- terminalType,
317
- terminalAt: (/* @__PURE__ */ new Date()).toISOString()
318
- };
319
- }
320
- async function publish(input) {
321
- const validationError = validatePublishInput(input);
322
- if (validationError) throw new Error(`notifier.publish: ${validationError}`);
323
- const entryId = (0, node_crypto.randomUUID)();
324
- const entry = {
325
- id: entryId,
326
- pluginPkg: input.pluginPkg,
327
- severity: input.severity,
328
- lifecycle: input.lifecycle,
329
- title: input.title,
330
- body: input.body,
331
- navigateTarget: input.navigateTarget,
332
- pluginData: input.pluginData,
333
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
334
- };
335
- await enqueue((state) => {
336
- state.entries[entryId] = entry;
337
- return { event: {
338
- type: "published",
339
- entry
340
- } };
341
- });
342
- return { id: entryId };
343
- }
344
- async function clear(entryId) {
345
- await enqueue((state) => {
346
- const entry = state.entries[entryId];
347
- if (!entry) return null;
348
- state.entries = removeEntry(state, entryId);
349
- return {
350
- event: {
351
- type: "cleared",
352
- id: entryId
353
- },
354
- historyEntry: buildHistoryEntry(entry, "cleared")
355
- };
356
- });
357
- }
358
- async function cancel(entryId) {
359
- await enqueue((state) => {
360
- const entry = state.entries[entryId];
361
- if (!entry) return null;
362
- state.entries = removeEntry(state, entryId);
363
- return {
364
- event: {
365
- type: "cancelled",
366
- id: entryId
367
- },
368
- historyEntry: buildHistoryEntry(entry, "cancelled")
369
- };
370
- });
371
- }
372
- /** In-place update for an active entry. Only the fields present on
373
- * `patch` are rewritten; `id`, `pluginPkg`, `lifecycle`, and
374
- * `createdAt` stay fixed. Emits a single `"updated"` event with the
375
- * post-mutation entry — no history record is written because the
376
- * entry is still active, just with refreshed content.
377
- *
378
- * No-ops (no throw) when the id is unknown, the entry belongs to a
379
- * different plugin, or the merged shape would violate
380
- * `validatePublishInput`. The silent skip matches `clearForPlugin`'s
381
- * isolation semantics; validation failures are logged for diagnosis. */
382
- async function updateForPlugin(pluginPkg, entryId, patch) {
383
- await enqueue((state) => {
384
- const entry = state.entries[entryId];
385
- if (!entry) return null;
386
- if (entry.pluginPkg !== pluginPkg) return null;
387
- const next = {
388
- ...entry,
389
- ...patch.severity !== void 0 ? { severity: patch.severity } : {},
390
- ...patch.title !== void 0 ? { title: patch.title } : {},
391
- ...patch.body !== void 0 ? { body: patch.body } : {},
392
- ...patch.navigateTarget !== void 0 ? { navigateTarget: patch.navigateTarget } : {},
393
- ...patch.pluginData !== void 0 ? { pluginData: patch.pluginData } : {}
394
- };
395
- const validationError = validatePublishInput({
396
- pluginPkg: next.pluginPkg,
397
- severity: next.severity,
398
- title: next.title,
399
- body: next.body,
400
- lifecycle: next.lifecycle,
401
- navigateTarget: next.navigateTarget,
402
- pluginData: next.pluginData
403
- });
404
- if (validationError) {
405
- logger().warn("update rejected by validation", {
406
- entryId,
407
- pluginPkg,
408
- error: validationError
409
- });
410
- return null;
411
- }
412
- state.entries[entryId] = next;
413
- return { event: {
414
- type: "updated",
415
- entry: next
416
- } };
417
- });
418
- }
419
- /** Plugin-scoped point lookup. Returns the entry by id, but only if it
420
- * belongs to the caller's plugin; otherwise undefined. Cross-plugin
421
- * reads return undefined for isolation — same property as
422
- * `clearForPlugin` / `updateForPlugin`. */
423
- async function getForPlugin(pluginPkg, entryId) {
424
- const entry = (await loadActive(activeFilePath)).entries[entryId];
425
- if (!entry) return void 0;
426
- if (entry.pluginPkg !== pluginPkg) return void 0;
427
- return entry;
428
- }
429
- /** Plugin-scoped clear. Same as `clear` but no-ops if the entry's
430
- * `pluginPkg` doesn't match the caller's, so a plugin can't dismiss
431
- * another plugin's notification by guessing or scraping its id. */
432
- async function clearForPlugin(pluginPkg, entryId) {
433
- await enqueue((state) => {
434
- const entry = state.entries[entryId];
435
- if (!entry) return null;
436
- if (entry.pluginPkg !== pluginPkg) return null;
437
- state.entries = removeEntry(state, entryId);
438
- return {
439
- event: {
440
- type: "cleared",
441
- id: entryId
442
- },
443
- historyEntry: buildHistoryEntry(entry, "cleared")
444
- };
445
- });
446
- }
447
- async function get(entryId) {
448
- return (await loadActive(activeFilePath)).entries[entryId];
449
- }
450
- async function listFor(pluginPkg) {
451
- const state = await loadActive(activeFilePath);
452
- return Object.values(state.entries).filter((entry) => entry.pluginPkg === pluginPkg);
453
- }
454
- async function listAll() {
455
- const state = await loadActive(activeFilePath);
456
- return Object.values(state.entries);
457
- }
458
- async function listHistory() {
459
- return (await loadHistory(historyFilePath)).entries;
460
- }
461
- //#endregion
462
- Object.defineProperty(exports, "HISTORY_CAP", {
463
- enumerable: true,
464
- get: function() {
465
- return HISTORY_CAP;
466
- }
467
- });
468
- Object.defineProperty(exports, "NOTIFIER_LIFECYCLES", {
469
- enumerable: true,
470
- get: function() {
471
- return NOTIFIER_LIFECYCLES;
472
- }
473
- });
474
- Object.defineProperty(exports, "NOTIFIER_LIMITS", {
475
- enumerable: true,
476
- get: function() {
477
- return NOTIFIER_LIMITS;
478
- }
479
- });
480
- Object.defineProperty(exports, "NOTIFIER_SEVERITIES", {
481
- enumerable: true,
482
- get: function() {
483
- return NOTIFIER_SEVERITIES;
484
- }
485
- });
486
- Object.defineProperty(exports, "cancel", {
487
- enumerable: true,
488
- get: function() {
489
- return cancel;
490
- }
491
- });
492
- Object.defineProperty(exports, "clear", {
493
- enumerable: true,
494
- get: function() {
495
- return clear;
496
- }
497
- });
498
- Object.defineProperty(exports, "clearForPlugin", {
499
- enumerable: true,
500
- get: function() {
501
- return clearForPlugin;
502
- }
503
- });
504
- Object.defineProperty(exports, "configureNotifier", {
505
- enumerable: true,
506
- get: function() {
507
- return configureNotifier;
508
- }
509
- });
510
- Object.defineProperty(exports, "get", {
511
- enumerable: true,
512
- get: function() {
513
- return get;
514
- }
515
- });
516
- Object.defineProperty(exports, "getForPlugin", {
517
- enumerable: true,
518
- get: function() {
519
- return getForPlugin;
520
- }
521
- });
522
- Object.defineProperty(exports, "listAll", {
523
- enumerable: true,
524
- get: function() {
525
- return listAll;
526
- }
527
- });
528
- Object.defineProperty(exports, "listFor", {
529
- enumerable: true,
530
- get: function() {
531
- return listFor;
532
- }
533
- });
534
- Object.defineProperty(exports, "listHistory", {
535
- enumerable: true,
536
- get: function() {
537
- return listHistory;
538
- }
539
- });
540
- Object.defineProperty(exports, "onEvent", {
541
- enumerable: true,
542
- get: function() {
543
- return onEvent;
544
- }
545
- });
546
- Object.defineProperty(exports, "publish", {
547
- enumerable: true,
548
- get: function() {
549
- return publish;
550
- }
551
- });
552
- Object.defineProperty(exports, "resetNotifier", {
553
- enumerable: true,
554
- get: function() {
555
- return resetNotifier;
556
- }
557
- });
558
- Object.defineProperty(exports, "setNotifierFilePaths", {
559
- enumerable: true,
560
- get: function() {
561
- return setNotifierFilePaths;
562
- }
563
- });
564
- Object.defineProperty(exports, "updateForPlugin", {
565
- enumerable: true,
566
- get: function() {
567
- return updateForPlugin;
568
- }
569
- });
570
- Object.defineProperty(exports, "validatePublishInput", {
571
- enumerable: true,
572
- get: function() {
573
- return validatePublishInput;
574
- }
575
- });
576
-
577
- //# sourceMappingURL=notifier-bS8IEeLA.cjs.map