@flow-industries/id 0.16.0 → 0.18.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/dist/sdk/client/flow-widget.d.ts +20 -0
- package/dist/sdk/client/flow-widget.js +88 -0
- package/dist/sdk/client/iframe-host.d.ts +12 -0
- package/dist/sdk/client/iframe-host.js +14 -0
- package/dist/sdk/client/index.d.ts +2 -1
- package/dist/sdk/client/index.js +1 -0
- package/dist/sdk/client/profile-button.js +20 -12
- package/dist/sdk/react/flow-widget.d.ts +16 -0
- package/dist/sdk/react/flow-widget.js +52 -0
- package/dist/sdk/react/index.d.ts +2 -1
- package/dist/sdk/react/index.js +1 -0
- package/dist/sdk/react/profile-button.d.ts +5 -0
- package/dist/sdk/react/profile-button.js +19 -3
- package/dist/sdk/types/events.d.ts +8 -1
- package/dist/sdk/types/index.d.ts +3 -2
- package/dist/sdk/types/room-events.d.ts +133 -0
- package/dist/sdk/types/room-events.js +14 -0
- package/dist/sdk/types/rooms.d.ts +8 -0
- package/dist/sdk/types/sdk.d.ts +66 -2
- package/package.json +2 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FlowWidgetHandle, MountFlowWidgetOptions } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Mounts one of the inline Flow ID widgets — the XP strip, the action timer —
|
|
4
|
+
* as a transparent iframe the embedder positions and sizes itself.
|
|
5
|
+
*
|
|
6
|
+
* The reason this exists rather than embedders hand-rolling an `<iframe>`: the
|
|
7
|
+
* widget reads the Flow ID API from a cross-site frame, where its own origin's
|
|
8
|
+
* cookie is unavailable or bound to a different audience, so it needs the
|
|
9
|
+
* host's access token. It gets one by asking over the postMessage bridge, and
|
|
10
|
+
* only a frame that received the `init` handshake knows which origin to ask.
|
|
11
|
+
* A hand-rolled iframe never sends `init` and never gets an answer, so it is
|
|
12
|
+
* silently limited to whatever the cookie happens to allow (AUTH-171).
|
|
13
|
+
*
|
|
14
|
+
* The handle exposes the frame instead of styling it, because each embedder
|
|
15
|
+
* wants different geometry (the in-game overlay is fixed and click-through,
|
|
16
|
+
* the menu's is a laid-out block) and none of that belongs in the SDK.
|
|
17
|
+
*
|
|
18
|
+
* No-op under SSR.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createFlowWidget(options: MountFlowWidgetOptions): FlowWidgetHandle;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { resolveIdHost } from "../id-host";
|
|
2
|
+
import { getFlow, requireFlow } from "./create-flow";
|
|
3
|
+
import { answerTokenRequest, bridgeToWindow, makeIframe } from "./iframe-host";
|
|
4
|
+
/**
|
|
5
|
+
* The inline widgets an embedder may mount, mapped to the dialog routes that
|
|
6
|
+
* serve them. Callers name a widget rather than passing a route so the dialog's
|
|
7
|
+
* URL space stays an internal detail and an embedder can't point this at an
|
|
8
|
+
* auth route, which would render a sign-in ceremony inside a decorative frame.
|
|
9
|
+
*/
|
|
10
|
+
const WIDGET_ROUTE = {
|
|
11
|
+
xp: "/dialog/xp-widget",
|
|
12
|
+
"action-timer": "/dialog/action-timer",
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Mounts one of the inline Flow ID widgets — the XP strip, the action timer —
|
|
16
|
+
* as a transparent iframe the embedder positions and sizes itself.
|
|
17
|
+
*
|
|
18
|
+
* The reason this exists rather than embedders hand-rolling an `<iframe>`: the
|
|
19
|
+
* widget reads the Flow ID API from a cross-site frame, where its own origin's
|
|
20
|
+
* cookie is unavailable or bound to a different audience, so it needs the
|
|
21
|
+
* host's access token. It gets one by asking over the postMessage bridge, and
|
|
22
|
+
* only a frame that received the `init` handshake knows which origin to ask.
|
|
23
|
+
* A hand-rolled iframe never sends `init` and never gets an answer, so it is
|
|
24
|
+
* silently limited to whatever the cookie happens to allow (AUTH-171).
|
|
25
|
+
*
|
|
26
|
+
* The handle exposes the frame instead of styling it, because each embedder
|
|
27
|
+
* wants different geometry (the in-game overlay is fixed and click-through,
|
|
28
|
+
* the menu's is a laid-out block) and none of that belongs in the SDK.
|
|
29
|
+
*
|
|
30
|
+
* No-op under SSR.
|
|
31
|
+
*/
|
|
32
|
+
export function createFlowWidget(options) {
|
|
33
|
+
if (typeof document === "undefined")
|
|
34
|
+
return {
|
|
35
|
+
frame: null,
|
|
36
|
+
post() { },
|
|
37
|
+
setTheme() { },
|
|
38
|
+
destroy() { },
|
|
39
|
+
};
|
|
40
|
+
const flow = options.flow ?? getFlow() ?? requireFlow();
|
|
41
|
+
const host = resolveIdHost(options.host ?? flow.host);
|
|
42
|
+
const hostOrigin = new URL(host).origin;
|
|
43
|
+
let theme = options.theme ?? "light dark";
|
|
44
|
+
const route = WIDGET_ROUTE[options.widget];
|
|
45
|
+
const frame = makeIframe(`${host}${route}`);
|
|
46
|
+
frame.style.background = "transparent";
|
|
47
|
+
const container = options.container ?? document.body;
|
|
48
|
+
// Appended before bridging: `contentWindow` is null until the frame is in the
|
|
49
|
+
// document, and the bridge is bound to that window.
|
|
50
|
+
container.appendChild(frame);
|
|
51
|
+
const bridge = bridgeToWindow(frame.contentWindow, {
|
|
52
|
+
targetOrigin: hostOrigin,
|
|
53
|
+
});
|
|
54
|
+
bridge.on("__internal", (payload) => {
|
|
55
|
+
if (payload.type === "profile-token-request")
|
|
56
|
+
void answerTokenRequest(bridge, flow, payload.id);
|
|
57
|
+
});
|
|
58
|
+
// The widget already loads its own route directly, so `route` here only
|
|
59
|
+
// confirms where the frame is; what the handshake is really for is telling
|
|
60
|
+
// the dialog which origin it is embedded by, which is what it later asks for
|
|
61
|
+
// a token.
|
|
62
|
+
void bridge.send("__internal", {
|
|
63
|
+
type: "init",
|
|
64
|
+
mode: "iframe",
|
|
65
|
+
referrer: { title: document.title },
|
|
66
|
+
theme: { colorScheme: theme },
|
|
67
|
+
route,
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
frame,
|
|
71
|
+
post(message) {
|
|
72
|
+
frame.contentWindow?.postMessage(message, hostOrigin);
|
|
73
|
+
},
|
|
74
|
+
setTheme(next) {
|
|
75
|
+
if (next === theme)
|
|
76
|
+
return;
|
|
77
|
+
theme = next;
|
|
78
|
+
void bridge.send("__internal", {
|
|
79
|
+
type: "set-theme",
|
|
80
|
+
theme: { colorScheme: next },
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
destroy() {
|
|
84
|
+
bridge.destroy();
|
|
85
|
+
frame.remove();
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Messenger from "../dialog/remote/Messenger";
|
|
2
|
+
import type { Flow } from "../types";
|
|
2
3
|
/**
|
|
3
4
|
* Permissions the dialog iframe needs to run WebAuthn passkey ceremonies and
|
|
4
5
|
* copy recovery values. Shared by every Flow iframe host (the dialog host and
|
|
@@ -21,3 +22,14 @@ export declare function makeIframe(src: string): HTMLIFrameElement;
|
|
|
21
22
|
export declare function bridgeToWindow(target: Window, options?: {
|
|
22
23
|
targetOrigin?: string;
|
|
23
24
|
}): Messenger.Bridge;
|
|
25
|
+
/**
|
|
26
|
+
* Answers a widget's request for the host's credential. Every Flow iframe that
|
|
27
|
+
* reads the Flow ID API from a cross-site frame asks for this, because its own
|
|
28
|
+
* origin's cookie is unavailable or bound to another audience there.
|
|
29
|
+
*
|
|
30
|
+
* `getToken` refreshes a token that is missing or near expiry, so a widget left
|
|
31
|
+
* open past the 1h access-token lifetime still gets a live one rather than a
|
|
32
|
+
* 401. A failure answers `null` — "no session", which widgets render as signed
|
|
33
|
+
* out — instead of leaving the request to time out.
|
|
34
|
+
*/
|
|
35
|
+
export declare function answerTokenRequest(bridge: Messenger.Bridge, flow: Flow, id: string): Promise<void>;
|
|
@@ -32,3 +32,17 @@ export function bridgeToWindow(target, options = {}) {
|
|
|
32
32
|
waitForReady: true,
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Answers a widget's request for the host's credential. Every Flow iframe that
|
|
37
|
+
* reads the Flow ID API from a cross-site frame asks for this, because its own
|
|
38
|
+
* origin's cookie is unavailable or bound to another audience there.
|
|
39
|
+
*
|
|
40
|
+
* `getToken` refreshes a token that is missing or near expiry, so a widget left
|
|
41
|
+
* open past the 1h access-token lifetime still gets a live one rather than a
|
|
42
|
+
* 401. A failure answers `null` — "no session", which widgets render as signed
|
|
43
|
+
* out — instead of leaving the request to time out.
|
|
44
|
+
*/
|
|
45
|
+
export async function answerTokenRequest(bridge, flow, id) {
|
|
46
|
+
const token = await flow.getToken().catch(() => null);
|
|
47
|
+
void bridge.send("__internal", { type: "profile-token", id, token });
|
|
48
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { defaultIdHost, isLocalHostname } from "../id-host";
|
|
2
|
-
export type { AccessKeyOptions, AdditionalSession, Address, ConnectCapabilities, ConnectResponse, CreateFlowOptions, DialogHost, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, LoginOptions, MethodName, MountProfileOptions, ProfileButtonHandle, ProfilePosition, Session, } from "../types";
|
|
2
|
+
export type { AccessKeyOptions, AdditionalSession, Address, ConnectCapabilities, ConnectResponse, CreateFlowOptions, DialogHost, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, FlowWidgetHandle, FlowWidgetName, LoginOptions, MethodName, MountFlowWidgetOptions, MountProfileOptions, ProfileButtonHandle, ProfilePosition, Session, } from "../types";
|
|
3
3
|
export { createFlow, getFlow, requireFlow, resetFlow } from "./create-flow";
|
|
4
4
|
export { createDialogHost } from "./dialog-host";
|
|
5
|
+
export { createFlowWidget } from "./flow-widget";
|
|
5
6
|
export { METHODS } from "./methods";
|
|
6
7
|
export { createProfileButton } from "./profile-button";
|
|
7
8
|
export { createRoomsApi, RoomsRequestError } from "./rooms";
|
package/dist/sdk/client/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { defaultIdHost, isLocalHostname } from "../id-host";
|
|
2
2
|
export { createFlow, getFlow, requireFlow, resetFlow } from "./create-flow";
|
|
3
3
|
export { createDialogHost } from "./dialog-host";
|
|
4
|
+
export { createFlowWidget } from "./flow-widget";
|
|
4
5
|
export { METHODS } from "./methods";
|
|
5
6
|
export { createProfileButton } from "./profile-button";
|
|
6
7
|
export { createRoomsApi, RoomsRequestError } from "./rooms";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveIdHost } from "../id-host";
|
|
2
2
|
import { getFlow, requireFlow } from "./create-flow";
|
|
3
|
-
import { bridgeToWindow, makeIframe } from "./iframe-host";
|
|
3
|
+
import { answerTokenRequest, bridgeToWindow, makeIframe } from "./iframe-host";
|
|
4
4
|
const POSITION_STYLE = {
|
|
5
5
|
"top-right": { top: "0", right: "0" },
|
|
6
6
|
"top-left": { top: "0", left: "0" },
|
|
@@ -29,11 +29,13 @@ const OVERLAY_STYLE = {
|
|
|
29
29
|
*/
|
|
30
30
|
export function createProfileButton(options) {
|
|
31
31
|
if (typeof document === "undefined")
|
|
32
|
-
return { destroy() { } };
|
|
32
|
+
return { setTheme() { }, destroy() { } };
|
|
33
33
|
const flow = options.flow ?? getFlow() ?? requireFlow();
|
|
34
34
|
const host = resolveIdHost(options.host ?? flow.host);
|
|
35
35
|
const hostOrigin = new URL(host).origin;
|
|
36
|
-
|
|
36
|
+
// Mutable so `setTheme` recolors over the open bridge, and so a dialog opened
|
|
37
|
+
// after a toggle is initialized with the theme the host is actually showing.
|
|
38
|
+
let theme = options.theme ?? "light dark";
|
|
37
39
|
let createdContainer = null;
|
|
38
40
|
let container = options.container ?? null;
|
|
39
41
|
if (!container) {
|
|
@@ -81,13 +83,6 @@ export function createProfileButton(options) {
|
|
|
81
83
|
? { username: user.username, isGuest: user.isGuest === true }
|
|
82
84
|
: null;
|
|
83
85
|
}
|
|
84
|
-
// The widget's only credential. `getToken` refreshes a token that is missing
|
|
85
|
-
// or near expiry, so a widget open on a page left idle past the 1h access-
|
|
86
|
-
// token lifetime still gets a live one instead of a 401.
|
|
87
|
-
async function answerTokenRequest(bridge, id) {
|
|
88
|
-
const token = await flow.getToken().catch(() => null);
|
|
89
|
-
void bridge.send("__internal", { type: "profile-token", id, token });
|
|
90
|
-
}
|
|
91
86
|
// ----- the persistent pill (stays mounted; sizes to its own content) -----
|
|
92
87
|
const pill = makeFrame();
|
|
93
88
|
let pillWidth = 0;
|
|
@@ -113,7 +108,7 @@ export function createProfileButton(options) {
|
|
|
113
108
|
showDialog();
|
|
114
109
|
}
|
|
115
110
|
else if (payload.type === "profile-token-request") {
|
|
116
|
-
void answerTokenRequest(pillBridge, payload.id);
|
|
111
|
+
void answerTokenRequest(pillBridge, flow, payload.id);
|
|
117
112
|
}
|
|
118
113
|
});
|
|
119
114
|
// ----- the dialog overlay (lazy; a separate fullscreen iframe layered ABOVE
|
|
@@ -143,7 +138,7 @@ export function createProfileButton(options) {
|
|
|
143
138
|
}
|
|
144
139
|
else if (payload.type === "profile-token-request") {
|
|
145
140
|
if (dialogBridge)
|
|
146
|
-
void answerTokenRequest(dialogBridge, payload.id);
|
|
141
|
+
void answerTokenRequest(dialogBridge, flow, payload.id);
|
|
147
142
|
}
|
|
148
143
|
});
|
|
149
144
|
void dialogBridge.send("__internal", {
|
|
@@ -190,6 +185,19 @@ export function createProfileButton(options) {
|
|
|
190
185
|
pushIdentity();
|
|
191
186
|
const unsubscribe = flow.subscribe(pushIdentity);
|
|
192
187
|
return {
|
|
188
|
+
setTheme(next) {
|
|
189
|
+
if (next === theme)
|
|
190
|
+
return;
|
|
191
|
+
theme = next;
|
|
192
|
+
void pillBridge.send("__internal", {
|
|
193
|
+
type: "set-theme",
|
|
194
|
+
theme: { colorScheme: next },
|
|
195
|
+
});
|
|
196
|
+
void dialogBridge?.send("__internal", {
|
|
197
|
+
type: "set-theme",
|
|
198
|
+
theme: { colorScheme: next },
|
|
199
|
+
});
|
|
200
|
+
},
|
|
193
201
|
destroy() {
|
|
194
202
|
unsubscribe();
|
|
195
203
|
pillBridge.destroy();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FlowWidgetProps } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Drops an inline Flow ID widget — the XP strip, the action timer — into a
|
|
4
|
+
* React tree. Renders a host element (style it via `className` for placement
|
|
5
|
+
* and size) that the widget iframe fills. Resolves the Flow instance from a
|
|
6
|
+
* `<FlowIdProvider>` or the createFlow() singleton.
|
|
7
|
+
*
|
|
8
|
+
* Prefer this over embedding the widget's URL in your own `<iframe>`: only a
|
|
9
|
+
* frame mounted this way completes the handshake that lets it authenticate as
|
|
10
|
+
* the signed-in user from a cross-site page (AUTH-171).
|
|
11
|
+
*
|
|
12
|
+
* A changing `theme` recolors the mounted widget over its open bridge rather
|
|
13
|
+
* than remounting it — see `<ProfileButton>` for why the frame must survive a
|
|
14
|
+
* light/dark toggle.
|
|
15
|
+
*/
|
|
16
|
+
export declare function FlowWidget({ widget, className, host, theme, }: FlowWidgetProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { createFlowWidget } from "../client/flow-widget";
|
|
4
|
+
import { useFlow } from "./hooks";
|
|
5
|
+
/**
|
|
6
|
+
* Drops an inline Flow ID widget — the XP strip, the action timer — into a
|
|
7
|
+
* React tree. Renders a host element (style it via `className` for placement
|
|
8
|
+
* and size) that the widget iframe fills. Resolves the Flow instance from a
|
|
9
|
+
* `<FlowIdProvider>` or the createFlow() singleton.
|
|
10
|
+
*
|
|
11
|
+
* Prefer this over embedding the widget's URL in your own `<iframe>`: only a
|
|
12
|
+
* frame mounted this way completes the handshake that lets it authenticate as
|
|
13
|
+
* the signed-in user from a cross-site page (AUTH-171).
|
|
14
|
+
*
|
|
15
|
+
* A changing `theme` recolors the mounted widget over its open bridge rather
|
|
16
|
+
* than remounting it — see `<ProfileButton>` for why the frame must survive a
|
|
17
|
+
* light/dark toggle.
|
|
18
|
+
*/
|
|
19
|
+
export function FlowWidget({ widget, className, host, theme, }) {
|
|
20
|
+
const flow = useFlow();
|
|
21
|
+
const ref = useRef(null);
|
|
22
|
+
const handleRef = useRef(null);
|
|
23
|
+
const themeRef = useRef(theme);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const container = ref.current;
|
|
26
|
+
if (!container)
|
|
27
|
+
return;
|
|
28
|
+
const handle = createFlowWidget({
|
|
29
|
+
widget,
|
|
30
|
+
container,
|
|
31
|
+
flow,
|
|
32
|
+
...(host ? { host } : {}),
|
|
33
|
+
...(themeRef.current ? { theme: themeRef.current } : {}),
|
|
34
|
+
});
|
|
35
|
+
handleRef.current = handle;
|
|
36
|
+
if (handle.frame) {
|
|
37
|
+
handle.frame.style.width = "100%";
|
|
38
|
+
handle.frame.style.height = "100%";
|
|
39
|
+
handle.frame.style.display = "block";
|
|
40
|
+
}
|
|
41
|
+
return () => {
|
|
42
|
+
handleRef.current = null;
|
|
43
|
+
handle.destroy();
|
|
44
|
+
};
|
|
45
|
+
}, [widget, flow, host]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
themeRef.current = theme;
|
|
48
|
+
if (theme)
|
|
49
|
+
handleRef.current?.setTheme(theme);
|
|
50
|
+
}, [theme]);
|
|
51
|
+
return _jsx("div", { ref: ref, className: className });
|
|
52
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export type { FlowIdProviderProps, ProfileButtonProps } from "../types";
|
|
1
|
+
export type { FlowIdProviderProps, FlowWidgetProps, ProfileButtonProps, } from "../types";
|
|
2
|
+
export { FlowWidget } from "./flow-widget";
|
|
2
3
|
export { useFlow, useFlowId, useFlowState, useRoom, useRooms } from "./hooks";
|
|
3
4
|
export { ProfileButton } from "./profile-button";
|
|
4
5
|
export { FlowIdProvider } from "./provider";
|
package/dist/sdk/react/index.js
CHANGED
|
@@ -5,5 +5,10 @@ import type { ProfileButtonProps } from "../types";
|
|
|
5
5
|
* the pill iframe mounts into; the iframe itself is sized to its content and
|
|
6
6
|
* expands to the profile dialog on click. Resolves the Flow instance from a
|
|
7
7
|
* `<FlowIdProvider>` or the createFlow() singleton.
|
|
8
|
+
*
|
|
9
|
+
* A changing `theme` recolors the mounted pill over its open bridge rather than
|
|
10
|
+
* remounting it: `theme` is deliberately absent from the mount effect's deps,
|
|
11
|
+
* because tearing the iframe down on a light/dark toggle reloads the widget and
|
|
12
|
+
* drops the pill out of the host's layout mid-toggle.
|
|
8
13
|
*/
|
|
9
14
|
export declare function ProfileButton({ className, host, theme }: ProfileButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -8,10 +8,17 @@ import { useFlow } from "./hooks";
|
|
|
8
8
|
* the pill iframe mounts into; the iframe itself is sized to its content and
|
|
9
9
|
* expands to the profile dialog on click. Resolves the Flow instance from a
|
|
10
10
|
* `<FlowIdProvider>` or the createFlow() singleton.
|
|
11
|
+
*
|
|
12
|
+
* A changing `theme` recolors the mounted pill over its open bridge rather than
|
|
13
|
+
* remounting it: `theme` is deliberately absent from the mount effect's deps,
|
|
14
|
+
* because tearing the iframe down on a light/dark toggle reloads the widget and
|
|
15
|
+
* drops the pill out of the host's layout mid-toggle.
|
|
11
16
|
*/
|
|
12
17
|
export function ProfileButton({ className, host, theme }) {
|
|
13
18
|
const flow = useFlow();
|
|
14
19
|
const ref = useRef(null);
|
|
20
|
+
const handleRef = useRef(null);
|
|
21
|
+
const themeRef = useRef(theme);
|
|
15
22
|
useEffect(() => {
|
|
16
23
|
const container = ref.current;
|
|
17
24
|
if (!container)
|
|
@@ -20,9 +27,18 @@ export function ProfileButton({ className, host, theme }) {
|
|
|
20
27
|
container,
|
|
21
28
|
flow,
|
|
22
29
|
...(host ? { host } : {}),
|
|
23
|
-
...(
|
|
30
|
+
...(themeRef.current ? { theme: themeRef.current } : {}),
|
|
24
31
|
});
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
handleRef.current = handle;
|
|
33
|
+
return () => {
|
|
34
|
+
handleRef.current = null;
|
|
35
|
+
handle.destroy();
|
|
36
|
+
};
|
|
37
|
+
}, [flow, host]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
themeRef.current = theme;
|
|
40
|
+
if (theme)
|
|
41
|
+
handleRef.current?.setTheme(theme);
|
|
42
|
+
}, [theme]);
|
|
27
43
|
return _jsx("div", { ref: ref, className: className });
|
|
28
44
|
}
|
|
@@ -9,7 +9,7 @@ export type AuthOutcome = "success" | "failure" | "info";
|
|
|
9
9
|
export type AuthMode = "sign-up" | "sign-in";
|
|
10
10
|
/** Client-only funnel steps reported via the `/api/events` beacon. */
|
|
11
11
|
export type FunnelStep = "mode_selected" | "email_entered" | "ceremony_started" | "ceremony_failed" | "done_shown";
|
|
12
|
-
export type AuthEventName = "auth.username.checked" | "auth.signup.succeeded" | "auth.signup.failed" | "auth.otp.sent" | "auth.otp.verified" | "auth.otp.failed" | "auth.email.verify.sent" | "auth.email.verify.succeeded" | "auth.email.verify.failed" | "auth.email.changed" | "auth.avatar.upload.succeeded" | "auth.avatar.upload.failed" | "auth.challenge.issued" | "auth.signin.succeeded" | "auth.signin.failed" | "auth.passkey.added" | "auth.passkey.add_failed" | "auth.restore.succeeded" | "auth.restore.failed" | "auth.restore.no_session" | "auth.refresh.succeeded" | "auth.refresh.failed" | "auth.refresh.reuse" | "auth.refresh.grace" | "auth.jwt.verified" | "auth.jwt.rejected" | "auth.signout" | "auth.session.revoked" | "auth.session.revoked_all" | "auth.audience.rejected" | "auth.audience.minted" | "auth.guest.created" | "auth.guest.restored" | "auth.guest.upgraded" | "auth.guest.failed" | "auth.role.granted" | "auth.role.revoked" | "auth.funnel.mode_selected" | "auth.funnel.email_entered" | "auth.funnel.ceremony_started" | "auth.funnel.ceremony_failed" | "auth.funnel.done_shown";
|
|
12
|
+
export type AuthEventName = "auth.username.checked" | "auth.signup.succeeded" | "auth.signup.failed" | "auth.otp.sent" | "auth.otp.verified" | "auth.otp.failed" | "auth.email.verify.sent" | "auth.email.verify.succeeded" | "auth.email.verify.failed" | "auth.email.changed" | "auth.avatar.upload.succeeded" | "auth.avatar.upload.failed" | "auth.challenge.issued" | "auth.signin.succeeded" | "auth.signin.failed" | "auth.passkey.added" | "auth.passkey.add_failed" | "auth.restore.succeeded" | "auth.restore.failed" | "auth.restore.no_session" | "auth.refresh.succeeded" | "auth.refresh.failed" | "auth.refresh.reuse" | "auth.refresh.grace" | "auth.jwt.verified" | "auth.jwt.rejected" | "auth.signout" | "auth.session.revoked" | "auth.session.revoked_all" | "auth.audience.rejected" | "auth.audience.minted" | "auth.guest.created" | "auth.guest.restored" | "auth.guest.upgraded" | "auth.guest.failed" | "auth.role.granted" | "auth.role.revoked" | "auth.funnel.mode_selected" | "auth.funnel.email_entered" | "auth.funnel.ceremony_started" | "auth.funnel.ceremony_failed" | "auth.funnel.done_shown" | "room_event.activated" | "room_event.completed" | "room_event.reminder_dispatched" | "room_event.reminder_failed";
|
|
13
13
|
export type AuthErrorCode = "username_taken" | "credential_taken" | "email_taken" | "email_not_verified" | "no_email" | "image_upload_forbidden" | "image_upload_rate_limited" | "invalid_image_type" | "image_too_large" | "image_upload_failed" | "otp_invalid" | "otp_expired" | "otp_attempts_exceeded" | "otp_resend_cooldown" | "otp_resend_limit" | "otp_global_limit" | "otp_send_failed" | "challenge_expired" | "unknown_credential" | "invalid_assertion_type" | "invalid_assertion_origin" | "user_verification_required" | "invalid_signature" | "user_not_found" | "missing_audience" | "audience_not_allowed" | "audience_mismatch" | "no_session" | "no_passkey" | "refresh_token_invalid" | "refresh_token_expired" | "refresh_epoch_stale" | "refresh_user_epoch_stale" | "refresh_reuse_detected" | "guest_rate_limited" | "guest_global_limit" | "guest_username_exhausted" | "guest_session" | "passkey_exists" | "already_upgraded" | "malformed_token" | "unknown_key" | "verification_failed" | "webauthn_not_allowed" | "webauthn_security" | "webauthn_invalid_state" | "webauthn_not_supported" | "webauthn_constraint" | "webauthn_aborted" | "webauthn_unknown" | "internal_error";
|
|
14
14
|
/** One flat record per event = one row in the `auth_events` stream. */
|
|
15
15
|
export interface AuthEventRecord {
|
|
@@ -37,6 +37,13 @@ export interface AuthEventRecord {
|
|
|
37
37
|
userAgent: string | null;
|
|
38
38
|
durationMs: number | null;
|
|
39
39
|
traceId: string | null;
|
|
40
|
+
/** Room-event scheduler fields: which series and dated instance a transition
|
|
41
|
+
* or dispatch was about, and for a dispatch, which reminder and how many
|
|
42
|
+
* subscribers it reached. */
|
|
43
|
+
roomEventId: string | null;
|
|
44
|
+
occurrenceId: string | null;
|
|
45
|
+
dispatchKind: string | null;
|
|
46
|
+
recipients: number | null;
|
|
40
47
|
}
|
|
41
48
|
/** Body accepted by `POST /api/events` from the dialog. */
|
|
42
49
|
export interface BeaconBody {
|
|
@@ -4,8 +4,9 @@ export type { AuthErrorCode, AuthEventName, AuthEventRecord, AuthMode, AuthOutco
|
|
|
4
4
|
export type { Bridge, BridgeParameters, FlowAccount, FlowRemote, FlowRemoteConfig, FromWindowOptions, MessageResponse, Messenger, OneOf, Payload, QueuedRequest, ReadyOptions, RemoteFlowState, RemoteState, Schema, Storage, Topic, WithReady, } from "./messenger";
|
|
5
5
|
export type { Call, ConnectCapabilities, ConnectRequest, ConnectResponse, GuestRequest, GuestResponse, MethodName, MethodParams, MethodResult, RestoreResponse, RpcRequest, SendCallsParams, SendCallsRequest, SendCallsResponse, SendTransactionParams, SendTransactionRequest, SendTransactionResponse, SignMessageRequest, SignMessageResponse, SignOutRequest, SignOutResponse, SignTypedDataRequest, SignTypedDataResponse, TransactionArgs, TypedData, TypedDataDomain, TypedDataField, } from "./protocol";
|
|
6
6
|
export { getConnectCapabilities, isPersonalSignParams, isSendCallsParams, isSendTransactionParams, } from "./protocol";
|
|
7
|
-
export type {
|
|
8
|
-
export type {
|
|
7
|
+
export type { CreateRoomEventInput, RoomEventAttendance, RoomEventFrequency, RoomEventInstance, RoomEventInterest, RoomEventInterestState, RoomEventList, RoomEventOccurrence, RoomEventRecurrence, RoomEventRoomRef, RoomEventSeries, RoomEventStatus, SetInterestInput, UpdateRoomEventInput, } from "./room-events";
|
|
8
|
+
export type { CreateRoomInput, GlobalRole, ModerationSubject, MyRooms, RoleChangeVerdict, RoomChannel, RoomDetail, RoomList, RoomMemberEntry, RoomMembers, RoomOccupancy, RoomOwner, RoomPresenceEntry, RoomPresenceSnapshot, RoomRestrictionKind, RoomRole, RoomSummary, RoomSurfaceSettings, RoomsApi, RoomVisibility, StaffEntry, UpdateRoomInput, VerifyRoomContext, Viewer, } from "./rooms";
|
|
9
|
+
export type { AccessKeyOptions, AccessKeyPreparation, Address, ColorScheme, CreateDialogHostOptions, CreateFlowOptions, DialogHost, DialogOpenOptions, FinalizeAccessKeyParams, Flow, FlowConnectorParameters, FlowCookieNames, FlowIdProviderProps, FlowSessionState, FlowState, FlowWidgetHandle, FlowWidgetName, FlowWidgetProps, Listener, LoginOptions, MountFlowWidgetOptions, MountProfileOptions, PrepareArgsWithAuth, PrepareTransactionRequestPhase, ProfileButtonHandle, ProfileButtonProps, ProfilePosition, ResolveAccountParams, ResolvedAccessKeyOptions, RootCredential, RunLoginParams, RunLoginResult, SendCallsArgs, SendTransactionArgs, Session, SigningContext, SignMessageArgs, SignTypedDataArgs, Store, StoredAccessKey, WagmiConnectCapabilities, WagmiConnectParams, } from "./sdk";
|
|
9
10
|
export type { ResolvedFlowSession, ResolveSessionOptions, SessionRouteOptions, SessionRouteResponse, SessionRouteSession, } from "./server";
|
|
10
11
|
export type { CoinAsset, IdentifiedTx, TxApprove, TxConvert, TxSend, TxSwap, } from "./tx";
|
|
11
12
|
export type { ActionDayContext, ActionEventKind, ActionSessionState, ActiveActionResponse, LevelProgress, PublicProfile, XpGrantResult, XpRecentGrant, XpSummary, } from "./xp";
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Room events — scheduled happenings inside a room, shared by the server
|
|
3
|
+
* routes and the client SDK.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately named `room-events` throughout: `types/events.ts` and
|
|
6
|
+
* `routes/events.ts` already mean structured product/telemetry events, and the
|
|
7
|
+
* two must never blur.
|
|
8
|
+
*
|
|
9
|
+
* Two shapes, and which one an endpoint speaks is load-bearing. A
|
|
10
|
+
* {@link RoomEventSeries} is what an owner authors and edits; an instance is
|
|
11
|
+
* one dated occurrence of it. Players only ever address occurrences — interest,
|
|
12
|
+
* reminders and attendance all attach to an instance, so a weekly race's third
|
|
13
|
+
* week is a thing in its own right.
|
|
14
|
+
*/
|
|
15
|
+
/** Discord's Guild Scheduled Event lifecycle, mirrored exactly. */
|
|
16
|
+
export type RoomEventStatus = "scheduled" | "active" | "completed" | "cancelled";
|
|
17
|
+
export type RoomEventFrequency = "daily" | "weekly" | "monthly";
|
|
18
|
+
/**
|
|
19
|
+
* The RRULE subset a series may repeat on. Expanded in the event's IANA
|
|
20
|
+
* timezone by the (db-free) expander, never in UTC. `until` is an ISO instant;
|
|
21
|
+
* `until` and `count` are mutually exclusive and both may be absent for an
|
|
22
|
+
* open-ended series.
|
|
23
|
+
*/
|
|
24
|
+
export interface RoomEventRecurrence {
|
|
25
|
+
freq: RoomEventFrequency;
|
|
26
|
+
interval?: number;
|
|
27
|
+
/** 0 = Sunday … 6 = Saturday, matching `Date.prototype.getDay`. */
|
|
28
|
+
byweekday?: number[];
|
|
29
|
+
bymonthday?: number[];
|
|
30
|
+
until?: string;
|
|
31
|
+
count?: number;
|
|
32
|
+
}
|
|
33
|
+
/** The room an event belongs to, denormalized so a listing needs no second read. */
|
|
34
|
+
export interface RoomEventRoomRef {
|
|
35
|
+
id: string;
|
|
36
|
+
slug: string;
|
|
37
|
+
displayName: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Interest in one dated instance. `interestedCount` is public; the two viewer
|
|
41
|
+
* flags are meaningful only when the request carried a bearer and read false
|
|
42
|
+
* otherwise — the same shape the room payload uses for the caller's role.
|
|
43
|
+
*
|
|
44
|
+
* A player counts as interested in an instance either by marking that instance
|
|
45
|
+
* or by following its whole series, so `viewerInterested` can be true while
|
|
46
|
+
* `viewerFollowing` is what actually produced it.
|
|
47
|
+
*/
|
|
48
|
+
export interface RoomEventInterest {
|
|
49
|
+
interestedCount: number;
|
|
50
|
+
viewerInterested: boolean;
|
|
51
|
+
viewerFollowing: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One dated instance, as it appears under the series that owns it: only what
|
|
55
|
+
* differs per instance, since the series already carries the title, room and
|
|
56
|
+
* timezone. `startsAt`/`endsAt` are ISO instants in UTC — render them in the
|
|
57
|
+
* series' `timezone` to show the local time the owner scheduled.
|
|
58
|
+
*/
|
|
59
|
+
export interface RoomEventInstance extends RoomEventInterest {
|
|
60
|
+
id: string;
|
|
61
|
+
startsAt: string;
|
|
62
|
+
endsAt: string | null;
|
|
63
|
+
status: RoomEventStatus;
|
|
64
|
+
/** Moved or cancelled on its own, so a series-wide edit leaves it alone. */
|
|
65
|
+
overridden: boolean;
|
|
66
|
+
/** Written once the instance completes — the history view reads these rather
|
|
67
|
+
* than scanning presence. Null while it has not finished. */
|
|
68
|
+
attendance: RoomEventAttendance | null;
|
|
69
|
+
}
|
|
70
|
+
/** What actually happened, as observed from room presence while it ran. */
|
|
71
|
+
export interface RoomEventAttendance {
|
|
72
|
+
attendeeCount: number;
|
|
73
|
+
peakConcurrent: number;
|
|
74
|
+
startedAt: string | null;
|
|
75
|
+
endedAt: string | null;
|
|
76
|
+
}
|
|
77
|
+
/** An instance standing on its own in a listing, carrying enough of its series
|
|
78
|
+
* and room to render a card. */
|
|
79
|
+
export interface RoomEventOccurrence extends RoomEventInstance {
|
|
80
|
+
eventId: string;
|
|
81
|
+
room: RoomEventRoomRef;
|
|
82
|
+
title: string;
|
|
83
|
+
description: string | null;
|
|
84
|
+
coverUrl: string | null;
|
|
85
|
+
timezone: string;
|
|
86
|
+
/** True when the parent series repeats, so a card can badge it as recurring. */
|
|
87
|
+
recurring: boolean;
|
|
88
|
+
createdBy: string | null;
|
|
89
|
+
}
|
|
90
|
+
/** The authored series, with the instances it has materialized so far. */
|
|
91
|
+
export interface RoomEventSeries {
|
|
92
|
+
id: string;
|
|
93
|
+
room: RoomEventRoomRef;
|
|
94
|
+
title: string;
|
|
95
|
+
description: string | null;
|
|
96
|
+
coverUrl: string | null;
|
|
97
|
+
startsAt: string;
|
|
98
|
+
endsAt: string | null;
|
|
99
|
+
timezone: string;
|
|
100
|
+
recurrence: RoomEventRecurrence | null;
|
|
101
|
+
/** Minutes before the start at which subscribers are reminded; null is the
|
|
102
|
+
* default schedule, an empty list is silence. */
|
|
103
|
+
reminderOffsets: number[] | null;
|
|
104
|
+
status: RoomEventStatus;
|
|
105
|
+
createdBy: string | null;
|
|
106
|
+
createdAt: string;
|
|
107
|
+
updatedAt: string;
|
|
108
|
+
occurrences: RoomEventInstance[];
|
|
109
|
+
}
|
|
110
|
+
export interface RoomEventList {
|
|
111
|
+
events: RoomEventOccurrence[];
|
|
112
|
+
}
|
|
113
|
+
export interface CreateRoomEventInput {
|
|
114
|
+
title: string;
|
|
115
|
+
description?: string | null;
|
|
116
|
+
coverUrl?: string | null;
|
|
117
|
+
startsAt: string;
|
|
118
|
+
endsAt?: string | null;
|
|
119
|
+
timezone?: string;
|
|
120
|
+
recurrence?: RoomEventRecurrence | null;
|
|
121
|
+
reminderOffsets?: number[] | null;
|
|
122
|
+
}
|
|
123
|
+
/** Every create field, all optional — a patch is merged over the stored row and
|
|
124
|
+
* validated by the same rules, so the two shapes can never drift. */
|
|
125
|
+
export type UpdateRoomEventInput = Partial<CreateRoomEventInput>;
|
|
126
|
+
export interface SetInterestInput {
|
|
127
|
+
/** Also follow (or unfollow) the whole series, not just this instance. */
|
|
128
|
+
series?: boolean;
|
|
129
|
+
}
|
|
130
|
+
/** What the interest endpoints answer with, so a card can update in place. */
|
|
131
|
+
export interface RoomEventInterestState extends RoomEventInterest {
|
|
132
|
+
occurrenceId: string;
|
|
133
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Room events — scheduled happenings inside a room, shared by the server
|
|
3
|
+
* routes and the client SDK.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately named `room-events` throughout: `types/events.ts` and
|
|
6
|
+
* `routes/events.ts` already mean structured product/telemetry events, and the
|
|
7
|
+
* two must never blur.
|
|
8
|
+
*
|
|
9
|
+
* Two shapes, and which one an endpoint speaks is load-bearing. A
|
|
10
|
+
* {@link RoomEventSeries} is what an owner authors and edits; an instance is
|
|
11
|
+
* one dated occurrence of it. Players only ever address occurrences — interest,
|
|
12
|
+
* reminders and attendance all attach to an instance, so a weekly race's third
|
|
13
|
+
* week is a thing in its own right.
|
|
14
|
+
*/
|
|
@@ -18,6 +18,14 @@ export interface ModerationSubject {
|
|
|
18
18
|
globalRole: GlobalRole;
|
|
19
19
|
roomRole: RoomRole | null;
|
|
20
20
|
}
|
|
21
|
+
/** A verified caller resolved against one room: the raw composite the rank
|
|
22
|
+
* lattice needs, plus the wire projection every route-side gate compares. */
|
|
23
|
+
export interface Viewer {
|
|
24
|
+
sub: string;
|
|
25
|
+
guest: boolean;
|
|
26
|
+
subject: ModerationSubject;
|
|
27
|
+
role: RoomRole | null;
|
|
28
|
+
}
|
|
21
29
|
/** One row of `GET /api/admin/staff` — a user holding a non-`user` tier. */
|
|
22
30
|
export interface StaffEntry {
|
|
23
31
|
userId: string;
|
package/dist/sdk/types/sdk.d.ts
CHANGED
|
@@ -187,6 +187,11 @@ export type DialogOpenOptions = {
|
|
|
187
187
|
icon?: string;
|
|
188
188
|
};
|
|
189
189
|
};
|
|
190
|
+
/**
|
|
191
|
+
* Color scheme for Flow ID iframe chrome. "light dark" follows the OS, which
|
|
192
|
+
* is the default; a host with its own theme toggle passes the resolved value.
|
|
193
|
+
*/
|
|
194
|
+
export type ColorScheme = "light" | "dark" | "light dark";
|
|
190
195
|
/** Anchor for an auto-created (host-less) profile widget container. */
|
|
191
196
|
export type ProfilePosition = "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
192
197
|
export type MountProfileOptions = {
|
|
@@ -209,14 +214,73 @@ export type MountProfileOptions = {
|
|
|
209
214
|
* Color scheme for the widget chrome. Set this to match the host (the dark
|
|
210
215
|
* game UI passes "dark"). Defaults to "light dark" (follows the OS).
|
|
211
216
|
*/
|
|
212
|
-
theme?:
|
|
217
|
+
theme?: ColorScheme;
|
|
213
218
|
/** Explicit Flow instance. Defaults to the createFlow() singleton. */
|
|
214
219
|
flow?: Flow;
|
|
215
220
|
};
|
|
216
221
|
export type ProfileButtonHandle = {
|
|
222
|
+
/**
|
|
223
|
+
* Recolors the mounted pill (and its profile dialog) in place. A host with a
|
|
224
|
+
* theme toggle calls this instead of remounting: rebuilding the iframe would
|
|
225
|
+
* reload the widget and flash the pill out of the layout on every toggle.
|
|
226
|
+
*/
|
|
227
|
+
setTheme: (theme: ColorScheme) => void;
|
|
217
228
|
/** Tears down the iframe, listeners, and any auto-created container. */
|
|
218
229
|
destroy: () => void;
|
|
219
230
|
};
|
|
231
|
+
/** The inline Flow ID widgets an embedder can mount alongside its own UI. */
|
|
232
|
+
export type FlowWidgetName = "xp" | "action-timer";
|
|
233
|
+
export type MountFlowWidgetOptions = {
|
|
234
|
+
/** Which inline widget to mount. */
|
|
235
|
+
widget: FlowWidgetName;
|
|
236
|
+
/**
|
|
237
|
+
* Element to append the widget iframe to. Defaults to `document.body`, which
|
|
238
|
+
* is what a fixed-position overlay wants; pass a container to lay the widget
|
|
239
|
+
* out inline instead.
|
|
240
|
+
*/
|
|
241
|
+
container?: HTMLElement;
|
|
242
|
+
/**
|
|
243
|
+
* The Flow ID origin serving the widget (e.g. https://id.flow.industries).
|
|
244
|
+
* Defaults to the resolved Flow instance's `host`, then the SDK default.
|
|
245
|
+
*/
|
|
246
|
+
host?: string;
|
|
247
|
+
/**
|
|
248
|
+
* Color scheme for the widget chrome. Set this to match the host (the dark
|
|
249
|
+
* game UI passes "dark"). Defaults to "light dark" (follows the OS).
|
|
250
|
+
*/
|
|
251
|
+
theme?: ColorScheme;
|
|
252
|
+
/** Explicit Flow instance. Defaults to the createFlow() singleton. */
|
|
253
|
+
flow?: Flow;
|
|
254
|
+
};
|
|
255
|
+
export type FlowWidgetHandle = {
|
|
256
|
+
/**
|
|
257
|
+
* The mounted iframe, so the embedder owns geometry and visibility. Null
|
|
258
|
+
* under SSR, where nothing was mounted.
|
|
259
|
+
*/
|
|
260
|
+
readonly frame: HTMLIFrameElement | null;
|
|
261
|
+
/**
|
|
262
|
+
* Sends a widget-specific message (e.g. `{ type: "xp-refresh" }`), pinned to
|
|
263
|
+
* the Flow ID origin.
|
|
264
|
+
*/
|
|
265
|
+
post: (message: unknown) => void;
|
|
266
|
+
/**
|
|
267
|
+
* Recolors the mounted widget in place, for the same reason the pill exposes
|
|
268
|
+
* it: a remount reloads the frame and flashes it out of the host's layout.
|
|
269
|
+
*/
|
|
270
|
+
setTheme: (theme: ColorScheme) => void;
|
|
271
|
+
/** Tears down the iframe and its bridge. */
|
|
272
|
+
destroy: () => void;
|
|
273
|
+
};
|
|
274
|
+
export type FlowWidgetProps = {
|
|
275
|
+
/** Which inline widget to mount. */
|
|
276
|
+
widget: FlowWidgetName;
|
|
277
|
+
/** Class applied to the host element the widget iframe fills. */
|
|
278
|
+
className?: string;
|
|
279
|
+
/** Override the Flow ID origin; defaults to the resolved Flow's `host`. */
|
|
280
|
+
host?: string;
|
|
281
|
+
/** Color scheme for the widget chrome; defaults to "light dark" (the OS). */
|
|
282
|
+
theme?: ColorScheme;
|
|
283
|
+
};
|
|
220
284
|
export type DialogHost = {
|
|
221
285
|
open: (options?: DialogOpenOptions) => void;
|
|
222
286
|
close: () => void;
|
|
@@ -316,7 +380,7 @@ export type ProfileButtonProps = {
|
|
|
316
380
|
/** Override the Flow ID origin; defaults to the resolved Flow's `host`. */
|
|
317
381
|
host?: string;
|
|
318
382
|
/** Color scheme for the widget chrome; defaults to "light dark" (the OS). */
|
|
319
|
-
theme?:
|
|
383
|
+
theme?: ColorScheme;
|
|
320
384
|
};
|
|
321
385
|
export type RunLoginParams = {
|
|
322
386
|
dialog: DialogHost;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flow-industries/id",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"main": "./dist/sdk/client/index.js",
|
|
5
5
|
"module": "./dist/sdk/client/index.js",
|
|
6
6
|
"types": "./dist/sdk/client/index.d.ts",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"build:sdk": "tsc --project tsconfig.sdk.json",
|
|
59
59
|
"prepublishOnly": "bun run build:sdk",
|
|
60
60
|
"start": "bun run src/index.ts",
|
|
61
|
+
"worker": "bun run src/worker.ts",
|
|
61
62
|
"email:dev": "email dev --dir src/emails --port 3010",
|
|
62
63
|
"db:generate": "bunx @better-auth/cli generate",
|
|
63
64
|
"db:migrate:gen": "bunx drizzle-kit generate",
|