@defold-typescript/library-types 0.22.0 → 0.24.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.
Files changed (145) hide show
  1. package/api-doc/boom.json +3870 -0
  2. package/api-doc/{bridge.bridge.json → bridge.json} +754 -844
  3. package/api-doc/bzAnim.json +338 -0
  4. package/api-doc/decore.json +23 -23
  5. package/api-doc/defcon.json +150 -0
  6. package/api-doc/{defmath.defmath.json → defmath.json} +6 -46
  7. package/api-doc/defsave.json +518 -0
  8. package/api-doc/deftest.json +741 -0
  9. package/api-doc/{dicebag.dicebag.json → dicebag.json} +19 -1
  10. package/api-doc/druid.json +249 -1846
  11. package/api-doc/event.json +1074 -0
  12. package/api-doc/gooey.json +2050 -0
  13. package/api-doc/immutable.json +65 -0
  14. package/api-doc/in.accelerometer.json +75 -1
  15. package/api-doc/in.button.json +17 -10
  16. package/api-doc/in.cursor.json +26 -0
  17. package/api-doc/in.gesture.json +401 -6
  18. package/api-doc/in.keyboard.json +2 -0
  19. package/api-doc/in.mapper.json +12 -8
  20. package/api-doc/in.onscreen.json +61 -5
  21. package/api-doc/in.state.json +61 -13
  22. package/api-doc/in.textbox.json +23 -12
  23. package/api-doc/in.triggers.json +336 -0
  24. package/api-doc/lang.json +528 -0
  25. package/api-doc/{event.event.json → log.json} +96 -101
  26. package/api-doc/metrics.fps.json +61 -2
  27. package/api-doc/metrics.mem.json +61 -2
  28. package/api-doc/monarch.monarch.json +249 -7
  29. package/api-doc/monarch.transitions.easings.json +19 -26
  30. package/api-doc/monarch.transitions.gui.json +782 -79
  31. package/api-doc/nakama.engine.defold.json +31 -18
  32. package/api-doc/{nakama.nakama.json → nakama.json} +4752 -2554
  33. package/api-doc/nakama.util.log.json +32 -4
  34. package/api-doc/narrator.json +647 -0
  35. package/api-doc/openapi/nakama.nakama.json +7680 -0
  36. package/api-doc/orthographic.camera.json +96 -98
  37. package/api-doc/orthographic.json +602 -0
  38. package/api-doc/panthera.json +1183 -0
  39. package/api-doc/{persist.persist.json → persist.json} +26 -1
  40. package/api-doc/{platypus.platypus.json → platypus.json} +68 -14
  41. package/api-doc/proto.json +995 -0
  42. package/api-doc/{rendy.rendy.json → rendy.json} +46 -99
  43. package/api-doc/richtext.color.json +77 -0
  44. package/api-doc/richtext.richtext.json +375 -5
  45. package/api-doc/saver.saver.json +714 -263
  46. package/api-doc/saver.storage.json +283 -52
  47. package/api-doc/squid.json +846 -0
  48. package/api-doc/{starly.starly.json → starly.json} +1 -1
  49. package/api-doc/tweener.json +277 -0
  50. package/api-doc/{yagames.yagames.json → yagames.json} +784 -189
  51. package/api-doc/{zzfx.api.json → zzfx.json} +9 -9
  52. package/authored-targets.json +403 -0
  53. package/generated/{boom.boom.d.ts → boom.d.ts} +14 -0
  54. package/generated/bridge.d.ts +468 -0
  55. package/generated/{bzAnim.bzLibrary.d.ts → bzAnim.d.ts} +6 -0
  56. package/generated/decore.d.ts +37 -37
  57. package/generated/defcon.d.ts +43 -0
  58. package/generated/{defmath.defmath.d.ts → defmath.d.ts} +3 -2
  59. package/generated/defsave.d.ts +144 -0
  60. package/generated/{dicebag.dicebag.d.ts → dicebag.d.ts} +25 -0
  61. package/generated/druid.d.ts +198 -502
  62. package/generated/event.d.ts +318 -0
  63. package/generated/{gooey.gooey.d.ts → gooey.d.ts} +61 -6
  64. package/generated/immutable.d.ts +13 -0
  65. package/generated/in.onscreen.d.ts +5 -0
  66. package/generated/in.textbox.d.ts +4 -0
  67. package/generated/lang.d.ts +101 -0
  68. package/generated/log.d.ts +36 -0
  69. package/generated/metrics.fps.d.ts +19 -1
  70. package/generated/metrics.mem.d.ts +19 -1
  71. package/generated/monarch.monarch.d.ts +44 -1
  72. package/generated/monarch.transitions.easings.d.ts +0 -1
  73. package/generated/monarch.transitions.gui.d.ts +96 -12
  74. package/generated/nakama.d.ts +1158 -0
  75. package/generated/nakama.engine.defold.d.ts +3 -1
  76. package/generated/nakama.util.log.d.ts +6 -0
  77. package/generated/narrator.d.ts +121 -0
  78. package/generated/openapi/nakama.nakama.d.ts +884 -0
  79. package/generated/orthographic.camera.d.ts +24 -38
  80. package/generated/orthographic.d.ts +155 -0
  81. package/generated/panthera.d.ts +170 -0
  82. package/generated/{persist.persist.d.ts → persist.d.ts} +3 -0
  83. package/generated/{platypus.platypus.d.ts → platypus.d.ts} +9 -0
  84. package/generated/proto.d.ts +146 -0
  85. package/generated/{rendy.rendy.d.ts → rendy.d.ts} +33 -80
  86. package/generated/richtext.color.d.ts +15 -0
  87. package/generated/saver.saver.d.ts +289 -42
  88. package/generated/saver.storage.d.ts +79 -14
  89. package/generated/squid.d.ts +127 -0
  90. package/generated/tweener.d.ts +42 -0
  91. package/generated/{yagames.yagames.d.ts → yagames.d.ts} +151 -29
  92. package/generated/{zzfx.api.d.ts → zzfx.d.ts} +7 -0
  93. package/library-classification.json +0 -227
  94. package/library-targets.json +1 -284
  95. package/luals-targets.json +168 -1
  96. package/openapi-targets.json +17 -0
  97. package/package.json +9 -88
  98. package/script-api-targets.json +15 -0
  99. package/scripts/__snapshots__/parse-luals.test.ts.snap +504 -85
  100. package/scripts/apply-luals-overrides.ts +95 -0
  101. package/scripts/authored-parity.ts +718 -0
  102. package/scripts/emit-library-dts.ts +177 -19
  103. package/scripts/extract-api-doc.ts +189 -38
  104. package/scripts/fidelity-floor.ts +178 -0
  105. package/scripts/import-upstream-docs.ts +125 -0
  106. package/scripts/lower-api-doc.ts +69 -19
  107. package/scripts/luals-fidelity.ts +22 -10
  108. package/scripts/map-luals-types.ts +86 -22
  109. package/scripts/parse-lua-surface.ts +187 -0
  110. package/scripts/parse-luals.ts +473 -22
  111. package/scripts/parse-markdown-api.ts +247 -0
  112. package/scripts/parse-openapi-api.ts +313 -0
  113. package/scripts/sync-authored-types.ts +249 -0
  114. package/scripts/sync-library-types.ts +99 -3
  115. package/scripts/sync-luals-types.ts +29 -2
  116. package/scripts/sync-markdown-types.ts +761 -0
  117. package/scripts/sync-openapi-types.ts +308 -0
  118. package/scripts/sync-script-api-types.ts +367 -0
  119. package/api-doc/boom.boom.json +0 -26
  120. package/api-doc/bzAnim.bzLibrary.json +0 -156
  121. package/api-doc/defcon.console.json +0 -109
  122. package/api-doc/defsave.defsave.json +0 -175
  123. package/api-doc/deftest.deftest.json +0 -71
  124. package/api-doc/gooey.gooey.json +0 -963
  125. package/api-doc/immutable.immutable.json +0 -63
  126. package/api-doc/lang.lang.json +0 -411
  127. package/api-doc/log.log.json +0 -50
  128. package/api-doc/narrator.narrator.json +0 -150
  129. package/api-doc/proto.proto.json +0 -355
  130. package/api-doc/squid.squid.json +0 -660
  131. package/api-doc/tweener.tweener.json +0 -419
  132. package/generated/bridge.bridge.d.ts +0 -533
  133. package/generated/defcon.console.d.ts +0 -24
  134. package/generated/defsave.defsave.d.ts +0 -31
  135. package/generated/event.event.d.ts +0 -54
  136. package/generated/immutable.immutable.d.ts +0 -13
  137. package/generated/lang.lang.d.ts +0 -33
  138. package/generated/log.log.d.ts +0 -40
  139. package/generated/nakama.nakama.d.ts +0 -594
  140. package/generated/narrator.narrator.d.ts +0 -66
  141. package/generated/proto.proto.d.ts +0 -36
  142. package/generated/squid.squid.d.ts +0 -106
  143. package/generated/tweener.tweener.d.ts +0 -151
  144. /package/generated/{deftest.deftest.d.ts → deftest.d.ts} +0 -0
  145. /package/generated/{starly.starly.d.ts → starly.d.ts} +0 -0
