@defold-typescript/types 0.37.0 → 0.39.0

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 (83) hide show
  1. package/api-signatures.json +348 -267
  2. package/examples/translations.json +323 -147
  3. package/generated/b2d_body.d.ts +7 -4
  4. package/generated/buffer.d.ts +16 -4
  5. package/generated/camera.d.ts +1 -0
  6. package/generated/collectionfactory.d.ts +5 -2
  7. package/generated/collectionproxy.d.ts +1 -1
  8. package/generated/factory.d.ts +2 -1
  9. package/generated/go.d.ts +141 -86
  10. package/generated/graphics.d.ts +2 -0
  11. package/generated/gui.d.ts +65 -40
  12. package/generated/html5.d.ts +1 -1
  13. package/generated/http.d.ts +4 -0
  14. package/generated/model.d.ts +9 -4
  15. package/generated/particlefx.d.ts +1 -1
  16. package/generated/physics.d.ts +16 -9
  17. package/generated/profiler.d.ts +9 -2
  18. package/generated/render.d.ts +224 -99
  19. package/generated/resource.d.ts +143 -157
  20. package/generated/socket.d.ts +13 -8
  21. package/generated/sound.d.ts +3 -2
  22. package/generated/sys.d.ts +16 -10
  23. package/generated/tilemap.d.ts +25 -19
  24. package/generated/timer.d.ts +33 -24
  25. package/generated/versions/defold-1.12.4/buffer.d.ts +16 -4
  26. package/generated/versions/defold-1.12.4/camera.d.ts +1 -0
  27. package/generated/versions/defold-1.12.4/collectionfactory.d.ts +5 -2
  28. package/generated/versions/defold-1.12.4/collectionproxy.d.ts +1 -1
  29. package/generated/versions/defold-1.12.4/factory.d.ts +2 -1
  30. package/generated/versions/defold-1.12.4/go.d.ts +141 -86
  31. package/generated/versions/defold-1.12.4/graphics.d.ts +2 -0
  32. package/generated/versions/defold-1.12.4/gui.d.ts +63 -39
  33. package/generated/versions/defold-1.12.4/html5.d.ts +1 -1
  34. package/generated/versions/defold-1.12.4/http.d.ts +4 -0
  35. package/generated/versions/defold-1.12.4/model.d.ts +9 -4
  36. package/generated/versions/defold-1.12.4/particlefx.d.ts +1 -1
  37. package/generated/versions/defold-1.12.4/physics.d.ts +16 -9
  38. package/generated/versions/defold-1.12.4/profiler.d.ts +9 -2
  39. package/generated/versions/defold-1.12.4/render.d.ts +223 -98
  40. package/generated/versions/defold-1.12.4/resource.d.ts +143 -157
  41. package/generated/versions/defold-1.12.4/socket.d.ts +13 -8
  42. package/generated/versions/defold-1.12.4/sound.d.ts +3 -2
  43. package/generated/versions/defold-1.12.4/sys.d.ts +16 -10
  44. package/generated/versions/defold-1.12.4/tilemap.d.ts +25 -19
  45. package/generated/versions/defold-1.12.4/timer.d.ts +33 -24
  46. package/generated/versions/defold-1.12.4/vmath.d.ts +13 -5
  47. package/generated/versions/defold-1.12.4/window.d.ts +5 -3
  48. package/generated/versions/defold-1.13.0/b2d_body.d.ts +7 -4
  49. package/generated/versions/defold-1.13.0/buffer.d.ts +16 -4
  50. package/generated/versions/defold-1.13.0/camera.d.ts +1 -0
  51. package/generated/versions/defold-1.13.0/collectionfactory.d.ts +5 -2
  52. package/generated/versions/defold-1.13.0/collectionproxy.d.ts +1 -1
  53. package/generated/versions/defold-1.13.0/factory.d.ts +2 -1
  54. package/generated/versions/defold-1.13.0/go.d.ts +141 -86
  55. package/generated/versions/defold-1.13.0/graphics.d.ts +2 -0
  56. package/generated/versions/defold-1.13.0/gui.d.ts +63 -39
  57. package/generated/versions/defold-1.13.0/html5.d.ts +1 -1
  58. package/generated/versions/defold-1.13.0/http.d.ts +4 -0
  59. package/generated/versions/defold-1.13.0/model.d.ts +9 -4
  60. package/generated/versions/defold-1.13.0/particlefx.d.ts +1 -1
  61. package/generated/versions/defold-1.13.0/physics.d.ts +16 -9
  62. package/generated/versions/defold-1.13.0/profiler.d.ts +9 -2
  63. package/generated/versions/defold-1.13.0/render.d.ts +224 -99
  64. package/generated/versions/defold-1.13.0/resource.d.ts +143 -157
  65. package/generated/versions/defold-1.13.0/socket.d.ts +13 -8
  66. package/generated/versions/defold-1.13.0/sound.d.ts +3 -2
  67. package/generated/versions/defold-1.13.0/sys.d.ts +16 -10
  68. package/generated/versions/defold-1.13.0/tilemap.d.ts +25 -19
  69. package/generated/versions/defold-1.13.0/timer.d.ts +33 -24
  70. package/generated/versions/defold-1.13.0/vmath.d.ts +13 -5
  71. package/generated/versions/defold-1.13.0/window.d.ts +5 -3
  72. package/generated/vmath.d.ts +13 -5
  73. package/generated/window.d.ts +5 -3
  74. package/package.json +2 -1
  75. package/src/api-doc.ts +8 -0
  76. package/src/doc-comment.ts +213 -32
  77. package/src/emit-dts.ts +501 -10
  78. package/src/example-store.ts +26 -0
  79. package/src/index.ts +5 -0
  80. package/src/lifecycle.ts +31 -8
  81. package/src/message-dispatch.d.ts +55 -13
  82. package/src/msg-overloads.d.ts +73 -3
  83. package/src/render-overloads.d.ts +0 -17
