@defold-typescript/types 0.50.0 → 0.51.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.
- package/api-signatures.json +286 -232
- package/examples/translations.json +15 -15
- package/fixtures/messages_doc.json +20 -39
- package/generated/builtin-messages.d.ts +16 -14
- package/generated/collectionfactory.d.ts +1 -1
- package/generated/collectionproxy.d.ts +2 -2
- package/generated/factory.d.ts +4 -2
- package/generated/graphics.d.ts +4 -0
- package/generated/gui.d.ts +2 -2
- package/generated/http.d.ts +3 -1
- package/generated/image.d.ts +1 -1
- package/generated/kinds/gui-script.d.ts +1 -0
- package/generated/kinds/render-script.d.ts +1 -0
- package/generated/kinds/script.d.ts +1 -0
- package/generated/model.d.ts +4 -5
- package/generated/physics.d.ts +3 -3
- package/generated/render.d.ts +41 -13
- package/generated/sys.d.ts +8 -4
- package/generated/tilemap.d.ts +4 -4
- package/generated/versions/defold-1.12.4/collectionfactory.d.ts +1 -1
- package/generated/versions/defold-1.12.4/collectionproxy.d.ts +1 -1
- package/generated/versions/defold-1.12.4/factory.d.ts +4 -2
- package/generated/versions/defold-1.12.4/gui.d.ts +2 -2
- package/generated/versions/defold-1.12.4/http.d.ts +3 -1
- package/generated/versions/defold-1.12.4/image.d.ts +1 -1
- package/generated/versions/defold-1.12.4/index.d.ts +1 -0
- package/generated/versions/defold-1.12.4/model.d.ts +4 -5
- package/generated/versions/defold-1.12.4/physics.d.ts +2 -2
- package/generated/versions/defold-1.12.4/render.d.ts +41 -13
- package/generated/versions/defold-1.12.4/sys.d.ts +1 -1
- package/generated/versions/defold-1.12.4/tilemap.d.ts +4 -4
- package/generated/versions/defold-1.13.0/collectionfactory.d.ts +1 -1
- package/generated/versions/defold-1.13.0/collectionproxy.d.ts +1 -1
- package/generated/versions/defold-1.13.0/factory.d.ts +4 -2
- package/generated/versions/defold-1.13.0/gui.d.ts +2 -2
- package/generated/versions/defold-1.13.0/http.d.ts +3 -1
- package/generated/versions/defold-1.13.0/image.d.ts +1 -1
- package/generated/versions/defold-1.13.0/index.d.ts +1 -0
- package/generated/versions/defold-1.13.0/model.d.ts +4 -5
- package/generated/versions/defold-1.13.0/physics.d.ts +2 -2
- package/generated/versions/defold-1.13.0/render.d.ts +41 -13
- package/generated/versions/defold-1.13.0/sys.d.ts +1 -1
- package/generated/versions/defold-1.13.0/tilemap.d.ts +4 -4
- package/generated/versions/defold-1.13.1/collectionfactory.d.ts +1 -1
- package/generated/versions/defold-1.13.1/collectionproxy.d.ts +1 -1
- package/generated/versions/defold-1.13.1/factory.d.ts +4 -2
- package/generated/versions/defold-1.13.1/gui.d.ts +2 -2
- package/generated/versions/defold-1.13.1/http.d.ts +3 -1
- package/generated/versions/defold-1.13.1/image.d.ts +1 -1
- package/generated/versions/defold-1.13.1/index.d.ts +1 -0
- package/generated/versions/defold-1.13.1/model.d.ts +4 -5
- package/generated/versions/defold-1.13.1/physics.d.ts +2 -2
- package/generated/versions/defold-1.13.1/render.d.ts +41 -13
- package/generated/versions/defold-1.13.1/sys.d.ts +1 -1
- package/generated/versions/defold-1.13.1/tilemap.d.ts +4 -4
- package/generated/versions/defold-1.13.2/collectionfactory.d.ts +1 -1
- package/generated/versions/defold-1.13.2/collectionproxy.d.ts +2 -2
- package/generated/versions/defold-1.13.2/factory.d.ts +4 -2
- package/generated/versions/defold-1.13.2/gui.d.ts +2 -2
- package/generated/versions/defold-1.13.2/http.d.ts +3 -1
- package/generated/versions/defold-1.13.2/image.d.ts +1 -1
- package/generated/versions/defold-1.13.2/index.d.ts +1 -0
- package/generated/versions/defold-1.13.2/model.d.ts +4 -5
- package/generated/versions/defold-1.13.2/physics.d.ts +3 -3
- package/generated/versions/defold-1.13.2/render.d.ts +41 -13
- package/generated/versions/defold-1.13.2/sys.d.ts +8 -4
- package/generated/versions/defold-1.13.2/tilemap.d.ts +4 -4
- package/generated/versions/defold-1.13.2/window.d.ts +1 -1
- package/generated/window.d.ts +1 -1
- package/identity-vocabulary.json +46 -0
- package/index.d.ts +1 -0
- package/package.json +2 -1
- package/scripts/generate-api-availability.ts +5 -1
- package/scripts/generate-api-signatures.ts +16 -11
- package/scripts/materialize-version.ts +7 -1
- package/scripts/regen.ts +42 -25
- package/src/component-properties.d.ts +137 -6
- package/src/core-types.ts +46 -11
- package/src/emit-dts.ts +555 -84
- package/src/emit-messages.ts +289 -0
- package/src/engine-globals.d.ts +3 -1
- package/src/go-overloads.d.ts +13 -3
- package/src/lifecycle.ts +16 -16
- package/src/luals-type-expr.ts +6 -1
- package/src/physics-event-guard.d.ts +39 -0
- package/src/render-overloads.d.ts +2 -2
- package/src/return-correction-provenance.ts +36 -6
- package/src/window-event-guard.d.ts +5 -3
package/src/emit-dts.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
splitExampleSources,
|
|
22
22
|
summaryFor,
|
|
23
23
|
} from "./doc-comment";
|
|
24
|
+
import { applyMessageOverlays, parseMessagesDoc } from "./emit-messages";
|
|
24
25
|
import type { TranslationStore } from "./example-store";
|
|
25
26
|
import { lookupExampleSegments, translateProseFences } from "./example-store";
|
|
26
27
|
import {
|
|
@@ -30,7 +31,12 @@ import {
|
|
|
30
31
|
indexBaseNoteResolver,
|
|
31
32
|
OVERRIDE_RETURN_SLOT,
|
|
32
33
|
} from "./index-slot-classifications";
|
|
33
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
isLualsFunctionToken,
|
|
36
|
+
type LualsPosition,
|
|
37
|
+
mapLualsExpression,
|
|
38
|
+
needsArrayParens,
|
|
39
|
+
} from "./luals-type-expr";
|
|
34
40
|
import { classifyUrlParameter, type UrlParameterTable } from "./url-parameters";
|
|
35
41
|
|
|
36
42
|
export { summaryFor };
|
|
@@ -75,6 +81,14 @@ export interface EmitOptions {
|
|
|
75
81
|
// `UNDOCUMENTED_CONSTANTS` back-fill. Absent for a surface with no vendored
|
|
76
82
|
// bindings (a ref-doc target), where every entry the ref-doc omits is emitted.
|
|
77
83
|
registeredConstants?: ReadonlySet<string>;
|
|
84
|
+
// The `RETURN_TYPE_CORRECTIONS` and `STRUCT_MEMBER_ADDITIONS` keys whose binding
|
|
85
|
+
// pin holds in the target's vendored engine source. A binding-pinned entry
|
|
86
|
+
// applies only when its key is here, so a surface with no vendored bindings
|
|
87
|
+
// (absent) applies none of them.
|
|
88
|
+
bindingEvidence?: ReadonlySet<string>;
|
|
89
|
+
// The functions a target's skip rules removed from the module, read only to
|
|
90
|
+
// type an `OVERLOAD_SLOT_ALIASES` slot.
|
|
91
|
+
withheldFunctions?: readonly ApiFunction[];
|
|
78
92
|
}
|
|
79
93
|
|
|
80
94
|
export const TS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
@@ -362,8 +376,22 @@ export const RETURN_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
362
376
|
// destructured into separate arguments; a boxed `LuaMultiReturn` is rejected
|
|
363
377
|
// because TSTL passes it as one table, which is always truthy. `reason` quotes
|
|
364
378
|
// the slot prose the provenance test searches for, as for the `nil` entries.
|
|
365
|
-
|
|
366
|
-
|
|
379
|
+
//
|
|
380
|
+
// An entry with a `binding` rests on the engine source instead of the prose:
|
|
381
|
+
// it applies only on a target whose vendored binding shows the pin (the
|
|
382
|
+
// `EmitOptions.bindingEvidence` keys), and `reason` names that binding in words.
|
|
383
|
+
// A nil push proves nil-ability and nothing about the base type, so such an
|
|
384
|
+
// entry usually carries `nullable` rather than `ts`.
|
|
385
|
+
export interface BindingPin {
|
|
386
|
+
// Relative to the target's vendored `engine/` tree.
|
|
387
|
+
readonly file: string;
|
|
388
|
+
readonly cFunction: string;
|
|
389
|
+
// Token snippets the function body must contain and must lack; at least one.
|
|
390
|
+
readonly contains?: string;
|
|
391
|
+
readonly lacks?: string;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
interface ReturnCorrectionBase {
|
|
367
395
|
readonly upstream: readonly string[];
|
|
368
396
|
// A later release's spelling of the same defect; see `correction-pins.ts`.
|
|
369
397
|
readonly retypedUpstream?: readonly (readonly string[])[];
|
|
@@ -371,10 +399,33 @@ export interface ReturnTypeCorrection {
|
|
|
371
399
|
// The named value of a multi-return the correction rewrites; `ts` is then
|
|
372
400
|
// that value's type. Absent, the correction rewrites a single return.
|
|
373
401
|
readonly slot?: string;
|
|
402
|
+
readonly binding?: BindingPin;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// `ts` replaces the emitted type. `nullable` keeps the type each target maps
|
|
406
|
+
// from its own tokens and only adds or drops the `nil`, so a target whose
|
|
407
|
+
// spelling differs keeps its own base.
|
|
408
|
+
export type ReturnTypeCorrection = ReturnCorrectionBase &
|
|
409
|
+
(
|
|
410
|
+
| { readonly ts: string; readonly nullable?: undefined }
|
|
411
|
+
| { readonly nullable: boolean; readonly ts?: undefined }
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
// The early nil return both tilemap readers take for an unknown layer, and
|
|
415
|
+
// `TileMap_Get` for a cell out of range too.
|
|
416
|
+
function tilemapEarlyNil(cFunction: string): BindingPin {
|
|
417
|
+
return {
|
|
418
|
+
file: "gamesys/src/gamesys/scripts/script_tilemap.cpp",
|
|
419
|
+
cFunction,
|
|
420
|
+
contains: "lua_pushnil(L); assert(top + 1 == lua_gettop(L)); return 1;",
|
|
421
|
+
};
|
|
374
422
|
}
|
|
375
423
|
|
|
376
424
|
// Keyed by FQN, mirroring PROPERTY_TYPE_CORRECTIONS.
|
|
377
|
-
export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection> = new Map
|
|
425
|
+
export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection> = new Map<
|
|
426
|
+
string,
|
|
427
|
+
ReturnTypeCorrection
|
|
428
|
+
>([
|
|
378
429
|
[
|
|
379
430
|
"b2d.get_body",
|
|
380
431
|
{
|
|
@@ -439,6 +490,147 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
439
490
|
reason: "the customized try function.",
|
|
440
491
|
},
|
|
441
492
|
],
|
|
493
|
+
[
|
|
494
|
+
"factory.create",
|
|
495
|
+
{
|
|
496
|
+
nullable: true,
|
|
497
|
+
upstream: ["hash"],
|
|
498
|
+
reason: "script_factory.cpp:FactoryComp_Create pushes nil when the spawn fails",
|
|
499
|
+
binding: {
|
|
500
|
+
file: "gamesys/src/gamesys/scripts/script_factory.cpp",
|
|
501
|
+
cFunction: "FactoryComp_Create",
|
|
502
|
+
contains: "dmScript::PushHash(L, id); } else { lua_pushnil(L); }",
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
// 1.13.2 retypes each tilemap return to a precise spelling of the same value
|
|
507
|
+
// and still omits the nil.
|
|
508
|
+
...(
|
|
509
|
+
[
|
|
510
|
+
["tilemap.get_tile", ["number"], ["integer"]],
|
|
511
|
+
[
|
|
512
|
+
"tilemap.get_tile_info",
|
|
513
|
+
["table"],
|
|
514
|
+
["{ index:integer, h_flip:boolean, v_flip:boolean, rotate_90:boolean }"],
|
|
515
|
+
],
|
|
516
|
+
] as const
|
|
517
|
+
).map(
|
|
518
|
+
([key, upstream, retyped]) =>
|
|
519
|
+
[
|
|
520
|
+
key,
|
|
521
|
+
{
|
|
522
|
+
nullable: true,
|
|
523
|
+
upstream,
|
|
524
|
+
retypedUpstream: [retyped],
|
|
525
|
+
reason:
|
|
526
|
+
"script_tilemap.cpp:TileMap_Get pushes nil for an unknown layer or a cell out of range",
|
|
527
|
+
binding: tilemapEarlyNil("TileMap_Get"),
|
|
528
|
+
},
|
|
529
|
+
] as const,
|
|
530
|
+
),
|
|
531
|
+
[
|
|
532
|
+
"tilemap.get_tiles",
|
|
533
|
+
{
|
|
534
|
+
nullable: true,
|
|
535
|
+
upstream: ["table"],
|
|
536
|
+
retypedUpstream: [["table<integer, table<integer, integer>>"]],
|
|
537
|
+
reason: "script_tilemap.cpp:TileMap_GetTiles pushes nil for an unknown layer",
|
|
538
|
+
binding: tilemapEarlyNil("TileMap_GetTiles"),
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
[
|
|
542
|
+
"collectionproxy.set_collection",
|
|
543
|
+
{
|
|
544
|
+
nullable: true,
|
|
545
|
+
upstream: ["number"],
|
|
546
|
+
retypedUpstream: [["collectionproxy.RESULT"]],
|
|
547
|
+
reason:
|
|
548
|
+
"script_collectionproxy.cpp:CollectionProxy_SetCollection pushes true, nil when the change succeeds",
|
|
549
|
+
slot: "code",
|
|
550
|
+
binding: {
|
|
551
|
+
file: "gamesys/src/gamesys/scripts/script_collectionproxy.cpp",
|
|
552
|
+
cFunction: "CollectionProxy_SetCollection",
|
|
553
|
+
contains: "case SET_COLLECTION_PATH_RESULT_OK: lua_pushboolean(L, 1); lua_pushnil(L);",
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
[
|
|
558
|
+
"image.get_astc_header",
|
|
559
|
+
{
|
|
560
|
+
nullable: false,
|
|
561
|
+
upstream: ["table", "nil"],
|
|
562
|
+
retypedUpstream: [["image.astc_header", "nil"]],
|
|
563
|
+
reason: "script_image.cpp:Image_GetAstcHeader raises on invalid data and never pushes nil",
|
|
564
|
+
binding: {
|
|
565
|
+
file: "gamesys/src/gamesys/scripts/script_image.cpp",
|
|
566
|
+
cFunction: "Image_GetAstcHeader",
|
|
567
|
+
contains: 'return luaL_error(L, "Data is not a valid .astc file");',
|
|
568
|
+
lacks: "lua_pushnil(L);",
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
],
|
|
572
|
+
// `PushRayCastResponse` has two definitions; `Physics_RayCast` calls the one
|
|
573
|
+
// that takes the world, which sets every payload field but `request_id`.
|
|
574
|
+
[
|
|
575
|
+
"physics.raycast",
|
|
576
|
+
{
|
|
577
|
+
ts: 'Omit<BuiltinMessages["ray_cast_response"], "request_id">[] | Omit<BuiltinMessages["ray_cast_response"], "request_id"> | undefined',
|
|
578
|
+
upstream: ["table", "nil"],
|
|
579
|
+
retypedUpstream: [
|
|
580
|
+
["message.physics.ray_cast_response[]", "message.physics.ray_cast_response", "nil"],
|
|
581
|
+
],
|
|
582
|
+
reason:
|
|
583
|
+
"script_physics.cpp:Physics_RayCast pushes each hit through PushRayCastResponse(L, world, hit), which sets no request_id",
|
|
584
|
+
binding: {
|
|
585
|
+
file: "gamesys/src/gamesys/scripts/script_physics.cpp",
|
|
586
|
+
cFunction: "Physics_RayCast",
|
|
587
|
+
contains: "PushRayCastResponse(L, world, hits[i]);",
|
|
588
|
+
lacks: '"request_id"',
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
],
|
|
592
|
+
...(
|
|
593
|
+
[
|
|
594
|
+
[
|
|
595
|
+
"collectionfactory.create",
|
|
596
|
+
"Hash",
|
|
597
|
+
["table<hash, hash>"],
|
|
598
|
+
"gamesys/src/gamesys/scripts/script_collection_factory.cpp",
|
|
599
|
+
"CollectionFactoryComp_Create",
|
|
600
|
+
],
|
|
601
|
+
[
|
|
602
|
+
"gui.get_tree",
|
|
603
|
+
'Opaque<"node">',
|
|
604
|
+
["table<hash, node>"],
|
|
605
|
+
"gui/src/gui_script.cpp",
|
|
606
|
+
"LuaGetTree",
|
|
607
|
+
],
|
|
608
|
+
[
|
|
609
|
+
"gui.clone_tree",
|
|
610
|
+
'Opaque<"node">',
|
|
611
|
+
["table<hash, node>"],
|
|
612
|
+
"gui/src/gui_script.cpp",
|
|
613
|
+
"LuaCloneTree",
|
|
614
|
+
],
|
|
615
|
+
] as const
|
|
616
|
+
).map(
|
|
617
|
+
([key, value, retyped, file, cFunction]) =>
|
|
618
|
+
[
|
|
619
|
+
key,
|
|
620
|
+
{
|
|
621
|
+
ts: `LuaTable<Hash, ${value}> & { get: LuaTableGetMethod<string, ${value}> }`,
|
|
622
|
+
upstream: ["table"],
|
|
623
|
+
retypedUpstream: [retyped],
|
|
624
|
+
reason: `${file.slice(file.lastIndexOf("/") + 1)}:${cFunction} sets HashTableIndex as the id table's __index, which hashes a string key`,
|
|
625
|
+
binding: {
|
|
626
|
+
file,
|
|
627
|
+
cFunction,
|
|
628
|
+
contains:
|
|
629
|
+
'lua_pushcfunction(L, HashTableIndex); lua_setfield(L, -2, "__index"); lua_setmetatable(L, -2);',
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
] as const,
|
|
633
|
+
),
|
|
442
634
|
]);
|
|
443
635
|
|
|
444
636
|
// A parameter whose declared `types` disagree with what the same element's
|
|
@@ -1553,6 +1745,13 @@ function unionAliasDeclaration(
|
|
|
1553
1745
|
return `type ${name} = ${members.map(arm).join(" | ")};`;
|
|
1554
1746
|
}
|
|
1555
1747
|
|
|
1748
|
+
// A slot of a function the authored-overload lane declares, published under
|
|
1749
|
+
// one alias on every target so the version-independent overload file can
|
|
1750
|
+
// name the type each target's own reference gives that slot.
|
|
1751
|
+
export const OVERLOAD_SLOT_ALIASES: ReadonlyMap<string, string> = new Map([
|
|
1752
|
+
["render.render_target:param:parameters", "RenderTargetParams"],
|
|
1753
|
+
]);
|
|
1754
|
+
|
|
1556
1755
|
const DOC_CONSTANT_TOKEN =
|
|
1557
1756
|
/(?<![A-Za-z0-9_.])([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*\.[A-Z][A-Z0-9_]*)(\*)?/g;
|
|
1558
1757
|
|
|
@@ -2267,10 +2466,13 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2267
2466
|
// buildTableDocResolver resolves only FUNCTION elements so the cross-ref
|
|
2268
2467
|
// cannot fire (the message payload lives in messages_doc.json, not the
|
|
2269
2468
|
// FUNCTION slot pool). The shape is curated directly from the message
|
|
2270
|
-
// catalog
|
|
2469
|
+
// catalog: the ray_cast_response payload fields but `request_id`, which
|
|
2470
|
+
// `Physics_RayCast` never sets. The doc declares a
|
|
2271
2471
|
// `nil` branch ("If missed it returns <code>nil</code>"), so the existing
|
|
2272
2472
|
// nil-aware emit already projects the curated array onto the
|
|
2273
2473
|
// `array-object | nil` return type — no curation change needed there.
|
|
2474
|
+
// Where its binding pin holds, the `physics.raycast` RETURN_TYPE_CORRECTIONS
|
|
2475
|
+
// entry replaces the whole return.
|
|
2274
2476
|
[
|
|
2275
2477
|
"physics.raycast:return:result",
|
|
2276
2478
|
{
|
|
@@ -2281,7 +2483,6 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2281
2483
|
{ name: "normal", types: ["vector3"] },
|
|
2282
2484
|
{ name: "id", types: ["hash"] },
|
|
2283
2485
|
{ name: "group", types: ["hash"] },
|
|
2284
|
-
{ name: "request_id", types: ["number"] },
|
|
2285
2486
|
],
|
|
2286
2487
|
},
|
|
2287
2488
|
],
|
|
@@ -2704,41 +2905,103 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2704
2905
|
]);
|
|
2705
2906
|
|
|
2706
2907
|
// Slot-keyed (`element:param:name`, mirroring TABLE_SLOT_CURATIONS) replacements
|
|
2707
|
-
// for a callback parameter's
|
|
2708
|
-
//
|
|
2709
|
-
//
|
|
2710
|
-
//
|
|
2711
|
-
// `
|
|
2712
|
-
//
|
|
2713
|
-
//
|
|
2714
|
-
// (receive, narrow) split.
|
|
2908
|
+
// for a callback parameter's signature. Each entry carries two forms, chosen by
|
|
2909
|
+
// the token the reference gives the slot. `untyped` answers a bare
|
|
2910
|
+
// `function(a, b, c)` token, where the generic `recoverCallbackSignature`
|
|
2911
|
+
// `unknown`-everywhere form leaves real engine type information on the table.
|
|
2912
|
+
// `typed` answers a `fun(a:T, ...)` token, where the reference names a type for
|
|
2913
|
+
// each parameter; `null` there emits the reference's own signature. Each form
|
|
2914
|
+
// is the full emitted function type. Honest only.
|
|
2715
2915
|
//
|
|
2716
|
-
//
|
|
2916
|
+
// `window.set_listener` discriminates `event` by the union of the
|
|
2917
|
+
// `WINDOW_EVENT_*` constants the surface's own `window` namespace declares, on
|
|
2918
|
+
// both forms: the union is derived from the surface, so each version accepts
|
|
2919
|
+
// exactly the constants it declares. Its untyped `data` is a bare record (only
|
|
2920
|
+
// resize carries fields) and its typed `data` is the reference's
|
|
2921
|
+
// `window.event_data`; `isWindowEvent` narrows either to the event's payload,
|
|
2922
|
+
// mirroring the `msg.post` (send, typed) / `isMessage` (receive, narrow) split.
|
|
2923
|
+
//
|
|
2924
|
+
// The other untyped forms type what the slot's own prose lists and nothing more.
|
|
2717
2925
|
// `http.request`'s response fields are the `<ul>` under "The response data.
|
|
2718
2926
|
// Contains the fields:"; each is conditional ("if not saved on disc", "only if
|
|
2719
2927
|
// option report_progress is true") except `status`, and `headers` is the same
|
|
2720
|
-
// string map the request sends.
|
|
2721
|
-
//
|
|
2928
|
+
// string map the request sends. The prose omits two fields the engine sets:
|
|
2929
|
+
// `url`, which every final response carries (script_http_util.h), and
|
|
2930
|
+
// `bytes_sent`, which a report_progress event carries beside `bytes_received`,
|
|
2931
|
+
// `bytes_total` and `url` (http_ddf.proto `HttpRequestProgress`). That event has
|
|
2932
|
+
// no `status`; typing it as its own shape needs a per-target authored overload,
|
|
2933
|
+
// so `status` stays required and the slot's `note` says how to tell the two
|
|
2934
|
+
// apart. Its typed form is the same signature: the typed reference names
|
|
2935
|
+
// `http.response`, which the runtime surface withholds.
|
|
2936
|
+
// `model.play_anim`'s completion callback receives self, the message id and the
|
|
2937
|
+
// message only: script_model.cpp calls it with `PCall(L, 3, 0)`, so both forms
|
|
2938
|
+
// drop the `sender` every reference lists and type the message as the typed
|
|
2939
|
+
// reference's `model_animation_done`.
|
|
2940
|
+
// `sprite.play_flipbook`'s message is the "Information about the completion"
|
|
2941
|
+
// list. Its typed form is the same signature: the typed reference names the
|
|
2942
|
+
// built-in `animation_done` message, which has this shape, and the inline form
|
|
2943
|
+
// keeps the fields visible in the signature. `collectionproxy.load` documents
|
|
2722
2944
|
// only that the callback receives `proxy_loading`, `proxy_ready` or
|
|
2723
|
-
// `proxy_error` messages and lists no fields
|
|
2724
|
-
// `window.set_listener`'s `data` is
|
|
2725
|
-
|
|
2945
|
+
// `proxy_error` messages and lists no fields on the untyped reference, so its
|
|
2946
|
+
// untyped message is the bare record `window.set_listener`'s untyped `data` is;
|
|
2947
|
+
// the typed reference names `collectionproxy.load_data`, so its typed form
|
|
2948
|
+
// defers to the reference.
|
|
2949
|
+
export interface CallbackSignatureCuration {
|
|
2950
|
+
/** Emitted for a bare `function(...)` token. */
|
|
2951
|
+
readonly untyped: string;
|
|
2952
|
+
/** Emitted for a typed `fun(...)` token; `null` emits the reference's own signature. */
|
|
2953
|
+
readonly typed: string | null;
|
|
2954
|
+
/** Appended to the parameter's doc, for what the signature cannot express. */
|
|
2955
|
+
readonly note?: string;
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
const onBothForms = (signature: string, note?: string): CallbackSignatureCuration => ({
|
|
2959
|
+
untyped: signature,
|
|
2960
|
+
typed: signature,
|
|
2961
|
+
...(note === undefined ? {} : { note }),
|
|
2962
|
+
});
|
|
2963
|
+
|
|
2964
|
+
export const CALLBACK_SIGNATURE_CURATIONS: ReadonlyMap<string, CallbackSignatureCuration> = new Map<
|
|
2965
|
+
string,
|
|
2966
|
+
CallbackSignatureCuration
|
|
2967
|
+
>([
|
|
2726
2968
|
[
|
|
2727
2969
|
"window.set_listener:param:callback",
|
|
2728
|
-
|
|
2729
|
-
|
|
2970
|
+
{
|
|
2971
|
+
untyped:
|
|
2972
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: emitted verbatim as a TS template literal type
|
|
2973
|
+
"(self: unknown, event: (typeof window)[Extract<keyof typeof window, `WINDOW_EVENT_${string}`>], data: Record<string | number, unknown>) => void",
|
|
2974
|
+
typed:
|
|
2975
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: emitted verbatim as a TS template literal type
|
|
2976
|
+
"(self: unknown, event: (typeof window)[Extract<keyof typeof window, `WINDOW_EVENT_${string}`>], data: window.event_data) => void",
|
|
2977
|
+
},
|
|
2730
2978
|
],
|
|
2731
2979
|
[
|
|
2732
2980
|
"http.request:param:callback",
|
|
2733
|
-
|
|
2981
|
+
onBothForms(
|
|
2982
|
+
"(self: unknown, id: Hash, response: { status: number; url: string; response?: string; headers?: LuaMap<string, string>; path?: string; error?: string; bytes_sent?: number; bytes_received?: number; bytes_total?: number; range_start?: number; range_end?: number; document_size?: number }) => void",
|
|
2983
|
+
"With `report_progress` set, the callback also receives progress events carrying `bytes_sent`, `bytes_received`, `bytes_total` and `url` but no `status`; `response.bytes_received !== undefined` marks a progress event, since a final response never carries it.",
|
|
2984
|
+
),
|
|
2985
|
+
],
|
|
2986
|
+
[
|
|
2987
|
+
"model.play_anim:param:complete_function",
|
|
2988
|
+
onBothForms(
|
|
2989
|
+
'(self: unknown, message_id: Hash, message: BuiltinMessages["model_animation_done"]) => void',
|
|
2990
|
+
),
|
|
2734
2991
|
],
|
|
2735
2992
|
[
|
|
2736
2993
|
"sprite.play_flipbook:param:complete_function",
|
|
2737
|
-
|
|
2994
|
+
onBothForms(
|
|
2995
|
+
"(self: unknown, message_id: Hash, message: { current_tile: number; id: Hash }, sender: Url) => void",
|
|
2996
|
+
),
|
|
2738
2997
|
],
|
|
2739
2998
|
[
|
|
2740
2999
|
"collectionproxy.load:param:callback",
|
|
2741
|
-
|
|
3000
|
+
{
|
|
3001
|
+
untyped:
|
|
3002
|
+
"(self: unknown, message_id: Hash, message: Record<string | number, unknown>, sender: Url) => void",
|
|
3003
|
+
typed: null,
|
|
3004
|
+
},
|
|
2742
3005
|
],
|
|
2743
3006
|
]);
|
|
2744
3007
|
|
|
@@ -2899,32 +3162,13 @@ export interface ReturnFieldOptionalityCorrection {
|
|
|
2899
3162
|
readonly evidence: string;
|
|
2900
3163
|
}
|
|
2901
3164
|
|
|
2902
|
-
//
|
|
2903
|
-
//
|
|
2904
|
-
//
|
|
3165
|
+
// A platform gate alone is not evidence: `sys.get_sys_info` gates four fields
|
|
3166
|
+
// on a platform, yet `Sys_GetSysInfo` pushes each from a zero-filled char array,
|
|
3167
|
+
// so off-platform they are empty strings, never nil.
|
|
2905
3168
|
export const RETURN_FIELD_OPTIONALITY_CORRECTIONS: ReadonlyMap<
|
|
2906
3169
|
string,
|
|
2907
3170
|
ReturnFieldOptionalityCorrection
|
|
2908
3171
|
> = new Map([
|
|
2909
|
-
[
|
|
2910
|
-
"sys.get_sys_info:return:sys_info:device_model",
|
|
2911
|
-
{ upstream: ["string"], evidence: "[ios][android] Only available on iOS and Android." },
|
|
2912
|
-
],
|
|
2913
|
-
[
|
|
2914
|
-
"sys.get_sys_info:return:sys_info:manufacturer",
|
|
2915
|
-
{ upstream: ["string"], evidence: "[ios][android] Only available on iOS and Android." },
|
|
2916
|
-
],
|
|
2917
|
-
[
|
|
2918
|
-
"sys.get_sys_info:return:sys_info:device_ident",
|
|
2919
|
-
{
|
|
2920
|
-
upstream: ["string"],
|
|
2921
|
-
evidence: '[ios] "identifierForVendor" on iOS. [android] "android_id" on Android.',
|
|
2922
|
-
},
|
|
2923
|
-
],
|
|
2924
|
-
[
|
|
2925
|
-
"sys.get_sys_info:return:sys_info:user_agent",
|
|
2926
|
-
{ upstream: ["string"], evidence: "[html5] The HTTP user agent" },
|
|
2927
|
-
],
|
|
2928
3172
|
[
|
|
2929
3173
|
"sys.get_ifaddrs:return:ifaddrs:address",
|
|
2930
3174
|
{ upstream: ["string"], evidence: "might be nil if not available." },
|
|
@@ -2975,8 +3219,10 @@ export function applyFieldOptionalityCorrections(
|
|
|
2975
3219
|
// `<element>:param:<slot>:<field>`, the TABLE_FIELD_TYPE_OVERRIDES shape, with
|
|
2976
3220
|
// `<field>.<member>` or `<field>[].<member>` one level down. A `STRUCT` member
|
|
2977
3221
|
// is keyed `<struct>:member:<field>`, for a target whose slot names that struct
|
|
2978
|
-
// (`field-correction-coverage.ts` pairs the two)
|
|
2979
|
-
//
|
|
3222
|
+
// (`field-correction-coverage.ts` pairs the two), or for a returned struct whose
|
|
3223
|
+
// binding always sets a member upstream marks optional. The value is the
|
|
3224
|
+
// evidence: the C++ binding, `<file>:<cFunction>`, and how it reads or sets the
|
|
3225
|
+
// field.
|
|
2980
3226
|
const CHECK_SHAPE_DEF = "CheckShapeDef reads the shape table with luaL_checkinteger(type)";
|
|
2981
3227
|
const V2_FILTER_DATA =
|
|
2982
3228
|
"v2/script_box2d_fixture_v2.cpp:CheckFilterData reads each with luaL_checkinteger";
|
|
@@ -3000,6 +3246,49 @@ export const OPTIONAL_MEMBER_CORRECTIONS: ReadonlyMap<string, string> = new Map(
|
|
|
3000
3246
|
],
|
|
3001
3247
|
]);
|
|
3002
3248
|
|
|
3249
|
+
// A `STRUCT` member the engine binding sets while the target's reference omits
|
|
3250
|
+
// it, keyed `<struct>:member:<field>`. It applies only where its binding pin
|
|
3251
|
+
// holds (the `EmitOptions.bindingEvidence` keys), and a struct that already
|
|
3252
|
+
// declares the member throws, so no entry outlives upstream's fix.
|
|
3253
|
+
export interface StructMemberAddition {
|
|
3254
|
+
readonly member: Omit<ApiStructMember, "name">;
|
|
3255
|
+
readonly binding: BindingPin;
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
export const STRUCT_MEMBER_ADDITIONS: ReadonlyMap<string, StructMemberAddition> = new Map([
|
|
3259
|
+
[
|
|
3260
|
+
memberCorrectionKey("graphics.adapter_limits", "max_storage_buffers_per_stage"),
|
|
3261
|
+
{
|
|
3262
|
+
member: {
|
|
3263
|
+
type: "integer",
|
|
3264
|
+
isOptional: false,
|
|
3265
|
+
doc: "Maximum number of storage buffers per shader stage.",
|
|
3266
|
+
},
|
|
3267
|
+
binding: {
|
|
3268
|
+
file: "script/src/script_graphics.cpp",
|
|
3269
|
+
cFunction: "Graphics_GetAdapterInfo",
|
|
3270
|
+
contains: 'lua_setfield(L, -2, "max_storage_buffers_per_stage");',
|
|
3271
|
+
},
|
|
3272
|
+
},
|
|
3273
|
+
],
|
|
3274
|
+
[
|
|
3275
|
+
memberCorrectionKey("sys.interface_info", "family"),
|
|
3276
|
+
{
|
|
3277
|
+
member: {
|
|
3278
|
+
type: '"ipv4"|"ipv6"',
|
|
3279
|
+
isOptional: true,
|
|
3280
|
+
doc: "Address family, absent for an interface that is neither IPv4 nor IPv6.",
|
|
3281
|
+
},
|
|
3282
|
+
binding: {
|
|
3283
|
+
file: "script/src/script_sys.cpp",
|
|
3284
|
+
cFunction: "Sys_GetIfaddrs",
|
|
3285
|
+
contains:
|
|
3286
|
+
'lua_pushliteral(L, "ipv6"); } else { lua_pushnil(L); } lua_setfield(L, -2, "family");',
|
|
3287
|
+
},
|
|
3288
|
+
},
|
|
3289
|
+
],
|
|
3290
|
+
]);
|
|
3291
|
+
|
|
3003
3292
|
export const REQUIRED_FIELD_CORRECTIONS: ReadonlyMap<string, string> = new Map<string, string>([
|
|
3004
3293
|
...(["mass", "center", "inertia"] as const).map(
|
|
3005
3294
|
(field) =>
|
|
@@ -3093,6 +3382,13 @@ export const REQUIRED_FIELD_CORRECTIONS: ReadonlyMap<string, string> = new Map<s
|
|
|
3093
3382
|
"resource.sound_data_options:member:data",
|
|
3094
3383
|
"script_resource.cpp:CreateSoundData reads the data with CheckBufferOrString",
|
|
3095
3384
|
],
|
|
3385
|
+
...(["device_model", "manufacturer", "device_ident", "user_agent"] as const).map(
|
|
3386
|
+
(field) =>
|
|
3387
|
+
[
|
|
3388
|
+
`sys.sys_info:member:${field}`,
|
|
3389
|
+
'script_sys.cpp:Sys_GetSysInfo pushes it with lua_pushstring from a zero-filled char array (user_agent falls back to "")',
|
|
3390
|
+
] as const,
|
|
3391
|
+
),
|
|
3096
3392
|
]);
|
|
3097
3393
|
|
|
3098
3394
|
// Mark the fields REQUIRED_FIELD_CORRECTIONS names for this slot required,
|
|
@@ -3611,7 +3907,10 @@ function typedefAlias(
|
|
|
3611
3907
|
const brand = `Opaque<"${typedef.name}">`;
|
|
3612
3908
|
const aliasOf = typedef.aliasOf ?? [];
|
|
3613
3909
|
if (aliasOf.length > 0 && !isHandleAlias(aliasOf)) {
|
|
3614
|
-
|
|
3910
|
+
const armType: DefoldTypeMapper = DISCRIMINATED_MESSAGE_UNIONS.has(typedef.name)
|
|
3911
|
+
? (token, position) => discriminatedMessageArm(token) ?? mapType(token, position)
|
|
3912
|
+
: mapType;
|
|
3913
|
+
return `type ${local} = ${unionFromTokens(aliasOf, armType)};`;
|
|
3615
3914
|
}
|
|
3616
3915
|
const mapped = rawMapType(typedef.name);
|
|
3617
3916
|
if (mapped.startsWith(brand)) return `type ${local} = ${mapped};`;
|
|
@@ -3642,6 +3941,7 @@ function collectTypeDeclarations(
|
|
|
3642
3941
|
skip: ReadonlySet<string>,
|
|
3643
3942
|
rawMapType: (token: string) => string,
|
|
3644
3943
|
mapType: DefoldTypeMapper,
|
|
3944
|
+
bindingEvidence: ReadonlySet<string>,
|
|
3645
3945
|
): TypeDeclaration[] {
|
|
3646
3946
|
const prefix = `${module.namespace}.`;
|
|
3647
3947
|
const out: TypeDeclaration[] = [];
|
|
@@ -3665,7 +3965,7 @@ function collectTypeDeclarations(
|
|
|
3665
3965
|
for (const struct of module.structs ?? []) {
|
|
3666
3966
|
const local = localOf(struct.name);
|
|
3667
3967
|
if (local === null || skip.has(local)) continue;
|
|
3668
|
-
out.push(structDeclaration(struct, local, mapType, module.structs ?? []));
|
|
3968
|
+
out.push(structDeclaration(struct, local, mapType, module.structs ?? [], bindingEvidence));
|
|
3669
3969
|
}
|
|
3670
3970
|
return out;
|
|
3671
3971
|
}
|
|
@@ -3718,10 +4018,22 @@ function structDeclaration(
|
|
|
3718
4018
|
path: string,
|
|
3719
4019
|
mapType: DefoldTypeMapper,
|
|
3720
4020
|
structs: readonly ApiStruct[],
|
|
4021
|
+
bindingEvidence: ReadonlySet<string>,
|
|
3721
4022
|
): TypeDeclaration {
|
|
3722
4023
|
const name = path.slice(path.lastIndexOf(".") + 1);
|
|
3723
4024
|
const memberPrefix = memberCorrectionKey(struct.name, "");
|
|
3724
4025
|
const declared = new Set(struct.members.map((member) => member.name));
|
|
4026
|
+
const redundant = [...STRUCT_MEMBER_ADDITIONS.keys()].filter(
|
|
4027
|
+
(key) => key.startsWith(memberPrefix) && declared.has(key.slice(memberPrefix.length)),
|
|
4028
|
+
);
|
|
4029
|
+
if (redundant.length > 0) {
|
|
4030
|
+
throw new Error(
|
|
4031
|
+
`struct member addition names a declared member: ${redundant.sort().join(", ")}`,
|
|
4032
|
+
);
|
|
4033
|
+
}
|
|
4034
|
+
const added = [...STRUCT_MEMBER_ADDITIONS]
|
|
4035
|
+
.filter(([key]) => key.startsWith(memberPrefix) && bindingEvidence.has(key))
|
|
4036
|
+
.map(([key, addition]) => ({ ...addition.member, name: key.slice(memberPrefix.length) }));
|
|
3725
4037
|
const stale = [
|
|
3726
4038
|
...REQUIRED_FIELD_CORRECTIONS.keys(),
|
|
3727
4039
|
...LIST_FIELD_CORRECTIONS.keys(),
|
|
@@ -3731,7 +4043,7 @@ function structDeclaration(
|
|
|
3731
4043
|
if (stale.length > 0) {
|
|
3732
4044
|
throw new Error(`member-keyed field correction names no member: ${stale.sort().join(", ")}`);
|
|
3733
4045
|
}
|
|
3734
|
-
const members = struct.members.map((member) => {
|
|
4046
|
+
const members = [...struct.members, ...added].map((member) => {
|
|
3735
4047
|
const key = memberCorrectionKey(struct.name, member.name);
|
|
3736
4048
|
const list = LIST_FIELD_CORRECTIONS.get(key);
|
|
3737
4049
|
const retyped = TABLE_FIELD_TYPE_OVERRIDES.get(key);
|
|
@@ -3935,6 +4247,7 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
3935
4247
|
const knownConstantFqns = options?.knownConstantFqns;
|
|
3936
4248
|
const translations = options?.translations ?? {};
|
|
3937
4249
|
const urlParameters = options?.urlParameters ?? [];
|
|
4250
|
+
const bindingEvidence = options?.bindingEvidence ?? new Set<string>();
|
|
3938
4251
|
const baseNotes = indexBaseNoteResolver(options?.indexBaseSource ?? "engine");
|
|
3939
4252
|
const { rawMapType, mapType } = moduleTypeMappers(module, constantsByFqn, options);
|
|
3940
4253
|
const constantTokens = constantSlotTokenResolver(
|
|
@@ -3991,7 +4304,13 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
3991
4304
|
// A typedef that also has colon methods is emitted as a method-bearing
|
|
3992
4305
|
// interface below, not an opaque brand alias.
|
|
3993
4306
|
const typeDeclarations = placeTypeDeclarations(
|
|
3994
|
-
collectTypeDeclarations(
|
|
4307
|
+
collectTypeDeclarations(
|
|
4308
|
+
module,
|
|
4309
|
+
new Set(handleGroups.keys()),
|
|
4310
|
+
rawMapType,
|
|
4311
|
+
mapType,
|
|
4312
|
+
bindingEvidence,
|
|
4313
|
+
),
|
|
3995
4314
|
nestedRoot,
|
|
3996
4315
|
);
|
|
3997
4316
|
|
|
@@ -4013,6 +4332,16 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4013
4332
|
for (const aliasDecl of constantUnionAliasDeclarations(module.namespace)) {
|
|
4014
4333
|
lines.push(`${INDENT}${decl}${aliasDecl}`);
|
|
4015
4334
|
}
|
|
4335
|
+
for (const aliasDecl of overloadSlotAliasDeclarations(
|
|
4336
|
+
[...module.functions, ...(options?.withheldFunctions ?? [])],
|
|
4337
|
+
prefix,
|
|
4338
|
+
mapType,
|
|
4339
|
+
constantTokens,
|
|
4340
|
+
urlParameters,
|
|
4341
|
+
bindingEvidence,
|
|
4342
|
+
)) {
|
|
4343
|
+
lines.push(`${INDENT}${decl}${aliasDecl}`);
|
|
4344
|
+
}
|
|
4016
4345
|
for (const alias of enumAliases.topLevel) {
|
|
4017
4346
|
lines.push(
|
|
4018
4347
|
`${INDENT}${decl}${unionAliasDeclaration(alias.name, alias.members, alias.nilable)}`,
|
|
@@ -4027,7 +4356,7 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4027
4356
|
lines.push(docLine);
|
|
4028
4357
|
}
|
|
4029
4358
|
lines.push(
|
|
4030
|
-
`${handleIndent}${emitMethod(fn, mapType, resolver, constantTokens, urlParameters)}`,
|
|
4359
|
+
`${handleIndent}${emitMethod(fn, mapType, resolver, constantTokens, urlParameters, bindingEvidence)}`,
|
|
4031
4360
|
);
|
|
4032
4361
|
}
|
|
4033
4362
|
lines.push(`${INDENT}}`);
|
|
@@ -4058,7 +4387,15 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4058
4387
|
const emitName = aliasName(fn.name, aliases);
|
|
4059
4388
|
for (const docLine of functionDocLines(fn.original, translations, baseNotes))
|
|
4060
4389
|
lines.push(docLine);
|
|
4061
|
-
const line = emitFunction(
|
|
4390
|
+
const line = emitFunction(
|
|
4391
|
+
fn,
|
|
4392
|
+
emitName,
|
|
4393
|
+
mapType,
|
|
4394
|
+
resolver,
|
|
4395
|
+
constantTokens,
|
|
4396
|
+
urlParameters,
|
|
4397
|
+
bindingEvidence,
|
|
4398
|
+
);
|
|
4062
4399
|
lines.push(`${INDENT}${reserved ? "" : decl}${line}`);
|
|
4063
4400
|
const alternative = firstSlotAlternative(fn, module.namespace);
|
|
4064
4401
|
if (alternative !== null) {
|
|
@@ -4074,6 +4411,7 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4074
4411
|
resolver,
|
|
4075
4412
|
constantTokens,
|
|
4076
4413
|
urlParameters,
|
|
4414
|
+
bindingEvidence,
|
|
4077
4415
|
);
|
|
4078
4416
|
lines.push(`${INDENT}${reserved ? "" : decl}${overload}`);
|
|
4079
4417
|
}
|
|
@@ -4148,7 +4486,7 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4148
4486
|
lines.push(docLine);
|
|
4149
4487
|
}
|
|
4150
4488
|
lines.push(
|
|
4151
|
-
`${bodyIndent}${reserved ? "" : segmentDecl}${emitFunction(fn, emitName, mapType, resolver, constantTokens, urlParameters)}`,
|
|
4489
|
+
`${bodyIndent}${reserved ? "" : segmentDecl}${emitFunction(fn, emitName, mapType, resolver, constantTokens, urlParameters, bindingEvidence)}`,
|
|
4152
4490
|
);
|
|
4153
4491
|
}
|
|
4154
4492
|
aliasReservedSegments(group.children.keys(), segmentAliases);
|
|
@@ -4179,6 +4517,55 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4179
4517
|
return `${lines.join("\n")}\n`;
|
|
4180
4518
|
}
|
|
4181
4519
|
|
|
4520
|
+
// One `type <alias> = <slot type>;` per `OVERLOAD_SLOT_ALIASES` slot these
|
|
4521
|
+
// functions carry, in alias order. The type is the one `emitFunction` reports
|
|
4522
|
+
// for the slot, so it reads as the function's own signature would. The
|
|
4523
|
+
// resolver spans these functions alone, leaving the module's own untouched.
|
|
4524
|
+
function overloadSlotAliasDeclarations(
|
|
4525
|
+
functions: readonly ApiFunction[],
|
|
4526
|
+
prefix: string,
|
|
4527
|
+
mapType: DefoldTypeMapper,
|
|
4528
|
+
constantTokens: ConstantSlotTokens,
|
|
4529
|
+
urlParameters: UrlParameterTable,
|
|
4530
|
+
bindingEvidence: ReadonlySet<string>,
|
|
4531
|
+
): string[] {
|
|
4532
|
+
const resolver = buildTableDocResolver(
|
|
4533
|
+
functions.map((fn) => ({ name: fn.name, slots: [...fn.parameters, ...fn.returnValues] })),
|
|
4534
|
+
);
|
|
4535
|
+
const types = new Map<string, string>();
|
|
4536
|
+
for (const fn of functions) {
|
|
4537
|
+
for (const [position, p] of fn.parameters.entries()) {
|
|
4538
|
+
const alias = OVERLOAD_SLOT_ALIASES.get(tableSlotKey(fn.name, "param", p.name));
|
|
4539
|
+
if (alias === undefined) continue;
|
|
4540
|
+
const prepared = prepareFunction(fn, prefix);
|
|
4541
|
+
if (prepared === null) continue;
|
|
4542
|
+
const slots: Record<string, SlotType> = {};
|
|
4543
|
+
emitFunction(
|
|
4544
|
+
prepared,
|
|
4545
|
+
prepared.name,
|
|
4546
|
+
mapType,
|
|
4547
|
+
resolver,
|
|
4548
|
+
constantTokens,
|
|
4549
|
+
urlParameters,
|
|
4550
|
+
bindingEvidence,
|
|
4551
|
+
slots,
|
|
4552
|
+
);
|
|
4553
|
+
const slot = slots[slotKey("param", position, p.name)];
|
|
4554
|
+
if (slot === undefined) {
|
|
4555
|
+
throw new Error(`${fn.name} reports no type for its aliased slot "${p.name}"`);
|
|
4556
|
+
}
|
|
4557
|
+
const seen = types.get(alias);
|
|
4558
|
+
if (seen !== undefined && seen !== slot.type) {
|
|
4559
|
+
throw new Error(`${alias} is typed both ${seen} and ${slot.type}`);
|
|
4560
|
+
}
|
|
4561
|
+
types.set(alias, slot.type);
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
return [...types]
|
|
4565
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
4566
|
+
.map(([alias, type]) => `type ${alias} = ${type};`);
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4182
4569
|
/**
|
|
4183
4570
|
* Every documented slot's rendered TS type and optionality, keyed
|
|
4184
4571
|
* `param|return:<position>:<raw ref-doc name>`. The position is part of the key
|
|
@@ -4232,6 +4619,7 @@ export function emitSymbolSignatures(
|
|
|
4232
4619
|
const constantsByFqn = new Map(module.constants.map((c) => [c.name, c]));
|
|
4233
4620
|
const knownConstantFqns = options?.knownConstantFqns;
|
|
4234
4621
|
const urlParameters = options?.urlParameters ?? [];
|
|
4622
|
+
const bindingEvidence = options?.bindingEvidence ?? new Set<string>();
|
|
4235
4623
|
const { rawMapType, mapType } = moduleTypeMappers(module, constantsByFqn, options);
|
|
4236
4624
|
const constantTokens = constantSlotTokenResolver(
|
|
4237
4625
|
module,
|
|
@@ -4269,6 +4657,7 @@ export function emitSymbolSignatures(
|
|
|
4269
4657
|
resolver,
|
|
4270
4658
|
constantTokens,
|
|
4271
4659
|
urlParameters,
|
|
4660
|
+
bindingEvidence,
|
|
4272
4661
|
slotTypes,
|
|
4273
4662
|
);
|
|
4274
4663
|
const alternative = firstSlotAlternative(prepared, module.namespace);
|
|
@@ -4282,6 +4671,7 @@ export function emitSymbolSignatures(
|
|
|
4282
4671
|
resolver,
|
|
4283
4672
|
constantTokens,
|
|
4284
4673
|
urlParameters,
|
|
4674
|
+
bindingEvidence,
|
|
4285
4675
|
)}`;
|
|
4286
4676
|
out.push({ identity: fnIdentity(fn), tsSignature, slotTypes });
|
|
4287
4677
|
}
|
|
@@ -4323,6 +4713,7 @@ export function emitSymbolSignatures(
|
|
|
4323
4713
|
resolver,
|
|
4324
4714
|
constantTokens,
|
|
4325
4715
|
urlParameters,
|
|
4716
|
+
bindingEvidence,
|
|
4326
4717
|
slotTypes,
|
|
4327
4718
|
),
|
|
4328
4719
|
slotTypes,
|
|
@@ -4341,6 +4732,7 @@ export function emitSymbolSignatures(
|
|
|
4341
4732
|
resolver,
|
|
4342
4733
|
constantTokens,
|
|
4343
4734
|
urlParameters,
|
|
4735
|
+
bindingEvidence,
|
|
4344
4736
|
slotTypes,
|
|
4345
4737
|
),
|
|
4346
4738
|
slotTypes,
|
|
@@ -4389,6 +4781,7 @@ export function emitSymbolSignatures(
|
|
|
4389
4781
|
new Set(handleGroups.keys()),
|
|
4390
4782
|
rawMapType,
|
|
4391
4783
|
mapType,
|
|
4784
|
+
bindingEvidence,
|
|
4392
4785
|
)) {
|
|
4393
4786
|
out.push({
|
|
4394
4787
|
identity: {
|
|
@@ -4846,6 +5239,7 @@ function memberSignature(
|
|
|
4846
5239
|
resolver: TableDocResolver,
|
|
4847
5240
|
constantTokens: ConstantSlotTokens,
|
|
4848
5241
|
urlParameters: UrlParameterTable,
|
|
5242
|
+
bindingEvidence: ReadonlySet<string>,
|
|
4849
5243
|
slotSink?: Record<string, SlotType>,
|
|
4850
5244
|
): string {
|
|
4851
5245
|
const original = prepared.original.parameters;
|
|
@@ -4874,6 +5268,7 @@ function memberSignature(
|
|
|
4874
5268
|
resolver,
|
|
4875
5269
|
constantTokens,
|
|
4876
5270
|
elementName,
|
|
5271
|
+
bindingEvidence,
|
|
4877
5272
|
);
|
|
4878
5273
|
const predicateToken = TYPE_PREDICATES.get(elementName);
|
|
4879
5274
|
const soleParam = original[0];
|
|
@@ -4911,9 +5306,10 @@ function emitFunction(
|
|
|
4911
5306
|
resolver: TableDocResolver,
|
|
4912
5307
|
constantTokens: ConstantSlotTokens,
|
|
4913
5308
|
urlParameters: UrlParameterTable,
|
|
5309
|
+
bindingEvidence: ReadonlySet<string>,
|
|
4914
5310
|
slotSink?: Record<string, SlotType>,
|
|
4915
5311
|
): string {
|
|
4916
|
-
return `function ${memberSignature(prepared, name, mapType, resolver, constantTokens, urlParameters, slotSink)}`;
|
|
5312
|
+
return `function ${memberSignature(prepared, name, mapType, resolver, constantTokens, urlParameters, bindingEvidence, slotSink)}`;
|
|
4917
5313
|
}
|
|
4918
5314
|
|
|
4919
5315
|
// A colon-method member of a handle interface: identical signature machinery to a
|
|
@@ -4925,6 +5321,7 @@ function emitMethod(
|
|
|
4925
5321
|
resolver: TableDocResolver,
|
|
4926
5322
|
constantTokens: ConstantSlotTokens,
|
|
4927
5323
|
urlParameters: UrlParameterTable,
|
|
5324
|
+
bindingEvidence: ReadonlySet<string>,
|
|
4928
5325
|
slotSink?: Record<string, SlotType>,
|
|
4929
5326
|
): string {
|
|
4930
5327
|
return memberSignature(
|
|
@@ -4934,6 +5331,7 @@ function emitMethod(
|
|
|
4934
5331
|
resolver,
|
|
4935
5332
|
constantTokens,
|
|
4936
5333
|
urlParameters,
|
|
5334
|
+
bindingEvidence,
|
|
4937
5335
|
slotSink,
|
|
4938
5336
|
);
|
|
4939
5337
|
}
|
|
@@ -4956,13 +5354,18 @@ function functionDocLines(
|
|
|
4956
5354
|
baseNotes: IndexBaseNoteResolver,
|
|
4957
5355
|
indent: string = INDENT,
|
|
4958
5356
|
): string[] {
|
|
4959
|
-
const params = fn.parameters.map((p, index) =>
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
5357
|
+
const params = fn.parameters.map((p, index) => {
|
|
5358
|
+
const curationNote = CALLBACK_SIGNATURE_CURATIONS.get(
|
|
5359
|
+
tableSlotKey(fn.name, "param", p.name),
|
|
5360
|
+
)?.note;
|
|
5361
|
+
return {
|
|
5362
|
+
name: emittedParamName(p, index),
|
|
5363
|
+
doc: appendNotes(translateProseFences(htmlToDocText(p.doc), fn.name, translations), [
|
|
5364
|
+
...baseNotes(fn.name, "param", p.name),
|
|
5365
|
+
...(curationNote === undefined ? [] : [curationNote]),
|
|
5366
|
+
]),
|
|
5367
|
+
};
|
|
5368
|
+
});
|
|
4966
5369
|
const onlyReturn = fn.returnValues.length === 1 ? fn.returnValues[0] : undefined;
|
|
4967
5370
|
const notedReturns =
|
|
4968
5371
|
multiReturnDoc(fn, translations, baseNotes) ?? overrideReturnDoc(fn, baseNotes);
|
|
@@ -5273,12 +5676,24 @@ function emitParameter(
|
|
|
5273
5676
|
return { text: `${name}${optional ? "?" : ""}: ${type}`, type, optional };
|
|
5274
5677
|
}
|
|
5275
5678
|
|
|
5679
|
+
// A `nullable` correction rewrites only the `nil` in the declared tokens and
|
|
5680
|
+
// leaves the rest to the ordinary mapping and its top-level `nil` projection.
|
|
5681
|
+
function correctedReturnTokens(
|
|
5682
|
+
types: readonly string[],
|
|
5683
|
+
correction: ReturnTypeCorrection | undefined,
|
|
5684
|
+
): readonly string[] {
|
|
5685
|
+
if (correction?.nullable === undefined) return types;
|
|
5686
|
+
const base = types.filter((token) => token !== "nil");
|
|
5687
|
+
return correction.nullable ? [...base, "nil"] : base;
|
|
5688
|
+
}
|
|
5689
|
+
|
|
5276
5690
|
function emitReturn(
|
|
5277
5691
|
returnValues: ApiParameter[],
|
|
5278
5692
|
mapType: DefoldTypeMapper,
|
|
5279
5693
|
resolver: TableDocResolver,
|
|
5280
5694
|
constantTokens: ConstantSlotTokens,
|
|
5281
5695
|
elementName: string,
|
|
5696
|
+
bindingEvidence: ReadonlySet<string>,
|
|
5282
5697
|
): { type: string; trailing: string; slots: EmittedSlot[] } {
|
|
5283
5698
|
// An authored whole-return override is not a per-slot render, so it reports no
|
|
5284
5699
|
// slots and leaves the consumer on its token-derived fallback.
|
|
@@ -5287,23 +5702,30 @@ function emitReturn(
|
|
|
5287
5702
|
// Kept beside the override lookup so the two stay visibly distinct: this one
|
|
5288
5703
|
// contradicts a declared token rather than filling an empty `returnvalues`,
|
|
5289
5704
|
// and it is a per-slot render, so it reports its slot.
|
|
5290
|
-
const
|
|
5705
|
+
const listed = RETURN_TYPE_CORRECTIONS.get(elementName);
|
|
5706
|
+
const correction =
|
|
5707
|
+
listed?.binding === undefined || bindingEvidence.has(elementName) ? listed : undefined;
|
|
5291
5708
|
if (returnValues.length === 0) return { type: "void", trailing: "", slots: [] };
|
|
5292
5709
|
if (returnValues.length > 1) {
|
|
5293
5710
|
// Defold multi-returns are positional and always present; each slot maps
|
|
5294
5711
|
// straight through (unknown when the doc lists no type) into a tuple that
|
|
5295
5712
|
// typescript-to-lua erases to `local a, b = fn()`.
|
|
5296
|
-
const emitted = returnValues.map((rv, index) =>
|
|
5297
|
-
|
|
5298
|
-
name: rv.name,
|
|
5299
|
-
ts:
|
|
5713
|
+
const emitted = returnValues.map((rv, index) => {
|
|
5714
|
+
const applied =
|
|
5300
5715
|
correction?.slot !== undefined &&
|
|
5301
5716
|
correction.slot === rv.name &&
|
|
5302
5717
|
pinMatches(rv.types, correctionPins(correction))
|
|
5303
|
-
? correction
|
|
5304
|
-
:
|
|
5718
|
+
? correction
|
|
5719
|
+
: undefined;
|
|
5720
|
+
const types = correctedReturnTokens(rv.types, applied);
|
|
5721
|
+
return {
|
|
5722
|
+
position: index,
|
|
5723
|
+
name: rv.name,
|
|
5724
|
+
ts:
|
|
5725
|
+
applied?.ts ??
|
|
5726
|
+
(types.length > 0
|
|
5305
5727
|
? mapSlotUnion(
|
|
5306
|
-
|
|
5728
|
+
types,
|
|
5307
5729
|
rv.doc,
|
|
5308
5730
|
mapType,
|
|
5309
5731
|
false,
|
|
@@ -5313,8 +5735,9 @@ function emitReturn(
|
|
|
5313
5735
|
"return",
|
|
5314
5736
|
rv.name,
|
|
5315
5737
|
)
|
|
5316
|
-
: "unknown",
|
|
5317
|
-
|
|
5738
|
+
: "unknown"),
|
|
5739
|
+
};
|
|
5740
|
+
});
|
|
5318
5741
|
return {
|
|
5319
5742
|
type: `LuaMultiReturn<[${emitted.map((slot) => slot.ts).join(", ")}]>`,
|
|
5320
5743
|
trailing: "",
|
|
@@ -5323,21 +5746,24 @@ function emitReturn(
|
|
|
5323
5746
|
}
|
|
5324
5747
|
const first = returnValues[0];
|
|
5325
5748
|
if (!first) return { type: "void", trailing: "", slots: [] };
|
|
5326
|
-
|
|
5749
|
+
const applied =
|
|
5327
5750
|
correction !== undefined &&
|
|
5328
5751
|
correction.slot === undefined &&
|
|
5329
5752
|
pinMatches(first.types, correctionPins(correction))
|
|
5330
|
-
|
|
5753
|
+
? correction
|
|
5754
|
+
: undefined;
|
|
5755
|
+
if (applied?.ts !== undefined) {
|
|
5331
5756
|
return {
|
|
5332
|
-
type:
|
|
5757
|
+
type: applied.ts,
|
|
5333
5758
|
trailing: "",
|
|
5334
|
-
slots: [{ position: 0, name: first.name, ts:
|
|
5759
|
+
slots: [{ position: 0, name: first.name, ts: applied.ts }],
|
|
5335
5760
|
};
|
|
5336
5761
|
}
|
|
5762
|
+
const types = correctedReturnTokens(first.types, applied);
|
|
5337
5763
|
const ts =
|
|
5338
|
-
|
|
5764
|
+
types.length > 0
|
|
5339
5765
|
? mapSlotUnion(
|
|
5340
|
-
|
|
5766
|
+
types,
|
|
5341
5767
|
first.doc,
|
|
5342
5768
|
mapType,
|
|
5343
5769
|
false,
|
|
@@ -5361,6 +5787,26 @@ function emitReturn(
|
|
|
5361
5787
|
// only".
|
|
5362
5788
|
const READ_ONLY_MARK = /^\s*<span class="mark">READ ONLY<\/span>/;
|
|
5363
5789
|
|
|
5790
|
+
// A property the engine reads but refuses to write while upstream omits the
|
|
5791
|
+
// READ ONLY mark. `evidence` names the component source that shows the missing
|
|
5792
|
+
// write branch. The emit throws once upstream adds the mark, so the entry is
|
|
5793
|
+
// deleted rather than outliving the defect it corrects.
|
|
5794
|
+
export interface ReadOnlyPropertyCorrection {
|
|
5795
|
+
readonly evidence: string;
|
|
5796
|
+
}
|
|
5797
|
+
|
|
5798
|
+
// Keyed `<namespace>.<property>`, mirroring PROPERTY_TYPE_CORRECTIONS.
|
|
5799
|
+
export const READ_ONLY_PROPERTY_CORRECTIONS: ReadonlyMap<string, ReadOnlyPropertyCorrection> =
|
|
5800
|
+
new Map([
|
|
5801
|
+
[
|
|
5802
|
+
"model.animation",
|
|
5803
|
+
{
|
|
5804
|
+
evidence:
|
|
5805
|
+
"comp_model.cpp:CompModelSetProperty has no animation branch, so the write falls through to the material-constant lookup and fails",
|
|
5806
|
+
},
|
|
5807
|
+
],
|
|
5808
|
+
]);
|
|
5809
|
+
|
|
5364
5810
|
function emitPropertyMembers(
|
|
5365
5811
|
p: ApiProperty,
|
|
5366
5812
|
mapType: DefoldTypeMapper,
|
|
@@ -5381,7 +5827,14 @@ function emitPropertyMembers(
|
|
|
5381
5827
|
: p.types.length > 0
|
|
5382
5828
|
? unionFromTokens(p.types, mapType)
|
|
5383
5829
|
: "unknown";
|
|
5384
|
-
const
|
|
5830
|
+
const marked = READ_ONLY_MARK.test(p.description);
|
|
5831
|
+
const readOnlyCorrected = READ_ONLY_PROPERTY_CORRECTIONS.has(`${namespace}.${p.name}`);
|
|
5832
|
+
if (marked && readOnlyCorrected) {
|
|
5833
|
+
throw new Error(
|
|
5834
|
+
`read-only property correction ${namespace}.${p.name}: upstream marks the property READ ONLY`,
|
|
5835
|
+
);
|
|
5836
|
+
}
|
|
5837
|
+
const modifier = marked || readOnlyCorrected ? "readonly " : "";
|
|
5385
5838
|
return keys.map((key) => `${modifier}${key}: ${ts};`);
|
|
5386
5839
|
}
|
|
5387
5840
|
|
|
@@ -5521,11 +5974,17 @@ function mapSlotUnion(
|
|
|
5521
5974
|
// return slots.
|
|
5522
5975
|
ts = "undefined";
|
|
5523
5976
|
} else {
|
|
5524
|
-
const
|
|
5977
|
+
const curation =
|
|
5525
5978
|
slotKind !== undefined && slotName !== undefined
|
|
5526
5979
|
? CALLBACK_SIGNATURE_CURATIONS.get(tableSlotKey(elementName, slotKind, slotName))
|
|
5527
5980
|
: undefined;
|
|
5528
|
-
|
|
5981
|
+
if (curation === undefined) {
|
|
5982
|
+
ts = mapType(token);
|
|
5983
|
+
} else if (isLualsFunctionToken(token)) {
|
|
5984
|
+
ts = curation.typed ?? mapType(token);
|
|
5985
|
+
} else {
|
|
5986
|
+
ts = curation.untyped;
|
|
5987
|
+
}
|
|
5529
5988
|
}
|
|
5530
5989
|
push(ts);
|
|
5531
5990
|
}
|
|
@@ -5690,7 +6149,7 @@ function joinUnionArms(mapped: readonly string[]): string {
|
|
|
5690
6149
|
}
|
|
5691
6150
|
|
|
5692
6151
|
const BUILTIN_MESSAGE_NAMES: ReadonlySet<string> = new Set(
|
|
5693
|
-
messagesDoc.
|
|
6152
|
+
applyMessageOverlays(parseMessagesDoc(messagesDoc)).entries.map((entry) => entry.name),
|
|
5694
6153
|
);
|
|
5695
6154
|
const MESSAGE_REFERENCE = /^message\.[a-z_]+\.([a-z_]+)$/;
|
|
5696
6155
|
|
|
@@ -5706,6 +6165,18 @@ function resolveDefoldLeaf(leaf: string): string | undefined {
|
|
|
5706
6165
|
return undefined;
|
|
5707
6166
|
}
|
|
5708
6167
|
|
|
6168
|
+
// The union typedefs whose message arms each carry the engine's message-name
|
|
6169
|
+
// hash in `type`: script_physics.cpp sets it on every `physics.set_event_listener`
|
|
6170
|
+
// event. The same message referenced anywhere else (`physics.raycast`) has none.
|
|
6171
|
+
const DISCRIMINATED_MESSAGE_UNIONS: ReadonlySet<string> = new Set(["physics.event"]);
|
|
6172
|
+
|
|
6173
|
+
function discriminatedMessageArm(token: string): string | undefined {
|
|
6174
|
+
const name = MESSAGE_REFERENCE.exec(token)?.[1];
|
|
6175
|
+
const payload = resolveDefoldLeaf(token);
|
|
6176
|
+
if (name === undefined || payload === undefined) return undefined;
|
|
6177
|
+
return `${payload} & { type: Hash<"${name}"> }`;
|
|
6178
|
+
}
|
|
6179
|
+
|
|
5709
6180
|
// Whole-token lookup and callback recovery run first, so the grammar sees only
|
|
5710
6181
|
// what they miss; every pre-1.13.2 token resolves before it is reached.
|
|
5711
6182
|
function wholeTokenMapping(token: string): string | undefined {
|