@defold-typescript/types 0.49.0 → 0.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/api-availability.json +696 -624
  2. package/api-signatures.json +6799 -160
  3. package/api-targets.json +393 -2
  4. package/examples/translations.json +12 -0
  5. package/generated/b2d.d.ts +15 -0
  6. package/generated/bullet3d.d.ts +16 -0
  7. package/generated/font.d.ts +7 -3
  8. package/generated/graphics.d.ts +7 -0
  9. package/generated/gui.d.ts +5 -4
  10. package/generated/label.d.ts +3 -3
  11. package/generated/render.d.ts +16 -1
  12. package/generated/versions/defold-1.13.2/b2d.d.ts +709 -0
  13. package/generated/versions/defold-1.13.2/b2d_body.d.ts +593 -0
  14. package/generated/versions/defold-1.13.2/b2d_chain.d.ts +105 -0
  15. package/generated/versions/defold-1.13.2/b2d_fixture.d.ts +175 -0
  16. package/generated/versions/defold-1.13.2/b2d_joint.d.ts +1301 -0
  17. package/generated/versions/defold-1.13.2/b2d_shape.d.ts +423 -0
  18. package/generated/versions/defold-1.13.2/b2d_world.d.ts +308 -0
  19. package/generated/versions/defold-1.13.2/buffer.d.ts +192 -0
  20. package/generated/versions/defold-1.13.2/bullet3d.d.ts +91 -0
  21. package/generated/versions/defold-1.13.2/bullet3d_collision_object.d.ts +433 -0
  22. package/generated/versions/defold-1.13.2/bullet3d_constraint.d.ts +818 -0
  23. package/generated/versions/defold-1.13.2/bullet3d_rigid_body.d.ts +429 -0
  24. package/generated/versions/defold-1.13.2/bullet3d_shape.d.ts +123 -0
  25. package/generated/versions/defold-1.13.2/bullet3d_world.d.ts +492 -0
  26. package/generated/versions/defold-1.13.2/camera.d.ts +300 -0
  27. package/generated/versions/defold-1.13.2/collectionfactory.d.ts +148 -0
  28. package/generated/versions/defold-1.13.2/collectionproxy.d.ts +109 -0
  29. package/generated/versions/defold-1.13.2/compute.d.ts +107 -0
  30. package/generated/versions/defold-1.13.2/crash.d.ts +157 -0
  31. package/generated/versions/defold-1.13.2/editor-vm/http.d.ts +139 -0
  32. package/generated/versions/defold-1.13.2/editor-vm/image.d.ts +48 -0
  33. package/generated/versions/defold-1.13.2/editor-vm/json.d.ts +21 -0
  34. package/generated/versions/defold-1.13.2/editor-vm/localization.d.ts +42 -0
  35. package/generated/versions/defold-1.13.2/editor-vm/tilemap_tiles.d.ts +126 -0
  36. package/generated/versions/defold-1.13.2/editor-vm/zip.d.ts +38 -0
  37. package/generated/versions/defold-1.13.2/editor-vm/zlib.d.ts +24 -0
  38. package/generated/versions/defold-1.13.2/editor.d.ts +2177 -0
  39. package/generated/versions/defold-1.13.2/factory.d.ts +121 -0
  40. package/generated/versions/defold-1.13.2/font.d.ts +123 -0
  41. package/generated/versions/defold-1.13.2/go.d.ts +1166 -0
  42. package/generated/versions/defold-1.13.2/graphics.d.ts +613 -0
  43. package/generated/versions/defold-1.13.2/gui.d.ts +2212 -0
  44. package/generated/versions/defold-1.13.2/html5.d.ts +55 -0
  45. package/generated/versions/defold-1.13.2/http.d.ts +83 -0
  46. package/generated/versions/defold-1.13.2/image.d.ts +181 -0
  47. package/generated/versions/defold-1.13.2/index.d.ts +63 -0
  48. package/generated/versions/defold-1.13.2/json.d.ts +94 -0
  49. package/generated/versions/defold-1.13.2/kinds/editor-script.d.ts +14 -0
  50. package/generated/versions/defold-1.13.2/label.d.ts +164 -0
  51. package/generated/versions/defold-1.13.2/liveupdate.d.ts +143 -0
  52. package/generated/versions/defold-1.13.2/material.d.ts +490 -0
  53. package/generated/versions/defold-1.13.2/model.d.ts +287 -0
  54. package/generated/versions/defold-1.13.2/msg.d.ts +10 -0
  55. package/generated/versions/defold-1.13.2/particlefx.d.ts +155 -0
  56. package/generated/versions/defold-1.13.2/physics.d.ts +789 -0
  57. package/generated/versions/defold-1.13.2/profiler.d.ts +228 -0
  58. package/generated/versions/defold-1.13.2/render.d.ts +1153 -0
  59. package/generated/versions/defold-1.13.2/resource.d.ts +1628 -0
  60. package/generated/versions/defold-1.13.2/socket.d.ts +797 -0
  61. package/generated/versions/defold-1.13.2/sound.d.ts +323 -0
  62. package/generated/versions/defold-1.13.2/sprite.d.ts +179 -0
  63. package/generated/versions/defold-1.13.2/sys.d.ts +754 -0
  64. package/generated/versions/defold-1.13.2/tilemap.d.ts +206 -0
  65. package/generated/versions/defold-1.13.2/timer.d.ts +136 -0
  66. package/generated/versions/defold-1.13.2/types.d.ts +61 -0
  67. package/generated/versions/defold-1.13.2/vmath.d.ts +775 -0
  68. package/generated/versions/defold-1.13.2/window.d.ts +196 -0
  69. package/generated/versions/defold-1.13.2/zlib.d.ts +41 -0
  70. package/identity-vocabulary.json +1 -0
  71. package/package.json +2 -2
  72. package/scripts/import-defold-release.ts +3 -31
  73. package/scripts/materialize-version.ts +13 -7
  74. package/scripts/regen.ts +63 -29
  75. package/scripts/sync-api-docs.ts +31 -2
  76. package/src/emit-dts.ts +25 -3
