Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/http-server-js"
---

Added support for encoding and decoding scalar types and default encodings prescribed by convention.
263 changes: 263 additions & 0 deletions packages/http-server-js/generated-defs/helpers/datetime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// Copyright (c) Microsoft Corporation
// Licensed under the MIT license.

import { Module } from "../../src/ctx.js";

export let module: Module = undefined as any;

// prettier-ignore
const lines = [
"// Copyright (c) Microsoft Corporation",
"// Licensed under the MIT license.",
"",
"// #region Duration",
"",
"/**",
" * Regular expression for matching ISO8601 duration strings.",
" *",
" * Yields:",
" * - 0: the full match",
" * - 1: the sign (optional)",
" * - 2: years (optional)",
" * - 3: months (optional)",
" * - 4: weeks (optional)",
" * - 5: days (optional)",
" * - 6: hours (optional)",
" * - 7: minutes (optional)",
" * - 8: seconds (optional)",
" */",
"const ISO8601_DURATION_REGEX =",
" /^(-)?P(?:((?:\\d*[.,])?\\d+)Y)?(?:((?:\\d*[.,])?\\d+)M)?(?:((?:\\d*[.,])?\\d+)W)?(?:((?:\\d*[.,])?\\d+)D)?(?:T(?:((?:\\d*[.,])?\\d+)H)?(?:((?:\\d*[.,])?\\d+)M)?(?:((?:\\d*[.,])?\\d+)S)?)?$/;",
"",
"/**",
" * A duration of time, measured in years, months, weeks, days, hours, minutes, and seconds.",
" *",
" * The values may be fractional and are not normalized (e.g. 36 hours is not the same duration as 1 day and 12 hours",
" * when accounting for Daylight Saving Time changes or leap seconds).",
" *",
" * @see https://en.wikipedia.org/wiki/ISO_8601#Durations",
" */",
"export interface Duration {",
" /**",
" * \"+\" if the duration is positive, \"-\" if the duration is negative.",
" */",
" sign: \"+\" | \"-\";",
" /**",
" * The number of years in the duration.",
" */",
" years: number;",
" /**",
" * The number of months in the duration.",
" */",
" months: number;",
" /**",
" * The number of weeks in the duration.",
" */",
" weeks: number;",
" /**",
" * The number of days in the duration.",
" */",
" days: number;",
" /**",
" * The number of hours in the duration.",
" */",
" hours: number;",
" /**",
" * The number of minutes in the duration.",
" */",
" minutes: number;",
" /**",
" * The number of seconds in the duration.",
" */",
" seconds: number;",
"}",
"",
"export const Duration = Object.freeze({",
" /**",
" * Parses an ISO8601 duration string into an object.",
" *",
" * @see https://en.wikipedia.org/wiki/ISO_8601#Durations",
" *",
" * @param duration - the duration string to parse",
" * @returns an object containing the parsed duration",
" */",
" parseISO8601(duration: string, maxLength: number = 100): Duration {",
" duration = duration.trim();",
" if (duration.length > maxLength)",
" throw new Error(`ISO8601 duration string is too long: ${duration}`);",
"",
" const match = duration.match(ISO8601_DURATION_REGEX);",
"",
" if (!match) throw new Error(`Invalid ISO8601 duration: ${duration}`);",
"",
" return {",
" sign: match[1] === undefined ? \"+\" : (match[1] as Duration[\"sign\"]),",
" years: parseFloatNormal(match[2]),",
" months: parseFloatNormal(match[3]),",
" weeks: parseFloatNormal(match[4]),",
" days: parseFloatNormal(match[5]),",
" hours: parseFloatNormal(match[6]),",
" minutes: parseFloatNormal(match[7]),",
" seconds: parseFloatNormal(match[8]),",
" };",
"",
" function parseFloatNormal(match: string | undefined): number {",
" if (match === undefined) return 0;",
"",
" const normalized = match.replace(\",\", \".\");",
"",
" const parsed = parseFloat(normalized);",
"",
" if (isNaN(parsed))",
" throw new Error(`Unreachable: Invalid number in ISO8601 duration string: ${match}`);",
"",
" return parsed;",
" }",
" },",
" /**",
" * Writes a Duration to an ISO8601 duration string.",
" *",
" * @see https://en.wikipedia.org/wiki/ISO_8601#Durations",
" *",
" * @param duration - the duration to write to a string",
" * @returns a string in ISO8601 duration format",
" */",
" toISO8601(duration: Duration): string {",
" const sign = duration.sign === \"+\" ? \"\" : \"-\";",
"",
" const years =",
" duration.years !== 0 && !isNaN(Number(duration.years)) ? `${duration.years}Y` : \"\";",
" const months =",
" duration.months !== 0 && !isNaN(Number(duration.months)) ? `${duration.months}M` : \"\";",
" const weeks =",
" duration.weeks !== 0 && !isNaN(Number(duration.weeks)) ? `${duration.weeks}W` : \"\";",
" const days = duration.days !== 0 && !isNaN(Number(duration.days)) ? `${duration.days}D` : \"\";",
"",
" let time = \"\";",
"",
" const _hours = duration.hours !== 0 && !isNaN(Number(duration.hours));",
" const _minutes = duration.minutes !== 0 && !isNaN(Number(duration.minutes));",
" const _seconds = duration.seconds !== 0 && !isNaN(Number(duration.seconds));",
"",
" if (_hours || _minutes || _seconds) {",
" const hours = _hours ? `${duration.hours}H` : \"\";",
" const minutes = _minutes ? `${duration.minutes}M` : \"\";",
" const seconds = _seconds ? `${duration.seconds}S` : \"\";",
"",
" time = `T${hours}${minutes}${seconds}`;",
" }",
"",
" return `${sign}P${years}${months}${weeks}${days}${time}`;",
" },",
"",
" /**",
" * Gets the total number of seconds in a duration.",
" *",
" * This method will throw an Error if the duration contains any years, months, weeks, or days, as those require a reference",
" * point to calculate the total number of seconds.",
" *",
" * WARNING: If the total number of seconds is larger than the maximum safe integer in JavaScript, this method will",
" * lose precision. @see Duration.totalSecondsBigInt for a BigInt alternative.",
" *",
" * @param duration - the duration to calculate the total number of seconds for",
" * @returns the total number of seconds in the duration",
" */",
" totalSeconds(duration: Duration): number {",
" if (",
" duration.years !== 0 ||",
" duration.months !== 0 ||",
" duration.weeks !== 0 ||",
" duration.days !== 0",
" ) {",
" throw new Error(",
" \"Cannot calculate total seconds for a duration with years, months, weeks, or days.\",",
" );",
" }",
"",
" return (",
" duration.seconds +",
" duration.minutes * 60 +",
" duration.hours * 60 * 60 +",
" duration.weeks * 7 * 24 * 60 * 60",
" );",
" },",
"",
" /**",
" * Gets the total number of seconds in a duration.",
" *",
" * This method will throw an Error if the duration contains any years, months, weeks, or days, as those require a reference",
" * point to calculate the total number of seconds. It will also throw an error if any of the components are not integers.",
" *",
" * @param duration - the duration to calculate the total number of seconds for",
" * @returns the total number of seconds in the duration",
" */",
" totalSecondsBigInt(duration: Duration): bigint {",
" if (",
" duration.years !== 0 ||",
" duration.months !== 0 ||",
" duration.weeks !== 0 ||",
" duration.days !== 0",
" ) {",
" throw new Error(",
" \"Cannot calculate total seconds for a duration with years, months, weeks, or days.\",",
" );",
" }",
"",
" if (",
" !Number.isInteger(duration.seconds) ||",
" !Number.isInteger(duration.minutes) ||",
" !Number.isInteger(duration.hours) ||",
" !Number.isInteger(duration.weeks)",
" ) {",
" throw new Error(",
" \"Cannot calculate total seconds as a BigInt for a duration with non-integer components.\",",
" );",
" }",
"",
" return (",
" BigInt(duration.seconds) +",
" BigInt(duration.minutes) * 60n +",
" BigInt(duration.hours) * 60n * 60n +",
" BigInt(duration.weeks) * 7n * 24n * 60n * 60n",
" );",
" },",
"",
" /**",
" * Creates a duration from a total number of seconds.",
" *",
" * The result is not normalized, so it will only contain a seconds field.",
" */",
" fromTotalSeconds(seconds: number): Duration {",
" return {",
" sign: seconds < 0 ? \"-\" : \"+\",",
" years: 0,",
" months: 0,",
" weeks: 0,",
" days: 0,",
" hours: 0,",
" minutes: 0,",
" seconds: Math.abs(seconds),",
" };",
" },",
"});",
"",
"// #endregion",
"",
];

