@gjsify/gtk-host 0.41.0 → 0.44.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 (86) hide show
  1. package/README.md +481 -6
  2. package/lib/esm/adapters/react.js +1 -0
  3. package/lib/esm/adapters/solid.js +1 -0
  4. package/lib/esm/adapters/vue.js +1 -0
  5. package/lib/esm/attrs.js +0 -0
  6. package/lib/esm/conformance/diagnostics.js +2 -2
  7. package/lib/esm/conformance/index.js +2 -2
  8. package/lib/esm/descriptors/adw.js +1 -1
  9. package/lib/esm/descriptors/gtk.js +1 -1
  10. package/lib/esm/descriptors/index.js +1 -1
  11. package/lib/esm/errors.js +1 -1
  12. package/lib/esm/generated/props.js +0 -0
  13. package/lib/esm/generated/widgets.js +1 -0
  14. package/lib/esm/host.js +1 -1
  15. package/lib/esm/index.js +1 -1
  16. package/lib/esm/jsx-runtime.js +1 -0
  17. package/lib/esm/policies.js +1 -1
  18. package/lib/esm/probe.js +1 -0
  19. package/lib/esm/props.js +1 -1
  20. package/lib/esm/react-jsx-runtime.js +1 -0
  21. package/lib/esm/registry.js +1 -1
  22. package/lib/esm/signals.js +1 -1
  23. package/lib/esm/style/errors.js +1 -0
  24. package/lib/esm/style/gtk-css.js +1 -0
  25. package/lib/esm/style/gtk-props.js +1 -0
  26. package/lib/esm/style/index.js +1 -0
  27. package/lib/esm/style/layout.js +1 -0
  28. package/lib/esm/style/paint.js +1 -0
  29. package/lib/esm/style/resolve.js +1 -0
  30. package/lib/esm/style/sheet.js +3 -0
  31. package/lib/esm/style/tokens.js +1 -0
  32. package/lib/esm/tags.js +2 -0
  33. package/lib/esm/vue-components.js +0 -0
  34. package/lib/types/adapters/react.d.ts +224 -0
  35. package/lib/types/adapters/react.spec.d.ts +2 -0
  36. package/lib/types/adapters/solid.d.ts +78 -0
  37. package/lib/types/adapters/solid.spec.d.ts +2 -0
  38. package/lib/types/adapters/vue.d.ts +24 -0
  39. package/lib/types/adapters/vue.spec.d.ts +2 -0
  40. package/lib/types/attrs.d.ts +102 -0
  41. package/lib/types/buildable.spec.d.ts +2 -0
  42. package/lib/types/conformance/diagnostics.d.ts +26 -2
  43. package/lib/types/conformance/index.d.ts +24 -1
  44. package/lib/types/conformance/vectors.d.mts +34 -0
  45. package/lib/types/descriptors/index.d.ts +52 -1
  46. package/lib/types/errors.d.ts +21 -0
  47. package/lib/types/generated/props.d.ts +5505 -0
  48. package/lib/types/generated/surface-data.d.mts +22 -0
  49. package/lib/types/generated/widgets.d.ts +3 -0
  50. package/lib/types/generated.spec.d.ts +4 -0
  51. package/lib/types/generator/emit-types.d.mts +54 -0
  52. package/lib/types/generator/emit.d.mts +50 -0
  53. package/lib/types/generator/gir.d.mts +164 -0
  54. package/lib/types/generator/main.d.mts +23 -0
  55. package/lib/types/generator/mini.fixture.d.mts +30 -0
  56. package/lib/types/generator/surface.d.mts +62 -0
  57. package/lib/types/generator/tsmap.d.mts +73 -0
  58. package/lib/types/generator.spec.d.ts +2 -0
  59. package/lib/types/host.d.ts +102 -1
  60. package/lib/types/index.d.ts +6 -3
  61. package/lib/types/jsx-runtime.d.ts +70 -0
  62. package/lib/types/policies.d.ts +58 -0
  63. package/lib/types/probe.d.ts +87 -0
  64. package/lib/types/props.d.ts +52 -2
  65. package/lib/types/react-jsx-runtime.d.ts +108 -0
  66. package/lib/types/registry.d.ts +1 -1
  67. package/lib/types/signals.d.ts +16 -1
  68. package/lib/types/style/errors.d.ts +6 -0
  69. package/lib/types/style/gtk-css.d.ts +24 -0
  70. package/lib/types/style/gtk-css.spec.d.ts +2 -0
  71. package/lib/types/style/gtk-props.d.ts +27 -0
  72. package/lib/types/style/gtk-props.spec.d.ts +2 -0
  73. package/lib/types/style/index.d.ts +13 -0
  74. package/lib/types/style/layout.d.ts +163 -0
  75. package/lib/types/style/layout.spec.d.ts +2 -0
  76. package/lib/types/style/paint.d.ts +53 -0
  77. package/lib/types/style/paint.spec.d.ts +2 -0
  78. package/lib/types/style/resolve.d.ts +58 -0
  79. package/lib/types/style/sheet.d.ts +40 -0
  80. package/lib/types/style/sheet.spec.d.ts +2 -0
  81. package/lib/types/style/tokens.d.ts +58 -0
  82. package/lib/types/tags.d.ts +25 -0
  83. package/lib/types/testing/gate.d.mts +25 -0
  84. package/lib/types/types.d.ts +56 -3
  85. package/lib/types/vue-components.d.ts +46 -0
  86. package/package.json +85 -8
