@defold-typescript/types 0.48.0 → 0.50.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 (78) hide show
  1. package/api-availability.json +696 -624
  2. package/api-signatures.json +6799 -160
  3. package/api-targets.json +393 -2
  4. package/examples/translations.json +12 -0
  5. package/generated/b2d.d.ts +15 -0
  6. package/generated/bullet3d.d.ts +16 -0
  7. package/generated/font.d.ts +7 -3
  8. package/generated/graphics.d.ts +7 -0
  9. package/generated/gui.d.ts +5 -4
  10. package/generated/label.d.ts +3 -3
  11. package/generated/render.d.ts +16 -1
  12. package/generated/versions/defold-1.13.2/b2d.d.ts +709 -0
  13. package/generated/versions/defold-1.13.2/b2d_body.d.ts +593 -0
  14. package/generated/versions/defold-1.13.2/b2d_chain.d.ts +105 -0
  15. package/generated/versions/defold-1.13.2/b2d_fixture.d.ts +175 -0
  16. package/generated/versions/defold-1.13.2/b2d_joint.d.ts +1301 -0
  17. package/generated/versions/defold-1.13.2/b2d_shape.d.ts +423 -0
  18. package/generated/versions/defold-1.13.2/b2d_world.d.ts +308 -0
  19. package/generated/versions/defold-1.13.2/buffer.d.ts +192 -0
  20. package/generated/versions/defold-1.13.2/bullet3d.d.ts +91 -0
  21. package/generated/versions/defold-1.13.2/bullet3d_collision_object.d.ts +433 -0
  22. package/generated/versions/defold-1.13.2/bullet3d_constraint.d.ts +818 -0
  23. package/generated/versions/defold-1.13.2/bullet3d_rigid_body.d.ts +429 -0
  24. package/generated/versions/defold-1.13.2/bullet3d_shape.d.ts +123 -0
  25. package/generated/versions/defold-1.13.2/bullet3d_world.d.ts +492 -0
  26. package/generated/versions/defold-1.13.2/camera.d.ts +300 -0
  27. package/generated/versions/defold-1.13.2/collectionfactory.d.ts +148 -0
  28. package/generated/versions/defold-1.13.2/collectionproxy.d.ts +109 -0
  29. package/generated/versions/defold-1.13.2/compute.d.ts +107 -0
  30. package/generated/versions/defold-1.13.2/crash.d.ts +157 -0
  31. package/generated/versions/defold-1.13.2/editor-vm/http.d.ts +139 -0
  32. package/generated/versions/defold-1.13.2/editor-vm/image.d.ts +48 -0
  33. package/generated/versions/defold-1.13.2/editor-vm/json.d.ts +21 -0
  34. package/generated/versions/defold-1.13.2/editor-vm/localization.d.ts +42 -0
  35. package/generated/versions/defold-1.13.2/editor-vm/tilemap_tiles.d.ts +126 -0
  36. package/generated/versions/defold-1.13.2/editor-vm/zip.d.ts +38 -0
  37. package/generated/versions/defold-1.13.2/editor-vm/zlib.d.ts +24 -0
  38. package/generated/versions/defold-1.13.2/editor.d.ts +2177 -0
  39. package/generated/versions/defold-1.13.2/factory.d.ts +121 -0
  40. package/generated/versions/defold-1.13.2/font.d.ts +123 -0
  41. package/generated/versions/defold-1.13.2/go.d.ts +1166 -0
  42. package/generated/versions/defold-1.13.2/graphics.d.ts +613 -0
  43. package/generated/versions/defold-1.13.2/gui.d.ts +2212 -0
  44. package/generated/versions/defold-1.13.2/html5.d.ts +55 -0
  45. package/generated/versions/defold-1.13.2/http.d.ts +83 -0
  46. package/generated/versions/defold-1.13.2/image.d.ts +181 -0
  47. package/generated/versions/defold-1.13.2/index.d.ts +63 -0
  48. package/generated/versions/defold-1.13.2/json.d.ts +94 -0
  49. package/generated/versions/defold-1.13.2/kinds/editor-script.d.ts +14 -0
  50. package/generated/versions/defold-1.13.2/label.d.ts +164 -0
  51. package/generated/versions/defold-1.13.2/liveupdate.d.ts +143 -0
  52. package/generated/versions/defold-1.13.2/material.d.ts +490 -0
  53. package/generated/versions/defold-1.13.2/model.d.ts +287 -0
  54. package/generated/versions/defold-1.13.2/msg.d.ts +10 -0
  55. package/generated/versions/defold-1.13.2/particlefx.d.ts +155 -0
  56. package/generated/versions/defold-1.13.2/physics.d.ts +789 -0
  57. package/generated/versions/defold-1.13.2/profiler.d.ts +228 -0
  58. package/generated/versions/defold-1.13.2/render.d.ts +1153 -0
  59. package/generated/versions/defold-1.13.2/resource.d.ts +1628 -0
  60. package/generated/versions/defold-1.13.2/socket.d.ts +797 -0
  61. package/generated/versions/defold-1.13.2/sound.d.ts +323 -0
  62. package/generated/versions/defold-1.13.2/sprite.d.ts +179 -0
  63. package/generated/versions/defold-1.13.2/sys.d.ts +754 -0
  64. package/generated/versions/defold-1.13.2/tilemap.d.ts +206 -0
  65. package/generated/versions/defold-1.13.2/timer.d.ts +136 -0
  66. package/generated/versions/defold-1.13.2/types.d.ts +61 -0
  67. package/generated/versions/defold-1.13.2/vmath.d.ts +775 -0
  68. package/generated/versions/defold-1.13.2/window.d.ts +196 -0
  69. package/generated/versions/defold-1.13.2/zlib.d.ts +41 -0
  70. package/hsm/debug.d.ts +6 -3
  71. package/hsm/index.d.ts +309 -77
  72. package/identity-vocabulary.json +1 -0
  73. package/package.json +2 -2
  74. package/scripts/import-defold-release.ts +3 -31
  75. package/scripts/materialize-version.ts +13 -7
  76. package/scripts/regen.ts +63 -29
  77. package/scripts/sync-api-docs.ts +31 -2
  78. package/src/emit-dts.ts +25 -3
