@myko/ui-svelte 4.4.0 → 4.4.3

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.
Files changed (80) hide show
  1. package/{src/lib → dist}/components/ConnectionStats.svelte +8 -22
  2. package/dist/components/ConnectionStats.svelte.d.ts +10 -0
  3. package/dist/components/EntityDiffBadge.svelte +10 -0
  4. package/dist/components/EntityDiffBadge.svelte.d.ts +7 -0
  5. package/{src/lib → dist}/components/EntityDiffFields.svelte +27 -49
  6. package/dist/components/EntityDiffFields.svelte.d.ts +11 -0
  7. package/{src/lib → dist}/components/EntityDiffNode.svelte +25 -43
  8. package/dist/components/EntityDiffNode.svelte.d.ts +14 -0
  9. package/{src/lib → dist}/components/EntityDiffTree.svelte +150 -200
  10. package/dist/components/EntityDiffTree.svelte.d.ts +21 -0
  11. package/dist/components/Logs.svelte +30 -0
  12. package/dist/components/Logs.svelte.d.ts +7 -0
  13. package/dist/components/Query.svelte +21 -0
  14. package/dist/components/Query.svelte.d.ts +35 -0
  15. package/dist/components/Report.svelte +13 -0
  16. package/dist/components/Report.svelte.d.ts +32 -0
  17. package/dist/components/Search.svelte +46 -0
  18. package/dist/components/Search.svelte.d.ts +56 -0
  19. package/dist/components/ServerView.svelte +76 -0
  20. package/dist/components/ServerView.svelte.d.ts +8 -0
  21. package/dist/components/state/resolutions.d.ts +11 -0
  22. package/dist/components/state/resolutions.js +129 -0
  23. package/dist/components/state/viewstate.svelte.d.ts +60 -0
  24. package/dist/components/state/viewstate.svelte.js +259 -0
  25. package/dist/components/state/windback.svelte.d.ts +16 -0
  26. package/dist/components/state/windback.svelte.js +65 -0
  27. package/dist/components/transactions/EntityHistory.svelte +135 -0
  28. package/dist/components/transactions/EntityHistory.svelte.d.ts +10 -0
  29. package/{src/lib → dist}/components/transactions/TimeStrip.svelte +18 -25
  30. package/dist/components/transactions/TimeStrip.svelte.d.ts +3 -0
  31. package/dist/components/transactions/TransactionDetails.svelte +24 -0
  32. package/dist/components/transactions/TransactionDetails.svelte.d.ts +7 -0
  33. package/{src/lib → dist}/components/transactions/TransactionEvent.svelte +20 -32
  34. package/dist/components/transactions/TransactionEvent.svelte.d.ts +7 -0
  35. package/{src/lib → dist}/components/transactions/TransactionEventGroup.svelte +8 -13
  36. package/dist/components/transactions/TransactionEventGroup.svelte.d.ts +8 -0
  37. package/dist/components/transactions/Transactions.svelte +94 -0
  38. package/dist/components/transactions/Transactions.svelte.d.ts +8 -0
  39. package/{src/lib → dist}/components/transactions/TransactonView.svelte +3 -7
  40. package/dist/components/transactions/TransactonView.svelte.d.ts +7 -0
  41. package/{src/lib → dist}/components/windback/WindbackFrame.svelte +3 -6
  42. package/dist/components/windback/WindbackFrame.svelte.d.ts +7 -0
  43. package/dist/components/windback/index.js +1 -0
  44. package/dist/index.d.ts +18 -0
  45. package/{src/lib/index.ts → dist/index.js} +2 -15
  46. package/dist/services/svelte-client.svelte.d.ts +278 -0
  47. package/dist/services/svelte-client.svelte.js +678 -0
  48. package/dist/utils/entity-apply.d.ts +17 -0
  49. package/dist/utils/entity-apply.js +35 -0
  50. package/dist/utils/entity-diff.d.ts +40 -0
  51. package/dist/utils/entity-diff.js +57 -0
  52. package/dist/utils/entity-tree.d.ts +24 -0
  53. package/dist/utils/entity-tree.js +111 -0
  54. package/package.json +13 -9
  55. package/.prettierignore +0 -4
  56. package/.prettierrc +0 -15
  57. package/src/app.d.ts +0 -13
  58. package/src/app.html +0 -12
  59. package/src/lib/components/EntityDiffBadge.svelte +0 -18
  60. package/src/lib/components/Logs.svelte +0 -37
  61. package/src/lib/components/Query.svelte +0 -34
  62. package/src/lib/components/Report.svelte +0 -25
  63. package/src/lib/components/Search.svelte +0 -85
  64. package/src/lib/components/ServerView.svelte +0 -95
  65. package/src/lib/components/state/resolutions.ts +0 -137
  66. package/src/lib/components/state/viewstate.svelte.ts +0 -375
  67. package/src/lib/components/state/windback.svelte.ts +0 -88
  68. package/src/lib/components/transactions/EntityHistory.svelte +0 -173
  69. package/src/lib/components/transactions/TransactionDetails.svelte +0 -26
  70. package/src/lib/components/transactions/Transactions.svelte +0 -111
  71. package/src/lib/services/svelte-client.svelte.ts +0 -863
  72. package/src/lib/utils/entity-apply.ts +0 -47
  73. package/src/lib/utils/entity-diff.ts +0 -105
  74. package/src/lib/utils/entity-tree.ts +0 -130
  75. package/src/routes/+page.svelte +0 -3
  76. package/static/favicon.png +0 -0
  77. package/svelte.config.js +0 -18
  78. package/tsconfig.json +0 -13
  79. package/vite.config.ts +0 -6
  80. /package/{src/lib/components/windback/index.ts → dist/components/windback/index.d.ts} +0 -0