package/src/emit-dts.ts CHANGED
@@ -13,9 +13,10 @@ import {
13
13
  htmlToCodeText,
14
14
  htmlToDocText,
15
15
  renderDocComment,
16
+ splitExampleSources,
16
17
  } from "./doc-comment";
17
18
  import type { TranslationStore } from "./example-store";
18
- import { hashExampleSource, lookupTranslation } from "./example-store";
19
+ import { hashExampleSource, lookupExampleTranslations, lookupTranslation } from "./example-store";
19
20
  import { classifyUrlParameter, type UrlParameterTable } from "./url-parameters";
20
21
 
21
22
  export interface EmitOptions {
@@ -415,6 +416,377 @@ export const CONSTANT_SLOT_RESOLUTIONS: ReadonlyMap<string, ConstantSlotResoluti
415
416
  ],
416
417
  ]);
417
418
 
419
+ export interface ConstantUnionAlias {
420
+ // The namespace the alias is declared in. It is the namespace the union
421
+ // *means*, which is not always the one its constants live in: the three
422
+ // buffer keys `render.clear` accepts are `graphics.BUFFER_TYPE_*` constants,
423
+ // but the name that reads right at the call site is `render.ClearBufferKey`.
424
+ readonly home: string;
425
+ readonly name: string;
426
+ // In documentation order, which is the order the arms are emitted in today.
427
+ readonly members: readonly string[];
428
+ }
429
+
430
+ // A documented constant union gets a public name so a caller can spell the type
431
+ // their own wrapper takes. Identity is the *member set*, never a name prefix:
432
+ // `gui.ANCHOR_*` splits into two rows because the x and y slots accept
433
+ // different thirds of it, and `graphics.BUFFER_TYPE_*` splits because
434
+ // `render.clear` accepts three of the six.
435
+ //
436
+ // The table is the only thing the emitter reads, and every name in it has been
437
+ // reviewed: a longest-common-prefix rule proposes, a human disposes. A union
438
+ // with no row keeps its inline spelling, which the coverage gate in
439
+ // `packages/types/test/constant-union-alias-coverage.test.ts` turns into a
440
+ // failure naming the row to review.
441
+ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
442
+ {
443
+ home: "buffer",
444
+ name: "ValueType",
445
+ members: [
446
+ "buffer.VALUE_TYPE_FLOAT32",
447
+ "buffer.VALUE_TYPE_INT16",
448
+ "buffer.VALUE_TYPE_INT32",
449
+ "buffer.VALUE_TYPE_INT64",
450
+ "buffer.VALUE_TYPE_INT8",
451
+ "buffer.VALUE_TYPE_UINT16",
452
+ "buffer.VALUE_TYPE_UINT32",
453
+ "buffer.VALUE_TYPE_UINT64",
454
+ "buffer.VALUE_TYPE_UINT8",
455
+ ],
456
+ },
457
+ {
458
+ home: "collectionfactory",
459
+ name: "Status",
460
+ members: [
461
+ "collectionfactory.STATUS_UNLOADED",
462
+ "collectionfactory.STATUS_LOADING",
463
+ "collectionfactory.STATUS_LOADED",
464
+ ],
465
+ },
466
+ {
467
+ home: "factory",
468
+ name: "Status",
469
+ members: ["factory.STATUS_UNLOADED", "factory.STATUS_LOADING", "factory.STATUS_LOADED"],
470
+ },
471
+ {
472
+ home: "go",
473
+ name: "Easing",
474
+ members: [
475
+ "go.EASING_INBACK",
476
+ "go.EASING_INBOUNCE",
477
+ "go.EASING_INCIRC",
478
+ "go.EASING_INCUBIC",
479
+ "go.EASING_INELASTIC",
480
+ "go.EASING_INEXPO",
481
+ "go.EASING_INOUTBACK",
482
+ "go.EASING_INOUTBOUNCE",
483
+ "go.EASING_INOUTCIRC",
484
+ "go.EASING_INOUTCUBIC",
485
+ "go.EASING_INOUTELASTIC",
486
+ "go.EASING_INOUTEXPO",
487
+ "go.EASING_INOUTQUAD",
488
+ "go.EASING_INOUTQUART",
489
+ "go.EASING_INOUTQUINT",
490
+ "go.EASING_INOUTSINE",
491
+ "go.EASING_INQUAD",
492
+ "go.EASING_INQUART",
493
+ "go.EASING_INQUINT",
494
+ "go.EASING_INSINE",
495
+ "go.EASING_LINEAR",
496
+ "go.EASING_OUTBACK",
497
+ "go.EASING_OUTBOUNCE",
498
+ "go.EASING_OUTCIRC",
499
+ "go.EASING_OUTCUBIC",
500
+ "go.EASING_OUTELASTIC",
501
+ "go.EASING_OUTEXPO",
502
+ "go.EASING_OUTINBACK",
503
+ "go.EASING_OUTINBOUNCE",
504
+ "go.EASING_OUTINCIRC",
505
+ "go.EASING_OUTINCUBIC",
506
+ "go.EASING_OUTINELASTIC",
507
+ "go.EASING_OUTINEXPO",
508
+ "go.EASING_OUTINQUAD",
509
+ "go.EASING_OUTINQUART",
510
+ "go.EASING_OUTINQUINT",
511
+ "go.EASING_OUTINSINE",
512
+ "go.EASING_OUTQUAD",
513
+ "go.EASING_OUTQUART",
514
+ "go.EASING_OUTQUINT",
515
+ "go.EASING_OUTSINE",
516
+ ],
517
+ },
518
+ {
519
+ home: "go",
520
+ name: "Playback",
521
+ members: [
522
+ "go.PLAYBACK_ONCE_FORWARD",
523
+ "go.PLAYBACK_ONCE_BACKWARD",
524
+ "go.PLAYBACK_ONCE_PINGPONG",
525
+ "go.PLAYBACK_LOOP_FORWARD",
526
+ "go.PLAYBACK_LOOP_BACKWARD",
527
+ "go.PLAYBACK_LOOP_PINGPONG",
528
+ ],
529
+ },
530
+ {
531
+ home: "graphics",
532
+ name: "BufferType",
533
+ members: [
534
+ "graphics.BUFFER_TYPE_COLOR0_BIT",
535
+ "graphics.BUFFER_TYPE_COLOR1_BIT",
536
+ "graphics.BUFFER_TYPE_COLOR2_BIT",
537
+ "graphics.BUFFER_TYPE_COLOR3_BIT",
538
+ "graphics.BUFFER_TYPE_DEPTH_BIT",
539
+ "graphics.BUFFER_TYPE_STENCIL_BIT",
540
+ ],
541
+ },
542
+ {
543
+ home: "graphics",
544
+ name: "State",
545
+ members: [
546
+ "graphics.STATE_DEPTH_TEST",
547
+ "graphics.STATE_STENCIL_TEST",
548
+ "graphics.STATE_BLEND",
549
+ "graphics.STATE_ALPHA_TEST",
550
+ "graphics.STATE_CULL_FACE",
551
+ "graphics.STATE_POLYGON_OFFSET_FILL",
552
+ ],
553
+ },
554
+ {
555
+ home: "gui",
556
+ name: "AdjustMode",
557
+ members: ["gui.ADJUST_FIT", "gui.ADJUST_ZOOM", "gui.ADJUST_STRETCH"],
558
+ },
559
+ {
560
+ home: "gui",
561
+ name: "BlendMode",
562
+ members: [
563
+ "gui.BLEND_ALPHA",
564
+ "gui.BLEND_ADD",
565
+ "gui.BLEND_ADD_ALPHA",
566
+ "gui.BLEND_MULT",
567
+ "gui.BLEND_SCREEN",
568
+ ],
569
+ },
570
+ {
571
+ home: "gui",
572
+ name: "ClippingMode",
573
+ members: ["gui.CLIPPING_MODE_NONE", "gui.CLIPPING_MODE_STENCIL"],
574
+ },
575
+ {
576
+ home: "gui",
577
+ name: "Easing",
578
+ members: [
579
+ "gui.EASING_INBACK",
580
+ "gui.EASING_INBOUNCE",
581
+ "gui.EASING_INCIRC",
582
+ "gui.EASING_INCUBIC",
583
+ "gui.EASING_INELASTIC",
584
+ "gui.EASING_INEXPO",
585
+ "gui.EASING_INOUTBACK",
586
+ "gui.EASING_INOUTBOUNCE",
587
+ "gui.EASING_INOUTCIRC",
588
+ "gui.EASING_INOUTCUBIC",
589
+ "gui.EASING_INOUTELASTIC",
590
+ "gui.EASING_INOUTEXPO",
591
+ "gui.EASING_INOUTQUAD",
592
+ "gui.EASING_INOUTQUART",
593
+ "gui.EASING_INOUTQUINT",
594
+ "gui.EASING_INOUTSINE",
595
+ "gui.EASING_INQUAD",
596
+ "gui.EASING_INQUART",
597
+ "gui.EASING_INQUINT",
598
+ "gui.EASING_INSINE",
599
+ "gui.EASING_LINEAR",
600
+ "gui.EASING_OUTBACK",
601
+ "gui.EASING_OUTBOUNCE",
602
+ "gui.EASING_OUTCIRC",
603
+ "gui.EASING_OUTCUBIC",
604
+ "gui.EASING_OUTELASTIC",
605
+ "gui.EASING_OUTEXPO",
606
+ "gui.EASING_OUTINBACK",
607
+ "gui.EASING_OUTINBOUNCE",
608
+ "gui.EASING_OUTINCIRC",
609
+ "gui.EASING_OUTINCUBIC",
610
+ "gui.EASING_OUTINELASTIC",
611
+ "gui.EASING_OUTINEXPO",
612
+ "gui.EASING_OUTINQUAD",
613
+ "gui.EASING_OUTINQUART",
614
+ "gui.EASING_OUTINQUINT",
615
+ "gui.EASING_OUTINSINE",
616
+ "gui.EASING_OUTQUAD",
617
+ "gui.EASING_OUTQUART",
618
+ "gui.EASING_OUTQUINT",
619
+ "gui.EASING_OUTSINE",
620
+ ],
621
+ },
622
+ {
623
+ home: "gui",
624
+ name: "KeyboardType",
625
+ members: [
626
+ "gui.KEYBOARD_TYPE_DEFAULT",
627
+ "gui.KEYBOARD_TYPE_EMAIL",
628
+ "gui.KEYBOARD_TYPE_NUMBER_PAD",
629
+ "gui.KEYBOARD_TYPE_PASSWORD",
630
+ ],
631
+ },
632
+ {
633
+ home: "gui",
634
+ name: "NodeType",
635
+ members: [
636
+ "gui.TYPE_BOX",
637
+ "gui.TYPE_TEXT",
638
+ "gui.TYPE_PIE",
639
+ "gui.TYPE_PARTICLEFX",
640
+ "gui.TYPE_CUSTOM",
641
+ ],
642
+ },
643
+ {
644
+ home: "gui",
645
+ name: "PieBounds",
646
+ members: ["gui.PIEBOUNDS_RECTANGLE", "gui.PIEBOUNDS_ELLIPSE"],
647
+ },
648
+ {
649
+ home: "gui",
650
+ name: "Pivot",
651
+ members: [
652
+ "gui.PIVOT_CENTER",
653
+ "gui.PIVOT_N",
654
+ "gui.PIVOT_NE",
655
+ "gui.PIVOT_E",
656
+ "gui.PIVOT_SE",
657
+ "gui.PIVOT_S",
658
+ "gui.PIVOT_SW",
659
+ "gui.PIVOT_W",
660
+ "gui.PIVOT_NW",
661
+ ],
662
+ },
663
+ {
664
+ home: "gui",
665
+ name: "Playback",
666
+ members: [
667
+ "gui.PLAYBACK_ONCE_FORWARD",
668
+ "gui.PLAYBACK_ONCE_BACKWARD",
669
+ "gui.PLAYBACK_ONCE_PINGPONG",
670
+ "gui.PLAYBACK_LOOP_FORWARD",
671
+ "gui.PLAYBACK_LOOP_BACKWARD",
672
+ "gui.PLAYBACK_LOOP_PINGPONG",
673
+ ],
674
+ },
675
+ {
676
+ home: "gui",
677
+ name: "Property",
678
+ members: [
679
+ "gui.PROP_POSITION",
680
+ "gui.PROP_ROTATION",
681
+ "gui.PROP_EULER",
682
+ "gui.PROP_SCALE",
683
+ "gui.PROP_COLOR",
684
+ "gui.PROP_OUTLINE",
685
+ "gui.PROP_SHADOW",
686
+ "gui.PROP_SIZE",
687
+ "gui.PROP_FILL_ANGLE",
688
+ "gui.PROP_INNER_RADIUS",
689
+ "gui.PROP_LEADING",
690
+ "gui.PROP_TRACKING",
691
+ "gui.PROP_SLICE9",
692
+ ],
693
+ },
694
+ {
695
+ home: "gui",
696
+ name: "SafeAreaMode",
697
+ members: [
698
+ "gui.SAFE_AREA_NONE",
699
+ "gui.SAFE_AREA_LONG",
700
+ "gui.SAFE_AREA_SHORT",
701
+ "gui.SAFE_AREA_BOTH",
702
+ ],
703
+ },
704
+ {
705
+ home: "gui",
706
+ name: "SizeMode",
707
+ members: ["gui.SIZE_MODE_MANUAL", "gui.SIZE_MODE_AUTO"],
708
+ },
709
+ {
710
+ home: "gui",
711
+ name: "XAnchor",
712
+ members: ["gui.ANCHOR_NONE", "gui.ANCHOR_LEFT", "gui.ANCHOR_RIGHT"],
713
+ },
714
+ {
715
+ home: "gui",
716
+ name: "YAnchor",
717
+ members: ["gui.ANCHOR_NONE", "gui.ANCHOR_TOP", "gui.ANCHOR_BOTTOM"],
718
+ },
719
+ {
720
+ home: "profiler",
721
+ name: "Mode",
722
+ members: [
723
+ "profiler.MODE_RUN",
724
+ "profiler.MODE_PAUSE",
725
+ "profiler.MODE_SHOW_PEAK_FRAME",
726
+ "profiler.MODE_RECORD",
727
+ ],
728
+ },
729
+ {
730
+ home: "profiler",
731
+ name: "ViewMode",
732
+ members: ["profiler.VIEW_MODE_FULL", "profiler.VIEW_MODE_MINIMIZED"],
733
+ },
734
+ {
735
+ home: "render",
736
+ name: "ClearBufferKey",
737
+ members: [
738
+ "graphics.BUFFER_TYPE_COLOR0_BIT",
739
+ "graphics.BUFFER_TYPE_DEPTH_BIT",
740
+ "graphics.BUFFER_TYPE_STENCIL_BIT",
741
+ ],
742
+ },
743
+ {
744
+ home: "sys",
745
+ name: "NetworkConnectivity",
746
+ members: [
747
+ "sys.NETWORK_DISCONNECTED",
748
+ "sys.NETWORK_CONNECTED_CELLULAR",
749
+ "sys.NETWORK_CONNECTED",
750
+ ],
751
+ },
752
+ {
753
+ home: "window",
754
+ name: "DimModeState",
755
+ members: ["window.DIMMING_UNKNOWN", "window.DIMMING_ON", "window.DIMMING_OFF"],
756
+ },
757
+ {
758
+ home: "window",
759
+ name: "DimModeStateSettable",
760
+ members: ["window.DIMMING_ON", "window.DIMMING_OFF"],
761
+ },
762
+ ];
763
+
764
+ const CONSTANT_UNION_ALIAS_BY_MEMBERS: ReadonlyMap<string, ConstantUnionAlias> = new Map(
765
+ CONSTANT_UNION_ALIASES.map((row) => [constantUnionAliasKey(row.members), row] as const),
766
+ );
767
+
768
+ function constantUnionAliasKey(members: readonly string[]): string {
769
+ return [...new Set(members)].sort().join("\u0000");
770
+ }
771
+
772
+ // The alias naming exactly these constants, or undefined when no row does. A
773
+ // single constant is never aliased: it already has a name.
774
+ export function constantUnionAlias(fqns: readonly string[]): ConstantUnionAlias | undefined {
775
+ if (fqns.length < 2) return undefined;
776
+ return CONSTANT_UNION_ALIAS_BY_MEMBERS.get(constantUnionAliasKey(fqns));
777
+ }
778
+
779
+ // The alias declarations a module owns, in name order. A row is declared in its
780
+ // home whether or not that module references it, so a cross-namespace row is
781
+ // reachable from the module whose signatures spell it.
782
+ export function constantUnionAliasDeclarations(namespace: string): string[] {
783
+ return CONSTANT_UNION_ALIASES.filter((row) => row.home === namespace)
784
+ .sort((a, b) => a.name.localeCompare(b.name))
785
+ .map(
786
+ (row) => `type ${row.name} = ${row.members.map((member) => `typeof ${member}`).join(" | ")};`,
787
+ );
788
+ }
789
+
418
790
  const DOC_CONSTANT_TOKEN =
