@typespec/http-server-js 0.58.0-alpha.13-dev.8 → 0.58.0-alpha.13-dev.10

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 (70) hide show
  1. package/.testignore +0 -1
  2. package/README.md +13 -0
  3. package/build-helpers.ts +22 -9
  4. package/dist/generated-defs/helpers/index.d.ts.map +1 -1
  5. package/dist/generated-defs/helpers/index.js +1 -0
  6. package/dist/generated-defs/helpers/index.js.map +1 -1
  7. package/dist/generated-defs/helpers/temporal/index.d.ts +4 -0
  8. package/dist/generated-defs/helpers/temporal/index.d.ts.map +1 -0
  9. package/dist/generated-defs/helpers/temporal/index.js +19 -0
  10. package/dist/generated-defs/helpers/temporal/index.js.map +1 -0
  11. package/dist/generated-defs/helpers/temporal/native.d.ts +4 -0
  12. package/dist/generated-defs/helpers/temporal/native.d.ts.map +1 -0
  13. package/dist/generated-defs/helpers/temporal/native.js +125 -0
  14. package/dist/generated-defs/helpers/temporal/native.js.map +1 -0
  15. package/dist/generated-defs/helpers/temporal/polyfill.d.ts +4 -0
  16. package/dist/generated-defs/helpers/temporal/polyfill.d.ts.map +1 -0
  17. package/dist/generated-defs/helpers/temporal/polyfill.js +124 -0
  18. package/dist/generated-defs/helpers/temporal/polyfill.js.map +1 -0
  19. package/dist/generated-defs/package.json.d.ts.map +1 -1
  20. package/dist/generated-defs/package.json.js +1 -0
  21. package/dist/generated-defs/package.json.js.map +1 -1
  22. package/dist/src/common/declaration.js +1 -1
  23. package/dist/src/common/declaration.js.map +1 -1
  24. package/dist/src/common/scalar.d.ts +1 -1
  25. package/dist/src/common/scalar.d.ts.map +1 -1
  26. package/dist/src/common/scalar.js +341 -49
  27. package/dist/src/common/scalar.js.map +1 -1
  28. package/dist/src/common/serialization/index.d.ts +2 -2
  29. package/dist/src/common/serialization/index.d.ts.map +1 -1
  30. package/dist/src/common/serialization/index.js +3 -9
  31. package/dist/src/common/serialization/index.js.map +1 -1
  32. package/dist/src/common/serialization/json.d.ts +2 -0
  33. package/dist/src/common/serialization/json.d.ts.map +1 -1
  34. package/dist/src/common/serialization/json.js +10 -19
  35. package/dist/src/common/serialization/json.js.map +1 -1
  36. package/dist/src/helpers/temporal/native.d.ts +40 -0
  37. package/dist/src/helpers/temporal/native.d.ts.map +1 -0
  38. package/dist/src/helpers/temporal/native.js +81 -0
  39. package/dist/src/helpers/temporal/native.js.map +1 -0
  40. package/dist/src/helpers/temporal/polyfill.d.ts +41 -0
  41. package/dist/src/helpers/temporal/polyfill.d.ts.map +1 -0
  42. package/dist/src/helpers/temporal/polyfill.js +80 -0
  43. package/dist/src/helpers/temporal/polyfill.js.map +1 -0
  44. package/dist/src/http/server/index.d.ts.map +1 -1
  45. package/dist/src/http/server/index.js +4 -1
  46. package/dist/src/http/server/index.js.map +1 -1
  47. package/dist/src/lib.d.ts +9 -0
  48. package/dist/src/lib.d.ts.map +1 -1
  49. package/dist/src/lib.js +7 -0
  50. package/dist/src/lib.js.map +1 -1
  51. package/dist/src/scripts/scaffold/data-mocks.d.ts.map +1 -1
  52. package/dist/src/scripts/scaffold/data-mocks.js +91 -32
  53. package/dist/src/scripts/scaffold/data-mocks.js.map +1 -1
  54. package/generated-defs/helpers/index.ts +1 -0
  55. package/generated-defs/helpers/temporal/index.ts +25 -0
  56. package/generated-defs/helpers/temporal/native.ts +132 -0
  57. package/generated-defs/helpers/temporal/polyfill.ts +131 -0
  58. package/generated-defs/package.json.ts +1 -0
  59. package/package.json +6 -5
  60. package/src/common/declaration.ts +1 -1
  61. package/src/common/scalar.ts +390 -62
  62. package/src/common/serialization/index.ts +4 -13
  63. package/src/common/serialization/json.ts +22 -25
  64. package/src/helpers/temporal/native.ts +104 -0
  65. package/src/helpers/temporal/polyfill.ts +103 -0
  66. package/src/http/server/index.ts +6 -1
  67. package/src/lib.ts +17 -0
  68. package/src/scripts/scaffold/data-mocks.ts +97 -32
  69. package/temp/tsconfig.tsbuildinfo +1 -1
  70. package/test/scalar.test.ts +547 -97
