Skip to content

Commit b05a11b

Browse files
committed
feat(core/schema): add schema classes
1 parent 06b0ce8 commit b05a11b

30 files changed

+1478
-15
lines changed

.changeset/grumpy-cobras-melt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@smithy/core": minor
3+
"@smithy/middleware-serde": patch
4+
---
5+
6+
add schema classes

packages/core/package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo || exit 0",
1212
"lint": "npx eslint -c ../../.eslintrc.js \"src/**/*.ts\" --fix && node ./scripts/lint",
1313
"format": "prettier --config ../../prettier.config.js --ignore-path ../../.prettierignore --write \"**/*.{ts,md,json}\"",
14-
"test": "yarn g:vitest run",
1514
"test:cbor:perf": "node ./scripts/cbor-perf.mjs",
15+
"test": "yarn g:vitest run",
1616
"test:watch": "yarn g:vitest watch"
1717
},
1818
"main": "./dist-cjs/index.js",
@@ -52,6 +52,13 @@
5252
"import": "./dist-es/submodules/serde/index.js",
5353
"require": "./dist-cjs/submodules/serde/index.js",
5454
"types": "./dist-types/submodules/serde/index.d.ts"
55+
},
56+
"./schema": {
57+
"module": "./dist-es/submodules/schema/index.js",
58+
"node": "./dist-cjs/submodules/schema/index.js",
59+
"import": "./dist-es/submodules/schema/index.js",
60+
"require": "./dist-cjs/submodules/schema/index.js",
61+
"types": "./dist-types/submodules/schema/index.d.ts"
5562
}
5663
},
5764
"author": {
@@ -85,6 +92,8 @@
8592
"./cbor.js",
8693
"./protocols.d.ts",
8794
"./protocols.js",
95+
"./schema.d.ts",
96+
"./schema.js",
8897
"./serde.d.ts",
8998
"./serde.js",
9099
"dist-*/**"

packages/core/schema.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
/**
3+
* Do not edit:
4+
* This is a compatibility redirect for contexts that do not understand package.json exports field.
5+
*/
6+
declare module "@smithy/core/schema" {
7+
export * from "@smithy/core/dist-types/submodules/schema/index.d";
8+
}

packages/core/schema.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
/**
3+
* Do not edit:
4+
* This is a compatibility redirect for contexts that do not understand package.json exports field.
5+
*/
6+
module.exports = require("./dist-cjs/submodules/schema/index.js");
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import type { Schema as ISchema } from "@smithy/types";
2+
3+
import { ErrorSchema } from "./schemas/ErrorSchema";
4+
5+
export class TypeRegistry {
6+
public static active: TypeRegistry | null = null;
7+
public static readonly registries = new Map<string, TypeRegistry>();
8+
9+
private constructor(
10+
public readonly namespace: string,
11+
private schemas: Map<string, ISchema> = new Map()
12+
) {}
13+
14+
/**
15+
* @param namespace - specifier.
16+
* @returns the schema for that namespace, creating it if necessary.
17+
*/
18+
public static for(namespace: string): TypeRegistry {
19+
if (!TypeRegistry.registries.has(namespace)) {
20+
TypeRegistry.registries.set(namespace, new TypeRegistry(namespace));
21+
}
22+
return TypeRegistry.registries.get(namespace)!;
23+
}
24+
25+
/**
26+
* The active type registry's namespace is used.
27+
* @param shapeId - to be registered.
28+
* @param schema - to be registered.
29+
*/
30+
public register(shapeId: string, schema: ISchema) {
31+
const qualifiedName = this.normalizeShapeId(shapeId);
32+
const registry = TypeRegistry.for(this.getNamespace(shapeId));
33+
registry.schemas.set(qualifiedName, schema);
34+
}
35+
36+
/**
37+
* @param shapeId - query.
38+
* @returns the schema.
39+
*/
40+
public getSchema(shapeId: string): ISchema {
41+
const id = this.normalizeShapeId(shapeId);
42+
if (!this.schemas.has(id)) {
43+
throw new Error(`@smithy/core/schema - schema not found for ${id}`);
44+
}
45+
return this.schemas.get(id)!;
46+
}
47+
48+
public getBaseException(): ErrorSchema | undefined {
49+
for (const [id, schema] of this.schemas.entries()) {
50+
if (id.startsWith("awssdkjs.synthetic.") && id.endsWith("ServiceException")) {
51+
return schema as ErrorSchema;
52+
}
53+
}
54+
return undefined;
55+
}
56+
57+
public find(seeker: (schema: ISchema) => boolean) {
58+
return [...this.schemas.values()].find(seeker);
59+
}
60+
61+
public destroy() {
62+
TypeRegistry.registries.delete(this.namespace);
63+
this.schemas.clear();
64+
}
65+
66+
private normalizeShapeId(shapeId: string) {
67+
if (shapeId.includes("#")) {
68+
return shapeId;
69+
}
70+
return this.namespace + "#" + shapeId;
71+
}
72+
73+
private getNamespace(shapeId: string) {
74+
return this.normalizeShapeId(shapeId).split("#")[0];
75+
}
76+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { Schema, SchemaRef } from "@smithy/types";
2+
3+
/**
4+
* Dereferences a SchemaRef if needed.
5+
* @internal
6+
*/
7+
export const deref = (schemaRef: SchemaRef): Schema => {
8+
if (typeof schemaRef === "function") {
9+
return schemaRef();
10+
}
11+
return schemaRef;
12+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export * from "./deref";
2+
export * from "./middleware/getSchemaSerdePlugin";
3+
export * from "./schemas/ListSchema";
4+
export * from "./schemas/MapSchema";
5+
export * from "./schemas/OperationSchema";
6+
export * from "./schemas/ErrorSchema";
7+
export * from "./schemas/NormalizedSchema";
8+
export * from "./schemas/Schema";
9+
export * from "./schemas/SimpleSchema";
10+
export * from "./schemas/StructureSchema";
11+
export * from "./schemas/sentinels";
12+
export * from "./TypeRegistry";
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
DeserializeHandlerOptions,
3+
MetadataBearer,
4+
MiddlewareStack,
5+
Pluggable,
6+
SerdeFunctions,
7+
SerializeHandlerOptions,
8+
} from "@smithy/types";
9+
10+
import { PreviouslyResolved } from "./schema-middleware-types";
11+
import { schemaDeserializationMiddleware } from "./schemaDeserializationMiddleware";
12+
import { schemaSerializationMiddleware } from "./schemaSerializationMiddleware";
13+
14+
/**
15+
* @internal
16+
*/
17+
export const deserializerMiddlewareOption: DeserializeHandlerOptions = {
18+
name: "deserializerMiddleware",
19+
step: "deserialize",
20+
tags: ["DESERIALIZER"],
21+
override: true,
22+
};
23+
24+
/**
25+
* @internal
26+
*/
27+
export const serializerMiddlewareOption: SerializeHandlerOptions = {
28+
name: "serializerMiddleware",
29+
step: "serialize",
30+
tags: ["SERIALIZER"],
31+
override: true,
32+
};
33+
34+
/**
35+
* @internal
36+
*/
37+
export function getSchemaSerdePlugin<InputType extends object = any, OutputType extends MetadataBearer = any>(
38+
config: PreviouslyResolved
39+
): Pluggable<InputType, OutputType> {
40+
return {
41+
applyToStack: (commandStack: MiddlewareStack<InputType, OutputType>) => {
42+
commandStack.add(schemaSerializationMiddleware(config), serializerMiddlewareOption);
43+
commandStack.add(schemaDeserializationMiddleware(config), deserializerMiddlewareOption);
44+
// `config` is fully resolved at the point of applying plugins.
45+
// As such, config qualifies as SerdeContext.
46+
config.protocol.setSerdeContext(config as SerdeFunctions);
47+
},
48+
};
49+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { ClientProtocol, SerdeContext, UrlParser } from "@smithy/types";
2+
3+
/**
4+
* @internal
5+
*/
6+
export type PreviouslyResolved = Omit<
7+
SerdeContext & {
8+
urlParser: UrlParser;
9+
protocol: ClientProtocol<any, any>;
10+
},
11+
"endpoint"
12+
>;

0 commit comments

Comments
 (0)