@@ -7,6 +7,7 @@ import {
7
7
  EDITOR_MANIFEST,
8
8
  type ExtractedFixture,
9
9
  extractAnnotationFixtures,
10
+ groupDocsByNamespace,
10
11
  IGNORED_UPSTREAM,
11
12
  mergeApiDocs,
12
13
  readZip,
@@ -15,7 +16,6 @@ import {
15
16
  } from "./sync-api-docs";
16
17
 
17
18
  const EXACT_VERSION = /^\d+\.\d+\.\d+$/;
18
- const LUA_NAMESPACE = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/;
19
19
  const DECLARED_NAME_KINDS = new Set(["CONSTANT", "ENUM", "STRUCT", "TYPEDEF", "CLASS"]);
20
20
  const PACKAGE_ROOT = resolve(import.meta.dir, "..");
21
21
 
@@ -75,11 +75,6 @@ interface RawElement extends Record<string, unknown> {
75
75
  name?: string;
76
76
  }
77
77
 
78
- interface RawApiDoc extends Record<string, unknown> {
79
- info?: { namespace?: string };
80
- elements?: RawElement[];
81
- }
82
-
83
78
  export interface ReleaseBaselineModule {
84
79
  readonly namespace: string;
85
80
  readonly fixture: string;
@@ -177,20 +172,6 @@ function isRecord(value: unknown): value is Record<string, unknown> {
177
172
  return typeof value === "object" && value !== null && !Array.isArray(value);
178
173
  }
179
174
 
180
- function parsedDoc(raw: string): RawApiDoc | null {
181
- try {
182
- const value: unknown = JSON.parse(raw);
183
- return isRecord(value) ? (value as RawApiDoc) : null;
184
- } catch {
185
- return null;
186
- }
187
- }
188
-
189
- function namespaceOf(doc: RawApiDoc): string | null {
190
- const namespace = isRecord(doc.info) ? doc.info.namespace : undefined;
191
- return typeof namespace === "string" && LUA_NAMESPACE.test(namespace) ? namespace : null;
192
- }
193
-
194
175
  function elementsOf(doc: unknown): RawElement[] {
195
176
  if (!isRecord(doc) || !Array.isArray(doc.elements)) return [];
196
177
  return doc.elements.filter(isRecord) as RawElement[];
@@ -297,16 +278,7 @@ export function buildReleaseImportPlan(input: {
297
278
  throw new Error(`expected an exact Defold release version, received '${input.version}'`);
298
279
  }
299
280
 
300
- const grouped = new Map<string, Array<{ entry: string; doc: RawApiDoc }>>();
301
- for (const zipEntry of [...input.zip.entries()].sort()) {
302
- const doc = parsedDoc(input.zip.read(zipEntry));
303
- if (!doc) continue;
304
- const namespace = namespaceOf(doc);
305
- if (!namespace) continue;
306
- const group = grouped.get(namespace) ?? [];
307
- group.push({ entry: zipEntry, doc });
308
- grouped.set(namespace, group);
309
- }
281
+ const grouped = groupDocsByNamespace(input.zip);
310
282
 
311
283
  const baselineModules = [...input.baseline.modules, ...input.baseline.luaStdlib];
312
284
  const generatedNamespaces = new Set(input.baseline.modules.map((module) => module.namespace));
@@ -319,7 +291,7 @@ export function buildReleaseImportPlan(input: {
319
291
  };
320
292
 
321
293
  const sources: ReleaseSource[] = incomingNamespaces.map((namespace) => {
322
- const group = grouped.get(namespace) as Array<{ entry: string; doc: RawApiDoc }>;
294
+ const group = grouped.get(namespace) ?? [];
323
295
  return {
324
296
  namespace,
325
297
  entries: group.map(({ entry }) => entry).sort(),
@@ -11,6 +11,7 @@ import {
11
11
  loadSrcAugmentations,
12
12
  loadTargetEditorModules,
13
13
  MESSAGES_MANIFEST,
14
+ type ModuleManifestEntry,
14
15
  type ResolveTargetOptions,
15
16
  resolveTargetModules,
16
17
  } from "./regen";
@@ -138,13 +139,18 @@ export async function buildVersionedSurfaceFiles(
138
139
  opts.augmentations ?? loadSrcAugmentations(opts.resolveOpts?.packageRoot ?? undefined)
139
140
  ).filter((file) => kept(file.path));
140
141
 
141
- const files: VersionedSurfaceFile[] = [...modules, ...editorModules].map((entry) => ({
142
- path: entry.outFile,
143
- contents: generateModuleDeclaration({
144
- ...entry,
145
- importsFrom: surfaceCoreTypesImport(entry.outFile),
146
- }).contents,
147
- }));
142
+ // Re-rooting an entry makes it a new object no committed manifest holds, so
143
+ // each lane is handed to the emit as the surface it routes: a type one module
144
+ // declares for another, or names from another, resolves only across the set.
145
+ const rerooted = (entries: readonly ModuleManifestEntry[]): ModuleManifestEntry[] =>
146
+ entries.map((entry) => ({ ...entry, importsFrom: surfaceCoreTypesImport(entry.outFile) }));
147
+ const files: VersionedSurfaceFile[] = [rerooted(modules), rerooted(editorModules)].flatMap(
148
+ (surface) =>
149
+ surface.map((entry) => ({
150
+ path: entry.outFile,
151
+ contents: generateModuleDeclaration(entry, { surface }).contents,
152
+ })),
153
+ );
148
154
 
149
155
  files.push(...augmentations);
150
156
  files.push({ path: "core-types.d.ts", contents: CORE_TYPES_REEXPORT });
package/scripts/regen.ts CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  import { loadTranslations } from "./example-store-io";
43
43
  import { synthesizeProseConstants } from "./prose-constants";
44
44
  import { restoreSharedHandleMethods } from "./shared-handle-methods";
45
- import { type readZip, SYNC_MANIFEST, type SyncManifestEntry } from "./sync-api-docs";
45
+ import { groupDocsByNamespace, mergeApiDocs, type readZip } from "./sync-api-docs";
46
46
 
47
47
  export interface ApiTargetModule {
48
48
  readonly namespace: string;
@@ -120,6 +120,12 @@ function withSkipRules(entry: ModuleManifestEntry, module: ApiTargetModule): Mod
120
120
  };
121
121
  }
122
122
 
123
+ // Shared by both module sources: a step applied in one branch alone gives a
124
+ // ref-doc surface different members from a committed one.
125
+ function restoreOmittedMembers(modules: readonly ModuleManifestEntry[]): ModuleManifestEntry[] {
126
+ return restoreSharedHandleMethods(synthesizeProseConstants(modules));
127
+ }
128
+
123
129
  export function loadTargetModules(
124
130
  target: ApiTarget,
125
131
  packageRoot: string = PACKAGE_ROOT,
@@ -142,7 +148,7 @@ export function loadTargetModules(
142
148
  };
143
149
  return withSkipRules(entry, module);
144
150
  });
145
- return restoreSharedHandleMethods(synthesizeProseConstants(modules));
151
+ return restoreOmittedMembers(modules);
146
152
  }
147
153
 
148
154
  export interface ModuleManifestEntry {
@@ -174,7 +180,6 @@ export interface ResolveTargetOptions {
174
180
  readonly cacheDir?: string;
175
181
  readonly download?: DownloadRefDoc;
176
182
  readonly readZip?: typeof readZip;
177
- readonly syncManifest?: readonly SyncManifestEntry[];
178
183
  readonly packageRoot?: string;
179
184
  readonly channel?: RefDocChannel;
180
185
  readonly fetchChannelInfo?: FetchChannelInfo;
@@ -182,8 +187,8 @@ export interface ResolveTargetOptions {
182
187
 
183
188
  // Source-aware module resolution: a `null`-source target reads committed
184
189
  // fixtures from disk (delegates to loadTargetModules); a `ref-doc` target
185
- // resolves its docs on demand from the version's ref-doc zip, keyed by the
186
- // SYNC_MANIFEST namespace -> zip-entry map.
190
+ // resolves its docs on demand from the version's ref-doc zip, reading every
191
+ // doc the zip declares for the namespace, merged.
187
192
  export async function resolveTargetModules(
188
193
  target: ApiTarget,
189
194
  opts: ResolveTargetOptions = {},
@@ -200,24 +205,24 @@ export async function resolveTargetModules(
200
205
  ...(opts.readZip ? { readZip: opts.readZip } : {}),
201
206
  ...(opts.fetchChannelInfo ? { fetchChannelInfo: opts.fetchChannelInfo } : {}),
202
207
  });
203
- const syncManifest = opts.syncManifest ?? SYNC_MANIFEST;
208
+ const grouped = groupDocsByNamespace(zip);
204
209
  const modules = target.modules.map((module) => {
205
- const sync = syncManifest.find((s) => s.namespace === module.namespace);
206
- if (!sync) {
210
+ const group = grouped.get(module.namespace);
211
+ if (!group) {
207
212
  throw new Error(
208
- `api-targets.json: target "${target.id}" module "${module.namespace}": no SYNC_MANIFEST zip entry`,
213
+ `api-targets.json: target "${target.id}" module "${module.namespace}": no doc in the ref-doc zip declares this namespace`,
209
214
  );
210
215
  }
211
216
  const entry: ModuleManifestEntry = {
212
217
  namespace: module.namespace,
213
- doc: JSON.parse(zip.read(sync.zipEntry)),
218
+ doc: mergeApiDocs(group.map((item) => item.doc)),
214
219
  outFile: module.outFile,
215
220
  importsFrom: target.coreTypesImport,
216
221
  sourceProvenance: provenance,
217
222
  };
218
223
  return withSkipRules(entry, module);
219
224
  });
220
- return synthesizeProseConstants(modules);
225
+ return restoreOmittedMembers(modules);
221
226
  }
222
227
 
223
228
  const API_TARGETS = loadApiTargets();
@@ -451,6 +456,10 @@ export interface GenerateOptions {
451
456
  translations?: TranslationStore;
452
457
  urlParameters?: UrlParameterTable;
453
458
  registeredConstants?: ReadonlySet<string>;
459
+ // The modules the entry is emitted beside, the entry among them. Routing
460
+ // reads this set instead of looking the entry up in a committed manifest, so
461
+ // a surface built with its own import root still routes as a whole.
462
+ surface?: readonly ModuleManifestEntry[];
454
463
  }
455
464
 
456
465
  interface PreparedGeneratedModule {
@@ -472,7 +481,7 @@ function prepareGeneratedModule(
472
481
  entry: ModuleManifestEntry,
473
482
  options?: GenerateOptions,
474
483
  ): PreparedGeneratedModule {
475
- const routed = routedModuleOf(entry);
484
+ const routed = routedModuleOf(entry, options?.surface);
476
485
  const { module, dropped } = routed?.prepared ?? withheldApiModule(entry);
477
486
  const knownConstantFqns = options?.knownConstantFqns ?? collectConstantFqns();
478
487
  const translations = options?.translations ?? loadTranslations();
@@ -495,36 +504,61 @@ interface RoutedSurface {
495
504
  }
496
505
 
497
506
  const routedSurfaces = new Map<string, RoutedSurface>();
507
+ const routedCallerSurfaces = new WeakMap<readonly ModuleManifestEntry[], RoutedSurface>();
508
+
509
+ function routeSurface(entries: readonly ModuleManifestEntry[]): RoutedSurface {
510
+ const withheld = entries.map(withheldApiModule);
511
+ const routed = routeTypeDeclarations(withheld.map(({ module }) => module));
512
+ return {
513
+ modules: new Map(
514
+ entries.map((member, index) => [
515
+ member,
516
+ {
517
+ module: routed[index] as ApiModule,
518
+ dropped: (withheld[index] as { dropped: string[] }).dropped,
519
+ },
520
+ ]),
521
+ ),
522
+ declaredTypes: declaredTypePaths(routed),
523
+ };
524
+ }
498
525
 
499
526
  // A doc may declare a type another module owns (`b2d.joint.revolute_definition`
500
527
  // in the `b2d` doc) or name one another doc declares (`material.constant_info`
501
528
  // in `compute`), so each committed surface is routed as a whole: the runtime
502
- // modules and the editor modules of one target, never across targets. An entry
503
- // outside every committed manifest routes alone, as a single-module surface.
504
- function routedModuleOf(entry: ModuleManifestEntry):
529
+ // modules and the editor modules of one target, never across targets. A caller
530
+ // that names its own surface routes that set instead. An entry with neither, and
531
+ // outside every committed manifest, routes alone, as a single-module surface.
532
+ function routedModuleOf(
533
+ entry: ModuleManifestEntry,
534
+ callerSurface?: readonly ModuleManifestEntry[],
535
+ ):
505
536
  | {
506
537
  prepared: { module: ApiModule; dropped: string[] };
507
538
  declaredTypes: ReadonlyMap<string, string>;
508
539
  }
509
540
  | undefined {
541
+ if (callerSurface !== undefined) {
542
+ let surface = routedCallerSurfaces.get(callerSurface);
543
+ if (surface === undefined) {
544
+ surface = routeSurface(callerSurface);
545
+ routedCallerSurfaces.set(callerSurface, surface);
546
+ }
547
+ const prepared = surface.modules.get(entry);
548
+ // Falling back to a lone module here would emit `unknown` for every type a
549
+ // sibling declares, which type-checks and so goes unnoticed.
550
+ if (prepared === undefined) {
551
+ throw new Error(
552
+ `module "${entry.namespace}" is not a member of the surface it is emitted in`,
553
+ );
554
+ }
555
+ return { prepared, declaredTypes: surface.declaredTypes };
556
+ }
510
557
  const located = committedSurfaceOf(entry);
511
558
  if (located === undefined) return undefined;
512
559
  let surface = routedSurfaces.get(located.key);
513
560
  if (surface === undefined) {
514
- const withheld = located.entries.map(withheldApiModule);
515
- const routed = routeTypeDeclarations(withheld.map(({ module }) => module));
516
- surface = {
517
- modules: new Map(
518
- located.entries.map((member, index) => [
519
- member,
520
- {
521
- module: routed[index] as ApiModule,
522
- dropped: (withheld[index] as { dropped: string[] }).dropped,
523
- },
524
- ]),
525
- ),
526
- declaredTypes: declaredTypePaths(routed),
527
- };
561
+ surface = routeSurface(located.entries);
528
562
  routedSurfaces.set(located.key, surface);
529
563
  }
530
564
  const prepared = surface.modules.get(located.member);
@@ -11,7 +11,7 @@ import { MODULE_MANIFEST } from "./regen";
11
11
  // model because the per-package `rootDir` boundary forbids a types-package
12
12
  // module importing outside its own tree; `scripts/release-model.test.ts`
13
13
  // correspondence-guards these against the model so drift fails CI.
14
- export const DEFOLD_VERSION = "1.13.2";
14
+ export const DEFOLD_VERSION = "1.14.0";
15
15
  export const refDocUrl = (version = DEFOLD_VERSION): string =>
16
16
  `https://github.com/defold/defold/releases/download/${version}/ref-doc.zip`;
17
17
 
@@ -216,7 +216,7 @@ function editorVm(namespace: string): SyncManifestEntry {
216
216
  function entry(
217
217
  namespace: string,
218
218
  zipEntry: string,
219
- fixture: string = `fixtures/defold-1.13.2/${namespace.replace(/\./g, "_")}_doc.json`,
219
+ fixture: string = `fixtures/defold-1.14.0/${namespace.replace(/\./g, "_")}_doc.json`,
220
220
  mergeEntries?: readonly string[],
221
221
  ): SyncManifestEntry {
222
222
  return { namespace, zipEntry, fixture, ...(mergeEntries ? { mergeEntries } : {}) };
@@ -538,6 +538,35 @@ export interface CoverageInput {
538
538
  ignoredUpstream?: ReadonlyMap<string, string>;
539
539
  }
540
540
 
541
+ // The Lua docs of a zip, grouped by the namespace each one declares. Entry names
542
+ // belong to one release (`label` is one entry in 1.9.8 and two in 1.14.0), so
543
+ // the declared namespace picks the docs. Entries are read in sorted order
544
+ // because `mergeApiDocs` keeps the first doc's `info` and the docs' element
545
+ // order; only `.json` entries are read because each read of a real zip is one
546
+ // `unzip -p` process.
547
+ export function groupDocsByNamespace(
548
+ zip: ZipAccessor,
549
+ ): Map<string, { entry: string; doc: Record<string, unknown> }[]> {
550
+ const grouped = new Map<string, { entry: string; doc: Record<string, unknown> }[]>();
551
+ for (const entry of [...zip.entries()].sort()) {
552
+ if (!entry.endsWith(".json")) continue;
553
+ const raw = zip.read(entry);
554
+ let doc: unknown;
555
+ try {
556
+ doc = JSON.parse(raw);
557
+ } catch {
558
+ continue;
559
+ }
560
+ if (!isRecord(doc)) continue;
561
+ const namespace = isRecord(doc.info) ? doc.info.namespace : undefined;
562
+ if (typeof namespace !== "string" || !LUA_NAMESPACE.test(namespace)) continue;
563
+ const group = grouped.get(namespace) ?? [];
564
+ group.push({ entry, doc });
565
+ grouped.set(namespace, group);
566
+ }
567
+ return grouped;
568
+ }
569
+
541
570
  // Enumerate every `doc/*.json` zip entry, reading its `info.namespace` and the
542
571
  // count of FUNCTION elements. Only function-bearing docs are returned, so
543
572
  // message-only / struct-only docs never reach the coverage guard.
package/src/emit-dts.ts CHANGED
@@ -5639,6 +5639,27 @@ function unionFromTokens(tokens: readonly string[], mapType: DefoldTypeMapper):
5639
5639
  }
5640
5640
 
5641
5641
  const BRAND_ARM = /^(?:number|string) & \{ readonly __brand: "([^"]+)" \}$/;
5642
+ const OPAQUE_ARM = /^Opaque<("[^"]+"(?: \| "[^"]+")*)>$/;
5643
+
5644
+ // Drops each `Opaque` arm whose brands are a proper subset of another `Opaque`
5645
+ // arm's. `Opaque<Name>` holds `Name` in one readonly property, so a narrower
5646
+ // brand set is assignable to a wider one and the narrower arm adds nothing.
5647
+ // An arm of any other shape (`Opaque<"x">[]`, an intersection) is never dropped
5648
+ // and never causes a drop.
5649
+ function withoutSubsumedOpaqueArms(mapped: readonly string[]): string[] {
5650
+ const brandSets = mapped.map((arm) => {
5651
+ const match = OPAQUE_ARM.exec(arm);
5652
+ return match === null ? undefined : new Set((match[1] as string).split(" | "));
5653
+ });
5654
+ return mapped.filter((_, index) => {
5655
+ const own = brandSets[index];
5656
+ if (own === undefined) return true;
5657
+ return !brandSets.some(
5658
+ (other) =>
5659
+ other !== undefined && other.size > own.size && [...own].every((b) => other.has(b)),
5660
+ );
5661
+ });
5662
+ }
5642
5663
 
5643
5664
  // Joins already-mapped arms, collapsing the branded ones to their alias when
5644
5665
  // they are exactly a `CONSTANT_UNION_ALIASES` member set. The alias takes the
@@ -5646,16 +5667,17 @@ const BRAND_ARM = /^(?:number|string) & \{ readonly __brand: "([^"]+)" \}$/;
5646
5667
  // (`gui.Easing | Vector`). Arms that are not constants are untouched, and a set
5647
5668
  // with no row keeps every arm inline.
5648
5669
  function joinUnionArms(mapped: readonly string[]): string {
5670
+ const arms = withoutSubsumedOpaqueArms(mapped);
5649
5671
  const branded: string[] = [];
5650
- for (const arm of mapped) {
5672
+ for (const arm of arms) {
5651
5673
  const match = BRAND_ARM.exec(arm);
5652
5674
  if (match !== null) branded.push(match[1] as string);
5653
5675
  }
5654
5676
  const alias = constantUnionAlias(branded);
5655
- if (alias === undefined) return mapped.join(" | ");
5677
+ if (alias === undefined) return arms.join(" | ");
5656
5678
  const out: string[] = [];
5657
5679
  let placed = false;
5658
- for (const arm of mapped) {
5680
+ for (const arm of arms) {
5659
5681
  if (BRAND_ARM.test(arm)) {
5660
5682
  if (placed) continue;
5661
5683
  out.push(`${alias.home}.${alias.name}`);