419
791
  /(?<![A-Za-z0-9_.])([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*\.[A-Z][A-Z0-9_]*)(\*)?/g;
420
792
 
@@ -906,8 +1278,8 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
906
1278
  // names as keys to their clear value: vector4 for the color buffer, number for
907
1279
  // depth/stencil. Both sides are union tokens the mapping string branch splits,
908
1280
  // so the key emits the three branded constants and the value `number | Vector4`.
909
- // The key is branded rather than `number`, which is the narrowing users migrate
910
- // to via the authored `render.ClearBufferKey` alias.
1281
+ // The key set is a CONSTANT_UNION_ALIASES row, so the signature carries the
1282
+ // `render.ClearBufferKey` alias rather than the three branded arms.
911
1283
  [
912
1284
  "render.clear:param:buffers",
913
1285
  {
@@ -1427,6 +1799,35 @@ export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
1427
1799
  ["material.get_vertex_attributes:return:table:value", ATTRIBUTE_VALUE_TS],
1428
1800
  ]);
1429
1801
 
1802
+ // A field the engine accepts on an option bag whose `<dl>` upstream otherwise
1803
+ // documents in full. Every other authored table here retypes, marks optional or
1804
+ // suppresses a slot the parser already recovered; this one supplies a member
1805
+ // upstream omits, leaving the documented fields parser-authoritative so an
1806
+ // upstream edit to any of them lands without a hand-maintained restatement.
1807
+ //
1808
+ // `evidence` pins where upstream itself shows the field. applyFieldAdditions
1809
+ // throws once the parse recovers it, so upstream documenting the field reds
1810
+ // here and the entry is deleted rather than re-pinned.
1811
+ //
1812
+ // Keyed `<element>:<kind>:<slot>`, the TABLE_SLOT_CURATIONS shape.
1813
+ export interface TableFieldAddition {
1814
+ readonly field: TableField;
1815
+ readonly evidence: readonly string[];
1816
+ }
1817
+
1818
+ export const TABLE_SLOT_FIELD_ADDITIONS: ReadonlyMap<string, TableFieldAddition> = new Map([
1819
+ [
1820
+ "resource.create_texture:param:table",
1821
+ {
1822
+ field: { name: "page_count", types: ["number"], optional: true },
1823
+ evidence: [
1824
+ "the array-texture example on this element creates with `page_count = 5`",
1825
+ "resource.get_texture_info documents `page_count` as the texture array's page count",
1826
+ ],
1827
+ },
1828
+ ],
1829
+ ]);
1830
+
1430
1831
  // A return-slot field upstream documents as platform-gated or nil-valued while
1431
1832
  // declaring it like any other. Lua removes a key assigned `nil`, so "might be
1432
1833
  // nil if not available" and "only available on iOS and Android" describe the
@@ -1516,6 +1917,26 @@ export function applyFieldOptionalityCorrections(
1516
1917
  return corrected;
1517
1918
  }
1518
1919
 
1920
+ // Append the member TABLE_SLOT_FIELD_ADDITIONS supplies for this slot, after
1921
+ // every field the parser recovered. Throws on an addition whose field the parse
1922
+ // already produced: upstream has documented it, and the entry is stale.
1923
+ // Returns a new array; never mutates the parser's result.
1924
+ export function applyFieldAdditions(
1925
+ elementName: string,
1926
+ slotKind: "param" | "return" | undefined,
1927
+ slotName: string | undefined,
1928
+ fields: readonly TableField[],
1929
+ ): TableField[] {
1930
+ if (slotKind === undefined || slotName === undefined) return [...fields];
1931
+ const key = tableSlotKey(elementName, slotKind, slotName);
1932
+ const addition = TABLE_SLOT_FIELD_ADDITIONS.get(key);
1933
+ if (addition === undefined) return [...fields];
1934
+ if (fields.some((field) => field.name === addition.field.name)) {
1935
+ throw new Error(`field addition names a recovered field: ${key}:${addition.field.name}`);
1936
+ }
1937
+ return [...fields, addition.field];
1938
+ }
1939
+
1519
1940
  // Pin a parser-recovered field's TS type from TABLE_FIELD_TYPE_OVERRIDES,
1520
1941
  // leaving every other field parser-authoritative. A field the override does not
1521
1942
  // name is returned unchanged; a field it names gains a `tsType` (emitted
@@ -1948,6 +2369,9 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
1948
2369
  if (handleGroups.has(t.name)) continue;
1949
2370
  lines.push(`${INDENT}${decl}type ${t.name} = Opaque<"${t.name}">;`);
1950
2371
  }
