@defold-typescript/types 0.36.0 → 0.38.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 +42 -42
- package/examples/translations.json +5 -5
- package/generated/b2d.d.ts +1 -1
- package/generated/b2d_body.d.ts +6 -4
- package/generated/b2d_fixture.d.ts +6 -4
- package/generated/b2d_shape.d.ts +6 -4
- package/generated/resource.d.ts +4 -4
- package/generated/sys.d.ts +2 -2
- package/generated/versions/defold-1.12.4/b2d.d.ts +1 -1
- package/generated/versions/defold-1.12.4/resource.d.ts +4 -4
- package/generated/versions/defold-1.12.4/sys.d.ts +2 -2
- package/generated/versions/defold-1.12.4/window.d.ts +1 -1
- package/generated/versions/defold-1.13.0/b2d.d.ts +1 -1
- package/generated/versions/defold-1.13.0/b2d_body.d.ts +6 -4
- package/generated/versions/defold-1.13.0/b2d_fixture.d.ts +6 -4
- package/generated/versions/defold-1.13.0/b2d_shape.d.ts +6 -4
- package/generated/versions/defold-1.13.0/resource.d.ts +4 -4
- package/generated/versions/defold-1.13.0/sys.d.ts +2 -2
- package/generated/versions/defold-1.13.0/window.d.ts +1 -1
- package/generated/window.d.ts +1 -1
- package/package.json +1 -1
- package/src/emit-dts.ts +182 -8
- package/src/lifecycle.ts +31 -8
- package/src/message-dispatch.d.ts +55 -13
- package/src/msg-overloads.d.ts +73 -3
- package/src/return-correction-provenance.ts +306 -0
|
@@ -294,7 +294,7 @@ declare global {
|
|
|
294
294
|
* }
|
|
295
295
|
* ```
|
|
296
296
|
*/
|
|
297
|
-
function get_ifaddrs(): { name: string; address
|
|
297
|
+
function get_ifaddrs(): { name: string; address?: string; mac?: string; up: boolean; running: boolean }[];
|
|
298
298
|
/**
|
|
299
299
|
* The save-file path is operating system specific and is typically located under the user's home directory.
|
|
300
300
|
* This function will raise a Lua error if unable to get the save file path.
|
|
@@ -365,7 +365,7 @@ declare global {
|
|
|
365
365
|
* }
|
|
366
366
|
* ```
|
|
367
367
|
*/
|
|
368
|
-
function get_sys_info(options?: { ignore_secure?: boolean }): { device_model
|
|
368
|
+
function get_sys_info(options?: { ignore_secure?: boolean }): { device_model?: string; manufacturer?: string; system_name: string; system_version: string; api_version: string; language: string; device_language: string; territory: string; gmt_offset: number; device_ident?: string; user_agent?: string };
|
|
369
369
|
/**
|
|
370
370
|
* If the file exists, it must have been created by `sys.save` to be loaded.
|
|
371
371
|
* This function will raise a Lua error if an error occurs while loading the file.
|
|
@@ -95,7 +95,7 @@ declare global {
|
|
|
95
95
|
*
|
|
96
96
|
* - number `inset_bottom`
|
|
97
97
|
*/
|
|
98
|
-
function get_safe_area(): {
|
|
98
|
+
function get_safe_area(): { x: number; y: number; width: number; height: number; inset_left: number; inset_top: number; inset_right: number; inset_bottom: number };
|
|
99
99
|
/**
|
|
100
100
|
* This returns the current window size (width and height).
|
|
101
101
|
*/
|
|
@@ -14,7 +14,7 @@ declare global {
|
|
|
14
14
|
* @param url - the url to the game object collision component
|
|
15
15
|
* @returns the body if successful. Otherwise `nil`.
|
|
16
16
|
*/
|
|
17
|
-
function get_body(url: string | Hash | Url): Opaque<"b2Body"
|
|
17
|
+
function get_body(url: string | Hash | Url): Opaque<"b2Body"> | undefined;
|
|
18
18
|
/**
|
|
19
19
|
* Get the Box2D version information for the active backend.
|
|
20
20
|
*
|
|
@@ -133,10 +133,12 @@ declare global {
|
|
|
133
133
|
* @example
|
|
134
134
|
* ```ts
|
|
135
135
|
* const body = b2d.get_body("#collisionobject");
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* }
|
|
136
|
+
* if (body !== undefined) {
|
|
137
|
+
* const triangle = b2d.body.create_fixture(body, {
|
|
138
|
+
* density: 1.0, friction: 0.3,
|
|
139
|
+
* shape: { type: b2d.shape.SHAPE_TYPE_POLYGON, vertices: [vmath.vector3(-16, -16, 0), vmath.vector3(16, -16, 0), vmath.vector3(0, 16, 0)] },
|
|
140
|
+
* });
|
|
141
|
+
* }
|
|
140
142
|
* ```
|
|
141
143
|
*/
|
|
142
144
|
function create_fixture(body: Opaque<"b2Body">, definition: { shape?: { type?: number; radius?: number; center?: Vector3; v0?: Vector3; v1?: Vector3; v2?: Vector3; v3?: Vector3; vertices?: Vector3[]; hx?: number; hy?: number; angle?: number; loop?: boolean; prev_vertex?: Vector3; next_vertex?: Vector3 }; friction?: number; restitution?: number; density?: number; sensor?: boolean; filter?: { category_bits?: number; mask_bits?: number; group_index?: number } }): { index: number; type: number; sensor: boolean; density: number; friction: number; restitution: number; child_count: number };
|
|
@@ -134,10 +134,12 @@ declare global {
|
|
|
134
134
|
* @example
|
|
135
135
|
* ```ts
|
|
136
136
|
* const body = b2d.get_body("#collisionobject");
|
|
137
|
-
*
|
|
138
|
-
* circle
|
|
139
|
-
*
|
|
140
|
-
*
|
|
137
|
+
* if (body !== undefined) {
|
|
138
|
+
* const circle = b2d.fixture.get_shape(body, 1);
|
|
139
|
+
* circle.center = vmath.vector3(24, 0, 0);
|
|
140
|
+
* b2d.fixture.set_shape(body, 1, circle, true);
|
|
141
|
+
* b2d.fixture.set_shape(body, 2, { type: b2d.shape.SHAPE_TYPE_EDGE, v1: vmath.vector3(-32, 0, 0), v2: vmath.vector3(32, 0, 0) });
|
|
142
|
+
* }
|
|
141
143
|
* ```
|
|
142
144
|
*/
|
|
143
145
|
function set_shape(body: Opaque<"b2Body">, fixture_index: number, shape: { type?: number; radius?: number; center?: Vector3; v0?: Vector3; v1?: Vector3; v2?: Vector3; v3?: Vector3; vertices?: Vector3[]; hx?: number; hy?: number; angle?: number; loop?: boolean; prev_vertex?: Vector3; next_vertex?: Vector3 }, update_mass?: boolean): void;
|
|
@@ -202,10 +202,12 @@ declare global {
|
|
|
202
202
|
* @example
|
|
203
203
|
* ```ts
|
|
204
204
|
* const body = b2d.get_body("#collisionobject");
|
|
205
|
-
*
|
|
206
|
-
* circle
|
|
207
|
-
*
|
|
208
|
-
*
|
|
205
|
+
* if (body !== undefined) {
|
|
206
|
+
* const circle = b2d.shape.get_shape(body, 1);
|
|
207
|
+
* circle.center = vmath.vector3(24, 0, 0);
|
|
208
|
+
* b2d.shape.set_shape(body, 1, circle, true);
|
|
209
|
+
* b2d.shape.set_shape(body, 2, { type: b2d.shape.SHAPE_TYPE_SEGMENT, v1: vmath.vector3(-32, 0, 0), v2: vmath.vector3(32, 0, 0) });
|
|
210
|
+
* }
|
|
209
211
|
* ```
|
|
210
212
|
*/
|
|
211
213
|
function set_shape(shape_id: Opaque<"b2Shape">, definition: { type?: number; radius?: number; center?: Vector3; v0?: Vector3; v1?: Vector3; v2?: Vector3; v3?: Vector3; vertices?: Vector3[]; hx?: number; hy?: number; angle?: number; loop?: boolean; prev_vertex?: Vector3; next_vertex?: Vector3 }, update_mass: boolean): void;
|
|
@@ -844,7 +844,7 @@ declare global {
|
|
|
844
844
|
* // the info table contains meta data about all the render target attachments
|
|
845
845
|
* // so it's not necessary to use resource.get_texture here, but we do it here
|
|
846
846
|
* // just to show that it's possible:
|
|
847
|
-
* const info_attachment_1 = resource.get_texture_info(info.attachments[0]
|
|
847
|
+
* const info_attachment_1 = resource.get_texture_info(info.attachments[0]!.handle);
|
|
848
848
|
* },
|
|
849
849
|
* });
|
|
850
850
|
*
|
|
@@ -852,7 +852,7 @@ declare global {
|
|
|
852
852
|
* export default defineScript({
|
|
853
853
|
* init() {
|
|
854
854
|
* const info = resource.get_render_target_info("/my_render_target.render_targetc");
|
|
855
|
-
* const attachment = info.attachments[0]
|
|
855
|
+
* const attachment = info.attachments[0]!.texture!;
|
|
856
856
|
* // you can also get texture info from the 'texture' field, since it's a resource hash
|
|
857
857
|
* const texture_info = resource.get_texture_info(attachment);
|
|
858
858
|
* go.set("#model", "texture0", attachment);
|
|
@@ -860,7 +860,7 @@ declare global {
|
|
|
860
860
|
* });
|
|
861
861
|
* ```
|
|
862
862
|
*/
|
|
863
|
-
function get_render_target_info(path: Hash | string | number): { handle: number; width: number; height: number; depth: number; mipmaps: number; type: number; buffer_type: number; texture
|
|
863
|
+
function get_render_target_info(path: Hash | string | number): { handle: number; attachments: { handle: number; width: number; height: number; depth: number; mipmaps: number; type: number; buffer_type: number; texture?: Hash }[] };
|
|
864
864
|
/**
|
|
865
865
|
* Gets the text metrics from a font
|
|
866
866
|
*
|
|
@@ -1051,7 +1051,7 @@ declare global {
|
|
|
1051
1051
|
* export default defineScript({
|
|
1052
1052
|
* init(self) {
|
|
1053
1053
|
* const rt_info = resource.get_render_target_info(self.my_render_target);
|
|
1054
|
-
* go.set("#model", "texture0", rt_info.attachments[0]
|
|
1054
|
+
* go.set("#model", "texture0", rt_info.attachments[0]!.texture!);
|
|
1055
1055
|
* },
|
|
1056
1056
|
* });
|
|
1057
1057
|
* ```
|
|
@@ -294,7 +294,7 @@ declare global {
|
|
|
294
294
|
* }
|
|
295
295
|
* ```
|
|
296
296
|
*/
|
|
297
|
-
function get_ifaddrs(): { name: string; address
|
|
297
|
+
function get_ifaddrs(): { name: string; address?: string; mac?: string; up: boolean; running: boolean }[];
|
|
298
298
|
/**
|
|
299
299
|
* The save-file path is operating system specific and is typically located under the user's home directory.
|
|
300
300
|
* This function will raise a Lua error if unable to get the save file path.
|
|
@@ -365,7 +365,7 @@ declare global {
|
|
|
365
365
|
* }
|
|
366
366
|
* ```
|
|
367
367
|
*/
|
|
368
|
-
function get_sys_info(options?: { ignore_secure?: boolean }): { device_model
|
|
368
|
+
function get_sys_info(options?: { ignore_secure?: boolean }): { device_model?: string; manufacturer?: string; system_name: string; system_version: string; api_version: string; language: string; device_language: string; territory: string; gmt_offset: number; device_ident?: string; user_agent?: string };
|
|
369
369
|
/**
|
|
370
370
|
* If the file exists, it must have been created by `sys.save` to be loaded.
|
|
371
371
|
* This function will raise a Lua error if an error occurs while loading the file.
|
|
@@ -95,7 +95,7 @@ declare global {
|
|
|
95
95
|
*
|
|
96
96
|
* - number `inset_bottom`
|
|
97
97
|
*/
|
|
98
|
-
function get_safe_area(): {
|
|
98
|
+
function get_safe_area(): { x: number; y: number; width: number; height: number; inset_left: number; inset_top: number; inset_right: number; inset_bottom: number };
|
|
99
99
|
/**
|
|
100
100
|
* This returns the current window size (width and height).
|
|
101
101
|
*/
|
package/generated/window.d.ts
CHANGED
|
@@ -95,7 +95,7 @@ declare global {
|
|
|
95
95
|
*
|
|
96
96
|
* - number `inset_bottom`
|
|
97
97
|
*/
|
|
98
|
-
function get_safe_area(): {
|
|
98
|
+
function get_safe_area(): { x: number; y: number; width: number; height: number; inset_left: number; inset_top: number; inset_right: number; inset_bottom: number };
|
|
99
99
|
/**
|
|
100
100
|
* This returns the current window size (width and height).
|
|
101
101
|
*/
|
package/package.json
CHANGED
package/src/emit-dts.ts
CHANGED
|
@@ -245,6 +245,32 @@ export const RETURN_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
245
245
|
["editor.tx.add", 'Opaque<"transaction_step">'],
|
|
246
246
|
]);
|
|
247
247
|
|
|
248
|
+
// A function whose `returnvalues` declare a concrete type while the same slot's
|
|
249
|
+
// prose documents a `nil` alternative. Distinct from RETURN_TYPE_OVERRIDES
|
|
250
|
+
// above, which fills an *empty* `returnvalues`: these contradict a token
|
|
251
|
+
// upstream explicitly declares, so each entry records it and
|
|
252
|
+
// `return-correction-provenance.test.ts` pins it against every vendored
|
|
253
|
+
// ref-doc. Upstream adding `nil` to `types` hands the case to the top-level
|
|
254
|
+
// `nil` projection in `mapSlotUnion` and reds the entry, which is then deleted
|
|
255
|
+
// rather than re-pinned.
|
|
256
|
+
export interface ReturnTypeCorrection {
|
|
257
|
+
readonly ts: string;
|
|
258
|
+
readonly upstream: readonly string[];
|
|
259
|
+
readonly reason: string;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Keyed by FQN, mirroring PROPERTY_TYPE_CORRECTIONS.
|
|
263
|
+
export const RETURN_TYPE_CORRECTIONS: ReadonlyMap<string, ReturnTypeCorrection> = new Map([
|
|
264
|
+
[
|
|
265
|
+
"b2d.get_body",
|
|
266
|
+
{
|
|
267
|
+
ts: 'Opaque<"b2Body"> | undefined',
|
|
268
|
+
upstream: ["b2Body"],
|
|
269
|
+
reason: "the body if successful. Otherwise nil.",
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
]);
|
|
273
|
+
|
|
248
274
|
// A property whose upstream `<span class="type">` states a type the engine does
|
|
249
275
|
// not use. Unlike every other override here, which fills a gap upstream left
|
|
250
276
|
// empty, these contradict a token upstream explicitly declares — so each entry
|
|
@@ -819,6 +845,38 @@ export const TABLE_SLOT_CURATIONS: ReadonlyMap<string, TableSlotCuration> = new
|
|
|
819
845
|
],
|
|
820
846
|
},
|
|
821
847
|
],
|
|
848
|
+
// resource.get_render_target_info's return `<dl>` writes one key as
|
|
849
|
+
// `<dt>'attachments'</dt>` — quoted rather than wrapped in `<code>` — so
|
|
850
|
+
// TABLE_FIELD drops it and parseTableFields hoists the seven per-attachment
|
|
851
|
+
// fields to the top level, contradicting the `info.attachments[0].handle`
|
|
852
|
+
// access in the doc's own `@example`. Same shape and same remedy as
|
|
853
|
+
// font.get_info:return:info above: replace the mis-parsed field list with the
|
|
854
|
+
// faithful nested isList object. `texture` is documented "only available if
|
|
855
|
+
// the render target passed in is a resource", hence optional.
|
|
856
|
+
[
|
|
857
|
+
"resource.get_render_target_info:return:table",
|
|
858
|
+
{
|
|
859
|
+
kind: "object",
|
|
860
|
+
fields: [
|
|
861
|
+
{ name: "handle", types: ["number"] },
|
|
862
|
+
{
|
|
863
|
+
name: "attachments",
|
|
864
|
+
types: ["table"],
|
|
865
|
+
isList: true,
|
|
866
|
+
fields: [
|
|
867
|
+
{ name: "handle", types: ["number"] },
|
|
868
|
+
{ name: "width", types: ["number"] },
|
|
869
|
+
{ name: "height", types: ["number"] },
|
|
870
|
+
{ name: "depth", types: ["number"] },
|
|
871
|
+
{ name: "mipmaps", types: ["number"] },
|
|
872
|
+
{ name: "type", types: ["number"] },
|
|
873
|
+
{ name: "buffer_type", types: ["number"] },
|
|
874
|
+
{ name: "texture", types: ["hash"], optional: true },
|
|
875
|
+
],
|
|
876
|
+
},
|
|
877
|
+
],
|
|
878
|
+
},
|
|
879
|
+
],
|
|
822
880
|
// profiler.view_recorded_frame's `frame_index` is a typed `<ul>` "specify one
|
|
823
881
|
// of the following": `distance` and `frame`, both numeric frame offsets. The
|
|
824
882
|
// <li> items carry no `<span class="type">`, so parseTableFields recovers
|
|
@@ -1369,6 +1427,95 @@ export const TABLE_FIELD_TYPE_OVERRIDES: ReadonlyMap<string, string> = new Map([
|
|
|
1369
1427
|
["material.get_vertex_attributes:return:table:value", ATTRIBUTE_VALUE_TS],
|
|
1370
1428
|
]);
|
|
1371
1429
|
|
|
1430
|
+
// A return-slot field upstream documents as platform-gated or nil-valued while
|
|
1431
|
+
// declaring it like any other. Lua removes a key assigned `nil`, so "might be
|
|
1432
|
+
// nil if not available" and "only available on iOS and Android" describe the
|
|
1433
|
+
// same runtime state — the key is absent — and the correction is always `?`,
|
|
1434
|
+
// never a required field widened to `| undefined`.
|
|
1435
|
+
//
|
|
1436
|
+
// Separate from TABLE_FIELD_TYPE_OVERRIDES because presence is the other axis:
|
|
1437
|
+
// the parser reads these fields' types correctly and stays authoritative for
|
|
1438
|
+
// them, so an entry here restates no type and cannot drift when upstream
|
|
1439
|
+
// retypes a field. `upstream` is pinned rather than consumed, and `evidence` is
|
|
1440
|
+
// the decoded prose `return-correction-provenance.test.ts` searches the slot doc
|
|
1441
|
+
// for; both red the entry once upstream marks the field or drops the gate.
|
|
1442
|
+
//
|
|
1443
|
+
// Keyed `<element>:<kind>:<slot>:<field>`, the TABLE_FIELD_TYPE_OVERRIDES shape.
|
|
1444
|
+
export interface ReturnFieldOptionalityCorrection {
|
|
1445
|
+
readonly upstream: readonly string[];
|
|
1446
|
+
readonly evidence: string;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// The platform-gate markers upstream renders as `<span class="icon-*">` decode
|
|
1450
|
+
// to `[ios]`, `[android]`, `[html5]` — `user_agent` states its gate through the
|
|
1451
|
+
// marker alone, with no availability sentence to pin.
|
|
1452
|
+
export const RETURN_FIELD_OPTIONALITY_CORRECTIONS: ReadonlyMap<
|
|
1453
|
+
string,
|
|
1454
|
+
ReturnFieldOptionalityCorrection
|
|
1455
|
+
> = new Map([
|
|
1456
|
+
[
|
|
1457
|
+
"sys.get_sys_info:return:sys_info:device_model",
|
|
1458
|
+
{ upstream: ["string"], evidence: "[ios][android] Only available on iOS and Android." },
|
|
1459
|
+
],
|
|
1460
|
+
[
|
|
1461
|
+
"sys.get_sys_info:return:sys_info:manufacturer",
|
|
1462
|
+
{ upstream: ["string"], evidence: "[ios][android] Only available on iOS and Android." },
|
|
1463
|
+
],
|
|
1464
|
+
[
|
|
1465
|
+
"sys.get_sys_info:return:sys_info:device_ident",
|
|
1466
|
+
{
|
|
1467
|
+
upstream: ["string"],
|
|
1468
|
+
evidence: '[ios] "identifierForVendor" on iOS. [android] "android_id" on Android.',
|
|
1469
|
+
},
|
|
1470
|
+
],
|
|
1471
|
+
[
|
|
1472
|
+
"sys.get_sys_info:return:sys_info:user_agent",
|
|
1473
|
+
{ upstream: ["string"], evidence: "[html5] The HTTP user agent" },
|
|
1474
|
+
],
|
|
1475
|
+
[
|
|
1476
|
+
"sys.get_ifaddrs:return:ifaddrs:address",
|
|
1477
|
+
{ upstream: ["string"], evidence: "might be nil if not available." },
|
|
1478
|
+
],
|
|
1479
|
+
[
|
|
1480
|
+
"sys.get_ifaddrs:return:ifaddrs:mac",
|
|
1481
|
+
{ upstream: ["string"], evidence: "might be nil if not available." },
|
|
1482
|
+
],
|
|
1483
|
+
]);
|
|
1484
|
+
|
|
1485
|
+
// Mark a parser-recovered field optional from RETURN_FIELD_OPTIONALITY_CORRECTIONS,
|
|
1486
|
+
// leaving every other field — and every field's type — untouched. Sets the
|
|
1487
|
+
// `optional` flag `TableField` already carries and `inlineTableType` already
|
|
1488
|
+
// reads, so no `mapType` is needed and no correction can change a type.
|
|
1489
|
+
//
|
|
1490
|
+
// A correction keyed to this slot that matches no recovered field throws rather
|
|
1491
|
+
// than no-opping: a field upstream renamed would otherwise leave a stale entry
|
|
1492
|
+
// looking applied while the emitted declaration silently re-widened.
|
|
1493
|
+
// Returns a new array; never mutates the parser's result.
|
|
1494
|
+
export function applyFieldOptionalityCorrections(
|
|
1495
|
+
elementName: string,
|
|
1496
|
+
slotKind: "param" | "return" | undefined,
|
|
1497
|
+
slotName: string | undefined,
|
|
1498
|
+
fields: readonly TableField[],
|
|
1499
|
+
): TableField[] {
|
|
1500
|
+
if (slotKind === undefined || slotName === undefined) return [...fields];
|
|
1501
|
+
const prefix = `${tableSlotKey(elementName, slotKind, slotName)}:`;
|
|
1502
|
+
const corrected = fields.map((field) =>
|
|
1503
|
+
RETURN_FIELD_OPTIONALITY_CORRECTIONS.has(`${prefix}${field.name}`)
|
|
1504
|
+
? { ...field, optional: true }
|
|
1505
|
+
: field,
|
|
1506
|
+
);
|
|
1507
|
+
const recovered = new Set(fields.map((field) => field.name));
|
|
1508
|
+
const stale = [...RETURN_FIELD_OPTIONALITY_CORRECTIONS.keys()].filter(
|
|
1509
|
+
(key) => key.startsWith(prefix) && !recovered.has(key.slice(prefix.length)),
|
|
1510
|
+
);
|
|
1511
|
+
if (stale.length > 0) {
|
|
1512
|
+
throw new Error(
|
|
1513
|
+
`field-optionality correction names no recovered field: ${stale.sort().join(", ")}`,
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
return corrected;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1372
1519
|
// Pin a parser-recovered field's TS type from TABLE_FIELD_TYPE_OVERRIDES,
|
|
1373
1520
|
// leaving every other field parser-authoritative. A field the override does not
|
|
1374
1521
|
// name is returned unchanged; a field it names gains a `tsType` (emitted
|
|
@@ -1605,7 +1752,11 @@ export function buildTableDocResolver(sources: readonly TableDocSource[]): Table
|
|
|
1605
1752
|
return (elementName) => docByName.get(elementName);
|
|
1606
1753
|
}
|
|
1607
1754
|
|
|
1608
|
-
export function parseTableFields(
|
|
1755
|
+
export function parseTableFields(
|
|
1756
|
+
doc: string,
|
|
1757
|
+
resolver?: TableDocResolver,
|
|
1758
|
+
slotName?: string,
|
|
1759
|
+
): TableField[] | null {
|
|
1609
1760
|
const fields: TableField[] = [];
|
|
1610
1761
|
for (const match of doc.matchAll(TABLE_FIELD)) {
|
|
1611
1762
|
const name = (match[1] ?? "").trim();
|
|
@@ -1624,14 +1775,20 @@ export function parseTableFields(doc: string, resolver?: TableDocResolver): Tabl
|
|
|
1624
1775
|
}
|
|
1625
1776
|
// Nested recovery, scoped to the one unambiguous shape: a `<dl>` declaring a
|
|
1626
1777
|
// single `table`-typed field whose keys sit in an immediately-following
|
|
1627
|
-
// top-level `<ul>` typed-field list
|
|
1628
|
-
//
|
|
1629
|
-
//
|
|
1778
|
+
// top-level `<ul>` typed-field list. Attach the `<ul>` fields as that field's
|
|
1779
|
+
// nested shape so the slot recovers as a nested object instead of a `Record`.
|
|
1780
|
+
//
|
|
1781
|
+
// Unless the header restates the slot it describes (`window.get_safe_area`'s
|
|
1782
|
+
// `safe_area` return documented under a `safe_area` header): that is a
|
|
1783
|
+
// restatement, not a nesting level, and wrapping it emits a level the engine
|
|
1784
|
+
// never returns. Only a caller that knows the slot name can tell the two
|
|
1785
|
+
// apart, so the `<ul>` keys are returned directly when they match.
|
|
1630
1786
|
if (fields.length === 1) {
|
|
1631
1787
|
const only = fields[0];
|
|
1632
1788
|
if (only && only.types.length === 1 && only.types[0] === "table") {
|
|
1633
1789
|
const nested = parseUlFields(doc);
|
|
1634
1790
|
if (nested.length > 0) {
|
|
1791
|
+
if (slotName !== undefined && slotName === only.name) return nested;
|
|
1635
1792
|
only.fields = nested;
|
|
1636
1793
|
return [only];
|
|
1637
1794
|
}
|
|
@@ -2586,6 +2743,10 @@ function emitReturn(
|
|
|
2586
2743
|
// slots and leaves the consumer on its token-derived fallback.
|
|
2587
2744
|
const override = RETURN_TYPE_OVERRIDES.get(elementName);
|
|
2588
2745
|
if (override !== undefined) return { type: override, trailing: "", slots: [] };
|
|
2746
|
+
// Kept beside the override lookup so the two stay visibly distinct: this one
|
|
2747
|
+
// contradicts a declared token rather than filling an empty `returnvalues`,
|
|
2748
|
+
// and it is a per-slot render, so it reports its slot.
|
|
2749
|
+
const correction = RETURN_TYPE_CORRECTIONS.get(elementName);
|
|
2589
2750
|
if (returnValues.length === 0) return { type: "void", trailing: "", slots: [] };
|
|
2590
2751
|
if (returnValues.length > 1) {
|
|
2591
2752
|
// Defold multi-returns are positional and always present; each slot maps
|
|
@@ -2617,6 +2778,13 @@ function emitReturn(
|
|
|
2617
2778
|
}
|
|
2618
2779
|
const first = returnValues[0];
|
|
2619
2780
|
if (!first) return { type: "void", trailing: "", slots: [] };
|
|
2781
|
+
if (correction !== undefined) {
|
|
2782
|
+
return {
|
|
2783
|
+
type: correction.ts,
|
|
2784
|
+
trailing: "",
|
|
2785
|
+
slots: [{ position: 0, name: first.name, ts: correction.ts }],
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2620
2788
|
const ts =
|
|
2621
2789
|
first.types.length > 0
|
|
2622
2790
|
? mapSlotUnion(
|
|
@@ -2727,19 +2895,25 @@ function mapSlotUnion(
|
|
|
2727
2895
|
// arbitrary name. Reusing the parser keeps the inner fields in lockstep
|
|
2728
2896
|
// with the doc instead of a hand-listed drift-prone copy; a documented
|
|
2729
2897
|
// array-form field is pinned via TABLE_FIELD_TYPE_OVERRIDES.
|
|
2730
|
-
const parsed =
|
|
2898
|
+
const parsed = applyFieldOptionalityCorrections(
|
|
2731
2899
|
elementName,
|
|
2732
2900
|
slotKind,
|
|
2733
2901
|
slotName,
|
|
2734
|
-
|
|
2902
|
+
applyFieldTypeOverrides(
|
|
2903
|
+
elementName,
|
|
2904
|
+
slotKind,
|
|
2905
|
+
slotName,
|
|
2906
|
+
parseTableFields(doc, resolver, slotName) ?? [],
|
|
2907
|
+
),
|
|
2735
2908
|
);
|
|
2736
2909
|
const object = inlineTableType(parsed, mapType, optionalFields);
|
|
2737
2910
|
ts = `Record<string, ${object}>`;
|
|
2738
2911
|
} else {
|
|
2739
|
-
const parsed = parseTableFields(doc, resolver);
|
|
2912
|
+
const parsed = parseTableFields(doc, resolver, slotName);
|
|
2740
2913
|
if (parsed !== null) {
|
|
2741
2914
|
const nested = applyNestedFieldCurations(elementName, slotKind, slotName, parsed);
|
|
2742
|
-
const
|
|
2915
|
+
const typed = applyFieldTypeOverrides(elementName, slotKind, slotName, nested);
|
|
2916
|
+
const fields = applyFieldOptionalityCorrections(elementName, slotKind, slotName, typed);
|
|
2743
2917
|
const object = inlineTableType(fields, mapType, optionalFields);
|
|
2744
2918
|
ts = isSlotLevelList(doc) ? `${object}[]` : object;
|
|
2745
2919
|
} else {
|
package/src/lifecycle.ts
CHANGED
|
@@ -571,6 +571,14 @@ export type RenderScriptHooksWithProperties<TProps, TSelf, TInitState> = Omit<
|
|
|
571
571
|
properties?: TProps;
|
|
572
572
|
};
|
|
573
573
|
|
|
574
|
+
// The script-local message map an `onMessage` dispatcher carries, lifted onto
|
|
575
|
+
// the factory result so `ScriptMessages<typeof script>` can read it. Matched
|
|
576
|
+
// structurally so this module stays usable through its own `./lifecycle`
|
|
577
|
+
// subpath, where the ambient `MessageDispatcher` is not declared.
|
|
578
|
+
export type ScriptMessageChannel<TMessages> = {
|
|
579
|
+
on_message?: { readonly __messages?: TMessages };
|
|
580
|
+
};
|
|
581
|
+
|
|
574
582
|
/**
|
|
575
583
|
* Extract a script module's declared property channel (`TProps`) as a nameable
|
|
576
584
|
* type. A script declares its editor properties with the value-keyed
|
|
@@ -617,9 +625,15 @@ export type ScriptPropertiesOf<T extends { properties?: object }> = NonNullable<
|
|
|
617
625
|
* });
|
|
618
626
|
* ```
|
|
619
627
|
*/
|
|
620
|
-
export function defineScript<
|
|
621
|
-
|
|
622
|
-
|
|
628
|
+
export function defineScript<
|
|
629
|
+
TProps extends object = Record<never, never>,
|
|
630
|
+
TInitState = TProps,
|
|
631
|
+
TMessages extends object = Record<never, never>,
|
|
632
|
+
>(
|
|
633
|
+
hooks: ScriptHooksWithProperties<TProps, TProps & TInitState, TInitState> &
|
|
634
|
+
ScriptMessageChannel<TMessages>,
|
|
635
|
+
): ScriptHooksWithProperties<TProps, TProps & TInitState, TInitState> &
|
|
636
|
+
ScriptMessageChannel<TMessages> {
|
|
623
637
|
return hooks;
|
|
624
638
|
}
|
|
625
639
|
|
|
@@ -652,9 +666,15 @@ export function defineScript<TProps extends object = Record<never, never>, TInit
|
|
|
652
666
|
* });
|
|
653
667
|
* ```
|
|
654
668
|
*/
|
|
655
|
-
export function defineGuiScript<
|
|
656
|
-
|
|
657
|
-
|
|
669
|
+
export function defineGuiScript<
|
|
670
|
+
TProps extends object = Record<never, never>,
|
|
671
|
+
TInitState = TProps,
|
|
672
|
+
TMessages extends object = Record<never, never>,
|
|
673
|
+
>(
|
|
674
|
+
hooks: GuiScriptHooksWithProperties<TProps, TProps & TInitState, TInitState> &
|
|
675
|
+
ScriptMessageChannel<TMessages>,
|
|
676
|
+
): GuiScriptHooksWithProperties<TProps, TProps & TInitState, TInitState> &
|
|
677
|
+
ScriptMessageChannel<TMessages> {
|
|
658
678
|
return hooks;
|
|
659
679
|
}
|
|
660
680
|
|
|
@@ -689,8 +709,11 @@ export function defineGuiScript<TProps extends object = Record<never, never>, TI
|
|
|
689
709
|
export function defineRenderScript<
|
|
690
710
|
TProps extends object = Record<never, never>,
|
|
691
711
|
TInitState = TProps,
|
|
712
|
+
TMessages extends object = Record<never, never>,
|
|
692
713
|
>(
|
|
693
|
-
hooks: RenderScriptHooksWithProperties<TProps, TProps & TInitState, TInitState
|
|
694
|
-
|
|
714
|
+
hooks: RenderScriptHooksWithProperties<TProps, TProps & TInitState, TInitState> &
|
|
715
|
+
ScriptMessageChannel<TMessages>,
|
|
716
|
+
): RenderScriptHooksWithProperties<TProps, TProps & TInitState, TInitState> &
|
|
717
|
+
ScriptMessageChannel<TMessages> {
|
|
695
718
|
return hooks;
|
|
696
719
|
}
|
|
@@ -3,8 +3,9 @@ import type { Hash, Url } from "./core-types";
|
|
|
3
3
|
|
|
4
4
|
declare global {
|
|
5
5
|
// DU-style dispatcher built on `isMessage`'s receive-side narrowing
|
|
6
|
-
// (message-guard.d.ts): each handler key is a `
|
|
7
|
-
// `message` param narrows to that id's `
|
|
6
|
+
// (message-guard.d.ts): each catalog handler key is a `MessageId` and its
|
|
7
|
+
// `message` param narrows to that id's `MessagePayload`; any other key is a
|
|
8
|
+
// script-local id declared by its annotated `message`. It returns
|
|
8
9
|
// an `on_message` handler so it reads as `on_message: onMessage<Self>({ ... })`;
|
|
9
10
|
// `self` threads via the explicit type argument, mirroring `defineScript<Self>`.
|
|
10
11
|
// The transpiler lowers the call to a flat `message_id == hash("...")`
|
|
@@ -18,7 +19,11 @@ declare global {
|
|
|
18
19
|
* explicit type argument, mirroring `defineScript<Self>`. The transpiler lowers
|
|
19
20
|
* it to a flat `if/elseif message_id == hash("...")` chain.
|
|
20
21
|
*
|
|
21
|
-
*
|
|
22
|
+
* A key outside the catalogs declares a script-local message when its handler
|
|
23
|
+
* annotates `message`; the returned dispatcher carries those ids so
|
|
24
|
+
* {@link ScriptMessages} can hand them to senders.
|
|
25
|
+
*
|
|
26
|
+
* @param handlers - a partial map from message id to its handler; a catalog id's `message` is narrowed to that id's payload, and an annotated handler under any other key declares a script-local id.
|
|
22
27
|
* @returns the `on_message` lifecycle handler that dispatches to the matching entry.
|
|
23
28
|
* @example
|
|
24
29
|
* ```ts
|
|
@@ -27,18 +32,55 @@ declare global {
|
|
|
27
32
|
* contact_point_response(self, message, sender) {
|
|
28
33
|
* print(message.other_group);
|
|
29
34
|
* },
|
|
35
|
+
* spawn_wave(self, message: { count: number }) {
|
|
36
|
+
* print(message.count);
|
|
37
|
+
* },
|
|
30
38
|
* }),
|
|
31
39
|
* });
|
|
32
40
|
* ```
|
|
33
41
|
*/
|
|
34
|
-
function onMessage<TSelf = Record<never, never>>(
|
|
35
|
-
handlers:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
function onMessage<TSelf = Record<never, never>, H extends object = Record<never, never>>(
|
|
43
|
+
handlers: H &
|
|
44
|
+
Partial<{
|
|
45
|
+
[K in MessageId]: (self: TSelf, message: MessagePayload<K>, sender: Url) => void;
|
|
46
|
+
}> &
|
|
47
|
+
OnMessageLocalHandlers<TSelf, H>,
|
|
48
|
+
): MessageDispatcher<TSelf, OnMessageLocalMessages<H>>;
|
|
49
|
+
|
|
50
|
+
// A key outside `MessageId` is a script-local id only when its handler
|
|
51
|
+
// annotates `message`; an unannotated one infers no payload and reds as an
|
|
52
|
+
// implicit `any`, which is the typo guard.
|
|
53
|
+
type OnMessageHandlerPayload<F> = F extends (
|
|
54
|
+
self: never,
|
|
55
|
+
message: infer M,
|
|
56
|
+
...rest: never[]
|
|
57
|
+
) => unknown
|
|
58
|
+
? M
|
|
59
|
+
: never;
|
|
60
|
+
|
|
61
|
+
type OnMessageLocalHandlers<TSelf, H> = {
|
|
62
|
+
[K in Exclude<keyof H, MessageId>]: (
|
|
63
|
+
self: TSelf,
|
|
64
|
+
message: OnMessageHandlerPayload<H[K]>,
|
|
65
|
+
sender: Url,
|
|
66
|
+
) => void;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// A numeric key is sent under its JavaScript property key (`1e3` is "1000"),
|
|
70
|
+
// the id the transpiler hashes.
|
|
71
|
+
type OnMessageLocalMessages<H> = {
|
|
72
|
+
[K in Exclude<keyof H, MessageId> as K extends number ? `${K}` : K]: OnMessageHandlerPayload<
|
|
73
|
+
H[K]
|
|
74
|
+
>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The `on_message` handler {@link onMessage} returns. `__messages` is a
|
|
79
|
+
* type-only record of the script-local ids its handlers declared; it never
|
|
80
|
+
* exists at runtime.
|
|
81
|
+
*/
|
|
82
|
+
interface MessageDispatcher<TSelf, TLocal = Record<never, never>> {
|
|
83
|
+
(self: TSelf, message_id: Hash, message: Record<string | number, unknown>, sender: Url): void;
|
|
84
|
+
readonly __messages?: TLocal;
|
|
85
|
+
}
|
|
44
86
|
}
|