Skip to content

Commit

Permalink
feat: schema
Browse files Browse the repository at this point in the history
schema generator wip

checkpoint: schema codegen compiles in typescript

feat: scaffold schema types

wip: codec/protocol/transport

operation schema
  • Loading branch information
kuhe committed Feb 4, 2025
1 parent f0562fe commit c855967
Show file tree
Hide file tree
Showing 69 changed files with 3,088 additions and 162 deletions.
7 changes: 7 additions & 0 deletions .changeset/nice-deers-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@smithy/smithy-client": minor
"@smithy/types": minor
"@smithy/core": minor
---

implement schema framework
9 changes: 9 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@
"import": "./dist-es/submodules/protocols/index.js",
"require": "./dist-cjs/submodules/protocols/index.js",
"types": "./dist-types/submodules/protocols/index.d.ts"
},
"./schema": {
"module": "./dist-es/submodules/schema/index.js",
"node": "./dist-cjs/submodules/schema/index.js",
"import": "./dist-es/submodules/schema/index.js",
"require": "./dist-cjs/submodules/schema/index.js",
"types": "./dist-types/submodules/schema/index.d.ts"
}
},
"author": {
Expand Down Expand Up @@ -78,6 +85,8 @@
"./cbor.js",
"./protocols.d.ts",
"./protocols.js",
"./schema.d.ts",
"./schema.js",
"dist-*/**"
],
"homepage": "https://github.com/awslabs/smithy-typescript/tree/main/packages/core",
Expand Down
7 changes: 7 additions & 0 deletions packages/core/schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Do not edit:
* This is a compatibility redirect for contexts that do not understand package.json exports field.
*/
declare module "@smithy/core/schema" {
export * from "@smithy/core/dist-types/submodules/schema/index.d";
}
6 changes: 6 additions & 0 deletions packages/core/schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

/**
* Do not edit:
* This is a compatibility redirect for contexts that do not understand package.json exports field.
*/
module.exports = require("./dist-cjs/submodules/schema/index.js");
140 changes: 140 additions & 0 deletions packages/core/src/submodules/cbor/CborCodec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { deref, ListSchema, MapSchema, StructureSchema } from "@smithy/core/schema";
import { copyDocumentWithTransform, parseEpochTimestamp } from "@smithy/smithy-client";
import {
Codec,
MemberSchema,
Schema,
SchemaRef,
ShapeDeserializer,
ShapeSerializer,
TraitsSchema,
} from "@smithy/types";

import { cbor } from "./cbor";
import { dateToTag } from "./parseCborBody";

export class CborCodec implements Codec {
public createSerializer(): CborShapeSerializer {
return new CborShapeSerializer();
}
public createDeserializer(): CborShapeDeserializer {
return new CborShapeDeserializer();
}
}

export class CborShapeSerializer implements ShapeSerializer {
private value: unknown;

public write(schema: Schema, value: unknown): void {
this.value = copyDocumentWithTransform(value, schema, (_: any, schemaRef: SchemaRef) => {
if (_ instanceof Date) {
return dateToTag(_);
}
const schema = deref((schemaRef as MemberSchema)?.[0] ?? schemaRef);
if (_ instanceof Uint8Array) {
return _;
}
const sparse = (schema as TraitsSchema)?.traits?.sparse;
if (Array.isArray(_)) {
if (!sparse) {
return _.filter((item) => item != null);
}
} else if (_ && typeof _ === "object") {
if (!sparse) {
for (const [k, v] of Object.entries(_)) {
if (v == null) {
delete _[k];
}
}
return _;
}
}
return _;
});
}

public async flush(): Promise<Uint8Array> {
const buffer = cbor.serialize(this.value);
this.value = undefined;
return buffer as Uint8Array;
}
}

export class CborShapeDeserializer implements ShapeDeserializer {
public read(schema: Schema, bytes: Uint8Array): any {
const data: any = cbor.deserialize(bytes);
return this.readValue(schema, data);
}

private readValue(schema: Schema, value: any): any {
if (typeof schema === "string") {
if (schema === "time" || schema === "epoch-seconds" || schema === "date-time") {
return parseEpochTimestamp(value);
}
if (schema === "blob") {
return value;
}
}
switch (typeof value) {
case "undefined":
case "boolean":
case "number":
case "string":
case "bigint":
case "symbol":
return value;
case "function":
case "object":
if (value === null) {
return null;
}
if ("byteLength" in (value as Uint8Array)) {
return value;
}
if (value instanceof Date) {
return value;
}
const traits =
Array.isArray(schema) && schema.length >= 2
? {
...(deref((schema as MemberSchema)[0]) as TraitsSchema)?.traits,
...(schema as MemberSchema)[1],
}
: (deref(schema) as TraitsSchema)?.traits;

if (Array.isArray(value)) {
const newArray = [];
for (const item of value) {
newArray.push(this.readValue(schema instanceof ListSchema ? deref(schema.valueSchema) : void 0, item));
if (!traits?.sparse) {
if (newArray[newArray.length - 1] == null) {
newArray.pop();
}
}
}
return newArray;
}

const newObject = {} as any;
for (const key of Object.keys(value)) {
const targetSchema =
schema instanceof StructureSchema
? deref(schema.members[key]?.[0])
: schema instanceof MapSchema
? deref(schema.valueSchema)
: void 0;
newObject[key] = this.readValue(targetSchema, value[key]);
if (!traits?.sparse && newObject[key] == null) {
delete newObject[key];
}
}
return newObject;
default:
return value;
}
}

public getContainerSize(): number {
throw new Error("Method not implemented.");
}
}
Loading

0 comments on commit c855967

Please sign in to comment.