@defold-typescript/types 0.40.0 → 0.42.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 (224) hide show
  1. package/api-availability.json +3963 -772
  2. package/api-migrations.json +27 -0
  3. package/api-signatures.json +18069 -10240
  4. package/api-targets.json +448 -11
  5. package/examples/translations.json +560 -89
  6. package/fixtures/messages_doc.json +96 -0
  7. package/generated/b2d.d.ts +680 -4
  8. package/generated/b2d_body.d.ts +29 -103
  9. package/generated/b2d_chain.d.ts +5 -6
  10. package/generated/b2d_fixture.d.ts +34 -38
  11. package/generated/b2d_joint.d.ts +503 -25
  12. package/generated/b2d_shape.d.ts +183 -20
  13. package/generated/b2d_world.d.ts +57 -63
  14. package/generated/buffer.d.ts +16 -15
  15. package/generated/builtin-messages.d.ts +7 -1
  16. package/generated/bullet3d.d.ts +91 -0
  17. package/generated/bullet3d_collision_object.d.ts +433 -0
  18. package/generated/bullet3d_constraint.d.ts +818 -0
  19. package/generated/bullet3d_rigid_body.d.ts +429 -0
  20. package/generated/bullet3d_shape.d.ts +123 -0
  21. package/generated/bullet3d_world.d.ts +492 -0
  22. package/generated/camera.d.ts +29 -32
  23. package/generated/collectionfactory.d.ts +15 -18
  24. package/generated/collectionproxy.d.ts +22 -6
  25. package/generated/compute.d.ts +12 -238
  26. package/generated/crash.d.ts +24 -10
  27. package/generated/editor-vm/http.d.ts +90 -7
  28. package/generated/editor-vm/image.d.ts +7 -7
  29. package/generated/editor-vm/json.d.ts +11 -0
  30. package/generated/editor-vm/localization.d.ts +7 -7
  31. package/generated/editor-vm/tilemap_tiles.d.ts +56 -10
  32. package/generated/editor-vm/zip.d.ts +28 -0
  33. package/generated/editor.d.ts +1496 -430
  34. package/generated/factory.d.ts +21 -25
  35. package/generated/font.d.ts +64 -22
  36. package/generated/go.d.ts +235 -182
  37. package/generated/graphics.d.ts +335 -97
  38. package/generated/gui.d.ts +240 -471
  39. package/generated/html5.d.ts +2 -2
  40. package/generated/http.d.ts +28 -35
  41. package/generated/image.d.ts +105 -72
  42. package/generated/json.d.ts +34 -16
  43. package/generated/kinds/gui-script.d.ts +7 -0
  44. package/generated/kinds/render-script.d.ts +7 -0
  45. package/generated/kinds/script.d.ts +7 -0
  46. package/generated/label.d.ts +77 -7
  47. package/generated/liveupdate.d.ts +13 -10
  48. package/generated/material.d.ts +312 -360
  49. package/generated/model.d.ts +40 -39
  50. package/generated/particlefx.d.ts +19 -28
  51. package/generated/physics.d.ts +365 -133
  52. package/generated/profiler.d.ts +13 -23
  53. package/generated/render.d.ts +237 -297
  54. package/generated/resource.d.ts +664 -583
  55. package/generated/socket.d.ts +120 -133
  56. package/generated/sound.d.ts +62 -31
  57. package/generated/sprite.d.ts +31 -17
  58. package/generated/sys.d.ts +234 -190
  59. package/generated/tilemap.d.ts +21 -16
  60. package/generated/timer.d.ts +34 -21
  61. package/generated/versions/defold-1.12.4/b2d_body.d.ts +0 -7
  62. package/generated/versions/defold-1.12.4/buffer.d.ts +1 -3
  63. package/generated/versions/defold-1.12.4/camera.d.ts +21 -21
  64. package/generated/versions/defold-1.12.4/collectionfactory.d.ts +9 -9
  65. package/generated/versions/defold-1.12.4/collectionproxy.d.ts +2 -2
  66. package/generated/versions/defold-1.12.4/crash.d.ts +2 -2
  67. package/generated/versions/defold-1.12.4/factory.d.ts +15 -16
  68. package/generated/versions/defold-1.12.4/go.d.ts +6 -12
  69. package/generated/versions/defold-1.12.4/gui.d.ts +77 -175
  70. package/generated/versions/defold-1.12.4/html5.d.ts +1 -1
  71. package/generated/versions/defold-1.12.4/http.d.ts +0 -13
  72. package/generated/versions/defold-1.12.4/image.d.ts +16 -33
  73. package/generated/versions/defold-1.12.4/index.d.ts +1 -0
  74. package/generated/versions/defold-1.12.4/json.d.ts +10 -6
  75. package/generated/versions/defold-1.12.4/label.d.ts +2 -3
  76. package/generated/versions/defold-1.12.4/liveupdate.d.ts +5 -3
  77. package/generated/versions/defold-1.12.4/model.d.ts +2 -10
  78. package/generated/versions/defold-1.12.4/particlefx.d.ts +2 -5
  79. package/generated/versions/defold-1.12.4/physics.d.ts +69 -67
  80. package/generated/versions/defold-1.12.4/profiler.d.ts +2 -5
  81. package/generated/versions/defold-1.12.4/render.d.ts +99 -128
  82. package/generated/versions/defold-1.12.4/resource.d.ts +120 -148
  83. package/generated/versions/defold-1.12.4/socket.d.ts +34 -60
  84. package/generated/versions/defold-1.12.4/sound.d.ts +8 -8
  85. package/generated/versions/defold-1.12.4/sprite.d.ts +15 -5
  86. package/generated/versions/defold-1.12.4/sys.d.ts +37 -71
  87. package/generated/versions/defold-1.12.4/tilemap.d.ts +18 -11
  88. package/generated/versions/defold-1.12.4/timer.d.ts +5 -3
  89. package/generated/versions/defold-1.12.4/vmath.d.ts +0 -21
  90. package/generated/versions/defold-1.12.4/window.d.ts +1 -16
  91. package/generated/versions/defold-1.13.0/b2d_body.d.ts +10 -55
  92. package/generated/versions/defold-1.13.0/b2d_fixture.d.ts +25 -25
  93. package/generated/versions/defold-1.13.0/b2d_shape.d.ts +169 -8
  94. package/generated/versions/defold-1.13.0/b2d_world.d.ts +16 -10
  95. package/generated/versions/defold-1.13.0/buffer.d.ts +1 -3
  96. package/generated/versions/defold-1.13.0/camera.d.ts +25 -25
  97. package/generated/versions/defold-1.13.0/collectionfactory.d.ts +9 -9
  98. package/generated/versions/defold-1.13.0/collectionproxy.d.ts +2 -2
  99. package/generated/versions/defold-1.13.0/compute.d.ts +0 -69
  100. package/generated/versions/defold-1.13.0/crash.d.ts +2 -2
  101. package/generated/versions/defold-1.13.0/editor-vm/tilemap_tiles.d.ts +3 -3
  102. package/generated/versions/defold-1.13.0/editor.d.ts +34 -24
  103. package/generated/versions/defold-1.13.0/factory.d.ts +15 -16
  104. package/generated/versions/defold-1.13.0/go.d.ts +6 -12
  105. package/generated/versions/defold-1.13.0/graphics.d.ts +73 -37
  106. package/generated/versions/defold-1.13.0/gui.d.ts +77 -175
  107. package/generated/versions/defold-1.13.0/html5.d.ts +1 -1
  108. package/generated/versions/defold-1.13.0/http.d.ts +0 -13
  109. package/generated/versions/defold-1.13.0/image.d.ts +16 -33
  110. package/generated/versions/defold-1.13.0/index.d.ts +1 -0
  111. package/generated/versions/defold-1.13.0/json.d.ts +10 -6
  112. package/generated/versions/defold-1.13.0/label.d.ts +2 -3
  113. package/generated/versions/defold-1.13.0/liveupdate.d.ts +7 -5
  114. package/generated/versions/defold-1.13.0/material.d.ts +68 -106
  115. package/generated/versions/defold-1.13.0/model.d.ts +2 -10
  116. package/generated/versions/defold-1.13.0/particlefx.d.ts +2 -5
  117. package/generated/versions/defold-1.13.0/physics.d.ts +69 -67
  118. package/generated/versions/defold-1.13.0/profiler.d.ts +2 -5
  119. package/generated/versions/defold-1.13.0/render.d.ts +99 -128
  120. package/generated/versions/defold-1.13.0/resource.d.ts +126 -150
  121. package/generated/versions/defold-1.13.0/socket.d.ts +34 -60
  122. package/generated/versions/defold-1.13.0/sound.d.ts +8 -8
  123. package/generated/versions/defold-1.13.0/sprite.d.ts +15 -5
  124. package/generated/versions/defold-1.13.0/sys.d.ts +37 -71
  125. package/generated/versions/defold-1.13.0/tilemap.d.ts +18 -11
  126. package/generated/versions/defold-1.13.0/timer.d.ts +5 -3
  127. package/generated/versions/defold-1.13.0/vmath.d.ts +0 -21
  128. package/generated/versions/defold-1.13.0/window.d.ts +1 -16
  129. package/generated/versions/defold-1.13.1/b2d.d.ts +33 -0
  130. package/generated/versions/defold-1.13.1/b2d_body.d.ts +636 -0
  131. package/generated/versions/defold-1.13.1/b2d_chain.d.ts +106 -0
  132. package/generated/versions/defold-1.13.1/b2d_fixture.d.ts +157 -0
  133. package/generated/versions/defold-1.13.1/b2d_joint.d.ts +823 -0
  134. package/generated/versions/defold-1.13.1/b2d_shape.d.ts +400 -0
  135. package/generated/versions/defold-1.13.1/b2d_world.d.ts +320 -0
  136. package/generated/versions/defold-1.13.1/buffer.d.ts +189 -0
  137. package/generated/versions/defold-1.13.1/camera.d.ts +303 -0
  138. package/generated/versions/defold-1.13.1/collectionfactory.d.ts +151 -0
  139. package/generated/versions/defold-1.13.1/collectionproxy.d.ts +93 -0
  140. package/generated/versions/defold-1.13.1/compute.d.ts +237 -0
  141. package/generated/versions/defold-1.13.1/crash.d.ts +143 -0
  142. package/generated/versions/defold-1.13.1/editor-vm/http.d.ts +56 -0
  143. package/generated/versions/defold-1.13.1/editor-vm/image.d.ts +48 -0
  144. package/generated/versions/defold-1.13.1/editor-vm/json.d.ts +10 -0
  145. package/generated/versions/defold-1.13.1/editor-vm/localization.d.ts +42 -0
  146. package/generated/versions/defold-1.13.1/editor-vm/tilemap_tiles.d.ts +80 -0
  147. package/generated/versions/defold-1.13.1/editor-vm/zip.d.ts +10 -0
  148. package/generated/versions/defold-1.13.1/editor-vm/zlib.d.ts +24 -0
  149. package/generated/versions/defold-1.13.1/editor.d.ts +1121 -0
  150. package/generated/versions/defold-1.13.1/factory.d.ts +124 -0
  151. package/generated/versions/defold-1.13.1/font.d.ts +81 -0
  152. package/generated/versions/defold-1.13.1/go.d.ts +1107 -0
  153. package/generated/versions/defold-1.13.1/graphics.d.ts +411 -0
  154. package/generated/versions/defold-1.13.1/gui.d.ts +2353 -0
  155. package/generated/versions/defold-1.13.1/html5.d.ts +55 -0
  156. package/generated/versions/defold-1.13.1/http.d.ts +77 -0
  157. package/generated/versions/defold-1.13.1/image.d.ts +131 -0
  158. package/generated/versions/defold-1.13.1/index.d.ts +57 -0
  159. package/generated/versions/defold-1.13.1/json.d.ts +80 -0
  160. package/generated/versions/defold-1.13.1/kinds/editor-script.d.ts +14 -0
  161. package/generated/versions/defold-1.13.1/label.d.ts +93 -0
  162. package/generated/versions/defold-1.13.1/liveupdate.d.ts +142 -0
  163. package/generated/versions/defold-1.13.1/material.d.ts +408 -0
  164. package/generated/versions/defold-1.13.1/model.d.ts +272 -0
  165. package/generated/versions/defold-1.13.1/msg.d.ts +10 -0
  166. package/generated/versions/defold-1.13.1/particlefx.d.ts +163 -0
  167. package/generated/versions/defold-1.13.1/physics.d.ts +559 -0
  168. package/generated/versions/defold-1.13.1/profiler.d.ts +235 -0
  169. package/generated/versions/defold-1.13.1/render.d.ts +1190 -0
  170. package/generated/versions/defold-1.13.1/resource.d.ts +1523 -0
  171. package/generated/versions/defold-1.13.1/socket.d.ts +784 -0
  172. package/generated/versions/defold-1.13.1/sound.d.ts +292 -0
  173. package/generated/versions/defold-1.13.1/sprite.d.ts +175 -0
  174. package/generated/versions/defold-1.13.1/sys.d.ts +678 -0
  175. package/generated/versions/defold-1.13.1/tilemap.d.ts +208 -0
  176. package/generated/versions/defold-1.13.1/timer.d.ts +125 -0
  177. package/generated/versions/defold-1.13.1/types.d.ts +61 -0
  178. package/generated/versions/defold-1.13.1/vmath.d.ts +774 -0
  179. package/generated/versions/defold-1.13.1/window.d.ts +183 -0
  180. package/generated/versions/defold-1.13.1/zlib.d.ts +41 -0
  181. package/generated/vmath.d.ts +1 -21
  182. package/generated/window.d.ts +71 -71
  183. package/identity-vocabulary.json +561 -0
  184. package/index.d.ts +7 -0
  185. package/package.json +18 -2
  186. package/scripts/example-store-io.ts +12 -1
  187. package/scripts/extension-goldens.ts +1 -0
  188. package/scripts/fidelity-baseline.json +146 -46
  189. package/scripts/generate-api-availability.ts +90 -12
  190. package/scripts/generate-api-signatures.ts +12 -2
  191. package/scripts/import-defold-release.ts +106 -26
  192. package/scripts/prose-constants.ts +77 -16
  193. package/scripts/regen.ts +391 -77
  194. package/scripts/shared-handle-methods.ts +53 -0
  195. package/scripts/signature-store-fs.ts +1 -0
  196. package/scripts/sync-api-docs.ts +48 -10
  197. package/src/api-availability.ts +0 -0
  198. package/src/api-doc.ts +201 -3
  199. package/src/api-identity.ts +252 -0
  200. package/src/core-types.ts +26 -3
  201. package/src/correction-pins.ts +49 -0
  202. package/src/doc-comment.ts +104 -11
  203. package/src/doc-corrections.ts +108 -0
  204. package/src/editor-vm-globals.d.ts +5 -2
  205. package/src/emit-dts.ts +2064 -281
  206. package/src/emit-messages.ts +52 -0
  207. package/src/example-store.ts +125 -14
  208. package/src/field-correction-coverage.ts +204 -0
  209. package/src/fnv1a.ts +16 -0
  210. package/src/go-overloads.d.ts +25 -6
  211. package/src/index-base-table.ts +145 -0
  212. package/src/index-slot-classifications.ts +588 -0
  213. package/src/index.ts +38 -5
  214. package/src/library-index-slot-classifications.ts +332 -0
  215. package/src/lifecycle.ts +61 -199
  216. package/src/luals-type-expr.ts +354 -0
  217. package/src/msg-overloads.d.ts +0 -1
  218. package/src/param-correction-provenance.ts +8 -11
  219. package/src/render-overloads.d.ts +0 -15
  220. package/src/return-correction-provenance.ts +58 -16
  221. package/src/sys-overloads.d.ts +48 -0
  222. package/src/vmath-overloads.d.ts +27 -1
  223. package/src/window-event-guard.d.ts +6 -9
  224. package/url-parameters.json +1 -2
