@freeappstore/sdk 0.5.0 → 0.5.1
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 +22 -1
- package/package.json +1 -1
- package/dist/auth.test.d.ts +0 -2
- package/dist/auth.test.d.ts.map +0 -1
- package/dist/auth.test.js +0 -151
- package/dist/auth.test.js.map +0 -1
- package/dist/kv.test.d.ts +0 -2
- package/dist/kv.test.d.ts.map +0 -1
- package/dist/kv.test.js +0 -66
- package/dist/kv.test.js.map +0 -1
- package/dist/proxy.test.d.ts +0 -2
- package/dist/proxy.test.d.ts.map +0 -1
- package/dist/proxy.test.js +0 -77
- package/dist/proxy.test.js.map +0 -1
- package/dist/rooms.test.d.ts +0 -2
- package/dist/rooms.test.d.ts.map +0 -1
- package/dist/rooms.test.js +0 -260
- package/dist/rooms.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ fas.auth.user; // User | null
|
|
|
41
41
|
fas.auth.token; // string | null
|
|
42
42
|
fas.auth.signIn(); // redirects to GitHub
|
|
43
43
|
fas.auth.signOut(); // clears local session
|
|
44
|
-
fas.auth.onChange(cb); // returns Unsubscribe
|
|
44
|
+
fas.auth.onChange(cb); // fires immediately + on change, returns Unsubscribe
|
|
45
45
|
await fas.auth.init(); // capture callback, must be called once
|
|
46
46
|
```
|
|
47
47
|
|
|
@@ -57,10 +57,31 @@ await fas.kv.delete('theme');
|
|
|
57
57
|
const allKeys = await fas.kv.list();
|
|
58
58
|
const noteKeys = await fas.kv.list({ prefix: 'note:' });
|
|
59
59
|
const notes = await fas.kv.getMany<Note>(noteKeys);
|
|
60
|
+
|
|
61
|
+
// All methods accept { signal } for AbortController cleanup
|
|
62
|
+
const ctrl = new AbortController();
|
|
63
|
+
await fas.kv.get('key', { signal: ctrl.signal });
|
|
60
64
|
```
|
|
61
65
|
|
|
62
66
|
Limits (server-enforced): max 1MB per user, max 100 keys per user, max 64KB per value.
|
|
63
67
|
|
|
68
|
+
### Shared counters
|
|
69
|
+
|
|
70
|
+
App-wide atomic counters — not user-scoped. Anyone can read; authenticated users can increment. Use for vote tallies, view counts, leaderboards.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// Read (no auth required)
|
|
74
|
+
const all = await fas.counters.list(); // { likes: 5, views: 100 }
|
|
75
|
+
const views = await fas.counters.get('views'); // 100
|
|
76
|
+
|
|
77
|
+
// Increment (auth required)
|
|
78
|
+
const newVal = await fas.counters.increment('likes'); // +1, returns new value
|
|
79
|
+
const newVal2 = await fas.counters.increment('score', 10); // +10
|
|
80
|
+
await fas.counters.increment('lives', -1); // decrement
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Limits: max 1000 counters per app, increment range -1000 to +1000 per call.
|
|
84
|
+
|
|
64
85
|
### Realtime rooms
|
|
65
86
|
|
|
66
87
|
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).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freeappstore/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Browser SDK for free apps on freeappstore.online — auth, per-user KV, light realtime rooms, secret-injecting proxy for third-party APIs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/dist/auth.test.d.ts
DELETED
package/dist/auth.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../src/auth.test.ts"],"names":[],"mappings":""}
|
package/dist/auth.test.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { Auth } from "./auth.js";
|
|
3
|
-
function makeWindow(initial) {
|
|
4
|
-
const url = new URL(initial.href);
|
|
5
|
-
if (initial.hash)
|
|
6
|
-
url.hash = initial.hash;
|
|
7
|
-
const assigns = [];
|
|
8
|
-
const replaces = [];
|
|
9
|
-
const storage = {
|
|
10
|
-
store: new Map(),
|
|
11
|
-
getItem: (k) => storage.store.get(k) ?? null,
|
|
12
|
-
setItem: (k, v) => void storage.store.set(k, v),
|
|
13
|
-
removeItem: (k) => void storage.store.delete(k),
|
|
14
|
-
};
|
|
15
|
-
return {
|
|
16
|
-
window: {
|
|
17
|
-
location: {
|
|
18
|
-
href: url.toString(),
|
|
19
|
-
hash: url.hash,
|
|
20
|
-
pathname: url.pathname,
|
|
21
|
-
search: url.search,
|
|
22
|
-
assign: (u) => assigns.push(u),
|
|
23
|
-
},
|
|
24
|
-
localStorage: storage,
|
|
25
|
-
history: {
|
|
26
|
-
replaceState: (_s, _t, u) => replaces.push(u),
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
assigns,
|
|
30
|
-
replaces,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
// happy-dom-style globals; we install per-test
|
|
35
|
-
delete globalThis.window;
|
|
36
|
-
delete globalThis.history;
|
|
37
|
-
delete globalThis.fetch;
|
|
38
|
-
});
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
delete globalThis.window;
|
|
41
|
-
delete globalThis.history;
|
|
42
|
-
delete globalThis.fetch;
|
|
43
|
-
});
|
|
44
|
-
describe("Auth.signIn", () => {
|
|
45
|
-
it("strips the hash from return_to so the OAuth callback does not clobber app router state", () => {
|
|
46
|
-
const { window, assigns } = makeWindow({
|
|
47
|
-
href: "https://app.freeappstore.online/dashboard?team=42#/settings/profile",
|
|
48
|
-
});
|
|
49
|
-
globalThis.window = window;
|
|
50
|
-
const auth = new Auth("demo", "https://api.freeappstore.online");
|
|
51
|
-
auth.signIn();
|
|
52
|
-
expect(assigns).toHaveLength(1);
|
|
53
|
-
const target = new URL(assigns[0]);
|
|
54
|
-
const returnTo = target.searchParams.get("return_to");
|
|
55
|
-
expect(returnTo).toBe("https://app.freeappstore.online/dashboard?team=42");
|
|
56
|
-
// No #/settings/profile carried into return_to.
|
|
57
|
-
expect(returnTo).not.toContain("#");
|
|
58
|
-
});
|
|
59
|
-
it("still preserves pathname + querystring", () => {
|
|
60
|
-
const { window, assigns } = makeWindow({ href: "https://app.example/page?x=1" });
|
|
61
|
-
globalThis.window = window;
|
|
62
|
-
const auth = new Auth("demo", "https://api.example");
|
|
63
|
-
auth.signIn();
|
|
64
|
-
const returnTo = new URL(assigns[0]).searchParams.get("return_to");
|
|
65
|
-
expect(returnTo).toBe("https://app.example/page?x=1");
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
describe("Auth.init — hash handling", () => {
|
|
69
|
-
it("clears the hash and stays signed out when the auth fetch fails (no crash, no stuck state)", async () => {
|
|
70
|
-
const { window, replaces } = makeWindow({
|
|
71
|
-
href: "https://app.example/",
|
|
72
|
-
hash: "#fas_session=bad-token",
|
|
73
|
-
});
|
|
74
|
-
globalThis.window = window;
|
|
75
|
-
globalThis.history = window.history;
|
|
76
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response("", { status: 401 }));
|
|
77
|
-
const auth = new Auth("demo", "https://api.example");
|
|
78
|
-
// init() no longer throws — it silently remains signed out.
|
|
79
|
-
await auth.init();
|
|
80
|
-
// The hash was cleared before the fetch, so reload won't retry.
|
|
81
|
-
expect(replaces).toHaveLength(1);
|
|
82
|
-
expect(replaces[0]).toBe("/");
|
|
83
|
-
expect(auth.user).toBeNull();
|
|
84
|
-
});
|
|
85
|
-
it("captures session and clears hash on successful auth callback", async () => {
|
|
86
|
-
const { window, replaces } = makeWindow({
|
|
87
|
-
href: "https://app.example/dashboard",
|
|
88
|
-
hash: "#fas_session=good-token",
|
|
89
|
-
});
|
|
90
|
-
globalThis.window = window;
|
|
91
|
-
globalThis.history = window.history;
|
|
92
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ id: "gh:1", login: "alice", avatarUrl: null }), {
|
|
93
|
-
status: 200,
|
|
94
|
-
}));
|
|
95
|
-
const auth = new Auth("demo", "https://api.example");
|
|
96
|
-
await auth.init();
|
|
97
|
-
expect(auth.user?.login).toBe("alice");
|
|
98
|
-
expect(auth.token).toBe("good-token");
|
|
99
|
-
expect(window.localStorage.store.get("fas:session")).toContain("good-token");
|
|
100
|
-
expect(replaces[0]).toBe("/dashboard");
|
|
101
|
-
});
|
|
102
|
-
it("is a no-op when no fas_session hash is present", async () => {
|
|
103
|
-
const { window, replaces } = makeWindow({ href: "https://app.example/" });
|
|
104
|
-
globalThis.window = window;
|
|
105
|
-
globalThis.history = window.history;
|
|
106
|
-
const fetchSpy = vi.fn();
|
|
107
|
-
globalThis.fetch = fetchSpy;
|
|
108
|
-
const auth = new Auth("demo", "https://api.example");
|
|
109
|
-
await auth.init();
|
|
110
|
-
expect(fetchSpy).not.toHaveBeenCalled();
|
|
111
|
-
expect(replaces).toHaveLength(0);
|
|
112
|
-
expect(auth.user).toBeNull();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
describe("Auth — storage round-trip", () => {
|
|
116
|
-
it("reads cached session from localStorage in the constructor", () => {
|
|
117
|
-
const { window } = makeWindow({ href: "https://app.example/" });
|
|
118
|
-
window.localStorage.store.set("fas:session", JSON.stringify({
|
|
119
|
-
token: "cached",
|
|
120
|
-
user: { id: "gh:7", login: "returning-user", avatarUrl: null },
|
|
121
|
-
}));
|
|
122
|
-
globalThis.window = window;
|
|
123
|
-
const auth = new Auth("demo", "https://api.example");
|
|
124
|
-
expect(auth.user?.login).toBe("returning-user");
|
|
125
|
-
expect(auth.token).toBe("cached");
|
|
126
|
-
});
|
|
127
|
-
it("signOut clears storage and notifies listeners", () => {
|
|
128
|
-
const { window } = makeWindow({ href: "https://app.example/" });
|
|
129
|
-
window.localStorage.store.set("fas:session", JSON.stringify({
|
|
130
|
-
token: "cached",
|
|
131
|
-
user: { id: "gh:7", login: "u", avatarUrl: null },
|
|
132
|
-
}));
|
|
133
|
-
globalThis.window = window;
|
|
134
|
-
const auth = new Auth("demo", "https://api.example");
|
|
135
|
-
const seen = [];
|
|
136
|
-
auth.onChange((u) => seen.push(u));
|
|
137
|
-
auth.signOut();
|
|
138
|
-
expect(auth.user).toBeNull();
|
|
139
|
-
expect(window.localStorage.store.has("fas:session")).toBe(false);
|
|
140
|
-
// onChange fires immediately with current user on subscribe, then null on signOut
|
|
141
|
-
expect(seen).toEqual([{ id: "gh:7", login: "u", avatarUrl: null }, null]);
|
|
142
|
-
});
|
|
143
|
-
it("returns null when localStorage has corrupt JSON (does not crash)", () => {
|
|
144
|
-
const { window } = makeWindow({ href: "https://app.example/" });
|
|
145
|
-
window.localStorage.store.set("fas:session", "{ not: json");
|
|
146
|
-
globalThis.window = window;
|
|
147
|
-
const auth = new Auth("demo", "https://api.example");
|
|
148
|
-
expect(auth.user).toBeNull();
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
//# sourceMappingURL=auth.test.js.map
|
package/dist/auth.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../src/auth.test.ts"],"names":[],"mappings":"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"}
|
package/dist/kv.test.d.ts
DELETED
package/dist/kv.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"kv.test.d.ts","sourceRoot":"","sources":["../src/kv.test.ts"],"names":[],"mappings":""}
|
package/dist/kv.test.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { Kv } from "./kv.js";
|
|
3
|
-
function fakeAuth(token) {
|
|
4
|
-
return { token };
|
|
5
|
-
}
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
// 204/304 require a null body per WHATWG; an empty string would throw.
|
|
8
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response(null, { status: 204 }));
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
delete globalThis.fetch;
|
|
12
|
-
});
|
|
13
|
-
describe("Kv key validation", () => {
|
|
14
|
-
const kv = (token = "tok") => new Kv("demo", "https://api.example", fakeAuth(token));
|
|
15
|
-
it("rejects empty-string key (regression: would yield /kv/ URL and a confusing 404)", async () => {
|
|
16
|
-
await expect(kv().get("")).rejects.toThrow(/non-empty/);
|
|
17
|
-
await expect(kv().set("", 1)).rejects.toThrow(/non-empty/);
|
|
18
|
-
await expect(kv().delete("")).rejects.toThrow(/non-empty/);
|
|
19
|
-
});
|
|
20
|
-
it("rejects keys longer than 128 chars", async () => {
|
|
21
|
-
const long = "x".repeat(129);
|
|
22
|
-
await expect(kv().set(long, 1)).rejects.toThrow(/128/);
|
|
23
|
-
});
|
|
24
|
-
it("accepts a 128-char key (boundary)", async () => {
|
|
25
|
-
const ok = "x".repeat(128);
|
|
26
|
-
await expect(kv().set(ok, 1)).resolves.toBeUndefined();
|
|
27
|
-
});
|
|
28
|
-
it("accepts ordinary keys", async () => {
|
|
29
|
-
await expect(kv().set("foo", 1)).resolves.toBeUndefined();
|
|
30
|
-
await expect(kv().set("a/b", 1)).resolves.toBeUndefined();
|
|
31
|
-
await expect(kv().set("color-preference", 1)).resolves.toBeUndefined();
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
describe("Kv.set value validation", () => {
|
|
35
|
-
const kv = () => new Kv("demo", "https://api.example", fakeAuth("tok"));
|
|
36
|
-
it("rejects undefined (regression: would store empty body, then break get)", async () => {
|
|
37
|
-
await expect(kv().set("k", undefined)).rejects.toThrow(/undefined/);
|
|
38
|
-
});
|
|
39
|
-
it("accepts null as a stored value", async () => {
|
|
40
|
-
await expect(kv().set("k", null)).resolves.toBeUndefined();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
describe("Kv auth gate", () => {
|
|
44
|
-
it("throws Not signed in when no session token", async () => {
|
|
45
|
-
const k = new Kv("demo", "https://api.example", fakeAuth(null));
|
|
46
|
-
await expect(k.get("foo")).rejects.toThrow(/not signed in/i);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe("Kv.get behavior", () => {
|
|
50
|
-
it("returns null on 404, the missing-key signal", async () => {
|
|
51
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response("", { status: 404 }));
|
|
52
|
-
const k = new Kv("demo", "https://api.example", fakeAuth("tok"));
|
|
53
|
-
expect(await k.get("missing")).toBeNull();
|
|
54
|
-
});
|
|
55
|
-
it("parses JSON on 200", async () => {
|
|
56
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ x: 1 }), { status: 200 }));
|
|
57
|
-
const k = new Kv("demo", "https://api.example", fakeAuth("tok"));
|
|
58
|
-
expect(await k.get("foo")).toEqual({ x: 1 });
|
|
59
|
-
});
|
|
60
|
-
it("throws on non-404 errors", async () => {
|
|
61
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response("", { status: 500 }));
|
|
62
|
-
const k = new Kv("demo", "https://api.example", fakeAuth("tok"));
|
|
63
|
-
await expect(k.get("foo")).rejects.toThrow(/500/);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
//# sourceMappingURL=kv.test.js.map
|
package/dist/kv.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"kv.test.js","sourceRoot":"","sources":["../src/kv.test.ts"],"names":[],"mappings":"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"}
|
package/dist/proxy.test.d.ts
DELETED
package/dist/proxy.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.test.d.ts","sourceRoot":"","sources":["../src/proxy.test.ts"],"names":[],"mappings":""}
|
package/dist/proxy.test.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { ApiProxy, normalizeTarget } from "./proxy.js";
|
|
3
|
-
describe("normalizeTarget", () => {
|
|
4
|
-
it("passes through bare host/path form", () => {
|
|
5
|
-
expect(normalizeTarget("api.example.com/v1/x")).toBe("api.example.com/v1/x");
|
|
6
|
-
});
|
|
7
|
-
it("strips https:// and http:// prefixes", () => {
|
|
8
|
-
expect(normalizeTarget("https://api.example.com/v1/x")).toBe("api.example.com/v1/x");
|
|
9
|
-
expect(normalizeTarget("http://api.example.com/v1/x")).toBe("api.example.com/v1/x");
|
|
10
|
-
});
|
|
11
|
-
it("matches the scheme case-insensitively", () => {
|
|
12
|
-
expect(normalizeTarget("HTTPS://api.example.com/v1/x")).toBe("api.example.com/v1/x");
|
|
13
|
-
expect(normalizeTarget("Http://api.example.com/v1/x")).toBe("api.example.com/v1/x");
|
|
14
|
-
});
|
|
15
|
-
it("strips leading slashes from bare form", () => {
|
|
16
|
-
expect(normalizeTarget("//api.example.com/v1")).toBe("api.example.com/v1");
|
|
17
|
-
});
|
|
18
|
-
it("preserves query strings", () => {
|
|
19
|
-
expect(normalizeTarget("api.example.com/x?a=1&b=2")).toBe("api.example.com/x?a=1&b=2");
|
|
20
|
-
});
|
|
21
|
-
it("rejects non-http(s) schemes", () => {
|
|
22
|
-
expect(() => normalizeTarget("ftp://api.example.com/x")).toThrow(/http\(s\)/);
|
|
23
|
-
expect(() => normalizeTarget("javascript://x")).toThrow(/http\(s\)/);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe("Proxy.fetch", () => {
|
|
27
|
-
it("throws when not signed in", async () => {
|
|
28
|
-
const auth = { token: null };
|
|
29
|
-
const proxy = new ApiProxy("weather", "https://api.example.com", auth);
|
|
30
|
-
await expect(proxy.fetch("api.openweathermap.org/data/2.5/weather")).rejects.toThrow(/not signed in/);
|
|
31
|
-
});
|
|
32
|
-
it("builds the right URL and forwards Bearer auth", async () => {
|
|
33
|
-
const auth = { token: "tok-123" };
|
|
34
|
-
const proxy = new ApiProxy("weather", "https://api.example.com", auth);
|
|
35
|
-
const captured = {};
|
|
36
|
-
const origFetch = globalThis.fetch;
|
|
37
|
-
globalThis.fetch = vi.fn(async (url, init) => {
|
|
38
|
-
captured.url = String(url);
|
|
39
|
-
captured.init = init;
|
|
40
|
-
return new Response("ok");
|
|
41
|
-
});
|
|
42
|
-
try {
|
|
43
|
-
await proxy.fetch("api.openweathermap.org/data/2.5/weather?q=London");
|
|
44
|
-
expect(captured.url).toBe("https://api.example.com/v1/apps/weather/proxy/api.openweathermap.org/data/2.5/weather?q=London");
|
|
45
|
-
const h = new Headers(captured.init?.headers);
|
|
46
|
-
expect(h.get("authorization")).toBe("Bearer tok-123");
|
|
47
|
-
}
|
|
48
|
-
finally {
|
|
49
|
-
globalThis.fetch = origFetch;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
it("preserves caller-supplied headers and method", async () => {
|
|
53
|
-
const auth = { token: "tok" };
|
|
54
|
-
const proxy = new ApiProxy("weather", "https://api.example.com", auth);
|
|
55
|
-
const captured = {};
|
|
56
|
-
const origFetch = globalThis.fetch;
|
|
57
|
-
globalThis.fetch = vi.fn(async (_url, init) => {
|
|
58
|
-
captured.init = init;
|
|
59
|
-
return new Response("ok");
|
|
60
|
-
});
|
|
61
|
-
try {
|
|
62
|
-
await proxy.fetch("api.example.com/v1/x", {
|
|
63
|
-
method: "POST",
|
|
64
|
-
headers: { "Content-Type": "application/json" },
|
|
65
|
-
body: "{}",
|
|
66
|
-
});
|
|
67
|
-
expect(captured.init?.method).toBe("POST");
|
|
68
|
-
const h = new Headers(captured.init?.headers);
|
|
69
|
-
expect(h.get("content-type")).toBe("application/json");
|
|
70
|
-
expect(h.get("authorization")).toBe("Bearer tok");
|
|
71
|
-
}
|
|
72
|
-
finally {
|
|
73
|
-
globalThis.fetch = origFetch;
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
//# sourceMappingURL=proxy.test.js.map
|
package/dist/proxy.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.test.js","sourceRoot":"","sources":["../src/proxy.test.ts"],"names":[],"mappings":"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"}
|
package/dist/rooms.test.d.ts
DELETED
package/dist/rooms.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rooms.test.d.ts","sourceRoot":"","sources":["../src/rooms.test.ts"],"names":[],"mappings":""}
|
package/dist/rooms.test.js
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { Rooms } from "./rooms.js";
|
|
3
|
-
/**
|
|
4
|
-
* Minimal WebSocket stand-in. Tests poke at `created[i]` to drive open/
|
|
5
|
-
* message/error/close events imperatively.
|
|
6
|
-
*/
|
|
7
|
-
class FakeWebSocket {
|
|
8
|
-
static CONNECTING = 0;
|
|
9
|
-
static OPEN = 1;
|
|
10
|
-
static CLOSING = 2;
|
|
11
|
-
static CLOSED = 3;
|
|
12
|
-
readyState = FakeWebSocket.CONNECTING;
|
|
13
|
-
sent = [];
|
|
14
|
-
url;
|
|
15
|
-
listeners = new Map();
|
|
16
|
-
constructor(url) {
|
|
17
|
-
this.url = url.toString();
|
|
18
|
-
created.push(this);
|
|
19
|
-
}
|
|
20
|
-
addEventListener(type, fn) {
|
|
21
|
-
if (!this.listeners.has(type))
|
|
22
|
-
this.listeners.set(type, new Set());
|
|
23
|
-
this.listeners.get(type).add(fn);
|
|
24
|
-
}
|
|
25
|
-
send(data) {
|
|
26
|
-
this.sent.push(data);
|
|
27
|
-
}
|
|
28
|
-
close() {
|
|
29
|
-
if (this.readyState !== FakeWebSocket.CLOSED) {
|
|
30
|
-
this.readyState = FakeWebSocket.CLOSED;
|
|
31
|
-
this.fire("close", {});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
triggerOpen() {
|
|
35
|
-
this.readyState = FakeWebSocket.OPEN;
|
|
36
|
-
this.fire("open", {});
|
|
37
|
-
}
|
|
38
|
-
triggerMessage(data) {
|
|
39
|
-
this.fire("message", { data });
|
|
40
|
-
}
|
|
41
|
-
triggerError() {
|
|
42
|
-
this.fire("error", {});
|
|
43
|
-
}
|
|
44
|
-
triggerClose() {
|
|
45
|
-
this.readyState = FakeWebSocket.CLOSED;
|
|
46
|
-
this.fire("close", {});
|
|
47
|
-
}
|
|
48
|
-
fire(type, ev) {
|
|
49
|
-
for (const fn of this.listeners.get(type) ?? [])
|
|
50
|
-
fn(ev);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
let created = [];
|
|
54
|
-
beforeEach(() => {
|
|
55
|
-
created = [];
|
|
56
|
-
globalThis.WebSocket = FakeWebSocket;
|
|
57
|
-
});
|
|
58
|
-
afterEach(() => {
|
|
59
|
-
delete globalThis.WebSocket;
|
|
60
|
-
vi.useRealTimers();
|
|
61
|
-
});
|
|
62
|
-
function fakeAuth(token) {
|
|
63
|
-
return { token };
|
|
64
|
-
}
|
|
65
|
-
function newRoom(token = "tok") {
|
|
66
|
-
const rooms = new Rooms("demo", "https://api.example", fakeAuth(token));
|
|
67
|
-
return rooms.join("lobby");
|
|
68
|
-
}
|
|
69
|
-
describe("Room.connect — initial state", () => {
|
|
70
|
-
it("starts in connecting state and creates a WebSocket immediately", () => {
|
|
71
|
-
const room = newRoom();
|
|
72
|
-
expect(created).toHaveLength(1);
|
|
73
|
-
expect(room.state).toBe("connecting");
|
|
74
|
-
expect(created[0].url).toMatch(/wss:\/\/api\.example\/v1\/apps\/demo\/rooms\/lobby/);
|
|
75
|
-
expect(created[0].url).toContain("token=tok");
|
|
76
|
-
});
|
|
77
|
-
it("encodes appId and roomId in the URL path", () => {
|
|
78
|
-
const rooms = new Rooms("app%name", "https://api.example", fakeAuth("tok"));
|
|
79
|
-
rooms.join("room/with/slashes");
|
|
80
|
-
expect(created[0].url).toContain("/v1/apps/app%25name/rooms/room%2Fwith%2Fslashes");
|
|
81
|
-
});
|
|
82
|
-
it("with no auth token, sets state to closed and does NOT open a socket", () => {
|
|
83
|
-
const room = newRoom(null);
|
|
84
|
-
expect(created).toHaveLength(0);
|
|
85
|
-
expect(room.state).toBe("closed");
|
|
86
|
-
});
|
|
87
|
-
it("flips to open state when the socket opens", () => {
|
|
88
|
-
const room = newRoom();
|
|
89
|
-
created[0].triggerOpen();
|
|
90
|
-
expect(room.state).toBe("open");
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
describe("Room.onConnectionState", () => {
|
|
94
|
-
it("fires immediately with current state on subscribe", () => {
|
|
95
|
-
const room = newRoom();
|
|
96
|
-
const seen = [];
|
|
97
|
-
room.onConnectionState((s) => seen.push(s));
|
|
98
|
-
expect(seen).toEqual(["connecting"]);
|
|
99
|
-
});
|
|
100
|
-
it("fires on every state transition", () => {
|
|
101
|
-
const room = newRoom();
|
|
102
|
-
const seen = [];
|
|
103
|
-
room.onConnectionState((s) => seen.push(s));
|
|
104
|
-
created[0].triggerOpen();
|
|
105
|
-
expect(seen).toEqual(["connecting", "open"]);
|
|
106
|
-
});
|
|
107
|
-
it("does not fire when state is set to the same value", () => {
|
|
108
|
-
const room = newRoom();
|
|
109
|
-
const seen = [];
|
|
110
|
-
room.onConnectionState((s) => seen.push(s));
|
|
111
|
-
// Trigger open twice — should only see one open transition.
|
|
112
|
-
created[0].triggerOpen();
|
|
113
|
-
created[0].triggerOpen();
|
|
114
|
-
expect(seen.filter((s) => s === "open")).toHaveLength(1);
|
|
115
|
-
});
|
|
116
|
-
it("returns an unsubscribe function", () => {
|
|
117
|
-
const room = newRoom();
|
|
118
|
-
const seen = [];
|
|
119
|
-
const unsub = room.onConnectionState((s) => seen.push(s));
|
|
120
|
-
unsub();
|
|
121
|
-
created[0].triggerOpen(); // should NOT fire after unsub
|
|
122
|
-
expect(seen).toEqual(["connecting"]);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
describe("Room.send", () => {
|
|
126
|
-
it("serializes data to a kind:msg envelope when socket is open", () => {
|
|
127
|
-
const room = newRoom();
|
|
128
|
-
created[0].triggerOpen();
|
|
129
|
-
room.send({ hello: "world" });
|
|
130
|
-
expect(created[0].sent).toEqual([JSON.stringify({ kind: "msg", data: { hello: "world" } })]);
|
|
131
|
-
});
|
|
132
|
-
it("silently drops messages when socket is not yet open (regression: no throw)", () => {
|
|
133
|
-
const room = newRoom();
|
|
134
|
-
expect(() => room.send({ hello: "world" })).not.toThrow();
|
|
135
|
-
expect(created[0].sent).toEqual([]);
|
|
136
|
-
});
|
|
137
|
-
it("silently drops messages after close", () => {
|
|
138
|
-
const room = newRoom();
|
|
139
|
-
created[0].triggerOpen();
|
|
140
|
-
room.close();
|
|
141
|
-
room.send({ hello: "world" });
|
|
142
|
-
// After close, socket is null — no sent message.
|
|
143
|
-
expect(created[0].sent).toEqual([]);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
describe("Room.onMessage", () => {
|
|
147
|
-
it("receives kind:msg frames as RoomMessage", () => {
|
|
148
|
-
const room = newRoom();
|
|
149
|
-
created[0].triggerOpen();
|
|
150
|
-
const messages = [];
|
|
151
|
-
room.onMessage((m) => messages.push(m));
|
|
152
|
-
created[0].triggerMessage(JSON.stringify({
|
|
153
|
-
kind: "msg",
|
|
154
|
-
from: { uid: "gh:1", login: "alice" },
|
|
155
|
-
data: { text: "hi" },
|
|
156
|
-
at: 1700000000000,
|
|
157
|
-
}));
|
|
158
|
-
expect(messages).toHaveLength(1);
|
|
159
|
-
expect(messages[0].from).toEqual({ uid: "gh:1", login: "alice" });
|
|
160
|
-
expect(messages[0].data).toEqual({ text: "hi" });
|
|
161
|
-
});
|
|
162
|
-
it("ignores malformed JSON frames without throwing", () => {
|
|
163
|
-
const room = newRoom();
|
|
164
|
-
created[0].triggerOpen();
|
|
165
|
-
const messages = [];
|
|
166
|
-
room.onMessage((m) => messages.push(m));
|
|
167
|
-
expect(() => created[0].triggerMessage("not-json{")).not.toThrow();
|
|
168
|
-
expect(messages).toEqual([]);
|
|
169
|
-
});
|
|
170
|
-
it("ignores frames with unknown `kind`", () => {
|
|
171
|
-
const room = newRoom();
|
|
172
|
-
created[0].triggerOpen();
|
|
173
|
-
const messages = [];
|
|
174
|
-
room.onMessage((m) => messages.push(m));
|
|
175
|
-
created[0].triggerMessage(JSON.stringify({ kind: "something-else", data: {} }));
|
|
176
|
-
expect(messages).toEqual([]);
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
describe("Room.onPeers", () => {
|
|
180
|
-
it("fires immediately with empty peers list on subscribe", () => {
|
|
181
|
-
const room = newRoom();
|
|
182
|
-
const calls = [];
|
|
183
|
-
room.onPeers((peers) => calls.push(peers));
|
|
184
|
-
expect(calls).toEqual([[]]);
|
|
185
|
-
});
|
|
186
|
-
it("updates on kind:peers frame", () => {
|
|
187
|
-
const room = newRoom();
|
|
188
|
-
created[0].triggerOpen();
|
|
189
|
-
const calls = [];
|
|
190
|
-
room.onPeers((peers) => calls.push(peers));
|
|
191
|
-
created[0].triggerMessage(JSON.stringify({
|
|
192
|
-
kind: "peers",
|
|
193
|
-
peers: [
|
|
194
|
-
{ uid: "gh:1", login: "alice" },
|
|
195
|
-
{ uid: "gh:2", login: "bob" },
|
|
196
|
-
],
|
|
197
|
-
}));
|
|
198
|
-
expect(calls).toHaveLength(2); // initial + the broadcast
|
|
199
|
-
expect(calls[1]).toEqual([
|
|
200
|
-
{ uid: "gh:1", login: "alice" },
|
|
201
|
-
{ uid: "gh:2", login: "bob" },
|
|
202
|
-
]);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe("Room.close", () => {
|
|
206
|
-
it("stops reconnect, clears listeners, sets state to closed", () => {
|
|
207
|
-
const room = newRoom();
|
|
208
|
-
const stateSeen = [];
|
|
209
|
-
const messages = [];
|
|
210
|
-
room.onConnectionState((s) => stateSeen.push(s));
|
|
211
|
-
room.onMessage((m) => messages.push(m));
|
|
212
|
-
created[0].triggerOpen();
|
|
213
|
-
room.close();
|
|
214
|
-
expect(room.state).toBe("closed");
|
|
215
|
-
// Sending should have no effect (socket cleared).
|
|
216
|
-
room.send({ x: 1 });
|
|
217
|
-
expect(created[0].sent.filter((s) => s.includes('"x":1'))).toEqual([]);
|
|
218
|
-
});
|
|
219
|
-
it("cancels a pending reconnect timer", () => {
|
|
220
|
-
vi.useFakeTimers();
|
|
221
|
-
const room = newRoom();
|
|
222
|
-
created[0].triggerOpen();
|
|
223
|
-
created[0].triggerClose(); // unexpected close → schedules reconnect
|
|
224
|
-
expect(room.state).toBe("closed");
|
|
225
|
-
room.close(); // user-initiated; should cancel reconnect
|
|
226
|
-
vi.advanceTimersByTime(60_000); // jump past any backoff
|
|
227
|
-
expect(created).toHaveLength(1); // no second connect
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
describe("Room reconnect on unexpected close", () => {
|
|
231
|
-
it("opens a new WebSocket after backoff", () => {
|
|
232
|
-
vi.useFakeTimers();
|
|
233
|
-
const room = newRoom();
|
|
234
|
-
created[0].triggerOpen();
|
|
235
|
-
expect(created).toHaveLength(1);
|
|
236
|
-
created[0].triggerClose(); // unexpected — schedules reconnect
|
|
237
|
-
expect(room.state).toBe("closed");
|
|
238
|
-
// First reconnect attempt fires after RECONNECT_BASE_MS (1000ms) + jitter (up to 1000ms).
|
|
239
|
-
vi.advanceTimersByTime(2100);
|
|
240
|
-
expect(created.length).toBeGreaterThanOrEqual(2);
|
|
241
|
-
expect(room.state).toBe("connecting");
|
|
242
|
-
});
|
|
243
|
-
it("opens succeed → state goes back to open + reconnect counter resets", () => {
|
|
244
|
-
vi.useFakeTimers();
|
|
245
|
-
const room = newRoom();
|
|
246
|
-
created[0].triggerOpen();
|
|
247
|
-
created[0].triggerClose();
|
|
248
|
-
vi.advanceTimersByTime(2100);
|
|
249
|
-
created[1].triggerOpen();
|
|
250
|
-
expect(room.state).toBe("open");
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
describe("Room error handling", () => {
|
|
254
|
-
it("socket error → state goes to error", () => {
|
|
255
|
-
const room = newRoom();
|
|
256
|
-
created[0].triggerError();
|
|
257
|
-
expect(room.state).toBe("error");
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
//# sourceMappingURL=rooms.test.js.map
|
package/dist/rooms.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rooms.test.js","sourceRoot":"","sources":["../src/rooms.test.ts"],"names":[],"mappings":"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"}
|