@typespec/http-server-js
Version:
TypeSpec HTTP server code generator for JavaScript
796 lines (564 loc) • 27.9 kB
text/typescript
import { ModelProperty, NoTarget, Scalar } from "@typespec/compiler";
import { BasicTestRunner, createTestRunner } from "@typespec/compiler/testing";
import { deepStrictEqual, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { getJsScalar } from "../src/common/scalar.js";
import { createPathCursor, JsContext, Module } from "../src/ctx.js";
import { module as dateTimeModule } from "../generated-defs/helpers/datetime.js";
import { module as temporalHelpersModule } from "../generated-defs/helpers/temporal/native.js";
import { module as temporalPolyfillHelpersModule } from "../generated-defs/helpers/temporal/polyfill.js";
import { JsEmitterOptions } from "../src/lib.js";
describe("scalar", () => {
let runner: BasicTestRunner;
beforeEach(async () => {
runner = await createTestRunner();
});
function createFakeModule(options?: JsEmitterOptions): [JsContext, Module] {
const module: Module = {
name: "example",
cursor: createPathCursor(),
imports: [],
declarations: [],
};
// Min context
const ctx: JsContext = {
program: runner.program,
rootModule: module,
options: options ?? {},
} as JsContext;
return [ctx, module];
}
async function getScalar(...names: string[]): Promise<Scalar[]> {
const { test } = (await runner.compile(`
model Example {
test: [${names.join(", ")}];
}
`)) as { test: ModelProperty };
if (test.type.kind !== "Tuple") {
throw new Error("Expected tuple type");
}
if (!test.type.values.every((t) => t.kind === "Scalar")) {
throw new Error("Expected scalar types only");
}
return test.type.values as Scalar[];
}
it("has no-op encoding for string", async () => {
const [string] = await getScalar("TypeSpec.string");
const [ctx, mod] = createFakeModule();
const jsScalar = getJsScalar(ctx, mod, string, NoTarget);
strictEqual(jsScalar.type, "string");
strictEqual(jsScalar.getEncoding("default", string)?.encode("asdf"), "(asdf)");
strictEqual(mod.imports.length, 0);
});
it("correctly encodes and decodes all numbers using default string encoding", async () => {
const [string, ...numbers] = await getScalar(
"string",
"float32",
"float64",
"int8",
"int16",
"int32",
"uint8",
"uint16",
"uint32",
"safeint",
);
const [ctx, mod] = createFakeModule();
for (const number of numbers) {
const jsScalar = getJsScalar(ctx, mod, number, NoTarget);
strictEqual(jsScalar.type, "number");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "globalThis.String((asdf))");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "globalThis.Number((asdf))");
}
});
it("encodes and decodes types that coerce to bigint using default string encoding", async () => {
const [string, ...bigints] = await getScalar("string", "uint64", "int64", "integer");
const [ctx, mod] = createFakeModule();
for (const bigint of bigints) {
const jsScalar = getJsScalar(ctx, mod, bigint, NoTarget);
strictEqual(jsScalar.type, "bigint");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "globalThis.String((asdf))");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "globalThis.BigInt((asdf))");
}
});
it("bytes base64 encoding", async () => {
const [string, bytes] = await getScalar("TypeSpec.string", "TypeSpec.bytes");
const [ctx, mod] = createFakeModule();
const jsScalar = getJsScalar(ctx, mod, bytes, NoTarget);
strictEqual(jsScalar.type, "Uint8Array");
const encoding = jsScalar.getEncoding("base64", string);
if (!encoding) {
throw new Error("Expected base64 encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(
encoded,
"((asdf) instanceof globalThis.Buffer ? (asdf) : globalThis.Buffer.from((asdf))).toString('base64')",
);
const decoded = encoding.decode("asdf");
strictEqual(decoded, "globalThis.Buffer.from((asdf), 'base64')");
});
it("bytes base64url encoding", async () => {
const [string, bytes] = await getScalar("TypeSpec.string", "TypeSpec.bytes");
const [ctx, mod] = createFakeModule();
const jsScalar = getJsScalar(ctx, mod, bytes, NoTarget);
strictEqual(jsScalar.type, "Uint8Array");
const encoding = jsScalar.getEncoding("base64url", string);
if (!encoding) {
throw new Error("Expected base64url encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(
encoded,
"globalThis.encodeURIComponent((((asdf)) instanceof globalThis.Buffer ? ((asdf)) : globalThis.Buffer.from(((asdf)))).toString('base64'))",
);
const decoded = encoding.decode("asdf");
strictEqual(
decoded,
"globalThis.Buffer.from((globalThis.decodeURIComponent((asdf))), 'base64')",
);
});
describe("date/time/duration types", () => {
describe("mode: temporal", () => {
const options: JsEmitterOptions = {
"no-format": false,
"omit-unreachable-types": false,
express: false,
datetime: "temporal",
};
describe("date", () => {
it("produces correct parse template for ISO8601 utcDateTime", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
"globalThis.Temporal.Instant.from((asdf))",
);
deepStrictEqual(mod.imports, []);
});
it("produces correct write template for ISO8601 utcDateTime", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
"((asdf)).toString()",
);
deepStrictEqual(mod.imports, []);
});
it("allows default string encoding through via", async () => {
const [utcDateTime, string] = await getScalar("utcDateTime", "string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "(((asdf))).toString()");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "globalThis.Temporal.Instant.from(((asdf)))");
});
it("transcodes to rfc7231", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
const encoding = jsScalar.getEncoding("rfc7231", string);
if (!encoding) {
throw new Error("Expected rfc7231 encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "formatHttpDate((asdf))");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "parseHttpDate((asdf))");
strictEqual(mod.imports[0].from, temporalHelpersModule);
deepStrictEqual(mod.imports[0].binder, ["formatHttpDate"]);
strictEqual(mod.imports[1].from, temporalHelpersModule);
deepStrictEqual(mod.imports[1].binder, ["parseHttpDate"]);
});
describe("ZonedDateTime", () => {
// Same as above, but with TypeSpec.offsetDateTime represented as Temporal.ZonedDateTime
it("produces correct parse template for ISO8601 offsetDateTime", async () => {
const [offsetDateTime, string] = await getScalar(
"TypeSpec.offsetDateTime",
"TypeSpec.string",
);
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
"globalThis.Temporal.ZonedDateTime.from((asdf))",
);
deepStrictEqual(mod.imports, []);
});
it("produces correct write template for ISO8601 offsetDateTime", async () => {
const [offsetDateTime, string] = await getScalar(
"TypeSpec.offsetDateTime",
"TypeSpec.string",
);
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
"((asdf)).toString()",
);
deepStrictEqual(mod.imports, []);
});
it("allows default string encoding through via", async () => {
const [offsetDateTime, string] = await getScalar("offsetDateTime", "string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "(((asdf))).toString()");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "globalThis.Temporal.ZonedDateTime.from(((asdf)))");
});
it("transcodes to rfc7231", async () => {
const [offsetDateTime, string] = await getScalar(
"TypeSpec.offsetDateTime",
"TypeSpec.string",
);
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
const encoding = jsScalar.getEncoding("rfc7231", string);
if (!encoding) {
throw new Error("Expected rfc7231 encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "formatHttpDate(((asdf)).toInstant())");
const decoded = encoding.decode("asdf");
strictEqual(decoded, 'parseHttpDate((asdf)).toZonedDateTimeISO("UTC")');
strictEqual(mod.imports[0].from, temporalHelpersModule);
deepStrictEqual(mod.imports[0].binder, ["formatHttpDate"]);
strictEqual(mod.imports[1].from, temporalHelpersModule);
deepStrictEqual(mod.imports[1].binder, ["parseHttpDate"]);
});
});
});
});
describe("mode: temporal-polyfill", () => {
const options: JsEmitterOptions = {
"no-format": false,
"omit-unreachable-types": false,
express: false,
datetime: "temporal-polyfill",
};
describe("date", () => {
it("produces correct parse template for ISO8601 utcDateTime", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
"Temporal.Instant.from((asdf))",
);
strictEqual(mod.imports[0].from, "temporal-polyfill");
deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
});
it("produces correct write template for ISO8601 utcDateTime", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
"((asdf)).toString()",
);
strictEqual(mod.imports[0].from, "temporal-polyfill");
deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
});
it("allows default string encoding through via", async () => {
const [utcDateTime, string] = await getScalar("utcDateTime", "string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "(((asdf))).toString()");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "Temporal.Instant.from(((asdf)))");
});
it("transcodes to rfc7231", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.Instant");
const encoding = jsScalar.getEncoding("rfc7231", string);
if (!encoding) {
throw new Error("Expected rfc7231 encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "formatHttpDate((asdf))");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "parseHttpDate((asdf))");
strictEqual(mod.imports[0].from, "temporal-polyfill");
deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
strictEqual(mod.imports[1].from, temporalPolyfillHelpersModule);
deepStrictEqual(mod.imports[1].binder, ["formatHttpDate"]);
strictEqual(mod.imports[2].from, temporalPolyfillHelpersModule);
deepStrictEqual(mod.imports[2].binder, ["parseHttpDate"]);
});
describe("ZonedDateTime", () => {
// Same as above, but with TypeSpec.offsetDateTime represented as Temporal.ZonedDateTime
it("produces correct parse template for ISO8601 offsetDateTime", async () => {
const [offsetDateTime, string] = await getScalar(
"TypeSpec.offsetDateTime",
"TypeSpec.string",
);
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
"Temporal.ZonedDateTime.from((asdf))",
);
strictEqual(mod.imports[0].from, "temporal-polyfill");
deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
});
it("produces correct write template for ISO8601 offsetDateTime", async () => {
const [offsetDateTime, string] = await getScalar(
"TypeSpec.offsetDateTime",
"TypeSpec.string",
);
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
"((asdf)).toString()",
);
strictEqual(mod.imports[0].from, "temporal-polyfill");
deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
});
it("allows default string encoding through via", async () => {
const [offsetDateTime, string] = await getScalar("offsetDateTime", "string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "(((asdf))).toString()");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "Temporal.ZonedDateTime.from(((asdf)))");
});
it("transcodes to rfc7231", async () => {
const [offsetDateTime, string] = await getScalar(
"TypeSpec.offsetDateTime",
"TypeSpec.string",
);
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
const encoding = jsScalar.getEncoding("rfc7231", string);
if (!encoding) {
throw new Error("Expected rfc7231 encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "formatHttpDate(((asdf)).toInstant())");
const decoded = encoding.decode("asdf");
strictEqual(decoded, 'parseHttpDate((asdf)).toZonedDateTimeISO("UTC")');
strictEqual(mod.imports[0].from, "temporal-polyfill");
deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
strictEqual(mod.imports[1].from, temporalPolyfillHelpersModule);
deepStrictEqual(mod.imports[1].binder, ["formatHttpDate"]);
strictEqual(mod.imports[2].from, temporalPolyfillHelpersModule);
deepStrictEqual(mod.imports[2].binder, ["parseHttpDate"]);
});
});
});
});
describe("mode: date-duration", () => {
const options: JsEmitterOptions = {
"no-format": false,
"omit-unreachable-types": false,
express: false,
datetime: "date-duration",
};
describe("date", () => {
it("produces correct parse template for ISO8601 utcDateTime", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Date");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
"new globalThis.Date((asdf))",
);
deepStrictEqual(mod.imports, []);
});
it("produces correct write template for ISO8601 utcDateTime", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Date");
strictEqual(
jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
"((asdf)).toISOString()",
);
deepStrictEqual(mod.imports, []);
});
it("allows default string encoding through via", async () => {
const [utcDateTime, string] = await getScalar("utcDateTime", "string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Date");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "(((asdf))).toISOString()");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "new globalThis.Date(((asdf)))");
});
it("correctly encodes and decodes rfc7231 date", async () => {
const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
strictEqual(jsScalar.type, "Date");
const encoding = jsScalar.getEncoding("rfc7231", string);
if (!encoding) {
throw new Error("Expected rfc7231 encoding");
}
const encoded = encoding.encode("asdf");
strictEqual(encoded, "((asdf)).toUTCString()");
const decoded = encoding.decode("asdf");
strictEqual(decoded, "new globalThis.Date((asdf))");
deepStrictEqual(mod.imports, []);
});
});
describe("duration", () => {
it("produces correct parse template for ISO8601 duration", async () => {
const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
strictEqual(jsScalar.type, "Duration");
strictEqual(
jsScalar.getEncoding("ISO8601", string)?.decode("asdf"),
"Duration.parseISO8601((asdf))",
);
strictEqual(mod.imports[0].from, dateTimeModule);
deepStrictEqual(mod.imports[0].binder, ["Duration"]);
});
it("produces correct write template for ISO8601 duration", async () => {
const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
strictEqual(jsScalar.type, "Duration");
strictEqual(
jsScalar.getEncoding("ISO8601", string)?.encode("asdf"),
"Duration.toISO8601((asdf))",
);
strictEqual(mod.imports[0].from, dateTimeModule);
deepStrictEqual(mod.imports[0].binder, ["Duration"]);
});
it("can parse and write ISO8601 duration", async () => {
const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
strictEqual(jsScalar.type, "Duration");
const encoding = jsScalar.getEncoding("ISO8601", string);
if (!encoding) {
throw new Error("Expected ISO8601 encoding");
}
const encoded = encoding.encode("duration");
strictEqual(encoded, "Duration.toISO8601((duration))");
const decoded = encoding.decode('"P1Y2M3DT4H5M6S"');
strictEqual(decoded, 'Duration.parseISO8601(("P1Y2M3DT4H5M6S"))');
strictEqual(mod.imports[0].from, dateTimeModule);
deepStrictEqual(mod.imports[0].binder, ["Duration"]);
});
it("allows default string encoding through via", async () => {
const [Duration, string] = await getScalar("duration", "string");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
strictEqual(jsScalar.type, "Duration");
const encoding = jsScalar.getEncoding("default", string);
if (!encoding) {
throw new Error("Expected default encoding");
}
const encoded = encoding.encode("duration");
strictEqual(encoded, "Duration.toISO8601(((duration)))");
const decoded = encoding.decode("duration");
strictEqual(decoded, "Duration.parseISO8601(((duration)))");
});
it("allows encoding seconds to number types", async () => {
const [Duration, int32, uint32] = await getScalar("duration", "int32", "uint32");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
strictEqual(jsScalar.type, "Duration");
const encodingInt32 = jsScalar.getEncoding("seconds", int32);
if (!encodingInt32) {
throw new Error("Expected seconds encoding int32");
}
const encodedInt32 = encodingInt32.encode("duration");
strictEqual(encodedInt32, "Duration.totalSeconds((duration))");
const decodedInt32 = encodingInt32.decode("duration");
strictEqual(decodedInt32, "Duration.fromTotalSeconds((duration))");
const encodingUint32 = jsScalar.getEncoding("seconds", uint32);
if (!encodingUint32) {
throw new Error("Expected seconds encoding uint32");
}
const encodedUint32 = encodingUint32.encode("duration");
strictEqual(encodedUint32, "Duration.totalSeconds((duration))");
const decodedUint32 = encodingUint32.decode("duration");
strictEqual(decodedUint32, "Duration.fromTotalSeconds((duration))");
});
it("allows encoding seconds to bigint types", async () => {
const [Duration, int64, uint64] = await getScalar("duration", "int64", "uint64");
const [ctx, mod] = createFakeModule(options);
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
strictEqual(jsScalar.type, "Duration");
const encodingInt64 = jsScalar.getEncoding("seconds", int64);
if (!encodingInt64) {
throw new Error("Expected seconds encoding int64");
}
const encodedInt64 = encodingInt64.encode("duration");
strictEqual(encodedInt64, "Duration.totalSecondsBigInt((duration))");
const decodedInt64 = encodingInt64.decode("duration");
strictEqual(decodedInt64, "Duration.fromTotalSeconds(globalThis.Number((duration)))");
const encodingUint64 = jsScalar.getEncoding("seconds", uint64);
if (!encodingUint64) {
throw new Error("Expected seconds encoding uint64");
}
const encodedUint64 = encodingUint64.encode("duration");
strictEqual(encodedUint64, "Duration.totalSecondsBigInt((duration))");
const decodedUint64 = encodingUint64.decode("duration");
strictEqual(decodedUint64, "Duration.fromTotalSeconds(globalThis.Number((duration)))");
});
});
});
});
});