@sylphx/lens-solid 2.0.5 → 2.1.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/dist/index.js CHANGED
@@ -1,3 +1,1260 @@
1
+ // ../core/dist/index.js
2
+ var {createRequire} = (() => ({}));
3
+ // ../../node_modules/.bun/@sylphx+reify-core@0.1.2/node_modules/@sylphx/reify-core/dist/builder.js
4
+ var DSL_MARKER = Symbol("reify");
5
+ // ../../node_modules/.bun/@sylphx+reify-core@0.1.2/node_modules/@sylphx/reify-core/dist/evaluator.js
6
+ var plugins = new Map;
7
+ // ../core/dist/index.js
8
+ var ENTITY_SYMBOL = Symbol("lens:entity");
9
+ var OPTIMISTIC_PLUGIN_SYMBOL = Symbol.for("lens:optimistic-plugin");
10
+ var DEFAULT_OPERATION_LOG_CONFIG = {
11
+ maxEntries: 1e4,
12
+ maxAge: 5 * 60 * 1000,
13
+ maxMemory: 10 * 1024 * 1024,
14
+ cleanupInterval: 60 * 1000
15
+ };
16
+ function murmurhash3(key, seed = 0) {
17
+ const remainder = key.length % 4;
18
+ const bytes = key.length - remainder;
19
+ let h1 = seed;
20
+ const c1 = 3432918353;
21
+ const c2 = 461845907;
22
+ let i = 0;
23
+ while (i < bytes) {
24
+ let k12 = key.charCodeAt(i) & 255 | (key.charCodeAt(i + 1) & 255) << 8 | (key.charCodeAt(i + 2) & 255) << 16 | (key.charCodeAt(i + 3) & 255) << 24;
25
+ k12 = Math.imul(k12, c1);
26
+ k12 = k12 << 15 | k12 >>> 17;
27
+ k12 = Math.imul(k12, c2);
28
+ h1 ^= k12;
29
+ h1 = h1 << 13 | h1 >>> 19;
30
+ h1 = Math.imul(h1, 5) + 3864292196;
31
+ i += 4;
32
+ }
33
+ let k1 = 0;
34
+ if (remainder >= 3) {
35
+ k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
36
+ }
37
+ if (remainder >= 2) {
38
+ k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
39
+ }
40
+ if (remainder >= 1) {
41
+ k1 ^= key.charCodeAt(i) & 255;
42
+ k1 = Math.imul(k1, c1);
43
+ k1 = k1 << 15 | k1 >>> 17;
44
+ k1 = Math.imul(k1, c2);
45
+ h1 ^= k1;
46
+ }
47
+ h1 ^= key.length;
48
+ h1 ^= h1 >>> 16;
49
+ h1 = Math.imul(h1, 2246822507);
50
+ h1 ^= h1 >>> 13;
51
+ h1 = Math.imul(h1, 3266489909);
52
+ h1 ^= h1 >>> 16;
53
+ return (h1 >>> 0).toString(16).padStart(8, "0");
54
+ }
55
+ function hashValue(value) {
56
+ if (value === null) {
57
+ return "n:null";
58
+ }
59
+ if (value === undefined) {
60
+ return "u:undefined";
61
+ }
62
+ const type = typeof value;
63
+ switch (type) {
64
+ case "string":
65
+ return `s:${murmurhash3(value)}`;
66
+ case "number":
67
+ return `n:${murmurhash3(String(value))}`;
68
+ case "boolean":
69
+ return value ? "b:true" : "b:false";
70
+ case "object":
71
+ return `o:${murmurhash3(stableStringify(value))}`;
72
+ case "bigint":
73
+ return `i:${murmurhash3(String(value))}`;
74
+ case "symbol":
75
+ return `y:${murmurhash3(String(value))}`;
76
+ case "function":
77
+ return `f:${murmurhash3(value.toString())}`;
78
+ default:
79
+ return `x:${murmurhash3(String(value))}`;
80
+ }
81
+ }
82
+ function stableStringify(value) {
83
+ if (value === null || typeof value !== "object") {
84
+ return JSON.stringify(value);
85
+ }
86
+ if (Array.isArray(value)) {
87
+ return `[${value.map(stableStringify).join(",")}]`;
88
+ }
89
+ const keys = Object.keys(value).sort();
90
+ const pairs = keys.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`);
91
+ return `{${pairs.join(",")}}`;
92
+ }
93
+ function hashEntityState(data) {
94
+ return `e:${murmurhash3(stableStringify(data))}`;
95
+ }
96
+ class HashCache {
97
+ cache = new Map;
98
+ maxSize;
99
+ maxAge;
100
+ constructor(maxSize = 1000, maxAge = 60000) {
101
+ this.maxSize = maxSize;
102
+ this.maxAge = maxAge;
103
+ }
104
+ get(value) {
105
+ if (typeof value !== "object" || value === null) {
106
+ const cached = this.cache.get(value);
107
+ if (cached && Date.now() - cached.timestamp < this.maxAge) {
108
+ return cached.hash;
109
+ }
110
+ const hash = hashValue(value);
111
+ this.set(value, hash);
112
+ return hash;
113
+ }
114
+ return hashValue(value);
115
+ }
116
+ set(value, hash) {
117
+ if (this.cache.size >= this.maxSize) {
118
+ const firstKey = this.cache.keys().next().value;
119
+ if (firstKey !== undefined) {
120
+ this.cache.delete(firstKey);
121
+ }
122
+ }
123
+ this.cache.set(value, { hash, timestamp: Date.now() });
124
+ }
125
+ clear() {
126
+ this.cache.clear();
127
+ }
128
+ getStats() {
129
+ return { size: this.cache.size, maxSize: this.maxSize };
130
+ }
131
+ }
132
+
133
+ class FieldHashMap {
134
+ hashes = new Map;
135
+ hasChanged(field, value) {
136
+ const newHash = hashValue(value);
137
+ const oldHash = this.hashes.get(field);
138
+ if (oldHash === newHash) {
139
+ return false;
140
+ }
141
+ this.hashes.set(field, newHash);
142
+ return true;
143
+ }
144
+ update(field, value) {
145
+ this.hashes.set(field, hashValue(value));
146
+ }
147
+ getHash(field) {
148
+ return this.hashes.get(field);
149
+ }
150
+ delete(field) {
151
+ this.hashes.delete(field);
152
+ }
153
+ clear() {
154
+ this.hashes.clear();
155
+ }
156
+ getAll() {
157
+ return new Map(this.hashes);
158
+ }
159
+ getCombinedHash() {
160
+ const sorted = Array.from(this.hashes.entries()).sort((a, b) => a[0].localeCompare(b[0]));
161
+ return murmurhash3(sorted.map(([k, v]) => `${k}:${v}`).join("|"));
162
+ }
163
+ }
164
+ var DEFAULT_METRICS_CONFIG = {
165
+ enabled: true,
166
+ sampleRate: 1,
167
+ maxHistory: 1000
168
+ };
169
+
170
+ class ReconnectionMetricsTracker {
171
+ config;
172
+ history = [];
173
+ pending = new Map;
174
+ totalAttempts = 0;
175
+ totalSuccesses = 0;
176
+ totalFailures = 0;
177
+ totalSubscriptionsProcessed = 0;
178
+ totalBytesTransferred = 0;
179
+ totalBytesCompressed = 0;
180
+ statusCounts = {
181
+ current: 0,
182
+ patched: 0,
183
+ snapshot: 0,
184
+ deleted: 0,
185
+ error: 0
186
+ };
187
+ latencies = [];
188
+ constructor(config = {}) {
189
+ this.config = { ...DEFAULT_METRICS_CONFIG, ...config };
190
+ }
191
+ startReconnection(reconnectId, subscriptionCount) {
192
+ if (!this.shouldSample())
193
+ return;
194
+ const record = {
195
+ reconnectId,
196
+ startTime: Date.now(),
197
+ subscriptionCount
198
+ };
199
+ this.pending.set(reconnectId, record);
200
+ this.totalAttempts++;
201
+ this.emit({
202
+ type: "reconnect_start",
203
+ timestamp: record.startTime,
204
+ data: {
205
+ reconnectId,
206
+ subscriptionCount
207
+ }
208
+ });
209
+ }
210
+ completeReconnection(reconnectId, results) {
211
+ const pending = this.pending.get(reconnectId);
212
+ if (!pending)
213
+ return;
214
+ this.pending.delete(reconnectId);
215
+ const endTime = Date.now();
216
+ const latency = endTime - pending.startTime;
217
+ this.totalSuccesses++;
218
+ this.totalSubscriptionsProcessed += results.length;
219
+ this.latencies.push(latency);
220
+ for (const result of results) {
221
+ this.statusCounts[result.status]++;
222
+ if (result.data) {
223
+ const size = JSON.stringify(result.data).length;
224
+ this.totalBytesTransferred += size;
225
+ }
226
+ if (result.patches) {
227
+ const size = JSON.stringify(result.patches).length;
228
+ this.totalBytesTransferred += size;
229
+ }
230
+ }
231
+ const record = {
232
+ reconnectId,
233
+ startTime: pending.startTime,
234
+ endTime,
235
+ latency,
236
+ subscriptionCount: pending.subscriptionCount,
237
+ resultCount: results.length,
238
+ statusBreakdown: this.countStatuses(results),
239
+ success: true
240
+ };
241
+ this.addToHistory(record);
242
+ this.emit({
243
+ type: "reconnect_complete",
244
+ timestamp: endTime,
245
+ data: {
246
+ reconnectId,
247
+ latency,
248
+ resultCount: results.length,
249
+ statusBreakdown: record.statusBreakdown
250
+ }
251
+ });
252
+ }
253
+ failReconnection(reconnectId, error) {
254
+ const pending = this.pending.get(reconnectId);
255
+ if (!pending)
256
+ return;
257
+ this.pending.delete(reconnectId);
258
+ const endTime = Date.now();
259
+ const latency = endTime - pending.startTime;
260
+ this.totalFailures++;
261
+ this.latencies.push(latency);
262
+ const record = {
263
+ reconnectId,
264
+ startTime: pending.startTime,
265
+ endTime,
266
+ latency,
267
+ subscriptionCount: pending.subscriptionCount,
268
+ resultCount: 0,
269
+ statusBreakdown: {},
270
+ success: false,
271
+ error: error.message
272
+ };
273
+ this.addToHistory(record);
274
+ this.emit({
275
+ type: "reconnect_error",
276
+ timestamp: endTime,
277
+ data: {
278
+ reconnectId,
279
+ latency,
280
+ error: error.message
281
+ }
282
+ });
283
+ }
284
+ recordCompression(originalSize, compressedSize) {
285
+ this.totalBytesTransferred += compressedSize;
286
+ this.totalBytesCompressed += originalSize - compressedSize;
287
+ }
288
+ getMetrics() {
289
+ const avgLatency = this.latencies.length > 0 ? this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length : 0;
290
+ const p50 = this.percentile(50);
291
+ const p95 = this.percentile(95);
292
+ const p99 = this.percentile(99);
293
+ return {
294
+ totalAttempts: this.totalAttempts,
295
+ successfulReconnects: this.totalSuccesses,
296
+ failedReconnects: this.totalFailures,
297
+ successRate: this.totalAttempts > 0 ? this.totalSuccesses / this.totalAttempts : 1,
298
+ averageLatency: avgLatency,
299
+ p50Latency: p50,
300
+ p95Latency: p95,
301
+ p99Latency: p99,
302
+ totalSubscriptionsProcessed: this.totalSubscriptionsProcessed,
303
+ statusBreakdown: { ...this.statusCounts },
304
+ bytesTransferred: this.totalBytesTransferred,
305
+ bytesSaved: this.totalBytesCompressed,
306
+ compressionRatio: this.totalBytesTransferred > 0 ? (this.totalBytesTransferred - this.totalBytesCompressed) / this.totalBytesTransferred : 1
307
+ };
308
+ }
309
+ getHealth() {
310
+ const metrics = this.getMetrics();
311
+ const recentHistory = this.history.slice(-10);
312
+ let status = "healthy";
313
+ const issues = [];
314
+ if (metrics.successRate < 0.5) {
315
+ status = "unhealthy";
316
+ issues.push(`Low success rate: ${(metrics.successRate * 100).toFixed(1)}%`);
317
+ } else if (metrics.successRate < 0.9) {
318
+ status = "degraded";
319
+ issues.push(`Degraded success rate: ${(metrics.successRate * 100).toFixed(1)}%`);
320
+ }
321
+ if (metrics.p95Latency > 5000) {
322
+ status = status === "healthy" ? "degraded" : status;
323
+ issues.push(`High p95 latency: ${metrics.p95Latency}ms`);
324
+ }
325
+ const recentFailures = recentHistory.filter((r) => !r.success).length;
326
+ if (recentFailures > 5) {
327
+ status = "unhealthy";
328
+ issues.push(`${recentFailures} failures in last 10 reconnections`);
329
+ }
330
+ return {
331
+ status,
332
+ metrics,
333
+ issues,
334
+ lastReconnect: this.history[this.history.length - 1]?.endTime ?? null,
335
+ pendingReconnects: this.pending.size
336
+ };
337
+ }
338
+ getHistory(limit = 100) {
339
+ return this.history.slice(-limit);
340
+ }
341
+ reset() {
342
+ this.history = [];
343
+ this.pending.clear();
344
+ this.totalAttempts = 0;
345
+ this.totalSuccesses = 0;
346
+ this.totalFailures = 0;
347
+ this.totalSubscriptionsProcessed = 0;
348
+ this.totalBytesTransferred = 0;
349
+ this.totalBytesCompressed = 0;
350
+ this.statusCounts = { current: 0, patched: 0, snapshot: 0, deleted: 0, error: 0 };
351
+ this.latencies = [];
352
+ }
353
+ toJSON() {
354
+ return {
355
+ metrics: this.getMetrics(),
356
+ health: this.getHealth(),
357
+ history: this.history.slice(-100)
358
+ };
359
+ }
360
+ shouldSample() {
361
+ if (!this.config.enabled)
362
+ return false;
363
+ if (this.config.sampleRate >= 1)
364
+ return true;
365
+ return Math.random() < this.config.sampleRate;
366
+ }
367
+ emit(event) {
368
+ this.config.collector?.(event);
369
+ }
370
+ addToHistory(record) {
371
+ this.history.push(record);
372
+ if (this.history.length > this.config.maxHistory) {
373
+ this.history.shift();
374
+ }
375
+ }
376
+ countStatuses(results) {
377
+ const counts = {};
378
+ for (const result of results) {
379
+ counts[result.status] = (counts[result.status] ?? 0) + 1;
380
+ }
381
+ return counts;
382
+ }
383
+ percentile(p) {
384
+ if (this.latencies.length === 0)
385
+ return 0;
386
+ const sorted = [...this.latencies].sort((a, b) => a - b);
387
+ const index = Math.ceil(p / 100 * sorted.length) - 1;
388
+ return sorted[Math.max(0, index)];
389
+ }
390
+ }
391
+
392
+ // ../client/dist/index.js
393
+ class ClientImpl {
394
+ transport;
395
+ plugins;
396
+ metadata = null;
397
+ connectPromise = null;
398
+ subscriptions = new Map;
399
+ queryResultCache = new Map;
400
+ callbackWrappers = new WeakMap;
401
+ constructor(config) {
402
+ this.transport = config.transport;
403
+ this.plugins = config.plugins ?? [];
404
+ this.connectPromise = this.transport.connect();
405
+ this.connectPromise.then((metadata) => {
406
+ this.metadata = metadata;
407
+ }).catch(() => {
408
+ this.connectPromise = null;
409
+ });
410
+ }
411
+ async ensureConnected() {
412
+ if (this.metadata)
413
+ return;
414
+ if (!this.connectPromise) {
415
+ this.connectPromise = this.transport.connect();
416
+ }
417
+ this.metadata = await this.connectPromise;
418
+ }
419
+ async execute(op2) {
420
+ await this.ensureConnected();
421
+ let processedOp = op2;
422
+ for (const plugin of this.plugins) {
423
+ if (plugin.beforeRequest) {
424
+ processedOp = await plugin.beforeRequest(processedOp);
425
+ }
426
+ }
427
+ const resultOrObservable = this.transport.execute(processedOp);
428
+ let result;
429
+ if (this.isObservable(resultOrObservable)) {
430
+ result = await this.firstValueFrom(resultOrObservable);
431
+ } else {
432
+ result = await resultOrObservable;
433
+ }
434
+ for (const plugin of this.plugins) {
435
+ if (plugin.afterResponse) {
436
+ result = await plugin.afterResponse(result, processedOp);
437
+ }
438
+ }
439
+ if (result.error) {
440
+ const error = result.error;
441
+ for (const plugin of this.plugins) {
442
+ if (plugin.onError) {
443
+ try {
444
+ result = await plugin.onError(error, processedOp, () => this.execute(processedOp));
445
+ if (!result.error)
446
+ break;
447
+ } catch (e) {
448
+ result = { error: e };
449
+ }
450
+ }
451
+ }
452
+ }
453
+ return result;
454
+ }
455
+ isObservable(value) {
456
+ return value !== null && typeof value === "object" && "subscribe" in value && typeof value.subscribe === "function";
457
+ }
458
+ firstValueFrom(observable) {
459
+ return new Promise((resolve, reject) => {
460
+ let resolved = false;
461
+ const subscription = observable.subscribe({
462
+ next: (value) => {
463
+ if (!resolved) {
464
+ resolved = true;
465
+ subscription.unsubscribe?.();
466
+ resolve(value);
467
+ }
468
+ },
469
+ error: (err) => {
470
+ if (!resolved) {
471
+ resolved = true;
472
+ reject(err);
473
+ }
474
+ },
475
+ complete: () => {
476
+ if (!resolved) {
477
+ resolved = true;
478
+ reject(new Error("Observable completed without emitting a value"));
479
+ }
480
+ }
481
+ });
482
+ });
483
+ }
484
+ getOperationMeta(path) {
485
+ if (!this.metadata)
486
+ return;
487
+ const parts = path.split(".");
488
+ let current = this.metadata.operations;
489
+ for (const part of parts) {
490
+ if (!current || typeof current !== "object")
491
+ return;
492
+ current = current[part];
493
+ }
494
+ if (current && typeof current === "object" && "type" in current) {
495
+ return current;
496
+ }
497
+ return;
498
+ }
499
+ generateId(type, path) {
500
+ return `${type}-${path}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
501
+ }
502
+ makeQueryKey(path, input) {
503
+ return `${path}:${JSON.stringify(input ?? null)}`;
504
+ }
505
+ executeQuery(path, input, select) {
506
+ const key = this.makeQueryKey(path, input);
507
+ const cached = this.queryResultCache.get(key);
508
+ if (cached && !select) {
509
+ return cached;
510
+ }
511
+ if (!this.subscriptions.has(key)) {
512
+ this.subscriptions.set(key, {
513
+ data: null,
514
+ callbacks: new Set
515
+ });
516
+ }
517
+ const sub = this.subscriptions.get(key);
518
+ const result = {
519
+ get value() {
520
+ return sub.data;
521
+ },
522
+ subscribe: (callback) => {
523
+ if (callback) {
524
+ const typedCallback = callback;
525
+ let wrapped = this.callbackWrappers.get(typedCallback);
526
+ if (!wrapped) {
527
+ wrapped = (data) => callback(data);
528
+ this.callbackWrappers.set(typedCallback, wrapped);
529
+ }
530
+ sub.callbacks.add(wrapped);
531
+ if (sub.data !== null) {
532
+ callback(sub.data);
533
+ }
534
+ }
535
+ if (!sub.unsubscribe) {
536
+ this.startSubscription(path, input, key);
537
+ }
538
+ return () => {
539
+ if (callback) {
540
+ const typedCallback = callback;
541
+ const wrapped = this.callbackWrappers.get(typedCallback);
542
+ if (wrapped) {
543
+ sub.callbacks.delete(wrapped);
544
+ }
545
+ }
546
+ if (sub.callbacks.size === 0 && sub.unsubscribe) {
547
+ sub.unsubscribe();
548
+ sub.unsubscribe = undefined;
549
+ }
550
+ };
551
+ },
552
+ select: (selection) => {
553
+ return this.executeQuery(path, input, selection);
554
+ },
555
+ then: async (onfulfilled, onrejected) => {
556
+ try {
557
+ const op2 = {
558
+ id: this.generateId("query", path),
559
+ path,
560
+ type: "query",
561
+ input,
562
+ meta: select ? { select } : {}
563
+ };
564
+ const response = await this.execute(op2);
565
+ if (response.error) {
566
+ throw response.error;
567
+ }
568
+ sub.data = response.data;
569
+ for (const cb of sub.callbacks) {
570
+ cb(response.data);
571
+ }
572
+ return onfulfilled ? onfulfilled(response.data) : response.data;
573
+ } catch (error) {
574
+ if (onrejected) {
575
+ return onrejected(error);
576
+ }
577
+ throw error;
578
+ }
579
+ }
580
+ };
581
+ if (!select) {
582
+ this.queryResultCache.set(key, result);
583
+ }
584
+ return result;
585
+ }
586
+ async startSubscription(path, input, key) {
587
+ const sub = this.subscriptions.get(key);
588
+ if (!sub)
589
+ return;
590
+ await this.ensureConnected();
591
+ const meta = this.getOperationMeta(path);
592
+ const isSubscription = meta?.type === "subscription";
593
+ if (isSubscription) {
594
+ const op2 = {
595
+ id: this.generateId("subscription", path),
596
+ path,
597
+ type: "subscription",
598
+ input
599
+ };
600
+ const resultOrObservable = this.transport.execute(op2);
601
+ if (this.isObservable(resultOrObservable)) {
602
+ const subscription = resultOrObservable.subscribe({
603
+ next: (result) => {
604
+ if (result.data !== undefined) {
605
+ sub.data = result.data;
606
+ for (const cb of sub.callbacks) {
607
+ cb(result.data);
608
+ }
609
+ }
610
+ },
611
+ error: () => {},
612
+ complete: () => {}
613
+ });
614
+ sub.unsubscribe = () => subscription.unsubscribe();
615
+ }
616
+ } else {
617
+ this.executeQuery(path, input).then(() => {});
618
+ }
619
+ }
620
+ async executeMutation(path, input, select) {
621
+ await this.ensureConnected();
622
+ const op2 = {
623
+ id: this.generateId("mutation", path),
624
+ path,
625
+ type: "mutation",
626
+ input,
627
+ meta: select ? { select } : {}
628
+ };
629
+ const response = await this.execute(op2);
630
+ if (response.error) {
631
+ throw response.error;
632
+ }
633
+ return { data: response.data };
634
+ }
635
+ createAccessor(path) {
636
+ return (descriptor) => {
637
+ const isNewApi = descriptor === undefined || descriptor === null || typeof descriptor === "object" && descriptor !== null && (("input" in descriptor) || ("select" in descriptor));
638
+ const input = isNewApi ? descriptor?.input : descriptor;
639
+ const select = isNewApi ? descriptor?.select : undefined;
640
+ const queryResult = this.executeQuery(path, input, select);
641
+ const originalThen = queryResult.then.bind(queryResult);
642
+ queryResult.then = async (onfulfilled, onrejected) => {
643
+ try {
644
+ await this.ensureConnected();
645
+ const meta = this.getOperationMeta(path);
646
+ if (meta?.type === "mutation") {
647
+ const inputObj = input ?? {};
648
+ const mutationResult = await this.executeMutation(path, inputObj, select);
649
+ return onfulfilled ? onfulfilled(mutationResult) : mutationResult;
650
+ }
651
+ return originalThen(onfulfilled, onrejected);
652
+ } catch (error) {
653
+ if (onrejected) {
654
+ return onrejected(error);
655
+ }
656
+ throw error;
657
+ }
658
+ };
659
+ return queryResult;
660
+ };
661
+ }
662
+ }
663
+ function createClient(config) {
664
+ const impl = new ClientImpl(config);
665
+ function createNestedProxy(prefix) {
666
+ const handler = {
667
+ get(_target, prop) {
668
+ if (typeof prop === "symbol")
669
+ return;
670
+ const key = prop;
671
+ if (key === "then")
672
+ return;
673
+ if (key.startsWith("_"))
674
+ return;
675
+ const path = prefix ? `${prefix}.${key}` : key;
676
+ return createNestedProxy(path);
677
+ },
678
+ apply(_target, _thisArg, args) {
679
+ const accessor = impl.createAccessor(prefix);
680
+ return accessor(args[0]);
681
+ }
682
+ };
683
+ return new Proxy(() => {}, handler);
684
+ }
685
+ return createNestedProxy("");
686
+ }
687
+ var http = function http2(options) {
688
+ const { url, headers: defaultHeaders = {}, fetch: fetchImpl = fetch, polling = {} } = options;
689
+ const { interval: pollInterval = 1000, maxRetries = 3 } = polling;
690
+ const baseUrl = url.replace(/\/$/, "");
691
+ return {
692
+ async connect() {
693
+ const response = await fetchImpl(`${baseUrl}/__lens/metadata`, {
694
+ method: "GET",
695
+ headers: {
696
+ Accept: "application/json",
697
+ ...defaultHeaders
698
+ }
699
+ });
700
+ if (!response.ok) {
701
+ throw new Error(`Failed to connect: ${response.status} ${response.statusText}`);
702
+ }
703
+ return response.json();
704
+ },
705
+ execute(op2) {
706
+ if (op2.type === "subscription") {
707
+ return createPollingObservable(baseUrl, op2, {
708
+ interval: pollInterval,
709
+ maxRetries,
710
+ headers: defaultHeaders,
711
+ fetch: fetchImpl
712
+ });
713
+ }
714
+ return executeRequest(baseUrl, op2, {
715
+ headers: {
716
+ ...defaultHeaders,
717
+ ...op2.meta?.headers ?? {}
718
+ },
719
+ fetch: fetchImpl,
720
+ timeout: op2.meta?.timeout
721
+ });
722
+ }
723
+ };
724
+ };
725
+ async function executeRequest(baseUrl, op2, options) {
726
+ const { headers, fetch: fetchImpl, timeout: timeout2 } = options;
727
+ try {
728
+ const controller = new AbortController;
729
+ let timeoutId;
730
+ if (timeout2) {
731
+ timeoutId = setTimeout(() => controller.abort(), timeout2);
732
+ }
733
+ const response = await fetchImpl(baseUrl, {
734
+ method: "POST",
735
+ headers: {
736
+ "Content-Type": "application/json",
737
+ Accept: "application/json",
738
+ ...headers
739
+ },
740
+ body: JSON.stringify({
741
+ id: op2.id,
742
+ path: op2.path,
743
+ type: op2.type,
744
+ input: op2.input
745
+ }),
746
+ signal: controller.signal
747
+ });
748
+ if (timeoutId) {
749
+ clearTimeout(timeoutId);
750
+ }
751
+ if (!response.ok) {
752
+ return {
753
+ error: new Error(`HTTP ${response.status}: ${response.statusText}`)
754
+ };
755
+ }
756
+ const result = await response.json();
757
+ return result;
758
+ } catch (error) {
759
+ if (error instanceof Error && error.name === "AbortError") {
760
+ return { error: new Error("Request timeout") };
761
+ }
762
+ return { error };
763
+ }
764
+ }
765
+ function createPollingObservable(baseUrl, op2, options) {
766
+ return {
767
+ subscribe(observer) {
768
+ let active = true;
769
+ let retries = 0;
770
+ let lastValue;
771
+ const poll = async () => {
772
+ if (!active)
773
+ return;
774
+ try {
775
+ const result = await executeRequest(baseUrl, op2, {
776
+ headers: options.headers,
777
+ fetch: options.fetch
778
+ });
779
+ if (!active)
780
+ return;
781
+ if (result.error) {
782
+ retries++;
783
+ if (retries > options.maxRetries) {
784
+ observer.error?.(result.error);
785
+ return;
786
+ }
787
+ } else {
788
+ retries = 0;
789
+ const newValue = JSON.stringify(result.data);
790
+ if (newValue !== JSON.stringify(lastValue)) {
791
+ lastValue = result.data;
792
+ observer.next?.(result);
793
+ }
794
+ }
795
+ if (active) {
796
+ setTimeout(poll, options.interval);
797
+ }
798
+ } catch (error) {
799
+ if (active) {
800
+ observer.error?.(error);
801
+ }
802
+ }
803
+ };
804
+ poll();
805
+ return {
806
+ unsubscribe() {
807
+ active = false;
808
+ }
809
+ };
810
+ }
811
+ };
812
+ }
813
+ http.server = function httpServer(options) {
814
+ const { port, path = "", hostname = "0.0.0.0" } = options;
815
+ return {
816
+ listen(server) {
817
+ Bun.serve({
818
+ port,
819
+ hostname,
820
+ async fetch(req) {
821
+ const url = new URL(req.url);
822
+ const basePath = path.replace(/\/$/, "");
823
+ if (url.pathname === `${basePath}/__lens/metadata` && req.method === "GET") {
824
+ return Response.json(server.getMetadata());
825
+ }
826
+ if (url.pathname === basePath && req.method === "POST") {
827
+ try {
828
+ const body = await req.json();
829
+ const result = await server.execute(body);
830
+ return Response.json(result);
831
+ } catch (error) {
832
+ return Response.json({ error: { message: error.message } }, { status: 500 });
833
+ }
834
+ }
835
+ return new Response("Not Found", { status: 404 });
836
+ }
837
+ });
838
+ }
839
+ };
840
+ };
841
+ class SubscriptionRegistry {
842
+ subscriptions = new Map;
843
+ entityIndex = new Map;
844
+ add(sub) {
845
+ const tracked = {
846
+ ...sub,
847
+ state: "pending",
848
+ lastDataHash: sub.lastData ? hashEntityState(sub.lastData) : null,
849
+ createdAt: Date.now(),
850
+ lastUpdateAt: null
851
+ };
852
+ this.subscriptions.set(sub.id, tracked);
853
+ const entityKey = `${sub.entity}:${sub.entityId}`;
854
+ let ids = this.entityIndex.get(entityKey);
855
+ if (!ids) {
856
+ ids = new Set;
857
+ this.entityIndex.set(entityKey, ids);
858
+ }
859
+ ids.add(sub.id);
860
+ }
861
+ get(id) {
862
+ return this.subscriptions.get(id);
863
+ }
864
+ has(id) {
865
+ return this.subscriptions.has(id);
866
+ }
867
+ remove(id) {
868
+ const sub = this.subscriptions.get(id);
869
+ if (!sub)
870
+ return;
871
+ this.subscriptions.delete(id);
872
+ const entityKey = `${sub.entity}:${sub.entityId}`;
873
+ const ids = this.entityIndex.get(entityKey);
874
+ if (ids) {
875
+ ids.delete(id);
876
+ if (ids.size === 0) {
877
+ this.entityIndex.delete(entityKey);
878
+ }
879
+ }
880
+ }
881
+ getByEntity(entity2, entityId) {
882
+ const entityKey = `${entity2}:${entityId}`;
883
+ const ids = this.entityIndex.get(entityKey);
884
+ if (!ids)
885
+ return [];
886
+ const result = [];
887
+ for (const id of ids) {
888
+ const sub = this.subscriptions.get(id);
889
+ if (sub) {
890
+ result.push(sub);
891
+ }
892
+ }
893
+ return result;
894
+ }
895
+ updateVersion(id, version, data) {
896
+ const sub = this.subscriptions.get(id);
897
+ if (!sub)
898
+ return;
899
+ sub.version = version;
900
+ sub.lastUpdateAt = Date.now();
901
+ if (data !== undefined) {
902
+ sub.lastData = data;
903
+ sub.lastDataHash = hashEntityState(data);
904
+ }
905
+ if (sub.state === "pending" || sub.state === "reconnecting") {
906
+ sub.state = "active";
907
+ }
908
+ }
909
+ updateData(id, data) {
910
+ const sub = this.subscriptions.get(id);
911
+ if (!sub)
912
+ return;
913
+ sub.lastData = data;
914
+ sub.lastDataHash = hashEntityState(data);
915
+ }
916
+ getLastData(id) {
917
+ return this.subscriptions.get(id)?.lastData ?? null;
918
+ }
919
+ getVersion(id) {
920
+ return this.subscriptions.get(id)?.version ?? null;
921
+ }
922
+ markActive(id) {
923
+ const sub = this.subscriptions.get(id);
924
+ if (sub) {
925
+ sub.state = "active";
926
+ }
927
+ }
928
+ markError(id) {
929
+ const sub = this.subscriptions.get(id);
930
+ if (sub) {
931
+ sub.state = "error";
932
+ }
933
+ }
934
+ markAllReconnecting() {
935
+ for (const sub of this.subscriptions.values()) {
936
+ if (sub.state === "active") {
937
+ sub.state = "reconnecting";
938
+ }
939
+ }
940
+ }
941
+ getByState(state) {
942
+ const result = [];
943
+ for (const sub of this.subscriptions.values()) {
944
+ if (sub.state === state) {
945
+ result.push(sub);
946
+ }
947
+ }
948
+ return result;
949
+ }
950
+ getAllForReconnect() {
951
+ const result = [];
952
+ for (const sub of this.subscriptions.values()) {
953
+ if (sub.state === "reconnecting" || sub.state === "active") {
954
+ const reconnectSub = {
955
+ id: sub.id,
956
+ entity: sub.entity,
957
+ entityId: sub.entityId,
958
+ fields: sub.fields,
959
+ version: sub.version,
960
+ input: sub.input
961
+ };
962
+ if (sub.lastDataHash) {
963
+ reconnectSub.dataHash = sub.lastDataHash;
964
+ }
965
+ result.push(reconnectSub);
966
+ }
967
+ }
968
+ return result;
969
+ }
970
+ processReconnectResult(id, version, data) {
971
+ const sub = this.subscriptions.get(id);
972
+ if (!sub)
973
+ return;
974
+ sub.version = version;
975
+ sub.state = "active";
976
+ sub.lastUpdateAt = Date.now();
977
+ if (data !== undefined) {
978
+ sub.lastData = data;
979
+ sub.lastDataHash = hashEntityState(data);
980
+ }
981
+ }
982
+ getObserver(id) {
983
+ return this.subscriptions.get(id)?.observer;
984
+ }
985
+ updateObserver(id, observer) {
986
+ const sub = this.subscriptions.get(id);
987
+ if (sub) {
988
+ sub.observer = observer;
989
+ }
990
+ }
991
+ notifyNext(id, data) {
992
+ const sub = this.subscriptions.get(id);
993
+ sub?.observer.next?.({ data, version: sub.version });
994
+ }
995
+ notifyError(id, error) {
996
+ this.subscriptions.get(id)?.observer.error?.(error);
997
+ }
998
+ notifyAllReconnectingError(error) {
999
+ for (const sub of this.subscriptions.values()) {
1000
+ if (sub.state === "reconnecting") {
1001
+ sub.observer.error?.(error);
1002
+ }
1003
+ }
1004
+ }
1005
+ get size() {
1006
+ return this.subscriptions.size;
1007
+ }
1008
+ getIds() {
1009
+ return Array.from(this.subscriptions.keys());
1010
+ }
1011
+ values() {
1012
+ return this.subscriptions.values();
1013
+ }
1014
+ getStats() {
1015
+ const byState = {
1016
+ pending: 0,
1017
+ active: 0,
1018
+ reconnecting: 0,
1019
+ error: 0
1020
+ };
1021
+ const byEntity = {};
1022
+ for (const sub of this.subscriptions.values()) {
1023
+ byState[sub.state]++;
1024
+ const entityKey = `${sub.entity}:${sub.entityId}`;
1025
+ byEntity[entityKey] = (byEntity[entityKey] ?? 0) + 1;
1026
+ }
1027
+ return {
1028
+ total: this.subscriptions.size,
1029
+ byState,
1030
+ byEntity
1031
+ };
1032
+ }
1033
+ clear() {
1034
+ for (const sub of this.subscriptions.values()) {
1035
+ sub.observer.complete?.();
1036
+ }
1037
+ this.subscriptions.clear();
1038
+ this.entityIndex.clear();
1039
+ }
1040
+ clearErrors() {
1041
+ const toRemove = [];
1042
+ for (const [id, sub] of this.subscriptions) {
1043
+ if (sub.state === "error") {
1044
+ toRemove.push(id);
1045
+ }
1046
+ }
1047
+ for (const id of toRemove) {
1048
+ this.remove(id);
1049
+ }
1050
+ }
1051
+ }
1052
+
1053
+ // src/create.ts
1054
+ import { createEffect, createSignal, onCleanup } from "solid-js";
1055
+ function createQueryHook(baseClient, path) {
1056
+ const getEndpoint = (p) => {
1057
+ const parts = p.split(".");
1058
+ let current = baseClient;
1059
+ for (const part of parts) {
1060
+ current = current[part];
1061
+ }
1062
+ return current;
1063
+ };
1064
+ const useQueryPrimitive = (options) => {
1065
+ const [data, setData] = createSignal(null);
1066
+ const [loading, setLoading] = createSignal(!options?.skip);
1067
+ const [error, setError] = createSignal(null);
1068
+ let unsubscribe = null;
1069
+ const executeQuery = () => {
1070
+ if (unsubscribe) {
1071
+ unsubscribe();
1072
+ unsubscribe = null;
1073
+ }
1074
+ if (options?.skip) {
1075
+ setData(null);
1076
+ setLoading(false);
1077
+ setError(null);
1078
+ return;
1079
+ }
1080
+ const endpoint = getEndpoint(path);
1081
+ const query = endpoint({ input: options?.input, select: options?.select });
1082
+ setLoading(true);
1083
+ setError(null);
1084
+ unsubscribe = query.subscribe((value) => {
1085
+ setData(() => value);
1086
+ setLoading(false);
1087
+ setError(null);
1088
+ });
1089
+ query.then((value) => {
1090
+ setData(() => value);
1091
+ setLoading(false);
1092
+ setError(null);
1093
+ }, (err) => {
1094
+ const queryError = err instanceof Error ? err : new Error(String(err));
1095
+ setError(queryError);
1096
+ setLoading(false);
1097
+ });
1098
+ };
1099
+ executeQuery();
1100
+ createEffect(() => {
1101
+ const _ = JSON.stringify(options);
1102
+ executeQuery();
1103
+ });
1104
+ onCleanup(() => {
1105
+ if (unsubscribe) {
1106
+ unsubscribe();
1107
+ unsubscribe = null;
1108
+ }
1109
+ });
1110
+ const refetch = () => {
1111
+ if (unsubscribe) {
1112
+ unsubscribe();
1113
+ unsubscribe = null;
1114
+ }
1115
+ setLoading(true);
1116
+ setError(null);
1117
+ const endpoint = getEndpoint(path);
1118
+ const query = endpoint({ input: options?.input, select: options?.select });
1119
+ if (query) {
1120
+ unsubscribe = query.subscribe((value) => {
1121
+ setData(() => value);
1122
+ setLoading(false);
1123
+ setError(null);
1124
+ });
1125
+ query.then((value) => {
1126
+ setData(() => value);
1127
+ setLoading(false);
1128
+ }, (err) => {
1129
+ const queryError = err instanceof Error ? err : new Error(String(err));
1130
+ setError(queryError);
1131
+ setLoading(false);
1132
+ });
1133
+ }
1134
+ };
1135
+ return { data, loading, error, refetch };
1136
+ };
1137
+ const fetch2 = async (options) => {
1138
+ const endpoint = getEndpoint(path);
1139
+ const queryResult = endpoint({ input: options?.input, select: options?.select });
1140
+ return queryResult.then((data) => data);
1141
+ };
1142
+ useQueryPrimitive.fetch = fetch2;
1143
+ return useQueryPrimitive;
1144
+ }
1145
+ function createMutationHook(baseClient, path) {
1146
+ const getEndpoint = (p) => {
1147
+ const parts = p.split(".");
1148
+ let current = baseClient;
1149
+ for (const part of parts) {
1150
+ current = current[part];
1151
+ }
1152
+ return current;
1153
+ };
1154
+ const useMutationPrimitive = (hookOptions) => {
1155
+ const [data, setData] = createSignal(null);
1156
+ const [loading, setLoading] = createSignal(false);
1157
+ const [error, setError] = createSignal(null);
1158
+ const mutate = async (options) => {
1159
+ setLoading(true);
1160
+ setError(null);
1161
+ try {
1162
+ const endpoint = getEndpoint(path);
1163
+ const result = await endpoint({ input: options.input, select: options.select });
1164
+ const mutationResult = result;
1165
+ setData(() => mutationResult.data);
1166
+ setLoading(false);
1167
+ hookOptions?.onSuccess?.(mutationResult.data);
1168
+ hookOptions?.onSettled?.();
1169
+ return mutationResult.data;
1170
+ } catch (err) {
1171
+ const mutationError = err instanceof Error ? err : new Error(String(err));
1172
+ setError(mutationError);
1173
+ setLoading(false);
1174
+ hookOptions?.onError?.(mutationError);
1175
+ hookOptions?.onSettled?.();
1176
+ throw mutationError;
1177
+ }
1178
+ };
1179
+ const reset = () => {
1180
+ setData(null);
1181
+ setLoading(false);
1182
+ setError(null);
1183
+ };
1184
+ return { mutate, loading, error, data, reset };
1185
+ };
1186
+ const fetch2 = async (options) => {
1187
+ const endpoint = getEndpoint(path);
1188
+ const result = await endpoint({ input: options.input, select: options.select });
1189
+ const mutationResult = result;
1190
+ return mutationResult.data;
1191
+ };
1192
+ useMutationPrimitive.fetch = fetch2;
1193
+ return useMutationPrimitive;
1194
+ }
1195
+ var hookCache = new Map;
1196
+ function createClient2(config) {
1197
+ const baseClient = createClient(config);
1198
+ function createProxy(path) {
1199
+ const handler = {
1200
+ get(_target, prop) {
1201
+ if (typeof prop === "symbol")
1202
+ return;
1203
+ const key = prop;
1204
+ if (key === "fetch") {
1205
+ return async (options) => {
1206
+ const parts = path.split(".");
1207
+ let current = baseClient;
1208
+ for (const part of parts) {
1209
+ current = current[part];
1210
+ }
1211
+ const endpointFn = current;
1212
+ const queryResult = endpointFn(options);
1213
+ const result = await queryResult;
1214
+ if (result && typeof result === "object" && "data" in result && Object.keys(result).length === 1) {
1215
+ return result.data;
1216
+ }
1217
+ return result;
1218
+ };
1219
+ }
1220
+ if (key === "then")
1221
+ return;
1222
+ if (key.startsWith("_"))
1223
+ return;
1224
+ const newPath = path ? `${path}.${key}` : key;
1225
+ return createProxy(newPath);
1226
+ },
1227
+ apply(_target, _thisArg, args) {
1228
+ const options = args[0];
1229
+ const isQueryOptions = options && (("input" in options) || ("select" in options) || ("skip" in options));
1230
+ const isMutationOptions = !options || !isQueryOptions && (Object.keys(options).length === 0 || ("onSuccess" in options) || ("onError" in options) || ("onSettled" in options));
1231
+ const cacheKeyQuery = `${path}:query`;
1232
+ const cacheKeyMutation = `${path}:mutation`;
1233
+ if (isQueryOptions) {
1234
+ if (!hookCache.has(cacheKeyQuery)) {
1235
+ hookCache.set(cacheKeyQuery, createQueryHook(baseClient, path));
1236
+ }
1237
+ const hook2 = hookCache.get(cacheKeyQuery);
1238
+ return hook2(options);
1239
+ }
1240
+ if (isMutationOptions) {
1241
+ if (!hookCache.has(cacheKeyMutation)) {
1242
+ hookCache.set(cacheKeyMutation, createMutationHook(baseClient, path));
1243
+ }
1244
+ const hook2 = hookCache.get(cacheKeyMutation);
1245
+ return hook2(options);
1246
+ }
1247
+ if (!hookCache.has(cacheKeyQuery)) {
1248
+ hookCache.set(cacheKeyQuery, createQueryHook(baseClient, path));
1249
+ }
1250
+ const hook = hookCache.get(cacheKeyQuery);
1251
+ return hook(options);
1252
+ }
1253
+ };
1254
+ return new Proxy(() => {}, handler);
1255
+ }
1256
+ return createProxy("");
1257
+ }
1
1258
  // src/context.tsx
