@freeappstore/sdk 0.1.2 → 0.3.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/LICENSE +21 -0
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +26 -15
- package/dist/auth.js.map +1 -1
- package/dist/auth.test.js +74 -72
- package/dist/auth.test.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/kv.d.ts +3 -1
- package/dist/kv.d.ts.map +1 -1
- package/dist/kv.js +32 -8
- package/dist/kv.js.map +1 -1
- package/dist/proxy.d.ts +40 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +62 -0
- package/dist/proxy.js.map +1 -0
- package/dist/proxy.test.d.ts +2 -0
- package/dist/proxy.test.d.ts.map +1 -0
- package/dist/proxy.test.js +77 -0
- package/dist/proxy.test.js.map +1 -0
- package/dist/rooms.test.d.ts +2 -0
- package/dist/rooms.test.d.ts.map +1 -0
- package/dist/rooms.test.js +256 -0
- package/dist/rooms.test.js.map +1 -0
- package/package.json +14 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FreeAppStore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/auth.d.ts
CHANGED
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAapD,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;IAM5D;;;;;;;OAOG;IACH,MAAM,IAAI,IAAI;IASd,OAAO,IAAI,IAAI;IAMf;;;;;;;;;;;;OAYG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YA8Bb,SAAS;IAQvB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,IAAI;CAGb"}
|
package/dist/auth.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Shared across all FAS apps on the same origin — this is intentional SSO.
|
|
3
|
+
* A user signed in on one FAS app is signed in on all of them.
|
|
4
|
+
*/
|
|
5
|
+
const STORAGE_KEY = "fas:session";
|
|
2
6
|
export class Auth {
|
|
3
7
|
appId;
|
|
4
8
|
apiBase;
|
|
@@ -17,6 +21,7 @@ export class Auth {
|
|
|
17
21
|
}
|
|
18
22
|
onChange(listener) {
|
|
19
23
|
this.listeners.add(listener);
|
|
24
|
+
listener(this.user);
|
|
20
25
|
return () => this.listeners.delete(listener);
|
|
21
26
|
}
|
|
22
27
|
/**
|
|
@@ -29,10 +34,10 @@ export class Auth {
|
|
|
29
34
|
*/
|
|
30
35
|
signIn() {
|
|
31
36
|
const here = new URL(window.location.href);
|
|
32
|
-
here.hash =
|
|
33
|
-
const url = new URL(
|
|
34
|
-
url.searchParams.set(
|
|
35
|
-
url.searchParams.set(
|
|
37
|
+
here.hash = "";
|
|
38
|
+
const url = new URL("/v1/auth/github/start", this.apiBase);
|
|
39
|
+
url.searchParams.set("app_id", this.appId);
|
|
40
|
+
url.searchParams.set("return_to", here.toString());
|
|
36
41
|
window.location.assign(url.toString());
|
|
37
42
|
}
|
|
38
43
|
signOut() {
|
|
@@ -54,18 +59,18 @@ export class Auth {
|
|
|
54
59
|
* render();
|
|
55
60
|
*/
|
|
56
61
|
async init() {
|
|
57
|
-
if (typeof window ===
|
|
62
|
+
if (typeof window === "undefined")
|
|
58
63
|
return;
|
|
59
64
|
const hash = window.location.hash;
|
|
60
|
-
if (!hash.startsWith(
|
|
65
|
+
if (!hash.startsWith("#fas_session="))
|
|
61
66
|
return;
|
|
62
67
|
// Always clear the hash before doing anything else — even on failure.
|
|
63
68
|
// Otherwise a bad token gets re-tried on every reload and the user is
|
|
64
69
|
// permanently stuck on a "broken" URL.
|
|
65
|
-
history.replaceState(null,
|
|
70
|
+
history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
66
71
|
let token;
|
|
67
72
|
try {
|
|
68
|
-
token = decodeURIComponent(hash.slice(
|
|
73
|
+
token = decodeURIComponent(hash.slice("#fas_session=".length));
|
|
69
74
|
}
|
|
70
75
|
catch {
|
|
71
76
|
// Malformed hash (% with nothing after, etc.). Hash already cleared.
|
|
@@ -73,13 +78,19 @@ export class Auth {
|
|
|
73
78
|
}
|
|
74
79
|
if (!token)
|
|
75
80
|
return;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
try {
|
|
82
|
+
const user = await this.fetchUser(token);
|
|
83
|
+
this.session = { token, user };
|
|
84
|
+
this.writeStorage(this.session);
|
|
85
|
+
this.emit();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Token was invalid or network failed. Hash already cleared so the user
|
|
89
|
+
// won't get stuck in a retry loop. Silently remain signed out.
|
|
90
|
+
}
|
|
80
91
|
}
|
|
81
92
|
async fetchUser(token) {
|
|
82
|
-
const res = await fetch(new URL(
|
|
93
|
+
const res = await fetch(new URL("/v1/auth/me", this.apiBase), {
|
|
83
94
|
headers: { Authorization: `Bearer ${token}` },
|
|
84
95
|
});
|
|
85
96
|
if (!res.ok)
|
|
@@ -87,7 +98,7 @@ export class Auth {
|
|
|
87
98
|
return (await res.json());
|
|
88
99
|
}
|
|
89
100
|
readStorage() {
|
|
90
|
-
if (typeof window ===
|
|
101
|
+
if (typeof window === "undefined")
|
|
91
102
|
return null;
|
|
92
103
|
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
93
104
|
if (!raw)
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,aAAa,CAAC;AAOlC,MAAM,OAAO,IAAI;IAKI;IACA;IALX,OAAO,GAAmB,IAAI,CAAC;IAC/B,SAAS,GAAG,IAAI,GAAG,EAA+B,CAAC;IAE3D,YACmB,KAAa,EACb,OAAe;QADf,UAAK,GAAL,KAAK,CAAQ;QACb,YAAO,GAAP,OAAO,CAAQ;QAEhC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,IAAI,CAAC;IACpC,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC;IACrC,CAAC;IAED,QAAQ,CAAC,QAAqC;QAC5C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;;;OAOG;IACH,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,uBAAuB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,OAAO;QACL,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,OAAO;QAE9C,sEAAsE;QACtE,sEAAsE;QACtE,uCAAuC;QACvC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAElF,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACH,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QACjE,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Auth } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { Auth } from "./auth.js";
|
|
3
3
|
function makeWindow(initial) {
|
|
4
4
|
const url = new URL(initial.href);
|
|
5
5
|
if (initial.hash)
|
|
@@ -32,117 +32,119 @@ function makeWindow(initial) {
|
|
|
32
32
|
}
|
|
33
33
|
beforeEach(() => {
|
|
34
34
|
// happy-dom-style globals; we install per-test
|
|
35
|
-
delete globalThis[
|
|
36
|
-
delete globalThis[
|
|
37
|
-
delete globalThis[
|
|
35
|
+
delete globalThis["window"];
|
|
36
|
+
delete globalThis["history"];
|
|
37
|
+
delete globalThis["fetch"];
|
|
38
38
|
});
|
|
39
39
|
afterEach(() => {
|
|
40
|
-
delete globalThis[
|
|
41
|
-
delete globalThis[
|
|
42
|
-
delete globalThis[
|
|
40
|
+
delete globalThis["window"];
|
|
41
|
+
delete globalThis["history"];
|
|
42
|
+
delete globalThis["fetch"];
|
|
43
43
|
});
|
|
44
|
-
describe(
|
|
45
|
-
it(
|
|
44
|
+
describe("Auth.signIn", () => {
|
|
45
|
+
it("strips the hash from return_to so the OAuth callback does not clobber app router state", () => {
|
|
46
46
|
const { window, assigns } = makeWindow({
|
|
47
|
-
href:
|
|
47
|
+
href: "https://app.freeappstore.online/dashboard?team=42#/settings/profile",
|
|
48
48
|
});
|
|
49
|
-
globalThis[
|
|
50
|
-
const auth = new Auth(
|
|
49
|
+
globalThis["window"] = window;
|
|
50
|
+
const auth = new Auth("demo", "https://api.freeappstore.online");
|
|
51
51
|
auth.signIn();
|
|
52
52
|
expect(assigns).toHaveLength(1);
|
|
53
53
|
const target = new URL(assigns[0]);
|
|
54
|
-
const returnTo = target.searchParams.get(
|
|
55
|
-
expect(returnTo).toBe(
|
|
54
|
+
const returnTo = target.searchParams.get("return_to");
|
|
55
|
+
expect(returnTo).toBe("https://app.freeappstore.online/dashboard?team=42");
|
|
56
56
|
// No #/settings/profile carried into return_to.
|
|
57
|
-
expect(returnTo).not.toContain(
|
|
57
|
+
expect(returnTo).not.toContain("#");
|
|
58
58
|
});
|
|
59
|
-
it(
|
|
60
|
-
const { window, assigns } = makeWindow({ href:
|
|
61
|
-
globalThis[
|
|
62
|
-
const auth = new Auth(
|
|
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
63
|
auth.signIn();
|
|
64
|
-
const returnTo = new URL(assigns[0]).searchParams.get(
|
|
65
|
-
expect(returnTo).toBe(
|
|
64
|
+
const returnTo = new URL(assigns[0]).searchParams.get("return_to");
|
|
65
|
+
expect(returnTo).toBe("https://app.example/page?x=1");
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
|
-
describe(
|
|
69
|
-
it(
|
|
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
70
|
const { window, replaces } = makeWindow({
|
|
71
|
-
href:
|
|
72
|
-
hash:
|
|
71
|
+
href: "https://app.example/",
|
|
72
|
+
hash: "#fas_session=bad-token",
|
|
73
73
|
});
|
|
74
|
-
globalThis[
|
|
75
|
-
globalThis[
|
|
76
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response(
|
|
77
|
-
const auth = new Auth(
|
|
78
|
-
|
|
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();
|
|
79
80
|
// The hash was cleared before the fetch, so reload won't retry.
|
|
80
81
|
expect(replaces).toHaveLength(1);
|
|
81
|
-
expect(replaces[0]).toBe(
|
|
82
|
+
expect(replaces[0]).toBe("/");
|
|
82
83
|
expect(auth.user).toBeNull();
|
|
83
84
|
});
|
|
84
|
-
it(
|
|
85
|
+
it("captures session and clears hash on successful auth callback", async () => {
|
|
85
86
|
const { window, replaces } = makeWindow({
|
|
86
|
-
href:
|
|
87
|
-
hash:
|
|
87
|
+
href: "https://app.example/dashboard",
|
|
88
|
+
hash: "#fas_session=good-token",
|
|
88
89
|
});
|
|
89
|
-
globalThis[
|
|
90
|
-
globalThis[
|
|
91
|
-
globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ id:
|
|
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 }), {
|
|
92
93
|
status: 200,
|
|
93
94
|
}));
|
|
94
|
-
const auth = new Auth(
|
|
95
|
+
const auth = new Auth("demo", "https://api.example");
|
|
95
96
|
await auth.init();
|
|
96
|
-
expect(auth.user?.login).toBe(
|
|
97
|
-
expect(auth.token).toBe(
|
|
98
|
-
expect(window.localStorage.store.get(
|
|
99
|
-
expect(replaces[0]).toBe(
|
|
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");
|
|
100
101
|
});
|
|
101
|
-
it(
|
|
102
|
-
const { window, replaces } = makeWindow({ href:
|
|
103
|
-
globalThis[
|
|
104
|
-
globalThis[
|
|
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;
|
|
105
106
|
const fetchSpy = vi.fn();
|
|
106
107
|
globalThis.fetch = fetchSpy;
|
|
107
|
-
const auth = new Auth(
|
|
108
|
+
const auth = new Auth("demo", "https://api.example");
|
|
108
109
|
await auth.init();
|
|
109
110
|
expect(fetchSpy).not.toHaveBeenCalled();
|
|
110
111
|
expect(replaces).toHaveLength(0);
|
|
111
112
|
expect(auth.user).toBeNull();
|
|
112
113
|
});
|
|
113
114
|
});
|
|
114
|
-
describe(
|
|
115
|
-
it(
|
|
116
|
-
const { window } = makeWindow({ href:
|
|
117
|
-
window.localStorage.store.set(
|
|
118
|
-
token:
|
|
119
|
-
user: { id:
|
|
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 },
|
|
120
121
|
}));
|
|
121
|
-
globalThis[
|
|
122
|
-
const auth = new Auth(
|
|
123
|
-
expect(auth.user?.login).toBe(
|
|
124
|
-
expect(auth.token).toBe(
|
|
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");
|
|
125
126
|
});
|
|
126
|
-
it(
|
|
127
|
-
const { window } = makeWindow({ href:
|
|
128
|
-
window.localStorage.store.set(
|
|
129
|
-
token:
|
|
130
|
-
user: { id:
|
|
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 },
|
|
131
132
|
}));
|
|
132
|
-
globalThis[
|
|
133
|
-
const auth = new Auth(
|
|
133
|
+
globalThis["window"] = window;
|
|
134
|
+
const auth = new Auth("demo", "https://api.example");
|
|
134
135
|
const seen = [];
|
|
135
136
|
auth.onChange((u) => seen.push(u));
|
|
136
137
|
auth.signOut();
|
|
137
138
|
expect(auth.user).toBeNull();
|
|
138
|
-
expect(window.localStorage.store.has(
|
|
139
|
-
|
|
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]);
|
|
140
142
|
});
|
|
141
|
-
it(
|
|
142
|
-
const { window } = makeWindow({ href:
|
|
143
|
-
window.localStorage.store.set(
|
|
144
|
-
globalThis[
|
|
145
|
-
const auth = new Auth(
|
|
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");
|
|
146
148
|
expect(auth.user).toBeNull();
|
|
147
149
|
});
|
|
148
150
|
});
|
package/dist/auth.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../src/auth.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../src/auth.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { Auth } from
|
|
2
|
-
import { Kv } from
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
export type { Room, RoomMessage, RoomPeer
|
|
1
|
+
import { Auth } from "./auth.js";
|
|
2
|
+
import { Kv } from "./kv.js";
|
|
3
|
+
import { ApiProxy } from "./proxy.js";
|
|
4
|
+
import { Rooms } from "./rooms.js";
|
|
5
|
+
import type { FasInitOptions } from "./types.js";
|
|
6
|
+
export type { ConnectionState, Room, RoomMessage, RoomPeer } from "./rooms.js";
|
|
7
|
+
export type { FasInitOptions, Unsubscribe, User } from "./types.js";
|
|
7
8
|
export declare class FreeAppStore {
|
|
8
9
|
readonly auth: Auth;
|
|
9
10
|
readonly kv: Kv;
|
|
10
11
|
readonly rooms: Rooms;
|
|
12
|
+
readonly proxy: ApiProxy;
|
|
11
13
|
constructor(opts: FasInitOptions);
|
|
12
14
|
}
|
|
13
15
|
export declare function initApp(opts: FasInitOptions): FreeAppStore;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpE,qBAAa,YAAY;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;gBAEb,IAAI,EAAE,cAAc;CAOjC;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,YAAY,CAE1D"}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import { Auth } from
|
|
2
|
-
import { Kv } from
|
|
3
|
-
import {
|
|
1
|
+
import { Auth } from "./auth.js";
|
|
2
|
+
import { Kv } from "./kv.js";
|
|
3
|
+
import { ApiProxy } from "./proxy.js";
|
|
4
|
+
import { Rooms } from "./rooms.js";
|
|
4
5
|
export class FreeAppStore {
|
|
5
6
|
auth;
|
|
6
7
|
kv;
|
|
7
8
|
rooms;
|
|
9
|
+
proxy;
|
|
8
10
|
constructor(opts) {
|
|
9
|
-
const apiBase = opts.apiBase ??
|
|
11
|
+
const apiBase = opts.apiBase ?? "https://api.freeappstore.online";
|
|
10
12
|
this.auth = new Auth(opts.appId, apiBase);
|
|
11
13
|
this.kv = new Kv(opts.appId, apiBase, this.auth);
|
|
12
14
|
this.rooms = new Rooms(opts.appId, apiBase, this.auth);
|
|
15
|
+
this.proxy = new ApiProxy(opts.appId, apiBase, this.auth);
|
|
13
16
|
}
|
|
14
17
|
}
|
|
15
18
|
export function initApp(opts) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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;
|
|
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,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAMnC,MAAM,OAAO,YAAY;IACd,IAAI,CAAO;IACX,EAAE,CAAK;IACP,KAAK,CAAQ;IACb,KAAK,CAAW;IAEzB,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;QACvD,IAAI,CAAC,KAAK,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,UAAU,OAAO,CAAC,IAAoB;IAC1C,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC"}
|
package/dist/kv.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { Auth } from
|
|
1
|
+
import type { Auth } from "./auth.js";
|
|
2
2
|
export declare class Kv {
|
|
3
3
|
private readonly appId;
|
|
4
4
|
private readonly apiBase;
|
|
5
5
|
private readonly auth;
|
|
6
6
|
constructor(appId: string, apiBase: string, auth: Auth);
|
|
7
|
+
list(): Promise<string[]>;
|
|
8
|
+
getMany<T = unknown>(keys: string[]): Promise<Map<string, T>>;
|
|
7
9
|
get<T = unknown>(key: string): Promise<T | null>;
|
|
8
10
|
set<T = unknown>(key: string, value: T): Promise<void>;
|
|
9
11
|
delete(key: string): Promise<void>;
|
package/dist/kv.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../src/kv.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAyBtC,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;IAQhD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IActD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC,OAAO,CAAC,OAAO;
|
|
1
|
+
{"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../src/kv.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAyBtC,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,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAWzB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAW7D,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAQhD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IActD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC,OAAO,CAAC,OAAO;CAUhB"}
|
package/dist/kv.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
const MAX_KEY_LENGTH = 128;
|
|
14
14
|
function assertValidKey(key) {
|
|
15
|
-
if (typeof key !==
|
|
16
|
-
throw new Error(
|
|
15
|
+
if (typeof key !== "string" || key.length === 0) {
|
|
16
|
+
throw new Error("kv key must be a non-empty string.");
|
|
17
17
|
}
|
|
18
18
|
if (key.length > MAX_KEY_LENGTH) {
|
|
19
19
|
throw new Error(`kv key exceeds ${MAX_KEY_LENGTH} chars.`);
|
|
@@ -28,9 +28,33 @@ export class Kv {
|
|
|
28
28
|
this.apiBase = apiBase;
|
|
29
29
|
this.auth = auth;
|
|
30
30
|
}
|
|
31
|
+
async list() {
|
|
32
|
+
const token = this.auth.token;
|
|
33
|
+
if (!token)
|
|
34
|
+
throw new Error("Not signed in.");
|
|
35
|
+
const url = new URL(`/v1/apps/${encodeURIComponent(this.appId)}/kv`, this.apiBase);
|
|
36
|
+
const res = await fetch(url, {
|
|
37
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok)
|
|
40
|
+
throw new Error(`kv.list failed: ${res.status}`);
|
|
41
|
+
return (await res.json());
|
|
42
|
+
}
|
|
43
|
+
async getMany(keys) {
|
|
44
|
+
for (const key of keys)
|
|
45
|
+
assertValidKey(key);
|
|
46
|
+
const results = new Map();
|
|
47
|
+
const fetches = keys.map(async (key) => {
|
|
48
|
+
const val = await this.get(key);
|
|
49
|
+
if (val !== null)
|
|
50
|
+
results.set(key, val);
|
|
51
|
+
});
|
|
52
|
+
await Promise.all(fetches);
|
|
53
|
+
return results;
|
|
54
|
+
}
|
|
31
55
|
async get(key) {
|
|
32
56
|
assertValidKey(key);
|
|
33
|
-
const res = await this.request(
|
|
57
|
+
const res = await this.request("GET", key);
|
|
34
58
|
if (res.status === 404)
|
|
35
59
|
return null;
|
|
36
60
|
if (!res.ok)
|
|
@@ -42,9 +66,9 @@ export class Kv {
|
|
|
42
66
|
// JSON.stringify(undefined) returns undefined, which would store an empty
|
|
43
67
|
// body and break later get() calls. Reject up front instead.
|
|
44
68
|
if (value === undefined) {
|
|
45
|
-
throw new Error(
|
|
69
|
+
throw new Error("kv.set: value is undefined. Use kv.delete(key) to remove a key.");
|
|
46
70
|
}
|
|
47
|
-
const res = await this.request(
|
|
71
|
+
const res = await this.request("PUT", key, JSON.stringify(value));
|
|
48
72
|
if (!res.ok) {
|
|
49
73
|
const text = await res.text();
|
|
50
74
|
throw new Error(`kv.set failed (${res.status}): ${text}`);
|
|
@@ -52,7 +76,7 @@ export class Kv {
|
|
|
52
76
|
}
|
|
53
77
|
async delete(key) {
|
|
54
78
|
assertValidKey(key);
|
|
55
|
-
const res = await this.request(
|
|
79
|
+
const res = await this.request("DELETE", key);
|
|
56
80
|
if (!res.ok && res.status !== 404) {
|
|
57
81
|
throw new Error(`kv.delete failed: ${res.status}`);
|
|
58
82
|
}
|
|
@@ -60,11 +84,11 @@ export class Kv {
|
|
|
60
84
|
request(method, key, body) {
|
|
61
85
|
const token = this.auth.token;
|
|
62
86
|
if (!token)
|
|
63
|
-
throw new Error(
|
|
87
|
+
throw new Error("Not signed in.");
|
|
64
88
|
const url = new URL(`/v1/apps/${encodeURIComponent(this.appId)}/kv/${encodeURIComponent(key)}`, this.apiBase);
|
|
65
89
|
const headers = { Authorization: `Bearer ${token}` };
|
|
66
90
|
if (body !== undefined)
|
|
67
|
-
headers[
|
|
91
|
+
headers["Content-Type"] = "application/json";
|
|
68
92
|
const init = { method, headers };
|
|
69
93
|
if (body !== undefined)
|
|
70
94
|
init.body = body;
|
package/dist/kv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kv.js","sourceRoot":"","sources":["../src/kv.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,kBAAkB,cAAc,SAAS,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,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,cAAc,CAAC,GAAG,CAAC,CAAC;QACpB,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,cAAc,CAAC,GAAG,CAAC,CAAC;QACpB,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,cAAc,CAAC,GAAG,CAAC,CAAC;QACpB,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,
|
|
1
|
+
{"version":3,"file":"kv.js","sourceRoot":"","sources":["../src/kv.ts"],"names":[],"mappings":"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"}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Auth } from "./auth.js";
|
|
2
|
+
/**
|
|
3
|
+
* Browser-side wrapper around the platform's per-app secret-injecting proxy.
|
|
4
|
+
*
|
|
5
|
+
* Pattern:
|
|
6
|
+
* const fas = initApp({ appId: 'weather' });
|
|
7
|
+
* const res = await fas.proxy.fetch(
|
|
8
|
+
* 'api.openweathermap.org/data/2.5/weather?q=London',
|
|
9
|
+
* );
|
|
10
|
+
*
|
|
11
|
+
* The first segment is the upstream host; the rest is path + query. The
|
|
12
|
+
* platform Worker authenticates the call with the user's session token,
|
|
13
|
+
* matches the URL against the app's allowlist, decrypts the developer's
|
|
14
|
+
* stored API key, and forwards the request server-side.
|
|
15
|
+
*
|
|
16
|
+
* The developer's secret never touches the browser.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ApiProxy {
|
|
19
|
+
private readonly appId;
|
|
20
|
+
private readonly apiBase;
|
|
21
|
+
private readonly auth;
|
|
22
|
+
constructor(appId: string, apiBase: string, auth: Auth);
|
|
23
|
+
/**
|
|
24
|
+
* Fetch via the proxy. Accepts either:
|
|
25
|
+
* - "host/path?query" (preferred, matches the SDK's CLI register form)
|
|
26
|
+
* - a full "https://host/path?query" URL (we strip the scheme)
|
|
27
|
+
*/
|
|
28
|
+
fetch(target: string, init?: RequestInit): Promise<Response>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Strip a leading scheme (`https://`, `http://`) so callers can paste either
|
|
32
|
+
* form and get the same result. Throws on schemes other than http(s) — the
|
|
33
|
+
* proxy only ever forwards over https upstream and we want a loud error
|
|
34
|
+
* rather than a silent rewrite.
|
|
35
|
+
*
|
|
36
|
+
* Scheme detection is case-insensitive: `HTTPS://api.example.com/x` should
|
|
37
|
+
* normalize the same as `https://api.example.com/x`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeTarget(target: string): string;
|
|
40
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,QAAQ;IAEjB,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;;;;OAIG;IACG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CASnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWtD"}
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side wrapper around the platform's per-app secret-injecting proxy.
|
|
3
|
+
*
|
|
4
|
+
* Pattern:
|
|
5
|
+
* const fas = initApp({ appId: 'weather' });
|
|
6
|
+
* const res = await fas.proxy.fetch(
|
|
7
|
+
* 'api.openweathermap.org/data/2.5/weather?q=London',
|
|
8
|
+
* );
|
|
9
|
+
*
|
|
10
|
+
* The first segment is the upstream host; the rest is path + query. The
|
|
11
|
+
* platform Worker authenticates the call with the user's session token,
|
|
12
|
+
* matches the URL against the app's allowlist, decrypts the developer's
|
|
13
|
+
* stored API key, and forwards the request server-side.
|
|
14
|
+
*
|
|
15
|
+
* The developer's secret never touches the browser.
|
|
16
|
+
*/
|
|
17
|
+
export class ApiProxy {
|
|
18
|
+
appId;
|
|
19
|
+
apiBase;
|
|
20
|
+
auth;
|
|
21
|
+
constructor(appId, apiBase, auth) {
|
|
22
|
+
this.appId = appId;
|
|
23
|
+
this.apiBase = apiBase;
|
|
24
|
+
this.auth = auth;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Fetch via the proxy. Accepts either:
|
|
28
|
+
* - "host/path?query" (preferred, matches the SDK's CLI register form)
|
|
29
|
+
* - a full "https://host/path?query" URL (we strip the scheme)
|
|
30
|
+
*/
|
|
31
|
+
async fetch(target, init) {
|
|
32
|
+
if (!this.auth.token) {
|
|
33
|
+
throw new Error("proxy.fetch: not signed in. Call fas.auth.signIn() first.");
|
|
34
|
+
}
|
|
35
|
+
const url = `${this.apiBase}/v1/apps/${encodeURIComponent(this.appId)}/proxy/${normalizeTarget(target)}`;
|
|
36
|
+
const headers = new Headers(init?.headers);
|
|
37
|
+
headers.set("Authorization", `Bearer ${this.auth.token}`);
|
|
38
|
+
return fetch(url, { ...init, headers });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strip a leading scheme (`https://`, `http://`) so callers can paste either
|
|
43
|
+
* form and get the same result. Throws on schemes other than http(s) — the
|
|
44
|
+
* proxy only ever forwards over https upstream and we want a loud error
|
|
45
|
+
* rather than a silent rewrite.
|
|
46
|
+
*
|
|
47
|
+
* Scheme detection is case-insensitive: `HTTPS://api.example.com/x` should
|
|
48
|
+
* normalize the same as `https://api.example.com/x`.
|
|
49
|
+
*/
|
|
50
|
+
export function normalizeTarget(target) {
|
|
51
|
+
const schemeMatch = /^([a-z][a-z0-9+.-]*):\/\//i.exec(target);
|
|
52
|
+
if (schemeMatch) {
|
|
53
|
+
const scheme = schemeMatch[1].toLowerCase();
|
|
54
|
+
if (scheme === "http" || scheme === "https") {
|
|
55
|
+
return target.slice(schemeMatch[0].length);
|
|
56
|
+
}
|
|
57
|
+
throw new Error("proxy.fetch: only http(s) targets are supported");
|
|
58
|
+
}
|
|
59
|
+
// Already in "host/path" form.
|
|
60
|
+
return target.replace(/^\/+/, "");
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,QAAQ;IAEA;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;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,MAAc,EAAE,IAAkB;QAC5C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,YAAY,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;QACzG,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1C,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,WAAW,GAAG,4BAA4B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;QAC7C,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,+BAA+B;IAC/B,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.test.d.ts","sourceRoot":"","sources":["../src/proxy.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { normalizeTarget, ApiProxy } 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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.test.js","sourceRoot":"","sources":["../src/proxy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rooms.test.d.ts","sourceRoot":"","sources":["../src/rooms.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, 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
|
+
this.listeners.get(type)?.forEach((fn) => fn(ev));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
let created = [];
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
created = [];
|
|
55
|
+
globalThis['WebSocket'] = FakeWebSocket;
|
|
56
|
+
});
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
delete globalThis['WebSocket'];
|
|
59
|
+
vi.useRealTimers();
|
|
60
|
+
});
|
|
61
|
+
function fakeAuth(token) {
|
|
62
|
+
return { token };
|
|
63
|
+
}
|
|
64
|
+
function newRoom(token = 'tok') {
|
|
65
|
+
const rooms = new Rooms('demo', 'https://api.example', fakeAuth(token));
|
|
66
|
+
return rooms.join('lobby');
|
|
67
|
+
}
|
|
68
|
+
describe('Room.connect — initial state', () => {
|
|
69
|
+
it('starts in connecting state and creates a WebSocket immediately', () => {
|
|
70
|
+
const room = newRoom();
|
|
71
|
+
expect(created).toHaveLength(1);
|
|
72
|
+
expect(room.state).toBe('connecting');
|
|
73
|
+
expect(created[0].url).toMatch(/wss:\/\/api\.example\/v1\/apps\/demo\/rooms\/lobby/);
|
|
74
|
+
expect(created[0].url).toContain('token=tok');
|
|
75
|
+
});
|
|
76
|
+
it('encodes appId and roomId in the URL path', () => {
|
|
77
|
+
const rooms = new Rooms('app%name', 'https://api.example', fakeAuth('tok'));
|
|
78
|
+
rooms.join('room/with/slashes');
|
|
79
|
+
expect(created[0].url).toContain('/v1/apps/app%25name/rooms/room%2Fwith%2Fslashes');
|
|
80
|
+
});
|
|
81
|
+
it('with no auth token, sets state to closed and does NOT open a socket', () => {
|
|
82
|
+
const room = newRoom(null);
|
|
83
|
+
expect(created).toHaveLength(0);
|
|
84
|
+
expect(room.state).toBe('closed');
|
|
85
|
+
});
|
|
86
|
+
it('flips to open state when the socket opens', () => {
|
|
87
|
+
const room = newRoom();
|
|
88
|
+
created[0].triggerOpen();
|
|
89
|
+
expect(room.state).toBe('open');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('Room.onConnectionState', () => {
|
|
93
|
+
it('fires immediately with current state on subscribe', () => {
|
|
94
|
+
const room = newRoom();
|
|
95
|
+
const seen = [];
|
|
96
|
+
room.onConnectionState((s) => seen.push(s));
|
|
97
|
+
expect(seen).toEqual(['connecting']);
|
|
98
|
+
});
|
|
99
|
+
it('fires on every state transition', () => {
|
|
100
|
+
const room = newRoom();
|
|
101
|
+
const seen = [];
|
|
102
|
+
room.onConnectionState((s) => seen.push(s));
|
|
103
|
+
created[0].triggerOpen();
|
|
104
|
+
expect(seen).toEqual(['connecting', 'open']);
|
|
105
|
+
});
|
|
106
|
+
it('does not fire when state is set to the same value', () => {
|
|
107
|
+
const room = newRoom();
|
|
108
|
+
const seen = [];
|
|
109
|
+
room.onConnectionState((s) => seen.push(s));
|
|
110
|
+
// Trigger open twice — should only see one open transition.
|
|
111
|
+
created[0].triggerOpen();
|
|
112
|
+
created[0].triggerOpen();
|
|
113
|
+
expect(seen.filter((s) => s === 'open')).toHaveLength(1);
|
|
114
|
+
});
|
|
115
|
+
it('returns an unsubscribe function', () => {
|
|
116
|
+
const room = newRoom();
|
|
117
|
+
const seen = [];
|
|
118
|
+
const unsub = room.onConnectionState((s) => seen.push(s));
|
|
119
|
+
unsub();
|
|
120
|
+
created[0].triggerOpen(); // should NOT fire after unsub
|
|
121
|
+
expect(seen).toEqual(['connecting']);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe('Room.send', () => {
|
|
125
|
+
it('serializes data to a kind:msg envelope when socket is open', () => {
|
|
126
|
+
const room = newRoom();
|
|
127
|
+
created[0].triggerOpen();
|
|
128
|
+
room.send({ hello: 'world' });
|
|
129
|
+
expect(created[0].sent).toEqual([JSON.stringify({ kind: 'msg', data: { hello: 'world' } })]);
|
|
130
|
+
});
|
|
131
|
+
it('silently drops messages when socket is not yet open (regression: no throw)', () => {
|
|
132
|
+
const room = newRoom();
|
|
133
|
+
expect(() => room.send({ hello: 'world' })).not.toThrow();
|
|
134
|
+
expect(created[0].sent).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
it('silently drops messages after close', () => {
|
|
137
|
+
const room = newRoom();
|
|
138
|
+
created[0].triggerOpen();
|
|
139
|
+
room.close();
|
|
140
|
+
room.send({ hello: 'world' });
|
|
141
|
+
// After close, socket is null — no sent message.
|
|
142
|
+
expect(created[0].sent).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('Room.onMessage', () => {
|
|
146
|
+
it('receives kind:msg frames as RoomMessage', () => {
|
|
147
|
+
const room = newRoom();
|
|
148
|
+
created[0].triggerOpen();
|
|
149
|
+
const messages = [];
|
|
150
|
+
room.onMessage((m) => messages.push(m));
|
|
151
|
+
created[0].triggerMessage(JSON.stringify({
|
|
152
|
+
kind: 'msg',
|
|
153
|
+
from: { uid: 'gh:1', login: 'alice' },
|
|
154
|
+
data: { text: 'hi' },
|
|
155
|
+
at: 1700000000000,
|
|
156
|
+
}));
|
|
157
|
+
expect(messages).toHaveLength(1);
|
|
158
|
+
expect(messages[0].from).toEqual({ uid: 'gh:1', login: 'alice' });
|
|
159
|
+
expect(messages[0].data).toEqual({ text: 'hi' });
|
|
160
|
+
});
|
|
161
|
+
it('ignores malformed JSON frames without throwing', () => {
|
|
162
|
+
const room = newRoom();
|
|
163
|
+
created[0].triggerOpen();
|
|
164
|
+
const messages = [];
|
|
165
|
+
room.onMessage((m) => messages.push(m));
|
|
166
|
+
expect(() => created[0].triggerMessage('not-json{')).not.toThrow();
|
|
167
|
+
expect(messages).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
it('ignores frames with unknown `kind`', () => {
|
|
170
|
+
const room = newRoom();
|
|
171
|
+
created[0].triggerOpen();
|
|
172
|
+
const messages = [];
|
|
173
|
+
room.onMessage((m) => messages.push(m));
|
|
174
|
+
created[0].triggerMessage(JSON.stringify({ kind: 'something-else', data: {} }));
|
|
175
|
+
expect(messages).toEqual([]);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
describe('Room.onPeers', () => {
|
|
179
|
+
it('fires immediately with empty peers list on subscribe', () => {
|
|
180
|
+
const room = newRoom();
|
|
181
|
+
const calls = [];
|
|
182
|
+
room.onPeers((peers) => calls.push(peers));
|
|
183
|
+
expect(calls).toEqual([[]]);
|
|
184
|
+
});
|
|
185
|
+
it('updates on kind:peers frame', () => {
|
|
186
|
+
const room = newRoom();
|
|
187
|
+
created[0].triggerOpen();
|
|
188
|
+
const calls = [];
|
|
189
|
+
room.onPeers((peers) => calls.push(peers));
|
|
190
|
+
created[0].triggerMessage(JSON.stringify({
|
|
191
|
+
kind: 'peers',
|
|
192
|
+
peers: [{ uid: 'gh:1', login: 'alice' }, { uid: 'gh:2', login: 'bob' }],
|
|
193
|
+
}));
|
|
194
|
+
expect(calls).toHaveLength(2); // initial + the broadcast
|
|
195
|
+
expect(calls[1]).toEqual([
|
|
196
|
+
{ uid: 'gh:1', login: 'alice' },
|
|
197
|
+
{ uid: 'gh:2', login: 'bob' },
|
|
198
|
+
]);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe('Room.close', () => {
|
|
202
|
+
it('stops reconnect, clears listeners, sets state to closed', () => {
|
|
203
|
+
const room = newRoom();
|
|
204
|
+
const stateSeen = [];
|
|
205
|
+
const messages = [];
|
|
206
|
+
room.onConnectionState((s) => stateSeen.push(s));
|
|
207
|
+
room.onMessage((m) => messages.push(m));
|
|
208
|
+
created[0].triggerOpen();
|
|
209
|
+
room.close();
|
|
210
|
+
expect(room.state).toBe('closed');
|
|
211
|
+
// Sending should have no effect (socket cleared).
|
|
212
|
+
room.send({ x: 1 });
|
|
213
|
+
expect(created[0].sent.filter((s) => s.includes('"x":1'))).toEqual([]);
|
|
214
|
+
});
|
|
215
|
+
it('cancels a pending reconnect timer', () => {
|
|
216
|
+
vi.useFakeTimers();
|
|
217
|
+
const room = newRoom();
|
|
218
|
+
created[0].triggerOpen();
|
|
219
|
+
created[0].triggerClose(); // unexpected close → schedules reconnect
|
|
220
|
+
expect(room.state).toBe('closed');
|
|
221
|
+
room.close(); // user-initiated; should cancel reconnect
|
|
222
|
+
vi.advanceTimersByTime(60_000); // jump past any backoff
|
|
223
|
+
expect(created).toHaveLength(1); // no second connect
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe('Room reconnect on unexpected close', () => {
|
|
227
|
+
it('opens a new WebSocket after backoff', () => {
|
|
228
|
+
vi.useFakeTimers();
|
|
229
|
+
const room = newRoom();
|
|
230
|
+
created[0].triggerOpen();
|
|
231
|
+
expect(created).toHaveLength(1);
|
|
232
|
+
created[0].triggerClose(); // unexpected — schedules reconnect
|
|
233
|
+
expect(room.state).toBe('closed');
|
|
234
|
+
// First reconnect attempt fires after RECONNECT_BASE_MS (1000ms) + jitter (up to 1000ms).
|
|
235
|
+
vi.advanceTimersByTime(2100);
|
|
236
|
+
expect(created.length).toBeGreaterThanOrEqual(2);
|
|
237
|
+
expect(room.state).toBe('connecting');
|
|
238
|
+
});
|
|
239
|
+
it('opens succeed → state goes back to open + reconnect counter resets', () => {
|
|
240
|
+
vi.useFakeTimers();
|
|
241
|
+
const room = newRoom();
|
|
242
|
+
created[0].triggerOpen();
|
|
243
|
+
created[0].triggerClose();
|
|
244
|
+
vi.advanceTimersByTime(2100);
|
|
245
|
+
created[1].triggerOpen();
|
|
246
|
+
expect(room.state).toBe('open');
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
describe('Room error handling', () => {
|
|
250
|
+
it('socket error → state goes to error', () => {
|
|
251
|
+
const room = newRoom();
|
|
252
|
+
created[0].triggerError();
|
|
253
|
+
expect(room.state).toBe('error');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
//# sourceMappingURL=rooms.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rooms.test.js","sourceRoot":"","sources":["../src/rooms.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freeappstore/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Browser SDK for free apps on freeappstore.online — auth, per-user KV, light realtime rooms.",
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"homepage": "https://github.com/freeappstore-online/
|
|
15
|
+
"homepage": "https://github.com/freeappstore-online/platform#readme",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/freeappstore-online/
|
|
18
|
+
"url": "git+https://github.com/freeappstore-online/platform.git",
|
|
19
19
|
"directory": "packages/sdk"
|
|
20
20
|
},
|
|
21
21
|
"bugs": {
|
|
22
|
-
"url": "https://github.com/freeappstore-online/
|
|
22
|
+
"url": "https://github.com/freeappstore-online/platform/issues"
|
|
23
23
|
},
|
|
24
24
|
"keywords": [
|
|
25
25
|
"freeappstore",
|
|
@@ -29,18 +29,20 @@
|
|
|
29
29
|
"rooms",
|
|
30
30
|
"websocket",
|
|
31
31
|
"github-oauth",
|
|
32
|
-
"cloudflare-workers"
|
|
32
|
+
"cloudflare-workers",
|
|
33
|
+
"proxy",
|
|
34
|
+
"api-key"
|
|
33
35
|
],
|
|
34
36
|
"files": [
|
|
35
37
|
"dist",
|
|
36
38
|
"README.md"
|
|
37
39
|
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"build": "tsc",
|
|
40
|
-
"typecheck": "tsc --noEmit"
|
|
41
|
-
},
|
|
42
40
|
"devDependencies": {
|
|
43
41
|
"typescript": "^5.7.0"
|
|
44
42
|
},
|
|
45
|
-
"sideEffects": false
|
|
46
|
-
|
|
43
|
+
"sideEffects": false,
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
}
|
|
48
|
+
}
|