gnim 1.2.8 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/dbus.ts +868 -0
  2. package/dist/env.d.ts +9 -0
  3. package/dist/fetch.ts +411 -0
  4. package/dist/gnim.gresource +0 -0
  5. package/dist/gnome/{jsx-runtime.js → jsx-runtime.ts} +63 -51
  6. package/dist/gnome/signalTracker.ts +10 -0
  7. package/dist/gobject.ts +489 -0
  8. package/dist/gtk3/jsx-runtime.ts +170 -0
  9. package/dist/gtk3/style.ts +35 -0
  10. package/dist/gtk4/jsx-runtime.ts +181 -0
  11. package/dist/gtk4/style.ts +35 -0
  12. package/dist/index.ts +18 -0
  13. package/dist/jsx/For.ts +115 -0
  14. package/dist/jsx/Fragment.ts +92 -0
  15. package/dist/jsx/This.ts +99 -0
  16. package/dist/jsx/With.ts +67 -0
  17. package/dist/jsx/env.ts +35 -0
  18. package/dist/jsx/jsx.ts +256 -0
  19. package/dist/jsx/scope.ts +185 -0
  20. package/dist/jsx/state.ts +439 -0
  21. package/dist/resource.xml +77 -0
  22. package/dist/util.ts +90 -0
  23. package/dist/variant.ts +350 -0
  24. package/package.json +15 -39
  25. package/dist/dbus.d.ts +0 -130
  26. package/dist/dbus.js +0 -552
  27. package/dist/fetch.d.ts +0 -87
  28. package/dist/fetch.js +0 -303
  29. package/dist/gnome/jsx-runtime.d.ts +0 -5
  30. package/dist/gnome/signalTracker.d.ts +0 -6
  31. package/dist/gnome/signalTracker.js +0 -5
  32. package/dist/gobject.d.ts +0 -184
  33. package/dist/gobject.js +0 -309
  34. package/dist/gtk3/jsx-runtime.d.ts +0 -5
  35. package/dist/gtk3/jsx-runtime.js +0 -143
  36. package/dist/gtk3/style.d.ts +0 -4
  37. package/dist/gtk3/style.js +0 -25
  38. package/dist/gtk4/jsx-runtime.d.ts +0 -5
  39. package/dist/gtk4/jsx-runtime.js +0 -143
  40. package/dist/gtk4/style.d.ts +0 -4
  41. package/dist/gtk4/style.js +0 -25
  42. package/dist/jsx/For.d.ts +0 -25
  43. package/dist/jsx/For.js +0 -67
  44. package/dist/jsx/Fragment.d.ts +0 -28
  45. package/dist/jsx/Fragment.js +0 -76
  46. package/dist/jsx/This.d.ts +0 -28
  47. package/dist/jsx/This.js +0 -64
  48. package/dist/jsx/With.d.ts +0 -17
  49. package/dist/jsx/With.js +0 -35
  50. package/dist/jsx/env.d.ts +0 -20
  51. package/dist/jsx/env.js +0 -14
  52. package/dist/jsx/index.d.ts +0 -7
  53. package/dist/jsx/index.js +0 -7
  54. package/dist/jsx/jsx.d.ts +0 -86
  55. package/dist/jsx/jsx.js +0 -121
  56. package/dist/jsx/scope.d.ts +0 -99
  57. package/dist/jsx/scope.js +0 -160
  58. package/dist/jsx/state.d.ts +0 -149
  59. package/dist/jsx/state.js +0 -283
  60. package/dist/util.d.ts +0 -13
  61. package/dist/util.js +0 -68
  62. package/dist/variant.d.ts +0 -78
  63. package/dist/variant.js +0 -4