@@ -0,0 +1,196 @@
1
+ /** @noSelfInFile */
2
+ declare global {
3
+ /**
4
+ * Functions and constants to access the window, window event listeners
5
+ * and screen dimming.
6
+ */
7
+ namespace window {
8
+ /**
9
+ * Width and height are present for window.WINDOW_EVENT_RESIZED and
10
+ * absent for other window events.
11
+ */
12
+ interface event_data {
13
+ /**
14
+ * Window width after a resize.
15
+ */
16
+ width?: number;
17
+ /**
18
+ * Window height after a resize.
19
+ */
20
+ height?: number;
21
+ }
22
+ /**
23
+ * Window safe-area data
24
+ */
25
+ interface safe_area {
26
+ /**
27
+ * Safe-area x-coordinate.
28
+ */
29
+ x: number;
30
+ /**
31
+ * Safe-area y-coordinate.
32
+ */
33
+ y: number;
34
+ /**
35
+ * Safe-area width.
36
+ */
37
+ width: number;
38
+ /**
39
+ * Safe-area height.
40
+ */
41
+ height: number;
42
+ /**
43
+ * Inset from the left window edge.
44
+ */
45
+ inset_left: number;
46
+ /**
47
+ * Inset from the top window edge.
48
+ */
49
+ inset_top: number;
50
+ /**
51
+ * Inset from the right window edge.
52
+ */
53
+ inset_right: number;
54
+ /**
55
+ * Inset from the bottom window edge.
56
+ */
57
+ inset_bottom: number;
58
+ }
59
+ type DimModeState = typeof window.DIMMING_UNKNOWN | typeof window.DIMMING_ON | typeof window.DIMMING_OFF;
60
+ type DimModeStateSettable = typeof window.DIMMING_ON | typeof window.DIMMING_OFF;
61
+ type DIMMING = typeof window.DIMMING_OFF | typeof window.DIMMING_ON | typeof window.DIMMING_UNKNOWN;
62
+ type WINDOW_EVENT = typeof window.WINDOW_EVENT_DEICONIFIED | typeof window.WINDOW_EVENT_FOCUS_GAINED | typeof window.WINDOW_EVENT_FOCUS_LOST | typeof window.WINDOW_EVENT_ICONIFIED | typeof window.WINDOW_EVENT_RESIZED;
63
+ /**
64
+ * dimming mode off Dimming mode is used to control whether or not a mobile device should dim the screen after a period without user interaction.
65
+ */
66
+ const DIMMING_OFF: number & { readonly __brand: "window.DIMMING_OFF" };
67
+ /**
68
+ * dimming mode on Dimming mode is used to control whether or not a mobile device should dim the screen after a period without user interaction.
69
+ */
70
+ const DIMMING_ON: number & { readonly __brand: "window.DIMMING_ON" };
71
+ /**
72
+ * dimming mode unknown Dimming mode is used to control whether or not a mobile device should dim the screen after a period without user interaction. This mode indicates that the dim mode can't be determined, or that the platform doesn't support dimming.
73
+ */
74
+ const DIMMING_UNKNOWN: number & { readonly __brand: "window.DIMMING_UNKNOWN" };
75
+ /**
76
+ * deiconified window event This event is sent to a window event listener when the game window or app screen is restored after being iconified.
77
+ */
78
+ const WINDOW_EVENT_DEICONIFIED: number & { readonly __brand: "window.WINDOW_EVENT_DEICONIFIED" };
79
+ /**
80
+ * focus gained window event This event is sent to a window event listener when the game window or app screen has gained focus. This event is also sent at game startup and the engine gives focus to the game.
81
+ */
82
+ const WINDOW_EVENT_FOCUS_GAINED: number & { readonly __brand: "window.WINDOW_EVENT_FOCUS_GAINED" };
83
+ /**
84
+ * focus lost window event This event is sent to a window event listener when the game window or app screen has lost focus.
85
+ */
86
+ const WINDOW_EVENT_FOCUS_LOST: number & { readonly __brand: "window.WINDOW_EVENT_FOCUS_LOST" };
87
+ /**
88
+ * Deprecated misspelling of `window.WINDOW_EVENT_ICONIFIED`, with the same value. The engine still registers it.
89
+ */
90
+ const WINDOW_EVENT_ICONFIED: typeof window.WINDOW_EVENT_ICONIFIED;
91
+ /**
92
+ * iconify window event This event is sent to a window event listener when the game window or app screen is iconified (reduced to an application icon in a toolbar, application tray or similar).
93
+ */
94
+ const WINDOW_EVENT_ICONIFIED: number & { readonly __brand: "window.WINDOW_EVENT_ICONIFIED" };
95
+ /**
96
+ * resized window event This event is sent to a window event listener when the game window or app screen is resized. The new size is passed along in the data field to the event listener.
97
+ */
98
+ const WINDOW_EVENT_RESIZED: number & { readonly __brand: "window.WINDOW_EVENT_RESIZED" };
99
+ /**
100
+ * Returns the current dimming mode set on a mobile device.
101
+ * The dimming mode specifies whether or not a mobile device should dim the screen after a period without user interaction.
102
+ * On platforms that does not support dimming, `window.DIMMING_UNKNOWN` is always returned.
103
+ *
104
+ * @returns The mode for screen dimming
105
+ */
106
+ function get_dim_mode(): window.DIMMING;
107
+ /**
108
+ * This returns the content scale of the current display.
109
+ *
110
+ * @returns The display scale
111
+ */
112
+ function get_display_scale(): number;
113
+ /**
114
+ * This returns the current lock state of the mouse cursor
115
+ *
116
+ * @returns The lock state
117
+ */
118
+ function get_mouse_lock(): boolean;
119
+ /**
120
+ * This returns the safe area rectangle (x, y, width, height) and the inset
121
+ * values relative to the window edges. On platforms without a safe area,
122
+ * this returns the full window size and zero insets.
123
+ *
124
+ * @returns safe area data
125
+ */
126
+ function get_safe_area(): window.safe_area;
127
+ /**
128
+ * This returns the current window size (width and height).
129
+ */
130
+ function get_size(): LuaMultiReturn<[number, number]>;
131
+ /**
132
+ * Sets the dimming mode on a mobile device.
133
+ * The dimming mode specifies whether or not a mobile device should dim the screen after a period without user interaction. The dimming mode will only affect the mobile device while the game is in focus on the device, but not when the game is running in the background.
134
+ * This function has no effect on platforms that does not support dimming.
135
+ *
136
+ * @param mode - The mode for screen dimming
137
+ */
138
+ function set_dim_mode(mode: window.DimModeStateSettable): void;
139
+ /**
140
+ * Sets a window event listener. Only one window event listener can be set at a time.
141
+ *
142
+ * @param callback - A callback which receives info about window events. Pass an empty function or `nil` if you no longer wish to receive callbacks.
143
+ * @example
144
+ * ```ts
145
+ * export default defineScript({
146
+ * init() {
147
+ * window.set_listener((self, event, data) => {
148
+ * if (event === window.WINDOW_EVENT_FOCUS_LOST) {
149
+ * print("window.WINDOW_EVENT_FOCUS_LOST");
150
+ * } else if (event === window.WINDOW_EVENT_FOCUS_GAINED) {
151
+ * print("window.WINDOW_EVENT_FOCUS_GAINED");
152
+ * } else if (event === window.WINDOW_EVENT_ICONIFIED) {
153
+ * print("window.WINDOW_EVENT_ICONIFIED");
154
+ * } else if (event === window.WINDOW_EVENT_DEICONIFIED) {
155
+ * print("window.WINDOW_EVENT_DEICONIFIED");
156
+ * } else if (event === window.WINDOW_EVENT_RESIZED) {
157
+ * print("Window resized: ", data.width, data.height);
158
+ * }
159
+ * });
160
+ * },
161
+ * });
162
+ * ```
163
+ */
164
+ function set_listener(callback: ((self: unknown, event: (typeof window)[Extract<keyof typeof window, `WINDOW_EVENT_${string}`>], data: Record<string | number, unknown>) => void) | undefined): void;
165
+ /**
166
+ * Set the locking state for current mouse cursor on a PC platform.
167
+ * This function locks or unlocks the mouse cursor to the center point of the window. While the cursor is locked,
168
+ * mouse position updates will still be sent to the scripts as usual.
169
+ *
170
+ * @param flag - The lock state for the mouse cursor
171
+ */
172
+ function set_mouse_lock(flag: boolean): void;
173
+ /**
174
+ * Sets the window position.
175
+ *
176
+ * @param x - Horizontal position of window
177
+ * @param y - Vertical position of window
178
+ */
179
+ function set_position(x: number, y: number): void;
180
+ /**
181
+ * Sets the window size. Works on desktop platforms only.
182
+ *
183
+ * @param width - Width of window
184
+ * @param height - Height of window
185
+ */
186
+ function set_size(width: number, height: number): void;
187
+ /**
188
+ * Sets the window title. Works on desktop platforms.
189
+ *
190
+ * @param title - The title, encoded as UTF-8
191
+ */
192
+ function set_title(title: string): void;
193
+ }
194
+ }
195
+
196
+ export {};
@@ -0,0 +1,41 @@
1
+ /** @noSelfInFile */
2
+ declare global {
3
+ /**
4
+ * Functions for compression and decompression of string buffers.
5
+ */
6
+ namespace zlib {
7
+ /**
8
+ * A lua error is raised is on error
9
+ *
10
+ * @param buf - buffer to deflate
11
+ * @returns deflated buffer
12
+ * @example
13
+ * ```ts
14
+ * const data = "This is a string with uncompressed data.";
15
+ * const compressed_data = zlib.deflate(data);
16
+ * let s = "";
17
+ * for (const c of compressed_data) {
18
+ * s = s + "\\" + c.charCodeAt(0);
19
+ * }
20
+ * print(s); //> \120\94\11\201\200\44\86\0\162\68\133\226\146\162 ...
21
+ * ```
22
+ */
23
+ function deflate(buf: string): string;
24
+ /**
25
+ * A lua error is raised is on error
26
+ *
27
+ * @param buf - buffer to inflate
28
+ * @returns inflated buffer
29
+ * @example
30
+ * ```ts
31
+ * const data =
32
+ * "\x78\x5e\x0b\xc9\xc8\x2c\x56\x00\xa2\x44\x85\xe2\x92\xa2\xcc\xbc\x74\x85\xf2\xcc\x92\x0c\x85\xd2\xbc\xe4\xfc\xdc\x82\xa2\xd4\xe2\xe2\xd4\x14\x85\x94\xc4\x92\x44\x3d\x00\x2c\x43\x0e\xc9";
33
+ * const uncompressed_data = zlib.inflate(data);
34
+ * print(uncompressed_data); //> This is a string with uncompressed data.
35
+ * ```
36
+ */
37
+ function inflate(buf: string): string;
38
+ }
39
+ }
40
+
41
+ export {};
@@ -158,6 +158,7 @@
158
158
  "graphics.COMPRESSION_TYPE": "number",
