@typespec/http-server-js 0.58.0-alpha.10-dev.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +21 -0
  3. package/README.md +183 -0
  4. package/build-helpers.ts +170 -0
  5. package/dist/generated-defs/helpers/header.d.ts +4 -0
  6. package/dist/generated-defs/helpers/header.d.ts.map +1 -0
  7. package/dist/generated-defs/helpers/header.js +76 -0
  8. package/dist/generated-defs/helpers/header.js.map +1 -0
  9. package/dist/generated-defs/helpers/http.d.ts +4 -0
  10. package/dist/generated-defs/helpers/http.d.ts.map +1 -0
  11. package/dist/generated-defs/helpers/http.js +134 -0
  12. package/dist/generated-defs/helpers/http.js.map +1 -0
  13. package/dist/generated-defs/helpers/index.d.ts +4 -0
  14. package/dist/generated-defs/helpers/index.d.ts.map +1 -0
  15. package/dist/generated-defs/helpers/index.js +21 -0
  16. package/dist/generated-defs/helpers/index.js.map +1 -0
  17. package/dist/generated-defs/helpers/multipart.d.ts +4 -0
  18. package/dist/generated-defs/helpers/multipart.d.ts.map +1 -0
  19. package/dist/generated-defs/helpers/multipart.js +249 -0
  20. package/dist/generated-defs/helpers/multipart.js.map +1 -0
  21. package/dist/generated-defs/helpers/router.d.ts +4 -0
  22. package/dist/generated-defs/helpers/router.d.ts.map +1 -0
  23. package/dist/generated-defs/helpers/router.js +259 -0
  24. package/dist/generated-defs/helpers/router.js.map +1 -0
  25. package/dist/src/common/declaration.d.ts +13 -0
  26. package/dist/src/common/declaration.d.ts.map +1 -0
  27. package/dist/src/common/declaration.js +45 -0
  28. package/dist/src/common/declaration.js.map +1 -0
  29. package/dist/src/common/documentation.d.ts +12 -0
  30. package/dist/src/common/documentation.d.ts.map +1 -0
  31. package/dist/src/common/documentation.js +21 -0
  32. package/dist/src/common/documentation.js.map +1 -0
  33. package/dist/src/common/enum.d.ts +10 -0
  34. package/dist/src/common/enum.d.ts.map +1 -0
  35. package/dist/src/common/enum.js +21 -0
  36. package/dist/src/common/enum.js.map +1 -0
  37. package/dist/src/common/interface.d.ts +50 -0
  38. package/dist/src/common/interface.d.ts.map +1 -0
  39. package/dist/src/common/interface.js +194 -0
  40. package/dist/src/common/interface.js.map +1 -0
  41. package/dist/src/common/model.d.ts +26 -0
  42. package/dist/src/common/model.d.ts.map +1 -0
  43. package/dist/src/common/model.js +115 -0
  44. package/dist/src/common/model.js.map +1 -0
  45. package/dist/src/common/namespace.d.ts +38 -0
  46. package/dist/src/common/namespace.d.ts.map +1 -0
  47. package/dist/src/common/namespace.js +184 -0
  48. package/dist/src/common/namespace.js.map +1 -0
  49. package/dist/src/common/reference.d.ts +46 -0
  50. package/dist/src/common/reference.d.ts.map +1 -0
  51. package/dist/src/common/reference.js +243 -0
  52. package/dist/src/common/reference.js.map +1 -0
  53. package/dist/src/common/scalar.d.ts +50 -0
  54. package/dist/src/common/scalar.d.ts.map +1 -0
  55. package/dist/src/common/scalar.js +144 -0
  56. package/dist/src/common/scalar.js.map +1 -0
  57. package/dist/src/common/serialization/index.d.ts +11 -0
  58. package/dist/src/common/serialization/index.d.ts.map +1 -0
  59. package/dist/src/common/serialization/index.js +72 -0
  60. package/dist/src/common/serialization/index.js.map +1 -0
  61. package/dist/src/common/serialization/json.d.ts +6 -0
  62. package/dist/src/common/serialization/json.d.ts.map +1 -0
  63. package/dist/src/common/serialization/json.js +341 -0
  64. package/dist/src/common/serialization/json.js.map +1 -0
  65. package/dist/src/common/union.d.ts +23 -0
  66. package/dist/src/common/union.d.ts.map +1 -0
  67. package/dist/src/common/union.js +57 -0
  68. package/dist/src/common/union.js.map +1 -0
  69. package/dist/src/ctx.d.ts +242 -0
  70. package/dist/src/ctx.d.ts.map +1 -0
  71. package/dist/src/ctx.js +211 -0
  72. package/dist/src/ctx.js.map +1 -0
  73. package/dist/src/helpers/header.d.ts +14 -0
  74. package/dist/src/helpers/header.d.ts.map +1 -0
  75. package/dist/src/helpers/header.js +38 -0
  76. package/dist/src/helpers/header.js.map +1 -0
  77. package/dist/src/helpers/http.d.ts +70 -0
  78. package/dist/src/helpers/http.d.ts.map +1 -0
  79. package/dist/src/helpers/http.js +86 -0
  80. package/dist/src/helpers/http.js.map +1 -0
  81. package/dist/src/helpers/multipart.d.ts +26 -0
  82. package/dist/src/helpers/multipart.d.ts.map +1 -0
  83. package/dist/src/helpers/multipart.js +182 -0
  84. package/dist/src/helpers/multipart.js.map +1 -0
  85. package/dist/src/helpers/router.d.ts +176 -0
  86. package/dist/src/helpers/router.d.ts.map +1 -0
  87. package/dist/src/helpers/router.js +55 -0
  88. package/dist/src/helpers/router.js.map +1 -0
  89. package/dist/src/http/index.d.ts +24 -0
  90. package/dist/src/http/index.d.ts.map +1 -0
  91. package/dist/src/http/index.js +52 -0
  92. package/dist/src/http/index.js.map +1 -0
  93. package/dist/src/http/server/index.d.ts +11 -0
  94. package/dist/src/http/server/index.d.ts.map +1 -0
  95. package/dist/src/http/server/index.js +413 -0
  96. package/dist/src/http/server/index.js.map +1 -0
  97. package/dist/src/http/server/multipart.d.ts +16 -0
  98. package/dist/src/http/server/multipart.d.ts.map +1 -0
  99. package/dist/src/http/server/multipart.js +214 -0
  100. package/dist/src/http/server/multipart.js.map +1 -0
  101. package/dist/src/http/server/router.d.ts +15 -0
  102. package/dist/src/http/server/router.d.ts.map +1 -0
  103. package/dist/src/http/server/router.js +459 -0
  104. package/dist/src/http/server/router.js.map +1 -0
  105. package/dist/src/index.d.ts +5 -0
  106. package/dist/src/index.d.ts.map +1 -0
  107. package/dist/src/index.js +38 -0
  108. package/dist/src/index.js.map +1 -0
  109. package/dist/src/lib.d.ts +141 -0
  110. package/dist/src/lib.d.ts.map +1 -0
  111. package/dist/src/lib.js +116 -0
  112. package/dist/src/lib.js.map +1 -0
  113. package/dist/src/scripts/scaffold/bin.d.mts +14 -0
  114. package/dist/src/scripts/scaffold/bin.d.mts.map +1 -0
  115. package/dist/src/scripts/scaffold/bin.mjs +559 -0
  116. package/dist/src/scripts/scaffold/bin.mjs.map +1 -0
  117. package/dist/src/testing/index.d.ts +3 -0
  118. package/dist/src/testing/index.d.ts.map +1 -0
  119. package/dist/src/testing/index.js +6 -0
  120. package/dist/src/testing/index.js.map +1 -0
  121. package/dist/src/util/case.d.ts +81 -0
  122. package/dist/src/util/case.d.ts.map +1 -0
  123. package/dist/src/util/case.js +111 -0
  124. package/dist/src/util/case.js.map +1 -0
  125. package/dist/src/util/differentiate.d.ts +251 -0
  126. package/dist/src/util/differentiate.d.ts.map +1 -0
  127. package/dist/src/util/differentiate.js +580 -0
  128. package/dist/src/util/differentiate.js.map +1 -0
  129. package/dist/src/util/error.d.ts +13 -0
  130. package/dist/src/util/error.d.ts.map +1 -0
  131. package/dist/src/util/error.js +25 -0
  132. package/dist/src/util/error.js.map +1 -0
  133. package/dist/src/util/extends.d.ts +10 -0
  134. package/dist/src/util/extends.d.ts.map +1 -0
  135. package/dist/src/util/extends.js +31 -0
  136. package/dist/src/util/extends.js.map +1 -0
  137. package/dist/src/util/iter.d.ts +39 -0
  138. package/dist/src/util/iter.d.ts.map +1 -0
  139. package/dist/src/util/iter.js +72 -0
  140. package/dist/src/util/iter.js.map +1 -0
  141. package/dist/src/util/keywords.d.ts +10 -0
  142. package/dist/src/util/keywords.d.ts.map +1 -0
  143. package/dist/src/util/keywords.js +85 -0
  144. package/dist/src/util/keywords.js.map +1 -0
  145. package/dist/src/util/name.d.ts +12 -0
  146. package/dist/src/util/name.d.ts.map +1 -0
  147. package/dist/src/util/name.js +26 -0
  148. package/dist/src/util/name.js.map +1 -0
  149. package/dist/src/util/once-queue.d.ts +24 -0
  150. package/dist/src/util/once-queue.d.ts.map +1 -0
  151. package/dist/src/util/once-queue.js +34 -0
  152. package/dist/src/util/once-queue.js.map +1 -0
  153. package/dist/src/util/openapi3.d.ts +23 -0
  154. package/dist/src/util/openapi3.d.ts.map +1 -0
  155. package/dist/src/util/openapi3.js +40 -0
  156. package/dist/src/util/openapi3.js.map +1 -0
  157. package/dist/src/util/pluralism.d.ts +23 -0
  158. package/dist/src/util/pluralism.d.ts.map +1 -0
  159. package/dist/src/util/pluralism.js +36 -0
  160. package/dist/src/util/pluralism.js.map +1 -0
  161. package/dist/src/util/scope.d.ts +85 -0
  162. package/dist/src/util/scope.d.ts.map +1 -0
  163. package/dist/src/util/scope.js +111 -0
  164. package/dist/src/util/scope.js.map +1 -0
  165. package/dist/src/write.d.ts +23 -0
  166. package/dist/src/write.d.ts.map +1 -0
  167. package/dist/src/write.js +62 -0
  168. package/dist/src/write.js.map +1 -0
  169. package/generated-defs/helpers/header.ts +83 -0
  170. package/generated-defs/helpers/http.ts +141 -0
  171. package/generated-defs/helpers/index.ts +27 -0
  172. package/generated-defs/helpers/multipart.ts +256 -0
  173. package/generated-defs/helpers/router.ts +266 -0
  174. package/package.json +71 -0
  175. package/src/common/declaration.ts +52 -0
  176. package/src/common/documentation.ts +26 -0
  177. package/src/common/enum.ts +28 -0
  178. package/src/common/interface.ts +264 -0
  179. package/src/common/model.ts +160 -0
  180. package/src/common/namespace.ts +243 -0
  181. package/src/common/reference.ts +319 -0
  182. package/src/common/scalar.ts +173 -0
  183. package/src/common/serialization/index.ts +124 -0
  184. package/src/common/serialization/json.ts +444 -0
  185. package/src/common/union.ts +76 -0
  186. package/src/ctx.ts +497 -0
  187. package/src/helpers/header.ts +55 -0
  188. package/src/helpers/http.ts +113 -0
  189. package/src/helpers/multipart.ts +228 -0
  190. package/src/helpers/router.ts +238 -0
  191. package/src/http/index.ts +81 -0
  192. package/src/http/server/index.ts +548 -0
  193. package/src/http/server/multipart.ts +272 -0
  194. package/src/http/server/router.ts +686 -0
  195. package/src/index.ts +56 -0
  196. package/src/lib.ts +130 -0
  197. package/src/scripts/scaffold/bin.mts +781 -0
  198. package/src/testing/index.ts +10 -0
  199. package/src/util/case.ts +182 -0
  200. package/src/util/differentiate.ts +957 -0
  201. package/src/util/error.ts +28 -0
  202. package/src/util/extends.ts +43 -0
  203. package/src/util/iter.ts +85 -0
  204. package/src/util/keywords.ts +90 -0
  205. package/src/util/name.ts +33 -0
  206. package/src/util/once-queue.ts +55 -0
  207. package/src/util/openapi3.ts +53 -0
  208. package/src/util/pluralism.ts +37 -0
  209. package/src/util/scope.ts +211 -0
  210. package/src/write.ts +88 -0
  211. package/temp/tsconfig.tsbuildinfo +1 -0
  212. package/test/header.test.ts +26 -0
  213. package/test/multipart.test.ts +169 -0
  214. package/tsconfig.json +10 -0
  215. package/vitest.config.ts +4 -0
