@fgv/ts-app-shell 5.1.0-15 → 5.1.0-17

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.
@@ -19,6 +19,13 @@
19
19
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
20
  * SOFTWARE.
21
21
  */
22
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
23
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
24
+ var m = o[Symbol.asyncIterator], i;
25
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
26
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
27
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
28
+ };
22
29
  /**
23
30
  * Generic AI assist hook — parameterized by settings and keystore, no app-specific context.
24
31
  *
@@ -207,13 +214,196 @@ export function useAiAssist(params) {
207
214
  return fail(`AI response validation failed after ${maxAttempts} attempts: ${entityResult.message}`);
208
215
  }
209
216
  }
210
- // Unreachable, but TypeScript needs it
217
+ /* c8 ignore next 2 - unreachable: every loop iteration returns; TypeScript needs the trailing return */
211
218
  return fail('AI generation failed');
212
219
  }
213
220
  finally {
214
221
  setIsWorking(false);
215
222
  }
216
223
  }, [settings, keyStore, logger]);
217
- return { actions, isWorking, copyPrompt, generateDirect };
224
+ const generateImages = useCallback(async (provider, params, signal) => {
225
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
226
+ if (!providerConfig) {
227
+ return fail(`Provider "${provider}" not configured`);
228
+ }
229
+ const descriptorResult = AiAssist.getProviderDescriptor(provider);
230
+ if (descriptorResult.isFailure()) {
231
+ return fail(descriptorResult.message);
232
+ }
233
+ const descriptor = descriptorResult.value;
234
+ if (descriptor.imageApiFormat === undefined) {
235
+ return fail(`Provider "${provider}" does not support image generation`);
236
+ }
237
+ if (!providerConfig.secretName) {
238
+ return fail(`Provider "${provider}" has no secret name configured`);
239
+ }
240
+ if (!keyStore) {
241
+ return fail('No keystore available');
242
+ }
243
+ const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
244
+ if (apiKeyResult.isFailure()) {
245
+ return fail(`Failed to get API key: ${apiKeyResult.message}`);
246
+ }
247
+ setIsWorking(true);
248
+ try {
249
+ const requestParams = {
250
+ descriptor,
251
+ apiKey: apiKeyResult.value,
252
+ params,
253
+ modelOverride: providerConfig.model,
254
+ logger,
255
+ signal
256
+ };
257
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
258
+ const result = useProxy
259
+ ? await AiAssist.callProxiedImageGeneration(settings.proxyUrl, requestParams)
260
+ : await AiAssist.callProviderImageGeneration(requestParams);
261
+ if (result.isFailure()) {
262
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI image generation failed: ${result.message}`);
263
+ }
264
+ return result;
265
+ }
266
+ finally {
267
+ setIsWorking(false);
268
+ }
269
+ }, [settings, keyStore, logger]);
270
+ const listModels = useCallback(async (provider, capability, signal) => {
271
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
272
+ if (!providerConfig) {
273
+ return fail(`Provider "${provider}" not configured`);
274
+ }
275
+ const descriptorResult = AiAssist.getProviderDescriptor(provider);
276
+ if (descriptorResult.isFailure()) {
277
+ return fail(descriptorResult.message);
278
+ }
279
+ const descriptor = descriptorResult.value;
280
+ if (!providerConfig.secretName) {
281
+ return fail(`Provider "${provider}" has no secret name configured`);
282
+ }
283
+ if (!keyStore) {
284
+ return fail('No keystore available');
285
+ }
286
+ const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
287
+ if (apiKeyResult.isFailure()) {
288
+ return fail(`Failed to get API key: ${apiKeyResult.message}`);
289
+ }
290
+ const requestParams = Object.assign(Object.assign({ descriptor, apiKey: apiKeyResult.value }, (capability !== undefined ? { capability } : {})), { logger,
291
+ signal });
292
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
293
+ const result = useProxy
294
+ ? await AiAssist.callProxiedListModels(settings.proxyUrl, requestParams)
295
+ : await AiAssist.callProviderListModels(requestParams);
296
+ if (result.isFailure()) {
297
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI list-models failed: ${result.message}`);
298
+ }
299
+ return result;
300
+ }, [settings, keyStore, logger]);
301
+ const streamDirect = useCallback(async (provider, prompt, onEvent, options) => {
302
+ var _a, e_1, _b, _c;
303
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
304
+ if (!providerConfig) {
305
+ return fail(`Provider "${provider}" not configured`);
306
+ }
307
+ const descriptorResult = AiAssist.getProviderDescriptor(provider);
308
+ if (descriptorResult.isFailure()) {
309
+ return fail(descriptorResult.message);
310
+ }
311
+ const descriptor = descriptorResult.value;
312
+ if (!providerConfig.secretName) {
313
+ return fail(`Provider "${provider}" has no secret name configured`);
314
+ }
315
+ if (!keyStore) {
316
+ return fail('No keystore available');
317
+ }
318
+ const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
319
+ if (apiKeyResult.isFailure()) {
320
+ return fail(`Failed to get API key: ${apiKeyResult.message}`);
321
+ }
322
+ const effectiveTools = AiAssist.resolveEffectiveTools(descriptor, providerConfig.tools, options === null || options === void 0 ? void 0 : options.tools);
323
+ const requestParams = {
324
+ descriptor,
325
+ apiKey: apiKeyResult.value,
326
+ prompt,
327
+ messagesBefore: options === null || options === void 0 ? void 0 : options.messagesBefore,
328
+ modelOverride: providerConfig.model,
329
+ logger,
330
+ tools: effectiveTools.length > 0 ? effectiveTools : undefined,
331
+ signal: options === null || options === void 0 ? void 0 : options.signal
332
+ };
333
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.streamingCorsRestricted);
334
+ const openResult = useProxy
335
+ ? await AiAssist.callProxiedCompletionStream(settings.proxyUrl, requestParams)
336
+ : await AiAssist.callProviderCompletionStream(requestParams);
337
+ if (openResult.isFailure()) {
338
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming failed to start: ${openResult.message}`);
339
+ return fail(openResult.message);
340
+ }
341
+ setIsWorking(true);
342
+ let fullText = '';
343
+ let truncated = false;
344
+ let terminalError;
345
+ let receivedTerminalEvent = false;
346
+ try {
347
+ try {
348
+ for (var _d = true, _e = __asyncValues(openResult.value), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
349
+ _c = _f.value;
350
+ _d = false;
351
+ const event = _c;
352
+ try {
353
+ onEvent(event);
354
+ }
355
+ catch (err) {
356
+ // Consumer callback threw (e.g. setState-after-unmount). Convert
357
+ // into a terminal failure so streamDirect always resolves to a
358
+ // Result instead of rejecting and breaking its contract.
359
+ terminalError = `AI stream event handler failed: ${err instanceof Error ? err.message : String(err)}`;
360
+ receivedTerminalEvent = true;
361
+ break;
362
+ }
363
+ if (event.type === 'text-delta') {
364
+ fullText += event.delta;
365
+ }
366
+ else if (event.type === 'done') {
367
+ fullText = event.fullText;
368
+ truncated = event.truncated;
369
+ receivedTerminalEvent = true;
370
+ }
371
+ else if (event.type === 'error') {
372
+ terminalError = event.message;
373
+ receivedTerminalEvent = true;
374
+ }
375
+ }
376
+ }
377
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
378
+ finally {
379
+ try {
380
+ if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
381
+ }
382
+ finally { if (e_1) throw e_1.error; }
383
+ }
384
+ }
385
+ finally {
386
+ setIsWorking(false);
387
+ }
388
+ if (terminalError !== undefined) {
389
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming ended in error: ${terminalError}`);
390
+ return fail(terminalError);
391
+ }
392
+ if (!receivedTerminalEvent) {
393
+ const message = 'AI stream ended without a terminal done or error event';
394
+ logger === null || logger === void 0 ? void 0 : logger.error(message);
395
+ return fail(message);
396
+ }
397
+ return succeed({ fullText, truncated });
398
+ }, [settings, keyStore, logger]);
399
+ return {
400
+ actions,
401
+ isWorking,
402
+ copyPrompt,
403
+ generateDirect,
404
+ generateImages,
405
+ listModels,
406
+ streamDirect
407
+ };
218
408
  }
