@flow-industries/id 0.18.0 → 0.19.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.
Files changed (61) hide show
  1. package/README.md +19 -0
  2. package/dist/sdk/client/access-key.d.ts +1 -1
  3. package/dist/sdk/client/access-key.js +20 -6
  4. package/dist/sdk/client/create-flow.js +19 -11
  5. package/dist/sdk/client/dialog-host.js +8 -2
  6. package/dist/sdk/client/flow-widget.js +2 -1
  7. package/dist/sdk/client/idb.d.ts +2 -2
  8. package/dist/sdk/client/idb.js +17 -2
  9. package/dist/sdk/client/profile-button.js +1 -1
  10. package/dist/sdk/client/rooms.js +19 -4
  11. package/dist/sdk/client/session.js +8 -5
  12. package/dist/sdk/client/signing.js +22 -7
  13. package/dist/sdk/client/store.js +4 -5
  14. package/dist/sdk/cookies.d.ts +3 -1
  15. package/dist/sdk/cookies.js +11 -9
  16. package/dist/sdk/dialog/remote/Messenger.d.ts +0 -5
  17. package/dist/sdk/dialog/remote/Messenger.js +26 -14
  18. package/dist/sdk/driver-error.d.ts +8 -0
  19. package/dist/sdk/driver-error.js +17 -0
  20. package/dist/sdk/hex.d.ts +6 -0
  21. package/dist/sdk/hex.js +6 -0
  22. package/dist/sdk/id-host.d.ts +1 -1
  23. package/dist/sdk/id-host.js +4 -5
  24. package/dist/sdk/json.d.ts +15 -0
  25. package/dist/sdk/json.js +3 -0
  26. package/dist/sdk/react/flow-widget.js +2 -2
  27. package/dist/sdk/react/hooks.d.ts +8 -4
  28. package/dist/sdk/react/hooks.js +1 -10
  29. package/dist/sdk/react/profile-button.js +2 -2
  30. package/dist/sdk/server.js +2 -1
  31. package/dist/sdk/session-core.d.ts +1 -6
  32. package/dist/sdk/session-core.js +7 -4
  33. package/dist/sdk/session-route.d.ts +0 -20
  34. package/dist/sdk/session-route.js +25 -9
  35. package/dist/sdk/start/graceful-shutdown.d.ts +8 -0
  36. package/dist/sdk/start/graceful-shutdown.js +81 -0
  37. package/dist/sdk/start/index.d.ts +2 -0
  38. package/dist/sdk/start/index.js +3 -2
  39. package/dist/sdk/token-expiry.js +3 -1
  40. package/dist/sdk/types/cosmetics.d.ts +162 -0
  41. package/dist/sdk/types/cosmetics.js +78 -0
  42. package/dist/sdk/types/events.d.ts +4 -1
  43. package/dist/sdk/types/game.d.ts +58 -0
  44. package/dist/sdk/types/game.js +8 -0
  45. package/dist/sdk/types/index.d.ts +8 -4
  46. package/dist/sdk/types/index.js +2 -0
  47. package/dist/sdk/types/messenger.d.ts +1 -1
  48. package/dist/sdk/types/protocol.d.ts +6 -5
  49. package/dist/sdk/types/protocol.js +10 -4
  50. package/dist/sdk/types/room-events.d.ts +49 -1
  51. package/dist/sdk/types/rooms.d.ts +66 -3
  52. package/dist/sdk/types/rooms.js +0 -1
  53. package/dist/sdk/types/sdk.d.ts +2 -1
  54. package/dist/sdk/types/server.d.ts +18 -0
  55. package/dist/sdk/types/xp.d.ts +8 -7
  56. package/dist/sdk/verify.js +1 -0
  57. package/dist/sdk/wagmi/index.d.ts +7 -3
  58. package/dist/sdk/wagmi/index.js +19 -32
  59. package/package.json +15 -8
  60. package/dist/sdk/client/refresh-store.d.ts +0 -22
  61. package/dist/sdk/client/refresh-store.js +0 -66