export async function createModule(parent: Module): Promise<Module> {
if (module) return module;

module = {
name: "datetime",
cursor: parent.cursor.enter("datetime"),
imports: [],
declarations: [],
};

module.declarations.push(lines);

parent.declarations.push(module);

return module;
}
1 change: 1 addition & 0 deletions packages/http-server-js/generated-defs/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export async function createModule(parent: Module): Promise<Module> {
};

// Child modules
await import("./datetime.js").then((m) => m.createModule(module));
await import("./header.js").then((m) => m.createModule(module));
await import("./http.js").then((m) => m.createModule(module));
await import("./multipart.js").then((m) => m.createModule(module));
Expand Down
2 changes: 1 addition & 1 deletion packages/http-server-js/src/common/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function* emitDeclaration(
break;
}
case "Scalar": {
yield emitScalar(ctx, type);
yield emitScalar(ctx, type, module);
break;
}
default: {
Expand Down
2 changes: 1 addition & 1 deletion packages/http-server-js/src/common/reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export function emitTypeReference(
switch (type.kind) {
case "Scalar":
// Get the scalar and return it directly, as it is a primitive.
return getJsScalar(ctx.program, type, position);
return getJsScalar(ctx, module, type, position).type;
case "Model": {
// First handle arrays.
if (isArrayModelType(ctx.program, type)) {
Expand Down
Loading
Loading