@defold-typescript/types 0.32.1 → 0.33.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.
- package/api-signatures.json +46 -46
- package/generated/b2d_fixture.d.ts +1 -1
- package/generated/b2d_joint.d.ts +12 -12
- package/generated/b2d_shape.d.ts +1 -1
- package/generated/gui.d.ts +2 -2
- package/generated/iap.d.ts +1 -1
- package/generated/resource.d.ts +2 -2
- package/generated/sys.d.ts +1 -1
- package/generated/versions/defold-1.12.4/gui.d.ts +2 -2
- package/generated/versions/defold-1.12.4/iap.d.ts +1 -1
- package/generated/versions/defold-1.12.4/resource.d.ts +2 -2
- package/generated/versions/defold-1.12.4/sys.d.ts +1 -1
- package/generated/versions/defold-1.13.0/b2d_fixture.d.ts +1 -1
- package/generated/versions/defold-1.13.0/b2d_joint.d.ts +12 -12
- package/generated/versions/defold-1.13.0/b2d_shape.d.ts +1 -1
- package/generated/versions/defold-1.13.0/gui.d.ts +2 -2
- package/generated/versions/defold-1.13.0/iap.d.ts +1 -1
- package/generated/versions/defold-1.13.0/resource.d.ts +2 -2
- package/generated/versions/defold-1.13.0/sys.d.ts +1 -1
- package/package.json +1 -1
- package/scripts/fidelity-audit.ts +213 -4
- package/src/emit-dts.ts +84 -5
- package/src/msg-overloads.d.ts +19 -2
- package/src/optional-correction-provenance.ts +126 -0
package/src/emit-dts.ts
CHANGED
|
@@ -288,6 +288,69 @@ export const PROPERTY_TYPE_CORRECTIONS: ReadonlyMap<string, PropertyTypeCorrecti
|
|
|
288
288
|
],
|
|
289
289
|
]);
|
|
290
290
|
|
|
291
|
+
// A parameter the ref-doc metadata marks required while the same element's prose
|
|
292
|
+
// or examples show it being omitted. `is_optional` is the only optionality
|
|
293
|
+
// signal the emitter has, and upstream leaves it `False` on slots its own
|
|
294
|
+
// examples skip — so the emitted declaration rejects the call the docs teach.
|
|
295
|
+
//
|
|
296
|
+
// Keyed `<element>:param:<slot>`, the shape `tableSlotKey` builds, so a
|
|
297
|
+
// same-named slot on another element is untouched. The value is the upstream
|
|
298
|
+
// evidence for that one entry: the example call that omits it, or the doc phrase
|
|
299
|
+
// that names it optional. Both emitted shapes follow from one predicate — a
|
|
300
|
+
// trailing slot becomes `slot?:`, an interior one `slot: T | undefined`.
|
|
301
|
+
//
|
|
302
|
+
// Demonstrated omissions only. `resource.set_texture`'s buffer and
|
|
303
|
+
// `sys.load_buffer_async`'s status_callback read as optional but every upstream
|
|
304
|
+
// example passes them, so neither is a correction candidate.
|
|
305
|
+
export const OPTIONAL_SLOT_CORRECTIONS: ReadonlyMap<string, string> = new Map([
|
|
306
|
+
[
|
|
307
|
+
"resource.create_texture:param:buffer",
|
|
308
|
+
'example `resource.create_texture("/my_custom_texture.texturec", tparams)`; "optional buffer of precreated pixel data"',
|
|
309
|
+
],
|
|
310
|
+
["resource.create_texture_async:param:buffer", '"optional buffer of precreated pixel data"'],
|
|
311
|
+
[
|
|
312
|
+
"resource.create_texture_async:param:callback",
|
|
313
|
+
'example `resource.create_texture_async("/my_texture.texturec", tparams, tbuffer)`',
|
|
314
|
+
],
|
|
315
|
+
[
|
|
316
|
+
"gui.new_texture:param:flip",
|
|
317
|
+
'example `gui.new_texture("orange_tx", w, h, "rgb", string.rep(orange, w * h))`',
|
|
318
|
+
],
|
|
319
|
+
[
|
|
320
|
+
"gui.set_texture_data:param:flip",
|
|
321
|
+
'example `gui.set_texture_data("dynamic_tx", w, h, "rgb", string.rep(orange, w * h))`',
|
|
322
|
+
],
|
|
323
|
+
["sys.set_engine_throttle:param:cooldown", "example `sys.set_engine_throttle(false)`"],
|
|
324
|
+
[
|
|
325
|
+
"b2d.fixture.set_shape:param:update_mass",
|
|
326
|
+
'2 of 3 examples omit it; "The body mass is not updated unless update_mass is true"',
|
|
327
|
+
],
|
|
328
|
+
[
|
|
329
|
+
"b2d.shape.ray_cast:param:max_fraction",
|
|
330
|
+
'"optional maximum translation fraction, defaults to 1"',
|
|
331
|
+
],
|
|
332
|
+
["iap.buy:param:options", '"optional parameters as properties"'],
|
|
333
|
+
...(
|
|
334
|
+
[
|
|
335
|
+
"create_distance",
|
|
336
|
+
"create_mouse",
|
|
337
|
+
"create_prismatic",
|
|
338
|
+
"create_revolute",
|
|
339
|
+
"create_weld",
|
|
340
|
+
"create_wheel",
|
|
341
|
+
"create_friction",
|
|
342
|
+
"create_rope",
|
|
343
|
+
"create_pulley",
|
|
344
|
+
"create_gear",
|
|
345
|
+
"create_motor",
|
|
346
|
+
"create_filter",
|
|
347
|
+
] as const
|
|
348
|
+
).map(
|
|
349
|
+
(name) =>
|
|
350
|
+
[`b2d.joint.${name}:param:definition`, '"optional definition"'] as readonly [string, string],
|
|
351
|
+
),
|
|
352
|
+
]);
|
|
353
|
+
|
|
291
354
|
// FQN-keyed allowlist of the `types.is_*` checks that genuinely narrow their
|
|
292
355
|
// argument, mapped to the `DEFOLD_TYPE_MAP` token whose interface they prove.
|
|
293
356
|
// Emitting these as user-defined type guards (`var_ is Vector3`) is the only way
|
|
@@ -2013,7 +2076,7 @@ function memberSignature(
|
|
|
2013
2076
|
): string {
|
|
2014
2077
|
const original = prepared.original.parameters;
|
|
2015
2078
|
const elementName = prepared.original.name;
|
|
2016
|
-
const cutoff = trailingOptionalCutoff(original);
|
|
2079
|
+
const cutoff = trailingOptionalCutoff(original, elementName);
|
|
2017
2080
|
const varargIndex = original.findIndex(isVarargParameter);
|
|
2018
2081
|
const positional = (varargIndex === -1 ? original : original.slice(0, varargIndex)).map((p, i) =>
|
|
2019
2082
|
emitParameter(p, i, i >= cutoff, mapType, resolver, elementName, urlParameters),
|
|
@@ -2124,15 +2187,31 @@ export function summaryFor(brief: string, description: string): string {
|
|
|
2124
2187
|
return description.trim() !== "" ? description : brief;
|
|
2125
2188
|
}
|
|
2126
2189
|
|
|
2127
|
-
function
|
|
2190
|
+
export function isMarkedOptional(p: ApiParameter): boolean {
|
|
2128
2191
|
return p.isOptional || p.types.includes("nil");
|
|
2129
2192
|
}
|
|
2130
2193
|
|
|
2131
|
-
|
|
2194
|
+
// The one optionality predicate, in two halves: the ref-doc's own marking
|
|
2195
|
+
// (`isMarkedOptional`, which the correction provenance guards read alone to
|
|
2196
|
+
// tell which targets still need a correction) and the OPTIONAL_SLOT_CORRECTIONS
|
|
2197
|
+
// entries that override it. Both emitted shapes read it — the trailing `?`
|
|
2198
|
+
// through `trailingOptionalCutoff` and the interior `| undefined` through
|
|
2199
|
+
// `emitParameter` — so a correction threaded in here reaches both without a
|
|
2200
|
+
// second copy of the rule. The fidelity audit reads it too, to tell which slots
|
|
2201
|
+
// the emitted surface still makes required.
|
|
2202
|
+
export function isDocOptional(p: ApiParameter, elementName: string): boolean {
|
|
2203
|
+
if (isMarkedOptional(p)) return true;
|
|
2204
|
+
return OPTIONAL_SLOT_CORRECTIONS.has(tableSlotKey(elementName, "param", p.name));
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
export function trailingOptionalCutoff(
|
|
2208
|
+
params: readonly ApiParameter[],
|
|
2209
|
+
elementName: string,
|
|
2210
|
+
): number {
|
|
2132
2211
|
let cutoff = params.length;
|
|
2133
2212
|
for (let i = params.length - 1; i >= 0; i -= 1) {
|
|
2134
2213
|
const p = params[i];
|
|
2135
|
-
if (p && isDocOptional(p)) cutoff = i;
|
|
2214
|
+
if (p && isDocOptional(p, elementName)) cutoff = i;
|
|
2136
2215
|
else break;
|
|
2137
2216
|
}
|
|
2138
2217
|
return cutoff;
|
|
@@ -2230,7 +2309,7 @@ function emitParameter(
|
|
|
2230
2309
|
// projection cannot mark it) keeps its optionality as `| undefined` — TSTL
|
|
2231
2310
|
// lowers `undefined` to `nil`, the faithful call. Trailing optionals keep the
|
|
2232
2311
|
// `?` form; required params are untouched.
|
|
2233
|
-
const interiorOptional = !optional && isDocOptional(p) ? " | undefined" : "";
|
|
2312
|
+
const interiorOptional = !optional && isDocOptional(p, elementName) ? " | undefined" : "";
|
|
2234
2313
|
return `${name}${optional ? "?" : ""}: ${ts}${interiorOptional}`;
|
|
2235
2314
|
}
|
|
2236
2315
|
|
package/src/msg-overloads.d.ts
CHANGED
|
@@ -48,6 +48,13 @@ declare global {
|
|
|
48
48
|
*
|
|
49
49
|
* The two-arg form `msg.url(socket, path)` is a runtime error.
|
|
50
50
|
*
|
|
51
|
+
* `socket` may be `undefined` (Lua `nil`), which addresses the current
|
|
52
|
+
* game world, exactly as the runtime's own examples do.
|
|
53
|
+
*
|
|
54
|
+
* The one-argument form's `urlstring` may likewise be `undefined` (Lua
|
|
55
|
+
* `nil`), which the ref-doc calls equivalent to `msg.url()` — the URL of the
|
|
56
|
+
* current script.
|
|
57
|
+
*
|
|
51
58
|
* In the same world, address a sibling **relatively** — by bare id
|
|
52
59
|
* (`msg.url("camera")`), absolute path (`msg.url("/camera")`), or
|
|
53
60
|
* component (`msg.url("#main")`). The `socket:` prefix only crosses
|
|
@@ -58,6 +65,9 @@ declare global {
|
|
|
58
65
|
* // No-arg, then take the current game object's URL.
|
|
59
66
|
* const self: Url = msg.url();
|
|
60
67
|
*
|
|
68
|
+
* // An `undefined` argument is the no-arg form spelled out.
|
|
69
|
+
* const alsoSelf: Url = msg.url(undefined);
|
|
70
|
+
*
|
|
61
71
|
* // A sibling in the same world — bare id, no socket prefix.
|
|
62
72
|
* const sibling: Url = msg.url("camera");
|
|
63
73
|
*
|
|
@@ -69,10 +79,17 @@ declare global {
|
|
|
69
79
|
*
|
|
70
80
|
* // Crossing into a proxy-loaded world — `socket` is the world name.
|
|
71
81
|
* const proxied: Url = msg.url(hash("level1"), hash("/door"), hash("script"));
|
|
82
|
+
*
|
|
83
|
+
* // An `undefined` socket addresses the current game world.
|
|
84
|
+
* const currentWorld: Url = msg.url(undefined, hash("/door"), hash("script"));
|
|
72
85
|
* ```
|
|
73
86
|
*/
|
|
74
87
|
function url(): Url;
|
|
75
|
-
function url(urlstring: SceneAddress): Url;
|
|
76
|
-
function url(
|
|
88
|
+
function url(urlstring: SceneAddress | undefined): Url;
|
|
89
|
+
function url(
|
|
90
|
+
socket: string | Hash | undefined,
|
|
91
|
+
path: string | Hash,
|
|
92
|
+
fragment: string | Hash,
|
|
93
|
+
): Url;
|
|
77
94
|
}
|
|
78
95
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { type ApiModule, parseDefoldApiDoc } from "./api-doc";
|
|
2
|
+
import { isMarkedOptional } from "./emit-dts";
|
|
3
|
+
|
|
4
|
+
export interface ProvenanceSurface {
|
|
5
|
+
readonly target: string;
|
|
6
|
+
readonly namespace: string;
|
|
7
|
+
readonly doc: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CorrectionProvenance {
|
|
11
|
+
readonly key: string;
|
|
12
|
+
readonly sightedIn: readonly string[];
|
|
13
|
+
readonly neededBy: readonly string[];
|
|
14
|
+
readonly markedIn: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ManifestEntry {
|
|
18
|
+
readonly namespace: string;
|
|
19
|
+
readonly doc: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface VersionedManifestEntry extends ManifestEntry {
|
|
23
|
+
readonly versionId: string;
|
|
24
|
+
readonly editor?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 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.
|
|
30
|
+
export function retainedSurfaces(
|
|
31
|
+
defaultTarget: string,
|
|
32
|
+
defaultManifest: readonly ManifestEntry[],
|
|
33
|
+
versionedManifest: readonly VersionedManifestEntry[],
|
|
34
|
+
): ProvenanceSurface[] {
|
|
35
|
+
return [
|
|
36
|
+
...defaultManifest.map((entry) => ({
|
|
37
|
+
target: defaultTarget,
|
|
38
|
+
namespace: entry.namespace,
|
|
39
|
+
doc: entry.doc,
|
|
40
|
+
})),
|
|
41
|
+
...versionedManifest
|
|
42
|
+
.filter((entry) => entry.editor !== true)
|
|
43
|
+
.map((entry) => ({ target: entry.versionId, namespace: entry.namespace, doc: entry.doc })),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const PARAM_SEPARATOR = ":param:";
|
|
48
|
+
|
|
49
|
+
// A target needs a correction while any sighting of the slot there is unmarked,
|
|
50
|
+
// so an overload that marks it in one declaration and not another still needs
|
|
51
|
+
// it. The correction can go only once no target needs it.
|
|
52
|
+
export function correctionProvenance(
|
|
53
|
+
keys: readonly string[],
|
|
54
|
+
surfaces: readonly ProvenanceSurface[],
|
|
55
|
+
): CorrectionProvenance[] {
|
|
56
|
+
const parsed = new Map<ProvenanceSurface, ApiModule>();
|
|
57
|
+
const parse = (surface: ProvenanceSurface): ApiModule => {
|
|
58
|
+
let module = parsed.get(surface);
|
|
59
|
+
if (!module) {
|
|
60
|
+
module = parseDefoldApiDoc(surface.doc);
|
|
61
|
+
parsed.set(surface, module);
|
|
62
|
+
}
|
|
63
|
+
return module;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return keys.map((key) => {
|
|
67
|
+
const separator = key.lastIndexOf(PARAM_SEPARATOR);
|
|
68
|
+
if (separator < 0) throw new Error(`correctionProvenance: malformed key ${key}`);
|
|
69
|
+
const element = key.slice(0, separator);
|
|
70
|
+
const slot = key.slice(separator + PARAM_SEPARATOR.length);
|
|
71
|
+
const namespace = element.slice(0, element.lastIndexOf("."));
|
|
72
|
+
|
|
73
|
+
const markedByTarget = new Map<string, boolean>();
|
|
74
|
+
for (const surface of surfaces) {
|
|
75
|
+
if (surface.namespace !== namespace) continue;
|
|
76
|
+
for (const fn of parse(surface).functions) {
|
|
77
|
+
if (fn.name !== element) continue;
|
|
78
|
+
for (const parameter of fn.parameters) {
|
|
79
|
+
if (parameter.name !== slot) continue;
|
|
80
|
+
const marked = markedByTarget.get(surface.target) ?? true;
|
|
81
|
+
markedByTarget.set(surface.target, marked && isMarkedOptional(parameter));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const sightedIn = [...markedByTarget.keys()].sort();
|
|
87
|
+
return {
|
|
88
|
+
key,
|
|
89
|
+
sightedIn,
|
|
90
|
+
neededBy: sightedIn.filter((target) => markedByTarget.get(target) === false),
|
|
91
|
+
markedIn: sightedIn.filter((target) => markedByTarget.get(target) === true),
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Reads the raw doc rather than `parseDefoldApiDoc`, which folds an absent
|
|
97
|
+
// `is_optional` into `isOptional: false` and so erases the difference between a
|
|
98
|
+
// parameter upstream calls required and one it never classified at all.
|
|
99
|
+
export function whollyUnmarkedNamespaces(
|
|
100
|
+
surfaces: readonly ProvenanceSurface[],
|
|
101
|
+
): Map<string, string[]> {
|
|
102
|
+
const out = new Map<string, string[]>();
|
|
103
|
+
for (const surface of surfaces) {
|
|
104
|
+
const namespaces = out.get(surface.target) ?? [];
|
|
105
|
+
out.set(surface.target, namespaces);
|
|
106
|
+
const parameters = rawFunctionParameters(surface.doc);
|
|
107
|
+
if (parameters.length > 0 && parameters.every((parameter) => !("is_optional" in parameter))) {
|
|
108
|
+
namespaces.push(surface.namespace);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (const namespaces of out.values()) namespaces.sort();
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function rawFunctionParameters(doc: unknown): Record<string, unknown>[] {
|
|
116
|
+
if (!isRecord(doc) || !Array.isArray(doc.elements)) return [];
|
|
117
|
+
return doc.elements.flatMap((element) =>
|
|
118
|
+
isRecord(element) && element.type === "FUNCTION" && Array.isArray(element.parameters)
|
|
119
|
+
? element.parameters.filter(isRecord)
|
|
120
|
+
: [],
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
125
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
126
|
+
}
|