@@ -5,33 +5,14 @@ flowConnector.type = "flow";
5
5
  function loginOptionsFromCapabilities(capabilities) {
6
6
  const signUp = capabilities?.type === "sign-up" || capabilities?.createAccount;
7
7
  const signIn = capabilities?.type === "sign-in" || capabilities?.signIn;
8
+ const headless = !signUp &&
9
+ (capabilities?.signInHeadless || capabilities?.type === "sign-in");
8
10
  return {
9
- ...(signUp ? { signUp: true } : {}),
10
- ...(!signUp && signIn ? { signIn: true } : {}),
11
- ...(!signUp &&
12
- (capabilities?.signInHeadless || capabilities?.type === "sign-in")
13
- ? { signInHeadless: true }
14
- : {}),
11
+ signUp: signUp ? true : undefined,
12
+ signIn: !signUp && signIn ? true : undefined,
13
+ signInHeadless: headless ? true : undefined,
15
14
  };
16
15
  }
17
- /**
18
- * Wagmi connector adapter for Flow. Drop into your wagmi config with
19
- * `connectors: [flowConnector()]` — no arguments needed in the common case.
20
- *
21
- * Resolution order for the underlying Flow instance:
22
- * 1. `parameters.flow` (explicit override — useful for tests)
23
- * 2. The Flow singleton set by a prior `createFlow()` call
24
- * 3. Lazily created on first connector use (also sets the singleton)
25
- *
26
- * This means wagmi-only apps get a working Flow without ever calling
27
- * `createFlow`, and apps mixing wagmi + direct SDK calls automatically
28
- * share state because they all resolve to the same singleton.
29
- *
30
- * The `setup()` method subscribes to the Flow store and emits wagmi
31
- * `connect`/`disconnect` events whenever the address changes — that's how
32
- * wagmi state stays in sync when login/logout happens via the SDK rather
33
- * than through wagmi's own `connect()`.
34
- */
35
16
  export function flowConnector(parameters = {}) {
36
17
  let flow = parameters.flow ?? null;
37
18
  return createConnector((config) => {
@@ -64,6 +45,8 @@ export function flowConnector(parameters = {}) {
64
45
  return;
65
46
  lastAddress = next;
66
47
  if (next) {
48
+ /* SAFETY: wagmi types `accounts` as a readonly tuple of its own `Address` brand;
49
+ `next` is the checksummed address this connector just resolved. */
67
50
  config.emitter.emit("connect", {
68
51
  accounts: [next],
69
52
  chainId: config.chains[0]?.id ?? 1,
@@ -76,6 +59,7 @@ export function flowConnector(parameters = {}) {
76
59
  },
77
60
  async connect(params = {}) {
78
61
  const f = getFlow();
62
+ // SAFETY: wagmi passes its own connect options; WagmiConnectParams names the subset read.
79
63
  const connectParams = params;
80
64
  const isReconnecting = connectParams.isReconnecting;
81
65
  // Wagmi calls connect({ isReconnecting: true }) on mount when it
@@ -135,15 +119,18 @@ export function flowConnector(parameters = {}) {
135
119
  },
136
120
  async getClient({ chainId } = {}) {
137
121
  const f = getFlow();
138
- return f.walletClient({ ...(chainId ? { chainId } : {}) });
122
+ /* SAFETY: wagmi types getClient against viem's Client generic over its own chain
123
+ config; the Flow wallet client implements that surface. */
124
+ return f.walletClient(chainId ? { chainId } : {});
139
125
  },
140
- async getProvider({ chainId } = {}) {
141
- const client = await this.getClient({
142
- ...(chainId ? { chainId } : {}),
143
- });
144
- return {
145
- request: client.request,
146
- };
126
+ async getProvider(options = {}) {
127
+ const client = await this.getClient?.({ chainId: options.chainId });
128
+ /* SAFETY: the wallet client viem builds always carries `request`; wagmi's Client type
129
+ does not name it, which is the only reason this is stated rather than inferred. */
130
+ const request = Object.entries(Object(client)).find(([k]) => k === "request")?.[1];
131
+ /* SAFETY: the wallet client viem builds always carries `request`; wagmi's Client type
132
+ does not name it, which is the only reason this is read by name. */
133
+ return { request: request };
147
134
  },
148
135
  onAccountsChanged() { },
149
136
  onChainChanged(chain) {
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "@flow-industries/id",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/flow-industries/auth.git"
7
+ },
4
8
  "main": "./dist/sdk/client/index.js",
5
9
  "module": "./dist/sdk/client/index.js",
6
10
  "types": "./dist/sdk/client/index.d.ts",
@@ -47,10 +51,7 @@
47
51
  "dist/sdk"
48
52
  ],
49
53
  "scripts": {
50
- "dev": "bunx vite & bunx vite --config vite.playground.config.ts & bunx vite --config vite.landing.config.ts & bun run --watch src/index.ts",
51
- "dev:dialog": "bunx vite",
52
- "dev:playground": "bunx vite --config vite.playground.config.ts",
53
- "dev:landing": "bunx vite --config vite.landing.config.ts",
54
+ "dev": "./dev.sh",
54
55
  "build:dialog": "bunx vite build",
55
56
  "build:playground": "bunx vite build --config vite.playground.config.ts",
56
57
  "build:landing": "bunx vite build --config vite.landing.config.ts",
@@ -59,12 +60,12 @@
59
60
  "prepublishOnly": "bun run build:sdk",
60
61
  "start": "bun run src/index.ts",
61
62
  "worker": "bun run src/worker.ts",
62
- "email:dev": "email dev --dir src/emails --port 3010",
63
63
  "db:generate": "bunx @better-auth/cli generate",
64
64
  "db:migrate:gen": "bunx drizzle-kit generate",
65
65
  "db:migrate": "bunx drizzle-kit migrate",
66
66
  "db:push": "bunx drizzle-kit push",
67
67
  "db:studio": "bunx drizzle-kit studio",
68
+ "usernames:refresh": "bun run scripts/generate-reserved-usernames.ts",
68
69
  "lint": "biome check",
69
70
  "test": "bun test src",
70
71
  "format": "biome format --write",
@@ -91,9 +92,9 @@
91
92
  },
92
93
  "devDependencies": {
93
94
  "@better-auth/cli": "^1.4.17",
94
- "@biomejs/biome": "2.4.12",
95
+ "@biomejs/biome": "2.5.8",
95
96
  "@electric-sql/pglite": "^0.5.4",
96
- "@flow-industries/lint": "0.1.0",
97
+ "@flow-industries/lint": "0.3.0",
97
98
  "@react-email/ui": "^6.6.0",
98
99
  "@tailwindcss/vite": "^4.1.18",
99
100
  "@tanstack/react-router-devtools": "^1.160.0",
@@ -104,6 +105,7 @@
104
105
  "@types/react": "^19.2.13",
105
106
  "@types/react-dom": "^19.2.3",
106
107
  "@vitejs/plugin-react": "^5.1.3",
108
+ "biome-anti-slop": "0.1.0",
107
109
  "drizzle-kit": "^0.31.8",
108
110
  "pino-pretty": "^13.1.3",
109
111
  "react-email": "^6.6.0",
@@ -112,6 +114,10 @@
112
114
  "typescript": "^5",
113
115
  "vite": "^7.3.1"
114
116
  },
117
+ "trustedDependencies": [
118
+ "esbuild",
119
+ "sharp"
120
+ ],
115
121
  "dependencies": {
116
122
  "@aws-sdk/client-s3": "^3.1073.0",
117
123
  "@flow-industries/ui": "^0.19.0",
@@ -146,6 +152,7 @@
146
152
  "tempo.ts": "^0.14.2",
147
153
  "viem": "2.47.10",
148
154
  "wagmi": "^3.4.2",
155
+ "zod": "^4.3.6",
149
156
  "zustand": "^5.0.11"
150
157
  }
151
158
  }
@@ -1,22 +0,0 @@
1
- import type { RefreshCookieConfig } from "../types";
2
- /**
3
- * First-party persistence for the rotating refresh token. It lives in the
4
- * consumer app's OWN localStorage (origin-scoped), so it survives reloads and
5
- * navigations and — unlike the old id.flow.industries cookie — is never a
6
- * third-party cookie, so iOS ITP can't block it. Keyed by `host` so pointing
7
- * the SDK at a different Flow ID origin (dev vs prod) doesn't collide.
8
- */
9
- export interface RefreshStore {
10
- get(): string | null;
11
- set(token: string): void;
12
- clear(): void;
13
- }
14
- /**
15
- * With a cookie config (`createFlow({ cookies: true })`) the token is
16
- * additionally mirrored into a first-party cookie so the app's own server can
17
- * refresh the session during SSR. Reads prefer the cookie: the only writer
18
- * that updates one side without the other is that server (Set-Cookie only),
19
- * and its value is always a successor of whatever localStorage still holds;
20
- * every client-side write hits both in the same call.
21
- */
22
- export declare function makeRefreshStore(host: string, cookie?: RefreshCookieConfig): RefreshStore;
@@ -1,66 +0,0 @@
1
- import { clearBrowserCookie, REFRESH_COOKIE_MAX_AGE_S, readBrowserCookie, writeBrowserCookie, } from "../cookies";
2
- /**
3
- * With a cookie config (`createFlow({ cookies: true })`) the token is
4
- * additionally mirrored into a first-party cookie so the app's own server can
5
- * refresh the session during SSR. Reads prefer the cookie: the only writer
6
- * that updates one side without the other is that server (Set-Cookie only),
7
- * and its value is always a successor of whatever localStorage still holds;
8
- * every client-side write hits both in the same call.
9
- */
10
- export function makeRefreshStore(host, cookie) {
11
- const key = `flow.id.refresh:${host}`;
12
- const readLocal = () => {
13
- try {
14
- return localStorage.getItem(key);
15
- }
16
- catch {
17
- return null;
18
- }
19
- };
20
- const readCookie = () => {
21
- if (!cookie || typeof document === "undefined")
22
- return null;
23
- return readBrowserCookie(document.cookie, cookie.name);
24
- };
25
- const store = {
26
- get() {
27
- return readCookie() ?? readLocal();
28
- },
29
- set(token) {
30
- // Never persist a missing token: localStorage.setItem coerces undefined
31
- // to the literal "undefined", which later reads back as a bogus bearer
32
- // and 401s as refresh_token_invalid. Fail safe by ignoring it instead.
33
- if (!token)
34
- return;
35
- try {
36
- localStorage.setItem(key, token);
37
- }
38
- catch { }
39
- if (cookie) {
40
- writeBrowserCookie(cookie.name, token, {
41
- maxAge: REFRESH_COOKIE_MAX_AGE_S,
42
- secure: cookie.secure,
43
- });
44
- }
45
- },
46
- clear() {
47
- try {
48
- localStorage.removeItem(key);
49
- }
50
- catch { }
51
- if (cookie)
52
- clearBrowserCookie(cookie.name, { secure: cookie.secure });
53
- },
54
- };
55
- // One-time reconcile so exactly one canonical value exists before the first
56
- // refresh: an SSR rotation updates only the cookie, leaving localStorage one
57
- // generation behind — presenting that stale token would look like reuse.
58
- const fromCookie = readCookie();
59
- if (fromCookie && fromCookie !== readLocal()) {
60
- try {
61
- localStorage.setItem(key, fromCookie);
62
- }
63
- catch { }
64
- }
65
- return store;
66
- }