package/dist/dbus.ts ADDED
@@ -0,0 +1,868 @@
1
+ /**
2
+ * A {@link Service} currently only allows interfacing with a single interface of a remote object.
3
+ * In the future I want to come up with an API to be able to create Service objects for multiple
4
+ * interfaces of an object at the same time. Example usage would be for example combining
5
+ * "org.mpris.MediaPlayer2" and "org.mpris.MediaPlayer2.Player" into a single object.
6
+ */
7
+ import Gio from "gi://Gio"
8
+ import GLib from "gi://GLib"
9
+ import GObject from "gi://GObject"
10
+ import { definePropertyGetter, kebabify, xml } from "./util.js"
11
+ import { type DeepInfer } from "./variant.js"
12
+ import {
13
+ register,
14
+ property as gproperty,
15
+ signal as gsignal,
16
+ getter as ggetter,
17
+ setter as gsetter,
18
+ } from "./gobject.js"
19
+
20
+ const DEFAULT_TIMEOUT = 10_000
21
+
22
+ export const Variant = GLib.Variant
23
+ export type Variant<T extends string> = GLib.Variant<T>
24
+
25
+ const info = Symbol("dbus interface info")
26
+ const internals = Symbol("dbus interface internals")
27
+ const remoteMethod = Symbol("proxy remoteMethod")
28
+ const remoteMethodAsync = Symbol("proxy remoteMethodAsync")
29
+ const remotePropertySet = Symbol("proxy remotePropertySet")
30
+
31
+ type Ctx = { private: false; static: false; name: string }
32
+
33
+ // TODO: consider making some parts public
34
+ // - remoteMethod, remoteMethodAsync, remotePropertySet
35
+ // - info, proxy, dbusObject
36
+
37
+ /**
38
+ * Base type for DBus services and proxies. Interface name is set with
39
+ * the {@link iface} decorator which also register it as a GObject type.
40
+ */
41
+ export class Service extends GObject.Object {
42
+ static [info]?: Gio.DBusInterfaceInfo
43
+
44
+ static {
45
+ GObject.registerClass(this)
46
+ }
47
+
48
+ [internals]: {
49
+ dbusObject?: Gio.DBusExportedObject
50
+ proxy?: Gio.DBusProxy
51
+ priv: Record<string | symbol, unknown>
52
+ onStop: Set<() => void>
53
+ } = {
54
+ priv: {},
55
+ onStop: new Set<() => void>(),
56
+ }
57
+
58
+ #info: Gio.DBusInterfaceInfo
59
+
60
+ constructor() {
61
+ super()
62
+ const service = this.constructor as unknown as typeof Service
63
+ if (!service[info]) throw Error("missing interface info")
64
+ this.#info = service[info]
65
+ }
66
+
67
+ notify(propertyName: Extract<keyof this, string> | (string & {})): void {
68
+ const prop = this.#info.lookup_property(propertyName)
69
+
70
+ if (prop && this[internals].dbusObject) {
71
+ this[internals].dbusObject.emit_property_changed(
72
+ propertyName,
73
+ new GLib.Variant(prop.signature, this[propertyName as keyof this]),
74
+ )
75
+ }
76
+
77
+ super.notify(prop ? kebabify(propertyName) : propertyName)
78
+ }
79
+
80
+ emit(name: string, ...params: unknown[]): void {
81
+ const signal = this.#info.lookup_signal(name)
82
+
83
+ if (signal && this[internals].dbusObject) {
84
+ const signature = `(${signal.args.map((a) => a.signature).join("")})`
85
+ this[internals].dbusObject.emit_signal(name, new GLib.Variant(signature, params))
86
+ }
87
+
88
+ return super.emit(signal ? kebabify(name) : name, ...params)
89
+ }
90
+
91
+ // server
92
+ #handlePropertyGet(_: Gio.DBusExportedObject, propertyName: Extract<keyof this, string>) {
93
+ const prop = this.#info.lookup_property(propertyName)
94
+
95
+ if (!prop) {
96
+ throw Error(`${this.constructor.name} has no exported property: "${propertyName}"`)
97
+ }
98
+
99
+ const value = this[propertyName]
100
+ if (typeof value !== "undefined") {
101
+ return new GLib.Variant(prop.signature, value)
102
+ } else {
103
+ return null
104
+ }
105
+ }
106
+
107
+ // server
108
+ #handlePropertySet(
109
+ _: Gio.DBusExportedObject,
110
+ propertyName: Extract<keyof this, string>,
111
+ value: GLib.Variant,
112
+ ) {
113
+ const newValue = value.deepUnpack()
114
+ const prop = this.#info.lookup_property(propertyName)
115
+
116
+ if (!prop) {
117
+ throw Error(`${this.constructor.name} has no property: "${propertyName}"`)
118
+ }
119
+
120
+ if (this[propertyName] !== newValue) {
121
+ this[propertyName] = value.deepUnpack()
122
+ }
123
+ }
124
+
125
+ // server
126
+ #returnError(error: unknown, invocation: Gio.DBusMethodInvocation) {
127
+ console.error(error)
128
+ if (error instanceof GLib.Error) {
129
+ return invocation.return_gerror(error)
130
+ }
131
+ if (error instanceof Error) {
132
+ return invocation.return_dbus_error(
133
+ error.name.includes(".") ? error.name : `gjs.JSError.${error.name}`,
134
+ error.message,
135
+ )
136
+ }
137
+ invocation.return_dbus_error("gjs.DBusService.UnknownError", `${error}`)
138
+ }
139
+
140
+ // server
141
+ #returnValue(value: unknown, methodName: string, invocation: Gio.DBusMethodInvocation) {
142
+ if (value === null || value === undefined) {
143
+ return invocation.return_value(new GLib.Variant("()", []))
144
+ }
145
+
146
+ const args = this.#info.lookup_method(methodName)?.out_args ?? []
147
+ const signature = `(${args.map((arg) => arg.signature).join("")})`
148
+ if (!Array.isArray(value)) throw Error("value has to be a tuple")
149
+ invocation.return_value(new GLib.Variant(signature, value))
150
+ }
151
+
152
+ // server
153
+ #handleMethodCall(
154
+ _: Gio.DBusExportedObject,
155
+ methodName: Extract<keyof this, string>,
156
+ parameters: GLib.Variant,
157
+ invocation: Gio.DBusMethodInvocation,
158
+ ): void {
159
+ try {
160
+ const value = (this[methodName] as (...args: unknown[]) => unknown)(
161
+ ...parameters.deepUnpack<Array<unknown>>(),
162
+ )
163
+
164
+ if (value instanceof GLib.Variant) {
165
+ invocation.return_value(value)
166
+ } else if (value instanceof Promise) {
167
+ value
168
+ .then((value) => this.#returnValue(value, methodName, invocation))
169
+ .catch((error) => this.#returnError(error, invocation))
170
+ } else {
171
+ this.#returnValue(value, methodName, invocation)
172
+ }
173
+ } catch (error) {
174
+ this.#returnError(error, invocation)
175
+ }
176
+ }
177
+
178
+ // server
179
+ async serve({
180
+ busType = Gio.BusType.SESSION,
181
+ name = this.#info.name,
182
+ objectPath = "/" + this.#info.name.split(".").join("/"),
183
+ flags = Gio.BusNameOwnerFlags.NONE,
184
+ timeout = DEFAULT_TIMEOUT,
185
+ }: {
186
+ busType?: Gio.BusType
187
+ name?: string
188
+ objectPath?: string
189
+ flags?: Gio.BusNameOwnerFlags
190
+ timeout?: number
191
+ } = {}): Promise<this> {
192
+ const impl = new Gio.DBusExportedObject(
193
+ // @ts-expect-error missing constructor type
194
+ { g_interface_info: this.#info },
195
+ )
196
+
197
+ impl.connect("handle-method-call", this.#handleMethodCall.bind(this))
198
+ impl.connect("handle-property-get", this.#handlePropertyGet.bind(this))
199
+ impl.connect("handle-property-set", this.#handlePropertySet.bind(this))
200
+
201
+ this.#info.cache_build()
202
+
203
+ return new Promise((resolve, reject) => {
204
+ let source =
205
+ timeout > 0
206
+ ? setTimeout(() => {
207
+ reject(Error(`serve timed out`))
208
+ source = null
209
+ }, timeout)
210
+ : null
211
+
212
+ const clear = () => {
213
+ if (source) {
214
+ clearTimeout(source)
215
+ source = null
216
+ }
217
+ }
218
+
219
+ const busId = Gio.bus_own_name(
220
+ busType,
221
+ name,
222
+ flags,
223
+ (conn: Gio.DBusConnection) => {
224
+ try {
225
+ impl.export(conn, objectPath)
226
+ this[internals].dbusObject = impl
227
+ this[internals].onStop.add(() => {
228
+ Gio.bus_unown_name(busId)
229
+ impl.unexport()
230
+ this.#info.cache_release()
231
+ delete this[internals].dbusObject
232
+ })
233
+
234
+ resolve(this)
235
+ } catch (error) {
236
+ reject(error)
237
+ }
238
+ },
239
+ clear,
240
+ clear,
241
+ )
242
+ })
243
+ }
244
+
245
+ // proxy
246
+ #handlePropertiesChanged(_: Gio.DBusProxy, changed: GLib.Variant, invalidated: string[]) {
247
+ const set = new Set([...Object.keys(changed.deepUnpack()), ...invalidated])
248
+ for (const prop of set.values()) {
249
+ this.notify(prop as Extract<keyof this, string>)
250
+ }
251
+ }
252
+
253
+ // proxy
254
+ #handleSignal(
255
+ _: Gio.DBusProxy,
256
+ _sender: string | null,
257
+ signal: string,
258
+ parameters: GLib.Variant,
259
+ ) {
260
+ this.emit(kebabify(signal), ...parameters.deepUnpack<Array<unknown>>())
261
+ }
262
+
263
+ // proxy
264
+ #remoteMethodParams(
265
+ methodName: string,
266
+ args: unknown[],
267
+ ): Parameters<Gio.DBusProxy["call_sync"]> {
268
+ const { proxy } = this[internals]
269
+ if (!proxy) throw Error("invalid remoteMethod invocation: not a proxy")
270
+
271
+ const method = this.#info.lookup_method(methodName)
272
+ if (!method) throw Error("method not found")
273
+
274
+ const signature = `(${method.in_args.map((a) => a.signature).join("")})`
275
+
276
+ return [
277
+ methodName,
278
+ new GLib.Variant(signature, args),
279
+ Gio.DBusCallFlags.NONE,
280
+ DEFAULT_TIMEOUT,
281
+ null,
282
+ ]
283
+ }
284
+
285
+ // proxy
286
+ [remoteMethod](methodName: string, args: unknown[]): GLib.Variant {
287
+ const params = this.#remoteMethodParams(methodName, args)
288
+ return this[internals].proxy!.call_sync(...params)
289
+ }
290
+
291
+ // proxy
292
+ [remoteMethodAsync](methodName: string, args: unknown[]): Promise<GLib.Variant> {
293
+ return new Promise((resolve, reject) => {
294
+ try {
295
+ const params = this.#remoteMethodParams(methodName, args)
296
+ this[internals].proxy!.call(...params, (_, res) => {
297
+ try {
298
+ resolve(this[internals].proxy!.call_finish(res))
299
+ } catch (error) {
300
+ reject(error)
301
+ }
302
+ })
303
+ } catch (error) {
304
+ reject(error)
305
+ }
306
+ })
307
+ }
308
+
309
+ // proxy
310
+ [remotePropertySet](name: string, value: unknown) {
311
+ const proxy = this[internals].proxy!
312
+ const prop = this.#info.lookup_property(name)!
313
+
314
+ const variant = new GLib.Variant(prop.signature, value)
315
+ proxy.set_cached_property(name, variant)
316
+
317
+ proxy.call(
318
+ "org.freedesktop.DBus.Properties.Set",
319
+ new GLib.Variant("(ssv)", [proxy.gInterfaceName, name, variant]),
320
+ Gio.DBusCallFlags.NONE,
321
+ -1,
322
+ null,
323
+ (_, res) => {
324
+ try {
325
+ proxy.call_finish(res)
326
+ } catch (e) {
327
+ console.error(e)
328
+ }
329
+ },
330
+ )
331
+ }
332
+
333
+ // proxy
334
+ async proxy({
335
+ bus = Gio.DBus.session,
336
+ name = this.#info.name,
337
+ objectPath = "/" + this.#info.name.split(".").join("/"),
338
+ flags = Gio.DBusProxyFlags.NONE,
339
+ timeout = DEFAULT_TIMEOUT,
340
+ }: {
341
+ bus?: Gio.DBusConnection
342
+ name?: string
343
+ objectPath?: string
344
+ flags?: Gio.DBusProxyFlags
345
+ timeout?: number
346
+ } = {}): Promise<this> {
347
+ const proxy = new Gio.DBusProxy({
348
+ gConnection: bus,
349
+ gInterfaceName: this.#info.name,
350
+ gInterfaceInfo: this.#info,
351
+ gName: name,
352
+ gFlags: flags,
353
+ gObjectPath: objectPath,
354
+ })
355
+
356
+ return new Promise((resolve, reject) => {
357
+ const cancallable = new Gio.Cancellable()
358
+
359
+ let source =
360
+ timeout > 0
361
+ ? setTimeout(() => {
362
+ reject(Error(`proxy timed out`))
363
+ source = null
364
+ cancallable.cancel()
365
+ }, timeout)
366
+ : null
367
+
368
+ proxy.init_async(GLib.PRIORITY_DEFAULT, cancallable, (_, res) => {
369
+ try {
370
+ if (source) {
371
+ clearTimeout(source)
372
+ source = null
373
+ }
374
+
375
+ proxy.init_finish(res)
376
+ this[internals].proxy = proxy
377
+
378
+ const ids = [
379
+ proxy.connect("g-signal", this.#handleSignal.bind(this)),
380
+ proxy.connect(
381
+ "g-properties-changed",
382
+ this.#handlePropertiesChanged.bind(this),
383
+ ),
384
+ ]
385
+
386
+ this[internals].onStop.add(() => {
387
+ ids.forEach((id) => proxy.disconnect(id))
388
+ delete this[internals].proxy
389
+ })
390
+
391
+ resolve(this)
392
+ } catch (error) {
393
+ reject(error)
394
+ }
395
+ })
396
+ })
397
+ }
398
+
399
+ stop() {
400
+ const { onStop } = this[internals]
401
+ for (const cb of onStop.values()) {
402
+ onStop.delete(cb)
403
+ cb()
404
+ }
405
+ }
406
+ }
407
+
408
+ type InterfaceMeta = {
409
+ dbusMethods?: Record<
410
+ string,
411
+ Array<{
412
+ name?: string
413
+ type: string
414
+ direction: "in" | "out"
415
+ }>
416
+ >
417
+ dbusSignals?: Record<
418
+ string,
419
+ Array<{
420
+ name?: string
421
+ type: string
422
+ }>
423
+ >
424
+ dbusProperties?: Record<
425
+ string,
426
+ {
427
+ name: string
428
+ type: string
429
+ read?: true
430
+ write?: true
431
+ }
432
+ >
433
+ }
434
+
435
+ /**
436
+ * Registers a {@link Service} as a dbus interface.
437
+ *
438
+ * @param name Interface name of the object. For example "org.gnome.Shell.SearchProvider2"
439
+ * @param options optional properties to pass to {@link register}
440
+ */
441
+ export function iface(name: string, options?: Parameters<typeof register>[0]) {
442
+ return function (cls: { new (...args: any[]): Service }, ctx: ClassDecoratorContext) {
443
+ const meta = ctx.metadata
444
+ if (!meta) throw Error(`${cls.name} is not an interface`)
445
+
446
+ const { dbusMethods = {}, dbusSignals = {}, dbusProperties = {} } = meta as InterfaceMeta
447
+
448
+ const infoXml = xml({
449
+ name: "node",
450
+ children: [
451
+ {
452
+ name: "interface",
453
+ attributes: { name },
454
+ children: [
455
+ ...Object.entries(dbusMethods).map(([name, args]) => ({
456
+ name: "method",
457
+ attributes: { name },
458
+ children: args.map((arg) => ({ name: "arg", attributes: arg })),
459
+ })),
460
+ ...Object.entries(dbusSignals).map(([name, args]) => ({
461
+ name: "signal",
462
+ attributes: { name },
463
+ children: args.map((arg) => ({ name: "arg", attributes: arg })),
464
+ })),
465
+ ...Object.values(dbusProperties).map(({ name, type, read, write }) => ({
466
+ name: "property",
467
+ attributes: {
468
+ ...(name && { name }),
469
+ type,
470
+ access: (read ? "read" : "") + (write ? "write" : ""),
471
+ },
472
+ })),
473
+ ],
474
+ },
475
+ ],
476
+ })
477
+
478
+ Object.assign(cls, { [info]: Gio.DBusInterfaceInfo.new_for_xml(infoXml) })
479
+ register(options)(cls, ctx)
480
+ }
481
+ }
482
+
483
+ type DBusType = string | { type: string; name: string }
484
+
485
+ type InferVariantTypes<T extends Array<DBusType>> = {
486
+ [K in keyof T]: T[K] extends string
487
+ ? DeepInfer<T[K]>
488
+ : T[K] extends { type: infer S }
489
+ ? S extends string
490
+ ? DeepInfer<S>
491
+ : never
492
+ : unknown
493
+ }
494
+
495
+ function installMethod<Args extends Array<DBusType>>(
496
+ args: Args | [Args, Args?],
497
+ method: (...args: any[]) => unknown,
498
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
499
+ ) {
500
+ const name = ctx.name
501
+ const meta = ctx.metadata! as InterfaceMeta
502
+ const methods = (meta.dbusMethods ??= {})
503
+
504
+ if (typeof name !== "string") {
505
+ throw Error("only string named methods are allowed")
506
+ }
507
+
508
+ const [inArgs, outArgs = []] = (Array.isArray(args[0]) ? args : [args]) as [Args, Args]
509
+
510
+ methods[name] = [
511
+ ...inArgs.map((arg) => ({
512
+ direction: "in" as const,
513
+ ...(typeof arg === "string" ? { type: arg } : arg),
514
+ })),
515
+ ...outArgs.map((arg) => ({
516
+ direction: "out" as const,
517
+ ...(typeof arg === "string" ? { type: arg } : arg),
518
+ })),
519
+ ]
520
+
521
+ return name
522
+ }
523
+
524
+ function installProperty<T extends string>(
525
+ type: T,
526
+ ctx: ClassFieldDecoratorContext | ClassGetterDecoratorContext | ClassSetterDecoratorContext,
527
+ ) {
528
+ const kind = ctx.kind
529
+ const name = ctx.name
530
+ const meta = ctx.metadata! as InterfaceMeta
531
+ const properties = (meta.dbusProperties ??= {})
532
+
533
+ if (typeof name !== "string") {
534
+ throw Error("only string named properties are allowed")
535
+ }
536
+
537
+ const read = kind === "field" || kind === "getter"
538
+ const write = kind === "field" || kind === "setter"
539
+
540
+ if (name in properties) {
541
+ if (write) properties[name].write = true
542
+ if (read) properties[name].read = true
543
+ } else {
544
+ properties[name] = {
545
+ name,
546
+ type,
547
+ ...(read && { read }),
548
+ ...(write && { write }),
549
+ }
550
+ }
551
+
552
+ return name
553
+ }
554
+
555
+ function installSignal<Params extends Array<DBusType>>(
556
+ params: Params,
557
+ ctx: ClassMethodDecoratorContext<Service>,
558
+ ) {
559
+ const name = ctx.name
560
+ const meta = ctx.metadata! as InterfaceMeta
561
+ const signals = (meta.dbusSignals ??= {})
562
+
563
+ if (typeof name === "symbol") {
564
+ throw Error("symbols are not valid signals")
565
+ }
566
+
567
+ signals[name] = params.map((arg) => (typeof arg === "string" ? { type: arg } : arg))
568
+
569
+ return name
570
+ }
571
+
572
+ function inferGTypeFromVariant(type: DBusType): GObject.GType<any> {
573
+ if (typeof type !== "string") return inferGTypeFromVariant(type.type)
574
+
575
+ if (type.startsWith("a") || type.startsWith("(")) {
576
+ return GObject.TYPE_JSOBJECT
577
+ }
578
+
579
+ switch (type) {
580
+ case "v":
581
+ return GObject.TYPE_VARIANT
582
+ case "b":
583
+ return GObject.TYPE_BOOLEAN
584
+ case "y":
585
+ return GObject.TYPE_UINT
586
+ case "n":
587
+ return GObject.TYPE_INT
588
+ case "q":
589
+ return GObject.TYPE_UINT
590
+ case "i":
591
+ return GObject.TYPE_INT
592
+ case "u":
593
+ return GObject.TYPE_UINT
594
+ case "x":
595
+ return GObject.TYPE_INT64
596
+ case "t":
597
+ return GObject.TYPE_UINT64
598
+ case "h":
599
+ return GObject.TYPE_INT
600
+ case "d":
601
+ return GObject.TYPE_DOUBLE
602
+ case "s":
603
+ case "g":
604
+ case "o":
605
+ return GObject.TYPE_STRING
606
+ default:
607
+ break
608
+ }
609
+
610
+ throw Error(`cannot infer GType from variant "${type}"`)
611
+ }
612
+
613
+ /**
614
+ * Registers a method.
615
+ * You should prefer using {@link methodAsync} when proxying, due to IO blocking.
616
+ * Note that this is functionally the same as {@link methodAsync} on exported objects.
617
+ * ```
618
+ */
619
+ export function method<const InArgs extends Array<DBusType>, const OutArgs extends Array<DBusType>>(
620
+ inArgs: InArgs,
621
+ outArgs: OutArgs,
622
+ ): (
623
+ method: (this: Service, ...args: InferVariantTypes<InArgs>) => InferVariantTypes<OutArgs>,
624
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
625
+ ) => void
626
+
627
+ /**
628
+ * Registers a method.
629
+ * You should prefer using {@link methodAsync} when proxying, due to IO blocking.
630
+ * Note that this is functionally the same as {@link methodAsync} on exported objects.
631
+ * ```
632
+ */
633
+ export function method<const InArgs extends Array<DBusType>>(
634
+ ...inArgs: InArgs
635
+ ): (
636
+ method: (this: Service, ...args: InferVariantTypes<InArgs>) => void,
637
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
638
+ ) => void
639
+
640
+ export function method<const InArgs extends Array<DBusType>, const OutArgs extends Array<DBusType>>(
641
+ ...args: InArgs | [inArgs: InArgs, outArgs?: OutArgs]
642
+ ) {
643
+ return function (
644
+ method: (
645
+ this: Service,
646
+ ...args: InferVariantTypes<InArgs>
647
+ ) => InferVariantTypes<OutArgs> | void,
648
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
649
+ ): typeof method {
650
+ const name = installMethod(args, method, ctx)
651
+
652
+ return function (...args: InferVariantTypes<InArgs>) {
653
+ if (this[internals].proxy) {
654
+ const value = this[remoteMethod](name, args)
655
+ return value.deepUnpack<InferVariantTypes<OutArgs>>()
656
+ } else {
657
+ return method.apply(this, args)
658
+ }
659
+ }
660
+ }
661
+ }
662
+
663
+ /**
664
+ * Registers a method.
665
+ * You should prefer using this over {@link method} when proxying, since this does not block IO.
666
+ * Note that this is functionally the same as {@link method} on exported objects.
667
+ * ```
668
+ */
669
+ export function methodAsync<
670
+ const InArgs extends Array<DBusType>,
671
+ const OutArgs extends Array<DBusType>,
672
+ >(
673
+ inArgs: InArgs,
674
+ outArgs: OutArgs,
675
+ ): (
676
+ method: (
677
+ this: Service,
678
+ ...args: InferVariantTypes<InArgs>
679
+ ) => Promise<InferVariantTypes<OutArgs>>,
680
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
681
+ ) => void
682
+
683
+ /**
684
+ * Registers a method.
685
+ * You should prefer using this over {@link method} when proxying, since this does not block IO.
686
+ * Note that this is functionally the same as {@link method} on exported objects.
687
+ * ```
688
+ */
689
+ export function methodAsync<const InArgs extends Array<DBusType>>(
690
+ ...inArgs: InArgs
691
+ ): (
692
+ method: (this: Service, ...args: InferVariantTypes<InArgs>) => Promise<void>,
693
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
694
+ ) => void
695
+
696
+ export function methodAsync<
697
+ const InArgs extends Array<DBusType>,
698
+ const OutArgs extends Array<DBusType>,
699
+ >(...args: InArgs | [inArgs: InArgs, outArgs?: OutArgs]) {
700
+ return function (
701
+ method: (
702
+ this: Service,
703
+ ...args: InferVariantTypes<InArgs>
704
+ ) => Promise<InferVariantTypes<OutArgs> | void>,
705
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
706
+ ): typeof method {
707
+ const name = installMethod(args, method, ctx)
708
+
709
+ return async function (...args: InferVariantTypes<InArgs>) {
710
+ if (this[internals].proxy) {
711
+ const value = await this[remoteMethodAsync](name, args)
712
+ return value.deepUnpack<InferVariantTypes<OutArgs>>()
713
+ } else {
714
+ return method.apply(this, args)
715
+ }
716
+ }
717
+ }
718
+ }
719
+
720
+ /**
721
+ * Registers a read-write property. When a new value is assigned the notify signal
722
+ * is automatically emitted on the local and exported object.
723
+ *
724
+ * Note that new values are checked by reference so assigning the same object will
725
+ * not emit the notify signal.
726
+ * ```
727
+ */
728
+ export function property<T extends string>(type: T) {
729
+ return function (
730
+ _: void,
731
+ ctx: ClassFieldDecoratorContext<Service, DeepInfer<T>>,
732
+ ): (this: Service, init: DeepInfer<T>) => DeepInfer<T> {
733
+ const name = installProperty(type, ctx)
734
+
735
+ void gproperty({ $gtype: inferGTypeFromVariant(type) })(
736
+ _,
737
+ ctx as ClassFieldDecoratorContext<GObject.Object> & Ctx,
738
+ { metaOnly: true },
739
+ )
740
+
741
+ ctx.addInitializer(function () {
742
+ Object.defineProperty(this, name, {
743
+ configurable: false,
744
+ enumerable: true,
745
+ set(value: DeepInfer<T>) {
746
+ const { proxy, priv } = this[internals]
747
+
748
+ if (proxy) {
749
+ this[remotePropertySet](name, value)
750
+ return
751
+ }
752
+
753
+ if (priv[name] !== value) {
754
+ priv[name] = value
755
+ this.notify(name as Extract<keyof Service, string>)
756
+ }
757
+ },
758
+ get(): DeepInfer<T> {
759
+ const { proxy, priv } = this[internals]
760
+
761
+ return proxy
762
+ ? proxy.get_cached_property(name)!.deepUnpack<DeepInfer<T>>()
763
+ : (priv[name] as DeepInfer<T>)
764
+ },
765
+ } satisfies ThisType<Service>)
766
+ })
767
+
768
+ return function (init) {
769
+ const priv = this[internals].priv
770
+ priv[name] = init
771
+ // we don't need to store the value on the object
772
+ return void 0 as unknown as DeepInfer<T>
773
+ }
774
+ }
775
+ }
776
+
777
+ /**
778
+ * Registers a read-only property. Can be used in conjuction with {@link setter} to define
779
+ * read-write properties as accessors.
780
+ *
781
+ * Note that you will need to explicitly emit the notify signal.
782
+ */
783
+ export function getter<T extends string>(type: T) {
784
+ return function (
785
+ getter: (this: Service) => DeepInfer<T>,
786
+ ctx: ClassGetterDecoratorContext<Service, DeepInfer<T>>,
787
+ ): (this: Service) => DeepInfer<T> {
788
+ const name = installProperty(type, ctx)
789
+
790
+ ctx.addInitializer(function () {
791
+ definePropertyGetter(this, name as Extract<keyof Service, string>)
792
+ })
793
+
794
+ void ggetter({ $gtype: inferGTypeFromVariant(type) })(
795
+ () => {},
796
+ ctx as ClassGetterDecoratorContext<GObject.Object> & Ctx,
797
+ )
798
+
799
+ return function () {
800
+ const { proxy } = this[internals]
801
+ return proxy
802
+ ? proxy.get_cached_property(name)!.deepUnpack<DeepInfer<T>>()
803
+ : getter.call(this)
804
+ }
805
+ }
806
+ }
807
+
808
+ /**
809
+ * Registers a write-only property. Can be used in conjuction with {@link getter} to define
810
+ * read-write properties as accessors.
811
+ *
812
+ * Note that you will need to explicitly emit the notify signal.
813
+ */
814
+ export function setter<T extends string>(type: T) {
815
+ return function (
816
+ setter: (this: Service, value: DeepInfer<T>) => void,
817
+ ctx: ClassSetterDecoratorContext<Service, DeepInfer<T>>,
818
+ ): (this: Service, value: DeepInfer<T>) => void {
819
+ const name = installProperty(type, ctx)
820
+
821
+ void gsetter({ $gtype: inferGTypeFromVariant(type) })(
822
+ () => {},
823
+ ctx as ClassSetterDecoratorContext<GObject.Object> & Ctx,
824
+ )
825
+
826
+ return function (value: DeepInfer<T>) {
827
+ const { proxy } = this[internals]
828
+
829
+ if (proxy) {
830
+ this[remotePropertySet](name, value)
831
+ } else {
832
+ setter.call(this, value)
833
+ }
834
+ }
835
+ }
836
+ }
837
+
838
+ /**
839
+ * Registers a signal which when invoked will emit the signal
840
+ * on the local object and the exported object.
841
+ *
842
+ * **Note**: its not possible to emit signals on remote objects through proxies.
843
+ */
844
+ export function signal<const Params extends Array<DBusType>>(...params: Params) {
845
+ return function (
846
+ method: (this: Service, ...params: InferVariantTypes<Params>) => void,
847
+ ctx: ClassMethodDecoratorContext<Service, typeof method>,
848
+ ): typeof method {
849
+ const name = installSignal(params, ctx)
850
+
851
+ void gsignal(...params.map(inferGTypeFromVariant))(
852
+ () => {},
853
+ ctx as ClassMethodDecoratorContext<GObject.Object> & Ctx,
854
+ )
855
+
856
+ return function (...params: InferVariantTypes<Params>) {
857
+ if (this[internals].proxy) {
858
+ console.warn(`cannot emit signal "${name}" on remote object`)
859
+ }
860
+
861
+ if (this[internals].dbusObject || !this[internals].proxy) {
862
+ method.apply(this, params)
863
+ }
864
+
865
+ return this.emit(name, ...params)
866
+ }
867
+ }
868
+ }