@@ -0,0 +1,22 @@
1
+ /** Declaration GType -> its OWN writable properties, kebab-spelled as GObject knows them. */
2
+ export declare const OWN_PROPS: Readonly<Record<string, readonly string[]>>;
3
+ /** Declaration GType -> its OWN signals. */
4
+ export declare const OWN_SIGNALS: Readonly<Record<string, readonly string[]>>;
5
+ /** Widget GType -> every declaration its members come from, self first. */
6
+ export declare const DECLS: Readonly<Record<string, readonly string[]>>;
7
+ /** Enum GType -> the nicks the surface offers. */
8
+ export declare const ENUM_NICKS: Readonly<Record<string, readonly string[]>>;
9
+ /** Widget GType -> its kebab tag. */
10
+ export declare const TAGS: Readonly<Record<string, string>>;
11
+ /**
12
+ * `Type.property` / `Type::signal` -> the release that introduced it.
13
+ *
14
+ * This is what keeps the runtime cross-check honest across a version gap instead
15
+ * of needing an allowlist. Measured on the pinned GIR against gtk 4.22.4:
16
+ * `GtkApplicationWindow::save-state` is in the surface and NOT in the installed
17
+ * library, because it arrived in 4.24. Without a since-map the only options are to
18
+ * stop checking signals or to hard-code two names; with it, the rule is exact —
19
+ * a member the installed library lacks must be explained by a version newer than
20
+ * the one running, and anything else is a real defect.
21
+ */
22
+ export declare const SINCE: Readonly<Record<string, string>>;
@@ -0,0 +1,3 @@
1
+ import type { GeneratedWidget } from '../types.js';
2
+ export declare const GENERATED_PROVENANCE: "Gtk-4.0 Adw-1";
3
+ export declare const GENERATED_WIDGETS: readonly GeneratedWidget[];
@@ -0,0 +1,4 @@
1
+ /** `'4.24'` against `'4.22.4'` — true. Missing or equal is NOT newer. */
2
+ export declare const newerThan: (since: string | undefined, running: string) => boolean;
3
+ declare const _default: () => Promise<void>;
4
+ export default _default;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Emit the TYPE surfaces: one interface per GIR declaration, two tag maps.
3
+ *
4
+ * WHAT THE DIALECTS FORCED, all of it measured rather than assumed (ADR 0028 § 7,
5
+ * and the numbers live there):
6
+ *
7
+ * - BOTH PROPERTY SPELLINGS, for opposite reasons in the two dialects. Vue does
8
+ * not need the kebab key — Volar camelizes an attribute before resolving it, so
9
+ * `:height-request` finds `heightRequest` and catches a wrong value type there.
10
+ * JSX does need it: TypeScript exempts every hyphenated attribute from
11
+ * excess-property checking, so `no-such={1}` on an intrinsic is silently
12
+ * accepted, and the only checking a kebab attribute can ever get is the value
13
+ * check that a DECLARED key gives it. Declaring both costs 561 extra members
14
+ * and buys the one spelling GTK itself uses.
15
+ * - JSX KEYS ARE KEBAB, VUE KEYS ARE THE GTYPE NAME. A capitalised
16
+ * `JSX.IntrinsicElements` key is never consulted — TypeScript reads `<GtkBox/>`
17
+ * as a value reference — while Volar resolves a kebab TAG against a GType key
18
+ * but not a Pascal tag against a kebab key. So each dialect gets the one
19
+ * spelling that works, from one shared props interface.
20
+ * - EXACT HANDLER SIGNATURES, never `(...args: unknown[]) => void`. Measured
21
+ * twice over: an annotated `(row: AdwActionRow) => void` will not assign to an
22
+ * `unknown[]` rest (TS2322, contravariance), and an inline arrow's parameter
23
+ * arrives as `unknown` so using it fails (TS2345). A generated surface that
24
+ * typed handlers loosely would break every handler that names its parameter.
25
+ */
26
+ import type { SurfaceModel } from './surface.mjs';
27
+ /**
28
+ * Volar's own resolution rule, reproduced so the generator can SEE the gap.
29
+ *
30
+ * A Vue template tag is camelized (`-x` -> `X`) and capitalised before lookup, and
31
+ * that transform has no acronym knowledge: `gtk-gl-area` becomes `GtkGlArea`,
32
+ * which is not `GtkGLArea`. Measured — `<gtk-gl-area>` against a `GtkGLArea` key
33
+ * is TS2339. Exactly one of the 164 concrete widgets is affected today, and the
34
+ * generator finds it rather than leaving it to a bug report: such a widget gets an
35
+ * extra kebab-spelled key, which a kebab tag does resolve against.
36
+ */
37
+ export declare const volarResolves: (tag: string, gtype: string) => boolean;
38
+ export interface EmittedFile {
39
+ readonly path: string;
40
+ readonly text: string;
41
+ }
42
+ export declare function emitProps(model: SurfaceModel, provenance: string): EmittedFile;
43
+ /**
44
+ * Emit the data the runtime cross-check reads.
45
+ *
46
+ * `.mts`, so it is EXCLUDED from the library build (`src/**​/*.{ts,js}`) and never
47
+ * ships — the same trick `testing/gate.mts` uses. It exists to let a spec ask the
48
+ * installed GTK whether the generated surface is true: every property present as a
49
+ * writable ParamSpec, every signal resolvable by `GObject.signal_lookup`, every
50
+ * enum nick resolvable through the host's own enum lookup. Shipping this as
51
+ * runtime data would make it a second source for what `paramSpecs()` already
52
+ * answers from the class that is actually installed.
53
+ */
54
+ export declare function emitSurfaceData(model: SurfaceModel, provenance: string): EmittedFile;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Emit the widget table, and the four gates that make it trustworthy.
3
+ *
4
+ * WHAT SHIPS is deliberately thin: tag, GType and the map between them (ADR 0028
5
+ * § 1, as amended). Property names, the construct-only set and signal names are
6
+ * consumed INSIDE the generator to emit the TYPE surfaces — shipping them as
7
+ * runtime data would be a second source with no consumer, because `props.ts` and
8
+ * `host.ts` already resolve every property through `paramSpecs()` on the class
9
+ * that is actually installed.
10
+ *
11
+ * The artifact is COMMITTED and gets no regenerate-and-compare gate. Committing
12
+ * is what makes `check`, `lint`, `format`, the build cache, the tarball and a
13
+ * fresh clone with no GIR all work unchanged, and it makes a widget table
14
+ * reviewable in a diff. A byte gate would fail for nothing the day a second CI
15
+ * leg runs a different GTK: four GTK versions sit on the maintainer workstation
16
+ * alone (4.16.13, 4.20.4, 4.22.4, 4.23.0) and the pinned `@girs` is a fifth
17
+ * answer. The machine-INDEPENDENT check is the runtime one that already exists —
18
+ * `descriptorProblems()` against the installed typelib.
19
+ */
20
+ import type { ChildPolicy, WidgetDescriptor } from '../types.js';
21
+ import type { GirClass, GirNamespace } from './gir.mjs';
22
+ /**
23
+ * The `gi://` import a generated table needs, per GIR namespace.
24
+ *
25
+ * Injectable through `EmitInput`, not read from here directly, and the fixture
26
+ * that forced that is the point: with the map hard-coded, G4 rejected every widget
27
+ * of a five-class test namespace as an "unexpected gtype shape" and the floor check
28
+ * below it could never be reached. A gate whose own test cannot run is a gate on
29
+ * trust.
30
+ */
31
+ export declare const GJS_MODULES: Readonly<Record<string, string>>;
32
+ export interface EmitInput {
33
+ readonly namespaces: readonly GirNamespace[];
34
+ readonly widgets: readonly GirClass[];
35
+ readonly curated: readonly WidgetDescriptor[];
36
+ /** `methodsOf` from the conformance module — one extractor, not a second copy. */
37
+ readonly methodsOf: (policy: ChildPolicy) => string[];
38
+ /** GIR namespace -> `gi://` import. Defaults to the GTK/Adwaita pair. */
39
+ readonly modules?: Readonly<Record<string, string>>;
40
+ }
41
+ export declare class GateFailure extends Error {
42
+ readonly gate: string;
43
+ constructor(gate: string, message: string);
44
+ }
45
+ export interface EmitResult {
46
+ readonly text: string;
47
+ readonly count: number;
48
+ readonly provenance: string;
49
+ }
50
+ export declare function emitWidgets(input: EmitInput, floor?: number): EmitResult;
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Read the GIR XML into a plain model.
3
+ *
4
+ * WHY THE XML AND NOT THE TYPELIB. Both carry the structure — measured on gtk
5
+ * 4.22.4, `GIRepository` 3.0 answers class enumeration, abstractness, properties
6
+ * with `CONSTRUCT_ONLY`, signals, methods and property enum types, and its
7
+ * concrete-widget set is IDENTICAL to the XML's. What the typelib does not carry,
8
+ * and offers no API that could, is documentation, default values and
9
+ * since-versions: it strips them by design, which is why gi-docgen reads the GIR.
10
+ * ADR 0028 § 6 makes these surfaces a PUBLISHED artifact whose point is what a
11
+ * consumer's editor shows on hover, so the richer input wins.
12
+ *
13
+ * WHY @gjsify/domparser. Measured on the 6.2 MB `Gtk-4.0.gir`: 232 ms, RSS 53 MB
14
+ * -> 96 MB, and its `application/xml` mode is right where it has to be —
15
+ * selectors are case-SENSITIVE (`CLASS` matches nothing), namespaced tags work
16
+ * (`glib\:signal` -> 352), namespaced attributes work (`[glib\:type-name]` ->
17
+ * 445). No new dependency, and a tier-1 package gets dogfooded against a real
18
+ * 6 MB document.
19
+ *
20
+ * ONE FROZEN WART TO KNOW ABOUT: in XML mode `tagName` is LOWERCASED and
21
+ * `nodeName` UPPERCASED, both wrong by the XML spec and both deliberately frozen
22
+ * (ADR 0026 § Decision 4) because a measured consumer switches on lowercase tag
23
+ * literals at 24 sites. Harmless here — every GIR tag is lowercase already — but
24
+ * a generator for a mixed-case dialect could not use this reader as-is.
25
+ */
26
+ /**
27
+ * A GIR type reference, with the array flag kept SEPARATE.
28
+ *
29
+ * Not a nicety: `<property name="css-classes"><array><type name="utf8"/></array>`
30
+ * is `string[]`, and a reader that asked for a descendant `<type>` would answer
31
+ * `string` — for a property authored as `cssClasses={['card']}` on every second
32
+ * widget. `querySelector` searches descendants, so the array case has to be
33
+ * checked FIRST, on own children only.
34
+ */
35
+ export interface GirType {
36
+ /** Qualified where GIR wrote a reference (`Gtk.Orientation`), bare for a C scalar (`gboolean`). */
37
+ readonly name: string;
38
+ readonly array: boolean;
39
+ }
40
+ export interface GirProperty {
41
+ readonly name: string;
42
+ readonly writable: boolean;
43
+ readonly readable: boolean;
44
+ readonly constructOnly: boolean;
45
+ readonly type: GirType;
46
+ /** GIR carries it, the typelib does not — `GTK_ORIENTATION_HORIZONTAL`. */
47
+ readonly defaultValue?: string;
48
+ readonly deprecated: boolean;
49
+ /** GIR's `version` attribute — the release that introduced this member. */
50
+ readonly since?: string;
51
+ readonly doc?: string;
52
+ }
53
+ /**
54
+ * The GObject nick a GIR enum member would get if GIR did not say.
55
+ *
56
+ * GIR writes `baseline_fill`; the nick GObject registered is `baseline-fill`. This
57
+ * is the FALLBACK for a `<member>` with no `glib:nick` — the attribute itself is
58
+ * the answer wherever it exists, because the substitution is right for Gtk and Adw
59
+ * by luck and not by construction: some nicks keep an underscore it would have
60
+ * replaced, and nothing but the attribute knows which one survived.
61
+ *
62
+ * The count that used to sit here is now `ts-for-gir/scripts/check-nick-derivation.mjs`,
63
+ * which owns the corpus this measures over. It also settles what looked like a
64
+ * contradiction: this file said 97 and ts-for-gir said 889 over the SAME 705 GIRs,
65
+ * and both were right — the derivations differ. 889 counts a fallback that also
66
+ * lowercases, which adds the 792 members whose GIR name carries uppercase and whose
67
+ * nick preserves it. This one does not lowercase, so 97 is its number. Quote a
68
+ * derived-nick count without naming its derivation and you get that pair back.
69
+ */
70
+ export declare const nickOf: (member: string) => string;
71
+ export interface GirParam {
72
+ readonly name: string;
73
+ readonly type: GirType;
74
+ /** GIR's `direction`, which defaults to `in` when the attribute is absent. */
75
+ readonly direction: 'in' | 'out' | 'inout';
76
+ /**
77
+ * GIR's `caller-allocates`. It decides whether a non-`in` slot holds anything
78
+ * at all — see `renderParam` in `surface.mts`, which is the only reader.
79
+ */
80
+ readonly callerAllocates: boolean;
81
+ }
82
+ export interface GirSignal {
83
+ readonly name: string;
84
+ /**
85
+ * WITHOUT the emitting object. GIR lists a signal's `<parameters>` free of the
86
+ * instance (unlike a `<method>`), and the host strips the instance too —
87
+ * `next(...args.slice(1))` in `signals.ts`. The two line up exactly, which is
88
+ * why a generated handler signature can be taken from here verbatim.
89
+ */
90
+ readonly params: readonly GirParam[];
91
+ readonly deprecated: boolean;
92
+ /** GIR's `version` attribute — the release that introduced this signal. */
93
+ readonly since?: string;
94
+ readonly doc?: string;
95
+ }
96
+ export interface GirClass {
97
+ /** The GType name — `GtkBox`. Absent for a class GIR declares without one. */
98
+ readonly gtype: string;
99
+ /** GIR-local name — `Box`. */
100
+ readonly name: string;
101
+ readonly namespace: string;
102
+ readonly kind: 'class' | 'interface';
103
+ readonly abstract: boolean;
104
+ /** Namespace-qualified parent — `Gtk.Widget` — or null at the root. */
105
+ readonly parent: string | null;
106
+ /** Qualified interfaces (`<implements>`), or an interface's `<prerequisite>`s. */
107
+ readonly implements: readonly string[];
108
+ readonly methods: readonly string[];
109
+ readonly signals: readonly GirSignal[];
110
+ readonly properties: readonly GirProperty[];
111
+ readonly doc?: string;
112
+ }
113
+ export interface GirEnum {
114
+ readonly gtype: string;
115
+ /** Namespace-qualified — `Gtk.Orientation`. */
116
+ readonly qualified: string;
117
+ readonly flags: boolean;
118
+ /** The NICKS, which is what a string-accepting surface needs. */
119
+ readonly members: readonly string[];
120
+ }
121
+ export interface GirNamespace {
122
+ readonly name: string;
123
+ readonly version: string;
124
+ /** Classes AND interfaces, in one list: `implements` resolves against the same index. */
125
+ readonly classes: readonly GirClass[];
126
+ readonly enums: readonly GirEnum[];
127
+ /** Every other named type GIR declares — records, unions, callbacks, aliases. */
128
+ readonly otherTypes: readonly string[];
129
+ }
130
+ export declare function readNamespace(xml: string): GirNamespace;
131
+ /** Every class and interface across the namespaces read, keyed the way GIR references them. */
132
+ export declare function indexClasses(namespaces: readonly GirNamespace[]): Map<string, GirClass>;
133
+ /**
134
+ * Walk to the root, or as far as the loaded namespaces reach.
135
+ *
136
+ * A chain that leaves the loaded set is not an error: `Gtk.Widget`'s own parent
137
+ * is `GObject.InitiallyUnowned`, and loading GObject-2.0 to learn that would buy
138
+ * nothing. What matters is whether `Gtk.Widget` is ON the chain.
139
+ */
140
+ export declare function ancestors(cls: GirClass, index: Map<string, GirClass>): GirClass[];
141
+ /**
142
+ * Every declaration a class's members can come from — self, ancestors, interfaces.
143
+ *
144
+ * The interfaces are not optional and the measurement says why: `GtkBox` declares
145
+ * four properties of its own and `orientation` is NOT among them. It lives on
146
+ * `Gtk.Orientable`, an `<implements>` of `GtkBox`, because GObject installs
147
+ * interface properties on the implementor at runtime while GIR keeps them once,
148
+ * on the interface. A class-only reader therefore emits a surface in which
149
+ * `<gtk-box orientation="vertical">` — the most-written GtkBox attribute there is
150
+ * — is a type error.
151
+ *
152
+ * Order is self, then the parent chain, then interfaces of each, so a nearer
153
+ * declaration of the same property name wins.
154
+ */
155
+ export declare function declarations(cls: GirClass, index: Map<string, GirClass>): GirClass[];
156
+ /**
157
+ * Concrete descendants of a root class — the set a renderer can actually create.
158
+ *
159
+ * The root is a PARAMETER so a fixture can exercise this with a five-class GIR of
160
+ * its own. Hard-coding `GtkWidget` would leave the walk, the abstract filter and
161
+ * the namespace-crossing parent chain testable only against the real 6.2 MB file,
162
+ * where an off-by-one is invisible.
163
+ */
164
+ export declare function concreteWidgets(namespaces: readonly GirNamespace[], root?: string): GirClass[];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The generator CLI.
3
+ *
4
+ * gjsify run generate -- [--gir-dir DIR]... [--out DIR]
5
+ *
6
+ * WHY THE GIR PATH IS AN ARGUMENT AND NOT A DEPENDENCY. The GIR files are not in
7
+ * this repository and should not be: `Gtk-4.0.gir` alone is 6.2 MB, they are
8
+ * distro artefacts, and four different GTK versions sit on the maintainer
9
+ * workstation. So the ARTEFACT is committed (ADR 0028 § Implementation) and the
10
+ * input is located at generation time — from `--gir-dir`, from `GJSIFY_GIR_DIR`,
11
+ * or from the search list below. A fresh clone builds, checks, packs and tests
12
+ * with no GIR present at all; only regenerating needs one.
13
+ *
14
+ * THERE IS NO `--check` MODE, and the attempt to add one is worth recording: the
15
+ * `generate` script pipes the output through `gjsify format`, which reflows it
16
+ * (+4 KB on `props.ts`), so a byte comparison against the committed file reports a
17
+ * difference on every run. Two of my own measurements said otherwise first, both
18
+ * because they compared a formatted file against itself. `git diff` after a
19
+ * regeneration answers the same question with no machinery, and the check that
20
+ * matters is machine-INDEPENDENT: `generated.spec.ts` asks the installed GTK
21
+ * whether every generated name is real.
22
+ */
23
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A five-class GIR, so the generator is testable without the 6.2 MB one.
3
+ *
4
+ * Every element here earns its place by exercising a branch the real GIR cannot
5
+ * exercise ON DEMAND:
6
+ *
7
+ * - `Orientable.label` is a `gint` and `Box.label` a `utf8`, so the two bases of
8
+ * `MiniBoxProps` disagree and the `Omit` machinery has to fire. Measured, the
9
+ * real Gtk+Adw hierarchy produces ZERO such conflicts, which left that code
10
+ * shipped and unexercised — this is what turns it into a tested path.
11
+ * - `baseline_fill` is a two-word enum member, so the GIR-underscore-to-nick
12
+ * substitution is visible.
13
+ * - `css-classes` is an ARRAY of a scalar, the case a descendant `<type>` lookup
14
+ * gets wrong by answering `string` for `string[]`.
15
+ * - `locked` is readable and NOT writable, so the writable filter has something to
16
+ * drop.
17
+ * - `MiniGLThing` has two adjacent capitals, so `tagOf`'s acronym rule and the
18
+ * Volar-resolution gap both have a case.
19
+ * - `Nameless` has no `glib:type-name`, and `NotAWidget` does not descend from the
20
+ * root — the two reasons a class is skipped.
21
+ *
22
+ * Embedded as a string rather than a file: the test bundle runs under gjs from a
23
+ * path that is not the source tree, and a fixture that cannot be found is a test
24
+ * that silently checks nothing.
25
+ */
26
+ export declare const MINI_GIR = "<?xml version=\"1.0\"?>\n<repository version=\"1.2\"\n xmlns=\"http://www.gtk.org/introspection/core/1.0\"\n xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"\n xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\">\n <namespace name=\"Mini\" version=\"1.0\">\n <enumeration name=\"Align\" glib:type-name=\"MiniAlign\">\n <member name=\"fill\" value=\"0\"/>\n <member name=\"baseline_fill\" value=\"1\"/>\n <!-- A nick GIR CARRIES and the substitution would get wrong. 97 members\n across this workspace's GIRs are shaped like this. -->\n <member name=\"two_words\" value=\"2\" glib:nick=\"renamed\"/>\n </enumeration>\n <bitfield name=\"StateFlags\" glib:type-name=\"MiniStateFlags\">\n <member name=\"active\" value=\"1\"/>\n </bitfield>\n <record name=\"Rect\" c:type=\"MiniRect\"/>\n <interface name=\"Orientable\" glib:type-name=\"MiniOrientable\">\n <doc xml:space=\"preserve\">Anything with an orientation. Second sentence, dropped.</doc>\n <property name=\"orientation\" writable=\"1\">\n <doc xml:space=\"preserve\">Which way round it goes.</doc>\n <type name=\"Align\"/>\n </property>\n <property name=\"label\" writable=\"1\">\n <type name=\"gint\"/>\n </property>\n </interface>\n <class name=\"Widget\" glib:type-name=\"MiniWidget\" abstract=\"1\" parent=\"GObject.Object\">\n <property name=\"visible\" writable=\"1\"><type name=\"gboolean\"/></property>\n <property name=\"css-classes\" writable=\"1\"><array><type name=\"utf8\"/></array></property>\n <property name=\"locked\"><type name=\"gint\"/></property>\n <property name=\"area\" writable=\"1\"><type name=\"Rect\"/></property>\n <glib:signal name=\"destroy\"/>\n <method name=\"show\"/>\n </class>\n <class name=\"Box\" glib:type-name=\"MiniBox\" parent=\"Widget\">\n <implements name=\"Orientable\"/>\n <property name=\"spacing\" writable=\"1\"><type name=\"gint\"/></property>\n <property name=\"label\" writable=\"1\"><type name=\"utf8\"/></property>\n <property name=\"old-thing\" writable=\"1\" deprecated=\"1\"><type name=\"gboolean\"/></property>\n <glib:signal name=\"row-activated\" version=\"1.2\">\n <parameters>\n <parameter name=\"row\"><type name=\"Widget\"/></parameter>\n <parameter name=\"how\"><type name=\"Align\"/></parameter>\n </parameters>\n </glib:signal>\n <method name=\"append\"/>\n <method name=\"remove\"/>\n </class>\n <class name=\"GLThing\" glib:type-name=\"MiniGLThing\" parent=\"Widget\">\n <property name=\"mode\" writable=\"1\"><type name=\"Align\"/></property>\n <property name=\"flags\" writable=\"1\"><type name=\"StateFlags\"/></property>\n </class>\n <class name=\"Nameless\" parent=\"Widget\"/>\n <class name=\"NotAWidget\" glib:type-name=\"MiniNotAWidget\" parent=\"GObject.Object\"/>\n </namespace>\n</repository>\n";
27
+ /** The fixture's own namespace-to-package map, which the real one has no entry for. */
28
+ export declare const MINI_PACKAGES: {
29
+ readonly Mini: "@girs/mini-1.0";
30
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Build the declaration graph the type surfaces are emitted from.
3
+ *
4
+ * ONE INTERFACE PER GIR DECLARATION, carrying its OWN members and extending its
5
+ * bases — not one flat interface per widget. The measurement decides it: the 164
6
+ * concrete widgets have 6768 writable property slots between them and only 561
7
+ * distinct property names. Flattening would emit the same `visible?: boolean`
8
+ * 164 times; mirroring GIR's own inheritance emits it once, on `GtkWidgetProps`,
9
+ * and the diff of a GTK update then shows what actually changed.
10
+ *
11
+ * THE CONFLICT MACHINERY IS NOT DEFENSIVE. TypeScript requires a multiply
12
+ * inherited member to be IDENTICAL in every base — `string` and `string | null`
13
+ * are not — and GTK's interfaces do redeclare class properties. So a base that
14
+ * loses a member is emitted as `Omit<Base, 'name'>`, because a local redeclaration
15
+ * does NOT repair incompatible bases (it turns one error into another).
16
+ */
17
+ import { type GirClass, type GirNamespace } from './gir.mjs';
18
+ import { type Universe } from './tsmap.mjs';
19
+ export interface PropMember {
20
+ readonly kebab: string;
21
+ readonly camel: string;
22
+ readonly ts: string;
23
+ readonly doc?: string;
24
+ readonly deprecated: boolean;
25
+ readonly since?: string;
26
+ }
27
+ export interface SignalMember {
28
+ readonly signal: string;
29
+ readonly prop: string;
30
+ /** Already rendered — `(row: Gtk.ListBoxRow) => void`. */
31
+ readonly ts: string;
32
+ readonly doc?: string;
33
+ readonly deprecated: boolean;
34
+ readonly since?: string;
35
+ }
36
+ export interface Declaration {
37
+ /** GIR-qualified — `Gtk.Box`. The graph is keyed on this, the way GIR references. */
38
+ readonly key: string;
39
+ readonly gtype: string;
40
+ readonly kind: 'class' | 'interface';
41
+ /** `GtkBoxProps`. */
42
+ readonly iface: string;
43
+ readonly bases: readonly string[];
44
+ readonly props: readonly PropMember[];
45
+ readonly signals: readonly SignalMember[];
46
+ readonly doc?: string;
47
+ }
48
+ export interface SurfaceModel {
49
+ /** The 164, sorted by GType name. */
50
+ readonly widgets: readonly GirClass[];
51
+ readonly declarations: ReadonlyMap<string, Declaration>;
52
+ /** Widget GType -> the GTypes of every declaration its members come from, self first. */
53
+ readonly closure: ReadonlyMap<string, readonly string[]>;
54
+ /** Enum GType -> nicks, for the emitted aliases and for the runtime nick check. */
55
+ readonly enumNicks: ReadonlyMap<string, readonly string[]>;
56
+ readonly namespacesUsed: ReadonlySet<string>;
57
+ /** Copied from the universe, so the emitter needs no second source for it. */
58
+ readonly packages: Readonly<Record<string, string>>;
59
+ /** Base -> members it must not contribute, because a nearer declaration disagrees. */
60
+ readonly omissions: ReadonlyMap<string, ReadonlyMap<string, readonly string[]>>;
61
+ }
62
+ export declare function buildSurface(widgets: readonly GirClass[], namespaces: readonly GirNamespace[], universe: Universe): SurfaceModel;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * GIR type -> TypeScript type, and the two places variance decides the answer.
3
+ *
4
+ * Measured over the real Gtk-4.0 + Adw-1 GIR with the seven referenced namespaces
5
+ * loaded: 6768 writable property slots and 1889 signal parameters, and NOT ONE
6
+ * type name that fails to resolve. So there is no "unknown fallback" branch to
7
+ * hide behind — every type either maps or the generator fails and says which.
8
+ *
9
+ * WHERE PROPERTIES AND PARAMETERS MUST DIFFER, twice, both times because a
10
+ * property is an INPUT and a handler parameter is an OUTPUT:
11
+ *
12
+ * 1. An enum PROPERTY takes the nick (`orientation="vertical"`) — the host
13
+ * resolves it (`coerce()` in props.ts) precisely because GObject would
14
+ * silently keep the old value. An enum PARAMETER arrives as a NUMBER: GJS
15
+ * hands the marshalled value. Offering the nick union in a parameter would
16
+ * invite `if (mode === 'single')`, which is always false — a silent bug worse
17
+ * than no typing at all.
18
+ * 2. Nullability widens a property harmlessly and BREAKS a parameter. GIR marks
19
+ * many parameters `nullable="1"`, but a parameter type is contravariant: with
20
+ * `(row: Gtk.ListBoxRow | null) => void` declared, the ordinary
21
+ * `onRowActivated={(row: Gtk.ListBoxRow) => …}` stops compiling. So object
22
+ * PROPERTIES get `| null` (clearing one is legitimate) and parameters do not,
23
+ * and this comment is the record that the second was a decision rather than an
24
+ * oversight.
25
+ *
26
+ * FLAGS ARE `number`, in both positions, and that mirrors the runtime exactly: the
27
+ * host REFUSES a nick string for a flags property by name (`err.badFlags`),
28
+ * because resolving a nick set ("horizontal|vertical") is not something GObject
29
+ * exposes. A union of nicks here would type-check something the host rejects.
30
+ */
31
+ import type { GirClass, GirEnum, GirNamespace, GirType } from './gir.mjs';
32
+ /** GIR namespace -> the `@girs` package that types it. Only these seven are referenced. */
33
+ export declare const GIRS_PACKAGES: Readonly<Record<string, string>>;
34
+ export interface Universe {
35
+ readonly enums: Map<string, GirEnum>;
36
+ readonly classes: Map<string, GirClass>;
37
+ readonly others: Set<string>;
38
+ /**
39
+ * GIR namespace -> the `@girs` package that types it.
40
+ *
41
+ * Carried here rather than read from the module constant, so a fixture can
42
+ * supply its own five-class namespace. Without it the type mapper is only ever
43
+ * exercised against the real GIR, where the branch that refuses an unknown
44
+ * namespace can never be reached.
45
+ */
46
+ readonly packages: Readonly<Record<string, string>>;
47
+ }
48
+ export declare function buildUniverse(namespaces: readonly GirNamespace[], packages?: Readonly<Record<string, string>>): Universe;
49
+ export { nickOf } from './gir.mjs';
50
+ export interface TsType {
51
+ readonly text: string;
52
+ /** GIR namespaces whose `@girs` import the emitted text needs. */
53
+ readonly namespaces: readonly string[];
54
+ /** Enum GTypes whose nick alias the emitted text references. */
55
+ readonly enums: readonly string[];
56
+ }
57
+ export declare class TypeMapError extends Error {
58
+ }
59
+ /** A nick union, or `never` for an enum GIR declares with no members. */
60
+ export declare const nickUnion: (e: GirEnum) => string;
61
+ /** The alias name a nick union is emitted under — the GType name, so it is unique. */
62
+ export declare const nickAliasOf: (e: GirEnum) => string;
63
+ /**
64
+ * Map one GIR type.
65
+ *
66
+ * `where` is not decoration: see the header — an enum in a `prop` accepts nicks
67
+ * and in a `param` never does, and an object in a `prop` accepts null.
68
+ */
69
+ export declare function tsTypeOf(type: GirType, u: Universe, where: 'prop' | 'param'): TsType;
70
+ /** `row-activated` -> `onRowActivated`; `notify` handled by the caller. */
71
+ export declare const eventPropOf: (signal: string) => string;
72
+ /** `can-focus` -> `canFocus`. The inverse of the host's `toPropertyName`. */
73
+ export declare const camelOf: (kebab: string) => string;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -26,9 +26,68 @@ export declare function setText(node: HostText | HostAnchor, data: string): void
26
26
  /** Vue's bulk path and React's `shouldSetTextContent`: drop children, set the sink. */
27
27
  export declare function setElementText(el: HostElement, text: string): void;
28
28
  export declare function insert(node: HostNode, parent: HostElement, anchor?: HostNode | null): void;
29
+ /**
30
+ * TWO facts, not one: it says it is an element, and it carries a descriptor.
31
+ *
32
+ * `kind` alone is a plain string a GObject wrapper could carry; the descriptor is
33
+ * what every host op actually dereferences. Checking both keeps the refusal from
34
+ * being either paranoid or fooled.
35
+ *
36
+ * Exported because the Vue adapter had these same two lines VERBATIM, and its own
37
+ * comment said so — it needs the question before `adopt`, to tell a host parent
38
+ * from the raw widget `<Teleport :to="el">` hands through. A predicate stated in
39
+ * two places is a predicate that can disagree with itself about what a node is.
40
+ */
41
+ export declare function isHostElement(value: unknown): value is HostElement;
42
+ /**
43
+ * Name a foreign value the way its owner would recognise it.
44
+ *
45
+ * ONE function, because there were two and each was wrong for the other's input.
46
+ * The host's read `constructor.$gtype`, so it could not name a DOM element — the
47
+ * single most likely thing to arrive from a Solid app that imported `<Dynamic>`
48
+ * from `solid-js/web`. The Solid adapter's read `tagName`, so it answered
49
+ * `a Object` for the GObject wrapper `<Teleport :to="el">` hands over. Both are
50
+ * asking "what did I get instead of a node", so both answers belong to one place.
51
+ *
52
+ * ORDER IS LOAD-BEARING, and it is measured rather than reasoned: under gjs
53
+ * 1.88.1 `({}).constructor.$gtype` EXISTS and `GObject.type_name` calls it
54
+ * `JSObject`. So a `$gtype`-first version names every object literal — and every
55
+ * DOM-ish stub, which is an object literal — `JSObject`, and the host's own
56
+ * documented `a plain object` fallback was unreachable for exactly the shape it
57
+ * names. `tagName` first, and `JSObject` treated as the absence of a GType.
58
+ */
59
+ export declare function describeValue(value: unknown): string;
29
60
  /** Detach only — reversible. Frameworks move nodes; `remove` must not destroy one. */
30
61
  export declare function remove(node: HostNode): void;
31
- export declare function clearContainer(parent: HostElement): void;
62
+ /**
63
+ * Destroy every child of `parent`, bounded.
64
+ *
65
+ * Exported because both adapters wrote their OWN `childSnapshot` to do exactly this
66
+ * — byte-identical to each other, and both walking `next` raw. That is the one walk
67
+ * the bound above exists to make unforgettable, and it is the walk where the
68
+ * malformed link actually appears: `insert(node, parent, node)` is a defined DOM
69
+ * no-op, so Solid's adjacent-swap fast path emits it against a CONTAINER's child
70
+ * list. Unbounded, that is a hang rather than GTK's usual exit 0.
71
+ *
72
+ * This replaces a `clearContainer` that had no callers anywhere and DETACHED rather
73
+ * than destroyed — the wrong half for every caller that wanted it, and a name React's
74
+ * own HostConfig already uses for the destroying kind.
75
+ */
76
+ export declare function destroyChildren(parent: HostElement): void;
77
+ /**
78
+ * Disconnect a node's handlers WITHOUT touching the tree.
79
+ *
80
+ * The narrow half of `destroy`, and it exists because a framework can tell us "this
81
+ * node is gone" while still holding its sibling links: Solid disposes a per-node
82
+ * scope BEFORE its reconciler runs, and `reconcileArrays` opens with
83
+ * `getNextSibling(last)`. Unlinking there made every trailing insertion append at
84
+ * the end of the parent instead of before the marker.
85
+ *
86
+ * The leak this closes is about handlers, not links — GJS blocks JS callbacks
87
+ * during GC, so an undisconnected handler outlives its widget. The framework's own
88
+ * `removeNode` still does the unlinking, in its own order.
89
+ */
90
+ export declare function disconnectHandlers(el: HostElement): void;
32
91
  /**
33
92
  * Tear a subtree down: disconnect every handler, unparent, drop the reference.
34
93
  *
@@ -49,6 +108,48 @@ export declare function destroy(node: HostNode): void;
49
108
  * method name here would be the generic `add` that GTK4 deliberately removed.
50
109
  */
51
110
  export declare function mountRoot(el: HostElement, container: Gtk.Widget): void;
111
+ /**
112
+ * The materialised widget of a host node — for a `ref`, or for a test.
113
+ *
114
+ * ONE implementation. The Solid and React adapters each carried this function,
115
+ * identical apart from two comment lines, and Vue carried none — so a Vue app had
116
+ * no supported way to reach a widget at all and its only route was
117
+ * `materialize()`, which is exactly the call the `destroyed` guard below exists to
118
+ * intercept.
119
+ *
120
+ * The flag is exact and the obvious heuristic is not: `materialize` would happily
121
+ * build a fresh, propertyless, unparented widget for a destroyed node — `destroy`
122
+ * clears `props` and `layout` precisely so one cannot look re-materialisable — and
123
+ * "no widget, not attached, no props" also describes a brand-new element.
124
+ */
125
+ export declare function widgetOf(node: HostNode): Gtk.Widget;
126
+ /**
127
+ * An off-screen container the host owns — the analogue of the DOM's detached `<div>`.
128
+ *
129
+ * `<KeepAlive>` and `<Suspense>` ask a renderer for one: `KeepAliveImpl.setup`
130
+ * opens with `createElement("div")` and `SuspenseImpl` does the same for its
131
+ * `hiddenContainer`. Forwarded as a TAG, "div" reached `lookupWidget` and threw
132
+ * `unknown-tag` inside `callWithErrorHandling`, whose production arm is
133
+ * `console.error` with no rethrow: `mount()` returned normally, the container held
134
+ * zero children, GTK said nothing, exit 0.
135
+ *
136
+ * The Vue adapter answered that with `adopt(new Gtk.Box())` — the one runtime
137
+ * `gi://` import and the one concrete widget class in any adapter, i.e. the widget
138
+ * knowledge ADR 0027 § 7 forbids. Which widget backs it is the table's business
139
+ * (`makeDetachedContainer`), and this op is what leaves the adapter with nothing to
140
+ * know: the deactivated subtree is really unparented from the visible tree and
141
+ * really held alive, so reactivating it moves the SAME widgets back.
142
+ */
143
+ export declare const createDetachedContainer: () => HostElement;
144
+ /**
145
+ * Wrap a widget the application owns as a host element.
146
+ *
147
+ * The seam every framework adapter needs: a renderer mounts into a container it
148
+ * did not create. The descriptor comes from the SAME table as every other parent,
149
+ * through `nearestRegistered`, so an application's own `GObject.registerClass`
150
+ * subclass inherits its ancestor's placement rules instead of failing.
151
+ */
152
+ export declare function adopt(container: Gtk.Widget): HostElement;
52
153
  export declare const parentNode: (node: HostNode) => HostElement | null;
53
154
  export declare const firstChild: (el: HostElement) => HostNode | null;
54
155
  export declare const nextSibling: (node: HostNode) => HostNode | null;
@@ -1,8 +1,11 @@
1
1
  export * from './types.js';
2
2
  export { GtkHostError } from './errors.js';
3
- export { createAnchor, createElement, createText, clearContainer, destroy, firstChild, insert, isText, materialize, mountRoot, nextSibling, parentNode, prevSibling, remove, setElementText, setEventHandler, setProp, setSlot, setText, } from './host.js';
3
+ export { adopt, createAnchor, createDetachedContainer, createElement, createText, destroyChildren, destroy, disconnectHandlers, firstChild, insert, isHostElement, isText, materialize, mountRoot, nextSibling, parentNode, prevSibling, remove, setElementText, setEventHandler, setProp, setSlot, setText, widgetOf, } from './host.js';
4
4
  export { addressOf, reorderMode } from './policies.js';
5
5
  export { toSignalName, isEventProp } from './signals.js';
6
- export { constructOnlyNames, isConstructOnly, isWritable, paramSpecs, toPropertyName } from './props.js';
6
+ export { constructOnlyNames, isConstructOnly, isWritable, lookupEnumNick, paramSpecs, toPropertyName, } from './props.js';
7
+ export { assertInjective, tagOf } from './tags.js';
8
+ export { probeEnabled, runHostProbe, runHostProbeApp, type HostProbe, type HostProbeApp, type ProbeCheck, } from './probe.js';
9
+ export type { ElementChild, ElementChildren, JsxAttributes, RawSignalAttributes, SlotAttribute, VueAttributes, WithOnce, } from './attrs.js';
7
10
  export { clearRegistry, hasWidget, lookupWidget, nearestRegistered, registerWidget, registerWidgets, registeredTags, } from './registry.js';
8
- export { ADW_DESCRIPTORS, BUILTIN_DESCRIPTORS, GTK_DESCRIPTORS, registerBuiltinWidgets } from './descriptors/index.js';
11
+ export { ADW_DESCRIPTORS, BUILTIN_DESCRIPTORS, CURATED_DESCRIPTORS, GENERATED_PROVENANCE, GENERATED_WIDGETS, GTK_DESCRIPTORS, mergeGenerated, registerBuiltinWidgets, tableProvenance, } from './descriptors/index.js';