@@ -15,7 +15,12 @@
15
15
  * `name` is scoped into a child `MapContext` (an identity rename) so a bare `T`
16
16
  * resolves to `T` instead of lowering to `unknown`, constraints and `extends`
17
17
  * targets map through the existing rename map, and an `extends` clause is emitted
18
- * only for parents that resolve to a declared interface.
18
+ * only for parents that resolve to a declared interface or an imported external type.
19
+ *
20
+ * A token owned by another target is declared through `externalTypes`: the emitter
21
+ * writes a real `import { <name> } from '<module>';` into the `declare module` block
22
+ * and resolves the token to that binding, so a cross-library reference carries its
23
+ * real type instead of lowering to `unknown`.
19
24
  */
20
25
 
21
26
  import {
@@ -26,13 +31,16 @@ import {
26
31
  varargElementType,
27
32
  } from "@defold-typescript/types";
28
33
  import {
34
+ LUALS_VARARG_TOKEN,
29
35
  type MapContext,
36
+ mapLualsCallSignature,
30
37
  mapLualsType,
31
38
  matchSelfHookField,
32
39
  scopeGenerics,
33
40
  } from "./map-luals-types";
34
41
  import type {
35
42
  LibraryAlias,
43
+ LibraryField,
36
44
  LibraryGeneric,
37
45
  LibraryInterface,
38
46
  LibraryMethod,
@@ -40,13 +48,38 @@ import type {
40
48
  LibraryParam,
41
49
  } from "./parse-luals";
42
50
 
51
+ /**
52
+ * A type token owned by another target: the module id that declares it and the name
53
+ * it exports. Resolving one emits a real `import` into the `declare module` block
54
+ * rather than lowering the token to `unknown`.
55
+ */
56
+ export interface ExternalTypeRef {
57
+ module: string;
58
+ name: string;
59
+ }
60
+
43
61
  export interface EmitLibraryOptions {
44
62
  moduleId: string;
45
63
  typeRenames?: Record<string, string>;
64
+ externalTypes?: Record<string, ExternalTypeRef> | undefined;
46
65
  }
47
66
 
48
67
  const INDENT = "\t";
49
68
 
69
+ // A member with an explicit non-public visibility is internal surface; keep only
70
+ // members with no visibility or an explicit `public`, mirroring how LuaLS hides
71
+ // `private`/`protected`/`package` (and, for methods, `local`) from a class's public
72
+ // shape. Shared by all three consumers — the emitter, the api-doc lowering, and the
73
+ // fidelity report — so the declaration, documentation, and coverage surfaces
74
+ // describe one identical public member set.
75
+ export function isPublicField(field: LibraryField): boolean {
76
+ return field.visibility === undefined || field.visibility === "public";
77
+ }
78
+
79
+ export function isPublicMethod(method: LibraryMethod): boolean {
80
+ return method.visibility === undefined || method.visibility === "public";
81
+ }
82
+
50
83
  /** A model type name (dotted like `druid.button`) reduced to a legal TS identifier. */
51
84
  export function sanitizeTypeName(name: string): string {
52
85
  const cleaned = name.replace(/[^A-Za-z0-9_$]/g, "_");
@@ -69,6 +102,42 @@ function safeParamName(name: string, index: number): string {
69
102
  return TS_RESERVED_NAMES.has(name) ? `${name}_` : name;
70
103
  }
71
104
 
105
+ /**
106
+ * The token -> local-alias rename map an `externalTypes` config contributes, aliasing
107
+ * exactly as a declared name would (dotted `event.foo` -> `event_foo`). Shared by the
108
+ * emitter, the fidelity report, and the api-doc lowering so all three resolve an
109
+ * external reference to the same identifier the import binds.
110
+ */
111
+ export function externalTypeRenames(
112
+ externalTypes?: Record<string, ExternalTypeRef>,
113
+ ): Record<string, string> {
114
+ const renames: Record<string, string> = {};
115
+ for (const token of Object.keys(externalTypes ?? {})) renames[token] = sanitizeTypeName(token);
116
+ return renames;
117
+ }
118
+
119
+ /**
120
+ * `import { ... } from '<module>';` lines for the external tokens, one per source
121
+ * module with its names sorted and the modules sorted by id, so re-rendering is
122
+ * byte-identical. A token whose local alias differs from the export name binds
123
+ * through an `as` clause.
124
+ */
125
+ function renderExternalImports(externalTypes?: Record<string, ExternalTypeRef>): string[] {
126
+ const byModule = new Map<string, Set<string>>();
127
+ for (const [token, ref] of Object.entries(externalTypes ?? {})) {
128
+ const alias = sanitizeTypeName(token);
129
+ const binding = alias === ref.name ? ref.name : `${ref.name} as ${alias}`;
130
+ const names = byModule.get(ref.module) ?? new Set<string>();
131
+ names.add(binding);
132
+ byModule.set(ref.module, names);
133
+ }
134
+ return [...byModule.entries()]
135
+ .sort(([a], [b]) => (a < b ? -1 : 1))
136
+ .map(
137
+ ([module, names]) => `${INDENT}import { ${[...names].sort().join(", ")} } from '${module}';`,
138
+ );
139
+ }
140
+
72
141
  export function mapTypes(types: readonly string[], ctx: MapContext): string {
73
142
  if (types.length === 0) return "unknown";
74
143
  return types.map((token) => mapLualsType(token, ctx).ts).join(" | ");
@@ -91,36 +160,67 @@ export function renderGenericParams(generics: readonly LibraryGeneric[], ctx: Ma
91
160
  function renderHookReturn(returnTokens: readonly string[], ctx: MapContext): string {
92
161
  if (returnTokens.length === 0) return "void";
93
162
  if (returnTokens.length === 1) return mapTypes([returnTokens[0] as string], ctx);
94
- return luaMultiReturn(returnTokens.map((token) => mapTypes([token], ctx)));
163
+ return luaMultiReturn(
164
+ returnTokens.map((token) => mapTypes([token], ctx)),
165
+ returnTokens.at(-1) === LUALS_VARARG_TOKEN,
166
+ );
95
167
  }
96
168
 
97
169
  /**
98
170
  * An ` extends X, Y` clause built from `iface.extends` split on commas, keeping only
99
- * parents that name a declared interface (so no `extends unknown` is ever emitted),
100
- * mapped through the rename map; `""` when none survive.
171
+ * parents that name a declared interface or an imported external type (so no
172
+ * `extends unknown` is ever emitted), mapped through the rename map; `""` when none
173
+ * survive.
101
174
  */
102
175
  function renderExtends(
103
176
  iface: LibraryInterface,
104
177
  ctx: MapContext,
105
178
  interfaceNames: ReadonlySet<string>,
179
+ externalTokens: ReadonlySet<string>,
106
180
  ): string {
107
181
  if (!iface.extends) return "";
108
182
  const parents = iface.extends
109
183
  .split(",")
110
184
  .map((name) => name.trim())
111
- .filter((name) => interfaceNames.has(name))
185
+ .filter((name) => interfaceNames.has(name) || externalTokens.has(name))
112
186
  .map((name) => mapTypes([name], ctx));
113
187
  return parents.length > 0 ? ` extends ${parents.join(", ")}` : "";
114
188
  }
115
189
 
190
+ /**
191
+ * Which non-vararg params may be emitted with a trailing `?`. Walking right-to-left,
192
+ * a param is emitted-optional iff it is itself omittable (`isOptional || isNilable`)
193
+ * and every later non-vararg param is too — a required param blocks every param to
194
+ * its left, since TS forbids a required parameter after an optional one. A trailing
195
+ * vararg (rendered `...args`, never `?`) does not break the preceding run. Exported so
196
+ * the api-doc lowering keys `is_optional` off the identical rule, keeping the rendered
197
+ * `/api` signature byte-consistent with the `.d.ts`.
198
+ */
199
+ export function paramOptionalFlags(params: readonly LibraryParam[]): boolean[] {
200
+ const flags = new Array<boolean>(params.length).fill(false);
201
+ let laterAllOptional = true;
202
+ for (let i = params.length - 1; i >= 0; i--) {
203
+ const param = params[i] as LibraryParam;
204
+ if (param.isVararg) continue;
205
+ const selfOptional = param.isOptional || param.isNilable === true;
206
+ if (laterAllOptional && selfOptional) {
207
+ flags[i] = true;
208
+ } else {
209
+ laterAllOptional = false;
210
+ }
211
+ }
212
+ return flags;
213
+ }
214
+
116
215
  function renderParams(params: readonly LibraryParam[], ctx: MapContext): string {
216
+ const optionalFlags = paramOptionalFlags(params);
117
217
  return params
118
218
  .map((param, index) => {
119
219
  const mapped = mapTypes(param.types, ctx);
120
220
  if (param.isVararg) {
121
221
  return `...args: ${varargElementType(mapped)}`;
122
222
  }
123
- const optional = param.isOptional ? "?" : "";
223
+ const optional = optionalFlags[index] ? "?" : "";
124
224
  return `${safeParamName(param.name, index)}${optional}: ${mapped}`;
125
225
  })
126
226
  .join(", ");
@@ -129,16 +229,22 @@ function renderParams(params: readonly LibraryParam[], ctx: MapContext): string
129
229
  function renderReturn(returns: readonly LibraryParam[], ctx: MapContext): string {
130
230
  if (returns.length === 0) return "void";
131
231
  if (returns.length === 1) return mapTypes((returns[0] as LibraryParam).types, ctx);
132
- return luaMultiReturn(returns.map((ret) => mapTypes(ret.types, ctx)));
232
+ const last = returns[returns.length - 1] as LibraryParam;
233
+ const restTail = last.types.length === 1 && last.types[0] === LUALS_VARARG_TOKEN;
234
+ return luaMultiReturn(
235
+ returns.map((ret) => mapTypes(ret.types, ctx)),
236
+ restTail,
237
+ );
133
238
  }
134
239
 
135
- function pushDoc(lines: string[], summary: string, indent: string): void {
136
- for (const line of renderDocComment({ summary })) lines.push(`${indent}${line}`);
240
+ function pushDoc(lines: string[], summary: string, indent: string, deprecated?: string): void {
241
+ const parts = deprecated !== undefined ? { summary, deprecated } : { summary };
242
+ for (const line of renderDocComment(parts)) lines.push(`${indent}${line}`);
137
243
  }
138
244
 
139
245
  function renderAlias(alias: LibraryAlias, ctx: MapContext): string[] {
140
246
  const lines: string[] = [];
141
- pushDoc(lines, alias.doc, INDENT);
247
+ pushDoc(lines, alias.doc, INDENT, alias.deprecated);
142
248
  lines.push(`${INDENT}type ${sanitizeTypeName(alias.name)} = ${mapTypes(alias.types, ctx)};`);
143
249
  return lines;
144
250
  }
@@ -147,15 +253,17 @@ function renderInterface(
147
253
  iface: LibraryInterface,
148
254
  ctx: MapContext,
149
255
  interfaceNames: ReadonlySet<string>,
256
+ externalTokens: ReadonlySet<string>,
150
257
  ): string[] {
151
258
  const lines: string[] = [];
152
- pushDoc(lines, iface.brief, INDENT);
259
+ pushDoc(lines, iface.brief, INDENT, iface.deprecated);
153
260
  const ifaceCtx = scopeGenerics(ctx, iface.generics);
154
261
  const params = renderGenericParams(iface.generics, ifaceCtx);
155
- const extendsClause = renderExtends(iface, ifaceCtx, interfaceNames);
262
+ const extendsClause = renderExtends(iface, ifaceCtx, interfaceNames, externalTokens);
156
263
  lines.push(`${INDENT}interface ${sanitizeTypeName(iface.name)}${params}${extendsClause} {`);
157
264
  const body = INDENT + INDENT;
158
265
  for (const field of iface.fields) {
266
+ if (!isPublicField(field)) continue;
159
267
  // A base's self-receiving lifecycle hook is emitted as a permissive optional
160
268
  // method (`name?(...args: any[]): ret`) so a concrete subinterface's refined
161
269
  // override stays assignable under `extends`; strict function-field variance
@@ -172,7 +280,8 @@ function renderInterface(
172
280
  lines.push(`${body}${memberKey(field.name)}${optional}: ${mapTypes(field.types, ifaceCtx)};`);
173
281
  }
174
282
  for (const method of iface.methods) {
175
- pushDoc(lines, method.brief, body);
283
+ if (!isPublicMethod(method)) continue;
284
+ pushDoc(lines, method.brief, body, method.deprecated);
176
285
  const methodCtx = scopeGenerics(ifaceCtx, method.generics);
177
286
  const methodParams = renderGenericParams(method.generics, methodCtx);
178
287
  lines.push(
@@ -182,13 +291,39 @@ function renderInterface(
182
291
  )}): ${renderReturn(method.returns, methodCtx)};`,
183
292
  );
184
293
  }
294
+ // A class `@overload fun(...)` becomes an interface call signature, making the
295
+ // instance callable (`event(...)`, `promise(...)`) the way the metatable's `__call`
296
+ // does at runtime. Rendered after the members with its own doc comment.
297
+ for (const overload of iface.overloads ?? []) {
298
+ pushDoc(lines, overload.doc, body);
299
+ lines.push(`${body}${mapLualsCallSignature(overload.type, ifaceCtx).ts};`);
300
+ }
185
301
  lines.push(`${INDENT}}`);
186
302
  return lines;
187
303
  }
188
304
 
305
+ /**
306
+ * The module object's public fields rendered as module-level `export const`s — the
307
+ * constants a returned module table (`return Squid`) carries. Emitted in place of the
308
+ * standalone interface, since consumers reach them as `squid.TRACE`, not `Squid.TRACE`.
309
+ */
310
+ function renderModuleConstants(iface: LibraryInterface, ctx: MapContext): string[] {
311
+ const lines: string[] = [];
312
+ for (const field of iface.fields) {
313
+ if (!isPublicField(field)) continue;
314
+ pushDoc(lines, field.doc, INDENT);
315
+ const name =
316
+ TS_IDENTIFIER.test(field.name) && !TS_RESERVED_NAMES.has(field.name)
317
+ ? field.name
318
+ : sanitizeTypeName(field.name);
319
+ lines.push(`${INDENT}export const ${name}: ${mapTypes(field.types, ctx)};`);
320
+ }
321
+ return lines;
322
+ }
323
+
189
324
  function renderModuleFunction(fn: LibraryMethod, ctx: MapContext): string[] {
190
325
  const lines: string[] = [];
191
- pushDoc(lines, fn.brief, INDENT);
326
+ pushDoc(lines, fn.brief, INDENT, fn.deprecated);
192
327
  const fnCtx = scopeGenerics(ctx, fn.generics);
193
328
  const genericParams = renderGenericParams(fn.generics, fnCtx);
194
329
  const params = renderParams(fn.params, fnCtx);
@@ -220,6 +355,7 @@ function renderModuleFunction(fn: LibraryMethod, ctx: MapContext): string[] {
220
355
  export function buildModelContext(
221
356
  model: LibraryModel,
222
357
  typeRenames?: Record<string, string>,
358
+ externalTypes?: Record<string, ExternalTypeRef>,
223
359
  ): MapContext {
224
360
  const declaredNames = [
225
361
  ...model.interfaces.map((iface) => iface.name),
@@ -227,9 +363,18 @@ export function buildModelContext(
227
363
  ];
228
364
  const nameRenames: Record<string, string> = {};
229
365
  for (const name of declaredNames) nameRenames[name] = sanitizeTypeName(name);
366
+ const externalRenames = externalTypeRenames(externalTypes);
367
+ for (const [token, alias] of Object.entries(externalRenames)) {
368
+ const collision = declaredNames.find((name) => sanitizeTypeName(name) === alias);
369
+ if (collision !== undefined) {
370
+ throw new Error(
371
+ `buildModelContext: external type "${token}" resolves to local name "${alias}", which the model already declares as "${collision}".`,
372
+ );
373
+ }
374
+ }
230
375
  return {
231
- knownNames: new Set(declaredNames),
232
- typeRenames: { ...(typeRenames ?? {}), ...nameRenames },
376
+ knownNames: new Set([...declaredNames, ...Object.values(externalRenames)]),
377
+ typeRenames: { ...(typeRenames ?? {}), ...externalRenames, ...nameRenames },
233
378
  };
234
379
  }
235
380
 
@@ -240,13 +385,26 @@ export function buildModelContext(
240
385
  * `unknown` inside `mapLualsType` exactly as the fidelity report records them.
241
386
  */
242
387
  export function emitLibraryDeclarations(model: LibraryModel, opts: EmitLibraryOptions): string {
243
- const ctx = buildModelContext(model, opts.typeRenames);
388
+ const ctx = buildModelContext(model, opts.typeRenames, opts.externalTypes);
244
389
 
245
390
  const interfaceNames = new Set(model.interfaces.map((iface) => iface.name));
391
+ const externalTokens = new Set(Object.keys(opts.externalTypes ?? {}));
246
392
  const out: string[] = ["/** @noResolution */", `declare module '${opts.moduleId}' {`];
393
+ out.push(...renderExternalImports(opts.externalTypes));
247
394
  for (const alias of model.aliases) out.push(...renderAlias(alias, ctx));
248
- for (const iface of model.interfaces) out.push(...renderInterface(iface, ctx, interfaceNames));
249
- for (const fn of model.moduleFunctions) out.push(...renderModuleFunction(fn, ctx));
395
+ for (const iface of model.interfaces) {
396
+ // The module object's fields become module-level `export const`s (below), so it is
397
+ // not also rendered as a standalone interface.
398
+ if (iface.name === model.moduleObject) {
399
+ out.push(...renderModuleConstants(iface, ctx));
400
+ continue;
401
+ }
402
+ out.push(...renderInterface(iface, ctx, interfaceNames, externalTokens));
403
+ }
404
+ for (const fn of model.moduleFunctions) {
405
+ if (!isPublicMethod(fn)) continue;
406
+ out.push(...renderModuleFunction(fn, ctx));
407
+ }
250
408
  out.push("}");
251
409
  return `${out.join("\n")}\n`;
252
410
  }