Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6384e33

Browse files
committedMar 17, 2025·
rework schemas
1 parent 7cc7d83 commit 6384e33

File tree

22 files changed

+773
-570
lines changed

22 files changed

+773
-570
lines changed
 

‎packages/core/src/submodules/cbor/CborCodec.ts

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
1-
import { deref, ListSchema, MapSchema, StructureSchema } from "@smithy/core/schema";
1+
import { NormalizedSchema } from "@smithy/core/schema";
22
import { copyDocumentWithTransform, parseEpochTimestamp } from "@smithy/core/serde";
3-
import {
4-
Codec,
5-
MemberSchema,
6-
Schema,
7-
SchemaRef,
8-
SerdeContext,
9-
ShapeDeserializer,
10-
ShapeSerializer,
11-
TraitsSchema,
12-
} from "@smithy/types";
3+
import { Codec, Schema, SchemaRef, SerdeContext, ShapeDeserializer, ShapeSerializer } from "@smithy/types";
134

145
import { cbor } from "./cbor";
156
import { dateToTag } from "./parseCborBody";
@@ -46,17 +37,19 @@ export class CborShapeSerializer implements ShapeSerializer<Uint8Array> {
4637
if (_ instanceof Date) {
4738
return dateToTag(_);
4839
}
49-
const schema = deref((schemaRef as MemberSchema)?.[0] ?? schemaRef);
5040
if (_ instanceof Uint8Array) {
5141
return _;
5242
}
53-
const sparse = (schema as TraitsSchema)?.traits?.sparse;
43+
44+
const ns = NormalizedSchema.of(schemaRef);
45+
const sparse = !!ns.getMergedTraits().sparse;
46+
5447
if (Array.isArray(_)) {
5548
if (!sparse) {
5649
return _.filter((item) => item != null);
5750
}
5851
} else if (_ && typeof _ === "object") {
59-
if (!sparse) {
52+
if (!sparse || ns.isStructSchema()) {
6053
for (const [k, v] of Object.entries(_)) {
6154
if (v == null) {
6255
delete _[k];
@@ -65,6 +58,7 @@ export class CborShapeSerializer implements ShapeSerializer<Uint8Array> {
6558
return _;
6659
}
6760
}
61+
6862
return _;
6963
});
7064
}
@@ -88,15 +82,20 @@ export class CborShapeDeserializer implements ShapeDeserializer {
8882
return this.readValue(schema, data);
8983
}
9084

91-
private readValue(schema: Schema, value: any): any {
92-
if (typeof schema === "string") {
93-
if (schema === "time" || schema === "epoch-seconds" || schema === "date-time") {
85+
private readValue(_schema: Schema, value: any): any {
86+
const ns = NormalizedSchema.of(_schema);
87+
const schema = ns.getSchema();
88+
89+
if (typeof schema === "number") {
90+
if (ns.isTimestampSchema()) {
91+
// format is ignored.
9492
return parseEpochTimestamp(value);
9593
}
96-
if (schema === "blob" || schema === "streaming-blob") {
94+
if (ns.isBlobSchema()) {
9795
return value;
9896
}
9997
}
98+
10099
switch (typeof value) {
101100
case "undefined":
102101
case "boolean":
@@ -116,38 +115,36 @@ export class CborShapeDeserializer implements ShapeDeserializer {
116115
if (value instanceof Date) {
117116
return value;
118117
}
119-
const traits =
120-
Array.isArray(schema) && schema.length >= 2
121-
? {
122-
...(deref((schema as MemberSchema)[0]) as TraitsSchema)?.traits,
123-
...(schema as MemberSchema)[1],
124-
}
125-
: (deref(schema) as TraitsSchema)?.traits;
126118

127119
if (Array.isArray(value)) {
128120
const newArray = [];
121+
const memberSchema = ns.getValueSchema();
122+
const sparse = ns.isListSchema() && !!ns.getMergedTraits().sparse;
123+
129124
for (const item of value) {
130-
newArray.push(this.readValue(schema instanceof ListSchema ? deref(schema.valueSchema) : void 0, item));
131-
if (!traits?.sparse) {
132-
if (newArray[newArray.length - 1] == null) {
133-
newArray.pop();
134-
}
125+
newArray.push(this.readValue(memberSchema, item));
126+
if (!sparse && newArray[newArray.length - 1] == null) {
127+
newArray.pop();
135128
}
136129
}
137130
return newArray;
138131
}
139132

140133
const newObject = {} as any;
141134
for (const key of Object.keys(value)) {
142-
const targetSchema =
143-
schema instanceof StructureSchema
144-
? deref(schema.members[key]?.[0])
145-
: schema instanceof MapSchema
146-
? deref(schema.valueSchema)
147-
: void 0;
148-
newObject[key] = this.readValue(targetSchema, value[key]);
149-
if (!traits?.sparse && newObject[key] == null) {
150-
delete newObject[key];
135+
if (ns.isMapSchema() || ns.isDocumentSchema()) {
136+
const targetSchema = ns.getValueSchema();
137+
newObject[key] = this.readValue(targetSchema, value[key]);
138+
139+
if (ns.isMapSchema() && newObject[key] == null && !ns.getMergedTraits().sparse) {
140+
delete newObject[key];
141+
}
142+
} else if (ns.isStructSchema()) {
143+
const targetSchema = ns.getMemberSchema(key);
144+
if (targetSchema === undefined) {
145+
continue;
146+
}
147+
newObject[key] = this.readValue(targetSchema, value[key]);
151148
}
152149
}
153150
return newObject;

‎packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ describe(SmithyRpcV2CborProtocol.name, () => {
154154
const protocol = new SmithyRpcV2CborProtocol();
155155
const httpRequest = await protocol.serializeRequest(
156156
{
157+
name: "dummy",
157158
input: testCase.schema,
158159
output: void 0,
159160
traits: {},
160-
errors: [],
161161
},
162162
testCase.input,
163163
{
@@ -249,10 +249,10 @@ describe(SmithyRpcV2CborProtocol.name, () => {
249249
const protocol = new SmithyRpcV2CborProtocol();
250250
const output = await protocol.deserializeResponse(
251251
{
252+
name: "dummy",
252253
input: void 0,
253254
output: testCase.schema,
254255
traits: {},
255-
errors: [],
256256
},
257257
{},
258258
new HttpResponse({

‎packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HttpInterceptingShapeSerializer,HttpProtocol } from "@smithy/core/protocols";
1+
import { HttpInterceptingShapeSerializer, HttpProtocol } from "@smithy/core/protocols";
22
import { deref, ErrorSchema, OperationSchema, TypeRegistry } from "@smithy/core/schema";
33
import type {
44
HandlerExecutionContext,
@@ -36,6 +36,10 @@ export class SmithyRpcV2CborProtocol extends HttpProtocol {
3636
delete request.body;
3737
delete request.headers["content-type"];
3838
} else {
39+
if (!request.body) {
40+
this.serializer.write(15, {})
41+
request.body = this.serializer.flush();
42+
}
3943
try {
4044
request.headers["content-length"] = String((request.body as Uint8Array).byteLength);
4145
} catch (e) {}
@@ -82,6 +86,7 @@ export class SmithyRpcV2CborProtocol extends HttpProtocol {
8286
// TODO(schema) throw client base exception using the dataObject.
8387
throw new Error("schema not found for " + error);
8488
}
89+
8590
const message = dataObject.message ?? dataObject.Message ?? "Unknown";
8691
const exception = new errorSchema.ctor(message);
8792
Object.assign(exception, {

‎packages/core/src/submodules/protocols/HttpProtocol.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
8787

8888
for (const memberName of Object.keys(_input)) {
8989
const memberNs = ns.getMemberSchema(memberName);
90+
if (memberNs === undefined) {
91+
continue;
92+
}
9093
const memberSchema = memberNs.getSchema();
9194
const memberTraits = memberNs.getMergedTraits();
9295
const inputMember = (_input as any)[memberName] as any;
@@ -122,13 +125,13 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
122125
} else if (memberTraits.httpPrefixHeaders) {
123126
for (const [key, val] of Object.entries(inputMember)) {
124127
const amalgam = memberTraits.httpPrefixHeaders + key;
125-
serializer.write([, { httpHeader: amalgam }], val);
128+
serializer.write([0, { httpHeader: amalgam }], val);
126129
headers[amalgam.toLowerCase()] = String(serializer.flush()).toLowerCase();
127130
}
128131
delete _input[memberName];
129132
} else if (memberTraits.httpQueryParams) {
130133
for (const [key, val] of Object.entries(inputMember)) {
131-
serializer.write([, { httpQuery: key }], val);
134+
serializer.write([0, { httpQuery: key }], val);
132135
query[key] = serializer.flush() as string;
133136
}
134137
delete _input[memberName];
@@ -178,6 +181,16 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
178181
const schema = ns.getSchema() as StructureSchema;
179182

180183
let dataObject: any = {};
184+
185+
if (response.statusCode >= 300) {
186+
const bytes: Uint8Array = await collectBody(response.body, context as SerdeContext);
187+
if (bytes.byteLength > 0) {
188+
Object.assign(dataObject, await deserializer.read(15, bytes));
189+
}
190+
await this.handleError(operationSchema, context, response, dataObject, this.deserializeMetadata(response));
191+
throw new Error("@smithy/core/protocols - HTTP Protocol error handler failed to throw.");
192+
}
193+
181194
let hasNonHttpBindingMember = false;
182195

183196
for (const header in response.headers) {
@@ -188,6 +201,9 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
188201

189202
for (const [memberName] of Object.entries(schema?.members ?? {})) {
190203
const memberSchema = ns.getMemberSchema(memberName);
204+
if (memberSchema === undefined) {
205+
continue;
206+
}
191207
const memberSchemas = memberSchema.getMemberSchemas();
192208
const memberTraits = memberSchema.getMemberTraits();
193209

@@ -261,10 +277,6 @@ export abstract class HttpProtocol implements Protocol<IHttpRequest, IHttpRespon
261277
}
262278
}
263279

264-
if (response.statusCode >= 300) {
265-
await this.handleError(operationSchema, context, response, dataObject, this.deserializeMetadata(response));
266-
}
267-
268280
const output: Output = {
269281
$metadata: this.deserializeMetadata(response),
270282
...dataObject,

‎packages/core/src/submodules/schema/TypeRegistry.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,13 @@ export class TypeRegistry {
2929
* @param schema - to be registered.
3030
*/
3131
public register(shapeId: string, schema: ISchema) {
32-
const qualifiedName = this.namespace + "#" + shapeId;
32+
const qualifiedName = this.normalizeShapeId(shapeId);
3333
this.schemas.set(qualifiedName, schema);
3434
if (typeof schema === "object") {
3535
TypeRegistry.schemaToRegistry.set(schema, this);
3636
}
3737
}
3838

39-
/**
40-
* Used to disambiguate e.g. XML values, which are all strings,
41-
* into other simple JavaScript types like boolean and number.
42-
*
43-
* @param simpleTypes - map of shape id strings to simple type.
44-
*/
45-
public registerSimpleTypes(simpleTypes: Record<string, "boolean" | "number" | "bigint" | "bigdecimal">): void {
46-
for (const [name, type] of Object.entries(simpleTypes)) {
47-
const normalizedName = name.includes("#") ? name : this.namespace + "#" + name;
48-
this.simpleTypes[normalizedName] = type;
49-
}
50-
}
51-
5239
/**
5340
* Used to disambiguate e.g. XML values, which are all strings,
5441
* into other simple JavaScript types like boolean and number.
@@ -57,21 +44,19 @@ export class TypeRegistry {
5744
* @returns simple type of the shape id in this registry.
5845
*/
5946
public getSimpleType(shapeId: string): string {
60-
if (shapeId.includes("#")) {
61-
return this.simpleTypes[shapeId];
62-
}
63-
return this.simpleTypes[this.namespace + "#" + shapeId] ?? "unknown";
47+
return this.simpleTypes[this.normalizeShapeId(shapeId)] ?? "unknown";
6448
}
6549

6650
/**
6751
* @param shapeId - query.
6852
* @returns the schema.
6953
*/
7054
public getSchema(shapeId: string): ISchema {
71-
if (shapeId.includes("#")) {
72-
return this.schemas.get(shapeId);
55+
const id = this.normalizeShapeId(shapeId);
56+
if (!this.schemas.has(id)) {
57+
throw new Error(`@smithy/core/schema - schema not found for ${id}`);
7358
}
74-
return this.schemas.get(this.namespace + "#" + shapeId);
59+
return this.schemas.get(id)!;
7560
}
7661

7762
/**
@@ -93,4 +78,11 @@ export class TypeRegistry {
9378
TypeRegistry.registries.delete(this.namespace);
9479
this.schemas.clear();
9580
}
81+
82+
private normalizeShapeId(shapeId: string) {
83+
if (shapeId.includes("#")) {
84+
return shapeId;
85+
}
86+
return this.namespace + "#" + shapeId;
87+
}
9688
}

‎packages/core/src/submodules/schema/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ export * from "./schemas/OperationSchema";
66
export * from "./schemas/ErrorSchema";
77
export * from "./schemas/NormalizedSchema";
88
export * from "./schemas/Schema";
9+
export * from "./schemas/SimpleSchema";
910
export * from "./schemas/StructureSchema";
1011
export * from "./TypeRegistry";

‎packages/core/src/submodules/schema/schemas/ErrorSchema.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,25 @@ export class ErrorSchema extends StructureSchema {
77
public constructor(
88
public name: string,
99
public traits: SchemaTraits,
10-
public members: Record<string, [SchemaRef, SchemaTraits]>,
10+
public memberNames: string[],
11+
public memberList: SchemaRef[],
1112
/**
1213
* Constructor for a modeled service exception class that extends Error.
1314
*/
1415
public ctor: any
1516
) {
16-
super(name, traits, members);
17+
super(name, traits, memberNames, memberList);
1718
}
1819
}
1920

2021
export function error(
2122
name: string,
2223
traits: SchemaTraits = {},
23-
members: Record<string, [SchemaRef, SchemaTraits]> = {},
24+
memberNames: string[],
25+
memberList: SchemaRef[],
2426
ctor: any
2527
): ErrorSchema {
26-
const schema = new ErrorSchema(name, traits, members, ctor);
28+
const schema = new ErrorSchema(name, traits, memberNames, memberList, ctor);
2729
if (TypeRegistry.active) {
2830
TypeRegistry.active.register(name, schema);
2931
}

‎packages/core/src/submodules/schema/schemas/ListSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class ListSchema extends Schema implements IListSchema {
1313
}
1414
}
1515

16-
export function list(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef = void 0): ListSchema {
16+
export function list(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef): ListSchema {
1717
const schema = new ListSchema(name, traits, typeof valueSchema === "function" ? valueSchema() : valueSchema);
1818
if (TypeRegistry.active) {
1919
TypeRegistry.active.register(name, schema);

‎packages/core/src/submodules/schema/schemas/MapSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class MapSchema extends Schema implements IMapSchema {
1313
}
1414
}
1515

16-
export function map(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef = void 0): MapSchema {
16+
export function map(name: string, traits: SchemaTraits = {}, valueSchema: SchemaRef): MapSchema {
1717
const schema = new MapSchema(name, traits, typeof valueSchema === "function" ? valueSchema() : valueSchema);
1818
if (TypeRegistry.active) {
1919
TypeRegistry.active.register(name, schema);

‎packages/core/src/submodules/schema/schemas/NormalizedSchema.ts

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -63,27 +63,60 @@ export class NormalizedSchema implements INormalizedSchema {
6363
}
6464

6565
public isListSchema(): boolean {
66-
return this.schema === 2 || this.schema instanceof ListSchema;
66+
if (typeof this.schema === "number") {
67+
return this.schema >> 6 === 0b01;
68+
}
69+
return this.schema instanceof ListSchema;
6770
}
6871

6972
public isMapSchema(): boolean {
70-
return this.schema === 4 || this.schema instanceof MapSchema;
73+
if (typeof this.schema === "number") {
74+
return this.schema >> 6 === 0b10;
75+
}
76+
return this.schema instanceof MapSchema;
77+
}
78+
79+
public isDocumentSchema(): boolean {
80+
return this.schema === 15;
7181
}
7282

7383
public isStructSchema(): boolean {
7484
return (
75-
this.schema === 8 ||
7685
(this.schema !== null && typeof this.schema === "object" && "members" in this.schema) ||
7786
this.schema instanceof StructureSchema
7887
);
7988
}
8089

8190
public isBlobSchema(): boolean {
82-
return this.schema === "blob" || this.schema === "streaming-blob";
91+
return this.schema === 21 || this.schema === 42;
92+
}
93+
94+
public isTimestampSchema(): boolean {
95+
return typeof this.schema === "number" && this.schema >= 4 && this.schema <= 7;
96+
}
97+
98+
public isStringSchema(): boolean {
99+
return this.schema === 0;
100+
}
101+
102+
public isBooleanSchema(): boolean {
103+
return this.schema === 2;
104+
}
105+
106+
public isNumericSchema(): boolean {
107+
return this.schema === 1;
108+
}
109+
110+
public isBigIntegerSchema(): boolean {
111+
return this.schema === 17;
112+
}
113+
114+
public isBigDecimalSchema(): boolean {
115+
return this.schema === 19;
83116
}
84117

85118
public isStreaming(): boolean {
86-
return !!this.getMergedTraits().streaming || this.getSchema() === "streaming-blob";
119+
return !!this.getMergedTraits().streaming || this.getSchema() === 42;
87120
}
88121

89122
public getMergedTraits(): SchemaTraits {
@@ -101,34 +134,55 @@ export class NormalizedSchema implements INormalizedSchema {
101134
return this.traits;
102135
}
103136

104-
public getMemberSchema(member?: string): NormalizedSchema {
105-
if (typeof this.schema !== "object") {
106-
return NormalizedSchema.of(void 0, member as string);
137+
public getValueSchema(): NormalizedSchema {
138+
if (typeof this.schema === "number" && this.schema >= 0b0010_0000) {
139+
return NormalizedSchema.of(0b0011_1111 & this.schema);
107140
}
108-
if ("members" in (this.schema as StructureSchema)) {
109-
if (typeof member === "undefined") {
110-
throw new Error("cannot call getMemberSchema(void) with StructureSchema");
141+
if (this.schema && typeof this.schema === "object") {
142+
if (this.isStructSchema()) {
143+
throw new Error("cannot call getValueSchema() with StructureSchema");
144+
}
145+
const collection = this.schema as MapSchema | ListSchema;
146+
if ("valueSchema" in collection) {
147+
return NormalizedSchema.of(collection.valueSchema);
111148
}
112-
return NormalizedSchema.of((this.schema as StructureSchema).members[member], member as string);
113149
}
114-
if ("valueSchema" in (this.schema as MapSchema | ListSchema)) {
115-
if (typeof member !== "undefined") {
116-
throw new Error("cannot call getMemberSchema(string) with List or Map Schema");
150+
if (this.isDocumentSchema()) {
151+
return NormalizedSchema.of(15);
152+
}
153+
throw new Error("@smithy/core/schema - the schema does not have a value member.");
154+
}
155+
156+
public getMemberSchema(member: string): NormalizedSchema | undefined {
157+
if (this.schema && typeof this.schema === "object") {
158+
const struct = this.schema as StructureSchema;
159+
if ("members" in struct) {
160+
if (typeof member === "undefined") {
161+
throw new Error("cannot call getMemberSchema(void) with StructureSchema");
162+
}
163+
if (!(member in struct.members)) {
164+
// indicates the member is not recognized.
165+
return undefined;
166+
}
167+
return NormalizedSchema.of(struct.members[member], member as string);
117168
}
118-
return NormalizedSchema.of((this.schema as MapSchema | ListSchema).valueSchema);
119169
}
120-
return NormalizedSchema.of(void 0, member as string);
170+
if (this.isDocumentSchema()) {
171+
return NormalizedSchema.of(15);
172+
}
173+
throw new Error("@smithy/core/schema - the schema does not have members.");
121174
}
122175

123176
public getMemberSchemas(): Record<string, NormalizedSchema> {
124177
const { schema } = this;
125-
if (!schema || typeof schema !== "object") {
178+
const struct = schema as StructureSchema;
179+
if (!struct || typeof struct !== "object") {
126180
return {};
127181
}
128-
if ("members" in (schema as StructureSchema)) {
182+
if ("members" in struct) {
129183
const buffer = {} as Record<string, NormalizedSchema>;
130-
for (const member of Object.keys((schema as StructureSchema).members)) {
131-
buffer[member] = this.getMemberSchema(member);
184+
for (const member of struct.memberNames) {
185+
buffer[member] = this.getMemberSchema(member)!;
132186
}
133187
return buffer;
134188
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { SchemaRef, SchemaTraits, TraitsSchema } from "@smithy/types";
2+
3+
import { TypeRegistry } from "../TypeRegistry";
4+
import { Schema } from "./Schema";
5+
6+
export class SimpleSchema extends Schema implements TraitsSchema {
7+
public constructor(
8+
public name: string,
9+
public schemaRef: SchemaRef,
10+
public traits: SchemaTraits
11+
) {
12+
super(name, traits);
13+
}
14+
}
15+
16+
export function sim(name: string, schemaRef: SchemaRef, traits: SchemaTraits) {
17+
const schema = new SimpleSchema(name, schemaRef, traits);
18+
if (TypeRegistry.active) {
19+
TypeRegistry.active.register(name, schema);
20+
}
21+
return schema;
22+
}

‎packages/core/src/submodules/schema/schemas/StructureSchema.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
1-
import type { SchemaRef, SchemaTraits, StructureSchema as IStructureSchema } from "@smithy/types";
1+
import type { MemberSchema, SchemaRef, SchemaTraits, StructureSchema as IStructureSchema } from "@smithy/types";
22

33
import { TypeRegistry } from "../TypeRegistry";
44
import { Schema } from "./Schema";
55

66
export class StructureSchema extends Schema implements IStructureSchema {
7+
public members: Record<string, [SchemaRef, SchemaTraits]> = {};
8+
79
public constructor(
810
public name: string,
911
public traits: SchemaTraits,
10-
public members: Record<string, [SchemaRef, SchemaTraits]>
12+
public memberNames: string[],
13+
public memberList: SchemaRef[]
1114
) {
1215
super(name, traits);
16+
for (let i = 0; i < memberNames.length; ++i) {
17+
this.members[memberNames[i]] = Array.isArray(memberList[i])
18+
? (memberList[i] as MemberSchema)
19+
: [memberList[i], {}];
20+
}
1321
}
1422
}
1523

1624
export function struct(
1725
name: string,
1826
traits: SchemaTraits = {},
19-
members: Record<string, [SchemaRef, SchemaTraits]> = {}
27+
memberNames: string[],
28+
memberList: SchemaRef[]
2029
): StructureSchema {
21-
const schema = new StructureSchema(name, traits, members);
30+
const schema = new StructureSchema(name, traits, memberNames, memberList);
2231
if (TypeRegistry.active) {
2332
TypeRegistry.active.register(name, schema);
2433
}

‎packages/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export * from "./profile";
2222
export * from "./response";
2323
export * from "./retry";
2424
export * from "./schema/schema";
25+
export * from "./schema/sentinels";
2526
export * from "./serde";
2627
export * from "./shapes";
2728
export * from "./signature";

‎packages/types/src/schema/schema.ts

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,39 @@ import { EndpointV2 } from "../endpoint";
22
import { HandlerExecutionContext } from "../middleware";
33
import { MetadataBearer } from "../response";
44
import { SerdeContext } from "../serde";
5-
6-
/**
7-
* The default schema is a sentinel value
8-
* indicating that the schema for a shape
9-
* behaves no differently than a Document.
10-
*
11-
* @public
12-
*/
13-
export type DefaultSchema = undefined;
5+
import type {
6+
BigDecimalSchema,
7+
BigIntegerSchema,
8+
BlobSchema,
9+
BooleanSchema,
10+
DocumentSchema,
11+
NumericSchema,
12+
StreamingBlobSchema,
13+
StringSchema,
14+
TimestampDateTimeSchema,
15+
TimestampDefaultSchema,
16+
TimestampEpochSecondsSchema,
17+
TimestampHttpDateSchema,
18+
} from "./sentinels";
1419

1520
/**
1621
* Sentinel value for Timestamp schema.
1722
* "time" means unspecified and to use the protocol serializer's default format.
1823
*
1924
* @public
2025
*/
21-
export type TimestampSchema = "time" | "date-time" | "http-date" | "epoch-seconds" | string;
26+
export type TimestampSchemas =
27+
| TimestampDefaultSchema
28+
| TimestampDateTimeSchema
29+
| TimestampHttpDateSchema
30+
| TimestampEpochSecondsSchema
31+
| string;
2232

2333
/**
24-
* Sentinel value for Blob schema.
25-
*
34+
* Sentinel values for Blob schema.
2635
* @public
2736
*/
28-
export type BlobSchema = "blob" | "streaming-blob";
37+
export type BlobSchemas = BlobSchema | StreamingBlobSchema;
2938

3039
/**
3140
* Signal value for operation Unit input or output.
@@ -74,12 +83,6 @@ export interface StructureSchema extends TraitsSchema {
7483
members: Record<string, [SchemaRef, SchemaTraits]>;
7584
}
7685

77-
/**
78-
* Sentinel value for structure schema with no traits.
79-
* @public
80-
*/
81-
export type DefaultStructureSchema = 8;
82-
8386
/**
8487
* @public
8588
*/
@@ -89,12 +92,6 @@ export interface ListSchema extends TraitsSchema {
8992
valueSchema: SchemaRef;
9093
}
9194

92-
/**
93-
* Sentinel value for list schema with no traits.
94-
* @public
95-
*/
96-
export type DefaultListSchema = 2;
97-
9895
/**
9996
* @public
10097
*/
@@ -104,12 +101,6 @@ export interface MapSchema extends TraitsSchema {
104101
valueSchema: SchemaRef;
105102
}
106103

107-
/**
108-
* Sentinel value for map schema with no traits.
109-
* @public
110-
*/
111-
export type DefaultMapSchema = 4;
112-
113104
/**
114105
* @public
115106
*/
@@ -140,26 +131,52 @@ export interface NormalizedSchema extends TraitsSchema {
140131
isListSchema(): boolean;
141132
isMapSchema(): boolean;
142133
isStructSchema(): boolean;
134+
isBlobSchema(): boolean;
135+
isTimestampSchema(): boolean;
136+
isStringSchema(): boolean;
137+
isBooleanSchema(): boolean;
138+
isNumericSchema(): boolean;
139+
isBigIntegerSchema(): boolean;
140+
isBigDecimalSchema(): boolean;
141+
isStreaming(): boolean;
143142
getMergedTraits(): SchemaTraits;
144143
getMemberTraits(): SchemaTraits;
145144
getOwnTraits(): SchemaTraits;
146-
getMemberSchema(member?: string): NormalizedSchema;
145+
/**
146+
* For list/set/map.
147+
*/
148+
getValueSchema(): NormalizedSchema;
149+
/**
150+
* For struct/union.
151+
*/
152+
getMemberSchema(member: string): NormalizedSchema | undefined;
153+
getMemberSchemas(): Record<string, NormalizedSchema>;
147154
}
148155

156+
/**
157+
* @public
158+
*/
159+
export type SimpleSchema =
160+
| BlobSchemas
161+
| StringSchema
162+
| BooleanSchema
163+
| NumericSchema
164+
| BigIntegerSchema
165+
| BigDecimalSchema
166+
| DocumentSchema
167+
| TimestampSchemas
168+
| number;
169+
149170
/**
150171
* @public
151172
*/
152173
export type Schema =
153174
| UnitSchema
154-
| DefaultSchema
155-
| TimestampSchema
156-
| BlobSchema
175+
| TraitsSchema
176+
| SimpleSchema
157177
| ListSchema
158-
| DefaultListSchema
159178
| MapSchema
160-
| DefaultMapSchema
161179
| StructureSchema
162-
| DefaultStructureSchema
163180
| MemberSchema
164181
| OperationSchema
165182
| NormalizedSchema;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// =============== Simple types ===================
2+
3+
/**
4+
* The blob Smithy type, in JS as Uint8Array and other representations
5+
* such as Buffer, string, or Readable(Stream) depending on circumstances.
6+
* @public
7+
*/
8+
export type BlobSchema = 0b0001_0101; // 21
9+
10+
/**
11+
* @public
12+
*/
13+
export type StreamingBlobSchema = 0b0010_1010; // 42
14+
15+
/**
16+
* @public
17+
*/
18+
export type BooleanSchema = 0b0000_0010; // 2
19+
20+
/**
21+
* Includes string and enum Smithy types.
22+
* @public
23+
*/
24+
export type StringSchema = 0b0000_0000; // 0
25+
26+
/**
27+
* Includes all numeric Smithy types except bigInteger and bigDecimal.
28+
* byte, short, integer, long, float, double, intEnum.
29+
*
30+
* @public
31+
*/
32+
export type NumericSchema = 0b0000_0001; // 1
33+
34+
/**
35+
* @public
36+
*/
37+
export type BigIntegerSchema = 0b0001_0001; // 17
38+
39+
/**
40+
* @public
41+
*/
42+
export type BigDecimalSchema = 0b0001_0011; // 19
43+
44+
/**
45+
* @public
46+
*/
47+
export type DocumentSchema = 0b0000_1111; // 15
48+
49+
/**
50+
* Smithy type timestamp, in JS as native Date object.
51+
* @public
52+
*/
53+
export type TimestampDefaultSchema = 0b0000_0100; // 4
54+
/**
55+
* @public
56+
*/
57+
export type TimestampDateTimeSchema = 0b0000_0101; // 5
58+
/**
59+
* @public
60+
*/
61+
export type TimestampHttpDateSchema = 0b0000_0110; // 6
62+
/**
63+
* @public
64+
*/
65+
export type TimestampEpochSecondsSchema = 0b0000_0111; // 7
66+
67+
// =============== Aggregate types ===================
68+
69+
/**
70+
* Additional bit indicating the type is a list.
71+
* @public
72+
*/
73+
export type ListSchemaModifier = 0b0100_0000; // 64
74+
75+
/**
76+
* Additional bit indicating the type is a map.
77+
* @public
78+
*/
79+
export type MapSchemaModifier = 0b1000_0000; // 128
Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
const _B = "Blob";
1+
const _B = "Byte";
2+
const _Bl = "Blob";
23
const _Bo = "Boolean";
3-
const _By = "Byte";
44
const _D = "Double";
55
const _F = "Float";
66
const _I = "Integer";
77
const _L = "Long";
8-
const _S = "Short";
8+
const _S = "String";
9+
const _Sh = "Short";
910
const _T = "Timestamp";
10-
const _b = "boolean";
11-
const _n = "number";
1211

1312
// smithy-typescript generated code
1413
import { TypeRegistry } from "@smithy/core/schema";
@@ -19,19 +18,4 @@ export const smithy_apiRegistry = TypeRegistry.for("smithy.api");
1918
smithy_apiRegistry.startCapture();
2019
export var Unit = "unit";
2120

22-
smithy_apiRegistry.registerSimpleTypes({
23-
[_Bo]: _b,
24-
25-
[_By]: _n,
26-
27-
[_D]: _n,
28-
29-
[_F]: _n,
30-
31-
[_I]: _n,
32-
33-
[_L]: _n,
34-
35-
[_S]: _n,
36-
});
3721
smithy_apiRegistry.stopCapture();
Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
const _VE = "ValidationException";
2+
const _VEF = "ValidationExceptionField";
3+
const _VEFL = "ValidationExceptionFieldList";
24
const _c = "client";
35
const _e = "error";
6+
const _fL = "fieldList";
7+
const _m = "message";
8+
const _p = "path";
49

510
// smithy-typescript generated code
611
import { ValidationException as __ValidationException } from "../models/index";
7-
import { TypeRegistry, error as __error } from "@smithy/core/schema";
12+
import { TypeRegistry, error as __error, list as __list, struct as __struct } from "@smithy/core/schema";
813

914
/* eslint no-var: 0 */
1015

@@ -15,13 +20,11 @@ export var ValidationException = __error(
1520
{
1621
[_e]: _c,
1722
},
18-
{},
23+
[_m, _fL],
24+
[0, () => ValidationExceptionFieldList],
1925

2026
__ValidationException
2127
);
22-
export var ValidationExceptionField = 8 as const;
23-
24-
export var ValidationExceptionFieldList = 2 as const;
25-
26-
smithy_frameworkRegistry.registerSimpleTypes({});
28+
export var ValidationExceptionField = __struct(_VEF, {}, [_p, _m], [0, 0]);
29+
export var ValidationExceptionFieldList = __list(_VEFL, {}, () => ValidationExceptionField);
2730
smithy_frameworkRegistry.stopCapture();

‎private/smithy-rpcv2-cbor/src/schemas/smithy.protocoltests.rpcv2Cbor.ts

Lines changed: 210 additions & 95 deletions
Large diffs are not rendered by default.
Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,58 @@
11
const _BL = "BlobList";
2+
const _BLo = "BooleanList";
23
const _DT = "DateTime";
4+
const _FE = "FooEnum";
5+
const _FEL = "FooEnumList";
6+
const _GS = "GreetingStruct";
37
const _IE = "IntegerEnum";
8+
const _IEL = "IntegerEnumList";
9+
const _IL = "IntegerList";
10+
const _NSL = "NestedStringList";
11+
const _SL = "StringList";
12+
const _SS = "StringSet";
413
const _SSL = "SparseStringList";
514
const _SSM = "SparseStringMap";
615
const _TL = "TimestampList";
7-
const _n = "number";
16+
const _h = "hi";
817
const _s = "sparse";
918

1019
// smithy-typescript generated code
11-
import { TypeRegistry, list as __list, map as __map } from "@smithy/core/schema";
20+
import { TypeRegistry, list as __list, map as __map, struct as __struct } from "@smithy/core/schema";
1221

1322
/* eslint no-var: 0 */
1423

1524
export const smithy_protocoltests_sharedRegistry = TypeRegistry.for("smithy.protocoltests.shared");
1625
smithy_protocoltests_sharedRegistry.startCapture();
17-
export var GreetingStruct = 8 as const;
26+
export var GreetingStruct = __struct(_GS, {}, [_h], [0]);
27+
export var BlobList = 64 | 21;
1828

19-
export var BlobList = __list(_BL, {}, [() => "blob", {}]);
20-
export var BooleanList = 2 as const;
29+
export var BooleanList = 64 | 2;
2130

22-
export var FooEnumList = 2 as const;
31+
export var FooEnumList = 64 | 0;
2332

24-
export var IntegerEnumList = 2 as const;
33+
export var IntegerEnumList = 64 | 1;
2534

26-
export var IntegerList = 2 as const;
27-
28-
export var NestedStringList = 2 as const;
35+
export var IntegerList = 64 | 1;
2936

37+
export var NestedStringList = __list(_NSL, {}, 64 | 0);
3038
export var SparseStringList = __list(
3139
_SSL,
3240
{
3341
[_s]: 1,
34-
} /* String */
42+
},
43+
0
3544
);
36-
export var StringList = 2 as const;
45+
export var StringList = 64 | 0;
46+
47+
export var StringSet = 64 | 0;
3748

38-
export var StringSet = 2 as const;
49+
export var TimestampList = 64 | 4;
3950

40-
export var TimestampList = __list(_TL, {}, [() => "time", {}]);
4151
export var SparseStringMap = __map(
4252
_SSM,
4353
{
4454
[_s]: 1,
45-
} /* String */
55+
},
56+
0
4657
);
47-
smithy_protocoltests_sharedRegistry.registerSimpleTypes({
48-
[_IE]: _n,
49-
});
5058
smithy_protocoltests_sharedRegistry.stopCapture();

‎smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/SchemaElisionIndex.java

Lines changed: 0 additions & 159 deletions
This file was deleted.

‎smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/SchemaGenerator.java

Lines changed: 126 additions & 133 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.typescript.codegen.schema;
7+
8+
import software.amazon.smithy.model.Model;
9+
import software.amazon.smithy.model.knowledge.KnowledgeIndex;
10+
import software.amazon.smithy.model.shapes.CollectionShape;
11+
import software.amazon.smithy.model.shapes.MapShape;
12+
import software.amazon.smithy.model.shapes.MemberShape;
13+
import software.amazon.smithy.model.shapes.Shape;
14+
import software.amazon.smithy.model.shapes.ShapeType;
15+
import software.amazon.smithy.utils.SmithyInternalApi;
16+
17+
/**
18+
* Can determine whether a Schema can be defined by a sentinel value.
19+
*/
20+
@SmithyInternalApi
21+
final class SchemaReferenceIndex implements KnowledgeIndex {
22+
public final SchemaTraitFilterIndex traits;
23+
private final Model model;
24+
25+
SchemaReferenceIndex(Model model) {
26+
this.model = model;
27+
traits = SchemaTraitFilterIndex.of(model);
28+
}
29+
30+
public static SchemaReferenceIndex of(Model model) {
31+
return model.getKnowledge(SchemaReferenceIndex.class, SchemaReferenceIndex::new);
32+
}
33+
34+
/**
35+
* A reference shape is a function pointer to a shape that doesn't have a constant numeric
36+
* sentinel value.
37+
* Simple non-aggregate types and lists/maps of those types are considered non-reference
38+
* in TypeScript.
39+
*
40+
* @return whether shape is a reference shape.
41+
*/
42+
public boolean isReferenceSchema(Shape shape) {
43+
Shape targetShape = shape;
44+
if (shape instanceof MemberShape member) {
45+
targetShape = model.expectShape(member.getTarget());
46+
}
47+
ShapeType type = targetShape.getType();
48+
switch (type) {
49+
case BOOLEAN, STRING, BYTE, DOUBLE, FLOAT, INTEGER, LONG, SHORT, ENUM, INT_ENUM -> {
50+
return false;
51+
}
52+
case TIMESTAMP, BLOB -> {
53+
return false;
54+
}
55+
case LIST, SET, MAP -> {
56+
if (shape instanceof CollectionShape collection) {
57+
return isReferenceSchema(collection.getMember());
58+
} else if (shape instanceof MapShape map) {
59+
return isReferenceSchema(map.getValue());
60+
}
61+
return true;
62+
}
63+
default -> {
64+
return true;
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)
Please sign in to comment.