@sylphx/lens-solid 1.2.22 → 1.2.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/lens-solid",
3
- "version": "1.2.22",
3
+ "version": "1.2.30",
4
4
  "description": "SolidJS bindings for Lens API framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,7 +12,7 @@
12
12
  }
13
13
  },
14
14
  "scripts": {
15
- "build": "bun build ./src/index.ts --outdir ./dist --target browser --external solid-js && tsc --emitDeclarationOnly --declaration --outDir ./dist",
15
+ "build": "bun build ./src/index.ts --outdir ./dist --target browser --external solid-js && bunx tsc --emitDeclarationOnly --declaration --outDir ./dist",
16
16
  "typecheck": "tsc --noEmit",
17
17
  "test": "bun test",
18
18
  "prepack": "[ -d dist ] || bun run build"
@@ -31,7 +31,7 @@
31
31
  "author": "SylphxAI",
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
- "@sylphx/lens-client": "^1.7.5"
34
+ "@sylphx/lens-client": "^1.15.2"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "solid-js": ">=1.8.0"
@@ -182,3 +182,458 @@ describe("primitive functions", () => {
182
182
  expect(query.loading()).toBe(false);
183
183
  });
184
184
  });
185
+
186
+ // =============================================================================
187
+ // Tests: createQuery - Comprehensive Coverage
188
+ // =============================================================================
189
+
190
+ describe("createQuery comprehensive", () => {
191
+ test("createQuery handles successful query with subscribe and then", async () => {
192
+ let _subscribeCallback: ((value: string) => void) | null = null;
193
+ let thenResolve: ((value: string) => void) | null = null;
194
+
195
+ const mockQueryResult = {
196
+ subscribe: (callback: (value: string) => void) => {
197
+ _subscribeCallback = callback;
198
+ return () => {
199
+ _subscribeCallback = null;
200
+ };
201
+ },
202
+ then: (resolve: (value: string) => void) => {
203
+ thenResolve = resolve;
204
+ },
205
+ };
206
+
207
+ const query = createQuery(mockQueryResult as any);
208
+
209
+ // Initially loading
210
+ expect(query.loading()).toBe(true);
211
+ expect(query.data()).toBe(null);
212
+
213
+ // Simulate promise resolution
214
+ if (thenResolve) {
215
+ thenResolve("test-data");
216
+ }
217
+
218
+ // Wait for next tick
219
+ await new Promise((resolve) => setTimeout(resolve, 0));
220
+
221
+ expect(query.data()).toBe("test-data");
222
+ expect(query.loading()).toBe(false);
223
+ expect(query.error()).toBe(null);
224
+ });
225
+
226
+ test("createQuery handles query error with non-Error object", async () => {
227
+ let thenReject: ((err: any) => void) | null = null;
228
+
229
+ const mockQueryResult = {
230
+ subscribe: () => () => {},
231
+ then: (_resolve: any, reject: (err: any) => void) => {
232
+ thenReject = reject;
233
+ },
234
+ };
235
+
236
+ const query = createQuery(mockQueryResult as any);
237
+
238
+ // Simulate promise rejection with non-Error
239
+ if (thenReject) {
240
+ thenReject("string error");
241
+ }
242
+
243
+ // Wait for next tick
244
+ await new Promise((resolve) => setTimeout(resolve, 0));
245
+
246
+ expect(query.loading()).toBe(false);
247
+ expect(query.error()).toBeInstanceOf(Error);
248
+ expect(query.error()?.message).toBe("string error");
249
+ });
250
+
251
+ test("createQuery handles query error with Error object", async () => {
252
+ let thenReject: ((err: any) => void) | null = null;
253
+
254
+ const mockQueryResult = {
255
+ subscribe: () => () => {},
256
+ then: (_resolve: any, reject: (err: any) => void) => {
257
+ thenReject = reject;
258
+ },
259
+ };
260
+
261
+ const query = createQuery(mockQueryResult as any);
262
+
263
+ const testError = new Error("test error");
264
+ if (thenReject) {
265
+ thenReject(testError);
266
+ }
267
+
268
+ // Wait for next tick
269
+ await new Promise((resolve) => setTimeout(resolve, 0));
270
+
271
+ expect(query.loading()).toBe(false);
272
+ expect(query.error()).toBe(testError);
273
+ });
274
+
275
+ test("createQuery refetch re-executes query", async () => {
276
+ let subscribeCount = 0;
277
+ let unsubscribeCalled = false;
278
+ let _thenResolve: ((value: string) => void) | null = null;
279
+
280
+ const mockQueryResult = {
281
+ subscribe: (callback: (value: string) => void) => {
282
+ subscribeCount++;
283
+ setTimeout(() => callback(`data-${subscribeCount}`), 0);
284
+ return () => {
285
+ unsubscribeCalled = true;
286
+ };
287
+ },
288
+ then: (resolve: (value: string) => void) => {
289
+ _thenResolve = resolve;
290
+ setTimeout(() => resolve(`then-${subscribeCount}`), 0);
291
+ },
292
+ };
293
+
294
+ const query = createQuery(mockQueryResult as any);
295
+
296
+ // Wait for initial load
297
+ await new Promise((resolve) => setTimeout(resolve, 10));
298
+
299
+ expect(subscribeCount).toBe(1);
300
+
301
+ // Call refetch
302
+ query.refetch();
303
+
304
+ expect(query.loading()).toBe(true);
305
+ expect(query.error()).toBe(null);
306
+ expect(unsubscribeCalled).toBe(true);
307
+
308
+ // Wait for refetch to complete
309
+ await new Promise((resolve) => setTimeout(resolve, 10));
310
+
311
+ expect(subscribeCount).toBe(2);
312
+ });
313
+
314
+ test("createQuery with skip option does not execute", () => {
315
+ let subscribed = false;
316
+
317
+ const mockQueryResult = {
318
+ subscribe: () => {
319
+ subscribed = true;
320
+ return () => {};
321
+ },
322
+ then: () => {},
323
+ };
324
+
325
+ const query = createQuery(mockQueryResult as any, { skip: true });
326
+
327
+ expect(query.loading()).toBe(false);
328
+ expect(query.data()).toBe(null);
329
+ expect(query.error()).toBe(null);
330
+ expect(subscribed).toBe(false);
331
+ });
332
+
333
+ test("createQuery with null query does not execute", () => {
334
+ const query = createQuery(null);
335
+
336
+ expect(query.loading()).toBe(false);
337
+ expect(query.data()).toBe(null);
338
+ expect(query.error()).toBe(null);
339
+ });
340
+
341
+ test("createQuery with undefined query does not execute", () => {
342
+ const query = createQuery(undefined);
343
+
344
+ expect(query.loading()).toBe(false);
345
+ expect(query.data()).toBe(null);
346
+ expect(query.error()).toBe(null);
347
+ });
348
+
349
+ test("createQuery with accessor function returning null", () => {
350
+ const query = createQuery(() => null);
351
+
352
+ expect(query.loading()).toBe(false);
353
+ expect(query.data()).toBe(null);
354
+ expect(query.error()).toBe(null);
355
+ });
356
+
357
+ test("createQuery with accessor function returning query", async () => {
358
+ let thenResolve: ((value: string) => void) | null = null;
359
+
360
+ const mockQueryResult = {
361
+ subscribe: () => () => {},
362
+ then: (resolve: (value: string) => void) => {
363
+ thenResolve = resolve;
364
+ },
365
+ };
366
+
367
+ const query = createQuery(() => mockQueryResult as any);
368
+
369
+ if (thenResolve) {
370
+ thenResolve("accessor-data");
371
+ }
372
+
373
+ await new Promise((resolve) => setTimeout(resolve, 0));
374
+
375
+ expect(query.data()).toBe("accessor-data");
376
+ });
377
+
378
+ test("createQuery sets up unsubscribe on query execution", () => {
379
+ let unsubscribeCalled = false;
380
+
381
+ const mockQueryResult = {
382
+ subscribe: () => {
383
+ return () => {
384
+ unsubscribeCalled = true;
385
+ };
386
+ },
387
+ then: () => {},
388
+ };
389
+
390
+ // Create query which will call subscribe and store unsubscribe
391
+ createQuery(mockQueryResult as any);
392
+
393
+ // The unsubscribe function exists and will be called on cleanup
394
+ // We can't directly test onCleanup without a full SolidJS environment
395
+ // but we verify the subscribe function was called and returns an unsubscribe
396
+ expect(unsubscribeCalled).toBe(false); // Not called yet until cleanup
397
+ });
398
+ });
399
+
400
+ // =============================================================================
401
+ // Tests: createMutation - Comprehensive Coverage
402
+ // =============================================================================
403
+
404
+ describe("createMutation comprehensive", () => {
405
+ test("createMutation handles successful mutation", async () => {
406
+ const mockMutation = async (input: { name: string }) => ({
407
+ data: { id: "123", name: input.name },
408
+ });
409
+
410
+ const mutation = createMutation(mockMutation);
411
+
412
+ expect(mutation.loading()).toBe(false);
413
+
414
+ const promise = mutation.mutate({ name: "test" });
415
+
416
+ expect(mutation.loading()).toBe(true);
417
+
418
+ const result = await promise;
419
+
420
+ expect(result.data.id).toBe("123");
421
+ expect(result.data.name).toBe("test");
422
+ expect(mutation.data()?.id).toBe("123");
423
+ expect(mutation.loading()).toBe(false);
424
+ expect(mutation.error()).toBe(null);
425
+ });
426
+
427
+ test("createMutation handles Error rejection", async () => {
428
+ const testError = new Error("mutation failed");
429
+ const mockMutation = async () => {
430
+ throw testError;
431
+ };
432
+
433
+ const mutation = createMutation(mockMutation);
434
+
435
+ try {
436
+ await mutation.mutate({} as any);
437
+ expect(true).toBe(false); // Should not reach
438
+ } catch (err) {
439
+ expect(err).toBe(testError);
440
+ expect(mutation.error()).toBe(testError);
441
+ expect(mutation.loading()).toBe(false);
442
+ }
443
+ });
444
+
445
+ test("createMutation handles non-Error rejection", async () => {
446
+ const mockMutation = async () => {
447
+ throw "string error";
448
+ };
449
+
450
+ const mutation = createMutation(mockMutation);
451
+
452
+ try {
453
+ await mutation.mutate({} as any);
454
+ expect(true).toBe(false); // Should not reach
455
+ } catch (err) {
456
+ expect(err).toBeInstanceOf(Error);
457
+ expect((err as Error).message).toBe("string error");
458
+ expect(mutation.error()?.message).toBe("string error");
459
+ expect(mutation.loading()).toBe(false);
460
+ }
461
+ });
462
+
463
+ test("createMutation reset clears all state after mutation", async () => {
464
+ const mockMutation = async (input: { name: string }) => ({
465
+ data: { id: "123", name: input.name },
466
+ });
467
+
468
+ const mutation = createMutation(mockMutation);
469
+
470
+ await mutation.mutate({ name: "test" });
471
+
472
+ expect(mutation.data()).not.toBe(null);
473
+
474
+ mutation.reset();
475
+
476
+ expect(mutation.data()).toBe(null);
477
+ expect(mutation.loading()).toBe(false);
478
+ expect(mutation.error()).toBe(null);
479
+ });
480
+
481
+ test("createMutation clears error on new mutation", async () => {
482
+ let shouldFail = true;
483
+ const mockMutation = async (input: { name: string }) => {
484
+ if (shouldFail) {
485
+ throw new Error("first error");
486
+ }
487
+ return { data: { id: "123", name: input.name } };
488
+ };
489
+
490
+ const mutation = createMutation(mockMutation);
491
+
492
+ // First call fails
493
+ try {
494
+ await mutation.mutate({ name: "test" });
495
+ } catch {}
496
+
497
+ expect(mutation.error()).not.toBe(null);
498
+
499
+ // Second call succeeds
500
+ shouldFail = false;
501
+ await mutation.mutate({ name: "test" });
502
+
503
+ expect(mutation.error()).toBe(null);
504
+ expect(mutation.data()?.id).toBe("123");
505
+ });
506
+ });
507
+
508
+ // =============================================================================
509
+ // Tests: createLazyQuery - Comprehensive Coverage
510
+ // =============================================================================
511
+
512
+ describe("createLazyQuery comprehensive", () => {
513
+ test("createLazyQuery handles successful execution", async () => {
514
+ const mockQueryResult = Promise.resolve("test-data");
515
+
516
+ const query = createLazyQuery(mockQueryResult as any);
517
+
518
+ expect(query.loading()).toBe(false);
519
+ expect(query.data()).toBe(null);
520
+
521
+ const result = await query.execute();
522
+
523
+ expect(result).toBe("test-data");
524
+ expect(query.data()).toBe("test-data");
525
+ expect(query.loading()).toBe(false);
526
+ expect(query.error()).toBe(null);
527
+ });
528
+
529
+ test("createLazyQuery handles Error rejection", async () => {
530
+ const testError = new Error("query failed");
531
+ const mockQueryResult = Promise.reject(testError);
532
+
533
+ const query = createLazyQuery(mockQueryResult as any);
534
+
535
+ try {
536
+ await query.execute();
537
+ expect(true).toBe(false); // Should not reach
538
+ } catch (err) {
539
+ expect(err).toBe(testError);
540
+ expect(query.error()).toBe(testError);
541
+ expect(query.loading()).toBe(false);
542
+ }
543
+ });
544
+
545
+ test("createLazyQuery handles non-Error rejection", async () => {
546
+ const mockQueryResult = Promise.reject("string error");
547
+
548
+ const query = createLazyQuery(mockQueryResult as any);
549
+
550
+ try {
551
+ await query.execute();
552
+ expect(true).toBe(false); // Should not reach
553
+ } catch (err) {
554
+ expect(err).toBeInstanceOf(Error);
555
+ expect((err as Error).message).toBe("string error");
556
+ expect(query.error()?.message).toBe("string error");
557
+ expect(query.loading()).toBe(false);
558
+ }
559
+ });
560
+
561
+ test("createLazyQuery with accessor function", async () => {
562
+ const mockQueryResult = Promise.resolve("accessor-data");
563
+ const query = createLazyQuery(() => mockQueryResult as any);
564
+
565
+ const result = await query.execute();
566
+
567
+ expect(result).toBe("accessor-data");
568
+ expect(query.data()).toBe("accessor-data");
569
+ });
570
+
571
+ test("createLazyQuery with accessor returning null", async () => {
572
+ const query = createLazyQuery(() => null);
573
+
574
+ const result = await query.execute();
575
+
576
+ expect(result).toBe(null);
577
+ expect(query.data()).toBe(null);
578
+ expect(query.loading()).toBe(false);
579
+ });
580
+
581
+ test("createLazyQuery with undefined", async () => {
582
+ const query = createLazyQuery(undefined);
583
+
584
+ const result = await query.execute();
585
+
586
+ expect(result).toBe(null);
587
+ expect(query.data()).toBe(null);
588
+ });
589
+
590
+ test("createLazyQuery reset after execution", async () => {
591
+ const mockQueryResult = Promise.resolve("test-data");
592
+ const query = createLazyQuery(mockQueryResult as any);
593
+
594
+ await query.execute();
595
+
596
+ expect(query.data()).toBe("test-data");
597
+
598
+ query.reset();
599
+
600
+ expect(query.data()).toBe(null);
601
+ expect(query.loading()).toBe(false);
602
+ expect(query.error()).toBe(null);
603
+ });
604
+
605
+ test("createLazyQuery reset after error", async () => {
606
+ const mockQueryResult = Promise.reject(new Error("test error"));
607
+ const query = createLazyQuery(mockQueryResult as any);
608
+
609
+ try {
610
+ await query.execute();
611
+ } catch {}
612
+
613
+ expect(query.error()).not.toBe(null);
614
+
615
+ query.reset();
616
+
617
+ expect(query.data()).toBe(null);
618
+ expect(query.loading()).toBe(false);
619
+ expect(query.error()).toBe(null);
620
+ });
621
+
622
+ test("createLazyQuery multiple executions", async () => {
623
+ let executeCount = 0;
624
+ const mockQueryFn = () => {
625
+ executeCount++;
626
+ return Promise.resolve(`data-${executeCount}`) as any;
627
+ };
628
+
629
+ const query = createLazyQuery(mockQueryFn);
630
+
631
+ const result1 = await query.execute();
632
+ expect(result1).toBe("data-1");
633
+
634
+ const result2 = await query.execute();
635
+ expect(result2).toBe("data-2");
636
+
637
+ expect(executeCount).toBe(2);
638
+ });
639
+ });