159
159
  "graphics.CONTEXT_FEATURE": "number",
160
160
  "graphics.COORDINATE_SPACE": "number",
161
+ "graphics.CUBEMAP_FACE": "number",
161
162
  "graphics.DATA_TYPE": "number",
162
163
  "graphics.FACE_TYPE": "number",
163
164
  "graphics.SEMANTIC_TYPE": "number",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defold-typescript/types",
3
- "version": "0.49.0",
3
+ "version": "0.50.1",
4
4
  "description": "TypeScript types for the Defold engine's Lua APIs.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -116,7 +116,7 @@
116
116
  "import-defold-release": "bun scripts/import-defold-release.ts",
117
117
  "sync-api-docs": "bun scripts/sync-api-docs.ts",
118
118
  "sync-engine-bindings": "bun scripts/sync-engine-bindings.ts",
119
- "ref-doc-delta": "bun scripts/ref-doc-delta.ts --target defold-1.9.8 --namespace label --present label.get_text --absent label.set_text"
119
+ "ref-doc-delta": "bun scripts/ref-doc-delta.ts --target defold-1.9.8 --namespace label --present label.get_text --present label.set_text --absent label.get_layout_objects"
120
120
  },
121
121
  "dependencies": {
122
122
  "lua-types": "^2.13.1",
@@ -7,6 +7,7 @@ import {
7
7
  EDITOR_MANIFEST,
8
8
  type ExtractedFixture,
9
9
  extractAnnotationFixtures,
10
+ groupDocsByNamespace,
10
11
  IGNORED_UPSTREAM,
11
12
  mergeApiDocs,
12
13
  readZip,
@@ -15,7 +16,6 @@ import {
15
16
  } from "./sync-api-docs";
16
17
 
17
18
  const EXACT_VERSION = /^\d+\.\d+\.\d+$/;
18
- const LUA_NAMESPACE = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/;
19
19
  const DECLARED_NAME_KINDS = new Set(["CONSTANT", "ENUM", "STRUCT", "TYPEDEF", "CLASS"]);
20
20
  const PACKAGE_ROOT = resolve(import.meta.dir, "..");
21
21
 
@@ -75,11 +75,6 @@ interface RawElement extends Record<string, unknown> {
75
75
  name?: string;
76
76
  }
77
77
 
78
- interface RawApiDoc extends Record<string, unknown> {
79
- info?: { namespace?: string };
80
- elements?: RawElement[];
81
- }
82
-
83
78
  export interface ReleaseBaselineModule {
84
79
  readonly namespace: string;
85
80
  readonly fixture: string;
@@ -177,20 +172,6 @@ function isRecord(value: unknown): value is Record<string, unknown> {
177
172
  return typeof value === "object" && value !== null && !Array.isArray(value);
178
173
  }
179
174
 
180
- function parsedDoc(raw: string): RawApiDoc | null {
181
- try {
182
- const value: unknown = JSON.parse(raw);
183
- return isRecord(value) ? (value as RawApiDoc) : null;
184
- } catch {
185
- return null;
186
- }
187
- }
188
-
189
- function namespaceOf(doc: RawApiDoc): string | null {
190
- const namespace = isRecord(doc.info) ? doc.info.namespace : undefined;
191
- return typeof namespace === "string" && LUA_NAMESPACE.test(namespace) ? namespace : null;
192
- }
193
-
194
175
  function elementsOf(doc: unknown): RawElement[] {
195
176
  if (!isRecord(doc) || !Array.isArray(doc.elements)) return [];
196
177
  return doc.elements.filter(isRecord) as RawElement[];
@@ -297,16 +278,7 @@ export function buildReleaseImportPlan(input: {
297
278
  throw new Error(`expected an exact Defold release version, received '${input.version}'`);
298
279
  }
299
280
 
300
- const grouped = new Map<string, Array<{ entry: string; doc: RawApiDoc }>>();
301
- for (const zipEntry of [...input.zip.entries()].sort()) {
302
- const doc = parsedDoc(input.zip.read(zipEntry));
303
- if (!doc) continue;
304
- const namespace = namespaceOf(doc);
305
- if (!namespace) continue;
306
- const group = grouped.get(namespace) ?? [];
307
- group.push({ entry: zipEntry, doc });
308
- grouped.set(namespace, group);
309
- }
281
+ const grouped = groupDocsByNamespace(input.zip);
310
282
 
311
283
  const baselineModules = [...input.baseline.modules, ...input.baseline.luaStdlib];
312
284
  const generatedNamespaces = new Set(input.baseline.modules.map((module) => module.namespace));
@@ -319,7 +291,7 @@ export function buildReleaseImportPlan(input: {
319
291
  };
320
292
 
321
293
  const sources: ReleaseSource[] = incomingNamespaces.map((namespace) => {
322
- const group = grouped.get(namespace) as Array<{ entry: string; doc: RawApiDoc }>;
294
+ const group = grouped.get(namespace) ?? [];
323
295
  return {
324
296
  namespace,
325
297
  entries: group.map(({ entry }) => entry).sort(),
@@ -11,6 +11,7 @@ import {
11
11
  loadSrcAugmentations,
12
12
  loadTargetEditorModules,
13
13
  MESSAGES_MANIFEST,
14
+ type ModuleManifestEntry,
14
15
  type ResolveTargetOptions,
15
16
  resolveTargetModules,
16
17
  } from "./regen";
@@ -138,13 +139,18 @@ export async function buildVersionedSurfaceFiles(
138
139
  opts.augmentations ?? loadSrcAugmentations(opts.resolveOpts?.packageRoot ?? undefined)
139
140
  ).filter((file) => kept(file.path));
140
141
 
141
- const files: VersionedSurfaceFile[] = [...modules, ...editorModules].map((entry) => ({
142
- path: entry.outFile,
143
- contents: generateModuleDeclaration({
144
- ...entry,
145
- importsFrom: surfaceCoreTypesImport(entry.outFile),
146
- }).contents,
147
- }));
142
+ // Re-rooting an entry makes it a new object no committed manifest holds, so
143
+ // each lane is handed to the emit as the surface it routes: a type one module
144
+ // declares for another, or names from another, resolves only across the set.
145
+ const rerooted = (entries: readonly ModuleManifestEntry[]): ModuleManifestEntry[] =>
146
+ entries.map((entry) => ({ ...entry, importsFrom: surfaceCoreTypesImport(entry.outFile) }));
147
+ const files: VersionedSurfaceFile[] = [rerooted(modules), rerooted(editorModules)].flatMap(
148
+ (surface) =>
149
+ surface.map((entry) => ({
150
+ path: entry.outFile,
151
+ contents: generateModuleDeclaration(entry, { surface }).contents,
152
+ })),
153
+ );
148
154
 
149
155
  files.push(...augmentations);
150
156
  files.push({ path: "core-types.d.ts", contents: CORE_TYPES_REEXPORT });
package/scripts/regen.ts CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  import { loadTranslations } from "./example-store-io";
43
43
  import { synthesizeProseConstants } from "./prose-constants";
44
44
  import { restoreSharedHandleMethods } from "./shared-handle-methods";
45
- import { type readZip, SYNC_MANIFEST, type SyncManifestEntry } from "./sync-api-docs";
45
+ import { groupDocsByNamespace, mergeApiDocs, type readZip } from "./sync-api-docs";
46
46
 
47
47
  export interface ApiTargetModule {
48
48
  readonly namespace: string;
@@ -120,6 +120,12 @@ function withSkipRules(entry: ModuleManifestEntry, module: ApiTargetModule): Mod
120
120
  };
121
121
  }
122
122
 
123
+ // Shared by both module sources: a step applied in one branch alone gives a
124
+ // ref-doc surface different members from a committed one.
125
+ function restoreOmittedMembers(modules: readonly ModuleManifestEntry[]): ModuleManifestEntry[] {
126
+ return restoreSharedHandleMethods(synthesizeProseConstants(modules));
127
+ }
128
+
123
129
  export function loadTargetModules(
124
130
  target: ApiTarget,
125
131
  packageRoot: string = PACKAGE_ROOT,
@@ -142,7 +148,7 @@ export function loadTargetModules(
142
148
  };
143
149
  return withSkipRules(entry, module);
144
150
  });
145
- return restoreSharedHandleMethods(synthesizeProseConstants(modules));
151
+ return restoreOmittedMembers(modules);
146
152
  }
147
153
 
148
154
  export interface ModuleManifestEntry {
@@ -174,7 +180,6 @@ export interface ResolveTargetOptions {
174
180
  readonly cacheDir?: string;
175
181
  readonly download?: DownloadRefDoc;
176
182
  readonly readZip?: typeof readZip;
177
- readonly syncManifest?: readonly SyncManifestEntry[];
178
183
  readonly packageRoot?: string;
179
184
  readonly channel?: RefDocChannel;
180
185
  readonly fetchChannelInfo?: FetchChannelInfo;
@@ -182,8 +187,8 @@ export interface ResolveTargetOptions {
182
187
 
183
188
  // Source-aware module resolution: a `null`-source target reads committed
184
189
  // fixtures from disk (delegates to loadTargetModules); a `ref-doc` target
185
- // resolves its docs on demand from the version's ref-doc zip, keyed by the
186
- // SYNC_MANIFEST namespace -> zip-entry map.
190
+ // resolves its docs on demand from the version's ref-doc zip, reading every
191
+ // doc the zip declares for the namespace, merged.
187
192
  export async function resolveTargetModules(
188
193
  target: ApiTarget,
189
194
  opts: ResolveTargetOptions = {},
@@ -200,24 +205,24 @@ export async function resolveTargetModules(
200
205
  ...(opts.readZip ? { readZip: opts.readZip } : {}),
201
206
  ...(opts.fetchChannelInfo ? { fetchChannelInfo: opts.fetchChannelInfo } : {}),
202
207
  });
203
- const syncManifest = opts.syncManifest ?? SYNC_MANIFEST;
208
+ const grouped = groupDocsByNamespace(zip);
204
209
  const modules = target.modules.map((module) => {
205
- const sync = syncManifest.find((s) => s.namespace === module.namespace);
206
- if (!sync) {
210
+ const group = grouped.get(module.namespace);
211
+ if (!group) {
207
212
  throw new Error(
208
- `api-targets.json: target "${target.id}" module "${module.namespace}": no SYNC_MANIFEST zip entry`,
213
+ `api-targets.json: target "${target.id}" module "${module.namespace}": no doc in the ref-doc zip declares this namespace`,
209
214
  );
210
215
  }
211
216
  const entry: ModuleManifestEntry = {
212
217
  namespace: module.namespace,
213
- doc: JSON.parse(zip.read(sync.zipEntry)),
218
+ doc: mergeApiDocs(group.map((item) => item.doc)),
214
219
  outFile: module.outFile,
215
220
  importsFrom: target.coreTypesImport,
216
221
  sourceProvenance: provenance,
217
222
  };
218
223
  return withSkipRules(entry, module);
219
224
  });
220
- return synthesizeProseConstants(modules);
225
+ return restoreOmittedMembers(modules);
221
226
  }
222
227
 
223
228
  const API_TARGETS = loadApiTargets();
@@ -451,6 +456,10 @@ export interface GenerateOptions {
451
456
  translations?: TranslationStore;
452
457
  urlParameters?: UrlParameterTable;
453
458
  registeredConstants?: ReadonlySet<string>;
459
+ // The modules the entry is emitted beside, the entry among them. Routing
460
+ // reads this set instead of looking the entry up in a committed manifest, so
461
+ // a surface built with its own import root still routes as a whole.
462
+ surface?: readonly ModuleManifestEntry[];
454
463
  }
455
464
 
456
465
  interface PreparedGeneratedModule {
@@ -472,7 +481,7 @@ function prepareGeneratedModule(
472
481
  entry: ModuleManifestEntry,
473
482
  options?: GenerateOptions,
474
483
  ): PreparedGeneratedModule {
475
- const routed = routedModuleOf(entry);
484
+ const routed = routedModuleOf(entry, options?.surface);
476
485
  const { module, dropped } = routed?.prepared ?? withheldApiModule(entry);
477
486
  const knownConstantFqns = options?.knownConstantFqns ?? collectConstantFqns();
478
487
  const translations = options?.translations ?? loadTranslations();
@@ -495,36 +504,61 @@ interface RoutedSurface {
495
504
  }
496
505
 
497
506
  const routedSurfaces = new Map<string, RoutedSurface>();
507
+ const routedCallerSurfaces = new WeakMap<readonly ModuleManifestEntry[], RoutedSurface>();
508
+
509
+ function routeSurface(entries: readonly ModuleManifestEntry[]): RoutedSurface {
510
+ const withheld = entries.map(withheldApiModule);
511
+ const routed = routeTypeDeclarations(withheld.map(({ module }) => module));
512
+ return {
513
+ modules: new Map(
514
+ entries.map((member, index) => [
515
+ member,
516
+ {
517
+ module: routed[index] as ApiModule,
518
+ dropped: (withheld[index] as { dropped: string[] }).dropped,
519
+ },
520
+ ]),
521
+ ),
522
+ declaredTypes: declaredTypePaths(routed),
523
+ };
524
+ }
498
525
 
499
526
  // A doc may declare a type another module owns (`b2d.joint.revolute_definition`
500
527
  // in the `b2d` doc) or name one another doc declares (`material.constant_info`
501
528
  // in `compute`), so each committed surface is routed as a whole: the runtime
502
- // modules and the editor modules of one target, never across targets. An entry
503
- // outside every committed manifest routes alone, as a single-module surface.
504
- function routedModuleOf(entry: ModuleManifestEntry):
529
+ // modules and the editor modules of one target, never across targets. A caller
530
+ // that names its own surface routes that set instead. An entry with neither, and
531
+ // outside every committed manifest, routes alone, as a single-module surface.
532
+ function routedModuleOf(
533
+ entry: ModuleManifestEntry,
534
+ callerSurface?: readonly ModuleManifestEntry[],
535
+ ):
505
536
  | {
506
537
  prepared: { module: ApiModule; dropped: string[] };
507
538
  declaredTypes: ReadonlyMap<string, string>;
508
539
  }
509
540
  | undefined {
541
+ if (callerSurface !== undefined) {
542
+ let surface = routedCallerSurfaces.get(callerSurface);
543
+ if (surface === undefined) {
544
+ surface = routeSurface(callerSurface);
545
+ routedCallerSurfaces.set(callerSurface, surface);
546
+ }
547
+ const prepared = surface.modules.get(entry);
548
+ // Falling back to a lone module here would emit `unknown` for every type a
549
+ // sibling declares, which type-checks and so goes unnoticed.
550
+ if (prepared === undefined) {
551
+ throw new Error(
552
+ `module "${entry.namespace}" is not a member of the surface it is emitted in`,
553
+ );
554
+ }
555
+ return { prepared, declaredTypes: surface.declaredTypes };
556
+ }
510
557
  const located = committedSurfaceOf(entry);
511
558
  if (located === undefined) return undefined;
512
559
  let surface = routedSurfaces.get(located.key);
513
560
  if (surface === undefined) {
514
- const withheld = located.entries.map(withheldApiModule);
515
- const routed = routeTypeDeclarations(withheld.map(({ module }) => module));
516
- surface = {
517
- modules: new Map(
518
- located.entries.map((member, index) => [
519
- member,
520
- {
521
- module: routed[index] as ApiModule,
522
- dropped: (withheld[index] as { dropped: string[] }).dropped,
523
- },
524
- ]),
525
- ),
526
- declaredTypes: declaredTypePaths(routed),
527
- };
561
+ surface = routeSurface(located.entries);
528
562
  routedSurfaces.set(located.key, surface);
529
563
  }
530
564
  const prepared = surface.modules.get(located.member);
@@ -11,7 +11,7 @@ import { MODULE_MANIFEST } from "./regen";
11
11
  // model because the per-package `rootDir` boundary forbids a types-package
12
12
  // module importing outside its own tree; `scripts/release-model.test.ts`
13
13
  // correspondence-guards these against the model so drift fails CI.
14
- export const DEFOLD_VERSION = "1.13.2";
14
+ export const DEFOLD_VERSION = "1.14.0";
15
15
  export const refDocUrl = (version = DEFOLD_VERSION): string =>
16
16
  `https://github.com/defold/defold/releases/download/${version}/ref-doc.zip`;
17
17
 
@@ -216,7 +216,7 @@ function editorVm(namespace: string): SyncManifestEntry {
216
216
  function entry(
217
217
  namespace: string,
218
218
  zipEntry: string,
219
- fixture: string = `fixtures/defold-1.13.2/${namespace.replace(/\./g, "_")}_doc.json`,
219
+ fixture: string = `fixtures/defold-1.14.0/${namespace.replace(/\./g, "_")}_doc.json`,
220
220
  mergeEntries?: readonly string[],
221
221
  ): SyncManifestEntry {
222
222
  return { namespace, zipEntry, fixture, ...(mergeEntries ? { mergeEntries } : {}) };
@@ -538,6 +538,35 @@ export interface CoverageInput {
538
538
  ignoredUpstream?: ReadonlyMap<string, string>;
539
539
  }
540
540
 
541
+ // The Lua docs of a zip, grouped by the namespace each one declares. Entry names
542
+ // belong to one release (`label` is one entry in 1.9.8 and two in 1.14.0), so
543
+ // the declared namespace picks the docs. Entries are read in sorted order
544
+ // because `mergeApiDocs` keeps the first doc's `info` and the docs' element
545
+ // order; only `.json` entries are read because each read of a real zip is one
546
+ // `unzip -p` process.
547
+ export function groupDocsByNamespace(
548
+ zip: ZipAccessor,
549
+ ): Map<string, { entry: string; doc: Record<string, unknown> }[]> {
550
+ const grouped = new Map<string, { entry: string; doc: Record<string, unknown> }[]>();
551
+ for (const entry of [...zip.entries()].sort()) {
552
+ if (!entry.endsWith(".json")) continue;
553
+ const raw = zip.read(entry);
554
+ let doc: unknown;
555
+ try {
556
+ doc = JSON.parse(raw);
557
+ } catch {
558
+ continue;
559
+ }
560
+ if (!isRecord(doc)) continue;
561
+ const namespace = isRecord(doc.info) ? doc.info.namespace : undefined;
562
+ if (typeof namespace !== "string" || !LUA_NAMESPACE.test(namespace)) continue;
563
+ const group = grouped.get(namespace) ?? [];
564
+ group.push({ entry, doc });
565
+ grouped.set(namespace, group);
566
+ }
567
+ return grouped;
568
+ }
569
+
541
570
  // Enumerate every `doc/*.json` zip entry, reading its `info.namespace` and the
542
571
  // count of FUNCTION elements. Only function-bearing docs are returned, so
543
572
  // message-only / struct-only docs never reach the coverage guard.
package/src/emit-dts.ts CHANGED
@@ -5639,6 +5639,27 @@ function unionFromTokens(tokens: readonly string[], mapType: DefoldTypeMapper):
5639
5639
  }
5640
5640
 
5641
5641
  const BRAND_ARM = /^(?:number|string) & \{ readonly __brand: "([^"]+)" \}$/;
5642
+ const OPAQUE_ARM = /^Opaque<("[^"]+"(?: \| "[^"]+")*)>$/;
5643
+
5644
+ // Drops each `Opaque` arm whose brands are a proper subset of another `Opaque`
5645
+ // arm's. `Opaque<Name>` holds `Name` in one readonly property, so a narrower
5646
+ // brand set is assignable to a wider one and the narrower arm adds nothing.
5647
+ // An arm of any other shape (`Opaque<"x">[]`, an intersection) is never dropped
5648
+ // and never causes a drop.
5649
+ function withoutSubsumedOpaqueArms(mapped: readonly string[]): string[] {
5650
+ const brandSets = mapped.map((arm) => {
5651
+ const match = OPAQUE_ARM.exec(arm);
5652
+ return match === null ? undefined : new Set((match[1] as string).split(" | "));
5653
+ });
5654
+ return mapped.filter((_, index) => {
5655
+ const own = brandSets[index];
5656
+ if (own === undefined) return true;
5657
+ return !brandSets.some(
5658
+ (other) =>
5659
+ other !== undefined && other.size > own.size && [...own].every((b) => other.has(b)),
5660
+ );
5661
+ });
5662
+ }
5642
5663
 
5643
5664
  // Joins already-mapped arms, collapsing the branded ones to their alias when
5644
5665
  // they are exactly a `CONSTANT_UNION_ALIASES` member set. The alias takes the
@@ -5646,16 +5667,17 @@ const BRAND_ARM = /^(?:number|string) & \{ readonly __brand: "([^"]+)" \}$/;
5646
5667
  // (`gui.Easing | Vector`). Arms that are not constants are untouched, and a set
5647
5668
  // with no row keeps every arm inline.
5648
5669
  function joinUnionArms(mapped: readonly string[]): string {
5670
+ const arms = withoutSubsumedOpaqueArms(mapped);
5649
5671
  const branded: string[] = [];
5650
- for (const arm of mapped) {
5672
+ for (const arm of arms) {
5651
5673
  const match = BRAND_ARM.exec(arm);
5652
5674
  if (match !== null) branded.push(match[1] as string);
5653
5675
  }
5654
5676
  const alias = constantUnionAlias(branded);
5655
- if (alias === undefined) return mapped.join(" | ");
5677
+ if (alias === undefined) return arms.join(" | ");
5656
5678
  const out: string[] = [];
5657
5679
  let placed = false;
5658
- for (const arm of mapped) {
5680
+ for (const arm of arms) {
5659
5681
  if (BRAND_ARM.test(arm)) {
5660
5682
  if (placed) continue;
5661
5683
  out.push(`${alias.home}.${alias.name}`);