@defold-typescript/types 0.33.1 → 0.35.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 (64) hide show
  1. package/api-signatures.json +156 -183
  2. package/api-targets.json +9 -66
  3. package/generated/buffer.d.ts +2 -2
  4. package/generated/collectionfactory.d.ts +2 -2
  5. package/generated/factory.d.ts +2 -2
  6. package/generated/gui.d.ts +23 -23
  7. package/generated/kinds/gui-script.d.ts +0 -4
  8. package/generated/kinds/render-script.d.ts +1 -4
  9. package/generated/kinds/script.d.ts +0 -4
  10. package/generated/model.d.ts +2 -2
  11. package/generated/profiler.d.ts +2 -4
  12. package/generated/render.d.ts +2 -150
  13. package/generated/sys.d.ts +1 -1
  14. package/generated/versions/defold-1.12.4/buffer.d.ts +2 -2
  15. package/generated/versions/defold-1.12.4/collectionfactory.d.ts +2 -2
  16. package/generated/versions/defold-1.12.4/factory.d.ts +2 -2
  17. package/generated/versions/defold-1.12.4/gui.d.ts +23 -23
  18. package/generated/versions/defold-1.12.4/index.d.ts +9 -4
  19. package/generated/versions/defold-1.12.4/model.d.ts +2 -2
  20. package/generated/versions/defold-1.12.4/profiler.d.ts +2 -4
  21. package/generated/versions/defold-1.12.4/render.d.ts +2 -150
  22. package/generated/versions/defold-1.12.4/sys.d.ts +1 -1
  23. package/generated/versions/defold-1.12.4/vmath.d.ts +0 -19
  24. package/generated/versions/defold-1.12.4/window.d.ts +2 -4
  25. package/generated/versions/defold-1.13.0/buffer.d.ts +2 -2
  26. package/generated/versions/defold-1.13.0/collectionfactory.d.ts +2 -2
  27. package/generated/versions/defold-1.13.0/factory.d.ts +2 -2
  28. package/generated/versions/defold-1.13.0/gui.d.ts +23 -23
  29. package/generated/versions/defold-1.13.0/index.d.ts +9 -4
  30. package/generated/versions/defold-1.13.0/model.d.ts +2 -2
  31. package/generated/versions/defold-1.13.0/profiler.d.ts +2 -4
  32. package/generated/versions/defold-1.13.0/render.d.ts +2 -150
  33. package/generated/versions/defold-1.13.0/sys.d.ts +1 -1
  34. package/generated/versions/defold-1.13.0/vmath.d.ts +0 -19
  35. package/generated/versions/defold-1.13.0/window.d.ts +2 -4
  36. package/generated/vmath.d.ts +0 -19
  37. package/generated/window.d.ts +2 -4
  38. package/index.d.ts +1 -4
  39. package/package.json +4 -2
  40. package/scripts/extension-goldens.ts +23 -0
  41. package/scripts/generate-api-signatures.ts +40 -1
  42. package/scripts/materialize-version.ts +15 -1
  43. package/scripts/regen.ts +109 -27
  44. package/scripts/signature-store-fs.ts +1 -0
  45. package/scripts/sync-api-docs.ts +11 -61
  46. package/src/doc-comment.ts +10 -6
  47. package/src/emit-dts.ts +212 -21
  48. package/src/optional-correction-provenance.ts +13 -2
  49. package/src/render-overloads.d.ts +29 -0
  50. package/src/script-api.ts +154 -23
  51. package/src/vmath-overloads.d.ts +19 -0
  52. package/generated/iac.d.ts +0 -18
  53. package/generated/iap.d.ts +0 -60
  54. package/generated/push.d.ts +0 -66
  55. package/generated/versions/defold-1.12.4/iac.d.ts +0 -18
  56. package/generated/versions/defold-1.12.4/iap.d.ts +0 -60
  57. package/generated/versions/defold-1.12.4/push.d.ts +0 -66
  58. package/generated/versions/defold-1.12.4/webview.d.ts +0 -68
  59. package/generated/versions/defold-1.13.0/iac.d.ts +0 -18
  60. package/generated/versions/defold-1.13.0/iap.d.ts +0 -60
  61. package/generated/versions/defold-1.13.0/push.d.ts +0 -66
  62. package/generated/versions/defold-1.13.0/webview.d.ts +0 -68
  63. package/generated/webview.d.ts +0 -68
  64. package/scripts/fidelity-audit.ts +0 -684
