@defold-typescript/types 0.35.0 → 0.37.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 (36) hide show
  1. package/api-signatures.json +9406 -18
  2. package/examples/translations.json +11 -11
  3. package/generated/b2d.d.ts +1 -1
  4. package/generated/b2d_body.d.ts +6 -4
  5. package/generated/b2d_fixture.d.ts +6 -4
  6. package/generated/b2d_shape.d.ts +6 -4
  7. package/generated/factory.d.ts +11 -10
  8. package/generated/render.d.ts +5 -5
  9. package/generated/resource.d.ts +4 -4
  10. package/generated/sys.d.ts +2 -2
  11. package/generated/versions/defold-1.12.4/b2d.d.ts +1 -1
  12. package/generated/versions/defold-1.12.4/factory.d.ts +11 -10
  13. package/generated/versions/defold-1.12.4/render.d.ts +5 -5
  14. package/generated/versions/defold-1.12.4/resource.d.ts +4 -4
  15. package/generated/versions/defold-1.12.4/sys.d.ts +2 -2
  16. package/generated/versions/defold-1.12.4/window.d.ts +1 -1
  17. package/generated/versions/defold-1.12.4/zlib.d.ts +2 -1
  18. package/generated/versions/defold-1.13.0/b2d.d.ts +1 -1
  19. package/generated/versions/defold-1.13.0/b2d_body.d.ts +6 -4
  20. package/generated/versions/defold-1.13.0/b2d_fixture.d.ts +6 -4
  21. package/generated/versions/defold-1.13.0/b2d_shape.d.ts +6 -4
  22. package/generated/versions/defold-1.13.0/factory.d.ts +11 -10
  23. package/generated/versions/defold-1.13.0/render.d.ts +5 -5
  24. package/generated/versions/defold-1.13.0/resource.d.ts +4 -4
  25. package/generated/versions/defold-1.13.0/sys.d.ts +2 -2
  26. package/generated/versions/defold-1.13.0/window.d.ts +1 -1
  27. package/generated/versions/defold-1.13.0/zlib.d.ts +2 -1
  28. package/generated/window.d.ts +1 -1
  29. package/generated/zlib.d.ts +2 -1
  30. package/index.d.ts +1 -0
  31. package/package.json +5 -2
  32. package/scripts/generate-api-signatures.ts +33 -8
  33. package/src/emit-dts.ts +355 -71
  34. package/src/index.ts +1 -0
  35. package/src/render-overloads.d.ts +17 -0
  36. package/src/return-correction-provenance.ts +306 -0
@@ -4,6 +4,23 @@ import type { Opaque } from "./core-types";
4
4
 