@@ -6,6 +6,9 @@ import { getJsScalar } from "../src/common/scalar.js";
6
6
  import { createPathCursor, JsContext, Module } from "../src/ctx.js";
7
7
 
8
8
  import { module as dateTimeModule } from "../generated-defs/helpers/datetime.js";
9
+ import { module as temporalHelpersModule } from "../generated-defs/helpers/temporal/native.js";
10
+ import { module as temporalPolyfillHelpersModule } from "../generated-defs/helpers/temporal/polyfill.js";
11
+ import { JsEmitterOptions } from "../src/lib.js";
9
12
 
10
13
  describe("scalar", () => {
11
14
  let runner: BasicTestRunner;
@@ -14,7 +17,7 @@ describe("scalar", () => {
14
17
  runner = await createTestRunner();
15
18
  });
16
19
 
17
- function createFakeModule(): [JsContext, Module] {
20
+ function createFakeModule(options?: JsEmitterOptions): [JsContext, Module] {
18
21
  const module: Module = {
19
22
  name: "example",
20
23
  cursor: createPathCursor(),
@@ -27,6 +30,7 @@ describe("scalar", () => {
27
30
  const ctx: JsContext = {
28
31
  program: runner.program,
29
32
  rootModule: module,
33
+ options: options ?? {},
30
34
  } as JsContext;
31
35
 
32
36
  return [ctx, module];
@@ -182,164 +186,610 @@ describe("scalar", () => {
182
186
  );
183
187
  });
184
188
 
185
- describe("duration", () => {
186
- it("produces correct parse template for ISO8601 duration", async () => {
187
- const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
189
+ describe("date/time/duration types", () => {
190
+ describe("mode: temporal", () => {
191
+ const options: JsEmitterOptions = {
192
+ "no-format": false,
193
+ "omit-unreachable-types": false,
194
+ express: false,
195
+ datetime: "temporal",
196
+ };
197
+
198
+ describe("date", () => {
199
+ it("produces correct parse template for ISO8601 utcDateTime", async () => {
200
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
188
201
 
189
- const [ctx, mod] = createFakeModule();
202
+ const [ctx, mod] = createFakeModule(options);
190
203
 
191
- const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
204
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
192
205
 
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
- });
206
+ strictEqual(jsScalar.type, "Temporal.Instant");
207
+ strictEqual(
208
+ jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
209
+ "globalThis.Temporal.Instant.from((asdf))",
210
+ );
211
+ deepStrictEqual(mod.imports, []);
212
+ });
201
213
 
202
- it("produces correct write template for ISO8601 duration", async () => {
203
- const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
214
+ it("produces correct write template for ISO8601 utcDateTime", async () => {
215
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
204
216
 
205
- const [ctx, mod] = createFakeModule();
217
+ const [ctx, mod] = createFakeModule(options);
206
218
 
207
- const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
219
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
208
220
 
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
- });
221
+ strictEqual(jsScalar.type, "Temporal.Instant");
222
+ strictEqual(
223
+ jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
224
+ "((asdf)).toString()",
225
+ );
226
+ deepStrictEqual(mod.imports, []);
227
+ });
217
228
 
218
- it("can parse and write ISO8601 duration", async () => {
219
- const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
229
+ it("allows default string encoding through via", async () => {
230
+ const [utcDateTime, string] = await getScalar("utcDateTime", "string");
220
231
 
221
- const [ctx, mod] = createFakeModule();
232
+ const [ctx, mod] = createFakeModule(options);
222
233
 
223
- const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
234
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
224
235
 
225
- strictEqual(jsScalar.type, "Duration");
236
+ strictEqual(jsScalar.type, "Temporal.Instant");
226
237
 
227
- const encoding = jsScalar.getEncoding("ISO8601", string);
238
+ const encoding = jsScalar.getEncoding("default", string);
228
239
 
229
- if (!encoding) {
230
- throw new Error("Expected ISO8601 encoding");
231
- }
240
+ if (!encoding) {
241
+ throw new Error("Expected default encoding");
242
+ }
243
+
244
+ const encoded = encoding.encode("asdf");
245
+
246
+ strictEqual(encoded, "(((asdf))).toString()");
247
+
248
+ const decoded = encoding.decode("asdf");
249
+
250
+ strictEqual(decoded, "globalThis.Temporal.Instant.from(((asdf)))");
251
+ });
252
+
253
+ it("transcodes to rfc7231", async () => {
254
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
255
+ const [ctx, mod] = createFakeModule(options);
256
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
257
+ strictEqual(jsScalar.type, "Temporal.Instant");
258
+ const encoding = jsScalar.getEncoding("rfc7231", string);
259
+ if (!encoding) {
260
+ throw new Error("Expected rfc7231 encoding");
261
+ }
262
+ const encoded = encoding.encode("asdf");
263
+ strictEqual(encoded, "formatHttpDate((asdf))");
264
+ const decoded = encoding.decode("asdf");
265
+ strictEqual(decoded, "parseHttpDate((asdf))");
266
+ strictEqual(mod.imports[0].from, temporalHelpersModule);
267
+ deepStrictEqual(mod.imports[0].binder, ["formatHttpDate"]);
268
+ strictEqual(mod.imports[1].from, temporalHelpersModule);
269
+ deepStrictEqual(mod.imports[1].binder, ["parseHttpDate"]);
270
+ });
271
+
272
+ describe("ZonedDateTime", () => {
273
+ // Same as above, but with TypeSpec.offsetDateTime represented as Temporal.ZonedDateTime
274
+
275
+ it("produces correct parse template for ISO8601 offsetDateTime", async () => {
276
+ const [offsetDateTime, string] = await getScalar(
277
+ "TypeSpec.offsetDateTime",
278
+ "TypeSpec.string",
279
+ );
280
+
281
+ const [ctx, mod] = createFakeModule(options);
282
+
283
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
284
+
285
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
286
+ strictEqual(
287
+ jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
288
+ "globalThis.Temporal.ZonedDateTime.from((asdf))",
289
+ );
290
+ deepStrictEqual(mod.imports, []);
291
+ });
292
+
293
+ it("produces correct write template for ISO8601 offsetDateTime", async () => {
294
+ const [offsetDateTime, string] = await getScalar(
295
+ "TypeSpec.offsetDateTime",
296
+ "TypeSpec.string",
297
+ );
298
+
299
+ const [ctx, mod] = createFakeModule(options);
300
+
301
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
302
+
303
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
304
+ strictEqual(
305
+ jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
306
+ "((asdf)).toString()",
307
+ );
308
+ deepStrictEqual(mod.imports, []);
309
+ });
310
+
311
+ it("allows default string encoding through via", async () => {
312
+ const [offsetDateTime, string] = await getScalar("offsetDateTime", "string");
313
+
314
+ const [ctx, mod] = createFakeModule(options);
315
+
316
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
317
+
318
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
319
+
320
+ const encoding = jsScalar.getEncoding("default", string);
321
+
322
+ if (!encoding) {
323
+ throw new Error("Expected default encoding");
324
+ }
325
+
326
+ const encoded = encoding.encode("asdf");
232
327
 
233
- const encoded = encoding.encode("duration");
328
+ strictEqual(encoded, "(((asdf))).toString()");
234
329
 
235
- strictEqual(encoded, "Duration.toISO8601((duration))");
330
+ const decoded = encoding.decode("asdf");
236
331
 
237
- const decoded = encoding.decode('"P1Y2M3DT4H5M6S"');
332
+ strictEqual(decoded, "globalThis.Temporal.ZonedDateTime.from(((asdf)))");
333
+ });
238
334
 
239
- strictEqual(decoded, 'Duration.parseISO8601(("P1Y2M3DT4H5M6S"))');
335
+ it("transcodes to rfc7231", async () => {
336
+ const [offsetDateTime, string] = await getScalar(
337
+ "TypeSpec.offsetDateTime",
338
+ "TypeSpec.string",
339
+ );
240
340
 
241
- strictEqual(mod.imports[0].from, dateTimeModule);
242
- deepStrictEqual(mod.imports[0].binder, ["Duration"]);
341
+ const [ctx, mod] = createFakeModule(options);
342
+
343
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
344
+
345
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
346
+
347
+ const encoding = jsScalar.getEncoding("rfc7231", string);
348
+
349
+ if (!encoding) {
350
+ throw new Error("Expected rfc7231 encoding");
351
+ }
352
+
353
+ const encoded = encoding.encode("asdf");
354
+
355
+ strictEqual(encoded, "formatHttpDate(((asdf)).toInstant())");
356
+
357
+ const decoded = encoding.decode("asdf");
358
+
359
+ strictEqual(decoded, 'parseHttpDate((asdf)).toZonedDateTimeISO("UTC")');
360
+
361
+ strictEqual(mod.imports[0].from, temporalHelpersModule);
362
+ deepStrictEqual(mod.imports[0].binder, ["formatHttpDate"]);
363
+ strictEqual(mod.imports[1].from, temporalHelpersModule);
364
+ deepStrictEqual(mod.imports[1].binder, ["parseHttpDate"]);
365
+ });
366
+ });
367
+ });
243
368
  });
244
369
 
245
- it("allows default string encoding through via", async () => {
246
- const [Duration, string] = await getScalar("duration", "string");
370
+ describe("mode: temporal-polyfill", () => {
371
+ const options: JsEmitterOptions = {
372
+ "no-format": false,
373
+ "omit-unreachable-types": false,
374
+ express: false,
375
+ datetime: "temporal-polyfill",
376
+ };
377
+
378
+ describe("date", () => {
379
+ it("produces correct parse template for ISO8601 utcDateTime", async () => {
380
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
381
+
382
+ const [ctx, mod] = createFakeModule(options);
383
+
384
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
385
+
386
+ strictEqual(jsScalar.type, "Temporal.Instant");
387
+ strictEqual(
388
+ jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
389
+ "Temporal.Instant.from((asdf))",
390
+ );
391
+ strictEqual(mod.imports[0].from, "temporal-polyfill");
392
+ deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
393
+ });
394
+
395
+ it("produces correct write template for ISO8601 utcDateTime", async () => {
396
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
397
+
398
+ const [ctx, mod] = createFakeModule(options);
399
+
400
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
401
+
402
+ strictEqual(jsScalar.type, "Temporal.Instant");
403
+ strictEqual(
404
+ jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
405
+ "((asdf)).toString()",
406
+ );
407
+ strictEqual(mod.imports[0].from, "temporal-polyfill");
408
+ deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
409
+ });
247
410
 
248
- const [ctx, mod] = createFakeModule();
411
+ it("allows default string encoding through via", async () => {
412
+ const [utcDateTime, string] = await getScalar("utcDateTime", "string");
249
413
 
250
- const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
414
+ const [ctx, mod] = createFakeModule(options);
251
415
 
252
- strictEqual(jsScalar.type, "Duration");
416
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
253
417
 
254
- const encoding = jsScalar.getEncoding("default", string);
418
+ strictEqual(jsScalar.type, "Temporal.Instant");
255
419
 
256
- if (!encoding) {
257
- throw new Error("Expected default encoding");
258
- }
420
+ const encoding = jsScalar.getEncoding("default", string);
421
+
422
+ if (!encoding) {
423
+ throw new Error("Expected default encoding");
424
+ }
259
425
 
260
- const encoded = encoding.encode("duration");
426
+ const encoded = encoding.encode("asdf");
261
427
 
262
- strictEqual(encoded, "Duration.toISO8601(((duration)))");
428
+ strictEqual(encoded, "(((asdf))).toString()");
263
429
 
264
- const decoded = encoding.decode("duration");
430
+ const decoded = encoding.decode("asdf");
265
431
 
266
- strictEqual(decoded, "Duration.parseISO8601(((duration)))");
432
+ strictEqual(decoded, "Temporal.Instant.from(((asdf)))");
433
+ });
434
+
435
+ it("transcodes to rfc7231", async () => {
436
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
437
+ const [ctx, mod] = createFakeModule(options);
438
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
439
+ strictEqual(jsScalar.type, "Temporal.Instant");
440
+ const encoding = jsScalar.getEncoding("rfc7231", string);
441
+ if (!encoding) {
442
+ throw new Error("Expected rfc7231 encoding");
443
+ }
444
+ const encoded = encoding.encode("asdf");
445
+ strictEqual(encoded, "formatHttpDate((asdf))");
446
+ const decoded = encoding.decode("asdf");
447
+ strictEqual(decoded, "parseHttpDate((asdf))");
448
+ strictEqual(mod.imports[0].from, "temporal-polyfill");
449
+ deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
450
+ strictEqual(mod.imports[1].from, temporalPolyfillHelpersModule);
451
+ deepStrictEqual(mod.imports[1].binder, ["formatHttpDate"]);
452
+ strictEqual(mod.imports[2].from, temporalPolyfillHelpersModule);
453
+ deepStrictEqual(mod.imports[2].binder, ["parseHttpDate"]);
454
+ });
455
+
456
+ describe("ZonedDateTime", () => {
457
+ // Same as above, but with TypeSpec.offsetDateTime represented as Temporal.ZonedDateTime
458
+
459
+ it("produces correct parse template for ISO8601 offsetDateTime", async () => {
460
+ const [offsetDateTime, string] = await getScalar(
461
+ "TypeSpec.offsetDateTime",
462
+ "TypeSpec.string",
463
+ );
464
+
465
+ const [ctx, mod] = createFakeModule(options);
466
+
467
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
468
+
469
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
470
+ strictEqual(
471
+ jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
472
+ "Temporal.ZonedDateTime.from((asdf))",
473
+ );
474
+ strictEqual(mod.imports[0].from, "temporal-polyfill");
475
+ deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
476
+ });
477
+
478
+ it("produces correct write template for ISO8601 offsetDateTime", async () => {
479
+ const [offsetDateTime, string] = await getScalar(
480
+ "TypeSpec.offsetDateTime",
481
+ "TypeSpec.string",
482
+ );
483
+
484
+ const [ctx, mod] = createFakeModule(options);
485
+
486
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
487
+
488
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
489
+ strictEqual(
490
+ jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
491
+ "((asdf)).toString()",
492
+ );
493
+ strictEqual(mod.imports[0].from, "temporal-polyfill");
494
+ deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
495
+ });
496
+
497
+ it("allows default string encoding through via", async () => {
498
+ const [offsetDateTime, string] = await getScalar("offsetDateTime", "string");
499
+
500
+ const [ctx, mod] = createFakeModule(options);
501
+
502
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
503
+
504
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
505
+
506
+ const encoding = jsScalar.getEncoding("default", string);
507
+
508
+ if (!encoding) {
509
+ throw new Error("Expected default encoding");
510
+ }
511
+
512
+ const encoded = encoding.encode("asdf");
513
+
514
+ strictEqual(encoded, "(((asdf))).toString()");
515
+
516
+ const decoded = encoding.decode("asdf");
517
+
518
+ strictEqual(decoded, "Temporal.ZonedDateTime.from(((asdf)))");
519
+ });
520
+
521
+ it("transcodes to rfc7231", async () => {
522
+ const [offsetDateTime, string] = await getScalar(
523
+ "TypeSpec.offsetDateTime",
524
+ "TypeSpec.string",
525
+ );
526
+ const [ctx, mod] = createFakeModule(options);
527
+ const jsScalar = getJsScalar(ctx, mod, offsetDateTime, NoTarget);
528
+ strictEqual(jsScalar.type, "Temporal.ZonedDateTime");
529
+ const encoding = jsScalar.getEncoding("rfc7231", string);
530
+ if (!encoding) {
531
+ throw new Error("Expected rfc7231 encoding");
532
+ }
533
+ const encoded = encoding.encode("asdf");
534
+ strictEqual(encoded, "formatHttpDate(((asdf)).toInstant())");
535
+ const decoded = encoding.decode("asdf");
536
+ strictEqual(decoded, 'parseHttpDate((asdf)).toZonedDateTimeISO("UTC")');
537
+ strictEqual(mod.imports[0].from, "temporal-polyfill");
538
+ deepStrictEqual(mod.imports[0].binder, ["Temporal"]);
539
+ strictEqual(mod.imports[1].from, temporalPolyfillHelpersModule);
540
+ deepStrictEqual(mod.imports[1].binder, ["formatHttpDate"]);
541
+ strictEqual(mod.imports[2].from, temporalPolyfillHelpersModule);
542
+ deepStrictEqual(mod.imports[2].binder, ["parseHttpDate"]);
543
+ });
544
+ });
545
+ });
267
546
  });
268
547
 
269
- it("allows encoding seconds to number types", async () => {
270
- const [Duration, int32, uint32] = await getScalar("duration", "int32", "uint32");
548
+ describe("mode: date-duration", () => {
549
+ const options: JsEmitterOptions = {
550
+ "no-format": false,
551
+ "omit-unreachable-types": false,
552
+ express: false,
553
+ datetime: "date-duration",
554
+ };
271
555
 
272
- const [ctx, mod] = createFakeModule();
556
+ describe("date", () => {
557
+ it("produces correct parse template for ISO8601 utcDateTime", async () => {
558
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
273
559
 
274
- const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
560
+ const [ctx, mod] = createFakeModule(options);
275
561
 
276
- strictEqual(jsScalar.type, "Duration");
562
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
277
563
 
278
- const encodingInt32 = jsScalar.getEncoding("seconds", int32);
564
+ strictEqual(jsScalar.type, "Date");
565
+ strictEqual(
566
+ jsScalar.getEncoding("iso8601", string)?.decode("asdf"),
567
+ "new globalThis.Date((asdf))",
568
+ );
569
+ deepStrictEqual(mod.imports, []);
570
+ });
279
571
 
280
- if (!encodingInt32) {
281
- throw new Error("Expected seconds encoding int32");
282
- }
572
+ it("produces correct write template for ISO8601 utcDateTime", async () => {
573
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
283
574
 
284
- const encodedInt32 = encodingInt32.encode("duration");
575
+ const [ctx, mod] = createFakeModule(options);
285
576
 
286
- strictEqual(encodedInt32, "Duration.totalSeconds((duration))");
577
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
287
578
 
288
- const decodedInt32 = encodingInt32.decode("duration");
579
+ strictEqual(jsScalar.type, "Date");
580
+ strictEqual(
581
+ jsScalar.getEncoding("iso8601", string)?.encode("asdf"),
582
+ "((asdf)).toISOString()",
583
+ );
584
+ deepStrictEqual(mod.imports, []);
585
+ });
289
586
 
290
- strictEqual(decodedInt32, "Duration.fromSeconds((duration))");
587
+ it("allows default string encoding through via", async () => {
588
+ const [utcDateTime, string] = await getScalar("utcDateTime", "string");
291
589
 
292
- const encodingUint32 = jsScalar.getEncoding("seconds", uint32);
590
+ const [ctx, mod] = createFakeModule(options);
293
591
 
294
- if (!encodingUint32) {
295
- throw new Error("Expected seconds encoding uint32");
296
- }
592
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
297
593
 
298
- const encodedUint32 = encodingUint32.encode("duration");
594
+ strictEqual(jsScalar.type, "Date");
299
595
 
300
- strictEqual(encodedUint32, "Duration.totalSeconds((duration))");
596
+ const encoding = jsScalar.getEncoding("default", string);
301
597
 
302
- const decodedUint32 = encodingUint32.decode("duration");
598
+ if (!encoding) {
599
+ throw new Error("Expected default encoding");
600
+ }
303
601
 
304
- strictEqual(decodedUint32, "Duration.fromSeconds((duration))");
305
- });
602
+ const encoded = encoding.encode("asdf");
306
603
 
307
- it("allows encoding seconds to bigint types", async () => {
308
- const [Duration, int64, uint64] = await getScalar("duration", "int64", "uint64");
604
+ strictEqual(encoded, "(((asdf))).toISOString()");
309
605
 
310
- const [ctx, mod] = createFakeModule();
606
+ const decoded = encoding.decode("asdf");
311
607
 
312
- const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
608
+ strictEqual(decoded, "new globalThis.Date(((asdf)))");
609
+ });
313
610
 
314
- strictEqual(jsScalar.type, "Duration");
611
+ it("correctly encodes and decodes rfc7231 date", async () => {
612
+ const [utcDateTime, string] = await getScalar("TypeSpec.utcDateTime", "TypeSpec.string");
315
613
 
316
- const encodingInt64 = jsScalar.getEncoding("seconds", int64);
614
+ const [ctx, mod] = createFakeModule(options);
317
615
 
318
- if (!encodingInt64) {
319
- throw new Error("Expected seconds encoding int64");
320
- }
616
+ const jsScalar = getJsScalar(ctx, mod, utcDateTime, NoTarget);
321
617
 
322
- const encodedInt64 = encodingInt64.encode("duration");
618
+ strictEqual(jsScalar.type, "Date");
323
619
 
324
- strictEqual(encodedInt64, "Duration.totalSecondsBigInt((duration))");
620
+ const encoding = jsScalar.getEncoding("rfc7231", string);
621
+ if (!encoding) {
622
+ throw new Error("Expected rfc7231 encoding");
623
+ }
325
624
 
326
- const decodedInt64 = encodingInt64.decode("duration");
625
+ const encoded = encoding.encode("asdf");
626
+ strictEqual(encoded, "((asdf)).toUTCString()");
627
+ const decoded = encoding.decode("asdf");
628
+ strictEqual(decoded, "new globalThis.Date((asdf))");
629
+ deepStrictEqual(mod.imports, []);
630
+ });
631
+ });
327
632
 
328
- strictEqual(decodedInt64, "Duration.fromSeconds(globalThis.Number((duration)))");
633
+ describe("duration", () => {
634
+ it("produces correct parse template for ISO8601 duration", async () => {
635
+ const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
329
636
 
330
- const encodingUint64 = jsScalar.getEncoding("seconds", uint64);
637
+ const [ctx, mod] = createFakeModule(options);
331
638
 
332
- if (!encodingUint64) {
333
- throw new Error("Expected seconds encoding uint64");
334
- }
639
+ const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
640
+
641
+ strictEqual(jsScalar.type, "Duration");
642
+ strictEqual(
643
+ jsScalar.getEncoding("ISO8601", string)?.decode("asdf"),
644
+ "Duration.parseISO8601((asdf))",
645
+ );
646
+ strictEqual(mod.imports[0].from, dateTimeModule);
647
+ deepStrictEqual(mod.imports[0].binder, ["Duration"]);
648
+ });
649
+
650
+ it("produces correct write template for ISO8601 duration", async () => {
651
+ const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
652
+
653
+ const [ctx, mod] = createFakeModule(options);
654
+
655
+ const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
656
+
657
+ strictEqual(jsScalar.type, "Duration");
658
+ strictEqual(
659
+ jsScalar.getEncoding("ISO8601", string)?.encode("asdf"),
660
+ "Duration.toISO8601((asdf))",
661
+ );
662
+ strictEqual(mod.imports[0].from, dateTimeModule);
663
+ deepStrictEqual(mod.imports[0].binder, ["Duration"]);
664
+ });
665
+
666
+ it("can parse and write ISO8601 duration", async () => {
667
+ const [Duration, string] = await getScalar("TypeSpec.duration", "TypeSpec.string");
668
+
669
+ const [ctx, mod] = createFakeModule(options);
670
+
671
+ const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
672
+
673
+ strictEqual(jsScalar.type, "Duration");
674
+
675
+ const encoding = jsScalar.getEncoding("ISO8601", string);
676
+
677
+ if (!encoding) {
678
+ throw new Error("Expected ISO8601 encoding");
679
+ }
680
+
681
+ const encoded = encoding.encode("duration");
682
+
683
+ strictEqual(encoded, "Duration.toISO8601((duration))");
684
+
685
+ const decoded = encoding.decode('"P1Y2M3DT4H5M6S"');
686
+
687
+ strictEqual(decoded, 'Duration.parseISO8601(("P1Y2M3DT4H5M6S"))');
688
+
689
+ strictEqual(mod.imports[0].from, dateTimeModule);
690
+ deepStrictEqual(mod.imports[0].binder, ["Duration"]);
691
+ });
692
+
693
+ it("allows default string encoding through via", async () => {
694
+ const [Duration, string] = await getScalar("duration", "string");
695
+
696
+ const [ctx, mod] = createFakeModule(options);
697
+
698
+ const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
699
+
700
+ strictEqual(jsScalar.type, "Duration");
701
+
702
+ const encoding = jsScalar.getEncoding("default", string);
703
+
704
+ if (!encoding) {
705
+ throw new Error("Expected default encoding");
706
+ }
707
+
708
+ const encoded = encoding.encode("duration");
709
+
710
+ strictEqual(encoded, "Duration.toISO8601(((duration)))");
711
+
712
+ const decoded = encoding.decode("duration");
713
+
714
+ strictEqual(decoded, "Duration.parseISO8601(((duration)))");
715
+ });
716
+
717
+ it("allows encoding seconds to number types", async () => {
718
+ const [Duration, int32, uint32] = await getScalar("duration", "int32", "uint32");
719
+
720
+ const [ctx, mod] = createFakeModule(options);
721
+
722
+ const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
723
+
724
+ strictEqual(jsScalar.type, "Duration");
725
+
726
+ const encodingInt32 = jsScalar.getEncoding("seconds", int32);
727
+
728
+ if (!encodingInt32) {
729
+ throw new Error("Expected seconds encoding int32");
730
+ }
731
+
732
+ const encodedInt32 = encodingInt32.encode("duration");
733
+
734
+ strictEqual(encodedInt32, "Duration.totalSeconds((duration))");
735
+
736
+ const decodedInt32 = encodingInt32.decode("duration");
737
+
738
+ strictEqual(decodedInt32, "Duration.fromTotalSeconds((duration))");
739
+
740
+ const encodingUint32 = jsScalar.getEncoding("seconds", uint32);
741
+
742
+ if (!encodingUint32) {
743
+ throw new Error("Expected seconds encoding uint32");
744
+ }
745
+
746
+ const encodedUint32 = encodingUint32.encode("duration");
747
+
748
+ strictEqual(encodedUint32, "Duration.totalSeconds((duration))");
749
+
750
+ const decodedUint32 = encodingUint32.decode("duration");
751
+
752
+ strictEqual(decodedUint32, "Duration.fromTotalSeconds((duration))");
753
+ });
754
+
755
+ it("allows encoding seconds to bigint types", async () => {
756
+ const [Duration, int64, uint64] = await getScalar("duration", "int64", "uint64");
757
+
758
+ const [ctx, mod] = createFakeModule(options);
759
+
760
+ const jsScalar = getJsScalar(ctx, mod, Duration, NoTarget);
761
+
762
+ strictEqual(jsScalar.type, "Duration");
763
+
764
+ const encodingInt64 = jsScalar.getEncoding("seconds", int64);
765
+
766
+ if (!encodingInt64) {
767
+ throw new Error("Expected seconds encoding int64");
768
+ }
769
+
770
+ const encodedInt64 = encodingInt64.encode("duration");
771
+
772
+ strictEqual(encodedInt64, "Duration.totalSecondsBigInt((duration))");
773
+
774
+ const decodedInt64 = encodingInt64.decode("duration");
775
+
776
+ strictEqual(decodedInt64, "Duration.fromTotalSeconds(globalThis.Number((duration)))");
777
+
778
+ const encodingUint64 = jsScalar.getEncoding("seconds", uint64);
779
+
780
+ if (!encodingUint64) {
781
+ throw new Error("Expected seconds encoding uint64");
782
+ }
335
783
 
336
- const encodedUint64 = encodingUint64.encode("duration");
784
+ const encodedUint64 = encodingUint64.encode("duration");
337
785
 
338
- strictEqual(encodedUint64, "Duration.totalSecondsBigInt((duration))");
786
+ strictEqual(encodedUint64, "Duration.totalSecondsBigInt((duration))");
339
787
 
340
- const decodedUint64 = encodingUint64.decode("duration");
788
+ const decodedUint64 = encodingUint64.decode("duration");
341
789
 
342
- strictEqual(decodedUint64, "Duration.fromSeconds(globalThis.Number((duration)))");
790
+ strictEqual(decodedUint64, "Duration.fromTotalSeconds(globalThis.Number((duration)))");
791
+ });
792
+ });
343
793
  });
344
794
  });
345
795
  });