@gtkx/runtime 1.0.0-rc.3 → 1.0.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 (141) hide show
  1. package/README.md +8 -9
  2. package/dist/application-class.d.ts +32 -0
  3. package/dist/application-class.d.ts.map +1 -0
  4. package/dist/application-class.js +68 -0
  5. package/dist/application-class.js.map +1 -0
  6. package/dist/arg.d.ts +9 -3
  7. package/dist/arg.d.ts.map +1 -1
  8. package/dist/arg.js +4 -3
  9. package/dist/arg.js.map +1 -1
  10. package/dist/bind.d.ts +11 -0
  11. package/dist/bind.d.ts.map +1 -1
  12. package/dist/bind.js +11 -0
  13. package/dist/bind.js.map +1 -1
  14. package/dist/callback.d.ts +2 -1
  15. package/dist/callback.d.ts.map +1 -1
  16. package/dist/callback.js +119 -21
  17. package/dist/callback.js.map +1 -1
  18. package/dist/closure.d.ts +26 -0
  19. package/dist/closure.d.ts.map +1 -0
  20. package/dist/closure.js +99 -0
  21. package/dist/closure.js.map +1 -0
  22. package/dist/descriptors.d.ts +105 -6
  23. package/dist/descriptors.d.ts.map +1 -1
  24. package/dist/descriptors.js +56 -12
  25. package/dist/descriptors.js.map +1 -1
  26. package/dist/error.d.ts +7 -0
  27. package/dist/error.d.ts.map +1 -1
  28. package/dist/error.js +1 -1
  29. package/dist/error.js.map +1 -1
  30. package/dist/fn.d.ts +24 -3
  31. package/dist/fn.d.ts.map +1 -1
  32. package/dist/fn.js +70 -33
  33. package/dist/fn.js.map +1 -1
  34. package/dist/index.d.ts +29 -7
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +26 -4
  37. package/dist/index.js.map +1 -1
  38. package/dist/internal.d.ts +1 -0
  39. package/dist/internal.d.ts.map +1 -1
  40. package/dist/internal.js +1 -0
  41. package/dist/internal.js.map +1 -1
  42. package/dist/library.d.ts +2 -1
  43. package/dist/library.d.ts.map +1 -1
  44. package/dist/library.js +2 -1
  45. package/dist/library.js.map +1 -1
  46. package/dist/lifecycle.d.ts +52 -10
  47. package/dist/lifecycle.d.ts.map +1 -1
  48. package/dist/lifecycle.js +62 -18
  49. package/dist/lifecycle.js.map +1 -1
  50. package/dist/listeners.d.ts +6 -0
  51. package/dist/listeners.d.ts.map +1 -1
  52. package/dist/listeners.js +3 -1
  53. package/dist/listeners.js.map +1 -1
  54. package/dist/mixin.d.ts +16 -2
  55. package/dist/mixin.d.ts.map +1 -1
  56. package/dist/mixin.js +18 -6
  57. package/dist/mixin.js.map +1 -1
  58. package/dist/native-value.d.ts +8 -0
  59. package/dist/native-value.d.ts.map +1 -1
  60. package/dist/native-value.js +12 -3
  61. package/dist/native-value.js.map +1 -1
  62. package/dist/object.d.ts +34 -6
  63. package/dist/object.d.ts.map +1 -1
  64. package/dist/object.js +89 -25
  65. package/dist/object.js.map +1 -1
  66. package/dist/param-spec.d.ts +11 -0
  67. package/dist/param-spec.d.ts.map +1 -0
  68. package/dist/param-spec.js +149 -0
  69. package/dist/param-spec.js.map +1 -0
  70. package/dist/promisify.d.ts +3 -5
  71. package/dist/promisify.d.ts.map +1 -1
  72. package/dist/promisify.js +4 -3
  73. package/dist/promisify.js.map +1 -1
  74. package/dist/properties.d.ts +39 -7
  75. package/dist/properties.d.ts.map +1 -1
  76. package/dist/properties.js +305 -42
  77. package/dist/properties.js.map +1 -1
  78. package/dist/register-class.d.ts +119 -14
  79. package/dist/register-class.d.ts.map +1 -1
  80. package/dist/register-class.js +109 -80
  81. package/dist/register-class.js.map +1 -1
  82. package/dist/registry.d.ts +81 -17
  83. package/dist/registry.d.ts.map +1 -1
  84. package/dist/registry.js +72 -26
  85. package/dist/registry.js.map +1 -1
  86. package/dist/signal.d.ts +22 -3
  87. package/dist/signal.d.ts.map +1 -1
  88. package/dist/signal.js +64 -8
  89. package/dist/signal.js.map +1 -1
  90. package/dist/t.d.ts +44 -0
  91. package/dist/t.d.ts.map +1 -1
  92. package/dist/t.js.map +1 -1
  93. package/dist/type.d.ts +2 -2
  94. package/dist/type.d.ts.map +1 -1
  95. package/dist/type.js +2 -0
  96. package/dist/type.js.map +1 -1
  97. package/dist/value.d.ts +10 -10
  98. package/dist/value.d.ts.map +1 -1
  99. package/dist/value.js +44 -22
  100. package/dist/value.js.map +1 -1
  101. package/dist/vfunc-call.d.ts +37 -0
  102. package/dist/vfunc-call.d.ts.map +1 -0
  103. package/dist/vfunc-call.js +168 -0
  104. package/dist/vfunc-call.js.map +1 -0
  105. package/dist/vfunc-seeds.d.ts +12 -0
  106. package/dist/vfunc-seeds.d.ts.map +1 -0
  107. package/dist/vfunc-seeds.js +19 -0
  108. package/dist/vfunc-seeds.js.map +1 -0
  109. package/dist/vfunc.d.ts +8 -0
  110. package/dist/vfunc.d.ts.map +1 -0
  111. package/dist/vfunc.js +23 -0
  112. package/dist/vfunc.js.map +1 -0
  113. package/package.json +4 -4
  114. package/src/application-class.ts +116 -0
  115. package/src/arg.ts +11 -5
  116. package/src/bind.ts +11 -0
  117. package/src/callback.ts +163 -23
  118. package/src/closure.ts +136 -0
  119. package/src/descriptors.ts +137 -41
  120. package/src/error.ts +8 -1
  121. package/src/fn.ts +104 -39
  122. package/src/index.ts +36 -7
  123. package/src/internal.ts +1 -0
  124. package/src/library.ts +2 -1
  125. package/src/lifecycle.ts +99 -22
  126. package/src/listeners.ts +9 -1
  127. package/src/mixin.ts +27 -8
  128. package/src/native-value.ts +13 -3
  129. package/src/object.ts +128 -34
  130. package/src/param-spec.ts +235 -0
  131. package/src/promisify.ts +8 -8
  132. package/src/properties.ts +459 -45
  133. package/src/register-class.ts +329 -114
  134. package/src/registry.ts +150 -38
  135. package/src/signal.ts +101 -17
  136. package/src/t.ts +44 -0
  137. package/src/type.ts +3 -2
  138. package/src/value.ts +57 -19
  139. package/src/vfunc-call.ts +243 -0
  140. package/src/vfunc-seeds.ts +36 -0
  141. package/src/vfunc.ts +31 -0
package/dist/library.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { boxedT, fundamentalT } from "./descriptors.js";
2
2
  const LIB = "libgobject-2.0.so.0,libglib-2.0.so.0";
3
3
  const VALUE_SIZE = 24;
4
+ const CLOSURE_SIZE = 32;
4
5
  const VALUE_T = boxedT("GValue", { sharedLibrary: LIB, getTypeFnName: "g_value_get_type" });