@@ -85,6 +85,58 @@ export function applyMessageDeprecations(
85
85
  return { entries };
86
86
  }
87
87
 
88
+ // A payload field whose documented type the engine refuses, with the type it
89
+ // takes instead. `upstream` is the documented type the correction replaces:
90
+ // once the catalog stops declaring it the entry throws and is deleted.
91
+ export interface MessageFieldCorrection {
92
+ readonly origin: string;
93
+ readonly name: string;
94
+ readonly field: string;
95
+ readonly types: readonly string[];
96
+ readonly upstream: readonly string[];
97
+ readonly reason: string;
98
+ }
99
+
100
+ // Demonstrated by the engine probe only: each entry is a payload the running
101
+ // engine rejected when it held the documented type.
102
+ export const MESSAGE_FIELD_CORRECTIONS: readonly MessageFieldCorrection[] = [
103
+ {
104
+ origin: "camera",
105
+ name: "set_camera",
106
+ field: "orthographic_projection",
107
+ types: ["number"],
108
+ upstream: ["boolean"],
109
+ reason:
110
+ "the engine decodes the payload into a number field and raises `number expected, got boolean` for the documented boolean",
111
+ },
112
+ ];
113
+
114
+ export function applyMessageFieldCorrections(
115
+ catalog: MessageCatalog,
116
+ overlay: readonly MessageFieldCorrection[] = MESSAGE_FIELD_CORRECTIONS,
117
+ ): MessageCatalog {
118
+ const entries = catalog.entries.map((entry) => ({
119
+ ...entry,
120
+ payload: entry.payload.map((field) => ({ ...field })),
121
+ }));
122
+ for (const correction of overlay) {
123
+ const field = entries
124
+ .find((entry) => entry.origin === correction.origin && entry.name === correction.name)
125
+ ?.payload.find((candidate) => candidate.name === correction.field);
126
+ const identity = `(origin=${correction.origin}, name=${correction.name}, field=${correction.field})`;
127
+ if (field === undefined) {
128
+ throw new Error(`applyMessageFieldCorrections: no catalog field matches ${identity}`);
129
+ }
130
+ if (field.types.join("|") !== correction.upstream.join("|")) {
131
+ throw new Error(
132
+ `applyMessageFieldCorrections: upstream now declares ${field.types.join("|")} for ${identity}; delete the correction`,
133
+ );
134
+ }
135
+ field.types = [...correction.types];
136
+ }
137
+ return { entries };
138
+ }
139
+
88
140
  const TS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
