@flow-industries/id 0.20.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/sdk/client/index.d.ts +1 -1
- package/dist/sdk/client/open-profile.js +16 -3
- package/dist/sdk/client/study.d.ts +5 -4
- package/dist/sdk/client/study.js +17 -4
- package/dist/sdk/react/open-profile.js +2 -2
- package/dist/sdk/types/actions.d.ts +16 -1
- package/dist/sdk/types/index.d.ts +3 -3
- package/dist/sdk/types/rooms.d.ts +11 -0
- package/dist/sdk/types/sdk.d.ts +9 -6
- package/dist/sdk/types/study.d.ts +66 -0
- package/dist/sdk/types/xp.d.ts +17 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -47,6 +47,38 @@ Full documentation at **[docs.flow.industries/en/auth](https://docs.flow.industr
|
|
|
47
47
|
- [JWT verify](https://docs.flow.industries/en/auth/jwt-verify) - verify Flow sessions on your backend
|
|
48
48
|
- [API reference](https://docs.flow.industries/en/auth/api) and [self-hosting](https://docs.flow.industries/en/auth/self-hosting)
|
|
49
49
|
|
|
50
|
+
## Public profile overlays
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { closeProfile, openProfile } from "@flow-industries/id";
|
|
54
|
+
import { isValidUsername } from "@flow-industries/id/usernames";
|
|
55
|
+
|
|
56
|
+
if (isValidUsername(username)) {
|
|
57
|
+
openProfile(username, { onClose: () => resumeInput() });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
closeProfile();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`@flow-industries/id/usernames` also exports `USERNAME_REGEX` and
|
|
64
|
+
`MAX_USERNAME_LENGTH`. It has no browser or server dependencies.
|
|
65
|
+
|
|
66
|
+
`OpenProfileOptions.onClose?: () => void` fires once after the overlay stops
|
|
67
|
+
accepting input, before its exit animation finishes. It covers the close button,
|
|
68
|
+
backdrop, Escape, `closeProfile()`, and sign-in/sign-out handoffs. The latest
|
|
69
|
+
successful `openProfile` call owns the callback, even when switching profiles
|
|
70
|
+
while open; an omitted callback clears the previous one. Invalid usernames and
|
|
71
|
+
SSR return `false` without replacing it. Closing an already closed overlay is a
|
|
72
|
+
no-op. A callback may open another profile.
|
|
73
|
+
|
|
74
|
+
React callers pass the same option to
|
|
75
|
+
`useOpenProfile({ onClose: () => resumeInput() })` from
|
|
76
|
+
`@flow-industries/id/react`; the callback is captured when its returned opener
|
|
77
|
+
is called. The overlay belongs to the page and survives component unmounts.
|
|
78
|
+
|
|
79
|
+
Open from the document that owns the full viewport. Canvas hosts must release
|
|
80
|
+
pointer lock before opening, then use `onClose` to restore their input state.
|
|
81
|
+
|
|
50
82
|
## License
|
|
51
83
|
|
|
52
84
|
MIT
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defaultIdHost, isLocalHostname } from "../id-host";
|
|
2
|
-
export type { AccessKeyOptions, ActionCatalogEntry, ActionCatalogResponse, ActionCurve, ActionEventRequest, ActionEventResponse, ActionSubjectRef, ActionSurface, ActionsApi, ActionTransitionKind, ActiveActionResponse, ActiveSessionConflict, AdditionalSession, Address, ConnectCapabilities, ConnectResponse, CreateFlowOptions, CreateStudySubjectInput, DialogHost, FinishActionRequest, FinishActionResponse, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, FlowWidgetHandle, FlowWidgetName, ListStudySubjectsOptions, LoginOptions, MethodName, MountFlowWidgetOptions, MountProfileOptions, OpenProfileOptions, ProfileButtonHandle, ProfilePosition, Session, StartActionInput, StartActionResponse, StartActionResult, StudyApi, StudyCatalog, StudyCategory, StudyField, StudySubject, StudySubjectList, StudySubjectRefusal, StudySubjectRemovalResponse, StudySubjectResponse, UpdateStudySubjectInput, UserActionSurface, XpEstimateContext, XpMode, } from "../types";
|
|
2
|
+
export type { AccessKeyOptions, ActionCatalogEntry, ActionCatalogResponse, ActionCurve, ActionEventRequest, ActionEventResponse, ActionSubjectRef, ActionSurface, ActionsApi, ActionTransitionKind, ActiveActionResponse, ActiveSessionConflict, AdditionalSession, Address, ConnectCapabilities, ConnectResponse, CreateFlowOptions, CreateStudySubjectInput, CurrentAction, DialogHost, FinishActionRequest, FinishActionResponse, Flow, FlowCredential, FlowSessionState, FlowState, FlowUser, FlowWidgetHandle, FlowWidgetName, ListStudySessionsOptions, ListStudySubjectsOptions, LoginOptions, MethodName, MountFlowWidgetOptions, MountProfileOptions, OpenProfileOptions, ProfileButtonHandle, ProfilePosition, RoomMemberEntry, Session, StartActionInput, StartActionResponse, StartActionResult, StudyApi, StudyCatalog, StudyCategory, StudyCategoryTotals, StudyField, StudyRange, StudySessionEntry, StudySessionPage, StudySubject, StudySubjectList, StudySubjectRefusal, StudySubjectRemovalResponse, StudySubjectResponse, StudySubjectTotals, StudySummary, UpdateStudySubjectInput, UserActionSurface, XpEstimateContext, XpMode, } from "../types";
|
|
3
3
|
export { consistencyCurve, estimateActionXp, FLOW_SCORE_MAX, flowMultiplier, } from "../xp/curve";
|
|
4
4
|
export { ActionsRequestError, createActionsApi, isActiveSessionConflict, } from "./actions";
|
|
5
5
|
export { createFlow, getFlow, requireFlow, resetFlow } from "./create-flow";
|
|
@@ -21,6 +21,7 @@ function createViewer(options) {
|
|
|
21
21
|
// meant for the page underneath with nothing on screen to explain it.
|
|
22
22
|
let ready = false;
|
|
23
23
|
let visible = false;
|
|
24
|
+
let onClose;
|
|
24
25
|
function applyHitTesting() {
|
|
25
26
|
if (frame)
|
|
26
27
|
frame.style.pointerEvents = ready && visible ? "auto" : "none";
|
|
@@ -78,7 +79,7 @@ function createViewer(options) {
|
|
|
78
79
|
});
|
|
79
80
|
return opened;
|
|
80
81
|
}
|
|
81
|
-
function open(username) {
|
|
82
|
+
function open(username, nextOnClose) {
|
|
82
83
|
const mounted = bridge;
|
|
83
84
|
const live = mounted ?? mount(username);
|
|
84
85
|
// A frame created just now is already on this profile; an existing one is
|
|
@@ -93,6 +94,8 @@ function createViewer(options) {
|
|
|
93
94
|
type: "profile-identity",
|
|
94
95
|
identity: hostIdentity(flow),
|
|
95
96
|
});
|
|
97
|
+
window.addEventListener("keydown", onKeyDown);
|
|
98
|
+
onClose = nextOnClose;
|
|
96
99
|
visible = true;
|
|
97
100
|
applyHitTesting();
|
|
98
101
|
void live.send("__internal", { type: "dialog-shown" });
|
|
@@ -104,11 +107,21 @@ function createViewer(options) {
|
|
|
104
107
|
// always-rendered, transparent and click-through while hidden keeps every
|
|
105
108
|
// open animation reliable, on the second open and the tenth.
|
|
106
109
|
function close() {
|
|
107
|
-
if (!frame || !bridge)
|
|
110
|
+
if (!visible || !frame || !bridge)
|
|
108
111
|
return;
|
|
109
112
|
visible = false;
|
|
113
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
110
114
|
applyHitTesting();
|
|
111
115
|
void bridge.send("__internal", { type: "dialog-hidden" });
|
|
116
|
+
const notifyClose = onClose;
|
|
117
|
+
onClose = undefined;
|
|
118
|
+
notifyClose?.();
|
|
119
|
+
}
|
|
120
|
+
function onKeyDown(event) {
|
|
121
|
+
if (event.key !== "Escape" || event.defaultPrevented)
|
|
122
|
+
return;
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
close();
|
|
112
125
|
}
|
|
113
126
|
function setTheme(next) {
|
|
114
127
|
if (next === theme)
|
|
@@ -163,7 +176,7 @@ export function openProfile(username, options = {}) {
|
|
|
163
176
|
viewer = live;
|
|
164
177
|
if (options.theme)
|
|
165
178
|
live.setTheme(options.theme);
|
|
166
|
-
live.open(username);
|
|
179
|
+
live.open(username, options.onClose);
|
|
167
180
|
return true;
|
|
168
181
|
}
|
|
169
182
|
/**
|
|
@@ -8,9 +8,10 @@ export declare class StudyRequestError extends Error {
|
|
|
8
8
|
constructor(status: number, message: string, reason?: string | undefined);
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Thin typed client for the study catalog
|
|
12
|
-
* bound to a host and the Flow token supplier. The
|
|
13
|
-
* long-cached by the browser;
|
|
14
|
-
* and
|
|
11
|
+
* Thin typed client for the study catalog, the caller's study subjects and
|
|
12
|
+
* their finished study time, bound to a host and the Flow token supplier. The
|
|
13
|
+
* catalog is public and long-cached by the browser; every other route attaches
|
|
14
|
+
* the caller's bearer and fails fast with a 401-shaped error when no session
|
|
15
|
+
* exists.
|
|
15
16
|
*/
|
|
16
17
|
export declare function createStudyApi(host: string, getToken: () => Promise<string | null>): StudyApi;
|
package/dist/sdk/client/study.js
CHANGED
|
@@ -13,10 +13,11 @@ export class StudyRequestError extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Thin typed client for the study catalog
|
|
17
|
-
* bound to a host and the Flow token supplier. The
|
|
18
|
-
* long-cached by the browser;
|
|
19
|
-
* and
|
|
16
|
+
* Thin typed client for the study catalog, the caller's study subjects and
|
|
17
|
+
* their finished study time, bound to a host and the Flow token supplier. The
|
|
18
|
+
* catalog is public and long-cached by the browser; every other route attaches
|
|
19
|
+
* the caller's bearer and fails fast with a 401-shaped error when no session
|
|
20
|
+
* exists.
|
|
20
21
|
*/
|
|
21
22
|
export function createStudyApi(host, getToken) {
|
|
22
23
|
const api = createJsonApi(host, "/api/study", getToken, (status, message, reason) => new StudyRequestError(status, message, reason));
|
|
@@ -38,5 +39,17 @@ export function createStudyApi(host, getToken) {
|
|
|
38
39
|
method: "DELETE",
|
|
39
40
|
authRequired: true,
|
|
40
41
|
}),
|
|
42
|
+
summary: (range) => api.request(`/summary?${new URLSearchParams({ range: range ?? "today" })}`, { authRequired: true }),
|
|
43
|
+
sessions: (options) => {
|
|
44
|
+
const params = new URLSearchParams({ subject: options.subject });
|
|
45
|
+
if (options.before !== undefined)
|
|
46
|
+
params.set("before", options.before);
|
|
47
|
+
if (options.limit !== undefined) {
|
|
48
|
+
params.set("limit", String(options.limit));
|
|
49
|
+
}
|
|
50
|
+
return api.request(`/sessions?${params}`, {
|
|
51
|
+
authRequired: true,
|
|
52
|
+
});
|
|
53
|
+
},
|
|
41
54
|
};
|
|
42
55
|
}
|
|
@@ -19,6 +19,6 @@ import { useFlow } from "./hooks";
|
|
|
19
19
|
*/
|
|
20
20
|
export function useOpenProfile(options = {}) {
|
|
21
21
|
const flow = useFlow();
|
|
22
|
-
const { host, theme } = options;
|
|
23
|
-
return useCallback((username) => openProfile(username, { flow, host, theme }), [flow, host, theme]);
|
|
22
|
+
const { host, theme, onClose } = options;
|
|
23
|
+
return useCallback((username) => openProfile(username, { flow, host, theme, onClose }), [flow, host, theme, onClose]);
|
|
24
24
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `createStudyApi` and `useActiveAction` take and return. The wire shapes
|
|
3
3
|
* themselves live in `./xp` and `./study`; this file only names the client
|
|
4
4
|
* inputs and the composed results a consumer app programs against. */
|
|
5
|
-
import type { CreateStudySubjectInput, StudyCatalog, StudySubject, StudySubjectList, StudySubjectRemovalResponse, UpdateStudySubjectInput } from "./study";
|
|
5
|
+
import type { CreateStudySubjectInput, StudyCatalog, StudyRange, StudySessionPage, StudySubject, StudySubjectList, StudySubjectRemovalResponse, StudySummary, UpdateStudySubjectInput } from "./study";
|
|
6
6
|
import type { ActionCatalogEntry, ActionCatalogResponse, ActionTransitionKind, ActiveActionResponse, ActiveSessionConflict, FinishActionResponse, StartActionResponse, UserActionSurface } from "./xp";
|
|
7
7
|
/** The curve parameters `estimateActionXp` reads. A catalog entry satisfies
|
|
8
8
|
* it, and so does the server's own registry descriptor, so the widget and a
|
|
@@ -61,6 +61,17 @@ export interface ListStudySubjectsOptions {
|
|
|
61
61
|
/** Include archived subjects after the live ones. */
|
|
62
62
|
archived?: boolean;
|
|
63
63
|
}
|
|
64
|
+
/** Query of GET /api/study/sessions as the SDK takes it: one subject's
|
|
65
|
+
* finished sessions, newest first. Feed a page's `nextBefore` back as
|
|
66
|
+
* `before` until it is null. */
|
|
67
|
+
export interface ListStudySessionsOptions {
|
|
68
|
+
/** The subject's id; an archived subject is readable too. */
|
|
69
|
+
subject: string;
|
|
70
|
+
/** ISO instant; only sessions started strictly before it are listed. */
|
|
71
|
+
before?: string;
|
|
72
|
+
/** 1..100; the server defaults to 50. */
|
|
73
|
+
limit?: number;
|
|
74
|
+
}
|
|
64
75
|
/** Envelope of every single-subject study route. */
|
|
65
76
|
export interface StudySubjectResponse {
|
|
66
77
|
subject: StudySubject;
|
|
@@ -72,6 +83,10 @@ export interface StudyApi {
|
|
|
72
83
|
createSubject(input: CreateStudySubjectInput): Promise<StudySubjectResponse>;
|
|
73
84
|
updateSubject(id: string, patch: UpdateStudySubjectInput): Promise<StudySubjectResponse>;
|
|
74
85
|
deleteSubject(id: string): Promise<StudySubjectRemovalResponse>;
|
|
86
|
+
/** Finished study time per subject and category; `range` defaults to
|
|
87
|
+
* `today`. The running session is absent: add `/api/actions/me` live. */
|
|
88
|
+
summary(range?: StudyRange): Promise<StudySummary>;
|
|
89
|
+
sessions(options: ListStudySessionsOptions): Promise<StudySessionPage>;
|
|
75
90
|
}
|
|
76
91
|
export type UseActiveActionOptions = {
|
|
77
92
|
/** Override the Flow ID origin; defaults to the resolved Flow's `host`. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ActionCurve, ActionEventRequest, ActionEventResponse, ActionsApi, ActiveActionState, FinishActionRequest, ListStudySubjectsOptions, StartActionInput, StartActionResult, StudyApi, StudySubjectResponse, UseActiveActionOptions, XpEstimateContext, } from "./actions";
|
|
1
|
+
export type { ActionCurve, ActionEventRequest, ActionEventResponse, ActionsApi, ActiveActionState, FinishActionRequest, ListStudySessionsOptions, ListStudySubjectsOptions, StartActionInput, StartActionResult, StudyApi, StudySubjectResponse, UseActiveActionOptions, XpEstimateContext, } from "./actions";
|
|
2
2
|
export type { AccountSession, AccountSessionsResponse, AdditionalSession, AuthConfig, AuthResponse, AuthResponseWithWebAuthn, BioNormalization, BioRejection, BioWriteRefusal, BioWriteResult, FlowCredential, FlowUser, PasskeyPluginOptions, RevokeAllSessionsResponse, RevokeSessionResponse, SecurityActivityEntry, SecurityActivityResponse, SessionUser, VerifiedFlowJWT, VerifyOptions, WebAuthnSignature, } from "./auth";
|
|
3
3
|
export type { BodyRegion, CosmeticItem, CosmeticMaterial, CosmeticPaint, CosmeticSlot, EquipConflict, EquippedCosmetic, EquippedCosmetics, EquipRegion, PaintSlot, SettingsConflict, } from "./cosmetics";
|
|
4
4
|
export { BODY_REGIONS, COSMETIC_MATERIALS } from "./cosmetics";
|
|
@@ -13,6 +13,6 @@ export type { CreateRoomEventInput, RoomEventArchive, RoomEventAttendance, RoomE
|
|
|
13
13
|
export type { CreateRoomInput, GlobalRole, ModerationSubject, MyRooms, PlayerPositionReport, RoleChangeVerdict, RoomCapabilities, RoomCapability, RoomChannel, RoomDetail, RoomList, RoomMemberEntry, RoomMembers, RoomOccupancy, RoomOwner, RoomPlayerPosition, RoomPresenceEntry, RoomPresenceSnapshot, RoomRestrictionKind, RoomRole, RoomSummary, RoomSurfaceSettings, RoomsApi, RoomVisibility, StaffEntry, UpdateRoomInput, VerifyRoomContext, Viewer, VoiceDecisionReason, VoiceDecisionSubject, VoiceRoomDecision, } from "./rooms";
|
|
14
14
|
export type { AccessKeyOptions, AccessKeyPreparation, Address, ColorScheme, CreateDialogHostOptions, CreateFlowOptions, DialogHost, DialogOpenOptions, FinalizeAccessKeyParams, Flow, FlowConnectorParameters, FlowCookieNames, FlowIdProviderProps, FlowSessionState, FlowState, FlowWidgetHandle, FlowWidgetName, FlowWidgetProps, Listener, LoginOptions, MountFlowWidgetOptions, MountProfileOptions, OpenProfileOptions, PrepareArgsWithAuth, PrepareTransactionRequestPhase, ProfileButtonHandle, ProfileButtonProps, ProfilePosition, ResolveAccountParams, ResolvedAccessKeyOptions, RootCredential, RunLoginParams, RunLoginResult, SendCallsArgs, SendTransactionArgs, Session, SigningContext, SignMessageArgs, SignTypedDataArgs, Store, StoredAccessKey, UseOpenProfileOptions, WagmiConnectCapabilities, WagmiConnectParams, } from "./sdk";
|
|
15
15
|
export type { GracefulShutdownCleanup, GracefulShutdownOptions, GracefulShutdownServer, GracefulShutdownSignal, ResolvedFlowSession, ResolveSessionOptions, SessionRouteOptions, SessionRouteResponse, SessionRouteSession, } from "./server";
|
|
16
|
-
export type { CreateStudySubjectInput, StudyCatalog, StudyCategory, StudyField, StudySubject, StudySubjectCreation, StudySubjectList, StudySubjectRefusal, StudySubjectRemoval, StudySubjectRemovalResponse, StudySubjectUpdate, UpdateStudySubjectInput, } from "./study";
|
|
16
|
+
export type { CreateStudySubjectInput, ListStudySessionsInput, StudyCatalog, StudyCategory, StudyCategoryTotals, StudyField, StudyRange, StudySessionEntry, StudySessionPage, StudySessionsResult, StudySubject, StudySubjectCreation, StudySubjectList, StudySubjectRefusal, StudySubjectRemoval, StudySubjectRemovalResponse, StudySubjectTotals, StudySubjectUpdate, StudySummary, UpdateStudySubjectInput, } from "./study";
|
|
17
17
|
export type { CoinAsset, IdentifiedTx, TxApprove, TxConvert, TxSend, TxSwap, } from "./tx";
|
|
18
|
-
export type { ActionCatalogEntry, ActionCatalogResponse, ActionDayContext, ActionEventKind, ActionSessionState, ActionSubjectRef, ActionSurface, ActionTransitionKind, ActiveActionResponse, ActiveSessionConflict, FinishActionResponse, LevelProgress, PublicProfile, PublicProfileRefusal, PublicProfileResult, PublicProfileSignal, PublicUserProfile, StartActionResponse, StartUserSessionResult, UserActionSurface, XpGrantResult, XpMode, XpRecentGrant, XpSummary, } from "./xp";
|
|
18
|
+
export type { ActionCatalogEntry, ActionCatalogResponse, ActionDayContext, ActionEventKind, ActionSessionState, ActionSubjectRef, ActionSurface, ActionTransitionKind, ActiveActionResponse, ActiveSessionConflict, CurrentAction, FinishActionResponse, LevelProgress, PublicProfile, PublicProfileRefusal, PublicProfileResult, PublicProfileSignal, PublicUserProfile, StartActionResponse, StartUserSessionResult, UserActionSurface, XpGrantResult, XpMode, XpRecentGrant, XpSummary, } from "./xp";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RawRecord } from "../json";
|
|
2
|
+
import type { CurrentAction } from "./xp";
|
|
2
3
|
/** Room registry types shared by the server routes and the client SDK. */
|
|
3
4
|
export type RoomVisibility = "public" | "unlisted" | "private";
|
|
4
5
|
/**
|
|
@@ -93,6 +94,16 @@ export interface RoomMemberEntry {
|
|
|
93
94
|
* producers omit it; this one because a whole deployment might.
|
|
94
95
|
*/
|
|
95
96
|
isGuest?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* The member's active action session as the roster may show it — "Studying",
|
|
99
|
+
* "Meditating" — or `null` when they have none. The subject rides inside
|
|
100
|
+
* only when that member shares it (`study.share_subject`).
|
|
101
|
+
*
|
|
102
|
+
* Optional for the reason `isGuest` is: this server always sends it, but a
|
|
103
|
+
* server older than the field omits it, and `undefined` must read as
|
|
104
|
+
* "unknown" rather than as "idle".
|
|
105
|
+
*/
|
|
106
|
+
currentAction?: CurrentAction | null;
|
|
96
107
|
role: RoomRole;
|
|
97
108
|
joinedAt: string;
|
|
98
109
|
}
|
package/dist/sdk/types/sdk.d.ts
CHANGED
|
@@ -230,6 +230,14 @@ export type ProfileButtonHandle = {
|
|
|
230
230
|
destroy: () => void;
|
|
231
231
|
};
|
|
232
232
|
export type OpenProfileOptions = {
|
|
233
|
+
/**
|
|
234
|
+
* Called once when the overlay closes, including its close button, backdrop,
|
|
235
|
+
* Escape, closeProfile(), or a sign-in/sign-out handoff. Runs after the
|
|
236
|
+
* overlay stops accepting input, without waiting for the exit animation.
|
|
237
|
+
* The latest successful open replaces this callback; omitting it clears the
|
|
238
|
+
* previous callback. Rejected opens and repeated closes do not notify.
|
|
239
|
+
*/
|
|
240
|
+
onClose?: () => void;
|
|
233
241
|
/**
|
|
234
242
|
* The Flow ID origin serving the overlay (e.g. https://id.flow.industries).
|
|
235
243
|
* Defaults to the resolved Flow instance's `host`, then the SDK default.
|
|
@@ -249,12 +257,7 @@ export type OpenProfileOptions = {
|
|
|
249
257
|
*/
|
|
250
258
|
flow?: Flow;
|
|
251
259
|
};
|
|
252
|
-
export type UseOpenProfileOptions =
|
|
253
|
-
/** Override the Flow ID origin; defaults to the resolved Flow's `host`. */
|
|
254
|
-
host?: string;
|
|
255
|
-
/** Color scheme for the dialog chrome; defaults to "light dark" (the OS). */
|
|
256
|
-
theme?: ColorScheme;
|
|
257
|
-
};
|
|
260
|
+
export type UseOpenProfileOptions = Pick<OpenProfileOptions, "host" | "theme" | "onClose">;
|
|
258
261
|
/** The inline Flow ID widgets an embedder can mount alongside its own UI. */
|
|
259
262
|
export type FlowWidgetName = "xp" | "action-timer";
|
|
260
263
|
export type MountFlowWidgetOptions = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Study catalog and study-subject shapes that cross the server/SDK boundary. */
|
|
2
|
+
import type { ActionSubjectRef, ActionSurface } from "./xp";
|
|
2
3
|
/**
|
|
3
4
|
* One predefined thing a person can study. `slug` is `category/field`,
|
|
4
5
|
* lowercase ASCII and stable forever — it is what a subject row stores and
|
|
@@ -86,3 +87,68 @@ export type StudySubjectRemoval = {
|
|
|
86
87
|
export type StudySubjectRemovalResponse = Extract<StudySubjectRemoval, {
|
|
87
88
|
ok: true;
|
|
88
89
|
}>;
|
|
90
|
+
/** The window GET /api/study/summary totals over. Days are UTC calendar days,
|
|
91
|
+
* like the XP curve's: `today` is since UTC midnight, `7d` and `30d` are the
|
|
92
|
+
* 7 and 30 UTC days ending today, `all` is unbounded. */
|
|
93
|
+
export type StudyRange = "today" | "7d" | "30d" | "all";
|
|
94
|
+
/** One subject's finished study time in a range. `lastAt` is the start of
|
|
95
|
+
* the most recent session counted. The subject resolves whether or not it has
|
|
96
|
+
* since been archived, so history keeps its name. */
|
|
97
|
+
export interface StudySubjectTotals {
|
|
98
|
+
subject: ActionSubjectRef;
|
|
99
|
+
seconds: number;
|
|
100
|
+
focusedSeconds: number;
|
|
101
|
+
sessions: number;
|
|
102
|
+
lastAt: string;
|
|
103
|
+
}
|
|
104
|
+
/** One catalog category's finished study time in a range: the sum over every
|
|
105
|
+
* subject whose `field` is that category or one of its fields. */
|
|
106
|
+
export interface StudyCategoryTotals {
|
|
107
|
+
slug: string;
|
|
108
|
+
label: string;
|
|
109
|
+
seconds: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Response of GET /api/study/summary: finished `action.study` sessions only,
|
|
113
|
+
* summed from the durations materialized at finish. `subjects` and
|
|
114
|
+
* `categories` are ordered by seconds descending. Sessions without a subject
|
|
115
|
+
* (a game Study started while the user had none) count in `total` only.
|
|
116
|
+
*/
|
|
117
|
+
export interface StudySummary {
|
|
118
|
+
range: StudyRange;
|
|
119
|
+
subjects: StudySubjectTotals[];
|
|
120
|
+
categories: StudyCategoryTotals[];
|
|
121
|
+
total: {
|
|
122
|
+
seconds: number;
|
|
123
|
+
sessions: number;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** One finished study session as GET /api/study/sessions lists it. */
|
|
127
|
+
export interface StudySessionEntry {
|
|
128
|
+
sessionId: string;
|
|
129
|
+
startedAt: string;
|
|
130
|
+
finishedAt: string;
|
|
131
|
+
seconds: number;
|
|
132
|
+
focusedSeconds: number;
|
|
133
|
+
xpGranted: number;
|
|
134
|
+
surface: ActionSurface;
|
|
135
|
+
}
|
|
136
|
+
/** Response of GET /api/study/sessions: newest first, `nextBefore` is the
|
|
137
|
+
* `before` of the next page or null on the last one. */
|
|
138
|
+
export interface StudySessionPage {
|
|
139
|
+
sessions: StudySessionEntry[];
|
|
140
|
+
nextBefore: string | null;
|
|
141
|
+
}
|
|
142
|
+
export interface ListStudySessionsInput {
|
|
143
|
+
subjectId: string;
|
|
144
|
+
/** Exclusive upper bound on `startedAt`; omit for the newest page. */
|
|
145
|
+
before?: Date;
|
|
146
|
+
limit: number;
|
|
147
|
+
}
|
|
148
|
+
export type StudySessionsResult = {
|
|
149
|
+
ok: true;
|
|
150
|
+
page: StudySessionPage;
|
|
151
|
+
} | {
|
|
152
|
+
ok: false;
|
|
153
|
+
reason: "not_found";
|
|
154
|
+
};
|
package/dist/sdk/types/xp.d.ts
CHANGED
|
@@ -195,3 +195,20 @@ export type PublicProfileResult = {
|
|
|
195
195
|
reason: PublicProfileRefusal;
|
|
196
196
|
signal: PublicProfileSignal;
|
|
197
197
|
};
|
|
198
|
+
/**
|
|
199
|
+
* What a user is doing right now, as OTHER users see it — the room roster's
|
|
200
|
+
* view of an active `action_session`, not the owner's own
|
|
201
|
+
* {@link ActiveActionResponse}. Deliberately narrower: no clock, no XP
|
|
202
|
+
* context, no session id, because a reader has no session to drive. `label`
|
|
203
|
+
* is `null` for a source the registry no longer names, exactly as the owner's
|
|
204
|
+
* view reports it. `subject` is the study subject's name and is present only
|
|
205
|
+
* while the owner's `study.share_subject` setting is on — the subject is their
|
|
206
|
+
* own words ("Bar exam prep"), so its absence is the default, not a gap.
|
|
207
|
+
*/
|
|
208
|
+
export interface CurrentAction {
|
|
209
|
+
source: string;
|
|
210
|
+
label: string | null;
|
|
211
|
+
/** ISO timestamp of the session's start. */
|
|
212
|
+
since: string;
|
|
213
|
+
subject?: string;
|
|
214
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flow-industries/id",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"types": "./dist/sdk/client/index.d.ts",
|
|
20
20
|
"import": "./dist/sdk/client/index.js"
|
|
21
21
|
},
|
|
22
|
+
"./usernames": {
|
|
23
|
+
"types": "./dist/sdk/usernames.d.ts",
|
|
24
|
+
"import": "./dist/sdk/usernames.js"
|
|
25
|
+
},
|
|
22
26
|
"./react": {
|
|
23
27
|
"types": "./dist/sdk/react/index.d.ts",
|
|
24
28
|
"import": "./dist/sdk/react/index.js"
|
|
@@ -69,6 +73,7 @@
|
|
|
69
73
|
"usernames:refresh": "bun run scripts/generate-reserved-usernames.ts",
|
|
70
74
|
"lint": "biome check",
|
|
71
75
|
"test": "bun test src",
|
|
76
|
+
"test:sdk-package": "bash scripts/test-sdk-package.sh",
|
|
72
77
|
"format": "biome format --write",
|
|
73
78
|
"check": "biome check --write",
|
|
74
79
|
"typecheck": "tsr generate && tsc --noEmit"
|