2372
+ for (const aliasDecl of constantUnionAliasDeclarations(module.namespace)) {
2373
+ lines.push(`${INDENT}${decl}${aliasDecl}`);
2374
+ }
1951
2375
  const handleIndent = `${INDENT}${INDENT}`;
1952
2376
  for (const receiver of handleReceivers) {
1953
2377
  const group = handleGroups.get(receiver) ?? [];
@@ -2235,6 +2659,19 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
2235
2659
  });
2236
2660
  }
2237
2661
 
2662
+ // The module's constant-union aliases travel as typedefs too: a signature that
2663
+ // names one carries no member list of its own, so the docs read the members
2664
+ // from here rather than from the emitter's table.
2665
+ for (const row of CONSTANT_UNION_ALIASES.filter((r) => r.home === module.namespace)) {
2666
+ out.push({
2667
+ identity: { namespace: module.namespace, kind: "TYPEDEF", name: row.name, signature: "" },
2668
+ tsSignature: `type ${row.name} = ${row.members
2669
+ .map((member) => `typeof ${member}`)
2670
+ .join(" | ")};`,
2671
+ slotTypes: NO_SLOTS,
2672
+ });
2673
+ }
2674
+
2238
2675
  return out;
2239
2676
  }
2240
2677
 
@@ -2538,11 +2975,35 @@ function functionDocLines(
2538
2975
  }));
