Skip to content

Commit 84c6d6c

Browse files
committed
feat(core): add rpcv2 cbor runtime protocol
1 parent ae11e3a commit 84c6d6c

19 files changed

+1723
-4
lines changed

.changeset/pretty-cows-call.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/core": minor
3+
---
4+
5+
add cbor protocol (alpha)

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"@smithy/middleware-serde": "workspace:^",
7373
"@smithy/protocol-http": "workspace:^",
7474
"@smithy/types": "workspace:^",
75+
"@smithy/util-base64": "workspace:^",
7576
"@smithy/util-body-length-browser": "workspace:^",
7677
"@smithy/util-middleware": "workspace:^",
7778
"@smithy/util-stream": "workspace:^",
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { NormalizedSchema } from "@smithy/core/schema";
2+
import { copyDocumentWithTransform, parseEpochTimestamp } from "@smithy/core/serde";
3+
import { Codec, Schema, SchemaRef, SerdeFunctions, ShapeDeserializer, ShapeSerializer } from "@smithy/types";
4+
5+
import { cbor } from "./cbor";
6+
import { dateToTag } from "./parseCborBody";
7+
8+
/**
9+
* @alpha
10+
*/
11+
export class CborCodec implements Codec<Uint8Array, Uint8Array> {
12+
private serdeContext?: SerdeFunctions;
13+
14+
public createSerializer(): CborShapeSerializer {
15+
const serializer = new CborShapeSerializer();
16+
serializer.setSerdeContext(this.serdeContext!);
17+
return serializer;
18+
}
19+
20+
public createDeserializer(): CborShapeDeserializer {
21+
const deserializer = new CborShapeDeserializer();
22+
deserializer.setSerdeContext(this.serdeContext!);
23+
return deserializer;
24+
}
25+
26+
public setSerdeContext(serdeContext: SerdeFunctions): void {
27+
this.serdeContext = serdeContext;
28+
}
29+
}
30+
31+
/**
32+
* @alpha
33+
*/
34+
export class CborShapeSerializer implements ShapeSerializer {
35+
private serdeContext?: SerdeFunctions;
36+
private value: unknown;
37+
38+
public setSerdeContext(serdeContext: SerdeFunctions) {
39+
this.serdeContext = serdeContext;
40+
}
41+
42+
public write(schema: Schema, value: unknown): void {
43+
this.value = copyDocumentWithTransform(value, schema, (_: any, schemaRef: SchemaRef) => {
44+
if (_ instanceof Date) {
45+
return dateToTag(_);
46+
}
47+
if (_ instanceof Uint8Array) {
48+
return _;
49+
}
50+
51+
const ns = NormalizedSchema.of(schemaRef);
52+
const sparse = !!ns.getMergedTraits().sparse;
53+
54+
if (Array.isArray(_)) {
55+
if (!sparse) {
56+
return _.filter((item) => item != null);
57+
}
58+
} else if (_ && typeof _ === "object") {
59+
if (!sparse || ns.isStructSchema()) {
60+
for (const [k, v] of Object.entries(_)) {
61+
if (v == null) {
62+
delete _[k];
63+
}
64+
}
65+
return _;
66+
}
67+
}
68+
69+
return _;
70+
});
71+
}
72+
73+
public flush(): Uint8Array {
74+
const buffer = cbor.serialize(this.value);
75+
this.value = undefined;
76+
return buffer as Uint8Array;
77+
}
78+
}
79+
80+
/**
81+
* @alpha
82+
*/
83+
export class CborShapeDeserializer implements ShapeDeserializer {
84+
private serdeContext?: SerdeFunctions;
85+
86+
public setSerdeContext(serdeContext: SerdeFunctions) {
87+
this.serdeContext = serdeContext;
88+
}
89+
90+
public read(schema: Schema, bytes: Uint8Array): any {
91+
const data: any = cbor.deserialize(bytes);
92+
return this.readValue(schema, data);
93+
}
94+
95+
private readValue(_schema: Schema, value: any): any {
96+
const ns = NormalizedSchema.of(_schema);
97+
const schema = ns.getSchema();
98+
99+
if (typeof schema === "number") {
100+
if (ns.isTimestampSchema()) {
101+
// format is ignored.
102+
return parseEpochTimestamp(value);
103+
}
104+
if (ns.isBlobSchema()) {
105+
return value;
106+
}
107+
}
108+
109+
if (
110+
typeof value === "undefined" ||
111+
typeof value === "boolean" ||
112+
typeof value === "number" ||
113+
typeof value === "string" ||
114+
typeof value === "bigint" ||
115+
typeof value === "symbol"
116+
) {
117+
return value;
118+
} else if (typeof value === "function" || typeof value === "object") {
119+
if (value === null) {
120+
return null;
121+
}
122+
if ("byteLength" in (value as Uint8Array)) {
123+
return value;
124+
}
125+
if (value instanceof Date) {
126+
return value;
127+
}
128+
if (ns.isDocumentSchema()) {
129+
return value;
130+
}
131+
132+
if (ns.isListSchema()) {
133+
const newArray = [];
134+
const memberSchema = ns.getValueSchema();
135+
const sparse = ns.isListSchema() && !!ns.getMergedTraits().sparse;
136+
137+
for (const item of value) {
138+
newArray.push(this.readValue(memberSchema, item));
139+
if (!sparse && newArray[newArray.length - 1] == null) {
140+
newArray.pop();
141+
}
142+
}
143+
return newArray;
144+
}
145+
146+
const newObject = {} as any;
147+
148+
if (ns.isMapSchema()) {
149+
const sparse = ns.getMergedTraits().sparse;
150+
const targetSchema = ns.getValueSchema();
151+
152+
for (const key of Object.keys(value)) {
153+
newObject[key] = this.readValue(targetSchema, value[key]);
154+
155+
if (newObject[key] == null && !sparse) {
156+
delete newObject[key];
157+
}
158+
}
159+
} else if (ns.isStructSchema()) {
160+
for (const key of Object.keys(value)) {
161+
const targetSchema = ns.getMemberSchema(key);
162+
if (targetSchema === undefined) {
163+
continue;
164+
}
165+
newObject[key] = this.readValue(targetSchema, value[key]);
166+
}
167+
}
168+
return newObject;
169+
} else {
170+
return value;
171+
}
172+
}
173+
}

0 commit comments

Comments
 (0)