5
5
  declare global {
6
6
  namespace render {
7
+ /**
8
+ * The buffer keys `render.clear` accepts: the three `graphics.BUFFER_TYPE_*`
9
+ * constants the reference names. Declare the clear table with this alias —
10
+ * `LuaMap`'s iterator makes its key parameter invariant, so a
11
+ * `LuaMap<number, ...>` is not assignable however its entries were set.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const buffers = new LuaMap<render.ClearBufferKey, number | Vector4>();
16
+ * buffers.set(graphics.BUFFER_TYPE_COLOR0_BIT, vmath.vector4(0, 0, 0, 0));
17
+ * render.clear(buffers);
18
+ * ```
19
+ */
20
+ type ClearBufferKey =
21
+ | typeof graphics.BUFFER_TYPE_COLOR0_BIT
22
+ | typeof graphics.BUFFER_TYPE_DEPTH_BIT
23
+ | typeof graphics.BUFFER_TYPE_STENCIL_BIT;
7
24
  /**
8
25
  * Creates a new render target according to the supplied specification table.
9
26
  * The parameter table is keyed by buffer type, with one parameter table per
@@ -0,0 +1,306 @@
1
+ // Provenance helpers for the two return-side correction tables. Both read the
2
+ // vendored ref-doc as text rather than through `parseDefoldApiDoc`, because the
3
+ // evidence a return-side correction rests on lives in the slot's HTML prose and
4
+ // the parser discards it.
5
+
6
+ import type { ReturnFieldOptionalityCorrection, ReturnTypeCorrection } from "./emit-dts";
7
+ import type { ProvenanceSurface } from "./optional-correction-provenance";
8
+
9
+ const NAMED_ENTITIES: Record<string, string> = {
10
+ "&lt;": "<",
11
+ "&gt;": ">",
12
+ "&quot;": '"',
13
+ "&#39;": "'",
14
+ "&apos;": "'",
15
+ };
16
+
17
+ function decodeEntities(text: string): string {
18
+ let out = text;
19
+ for (const [entity, char] of Object.entries(NAMED_ENTITIES)) {
20
+ out = out.split(entity).join(char);
21
+ }
22
+ out = out.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code)));
23
+ // `&amp;` last so an already-decoded `&` is never re-interpreted.
24
+ return out.split("&amp;").join("&");
25
+ }
26
+
27
+ /**
28
+ * Decode a ref-doc prose fragment to the plain text a correction's `evidence`
29
+ * string is matched against.
30
+ *
31
+ * Unlike `htmlToDocText`, a `<span class="icon-X">` marker survives as `[X]`.
32
+ * Upstream states some field-presence gates only through that marker —
33
+ * `user_agent` carries `icon-html5` and no availability sentence at all — so
34
+ * stripping it would leave those corrections pinned to prose that says nothing
35
+ * about presence, and an upstream fix removing the gate would not red them.
36
+ */
37
+ export function decodeSlotProse(html: string): string {
38
+ const withIcons = html.replace(
39
+ /<span class="icon-([a-z0-9-]+)"><\/span>/gi,
40
+ (_, platform: string) => `[${platform}]`,
41
+ );
42
+ return decodeEntities(withIcons.replace(/<[^>]+>/g, ""))
43
+ .replace(/[ \t]+/g, " ")
44
+ .replace(/ *\n */g, "\n")
45
+ .trim();
46
+ }
47
+
48
+ /**
49
+ * The decoded prose of one `<dd>` in a slot doc's `<dl>`, keyed by the `<dt>`
50
+ * field name. Returns `undefined` when the slot doc has no entry for the field,
51
+ * which is the signal that upstream renamed or dropped it.
52
+ */
53
+ export function fieldProse(slotDoc: string, field: string): string | undefined {
54
+ const escaped = field.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55
+ const match = new RegExp(`<dt><code>${escaped}</code></dt>\\s*<dd>([\\s\\S]*?)</dd>`, "i").exec(
56
+ slotDoc,
57
+ );
58
+ return match?.[1] === undefined ? undefined : decodeSlotProse(match[1]);
59
+ }
60
+
61
+ /**
62
+ * The `<span class="type">` tokens upstream declares for one `<dd>`, split the
63
+ * way the table-field parser splits them. An absent field yields `undefined`;
64
+ * a field with no type span yields an empty array.
65
+ */
66
+ export function fieldTypeTokens(slotDoc: string, field: string): readonly string[] | undefined {
67
+ const escaped = field.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68
+ const match = new RegExp(`<dt><code>${escaped}</code></dt>\\s*<dd>([\\s\\S]*?)</dd>`, "i").exec(
69
+ slotDoc,
70
+ );
71
+ if (match?.[1] === undefined) return undefined;
72
+ const span = /<span class="type">([^<]*)<\/span>/.exec(match[1]);
73
+ if (span?.[1] === undefined) return [];
74
+ return span[1]
75
+ .split("|")
76
+ .map((token) => token.trim())
77
+ .filter((token) => token.length > 0);
78
+ }
79
+
80
+ interface RawSlot {
81
+ readonly name?: string;
82
+ readonly doc?: string;
83
+ readonly types?: readonly string[];
84
+ }
85
+
86
+ function isRecord(value: unknown): value is Record<string, unknown> {
87
+ return typeof value === "object" && value !== null && !Array.isArray(value);
88
+ }
89
+
90
+ // Every `returnvalues` entry of every FUNCTION in the surface named `element`,
91
+ // unfiltered by slot: one target can declare the element more than once.
92
+ function returnSlots(doc: unknown, element: string): RawSlot[] {
93
+ if (!isRecord(doc) || !Array.isArray(doc.elements)) return [];
94
+ return doc.elements.flatMap((candidate) =>
95
+ isRecord(candidate) &&
96
+ candidate.type === "FUNCTION" &&
97
+ candidate.name === element &&
98
+ Array.isArray(candidate.returnvalues)
99
+ ? candidate.returnvalues.filter(isRecord)
100
+ : [],
101
+ );
102
+ }
103
+
104
+ // The first `returnvalues` entry of each declaration of `element`, one per
105
+ // declaration so a target that declares the element twice is folded, not sliced.
106
+ function firstReturnSlots(doc: unknown, element: string): RawSlot[] {
107
+ if (!isRecord(doc) || !Array.isArray(doc.elements)) return [];
108
+ return doc.elements.flatMap((candidate) => {
109
+ if (
110
+ !isRecord(candidate) ||
111
+ candidate.type !== "FUNCTION" ||
112
+ candidate.name !== element ||
113
+ !Array.isArray(candidate.returnvalues)
114
+ ) {
115
+ return [];
116
+ }
117
+ const first = candidate.returnvalues[0];
118
+ return isRecord(first) ? [first] : [];
119
+ });
120
+ }
121
+
122
+ function namespaceOf(element: string): string {
123
+ return element.slice(0, element.lastIndexOf("."));
124
+ }
125
+
126
+ // A sighted target lands in exactly one bucket, strongest signal first. A
127
+ // dropped field outranks a needed one because `applyFieldOptionalityCorrections`
128
+ // throws for that target, so regen fails there whatever the other declarations
129
+ // say; a needed one outranks a resolved one so an overload that still shows the
130
+ // defect keeps the correction alive, matching `correctionProvenance`.
131
+ type FieldVerdict = "missing" | "needed" | "resolved";
132
+
133
+ const FIELD_VERDICT_RANK: Record<FieldVerdict, number> = {
134
+ missing: 2,
135
+ needed: 1,
136
+ resolved: 0,
137
+ };
138
+
139
+ export interface ReturnFieldProvenance {
140
+ readonly key: string;
141
+ readonly sightedIn: readonly string[];
142
+ readonly neededBy: readonly string[];
143
+ readonly resolvedIn: readonly string[];
144
+ readonly missingFieldIn: readonly string[];
145
+ readonly typeDrift: readonly string[];
146
+ }
147
+
148
+ // Upstream sets no structural optionality flag on a return *field* — the parser
149
+ // never populates `TableField.optional`, only the curation and correction passes
150
+ // do — so there is no `isMarkedOptional` analogue to read here. "Resolved" is
151
+ // therefore prose- and token-shaped: the evidence sentence is gone from the
152
+ // field's `<dd>`, or its type span now declares `nil` itself.
153
+ //
154
+ // `missingFieldIn` is deliberately not a resolution. A field upstream dropped
155
+ // from a slot the target still declares makes the correction stale, and
156
+ // `applyFieldOptionalityCorrections` throws rather than no-op for it, so that
157
+ // target needs a human decision instead of a silent deletion.
158
+ export function fieldCorrectionProvenance(
159
+ entries: readonly [string, ReturnFieldOptionalityCorrection][],
160
+ surfaces: readonly ProvenanceSurface[],
161
+ ): ReturnFieldProvenance[] {
162
+ return entries.map(([key, correction]) => {
163
+ const parts = key.split(":");
164
+ if (parts.length !== 4) {
165
+ throw new Error(`fieldCorrectionProvenance: malformed key ${key}`);
166
+ }
167
+ const [element, , slotName, field] = parts as [string, string, string, string];
168
+ const namespace = namespaceOf(element);
169
+
170
+ const verdicts = new Map<string, FieldVerdict>();
171
+ const drift = new Map<string, Set<string>>();
172
+ for (const surface of surfaces) {
173
+ if (surface.namespace !== namespace) continue;
174
+ for (const slot of returnSlots(surface.doc, element)) {
175
+ if (slot.name !== slotName) continue;
176
+ const slotDoc = slot.doc ?? "";
177
+ const prose = fieldProse(slotDoc, field);
178
+ const tokens = fieldTypeTokens(slotDoc, field);
179
+
180
+ let verdict: FieldVerdict;
181
+ if (prose === undefined) {
182
+ verdict = "missing";
183
+ } else if (!prose.includes(correction.evidence) || (tokens ?? []).includes("nil")) {
184
+ verdict = "resolved";
185
+ } else {
186
+ verdict = "needed";
187
+ if ((tokens ?? []).join("|") !== correction.upstream.join("|")) {
188
+ const messages = drift.get(surface.target) ?? new Set<string>();
189
+ drift.set(surface.target, messages);
190
+ messages.add(
191
+ `${surface.target}: pinned ${correction.upstream.join("|")}, found ${tokens?.join("|") ?? "no field"}`,
192
+ );
193
+ }
194
+ }
195
+
196
+ const seen = verdicts.get(surface.target);
197
+ if (seen === undefined || FIELD_VERDICT_RANK[verdict] > FIELD_VERDICT_RANK[seen]) {
198
+ verdicts.set(surface.target, verdict);
199
+ }
200
+ }
201
+ }
202
+
203
+ const sightedIn = [...verdicts.keys()].sort();
204
+ const inBucket = (verdict: FieldVerdict) =>
205
+ sightedIn.filter((target) => verdicts.get(target) === verdict);
206
+ const neededBy = inBucket("needed");
207
+ return {
208
+ key,
209
+ sightedIn,
210
+ neededBy,
211
+ resolvedIn: inBucket("resolved"),
212
+ missingFieldIn: inBucket("missing"),
213
+ typeDrift: neededBy.flatMap((target) => [...(drift.get(target) ?? [])]).sort(),
214
+ };
215
+ });
216
+ }
217
+
218
+ export interface ReturnTypeProvenance {
219
+ readonly key: string;
220
+ readonly sightedIn: readonly string[];
221
+ readonly neededBy: readonly string[];
222
+ readonly resolvedIn: readonly string[];
223
+ readonly driftedIn: readonly string[];
224
+ readonly typeDrift: readonly string[];
225
+ }
226
+
227
+ // Same ranking argument as `FIELD_VERDICT_RANK`: `emitReturn` applies the
228
+ // correction to every declaration of the FQN in a target, so one drifted
229
+ // declaration makes that target's emit wrong whatever its siblings say.
230
+ type ReturnVerdict = "drift" | "needed" | "resolved";
231
+
232
+ const RETURN_VERDICT_RANK: Record<ReturnVerdict, number> = {
233
+ drift: 2,
234
+ needed: 1,
235
+ resolved: 0,
236
+ };
237
+
238
+ function baseTokens(tokens: readonly string[]): string {
239
+ return tokens.filter((token) => token !== "nil").join("|");
240
+ }
241
+
242
+ // The return-type class has the same two-sided evidence: the declared tokens
243
+ // still say what the correction contradicts, and the slot prose still states the
244
+ // contradiction. A target that fixes either half no longer needs the entry.
245
+ //
246
+ // Unlike the field lane, a changed base type is drift here rather than a
247
+ // resolution. A field correction only flips `optional` and leaves the type
248
+ // parser-authoritative, so a retyped field still emits what upstream declares; a
249
+ // return correction replaces the whole emitted type, so a base upstream no longer
250
+ // declares means the correction now overwrites a type nobody verified — a
251
+ // decision for a human, not a deletion and not a silent bank.
252
+ export function returnCorrectionProvenance(
253
+ entries: readonly [string, ReturnTypeCorrection][],
254
+ surfaces: readonly ProvenanceSurface[],
255
+ ): ReturnTypeProvenance[] {
256
+ return entries.map(([key, correction]) => {
257
+ const namespace = namespaceOf(key);
258
+ const pinned = baseTokens(correction.upstream);
259
+
260
+ const verdicts = new Map<string, ReturnVerdict>();
261
+ const drift = new Map<string, Set<string>>();
262
+ for (const surface of surfaces) {
263
+ if (surface.namespace !== namespace) continue;
264
+ // The correction rewrites the first return only, so only that slot is
265
+ // evidence; a declaration with no returns at all sights nothing.
266
+ for (const slot of firstReturnSlots(surface.doc, key)) {
267
+ const tokens = slot.types ?? [];
268
+
269
+ let verdict: ReturnVerdict;
270
+ if (baseTokens(tokens) !== pinned) {
271
+ verdict = "drift";
272
+ const messages = drift.get(surface.target) ?? new Set<string>();
273
+ drift.set(surface.target, messages);
274
+ messages.add(
275
+ `${surface.target}: pinned ${correction.upstream.join("|")}, found ${tokens.join("|")}`,
276
+ );
277
+ } else if (
278
+ tokens.includes("nil") ||
279
+ !decodeSlotProse(slot.doc ?? "").includes(correction.reason)
280
+ ) {
281
+ verdict = "resolved";
282
+ } else {
283
+ verdict = "needed";
284
+ }
285
+
286
+ const seen = verdicts.get(surface.target);
287
+ if (seen === undefined || RETURN_VERDICT_RANK[verdict] > RETURN_VERDICT_RANK[seen]) {
288
+ verdicts.set(surface.target, verdict);
289
+ }
290
+ }
291
+ }
292
+
293
+ const sightedIn = [...verdicts.keys()].sort();
294
+ const inBucket = (verdict: ReturnVerdict) =>
295
+ sightedIn.filter((target) => verdicts.get(target) === verdict);
296
+ const driftedIn = inBucket("drift");
297
+ return {
298
+ key,
299
+ sightedIn,
300
+ neededBy: inBucket("needed"),
301
+ resolvedIn: inBucket("resolved"),
302
+ driftedIn,
303
+ typeDrift: driftedIn.flatMap((target) => [...(drift.get(target) ?? [])]).sort(),
304
+ };
305
+ });
306
+ }