219
409
  //# sourceMappingURL=useAiAssist.js.map
@@ -57,6 +57,32 @@ export interface IUseAiAssistResult {
57
57
  * @returns Success with the validated entity, or failure.
58
58
  */
59
59
  readonly generateDirect: <TEntity>(provider: AiAssist.AiProviderId, prompt: AiAssist.AiPrompt, convert: (from: unknown) => Result<TEntity>, tools?: ReadonlyArray<AiAssist.AiServerToolConfig>) => Promise<Result<IAiAssistResult<TEntity>>>;
60
+ /**
61
+ * Execute an image-generation action: calls the provider's image API and
62
+ * returns the generated images.
63
+ * @returns Success with the image generation response, or failure.
64
+ */
65
+ readonly generateImages: (provider: AiAssist.AiProviderId, params: AiAssist.IAiImageGenerationParams, signal?: AbortSignal) => Promise<Result<AiAssist.IAiImageGenerationResponse>>;
66
+ /**
67
+ * List the models available from a provider, optionally filtered by capability.
68
+ * @returns Success with the resolved model list, or failure (no silent fallback).
69
+ */
70
+ readonly listModels: (provider: AiAssist.AiProviderId, capability?: AiAssist.AiModelCapability, signal?: AbortSignal) => Promise<Result<ReadonlyArray<AiAssist.IAiModelInfo>>>;
71
+ /**
72
+ * Stream a chat completion from the provider. The `onEvent` callback fires
73
+ * for every event (text deltas, tool progress, errors); the returned
74
+ * promise resolves with the aggregated final text and truncation flag on
75
+ * success, or with `Result.fail` when the stream couldn't be started or
76
+ * ended in a terminal error event.
77
+ */
78
+ readonly streamDirect: (provider: AiAssist.AiProviderId, prompt: AiAssist.AiPrompt, onEvent: (event: AiAssist.IAiStreamEvent) => void, options?: {
79
+ readonly tools?: ReadonlyArray<AiAssist.AiServerToolConfig>;
80
+ readonly messagesBefore?: ReadonlyArray<AiAssist.IChatMessage>;
81
+ readonly signal?: AbortSignal;
82
+ }) => Promise<Result<{
83
+ readonly fullText: string;
84
+ readonly truncated: boolean;
85
+ }>>;
60
86
  }