2539
2976
  const onlyReturn = fn.returnValues.length === 1 ? fn.returnValues[0] : undefined;
2540
2977
  const lua = htmlToCodeText(fn.examples ?? "");
2541
- // A hand-authored TS translation pinned to this exact Lua flips the fence to
2542
- // ```ts; any hash mismatch (or absent translation) keeps the Lua fallback.
2978
+ // A blob carrying several examples documents them as several, but only when
2979
+ // every segment has an authored body: a partial resolve would drop the rest.
2980
+ // Otherwise the whole-blob ladder stands — a hand-authored TS translation
2981
+ // pinned to this exact Lua flips the fence to ```ts; any hash mismatch (or
2982
+ // absent translation) keeps the Lua fallback.
2983
+ const segments = splitExampleSources(fn.examples ?? "");
2984
+ const perSegment =
2985
+ segments.length > 1
2986
+ ? lookupExampleTranslations(
2987
+ translations,
2988
+ fn.name,
2989
+ segments.map((segment) => hashExampleSource(segment.code)),
2990
+ )
2991
+ : null;
2543
2992
  const ts = lua === "" ? null : lookupTranslation(translations, fn.name, hashExampleSource(lua));
2544
- const exampleParts: Pick<DocCommentParts, "example" | "exampleLang"> =
2545
- ts !== null ? { example: ts, exampleLang: "ts" } : lua !== "" ? { example: lua } : {};
2993
+ const exampleParts: Pick<DocCommentParts, "examples"> =
2994
+ perSegment !== null
2995
+ ? {
2996
+ examples: perSegment.map((text, index) => ({
2997
+ text,
2998
+ lang: "ts" as const,
2999
+ ...(segments[index]?.prose ? { prose: segments[index]?.prose } : {}),
3000
+ })),
3001
+ }
3002
+ : ts !== null
3003
+ ? { examples: [{ text: ts, lang: "ts" }] }
3004
+ : lua !== ""
3005
+ ? { examples: [{ text: lua, lang: "lua" }] }
3006
+ : {};
2546
3007
  const parts: DocCommentParts = {
2547
3008
  summary: htmlToDocText(summaryFor(fn.brief, fn.description)),
2548
3009
  params,
@@ -2911,7 +3372,8 @@ function mapSlotUnion(
2911
3372
  } else {
2912
3373
  const parsed = parseTableFields(doc, resolver, slotName);
2913
3374
  if (parsed !== null) {
2914
- const nested = applyNestedFieldCurations(elementName, slotKind, slotName, parsed);
3375
+ const added = applyFieldAdditions(elementName, slotKind, slotName, parsed);
3376
+ const nested = applyNestedFieldCurations(elementName, slotKind, slotName, added);
2915
3377
  const typed = applyFieldTypeOverrides(elementName, slotKind, slotName, nested);
2916
3378
  const fields = applyFieldOptionalityCorrections(elementName, slotKind, slotName, typed);
2917
3379
  const object = inlineTableType(fields, mapType, optionalFields);
@@ -2940,7 +3402,7 @@ function mapSlotUnion(
2940
3402
  // every other union member, so collapse to exactly `unknown` rather than emit
2941
3403
  // a redundant `T | unknown` or `unknown | undefined`.
2942
3404
  if (mapped.includes("unknown")) return "unknown";
2943
- return mapped.join(" | ");
3405
+ return joinUnionArms(mapped);
2944
3406
  }
2945
3407
 
2946
3408
  function tableSlotKey(elementName: string, slotKind: "param" | "return", slotName: string): string {
@@ -3030,7 +3492,36 @@ function unionFromTokens(tokens: readonly string[], mapType: (t: string) => stri
3030
3492
  seen.add(ts);
3031
3493
  mapped.push(ts);
3032
3494
  }
3033
- return mapped.join(" | ");
3495
+ return joinUnionArms(mapped);
3496
+ }
3497
+
3498
+ const BRAND_ARM = /^number & \{ readonly __brand: "([^"]+)" \}$/;
3499
+
3500
+ // Joins already-mapped arms, collapsing the branded ones to their alias when
3501
+ // they are exactly a `CONSTANT_UNION_ALIASES` member set. The alias takes the
3502
+ // position of the first branded arm, so a mixed union keeps its reading order
3503
+ // (`gui.Easing | Vector`). Arms that are not constants are untouched, and a set
3504
+ // with no row keeps every arm inline.
3505
+ function joinUnionArms(mapped: readonly string[]): string {
3506
+ const branded: string[] = [];
3507
+ for (const arm of mapped) {
3508
+ const match = BRAND_ARM.exec(arm);
3509
+ if (match !== null) branded.push(match[1] as string);
3510
+ }
3511
+ const alias = constantUnionAlias(branded);
3512
+ if (alias === undefined) return mapped.join(" | ");
3513
+ const out: string[] = [];
3514
+ let placed = false;
3515
+ for (const arm of mapped) {
3516
+ if (BRAND_ARM.test(arm)) {
3517
+ if (placed) continue;
3518
+ out.push(`${alias.home}.${alias.name}`);
3519
+ placed = true;
3520
+ continue;
3521
+ }
3522
+ out.push(arm);
3523
+ }
3524
+ return out.join(" | ");
3034
3525
  }
3035
3526
 
3036
3527
  export function isKnownDefoldTypeToken(token: string): boolean {
@@ -51,3 +51,29 @@ export function lookupTranslation(
51
51
  const match = entries.find((entry) => entry.sourceHash === sourceHash);
52
52
  return match ? match.ts : null;
53
53
  }
54
+
55
+ /**
56
+ * The stored bodies for a blob's segment hashes, in the order given, or `null`
57
+ * the moment one of them does not resolve.
58
+ *
59
+ * All-or-nothing on purpose: an element whose blob carries several examples is
60
+ * emitted as several `@example` blocks only when every one of them has an
61
+ * authored body. A partial resolve would document some of the element's
62
+ * examples and silently drop the rest, so the caller falls back to the
63
+ * whole-blob body instead. An empty hash list is a miss for the same reason —
64
+ * it would otherwise report success while documenting nothing.
65
+ */
66
+ export function lookupExampleTranslations(
67
+ store: TranslationStore,
68
+ fqn: string,
69
+ sourceHashes: readonly string[],
70
+ ): string[] | null {
71
+ if (sourceHashes.length === 0) return null;
72
+ const bodies: string[] = [];
73
+ for (const sourceHash of sourceHashes) {
74
+ const ts = lookupTranslation(store, fqn, sourceHash);
75
+ if (ts === null) return null;
76
+ bodies.push(ts);
77
+ }
78
+ return bodies;
79
+ }
package/src/index.ts CHANGED
@@ -35,10 +35,14 @@ export {
35
35
  } from "./core-types";
36
36
  export {
37
37
  type DocCommentParts,
38
+ type ExampleSegment,
39
+ type ExampleSegmentation,
38
40
  examplesHtmlToMarkdown,
39
41
  htmlToCodeText,
40
42
  htmlToDocText,
41
43
  renderDocComment,
44
+ segmentExampleRegions,
45
+ splitExampleSources,
42
46
  } from "./doc-comment";
43
47
  export {
44
48
  defineEditorScript,
@@ -56,6 +60,7 @@ export {
56
60
  } from "./emit-dts";
57
61
  export {
58
62
  hashExampleSource,
63
+ lookupExampleTranslations,
59
64
  lookupTranslation,
60
65
  type Translation,
61
66
  type TranslationStore,