From 47eefe37a594dd60dfcd6d6bcef66d759a948eda Mon Sep 17 00:00:00 2001 From: John Lwin Date: Mon, 3 Mar 2025 21:04:43 -0800 Subject: [PATCH 01/13] fix(docs): improve deprecatedTrait with default description --- .../amazon/smithy/typescript/codegen/TypeScriptWriter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java index c183b7d2054..5e2ab58577b 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java @@ -225,9 +225,9 @@ boolean writeShapeDocs(Shape shape, UnaryOperator preprocessor) { if (shape.getTrait(DeprecatedTrait.class).isPresent()) { DeprecatedTrait deprecatedTrait = shape.expectTrait(DeprecatedTrait.class); String deprecationMessage = deprecatedTrait.getMessage() - .map(msg -> " " + msg) - .orElse(""); - String deprecationString = "@deprecated" + deprecationMessage; + .map(msg -> " " + msg) + .orElse(" see description"); + String deprecationString = deprecatedTrait.getMessage().isPresent() ? "@deprecated" + deprecationMessage : "@deprecated"; docs = docs + "\n\n" + deprecationString; } docs = preprocessor.apply(docs); From 59f11a2596916ddf1f3efa4c3715c06b1584e2ca Mon Sep 17 00:00:00 2001 From: John Lwin Date: Thu, 25 Sep 2025 22:34:49 -0700 Subject: [PATCH 02/13] fix: ensure deprecated annotations always include message for TSDoc compliance --- .../amazon/smithy/typescript/codegen/TypeScriptWriter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java index 5e2ab58577b..46bf1878405 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java @@ -227,8 +227,8 @@ boolean writeShapeDocs(Shape shape, UnaryOperator preprocessor) { String deprecationMessage = deprecatedTrait.getMessage() .map(msg -> " " + msg) .orElse(" see description"); - String deprecationString = deprecatedTrait.getMessage().isPresent() ? "@deprecated" + deprecationMessage : "@deprecated"; - docs = docs + "\n\n" + deprecationString; + String deprecationAnnotation = "@deprecated " + deprecationMessage; + docs = docs + "\n\n" + deprecationAnnotation; } docs = preprocessor.apply(docs); docs = addReleaseTag(shape, docs); From 29c0c4d0378346d27b887e7db5dfb261f6e9be0d Mon Sep 17 00:00:00 2001 From: John Lwin Date: Mon, 6 Oct 2025 10:56:34 -0700 Subject: [PATCH 03/13] chore: remove prefix spaces --- .../amazon/smithy/typescript/codegen/TypeScriptWriter.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java index 46bf1878405..5b5d7e61437 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java @@ -225,9 +225,8 @@ boolean writeShapeDocs(Shape shape, UnaryOperator preprocessor) { if (shape.getTrait(DeprecatedTrait.class).isPresent()) { DeprecatedTrait deprecatedTrait = shape.expectTrait(DeprecatedTrait.class); String deprecationMessage = deprecatedTrait.getMessage() - .map(msg -> " " + msg) - .orElse(" see description"); - String deprecationAnnotation = "@deprecated " + deprecationMessage; + .orElse("see description"); + String deprecationAnnotation = "@deprecated " + deprecationMessage; docs = docs + "\n\n" + deprecationAnnotation; } docs = preprocessor.apply(docs); From b4e0d94147f07105c14e00c778bdf0913dfc058f Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 7 Oct 2025 13:08:27 -0400 Subject: [PATCH 04/13] fix(util-body-length-node): restrict fs calls to fs.ReadStream objects (#1735) --- .changeset/odd-rats-reflect.md | 5 +++++ .../src/calculateBodyLength.spec.ts | 8 +++---- .../src/calculateBodyLength.ts | 22 +++++++++++++------ 3 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 .changeset/odd-rats-reflect.md diff --git a/.changeset/odd-rats-reflect.md b/.changeset/odd-rats-reflect.md new file mode 100644 index 00000000000..b97b46a3d6c --- /dev/null +++ b/.changeset/odd-rats-reflect.md @@ -0,0 +1,5 @@ +--- +"@smithy/util-body-length-node": patch +--- + +restrict fs calls to fs.ReadStream instances diff --git a/packages/util-body-length-node/src/calculateBodyLength.spec.ts b/packages/util-body-length-node/src/calculateBodyLength.spec.ts index 1be530f21fe..c183a17c7fe 100644 --- a/packages/util-body-length-node/src/calculateBodyLength.spec.ts +++ b/packages/util-body-length-node/src/calculateBodyLength.spec.ts @@ -84,11 +84,9 @@ describe(calculateBodyLength.name, () => { it("should handle stream created using fd.createReadStream", async () => { const fd = await fs.promises.open(__filename, "r"); - if ((fd as any).createReadStream) { - const fdReadStream = (fd as any).createReadStream(); - expect(calculateBodyLength(fdReadStream)).toEqual(fileSize); - fdReadStream.close(); - } + const fdReadStream = fd.createReadStream(); + expect(calculateBodyLength(fdReadStream)).toEqual(fileSize); + fdReadStream.close(); }); }); diff --git a/packages/util-body-length-node/src/calculateBodyLength.ts b/packages/util-body-length-node/src/calculateBodyLength.ts index 2e3bf4d40ba..a941e931b8d 100644 --- a/packages/util-body-length-node/src/calculateBodyLength.ts +++ b/packages/util-body-length-node/src/calculateBodyLength.ts @@ -1,4 +1,11 @@ -import { fstatSync, lstatSync } from "fs"; +import { fstatSync, lstatSync, ReadStream } from "node:fs"; + +/** + * @internal + */ +type HasFileDescriptor = { + fd: number; +}; /** * @internal @@ -16,12 +23,13 @@ export const calculateBodyLength = (body: any): number | undefined => { return body.size; } else if (typeof body.start === "number" && typeof body.end === "number") { return body.end + 1 - body.start; - } else if (typeof body.path === "string" || Buffer.isBuffer(body.path)) { - // handles fs readable streams - return lstatSync(body.path).size; - } else if (typeof body.fd === "number") { - // handles fd readable streams - return fstatSync(body.fd).size; + } else if (body instanceof ReadStream) { + // the previous use case where start and end are numbers is also potentially a ReadStream. + if (body.path != null) { + return lstatSync(body.path).size; + } else if (typeof (body as ReadStream & HasFileDescriptor).fd === "number") { + return fstatSync((body as ReadStream & HasFileDescriptor).fd).size; + } } throw new Error(`Body Length computation failed for ${body}`); }; From 9d6ef6d2c97fdacfc2453cb3c1e5a72c2213380b Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 7 Oct 2025 16:09:30 -0400 Subject: [PATCH 05/13] feat: use feature detection for defaults-mode-browser (#1730) --- .changeset/sixty-cows-pretend.md | 5 ++ .../util-defaults-mode-browser/package.json | 1 - .../src/resolveDefaultsModeConfig.spec.ts | 40 ++++++++++--- .../src/resolveDefaultsModeConfig.ts | 56 +++++++++++++++---- yarn.lock | 8 --- 5 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 .changeset/sixty-cows-pretend.md diff --git a/.changeset/sixty-cows-pretend.md b/.changeset/sixty-cows-pretend.md new file mode 100644 index 00000000000..7cc2315ee79 --- /dev/null +++ b/.changeset/sixty-cows-pretend.md @@ -0,0 +1,5 @@ +--- +"@smithy/util-defaults-mode-browser": minor +--- + +remove bower from mobile device detection diff --git a/packages/util-defaults-mode-browser/package.json b/packages/util-defaults-mode-browser/package.json index cc31bba37ff..44d2314ff75 100644 --- a/packages/util-defaults-mode-browser/package.json +++ b/packages/util-defaults-mode-browser/package.json @@ -27,7 +27,6 @@ "@smithy/property-provider": "workspace:^", "@smithy/smithy-client": "workspace:^", "@smithy/types": "workspace:^", - "bowser": "^2.11.0", "tslib": "^2.6.2" }, "devDependencies": { diff --git a/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.spec.ts b/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.spec.ts index 520bd6616f1..70daa705814 100644 --- a/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.spec.ts +++ b/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.spec.ts @@ -1,14 +1,37 @@ -import bowser from "bowser"; -import { afterEach, describe, expect, test as it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { DEFAULTS_MODE_OPTIONS } from "./constants"; import { resolveDefaultsModeConfig } from "./resolveDefaultsModeConfig"; -vi.mock("bowser"); + +/** + * @internal + */ +type NavigatorTestAugment = Navigator & { + userAgentData?: { + mobile?: boolean; + }; + connection?: { + effectiveType?: "4g" | string; + rtt?: number; + downlink?: number; + }; +}; describe("resolveDefaultsModeConfig", () => { const uaSpy = vi.spyOn(window.navigator, "userAgent", "get").mockReturnValue("some UA"); + beforeEach(() => { + const navigator = window.navigator as NavigatorTestAugment; + if (!navigator.userAgentData || !navigator.connection) { + navigator.userAgentData = {}; + navigator.connection = {}; + } + }); + afterEach(() => { + const navigator = window.navigator as NavigatorTestAugment; + delete navigator.userAgentData; + delete navigator.connection; uaSpy.mockClear(); }); @@ -22,17 +45,20 @@ describe("resolveDefaultsModeConfig", () => { }); it("should resolve auto mode to mobile if platform is mobile", async () => { - (bowser.parse as any).mockReturnValue({ platform: { type: "mobile" } }); + vi.spyOn(window.navigator as NavigatorTestAugment, "userAgentData", "get").mockReturnValue({ + mobile: true, + }); expect(await resolveDefaultsModeConfig({ defaultsMode: () => Promise.resolve("auto") })()).toBe("mobile"); }); - it("should resolve auto mode to mobile if platform is tablet", async () => { - (bowser.parse as any).mockReturnValue({ platform: { type: "tablet" } }); + it("should resolve auto mode to mobile if connection is not 4g (5g is not possible in this enum)", async () => { + vi.spyOn(window.navigator as NavigatorTestAugment, "connection", "get").mockReturnValue({ + effectiveType: "3g", + }); expect(await resolveDefaultsModeConfig({ defaultsMode: () => Promise.resolve("auto") })()).toBe("mobile"); }); it("should resolve auto mode to standard if platform not mobile or tablet", async () => { - (bowser.parse as any).mockReturnValue({ platform: { type: "desktop" } }); expect(await resolveDefaultsModeConfig({ defaultsMode: () => Promise.resolve("auto") })()).toBe("standard"); }); diff --git a/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.ts b/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.ts index e2cf4b7611a..a88d3a13668 100644 --- a/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.ts +++ b/packages/util-defaults-mode-browser/src/resolveDefaultsModeConfig.ts @@ -1,7 +1,6 @@ import { memoize } from "@smithy/property-provider"; import type { DefaultsMode, ResolvedDefaultsMode } from "@smithy/smithy-client"; import type { Provider } from "@smithy/types"; -import bowser from "bowser"; import { DEFAULTS_MODE_OPTIONS } from "./constants"; @@ -27,7 +26,7 @@ export const resolveDefaultsModeConfig = ({ const mode = typeof defaultsMode === "function" ? await defaultsMode() : defaultsMode; switch (mode?.toLowerCase()) { case "auto": - return Promise.resolve(isMobileBrowser() ? "mobile" : "standard"); + return Promise.resolve(useMobileConfiguration() ? "mobile" : "standard"); case "mobile": case "in-region": case "cross-region": @@ -43,12 +42,49 @@ export const resolveDefaultsModeConfig = ({ } }); -const isMobileBrowser = (): boolean => { - const parsedUA = - typeof window !== "undefined" && window?.navigator?.userAgent - ? bowser.parse(window.navigator.userAgent) - : undefined; - const platform = parsedUA?.platform?.type; - // Reference: https://github.com/lancedikson/bowser/blob/master/src/constants.js#L86 - return platform === "tablet" || platform === "mobile"; +/** + * @internal + */ +type NavigatorAugment = { + userAgentData?: { + mobile?: boolean; + }; + connection?: { + effectiveType?: "4g" | string; + rtt?: number; + downlink?: number; + }; +}; + +/** + * The aim of the mobile detection function is not really to know whether the device is a mobile device. + * This is emphasized in the modern guidance on browser detection that feature detection is correct + * whereas UA "sniffing" is usually a mistake. + * + * So then, the underlying reason we are trying to detect a mobile device is not for any particular device feature, + * but rather the implied network speed available to the program (we use it to set a default request timeout value). + * + * Therefore, it is better to use network speed related feature detection when available. This also saves + * 20kb (minified) from the bowser dependency we were using. + * + * @internal + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Browser_detection_using_the_user_agent + */ +const useMobileConfiguration = (): boolean => { + const navigator = window?.navigator as (typeof window.navigator & NavigatorAugment) | undefined; + if (navigator?.connection) { + // https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType + // The maximum will report as 4g, regardless of 5g or further developments. + const { effectiveType, rtt, downlink } = navigator?.connection; + const slow = + (typeof effectiveType === "string" && effectiveType !== "4g") || Number(rtt) > 100 || Number(downlink) < 10; + if (slow) { + return true; + } + } + + // without the networkInformation object, we use the userAgentData or touch feature detection as a proxy. + return ( + navigator?.userAgentData?.mobile || (typeof navigator?.maxTouchPoints === "number" && navigator?.maxTouchPoints > 1) + ); }; diff --git a/yarn.lock b/yarn.lock index 45fab0dd8ae..5de2ba342cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3286,7 +3286,6 @@ __metadata: "@smithy/smithy-client": "workspace:^" "@smithy/types": "workspace:^" "@types/node": "npm:^18.11.9" - bowser: "npm:^2.11.0" concurrently: "npm:7.0.0" downlevel-dts: "npm:0.10.1" rimraf: "npm:3.0.2" @@ -4655,13 +4654,6 @@ __metadata: languageName: node linkType: hard -"bowser@npm:^2.11.0": - version: 2.11.0 - resolution: "bowser@npm:2.11.0" - checksum: 10c0/04efeecc7927a9ec33c667fa0965dea19f4ac60b3fea60793c2e6cf06c1dcd2f7ae1dbc656f450c5f50783b1c75cf9dc173ba6f3b7db2feee01f8c4b793e1bd3 - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" From fb7bffdd5f722729731e35212982cafc57caec1d Mon Sep 17 00:00:00 2001 From: George Fu Date: Wed, 8 Oct 2025 13:50:47 -0400 Subject: [PATCH 06/13] refactoring to reduce code size (#1720) * refactoring to reduce code size fix base64 alphabetByValue date util testing chore: inline schema magic numbers * move translateTraits fn to own file --- .changeset/tame-suns-carry.md | 7 + .../core/src/submodules/cbor/CborCodec.ts | 4 +- .../cbor/SmithyRpcV2CborProtocol.spec.ts | 43 +- .../event-streams/EventStreamSerde.spec.ts | 22 +- .../event-streams/EventStreamSerde.ts | 11 +- .../protocols/HttpBindingProtocol.spec.ts | 15 +- .../protocols/HttpBindingProtocol.ts | 10 +- .../submodules/protocols/HttpProtocol.spec.ts | 12 +- .../src/submodules/protocols/HttpProtocol.ts | 6 +- .../src/submodules/protocols/RpcProtocol.ts | 5 +- .../serde/FromStringShapeDeserializer.ts | 50 ++- .../serde/ToStringShapeSerializer.ts | 18 +- .../serde/determineTimestampFormat.ts | 11 +- packages/core/src/submodules/schema/index.ts | 1 + .../schema/schemas/NormalizedSchema.spec.ts | 82 ++-- .../schema/schemas/NormalizedSchema.ts | 321 +++++-------- .../submodules/schema/schemas/schemas.spec.ts | 22 - .../submodules/schema/schemas/sentinels.ts | 2 + .../schema/schemas/translateTraits.ts | 29 ++ packages/core/src/submodules/serde/index.ts | 1 + .../schema-date-utils.spec.ts | 421 ++++++++++++++++++ .../schema-serde-lib/schema-date-utils.ts | 176 ++++++++ .../serde/value/NumericValue.spec.ts | 8 +- .../submodules/serde/value/NumericValue.ts | 43 +- packages/util-base64/src/constants.browser.ts | 48 +- packages/util-base64/vitest.config.mts | 2 +- .../src/blob/Uint8ArrayBlobAdapter.ts | 9 +- 27 files changed, 965 insertions(+), 414 deletions(-) create mode 100644 .changeset/tame-suns-carry.md create mode 100644 packages/core/src/submodules/schema/schemas/translateTraits.ts create mode 100644 packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.spec.ts create mode 100644 packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.ts diff --git a/.changeset/tame-suns-carry.md b/.changeset/tame-suns-carry.md new file mode 100644 index 00000000000..26b79823254 --- /dev/null +++ b/.changeset/tame-suns-carry.md @@ -0,0 +1,7 @@ +--- +"@smithy/util-base64": minor +"@smithy/util-stream": minor +"@smithy/core": minor +--- + +refactoring to reduce code size diff --git a/packages/core/src/submodules/cbor/CborCodec.ts b/packages/core/src/submodules/cbor/CborCodec.ts index a3d1f911cb2..1331cf79738 100644 --- a/packages/core/src/submodules/cbor/CborCodec.ts +++ b/packages/core/src/submodules/cbor/CborCodec.ts @@ -1,5 +1,5 @@ import { NormalizedSchema } from "@smithy/core/schema"; -import { generateIdempotencyToken, parseEpochTimestamp } from "@smithy/core/serde"; +import { _parseEpochTimestamp, generateIdempotencyToken } from "@smithy/core/serde"; import type { Codec, Schema, SerdeFunctions, ShapeDeserializer, ShapeSerializer } from "@smithy/types"; import { fromBase64 } from "@smithy/util-base64"; @@ -148,7 +148,7 @@ export class CborShapeDeserializer implements ShapeDeserializer { if (ns.isTimestampSchema() && typeof value === "number") { // format is ignored. - return parseEpochTimestamp(value); + return _parseEpochTimestamp(value); } if (ns.isBlobSchema()) { diff --git a/packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts b/packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts index 96627f9e05d..65bd1c6a0cd 100644 --- a/packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts +++ b/packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts @@ -1,7 +1,16 @@ -import type { ErrorSchema } from "@smithy/core/schema"; import { error, list, map, op, SCHEMA, struct, TypeRegistry } from "@smithy/core/schema"; import { HttpRequest, HttpResponse } from "@smithy/protocol-http"; -import type { ResponseMetadata, RetryableTrait, SchemaRef } from "@smithy/types"; +import type { + BlobSchema, + BooleanSchema, + MapSchemaModifier, + NumericSchema, + ResponseMetadata, + RetryableTrait, + SchemaRef, + StringSchema, + TimestampDefaultSchema, +} from "@smithy/types"; import { beforeEach, describe, expect, test as it } from "vitest"; import { cbor } from "./cbor"; @@ -29,8 +38,8 @@ describe(SmithyRpcV2CborProtocol.name, () => { {}, ["timestamp", "blob"], [ - [SCHEMA.TIMESTAMP_DEFAULT, 0], - [SCHEMA.BLOB, 0], + [4 satisfies TimestampDefaultSchema, 0], + [21 satisfies BlobSchema, 0], ] ), input: { @@ -56,11 +65,11 @@ describe(SmithyRpcV2CborProtocol.name, () => { {}, ["bool", "timestamp", "blob", "prefixHeaders", "searchParams"], [ - [SCHEMA.BOOLEAN, { httpQuery: "bool" }], - [SCHEMA.TIMESTAMP_DEFAULT, { httpHeader: "timestamp" }], - [SCHEMA.BLOB, { httpHeader: "blob" }], - [SCHEMA.MAP_MODIFIER | SCHEMA.STRING, { httpPrefixHeaders: "anti-" }], - [SCHEMA.MAP_MODIFIER | SCHEMA.STRING, { httpQueryParams: 1 }], + [2 satisfies BooleanSchema, { httpQuery: "bool" }], + [4 satisfies TimestampDefaultSchema, { httpHeader: "timestamp" }], + [21 satisfies BlobSchema, { httpHeader: "blob" }], + [(128 satisfies MapSchemaModifier) | (0 satisfies StringSchema), { httpPrefixHeaders: "anti-" }], + [(128 satisfies MapSchemaModifier) | (0 satisfies StringSchema), { httpQueryParams: 1 }], ] ), input: { @@ -104,10 +113,10 @@ describe(SmithyRpcV2CborProtocol.name, () => { 0, ["mySparseList", "myRegularList", "mySparseMap", "myRegularMap"], [ - [() => list("", "MySparseList", { sparse: 1 }, SCHEMA.NUMERIC), {}], - [() => list("", "MyList", {}, SCHEMA.NUMERIC), {}], - [() => map("", "MySparseMap", { sparse: 1 }, SCHEMA.STRING, SCHEMA.NUMERIC), {}], - [() => map("", "MyMap", {}, SCHEMA.STRING, SCHEMA.NUMERIC), {}], + [() => list("", "MySparseList", { sparse: 1 }, 1 satisfies NumericSchema), {}], + [() => list("", "MyList", {}, 1 satisfies NumericSchema), {}], + [() => map("", "MySparseMap", { sparse: 1 }, 0 satisfies StringSchema, 1 satisfies NumericSchema), {}], + [() => map("", "MyMap", {}, 0 satisfies StringSchema, 1 satisfies NumericSchema), {}], ] ), input: { @@ -207,10 +216,10 @@ describe(SmithyRpcV2CborProtocol.name, () => { 0, ["mySparseList", "myRegularList", "mySparseMap", "myRegularMap"], [ - [() => list("", "MyList", { sparse: 1 }, SCHEMA.NUMERIC), {}], - [() => list("", "MyList", {}, SCHEMA.NUMERIC), {}], - [() => map("", "MyMap", { sparse: 1 }, SCHEMA.STRING, SCHEMA.NUMERIC), {}], - [() => map("", "MyMap", {}, SCHEMA.STRING, SCHEMA.NUMERIC), {}], + [() => list("", "MyList", { sparse: 1 }, 1 satisfies NumericSchema), {}], + [() => list("", "MyList", {}, 1 satisfies NumericSchema), {}], + [() => map("", "MyMap", { sparse: 1 }, 0 satisfies StringSchema, 1 satisfies NumericSchema), {}], + [() => map("", "MyMap", {}, 0 satisfies StringSchema, 1 satisfies NumericSchema), {}], ] ), mockOutput: { diff --git a/packages/core/src/submodules/event-streams/EventStreamSerde.spec.ts b/packages/core/src/submodules/event-streams/EventStreamSerde.spec.ts index 0b8fef2098f..044554caea7 100644 --- a/packages/core/src/submodules/event-streams/EventStreamSerde.spec.ts +++ b/packages/core/src/submodules/event-streams/EventStreamSerde.spec.ts @@ -1,8 +1,14 @@ import { cbor, CborCodec, dateToTag } from "@smithy/core/cbor"; -import { NormalizedSchema, SCHEMA, sim, struct } from "@smithy/core/schema"; +import { NormalizedSchema, sim, struct } from "@smithy/core/schema"; import { EventStreamMarshaller } from "@smithy/eventstream-serde-node"; import { HttpResponse } from "@smithy/protocol-http"; -import type { Message as EventMessage } from "@smithy/types"; +import type { + BlobSchema, + Message as EventMessage, + StreamingBlobSchema, + StringSchema, + TimestampEpochSecondsSchema, +} from "@smithy/types"; import { fromUtf8, toUtf8 } from "@smithy/util-utf8"; import { describe, expect, test as it } from "vitest"; @@ -53,9 +59,15 @@ describe(EventStreamSerde.name, () => { "Payload", 0, ["payload"], - [sim("ns", "StreamingBlobPayload", SCHEMA.STREAMING_BLOB, { eventPayload: 1 })] + [sim("ns", "StreamingBlobPayload", 42 satisfies StreamingBlobSchema, { eventPayload: 1 })] + ), + struct( + "ns", + "TextPayload", + 0, + ["payload"], + [sim("ns", "TextPayload", 0 satisfies StringSchema, { eventPayload: 1 })] ), - struct("ns", "TextPayload", 0, ["payload"], [sim("ns", "TextPayload", SCHEMA.STRING, { eventPayload: 1 })]), struct( "ns", "CustomHeaders", @@ -73,7 +85,7 @@ describe(EventStreamSerde.name, () => { // here the non-eventstream members form an initial-request // or initial-response when present. ["eventStreamMember", "dateMember", "blobMember"], - [eventStreamUnionSchema, SCHEMA.TIMESTAMP_EPOCH_SECONDS, SCHEMA.BLOB] + [eventStreamUnionSchema, 7 satisfies TimestampEpochSecondsSchema, 21 satisfies BlobSchema] ); describe("serialization", () => { diff --git a/packages/core/src/submodules/event-streams/EventStreamSerde.ts b/packages/core/src/submodules/event-streams/EventStreamSerde.ts index 7bff737f50b..91531f9643d 100644 --- a/packages/core/src/submodules/event-streams/EventStreamSerde.ts +++ b/packages/core/src/submodules/event-streams/EventStreamSerde.ts @@ -1,6 +1,6 @@ -import type { NormalizedSchema } from "@smithy/core/schema"; -import { SCHEMA } from "@smithy/core/schema"; +import type { NormalizedSchema, StructureSchema } from "@smithy/core/schema"; import type { + DocumentSchema, EventStreamMarshaller, HttpRequest as IHttpRequest, HttpResponse as IHttpResponse, @@ -232,14 +232,17 @@ export class EventStreamSerde { let explicitPayloadMember = null as null | string; let explicitPayloadContentType: undefined | string; - const isKnownSchema = unionSchema.hasMemberSchema(unionMember); + const isKnownSchema = (() => { + const struct = unionSchema.getSchema() as StructureSchema; + return struct.memberNames.includes(unionMember); + })(); const additionalHeaders: MessageHeaders = {}; if (!isKnownSchema) { // $unknown member const [type, value] = event[unionMember]; eventType = type; - serializer.write(SCHEMA.DOCUMENT, value); + serializer.write(15 satisfies DocumentSchema, value); } else { const eventSchema = unionSchema.getMemberSchema(unionMember); diff --git a/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts b/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts index 98eba8f158f..ce6eed9b625 100644 --- a/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts +++ b/packages/core/src/submodules/protocols/HttpBindingProtocol.spec.ts @@ -1,10 +1,12 @@ -import { map, op, SCHEMA, struct } from "@smithy/core/schema"; +import { map, op, struct } from "@smithy/core/schema"; import { HttpResponse } from "@smithy/protocol-http"; import type { Codec, CodecSettings, HandlerExecutionContext, HttpResponse as IHttpResponse, + ListSchemaModifier, + MapSchemaModifier, MetadataBearer, OperationSchema, ResponseMetadata, @@ -12,6 +14,9 @@ import type { SerdeFunctions, ShapeDeserializer, ShapeSerializer, + StringSchema, + TimestampDefaultSchema, + TimestampEpochSecondsSchema, } from "@smithy/types"; import { parseUrl } from "@smithy/url-parser/src"; import { describe, expect, test as it } from "vitest"; @@ -32,7 +37,7 @@ describe(HttpBindingProtocol.name, () => { const settings: CodecSettings = { timestampFormat: { useTrait: true, - default: SCHEMA.TIMESTAMP_EPOCH_SECONDS, + default: 7 satisfies TimestampEpochSecondsSchema, }, httpBindings: true, }; @@ -82,7 +87,7 @@ describe(HttpBindingProtocol.name, () => { ["timestampList"], [ [ - SCHEMA.LIST_MODIFIER | SCHEMA.TIMESTAMP_DEFAULT, + (64 satisfies ListSchemaModifier) | (4 satisfies TimestampDefaultSchema), { httpHeader: "x-timestamplist", }, @@ -122,7 +127,7 @@ describe(HttpBindingProtocol.name, () => { ["httpPrefixHeaders"], [ [ - SCHEMA.MAP_MODIFIER | SCHEMA.STRING, + (128 satisfies MapSchemaModifier) | (0 satisfies StringSchema), { httpPrefixHeaders: "", }, @@ -175,7 +180,7 @@ describe(HttpBindingProtocol.name, () => { httpBindings: true, timestampFormat: { useTrait: true, - default: SCHEMA.TIMESTAMP_EPOCH_SECONDS, + default: 7 satisfies TimestampEpochSecondsSchema, }, }), } diff --git a/packages/core/src/submodules/protocols/HttpBindingProtocol.ts b/packages/core/src/submodules/protocols/HttpBindingProtocol.ts index 5f38158f92f..b3266b13ea9 100644 --- a/packages/core/src/submodules/protocols/HttpBindingProtocol.ts +++ b/packages/core/src/submodules/protocols/HttpBindingProtocol.ts @@ -1,7 +1,8 @@ -import { NormalizedSchema, SCHEMA } from "@smithy/core/schema"; +import { NormalizedSchema, translateTraits } from "@smithy/core/schema"; import { splitEvery, splitHeader } from "@smithy/core/serde"; import { HttpRequest } from "@smithy/protocol-http"; import type { + DocumentSchema, Endpoint, EndpointBearer, HandlerExecutionContext, @@ -11,6 +12,7 @@ import type { OperationSchema, Schema, SerdeFunctions, + TimestampDefaultSchema, } from "@smithy/types"; import { sdkStreamMixin } from "@smithy/util-stream"; @@ -58,7 +60,7 @@ export abstract class HttpBindingProtocol extends HttpProtocol { if (endpoint) { this.updateServiceEndpoint(request, endpoint); this.setHostPrefix(request, operationSchema, input); - const opTraits = NormalizedSchema.translateTraits(operationSchema.traits); + const opTraits = translateTraits(operationSchema.traits); if (opTraits.http) { request.method = opTraits.http[0]; const [path, search] = opTraits.http[1].split("?"); @@ -203,7 +205,7 @@ export abstract class HttpBindingProtocol extends HttpProtocol { if (response.statusCode >= 300) { const bytes: Uint8Array = await collectBody(response.body, context); if (bytes.byteLength > 0) { - Object.assign(dataObject, await deserializer.read(SCHEMA.DOCUMENT, bytes)); + Object.assign(dataObject, await deserializer.read(15 satisfies DocumentSchema, bytes)); } await this.handleError(operationSchema, context, response, dataObject, this.deserializeMetadata(response)); throw new Error("@smithy/core/protocols - HTTP Protocol error handler failed to throw."); @@ -304,7 +306,7 @@ export abstract class HttpBindingProtocol extends HttpProtocol { let sections: string[]; if ( headerListValueSchema.isTimestampSchema() && - headerListValueSchema.getSchema() === SCHEMA.TIMESTAMP_DEFAULT + headerListValueSchema.getSchema() === (4 satisfies TimestampDefaultSchema) ) { sections = splitEvery(value, ",", 2); } else { diff --git a/packages/core/src/submodules/protocols/HttpProtocol.spec.ts b/packages/core/src/submodules/protocols/HttpProtocol.spec.ts index bf84daf1978..e825c8d67ef 100644 --- a/packages/core/src/submodules/protocols/HttpProtocol.spec.ts +++ b/packages/core/src/submodules/protocols/HttpProtocol.spec.ts @@ -1,5 +1,11 @@ -import { map, SCHEMA, struct } from "@smithy/core/schema"; -import type { HandlerExecutionContext, HttpResponse as IHttpResponse, Schema, SerdeFunctions } from "@smithy/types"; +import { map, struct } from "@smithy/core/schema"; +import type { + HandlerExecutionContext, + HttpResponse as IHttpResponse, + Schema, + SerdeFunctions, + TimestampEpochSecondsSchema, +} from "@smithy/types"; import { describe, expect, test as it } from "vitest"; import { HttpProtocol } from "./HttpProtocol"; @@ -18,7 +24,7 @@ describe(HttpProtocol.name, () => { httpBindings: true, timestampFormat: { useTrait: true, - default: SCHEMA.TIMESTAMP_EPOCH_SECONDS, + default: 7 satisfies TimestampEpochSecondsSchema, }, }), }); diff --git a/packages/core/src/submodules/protocols/HttpProtocol.ts b/packages/core/src/submodules/protocols/HttpProtocol.ts index ec984c79a68..f178e84ceee 100644 --- a/packages/core/src/submodules/protocols/HttpProtocol.ts +++ b/packages/core/src/submodules/protocols/HttpProtocol.ts @@ -73,10 +73,10 @@ export abstract class HttpProtocol implements ClientProtocol= 300) { const bytes: Uint8Array = await collectBody(response.body, context as SerdeFunctions); if (bytes.byteLength > 0) { - Object.assign(dataObject, await deserializer.read(SCHEMA.DOCUMENT, bytes)); + Object.assign(dataObject, await deserializer.read(15 satisfies DocumentSchema, bytes)); } await this.handleError(operationSchema, context, response, dataObject, this.deserializeMetadata(response)); throw new Error("@smithy/core/protocols - RPC Protocol error handler failed to throw."); diff --git a/packages/core/src/submodules/protocols/serde/FromStringShapeDeserializer.ts b/packages/core/src/submodules/protocols/serde/FromStringShapeDeserializer.ts index 8c1f1bd47cd..d3a1641d04b 100644 --- a/packages/core/src/submodules/protocols/serde/FromStringShapeDeserializer.ts +++ b/packages/core/src/submodules/protocols/serde/FromStringShapeDeserializer.ts @@ -1,13 +1,21 @@ -import { NormalizedSchema, SCHEMA } from "@smithy/core/schema"; +import { NormalizedSchema } from "@smithy/core/schema"; import { + _parseEpochTimestamp, + _parseRfc3339DateTimeWithOffset, + _parseRfc7231DateTime, LazyJsonString, NumericValue, - parseEpochTimestamp, - parseRfc3339DateTimeWithOffset, - parseRfc7231DateTime, splitHeader, } from "@smithy/core/serde"; -import type { CodecSettings, Schema, SerdeFunctions, ShapeDeserializer } from "@smithy/types"; +import type { + CodecSettings, + Schema, + SerdeFunctions, + ShapeDeserializer, + TimestampDateTimeSchema, + TimestampEpochSecondsSchema, + TimestampHttpDateSchema, +} from "@smithy/types"; import { fromBase64 } from "@smithy/util-base64"; import { toUtf8 } from "@smithy/util-utf8"; @@ -42,12 +50,12 @@ export class FromStringShapeDeserializer implements ShapeDeserializer { const format = determineTimestampFormat(ns, this.settings); switch (format) { - case SCHEMA.TIMESTAMP_DATE_TIME: - return parseRfc3339DateTimeWithOffset(data); - case SCHEMA.TIMESTAMP_HTTP_DATE: - return parseRfc7231DateTime(data); - case SCHEMA.TIMESTAMP_EPOCH_SECONDS: - return parseEpochTimestamp(data); + case 5 satisfies TimestampDateTimeSchema: + return _parseRfc3339DateTimeWithOffset(data); + case 6 satisfies TimestampHttpDateSchema: + return _parseRfc7231DateTime(data); + case 7 satisfies TimestampEpochSecondsSchema: + return _parseEpochTimestamp(data); default: console.warn("Missing timestamp format, parsing value with Date constructor:", data); return new Date(data as string | number); @@ -69,15 +77,17 @@ export class FromStringShapeDeserializer implements ShapeDeserializer { } } - switch (true) { - case ns.isNumericSchema(): - return Number(data); - case ns.isBigIntegerSchema(): - return BigInt(data); - case ns.isBigDecimalSchema(): - return new NumericValue(data, "bigDecimal"); - case ns.isBooleanSchema(): - return String(data).toLowerCase() === "true"; + if (ns.isNumericSchema()) { + return Number(data); + } + if (ns.isBigIntegerSchema()) { + return BigInt(data); + } + if (ns.isBigDecimalSchema()) { + return new NumericValue(data, "bigDecimal"); + } + if (ns.isBooleanSchema()) { + return String(data).toLowerCase() === "true"; } return data; } diff --git a/packages/core/src/submodules/protocols/serde/ToStringShapeSerializer.ts b/packages/core/src/submodules/protocols/serde/ToStringShapeSerializer.ts index a16960b4c30..e318f8133c2 100644 --- a/packages/core/src/submodules/protocols/serde/ToStringShapeSerializer.ts +++ b/packages/core/src/submodules/protocols/serde/ToStringShapeSerializer.ts @@ -1,6 +1,14 @@ -import { NormalizedSchema, SCHEMA } from "@smithy/core/schema"; +import { NormalizedSchema } from "@smithy/core/schema"; import { dateToUtcString, LazyJsonString, quoteHeader } from "@smithy/core/serde"; -import type { CodecSettings, Schema, SerdeFunctions, ShapeSerializer } from "@smithy/types"; +import type { + CodecSettings, + Schema, + SerdeFunctions, + ShapeSerializer, + TimestampDateTimeSchema, + TimestampEpochSecondsSchema, + TimestampHttpDateSchema, +} from "@smithy/types"; import { toBase64 } from "@smithy/util-base64"; import { determineTimestampFormat } from "./determineTimestampFormat"; @@ -38,13 +46,13 @@ export class ToStringShapeSerializer implements ShapeSerializer { } const format = determineTimestampFormat(ns, this.settings); switch (format) { - case SCHEMA.TIMESTAMP_DATE_TIME: + case 5 satisfies TimestampDateTimeSchema: this.stringBuffer = value.toISOString().replace(".000Z", "Z"); break; - case SCHEMA.TIMESTAMP_HTTP_DATE: + case 6 satisfies TimestampHttpDateSchema: this.stringBuffer = dateToUtcString(value); break; - case SCHEMA.TIMESTAMP_EPOCH_SECONDS: + case 7 satisfies TimestampEpochSecondsSchema: this.stringBuffer = String(value.getTime() / 1000); break; default: diff --git a/packages/core/src/submodules/protocols/serde/determineTimestampFormat.ts b/packages/core/src/submodules/protocols/serde/determineTimestampFormat.ts index 1e9d48b39ae..9c31218aad6 100644 --- a/packages/core/src/submodules/protocols/serde/determineTimestampFormat.ts +++ b/packages/core/src/submodules/protocols/serde/determineTimestampFormat.ts @@ -1,5 +1,4 @@ import type { NormalizedSchema } from "@smithy/core/schema"; -import { SCHEMA } from "@smithy/core/schema"; import type { CodecSettings, TimestampDateTimeSchema, @@ -20,9 +19,9 @@ export function determineTimestampFormat( if (settings.timestampFormat.useTrait) { if ( ns.isTimestampSchema() && - (ns.getSchema() === SCHEMA.TIMESTAMP_DATE_TIME || - ns.getSchema() === SCHEMA.TIMESTAMP_HTTP_DATE || - ns.getSchema() === SCHEMA.TIMESTAMP_EPOCH_SECONDS) + (ns.getSchema() === (5 satisfies TimestampDateTimeSchema) || + ns.getSchema() === (6 satisfies TimestampHttpDateSchema) || + ns.getSchema() === (7 satisfies TimestampEpochSecondsSchema)) ) { return ns.getSchema() as TimestampDateTimeSchema | TimestampHttpDateSchema | TimestampEpochSecondsSchema; } @@ -32,9 +31,9 @@ export function determineTimestampFormat( const bindingFormat = settings.httpBindings ? typeof httpPrefixHeaders === "string" || Boolean(httpHeader) - ? SCHEMA.TIMESTAMP_HTTP_DATE + ? (6 satisfies TimestampHttpDateSchema) : Boolean(httpQuery) || Boolean(httpLabel) - ? SCHEMA.TIMESTAMP_DATE_TIME + ? (5 satisfies TimestampDateTimeSchema) : undefined : undefined; diff --git a/packages/core/src/submodules/schema/index.ts b/packages/core/src/submodules/schema/index.ts index 994b18403a2..5229be011ea 100644 --- a/packages/core/src/submodules/schema/index.ts +++ b/packages/core/src/submodules/schema/index.ts @@ -9,4 +9,5 @@ export * from "./schemas/Schema"; export * from "./schemas/SimpleSchema"; export * from "./schemas/StructureSchema"; export * from "./schemas/sentinels"; +export * from "./schemas/translateTraits"; export * from "./TypeRegistry"; diff --git a/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts b/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts index 36a868faaac..ef8251b70d1 100644 --- a/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts +++ b/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts @@ -1,12 +1,26 @@ -import type { MemberSchema, StructureSchema } from "@smithy/types"; +import type { + BigDecimalSchema, + BigIntegerSchema, + BlobSchema, + BooleanSchema, + DocumentSchema, + ListSchemaModifier, + MapSchemaModifier, + MemberSchema, + NumericSchema, + StreamingBlobSchema, + StringSchema, + StructureSchema, + TimestampDefaultSchema, +} from "@smithy/types"; import { describe, expect, test as it } from "vitest"; import { list } from "./ListSchema"; import { map } from "./MapSchema"; import { NormalizedSchema } from "./NormalizedSchema"; -import { SCHEMA } from "./sentinels"; import { sim } from "./SimpleSchema"; import { struct } from "./StructureSchema"; +import { translateTraits } from "./translateTraits"; describe(NormalizedSchema.name, () => { const [List, Map, Struct] = [list("ack", "List", { sparse: 1 }, 0), map("ack", "Map", 0, 0, 1), () => schema]; @@ -39,31 +53,31 @@ describe(NormalizedSchema.name, () => { }); it("translates a bitvector of traits to a traits object", () => { - expect(NormalizedSchema.translateTraits(0b0000_0000)).toEqual({}); - expect(NormalizedSchema.translateTraits(0b0000_0001)).toEqual({ + expect(translateTraits(0b0000_0000)).toEqual({}); + expect(translateTraits(0b0000_0001)).toEqual({ httpLabel: 1, }); - expect(NormalizedSchema.translateTraits(0b0000_0011)).toEqual({ + expect(translateTraits(0b0000_0011)).toEqual({ httpLabel: 1, idempotent: 1, }); - expect(NormalizedSchema.translateTraits(0b0000_0110)).toEqual({ + expect(translateTraits(0b0000_0110)).toEqual({ idempotent: 1, idempotencyToken: 1, }); - expect(NormalizedSchema.translateTraits(0b0000_1100)).toEqual({ + expect(translateTraits(0b0000_1100)).toEqual({ idempotencyToken: 1, sensitive: 1, }); - expect(NormalizedSchema.translateTraits(0b0001_1000)).toEqual({ + expect(translateTraits(0b0001_1000)).toEqual({ sensitive: 1, httpPayload: 1, }); - expect(NormalizedSchema.translateTraits(0b0011_0000)).toEqual({ + expect(translateTraits(0b0011_0000)).toEqual({ httpPayload: 1, httpResponseCode: 1, }); - expect(NormalizedSchema.translateTraits(0b0110_0000)).toEqual({ + expect(translateTraits(0b0110_0000)).toEqual({ httpResponseCode: 1, httpQueryParams: 1, }); @@ -79,39 +93,37 @@ describe(NormalizedSchema.name, () => { expect(member.getSchema()).toBe(List); expect(member.getMemberName()).toBe("list"); }); - - it("throws when treating a non-member schema as a member schema", () => { - expect(() => { - ns.getMemberName(); - }).toThrow(); - }); }); describe("traversal and type identifiers", () => { it("type identifiers", () => { expect(NormalizedSchema.of("unit").isUnitSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.LIST_MODIFIER | SCHEMA.NUMERIC).isListSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.MAP_MODIFIER | SCHEMA.NUMERIC).isMapSchema()).toBe(true); + expect(NormalizedSchema.of((64 satisfies ListSchemaModifier) | (1 satisfies NumericSchema)).isListSchema()).toBe( + true + ); + expect(NormalizedSchema.of((128 satisfies MapSchemaModifier) | (1 satisfies NumericSchema)).isMapSchema()).toBe( + true + ); - expect(NormalizedSchema.of(SCHEMA.DOCUMENT).isDocumentSchema()).toBe(true); + expect(NormalizedSchema.of(15 satisfies DocumentSchema).isDocumentSchema()).toBe(true); expect(NormalizedSchema.of(ns.getMemberSchema("struct")).isStructSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.BLOB).isBlobSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.TIMESTAMP_DEFAULT).isTimestampSchema()).toBe(true); + expect(NormalizedSchema.of(21 satisfies BlobSchema).isBlobSchema()).toBe(true); + expect(NormalizedSchema.of(4 satisfies TimestampDefaultSchema).isTimestampSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.STRING).isStringSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.BOOLEAN).isBooleanSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.NUMERIC).isNumericSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.BIG_INTEGER).isBigIntegerSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.BIG_DECIMAL).isBigDecimalSchema()).toBe(true); - expect(NormalizedSchema.of(SCHEMA.STREAMING_BLOB).isStreaming()).toBe(true); + expect(NormalizedSchema.of(0 satisfies StringSchema).isStringSchema()).toBe(true); + expect(NormalizedSchema.of(2 satisfies BooleanSchema).isBooleanSchema()).toBe(true); + expect(NormalizedSchema.of(1 satisfies NumericSchema).isNumericSchema()).toBe(true); + expect(NormalizedSchema.of(17 satisfies BigIntegerSchema).isBigIntegerSchema()).toBe(true); + expect(NormalizedSchema.of(19 satisfies BigDecimalSchema).isBigDecimalSchema()).toBe(true); + expect(NormalizedSchema.of(42 satisfies StreamingBlobSchema).isStreaming()).toBe(true); const structWithStreamingMember = struct( "ack", "StructWithStreamingMember", 0, ["m"], - [sim("ns", "blob", SCHEMA.BLOB, { streaming: 1 })] + [sim("ns", "blob", 21 as BlobSchema, { streaming: 1 })] ); expect(NormalizedSchema.of(structWithStreamingMember).getMemberSchema("m").isStreaming()).toBe(true); }); @@ -149,6 +161,11 @@ describe(NormalizedSchema.name, () => { expect(member.getSchema()).toBe(0); expect(member.getMemberName()).toBe("key"); }); + it("should return a defined key schema even if the map was defined by a numeric sentinel value", () => { + const map = NormalizedSchema.of((128 satisfies MapSchemaModifier) | (1 satisfies NumericSchema)); + expect(map.getKeySchema().isStringSchema()).toBe(true); + expect(map.getValueSchema().isNumericSchema()).toBe(true); + }); it("map value member", () => { const member = ns.getMemberSchema("map").getValueSchema(); expect(member.isMemberSchema()).toBe(true); @@ -181,15 +198,6 @@ describe(NormalizedSchema.name, () => { }); }); - it("can identify whether a member exists", () => { - expect(ns.hasMemberSchema("list")).toBe(true); - expect(ns.hasMemberSchema("map")).toBe(true); - expect(ns.hasMemberSchema("struct")).toBe(true); - expect(ns.hasMemberSchema("a")).toBe(false); - expect(ns.hasMemberSchema("b")).toBe(false); - expect(ns.hasMemberSchema("c")).toBe(false); - }); - describe("iteration", () => { it("iterates over member schemas", () => { const iteration = Array.from(ns.structIterator()) as [string, NormalizedSchema][]; diff --git a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts index 55887721d47..83b187b931d 100644 --- a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts +++ b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts @@ -1,19 +1,33 @@ import type { + BigDecimalSchema, + BigIntegerSchema, + BlobSchema, + BooleanSchema, + DocumentSchema, + ListSchemaModifier, + MapSchemaModifier, MemberSchema, NormalizedSchema as INormalizedSchema, + NumericSchema, Schema as ISchema, SchemaRef, SchemaTraits, SchemaTraitsObject, + StreamingBlobSchema, + StringSchema, + TimestampDefaultSchema, + TimestampEpochSecondsSchema, + UnitSchema, } from "@smithy/types"; +import type { IdempotencyTokenBitMask, TraitBitVector } from "@smithy/types/src/schema/traits"; import { deref } from "../deref"; import { ListSchema } from "./ListSchema"; import { MapSchema } from "./MapSchema"; import { Schema } from "./Schema"; -import { SCHEMA } from "./sentinels"; -import { SimpleSchema } from "./SimpleSchema"; +import type { SimpleSchema } from "./SimpleSchema"; import { StructureSchema } from "./StructureSchema"; +import { translateTraits } from "./translateTraits"; /** * Wraps both class instances, numeric sentinel values, and member schema pairs. @@ -22,6 +36,13 @@ import { StructureSchema } from "./StructureSchema"; * @alpha */ export class NormalizedSchema implements INormalizedSchema { + // ======================== + // + // This class implementation may be a little bit code-golfed to save space. + // This class is core to all clients in schema-serde mode. + // For readability, add comments rather than code. + // + // ======================== public static readonly symbol = Symbol.for("@smithy/nor"); protected readonly symbol = NormalizedSchema.symbol; @@ -39,7 +60,7 @@ export class NormalizedSchema implements INormalizedSchema { */ private constructor( readonly ref: SchemaRef, - private memberName?: string + private readonly memberName?: string ) { const traitStack = [] as SchemaTraits[]; let _ref = ref; @@ -57,7 +78,7 @@ export class NormalizedSchema implements INormalizedSchema { this.memberTraits = {}; for (let i = traitStack.length - 1; i >= 0; --i) { const traitSet = traitStack[i]; - Object.assign(this.memberTraits, NormalizedSchema.translateTraits(traitSet)); + Object.assign(this.memberTraits, translateTraits(traitSet)); } } else { this.memberTraits = 0; @@ -80,8 +101,7 @@ export class NormalizedSchema implements INormalizedSchema { this.traits = 0; } - this.name = - (this.schema instanceof Schema ? this.schema.getName?.() : void 0) ?? this.memberName ?? this.getSchemaName(); + this.name = (this.schema instanceof Schema ? this.schema.getName?.() : void 0) ?? this.memberName ?? String(schema); if (this._isMemberSchema && !memberName) { throw new Error(`@smithy/core/schema - NormalizedSchema member init ${this.getName(true)} missing member name.`); @@ -96,61 +116,31 @@ export class NormalizedSchema implements INormalizedSchema { * Static constructor that attempts to avoid wrapping a NormalizedSchema within another. */ public static of(ref: SchemaRef): NormalizedSchema { - if (ref instanceof NormalizedSchema) { - return ref; + const sc = deref(ref); + if (sc instanceof NormalizedSchema) { + return sc; } - if (Array.isArray(ref)) { - const [ns, traits] = ref; + if (Array.isArray(sc)) { + const [ns, traits] = sc; if (ns instanceof NormalizedSchema) { - Object.assign(ns.getMergedTraits(), NormalizedSchema.translateTraits(traits)); + Object.assign(ns.getMergedTraits(), translateTraits(traits)); return ns; } // An aggregate schema must be initialized with members and the member retrieved through the aggregate // container. throw new Error(`@smithy/core/schema - may not init unwrapped member schema=${JSON.stringify(ref, null, 2)}.`); } - return new NormalizedSchema(ref); - } - - /** - * @param indicator - numeric indicator for preset trait combination. - * @returns equivalent trait object. - */ - public static translateTraits(indicator: SchemaTraits): SchemaTraitsObject { - if (typeof indicator === "object") { - return indicator; - } - indicator = indicator | 0; - const traits = {} as SchemaTraitsObject; - let i = 0; - for (const trait of [ - "httpLabel", - "idempotent", - "idempotencyToken", - "sensitive", - "httpPayload", - "httpResponseCode", - "httpQueryParams", - ] as Array) { - if (((indicator >> i++) & 1) === 1) { - traits[trait] = 1; - } - } - return traits; + return new NormalizedSchema(sc); } /** * @returns the underlying non-normalized schema. */ public getSchema(): Exclude { - if (this.schema instanceof NormalizedSchema) { - Object.assign(this, { schema: this.schema.getSchema() }); - return this.schema; - } - if (this.schema instanceof SimpleSchema) { - return deref(this.schema.schemaRef) as Exclude; - } - return deref(this.schema) as Exclude; + return deref((this.schema as SimpleSchema)?.schemaRef ?? this.schema) as Exclude< + ISchema, + MemberSchema | INormalizedSchema + >; } /** @@ -158,23 +148,16 @@ export class NormalizedSchema implements INormalizedSchema { * @returns e.g. `MyShape` or `com.namespace#MyShape`. */ public getName(withNamespace = false): string | undefined { - if (!withNamespace) { - if (this.name && this.name.includes("#")) { - return this.name.split("#")[1]; - } - } + const { name } = this; + const short = !withNamespace && name && name.includes("#"); // empty name should return as undefined - return this.name || undefined; + return short ? name.split("#")[1] : name || undefined; } /** * @returns the member name if the schema is a member schema. - * @throws Error when the schema isn't a member schema. */ public getMemberName(): string { - if (!this.isMemberSchema()) { - throw new Error(`@smithy/core/schema - non-member schema: ${this.getName(true)}`); - } return this.memberName!; } @@ -182,73 +165,73 @@ export class NormalizedSchema implements INormalizedSchema { return this._isMemberSchema; } - public isUnitSchema(): boolean { - return this.getSchema() === ("unit" as const); - } - /** * boolean methods on this class help control flow in shape serialization and deserialization. */ public isListSchema(): boolean { - const inner = this.getSchema(); - if (typeof inner === "number") { - return inner >= SCHEMA.LIST_MODIFIER && inner < SCHEMA.MAP_MODIFIER; - } - return inner instanceof ListSchema; + const sc = this.getSchema(); + return typeof sc === "number" + ? sc >= (64 satisfies ListSchemaModifier) && sc < (128 satisfies MapSchemaModifier) + : sc instanceof ListSchema; } public isMapSchema(): boolean { - const inner = this.getSchema(); - if (typeof inner === "number") { - return inner >= SCHEMA.MAP_MODIFIER && inner <= 0b1111_1111; - } - return inner instanceof MapSchema; + const sc = this.getSchema(); + return typeof sc === "number" + ? sc >= (128 satisfies MapSchemaModifier) && sc <= 0b1111_1111 + : sc instanceof MapSchema; } public isStructSchema(): boolean { - const inner = this.getSchema(); - return (inner !== null && typeof inner === "object" && "members" in inner) || inner instanceof StructureSchema; + const sc = this.getSchema(); + return (sc !== null && typeof sc === "object" && "members" in sc) || sc instanceof StructureSchema; } public isBlobSchema(): boolean { - return this.getSchema() === SCHEMA.BLOB || this.getSchema() === SCHEMA.STREAMING_BLOB; + const sc = this.getSchema(); + return sc === (21 satisfies BlobSchema) || sc === (42 satisfies StreamingBlobSchema); } public isTimestampSchema(): boolean { - const schema = this.getSchema(); - return typeof schema === "number" && schema >= SCHEMA.TIMESTAMP_DEFAULT && schema <= SCHEMA.TIMESTAMP_EPOCH_SECONDS; + const sc = this.getSchema(); + return ( + typeof sc === "number" && + sc >= (4 satisfies TimestampDefaultSchema) && + sc <= (7 satisfies TimestampEpochSecondsSchema) + ); + } + + public isUnitSchema(): boolean { + return this.getSchema() === ("unit" satisfies UnitSchema); } public isDocumentSchema(): boolean { - return this.getSchema() === SCHEMA.DOCUMENT; + return this.getSchema() === (15 satisfies DocumentSchema); } public isStringSchema(): boolean { - return this.getSchema() === SCHEMA.STRING; + return this.getSchema() === (0 satisfies StringSchema); } public isBooleanSchema(): boolean { - return this.getSchema() === SCHEMA.BOOLEAN; + return this.getSchema() === (2 satisfies BooleanSchema); } public isNumericSchema(): boolean { - return this.getSchema() === SCHEMA.NUMERIC; + return this.getSchema() === (1 satisfies NumericSchema); } public isBigIntegerSchema(): boolean { - return this.getSchema() === SCHEMA.BIG_INTEGER; + return this.getSchema() === (17 satisfies BigIntegerSchema); } public isBigDecimalSchema(): boolean { - return this.getSchema() === SCHEMA.BIG_DECIMAL; + return this.getSchema() === (19 satisfies BigDecimalSchema); } public isStreaming(): boolean { - const streaming = !!this.getMergedTraits().streaming; - if (streaming) { - return true; - } - return this.getSchema() === SCHEMA.STREAMING_BLOB; + const { streaming } = this.getMergedTraits(); + return !!streaming || this.getSchema() === (42 satisfies StreamingBlobSchema); } /** @@ -256,21 +239,14 @@ export class NormalizedSchema implements INormalizedSchema { * @returns whether the schema has the idempotencyToken trait. */ public isIdempotencyToken(): boolean { - if (this.normalizedTraits) { - return !!this.normalizedTraits.idempotencyToken; - } - for (const traits of [this.traits, this.memberTraits]) { - if (typeof traits === "number") { - if ((traits & 0b0100) === 0b0100) { - return true; - } - } else if (typeof traits === "object") { - if (!!traits.idempotencyToken) { - return true; - } - } - } - return false; + // it is ok to perform the & operation on a trait object, + // since its int32 representation is 0. + const match = (traits?: SchemaTraits) => + ((traits as TraitBitVector) & (0b0100 satisfies IdempotencyTokenBitMask)) === 0b0100 || + !!(traits as SchemaTraitsObject)?.idempotencyToken; + + const { normalizedTraits, traits, memberTraits } = this; + return match(normalizedTraits) || match(traits) || match(memberTraits); } /** @@ -291,7 +267,7 @@ export class NormalizedSchema implements INormalizedSchema { * @returns only the member traits. If the schema is not a member, this returns empty. */ public getMemberTraits(): SchemaTraitsObject { - return NormalizedSchema.translateTraits(this.memberTraits); + return translateTraits(this.memberTraits); } /** @@ -299,7 +275,7 @@ export class NormalizedSchema implements INormalizedSchema { * If there are any member traits they are excluded. */ public getOwnTraits(): SchemaTraitsObject { - return NormalizedSchema.translateTraits(this.traits); + return translateTraits(this.traits); } /** @@ -308,17 +284,15 @@ export class NormalizedSchema implements INormalizedSchema { * @throws Error if the schema is not a Map or Document. */ public getKeySchema(): NormalizedSchema { - if (this.isDocumentSchema()) { - return this.memberFrom([SCHEMA.DOCUMENT, 0], "key"); - } - if (!this.isMapSchema()) { + const [isDoc, isMap] = [this.isDocumentSchema(), this.isMapSchema()]; + if (!isDoc && !isMap) { throw new Error(`@smithy/core/schema - cannot get key for non-map: ${this.getName(true)}`); } const schema = this.getSchema(); - if (typeof schema === "number") { - return this.memberFrom([0b0011_1111 & schema, 0], "key"); - } - return this.memberFrom([(schema as MapSchema).keySchema, 0], "key"); + const memberSchema = isDoc + ? (15 satisfies DocumentSchema) + : (schema as MapSchema)?.keySchema ?? (0 satisfies StringSchema); + return member([memberSchema, 0], "key"); } /** @@ -328,71 +302,41 @@ export class NormalizedSchema implements INormalizedSchema { * @throws Error if the schema is not a Map, List, nor Document. */ public getValueSchema(): NormalizedSchema { - const schema = this.getSchema(); - - if (typeof schema === "number") { - if (this.isMapSchema()) { - return this.memberFrom([0b0011_1111 & schema, 0], "value"); - } else if (this.isListSchema()) { - return this.memberFrom([0b0011_1111 & schema, 0], "member"); - } - } - - if (schema && typeof schema === "object") { - if (this.isStructSchema()) { - throw new Error(`may not getValueSchema() on structure ${this.getName(true)}`); - } - const collection = schema as MapSchema | ListSchema; - if ("valueSchema" in collection) { - if (this.isMapSchema()) { - return this.memberFrom([collection.valueSchema, 0], "value"); - } else if (this.isListSchema()) { - return this.memberFrom([collection.valueSchema, 0], "member"); - } - } - } - - if (this.isDocumentSchema()) { - return this.memberFrom([SCHEMA.DOCUMENT, 0], "value"); + const sc = this.getSchema(); + const [isDoc, isMap, isList] = [this.isDocumentSchema(), this.isMapSchema(), this.isListSchema()]; + const memberSchema = + typeof sc === "number" + ? 0b0011_1111 & sc + : sc && typeof sc === "object" && (isMap || isList) + ? ((sc as MapSchema | ListSchema).valueSchema as typeof sc) + : isDoc + ? (15 satisfies DocumentSchema) + : void 0; + if (memberSchema != null) { + return member([memberSchema, 0], isMap ? "value" : "member"); } - throw new Error(`@smithy/core/schema - ${this.getName(true)} has no value member.`); } - /** - * @param member - to query. - * @returns whether there is a memberSchema with the given member name. False if not a structure (or union). - */ - public hasMemberSchema(member: string): boolean { - if (this.isStructSchema()) { - const struct = this.getSchema() as StructureSchema; - return struct.memberNames.includes(member); - } - return false; - } - /** * @returns the NormalizedSchema for the given member name. The returned instance will return true for `isMemberSchema()` * and will have the member name given. - * @param member - which member to retrieve and wrap. + * @param memberName - which member to retrieve and wrap. * * @throws Error if member does not exist or the schema is neither a document nor structure. * Note that errors are assumed to be structures and unions are considered structures for these purposes. */ - public getMemberSchema(member: string): NormalizedSchema { - if (this.isStructSchema()) { - const struct = this.getSchema() as StructureSchema; - if (!struct.memberNames.includes(member)) { - throw new Error(`@smithy/core/schema - ${this.getName(true)} has no member=${member}.`); - } - const i = struct.memberNames.indexOf(member); + public getMemberSchema(memberName: string): NormalizedSchema { + const struct = this.getSchema() as StructureSchema; + if (this.isStructSchema() && struct.memberNames.includes(memberName)) { + const i = struct.memberNames.indexOf(memberName); const memberSchema = struct.memberList[i]; - return this.memberFrom(Array.isArray(memberSchema) ? memberSchema : [memberSchema, 0], member); + return member(Array.isArray(memberSchema) ? memberSchema : [memberSchema, 0], memberName); } if (this.isDocumentSchema()) { - return this.memberFrom([SCHEMA.DOCUMENT, 0], member); + return member([15 satisfies DocumentSchema, 0], memberName); } - throw new Error(`@smithy/core/schema - ${this.getName(true)} has no members.`); + throw new Error(`@smithy/core/schema - ${this.getName(true)} has no no member=${memberName}.`); } /** @@ -445,44 +389,23 @@ export class NormalizedSchema implements INormalizedSchema { } const struct = this.getSchema() as StructureSchema; for (let i = 0; i < struct.memberNames.length; ++i) { - yield [struct.memberNames[i], this.memberFrom([struct.memberList[i], 0], struct.memberNames[i])]; + yield [struct.memberNames[i], member([struct.memberList[i], 0], struct.memberNames[i])]; } } +} - /** - * Creates a normalized member schema from the given schema and member name. - */ - private memberFrom(memberSchema: NormalizedSchema | [SchemaRef, SchemaTraits], memberName: string): NormalizedSchema { - if (memberSchema instanceof NormalizedSchema) { - return Object.assign(memberSchema, { - memberName, - _isMemberSchema: true, - }); - } - return new NormalizedSchema(memberSchema, memberName); - } - - /** - * @returns a last-resort human-readable name for the schema if it has no other identifiers. - */ - private getSchemaName(): string { - const schema = this.getSchema(); - if (typeof schema === "number") { - const _schema = 0b0011_1111 & schema; - const container = 0b1100_0000 & schema; - const type = - Object.entries(SCHEMA).find(([, value]) => { - return value === _schema; - })?.[0] ?? "Unknown"; - switch (container) { - case SCHEMA.MAP_MODIFIER: - return `${type}Map`; - case SCHEMA.LIST_MODIFIER: - return `${type}List`; - case 0: - return type; - } - } - return "Unknown"; - } +/** + * Creates a normalized member schema from the given schema and member name. + * + * @internal + */ +function member(memberSchema: NormalizedSchema | [SchemaRef, SchemaTraits], memberName: string): NormalizedSchema { + if (memberSchema instanceof NormalizedSchema) { + return Object.assign(memberSchema, { + memberName, + _isMemberSchema: true, + }); + } + const internalCtorAccess = NormalizedSchema as any; + return new internalCtorAccess(memberSchema, memberName); } diff --git a/packages/core/src/submodules/schema/schemas/schemas.spec.ts b/packages/core/src/submodules/schema/schemas/schemas.spec.ts index b6e13c448e3..81422f9f34f 100644 --- a/packages/core/src/submodules/schema/schemas/schemas.spec.ts +++ b/packages/core/src/submodules/schema/schemas/schemas.spec.ts @@ -7,32 +7,10 @@ import { list, ListSchema } from "./ListSchema"; import { map, MapSchema } from "./MapSchema"; import { op, OperationSchema } from "./OperationSchema"; import { Schema } from "./Schema"; -import { SCHEMA } from "./sentinels"; import { sim, SimpleSchema } from "./SimpleSchema"; import { struct, StructureSchema } from "./StructureSchema"; describe("schemas", () => { - describe("sentinels", () => { - it("should be constant", () => { - expect(SCHEMA).toEqual({ - BLOB: 0b0001_0101, // 21 - STREAMING_BLOB: 0b0010_1010, // 42 - BOOLEAN: 0b0000_0010, // 2 - STRING: 0b0000_0000, // 0 - NUMERIC: 0b0000_0001, // 1 - BIG_INTEGER: 0b0001_0001, // 17 - BIG_DECIMAL: 0b0001_0011, // 19 - DOCUMENT: 0b0000_1111, // 15 - TIMESTAMP_DEFAULT: 0b0000_0100, // 4 - TIMESTAMP_DATE_TIME: 0b0000_0101, // 5 - TIMESTAMP_HTTP_DATE: 0b0000_0110, // 6 - TIMESTAMP_EPOCH_SECONDS: 0b0000_0111, // 7 - LIST_MODIFIER: 0b0100_0000, // 64 - MAP_MODIFIER: 0b1000_0000, // 128 - }); - }); - }); - describe(ErrorSchema.name, () => { const schema = error("ack", "Error", 0, [], []); diff --git a/packages/core/src/submodules/schema/schemas/sentinels.ts b/packages/core/src/submodules/schema/schemas/sentinels.ts index 2a2580df628..da7702ac24d 100644 --- a/packages/core/src/submodules/schema/schemas/sentinels.ts +++ b/packages/core/src/submodules/schema/schemas/sentinels.ts @@ -18,6 +18,8 @@ import type { /** * Schema sentinel runtime values. * @alpha + * + * @deprecated use inline numbers with type annotation to save space. */ export const SCHEMA: { BLOB: BlobSchema; diff --git a/packages/core/src/submodules/schema/schemas/translateTraits.ts b/packages/core/src/submodules/schema/schemas/translateTraits.ts new file mode 100644 index 00000000000..3e2d10159d9 --- /dev/null +++ b/packages/core/src/submodules/schema/schemas/translateTraits.ts @@ -0,0 +1,29 @@ +import type { SchemaTraits, SchemaTraitsObject } from "@smithy/types"; + +/** + * @internal + * @param indicator - numeric indicator for preset trait combination. + * @returns equivalent trait object. + */ +export function translateTraits(indicator: SchemaTraits): SchemaTraitsObject { + if (typeof indicator === "object") { + return indicator; + } + indicator = indicator | 0; + const traits = {} as SchemaTraitsObject; + let i = 0; + for (const trait of [ + "httpLabel", + "idempotent", + "idempotencyToken", + "sensitive", + "httpPayload", + "httpResponseCode", + "httpQueryParams", + ] as Array) { + if (((indicator >> i++) & 1) === 1) { + traits[trait] = 1; + } + } + return traits; +} diff --git a/packages/core/src/submodules/serde/index.ts b/packages/core/src/submodules/serde/index.ts index b6e01240a11..421ec8ea159 100644 --- a/packages/core/src/submodules/serde/index.ts +++ b/packages/core/src/submodules/serde/index.ts @@ -4,6 +4,7 @@ export * from "./generateIdempotencyToken"; export * from "./lazy-json"; export * from "./parse-utils"; export * from "./quote-header"; +export * from "./schema-serde-lib/schema-date-utils"; export * from "./split-every"; export * from "./split-header"; export * from "./value/NumericValue"; diff --git a/packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.spec.ts b/packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.spec.ts new file mode 100644 index 00000000000..902fe680d80 --- /dev/null +++ b/packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.spec.ts @@ -0,0 +1,421 @@ +import { describe, expect, it } from "vitest"; + +import { _parseEpochTimestamp, _parseRfc3339DateTimeWithOffset, _parseRfc7231DateTime } from "./schema-date-utils"; + +const millisecond = 1; +const second = 1000 * millisecond; +const minute = 60 * second; +const hour = 60 * minute; +const day = 24 * hour; +const year = 365 * day; + +describe("_parseEpochTimestamp", () => { + it("should parse numeric timestamps", () => { + expect(_parseEpochTimestamp(1234567890)).toEqual(new Date(1234567890000)); + expect(_parseEpochTimestamp(1234567890.123)).toEqual(new Date(1234567890123)); + expect(_parseEpochTimestamp(1234567890.123456)).toEqual(new Date(1234567890123)); + }); + + it("should parse string timestamps", () => { + expect(_parseEpochTimestamp("1234567890")).toEqual(new Date(1234567890000)); + expect(_parseEpochTimestamp("1234567890.123")).toEqual(new Date(1234567890123)); + expect(_parseEpochTimestamp("1234567890.123456")).toEqual(new Date(1234567890123)); + }); + + it("should parse CBOR tag timestamps", () => { + expect(_parseEpochTimestamp({ tag: 1, value: 1234567890 })).toEqual(new Date(1234567890000)); + expect(_parseEpochTimestamp({ tag: 1, value: 1234567890.123 })).toEqual(new Date(1234567890123)); + expect(_parseEpochTimestamp({ tag: 1, value: 1234567890.123456 })).toEqual(new Date(1234567890123)); + }); + + it("should return undefined for null/undefined", () => { + expect(_parseEpochTimestamp(null)).toBeUndefined(); + expect(_parseEpochTimestamp(undefined)).toBeUndefined(); + }); + + it("should throw for invalid numbers", () => { + expect(() => _parseEpochTimestamp("abc")).toThrow(); + expect(() => _parseEpochTimestamp(Infinity)).toThrow(); + expect(() => _parseEpochTimestamp(NaN)).toThrow(); + }); +}); + +describe("_parseRfc3339DateTimeWithOffset", () => { + it("should parse UTC timestamps", () => { + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T00:00:00Z")).toEqual(new Date(3407616000_000)); + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T12:12:12.01Z")).toEqual( + new Date(3407616000_000 + 12 * hour + 12 * minute + 12 * second + 10 * millisecond) + ); + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T23:59:59.999Z")).toEqual( + new Date(3407616000_000 + 23 * hour + 59 * minute + 59 * second + 999 * millisecond) + ); + }); + + it("should parse timestamps with positive offset", () => { + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T00:00:00-04:30")).toEqual(new Date(3407616000_000 + 4.5 * hour)); + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T12:12:12.01-04:30")).toEqual( + new Date(3407616000_000 + 12 * hour + 12 * minute + 12 * second + 10 * millisecond + 4.5 * hour) + ); + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T23:59:59.999-04:30")).toEqual( + new Date(3407616000_000 + 23 * hour + 59 * minute + 59 * second + 999 * millisecond + 4.5 * hour) + ); + }); + + it("should parse timestamps with negative offset", () => { + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T00:00:00+05:00")).toEqual(new Date(3407616000_000 - 5 * hour)); + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T12:12:12.01+05:00")).toEqual( + new Date(3407616000_000 + 12 * hour + 12 * minute + 12 * second + 10 * millisecond - 5 * hour) + ); + expect(_parseRfc3339DateTimeWithOffset("2077-12-25T23:59:59.999+05:00")).toEqual( + new Date(3407616000_000 + 23 * hour + 59 * minute + 59 * second + 999 * millisecond - 5 * hour) + ); + }); + + it("should parse timestamps with fractional seconds", () => { + expect(_parseRfc3339DateTimeWithOffset("2023-12-25T12:00:00.123Z")).toEqual(new Date("2023-12-25T12:00:00.123Z")); + }); + + it("should return undefined for null/undefined", () => { + expect(_parseRfc3339DateTimeWithOffset(null)).toBeUndefined(); + expect(_parseRfc3339DateTimeWithOffset(undefined)).toBeUndefined(); + }); + + it("should throw for invalid formats", () => { + expect(() => _parseRfc3339DateTimeWithOffset("2023-12-25")).toThrow(); + expect(() => _parseRfc3339DateTimeWithOffset(123)).toThrow(); + }); +}); + +describe("_parseRfc7231DateTime", () => { + it("should parse RFC7231 timestamps", () => { + expect(_parseRfc7231DateTime("Mon, 31 Dec 2077 23:59:30 GMT")).toEqual(new Date(3408220800000 - 30 * second)); + }); + + it("should parse timestamps with fractional seconds", () => { + expect(_parseRfc7231DateTime("Mon, 31 Dec 2077 23:59:30.123 GMT")).toEqual( + new Date(3408220800000 - 29 * second - 877 * millisecond) + ); + }); + + it("should parse RFC850 timestamps", () => { + expect(_parseRfc7231DateTime("Monday, 31-Dec-77 23:59:30 GMT")).toEqual( + new Date(3408220800000 - 100 * year - 25 * day - 30 * second) + ); + }); + + it("should parse asctime timestamps", () => { + expect(_parseRfc7231DateTime("Mon Dec 31 23:59:30 2077")).toEqual(new Date(3408220800000 - 30 * second)); + }); + + it("should return undefined for null/undefined", () => { + expect(_parseRfc7231DateTime(null)).toBeUndefined(); + expect(_parseRfc7231DateTime(undefined)).toBeUndefined(); + }); + + it("should throw for invalid formats", () => { + expect(() => _parseRfc7231DateTime("2077-12-25T08:49:37Z")).toThrow(); + expect(() => _parseRfc7231DateTime(123)).toThrow(); + expect(() => _parseRfc7231DateTime("Invalid, 25 Dec 2077 08:49:37 GMT")).toThrow(); + }); +}); + +// some invalid values are not validated client side +// because of excessive code requirements. +const invalidRfc3339DateTimes = [ + "85-04-12T23:20:50.52Z", // Year must be 4 digits + "985-04-12T23:20:50.52Z", // Year must be 4 digits + "1985-13-12T23:20:50.52Z", // Month cannot be greater than 12 + "1985-00-12T23:20:50.52Z", // Month cannot be 0 + "1985-4-12T23:20:50.52Z", // Month must be 2 digits with leading zero + "1985-04-32T23:20:50.52Z", // Day cannot be greater than 31 + "1985-04-00T23:20:50.52Z", // Day cannot be 0 + "1985-04-05T24:20:50.52Z", // Hours cannot be greater than 23 + "1985-04-05T23:61:50.52Z", // Minutes cannot be greater than 59 + "1985-04-05T23:20:61.52Z", // Seconds cannot be greater than 59 (except leap second) + // "1985-04-31T23:20:50.52Z", // April only has 30 days + // "2005-02-29T15:59:59Z", // 2005 was not a leap year, so February only had 28 days + "1996-12-19T16:39:57", // Missing timezone offset + "Mon, 31 Dec 1990 15:59:60 GMT", // RFC 7231 format, not RFC 3339 + "Monday, 31-Dec-90 15:59:60 GMT", // RFC 7231 format, not RFC 3339 + "Mon Dec 31 15:59:60 1990", // RFC 7231 format, not RFC 3339 + "1985-04-12T23:20:50.52Z1985-04-12T23:20:50.52Z", // Contains multiple timestamps + "1985-04-12T23:20:50.52ZA", // Contains invalid characters after timezone + "A1985-04-12T23:20:50.52Z", // Contains invalid characters before timestamp +]; + +describe("parseRfc3339DateTime", () => { + it.each([null, undefined])("returns undefined for %s", (value) => { + expect(_parseRfc3339DateTimeWithOffset(value)).toBeUndefined(); + }); + + describe("parses properly formatted dates", () => { + it("with fractional seconds", () => { + expect(_parseRfc3339DateTimeWithOffset("1985-04-12T23:20:50.52Z")).toEqual( + new Date(Date.UTC(1985, 3, 12, 23, 20, 50, 520)) + ); + }); + it("without fractional seconds", () => { + expect(_parseRfc3339DateTimeWithOffset("1985-04-12T23:20:50Z")).toEqual( + new Date(Date.UTC(1985, 3, 12, 23, 20, 50, 0)) + ); + }); + it("with leap seconds", () => { + expect(_parseRfc3339DateTimeWithOffset("1990-12-31T15:59:60Z")).toEqual( + new Date(Date.UTC(1990, 11, 31, 15, 59, 60, 0)) + ); + }); + it("with leap days", () => { + expect(_parseRfc3339DateTimeWithOffset("2004-02-29T15:59:59Z")).toEqual( + new Date(Date.UTC(2004, 1, 29, 15, 59, 59, 0)) + ); + }); + it("with leading zeroes", () => { + expect(_parseRfc3339DateTimeWithOffset("0004-02-09T05:09:09.09Z")).toEqual(new Date(-62037600650910)); + expect(_parseRfc3339DateTimeWithOffset("0004-02-09T00:00:00.00Z")).toEqual(new Date(-62037619200000)); + }); + }); + + it.each(invalidRfc3339DateTimes)("rejects %s", (value) => { + expect(() => _parseRfc3339DateTimeWithOffset(value)).toThrowError(); + }); +}); + +describe("parseRfc3339DateTimeWithOffset", () => { + it.each([null, undefined])("returns undefined for %s", (value) => { + expect(_parseRfc3339DateTimeWithOffset(value)).toBeUndefined(); + }); + + describe("parses properly formatted dates", () => { + it("with fractional seconds", () => { + expect(_parseRfc3339DateTimeWithOffset("1985-04-12T23:20:50.52Z")).toEqual( + new Date(Date.UTC(1985, 3, 12, 23, 20, 50, 520)) + ); + }); + it("without fractional seconds", () => { + expect(_parseRfc3339DateTimeWithOffset("1985-04-12T23:20:50Z")).toEqual( + new Date(Date.UTC(1985, 3, 12, 23, 20, 50, 0)) + ); + }); + it("with leap seconds", () => { + expect(_parseRfc3339DateTimeWithOffset("1990-12-31T15:59:60Z")).toEqual( + new Date(Date.UTC(1990, 11, 31, 15, 59, 60, 0)) + ); + }); + it("with leap days", () => { + expect(_parseRfc3339DateTimeWithOffset("2004-02-29T15:59:59Z")).toEqual( + new Date(Date.UTC(2004, 1, 29, 15, 59, 59, 0)) + ); + }); + it("with leading zeroes", () => { + expect(_parseRfc3339DateTimeWithOffset("0104-02-09T05:09:09.09Z")).toEqual( + new Date(Date.UTC(104, 1, 9, 5, 9, 9, 90)) + ); + expect(_parseRfc3339DateTimeWithOffset("0104-02-09T00:00:00.00Z")).toEqual( + new Date(Date.UTC(104, 1, 9, 0, 0, 0, 0)) + ); + }); + it("with negative offset", () => { + expect(_parseRfc3339DateTimeWithOffset("2019-12-16T22:48:18-01:02")).toEqual( + new Date(Date.UTC(2019, 11, 16, 23, 50, 18, 0)) + ); + }); + it("with positive offset", () => { + expect(_parseRfc3339DateTimeWithOffset("2019-12-16T22:48:18+02:04")).toEqual( + new Date(Date.UTC(2019, 11, 16, 20, 44, 18, 0)) + ); + }); + }); + + it.each(invalidRfc3339DateTimes)("rejects %s", (value) => { + expect(() => _parseRfc3339DateTimeWithOffset(value)).toThrowError(); + }); +}); + +describe("_parseRfc7231DateTime", () => { + it.each([null, undefined])("returns undefined for %s", (value) => { + expect(_parseRfc7231DateTime(value)).toBeUndefined(); + }); + + describe("parses properly formatted dates", () => { + describe("with fractional seconds", () => { + it.each([ + ["imf-fixdate", "Sun, 06 Nov 1994 08:49:37.52 GMT"], + ["rfc-850", "Sunday, 06-Nov-94 08:49:37.52 GMT"], + ["asctime", "Sun Nov 6 08:49:37.52 1994"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(1994, 10, 6, 8, 49, 37, 520))); + }); + }); + describe("with fractional seconds - single digit hour", () => { + it.each([ + ["imf-fixdate", "Sun, 06 Nov 1994 8:49:37.52 GMT"], + ["rfc-850", "Sunday, 06-Nov-94 8:49:37.52 GMT"], + ["asctime", "Sun Nov 6 8:49:37.52 1994"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(1994, 10, 6, 8, 49, 37, 520))); + }); + }); + describe("without fractional seconds", () => { + it.each([ + ["imf-fixdate", "Sun, 06 Nov 1994 08:49:37 GMT"], + ["rfc-850", "Sunday, 06-Nov-94 08:49:37 GMT"], + ["asctime", "Sun Nov 6 08:49:37 1994"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(1994, 10, 6, 8, 49, 37, 0))); + }); + }); + describe("without fractional seconds - single digit hour", () => { + it.each([ + ["imf-fixdate", "Sun, 06 Nov 1994 8:49:37 GMT"], + ["rfc-850", "Sunday, 06-Nov-94 8:49:37 GMT"], + ["asctime", "Sun Nov 6 8:49:37 1994"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(1994, 10, 6, 8, 49, 37, 0))); + }); + }); + describe("with leap seconds", () => { + it.each([ + ["imf-fixdate", "Mon, 31 Dec 1990 15:59:60 GMT"], + ["rfc-850", "Monday, 31-Dec-90 15:59:60 GMT"], + ["asctime", "Mon Dec 31 15:59:60 1990"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(1990, 11, 31, 15, 59, 60, 0))); + }); + }); + describe("with leap seconds - single digit hour", () => { + it.each([ + ["imf-fixdate", "Mon, 31 Dec 1990 8:59:60 GMT"], + ["rfc-850", "Monday, 31-Dec-90 8:59:60 GMT"], + ["asctime", "Mon Dec 31 8:59:60 1990"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(1990, 11, 31, 8, 59, 60, 0))); + }); + }); + describe("with leap days", () => { + it.each([ + ["imf-fixdate", "Sun, 29 Feb 2004 15:59:59 GMT"], + ["asctime", "Sun Feb 29 15:59:59 2004"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(2004, 1, 29, 15, 59, 59, 0))); + }); + }); + describe("with leap days - single digit hour", () => { + it.each([ + ["imf-fixdate", "Sun, 29 Feb 2004 8:59:59 GMT"], + ["asctime", "Sun Feb 29 8:59:59 2004"], + ])("in format %s", (_, value) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(2004, 1, 29, 8, 59, 59, 0))); + }); + }); + describe("with leading zeroes", () => { + it.each([ + ["imf-fixdate", "Sun, 06 Nov 0104 08:09:07.02 GMT", 104], + ["rfc-850", "Sunday, 06-Nov-04 08:09:07.02 GMT", 1904], + ["asctime", "Sun Nov 6 08:09:07.02 0104", 104], + ])("in format %s", (_, value, year) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(year, 10, 6, 8, 9, 7, 20))); + }); + }); + describe("with all-zero components", () => { + it.each([ + ["imf-fixdate", "Sun, 06 Nov 0104 00:00:00.00 GMT", 104], + ["rfc-850", "Sunday, 06-Nov-94 00:00:00.00 GMT", 1994], + ["asctime", "Sun Nov 6 00:00:00.00 0104", 104], + ])("in format %s", (_, value, year) => { + expect(_parseRfc7231DateTime(value)).toEqual(new Date(Date.UTC(year, 10, 6, 0, 0, 0, 0))); + }); + }); + }); + + // note: some edge cases are not handled because the amount of code needed to enforce + // them client-side is excessive. We trust our services' response values. + it.each([ + "1985-04-12T23:20:50.52Z", // RFC 3339 format, not RFC 7231 + "1985-04-12T23:20:50Z", // RFC 3339 format, not RFC 7231 + + "Sun, 06 Nov 0004 08:09:07.02 GMTSun, 06 Nov 0004 08:09:07.02 GMT", // Contains multiple timestamps + "Sun, 06 Nov 0004 08:09:07.02 GMTA", // Contains invalid characters after GMT + "ASun, 06 Nov 0004 08:09:07.02 GMT", // Contains invalid characters before timestamp + "Sun, 06 Nov 94 08:49:37 GMT", // Year must be 4 digits + "Sun, 06 Dov 1994 08:49:37 GMT", // Invalid month name + "Mun, 06 Nov 1994 08:49:37 GMT", // Invalid day name + // "Sunday, 06 Nov 1994 08:49:37 GMT", // Wrong format - uses full day name in IMF-fixdate format + "Sun, 06 November 1994 08:49:37 GMT", // Wrong format - uses full month name + "Sun, 06 Nov 1994 24:49:37 GMT", // Hours cannot be 24 + "Sun, 06 Nov 1994 08:69:37 GMT", // Minutes cannot be > 59 + "Sun, 06 Nov 1994 08:49:67 GMT", // Seconds cannot be > 60 (60 only allowed for leap second) + "Sun, 06-11-1994 08:49:37 GMT", // Wrong date format - uses dashes instead of spaces + "Sun, 06 11 1994 08:49:37 GMT", // Wrong format - uses numeric month instead of abbreviated name + // "Sun, 31 Nov 1994 08:49:37 GMT", // Invalid date - November has 30 days + // "Sun, 29 Feb 2005 15:59:59 GMT", // Invalid date - 2005 was not a leap year + + "Sunday, 06-Nov-04 08:09:07.02 GMTSunday, 06-Nov-04 08:09:07.02 GMT", // Contains multiple timestamps + "ASunday, 06-Nov-04 08:09:07.02 GMT", // Contains invalid characters before timestamp + "Sunday, 06-Nov-04 08:09:07.02 GMTA", // Contains invalid characters after GMT + "Sunday, 06-Nov-1994 08:49:37 GMT", // Wrong format - uses 4 digit year in RFC 850 format + "Sunday, 06-Dov-94 08:49:37 GMT", // Invalid month name + "Sundae, 06-Nov-94 08:49:37 GMT", // Invalid day name + // "Sun, 06-Nov-94 08:49:37 GMT", // Wrong format - uses abbreviated day name in RFC 850 format + "Sunday, 06-November-94 08:49:37 GMT", // Wrong format - uses full month name + "Sunday, 06-Nov-94 24:49:37 GMT", // Hours cannot be 24 + "Sunday, 06-Nov-94 08:69:37 GMT", // Minutes cannot be > 59 + "Sunday, 06-Nov-94 08:49:67 GMT", // Seconds cannot be > 60 (60 only allowed for leap second) + "Sunday, 06 11 94 08:49:37 GMT", // Wrong format - uses spaces instead of dashes + "Sunday, 06-11-1994 08:49:37 GMT", // Wrong format - uses numeric month and 4 digit year + // "Sunday, 31-Nov-94 08:49:37 GMT", // Invalid date - November has 30 days + // "Sunday, 29-Feb-05 15:59:59 GMT", // Invalid date - 2005 was not a leap year + + "Sun Nov 6 08:09:07.02 0004Sun Nov 6 08:09:07.02 0004", // Contains multiple timestamps + "ASun Nov 6 08:09:07.02 0004", // Contains invalid characters before timestamp + "Sun Nov 6 08:09:07.02 0004A", // Contains invalid characters after timestamp + "Sun Nov 6 08:49:37 94", // Year must be 4 digits in asctime format + "Sun Dov 6 08:49:37 1994", // Invalid month name + "Mun Nov 6 08:49:37 1994", // Invalid day name + // "Sunday Nov 6 08:49:37 1994", // Wrong format - uses full day name + "Sun November 6 08:49:37 1994", // Wrong format - uses full month name + "Sun Nov 6 24:49:37 1994", // Hours cannot be 24 + "Sun Nov 6 08:69:37 1994", // Minutes cannot be > 59 + "Sun Nov 6 08:49:67 1994", // Seconds cannot be > 60 (60 only allowed for leap second) + "Sun 06-11 08:49:37 1994", // Wrong format - uses dashes in date + "Sun 06 11 08:49:37 1994", // Wrong format - uses numeric month + "Sun 11 6 08:49:37 1994", // Wrong format - month and day in wrong order + // "Sun Nov 31 08:49:37 1994", // Invalid date - November has 30 days + // "Sun Feb 29 15:59:59 2005", // Invalid date - 2005 was not a leap year + "Sun Nov 6 08:49:37 1994", // Wrong format - missing space after single digit day + ])("rejects %s", (value) => { + expect(() => _parseRfc7231DateTime(value)).toThrowError(); + }); +}); + +describe("_parseEpochTimestamp", () => { + it.each([null, undefined])("returns undefined for %s", (value) => { + expect(_parseEpochTimestamp(value)).toBeUndefined(); + }); + + describe("parses properly formatted dates", () => { + describe("with fractional seconds", () => { + it.each(["482196050.52", 482196050.52])("parses %s", (value) => { + expect(_parseEpochTimestamp(value)).toEqual(new Date(Date.UTC(1985, 3, 12, 23, 20, 50, 520))); + }); + }); + describe("without fractional seconds", () => { + it.each(["482196050", 482196050, 482196050.0])("parses %s", (value) => { + expect(_parseEpochTimestamp(value)).toEqual(new Date(Date.UTC(1985, 3, 12, 23, 20, 50, 0))); + }); + }); + }); + it.each([ + "1985-04-12T23:20:50.52Z", + "1985-04-12T23:20:50Z", + "Mon, 31 Dec 1990 15:59:60 GMT", + "Monday, 31-Dec-90 15:59:60 GMT", + "Mon Dec 31 15:59:60 1990", + "NaN", + NaN, + "Infinity", + Infinity, + "0x42", + ])("rejects %s", (value) => { + expect(() => _parseEpochTimestamp(value)).toThrowError(); + }); +}); diff --git a/packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.ts b/packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.ts new file mode 100644 index 00000000000..a19ddd23c90 --- /dev/null +++ b/packages/core/src/submodules/serde/schema-serde-lib/schema-date-utils.ts @@ -0,0 +1,176 @@ +const ddd = `(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)(?:[ne|u?r]?s?day)?`; +const mmm = `(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)`; +const time = `(\\d?\\d):(\\d{2}):(\\d{2})(?:\\.(\\d+))?`; +const date = `(\\d?\\d)`; +const year = `(\\d{4})`; + +const RFC3339_WITH_OFFSET = new RegExp( + /^(\d{4})-(\d\d)-(\d\d)[tT](\d\d):(\d\d):(\d\d)(\.(\d+))?(([-+]\d\d:\d\d)|[zZ])$/ +); +const IMF_FIXDATE = new RegExp(`^${ddd}, ${date} ${mmm} ${year} ${time} GMT$`); +const RFC_850_DATE = new RegExp(`^${ddd}, ${date}-${mmm}-(\\d\\d) ${time} GMT$`); +const ASC_TIME = new RegExp(`^${ddd} ${mmm} ( [1-9]|\\d\\d) ${time} ${year}$`); + +const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + +/** + * @internal + * + * Parses a value into a Date. Returns undefined if the input is null or + * undefined, throws an error if the input is not a number or a parseable string. + * + * Input strings must be an integer or floating point number. Fractional seconds are supported. + * + * @param value - the value to parse + * @returns a Date or undefined + */ +export const _parseEpochTimestamp = (value: unknown): Date | undefined => { + if (value == null) { + return void 0; + } + let num: number = NaN; + if (typeof value === "number") { + num = value; + } else if (typeof value === "string") { + if (!/^-?\d*\.?\d+$/.test(value)) { + throw new TypeError(`parseEpochTimestamp - numeric string invalid.`); + } + num = Number.parseFloat(value); + } else if (typeof value === "object" && (value as { tag: number; value: number }).tag === 1) { + // timestamp is a CBOR tag type. + num = (value as { tag: number; value: number }).value; + } + if (isNaN(num) || Math.abs(num) === Infinity) { + throw new TypeError("Epoch timestamps must be valid finite numbers."); + } + return new Date(Math.round(num * 1000)); +}; + +/** + * @internal + * + * Parses a value into a Date. Returns undefined if the input is null or + * undefined, throws an error if the input is not a string that can be parsed + * as an RFC 3339 date. + * + * Input strings must conform to RFC3339 section 5.6, and can have a UTC + * offset. Fractional precision is supported. + * + * @see {@link https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14} + * + * @param value - the value to parse + * @returns a Date or undefined + */ +export const _parseRfc3339DateTimeWithOffset = (value: unknown): Date | undefined => { + if (value == null) { + return void 0; + } + if (typeof value !== "string") { + throw new TypeError("RFC3339 timestamps must be strings"); + } + + const matches = RFC3339_WITH_OFFSET.exec(value); + + if (!matches) { + throw new TypeError(`Invalid RFC3339 timestamp format ${value}`); + } + + const [, yearStr, monthStr, dayStr, hours, minutes, seconds, , ms, offsetStr] = matches; + + range(monthStr, 1, 12); + range(dayStr, 1, 31); + range(hours, 0, 23); + range(minutes, 0, 59); + range(seconds, 0, 60); // leap second + + const date = new Date(); + date.setUTCFullYear(Number(yearStr), Number(monthStr) - 1, Number(dayStr)); + date.setUTCHours(Number(hours)); + date.setUTCMinutes(Number(minutes)); + date.setUTCSeconds(Number(seconds)); + date.setUTCMilliseconds(Number(ms) ? Math.round(parseFloat(`0.${ms}`) * 1000) : 0); + + if (offsetStr.toUpperCase() != "Z") { + const [, sign, offsetH, offsetM] = /([+-])(\d\d):(\d\d)/.exec(offsetStr) || [void 0, "+", 0, 0]; + const scalar = sign === "-" ? 1 : -1; + date.setTime(date.getTime() + scalar * (Number(offsetH) * 60 * 60 * 1000 + Number(offsetM) * 60 * 1000)); + } + + return date; +}; + +/** + * @internal + * + * Parses a value into a Date. Returns undefined if the input is null or + * undefined, throws an error if the input is not a string that can be parsed + * as an RFC 7231 date. + * + * Input strings must conform to RFC7231 section 7.1.1.1. Fractional seconds are supported. + * + * RFC 850 and unix asctime formats are also accepted. + * todo: practically speaking, are RFC 850 and asctime even used anymore? + * todo: can we remove those parts? + * + * @see {@link https://datatracker.ietf.org/doc/html/rfc7231.html#section-7.1.1.1} + * + * @param value - the value to parse. + * @returns a Date or undefined. + */ +export const _parseRfc7231DateTime = (value: unknown): Date | undefined => { + if (value == null) { + return void 0; + } + if (typeof value !== "string") { + throw new TypeError("RFC7231 timestamps must be strings."); + } + + let day!: string; + let month!: string; + let year!: string; + let hour!: string; + let minute!: string; + let second!: string; + let fraction!: string; + + let matches: string[] | null; + if ((matches = IMF_FIXDATE.exec(value))) { + // "Mon, 25 Dec 2077 23:59:59 GMT" + [, day, month, year, hour, minute, second, fraction] = matches; + } else if ((matches = RFC_850_DATE.exec(value))) { + // "Monday, 25-Dec-77 23:59:59 GMT" + [, day, month, year, hour, minute, second, fraction] = matches; + year = (Number(year) + 1900).toString(); + } else if ((matches = ASC_TIME.exec(value))) { + // "Mon Dec 25 23:59:59 2077" + [, month, day, hour, minute, second, fraction, year] = matches; + } + + if (year && second) { + const date = new Date(); + date.setUTCFullYear(Number(year)); + date.setUTCMonth(months.indexOf(month)); + range(day, 1, 31); + date.setUTCDate(Number(day)); + range(hour, 0, 23); + date.setUTCHours(Number(hour)); + range(minute, 0, 59); + date.setUTCMinutes(Number(minute)); + range(second, 0, 60); // leap second. + date.setUTCSeconds(Number(second)); + date.setUTCMilliseconds(fraction ? Math.round(parseFloat(`0.${fraction}`) * 1000) : 0); + return date; + } + + throw new TypeError(`Invalid RFC7231 date-time value ${value}.`); +}; + +/** + * @internal + */ +function range(v: number | string, min: number, max: number): void { + const _v = Number(v); + if (_v < min || _v > max) { + throw new Error(`Value ${_v} out of range [${min}, ${max}]`); + } +} diff --git a/packages/core/src/submodules/serde/value/NumericValue.spec.ts b/packages/core/src/submodules/serde/value/NumericValue.spec.ts index f790632ba7f..3eb1ed89351 100644 --- a/packages/core/src/submodules/serde/value/NumericValue.spec.ts +++ b/packages/core/src/submodules/serde/value/NumericValue.spec.ts @@ -24,11 +24,8 @@ describe(NumericValue.name, () => { new NumericValue("0", "bigDecimal"), new NumericValue("-0.00", "bigDecimal"), { - string: "abcd", + string: "-.123", type: "bigDecimal", - constructor: { - name: "_NumericValue", - }, }, (() => { const x = {}; @@ -52,9 +49,6 @@ describe(NumericValue.name, () => { { string: "abcd", type: "bigDecimal", - constructor: { - name: "_NumericValue_", - }, }, (() => { const x = {}; diff --git a/packages/core/src/submodules/serde/value/NumericValue.ts b/packages/core/src/submodules/serde/value/NumericValue.ts index d3b081ca42a..9900b2ece08 100644 --- a/packages/core/src/submodules/serde/value/NumericValue.ts +++ b/packages/core/src/submodules/serde/value/NumericValue.ts @@ -8,6 +8,11 @@ */ export type NumericType = "bigDecimal"; +/** + * @internal + */ +const format = /^-?\d*(\.\d+)?$/; + /** * Serialization container for Smithy simple types that do not have a * direct JavaScript runtime representation. @@ -25,27 +30,10 @@ export class NumericValue { public readonly string: string, public readonly type: NumericType ) { - let dot = 0; - for (let i = 0; i < string.length; ++i) { - const char = string.charCodeAt(i); - if (i === 0 && char === 45) { - // negation prefix "-" - continue; - } - if (char === 46) { - // decimal point "." - if (dot) { - throw new Error("@smithy/core/serde - NumericValue must contain at most one decimal point."); - } - dot = 1; - continue; - } - if (char < 48 || char > 57) { - // not in 0 through 9 - throw new Error( - `@smithy/core/serde - NumericValue must only contain [0-9], at most one decimal point ".", and an optional negation prefix "-".` - ); - } + if (!format.test(string)) { + throw new Error( + `@smithy/core/serde - NumericValue must only contain [0-9], at most one decimal point ".", and an optional negation prefix "-".` + ); } } @@ -58,18 +46,7 @@ export class NumericValue { return false; } const _nv = object as NumericValue; - const prototypeMatch = NumericValue.prototype.isPrototypeOf(object); - if (prototypeMatch) { - return prototypeMatch; - } - if ( - typeof _nv.string === "string" && - typeof _nv.type === "string" && - _nv.constructor?.name?.endsWith("NumericValue") - ) { - return true; - } - return prototypeMatch; + return NumericValue.prototype.isPrototypeOf(object) || (_nv.type === "bigDecimal" && format.test(_nv.string)); } } diff --git a/packages/util-base64/src/constants.browser.ts b/packages/util-base64/src/constants.browser.ts index 256d216ef7b..76a45aa6ef0 100644 --- a/packages/util-base64/src/constants.browser.ts +++ b/packages/util-base64/src/constants.browser.ts @@ -1,34 +1,14 @@ -const alphabetByEncoding: Record = {}; -const alphabetByValue: Array = new Array(64); - -for (let i = 0, start = "A".charCodeAt(0), limit = "Z".charCodeAt(0); i + start <= limit; i++) { - const char = String.fromCharCode(i + start); - alphabetByEncoding[char] = i; - alphabetByValue[i] = char; -} - -for (let i = 0, start = "a".charCodeAt(0), limit = "z".charCodeAt(0); i + start <= limit; i++) { - const char = String.fromCharCode(i + start); - const index = i + 26; - alphabetByEncoding[char] = index; - alphabetByValue[index] = char; -} - -for (let i = 0; i < 10; i++) { - alphabetByEncoding[i.toString(10)] = i + 52; - const char = i.toString(10); - const index = i + 52; - alphabetByEncoding[char] = index; - alphabetByValue[index] = char; -} - -alphabetByEncoding["+"] = 62; -alphabetByValue[62] = "+"; -alphabetByEncoding["/"] = 63; -alphabetByValue[63] = "/"; - -const bitsPerLetter = 6; -const bitsPerByte = 8; -const maxLetterValue = 0b111111; - -export { alphabetByEncoding, alphabetByValue, bitsPerLetter, bitsPerByte, maxLetterValue }; +const chars = `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`; + +export const alphabetByEncoding: Record = Object.entries(chars).reduce( + (acc, [i, c]) => { + acc[c] = Number(i); + return acc; + }, + {} as Record +); +export const alphabetByValue: Array = chars.split(""); + +export const bitsPerLetter = 6; +export const bitsPerByte = 8; +export const maxLetterValue = 0b111111; diff --git a/packages/util-base64/vitest.config.mts b/packages/util-base64/vitest.config.mts index 4e46707824a..0354cc268ab 100644 --- a/packages/util-base64/vitest.config.mts +++ b/packages/util-base64/vitest.config.mts @@ -2,7 +2,7 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { - exclude: ["**/*.{integ,e2e,browser}.spec.ts"], + exclude: ["**/*.{integ,e2e}.spec.ts"], include: ["**/*.spec.ts"], environment: "node", }, diff --git a/packages/util-stream/src/blob/Uint8ArrayBlobAdapter.ts b/packages/util-stream/src/blob/Uint8ArrayBlobAdapter.ts index 447be19b21d..2a78e35906b 100644 --- a/packages/util-stream/src/blob/Uint8ArrayBlobAdapter.ts +++ b/packages/util-stream/src/blob/Uint8ArrayBlobAdapter.ts @@ -7,15 +7,14 @@ import { transformFromString, transformToString } from "./transforms"; export class Uint8ArrayBlobAdapter extends Uint8Array { /** * @param source - such as a string or Stream. + * @param encoding - utf-8 or base64. * @returns a new Uint8ArrayBlobAdapter extending Uint8Array. */ public static fromString(source: string, encoding = "utf-8"): Uint8ArrayBlobAdapter { - switch (typeof source) { - case "string": - return transformFromString(source, encoding); - default: - throw new Error(`Unsupported conversion from ${typeof source} to Uint8ArrayBlobAdapter.`); + if (typeof source === "string") { + return transformFromString(source, encoding); } + throw new Error(`Unsupported conversion from ${typeof source} to Uint8ArrayBlobAdapter.`); } /** From 17ed62cb78bd3506d601936c9d24298524336628 Mon Sep 17 00:00:00 2001 From: Smithy Automation Date: Wed, 8 Oct 2025 19:13:57 +0000 Subject: [PATCH 07/13] Version NPM packages --- .changeset/clean-terms-argue.md | 2 -- .changeset/odd-rats-reflect.md | 5 ----- .changeset/sixty-cows-pretend.md | 5 ----- .changeset/tame-suns-carry.md | 7 ------- packages/chunked-blob-reader-native/CHANGELOG.md | 7 +++++++ packages/chunked-blob-reader-native/package.json | 2 +- packages/core/CHANGELOG.md | 12 ++++++++++++ packages/core/package.json | 2 +- packages/experimental-identity-and-auth/CHANGELOG.md | 7 +++++++ packages/experimental-identity-and-auth/package.json | 2 +- packages/fetch-http-handler/CHANGELOG.md | 7 +++++++ packages/fetch-http-handler/package.json | 2 +- packages/hash-blob-browser/CHANGELOG.md | 6 ++++++ packages/hash-blob-browser/package.json | 2 +- packages/middleware-compression/CHANGELOG.md | 7 +++++++ packages/middleware-compression/package.json | 2 +- packages/middleware-endpoint/CHANGELOG.md | 7 +++++++ packages/middleware-endpoint/package.json | 2 +- packages/middleware-retry/CHANGELOG.md | 6 ++++++ packages/middleware-retry/package.json | 2 +- packages/smithy-client/CHANGELOG.md | 9 +++++++++ packages/smithy-client/package.json | 2 +- packages/util-base64/CHANGELOG.md | 6 ++++++ packages/util-base64/package.json | 2 +- packages/util-body-length-node/CHANGELOG.md | 6 ++++++ packages/util-body-length-node/package.json | 2 +- packages/util-defaults-mode-browser/CHANGELOG.md | 10 ++++++++++ packages/util-defaults-mode-browser/package.json | 2 +- packages/util-defaults-mode-node/CHANGELOG.md | 6 ++++++ packages/util-defaults-mode-node/package.json | 2 +- packages/util-stream-browser/CHANGELOG.md | 8 ++++++++ packages/util-stream-browser/package.json | 2 +- packages/util-stream/CHANGELOG.md | 12 ++++++++++++ packages/util-stream/package.json | 2 +- 34 files changed, 131 insertions(+), 34 deletions(-) delete mode 100644 .changeset/clean-terms-argue.md delete mode 100644 .changeset/odd-rats-reflect.md delete mode 100644 .changeset/sixty-cows-pretend.md delete mode 100644 .changeset/tame-suns-carry.md diff --git a/.changeset/clean-terms-argue.md b/.changeset/clean-terms-argue.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/clean-terms-argue.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/odd-rats-reflect.md b/.changeset/odd-rats-reflect.md deleted file mode 100644 index b97b46a3d6c..00000000000 --- a/.changeset/odd-rats-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@smithy/util-body-length-node": patch ---- - -restrict fs calls to fs.ReadStream instances diff --git a/.changeset/sixty-cows-pretend.md b/.changeset/sixty-cows-pretend.md deleted file mode 100644 index 7cc2315ee79..00000000000 --- a/.changeset/sixty-cows-pretend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@smithy/util-defaults-mode-browser": minor ---- - -remove bower from mobile device detection diff --git a/.changeset/tame-suns-carry.md b/.changeset/tame-suns-carry.md deleted file mode 100644 index 26b79823254..00000000000 --- a/.changeset/tame-suns-carry.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@smithy/util-base64": minor -"@smithy/util-stream": minor -"@smithy/core": minor ---- - -refactoring to reduce code size diff --git a/packages/chunked-blob-reader-native/CHANGELOG.md b/packages/chunked-blob-reader-native/CHANGELOG.md index 10a28697433..a8f1284a4dc 100644 --- a/packages/chunked-blob-reader-native/CHANGELOG.md +++ b/packages/chunked-blob-reader-native/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 4.2.1 + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/util-base64@4.3.0 + ## 4.2.0 ### Minor Changes diff --git a/packages/chunked-blob-reader-native/package.json b/packages/chunked-blob-reader-native/package.json index 1ccb33e8d5a..eef21aa190a 100644 --- a/packages/chunked-blob-reader-native/package.json +++ b/packages/chunked-blob-reader-native/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/chunked-blob-reader-native", - "version": "4.2.0", + "version": "4.2.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline chunked-blob-reader-native", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 31e26f8809f..9d601ef597c 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## 3.15.0 + +### Minor Changes + +- 813c9a5: refactoring to reduce code size + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/util-base64@4.3.0 + - @smithy/util-stream@4.5.0 + ## 3.14.0 ### Minor Changes diff --git a/packages/core/package.json b/packages/core/package.json index c43a8cf74c6..93cc1fdab58 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/core", - "version": "3.14.0", + "version": "3.15.0", "scripts": { "build": "yarn lint && concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline core", diff --git a/packages/experimental-identity-and-auth/CHANGELOG.md b/packages/experimental-identity-and-auth/CHANGELOG.md index fc9230244d9..9b6dc070cda 100644 --- a/packages/experimental-identity-and-auth/CHANGELOG.md +++ b/packages/experimental-identity-and-auth/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 0.5.1 + +### Patch Changes + +- @smithy/middleware-endpoint@4.3.1 +- @smithy/middleware-retry@4.4.1 + ## 0.5.0 ### Minor Changes diff --git a/packages/experimental-identity-and-auth/package.json b/packages/experimental-identity-and-auth/package.json index 081a4e8ddb8..261852123e2 100644 --- a/packages/experimental-identity-and-auth/package.json +++ b/packages/experimental-identity-and-auth/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/experimental-identity-and-auth", - "version": "0.5.0", + "version": "0.5.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline experimental-identity-and-auth", diff --git a/packages/fetch-http-handler/CHANGELOG.md b/packages/fetch-http-handler/CHANGELOG.md index f9239a6e61c..db21d97dcbc 100644 --- a/packages/fetch-http-handler/CHANGELOG.md +++ b/packages/fetch-http-handler/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 5.3.1 + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/util-base64@4.3.0 + ## 5.3.0 ### Minor Changes diff --git a/packages/fetch-http-handler/package.json b/packages/fetch-http-handler/package.json index d3783234887..56b78666082 100644 --- a/packages/fetch-http-handler/package.json +++ b/packages/fetch-http-handler/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/fetch-http-handler", - "version": "5.3.0", + "version": "5.3.1", "description": "Provides a way to make requests", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", diff --git a/packages/hash-blob-browser/CHANGELOG.md b/packages/hash-blob-browser/CHANGELOG.md index dc975c97b73..99c21d3ddeb 100644 --- a/packages/hash-blob-browser/CHANGELOG.md +++ b/packages/hash-blob-browser/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 4.2.1 + +### Patch Changes + +- @smithy/chunked-blob-reader-native@4.2.1 + ## 4.2.0 ### Minor Changes diff --git a/packages/hash-blob-browser/package.json b/packages/hash-blob-browser/package.json index dc67b9f95da..91f582b6d65 100644 --- a/packages/hash-blob-browser/package.json +++ b/packages/hash-blob-browser/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/hash-blob-browser", - "version": "4.2.0", + "version": "4.2.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline hash-blob-browser", diff --git a/packages/middleware-compression/CHANGELOG.md b/packages/middleware-compression/CHANGELOG.md index b90aabf116c..9d3f109aeaa 100644 --- a/packages/middleware-compression/CHANGELOG.md +++ b/packages/middleware-compression/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 4.3.1 + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/core@3.15.0 + ## 4.3.0 ### Minor Changes diff --git a/packages/middleware-compression/package.json b/packages/middleware-compression/package.json index 1b81f0126b0..0435f38414c 100644 --- a/packages/middleware-compression/package.json +++ b/packages/middleware-compression/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/middleware-compression", - "version": "4.3.0", + "version": "4.3.1", "description": "Middleware and Plugin for request compression.", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'", diff --git a/packages/middleware-endpoint/CHANGELOG.md b/packages/middleware-endpoint/CHANGELOG.md index ebbcd378049..72a0d955e22 100644 --- a/packages/middleware-endpoint/CHANGELOG.md +++ b/packages/middleware-endpoint/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 4.3.1 + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/core@3.15.0 + ## 4.3.0 ### Minor Changes diff --git a/packages/middleware-endpoint/package.json b/packages/middleware-endpoint/package.json index 56063fc0515..e9e17130ed1 100644 --- a/packages/middleware-endpoint/package.json +++ b/packages/middleware-endpoint/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/middleware-endpoint", - "version": "4.3.0", + "version": "4.3.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline middleware-endpoint", diff --git a/packages/middleware-retry/CHANGELOG.md b/packages/middleware-retry/CHANGELOG.md index 64e14870bf2..7a9d0738570 100644 --- a/packages/middleware-retry/CHANGELOG.md +++ b/packages/middleware-retry/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 4.4.1 + +### Patch Changes + +- @smithy/smithy-client@4.7.1 + ## 4.4.0 ### Minor Changes diff --git a/packages/middleware-retry/package.json b/packages/middleware-retry/package.json index 1b09cb2cc29..ce66cb18a28 100644 --- a/packages/middleware-retry/package.json +++ b/packages/middleware-retry/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/middleware-retry", - "version": "4.4.0", + "version": "4.4.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline middleware-retry", diff --git a/packages/smithy-client/CHANGELOG.md b/packages/smithy-client/CHANGELOG.md index 29a5eae91f6..056dfac4257 100644 --- a/packages/smithy-client/CHANGELOG.md +++ b/packages/smithy-client/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 4.7.1 + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/util-stream@4.5.0 + - @smithy/core@3.15.0 + - @smithy/middleware-endpoint@4.3.1 + ## 4.7.0 ### Minor Changes diff --git a/packages/smithy-client/package.json b/packages/smithy-client/package.json index b8e4bb1f614..0ddc181365c 100644 --- a/packages/smithy-client/package.json +++ b/packages/smithy-client/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/smithy-client", - "version": "4.7.0", + "version": "4.7.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline smithy-client", diff --git a/packages/util-base64/CHANGELOG.md b/packages/util-base64/CHANGELOG.md index 6b08164c44f..99231b989a2 100644 --- a/packages/util-base64/CHANGELOG.md +++ b/packages/util-base64/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 4.3.0 + +### Minor Changes + +- 813c9a5: refactoring to reduce code size + ## 4.2.0 ### Minor Changes diff --git a/packages/util-base64/package.json b/packages/util-base64/package.json index 99740325d4f..b8e9ba2a3d3 100644 --- a/packages/util-base64/package.json +++ b/packages/util-base64/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/util-base64", - "version": "4.2.0", + "version": "4.3.0", "description": "A Base64 <-> UInt8Array converter", "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/util-body-length-node/CHANGELOG.md b/packages/util-body-length-node/CHANGELOG.md index 8f8f36aba42..98aac2f8ddc 100644 --- a/packages/util-body-length-node/CHANGELOG.md +++ b/packages/util-body-length-node/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 4.2.1 + +### Patch Changes + +- 300177f: restrict fs calls to fs.ReadStream instances + ## 4.2.0 ### Minor Changes diff --git a/packages/util-body-length-node/package.json b/packages/util-body-length-node/package.json index 30da42e5280..9bee430e68d 100644 --- a/packages/util-body-length-node/package.json +++ b/packages/util-body-length-node/package.json @@ -1,7 +1,7 @@ { "name": "@smithy/util-body-length-node", "description": "Determines the length of a request body in node.js", - "version": "4.2.0", + "version": "4.2.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline util-body-length-node", diff --git a/packages/util-defaults-mode-browser/CHANGELOG.md b/packages/util-defaults-mode-browser/CHANGELOG.md index d5248b7c221..84f13333452 100644 --- a/packages/util-defaults-mode-browser/CHANGELOG.md +++ b/packages/util-defaults-mode-browser/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## 4.3.0 + +### Minor Changes + +- c1544be: remove bower from mobile device detection + +### Patch Changes + +- @smithy/smithy-client@4.7.1 + ## 4.2.0 ### Minor Changes diff --git a/packages/util-defaults-mode-browser/package.json b/packages/util-defaults-mode-browser/package.json index 44d2314ff75..d45aa41be1e 100644 --- a/packages/util-defaults-mode-browser/package.json +++ b/packages/util-defaults-mode-browser/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/util-defaults-mode-browser", - "version": "4.2.0", + "version": "4.3.0", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline util-defaults-mode-browser", diff --git a/packages/util-defaults-mode-node/CHANGELOG.md b/packages/util-defaults-mode-node/CHANGELOG.md index 219289d4f40..cd4e23f4087 100644 --- a/packages/util-defaults-mode-node/CHANGELOG.md +++ b/packages/util-defaults-mode-node/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 4.2.1 + +### Patch Changes + +- @smithy/smithy-client@4.7.1 + ## 4.2.0 ### Minor Changes diff --git a/packages/util-defaults-mode-node/package.json b/packages/util-defaults-mode-node/package.json index 797555b7ba4..d0f59f8c672 100644 --- a/packages/util-defaults-mode-node/package.json +++ b/packages/util-defaults-mode-node/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/util-defaults-mode-node", - "version": "4.2.0", + "version": "4.2.1", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline util-defaults-mode-node", diff --git a/packages/util-stream-browser/CHANGELOG.md b/packages/util-stream-browser/CHANGELOG.md index 23d1060abc4..f0b425a5d1a 100644 --- a/packages/util-stream-browser/CHANGELOG.md +++ b/packages/util-stream-browser/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## 3.2.1 + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/util-base64@4.3.0 + - @smithy/fetch-http-handler@5.3.1 + ## 3.2.0 ### Minor Changes diff --git a/packages/util-stream-browser/package.json b/packages/util-stream-browser/package.json index 44cb10bb3bf..ab7ae4faaa0 100644 --- a/packages/util-stream-browser/package.json +++ b/packages/util-stream-browser/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/util-stream-browser", - "version": "3.2.0", + "version": "3.2.1", "scripts": { "build": "concurrently 'yarn:build:es' 'yarn:build:types'", "build:es": "yarn g:tsc -p tsconfig.es.json", diff --git a/packages/util-stream/CHANGELOG.md b/packages/util-stream/CHANGELOG.md index 9315b7f81ec..e1713cc728c 100644 --- a/packages/util-stream/CHANGELOG.md +++ b/packages/util-stream/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## 4.5.0 + +### Minor Changes + +- 813c9a5: refactoring to reduce code size + +### Patch Changes + +- Updated dependencies [813c9a5] + - @smithy/util-base64@4.3.0 + - @smithy/fetch-http-handler@5.3.1 + ## 4.4.0 ### Minor Changes diff --git a/packages/util-stream/package.json b/packages/util-stream/package.json index 560f3838f0f..e5e31e7f062 100644 --- a/packages/util-stream/package.json +++ b/packages/util-stream/package.json @@ -1,6 +1,6 @@ { "name": "@smithy/util-stream", - "version": "4.4.0", + "version": "4.5.0", "scripts": { "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types && yarn build:types:downlevel'", "build:cjs": "node ../../scripts/inline util-stream", From 1c187ac35371b82a9df8100d74527fa0beb66817 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:06:20 -0700 Subject: [PATCH 08/13] Run gradle publish step in CI (#1737) --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08cd9c4052b..13c92d4378f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,9 @@ jobs: - name: clean and build run: ./gradlew clean build -Plog-tests + - name: publish + run: ./gradlew publish + protocol-tests: runs-on: ${{ matrix.os }} name: Protocol Tests From a241daf75980428fa992c5381f862e8fd3401b65 Mon Sep 17 00:00:00 2001 From: George Fu Date: Thu, 9 Oct 2025 13:28:47 -0400 Subject: [PATCH 09/13] add SerdeContext base class (#1738) --- .changeset/popular-squids-lick.md | 5 ++++ packages/core/src/index.ts | 2 +- .../requestBuilder.ts | 0 .../core/src/submodules/cbor/CborCodec.ts | 26 ++++--------------- .../src/submodules/protocols/HttpProtocol.ts | 12 ++++++--- .../src/submodules/protocols/SerdeContext.ts | 12 +++++++++ .../core/src/submodules/protocols/index.ts | 1 + .../serde/FromStringShapeDeserializer.ts | 12 +++------ .../HttpInterceptingShapeDeserializer.ts | 7 ++++- .../serde/HttpInterceptingShapeSerializer.ts | 13 ++++++++-- .../serde/ToStringShapeSerializer.ts | 11 +++----- 11 files changed, 58 insertions(+), 43 deletions(-) create mode 100644 .changeset/popular-squids-lick.md rename packages/core/src/{protocols => request-builder}/requestBuilder.ts (100%) create mode 100644 packages/core/src/submodules/protocols/SerdeContext.ts diff --git a/.changeset/popular-squids-lick.md b/.changeset/popular-squids-lick.md new file mode 100644 index 00000000000..6459937b8ca --- /dev/null +++ b/.changeset/popular-squids-lick.md @@ -0,0 +1,5 @@ +--- +"@smithy/core": minor +--- + +implement SerdeContext base class diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1dcdba11994..82c90b929e3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -3,6 +3,6 @@ export * from "./middleware-http-auth-scheme"; export * from "./middleware-http-signing"; export * from "./normalizeProvider"; export { createPaginator } from "./pagination/createPaginator"; -export * from "./protocols/requestBuilder"; +export * from "./request-builder/requestBuilder"; export * from "./setFeature"; export * from "./util-identity-and-auth"; diff --git a/packages/core/src/protocols/requestBuilder.ts b/packages/core/src/request-builder/requestBuilder.ts similarity index 100% rename from packages/core/src/protocols/requestBuilder.ts rename to packages/core/src/request-builder/requestBuilder.ts diff --git a/packages/core/src/submodules/cbor/CborCodec.ts b/packages/core/src/submodules/cbor/CborCodec.ts index 1331cf79738..3d1dad359d8 100644 --- a/packages/core/src/submodules/cbor/CborCodec.ts +++ b/packages/core/src/submodules/cbor/CborCodec.ts @@ -1,6 +1,7 @@ +import { SerdeContext } from "@smithy/core/protocols"; import { NormalizedSchema } from "@smithy/core/schema"; import { _parseEpochTimestamp, generateIdempotencyToken } from "@smithy/core/serde"; -import type { Codec, Schema, SerdeFunctions, ShapeDeserializer, ShapeSerializer } from "@smithy/types"; +import type { Codec, Schema, ShapeDeserializer, ShapeSerializer } from "@smithy/types"; import { fromBase64 } from "@smithy/util-base64"; import { cbor } from "./cbor"; @@ -9,9 +10,7 @@ import { dateToTag } from "./parseCborBody"; /** * @alpha */ -export class CborCodec implements Codec { - private serdeContext?: SerdeFunctions; - +export class CborCodec extends SerdeContext implements Codec { public createSerializer(): CborShapeSerializer { const serializer = new CborShapeSerializer(); serializer.setSerdeContext(this.serdeContext!); @@ -23,23 +22,14 @@ export class CborCodec implements Codec { deserializer.setSerdeContext(this.serdeContext!); return deserializer; } - - public setSerdeContext(serdeContext: SerdeFunctions): void { - this.serdeContext = serdeContext; - } } /** * @alpha */ -export class CborShapeSerializer implements ShapeSerializer { - private serdeContext?: SerdeFunctions; +export class CborShapeSerializer extends SerdeContext implements ShapeSerializer { private value: unknown; - public setSerdeContext(serdeContext: SerdeFunctions) { - this.serdeContext = serdeContext; - } - public write(schema: Schema, value: unknown): void { this.value = this.serialize(schema, value); } @@ -127,13 +117,7 @@ export class CborShapeSerializer implements ShapeSerializer { /** * @alpha */ -export class CborShapeDeserializer implements ShapeDeserializer { - private serdeContext?: SerdeFunctions; - - public setSerdeContext(serdeContext: SerdeFunctions) { - this.serdeContext = serdeContext; - } - +export class CborShapeDeserializer extends SerdeContext implements ShapeDeserializer { public read(schema: Schema, bytes: Uint8Array): any { const data: any = cbor.deserialize(bytes); return this.readValue(schema, data); diff --git a/packages/core/src/submodules/protocols/HttpProtocol.ts b/packages/core/src/submodules/protocols/HttpProtocol.ts index f178e84ceee..7d94e616d37 100644 --- a/packages/core/src/submodules/protocols/HttpProtocol.ts +++ b/packages/core/src/submodules/protocols/HttpProtocol.ts @@ -21,21 +21,24 @@ import type { ShapeSerializer, } from "@smithy/types"; +import { SerdeContext } from "./SerdeContext"; + /** * Abstract base for HTTP-based client protocols. * * @alpha */ -export abstract class HttpProtocol implements ClientProtocol { +export abstract class HttpProtocol extends SerdeContext implements ClientProtocol { protected abstract serializer: ShapeSerializer; protected abstract deserializer: ShapeDeserializer; - protected serdeContext?: SerdeFunctions; protected constructor( public readonly options: { defaultNamespace: string; } - ) {} + ) { + super(); + } public abstract getShapeId(): string; @@ -49,6 +52,9 @@ export abstract class HttpProtocol implements ClientProtocol { - private serdeContext: SerdeFunctions | undefined; - - public constructor(private settings: CodecSettings) {} - - public setSerdeContext(serdeContext: SerdeFunctions): void { - this.serdeContext = serdeContext; +export class FromStringShapeDeserializer extends SerdeContext implements ShapeDeserializer { + public constructor(private settings: CodecSettings) { + super(); } public read(_schema: Schema, data: string): any { diff --git a/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeDeserializer.ts b/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeDeserializer.ts index 7aa9feb4735..af72723cb5a 100644 --- a/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeDeserializer.ts +++ b/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeDeserializer.ts @@ -2,6 +2,7 @@ import { NormalizedSchema } from "@smithy/core/schema"; import type { CodecSettings, Schema, SerdeFunctions, ShapeDeserializer } from "@smithy/types"; import { fromUtf8, toUtf8 } from "@smithy/util-utf8"; +import { SerdeContext } from "../SerdeContext"; import { FromStringShapeDeserializer } from "./FromStringShapeDeserializer"; /** @@ -14,18 +15,22 @@ import { FromStringShapeDeserializer } from "./FromStringShapeDeserializer"; * @alpha */ export class HttpInterceptingShapeDeserializer> + extends SerdeContext implements ShapeDeserializer { private stringDeserializer: FromStringShapeDeserializer; - private serdeContext: SerdeFunctions | undefined; public constructor( private codecDeserializer: CodecShapeDeserializer, codecSettings: CodecSettings ) { + super(); this.stringDeserializer = new FromStringShapeDeserializer(codecSettings); } + /** + * @override + */ public setSerdeContext(serdeContext: SerdeFunctions): void { this.stringDeserializer.setSerdeContext(serdeContext); this.codecDeserializer.setSerdeContext(serdeContext); diff --git a/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeSerializer.ts b/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeSerializer.ts index dda09dd192c..f4a45150827 100644 --- a/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeSerializer.ts +++ b/packages/core/src/submodules/protocols/serde/HttpInterceptingShapeSerializer.ts @@ -1,5 +1,11 @@ import { NormalizedSchema } from "@smithy/core/schema"; -import type { CodecSettings, Schema as ISchema, SerdeFunctions, ShapeSerializer } from "@smithy/types"; +import type { + CodecSettings, + ConfigurableSerdeContext, + Schema as ISchema, + SerdeFunctions, + ShapeSerializer, +} from "@smithy/types"; import { ToStringShapeSerializer } from "./ToStringShapeSerializer"; @@ -13,7 +19,7 @@ import { ToStringShapeSerializer } from "./ToStringShapeSerializer"; * @alpha */ export class HttpInterceptingShapeSerializer> - implements ShapeSerializer + implements ShapeSerializer, ConfigurableSerdeContext { private buffer: string | undefined; @@ -23,6 +29,9 @@ export class HttpInterceptingShapeSerializer { +export class ToStringShapeSerializer extends SerdeContext implements ShapeSerializer { private stringBuffer = ""; - private serdeContext: SerdeFunctions | undefined = undefined; - public constructor(private settings: CodecSettings) {} - - public setSerdeContext(serdeContext: SerdeFunctions): void { - this.serdeContext = serdeContext; + public constructor(private settings: CodecSettings) { + super(); } public write(schema: Schema, value: unknown): void { From 824a48c6ca13267821909aa0f3c765592aad1c5c Mon Sep 17 00:00:00 2001 From: George Fu Date: Fri, 10 Oct 2025 10:33:53 -0400 Subject: [PATCH 10/13] feat: generate static schema (#1739) --- .changeset/lazy-brooms-sip.md | 7 + .../src/submodules/schema/TypeRegistry.ts | 14 +- .../schemaDeserializationMiddleware.ts | 10 +- .../schemaSerializationMiddleware.ts | 9 +- .../schema/schemas/NormalizedSchema.spec.ts | 31 ++ .../schema/schemas/NormalizedSchema.ts | 69 ++- packages/smithy-client/src/command.ts | 17 +- packages/types/src/index.ts | 1 + packages/types/src/schema/schema.ts | 2 + packages/types/src/schema/static-schemas.ts | 90 ++++ .../src/commands/EmptyInputOutputCommand.ts | 2 +- .../src/commands/Float16Command.ts | 2 +- .../src/commands/FractionalSecondsCommand.ts | 2 +- .../src/commands/GreetingWithErrorsCommand.ts | 2 +- .../src/commands/NoInputOutputCommand.ts | 2 +- .../commands/OperationWithDefaultsCommand.ts | 2 +- .../commands/OptionalInputOutputCommand.ts | 2 +- .../src/commands/RecursiveShapesCommand.ts | 2 +- .../src/commands/RpcV2CborDenseMapsCommand.ts | 2 +- .../src/commands/RpcV2CborListsCommand.ts | 2 +- .../commands/RpcV2CborSparseMapsCommand.ts | 2 +- .../commands/SimpleScalarPropertiesCommand.ts | 2 +- .../commands/SparseNullsOperationCommand.ts | 2 +- .../src/schemas/schemas_0.ts | 369 ++++++++++++++ .../src/schemas/schemas_1_Rpc.ts | 454 ------------------ .../src/schemas/schemas_2_EmptyInputOutput.ts | 14 - .../schemas/schemas_3_FractionalSeconds.ts | 15 - .../src/schemas/schemas_4_.ts | 15 - .../src/schemas/schemas_5_NoInputOutput.ts | 14 - .../codegen/schema/SchemaGenerator.java | 76 +-- .../codegen/schema/ShapeGroupingIndex.java | 9 + 31 files changed, 657 insertions(+), 585 deletions(-) create mode 100644 .changeset/lazy-brooms-sip.md create mode 100644 packages/types/src/schema/static-schemas.ts delete mode 100644 private/smithy-rpcv2-cbor-schema/src/schemas/schemas_1_Rpc.ts delete mode 100644 private/smithy-rpcv2-cbor-schema/src/schemas/schemas_2_EmptyInputOutput.ts delete mode 100644 private/smithy-rpcv2-cbor-schema/src/schemas/schemas_3_FractionalSeconds.ts delete mode 100644 private/smithy-rpcv2-cbor-schema/src/schemas/schemas_4_.ts delete mode 100644 private/smithy-rpcv2-cbor-schema/src/schemas/schemas_5_NoInputOutput.ts diff --git a/.changeset/lazy-brooms-sip.md b/.changeset/lazy-brooms-sip.md new file mode 100644 index 00000000000..03b73bb49ee --- /dev/null +++ b/.changeset/lazy-brooms-sip.md @@ -0,0 +1,7 @@ +--- +"@smithy/smithy-client": minor +"@smithy/types": minor +"@smithy/core": minor +--- + +generation of static schema diff --git a/packages/core/src/submodules/schema/TypeRegistry.ts b/packages/core/src/submodules/schema/TypeRegistry.ts index f6ff1f7eb1c..03c388f011b 100644 --- a/packages/core/src/submodules/schema/TypeRegistry.ts +++ b/packages/core/src/submodules/schema/TypeRegistry.ts @@ -1,4 +1,4 @@ -import type { Schema as ISchema } from "@smithy/types"; +import type { Schema as ISchema, StaticErrorSchema } from "@smithy/types"; import type { ErrorSchema } from "./schemas/ErrorSchema"; @@ -13,7 +13,7 @@ export class TypeRegistry { private constructor( public readonly namespace: string, private schemas: Map = new Map(), - private exceptions: Map = new Map() + private exceptions: Map = new Map() ) {} /** @@ -53,16 +53,16 @@ export class TypeRegistry { /** * Associates an error schema with its constructor. */ - public registerError(errorSchema: ErrorSchema, ctor: any) { - this.exceptions.set(errorSchema, ctor); + public registerError(es: ErrorSchema | StaticErrorSchema, ctor: any) { + this.exceptions.set(es, ctor); } /** - * @param errorSchema - query. + * @param es - query. * @returns Error constructor that extends the service's base exception. */ - public getErrorCtor(errorSchema: ErrorSchema): any { - return this.exceptions.get(errorSchema); + public getErrorCtor(es: ErrorSchema | StaticErrorSchema): any { + return this.exceptions.get(es); } /** diff --git a/packages/core/src/submodules/schema/middleware/schemaDeserializationMiddleware.ts b/packages/core/src/submodules/schema/middleware/schemaDeserializationMiddleware.ts index 57032e13edf..1ad63a66738 100644 --- a/packages/core/src/submodules/schema/middleware/schemaDeserializationMiddleware.ts +++ b/packages/core/src/submodules/schema/middleware/schemaDeserializationMiddleware.ts @@ -5,9 +5,11 @@ import type { HandlerExecutionContext, MetadataBearer, OperationSchema, + StaticOperationSchema, } from "@smithy/types"; import { getSmithyContext } from "@smithy/util-middleware"; +import { hydrate, isStaticSchema } from "../schemas/NormalizedSchema"; import type { PreviouslyResolved } from "./schema-middleware-types"; /** @@ -18,9 +20,13 @@ export const schemaDeserializationMiddleware = (next: DeserializeHandler, context: HandlerExecutionContext) => async (args: DeserializeHandlerArguments) => { const { response } = await next(args); - const { operationSchema } = getSmithyContext(context) as { - operationSchema: OperationSchema; + let { operationSchema } = getSmithyContext(context) as { + operationSchema: OperationSchema | StaticOperationSchema; }; + if (isStaticSchema(operationSchema)) { + operationSchema = hydrate(operationSchema); + } + try { const parsed = await config.protocol.deserializeResponse( operationSchema, diff --git a/packages/core/src/submodules/schema/middleware/schemaSerializationMiddleware.ts b/packages/core/src/submodules/schema/middleware/schemaSerializationMiddleware.ts index b94000adc61..46cc50e5e0d 100644 --- a/packages/core/src/submodules/schema/middleware/schemaSerializationMiddleware.ts +++ b/packages/core/src/submodules/schema/middleware/schemaSerializationMiddleware.ts @@ -6,9 +6,11 @@ import type { Provider, SerializeHandler, SerializeHandlerArguments, + StaticOperationSchema, } from "@smithy/types"; import { getSmithyContext } from "@smithy/util-middleware"; +import { hydrate, isStaticSchema } from "../schemas/NormalizedSchema"; import type { PreviouslyResolved } from "./schema-middleware-types"; /** @@ -18,9 +20,12 @@ export const schemaSerializationMiddleware = (config: PreviouslyResolved) => (next: SerializeHandler, context: HandlerExecutionContext) => async (args: SerializeHandlerArguments) => { - const { operationSchema } = getSmithyContext(context) as { - operationSchema: IOperationSchema; + let { operationSchema } = getSmithyContext(context) as { + operationSchema: IOperationSchema | StaticOperationSchema; }; + if (isStaticSchema(operationSchema)) { + operationSchema = hydrate(operationSchema); + } const endpoint: Provider = context.endpointV2?.url && config.urlParser diff --git a/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts b/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts index ef8251b70d1..c5e130f75d5 100644 --- a/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts +++ b/packages/core/src/submodules/schema/schemas/NormalizedSchema.spec.ts @@ -8,6 +8,9 @@ import type { MapSchemaModifier, MemberSchema, NumericSchema, + StaticListSchema, + StaticMapSchema, + StaticStructureSchema, StreamingBlobSchema, StringSchema, StructureSchema, @@ -313,4 +316,32 @@ describe(NormalizedSchema.name, () => { expect(ns.getEventStreamMember()).toEqual(""); }); }); + + describe("static schema", () => { + it("can normalize static schema indifferently to schema class objects", () => { + const [List, Map, Struct]: [StaticListSchema, StaticMapSchema, () => StaticStructureSchema] = [ + [1, "ack", "List", { sparse: 1 }, 0], + [2, "ack", "Map", 0, 0, 1], + () => schema, + ]; + const schema: StaticStructureSchema = [3, "ack", "Structure", {}, ["list", "map", "struct"], [List, Map, Struct]]; + + const ns = NormalizedSchema.of(schema); + + expect(ns.isStructSchema()).toBe(true); + expect(ns.getMemberSchema("list").isListSchema()).toBe(true); + expect(ns.getMemberSchema("list").getMergedTraits().sparse).toBe(1); + + expect(ns.getMemberSchema("map").isMapSchema()).toBe(true); + expect(ns.getMemberSchema("map").getKeySchema().isStringSchema()).toBe(true); + expect(ns.getMemberSchema("map").getValueSchema().isNumericSchema()).toBe(true); + + expect(ns.getMemberSchema("struct").isStructSchema()).toBe(true); + expect(ns.getMemberSchema("struct").getMemberSchema("list").isListSchema()).toBe(true); + expect(ns.getMemberSchema("struct").getMemberSchema("list").getMergedTraits().sparse).toBe(1); + expect(ns.getMemberSchema("struct").getMemberSchema("map").isMapSchema()).toBe(true); + expect(ns.getMemberSchema("struct").getMemberSchema("map").getKeySchema().isStringSchema()).toBe(true); + expect(ns.getMemberSchema("struct").getMemberSchema("map").getValueSchema().isNumericSchema()).toBe(true); + }); + }); }); diff --git a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts index 83b187b931d..2fd34fb2c43 100644 --- a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts +++ b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts @@ -13,6 +13,14 @@ import type { SchemaRef, SchemaTraits, SchemaTraitsObject, + StaticErrorSchema, + StaticListSchema, + StaticMapSchema, + StaticOperationSchema, + StaticSchema, + StaticSchemaId, + StaticSimpleSchema, + StaticStructureSchema, StreamingBlobSchema, StringSchema, TimestampDefaultSchema, @@ -22,11 +30,16 @@ import type { import type { IdempotencyTokenBitMask, TraitBitVector } from "@smithy/types/src/schema/traits"; import { deref } from "../deref"; -import { ListSchema } from "./ListSchema"; -import { MapSchema } from "./MapSchema"; +import type { ErrorSchema } from "./ErrorSchema"; +import { error } from "./ErrorSchema"; +import { list, ListSchema } from "./ListSchema"; +import { map, MapSchema } from "./MapSchema"; +import type { OperationSchema } from "./OperationSchema"; +import { op } from "./OperationSchema"; import { Schema } from "./Schema"; import type { SimpleSchema } from "./SimpleSchema"; -import { StructureSchema } from "./StructureSchema"; +import { sim } from "./SimpleSchema"; +import { struct, StructureSchema } from "./StructureSchema"; import { translateTraits } from "./translateTraits"; /** @@ -67,13 +80,15 @@ export class NormalizedSchema implements INormalizedSchema { let schema = ref; this._isMemberSchema = false; - while (Array.isArray(_ref)) { + while (isMemberSchema(_ref)) { traitStack.push(_ref[1]); _ref = _ref[0]; schema = deref(_ref); this._isMemberSchema = true; } + if (isStaticSchema(schema)) schema = hydrate(schema); + if (traitStack.length > 0) { this.memberTraits = {}; for (let i = traitStack.length - 1; i >= 0; --i) { @@ -96,7 +111,8 @@ export class NormalizedSchema implements INormalizedSchema { this.schema = deref(schema) as Exclude; if (this.schema && typeof this.schema === "object") { - this.traits = this.schema?.traits ?? {}; + // excluded by the checked hydrate call above. + this.traits = (this.schema as Exclude)?.traits ?? {}; } else { this.traits = 0; } @@ -120,7 +136,7 @@ export class NormalizedSchema implements INormalizedSchema { if (sc instanceof NormalizedSchema) { return sc; } - if (Array.isArray(sc)) { + if (isMemberSchema(sc)) { const [ns, traits] = sc; if (ns instanceof NormalizedSchema) { Object.assign(ns.getMergedTraits(), translateTraits(traits)); @@ -331,7 +347,7 @@ export class NormalizedSchema implements INormalizedSchema { if (this.isStructSchema() && struct.memberNames.includes(memberName)) { const i = struct.memberNames.indexOf(memberName); const memberSchema = struct.memberList[i]; - return member(Array.isArray(memberSchema) ? memberSchema : [memberSchema, 0], memberName); + return member(isMemberSchema(memberSchema) ? memberSchema : [memberSchema, 0], memberName); } if (this.isDocumentSchema()) { return member([15 satisfies DocumentSchema, 0], memberName); @@ -409,3 +425,42 @@ function member(memberSchema: NormalizedSchema | [SchemaRef, SchemaTraits], memb const internalCtorAccess = NormalizedSchema as any; return new internalCtorAccess(memberSchema, memberName); } + +/** + * @internal + * @returns a class instance version of a static schema. + */ +export function hydrate(ss: StaticSimpleSchema): SimpleSchema; +export function hydrate(ss: StaticListSchema): ListSchema; +export function hydrate(ss: StaticMapSchema): MapSchema; +export function hydrate(ss: StaticStructureSchema): StructureSchema; +export function hydrate(ss: StaticErrorSchema): ErrorSchema; +export function hydrate(ss: StaticOperationSchema): OperationSchema; +export function hydrate( + ss: StaticSchema +): SimpleSchema | ListSchema | MapSchema | StructureSchema | ErrorSchema | OperationSchema; +export function hydrate( + ss: StaticSchema +): SimpleSchema | ListSchema | MapSchema | StructureSchema | ErrorSchema | OperationSchema { + const [id, ...rest] = ss; + return ( + { + [0 satisfies StaticSchemaId.Simple]: sim, + [1 satisfies StaticSchemaId.List]: list, + [2 satisfies StaticSchemaId.Map]: map, + [3 satisfies StaticSchemaId.Struct]: struct, + [-3 satisfies StaticSchemaId.Error]: error, + [9 satisfies StaticSchemaId.Operation]: op, + }[id] as Function + ).call(null, ...rest); +} + +/** + * @internal + */ +const isMemberSchema = (sc: SchemaRef): sc is MemberSchema => Array.isArray(sc) && sc.length === 2; + +/** + * @internal + */ +export const isStaticSchema = (sc: SchemaRef): sc is StaticSchema => Array.isArray(sc) && sc.length >= 5; diff --git a/packages/smithy-client/src/command.ts b/packages/smithy-client/src/command.ts index 278d6c7e84d..35a4dbda176 100644 --- a/packages/smithy-client/src/command.ts +++ b/packages/smithy-client/src/command.ts @@ -17,6 +17,7 @@ import type { Pluggable, RequestHandler, SerdeContext, + StaticOperationSchema, } from "@smithy/types"; import { SMITHY_CONTEXT_KEY } from "@smithy/types"; @@ -35,7 +36,7 @@ export abstract class Command< { public abstract input: Input; public readonly middlewareStack: IMiddlewareStack = constructStack(); - public readonly schema?: OperationSchema; + public readonly schema?: OperationSchema | StaticOperationSchema; /** * Factory for Command ClassBuilder. @@ -136,7 +137,7 @@ class ClassBuilder< private _outputFilterSensitiveLog: any = undefined; private _serializer: (input: I, context: SerdeContext | any) => Promise = null as any; private _deserializer: (output: IHttpResponse, context: SerdeContext | any) => Promise = null as any; - private _operationSchema?: OperationSchema; + private _operationSchema?: OperationSchema | StaticOperationSchema; /** * Optional init callback. @@ -223,7 +224,7 @@ class ClassBuilder< /** * Sets input/output schema for the operation. */ - public sc(operation: OperationSchema): ClassBuilder { + public sc(operation: OperationSchema | StaticOperationSchema): ClassBuilder { this._operationSchema = operation; this._smithyContext.operationSchema = operation; return this; @@ -265,17 +266,19 @@ class ClassBuilder< * @internal */ public resolveMiddleware(stack: IMiddlewareStack, configuration: C, options: any): Handler { + const op = closure._operationSchema; + const input = (op as StaticOperationSchema)?.[4] ?? (op as OperationSchema)?.input; + const output = (op as StaticOperationSchema)?.[5] ?? (op as OperationSchema)?.output; + return this.resolveMiddlewareWithContext(stack, configuration, options, { CommandCtor: CommandRef, middlewareFn: closure._middlewareFn, clientName: closure._clientName, commandName: closure._commandName, inputFilterSensitiveLog: - closure._inputFilterSensitiveLog ?? - (closure._operationSchema ? schemaLogFilter.bind(null, closure._operationSchema!.input) : (_) => _), + closure._inputFilterSensitiveLog ?? (op ? schemaLogFilter.bind(null, input) : (_) => _), outputFilterSensitiveLog: - closure._outputFilterSensitiveLog ?? - (closure._operationSchema ? schemaLogFilter.bind(null, closure._operationSchema!.output) : (_) => _), + closure._outputFilterSensitiveLog ?? (op ? schemaLogFilter.bind(null, output) : (_) => _), smithyContext: closure._smithyContext, additionalContext: closure._additionalContext, }); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index b1dfb30b38f..12338d6cdce 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -23,6 +23,7 @@ export * from "./response"; export * from "./retry"; export * from "./schema/schema"; export * from "./schema/sentinels"; +export * from "./schema/static-schemas"; export * from "./serde"; export * from "./shapes"; export * from "./signature"; diff --git a/packages/types/src/schema/schema.ts b/packages/types/src/schema/schema.ts index 108ffefb375..4a53e4b3b58 100644 --- a/packages/types/src/schema/schema.ts +++ b/packages/types/src/schema/schema.ts @@ -16,6 +16,7 @@ import type { TimestampEpochSecondsSchema, TimestampHttpDateSchema, } from "./sentinels"; +import type { StaticSchema } from "./static-schemas"; import type { TraitBitVector } from "./traits"; /** @@ -31,6 +32,7 @@ export type Schema = | StructureSchema | MemberSchema | OperationSchema + | StaticSchema | NormalizedSchema; /** diff --git a/packages/types/src/schema/static-schemas.ts b/packages/types/src/schema/static-schemas.ts new file mode 100644 index 00000000000..77bacf32da1 --- /dev/null +++ b/packages/types/src/schema/static-schemas.ts @@ -0,0 +1,90 @@ +/* +A static schema is a non-function-call object that has no side effects. +Schemas are generated as static objects to improve tree-shaking behavior in downstream applications. + */ + +import type { SchemaRef, SchemaTraits } from "../schema/schema"; + +/** + * @alpha + */ +export namespace StaticSchemaId { + export type Simple = 0; + export type List = 1; + export type Map = 2; + export type Struct = 3; + export type Error = -3; + export type Operation = 9; +} + +/** + * @alpha + */ +export type StaticSchema = + | StaticSimpleSchema + | StaticListSchema + | StaticMapSchema + | StaticStructureSchema + | StaticErrorSchema + | StaticOperationSchema; + +/** + * @alpha + */ +export type ShapeName = string; + +/** + * @alpha + */ +export type ShapeNamespace = string; + +/** + * @alpha + */ +export type StaticSimpleSchema = [StaticSchemaId.Simple, ShapeNamespace, ShapeName, SchemaRef, SchemaTraits]; + +/** + * @alpha + */ +export type StaticListSchema = [StaticSchemaId.List, ShapeNamespace, ShapeName, SchemaTraits, SchemaRef]; + +/** + * @alpha + */ +export type StaticMapSchema = [StaticSchemaId.Map, ShapeNamespace, ShapeName, SchemaTraits, SchemaRef, SchemaRef]; + +/** + * @alpha + */ +export type StaticStructureSchema = [ + StaticSchemaId.Struct, + ShapeNamespace, + ShapeName, + SchemaTraits, + string[], // member name list + SchemaRef[], // member schema list +]; + +/** + * @alpha + */ +export type StaticErrorSchema = [ + StaticSchemaId.Error, + ShapeNamespace, + ShapeName, + SchemaTraits, + string[], // member name list + SchemaRef[], // member schema list +]; + +/** + * @alpha + */ +export type StaticOperationSchema = [ + StaticSchemaId.Operation, + ShapeNamespace, + ShapeName, + SchemaTraits, + SchemaRef, // input schema + SchemaRef, // output schema +]; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/EmptyInputOutputCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/EmptyInputOutputCommand.ts index 433bb6d9d72..c3d41f06cac 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/EmptyInputOutputCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/EmptyInputOutputCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { EmptyStructure } from "../models/models_0"; -import { EmptyInputOutput } from "../schemas/schemas_2_EmptyInputOutput"; +import { EmptyInputOutput } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/Float16Command.ts b/private/smithy-rpcv2-cbor-schema/src/commands/Float16Command.ts index 31663fd18e4..9d59e6e72e3 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/Float16Command.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/Float16Command.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { Float16Output } from "../models/models_0"; -import { Float16 } from "../schemas/schemas_4_"; +import { Float16 } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/FractionalSecondsCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/FractionalSecondsCommand.ts index d05f7c00762..28c498feccd 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/FractionalSecondsCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/FractionalSecondsCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { FractionalSecondsOutput } from "../models/models_0"; -import { FractionalSeconds } from "../schemas/schemas_3_FractionalSeconds"; +import { FractionalSeconds } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/GreetingWithErrorsCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/GreetingWithErrorsCommand.ts index 8bb41082673..08e0e78adaf 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/GreetingWithErrorsCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/GreetingWithErrorsCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { GreetingWithErrorsOutput } from "../models/models_0"; -import { GreetingWithErrors } from "../schemas/schemas_1_Rpc"; +import { GreetingWithErrors } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/NoInputOutputCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/NoInputOutputCommand.ts index 141457db04c..3bb36ad9665 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/NoInputOutputCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/NoInputOutputCommand.ts @@ -1,7 +1,7 @@ // smithy-typescript generated code import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; -import { NoInputOutput } from "../schemas/schemas_5_NoInputOutput"; +import { NoInputOutput } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/OperationWithDefaultsCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/OperationWithDefaultsCommand.ts index 8304cd43813..bccb5ba3186 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/OperationWithDefaultsCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/OperationWithDefaultsCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { OperationWithDefaultsInput, OperationWithDefaultsOutput } from "../models/models_0"; -import { OperationWithDefaults } from "../schemas/schemas_1_Rpc"; +import { OperationWithDefaults } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/OptionalInputOutputCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/OptionalInputOutputCommand.ts index 3c8c89a56f9..5dd233c817e 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/OptionalInputOutputCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/OptionalInputOutputCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { SimpleStructure } from "../models/models_0"; -import { OptionalInputOutput } from "../schemas/schemas_1_Rpc"; +import { OptionalInputOutput } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/RecursiveShapesCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/RecursiveShapesCommand.ts index e099181e657..d72b623f57a 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/RecursiveShapesCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/RecursiveShapesCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { RecursiveShapesInputOutput } from "../models/models_0"; -import { RecursiveShapes } from "../schemas/schemas_1_Rpc"; +import { RecursiveShapes } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborDenseMapsCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborDenseMapsCommand.ts index 468cfd2d930..70b0761241e 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborDenseMapsCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborDenseMapsCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { RpcV2CborDenseMapsInputOutput } from "../models/models_0"; -import { RpcV2CborDenseMaps } from "../schemas/schemas_1_Rpc"; +import { RpcV2CborDenseMaps } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborListsCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborListsCommand.ts index faabf08c94a..a9744f5f37c 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborListsCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborListsCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { RpcV2CborListInputOutput } from "../models/models_0"; -import { RpcV2CborLists } from "../schemas/schemas_1_Rpc"; +import { RpcV2CborLists } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborSparseMapsCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborSparseMapsCommand.ts index f98afd43b48..70bc737cb63 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborSparseMapsCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/RpcV2CborSparseMapsCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { RpcV2CborSparseMapsInputOutput } from "../models/models_0"; -import { RpcV2CborSparseMaps } from "../schemas/schemas_1_Rpc"; +import { RpcV2CborSparseMaps } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/SimpleScalarPropertiesCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/SimpleScalarPropertiesCommand.ts index 70ea70f0e7c..a7570d27547 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/SimpleScalarPropertiesCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/SimpleScalarPropertiesCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { SimpleScalarStructure } from "../models/models_0"; -import { SimpleScalarProperties } from "../schemas/schemas_1_Rpc"; +import { SimpleScalarProperties } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/commands/SparseNullsOperationCommand.ts b/private/smithy-rpcv2-cbor-schema/src/commands/SparseNullsOperationCommand.ts index 794c71810e4..e682281c792 100644 --- a/private/smithy-rpcv2-cbor-schema/src/commands/SparseNullsOperationCommand.ts +++ b/private/smithy-rpcv2-cbor-schema/src/commands/SparseNullsOperationCommand.ts @@ -2,7 +2,7 @@ import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; import { commonParams } from "../endpoint/EndpointParameters"; import { SparseNullsOperationInputOutput } from "../models/models_0"; -import { SparseNullsOperation } from "../schemas/schemas_1_Rpc"; +import { SparseNullsOperation } from "../schemas/schemas_0"; import { getEndpointPlugin } from "@smithy/middleware-endpoint"; import { Command as $Command } from "@smithy/smithy-client"; import { MetadataBearer as __MetadataBearer } from "@smithy/types"; diff --git a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_0.ts b/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_0.ts index 6b2c9ec0c83..d77bd655138 100644 --- a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_0.ts +++ b/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_0.ts @@ -132,9 +132,378 @@ export const n2 = "smithy.protocoltests.shared"; // smithy-typescript generated code import { RpcV2ProtocolServiceException as __RpcV2ProtocolServiceException } from "../models/RpcV2ProtocolServiceException"; +import { + ComplexError as __ComplexError, + InvalidGreeting as __InvalidGreeting, + ValidationException as __ValidationException, +} from "../models/index"; import { TypeRegistry, error } from "@smithy/core/schema"; +import { + StaticErrorSchema, + StaticListSchema, + StaticMapSchema, + StaticOperationSchema, + StaticStructureSchema, +} from "@smithy/types"; /* eslint no-var: 0 */ +export var Unit = "unit" as const; + +export var ValidationException: StaticErrorSchema = [ + -3, + n0, + _VE, + { + [_e]: _c, + }, + [_m, _fL], + [0, () => ValidationExceptionFieldList], +]; +TypeRegistry.for(n0).registerError(ValidationException, __ValidationException); + +export var ValidationExceptionField: StaticStructureSchema = [3, n0, _VEF, 0, [_p, _m], [0, 0]]; +export var ClientOptionalDefaults: StaticStructureSchema = [3, n1, _COD, 0, [_me], [1]]; +export var ComplexError: StaticErrorSchema = [ + -3, + n1, + _CE, + { + [_e]: _c, + }, + [_TL, _N], + [0, () => ComplexNestedErrorData], +]; +TypeRegistry.for(n1).registerError(ComplexError, __ComplexError); + +export var ComplexNestedErrorData: StaticStructureSchema = [3, n1, _CNED, 0, [_F], [0]]; +export var Defaults: StaticStructureSchema = [ + 3, + n1, + _D, + 0, + [ + _dS, + _dB, + _dL, + _dT, + _dBe, + _dBef, + _dSe, + _dI, + _dLe, + _dF, + _dD, + _dM, + _dE, + _dIE, + _eS, + _fB, + _eB, + _zB, + _zS, + _zI, + _zL, + _zF, + _zD, + ], + [0, 2, 64 | 0, 4, 21, 1, 1, 1, 1, 1, 1, 128 | 0, 0, 1, 0, 2, 21, 1, 1, 1, 1, 1, 1], +]; +export var EmptyStructure: StaticStructureSchema = [3, n1, _ES, 0, [], []]; +export var Float16Output: StaticStructureSchema = [3, n1, _FO, 0, [_v], [1]]; +export var FractionalSecondsOutput: StaticStructureSchema = [3, n1, _FSO, 0, [_d], [5]]; +export var GreetingWithErrorsOutput: StaticStructureSchema = [3, n1, _GWEO, 0, [_g], [0]]; +export var InvalidGreeting: StaticErrorSchema = [ + -3, + n1, + _IG, + { + [_e]: _c, + }, + [_M], + [0], +]; +TypeRegistry.for(n1).registerError(InvalidGreeting, __InvalidGreeting); + +export var OperationWithDefaultsInput: StaticStructureSchema = [ + 3, + n1, + _OWDI, + 0, + [_de, _cOD, _tLD, _oTLD], + [() => Defaults, () => ClientOptionalDefaults, 0, 1], +]; +export var OperationWithDefaultsOutput: StaticStructureSchema = [ + 3, + n1, + _OWDO, + 0, + [ + _dS, + _dB, + _dL, + _dT, + _dBe, + _dBef, + _dSe, + _dI, + _dLe, + _dF, + _dD, + _dM, + _dE, + _dIE, + _eS, + _fB, + _eB, + _zB, + _zS, + _zI, + _zL, + _zF, + _zD, + ], + [0, 2, 64 | 0, 4, 21, 1, 1, 1, 1, 1, 1, 128 | 0, 0, 1, 0, 2, 21, 1, 1, 1, 1, 1, 1], +]; +export var RecursiveShapesInputOutput: StaticStructureSchema = [ + 3, + n1, + _RSIO, + 0, + [_n], + [() => RecursiveShapesInputOutputNested1], +]; +export var RecursiveShapesInputOutputNested1: StaticStructureSchema = [ + 3, + n1, + _RSION, + 0, + [_f, _n], + [0, () => RecursiveShapesInputOutputNested2], +]; +export var RecursiveShapesInputOutputNested2: StaticStructureSchema = [ + 3, + n1, + _RSIONe, + 0, + [_b, _rM], + [0, () => RecursiveShapesInputOutputNested1], +]; +export var RpcV2CborDenseMapsInputOutput: StaticStructureSchema = [ + 3, + n1, + _RVCDMIO, + 0, + [_dSM, _dNM, _dBM, _dSMe, _dSMen], + [() => DenseStructMap, 128 | 1, 128 | 2, 128 | 0, [2, n1, _DSM, 0, 64 | 0, 64 | 0] as StaticMapSchema], +]; +export var RpcV2CborListInputOutput: StaticStructureSchema = [ + 3, + n1, + _RVCLIO, + 0, + [_sL, _sS, _iL, _bL, _tL, _eL, _iEL, _nSL, _sLt, _bLl], + [ + 64 | 0, + 64 | 0, + 64 | 1, + 64 | 2, + 64 | 4, + 64 | 0, + 64 | 1, + [1, n2, _NSL, 0, 64 | 0] as StaticListSchema, + () => StructureList, + 64 | 21, + ], +]; +export var RpcV2CborSparseMapsInputOutput: StaticStructureSchema = [ + 3, + n1, + _RVCSMIO, + 0, + [_sSM, _sNM, _sBM, _sSMp, _sSMpa], + [ + [() => SparseStructMap, 0], + [() => SparseNumberMap, 0], + [() => SparseBooleanMap, 0], + [() => SparseStringMap, 0], + [() => SparseSetMap, 0], + ], +]; +export var SimpleScalarStructure: StaticStructureSchema = [ + 3, + n1, + _SSS, + 0, + [_tBV, _fBV, _bV, _dV, _fV, _iV, _lV, _sV, _sVt, _bVl], + [2, 2, 1, 1, 1, 1, 1, 1, 0, 21], +]; +export var SimpleStructure: StaticStructureSchema = [3, n1, _SS, 0, [_v], [0]]; +export var SparseNullsOperationInputOutput: StaticStructureSchema = [ + 3, + n1, + _SNOIO, + 0, + [_sSL, _sSMp], + [ + [() => SparseStringList, 0], + [() => SparseStringMap, 0], + ], +]; +export var StructureListMember: StaticStructureSchema = [3, n1, _SLM, 0, [_a, _b_], [0, 0]]; +export var GreetingStruct: StaticStructureSchema = [3, n2, _GS, 0, [_h], [0]]; export var RpcV2ProtocolServiceException = error(_sC, "RpcV2ProtocolServiceException", 0, [], [], null); TypeRegistry.for(_sC).registerError(RpcV2ProtocolServiceException, __RpcV2ProtocolServiceException); + +export var ValidationExceptionFieldList: StaticListSchema = [1, n0, _VEFL, 0, () => ValidationExceptionField]; +export var StructureList: StaticListSchema = [1, n1, _SL, 0, () => StructureListMember]; +export var TestStringList = 64 | 0; + +export var BlobList = 64 | 21; + +export var BooleanList = 64 | 2; + +export var FooEnumList = 64 | 0; + +export var IntegerEnumList = 64 | 1; + +export var IntegerList = 64 | 1; + +export var NestedStringList: StaticListSchema = [1, n2, _NSL, 0, 64 | 0]; +export var SparseStringList: StaticListSchema = [ + 1, + n2, + _SSL, + { + [_s]: 1, + }, + 0, +]; +export var StringList = 64 | 0; + +export var StringSet = 64 | 0; + +export var TimestampList = 64 | 4; + +export var DenseBooleanMap = 128 | 2; + +export var DenseNumberMap = 128 | 1; + +export var DenseSetMap: StaticMapSchema = [2, n1, _DSM, 0, 0, 64 | 0]; +export var DenseStringMap = 128 | 0; + +export var DenseStructMap: StaticMapSchema = [2, n1, _DSMe, 0, 0, () => GreetingStruct]; +export var SparseBooleanMap: StaticMapSchema = [ + 2, + n1, + _SBM, + { + [_s]: 1, + }, + 0, + 2, +]; +export var SparseNumberMap: StaticMapSchema = [ + 2, + n1, + _SNM, + { + [_s]: 1, + }, + 0, + 1, +]; +export var SparseSetMap: StaticMapSchema = [ + 2, + n1, + _SSM, + { + [_s]: 1, + }, + 0, + 64 | 0, +]; +export var SparseStructMap: StaticMapSchema = [ + 2, + n1, + _SSMp, + { + [_s]: 1, + }, + 0, + () => GreetingStruct, +]; +export var TestStringMap = 128 | 0; + +export var SparseStringMap: StaticMapSchema = [ + 2, + n2, + _SSMpa, + { + [_s]: 1, + }, + 0, + 0, +]; +export var EmptyInputOutput: StaticOperationSchema = [9, n1, _EIO, 0, () => EmptyStructure, () => EmptyStructure]; +export var Float16: StaticOperationSchema = [9, n1, _Fl, 0, () => Unit, () => Float16Output]; +export var FractionalSeconds: StaticOperationSchema = [9, n1, _FS, 0, () => Unit, () => FractionalSecondsOutput]; +export var GreetingWithErrors: StaticOperationSchema = [9, n1, _GWE, 2, () => Unit, () => GreetingWithErrorsOutput]; +export var NoInputOutput: StaticOperationSchema = [9, n1, _NIO, 0, () => Unit, () => Unit]; +export var OperationWithDefaults: StaticOperationSchema = [ + 9, + n1, + _OWD, + 0, + () => OperationWithDefaultsInput, + () => OperationWithDefaultsOutput, +]; +export var OptionalInputOutput: StaticOperationSchema = [9, n1, _OIO, 0, () => SimpleStructure, () => SimpleStructure]; +export var RecursiveShapes: StaticOperationSchema = [ + 9, + n1, + _RS, + 0, + () => RecursiveShapesInputOutput, + () => RecursiveShapesInputOutput, +]; +export var RpcV2CborDenseMaps: StaticOperationSchema = [ + 9, + n1, + _RVCDM, + 0, + () => RpcV2CborDenseMapsInputOutput, + () => RpcV2CborDenseMapsInputOutput, +]; +export var RpcV2CborLists: StaticOperationSchema = [ + 9, + n1, + _RVCL, + 2, + () => RpcV2CborListInputOutput, + () => RpcV2CborListInputOutput, +]; +export var RpcV2CborSparseMaps: StaticOperationSchema = [ + 9, + n1, + _RVCSM, + 0, + () => RpcV2CborSparseMapsInputOutput, + () => RpcV2CborSparseMapsInputOutput, +]; +export var SimpleScalarProperties: StaticOperationSchema = [ + 9, + n1, + _SSP, + 0, + () => SimpleScalarStructure, + () => SimpleScalarStructure, +]; +export var SparseNullsOperation: StaticOperationSchema = [ + 9, + n1, + _SNO, + 0, + () => SparseNullsOperationInputOutput, + () => SparseNullsOperationInputOutput, +]; diff --git a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_1_Rpc.ts b/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_1_Rpc.ts deleted file mode 100644 index d392f856fb8..00000000000 --- a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_1_Rpc.ts +++ /dev/null @@ -1,454 +0,0 @@ -// smithy-typescript generated code -import { - ComplexError as __ComplexError, - InvalidGreeting as __InvalidGreeting, - ValidationException as __ValidationException, -} from "../models/index"; -import { - _CE, - _CNED, - _COD, - _D, - _DSM, - _DSMe, - _F, - _GS, - _GWE, - _GWEO, - _IG, - _M, - _N, - _NSL, - _OIO, - _OWD, - _OWDI, - _OWDO, - _RS, - _RSIO, - _RSION, - _RSIONe, - _RVCDM, - _RVCDMIO, - _RVCL, - _RVCLIO, - _RVCSM, - _RVCSMIO, - _SBM, - _SL, - _SLM, - _SNM, - _SNO, - _SNOIO, - _SS, - _SSL, - _SSM, - _SSMp, - _SSMpa, - _SSP, - _SSS, - _TL, - _VE, - _VEF, - _VEFL, - _a, - _b, - _bL, - _bLl, - _bV, - _bVl, - _b_, - _c, - _cOD, - _dB, - _dBM, - _dBe, - _dBef, - _dD, - _dE, - _dF, - _dI, - _dIE, - _dL, - _dLe, - _dM, - _dNM, - _dS, - _dSM, - _dSMe, - _dSMen, - _dSe, - _dT, - _dV, - _de, - _e, - _eB, - _eL, - _eS, - _f, - _fB, - _fBV, - _fL, - _fV, - _g, - _h, - _iEL, - _iL, - _iV, - _lV, - _m, - _me, - _n, - _nSL, - _oTLD, - _p, - _rM, - _s, - _sBM, - _sL, - _sLt, - _sNM, - _sS, - _sSL, - _sSM, - _sSMp, - _sSMpa, - _sV, - _sVt, - _tBV, - _tL, - _tLD, - _v, - _zB, - _zD, - _zF, - _zI, - _zL, - _zS, - n0, - n1, - n2, -} from "./schemas_0"; -import { TypeRegistry, error, list, map, op, struct } from "@smithy/core/schema"; - -/* eslint no-var: 0 */ - -export var Unit = "unit" as const; - -export var ValidationException = error( - n0, - _VE, - { - [_e]: _c, - }, - [_m, _fL], - [0, () => ValidationExceptionFieldList], - null -); -TypeRegistry.for(n0).registerError(ValidationException, __ValidationException); - -export var ValidationExceptionField = struct(n0, _VEF, 0, [_p, _m], [0, 0]); -export var ClientOptionalDefaults = struct(n1, _COD, 0, [_me], [1]); -export var ComplexError = error( - n1, - _CE, - { - [_e]: _c, - }, - [_TL, _N], - [0, () => ComplexNestedErrorData], - null -); -TypeRegistry.for(n1).registerError(ComplexError, __ComplexError); - -export var ComplexNestedErrorData = struct(n1, _CNED, 0, [_F], [0]); -export var Defaults = struct( - n1, - _D, - 0, - [ - _dS, - _dB, - _dL, - _dT, - _dBe, - _dBef, - _dSe, - _dI, - _dLe, - _dF, - _dD, - _dM, - _dE, - _dIE, - _eS, - _fB, - _eB, - _zB, - _zS, - _zI, - _zL, - _zF, - _zD, - ], - [0, 2, 64 | 0, 4, 21, 1, 1, 1, 1, 1, 1, 128 | 0, 0, 1, 0, 2, 21, 1, 1, 1, 1, 1, 1] -); -export var GreetingWithErrorsOutput = struct(n1, _GWEO, 0, [_g], [0]); -export var InvalidGreeting = error( - n1, - _IG, - { - [_e]: _c, - }, - [_M], - [0], - null -); -TypeRegistry.for(n1).registerError(InvalidGreeting, __InvalidGreeting); - -export var OperationWithDefaultsInput = struct( - n1, - _OWDI, - 0, - [_de, _cOD, _tLD, _oTLD], - [() => Defaults, () => ClientOptionalDefaults, 0, 1] -); -export var OperationWithDefaultsOutput = struct( - n1, - _OWDO, - 0, - [ - _dS, - _dB, - _dL, - _dT, - _dBe, - _dBef, - _dSe, - _dI, - _dLe, - _dF, - _dD, - _dM, - _dE, - _dIE, - _eS, - _fB, - _eB, - _zB, - _zS, - _zI, - _zL, - _zF, - _zD, - ], - [0, 2, 64 | 0, 4, 21, 1, 1, 1, 1, 1, 1, 128 | 0, 0, 1, 0, 2, 21, 1, 1, 1, 1, 1, 1] -); -export var RecursiveShapesInputOutput = struct(n1, _RSIO, 0, [_n], [() => RecursiveShapesInputOutputNested1]); -export var RecursiveShapesInputOutputNested1 = struct( - n1, - _RSION, - 0, - [_f, _n], - [0, () => RecursiveShapesInputOutputNested2] -); -export var RecursiveShapesInputOutputNested2 = struct( - n1, - _RSIONe, - 0, - [_b, _rM], - [0, () => RecursiveShapesInputOutputNested1] -); -export var RpcV2CborDenseMapsInputOutput = struct( - n1, - _RVCDMIO, - 0, - [_dSM, _dNM, _dBM, _dSMe, _dSMen], - [() => DenseStructMap, 128 | 1, 128 | 2, 128 | 0, map(n1, _DSM, 0, 0, 64 | 0)] -); -export var RpcV2CborListInputOutput = struct( - n1, - _RVCLIO, - 0, - [_sL, _sS, _iL, _bL, _tL, _eL, _iEL, _nSL, _sLt, _bLl], - [64 | 0, 64 | 0, 64 | 1, 64 | 2, 64 | 4, 64 | 0, 64 | 1, list(n2, _NSL, 0, 64 | 0), () => StructureList, 64 | 21] -); -export var RpcV2CborSparseMapsInputOutput = struct( - n1, - _RVCSMIO, - 0, - [_sSM, _sNM, _sBM, _sSMp, _sSMpa], - [ - [() => SparseStructMap, 0], - [() => SparseNumberMap, 0], - [() => SparseBooleanMap, 0], - [() => SparseStringMap, 0], - [() => SparseSetMap, 0], - ] -); -export var SimpleScalarStructure = struct( - n1, - _SSS, - 0, - [_tBV, _fBV, _bV, _dV, _fV, _iV, _lV, _sV, _sVt, _bVl], - [2, 2, 1, 1, 1, 1, 1, 1, 0, 21] -); -export var SimpleStructure = struct(n1, _SS, 0, [_v], [0]); -export var SparseNullsOperationInputOutput = struct( - n1, - _SNOIO, - 0, - [_sSL, _sSMp], - [ - [() => SparseStringList, 0], - [() => SparseStringMap, 0], - ] -); -export var StructureListMember = struct(n1, _SLM, 0, [_a, _b_], [0, 0]); -export var GreetingStruct = struct(n2, _GS, 0, [_h], [0]); -export var ValidationExceptionFieldList = list(n0, _VEFL, 0, () => ValidationExceptionField); -export var StructureList = list(n1, _SL, 0, () => StructureListMember); -export var TestStringList = 64 | 0; - -export var BlobList = 64 | 21; - -export var BooleanList = 64 | 2; - -export var FooEnumList = 64 | 0; - -export var IntegerEnumList = 64 | 1; - -export var IntegerList = 64 | 1; - -export var NestedStringList = list(n2, _NSL, 0, 64 | 0); -export var SparseStringList = list( - n2, - _SSL, - { - [_s]: 1, - }, - 0 -); -export var StringList = 64 | 0; - -export var StringSet = 64 | 0; - -export var TimestampList = 64 | 4; - -export var DenseBooleanMap = 128 | 2; - -export var DenseNumberMap = 128 | 1; - -export var DenseSetMap = map(n1, _DSM, 0, 0, 64 | 0); -export var DenseStringMap = 128 | 0; - -export var DenseStructMap = map(n1, _DSMe, 0, 0, () => GreetingStruct); -export var SparseBooleanMap = map( - n1, - _SBM, - { - [_s]: 1, - }, - 0, - 2 -); -export var SparseNumberMap = map( - n1, - _SNM, - { - [_s]: 1, - }, - 0, - 1 -); -export var SparseSetMap = map( - n1, - _SSM, - { - [_s]: 1, - }, - 0, - 64 | 0 -); -export var SparseStructMap = map( - n1, - _SSMp, - { - [_s]: 1, - }, - 0, - () => GreetingStruct -); -export var TestStringMap = 128 | 0; - -export var SparseStringMap = map( - n2, - _SSMpa, - { - [_s]: 1, - }, - 0, - 0 -); -export var GreetingWithErrors = op( - n1, - _GWE, - 2, - () => Unit, - () => GreetingWithErrorsOutput -); -export var OperationWithDefaults = op( - n1, - _OWD, - 0, - () => OperationWithDefaultsInput, - () => OperationWithDefaultsOutput -); -export var OptionalInputOutput = op( - n1, - _OIO, - 0, - () => SimpleStructure, - () => SimpleStructure -); -export var RecursiveShapes = op( - n1, - _RS, - 0, - () => RecursiveShapesInputOutput, - () => RecursiveShapesInputOutput -); -export var RpcV2CborDenseMaps = op( - n1, - _RVCDM, - 0, - () => RpcV2CborDenseMapsInputOutput, - () => RpcV2CborDenseMapsInputOutput -); -export var RpcV2CborLists = op( - n1, - _RVCL, - 2, - () => RpcV2CborListInputOutput, - () => RpcV2CborListInputOutput -); -export var RpcV2CborSparseMaps = op( - n1, - _RVCSM, - 0, - () => RpcV2CborSparseMapsInputOutput, - () => RpcV2CborSparseMapsInputOutput -); -export var SimpleScalarProperties = op( - n1, - _SSP, - 0, - () => SimpleScalarStructure, - () => SimpleScalarStructure -); -export var SparseNullsOperation = op( - n1, - _SNO, - 0, - () => SparseNullsOperationInputOutput, - () => SparseNullsOperationInputOutput -); diff --git a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_2_EmptyInputOutput.ts b/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_2_EmptyInputOutput.ts deleted file mode 100644 index 6f08d2ec392..00000000000 --- a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_2_EmptyInputOutput.ts +++ /dev/null @@ -1,14 +0,0 @@ -// smithy-typescript generated code -import { _EIO, _ES, n1 } from "./schemas_0"; -import { op, struct } from "@smithy/core/schema"; - -/* eslint no-var: 0 */ - -export var EmptyStructure = struct(n1, _ES, 0, [], []); -export var EmptyInputOutput = op( - n1, - _EIO, - 0, - () => EmptyStructure, - () => EmptyStructure -); diff --git a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_3_FractionalSeconds.ts b/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_3_FractionalSeconds.ts deleted file mode 100644 index 7f9a11ccc86..00000000000 --- a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_3_FractionalSeconds.ts +++ /dev/null @@ -1,15 +0,0 @@ -// smithy-typescript generated code -import { _FS, _FSO, _d, n1 } from "./schemas_0"; -import { Unit } from "./schemas_1_Rpc"; -import { op, struct } from "@smithy/core/schema"; - -/* eslint no-var: 0 */ - -export var FractionalSecondsOutput = struct(n1, _FSO, 0, [_d], [5]); -export var FractionalSeconds = op( - n1, - _FS, - 0, - () => Unit, - () => FractionalSecondsOutput -); diff --git a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_4_.ts b/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_4_.ts deleted file mode 100644 index e44a5e80c78..00000000000 --- a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_4_.ts +++ /dev/null @@ -1,15 +0,0 @@ -// smithy-typescript generated code -import { _FO, _Fl, _v, n1 } from "./schemas_0"; -import { Unit } from "./schemas_1_Rpc"; -import { op, struct } from "@smithy/core/schema"; - -/* eslint no-var: 0 */ - -export var Float16Output = struct(n1, _FO, 0, [_v], [1]); -export var Float16 = op( - n1, - _Fl, - 0, - () => Unit, - () => Float16Output -); diff --git a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_5_NoInputOutput.ts b/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_5_NoInputOutput.ts deleted file mode 100644 index d31aec32b48..00000000000 --- a/private/smithy-rpcv2-cbor-schema/src/schemas/schemas_5_NoInputOutput.ts +++ /dev/null @@ -1,14 +0,0 @@ -// smithy-typescript generated code -import { _NIO, n1 } from "./schemas_0"; -import { Unit } from "./schemas_1_Rpc"; -import { op } from "@smithy/core/schema"; - -/* eslint no-var: 0 */ - -export var NoInputOutput = op( - n1, - _NIO, - 0, - () => Unit, - () => Unit -); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/SchemaGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/SchemaGenerator.java index cfc1dd1d89b..6329bb04c0e 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/SchemaGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/SchemaGenerator.java @@ -275,16 +275,16 @@ private String getShapeVariableName(Shape shape) { private void writeSimpleSchema(Shape shape) { TypeScriptWriter writer = getWriter(shape.getId()); if (elision.traits.hasSchemaTraits(shape)) { - writer.addImportSubmodule("sim", "sim", TypeScriptDependency.SMITHY_CORE, "/schema"); + writer.addImport("StaticSimpleSchema", null, TypeScriptDependency.SMITHY_TYPES); writer.write(""" - export var $L = sim($L, $L, $L,""", + export var $L: StaticSimpleSchema = [0, $L, $L, $L,""", getShapeVariableName(shape), checkImportString(shape, shape.getId().getNamespace(), "n"), checkImportString(shape, shape.getId().getName()), resolveSimpleSchema(shape, shape) ); writeTraits(shape); - writer.write(");"); + writer.write("];"); } } @@ -294,15 +294,15 @@ private void writeStructureSchema(StructureShape shape) { String symbolName = reservedWords.escape(shape.getId().getName()); if (shape.hasTrait(ErrorTrait.class)) { String exceptionCtorSymbolName = "__" + symbolName; - writer.addImportSubmodule("error", "error", TypeScriptDependency.SMITHY_CORE, "/schema"); + writer.addImport("StaticErrorSchema", null, TypeScriptDependency.SMITHY_TYPES); writer.addRelativeImport( symbolName, exceptionCtorSymbolName, Paths.get("..", "models", "index") ); writer.openBlock(""" - export var $L = error($L, $L,""", - ", null);", + export var $L: StaticErrorSchema = [-3, $L, $L,""", + "];", getShapeVariableName(shape), checkImportString(shape, shape.getId().getNamespace(), "n"), checkImportString(shape, shape.getId().getName()), @@ -317,10 +317,10 @@ private void writeStructureSchema(StructureShape shape) { exceptionCtorSymbolName ); } else { - writer.addImportSubmodule("struct", "struct", TypeScriptDependency.SMITHY_CORE, "/schema"); + writer.addImport("StaticStructureSchema", null, TypeScriptDependency.SMITHY_TYPES); writer.openBlock(""" - export var $L = struct($L, $L,""", - ");", + export var $L: StaticStructureSchema = [3, $L, $L,""", + "];", getShapeVariableName(shape), checkImportString(shape, shape.getId().getNamespace(), "n"), checkImportString(shape, shape.getId().getName()), @@ -368,10 +368,10 @@ private void writeBaseError() { private void writeUnionSchema(UnionShape shape) { TypeScriptWriter writer = getWriter(shape.getId()); checkedWriteSchema(shape, () -> { - writer.addImportSubmodule("struct", "uni", TypeScriptDependency.SMITHY_CORE, "/schema"); + writer.addImport("StaticStructureSchema", null, TypeScriptDependency.SMITHY_TYPES); writer.openBlock(""" - export var $L = uni($L, $L,""", - ");", + export var $L: StaticStructureSchema = [3, $L, $L,""", + "];", getShapeVariableName(shape), checkImportString(shape, shape.getId().getNamespace(), "n"), checkImportString(shape, shape.getId().getName()), @@ -416,10 +416,10 @@ private void doWithMembers(Shape shape) { private void writeListSchema(CollectionShape shape) { TypeScriptWriter writer = getWriter(shape.getId()); checkedWriteSchema(shape, () -> { - writer.addImportSubmodule("list", "list", TypeScriptDependency.SMITHY_CORE, "/schema"); + writer.addImport("StaticListSchema", null, TypeScriptDependency.SMITHY_TYPES); writer.openBlock(""" - export var $L = list($L, $L,""", - ");", + export var $L: StaticListSchema = [1, $L, $L,""", + "];", getShapeVariableName(shape), checkImportString(shape, shape.getId().getNamespace(), "n"), checkImportString(shape, shape.getId().getName()), @@ -434,10 +434,10 @@ private void writeListSchema(CollectionShape shape) { private void writeMapSchema(MapShape shape) { TypeScriptWriter writer = getWriter(shape.getId()); checkedWriteSchema(shape, () -> { - writer.addImportSubmodule("map", "map", TypeScriptDependency.SMITHY_CORE, "/schema"); + writer.addImport("StaticMapSchema", null, TypeScriptDependency.SMITHY_TYPES); writer.openBlock(""" - export var $L = map($L, $L,""", - ");", + export var $L: StaticMapSchema = [2, $L, $L,""", + "];", getShapeVariableName(shape), checkImportString(shape, shape.getId().getNamespace(), "n"), checkImportString(shape, shape.getId().getName()), @@ -503,10 +503,10 @@ private void doWithMember(Shape shape, MemberShape keyShape, MemberShape memberS private void writeOperationSchema(OperationShape shape) { TypeScriptWriter writer = getWriter(shape.getId()); - writer.addImportSubmodule("op", "op", TypeScriptDependency.SMITHY_CORE, "/schema"); + writer.addImport("StaticOperationSchema", null, TypeScriptDependency.SMITHY_TYPES); writer.openBlock(""" - export var $L = op($L, $L,""", - ");", + export var $L: StaticOperationSchema = [9, $L, $L,""", + "];", getShapeVariableName(shape), checkImportString(shape, shape.getId().getNamespace(), "n"), checkImportString(shape, shape.getId().getName()), @@ -669,21 +669,28 @@ private String resolveSimpleSchema(Shape context, Shape shape) { */ private String resolveSimpleSchemaNestedContainer(Shape context, Shape shape, TypeScriptWriter writer) { Shape contained; - String factory; + String staticTypePrefix; String sentinel; - String keyMemberSchema; + String keySchema = ""; + String valueSchema; + String as; switch (shape.getType()) { case LIST -> { contained = shape.asListShape().get().getMember(); - factory = "list"; - keyMemberSchema = ""; + staticTypePrefix = "([1, "; + valueSchema = ""; sentinel = "64"; + writer.addImport("StaticListSchema", null, TypeScriptDependency.SMITHY_TYPES); + as = " as StaticListSchema)"; } case MAP -> { contained = shape.asMapShape().get().getValue(); - factory = "map"; - keyMemberSchema = this.resolveSimpleSchema(context, shape.asMapShape().get().getKey()) + ", "; + staticTypePrefix = "([2, "; + keySchema = this.resolveSimpleSchema(context, shape.asMapShape().get().getKey()) + ", "; + valueSchema = this.resolveSimpleSchema(context, shape.asMapShape().get().getValue()) + ", "; sentinel = "128"; + writer.addImport("StaticMapSchema", null, TypeScriptDependency.SMITHY_TYPES); + as = " as StaticMapSchema)"; } default -> { throw new IllegalArgumentException( @@ -696,19 +703,18 @@ private String resolveSimpleSchemaNestedContainer(Shape context, Shape shape, Ty } if (contained.isListShape()) { - writer.addImportSubmodule(factory, factory, TypeScriptDependency.SMITHY_CORE, "/schema"); String schemaVarName = checkImportString(context, shape.getId().getName()); - return factory + "(" + return staticTypePrefix + checkImportString(context, shape.getId().getNamespace(), "n") + ", " + schemaVarName + ", 0, " - + keyMemberSchema - + this.resolveSimpleSchema(context, contained) + ")"; + + valueSchema + + this.resolveSimpleSchema(context, contained) + "]" + as; } else if (contained.isMapShape()) { - writer.addImportSubmodule(factory, factory, TypeScriptDependency.SMITHY_CORE, "/schema"); String schemaVarName = checkImportString(context, shape.getId().getName()); - return factory + "(" + return staticTypePrefix + checkImportString(context, shape.getId().getNamespace(), "n") + ", " + schemaVarName + ", 0, " - + keyMemberSchema - + this.resolveSimpleSchema(context, contained) + ")"; + + keySchema + + valueSchema + + this.resolveSimpleSchema(context, contained) + "]" + as; } else { return sentinel + "|" + this.resolveSimpleSchema(context, contained); } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/ShapeGroupingIndex.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/ShapeGroupingIndex.java index ba43550fcd0..98d92bee201 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/ShapeGroupingIndex.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/schema/ShapeGroupingIndex.java @@ -85,11 +85,20 @@ public static ShapeGroupingIndex of(Model model) { * @return the group name (filename) of the schema group for the given shape. */ public String getGroup(ShapeId id) { + /* + As of the introduction of static schemas, we don't need to bucket schemas into + different files anymore. + todo: remove usage of this class. + */ + return getBaseGroup(); + + /* if (!shapeToOperationalGroup.containsKey(id)) { return getBaseGroup(); } TreeSet operations = shapeToOperationalGroup.get(id); return hashOperationSet(operations); + */ } /** From e9420ca3031444bd654127b809364a6bb3ab6d63 Mon Sep 17 00:00:00 2001 From: George Fu Date: Fri, 10 Oct 2025 13:15:44 -0400 Subject: [PATCH 11/13] chore: de-namespace types (#1741) --- .changeset/eight-ducks-rush.md | 2 + .../schema/schemas/NormalizedSchema.ts | 19 +++++--- packages/types/src/schema/static-schemas.ts | 46 +++++++++++++------ 3 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 .changeset/eight-ducks-rush.md diff --git a/.changeset/eight-ducks-rush.md b/.changeset/eight-ducks-rush.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/eight-ducks-rush.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts index 2fd34fb2c43..25941d294ff 100644 --- a/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts +++ b/packages/core/src/submodules/schema/schemas/NormalizedSchema.ts @@ -18,7 +18,12 @@ import type { StaticMapSchema, StaticOperationSchema, StaticSchema, - StaticSchemaId, + StaticSchemaIdError, + StaticSchemaIdList, + StaticSchemaIdMap, + StaticSchemaIdOperation, + StaticSchemaIdSimple, + StaticSchemaIdStruct, StaticSimpleSchema, StaticStructureSchema, StreamingBlobSchema, @@ -445,12 +450,12 @@ export function hydrate( const [id, ...rest] = ss; return ( { - [0 satisfies StaticSchemaId.Simple]: sim, - [1 satisfies StaticSchemaId.List]: list, - [2 satisfies StaticSchemaId.Map]: map, - [3 satisfies StaticSchemaId.Struct]: struct, - [-3 satisfies StaticSchemaId.Error]: error, - [9 satisfies StaticSchemaId.Operation]: op, + [0 satisfies StaticSchemaIdSimple]: sim, + [1 satisfies StaticSchemaIdList]: list, + [2 satisfies StaticSchemaIdMap]: map, + [3 satisfies StaticSchemaIdStruct]: struct, + [-3 satisfies StaticSchemaIdError]: error, + [9 satisfies StaticSchemaIdOperation]: op, }[id] as Function ).call(null, ...rest); } diff --git a/packages/types/src/schema/static-schemas.ts b/packages/types/src/schema/static-schemas.ts index 77bacf32da1..fea9fb232a3 100644 --- a/packages/types/src/schema/static-schemas.ts +++ b/packages/types/src/schema/static-schemas.ts @@ -8,14 +8,32 @@ import type { SchemaRef, SchemaTraits } from "../schema/schema"; /** * @alpha */ -export namespace StaticSchemaId { - export type Simple = 0; - export type List = 1; - export type Map = 2; - export type Struct = 3; - export type Error = -3; - export type Operation = 9; -} +export type StaticSchemaIdSimple = 0; + +/** + * @alpha + */ +export type StaticSchemaIdList = 1; + +/** + * @alpha + */ +export type StaticSchemaIdMap = 2; + +/** + * @alpha + */ +export type StaticSchemaIdStruct = 3; + +/** + * @alpha + */ +export type StaticSchemaIdError = -3; + +/** + * @alpha + */ +export type StaticSchemaIdOperation = 9; /** * @alpha @@ -41,23 +59,23 @@ export type ShapeNamespace = string; /** * @alpha */ -export type StaticSimpleSchema = [StaticSchemaId.Simple, ShapeNamespace, ShapeName, SchemaRef, SchemaTraits]; +export type StaticSimpleSchema = [StaticSchemaIdSimple, ShapeNamespace, ShapeName, SchemaRef, SchemaTraits]; /** * @alpha */ -export type StaticListSchema = [StaticSchemaId.List, ShapeNamespace, ShapeName, SchemaTraits, SchemaRef]; +export type StaticListSchema = [StaticSchemaIdList, ShapeNamespace, ShapeName, SchemaTraits, SchemaRef]; /** * @alpha */ -export type StaticMapSchema = [StaticSchemaId.Map, ShapeNamespace, ShapeName, SchemaTraits, SchemaRef, SchemaRef]; +export type StaticMapSchema = [StaticSchemaIdMap, ShapeNamespace, ShapeName, SchemaTraits, SchemaRef, SchemaRef]; /** * @alpha */ export type StaticStructureSchema = [ - StaticSchemaId.Struct, + StaticSchemaIdStruct, ShapeNamespace, ShapeName, SchemaTraits, @@ -69,7 +87,7 @@ export type StaticStructureSchema = [ * @alpha */ export type StaticErrorSchema = [ - StaticSchemaId.Error, + StaticSchemaIdError, ShapeNamespace, ShapeName, SchemaTraits, @@ -81,7 +99,7 @@ export type StaticErrorSchema = [ * @alpha */ export type StaticOperationSchema = [ - StaticSchemaId.Operation, + StaticSchemaIdOperation, ShapeNamespace, ShapeName, SchemaTraits, From 03c54b9838daf65a2d067e0636890286ce2651a7 Mon Sep 17 00:00:00 2001 From: John Lwin Date: Fri, 10 Oct 2025 12:48:22 -0700 Subject: [PATCH 12/13] improve memberDocs deprecatedTrait with fallback message --- .../src/commands/GetNumbersCommand.ts | 2 ++ private/my-local-model/src/models/models_0.ts | 15 +++++++++++++++ private/my-local-model/src/protocols/Rpcv2cbor.ts | 2 ++ .../typescript/codegen/TypeScriptWriter.java | 10 ++++++++-- .../model/my-local-model/main.smithy | 9 +++++++++ 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/private/my-local-model/src/commands/GetNumbersCommand.ts b/private/my-local-model/src/commands/GetNumbersCommand.ts index 356bcb5ad2e..e645840a1c0 100644 --- a/private/my-local-model/src/commands/GetNumbersCommand.ts +++ b/private/my-local-model/src/commands/GetNumbersCommand.ts @@ -40,6 +40,8 @@ export interface GetNumbersCommandOutput extends GetNumbersResponse, __MetadataB * const input = { // GetNumbersRequest * bigDecimal: Number("bigdecimal"), * bigInteger: Number("bigint"), + * fieldWithoutMessage: "STRING_VALUE", + * fieldWithMessage: "STRING_VALUE", * }; * const command = new GetNumbersCommand(input); * const response = await client.send(command); diff --git a/private/my-local-model/src/models/models_0.ts b/private/my-local-model/src/models/models_0.ts index 75519962b58..aad247f126f 100644 --- a/private/my-local-model/src/models/models_0.ts +++ b/private/my-local-model/src/models/models_0.ts @@ -39,6 +39,21 @@ export class CodedThrottlingError extends __BaseException { export interface GetNumbersRequest { bigDecimal?: NumericValue | undefined; bigInteger?: bigint | undefined; + /** + * This is deprecated documentation annotation + * + * @deprecated see description + * @public + */ + fieldWithoutMessage?: string | undefined; + + /** + * This is deprecated documentation annotation + * + * @deprecated This field has been deprecated + * @public + */ + fieldWithMessage?: string | undefined; } /** diff --git a/private/my-local-model/src/protocols/Rpcv2cbor.ts b/private/my-local-model/src/protocols/Rpcv2cbor.ts index 7c8ce383bd7..7d5d91d6f80 100644 --- a/private/my-local-model/src/protocols/Rpcv2cbor.ts +++ b/private/my-local-model/src/protocols/Rpcv2cbor.ts @@ -296,6 +296,8 @@ const se_GetNumbersRequest = (input: GetNumbersRequest, context: __SerdeContext) return take(input, { bigDecimal: __nv, bigInteger: [], + fieldWithMessage: [], + fieldWithoutMessage: [], }); }; diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java index 5b5d7e61437..c83349a9ed0 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java @@ -225,7 +225,7 @@ boolean writeShapeDocs(Shape shape, UnaryOperator preprocessor) { if (shape.getTrait(DeprecatedTrait.class).isPresent()) { DeprecatedTrait deprecatedTrait = shape.expectTrait(DeprecatedTrait.class); String deprecationMessage = deprecatedTrait.getMessage() - .orElse("see description"); + .orElse("deprecated"); String deprecationAnnotation = "@deprecated " + deprecationMessage; docs = docs + "\n\n" + deprecationAnnotation; } @@ -265,7 +265,13 @@ boolean writeMemberDocs(Model model, MemberShape member) { docs = docs.replace("{", "\\{") .replace("}", "\\}"); if (member.getTrait(DeprecatedTrait.class).isPresent() || isTargetDeprecated(model, member)) { - docs = docs + "\n\n@deprecated"; + DeprecatedTrait deprecatedTrait = member.getTrait(DeprecatedTrait.class) + .or(() -> model.expectShape(member.getTarget()).getTrait(DeprecatedTrait.class)) + .orElseThrow(); + String deprecationMessage = deprecatedTrait.getMessage() + .orElse("deprecated"); + String deprecationAnnotation = "@deprecated " + deprecationMessage; + docs = docs + "\n\n" + deprecationAnnotation; } docs = addReleaseTag(member, docs); writeDocs(docs); diff --git a/smithy-typescript-protocol-test-codegen/model/my-local-model/main.smithy b/smithy-typescript-protocol-test-codegen/model/my-local-model/main.smithy index d7cb9f671d9..0d26fe5db2b 100644 --- a/smithy-typescript-protocol-test-codegen/model/my-local-model/main.smithy +++ b/smithy-typescript-protocol-test-codegen/model/my-local-model/main.smithy @@ -29,7 +29,16 @@ operation GetNumbers { @input structure GetNumbersRequest { bigDecimal: BigDecimal + bigInteger: BigInteger + + @documentation("This is deprecated documentation annotation") + @deprecated + fieldWithoutMessage: String + + @documentation("This is deprecated documentation annotation") + @deprecated(message: "This field has been deprecated", since: "3.0") + fieldWithMessage: String } @output From da8622ed63ecec72621a152bd6b10b97255dcdb8 Mon Sep 17 00:00:00 2001 From: John Lwin Date: Fri, 10 Oct 2025 13:17:43 -0700 Subject: [PATCH 13/13] chore: update default message to deprecated --- private/my-local-model/src/models/models_0.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private/my-local-model/src/models/models_0.ts b/private/my-local-model/src/models/models_0.ts index aad247f126f..616868dacbe 100644 --- a/private/my-local-model/src/models/models_0.ts +++ b/private/my-local-model/src/models/models_0.ts @@ -42,7 +42,7 @@ export interface GetNumbersRequest { /** * This is deprecated documentation annotation * - * @deprecated see description + * @deprecated deprecated * @public */ fieldWithoutMessage?: string | undefined;