@@ -0,0 +1,444 @@
1
+ // Copyright (c) Microsoft Corporation
2
+ // Licensed under the MIT license.
3
+
4
+ import {
5
+ BooleanLiteral,
6
+ IntrinsicType,
7
+ ModelProperty,
8
+ NoTarget,
9
+ NumericLiteral,
10
+ StringLiteral,
11
+ Type,
12
+ compilerAssert,
13
+ getEncode,
14
+ getProjectedName,
15
+ isArrayModelType,
16
+ isRecordModelType,
17
+ resolveEncodedName,
18
+ } from "@typespec/compiler";
19
+ import { getHeaderFieldOptions, getPathParamOptions, getQueryParamOptions } from "@typespec/http";
20
+ import { JsContext, Module } from "../../ctx.js";
21
+ import { parseCase } from "../../util/case.js";
22
+ import { differentiateUnion, writeCodeTree } from "../../util/differentiate.js";
23
+ import { UnimplementedError } from "../../util/error.js";
24
+ import { indent } from "../../util/iter.js";
25
+ import { emitTypeReference, escapeUnsafeChars } from "../reference.js";
26
+ import { getJsScalar } from "../scalar.js";
27
+ import { SerializableType, SerializationContext, requireSerialization } from "./index.js";
28
+
29
+ /**
30
+ * Memoization cache for requiresJsonSerialization.
31
+ */
32
+ const _REQUIRES_JSON_SERIALIZATION = new WeakMap<SerializableType | ModelProperty, boolean>();
33
+
34
+ export function requiresJsonSerialization(ctx: JsContext, type: Type): boolean {
35
+ if (!isSerializable(type)) return false;
36
+
37
+ if (_REQUIRES_JSON_SERIALIZATION.has(type)) {
38
+ return _REQUIRES_JSON_SERIALIZATION.get(type)!;
39
+ }
40
+
41
+ // Assume the type is serializable until proven otherwise, in case this model is encountered recursively.
42
+ // This isn't an exactly correct algorithm, but in the recursive case it will at least produce something that
43
+ // is correct.
44
+ _REQUIRES_JSON_SERIALIZATION.set(type, true);
45
+
46
+ let requiresSerialization: boolean;
47
+
48
+ switch (type.kind) {
49
+ case "Model": {
50
+ if (isArrayModelType(ctx.program, type)) {
51
+ const argumentType = type.indexer.value;
52
+ requiresSerialization = requiresJsonSerialization(ctx, argumentType);
53
+ break;
54
+ }
55
+
56
+ requiresSerialization = [...type.properties.values()].some((property) =>
57
+ propertyRequiresJsonSerialization(ctx, property),
58
+ );
59
+ break;
60
+ }
61
+ case "Scalar": {
62
+ const scalar = getJsScalar(ctx.program, type, type);
63
+ requiresSerialization = scalar === "Uint8Array" || getEncode(ctx.program, type) !== undefined;
64
+ break;
65
+ }
66
+ case "Union": {
67
+ requiresSerialization = [...type.variants.values()].some((variant) =>
68
+ requiresJsonSerialization(ctx, variant),
69
+ );
70
+ break;
71
+ }
72
+ case "ModelProperty":
73
+ requiresSerialization = requiresJsonSerialization(ctx, type.type);
74
+ break;
75
+ }
76
+
77
+ _REQUIRES_JSON_SERIALIZATION.set(type, requiresSerialization);
78
+
79
+ return requiresSerialization;
80
+ }
81
+
82
+ function propertyRequiresJsonSerialization(ctx: JsContext, property: ModelProperty): boolean {
83
+ return !!(
84
+ isHttpMetadata(ctx, property) ||
85
+ getEncode(ctx.program, property) ||
86
+ resolveEncodedName(ctx.program, property, "application/json") !== property.name ||
87
+ getProjectedName(ctx.program, property, "json") ||
88
+ (isSerializable(property.type) && requiresJsonSerialization(ctx, property.type))
89
+ );
90
+ }
91
+
92
+ function isHttpMetadata(ctx: JsContext, property: ModelProperty): boolean {
93
+ return (
94
+ getQueryParamOptions(ctx.program, property) !== undefined ||
95
+ getHeaderFieldOptions(ctx.program, property) !== undefined ||
96
+ getPathParamOptions(ctx.program, property) !== undefined
97
+ );
98
+ }
99
+
100
+ function isSerializable(type: Type): type is SerializableType | ModelProperty {
101
+ return (
102
+ type.kind === "Model" ||
103
+ type.kind === "Scalar" ||
104
+ type.kind === "Union" ||
105
+ type.kind === "ModelProperty"
106
+ );
107
+ }
108
+
109
+ export function* emitJsonSerialization(
110
+ ctx: SerializationContext,
111
+ type: SerializableType,
112
+ module: Module,
113
+ typeName: string,
114
+ ): Iterable<string> {
115
+ yield `toJsonObject(input: ${typeName}): any {`;
116
+ yield* indent(emitToJson(ctx, type, module));
117
+ yield `},`;
118
+
119
+ yield `fromJsonObject(input: any): ${typeName} {`;
120
+ yield* indent(emitFromJson(ctx, type, module));
121
+ yield `},`;
122
+ }
123
+
124
+ function* emitToJson(
125
+ ctx: SerializationContext,
126
+ type: SerializableType,
127
+ module: Module,
128
+ ): Iterable<string> {
129
+ switch (type.kind) {
130
+ case "Model": {
131
+ yield `return {`;
132
+
133
+ for (const property of type.properties.values()) {
134
+ const encodedName =
135
+ getProjectedName(ctx.program, property, "json") ??
136
+ resolveEncodedName(ctx.program, property, "application/json") ??
137
+ property.name;
138
+
139
+ const expr = transposeExpressionToJson(
140
+ ctx,
141
+ property.type,
142
+ `input.${property.name}`,
143
+ module,
144
+ );
145
+
146
+ yield ` ${encodedName}: ${expr},`;
147
+ }
148
+
149
+ yield `};`;
150
+
151
+ return;
152
+ }
153
+ case "Scalar": {
154
+ yield `throw new Error("Unimplemented: scalar JSON serialization");`;
155
+ return;
156
+ }
157
+ case "Union": {
158
+ const codeTree = differentiateUnion(ctx, type);
159
+
160
+ yield* writeCodeTree(ctx, codeTree, {
161
+ subject: "input",
162
+ referenceModelProperty(p) {
163
+ return "input." + parseCase(p.name).camelCase;
164
+ },
165
+ renderResult(type) {
166
+ return [`return ${transposeExpressionToJson(ctx, type, "input", module)};`];
167
+ },
168
+ });
169
+
170
+ return;
171
+ }
172
+ }
173
+ }
174
+
175
+ function transposeExpressionToJson(
176
+ ctx: SerializationContext,
177
+ type: Type,
178
+ expr: string,
179
+ module: Module,
180
+ ): string {
181
+ switch (type.kind) {
182
+ case "Model": {
183
+ if (isArrayModelType(ctx.program, type)) {
184
+ const argumentType = type.indexer.value;
185
+
186
+ if (requiresJsonSerialization(ctx, argumentType)) {
187
+ return `${expr}?.map((item) => ${transposeExpressionToJson(ctx, argumentType, "item", module)})`;
188
+ } else {
189
+ return expr;
190
+ }
191
+ } else if (isRecordModelType(ctx.program, type)) {
192
+ const argumentType = type.indexer.value;
193
+
194
+ if (requiresJsonSerialization(ctx, argumentType)) {
195
+ return `Object.fromEntries(Object.entries(${expr}).map(([key, value]) => [String(key), ${transposeExpressionToJson(
196
+ ctx,
197
+ argumentType,
198
+ "value",
199
+ module,
200
+ )}]))`;
201
+ } else {
202
+ return expr;
203
+ }
204
+ } else if (!requiresJsonSerialization(ctx, type)) {
205
+ return expr;
206
+ } else {
207
+ requireSerialization(ctx, type, "application/json");
208
+ const typeReference = emitTypeReference(ctx, type, NoTarget, module);
209
+
210
+ return `${typeReference}.toJsonObject(${expr})`;
211
+ }
212
+ }
213
+ case "Scalar":
214
+ const scalar = getJsScalar(ctx.program, type, type);
215
+
216
+ switch (scalar) {
217
+ case "Uint8Array":
218
+ // Coerce to Buffer if we aren't given a buffer. This avoids having to do unholy things to
219
+ // convert through an intermediate and use globalThis.btoa. v8 does not support Uint8Array.toBase64
220
+ return `((${expr} instanceof Buffer) ? ${expr} : Buffer.from(${expr})).toString('base64')`;
221
+ default:
222
+ return expr;
223
+ }
224
+ case "Union":
225
+ if (!requiresJsonSerialization(ctx, type)) {
226
+ return expr;
227
+ } else {
228
+ requireSerialization(ctx, type, "application/json");
229
+ const typeReference = emitTypeReference(ctx, type, NoTarget, module, {
230
+ altName: "WeirdUnion",
231
+ requireDeclaration: true,
232
+ });
233
+
234
+ return `${typeReference}.toJsonObject(${expr})`;
235
+ }
236
+ case "ModelProperty":
237
+ return transposeExpressionToJson(ctx, type.type, expr, module);
238
+ case "Intrinsic":
239
+ switch (type.name) {
240
+ case "void":
241
+ return "undefined";
242
+ case "null":
243
+ return "null";
244
+ case "ErrorType":
245
+ compilerAssert(false, "Encountered ErrorType in JSON serialization", type);
246
+ return expr;
247
+ case "never":
248
+ case "unknown":
249
+ default:
250
+ // Unhandled intrinsics will have been caught during type construction. We'll ignore this and
251
+ // just return the expr as-is.
252
+ return expr;
253
+ }
254
+ case "String":
255
+ case "Number":
256
+ case "Boolean":
257
+ return literalToExpr(type);
258
+ case "Interface":
259
+ case "Enum":
260
+ case "EnumMember":
261
+ case "TemplateParameter":
262
+ case "Namespace":
263
+ case "Operation":
264
+ case "StringTemplate":
265
+ case "StringTemplateSpan":
266
+ case "Tuple":
267
+ case "UnionVariant":
268
+ case "Function":
269
+ case "Decorator":
270
+ case "FunctionParameter":
271
+ case "Object":
272
+ case "Projection":
273
+ case "ScalarConstructor":
274
+ default:
275
+ throw new UnimplementedError(`transformJsonExprForType: ${type.kind}`);
276
+ }
277
+ }
278
+
279
+ function literalToExpr(type: StringLiteral | BooleanLiteral | NumericLiteral): string {
280
+ switch (type.kind) {
281
+ case "String":
282
+ return escapeUnsafeChars(JSON.stringify(type.value));
283
+ case "Number":
284
+ case "Boolean":
285
+ return String(type.value);
286
+ }
287
+ }
288
+
289
+ function* emitFromJson(
290
+ ctx: SerializationContext,
291
+ type: SerializableType,
292
+ module: Module,
293
+ ): Iterable<string> {
294
+ switch (type.kind) {
295
+ case "Model": {
296
+ yield `return {`;
297
+
298
+ for (const property of type.properties.values()) {
299
+ const encodedName =
300
+ getProjectedName(ctx.program, property, "json") ??
301
+ resolveEncodedName(ctx.program, property, "application/json") ??
302
+ property.name;
303
+
304
+ const expr = transposeExpressionFromJson(
305
+ ctx,
306
+ property.type,
307
+ `input["${encodedName}"]`,
308
+ module,
309
+ );
310
+
311
+ yield ` ${property.name}: ${expr},`;
312
+ }
313
+
314
+ yield "};";
315
+
316
+ return;
317
+ }
318
+ case "Scalar": {
319
+ yield `throw new Error("Unimplemented: scalar JSON serialization");`;
320
+ return;
321
+ }
322
+ case "Union": {
323
+ const codeTree = differentiateUnion(ctx, type);
324
+
325
+ yield* writeCodeTree(ctx, codeTree, {
326
+ subject: "input",
327
+ referenceModelProperty(p) {
328
+ const jsonName =
329
+ getProjectedName(ctx.program, p, "json") ??
330
+ resolveEncodedName(ctx.program, p, "application/json") ??
331
+ p.name;
332
+ return "input[" + JSON.stringify(jsonName) + "]";
333
+ },
334
+ renderResult(type) {
335
+ return [`return ${transposeExpressionFromJson(ctx, type, "input", module)};`];
336
+ },
337
+ });
338
+
339
+ return;
340
+ }
341
+ }
342
+ }
343
+
344
+ function transposeExpressionFromJson(
345
+ ctx: SerializationContext,
346
+ type: Type,
347
+ expr: string,
348
+ module: Module,
349
+ ): string {
350
+ switch (type.kind) {
351
+ case "Model": {
352
+ if (isArrayModelType(ctx.program, type)) {
353
+ const argumentType = type.indexer.value;
354
+
355
+ if (requiresJsonSerialization(ctx, argumentType)) {
356
+ return `${expr}?.map((item: any) => ${transposeExpressionFromJson(ctx, argumentType, "item", module)})`;
357
+ } else {
358
+ return expr;
359
+ }
360
+ } else if (isRecordModelType(ctx.program, type)) {
361
+ const argumentType = type.indexer.value;
362
+
363
+ if (requiresJsonSerialization(ctx, argumentType)) {
364
+ return `Object.fromEntries(Object.entries(${expr}).map(([key, value]) => [key, ${transposeExpressionFromJson(
365
+ ctx,
366
+ argumentType,
367
+ "value",
368
+ module,
369
+ )}]))`;
370
+ } else {
371
+ return expr;
372
+ }
373
+ } else if (!requiresJsonSerialization(ctx, type)) {
374
+ return `${expr} as ${emitTypeReference(ctx, type, NoTarget, module)}`;
375
+ } else {
376
+ requireSerialization(ctx, type, "application/json");
377
+ const typeReference = emitTypeReference(ctx, type, NoTarget, module);
378
+
379
+ return `${typeReference}.fromJsonObject(${expr})`;
380
+ }
381
+ }
382
+ case "Scalar":
383
+ const scalar = getJsScalar(ctx.program, type, type);
384
+
385
+ switch (scalar) {
386
+ case "Uint8Array":
387
+ return `Buffer.from(${expr}, 'base64')`;
388
+ default:
389
+ return expr;
390
+ }
391
+ case "Union":
392
+ if (!requiresJsonSerialization(ctx, type)) {
393
+ return expr;
394
+ } else {
395
+ requireSerialization(ctx, type, "application/json");
396
+ const typeReference = emitTypeReference(ctx, type, NoTarget, module, {
397
+ altName: "WeirdUnion",
398
+ requireDeclaration: true,
399
+ });
400
+
401
+ return `${typeReference}.fromJsonObject(${expr})`;
402
+ }
403
+ case "ModelProperty":
404
+ return transposeExpressionFromJson(ctx, type.type, expr, module);
405
+ case "Intrinsic":
406
+ switch (type.name) {
407
+ case "ErrorType":
408
+ throw new Error("UNREACHABLE: ErrorType in JSON deserialization");
409
+ case "void":
410
+ return "undefined";
411
+ case "null":
412
+ return "null";
413
+ case "never":
414
+ case "unknown":
415
+ return expr;
416
+ default:
417
+ throw new Error(
418
+ `Unreachable: intrinsic type ${(type satisfies never as IntrinsicType).name}`,
419
+ );
420
+ }
421
+ case "String":
422
+ case "Number":
423
+ case "Boolean":
424
+ return literalToExpr(type);
425
+ case "Interface":
426
+ case "Enum":
427
+ case "EnumMember":
428
+ case "TemplateParameter":
429
+ case "Namespace":
430
+ case "Operation":
431
+ case "StringTemplate":
432
+ case "StringTemplateSpan":
433
+ case "Tuple":
434
+ case "UnionVariant":
435
+ case "Function":
436
+ case "Decorator":
437
+ case "FunctionParameter":
438
+ case "Object":
439
+ case "Projection":
440
+ case "ScalarConstructor":
441
+ default:
442
+ throw new UnimplementedError(`transformJsonExprForType: ${type.kind}`);
443
+ }
444
+ }
@@ -0,0 +1,76 @@
1
+ // Copyright (c) Microsoft Corporation
2
+ // Licensed under the MIT license.
3
+
4
+ import { Union, UnionVariant } from "@typespec/compiler";
5
+ import { JsContext, Module, PartialUnionSynthetic } from "../ctx.js";
6
+ import { parseCase } from "../util/case.js";
7
+ import { emitDocumentation } from "./documentation.js";
8
+ import { emitTypeReference } from "./reference.js";
9
+
10
+ /**
11
+ * Emit an inline union type. This will automatically import any referenced types that are part of the union.
12
+ *
13
+ * @param ctx - The emitter context.
14
+ * @param variants - The variants of the union.
15
+ * @param module - The module that this union is written into.
16
+ * @returns a string that can be used as a type reference
17
+ */
18
+ export function emitUnionType(ctx: JsContext, variants: UnionVariant[], module: Module): string {
19
+ // Treat empty unions as never so that we always return a good type reference here.
20
+ if (variants.length === 0) return "never";
21
+
22
+ const variantTypes: string[] = [];
23
+
24
+ for (const [_, v] of variants.entries()) {
25
+ const name = emitTypeReference(ctx, v.type, v, module);
26
+
27
+ variantTypes.push(name);
28
+
29
+ // if (isImportableType(ctx, v.type)) {
30
+ // module.imports.push({
31
+ // binder: [name],
32
+ // from: createOrGetModuleForNamespace(ctx, v.type.namespace!),
33
+ // });
34
+ // }
35
+ }
36
+
37
+ return variantTypes.join(" | ");
38
+ }
39
+
40
+ /**
41
+ * Emits a union type declaration as an alias.
42
+ *
43
+ * This is rare in TypeScript, but may occur in some niche cases where an alias is desirable.
44
+ *
45
+ * @param ctx - The emitter context.
46
+ * @param union - The union to emit.
47
+ * @param module - The module that this union declaration is written into.
48
+ * @param altName - An alternative name to use for the union if it is not named.
49
+ */
50
+ export function* emitUnion(
51
+ ctx: JsContext,
52
+ union: Union | PartialUnionSynthetic,
53
+ module: Module,
54
+ altName?: string,
55
+ ): Iterable<string> {
56
+ const name = union.name ? parseCase(union.name).pascalCase : altName;
57
+ const isPartialSynthetic = union.kind === "partialUnion";
58
+
59
+ if (name === undefined) {
60
+ throw new Error("Internal Error: Union name is undefined");
61
+ }
62
+
63
+ if (!isPartialSynthetic) yield* emitDocumentation(ctx, union);
64
+
65
+ const variants = isPartialSynthetic
66
+ ? union.variants.map((v) => [v.name, v] as const)
67
+ : union.variants.entries();
68
+
69
+ const variantTypes = [...variants].map(([_, v]) =>
70
+ emitTypeReference(ctx, v.type, v, module, {
71
+ altName: name + parseCase(String(v.name)).pascalCase,
72
+ }),
73
+ );
74
+
75
+ yield `export type ${name} = ${variantTypes.join(" | ")};`;
76
+ }