Skip to content

Commit a5535c9

Browse files
docs(http-canonicalization): Add JSDoc for public APIs [skip chg] (#12230)
Canonicalization consumers need to understand how codecs, operations, canonical shapes, and options fit together and which discriminants narrow returned metadata. Document the public canonicalization APIs as one package reference, including ownership, selection rules, and supported shapes. Keep useful shared narrowing explanations without repetitive literal-field comments; behavior is unchanged. Consolidates [12235](#12235), [12242](#12242). Tracked in #12228.
1 parent d1bc2a6 commit a5535c9

14 files changed

Lines changed: 327 additions & 19 deletions

‎packages/http-canonicalization/src/codecs.ts‎

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,41 @@ import {
99
import type { Typekit } from "@typespec/compiler/typekit";
1010
import { isHeader } from "@typespec/http";
1111

12+
/** The codec selected for a source type and the two type-graph projections it produces. */
1213
export interface EncodingInfo {
14+
/** First codec in the registry that accepted the source and its reference context. */
1315
codec: Codec;
16+
/** Type exposed to generated language code; may be the unmodified source type. */
1417
languageType: Type;
18+
/** Type describing the serialized representation, not an encoded runtime value. */
1519
wireType: Type;
1620
}
1721

22+
/**
23+
* Selects a codec by registration order. A registry starts empty; register an
24+
* {@link IdentityCodec} last when unrecognized types should pass through.
25+
*/
1826
export class CodecRegistry {
27+
/** Typekit supplied to every codec when selecting a representation. */
1928
$: Typekit;
2029
#codecs: Codec[];
2130

31+
/** Creates an empty registry bound to the typekit's program. */
2232
constructor($: Typekit) {
2333
this.$ = $;
2434
this.#codecs = [];
2535
}
2636

37+
/** Appends a codec; previously registered codecs retain priority. */
2738
addCodec(codec: Codec) {
2839
this.#codecs.push(codec);
2940
}
3041

42+
/**
43+
* Returns the first non-null codec result, together with the selected codec.
44+
* Reference members let codecs honor use-site metadata before type metadata.
45+
* @throws Error when every registered codec declines the source type.
46+
*/
3147
encode(sourceType: Type, referenceTypes: MemberType[]): EncodingInfo {
3248
for (const codec of this.#codecs) {
3349
const result = codec.encode(this.$, sourceType, referenceTypes);
@@ -40,15 +56,34 @@ export class CodecRegistry {
4056
}
4157
}
4258

59+
/**
60+
* Chooses language and wire types for a source type in its use-site context.
61+
* Codecs describe representations; they do not serialize or deserialize values.
62+
*/
4363
export abstract class Codec {
64+
/** Stable selection identifier, also used in scalar canonicalization cache keys. */
4465
abstract id: string;
4566

67+
/**
68+
* Selects type representations, or returns `null` to let the next codec try.
69+
* @param referenceTypes Members referencing the source, in metadata lookup order.
70+
*/
4671
abstract encode(
4772
$: Typekit,
4873
sourceType: Type,
4974
referenceTypes: MemberType[],
50-
): { languageType: Type; wireType: Type } | null;
51-
75+
): {
76+
/** Type to use in the generated language's type graph. */
77+
languageType: Type;
78+
/** Type to use in the serialized payload's type graph. */
79+
wireType: Type;
80+
} | null;
81+
82+
/**
83+
* Finds the first truthy metadata value on applicable reference members, then
84+
* falls back to the source type. The source getter's result is returned as-is;
85+
* without a source or a matching member value, returns `undefined`.
86+
*/
5287
static getMetadata<
5388
TTypeSource extends Type,
5489
TMemberSource extends MemberType,
@@ -84,9 +119,12 @@ export abstract class Codec {
84119
}
85120
}
86121

122+
/** Fallback codec that accepts every type and preserves both representations. */
87123
export class IdentityCodec extends Codec {
124+
/** Marks the result as unchanged for identity-codec predicates. */
88125
readonly id = "identity";
89126

127+
/** Returns the source object itself for both language and wire types. */
90128
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
91129
return {
92130
wireType: sourceType,
@@ -95,9 +133,15 @@ export class IdentityCodec extends Codec {
95133
}
96134
}
97135

136+
/**
137+
* Recognizes UTC date-time scalars encoded as `"unix-timestamp"` with `int64`.
138+
* Use-site encoding metadata takes precedence over scalar metadata.
139+
*/
98140
export class UnixTimestamp64Codec extends Codec {
141+
/** Distinguishes this timestamp selection in scalar cache keys. */
99142
readonly id = "unix-timestamp-64";
100143

144+
/** Uses builtin `utcDateTime` and `float64`, or declines nonmatching encodings. */
101145
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
102146
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
103147
return null;
@@ -126,9 +170,15 @@ export class UnixTimestamp64Codec extends Codec {
126170
}
127171
}
128172

173+
/**
174+
* Recognizes UTC date-time scalars encoded as `"unixTimestamp"` with `int32`.
175+
* Use-site encoding metadata takes precedence over scalar metadata.
176+
*/
129177
export class UnixTimestamp32Codec extends Codec {
178+
/** Distinguishes this timestamp selection in scalar cache keys. */
130179
readonly id = "unix-timestamp-32";
131180

181+
/** Uses builtin `utcDateTime` and `float64`, or declines nonmatching encodings. */
132182
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
133183
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
134184
return null;
@@ -157,9 +207,15 @@ export class UnixTimestamp32Codec extends Codec {
157207
}
158208
}
159209

210+
/**
211+
* Fallback string representation for scalars assignable to `utcDateTime`.
212+
* Does not inspect encoding metadata; register more specific date codecs first.
213+
*/
160214
export class Rfc3339Codec extends Codec {
215+
/** Distinguishes the RFC 3339 selection in scalar cache keys. */
161216
readonly id = "rfc3339";
162217

218+
/** Preserves the date-time language type and uses `string` on the wire, or declines. */
163219
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
164220
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
165221
return null;
@@ -172,9 +228,15 @@ export class Rfc3339Codec extends Codec {
172228
}
173229
}
174230

231+
/**
232+
* Selects the HTTP-date string representation for UTC date-time scalars.
233+
* Accepts explicit `rfc7231` encoding, or a header use site with no encoding.
234+
*/
175235
export class Rfc7231Codec extends Codec {
236+
/** Distinguishes the HTTP-date selection in scalar cache keys. */
176237
readonly id = "rfc7231";
177238

239+
/** Preserves the source date-time type and uses `string` on the wire, or declines. */
178240
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
179241
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
180242
return null;
@@ -209,9 +271,15 @@ export class Rfc7231Codec extends Codec {
209271
}
210272
}
211273

274+
/**
275+
* Selects a string wire representation for types assignable to `bytes`.
276+
* This selection does not inspect an explicit encoding decorator.
277+
*/
212278
export class Base64Codec extends Codec {
279+
/** Identifies the byte-to-string representation. */
213280
readonly id = "base64";
214281

282+
/** Preserves the byte language type and uses `string` on the wire, or declines. */
215283
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
216284
if (!$.type.isAssignableTo(sourceType, $.builtin.bytes)) {
217285
return null;
@@ -224,19 +292,30 @@ export class Base64Codec extends Codec {
224292
}
225293
}
226294

295+
/**
296+
* Optional category filters for numeric codec selection. Neither flag bypasses
297+
* the final requirement to extend `float`, `int32`, or `uint32`.
298+
*/
227299
export interface CoerceToFloat64CodecOptions {
300+
/** When true, additionally requires an integer or float scalar; defaults to false. */
228301
lossyInteger?: boolean;
302+
/** When true, additionally requires a decimal or float scalar; defaults to false. */
229303
lossyDecimal?: boolean;
230304
}
231305

306+
/** Uses `float64` on the wire for scalars extending `float`, `int32`, or `uint32`. */
232307
export class CoerceToFloat64Codec extends Codec {
308+
/** Identifies numeric wire coercion without a language-type replacement. */
233309
readonly id = "coerce-to-float64";
310+
/** Category filters consulted on each call; the supplied object is retained. */
234311
options: CoerceToFloat64CodecOptions;
312+
/** Creates a numeric codec with no additional category filters by default. */
235313
constructor(options: CoerceToFloat64CodecOptions = {}) {
236314
super();
237315
this.options = options;
238316
}
239317

318+
/** Preserves an accepted numeric language type and uses `float64`, or returns `null`. */
240319
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
241320
if (
242321
this.options.lossyInteger &&
@@ -271,9 +350,15 @@ export class CoerceToFloat64Codec extends Codec {
271350
}
272351
}
273352

353+
/**
354+
* Selects a string wire representation for any array type, regardless of HTTP
355+
* location. The codec does not choose a delimiter or perform the join itself.
356+
*/
274357
export class ArrayJoinCodec extends Codec {
358+
/** Identifies an array represented as a string on the wire. */
275359
readonly id = "array-join";
276360

361+
/** Preserves the array language type and uses `string` on the wire, or declines. */
277362
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
278363
if (!$.array.is(sourceType)) {
279364
return null;
@@ -288,22 +373,32 @@ export class ArrayJoinCodec extends Codec {
288373
}
289374
}
290375

376+
/** Naming hook used after checking a property's `@friendlyName`. */
291377
interface RenameCodecOptions {
378+
/** Returns a language-side property name, or `undefined` to leave it unchanged. */
292379
namer?(type: Type): string | undefined;
293380
}
294381
/**
295382
* Renames the language type according to the language's name policy and
296383
* friendly name decorator.
297384
*/
298385
export class RenameCodec extends Codec {
386+
/** Identifies a language-only model property rename. */
299387
readonly id = "model-property-rename";
388+
/** Naming configuration retained by reference and consulted for each property. */
300389
options: RenameCodecOptions;
301390

391+
/** Creates a codec that only uses `@friendlyName` unless a naming hook is supplied. */
302392
constructor(options: RenameCodecOptions = {}) {
303393
super();
304394
this.options = options;
305395
}
306396

397+
/**
398+
* Clones a model property with a changed language name and preserves its wire
399+
* type. A different, nonempty friendly name wins over the naming hook.
400+
* Returns `null` for other types or when neither source supplies a changed name.
401+
*/
307402
encode($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
308403
if (!$.modelProperty.is(sourceType)) {
309404
return null;
@@ -341,6 +436,12 @@ export class RenameCodec extends Codec {
341436
}
342437

343438
const jsonCodecRegistryCache = new WeakMap<Program, CodecRegistry>();
439+
/**
440+
* Gets the mutable default registry cached per program. Specific numeric and
441+
* date-time codecs precede byte, array, and rename codecs, with identity last.
442+
* Appending a codec to this registry cannot override its identity fallback;
443+
* supply a separate registry to customize selection priority.
444+
*/
344445
export const getJsonCodecRegistry = ($: Typekit) => {
345446
if (jsonCodecRegistryCache.has($.program)) {
346447
return jsonCodecRegistryCache.get($.program)!;

‎packages/http-canonicalization/src/enum-member.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export class EnumMemberHttpCanonicalization
6464
return this.engine.subgraphMatchesPredicate(this, predicate);
6565
}
6666

67+
/** Supplies an option-based cache key for an enum member, which needs no encoding codec. */
6768
static mutationInfo(
6869
engine: HttpCanonicalizer,
6970
sourceType: EnumMember,
@@ -79,6 +80,10 @@ export class EnumMemberHttpCanonicalization
7980
};
8081
}
8182

83+
/**
84+
* Creates language and wire nodes that preserve the member's name and value.
85+
* Enum members are leaves; no child canonicalizations are required.
86+
*/
8287
constructor(
8388
engine: HttpCanonicalizer,
8489
sourceType: EnumMember,

‎packages/http-canonicalization/src/enum.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export class EnumHttpCanonicalization
6464
return this.engine.subgraphMatchesPredicate(this, predicate);
6565
}
6666

67+
/** Supplies the enum's option-based cache key and synthetic trait without selecting a codec. */
6768
static mutationInfo(
6869
engine: HttpCanonicalizer,
6970
sourceType: Enum,
@@ -79,6 +80,10 @@ export class EnumHttpCanonicalization
7980
};
8081
}
8182

83+
/**
84+
* Initializes declaration status and projection nodes for the enum.
85+
* Use the engine's `canonicalize` method to also traverse and connect its members.
86+
*/
8287
constructor(
8388
engine: HttpCanonicalizer,
8489
sourceType: Enum,

‎packages/http-canonicalization/src/http-canonicalization-classes.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import { ScalarHttpCanonicalization } from "./scalar.js";
1010
import { UnionVariantHttpCanonicalization } from "./union-variant.js";
1111
import { UnionHttpCanonicalization } from "./union.js";
1212

13+
/**
14+
* Source-kind dispatch table used by {@link HttpCanonicalizer}. Several source
15+
* kinds share the literal canonicalization; node `kind` need not equal source `kind`.
16+
*/
1317
export const CANONICALIZATION_CLASSES = {
1418
Operation: OperationHttpCanonicalization,
1519
Model: ModelHttpCanonicalization,
@@ -27,8 +31,10 @@ export const CANONICALIZATION_CLASSES = {
2731
Tuple: LiteralHttpCanonicalization,
2832
} as const;
2933

34+
/** Instance types keyed by source kind, for the HTTP mutation engine's typed dispatch. */
3035
export type HttpCanonicalizationMutations = InstancesFor<typeof CANONICALIZATION_CLASSES>;
3136

37+
/** Union of registered HTTP canonicalization nodes; narrow by `kind` to navigate shape-specific data. */
3238
export type HttpCanonicalization = InstanceType<
3339
(typeof CANONICALIZATION_CLASSES)[keyof typeof CANONICALIZATION_CLASSES]
3440
>;

0 commit comments

Comments
 (0)