89
141
  const ENGINE_TYPES = [
90
142
  "Hash",
@@ -1,3 +1,7 @@
1
+ import type { ApiModule } from "./api-doc";
2
+ import { htmlToDocText, summaryFor } from "./doc-comment";
3
+ import { fnv1a64 } from "./fnv1a";
4
+
1
5
  // A hand-authored TypeScript translation of one element's ref-doc `@example`,
2
6
  // pinned by a hash of the exact source Lua it replaces. A ref-doc re-pin that
3
7
  // changes the source Lua flips the hash, so a stale translation stops matching
@@ -5,6 +9,10 @@
5
9
  export interface Translation {
6
10
  sourceHash: string;
7
11
  ts: string;
12
+ // The authored replacement for the upstream sentence that introduces this
13
+ // segment. Pinned by the same `sourceHash`, so an upstream rewrite of the
14
+ // example body drops the caption together with the body.
15
+ prose?: string;
8
16
  }
9
17
 
10
18
  // An FQN maps to one translation per distinct example body it carries: an
@@ -17,25 +25,20 @@ export interface Translation {
17
25
  // replacing the entry silently regresses every demoted surface to raw Lua.
18
26
  export type TranslationStore = Record<string, Translation[]>;
19
27
 
20
- const FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
21
- const FNV_PRIME = 0x100000001b3n;
22
- const U64_MASK = 0xffffffffffffffffn;
28
+ // A prose Lua fence deliberately left as Lua, pinned like a translation by the
29
+ // hash of the fence body, with the reason no TypeScript form fits. Keyed the
30
+ // way `proseLuaFences` keys the fence.
31
+ export type ProseLuaKeepStore = Record<string, { sourceHash: string; reason: string }[]>;
23
32
 
24
- // A pure, dependency-free FNV-1a 64-bit hash over the source's UTF-16 code
25
- // units, returned as zero-padded hex. Deliberately node- and Bun-free: this
26
- // module is reachable from `index.ts` (via `emit-dts`), so a `node:crypto` or
27
- // ambient-`Bun` reference here would fail type-checking in every downstream
28
- // consumer that compiles the shipped `src/` graph.
33
+ // The `fnv1a64` hash of an example's source, which must stay node- and Bun-free
34
+ // for the reason given there: this module is reachable from `index.ts` (via
35
+ // `emit-dts`).
29
36
  //
30
37
  // The input is the already-normalized post-`htmlToCodeText` string (per-line
31
38
  // trailing whitespace and surrounding blank lines stripped), so the hash is
32
39
  // independent of trailing whitespace in the original ref-doc HTML.
33
40
  export function hashExampleSource(source: string): string {
34
- let hash = FNV_OFFSET_BASIS;
35
- for (let i = 0; i < source.length; i++) {
36
- hash = ((hash ^ BigInt(source.charCodeAt(i))) * FNV_PRIME) & U64_MASK;
37
- }
38
- return hash.toString(16).padStart(16, "0");
41
+ return fnv1a64(source);
39
42
  }
40
43
 
41
44
  // Return the stored TypeScript body only when the FQN exists and one of its
@@ -60,7 +63,7 @@ export function lookupTranslation(
60
63
  * emitted as several `@example` blocks only when every one of them has an
61
64
  * authored body. A partial resolve would document some of the element's
62
65
  * examples and silently drop the rest, so the caller falls back to the
63
- * whole-blob body instead. An empty hash list is a miss for the same reason —
66
+ * upstream Lua instead. An empty hash list is a miss for the same reason —
64
67
  * it would otherwise report success while documenting nothing.
65
68
  */
66
69
  export function lookupExampleTranslations(
@@ -77,3 +80,111 @@ export function lookupExampleTranslations(
77
80
  }
78
81
  return bodies;
79
82
  }
83
+
84
+ /**
85
+ * The stored body and caption for each of a blob's segments, in the order
86
+ * given, or `null` the moment one of them does not resolve.
87
+ *
88
+ * Same all-or-nothing and empty-list rules as `lookupExampleTranslations`. Each
89
+ * caption is the entry's authored `prose` when it carries one, else the
90
+ * segment's upstream prose.
91
+ */
92
+ export function lookupExampleSegments(
93
+ store: TranslationStore,
94
+ fqn: string,
95
+ segments: readonly { code: string; prose: string }[],
96
+ ): { ts: string; prose: string }[] | null {
97
+ const entries = store[fqn];
98
+ if (!entries || segments.length === 0) return null;
99
+ const resolved: { ts: string; prose: string }[] = [];
100
+ for (const segment of segments) {
101
+ const sourceHash = hashExampleSource(segment.code);
102
+ const entry = entries.find((candidate) => candidate.sourceHash === sourceHash);
103
+ if (!entry) return null;
104
+ resolved.push({ ts: entry.ts, prose: entry.prose ?? segment.prose });
105
+ }
106
+ return resolved;
107
+ }
108
+
109
+ // A fence `htmlToDocText` opened on a line of its own, located by line index:
110
+ // `open` is the opener, `close` the closing ``` line.
111
+ interface DocFence {
112
+ open: number;
113
+ close: number;
114
+ }
115
+
116
+ // The one reader of prose fences, shared by the enumeration and the swap so the
117
+ // body one hashes is the body the other replaces. Every fence is walked, so a
118
+ // closing ``` of a `text` fence is never read as the opener of the next one.
119
+ function luaDocFences(lines: readonly string[]): DocFence[] {
120
+ const fences: DocFence[] = [];
121
+ for (let open = 0; open < lines.length; open += 1) {
122
+ if (!lines[open]?.startsWith("```")) continue;
123
+ let close = open + 1;
124
+ while (close < lines.length && !lines[close]?.startsWith("```")) close += 1;
125
+ if (close >= lines.length) break;
126
+ if (lines[open] === "```lua") fences.push({ open, close });
127
+ open = close;
128
+ }
129
+ return fences;
130
+ }
131
+
132
+ function fenceBody(lines: readonly string[], fence: DocFence): string {
133
+ return lines.slice(fence.open + 1, fence.close).join("\n");
134
+ }
135
+
136
+ /**
137
+ * Swap each ```lua fence in `htmlToDocText` output for the TypeScript pinned
138
+ * under `key` to its body's hash, leaving every other byte alone. A fence with
139
+ * no matching entry stays Lua, the same fallback the `@example` ladder keeps.
140
+ * With `keepSource`, each swapped fence is followed by a blank line and its
141
+ * untouched body as a ```lua original fence, the pairing the API reference
142
+ * renders as TypeScript and Lua tabs.
143
+ */
144
+ export function translateProseFences(
145
+ markdown: string,
146
+ key: string,
147
+ store: TranslationStore,
148
+ options: { keepSource?: boolean } = {},
149
+ ): string {
150
+ if (!store[key]) return markdown;
151
+ const lines = markdown.split("\n");
152
+ const fences = luaDocFences(lines);
153
+ for (const fence of fences.reverse()) {
154
+ const lua = fenceBody(lines, fence);
155
+ const ts = lookupTranslation(store, key, hashExampleSource(lua));
156
+ if (ts === null) continue;
157
+ const source = options.keepSource ? ["", "```lua original", ...lua.split("\n"), "```"] : [];
158
+ lines.splice(
159
+ fence.open,
160
+ fence.close - fence.open + 1,
161
+ "```ts",
162
+ ...ts.split("\n"),
163
+ "```",
164
+ ...source,
165
+ );
166
+ }
167
+ return lines.join("\n");
168
+ }
169
+
170
+ /**
171
+ * Every ```lua fence in the prose a module documents, keyed the way the emit
172
+ * and the API reference look its translation up: the namespace description
173
+ * under the namespace, and a function's summary, parameter and return docs
174
+ * under the function's name.
175
+ */
176
+ export function proseLuaFences(module: ApiModule): { key: string; lua: string }[] {
177
+ const slots: [string, string][] = [
178
+ [module.namespace, summaryFor(module.brief, module.description)],
179
+ ];
180
+ for (const fn of module.functions) {
181
+ slots.push([fn.name, summaryFor(fn.brief, fn.description)]);
182
+ for (const slot of [...fn.parameters, ...fn.returnValues]) slots.push([fn.name, slot.doc]);
183
+ }
184
+ const out: { key: string; lua: string }[] = [];
185
+ for (const [key, html] of slots) {
186
+ const lines = htmlToDocText(html).split("\n");
187
+ for (const fence of luaDocFences(lines)) out.push({ key, lua: fenceBody(lines, fence) });
188
+ }
189
+ return out;
190
+ }
@@ -0,0 +1,204 @@
1
+ import type { ApiFunction, ApiModule, ApiParameter, ApiStruct } from "./api-doc";
2
+ import {
3
+ LIST_FIELD_CORRECTIONS,
4
+ memberCorrectionKey,
5
+ REQUIRED_FIELD_CORRECTIONS,
6
+ RETURN_FIELD_OPTIONALITY_CORRECTIONS,
7
+ } from "./emit-dts";
8
+ import { matchBracket, splitTopLevel } from "./luals-type-expr";
9
+
10
+ // The slot-keyed field corrections are applied to fields recovered from a prose
11
+ // `table` slot. A release that types the slot as a `STRUCT` or an inline record
12
+ // recovers no fields, so each such entry must be stated upstream already or be
13
+ // carried by a member-keyed entry on the struct; anything else would go inert.
14
+ export interface FieldCorrectionTables {
15
+ readonly required: ReadonlyMap<string, unknown>;
16
+ readonly list: ReadonlyMap<string, unknown>;
17
+ readonly optionality: ReadonlyMap<string, unknown>;
18
+ }
19
+
20
+ export const FIELD_CORRECTION_TABLES: FieldCorrectionTables = {
21
+ required: REQUIRED_FIELD_CORRECTIONS,
22
+ list: LIST_FIELD_CORRECTIONS,
23
+ optionality: RETURN_FIELD_OPTIONALITY_CORRECTIONS,
24
+ };
25
+
26
+ export interface FieldCorrectionGap {
27
+ readonly key: string;
28
+ // The struct whose member would carry the correction, when the path reached one.
29
+ readonly struct?: string;
30
+ readonly reason: string;
31
+ }
32
+
33
+ type Kind = keyof FieldCorrectionTables;
34
+
35
+ interface Member {
36
+ readonly name: string;
37
+ readonly type: string;
38
+ readonly optional: boolean;
39
+ }
40
+
41
+ // A slot or member type resolved to the members it declares: a `STRUCT` by name,
42
+ // or an inline `{ name:type, ... }` record.
43
+ interface Shape {
44
+ readonly struct?: string;
45
+ readonly members: readonly Member[];
46
+ }
47
+
48
+ const PROSE_TABLE = "table";
49
+
50
+ function stripList(type: string): string {
51
+ let t = type.trim();
52
+ while (t.endsWith("[]")) t = t.slice(0, -2).trim();
53
+ while (t.startsWith("(") && matchBracket(t, 0) === t.length - 1) t = t.slice(1, -1).trim();
54
+ return t;
55
+ }
56
+
57
+ function recordMembers(record: string): Member[] {
58
+ return splitTopLevel(record.slice(1, -1), ",")
59
+ .map((part) => part.trim())
60
+ .filter((part) => part.length > 0)
61
+ .map((part) => {
62
+ const colon = part.indexOf(":");
63
+ const raw = colon === -1 ? part : part.slice(0, colon).trim();
64
+ const optional = raw.endsWith("?");
65
+ return {
66
+ name: optional ? raw.slice(0, -1) : raw,
67
+ type: colon === -1 ? "" : part.slice(colon + 1).trim(),
68
+ optional,
69
+ };
70
+ });
71
+ }
72
+
73
+ function shapeOf(type: string, structs: ReadonlyMap<string, ApiStruct>): Shape | null {
74
+ const t = stripList(type);
75
+ if (t.startsWith("{") && matchBracket(t, 0) === t.length - 1) {
76
+ return { members: recordMembers(t) };
77
+ }
78
+ const struct = structs.get(t);
79
+ if (struct === undefined) return null;
80
+ return {
81
+ struct: struct.name,
82
+ members: struct.members.map((member) => ({
83
+ name: member.name,
84
+ type: member.type,
85
+ optional: member.isOptional,
86
+ })),
87
+ };
88
+ }
89
+
90
+ function parseKey(key: string): {
91
+ element: string;
92
+ kind: "param" | "return";
93
+ slot: string;
94
+ segments: string[];
95
+ } | null {
96
+ const [element, kind, slot, path, ...rest] = key.split(":");
97
+ if (
98
+ element === undefined ||
99
+ slot === undefined ||
100
+ path === undefined ||
101
+ rest.length > 0 ||
102
+ (kind !== "param" && kind !== "return")
103
+ ) {
104
+ return null;
105
+ }
106
+ return { element, kind, slot, segments: path.split(".").map((s) => s.replace(/\[\]$/, "")) };
107
+ }
108
+
109
+ function slotsNamed(
110
+ overloads: readonly ApiFunction[],
111
+ kind: "param" | "return",
112
+ slot: string,
113
+ ): ApiParameter[] {
114
+ return overloads.flatMap((fn) =>
115
+ (kind === "param" ? fn.parameters : fn.returnValues).filter((p) => p.name === slot),
116
+ );
117
+ }
118
+
119
+ function slotGap(
120
+ key: string,
121
+ table: Kind,
122
+ slotTypes: readonly string[],
123
+ segments: readonly string[],
124
+ structs: ReadonlyMap<string, ApiStruct>,
125
+ tables: FieldCorrectionTables,
126
+ ): FieldCorrectionGap | null {
127
+ const shapes = slotTypes.map((type) => shapeOf(type, structs)).filter((s) => s !== null);
128
+ if (shapes.length === 0) {
129
+ return { key, reason: `slot is typed ${slotTypes.join("|")}, which declares no members` };
130
+ }
131
+ for (const initial of shapes) {
132
+ let shape: Shape = initial;
133
+ for (const [index, name] of segments.entries()) {
134
+ const member = shape.members.find((candidate) => candidate.name === name);
135
+ const struct = shape.struct === undefined ? {} : { struct: shape.struct };
136
+ if (member === undefined) return { key, ...struct, reason: `no member ${name}` };
137
+ if (index < segments.length - 1) {
138
+ const next = shapeOf(member.type, structs);
139
+ if (next === null) {
140
+ return { key, ...struct, reason: `member ${name} is typed ${member.type}` };
141
+ }
142
+ shape = next;
143
+ continue;
144
+ }
145
+ const carried =
146
+ shape.struct !== undefined && tables[table].has(memberCorrectionKey(shape.struct, name));
147
+ const upstream =
148
+ table === "required" ? !member.optional : table === "optionality" ? member.optional : false;
149
+ if (!carried && !upstream) {
150
+ return {
151
+ key,
152
+ ...struct,
153
+ reason: `neither upstream nor a member-keyed ${table} correction states it`,
154
+ };
155
+ }
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+
161
+ /**
162
+ * Every slot-keyed field correction that goes inert on these modules — one
163
+ * target's, after its skip rules, since a slot in `b2d.fixture` can name a struct
164
+ * `b2d` declares. An element the target does not declare, and a slot still typed
165
+ * as a prose `table`, are out of scope and report nothing.
166
+ */
167
+ export function fieldCorrectionGaps(
168
+ modules: readonly ApiModule[],
169
+ tables: FieldCorrectionTables = FIELD_CORRECTION_TABLES,
170
+ ): FieldCorrectionGap[] {
171
+ const structs = new Map(
172
+ modules.flatMap((module) => module.structs ?? []).map((struct) => [struct.name, struct]),
173
+ );
174
+ const functions = modules.flatMap((module) => module.functions);
175
+ const gaps: FieldCorrectionGap[] = [];
176
+ for (const table of ["required", "list", "optionality"] as const) {
177
+ for (const key of tables[table].keys()) {
178
+ const parsed = parseKey(key);
179
+ if (parsed === null) continue;
180
+ const overloads = functions.filter((fn) => fn.name === parsed.element);
181
+ if (overloads.length === 0) continue;
182
+ const slots = slotsNamed(overloads, parsed.kind, parsed.slot);
183
+ if (slots.length === 0) {
184
+ gaps.push({ key, reason: `${parsed.element} declares no ${parsed.kind} ${parsed.slot}` });
185
+ continue;
186
+ }
187
+ for (const slot of slots) {
188
+ if (slot.types.includes(PROSE_TABLE)) continue;
189
+ const gap = slotGap(key, table, slot.types, parsed.segments, structs, tables);
190
+ if (gap !== null) {
191
+ gaps.push(gap);
192
+ break;
193
+ }
194
+ }
195
+ }
196
+ }
197
+ return gaps;
198
+ }
199
+
200
+ export function formatFieldCorrectionGaps(gaps: readonly FieldCorrectionGap[]): string {
201
+ return gaps
202
+ .map((gap) => `${gap.key}${gap.struct === undefined ? "" : ` (${gap.struct})`}: ${gap.reason}`)
203
+ .join("\n");
204
+ }
package/src/fnv1a.ts ADDED
@@ -0,0 +1,16 @@
1
+ const FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
2
+ const FNV_PRIME = 0x100000001b3n;
3
+ const U64_MASK = 0xffffffffffffffffn;
4
+
5
+ // A pure, dependency-free FNV-1a 64-bit hash over the text's UTF-16 code units,
6
+ // returned as zero-padded hex. Deliberately node- and Bun-free: this module is
7
+ // reachable from `index.ts`, so a `node:crypto` or ambient-`Bun` reference here
8
+ // would fail type-checking in every downstream consumer that compiles the
9
+ // shipped `src/` graph.
10
+ export function fnv1a64(text: string): string {
11
+ let hash = FNV_OFFSET_BASIS;
12
+ for (let i = 0; i < text.length; i++) {
13
+ hash = ((hash ^ BigInt(text.charCodeAt(i))) * FNV_PRIME) & U64_MASK;
14
+ }
15
+ return hash.toString(16).padStart(16, "0");
16
+ }
@@ -6,10 +6,23 @@ interface ScriptProperty<TValue> {
6
6
  readonly __defoldScriptProperty: TValue;
7
7
  }
8
8
 
9
+ // True when `A` and `B` are identical, `readonly` modifiers included, which
10
+ // plain assignability ignores.
11
+ type Identical<A, B> =
12
+ (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
13
+
14
+ // The keys of `P` a write may name: every key but a `readonly` one, which the
15
+ // engine refuses to set.
16
+ type WritableKey<P> = {
17
+ [K in keyof P]-?: Identical<{ [Q in K]: P[K] }, { -readonly [Q in K]: P[K] }> extends true
18
+ ? K
19
+ : never;
20
+ }[keyof P];
21
+
9
22
  declare global {
10
23
  namespace go {
11
24
  interface GoPropertyOptions {
12
- /** Zero-based element index into an array property; the transpiler emits Defold's 1-based index. */
25
+ /** **⚠️ 1-based index into an array property; passed to Defold unchanged.** */
13
26
  index?: number;
14
27
  key?: Hash | string;
15
28
  keys?: (Hash | string)[];
@@ -21,7 +34,7 @@ declare global {
21
34
  * @param url - url of the game object or component having the property
22
35
  * @param property - id of the property to retrieve
23
36
  * @param options - optional options table
24
- * - index number zero-based index into array property; the transpiler emits Defold's 1-based index
37
+ * - index number **⚠️ 1-based index into an array property; passed to Defold unchanged.**
25
38
  * - key hash or string name of internal property
26
39
  * - keys array of hashes or strings identifying internal component resources (e.g. a particle fx emitter, see examples below)
27
40
  * @returns the value of the specified property
@@ -55,18 +68,19 @@ declare global {
55
68
  * @param property - id of the property to set
56
69
  * @param value - the value to set, or an array of vector4 to set a material property array
57
70
  * @param options - optional options table
58
- * - index integer zero-based index into array property; the transpiler emits Defold's 1-based index
71
+ * - index integer **⚠️ 1-based index into an array property; passed to Defold unchanged.**
59
72
  * - key hash or string name of internal property
60
73
  * - keys array of hashes or strings identifying internal component resources (e.g. a particle fx emitter, see examples below)
61
74
  * @example
62
75
  * ```ts
63
76
  * go.set("#sprite", "tint", vmath.vector4(1, 0, 0, 1));
64
77
  * // Name the target component to gate the value to its property type. The
65
- * // empty call applies the type argument, then the inner call infers the key:
78
+ * // empty call applies the type argument, then the inner call infers the key.
79
+ * // A read-only property such as "animation" is not a key it accepts:
66
80
  * go.set<sprite.properties>()("#sprite", "playback_rate", 2);
67
81
  * ```
68
82
  */
69
- function set<P>(): <K extends keyof P>(
83
+ function set<P>(): <K extends WritableKey<P>>(
70
84
  url: SceneAddress | Hash | Url,
71
85
  property: K,
72
86
  value: P[K],
@@ -85,7 +99,10 @@ declare global {
85
99
  options?: GoPropertyOptions,
86
100
  ): void;
87
101
  /**
88
- * Registers a Defold editor script property (deprecated escape hatch).
102
+ * Registers a script property, which the Defold editor exposes on every game
103
+ * object and collection that uses the script. Defold accepts the call only at
104
+ * the top level of the script, outside callbacks such as `init` and `update`.
105
+ * A `string` default needs Defold 1.13.2 or later.
89
106
  *
90
107
  * @deprecated Don't call `go.property` yourself. Declare the property in
91
108
  * `defineScript({ properties })` — that is the only form that types it onto
@@ -122,5 +139,7 @@ declare global {
122
139
  function property(name: string, value: Vector4): ScriptProperty<Vector4>;
123
140
  /** @deprecated Declare quaternions via the `defineScript({ properties })` field. */
124
141
  function property(name: string, value: Quaternion): ScriptProperty<Quaternion>;
142
+ /** @deprecated Declare text via the `defineScript({ properties })` field; needs Defold 1.13.2. */
143
+ function property(name: string, value: string): ScriptProperty<string>;
125
144
  }
126
145
  }
@@ -0,0 +1,145 @@
1
+ import {
2
+ INDEX_BASE_MARKERS,
3
+ INDEX_SLOT_CLASSIFICATIONS,
4
+ type IndexSlotClassification,
5
+ } from "./index-slot-classifications";
6
+
7
+ type NativeBase = "native-1" | "native-0";
8
+
9
+ export interface EngineIndexBaseRow {
10
+ readonly base: NativeBase;
11
+ readonly elements: readonly string[];
12
+ // The slot, or the field path inside it, in plain words.
13
+ readonly position: string;
14
+ // The classification keys the row covers.
15
+ readonly keys: readonly string[];
16
+ }
17
+
18
+ interface Slot {
19
+ readonly key: string;
20
+ readonly element: string;
21
+ readonly kind: "param" | "return";
22
+ readonly path: readonly string[];
23
+ readonly base: NativeBase;
24
+ readonly fromEnd: boolean;
25
+ }
26
+
27
+ const KEY = /^(.+?):(param|return):(.+)$/;
28
+ const BASE_LABELS: Readonly<Record<NativeBase, string>> = {
29
+ "native-1": "**1-based**",
30
+ "native-0": "**0-based**",
31
+ };
32
+
33
+ function nativeSlot(key: string, classification: IndexSlotClassification): Slot | undefined {
34
+ if (classification.class === "not-a-position") return undefined;
35
+ const match = KEY.exec(key);
36
+ if (match?.[1] === undefined || match[3] === undefined) return undefined;
37
+ return {
38
+ key,
39
+ element: match[1],
40
+ kind: match[2] === "return" ? "return" : "param",
41
+ path: match[3].split(":"),
42
+ base: classification.class,
43
+ fromEnd: classification.fromEnd === true,
44
+ };
45
+ }
46
+
47
+ // `b2d.fixture.get_density` -> `b2d.fixture`, `client:send` -> `client`.
48
+ function namespaceOf(element: string): string {
49
+ const end = Math.max(element.lastIndexOf("."), element.lastIndexOf(":"));
50
+ return end < 0 ? element : element.slice(0, end);
51
+ }
52
+
53
+ function groupBy<T>(items: readonly T[], keyOf: (item: T) => string): [T, ...T[]][] {
54
+ const groups = new Map<string, [T, ...T[]]>();
55
+ for (const item of items) {
56
+ const key = keyOf(item);
57
+ const group = groups.get(key);
58
+ if (group) group.push(item);
59
+ else groups.set(key, [item]);
60
+ }
61
+ return [...groups.values()];
62
+ }
63
+
64
+ function codeList(names: readonly string[]): string {
65
+ const code = names.map((name) => `\`${name}\``);
66
+ if (code.length < 2) return code.join("");
67
+ return `${code.slice(0, -1).join(", ")} and ${code[code.length - 1]}`;
68
+ }
69
+
70
+ function describePosition(
71
+ kind: "param" | "return",
72
+ parent: readonly string[],
73
+ leaves: readonly string[],
74
+ fromEnd: boolean,
75
+ ): string {
76
+ const names = codeList(leaves);
77
+ const holder = parent.length === 0 ? "" : `\`${parent.join(".")}\``;
78
+ let position: string;
79
+ if (parent.length === 0) position = kind === "return" ? `returned ${names}` : names;
80
+ else {
81
+ const fields = leaves.length === 1 ? "field" : "fields";
82
+ position = `${names} ${fields} of ${kind === "return" ? "the returned " : ""}${holder}`;
83
+ }
84
+ return fromEnd ? `${position}; counts from the end when negative` : position;
85
+ }
86
+
87
+ /**
88
+ * Every engine position the classification table records, one row per slot of
89
+ * one base in one namespace. Slots that differ only in their last name and are
90
+ * taken by the same functions share a row (`x` and `y`), so each row states
91
+ * one fact about one set of calls. Rows come 1-based first, then 0-based, each
92
+ * in table order.
93
+ */
94
+ export function engineIndexBaseRows(
95
+ classifications: ReadonlyMap<string, IndexSlotClassification> = INDEX_SLOT_CLASSIFICATIONS,
96
+ ): EngineIndexBaseRow[] {
97
+ const slots = [...classifications].flatMap(([key, classification]) => {
98
+ const slot = nativeSlot(key, classification);
99
+ return slot === undefined ? [] : [slot];
100
+ });
101
+ const bySlot = groupBy(slots, (slot) =>
102
+ [slot.base, namespaceOf(slot.element), slot.kind, slot.path.join(":")].join("|"),
103
+ );
104
+ const merged = groupBy(bySlot, (group) =>
105
+ [
106
+ group[0].base,
107
+ group[0].kind,
108
+ group[0].path.slice(0, -1).join(":"),
109
+ group[0].fromEnd,
110
+ group.map((slot) => slot.element).join(","),
111
+ ].join("|"),
112
+ );
113
+ const rows = merged.map((groups): EngineIndexBaseRow => {
114
+ const [first] = groups[0];
115
+ return {
116
+ base: first.base,
117
+ elements: groups[0].map((slot) => slot.element),
118
+ position: describePosition(
119
+ first.kind,
120
+ first.path.slice(0, -1),
121
+ groups.map(([slot]) => slot.path.at(-1) ?? ""),
122
+ first.fromEnd,
123
+ ),
124
+ keys: groups.flatMap((group) => group.map((slot) => slot.key)),
125
+ };
126
+ });
127
+ return [
128
+ ...rows.filter((row) => row.base === "native-1"),
129
+ ...rows.filter((row) => row.base === "native-0"),
130
+ ];
131
+ }
132
+
133
+ // The guide's table of engine positions by base, so the guide lists exactly
134
+ // the positions the hovers annotate.
135
+ export function engineIndexBaseTable(
136
+ classifications: ReadonlyMap<string, IndexSlotClassification> = INDEX_SLOT_CLASSIFICATIONS,
137
+ ): string {
138
+ const lines = ["| Base | API | Position |", "| --- | --- | --- |"];
139
+ for (const row of engineIndexBaseRows(classifications)) {
140
+ const base = `${INDEX_BASE_MARKERS[row.base]} ${BASE_LABELS[row.base]}`;
141
+ const api = row.elements.map((element) => `\`${element}\``).join(", ");
142
+ lines.push(`| ${base} | ${api} | ${row.position} |`);
143
+ }
144
+ return lines.join("\n");
145
+ }