@gjsify/app-settings 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,31 @@
1
- # Temporary Holding Version
1
+ # @gjsify/app-settings
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
+ A `Gio.Settings` subset that runs on the browser and NativeScript (Android), with the same names and semantics as real GJS. See ADR 0099.
4
+
5
+ | Platform | Backend |
6
+ |---|---|
7
+ | GJS | real `Gio.Settings` (`src/index.ts`) |
8
+ | Browser | `localStorage` (`src/browser.ts`) |
9
+ | NativeScript (`src/nativescript.ts`) | `ApplicationSettings` (`@nativescript/core`, optional peer) |
10
+
11
+ ## API
12
+
13
+ ```ts
14
+ import { Settings } from '@gjsify/app-settings';
15
+
16
+ const settings = new Settings({ schema_id: 'org.example.App', path: '/org/example/App/' });
17
+ settings.set_int('window-width', 800);
18
+ settings.get_int('window-width');
19
+ settings.connect('changed::window-width', (_s, key) => {});
20
+ ```
21
+
22
+ Supported: `get/set_{boolean,int,uint,double,string}`, `connect('changed[::key]')`, `disconnect`, `schema_id`, `path`.
23
+ Key types: `b i u d s`. Everything else (`list_keys`, `get_value`, `reset`, `apply`, `delay`, `Gio.Settings.new`) is refused by name, and `bind()` throws.
24
+
25
+ ## Schema
26
+
27
+ The schema is a `.gschema.xml`. Import it with the `?schema` specifier; the build step turns it into a module that calls `Settings.register`. On GJS, `register` is a no-op and the compiled schema is used.
28
+
29
+ Unsupported schema features (range, choices, enum, flags, child, override) are refused at build time.
30
+
31
+ A stored value that cannot be parsed counts as unset and is logged once.
@@ -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{defineSettings as e}from"./settings.js";const t=new Set;function decode(e,n,r){let i;if(n===`s`)i=r;else if(n===`b`)i=r===`true`?!0:r===`false`?!1:void 0;else{let e=Number(r);i=r.trim()!==``&&Number.isFinite(e)&&(n===`d`||Number.isInteger(e))?e:void 0}return i===void 0&&!t.has(e)&&(t.add(e),console.warn(`Gio.Settings: stored value of '${e}' is not a '${n}'; using the default`)),i}const n={read(e,t){let n=globalThis.localStorage.getItem(e);return n===null?void 0:decode(e,t,n)},write(e,t,n){try{globalThis.localStorage.setItem(e,String(n))}catch(t){throw Error(`Gio.Settings: cannot store '${e}': ${t.message}`)}}},r=e(n);typeof globalThis.addEventListener==`function`&&globalThis.addEventListener(`storage`,e=>{let{key:t}=e;t!==null&&r.notifyStorage(t)});export{r as Settings,e as defineSettings,n as localStorageStorage};
@@ -0,0 +1 @@
1
+ import{FIXTURE_SCHEMA as e,FIXTURE_SCHEMA_XML as t,SETTINGS_VECTORS as n,driveSettingsVectors as r}from"./settings.js";export{e as FIXTURE_SCHEMA,t as FIXTURE_SCHEMA_XML,n as SETTINGS_VECTORS,r as driveSettingsVectors};
@@ -0,0 +1,18 @@
1
+ import"../_virtual/_rolldown/runtime.js";const e={id:`eu.jumplink.Learn6502`,path:`/eu/jumplink/Learn6502/`,keys:{"ui-font":{type:`s`,default:`Sans 11`},"primary-color":{type:`s`,default:``},"accent-color":{type:`s`,default:``},"font-name":{type:`s`,default:`Monospace 12`},"color-scheme":{type:`i`,default:0},"window-width":{type:`i`,default:1e3},"window-height":{type:`i`,default:700},"is-maximized":{type:`b`,default:!1},"show-line-numbers":{type:`b`,default:!0},"auto-indent":{type:`b`,default:!0},count:{type:`u`,default:7},ratio:{type:`d`,default:1.5}}},t=`<?xml version="1.0" encoding="UTF-8"?>
2
+ <schemalist>
3
+ <schema id="eu.jumplink.Learn6502" path="/eu/jumplink/Learn6502/">
4
+ <key name="ui-font" type="s"><default>'Sans 11'</default></key>
5
+ <key name="primary-color" type="s"><default>''</default></key>
6
+ <key name="accent-color" type="s"><default>''</default></key>
7
+ <key name="font-name" type="s"><default>'Monospace 12'</default></key>
8
+ <key name="color-scheme" type="i"><default>0</default></key>
9
+ <key name="window-width" type="i"><default>1000</default></key>
10
+ <key name="window-height" type="i"><default>700</default></key>
11
+ <key name="is-maximized" type="b"><default>false</default></key>
12
+ <key name="show-line-numbers" type="b"><default>true</default></key>
13
+ <key name="auto-indent" type="b"><default>true</default></key>
14
+ <key name="count" type="u"><default>7</default></key>
15
+ <key name="ratio" type="d"><default>1.5</default></key>
16
+ </schema>
17
+ </schemalist>
18
+ `,n=e.id,threw=e=>{try{return e(),null}catch(e){return e.message}},make=e=>new e.Settings({schema_id:n}),r=[{row:`defaults`,rule:`a never-written key returns the schema default, for each of b i u d s`,run:e=>{let t=make(e);return[t.get_boolean(`show-line-numbers`),t.get_int(`window-width`),t.get_uint(`count`),t.get_double(`ratio`),t.get_string(`font-name`)]},shows:[!0,1e3,7,1.5,`Monospace 12`]},{row:`roundtrip`,rule:`set then get returns the value, for each of b i u d s`,run:e=>{let t=make(e);return t.set_boolean(`is-maximized`,!0),t.set_int(`window-width`,-5),t.set_uint(`count`,4e9),t.set_double(`ratio`,.25),t.set_string(`font-name`,`Mono 9`),[t.get_boolean(`is-maximized`),t.get_int(`window-width`),t.get_uint(`count`),t.get_double(`ratio`),t.get_string(`font-name`)]},shows:[!0,-5,4e9,.25,`Mono 9`]},{row:`instances`,rule:`a second instance on the schema sees the value and hears the change`,run:e=>{let t=make(e),n=make(e),r=[];return n.connect(`changed::window-width`,(e,t)=>r.push(t)),t.set_int(`window-width`,640),[n.get_int(`window-width`),r]},shows:[640,[`window-width`]]},{row:`changed`,rule:`changed::key runs once per differing set with the key; changed::other does not; changed runs for every key`,run:e=>{let t=make(e),n=[];return t.connect(`changed::window-width`,(e,t)=>n.push(`w:${t}`)),t.connect(`changed::window-height`,(e,t)=>n.push(`h:${t}`)),t.connect(`changed`,(e,t)=>n.push(`any:${t}`)),t.set_int(`window-width`,1),t.set_int(`window-width`,2),n},shows:[`w:window-width`,`any:window-width`,`w:window-width`,`any:window-width`]},{row:`equal`,rule:`the first write of the default fires (the key was unset); a write equal to the stored value is silent (memory backend)`,run:e=>{let t=make(e),n=0;return t.connect(`changed::window-width`,()=>n+=1),t.set_int(`window-width`,1e3),t.set_int(`window-width`,1e3),t.set_int(`window-width`,5),t.set_int(`window-width`,5),n},shows:2},{row:`disconnect`,rule:`disconnect stops the handler`,run:e=>{let t=make(e),n=0,r=t.connect(`changed::window-width`,()=>n+=1);return t.set_int(`window-width`,1),t.disconnect(r),t.set_int(`window-width`,2),n},shows:1},{row:`properties`,rule:`schema_id and path read back; the schema path is taken when the call gives none`,run:e=>{let t=make(e);return[t.schema_id,t.path]},shows:[n,e.path]},{row:`errors`,rule:`an unknown key, an out-of-range uint, an unknown signal and an unknown schema throw`,run:e=>{let t=make(e);return[threw(()=>t.get_int(`nope`))!==null,threw(()=>t.set_uint(`count`,-1))!==null,threw(()=>t.connect(`bogus`,()=>{}))!==null,threw(()=>new e.Settings({schema_id:`no.such`}))!==null]},shows:[!0,!0,!0,!0]},{row:`refusal-type`,rule:`a wrong-type getter or setter throws, naming the key (GJS only logs a GLib-CRITICAL and goes on)`,subsetOnly:!0,run:e=>{let t=make(e);return[(threw(()=>t.get_int(`is-maximized`))??``).includes(`is-maximized`),(threw(()=>t.set_int(`is-maximized`,1))??``).includes(`is-maximized`)]},shows:[!0,!0]},{row:`refusal`,rule:`every row ADR 0099 § 2 refuses throws, naming itself`,subsetOnly:!0,run:e=>{let t=make(e);return[`list_keys`,`get_value`,`set_value`,`reset`,`apply`,`delay`,`revert`,`sync`,`bind`,`create_action`,`get_child`].map(e=>(threw(()=>t[e](`k`))??``).includes(e))},shows:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0,!0]},{row:`refusal-construct`,rule:`the backend construct property and a foreign path are refused`,subsetOnly:!0,run:e=>[threw(()=>new e.Settings({schema_id:n,backend:{}}))!==null,threw(()=>new e.Settings({schema_id:n,path:`/other/`}))!==null],shows:[!0,!0]}];async function driveSettingsVectors(e,{describe:t,it:n,expect:i}){await t(`Gio.Settings vectors: ${e.name}`,async()=>{for(let t of r)t.subsetOnly&&e.isOracle||await n(`${t.row}: ${t.rule}`,()=>{e.reset(),i(t.run(e)).toStrictEqual(t.shows)})})}export{e as FIXTURE_SCHEMA,t as FIXTURE_SCHEMA_XML,r as SETTINGS_VECTORS,driveSettingsVectors};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";import e from"gi://Gio?version=2.0";const t=Object.assign(e.Settings,{register(e){}});export{t as Settings};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";import{defineSettings as e}from"./settings.js";import{ApplicationSettings as t}from"@nativescript/core";const n={read(e,n){if(t.hasKey(e))return n===`b`?t.getBoolean(e):n===`s`?t.getString(e):t.getNumber(e)},write(e,n,r){n===`b`?t.setBoolean(e,r):n===`s`?t.setString(e,r):t.setNumber(e,r)}},r=e(n);export{r as Settings,n as applicationSettingsStorage,e as defineSettings};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";const e=[`list_keys`,`get_value`,`set_value`,`get_user_value`,`get_default_value`,`reset`,`apply`,`delay`,`revert`,`sync`,`get_has_unapplied`,`is_writable`,`list_children`,`get_child`,`bind`,`bind_with_mapping`,`bind_writable`,`create_action`],t={min:-(2**31),max:2**31-1},n={min:0,max:2**32-1},r={b:`boolean`,i:`int`,u:`uint`,d:`double`,s:`string`},i=new Set([`b`,`i`,`u`,`d`,`s`]);function refuse(e,t){return Error(`Gio.Settings: ${e} ${t}, which is outside the Gio.Settings subset (ADR 0099 § 2).`)}function checkValue(e,i,a){let bad=()=>TypeError(`Gio.Settings: key '${a}' is of type '${e}', not ${typeof i}`);if(e===`b`){if(typeof i!=`boolean`)throw bad()}else if(e===`s`){if(typeof i!=`string`)throw bad()}else{if(typeof i!=`number`||Number.isNaN(i))throw bad();if(e!==`d`){let o=e===`i`?t:n;if(!Number.isInteger(i)||i<o.min||i>o.max)throw RangeError(`Gio.Settings: value ${i} for key '${a}' is out of range for ${r[e]}`)}}}function defineSettings(t){let n=new Map,a=new Map;class Settings{schema_id;path;#e;#t=1;#n=new Map;static register(e){if(!e.path)throw Error(`Gio.Settings: schema '${e.id}' has no path`);for(let[t,n]of Object.entries(e.keys))if(!i.has(n.type))throw refuse(`key '${t}' of type '${n.type}' in schema '${e.id}'`,`is not a b i u d s key`);n.set(e.id,e)}constructor(e){for(let t of Object.keys(e??{}))if(t!==`schema_id`&&t!==`path`)throw refuse(`the '${t}' construct property`,`is not implemented`);let t=n.get(e?.schema_id);if(!t)throw Error(`GSettings schema ${e?.schema_id} not found`);if(e.path!==void 0&&e.path!==t.path)throw Error(`Gio.Settings: path '${e.path}' differs from the schema's '${t.path}'`);this.#e=t,this.schema_id=t.id,this.path=t.path;let r=a.get(t.path);r||a.set(t.path,r=new Set),r.add(this)}#r(e,t){let n=this.#e.keys[e];if(!n)throw Error(`GSettings key ${e} not found in schema ${this.schema_id}`);if(n.type!==t)throw TypeError(`Gio.Settings: key '${e}' is of type '${n.type}', not '${t}' (${r[t]})`);return n}#i(e,n){let r=this.#r(e,n),i=t.read(this.path+e,n);return i===void 0?r.default:i}#a(e,n,r){this.#r(e,n),checkValue(n,r,e);let i=this.path+e,a=t.read(i,n);return t.write(i,n,r),a!==r&&this.notifyAll(e),!0}notifyAll(e){Settings.#o(this.path,e)}static#o(e,t){for(let n of a.get(e)??[])n.#s(t)}static notifyStorage(e){for(let[t,n]of a){if(!e.startsWith(t))continue;let r=e.slice(t.length),[i]=n;i&&r in i.#e.keys&&Settings.#o(t,r)}}#s(e){for(let{key:t,run:n}of Array.from(this.#n.values()))(t===null||t===e)&&n(this,e)}get_boolean(e){return this.#i(e,`b`)}get_int(e){return this.#i(e,`i`)}get_uint(e){return this.#i(e,`u`)}get_double(e){return this.#i(e,`d`)}get_string(e){return this.#i(e,`s`)}set_boolean(e,t){return this.#a(e,`b`,t)}set_int(e,t){return this.#a(e,`i`,t)}set_uint(e,t){return this.#a(e,`u`,t)}set_double(e,t){return this.#a(e,`d`,t)}set_string(e,t){return this.#a(e,`s`,t)}connect(e,t){let n=e.indexOf(`::`);if((n===-1?e:e.slice(0,n))!==`changed`)throw Error(`No signal '${e}' on object 'GSettings'`);let r=this.#t++;return this.#n.set(r,{key:n===-1?null:e.slice(n+2),run:t}),r}disconnect(e){if(!this.#n.delete(e))throw Error(`No handler with id ${e} is connected to GSettings`)}}for(let t of e)Object.defineProperty(Settings.prototype,t,{value(){throw refuse(`${t}()`,`is not implemented`)}});for(let e of[`new`,`new_with_path`,`new_full`])Object.defineProperty(Settings,e,{value(){throw refuse(`Settings.${e}()`,`is not implemented; construct with { schema_id }`)}});return Settings}export{defineSettings};
@@ -0,0 +1,6 @@
1
+ import { type SettingsStorage } from './settings.js';
2
+ export type { Schema, SchemaKey, SettingValue, SettingsProps, SettingsStorage, KeyType } from './settings.js';
3
+ export { defineSettings } from './settings.js';
4
+ export declare const localStorageStorage: SettingsStorage;
5
+ export declare const Settings: import("./settings.js").SettingsClass;
6
+ export type Settings = InstanceType<typeof Settings>;
@@ -0,0 +1 @@
1
+ export * from './settings.js';
@@ -0,0 +1,40 @@
1
+ import type { Schema } from '../settings.js';
2
+ export interface SettingsLike {
3
+ readonly schema_id: string;
4
+ readonly path: string;
5
+ [method: string]: any;
6
+ }
7
+ export interface SettingsClassLike {
8
+ new (props: {
9
+ schema_id: string;
10
+ path?: string;
11
+ }): SettingsLike;
12
+ }
13
+ export interface SettingsSubject {
14
+ readonly name: string;
15
+ readonly isOracle: boolean;
16
+ readonly Settings: SettingsClassLike;
17
+ /** Forgets everything written, so a vector starts from the defaults. */
18
+ reset(): void;
19
+ }
20
+ /** Learn6502's nine keys (`eu.jumplink.Learn6502.gschema.xml`), plus `u` and `d` for coverage. */
21
+ export declare const FIXTURE_SCHEMA: Schema;
22
+ /** The XML GJS compiles for the oracle; the same keys as {@link FIXTURE_SCHEMA}. */
23
+ export declare const FIXTURE_SCHEMA_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<schemalist>\n <schema id=\"eu.jumplink.Learn6502\" path=\"/eu/jumplink/Learn6502/\">\n <key name=\"ui-font\" type=\"s\"><default>'Sans 11'</default></key>\n <key name=\"primary-color\" type=\"s\"><default>''</default></key>\n <key name=\"accent-color\" type=\"s\"><default>''</default></key>\n <key name=\"font-name\" type=\"s\"><default>'Monospace 12'</default></key>\n <key name=\"color-scheme\" type=\"i\"><default>0</default></key>\n <key name=\"window-width\" type=\"i\"><default>1000</default></key>\n <key name=\"window-height\" type=\"i\"><default>700</default></key>\n <key name=\"is-maximized\" type=\"b\"><default>false</default></key>\n <key name=\"show-line-numbers\" type=\"b\"><default>true</default></key>\n <key name=\"auto-indent\" type=\"b\"><default>true</default></key>\n <key name=\"count\" type=\"u\"><default>7</default></key>\n <key name=\"ratio\" type=\"d\"><default>1.5</default></key>\n </schema>\n</schemalist>\n";
24
+ export interface SettingsVector {
25
+ readonly row: string;
26
+ readonly rule: string;
27
+ /** Subset-only vectors are not run on the oracle. */
28
+ readonly subsetOnly?: boolean;
29
+ readonly run: (subject: SettingsSubject) => unknown;
30
+ readonly shows: unknown;
31
+ }
32
+ export declare const SETTINGS_VECTORS: readonly SettingsVector[];
33
+ export interface SettingsHarness {
34
+ describe: (name: string, body: () => void | Promise<void>) => void | Promise<void>;
35
+ it: (name: string, body: () => void | Promise<void>) => void | Promise<void>;
36
+ expect: (value: unknown) => {
37
+ toStrictEqual(expected: unknown): void;
38
+ };
39
+ }
40
+ export declare function driveSettingsVectors(subject: SettingsSubject, { describe, it, expect }: SettingsHarness): Promise<void>;
@@ -0,0 +1,13 @@
1
+ import Gio from 'gi://Gio?version=2.0';
2
+ export declare const Settings: {
3
+ new (props: {
4
+ schema_id: string;
5
+ path?: string;
6
+ }): {
7
+ readonly schema_id: string;
8
+ readonly path: string;
9
+ };
10
+ } & {
11
+ register(_schema: unknown): void;
12
+ };
13
+ export type Settings = InstanceType<typeof Gio.Settings>;
@@ -0,0 +1,6 @@
1
+ import { type SettingsStorage } from './settings.js';
2
+ export type { Schema, SchemaKey, SettingValue, SettingsProps, SettingsStorage, KeyType } from './settings.js';
3
+ export { defineSettings } from './settings.js';
4
+ export declare const applicationSettingsStorage: SettingsStorage;
5
+ export declare const Settings: import("./settings.js").SettingsClass;
6
+ export type Settings = InstanceType<typeof Settings>;
@@ -0,0 +1,49 @@
1
+ export type KeyType = 'b' | 'i' | 'u' | 'd' | 's';
2
+ export type SettingValue = boolean | number | string;
3
+ export interface SchemaKey {
4
+ readonly type: KeyType;
5
+ readonly default: SettingValue;
6
+ }
7
+ /** What the build step (`?schema`) carries per schema: type and default per key, nothing else. */
8
+ export interface Schema {
9
+ readonly id: string;
10
+ readonly path: string;
11
+ readonly keys: Readonly<Record<string, SchemaKey>>;
12
+ }
13
+ /** The storage below `Settings`. `read` returns `undefined` for a key never written. */
14
+ export interface SettingsStorage {
15
+ read(storageKey: string, type: KeyType): SettingValue | undefined;
16
+ write(storageKey: string, type: KeyType, value: SettingValue): void;
17
+ }
18
+ export interface SettingsProps {
19
+ schema_id: string;
20
+ path?: string;
21
+ }
22
+ type ChangedHandler = (settings: object, key: string) => void;
23
+ /** The public instance surface; spelled out because declaration emit cannot carry `#private` members. */
24
+ export interface Settings {
25
+ readonly schema_id: string;
26
+ readonly path: string;
27
+ get_boolean(key: string): boolean;
28
+ get_int(key: string): number;
29
+ get_uint(key: string): number;
30
+ get_double(key: string): number;
31
+ get_string(key: string): string;
32
+ set_boolean(key: string, value: boolean): boolean;
33
+ set_int(key: string, value: number): boolean;
34
+ set_uint(key: string, value: number): boolean;
35
+ set_double(key: string, value: number): boolean;
36
+ set_string(key: string, value: string): boolean;
37
+ connect(signal: string, run: ChangedHandler): number;
38
+ disconnect(id: number): void;
39
+ }
40
+ export interface SettingsClass {
41
+ new (props: SettingsProps): Settings;
42
+ /** The only addition to GIO's names: the build step calls it with the `?schema` module. */
43
+ register(schema: Schema): void;
44
+ /** A write made elsewhere (another tab's `storage` event): emits `changed::key` for it. */
45
+ notifyStorage(storageKey: string): void;
46
+ }
47
+ /** The one `Settings` class for a storage. Registered schemas and live instances are per class. */
48
+ export declare function defineSettings(storage: SettingsStorage): SettingsClass;
49
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
- "name": "@gjsify/app-settings",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@gjsify/app-settings",
3
+ "version": "0.61.0",
4
+ "description": "Gio.Settings as a strict subset for the browser (localStorage) and NativeScript (ApplicationSettings); the native Gio.Settings on GJS",
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
+ "./browser": {
14
+ "types": "./lib/types/browser.d.ts",
15
+ "default": "./lib/esm/browser.js"
16
+ },
17
+ "./nativescript": {
18
+ "types": "./lib/types/nativescript.d.ts",
19
+ "default": "./lib/esm/nativescript.js"
20
+ },
21
+ "./conformance": {
22
+ "types": "./lib/types/conformance/index.d.ts",
23
+ "default": "./lib/esm/conformance/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "lib",
28
+ "!lib/**/*.spec.d.ts"
29
+ ],
30
+ "sideEffects": false,
31
+ "scripts": {
32
+ "clear": "gjsify clear lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs test.node.mjs",
33
+ "check": "gjsify tsc --noEmit",
34
+ "build": "gjsify run build:gjsify && gjsify run build:types",
35
+ "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
36
+ "build:types": "gjsify tsc",
37
+ "build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
38
+ "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
39
+ "build:test:browser": "gjsify build src/test.browser.mts --app browser --outfile dist/test.browser.mjs",
40
+ "build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
41
+ "test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:node && gjsify env GSETTINGS_BACKEND=memory gjsify run test:gjs",
42
+ "test:gjs": "gjsify run test.gjs.mjs",
43
+ "test:node": "node test.node.mjs"
44
+ },
45
+ "keywords": [
46
+ "gjs",
47
+ "gsettings",
48
+ "settings",
49
+ "nativescript",
50
+ "browser"
51
+ ],
52
+ "devDependencies": {
53
+ "@gjsify/cli": "^0.61.0",
54
+ "@gjsify/unit": "^0.61.0",
55
+ "@types/node": "^25.9.2",
56
+ "typescript": "^6.0.3"
57
+ },
58
+ "peerDependencies": {
59
+ "@nativescript/core": "*"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "@nativescript/core": {
63
+ "optional": true
64
+ }
65
+ },
66
+ "gjsify": {
67
+ "runtimes": {
68
+ "gjs": "polyfill",
69
+ "node": "none",
70
+ "browser": "polyfill",
71
+ "nativescript": "polyfill"
72
+ },
73
+ "tier": 3
74
+ },
75
+ "license": "MIT",
76
+ "repository": {
77
+ "type": "git",
78
+ "url": "git+https://github.com/gjsify/gjsify.git",
79
+ "directory": "packages/web/app-settings"
80
+ },
81
+ "bugs": {
82
+ "url": "https://github.com/gjsify/gjsify/issues"
83
+ },
84
+ "homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/app-settings#readme"
85
+ }