tangled
alpha
login
or
join now
tylur.dev
/
prototypey
prototypey.org - atproto lexicon typescript toolkit - mirror https://github.com/tylersayshi/prototypey
1
fork
atom
overview
issues
pulls
pipelines
update docs and add bsky profile test for fromJSON
Tyler
3 months ago
d5d3143d
df46810b
+89
-116
3 changed files
expand all
collapse all
unified
split
.changeset
cold-bugs-turn.md
packages
prototypey
README.md
core
tests
from-json-infer.test.ts
+5
.changeset/cold-bugs-turn.md
···
1
1
+
---
2
2
+
"prototypey": patch
3
3
+
---
4
4
+
5
5
+
update docs - we're featured!
+1
-1
packages/prototypey/README.md
···
1
1
# prototypey
2
2
3
3
-
A (soon-to-be) fully-featured sdk for developing lexicons with typescript.
3
3
+
A fully-featured sdk for developing lexicons with typescript.
4
4
5
5
## Installation
6
6
+83
-115
packages/prototypey/core/tests/from-json-infer.test.ts
···
1
1
import { test } from "vitest";
2
2
import { attest } from "@ark/attest";
3
3
import { fromJSON } from "../lib.ts";
4
4
+
import { Infer } from "../infer.ts";
4
5
5
6
test("fromJSON InferNS produces expected type shape", () => {
6
7
const exampleLexicon = fromJSON({
···
503
504
// REF TYPE TESTS
504
505
// ============================================================================
505
506
506
506
-
test("fromJSON InferRef handles basic reference", () => {
507
507
+
test("fromJSON InferRef handles external reference (unknown)", () => {
507
508
const lexicon = fromJSON({
508
509
id: "test.ref",
509
510
defs: {
···
524
525
}`);
525
526
});
526
527
527
527
-
test("fromJSON InferRef handles required reference", () => {
528
528
-
const lexicon = fromJSON({
529
529
-
id: "test.refRequired",
530
530
-
defs: {
531
531
-
main: {
532
532
-
type: "object",
533
533
-
properties: {
534
534
-
author: { type: "ref", ref: "com.example.user", required: true },
535
535
-
},
536
536
-
required: ["author"],
537
537
-
},
538
538
-
},
539
539
-
});
540
540
-
541
541
-
attest(lexicon["~infer"]).type.toString.snap(`{
542
542
-
$type: "test.refRequired"
543
543
-
author: {
544
544
-
[x: string]: unknown
545
545
-
$type: "com.example.user"
546
546
-
}
547
547
-
}`);
548
548
-
});
549
549
-
550
550
-
test("fromJSON InferRef handles nullable reference", () => {
551
551
-
const lexicon = fromJSON({
552
552
-
id: "test.refNullable",
553
553
-
defs: {
554
554
-
main: {
555
555
-
type: "object",
556
556
-
properties: {
557
557
-
parent: { type: "ref", ref: "com.example.node", nullable: true },
558
558
-
},
559
559
-
nullable: ["parent"],
560
560
-
},
561
561
-
},
562
562
-
});
563
563
-
564
564
-
attest(lexicon["~infer"]).type.toString.snap(`{
565
565
-
$type: "test.refNullable"
566
566
-
parent?:
567
567
-
| { [x: string]: unknown; $type: "com.example.node" }
568
568
-
| null
569
569
-
| undefined
570
570
-
}`);
571
571
-
});
572
572
-
573
528
// ============================================================================
574
529
// UNION TYPE TESTS
575
530
// ============================================================================
576
531
577
577
-
test("fromJSON InferUnion handles basic union", () => {
532
532
+
test("fromJSON InferUnion handles external union (unknown)", () => {
578
533
const lexicon = fromJSON({
579
534
id: "test.union",
580
535
defs: {
···
599
554
}`);
600
555
});
601
556
602
602
-
test("fromJSON InferUnion handles required union", () => {
603
603
-
const lexicon = fromJSON({
604
604
-
id: "test.unionRequired",
605
605
-
defs: {
606
606
-
main: {
607
607
-
type: "object",
608
608
-
properties: {
609
609
-
media: {
610
610
-
type: "union",
611
611
-
refs: ["com.example.video", "com.example.audio"],
612
612
-
required: true,
613
613
-
},
614
614
-
},
615
615
-
required: ["media"],
616
616
-
},
617
617
-
},
618
618
-
});
619
619
-
620
620
-
attest(lexicon["~infer"]).type.toString.snap(`{
621
621
-
$type: "test.unionRequired"
622
622
-
media:
623
623
-
| { [x: string]: unknown; $type: "com.example.video" }
624
624
-
| { [x: string]: unknown; $type: "com.example.audio" }
625
625
-
}`);
626
626
-
});
627
627
-
628
628
-
test("fromJSON InferUnion handles union with many types", () => {
629
629
-
const lexicon = fromJSON({
630
630
-
id: "test.unionMultiple",
631
631
-
defs: {
632
632
-
main: {
633
633
-
type: "object",
634
634
-
properties: {
635
635
-
attachment: {
636
636
-
type: "union",
637
637
-
refs: [
638
638
-
"com.example.image",
639
639
-
"com.example.video",
640
640
-
"com.example.audio",
641
641
-
"com.example.document",
642
642
-
],
643
643
-
},
644
644
-
},
645
645
-
},
646
646
-
},
647
647
-
});
648
648
-
649
649
-
attest(lexicon["~infer"]).type.toString.snap(`{
650
650
-
$type: "test.unionMultiple"
651
651
-
attachment?:
652
652
-
| { [x: string]: unknown; $type: "com.example.image" }
653
653
-
| { [x: string]: unknown; $type: "com.example.video" }
654
654
-
| { [x: string]: unknown; $type: "com.example.audio" }
655
655
-
| {
656
656
-
[x: string]: unknown
657
657
-
$type: "com.example.document"
658
658
-
}
659
659
-
| undefined
660
660
-
}`);
661
661
-
});
662
662
-
663
557
// ============================================================================
664
558
// PARAMS TYPE TESTS
665
559
// ============================================================================
···
869
763
const lexicon = fromJSON({
870
764
id: "test.arrayOfRefs",
871
765
defs: {
766
766
+
user: {
767
767
+
type: "object",
768
768
+
properties: {
769
769
+
name: { type: "string", required: true },
770
770
+
handle: { type: "string", required: true },
771
771
+
},
772
772
+
required: ["name", "handle"],
773
773
+
},
872
774
main: {
873
775
type: "object",
874
776
properties: {
875
777
followers: {
876
778
type: "array",
877
877
-
items: { type: "ref", ref: "com.example.user" },
779
779
+
items: { type: "ref", ref: "#user" },
878
780
},
879
781
},
880
782
},
···
884
786
attest(lexicon["~infer"]).type.toString.snap(`{
885
787
$type: "test.arrayOfRefs"
886
788
followers?:
887
887
-
| { [x: string]: unknown; $type: "com.example.user" }[]
789
789
+
| { handle: string; name: string; $type: "#user" }[]
888
790
| undefined
889
791
}`);
890
792
});
···
897
799
const lexicon = fromJSON({
898
800
id: "test.complex",
899
801
defs: {
802
802
+
text: {
803
803
+
type: "object",
804
804
+
properties: {
805
805
+
content: { type: "string", required: true },
806
806
+
},
807
807
+
required: ["content"],
808
808
+
},
809
809
+
image: {
810
810
+
type: "object",
811
811
+
properties: {
812
812
+
url: { type: "string", required: true },
813
813
+
alt: { type: "string" },
814
814
+
},
815
815
+
required: ["url"],
816
816
+
},
900
817
main: {
901
818
type: "object",
902
819
properties: {
···
912
829
},
913
830
content: {
914
831
type: "union",
915
915
-
refs: ["com.example.text", "com.example.image"],
832
832
+
refs: ["#text", "#image"],
916
833
},
917
834
tags: { type: "array", items: { type: "string" }, maxLength: 10 },
918
835
metadata: {
···
933
850
$type: "test.complex"
934
851
tags?: string[] | undefined
935
852
content?:
936
936
-
| { [x: string]: unknown; $type: "com.example.text" }
937
937
-
| { [x: string]: unknown; $type: "com.example.image" }
853
853
+
| { content: string; $type: "#text" }
854
854
+
| {
855
855
+
alt?: string | undefined
856
856
+
url: string
857
857
+
$type: "#image"
858
858
+
}
938
859
| undefined
939
860
author?:
940
861
| {
···
982
903
type: "object",
983
904
properties: {
984
905
text: { type: "string", required: true },
985
985
-
author: { type: "ref", ref: "com.example.user" },
906
906
+
author: { type: "ref", ref: "#user" },
986
907
},
987
908
required: ["text"],
988
909
},
···
1256
1177
author: "[Reference not found: #user]"
1257
1178
}`);
1258
1179
});
1180
1180
+
1181
1181
+
// ============================================================================
1182
1182
+
// REAL-WORLD EXAMPLE: BLUESKY PROFILE
1183
1183
+
// ============================================================================
1184
1184
+
1185
1185
+
test("fromJSON Real-world example: app.bsky.actor.profile", () => {
1186
1186
+
const lexicon = fromJSON({
1187
1187
+
id: "app.bsky.actor.profile",
1188
1188
+
defs: {
1189
1189
+
main: {
1190
1190
+
type: "record",
1191
1191
+
key: "self",
1192
1192
+
record: {
1193
1193
+
type: "object",
1194
1194
+
properties: {
1195
1195
+
displayName: {
1196
1196
+
type: "string",
1197
1197
+
maxLength: 64,
1198
1198
+
maxGraphemes: 64,
1199
1199
+
},
1200
1200
+
description: {
1201
1201
+
type: "string",
1202
1202
+
maxLength: 256,
1203
1203
+
maxGraphemes: 256,
1204
1204
+
},
1205
1205
+
},
1206
1206
+
},
1207
1207
+
},
1208
1208
+
},
1209
1209
+
});
1210
1210
+
1211
1211
+
type Profile = Infer<typeof lexicon>;
1212
1212
+
1213
1213
+
const george: Profile = {
1214
1214
+
$type: "app.bsky.actor.profile",
1215
1215
+
description: "George",
1216
1216
+
};
1217
1217
+
1218
1218
+
lexicon.validate({ foo: "bar" }); // will fail
1219
1219
+
lexicon.validate(george); // will pass 🎉
1220
1220
+
1221
1221
+
attest(lexicon["~infer"]).type.toString.snap(`{
1222
1222
+
$type: "app.bsky.actor.profile"
1223
1223
+
displayName?: string | undefined
1224
1224
+
description?: string | undefined
1225
1225
+
}`);
1226
1226
+
});