Suite of AT Protocol TypeScript libraries built on web standards
1import { CID } from "multiformats/cid";
2import {
3 check,
4 ipldToJson,
5 type IpldValue,
6 jsonToIpld,
7 type JsonValue,
8} from "@atp/common";
9import { BlobRef, jsonBlobRef } from "./blob-refs.ts";
10
11export type LexValue =
12 | IpldValue
13 | BlobRef
14 | Array<LexValue>
15 | { [key: string]: LexValue };
16
17/** Record of LexValues */
18export type RepoRecord = Record<string, LexValue>;
19
20// @NOTE avoiding use of check.is() here only because it makes
21// these implementations slow, and they often live in hot paths.
22
23export const lexToIpld = (val: LexValue): IpldValue => {
24 // walk arrays
25 if (Array.isArray(val)) {
26 return val.map((item) => lexToIpld(item));
27 }
28 // objects
29 if (val && typeof val === "object") {
30 const obj = val as Record<string, LexValue>;
31 // convert blobs, leaving the original encoding so that we don't change CIDs on re-encode
32 if (val instanceof BlobRef) {
33 return val.original;
34 }
35 // retain cids & bytes
36 if (CID.asCID(val) || val instanceof Uint8Array) {
37 return val;
38 }
39 // walk plain objects
40 const toReturn: Record<string, IpldValue> = {};
41 for (const key of Object.keys(val)) {
42 toReturn[key] = lexToIpld(obj[key]);
43 }
44 return toReturn;
45 }
46 // pass through
47 return val;
48};
49
50export const ipldToLex = (val: IpldValue): LexValue => {
51 // map arrays
52 if (Array.isArray(val)) {
53 return val.map((item) => ipldToLex(item));
54 }
55 // objects
56 if (val && typeof val === "object") {
57 // convert blobs, using hints to avoid expensive is() check
58 const obj = val as Record<string, unknown>;
59 if (
60 (obj["$type"] === "blob" ||
61 (typeof obj["cid"] === "string" &&
62 typeof obj["mimeType"] === "string")) &&
63 check.is(val, jsonBlobRef)
64 ) {
65 return BlobRef.fromJsonRef(val);
66 }
67 // retain cids, bytes
68 if (CID.asCID(val) || val instanceof Uint8Array) {
69 return val;
70 }
71 // map plain objects
72 const toReturn: Record<string, LexValue> = {};
73 for (const key of Object.keys(val)) {
74 toReturn[key] = ipldToLex(obj[key]);
75 }
76 return toReturn;
77 }
78 // pass through
79 return val;
80};
81
82export const lexToJson = (val: LexValue): JsonValue => {
83 return ipldToJson(lexToIpld(val));
84};
85
86export const stringifyLex = (val: LexValue): string => {
87 return JSON.stringify(lexToJson(val));
88};
89
90export const jsonToLex = (val: JsonValue): LexValue => {
91 return ipldToLex(jsonToIpld(val));
92};
93
94export const jsonStringToLex = (val: string): LexValue => {
95 return jsonToLex(JSON.parse(val));
96};