5
6
  const PARAM_T = fundamentalT(LIB, "g_param_spec_ref", "g_param_spec_unref", {
6
7
  ownership: "borrowed",
@@ -10,5 +11,5 @@ const VARIANT_T = fundamentalT(LIB, "g_variant_ref", "g_variant_unref", {
10
11
  ownership: "borrowed",
11
12
  typeName: "GVariant",
12
13
  });
13
- export { LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };
14
+ export { CLOSURE_SIZE, LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };
14
15
  //# sourceMappingURL=library.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"library.js","sourceRoot":"","sources":["../src/library.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,MAAM,EAA8B,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE1G,MAAM,GAAG,GAAG,sCAAsC,CAAC;AACnD,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,OAAO,GAAoB,MAAM,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAC,CAAC;AAE7G,MAAM,OAAO,GAA0B,YAAY,CAAC,GAAG,EAAE,kBAAkB,EAAE,oBAAoB,EAAE;IAC/F,SAAS,EAAE,UAAU;IACrB,QAAQ,EAAE,QAAQ;CACrB,CAAC,CAAC;AAEH,MAAM,SAAS,GAA0B,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,iBAAiB,EAAE;IAC3F,SAAS,EAAE,UAAU;IACrB,QAAQ,EAAE,UAAU;CACvB,CAAC,CAAC;AAEH,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC","sourcesContent":["import { type BoxedDescriptor, boxedT, type FundamentalDescriptor, fundamentalT } from \"./descriptors.js\";\n\nconst LIB = \"libgobject-2.0.so.0,libglib-2.0.so.0\";\nconst VALUE_SIZE = 24;\nconst VALUE_T: BoxedDescriptor = boxedT(\"GValue\", { sharedLibrary: LIB, getTypeFnName: \"g_value_get_type\" });\n\nconst PARAM_T: FundamentalDescriptor = fundamentalT(LIB, \"g_param_spec_ref\", \"g_param_spec_unref\", {\n ownership: \"borrowed\",\n typeName: \"GParam\",\n});\n\nconst VARIANT_T: FundamentalDescriptor = fundamentalT(LIB, \"g_variant_ref\", \"g_variant_unref\", {\n ownership: \"borrowed\",\n typeName: \"GVariant\",\n});\n\nexport { LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };\n"]}
1
+ {"version":3,"file":"library.js","sourceRoot":"","sources":["../src/library.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,MAAM,EAA8B,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE1G,MAAM,GAAG,GAAG,sCAAsC,CAAC;AACnD,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,OAAO,GAAoB,MAAM,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAC,CAAC;AAE7G,MAAM,OAAO,GAA0B,YAAY,CAAC,GAAG,EAAE,kBAAkB,EAAE,oBAAoB,EAAE;IAC/F,SAAS,EAAE,UAAU;IACrB,QAAQ,EAAE,QAAQ;CACrB,CAAC,CAAC;AAEH,MAAM,SAAS,GAA0B,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,iBAAiB,EAAE;IAC3F,SAAS,EAAE,UAAU;IACrB,QAAQ,EAAE,UAAU;CACvB,CAAC,CAAC;AAEH,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC","sourcesContent":["import { type BoxedDescriptor, boxedT, type FundamentalDescriptor, fundamentalT } from \"./descriptors.js\";\n\nconst LIB = \"libgobject-2.0.so.0,libglib-2.0.so.0\";\nconst VALUE_SIZE = 24;\nconst CLOSURE_SIZE = 32;\nconst VALUE_T: BoxedDescriptor = boxedT(\"GValue\", { sharedLibrary: LIB, getTypeFnName: \"g_value_get_type\" });\n\nconst PARAM_T: FundamentalDescriptor = fundamentalT(LIB, \"g_param_spec_ref\", \"g_param_spec_unref\", {\n ownership: \"borrowed\",\n typeName: \"GParam\",\n});\n\nconst VARIANT_T: FundamentalDescriptor = fundamentalT(LIB, \"g_variant_ref\", \"g_variant_unref\", {\n ownership: \"borrowed\",\n typeName: \"GVariant\",\n});\n\nexport { CLOSURE_SIZE, LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };\n"]}
@@ -1,16 +1,37 @@
1
1
  /**
2
- * Minimal structural interface of a GTK4/GIO application needed to register, run,
3
- * and shut it down, plus the signals its lifecycle helpers connect to.
2
+ * The GIO and GTK application surface {@link runApplication} and {@link quitApplication} drive, so any
3
+ * application subclass satisfies it structurally; the window members only `Gtk.Application` defines are
4
+ * optional.
4
5
  */
5
6
  type ApplicationLike = {
7
+ /** Returns whether the application has already been registered. */
6
8
  getIsRegistered(): boolean;
9
+ /** Returns whether another process owns the application ID, making this one a remote instance. */
10
+ getIsRemote?(): boolean;
11
+ /** Registers the application with the session, returning whether it succeeded. */
7
12
  register(cancellable: null): boolean;
13
+ /** Emits `activate`, bringing up the application's initial user interface. */
8
14
  activate(): void;
9
- quit(): void;
15
+ /**
16
+ * Runs GLib's own `g_application_run`, whose tail emits `shutdown`, destroys the application
17
+ * implementation and clears the registration.
18
+ */
10
19
  run(argv: string[]): number;
20
+ /** Returns the windows currently attached to the application. */
11
21
  getWindows?(): object[];
22
+ /** Detaches a window, so it no longer holds the application open. */
12
23
  removeWindow?(window: object): void;
24
+ /** Connects a handler to the application's `activate` or `shutdown` signal. */
13
25
  on(signal: "activate" | "shutdown", handler: () => void): unknown;
26
+ /** Emits one of the application's own signals. */
27
+ emit(signal: "shutdown"): unknown;
28
+ };
29
+ /** What {@link runApplication} reports about the process it just started. */
30
+ type RunApplicationResult = {
31
+ /** Whether this process owns the application ID and may build a user interface. */
32
+ isPrimary: boolean;
33
+ /** The status GLib determined for the command line, which the process should exit with. */
34
+ exitStatus: number;
14
35
  };
15
36
  /**
16
37
  * Runs every registered exit callback and shuts down the native runtime. Safe to
@@ -25,18 +46,39 @@ declare const quit: () => void;
25
46
  */
26
47
  declare const onExit: (callback: () => void) => void;
27
48
  /**
28
- * Registers the application if needed and activates it, keeping the runtime alive
29
- * while it is active and releasing it on shutdown.
49
+ * Hands `argv` to GLib's own command line handling, which parses the application's options, prints
50
+ * `--help`, runs `handle-local-options`, registers the application, and either activates it or
51
+ * forwards the command line to the process that already owns the application ID. The runtime is
52
+ * held alive while the application is active and released on shutdown.
53
+ *
54
+ * `g_application_run()` is not what starts the application, because it would drive its own main loop
55
+ * and freeze Node; only the local command line handling it delegates to runs here. {@link
56
+ * quitApplication} calls it once no window holds the application open, to reach the teardown only it
57
+ * performs.
58
+ *
59
+ * GLib parses a given application's command line at most once, so starting an application that has
60
+ * already run registers and activates it instead of reading `argv` again.
30
61
  *
31
- * @param application The application to register and activate.
62
+ * When another process already owns the application ID, this one registers as a remote instance: no
63
+ * user interface may be built here, because a remote application has no `GtkApplicationImpl` and
64
+ * attaching a window to it crashes.
65
+ *
66
+ * @param application The application to start.
67
+ * @param argv The command line, whose first entry names the program as `--help` should print it.
68
+ * @returns Whether this process may build a user interface, and the status to exit with.
32
69
  */
33
- declare const runApplication: (application: ApplicationLike) => void;
70
+ declare const runApplication: (application: ApplicationLike, argv: string[]) => RunApplicationResult;
34
71
  /**
35
- * Closes all of the application's windows and drives its main loop until it quits,
36
- * so pending shutdown work can complete before the process exits.
72
+ * Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,
73
+ * destroys the application implementation and releases the D-Bus registration. Does nothing for an
74
+ * application that is not registered, so a repeated call is a no-op.
75
+ *
76
+ * GLib's own shutdown is reachable once per application: reaching it marks the application as
77
+ * quitting, which GLib never undoes. An application that has already quit falls back to emitting
78
+ * `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.
37
79
  *
38
80
  * @param application The application to shut down.
39
81
  */
40
82
  declare const quitApplication: (application: ApplicationLike) => void;
41
- export { onExit, quit, runApplication, quitApplication, type ApplicationLike };
83
+ export { onExit, quit, runApplication, quitApplication, type ApplicationLike, type RunApplicationResult };
42
84
  //# sourceMappingURL=lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,KAAK,eAAe,GAAG;IACnB,eAAe,IAAI,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC;IACrC,QAAQ,IAAI,IAAI,CAAC;IACjB,IAAI,IAAI,IAAI,CAAC;IACb,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC5B,UAAU,CAAC,IAAI,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACrE,CAAC;AAGF;;;GAGG;AACH,QAAA,MAAM,IAAI,EAAE,MAAM,IAAmB,CAAC;AAoBtC;;;;;GAKG;AACH,QAAA,MAAM,MAAM,GAAI,UAAU,MAAM,IAAI,KAAG,IAEtC,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,cAAc,GAAI,aAAa,eAAe,KAAG,IActD,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,eAAe,GAAI,aAAa,eAAe,KAAG,IAiBvD,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,KAAK,eAAe,GAAG;IACnB,mEAAmE;IACnE,eAAe,IAAI,OAAO,CAAC;IAC3B,kGAAkG;IAClG,WAAW,CAAC,IAAI,OAAO,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC;IACrC,8EAA8E;IAC9E,QAAQ,IAAI,IAAI,CAAC;IACjB;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC5B,iEAAiE;IACjE,UAAU,CAAC,IAAI,MAAM,EAAE,CAAC;IACxB,qEAAqE;IACrE,YAAY,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,+EAA+E;IAC/E,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;IAClE,kDAAkD;IAClD,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC;CACrC,CAAC;AAEF,6EAA6E;AAC7E,KAAK,oBAAoB,GAAG;IACxB,mFAAmF;IACnF,SAAS,EAAE,OAAO,CAAC;IACnB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;CACtB,CAAC;AAKF;;;GAGG;AACH,QAAA,MAAM,IAAI,EAAE,MAAM,IAAmB,CAAC;AAoBtC;;;;;GAKG;AACH,QAAA,MAAM,MAAM,GAAI,UAAU,MAAM,IAAI,KAAG,IAEtC,CAAC;AA4BF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,cAAc,GAAI,aAAa,eAAe,EAAE,MAAM,MAAM,EAAE,KAAG,oBAoBtE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,QAAA,MAAM,eAAe,GAAI,aAAa,eAAe,KAAG,IAiBvD,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,oBAAoB,EAAE,CAAC"}
package/dist/lifecycle.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { keepAlive, quit as nativeQuit } from "@gtkx/native";
2
- import { blockMatchedSignalHandlers } from "./signal.js";
2
+ import { isDerivedApplication, shutDownThroughRun } from "./application-class.js";
3
3
  const shutdownCallbacks = [];
4
+ const startedApplications = new WeakSet();
5
+ const shutDownApplications = new WeakSet();
4
6
  /**
5
7
  * Runs every registered exit callback and shuts down the native runtime. Safe to
6
8
  * call more than once; only the first call takes effect.
@@ -28,43 +30,85 @@ function createQuit() {
28
30
  const onExit = (callback) => {
29
31
  shutdownCallbacks.push(callback);
30
32
  };
31
- /**
32
- * Registers the application if needed and activates it, keeping the runtime alive
33
- * while it is active and releasing it on shutdown.
34
- *
35
- * @param application The application to register and activate.
36
- */
37
- const runApplication = (application) => {
33
+ const startApplication = (application, argv) => {
34
+ startedApplications.add(application);
38
35
  application.on("activate", () => {
39
36
  keepAlive(true);
40
37
  });
41
38
  application.on("shutdown", () => {
42
39
  keepAlive(false);
43
40
  });
44
- if (!application.getIsRegistered()) {
45
- application.register(null);
41
+ return application.runLocalCommandLine(argv)[2];
42
+ };
43
+ const restartApplication = (application) => {
44
+ shutDownApplications.delete(application);
45
+ if (!application.register(null)) {
46
+ return 1;
46
47
  }
47
48
  application.activate();
49
+ return 0;
50
+ };
51
+ /**
52
+ * Hands `argv` to GLib's own command line handling, which parses the application's options, prints
53
+ * `--help`, runs `handle-local-options`, registers the application, and either activates it or
54
+ * forwards the command line to the process that already owns the application ID. The runtime is
55
+ * held alive while the application is active and released on shutdown.
56
+ *
57
+ * `g_application_run()` is not what starts the application, because it would drive its own main loop
58
+ * and freeze Node; only the local command line handling it delegates to runs here. {@link
59
+ * quitApplication} calls it once no window holds the application open, to reach the teardown only it
60
+ * performs.
61
+ *
62
+ * GLib parses a given application's command line at most once, so starting an application that has
63
+ * already run registers and activates it instead of reading `argv` again.
64
+ *
65
+ * When another process already owns the application ID, this one registers as a remote instance: no
66
+ * user interface may be built here, because a remote application has no `GtkApplicationImpl` and
67
+ * attaching a window to it crashes.
68
+ *
69
+ * @param application The application to start.
70
+ * @param argv The command line, whose first entry names the program as `--help` should print it.
71
+ * @returns Whether this process may build a user interface, and the status to exit with.
72
+ */
73
+ const runApplication = (application, argv) => {
74
+ if (!isDerivedApplication(application)) {
75
+ throw new Error("runApplication: this application was not built by GTKX, so its command line cannot be " +
76
+ "parsed and it cannot be shut down safely; render <GtkApplication> or <AdwApplication>, " +
77
+ "or construct it with createApplication from @gtkx/runtime");
78
+ }
79
+ const exitStatus = startedApplications.has(application)
80
+ ? restartApplication(application)
81
+ : startApplication(application, argv);
82
+ const isPrimary = application.getIsRegistered() && application.getIsRemote?.() !== true;
83
+ if (isPrimary) {
84
+ keepAlive(true);
85
+ }
86
+ return { isPrimary, exitStatus };
48
87
  };
49
88
  /**
50
- * Closes all of the application's windows and drives its main loop until it quits,
51
- * so pending shutdown work can complete before the process exits.
89
+ * Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,
90
+ * destroys the application implementation and releases the D-Bus registration. Does nothing for an
91
+ * application that is not registered, so a repeated call is a no-op.
92
+ *
93
+ * GLib's own shutdown is reachable once per application: reaching it marks the application as
94
+ * quitting, which GLib never undoes. An application that has already quit falls back to emitting
95
+ * `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.
52
96
  *
53
97
  * @param application The application to shut down.
54
98
  */
55
99
  const quitApplication = (application) => {
56
- if (!application.getIsRegistered()) {
100
+ if (!application.getIsRegistered() || shutDownApplications.has(application)) {
57
101
  return;
58
102
  }
59
103
  const windows = application.getWindows?.() ?? [];
60
104
  for (const window of windows) {
61
105
  application.removeWindow?.(window);
62
106
  }
63
- application.on("shutdown", () => {
64
- application.quit();
65
- });
66
- blockMatchedSignalHandlers(application, "activate");
67
- application.run([]);
107
+ shutDownThroughRun(application);
108
+ if (application.getIsRegistered()) {
109
+ shutDownApplications.add(application);
110
+ application.emit("shutdown");
111
+ }
68
112
  };
69
113
  export { onExit, quit, runApplication, quitApplication };
70
114
  //# sourceMappingURL=lifecycle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAiBzD,MAAM,iBAAiB,GAAmB,EAAE,CAAC;AAC7C;;;GAGG;AACH,MAAM,IAAI,GAAe,UAAU,EAAE,CAAC;AAEtC,SAAS,UAAU;IACf,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,OAAO,GAAG,EAAE;QACR,IAAI,OAAO,EAAE,CAAC;YACV,OAAO;QACX,CAAC;QAED,OAAO,GAAG,IAAI,CAAC;QAEf,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;YACvC,QAAQ,EAAE,CAAC;QACf,CAAC;QAED,UAAU,EAAE,CAAC;IACjB,CAAC,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,QAAoB,EAAQ,EAAE;IAC1C,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,cAAc,GAAG,CAAC,WAA4B,EAAQ,EAAE;IAC1D,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE;QAC5B,SAAS,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE;QAC5B,SAAS,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,EAAE,CAAC;QACjC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,WAAW,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,eAAe,GAAG,CAAC,WAA4B,EAAQ,EAAE;IAC3D,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,EAAE,CAAC;QACjC,OAAO;IACX,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC;IAEjD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC3B,WAAW,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE;QAC5B,WAAW,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,0BAA0B,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpD,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,eAAe,EAAwB,CAAC","sourcesContent":["import { keepAlive, quit as nativeQuit } from \"@gtkx/native\";\nimport { blockMatchedSignalHandlers } from \"./signal.js\";\n\n/**\n * Minimal structural interface of a GTK4/GIO application needed to register, run,\n * and shut it down, plus the signals its lifecycle helpers connect to.\n */\ntype ApplicationLike = {\n getIsRegistered(): boolean;\n register(cancellable: null): boolean;\n activate(): void;\n quit(): void;\n run(argv: string[]): number;\n getWindows?(): object[];\n removeWindow?(window: object): void;\n on(signal: \"activate\" | \"shutdown\", handler: () => void): unknown;\n};\n\nconst shutdownCallbacks: (() => void)[] = [];\n/**\n * Runs every registered exit callback and shuts down the native runtime. Safe to\n * call more than once; only the first call takes effect.\n */\nconst quit: () => void = createQuit();\n\nfunction createQuit(): () => void {\n let hasQuit = false;\n\n return () => {\n if (hasQuit) {\n return;\n }\n\n hasQuit = true;\n\n for (const callback of shutdownCallbacks) {\n callback();\n }\n\n nativeQuit();\n };\n}\n\n/**\n * Registers a callback to run once when the process quits, before the native\n * runtime is torn down.\n *\n * @param callback Invoked during shutdown.\n */\nconst onExit = (callback: () => void): void => {\n shutdownCallbacks.push(callback);\n};\n\n/**\n * Registers the application if needed and activates it, keeping the runtime alive\n * while it is active and releasing it on shutdown.\n *\n * @param application The application to register and activate.\n */\nconst runApplication = (application: ApplicationLike): void => {\n application.on(\"activate\", () => {\n keepAlive(true);\n });\n\n application.on(\"shutdown\", () => {\n keepAlive(false);\n });\n\n if (!application.getIsRegistered()) {\n application.register(null);\n }\n\n application.activate();\n};\n\n/**\n * Closes all of the application's windows and drives its main loop until it quits,\n * so pending shutdown work can complete before the process exits.\n *\n * @param application The application to shut down.\n */\nconst quitApplication = (application: ApplicationLike): void => {\n if (!application.getIsRegistered()) {\n return;\n }\n\n const windows = application.getWindows?.() ?? [];\n\n for (const window of windows) {\n application.removeWindow?.(window);\n }\n\n application.on(\"shutdown\", () => {\n application.quit();\n });\n\n blockMatchedSignalHandlers(application, \"activate\");\n application.run([]);\n};\n\nexport { onExit, quit, runApplication, quitApplication, type ApplicationLike };\n"]}
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { keepAlive, quit as nativeQuit } from \"@gtkx/native\";\nimport { isDerivedApplication, type LocalCommandLineApplication, shutDownThroughRun } from \"./application-class.js\";\n\n/**\n * The GIO and GTK application surface {@link runApplication} and {@link quitApplication} drive, so any\n * application subclass satisfies it structurally; the window members only `Gtk.Application` defines are\n * optional.\n */\ntype ApplicationLike = {\n /** Returns whether the application has already been registered. */\n getIsRegistered(): boolean;\n /** Returns whether another process owns the application ID, making this one a remote instance. */\n getIsRemote?(): boolean;\n /** Registers the application with the session, returning whether it succeeded. */\n register(cancellable: null): boolean;\n /** Emits `activate`, bringing up the application's initial user interface. */\n activate(): void;\n /**\n * Runs GLib's own `g_application_run`, whose tail emits `shutdown`, destroys the application\n * implementation and clears the registration.\n */\n run(argv: string[]): number;\n /** Returns the windows currently attached to the application. */\n getWindows?(): object[];\n /** Detaches a window, so it no longer holds the application open. */\n removeWindow?(window: object): void;\n /** Connects a handler to the application's `activate` or `shutdown` signal. */\n on(signal: \"activate\" | \"shutdown\", handler: () => void): unknown;\n /** Emits one of the application's own signals. */\n emit(signal: \"shutdown\"): unknown;\n};\n\n/** What {@link runApplication} reports about the process it just started. */\ntype RunApplicationResult = {\n /** Whether this process owns the application ID and may build a user interface. */\n isPrimary: boolean;\n /** The status GLib determined for the command line, which the process should exit with. */\n exitStatus: number;\n};\n\nconst shutdownCallbacks: (() => void)[] = [];\nconst startedApplications: WeakSet<object> = new WeakSet();\nconst shutDownApplications: WeakSet<object> = new WeakSet();\n/**\n * Runs every registered exit callback and shuts down the native runtime. Safe to\n * call more than once; only the first call takes effect.\n */\nconst quit: () => void = createQuit();\n\nfunction createQuit(): () => void {\n let hasQuit = false;\n\n return () => {\n if (hasQuit) {\n return;\n }\n\n hasQuit = true;\n\n for (const callback of shutdownCallbacks) {\n callback();\n }\n\n nativeQuit();\n };\n}\n\n/**\n * Registers a callback to run once when the process quits, before the native\n * runtime is torn down.\n *\n * @param callback Invoked during shutdown.\n */\nconst onExit = (callback: () => void): void => {\n shutdownCallbacks.push(callback);\n};\n\nconst startApplication = (application: ApplicationLike & LocalCommandLineApplication, argv: string[]): number => {\n startedApplications.add(application);\n\n application.on(\"activate\", () => {\n keepAlive(true);\n });\n\n application.on(\"shutdown\", () => {\n keepAlive(false);\n });\n\n return application.runLocalCommandLine(argv)[2];\n};\n\nconst restartApplication = (application: ApplicationLike): number => {\n shutDownApplications.delete(application);\n\n if (!application.register(null)) {\n return 1;\n }\n\n application.activate();\n\n return 0;\n};\n\n/**\n * Hands `argv` to GLib's own command line handling, which parses the application's options, prints\n * `--help`, runs `handle-local-options`, registers the application, and either activates it or\n * forwards the command line to the process that already owns the application ID. The runtime is\n * held alive while the application is active and released on shutdown.\n *\n * `g_application_run()` is not what starts the application, because it would drive its own main loop\n * and freeze Node; only the local command line handling it delegates to runs here. {@link\n * quitApplication} calls it once no window holds the application open, to reach the teardown only it\n * performs.\n *\n * GLib parses a given application's command line at most once, so starting an application that has\n * already run registers and activates it instead of reading `argv` again.\n *\n * When another process already owns the application ID, this one registers as a remote instance: no\n * user interface may be built here, because a remote application has no `GtkApplicationImpl` and\n * attaching a window to it crashes.\n *\n * @param application The application to start.\n * @param argv The command line, whose first entry names the program as `--help` should print it.\n * @returns Whether this process may build a user interface, and the status to exit with.\n */\nconst runApplication = (application: ApplicationLike, argv: string[]): RunApplicationResult => {\n if (!isDerivedApplication(application)) {\n throw new Error(\n \"runApplication: this application was not built by GTKX, so its command line cannot be \" +\n \"parsed and it cannot be shut down safely; render <GtkApplication> or <AdwApplication>, \" +\n \"or construct it with createApplication from @gtkx/runtime\",\n );\n }\n\n const exitStatus = startedApplications.has(application)\n ? restartApplication(application)\n : startApplication(application, argv);\n\n const isPrimary = application.getIsRegistered() && application.getIsRemote?.() !== true;\n\n if (isPrimary) {\n keepAlive(true);\n }\n\n return { isPrimary, exitStatus };\n};\n\n/**\n * Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,\n * destroys the application implementation and releases the D-Bus registration. Does nothing for an\n * application that is not registered, so a repeated call is a no-op.\n *\n * GLib's own shutdown is reachable once per application: reaching it marks the application as\n * quitting, which GLib never undoes. An application that has already quit falls back to emitting\n * `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.\n *\n * @param application The application to shut down.\n */\nconst quitApplication = (application: ApplicationLike): void => {\n if (!application.getIsRegistered() || shutDownApplications.has(application)) {\n return;\n }\n\n const windows = application.getWindows?.() ?? [];\n\n for (const window of windows) {\n application.removeWindow?.(window);\n }\n\n shutDownThroughRun(application);\n\n if (application.getIsRegistered()) {\n shutDownApplications.add(application);\n application.emit(\"shutdown\");\n }\n};\n\nexport { onExit, quit, runApplication, quitApplication, type ApplicationLike, type RunApplicationResult };\n"]}
@@ -1,6 +1,12 @@
1
1
  import { type SignalHandler } from "./signal.js";
2
+ /**
3
+ * The connect and disconnect surface {@link onSignal}, {@link onceSignal} and {@link offSignal}
4
+ * drive on an emitter.
5
+ */
2
6
  type SignalConnectable = {
7
+ /** Connects a handler to a signal and returns its handler id. */
3
8
  connect(signal: string, handler: SignalHandler, isAfter?: boolean): number;
9
+ /** Disconnects the handler previously connected under the given id. */
4
10
  disconnect(handlerId: number): void;
5
11
  };
6
12
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"listeners.d.ts","sourceRoot":"","sources":["../src/listeners.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE3E,KAAK,iBAAiB,GAAG;IACrB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3E,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC;AA4CF;;;;;;;;GAQG;AACH,iBAAS,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAG9G;AAED;;;;;;;;GAQG;AACH,iBAAS,UAAU,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAahH;AAED;;;;;;;GAOG;AACH,iBAAS,SAAS,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAc5F;AAED,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"listeners.d.ts","sourceRoot":"","sources":["../src/listeners.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAqB,MAAM,aAAa,CAAC;AAE9F;;;GAGG;AACH,KAAK,iBAAiB,GAAG;IACrB,iEAAiE;IACjE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3E,uEAAuE;IACvE,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC;AA4CF;;;;;;;;GAQG;AACH,iBAAS,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAG9G;AAED;;;;;;;;GAQG;AACH,iBAAS,UAAU,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAchH;AAED;;;;;;;GAOG;AACH,iBAAS,SAAS,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAe5F;AAED,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC"}
package/dist/listeners.js CHANGED
@@ -1,4 +1,4 @@
1
- import { isSignalHandlerConnected } from "./signal.js";
1
+ import { isSignalHandlerConnected, untrackConnection } from "./signal.js";
2
2
  const listenerTable = new WeakMap();
3
3
  const findListenerHandlerId = (instance, signal, handler) => listenerTable.get(instance)?.get(signal)?.get(handler);
4
4
  const trackListener = (instance, signal, handler, handlerId) => {
@@ -55,6 +55,7 @@ function onceSignal(instance, signal, handler, isAfter) {
55
55
  let handlerId = 0;
56
56
  const wrapped = (...args) => {
57
57
  untrackHandlerId(instance, signal, handlerId);
58
+ untrackConnection(instance, handlerId);
58
59
  instance.disconnect(handlerId);
59
60
  return handler(...args);
60
61
  };
@@ -76,6 +77,7 @@ function offSignal(instance, signal, handler) {
76
77
  return;
77
78
  }
78
79
  untrackHandlerId(instance, signal, handlerId);
80
+ untrackConnection(instance, handlerId);
79
81
  if (!isSignalHandlerConnected(instance, handlerId)) {
80
82
  return;
81
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"listeners.js","sourceRoot":"","sources":["../src/listeners.ts"],"names":[],"mappings":"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","sourcesContent":["import { isSignalHandlerConnected, type SignalHandler } from \"./signal.js\";\n\ntype SignalConnectable = {\n connect(signal: string, handler: SignalHandler, isAfter?: boolean): number;\n disconnect(handlerId: number): void;\n};\n\nconst listenerTable: WeakMap<object, Map<string, Map<SignalHandler, number>>> = new WeakMap();\n\nconst findListenerHandlerId = (instance: object, signal: string, handler: SignalHandler): number | undefined =>\n listenerTable.get(instance)?.get(signal)?.get(handler);\n\nconst trackListener = (instance: object, signal: string, handler: SignalHandler, handlerId: number): void => {\n let bySignal = listenerTable.get(instance);\n\n if (!bySignal) {\n bySignal = new Map();\n listenerTable.set(instance, bySignal);\n }\n\n let byHandler = bySignal.get(signal);\n\n if (!byHandler) {\n byHandler = new Map();\n bySignal.set(signal, byHandler);\n }\n\n byHandler.set(handler, handlerId);\n};\n\nconst untrackHandlerId = (instance: object, signal: string, handlerId: number): void => {\n const bySignal = listenerTable.get(instance);\n const byHandler = bySignal?.get(signal);\n\n if (byHandler === undefined) {\n return;\n }\n\n for (const [handler, id] of byHandler) {\n if (id === handlerId) {\n byHandler.delete(handler);\n }\n }\n\n if (byHandler.size === 0) {\n bySignal?.delete(signal);\n }\n};\n\n/**\n * Connects a handler to a signal and tracks it so it can later be removed with\n * {@link offSignal}.\n *\n * @param instance The object emitting the signal.\n * @param signal The signal name to connect to.\n * @param handler The callback invoked on each emission.\n * @param isAfter When true, run the handler after the default handler.\n */\nfunction onSignal(instance: SignalConnectable, signal: string, handler: SignalHandler, isAfter?: boolean): void {\n const handlerId = instance.connect(signal, handler, isAfter);\n trackListener(instance, signal, handler, handlerId);\n}\n\n/**\n * Connects a handler that runs at most once, disconnecting itself after the first\n * emission.\n *\n * @param instance The object emitting the signal.\n * @param signal The signal name to connect to.\n * @param handler The callback invoked on the first emission.\n * @param isAfter When true, run the handler after the default handler.\n */\nfunction onceSignal(instance: SignalConnectable, signal: string, handler: SignalHandler, isAfter?: boolean): void {\n let handlerId = 0;\n\n const wrapped: SignalHandler = (...args) => {\n untrackHandlerId(instance, signal, handlerId);\n instance.disconnect(handlerId);\n\n return handler(...args);\n };\n\n handlerId = instance.connect(signal, wrapped, isAfter);\n trackListener(instance, signal, wrapped, handlerId);\n trackListener(instance, signal, handler, handlerId);\n}\n\n/**\n * Disconnects a handler previously connected with {@link onSignal} or\n * {@link onceSignal}. Does nothing if the handler is not currently connected.\n *\n * @param instance The object the handler was connected to.\n * @param signal The signal name the handler was connected to.\n * @param handler The handler to disconnect.\n */\nfunction offSignal(instance: SignalConnectable, signal: string, handler: SignalHandler): void {\n const handlerId = findListenerHandlerId(instance, signal, handler);\n\n if (handlerId === undefined) {\n return;\n }\n\n untrackHandlerId(instance, signal, handlerId);\n\n if (!isSignalHandlerConnected(instance, handlerId)) {\n return;\n }\n\n instance.disconnect(handlerId);\n}\n\nexport { onSignal, onceSignal, offSignal };\n"]}
1
+ {"version":3,"file":"listeners.js","sourceRoot":"","sources":["../src/listeners.ts"],"names":[],"mappings":"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","sourcesContent":["import { isSignalHandlerConnected, type SignalHandler, untrackConnection } from \"./signal.js\";\n\n/**\n * The connect and disconnect surface {@link onSignal}, {@link onceSignal} and {@link offSignal}\n * drive on an emitter.\n */\ntype SignalConnectable = {\n /** Connects a handler to a signal and returns its handler id. */\n connect(signal: string, handler: SignalHandler, isAfter?: boolean): number;\n /** Disconnects the handler previously connected under the given id. */\n disconnect(handlerId: number): void;\n};\n\nconst listenerTable: WeakMap<object, Map<string, Map<SignalHandler, number>>> = new WeakMap();\n\nconst findListenerHandlerId = (instance: object, signal: string, handler: SignalHandler): number | undefined =>\n listenerTable.get(instance)?.get(signal)?.get(handler);\n\nconst trackListener = (instance: object, signal: string, handler: SignalHandler, handlerId: number): void => {\n let bySignal = listenerTable.get(instance);\n\n if (!bySignal) {\n bySignal = new Map();\n listenerTable.set(instance, bySignal);\n }\n\n let byHandler = bySignal.get(signal);\n\n if (!byHandler) {\n byHandler = new Map();\n bySignal.set(signal, byHandler);\n }\n\n byHandler.set(handler, handlerId);\n};\n\nconst untrackHandlerId = (instance: object, signal: string, handlerId: number): void => {\n const bySignal = listenerTable.get(instance);\n const byHandler = bySignal?.get(signal);\n\n if (byHandler === undefined) {\n return;\n }\n\n for (const [handler, id] of byHandler) {\n if (id === handlerId) {\n byHandler.delete(handler);\n }\n }\n\n if (byHandler.size === 0) {\n bySignal?.delete(signal);\n }\n};\n\n/**\n * Connects a handler to a signal and tracks it so it can later be removed with\n * {@link offSignal}.\n *\n * @param instance The object emitting the signal.\n * @param signal The signal name to connect to.\n * @param handler The callback invoked on each emission.\n * @param isAfter When true, run the handler after the default handler.\n */\nfunction onSignal(instance: SignalConnectable, signal: string, handler: SignalHandler, isAfter?: boolean): void {\n const handlerId = instance.connect(signal, handler, isAfter);\n trackListener(instance, signal, handler, handlerId);\n}\n\n/**\n * Connects a handler that runs at most once, disconnecting itself after the first\n * emission.\n *\n * @param instance The object emitting the signal.\n * @param signal The signal name to connect to.\n * @param handler The callback invoked on the first emission.\n * @param isAfter When true, run the handler after the default handler.\n */\nfunction onceSignal(instance: SignalConnectable, signal: string, handler: SignalHandler, isAfter?: boolean): void {\n let handlerId = 0;\n\n const wrapped: SignalHandler = (...args) => {\n untrackHandlerId(instance, signal, handlerId);\n untrackConnection(instance, handlerId);\n instance.disconnect(handlerId);\n\n return handler(...args);\n };\n\n handlerId = instance.connect(signal, wrapped, isAfter);\n trackListener(instance, signal, wrapped, handlerId);\n trackListener(instance, signal, handler, handlerId);\n}\n\n/**\n * Disconnects a handler previously connected with {@link onSignal} or\n * {@link onceSignal}. Does nothing if the handler is not currently connected.\n *\n * @param instance The object the handler was connected to.\n * @param signal The signal name the handler was connected to.\n * @param handler The handler to disconnect.\n */\nfunction offSignal(instance: SignalConnectable, signal: string, handler: SignalHandler): void {\n const handlerId = findListenerHandlerId(instance, signal, handler);\n\n if (handlerId === undefined) {\n return;\n }\n\n untrackHandlerId(instance, signal, handlerId);\n untrackConnection(instance, handlerId);\n\n if (!isSignalHandlerConnected(instance, handlerId)) {\n return;\n }\n\n instance.disconnect(handlerId);\n}\n\nexport { onSignal, onceSignal, offSignal };\n"]}
package/dist/mixin.d.ts CHANGED
@@ -1,6 +1,12 @@
1
- import type { AnyClass } from "@gtkx/utils";
1
+ import { type AnyClass } from "@gtkx/utils";
2
+ /**
3
+ * The signal plumbing every base class passed to a {@link Mixin} provides, so mixed-in interface
4
+ * members can connect and emit without knowing the concrete class.
5
+ */
2
6
  type MixinReceiver = {
7
+ /** Connects a handler to a signal and returns its handler id. */
3
8
  connect(signal: string, handler: (...args: unknown[]) => unknown, isAfter?: boolean): number;
9
+ /** Emits a signal with the given arguments and returns whatever the emission produced. */
4
10
  emit(signal: string, ...args: unknown[]): unknown;
5
11
  };
6
12
  /**
@@ -8,6 +14,14 @@ type MixinReceiver = {
8
14
  * be merged onto a target prototype.
9
15
  */
10
16
  type Mixin = (base: AnyClass<MixinReceiver>) => AnyClass;
17
+ /**
18
+ * Copies each mixin's prototype members onto the target class prototype, skipping
19
+ * any member already defined anywhere in the target's class chain.
20
+ *
21
+ * @param target The class whose prototype receives the mixin members.
22
+ * @param mixins The mixins to apply, in order.
23
+ */
11
24
  declare function installMixins(target: AnyClass, mixins: Mixin[]): void;
12
- export { installMixins, type MixinReceiver, type Mixin };
25
+ declare function insertMixinLayer(target: AnyClass, mixin: Mixin, inheritedNames: Set<string>): void;
26
+ export { insertMixinLayer, installMixins, type MixinReceiver, type Mixin };
13
27
  //# sourceMappingURL=mixin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mixin.d.ts","sourceRoot":"","sources":["../src/mixin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,aAAa,GAAG;IACjB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7F,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;CACrD,CAAC;AAEF;;;GAGG;AACH,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,KAAK,QAAQ,CAAC;AA6CzD,iBAAS,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAc9D;AAED,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"mixin.d.ts","sourceRoot":"","sources":["../src/mixin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,aAAa,CAAC;AAE5D;;;GAGG;AACH,KAAK,aAAa,GAAG;IACjB,iEAAiE;IACjE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7F,0FAA0F;IAC1F,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;CACrD,CAAC;AAEF;;;GAGG;AACH,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,KAAK,QAAQ,CAAC;AAsCzD;;;;;;GAMG;AACH,iBAAS,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAc9D;AAQD,iBAAS,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAK3F;AAED,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,CAAC"}
package/dist/mixin.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { getParentClass } from "@gtkx/utils";
1
2
  function isDefinedInClassChain(prototype, key) {
2
3
  let current = prototype;
3
4
  while (current !== null && current !== Object.prototype) {
@@ -20,6 +21,11 @@ function copyLayerMember(target, layer, key) {
20
21
  Object.defineProperty(target.prototype, key, descriptor);
21
22
  }
22
23
  }
24
+ function copyLayerMembers(target, layer) {
25
+ for (const key of Object.getOwnPropertyNames(layer)) {
26
+ copyLayerMember(target, layer, key);
27
+ }
28
+ }
23
29
  /**
24
30
  * Copies each mixin's prototype members onto the target class prototype, skipping
25
31
  * any member already defined anywhere in the target's class chain.
@@ -27,11 +33,6 @@ function copyLayerMember(target, layer, key) {
27
33
  * @param target The class whose prototype receives the mixin members.
28
34
  * @param mixins The mixins to apply, in order.
29
35
  */
30
- function copyLayerMembers(target, layer) {
31
- for (const key of Object.getOwnPropertyNames(layer)) {
32
- copyLayerMember(target, layer, key);
33
- }
34
- }
35
36
  function installMixins(target, mixins) {
36
37
  const empty = class {
37
38
  connect() {
@@ -45,5 +46,16 @@ function installMixins(target, mixins) {
45
46
  copyLayerMembers(target, mixin(empty).prototype);
46
47
  }
47
48
  }
48
- export { installMixins };
49
+ function dropLayerMembers(layer, names) {
50
+ for (const name of names) {
51
+ Reflect.deleteProperty(layer.prototype, name);
52
+ }
53
+ }
54
+ function insertMixinLayer(target, mixin, inheritedNames) {
55
+ const layer = mixin(getParentClass(target));
56
+ dropLayerMembers(layer, inheritedNames);
57
+ Object.setPrototypeOf(target.prototype, layer.prototype);
58
+ Object.setPrototypeOf(target, layer);
59
+ }
60
+ export { insertMixinLayer, installMixins };
49
61
  //# sourceMappingURL=mixin.js.map
package/dist/mixin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mixin.js","sourceRoot":"","sources":["../src/mixin.ts"],"names":[],"mappings":"AAaA,SAAS,qBAAqB,CAAC,SAAiB,EAAE,GAAW;IACzD,IAAI,OAAO,GAAkB,SAAS,CAAC;IAEvC,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QACtD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,eAAe,CAAC,MAAgB,EAAE,KAAa,EAAE,GAAW;IACjE,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;QACxB,OAAO;IACX,CAAC;IAED,IAAI,qBAAqB,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO;IACX,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE/D,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC7D,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,MAAgB,EAAE,KAAa;IACrD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,MAAgB,EAAE,MAAe;IACpD,MAAM,KAAK,GAA4B;QACnC,OAAO;YACH,OAAO,CAAC,CAAC;QACb,CAAC;QAED,IAAI;YACA,OAAO,SAAS,CAAC;QACrB,CAAC;KACJ,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACzB,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;AACL,CAAC;AAED,OAAO,EAAE,aAAa,EAAkC,CAAC","sourcesContent":["import type { AnyClass } from \"@gtkx/utils\";\n\ntype MixinReceiver = {\n connect(signal: string, handler: (...args: unknown[]) => unknown, isAfter?: boolean): number;\n emit(signal: string, ...args: unknown[]): unknown;\n};\n\n/**\n * A factory that, given a base class, returns a subclass adding extra members to\n * be merged onto a target prototype.\n */\ntype Mixin = (base: AnyClass<MixinReceiver>) => AnyClass;\n\nfunction isDefinedInClassChain(prototype: object, key: string): boolean {\n let current: object | null = prototype;\n\n while (current !== null && current !== Object.prototype) {\n if (Object.hasOwn(current, key)) {\n return true;\n }\n\n current = Reflect.getPrototypeOf(current);\n }\n\n return false;\n}\n\nfunction copyLayerMember(target: AnyClass, layer: object, key: string): void {\n if (key === \"constructor\") {\n return;\n }\n\n if (isDefinedInClassChain(target.prototype, key)) {\n return;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(layer, key);\n\n if (descriptor !== undefined) {\n Object.defineProperty(target.prototype, key, descriptor);\n }\n}\n\n/**\n * Copies each mixin's prototype members onto the target class prototype, skipping\n * any member already defined anywhere in the target's class chain.\n *\n * @param target The class whose prototype receives the mixin members.\n * @param mixins The mixins to apply, in order.\n */\nfunction copyLayerMembers(target: AnyClass, layer: object): void {\n for (const key of Object.getOwnPropertyNames(layer)) {\n copyLayerMember(target, layer, key);\n }\n}\n\nfunction installMixins(target: AnyClass, mixins: Mixin[]): void {\n const empty: AnyClass<MixinReceiver> = class {\n connect(): number {\n return 0;\n }\n\n emit(): unknown {\n return undefined;\n }\n };\n\n for (const mixin of mixins) {\n copyLayerMembers(target, mixin(empty).prototype);\n }\n}\n\nexport { installMixins, type MixinReceiver, type Mixin };\n"]}
1
+ {"version":3,"file":"mixin.js","sourceRoot":"","sources":["../src/mixin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,cAAc,EAAE,MAAM,aAAa,CAAC;AAmB5D,SAAS,qBAAqB,CAAC,SAAiB,EAAE,GAAW;IACzD,IAAI,OAAO,GAAkB,SAAS,CAAC;IAEvC,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QACtD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,eAAe,CAAC,MAAgB,EAAE,KAAa,EAAE,GAAW;IACjE,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;QACxB,OAAO;IACX,CAAC;IAED,IAAI,qBAAqB,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO;IACX,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE/D,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC7D,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAgB,EAAE,KAAa;IACrD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,MAAgB,EAAE,MAAe;IACpD,MAAM,KAAK,GAA4B;QACnC,OAAO;YACH,OAAO,CAAC,CAAC;QACb,CAAC;QAED,IAAI;YACA,OAAO,SAAS,CAAC;QACrB,CAAC;KACJ,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACzB,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAe,EAAE,KAAkB;IACzD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAgB,EAAE,KAAY,EAAE,cAA2B;IACjF,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,CAAC,MAAM,CAA4B,CAAC,CAAC;IACvE,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IACxC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAkC,CAAC","sourcesContent":["import { type AnyClass, getParentClass } from \"@gtkx/utils\";\n\n/**\n * The signal plumbing every base class passed to a {@link Mixin} provides, so mixed-in interface\n * members can connect and emit without knowing the concrete class.\n */\ntype MixinReceiver = {\n /** Connects a handler to a signal and returns its handler id. */\n connect(signal: string, handler: (...args: unknown[]) => unknown, isAfter?: boolean): number;\n /** Emits a signal with the given arguments and returns whatever the emission produced. */\n emit(signal: string, ...args: unknown[]): unknown;\n};\n\n/**\n * A factory that, given a base class, returns a subclass adding extra members to\n * be merged onto a target prototype.\n */\ntype Mixin = (base: AnyClass<MixinReceiver>) => AnyClass;\n\nfunction isDefinedInClassChain(prototype: object, key: string): boolean {\n let current: object | null = prototype;\n\n while (current !== null && current !== Object.prototype) {\n if (Object.hasOwn(current, key)) {\n return true;\n }\n\n current = Reflect.getPrototypeOf(current);\n }\n\n return false;\n}\n\nfunction copyLayerMember(target: AnyClass, layer: object, key: string): void {\n if (key === \"constructor\") {\n return;\n }\n\n if (isDefinedInClassChain(target.prototype, key)) {\n return;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(layer, key);\n\n if (descriptor !== undefined) {\n Object.defineProperty(target.prototype, key, descriptor);\n }\n}\n\nfunction copyLayerMembers(target: AnyClass, layer: object): void {\n for (const key of Object.getOwnPropertyNames(layer)) {\n copyLayerMember(target, layer, key);\n }\n}\n\n/**\n * Copies each mixin's prototype members onto the target class prototype, skipping\n * any member already defined anywhere in the target's class chain.\n *\n * @param target The class whose prototype receives the mixin members.\n * @param mixins The mixins to apply, in order.\n */\nfunction installMixins(target: AnyClass, mixins: Mixin[]): void {\n const empty: AnyClass<MixinReceiver> = class {\n connect(): number {\n return 0;\n }\n\n emit(): unknown {\n return undefined;\n }\n };\n\n for (const mixin of mixins) {\n copyLayerMembers(target, mixin(empty).prototype);\n }\n}\n\nfunction dropLayerMembers(layer: AnyClass, names: Set<string>): void {\n for (const name of names) {\n Reflect.deleteProperty(layer.prototype, name);\n }\n}\n\nfunction insertMixinLayer(target: AnyClass, mixin: Mixin, inheritedNames: Set<string>): void {\n const layer = mixin(getParentClass(target) as AnyClass<MixinReceiver>);\n dropLayerMembers(layer, inheritedNames);\n Object.setPrototypeOf(target.prototype, layer.prototype);\n Object.setPrototypeOf(target, layer);\n}\n\nexport { insertMixinLayer, installMixins, type MixinReceiver, type Mixin };\n"]}
@@ -8,6 +8,14 @@ import type { Descriptor } from "@gtkx/native";
8
8
  * @param value The raw native value to convert.
9
9
  */
10
10
  declare function fromNative(descriptor: Descriptor, value: unknown): unknown;
11
+ /**
12
+ * Converts a JavaScript value into the raw form native code expects, unwrapping
13
+ * object, struct, boxed, and fundamental wrappers back to their handles and
14
+ * recursively converting arrays and maps according to the descriptor.
15
+ *
16
+ * @param descriptor Describes the native type to convert to.
17
+ * @param value The JavaScript value to convert.
18
+ */
11
19
  declare function toNative(descriptor: Descriptor, value: unknown): unknown;
12
20
  export { fromNative, toNative };
13
21
  //# sourceMappingURL=native-value.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"native-value.d.ts","sourceRoot":"","sources":["../src/native-value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AA+DvE;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAyBnE;AAiBD,iBAAS,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAmBjE;AAED,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"native-value.d.ts","sourceRoot":"","sources":["../src/native-value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA0B,MAAM,cAAc,CAAC;AA+DvE;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAyBnE;AAiBD;;;;;;;GAOG;AACH,iBAAS,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAqBjE;AAED,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC"}
@@ -1,4 +1,4 @@
1
- import { getWrapperClass, tryGetHandle, wrapHandle } from "./registry.js";
1
+ import { getHandle, getWrapperClass, wrapHandle, wrapObject } from "./registry.js";
2
2
  import { resolveDescriptorType } from "./type.js";
3
3
  const MARSHALLED_KINDS = new Set([
4
4
  "object",
@@ -51,7 +51,7 @@ function fromNative(descriptor, value) {
51
51
  }
52
52
  switch (descriptor.kind) {
53
53
  case "object": {
54
- return wrapHandle(value);
54
+ return wrapObject(value);
55
55
  }
56
56
  case "struct": {
57
57
  return wrapHandle(value, descriptor.wrapperClass);
@@ -82,6 +82,14 @@ function hashTableToNative(descriptor, value) {
82
82
  toNative(descriptor.valueDescriptor, val),
83
83
  ]);
84
84
  }
85
+ /**
86
+ * Converts a JavaScript value into the raw form native code expects, unwrapping
87
+ * object, struct, boxed, and fundamental wrappers back to their handles and
88
+ * recursively converting arrays and maps according to the descriptor.
89
+ *
90
+ * @param descriptor Describes the native type to convert to.
91
+ * @param value The JavaScript value to convert.
92
+ */
85
93
  function toNative(descriptor, value) {
86
94
  if (!isMarshalledDescriptor(descriptor)) {
87
95
  return value;
@@ -91,7 +99,8 @@ function toNative(descriptor, value) {
91
99
  case "struct":
92
100
  case "boxed":
93
101
  case "fundamental": {
94
- return tryGetHandle(value) ?? null;
102
+ const instance = value;
103
+ return instance == null ? null : getHandle(instance);
95
104
  }
96
105
  case "array": {
97
106
  return collectionToNative(descriptor, value);
@@ -1 +1 @@
1
- {"version":3,"file":"native-value.js","sourceRoot":"","sources":["../src/native-value.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Descriptor, ExternalObject, Handle } from \"@gtkx/native\";\nimport type { ArrayDescriptor, FundamentalDescriptor, HashTableDescriptor, StructDescriptor } from \"./descriptors.js\";\nimport { getWrapperClass, tryGetHandle, wrapHandle } from \"./registry.js\";\nimport { resolveDescriptorType } from \"./type.js\";\n\ntype MarshalledKind = \"object\" | \"struct\" | \"boxed\" | \"fundamental\" | \"array\" | \"hashtable\";\ntype MarshalledDescriptor = Extract<Descriptor, { kind: MarshalledKind }>;\n\nconst MARSHALLED_KINDS: Set<Descriptor[\"kind\"]> = new Set<MarshalledKind>([\n \"object\",\n \"struct\",\n \"boxed\",\n \"fundamental\",\n \"array\",\n \"hashtable\",\n]);\n\nfunction isMarshalledDescriptor(descriptor: Descriptor): descriptor is MarshalledDescriptor {\n return MARSHALLED_KINDS.has(descriptor.kind);\n}\n\nfunction mapCollection(\n descriptor: ArrayDescriptor,\n value: unknown,\n convert: (itemDescriptor: Descriptor, item: unknown) => unknown,\n): unknown[] {\n return (value as unknown[]).map((item) => convert(descriptor.itemDescriptor, item));\n}\n\nfunction collectionFromNative(descriptor: ArrayDescriptor, value: unknown): unknown {\n return value === null ? null : mapCollection(descriptor, value, fromNative);\n}\n\nfunction boxedFromNative(descriptor: Descriptor, value: unknown): unknown {\n return value == null\n ? null\n : wrapHandle(value as ExternalObject<Handle>, getWrapperClass(resolveDescriptorType(descriptor)));\n}\n\nfunction fundamentalFromNative(descriptor: FundamentalDescriptor, value: unknown): unknown {\n return value == null\n ? null\n : wrapHandle(\n value as ExternalObject<Handle>,\n descriptor.wrapperClass ?? getWrapperClass(resolveDescriptorType(descriptor)),\n );\n}\n\nfunction hashTableFromNative(descriptor: HashTableDescriptor, value: unknown): unknown {\n if (value === null) {\n return null;\n }\n\n const entries = value as [unknown, unknown][];\n\n return new Map(\n entries.map(([key, val]): [unknown, unknown] => [\n fromNative(descriptor.keyDescriptor, key),\n fromNative(descriptor.valueDescriptor, val),\n ]),\n );\n}\n\n/**\n * Converts a raw value returned from native code into its JavaScript form,\n * wrapping object, struct, boxed, and fundamental handles and recursively\n * converting arrays and hash tables according to the descriptor.\n *\n * @param descriptor Describes the native type of the value.\n * @param value The raw native value to convert.\n */\nfunction fromNative(descriptor: Descriptor, value: unknown): unknown {\n if (!isMarshalledDescriptor(descriptor)) {\n return value;\n }\n\n switch (descriptor.kind) {\n case \"object\": {\n return wrapHandle(value as ExternalObject<Handle> | null);\n }\n case \"struct\": {\n return wrapHandle(value as ExternalObject<Handle> | null, (descriptor as StructDescriptor).wrapperClass);\n }\n case \"boxed\": {\n return boxedFromNative(descriptor, value);\n }\n case \"fundamental\": {\n return fundamentalFromNative(descriptor, value);\n }\n case \"array\": {\n return collectionFromNative(descriptor, value);\n }\n case \"hashtable\": {\n return hashTableFromNative(descriptor, value);\n }\n }\n}\n\nfunction collectionToNative(descriptor: ArrayDescriptor, value: unknown): unknown {\n return value == null ? null : mapCollection(descriptor, value, toNative);\n}\n\nfunction hashTableToNative(descriptor: HashTableDescriptor, value: unknown): unknown {\n if (value == null) {\n return null;\n }\n\n return [...(value as Map<unknown, unknown>)].map(([key, val]): [unknown, unknown] => [\n toNative(descriptor.keyDescriptor, key),\n toNative(descriptor.valueDescriptor, val),\n ]);\n}\n\nfunction toNative(descriptor: Descriptor, value: unknown): unknown {\n if (!isMarshalledDescriptor(descriptor)) {\n return value;\n }\n\n switch (descriptor.kind) {\n case \"object\":\n case \"struct\":\n case \"boxed\":\n case \"fundamental\": {\n return tryGetHandle(value as object | null | undefined) ?? null;\n }\n case \"array\": {\n return collectionToNative(descriptor, value);\n }\n case \"hashtable\": {\n return hashTableToNative(descriptor, value);\n }\n }\n}\n\nexport { fromNative, toNative };\n"]}
1
+ {"version":3,"file":"native-value.js","sourceRoot":"","sources":["../src/native-value.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Descriptor, ExternalObject, Handle } from \"@gtkx/native\";\nimport type { ArrayDescriptor, FundamentalDescriptor, HashTableDescriptor, StructDescriptor } from \"./descriptors.js\";\nimport { getHandle, getWrapperClass, wrapHandle, wrapObject } from \"./registry.js\";\nimport { resolveDescriptorType } from \"./type.js\";\n\ntype MarshalledKind = \"object\" | \"struct\" | \"boxed\" | \"fundamental\" | \"array\" | \"hashtable\";\ntype MarshalledDescriptor = Extract<Descriptor, { kind: MarshalledKind }>;\n\nconst MARSHALLED_KINDS: Set<Descriptor[\"kind\"]> = new Set<MarshalledKind>([\n \"object\",\n \"struct\",\n \"boxed\",\n \"fundamental\",\n \"array\",\n \"hashtable\",\n]);\n\nfunction isMarshalledDescriptor(descriptor: Descriptor): descriptor is MarshalledDescriptor {\n return MARSHALLED_KINDS.has(descriptor.kind);\n}\n\nfunction mapCollection(\n descriptor: ArrayDescriptor,\n value: unknown,\n convert: (itemDescriptor: Descriptor, item: unknown) => unknown,\n): unknown[] {\n return (value as unknown[]).map((item) => convert(descriptor.itemDescriptor, item));\n}\n\nfunction collectionFromNative(descriptor: ArrayDescriptor, value: unknown): unknown {\n return value === null ? null : mapCollection(descriptor, value, fromNative);\n}\n\nfunction boxedFromNative(descriptor: Descriptor, value: unknown): unknown {\n return value == null\n ? null\n : wrapHandle(value as ExternalObject<Handle>, getWrapperClass(resolveDescriptorType(descriptor)));\n}\n\nfunction fundamentalFromNative(descriptor: FundamentalDescriptor, value: unknown): unknown {\n return value == null\n ? null\n : wrapHandle(\n value as ExternalObject<Handle>,\n descriptor.wrapperClass ?? getWrapperClass(resolveDescriptorType(descriptor)),\n );\n}\n\nfunction hashTableFromNative(descriptor: HashTableDescriptor, value: unknown): unknown {\n if (value === null) {\n return null;\n }\n\n const entries = value as [unknown, unknown][];\n\n return new Map(\n entries.map(([key, val]): [unknown, unknown] => [\n fromNative(descriptor.keyDescriptor, key),\n fromNative(descriptor.valueDescriptor, val),\n ]),\n );\n}\n\n/**\n * Converts a raw value returned from native code into its JavaScript form,\n * wrapping object, struct, boxed, and fundamental handles and recursively\n * converting arrays and hash tables according to the descriptor.\n *\n * @param descriptor Describes the native type of the value.\n * @param value The raw native value to convert.\n */\nfunction fromNative(descriptor: Descriptor, value: unknown): unknown {\n if (!isMarshalledDescriptor(descriptor)) {\n return value;\n }\n\n switch (descriptor.kind) {\n case \"object\": {\n return wrapObject(value);\n }\n case \"struct\": {\n return wrapHandle(value as ExternalObject<Handle> | null, (descriptor as StructDescriptor).wrapperClass);\n }\n case \"boxed\": {\n return boxedFromNative(descriptor, value);\n }\n case \"fundamental\": {\n return fundamentalFromNative(descriptor, value);\n }\n case \"array\": {\n return collectionFromNative(descriptor, value);\n }\n case \"hashtable\": {\n return hashTableFromNative(descriptor, value);\n }\n }\n}\n\nfunction collectionToNative(descriptor: ArrayDescriptor, value: unknown): unknown {\n return value == null ? null : mapCollection(descriptor, value, toNative);\n}\n\nfunction hashTableToNative(descriptor: HashTableDescriptor, value: unknown): unknown {\n if (value == null) {\n return null;\n }\n\n return [...(value as Map<unknown, unknown>)].map(([key, val]): [unknown, unknown] => [\n toNative(descriptor.keyDescriptor, key),\n toNative(descriptor.valueDescriptor, val),\n ]);\n}\n\n/**\n * Converts a JavaScript value into the raw form native code expects, unwrapping\n * object, struct, boxed, and fundamental wrappers back to their handles and\n * recursively converting arrays and maps according to the descriptor.\n *\n * @param descriptor Describes the native type to convert to.\n * @param value The JavaScript value to convert.\n */\nfunction toNative(descriptor: Descriptor, value: unknown): unknown {\n if (!isMarshalledDescriptor(descriptor)) {\n return value;\n }\n\n switch (descriptor.kind) {\n case \"object\":\n case \"struct\":\n case \"boxed\":\n case \"fundamental\": {\n const instance = value as object | null | undefined;\n\n return instance == null ? null : getHandle(instance);\n }\n case \"array\": {\n return collectionToNative(descriptor, value);\n }\n case \"hashtable\": {\n return hashTableToNative(descriptor, value);\n }\n }\n}\n\nexport { fromNative, toNative };\n"]}
package/dist/object.d.ts CHANGED
@@ -1,14 +1,42 @@
1
- import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
1
+ import { type Descriptor, type ExternalObject, type Handle } from "@gtkx/native";
2
+ import { type AnyClass } from "@gtkx/utils";
3
+ /**
4
+ * One construct property a wrapper class accepts: the canonical `GObject` name it is set under,
5
+ * and the descriptor its value is marshalled through.
6
+ */
7
+ type ConstructBinding = [name: string, descriptor: Descriptor];
8
+ /** The construct properties a wrapper class accepts, keyed by the camelCased name callers give them. */
9
+ type ConstructBindings = Record<string, ConstructBinding>;
10
+ /**
11
+ * Declares the construct properties a wrapper class accepts, so `newObjectWithProperties`
12
+ * marshals each one through its descriptor rather than resolving it from the
13
+ * `GObject.ParamSpec` the type installs. A class inherits the declarations of its ancestors.
14
+ * Registering takes effect immediately, including for classes already constructed from and for
15
+ * subclasses that already inherited an earlier declaration.
16
+ *
17
+ * @param cls The wrapper class the properties are declared on.
18
+ * @param bindings CamelCased property names mapped to their canonical name and descriptor.
19
+ */
20
+ declare function registerConstructProperties(cls: AnyClass, bindings: ConstructBindings): void;
2
21
  /**
3
22
  * Constructs a new GObject of the given type, setting the supplied construct
4
- * properties. Entries whose value is `undefined` or not a `[descriptor, value]`
5
- * pair are skipped.
23
+ * properties, and binds `wrapper` to it. A property the wrapper's class declares
24
+ * through `registerConstructProperties` is marshalled through its descriptor; any
25
+ * other one is marshalled through the `GObject.ParamSpec` the type installs under
26
+ * that name, dashed or camelCased, and is skipped when the type installs none.
27
+ * A value that ParamSpec would refuse throws before GObject sees it: a `TypeError`
28
+ * for a read-only property and for a value of a type the property cannot hold, and
29
+ * a `RangeError` for a value the ParamSpec rejects.
30
+ * Properties whose value is `undefined` are skipped. A type registered with
31
+ * `registerClass` binds the wrapper before its `constructed` slot runs, so an
32
+ * override of that slot already sees a usable instance.
6
33
  *
7
34
  * @param gtype The GType of the object to construct.
8
- * @param props Property names mapped to `[descriptor, value]` pairs.
35
+ * @param props Property names mapped to the values to set them to.
36
+ * @param wrapper The wrapper instance to bind to the new object.
9
37
  * @returns The handle of the newly created object.
10
38
  */
11
- declare function newObjectWithProperties(gtype: bigint, props: Record<string, unknown>): ExternalObject<Handle>;
39
+ declare function newObjectWithProperties(gtype: bigint, props: object, wrapper: object): ExternalObject<Handle>;
12
40
  /**
13
41
  * Reads a GObject property and converts it to its JavaScript value using the
14
42
  * descriptor.
@@ -28,5 +56,5 @@ declare function getObjectProperty(obj: object, propertyName: string, descriptor
28
56
  * @param jsValue The value to set.
29
57
  */
30
58
  declare function setObjectProperty(obj: object, propertyName: string, descriptor: Descriptor, jsValue: unknown): void;
31
- export { newObjectWithProperties, getObjectProperty, setObjectProperty };
59
+ export { newObjectWithProperties, getObjectProperty, registerConstructProperties, setObjectProperty, type ConstructBinding, type ConstructBindings, };
32
60
  //# sourceMappingURL=object.d.ts.map