@garretapp/sdk 0.1.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/dist/ui.cjs ADDED
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ // src/errors.ts
4
+ var GarretError = class _GarretError extends Error {
5
+ code;
6
+ hint;
7
+ constructor(code, message, options) {
8
+ super(message);
9
+ this.name = "GarretError";
10
+ this.code = code;
11
+ this.hint = options?.hint;
12
+ Object.setPrototypeOf(this, _GarretError.prototype);
13
+ }
14
+ };
15
+ function garretErrorFromWire(code, message, hint) {
16
+ const known = [
17
+ "BINARY_NOT_FOUND",
18
+ "NOT_FOUND",
19
+ "PERMISSION",
20
+ "UNAVAILABLE",
21
+ "BAD_ARGS",
22
+ "TIMEOUT",
23
+ "CANCELLED",
24
+ "NETWORK",
25
+ "INTERNAL"
26
+ ];
27
+ const c = known.includes(code) ? code : "INTERNAL";
28
+ return new GarretError(c, message, hint ? { hint } : void 0);
29
+ }
30
+
31
+ // src/client.ts
32
+ var MAX_PREATTACH_CHUNKS = 1e3;
33
+ var IDLE_TIMEOUT_MS = 3e4;
34
+ function createHostClient(transport, opts) {
35
+ const calls = /* @__PURE__ */ new Map();
36
+ const events = /* @__PURE__ */ new Map();
37
+ const eventBuffer = /* @__PURE__ */ new Map();
38
+ let seq = 0;
39
+ const nextId = () => `${opts.instanceId}:${++seq}`;
40
+ const settle = (id, fn) => {
41
+ const s = calls.get(id);
42
+ if (!s) return;
43
+ clearTimeout(s.timer);
44
+ calls.delete(id);
45
+ fn(s);
46
+ };
47
+ const timeoutError = (method) => new GarretError("TIMEOUT", `"${method}" timed out`);
48
+ const off = transport.onMessage((msg) => {
49
+ switch (msg.t) {
50
+ case "res":
51
+ settle(msg.id, (s) => s.resolve(msg.result));
52
+ break;
53
+ case "stream_end":
54
+ settle(msg.id, (s) => {
55
+ s.onEnd.forEach((cb) => cb(msg.result));
56
+ s.resolve(msg.result);
57
+ });
58
+ break;
59
+ case "err":
60
+ settle(msg.id, (s) => {
61
+ const e = garretErrorFromWire(msg.code, msg.message, msg.hint);
62
+ s.onError.forEach((cb) => cb(e));
63
+ s.reject(e);
64
+ });
65
+ break;
66
+ case "stream_err":
67
+ settle(msg.id, (s) => {
68
+ const e = garretErrorFromWire(msg.code, msg.message);
69
+ s.onError.forEach((cb) => cb(e));
70
+ s.reject(e);
71
+ });
72
+ break;
73
+ case "chunk": {
74
+ const s = calls.get(msg.id);
75
+ if (!s) break;
76
+ clearTimeout(s.timer);
77
+ s.timer = setTimeout(() => settle(msg.id, (st) => st.reject(timeoutError(msg.id))), IDLE_TIMEOUT_MS);
78
+ if (s.onData.length) s.onData.forEach((cb) => cb(msg.data));
79
+ else if (s.buffer.length < MAX_PREATTACH_CHUNKS) s.buffer.push(msg.data);
80
+ else s.overflow = true;
81
+ break;
82
+ }
83
+ case "event": {
84
+ const subs = events.get(msg.channel);
85
+ if (subs && subs.size) subs.forEach((cb) => cb(msg.payload));
86
+ else {
87
+ const buf = eventBuffer.get(msg.channel) ?? [];
88
+ if (buf.length < 100) buf.push(msg.payload);
89
+ eventBuffer.set(msg.channel, buf);
90
+ }
91
+ break;
92
+ }
93
+ }
94
+ });
95
+ function makeCall(method, args) {
96
+ const id = nextId();
97
+ let resolve;
98
+ let reject;
99
+ const promise = new Promise((res, rej) => {
100
+ resolve = res;
101
+ reject = rej;
102
+ });
103
+ promise.catch(() => {
104
+ });
105
+ const s = {
106
+ onData: [],
107
+ onEnd: [],
108
+ onError: [],
109
+ buffer: [],
110
+ overflow: false,
111
+ resolve,
112
+ reject,
113
+ timer: setTimeout(() => {
114
+ const e = timeoutError(method);
115
+ settle(id, (st) => {
116
+ st.onError.forEach((cb) => cb(e));
117
+ st.reject(e);
118
+ });
119
+ }, IDLE_TIMEOUT_MS)
120
+ };
121
+ calls.set(id, s);
122
+ transport.send({ t: "req", id, method, args });
123
+ const flush = (cb) => {
124
+ const buf = s.buffer;
125
+ s.buffer = [];
126
+ buf.forEach((c) => cb(c));
127
+ };
128
+ const call = {
129
+ then: (onF, onR) => promise.then(onF, onR),
130
+ catch: (onR) => promise.catch(onR),
131
+ finally: (cb) => promise.finally(cb),
132
+ result: () => promise,
133
+ onData(cb) {
134
+ s.onData.push(cb);
135
+ flush(cb);
136
+ return call;
137
+ },
138
+ onEnd(cb) {
139
+ s.onEnd.push(cb);
140
+ return call;
141
+ },
142
+ onError(cb) {
143
+ s.onError.push(cb);
144
+ return call;
145
+ },
146
+ cancel() {
147
+ if (!calls.has(id)) return;
148
+ clearTimeout(s.timer);
149
+ calls.delete(id);
150
+ transport.send({ t: "cancel", id });
151
+ }
152
+ };
153
+ return call;
154
+ }
155
+ const base = {
156
+ on(channel, cb) {
157
+ let set = events.get(channel);
158
+ if (!set) events.set(channel, set = /* @__PURE__ */ new Set());
159
+ set.add(cb);
160
+ const buffered = eventBuffer.get(channel);
161
+ if (buffered?.length) {
162
+ eventBuffer.delete(channel);
163
+ buffered.forEach((p) => cb(p));
164
+ }
165
+ return () => set.delete(cb);
166
+ },
167
+ dispose() {
168
+ off();
169
+ for (const s of calls.values()) clearTimeout(s.timer);
170
+ calls.clear();
171
+ events.clear();
172
+ eventBuffer.clear();
173
+ }
174
+ };
175
+ return new Proxy(base, {
176
+ get(target, prop) {
177
+ if (prop in target) return target[prop];
178
+ return (args) => makeCall(prop, args);
179
+ }
180
+ });
181
+ }
182
+
183
+ // src/platform.ts
184
+ function getRuntime() {
185
+ return typeof window !== "undefined" ? window.__garret : void 0;
186
+ }
187
+ function getHostTransport() {
188
+ return getRuntime()?.hostTransport ?? null;
189
+ }
190
+ function getInstanceId() {
191
+ return getRuntime()?.instanceId ?? "dev";
192
+ }
193
+ function nope() {
194
+ throw new GarretError("UNAVAILABLE", "not running inside Garret");
195
+ }
196
+ var stubStorage = { get: nope, set: nope, delete: nope, keys: nope, clear: nope };
197
+ var stubSecrets = { get: nope, set: nope, delete: nope };
198
+ function getGarret() {
199
+ const rt = getRuntime();
200
+ if (rt) return rt;
201
+ return {
202
+ inGarret: false,
203
+ storage: stubStorage,
204
+ instanceStorage: stubStorage,
205
+ secrets: stubSecrets,
206
+ shared: { storage: stubStorage, secrets: stubSecrets },
207
+ fetch: async () => {
208
+ throw new GarretError("UNAVAILABLE", "g.fetch is only available inside Garret");
209
+ },
210
+ service: () => ({ status: nope, query: nope }),
211
+ notify: () => {
212
+ },
213
+ openExternal: async () => false,
214
+ clipboard: { readText: nope, writeText: nope },
215
+ active: true,
216
+ onActiveChange: () => () => {
217
+ },
218
+ onOpenSettings: () => () => {
219
+ },
220
+ surfaces: { open: nope, onClosed: () => () => {
221
+ } },
222
+ window: { setAspectRatio: () => {
223
+ }, resize: () => {
224
+ }, close: () => {
225
+ } },
226
+ onReady: (cb) => {
227
+ cb({});
228
+ return () => {
229
+ };
230
+ }
231
+ };
232
+ }
233
+
234
+ // src/types.ts
235
+ function defineManifest(manifest) {
236
+ return manifest;
237
+ }
238
+ function defineConfig(schema) {
239
+ return schema;
240
+ }
241
+
242
+ exports.GarretError = GarretError;
243
+ exports.createHostClient = createHostClient;
244
+ exports.defineConfig = defineConfig;
245
+ exports.defineManifest = defineManifest;
246
+ exports.getGarret = getGarret;
247
+ exports.getHostTransport = getHostTransport;
248
+ exports.getInstanceId = getInstanceId;
249
+ exports.getRuntime = getRuntime;
package/dist/ui.d.cts ADDED
@@ -0,0 +1,23 @@
1
+ import { T as Transport } from './protocol-Do0BJdeE.cjs';
2
+ import { E as EventMap, H as HostClient } from './types-BxSYAH_H.cjs';
3
+ export { C as Capability, G as GarretError, M as Manifest, S as Stream, f as StreamCall, g as defineConfig, h as defineManifest } from './types-BxSYAH_H.cjs';
4
+ export { G as GarretPlatform, c as GarretRuntime, d as SecretsApi, e as ServiceClient, f as StorageApi, S as SurfaceApi, a as SurfaceHandle, b as SurfaceOpenOptions, W as WindowControls, g as getGarret, h as getHostTransport, i as getInstanceId, j as getRuntime } from './platform-DnI7gJTx.cjs';
5
+
6
+ /**
7
+ * UI-side host client — a typed proxy over the wire (protocol.ts). Every call returns ONE handle
8
+ * that is both a Promise (plain methods `await` it) and a stream sink (stream methods use
9
+ * `.onData/.onEnd`). The `HostClient<Api>` TYPE narrows it per method from the `Api` return type —
10
+ * so authors never restate which methods stream. The host decides stream-vs-value from the runtime
11
+ * return; the client handles either response on the same id.
12
+ */
13
+ interface HostClientOptions {
14
+ /** Correlation namespace so two placements of a widget never cross wires. */
15
+ instanceId: string;
16
+ }
17
+ type Client<Api, Events extends EventMap> = HostClient<Api> & {
18
+ on<K extends keyof Events & string>(channel: K, cb: (payload: Events[K]) => void): () => void;
19
+ dispose(): void;
20
+ };
21
+ declare function createHostClient<Api, Events extends EventMap = EventMap>(transport: Transport, opts: HostClientOptions): Client<Api, Events>;
22
+
23
+ export { type Client, EventMap, HostClient, type HostClientOptions, createHostClient };
package/dist/ui.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { T as Transport } from './protocol-Do0BJdeE.js';
2
+ import { E as EventMap, H as HostClient } from './types-BxSYAH_H.js';
3
+ export { C as Capability, G as GarretError, M as Manifest, S as Stream, f as StreamCall, g as defineConfig, h as defineManifest } from './types-BxSYAH_H.js';
4
+ export { G as GarretPlatform, c as GarretRuntime, d as SecretsApi, e as ServiceClient, f as StorageApi, S as SurfaceApi, a as SurfaceHandle, b as SurfaceOpenOptions, W as WindowControls, g as getGarret, h as getHostTransport, i as getInstanceId, j as getRuntime } from './platform-CZHtdUK0.js';
5
+
6
+ /**
7
+ * UI-side host client — a typed proxy over the wire (protocol.ts). Every call returns ONE handle
8
+ * that is both a Promise (plain methods `await` it) and a stream sink (stream methods use
9
+ * `.onData/.onEnd`). The `HostClient<Api>` TYPE narrows it per method from the `Api` return type —
10
+ * so authors never restate which methods stream. The host decides stream-vs-value from the runtime
11
+ * return; the client handles either response on the same id.
12
+ */
13
+ interface HostClientOptions {
14
+ /** Correlation namespace so two placements of a widget never cross wires. */
15
+ instanceId: string;
16
+ }
17
+ type Client<Api, Events extends EventMap> = HostClient<Api> & {
18
+ on<K extends keyof Events & string>(channel: K, cb: (payload: Events[K]) => void): () => void;
19
+ dispose(): void;
20
+ };
21
+ declare function createHostClient<Api, Events extends EventMap = EventMap>(transport: Transport, opts: HostClientOptions): Client<Api, Events>;
22
+
23
+ export { type Client, EventMap, HostClient, type HostClientOptions, createHostClient };
package/dist/ui.js ADDED
@@ -0,0 +1,3 @@
1
+ export { defineConfig, defineManifest } from './chunk-RZJO3X6O.js';
2
+ export { createHostClient, getGarret, getHostTransport, getInstanceId, getRuntime } from './chunk-ZJYHRC5C.js';
3
+ export { GarretError } from './chunk-ENIDFSMM.js';
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@garretapp/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Build extensions for Garret — one SDK for both web widgets and full-access native extensions. Host runtime (defineHost), UI client + React hooks. See docs/garret.html.",
5
+ "license": "MIT",
6
+ "author": "Sudharsan Selvaraj",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
11
+ "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
12
+ },
13
+ "./host": {
14
+ "import": { "types": "./dist/host.d.ts", "default": "./dist/host.js" },
15
+ "require": { "types": "./dist/host.d.cts", "default": "./dist/host.cjs" }
16
+ },
17
+ "./ui": {
18
+ "import": { "types": "./dist/ui.d.ts", "default": "./dist/ui.js" },
19
+ "require": { "types": "./dist/ui.d.cts", "default": "./dist/ui.cjs" }
20
+ },
21
+ "./react": {
22
+ "import": { "types": "./dist/react.d.ts", "default": "./dist/react.js" },
23
+ "require": { "types": "./dist/react.d.cts", "default": "./dist/react.cjs" }
24
+ }
25
+ },
26
+ "files": ["dist"],
27
+ "scripts": {
28
+ "build": "tsup src/index.ts src/host.ts src/ui.ts src/react.ts --format esm,cjs --dts --clean --treeshake",
29
+ "prepublishOnly": "npm run build"
30
+ },
31
+ "peerDependencies": {
32
+ "react": ">=18"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "react": { "optional": true }
36
+ }
37
+ }