@freeappstore/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/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @freeappstore/sdk
2
+
3
+ Browser SDK for free apps published on **freeappstore.online**.
4
+
5
+ ```bash
6
+ npm i @freeappstore/sdk
7
+ ```
8
+
9
+ ## Quick start
10
+
11
+ ```ts
12
+ import { initApp } from '@freeappstore/sdk';
13
+
14
+ const fas = initApp({ appId: 'my-app' });
15
+
16
+ // Capture the OAuth callback if we're returning from sign-in. Call this once
17
+ // at app start, before any UI that depends on auth state.
18
+ await fas.auth.init();
19
+
20
+ // React to sign-in / sign-out.
21
+ fas.auth.onChange((user) => {
22
+ console.log(user ? `Hello @${user.login}` : 'signed out');
23
+ });
24
+
25
+ // Trigger sign-in (redirects to GitHub).
26
+ document.querySelector('#signin')?.addEventListener('click', () => {
27
+ fas.auth.signIn();
28
+ });
29
+ ```
30
+
31
+ ## Modules
32
+
33
+ ### Auth
34
+
35
+ GitHub OAuth via redirect. Session persists in `localStorage`.
36
+
37
+ ```ts
38
+ fas.auth.user; // User | null
39
+ fas.auth.token; // string | null
40
+ fas.auth.signIn(); // redirects to GitHub
41
+ fas.auth.signOut(); // clears local session
42
+ fas.auth.onChange(cb); // returns Unsubscribe
43
+ await fas.auth.init(); // capture callback, must be called once
44
+ ```
45
+
46
+ ### Per-user KV
47
+
48
+ Per-user, per-app key-value store. Scoped to `(appId, userId)` server-side, so apps cannot read each other's data and users cannot read each other's data.
49
+
50
+ ```ts
51
+ await fas.kv.set('theme', { color: 'plum' });
52
+ const theme = await fas.kv.get<{ color: string }>('theme');
53
+ await fas.kv.delete('theme');
54
+ ```
55
+
56
+ Limits (server-enforced): max 1MB per user, max 100 keys per user, max 64KB per value. See [`docs/LIMITS.md`](../../docs/LIMITS.md) for the full list.
57
+
58
+ ### Realtime rooms
59
+
60
+ Durable-Object-backed WebSocket fan-out. Ephemeral — messages are not persisted. Sized for cursor presence, light collab, and Slither-style multiplayer (low state, high frequency).
61
+
62
+ ```ts
63
+ const room = fas.rooms.join('lobby');
64
+
65
+ room.onPeers((peers) => console.log('peers:', peers));
66
+ room.onMessage<{ text: string }>((msg) => {
67
+ console.log(msg.from, msg.data.text);
68
+ });
69
+
70
+ room.send({ text: 'hello' });
71
+ // later:
72
+ room.close();
73
+ ```
74
+
75
+ Limits (server-enforced): 32 peers per room, 100 msgs/sec per peer, 4KB per message, 24h idle eviction, 64 active rooms per app.
76
+
77
+ ## What's not in v0
78
+
79
+ - File uploads / R2 storage
80
+ - Push notifications
81
+ - Outbound HTTP / AI proxy
82
+ - Scheduled tasks / cron
83
+ - Email / magic links
84
+ - Search
85
+
86
+ These come in v0.1+ once we know what creators actually ask for. Pro-only modules (Stripe, paid quotas) are explicitly out of scope for `@freeappstore/sdk` — they live in `@proappstore/sdk`.
87
+
88
+ ## License
89
+
90
+ MIT.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { User, Unsubscribe } from './types.js';
2
+ export declare class Auth {
3
+ private readonly appId;
4
+ private readonly apiBase;
5
+ private session;
6
+ private listeners;
7
+ constructor(appId: string, apiBase: string);
8
+ get user(): User | null;
9
+ get token(): string | null;
10
+ onChange(listener: (user: User | null) => void): Unsubscribe;
11
+ /**
12
+ * Redirect-based GitHub OAuth. Opens the platform's hosted OAuth start URL,
13
+ * which redirects back to the current page with a session token in the hash.
14
+ */
15
+ signIn(): void;
16
+ signOut(): void;
17
+ /**
18
+ * Call this once at app start. If the page was loaded via an auth callback,
19
+ * captures the session from the URL hash and persists it.
20
+ */
21
+ init(): Promise<void>;
22
+ private fetchUser;
23
+ private readStorage;
24
+ private writeStorage;
25
+ private emit;
26
+ }
27
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AASpD,qBAAa,IAAI;IAKb,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,SAAS,CAA0C;gBAGxC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM;IAKlC,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,CAEtB;IAED,IAAI,KAAK,IAAI,MAAM,GAAG,IAAI,CAEzB;IAED,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,WAAW;IAK5D;;;OAGG;IACH,MAAM,IAAI,IAAI;IAQd,OAAO,IAAI,IAAI;IAMf;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAYb,SAAS;IAQvB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,IAAI;CAGb"}
package/dist/auth.js ADDED
@@ -0,0 +1,84 @@
1
+ const STORAGE_KEY = 'fas:session';
2
+ export class Auth {
3
+ appId;
4
+ apiBase;
5
+ session = null;
6
+ listeners = new Set();
7
+ constructor(appId, apiBase) {
8
+ this.appId = appId;
9
+ this.apiBase = apiBase;
10
+ this.session = this.readStorage();
11
+ }
12
+ get user() {
13
+ return this.session?.user ?? null;
14
+ }
15
+ get token() {
16
+ return this.session?.token ?? null;
17
+ }
18
+ onChange(listener) {
19
+ this.listeners.add(listener);
20
+ return () => this.listeners.delete(listener);
21
+ }
22
+ /**
23
+ * Redirect-based GitHub OAuth. Opens the platform's hosted OAuth start URL,
24
+ * which redirects back to the current page with a session token in the hash.
25
+ */
26
+ signIn() {
27
+ const returnTo = window.location.href;
28
+ const url = new URL('/v1/auth/github/start', this.apiBase);
29
+ url.searchParams.set('app_id', this.appId);
30
+ url.searchParams.set('return_to', returnTo);
31
+ window.location.assign(url.toString());
32
+ }
33
+ signOut() {
34
+ this.session = null;
35
+ window.localStorage.removeItem(STORAGE_KEY);
36
+ this.emit();
37
+ }
38
+ /**
39
+ * Call this once at app start. If the page was loaded via an auth callback,
40
+ * captures the session from the URL hash and persists it.
41
+ */
42
+ async init() {
43
+ if (typeof window === 'undefined')
44
+ return;
45
+ const hash = window.location.hash;
46
+ if (!hash.startsWith('#fas_session='))
47
+ return;
48
+ const token = decodeURIComponent(hash.slice('#fas_session='.length));
49
+ const user = await this.fetchUser(token);
50
+ this.session = { token, user };
51
+ this.writeStorage(this.session);
52
+ history.replaceState(null, '', window.location.pathname + window.location.search);
53
+ this.emit();
54
+ }
55
+ async fetchUser(token) {
56
+ const res = await fetch(new URL('/v1/auth/me', this.apiBase), {
57
+ headers: { Authorization: `Bearer ${token}` },
58
+ });
59
+ if (!res.ok)
60
+ throw new Error(`Auth failed: ${res.status}`);
61
+ return (await res.json());
62
+ }
63
+ readStorage() {
64
+ if (typeof window === 'undefined')
65
+ return null;
66
+ const raw = window.localStorage.getItem(STORAGE_KEY);
67
+ if (!raw)
68
+ return null;
69
+ try {
70
+ return JSON.parse(raw);
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ writeStorage(session) {
77
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(session));
78
+ }
79
+ emit() {
80
+ for (const listener of this.listeners)
81
+ listener(this.user);
82
+ }
83
+ }
84
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Auth } from './auth.js';
2
+ import { Kv } from './kv.js';
3
+ import { Rooms } from './rooms.js';
4
+ import type { FasInitOptions } from './types.js';
5
+ export type { User, FasInitOptions, Unsubscribe } from './types.js';
6
+ export type { Room, RoomMessage, RoomPeer, ConnectionState } from './rooms.js';
7
+ export declare class FreeAppStore {
8
+ readonly auth: Auth;
9
+ readonly kv: Kv;
10
+ readonly rooms: Rooms;
11
+ constructor(opts: FasInitOptions);
12
+ }
13
+ export declare function initApp(opts: FasInitOptions): FreeAppStore;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/E,qBAAa,YAAY;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;gBAEV,IAAI,EAAE,cAAc;CAMjC;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,YAAY,CAE1D"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ import { Auth } from './auth.js';
2
+ import { Kv } from './kv.js';
3
+ import { Rooms } from './rooms.js';
4
+ export class FreeAppStore {
5
+ auth;
6
+ kv;
7
+ rooms;
8
+ constructor(opts) {
9
+ const apiBase = opts.apiBase ?? 'https://api.freeappstore.online';
10
+ this.auth = new Auth(opts.appId, apiBase);
11
+ this.kv = new Kv(opts.appId, apiBase, this.auth);
12
+ this.rooms = new Rooms(opts.appId, apiBase, this.auth);
13
+ }
14
+ }
15
+ export function initApp(opts) {
16
+ return new FreeAppStore(opts);
17
+ }
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAMnC,MAAM,OAAO,YAAY;IACd,IAAI,CAAO;IACX,EAAE,CAAK;IACP,KAAK,CAAQ;IAEtB,YAAY,IAAoB;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,iCAAiC,CAAC;QAClE,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC;CACF;AAED,MAAM,UAAU,OAAO,CAAC,IAAoB;IAC1C,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC"}
package/dist/kv.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { Auth } from './auth.js';
2
+ /**
3
+ * Per-user key-value store, scoped to (appId, userId).
4
+ *
5
+ * Limits (enforced server-side):
6
+ * - max 1MB total per user
7
+ * - max 100 keys per user
8
+ * - max 64KB per value
9
+ */
10
+ export declare class Kv {
11
+ private readonly appId;
12
+ private readonly apiBase;
13
+ private readonly auth;
14
+ constructor(appId: string, apiBase: string, auth: Auth);
15
+ get<T = unknown>(key: string): Promise<T | null>;
16
+ set<T = unknown>(key: string, value: T): Promise<void>;
17
+ delete(key: string): Promise<void>;
18
+ private request;
19
+ }
20
+ //# sourceMappingURL=kv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../src/kv.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC;;;;;;;GAOG;AACH,qBAAa,EAAE;IAEX,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAFJ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI;IAGvB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAOhD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAatD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxC,OAAO,CAAC,OAAO;CAahB"}
package/dist/kv.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Per-user key-value store, scoped to (appId, userId).
3
+ *
4
+ * Limits (enforced server-side):
5
+ * - max 1MB total per user
6
+ * - max 100 keys per user
7
+ * - max 64KB per value
8
+ */
9
+ export class Kv {
10
+ appId;
11
+ apiBase;
12
+ auth;
13
+ constructor(appId, apiBase, auth) {
14
+ this.appId = appId;
15
+ this.apiBase = apiBase;
16
+ this.auth = auth;
17
+ }
18
+ async get(key) {
19
+ const res = await this.request('GET', key);
20
+ if (res.status === 404)
21
+ return null;
22
+ if (!res.ok)
23
+ throw new Error(`kv.get failed: ${res.status}`);
24
+ return (await res.json());
25
+ }
26
+ async set(key, value) {
27
+ // JSON.stringify(undefined) returns undefined, which would store an empty
28
+ // body and break later get() calls. Reject up front instead.
29
+ if (value === undefined) {
30
+ throw new Error('kv.set: value is undefined. Use kv.delete(key) to remove a key.');
31
+ }
32
+ const res = await this.request('PUT', key, JSON.stringify(value));
33
+ if (!res.ok) {
34
+ const text = await res.text();
35
+ throw new Error(`kv.set failed (${res.status}): ${text}`);
36
+ }
37
+ }
38
+ async delete(key) {
39
+ const res = await this.request('DELETE', key);
40
+ if (!res.ok && res.status !== 404) {
41
+ throw new Error(`kv.delete failed: ${res.status}`);
42
+ }
43
+ }
44
+ request(method, key, body) {
45
+ const token = this.auth.token;
46
+ if (!token)
47
+ throw new Error('Not signed in.');
48
+ const url = new URL(`/v1/apps/${encodeURIComponent(this.appId)}/kv/${encodeURIComponent(key)}`, this.apiBase);
49
+ const headers = { Authorization: `Bearer ${token}` };
50
+ if (body !== undefined)
51
+ headers['Content-Type'] = 'application/json';
52
+ const init = { method, headers };
53
+ if (body !== undefined)
54
+ init.body = body;
55
+ return fetch(url, init);
56
+ }
57
+ }
58
+ //# sourceMappingURL=kv.js.map
package/dist/kv.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv.js","sourceRoot":"","sources":["../src/kv.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,OAAO,EAAE;IAEM;IACA;IACA;IAHnB,YACmB,KAAa,EACb,OAAe,EACf,IAAU;QAFV,UAAK,GAAL,KAAK,CAAQ;QACb,YAAO,GAAP,OAAO,CAAQ;QACf,SAAI,GAAJ,IAAI,CAAM;IAC1B,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAc,GAAW;QAChC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QACpC,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAM,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,GAAG,CAAc,GAAW,EAAE,KAAQ;QAC1C,0EAA0E;QAC1E,6DAA6D;QAC7D,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACrF,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,MAAc,EAAE,GAAW,EAAE,IAAa;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAC9B,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CACjB,YAAY,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,GAAG,CAAC,EAAE,EAC1E,IAAI,CAAC,OAAO,CACb,CAAC;QACF,MAAM,OAAO,GAA2B,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC;QAC7E,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QACrE,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC9C,IAAI,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACzC,OAAO,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF"}
@@ -0,0 +1,60 @@
1
+ import type { Auth } from './auth.js';
2
+ import type { Unsubscribe } from './types.js';
3
+ /**
4
+ * Light realtime room — Durable-Object-backed WebSocket fan-out.
5
+ *
6
+ * Use cases: cursor presence, low-state multiplayer (Slither-style), chat-light.
7
+ * Not a multiplayer game server. Messages are not persisted.
8
+ *
9
+ * Limits (enforced server-side):
10
+ * - max 32 concurrent peers per room
11
+ * - max 100 messages/sec per peer
12
+ * - max 4KB per message
13
+ * - max 64 active rooms per app (LRU evicts the oldest)
14
+ * - rooms idle for 24h are auto-evicted
15
+ */
16
+ export interface RoomPeer {
17
+ uid: string;
18
+ login: string;
19
+ }
20
+ export interface RoomMessage<T = unknown> {
21
+ from: RoomPeer;
22
+ data: T;
23
+ at: number;
24
+ }
25
+ export type ConnectionState = 'connecting' | 'open' | 'closed' | 'error';
26
+ export declare class Rooms {
27
+ private readonly appId;
28
+ private readonly apiBase;
29
+ private readonly auth;
30
+ constructor(appId: string, apiBase: string, auth: Auth);
31
+ join(roomId: string): Room;
32
+ }
33
+ export declare class Room {
34
+ private readonly appId;
35
+ private readonly apiBase;
36
+ private readonly auth;
37
+ private readonly roomId;
38
+ private socket;
39
+ private listeners;
40
+ private peerListeners;
41
+ private stateListeners;
42
+ private peers;
43
+ private connectionState;
44
+ private reconnectAttempt;
45
+ private reconnectTimer;
46
+ private explicitlyClosed;
47
+ constructor(appId: string, apiBase: string, auth: Auth, roomId: string);
48
+ /** Current connection state. */
49
+ get state(): ConnectionState;
50
+ send<T>(data: T): void;
51
+ onMessage<T = unknown>(listener: (msg: RoomMessage<T>) => void): Unsubscribe;
52
+ onPeers(listener: (peers: RoomPeer[]) => void): Unsubscribe;
53
+ onConnectionState(listener: (state: ConnectionState) => void): Unsubscribe;
54
+ /** Permanently close the room. Stops any pending reconnect. */
55
+ close(): void;
56
+ private connect;
57
+ private scheduleReconnect;
58
+ private setState;
59
+ }
60
+ //# sourceMappingURL=rooms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../src/rooms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;IACR,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEzE,qBAAa,KAAK;IAEd,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAFJ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI;IAG7B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAG3B;AAKD,qBAAa,IAAI;IAYb,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAdzB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,gBAAgB,CAAS;gBAGd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM;IAKjC,gCAAgC;IAChC,IAAI,KAAK,IAAI,eAAe,CAE3B;IAED,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI;IAKtB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,WAAW;IAK5E,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,GAAG,WAAW;IAM3D,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,WAAW;IAM1E,+DAA+D;IAC/D,KAAK,IAAI,IAAI;IAcb,OAAO,CAAC,OAAO;IA4Df,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,QAAQ;CAKjB"}
package/dist/rooms.js ADDED
@@ -0,0 +1,151 @@
1
+ export class Rooms {
2
+ appId;
3
+ apiBase;
4
+ auth;
5
+ constructor(appId, apiBase, auth) {
6
+ this.appId = appId;
7
+ this.apiBase = apiBase;
8
+ this.auth = auth;
9
+ }
10
+ join(roomId) {
11
+ return new Room(this.appId, this.apiBase, this.auth, roomId);
12
+ }
13
+ }
14
+ const RECONNECT_BASE_MS = 1000;
15
+ const RECONNECT_MAX_MS = 30_000;
16
+ export class Room {
17
+ appId;
18
+ apiBase;
19
+ auth;
20
+ roomId;
21
+ socket = null;
22
+ listeners = new Set();
23
+ peerListeners = new Set();
24
+ stateListeners = new Set();
25
+ peers = [];
26
+ connectionState = 'connecting';
27
+ reconnectAttempt = 0;
28
+ reconnectTimer = null;
29
+ explicitlyClosed = false;
30
+ constructor(appId, apiBase, auth, roomId) {
31
+ this.appId = appId;
32
+ this.apiBase = apiBase;
33
+ this.auth = auth;
34
+ this.roomId = roomId;
35
+ this.connect();
36
+ }
37
+ /** Current connection state. */
38
+ get state() {
39
+ return this.connectionState;
40
+ }
41
+ send(data) {
42
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
43
+ return;
44
+ this.socket.send(JSON.stringify({ kind: 'msg', data }));
45
+ }
46
+ onMessage(listener) {
47
+ this.listeners.add(listener);
48
+ return () => this.listeners.delete(listener);
49
+ }
50
+ onPeers(listener) {
51
+ this.peerListeners.add(listener);
52
+ listener(this.peers);
53
+ return () => this.peerListeners.delete(listener);
54
+ }
55
+ onConnectionState(listener) {
56
+ this.stateListeners.add(listener);
57
+ listener(this.connectionState);
58
+ return () => this.stateListeners.delete(listener);
59
+ }
60
+ /** Permanently close the room. Stops any pending reconnect. */
61
+ close() {
62
+ this.explicitlyClosed = true;
63
+ if (this.reconnectTimer !== null) {
64
+ clearTimeout(this.reconnectTimer);
65
+ this.reconnectTimer = null;
66
+ }
67
+ this.socket?.close();
68
+ this.socket = null;
69
+ this.setState('closed');
70
+ this.listeners.clear();
71
+ this.peerListeners.clear();
72
+ this.stateListeners.clear();
73
+ }
74
+ connect() {
75
+ const token = this.auth.token;
76
+ if (!token) {
77
+ // Auth state may have changed (sign-out). Stop trying.
78
+ this.setState('closed');
79
+ return;
80
+ }
81
+ this.setState('connecting');
82
+ const url = new URL(`/v1/apps/${encodeURIComponent(this.appId)}/rooms/${encodeURIComponent(this.roomId)}`, this.apiBase);
83
+ url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
84
+ url.searchParams.set('token', token);
85
+ const socket = new WebSocket(url.toString());
86
+ this.socket = socket;
87
+ socket.addEventListener('open', () => {
88
+ this.reconnectAttempt = 0;
89
+ this.setState('open');
90
+ });
91
+ socket.addEventListener('message', (ev) => {
92
+ try {
93
+ const parsed = JSON.parse(ev.data);
94
+ if (parsed.kind === 'msg') {
95
+ for (const l of this.listeners) {
96
+ l({ from: parsed.from, data: parsed.data, at: parsed.at });
97
+ }
98
+ }
99
+ else if (parsed.kind === 'peers') {
100
+ this.peers = parsed.peers;
101
+ for (const l of this.peerListeners)
102
+ l(this.peers);
103
+ }
104
+ }
105
+ catch {
106
+ // Ignore malformed frames — server should never send them; if it
107
+ // does, dropping is the right move and an error frame would have
108
+ // come through `kind: 'error'` instead.
109
+ }
110
+ });
111
+ socket.addEventListener('close', () => {
112
+ // Only one of close/error fires the reconnect; we use close because
113
+ // it always fires, even after an error, and is the canonical signal.
114
+ if (this.socket === socket)
115
+ this.socket = null;
116
+ if (this.explicitlyClosed)
117
+ return;
118
+ this.setState('closed');
119
+ this.scheduleReconnect();
120
+ });
121
+ socket.addEventListener('error', () => {
122
+ // We let the close handler do the actual reconnect logic. error is
123
+ // informational and may or may not be followed by close (it always is
124
+ // in browsers per spec).
125
+ this.setState('error');
126
+ });
127
+ }
128
+ scheduleReconnect() {
129
+ if (this.reconnectTimer !== null)
130
+ return;
131
+ // Exponential backoff capped at 30s, with up to 1s of jitter so a
132
+ // backend hiccup doesn't produce a thundering herd of reconnects.
133
+ const backoff = Math.min(RECONNECT_MAX_MS, RECONNECT_BASE_MS * 2 ** this.reconnectAttempt);
134
+ const jitter = Math.random() * 1000;
135
+ this.reconnectAttempt++;
136
+ this.reconnectTimer = setTimeout(() => {
137
+ this.reconnectTimer = null;
138
+ if (this.explicitlyClosed)
139
+ return;
140
+ this.connect();
141
+ }, backoff + jitter);
142
+ }
143
+ setState(state) {
144
+ if (this.connectionState === state)
145
+ return;
146
+ this.connectionState = state;
147
+ for (const l of this.stateListeners)
148
+ l(state);
149
+ }
150
+ }
151
+ //# sourceMappingURL=rooms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rooms.js","sourceRoot":"","sources":["../src/rooms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface User {
2
+ id: string;
3
+ login: string;
4
+ avatarUrl: string | null;
5
+ }
6
+ export interface FasInitOptions {
7
+ appId: string;
8
+ apiBase?: string;
9
+ }
10
+ export type Unsubscribe = () => void;
11
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@freeappstore/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Browser SDK for free apps on freeappstore.online — auth, per-user KV, light realtime rooms.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "homepage": "https://github.com/freeappstore-online/sdk#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/freeappstore-online/sdk.git",
19
+ "directory": "packages/sdk"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/freeappstore-online/sdk/issues"
23
+ },
24
+ "keywords": [
25
+ "freeappstore",
26
+ "sdk",
27
+ "auth",
28
+ "kv",
29
+ "rooms",
30
+ "websocket",
31
+ "github-oauth",
32
+ "cloudflare-workers"
33
+ ],
34
+ "files": [
35
+ "dist",
36
+ "README.md"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "typecheck": "tsc --noEmit"
41
+ },
42
+ "devDependencies": {
43
+ "typescript": "^5.7.0"
44
+ },
45
+ "sideEffects": false
46
+ }