@nexalink/carecompass-sdk 0.2.0-beta.15 → 0.3.0-beta.15
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/dist/cli.js +313 -19
- package/dist/snippet.d.ts +1 -1
- package/dist/snippet.js +4 -1
- package/dist/telemetry.d.ts +43 -2
- package/dist/telemetry.js +85 -3
- package/dist/version.js +1 -1
- package/package.json +8 -5
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync, } from "node:fs";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { createDefaultCareCompassConfig, validateCareCompassConfig, } from "./schema.js";
|
|
6
|
+
import { careCompassApiBaseUrlSchema, createDefaultCareCompassConfig, validateCareCompassConfig, } from "./schema.js";
|
|
7
7
|
import { normalizeApiKey, resolveApiKey, resolveCredentialsPath, writeStoredApiKey, } from "./credentials.js";
|
|
8
8
|
import { generateCareCompassIframeSnippet, generateCareCompassSnippet, } from "./snippet.js";
|
|
9
9
|
import { CARECOMPASS_TELEMETRY_ERROR_CODES, CARECOMPASS_TELEMETRY_EVENTS, createCareCompassCliTelemetry, } from "./telemetry.js";
|
|
@@ -31,6 +31,172 @@ support resources, and appropriate next steps. Stay out of diagnosis, treatment
|
|
|
31
31
|
planning, emergency triage, legal advice, and coverage guarantees. When a member
|
|
32
32
|
needs clinical, emergency, plan-specific, or account-specific help that requires
|
|
33
33
|
a human, route them to the customer's approved support resources.`;
|
|
34
|
+
const CLI_COMMAND_ALLOWLIST = new Set([
|
|
35
|
+
"doctor",
|
|
36
|
+
"help",
|
|
37
|
+
"init",
|
|
38
|
+
"login",
|
|
39
|
+
"snippet",
|
|
40
|
+
"validate",
|
|
41
|
+
]);
|
|
42
|
+
class CliTelemetryError extends Error {
|
|
43
|
+
constructor(message, telemetry) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.telemetry = telemetry;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const sanitizeCommandName = (command) => {
|
|
49
|
+
const normalized = command === "--help" || command === "-h" ? "help" : command;
|
|
50
|
+
return CLI_COMMAND_ALLOWLIST.has(normalized) ? normalized : "unknown_command";
|
|
51
|
+
};
|
|
52
|
+
const authTelemetryForSource = (source) => {
|
|
53
|
+
if (source === "environment") {
|
|
54
|
+
return { authMethod: "env_var", keySource: "env_var" };
|
|
55
|
+
}
|
|
56
|
+
if (source === "file") {
|
|
57
|
+
return { authMethod: "config_file", keySource: "config_file" };
|
|
58
|
+
}
|
|
59
|
+
return { authMethod: "api_key", keySource: "cli_flag" };
|
|
60
|
+
};
|
|
61
|
+
const normalizeBaaStatus = (value) => {
|
|
62
|
+
if (value === "signed" || value === "pending" || value === "not_required") {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
return "signed";
|
|
66
|
+
};
|
|
67
|
+
const classifyAuthFailure = (status, message) => {
|
|
68
|
+
const normalized = message.toLowerCase();
|
|
69
|
+
if (status === 429 || normalized.includes("rate limit")) {
|
|
70
|
+
return {
|
|
71
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.RATE_LIMIT_EXCEEDED,
|
|
72
|
+
failureCategory: "rate_limit_error",
|
|
73
|
+
baaStatus: "signed",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (status === 403 ||
|
|
77
|
+
normalized.includes("baa") ||
|
|
78
|
+
normalized.includes("business associate")) {
|
|
79
|
+
return {
|
|
80
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.BAA_REQUIRED,
|
|
81
|
+
failureCategory: "baa_not_signed",
|
|
82
|
+
baaStatus: "pending",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (normalized.includes("expired")) {
|
|
86
|
+
return {
|
|
87
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.AUTH_KEY_EXPIRED,
|
|
88
|
+
failureCategory: "auth_error",
|
|
89
|
+
baaStatus: "signed",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.AUTH_KEY_INVALID,
|
|
94
|
+
failureCategory: "auth_error",
|
|
95
|
+
baaStatus: "signed",
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
const classifyCliFailure = (error) => {
|
|
99
|
+
if (error instanceof CliTelemetryError)
|
|
100
|
+
return error.telemetry;
|
|
101
|
+
const code = error && typeof error === "object" && "code" in error
|
|
102
|
+
? String(error.code)
|
|
103
|
+
: "";
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
const normalized = message.toLowerCase();
|
|
106
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
107
|
+
return {
|
|
108
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.PERMISSION_DENIED,
|
|
109
|
+
failureCategory: "permission_error",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (code === "ENOENT" || normalized.includes("was not found")) {
|
|
113
|
+
return {
|
|
114
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.CONFIG_NOT_FOUND,
|
|
115
|
+
failureCategory: "config_error",
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (normalized.includes("invalid carecompass config") ||
|
|
119
|
+
normalized.includes("unexpected argument") ||
|
|
120
|
+
normalized.includes("must be either") ||
|
|
121
|
+
normalized.includes("api base url")) {
|
|
122
|
+
return {
|
|
123
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.CONFIG_INVALID,
|
|
124
|
+
failureCategory: "config_error",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (normalized.includes("already exists")) {
|
|
128
|
+
return {
|
|
129
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.CONFIG_ALREADY_EXISTS,
|
|
130
|
+
failureCategory: "config_error",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (normalized.includes("missing nexalink api key")) {
|
|
134
|
+
return {
|
|
135
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.AUTH_KEY_MISSING,
|
|
136
|
+
failureCategory: "auth_error",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (normalized.includes("api key format") ||
|
|
140
|
+
normalized.includes("invalid api key")) {
|
|
141
|
+
return {
|
|
142
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.AUTH_KEY_INVALID,
|
|
143
|
+
failureCategory: "auth_error",
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (normalized.includes("baa execution") ||
|
|
147
|
+
normalized.includes("business associate")) {
|
|
148
|
+
return {
|
|
149
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.BAA_REQUIRED,
|
|
150
|
+
failureCategory: "baa_not_signed",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (normalized.includes("rate limit") || normalized.includes("quota")) {
|
|
154
|
+
return {
|
|
155
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.RATE_LIMIT_EXCEEDED,
|
|
156
|
+
failureCategory: "rate_limit_error",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (normalized.includes("timed out") || normalized.includes("timeout")) {
|
|
160
|
+
return {
|
|
161
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.NET_TIMEOUT,
|
|
162
|
+
failureCategory: "network_error",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (normalized.includes("could not reach")) {
|
|
166
|
+
return {
|
|
167
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.NET_UNREACHABLE,
|
|
168
|
+
failureCategory: "network_error",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (normalized.includes("model load")) {
|
|
172
|
+
return {
|
|
173
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.MODEL_LOAD_FAILED,
|
|
174
|
+
failureCategory: "model_error",
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (normalized.includes("model") || normalized.includes("inference")) {
|
|
178
|
+
return {
|
|
179
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.MODEL_INFERENCE_FAILED,
|
|
180
|
+
failureCategory: "model_error",
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (normalized.includes("unknown command")) {
|
|
184
|
+
return {
|
|
185
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.CLI_COMMAND_UNKNOWN,
|
|
186
|
+
failureCategory: "unknown",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (normalized.includes("cancelled")) {
|
|
190
|
+
return {
|
|
191
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.CLI_COMMAND_CANCELLED,
|
|
192
|
+
failureCategory: "unknown",
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.UNKNOWN_ERROR,
|
|
197
|
+
failureCategory: "unknown",
|
|
198
|
+
};
|
|
199
|
+
};
|
|
34
200
|
function parseArgs(argv) {
|
|
35
201
|
const [command = "help", ...rest] = argv;
|
|
36
202
|
const flags = {};
|
|
@@ -210,6 +376,12 @@ const signupUrls = (flags, io) => ({
|
|
|
210
376
|
io.env.CARECOMPASS_DEVELOPER_SIGNUP_URL ??
|
|
211
377
|
DEFAULT_DEVELOPER_SIGNUP_URL,
|
|
212
378
|
});
|
|
379
|
+
function resolveTrustedApiBaseUrl(value) {
|
|
380
|
+
const result = careCompassApiBaseUrlSchema.safeParse(value);
|
|
381
|
+
if (result.success)
|
|
382
|
+
return result.data;
|
|
383
|
+
throw new Error(result.error.issues.map((issue) => issue.message).join("\n"));
|
|
384
|
+
}
|
|
213
385
|
function rejectLegacyBaaInitFlags(flags, io) {
|
|
214
386
|
const legacyFlag = LEGACY_BAA_INIT_FLAGS.find((flag) => flag in flags);
|
|
215
387
|
if (!legacyFlag)
|
|
@@ -267,7 +439,7 @@ async function validateInitApiKey(apiBaseUrl, apiKey, accountType) {
|
|
|
267
439
|
}
|
|
268
440
|
catch (error) {
|
|
269
441
|
const message = error instanceof Error ? error.message : "Network request failed";
|
|
270
|
-
throw new
|
|
442
|
+
throw new CliTelemetryError(`Could not reach CareCompass authentication API at ${url}. ${message}`, classifyCliFailure(new Error(`Could not reach CareCompass authentication API. ${message}`)));
|
|
271
443
|
}
|
|
272
444
|
const text = await response.text();
|
|
273
445
|
let payload = {};
|
|
@@ -278,27 +450,33 @@ async function validateInitApiKey(apiBaseUrl, apiKey, accountType) {
|
|
|
278
450
|
payload = {};
|
|
279
451
|
}
|
|
280
452
|
if (!response.ok || payload.ok === false) {
|
|
453
|
+
const message = payload.error ?? "Invalid API key.";
|
|
454
|
+
const failure = classifyAuthFailure(response.status, message);
|
|
281
455
|
return {
|
|
282
456
|
ok: false,
|
|
283
|
-
message
|
|
457
|
+
message,
|
|
458
|
+
...failure,
|
|
284
459
|
};
|
|
285
460
|
}
|
|
286
461
|
const validatedType = typeof payload.accountType === "string"
|
|
287
462
|
? normalizeAccountType(payload.accountType)
|
|
288
463
|
: null;
|
|
289
464
|
if (!validatedType) {
|
|
465
|
+
const message = "Credential validation succeeded but did not return a recognized account type. Contact support@nexalink.care.";
|
|
290
466
|
return {
|
|
291
467
|
ok: false,
|
|
292
|
-
message
|
|
468
|
+
message,
|
|
469
|
+
...classifyAuthFailure(401, message),
|
|
293
470
|
};
|
|
294
471
|
}
|
|
295
472
|
return {
|
|
296
473
|
ok: true,
|
|
297
474
|
accountType: validatedType,
|
|
475
|
+
baaStatus: normalizeBaaStatus(payload.baaStatus),
|
|
298
476
|
setup: normalizeAuthSetup(payload.setup),
|
|
299
477
|
};
|
|
300
478
|
}
|
|
301
|
-
async function resolveInitApiKey(flags, io, apiBaseUrl) {
|
|
479
|
+
async function resolveInitApiKey(flags, io, apiBaseUrl, onAuthChecked = async () => undefined) {
|
|
302
480
|
const credentialsPath = resolveCredentialsPath(io.env);
|
|
303
481
|
let credentials = resolveApiKey(flagString(flags, "api-key"), io.env, credentialsPath);
|
|
304
482
|
const urls = signupUrls(flags, io);
|
|
@@ -316,6 +494,7 @@ async function resolveInitApiKey(flags, io, apiBaseUrl) {
|
|
|
316
494
|
const apiKey = useResolvedCredential
|
|
317
495
|
? credentials.apiKey
|
|
318
496
|
: await promptForSecret(io, "Enter API key: ");
|
|
497
|
+
const authTelemetry = authTelemetryForSource(credentials.source);
|
|
319
498
|
useResolvedCredential = false;
|
|
320
499
|
credentials = { apiKey: null, source: "missing" };
|
|
321
500
|
const normalizedApiKey = normalizeApiKey(apiKey);
|
|
@@ -324,11 +503,37 @@ async function resolveInitApiKey(flags, io, apiBaseUrl) {
|
|
|
324
503
|
continue;
|
|
325
504
|
}
|
|
326
505
|
io.stdout.write("Validating credentials...\n");
|
|
327
|
-
|
|
506
|
+
let validation;
|
|
507
|
+
try {
|
|
508
|
+
validation = await validateInitApiKey(apiBaseUrl, normalizedApiKey, accountType);
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
const failure = classifyCliFailure(error);
|
|
512
|
+
await onAuthChecked({
|
|
513
|
+
authResult: "failure",
|
|
514
|
+
baaStatus: "signed",
|
|
515
|
+
keySource: authTelemetry.keySource,
|
|
516
|
+
...failure,
|
|
517
|
+
});
|
|
518
|
+
throw error;
|
|
519
|
+
}
|
|
328
520
|
if (!validation.ok) {
|
|
521
|
+
await onAuthChecked({
|
|
522
|
+
authResult: "failure",
|
|
523
|
+
baaStatus: validation.baaStatus,
|
|
524
|
+
keySource: authTelemetry.keySource,
|
|
525
|
+
errorCode: validation.errorCode,
|
|
526
|
+
failureCategory: validation.failureCategory,
|
|
527
|
+
});
|
|
329
528
|
io.stdout.write(`${validation.message}\n`);
|
|
330
529
|
continue;
|
|
331
530
|
}
|
|
531
|
+
await onAuthChecked({
|
|
532
|
+
authResult: "success",
|
|
533
|
+
baaStatus: validation.baaStatus,
|
|
534
|
+
keySource: authTelemetry.keySource,
|
|
535
|
+
failureCategory: null,
|
|
536
|
+
});
|
|
332
537
|
if (validation.accountType !== accountType) {
|
|
333
538
|
if (validation.accountType === "enterprise") {
|
|
334
539
|
io.stdout.write(`This API key belongs to an enterprise account. ${enterpriseUnavailableMessage(urls.developer)}\n`);
|
|
@@ -340,7 +545,12 @@ async function resolveInitApiKey(flags, io, apiBaseUrl) {
|
|
|
340
545
|
writeStoredApiKey(normalizedApiKey, credentialsPath);
|
|
341
546
|
io.stdout.write(`Stored CareCompass credentials at ${credentialsPath}\n`);
|
|
342
547
|
io.stdout.write("Sign-in successful.\n");
|
|
343
|
-
return {
|
|
548
|
+
return {
|
|
549
|
+
apiKey: normalizedApiKey,
|
|
550
|
+
authMethod: authTelemetry.authMethod,
|
|
551
|
+
baaStatus: validation.baaStatus,
|
|
552
|
+
setup: validation.setup,
|
|
553
|
+
};
|
|
344
554
|
}
|
|
345
555
|
}
|
|
346
556
|
function resolvePromptFile(config, configPath) {
|
|
@@ -349,8 +559,23 @@ function resolvePromptFile(config, configPath) {
|
|
|
349
559
|
return config;
|
|
350
560
|
// Prompt files are resolved relative to the config file so generated kits work
|
|
351
561
|
// after the caller changes directories.
|
|
352
|
-
|
|
353
|
-
|
|
562
|
+
if (isAbsolute(promptFile)) {
|
|
563
|
+
throw new Error("Prompt file must stay inside the config directory.");
|
|
564
|
+
}
|
|
565
|
+
const configDir = realpathSync(resolve(dirname(configPath)));
|
|
566
|
+
const resolvedPath = resolve(configDir, promptFile);
|
|
567
|
+
let realPromptPath;
|
|
568
|
+
try {
|
|
569
|
+
realPromptPath = realpathSync(resolvedPath);
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
throw new Error(`CareCompass prompt file was not found: ${promptFile}`);
|
|
573
|
+
}
|
|
574
|
+
const relativePromptPath = relative(configDir, realPromptPath);
|
|
575
|
+
if (relativePromptPath.startsWith("..") || isAbsolute(relativePromptPath)) {
|
|
576
|
+
throw new Error("Prompt file must stay inside the config directory.");
|
|
577
|
+
}
|
|
578
|
+
const systemInstruction = readFileSync(realPromptPath, "utf8").trim();
|
|
354
579
|
const validation = validateCareCompassConfig({
|
|
355
580
|
...config,
|
|
356
581
|
prompt: {
|
|
@@ -562,13 +787,63 @@ export async function runCareCompassCli(argv = process.argv.slice(2), io = {
|
|
|
562
787
|
// Start with anonymous CLI telemetry, then rebuild it with payer context after
|
|
563
788
|
// a config has been generated or read.
|
|
564
789
|
let cliTelemetry = createCareCompassCliTelemetry(cliSessionId);
|
|
565
|
-
|
|
790
|
+
const sessionStartedAt = Date.now();
|
|
791
|
+
const rawCommand = argv[0] ?? "help";
|
|
792
|
+
const telemetryCommandName = sanitizeCommandName(rawCommand);
|
|
793
|
+
const commandStartedAt = Date.now();
|
|
794
|
+
let commandsAttempted = 0;
|
|
795
|
+
let commandsSucceeded = 0;
|
|
796
|
+
let commandsFailed = 0;
|
|
797
|
+
let activeCommand = rawCommand;
|
|
798
|
+
let authCheckedCaptured = false;
|
|
799
|
+
let sessionEndedCaptured = false;
|
|
800
|
+
const captureAuthChecked = async (telemetry) => {
|
|
801
|
+
if (authCheckedCaptured)
|
|
802
|
+
return;
|
|
803
|
+
authCheckedCaptured = true;
|
|
804
|
+
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_AUTH_CHECKED, {
|
|
805
|
+
authResult: telemetry.authResult,
|
|
806
|
+
failureCategory: telemetry.authResult === "success"
|
|
807
|
+
? null
|
|
808
|
+
: (telemetry.failureCategory ?? "unknown"),
|
|
809
|
+
...(telemetry.errorCode ? { errorCode: telemetry.errorCode } : {}),
|
|
810
|
+
baaStatus: telemetry.baaStatus,
|
|
811
|
+
keySource: telemetry.keySource,
|
|
812
|
+
});
|
|
813
|
+
};
|
|
814
|
+
const captureCommandCompleted = async () => {
|
|
815
|
+
commandsSucceeded += 1;
|
|
816
|
+
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_COMMAND_COMPLETED, {
|
|
817
|
+
commandName: telemetryCommandName,
|
|
818
|
+
commandDurationMs: Date.now() - commandStartedAt,
|
|
819
|
+
tokensUsed: null,
|
|
820
|
+
modelId: null,
|
|
821
|
+
});
|
|
822
|
+
};
|
|
823
|
+
const captureSessionEnded = async (exitReason) => {
|
|
824
|
+
if (sessionEndedCaptured)
|
|
825
|
+
return;
|
|
826
|
+
sessionEndedCaptured = true;
|
|
827
|
+
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_SESSION_ENDED, {
|
|
828
|
+
sessionDurationMs: Date.now() - sessionStartedAt,
|
|
829
|
+
commandsAttempted,
|
|
830
|
+
commandsSucceeded,
|
|
831
|
+
commandsFailed,
|
|
832
|
+
exitReason,
|
|
833
|
+
});
|
|
834
|
+
};
|
|
566
835
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_STARTED);
|
|
836
|
+
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_COMMAND_STARTED, {
|
|
837
|
+
commandName: telemetryCommandName,
|
|
838
|
+
commandArgsCount: Math.max(argv.length - 1, 0),
|
|
839
|
+
});
|
|
840
|
+
commandsAttempted += 1;
|
|
567
841
|
try {
|
|
568
842
|
const { command, flags, positionals } = parseArgs(argv);
|
|
569
843
|
activeCommand = command;
|
|
570
844
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
571
845
|
printHelp(io);
|
|
846
|
+
await captureCommandCompleted();
|
|
572
847
|
return 0;
|
|
573
848
|
}
|
|
574
849
|
if (command === "login") {
|
|
@@ -581,20 +856,23 @@ export async function runCareCompassCli(argv = process.argv.slice(2), io = {
|
|
|
581
856
|
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.AUTHENTICATION_FAILED,
|
|
582
857
|
failureCategory: "authentication",
|
|
583
858
|
});
|
|
859
|
+
commandsFailed += 1;
|
|
584
860
|
return 1;
|
|
585
861
|
}
|
|
586
862
|
const credentialsPath = resolveCredentialsPath(io.env);
|
|
587
863
|
writeStoredApiKey(apiKey, credentialsPath);
|
|
588
864
|
io.stdout.write(`Stored CareCompass credentials at ${credentialsPath}\n`);
|
|
589
865
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_LOGIN_SUCCEEDED);
|
|
866
|
+
await captureCommandCompleted();
|
|
590
867
|
return 0;
|
|
591
868
|
}
|
|
592
869
|
if (command === "init") {
|
|
870
|
+
const initStartedAt = Date.now();
|
|
593
871
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_INIT_STARTED);
|
|
594
872
|
rejectLegacyBaaInitFlags(flags, io);
|
|
595
873
|
const outputDir = flagString(flags, "output-dir") ?? DEFAULT_OUTPUT_DIR;
|
|
596
|
-
const apiBaseUrl = flagString(flags, "api-base-url") ?? DEFAULT_API_BASE_URL;
|
|
597
|
-
const initAuth = await resolveInitApiKey(flags, io, apiBaseUrl);
|
|
874
|
+
const apiBaseUrl = resolveTrustedApiBaseUrl(flagString(flags, "api-base-url") ?? DEFAULT_API_BASE_URL);
|
|
875
|
+
const initAuth = await resolveInitApiKey(flags, io, apiBaseUrl, captureAuthChecked);
|
|
598
876
|
const output = flagString(flags, "output") ??
|
|
599
877
|
join(outputDir, "carecompass.config.json");
|
|
600
878
|
const snippetOutput = flagString(flags, "snippet-output") ??
|
|
@@ -657,7 +935,13 @@ Step 5: Follow the generated setup guide
|
|
|
657
935
|
Next command:
|
|
658
936
|
npx @nexalink/carecompass-sdk validate --config ${output}
|
|
659
937
|
`);
|
|
938
|
+
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_INIT_COMPLETED, {
|
|
939
|
+
initDurationMs: Date.now() - initStartedAt,
|
|
940
|
+
authMethod: initAuth.authMethod,
|
|
941
|
+
baaStatus: initAuth.baaStatus,
|
|
942
|
+
});
|
|
660
943
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_SETUP_KIT_GENERATED);
|
|
944
|
+
await captureCommandCompleted();
|
|
661
945
|
return 0;
|
|
662
946
|
}
|
|
663
947
|
if (command === "validate") {
|
|
@@ -670,12 +954,14 @@ Next command:
|
|
|
670
954
|
}
|
|
671
955
|
io.stdout.write(`CareCompass config is valid for ${config.payerId}. Credentials loaded from ${credentials.source}.\n`);
|
|
672
956
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_VALIDATION_SUCCEEDED);
|
|
957
|
+
await captureCommandCompleted();
|
|
673
958
|
return 0;
|
|
674
959
|
}
|
|
675
960
|
catch (error) {
|
|
961
|
+
const failure = classifyCliFailure(error);
|
|
676
962
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_VALIDATION_FAILED, {
|
|
677
|
-
errorCode:
|
|
678
|
-
failureCategory:
|
|
963
|
+
errorCode: failure.errorCode,
|
|
964
|
+
failureCategory: failure.failureCategory,
|
|
679
965
|
});
|
|
680
966
|
throw error;
|
|
681
967
|
}
|
|
@@ -697,6 +983,7 @@ Next command:
|
|
|
697
983
|
io.stdout.write(`${snippet}\n`);
|
|
698
984
|
}
|
|
699
985
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_SNIPPET_GENERATED);
|
|
986
|
+
await captureCommandCompleted();
|
|
700
987
|
return 0;
|
|
701
988
|
}
|
|
702
989
|
if (command === "doctor") {
|
|
@@ -723,24 +1010,31 @@ Hosted API: ${hostedStatus}
|
|
|
723
1010
|
Status: ready for staging verification
|
|
724
1011
|
`);
|
|
725
1012
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_DOCTOR_COMPLETED);
|
|
1013
|
+
await captureCommandCompleted();
|
|
726
1014
|
return 0;
|
|
727
1015
|
}
|
|
728
1016
|
throw new Error(`Unknown command: ${command}`);
|
|
729
1017
|
}
|
|
730
1018
|
catch (error) {
|
|
1019
|
+
commandsFailed += 1;
|
|
1020
|
+
const failure = classifyCliFailure(error);
|
|
731
1021
|
if (activeCommand === "init") {
|
|
732
1022
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_INIT_FAILED, {
|
|
733
|
-
errorCode:
|
|
734
|
-
failureCategory:
|
|
1023
|
+
errorCode: failure.errorCode,
|
|
1024
|
+
failureCategory: failure.failureCategory,
|
|
735
1025
|
});
|
|
736
1026
|
}
|
|
737
1027
|
await cliTelemetry.captureAsync(CARECOMPASS_TELEMETRY_EVENTS.CLI_COMMAND_FAILED, {
|
|
738
|
-
errorCode:
|
|
739
|
-
failureCategory:
|
|
1028
|
+
errorCode: failure.errorCode,
|
|
1029
|
+
failureCategory: failure.failureCategory,
|
|
1030
|
+
commandName: telemetryCommandName,
|
|
740
1031
|
});
|
|
741
1032
|
io.stderr.write(`${error.message}\n`);
|
|
742
1033
|
return 1;
|
|
743
1034
|
}
|
|
1035
|
+
finally {
|
|
1036
|
+
await captureSessionEnded(commandsFailed > 0 ? "error" : "user_exit");
|
|
1037
|
+
}
|
|
744
1038
|
}
|
|
745
1039
|
const isMainModule = () => Boolean(process.argv[1]) &&
|
|
746
1040
|
realpathSync(fileURLToPath(import.meta.url)) ===
|
package/dist/snippet.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CareCompassConfig } from "./schema.js";
|
|
2
2
|
export type CareCompassSnippetMode = "bundler" | "cdn";
|
|
3
3
|
export declare function generateCareCompassIframeSnippet(config: CareCompassConfig, options?: {
|
|
4
4
|
src?: string;
|
package/dist/snippet.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { careCompassThemeSchema } from "./schema.js";
|
|
1
2
|
const SCRIPT_JSON_ESCAPES = {
|
|
2
3
|
"<": "\\u003c",
|
|
3
4
|
">": "\\u003e",
|
|
@@ -16,12 +17,14 @@ const serializeScriptJson = (value) => JSON.stringify(value, null, 2).replace(/[
|
|
|
16
17
|
export function generateCareCompassIframeSnippet(config, options = {}) {
|
|
17
18
|
const src = options.src ?? "/carecompass/carecompass-embed.html";
|
|
18
19
|
const title = `${config.branding.displayName} CareCompass`;
|
|
20
|
+
const theme = careCompassThemeSchema.safeParse(config.theme);
|
|
21
|
+
const borderRadius = theme.success ? theme.data.borderRadius : "8px";
|
|
19
22
|
// The iframe form is intentionally static so it can be pasted into CMS pages
|
|
20
23
|
// or server-rendered apps without needing a bundler.
|
|
21
24
|
return `<iframe
|
|
22
25
|
title="${escapeHtmlAttribute(title)}"
|
|
23
26
|
src="${escapeHtmlAttribute(src)}"
|
|
24
|
-
style="width: 100%; min-height: 640px; border: 0; border-radius: ${
|
|
27
|
+
style="width: 100%; min-height: 640px; border: 0; border-radius: ${borderRadius};"
|
|
25
28
|
loading="lazy"
|
|
26
29
|
></iframe>`;
|
|
27
30
|
}
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ export declare const CARECOMPASS_TELEMETRY_EVENTS: {
|
|
|
21
21
|
readonly CLI_LOGIN_FAILED: "carecompass_cli_login_failed";
|
|
22
22
|
readonly CLI_INIT_STARTED: "carecompass_cli_init_started";
|
|
23
23
|
readonly CLI_INIT_FAILED: "carecompass_cli_init_failed";
|
|
24
|
+
readonly CLI_INIT_COMPLETED: "carecompass_cli_init_completed";
|
|
25
|
+
readonly CLI_COMMAND_STARTED: "carecompass_cli_command_started";
|
|
26
|
+
readonly CLI_COMMAND_COMPLETED: "carecompass_cli_command_completed";
|
|
27
|
+
readonly CLI_SESSION_ENDED: "carecompass_cli_session_ended";
|
|
28
|
+
readonly CLI_AUTH_CHECKED: "carecompass_cli_auth_checked";
|
|
24
29
|
readonly CLI_SETUP_KIT_GENERATED: "carecompass_cli_setup_kit_generated";
|
|
25
30
|
readonly CLI_VALIDATION_SUCCEEDED: "carecompass_cli_validation_succeeded";
|
|
26
31
|
readonly CLI_VALIDATION_FAILED: "carecompass_cli_validation_failed";
|
|
@@ -41,14 +46,50 @@ export declare const CARECOMPASS_TELEMETRY_ERROR_CODES: {
|
|
|
41
46
|
readonly RATE_LIMITED: "rate_limited";
|
|
42
47
|
readonly RENDER_FAILED: "render_failed";
|
|
43
48
|
readonly CLI_COMMAND_FAILED: "cli_command_failed";
|
|
49
|
+
readonly AUTH_KEY_MISSING: "AUTH_KEY_MISSING";
|
|
50
|
+
readonly AUTH_KEY_INVALID: "AUTH_KEY_INVALID";
|
|
51
|
+
readonly AUTH_KEY_EXPIRED: "AUTH_KEY_EXPIRED";
|
|
52
|
+
readonly BAA_REQUIRED: "BAA_REQUIRED";
|
|
53
|
+
readonly NET_TIMEOUT: "NET_TIMEOUT";
|
|
54
|
+
readonly NET_UNREACHABLE: "NET_UNREACHABLE";
|
|
55
|
+
readonly CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND";
|
|
56
|
+
readonly CONFIG_INVALID: "CONFIG_INVALID";
|
|
57
|
+
readonly CONFIG_ALREADY_EXISTS: "CONFIG_ALREADY_EXISTS";
|
|
58
|
+
readonly PERMISSION_DENIED: "PERMISSION_DENIED";
|
|
59
|
+
readonly MODEL_LOAD_FAILED: "MODEL_LOAD_FAILED";
|
|
60
|
+
readonly MODEL_INFERENCE_FAILED: "MODEL_INFERENCE_FAILED";
|
|
61
|
+
readonly RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED";
|
|
62
|
+
readonly CLI_COMMAND_CANCELLED: "CLI_COMMAND_CANCELLED";
|
|
63
|
+
readonly CLI_COMMAND_UNKNOWN: "CLI_COMMAND_UNKNOWN";
|
|
64
|
+
readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
|
|
44
65
|
};
|
|
45
66
|
export type CareCompassTelemetryErrorCode = (typeof CARECOMPASS_TELEMETRY_ERROR_CODES)[keyof typeof CARECOMPASS_TELEMETRY_ERROR_CODES];
|
|
46
|
-
export type CareCompassTelemetryFailureCategory = "authentication" | "configuration" | "inference" | "network" | "rate_limit" | "rendering" | "timeout" | "unknown";
|
|
67
|
+
export type CareCompassTelemetryFailureCategory = "authentication" | "auth_error" | "baa_not_signed" | "configuration" | "config_error" | "inference" | "model_error" | "network" | "network_error" | "permission_error" | "rate_limit" | "rate_limit_error" | "rendering" | "timeout" | "unknown";
|
|
68
|
+
export type CareCompassTelemetryBaaStatus = "signed" | "pending" | "not_required";
|
|
69
|
+
export type CareCompassTelemetryAuthMethod = "api_key" | "env_var" | "config_file";
|
|
70
|
+
export type CareCompassTelemetryKeySource = "env_var" | "config_file" | "cli_flag";
|
|
71
|
+
export type CareCompassTelemetryExitReason = "user_exit" | "error" | "timeout" | "signal";
|
|
72
|
+
export type CareCompassTelemetryAuthResult = "success" | "failure";
|
|
47
73
|
export type CareCompassTelemetryDurationBucket = "under_250_ms" | "250_to_999_ms" | "1_to_4_999_ms" | "5_to_29_999_ms" | "30_s_or_more";
|
|
48
74
|
export type CareCompassTelemetryCaptureOptions = {
|
|
49
75
|
durationMs?: number;
|
|
50
76
|
errorCode?: CareCompassTelemetryErrorCode;
|
|
51
|
-
failureCategory?: CareCompassTelemetryFailureCategory;
|
|
77
|
+
failureCategory?: CareCompassTelemetryFailureCategory | null;
|
|
78
|
+
initDurationMs?: number;
|
|
79
|
+
authMethod?: CareCompassTelemetryAuthMethod;
|
|
80
|
+
baaStatus?: CareCompassTelemetryBaaStatus;
|
|
81
|
+
commandName?: string;
|
|
82
|
+
commandArgsCount?: number;
|
|
83
|
+
commandDurationMs?: number;
|
|
84
|
+
tokensUsed?: number | null;
|
|
85
|
+
modelId?: string | null;
|
|
86
|
+
sessionDurationMs?: number;
|
|
87
|
+
commandsAttempted?: number;
|
|
88
|
+
commandsSucceeded?: number;
|
|
89
|
+
commandsFailed?: number;
|
|
90
|
+
exitReason?: CareCompassTelemetryExitReason;
|
|
91
|
+
authResult?: CareCompassTelemetryAuthResult;
|
|
92
|
+
keySource?: CareCompassTelemetryKeySource;
|
|
52
93
|
};
|
|
53
94
|
type Fetcher = typeof fetch;
|
|
54
95
|
export type CareCompassTelemetry = {
|
package/dist/telemetry.js
CHANGED
|
@@ -21,6 +21,11 @@ export const CARECOMPASS_TELEMETRY_EVENTS = {
|
|
|
21
21
|
CLI_LOGIN_FAILED: "carecompass_cli_login_failed",
|
|
22
22
|
CLI_INIT_STARTED: "carecompass_cli_init_started",
|
|
23
23
|
CLI_INIT_FAILED: "carecompass_cli_init_failed",
|
|
24
|
+
CLI_INIT_COMPLETED: "carecompass_cli_init_completed",
|
|
25
|
+
CLI_COMMAND_STARTED: "carecompass_cli_command_started",
|
|
26
|
+
CLI_COMMAND_COMPLETED: "carecompass_cli_command_completed",
|
|
27
|
+
CLI_SESSION_ENDED: "carecompass_cli_session_ended",
|
|
28
|
+
CLI_AUTH_CHECKED: "carecompass_cli_auth_checked",
|
|
24
29
|
CLI_SETUP_KIT_GENERATED: "carecompass_cli_setup_kit_generated",
|
|
25
30
|
CLI_VALIDATION_SUCCEEDED: "carecompass_cli_validation_succeeded",
|
|
26
31
|
CLI_VALIDATION_FAILED: "carecompass_cli_validation_failed",
|
|
@@ -40,6 +45,22 @@ export const CARECOMPASS_TELEMETRY_ERROR_CODES = {
|
|
|
40
45
|
RATE_LIMITED: "rate_limited",
|
|
41
46
|
RENDER_FAILED: "render_failed",
|
|
42
47
|
CLI_COMMAND_FAILED: "cli_command_failed",
|
|
48
|
+
AUTH_KEY_MISSING: "AUTH_KEY_MISSING",
|
|
49
|
+
AUTH_KEY_INVALID: "AUTH_KEY_INVALID",
|
|
50
|
+
AUTH_KEY_EXPIRED: "AUTH_KEY_EXPIRED",
|
|
51
|
+
BAA_REQUIRED: "BAA_REQUIRED",
|
|
52
|
+
NET_TIMEOUT: "NET_TIMEOUT",
|
|
53
|
+
NET_UNREACHABLE: "NET_UNREACHABLE",
|
|
54
|
+
CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND",
|
|
55
|
+
CONFIG_INVALID: "CONFIG_INVALID",
|
|
56
|
+
CONFIG_ALREADY_EXISTS: "CONFIG_ALREADY_EXISTS",
|
|
57
|
+
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
58
|
+
MODEL_LOAD_FAILED: "MODEL_LOAD_FAILED",
|
|
59
|
+
MODEL_INFERENCE_FAILED: "MODEL_INFERENCE_FAILED",
|
|
60
|
+
RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
|
|
61
|
+
CLI_COMMAND_CANCELLED: "CLI_COMMAND_CANCELLED",
|
|
62
|
+
CLI_COMMAND_UNKNOWN: "CLI_COMMAND_UNKNOWN",
|
|
63
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
43
64
|
};
|
|
44
65
|
const DEPLOYMENT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{2,127}$/;
|
|
45
66
|
const POSTHOG_PROJECT_TOKEN_RE = /^phc_[A-Za-z0-9_-]{8,}$/;
|
|
@@ -86,6 +107,8 @@ export const careCompassTelemetryDurationBucket = (durationMs) => {
|
|
|
86
107
|
return "5_to_29_999_ms";
|
|
87
108
|
return "30_s_or_more";
|
|
88
109
|
};
|
|
110
|
+
const integerMs = (value) => Math.max(0, Math.round(value));
|
|
111
|
+
const integerCount = (value) => Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
89
112
|
function createTelemetry(context, options = {}) {
|
|
90
113
|
const projectToken = CARECOMPASS_DEFAULT_POSTHOG_PROJECT_TOKEN;
|
|
91
114
|
// Invalid identifiers disable telemetry instead of producing malformed events
|
|
@@ -120,12 +143,71 @@ function createTelemetry(context, options = {}) {
|
|
|
120
143
|
: {
|
|
121
144
|
duration_bucket: careCompassTelemetryDurationBucket(captureOptions.durationMs),
|
|
122
145
|
}),
|
|
123
|
-
...(captureOptions.failureCategory
|
|
124
|
-
? {
|
|
125
|
-
: {}),
|
|
146
|
+
...(captureOptions.failureCategory === undefined
|
|
147
|
+
? {}
|
|
148
|
+
: { failure_category: captureOptions.failureCategory }),
|
|
126
149
|
...(captureOptions.errorCode
|
|
127
150
|
? { error_code: captureOptions.errorCode }
|
|
128
151
|
: {}),
|
|
152
|
+
...(captureOptions.initDurationMs === undefined
|
|
153
|
+
? {}
|
|
154
|
+
: { init_duration_ms: integerMs(captureOptions.initDurationMs) }),
|
|
155
|
+
...(captureOptions.authMethod
|
|
156
|
+
? { auth_method: captureOptions.authMethod }
|
|
157
|
+
: {}),
|
|
158
|
+
...(captureOptions.baaStatus
|
|
159
|
+
? { baa_status: captureOptions.baaStatus }
|
|
160
|
+
: {}),
|
|
161
|
+
...(captureOptions.commandName
|
|
162
|
+
? { command_name: captureOptions.commandName }
|
|
163
|
+
: {}),
|
|
164
|
+
...(captureOptions.commandArgsCount === undefined
|
|
165
|
+
? {}
|
|
166
|
+
: {
|
|
167
|
+
command_args_count: integerCount(captureOptions.commandArgsCount),
|
|
168
|
+
}),
|
|
169
|
+
...(captureOptions.commandDurationMs === undefined
|
|
170
|
+
? {}
|
|
171
|
+
: {
|
|
172
|
+
command_duration_ms: integerMs(captureOptions.commandDurationMs),
|
|
173
|
+
}),
|
|
174
|
+
...(captureOptions.tokensUsed === undefined
|
|
175
|
+
? {}
|
|
176
|
+
: {
|
|
177
|
+
tokens_used: captureOptions.tokensUsed === null
|
|
178
|
+
? null
|
|
179
|
+
: integerCount(captureOptions.tokensUsed),
|
|
180
|
+
}),
|
|
181
|
+
...(captureOptions.modelId === undefined
|
|
182
|
+
? {}
|
|
183
|
+
: { model_id: captureOptions.modelId }),
|
|
184
|
+
...(captureOptions.sessionDurationMs === undefined
|
|
185
|
+
? {}
|
|
186
|
+
: {
|
|
187
|
+
session_duration_ms: integerMs(captureOptions.sessionDurationMs),
|
|
188
|
+
}),
|
|
189
|
+
...(captureOptions.commandsAttempted === undefined
|
|
190
|
+
? {}
|
|
191
|
+
: {
|
|
192
|
+
commands_attempted: integerCount(captureOptions.commandsAttempted),
|
|
193
|
+
}),
|
|
194
|
+
...(captureOptions.commandsSucceeded === undefined
|
|
195
|
+
? {}
|
|
196
|
+
: {
|
|
197
|
+
commands_succeeded: integerCount(captureOptions.commandsSucceeded),
|
|
198
|
+
}),
|
|
199
|
+
...(captureOptions.commandsFailed === undefined
|
|
200
|
+
? {}
|
|
201
|
+
: { commands_failed: integerCount(captureOptions.commandsFailed) }),
|
|
202
|
+
...(captureOptions.exitReason
|
|
203
|
+
? { exit_reason: captureOptions.exitReason }
|
|
204
|
+
: {}),
|
|
205
|
+
...(captureOptions.authResult
|
|
206
|
+
? { auth_result: captureOptions.authResult }
|
|
207
|
+
: {}),
|
|
208
|
+
...(captureOptions.keySource
|
|
209
|
+
? { key_source: captureOptions.keySource }
|
|
210
|
+
: {}),
|
|
129
211
|
};
|
|
130
212
|
const controller = new AbortController();
|
|
131
213
|
const timeout = setTimeout(() => controller.abort(), TELEMETRY_CAPTURE_TIMEOUT_MS);
|
package/dist/version.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const CARECOMPASS_SDK_VERSION = "0.
|
|
1
|
+
export const CARECOMPASS_SDK_VERSION = "0.3.0-beta.15"; // x-release-please-version
|
|
2
2
|
export const CARECOMPASS_COMPONENT_VERSION = "1.0.0";
|
|
3
3
|
export const CARECOMPASS_TELEMETRY_CONTRACT_VERSION = "1.0.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexalink/carecompass-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-beta.15",
|
|
4
4
|
"description": "Embeddable CareCompass SDK and CLI for healthcare ecosystem integrations with Nexalink onboarding, BAA coverage, and API keys.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "bun@1.3.9",
|
|
@@ -64,13 +64,13 @@
|
|
|
64
64
|
"react-dom": "^18.3.0 || ^19.0.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
+
"@eslint/js": "^9.39.4",
|
|
67
68
|
"@testing-library/jest-dom": "^6.9.1",
|
|
68
69
|
"@testing-library/react": "^16.3.2",
|
|
69
70
|
"@types/jest": "^30.0.0",
|
|
70
|
-
"@types/node": "^
|
|
71
|
+
"@types/node": "^25.9.3",
|
|
71
72
|
"@types/react": "^19.2.15",
|
|
72
73
|
"@types/react-dom": "^19.2.3",
|
|
73
|
-
"@eslint/js": "^9.39.4",
|
|
74
74
|
"eslint": "^9.39.4",
|
|
75
75
|
"globals": "^16.5.0",
|
|
76
76
|
"jest": "^30.4.2",
|
|
@@ -79,8 +79,11 @@
|
|
|
79
79
|
"react": "^19.2.6",
|
|
80
80
|
"react-dom": "^19.2.6",
|
|
81
81
|
"ts-jest": "^29.4.1",
|
|
82
|
-
"typescript
|
|
83
|
-
"typescript": "^
|
|
82
|
+
"typescript": "^5.9.3",
|
|
83
|
+
"typescript-eslint": "^8.48.1"
|
|
84
|
+
},
|
|
85
|
+
"overrides": {
|
|
86
|
+
"js-yaml": "4.2.0"
|
|
84
87
|
},
|
|
85
88
|
"publishConfig": {
|
|
86
89
|
"access": "public",
|