@defold-typescript/types 0.6.0 → 0.8.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/README.md CHANGED
@@ -12,8 +12,8 @@ Most users get these wired in automatically by the
12
12
  [`defold-typescript`](https://www.npmjs.com/package/@defold-typescript/cli) CLI
13
13
  (`bunx @defold-typescript/cli init`), which points `tsconfig.json` at the right surface.
14
14
 
15
- See the repository [README](https://github.com/king8fisher/defold-typescript#readme)
16
- and [`docs/guide/`](https://github.com/king8fisher/defold-typescript/tree/main/docs/guide)
15
+ See the repository [README](https://github.com/defold-typescript/toolchain#readme)
16
+ and [`docs/guide/`](https://github.com/defold-typescript/toolchain/tree/main/docs/guide)
17
17
  for the full workflow.
18
18
 
19
19
  ## License
package/api-targets.json CHANGED
@@ -31,6 +31,7 @@
31
31
  },
32
32
  { "namespace": "graphics", "fixture": "graphics_doc.json", "outFile": "graphics.d.ts" },
33
33
  { "namespace": "gui", "fixture": "gui_doc.json", "outFile": "gui.d.ts" },
34
+ { "namespace": "html5", "fixture": "html5_doc.json", "outFile": "html5.d.ts" },
34
35
  { "namespace": "http", "fixture": "http_doc.json", "outFile": "http.d.ts" },
35
36
  { "namespace": "iac", "fixture": "iac_doc.json", "outFile": "iac.d.ts" },
36
37
  { "namespace": "iap", "fixture": "iap_doc.json", "outFile": "iap.d.ts" },
@@ -65,6 +66,7 @@
65
66
  { "namespace": "sys", "fixture": "sys_doc.json", "outFile": "sys.d.ts" },
66
67
  { "namespace": "tilemap", "fixture": "tilemap_doc.json", "outFile": "tilemap.d.ts" },
67
68
  { "namespace": "timer", "fixture": "timer_doc.json", "outFile": "timer.d.ts" },
69
+ { "namespace": "types", "fixture": "types_doc.json", "outFile": "types.d.ts" },
68
70
  { "namespace": "vmath", "fixture": "vmath_doc.json", "outFile": "vmath.d.ts" },
69
71
  { "namespace": "webview", "fixture": "webview_doc.json", "outFile": "webview.d.ts" },
70
72
  { "namespace": "window", "fixture": "window_doc.json", "outFile": "window.d.ts" },
@@ -40,7 +40,7 @@ declare global {
40
40
  * }
41
41
  * ```
42
42
  */
43
- function get_resources(collectionproxy: Url): Record<string | number, unknown>;
43
+ function get_resources(collectionproxy: Url): Hash[];
44
44
  /**
45
45
  * The collection should be loaded by the collection proxy.
46
46
  * Setting the collection to "nil" will revert it back to the original collection.
package/generated/go.d.ts CHANGED
@@ -689,7 +689,7 @@ declare global {
689
689
  * });
690
690
  * ```
691
691
  */
692
- export function on_input(self: Opaque<"userdata">, action_id: Hash, action: Record<string | number, unknown>): boolean | unknown;
692
+ export function on_input(self: Opaque<"userdata">, action_id: Hash, action: { value?: number; pressed?: boolean; released?: boolean; repeated?: boolean; x?: number; y?: number; screen_x?: number; screen_y?: number; dx?: number; dy?: number; screen_dx?: number; screen_dy?: number; gamepad?: number; gamepad_axis?: Vector3; touch?: { id?: number; pressed?: boolean; released?: boolean; tap_count?: number; x?: number; y?: number; dx?: number; dy?: number; acc_x?: number; acc_y?: number; acc_z?: number }[]; text?: string }): boolean | unknown;
693
693
  /**
694
694
  * This is a callback-function, which is called by the engine whenever a message has been sent to the script component.
695
695
  * It can be used to take action on the message, e.g. send a response back to the sender of the message.
@@ -1381,7 +1381,7 @@ declare global {
1381
1381
  * end
1382
1382
  * ```
1383
1383
  */
1384
- function on_input(self: Opaque<"userdata">, action_id: Hash, action: Record<string | number, unknown>): boolean | unknown;
1384
+ function on_input(self: Opaque<"userdata">, action_id: Hash, action: { value?: number; pressed?: boolean; released?: boolean; repeated?: boolean; x?: number; y?: number; screen_x?: number; screen_y?: number; dx?: number; dy?: number; screen_dx?: number; screen_dy?: number; gamepad?: number; gamepad_axis?: Vector3; touch?: { id?: number; pressed?: boolean; released?: boolean; tap_count?: number; x?: number; y?: number; dx?: number; dy?: number; acc_x?: number; acc_y?: number; acc_z?: number }[]; text?: string }): boolean | unknown;
1385
1385
  /**
1386
1386
  * This is a callback-function, which is called by the engine whenever a message has been sent to the gui component.
1387
1387
  * It can be used to take action on the message, e.g. update the gui or send a response back to the sender of the message.
@@ -0,0 +1,49 @@
1
+ /** @noSelfInFile */
2
+ declare global {
3
+ /**
4
+ * HTML5 platform specific functions.
5
+ * [icon:html5] The following functions are only available on HTML5 builds, the `html5.*` Lua namespace will not be available on other platforms.
6
+ */
7
+ namespace html5 {
8
+ /**
9
+ * Executes the supplied string as JavaScript inside the browser.
10
+ * A call to this function is blocking, the result is returned as-is, as a string.
11
+ * (Internally this will execute the string using the `eval()` JavaScript function.)
12
+ *
13
+ * @param code - Javascript code to run
14
+ * @returns result as string
15
+ * @example
16
+ * ```lua
17
+ * local res = html5.run("10 + 20") -- returns the string "30"
18
+ * print(res)
19
+ * local res_num = tonumber(res) -- convert to number
20
+ * print(res_num - 20) -- prints 10
21
+ * ```
22
+ */
23
+ function run(code: string): string;
24
+ /**
25
+ * Set a JavaScript interaction listener callaback from lua that will be
26
+ * invoked when a user interacts with the web page by clicking, touching or typing.
27
+ * The callback can then call DOM restricted actions like requesting a pointer lock,
28
+ * or start playing sounds the first time the callback is invoked.
29
+ *
30
+ * @param callback - The interaction callback. Pass an empty function or `nil` if you no longer wish to receive callbacks.
31
+ * `self`
32
+ * object The calling script
33
+ * @example
34
+ * ```lua
35
+ * local function on_interaction(self)
36
+ * print("on_interaction called")
37
+ * html5.set_interaction_listener(nil)
38
+ * end
39
+ *
40
+ * function init(self)
41
+ * html5.set_interaction_listener(on_interaction)
42
+ * end
43
+ * ```
44
+ */
45
+ function set_interaction_listener(callback?: (self: unknown) => void): void;
46
+ }
47
+ }
48
+
49
+ export {};
@@ -61,7 +61,7 @@ declare global {
61
61
  * });
62
62
  * ```
63
63
  */
64
- function request(url: string, method: string, callback: (self: unknown, id: unknown, response: unknown) => void, headers?: Record<string | number, unknown>, post_data?: string, options?: { timeout?: number; path?: string; ignore_cache?: boolean; chunked_transfer?: boolean; report_progress?: boolean }): void;
64
+ function request(url: string, method: string, callback: (self: unknown, id: unknown, response: unknown) => void, headers?: LuaMap<string, string>, post_data?: string, options?: { timeout?: number; path?: string; ignore_cache?: boolean; chunked_transfer?: boolean; report_progress?: boolean }): void;
65
65
  }
66
66
  }
67
67
 
@@ -1,3 +1,5 @@
1
+ /// <reference types="lua-types/5.1" />
2
+ /// <reference types="lua-types/special/jit-only" />
1
3
  import "../b2d";
2
4
  import "../buffer";
3
5
  import "../camera";
@@ -7,6 +9,7 @@ import "../crash";
7
9
  import "../factory";
8
10
  import "../go";
9
11
  import "../graphics";
12
+ import "../html5";
10
13
  import "../http";
11
14
  import "../iac";
12
15
  import "../iap";
@@ -27,6 +30,7 @@ import "../sprite";
27
30
  import "../sys";
28
31
  import "../tilemap";
29
32
  import "../timer";
33
+ import "../types";
30
34
  import "../vmath";
31
35
  import "../webview";
32
36
  import "../window";
@@ -1,3 +1,5 @@
1
+ /// <reference types="lua-types/5.1" />
2
+ /// <reference types="lua-types/special/jit-only" />
1
3
  import "../b2d";
2
4
  import "../buffer";
3
5
  import "../camera";
@@ -7,6 +9,7 @@ import "../crash";
7
9
  import "../factory";
8
10
  import "../go";
9
11
  import "../graphics";
12
+ import "../html5";
10
13
  import "../http";
11
14
  import "../iac";
12
15
  import "../iap";
@@ -27,6 +30,7 @@ import "../sprite";
27
30
  import "../sys";
28
31
  import "../tilemap";
29
32
  import "../timer";
33
+ import "../types";
30
34
  import "../vmath";
31
35
  import "../webview";
32
36
  import "../window";
@@ -1,3 +1,5 @@
1
+ /// <reference types="lua-types/5.1" />
2
+ /// <reference types="lua-types/special/jit-only" />
1
3
  import "../b2d";
2
4
  import "../buffer";
3
5
  import "../camera";
@@ -7,6 +9,7 @@ import "../crash";
7
9
  import "../factory";
8
10
  import "../go";
9
11
  import "../graphics";
12
+ import "../html5";
10
13
  import "../http";
11
14
  import "../iac";
12
15
  import "../iap";
@@ -27,6 +30,7 @@ import "../sprite";
27
30
  import "../sys";
28
31
  import "../tilemap";
29
32
  import "../timer";
33
+ import "../types";
30
34
  import "../vmath";
31
35
  import "../webview";
32
36
  import "../window";
@@ -50,7 +50,7 @@ declare global {
50
50
  * See each joint type for possible properties field. The one field that is accepted for all joint types is:
51
51
  * - boolean `collide_connected`: Set this flag to true if the attached bodies should collide.
52
52
  */
53
- function create_joint(joint_type: number, collisionobject_a: string | Hash | Url, joint_id: string | Hash, position_a: Vector3, collisionobject_b: string | Hash | Url, position_b: Vector3, properties?: Record<string | number, unknown>): void;
53
+ function create_joint(joint_type: number, collisionobject_a: string | Hash | Url, joint_id: string | Hash, position_a: Vector3, collisionobject_b: string | Hash | Url, position_b: Vector3, properties?: { collide_connected?: boolean }): void;
54
54
  /**
55
55
  * Destroy an already physics joint. The joint has to be created before a
56
56
  * destroy can be issued.
@@ -205,7 +205,7 @@ declare global {
205
205
  * });
206
206
  * ```
207
207
  */
208
- function raycast(from: Vector3, to: Vector3, groups: Hash[], options?: { all?: boolean }): Record<string | number, unknown> | unknown;
208
+ function raycast(from: Vector3, to: Vector3, groups: Hash[], options?: { all?: boolean }): { fraction: number; position: Vector3; normal: Vector3; id: Hash; group: Hash; request_id: number }[] | unknown;
209
209
  /**
210
210
  * Ray casts are used to test for intersections against collision objects in the physics world.
211
211
  * Collision objects of types kinematic, dynamic and static are tested against. Trigger objects
@@ -400,7 +400,7 @@ declare global {
400
400
  * @param properties - joint specific properties table
401
401
  * Note: The `collide_connected` field cannot be updated/changed after a connection has been made.
402
402
  */
403
- function set_joint_properties(collisionobject: string | Hash | Url, joint_id: string | Hash, properties: Record<string | number, unknown>): void;
403
+ function set_joint_properties(collisionobject: string | Hash | Url, joint_id: string | Hash, properties: { collide_connected?: boolean }): void;
404
404
  /**
405
405
  * Sets or clears the masking of a group (maskbit) in a collision object.
406
406
  *
@@ -213,7 +213,7 @@ declare global {
213
213
  * profiler.view_recorded_frame({ distance: -1 });
214
214
  * ```
215
215
  */
216
- function view_recorded_frame(frame_index: Record<string | number, unknown>): void;
216
+ function view_recorded_frame(frame_index: { distance?: number; frame?: number }): void;
217
217
  }
218
218
  }
219
219
 
@@ -1,5 +1,5 @@
1
1
  /** @noSelfInFile */
2
- import type { Hash, Matrix4, Opaque, Url } from "../src/core-types";
2
+ import type { Hash, Matrix4, Opaque, Url, Vector4 } from "../src/core-types";
3
3
 
4
4
  declare global {
5
5
  /**
@@ -47,7 +47,7 @@ declare global {
47
47
  * });
48
48
  * ```
49
49
  */
50
- function clear(buffers: Record<string | number, unknown>): void;
50
+ function clear(buffers: LuaMap<number, number | Vector4>): void;
51
51
  /**
52
52
  * Constant buffers are used to set shader program variables and are optionally passed to the `render.draw()` function.
53
53
  * The buffer's constant elements can be indexed like an ordinary Lua table, but you can't iterate over them with pairs() or ipairs().
@@ -468,7 +468,7 @@ declare global {
468
468
  * const p = render.predicate([hash("opaque"), hash("smoke")]);
469
469
  * ```
470
470
  */
471
- function predicate(tags: Record<string | number, unknown>): number;
471
+ function predicate(tags: (string | Hash)[]): number;
472
472
  /**
473
473
  * Creates a new render target according to the supplied
474
474
  * specification table.
@@ -626,7 +626,7 @@ declare global {
626
626
  * `geometries`, preserving the frame-to-geometry mapping used by the atlas.
627
627
  * See resource.set_atlas for a detailed description of each field
628
628
  */
629
- function get_atlas(path: Hash | string): { texture: string | Hash; animations: { id: string; width: number; height: number; frame_start: number; frame_end: number; playback: Opaque<"constant">; fps: number; flip_vertical: boolean; flip_horizontal: boolean }[]; geometries: Record<string | number, unknown> };
629
+ function get_atlas(path: Hash | string): { texture: string | Hash; animations: { id: string; width: number; height: number; frame_start: number; frame_end: number; playback: Opaque<"constant">; fps: number; flip_vertical: boolean; flip_horizontal: boolean }[]; geometries: { vertices: number[]; uvs: number[]; indices: number[] }[] };
630
630
  /**
631
631
  * gets the buffer from a resource
632
632
  *
@@ -739,7 +739,7 @@ declare global {
739
739
  * });
740
740
  * ```
741
741
  */
742
- function get_text_metrics(url: Hash, text: string, options?: { width?: number; leading?: number; tracking?: number; line_break?: boolean }): Record<string | number, unknown>;
742
+ function get_text_metrics(url: Hash, text: string, options?: { width?: number; leading?: number; tracking?: number; line_break?: boolean }): { width: number; height: number; max_ascent: number; max_descent: number };
743
743
  /**
744
744
  * Gets texture info from a texture resource path or a texture handle
745
745
  *
@@ -1007,7 +1007,7 @@ declare global {
1007
1007
  * });
1008
1008
  * ```
1009
1009
  */
1010
- function set_atlas(path: Hash | string, table: { texture?: string | Hash; animations?: { id?: string; width?: number; height?: number; frame_start?: number; frame_end?: number; playback?: Opaque<"constant">; fps?: number; flip_vertical?: boolean; flip_horizontal?: boolean }[]; geometries?: Record<string | number, unknown>; vertices?: number[]; uvs?: number[]; indices?: number[] }): void;
1010
+ function set_atlas(path: Hash | string, table: { texture?: string | Hash; animations?: { id?: string; width?: number; height?: number; frame_start?: number; frame_end?: number; playback?: Opaque<"constant">; fps?: number; flip_vertical?: boolean; flip_horizontal?: boolean }[]; geometries?: { vertices?: number[]; uvs?: number[]; indices?: number[] }[]; vertices?: number[]; uvs?: number[]; indices?: number[] }): void;
1011
1011
  /**
1012
1012
  * Sets the buffer of a resource. By default, setting the resource buffer will either copy the data from the incoming buffer object
1013
1013
  * to the buffer stored in the destination resource, or make a new buffer object if the sizes between the source buffer and the destination buffer
@@ -0,0 +1,59 @@
1
+ /** @noSelfInFile */
2
+ declare global {
3
+ /**
4
+ * Functions for checking Defold userdata types.
5
+ */
6
+ namespace types {
7
+ /**
8
+ * Check if passed type is hash.
9
+ *
10
+ * @param var_ - Variable to check type
11
+ * @returns True if passed type is hash
12
+ */
13
+ function is_hash(var_: unknown): boolean;
14
+ /**
15
+ * Check if passed type is matrix4.
16
+ *
17
+ * @param var_ - Variable to check type
18
+ * @returns True if passed type is matrix4
19
+ */
20
+ function is_matrix4(var_: unknown): boolean;
21
+ /**
22
+ * Check if passed type is quaternion.
23
+ *
24
+ * @param var_ - Variable to check type
25
+ * @returns True if passed type is quaternion
26
+ */
27
+ function is_quat(var_: unknown): boolean;
28
+ /**
29
+ * Check if passed type is URL.
30
+ *
31
+ * @param var_ - Variable to check type
32
+ * @returns True if passed type is URL
33
+ */
34
+ function is_url(var_: unknown): boolean;
35
+ /**
36
+ * Check if passed type is vector.
37
+ *
38
+ * @param var_ - Variable to check type
39
+ * @returns True if passed type is vector
40
+ */
41
+ function is_vector(var_: unknown): boolean;
42
+ /**
43
+ * Check if passed type is vector3.
44
+ *
45
+ * @param var_ - Variable to check type
46
+ * @returns True if passed type is vector3
47
+ */
48
+ function is_vector3(var_: unknown): boolean;
49
+ /**
50
+ * Check if passed type is vector4.
51
+ *
52
+ * @param var_ - Variable to check type
53
+ * @returns True if passed type is vector4
54
+ */
55
+ function is_vector4(var_: unknown): boolean;
56
+ }
57
+ }
58
+
59
+ export {};
package/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /// <reference types="lua-types/5.1" />
2
+ /// <reference types="lua-types/special/jit-only" />
1
3
  import "./generated/builtin-messages";
2
4
  import "./src/msg-overloads";
3
5
  import "./src/message-guard";
@@ -14,6 +16,7 @@ import "./generated/go";
14
16
  import "./src/go-overloads";
15
17
  import "./generated/graphics";
16
18
  import "./generated/gui";
19
+ import "./generated/html5";
17
20
  import "./generated/http";
18
21
  import "./generated/iac";
19
22
  import "./generated/iap";
@@ -35,6 +38,7 @@ import "./generated/sprite";
35
38
  import "./generated/sys";
36
39
  import "./generated/tilemap";
37
40
  import "./generated/timer";
41
+ import "./generated/types";
38
42
  import "./generated/vmath";
39
43
  import "./generated/webview";
40
44
  import "./generated/window";
package/package.json CHANGED
@@ -1,8 +1,15 @@
1
1
  {
2
2
  "name": "@defold-typescript/types",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "TypeScript types for the Defold engine's Lua APIs.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/defold-typescript/toolchain.git",
9
+ "directory": "packages/types"
10
+ },
11
+ "homepage": "https://github.com/defold-typescript/toolchain#readme",
12
+ "bugs": "https://github.com/defold-typescript/toolchain/issues",
6
13
  "type": "module",
7
14
  "main": "./src/index.ts",
8
15
  "types": "./index.d.ts",
@@ -23,6 +30,9 @@
23
30
  "./core-types": {
24
31
  "types": "./src/core-types.ts"
25
32
  },
33
+ "./timers": {
34
+ "types": "./src/timers.d.ts"
35
+ },
26
36
  "./package.json": "./package.json"
27
37
  },
28
38
  "files": [
@@ -45,6 +55,9 @@
45
55
  "sync-api-docs": "bun scripts/sync-api-docs.ts",
46
56
  "ref-doc-delta": "bun scripts/ref-doc-delta.ts --target defold-1.9.8 --namespace label --present label.get_text --absent label.set_text"
47
57
  },
58
+ "dependencies": {
59
+ "lua-types": "^2.13.1"
60
+ },
48
61
  "devDependencies": {
49
62
  "@typescript-to-lua/language-extensions": "1.19.0"
50
63
  }
@@ -11,10 +11,70 @@ export interface ResolvedRefDoc {
11
11
  provenance: DocSourceProvenance;
12
12
  }
13
13
 
14
- export type DownloadRefDoc = (version: string) => Promise<Uint8Array>;
14
+ // Download by the *resolved* URL (channel-aware), not by re-deriving it from the
15
+ // version: stable is the GitHub release URL, beta/alpha are the channel-head
16
+ // archive on d.defold.com.
17
+ export type DownloadRefDoc = (url: string) => Promise<Uint8Array>;
15
18
 
16
19
  export type LocateLocalRefDoc = (version: string) => string | null;
17
20
 
21
+ export type RefDocChannel = "stable" | "beta" | "alpha";
22
+
23
+ export const DEFAULT_REF_DOC_CHANNEL: RefDocChannel = "stable";
24
+
25
+ export const channelInfoUrl = (channel: RefDocChannel): string =>
26
+ `https://d.defold.com/${channel}/info.json`;
27
+
28
+ // The channel segment is canonical; the bare `archive/<sha>/...` form
29
+ // 301-redirects to `archive/<channel>/<sha>/...`.
30
+ export const channelArchiveUrl = (channel: RefDocChannel, sha1: string): string =>
31
+ `https://d.defold.com/archive/${channel}/${sha1}/engine/share/ref-doc.zip`;
32
+
33
+ export interface ChannelInfo {
34
+ readonly version: string;
35
+ readonly sha1: string;
36
+ }
37
+
38
+ export type FetchChannelInfo = (channel: RefDocChannel) => Promise<ChannelInfo>;
39
+
40
+ export function createFetchChannelInfo(fetchImpl: typeof fetch = fetch): FetchChannelInfo {
41
+ return async (channel) => {
42
+ const url = channelInfoUrl(channel);
43
+ const res = await fetchImpl(url);
44
+ if (!res.ok) {
45
+ throw new Error(
46
+ `channel info fetch failed for '${channel}': ${url} -> ${res.status} ${res.statusText}`,
47
+ );
48
+ }
49
+ const data = (await res.json()) as ChannelInfo;
50
+ return { version: data.version, sha1: data.sha1 };
51
+ };
52
+ }
53
+
54
+ const defaultFetchChannelInfo: FetchChannelInfo = createFetchChannelInfo();
55
+
56
+ export interface RefDocDownloadPlan {
57
+ readonly url: string;
58
+ readonly cacheSubdir: string;
59
+ }
60
+
61
+ // Where the ref-doc bytes live and where they cache. Stable is version-addressed
62
+ // (GitHub release, `<version>` subdir) with no network probe; beta/alpha are
63
+ // channel-head-addressed, so the channel head sha is read from info.json and the
64
+ // cache is scoped `<channel>/<sha1>`.
65
+ export async function resolveDownloadPlan(opts: {
66
+ version: string;
67
+ channel?: RefDocChannel;
68
+ fetchChannelInfo?: FetchChannelInfo;
69
+ }): Promise<RefDocDownloadPlan> {
70
+ const channel = opts.channel ?? DEFAULT_REF_DOC_CHANNEL;
71
+ if (channel === "stable") {
72
+ return { url: refDocUrl(opts.version), cacheSubdir: opts.version };
73
+ }
74
+ const info = await (opts.fetchChannelInfo ?? defaultFetchChannelInfo)(channel);
75
+ return { url: channelArchiveUrl(channel, info.sha1), cacheSubdir: join(channel, info.sha1) };
76
+ }
77
+
18
78
  export function defaultDistributionRoots(
19
79
  platform: NodeJS.Platform,
20
80
  env: NodeJS.ProcessEnv = process.env,
@@ -64,8 +124,7 @@ export function refDocCacheDir(
64
124
  return join(env.XDG_CACHE_HOME ?? join(home(), ".cache"), "defold-typescript", "ref-doc");
65
125
  }
66
126
 
67
- const fetchRefDoc: DownloadRefDoc = async (version) => {
68
- const url = refDocUrl(version);
127
+ const fetchRefDoc: DownloadRefDoc = async (url) => {
69
128
  const res = await fetch(url);
70
129
  if (!res.ok) {
71
130
  throw new Error(`download failed: ${url} -> ${res.status} ${res.statusText}`);
@@ -76,12 +135,19 @@ const fetchRefDoc: DownloadRefDoc = async (version) => {
76
135
  export async function resolveRefDoc(opts: {
77
136
  version: string;
78
137
  cacheDir: string;
138
+ channel?: RefDocChannel;
79
139
  download?: DownloadRefDoc;
80
140
  locate?: LocateLocalRefDoc;
81
141
  readZip?: typeof readZip;
142
+ fetchChannelInfo?: FetchChannelInfo;
82
143
  }): Promise<ResolvedRefDoc> {
83
144
  const open = opts.readZip ?? readZip;
84
- const zipPath = join(opts.cacheDir, opts.version, "ref-doc.zip");
145
+ const plan = await resolveDownloadPlan({
146
+ version: opts.version,
147
+ ...(opts.channel ? { channel: opts.channel } : {}),
148
+ ...(opts.fetchChannelInfo ? { fetchChannelInfo: opts.fetchChannelInfo } : {}),
149
+ });
150
+ const zipPath = join(opts.cacheDir, plan.cacheSubdir, "ref-doc.zip");
85
151
  if (existsSync(zipPath)) {
86
152
  return { zip: open(zipPath), zipPath, provenance: "cache" };
87
153
  }
@@ -93,7 +159,7 @@ export async function resolveRefDoc(opts: {
93
159
  return { zip: open(zipPath), zipPath, provenance: "local" };
94
160
  }
95
161
  const download = opts.download ?? fetchRefDoc;
96
- const bytes = await download(opts.version);
162
+ const bytes = await download(plan.url);
97
163
  mkdirSync(dirname(zipPath), { recursive: true });
98
164
  writeFileSync(zipPath, bytes);
99
165
  return { zip: open(zipPath), zipPath, provenance: "download" };
@@ -2,10 +2,12 @@ import { resolve } from "node:path";
2
2
  import { DEFOLD_TYPE_MAP } from "../src/core-types";
3
3
  import {
4
4
  ARBITRARY_TABLE_SLOTS,
5
+ applyNestedFieldCurations,
5
6
  buildTableDocResolver,
6
7
  HOMOGENEOUS_ARRAY_SLOTS,
7
8
  MAPPING_TABLE_SLOTS,
8
9
  type NestedMapping,
10
+ OVERLOAD_COVERED_SKIPS,
9
11
  parseTableFields,
10
12
  recoverCallbackSignature,
11
13
  TABLE_SLOT_CURATIONS,
@@ -117,6 +119,7 @@ function auditEntry(
117
119
  mappingSlot?: { key: string; value: string },
118
120
  homogeneousElement?: string | readonly string[],
119
121
  tableSlotCuration?: TableSlotCuration,
122
+ slot?: { element: string; kind: "param" | "return"; name: string },
120
123
  ) => {
121
124
  for (const token of types) {
122
125
  // A `table` slot whose doc carries a parseable `<dl>` field list is
@@ -138,12 +141,22 @@ function auditEntry(
138
141
  // (`LuaMap<K, LuaMap<K, V>>`) feeds the outer key plus the inner
139
142
  // key/value tokens — so an unmapped token in any arm still surfaces.
140
143
  if (typeof tableSlotCuration.value === "string") {
141
- considerTypes([tableSlotCuration.key, tableSlotCuration.value]);
144
+ // The value may be a `T | U` union token (render.clear's
145
+ // `number | vector4`); split on `|` exactly as the emit branch does
146
+ // so each token is checked against DEFOLD_TYPE_MAP individually and a
147
+ // single-token value is unaffected.
148
+ considerTypes([
149
+ tableSlotCuration.key,
150
+ ...tableSlotCuration.value.split("|").map((token) => token.trim()),
151
+ ]);
142
152
  } else if (Array.isArray(tableSlotCuration.value)) {
143
153
  considerTypes([tableSlotCuration.key]);
144
154
  for (const field of tableSlotCuration.value) {
145
155
  if (field.fields !== undefined) {
146
- for (const nested of field.fields) considerTypes(nested.types);
156
+ for (const nested of field.fields) {
157
+ if (nested.numberList === true) continue;
158
+ considerTypes(nested.types);
159
+ }
147
160
  } else if (field.numberList !== true) {
148
161
  considerTypes(field.types);
149
162
  }
@@ -174,7 +187,10 @@ function auditEntry(
174
187
  if (tableSlotCuration?.kind === "object" || tableSlotCuration?.kind === "array-object") {
175
188
  for (const field of tableSlotCuration.fields) {
176
189
  if (field.fields !== undefined) {
177
- for (const nested of field.fields) considerTypes(nested.types);
190
+ for (const nested of field.fields) {
191
+ if (nested.numberList === true) continue;
192
+ considerTypes(nested.types);
193
+ }
178
194
  } else if (field.numberList !== true) {
179
195
  considerTypes(field.types);
180
196
  }
@@ -187,20 +203,35 @@ function auditEntry(
187
203
  );
188
204
  continue;
189
205
  }
190
- const fields = doc !== undefined ? parseTableFields(doc, resolver) : null;
206
+ const parsed = doc !== undefined ? parseTableFields(doc, resolver) : null;
207
+ const fields =
208
+ parsed !== null && slot !== undefined
209
+ ? applyNestedFieldCurations(slot.element, slot.kind, slot.name, parsed)
210
+ : parsed;
191
211
  if (fields !== null) {
192
212
  // A recovered field carrying nested fields (the mixed `<dl>`+`<ul>`
193
- // shape) emits a nested object, not a `Record` — recurse into the
194
- // nested field types instead of counting the nested `table`.
213
+ // shape, or an injected nested-field curation) emits a nested object,
214
+ // not a `Record` recurse into the nested field types instead of
215
+ // counting the nested `table`. A nested number-list member is recovered
216
+ // as `number[]`, so skip it the same way the top-level branch does.
217
+ // The function-level arbitraryTable / mappingSlot / homogeneousElement
218
+ // flags propagate to the recursion so a slot on an ARBITRARY_TABLE_SLOTS
219
+ // element (or any other function-level reclassification) does not
220
+ // re-count its parsed sub-tables. The parser-recovered field has no
221
+ // tableSlotCuration, so the per-slot curation lookup is irrelevant
222
+ // here.
195
223
  for (const field of fields) {
196
224
  if (field.fields !== undefined) {
197
- for (const nested of field.fields) considerTypes(nested.types);
225
+ for (const nested of field.fields) {
226
+ if (nested.numberList === true) continue;
227
+ considerTypes(nested.types, undefined, arbitraryTable);
228
+ }
198
229
  } else if (field.numberList === true) {
199
230
  // Recovered as `number[]` by inlineTableType — no longer a
200
231
  // `Record`, so skip it. Re-counting its `table` token here would
201
232
  // double-count a slot the emitted surface no longer loses.
202
233
  } else {
203
- considerTypes(field.types);
234
+ considerTypes(field.types, undefined, arbitraryTable);
204
235
  }
205
236
  }
206
237
  continue;
@@ -292,6 +323,9 @@ function auditEntry(
292
323
  mappingSlot,
293
324
  homogeneousElement,
294
325
  tableSlotCuration,
326
+ typeof element.name === "string" && typeof param.name === "string"
327
+ ? { element: element.name, kind: "param", name: param.name }
328
+ : undefined,
295
329
  );
296
330
  // Residual: a doc-optional param the emitter cannot mark `?` because a
297
331
  // required param follows it. The trailing-run cutoff must match
@@ -310,6 +344,9 @@ function auditEntry(
310
344
  mappingSlot,
311
345
  homogeneousElement,
312
346
  tableSlotCuration,
347
+ typeof element.name === "string" && typeof ret.name === "string"
348
+ ? { element: element.name, kind: "return", name: ret.name }
349
+ : undefined,
313
350
  );
314
351
  }
315
352
  }
@@ -319,8 +356,9 @@ function auditEntry(
319
356
  unknownTokens: [...unknown].sort(),
320
357
  recordTables,
321
358
  multiReturn,
322
- droppedMembers: generateModuleDeclaration(entry, { knownConstantFqns: NO_KNOWN_CONSTANTS })
323
- .dropped.length,
359
+ droppedMembers: generateModuleDeclaration(entry, {
360
+ knownConstantFqns: NO_KNOWN_CONSTANTS,
361
+ }).dropped.filter((name) => !OVERLOAD_COVERED_SKIPS.has(name)).length,
324
362
  optionalAsRequired,
325
363
  };
326
364
  }