@bridgething/client 0.1.6 → 0.2.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.
@@ -0,0 +1,67 @@
1
+ import type { ConfigEntry, ConfigField, DocEntry } from '@bridgething/lib';
2
+ export type { ConfigEntry, ConfigField, DocEntry } from '@bridgething/lib';
3
+ /** Identity of the webapp whose settings page is being rendered. */
4
+ export type SettingsContext = {
5
+ webappId: string;
6
+ name: string;
7
+ version: string;
8
+ deviceId: string;
9
+ };
10
+ /** A config or doc key/value after a write; `value` is null when reset to no value. */
11
+ export type KeyValue = {
12
+ key: string;
13
+ value: string | null;
14
+ };
15
+ /** Callback for unsolicited doc changes pushed by the host. */
16
+ export type DocChangedListener = (key: string, value: string | null) => void;
17
+ declare global {
18
+ interface Window {
19
+ ReactNativeWebView?: {
20
+ postMessage: (json: string) => void;
21
+ };
22
+ __bridgethingSettingsDeliver?: (json: string) => void;
23
+ }
24
+ }
25
+ /**
26
+ * Bridge a webapp settings page to its host inside the bridgething companion
27
+ * app. Speaks the React-Native WebView `postMessage` protocol, not a
28
+ * WebSocket; the host delivers replies through a global installed at import.
29
+ *
30
+ * Network caveat: the page runs on the phone with real internet, but from a
31
+ * `file://` origin, so the webview enforces CORS on fetch/XHR (requests carry
32
+ * `Origin: null`). Websocket APIs are not CORS-gated and work out of the box;
33
+ * prefer them. For a CORS-strict HTTP-only service, fetch from the on-device
34
+ * webapp via `client.net` (phone-tunneled, not origin-restricted) instead.
35
+ */
36
+ export declare const settings: {
37
+ /** Identity of the webapp and device this settings page is scoped to. */
38
+ context(): Promise<SettingsContext>;
39
+ config: {
40
+ /** The config schema the webapp declared in its manifest. */
41
+ fields(): Promise<ConfigField[]>;
42
+ /** Current config values (declared defaults plus user overrides). */
43
+ list(): Promise<ConfigEntry[]>;
44
+ /** Set a config value; resolves with the stored key/value. */
45
+ set(key: string, value: string): Promise<KeyValue>;
46
+ /** Clear a config override; resolves with the restored default or null. */
47
+ delete(key: string): Promise<KeyValue>;
48
+ };
49
+ doc: {
50
+ /** Read one doc value; resolves with null when unset. */
51
+ get(key: string): Promise<KeyValue>;
52
+ /** List every doc key/value in the webapp's namespace. */
53
+ list(): Promise<DocEntry[]>;
54
+ /** Write one doc value, readable by both the webapp and companion. */
55
+ set(key: string, value: string): Promise<KeyValue>;
56
+ /** Delete one doc value; resolves with a null value. */
57
+ delete(key: string): Promise<{
58
+ key: string;
59
+ value: null;
60
+ }>;
61
+ };
62
+ /** Subscribe to host-pushed doc changes; returns an unsubscribe function. */
63
+ onDocChanged(listener: DocChangedListener): () => void;
64
+ /** Signal the host to close the settings webview. Fire-and-forget. */
65
+ done(): void;
66
+ };
67
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE3E,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK3E,oEAAoE;AACpE,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,uFAAuF;AACvF,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE7D,+DAA+D;AAC/D,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;AAW7E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,kBAAkB,CAAC,EAAE;YAAE,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;SAAE,CAAC;QAC7D,4BAA4B,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;KACvD;CACF;AAmDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ;IACnB,yEAAyE;eAC9D,OAAO,CAAC,eAAe,CAAC;;QAIjC,6DAA6D;kBACnD,OAAO,CAAC,WAAW,EAAE,CAAC;QAGhC,qEAAqE;gBAC7D,OAAO,CAAC,WAAW,EAAE,CAAC;QAG9B,8DAA8D;iBACrD,MAAM,SAAS,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;QAGlD,2EAA2E;oBAC/D,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;;;QAKtC,yDAAyD;iBAChD,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;QAGnC,0DAA0D;gBAClD,OAAO,CAAC,QAAQ,EAAE,CAAC;QAG3B,sEAAsE;iBAC7D,MAAM,SAAS,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;QAGlD,wDAAwD;oBAC5C,MAAM,GAAG,OAAO,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,IAAI,CAAA;SAAE,CAAC;;IAI5D,6EAA6E;2BACtD,kBAAkB,GAAG,MAAM,IAAI;IAItD,sEAAsE;YAC9D,IAAI;CAKb,CAAC"}
@@ -0,0 +1,118 @@
1
+ const CALL_TIMEOUT_MS = 15_000;
2
+ const HOST_MISSING = 'not running inside the bridgething companion settings host (window.ReactNativeWebView is absent)';
3
+ let nextId = 1;
4
+ const pending = new Map();
5
+ const docListeners = new Set();
6
+ function deliver(json) {
7
+ let parsed;
8
+ try {
9
+ parsed = JSON.parse(json);
10
+ }
11
+ catch {
12
+ return;
13
+ }
14
+ if ('event' in parsed && parsed.event === 'docChanged') {
15
+ for (const listener of docListeners)
16
+ listener(parsed.key, parsed.value);
17
+ return;
18
+ }
19
+ const reply = parsed;
20
+ const entry = pending.get(reply.id);
21
+ if (!entry)
22
+ return;
23
+ pending.delete(reply.id);
24
+ clearTimeout(entry.timer);
25
+ if (reply.ok)
26
+ entry.resolve(reply.value);
27
+ else
28
+ entry.reject(new Error(reply.error));
29
+ }
30
+ if (typeof window !== 'undefined')
31
+ window.__bridgethingSettingsDeliver = deliver;
32
+ function call(verb, payload) {
33
+ const host = typeof window !== 'undefined' ? window.ReactNativeWebView : undefined;
34
+ if (!host)
35
+ return Promise.reject(new Error(HOST_MISSING));
36
+ const id = nextId++;
37
+ return new Promise((resolve, reject) => {
38
+ const timer = setTimeout(() => {
39
+ if (pending.delete(id))
40
+ reject(new Error(`bridgething settings call '${verb}' timed out after ${CALL_TIMEOUT_MS}ms`));
41
+ }, CALL_TIMEOUT_MS);
42
+ pending.set(id, { resolve: resolve, reject, timer });
43
+ try {
44
+ host.postMessage(JSON.stringify({ id, verb, payload }));
45
+ }
46
+ catch (err) {
47
+ pending.delete(id);
48
+ clearTimeout(timer);
49
+ reject(err instanceof Error ? err : new Error(String(err)));
50
+ }
51
+ });
52
+ }
53
+ /**
54
+ * Bridge a webapp settings page to its host inside the bridgething companion
55
+ * app. Speaks the React-Native WebView `postMessage` protocol, not a
56
+ * WebSocket; the host delivers replies through a global installed at import.
57
+ *
58
+ * Network caveat: the page runs on the phone with real internet, but from a
59
+ * `file://` origin, so the webview enforces CORS on fetch/XHR (requests carry
60
+ * `Origin: null`). Websocket APIs are not CORS-gated and work out of the box;
61
+ * prefer them. For a CORS-strict HTTP-only service, fetch from the on-device
62
+ * webapp via `client.net` (phone-tunneled, not origin-restricted) instead.
63
+ */
64
+ export const settings = {
65
+ /** Identity of the webapp and device this settings page is scoped to. */
66
+ context() {
67
+ return call('context');
68
+ },
69
+ config: {
70
+ /** The config schema the webapp declared in its manifest. */
71
+ fields() {
72
+ return call('config.fields');
73
+ },
74
+ /** Current config values (declared defaults plus user overrides). */
75
+ list() {
76
+ return call('config.list');
77
+ },
78
+ /** Set a config value; resolves with the stored key/value. */
79
+ set(key, value) {
80
+ return call('config.set', { key, value });
81
+ },
82
+ /** Clear a config override; resolves with the restored default or null. */
83
+ delete(key) {
84
+ return call('config.delete', { key });
85
+ },
86
+ },
87
+ doc: {
88
+ /** Read one doc value; resolves with null when unset. */
89
+ get(key) {
90
+ return call('doc.get', { key });
91
+ },
92
+ /** List every doc key/value in the webapp's namespace. */
93
+ list() {
94
+ return call('doc.list');
95
+ },
96
+ /** Write one doc value, readable by both the webapp and companion. */
97
+ set(key, value) {
98
+ return call('doc.set', { key, value });
99
+ },
100
+ /** Delete one doc value; resolves with a null value. */
101
+ delete(key) {
102
+ return call('doc.delete', { key });
103
+ },
104
+ },
105
+ /** Subscribe to host-pushed doc changes; returns an unsubscribe function. */
106
+ onDocChanged(listener) {
107
+ docListeners.add(listener);
108
+ return () => docListeners.delete(listener);
109
+ },
110
+ /** Signal the host to close the settings webview. Fire-and-forget. */
111
+ done() {
112
+ const host = typeof window !== 'undefined' ? window.ReactNativeWebView : undefined;
113
+ if (!host)
114
+ throw new Error(HOST_MISSING);
115
+ host.postMessage(JSON.stringify({ id: nextId++, verb: 'done' }));
116
+ },
117
+ };
118
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridgething/client",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "description": "Typed client SDK a bridgething webapp uses to talk to the on-device daemon on a Spotify Car Thing.",
5
5
  "license": "MIT",
6
6
  "author": "Joey Eamigh <55670930+JoeyEamigh@users.noreply.github.com> (https://github.com/JoeyEamigh)",
@@ -24,6 +24,10 @@
24
24
  ".": {
25
25
  "types": "./dist/index.d.ts",
26
26
  "default": "./dist/index.js"
27
+ },
28
+ "./settings": {
29
+ "types": "./dist/settings.d.ts",
30
+ "default": "./dist/settings.js"
27
31
  }
28
32
  },
29
33
  "files": ["dist"],
@@ -35,7 +39,7 @@
35
39
  "lint": "eslint ."
36
40
  },
37
41
  "dependencies": {
38
- "@bridgething/lib": "0.1.6"
42
+ "@bridgething/lib": "0.2.0"
39
43
  },
40
44
  "peerDependencies": {
41
45
  "typescript": "^6.0.3"