@@ -0,0 +1,678 @@
1
+ /**
2
+ * Svelte-friendly Myko client wrapper
3
+ *
4
+ * Provides reactive state using Svelte 5 runes and SvelteMap for efficient updates.
5
+ * Queries and reports are deduplicated - multiple calls with the same args share
6
+ * the same subscription and are only cancelled when all consumers unsubscribe.
7
+ */
8
+ import { ConnectionStatus, MykoClient } from '@myko/core';
9
+ import { SvelteMap } from 'svelte/reactivity';
10
+ import { Subject } from 'rxjs';
11
+ import { untrack } from 'svelte';
12
+ function stableStringify(value) {
13
+ const seen = new WeakSet();
14
+ try {
15
+ return JSON.stringify(value, (_key, raw) => {
16
+ if (typeof raw === 'bigint') {
17
+ return `__bigint:${raw.toString()}`;
18
+ }
19
+ if (!raw || typeof raw !== 'object') {
20
+ return raw;
21
+ }
22
+ if (seen.has(raw)) {
23
+ return '__circular__';
24
+ }
25
+ seen.add(raw);
26
+ if (Array.isArray(raw)) {
27
+ return raw;
28
+ }
29
+ const sorted = {};
30
+ for (const key of Object.keys(raw).sort()) {
31
+ sorted[key] = raw[key];
32
+ }
33
+ return sorted;
34
+ });
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ function requestKey(kind, request) {
41
+ const serialized = stableStringify(request);
42
+ return serialized ? `${kind}:${serialized}` : null;
43
+ }
44
+ /**
45
+ * Svelte-friendly Myko client
46
+ *
47
+ * Wraps MykoClient to provide reactive Svelte state with automatic lifecycle management.
48
+ */
49
+ export class SvelteMykoClient {
50
+ client;
51
+ // Command lifecycle subjects
52
+ commandSentSubject = new Subject();
53
+ commandSuccessSubject = new Subject();
54
+ commandErrorSubject = new Subject();
55
+ /** Observable of all commands when sent (before response) */
56
+ commandSent$ = this.commandSentSubject.asObservable();
57
+ /** Observable of all command successes */
58
+ commandSuccess$ = this.commandSuccessSubject.asObservable();
59
+ /** Observable of all command errors */
60
+ commandError$ = this.commandErrorSubject.asObservable();
61
+ // Reactive connection status
62
+ #connectionStatus = $state(ConnectionStatus.Disconnected);
63
+ // Reactive connection stats
64
+ #stats = $state(null);
65
+ statsSubscription = null;
66
+ constructor() {
67
+ this.client = new MykoClient();
68
+ // Sync connection status to reactive state
69
+ this.client.connectionStatus$.subscribe((status) => {
70
+ this.#connectionStatus = status;
71
+ // Start/stop stats subscription based on connection
72
+ if (status === ConnectionStatus.Connected && !this.statsSubscription) {
73
+ this.statsSubscription = this.client.stats().subscribe((stats) => {
74
+ this.#stats = stats;
75
+ });
76
+ }
77
+ else if (status === ConnectionStatus.Disconnected && this.statsSubscription) {
78
+ this.statsSubscription.unsubscribe();
79
+ this.statsSubscription = null;
80
+ this.#stats = null;
81
+ }
82
+ });
83
+ }
84
+ /** Current connection status (reactive) */
85
+ get connectionStatus() {
86
+ return this.#connectionStatus;
87
+ }
88
+ /** Whether currently connected (reactive) */
89
+ get isConnected() {
90
+ return this.#connectionStatus === ConnectionStatus.Connected;
91
+ }
92
+ /** Current connection stats (reactive, null when disconnected) */
93
+ get stats() {
94
+ return this.#stats;
95
+ }
96
+ /** Set the server address and connect */
97
+ connect(address) {
98
+ this.client.setAddress(address);
99
+ }
100
+ /**
101
+ * Enable automatic peer discovery via GetPeerServers query.
102
+ * When enabled, the client will automatically add discovered peer servers
103
+ * to the connection pool for redundancy and load balancing.
104
+ */
105
+ enablePeerDiscovery(enabled, secure = false) {
106
+ this.client.enablePeerDiscovery(enabled, secure);
107
+ }
108
+ /** Set authentication token for commands */
109
+ setToken(token) {
110
+ this.client.setToken(token);
111
+ }
112
+ /** Disconnect from the server */
113
+ disconnect() {
114
+ // Unsubscribe stats
115
+ if (this.statsSubscription) {
116
+ this.statsSubscription.unsubscribe();
117
+ this.statsSubscription = null;
118
+ this.#stats = null;
119
+ }
120
+ this.client.disconnect();
121
+ }
122
+ /**
123
+ * Create a live report subscription with automatic lifecycle management.
124
+ *
125
+ * The subscription automatically:
126
+ * - Re-subscribes when dependencies in the factory function change
127
+ * - Cleans up when the component unmounts
128
+ * - No manual release() call needed
129
+ *
130
+ * If the factory returns null/undefined, no subscription is created and value remains undefined.
131
+ *
132
+ * IMPORTANT: Must be called during component initialization (in the <script> block),
133
+ * not inside event handlers or other callbacks.
134
+ *
135
+ * @example
136
+ * ```svelte
137
+ * <script>
138
+ * let { nodeId, sessionId } = $props()
139
+ * const output = client.liveReport(() =>
140
+ * sessionId ? new BindingNodeOutputValue({ nodeId, sessionId }) : null
141
+ * )
142
+ * </script>
143
+ *
144
+ * <div>{output.value?.datagram.data}</div>
145
+ * ```
146
+ */
147
+ liveReport(factory) {
148
+ let value = $state(undefined);
149
+ let error = $state(undefined);
150
+ const reportKey = $derived.by(() => {
151
+ const report = factory();
152
+ if (!report)
153
+ return null;
154
+ return requestKey('report', report);
155
+ });
156
+ $effect(() => {
157
+ const key = reportKey;
158
+ const report = untrack(factory);
159
+ // If factory returns null/undefined, don't subscribe
160
+ if (!report) {
161
+ value = undefined;
162
+ error = undefined;
163
+ return;
164
+ }
165
+ if (!key) {
166
+ value = undefined;
167
+ error = new Error('Failed to compute stable report key');
168
+ return;
169
+ }
170
+ error = undefined;
171
+ const subscription = this.client.watchReport(report).subscribe({
172
+ next: (result) => {
173
+ value = result;
174
+ error = undefined;
175
+ },
176
+ error: (e) => {
177
+ error = e instanceof Error ? e : new Error(String(e));
178
+ }
179
+ });
180
+ return () => subscription.unsubscribe();
181
+ });
182
+ return {
183
+ get value() {
184
+ return value;
185
+ },
186
+ get error() {
187
+ return error;
188
+ }
189
+ };
190
+ }
191
+ /**
192
+ * Create a live query subscription with automatic lifecycle management.
193
+ *
194
+ * The subscription automatically:
195
+ * - Re-subscribes when dependencies in the factory function change
196
+ * - Cleans up when the component unmounts
197
+ * - No manual release() call needed
198
+ *
199
+ * If the factory returns null/undefined, no subscription is created and items map is cleared.
200
+ *
201
+ * IMPORTANT: Must be called during component initialization (in the <script> block),
202
+ * not inside event handlers or other callbacks.
203
+ *
204
+ * @example
205
+ * ```svelte
206
+ * <script>
207
+ * let { sceneId } = $props()
208
+ * const nodes = client.liveQuery(() =>
209
+ * sceneId ? new GetBindingNodesByQuery({ sceneId }) : null
210
+ * )
211
+ * </script>
212
+ *
213
+ * {#each nodes.items as [id, node] (id)}
214
+ * <div>{node.name}</div>
215
+ * {/each}
216
+ * ```
217
+ */
218
+ liveQuery(factory) {
219
+ const items = new SvelteMap();
220
+ let resolved = $state(false);
221
+ let error = $state(undefined);
222
+ const queryKey = $derived.by(() => {
223
+ const query = factory();
224
+ if (!query)
225
+ return null;
226
+ return requestKey('query', query);
227
+ });
228
+ $effect(() => {
229
+ const key = queryKey;
230
+ const query = untrack(factory);
231
+ // If factory returns null/undefined, clear and don't subscribe
232
+ if (!query) {
233
+ items.clear();
234
+ resolved = false;
235
+ error = undefined;
236
+ return;
237
+ }
238
+ if (!key) {
239
+ items.clear();
240
+ resolved = false;
241
+ error = new Error('Failed to compute stable query key');
242
+ return;
243
+ }
244
+ error = undefined;
245
+ const subscription = this.client.watchQueryDiff(query).subscribe({
246
+ next: (diff) => {
247
+ if (diff.sequence === 0n) {
248
+ items.clear();
249
+ }
250
+ for (const id of diff.deletes) {
251
+ items.delete(id);
252
+ }
253
+ for (const item of diff.upserts) {
254
+ const typedItem = item;
255
+ items.set(typedItem.id, typedItem);
256
+ }
257
+ resolved = true;
258
+ error = undefined;
259
+ },
260
+ error: (e) => {
261
+ error = e instanceof Error ? e : new Error(String(e));
262
+ }
263
+ });
264
+ return () => {
265
+ subscription.unsubscribe();
266
+ items.clear();
267
+ resolved = false;
268
+ };
269
+ });
270
+ return {
271
+ items,
272
+ get resolved() {
273
+ return resolved;
274
+ },
275
+ get error() {
276
+ return error;
277
+ }
278
+ };
279
+ }
280
+ /** Create a live view subscription with automatic lifecycle management. */
281
+ liveView(factory) {
282
+ const items = new SvelteMap();
283
+ let resolved = $state(false);
284
+ let error = $state(undefined);
285
+ const viewKey = $derived.by(() => {
286
+ const view = factory();
287
+ if (!view)
288
+ return null;
289
+ return requestKey('view', view);
290
+ });
291
+ $effect(() => {
292
+ const key = viewKey;
293
+ const view = untrack(factory);
294
+ if (!view) {
295
+ items.clear();
296
+ resolved = false;
297
+ error = undefined;
298
+ return;
299
+ }
300
+ if (!key) {
301
+ items.clear();
302
+ resolved = false;
303
+ error = new Error('Failed to compute stable view key');
304
+ return;
305
+ }
306
+ error = undefined;
307
+ const subscription = this.client.watchViewDiff(view).subscribe({
308
+ next: (diff) => {
309
+ if (diff.sequence === 0n) {
310
+ items.clear();
311
+ }
312
+ for (const id of diff.deletes) {
313
+ items.delete(id);
314
+ }
315
+ for (const item of diff.upserts) {
316
+ const typedItem = item;
317
+ items.set(typedItem.id, typedItem);
318
+ }
319
+ resolved = true;
320
+ error = undefined;
321
+ },
322
+ error: (e) => {
323
+ error = e instanceof Error ? e : new Error(String(e));
324
+ }
325
+ });
326
+ return () => {
327
+ subscription.unsubscribe();
328
+ items.clear();
329
+ resolved = false;
330
+ };
331
+ });
332
+ return {
333
+ items,
334
+ get resolved() {
335
+ return resolved;
336
+ },
337
+ get error() {
338
+ return error;
339
+ }
340
+ };
341
+ }
342
+ /**
343
+ * Create a live windowed query with automatic lifecycle management.
344
+ *
345
+ * Uses server-side query windows while exposing Svelte-native reactive state.
346
+ */
347
+ liveQueryWindowed(factory, optionsFactory) {
348
+ let items = $state([]);
349
+ let totalCount = $state(null);
350
+ let window = $state(null);
351
+ let resolved = $state(false);
352
+ let error = $state(undefined);
353
+ let setWindowFn = () => { };
354
+ const queryWindowKey = $derived.by(() => {
355
+ const query = factory();
356
+ if (!query)
357
+ return null;
358
+ const q = requestKey('query', query);
359
+ const o = stableStringify(optionsFactory?.());
360
+ if (!q)
361
+ return null;
362
+ return `${q}|options:${o ?? '__none__'}`;
363
+ });
364
+ $effect(() => {
365
+ const key = queryWindowKey;
366
+ const query = untrack(factory);
367
+ const options = untrack(() => optionsFactory?.());
368
+ if (!query) {
369
+ items = [];
370
+ totalCount = null;
371
+ window = null;
372
+ resolved = false;
373
+ error = undefined;
374
+ setWindowFn = () => { };
375
+ return;
376
+ }
377
+ if (!key) {
378
+ items = [];
379
+ totalCount = null;
380
+ window = null;
381
+ resolved = false;
382
+ error = new Error('Failed to compute stable windowed query key');
383
+ setWindowFn = () => { };
384
+ return;
385
+ }
386
+ error = undefined;
387
+ resolved = false;
388
+ const handle = this.client.watchQueryWindowed(query, options);
389
+ setWindowFn = handle.setWindow;
390
+ const itemsSub = handle.results$.subscribe({
391
+ next: (next) => {
392
+ items = next;
393
+ resolved = true;
394
+ error = undefined;
395
+ },
396
+ error: (e) => {
397
+ error = e instanceof Error ? e : new Error(String(e));
398
+ }
399
+ });
400
+ const infoSub = handle.windowInfo$.subscribe({
401
+ next: (info) => {
402
+ totalCount = info.totalCount;
403
+ window = info.window;
404
+ resolved = true;
405
+ error = undefined;
406
+ },
407
+ error: (e) => {
408
+ error = e instanceof Error ? e : new Error(String(e));
409
+ }
410
+ });
411
+ return () => {
412
+ itemsSub.unsubscribe();
413
+ infoSub.unsubscribe();
414
+ items = [];
415
+ totalCount = null;
416
+ window = null;
417
+ resolved = false;
418
+ setWindowFn = () => { };
419
+ };
420
+ });
421
+ return {
422
+ get items() {
423
+ return items;
424
+ },
425
+ get totalCount() {
426
+ return totalCount;
427
+ },
428
+ get window() {
429
+ return window;
430
+ },
431
+ get resolved() {
432
+ return resolved;
433
+ },
434
+ get error() {
435
+ return error;
436
+ },
437
+ setWindow(nextWindow) {
438
+ setWindowFn(nextWindow);
439
+ }
440
+ };
441
+ }
442
+ /** Create a live windowed view with automatic lifecycle management. */
443
+ liveViewWindowed(factory, optionsFactory) {
444
+ let items = $state([]);
445
+ let totalCount = $state(null);
446
+ let window = $state(null);
447
+ let resolved = $state(false);
448
+ let error = $state(undefined);
449
+ let setWindowFn = () => { };
450
+ const viewWindowKey = $derived.by(() => {
451
+ const view = factory();
452
+ if (!view)
453
+ return null;
454
+ const v = requestKey('view', view);
455
+ const o = stableStringify(optionsFactory?.());
456
+ if (!v)
457
+ return null;
458
+ return `${v}|options:${o ?? '__none__'}`;
459
+ });
460
+ $effect(() => {
461
+ const key = viewWindowKey;
462
+ const view = untrack(factory);
463
+ const options = untrack(() => optionsFactory?.());
464
+ if (!view) {
465
+ items = [];
466
+ totalCount = null;
467
+ window = null;
468
+ resolved = false;
469
+ error = undefined;
470
+ setWindowFn = () => { };
471
+ return;
472
+ }
473
+ if (!key) {
474
+ items = [];
475
+ totalCount = null;
476
+ window = null;
477
+ resolved = false;
478
+ error = new Error('Failed to compute stable windowed view key');
479
+ setWindowFn = () => { };
480
+ return;
481
+ }
482
+ error = undefined;
483
+ resolved = false;
484
+ const handle = this.client.watchViewWindowed(view, options);
485
+ setWindowFn = handle.setWindow;
486
+ const itemsSub = handle.results$.subscribe({
487
+ next: (next) => {
488
+ items = next;
489
+ resolved = true;
490
+ error = undefined;
491
+ },
492
+ error: (e) => {
493
+ error = e instanceof Error ? e : new Error(String(e));
494
+ }
495
+ });
496
+ const infoSub = handle.windowInfo$.subscribe({
497
+ next: (info) => {
498
+ totalCount = info.totalCount;
499
+ window = info.window;
500
+ resolved = true;
501
+ error = undefined;
502
+ },
503
+ error: (e) => {
504
+ error = e instanceof Error ? e : new Error(String(e));
505
+ }
506
+ });
507
+ return () => {
508
+ itemsSub.unsubscribe();
509
+ infoSub.unsubscribe();
510
+ items = [];
511
+ totalCount = null;
512
+ window = null;
513
+ resolved = false;
514
+ setWindowFn = () => { };
515
+ };
516
+ });
517
+ return {
518
+ get items() {
519
+ return items;
520
+ },
521
+ get totalCount() {
522
+ return totalCount;
523
+ },
524
+ get window() {
525
+ return window;
526
+ },
527
+ get resolved() {
528
+ return resolved;
529
+ },
530
+ get error() {
531
+ return error;
532
+ },
533
+ setWindow(nextWindow) {
534
+ setWindowFn(nextWindow);
535
+ }
536
+ };
537
+ }
538
+ /**
539
+ * Watch a query with Observable-based updates.
540
+ *
541
+ * Returns an Observable that emits the full array of items whenever
542
+ * the result set changes.
543
+ *
544
+ * @deprecated Use `liveQuery()` for component usage. This method requires manual
545
+ * subscription management and doesn't integrate with Svelte's lifecycle.
546
+ * Only use for non-component contexts (e.g., context classes).
547
+ */
548
+ watchQuery(queryFactory) {
549
+ return this.client.watchQuery(queryFactory);
550
+ }
551
+ /**
552
+ * Watch a view with Observable-based updates.
553
+ *
554
+ * @deprecated Use `liveView()` for component usage. Kept for backward compatibility
555
+ * with existing service code paths that still consume observables.
556
+ */
557
+ watchView(viewFactory) {
558
+ return this.client.watchView(viewFactory);
559
+ }
560
+ /**
561
+ * Watch a query with optional server-side windowing.
562
+ *
563
+ * @deprecated Use `liveQuery()` (or `liveQueryWindowed()` for runes-native windowing)
564
+ * in component/service rune contexts.
565
+ */
566
+ watchQueryWithOptions(queryFactory, options) {
567
+ return this.client.watchQuery(queryFactory, options);
568
+ }
569
+ /**
570
+ * Watch a view with optional server-side windowing.
571
+ *
572
+ * @deprecated Use `liveView()` in component/service rune contexts.
573
+ */
574
+ watchViewWithOptions(viewFactory, options) {
575
+ return this.client.watchView(viewFactory, options);
576
+ }
577
+ /**
578
+ * Watch a query and mutate its server-side window without re-subscribing.
579
+ *
580
+ * @deprecated Use `liveQueryWindowed()` in component/service rune contexts.
581
+ */
582
+ watchQueryWindowed(queryFactory, options) {
583
+ return this.client.watchQueryWindowed(queryFactory, options);
584
+ }
585
+ /**
586
+ * Watch a view and mutate its server-side window without re-subscribing.
587
+ *
588
+ * @deprecated Use `liveView()` in component/service rune contexts.
589
+ */
590
+ watchViewWindowed(viewFactory, options) {
591
+ return this.client.watchViewWindowed(viewFactory, options);
592
+ }
593
+ /**
594
+ * Watch a report with Observable-based updates.
595
+ *
596
+ * Returns an Observable that emits whenever the report result changes.
597
+ *
598
+ * @deprecated Use `liveReport()` for component usage. This method requires manual
599
+ * subscription management and doesn't integrate with Svelte's lifecycle.
600
+ * Only use for non-component contexts (e.g., context classes).
601
+ */
602
+ watchReport(reportFactory) {
603
+ return this.client.watchReport(reportFactory);
604
+ }
605
+ /**
606
+ * Send a command and wait for the response.
607
+ *
608
+ * Emits to commandSuccess$ or commandError$ observables for generic handling.
609
+ *
610
+ * @example
611
+ * ```svelte
612
+ * <script>
613
+ * async function deleteMachine(id: string) {
614
+ * const result = await myko.sendCommand(new DeleteMachine({ id }))
615
+ * console.log('Deleted:', result)
616
+ * }
617
+ * </script>
618
+ * ```
619
+ */
620
+ async sendCommand(commandFactory) {
621
+ const commandId = commandFactory.commandId;
622
+ this.commandSentSubject.next({ commandId });
623
+ try {
624
+ const response = await this.client.sendCommand(commandFactory);
625
+ this.commandSuccessSubject.next({ commandId, response });
626
+ return response;
627
+ }
628
+ catch (e) {
629
+ const error = e instanceof Error ? e : new Error(String(e));
630
+ this.commandErrorSubject.next({ commandId, error });
631
+ throw e;
632
+ }
633
+ }
634
+ /** Access the underlying MykoClient for advanced use cases */
635
+ get raw() {
636
+ return this.client;
637
+ }
638
+ /** Measure round-trip latency */
639
+ ping() {
640
+ return this.client.ping();
641
+ }
642
+ /** Observable of all errors from the server */
643
+ get errors() {
644
+ return this.client.errors$;
645
+ }
646
+ /**
647
+ * Watch command completion status.
648
+ * Note: This is a compatibility shim - the underlying MykoClient doesn't track
649
+ * command completions the same way as the legacy WSMClient.
650
+ */
651
+ watchCommandStatus() {
652
+ // Return empty observable - command tracking is handled via sendCommand promises
653
+ return new Subject().asObservable();
654
+ }
655
+ /**
656
+ * Clear a command completion from tracking.
657
+ * Note: This is a compatibility shim - no-op in the new client.
658
+ */
659
+ clearCommandCompletion(_tx) {
660
+ // No-op - command tracking is handled via sendCommand promises
661
+ }
662
+ }
663
+ /** Global singleton client instance (auto-initialized) */
664
+ export const myko = new SvelteMykoClient();
665
+ // HMR cleanup - disconnect old client when module is hot-reloaded
666
+ if (import.meta.hot) {
667
+ import.meta.hot.dispose(() => {
668
+ myko.disconnect();
669
+ });
670
+ }
671
+ /** Get the global MykoClient instance */
672
+ export function getMykoClient() {
673
+ return myko;
674
+ }
675
+ /** Create a new SvelteMykoClient instance (non-singleton, for advanced use) */
676
+ export function createMykoClient() {
677
+ return new SvelteMykoClient();
678
+ }
@@ -0,0 +1,17 @@
1
+ import type { ExportedEntity, DiffStatus, FieldDiff } from './entity-diff.js';
2
+ export interface ApplyPlan {
3
+ toSet: ExportedEntity[];
4
+ toDel: ExportedEntity[];
5
+ }
6
+ /**
7
+ * Compute the minimal SET/DEL operations to apply an import.
8
+ *
9
+ * @param diffs - Flat diff map from diffEntityLists()
10
+ * @param allFkFields - All FK field names used in BelongsTo relationships (for cascade detection)
11
+ * @param excluded - Optional set of "Type:id" keys to skip (user chose to keep current version)
12
+ */
13
+ export declare function computeApplyPlan(diffs: Map<string, {
14
+ status: DiffStatus;
15
+ fields?: FieldDiff[];
16
+ entity: ExportedEntity;
17
+ }>, allFkFields: string[], excluded?: Set<string>): ApplyPlan;