@typespec/http-server-js 0.58.0-alpha.12-dev.0 → 0.58.0-alpha.12-dev.2
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.
- package/.testignore +26 -0
- package/dist/generated-defs/helpers/datetime.d.ts +4 -0
- package/dist/generated-defs/helpers/datetime.d.ts.map +1 -0
- package/dist/generated-defs/helpers/datetime.js +256 -0
- package/dist/generated-defs/helpers/datetime.js.map +1 -0
- package/dist/generated-defs/helpers/index.d.ts.map +1 -1
- package/dist/generated-defs/helpers/index.js +1 -0
- package/dist/generated-defs/helpers/index.js.map +1 -1
- package/dist/src/common/declaration.js +1 -1
- package/dist/src/common/declaration.js.map +1 -1
- package/dist/src/common/reference.js +1 -1
- package/dist/src/common/reference.js.map +1 -1
- package/dist/src/common/scalar.d.ts +175 -22
- package/dist/src/common/scalar.d.ts.map +1 -1
- package/dist/src/common/scalar.js +420 -93
- package/dist/src/common/scalar.js.map +1 -1
- package/dist/src/common/serialization/index.d.ts +2 -2
- package/dist/src/common/serialization/index.d.ts.map +1 -1
- package/dist/src/common/serialization/index.js +9 -3
- package/dist/src/common/serialization/index.js.map +1 -1
- package/dist/src/common/serialization/json.d.ts +2 -2
- package/dist/src/common/serialization/json.d.ts.map +1 -1
- package/dist/src/common/serialization/json.js +144 -42
- package/dist/src/common/serialization/json.js.map +1 -1
- package/dist/src/helpers/datetime.d.ts +92 -0
- package/dist/src/helpers/datetime.d.ts.map +1 -0
- package/dist/src/helpers/datetime.js +151 -0
- package/dist/src/helpers/datetime.js.map +1 -0
- package/dist/src/http/server/index.d.ts.map +1 -1
- package/dist/src/http/server/index.js +17 -12
- package/dist/src/http/server/index.js.map +1 -1
- package/dist/src/http/server/multipart.js +1 -1
- package/dist/src/http/server/multipart.js.map +1 -1
- package/dist/src/lib.d.ts +10 -1
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +6 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/util/case.d.ts +9 -0
- package/dist/src/util/case.d.ts.map +1 -1
- package/dist/src/util/case.js +18 -0
- package/dist/src/util/case.js.map +1 -1
- package/dist/src/util/differentiate.d.ts +4 -4
- package/dist/src/util/differentiate.d.ts.map +1 -1
- package/dist/src/util/differentiate.js +10 -10
- package/dist/src/util/differentiate.js.map +1 -1
- package/eng/scripts/emit-e2e.js +315 -0
- package/eng/scripts/tspconfig.yaml +6 -0
- package/generated-defs/helpers/datetime.ts +263 -0
- package/generated-defs/helpers/index.ts +1 -0
- package/package.json +23 -9
- package/src/common/declaration.ts +1 -1
- package/src/common/reference.ts +1 -1
- package/src/common/scalar.ts +709 -103
- package/src/common/serialization/index.ts +11 -4
- package/src/common/serialization/json.ts +174 -52
- package/src/helpers/datetime.ts +235 -0
- package/src/http/server/index.ts +29 -15
- package/src/http/server/multipart.ts +1 -1
- package/src/lib.ts +6 -0
- package/src/util/case.ts +19 -0
- package/src/util/differentiate.ts +15 -8
- package/temp/tsconfig.tsbuildinfo +1 -1
- package/test/datetime.test.ts +226 -0
- package/test/e2e/helpers.ts +59 -0
- package/test/e2e/http/parameters/basic/main.test.e2e.ts +36 -0
- package/test/e2e/http/parameters/body-optionality/main.test.e2e.ts +45 -0
- package/test/e2e/http/parameters/spread/main.test.e2e.ts +92 -0
- package/test/e2e/http/type/model/empty/main.test.e2e.ts +35 -0
- package/test/e2e/spector.ts +33 -0
- package/test/scalar.test.ts +345 -0
- package/vitest.config.e2e.ts +20 -0
- package/vitest.config.ts +8 -1
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { ModelProperty, NoTarget, Scalar } from "@typespec/compiler";
|
|
2
|
+
import { BasicTestRunner, createTestRunner } from "@typespec/compiler/testing";
|
|
3
|
+
import { deepStrictEqual, strictEqual } from "assert";
|
|
4
|
+
import { beforeEach, describe, it } from "vitest";
|
|
5
|
+
import { getJsScalar } from "../src/common/scalar.js";
|
|
6
|
+
import { createPathCursor, JsContext, Module } from "../src/ctx.js";
|
|
7
|
+
|
|
8
|
+
import { module as dateTimeModule } from "../generated-defs/helpers/datetime.js";
|
|
9
|
+
|
|
10
|
+
describe("scalar", () => {
|
|
11
|
+
let runner: BasicTestRunner;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
runner = await createTestRunner();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function createFakeModule(): [JsContext, Module] {
|
|
18
|
+
const module: Module = {
|
|
19
|
+
name: "example",
|
|
20
|
+
cursor: createPathCursor(),
|
|
21
|
+
|
|
22
|
+
imports: [],
|
|
23
|
+
declarations: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Min context
|
|
27
|
+
const ctx: JsContext = {
|
|
28
|
+
program: runner.program,
|
|
29
|
+
rootModule: module,
|
|
30
|
+
} as JsContext;
|
|
31
|
+
|
|
32
|
+
return [ctx, module];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function getScalar(...names: string[]): Promise<Scalar[]> {
|
|
36
|
+
const { test } = (await runner.compile(`
|
|
37
|
+
model Example {
|
|
38
|
+
@test test: [${names.join(", ")}];
|
|
39
|
+
}
|
|
40
|
+
`)) as { test: ModelProperty };
|
|
41
|
+
|
|
42
|
+
if (test.type.kind !== "Tuple") {
|
|
43
|
+
throw new Error("Expected tuple type");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!test.type.values.every((t) => t.kind === "Scalar")) {
|
|
47
|
+
throw new Error("Expected scalar types only");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return test.type.values as Scalar[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
it("has no-op encoding for string", async () => {
|
|
54
|
+
const [string] = await getScalar("TypeSpec.string");
|
|
55
|
+
|
|
56
|
+
const [ctx, mod] = createFakeModule();
|
|
57
|
+
|
|
58
|
+
const jsScalar = getJsScalar(ctx, mod, string, NoTarget);
|
|
59
|
+
|
|
60
|
+
strictEqual(jsScalar.type, "string");
|
|
61
|
+
strictEqual(jsScalar.getEncoding("default", string)?.encode("asdf"), "(asdf)");
|
|
62
|
+
strictEqual(mod.imports.length, 0);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("correctly encodes and decodes all numbers using default string encoding", async () => {
|
|
66
|
+
const [string, ...numbers] = await getScalar(
|
|
67
|
+
"string",
|
|
68
|
+
"float32",
|
|
69
|
+
"float64",
|
|
70
|
+
"int8",
|
|
71
|
+
"int16",
|
|
72
|
+
"int32",
|
|
73
|
+
"uint8",
|
|
74
|
+
"uint16",
|
|
75
|
+
"uint32",
|
|
76
|
+
"safeint",
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const [ctx, mod] = createFakeModule();
|
|
80
|
+
|
|
81
|
+
for (const number of numbers) {
|
|
82
|
+
const jsScalar = getJsScalar(ctx, mod, number, NoTarget);
|
|
83
|
+
|
|
84
|
+
strictEqual(jsScalar.type, "number");
|
|
85
|
+
|
|
86
|
+
const encoding = jsScalar.getEncoding("default", string);
|
|
87
|
+
|
|
88
|
+
if (!encoding) {
|
|
89
|
+
throw new Error("Expected default encoding");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const encoded = encoding.encode("asdf");
|
|
93
|
+
|
|
94
|
+
strictEqual(encoded, "globalThis.String((asdf))");
|
|
95
|
+
|
|
96
|
+
const decoded = encoding.decode("asdf");
|
|
97
|
+
|
|
98
|
+
strictEqual(decoded, "globalThis.Number((asdf))");
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("encodes and decodes types that coerce to bigint using default string encoding", async () => {
|
|
103
|
+
const [string, ...bigints] = await getScalar("string", "uint64", "int64", "integer");
|
|
104
|
+
|
|
105
|
+
const [ctx, mod] = createFakeModule();
|
|
106
|
+
|
|
107
|
+
for (const bigint of bigints) {
|
|
108
|
+
const jsScalar = getJsScalar(ctx, mod, bigint, NoTarget);
|
|
109
|
+
|
|
110
|
+
strictEqual(jsScalar.type, "bigint");
|
|
111
|
+
|
|
112
|
+
const encoding = jsScalar.getEncoding("default", string);
|
|
113
|
+
|
|
114
|
+
if (!encoding) {
|
|
115
|
+
throw new Error("Expected default encoding");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const encoded = encoding.encode("asdf");
|
|
119
|
+
|
|
120
|
+
strictEqual(encoded, "globalThis.String((asdf))");
|
|
121
|
+
|
|
122
|
+
const decoded = encoding.decode("asdf");
|
|
123
|
+
|
|
124
|
+
strictEqual(decoded, "globalThis.BigInt((asdf))");
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("bytes base64 encoding", async () => {
|
|
129
|
+
const [string, bytes] = await getScalar("TypeSpec.string", "TypeSpec.bytes");
|
|
130
|
+
|
|
131
|
+
const [ctx, mod] = createFakeModule();
|
|
132
|
+
|
|
133
|
+
const jsScalar = getJsScalar(ctx, mod, bytes, NoTarget);
|
|
134
|
+
|
|
135
|
+
strictEqual(jsScalar.type, "Uint8Array");
|
|
136
|
+
|
|
137
|
+
const encoding = jsScalar.getEncoding("base64", string);
|
|
138
|
+
|
|
139
|
+
if (!encoding) {
|
|
140
|
+
throw new Error("Expected base64 encoding");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const encoded = encoding.encode("asdf");
|
|
144
|
+
|
|
145
|
+
strictEqual(
|
|
146
|
+
encoded,
|
|
147
|
+
"((asdf) instanceof globalThis.Buffer ? (asdf) : globalThis.Buffer.from((asdf))).toString('base64')",
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const decoded = encoding.decode("asdf");
|
|
151
|
+
|
|
152
|
+
strictEqual(decoded, "globalThis.Buffer.from((asdf), 'base64')");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("bytes base64url encoding", async () => {
|
|
156
|
+
const [string, bytes] = await getScalar("TypeSpec.string", "TypeSpec.bytes");
|
|
157
|
+
|
|
158
|
+
const [ctx, mod] = createFakeModule();
|
|
159
|
+
|
|
160
|
+
const jsScalar = getJsScalar(ctx, mod, bytes, NoTarget);
|
|
161
|
+
|
|
162
|
+
strictEqual(jsScalar.type, "Uint8Array");
|
|
163
|
+
|
|
164
|
+
const encoding = jsScalar.getEncoding("base64url", string);
|
|
165
|
+
|
|
166
|
+
if (!encoding) {
|
|
167
|
+
throw new Error("Expected base64url encoding");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const encoded = encoding.encode("asdf");
|
|
171
|
+
|
|
172
|
+
strictEqual(
|
|
173
|
+
encoded,
|
|
174
|
+
"globalThis.encodeURIComponent((((asdf)) instanceof globalThis.Buffer ? ((asdf)) : globalThis.Buffer.from(((asdf)))).toString('base64'))",
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const decoded = encoding.decode("asdf");
|
|
178
|
+
|
|
179
|
+
strictEqual(
|
|
180
|
+
decoded,
|
|
181
|
+
"globalThis.Buffer.from((globalThis.decodeURIComponent((asdf))), 'base64')",
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe("duration", () => {
|
|
186
|
+
it("produces correct parse template for ISO8601 duration", async () => {
|
|
187
|
+
const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
|
|
188
|
+
|
|
189
|
+
const [ctx, mod] = createFakeModule();
|
|
190
|
+
|
|
191
|
+
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
|
|
192
|
+
|
|
193
|
+
strictEqual(jsScalar.type, "Duration");
|
|
194
|
+
strictEqual(
|
|
195
|
+
jsScalar.getEncoding("ISO8601", string)?.decode("asdf"),
|
|
196
|
+
"Duration.parseISO8601((asdf))",
|
|
197
|
+
);
|
|
198
|
+
strictEqual(mod.imports[0].from, dateTimeModule);
|
|
199
|
+
deepStrictEqual(mod.imports[0].binder, ["Duration"]);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("produces correct write template for ISO8601 duration", async () => {
|
|
203
|
+
const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
|
|
204
|
+
|
|
205
|
+
const [ctx, mod] = createFakeModule();
|
|
206
|
+
|
|
207
|
+
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
|
|
208
|
+
|
|
209
|
+
strictEqual(jsScalar.type, "Duration");
|
|
210
|
+
strictEqual(
|
|
211
|
+
jsScalar.getEncoding("ISO8601", string)?.encode("asdf"),
|
|
212
|
+
"Duration.toISO8601((asdf))",
|
|
213
|
+
);
|
|
214
|
+
strictEqual(mod.imports[0].from, dateTimeModule);
|
|
215
|
+
deepStrictEqual(mod.imports[0].binder, ["Duration"]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("can parse and write ISO8601 duration", async () => {
|
|
219
|
+
const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
|
|
220
|
+
|
|
221
|
+
const [ctx, mod] = createFakeModule();
|
|
222
|
+
|
|
223
|
+
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
|
|
224
|
+
|
|
225
|
+
strictEqual(jsScalar.type, "Duration");
|
|
226
|
+
|
|
227
|
+
const encoding = jsScalar.getEncoding("ISO8601", string);
|
|
228
|
+
|
|
229
|
+
if (!encoding) {
|
|
230
|
+
throw new Error("Expected ISO8601 encoding");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const encoded = encoding.encode("duration");
|
|
234
|
+
|
|
235
|
+
strictEqual(encoded, "Duration.toISO8601((duration))");
|
|
236
|
+
|
|
237
|
+
const decoded = encoding.decode('"P1Y2M3DT4H5M6S"');
|
|
238
|
+
|
|
239
|
+
strictEqual(decoded, 'Duration.parseISO8601(("P1Y2M3DT4H5M6S"))');
|
|
240
|
+
|
|
241
|
+
strictEqual(mod.imports[0].from, dateTimeModule);
|
|
242
|
+
deepStrictEqual(mod.imports[0].binder, ["Duration"]);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("allows default string encoding through via", async () => {
|
|
246
|
+
const [Duration, string] = await getScalar("duration", "string");
|
|
247
|
+
|
|
248
|
+
const [ctx, mod] = createFakeModule();
|
|
249
|
+
|
|
250
|
+
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
|
|
251
|
+
|
|
252
|
+
strictEqual(jsScalar.type, "Duration");
|
|
253
|
+
|
|
254
|
+
const encoding = jsScalar.getEncoding("default", string);
|
|
255
|
+
|
|
256
|
+
if (!encoding) {
|
|
257
|
+
throw new Error("Expected default encoding");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const encoded = encoding.encode("duration");
|
|
261
|
+
|
|
262
|
+
strictEqual(encoded, "Duration.toISO8601(((duration)))");
|
|
263
|
+
|
|
264
|
+
const decoded = encoding.decode("duration");
|
|
265
|
+
|
|
266
|
+
strictEqual(decoded, "Duration.parseISO8601(((duration)))");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("allows encoding seconds to number types", async () => {
|
|
270
|
+
const [Duration, int32, uint32] = await getScalar("duration", "int32", "uint32");
|
|
271
|
+
|
|
272
|
+
const [ctx, mod] = createFakeModule();
|
|
273
|
+
|
|
274
|
+
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
|
|
275
|
+
|
|
276
|
+
strictEqual(jsScalar.type, "Duration");
|
|
277
|
+
|
|
278
|
+
const encodingInt32 = jsScalar.getEncoding("seconds", int32);
|
|
279
|
+
|
|
280
|
+
if (!encodingInt32) {
|
|
281
|
+
throw new Error("Expected seconds encoding int32");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const encodedInt32 = encodingInt32.encode("duration");
|
|
285
|
+
|
|
286
|
+
strictEqual(encodedInt32, "Duration.totalSeconds((duration))");
|
|
287
|
+
|
|
288
|
+
const decodedInt32 = encodingInt32.decode("duration");
|
|
289
|
+
|
|
290
|
+
strictEqual(decodedInt32, "Duration.fromSeconds((duration))");
|
|
291
|
+
|
|
292
|
+
const encodingUint32 = jsScalar.getEncoding("seconds", uint32);
|
|
293
|
+
|
|
294
|
+
if (!encodingUint32) {
|
|
295
|
+
throw new Error("Expected seconds encoding uint32");
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const encodedUint32 = encodingUint32.encode("duration");
|
|
299
|
+
|
|
300
|
+
strictEqual(encodedUint32, "Duration.totalSeconds((duration))");
|
|
301
|
+
|
|
302
|
+
const decodedUint32 = encodingUint32.decode("duration");
|
|
303
|
+
|
|
304
|
+
strictEqual(decodedUint32, "Duration.fromSeconds((duration))");
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("allows encoding seconds to bigint types", async () => {
|
|
308
|
+
const [Duration, int64, uint64] = await getScalar("duration", "int64", "uint64");
|
|
309
|
+
|
|
310
|
+
const [ctx, mod] = createFakeModule();
|
|
311
|
+
|
|
312
|
+
const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
|
|
313
|
+
|
|
314
|
+
strictEqual(jsScalar.type, "Duration");
|
|
315
|
+
|
|
316
|
+
const encodingInt64 = jsScalar.getEncoding("seconds", int64);
|
|
317
|
+
|
|
318
|
+
if (!encodingInt64) {
|
|
319
|
+
throw new Error("Expected seconds encoding int64");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const encodedInt64 = encodingInt64.encode("duration");
|
|
323
|
+
|
|
324
|
+
strictEqual(encodedInt64, "Duration.totalSecondsBigInt((duration))");
|
|
325
|
+
|
|
326
|
+
const decodedInt64 = encodingInt64.decode("duration");
|
|
327
|
+
|
|
328
|
+
strictEqual(decodedInt64, "Duration.fromSeconds(globalThis.Number((duration)))");
|
|
329
|
+
|
|
330
|
+
const encodingUint64 = jsScalar.getEncoding("seconds", uint64);
|
|
331
|
+
|
|
332
|
+
if (!encodingUint64) {
|
|
333
|
+
throw new Error("Expected seconds encoding uint64");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const encodedUint64 = encodingUint64.encode("duration");
|
|
337
|
+
|
|
338
|
+
strictEqual(encodedUint64, "Duration.totalSecondsBigInt((duration))");
|
|
339
|
+
|
|
340
|
+
const decodedUint64 = encodingUint64.decode("duration");
|
|
341
|
+
|
|
342
|
+
strictEqual(decodedUint64, "Duration.fromSeconds(globalThis.Number((duration)))");
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineConfig, mergeConfig } from "vitest/config";
|
|
2
|
+
import { defaultTypeSpecVitestConfig } from "../../vitest.workspace.js";
|
|
3
|
+
|
|
4
|
+
export default mergeConfig(
|
|
5
|
+
defaultTypeSpecVitestConfig,
|
|
6
|
+
defineConfig({
|
|
7
|
+
test: {
|
|
8
|
+
environment: "node",
|
|
9
|
+
testTimeout: 10_000,
|
|
10
|
+
isolate: false,
|
|
11
|
+
coverage: {
|
|
12
|
+
reporter: ["cobertura", "json", "text"],
|
|
13
|
+
},
|
|
14
|
+
outputFile: {
|
|
15
|
+
junit: "./test-results.xml",
|
|
16
|
+
},
|
|
17
|
+
include: ["test/**/*.e2e.ts"],
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
);
|
package/vitest.config.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { defineConfig, mergeConfig } from "vitest/config";
|
|
2
2
|
import { defaultTypeSpecVitestConfig } from "../../vitest.workspace.js";
|
|
3
3
|
|
|
4
|
-
export default mergeConfig(
|
|
4
|
+
export default mergeConfig(
|
|
5
|
+
defaultTypeSpecVitestConfig,
|
|
6
|
+
defineConfig({
|
|
7
|
+
test: {
|
|
8
|
+
include: ["test/**/*.test.ts"],
|
|
9
|
+
},
|
|
10
|
+
}),
|
|
11
|
+
);
|