@gjsify/adwaita-gobject-native 0.0.0-stage → 0.61.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.
package/README.md CHANGED
@@ -1,3 +1,101 @@
1
- # Temporary Holding Version
1
+ # @gjsify/adwaita-gobject-native
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Real GObject behind `@gjsify/adwaita-core`'s GObject subset (ADR 0105): a `GObjectEngine` whose
4
+ property store, `notify`, signal bus, emission-stopping and `bind_property` are a live GObject,
5
+ reached through `@gjsify/node-gi` (ADR 0104).
6
+
7
+ ```ts
8
+ import { createGObject } from '@gjsify/adwaita-core';
9
+ import { createRealGioEngine } from '@gjsify/adwaita-gobject-native';
10
+
11
+ const GObject = createGObject(door, createRealGioEngine(requireGi));
12
+ ```
13
+
14
+ `requireGi` is `@gjsify/node-gi/gi`'s loader — injected, not imported, so this package names the
15
+ bridge nowhere: a static import of a non-workspace member fails `build:types` on every job that
16
+ has not linked it, and a dynamic `gi://` load would mis-declare the package's runtime triplet in
17
+ `scripts/audit-runtimes.mjs`. GJS's own `imports.gi` answers the same calls.
18
+
19
+ ## What the engine does and does not own
20
+
21
+ The engine is a value store and a signal bus, nothing else. `registerClass`'s refusals, the
22
+ accessor rule, the construct order and every error message stay in `@gjsify/adwaita-core` — so a
23
+ class the subset refuses is refused here identically, and the vectors of `GOBJECT_VECTORS` that
24
+ decide a refusal hold unchanged. What the engine can change is only what a read, a write and an
25
+ emission DO.
26
+
27
+ - A registered subset class gets a real GType; each instance gets one real instance of it, its
28
+ **twin**, held beside the instance and keyed by it. The twins mirror the subset's own class
29
+ chain. A handler is always called with the INSTANCE, never with the twin, and a `notify::`
30
+ handler gets the SUBSET's `ParamSpec`.
31
+ - The twin declares every property `READWRITE`, including the construct-only ones: after the twin
32
+ exists, the only writer left is the engine itself, from the core's `defineConstructOnly`.
33
+ Construct-only is still enforced — by the core, on the same vector.
34
+ - `setValue` tells the core it owes no `notify`: `g_object_set_property` already emitted one to
35
+ the handlers connected to the twin.
36
+ - GType names are suffixed per registration (`<name>_RealGio<n>`). A GType name is process-global
37
+ and a duplicate throws, while the vectors register the same class names on every subject.
38
+ - `bind` makes a real `GBinding` when both sides have twins, and falls back to the core's portable
39
+ `bindProperties` when either side is a door widget the engine never registered.
40
+
41
+ ## The portable list model in a real `Gio.ListStore`
42
+
43
+ ```ts
44
+ import { createRealGioListStore } from '@gjsify/adwaita-gobject-native';
45
+
46
+ const model = createRealGioListStore(requireGi);
47
+ model.onItemsChanged(({ position, removed, added }) => redrawRange(position, removed, added));
48
+ model.setModel([{ value: 'a', label: 'A' }]); // → { position: 0, removed: 0, added: 1 }
49
+ model.store; // a real GListModel — what `Adw.ComboRow:model` / `Gtk.DropDown:model` take
50
+ ```
51
+
52
+ `@gjsify/adwaita-core`'s `listItemsChanged` computes the ONE minimal splice from the old model to
53
+ the new one (ADR 0046), which is `g_list_store_splice`'s shape. This hands that splice to the real
54
+ store: the arithmetic, the item vocabulary and the equality rule stay the core's, and nothing here
55
+ decides anything. Items outside the splice carry over as the same item objects, which is what the
56
+ vectors' `survivors` counts — here counted off the live store by identity rather than derived.
57
+
58
+ **The `null` `listItemsChanged` returns for an equal model is load-bearing, and the real store is
59
+ what shows it.** `g_list_store_splice` emits `items-changed` UNCONDITIONALLY: a `splice(1, 0, [])`
60
+ emits `(1, 0, 0)` — a change of nothing that every listener must re-read the model for (measured,
61
+ GLib 2.86 through node-gi). So "an equal model emits nothing, as GTK does" is kept by not splicing,
62
+ never by splicing zeros; in pure JS the two are the same no-op.
63
+
64
+ ## What is measured
65
+
66
+ `src/engine.node-gi.spec.ts` drives all 24 rows of `@gjsify/adwaita-core/conformance`'s
67
+ `GOBJECT_VECTORS` against `createGObject(door, createRealGioEngine(requireGi))` under Node:
68
+
69
+ ```sh
70
+ gjsify workspace @gjsify/adwaita-gobject-native run test:gjs-on-node
71
+ ```
72
+
73
+ Unlike the oracle leg in `adwaita-core` (`isOracle: true`, which skips the seven `holds: 'subset'`
74
+ rows because real GObject accepts what the subset refuses), this subject IS the subset, so the
75
+ refusals run too — they are the one claim an engine swap could silently break. The four
76
+ `widget: true` rows run as well: the door is the same two-fake-widget door `gobject.spec.ts` uses,
77
+ so no display is involved.
78
+
79
+ `src/list-store.node-gi.spec.ts` drives every row of `LIST_ITEMS_CHANGED_VECTORS` through a real
80
+ store on the same leg, asserting the three numbers, that there is exactly ONE emission, and that
81
+ the store then reads as the new model with `survivors` items carried over by identity.
82
+
83
+ Both subjects run in CI in `node-gi.yml`'s `gtk-host-node` job, beside the stage-0 ORACLE leg that
84
+ drives the same `GOBJECT_VECTORS` against node-gi directly. The pair is the point: the oracle says
85
+ what the right answer is on this binding, this package says the subset still gives it with a real
86
+ store underneath.
87
+
88
+ ## What is NOT measured
89
+
90
+ Nothing here has run on Android. `@gjsify/adwaita-nativescript`'s `createGObject(door)` does not
91
+ pass this engine yet, and the reason is in a comment at that call: the line is evaluated at module
92
+ load, so a `requireGi` that throws on NativeScript's V8 would take an app down at startup, and
93
+ whether node-gi loads there at all is ADR 0105's own open question. The evidence for everything
94
+ above is node-gi on Linux.
95
+
96
+ ## Runtimes
97
+
98
+ `nativescript: 'native'` is the target — this is where real GObject reaches Android. The `node`
99
+ slot is `'polyfill'` rather than `'none'`, which is an exception to the convention in
100
+ [AGENTS.md](../AGENTS.md): node-gi makes the engine measurable on Linux CI, and a leg that cannot
101
+ run is a claim nobody checks.
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";import{bindProperties as e}from"@gjsify/adwaita-core";const t={gchararray:`string`,gboolean:`boolean`,gint:`int`,guint:`uint`,gdouble:`double`},n={bidirectional:`BIDIRECTIONAL`,"sync-create":`SYNC_CREATE`,"invert-boolean":`INVERT_BOOLEAN`},r={int:[-2147483648,2147483647],uint:[0,4294967295],double:[-Number.MAX_VALUE,Number.MAX_VALUE]};function aliasesOf(e){return[e,e.replace(/-/g,`_`),e.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())]}function createRealGioEngine(i){let a=i(`GObject`,`2.0`),o=a.ParamFlags.READWRITE,s=new WeakMap,c=new WeakMap,l=new WeakMap,u=new WeakMap,d=new WeakMap,f=new WeakMap,p=0;function touch(e,t){let n=f.get(e);n||f.set(e,n=new Set);let r=!n.has(t);return n.add(t),r}function uniqueTypeName(e){return`${e.replace(/[^A-Za-z0-9_+-]/g,`_`).replace(/^[^A-Za-z_]/,`_`)||`_`}_RealGio${++p}`}function paramSpecFor(e){let{name:t,nick:n,blurb:i}=e,s=e.kind===`string`||e.kind===`boolean`?void 0:r[e.kind],c=e.minimum??s?.[0]??0,l=e.maximum??s?.[1]??0,u=e.get_default_value();switch(e.kind){case`boolean`:return a.ParamSpec.boolean(t,n,i,o,u);case`string`:return a.ParamSpec.string(t,n,i,o,u??void 0);case`int`:return a.ParamSpec.int(t,n,i,o,c,l,u);case`uint`:return a.ParamSpec.uint(t,n,i,o,c,l,u);case`double`:return a.ParamSpec.double(t,n,i,o,c,l,u)}}function signalTypeOf(e){let n=t[e.name];if(n===void 0)throw TypeError(`real-gio engine: no GObject signal parameter type for '${e.name}'`);return n}function parentTwinOf(e){for(let t=Object.getPrototypeOf(e);typeof t==`function`;){let e=s.get(t);if(e)return e;t=Object.getPrototypeOf(t)}return a.Object}function registeredChainOf(e){let t=[];for(let n=e.constructor;typeof n==`function`;){let e=c.get(n);e&&t.push(e),n=Object.getPrototypeOf(n)}return t}function twinClassOf(e){for(let t=e.constructor;typeof t==`function`;){let e=s.get(t);if(e)return e;t=Object.getPrototypeOf(t)}}function twinOf(e){let t=l.get(e);if(t)return t;let n=twinClassOf(e);if(!n){let t=e.constructor?.name??String(e);throw Error(`real-gio engine: '${t}' was never registered against this engine`)}let r=new n;return l.set(e,r),r}function specOf(e,t){for(let n of registeredChainOf(e))for(let e of n.properties)if(e.name===t)return e}function canonicalPropertyOf(e,t){for(let n of registeredChainOf(e))for(let e of n.properties)if(aliasesOf(e.name).includes(t))return e.name;return t}function withEmission(e,t,n){let r=d.get(e);r||d.set(e,r=[]),r.push({key:t,stopped:!1});try{n()}finally{r.pop()}}return{name:`real-gio`,register(e){let t=parentTwinOf(e.klass),n={};for(let t of e.properties)n[t.name]=paramSpecFor(t);let r={};for(let[t,n]of Object.entries(e.signals))r[t]={param_types:n.map(signalTypeOf)};s.set(e.klass,a.registerClass({GTypeName:uniqueTypeName(e.typeName),Properties:n,Signals:r},class extends t{})),c.set(e.klass,e)},getValue(e,t){let n=twinOf(e);return touch(e,t.name),n[t.name]},setValue(e,t,n){let r=twinOf(e);return!touch(e,t.name)&&r[t.name]===n||withEmission(e,`notify::${t.name}`,()=>{r.set_property(t.name,n)}),!1},addHandler(e,t,n,r){let i=twinOf(e),a=n.startsWith(`notify::`)?specOf(e,n.slice(8)):void 0,o=i.connect(n,(t,...n)=>a===void 0?r(e,...n):r(e,a)),s=u.get(e);s||u.set(e,s=new Map),s.set(t,o)},removeHandler(e,t){let n=u.get(e),r=n?.get(t);return r===void 0?!1:(n.delete(t),twinOf(e).disconnect(r),!0)},emit(e,t,n){withEmission(e,t,()=>{twinOf(e).emit(t,...n)})},notify(e,t){withEmission(e,`notify::${t.name}`,()=>{twinOf(e).notify(t.name)})},stopEmission(e,t){let n=d.get(e)??[];for(let r=n.length-1;r>=0;r--){let i=n[r];if(i.key===t&&!i.stopped)return i.stopped=!0,a.signal_stop_emission_by_name(twinOf(e),t),!0}return!1},bind(t,r,i,o,s){if(!twinClassOf(t)||!twinClassOf(i))return e(t,r,i,o,s);let c=s.reduce((e,t)=>e|(a.BindingFlags[n[t]]??0),a.BindingFlags.DEFAULT??0),l=twinOf(t).bind_property(canonicalPropertyOf(t,r),twinOf(i),canonicalPropertyOf(i,o),c);return{unbind:()=>l.unbind()}}}}export{createRealGioEngine};
@@ -0,0 +1 @@
1
+ import{createRealGioEngine as e}from"./engine.js";import{useRealGObjectEngine as t}from"./opt-in.js";import{createRealGioListStore as n}from"./list-store.js";import{SUBSET_SUBJECT_NAME as r,createSubsetSubject as i}from"./vector-subject.js";export{r as SUBSET_SUBJECT_NAME,e as createRealGioEngine,n as createRealGioListStore,i as createSubsetSubject,t as useRealGObjectEngine};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";import{listItemsChanged as e}from"@gjsify/adwaita-core";let t=0;function createRealGioListStore(n){let r=n(`GObject`,`2.0`),i=n(`Gio`,`2.0`),a=r.ParamFlags.READWRITE,o=r.registerClass({GTypeName:`AdwPortableListItem_RealGio${++t}`,Properties:{value:r.ParamSpec.string(`value`,`Value`,`The value the item is addressed by`,a,``),label:r.ParamSpec.string(`label`,`Label`,`The text the item draws`,a,``)}},class AdwPortableListItem extends r.Object{}),s=new i.ListStore({item_type:o.$gtype});function read(){let e=[];for(let t=0;t<s.get_n_items();t++){let n=s.get_item(t);n&&e.push({value:n.value,label:n.label})}return e}return{store:s,read,setModel(t){let n=e(read(),t??[]);if(!n)return null;let r=(t??[]).slice(n.position,n.position+n.added).map(({value:e,label:t})=>new o({value:e,label:t}));return s.splice(n.position,n.removed,r),n},onItemsChanged(e){let t=s.connect(`items-changed`,(t,n,r,i)=>e({position:n,removed:r,added:i}));return()=>s.disconnect(t)}}}export{createRealGioListStore};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";import{createRealGioEngine as e}from"./engine.js";import{registerGObjectEngine as t}from"@gjsify/adwaita-core";function useRealGObjectEngine(n){let r;try{r=e(n)}catch(e){let t=Error(`useRealGObjectEngine: real GObject is not reachable, so the opt-in cannot be honoured. requireGi('GObject', '2.0') failed — the app opted in but '@gjsify/gi-runtime-android' did not deliver: check that it is a dependency of the app, that the APK carries 'lib/<abi>/libnode_gi.so' for this device's ABI, and that NativeScript extracted 'app/girepository-1.0' into the app's files dir. Remove the opt-in to run on pure JS.`);throw t.cause=e,t}return t(r),r}export{useRealGObjectEngine};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";import{createGObject as e}from"@gjsify/adwaita-core";const t=`adwaita-core on real GObject (node-gi engine)`;function createSubsetSubject(n,r=t){let i,a,o=e({name:`test door`,dispatch(e,t){throw Error(`test door: nothing to dispatch '${t}' on`)},listen(e,t){throw Error(`test door: nothing to listen '${t}' on`)},createFromTree(e,t){let n={},byId=e=>e===`template`?t.instance:n[e],build=e=>{let r=e.tag===`GtkSwitch`?i:e.tag===`GtkButton`?a:void 0;if(!r)throw Error(`test door: no fake widget for ${e.tag}`);let o=new r;e.id!==void 0&&(n[e.id]=o);for(let n of e.signals??[])o.connect(n.name,t.handler(n.handler,{flags:n.flags}));for(let[n,r]of Object.entries(e.bindings??{})){let e=[];r.flags?.includes(`bidirectional`)&&e.push(`bidirectional`),r.flags?.includes(`inverted`)&&e.push(`invert-boolean`),r.flags?.includes(`no-sync-create`)||e.push(`sync-create`),t.bind(byId(r.source),r.property,o,n,e)}return o};return{children:(e.children??[]).map(build),objects:n}},attach(){},register(){}},n),{ParamSpec:s,ParamFlags:c}=o;return i=o.registerClass({GTypeName:`FakeSwitch`,Properties:{active:s.boolean(`active`,``,``,c.READWRITE,!1)}},class extends o.Object{}),a=o.registerClass({GTypeName:`FakeButton`,Signals:{clicked:{param_types:[]}}},class extends o.Object{}),{name:r,isOracle:!1,GObject:o,Widget:o.Object,template:e=>e.tree,bind:(e,t,r,i,a)=>{n.bind(e,t,r,i,a)}}}export{t as SUBSET_SUBJECT_NAME,createSubsetSubject};
@@ -0,0 +1,14 @@
1
+ import type { GObjectEngine } from '@gjsify/adwaita-core';
2
+ /**
3
+ * `@gjsify/node-gi/gi`'s default export, as much of it as this engine uses. Declared rather than
4
+ * imported for the reason in the header — the bridge's own types are not resolvable in a job that
5
+ * has not linked it.
6
+ */
7
+ export type RequireGi = (namespace: string, version?: string) => unknown;
8
+ /**
9
+ * A real {@link GObjectEngine}: every registered class gets a GType, every instance a GObject.
10
+ *
11
+ * `requireGi` is `@gjsify/node-gi/gi`'s loader (or anything GJS-shaped — `imports.gi` answers the
12
+ * same calls), so the only thing this package needs of the bridge is handed to it.
13
+ */
14
+ export declare function createRealGioEngine(requireGi: RequireGi): GObjectEngine;
@@ -0,0 +1,2 @@
1
+ import { type RequireGi } from './engine.js';
2
+ export declare const realGioEngineSuite: (requireGi: RequireGi) => () => Promise<void>;
@@ -0,0 +1,6 @@
1
+ export { useRealGObjectEngine } from './opt-in.js';
2
+ export { createRealGioEngine } from './engine.js';
3
+ export type { RequireGi } from './engine.js';
4
+ export { createRealGioListStore } from './list-store.js';
5
+ export type { RealGioListStore } from './list-store.js';
6
+ export { createSubsetSubject, SUBSET_SUBJECT_NAME } from './vector-subject.js';
@@ -0,0 +1,29 @@
1
+ import type { AdwComboOption, AdwListItemsChanged, AdwListModel } from '@gjsify/adwaita-core';
2
+ import type { RequireGi } from './engine.js';
3
+ /** The portable list model, stored in a real `Gio.ListStore`. */
4
+ export interface RealGioListStore {
5
+ /**
6
+ * The real `Gio.ListStore`. This is the value a GTK widget's `model` property takes — handed
7
+ * out untyped because the typed one would need `@girs/gio-2.0`, which this package does not
8
+ * depend on for the reason `engine.ts`'s header gives.
9
+ */
10
+ readonly store: unknown;
11
+ /** The store's content, read back OUT of it as portable items. */
12
+ read(): AdwComboOption[];
13
+ /**
14
+ * Replace the content with `next` and answer the one `items-changed` the store emitted, or
15
+ * `null` when nothing was spliced because the two models are equal.
16
+ *
17
+ * Items outside the splice carry over as the SAME item objects: a consumer holding an item
18
+ * view per item keeps it, which is what the portable vectors' `survivors` counts.
19
+ */
20
+ setModel(next: AdwListModel | null | undefined): AdwListItemsChanged | null;
21
+ /** Subscribe to the store's REAL `items-changed`; the returned function disconnects. */
22
+ onItemsChanged(handler: (change: AdwListItemsChanged) => void): () => void;
23
+ }
24
+ /**
25
+ * A portable list model whose store is a real `Gio.ListStore`.
26
+ *
27
+ * `requireGi` is injected for the same reason the engine's is — see `engine.ts`'s header.
28
+ */
29
+ export declare function createRealGioListStore(requireGi: RequireGi): RealGioListStore;
@@ -0,0 +1,2 @@
1
+ import type { RequireGi } from './engine.js';
2
+ export declare const realGioListStoreSuite: (requireGi: RequireGi) => () => Promise<void>;
@@ -0,0 +1,12 @@
1
+ import { type GObjectEngine } from '@gjsify/adwaita-core';
2
+ import { type RequireGi } from './engine.js';
3
+ /**
4
+ * Builds the real-GObject engine over `requireGi` and registers it as THE engine of this app.
5
+ * Returns it, so a caller that wants to assert on `engine.name` can.
6
+ *
7
+ * Throws — never falls back — when `requireGi` cannot produce GObject, and when the core's slot
8
+ * was already read or already filled. The app passes `requireGi` itself (`@gjsify/node-gi/gi`'s
9
+ * default export) for the reason in `engine.ts`' header: nothing in this package may import the
10
+ * bridge statically.
11
+ */
12
+ export declare function useRealGObjectEngine(requireGi: RequireGi): GObjectEngine;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { type GObjectEngine } from '@gjsify/adwaita-core';
2
+ import type { GObjectSubject } from '@gjsify/adwaita-core/conformance';
3
+ /** The name the real-engine leg has reported since ADR 0105 stage 2; a leg may name itself. */
4
+ export declare const SUBSET_SUBJECT_NAME = "adwaita-core on real GObject (node-gi engine)";
5
+ /**
6
+ * The GObject subset with `engine` behind it, as a {@link GObjectSubject} the conformance driver
7
+ * takes.
8
+ *
9
+ * @param engine the engine under test — `createRealGioEngine(requireGi)` on both legs
10
+ * @param name what the driver calls the suite, so a device run is distinguishable in logcat
11
+ */
12
+ export declare function createSubsetSubject(engine: GObjectEngine, name?: string): GObjectSubject;
package/package.json CHANGED
@@ -1,6 +1,70 @@
1
1
  {
2
- "name": "@gjsify/adwaita-gobject-native",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@gjsify/adwaita-gobject-native",
3
+ "version": "0.61.0",
4
+ "description": "Real GObject behind @gjsify/adwaita-core's GObject subset — a GObjectEngine whose property store and signal bus are a live GObject reached through node-gi (ADR 0105)",
5
+ "type": "module",
6
+ "module": "lib/esm/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "default": "./lib/esm/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "lib",
16
+ "!lib/types/**/*.d.mts",
17
+ "!lib/types/**/*.spec.d.ts"
18
+ ],
19
+ "scripts": {
20
+ "clear": "gjsify clear lib tmp dist tsconfig.tsbuildinfo",
21
+ "check": "gjsify tsc --noEmit",
22
+ "build": "gjsify run build:gjsify && gjsify run build:types",
23
+ "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
24
+ "build:types": "gjsify tsc",
25
+ "build:test": "gjsify run build:test:node-gi",
26
+ "build:test:node-gi": "gjsify build src/test.node-gi.mts --app node --outfile dist/test.node-gi.mjs",
27
+ "test": "gjsify run build:gjsify && gjsify run build:test",
28
+ "test:gjs-on-node": "gjsify run build:test:node-gi && node dist/test.node-gi.mjs"
29
+ },
30
+ "keywords": [
31
+ "gjs",
32
+ "gobject",
33
+ "node-gi",
34
+ "nativescript",
35
+ "android",
36
+ "adwaita"
37
+ ],
38
+ "dependencies": {
39
+ "@gjsify/adwaita-core": "^0.61.0"
40
+ },
41
+ "devDependencies": {
42
+ "@gjsify/cli": "^0.61.0",
43
+ "@gjsify/node-gi": "file:../../node-gi/node-gi",
44
+ "@gjsify/unit": "^0.61.0",
45
+ "@types/node": "^25.9.2",
46
+ "typescript": "^6.0.3"
47
+ },
48
+ "gjsify": {
49
+ "runtimes": {
50
+ "gjs": "none",
51
+ "node": "polyfill",
52
+ "browser": "none",
53
+ "nativescript": "native"
54
+ },
55
+ "tier": 3,
56
+ "nativescriptPlatforms": [
57
+ "android"
58
+ ]
59
+ },
60
+ "license": "MIT",
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "git+https://github.com/gjsify/gjsify.git",
64
+ "directory": "packages/nativescript-bridge/gobject"
65
+ },
66
+ "bugs": {
67
+ "url": "https://github.com/gjsify/gjsify/issues"
68
+ },
69
+ "homepage": "https://github.com/gjsify/gjsify/tree/main/packages/nativescript-bridge/gobject#readme"
70
+ }