2
1259
  import { createContext, useContext } from "solid-js";
3
1260
  var LensClientContext = createContext();
@@ -14,17 +1271,20 @@ function useLensClient() {
14
1271
  return client;
15
1272
  }
16
1273
  // src/primitives.ts
17
- import { createSignal, onCleanup } from "solid-js";
1274
+ import { createEffect as createEffect2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
18
1275
  function resolveQuery(input) {
19
1276
  return typeof input === "function" ? input() : input;
20
1277
  }
21
1278
  function createQuery(queryInput, options) {
22
- const [data, setData] = createSignal(null);
23
- const [loading, setLoading] = createSignal(!options?.skip);
24
- const [error, setError] = createSignal(null);
1279
+ const [data, setData] = createSignal2(null);
1280
+ const [loading, setLoading] = createSignal2(!options?.skip);
1281
+ const [error, setError] = createSignal2(null);
25
1282
  let unsubscribe = null;
26
- const executeQuery = () => {
27
- const queryResult = resolveQuery(queryInput);
1283
+ const executeQuery = (queryResult) => {
1284
+ if (unsubscribe) {
1285
+ unsubscribe();
1286
+ unsubscribe = null;
1287
+ }
28
1288
  if (options?.skip || queryResult == null) {
29
1289
  setData(null);
30
1290
  setLoading(false);
@@ -48,8 +1308,15 @@ function createQuery(queryInput, options) {
48
1308
  setLoading(false);
49
1309
  });
50
1310
  };
51
- executeQuery();
52
- onCleanup(() => {
1311
+ const initialQuery = resolveQuery(queryInput);
1312
+ executeQuery(initialQuery);
1313
+ createEffect2(() => {
1314
+ const queryResult = resolveQuery(queryInput);
1315
+ if (queryResult !== initialQuery) {
1316
+ executeQuery(queryResult);
1317
+ }
1318
+ });
1319
+ onCleanup2(() => {
53
1320
  if (unsubscribe) {
54
1321
  unsubscribe();
55
1322
  unsubscribe = null;
@@ -62,7 +1329,22 @@ function createQuery(queryInput, options) {
62
1329
  }
63
1330
  setLoading(true);
64
1331
  setError(null);
65
- executeQuery();
1332
+ const queryResult = resolveQuery(queryInput);
1333
+ if (queryResult) {
1334
+ unsubscribe = queryResult.subscribe((value) => {
1335
+ setData(() => value);
1336
+ setLoading(false);
1337
+ setError(null);
1338
+ });
1339
+ queryResult.then((value) => {
1340
+ setData(() => value);
1341
+ setLoading(false);
1342
+ }, (err) => {
1343
+ const queryError = err instanceof Error ? err : new Error(String(err));
1344
+ setError(queryError);
1345
+ setLoading(false);
1346
+ });
1347
+ }
66
1348
  };
67
1349
  return {
68
1350
  data,
@@ -72,9 +1354,9 @@ function createQuery(queryInput, options) {
72
1354
  };
73
1355
  }
74
1356
  function createMutation(mutationFn) {
75
- const [data, setData] = createSignal(null);
76
- const [loading, setLoading] = createSignal(false);
77
- const [error, setError] = createSignal(null);
1357
+ const [data, setData] = createSignal2(null);
1358
+ const [loading, setLoading] = createSignal2(false);
1359
+ const [error, setError] = createSignal2(null);
78
1360
  const mutate = async (input) => {
79
1361
  setLoading(true);
80
1362
  setError(null);
@@ -104,10 +1386,10 @@ function createMutation(mutationFn) {
104
1386
  };
105
1387
  }
106
1388
  function createLazyQuery(queryInput) {
107
- const [data, setData] = createSignal(null);
108
- const [loading, setLoading] = createSignal(false);
109
- const [error, setError] = createSignal(null);
110
- const execute = async () => {
1389
+ const [data, setData] = createSignal2(null);
1390
+ const [loading, setLoading] = createSignal2(false);
1391
+ const [error, setError] = createSignal2(null);
1392
+ const execute2 = async () => {
111
1393
  const queryResult = resolveQuery(queryInput);
112
1394
  if (queryResult == null) {
113
1395
  setData(null);
@@ -137,7 +1419,7 @@ function createLazyQuery(queryInput) {
137
1419
  data,
138
1420
  loading,
139
1421
  error,
140
- execute,
1422
+ execute: execute2,
141
1423
  reset
142
1424
  };
143
1425
  }
@@ -146,5 +1428,6 @@ export {
146
1428
  createQuery,
147
1429
  createMutation,
148
1430
  createLazyQuery,
1431
+ createClient2 as createClient,
149
1432
  LensProvider
150
1433
  };