@@ -24,6 +24,7 @@ export const SOCKET_SIGNATURES_PATH = resolve(import.meta.dir, "..", "signatures
24
24
  export const VMATH_SIGNATURES_PATH = resolve(import.meta.dir, "..", "signatures", "vmath.json");
25
25
  export const GO_SIGNATURES_PATH = resolve(import.meta.dir, "..", "signatures", "go.json");
26
26
  export const MSG_SIGNATURES_PATH = resolve(import.meta.dir, "..", "signatures", "msg.json");
27
+ export const RENDER_SIGNATURES_PATH = resolve(import.meta.dir, "..", "signatures", "render.json");
27
28
 
28
29
  export function loadSignatureFile(path: string): SignatureStore {
29
30
  let raw: string;
@@ -109,9 +109,9 @@ export const SYNC_MANIFEST: readonly SyncManifestEntry[] = [
109
109
  entry("zlib", "doc/src-script_zlib.cpp_doc.json"),
110
110
  ];
111
111
 
112
- // Checklist namespaces with no machine-readable doc anywhere (neither core
113
- // ref-doc.zip nor a published extension `.script_api`). Currently empty: the
114
- // four extension-only surfaces are wired via EXTENSION_MANIFEST below.
112
+ // Checklist namespaces with no machine-readable doc anywhere in core
113
+ // ref-doc.zip. Currently empty: extension namespaces are not engine surfaces;
114
+ // they are typed through `resolve` from each extension's own `.script_api`.
115
115
  export const UNMAPPED: ReadonlyMap<string, string> = new Map();
116
116
 
117
117
  // A Lua script namespace is a lowercase, dot-separated identifier (`sys`,
@@ -211,53 +211,18 @@ function entry(
211
211
  return { namespace, zipEntry, fixture, ...(mergeEntries ? { mergeEntries } : {}) };
212
212
  }
213
213
 
214
- export interface ExtensionManifestEntry {
215
- readonly namespace: string;
216
- readonly repo: string;
217
- readonly tag: string;
218
- readonly path: string;
219
- readonly fixture: string;
220
- }
221
-
222
- // Extension-only namespaces: each ships a single `.script_api` (YAML) doc in
223
- // its own repo rather than appearing in core ref-doc.zip. Pinned to a release
224
- // tag and converted to the core ref-doc JSON shape via parseScriptApi so they
225
- // flow through MODULE_MANIFEST and the drift guard like every core namespace.
226
- export const EXTENSION_MANIFEST: readonly ExtensionManifestEntry[] = [
227
- ext("iac", "defold/extension-iac", "1.4.0", "extension-iac/api/iac.script_api"),
228
- ext("iap", "defold/extension-iap", "8.4.0", "extension-iap/api/iap.script_api"),
229
- ext("push", "defold/extension-push", "4.1.0", "extension-push/api/push.script_api"),
230
- ext("webview", "defold/extension-webview", "1.5.0", "webview/api/webview.script_api"),
231
- ];
232
-
233
- function ext(namespace: string, repo: string, tag: string, path: string): ExtensionManifestEntry {
234
- return {
235
- namespace,
236
- repo,
237
- tag,
238
- path,
239
- fixture: `fixtures/defold-1.13.1/${namespace}_doc.json`,
240
- };
241
- }
242
-
243
214
  // Every vendored fixture set the coverage report audits, and the namespaces it
244
215
  // counts as reached from upstream. Composed once here so the `--check` run and
245
216
  // the guard over it cannot disagree about which sets are in scope.
246
217
  export const COVERAGE_MANIFEST: readonly { readonly namespace: string }[] = [
247
218
  ...SYNC_MANIFEST,
248
- ...EXTENSION_MANIFEST,
249
219
  ...EDITOR_MANIFEST,
250
220
  ];
251
221
 
252
222
  export const UPSTREAM_MAPPED_NAMESPACES: ReadonlySet<string> = new Set(
253
- [...SYNC_MANIFEST, ...EXTENSION_MANIFEST, ...LUA_STDLIB_MANIFEST, ...EDITOR_MANIFEST].map(
254
- (e) => e.namespace,
255
- ),
223
+ [...SYNC_MANIFEST, ...LUA_STDLIB_MANIFEST, ...EDITOR_MANIFEST].map((e) => e.namespace),
256
224
  );
257
225
 
258
- export const extensionRawUrl = (e: ExtensionManifestEntry): string =>
259
- `https://raw.githubusercontent.com/${e.repo}/${e.tag}/${e.path}`;
260
-
261
226
  // Convert a raw `.script_api` YAML doc to the core ref-doc JSON string written
262
227
  // to fixtures/<ns>_doc.json. The result is byte-compared by the same drift
263
228
  // semantics core fixtures use.
@@ -265,6 +230,12 @@ export function scriptApiToFixtureJson(text: string): string {
265
230
  return JSON.stringify(parseScriptApi(text));
266
231
  }
267
232
 
233
+ // The docs-only variant: the same ref-doc JSON with constants kept, for pages
234
+ // that render a module's full surface rather than feed the emitter.
235
+ export function scriptApiToDocsJson(text: string): string {
236
+ return JSON.stringify(parseScriptApi(text, { complete: true }));
237
+ }
238
+
268
239
  // The types-api-coverage `What` checklist in vision.md lists every namespace as
269
240
  // a backtick-quoted token on the "Concrete breadth checklist" line. Reading it
270
241
  // here keeps SYNC_MANIFEST + UNMAPPED honest against the source of truth: the
@@ -486,25 +457,6 @@ export function syncExtractedFixtures(
486
457
  return results;
487
458
  }
488
459
 
489
- export async function downloadExtensionFixtures(
490
- manifest: readonly ExtensionManifestEntry[] = EXTENSION_MANIFEST,
491
- ): Promise<ExtractedFixture[]> {
492
- const out: ExtractedFixture[] = [];
493
- for (const e of manifest) {
494
- const url = extensionRawUrl(e);
495
- const res = await fetch(url);
496
- if (!res.ok) {
497
- throw new Error(`extension doc download failed: ${url} -> ${res.status} ${res.statusText}`);
498
- }
499
- out.push({
500
- namespace: e.namespace,
501
- fixture: e.fixture,
502
- contents: scriptApiToFixtureJson(await res.text()),
503
- });
504
- }
505
- return out;
506
- }
507
-
508
460
  function readFixtureOrNull(path: string): string | null {
509
461
  try {
510
462
  return readFileSync(path, "utf8");
@@ -675,18 +627,16 @@ if (import.meta.main) {
675
627
  const zip = zipPath ? readZip(zipPath) : await downloadZip();
676
628
 
677
629
  const coreFixtures = extractFixtures(zip);
678
- const extensionFixtures = await downloadExtensionFixtures();
679
630
  const luaStdlibFixtures = extractFixtures(zip, LUA_STDLIB_MANIFEST);
680
631
  const editorFixtures = extractFixtures(zip, EDITOR_MANIFEST);
681
632
  const editorVmFixtures = extractFixtures(zip, EDITOR_VM_MANIFEST);
682
633
  const results = [
683
634
  ...syncExtractedFixtures(coreFixtures, { check }),
684
- ...syncExtractedFixtures(extensionFixtures, { check }),
685
635
  ...syncExtractedFixtures(luaStdlibFixtures, { check }),
686
636
  ...syncExtractedFixtures(editorFixtures, { check }),
687
637
  ...syncExtractedFixtures(editorVmFixtures, { check }),
688
638
  ];
689
- const syncedDocs = [...coreFixtures, ...extensionFixtures].map((f) => ({
639
+ const syncedDocs = coreFixtures.map((f) => ({
690
640
  namespace: f.namespace,
691
641
  doc: JSON.parse(f.contents),
692
642
  }));
@@ -73,10 +73,12 @@ export function htmlToCodeText(html: string): string {
73
73
  /**
74
74
  * Convert a ref-doc `examples` HTML fragment — prose interleaved with one or
75
75
  * more `<div class="codehilite">…</div>` syntax-highlight blocks — into Markdown:
76
- * prose runs become `htmlToDocText`, each highlight block becomes a ` ```lua `
77
- * fence via `htmlToCodeText`. A fragment with no `codehilite` block is wrapped
78
- * whole as a single ` ```lua ` fence (back-compat for plain-code examples).
79
- * Returns `""` for empty / whitespace-only input.
76
+ * prose runs become `htmlToDocText`, each highlight block becomes a fence via
77
+ * `htmlToCodeText`. The fence language comes from a `class="language-X"` on the
78
+ * block's `<code>` (as converted extension `.script_api` examples carry), else
79
+ * `lua`, since engine ref-doc blocks carry no language class. A fragment with no
80
+ * `codehilite` block is wrapped whole as a single ` ```lua ` fence (back-compat
81
+ * for plain-code examples). Returns `""` for empty / whitespace-only input.
80
82
  */
81
83
  export function examplesHtmlToMarkdown(html: string): string {
82
84
  if (html.trim() === "") return "";
@@ -89,8 +91,10 @@ export function examplesHtmlToMarkdown(html: string): string {
89
91
  matched = true;
90
92
  const prose = htmlToDocText(html.slice(lastIndex, match.index));
91
93
  if (prose !== "") parts.push(prose);
92
- const code = htmlToCodeText(match[1] ?? "");
93
- if (code !== "") parts.push(`\`\`\`lua\n${code}\n\`\`\``);
94
+ const inner = match[1] ?? "";
95
+ const lang = /<code\b[^>]*\bclass="language-([^"\s]+)"/i.exec(inner)?.[1] ?? "lua";
96
+ const code = htmlToCodeText(inner);
97
+ if (code !== "") parts.push(`\`\`\`${lang}\n${code}\n\`\`\``);
94
98
  lastIndex = match.index + match[0].length;
95
99
  }
96
100
 
package/src/emit-dts.ts CHANGED
@@ -151,11 +151,10 @@ export const ARBITRARY_TABLE_SLOTS = new Set([
151
151
  "socket.dns.toip",
152
152
  "socket.dns.getaddrinfo",
153
153
  "socket.dns.getnameinfo",
154
- // engine-built render-target tables: the "see the description" cross-refs
155
- // are dead (the function's own description is empty) or carry a typed
154
+ // engine-built render-target tables: the "see the description" cross-ref is
155
+ // dead (the function's own description is empty) or carries a typed
156
156
  // `transient: table` field whose sub-doc is prose-only, so the nested table
157
157
  // is opaque to the field-list parser and the whole-slot `Record` is faithful.
158
- "render.render_target",
159
158
  "render.set_render_target",
160
159
  // reserved-but-undocumented options bag: `collectionproxy.load`'s `options`
161
160
  // slot is documented verbatim as "options table, currently unused" and accepts
@@ -204,6 +203,10 @@ export const OVERLOAD_COVERED_SKIPS = new Set([
204
203
  // msg-overloads.d.ts supplies the typed msg.url shape (0 / 1-string / 3-arg
205
204
  // arities; the runtime-invalid two-arg form is intentionally omitted).
206
205
  "msg.url",
206
+ // render-overloads.d.ts supplies the name-less and named render_target arities.
207
+ "render.render_target",
208
+ // vmath-overloads.d.ts supplies the vector3 and three-number euler_to_quat arities.
209
+ "vmath.euler_to_quat",
207
210
  // vmath-overloads.d.ts supplies the generic covariant signatures.
208
211
  "vmath.clamp",
209
212
  "vmath.lerp",
@@ -351,6 +354,119 @@ export const OPTIONAL_SLOT_CORRECTIONS: ReadonlyMap<string, string> = new Map([
351
354
  ),
352
355
  ]);
353
356
 
357
+ export type ConstantSlotResolution =
358
+ | { readonly borrow: string }
359
+ | { readonly family: string; readonly evidence: string }
360
+ | { readonly residual: string };
361
+
362
+ // A top-level `constant` slot takes the constants its own doc names. This table
363
+ // fills only a slot whose own doc names none: a `borrow` reads another slot's doc
364
+ // (same key shape), a `family` expands a `<prefix>*` wildcard evidenced by the
365
+ // named element's example, and a `residual` records why nothing can brand it.
366
+ // Constants always come from the brand universe, never from a hand-typed list.
367
+ export const CONSTANT_SLOT_RESOLUTIONS: ReadonlyMap<string, ConstantSlotResolution> = new Map<
368
+ string,
369
+ ConstantSlotResolution
370
+ >([
371
+ ["gui.get:param:property", { borrow: "gui.animate:param:property" }],
372
+ ["gui.set:param:property", { borrow: "gui.animate:param:property" }],
373
+ ["gui.cancel_animations:param:property", { borrow: "gui.animate:param:property" }],
374
+ [
375
+ "buffer.set_metadata:param:value_type",
376
+ { family: "buffer.VALUE_TYPE_*", evidence: "buffer.set_metadata" },
377
+ ],
378
+ [
379
+ "buffer.get_metadata:return:value_type",
380
+ { family: "buffer.VALUE_TYPE_*", evidence: "buffer.set_metadata" },
381
+ ],
382
+ ["gui.new_texture:param:type", { residual: "the doc lists texture type strings only" }],
383
+ ["gui.set_texture_data:param:type", { residual: "the doc lists texture type strings only" }],
384
+ [
385
+ "iap.get_provider_id:return:provider_id",
386
+ { residual: "the doc names iap.PROVIDER_ID_*, which no ref-doc defines as a constant" },
387
+ ],
388
+ ]);
389
+
390
+ const DOC_CONSTANT_TOKEN =
391
+ /(?<![A-Za-z0-9_.])([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*\.[A-Z][A-Z0-9_]*)(\*)?/g;
392
+
393
+ // The constant FQNs a piece of ref-doc text names, in doc order: exact tokens kept
394
+ // only when they are in `universe`, and each `<prefix>*` wildcard expanded to every
395
+ // universe FQN with that prefix, sorted.
396
+ export function documentedConstantTokens(text: string, universe: ReadonlySet<string>): string[] {
397
+ const out = new Set<string>();
398
+ for (const match of text.replace(/<[^>]*>/g, "").matchAll(DOC_CONSTANT_TOKEN)) {
399
+ const token = match[1] as string;
400
+ if (match[2] === undefined) {
401
+ if (universe.has(token)) out.add(token);
402
+ continue;
403
+ }
404
+ for (const fqn of [...universe].filter((candidate) => candidate.startsWith(token)).sort()) {
405
+ out.add(fqn);
406
+ }
407
+ }
408
+ return [...out];
409
+ }
410
+
411
+ type SlotDocLookup = (slotKey: string) => string | undefined;
412
+
413
+ function constantSlotTokens(
414
+ ownDoc: string,
415
+ slotKey: string,
416
+ lookupDoc: SlotDocLookup,
417
+ universe: ReadonlySet<string>,
418
+ ): string[] {
419
+ const own = documentedConstantTokens(ownDoc, universe);
420
+ if (own.length > 0) return own;
421
+ const entry = CONSTANT_SLOT_RESOLUTIONS.get(slotKey);
422
+ if (entry === undefined || "residual" in entry) return [];
423
+ if ("borrow" in entry) return documentedConstantTokens(lookupDoc(entry.borrow) ?? "", universe);
424
+ return documentedConstantTokens(entry.family, universe);
425
+ }
426
+
427
+ function slotDocLookup(module: ApiModule): SlotDocLookup {
428
+ const docs = new Map<string, string>();
429
+ for (const fn of module.functions) {
430
+ for (const p of fn.parameters) {
431
+ const key = tableSlotKey(fn.name, "param", p.name);
432
+ if (!docs.has(key)) docs.set(key, p.doc);
433
+ }
434
+ for (const rv of fn.returnValues) {
435
+ const key = tableSlotKey(fn.name, "return", rv.name);
436
+ if (!docs.has(key)) docs.set(key, rv.doc);
437
+ }
438
+ }
439
+ return (slotKey) => docs.get(slotKey);
440
+ }
441
+
442
+ // The constants a module's top-level `constant` slot expands into: its own doc
443
+ // first, then its CONSTANT_SLOT_RESOLUTIONS entry. Empty means the slot keeps
444
+ // `Opaque<"constant">`.
445
+ export function resolveConstantSlotTokens(
446
+ module: ApiModule,
447
+ slotKey: string,
448
+ universe: ReadonlySet<string>,
449
+ ): string[] {
450
+ const lookupDoc = slotDocLookup(module);
451
+ return constantSlotTokens(lookupDoc(slotKey) ?? "", slotKey, lookupDoc, universe);
452
+ }
453
+
454
+ type ConstantSlotTokens = (
455
+ elementName: string,
456
+ slotKind: "param" | "return",
457
+ slotName: string,
458
+ doc: string,
459
+ ) => readonly string[];
460
+
461
+ function constantSlotTokenResolver(
462
+ module: ApiModule,
463
+ universe: ReadonlySet<string>,
464
+ ): ConstantSlotTokens {
465
+ const lookupDoc = slotDocLookup(module);
466
+ return (elementName, slotKind, slotName, doc) =>
467
+ constantSlotTokens(doc, tableSlotKey(elementName, slotKind, slotName), lookupDoc, universe);
468
+ }
469
+
354
470
  // FQN-keyed allowlist of the `types.is_*` checks that genuinely narrow their
355
471
  // argument, mapped to the `DEFOLD_TYPE_MAP` token whose interface they prove.
356
472
  // Emitting these as user-defined type guards (`var_ is Vector3`) is the only way
@@ -1580,6 +1696,10 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
1580
1696
  constantFqns.has(token) || knownConstantFqns?.has(token)
1581
1697
  ? brandType(token)
1582
1698
  : baseMapType(token);
1699
+ const constantTokens = constantSlotTokenResolver(
1700
+ module,
1701
+ new Set([...constantFqns, ...(knownConstantFqns ?? [])]),
1702
+ );
1583
1703
 
1584
1704
  const constants = module.constants
1585
1705
  .map((c) => prepareConstant(c, prefix))
@@ -1656,7 +1776,9 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
1656
1776
  for (const docLine of functionDocLines(fn.original, translations, handleIndent)) {
1657
1777
  lines.push(docLine);
1658
1778
  }
1659
- lines.push(`${handleIndent}${emitMethod(fn, mapType, resolver, urlParameters)}`);
1779
+ lines.push(
1780
+ `${handleIndent}${emitMethod(fn, mapType, resolver, constantTokens, urlParameters)}`,
1781
+ );
1660
1782
  }
1661
1783
  lines.push(`${INDENT}}`);
1662
1784
  }
@@ -1685,7 +1807,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
1685
1807
  const reserved = TS_RESERVED_NAMES.has(fn.name);
1686
1808
  const emitName = aliasName(fn.name, aliases);
1687
1809
  for (const docLine of functionDocLines(fn.original, translations)) lines.push(docLine);
1688
- const line = emitFunction(fn, emitName, mapType, resolver, urlParameters);
1810
+ const line = emitFunction(fn, emitName, mapType, resolver, constantTokens, urlParameters);
1689
1811
  lines.push(`${INDENT}${reserved ? "" : decl}${line}`);
1690
1812
  }
1691
1813
 
@@ -1733,7 +1855,7 @@ export function emitDeclarations(module: ApiModule, options?: EmitOptions): stri
1733
1855
  lines.push(docLine);
1734
1856
  }
1735
1857
  lines.push(
1736
- `${bodyIndent}${reserved ? "" : segmentDecl}${emitFunction(fn, emitName, mapType, resolver, urlParameters)}`,
1858
+ `${bodyIndent}${reserved ? "" : segmentDecl}${emitFunction(fn, emitName, mapType, resolver, constantTokens, urlParameters)}`,
1737
1859
  );
1738
1860
  }
1739
1861
  for (const alias of [...segmentAliases].sort((a, b) => a.public.localeCompare(b.public))) {
@@ -1789,6 +1911,10 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
1789
1911
  constantFqns.has(token) || knownConstantFqns?.has(token)
1790
1912
  ? brandType(token)
1791
1913
  : baseMapType(token);
1914
+ const constantTokens = constantSlotTokenResolver(
1915
+ module,
1916
+ new Set([...constantFqns, ...(knownConstantFqns ?? [])]),
1917
+ );
1792
1918
  const resolver = buildTableDocResolver(
1793
1919
  module.functions.map((fn) => ({
1794
1920
  name: fn.name,
@@ -1815,6 +1941,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
1815
1941
  emitName(prepared.name),
1816
1942
  mapType,
1817
1943
  resolver,
1944
+ constantTokens,
1818
1945
  urlParameters,
1819
1946
  ),
1820
1947
  });
@@ -1835,7 +1962,14 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
1835
1962
  for (const fn of nested.functions) {
1836
1963
  out.push({
1837
1964
  identity: fnIdentity(fn.original),
1838
- tsSignature: emitFunction(fn, emitName(fn.name), mapType, resolver, urlParameters),
1965
+ tsSignature: emitFunction(
1966
+ fn,
1967
+ emitName(fn.name),
1968
+ mapType,
1969
+ resolver,
1970
+ constantTokens,
1971
+ urlParameters,
1972
+ ),
1839
1973
  });
1840
1974
  }
1841
1975
 
@@ -1844,7 +1978,7 @@ export function emitSymbolSignatures(module: ApiModule, options?: EmitOptions):
1844
1978
  for (const prepared of group) {
1845
1979
  out.push({
1846
1980
  identity: fnIdentity(prepared.original),
1847
- tsSignature: emitMethod(prepared, mapType, resolver, urlParameters),
1981
+ tsSignature: emitMethod(prepared, mapType, resolver, constantTokens, urlParameters),
1848
1982
  });
1849
1983
  }
1850
1984
  }
@@ -2072,6 +2206,7 @@ function memberSignature(
2072
2206
  name: string,
2073
2207
  mapType: (t: string) => string,
2074
2208
  resolver: TableDocResolver,
2209
+ constantTokens: ConstantSlotTokens,
2075
2210
  urlParameters: UrlParameterTable,
2076
2211
  ): string {
2077
2212
  const original = prepared.original.parameters;
@@ -2079,17 +2214,31 @@ function memberSignature(
2079
2214
  const cutoff = trailingOptionalCutoff(original, elementName);
2080
2215
  const varargIndex = original.findIndex(isVarargParameter);
2081
2216
  const positional = (varargIndex === -1 ? original : original.slice(0, varargIndex)).map((p, i) =>
2082
- emitParameter(p, i, i >= cutoff, mapType, resolver, elementName, urlParameters),
2217
+ emitParameter(p, i, i >= cutoff, mapType, resolver, constantTokens, elementName, urlParameters),
2083
2218
  );
2084
2219
  const params = (
2085
2220
  varargIndex === -1
2086
2221
  ? positional
2087
2222
  : [
2088
2223
  ...positional,
2089
- emitRestParameter(original, varargIndex, mapType, resolver, elementName, urlParameters),
2224
+ emitRestParameter(
2225
+ original,
2226
+ varargIndex,
2227
+ mapType,
2228
+ resolver,
2229
+ constantTokens,
2230
+ elementName,
2231
+ urlParameters,
2232
+ ),
2090
2233
  ]
2091
2234
  ).join(", ");
2092
- const ret = emitReturn(prepared.original.returnValues, mapType, resolver, elementName);
2235
+ const ret = emitReturn(
2236
+ prepared.original.returnValues,
2237
+ mapType,
2238
+ resolver,
2239
+ constantTokens,
2240
+ elementName,
2241
+ );
2093
2242
  const predicateToken = TYPE_PREDICATES.get(elementName);
2094
2243
  const soleParam = original[0];
2095
2244
  if (
@@ -2108,9 +2257,10 @@ function emitFunction(
2108
2257
  name: string,
2109
2258
  mapType: (t: string) => string,
2110
2259
  resolver: TableDocResolver,
2260
+ constantTokens: ConstantSlotTokens,
2111
2261
  urlParameters: UrlParameterTable,
2112
2262
  ): string {
2113
- return `function ${memberSignature(prepared, name, mapType, resolver, urlParameters)}`;
2263
+ return `function ${memberSignature(prepared, name, mapType, resolver, constantTokens, urlParameters)}`;
2114
2264
  }
2115
2265
 
2116
2266
  // A colon-method member of a handle interface: identical signature machinery to a
@@ -2120,9 +2270,10 @@ function emitMethod(
2120
2270
  prepared: PreparedFunction,
2121
2271
  mapType: (t: string) => string,
2122
2272
  resolver: TableDocResolver,
2273
+ constantTokens: ConstantSlotTokens,
2123
2274
  urlParameters: UrlParameterTable,
2124
2275
  ): string {
2125
- return memberSignature(prepared, prepared.name, mapType, resolver, urlParameters);
2276
+ return memberSignature(prepared, prepared.name, mapType, resolver, constantTokens, urlParameters);
2126
2277
  }
2127
2278
 
2128
2279
  // Build the indented JSDoc lines for a function from its ref-doc prose. The
@@ -2238,6 +2389,7 @@ function parameterType(
2238
2389
  p: ApiParameter,
2239
2390
  mapType: (t: string) => string,
2240
2391
  resolver: TableDocResolver,
2392
+ constantTokens: ConstantSlotTokens,
2241
2393
  elementName: string,
2242
2394
  urlParameters: UrlParameterTable,
2243
2395
  ): string {
@@ -2247,13 +2399,25 @@ function parameterType(
2247
2399
  const alias = SCENE_ADDRESS_ALIASES[classifyUrlParameter(urlParameters, elementName, p.name)];
2248
2400
  const slotMapType = alias === undefined ? mapType : addressMapType(mapType, alias);
2249
2401
  return concrete.length > 0
2250
- ? mapSlotUnion(concrete, p.doc, slotMapType, true, resolver, elementName, "param", p.name)
2402
+ ? mapSlotUnion(
2403
+ concrete,
2404
+ p.doc,
2405
+ slotMapType,
2406
+ true,
2407
+ resolver,
2408
+ constantTokens,
2409
+ elementName,
2410
+ "param",
2411
+ p.name,
2412
+ )
2251
2413
  : "unknown";
2252
2414
  }
2253
2415
 
2254
2416
  const VARARG_PREFIX = "...";
2255
2417
 
2256
- function isVarargParameter(p: ApiParameter): boolean {
2418
+ // Exported because the fidelity audit must ask the same question about the
2419
+ // same input: what the emitted declaration accepts is the emitter's to decide.
2420
+ export function isVarargParameter(p: ApiParameter): boolean {
2257
2421
  return p.name.startsWith(VARARG_PREFIX);
2258
2422
  }
2259
2423
 
@@ -2277,6 +2441,7 @@ function emitRestParameter(
2277
2441
  varargIndex: number,
2278
2442
  mapType: (t: string) => string,
2279
2443
  resolver: TableDocResolver,
2444
+ constantTokens: ConstantSlotTokens,
2280
2445
  elementName: string,
2281
2446
  urlParameters: UrlParameterTable,
2282
2447
  ): string {
@@ -2286,7 +2451,9 @@ function emitRestParameter(
2286
2451
  ...new Set(
2287
2452
  params
2288
2453
  .slice(varargIndex)
2289
- .map((p) => parameterType(p, mapType, resolver, elementName, urlParameters)),
2454
+ .map((p) =>
2455
+ parameterType(p, mapType, resolver, constantTokens, elementName, urlParameters),
2456
+ ),
2290
2457
  ),
2291
2458
  ];
2292
2459
  const first = members[0] ?? "unknown";
@@ -2300,11 +2467,12 @@ function emitParameter(
2300
2467
  optional: boolean,
2301
2468
  mapType: (t: string) => string,
2302
2469
  resolver: TableDocResolver,
2470
+ constantTokens: ConstantSlotTokens,
2303
2471
  elementName: string,
2304
2472
  urlParameters: UrlParameterTable,
2305
2473
  ): string {
2306
2474
  const name = safeParamName(p.name, index);
2307
- const ts = parameterType(p, mapType, resolver, elementName, urlParameters);
2475
+ const ts = parameterType(p, mapType, resolver, constantTokens, elementName, urlParameters);
2308
2476
  // An interior doc-optional param (a required param follows, so the trailing-`?`
2309
2477
  // projection cannot mark it) keeps its optionality as `| undefined` — TSTL
2310
2478
  // lowers `undefined` to `nil`, the faithful call. Trailing optionals keep the
@@ -2317,6 +2485,7 @@ function emitReturn(
2317
2485
  returnValues: ApiParameter[],
2318
2486
  mapType: (t: string) => string,
2319
2487
  resolver: TableDocResolver,
2488
+ constantTokens: ConstantSlotTokens,
2320
2489
  elementName: string,
2321
2490
  ): { type: string; trailing: string } {
2322
2491
  const override = RETURN_TYPE_OVERRIDES.get(elementName);
@@ -2328,7 +2497,17 @@ function emitReturn(
2328
2497
  // typescript-to-lua erases to `local a, b = fn()`.
2329
2498
  const slots = returnValues.map((rv) =>
2330
2499
  rv.types.length > 0
2331
- ? mapSlotUnion(rv.types, rv.doc, mapType, false, resolver, elementName, "return", rv.name)
2500
+ ? mapSlotUnion(
2501
+ rv.types,
2502
+ rv.doc,
2503
+ mapType,
2504
+ false,
2505
+ resolver,
2506
+ constantTokens,
2507
+ elementName,
2508
+ "return",
2509
+ rv.name,
2510
+ )
2332
2511
  : "unknown",
2333
2512
  );
2334
2513
  return { type: `LuaMultiReturn<[${slots.join(", ")}]>`, trailing: "" };
@@ -2343,6 +2522,7 @@ function emitReturn(
2343
2522
  mapType,
2344
2523
  false,
2345
2524
  resolver,
2525
+ constantTokens,
2346
2526
  elementName,
2347
2527
  "return",
2348
2528
  first.name,
@@ -2380,13 +2560,26 @@ function mapSlotUnion(
2380
2560
  mapType: (t: string) => string,
2381
2561
  optionalFields: boolean,
2382
2562
  resolver: TableDocResolver,
2563
+ constantTokens: ConstantSlotTokens,
2383
2564
  elementName: string,
2384
2565
  slotKind?: "param" | "return",
2385
2566
  slotName?: string,
2386
2567
  ): string {
2387
2568
  const mapped: string[] = [];
2388
2569
  const seen = new Set<string>();
2570
+ const push = (ts: string): void => {
2571
+ if (seen.has(ts)) return;
2572
+ seen.add(ts);
2573
+ mapped.push(ts);
2574
+ };
2389
2575
  for (const token of types) {
2576
+ if (token === "constant" && slotKind !== undefined && slotName !== undefined) {
2577
+ const expanded = constantTokens(elementName, slotKind, slotName, doc);
2578
+ if (expanded.length > 0) {
2579
+ for (const fqn of expanded) push(mapType(fqn));
2580
+ continue;
2581
+ }
2582
+ }
2390
2583
  let ts: string;
2391
2584
  if (token === "table") {
2392
2585
  const curation =
@@ -2457,9 +2650,7 @@ function mapSlotUnion(
2457
2650
  : undefined;
2458
2651
  ts = curated ?? mapType(token);
2459
2652
  }
2460
- if (seen.has(ts)) continue;
2461
- seen.add(ts);
2462
- mapped.push(ts);
2653
+ push(ts);
2463
2654
  }
2464
2655
  // A genuine `unknown` member (an `any` slot, including `any | nil`) absorbs
2465
2656
  // every other union member, so collapse to exactly `unknown` rather than emit
@@ -24,13 +24,19 @@ interface VersionedManifestEntry extends ManifestEntry {
24
24
  readonly editor?: boolean;
25
25
  }
26
26
 
27
+ // The provenance target the committed extension goldens are recorded under. They
28
+ // belong to no engine version: `resolve` applies the same correction table to them.
29
+ export const EXTENSION_GOLDENS_TARGET = "extension-goldens";
30
+
27
31
  // Every runtime module regen emits through the global correction table: the
28
- // default target's manifest plus every older generated target's. Editor-VM
29
- // entries are outside the scope the corrections cover.
32
+ // default target's manifest plus every older generated target's, plus the
33
+ // extension goldens when given. Editor-VM entries are outside the scope the
34
+ // corrections cover.
30
35
  export function retainedSurfaces(
31
36
  defaultTarget: string,
32
37
  defaultManifest: readonly ManifestEntry[],
33
38
  versionedManifest: readonly VersionedManifestEntry[],
39
+ extensionGoldenManifest: readonly ManifestEntry[] = [],
34
40
  ): ProvenanceSurface[] {
35
41
  return [
36
42
  ...defaultManifest.map((entry) => ({
@@ -41,6 +47,11 @@ export function retainedSurfaces(
41
47
  ...versionedManifest
42
48
  .filter((entry) => entry.editor !== true)
43
49
  .map((entry) => ({ target: entry.versionId, namespace: entry.namespace, doc: entry.doc })),
50
+ ...extensionGoldenManifest.map((entry) => ({
51
+ target: EXTENSION_GOLDENS_TARGET,
52
+ namespace: entry.namespace,
53
+ doc: entry.doc,
54
+ })),
44
55
  ];
45
56
  }
46
57
 
@@ -0,0 +1,29 @@
1
+ /** @noSelfInFile */
2
+
3
+ import type { Opaque } from "./core-types";
4
+
5
+ declare global {
6
+ namespace render {
7
+ /**
8
+ * Creates a new render target according to the supplied specification table.
9
+ * The parameter table is keyed by buffer type, with one parameter table per
10
+ * attachment; see the reference for the available keys and values.
11
+ *
12
+ * @param parameters - table of buffer parameters, see the description for available keys and values
13
+ * @returns new render target
14
+ */
15
+ function render_target(parameters: Record<string | number, unknown>): Opaque<"render_target">;
16
+ /**
17
+ * Creates a new named render target according to the supplied specification
18
+ * table. The name is used by the profiler and by render target lookups.
19
+ *
20
+ * @param name - render target name
21
+ * @param parameters - table of buffer parameters, see the description for available keys and values
22
+ * @returns new render target
23
+ */
24
+ function render_target(
25
+ name: string,
26
+ parameters: Record<string | number, unknown>,
27
+ ): Opaque<"render_target">;
28
+ }
29
+ }