@defold-typescript/types 0.51.0 → 0.52.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-availability.json +4 -58
- package/api-signatures.json +878 -1356
- package/api-targets.json +15 -5
- package/examples/translations.json +4 -56
- package/generated/b2d_world.d.ts +10 -10
- package/generated/buffer.d.ts +9 -9
- package/generated/bullet3d_collision_object.d.ts +1 -1
- package/generated/bullet3d_constraint.d.ts +109 -7
- package/generated/bullet3d_shape.d.ts +2 -2
- package/generated/go.d.ts +114 -220
- package/generated/graphics.d.ts +1 -0
- package/generated/gui.d.ts +148 -371
- package/generated/http.d.ts +2 -2
- package/generated/image.d.ts +55 -3
- package/generated/kinds/gui-script.d.ts +2 -0
- package/generated/kinds/render-script.d.ts +14 -15
- package/generated/liveupdate.d.ts +5 -1
- package/generated/material.d.ts +3 -3
- package/generated/particlefx.d.ts +20 -1
- package/generated/physics.d.ts +45 -1
- package/generated/render-script-modules/b2d.d.ts +711 -0
- package/generated/render-script-modules/bullet3d.d.ts +73 -0
- package/generated/render-script-modules/collectionfactory.d.ts +25 -0
- package/generated/render-script-modules/collectionproxy.d.ts +39 -0
- package/generated/render-script-modules/factory.d.ts +25 -0
- package/generated/render-script-modules/go.d.ts +436 -0
- package/generated/render-script-modules/label.d.ts +105 -0
- package/generated/render-script-modules/model.d.ts +89 -0
- package/generated/render-script-modules/particlefx.d.ts +59 -0
- package/generated/render-script-modules/physics.d.ts +333 -0
- package/generated/render-script-modules/resource.d.ts +1233 -0
- package/generated/render-script-modules/sound.d.ts +227 -0
- package/generated/render-script-modules/sprite.d.ts +68 -0
- package/generated/render-script-modules/tilemap.d.ts +43 -0
- package/generated/render.d.ts +134 -8
- package/generated/resource.d.ts +31 -10
- package/generated/sprite.d.ts +49 -1
- package/generated/sys.d.ts +70 -6
- package/generated/versions/defold-1.12.4/buffer.d.ts +8 -8
- package/generated/versions/defold-1.12.4/go.d.ts +115 -343
- package/generated/versions/defold-1.12.4/graphics.d.ts +1 -0
- package/generated/versions/defold-1.12.4/gui.d.ts +151 -513
- package/generated/versions/defold-1.12.4/http.d.ts +2 -2
- package/generated/versions/defold-1.12.4/image.d.ts +82 -2
- package/generated/versions/defold-1.12.4/index.d.ts +1 -0
- package/generated/versions/defold-1.12.4/liveupdate.d.ts +4 -0
- package/generated/versions/defold-1.12.4/particlefx.d.ts +22 -1
- package/generated/versions/defold-1.12.4/physics.d.ts +45 -1
- package/generated/versions/defold-1.12.4/render.d.ts +78 -2
- package/generated/versions/defold-1.12.4/resource.d.ts +43 -10
- package/generated/versions/defold-1.12.4/sprite.d.ts +58 -1
- package/generated/versions/defold-1.12.4/sys.d.ts +101 -6
- package/generated/versions/defold-1.12.4/vmath.d.ts +0 -24
- package/generated/versions/defold-1.13.0/buffer.d.ts +8 -8
- package/generated/versions/defold-1.13.0/go.d.ts +115 -370
- package/generated/versions/defold-1.13.0/graphics.d.ts +1 -0
- package/generated/versions/defold-1.13.0/gui.d.ts +151 -513
- package/generated/versions/defold-1.13.0/http.d.ts +2 -2
- package/generated/versions/defold-1.13.0/image.d.ts +82 -2
- package/generated/versions/defold-1.13.0/index.d.ts +1 -0
- package/generated/versions/defold-1.13.0/liveupdate.d.ts +4 -0
- package/generated/versions/defold-1.13.0/material.d.ts +3 -1
- package/generated/versions/defold-1.13.0/particlefx.d.ts +22 -1
- package/generated/versions/defold-1.13.0/physics.d.ts +45 -1
- package/generated/versions/defold-1.13.0/render.d.ts +142 -4
- package/generated/versions/defold-1.13.0/resource.d.ts +43 -10
- package/generated/versions/defold-1.13.0/sprite.d.ts +58 -1
- package/generated/versions/defold-1.13.0/sys.d.ts +101 -6
- package/generated/versions/defold-1.13.0/vmath.d.ts +0 -24
- package/generated/versions/defold-1.13.1/buffer.d.ts +8 -8
- package/generated/versions/defold-1.13.1/go.d.ts +115 -370
- package/generated/versions/defold-1.13.1/graphics.d.ts +1 -0
- package/generated/versions/defold-1.13.1/gui.d.ts +151 -529
- package/generated/versions/defold-1.13.1/http.d.ts +2 -2
- package/generated/versions/defold-1.13.1/image.d.ts +82 -2
- package/generated/versions/defold-1.13.1/index.d.ts +2 -0
- package/generated/versions/defold-1.13.1/liveupdate.d.ts +4 -0
- package/generated/versions/defold-1.13.1/material.d.ts +3 -1
- package/generated/versions/defold-1.13.1/particlefx.d.ts +22 -1
- package/generated/versions/defold-1.13.1/physics.d.ts +45 -1
- package/generated/versions/defold-1.13.1/render.d.ts +142 -4
- package/generated/versions/defold-1.13.1/resource.d.ts +43 -10
- package/generated/versions/defold-1.13.1/sprite.d.ts +58 -1
- package/generated/versions/defold-1.13.1/sys.d.ts +101 -6
- package/generated/versions/defold-1.13.1/vmath.d.ts +0 -24
- package/generated/versions/defold-1.13.2/b2d_world.d.ts +10 -10
- package/generated/versions/defold-1.13.2/buffer.d.ts +9 -9
- package/generated/versions/defold-1.13.2/bullet3d_collision_object.d.ts +1 -1
- package/generated/versions/defold-1.13.2/bullet3d_constraint.d.ts +109 -7
- package/generated/versions/defold-1.13.2/bullet3d_shape.d.ts +2 -2
- package/generated/versions/defold-1.13.2/go.d.ts +114 -220
- package/generated/versions/defold-1.13.2/graphics.d.ts +1 -0
- package/generated/versions/defold-1.13.2/gui.d.ts +148 -371
- package/generated/versions/defold-1.13.2/http.d.ts +2 -2
- package/generated/versions/defold-1.13.2/image.d.ts +55 -3
- package/generated/versions/defold-1.13.2/index.d.ts +2 -0
- package/generated/versions/defold-1.13.2/liveupdate.d.ts +5 -1
- package/generated/versions/defold-1.13.2/material.d.ts +3 -3
- package/generated/versions/defold-1.13.2/particlefx.d.ts +20 -1
- package/generated/versions/defold-1.13.2/physics.d.ts +45 -1
- package/generated/versions/defold-1.13.2/render.d.ts +123 -7
- package/generated/versions/defold-1.13.2/resource.d.ts +31 -10
- package/generated/versions/defold-1.13.2/sprite.d.ts +49 -1
- package/generated/versions/defold-1.13.2/sys.d.ts +70 -6
- package/generated/versions/defold-1.13.2/vmath.d.ts +0 -24
- package/generated/vmath.d.ts +0 -24
- package/identity-vocabulary.json +52 -7
- package/index.d.ts +2 -0
- package/package.json +2 -1
- package/scripts/generate-api-availability.ts +1 -21
- package/scripts/generate-api-signatures.ts +75 -14
- package/scripts/materialize-version.ts +48 -7
- package/scripts/regen.ts +214 -19
- package/scripts/signature-store-fs.ts +1 -0
- package/scripts/target-version.ts +26 -0
- package/src/api-doc.ts +26 -0
- package/src/component-properties.d.ts +89 -0
- package/src/core-types.ts +42 -15
- package/src/doc-corrections.ts +53 -5
- package/src/emit-dts.ts +758 -198
- package/src/engine-globals.d.ts +5 -1
- package/src/go-overloads.d.ts +68 -13
- package/src/gui-overloads.d.ts +489 -0
- package/src/gui-texture-removal.d.ts +41 -0
- package/src/index.ts +2 -0
- package/src/lifecycle.ts +30 -21
- package/src/msg-overloads.d.ts +25 -13
- package/src/signature-store.ts +50 -0
- package/src/timers.d.ts +2 -1
- package/src/vmath-overloads.d.ts +26 -0
package/src/emit-dts.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import messagesDoc from "../fixtures/messages_doc.json" with { type: "json" };
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type ApiSymbolIdentity,
|
|
4
|
+
type Box2dBackend,
|
|
5
|
+
normalizedFunctionSignature,
|
|
6
|
+
} from "./api-availability";
|
|
3
7
|
import type {
|
|
4
8
|
ApiConstant,
|
|
5
9
|
ApiFunction,
|
|
@@ -36,6 +40,7 @@ import {
|
|
|
36
40
|
type LualsPosition,
|
|
37
41
|
mapLualsExpression,
|
|
38
42
|
needsArrayParens,
|
|
43
|
+
splitTopLevel,
|
|
39
44
|
} from "./luals-type-expr";
|
|
40
45
|
import { classifyUrlParameter, type UrlParameterTable } from "./url-parameters";
|
|
41
46
|
|
|
@@ -86,8 +91,15 @@ export interface EmitOptions {
|
|
|
86
91
|
// applies only when its key is here, so a surface with no vendored bindings
|
|
87
92
|
// (absent) applies none of them.
|
|
88
93
|
bindingEvidence?: ReadonlySet<string>;
|
|
94
|
+
// The 1-based parameter slots, keyed by function FQN, that the target's engine
|
|
95
|
+
// bindings let a call omit but raise on when passed nil. Each optional one
|
|
96
|
+
// splits its function into a call without the slot and a call requiring it,
|
|
97
|
+
// so a possibly-undefined value is a compile error. Absent for a surface with
|
|
98
|
+
// no vendored bindings (a ref-doc target), which keeps every `?`.
|
|
99
|
+
nilRaisingSlots?: ReadonlyMap<string, readonly number[]>;
|
|
89
100
|
// The functions a target's skip rules removed from the module, read only to
|
|
90
|
-
// type an `OVERLOAD_SLOT_ALIASES` slot
|
|
101
|
+
// type an `OVERLOAD_SLOT_ALIASES` slot and to resolve a
|
|
102
|
+
// `CONSTANT_SLOT_RESOLUTIONS` borrow from their docs.
|
|
91
103
|
withheldFunctions?: readonly ApiFunction[];
|
|
92
104
|
}
|
|
93
105
|
|
|
@@ -179,14 +191,12 @@ export const ARBITRARY_TABLE_SLOTS = new Set([
|
|
|
179
191
|
// runtime-owned passthrough: the table is filled by user code at runtime, never
|
|
180
192
|
// by Defold's API contract. factory.create/collectionfactory.create `properties`
|
|
181
193
|
// are spawn-time overrides keyed by the spawned object's own script-property
|
|
182
|
-
// names
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// mapping; each per-instance property table stays that faithful `Record`.
|
|
194
|
+
// names. No static shape exists, so `Record<string | number, unknown>` is
|
|
195
|
+
// faithful, not a loss. collectionfactory.create curates the instance-id layer
|
|
196
|
+
// of `properties` as a mapping; each per-instance property table stays that
|
|
197
|
+
// faithful `Record`.
|
|
187
198
|
"factory.create",
|
|
188
199
|
"collectionfactory.create",
|
|
189
|
-
"on_message",
|
|
190
200
|
// engine-formatted blob: the engine produces the value at runtime and no
|
|
191
201
|
// field list is documented, so the whole-slot `Record` is faithful, not a
|
|
192
202
|
// loss. crash.get_backtrace is "table containing the backtrace",
|
|
@@ -259,6 +269,11 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
|
259
269
|
"go.get",
|
|
260
270
|
"go.set",
|
|
261
271
|
"go.property",
|
|
272
|
+
// gui-overloads.d.ts closes the node property names and types each value per
|
|
273
|
+
// name, with a hash form for material constants.
|
|
274
|
+
"gui.animate",
|
|
275
|
+
"gui.get",
|
|
276
|
+
"gui.set",
|
|
262
277
|
// msg-overloads.d.ts supplies the typed msg.post shape.
|
|
263
278
|
"msg.post",
|
|
264
279
|
// msg-overloads.d.ts supplies the typed msg.url shape (0 / 1-string / 3-arg
|
|
@@ -277,6 +292,7 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
|
|
|
277
292
|
"vmath.slerp",
|
|
278
293
|
"vmath.mul_per_elem",
|
|
279
294
|
"vmath.normalize",
|
|
295
|
+
"vmath.dot",
|
|
280
296
|
// sys-overloads.d.ts supplies the disabling form and the form that takes the
|
|
281
297
|
// cooldown the engine requires when enabling.
|
|
282
298
|
"sys.set_engine_throttle",
|
|
@@ -334,13 +350,10 @@ export const MAPPING_TABLE_SLOTS: ReadonlyMap<string, { key: string; value: stri
|
|
|
334
350
|
]);
|
|
335
351
|
|
|
336
352
|
// FQN-keyed return-type overrides for functions whose doc `returnvalues` are
|
|
337
|
-
// empty yet the engine returns a value.
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
// -> unknown" rule: `gui.set` also has empty returnvalues and `void` is correct
|
|
341
|
-
// there.
|
|
353
|
+
// empty yet the engine returns a value. Per-FQN, not a blanket "empty
|
|
354
|
+
// returnvalues -> unknown" rule: most functions with empty returnvalues return
|
|
355
|
+
// nothing, and `void` is correct there.
|
|
342
356
|
export const RETURN_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
343
|
-
["gui.get", "unknown"],
|
|
344
357
|
// Upstream documents the returned transaction step in prose only — its
|
|
345
358
|
// `returnvalues` is empty while every sibling `editor.tx.*` builder declares
|
|
346
359
|
// one, and `editor.transact()` takes exactly that.
|
|
@@ -459,8 +472,8 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
459
472
|
slot: "code",
|
|
460
473
|
},
|
|
461
474
|
],
|
|
462
|
-
// 1.13.2
|
|
463
|
-
//
|
|
475
|
+
// 1.13.2 retypes the return to `render_predicate`, whose TYPEDEF aliases
|
|
476
|
+
// `userdata`; its type mapping emits the same brand.
|
|
464
477
|
[
|
|
465
478
|
"render.predicate",
|
|
466
479
|
{
|
|
@@ -538,6 +551,20 @@ export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection>
|
|
|
538
551
|
binding: tilemapEarlyNil("TileMap_GetTiles"),
|
|
539
552
|
},
|
|
540
553
|
],
|
|
554
|
+
[
|
|
555
|
+
"bullet3d.shape.get_shape",
|
|
556
|
+
{
|
|
557
|
+
ts: "bullet3d.shape.definition | { type: typeof bullet3d.shape.SHAPE_TYPE_MESH }",
|
|
558
|
+
upstream: ["bullet3d.shape.definition"],
|
|
559
|
+
reason:
|
|
560
|
+
"script_bullet3d_shape.cpp:Shape_GetShape sets only `type` for a triangle mesh, which no shape definition arm matches",
|
|
561
|
+
binding: {
|
|
562
|
+
file: "gamesys/src/gamesys/scripts/bullet3d/script_bullet3d_shape.cpp",
|
|
563
|
+
cFunction: "Shape_GetShape",
|
|
564
|
+
contains: "case TRIANGLE_MESH_SHAPE_PROXYTYPE: case GIMPACT_SHAPE_PROXYTYPE: break;",
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
],
|
|
541
568
|
[
|
|
542
569
|
"collectionproxy.set_collection",
|
|
543
570
|
{
|
|
@@ -757,6 +784,35 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
757
784
|
},
|
|
758
785
|
] as const,
|
|
759
786
|
),
|
|
787
|
+
...(
|
|
788
|
+
[
|
|
789
|
+
["clone_tree", "LuaCloneTree"],
|
|
790
|
+
["get_tree", "LuaGetTree"],
|
|
791
|
+
] as const
|
|
792
|
+
).map(
|
|
793
|
+
([name, cFunction]) =>
|
|
794
|
+
[
|
|
795
|
+
`gui.${name}:param:node`,
|
|
796
|
+
{
|
|
797
|
+
adds: "undefined",
|
|
798
|
+
upstream: ["node"],
|
|
799
|
+
reason: `gui_script.cpp:${cFunction} pushes its result table before testing argument 1 with lua_isnil, so nil selects the whole scene and an absent argument reaches LuaCheckNodeInternal and raises`,
|
|
800
|
+
},
|
|
801
|
+
] as const,
|
|
802
|
+
),
|
|
803
|
+
...(["get_slider_motor", "set_slider_motor"] as const).map(
|
|
804
|
+
(name) =>
|
|
805
|
+
[
|
|
806
|
+
`bullet3d.constraint.${name}:param:motor`,
|
|
807
|
+
{
|
|
808
|
+
adds: '"linear" | "angular"',
|
|
809
|
+
removes: ["string"],
|
|
810
|
+
upstream: ["string"],
|
|
811
|
+
reason:
|
|
812
|
+
"script_bullet3d_constraint.cpp:CheckSliderMotorAxis raises \"motor must be 'linear' or 'angular'\" on any other string",
|
|
813
|
+
},
|
|
814
|
+
] as const,
|
|
815
|
+
),
|
|
760
816
|
[
|
|
761
817
|
"sound.get_group_name:param:group",
|
|
762
818
|
{
|
|
@@ -835,9 +891,10 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
835
891
|
"render.disable_texture:param:binding",
|
|
836
892
|
{
|
|
837
893
|
adds: "number",
|
|
894
|
+
removes: ['Opaque<"texture">'],
|
|
838
895
|
upstream: ["texture", "string", "hash"],
|
|
839
896
|
reason:
|
|
840
|
-
|
|
897
|
+
"render_script.cpp:RenderScript_DisableTexture reads a number as a texture unit with lua_isnumber and anything else with CheckHashOrString; a texture handle is a number, so it is read as a unit",
|
|
841
898
|
},
|
|
842
899
|
],
|
|
843
900
|
[
|
|
@@ -855,11 +912,42 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
855
912
|
adds: 'Opaque<"render_predicate">',
|
|
856
913
|
removes: ["number"],
|
|
857
914
|
upstream: ["number"],
|
|
858
|
-
retypedUpstream: [["render_predicate"]],
|
|
859
915
|
reason:
|
|
860
|
-
"render_script.cpp:RenderScript_Draw reads the predicate with RenderScriptPredicate_Check, the userdata render.predicate returns; 1.13.2
|
|
916
|
+
"render_script.cpp:RenderScript_Draw reads the predicate with RenderScriptPredicate_Check, the userdata render.predicate returns; 1.13.2 types the slot `render_predicate`, whose type mapping is that brand",
|
|
861
917
|
},
|
|
862
918
|
],
|
|
919
|
+
...(["enable_state", "disable_state"] as const).map(
|
|
920
|
+
(name) =>
|
|
921
|
+
[
|
|
922
|
+
`render.${name}:param:state`,
|
|
923
|
+
{
|
|
924
|
+
adds: "graphics.State",
|
|
925
|
+
removes: ["graphics.STATE"],
|
|
926
|
+
upstream: ["graphics.STATE"],
|
|
927
|
+
reason: `render_script.cpp:RenderScript_${name === "enable_state" ? "Enable" : "Disable"}State raises "Invalid state" on any state but the six it lists, so graphics.STATE_SCISSOR_TEST and graphics.STATE_ALPHA_TEST_SUPPORTED are rejected`,
|
|
928
|
+
},
|
|
929
|
+
] as const,
|
|
930
|
+
),
|
|
931
|
+
...(
|
|
932
|
+
[
|
|
933
|
+
"source_factor_color",
|
|
934
|
+
"destination_factor_color",
|
|
935
|
+
"source_factor_alpha",
|
|
936
|
+
"destination_factor_alpha",
|
|
937
|
+
] as const
|
|
938
|
+
).map(
|
|
939
|
+
(slot) =>
|
|
940
|
+
[
|
|
941
|
+
`render.set_blend_func_separate:param:${slot}`,
|
|
942
|
+
{
|
|
943
|
+
adds: "graphics.BlendFactor",
|
|
944
|
+
removes: ["number"],
|
|
945
|
+
upstream: ["number"],
|
|
946
|
+
reason:
|
|
947
|
+
'render_script.cpp:RenderScript_SetBlendFuncSeparate raises "Invalid blend factor" on any value CheckBlendFactor does not list',
|
|
948
|
+
},
|
|
949
|
+
] as const,
|
|
950
|
+
),
|
|
863
951
|
[
|
|
864
952
|
"go.delete:param:id",
|
|
865
953
|
{
|
|
@@ -878,15 +966,6 @@ export const PARAM_TYPE_CORRECTIONS: ReadonlyMap<string, ParamTypeCorrection> =
|
|
|
878
966
|
reason: 'sys.load_buffer example `buffer.get_bytes(my_buffer, "data")`',
|
|
879
967
|
},
|
|
880
968
|
],
|
|
881
|
-
[
|
|
882
|
-
"gui.set:param:value",
|
|
883
|
-
{
|
|
884
|
-
adds: "Matrix4 | Hash",
|
|
885
|
-
upstream: ["number", "vector4", "vector3", "quaternion"],
|
|
886
|
-
reason:
|
|
887
|
-
'example `gui.set(node, "light_matrix", vmath.matrix4())` ("matrix4 is also supported"); example `gui.set(msg.url(), "textures", atlas_id, {key = "runtime_texture"})` passes the hash `resource.create_atlas` returns',
|
|
888
|
-
},
|
|
889
|
-
],
|
|
890
969
|
[
|
|
891
970
|
"gui.set_rotation:param:rotation",
|
|
892
971
|
{
|
|
@@ -1095,6 +1174,10 @@ export const OPTIONAL_SLOT_CORRECTIONS: ReadonlyMap<string, string> = new Map([
|
|
|
1095
1174
|
"render.set_render_target:param:render_target",
|
|
1096
1175
|
"render_script.cpp:RenderScript_SetRenderTarget sets the default target when lua_gettop(L) == 0",
|
|
1097
1176
|
],
|
|
1177
|
+
[
|
|
1178
|
+
"buffer.get_bytes:param:stream_name",
|
|
1179
|
+
"script_buffer.cpp:GetBytes reads only argument 1 with CheckBufferUnpack and returns every byte of the buffer",
|
|
1180
|
+
],
|
|
1098
1181
|
["socket.newtry:param:finalizer", "socket.protect's example `local try = socket.newtry()`"],
|
|
1099
1182
|
...(
|
|
1100
1183
|
[
|
|
@@ -1237,13 +1320,6 @@ export const REQUIRED_SLOT_CORRECTIONS: ReadonlyMap<string, RequiredSlotCorrecti
|
|
|
1237
1320
|
evidence: "gui_script.cpp:LuaSetParent reads a node from argument 2 unless lua_isnil(L, 2)",
|
|
1238
1321
|
},
|
|
1239
1322
|
],
|
|
1240
|
-
[
|
|
1241
|
-
"gui.set:param:value",
|
|
1242
|
-
{
|
|
1243
|
-
nil: true,
|
|
1244
|
-
evidence: "gui_script.cpp:LuaSet converts argument 3 with LuaToVar unless lua_isnil(L, 3)",
|
|
1245
|
-
},
|
|
1246
|
-
],
|
|
1247
1323
|
[
|
|
1248
1324
|
"window.set_listener:param:callback",
|
|
1249
1325
|
{
|
|
@@ -1333,6 +1409,335 @@ function firstSlotAlternative(
|
|
|
1333
1409
|
return original === null ? null : { ...prepared, original };
|
|
1334
1410
|
}
|
|
1335
1411
|
|
|
1412
|
+
// The calls a function splits into at its optional nil-raising slots
|
|
1413
|
+
// (`EmitOptions.nilRaisingSlots`): one ending before each such slot, then one
|
|
1414
|
+
// taking them all, every such slot before an arm's end declared required. A
|
|
1415
|
+
// required slot, or one a rest parameter folds in, splits nothing.
|
|
1416
|
+
function nilRaisingArms(
|
|
1417
|
+
prepared: PreparedFunction,
|
|
1418
|
+
nilRaisingSlots: ReadonlyMap<string, readonly number[]>,
|
|
1419
|
+
): PreparedFunction[] {
|
|
1420
|
+
const { name, parameters } = prepared.original;
|
|
1421
|
+
const firstVararg = parameters.findIndex(isVarargParameter);
|
|
1422
|
+
const positional = firstVararg === -1 ? parameters.length : firstVararg;
|
|
1423
|
+
const split = [...new Set(nilRaisingSlots.get(name) ?? [])]
|
|
1424
|
+
.map((slot) => slot - 1)
|
|
1425
|
+
.filter((index) => {
|
|
1426
|
+
const p = parameters[index];
|
|
1427
|
+
return index < positional && p !== undefined && isDocOptional(p, name);
|
|
1428
|
+
})
|
|
1429
|
+
.sort((a, b) => a - b);
|
|
1430
|
+
if (split.length === 0) return [prepared];
|
|
1431
|
+
const shorter = split.map((end, k) => ({
|
|
1432
|
+
...prepared,
|
|
1433
|
+
original: { ...prepared.original, parameters: parameters.slice(0, end) },
|
|
1434
|
+
required: new Set(split.slice(0, k)),
|
|
1435
|
+
}));
|
|
1436
|
+
return [...shorter, { ...prepared, required: new Set(split) }];
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// Table fields a binding reads as required only when `argument` is non-nil,
|
|
1440
|
+
// keyed by FQN. A curated function splits into a call whose `table` slot requires
|
|
1441
|
+
// `fields`, with `argument` as declared, then a call whose `argument` is nil, so
|
|
1442
|
+
// a possibly-nil argument must carry them.
|
|
1443
|
+
export interface PresenceRequiredFields {
|
|
1444
|
+
readonly argument: string;
|
|
1445
|
+
readonly table: string;
|
|
1446
|
+
readonly fields: readonly string[];
|
|
1447
|
+
readonly reason: string;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
const FRAME_B_FIELDS = ["frame_b_position", "frame_b_rotation"] as const;
|
|
1451
|
+
|
|
1452
|
+
export const PRESENCE_REQUIRED_FIELDS: ReadonlyMap<string, PresenceRequiredFields> = new Map<
|
|
1453
|
+
string,
|
|
1454
|
+
PresenceRequiredFields
|
|
1455
|
+
>([
|
|
1456
|
+
[
|
|
1457
|
+
"bullet3d.constraint.create_point_to_point",
|
|
1458
|
+
{
|
|
1459
|
+
argument: "body_b",
|
|
1460
|
+
table: "params",
|
|
1461
|
+
fields: ["pivot_b"],
|
|
1462
|
+
reason:
|
|
1463
|
+
"script_bullet3d_constraint.cpp:Constraint_CreatePointToPoint reads pivot_b as required when input.m_BodyB != 0",
|
|
1464
|
+
},
|
|
1465
|
+
],
|
|
1466
|
+
...(
|
|
1467
|
+
[
|
|
1468
|
+
["create_hinge", "Constraint_CreateHinge"],
|
|
1469
|
+
["create_cone_twist", "Constraint_CreateConeTwist"],
|
|
1470
|
+
["create_generic_6dof", "Constraint_CreateGeneric6Dof"],
|
|
1471
|
+
["create_slider", "Constraint_CreateSlider"],
|
|
1472
|
+
] as const
|
|
1473
|
+
).map(
|
|
1474
|
+
([name, cFunction]) =>
|
|
1475
|
+
[
|
|
1476
|
+
`bullet3d.constraint.${name}`,
|
|
1477
|
+
{
|
|
1478
|
+
argument: "body_b",
|
|
1479
|
+
table: "params",
|
|
1480
|
+
fields: FRAME_B_FIELDS,
|
|
1481
|
+
reason: `script_bullet3d_constraint.cpp:${cFunction} reads frame B as required under if (input.m_BodyB)`,
|
|
1482
|
+
},
|
|
1483
|
+
] as const,
|
|
1484
|
+
),
|
|
1485
|
+
]);
|
|
1486
|
+
|
|
1487
|
+
// The calls PRESENCE_REQUIRED_FIELDS splits `prepared` into: the declared one
|
|
1488
|
+
// with its fields required, then the one whose argument is nil.
|
|
1489
|
+
function presenceArms(
|
|
1490
|
+
prepared: PreparedFunction,
|
|
1491
|
+
structs: readonly ApiStruct[],
|
|
1492
|
+
): PreparedFunction[] {
|
|
1493
|
+
const { name, parameters } = prepared.original;
|
|
1494
|
+
const entry = PRESENCE_REQUIRED_FIELDS.get(name);
|
|
1495
|
+
if (entry === undefined) return [prepared];
|
|
1496
|
+
const argument = parameters.findIndex((p) => p.name === entry.argument);
|
|
1497
|
+
const table = parameters.findIndex((p) => p.name === entry.table);
|
|
1498
|
+
const struct = structs.find((s) => parameters[table]?.types.includes(s.name));
|
|
1499
|
+
if (argument === -1 || struct === undefined) {
|
|
1500
|
+
throw new Error(
|
|
1501
|
+
`presence-required fields ${name}: no ${entry.argument} parameter or ${entry.table} table`,
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
const missing = entry.fields.filter((field) => !struct.members.some((m) => m.name === field));
|
|
1505
|
+
if (missing.length > 0) {
|
|
1506
|
+
throw new Error(`presence-required fields ${name}: ${struct.name} lacks ${missing.join(", ")}`);
|
|
1507
|
+
}
|
|
1508
|
+
const picked = entry.fields.map((field) => `"${field}"`).join(" | ");
|
|
1509
|
+
const requireFields = (type: string): string => {
|
|
1510
|
+
const operand = /^[\w$.]+$/.test(type) ? type : `(${type})`;
|
|
1511
|
+
return `${operand} & Required<Pick<${operand}, ${picked}>>`;
|
|
1512
|
+
};
|
|
1513
|
+
const strict: PreparedFunction = { ...prepared, retype: new Map([[table, requireFields]]) };
|
|
1514
|
+
// Declared required, the nil argument emits as `undefined` alone rather than
|
|
1515
|
+
// gaining the `| undefined` of an interior optional slot.
|
|
1516
|
+
const absent: PreparedFunction = {
|
|
1517
|
+
...prepared,
|
|
1518
|
+
required: new Set([...(prepared.required ?? []), argument]),
|
|
1519
|
+
retype: new Map([[argument, () => "undefined"]]),
|
|
1520
|
+
};
|
|
1521
|
+
return [strict, absent];
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// A rule an engine binding enforces on one parameter that no ref-doc token
|
|
1525
|
+
// states, keyed `<fqn>:param:<name>`. Each rewrites the slot's mapped type, so
|
|
1526
|
+
// one entry holds on every target whatever token spelling produced that type.
|
|
1527
|
+
export type EngineSlotRule =
|
|
1528
|
+
// `E[]` becomes `[E, ...E[]]`: the binding raises on an empty list.
|
|
1529
|
+
| { readonly kind: "non-empty"; readonly reason: string }
|
|
1530
|
+
// The constants the binding raises on leave the slot's type.
|
|
1531
|
+
| { readonly kind: "excludes"; readonly constants: readonly string[]; readonly reason: string }
|
|
1532
|
+
// With `flag` true the table must carry `fields`, which the binding then reads
|
|
1533
|
+
// without a default.
|
|
1534
|
+
| {
|
|
1535
|
+
readonly kind: "flag-requires";
|
|
1536
|
+
readonly flag: string;
|
|
1537
|
+
readonly fields: readonly string[];
|
|
1538
|
+
readonly reason: string;
|
|
1539
|
+
}
|
|
1540
|
+
// In each call that requires `slot`, this slot is required too and its
|
|
1541
|
+
// top-level union loses `drops`.
|
|
1542
|
+
| {
|
|
1543
|
+
readonly kind: "alongside";
|
|
1544
|
+
readonly slot: string;
|
|
1545
|
+
readonly drops: readonly string[];
|
|
1546
|
+
readonly reason: string;
|
|
1547
|
+
};
|
|
1548
|
+
|
|
1549
|
+
export const ENGINE_SLOT_RULES: ReadonlyMap<string, EngineSlotRule> = new Map<
|
|
1550
|
+
string,
|
|
1551
|
+
EngineSlotRule
|
|
1552
|
+
>([
|
|
1553
|
+
[
|
|
1554
|
+
"buffer.create:param:declaration",
|
|
1555
|
+
{
|
|
1556
|
+
kind: "non-empty",
|
|
1557
|
+
reason:
|
|
1558
|
+
'script_buffer.cpp:Create raises "You must specify at least one stream declaration" when the list holds fewer than one entry',
|
|
1559
|
+
},
|
|
1560
|
+
],
|
|
1561
|
+
[
|
|
1562
|
+
"buffer.set_metadata:param:values",
|
|
1563
|
+
{
|
|
1564
|
+
kind: "non-empty",
|
|
1565
|
+
reason:
|
|
1566
|
+
'script_buffer.cpp:SetMetadata raises "invalid metadata" when lua_objlen of values is 0',
|
|
1567
|
+
},
|
|
1568
|
+
],
|
|
1569
|
+
[
|
|
1570
|
+
"buffer.set_metadata:param:value_type",
|
|
1571
|
+
{
|
|
1572
|
+
kind: "excludes",
|
|
1573
|
+
constants: ["buffer.VALUE_TYPE_INT64", "buffer.VALUE_TYPE_UINT64"],
|
|
1574
|
+
reason:
|
|
1575
|
+
'script_buffer.cpp:SetMetadata raises "64 bit integer metadata are not supported." on VALUE_TYPE_UINT64 and VALUE_TYPE_INT64',
|
|
1576
|
+
},
|
|
1577
|
+
],
|
|
1578
|
+
[
|
|
1579
|
+
"resource.create_sound_data:param:options",
|
|
1580
|
+
{
|
|
1581
|
+
kind: "flag-requires",
|
|
1582
|
+
flag: "partial",
|
|
1583
|
+
fields: ["filesize"],
|
|
1584
|
+
reason:
|
|
1585
|
+
"script_resource.cpp:CreateSoundData reads filesize with the no-default CheckFieldValue<int> when partial is true, and CheckValue<int> raises on a non-number",
|
|
1586
|
+
},
|
|
1587
|
+
],
|
|
1588
|
+
[
|
|
1589
|
+
"go.delete:param:id",
|
|
1590
|
+
{
|
|
1591
|
+
kind: "alongside",
|
|
1592
|
+
slot: "recursive",
|
|
1593
|
+
drops: ["boolean"],
|
|
1594
|
+
reason:
|
|
1595
|
+
"gameobject_script.cpp:Script_Delete raises on a boolean id with a second argument, and on a nil id once recursive is popped",
|
|
1596
|
+
},
|
|
1597
|
+
],
|
|
1598
|
+
]);
|
|
1599
|
+
|
|
1600
|
+
// `prepared` with `rewrite` applied after any rewrite it already makes at `index`.
|
|
1601
|
+
function withRetype(
|
|
1602
|
+
prepared: PreparedFunction,
|
|
1603
|
+
index: number,
|
|
1604
|
+
rewrite: (type: string) => string,
|
|
1605
|
+
): PreparedFunction {
|
|
1606
|
+
const retype = new Map(prepared.retype);
|
|
1607
|
+
const prior = retype.get(index);
|
|
1608
|
+
retype.set(index, prior === undefined ? rewrite : (type) => rewrite(prior(type)));
|
|
1609
|
+
return { ...prepared, retype };
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// `E[]` as `[E, ...E[]]`; the element keeps its own parentheses.
|
|
1613
|
+
function nonEmptyList(type: string, key: string): string {
|
|
1614
|
+
const element = type.endsWith("[]") ? type.slice(0, -2) : "";
|
|
1615
|
+
const single =
|
|
1616
|
+
splitTopLevel(type, "|").length === 1 &&
|
|
1617
|
+
splitTopLevel(type, "&").length === 1 &&
|
|
1618
|
+
!isFunctionTypeText(type);
|
|
1619
|
+
if (element === "" || !single) {
|
|
1620
|
+
throw new Error(`engine slot rule ${key}: ${type} is not one array`);
|
|
1621
|
+
}
|
|
1622
|
+
return `[${element}, ...${element}[]]`;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
function withoutMembers(type: string, drops: readonly string[], key: string): string {
|
|
1626
|
+
const members = splitTopLevel(type, "|").map((member) => member.trim());
|
|
1627
|
+
const missing = drops.filter((drop) => !members.includes(drop));
|
|
1628
|
+
if (missing.length > 0) {
|
|
1629
|
+
throw new Error(`engine slot rule ${key}: ${type} has no ${missing.join(", ")} to drop`);
|
|
1630
|
+
}
|
|
1631
|
+
return members.filter((member) => !drops.includes(member)).join(" | ");
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// The field names a table slot declares: its STRUCT's members, else the fields
|
|
1635
|
+
// parsed from its own doc.
|
|
1636
|
+
function slotFieldNames(parameter: ApiParameter, structs: readonly ApiStruct[]): string[] {
|
|
1637
|
+
const struct = structs.find((s) => parameter.types.includes(s.name));
|
|
1638
|
+
if (struct !== undefined) return struct.members.map((member) => member.name);
|
|
1639
|
+
const fields = parameter.fields ?? parseTableFields(parameter.doc) ?? [];
|
|
1640
|
+
return fields.map((field) => field.name);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
function engineSlotRuleArms(
|
|
1644
|
+
prepared: PreparedFunction,
|
|
1645
|
+
arms: readonly PreparedFunction[],
|
|
1646
|
+
key: string,
|
|
1647
|
+
rule: EngineSlotRule,
|
|
1648
|
+
index: number,
|
|
1649
|
+
structs: readonly ApiStruct[],
|
|
1650
|
+
bindingsRead: boolean,
|
|
1651
|
+
): PreparedFunction[] {
|
|
1652
|
+
const { name, parameters } = prepared.original;
|
|
1653
|
+
const takes = (arm: PreparedFunction): boolean => index < arm.original.parameters.length;
|
|
1654
|
+
const rewriteEach = (rewrite: (type: string) => string): PreparedFunction[] =>
|
|
1655
|
+
arms.map((arm) => (takes(arm) ? withRetype(arm, index, rewrite) : arm));
|
|
1656
|
+
switch (rule.kind) {
|
|
1657
|
+
case "non-empty":
|
|
1658
|
+
return rewriteEach((type) => nonEmptyList(type, key));
|
|
1659
|
+
case "excludes": {
|
|
1660
|
+
const excluded = rule.constants.map((constant) => `typeof ${constant}`).join(" | ");
|
|
1661
|
+
return rewriteEach((type) => `Exclude<${type}, ${excluded}>`);
|
|
1662
|
+
}
|
|
1663
|
+
case "flag-requires": {
|
|
1664
|
+
const declared = slotFieldNames(parameters[index] as ApiParameter, structs);
|
|
1665
|
+
const missing = [rule.flag, ...rule.fields].filter((field) => !declared.includes(field));
|
|
1666
|
+
if (missing.length > 0) {
|
|
1667
|
+
throw new Error(`engine slot rule ${key}: the table declares no ${missing.join(", ")}`);
|
|
1668
|
+
}
|
|
1669
|
+
const picked = rule.fields.map((field) => `"${field}"`).join(" | ");
|
|
1670
|
+
return rewriteEach((type) => {
|
|
1671
|
+
const operand =
|
|
1672
|
+
splitTopLevel(type, "|").length > 1 || isFunctionTypeText(type) ? `(${type})` : type;
|
|
1673
|
+
return `${operand} & ({ ${rule.flag}?: false } | ({ ${rule.flag}: true } & Required<Pick<${type}, ${picked}>>))`;
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
case "alongside": {
|
|
1677
|
+
const partner = parameters.findIndex((p) => p.name === rule.slot);
|
|
1678
|
+
const partnerParameter = parameters[partner];
|
|
1679
|
+
const requires = (arm: PreparedFunction): boolean =>
|
|
1680
|
+
partnerParameter !== undefined &&
|
|
1681
|
+
partner < arm.original.parameters.length &&
|
|
1682
|
+
(arm.required?.has(partner) === true || !isDocOptional(partnerParameter, name));
|
|
1683
|
+
if (!arms.some(requires)) {
|
|
1684
|
+
// Without the target's nil-raising slots no call was split out to
|
|
1685
|
+
// require the partner, so there is no binding to have stopped marking it.
|
|
1686
|
+
if (!bindingsRead) return [...arms];
|
|
1687
|
+
throw new Error(
|
|
1688
|
+
partnerParameter === undefined
|
|
1689
|
+
? `engine slot rule ${key}: ${name} has no ${rule.slot} parameter`
|
|
1690
|
+
: `engine slot rule ${key}: no call of ${name} requires ${rule.slot}`,
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
return arms.map((arm) =>
|
|
1694
|
+
requires(arm)
|
|
1695
|
+
? {
|
|
1696
|
+
...withRetype(arm, index, (type) => withoutMembers(type, rule.drops, key)),
|
|
1697
|
+
required: new Set([...(arm.required ?? []), index]),
|
|
1698
|
+
}
|
|
1699
|
+
: arm,
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
// `arms` with every ENGINE_SLOT_RULES entry keyed to `prepared` applied.
|
|
1706
|
+
// `bindingsRead` says the emit was given the target's nil-raising slots.
|
|
1707
|
+
function applyEngineSlotRules(
|
|
1708
|
+
prepared: PreparedFunction,
|
|
1709
|
+
arms: readonly PreparedFunction[],
|
|
1710
|
+
structs: readonly ApiStruct[],
|
|
1711
|
+
bindingsRead: boolean,
|
|
1712
|
+
): PreparedFunction[] {
|
|
1713
|
+
const { name, parameters } = prepared.original;
|
|
1714
|
+
const prefix = `${name}:param:`;
|
|
1715
|
+
let ruled = [...arms];
|
|
1716
|
+
for (const [key, rule] of ENGINE_SLOT_RULES) {
|
|
1717
|
+
if (!key.startsWith(prefix)) continue;
|
|
1718
|
+
const slot = key.slice(prefix.length);
|
|
1719
|
+
const index = parameters.findIndex((p) => p.name === slot);
|
|
1720
|
+
if (index === -1) {
|
|
1721
|
+
throw new Error(`engine slot rule ${key}: ${name} has no ${slot} parameter`);
|
|
1722
|
+
}
|
|
1723
|
+
ruled = engineSlotRuleArms(prepared, ruled, key, rule, index, structs, bindingsRead);
|
|
1724
|
+
}
|
|
1725
|
+
return ruled;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// Every call a function declares, in order: its nil-raising arms, each split
|
|
1729
|
+
// at its presence-required fields, then narrowed by its engine slot rules.
|
|
1730
|
+
function callArms(
|
|
1731
|
+
prepared: PreparedFunction,
|
|
1732
|
+
nilRaisingSlots: ReadonlyMap<string, readonly number[]> | undefined,
|
|
1733
|
+
structs: readonly ApiStruct[],
|
|
1734
|
+
): PreparedFunction[] {
|
|
1735
|
+
const arms = nilRaisingArms(prepared, nilRaisingSlots ?? new Map()).flatMap((arm) =>
|
|
1736
|
+
presenceArms(arm, structs),
|
|
1737
|
+
);
|
|
1738
|
+
return applyEngineSlotRules(prepared, arms, structs, nilRaisingSlots !== undefined);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1336
1741
|
export type ConstantSlotResolution =
|
|
1337
1742
|
| { readonly borrow: string }
|
|
1338
1743
|
| { readonly family: string; readonly evidence: string }
|
|
@@ -1347,8 +1752,6 @@ export const CONSTANT_SLOT_RESOLUTIONS: ReadonlyMap<string, ConstantSlotResoluti
|
|
|
1347
1752
|
string,
|
|
1348
1753
|
ConstantSlotResolution
|
|
1349
1754
|
>([
|
|
1350
|
-
["gui.get:param:property", { borrow: "gui.animate:param:property" }],
|
|
1351
|
-
["gui.set:param:property", { borrow: "gui.animate:param:property" }],
|
|
1352
1755
|
["gui.cancel_animations:param:property", { borrow: "gui.animate:param:property" }],
|
|
1353
1756
|
[
|
|
1354
1757
|
"buffer.set_metadata:param:value_type",
|
|
@@ -1477,6 +1880,29 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1477
1880
|
"go.PLAYBACK_LOOP_PINGPONG",
|
|
1478
1881
|
],
|
|
1479
1882
|
},
|
|
1883
|
+
// The factors render_script.cpp:CheckBlendFactor accepts, in its order. Every
|
|
1884
|
+
// target defines the constants; only the newer ones name the union.
|
|
1885
|
+
{
|
|
1886
|
+
home: "graphics",
|
|
1887
|
+
name: "BlendFactor",
|
|
1888
|
+
members: [
|
|
1889
|
+
"graphics.BLEND_FACTOR_ZERO",
|
|
1890
|
+
"graphics.BLEND_FACTOR_ONE",
|
|
1891
|
+
"graphics.BLEND_FACTOR_SRC_COLOR",
|
|
1892
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_SRC_COLOR",
|
|
1893
|
+
"graphics.BLEND_FACTOR_DST_COLOR",
|
|
1894
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_DST_COLOR",
|
|
1895
|
+
"graphics.BLEND_FACTOR_SRC_ALPHA",
|
|
1896
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA",
|
|
1897
|
+
"graphics.BLEND_FACTOR_DST_ALPHA",
|
|
1898
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_DST_ALPHA",
|
|
1899
|
+
"graphics.BLEND_FACTOR_SRC_ALPHA_SATURATE",
|
|
1900
|
+
"graphics.BLEND_FACTOR_CONSTANT_COLOR",
|
|
1901
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR",
|
|
1902
|
+
"graphics.BLEND_FACTOR_CONSTANT_ALPHA",
|
|
1903
|
+
"graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA",
|
|
1904
|
+
],
|
|
1905
|
+
},
|
|
1480
1906
|
{
|
|
1481
1907
|
home: "graphics",
|
|
1482
1908
|
name: "BufferType",
|
|
@@ -1690,6 +2116,12 @@ export const CONSTANT_UNION_ALIASES: readonly ConstantUnionAlias[] = [
|
|
|
1690
2116
|
"graphics.BUFFER_TYPE_STENCIL_BIT",
|
|
1691
2117
|
],
|
|
1692
2118
|
},
|
|
2119
|
+
// The render-target creator raises on any other texture type.
|
|
2120
|
+
{
|
|
2121
|
+
home: "render",
|
|
2122
|
+
name: "RenderTargetTextureType",
|
|
2123
|
+
members: ["graphics.TEXTURE_TYPE_2D", "graphics.TEXTURE_TYPE_CUBE_MAP"],
|
|
2124
|
+
},
|
|
1693
2125
|
{
|
|
1694
2126
|
home: "sys",
|
|
1695
2127
|
name: "NetworkConnectivity",
|
|
@@ -1801,9 +2233,14 @@ function constantSlotTokens(
|
|
|
1801
2233
|
return documentedConstantTokens(entry.family, universe);
|
|
1802
2234
|
}
|
|
1803
2235
|
|
|
1804
|
-
function
|
|
2236
|
+
// A borrow reads the ref-doc, so a slot of a function the skip rules withheld
|
|
2237
|
+
// (`gui.animate`, which gui-overloads.d.ts declares) still lends its doc.
|
|
2238
|
+
function slotDocLookup(
|
|
2239
|
+
module: ApiModule,
|
|
2240
|
+
withheldFunctions: readonly ApiFunction[] = [],
|
|
2241
|
+
): SlotDocLookup {
|
|
1805
2242
|
const docs = new Map<string, string>();
|
|
1806
|
-
for (const fn of module.functions) {
|
|
2243
|
+
for (const fn of [...module.functions, ...withheldFunctions]) {
|
|
1807
2244
|
for (const p of fn.parameters) {
|
|
1808
2245
|
const key = tableSlotKey(fn.name, "param", p.name);
|
|
1809
2246
|
if (!docs.has(key)) docs.set(key, p.doc);
|
|
@@ -1823,8 +2260,9 @@ export function resolveConstantSlotTokens(
|
|
|
1823
2260
|
module: ApiModule,
|
|
1824
2261
|
slotKey: string,
|
|
1825
2262
|
universe: ReadonlySet<string>,
|
|
2263
|
+
withheldFunctions: readonly ApiFunction[] = [],
|
|
1826
2264
|
): string[] {
|
|
1827
|
-
const lookupDoc = slotDocLookup(module);
|
|
2265
|
+
const lookupDoc = slotDocLookup(module, withheldFunctions);
|
|
1828
2266
|
return constantSlotTokens(lookupDoc(slotKey) ?? "", slotKey, lookupDoc, universe);
|
|
1829
2267
|
}
|
|
1830
2268
|
|
|
@@ -1838,21 +2276,23 @@ type ConstantSlotTokens = (
|
|
|
1838
2276
|
function constantSlotTokenResolver(
|
|
1839
2277
|
module: ApiModule,
|
|
1840
2278
|
universe: ReadonlySet<string>,
|
|
2279
|
+
withheldFunctions: readonly ApiFunction[] = [],
|
|
1841
2280
|
): ConstantSlotTokens {
|
|
1842
|
-
const lookupDoc = slotDocLookup(module);
|
|
2281
|
+
const lookupDoc = slotDocLookup(module, withheldFunctions);
|
|
1843
2282
|
return (elementName, slotKind, slotName, doc) =>
|
|
1844
2283
|
constantSlotTokens(doc, tableSlotKey(elementName, slotKind, slotName), lookupDoc, universe);
|
|
1845
2284
|
}
|
|
1846
2285
|
|
|
1847
|
-
// FQN-keyed allowlist of the
|
|
1848
|
-
//
|
|
1849
|
-
//
|
|
1850
|
-
//
|
|
1851
|
-
//
|
|
1852
|
-
// `
|
|
1853
|
-
//
|
|
1854
|
-
//
|
|
1855
|
-
//
|
|
2286
|
+
// FQN-keyed allowlist of the checks that genuinely narrow their argument, mapped
|
|
2287
|
+
// to the `DEFOLD_TYPE_MAP` token whose type they prove: the `types.is_*` checks,
|
|
2288
|
+
// and `bullet3d.collision_object.is_rigid_body`, whose binding is a
|
|
2289
|
+
// `btRigidBody::upcast` test. Emitting these as user-defined type guards
|
|
2290
|
+
// (`var_ is Vector3`) is the only way to narrow an engine userdata value, which
|
|
2291
|
+
// `typeof` cannot. An allowlist, not a blanket `is_*` match: `b2d.body.is_active`,
|
|
2292
|
+
// `gui.is_enabled`, `sound.is_music_playing`, `go.exists`, `sys.exists` are
|
|
2293
|
+
// runtime-state / existence checks on already-typed arguments and must stay
|
|
2294
|
+
// `boolean`. An unlisted future `types.is_*` also stays a visible `boolean` until
|
|
2295
|
+
// curated here, matching the ARBITRARY_TABLE_SLOTS deliberate-opt-in precedent.
|
|
1856
2296
|
export const TYPE_PREDICATES: ReadonlyMap<string, string> = new Map([
|
|
1857
2297
|
["types.is_hash", "hash"],
|
|
1858
2298
|
["types.is_matrix4", "matrix4"],
|
|
@@ -1861,13 +2301,47 @@ export const TYPE_PREDICATES: ReadonlyMap<string, string> = new Map([
|
|
|
1861
2301
|
["types.is_vector", "vector"],
|
|
1862
2302
|
["types.is_vector3", "vector3"],
|
|
1863
2303
|
["types.is_vector4", "vector4"],
|
|
2304
|
+
["bullet3d.collision_object.is_rigid_body", "btRigidBody"],
|
|
1864
2305
|
]);
|
|
1865
2306
|
|
|
2307
|
+
// The merged ref-doc gives each Box2D backend's form of these queries one
|
|
2308
|
+
// parameter list, so the first (v2) arm always wins and a v3 hit is unreachable.
|
|
2309
|
+
// A type parameter tells them apart: v2/script_box2d_world_v2.cpp:PushCastHit
|
|
2310
|
+
// sets `fixture` and `shape` to one PushFixtureInfo table, while
|
|
2311
|
+
// v3/script_box2d_world_v3.cpp:PushCastHit sets `shape` alone from
|
|
2312
|
+
// PushShapeInfoForShape. The v2 arm is declared first, so a call with no type
|
|
2313
|
+
// argument keeps the v2 hits; its `= "v2"` default only states that. Keyed by
|
|
2314
|
+
// `box2dBackendArmKey`.
|
|
2315
|
+
export const BOX2D_BACKEND_ARMS: ReadonlyMap<string, Box2dBackend> = new Map<string, Box2dBackend>([
|
|
2316
|
+
["b2d.world.cast_ray:b2d.fixture_cast_hit[]", "v2"],
|
|
2317
|
+
["b2d.world.cast_ray:b2d.shape_cast_hit[]", "v3"],
|
|
2318
|
+
["b2d.world.cast_ray_closest:b2d.fixture_cast_hit", "v2"],
|
|
2319
|
+
["b2d.world.cast_ray_closest:b2d.shape_cast_hit", "v3"],
|
|
2320
|
+
["b2d.world.cast_shape:b2d.fixture_cast_hit[]", "v2"],
|
|
2321
|
+
["b2d.world.cast_shape:b2d.shape_cast_hit[]", "v3"],
|
|
2322
|
+
["b2d.world.overlap_aabb:b2d.fixture_info[]", "v2"],
|
|
2323
|
+
["b2d.world.overlap_aabb:b2d.shape_info[]", "v3"],
|
|
2324
|
+
["b2d.world.overlap_shape:b2d.fixture_info[]", "v2"],
|
|
2325
|
+
["b2d.world.overlap_shape:b2d.shape_info[]", "v3"],
|
|
2326
|
+
]);
|
|
2327
|
+
|
|
2328
|
+
// `<fqn>:<first non-nil type of the first return>`: the two arms of each
|
|
2329
|
+
// `BOX2D_BACKEND_ARMS` query share every parameter and differ only there.
|
|
2330
|
+
export function box2dBackendArmKey(fn: ApiFunction): string {
|
|
2331
|
+
const first = fn.returnValues[0]?.types.find((type) => type !== "nil") ?? "";
|
|
2332
|
+
return `${fn.name}:${first}`;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
function box2dBackendTypeParameters(fn: ApiFunction): string {
|
|
2336
|
+
const backend = BOX2D_BACKEND_ARMS.get(box2dBackendArmKey(fn));
|
|
2337
|
+
if (backend === undefined) return "";
|
|
2338
|
+
return backend === "v2" ? '<Backend extends "v2" = "v2">' : '<Backend extends "v3">';
|
|
2339
|
+
}
|
|
2340
|
+
|
|
1866
2341
|
// Element names whose `table` slot is a prose-only `array/list/table of <T>` shape
|
|
1867
2342
|
// the field-list parser cannot read, but whose element type a human curated from
|
|
1868
2343
|
// the doc. The value is a single element token (`T[]`) or a token list when the
|
|
1869
|
-
// element is itself a union
|
|
1870
|
-
// `string | hash | url`, emitted `(string | Hash | Url)[]`. Emitted as a plain
|
|
2344
|
+
// element is itself a union, emitted `(A | B)[]`. Emitted as a plain
|
|
1871
2345
|
// array, never `LuaMap`: an array element carries no illegal-index-key problem so
|
|
1872
2346
|
// no TSTL wiring is needed. The audit consults this map too, so the gate and the
|
|
1873
2347
|
// emitted surface stay coupled; a slot not curated here still surfaces under
|
|
@@ -1882,7 +2356,6 @@ export const HOMOGENEOUS_ARRAY_SLOTS: ReadonlyMap<string, string | readonly stri
|
|
|
1882
2356
|
["vmath.vector", "number"],
|
|
1883
2357
|
["sound.get_groups", "hash"],
|
|
1884
2358
|
["iap.list", "string"],
|
|
1885
|
-
["go.delete", ["string", "hash", "url"]],
|
|
1886
2359
|
// push.register's `notifications` is a prose-only array of push.NOTIFICATION_*
|
|
1887
2360
|
// bitmask constants (the ref example sums NOTIFICATION_BADGE/SOUND/ALERT). The
|
|
1888
2361
|
// vendored push_doc.json fixture declares no NOTIFICATION_* constant elements, so
|
|
@@ -1905,7 +2378,11 @@ export type NestedMapping = { key: string; value: string };
|
|
|
1905
2378
|
// for `collectionfactory.create`'s properties.
|
|
1906
2379
|
export type TableSlotCuration = (
|
|
1907
2380
|
| { kind: "mapping"; key: string; value: string | readonly TableField[] | NestedMapping }
|
|
1908
|
-
|
|
2381
|
+
// `pairs` also admits any table whose values are the element, for a binding
|
|
2382
|
+
// that walks the table with lua_next and reads only its values. The extra arm
|
|
2383
|
+
// is `LuaPairsIterable`, which is covariant in its value; `LuaTable` is
|
|
2384
|
+
// invariant through `set`, so it would reject a `LuaTable<Hash, Hash>`.
|
|
2385
|
+
| { kind: "array"; element: string | readonly string[]; pairs?: true }
|
|
1909
2386
|
| { kind: "object"; fields: readonly TableField[] }
|
|
1910
2387
|
| { kind: "array-object"; fields: readonly TableField[] }
|
|
1911
2388
|
// A `<key> -> <args table>` slot: the doc's `<dl>` describes the *value* shape,
|
|
@@ -2082,25 +2559,6 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2082
2559
|
string,
|
|
2083
2560
|
TableSlotCuration
|
|
2084
2561
|
>([
|
|
2085
|
-
// gui_script.cpp:LuaGet and LuaSet read the `key` and `keys` options. On a
|
|
2086
|
-
// prose `table` slot TABLE_SLOT_FIELD_ADDITIONS supplies them; 1.13.2 types the
|
|
2087
|
-
// slots as an inline record and a struct that omit them.
|
|
2088
|
-
...(
|
|
2089
|
-
[
|
|
2090
|
-
["gui.get:param:options", "{ index?:integer }"],
|
|
2091
|
-
["gui.set:param:options", "gui.set_options"],
|
|
2092
|
-
] as const
|
|
2093
|
-
).map(
|
|
2094
|
-
([key, retyped]) =>
|
|
2095
|
-
[
|
|
2096
|
-
key,
|
|
2097
|
-
{
|
|
2098
|
-
kind: "verbatim",
|
|
2099
|
-
ts: "{ index?: number; key?: string | Hash; keys?: (Hash | string)[] }",
|
|
2100
|
-
retypedUpstream: [retyped],
|
|
2101
|
-
},
|
|
2102
|
-
] as const,
|
|
2103
|
-
),
|
|
2104
2562
|
// buffer.create's `declaration` is "a table where each entry (table) describes
|
|
2105
2563
|
// a stream", and upstream's example passes a list of stream records whose
|
|
2106
2564
|
// `type` is a buffer.VALUE_TYPE_* constant.
|
|
@@ -2128,6 +2586,19 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2128
2586
|
},
|
|
2129
2587
|
],
|
|
2130
2588
|
["collectionfactory.create:return:ids", { kind: "mapping", key: "hash", value: "hash" }],
|
|
2589
|
+
// go.delete's "table of id's": gameobject_script.cpp:DeleteGOTable walks any
|
|
2590
|
+
// table with lua_next and resolves each value with ResolveURL, so the id table
|
|
2591
|
+
// collectionfactory.create returns deletes as it is. 1.13.2 retypes the slot
|
|
2592
|
+
// to an id array, which still rejects that table.
|
|
2593
|
+
[
|
|
2594
|
+
"go.delete:param:id",
|
|
2595
|
+
{
|
|
2596
|
+
kind: "array",
|
|
2597
|
+
element: ["string", "hash", "url"],
|
|
2598
|
+
pairs: true,
|
|
2599
|
+
retypedUpstream: ["(string|hash|url)[]"],
|
|
2600
|
+
},
|
|
2601
|
+
],
|
|
2131
2602
|
// font.get_info's `info` return is a `<dl>` with `path: hash` and a nested
|
|
2132
2603
|
// `fonts` field whose `<dd>` is itself a `<dl>` (`path: string`, `path_hash:
|
|
2133
2604
|
// hash`). parseTableFields flattens the inner `<dl>` to the top level, which
|
|
@@ -2385,62 +2856,6 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2385
2856
|
value: "number | vector4",
|
|
2386
2857
|
},
|
|
2387
2858
|
],
|
|
2388
|
-
// go.on_input and gui.on_input share the same well-known InputAction shape
|
|
2389
|
-
// (the on_input function description lists the well-known fields: value,
|
|
2390
|
-
// pressed, released, repeated, x/y/screen_x/screen_y/dx/dy/screen_dx/screen_dy,
|
|
2391
|
-
// plus gamepad/touch/text multi-touch fields). The doc markup carries no
|
|
2392
|
-
// field list of its own, so the shape is curated verbatim from the prose.
|
|
2393
|
-
// Param-side optional fields: each input kind (mouse/key/text/gamepad) sets a
|
|
2394
|
-
// different subset, so the call site always sees an `?` on every field.
|
|
2395
|
-
// `touch` stays `table` (the nested multi-touch table has no machine-readable
|
|
2396
|
-
// field list; mirrors the render.set_render_target.transient recursive case).
|
|
2397
|
-
// The element is the bare `on_input` script hook (no namespace prefix), the
|
|
2398
|
-
// same form the `on_message` arbitrary-table entry uses. The doc carries a
|
|
2399
|
-
// well-formed touch input table, so `touch` is curated as a nested
|
|
2400
|
-
// array-of-touch-records (the field has its own `<table>` listing id,
|
|
2401
|
-
// pressed, released, tap_count, x, y, dx, dy, acc_x/y/z).
|
|
2402
|
-
[
|
|
2403
|
-
"on_input:param:action",
|
|
2404
|
-
{
|
|
2405
|
-
kind: "object",
|
|
2406
|
-
fields: [
|
|
2407
|
-
{ name: "value", types: ["number"], optional: true },
|
|
2408
|
-
{ name: "pressed", types: ["boolean"], optional: true },
|
|
2409
|
-
{ name: "released", types: ["boolean"], optional: true },
|
|
2410
|
-
{ name: "repeated", types: ["boolean"], optional: true },
|
|
2411
|
-
{ name: "x", types: ["number"], optional: true },
|
|
2412
|
-
{ name: "y", types: ["number"], optional: true },
|
|
2413
|
-
{ name: "screen_x", types: ["number"], optional: true },
|
|
2414
|
-
{ name: "screen_y", types: ["number"], optional: true },
|
|
2415
|
-
{ name: "dx", types: ["number"], optional: true },
|
|
2416
|
-
{ name: "dy", types: ["number"], optional: true },
|
|
2417
|
-
{ name: "screen_dx", types: ["number"], optional: true },
|
|
2418
|
-
{ name: "screen_dy", types: ["number"], optional: true },
|
|
2419
|
-
{ name: "gamepad", types: ["number"], optional: true },
|
|
2420
|
-
{ name: "gamepad_axis", types: ["vector3"], optional: true },
|
|
2421
|
-
{
|
|
2422
|
-
name: "touch",
|
|
2423
|
-
types: ["table"],
|
|
2424
|
-
optional: true,
|
|
2425
|
-
isList: true,
|
|
2426
|
-
fields: [
|
|
2427
|
-
{ name: "id", types: ["number"] },
|
|
2428
|
-
{ name: "pressed", types: ["boolean"] },
|
|
2429
|
-
{ name: "released", types: ["boolean"] },
|
|
2430
|
-
{ name: "tap_count", types: ["number"] },
|
|
2431
|
-
{ name: "x", types: ["number"] },
|
|
2432
|
-
{ name: "y", types: ["number"] },
|
|
2433
|
-
{ name: "dx", types: ["number"] },
|
|
2434
|
-
{ name: "dy", types: ["number"] },
|
|
2435
|
-
{ name: "acc_x", types: ["number"] },
|
|
2436
|
-
{ name: "acc_y", types: ["number"] },
|
|
2437
|
-
{ name: "acc_z", types: ["number"] },
|
|
2438
|
-
],
|
|
2439
|
-
},
|
|
2440
|
-
{ name: "text", types: ["string"], optional: true },
|
|
2441
|
-
],
|
|
2442
|
-
},
|
|
2443
|
-
],
|
|
2444
2859
|
// physics.create_joint and physics.set_joint_properties both declare
|
|
2445
2860
|
// `properties` as a joint-type-specific table; the only universal field is
|
|
2446
2861
|
// `collide_connected` (parseTableFields already recovers it from the
|
|
@@ -2932,7 +3347,9 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
2932
3347
|
// no `status`; typing it as its own shape needs a per-target authored overload,
|
|
2933
3348
|
// so `status` stays required and the slot's `note` says how to tell the two
|
|
2934
3349
|
// apart. Its typed form is the same signature: the typed reference names
|
|
2935
|
-
// `http.response`, which the runtime surface withholds.
|
|
3350
|
+
// `http.response`, which the runtime surface withholds. Both forms add the
|
|
3351
|
+
// `sender` no reference lists: comp_script.cpp delivers the response as a
|
|
3352
|
+
// message, with `PushURL(L, message->m_Sender)` then `PCall(L, 4, 0)`.
|
|
2936
3353
|
// `model.play_anim`'s completion callback receives self, the message id and the
|
|
2937
3354
|
// message only: script_model.cpp calls it with `PCall(L, 3, 0)`, so both forms
|
|
2938
3355
|
// drop the `sender` every reference lists and type the message as the typed
|
|
@@ -2979,7 +3396,7 @@ export const CALLBACK_SIGNATURE_CURATIONS: ReadonlyMap<string, CallbackSignature
|
|
|
2979
3396
|
[
|
|
2980
3397
|
"http.request:param:callback",
|
|
2981
3398
|
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",
|
|
3399
|
+
"(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 }, sender: Url) => void",
|
|
2983
3400
|
"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
3401
|
),
|
|
2985
3402
|
],
|
|
@@ -3064,11 +3481,11 @@ export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
3064
3481
|
["resource.set_atlas:param:table:animations[].playback", "go.Playback"],
|
|
3065
3482
|
["resource.create_atlas:param:table:animations[].playback", "go.Playback"],
|
|
3066
3483
|
["resource.get_atlas:return:data:animations[].playback", "go.Playback"],
|
|
3067
|
-
["gui.set:param:options:key", "string | Hash"],
|
|
3068
3484
|
["render.set_render_target:param:options:transient", "graphics.BufferType[]"],
|
|
3069
3485
|
["resource.create_sound_data:param:options:data", 'string | Opaque<"buffer">'],
|
|
3070
|
-
// The
|
|
3071
|
-
//
|
|
3486
|
+
// The option bags 1.13.2 types as a STRUCT, whose members still carry the
|
|
3487
|
+
// narrower upstream types: `gui.set`'s (its slot is declared in
|
|
3488
|
+
// gui-overloads.d.ts) and `resource.create_sound_data`'s.
|
|
3072
3489
|
["gui.set_options:member:key", "string | Hash"],
|
|
3073
3490
|
["resource.sound_data_options:member:data", 'string | Opaque<"buffer">'],
|
|
3074
3491
|
// Bitmasks typed as their flag enum: the create_texture example passes
|
|
@@ -3083,6 +3500,11 @@ export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
3083
3500
|
] as const
|
|
3084
3501
|
).map((struct) => [`${struct}:member:flags`, "graphics.TEXTURE_USAGE_FLAG | number"] as const),
|
|
3085
3502
|
["render.render_target_buffer_params:member:flags", "render.RENDER_TARGET_FLAG | number"],
|
|
3503
|
+
// gamesys_script.cpp:PushSampler, behind both get_samplers, pushes the raw
|
|
3504
|
+
// texture type; NormalizeBindableTextureType never runs there. A separate
|
|
3505
|
+
// sampler reports TEXTURE_TYPE_SAMPLER (6) and a separate texture binding one
|
|
3506
|
+
// of the four TEXTURE_TYPE_TEXTURE_* (7-10), none of which has a Lua constant.
|
|
3507
|
+
["material.sampler_info:member:type", "graphics.TEXTURE_TYPE | 6 | 7 | 8 | 9 | 10"],
|
|
3086
3508
|
]);
|
|
3087
3509
|
|
|
3088
3510
|
// A field the engine accepts on an option bag whose `<dl>` upstream otherwise
|
|
@@ -3102,13 +3524,6 @@ export interface TableFieldAddition {
|
|
|
3102
3524
|
readonly evidence: readonly string[];
|
|
3103
3525
|
}
|
|
3104
3526
|
|
|
3105
|
-
const PROPERTY_OPTION_KEYS: TableField = {
|
|
3106
|
-
name: "keys",
|
|
3107
|
-
types: ["table"],
|
|
3108
|
-
optional: true,
|
|
3109
|
-
tsType: "(Hash | string)[]",
|
|
3110
|
-
};
|
|
3111
|
-
|
|
3112
3527
|
export const TABLE_SLOT_FIELD_ADDITIONS: ReadonlyMap<string, TableFieldAddition> = new Map<
|
|
3113
3528
|
string,
|
|
3114
3529
|
TableFieldAddition
|
|
@@ -3123,24 +3538,6 @@ export const TABLE_SLOT_FIELD_ADDITIONS: ReadonlyMap<string, TableFieldAddition>
|
|
|
3123
3538
|
],
|
|
3124
3539
|
},
|
|
3125
3540
|
],
|
|
3126
|
-
[
|
|
3127
|
-
"gui.get:param:options",
|
|
3128
|
-
{
|
|
3129
|
-
fields: [{ name: "key", types: ["string", "hash"], optional: true }, PROPERTY_OPTION_KEYS],
|
|
3130
|
-
evidence: [
|
|
3131
|
-
"gui_script.cpp:LuaGet reads its options through dmGameObject::LuaToPropertyOptions, which reads index, key and keys",
|
|
3132
|
-
],
|
|
3133
|
-
},
|
|
3134
|
-
],
|
|
3135
|
-
[
|
|
3136
|
-
"gui.set:param:options",
|
|
3137
|
-
{
|
|
3138
|
-
fields: [PROPERTY_OPTION_KEYS],
|
|
3139
|
-
evidence: [
|
|
3140
|
-
"gui_script.cpp:LuaSet reads its options through dmGameObject::LuaToPropertyOptions, which reads index, key and keys",
|
|
3141
|
-
],
|
|
3142
|
-
},
|
|
3143
|
-
],
|
|
3144
3541
|
]);
|
|
3145
3542
|
|
|
3146
3543
|
// A return-slot field upstream documents as platform-gated or nil-valued while
|
|
@@ -3890,6 +4287,58 @@ function groupFlattenedTableFields(fields: readonly TableField[]): TableField[]
|
|
|
3890
4287
|
return grouped;
|
|
3891
4288
|
}
|
|
3892
4289
|
|
|
4290
|
+
// A TYPEDEF whose aliased type the engine binding contradicts, replaced verbatim.
|
|
4291
|
+
// Member overrides reach only a STRUCT, and these alias inline LuaLS tables.
|
|
4292
|
+
// Each applies only while the typedef still aliases exactly its `upstream` tokens,
|
|
4293
|
+
// in order, so a changed upstream typedef is emitted as written rather than
|
|
4294
|
+
// shadowed; `typedef-correction-provenance.test.ts` pins every entry against the
|
|
4295
|
+
// retained ref-docs. Keyed by the typedef's ref-doc name.
|
|
4296
|
+
export interface TypedefAliasCorrection {
|
|
4297
|
+
readonly upstream: readonly string[];
|
|
4298
|
+
readonly ts: string;
|
|
4299
|
+
readonly reason: string;
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
// One arm per shape a script can build, in upstream's order, each keyed by its
|
|
4303
|
+
// own shape-type constant.
|
|
4304
|
+
const BULLET3D_SHAPE_ARMS = [
|
|
4305
|
+
["SPHERE", "diameter: number"],
|
|
4306
|
+
["BOX", "dimensions: Vector3"],
|
|
4307
|
+
["CAPSULE", "diameter: number; height: number"],
|
|
4308
|
+
["HULL", "vertices: Vector3[]"],
|
|
4309
|
+
] as const;
|
|
4310
|
+
|
|
4311
|
+
export const TYPEDEF_ALIAS_CORRECTIONS: ReadonlyMap<string, TypedefAliasCorrection> = new Map([
|
|
4312
|
+
[
|
|
4313
|
+
"render.render_target_params",
|
|
4314
|
+
{
|
|
4315
|
+
upstream: [
|
|
4316
|
+
"{ type?:graphics.TEXTURE_TYPE, sample_count?:integer, [graphics.BUFFER_TYPE]:render.render_target_buffer_params }",
|
|
4317
|
+
],
|
|
4318
|
+
ts: "{ type?: render.RenderTargetTextureType; sample_count?: number } & { [key: number]: render.render_target_buffer_params }",
|
|
4319
|
+
reason:
|
|
4320
|
+
'render_script.cpp:RenderScript_RenderTarget raises "Invalid render target texture type" on any `type` but graphics.TEXTURE_TYPE_2D and graphics.TEXTURE_TYPE_CUBE_MAP',
|
|
4321
|
+
},
|
|
4322
|
+
],
|
|
4323
|
+
[
|
|
4324
|
+
"bullet3d.shape.definition",
|
|
4325
|
+
{
|
|
4326
|
+
upstream: [
|
|
4327
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, diameter:number, position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4328
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, dimensions:vector3, position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4329
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, diameter:number, height:number, position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4330
|
+
"{ type:bullet3d.shape.SHAPE_TYPE, vertices:vector3[], position?:vector3, rotation?:quaternion, target_rotation?:quaternion }",
|
|
4331
|
+
],
|
|
4332
|
+
ts: BULLET3D_SHAPE_ARMS.map(
|
|
4333
|
+
([kind, fields]) =>
|
|
4334
|
+
`{ type: typeof bullet3d.shape.SHAPE_TYPE_${kind}; ${fields}; position?: Vector3; rotation?: Quaternion; target_rotation?: Quaternion }`,
|
|
4335
|
+
).join(" | "),
|
|
4336
|
+
reason:
|
|
4337
|
+
'script_bullet3d_shape.cpp:CheckBullet3DShapeDef reads only the fields of the table\'s own `type` and raises "Unsupported shape type" on any other, a mesh included',
|
|
4338
|
+
},
|
|
4339
|
+
],
|
|
4340
|
+
]);
|
|
4341
|
+
|
|
3893
4342
|
// A handle the type map widens (`constant_buffer` takes named constants) keeps
|
|
3894
4343
|
// that shape, so annotating with the alias loses nothing the handle-returning
|
|
3895
4344
|
// function yields.
|
|
@@ -3906,6 +4355,14 @@ function typedefAlias(
|
|
|
3906
4355
|
): string {
|
|
3907
4356
|
const brand = `Opaque<"${typedef.name}">`;
|
|
3908
4357
|
const aliasOf = typedef.aliasOf ?? [];
|
|
4358
|
+
const correction = TYPEDEF_ALIAS_CORRECTIONS.get(typedef.name);
|
|
4359
|
+
if (
|
|
4360
|
+
correction !== undefined &&
|
|
4361
|
+
aliasOf.length === correction.upstream.length &&
|
|
4362
|
+
aliasOf.every((token, i) => token === correction.upstream[i])
|
|
4363
|
+
) {
|
|
4364
|
+
return `type ${local} = ${correction.ts};`;
|
|
4365
|
+
}
|
|
3909
4366
|
if (aliasOf.length > 0 && !isHandleAlias(aliasOf)) {
|
|
3910
4367
|
const armType: DefoldTypeMapper = DISCRIMINATED_MESSAGE_UNIONS.has(typedef.name)
|
|
3911
4368
|
? (token, position) => discriminatedMessageArm(token) ?? mapType(token, position)
|
|
@@ -4248,11 +4705,13 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4248
4705
|
const translations = options?.translations ?? {};
|
|
4249
4706
|
const urlParameters = options?.urlParameters ?? [];
|
|
4250
4707
|
const bindingEvidence = options?.bindingEvidence ?? new Set<string>();
|
|
4708
|
+
const nilRaisingSlots = options?.nilRaisingSlots;
|
|
4251
4709
|
const baseNotes = indexBaseNoteResolver(options?.indexBaseSource ?? "engine");
|
|
4252
4710
|
const { rawMapType, mapType } = moduleTypeMappers(module, constantsByFqn, options);
|
|
4253
4711
|
const constantTokens = constantSlotTokenResolver(
|
|
4254
4712
|
module,
|
|
4255
4713
|
new Set([...constantsByFqn.keys(), ...(knownConstantFqns ?? [])]),
|
|
4714
|
+
options?.withheldFunctions,
|
|
4256
4715
|
);
|
|
4257
4716
|
|
|
4258
4717
|
const constants = module.constants
|
|
@@ -4385,18 +4844,20 @@ export function emitDeclarations(documented: ApiModule, options?: EmitOptions):
|
|
|
4385
4844
|
for (const fn of functions) {
|
|
4386
4845
|
const reserved = TS_RESERVED_NAMES.has(fn.name);
|
|
4387
4846
|
const emitName = aliasName(fn.name, aliases);
|
|
4388
|
-
for (const
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4847
|
+
for (const arm of callArms(fn, nilRaisingSlots, module.structs ?? [])) {
|
|
4848
|
+
for (const docLine of functionDocLines(fn.original, translations, baseNotes))
|
|
4849
|
+
lines.push(docLine);
|
|
4850
|
+
const line = emitFunction(
|
|
4851
|
+
arm,
|
|
4852
|
+
emitName,
|
|
4853
|
+
mapType,
|
|
4854
|
+
resolver,
|
|
4855
|
+
constantTokens,
|
|
4856
|
+
urlParameters,
|
|
4857
|
+
bindingEvidence,
|
|
4858
|
+
);
|
|
4859
|
+
lines.push(`${INDENT}${reserved ? "" : decl}${line}`);
|
|
4860
|
+
}
|
|
4400
4861
|
const alternative = firstSlotAlternative(fn, module.namespace);
|
|
4401
4862
|
if (alternative !== null) {
|
|
4402
4863
|
// The overload documents its own parameters, so its hover names the base
|
|
@@ -4620,10 +5081,12 @@ export function emitSymbolSignatures(
|
|
|
4620
5081
|
const knownConstantFqns = options?.knownConstantFqns;
|
|
4621
5082
|
const urlParameters = options?.urlParameters ?? [];
|
|
4622
5083
|
const bindingEvidence = options?.bindingEvidence ?? new Set<string>();
|
|
5084
|
+
const nilRaisingSlots = options?.nilRaisingSlots;
|
|
4623
5085
|
const { rawMapType, mapType } = moduleTypeMappers(module, constantsByFqn, options);
|
|
4624
5086
|
const constantTokens = constantSlotTokenResolver(
|
|
4625
5087
|
module,
|
|
4626
5088
|
new Set([...constantsByFqn.keys(), ...(knownConstantFqns ?? [])]),
|
|
5089
|
+
options?.withheldFunctions,
|
|
4627
5090
|
);
|
|
4628
5091
|
const resolver = buildTableDocResolver(
|
|
4629
5092
|
module.functions.map((fn) => ({
|
|
@@ -4650,8 +5113,9 @@ export function emitSymbolSignatures(
|
|
|
4650
5113
|
const prepared = prepareFunction(fn, prefix);
|
|
4651
5114
|
if (prepared === null) continue;
|
|
4652
5115
|
const slotTypes: Record<string, SlotType> = {};
|
|
5116
|
+
const [first = prepared, ...later] = callArms(prepared, nilRaisingSlots, module.structs ?? []);
|
|
4653
5117
|
const primary = emitFunction(
|
|
4654
|
-
|
|
5118
|
+
first,
|
|
4655
5119
|
emitName(prepared.name),
|
|
4656
5120
|
mapType,
|
|
4657
5121
|
resolver,
|
|
@@ -4661,18 +5125,18 @@ export function emitSymbolSignatures(
|
|
|
4661
5125
|
slotTypes,
|
|
4662
5126
|
);
|
|
4663
5127
|
const alternative = firstSlotAlternative(prepared, module.namespace);
|
|
4664
|
-
const
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
5128
|
+
const arms = [...later, ...(alternative === null ? [] : [alternative])].map((arm) =>
|
|
5129
|
+
emitFunction(
|
|
5130
|
+
arm,
|
|
5131
|
+
emitName(prepared.name),
|
|
5132
|
+
mapType,
|
|
5133
|
+
resolver,
|
|
5134
|
+
constantTokens,
|
|
5135
|
+
urlParameters,
|
|
5136
|
+
bindingEvidence,
|
|
5137
|
+
),
|
|
5138
|
+
);
|
|
5139
|
+
const tsSignature = [primary, ...arms].join("\n");
|
|
4676
5140
|
out.push({ identity: fnIdentity(fn), tsSignature, slotTypes });
|
|
4677
5141
|
}
|
|
4678
5142
|
|
|
@@ -4829,6 +5293,12 @@ interface PreparedConstant {
|
|
|
4829
5293
|
interface PreparedFunction {
|
|
4830
5294
|
name: string;
|
|
4831
5295
|
original: ApiFunction;
|
|
5296
|
+
// Parameter positions declared required whatever their doc says: the
|
|
5297
|
+
// nil-raising slots an arm of `nilRaisingArms` takes.
|
|
5298
|
+
required?: ReadonlySet<number>;
|
|
5299
|
+
// A rewrite of the mapped type at a parameter position: the fields an arm of
|
|
5300
|
+
// `presenceArms` requires.
|
|
5301
|
+
retype?: ReadonlyMap<number, (type: string) => string>;
|
|
4832
5302
|
}
|
|
4833
5303
|
|
|
4834
5304
|
interface PreparedVariable {
|
|
@@ -5112,10 +5582,13 @@ export const NIL_CONSTANTS: ReadonlyMap<string, string> = new Map([
|
|
|
5112
5582
|
// `undocumented-constant-provenance.test.ts` reds an entry no committed target
|
|
5113
5583
|
// still needs. `sameValueAs` names the documented constant the engine registers
|
|
5114
5584
|
// with the same value; where the surface declares it, the back-fill takes its
|
|
5115
|
-
// type, so a value compares equal to either spelling.
|
|
5585
|
+
// type, so a value compares equal to either spelling. `memberOf` names the
|
|
5586
|
+
// ref-doc `ENUM` the engine returns the constant through; the back-fill joins it
|
|
5587
|
+
// only on a surface whose ref-doc declares that enum, so an older surface
|
|
5588
|
+
// without one keeps a plain constant.
|
|
5116
5589
|
export const UNDOCUMENTED_CONSTANTS: ReadonlyMap<
|
|
5117
5590
|
string,
|
|
5118
|
-
{ readonly doc: string; readonly sameValueAs?: string }
|
|
5591
|
+
{ readonly doc: string; readonly sameValueAs?: string; readonly memberOf?: string }
|
|
5119
5592
|
> = new Map([
|
|
5120
5593
|
[
|
|
5121
5594
|
"render.CONTEXT_EVENT_CONTEXT_LOST",
|
|
@@ -5136,6 +5609,13 @@ export const UNDOCUMENTED_CONSTANTS: ReadonlyMap<
|
|
|
5136
5609
|
sameValueAs: "window.WINDOW_EVENT_ICONIFIED",
|
|
5137
5610
|
},
|
|
5138
5611
|
],
|
|
5612
|
+
[
|
|
5613
|
+
"liveupdate.LIVEUPDATE_NOT_INITIALIZED",
|
|
5614
|
+
{
|
|
5615
|
+
doc: "Live update is not initialized. `liveupdate.add_mount` and `liveupdate.remove_mount` return it while live update is disabled.",
|
|
5616
|
+
memberOf: "liveupdate.LIVEUPDATE",
|
|
5617
|
+
},
|
|
5618
|
+
],
|
|
5139
5619
|
]);
|
|
5140
5620
|
|
|
5141
5621
|
function withUndocumentedConstants(
|
|
@@ -5145,7 +5625,8 @@ function withUndocumentedConstants(
|
|
|
5145
5625
|
const prefix = `${module.namespace}.`;
|
|
5146
5626
|
const documented = new Set(module.constants.map((c) => c.name));
|
|
5147
5627
|
const backfill: ApiConstant[] = [];
|
|
5148
|
-
|
|
5628
|
+
const joined = new Map<string, string[]>();
|
|
5629
|
+
for (const [fqn, { doc, sameValueAs, memberOf }] of UNDOCUMENTED_CONSTANTS) {
|
|
5149
5630
|
if (!fqn.startsWith(prefix) || fqn.slice(prefix.length).includes(".")) continue;
|
|
5150
5631
|
if (documented.has(fqn)) continue;
|
|
5151
5632
|
if (registered !== undefined && !registered.has(fqn)) continue;
|
|
@@ -5157,10 +5638,18 @@ function withUndocumentedConstants(
|
|
|
5157
5638
|
description: "",
|
|
5158
5639
|
...(aliasOf === undefined ? {} : { aliasOf }),
|
|
5159
5640
|
});
|
|
5641
|
+
if (memberOf !== undefined) joined.set(memberOf, [...(joined.get(memberOf) ?? []), fqn]);
|
|
5160
5642
|
}
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5643
|
+
if (backfill.length === 0) return module;
|
|
5644
|
+
const enums = module.enums?.map((e) => {
|
|
5645
|
+
const members = joined.get(e.name);
|
|
5646
|
+
return members === undefined ? e : { ...e, members: [...e.members, ...members] };
|
|
5647
|
+
});
|
|
5648
|
+
return {
|
|
5649
|
+
...module,
|
|
5650
|
+
constants: [...module.constants, ...backfill],
|
|
5651
|
+
...(enums === undefined ? {} : { enums }),
|
|
5652
|
+
};
|
|
5164
5653
|
}
|
|
5165
5654
|
|
|
5166
5655
|
// `constant` is the module's own element for `fqn`, absent for a constant another
|
|
@@ -5244,10 +5733,25 @@ function memberSignature(
|
|
|
5244
5733
|
): string {
|
|
5245
5734
|
const original = prepared.original.parameters;
|
|
5246
5735
|
const elementName = prepared.original.name;
|
|
5247
|
-
const
|
|
5736
|
+
const required = prepared.required ?? new Set<number>();
|
|
5737
|
+
const cutoff = Math.max(
|
|
5738
|
+
trailingOptionalCutoff(original, elementName),
|
|
5739
|
+
...[...required].map((index) => index + 1),
|
|
5740
|
+
);
|
|
5248
5741
|
const varargIndex = original.findIndex(isVarargParameter);
|
|
5249
5742
|
const positional = (varargIndex === -1 ? original : original.slice(0, varargIndex)).map((p, i) =>
|
|
5250
|
-
emitParameter(
|
|
5743
|
+
emitParameter(
|
|
5744
|
+
p,
|
|
5745
|
+
i,
|
|
5746
|
+
i >= cutoff,
|
|
5747
|
+
mapType,
|
|
5748
|
+
resolver,
|
|
5749
|
+
constantTokens,
|
|
5750
|
+
elementName,
|
|
5751
|
+
urlParameters,
|
|
5752
|
+
required.has(i),
|
|
5753
|
+
prepared.retype?.get(i),
|
|
5754
|
+
),
|
|
5251
5755
|
);
|
|
5252
5756
|
const rest =
|
|
5253
5757
|
varargIndex === -1
|
|
@@ -5296,7 +5800,7 @@ function memberSignature(
|
|
|
5296
5800
|
if (isPredicate && soleParam !== undefined && predicateToken !== undefined) {
|
|
5297
5801
|
return `${name}(${params}): ${safeParamName(soleParam.name, 0)} is ${mapType(predicateToken)};`;
|
|
5298
5802
|
}
|
|
5299
|
-
return `${name}(${params}): ${ret.type};${ret.trailing}`;
|
|
5803
|
+
return `${name}${box2dBackendTypeParameters(prepared.original)}(${params}): ${ret.type};${ret.trailing}`;
|
|
5300
5804
|
}
|
|
5301
5805
|
|
|
5302
5806
|
function emitFunction(
|
|
@@ -5367,6 +5871,7 @@ function functionDocLines(
|
|
|
5367
5871
|
};
|
|
5368
5872
|
});
|
|
5369
5873
|
const onlyReturn = fn.returnValues.length === 1 ? fn.returnValues[0] : undefined;
|
|
5874
|
+
const sparseNote = SPARSE_ARRAY_RETURNS.get(fn.name)?.note;
|
|
5370
5875
|
const notedReturns =
|
|
5371
5876
|
multiReturnDoc(fn, translations, baseNotes) ?? overrideReturnDoc(fn, baseNotes);
|
|
5372
5877
|
const lua = htmlToCodeText(fn.examples ?? "");
|
|
@@ -5399,7 +5904,10 @@ function functionDocLines(
|
|
|
5399
5904
|
? {
|
|
5400
5905
|
returns: appendNotes(
|
|
5401
5906
|
translateProseFences(htmlToDocText(onlyReturn.doc), fn.name, translations),
|
|
5402
|
-
|
|
5907
|
+
[
|
|
5908
|
+
...baseNotes(fn.name, "return", onlyReturn.name),
|
|
5909
|
+
...(sparseNote === undefined ? [] : [sparseNote]),
|
|
5910
|
+
],
|
|
5403
5911
|
),
|
|
5404
5912
|
}
|
|
5405
5913
|
: notedReturns !== undefined
|
|
@@ -5661,15 +6169,19 @@ function emitParameter(
|
|
|
5661
6169
|
constantTokens: ConstantSlotTokens,
|
|
5662
6170
|
elementName: string,
|
|
5663
6171
|
urlParameters: UrlParameterTable,
|
|
6172
|
+
required = false,
|
|
6173
|
+
retype: (type: string) => string = (type) => type,
|
|
5664
6174
|
): { text: string; type: string; optional: boolean } {
|
|
5665
6175
|
const name = safeParamName(p.name, index);
|
|
5666
|
-
const ts =
|
|
6176
|
+
const ts = retype(
|
|
6177
|
+
parameterType(p, mapType, resolver, constantTokens, elementName, urlParameters),
|
|
6178
|
+
);
|
|
5667
6179
|
// An interior doc-optional param (a required param follows, so the trailing-`?`
|
|
5668
6180
|
// projection cannot mark it) keeps its optionality as `| undefined` — TSTL
|
|
5669
6181
|
// lowers `undefined` to `nil`, the faithful call. Trailing optionals keep the
|
|
5670
6182
|
// `?` form; required params are untouched.
|
|
5671
6183
|
const nilRequired = requiredSlotCorrection(p, elementName)?.nil === true;
|
|
5672
|
-
const interiorOptional = !optional && (isDocOptional(p, elementName) || nilRequired);
|
|
6184
|
+
const interiorOptional = !optional && !required && (isDocOptional(p, elementName) || nilRequired);
|
|
5673
6185
|
// `(a) => void | undefined` reads as a function returning `void | undefined`.
|
|
5674
6186
|
const operand = interiorOptional && isFunctionTypeText(ts) ? `(${ts})` : ts;
|
|
5675
6187
|
const type = `${operand}${interiorOptional ? " | undefined" : ""}`;
|
|
@@ -5838,6 +6350,48 @@ function emitPropertyMembers(
|
|
|
5838
6350
|
return keys.map((key) => `${modifier}${key}: ${ts};`);
|
|
5839
6351
|
}
|
|
5840
6352
|
|
|
6353
|
+
// A function whose returned table the engine binding keys by a sparse integer,
|
|
6354
|
+
// so the sequence its reference declares is a map with holes: `ipairs` stops at
|
|
6355
|
+
// the first hole, and `#t` may return any border. Keyed by function name, since the return slot is named
|
|
6356
|
+
// `table` in 1.13.0/1.13.1 and `textures` from 1.13.2. The return's `T[]` member
|
|
6357
|
+
// emits as `LuaMap<number, T>`, which `for...of` lowers to `pairs`, and `note`
|
|
6358
|
+
// joins its `@returns`.
|
|
6359
|
+
export const SPARSE_ARRAY_RETURNS: ReadonlyMap<string, { note: string; reason: string }> = new Map([
|
|
6360
|
+
[
|
|
6361
|
+
"material.get_textures",
|
|
6362
|
+
{
|
|
6363
|
+
note: "Keyed by sampler slot from 1; a slot with no texture has no entry, so iterate with `for (const [slot, info] of textures)`.",
|
|
6364
|
+
reason:
|
|
6365
|
+
"script_material.cpp:Material_GetTextures pushes key i+1 only for a slot that has a texture, unlike Compute_GetTextures, which packs its keys",
|
|
6366
|
+
},
|
|
6367
|
+
],
|
|
6368
|
+
]);
|
|
6369
|
+
|
|
6370
|
+
function sparseArrayMap(ts: string, elementName: string): string {
|
|
6371
|
+
const members = unionMembers(ts);
|
|
6372
|
+
const arrays = members.filter((member) => member.endsWith("[]"));
|
|
6373
|
+
if (arrays.length === 0) {
|
|
6374
|
+
throw new Error(`SPARSE_ARRAY_RETURNS ${elementName}: the return emits no array (${ts})`);
|
|
6375
|
+
}
|
|
6376
|
+
return members
|
|
6377
|
+
.map((member) => {
|
|
6378
|
+
if (!member.endsWith("[]")) return member;
|
|
6379
|
+
const element = member.slice(0, -2);
|
|
6380
|
+
return `LuaMap<number, ${wrappedInParens(element) ? element.slice(1, -1) : element}>`;
|
|
6381
|
+
})
|
|
6382
|
+
.join(" | ");
|
|
6383
|
+
}
|
|
6384
|
+
|
|
6385
|
+
function wrappedInParens(text: string): boolean {
|
|
6386
|
+
if (!text.startsWith("(")) return false;
|
|
6387
|
+
let depth = 0;
|
|
6388
|
+
for (let i = 0; i < text.length; i++) {
|
|
6389
|
+
if (text[i] === "(") depth++;
|
|
6390
|
+
else if (text[i] === ")" && --depth === 0) return i === text.length - 1;
|
|
6391
|
+
}
|
|
6392
|
+
return false;
|
|
6393
|
+
}
|
|
6394
|
+
|
|
5841
6395
|
// Like `unionFromTokens`, but a `table` token whose slot doc carries a parseable
|
|
5842
6396
|
// `<dl>` field list emits an inline object type instead of the opaque `Record`
|
|
5843
6397
|
// fallback. Other tokens in the union map unchanged. `optionalFields` marks the
|
|
@@ -5918,6 +6472,10 @@ function mapSlotUnion(
|
|
|
5918
6472
|
}
|
|
5919
6473
|
} else if (element !== undefined) {
|
|
5920
6474
|
ts = arrayTypeFromTokens(element, mapType);
|
|
6475
|
+
if (curation?.kind === "array" && curation.pairs === true) {
|
|
6476
|
+
const tokens = typeof element === "string" ? [element] : element;
|
|
6477
|
+
ts = `${ts} | LuaPairsIterable<AnyNotNil, ${unionFromTokens(tokens, mapType)}>`;
|
|
6478
|
+
}
|
|
5921
6479
|
} else if (curation?.kind === "verbatim") {
|
|
5922
6480
|
ts = curation.ts;
|
|
5923
6481
|
} else if (curation?.kind === "object" || curation?.kind === "array-object") {
|
|
@@ -5991,8 +6549,10 @@ function mapSlotUnion(
|
|
|
5991
6549
|
// A genuine `unknown` member (an `any` slot, including `any | nil`) absorbs
|
|
5992
6550
|
// every other union member, so collapse to exactly `unknown` rather than emit
|
|
5993
6551
|
// a redundant `T | unknown` or `unknown | undefined`.
|
|
5994
|
-
|
|
5995
|
-
return
|
|
6552
|
+
const joined = mapped.includes("unknown") ? "unknown" : joinUnionArms(mapped);
|
|
6553
|
+
return slotKind === "return" && SPARSE_ARRAY_RETURNS.has(elementName)
|
|
6554
|
+
? sparseArrayMap(joined, elementName)
|
|
6555
|
+
: joined;
|
|
5996
6556
|
}
|
|
5997
6557
|
|
|
5998
6558
|
function tableSlotKey(elementName: string, slotKind: "param" | "return", slotName: string): string {
|