@foir/sdk 0.5.0 → 0.8.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +1126 -0
  2. package/README.md +94 -0
  3. package/dist/auth/auth.d.ts +243 -0
  4. package/dist/auth/hosted.d.ts +40 -0
  5. package/dist/auth/index.d.ts +21 -0
  6. package/dist/auth/operations.d.ts +144 -0
  7. package/dist/auth/ops.d.ts +99 -0
  8. package/dist/auth/session-client.d.ts +76 -0
  9. package/dist/auth-session.js +137 -0
  10. package/dist/auth.js +973 -92
  11. package/dist/chunk-IGNLWT3H.js +35 -0
  12. package/dist/chunk-OHYVKTGB.js +45 -0
  13. package/dist/chunk-TAL5H6VA.js +39 -0
  14. package/dist/client/platform-client.d.ts +29 -0
  15. package/dist/client.js +275 -37
  16. package/dist/codegen/block-registry.d.ts +13 -23
  17. package/dist/codegen/block-registry.js +3 -31
  18. package/dist/codegen/block-scaffold.d.ts +12 -25
  19. package/dist/codegen/block-scaffold.js +3 -41
  20. package/dist/codegen/index.d.ts +12 -61
  21. package/dist/codegen/index.js +7 -11
  22. package/dist/codegen/item-registry.d.ts +28 -0
  23. package/dist/codegen/item-registry.js +7 -0
  24. package/dist/codegen/item-scaffold.d.ts +29 -0
  25. package/dist/codegen/item-scaffold.js +7 -0
  26. package/dist/codegen/preset.d.ts +67 -0
  27. package/dist/codegen/shared.d.ts +36 -0
  28. package/dist/components/EditorProvider.d.ts +36 -0
  29. package/dist/content/content-renderer.d.ts +113 -0
  30. package/dist/content/index.d.ts +18 -0
  31. package/dist/content/richtext.d.ts +118 -0
  32. package/dist/content.js +227 -80
  33. package/dist/data/builtins.d.ts +506 -0
  34. package/dist/data/client.d.ts +166 -0
  35. package/dist/data/document.d.ts +251 -0
  36. package/dist/data/index.d.ts +73 -0
  37. package/dist/data/manifest.d.ts +424 -0
  38. package/dist/data/schema.d.ts +313 -0
  39. package/dist/data/selection.d.ts +524 -0
  40. package/dist/data/subjects.d.ts +221 -0
  41. package/dist/data/subscription-document.d.ts +75 -0
  42. package/dist/data/subscriptions.d.ts +181 -0
  43. package/dist/data/transport.d.ts +325 -0
  44. package/dist/data/ws-transport.d.ts +197 -0
  45. package/dist/data.js +1683 -0
  46. package/dist/graphql/cdn-route.d.ts +26 -0
  47. package/dist/graphql/client.d.ts +30 -0
  48. package/dist/graphql/content-client.d.ts +66 -0
  49. package/dist/graphql/foir-request.d.ts +47 -0
  50. package/dist/hooks/useAutoResize.d.ts +22 -0
  51. package/dist/hooks/useEditor.d.ts +16 -0
  52. package/dist/index.d.ts +10 -394
  53. package/dist/media/BlurhashCanvas.d.ts +10 -0
  54. package/dist/media/ProgressiveMedia.d.ts +56 -0
  55. package/dist/media/index.d.ts +18 -0
  56. package/dist/media/media.d.ts +139 -0
  57. package/dist/media/screen-capture.d.ts +81 -0
  58. package/dist/media/useProgressiveLoad.d.ts +23 -0
  59. package/dist/media.js +102 -0
  60. package/dist/next/cookieAdapter.d.ts +26 -0
  61. package/dist/next/index.d.ts +15 -0
  62. package/dist/next/middleware.d.ts +49 -0
  63. package/dist/next/session.d.ts +53 -0
  64. package/dist/next.js +763 -78
  65. package/dist/protocol/constants.d.ts +6 -0
  66. package/dist/protocol/messages.d.ts +155 -0
  67. package/dist/protocol/validation.d.ts +8 -0
  68. package/dist/push/index.d.ts +14 -0
  69. package/dist/push/operations.d.ts +22 -0
  70. package/dist/push/web-push.d.ts +39 -0
  71. package/dist/push.js +892 -78
  72. package/dist/react/FoirFeedback.d.ts +62 -0
  73. package/dist/react/FoirProvider.d.ts +116 -0
  74. package/dist/react/OAuthButtons.d.ts +20 -0
  75. package/dist/react/SignIn.d.ts +22 -0
  76. package/dist/react/UserButton.d.ts +13 -0
  77. package/dist/react/feedback-notes.d.ts +101 -0
  78. package/dist/react/index.d.ts +25 -0
  79. package/dist/react/localStorageAdapter.d.ts +10 -0
  80. package/dist/react.js +1373 -91
  81. package/dist/{realtime.d.ts → realtime/index.d.ts} +35 -16
  82. package/dist/realtime.js +1 -1
  83. package/dist/server/callback-client.d.ts +92 -0
  84. package/dist/server/events-client.d.ts +279 -0
  85. package/dist/server/index.d.ts +12 -0
  86. package/dist/server/operation-types.d.ts +134 -0
  87. package/dist/server/require-scoped-token.d.ts +76 -0
  88. package/dist/server/server-client.d.ts +34 -0
  89. package/dist/server/types.d.ts +54 -0
  90. package/dist/server/verify-scoped-token.d.ts +100 -0
  91. package/dist/server/webhook-helpers.d.ts +36 -0
  92. package/dist/server.js +298 -0
  93. package/dist/transport/foir-http.d.ts +234 -0
  94. package/dist/transport/persisted-query.d.ts +68 -0
  95. package/dist/transport/retry.d.ts +61 -0
  96. package/dist/types/index.d.ts +124 -0
  97. package/package.json +35 -13
  98. package/dist/auth.d.ts +0 -382
  99. package/dist/chunk-ADO5FJNM.js +0 -30
  100. package/dist/client.d.ts +0 -135
  101. package/dist/content.d.ts +0 -96
  102. package/dist/media.d.ts +0 -234
  103. package/dist/next.d.ts +0 -178
  104. package/dist/push.d.ts +0 -109
  105. package/dist/react.d.ts +0 -319
  106. package/dist/server.d.ts +0 -534
  107. package/dist/shared-DkWlEd2Z.d.ts +0 -18
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Foir-hosted first-party sessions, from the browser — the model for a
3
+ * backend-less SPA (Vite, CRA, a static site) that wants no tokens in JS.
4
+ *
5
+ * The sibling `fetchHostedSession` in ./hosted does the same exchange from a
6
+ * SERVER: it reads the cookie off an incoming request and forwards it by hand.
7
+ * A browser cannot set a `Cookie` header, so that function is unusable from an
8
+ * SPA by construction. This one lets the browser send the cookie the only way it
9
+ * can — `credentials: 'include'` — and keeps the resulting bearer in memory.
10
+ *
11
+ * How the pieces divide:
12
+ *
13
+ * cookie — long-lived (30d), HttpOnly, first-party on your registrable
14
+ * domain, set by Foir when hosted login finalizes. JS cannot read
15
+ * it, so XSS cannot exfiltrate it.
16
+ * bearer — 15 minutes, held in a closure here. Never written to
17
+ * localStorage/sessionStorage/IndexedDB, never in a cookie of ours.
18
+ *
19
+ * There is no refresh token in this flow. Re-exchanging the cookie IS the
20
+ * refresh, which is why nothing here needs rotation or reuse-detection: the
21
+ * durable credential is an opaque server-side session handle, revocable at any
22
+ * time from the admin panel or `signOut()`.
23
+ *
24
+ * REQUIRES a verified custom login domain that shares a registrable domain with
25
+ * your app (app.acme.com + login.acme.com). That is what makes the cookie
26
+ * first-party and lets `SameSite=Lax` send it. The same endpoints exist on
27
+ * api.foir.dev but a browser cannot reach them there — the cookie would be
28
+ * cross-site, and that CORS surface deliberately refuses credentials.
29
+ */
30
+ export interface FoirSessionConfig {
31
+ /**
32
+ * Your verified custom login domain, e.g. `https://login.acme.com`. Must share
33
+ * a registrable domain with the app calling it.
34
+ */
35
+ loginOrigin: string;
36
+ /** Session-exchange path. Default `/session/token`. */
37
+ sessionPath?: string;
38
+ /** Logout path. Default `/session/logout`. */
39
+ logoutPath?: string;
40
+ /** Seconds before expiry to re-exchange. Default 60. */
41
+ refreshSkewSeconds?: number;
42
+ /**
43
+ * Called when the session goes away — the cookie expired, was revoked, or
44
+ * `signOut()` ran. Use it to redirect to `signInUrl()`.
45
+ */
46
+ onSignOut?: () => void;
47
+ }
48
+ /** The identity behind the current session. */
49
+ export interface FoirSessionUser {
50
+ customerId: string;
51
+ email: string;
52
+ }
53
+ export interface FoirSessionClient {
54
+ /**
55
+ * The current access token, exchanging or re-exchanging the cookie as needed.
56
+ * Null when there is no session. Concurrent callers share one in-flight
57
+ * exchange. Call this before each API request — do not cache the result.
58
+ */
59
+ getToken(): Promise<string | null>;
60
+ /** The signed-in customer, or null. Exchanges if the session isn't loaded yet. */
61
+ getUser(): Promise<FoirSessionUser | null>;
62
+ /** Revoke the session server-side and clear the cookie. Idempotent. */
63
+ signOut(): Promise<void>;
64
+ /** Where to send the browser to sign in. */
65
+ signInUrl(returnTo?: string): string;
66
+ /** Observe session changes. Returns an unsubscribe function. */
67
+ subscribe(listener: (user: FoirSessionUser | null) => void): () => void;
68
+ /** Cancel the refresh timer. Call on unmount. */
69
+ destroy(): void;
70
+ }
71
+ /**
72
+ * Create a session client. Safe to call during SSR — it does nothing until a
73
+ * method is invoked, and `getToken()` on the server returns null rather than
74
+ * throwing (there is no cookie jar to draw on).
75
+ */
76
+ export declare function createFoirSessionClient(config: FoirSessionConfig): FoirSessionClient;
@@ -0,0 +1,137 @@
1
+ // src/auth/session-client.ts
2
+ var DEFAULT_SESSION_PATH = "/session/token";
3
+ var DEFAULT_LOGOUT_PATH = "/session/logout";
4
+ var DEFAULT_REFRESH_SKEW_SECONDS = 60;
5
+ var MIN_REFRESH_DELAY_MS = 5e3;
6
+ var CSRF_HEADER = { "X-Foir-Session": "1" };
7
+ function createFoirSessionClient(config) {
8
+ const sessionUrl = `${trimSlash(config.loginOrigin)}${config.sessionPath ?? DEFAULT_SESSION_PATH}`;
9
+ const logoutUrl = `${trimSlash(config.loginOrigin)}${config.logoutPath ?? DEFAULT_LOGOUT_PATH}`;
10
+ const skewMs = (config.refreshSkewSeconds ?? DEFAULT_REFRESH_SKEW_SECONDS) * 1e3;
11
+ let cached = null;
12
+ let inflight = null;
13
+ let timer = null;
14
+ let destroyed = false;
15
+ const listeners = /* @__PURE__ */ new Set();
16
+ function emit(user) {
17
+ for (const l of listeners) l(user);
18
+ }
19
+ function clearTimer() {
20
+ if (timer !== null) {
21
+ clearTimeout(timer);
22
+ timer = null;
23
+ }
24
+ }
25
+ function scheduleRefresh(expiresAt) {
26
+ clearTimer();
27
+ if (destroyed || typeof setTimeout === "undefined") return;
28
+ const delay = Math.max(expiresAt - Date.now() - skewMs, MIN_REFRESH_DELAY_MS);
29
+ timer = setTimeout(() => {
30
+ void exchange();
31
+ }, delay);
32
+ timer.unref?.();
33
+ }
34
+ function forget() {
35
+ const had = cached !== null;
36
+ cached = null;
37
+ clearTimer();
38
+ if (had) emit(null);
39
+ }
40
+ function exchange() {
41
+ if (inflight) return inflight;
42
+ const pending = (async () => {
43
+ let res;
44
+ try {
45
+ res = await fetch(sessionUrl, {
46
+ method: "GET",
47
+ // The whole point: send the first-party cookie cross-origin (but
48
+ // same-site) to the login domain. Without this the cookie is not sent.
49
+ credentials: "include",
50
+ headers: { ...CSRF_HEADER }
51
+ });
52
+ } catch {
53
+ return cached;
54
+ }
55
+ if (res.status === 401) {
56
+ forget();
57
+ config.onSignOut?.();
58
+ return null;
59
+ }
60
+ if (!res.ok) return cached;
61
+ let json;
62
+ try {
63
+ json = await res.json();
64
+ } catch {
65
+ return cached;
66
+ }
67
+ if (!json.access_token || !json.customer_id) return cached;
68
+ const expiresIn = typeof json.expires_in === "number" ? json.expires_in : 0;
69
+ const next = {
70
+ token: json.access_token,
71
+ expiresAt: Date.now() + expiresIn * 1e3,
72
+ user: { customerId: json.customer_id, email: json.email ?? "" }
73
+ };
74
+ const changed = cached?.user.customerId !== next.user.customerId;
75
+ cached = next;
76
+ scheduleRefresh(next.expiresAt);
77
+ if (changed) emit(next.user);
78
+ return next;
79
+ })();
80
+ inflight = pending;
81
+ void pending.finally(() => {
82
+ if (inflight === pending) inflight = null;
83
+ });
84
+ return pending;
85
+ }
86
+ function fresh() {
87
+ if (!cached) return null;
88
+ return Date.now() < cached.expiresAt - skewMs ? cached : null;
89
+ }
90
+ return {
91
+ async getToken() {
92
+ const hit = fresh();
93
+ if (hit) return hit.token;
94
+ const got = await exchange();
95
+ return got?.token ?? null;
96
+ },
97
+ async getUser() {
98
+ const hit = fresh();
99
+ if (hit) return hit.user;
100
+ const got = await exchange();
101
+ return got?.user ?? null;
102
+ },
103
+ async signOut() {
104
+ try {
105
+ await fetch(logoutUrl, {
106
+ method: "POST",
107
+ credentials: "include",
108
+ headers: { ...CSRF_HEADER }
109
+ });
110
+ } catch {
111
+ }
112
+ forget();
113
+ config.onSignOut?.();
114
+ },
115
+ signInUrl(returnTo) {
116
+ const dest = new URL(trimSlash(config.loginOrigin));
117
+ const target = returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
118
+ if (target && target !== "/") dest.searchParams.set("returnTo", target);
119
+ return dest.toString();
120
+ },
121
+ subscribe(listener) {
122
+ listeners.add(listener);
123
+ return () => listeners.delete(listener);
124
+ },
125
+ destroy() {
126
+ destroyed = true;
127
+ clearTimer();
128
+ listeners.clear();
129
+ }
130
+ };
131
+ }
132
+ function trimSlash(s) {
133
+ return s.replace(/\/+$/, "");
134
+ }
135
+ export {
136
+ createFoirSessionClient
137
+ };