@databrainhq/mcp-server 0.3.0-rc.13 → 0.3.0-rc.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +6 -1
- package/dist/apps/server.d.ts +102 -389
- package/dist/apps/server.js +5 -5
- package/dist/apps/stdio.d.ts +1 -2
- package/dist/apps/stdio.js +6 -6
- package/dist/{bundle-TDQXWIPM.js → bundle-QJNCUMNY.js} +2 -2
- package/dist/{chunk-Q5EYDKSA.js → chunk-2F7V6RZR.js} +511 -107
- package/dist/chunk-2F7V6RZR.js.map +1 -0
- package/dist/{chunk-AEQTEE5C.js → chunk-AWICHHDU.js} +153 -61
- package/dist/chunk-AWICHHDU.js.map +1 -0
- package/dist/{chunk-X44C5GW2.js → chunk-CEGHG5U3.js} +59 -18
- package/dist/chunk-CEGHG5U3.js.map +1 -0
- package/dist/{chunk-233VGFHY.js → chunk-NIC5MAGC.js} +317 -147
- package/dist/chunk-NIC5MAGC.js.map +1 -0
- package/dist/{chunk-DTFYHLYN.js → chunk-S65KA3Z7.js} +26 -11
- package/dist/chunk-S65KA3Z7.js.map +1 -0
- package/dist/{chunk-PSJVYD4W.js → chunk-UYFFFAYV.js} +11722 -10560
- package/dist/chunk-UYFFFAYV.js.map +1 -0
- package/dist/{chunk-HCYZN7I3.js → chunk-VQ5G5AZV.js} +10 -7
- package/dist/chunk-VQ5G5AZV.js.map +1 -0
- package/dist/cli.d.ts +16 -15
- package/dist/cli.js +134 -36
- package/dist/cli.js.map +1 -1
- package/dist/custom-chart/custom-runtime.js +6 -6
- package/dist/custom-chart/template/chart.ts +116 -28
- package/dist/custom-chart/template/icon.svg +1 -1
- package/dist/custom-chart/template/manifest.json +66 -12
- package/dist/custom-chart/template/sample-data.json +21 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.js +20 -10
- package/dist/index.js.map +1 -1
- package/dist/{main-VEKNKWL5.js → main-QCBVJSSR.js} +348 -155
- package/dist/main-QCBVJSSR.js.map +1 -0
- package/dist/{test-only-LJGOY2QA.js → test-only-6474NMTR.js} +12 -8
- package/dist/test-only-6474NMTR.js.map +1 -0
- package/package.json +20 -9
- package/dist/chunk-233VGFHY.js.map +0 -1
- package/dist/chunk-AEQTEE5C.js.map +0 -1
- package/dist/chunk-DTFYHLYN.js.map +0 -1
- package/dist/chunk-HCYZN7I3.js.map +0 -1
- package/dist/chunk-PSJVYD4W.js.map +0 -1
- package/dist/chunk-Q5EYDKSA.js.map +0 -1
- package/dist/chunk-X44C5GW2.js.map +0 -1
- package/dist/main-VEKNKWL5.js.map +0 -1
- package/dist/test-only-LJGOY2QA.js.map +0 -1
- /package/dist/{bundle-TDQXWIPM.js.map → bundle-QJNCUMNY.js.map} +0 -0
|
@@ -7,10 +7,10 @@ import { join as join2, resolve as resolve2, sep } from "path";
|
|
|
7
7
|
import { build as esbuild } from "esbuild";
|
|
8
8
|
|
|
9
9
|
// src/custom-chart/assets.ts
|
|
10
|
-
import { buildSync } from "esbuild";
|
|
11
10
|
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
12
11
|
import { dirname, join, resolve } from "path";
|
|
13
12
|
import { fileURLToPath } from "url";
|
|
13
|
+
import { buildSync } from "esbuild";
|
|
14
14
|
var cachedDir;
|
|
15
15
|
function customChartAssetDir() {
|
|
16
16
|
if (cachedDir) return cachedDir;
|
|
@@ -311,9 +311,10 @@ var cleanAttributes = (element, rawAttributes) => {
|
|
|
311
311
|
let dropElement = false;
|
|
312
312
|
const kept = [];
|
|
313
313
|
ATTRIBUTE_RE.lastIndex = 0;
|
|
314
|
-
let match;
|
|
315
|
-
while (
|
|
314
|
+
let match = ATTRIBUTE_RE.exec(rawAttributes);
|
|
315
|
+
while (match !== null) {
|
|
316
316
|
const [whole, name, dq, sq, bare] = match;
|
|
317
|
+
match = ATTRIBUTE_RE.exec(rawAttributes);
|
|
317
318
|
if (!name || name === "/") continue;
|
|
318
319
|
const lower = name.toLowerCase();
|
|
319
320
|
const value = dq ?? sq ?? bare ?? "";
|
|
@@ -464,17 +465,47 @@ var MAX_OPTIONS = 32;
|
|
|
464
465
|
// src/custom-chart/icon.ts
|
|
465
466
|
var UNSAFE = "bundle.icon.unsafe";
|
|
466
467
|
var RULES = [
|
|
467
|
-
{
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
468
|
+
{
|
|
469
|
+
pattern: /<\s*script\b/i,
|
|
470
|
+
message: "Remove every <script> element from icon.svg."
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
pattern: /<\s*foreignObject\b/i,
|
|
474
|
+
message: "Remove every <foreignObject> element from icon.svg."
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
pattern: /<\s*image\b/i,
|
|
478
|
+
message: "Remove every <image> element from icon.svg. Draw with paths and shapes."
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
pattern: /[\s/]on[a-z]+\s*=/i,
|
|
482
|
+
message: "Remove every on*= event attribute from icon.svg."
|
|
483
|
+
},
|
|
471
484
|
{ pattern: /<!--/, message: "Remove every <!-- comment --> from icon.svg." },
|
|
472
|
-
{
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
{
|
|
477
|
-
|
|
485
|
+
{
|
|
486
|
+
pattern: /<!(?:ENTITY|DOCTYPE)\b/i,
|
|
487
|
+
message: "Remove the DOCTYPE and entity declarations from icon.svg."
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
pattern: /<!\[CDATA\[/,
|
|
491
|
+
message: "Remove every CDATA section from icon.svg."
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
pattern: /(?:xlink:)?href\s*=\s*["']\s*(?!#)/i,
|
|
495
|
+
message: "Every href in icon.svg must start with # (no external or data references)."
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
pattern: /javascript\s*:/i,
|
|
499
|
+
message: "Remove every javascript: reference from icon.svg."
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
pattern: /@import|expression\s*\(|-moz-binding/i,
|
|
503
|
+
message: "Remove @import, expression() and -moz-binding from icon.svg styles."
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
pattern: /url\s*\(\s*['"]?\s*(?!#)/i,
|
|
507
|
+
message: "Every url() in icon.svg must point at a same-document #id."
|
|
508
|
+
}
|
|
478
509
|
];
|
|
479
510
|
var GENERIC_UNSAFE = "icon.svg must use only plain SVG drawing elements and attributes: no script, event handlers, foreignObject, image, animation, external hrefs, url() styles, comments, DOCTYPE or unknown attributes.";
|
|
480
511
|
var XML_DECLARATION = /^\s*<\?xml[^>]*\?>\s*/i;
|
|
@@ -484,18 +515,38 @@ function isSingleSvgRoot(svg) {
|
|
|
484
515
|
}
|
|
485
516
|
function validateIcon(svg, path = "/icon.svg") {
|
|
486
517
|
if (typeof svg !== "string" || svg.trim().length === 0) {
|
|
487
|
-
return [
|
|
518
|
+
return [
|
|
519
|
+
{
|
|
520
|
+
code: "bundle.icon.required",
|
|
521
|
+
path,
|
|
522
|
+
message: "icon.svg is required and must not be empty."
|
|
523
|
+
}
|
|
524
|
+
];
|
|
488
525
|
}
|
|
489
526
|
if (Buffer.byteLength(svg, "utf8") > MAX_ICON_BYTES) {
|
|
490
|
-
return [
|
|
527
|
+
return [
|
|
528
|
+
{
|
|
529
|
+
code: "bundle.icon.size",
|
|
530
|
+
path,
|
|
531
|
+
message: `icon.svg must be at most ${MAX_ICON_BYTES} bytes.`
|
|
532
|
+
}
|
|
533
|
+
];
|
|
491
534
|
}
|
|
492
535
|
const cleaned = sanitizeSvg(svg);
|
|
493
536
|
if (!cleaned.isSvg || !isSingleSvgRoot(cleaned.svg)) {
|
|
494
|
-
return [
|
|
537
|
+
return [
|
|
538
|
+
{
|
|
539
|
+
code: "bundle.icon.notSvg",
|
|
540
|
+
path,
|
|
541
|
+
message: "icon.svg must be one <svg> element with nothing after it."
|
|
542
|
+
}
|
|
543
|
+
];
|
|
495
544
|
}
|
|
496
545
|
if (cleaned.removed > 0 || /<\s*image\b/i.test(svg)) {
|
|
497
546
|
const rule = RULES.find((candidate) => candidate.pattern.test(svg));
|
|
498
|
-
return [
|
|
547
|
+
return [
|
|
548
|
+
{ code: UNSAFE, path, message: rule ? rule.message : GENERIC_UNSAFE }
|
|
549
|
+
];
|
|
499
550
|
}
|
|
500
551
|
return [];
|
|
501
552
|
}
|
|
@@ -559,13 +610,21 @@ var Collector = class {
|
|
|
559
610
|
let ok = true;
|
|
560
611
|
for (const key of Object.keys(value)) {
|
|
561
612
|
if (!allowed.has(key)) {
|
|
562
|
-
this.add(
|
|
613
|
+
this.add(
|
|
614
|
+
`${scope}.unknownKey`,
|
|
615
|
+
`${path}/${key}`,
|
|
616
|
+
`Remove "${key}". It is not a known key here.`
|
|
617
|
+
);
|
|
563
618
|
ok = false;
|
|
564
619
|
}
|
|
565
620
|
}
|
|
566
621
|
for (const key of required) {
|
|
567
622
|
if (!Object.hasOwn(value, key)) {
|
|
568
|
-
this.add(
|
|
623
|
+
this.add(
|
|
624
|
+
missingCode(key),
|
|
625
|
+
`${path}/${key}`,
|
|
626
|
+
`Add the required key "${key}".`
|
|
627
|
+
);
|
|
569
628
|
ok = false;
|
|
570
629
|
}
|
|
571
630
|
}
|
|
@@ -577,29 +636,64 @@ function validateSlot(c, value, path, v2) {
|
|
|
577
636
|
c.add("manifest.slot.invalid", path, "A slot must be an object.");
|
|
578
637
|
return;
|
|
579
638
|
}
|
|
580
|
-
if (!c.checkKeys(
|
|
639
|
+
if (!c.checkKeys(
|
|
640
|
+
value,
|
|
641
|
+
path,
|
|
642
|
+
"manifest.slot",
|
|
643
|
+
V1_SLOT_KEYS,
|
|
644
|
+
v2 ? V2_SLOT_EXTRA_KEYS : []
|
|
645
|
+
))
|
|
646
|
+
return;
|
|
581
647
|
if (!isId(value.id)) {
|
|
582
|
-
c.add(
|
|
648
|
+
c.add(
|
|
649
|
+
"manifest.slot.id",
|
|
650
|
+
`${path}/id`,
|
|
651
|
+
"A slot id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters."
|
|
652
|
+
);
|
|
583
653
|
}
|
|
584
654
|
if (!isText(value.label)) {
|
|
585
|
-
c.add(
|
|
655
|
+
c.add(
|
|
656
|
+
"manifest.slot.label",
|
|
657
|
+
`${path}/label`,
|
|
658
|
+
`A slot label is 1 to ${MAX_TEXT} characters.`
|
|
659
|
+
);
|
|
586
660
|
}
|
|
587
661
|
const role = value.role;
|
|
588
662
|
if (role !== "dimension" && role !== "measure") {
|
|
589
|
-
c.add(
|
|
663
|
+
c.add(
|
|
664
|
+
"manifest.slot.role",
|
|
665
|
+
`${path}/role`,
|
|
666
|
+
'A slot role is "dimension" or "measure".'
|
|
667
|
+
);
|
|
590
668
|
}
|
|
591
669
|
if (typeof value.required !== "boolean") {
|
|
592
|
-
c.add(
|
|
670
|
+
c.add(
|
|
671
|
+
"manifest.slot.required",
|
|
672
|
+
`${path}/required`,
|
|
673
|
+
"required is true or false."
|
|
674
|
+
);
|
|
593
675
|
}
|
|
594
676
|
const types = value.types;
|
|
595
677
|
const typesOk = Array.isArray(types) && types.length > 0 && types.length <= 3 && new Set(types).size === types.length && types.every((type) => FIELD_TYPES.includes(type));
|
|
596
678
|
if (!typesOk) {
|
|
597
|
-
c.add(
|
|
679
|
+
c.add(
|
|
680
|
+
"manifest.slot.types",
|
|
681
|
+
`${path}/types`,
|
|
682
|
+
'types is a list of 1 to 3 distinct values from "string", "number" and "boolean".'
|
|
683
|
+
);
|
|
598
684
|
} else if (role === "measure" && (types.length !== 1 || types[0] !== "number")) {
|
|
599
|
-
c.add(
|
|
685
|
+
c.add(
|
|
686
|
+
"manifest.slot.types",
|
|
687
|
+
`${path}/types`,
|
|
688
|
+
'A measure slot accepts only ["number"].'
|
|
689
|
+
);
|
|
600
690
|
}
|
|
601
691
|
if (Object.hasOwn(value, "description") && !isText(value.description)) {
|
|
602
|
-
c.add(
|
|
692
|
+
c.add(
|
|
693
|
+
"manifest.slot.description",
|
|
694
|
+
`${path}/description`,
|
|
695
|
+
`A slot description is 1 to ${MAX_TEXT} characters.`
|
|
696
|
+
);
|
|
603
697
|
}
|
|
604
698
|
}
|
|
605
699
|
function validateOption(c, value, path, v2) {
|
|
@@ -609,7 +703,11 @@ function validateOption(c, value, path, v2) {
|
|
|
609
703
|
}
|
|
610
704
|
const id = value.id;
|
|
611
705
|
if (typeof id === "string" && (id === "theme" || id.startsWith("theme."))) {
|
|
612
|
-
c.add(
|
|
706
|
+
c.add(
|
|
707
|
+
"manifest.option.reserved",
|
|
708
|
+
`${path}/id`,
|
|
709
|
+
'The option id "theme" (and any id starting with "theme.") is reserved for the host theme.'
|
|
710
|
+
);
|
|
613
711
|
return;
|
|
614
712
|
}
|
|
615
713
|
const type = typeof value.type === "string" ? value.type : "";
|
|
@@ -622,46 +720,89 @@ function validateOption(c, value, path, v2) {
|
|
|
622
720
|
);
|
|
623
721
|
return;
|
|
624
722
|
}
|
|
625
|
-
if (!c.checkKeys(
|
|
723
|
+
if (!c.checkKeys(
|
|
724
|
+
value,
|
|
725
|
+
path,
|
|
726
|
+
"manifest.option",
|
|
727
|
+
[...OPTION_BASE_KEYS, ...typeKeys],
|
|
728
|
+
v2 ? V2_OPTION_EXTRA_KEYS[type] : []
|
|
729
|
+
))
|
|
730
|
+
return;
|
|
626
731
|
if (!isId(id)) {
|
|
627
|
-
c.add(
|
|
732
|
+
c.add(
|
|
733
|
+
"manifest.option.id",
|
|
734
|
+
`${path}/id`,
|
|
735
|
+
"An option id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters."
|
|
736
|
+
);
|
|
628
737
|
}
|
|
629
738
|
if (!isText(value.label)) {
|
|
630
|
-
c.add(
|
|
739
|
+
c.add(
|
|
740
|
+
"manifest.option.label",
|
|
741
|
+
`${path}/label`,
|
|
742
|
+
`An option label is 1 to ${MAX_TEXT} characters.`
|
|
743
|
+
);
|
|
631
744
|
}
|
|
632
745
|
if (Object.hasOwn(value, "group") && !isText(value.group, MAX_GROUP)) {
|
|
633
|
-
c.add(
|
|
746
|
+
c.add(
|
|
747
|
+
"manifest.option.group",
|
|
748
|
+
`${path}/group`,
|
|
749
|
+
`An option group is 1 to ${MAX_GROUP} characters.`
|
|
750
|
+
);
|
|
634
751
|
}
|
|
635
752
|
switch (type) {
|
|
636
753
|
case "boolean":
|
|
637
754
|
if (typeof value.default !== "boolean") {
|
|
638
|
-
c.add(
|
|
755
|
+
c.add(
|
|
756
|
+
"manifest.option.default",
|
|
757
|
+
`${path}/default`,
|
|
758
|
+
"A boolean default is true or false."
|
|
759
|
+
);
|
|
639
760
|
}
|
|
640
761
|
break;
|
|
641
762
|
case "number": {
|
|
642
763
|
const rangeOk = isFiniteNumber(value.min) && isFiniteNumber(value.max) && value.min <= value.max;
|
|
643
764
|
if (!rangeOk) {
|
|
644
|
-
c.add(
|
|
765
|
+
c.add(
|
|
766
|
+
"manifest.option.range",
|
|
767
|
+
`${path}/min`,
|
|
768
|
+
"Set finite min and max with min <= max."
|
|
769
|
+
);
|
|
645
770
|
break;
|
|
646
771
|
}
|
|
647
772
|
const min = value.min;
|
|
648
773
|
const max = value.max;
|
|
649
774
|
if (!isFiniteNumber(value.default) || value.default < min || value.default > max) {
|
|
650
|
-
c.add(
|
|
775
|
+
c.add(
|
|
776
|
+
"manifest.option.default",
|
|
777
|
+
`${path}/default`,
|
|
778
|
+
"A number default is a finite number between min and max."
|
|
779
|
+
);
|
|
651
780
|
}
|
|
652
781
|
if (Object.hasOwn(value, "step") && (!isFiniteNumber(value.step) || value.step <= 0 || value.step > max - min)) {
|
|
653
|
-
c.add(
|
|
782
|
+
c.add(
|
|
783
|
+
"manifest.option.step",
|
|
784
|
+
`${path}/step`,
|
|
785
|
+
"step is a positive number no larger than max - min."
|
|
786
|
+
);
|
|
654
787
|
}
|
|
655
788
|
break;
|
|
656
789
|
}
|
|
657
790
|
case "string": {
|
|
658
791
|
const maxLength = value.maxLength;
|
|
659
792
|
if (!isFiniteNumber(maxLength) || !Number.isInteger(maxLength) || maxLength < 1 || maxLength > MAX_STRING_OPTION_LENGTH) {
|
|
660
|
-
c.add(
|
|
793
|
+
c.add(
|
|
794
|
+
"manifest.option.maxLength",
|
|
795
|
+
`${path}/maxLength`,
|
|
796
|
+
`A string option needs an integer "maxLength" from 1 to ${MAX_STRING_OPTION_LENGTH}.`
|
|
797
|
+
);
|
|
661
798
|
break;
|
|
662
799
|
}
|
|
663
800
|
if (typeof value.default !== "string" || value.default.length > maxLength) {
|
|
664
|
-
c.add(
|
|
801
|
+
c.add(
|
|
802
|
+
"manifest.option.default",
|
|
803
|
+
`${path}/default`,
|
|
804
|
+
"A string default is a string no longer than maxLength."
|
|
805
|
+
);
|
|
665
806
|
}
|
|
666
807
|
break;
|
|
667
808
|
}
|
|
@@ -671,17 +812,29 @@ function validateOption(c, value, path, v2) {
|
|
|
671
812
|
(choice) => isRecord(choice) && Object.keys(choice).length === 2 && isId(choice.value) && isText(choice.label)
|
|
672
813
|
) && new Set(choices.map((choice) => choice.value)).size === choices.length;
|
|
673
814
|
if (!choicesOk) {
|
|
674
|
-
c.add(
|
|
815
|
+
c.add(
|
|
816
|
+
"manifest.option.choices",
|
|
817
|
+
`${path}/choices`,
|
|
818
|
+
`choices is a list of 1 to ${MAX_CHOICES} distinct { value, label } pairs; a value follows the id rule.`
|
|
819
|
+
);
|
|
675
820
|
break;
|
|
676
821
|
}
|
|
677
822
|
if (!choices.some((choice) => choice.value === value.default)) {
|
|
678
|
-
c.add(
|
|
823
|
+
c.add(
|
|
824
|
+
"manifest.option.default",
|
|
825
|
+
`${path}/default`,
|
|
826
|
+
"A select default is the value of one of its choices."
|
|
827
|
+
);
|
|
679
828
|
}
|
|
680
829
|
break;
|
|
681
830
|
}
|
|
682
831
|
case "color":
|
|
683
832
|
if (typeof value.default !== "string" || !COLOR_PATTERN.test(value.default)) {
|
|
684
|
-
c.add(
|
|
833
|
+
c.add(
|
|
834
|
+
"manifest.option.color",
|
|
835
|
+
`${path}/default`,
|
|
836
|
+
'A color default is a 6 digit hex colour like "#3366cc".'
|
|
837
|
+
);
|
|
685
838
|
}
|
|
686
839
|
break;
|
|
687
840
|
default:
|
|
@@ -691,44 +844,90 @@ function validateOption(c, value, path, v2) {
|
|
|
691
844
|
function validateTranslations(c, value, slots, options) {
|
|
692
845
|
const path = "/translations";
|
|
693
846
|
if (!isRecord(value)) {
|
|
694
|
-
c.add(
|
|
847
|
+
c.add(
|
|
848
|
+
"manifest.translations.invalid",
|
|
849
|
+
path,
|
|
850
|
+
"translations is an object keyed by language code."
|
|
851
|
+
);
|
|
695
852
|
return;
|
|
696
853
|
}
|
|
697
854
|
for (const [language, entry] of Object.entries(value)) {
|
|
698
855
|
const langPath = `${path}/${language}`;
|
|
699
856
|
if (!LANGUAGE_PATTERN.test(language)) {
|
|
700
|
-
c.add(
|
|
857
|
+
c.add(
|
|
858
|
+
"manifest.translations.language",
|
|
859
|
+
langPath,
|
|
860
|
+
`"${language}" is not a language code like "fr" or "pt-BR".`
|
|
861
|
+
);
|
|
701
862
|
continue;
|
|
702
863
|
}
|
|
703
864
|
if (!isRecord(entry)) {
|
|
704
|
-
c.add(
|
|
865
|
+
c.add(
|
|
866
|
+
"manifest.translations.invalid",
|
|
867
|
+
langPath,
|
|
868
|
+
"A translation is an object with optional name, description, slots and options."
|
|
869
|
+
);
|
|
705
870
|
continue;
|
|
706
871
|
}
|
|
707
|
-
if (!c.checkKeys(
|
|
872
|
+
if (!c.checkKeys(
|
|
873
|
+
entry,
|
|
874
|
+
langPath,
|
|
875
|
+
"manifest.translations",
|
|
876
|
+
[],
|
|
877
|
+
TRANSLATION_KEYS
|
|
878
|
+
))
|
|
879
|
+
continue;
|
|
708
880
|
for (const key of ["name", "description"]) {
|
|
709
881
|
if (Object.hasOwn(entry, key) && !isTranslationText(entry[key])) {
|
|
710
|
-
c.add(
|
|
882
|
+
c.add(
|
|
883
|
+
"manifest.translations.text",
|
|
884
|
+
`${langPath}/${key}`,
|
|
885
|
+
`A translated string is at most ${MAX_TEXT} characters.`
|
|
886
|
+
);
|
|
711
887
|
}
|
|
712
888
|
}
|
|
713
889
|
if (Object.hasOwn(entry, "slots")) {
|
|
714
890
|
const entrySlots = entry.slots;
|
|
715
891
|
if (!isRecord(entrySlots)) {
|
|
716
|
-
c.add(
|
|
892
|
+
c.add(
|
|
893
|
+
"manifest.translations.invalid",
|
|
894
|
+
`${langPath}/slots`,
|
|
895
|
+
"slots is an object keyed by slot id."
|
|
896
|
+
);
|
|
717
897
|
} else {
|
|
718
898
|
for (const [slotId, slotEntry] of Object.entries(entrySlots)) {
|
|
719
899
|
const slotPath = `${langPath}/slots/${slotId}`;
|
|
720
900
|
if (!slots.some((slot) => slot.id === slotId)) {
|
|
721
|
-
c.add(
|
|
901
|
+
c.add(
|
|
902
|
+
"manifest.translations.slot",
|
|
903
|
+
slotPath,
|
|
904
|
+
`"${slotId}" is not a slot id in this manifest.`
|
|
905
|
+
);
|
|
722
906
|
continue;
|
|
723
907
|
}
|
|
724
908
|
if (!isRecord(slotEntry)) {
|
|
725
|
-
c.add(
|
|
909
|
+
c.add(
|
|
910
|
+
"manifest.translations.invalid",
|
|
911
|
+
slotPath,
|
|
912
|
+
"A slot translation is an object with optional label and description."
|
|
913
|
+
);
|
|
726
914
|
continue;
|
|
727
915
|
}
|
|
728
|
-
if (!c.checkKeys(
|
|
916
|
+
if (!c.checkKeys(
|
|
917
|
+
slotEntry,
|
|
918
|
+
slotPath,
|
|
919
|
+
"manifest.translations",
|
|
920
|
+
[],
|
|
921
|
+
SLOT_TRANSLATION_KEYS
|
|
922
|
+
))
|
|
923
|
+
continue;
|
|
729
924
|
for (const key of ["label", "description"]) {
|
|
730
925
|
if (Object.hasOwn(slotEntry, key) && !isTranslationText(slotEntry[key])) {
|
|
731
|
-
c.add(
|
|
926
|
+
c.add(
|
|
927
|
+
"manifest.translations.text",
|
|
928
|
+
`${slotPath}/${key}`,
|
|
929
|
+
`A translated string is at most ${MAX_TEXT} characters.`
|
|
930
|
+
);
|
|
732
931
|
}
|
|
733
932
|
}
|
|
734
933
|
}
|
|
@@ -737,37 +936,74 @@ function validateTranslations(c, value, slots, options) {
|
|
|
737
936
|
if (Object.hasOwn(entry, "options")) {
|
|
738
937
|
const entryOptions = entry.options;
|
|
739
938
|
if (!isRecord(entryOptions)) {
|
|
740
|
-
c.add(
|
|
939
|
+
c.add(
|
|
940
|
+
"manifest.translations.invalid",
|
|
941
|
+
`${langPath}/options`,
|
|
942
|
+
"options is an object keyed by option id."
|
|
943
|
+
);
|
|
741
944
|
} else {
|
|
742
945
|
for (const [optionId, optionEntry] of Object.entries(entryOptions)) {
|
|
743
946
|
const optionPath = `${langPath}/options/${optionId}`;
|
|
744
947
|
const option = options.find((item) => item.id === optionId);
|
|
745
948
|
if (!option) {
|
|
746
|
-
c.add(
|
|
949
|
+
c.add(
|
|
950
|
+
"manifest.translations.option",
|
|
951
|
+
optionPath,
|
|
952
|
+
`"${optionId}" is not an option id in this manifest.`
|
|
953
|
+
);
|
|
747
954
|
continue;
|
|
748
955
|
}
|
|
749
956
|
if (!isRecord(optionEntry)) {
|
|
750
|
-
c.add(
|
|
957
|
+
c.add(
|
|
958
|
+
"manifest.translations.invalid",
|
|
959
|
+
optionPath,
|
|
960
|
+
"An option translation is an object with optional label, group and choices."
|
|
961
|
+
);
|
|
751
962
|
continue;
|
|
752
963
|
}
|
|
753
|
-
if (!c.checkKeys(
|
|
964
|
+
if (!c.checkKeys(
|
|
965
|
+
optionEntry,
|
|
966
|
+
optionPath,
|
|
967
|
+
"manifest.translations",
|
|
968
|
+
[],
|
|
969
|
+
OPTION_TRANSLATION_KEYS
|
|
970
|
+
))
|
|
971
|
+
continue;
|
|
754
972
|
for (const key of ["label", "group"]) {
|
|
755
973
|
if (Object.hasOwn(optionEntry, key) && !isTranslationText(optionEntry[key])) {
|
|
756
|
-
c.add(
|
|
974
|
+
c.add(
|
|
975
|
+
"manifest.translations.text",
|
|
976
|
+
`${optionPath}/${key}`,
|
|
977
|
+
`A translated string is at most ${MAX_TEXT} characters.`
|
|
978
|
+
);
|
|
757
979
|
}
|
|
758
980
|
}
|
|
759
981
|
if (!Object.hasOwn(optionEntry, "choices")) continue;
|
|
760
982
|
const choices = optionEntry.choices;
|
|
761
983
|
if (!isRecord(choices)) {
|
|
762
|
-
c.add(
|
|
984
|
+
c.add(
|
|
985
|
+
"manifest.translations.invalid",
|
|
986
|
+
`${optionPath}/choices`,
|
|
987
|
+
"choices is an object keyed by choice value."
|
|
988
|
+
);
|
|
763
989
|
continue;
|
|
764
990
|
}
|
|
765
991
|
for (const [choiceValue, label] of Object.entries(choices)) {
|
|
766
992
|
const choicePath = `${optionPath}/choices/${choiceValue}`;
|
|
767
|
-
if (option.type !== "select" || !(option.choices ?? []).some(
|
|
768
|
-
|
|
993
|
+
if (option.type !== "select" || !(option.choices ?? []).some(
|
|
994
|
+
(choice) => choice.value === choiceValue
|
|
995
|
+
)) {
|
|
996
|
+
c.add(
|
|
997
|
+
"manifest.translations.choice",
|
|
998
|
+
choicePath,
|
|
999
|
+
`"${choiceValue}" is not a choice of option "${optionId}".`
|
|
1000
|
+
);
|
|
769
1001
|
} else if (!isTranslationText(label)) {
|
|
770
|
-
c.add(
|
|
1002
|
+
c.add(
|
|
1003
|
+
"manifest.translations.text",
|
|
1004
|
+
choicePath,
|
|
1005
|
+
`A translated string is at most ${MAX_TEXT} characters.`
|
|
1006
|
+
);
|
|
771
1007
|
}
|
|
772
1008
|
}
|
|
773
1009
|
}
|
|
@@ -783,24 +1019,51 @@ function validateManifest(value) {
|
|
|
783
1019
|
}
|
|
784
1020
|
const schemaVersion = value.schemaVersion;
|
|
785
1021
|
if (schemaVersion !== 1 && schemaVersion !== 2) {
|
|
786
|
-
c.add(
|
|
1022
|
+
c.add(
|
|
1023
|
+
"manifest.schemaVersion.unsupported",
|
|
1024
|
+
"/schemaVersion",
|
|
1025
|
+
"schemaVersion must be 1 or 2."
|
|
1026
|
+
);
|
|
787
1027
|
return c.problems;
|
|
788
1028
|
}
|
|
789
1029
|
const v2 = schemaVersion === 2;
|
|
790
|
-
if (!c.checkKeys(
|
|
1030
|
+
if (!c.checkKeys(
|
|
1031
|
+
value,
|
|
1032
|
+
"",
|
|
1033
|
+
"manifest",
|
|
1034
|
+
V1_TOP_KEYS,
|
|
1035
|
+
v2 ? V2_TOP_EXTRA_KEYS : [],
|
|
1036
|
+
(key) => `manifest.${key}.required`
|
|
1037
|
+
)) {
|
|
791
1038
|
return c.problems;
|
|
792
1039
|
}
|
|
793
1040
|
if (!isId(value.id)) {
|
|
794
|
-
c.add(
|
|
1041
|
+
c.add(
|
|
1042
|
+
"manifest.id.invalid",
|
|
1043
|
+
"/id",
|
|
1044
|
+
'id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters. It becomes the chart type "custom:<id>".'
|
|
1045
|
+
);
|
|
795
1046
|
}
|
|
796
1047
|
if (!isText(value.name)) {
|
|
797
|
-
c.add(
|
|
1048
|
+
c.add(
|
|
1049
|
+
"manifest.name.invalid",
|
|
1050
|
+
"/name",
|
|
1051
|
+
`name is 1 to ${MAX_TEXT} characters.`
|
|
1052
|
+
);
|
|
798
1053
|
}
|
|
799
1054
|
if (Object.hasOwn(value, "version") && (typeof value.version !== "string" || !SEMVER_PATTERN.test(value.version))) {
|
|
800
|
-
c.add(
|
|
1055
|
+
c.add(
|
|
1056
|
+
"manifest.version.invalid",
|
|
1057
|
+
"/version",
|
|
1058
|
+
'version is a semver string like "1.2.0".'
|
|
1059
|
+
);
|
|
801
1060
|
}
|
|
802
1061
|
if (Object.hasOwn(value, "description") && !isText(value.description, MAX_DESCRIPTION)) {
|
|
803
|
-
c.add(
|
|
1062
|
+
c.add(
|
|
1063
|
+
"manifest.description.invalid",
|
|
1064
|
+
"/description",
|
|
1065
|
+
`description is 1 to ${MAX_DESCRIPTION} characters.`
|
|
1066
|
+
);
|
|
804
1067
|
}
|
|
805
1068
|
const slots = value.slots;
|
|
806
1069
|
if (!Array.isArray(slots)) {
|
|
@@ -808,30 +1071,54 @@ function validateManifest(value) {
|
|
|
808
1071
|
} else if (slots.length === 0) {
|
|
809
1072
|
c.add("manifest.slots.empty", "/slots", "Declare at least one slot.");
|
|
810
1073
|
} else if (slots.length > MAX_SLOTS) {
|
|
811
|
-
c.add(
|
|
1074
|
+
c.add(
|
|
1075
|
+
"manifest.slots.tooMany",
|
|
1076
|
+
"/slots",
|
|
1077
|
+
`Declare at most ${MAX_SLOTS} slots.`
|
|
1078
|
+
);
|
|
812
1079
|
} else {
|
|
813
|
-
|
|
1080
|
+
for (const [index, slot] of slots.entries()) {
|
|
1081
|
+
validateSlot(c, slot, `/slots/${index}`, v2);
|
|
1082
|
+
}
|
|
814
1083
|
const seen = /* @__PURE__ */ new Set();
|
|
815
1084
|
slots.forEach((slot, index) => {
|
|
816
1085
|
if (!isRecord(slot) || typeof slot.id !== "string") return;
|
|
817
1086
|
if (seen.has(slot.id)) {
|
|
818
|
-
c.add(
|
|
1087
|
+
c.add(
|
|
1088
|
+
"manifest.slot.duplicate",
|
|
1089
|
+
`/slots/${index}/id`,
|
|
1090
|
+
`Slot id "${slot.id}" is used more than once.`
|
|
1091
|
+
);
|
|
819
1092
|
}
|
|
820
1093
|
seen.add(slot.id);
|
|
821
1094
|
});
|
|
822
1095
|
}
|
|
823
1096
|
const options = value.options;
|
|
824
1097
|
if (!Array.isArray(options)) {
|
|
825
|
-
c.add(
|
|
1098
|
+
c.add(
|
|
1099
|
+
"manifest.options.invalid",
|
|
1100
|
+
"/options",
|
|
1101
|
+
"options must be an array (it may be empty)."
|
|
1102
|
+
);
|
|
826
1103
|
} else if (options.length > MAX_OPTIONS) {
|
|
827
|
-
c.add(
|
|
1104
|
+
c.add(
|
|
1105
|
+
"manifest.options.tooMany",
|
|
1106
|
+
"/options",
|
|
1107
|
+
`Declare at most ${MAX_OPTIONS} options.`
|
|
1108
|
+
);
|
|
828
1109
|
} else {
|
|
829
|
-
|
|
1110
|
+
for (const [index, option] of options.entries()) {
|
|
1111
|
+
validateOption(c, option, `/options/${index}`, v2);
|
|
1112
|
+
}
|
|
830
1113
|
const seen = /* @__PURE__ */ new Set();
|
|
831
1114
|
options.forEach((option, index) => {
|
|
832
1115
|
if (!isRecord(option) || typeof option.id !== "string") return;
|
|
833
1116
|
if (seen.has(option.id)) {
|
|
834
|
-
c.add(
|
|
1117
|
+
c.add(
|
|
1118
|
+
"manifest.option.duplicate",
|
|
1119
|
+
`/options/${index}/id`,
|
|
1120
|
+
`Option id "${option.id}" is used more than once.`
|
|
1121
|
+
);
|
|
835
1122
|
}
|
|
836
1123
|
seen.add(option.id);
|
|
837
1124
|
});
|
|
@@ -845,7 +1132,11 @@ function validateManifest(value) {
|
|
|
845
1132
|
);
|
|
846
1133
|
}
|
|
847
1134
|
if (c.problems.length === 0 && jsonBytes(value) > MAX_MANIFEST_BYTES) {
|
|
848
|
-
c.add(
|
|
1135
|
+
c.add(
|
|
1136
|
+
"manifest.size",
|
|
1137
|
+
"",
|
|
1138
|
+
`manifest.json must be at most ${MAX_MANIFEST_BYTES} bytes as JSON.`
|
|
1139
|
+
);
|
|
849
1140
|
}
|
|
850
1141
|
return c.problems;
|
|
851
1142
|
}
|
|
@@ -877,13 +1168,14 @@ var manifestSchema = z.object({
|
|
|
877
1168
|
description: z.string().optional(),
|
|
878
1169
|
slots: z.array(slotSchema),
|
|
879
1170
|
options: z.array(optionSchema),
|
|
880
|
-
translations: z.record(z.unknown()).optional()
|
|
1171
|
+
translations: z.record(z.string(), z.unknown()).optional()
|
|
881
1172
|
});
|
|
882
1173
|
function sortKeys(value) {
|
|
883
1174
|
if (Array.isArray(value)) return value.map((item) => sortKeys(item));
|
|
884
1175
|
if (isRecord(value)) {
|
|
885
1176
|
const out = {};
|
|
886
|
-
for (const key of Object.keys(value).sort())
|
|
1177
|
+
for (const key of Object.keys(value).sort())
|
|
1178
|
+
out[key] = sortKeys(value[key]);
|
|
887
1179
|
return out;
|
|
888
1180
|
}
|
|
889
1181
|
return value;
|
|
@@ -891,14 +1183,22 @@ function sortKeys(value) {
|
|
|
891
1183
|
|
|
892
1184
|
// src/custom-chart/zip.ts
|
|
893
1185
|
import { strFromU8, strToU8, unzipSync, zipSync } from "fflate";
|
|
894
|
-
var BUNDLE_ZIP_FILES = [
|
|
1186
|
+
var BUNDLE_ZIP_FILES = [
|
|
1187
|
+
"index.js",
|
|
1188
|
+
"manifest.json",
|
|
1189
|
+
"icon.svg",
|
|
1190
|
+
"index.css"
|
|
1191
|
+
];
|
|
895
1192
|
function writeBundleZip(files) {
|
|
896
1193
|
const entries = {};
|
|
897
1194
|
for (const name of BUNDLE_ZIP_FILES) {
|
|
898
1195
|
const content = files[name];
|
|
899
1196
|
if (typeof content === "string") entries[name] = strToU8(content);
|
|
900
1197
|
}
|
|
901
|
-
return zipSync(entries, {
|
|
1198
|
+
return zipSync(entries, {
|
|
1199
|
+
level: 6,
|
|
1200
|
+
mtime: /* @__PURE__ */ new Date("2000-01-01T00:00:00Z")
|
|
1201
|
+
});
|
|
902
1202
|
}
|
|
903
1203
|
|
|
904
1204
|
// src/custom-chart/bundle.ts
|
|
@@ -919,9 +1219,17 @@ function validateBundle(input) {
|
|
|
919
1219
|
const problems = validateManifest(input.manifest);
|
|
920
1220
|
if (input.bundleJs !== void 0) {
|
|
921
1221
|
if (typeof input.bundleJs !== "string") {
|
|
922
|
-
problems.push({
|
|
1222
|
+
problems.push({
|
|
1223
|
+
code: "bundle.js.type",
|
|
1224
|
+
path: "/bundleJs",
|
|
1225
|
+
message: "bundleJs must be a string."
|
|
1226
|
+
});
|
|
923
1227
|
} else if (input.bundleJs.trim().length === 0) {
|
|
924
|
-
problems.push({
|
|
1228
|
+
problems.push({
|
|
1229
|
+
code: "bundle.js.empty",
|
|
1230
|
+
path: "/bundleJs",
|
|
1231
|
+
message: "bundleJs must contain JavaScript."
|
|
1232
|
+
});
|
|
925
1233
|
} else if (utf8Bytes(input.bundleJs) > MAX_BUNDLE_JS_BYTES) {
|
|
926
1234
|
problems.push({
|
|
927
1235
|
code: "bundle.js.size",
|
|
@@ -932,7 +1240,11 @@ function validateBundle(input) {
|
|
|
932
1240
|
}
|
|
933
1241
|
if (input.bundleCss !== void 0) {
|
|
934
1242
|
if (typeof input.bundleCss !== "string") {
|
|
935
|
-
problems.push({
|
|
1243
|
+
problems.push({
|
|
1244
|
+
code: "bundle.css.type",
|
|
1245
|
+
path: "/bundleCss",
|
|
1246
|
+
message: "bundleCss must be a string when present."
|
|
1247
|
+
});
|
|
936
1248
|
} else if (utf8Bytes(input.bundleCss) > MAX_BUNDLE_CSS_BYTES) {
|
|
937
1249
|
problems.push({
|
|
938
1250
|
code: "bundle.css.size",
|
|
@@ -941,7 +1253,8 @@ function validateBundle(input) {
|
|
|
941
1253
|
});
|
|
942
1254
|
}
|
|
943
1255
|
}
|
|
944
|
-
if (input.iconSvg !== void 0)
|
|
1256
|
+
if (input.iconSvg !== void 0)
|
|
1257
|
+
problems.push(...validateIcon(input.iconSvg, "/iconSvg"));
|
|
945
1258
|
return problems;
|
|
946
1259
|
}
|
|
947
1260
|
function readProject(dirInput) {
|
|
@@ -950,7 +1263,11 @@ function readProject(dirInput) {
|
|
|
950
1263
|
const project = { dir, manifest: void 0, problems };
|
|
951
1264
|
const manifestPath = join2(dir, "manifest.json");
|
|
952
1265
|
if (!existsSync2(manifestPath)) {
|
|
953
|
-
problems.push({
|
|
1266
|
+
problems.push({
|
|
1267
|
+
code: "project.manifest.missing",
|
|
1268
|
+
path: "/manifest.json",
|
|
1269
|
+
message: `No manifest.json in ${dir}.`
|
|
1270
|
+
});
|
|
954
1271
|
} else {
|
|
955
1272
|
project.manifestText = readFileSync2(manifestPath, "utf8");
|
|
956
1273
|
try {
|
|
@@ -965,7 +1282,11 @@ function readProject(dirInput) {
|
|
|
965
1282
|
}
|
|
966
1283
|
const entry = ["chart.ts", "chart.js"].map((name) => join2(dir, name)).find((path) => existsSync2(path));
|
|
967
1284
|
if (!entry) {
|
|
968
|
-
problems.push({
|
|
1285
|
+
problems.push({
|
|
1286
|
+
code: "project.chart.missing",
|
|
1287
|
+
path: "/chart.ts",
|
|
1288
|
+
message: `No chart.ts in ${dir}. It must export render(ctx).`
|
|
1289
|
+
});
|
|
969
1290
|
} else {
|
|
970
1291
|
project.chartEntry = entry;
|
|
971
1292
|
}
|
|
@@ -973,7 +1294,11 @@ function readProject(dirInput) {
|
|
|
973
1294
|
if (existsSync2(cssPath)) project.css = readFileSync2(cssPath, "utf8");
|
|
974
1295
|
const iconPath = join2(dir, "icon.svg");
|
|
975
1296
|
if (!existsSync2(iconPath)) {
|
|
976
|
-
problems.push({
|
|
1297
|
+
problems.push({
|
|
1298
|
+
code: "bundle.icon.required",
|
|
1299
|
+
path: "/icon.svg",
|
|
1300
|
+
message: `No icon.svg in ${dir}.`
|
|
1301
|
+
});
|
|
977
1302
|
} else {
|
|
978
1303
|
project.icon = readFileSync2(iconPath, "utf8");
|
|
979
1304
|
}
|
|
@@ -994,21 +1319,34 @@ function readProject(dirInput) {
|
|
|
994
1319
|
function validateProject(dir) {
|
|
995
1320
|
const project = readProject(dir);
|
|
996
1321
|
const problems = [...project.problems];
|
|
997
|
-
if (project.manifest !== void 0)
|
|
998
|
-
|
|
1322
|
+
if (project.manifest !== void 0)
|
|
1323
|
+
problems.push(...validateManifest(project.manifest));
|
|
1324
|
+
if (project.icon !== void 0)
|
|
1325
|
+
problems.push(...validateIcon(sanitizeIcon(project.icon)));
|
|
999
1326
|
if (project.css !== void 0 && utf8Bytes(project.css) > MAX_BUNDLE_CSS_BYTES) {
|
|
1000
|
-
problems.push({
|
|
1327
|
+
problems.push({
|
|
1328
|
+
code: "bundle.css.size",
|
|
1329
|
+
path: "/chart.css",
|
|
1330
|
+
message: `chart.css must be at most ${MAX_BUNDLE_CSS_BYTES} bytes.`
|
|
1331
|
+
});
|
|
1001
1332
|
}
|
|
1002
1333
|
return { ok: problems.length === 0, problems, project };
|
|
1003
1334
|
}
|
|
1004
|
-
var DIST_FILES = [
|
|
1335
|
+
var DIST_FILES = [
|
|
1336
|
+
"index.js",
|
|
1337
|
+
"manifest.json",
|
|
1338
|
+
"icon.svg",
|
|
1339
|
+
"index.css",
|
|
1340
|
+
"bundle.zip"
|
|
1341
|
+
];
|
|
1005
1342
|
function insideProjectOnly(dir) {
|
|
1006
1343
|
const root = dir.endsWith(sep) ? dir : `${dir}${sep}`;
|
|
1007
1344
|
return {
|
|
1008
1345
|
name: "databrain-inside-project-only",
|
|
1009
1346
|
setup(build) {
|
|
1010
1347
|
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
1011
|
-
if (args.kind === "entry-point" || args.pluginData?.databrainChecked)
|
|
1348
|
+
if (args.kind === "entry-point" || args.pluginData?.databrainChecked)
|
|
1349
|
+
return void 0;
|
|
1012
1350
|
const resolved = await build.resolve(args.path, {
|
|
1013
1351
|
kind: args.kind,
|
|
1014
1352
|
resolveDir: args.resolveDir,
|
|
@@ -1016,7 +1354,8 @@ function insideProjectOnly(dir) {
|
|
|
1016
1354
|
pluginData: { databrainChecked: true }
|
|
1017
1355
|
});
|
|
1018
1356
|
if (resolved.errors.length > 0) return { errors: resolved.errors };
|
|
1019
|
-
if (resolved.external || resolved.path.startsWith(root))
|
|
1357
|
+
if (resolved.external || resolved.path.startsWith(root))
|
|
1358
|
+
return resolved;
|
|
1020
1359
|
return {
|
|
1021
1360
|
errors: [
|
|
1022
1361
|
{
|
|
@@ -1072,10 +1411,18 @@ async function buildProject(dir, options = {}) {
|
|
|
1072
1411
|
if (Array.isArray(errors) && errors.length > 0) {
|
|
1073
1412
|
for (const error of errors) {
|
|
1074
1413
|
const where = error.location ? `${error.location.file ?? "chart.ts"}:${error.location.line ?? 0}` : "chart.ts";
|
|
1075
|
-
problems.push({
|
|
1414
|
+
problems.push({
|
|
1415
|
+
code: "bundle.build.failed",
|
|
1416
|
+
path: `/${where}`,
|
|
1417
|
+
message: error.text
|
|
1418
|
+
});
|
|
1076
1419
|
}
|
|
1077
1420
|
} else {
|
|
1078
|
-
problems.push({
|
|
1421
|
+
problems.push({
|
|
1422
|
+
code: "bundle.build.failed",
|
|
1423
|
+
path: "/chart.ts",
|
|
1424
|
+
message: err.message
|
|
1425
|
+
});
|
|
1079
1426
|
}
|
|
1080
1427
|
return { ok: false, problems, written: [] };
|
|
1081
1428
|
}
|
|
@@ -1101,7 +1448,8 @@ globalThis.__dbnCustomChart=__dbnCustomChart;
|
|
|
1101
1448
|
if (options.write !== false) {
|
|
1102
1449
|
const dist = join2(project.dir, "dist");
|
|
1103
1450
|
mkdirSync(dist, { recursive: true });
|
|
1104
|
-
const manifestJson = JSON.stringify(manifest, null, 2)
|
|
1451
|
+
const manifestJson = `${JSON.stringify(manifest, null, 2)}
|
|
1452
|
+
`;
|
|
1105
1453
|
const write = (name, content) => {
|
|
1106
1454
|
const path = join2(dist, name);
|
|
1107
1455
|
writeFileSync(path, content);
|
|
@@ -1129,7 +1477,11 @@ function readDist(dir) {
|
|
|
1129
1477
|
const need = ["index.js", "manifest.json", "icon.svg"];
|
|
1130
1478
|
for (const name of need) {
|
|
1131
1479
|
if (!existsSync2(join2(dist, name))) {
|
|
1132
|
-
problems.push({
|
|
1480
|
+
problems.push({
|
|
1481
|
+
code: "project.dist.missing",
|
|
1482
|
+
path: `/dist/${name}`,
|
|
1483
|
+
message: `dist/${name} is missing. Run databrain custom-chart build first.`
|
|
1484
|
+
});
|
|
1133
1485
|
}
|
|
1134
1486
|
}
|
|
1135
1487
|
if (problems.length > 0) return { problems };
|
|
@@ -1137,7 +1489,15 @@ function readDist(dir) {
|
|
|
1137
1489
|
try {
|
|
1138
1490
|
manifest = JSON.parse(readFileSync2(join2(dist, "manifest.json"), "utf8"));
|
|
1139
1491
|
} catch (err) {
|
|
1140
|
-
return {
|
|
1492
|
+
return {
|
|
1493
|
+
problems: [
|
|
1494
|
+
{
|
|
1495
|
+
code: "project.dist.parse",
|
|
1496
|
+
path: "/dist/manifest.json",
|
|
1497
|
+
message: err.message
|
|
1498
|
+
}
|
|
1499
|
+
]
|
|
1500
|
+
};
|
|
1141
1501
|
}
|
|
1142
1502
|
const manifestProblems = validateManifest(manifest);
|
|
1143
1503
|
if (manifestProblems.length > 0) return { problems: manifestProblems };
|
|
@@ -1158,16 +1518,31 @@ async function buildSource(input) {
|
|
|
1158
1518
|
const manifest = input.manifest;
|
|
1159
1519
|
try {
|
|
1160
1520
|
const common = {
|
|
1161
|
-
stdin: {
|
|
1521
|
+
stdin: {
|
|
1522
|
+
contents: input.chartTs,
|
|
1523
|
+
loader: "ts",
|
|
1524
|
+
sourcefile: "chart.ts"
|
|
1525
|
+
},
|
|
1162
1526
|
bundle: true,
|
|
1163
1527
|
platform: "browser",
|
|
1164
1528
|
target: "es2020",
|
|
1165
1529
|
write: false,
|
|
1166
1530
|
legalComments: "none",
|
|
1167
1531
|
logLevel: "silent",
|
|
1168
|
-
plugins: [
|
|
1169
|
-
|
|
1170
|
-
|
|
1532
|
+
plugins: [
|
|
1533
|
+
{
|
|
1534
|
+
name: "no-host-imports",
|
|
1535
|
+
setup(build) {
|
|
1536
|
+
build.onResolve({ filter: /.*/ }, (args) => ({
|
|
1537
|
+
errors: [
|
|
1538
|
+
{
|
|
1539
|
+
text: `Import "${args.path}" is unavailable in hosted chart source. Use browser APIs or pass a prebuilt bundle.`
|
|
1540
|
+
}
|
|
1541
|
+
]
|
|
1542
|
+
}));
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
]
|
|
1171
1546
|
};
|
|
1172
1547
|
let shape = await esbuild({ ...common, format: "esm", metafile: true });
|
|
1173
1548
|
if (!Object.values(shape.metafile.outputs)[0]?.exports.includes("render")) {
|
|
@@ -1175,9 +1550,24 @@ async function buildSource(input) {
|
|
|
1175
1550
|
shape = await esbuild({ ...common, format: "esm", metafile: true });
|
|
1176
1551
|
}
|
|
1177
1552
|
if (!Object.values(shape.metafile.outputs)[0]?.exports.includes("render")) {
|
|
1178
|
-
return {
|
|
1553
|
+
return {
|
|
1554
|
+
ok: false,
|
|
1555
|
+
problems: [
|
|
1556
|
+
{
|
|
1557
|
+
code: "bundle.build.noRender",
|
|
1558
|
+
path: "/chart.ts",
|
|
1559
|
+
message: "Export function render(ctx)."
|
|
1560
|
+
}
|
|
1561
|
+
],
|
|
1562
|
+
written: []
|
|
1563
|
+
};
|
|
1179
1564
|
}
|
|
1180
|
-
const built = await esbuild({
|
|
1565
|
+
const built = await esbuild({
|
|
1566
|
+
...common,
|
|
1567
|
+
format: "iife",
|
|
1568
|
+
globalName: "__dbnCustomChart",
|
|
1569
|
+
minify: true
|
|
1570
|
+
});
|
|
1181
1571
|
const bundle = {
|
|
1182
1572
|
manifest,
|
|
1183
1573
|
bundleCss: input.bundleCss,
|
|
@@ -1193,20 +1583,34 @@ globalThis.__dbnCustomChart=__dbnCustomChart;
|
|
|
1193
1583
|
ok: true,
|
|
1194
1584
|
problems: [],
|
|
1195
1585
|
written: [],
|
|
1196
|
-
outputs: {
|
|
1586
|
+
outputs: {
|
|
1587
|
+
...bundle,
|
|
1588
|
+
contentHash: contentHash(bundle),
|
|
1589
|
+
sizeBytes: bundleSizeBytes(bundle)
|
|
1590
|
+
}
|
|
1197
1591
|
};
|
|
1198
1592
|
} catch (error) {
|
|
1199
|
-
return {
|
|
1593
|
+
return {
|
|
1594
|
+
ok: false,
|
|
1595
|
+
problems: [
|
|
1596
|
+
{
|
|
1597
|
+
code: "bundle.build.failed",
|
|
1598
|
+
path: "/chart.ts",
|
|
1599
|
+
message: error.message
|
|
1600
|
+
}
|
|
1601
|
+
],
|
|
1602
|
+
written: []
|
|
1603
|
+
};
|
|
1200
1604
|
}
|
|
1201
1605
|
}
|
|
1202
1606
|
|
|
1203
1607
|
export {
|
|
1204
|
-
readTemplateFiles,
|
|
1205
|
-
readCustomChartBrowserRuntime,
|
|
1206
1608
|
MAX_MANIFEST_BYTES,
|
|
1207
1609
|
MAX_BUNDLE_JS_BYTES,
|
|
1208
1610
|
MAX_BUNDLE_CSS_BYTES,
|
|
1209
1611
|
MAX_ICON_BYTES,
|
|
1612
|
+
readTemplateFiles,
|
|
1613
|
+
readCustomChartBrowserRuntime,
|
|
1210
1614
|
isId,
|
|
1211
1615
|
validateManifest,
|
|
1212
1616
|
slotSchema,
|
|
@@ -1221,4 +1625,4 @@ export {
|
|
|
1221
1625
|
readDist,
|
|
1222
1626
|
buildSource
|
|
1223
1627
|
};
|
|
1224
|
-
//# sourceMappingURL=chunk-
|
|
1628
|
+
//# sourceMappingURL=chunk-2F7V6RZR.js.map
|