tutuca 0.9.98 → 0.9.100

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.
@@ -0,0 +1,2444 @@
1
+ // src/components/data/data.js
2
+ import { component as component3, html as html3, IMap as IMap2, ISet as ISet2, List as List2, OMap as OMap2, OrderedSet as OrderedSet2 } from "tutuca";
3
+
4
+ // src/components/data/immutable-inspector.js
5
+ import { component as component2, html as html2, IMap, ISet, List, OMap, OrderedSet, Record, Seq, Stack } from "tutuca";
6
+
7
+ // src/components/data/json.js
8
+ import { component, html } from "tutuca";
9
+ var JsonNull = component({
10
+ name: "JsonNull",
11
+ fields: {},
12
+ view: html`<span
13
+ class="font-mono text-sm leading-tight text-warning italic"
14
+ >null</span
15
+ >`
16
+ });
17
+ var JsonBoolean = component({
18
+ name: "JsonBoolean",
19
+ fields: { value: false },
20
+ methods: {
21
+ text() {
22
+ return this.value ? "true" : "false";
23
+ },
24
+ cssClass() {
25
+ const base = "font-mono text-sm leading-tight";
26
+ return `${base} ${this.value ? "text-success" : "text-warning"}`;
27
+ }
28
+ },
29
+ view: html`<span :class="$cssClass" @text="$text"></span>`,
30
+ views: {
31
+ _margauiClasses: html`<p class="text-success text-warning"></p>`
32
+ }
33
+ });
34
+ var JsonNumber = component({
35
+ name: "JsonNumber",
36
+ fields: { value: 0 },
37
+ methods: {
38
+ text() {
39
+ return String(this.value);
40
+ }
41
+ },
42
+ view: html`<span
43
+ class="font-mono text-sm leading-tight text-info"
44
+ @text="$text"
45
+ ></span>`
46
+ });
47
+ var JsonString = component({
48
+ name: "JsonString",
49
+ fields: { value: "" },
50
+ methods: {
51
+ text() {
52
+ return JSON.stringify(this.value ?? "");
53
+ }
54
+ },
55
+ view: html`<span
56
+ class="font-mono text-sm leading-tight inline-block max-w-xs truncate align-bottom"
57
+ :title=".value"
58
+ @text="$text"
59
+ ></span>`
60
+ });
61
+ var compositeFields = {
62
+ items: [],
63
+ isExpanded: false,
64
+ itemsPerPage: 10,
65
+ currentPage: 0
66
+ };
67
+ var compositeMethods = {
68
+ arrowText() {
69
+ return this.isExpanded ? "▼" : "▶";
70
+ },
71
+ isItemsEmpty() {
72
+ return this.items.size === 0;
73
+ },
74
+ pageCount() {
75
+ return Math.max(1, Math.ceil(this.items.size / this.itemsPerPage));
76
+ },
77
+ pageStart() {
78
+ return this.currentPage * this.itemsPerPage;
79
+ },
80
+ pageEnd() {
81
+ return Math.min(this.items.size, this.pageStart() + this.itemsPerPage);
82
+ },
83
+ hasPagination() {
84
+ return this.items.size > this.itemsPerPage;
85
+ },
86
+ showPagination() {
87
+ return this.isExpanded && this.hasPagination();
88
+ },
89
+ cannotPrevPage() {
90
+ return this.currentPage <= 0;
91
+ },
92
+ cannotNextPage() {
93
+ return this.currentPage >= this.pageCount() - 1;
94
+ },
95
+ pageIndicatorText() {
96
+ return `${this.currentPage + 1} / ${this.pageCount()}`;
97
+ },
98
+ prevPage() {
99
+ return this.currentPage > 0 ? this.setCurrentPage(this.currentPage - 1) : this;
100
+ },
101
+ nextPage() {
102
+ return this.currentPage < this.pageCount() - 1 ? this.setCurrentPage(this.currentPage + 1) : this;
103
+ }
104
+ };
105
+ var compositeAlter = {
106
+ getPageRange() {
107
+ return { start: this.pageStart(), end: this.pageEnd() };
108
+ }
109
+ };
110
+ function makeCompositeView({
111
+ typeClass = "",
112
+ borderClass = "border-base-content/10",
113
+ toggleHandler = "$toggleIsExpanded"
114
+ } = {}) {
115
+ return html`<span class="font-mono text-sm leading-tight inline-block">
116
+ <span class="inline-flex items-center gap-2">
117
+ <button
118
+ type="button"
119
+ class="cursor-pointer text-base-content/70 hover:text-base-content inline-flex items-center gap-1"
120
+ :disabled="$isItemsEmpty"
121
+ @on.click="${toggleHandler}"
122
+ >
123
+ <span @text="$arrowText"></span>
124
+ <span class="${typeClass}" @text="$typeText"></span>
125
+ <span @text="$countText"></span>
126
+ </button>
127
+ <div @show="$showPagination" class="join">
128
+ <button
129
+ type="button"
130
+ class="join-item btn btn-xs"
131
+ :disabled="$cannotPrevPage"
132
+ @on.click="$prevPage"
133
+ >
134
+ «
135
+ </button>
136
+ <span
137
+ class="join-item badge font-mono text-xs"
138
+ @text="$pageIndicatorText"
139
+ ></span>
140
+ <button
141
+ type="button"
142
+ class="join-item btn btn-xs"
143
+ :disabled="$cannotNextPage"
144
+ @on.click="$nextPage"
145
+ >
146
+ »
147
+ </button>
148
+ </div>
149
+ </span>
150
+ <div
151
+ @show=".isExpanded"
152
+ class="ml-1 flex flex-col gap-0.5 border-l ${borderClass} pl-2 mt-0.5"
153
+ >
154
+ <x render-each=".items" loop-with="getPageRange"></x>
155
+ </div>
156
+ </span>`;
157
+ }
158
+ var compositeView = makeCompositeView();
159
+ var JsonArray = component({
160
+ name: "JsonArray",
161
+ fields: compositeFields,
162
+ methods: {
163
+ ...compositeMethods,
164
+ typeText() {
165
+ return "Array";
166
+ },
167
+ countText() {
168
+ return `(${this.items.size})`;
169
+ }
170
+ },
171
+ alter: compositeAlter,
172
+ view: compositeView
173
+ });
174
+ var JsonObject = component({
175
+ name: "JsonObject",
176
+ fields: compositeFields,
177
+ methods: {
178
+ ...compositeMethods,
179
+ typeText() {
180
+ return "Object";
181
+ },
182
+ countText() {
183
+ return `{${this.items.size}}`;
184
+ }
185
+ },
186
+ alter: compositeAlter,
187
+ view: compositeView
188
+ });
189
+ var JsonProperty = component({
190
+ name: "JsonProperty",
191
+ fields: {
192
+ key: "",
193
+ child: null
194
+ },
195
+ view: html`<div class="flex items-center gap-2 leading-tight">
196
+ <span class="text-base-content/60 font-mono text-sm" @text=".key"></span>
197
+ <span class="text-base-content/30">:</span>
198
+ <x render=".child"></x>
199
+ </div>`
200
+ });
201
+ var JsonViewer = component({
202
+ name: "JsonViewer",
203
+ fields: {
204
+ value: null
205
+ },
206
+ methods: {
207
+ toggleIsExpanded() {
208
+ return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
209
+ }
210
+ },
211
+ statics: {
212
+ fromData(data) {
213
+ return this.make({ value: classifyData(data) });
214
+ }
215
+ },
216
+ view: html`<span class="contents"><x render=".value"></x></span>`
217
+ });
218
+ function classifyJson(data, recurse = classifyJson) {
219
+ if (data === null || data === undefined)
220
+ return JsonNull.make({});
221
+ const t = typeof data;
222
+ if (t === "boolean")
223
+ return JsonBoolean.make({ value: data });
224
+ if (t === "number")
225
+ return JsonNumber.make({ value: data });
226
+ if (t === "string")
227
+ return JsonString.make({ value: data });
228
+ if (Array.isArray(data)) {
229
+ const items = data.map((v, i) => JsonProperty.make({ key: String(i), child: recurse(v) }));
230
+ return JsonArray.make({ items });
231
+ }
232
+ if (t === "object") {
233
+ const proto = Object.getPrototypeOf(data);
234
+ if (proto === Object.prototype || proto === null) {
235
+ const items = Object.entries(data).map(([k, v]) => JsonProperty.make({ key: k, child: recurse(v) }));
236
+ return JsonObject.make({ items });
237
+ }
238
+ }
239
+ return null;
240
+ }
241
+ function classifyData(data, recurse = classifyData) {
242
+ if (data === null || data === undefined)
243
+ return JsonNull.make({});
244
+ const t = typeof data;
245
+ if (t === "boolean")
246
+ return JsonBoolean.make({ value: data });
247
+ if (t === "number")
248
+ return JsonNumber.make({ value: data });
249
+ if (t === "string")
250
+ return JsonString.make({ value: data });
251
+ if (Array.isArray(data)) {
252
+ const items = data.map((v, i) => JsonProperty.make({ key: String(i), child: recurse(v) }));
253
+ return JsonArray.make({ items });
254
+ }
255
+ if (t === "object") {
256
+ const items = Object.entries(data).map(([k, v]) => JsonProperty.make({ key: k, child: recurse(v) }));
257
+ return JsonObject.make({ items });
258
+ }
259
+ return JsonNull.make({});
260
+ }
261
+ function chain(...classifiers) {
262
+ const recurse = (data) => {
263
+ for (const c of classifiers) {
264
+ const result = c(data, recurse);
265
+ if (result != null)
266
+ return result;
267
+ }
268
+ return null;
269
+ };
270
+ return recurse;
271
+ }
272
+ function getComponents() {
273
+ return [
274
+ JsonViewer,
275
+ JsonProperty,
276
+ JsonNull,
277
+ JsonBoolean,
278
+ JsonNumber,
279
+ JsonString,
280
+ JsonArray,
281
+ JsonObject
282
+ ];
283
+ }
284
+
285
+ // src/components/data/immutable-inspector.js
286
+ var immutableContainerView = makeCompositeView({
287
+ typeClass: "text-accent",
288
+ borderClass: "border-accent"
289
+ });
290
+ function fmtMapKey(k) {
291
+ if (k === null || k === undefined)
292
+ return String(k);
293
+ const t = typeof k;
294
+ if (t === "string" || t === "number" || t === "boolean")
295
+ return String(k);
296
+ if (List.isList(k))
297
+ return `List[${k.size}]`;
298
+ if (OMap.isOrderedMap(k))
299
+ return `OrderedMap[${k.size}]`;
300
+ if (IMap.isMap(k))
301
+ return `Map[${k.size}]`;
302
+ if (OrderedSet.isOrderedSet(k))
303
+ return `OrderedSet[${k.size}]`;
304
+ if (ISet.isSet(k))
305
+ return `Set[${k.size}]`;
306
+ return String(k);
307
+ }
308
+ var ImEntry = component2({
309
+ name: "ImEntry",
310
+ fields: { key: "", child: null, showKey: true },
311
+ view: html2`<div class="flex items-center gap-2 leading-tight">
312
+ <span
313
+ @show=".showKey"
314
+ class="text-base-content/60 font-mono text-sm"
315
+ @text=".key"
316
+ ></span>
317
+ <span @show=".showKey" class="text-base-content/30">:</span>
318
+ <x render=".child"></x>
319
+ </div>`
320
+ });
321
+ var ImList = component2({
322
+ name: "ImList",
323
+ fields: compositeFields,
324
+ methods: {
325
+ ...compositeMethods,
326
+ typeText() {
327
+ return "List";
328
+ },
329
+ countText() {
330
+ return `[${this.items.size}]`;
331
+ }
332
+ },
333
+ alter: compositeAlter,
334
+ statics: {
335
+ fromData(list, recurse) {
336
+ const items = list.toArray().map((v, i) => ImEntry.make({ key: String(i), child: recurse(v) }));
337
+ return this.make({ items });
338
+ }
339
+ },
340
+ view: immutableContainerView
341
+ });
342
+ var ImStack = component2({
343
+ name: "ImStack",
344
+ fields: compositeFields,
345
+ methods: {
346
+ ...compositeMethods,
347
+ typeText() {
348
+ return "Stack";
349
+ },
350
+ countText() {
351
+ return `[${this.items.size}]`;
352
+ }
353
+ },
354
+ alter: compositeAlter,
355
+ statics: {
356
+ fromData(stack, recurse) {
357
+ const items = stack.toArray().map((v, i) => ImEntry.make({ key: String(i), child: recurse(v) }));
358
+ return this.make({ items });
359
+ }
360
+ },
361
+ view: immutableContainerView
362
+ });
363
+ var ImMap = component2({
364
+ name: "ImMap",
365
+ fields: compositeFields,
366
+ methods: {
367
+ ...compositeMethods,
368
+ typeText() {
369
+ return "Map";
370
+ },
371
+ countText() {
372
+ return `{${this.items.size}}`;
373
+ }
374
+ },
375
+ alter: compositeAlter,
376
+ statics: {
377
+ fromData(map, recurse) {
378
+ const items = [];
379
+ map.forEach((v, k) => {
380
+ items.push(ImEntry.make({ key: fmtMapKey(k), child: recurse(v) }));
381
+ });
382
+ return this.make({ items });
383
+ }
384
+ },
385
+ view: immutableContainerView
386
+ });
387
+ var ImOMap = component2({
388
+ name: "ImOMap",
389
+ fields: compositeFields,
390
+ methods: {
391
+ ...compositeMethods,
392
+ typeText() {
393
+ return "OrderedMap";
394
+ },
395
+ countText() {
396
+ return `{${this.items.size}}`;
397
+ }
398
+ },
399
+ alter: compositeAlter,
400
+ statics: {
401
+ fromData(map, recurse) {
402
+ const items = [];
403
+ map.forEach((v, k) => {
404
+ items.push(ImEntry.make({ key: fmtMapKey(k), child: recurse(v) }));
405
+ });
406
+ return this.make({ items });
407
+ }
408
+ },
409
+ view: immutableContainerView
410
+ });
411
+ var ImSet = component2({
412
+ name: "ImSet",
413
+ fields: compositeFields,
414
+ methods: {
415
+ ...compositeMethods,
416
+ typeText() {
417
+ return "Set";
418
+ },
419
+ countText() {
420
+ return `{${this.items.size}}`;
421
+ }
422
+ },
423
+ alter: compositeAlter,
424
+ statics: {
425
+ fromData(set, recurse) {
426
+ const items = set.toArray().map((v) => ImEntry.make({ key: "", showKey: false, child: recurse(v) }));
427
+ return this.make({ items });
428
+ }
429
+ },
430
+ view: immutableContainerView
431
+ });
432
+ var ImOSet = component2({
433
+ name: "ImOSet",
434
+ fields: compositeFields,
435
+ methods: {
436
+ ...compositeMethods,
437
+ typeText() {
438
+ return "OrderedSet";
439
+ },
440
+ countText() {
441
+ return `{${this.items.size}}`;
442
+ }
443
+ },
444
+ alter: compositeAlter,
445
+ statics: {
446
+ fromData(set, recurse) {
447
+ const items = set.toArray().map((v) => ImEntry.make({ key: "", showKey: false, child: recurse(v) }));
448
+ return this.make({ items });
449
+ }
450
+ },
451
+ view: immutableContainerView
452
+ });
453
+ var ImRecord = component2({
454
+ name: "ImRecord",
455
+ fields: { ...compositeFields, recordName: "" },
456
+ methods: {
457
+ ...compositeMethods,
458
+ typeText() {
459
+ return this.recordName ? `Record ${this.recordName}` : "Record";
460
+ },
461
+ countText() {
462
+ return `{${this.items.size}}`;
463
+ }
464
+ },
465
+ alter: compositeAlter,
466
+ statics: {
467
+ fromData(rec, recurse) {
468
+ const recordName = rec._name || rec.constructor.name || "Record";
469
+ const items = [];
470
+ rec.toSeq().forEach((v, k) => {
471
+ items.push(ImEntry.make({ key: String(k), child: recurse(v) }));
472
+ });
473
+ return this.make({ recordName, items });
474
+ }
475
+ },
476
+ view: immutableContainerView
477
+ });
478
+ var ImRange = component2({
479
+ name: "ImRange",
480
+ fields: { rangeText: "" },
481
+ statics: {
482
+ fromData(seq) {
483
+ const { _start: start, _end: end, _step: step } = seq;
484
+ const rangeText = step === 1 ? `${start}…${end}` : `${start}…${end} by ${step}`;
485
+ return this.make({ rangeText });
486
+ }
487
+ },
488
+ view: html2`<span
489
+ class="font-mono text-sm leading-tight inline-flex items-center gap-1"
490
+ >
491
+ <span class="font-mono text-accent">Range</span>
492
+ <span class="text-base-content/70" @text=".rangeText"></span>
493
+ </span>`
494
+ });
495
+ var ImInspector = component2({
496
+ name: "ImInspector",
497
+ fields: { value: null },
498
+ methods: {
499
+ toggleIsExpanded() {
500
+ return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
501
+ }
502
+ },
503
+ statics: {
504
+ fromData(data) {
505
+ return this.make({ value: dispatch(data) });
506
+ }
507
+ },
508
+ view: html2`<span class="contents"><x render=".value"></x></span>`
509
+ });
510
+ function classifyImmutable(data, recurse) {
511
+ if (Seq.isSeq(data) && data._start !== undefined && data._end !== undefined && data._step !== undefined) {
512
+ return ImRange.Class.fromData(data, recurse);
513
+ }
514
+ if (Record.isRecord(data))
515
+ return ImRecord.Class.fromData(data, recurse);
516
+ if (List.isList(data))
517
+ return ImList.Class.fromData(data, recurse);
518
+ if (Stack.isStack(data))
519
+ return ImStack.Class.fromData(data, recurse);
520
+ if (OMap.isOrderedMap(data))
521
+ return ImOMap.Class.fromData(data, recurse);
522
+ if (IMap.isMap(data))
523
+ return ImMap.Class.fromData(data, recurse);
524
+ if (OrderedSet.isOrderedSet(data))
525
+ return ImOSet.Class.fromData(data, recurse);
526
+ if (ISet.isSet(data))
527
+ return ImSet.Class.fromData(data, recurse);
528
+ return null;
529
+ }
530
+ var dispatch = chain(classifyImmutable, classifyData);
531
+ function getComponents2() {
532
+ return [
533
+ ImInspector,
534
+ ImEntry,
535
+ ImList,
536
+ ImStack,
537
+ ImMap,
538
+ ImOMap,
539
+ ImSet,
540
+ ImOSet,
541
+ ImRecord,
542
+ ImRange,
543
+ ...getComponents()
544
+ ];
545
+ }
546
+
547
+ // src/components/data/data.js
548
+ var JsUndefined = component3({
549
+ name: "JsUndefined",
550
+ fields: {},
551
+ view: html3`<span
552
+ class="font-mono text-sm leading-tight text-warning italic"
553
+ >undefined</span
554
+ >`
555
+ });
556
+ var JsBigInt = component3({
557
+ name: "JsBigInt",
558
+ fields: { value: "" },
559
+ methods: {
560
+ text() {
561
+ return `${this.value}n`;
562
+ }
563
+ },
564
+ view: html3`<span
565
+ class="font-mono text-sm leading-tight text-info"
566
+ @text="$text"
567
+ ></span>`
568
+ });
569
+ var JsSymbol = component3({
570
+ name: "JsSymbol",
571
+ fields: { description: "" },
572
+ view: html3`<span class="font-mono text-sm leading-tight"
573
+ ><span class="text-info">Symbol</span
574
+ ><span class="text-base-content/40">: </span
575
+ ><span @text=".description"></span
576
+ ></span>`
577
+ });
578
+ var JsFunction = component3({
579
+ name: "JsFunction",
580
+ fields: { name: "", kind: "function" },
581
+ methods: {
582
+ hasIdentifier() {
583
+ return this.kind !== "arrow" && this.name !== "";
584
+ },
585
+ prefixText() {
586
+ if (this.kind === "arrow")
587
+ return "() => { … }";
588
+ if (this.kind === "class")
589
+ return this.name ? "class " : "class { … }";
590
+ return this.name ? "function " : "function () { … }";
591
+ },
592
+ suffixText() {
593
+ if (this.kind === "class")
594
+ return " { … }";
595
+ return "() { … }";
596
+ }
597
+ },
598
+ view: html3`<span class="font-mono text-sm leading-tight"
599
+ ><span class="text-base-content/40" @text="$prefixText"></span
600
+ ><span
601
+ class="text-info"
602
+ @show="$hasIdentifier"
603
+ @text=".name"
604
+ ></span
605
+ ><span
606
+ class="text-base-content/40"
607
+ @show="$hasIdentifier"
608
+ @text="$suffixText"
609
+ ></span
610
+ ></span>`
611
+ });
612
+ var JsDate = component3({
613
+ name: "JsDate",
614
+ fields: { value: "" },
615
+ view: html3`<span class="font-mono text-sm leading-tight"
616
+ ><span class="text-info">Date</span
617
+ ><span class="text-base-content/40">: </span
618
+ ><span @text=".value"></span
619
+ ></span>`
620
+ });
621
+ var JsRegExp = component3({
622
+ name: "JsRegExp",
623
+ fields: { value: "" },
624
+ view: html3`<span
625
+ class="font-mono text-sm leading-tight text-error"
626
+ @text=".value"
627
+ ></span>`
628
+ });
629
+ var JsError = component3({
630
+ name: "JsError",
631
+ fields: { errorName: "Error", message: "" },
632
+ view: html3`<span class="font-mono text-sm leading-tight"
633
+ ><span class="text-info" @text=".errorName"></span
634
+ ><span class="text-base-content/40">: </span
635
+ ><span
636
+ class="inline-block max-w-xs truncate align-bottom"
637
+ :title=".message"
638
+ @text=".message"
639
+ ></span
640
+ ></span>`
641
+ });
642
+ var JsSetItem = component3({
643
+ name: "JsSetItem",
644
+ fields: { child: null },
645
+ view: html3`<div class="flex items-center gap-2 leading-tight">
646
+ <x render=".child"></x>
647
+ </div>`
648
+ });
649
+ function fmtAnyKey(k) {
650
+ if (k === null)
651
+ return "null";
652
+ if (k === undefined)
653
+ return "undefined";
654
+ const t = typeof k;
655
+ if (t === "string" || t === "number" || t === "boolean" || t === "bigint")
656
+ return String(k);
657
+ if (t === "symbol")
658
+ return String(k);
659
+ if (t === "function")
660
+ return `ƒ ${k.name || "(anonymous)"}()`;
661
+ if (Array.isArray(k))
662
+ return `Array(${k.length})`;
663
+ if (k instanceof Map)
664
+ return `Map(${k.size})`;
665
+ if (k instanceof Set)
666
+ return `Set(${k.size})`;
667
+ if (k instanceof Date)
668
+ return k.toISOString();
669
+ if (List2.isList(k))
670
+ return `List[${k.size}]`;
671
+ if (OMap2.isOrderedMap(k))
672
+ return `OrderedMap[${k.size}]`;
673
+ if (IMap2.isMap(k))
674
+ return `Map[${k.size}]`;
675
+ if (OrderedSet2.isOrderedSet(k))
676
+ return `OrderedSet[${k.size}]`;
677
+ if (ISet2.isSet(k))
678
+ return `Set[${k.size}]`;
679
+ const ctor = k.constructor?.name;
680
+ return ctor && ctor !== "Object" ? `${ctor} {…}` : Object.prototype.toString.call(k);
681
+ }
682
+ var JsMap = component3({
683
+ name: "JsMap",
684
+ fields: compositeFields,
685
+ methods: {
686
+ ...compositeMethods,
687
+ typeText() {
688
+ return "Map";
689
+ },
690
+ countText() {
691
+ return `(${this.items.size})`;
692
+ }
693
+ },
694
+ alter: compositeAlter,
695
+ statics: {
696
+ fromData(map, recurse) {
697
+ const items = [];
698
+ map.forEach((v, k) => {
699
+ items.push(JsonProperty.make({ key: fmtAnyKey(k), child: recurse(v) }));
700
+ });
701
+ return this.make({ items });
702
+ }
703
+ },
704
+ view: compositeView
705
+ });
706
+ var JsSet = component3({
707
+ name: "JsSet",
708
+ fields: compositeFields,
709
+ methods: {
710
+ ...compositeMethods,
711
+ typeText() {
712
+ return "Set";
713
+ },
714
+ countText() {
715
+ return `(${this.items.size})`;
716
+ }
717
+ },
718
+ alter: compositeAlter,
719
+ statics: {
720
+ fromData(set, recurse) {
721
+ const items = [...set].map((v) => JsSetItem.make({ child: recurse(v) }));
722
+ return this.make({ items });
723
+ }
724
+ },
725
+ view: compositeView
726
+ });
727
+ var JsClassInstance = component3({
728
+ name: "JsClassInstance",
729
+ fields: { ...compositeFields, className: "Object" },
730
+ methods: {
731
+ ...compositeMethods,
732
+ typeText() {
733
+ return this.className;
734
+ },
735
+ countText() {
736
+ return `{${this.items.size}}`;
737
+ }
738
+ },
739
+ alter: compositeAlter,
740
+ statics: {
741
+ fromData(obj, recurse) {
742
+ const className = obj.constructor?.name || "Object";
743
+ const items = [];
744
+ try {
745
+ for (const [k, v] of Object.entries(obj)) {
746
+ items.push(JsonProperty.make({ key: k, child: recurse(v) }));
747
+ }
748
+ } catch (_e) {}
749
+ return this.make({ className, items });
750
+ }
751
+ },
752
+ view: compositeView
753
+ });
754
+ function classifyJsExtra(data, recurse) {
755
+ if (data === undefined)
756
+ return JsUndefined.make({});
757
+ const t = typeof data;
758
+ if (t === "symbol")
759
+ return JsSymbol.make({ description: data.description ?? "" });
760
+ if (t === "function") {
761
+ let kind = "function";
762
+ const src = String(data);
763
+ if (/^(?:async\s+)?class\s/.test(src))
764
+ kind = "class";
765
+ else if (!/^(?:async\s+)?function\b/.test(src) && /=>/.test(src))
766
+ kind = "arrow";
767
+ return JsFunction.make({ name: data.name ?? "", kind });
768
+ }
769
+ if (t === "bigint")
770
+ return JsBigInt.make({ value: String(data) });
771
+ if (t !== "object" || data === null)
772
+ return null;
773
+ if (data instanceof Date)
774
+ return JsDate.make({ value: data.toISOString() });
775
+ if (data instanceof RegExp)
776
+ return JsRegExp.make({ value: String(data) });
777
+ if (data instanceof Error)
778
+ return JsError.make({
779
+ errorName: data.name ?? "Error",
780
+ message: data.message ?? ""
781
+ });
782
+ if (data instanceof Map)
783
+ return JsMap.Class.fromData(data, recurse);
784
+ if (data instanceof Set)
785
+ return JsSet.Class.fromData(data, recurse);
786
+ const proto = Object.getPrototypeOf(data);
787
+ if (proto !== Object.prototype && proto !== null) {
788
+ return JsClassInstance.Class.fromData(data, recurse);
789
+ }
790
+ return null;
791
+ }
792
+ var dispatch2 = chain(classifyImmutable, classifyJsExtra, classifyJson);
793
+ var DataInspector = component3({
794
+ name: "DataInspector",
795
+ fields: { value: null },
796
+ methods: {
797
+ toggleIsExpanded() {
798
+ return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
799
+ }
800
+ },
801
+ statics: {
802
+ fromData(data) {
803
+ return this.make({ value: dispatch2(data) });
804
+ }
805
+ },
806
+ view: html3`<span class="contents"><x render=".value"></x></span>`
807
+ });
808
+ function getComponents3() {
809
+ return [
810
+ DataInspector,
811
+ JsUndefined,
812
+ JsBigInt,
813
+ JsSymbol,
814
+ JsFunction,
815
+ JsDate,
816
+ JsRegExp,
817
+ JsError,
818
+ JsMap,
819
+ JsSet,
820
+ JsClassInstance,
821
+ JsSetItem,
822
+ ...getComponents2()
823
+ ];
824
+ }
825
+
826
+ // src/components/data/json-schema.js
827
+ import { component as component4, html as html4 } from "tutuca";
828
+ var schemaNodeFields = {
829
+ ...compositeFields,
830
+ typeLabel: "",
831
+ badges: [],
832
+ title: "",
833
+ description: "",
834
+ deprecated: false
835
+ };
836
+ function schemaNodeMethods(extra = {}) {
837
+ return {
838
+ ...compositeMethods,
839
+ typeText() {
840
+ return this.typeLabel;
841
+ },
842
+ ...extra
843
+ };
844
+ }
845
+ var GRID_BODY = html4`<div
846
+ @show=".isExpanded"
847
+ class="ml-1 grid gap-x-2 gap-y-0.5 items-baseline border-l border-base-content/10 pl-2 mt-0.5"
848
+ style="grid-template-columns: auto 1fr"
849
+ >
850
+ <x render-each=".items" loop-with="getPageRange"></x>
851
+ </div>`;
852
+ var LIST_BODY = html4`<div
853
+ @show=".isExpanded"
854
+ class="ml-1 flex flex-col gap-0.5 border-l border-base-content/10 pl-2 mt-0.5"
855
+ >
856
+ <x render-each=".items" loop-with="getPageRange"></x>
857
+ </div>`;
858
+ function makeSchemaView(accent, body = GRID_BODY) {
859
+ return html4`<span class="font-mono text-sm leading-tight inline-block">
860
+ <span class="inline-flex items-center gap-2 flex-wrap">
861
+ <button
862
+ type="button"
863
+ class="cursor-pointer text-base-content/70 hover:text-base-content inline-flex items-center gap-1"
864
+ :disabled="$isItemsEmpty"
865
+ @on.click="$toggleIsExpanded"
866
+ >
867
+ <span @hide="$isItemsEmpty" @text="$arrowText"></span>
868
+ <span class="${accent} font-semibold" @text="$typeText"></span>
869
+ <span class="text-base-content/50" @text="$countText"></span>
870
+ </button>
871
+ <span
872
+ @each=".badges"
873
+ class="badge badge-xs badge-ghost font-mono font-normal"
874
+ ><x text="@value"></x
875
+ ></span>
876
+ <span @show=".deprecated" class="badge badge-xs badge-warning"
877
+ >deprecated</span
878
+ >
879
+ <div @show="$showPagination" class="join">
880
+ <button
881
+ type="button"
882
+ class="join-item btn btn-xs"
883
+ :disabled="$cannotPrevPage"
884
+ @on.click="$prevPage"
885
+ >
886
+ «
887
+ </button>
888
+ <span
889
+ class="join-item badge font-mono text-xs"
890
+ @text="$pageIndicatorText"
891
+ ></span>
892
+ <button
893
+ type="button"
894
+ class="join-item btn btn-xs"
895
+ :disabled="$cannotNextPage"
896
+ @on.click="$nextPage"
897
+ >
898
+ »
899
+ </button>
900
+ </div>
901
+ </span>
902
+ <span
903
+ @show="truthy? .title"
904
+ class="block text-base-content/80 italic"
905
+ @text=".title"
906
+ ></span>
907
+ <span
908
+ @show="truthy? .description"
909
+ class="block text-base-content/50"
910
+ @text=".description"
911
+ ></span>
912
+ ${body}
913
+ </span>`;
914
+ }
915
+ function metaOf(schema) {
916
+ return {
917
+ title: typeof schema.title === "string" ? schema.title : "",
918
+ description: typeof schema.description === "string" ? schema.description : "",
919
+ deprecated: schema.deprecated === true
920
+ };
921
+ }
922
+ function displayType(schema, fallback) {
923
+ const t = schema.type;
924
+ if (Array.isArray(t))
925
+ return t.join(" | ");
926
+ if (typeof t === "string")
927
+ return t;
928
+ return fallback;
929
+ }
930
+ function simpleScalarType(schema) {
931
+ if (schema == null || typeof schema !== "object")
932
+ return null;
933
+ const t = schema.type;
934
+ const typeOk = typeof t === "string" || Array.isArray(t) && t.every((x) => typeof x === "string");
935
+ if (!typeOk)
936
+ return null;
937
+ if (Object.keys(schema).some((k) => k !== "type"))
938
+ return null;
939
+ return Array.isArray(t) ? t.join(" | ") : t;
940
+ }
941
+ function combinatorPhrasing(kind) {
942
+ if (kind === "allOf")
943
+ return "all of";
944
+ if (kind === "anyOf")
945
+ return "any of";
946
+ if (kind === "oneOf")
947
+ return "one of";
948
+ return "combination";
949
+ }
950
+ function collectBadges(schema) {
951
+ const b = [];
952
+ if (schema.format != null)
953
+ b.push(`format: ${schema.format}`);
954
+ if (schema.minLength != null)
955
+ b.push(`min length: ${schema.minLength}`);
956
+ if (schema.maxLength != null)
957
+ b.push(`max length: ${schema.maxLength}`);
958
+ if (schema.pattern != null)
959
+ b.push(`pattern: /${schema.pattern}/`);
960
+ if (schema.minimum != null)
961
+ b.push(`≥ ${schema.minimum}`);
962
+ if (schema.maximum != null)
963
+ b.push(`≤ ${schema.maximum}`);
964
+ if (schema.exclusiveMinimum != null)
965
+ b.push(`> ${schema.exclusiveMinimum}`);
966
+ if (schema.exclusiveMaximum != null)
967
+ b.push(`< ${schema.exclusiveMaximum}`);
968
+ if (schema.multipleOf != null)
969
+ b.push(`multiple of ${schema.multipleOf}`);
970
+ if (schema.minItems != null)
971
+ b.push(`min items: ${schema.minItems}`);
972
+ if (schema.maxItems != null)
973
+ b.push(`max items: ${schema.maxItems}`);
974
+ if (schema.uniqueItems)
975
+ b.push("unique items");
976
+ if (schema.minProperties != null)
977
+ b.push(`min properties: ${schema.minProperties}`);
978
+ if (schema.maxProperties != null)
979
+ b.push(`max properties: ${schema.maxProperties}`);
980
+ if (schema.additionalProperties === false)
981
+ b.push("no additional properties");
982
+ if (schema.readOnly)
983
+ b.push("read-only");
984
+ if (schema.writeOnly)
985
+ b.push("write-only");
986
+ return b;
987
+ }
988
+ function valueBranch(label, value) {
989
+ return SchemaBranch.make({
990
+ label,
991
+ child: JsonViewer.Class.fromData(value),
992
+ labelClass: "text-info"
993
+ });
994
+ }
995
+ function collectExtras(schema, recurse, primary) {
996
+ const rows = [];
997
+ if (primary !== "const" && "const" in schema)
998
+ rows.push(valueBranch("const", schema.const));
999
+ if (primary !== "enum" && Array.isArray(schema.enum))
1000
+ rows.push(valueBranch("enum", schema.enum));
1001
+ if ("default" in schema)
1002
+ rows.push(valueBranch("default", schema.default));
1003
+ if ("examples" in schema)
1004
+ rows.push(valueBranch("examples", schema.examples));
1005
+ if (primary !== "combinator") {
1006
+ for (const k of ["allOf", "anyOf", "oneOf"]) {
1007
+ if (Array.isArray(schema[k])) {
1008
+ for (const s of schema[k]) {
1009
+ rows.push(SchemaBranch.make({
1010
+ label: combinatorPhrasing(k),
1011
+ child: recurse(s),
1012
+ labelClass: "text-accent"
1013
+ }));
1014
+ }
1015
+ }
1016
+ }
1017
+ }
1018
+ if (primary !== "not" && schema.not !== undefined) {
1019
+ rows.push(SchemaBranch.make({
1020
+ label: "not",
1021
+ child: recurse(schema.not),
1022
+ labelClass: "text-error"
1023
+ }));
1024
+ }
1025
+ if (primary !== "conditional") {
1026
+ if (schema.if !== undefined)
1027
+ rows.push(SchemaBranch.make({ label: "if", child: recurse(schema.if) }));
1028
+ if (schema.then !== undefined)
1029
+ rows.push(SchemaBranch.make({ label: "then", child: recurse(schema.then) }));
1030
+ if (schema.else !== undefined)
1031
+ rows.push(SchemaBranch.make({ label: "else", child: recurse(schema.else) }));
1032
+ }
1033
+ const defs = schema.$defs || schema.definitions;
1034
+ if (defs && typeof defs === "object") {
1035
+ for (const [name, s] of Object.entries(defs)) {
1036
+ rows.push(SchemaBranch.make({
1037
+ label: `$defs/${name}`,
1038
+ child: recurse(s),
1039
+ labelClass: "text-secondary"
1040
+ }));
1041
+ }
1042
+ }
1043
+ return rows;
1044
+ }
1045
+ var SchemaProperty = component4({
1046
+ name: "SchemaProperty",
1047
+ fields: { key: "", child: null, required: false },
1048
+ view: html4`<div class="contents">
1049
+ <span class="font-mono text-sm flex items-center gap-1 leading-tight">
1050
+ <span class="text-base-content/60" @text=".key"></span>
1051
+ <span @show=".required" class="text-error font-bold" title="required"
1052
+ >*</span
1053
+ >
1054
+ <span class="text-base-content/30">:</span>
1055
+ </span>
1056
+ <x render=".child"></x>
1057
+ </div>`
1058
+ });
1059
+ var SchemaBranch = component4({
1060
+ name: "SchemaBranch",
1061
+ fields: { label: "", child: null, labelClass: "text-accent" },
1062
+ methods: {
1063
+ labelCssClass() {
1064
+ return `font-mono text-sm ${this.labelClass}`;
1065
+ }
1066
+ },
1067
+ view: html4`<div class="contents">
1068
+ <span class="flex items-center gap-1 leading-tight">
1069
+ <span :class="$labelCssClass" @text=".label"></span>
1070
+ <span class="text-base-content/30">:</span>
1071
+ </span>
1072
+ <x render=".child"></x>
1073
+ </div>`,
1074
+ views: {
1075
+ _margauiClasses: html4`<p
1076
+ class="text-accent text-secondary text-info text-error"
1077
+ ></p>`
1078
+ }
1079
+ });
1080
+ var SchemaScalar = component4({
1081
+ name: "SchemaScalar",
1082
+ fields: schemaNodeFields,
1083
+ methods: schemaNodeMethods({
1084
+ countText() {
1085
+ return "";
1086
+ }
1087
+ }),
1088
+ alter: compositeAlter,
1089
+ statics: {
1090
+ fromData(schema, recurse) {
1091
+ return this.make({
1092
+ typeLabel: displayType(schema, "any"),
1093
+ badges: collectBadges(schema),
1094
+ ...metaOf(schema),
1095
+ items: collectExtras(schema, recurse, "scalar")
1096
+ });
1097
+ }
1098
+ },
1099
+ view: makeSchemaView("text-success")
1100
+ });
1101
+ var SchemaObject = component4({
1102
+ name: "SchemaObject",
1103
+ fields: schemaNodeFields,
1104
+ methods: schemaNodeMethods({
1105
+ countText() {
1106
+ return this.items.size > 0 ? `{${this.items.size}}` : "";
1107
+ }
1108
+ }),
1109
+ alter: compositeAlter,
1110
+ statics: {
1111
+ fromData(schema, recurse) {
1112
+ const required = Array.isArray(schema.required) ? schema.required : [];
1113
+ const items = [];
1114
+ if (schema.properties && typeof schema.properties === "object") {
1115
+ for (const [k, v] of Object.entries(schema.properties)) {
1116
+ items.push(SchemaProperty.make({
1117
+ key: k,
1118
+ required: required.includes(k),
1119
+ child: recurse(v)
1120
+ }));
1121
+ }
1122
+ }
1123
+ if (schema.patternProperties && typeof schema.patternProperties === "object") {
1124
+ for (const [pat, v] of Object.entries(schema.patternProperties)) {
1125
+ items.push(SchemaBranch.make({
1126
+ label: `/${pat}/`,
1127
+ child: recurse(v),
1128
+ labelClass: "text-secondary"
1129
+ }));
1130
+ }
1131
+ }
1132
+ if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
1133
+ items.push(SchemaBranch.make({
1134
+ label: "additional properties",
1135
+ child: recurse(schema.additionalProperties),
1136
+ labelClass: "text-secondary"
1137
+ }));
1138
+ }
1139
+ if (schema.propertyNames !== undefined) {
1140
+ items.push(SchemaBranch.make({
1141
+ label: "property names",
1142
+ child: recurse(schema.propertyNames),
1143
+ labelClass: "text-secondary"
1144
+ }));
1145
+ }
1146
+ items.push(...collectExtras(schema, recurse, "object"));
1147
+ return this.make({
1148
+ typeLabel: displayType(schema, "object"),
1149
+ badges: collectBadges(schema),
1150
+ ...metaOf(schema),
1151
+ items
1152
+ });
1153
+ }
1154
+ },
1155
+ view: makeSchemaView("text-primary")
1156
+ });
1157
+ var SchemaArray = component4({
1158
+ name: "SchemaArray",
1159
+ fields: schemaNodeFields,
1160
+ methods: schemaNodeMethods({
1161
+ countText() {
1162
+ return this.items.size > 0 ? `[${this.items.size}]` : "";
1163
+ }
1164
+ }),
1165
+ alter: compositeAlter,
1166
+ statics: {
1167
+ fromData(schema, recurse) {
1168
+ const items = [];
1169
+ const isTuple = Array.isArray(schema.prefixItems);
1170
+ const inlineType = !isTuple && !Array.isArray(schema.items) ? simpleScalarType(schema.items) : null;
1171
+ if (isTuple) {
1172
+ schema.prefixItems.forEach((s, i) => {
1173
+ items.push(SchemaBranch.make({ label: `item ${i}`, child: recurse(s) }));
1174
+ });
1175
+ }
1176
+ if (Array.isArray(schema.items)) {
1177
+ schema.items.forEach((s, i) => {
1178
+ items.push(SchemaBranch.make({ label: `item ${i}`, child: recurse(s) }));
1179
+ });
1180
+ } else if (schema.items !== undefined && inlineType == null) {
1181
+ items.push(SchemaBranch.make({ label: "items", child: recurse(schema.items) }));
1182
+ }
1183
+ if (schema.contains !== undefined) {
1184
+ items.push(SchemaBranch.make({
1185
+ label: "contains",
1186
+ child: recurse(schema.contains)
1187
+ }));
1188
+ }
1189
+ items.push(...collectExtras(schema, recurse, "array"));
1190
+ const typeLabel = isTuple ? "tuple" : inlineType != null ? `array of ${inlineType}` : displayType(schema, "array");
1191
+ return this.make({
1192
+ typeLabel,
1193
+ badges: collectBadges(schema),
1194
+ ...metaOf(schema),
1195
+ items
1196
+ });
1197
+ }
1198
+ },
1199
+ view: makeSchemaView("text-primary")
1200
+ });
1201
+ var SchemaEnum = component4({
1202
+ name: "SchemaEnum",
1203
+ fields: schemaNodeFields,
1204
+ methods: schemaNodeMethods({
1205
+ countText() {
1206
+ return this.items.size > 0 ? `(${this.items.size})` : "";
1207
+ }
1208
+ }),
1209
+ alter: compositeAlter,
1210
+ statics: {
1211
+ fromData(schema, recurse) {
1212
+ const members = Array.isArray(schema.enum) ? schema.enum : [];
1213
+ const items = members.map((v) => JsonViewer.Class.fromData(v));
1214
+ items.push(...collectExtras(schema, recurse, "enum"));
1215
+ return this.make({
1216
+ typeLabel: "enum",
1217
+ badges: collectBadges(schema),
1218
+ ...metaOf(schema),
1219
+ items
1220
+ });
1221
+ }
1222
+ },
1223
+ view: makeSchemaView("text-info", LIST_BODY)
1224
+ });
1225
+ var SchemaConst = component4({
1226
+ name: "SchemaConst",
1227
+ fields: { value: null },
1228
+ statics: {
1229
+ fromData(schema) {
1230
+ return this.make({ value: JsonViewer.Class.fromData(schema.const) });
1231
+ }
1232
+ },
1233
+ view: html4`<span
1234
+ class="font-mono text-sm leading-tight inline-flex items-center gap-2"
1235
+ >
1236
+ <span class="text-info font-semibold">const</span>
1237
+ <span class="text-base-content/30">=</span>
1238
+ <x render=".value"></x>
1239
+ </span>`
1240
+ });
1241
+ var SchemaCombinator = component4({
1242
+ name: "SchemaCombinator",
1243
+ fields: schemaNodeFields,
1244
+ methods: schemaNodeMethods({
1245
+ countText() {
1246
+ return this.items.size > 0 ? `(${this.items.size})` : "";
1247
+ }
1248
+ }),
1249
+ alter: compositeAlter,
1250
+ statics: {
1251
+ fromData(schema, recurse) {
1252
+ const items = [];
1253
+ let kind = null;
1254
+ for (const k of ["allOf", "anyOf", "oneOf"]) {
1255
+ if (Array.isArray(schema[k])) {
1256
+ kind = kind ?? k;
1257
+ for (const s of schema[k])
1258
+ items.push(recurse(s));
1259
+ }
1260
+ }
1261
+ items.push(...collectExtras(schema, recurse, "combinator"));
1262
+ return this.make({
1263
+ typeLabel: combinatorPhrasing(kind),
1264
+ badges: collectBadges(schema),
1265
+ ...metaOf(schema),
1266
+ items
1267
+ });
1268
+ }
1269
+ },
1270
+ view: makeSchemaView("text-accent", LIST_BODY)
1271
+ });
1272
+ var SchemaConditional = component4({
1273
+ name: "SchemaConditional",
1274
+ fields: {
1275
+ ifNode: null,
1276
+ thenNode: null,
1277
+ elseNode: null,
1278
+ title: "",
1279
+ description: "",
1280
+ deprecated: false
1281
+ },
1282
+ statics: {
1283
+ fromData(schema, recurse) {
1284
+ return this.make({
1285
+ ifNode: schema.if !== undefined ? recurse(schema.if) : null,
1286
+ thenNode: schema.then !== undefined ? recurse(schema.then) : null,
1287
+ elseNode: schema.else !== undefined ? recurse(schema.else) : null,
1288
+ ...metaOf(schema)
1289
+ });
1290
+ }
1291
+ },
1292
+ view: html4`<span class="font-mono text-sm leading-tight inline-block">
1293
+ <span class="inline-flex items-center gap-2 flex-wrap">
1294
+ <span class="text-accent font-semibold">conditional</span>
1295
+ <span @show=".deprecated" class="badge badge-xs badge-warning"
1296
+ >deprecated</span
1297
+ >
1298
+ </span>
1299
+ <span
1300
+ @show="truthy? .title"
1301
+ class="block text-base-content/80 italic"
1302
+ @text=".title"
1303
+ ></span>
1304
+ <span
1305
+ @show="truthy? .description"
1306
+ class="block text-base-content/50"
1307
+ @text=".description"
1308
+ ></span>
1309
+ <div
1310
+ class="ml-1 grid gap-x-2 gap-y-0.5 items-baseline border-l border-base-content/10 pl-2 mt-0.5"
1311
+ style="grid-template-columns: auto 1fr"
1312
+ >
1313
+ <div @show="truthy? .ifNode" class="contents">
1314
+ <span class="font-mono text-sm flex items-center gap-1 leading-tight">
1315
+ <span class="text-info">if</span>
1316
+ <span class="text-base-content/30">:</span>
1317
+ </span>
1318
+ <x render=".ifNode"></x>
1319
+ </div>
1320
+ <div @show="truthy? .thenNode" class="contents">
1321
+ <span class="font-mono text-sm flex items-center gap-1 leading-tight">
1322
+ <span class="text-success">then</span>
1323
+ <span class="text-base-content/30">:</span>
1324
+ </span>
1325
+ <x render=".thenNode"></x>
1326
+ </div>
1327
+ <div @show="truthy? .elseNode" class="contents">
1328
+ <span class="font-mono text-sm flex items-center gap-1 leading-tight">
1329
+ <span class="text-warning">else</span>
1330
+ <span class="text-base-content/30">:</span>
1331
+ </span>
1332
+ <x render=".elseNode"></x>
1333
+ </div>
1334
+ </div>
1335
+ </span>`
1336
+ });
1337
+ var SchemaNot = component4({
1338
+ name: "SchemaNot",
1339
+ fields: { child: null },
1340
+ statics: {
1341
+ fromData(schema, recurse) {
1342
+ return this.make({ child: recurse(schema.not) });
1343
+ }
1344
+ },
1345
+ view: html4`<span
1346
+ class="font-mono text-sm leading-tight inline-flex items-center gap-2"
1347
+ >
1348
+ <span class="text-error font-semibold">not</span>
1349
+ <span class="text-base-content/30">:</span>
1350
+ <x render=".child"></x>
1351
+ </span>`
1352
+ });
1353
+ var SchemaBoolean = component4({
1354
+ name: "SchemaBoolean",
1355
+ fields: { value: true },
1356
+ methods: {
1357
+ text() {
1358
+ return this.value ? "any value (true)" : "no value allowed (false)";
1359
+ },
1360
+ cssClass() {
1361
+ const base = "font-mono text-sm leading-tight italic";
1362
+ return `${base} ${this.value ? "text-success" : "text-error"}`;
1363
+ }
1364
+ },
1365
+ view: html4`<span :class="$cssClass" @text="$text"></span>`,
1366
+ views: {
1367
+ _margauiClasses: html4`<p class="text-success text-error"></p>`
1368
+ }
1369
+ });
1370
+ var SchemaRef = component4({
1371
+ name: "SchemaRef",
1372
+ fields: { target: "" },
1373
+ view: html4`<span class="font-mono text-sm leading-tight text-secondary"
1374
+ >→ <span @text=".target"></span
1375
+ ></span>`
1376
+ });
1377
+ var SchemaViewer = component4({
1378
+ name: "SchemaViewer",
1379
+ fields: {
1380
+ value: null,
1381
+ raw: null,
1382
+ rawSchema: null,
1383
+ showRaw: false
1384
+ },
1385
+ methods: {
1386
+ toggleIsExpanded() {
1387
+ return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
1388
+ },
1389
+ toggleLabel() {
1390
+ return this.showRaw ? "high-level view" : "raw schema";
1391
+ }
1392
+ },
1393
+ statics: {
1394
+ fromData(schema) {
1395
+ return this.make({
1396
+ value: dispatch3(schema),
1397
+ raw: JsonViewer.Class.fromData(schema),
1398
+ rawSchema: schema
1399
+ });
1400
+ }
1401
+ },
1402
+ view: html4`<div class="inline-flex flex-col items-start gap-1">
1403
+ <button
1404
+ type="button"
1405
+ class="btn btn-xs btn-ghost self-end"
1406
+ @on.click="$toggleShowRaw"
1407
+ @text="$toggleLabel"
1408
+ ></button>
1409
+ <x render=".value" hide=".showRaw"></x>
1410
+ <x render=".raw" show=".showRaw"></x>
1411
+ </div>`
1412
+ });
1413
+ function hasObjectKeywords(schema) {
1414
+ const t = schema.type;
1415
+ if (t === "object" || Array.isArray(t) && t.includes("object"))
1416
+ return true;
1417
+ return schema.properties !== undefined || schema.required !== undefined || schema.patternProperties !== undefined || schema.propertyNames !== undefined || schema.additionalProperties !== undefined || schema.minProperties != null || schema.maxProperties != null;
1418
+ }
1419
+ function hasArrayKeywords(schema) {
1420
+ const t = schema.type;
1421
+ if (t === "array" || Array.isArray(t) && t.includes("array"))
1422
+ return true;
1423
+ return schema.items !== undefined || schema.prefixItems !== undefined || schema.contains !== undefined || schema.minItems != null || schema.maxItems != null || schema.uniqueItems !== undefined;
1424
+ }
1425
+ function classifySchema(schema, recurse = classifySchema) {
1426
+ if (schema === true || schema === false)
1427
+ return SchemaBoolean.make({ value: schema });
1428
+ if (schema === null || typeof schema !== "object")
1429
+ return JsonViewer.Class.fromData(schema);
1430
+ if (typeof schema.$ref === "string")
1431
+ return SchemaRef.make({ target: schema.$ref });
1432
+ if (hasObjectKeywords(schema))
1433
+ return SchemaObject.Class.fromData(schema, recurse);
1434
+ if (hasArrayKeywords(schema))
1435
+ return SchemaArray.Class.fromData(schema, recurse);
1436
+ if (Array.isArray(schema.enum))
1437
+ return SchemaEnum.Class.fromData(schema, recurse);
1438
+ if ("const" in schema)
1439
+ return SchemaConst.Class.fromData(schema, recurse);
1440
+ if (schema.allOf || schema.anyOf || schema.oneOf)
1441
+ return SchemaCombinator.Class.fromData(schema, recurse);
1442
+ if (schema.not !== undefined)
1443
+ return SchemaNot.Class.fromData(schema, recurse);
1444
+ if (schema.if !== undefined || schema.then !== undefined || schema.else !== undefined)
1445
+ return SchemaConditional.Class.fromData(schema, recurse);
1446
+ return SchemaScalar.Class.fromData(schema, recurse);
1447
+ }
1448
+ var dispatch3 = chain(classifySchema);
1449
+ function getComponents4() {
1450
+ return [
1451
+ SchemaViewer,
1452
+ SchemaScalar,
1453
+ SchemaObject,
1454
+ SchemaArray,
1455
+ SchemaEnum,
1456
+ SchemaConst,
1457
+ SchemaCombinator,
1458
+ SchemaConditional,
1459
+ SchemaNot,
1460
+ SchemaProperty,
1461
+ SchemaBranch,
1462
+ SchemaBoolean,
1463
+ SchemaRef,
1464
+ ...getComponents()
1465
+ ];
1466
+ }
1467
+
1468
+ // src/components/tutuca/component-inspector.js
1469
+ import { component as component5, html as html5 } from "tutuca";
1470
+ function introspectComponent(comp) {
1471
+ const { fields, methods } = comp.Class.getMetaClass();
1472
+ return {
1473
+ name: comp.name,
1474
+ id: comp.id,
1475
+ fields: Object.entries(fields).map(([name, f]) => ({
1476
+ name,
1477
+ type: f.type,
1478
+ defaultValue: f.defaultValue
1479
+ })),
1480
+ methods: Object.keys(methods),
1481
+ input: Object.keys(comp.input ?? {}),
1482
+ receive: Object.keys(comp.receive ?? {}),
1483
+ bubble: Object.keys(comp.bubble ?? {}),
1484
+ response: Object.keys(comp.response ?? {}),
1485
+ alter: Object.keys(comp.alter ?? {}),
1486
+ statics: Object.keys(comp.spec?.statics ?? {}),
1487
+ views: Object.values(comp.views ?? {}).map((v) => ({
1488
+ name: v.name,
1489
+ rawView: v.rawView
1490
+ }))
1491
+ };
1492
+ }
1493
+ var sectionView = makeCompositeView({
1494
+ typeClass: "font-semibold",
1495
+ borderClass: "border-base-content/15",
1496
+ toggleHandler: "toggle isCtrl"
1497
+ });
1498
+ var CompName = component5({
1499
+ name: "CompName",
1500
+ fields: { name: "" },
1501
+ view: html5`<span
1502
+ class="font-mono text-sm leading-tight badge badge-sm badge-ghost"
1503
+ @text=".name"
1504
+ ></span>`
1505
+ });
1506
+ var CompField = component5({
1507
+ name: "CompField",
1508
+ fields: { name: "", typeName: "", child: null },
1509
+ view: html5`<div class="flex items-center gap-2 leading-tight">
1510
+ <span class="text-base-content/70 font-mono text-sm" @text=".name"></span>
1511
+ <span class="text-base-content/30">:</span>
1512
+ <span class="text-base-content/50 font-mono text-xs" @text=".typeName"></span>
1513
+ <span class="text-base-content/30">=</span>
1514
+ <x render=".child"></x>
1515
+ </div>`
1516
+ });
1517
+ var CompView = component5({
1518
+ name: "CompView",
1519
+ fields: { name: "", rawView: "", isExpanded: false },
1520
+ methods: {
1521
+ arrowText() {
1522
+ return this.isExpanded ? "▼" : "▶";
1523
+ }
1524
+ },
1525
+ input: {
1526
+ toggle(isCtrl, ctx) {
1527
+ if (isCtrl) {
1528
+ ctx.bubble("toggleAllViews", [!this.isExpanded]);
1529
+ return this;
1530
+ }
1531
+ return this.toggleIsExpanded();
1532
+ }
1533
+ },
1534
+ view: html5`<div class="flex flex-col gap-0.5 leading-tight">
1535
+ <button
1536
+ type="button"
1537
+ class="cursor-pointer text-base-content/70 hover:text-base-content inline-flex items-center gap-1 self-start"
1538
+ @on.click="toggle isCtrl"
1539
+ >
1540
+ <span @text="$arrowText"></span>
1541
+ <span class="font-mono text-sm" @text=".name"></span>
1542
+ </button>
1543
+ <pre
1544
+ @show=".isExpanded"
1545
+ class="text-xs bg-base-200 rounded p-2 overflow-x-auto whitespace-pre-wrap"
1546
+ ><code @text=".rawView"></code></pre>
1547
+ </div>`
1548
+ });
1549
+ var CompSection = component5({
1550
+ name: "CompSection",
1551
+ fields: { ...compositeFields, label: "" },
1552
+ methods: {
1553
+ ...compositeMethods,
1554
+ typeText() {
1555
+ return this.label;
1556
+ },
1557
+ countText() {
1558
+ return `(${this.items.size})`;
1559
+ }
1560
+ },
1561
+ input: {
1562
+ toggle(isCtrl, ctx) {
1563
+ if (isCtrl) {
1564
+ ctx.bubble("toggleAllSections", [!this.isExpanded]);
1565
+ return this;
1566
+ }
1567
+ return this.toggleIsExpanded();
1568
+ }
1569
+ },
1570
+ alter: compositeAlter,
1571
+ view: sectionView
1572
+ });
1573
+ var ComponentInspector = component5({
1574
+ name: "ComponentInspector",
1575
+ fields: { compName: "", compId: 0, sections: [] },
1576
+ methods: {
1577
+ idText() {
1578
+ return `#${this.compId}`;
1579
+ },
1580
+ setAllSections(state) {
1581
+ return this.setSections(this.sections.map((s) => s.setIsExpanded(state)));
1582
+ },
1583
+ expandAll() {
1584
+ return this.setAllSections(true);
1585
+ },
1586
+ collapseAll() {
1587
+ return this.setAllSections(false);
1588
+ },
1589
+ setAllViews(state) {
1590
+ return this.setSections(this.sections.map((s) => s.label === "Views" ? s.setItems(s.items.map((v) => v.setIsExpanded(state))) : s));
1591
+ },
1592
+ expandAllViews() {
1593
+ return this.setAllViews(true);
1594
+ },
1595
+ collapseAllViews() {
1596
+ return this.setAllViews(false);
1597
+ }
1598
+ },
1599
+ bubble: {
1600
+ toggleAllSections(state) {
1601
+ return this.setAllSections(state);
1602
+ },
1603
+ toggleAllViews(state) {
1604
+ return this.setAllViews(state);
1605
+ }
1606
+ },
1607
+ statics: {
1608
+ fromData(comp) {
1609
+ const d = introspectComponent(comp);
1610
+ const sections = [];
1611
+ const add = (label, items, isExpanded = false) => {
1612
+ if (items.length > 0) {
1613
+ sections.push(CompSection.make({ label, items, isExpanded }));
1614
+ }
1615
+ };
1616
+ const names = (list) => list.map((name) => CompName.make({ name }));
1617
+ add("Fields", d.fields.map((f) => CompField.make({
1618
+ name: f.name,
1619
+ typeName: f.type,
1620
+ child: ImInspector.Class.fromData(f.defaultValue)
1621
+ })), true);
1622
+ add("Methods", names(d.methods));
1623
+ add("Input", names(d.input));
1624
+ add("Receive", names(d.receive));
1625
+ add("Bubble", names(d.bubble));
1626
+ add("Response", names(d.response));
1627
+ add("Alter", names(d.alter));
1628
+ add("Statics", names(d.statics));
1629
+ add("Views", d.views.map((v) => CompView.make({ name: v.name, rawView: v.rawView })));
1630
+ return this.make({ compName: d.name, compId: d.id, sections });
1631
+ }
1632
+ },
1633
+ view: html5`<div class="font-mono text-sm leading-tight flex flex-col gap-1">
1634
+ <div class="inline-flex items-center gap-2">
1635
+ <span class="font-semibold" @text=".compName"></span>
1636
+ <span class="text-base-content/40 text-xs" @text="$idText"></span>
1637
+ </div>
1638
+ <x render-each=".sections"></x>
1639
+ </div>`
1640
+ });
1641
+ function getComponents5() {
1642
+ return [ComponentInspector, CompSection, CompField, CompName, CompView, ...getComponents2()];
1643
+ }
1644
+
1645
+ // src/components/tutuca/instance-inspector.js
1646
+ import { component as component8, html as html8, isRecord } from "tutuca";
1647
+
1648
+ // src/components/tutuca/lint-inspector.js
1649
+ import { component as component6, html as html6 } from "tutuca";
1650
+ var tagDisplay = (tag) => tag ? String(tag).toLowerCase() : "";
1651
+ function originSuffix(info) {
1652
+ if (!info)
1653
+ return "";
1654
+ const parts = [];
1655
+ if (info.originAttr) {
1656
+ const branch = info.branch ? `[${info.branch}]` : "";
1657
+ parts.push(`in ${info.originAttr}${branch}`);
1658
+ }
1659
+ if (info.handlerName) {
1660
+ parts.push(`handler '${info.handlerName}'${info.argIndex !== undefined ? ` arg ${info.argIndex}` : ""}`);
1661
+ }
1662
+ const t = tagDisplay(info.tag);
1663
+ if (t && t !== "x")
1664
+ parts.push(`on <${t}>`);
1665
+ return parts.length ? ` (${parts.join(", ")})` : "";
1666
+ }
1667
+ function tagSuffix(info) {
1668
+ const t = tagDisplay(info?.tag);
1669
+ return t && t !== "x" ? ` on <${t}>` : "";
1670
+ }
1671
+ function eventSuffix(info) {
1672
+ if (info?.originAttr)
1673
+ return ` in ${info.originAttr}`;
1674
+ if (info?.eventName)
1675
+ return ` in @on.${info.eventName}`;
1676
+ return "";
1677
+ }
1678
+ function humanizeId(id) {
1679
+ if (!id)
1680
+ return "Lint finding";
1681
+ const s = id.toLowerCase().replace(/_/g, " ");
1682
+ return s.charAt(0).toUpperCase() + s.slice(1);
1683
+ }
1684
+ function lintMessage(id, info = {}) {
1685
+ switch (id) {
1686
+ case "FIELD_VAL_NOT_DEFINED":
1687
+ return `Field '.${info.name}' is not defined${originSuffix(info)}`;
1688
+ case "FIELD_VAL_IS_METHOD":
1689
+ return `'.${info.name}' reads a field, but '${info.name}' is a method — use '$${info.name}'${originSuffix(info)}`;
1690
+ case "METHOD_VAL_NOT_DEFINED":
1691
+ return `Method '$${info.name}' is not defined${originSuffix(info)}`;
1692
+ case "METHOD_VAL_IS_FIELD":
1693
+ return `'$${info.name}' calls a method, but '${info.name}' is a field — use '.${info.name}'${originSuffix(info)}`;
1694
+ case "INPUT_HANDLER_NOT_IMPLEMENTED":
1695
+ return `Input handler '${info.name}' is not implemented${eventSuffix(info)}`;
1696
+ case "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED":
1697
+ return `Method '$${info.name}' is not implemented${eventSuffix(info)}`;
1698
+ case "INPUT_HANDLER_NOT_REFERENCED":
1699
+ return `Input handler '${info.name}' is defined but never used`;
1700
+ case "ALT_HANDLER_NOT_DEFINED":
1701
+ return `Alter handler '${info.name}' is not defined${originSuffix(info)}`;
1702
+ case "DYN_VAL_NOT_DEFINED":
1703
+ return `Dynamic variable '*${info.name}' is not defined${originSuffix(info)}`;
1704
+ case "UNKNOWN_COMPONENT_NAME":
1705
+ return `Unknown component '${info.name}'${originSuffix(info)}`;
1706
+ case "UNKNOWN_DIRECTIVE":
1707
+ return `Unknown directive '@${info.name}'${tagSuffix(info)}`;
1708
+ case "IF_NO_BRANCH_SET":
1709
+ return `'@if.${info.attr}' has no '@then' or '@else' branch${tagSuffix(info)}`;
1710
+ case "REDUNDANT_TEMPLATE_STRING":
1711
+ return `Redundant template string — '{${info.simpler}}' should be just '${info.simpler}'${originSuffix(info)}`;
1712
+ case "PLACEHOLDERLESS_TEMPLATE_STRING":
1713
+ return `Template string has no dynamic parts — use the literal ${info.literal} instead${originSuffix(info)}`;
1714
+ case "ASYNC_HANDLER":
1715
+ return `Handler '${info.name}' in '${info.channel}' is async — handlers must be synchronous`;
1716
+ case "UNKNOWN_COMPONENT_SPEC_KEY":
1717
+ return `Unknown component spec key '${info.key}' — ignored at runtime`;
1718
+ default:
1719
+ return `${humanizeId(id)}${originSuffix(info)}`;
1720
+ }
1721
+ }
1722
+ function suggestionText(s) {
1723
+ if (!s)
1724
+ return "";
1725
+ if (s.to != null)
1726
+ return `${s.from ?? ""} → ${s.to}`;
1727
+ return s.text ?? s.kind ?? "";
1728
+ }
1729
+ var componentView = makeCompositeView({
1730
+ typeClass: "font-semibold",
1731
+ borderClass: "border-base-content/15"
1732
+ });
1733
+ var LintFinding = component6({
1734
+ name: "LintFinding",
1735
+ fields: { id: "", level: "", message: "", suggestion: "", detail: null },
1736
+ methods: {
1737
+ levelBadgeClass() {
1738
+ const base = "badge badge-sm badge-soft";
1739
+ switch (this.level) {
1740
+ case "error":
1741
+ return `${base} badge-error`;
1742
+ case "warn":
1743
+ return `${base} badge-warning`;
1744
+ default:
1745
+ return `${base} badge-neutral`;
1746
+ }
1747
+ },
1748
+ hasSuggestion() {
1749
+ return this.suggestion !== "";
1750
+ },
1751
+ hasDetail() {
1752
+ return this.detail != null;
1753
+ }
1754
+ },
1755
+ views: {
1756
+ _palette: html6`<span
1757
+ class="badge-soft badge-error badge-warning badge-neutral"
1758
+ ></span>`
1759
+ },
1760
+ view: html6`<div class="flex flex-col gap-1.5 py-1.5 leading-snug">
1761
+ <div class="flex items-baseline gap-2 flex-wrap">
1762
+ <span :class="$levelBadgeClass" @text=".level"></span>
1763
+ <span class="text-sm text-base-content/90" @text=".message"></span>
1764
+ </div>
1765
+ <div
1766
+ @show="$hasSuggestion"
1767
+ class="ml-3 flex items-baseline gap-1.5 text-xs text-base-content/60"
1768
+ >
1769
+ <span>fix</span>
1770
+ <span class="font-mono" @text=".suggestion"></span>
1771
+ </div>
1772
+ <div @show="$hasDetail" class="ml-3"><x render=".detail"></x></div>
1773
+ </div>`
1774
+ });
1775
+ function buildFinding(f) {
1776
+ const info = f.info ?? {};
1777
+ const ctx = f.context ?? {};
1778
+ return LintFinding.make({
1779
+ id: f.id ?? "",
1780
+ level: f.level ?? "",
1781
+ message: lintMessage(f.id, info),
1782
+ suggestion: suggestionText(f.suggestion),
1783
+ detail: ImInspector.Class.fromData({ id: f.id, info, context: ctx })
1784
+ });
1785
+ }
1786
+ var LintComponent = component6({
1787
+ name: "LintComponent",
1788
+ fields: { ...compositeFields, componentName: "", errors: 0, warns: 0, hints: 0 },
1789
+ methods: {
1790
+ ...compositeMethods,
1791
+ typeText() {
1792
+ return this.componentName;
1793
+ },
1794
+ countText() {
1795
+ const parts = [];
1796
+ if (this.errors)
1797
+ parts.push(`${this.errors} error${this.errors === 1 ? "" : "s"}`);
1798
+ if (this.warns)
1799
+ parts.push(`${this.warns} warning${this.warns === 1 ? "" : "s"}`);
1800
+ if (this.hints)
1801
+ parts.push(`${this.hints} hint${this.hints === 1 ? "" : "s"}`);
1802
+ return parts.length ? parts.join(", ") : "ok";
1803
+ }
1804
+ },
1805
+ alter: compositeAlter,
1806
+ view: componentView
1807
+ });
1808
+ var LintReport = component6({
1809
+ name: "LintReport",
1810
+ fields: {
1811
+ title: "",
1812
+ path: "",
1813
+ errors: 0,
1814
+ warnings: 0,
1815
+ hints: 0,
1816
+ clean: true,
1817
+ components: []
1818
+ },
1819
+ methods: {
1820
+ errText() {
1821
+ return `${this.errors} error${this.errors === 1 ? "" : "s"}`;
1822
+ },
1823
+ warnText() {
1824
+ return `${this.warnings} warning${this.warnings === 1 ? "" : "s"}`;
1825
+ },
1826
+ hintText() {
1827
+ return `${this.hints} hint${this.hints === 1 ? "" : "s"}`;
1828
+ },
1829
+ hasErrors() {
1830
+ return this.errors > 0;
1831
+ },
1832
+ hasWarnings() {
1833
+ return this.warnings > 0;
1834
+ },
1835
+ hasHints() {
1836
+ return this.hints > 0;
1837
+ }
1838
+ },
1839
+ statics: {
1840
+ fromData(report, { title = "Lint", path = "" } = {}) {
1841
+ const comps = report?.components ?? [];
1842
+ let errors = 0;
1843
+ let warnings = 0;
1844
+ let hints = 0;
1845
+ const built = [];
1846
+ for (const c of comps) {
1847
+ const findings = c.findings ?? [];
1848
+ if (findings.length === 0)
1849
+ continue;
1850
+ const e = findings.filter((f) => f.level === "error").length;
1851
+ const w = findings.filter((f) => f.level === "warn").length;
1852
+ const h = findings.filter((f) => f.level === "hint").length;
1853
+ errors += e;
1854
+ warnings += w;
1855
+ hints += h;
1856
+ built.push(LintComponent.make({
1857
+ componentName: c.componentName,
1858
+ errors: e,
1859
+ warns: w,
1860
+ hints: h,
1861
+ items: findings.map(buildFinding),
1862
+ isExpanded: e > 0
1863
+ }));
1864
+ }
1865
+ return this.make({
1866
+ title,
1867
+ path,
1868
+ errors,
1869
+ warnings,
1870
+ hints,
1871
+ clean: built.length === 0,
1872
+ components: built
1873
+ });
1874
+ }
1875
+ },
1876
+ views: {
1877
+ _palette: html6`<span
1878
+ class="badge-soft badge-error badge-warning badge-neutral badge-success"
1879
+ ></span>`
1880
+ },
1881
+ view: html6`<div class="font-mono text-sm leading-snug flex flex-col gap-3">
1882
+ <div class="flex items-center gap-2 flex-wrap">
1883
+ <span class="font-semibold" @text=".title"></span>
1884
+ <span class="text-base-content/40 text-xs" @text=".path"></span>
1885
+ <span
1886
+ @show="$hasErrors"
1887
+ class="badge badge-sm badge-soft badge-error"
1888
+ @text="$errText"
1889
+ ></span>
1890
+ <span
1891
+ @show="$hasWarnings"
1892
+ class="badge badge-sm badge-soft badge-warning"
1893
+ @text="$warnText"
1894
+ ></span>
1895
+ <span
1896
+ @show="$hasHints"
1897
+ class="badge badge-sm badge-soft badge-neutral"
1898
+ @text="$hintText"
1899
+ ></span>
1900
+ <span
1901
+ @show=".clean"
1902
+ class="badge badge-sm badge-soft badge-success"
1903
+ >✓ clean</span
1904
+ >
1905
+ </div>
1906
+ <x render-each=".components"></x>
1907
+ </div>`
1908
+ });
1909
+ function getComponents6() {
1910
+ return [LintReport, LintComponent, LintFinding, ...getComponents2()];
1911
+ }
1912
+
1913
+ // src/components/tutuca/test-inspector.js
1914
+ import { component as component7, html as html7 } from "tutuca";
1915
+ function collectTests(getTests) {
1916
+ const root = { children: [] };
1917
+ const stack = [root];
1918
+ const describe = (head, optsOrFn, maybeFn) => {
1919
+ const fn = typeof maybeFn === "function" ? maybeFn : optsOrFn;
1920
+ const title = typeof head === "string" ? head : head?.name ?? String(head);
1921
+ const node = { title, children: [] };
1922
+ stack[stack.length - 1].children.push(node);
1923
+ stack.push(node);
1924
+ try {
1925
+ fn();
1926
+ } finally {
1927
+ stack.pop();
1928
+ }
1929
+ };
1930
+ const test = (title) => {
1931
+ stack[stack.length - 1].children.push({ title });
1932
+ };
1933
+ const noop = () => {};
1934
+ getTests({ describe, test, expect: noop, drive: async () => null });
1935
+ return root.children;
1936
+ }
1937
+ var suiteView = makeCompositeView({
1938
+ typeClass: "font-semibold",
1939
+ borderClass: "border-base-content/15",
1940
+ toggleHandler: "toggle isCtrl"
1941
+ });
1942
+ var TestCase = component7({
1943
+ name: "TestCase",
1944
+ fields: { title: "", status: "", durationMs: 0, message: "", detail: null },
1945
+ methods: {
1946
+ mark() {
1947
+ switch (this.status) {
1948
+ case "pass":
1949
+ return "✓";
1950
+ case "fail":
1951
+ return "✗";
1952
+ case "skip":
1953
+ return "○";
1954
+ default:
1955
+ return "•";
1956
+ }
1957
+ },
1958
+ markClass() {
1959
+ const base = "font-mono text-sm";
1960
+ switch (this.status) {
1961
+ case "pass":
1962
+ return `${base} text-success`;
1963
+ case "fail":
1964
+ return `${base} text-error`;
1965
+ default:
1966
+ return `${base} text-base-content/40`;
1967
+ }
1968
+ },
1969
+ durText() {
1970
+ return this.status && this.status !== "skip" ? `(${Math.round(this.durationMs)}ms)` : "";
1971
+ },
1972
+ hasMessage() {
1973
+ return this.message !== "";
1974
+ },
1975
+ hasDetail() {
1976
+ return this.detail != null;
1977
+ }
1978
+ },
1979
+ views: {
1980
+ _palette: html7`<span class="text-success text-error text-base-content/40"></span>`
1981
+ },
1982
+ view: html7`<div class="flex flex-col gap-0.5 leading-tight">
1983
+ <div class="flex items-center gap-2">
1984
+ <span :class="$markClass" @text="$mark"></span>
1985
+ <span class="font-mono text-sm" @text=".title"></span>
1986
+ <span class="text-base-content/40 text-xs" @text="$durText"></span>
1987
+ </div>
1988
+ <div @show="$hasMessage" class="ml-5 text-error text-xs" @text=".message"></div>
1989
+ <div @show="$hasDetail" class="ml-5"><x render=".detail"></x></div>
1990
+ </div>`
1991
+ });
1992
+ var TestSuite = component7({
1993
+ name: "TestSuite",
1994
+ fields: { ...compositeFields, title: "", summary: "" },
1995
+ methods: {
1996
+ ...compositeMethods,
1997
+ typeText() {
1998
+ return this.title;
1999
+ },
2000
+ countText() {
2001
+ return this.summary || `(${this.items.size})`;
2002
+ }
2003
+ },
2004
+ input: {
2005
+ toggle(isCtrl, ctx) {
2006
+ if (isCtrl) {
2007
+ ctx.bubble("toggleAll", [!this.isExpanded]);
2008
+ return this;
2009
+ }
2010
+ return this.toggleIsExpanded();
2011
+ }
2012
+ },
2013
+ alter: compositeAlter,
2014
+ view: suiteView
2015
+ });
2016
+ function setSuiteTreeExpanded(node, state) {
2017
+ if (typeof node.setIsExpanded !== "function")
2018
+ return node;
2019
+ return node.setIsExpanded(state).setItems(node.items.map((c) => setSuiteTreeExpanded(c, state)));
2020
+ }
2021
+ function buildDefNode(node) {
2022
+ if (Array.isArray(node.children)) {
2023
+ return TestSuite.make({
2024
+ title: node.title,
2025
+ items: node.children.map(buildDefNode)
2026
+ });
2027
+ }
2028
+ return TestCase.make({ title: node.title });
2029
+ }
2030
+ function buildResultNode(node) {
2031
+ if (Array.isArray(node.children)) {
2032
+ const built = node.children.map(buildResultNode);
2033
+ const pass = built.reduce((n, b) => n + b.pass, 0);
2034
+ const fail = built.reduce((n, b) => n + b.fail, 0);
2035
+ const skip = built.reduce((n, b) => n + b.skip, 0);
2036
+ const summary = `✓${pass}${fail ? ` ✗${fail}` : ""}${skip ? ` ○${skip}` : ""}`;
2037
+ const comp2 = TestSuite.make({
2038
+ title: node.title,
2039
+ summary,
2040
+ items: built.map((b) => b.comp),
2041
+ isExpanded: fail > 0
2042
+ });
2043
+ return { comp: comp2, pass, fail, skip };
2044
+ }
2045
+ const status = node.status ?? "";
2046
+ const err = node.error ?? null;
2047
+ const hasDiff = err != null && (("expected" in err) || ("actual" in err));
2048
+ const comp = TestCase.make({
2049
+ title: node.title,
2050
+ status,
2051
+ durationMs: node.durationMs ?? 0,
2052
+ message: err?.message ?? "",
2053
+ detail: hasDiff ? ImInspector.Class.fromData({ expected: err.expected, actual: err.actual }) : null
2054
+ });
2055
+ return {
2056
+ comp,
2057
+ pass: status === "pass" ? 1 : 0,
2058
+ fail: status === "fail" ? 1 : 0,
2059
+ skip: status === "skip" ? 1 : 0
2060
+ };
2061
+ }
2062
+ var TestReport = component7({
2063
+ name: "TestReport",
2064
+ fields: {
2065
+ title: "",
2066
+ path: "",
2067
+ pass: 0,
2068
+ fail: 0,
2069
+ skip: 0,
2070
+ hasCounts: false,
2071
+ suites: []
2072
+ },
2073
+ methods: {
2074
+ passText() {
2075
+ return `✓ ${this.pass}`;
2076
+ },
2077
+ failText() {
2078
+ return `✗ ${this.fail}`;
2079
+ },
2080
+ skipText() {
2081
+ return `○ ${this.skip}`;
2082
+ },
2083
+ hasFailures() {
2084
+ return this.hasCounts && this.fail > 0;
2085
+ },
2086
+ hasSkips() {
2087
+ return this.hasCounts && this.skip > 0;
2088
+ },
2089
+ setAllSuites(state) {
2090
+ return this.setSuites(this.suites.map((s) => setSuiteTreeExpanded(s, state)));
2091
+ }
2092
+ },
2093
+ bubble: {
2094
+ toggleAll(state) {
2095
+ return this.setAllSuites(state);
2096
+ }
2097
+ },
2098
+ statics: {
2099
+ fromTests(source, { title = "Tests", path = "" } = {}) {
2100
+ const suites = Array.isArray(source) ? source : source?.suites ?? [];
2101
+ return this.make({
2102
+ title,
2103
+ path,
2104
+ hasCounts: false,
2105
+ suites: suites.map(buildDefNode)
2106
+ });
2107
+ },
2108
+ fromResults(report) {
2109
+ const mod = report?.modules ? report.modules[0] : report;
2110
+ const { path = "", suites = [], counts = {} } = mod ?? {};
2111
+ return this.make({
2112
+ title: "Test run",
2113
+ path,
2114
+ pass: counts.pass ?? 0,
2115
+ fail: counts.fail ?? 0,
2116
+ skip: counts.skip ?? 0,
2117
+ hasCounts: true,
2118
+ suites: suites.map((s) => buildResultNode(s).comp)
2119
+ });
2120
+ }
2121
+ },
2122
+ view: html7`<div class="font-mono text-sm leading-tight flex flex-col gap-1">
2123
+ <div class="flex items-center gap-2 flex-wrap">
2124
+ <span class="font-semibold" @text=".title"></span>
2125
+ <span class="text-base-content/40 text-xs" @text=".path"></span>
2126
+ <span
2127
+ @show=".hasCounts"
2128
+ class="badge badge-sm badge-soft badge-success"
2129
+ @text="$passText"
2130
+ ></span>
2131
+ <span
2132
+ @show="$hasFailures"
2133
+ class="badge badge-sm badge-soft badge-error"
2134
+ @text="$failText"
2135
+ ></span>
2136
+ <span
2137
+ @show="$hasSkips"
2138
+ class="badge badge-sm badge-soft badge-neutral"
2139
+ @text="$skipText"
2140
+ ></span>
2141
+ </div>
2142
+ <x render-each=".suites"></x>
2143
+ </div>`
2144
+ });
2145
+ function getComponents7() {
2146
+ return [TestReport, TestSuite, TestCase, ...getComponents2()];
2147
+ }
2148
+
2149
+ // src/components/tutuca/instance-inspector.js
2150
+ var isComponentInstance = (v) => isRecord(v) && typeof v?.constructor?.getMetaClass === "function";
2151
+ var fieldsView = makeCompositeView({
2152
+ typeClass: "font-semibold",
2153
+ borderClass: "border-base-content/15"
2154
+ });
2155
+ var InstanceFields = component8({
2156
+ name: "InstanceFields",
2157
+ fields: { ...compositeFields, typeName: "" },
2158
+ methods: {
2159
+ ...compositeMethods,
2160
+ typeText() {
2161
+ return this.typeName;
2162
+ },
2163
+ countText() {
2164
+ return `{${this.items.size}}`;
2165
+ }
2166
+ },
2167
+ alter: compositeAlter,
2168
+ statics: {
2169
+ fromData(instance, comp) {
2170
+ const d = introspectComponent(comp);
2171
+ const items = d.fields.map((f) => ImEntry.make({
2172
+ key: f.name,
2173
+ child: ImInspector.Class.fromData(instance.get(f.name))
2174
+ }));
2175
+ return this.make({ typeName: d.name, items, isExpanded: true });
2176
+ }
2177
+ },
2178
+ view: fieldsView
2179
+ });
2180
+ var InstanceInspector = component8({
2181
+ name: "InstanceInspector",
2182
+ fields: { value: null },
2183
+ methods: {
2184
+ toggleIsExpanded() {
2185
+ return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
2186
+ }
2187
+ },
2188
+ statics: {
2189
+ fromData(instance, comp) {
2190
+ const value = comp && isComponentInstance(instance) ? InstanceFields.Class.fromData(instance, comp) : ImInspector.Class.fromData(instance);
2191
+ return this.make({ value });
2192
+ }
2193
+ },
2194
+ view: html8`<span class="contents"><x render=".value"></x></span>`
2195
+ });
2196
+ function asTestView(tests) {
2197
+ if (tests == null)
2198
+ return null;
2199
+ return isComponentInstance(tests) ? tests : TestReport.Class.fromResults(tests);
2200
+ }
2201
+ function asLintView(lint) {
2202
+ if (lint == null)
2203
+ return null;
2204
+ return isComponentInstance(lint) ? lint : LintReport.Class.fromData(lint);
2205
+ }
2206
+ var InstanceExplorer = component8({
2207
+ name: "InstanceExplorer",
2208
+ fields: {
2209
+ activeTab: "instance",
2210
+ instanceView: null,
2211
+ componentView: null,
2212
+ testView: null,
2213
+ lintView: null,
2214
+ hasComponent: false,
2215
+ hasTests: false,
2216
+ hasLint: false
2217
+ },
2218
+ statics: {
2219
+ fromData(instance, comp, { tests = null, lint = null } = {}) {
2220
+ const testView = asTestView(tests);
2221
+ const lintView = asLintView(lint);
2222
+ return this.make({
2223
+ instanceView: InstanceInspector.Class.fromData(instance, comp),
2224
+ componentView: comp ? ComponentInspector.Class.fromData(comp) : null,
2225
+ testView,
2226
+ lintView,
2227
+ hasComponent: !!comp,
2228
+ hasTests: !!testView,
2229
+ hasLint: !!lintView
2230
+ });
2231
+ }
2232
+ },
2233
+ view: html8`<div class="font-mono text-sm leading-snug flex flex-col gap-3">
2234
+ <div role="tablist" class="tabs">
2235
+ <a
2236
+ role="tab"
2237
+ @if.class="equals? .activeTab 'instance'"
2238
+ @then="'tab tab-active'"
2239
+ @else="'tab'"
2240
+ @on.click="$setActiveTab 'instance'"
2241
+ >
2242
+ Instance
2243
+ </a>
2244
+ <a
2245
+ role="tab"
2246
+ @show=".hasComponent"
2247
+ @if.class="equals? .activeTab 'component'"
2248
+ @then="'tab tab-active'"
2249
+ @else="'tab'"
2250
+ @on.click="$setActiveTab 'component'"
2251
+ >
2252
+ Component
2253
+ </a>
2254
+ <a
2255
+ role="tab"
2256
+ @show=".hasTests"
2257
+ @if.class="equals? .activeTab 'tests'"
2258
+ @then="'tab tab-active'"
2259
+ @else="'tab'"
2260
+ @on.click="$setActiveTab 'tests'"
2261
+ >
2262
+ Tests
2263
+ </a>
2264
+ <a
2265
+ role="tab"
2266
+ @show=".hasLint"
2267
+ @if.class="equals? .activeTab 'lint'"
2268
+ @then="'tab tab-active'"
2269
+ @else="'tab'"
2270
+ @on.click="$setActiveTab 'lint'"
2271
+ >
2272
+ Lint
2273
+ </a>
2274
+ </div>
2275
+ <div @show="equals? .activeTab 'instance'">
2276
+ <x render=".instanceView"></x>
2277
+ </div>
2278
+ <div @show="equals? .activeTab 'component'">
2279
+ <div @show=".hasComponent"><x render=".componentView"></x></div>
2280
+ <div @hide=".hasComponent" class="text-base-content/50 text-xs">
2281
+ Pass the Component descriptor to inspect its definition.
2282
+ </div>
2283
+ </div>
2284
+ <div @show="equals? .activeTab 'tests'">
2285
+ <x render=".testView"></x>
2286
+ </div>
2287
+ <div @show="equals? .activeTab 'lint'">
2288
+ <x render=".lintView"></x>
2289
+ </div>
2290
+ </div>`
2291
+ });
2292
+ function getComponents8() {
2293
+ const all = [
2294
+ InstanceExplorer,
2295
+ InstanceInspector,
2296
+ InstanceFields,
2297
+ ...getComponents5(),
2298
+ ...getComponents7(),
2299
+ ...getComponents6()
2300
+ ];
2301
+ const seen = new Set;
2302
+ return all.filter((c) => {
2303
+ if (seen.has(c.name))
2304
+ return false;
2305
+ seen.add(c.name);
2306
+ return true;
2307
+ });
2308
+ }
2309
+
2310
+ // src/components/build-views.js
2311
+ async function buildInspectorViews(value, scope, { getTests = null, components = [], dev = null, name } = {}) {
2312
+ const comp = isComponentInstance(value) ? scope.getCompFor(value) : null;
2313
+ const out = {
2314
+ instanceView: InstanceInspector.Class.fromData(value, scope.getCompFor(value)),
2315
+ componentView: null,
2316
+ lintView: null,
2317
+ testView: null,
2318
+ hasInspect: true,
2319
+ hasComponent: false,
2320
+ hasLint: false,
2321
+ hasTest: false
2322
+ };
2323
+ if (comp) {
2324
+ out.componentView = ComponentInspector.Class.fromData(comp);
2325
+ out.hasComponent = true;
2326
+ if (dev) {
2327
+ const findings = dev.shadowCheckComponent(comp);
2328
+ out.lintView = LintReport.Class.fromData({
2329
+ components: [{ componentName: comp.name, findings }]
2330
+ });
2331
+ out.hasLint = true;
2332
+ if (getTests) {
2333
+ const report = await dev.runTests({
2334
+ getTests,
2335
+ components,
2336
+ expect: dev.expect,
2337
+ name: name === undefined ? comp.name : name
2338
+ });
2339
+ out.testView = TestReport.Class.fromResults(report);
2340
+ out.hasTest = true;
2341
+ }
2342
+ }
2343
+ }
2344
+ return out;
2345
+ }
2346
+
2347
+ // src/components/index.js
2348
+ function getComponents9() {
2349
+ const all = [
2350
+ ...getComponents5(),
2351
+ ...getComponents8(),
2352
+ ...getComponents6(),
2353
+ ...getComponents7(),
2354
+ ...getComponents3(),
2355
+ ...getComponents4(),
2356
+ ...getComponents2(),
2357
+ ...getComponents()
2358
+ ];
2359
+ const seen = new Set;
2360
+ return all.filter((c) => {
2361
+ if (seen.has(c.name))
2362
+ return false;
2363
+ seen.add(c.name);
2364
+ return true;
2365
+ });
2366
+ }
2367
+ export {
2368
+ makeCompositeView,
2369
+ lintMessage,
2370
+ isComponentInstance,
2371
+ introspectComponent,
2372
+ getComponents9 as getComponents,
2373
+ fmtMapKey,
2374
+ fmtAnyKey,
2375
+ compositeView,
2376
+ compositeMethods,
2377
+ compositeFields,
2378
+ compositeAlter,
2379
+ collectTests,
2380
+ classifySchema,
2381
+ classifyJson,
2382
+ classifyJsExtra,
2383
+ classifyImmutable,
2384
+ classifyData,
2385
+ chain,
2386
+ buildInspectorViews,
2387
+ TestSuite,
2388
+ TestReport,
2389
+ TestCase,
2390
+ SchemaViewer,
2391
+ SchemaScalar,
2392
+ SchemaRef,
2393
+ SchemaProperty,
2394
+ SchemaObject,
2395
+ SchemaNot,
2396
+ SchemaEnum,
2397
+ SchemaConst,
2398
+ SchemaConditional,
2399
+ SchemaCombinator,
2400
+ SchemaBranch,
2401
+ SchemaBoolean,
2402
+ SchemaArray,
2403
+ LintReport,
2404
+ LintFinding,
2405
+ LintComponent,
2406
+ JsonViewer,
2407
+ JsonString,
2408
+ JsonProperty,
2409
+ JsonObject,
2410
+ JsonNumber,
2411
+ JsonNull,
2412
+ JsonBoolean,
2413
+ JsonArray,
2414
+ JsUndefined,
2415
+ JsSymbol,
2416
+ JsSetItem,
2417
+ JsSet,
2418
+ JsRegExp,
2419
+ JsMap,
2420
+ JsFunction,
2421
+ JsError,
2422
+ JsDate,
2423
+ JsClassInstance,
2424
+ JsBigInt,
2425
+ InstanceInspector,
2426
+ InstanceFields,
2427
+ InstanceExplorer,
2428
+ ImStack,
2429
+ ImSet,
2430
+ ImRecord,
2431
+ ImRange,
2432
+ ImOSet,
2433
+ ImOMap,
2434
+ ImMap,
2435
+ ImList,
2436
+ ImInspector,
2437
+ ImEntry,
2438
+ DataInspector,
2439
+ ComponentInspector,
2440
+ CompView,
2441
+ CompSection,
2442
+ CompName,
2443
+ CompField
2444
+ };