61
87
  /**
62
88
  * Checks whether a parsed AI response is an error object (with an "error" field)
@@ -20,6 +20,13 @@
20
20
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  * SOFTWARE.
22
22
  */
23
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
24
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
25
+ var m = o[Symbol.asyncIterator], i;
26
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
27
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
28
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
29
+ };
23
30
  Object.defineProperty(exports, "__esModule", { value: true });
24
31
  exports.checkForAiErrorObject = checkForAiErrorObject;
25
32
  exports.useAiAssist = useAiAssist;
@@ -211,13 +218,196 @@ function useAiAssist(params) {
211
218
  return (0, ts_utils_1.fail)(`AI response validation failed after ${maxAttempts} attempts: ${entityResult.message}`);
212
219
  }
213
220
  }
214
- // Unreachable, but TypeScript needs it
221
+ /* c8 ignore next 2 - unreachable: every loop iteration returns; TypeScript needs the trailing return */
215
222
  return (0, ts_utils_1.fail)('AI generation failed');
216
223
  }
217
224
  finally {
218
225
  setIsWorking(false);
219
226
  }
220
227
  }, [settings, keyStore, logger]);
221
- return { actions, isWorking, copyPrompt, generateDirect };
228
+ const generateImages = (0, react_1.useCallback)(async (provider, params, signal) => {
229
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
230
+ if (!providerConfig) {
231
+ return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
232
+ }
233
+ const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
234
+ if (descriptorResult.isFailure()) {
235
+ return (0, ts_utils_1.fail)(descriptorResult.message);
236
+ }
237
+ const descriptor = descriptorResult.value;
238
+ if (descriptor.imageApiFormat === undefined) {
239
+ return (0, ts_utils_1.fail)(`Provider "${provider}" does not support image generation`);
240
+ }
241
+ if (!providerConfig.secretName) {
242
+ return (0, ts_utils_1.fail)(`Provider "${provider}" has no secret name configured`);
243
+ }
244
+ if (!keyStore) {
245
+ return (0, ts_utils_1.fail)('No keystore available');
246
+ }
247
+ const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
248
+ if (apiKeyResult.isFailure()) {
249
+ return (0, ts_utils_1.fail)(`Failed to get API key: ${apiKeyResult.message}`);
250
+ }
251
+ setIsWorking(true);
252
+ try {
253
+ const requestParams = {
254
+ descriptor,
255
+ apiKey: apiKeyResult.value,
256
+ params,
257
+ modelOverride: providerConfig.model,
258
+ logger,
259
+ signal
260
+ };
261
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
262
+ const result = useProxy
263
+ ? await ts_extras_1.AiAssist.callProxiedImageGeneration(settings.proxyUrl, requestParams)
264
+ : await ts_extras_1.AiAssist.callProviderImageGeneration(requestParams);
265
+ if (result.isFailure()) {
266
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI image generation failed: ${result.message}`);
267
+ }
268
+ return result;
269
+ }
270
+ finally {
271
+ setIsWorking(false);
272
+ }
273
+ }, [settings, keyStore, logger]);
274
+ const listModels = (0, react_1.useCallback)(async (provider, capability, signal) => {
275
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
276
+ if (!providerConfig) {
277
+ return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
278
+ }
279
+ const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
280
+ if (descriptorResult.isFailure()) {
281
+ return (0, ts_utils_1.fail)(descriptorResult.message);
282
+ }
283
+ const descriptor = descriptorResult.value;
284
+ if (!providerConfig.secretName) {
285
+ return (0, ts_utils_1.fail)(`Provider "${provider}" has no secret name configured`);
286
+ }
287
+ if (!keyStore) {
288
+ return (0, ts_utils_1.fail)('No keystore available');
289
+ }
290
+ const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
291
+ if (apiKeyResult.isFailure()) {
292
+ return (0, ts_utils_1.fail)(`Failed to get API key: ${apiKeyResult.message}`);
293
+ }
294
+ const requestParams = Object.assign(Object.assign({ descriptor, apiKey: apiKeyResult.value }, (capability !== undefined ? { capability } : {})), { logger,
295
+ signal });
296
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.corsRestricted);
297
+ const result = useProxy
298
+ ? await ts_extras_1.AiAssist.callProxiedListModels(settings.proxyUrl, requestParams)
299
+ : await ts_extras_1.AiAssist.callProviderListModels(requestParams);
300
+ if (result.isFailure()) {
301
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI list-models failed: ${result.message}`);
302
+ }
303
+ return result;
304
+ }, [settings, keyStore, logger]);
305
+ const streamDirect = (0, react_1.useCallback)(async (provider, prompt, onEvent, options) => {
306
+ var _a, e_1, _b, _c;
307
+ const providerConfig = settings === null || settings === void 0 ? void 0 : settings.providers.find((p) => p.provider === provider);
308
+ if (!providerConfig) {
309
+ return (0, ts_utils_1.fail)(`Provider "${provider}" not configured`);
310
+ }
311
+ const descriptorResult = ts_extras_1.AiAssist.getProviderDescriptor(provider);
312
+ if (descriptorResult.isFailure()) {
313
+ return (0, ts_utils_1.fail)(descriptorResult.message);
314
+ }
315
+ const descriptor = descriptorResult.value;
316
+ if (!providerConfig.secretName) {
317
+ return (0, ts_utils_1.fail)(`Provider "${provider}" has no secret name configured`);
318
+ }
319
+ if (!keyStore) {
320
+ return (0, ts_utils_1.fail)('No keystore available');
321
+ }
322
+ const apiKeyResult = keyStore.getApiKey(providerConfig.secretName);
323
+ if (apiKeyResult.isFailure()) {
324
+ return (0, ts_utils_1.fail)(`Failed to get API key: ${apiKeyResult.message}`);
325
+ }
326
+ const effectiveTools = ts_extras_1.AiAssist.resolveEffectiveTools(descriptor, providerConfig.tools, options === null || options === void 0 ? void 0 : options.tools);
327
+ const requestParams = {
328
+ descriptor,
329
+ apiKey: apiKeyResult.value,
330
+ prompt,
331
+ messagesBefore: options === null || options === void 0 ? void 0 : options.messagesBefore,
332
+ modelOverride: providerConfig.model,
333
+ logger,
334
+ tools: effectiveTools.length > 0 ? effectiveTools : undefined,
335
+ signal: options === null || options === void 0 ? void 0 : options.signal
336
+ };
337
+ const useProxy = !!(settings === null || settings === void 0 ? void 0 : settings.proxyUrl) && (settings.proxyAllProviders === true || descriptor.streamingCorsRestricted);
338
+ const openResult = useProxy
339
+ ? await ts_extras_1.AiAssist.callProxiedCompletionStream(settings.proxyUrl, requestParams)
340
+ : await ts_extras_1.AiAssist.callProviderCompletionStream(requestParams);
341
+ if (openResult.isFailure()) {
342
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming failed to start: ${openResult.message}`);
343
+ return (0, ts_utils_1.fail)(openResult.message);
344
+ }
345
+ setIsWorking(true);
346
+ let fullText = '';
347
+ let truncated = false;
348
+ let terminalError;
349
+ let receivedTerminalEvent = false;
350
+ try {
351
+ try {
352
+ for (var _d = true, _e = __asyncValues(openResult.value), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
353
+ _c = _f.value;
354
+ _d = false;
355
+ const event = _c;
356
+ try {
357
+ onEvent(event);
358
+ }
359
+ catch (err) {
360
+ // Consumer callback threw (e.g. setState-after-unmount). Convert
361
+ // into a terminal failure so streamDirect always resolves to a
362
+ // Result instead of rejecting and breaking its contract.
363
+ terminalError = `AI stream event handler failed: ${err instanceof Error ? err.message : String(err)}`;
364
+ receivedTerminalEvent = true;
365
+ break;
366
+ }
367
+ if (event.type === 'text-delta') {
368
+ fullText += event.delta;
369
+ }
370
+ else if (event.type === 'done') {
371
+ fullText = event.fullText;
372
+ truncated = event.truncated;
373
+ receivedTerminalEvent = true;
374
+ }
375
+ else if (event.type === 'error') {
376
+ terminalError = event.message;
377
+ receivedTerminalEvent = true;
378
+ }
379
+ }
380
+ }
381
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
382
+ finally {
383
+ try {
384
+ if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
385
+ }
386
+ finally { if (e_1) throw e_1.error; }
387
+ }
388
+ }
389
+ finally {
390
+ setIsWorking(false);
391
+ }
392
+ if (terminalError !== undefined) {
393
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming ended in error: ${terminalError}`);
394
+ return (0, ts_utils_1.fail)(terminalError);
395
+ }
396
+ if (!receivedTerminalEvent) {
397
+ const message = 'AI stream ended without a terminal done or error event';
398
+ logger === null || logger === void 0 ? void 0 : logger.error(message);
399
+ return (0, ts_utils_1.fail)(message);
400
+ }
401
+ return (0, ts_utils_1.succeed)({ fullText, truncated });
402
+ }, [settings, keyStore, logger]);
403
+ return {
404
+ actions,
405
+ isWorking,
406
+ copyPrompt,
407
+ generateDirect,
408
+ generateImages,
409
+ listModels,
410
+ streamDirect
411
+ };
222
412
  }
