@skillsmith/core 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +7 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts +23 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js +137 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.js +83 -10
- package/dist/src/analysis/tree-sitter/pythonIncremental.js.map +1 -1
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js +6 -1
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js.map +1 -1
- package/dist/src/config/audit-notify-state.d.ts +46 -0
- package/dist/src/config/audit-notify-state.d.ts.map +1 -0
- package/dist/src/config/audit-notify-state.js +55 -0
- package/dist/src/config/audit-notify-state.js.map +1 -0
- package/dist/src/config/audit-notify-state.test.d.ts +9 -0
- package/dist/src/config/audit-notify-state.test.d.ts.map +1 -0
- package/dist/src/config/audit-notify-state.test.js +63 -0
- package/dist/src/config/audit-notify-state.test.js.map +1 -0
- package/dist/src/config/index.d.ts +10 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/exports/services.d.ts +1 -1
- package/dist/src/exports/services.d.ts.map +1 -1
- package/dist/src/exports/services.js +3 -1
- package/dist/src/exports/services.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/logging/context.d.ts +58 -0
- package/dist/src/logging/context.d.ts.map +1 -0
- package/dist/src/logging/context.js +62 -0
- package/dist/src/logging/context.js.map +1 -0
- package/dist/src/logging/context.test.d.ts +14 -0
- package/dist/src/logging/context.test.d.ts.map +1 -0
- package/dist/src/logging/context.test.js +88 -0
- package/dist/src/logging/context.test.js.map +1 -0
- package/dist/src/logging/index.d.ts +13 -0
- package/dist/src/logging/index.d.ts.map +1 -0
- package/dist/src/logging/index.js +12 -0
- package/dist/src/logging/index.js.map +1 -0
- package/dist/src/logging/logger.d.ts +71 -0
- package/dist/src/logging/logger.d.ts.map +1 -0
- package/dist/src/logging/logger.js +264 -0
- package/dist/src/logging/logger.js.map +1 -0
- package/dist/src/logging/logger.test.d.ts +9 -0
- package/dist/src/logging/logger.test.d.ts.map +1 -0
- package/dist/src/logging/logger.test.js +320 -0
- package/dist/src/logging/logger.test.js.map +1 -0
- package/dist/src/logging/redact.d.ts +35 -0
- package/dist/src/logging/redact.d.ts.map +1 -0
- package/dist/src/logging/redact.js +152 -0
- package/dist/src/logging/redact.js.map +1 -0
- package/dist/src/logging/redact.test.d.ts +8 -0
- package/dist/src/logging/redact.test.d.ts.map +1 -0
- package/dist/src/logging/redact.test.js +330 -0
- package/dist/src/logging/redact.test.js.map +1 -0
- package/dist/src/logging/rotation.d.ts +75 -0
- package/dist/src/logging/rotation.d.ts.map +1 -0
- package/dist/src/logging/rotation.js +284 -0
- package/dist/src/logging/rotation.js.map +1 -0
- package/dist/src/logging/rotation.test.d.ts +11 -0
- package/dist/src/logging/rotation.test.d.ts.map +1 -0
- package/dist/src/logging/rotation.test.js +132 -0
- package/dist/src/logging/rotation.test.js.map +1 -0
- package/dist/src/logging/types.d.ts +69 -0
- package/dist/src/logging/types.d.ts.map +1 -0
- package/dist/src/logging/types.js +9 -0
- package/dist/src/logging/types.js.map +1 -0
- package/dist/src/security/index.d.ts +2 -0
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +4 -0
- package/dist/src/security/index.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.hostile-update.d.ts +46 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.js +209 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.js.map +1 -0
- package/dist/src/security/scanner/index.d.ts +2 -1
- package/dist/src/security/scanner/index.d.ts.map +1 -1
- package/dist/src/security/scanner/index.js +2 -0
- package/dist/src/security/scanner/index.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +21 -0
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.d.ts +13 -1
- package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.js +14 -1
- package/dist/src/services/skill-installation.helpers.js.map +1 -1
- package/dist/src/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.js +19 -2
- package/dist/src/services/skill-installation.io.js.map +1 -1
- package/dist/src/sync/BackgroundSyncService.d.ts +10 -2
- package/dist/src/sync/BackgroundSyncService.d.ts.map +1 -1
- package/dist/src/sync/BackgroundSyncService.js +46 -4
- package/dist/src/sync/BackgroundSyncService.js.map +1 -1
- package/dist/src/sync/SyncEngine.d.ts +9 -0
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +23 -4
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/sync/access-token.d.ts +27 -0
- package/dist/src/sync/access-token.d.ts.map +1 -0
- package/dist/src/sync/access-token.js +40 -0
- package/dist/src/sync/access-token.js.map +1 -0
- package/dist/src/sync/audit-notify-client.d.ts +83 -0
- package/dist/src/sync/audit-notify-client.d.ts.map +1 -0
- package/dist/src/sync/audit-notify-client.js +126 -0
- package/dist/src/sync/audit-notify-client.js.map +1 -0
- package/dist/src/sync/audit-notify-client.test.d.ts +14 -0
- package/dist/src/sync/audit-notify-client.test.d.ts.map +1 -0
- package/dist/src/sync/audit-notify-client.test.js +133 -0
- package/dist/src/sync/audit-notify-client.test.js.map +1 -0
- package/dist/src/sync/index.d.ts +1 -0
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/index.js +2 -0
- package/dist/src/sync/index.js.map +1 -1
- package/dist/src/sync/inventory-client.d.ts.map +1 -1
- package/dist/src/sync/inventory-client.js +5 -16
- package/dist/src/sync/inventory-client.js.map +1 -1
- package/dist/src/telemetry/posthog.d.ts +15 -0
- package/dist/src/telemetry/posthog.d.ts.map +1 -1
- package/dist/src/telemetry/posthog.js +6 -1
- package/dist/src/telemetry/posthog.js.map +1 -1
- package/dist/src/telemetry/wrap.bench.js +5 -0
- package/dist/src/telemetry/wrap.bench.js.map +1 -1
- package/dist/src/telemetry/wrap.correlation.test.d.ts +20 -0
- package/dist/src/telemetry/wrap.correlation.test.d.ts.map +1 -0
- package/dist/src/telemetry/wrap.correlation.test.js +174 -0
- package/dist/src/telemetry/wrap.correlation.test.js.map +1 -0
- package/dist/src/telemetry/wrap.d.ts.map +1 -1
- package/dist/src/telemetry/wrap.gate.test.js +5 -0
- package/dist/src/telemetry/wrap.gate.test.js.map +1 -1
- package/dist/src/telemetry/wrap.js +111 -52
- package/dist/src/telemetry/wrap.js.map +1 -1
- package/dist/src/telemetry/wrap.marker.test.js +5 -0
- package/dist/src/telemetry/wrap.marker.test.js.map +1 -1
- package/dist/src/telemetry/wrap.test.js +10 -0
- package/dist/src/telemetry/wrap.test.js.map +1 -1
- package/dist/src/types.d.ts +6 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +179 -1
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/sync/BackgroundSyncService.abort.test.d.ts +17 -0
- package/dist/tests/sync/BackgroundSyncService.abort.test.d.ts.map +1 -0
- package/dist/tests/sync/BackgroundSyncService.abort.test.js +182 -0
- package/dist/tests/sync/BackgroundSyncService.abort.test.js.map +1 -0
- package/dist/tests/sync/BackgroundSyncService.test.d.ts +9 -1
- package/dist/tests/sync/BackgroundSyncService.test.d.ts.map +1 -1
- package/dist/tests/sync/BackgroundSyncService.test.js +8 -3
- package/dist/tests/sync/BackgroundSyncService.test.js.map +1 -1
- package/dist/tests/sync/SyncEngine.abort.test.d.ts +15 -0
- package/dist/tests/sync/SyncEngine.abort.test.d.ts.map +1 -0
- package/dist/tests/sync/SyncEngine.abort.test.js +100 -0
- package/dist/tests/sync/SyncEngine.abort.test.js.map +1 -0
- package/dist/tests/sync/SyncEngine.status.test.d.ts +10 -0
- package/dist/tests/sync/SyncEngine.status.test.d.ts.map +1 -0
- package/dist/tests/sync/SyncEngine.status.test.js +97 -0
- package/dist/tests/sync/SyncEngine.status.test.js.map +1 -0
- package/dist/tests/sync/SyncEngine.test.d.ts +22 -1
- package/dist/tests/sync/SyncEngine.test.d.ts.map +1 -1
- package/dist/tests/sync/SyncEngine.test.js +3 -69
- package/dist/tests/sync/SyncEngine.test.js.map +1 -1
- package/package.json +7 -2
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* user-configured frequency (daily/weekly). Uses non-blocking timers
|
|
6
6
|
* that don't prevent process exit.
|
|
7
7
|
*/
|
|
8
|
+
import { createLogger } from '../logging/index.js';
|
|
9
|
+
// SMI-5649: sync errors were previously invisible by default (a no-op
|
|
10
|
+
// `onSyncError`); mirrors shutdown.ts's SMI-5615 stderr-visibility pattern
|
|
11
|
+
// so a background sync failure is never silently swallowed.
|
|
12
|
+
const logger = createLogger('mcp');
|
|
8
13
|
/**
|
|
9
14
|
* Background sync service for automatic registry synchronization
|
|
10
15
|
*/
|
|
@@ -15,6 +20,10 @@ export class BackgroundSyncService {
|
|
|
15
20
|
timer = null;
|
|
16
21
|
isRunning = false;
|
|
17
22
|
isStopped = false;
|
|
23
|
+
// SMI-5649: abort + in-flight tracking so stop() can await settlement
|
|
24
|
+
// instead of being fire-and-forget (the root cause of the shutdown race).
|
|
25
|
+
abortController = null;
|
|
26
|
+
inFlightSync = null;
|
|
18
27
|
state = {
|
|
19
28
|
isStarted: false,
|
|
20
29
|
isRunning: false,
|
|
@@ -30,7 +39,13 @@ export class BackgroundSyncService {
|
|
|
30
39
|
checkIntervalMs: options.checkIntervalMs ?? 60000, // 1 minute
|
|
31
40
|
syncOnStart: options.syncOnStart ?? true,
|
|
32
41
|
onSyncComplete: options.onSyncComplete ?? (() => { }),
|
|
33
|
-
|
|
42
|
+
// SMI-5649: default now logs (was a silent no-op) — mirrors
|
|
43
|
+
// closeDbOnShutdown's SMI-5615 stderr-visibility rationale so a
|
|
44
|
+
// background sync failure is visible even when no caller overrides it.
|
|
45
|
+
onSyncError: options.onSyncError ??
|
|
46
|
+
((error) => {
|
|
47
|
+
logger.error('[skillsmith] Background sync failed', { err: error });
|
|
48
|
+
}),
|
|
34
49
|
debug: options.debug ?? false,
|
|
35
50
|
};
|
|
36
51
|
}
|
|
@@ -54,6 +69,9 @@ export class BackgroundSyncService {
|
|
|
54
69
|
}
|
|
55
70
|
this.state.isStarted = true;
|
|
56
71
|
this.isStopped = false;
|
|
72
|
+
// SMI-5649: fresh controller per start() so a stop()->start() cycle
|
|
73
|
+
// (e.g. in tests) doesn't reuse an already-aborted signal.
|
|
74
|
+
this.abortController = new AbortController();
|
|
57
75
|
this.log('Starting background sync service', {
|
|
58
76
|
frequency: config.frequency,
|
|
59
77
|
intervalMs: config.intervalMs,
|
|
@@ -72,16 +90,31 @@ export class BackgroundSyncService {
|
|
|
72
90
|
}
|
|
73
91
|
}
|
|
74
92
|
/**
|
|
75
|
-
* Stop the background sync service
|
|
93
|
+
* Stop the background sync service.
|
|
94
|
+
*
|
|
95
|
+
* SMI-5649: returns `Promise<void>` (was `void`, fire-and-forget) so a
|
|
96
|
+
* caller — namely the shutdown coordinator — can await settlement of any
|
|
97
|
+
* in-flight sync before proceeding to close the database. Non-awaiting
|
|
98
|
+
* callers still get the synchronous timer-clear + abort-signal behavior
|
|
99
|
+
* unchanged, since steps 1-2 below run before any `await` point.
|
|
76
100
|
*/
|
|
77
101
|
stop() {
|
|
78
102
|
this.log('Stopping background sync service');
|
|
103
|
+
// 1. Synchronous first — preserves fire-and-forget semantics for
|
|
104
|
+
// existing non-awaiting callers.
|
|
79
105
|
this.isStopped = true;
|
|
80
106
|
this.state.isStarted = false;
|
|
81
107
|
if (this.timer) {
|
|
82
108
|
clearInterval(this.timer);
|
|
83
109
|
this.timer = null;
|
|
84
110
|
}
|
|
111
|
+
// 2. Signal the in-flight sync (if any) to bail at its next checkpoint.
|
|
112
|
+
this.abortController?.abort();
|
|
113
|
+
// 3. Await settlement, never letting a rejection escape.
|
|
114
|
+
return Promise.resolve(this.inFlightSync ?? undefined).catch(() => {
|
|
115
|
+
// inFlightSync is already normalized to never reject (see
|
|
116
|
+
// triggerSync/manualSync below); this is belt-and-suspenders.
|
|
117
|
+
});
|
|
85
118
|
}
|
|
86
119
|
/**
|
|
87
120
|
* Check if sync should run now
|
|
@@ -130,9 +163,14 @@ export class BackgroundSyncService {
|
|
|
130
163
|
this.isRunning = true;
|
|
131
164
|
this.state.isRunning = true;
|
|
132
165
|
this.state.syncsTriggered++;
|
|
166
|
+
// SMI-5649: track the raw sync() promise (never rejects — SyncEngine.sync
|
|
167
|
+
// try/catches everything internally) so stop() can await its settlement
|
|
168
|
+
// without depending on this method's own try/catch/finally timing.
|
|
169
|
+
const syncPromise = this.syncEngine.sync({ signal: this.abortController?.signal });
|
|
170
|
+
this.inFlightSync = syncPromise.then(() => undefined, () => undefined);
|
|
133
171
|
try {
|
|
134
172
|
this.log('Starting sync');
|
|
135
|
-
const result = await
|
|
173
|
+
const result = await syncPromise;
|
|
136
174
|
this.state.lastResult = result;
|
|
137
175
|
this.state.lastError = null;
|
|
138
176
|
this.log('Sync completed', {
|
|
@@ -153,6 +191,7 @@ export class BackgroundSyncService {
|
|
|
153
191
|
finally {
|
|
154
192
|
this.isRunning = false;
|
|
155
193
|
this.state.isRunning = false;
|
|
194
|
+
this.inFlightSync = null;
|
|
156
195
|
}
|
|
157
196
|
}
|
|
158
197
|
/**
|
|
@@ -166,8 +205,10 @@ export class BackgroundSyncService {
|
|
|
166
205
|
}
|
|
167
206
|
this.isRunning = true;
|
|
168
207
|
this.state.isRunning = true;
|
|
208
|
+
const syncPromise = this.syncEngine.sync({ signal: this.abortController?.signal });
|
|
209
|
+
this.inFlightSync = syncPromise.then(() => undefined, () => undefined);
|
|
169
210
|
try {
|
|
170
|
-
const result = await
|
|
211
|
+
const result = await syncPromise;
|
|
171
212
|
this.state.lastResult = result;
|
|
172
213
|
this.state.lastError = null;
|
|
173
214
|
this.options.onSyncComplete(result);
|
|
@@ -182,6 +223,7 @@ export class BackgroundSyncService {
|
|
|
182
223
|
finally {
|
|
183
224
|
this.isRunning = false;
|
|
184
225
|
this.state.isRunning = false;
|
|
226
|
+
this.inFlightSync = null;
|
|
185
227
|
}
|
|
186
228
|
}
|
|
187
229
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BackgroundSyncService.js","sourceRoot":"","sources":["../../../src/sync/BackgroundSyncService.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"BackgroundSyncService.js","sourceRoot":"","sources":["../../../src/sync/BackgroundSyncService.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,15 @@ export interface SyncOptions {
|
|
|
23
23
|
pageSize?: number;
|
|
24
24
|
/** Progress callback */
|
|
25
25
|
onProgress?: (progress: SyncProgress) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Abort signal (SMI-5649). Checked at loop and pre-write boundaries so a
|
|
28
|
+
* shutdown quiesce can stop an in-flight sync before it writes against a
|
|
29
|
+
* db that is about to be closed. Every driver write is synchronous, so
|
|
30
|
+
* once the signal is aborted, no new write ever begins — this is what
|
|
31
|
+
* makes it safe for the coordinator to proceed to `db.close()` once its
|
|
32
|
+
* bounded quiesce timeout elapses, even if this method hasn't returned yet.
|
|
33
|
+
*/
|
|
34
|
+
signal?: AbortSignal;
|
|
26
35
|
}
|
|
27
36
|
/**
|
|
28
37
|
* Sync progress info
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SyncEngine.d.ts","sourceRoot":"","sources":["../../../src/sync/SyncEngine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,kBAAkB,CAAA;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAA;AACnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAA;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAA;AACvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAA;AAa/E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"SyncEngine.d.ts","sourceRoot":"","sources":["../../../src/sync/SyncEngine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,kBAAkB,CAAA;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAA;AACnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAA;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAA;AACvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAA;AAa/E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;IAC7C;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAA;IACzE,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,OAAO,CAAA;CAChB;AAWD;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,YAAY,CAA2B;gBAG7C,SAAS,EAAE,mBAAmB,EAC9B,SAAS,EAAE,eAAe,EAC1B,cAAc,EAAE,oBAAoB,EACpC,eAAe,EAAE,qBAAqB,EACtC,gBAAgB,EAAE,sBAAsB,EACxC,YAAY,CAAC,EAAE,kBAAkB,GAAG,IAAI;IAU1C;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACG,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAwQ1D;;OAEG;YACW,YAAY;IAiF1B;;OAEG;IACH,SAAS,IAAI;QACX,MAAM,EAAE,UAAU,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,CAAA;QACrD,OAAO,EAAE,UAAU,CAAC,qBAAqB,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAC/D,SAAS,EAAE,OAAO,CAAA;QAClB,KAAK,EAAE,OAAO,CAAA;KACf;CAQF"}
|
|
@@ -52,7 +52,7 @@ export class SyncEngine {
|
|
|
52
52
|
* Run sync operation
|
|
53
53
|
*/
|
|
54
54
|
async sync(options = {}) {
|
|
55
|
-
const { force = false, dryRun = false, pageSize = 100, onProgress } = options;
|
|
55
|
+
const { force = false, dryRun = false, pageSize = 100, onProgress, signal } = options;
|
|
56
56
|
const startTime = Date.now();
|
|
57
57
|
const errors = [];
|
|
58
58
|
let skillsAdded = 0;
|
|
@@ -99,9 +99,17 @@ export class SyncEngine {
|
|
|
99
99
|
const searchQueries = ['git', 'code', 'dev', 'test', 'npm', 'api', 'cli', 'doc'];
|
|
100
100
|
const seenIds = new Set();
|
|
101
101
|
for (const searchQuery of searchQueries) {
|
|
102
|
+
// SMI-5649: stop fetching new query batches once aborted.
|
|
103
|
+
if (signal?.aborted)
|
|
104
|
+
break;
|
|
102
105
|
offset = 0;
|
|
103
106
|
hasMore = true;
|
|
104
107
|
while (hasMore) {
|
|
108
|
+
// SMI-5649: stop paginating the current query once aborted.
|
|
109
|
+
if (signal?.aborted) {
|
|
110
|
+
hasMore = false;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
105
113
|
try {
|
|
106
114
|
const response = await this.apiClient.search({
|
|
107
115
|
query: searchQuery,
|
|
@@ -171,7 +179,13 @@ export class SyncEngine {
|
|
|
171
179
|
message: `Processing ${skillsToProcess.length} changed skills...`,
|
|
172
180
|
});
|
|
173
181
|
// Upsert changed skills
|
|
174
|
-
if
|
|
182
|
+
// SMI-5649: if aborted before any writes began, skip the upsert
|
|
183
|
+
// entirely and report as if nothing changed — never start a partial
|
|
184
|
+
// write once the shutdown quiesce has signaled abort.
|
|
185
|
+
if (signal?.aborted) {
|
|
186
|
+
skillsUnchanged = allSkills.length;
|
|
187
|
+
}
|
|
188
|
+
else if (!dryRun && skillsToProcess.length > 0) {
|
|
175
189
|
const stats = await this.upsertSkills(skillsToProcess, (current) => {
|
|
176
190
|
onProgress?.({
|
|
177
191
|
phase: 'upserting',
|
|
@@ -181,7 +195,7 @@ export class SyncEngine {
|
|
|
181
195
|
skillsChanged: skillsToProcess.length,
|
|
182
196
|
message: `Upserting skill ${current}/${skillsToProcess.length}...`,
|
|
183
197
|
});
|
|
184
|
-
});
|
|
198
|
+
}, signal);
|
|
185
199
|
skillsAdded = stats.added;
|
|
186
200
|
skillsUpdated = stats.updated;
|
|
187
201
|
skillsUnchanged = stats.unchanged;
|
|
@@ -276,11 +290,16 @@ export class SyncEngine {
|
|
|
276
290
|
/**
|
|
277
291
|
* Upsert skills into local database
|
|
278
292
|
*/
|
|
279
|
-
async upsertSkills(skills, onProgress) {
|
|
293
|
+
async upsertSkills(skills, onProgress, signal) {
|
|
280
294
|
let added = 0;
|
|
281
295
|
let updated = 0;
|
|
282
296
|
let unchanged = 0;
|
|
283
297
|
for (let i = 0; i < skills.length; i++) {
|
|
298
|
+
// SMI-5649: stop before the next skill once aborted. Each
|
|
299
|
+
// already-issued skillRepo.create/update + recordVersion call above
|
|
300
|
+
// this point is already committed — we simply don't start another.
|
|
301
|
+
if (signal?.aborted)
|
|
302
|
+
break;
|
|
284
303
|
const skill = skills[i];
|
|
285
304
|
const existing = this.skillRepo.findById(skill.id);
|
|
286
305
|
if (existing) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SyncEngine.js","sourceRoot":"","sources":["../../../src/sync/SyncEngine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAQnC;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,OAAe;IAClC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACnE,CAAC;
|
|
1
|
+
{"version":3,"file":"SyncEngine.js","sourceRoot":"","sources":["../../../src/sync/SyncEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared device-session access-token resolver (SMI-5541, extracted from
|
|
3
|
+
* SMI-5392 `inventory-client.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Both the inventory upload/purge client and the audit-notify client need to
|
|
6
|
+
* resolve a fresh access token from the stored device-login credentials,
|
|
7
|
+
* transparently refreshing (and persisting) one that is within
|
|
8
|
+
* {@link TOKEN_REFRESH_SKEW_MS} of expiry. This is security-sensitive logic
|
|
9
|
+
* (token refresh + persistence), so it lives in exactly one place; each caller
|
|
10
|
+
* supplies its own namespaced "not authenticated" error via `makeAuthError` so
|
|
11
|
+
* the shared helper stays error-type-agnostic.
|
|
12
|
+
*
|
|
13
|
+
* @module @skillsmith/core/sync/access-token
|
|
14
|
+
*/
|
|
15
|
+
/** Refresh the access token this many ms before it actually expires. */
|
|
16
|
+
export declare const TOKEN_REFRESH_SKEW_MS = 60000;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a fresh access token, refreshing (and persisting) if the stored one
|
|
19
|
+
* is within {@link TOKEN_REFRESH_SKEW_MS} of expiry.
|
|
20
|
+
*
|
|
21
|
+
* @param makeAuthError - Factory for the caller's namespaced auth error, thrown
|
|
22
|
+
* when there are no stored credentials or the refresh fails. Kept as a factory
|
|
23
|
+
* (not a value) so the stack originates at the throw site.
|
|
24
|
+
* @throws Whatever `makeAuthError()` returns, on no/expired-unrefreshable session.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveAccessToken(makeAuthError: () => Error): Promise<string>;
|
|
27
|
+
//# sourceMappingURL=access-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token.d.ts","sourceRoot":"","sources":["../../../src/sync/access-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,QAAS,CAAA;AAE3C;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAYpF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared device-session access-token resolver (SMI-5541, extracted from
|
|
3
|
+
* SMI-5392 `inventory-client.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Both the inventory upload/purge client and the audit-notify client need to
|
|
6
|
+
* resolve a fresh access token from the stored device-login credentials,
|
|
7
|
+
* transparently refreshing (and persisting) one that is within
|
|
8
|
+
* {@link TOKEN_REFRESH_SKEW_MS} of expiry. This is security-sensitive logic
|
|
9
|
+
* (token refresh + persistence), so it lives in exactly one place; each caller
|
|
10
|
+
* supplies its own namespaced "not authenticated" error via `makeAuthError` so
|
|
11
|
+
* the shared helper stays error-type-agnostic.
|
|
12
|
+
*
|
|
13
|
+
* @module @skillsmith/core/sync/access-token
|
|
14
|
+
*/
|
|
15
|
+
import { loadCredentials, refreshAccessToken, storeCredentials, } from '../config/token-credentials.js';
|
|
16
|
+
/** Refresh the access token this many ms before it actually expires. */
|
|
17
|
+
export const TOKEN_REFRESH_SKEW_MS = 60_000;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a fresh access token, refreshing (and persisting) if the stored one
|
|
20
|
+
* is within {@link TOKEN_REFRESH_SKEW_MS} of expiry.
|
|
21
|
+
*
|
|
22
|
+
* @param makeAuthError - Factory for the caller's namespaced auth error, thrown
|
|
23
|
+
* when there are no stored credentials or the refresh fails. Kept as a factory
|
|
24
|
+
* (not a value) so the stack originates at the throw site.
|
|
25
|
+
* @throws Whatever `makeAuthError()` returns, on no/expired-unrefreshable session.
|
|
26
|
+
*/
|
|
27
|
+
export async function resolveAccessToken(makeAuthError) {
|
|
28
|
+
const creds = await loadCredentials();
|
|
29
|
+
if (!creds)
|
|
30
|
+
throw makeAuthError();
|
|
31
|
+
if (creds.expiresAt <= Date.now() + TOKEN_REFRESH_SKEW_MS) {
|
|
32
|
+
const refreshed = await refreshAccessToken(creds.refreshToken);
|
|
33
|
+
if (!refreshed)
|
|
34
|
+
throw makeAuthError();
|
|
35
|
+
await storeCredentials(refreshed);
|
|
36
|
+
return refreshed.accessToken;
|
|
37
|
+
}
|
|
38
|
+
return creds.accessToken;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=access-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token.js","sourceRoot":"","sources":["../../../src/sync/access-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,gCAAgC,CAAA;AAEvC,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAA;AAE3C;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,aAA0B;IACjE,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK;QAAE,MAAM,aAAa,EAAE,CAAA;IAEjC,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,qBAAqB,EAAE,CAAC;QAC1D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAC9D,IAAI,CAAC,SAAS;YAAE,MAAM,aAAa,EAAE,CAAA;QACrC,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAA;QACjC,OAAO,SAAS,CAAC,WAAW,CAAA;IAC9B,CAAC;IAED,OAAO,KAAK,CAAC,WAAW,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit-digest push client (SMI-5541 Wave 2C Stage 2, Option 1).
|
|
3
|
+
*
|
|
4
|
+
* The security audit runs CLIENT-side (the inventory data plane is
|
|
5
|
+
* metadata-only, ADR-124), so the digest's email channel is a thin push: this
|
|
6
|
+
* client authenticates with the stored device-login session and POSTs a
|
|
7
|
+
* COMPACT findings summary — never raw skill content — to the gateway-verified
|
|
8
|
+
* `audit-notify` edge function, which gates on consent + a verified email
|
|
9
|
+
* server-side and sends the email via Resend.
|
|
10
|
+
*
|
|
11
|
+
* The payload contract here is the client-side twin of the edge function's
|
|
12
|
+
* `parseDigest`: `{ scanned, hostile, malicious, suspicious, findings }`, each
|
|
13
|
+
* finding `{ identifier, kind, verdict, reason }`. Defined independently in
|
|
14
|
+
* `@skillsmith/core` (which must not depend on `@skillsmith/mcp-server`); the
|
|
15
|
+
* mapping from a `RunSecurityAuditResult` into this shape lives in
|
|
16
|
+
* `@skillsmith/mcp-server/audit` (`buildAuditDigestPayload`), which can see
|
|
17
|
+
* both types.
|
|
18
|
+
*
|
|
19
|
+
* @module @skillsmith/core/sync/audit-notify-client
|
|
20
|
+
*/
|
|
21
|
+
/** The three security verdicts the digest carries (mirrors the edge fn). */
|
|
22
|
+
export type AuditDigestVerdict = 'hostile' | 'malicious' | 'suspicious';
|
|
23
|
+
/** One compact finding in a pushed digest. Carries NO raw skill content. */
|
|
24
|
+
export interface AuditDigestPushFinding {
|
|
25
|
+
/** Skill/command/agent identifier (e.g. `author/name`). */
|
|
26
|
+
identifier: string;
|
|
27
|
+
/** Inventory kind (`skill` | `command` | `agent`). */
|
|
28
|
+
kind: string;
|
|
29
|
+
verdict: AuditDigestVerdict;
|
|
30
|
+
/** One human-readable sentence citing the deciding signal. */
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
/** The compact digest pushed to `audit-notify`. Mirrors the edge fn's parser. */
|
|
34
|
+
export interface AuditDigestPushPayload {
|
|
35
|
+
scanned: number;
|
|
36
|
+
hostile: number;
|
|
37
|
+
malicious: number;
|
|
38
|
+
suspicious: number;
|
|
39
|
+
findings: AuditDigestPushFinding[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Outcome of a push. `ok`/`sent`/`reason` mirror the edge function's 200-body
|
|
43
|
+
* shapes so callers can render a precise message without re-deriving:
|
|
44
|
+
* - `{ ok: true, sent: true }` — email dispatched.
|
|
45
|
+
* - `{ ok: true, sent: false, reason: 'nothing_to_report' }`
|
|
46
|
+
* - `{ ok: false, reason: 'not_consented' | 'email_not_verified' | 'no_email' }`
|
|
47
|
+
* - `{ ok: false, sent: false, reason: 'email_send_failed' }`
|
|
48
|
+
*/
|
|
49
|
+
export interface AuditDigestPushResult {
|
|
50
|
+
ok: boolean;
|
|
51
|
+
sent: boolean;
|
|
52
|
+
reason?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* No usable device session. The message hints at the recovery action so the
|
|
56
|
+
* CLI/MCP surface can relay it.
|
|
57
|
+
*/
|
|
58
|
+
export declare class AuditNotifyAuthError extends Error {
|
|
59
|
+
constructor(message?: string);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Catch-all for transport failures and unexpected server responses (HTTP 5xx,
|
|
63
|
+
* 400 `invalid_payload`, network errors, unparseable bodies).
|
|
64
|
+
*/
|
|
65
|
+
export declare class AuditNotifyError extends Error {
|
|
66
|
+
constructor(message: string);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Push a compact audit digest to the `audit-notify` edge function.
|
|
70
|
+
*
|
|
71
|
+
* A `200` response is returned verbatim as an {@link AuditDigestPushResult} —
|
|
72
|
+
* this INCLUDES the consent-off `{ ok: false, reason: 'not_consented' }` and
|
|
73
|
+
* the `{ ok: true, sent: false, reason: 'nothing_to_report' }` no-ops, which
|
|
74
|
+
* are successful round-trips, not errors.
|
|
75
|
+
*
|
|
76
|
+
* @param payload - The compact digest (never raw content).
|
|
77
|
+
* @returns The edge function's outcome.
|
|
78
|
+
* @throws {AuditNotifyAuthError} HTTP 401, or no/expired session.
|
|
79
|
+
* @throws {AuditNotifyError} HTTP 400/5xx, network failure, or unparseable 200 body.
|
|
80
|
+
* @see SMI-5541
|
|
81
|
+
*/
|
|
82
|
+
export declare function sendAuditDigest(payload: AuditDigestPushPayload): Promise<AuditDigestPushResult>;
|
|
83
|
+
//# sourceMappingURL=audit-notify-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-notify-client.d.ts","sourceRoot":"","sources":["../../../src/sync/audit-notify-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAA;AAEvE,4EAA4E;AAC5E,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAA;IAClB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,kBAAkB,CAAA;IAC3B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAA;CACf;AAED,iFAAiF;AACjF,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,sBAAsB,EAAE,CAAA;CACnC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,SAA6D;CAIjF;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAkBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CA2DhC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit-digest push client (SMI-5541 Wave 2C Stage 2, Option 1).
|
|
3
|
+
*
|
|
4
|
+
* The security audit runs CLIENT-side (the inventory data plane is
|
|
5
|
+
* metadata-only, ADR-124), so the digest's email channel is a thin push: this
|
|
6
|
+
* client authenticates with the stored device-login session and POSTs a
|
|
7
|
+
* COMPACT findings summary — never raw skill content — to the gateway-verified
|
|
8
|
+
* `audit-notify` edge function, which gates on consent + a verified email
|
|
9
|
+
* server-side and sends the email via Resend.
|
|
10
|
+
*
|
|
11
|
+
* The payload contract here is the client-side twin of the edge function's
|
|
12
|
+
* `parseDigest`: `{ scanned, hostile, malicious, suspicious, findings }`, each
|
|
13
|
+
* finding `{ identifier, kind, verdict, reason }`. Defined independently in
|
|
14
|
+
* `@skillsmith/core` (which must not depend on `@skillsmith/mcp-server`); the
|
|
15
|
+
* mapping from a `RunSecurityAuditResult` into this shape lives in
|
|
16
|
+
* `@skillsmith/mcp-server/audit` (`buildAuditDigestPayload`), which can see
|
|
17
|
+
* both types.
|
|
18
|
+
*
|
|
19
|
+
* @module @skillsmith/core/sync/audit-notify-client
|
|
20
|
+
*/
|
|
21
|
+
import { resolveAccessToken as resolveSessionToken } from './access-token.js';
|
|
22
|
+
import { DEFAULT_BASE_URL, PRODUCTION_ANON_KEY } from '../api/utils.js';
|
|
23
|
+
/**
|
|
24
|
+
* No usable device session. The message hints at the recovery action so the
|
|
25
|
+
* CLI/MCP surface can relay it.
|
|
26
|
+
*/
|
|
27
|
+
export class AuditNotifyAuthError extends Error {
|
|
28
|
+
constructor(message = 'Not authenticated. Run `skillsmith login` and try again.') {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'AuditNotifyAuthError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Catch-all for transport failures and unexpected server responses (HTTP 5xx,
|
|
35
|
+
* 400 `invalid_payload`, network errors, unparseable bodies).
|
|
36
|
+
*/
|
|
37
|
+
export class AuditNotifyError extends Error {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'AuditNotifyError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Best-effort extraction of the server's `{ error }` / `{ reason }` body so a
|
|
45
|
+
* typed error can carry the precise server message. Returns `null` when the
|
|
46
|
+
* body is absent or not JSON.
|
|
47
|
+
*/
|
|
48
|
+
async function readServerMessage(res) {
|
|
49
|
+
try {
|
|
50
|
+
const body = (await res.json());
|
|
51
|
+
if (typeof body.error === 'string')
|
|
52
|
+
return body.error;
|
|
53
|
+
if (typeof body.reason === 'string')
|
|
54
|
+
return body.reason;
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Push a compact audit digest to the `audit-notify` edge function.
|
|
63
|
+
*
|
|
64
|
+
* A `200` response is returned verbatim as an {@link AuditDigestPushResult} —
|
|
65
|
+
* this INCLUDES the consent-off `{ ok: false, reason: 'not_consented' }` and
|
|
66
|
+
* the `{ ok: true, sent: false, reason: 'nothing_to_report' }` no-ops, which
|
|
67
|
+
* are successful round-trips, not errors.
|
|
68
|
+
*
|
|
69
|
+
* @param payload - The compact digest (never raw content).
|
|
70
|
+
* @returns The edge function's outcome.
|
|
71
|
+
* @throws {AuditNotifyAuthError} HTTP 401, or no/expired session.
|
|
72
|
+
* @throws {AuditNotifyError} HTTP 400/5xx, network failure, or unparseable 200 body.
|
|
73
|
+
* @see SMI-5541
|
|
74
|
+
*/
|
|
75
|
+
export async function sendAuditDigest(payload) {
|
|
76
|
+
const accessToken = await resolveSessionToken(() => new AuditNotifyAuthError());
|
|
77
|
+
let res;
|
|
78
|
+
try {
|
|
79
|
+
res = await fetch(`${DEFAULT_BASE_URL}/audit-notify`, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
apikey: PRODUCTION_ANON_KEY,
|
|
84
|
+
Authorization: `Bearer ${accessToken}`,
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify(payload),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
91
|
+
throw new AuditNotifyError(`Audit digest request failed: ${detail}`);
|
|
92
|
+
}
|
|
93
|
+
if (res.ok) {
|
|
94
|
+
let body;
|
|
95
|
+
try {
|
|
96
|
+
body = (await res.json());
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
100
|
+
throw new AuditNotifyError(`Audit digest returned an unreadable body: ${detail}`);
|
|
101
|
+
}
|
|
102
|
+
// A parseable-but-non-object 200 body (e.g. JSON `null`) would NPE the field
|
|
103
|
+
// reads below — reject it as the documented AuditNotifyError, not a TypeError.
|
|
104
|
+
if (typeof body !== 'object' || body === null) {
|
|
105
|
+
throw new AuditNotifyError('Audit digest returned an unexpected 200 body shape.');
|
|
106
|
+
}
|
|
107
|
+
// Most 200 outcomes carry `reason`; the Resend-failure body uses `error`
|
|
108
|
+
// (`email_send_failed`) — surface either so the caller can render it.
|
|
109
|
+
const reason = typeof body.reason === 'string'
|
|
110
|
+
? body.reason
|
|
111
|
+
: typeof body.error === 'string'
|
|
112
|
+
? body.error
|
|
113
|
+
: undefined;
|
|
114
|
+
return {
|
|
115
|
+
ok: body.ok === true,
|
|
116
|
+
sent: body.sent === true,
|
|
117
|
+
...(reason !== undefined ? { reason } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const serverMessage = await readServerMessage(res);
|
|
121
|
+
if (res.status === 401) {
|
|
122
|
+
throw new AuditNotifyAuthError();
|
|
123
|
+
}
|
|
124
|
+
throw new AuditNotifyError(`Audit digest push failed (HTTP ${res.status})${serverMessage ? `: ${serverMessage}` : ''}`);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=audit-notify-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-notify-client.js","sourceRoot":"","sources":["../../../src/sync/audit-notify-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-notify-client tests (SMI-5541 Wave 2C Stage 2).
|
|
3
|
+
*
|
|
4
|
+
* Mocks the token-credentials leaf module (which the shared `access-token`
|
|
5
|
+
* resolver imports) and global fetch.
|
|
6
|
+
*
|
|
7
|
+
* UC-1: no credentials -> AuditNotifyAuthError, no fetch.
|
|
8
|
+
* UC-2: expired credentials -> refresh path runs, new creds stored, new token used.
|
|
9
|
+
* UC-3: 200 bodies (sent / nothing_to_report / not_consented) returned verbatim.
|
|
10
|
+
* UC-4: 401 -> AuditNotifyAuthError; 400 / 500 -> AuditNotifyError (carries reason).
|
|
11
|
+
* UC-5: network throw / unreadable 200 -> AuditNotifyError.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=audit-notify-client.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-notify-client.test.d.ts","sourceRoot":"","sources":["../../../src/sync/audit-notify-client.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|