223
413
  //# sourceMappingURL=useAiAssist.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fgv/ts-app-shell",
3
- "version": "5.1.0-15",
3
+ "version": "5.1.0-17",
4
4
  "description": "Shared React UI primitives for application shells: column cascade, sidebar, toast/log messages, command palette, keybinding registry",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -17,8 +17,8 @@
17
17
  "dependencies": {
18
18
  "@heroicons/react": "~2.2.0",
19
19
  "tslib": "^2.8.1",
20
- "@fgv/ts-utils": "5.1.0-15",
21
- "@fgv/ts-extras": "5.1.0-15"
20
+ "@fgv/ts-utils": "5.1.0-17",
21
+ "@fgv/ts-extras": "5.1.0-17"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "react": ">=18.0.0",
@@ -51,8 +51,8 @@
51
51
  "@rushstack/heft-jest-plugin": "1.2.6",
52
52
  "@testing-library/dom": "^10.4.0",
53
53
  "@rushstack/heft-node-rig": "2.11.27",
54
- "@fgv/ts-utils-jest": "5.1.0-15",
55
- "@fgv/heft-dual-rig": "5.1.0-15"
54
+ "@fgv/heft-dual-rig": "5.1.0-17",
55
+ "@fgv/ts-utils-jest": "5.1.0-17"
56
56
  },
57
57
  "exports": {
58
58
  ".": {